Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ransome
sleek
Commits
38bf7348
Commit
38bf7348
authored
Jul 25, 2021
by
ransome1
Browse files
Preserve file history, enhanced hidden todos function
parent
abf595b2
Changes
15
Expand all
Hide whitespace changes
Inline
Side-by-side
.gitignore
View file @
38bf7348
...
...
@@ -18,8 +18,7 @@ flatpak/com.github.ransome1.sleek.yml
assets/icons/bak
squashfs-root/
.eslintrc.json
.stylelintrc.json
package-lock.json
.vs/
.vscode/
.stylelintrc.json
src/css/
package.json
View file @
38bf7348
{
"name"
:
"sleek"
,
"productName"
:
"sleek"
,
"version"
:
"1.1.0-rc.
4
"
,
"version"
:
"1.1.0-rc.
5
"
,
"description"
:
"Todo app based on todo.txt for Linux, Windows and MacOS, free and open-source"
,
"synopsis"
:
"Todo app based on todo.txt for Linux, Windows and MacOS, free and open-source"
,
"category"
:
"ProjectManagement"
,
...
...
@@ -102,7 +102,7 @@
"pack"
:
"yarn build:css && yarn build:pegjs && electron-builder --dir"
,
"lint"
:
"eslint --ext .js, src --ext .mjs, src"
,
"test"
:
"mocha --timeout 10000"
,
"test1"
:
"mocha ./test/
createTodos
.js --timeout 10000"
,
"test1"
:
"mocha ./test/
onboarding
.js --timeout 10000"
,
"sass"
:
"sass -w src/scss/style.scss:src/css/style.css"
,
"start"
:
"yarn sass & electron ."
},
...
...
src/css/style.css
0 → 100644
View file @
38bf7348
This diff is collapsed.
Click to expand it.
src/css/style.css.map
0 → 100644
View file @
38bf7348
This diff is collapsed.
Click to expand it.
src/js/files.mjs
View file @
38bf7348
"
use strict
"
;
import
{
resetFilters
,
resetModal
,
handleError
,
userData
,
appData
,
setUserData
,
translations
}
from
"
../render.js
"
;
import
{
resetFilters
,
resetModal
,
handleError
,
userData
,
appData
,
setUserData
,
translations
,
showOnboarding
}
from
"
../render.js
"
;
import
{
_paq
}
from
"
./matomo.mjs
"
;
import
{
createModalJail
}
from
"
../configs/modal.config.mjs
"
;
...
...
@@ -8,22 +8,39 @@ const modalChangeFile = document.getElementById("modalChangeFile");
const
modalChangeFileTable
=
document
.
getElementById
(
"
modalChangeFileTable
"
);
const
fileTabBarList
=
document
.
querySelector
(
"
#fileTabBar ul
"
);
function
removeFileFromList
(
i
sActive
,
index
)
{
function
removeFileFromList
(
i
ndex
,
isTabItem
)
{
try
{
if
(
isActive
&&
index
-
1
===
-
1
)
{
userData
.
files
[
index
+
1
][
0
]
=
1
;
}
else
if
(
isActive
&&
index
-
1
>=
0
)
{
userData
.
files
[
index
-
1
][
0
]
=
1
;
if
(
isTabItem
)
{
let
newItemIndex
;
userData
.
files
[
index
][
0
]
=
0
;
userData
.
files
[
index
][
2
]
=
0
;
newItemIndex
=
userData
.
files
.
findIndex
(
file
=>
{
return
file
[
2
]
===
1
;
});
if
(
newItemIndex
>=
0
)
{
userData
.
files
[
newItemIndex
][
0
]
=
1
;
userData
.
files
[
newItemIndex
][
2
]
=
1
;
resetFilters
(
true
).
then
(
function
(
response
)
{
console
.
log
(
response
);
setUserData
(
"
files
"
,
userData
.
files
);
window
.
api
.
send
(
"
startFileWatcher
"
,
[
userData
.
files
[
newItemIndex
][
1
],
1
]);
}).
catch
(
function
(
error
)
{
handleError
(
error
);
});
}
else
{
userData
.
files
[
index
][
0
]
=
0
;
userData
.
files
[
index
][
2
]
=
0
;
setUserData
(
"
files
"
,
userData
.
files
);
if
(
userData
.
files
.
length
>
0
)
generateFileList
();
showOnboarding
(
true
);
}
}
else
{
if
(
userData
.
files
[
index
][
0
])
{
showOnboarding
(
true
);
}
userData
.
files
.
splice
(
index
,
1
);
setUserData
(
"
files
"
,
userData
.
files
);
}
userData
.
files
.
splice
(
index
,
1
);
setUserData
(
"
files
"
,
userData
.
files
);
resetFilters
(
true
).
then
(
function
(
response
)
{
console
.
info
(
response
);
index
=
userData
.
files
.
findIndex
(
file
=>
file
[
0
]
===
1
);
window
.
api
.
send
(
"
startFileWatcher
"
,
userData
.
files
[
index
][
1
]);
}).
catch
(
function
(
error
)
{
handleError
(
error
);
});
return
Promise
.
resolve
(
"
Success: File removed from list
"
);
}
catch
(
error
)
{
return
Promise
.
reject
(
error
);
...
...
@@ -38,7 +55,7 @@ function selectFileFromList(index) {
handleError
(
error
);
});
resetModal
().
then
(
response
=>
{
window
.
api
.
send
(
"
startFileWatcher
"
,
userData
.
files
[
index
][
1
]);
window
.
api
.
send
(
"
startFileWatcher
"
,
[
userData
.
files
[
index
][
1
],
1
]);
console
.
info
(
response
);
}).
catch
(
error
=>
{
handleError
(
error
);
...
...
@@ -51,40 +68,43 @@ function selectFileFromList(index) {
function
generateFileList
()
{
try
{
if
(
userData
.
files
.
length
>
1
&&
userData
.
fileTabs
)
{
fileTabBar
.
classList
.
add
(
"
is-active
"
);
}
else
{
fileTabBar
.
classList
.
remove
(
"
is-active
"
);
}
fileTabBar
.
classList
.
remove
(
"
is-active
"
);
fileTabBarList
.
innerHTML
=
null
;
modalChangeFileTable
.
innerHTML
=
null
;
modalChangeFileTable
.
classList
.
add
(
"
files
"
);
let
j
=
0
;
for
(
let
i
=
0
;
i
<
userData
.
files
.
length
;
i
++
)
{
let
isActive
=
userData
.
files
[
i
][
0
];
let
fileName
=
userData
.
files
[
i
][
1
].
split
(
"
/
"
).
pop
();
if
(
appData
.
os
===
"
windows
"
)
fileName
=
userData
.
files
[
i
][
1
].
split
(
"
\\
"
).
pop
();
let
listItem
=
document
.
createElement
(
"
li
"
);
listItem
.
innerHTML
=
fileName
;
listItem
.
innerHTML
+=
"
<i class=
\"
fas fa-minus-circle
\"
></i>
"
;
if
(
isActive
===
1
)
listItem
.
classList
.
add
(
"
is-highlighted
"
);
listItem
.
querySelector
(
"
i
"
).
onclick
=
function
()
{
removeFileFromList
(
isActive
,
i
);
// trigger matomo event
if
(
userData
.
matomoEvents
)
_paq
.
push
([
"
trackEvent
"
,
"
File-Tab
"
,
"
Click on remove icon
"
]);
}
if
(
!
isActive
)
{
listItem
.
onclick
=
function
(
event
)
{
if
(
event
.
target
.
classList
.
contains
(
"
fas
"
))
return
false
;
selectFileFromList
(
i
).
then
(
function
(
response
)
{
console
.
info
(
response
);
}).
catch
(
function
(
error
)
{
handleError
(
error
);
});
let
isTabItem
=
userData
.
files
[
i
][
2
];
if
(
isTabItem
)
{
j
++
;
let
fileName
=
userData
.
files
[
i
][
1
].
split
(
"
/
"
).
pop
();
if
(
j
>
1
&&
userData
.
fileTabs
)
fileTabBar
.
classList
.
add
(
"
is-active
"
);
if
(
appData
.
os
===
"
windows
"
)
fileName
=
userData
.
files
[
i
][
1
].
split
(
"
\\
"
).
pop
();
let
listItem
=
document
.
createElement
(
"
li
"
);
listItem
.
setAttribute
(
"
title
"
,
userData
.
files
[
i
][
1
]);
listItem
.
innerHTML
=
fileName
;
listItem
.
innerHTML
+=
"
<i class=
\"
fas fa-times
\"
></i>
"
;
if
(
isActive
===
1
)
listItem
.
classList
.
add
(
"
is-highlighted
"
);
listItem
.
querySelector
(
"
i
"
).
onclick
=
function
()
{
removeFileFromList
(
i
,
isTabItem
);
// trigger matomo event
if
(
userData
.
matomoEvents
)
_paq
.
push
([
"
trackEvent
"
,
"
File-Tab
"
,
"
Click on tab
"
]);
if
(
userData
.
matomoEvents
)
_paq
.
push
([
"
trackEvent
"
,
"
File-Tab
"
,
"
Click on remove icon
"
]);
}
if
(
!
isActive
)
{
listItem
.
onclick
=
function
(
event
)
{
if
(
event
.
target
.
classList
.
contains
(
"
fas
"
))
return
false
;
selectFileFromList
(
i
).
then
(
function
(
response
)
{
console
.
info
(
response
);
}).
catch
(
function
(
error
)
{
handleError
(
error
);
});
// trigger matomo event
if
(
userData
.
matomoEvents
)
_paq
.
push
([
"
trackEvent
"
,
"
File-Tab
"
,
"
Click on tab
"
]);
}
}
fileTabBarList
.
appendChild
(
listItem
);
}
fileTabBarList
.
appendChild
(
listItem
);
let
row
=
modalChangeFileTable
.
insertRow
(
-
1
);
let
cell1
=
row
.
insertCell
(
0
);
let
cell2
=
row
.
insertCell
(
1
);
...
...
@@ -107,7 +127,7 @@ function generateFileList() {
cell3
.
innerHTML
=
"
<a href=
\"
#
\"
tabindex=
\"
0
\"
><i class=
\"
fas fa-minus-circle
\"
></i></a>
"
;
cell3
.
title
=
translations
.
delete
;
cell3
.
onclick
=
function
()
{
removeFileFromList
(
isActive
,
i
);
removeFileFromList
(
i
);
generateFileList
().
then
(
response
=>
{
modalChangeFile
.
classList
.
add
(
"
is-active
"
);
modalChangeFile
.
focus
();
...
...
src/js/filters.mjs
View file @
38bf7348
...
...
@@ -155,7 +155,7 @@ function filterItems(items) {
}
// apply filters
items
=
items
.
filter
(
function
(
item
)
{
if
(
!
item
.
text
&&
!
item
.
h
)
return
false
;
//
if(!item.text && !item.h) return false;
if
(
!
userData
.
showHidden
&&
item
.
h
)
return
false
;
if
(
!
userData
.
showCompleted
&&
item
.
complete
)
return
false
;
if
(
!
userData
.
showDueIsToday
&&
item
.
due
&&
isToday
(
item
.
due
))
return
false
;
...
...
src/js/form.mjs
View file @
38bf7348
...
...
@@ -370,12 +370,12 @@ function show(todo, templated) {
}
function
submitForm
()
{
try
{
if
(
userData
.
file
===
undefined
)
{
modalFormAlert
.
innerHTML
=
translations
.
formErrorWritingFile
;
modalFormAlert
.
parentElement
.
classList
.
remove
(
"
is-active
"
,
'
is-danger
'
);
modalFormAlert
.
parentElement
.
classList
.
add
(
"
is-active
"
,
'
is-warning
'
);
return
Promise
.
resolve
(
"
Info: No todo.txt defined yet
"
);
}
//
if(userData.file === undefined) {
//
modalFormAlert.innerHTML = translations.formErrorWritingFile;
//
modalFormAlert.parentElement.classList.remove("is-active", 'is-danger');
//
modalFormAlert.parentElement.classList.add("is-active", 'is-warning');
//
return Promise.resolve("Info: No todo.txt defined yet");
//
}
// check if there is an input in the text field, otherwise indicate it to the user
// input value and data item are the same, nothing has changed, nothing will be written
if
(
modalForm
.
getAttribute
(
"
data-item
"
)
===
document
.
getElementById
(
"
modalFormInput
"
).
value
)
{
...
...
@@ -440,7 +440,7 @@ function submitForm() {
}
//write the data to the file
// a newline character is added to prevent other todo.txt apps to append new todos to the last line
window
.
api
.
send
(
"
writeToFile
"
,
[
items
.
objects
.
join
(
"
\n
"
).
toString
()
+
"
\n
"
,
userData
.
file
]);
window
.
api
.
send
(
"
writeToFile
"
,
[
items
.
objects
.
join
(
"
\n
"
).
toString
()
+
"
\n
"
]);
// close and reset any modal
resetModal
().
then
(
function
(
result
)
{
console
.
log
(
result
);
...
...
@@ -449,11 +449,11 @@ function submitForm() {
});
// trigger matomo event
if
(
userData
.
matomoEvents
)
_paq
.
push
([
"
trackEvent
"
,
"
Form
"
,
"
Submit
"
]);
return
Promise
.
resolve
(
"
Success: Changes written to file
:
"
+
userData
.
file
);
return
Promise
.
resolve
(
"
Success: Changes written to file
"
);
// if the input field is empty, let users know
}
catch
(
error
)
{
// if writing into file is denied throw alert
modalFormAlert
.
innerHTML
=
translations
.
formErrorWritingFile
+
userData
.
file
;
modalFormAlert
.
innerHTML
=
translations
.
formErrorWritingFile
;
modalFormAlert
.
parentElement
.
classList
.
add
(
"
is-active
"
,
'
is-danger
'
);
error
.
functionName
=
submitForm
.
name
;
return
Promise
.
reject
(
error
);
...
...
src/js/todos.mjs
View file @
38bf7348
...
...
@@ -115,10 +115,10 @@ function configureTodoTableTemplate(append) {
function
generateItems
(
content
)
{
try
{
items
=
{
objects
:
TodoTxt
.
parse
(
content
,
[
new
DueExtension
(),
new
HiddenExtension
(),
new
RecExtension
(),
new
ThresholdExtension
()
])
}
items
.
objects
=
items
.
objects
.
filter
(
function
(
item
)
{
if
(
!
item
.
text
&&
!
item
.
h
)
return
false
;
return
true
;
});
//
items.objects = items.objects.filter(function(item) {
//
if(!item.text && !item.h) return false;
//
return true;
//
});
items
.
complete
=
items
.
objects
.
filter
(
function
(
item
)
{
return
item
.
complete
===
true
});
items
.
incomplete
=
items
.
objects
.
filter
(
function
(
item
)
{
return
item
.
complete
===
false
});
items
.
objects
=
items
.
objects
.
filter
(
function
(
item
)
{
return
item
.
toString
()
!=
""
});
...
...
@@ -538,16 +538,18 @@ function addTodo(todo) {
}
async
function
archiveTodos
()
{
try
{
const
index
=
userData
.
files
.
findIndex
(
file
=>
file
[
0
]
===
1
);
const
file
=
userData
.
files
[
index
][
1
];
// cancel operation if there are no completed todos
if
(
items
.
complete
.
length
===
0
)
return
Promise
.
resolve
(
"
Info: No completed todos found, nothing will be archived
"
)
// if user archives within done.txt file, operating is canceled
if
(
userData
.
file
.
includes
(
"
_done.
"
))
return
Promise
.
resolve
(
"
Info: Current file seems to be a done.txt file, won't archive
"
)
if
(
file
.
includes
(
"
_done.
"
))
return
Promise
.
resolve
(
"
Info: Current file seems to be a done.txt file, won't archive
"
)
// define path to done.txt
let
doneFile
=
function
()
{
if
(
appData
.
os
===
"
windows
"
)
{
return
userData
.
file
.
replace
(
userData
.
file
.
split
(
"
\\
"
).
pop
(),
userData
.
file
.
substr
(
0
,
userData
.
file
.
lastIndexOf
(
"
.
"
)).
split
(
"
\\
"
).
pop
()
+
"
_done.txt
"
);
return
file
.
replace
(
file
.
split
(
"
\\
"
).
pop
(),
file
.
substr
(
0
,
file
.
lastIndexOf
(
"
.
"
)).
split
(
"
\\
"
).
pop
()
+
"
_done.txt
"
);
}
else
{
return
userData
.
file
.
replace
(
userData
.
file
.
split
(
"
/
"
).
pop
(),
userData
.
file
.
substr
(
0
,
userData
.
file
.
lastIndexOf
(
"
.
"
)).
split
(
"
/
"
).
pop
()
+
"
_done.txt
"
);
return
file
.
replace
(
file
.
split
(
"
/
"
).
pop
(),
file
.
substr
(
0
,
file
.
lastIndexOf
(
"
.
"
)).
split
(
"
/
"
).
pop
()
+
"
_done.txt
"
);
}
}
const
getContentFromDoneFile
=
new
Promise
(
function
(
resolve
)
{
...
...
@@ -573,7 +575,7 @@ async function archiveTodos() {
//write completed items to done file
window
.
api
.
send
(
"
writeToFile
"
,
[
contentForDoneFile
.
join
(
"
\n
"
).
toString
()
+
"
\n
"
,
doneFile
()]);
// write incompleted items to todo file
window
.
api
.
send
(
"
writeToFile
"
,
[
items
.
incomplete
.
join
(
"
\n
"
).
toString
()
+
"
\n
"
,
userData
.
file
]);
window
.
api
.
send
(
"
writeToFile
"
,
[
items
.
incomplete
.
join
(
"
\n
"
).
toString
()
+
"
\n
"
,
file
]);
// send notifcation on success
generateNotification
(
null
,
null
,
translations
.
archivingCompletedTitle
,
translations
.
archivingCompletedBody
+
doneFile
());
...
...
src/main.js
View file @
38bf7348
...
...
@@ -123,7 +123,7 @@ const createWindow = async function() {
userData
.
data
.
path
=
path
.
dirname
(
file
);
userData
.
set
(
"
path
"
,
userData
.
data
.
path
);
console
.
info
(
"
Success: Opened file:
"
+
file
);
startFileWatcher
(
file
).
then
(
response
=>
{
startFileWatcher
(
file
,
1
).
then
(
response
=>
{
console
.
info
(
response
);
mainWindow
.
webContents
.
send
(
"
triggerFunction
"
,
"
resetModal
"
)
}).
catch
(
error
=>
{
...
...
@@ -153,7 +153,7 @@ const createWindow = async function() {
userData
.
data
.
path
=
path
.
dirname
(
file
.
filePath
);
userData
.
set
(
"
path
"
,
userData
.
data
.
path
);
console
.
info
(
"
Success: New file created:
"
+
file
.
filePath
);
startFileWatcher
(
file
.
filePath
).
then
(
response
=>
{
startFileWatcher
(
file
.
filePath
,
1
).
then
(
response
=>
{
console
.
info
(
response
);
mainWindow
.
webContents
.
send
(
"
triggerFunction
"
,
"
resetModal
"
)
}).
catch
(
error
=>
{
...
...
@@ -167,14 +167,13 @@ const createWindow = async function() {
break
;
}
}
const
startFileWatcher
=
function
(
file
)
{
const
startFileWatcher
=
function
(
file
,
isTabItem
)
{
try
{
if
(
!
fs
.
existsSync
(
file
))
throw
(
"
Error: File not found on disk
"
)
// skip persisted files and go with ENV if set
if
(
process
.
env
.
SLEEK_CUSTOM_FILE
&&
fs
.
existsSync
(
process
.
env
.
SLEEK_CUSTOM_FILE
))
{
file
=
process
.
env
.
SLEEK_CUSTOM_FILE
;
}
let
args
;
if
(
process
.
defaultApp
)
{
// electron "unbundled" app -- have to skip "electron" and script name arg eg: "."
args
=
process
.
argv
.
slice
(
2
);
...
...
@@ -192,6 +191,7 @@ const createWindow = async function() {
// if path is found it is set active
if
(
element
[
1
]
===
file
)
{
element
[
0
]
=
1
if
(
isTabItem
)
element
[
2
]
=
1
;
fileFound
=
true
;
// if this entry is not equal to the new path it is set 0
}
else
{
...
...
@@ -202,10 +202,10 @@ const createWindow = async function() {
userData
.
data
.
files
=
new
Array
;
}
// only push new path if it is not already in the user data
if
((
!
fileFound
||
!
userData
.
data
.
files
)
&&
file
)
userData
.
data
.
files
.
push
([
1
,
file
]);
if
((
!
fileFound
||
!
userData
.
data
.
files
)
&&
file
)
userData
.
data
.
files
.
push
([
1
,
file
,
1
]);
userData
.
set
(
"
files
"
,
userData
.
data
.
files
);
userData
.
data
.
file
=
file
;
userData
.
set
(
"
file
"
,
file
);
//
userData.data.file = file;
//
userData.set("file", file);
// TODO describe
if
(
fileWatcher
)
fileWatcher
.
close
();
fileWatcher
=
chokidar
.
watch
(
file
);
...
...
@@ -589,7 +589,7 @@ const createWindow = async function() {
type
:
"
radio
"
,
checked
:
false
,
click
:
function
()
{
startFileWatcher
(
file
[
1
]);
startFileWatcher
(
file
[
1
]
,
1
);
mainWindow
.
show
();
mainWindow
.
setSkipTaskbar
(
true
);
}
...
...
@@ -694,8 +694,15 @@ const createWindow = async function() {
})
.
on
(
"
writeToFile
"
,
function
(
event
,
args
)
{
try
{
let
file
;
if
(
!
args
[
1
])
{
const
index
=
userData
.
data
.
files
.
findIndex
(
file
=>
file
[
0
]
===
1
);
file
=
userData
.
data
.
files
[
index
][
1
];
}
else
{
file
=
args
[
1
];
}
// Write content to file
fs
.
writeFileSync
(
args
[
1
]
,
args
[
0
],
{
encoding
:
"
utf-8
"
});
if
(
file
)
fs
.
writeFileSync
(
file
,
args
[
0
],
{
encoding
:
"
utf-8
"
});
}
catch
(
error
)
{
console
.
error
(
error
);
error
.
functionName
=
"
fs.writeFileSync
"
;
...
...
@@ -708,8 +715,8 @@ const createWindow = async function() {
.
on
(
"
openOrCreateFile
"
,
(
event
,
args
)
=>
{
openDialog
(
args
);
})
.
on
(
"
startFileWatcher
"
,
(
event
,
file
)
=>
{
startFileWatcher
(
file
).
then
(
response
=>
{
.
on
(
"
startFileWatcher
"
,
(
event
,
data
)
=>
{
startFileWatcher
(
data
[
0
],
data
[
1
]
).
then
(
response
=>
{
console
.
info
(
response
);
}).
catch
(
error
=>
{
console
.
error
(
error
);
...
...
@@ -763,8 +770,9 @@ const createWindow = async function() {
// REFRESH WHEN IN BACKGROUND
// ########################################################################################################################
setInterval
(()
=>
{
if
(
userData
.
data
.
file
&&
!
mainWindow
.
isFocused
())
{
getContent
(
userData
.
data
.
file
).
then
(
content
=>
{
if
(
userData
.
data
.
files
.
length
>
0
&&
!
mainWindow
.
isFocused
())
{
const
index
=
userData
.
data
.
files
.
findIndex
(
file
=>
file
[
0
]
===
1
);
getContent
(
userData
.
data
.
files
[
index
][
1
]).
then
(
content
=>
{
mainWindow
.
webContents
.
send
(
"
refresh
"
,
[
content
])
}).
catch
(
error
=>
{
console
.
error
(
error
);
...
...
src/render.js
View file @
38bf7348
...
...
@@ -134,19 +134,19 @@ function configureMainView() {
handleError
(
error
);
});
// configure table view
if
(
userData
.
file
&&
todos
.
items
.
objects
.
length
===
0
)
{
if
(
todos
.
items
.
objects
.
length
===
0
)
{
addTodoContainer
.
classList
.
add
(
"
is-active
"
);
todoTableSearchContainer
.
classList
.
remove
(
"
is-active
"
);
todoTable
.
classList
.
remove
(
"
is-active
"
);
noResultContainer
.
classList
.
remove
(
"
is-active
"
);
return
Promise
.
resolve
(
"
Info: File is empty
"
);
}
else
if
(
userData
.
file
&&
todos
.
items
.
filtered
.
length
===
0
)
{
}
else
if
(
todos
.
items
.
filtered
.
length
===
0
)
{
addTodoContainer
.
classList
.
remove
(
"
is-active
"
);
todoTableSearchContainer
.
classList
.
add
(
"
is-active
"
);
noResultContainer
.
classList
.
add
(
"
is-active
"
);
return
Promise
.
resolve
(
"
Info: No results
"
);
// TODO explain
}
else
if
(
userData
.
file
&&
todos
.
items
.
filtered
.
length
>
0
)
{
}
else
if
(
todos
.
items
.
filtered
.
length
>
0
)
{
todoTableSearchContainer
.
classList
.
add
(
"
is-active
"
);
addTodoContainer
.
classList
.
remove
(
"
is-active
"
);
noResultContainer
.
classList
.
remove
(
"
is-active
"
);
...
...
@@ -318,7 +318,19 @@ function registerEvents() {
if
(
userData
.
matomoEvents
)
matomo
.
_paq
.
push
([
"
trackEvent
"
,
"
Onboarding
"
,
"
Click on Create file
"
]);
}
btnOnboardingOpenTodoFile
.
onclick
=
function
()
{
window
.
api
.
send
(
"
openOrCreateFile
"
,
"
open
"
);
//TODO: thhis is a duplicate
if
(
typeof
userData
.
files
===
"
object
"
&&
userData
.
files
.
length
>
0
)
{
files
.
generateFileList
().
then
(
response
=>
{
console
.
info
(
response
);
modalChangeFile
.
classList
.
add
(
"
is-active
"
);
modalChangeFile
.
focus
();
createModalJail
(
modalChangeFile
);
}).
catch
(
error
=>
{
handleError
(
error
);
});
}
else
{
window
.
api
.
send
(
"
openOrCreateFile
"
,
"
open
"
);
}
// trigger matomo event
if
(
userData
.
matomoEvents
)
matomo
.
_paq
.
push
([
"
trackEvent
"
,
"
Onboarding
"
,
"
Click on Open file
"
]);
}
...
...
@@ -386,37 +398,40 @@ function registerKeyboardShortcuts() {
}
// close tab or window
if
((
event
.
ctrlKey
||
event
.
metaKey
)
&&
event
.
key
===
"
w
"
)
{
if
(
userData
.
files
.
length
>
1
)
{
const
isTabFound
=
userData
.
files
.
findIndex
(
file
=>
{
return
file
[
2
]
===
1
;
});
if
(
isTabFound
>=
0
)
{
let
index
=
userData
.
files
.
findIndex
(
file
=>
file
[
0
]
===
1
);
files
.
removeFileFromList
(
1
,
index
);
files
.
removeFileFromList
(
index
,
1
);
}
else
{
window
.
api
.
send
(
"
closeWindow
"
);
}
}
},
true
)
window
.
addEventListener
(
"
keyup
"
,
function
(
event
)
{
// switch files
const
regex
=
/^
[
1-9
]
+$/
;
if
(
event
.
key
.
match
(
regex
)
&&
userData
.
files
.
length
>
1
&&
userData
.
files
[
event
.
key
-
1
]
&&
!
modalForm
.
classList
.
contains
(
"
is-active
"
)
&&
(
document
.
activeElement
.
id
!=
"
todoTableSearch
"
&&
document
.
activeElement
.
id
!=
"
filterContextInput
"
&&
document
.
activeElement
.
id
!=
"
modalFormInput
"
))
{
if
(
userData
.
files
[
event
.
key
-
1
][
1
])
window
.
api
.
send
(
"
startFileWatcher
"
,
userData
.
files
[
event
.
key
-
1
][
1
]);
}
// cycle through tabs
if
(
event
.
ctrlKey
&&
!
event
.
shiftKey
&&
event
.
keyCode
===
9
)
{
let
index
=
userData
.
files
.
findIndex
(
file
=>
file
[
0
]
===
1
);
if
(
!
userData
.
files
[
index
+
1
])
{
window
.
api
.
send
(
"
startFileWatcher
"
,
userData
.
files
[
0
][
1
]);
window
.
api
.
send
(
"
startFileWatcher
"
,
[
userData
.
files
[
0
][
1
],
1
]);
}
else
{
window
.
api
.
send
(
"
startFileWatcher
"
,
userData
.
files
[
index
+
1
][
1
]);
window
.
api
.
send
(
"
startFileWatcher
"
,
[
userData
.
files
[
index
+
1
][
1
],
1
]);
}
}
if
(
event
.
ctrlKey
&&
event
.
shiftKey
&&
event
.
keyCode
===
9
)
{
let
index
=
userData
.
files
.
findIndex
(
file
=>
file
[
0
]
===
1
);
if
(
!
userData
.
files
[
index
-
1
])
{
window
.
api
.
send
(
"
startFileWatcher
"
,
userData
.
files
[
userData
.
files
.
length
-
1
][
1
]);
window
.
api
.
send
(
"
startFileWatcher
"
,
[
userData
.
files
[
userData
.
files
.
length
-
1
][
1
],
1
]);
}
else
{
window
.
api
.
send
(
"
startFileWatcher
"
,
userData
.
files
[
index
-
1
][
1
]);
window
.
api
.
send
(
"
startFileWatcher
"
,
[
userData
.
files
[
index
-
1
][
1
],
1
]);
}
}
},
true
)
window
.
addEventListener
(
"
keyup
"
,
function
(
event
)
{
// switch files
const
regex
=
/^
[
1-9
]
+$/
;
if
(
event
.
key
.
match
(
regex
)
&&
userData
.
files
.
length
>
1
&&
userData
.
files
[
event
.
key
-
1
]
&&
!
modalForm
.
classList
.
contains
(
"
is-active
"
)
&&
(
document
.
activeElement
.
id
!=
"
todoTableSearch
"
&&
document
.
activeElement
.
id
!=
"
filterContextInput
"
&&
document
.
activeElement
.
id
!=
"
modalFormInput
"
))
{
if
(
userData
.
files
[
event
.
key
-
1
][
1
])
window
.
api
.
send
(
"
startFileWatcher
"
,
[
userData
.
files
[
event
.
key
-
1
][
1
]]);
}
// open settings
if
(
event
.
key
===
"
,
"
&&
!
modalForm
.
classList
.
contains
(
"
is-active
"
)
&&
(
document
.
activeElement
.
id
!=
"
todoTableSearch
"
&&
document
.
activeElement
.
id
!=
"
filterContextInput
"
&&
document
.
activeElement
.
id
!=
"
modalFormInput
"
))
{
content
.
showContent
(
"
modalSettings
"
).
then
(
function
(
response
)
{
...
...
@@ -435,9 +450,7 @@ function registerKeyboardShortcuts() {
}
// create new todo
if
(
event
.
key
===
"
n
"
&&
!
modalForm
.
classList
.
contains
(
"
is-active
"
)
&&
(
document
.
activeElement
.
id
!=
"
todoTableSearch
"
&&
document
.
activeElement
.
id
!=
"
filterContextInput
"
&&
document
.
activeElement
.
id
!=
"
modalFormInput
"
))
{
// abort when onboarding is shown
if
(
onboarding
)
return
false
;
form
.
show
().
then
(
function
(
response
)
{
console
.
info
(
response
);
}).
catch
(
function
(
error
)
{
...
...
@@ -446,9 +459,7 @@ function registerKeyboardShortcuts() {
}
// reset filters
if
(
event
.
key
===
"
0
"
&&
!
modalForm
.
classList
.
contains
(
"
is-active
"
)
&&
(
document
.
activeElement
.
id
!=
"
todoTableSearch
"
&&
document
.
activeElement
.
id
!=
"
filterContextInput
"
&&
document
.
activeElement
.
id
!=
"
modalFormInput
"
))
{
// abort when onboarding is shown
if
(
onboarding
)
return
false
;
resetFilters
(
true
).
then
(
function
(
response
)
{
console
.
info
(
response
);
}).
catch
(
function
(
error
)
{
...
...
@@ -457,15 +468,22 @@ function registerKeyboardShortcuts() {
}
// toggle completed todos
if
(
event
.
key
===
"
h
"
&&
!
modalForm
.
classList
.
contains
(
"
is-active
"
)
&&
(
document
.
activeElement
.
id
!=
"
todoTableSearch
"
&&
document
.
activeElement
.
id
!=
"
filterContextInput
"
&&
document
.
activeElement
.
id
!=
"
modalFormInput
"
))
{
// abort when onboarding is shown
if
(
onboarding
)
return
false
;
view
.
toggle
(
"
showCompleted
"
).
then
(
function
(
response
)
{
console
.
info
(
response
);
}).
catch
(
function
(
error
)
{
handleError
(
error
);
});
}
// toggle deferred todos
if
(
event
.
key
===
"
t
"
&&
!
modalForm
.
classList
.
contains
(
"
is-active
"
)
&&
(
document
.
activeElement
.
id
!=
"
todoTableSearch
"
&&
document
.
activeElement
.
id
!=
"
filterContextInput
"
&&
document
.
activeElement
.
id
!=
"
modalFormInput
"
))
{
if
(
onboarding
)
return
false
;
view
.
toggle
(
"
deferredTodos
"
).
then
(
function
(
response
)
{
console
.
info
(
response
);
}).
catch
(
function
(
error
)
{
handleError
(
error
);
});
}
// archive todos
if
(
event
.
key
===
"
a
"
&&
!
modalForm
.
classList
.
contains
(
"
is-active
"
)
&&
(
document
.
activeElement
.
id
!=
"
todoTableSearch
"
&&
document
.
activeElement
.
id
!=
"
filterContextInput
"
&&
document
.
activeElement
.
id
!=
"
modalFormInput
"
))
{
// abort when onboarding is shown
...
...
@@ -497,7 +515,7 @@ function registerKeyboardShortcuts() {
}
},
true
)
// shortcuts for modal form
modalForm
.
addEventListener
(
"
key
up
"
,
function
(
event
)
{
modalForm
.
addEventListener
(
"
key
down
"
,
function
(
event
)
{
// priority up
if
(
!
(
event
.
ctrlKey
||
event
.
metaKey
)
&&
event
.
altKey
&&
event
.
key
===
"
ArrowUp
"
)
{
form
.
setPriority
(
"
up
"
);
...
...
@@ -768,11 +786,10 @@ window.onload = async function () {
filters
=
await
import
(
"
./js/filters.mjs
"
);
drawer
=
await
import
(
"
./js/drawer.mjs
"
);
files
=
await
import
(
"
./js/files.mjs
"
);
if
(
userData
.
file
)
{
window
.
api
.
send
(
"
startFileWatcher
"
,
userData
.
file
);
// for users who upgrade from very old versions
}
else
if
(
userData
.
pathToFile
)
{
window
.
api
.
send
(
"
startFileWatcher
"
,
userData
.
pathToFile
);
//TODO: Refactoring
if
(
userData
.
files
)
{
const
index
=
userData
.
files
.
findIndex
(
file
=>
file
[
0
]
===
1
);
window
.
api
.
send
(
"
startFileWatcher
"
,
[
userData
.
files
[
index
][
1
],
0
]);
}
else
{
showOnboarding
(
true
).
then
(
function
(
response
)
{
console
.
info
(
response
);
...
...
@@ -899,4 +916,4 @@ window.api.receive("refresh", async (args) => {
});
});
export
{
resetFilters
,
resetModal
,
setUserData
,
startBuilding
,
handleError
,
userData
,
appData
,
translations
,
modal
,
setTheme
,
getConfirmation
};
export
{
showOnboarding
,
resetFilters
,
resetModal
,
setUserData
,
startBuilding
,
handleError
,
userData
,
appData
,
translations
,
modal
,
setTheme
,
getConfirmation
};
src/scss/tabs.scss
View file @
38bf7348
...
...
@@ -28,7 +28,7 @@
i
{
position
:
absolute
;
top
:
0
.65em
;
right
:
0
.5em
;
right
:
0
.
8
5em
;
color
:
$light-grey
;
padding-left
:
0
.5em
;
}
...
...
test/preferences_empty/todo_done.txt
View file @
38bf7348
...
...
@@ -6,3 +6,4 @@ x 2021-06-19 2021-06-19 test todo
x 2021-07-05 2021-07-05 test todo
x 2021-07-15 2021-07-15 test todo
x 2021-07-17 2021-07-17 test todo
x 2021-07-25 2021-07-25 test todo
test/preferences_empty/user-preferences.json
View file @
38bf7348
{
"theme"
:
"light"
,
"width"
:
1538
,
"height"
:
863
,
"horizontal"
:
2450
,
"vertical"
:
280
,
"maximizeWindow"
:
false
,
"notifications"
:
true
,
"useTextarea"
:
false
,
"compactView"
:
false
,
"matomoEvents"
:
false
,
"drawerWidth"
:
"500"
,
"showDueIsPast"
:
true
,
"showDueIsFuture"
:
true
,
"showDueIsToday"
:
true
,
"showHidden"
:
false
,
"showCompleted"
:
true
,
"sortCompletedLast"
:
false
,
"sortBy"
:[
"priority"
,
"dueString"
,
"contexts"
,
"projects"
],
"zoom"
:
"100"
,
"tray"
:
false
,
"showEmptyFilters"
:
true
,
"dismissedNotifications"
:[
-1319247018
],
"dismissedMessages"
:[],
"hideFilterCategories"
:[],
"language"
:
"en"
,
"uid"
:
"TESTING"
,
"path"
:
"test/preferences_empty/todo.txt"
,
"files"
:[[
1
,
"test/preferences_empty/todo.txt"
]],
"file"
:
"test/preferences_empty/todo.txt"
,
"filterDrawer"
:
false
,
"selectedFilters"
:[],
"viewDrawer"
:
false
,
"sortByLevel"
:[
"priority"
,
"dueString"
,
"contexts"
,
"projects"
],
"deferredTodos"
:
true
,
"fileTabs"
:
true
}
\ No newline at end of file
{
"theme"
:
"light"
,
"width"
:
1538
,
"height"
:
863
,
"horizontal"
:
46
,
"vertical"
:
217
,
"maximizeWindow"
:
false
,
"notifications"
:
true
,
"useTextarea"
:
false
,
"compactView"
:
false
,
"matomoEvents"
:
false
,
"drawerWidth"
:
"500"
,
"showDueIsPast"
:
true
,
"showDueIsFuture"
:
true
,
"showDueIsToday"
:
true
,
"showHidden"
:
false
,
"showCompleted"
:
true
,
"sortCompletedLast"
:
false
,
"sortBy"
:[
"priority"
,
"dueString"
,
"contexts"
,
"projects"
],
"zoom"
:
"100"
,
"tray"
:
false
,
"showEmptyFilters"
:
true
,
"dismissedNotifications"
:[
-1319247018
],
"dismissedMessages"
:[],
"hideFilterCategories"
:[],
"language"
:
"en"
,
"uid"
:
"TESTING"
,
"path"
:
"test/preferences_empty/todo.txt"
,
"files"
:[[
1
,
"test/preferences_empty/todo.txt"
]],
"file"
:
"test/preferences_empty/todo.txt"
,
"filterDrawer"
:
false
,
"selectedFilters"
:[],
"viewDrawer"
:
false
,
"sortByLevel"
:[
"priority"
,
"dueString"
,
"contexts"
,
"projects"
],
"deferredTodos"
:
true
,
"fileTabs"
:
true
}
\ No newline at end of file
test/preferences_existent/user-preferences.json
View file @
38bf7348
{
"theme"
:
"light"
,
"width"
:
1538
,
"height"
:
863
,
"horizontal"
:
2181
,
"vertical"
:
577
,
"maximizeWindow"
:
false
,
"notifications"
:
true
,
"useTextarea"
:
false
,
"compactView"
:
false
,
"matomoEvents"
:
false
,
"drawerWidth"
:
"500"
,
"showDueIsPast"
:
true
,
"showDueIsFuture"
:
true
,
"showDueIsToday"
:
true
,
"showHidden"
:
false
,
"showCompleted"
:
true
,
"sortCompletedLast"
:
false
,
"sortBy"
:[
"priority"
,
"dueString"
,
"contexts"
,
"projects"
],
"zoom"
:
"100"
,
"tray"
:
false
,
"showEmptyFilters"
:
true
,
"dismissedNotifications"
:[
-1319247018
],
"dismissedMessages"
:[],
"hideFilterCategories"
:[],
"language"
:
"en"
,
"uid"
:
"TESTING"
,
"path"
:
"test/preferences_existent/todo.txt"
,
"files"
:[[
1
,
"test/preferences_existent/todo.txt"
]],
"file"