Commit a1b0003a authored by ransome1's avatar ransome1
Browse files

Minor cleanups

parent 43b83e8b
# sleek
## sleek is a todo app based on todo.txt, free and open-source. Available for Linux, Windows and MacOS
## sleek is a free and open-source (FOSS) todo manager that makes use of the todo.txt format. Available for Linux, Windows and MacOS
+ [Screenshots](#screenshots)
+ [Support sleek](#support-sleek)
+ [Get it from Microsoft Store](#get-sleek-from-microsoft-store)
......@@ -13,7 +13,7 @@
+ [Features](#features)
+ [Used libraries](#used-libraries)
sleek is an open-source todo app that makes use of the todo.txt format. sleeks GUI is modern and simple but still offers a decent set of functions which help users getting things done. sleek is available as a client for Windows, MacOS and Linux.
sleek is a free and open-source (FOSS) todo manager that makes use of the todo.txt format. sleeks GUI is modern and simple but still offers a decent set of functions which help users getting things done. sleek is available as a client for Windows, MacOS and Linux.
Users can add contexts, projects, priorities, due dates, recurrences or threshold dates to their todos. These todo.txt attributes can then be used in full-text search, as filters or to group and sort the todo list.
......@@ -94,7 +94,7 @@ A prioritized backlog of new features and known issues can be found <a href="htt
* Due dates trigger alarms and appear as badges in sleeks icon
* Dark and light mode can be toggled
* A compact view is available
* Completed todos can be bulk archived to a separate done.txt ([name of todo file]_done.txt) file
* Completed todos can be bulk archived to a separate done.txt file
* Completed todos can be shown or hidden
* Multi line todos can be created
* Filters can be renamed or deleted by right clicking on them
......
......@@ -2,8 +2,8 @@
"name": "sleek",
"productName": "sleek",
"version": "1.1.7-rc.2",
"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",
"description": "todo.txt manager for Linux, Windows and MacOS, free and open-source (FOSS)",
"synopsis": "todo.txt manager for Linux, Windows and MacOS, free and open-source (FOSS)",
"category": "ProjectManagement",
"keywords": [
"todo.txt",
......@@ -75,7 +75,7 @@
"artifactName": "${productName}-${version}-win.${ext}"
},
"appx": {
"displayName": "sleek - Todo.txt app for Windows, free and open-source",
"displayName": "sleek - todo.txt manager for Windows, free and open-source (FOSS)",
"identityName": "17450RobinAhle.sleektodomanager",
"publisher": "CN=2B3D4037-FF2E-4C36-84A6-CFF49F585C0C",
"publisherDisplayName": "Robin Ahle",
......
......@@ -5,5 +5,5 @@ Terminal=false
Type=Application
Icon=${SNAP}/meta/gui/icon.png
StartupWMClass=sleek
Comment=Todo app based on the todo.txt format for Linux, free and open-source
Comment=todo.txt manager for Linux, free and open-source (FOSS)
Categories=ProjectManagement
name: sleek
base: core18
version: "1.1.7"
summary: Todo app based on the todo.txt format for Linux, free and open-source
summary: todo.txt manager for Linux, free and open-source (FOSS)
description: |
sleek is an open-source todo app that makes use of the todo.txt format. sleeks GUI is modern and simple but still offers a decent set of functions which help users getting things done. sleek is available as a client for Windows, MacOS and Linux.
sleek is a free and open-source (FOSS) todo manager that makes use of the todo.txt format. sleeks GUI is modern and simple but still offers a decent set of functions which help users getting things done. sleek is available as a client for Windows, MacOS and Linux.
Users can add contexts, projects, priorities, due dates or recurrences to their todos. These todo.txt attributes can then be used as filters or to group and sort the todo list.
......
......@@ -204,15 +204,15 @@ body.dark #todoTable .todo .cell.itemDueDate i {
body.dark #todoTable .todo .cell.itemDueDate i.fa-sort-down {
color: #5a5a5a;
}
body.dark #todoTable .todo .cell.itemDueDate.isToday,
body.dark #todoTable .todo .cell.itemDueDate.isPast {
body.dark #todoTable .todo .cell.itemDueDate.today,
body.dark #todoTable .todo .cell.itemDueDate.past {
color: #ff3860;
}
body.dark #todoTable .todo .cell.itemDueDate.isToday i,
body.dark #todoTable .todo .cell.itemDueDate.isPast i {
body.dark #todoTable .todo .cell.itemDueDate.today i,
body.dark #todoTable .todo .cell.itemDueDate.past i {
color: inherit;
}
body.dark #todoTable .todo .cell.itemDueDate.isTomorrow i {
body.dark #todoTable .todo .cell.itemDueDate.tomorrow i {
color: #fa745e !important;
}
body.dark #todoTable .todo .cell a.contexts {
......@@ -1709,7 +1709,7 @@ body.compact #autoCompleteContainer h4 {
#modalForm #modalFormInput {
z-index: 60;
padding-right: 3em;
padding-right: 2em !important;
}
#modalForm textarea {
min-height: 5.5em;
......
......@@ -122,7 +122,7 @@ function deleteFilter(filter, category) {
}
}
function filterItems() {
function applyFilters() {
try {
items.filtered = items.objects;
......@@ -151,40 +151,6 @@ function filterItems() {
});
}
// TODO: understand
if(todoTableSearch.value) { // assume that this is an advanced search expr
let queryString = todoTableSearch.value;
try {
let query = filterlang.parse(queryString);
if (query.length > 0) {
items.filtered = items.filtered.filter(function(item) {
return runQuery(item, query);
});
lastFilterQueryString = queryString;
lastFilterItems = items.filtered;
todoTableSearch.classList.add("is-valid-query");
todoTableSearch.classList.remove("is-previous-query");
}
} catch(error) {
// oops, that wasn't a syntactically correct search expression
todoTableSearch.classList.remove("is-valid-query");
if (lastFilterQueryString && queryString.startsWith(lastFilterQueryString)) {
// keep table more stable by using the previous valid query while
// user continues to type additional query syntax.
items.filtered = lastFilterItems;
todoTableSearch.classList.add("is-previous-query");
} else {
// the query is not syntactically correct and isn't a longer version
// of the last working query, so let's assume that it is a
// plain-text query.
items.filtered = items.filtered.filter(function(item) {
return item.toString().toLowerCase().indexOf(queryString.toLowerCase()) !== -1;
});
todoTableSearch.classList.remove("is-previous-query");
}
}
}
// apply filters
items.filtered = items.filtered.filter(function(item) {
if(!userData.showHidden && item.h) return false;
......@@ -193,18 +159,49 @@ function filterItems() {
if(!userData.showDueIsPast && item.due && isPast(item.due)) return false;
if(!userData.showDueIsFuture && item.due && isFuture(item.due)) return false;
if(!userData.deferredTodos && item.t && isFuture(item.t)) return false;
return true;
return item;
});
return Promise.resolve("Success: todo.txt objects filtered");
} catch(error) {
error.functionName = filterItems.name;
error.functionName = applyFilters.name;
return Promise.reject(error);
}
}
function applySearchInput(queryString) {
try {
const query = filterlang.parse(queryString);
items.filtered = items.filtered.filter(function(item) {
return runQuery(item, query);
});
lastFilterQueryString = queryString;
lastFilterItems = items.filtered;
return Promise.resolve("Success: Advanced search query applied");
} catch(error) {
// oops, that wasn't a syntactically correct search expression
if (lastFilterQueryString && queryString.startsWith(lastFilterQueryString)) {
// keep table more stable by using the previous valid query while
// user continues to type additional query syntax.
items.filtered = lastFilterItems;
} else {
// the query is not syntactically correct and isn't a longer version
// of the last working query, so let's assume that it is a
// plain-text query.
items.filtered = items.filtered.filter(function(item) {
return item.toString().toLowerCase().indexOf(queryString.toLowerCase()) !== -1;
});
}
}
return Promise.resolve("Success: Plain-text search query applied");
}
......@@ -345,9 +342,8 @@ function generateFilterData(autoCompleteCategory, autoCompleteValue, autoComplet
}
});
// search within filters according to autoCompleteValue
if(autoCompletePrefix) {
filters = filters.filter(function(el) { return el.toString().toLowerCase().includes(autoCompleteValue.toLowerCase()); });
}
if(autoCompletePrefix) filters = filters.filter(function(filter) { return filter.toString().toLowerCase().includes(autoCompleteValue.toLowerCase()); });
// remove duplicates, create the count object and avoid counting filters of hidden todos
filtersCounted = filters.reduce(function(filters, filter) {
// if filter is already in object and should be counted
......@@ -617,7 +613,7 @@ async function resetFilters(refresh) {
//todoFilters.innerHTML = "";
// clear search input
document.getElementById("todoTableSearch").value = null;
todoTableSearch.value = null;
// regenerate the data
if(refresh) await buildTable().then(function(response) {
......@@ -633,4 +629,4 @@ async function resetFilters(refresh) {
}
}
export { filterItems, generateFilterData, selectFilter, categories, filterCounter, resetFilters };
export { applyFilters, applySearchInput, generateFilterData, selectFilter, categories, filterCounter, resetFilters };
......@@ -264,7 +264,14 @@ export function setupInterface() {
}
export function handleError(error) {
try {
console.error(error);
// showGenericMessage(error).then(function(response) {
// console.log(response)
// }).catch(function(error) {
// handleError(error);
// });
// trigger matomo event
if(userData.matomoEvents) _paq.push(["trackEvent", "Error", error.functionName, error])
......
......@@ -10,7 +10,7 @@ const todoTableSearchContainer = document.getElementById("todoTableSearchContain
todoTableSearch.oninput = debounce(function() {
// on focus show addTodo button
todoTableSearchAddTodo.classList.add("is-active");
//todoTableSearchAddTodo.classList.add("is-active");
// rebuild table
buildTable().then(function(response) {
......@@ -23,9 +23,15 @@ todoTableSearch.oninput = debounce(function() {
todoTableSearch.onfocus = function() {
// add blue highlighting to search bar
todoTableSearchContainer.classList.add("is-focused");
todoTableSearchAddTodo.classList.add("is-active")
}
todoTableSearch.onblur = function(event) {
// this represents the tabbing to add button and will prevent the removal of is-active class
if(event.relatedTarget && event.relatedTarget.id !== "todoTableSearchAddTodo") todoTableSearchAddTodo.classList.remove("is-active")
// if input loses focus addTodo will be hidden
todoTableSearchContainer.classList.remove("is-focused");
......
......@@ -69,13 +69,13 @@ todoTableWrapper.onscroll = function(event) {
async function generateTodoTxtObjects(fileContent) {
try {
// stop it, when there is no new content from file
if(!fileContent) return Promise.resolve("Info: No new content, won't change todo object")
// create todo.txt objects
items = await { objects: TodoTxt.parse(fileContent, [ new SugarDueExtension(), new HiddenExtension(), new RecExtension(), new ThresholdExtension() ]) }
// empty lines will be filtered
items.objects = items.objects.filter(function(item) { return item.toString() !== "" });
return Promise.resolve("Success: Todo object created");
return Promise.resolve("Success: New todo object created");
} catch(error) {
error.functionName = generateTodoTxtObjects.name;
return Promise.reject(error);
......
......@@ -4,10 +4,9 @@ const { Tray, app, Notification, clipboard, Menu, ipcMain, BrowserWindow, native
const path = require("path");
const fs = require("fs");
const chokidar = require("chokidar");
const i18next = require("./configs/i18next.config");
const Store = require("./configs/store.config.js");
const autoUpdater = require("./configs/autoUpdater.config.js");
const dismissedNotificationsStorage = require("./configs/store.config.js");
//const dismissedNotificationsStorage = require("./configs/store.config.js");
const os = {
darwin: "mac",
win32: "windows",
......@@ -26,13 +25,10 @@ let
configName: "user-preferences",
defaults: {}
}),
defaultPath,
_paq,
fileWatcher,
translations,
mainWindow,
tray = null,
dismissedNotifications;
tray = null;
// ########################################################################################################################
// HOT RELOAD
......@@ -219,8 +215,8 @@ async function startFileWatcher(file) {
// TODO: Check windows start
// electron "unbundled" app -- have to skip "electron" and script name arg eg: "."
// electron "bundled" app -- skip only the app name, eg: "sleek"
let args;
(process.defaultApp) ? args = process.argv.slice(2) : args = process.argv.slice(1);
//let args;
//(process.defaultApp) ? args = process.argv.slice(2) : args = process.argv.slice(1);
// set the current file to not active
const indexOfActiveFile = userData.data.files.findIndex(file => file[0] === 1);
......@@ -487,14 +483,14 @@ function configureWindowEvents() {
console.error(error);
});
})
.on("getContent", async (event, file) => {
const content = await getContent(file).then(content => {
.on("getContent", (event, file) => {
getContent(file).then(content => {
mainWindow.webContents.send("getContent", content);
}).catch(error => {
console.error(error);
});
})
.on("translations", (event) => {
.on("translations", () => {
getTranslations().then(function(translations) {
mainWindow.webContents.send("translations", translations)
});
......
......@@ -82,7 +82,7 @@ function getTranslations() {
}
}
async function buildTable(fileContent, loadAll) {
try {
//try {
// start timer for table
const t0 = performance.now();
......@@ -103,11 +103,19 @@ async function buildTable(fileContent, loadAll) {
if(typeof todos.items !== "object") throw(new Error("Info: No todo.txt data found, starting onboarding"))
// apply persisted filters and update object key "filtered"
filters.filterItems().then(function(response) {
filters.applyFilters().then(function(response) {
console.log(response)
}).catch(function(error) {
helper.handleError(error);
});
// if there is a queryString, we pass it on for additional filtering
const queryString = document.getElementById("todoTableSearch").value;
if(queryString.length > 0) filters.applySearchInput(queryString).then(function(response) {
console.log(response)
}).catch(function(error) {
helper.handleError(error);
})
// once we have the filtered objects, we can adjust the gui
helper.setupInterface().then(function(response) {
......@@ -137,19 +145,20 @@ async function buildTable(fileContent, loadAll) {
helper.handleError(error);
});
} catch(error) {
onboarding.showOnboarding(true).then(function(response) {
console.log(response);
}).catch(function(error) {
helper.handleError(error);
});
// if anything fails onboarding is shown and error presented to user
// } catch(error) {
messages.showGenericMessage(error);
// onboarding.showOnboarding(true).then(function(response) {
// console.log(response);
// }).catch(function(error) {
// helper.handleError(error);
// });
error.functionName = buildTable.name;
return Promise.reject(error);
}
// messages.showGenericMessage(error);
// error.functionName = buildTable.name;
// return Promise.reject(error);
// }
}
window.onload = async function() {
......
......@@ -226,14 +226,14 @@ body.dark {
color: $dark-grey;
}
}
.cell.itemDueDate.isToday,
.cell.itemDueDate.isPast {
.cell.itemDueDate.today,
.cell.itemDueDate.past {
color: $has-text-danger;
i {
color: inherit;
}
}
.cell.itemDueDate.isTomorrow {
.cell.itemDueDate.tomorrow {
i {
color: $has-text-attention!important;
}
......
......@@ -142,7 +142,7 @@
#modalForm {
#modalFormInput {
z-index: 60;
padding-right: 3em;
padding-right: 2em!important;
}
textarea {
min-height: 5.5em;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment