Commit fe080b66 authored by ransome1's avatar ransome1
Browse files

GUI refresh

parent abf8c0c4
......@@ -12,14 +12,12 @@ snap/local/
*.db:encryptable
*.db
System Volume Information/
build/
Thumbs.db
flatpak/generated-sources.json
flatpak/com.github.ransome1.sleek.yml
assets/icons/bak
squashfs-root/
.eslintrc.json
build/
package-lock.json
.vs/
.vscode/
......
{
"name": "sleek",
"productName": "sleek",
"version": "1.0.7",
"version": "1.0.8",
"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",
......@@ -92,17 +92,16 @@
"artifactName": "${productName}-${version}-${arch}.${ext}"
},
"scripts": {
"pack": "yarn build:css && electron-builder --dir",
"build:windows": "yarn build:css && electron-builder -w --publish never",
"build:macos": "yarn build:css && electron-builder -m --publish never",
"build:linux": "yarn build:css && electron-builder -l --publish never",
"build:appx": "electron-builder -w appx --publish never",
"build:pacman": "electron-builder -l pacman --publish never",
"build:appx": "yarn build:css && electron-builder -w appx --publish never",
"build:appimage": "yarn build:css && yarn build:pegjs && electron-builder -l AppImage --publish never",
"pack": "electron-builder --dir",
"build:css": "sass src/scss/style.scss:src/css/style.css",
"lint": "eslint --ext .js, src --ext .mjs, src",
"test": "mocha --timeout 10000",
"test1": "mocha ./test/onboarding.js --timeout 10000",
"build:css": "sass src/scss/style.scss:src/css/style.css",
"sass": "sass -w src/scss/style.scss:src/css/style.css",
"start": "yarn sass & electron ."
},
......
export function createModalJail(modal) {
// add all the elements inside modal which you want to make focusable
const focusableElements = 'a.button, [tabindex]:not([tabindex="-1"])';
const focusableElements = '[tabindex]:not([tabindex="-1"])';
const firstFocusableElement = modal.querySelectorAll(focusableElements)[0]; // get first element to be focused inside modal
const focusableContent = modal.querySelectorAll(focusableElements);
const lastFocusableElement = focusableContent[focusableContent.length - 1]; // get last element to be focused inside modal
......
This diff is collapsed.
This diff is collapsed.
......@@ -24,15 +24,15 @@
<nav>
<ul>
<li class="logo">sleek</li>
<li id="navBtnAddTodo" tabindex="0"><i class="fas fa-plus"></i></li>
<li id="navBtnFilter" class="drawerTrigger" data-drawer="filterDrawer" tabindex="0"><i class="fas fa-filter"></i></li>
<li id="navBtnView" class="drawerTrigger" data-drawer="viewDrawer"tabindex="0"><i class="fas fa-sliders-h"></i></li>
<li id="btnOpenTodoFile" tabindex="-1"><i class="fas fa-folder-open"></i></li>
<li id="btnTheme" tabindex="-1"><i class="fas fa-adjust"></i></li>
<li><a href="#" id="navBtnAddTodo" tabindex="0"><i class="fas fa-plus"></i></a></li>
<li><a href="#" id="navBtnFilter" class="drawerTrigger" data-drawer="filterDrawer" tabindex="0"><i class="fas fa-filter"></i></a></li>
<li><a href="#" id="navBtnView" class="drawerTrigger" data-drawer="viewDrawer"tabindex="0"><i class="fas fa-sliders-h"></i></a></li>
<li><a href="#" id="btnOpenTodoFile" tabindex="-1"><i class="fas fa-folder-open"></i></a></li>
<li><a href="#" id="btnTheme" tabindex="-1"><i class="fas fa-adjust"></i></a></li>
</ul>
<ul>
<li id="navBtnSettings" tabindex="-1"><i class="fas fa-cog"></i></li>
<li id="navBtnHelp" tabindex="-1"><i class="fas fa-question-circle"></i></li>
<li><a href="#" id="navBtnSettings" tabindex="-1"><i class="fas fa-cog"></i></a></li>
<li><a href="#" id="navBtnHelp" tabindex="-1"><i class="fas fa-question-circle"></i></a></li>
</ul>
<div id="drawerContainer">
<section id="filterDrawer" class="drawer dropdown" tabindex="0">
......@@ -187,25 +187,19 @@
<div id="todoTableWrapper" class="column content">
<section id="todoTableSearchContainer" class="control has-icons-left">
<div class="column">
<input id="todoTableSearch" class="input is-medium" type="text" placeholder="Search" tabindex="10">
<span class="icon is-left">
<i class="fas fa-search"></i>
</span>
</div>
<section id="todoTableSearchContainer" class="inputWrapper">
<i class="fas fa-search"></i>
<label id="todoTableSearchLabel" for="todoTableSearch">Search by todo.txt syntax</label>
<input id="todoTableSearch" class="input is-medium" type="search" tabindex="1" placeholder="(A) Todo text +project @context due:2020-12-12 rec:d">
<a href="#" id="todoTableSearchAddTodo" class="tag" tabindex="5"><i class="fas fa-plus"></i>&nbsp;Add as new todo</a>
</section>
<div id="resultStats">
<section id="resultStats">
<span class="tag"></span>
</div>
<section id="todoTable" tabindex="-1">
<div id="todoTableContainer"></div>
</section>
<section id="todoTable" tabindex="-1"></section>
<section id="onboardingContainer" class="contentContainer">
<h1 id="welcomeToSleek" class="title is-1"></h1>
<p id="onboardingContainerSubtitle" class="subtitle"></p>
......@@ -241,20 +235,19 @@
</div>
</div>
<form id="modalForm" class="modal modalFormSubmit">
<form id="modalForm" class="modal">
<div class="modal-background"></div>
<div class="modal-content">
<div class="card">
<header id="modalTitle" class="card-header-title"></header>
<div class="card-content">
<div class="content">
<div class="control has-icons-right">
<input id="modalFormInput" class="input is-medium" type="text" tabindex="0">
<div id="autoCompleteContainer" class="card"></div>
<a href="#" id="modalFormInputResize" class="icon is-right" tabindex="-1" data-input-type="input">
<i class="fas fa-expand-alt"></i>
</a>
<div class="inputWrapper">
<label id="modalFormInputLabel" for="modalFormInput">Search by todo.txt syntax</label>
<input id="modalFormInput" class="input is-medium" type="text" tabindex="0" placeholder="(A) Todo text +project @context due:2020-12-12 rec:d">
<a href="#" id="modalFormInputResize" class="icon is-right" tabindex="-1" data-input-type="input"><i class="fas fa-expand-alt"></i></a>
</div>
<div id="autoCompleteContainer" class="card"></div>
<article class="message">
<div id="modalFormAlert" class="message-body"></div>
</article>
......@@ -342,12 +335,11 @@
</div>
</div>
<footer class="card-footer">
<a href="#" class="card-footer-item selection" value="" id="recurrencePickerNoRecurrence" tabindex="0"></a>
<a href="#" class="card-footer-item selection" id="recurrencePickerNoRecurrence" tabindex="0"></a>
</footer>
</div>
</div>
<br />
</div>
</div>
<footer class="card-footer">
<button id="btnSave" type="submit" class="card-footer-item" tabindex="0"></button>
......@@ -657,11 +649,13 @@
</div>
</div>
<div id="todoContext" class="dropdown-menu" role="menu" tabindex="0">
<div class="dropdown-content">
<a id="todoContextUseAsTemplate" class="dropdown-item" tabindex="0"></a>
<a id="todoContextEdit" href="#" class="dropdown-item" tabindex="0"></a>
<a id="todoContextDelete" class="dropdown-item" tabindex="0"></a>
<div id="todoContext" role="menu" tabindex="0">
<div class="card">
<div class="card-content">
<a id="todoContextUseAsTemplate" class="dropdown-item" tabindex="0"></a>
<a id="todoContextEdit" href="#" class="dropdown-item" tabindex="0"></a>
<a id="todoContextDelete" class="dropdown-item" tabindex="0"></a>
</div>
</div>
</div>
......
......@@ -6,7 +6,7 @@ import { getHandleElement, startDragging } from "./drawer_handle.mjs";
const drawerContainer = document.getElementById("drawerContainer");
const navBtnFilter = document.getElementById("navBtnFilter");
const navBtnView = document.getElementById("navBtnView");
const drawers = document.querySelectorAll("nav ul li.drawerTrigger");
const drawers = document.querySelectorAll("nav ul li a.drawerTrigger");
if(userData.filterDrawer) {
show(document.getElementById("navBtnFilter"), document.getElementById("navBtnFilter").getAttribute("data-drawer")).then(function(result) {
......
......@@ -14,17 +14,17 @@ const recurrencePickerInput = document.getElementById("recurrencePickerInput");
const modalTitle = document.getElementById("modalTitle");
const modalFormAlert = document.getElementById("modalFormAlert");
const modalForm = document.getElementById("modalForm");
const modalFormInputLabel = document.getElementById("modalFormInputLabel");
const modalFormInputResize = document.getElementById("modalFormInputResize");
const modalBackground = document.querySelectorAll('.modal-background');
const modalClose = document.querySelectorAll('.close');
const priorityPicker = document.getElementById("priorityPicker");
const btnItemStatus = document.getElementById("btnItemStatus");
document.getElementById("modalFormInput").placeholder = translations.formTodoInputPlaceholder;
modalFormInputLabel.innerHTML = translations.todoTxtSyntax;
btnItemStatus.onclick = function() {
setTodoComplete(modalForm.getAttribute("data-item")).then(response => {
//modalForm.classList.remove("is-active");
resetModal().then(function(result) {
console.log(result);
}).catch(function(error) {
......@@ -44,9 +44,18 @@ modalFormInputResize.onclick = function() {
}
document.getElementById("modalFormInput").addEventListener("keyup", event => {
// do not show suggestion container if Escape has been pressed
if(event.key==="Escape") return false;
if(event.key==="Escape") {
autoCompleteContainer.classList.remove("is-active");
return false;
}
modalFormInputEvent();
});
document.getElementById("modalFormInput").onfocus = function() {
modalForm.classList.add("is-focused");
}
document.getElementById("modalFormInput").onblur = function() {
modalForm.classList.remove("is-focused");
}
modalForm.addEventListener("submit", function(event) {
// intercept submit
event.preventDefault();
......@@ -115,7 +124,7 @@ function positionAutoCompleteContainer() {
// Adjust position of suggestion box to input field
let modalFormInputPosition = document.getElementById("modalFormInput").getBoundingClientRect();
autoCompleteContainer.style.width = document.getElementById("modalFormInput").offsetWidth + "px";
autoCompleteContainer.style.top = modalFormInputPosition.top + document.getElementById("modalFormInput").offsetHeight+2 + "px";
autoCompleteContainer.style.top = modalFormInputPosition.top + document.getElementById("modalFormInput").offsetHeight - 40 + "px";
autoCompleteContainer.style.left = modalFormInputPosition.left + "px";
}
function modalFormInputEvent() {
......@@ -404,6 +413,7 @@ function submitForm() {
return Promise.reject(error);
}
}
function toggleInputSize(type) {
let newInputElement;
switch (type) {
......@@ -424,7 +434,7 @@ function toggleInputSize(type) {
newInputElement.id = "modalFormInput";
newInputElement.setAttribute("tabindex", 0);
newInputElement.setAttribute("class", "input is-medium");
newInputElement.setAttribute("placeholder", translations.formTodoInputPlaceholder);
//newInputElement.setAttribute("placeholder", translations.formTodoInputPlaceholder);
// replace old element with the new one
document.getElementById("modalFormInput").replaceWith(newInputElement);
// replace special char with line break before passing it to textarea
......@@ -437,6 +447,12 @@ function toggleInputSize(type) {
document.getElementById("modalFormInput").addEventListener("keyup", () => {
modalFormInputEvent();
});
document.getElementById("modalFormInput").onfocus = function() {
modalForm.classList.add("is-focused");
}
document.getElementById("modalFormInput").onblur = function() {
modalForm.classList.remove("is-focused");
}
document.getElementById("modalFormInput").focus();
createModalJail(modalForm);
}
......
......@@ -49,7 +49,7 @@ function configureMatomo() {
if(typeof userData.showDueIsToday === "boolean")_paq.push(['setCustomDimension', 13, userData.showDueIsToday]);
if(typeof userData.showDueIsFuture === "boolean")_paq.push(['setCustomDimension', 14, userData.showDueIsFuture]);
if(typeof userData.showDueIsPast === "boolean")_paq.push(['setCustomDimension', 15, userData.showDueIsPast]);
if(userData.sortBy)_paq.push(['setCustomDimension', 16, userData.sortBy]);
if(userData.sortBy)_paq.push(['setCustomDimension', 16, userData.sortBy.join(", ")]);
if(userData.zoom)_paq.push(['setCustomDimension', 17, userData.zoom]);
if(appData.channel)_paq.push(['setCustomDimension', 18, appData.channel]);
if(typeof userData.tray === "boolean")_paq.push(['setCustomDimension', 19, userData.tray]);
......
......@@ -39,7 +39,7 @@ recurrencePickerContainer.addEventListener("keyup", function(event) {
export function setInput(recurrence) {
try {
let recSplit = recurrences.splitRecurrence(recurrence);
let label = translations.noRecurrence;
let label;
if(recSplit.period !== undefined) {
if(recSplit.mul > 1) {
switch (recSplit.period) {
......@@ -79,8 +79,10 @@ export function setInput(recurrence) {
break;
}
}
recurrencePickerInput.value = label;
} else {
recurrencePickerInput.value = null;
}
recurrencePickerInput.value = label;
resizeInput(recurrencePickerInput);
// trigger matomo event
if(userData.matomoEvents) _paq.push(["trackEvent", "Form", "Recurrence selected: " + label]);
......
"use strict";
import { translations, startBuilding } from "../render.js";
import { translations, startBuilding, handleError, userData } from "../render.js";
import { addTodo } from "./todos.mjs";
const todoTableSearch = document.getElementById("todoTableSearch");
const todoTableSearchAddTodo = document.getElementById("todoTableSearchAddTodo");
const todoTableSearchContainer = document.getElementById("todoTableSearchContainer");
todoTableSearch.placeholder = translations.search;
todoTableSearchLabel.innerHTML = translations.todoTxtSyntax;
todoTableSearch.addEventListener("input", debounce(function() {
startBuilding()
if(this.value) {
todoTableSearchAddTodo.classList.add("is-active");
} else {
todoTableSearchAddTodo.classList.remove("is-active");
}
startBuilding();
}, 250));
todoTableSearch.onfocus = function() {
if(this.value) {
todoTableSearchAddTodo.classList.add("is-active");
} else {
todoTableSearchAddTodo.classList.remove("is-active");
}
todoTableSearchContainer.classList.add("is-focused");
}
todoTableSearch.onblur = function() {
todoTableSearchContainer.classList.remove("is-focused");
}
// shortcuts for search input field
todoTableSearch.addEventListener("keyup", function () {
if(event.key === "Escape") todoTableSearch.blur();
......@@ -21,6 +42,18 @@ window.addEventListener("keyup", function () {
}
});
todoTableSearchAddTodo.onclick = function() {
addTodo(todoTableSearch.value).then(response => {
console.log(response);
document.getElementById("todoTableSearch").value = null;
document.getElementById("todoTableSearch").focus();
// trigger matomo event
if(userData.matomoEvents) _paq.push(["trackEvent", "Search", "Click on Add as new todo"]);
}).catch(error => {
handleError(error);
});
}
// https://davidwalsh.name/javascript-debounce-function
function debounce(func, wait, immediate) {
let timeout;
......
"use strict";
import "../../node_modules/jstodotxt/jsTodoExtensions.js";
import { userData, appData, handleError, translations, setUserData, startBuilding, getConfirmation } from "../render.js";
import { userData, appData, handleError, translations, setUserData, startBuilding, getConfirmation, resetModal } from "../render.js";
import { _paq } from "./matomo.mjs";
import { categories } from "./filters.mjs";
import { generateRecurrence } from "./recurrences.mjs";
import { convertDate, isToday, isTomorrow, isPast } from "./date.mjs";
import { show } from "./form.mjs";
import { RecExtension } from "./todotxtExtensions.mjs";
import { SugarDueExtension, RecExtension } from "./todotxtExtensions.mjs";
const modalForm = document.getElementById("modalForm");
const todoContext = document.getElementById("todoContext");
const todoContextDelete = document.getElementById("todoContextDelete");
const todoContextEdit = document.getElementById("todoContextEdit");
const todoContextUseAsTemplate = document.getElementById("todoContextUseAsTemplate");
const todoTableContainer = document.getElementById("todoTableContainer");
const todoTableWrapper = document.getElementById("todoTableWrapper");
todoContextUseAsTemplate.innerHTML = translations.useAsTemplate;
......@@ -78,7 +77,7 @@ function configureTodoTableTemplate(append) {
try {
// setting up for the first cluster
if(!append) {
todoTableContainer.innerHTML = "";
todoTable.innerHTML = "";
visibleRows = 0;
clusterThreshold = 0;
stopBuilding = false;
......@@ -115,7 +114,7 @@ function generateItems(content) {
}
}
function generateGroups(items) {
const sortBy = userData.sortByLevel[0];
const sortBy = userData.sortBy[0];
// build object according to sorting method
items = items.reduce((object, a) => {
if(userData.sortCompletedLast && a.complete) {
......@@ -167,27 +166,27 @@ async function generateTable(groups, append) {
let dividerRow;
// completed todos
if(userData.sortCompletedLast && groups[group][0]==="completed") {
dividerRow = document.createRange().createContextualFragment("<div id=\"" + userData.sortByLevel[0] + groups[group][0] + "\" class=\"group " + userData.sortByLevel[0] + " " + groups[group][0] + "\"><div class=\"cell\"></div></div>")
dividerRow = document.createRange().createContextualFragment("<div id=\"" + userData.sortBy[0] + groups[group][0] + "\" class=\"group " + userData.sortBy[0] + " " + groups[group][0] + "\"><div class=\"cell\"></div></div>")
// for priority, context and project
} else if(groups[group][0]!="null" && userData.sortByLevel[0]!="dueString") {
dividerRow = document.createRange().createContextualFragment("<div id=\"" + userData.sortByLevel[0] + groups[group][0] + "\" class=\"group " + userData.sortByLevel[0] + " " + groups[group][0] + "\"><div class=\"cell\"><span class=\"button " + groups[group][0] + "\">" + groups[group][0].replace(/,/g, ', ') + "</span></div></div>")
} else if(groups[group][0]!="null" && userData.sortBy[0]!="dueString") {
dividerRow = document.createRange().createContextualFragment("<div id=\"" + userData.sortBy[0] + groups[group][0] + "\" class=\"group " + userData.sortBy[0] + " " + groups[group][0] + "\"><div class=\"cell\"><span class=\"button " + groups[group][0] + "\">" + groups[group][0].replace(/,/g, ', ') + "</span></div></div>")
// if sorting is by due date
} else if(userData.sortByLevel[0]==="dueString" && groups[group][1][0].due) {
} else if(userData.sortBy[0]==="dueString" && groups[group][1][0].due) {
if(isToday(groups[group][1][0].due)) {
dividerRow= document.createRange().createContextualFragment("<div id=\"" + userData.sortByLevel[0] + groups[group][0] + "\" class=\"group due\"><div class=\"cell isToday\"><span class=\"button\">" + translations.today + "</span></div></div>")
dividerRow= document.createRange().createContextualFragment("<div id=\"" + userData.sortBy[0] + groups[group][0] + "\" class=\"group due\"><div class=\"cell isToday\"><span class=\"button\">" + translations.today + "</span></div></div>")
} else if(isTomorrow(groups[group][1][0].due)) {
dividerRow = document.createRange().createContextualFragment("<div id=\"" + userData.sortByLevel[0] + groups[group][0] + "\" class=\"group due\"><div class=\"cell isTomorrow\"><span class=\"button\">" + translations.tomorrow + "</span></div></div>")
dividerRow = document.createRange().createContextualFragment("<div id=\"" + userData.sortBy[0] + groups[group][0] + "\" class=\"group due\"><div class=\"cell isTomorrow\"><span class=\"button\">" + translations.tomorrow + "</span></div></div>")
} else if(isPast(groups[group][1][0].due)) {
dividerRow = document.createRange().createContextualFragment("<div id=\"" + userData.sortByLevel[0] + groups[group][0] + "\" class=\"group due\"><div class=\"cell isPast\"><span class=\"button\">" + groups[group][0] + "</span></div></div>")
dividerRow = document.createRange().createContextualFragment("<div id=\"" + userData.sortBy[0] + groups[group][0] + "\" class=\"group due\"><div class=\"cell isPast\"><span class=\"button\">" + groups[group][0] + "</span></div></div>")
} else {
dividerRow = document.createRange().createContextualFragment("<div id=\"" + userData.sortByLevel[0] + groups[group][0] + "\" class=\"group due\"><div class=\"cell\"><span class=\"button\">" + groups[group][0] + "</span></div></div>")
dividerRow = document.createRange().createContextualFragment("<div id=\"" + userData.sortBy[0] + groups[group][0] + "\" class=\"group due\"><div class=\"cell\"><span class=\"button\">" + groups[group][0] + "</span></div></div>")
}
// create an empty divider row
} else {
dividerRow = document.createRange().createContextualFragment("<div class=\"group\"></div>")
}
// add divider row only if it doesn't exist yet
if(!append && !document.getElementById(userData.sortByLevel[0] + groups[group][0]) && dividerRow) tableContainerContent.appendChild(dividerRow);
if(!append && !document.getElementById(userData.sortBy[0] + groups[group][0]) && dividerRow) tableContainerContent.appendChild(dividerRow);
for (let item in groups[group][1]) {
//
if(clusterCounter<clusterThreshold) {
......@@ -221,7 +220,7 @@ async function generateTable(groups, append) {
tableContainerContent.appendChild(generateTableRow(todo));
}
}
todoTableContainer.appendChild(tableContainerContent);
todoTable.appendChild(tableContainerContent);
return Promise.resolve("Success: Todo table generated");
} catch(error) {
error.functionName = archiveTodos.name;
......@@ -253,7 +252,7 @@ function generateTableRow(todo) {
}
todoTableBodyRow.setAttribute("data-item", todo.toString());
// add the priority marker or a white spacer
if(todo.priority && userData.sortByLevel[0]==="priority") {
if(todo.priority && userData.sortBy[0]==="priority") {
todoTableBodyCellPriority.setAttribute("class", "cell priority " + todo.priority);
todoTableBodyRow.appendChild(todoTableBodyCellPriority);
}
......@@ -280,7 +279,7 @@ function generateTableRow(todo) {
// add archiving icon
if(todo.complete) {
todoTableBodyCellArchive.setAttribute("class", "cell archive");
todoTableBodyCellArchive.innerHTML = "<i class=\"fas fa-archive\"></i>";
todoTableBodyCellArchive.innerHTML = "<a href=\"#\"><i class=\"fas fa-archive\"></i></a>";
todoTableBodyCellArchive.onclick = function() {
getConfirmation(archiveTodos, translations.archivingPrompt);
// trigger matomo event
......@@ -299,7 +298,7 @@ function generateTableRow(todo) {
}
// creates cell for the text
if(todo.text) {
if(todo.priority && userData.sortByLevel[0]!="priority") todoTableBodyCellText.innerHTML = "<span class=\"priority\"><span class=\"button " + todo.priority + "\">" + todo.priority + "</span></span>";
if(todo.priority && userData.sortBy[0]!="priority") todoTableBodyCellText.innerHTML = "<span class=\"priority\"><span class=\"button " + todo.priority + "\">" + todo.priority + "</span></span>";
// parse text string through markdown parser
todoTableBodyCellText.innerHTML += "<span class=\"text\">" + marked.parseInline(todo.text) + "</span>";
// replace line feed character with a space
......@@ -359,30 +358,30 @@ function generateTableRow(todo) {
// add the text cell to the row
todoTableBodyRow.appendChild(todoTableBodyCellText);
todoTableBodyRow.addEventListener("contextmenu", event => {
todoContext.focus();
//todoContextUseAsTemplate.focus();
todoContext.style.left = event.x + "px";
todoContext.style.top = event.y + "px";
todoContext.classList.toggle("is-active");
todoContext.setAttribute("data-item", todo.toString())
// click on use as template option
todoContext.firstElementChild.children[0].onclick = function() {
show(this.parentElement.parentElement.getAttribute('data-item'), true);
todoContextUseAsTemplate.onclick = function() {
show(todoContext.getAttribute('data-item'), true);
todoContext.classList.toggle("is-active");
todoContext.removeAttribute("data-item");
// trigger matomo event
if(userData.matomoEvents) _paq.push(["trackEvent", "Todo-Table-Context", "Click on Use as template"]);
}
todoContext.firstElementChild.children[1].onclick = function() {
show(this.parentElement.parentElement.getAttribute('data-item'));
todoContextEdit.onclick = function() {
show(todoContext.getAttribute('data-item'));
todoContext.classList.toggle("is-active");
todoContext.removeAttribute("data-item");
// trigger matomo event
if(userData.matomoEvents) _paq.push(["trackEvent", "Todo-Table-Context", "Click on Edit"]);
}
// click on delete
todoContext.firstElementChild.children[2].onclick = function() {
todoContextDelete.onclick = function() {
// passing the data-item attribute of the parent tag to complete function
setTodoDelete(this.parentElement.parentElement.getAttribute('data-item')).then(response => {
setTodoDelete(todoContext.getAttribute('data-item')).then(response => {
console.log(response);
todoContext.classList.toggle("is-active");
todoContext.removeAttribute("data-item");
......@@ -402,14 +401,14 @@ function generateTableRow(todo) {
}
function sortTodoData(group) {
try {
for(let i = 1; i < userData.sortByLevel.length; i++) {
for(let i = 1; i < userData.sortBy.length; i++) {
group.sort(function(a, b) {
// only continue if the two items have the same filters from the previous iteration
if(i>1 && JSON.stringify(a[userData.sortByLevel[i-2]]) !== JSON.stringify(b[userData.sortByLevel[i-2]]) ) return;
if(i>1 && JSON.stringify(a[userData.sortByLevel[i-1]]) !== JSON.stringify(b[userData.sortByLevel[i-1]]) ) return;
if(i>1 && JSON.stringify(a[userData.sortBy[i-2]]) !== JSON.stringify(b[userData.sortBy[i-2]]) ) return;
if(i>1 && JSON.stringify(a[userData.sortBy[i-1]]) !== JSON.stringify(b[userData.sortBy[i-1]]) ) return;
let
item1 = a[userData.sortByLevel[i]],
item2 = b[userData.sortByLevel[i]];
item1 = a[userData.sortBy[i]],
item2 = b[userData.sortBy[i]];
// when item1 is empty or bigger than item2 it will be sorted after item2
if(!item1 && item2 || item1 > item2) {
return 1;
......@@ -497,6 +496,29 @@ function setTodoDelete(todo) {
return Promise.reject(error);
}
}
function addTodo(todo) {
try {
todo = new TodoTxtItem(todo, [ new SugarDueExtension(), new HiddenExtension(), new RecExtension() ]);
// abort if there is no text
if(!todo.text) return Promise.resolve("Info: Text is missing, no todo is written");
// we add the current date to the start date attribute of the todo.txt object
todo.date = new Date();
// get index of todo
const index = items.objects.map(function(item) { return item.toString(); }).indexOf(todo.toString());
if(index===-1) {
// we build the array
items.objects.push(todo);
//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]);
return Promise.resolve("Success: New todo added to file: " + userData.file);
} else {
return Promise.resolve("Info: Todo already in file, nothing will be written");
}
} catch (error) {
return Promise.reject(error);
}
}
async function archiveTodos() {
try {
// cancel operation if there are no completed todos
......@@ -606,4 +628,4 @@ function generateHash(string) {
(((prevHash << 5) - prevHash) + currVal.charCodeAt(0))|0, 0);
}
export { generateItems, generateGroups, generateTable, items, item, visibleRows, setTodoComplete, archiveTodos };
export { generateItems, generateGroups, generateTable, items, item, visibleRows, setTodoComplete, archiveTodos, addTodo };
"use strict";
import { userData, setUserData, handleError, startBuilding, translations, resetModal } from "../render.js";
import { _paq } from "./matomo.mjs";
//import { dragonfly } from "../configs/dragonfly.mjs";
const html = document.getElementById("html");
const body = document.getElementById("body");
......@@ -53,8 +52,8 @@ viewToggleZoom.innerHTML = translations.viewToggleZoom;
viewToggleShowEmptyFilters.innerHTML = translations.viewToggleShowEmptyFilters;
// build the sort by list
for(let i=0; i < userData.sortByLevel.length; i++) {
let sortBy = userData.sortByLevel[i];
for(let i=0; i < userData.sortBy.length; i++) {