Commit 9d3be623 authored by ransome1's avatar ransome1
Browse files

Fixed archiving function, fixed duplicated todos after file change

parent 9046ded2
......@@ -21,5 +21,6 @@ assets/icons/bak
squashfs-root/
test/
src/__tests__
.eslintrc.json
appx/
eslintrc.json
build/
src/css/
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1000"
height="1000"
viewBox="0 0 264.58333 264.58334"
version="1.1"
id="svg8"
inkscape:version="1.0.2 (e86c870879, 2021-01-15, custom)"
sodipodi:docname="20210324_logo_circle_colored.svg"
inkscape:export-filename="/home/ransome/development/sleek/assets/icons/512x512.png"
inkscape:export-xdpi="49.151997"
inkscape:export-ydpi="49.151997">
<defs
id="defs2">
<linearGradient
id="linearGradient943"
osb:paint="solid">
<stop
style="stop-color:#075a44;stop-opacity:1;"
offset="0"
id="stop941" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient859-6">
<stop
style="stop-color:#075a44;stop-opacity:1"
offset="0"
id="stop855" />
<stop
style="stop-color:#bc30ba;stop-opacity:1"
offset="1"
id="stop857" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient859-6"
id="linearGradient863"
gradientUnits="userSpaceOnUse"
x1="20.609758"
y1="-35.978325"
x2="-376.78461"
y2="-271.12573" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient859-6"
id="linearGradient913"
x1="-264.05746"
y1="-60.426601"
x2="-5.0754561"
y2="-201.82214"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="0.88668073"
inkscape:cx="257.79834"
inkscape:cy="542.20488"
inkscape:document-units="mm"
inkscape:current-layer="layer3"
inkscape:document-rotation="0"
showgrid="false"
units="px"
inkscape:window-width="1874"
inkscape:window-height="1051"
inkscape:window-x="46"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:pagecheckerboard="true"
showborder="false" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="bg">
<rect
style="opacity:1;fill:url(#linearGradient863);fill-opacity:1;stroke:#495f91;stroke-width:0.28639;stroke-opacity:1"
id="rect184"
width="264.29694"
height="264.29694"
x="-264.44012"
y="-264.44012"
inkscape:export-filename="/home/ransome/development/sleek/assets/icons/300x300.png"
inkscape:export-xdpi="28.799999"
inkscape:export-ydpi="28.799999"
transform="scale(-1)"
ry="132.14847" />
<rect
style="opacity:0.531869;mix-blend-mode:normal;fill:url(#linearGradient913);fill-opacity:1;stroke:none;stroke-width:0.28639;stroke-opacity:1"
id="rect184-3"
width="195.59337"
height="195.59337"
x="-230.18045"
y="-231.50604"
inkscape:export-filename="/home/ransome/development/sleek/assets/icons/300x300.png"
inkscape:export-xdpi="28.799999"
inkscape:export-ydpi="28.799999"
transform="scale(-1)"
ry="97.796684" />
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="TDY.TXT">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-size:90.07px;line-height:90%;font-family:FreeSans;-inkscape-font-specification:'FreeSans, Semi-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:-4.50352px;word-spacing:0px;stroke-width:0.264583"
x="130.22496"
y="165.18904"
id="text1012-5"
inkscape:export-filename="/home/ransome/development/sleek/assets/icons/300x300.png"
inkscape:export-xdpi="28.799999"
inkscape:export-ydpi="28.799999"><tspan
x="128.72379"
y="165.18904"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:90.07px;font-family:FreeSans;-inkscape-font-specification:'FreeSans Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:-3.00234px;text-anchor:middle;fill:#ffffff;stroke-width:0.264583"
dx="0 0 0 0 0 0 0 0 0 0 0"
dy="0 0 0 0 0 0 0 0 0 0 0"
rotate="0 0 0 0 0 0 0 0 0 0 0 0 0 0"
sodipodi:role="line"
id="tspan1178-6">sleek</tspan></text>
</g>
</svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1000"
height="1000"
viewBox="0 0 264.58333 264.58334"
version="1.1"
id="svg8"
inkscape:version="1.0.2 (e86c870879, 2021-01-15, custom)"
sodipodi:docname="20210503_sleek_tray.svg"
inkscape:export-filename="/home/ransome/Development/sleek/assets/icons/tray/tray@4x.png"
inkscape:export-xdpi="6.1399999"
inkscape:export-ydpi="6.1399999">
<defs
id="defs2">
<linearGradient
id="linearGradient943"
osb:paint="solid">
<stop
style="stop-color:#075a44;stop-opacity:1;"
offset="0"
id="stop941" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="sleek_gradiant">
<stop
style="stop-color:#075a44;stop-opacity:1"
offset="0"
id="stop855" />
<stop
style="stop-color:#bc30ba;stop-opacity:1"
offset="1"
id="stop857" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#sleek_gradiant"
id="linearGradient863"
gradientUnits="userSpaceOnUse"
x1="20.609758"
y1="-35.978325"
x2="-376.78461"
y2="-271.12573" />
<linearGradient
inkscape:collect="always"
xlink:href="#sleek_gradiant"
id="linearGradient913"
x1="-264.05746"
y1="-60.426601"
x2="-5.0754561"
y2="-201.82214"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="0.69044441"
inkscape:cx="473.99304"
inkscape:cy="542.20488"
inkscape:document-units="mm"
inkscape:current-layer="layer2"
inkscape:document-rotation="0"
showgrid="false"
units="px"
inkscape:window-width="1874"
inkscape:window-height="1051"
inkscape:window-x="46"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:pagecheckerboard="true"
showborder="false" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="outer gradiant">
<rect
style="opacity:1;fill:url(#linearGradient863);fill-opacity:1;stroke:none;stroke-width:0.28639;stroke-opacity:1"
id="rect184"
width="264.29694"
height="264.29694"
x="-264.44012"
y="-264.44012"
inkscape:export-filename="/home/ransome/development/sleek/assets/icons/300x300.png"
inkscape:export-xdpi="28.799999"
inkscape:export-ydpi="28.799999"
transform="scale(-1)"
ry="132.14847" />
</g>
<g
inkscape:groupmode="layer"
id="layer4"
inkscape:label="inner gradiant">
<rect
style="opacity:0.531869;mix-blend-mode:normal;fill:url(#linearGradient913);fill-opacity:1;stroke:none;stroke-width:0.28639;stroke-opacity:1"
id="rect184-3"
width="195.59337"
height="195.59337"
x="-230.18045"
y="-231.50604"
inkscape:export-filename="/home/ransome/development/sleek/assets/icons/300x300.png"
inkscape:export-xdpi="28.799999"
inkscape:export-ydpi="28.799999"
transform="scale(-1)"
ry="97.796684"
rx="97.796684" />
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="sleek (text)"
style="display:none"
sodipodi:insensitive="true">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-size:90.07px;line-height:90%;font-family:FreeSans;-inkscape-font-specification:'FreeSans, Semi-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:-4.50352px;word-spacing:0px;stroke-width:0.264583"
x="130.22496"
y="165.18904"
id="text1012-5"
inkscape:export-filename="/home/ransome/development/sleek/assets/icons/300x300.png"
inkscape:export-xdpi="28.799999"
inkscape:export-ydpi="28.799999"><tspan
x="128.72379"
y="165.18904"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:90.07px;font-family:FreeSans;-inkscape-font-specification:'FreeSans Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;letter-spacing:-3.00234px;text-anchor:middle;fill:#ffffff;stroke-width:0.264583"
dx="0 0 0 0 0 0 0 0 0 0 0"
dy="0 0 0 0 0 0 0 0 0 0 0"
rotate="0 0 0 0 0 0 0 0 0 0 0 0 0 0"
sodipodi:role="line"
id="tspan1178-6">sleek</tspan></text>
</g>
</svg>
{
"name": "sleek",
"productName": "sleek",
"version": "1.0.3",
"version": "1.0.3-1",
"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",
......
......@@ -60,7 +60,7 @@ button {
.button.is-link,
.button.is-link:hover {
color: #4a4a4a !important;
background-color: white;
background-color: white !important;
}
.button.is-link svg,
.button.is-link:hover svg {
......@@ -316,8 +316,10 @@ nav ul li.is-highlighted a {
.modal .modal-content .card .card-header-title {
color: white;
}
.modal .modal-content .card .card-content button {
background: #2d2d2d !important;
.modal .modal-content .card .card-content {
/*button {
background: $darker-grey!important;
}*/
}
.modal .modal-content .card .card-footer,
.modal .modal-content .card .card-footer-item {
......
......@@ -45,7 +45,7 @@ function generateRecurrence(todo) {
if(index===-1) {
items.objects.push(recurringTodo);
//tableContainerDue.appendChild(generateTableRow(recurringTodo));
window.api.send("writeToFile", [items.objects.join("\n").toString(), userData.file]);
window.api.send("writeToFile", [items.objects.join("\n").toString() + "\n", userData.file]);
return Promise.resolve("Success: Recurring todo created and written into file: " + recurringTodo);
} else {
return Promise.resolve("Info: Recurring todo already in file, won't write anything");
......
......@@ -405,7 +405,7 @@ function setTodoComplete(todo) {
if(todo.rec) generateRecurrence(todo)
}
//write the data to the file
window.api.send("writeToFile", [items.objects.join("\n").toString(), userData.file]);
window.api.send("writeToFile", [items.objects.join("\n").toString() + "\n", userData.file]);
return Promise.resolve("Success: Changes written to file: " + userData.file);
} catch(error) {
error.functionName = setTodoComplete.name;
......@@ -434,52 +434,53 @@ function setTodoDelete(todo) {
}
items.objects.splice(index, 1);
//write the data to the file
window.api.send("writeToFile", [items.objects.join("\n").toString(), userData.file]);
window.api.send("writeToFile", [items.objects.join("\n").toString() + "\n", userData.file]);
return Promise.resolve("Success: Changes written to file: " + userData.file);
} catch(error) {
error.functionName = setTodoDelete.name;
return Promise.reject(error);
}
}
function archiveTodos() {
async function archiveTodos() {
try {
// 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.split("/").pop() === "done.txt") return Promise.resolve("Info: Current file seems to be a done.txt file, won't archive")
if(userData.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;
switch (appData.os) {
case "windows":
doneFile = userData.file.replace(userData.file.split("\\").pop(), userData.file.substr(0, userData.file.lastIndexOf(".")).split("\\").pop() + "_done.txt");
break;
default:
doneFile = userData.file.replace(userData.file.split("/").pop(), userData.file.substr(0, userData.file.lastIndexOf(".")).split("/").pop() + "_done.txt");
break;
}
window.api.send("getContent", doneFile);
window.api.receive("getContent", (content) => {
items.doneTxtObjects = new Array;
if(content) items.doneTxtObjects = TodoTxt.parse(content, [ new DueExtension(), new HiddenExtension(), new RecExtension() ]);
// in case done file was not empty the completed todos will be appended
if(items.doneTxtObjects.length>0) {
// only consider completed todo that are not already present in done.txt
items.doneTxtObjects.forEach(itemComplete => {
items.complete = items.complete.filter(function(item) { return item.toString() != itemComplete.toString() });
});
// combine done objects from todo.txt and done.txt
items.complete = items.doneTxtObjects.concat(items.complete);
// write combined objects to done.txt
window.api.send("writeToFile", [items.complete.join("\n").toString(), doneFile]);
// if done.txt did not exist or was empty, file will be created and filled with data
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");
} else {
// if done.txt did not exist or was empty, file will be created and filled with data
window.api.send("writeToFile", [items.complete.join("\n").toString(), doneFile]);
return userData.file.replace(userData.file.split("/").pop(), userData.file.substr(0, userData.file.lastIndexOf(".")).split("/").pop() + "_done.txt");
}
// write incomplete only todos to todo.txt
window.api.send("writeToFile", [items.incomplete.join("\n").toString(), userData.file]);
}
const getContentFromDoneFile = new Promise(function(resolve, reject) {
window.api.send("getContent", doneFile());
return window.api.receive("getContent", (content) => {
//resolve(TodoTxt.parse(content, [ new DueExtension(), new HiddenExtension(), new RecExtension() ]));
resolve(content);
});
});
return Promise.resolve("Success: Completed todo moved to: " + doneFile)
let contentFromDoneFile = await getContentFromDoneFile;
let contentForDoneFile = items.complete;
if(contentFromDoneFile) {
// create array from done file
contentFromDoneFile = contentFromDoneFile.split("\n");
//combine the two arrays
contentForDoneFile = contentFromDoneFile.concat(items.complete.toString().split(","));
// use Set function to remove the duplicates: https://www.javascripttutorial.net/array/javascript-remove-duplicates-from-array/
contentForDoneFile= [...new Set(contentForDoneFile)];
// remove empty entries
contentForDoneFile = contentForDoneFile.filter(function(element) {
return element;
});
}
//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]);
return Promise.resolve("Success: Completed todos appended to: " + doneFile())
} catch(error) {
error.functionName = archiveTodos.name;
return Promise.reject(error);
......
......@@ -91,8 +91,9 @@ viewToggles.forEach(function(viewToggle) {
function zoom(zoom) {
try {
html.style.zoom = zoom + "%";
document.getElementById("zoomStatus").innerHTML = zoom + "%";
const zoomStatus = zoom + "%";
html.style.zoom = zoomStatus;
document.getElementById("zoomStatus").innerHTML = zoomStatus;
zoomRangePicker.value = zoom;
// persist zoom setting
setUserData("zoom", zoom);
......
......@@ -172,25 +172,23 @@ const createWindow = async function() {
userData.data.file = file;
userData.set("file", file);
getContent(file).then(content => {
//mainWindow.webContents.send("userData", userData.data);
mainWindow.webContents.send("refresh", content)
}).catch(error => {
console.error(error);
});
if(fileWatcher) fileWatcher.close();
fileWatcher = fs.watch(file, () => {
if(fs.existsSync(file)) {
setTimeout(function() {
getContent(file).then(content => {
mainWindow.webContents.send("refresh", content)
}).catch(error => {
console.error(error);
});
}, 10);
} else {
mainWindow.webContents.send("triggerFunction", "showOnboarding", [true]);
}
fileWatcher = fs.watch(file, (event, filename) => {
console.log("Info: File " + filename + " has changed");
setTimeout(function() {
getContent(file).then(content => {
mainWindow.webContents.send("refresh", content)
}).catch(error => {
console.log(error);
});
}, 10);
});
return Promise.resolve("Success: Filewatcher is watching: " + file);
} catch (error) {
// if something file related crashes, onboarding will be triggered
......@@ -456,7 +454,6 @@ const createWindow = async function() {
label: translations.windowButtonOpenFile,
click: function() {
mainWindow.show();
//if(!mainWindow.isVisible()) mainWindow.focus();
}
},
{
......@@ -486,7 +483,6 @@ const createWindow = async function() {
// TODO macos exception
tray.on("click", function() {
mainWindow.show();
//if(!mainWindow.isVisible()) mainWindow.show();
});
}
if(userData.data.tray) setupTray();
......@@ -541,11 +537,17 @@ const createWindow = async function() {
app.relaunch();
app.exit();
})
.on("writeToFile", (event, args) => {
// Write content to file
.on("writeToFile", function(event, args) {
try {
// Write content to file
fs.writeFileSync(args[1], args[0], {encoding: "utf-8"});
console.log("File written successfully");
// Restart file watcher
startFileWatcher(userData.data.file).then(response => {
console.info(response);
}).catch(error => {
console.error(error);
});
//console.log("File written successfully");
} catch(error) {
console.error(error);
error.functionName = "fs.writeFileSync";
......
......@@ -155,6 +155,20 @@ function configureMatomo() {
return Promise.reject(error);
}
}
function setWindowTitle(file) {
if(file) {
switch (appData.os) {
case "windows":
document.title = file.split("\\").pop() + " - sleek";
break;
default:
document.title = file.split("/").pop() + " - sleek";
break;
}
} else {
document.title = "sleek";
}
}
function configureMainView() {
try {
// set scaling factor if default font size has changed
......@@ -171,16 +185,7 @@ function configureMainView() {
// jump to previously added item
if(document.getElementById("previousItem")) jumpToItem(document.getElementById("previousItem"))
// add filename to application title
if(userData.file) {
switch (appData.os) {
case "windows":
document.title = userData.file.split("\\").pop() + " - sleek";
break;
default:
document.title = userData.file.split("/").pop() + " - sleek";
break;
}
}
setWindowTitle(userData.file);
// show add todo buttons
btnAddTodo.forEach(item => item.classList.remove("is-hidden"));
// remove onboarding
......@@ -247,20 +252,6 @@ function checkDismissedMessages() {
return Promise.reject(error);
}
}
/*function getUserData() {
try {
window.api.send("userData");
return new Promise(function(resolve) {
return window.api.receive("userData", (data) => {
userData = data;
resolve("Success: User data received");
});
});
} catch(error) {
error.functionName = getUserData.name;
return Promise.reject(error);
}
}*/
function getUserData() {
try {
window.api.send("userData");
......@@ -764,7 +755,6 @@ function showFiles() {
} else {
cell1.innerHTML = "<button class=\"button is-link\">" + translations.select + "</button>";
cell1.onclick = function() {
console.log(this.parentElement.getAttribute("data-path"));
window.api.send("startFileWatcher", this.parentElement.getAttribute("data-path"));
resetModal().then(response => {
console.info(response);
......@@ -796,7 +786,6 @@ function showFiles() {
}
return Promise.resolve("Success: File changer modal built and opened");
} catch (error) {
//error.functionName = arguments.callee.name;
return Promise.reject(error);
}
}
......@@ -969,12 +958,13 @@ window.api.receive("triggerFunction", (name, args) => {
}
});
window