Commit 28469a87 authored by ransome1's avatar ransome1
Browse files

Switched to sass, updated modules, code refactoring, added support for Apple ARM64 devices

parent 59c6f0ee
name: Code scan, build & release
on: push
jobs:
njsscan:
name: njsscan
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v2
- name: nodejsscan scan
id: njsscan
uses: ajinabraham/njsscan-action@master
with:
args: '. --sarif --output results.sarif || true'
- name: Upload njsscan report
uses: github/codeql-action/upload-sarif@v1
with:
sarif_file: results.sarif
codeql:
needs: njsscan
name: CodeQL
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
macos:
needs: codeql
name: MacOS (Build & Release)
runs-on: ${{ matrix.os }}
strategy:
......@@ -57,7 +21,6 @@ jobs:
github_token: ${{ secrets.github_token }}
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
windows:
needs: codeql
name: Windows (Build & Release)
runs-on: ${{ matrix.os }}
strategy:
......@@ -80,7 +43,6 @@ jobs:
github_token: ${{ secrets.github_token }}
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
linux:
needs: codeql
name: Linux (Build & Release)
runs-on: ${{ matrix.os }}
strategy:
......@@ -99,8 +61,42 @@ jobs:
max_attempts: 3
github_token: ${{ secrets.github_token }}
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
njsscan:
name: njsscan
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v2
- name: nodejsscan scan
id: njsscan
uses: ajinabraham/njsscan-action@master
with:
args: '. --sarif --output results.sarif || true'
- name: Upload njsscan report
uses: github/codeql-action/upload-sarif@v1
with:
sarif_file: results.sarif
codeql:
needs: njsscan
name: CodeQL
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
mirror:
needs: [windows, macos, linux]
needs: codeql
name: Mirror code to opencode.net
runs-on: ubuntu-latest
steps:
......
......@@ -7,6 +7,7 @@ dist/
snap/local/
*.py
*.ini
*.map
.gitignore
.git/
*.db:encryptable
......@@ -20,4 +21,4 @@ assets/icons/bak
squashfs-root/
test/
src/__tests__
yarn.lock
.eslintrc.json
(A) Thank Mom for the meatballs @phone
x 2021-04-07 (B) Schedule Goodwill pickup +GarageSale @phone
Eskimo pies @GroceryStore
Really gotta call Mom \this task has no priority\ (A) @phone @someday
(b) Get back to the boss \this task has no priority\
(B) ->Submit TPS report \this task has no priority\
(A) Call Mom 2011-03-02 \this task has no creation date\ due:2021-04-09
(A) Call Mom \multiple projects and contexts\ +Family +PeaceLoveAndHappiness @iphone @phone
Email SoAndSo at soandso@example.com \this task has no context\
Learn how to add 2+2 \this task has no project\
x 2011-03-03 Call Mom \this task is complete\
xylophone lesson \this task is incomplete\
Make resolutions \this task is incomplete\
(A) x Find ticket prices \this task is incomplete\
This task has a due date in it due:2021-07-01
This task has a threshold date in it t:2021-12-15
(C) This task is a hidden task h:1
(A) 2021-02-28 A task list with all possible task types \comments\ due:2021-03-30 +todotxt @test
daily task revisions within Sleek \Daily recurrence\ rec:d
weekly task revisions within Sleek \Weekly Recurrence\ rec:w
monthly task revisions within Sleek \Monthly recurrence\ rec:m
annual task revisions within Sleek \annual recurrence\ rec:y
2021-03-22 This is a multi line taskcreated within Sleek due:2021-05-01 +todotxt @test
2021-04-03 Todo with due date tomorrow due:2021-04-06
2021-04-05 monthly task revisions within Sleek \Monthly recurrence\ due:2021-05-05 rec:m
(A) 2021-04-07 Todo due today due:2021-04-07
\ No newline at end of file
{
"name": "sleek",
"productName": "sleek",
"version": "1.0.3-3",
"version": "1.0.4",
"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",
......@@ -97,28 +97,31 @@
"build:appx": "electron-builder -w appx --publish never",
"build:pacman": "electron-builder -l pacman --publish never",
"build:appimage": "electron-builder -l AppImage --publish never",
"pack": "electron-builder --dir",
"lint": "eslint --ext .js, src --ext .mjs, src",
"jest": "jest",
"mocha": "mocha",
"pack": "electron-builder --dir",
"css-build": "node-sass src/scss/ -o src/css/",
"css-watch": "node-sass src/scss/ -wo src/css/",
"start": "yarn css-build & yarn css-watch & electron ."
"sass": "sass -w src/scss/:src/css/",
"start": "yarn sass & electron ."
},
"dependencies": {
"@fortawesome/fontawesome-free": "^5.15.3",
"bulma": "^0.9.2",
"i18next": "^20.2.2",
"i18next-browser-languagedetector": "^6.1.0",
"i18next-fs-backend": "^1.1.1",
"jstodotxt": "^0.9.0",
"marked": "^2.0.3",
"vanillajs-datepicker": "^1.1.4"
},
"devDependencies": {
"sass": "^1.32.12",
"chai": "^4.3.4",
"electron": "^12.0.6",
"electron-builder": "^22.10.5",
"electron-builder": "^22.11.1",
"eslint": "^7.25.0",
"jest": "^26.6.3",
"mocha": "^8.3.2",
"node-sass": "^5.0.0",
"spectron": "^14.0.0"
}
}
......@@ -23,8 +23,8 @@ textarea {
input[type="search"]::-webkit-search-cancel-button {
background-color: white;
-webkit-mask-image: url("../svg/times-circle.svg");
mask-image: url("../svg/times-circle.svg"); }
-webkit-mask-image: url("../img/svg/times-circle.svg");
mask-image: url("../img/svg/times-circle.svg"); }
input:active,
input:focus,
......
This diff is collapsed.
......@@ -144,7 +144,7 @@
<td>Zoom</td>
<td class="zoom">
<input type="range" id="zoomRangePicker" name="vol" min="75" max="125"><br>
<span id="zoomStatus"></span><i id="zoomUndo" class="fas fa-undo"></i>
<span id="zoomStatus"></span><span id="zoomUndo"><i class="fas fa-undo"></i></span>
</td>
</tr>
</table>
......@@ -407,8 +407,6 @@
</td>
</tr>
</table>
<table class="settings">
<tr>
<th colspan="100%" id="settingsTabSettingsLogging"></th>
......@@ -654,13 +652,13 @@
</p>
</article>
</section>
<script defer src="js/jsTodoExtensions.js"></script>
<script defer src="js/jsTodoTxt.js"></script>
<script defer src="js/marked.min.js"></script>
<script defer src="../node_modules/jstodotxt/jsTodoExtensions.js"></script>
<script defer src="../node_modules/jstodotxt/jsTodoTxt.js"></script>
<script defer src="../node_modules/marked/marked.min.js"></script>
<script defer type="module" src="render.js"></script>
<script defer src="js/fontawesome.js"></script>
<script defer src="js/fontawesome.solid.min.js"></script>
<script defer src="js/fontawesome.regular.min.js"></script>
<script defer src="js/fontawesome.brands.min.js"></script>
<script defer src="../node_modules/@fortawesome/fontawesome-free/js/fontawesome.min.js"></script>
<script defer src="../node_modules/@fortawesome/fontawesome-free/js/solid.min.js"></script>
<script defer src="../node_modules/@fortawesome/fontawesome-free/js/regular.min.js"></script>
<script defer src="../node_modules/@fortawesome/fontawesome-free/js/brands.min.js"></script>
</body>
</html>
"use strict";
import { modal } from "../../render.js";
import { modal, userData, _paq } from "../render.js";
function showTab(tab) {
contentTabsCards.forEach(function(el) {
......@@ -33,14 +33,14 @@ function showContent(section) {
}
const contentTabs = document.querySelectorAll('.modal.content ul li');
const contentTabsCards = document.querySelectorAll('.modal.content section');
contentTabs.forEach(el => el.addEventListener("click", function(el) {
contentTabs.forEach(el => el.addEventListener("click", function() {
contentTabs.forEach(function(el) {
el.classList.remove("is-active");
});
this.classList.add("is-active");
showTab(this.classList[0]);
// trigger matomo event
if(window.consent) _paq.push(["trackEvent", "Content", "Click on " + this.firstElementChild.innerHTML, this.classList[0]]);
if(userData.matomoEvents) _paq.push(["trackEvent", "Content", "Click on " + this.firstElementChild.innerHTML, this.classList[0]]);
}));
export { showContent };
......@@ -5,35 +5,30 @@ function convertDate(date) {
let month = ("0" + (date.getMonth() + 1)).slice(-2);
let year = date.getFullYear();
return year + "-" + month + "-" + day;
};
}
function isToday(date) {
const today = new Date()
return date.getDate() === today.getDate() &&
date.getMonth() === today.getMonth() &&
date.getFullYear() === today.getFullYear();
};
}
function isTomorrow(date) {
const today = new Date()
return date.getDate() === today.getDate()+1 &&
date.getMonth() === today.getMonth() &&
date.getFullYear() === today.getFullYear();
};
}
function isPast(date) {
const today = new Date();
if (date.setHours(0, 0, 0, 0) < today.setHours(0, 0, 0, 0)) {
return true;
}
return false;
};
/*Date.prototype.isFuture = function () {
const today = new Date();
if (date.setHours(0, 0, 0, 0) > today.setHours(0, 0, 0, 0)) return true
return false;
};*/
}
function isFuture(date) {
const today = new Date();
if (date.setHours(0, 0, 0, 0) > today.setHours(0, 0, 0, 0)) return true
return false;
};
}
export { convertDate, isToday, isTomorrow, isPast, isFuture };
"use strict";
import { translations, userData, resizeInput } from "../../render.js";
import Datepicker from "../../../node_modules/vanillajs-datepicker/js/Datepicker.js";
import de from "../../../node_modules/vanillajs-datepicker/js/i18n/locales/de.js";
import it from "../../../node_modules/vanillajs-datepicker/js/i18n/locales/it.js";
import es from "../../../node_modules/vanillajs-datepicker/js/i18n/locales/es.js";
import fr from "../../../node_modules/vanillajs-datepicker/js/i18n/locales/fr.js";
import { translations, userData, _paq } from "../render.js";
import { resizeInput } from "./form.mjs";
import { RecExtension } from "./todotxtExtensions.mjs";
import Datepicker from "../../node_modules/vanillajs-datepicker/js/Datepicker.js";
import de from "../../node_modules/vanillajs-datepicker/js/i18n/locales/de.js";
import it from "../../node_modules/vanillajs-datepicker/js/i18n/locales/it.js";
import es from "../../node_modules/vanillajs-datepicker/js/i18n/locales/es.js";
import fr from "../../node_modules/vanillajs-datepicker/js/i18n/locales/fr.js";
const autoCompleteContainer = document.getElementById("autoCompleteContainer");
const modalFormInput = document.getElementById("modalFormInput");
const datePickerInput = document.getElementById("datePickerInput");
datePickerInput.onfocus = function () {
datePicker.update();
autoCompleteContainer.classList.remove("is-active");
resizeInput(datePickerInput);
};
datePickerInput.addEventListener("changeDate", function (e, detail) {
datePickerInput.addEventListener("changeDate", function (e) {
// we only update the object if there is a date selected. In case of a refresh it would throw an error otherwise
if(e.detail.date) {
// generate the object on what is written into input, so we don't overwrite previous inputs of user
let todo = new TodoTxtItem(modalFormInput.value, [ new DueExtension(), new HiddenExtension(), new RecExtension() ]);
let todo = new TodoTxtItem(document.getElementById("modalFormInput").value, [ new DueExtension(), new HiddenExtension(), new RecExtension() ]);
todo.due = new Date(e.detail.date);
todo.dueString = new Date(e.detail.date.getTime() - (e.detail.date.getTimezoneOffset() * 60000 )).toISOString().split("T")[0];
// if suggestion box was open, it needs to be closed
autoCompleteContainer.classList.remove("is-active");
autoCompleteContainer.blur();
// if a due date is set, the recurrence picker will be shown);
modalFormInput.value = todo.toString();
modalFormInput.focus();
document.getElementById("modalFormInput").value = todo.toString();
document.getElementById("modalFormInput").focus();
resizeInput(datePickerInput);
datePicker.hide();
// trigger matomo event
if(window.consent) _paq.push(["trackEvent", "Form", "Datepicker used to add date to input"]);
if(userData.matomoEvents) _paq.push(["trackEvent", "Form", "Datepicker used to add date to input"]);
}
});
datePickerInput.placeholder = translations.formSelectDueDate;
......@@ -47,12 +51,11 @@ const datePicker = new Datepicker(datePickerInput, {
}
});
document.querySelector(".datepicker .clear-btn").onclick = function() {
let todo = new TodoTxtItem(modalFormInput.value, [ new DueExtension(), new HiddenExtension(), new RecExtension() ]);
let todo = new TodoTxtItem(document.getElementById("modalFormInput").value, [ new DueExtension(), new HiddenExtension(), new RecExtension() ]);
todo.due = undefined;
todo.dueString = undefined;
modalFormInput.value = todo.toString();
document.getElementById("modalFormInput").value = todo.toString();
resizeInput(datePickerInput);
datePicker.hide();
}
export { datePickerInput, datePicker};
"use strict";
import { setUserData, showMore, userData, handleError, navBtns } from "../../render.js";
// ########################################################################################################################
// RESIZEABLE FILTER DRAWER
// https://spin.atomicobject.com/2019/11/21/creating-a-resizable-html-element/
// ########################################################################################################################
const getResizeableElement = () => { return document.getElementById("drawerContainer"); };
const getHandleElement = () => { return document.getElementById("handle"); };
const minPaneSize = 400;
const maxPaneSize = document.body.clientWidth * .75
const setPaneWidth = (width) => {
getResizeableElement().style.setProperty("--resizeable-width", `${width}px`);
setUserData("drawerWidth", `${width}`);
};
const getPaneWidth = () => {
const pxWidth = getComputedStyle(getResizeableElement())
.getPropertyValue("--resizeable-width");
return parseInt(pxWidth, 10);
};
const startDragging = (event) => {
event.preventDefault();
const host = getResizeableElement();
const startingPaneWidth = getPaneWidth();
const xOffset = event.pageX;
const mouseDragHandler = (moveEvent) => {
moveEvent.preventDefault();
const primaryButtonPressed = moveEvent.buttons === 1;
if (!primaryButtonPressed) {
setPaneWidth(Math.min(Math.max(getPaneWidth(), minPaneSize), maxPaneSize));
document.body.removeEventListener("pointermove", mouseDragHandler);
return;
}
const paneOriginAdjustment = "left" === "right" ? 1 : -1;
setPaneWidth((xOffset - moveEvent.pageX ) * paneOriginAdjustment + startingPaneWidth);
};
const remove = document.body.addEventListener("pointermove", mouseDragHandler);
};
getResizeableElement().style.setProperty("--max-width", `${maxPaneSize}px`);
getResizeableElement().style.setProperty("--min-width", `${minPaneSize}px`);
getHandleElement().addEventListener("mousedown", startDragging);
import { setUserData, showMore, userData, handleError, navBtns, _paq } from "../render.js";
import { getHandleElement, startDragging } from "./drawer_handle.mjs";
const viewDrawer = document.getElementById("viewDrawer");
document.getElementById("filterDrawer").addEventListener ("keydown", function () {
if(event.key === "Escape") {
showDrawer(false, navBtnFilter.id, filterDrawer.id).then(function(result) {
const filterDrawer = document.getElementById("filterDrawer");
const drawerContainer = document.getElementById("drawerContainer");
const todoTable = document.getElementById("todoTable");
const todoTableSearchContainer = document.getElementById("todoTableSearchContainer");
const navBtnFilter = document.getElementById("navBtnFilter");
const navBtnView = document.getElementById("navBtnView");
const drawers = document.querySelectorAll(".drawer");
document.querySelectorAll(".drawerClose").forEach(function(drawerClose) {
drawerClose.onclick = function() {
showDrawer(false).then(function(result) {
console.log(result);
}).catch(function(error) {
handleError(error);
});
// trigger matomo event
if(userData.matomoEvents) _paq.push(["trackEvent", "Drawer", "Click on close button"])
}
});
document.getElementById("viewDrawer").addEventListener ("keydown", function () {
})
getHandleElement.addEventListener("mousedown", startDragging);
navBtnFilter.onclick = function() {
// close filter drawer first
viewDrawer.classList.remove("is-active")
navBtnView.classList.remove("is-highlighted")
showDrawer("toggle", this.id, filterDrawer.id).then(function(result) {
console.log(result);
}).catch(function(error) {
handleError(error);
});
// trigger matomo event
if(userData.matomoEvents) _paq.push(["trackEvent", "Menu", "Click on filter"]);
}
navBtnView.onclick = function() {
// close filter drawer first
filterDrawer.classList.remove("is-active")
navBtnFilter.classList.remove("is-highlighted")
showDrawer("toggle", this.id, viewDrawer.id).then(function(result) {
console.log(result);
}).catch(function(error) {
handleError(error);
});
// trigger matomo event
if(userData.matomoEvents) _paq.push(["trackEvent", "Menu", "Click on view"]);
}
// open filter drawer if it has been persisted
if(userData.filterDrawer) {
showDrawer(true, navBtnFilter.id, filterDrawer.id).then(function(result) {
console.log(result);
}).catch(function(error) {
handleError(error);
});
// open view drawer if it has been persisted
} else if(userData.viewDrawer) {
showDrawer(true, navBtnView.id, viewDrawer.id).then(function(result) {
console.log(result);
}).catch(function(error) {
handleError(error);
});
}
document.getElementById("filterDrawer").addEventListener ("keydown", function () {
if(event.key === "Escape") {
showDrawer(false, navBtnView.id, viewDrawer.id).then(function(result) {
showDrawer(false, navBtnFilter.id, this.id).then(function(result) {
console.log(result);
}).catch(function(error) {
handleError(error);
});
}
});
const drawers = document.querySelectorAll(".drawer");
const drawerCloser = document.querySelectorAll(".drawerClose").forEach(function(drawerClose) {
drawerClose.onclick = function() {
showDrawer(false).then(function(result) {
document.getElementById("viewDrawer").addEventListener ("keydown", function () {
if(event.key === "Escape") {
showDrawer(false, document.getElementById("navBtnView").id, this.id).then(function(result) {
console.log(result);
}).catch(function(error) {
handleError(error);
});
// trigger matomo event
if(window.consent) _paq.push(["trackEvent", "Drawer", "Click on close button"])
}
})
});
function showDrawer(variable, buttonId, drawerId) {
try {
const viewToggleSortCompletedLast = document.getElementById("viewToggleSortCompletedLast");
switch (drawerId) {
case "viewDrawer":
if(userData.showCompleted) {
......@@ -80,7 +93,7 @@ function showDrawer(variable, buttonId, drawerId) {
viewToggleSortCompletedLast.parentElement.classList.add("is-hidden");
}
// set viewContainer sort select
Array.from(viewSelectSortBy.options).forEach(function(item) {
Array.from(document.getElementById("viewSelectSortBy").options).forEach(function(item) {
if(item.value===userData.sortBy) item.selected = true
});
break;
......@@ -137,4 +150,4 @@ function showDrawer(variable, buttonId, drawerId) {
}
}
export { setPaneWidth, showDrawer };
export { showDrawer };
"use strict";
import { userData, setUserData } from "../render.js";
// ########################################################################################################################
// RESIZEABLE FILTER DRAWER
// https://spin.atomicobject.com/2019/11/21/creating-a-resizable-html-element/
// ########################################################################################################################
export const getHandleElement = document.getElementById("handle");
const getResizeableElement = () => { return document.getElementById("drawerContainer"); };
const minPaneSize = 400;
const maxPaneSize = document.body.clientWidth * .75
const setPaneWidth = (width) => {
getResizeableElement().style.setProperty("--resizeable-width", `${width}px`);
setUserData("drawerWidth", `${width}`);
};
// restore persisted width of filter drawer
if(userData.drawerWidth) setPaneWidth(userData.drawerWidth);
const getPaneWidth = () => {
const pxWidth = getComputedStyle(getResizeableElement())
.getPropertyValue("--resizeable-width");
return parseInt(pxWidth, 10);
};
export const startDragging = (event) => {
event.preventDefault();
//const host = getResizeableElement();
const startingPaneWidth = getPaneWidth();
const xOffset = event.pageX;
const mouseDragHandler = (moveEvent) => {
moveEvent.preventDefault();
const primaryButtonPressed = moveEvent.buttons === 1;
if (!primaryButtonPressed) {
setPaneWidth(Math.min(Math.max(getPaneWidth(), minPaneSize), maxPaneSize));
document.body.removeEventListener("pointermove", mouseDragHandler);
return;
}
const paneOriginAdjustment = "left" === "right" ? 1 : -1;
setPaneWidth((xOffset - moveEvent.pageX ) * paneOriginAdjustment + startingPaneWidth);
};
document.body.addEventListener("pointermove", mouseDragHandler);
};
getResizeableElement().style.setProperty("--max-width", `${maxPaneSize}px`);
getResizeableElement().style.setProperty("--min-width", `${minPaneSize}px`);
"use strict";
import { userData, handleError, translations, setUserData, startBuilding } from "../../render.js";
import { userData, handleError, translations, setUserData, startBuilding, _paq } from "../render.js";
import { items, generateGroups, generateTable } from "./todos.mjs";
let categories, filtersCounted, selectedFilters;
import { isToday, isPast, isFuture } from "./date.mjs";
const modalFormInput = document.getElementById("modalFormInput");
const todoTableSearch = document.getElementById("todoTableSearch");
const autoCompleteContainer = document.getElementById("autoCompleteContainer");
const todoFilters = document.getElementById("todoFilters");
let categories, filtersCounted, selectedFilters, container, headline;
function filterItems(items, searchString) {
try {
// selected filters are empty, unless they were persisted
if(userData.selectedFilters && userData.selectedFilters.length>0) {
var selectedFilters = JSON.parse(userData.selectedFilters);
selectedFilters = JSON.parse(userData.selectedFilters);
} else