Commit 7e98cd72 authored by ransome1's avatar ransome1
Browse files

Improved ipc events and file watcher

parent 0d7a4555
......@@ -56,7 +56,8 @@ body input[type=checkbox]:active {
outline: none;
box-shadow: none !important;
}
body input[type=range] {
body input[type=range],
body input[type=range]:focus {
background: #5a5a5a !important;
}
body input[type=range]::-webkit-slider-thumb {
......@@ -686,6 +687,7 @@ input[type=range]:focus {
padding: 0.1em;
-webkit-appearance: none;
border-radius: 0.65em;
background: #ccc !important;
}
input[type=range]::-webkit-slider-thumb {
......
This diff is collapsed.
"use strict";
import { _paq } from "./matomo.mjs";
import { appData, userData, setUserData, translations, startFileWatcher } from "../render.js";
import { appData, userData, setUserData, translations } from "../render.js";
import { createModalJail } from "./jail.mjs";
import { focusRow } from "./keyboard.mjs";
import { handleError } from "./helper.mjs";
......@@ -105,11 +105,7 @@ function removeFileFromList(index, removeFile) {
handleError(error);
});
startFileWatcher(userData.files[newIndex][1]).then(response => {
console.info(response);
}).catch(error => {
handleError(error);
});
window.api.send("startFileWatcher", userData.files[newIndex][1]);
return Promise.resolve("Success: File removed from tab");
......@@ -128,11 +124,7 @@ function selectFileFromList(index) {
focusRow(false);
// load new file
startFileWatcher(userData.files[index][1]).then(response => {
console.info(response);
}).catch(error => {
handleError(error);
});
window.api.send("startFileWatcher", userData.files[index][1]);
return Promise.resolve("Success: File selected");
......
......@@ -9,7 +9,7 @@ import { show, setPriority, resetForm } from "./form.mjs";
import { showDrawer } from "./drawer.mjs";
import { showModal } from "./content.mjs";
import { triggerToggle } from "./toggles.mjs";
import { appData, userData, translations, startFileWatcher } from "../render.js";
import { appData, userData, translations } from "../render.js";
const
autoCompleteContainer = document.getElementById("autoCompleteContainer"),
......@@ -277,17 +277,9 @@ export async function registerShortcuts() {
if(event.ctrlKey && !event.shiftKey && event.keyCode === 9) {
let index = userData.files.findIndex(file => file[0] === 1);
if(!userData.files[index+1]) {
startFileWatcher(userData.files[0][1]).then(response => {
console.info(response);
}).catch(error => {
handleError(error);
});
window.api.send("startFileWatcher", userData.files[0][1]);
} else {
startFileWatcher(userData.files[index+1][1]).then(response => {
console.info(response);
}).catch(error => {
handleError(error);
});
window.api.send("startFileWatcher", userData.files[index+1][1]);
}
return false;
}
......@@ -295,17 +287,9 @@ export async function registerShortcuts() {
if(event.ctrlKey && event.shiftKey && event.keyCode === 9) {
let index = userData.files.findIndex(file => file[0] === 1);
if(!userData.files[index-1]) {
startFileWatcher(userData.files[userData.files.length-1][1]).then(response => {
console.info(response);
}).catch(error => {
handleError(error);
});
window.api.send("startFileWatcher", userData.files[userData.files.length-1][1]);
} else {
startFileWatcher(userData.files[index-1][1]).then(response => {
console.info(response);
}).catch(error => {
handleError(error);
});
window.api.send("startFileWatcher", userData.files[index-1][1]);
}
return false;
}
......@@ -325,11 +309,7 @@ export async function registerShortcuts() {
// throw false at this function and current row will be reset
focusRow(false);
//
startFileWatcher(filesInTabBar[event.key-1][1]).then(function(response) {
console.info(response);
}).catch(function(error) {
handleError(error);
});
window.api.send("startFileWatcher", filesInTabBar[event.key-1][1]);
}
return false;
}
......
"use strict";
import { userData, translations } from "../render.js";
import { generateFileList } from "./files.mjs";
import { generateFileList, generateFileTabs } from "./files.mjs";
import { show } from "./form.mjs";
import { showDrawer } from "./drawer.mjs";
import { _paq } from "./matomo.mjs";
import { handleError } from "./helper.mjs";
import { handleError, setupInterface } from "./helper.mjs";
const addTodoContainerButton = document.getElementById("addTodoContainerButton");
const addTodoContainerHeadline = document.getElementById("addTodoContainerHeadline");
......@@ -12,6 +12,7 @@ const addTodoContainerSubtitle = document.getElementById("addTodoContainerSubtit
const btnAddTodoContainer = document.getElementById("btnAddTodoContainer");
const btnOnboardingCreateTodoFile = document.getElementById("btnOnboardingCreateTodoFile");
const btnOnboardingOpenTodoFile = document.getElementById("btnOnboardingOpenTodoFile");
const fileTabBar = document.getElementById("fileTabBar");
const navBtnAddTodo = document.getElementById("navBtnAddTodo");
const navBtnFilter = document.getElementById("navBtnFilter");
const navBtnView = document.getElementById("navBtnView");
......@@ -78,9 +79,10 @@ btnOnboardingOpenTodoFile.onclick = function() {
export function showOnboarding() {
try {
// show onboarding
// show standard onboarding
if(arguments[0]) {
fileTabBar.classList.remove("is-active");
navBtnAddTodo.classList.add("is-hidden");
navBtnFilter.classList.add("is-hidden");
navBtnView.classList.add("is-hidden");
......
......@@ -820,13 +820,8 @@ async function archiveTodos() {
// if user archives within done.txt file, operating is canceled
if(activeFile.includes("_done.")) return Promise.resolve("Info: Current file seems to be a done.txt file, won't archive")
let contentFromDoneFile = await new Promise(function(resolve) {
window.api.send("getContent", doneFile);
return window.api.receive("getContent", (content) => {
resolve(content);
});
});
let contentFromDoneFile = await window.api.invoke("getContent", doneFile);
if(contentFromDoneFile) {
// create array from done file
......
......@@ -23,7 +23,6 @@ const viewToggleShowEmptyFilters = document.getElementById("viewToggleShowEmptyF
const sortByContainer = document.getElementById("sortByContainer");
const viewToggleDeferredTodos = document.getElementById("viewToggleDeferredTodos");
const viewToggleFileTabs = document.getElementById("viewToggleFileTabs");
const theme = document.getElementById("theme");
sortBy.innerHTML = translations.sortBy;
viewHeadlineAppView.innerHTML = translations.viewHeadlineAppView;
......
......@@ -81,17 +81,11 @@ function getContent(file) {
const activeFile = getActiveFile();
if(process.mas) stopAccessingSecurityScopedResource = app.startAccessingSecurityScopedResource(activeFile[3])
if(!fs.existsSync(file)) {
// TODO: understand what's happening here
return Promise.resolve(fs.writeFile(file, "", function(error) {
if(error) return Promise.reject("Error: Could not create file")
return "";
}));
}
return Promise.resolve(fs.readFileSync(file, {encoding: "utf-8"}, function(err,data) {
return Promise.resolve(fs.readFileSync(file, {encoding: "utf-8"}, function(error, data) {
if(process.mas) stopAccessingSecurityScopedResource()
return data;
}));
} catch (error) {
return Promise.reject("Error in getContent(): " + error);
}
......@@ -119,13 +113,12 @@ function openFileChooser(args) {
extensions: ["txt", "md", "todo"]
}],
properties: ["openFile"]
}).then(({ canceled, filePaths, bookmarks }) => {
if(canceled || filePaths.length === 0) return false;
const bookmark = bookmarks ? bookmarks[0] : undefined
if(fileWatcher) fileWatcher.close()
startFileWatcher(filePaths[0].toString(), bookmark).then(response => {
console.info(response);
......@@ -151,6 +144,7 @@ function openFileChooser(args) {
extensions: ["txt", "md", "todo"]
}],
properties: ["openFile", "createDirectory"]
}).then(({ canceled, filePath, bookmark }) => {
if(canceled || !filePath) return false;
......@@ -159,7 +153,7 @@ function openFileChooser(args) {
//if(process.mas && bookmark) userData.set("securityBookmark", bookmark);
// close filewatcher, otherwise the change of file will trigger a duplicate refresh
if(fileWatcher) fileWatcher.close()
//if(fileWatcher) fileWatcher.close()
fs.writeFile(filePath, "", function() {
startFileWatcher(filePath, bookmark).then(response => {
......@@ -180,152 +174,112 @@ function openFileChooser(args) {
return Promise.reject("Error in openFileChooser(): " + error);
}
}
function refreshFiles() {
try {
// only for MAS (Sandboxed)
// https://gist.github.com/ngehlert/74d5a26990811eed59c635e49134d669
const activeFile = getActiveFile();
if(process.mas) stopAccessingSecurityScopedResource = app.startAccessingSecurityScopedResource(activeFile[3])
// 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("Info: Not found and removed from file list: " + file[1])
object.splice(index, 1);
// 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);
}
if(process.mas) stopAccessingSecurityScopedResource()
});
userData.set("files", userData.data.files);
return Promise.resolve("Success: Files refreshed");
} catch (error) {
return Promise.reject("Error in startFileWatcher(): " + error);
}
}
function getActiveFile() {
const index = userData.data.files.findIndex(file => file[0] === 1);
if(index === -1) return false
return userData.data.files[index];
}
async function startFileWatcher(file, bookmark) {
async function startFileWatcher(pathToFile, bookmark) {
try {
// TODO: consider doing this in store.config.js
// 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
// check if any saved files are not existing anymore and remove them from config
await refreshFiles().then(response => {
console.info(response);
}).catch(error => {
console.error(error);
});
// 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);
if(process.env.SLEEK_CUSTOM_FILE && fs.existsSync(process.env.SLEEK_CUSTOM_FILE)) pathToFile = process.env.SLEEK_CUSTOM_FILE
// if requested file is not found on disk, this function will be aborted
if(!fs.existsSync(pathToFile)) {
showNotification({
title: "File not found on disk",
body: "The requested file cannot be found on disk: " + pathToFile,
timeoutType: "never",
silent: false
});
return Promise.reject("Error: The requested file cannot be found on disk: " + pathToFile);
}
// set the current file to not active
// TODO: use getActiveFile()
const indexOfActiveFile = userData.data.files.findIndex(file => file[0] === 1);
if(indexOfActiveFile !== -1) userData.data.files[indexOfActiveFile][0] = 0;
// if file is in array, get the index
const index = userData.data.files.findIndex(element => element[1] === file);
const index = userData.data.files.findIndex(element => element[1] === pathToFile);
// this is a new file so it is pushed to array
// or this is an existing file so it is just set active
if(index === -1) {
(process.mas && bookmark) ? userData.data.files.push([1, file, 1, bookmark]) : userData.data.files.push([1, file, 1])
(process.mas && bookmark) ? userData.data.files.push([1, pathToFile, 1, bookmark]) : userData.data.files.push([1, pathToFile, 1])
// or this is an existing file so it is just set active
} else {
userData.data.files[index][0] = 1;
userData.data.files[index][2] = 1;
}
// persist the new file
userData.set("files", userData.data.files);
// get activ file and its attributes
const file = getActiveFile();
// only for MAS (Sandboxed)
// https://gist.github.com/ngehlert/74d5a26990811eed59c635e49134d669
const activeFile = getActiveFile();
if(process.mas) stopAccessingSecurityScopedResource = app.startAccessingSecurityScopedResource(activeFile[3])
if(process.mas) stopAccessingSecurityScopedResource = app.startAccessingSecurityScopedResource(file[3])
if(fileWatcher) await fileWatcher.close().then(() => console.log("Info: Filewatcher closed"));
fileWatcher = await chokidar.watch(file[1])
fileWatcher
.on("add", async function(fileToLink) {
const content = await getContent(fileToLink);
mainWindow.webContents.send("buildTable", [content])
// change window title
const title = path.basename(fileToLink) + " - sleek";
mainWindow.setTitle(title);
console.log("Info: File " + fileToLink + " has been added");
})
.on("unlink", function(unlinkedFile) {
showNotification({
title: "File not found on disk",
body: "Currently watched file not found on disk: " + unlinkedFile,
timeoutType: "never",
silent: false
});
// set unlinked file inactive
const index = userData.data.files.findIndex(element => element[1] === unlinkedFile);
if(index !== -1) {
userData.data.files[index][0] = 0;
userData.data.files[index][2] = 0;
}
userData.set("files", userData.data.files);
// check if file exists, otherwise abort and call onbaording
if(!fs.existsSync(file)) {
mainWindow.webContents.send("triggerFunction", "showOnboarding", [true]);
return Promise.reject("Info: File not found on disk, starting onboarding");
}
console.log("Info: File " + unlinkedFile + " has been unlinked");
// an active filewatcher will be destroyed first
if(fileWatcher) {
await fileWatcher.close().then(() => console.log("Info: Filewatcher instance closed"));
await fileWatcher.unwatch();
fileWatcher = null;
}
})
// create a new filewatcher
fileWatcher = await chokidar.watch(file);
.on("error", function(error) {
console.error("Error: " + error);
})
fileWatcher
.on("add", async function() {
const content = await getContent(file).then(content => {
return content;
}).catch(error => {
console.log(error);
});
mainWindow.webContents.send("buildTable", [content])
console.log("Info: File " + file + " has been added");
})
.on("change", async function(linkedFile) {
.on("unlink", async function() {
startFileWatcher().then(response => {
console.info(response);
}).catch(error => {
console.error(error);
});
console.log("Info: File " + file + " has been unlinked");
})
const content = await getContent(linkedFile);
mainWindow.webContents.send("buildTable", [content])
.on("change", async function() {
// wait 10ms before rereading in case the file is being updated with a delay
setTimeout(async function() {
const content = await getContent(file).then(content => {
return content;
}).catch(error => {
console.log(error);
});
mainWindow.webContents.send("buildTable", [content])
console.log("Info: File " + file + " has changed");
}, 10);
})
console.log("Info: File " + linkedFile + " has changed");
// change window title
const title = path.basename(file) + " - sleek";
mainWindow.title = title;
mainWindow.webContents.once("did-finish-load",() => {
mainWindow.setTitle(title);
});
})
// remove access to secure ressource
if(process.mas) stopAccessingSecurityScopedResource()
// persist
userData.set("files", userData.data.files);
return Promise.resolve("Success: Filewatcher is watching: " + file);
return Promise.resolve("Success: Filewatcher is watching: " + file[1]);
} catch (error) {
// if some file related crash occurs, onboarding will be triggered
......@@ -422,10 +376,6 @@ function showNotification(config) {
}
function configureWindowEvents() {
try {
// nativeTheme.on("updated", () => {
// (nativeTheme.shouldUseDarkColors) ? mainWindow.webContents.executeJavaScript(`body.classList.add("dark");`) : mainWindow.webContents.executeJavaScript(`body.classList.remove("dark");`)
// })
mainWindow
.on("resize", function() {
userData.set("width", this.getBounds().width);
......@@ -455,7 +405,6 @@ function configureWindowEvents() {
})
.on("did-finish-load", function() {
nativeTheme.themeSource = userData.data.theme;
//(nativeTheme.shouldUseDarkColors) ? mainWindow.webContents.executeJavaScript(`body.classList.add("dark");`) : mainWindow.webContents.executeJavaScript(`body.classList.remove("dark");`)
});
ipcMain.handle("userData", (event, args) => {
......@@ -475,34 +424,42 @@ function configureWindowEvents() {
return translations
});
ipcMain.handle("getContent", async (event, file) => {
const content = await getContent(file);
return content;
})
ipcMain
.on("setTheme", (event, theme) => {
if(!theme) theme = (nativeTheme.shouldUseDarkColors) ? "light" : "dark"
nativeTheme.themeSource = theme;
userData.set("theme", nativeTheme.themeSource);
})
.on("closeWindow", () => {
.once("closeWindow", () => {
mainWindow.close()
})
.on("changeLanguage", async (event, language) => {
.once("restart", () => {
app.relaunch();
app.exit();
app.quit();
})
.once("changeLanguage", async (event, language) => {
event.preventDefault();
await userData.set("language", language);
app.relaunch();
app.exit();
});
ipcMain
.on("setTheme", (event, theme) => {
if(!theme) theme = (nativeTheme.shouldUseDarkColors) ? "light" : "dark"
nativeTheme.themeSource = theme;
userData.set("theme", nativeTheme.themeSource);
})
.on("writeToFile", function(event, args) {
const content = args[0];
let file = args[1];
if(!file) {
const index = userData.data.files.findIndex(file => file[0] ===1 );
file = userData.data.files[index][1];
}
if(!file) file = getActiveFile()[1]
// only for MAS (Sandboxed)
// https://gist.github.com/ngehlert/74d5a26990811eed59c635e49134d669
const activeFile = getActiveFile();
if(process.mas) stopAccessingSecurityScopedResource = app.startAccessingSecurityScopedResource(activeFile[3])
if(process.mas) stopAccessingSecurityScopedResource = app.startAccessingSecurityScopedResource(getActiveFile()[3])
if(file) fs.writeFileSync(file, content, {encoding: "utf-8"});
fs.writeFileSync(file, content, {encoding: "utf-8"});
if(process.mas) stopAccessingSecurityScopedResource()
......@@ -516,14 +473,7 @@ function configureWindowEvents() {
})
.on("startFileWatcher", (event, file) => {
startFileWatcher(file).then(response => {
mainWindow.webContents.send("startFileWatcher", response);
}).catch(error => {
console.error(error);
});
})
.on("getContent", (event, file) => {
getContent(file).then(content => {
mainWindow.webContents.send("getContent", content);
console.log(response)
}).catch(error => {
console.error(error);
});
......@@ -537,11 +487,6 @@ function configureWindowEvents() {
})
.on("update-badge", (event, count) => {
if(appData.os === "mac") app.setBadgeCount(count);
})
.on("restart", () => {
app.relaunch();
app.exit();
app.quit();
});
return Promise.resolve("Success: Window events setup");
......@@ -570,6 +515,7 @@ function setupTray() {
let trayFiles = new Array;
// build file selection
if(userData.data.files && userData.data.files.length > 1) {
userData.data.files.forEach((file) => {
const menuItem = {
......@@ -589,7 +535,7 @@ function setupTray() {
{ type: "separator" },
);
}
let contextMenu = [
const contextMenu = [
{
label: translations.windowButtonOpenFile,
click: function() {
......@@ -611,13 +557,12 @@ function setupTray() {
}
}
]
let menu;
(trayFiles.length > 0) ? menu = Menu.buildFromTemplate(trayFiles.concat(contextMenu)) : menu = Menu.buildFromTemplate(contextMenu)
let menu = (trayFiles.length > 0) ? Menu.buildFromTemplate(trayFiles.concat(contextMenu)) : Menu.buildFromTemplate(contextMenu)
tray.setContextMenu(menu)
tray.setToolTip("sleek")
tray.on("click", function() {
// don't do this on MacOS
if(appData.os === "mac") return false;
//if(appData.os === "mac") return false;
mainWindow.show();
});
}
......@@ -859,13 +804,13 @@ async function createWindow() {
// setup reload intervall (reload every 10 minutes)
setInterval(async () => {
if(!getActiveFile() && mainWindow.isFocused()) return false;
const content = await getContent(getActiveFile()[1]).then(content => {
return content;
}).catch(error => {
console.error(error);
});
const content = await getContent(getActiveFile()[1]);
mainWindow.webContents.send("buildTable", [content]);
}, 600000);
return Promise.resolve("Success: Renderer window created");
......@@ -889,6 +834,9 @@ if(!process.mas && (!app.requestSingleInstanceLock() && process.env.SLEEK_MULTIP
app.on("ready", () => {
// in tray mode, dock icon is hidden
if(userData.data.tray) app.dock.hide()
// setup autoupdater for AppImage build
if(appData.channel === "AppImage" && userData.data.autoUpdate) autoUpdaterAppImage.checkForUpdatesAndNotify()
......@@ -897,7 +845,7 @@ if(!process.mas && (!app.requestSingleInstanceLock() && process.env.SLEEK_MULTIP