Commit 20b319ec authored by ransome1's avatar ransome1
Browse files

CSS improvements, inverted sorting, file handling

parent c0953825
......@@ -24,3 +24,5 @@ package-lock.json
.vs/
.vscode/
.yarnclean
sleek.sublime-project
sleek.sublime-workspace
\ No newline at end of file
......@@ -88,7 +88,7 @@ A prioritized backlog of new features and known issues can be found <a href="htt
* Autocomplete function suggests available contexts and projects
* <a href="https://github.com/ransome1/sleek/wiki/Keyboard-shortcuts">Keyboard shortcuts following todotxt.net</a>
* Tabindex available
* <a href="https://github.com/ransome1/sleek/wiki/Hidden-todos">A todo can be hidden but its attributes will be available in the filter drawer and autocomplete function</a>
* <a href="https://github.com/ransome1/sleek/wiki/Hidden-todos-(h:)">A todo can be hidden but its attributes will be available in the filter drawer and autocomplete function</a>
* Due dates trigger alarms and appear as badges in sleeks icon
* Dark and light mode can be toggled
* A compact view is available
......
{
"name": "sleek",
"productName": "sleek",
"version": "1.1.6-rc.2",
"version": "1.1.6-rc.3",
"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",
......
name: sleek
base: core18
version: '1.1.5'
version: "1.1.6"
summary: Todo app based on the todo.txt format for Linux, free and open-source
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.
......
......@@ -6,9 +6,12 @@ body.dark {
body.dark code, body.dark pre {
background-color: #212224 !important;
}
body.dark a {
color: #3273dc;
}
body.dark a:hover,
body.dark a:focus-visible {
color: #3273dc;
color: #f0f0f0;
}
body.dark strong {
color: #f0f0f0;
......@@ -284,7 +287,7 @@ body.dark #drawerContainer .drawer #sortByContainer li {
color: white;
}
body.dark #drawerContainer .drawer #sortByContainer li i {
color: #ebebeb;
/*color: $light-grey;*/
}
body.dark #drawerContainer .drawer #sortByContainer li.drag-sort-active {
border-left: 0.2em solid white;
......@@ -384,7 +387,7 @@ body.dark .toggle .switch .slider {
background-color: #5a5a5a;
}
body.dark .toggle .switch input:checked + .slider {
background-color: #2d2d2d !important;
background-color: #3273dc !important;
}
body.dark .toggle .switch input:focus + .slider {
box-shadow: none;
......@@ -837,14 +840,6 @@ code, pre {
cursor: pointer;
}
#rowDatePicker {
position: fixed;
top: auto;
left: auto;
opacity: 0;
visibility: hidden;
}
.contentContainer {
width: 100%;
text-align: center;
......@@ -1184,7 +1179,7 @@ nav ul:nth-child(2):hover #versionNumber {
}
#modalForm textarea {
padding: 0.75em;
padding: 0.75em !important;
}
#modalForm #modalFormInputResize {
top: 1.65em;
......@@ -1296,7 +1291,7 @@ body.compact .inputWrapper label {
display: none;
}
body.compact #modalForm textarea {
padding: 0.75em;
padding: 0.75em !important;
}
body.compact #modalForm #modalFormInputResize {
top: 1.65em;
......@@ -1964,7 +1959,6 @@ body.compact #autoCompleteContainer h4 {
width: 100%;
position: relative;
display: flex;
flex-flow: row wrap;
border-bottom: solid 1px #ebebeb;
}
#todoTable .todo:last-child {
......@@ -2018,7 +2012,6 @@ body.compact #autoCompleteContainer h4 {
background: #3273dc;
}
#todoTable .cell {
float: left;
line-height: 1.7em;
padding: 0.75em 0;
margin: 0 1em 0 0;
......@@ -2189,7 +2182,7 @@ body.compact #autoCompleteContainer h4 {
background: none;
}
.toggle .switch input:checked + .slider {
background-color: #5a5a5a;
background-color: #3273dc !important;
}
.toggle .switch input:focus {
box-shadow: none !important;
......
This diff is collapsed.
......@@ -68,6 +68,7 @@ const datePicker = new Datepicker(datePickerInput, {
language: userData.language,
format: "yyyy-mm-dd",
clearBtn: true,
container: "body",
calendarWeeks: true,
weekStart: 1,
beforeShowDay: function(date) {
......@@ -84,12 +85,5 @@ datePickerInput.onfocus = function () {
autoCompleteContainer.classList.remove("is-active");
resizeInput(datePickerInput);
};
// document.querySelector(".datepicker .clear-btn").onclick = function() {
// let todo = new TodoTxtItem(document.getElementById("modalFormInput").value, [ new SugarDueExtension(), new HiddenExtension(), new RecExtension(), new ThresholdExtension() ]);
// todo.due = undefined;
// todo.dueString = undefined;
// document.getElementById("modalFormInput").value = todo.toString();
// resizeInput(datePickerInput);
// datePicker.hide();
// }
export { datePickerInput, datePicker };
"use strict";
import { appData, userData, setUserData, translations } from "../render.js";
import { appData, userData, getUserData, setUserData, translations } from "../render.js";
import { _paq } from "./matomo.mjs";
import { createModalJail } from "./jail.mjs";
import { resetFilters } from "./filters.mjs";
......@@ -79,7 +79,7 @@ function selectFileFromList(index) {
}
}
function generateFileList() {
async function generateFileList() {
try {
fileTabBar.classList.remove("is-active");
fileTabBarList.innerHTML = null;
......
// Generated by Peggy 1.2.0.
//
// https://peggyjs.org/
import { addIntervalToDate } from "./recurrences.mjs";
"use strict";
import { addIntervalToDate } from "./recurrences.mjs";
function peg$subclass(child, parent) {
function C() { this.constructor = child; }
......
"use strict";
// This is a simple stack machine that executes a filter language query
// compiled by filterlang.pegjs (which generates filterlang.mjs).
// The compiled query consists of a list of postfix opcodes designed
......
......@@ -214,6 +214,7 @@ export function configureMainView() {
document.getElementById("viewSortByRow").classList.remove("is-hidden");
}
// generate file tabs
// TODO: Only if there are files
generateFileList();
// close filterContext if open
if(document.getElementById("filterContext").classList.contains("is-active")) document.getElementById("filterContext").classList.remove("is-active");
......
"use strict";
export function createModalJail(modal) {
// add all the elements inside modal which you want to make focusable
let currentElement = 0;
......
"use strict";
import { userData, appData, translations } from "../render.js";
import { showContent } from "./content.mjs";
import { show } from "./form.mjs";
......
......@@ -3,13 +3,11 @@ import { userData, translations } from "../render.js";
import { createModalJail } from "./jail.mjs";
import { generateFileList } from "./files.mjs";
import { _paq } from "./matomo.mjs";
import { handleError } from "./helper.mjs";
import { handleError, configureMainView } from "./helper.mjs";
//const addTodoContainer = document.getElementById("addTodoContainer");
const addTodoContainerButton = document.getElementById("addTodoContainerButton");
const addTodoContainerHeadline = document.getElementById("addTodoContainerHeadline");
const addTodoContainerSubtitle = document.getElementById("addTodoContainerSubtitle");
//const btnAddTodoContainer = document.getElementById("btnAddTodoContainer");
const btnOnboardingCreateTodoFile = document.getElementById("btnOnboardingCreateTodoFile");
const btnOnboardingOpenTodoFile = document.getElementById("btnOnboardingOpenTodoFile");
const onboardingContainer = document.getElementById("onboardingContainer");
......
"use strict";
import { translations } from "../render.js";
import { createModalJail } from "./jail.mjs";
import { handleError } from "./helper.mjs";
......
"use strict"; import "../../node_modules/jstodotxt/jsTodoExtensions.js"; import { appData, userData, setUserData,
translations, startBuilding } from "../render.js"; import { _paq } from "./matomo.mjs"; import{ categories,
selectFilter } from "./filters.mjs"; import{ generateRecurrence } from "./recurrences.mjs"; import { convertDate,
isToday, isTomorrow, isPast } from "./date.mjs"; import { show } from "./form.mjs"; import { SugarDueExtension,
RecExtension, ThresholdExtension } from "./todotxtExtensions.mjs"; import{ createModalJail } from "./jail.mjs";
import { getConfirmation } from "./prompt.mjs"; import { getActiveFile, generateHash, handleError, formatDate }
from "./helper.mjs"; import { focusRow } from "./keyboard.mjs";
"use strict";
import "../../node_modules/jstodotxt/jsTodoExtensions.js";
import { appData, userData, setUserData, translations, startBuilding } from "../render.js";
import { _paq } from "./matomo.mjs";
import { categories, selectFilter } from "./filters.mjs";
import { generateRecurrence } from "./recurrences.mjs";
import { convertDate, isToday, isTomorrow, isPast } from "./date.mjs";
import { show } from "./form.mjs";
import { SugarDueExtension, RecExtension, ThresholdExtension } from "./todotxtExtensions.mjs";
import { createModalJail } from "./jail.mjs";
import { getConfirmation } from "./prompt.mjs";
import { getActiveFile, generateHash, handleError, formatDate } from "./helper.mjs";
import { focusRow } from "./keyboard.mjs";
import { datePicker } from "./datePicker.mjs";
const todoContext = document.getElementById("todoContext");
const todoContextDelete = document.getElementById("todoContextDelete");
const todoContextEdit = document.getElementById("todoContextEdit");
......
"use strict";
import "../../node_modules/jstodotxt/jsTodoExtensions.js";
function RecExtension() {
......
"use strict";
let userData, defaultPath, _paq, fileWatcher, translations;
const { Tray, app, Notification, clipboard, Menu, ipcMain, BrowserWindow, nativeTheme } = require("electron");
const path = require("path");
......@@ -171,21 +172,33 @@ const createWindow = async function() {
break;
}
}
const startFileWatcher = async function(file, isTabItem) {
try {
if(!fs.existsSync(file)) throw("Error: File not found on disk")
const refreshFiles = function() {
let fileRemoved = false;
// if a file is not found it will be removed from list and user will be informed
userData.data.files.forEach(function(file, index, object) {
if(!fs.existsSync(file[1])) {
console.log("Not found and removed from file list: " + file[1])
object.splice(index, 1);
// userData.set("files", userData.data.files.filter(function(fileInArray) {
// //console.log(fileInArray[1])
// if(fs.existsSync(fileInArray[1])) return fileInArray;
// //if(fs.existsSync(file[1])) console.log("gefuden");
// //if(fs.existsSync(fileInArray[1])) return fileInArray;
// //fs.existsSync(process.env.SLEEK_CUSTOM_FILE);
// })
// );
fileRemoved = true;
// notify user about file not being found on disk
const notification = {
title: "File not found",
body: "The currently selected todo file was not found: " + file[1],
timeoutType: "never",
silent: false
}
showNotification(notification);
}
});
userData.set("files", userData.data.files);
if(fileRemoved) mainWindow.webContents.send("triggerFunction", "configureMainView");
}
const startFileWatcher = async function(file, isTabItem) {
try {
// 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;
......@@ -201,6 +214,14 @@ const createWindow = async function() {
if(args.length > 0 && fs.existsSync(args[0])) {
file = args[0];
}
// check if file exists, otherwise abort and call onbaording
if(!fs.existsSync(file)) {
await refreshFiles();
mainWindow.webContents.reloadIgnoringCache();
return Promise.reject("Info: File not found on disk");
}
// use the loop to check if the new path is already in the user data
let fileFound = false;
if(userData.data.files) {
......@@ -208,6 +229,7 @@ const createWindow = async function() {
// if path is found it is set active
if(element[1]===file) {
element[0] = 1
// put active file into tabbar
if(isTabItem) element[2] = 1;
fileFound = true;
// if this entry is not equal to the new path it is set 0
......@@ -221,6 +243,7 @@ const createWindow = async function() {
// 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, 1]);
userData.set("files", userData.data.files);
// TODO describe
if(fileWatcher) {
fileWatcher.close().then(() => console.log("Info: Filewatcher instance closed"));
......@@ -235,15 +258,28 @@ const createWindow = async function() {
console.log(error);
});
})
.on("unlink", function() {
.on("unlink", async function() {
// if file doesn't exist refresh file list and reload app
if(!fs.existsSync(file)) {
await refreshFiles();
mainWindow.webContents.reloadIgnoringCache();
console.log("unlink: File not found on disk: " + file);
return false;
}
// Restart file watcher
startFileWatcher(userData.data.file).then(response => {
startFileWatcher().then(response => {
console.info(response);
}).catch(error => {
console.error(error);
});
})
.on("change", function() {
.on("change", async function() {
if(!fs.existsSync(file)) {
await refreshFiles();
mainWindow.webContents.reloadIgnoringCache();
console.log("unlink: File not found on disk: " + file);
return false;
}
console.log("Info: File " + file + " has changed");
// wait 10ms before rereading in case the file is being updated with a delay
setTimeout(function() {
......
"use strict";
const { contextBridge, ipcRenderer } = require("electron");
contextBridge.exposeInMainWorld(
"api", {
......
"use strict";
let
a0,
appData,
......@@ -10,12 +9,14 @@ let
filters,
userData,
todos;
function getUserData() {
try {
window.api.send("userData");
return new Promise(function(resolve) {
return window.api.receive("userData", function(data) {
resolve(data);
userData = data;
resolve("Success: User data received");
});
});
} catch(error) {
......@@ -43,7 +44,8 @@ function getAppData() {
window.api.send("appData");
return new Promise(function(resolve) {
return window.api.receive("appData", (data) => {
resolve(data);
appData = data;
resolve("Success: App data received");
});
});
} catch(error) {
......@@ -69,7 +71,9 @@ async function startBuilding(loadAll) {
const t0 = performance.now();
todos.items.filtered = await filters.filterItems(todos.items.objects);
await filters.generateFilterData();
userData = await getUserData();
await getUserData();
if(!userData.sortByFile) groups = await todos.generateGroups(todos.items.filtered);
await todos.generateTable(groups, loadAll);
await helper.configureMainView();
......@@ -82,8 +86,10 @@ async function startBuilding(loadAll) {
}
window.onload = async function () {
a0 = performance.now();
userData = await getUserData();
appData = await getAppData();
await getUserData();
await getAppData();
translations = await getTranslations();
todos = await import("./js/todos.mjs");
filters = await import("./js/filters.mjs");
......@@ -135,6 +141,9 @@ window.api.receive("triggerFunction", async (name, args) => {
try {
if(!args) args = new Array;
switch (name) {
case "startBuilding":
startBuilding();
break;
case "showOnboarding":
onboarding.showOnboarding(...args).then(function(response) {
console.info(response);
......@@ -195,6 +204,13 @@ window.api.receive("triggerFunction", async (name, args) => {
helper.handleError(error);
});
break;
case "configureMainView":
helper.configureMainView().then(function(response) {
console.info(response);
}).catch(function(error) {
helper.handleError(error);
});
break;
case "setTheme":
helper.setTheme(...args).then(function(response) {
console.info(response);
......@@ -215,4 +231,4 @@ window.api.receive("refresh", async (args) => {
helper.handleError(error);
});
});
export { setUserData, startBuilding, userData, appData, translations };
export { getUserData, setUserData, startBuilding, userData, appData, translations };
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