From 42017fde5f2c2969761d87ee7c136a88aa17732d Mon Sep 17 00:00:00 2001 From: zadam Date: Wed, 5 Feb 2020 22:08:45 +0100 Subject: [PATCH] refactored access to options on frontend --- package-lock.json | 18 ++--- package.json | 4 +- src/public/javascripts/desktop.js | 43 +---------- .../javascripts/dialogs/options/appearance.js | 3 +- .../javascripts/dialogs/options/code_notes.js | 11 ++- .../javascripts/dialogs/options/other.js | 4 -- .../javascripts/dialogs/options/sidebar.js | 2 - .../javascripts/services/app_context.js | 23 ++++-- src/public/javascripts/services/clipboard.js | 2 +- .../javascripts/services/entrypoints.js | 6 -- .../javascripts/services/hoisted_note.js | 27 ++----- .../javascripts/services/load_results.js | 10 +++ src/public/javascripts/services/mime_types.js | 11 ++- src/public/javascripts/services/options.js | 58 +++++---------- .../services/protected_session_holder.js | 19 ++--- .../javascripts/services/search_notes.js | 22 ++++-- .../javascripts/services/spell_check.js | 4 +- src/public/javascripts/services/split.js | 23 ++---- .../javascripts/services/tab_context.js | 6 -- src/public/javascripts/services/tree_cache.js | 7 ++ src/public/javascripts/services/ws.js | 2 +- src/public/javascripts/services/zoom.js | 72 +++++++++---------- src/public/javascripts/widgets/note_tree.js | 4 +- src/public/javascripts/widgets/search_box.js | 19 +---- .../widgets/side_pane_container.js | 6 +- .../javascripts/widgets/sidebar_toggle.js | 72 +++++++++++++++++++ .../javascripts/widgets/title_bar_buttons.js | 62 ++++++++-------- src/public/stylesheets/desktop.css | 14 ---- src/services/repository.js | 4 +- src/services/sync.js | 11 +-- src/services/ws.js | 3 + src/views/desktop.ejs | 6 -- 32 files changed, 267 insertions(+), 311 deletions(-) create mode 100644 src/public/javascripts/widgets/sidebar_toggle.js diff --git a/package-lock.json b/package-lock.json index b09696ac6..203d65d5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2157,9 +2157,9 @@ "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=" }, "dayjs": { - "version": "1.8.19", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.19.tgz", - "integrity": "sha512-7kqOoj3oQSmqbvtvGFLU5iYqies+SqUiEGNT0UtUPPxcPYgY1BrkXR0Cq2R9HYSimBXN+xHkEN4Hi399W+Ovlg==" + "version": "1.8.20", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.20.tgz", + "integrity": "sha512-mH0MCDxw6UCGJYxVN78h8ugWycZAO8thkj3bW6vApL5tS0hQplIDdAQcmbvl7n35H0AKdCJQaArTrIQw2xt4Qg==" }, "debug": { "version": "4.1.1", @@ -3782,9 +3782,9 @@ } }, "file-type": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-14.0.0.tgz", - "integrity": "sha512-+gxNvurlwHfTohZC6gqf0ybMl+cXYB9f1x++kw9AgKItdFx20J0fV9wCVR38a5/jphL5EUcusJ9tLYkPRtGHaw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-14.1.0.tgz", + "integrity": "sha512-HfxnzrPH+LLClSAsno88/0frRtamu1XfqEP4IP/8RqBmqQnBQkemv3Udde0t53wZmrdOtc70aaR9WUHyQhjCUQ==", "requires": { "readable-web-to-node-stream": "^2.0.0", "strtok3": "^6.0.0", @@ -6859,9 +6859,9 @@ "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==" }, "node-abi": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.13.0.tgz", - "integrity": "sha512-9HrZGFVTR5SOu3PZAnAY2hLO36aW1wmA+FDsVkr85BTST32TLCA1H/AEcatVRAsWLyXS3bqUDYCAjq5/QGuSTA==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.14.0.tgz", + "integrity": "sha512-y54KGgEOHnRHlGQi7E5UiryRkH8bmksmQLj/9iLAjoje743YS+KaKB/sDYXgqtT0J16JT3c3AYJZNI98aU/kYg==", "requires": { "semver": "^5.4.1" }, diff --git a/package.json b/package.json index c7fbb170b..6c6a3faf8 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "electron-window-state": "5.0.3", "express": "4.17.1", "express-session": "1.17.0", - "file-type": "14.0.0", + "file-type": "14.1.0", "fs-extra": "8.1.0", "helmet": "3.21.2", "html": "1.0.0", @@ -53,7 +53,7 @@ "jimp": "0.9.3", "mime-types": "2.1.26", "multer": "1.4.2", - "node-abi": "2.13.0", + "node-abi": "2.14.0", "open": "7.0.2", "pngjs": "3.4.0", "portscanner": "2.2.0", diff --git a/src/public/javascripts/desktop.js b/src/public/javascripts/desktop.js index 1e0c25730..f9bc52396 100644 --- a/src/public/javascripts/desktop.js +++ b/src/public/javascripts/desktop.js @@ -28,7 +28,7 @@ import dateNoteService from './services/date_notes.js'; import importService from './services/import.js'; import keyboardActionService from "./services/keyboard_actions.js"; import splitService from "./services/split.js"; -import optionService from "./services/options.js"; +import options from "./services/options.js"; import noteContentRenderer from "./services/note_content_renderer.js"; import appContext from "./services/app_context.js"; @@ -139,43 +139,4 @@ appContext.start(); noteTooltipService.setupGlobalTooltip(); -noteAutocompleteService.init(); - -if (utils.isElectron()) { - import("./services/spell_check.js").then(spellCheckService => spellCheckService.initSpellCheck()); -} - -optionService.waitForOptions().then(options => { - toggleSidebar('left', options.is('leftPaneVisible')); - toggleSidebar('right', options.is('rightPaneVisible')); - - splitService.setupSplit(paneVisible.left, paneVisible.right); -}); - -const paneVisible = {}; - -function toggleSidebar(side, show) { - $(`#${side}-pane`).toggle(show); - $(`#show-${side}-pane-button`).toggle(!show); - $(`#hide-${side}-pane-button`).toggle(show); - - paneVisible[side] = show; -} - -async function toggleAndSave(side, show) { - toggleSidebar(side, show); - - await server.put(`options/${side}PaneVisible/` + show.toString()); - - await optionService.reloadOptions(); - - splitService.setupSplit(paneVisible.left, paneVisible.right); - - appContext.trigger('sidebarVisibilityChanged', {side, show}); -} - -$("#show-right-pane-button").on('click', () => toggleAndSave('right', true)); -$("#hide-right-pane-button").on('click', () => toggleAndSave('right', false)); - -$("#show-left-pane-button").on('click', () => toggleAndSave('left', true)); -$("#hide-left-pane-button").on('click', () => toggleAndSave('left', false)); +noteAutocompleteService.init(); \ No newline at end of file diff --git a/src/public/javascripts/dialogs/options/appearance.js b/src/public/javascripts/dialogs/options/appearance.js index f478bbec5..484d63bc8 100644 --- a/src/public/javascripts/dialogs/options/appearance.js +++ b/src/public/javascripts/dialogs/options/appearance.js @@ -3,6 +3,7 @@ import utils from "../../services/utils.js"; import cssLoader from "../../services/css_loader.js"; import zoomService from "../../services/zoom.js"; import optionsService from "../../services/options.js"; +import appContext from "../../services/app_context.js"; const TPL = `

Settings on this options tab are saved automatically after each change.

@@ -107,7 +108,7 @@ export default class ApperanceOptions { server.put('options/theme/' + newTheme); }); - this.$zoomFactorSelect.on('change', () => { zoomService.setZoomFactorAndSave(this.$zoomFactorSelect.val()); }); + this.$zoomFactorSelect.on('change', () => { appContext.trigger('setZoomFactorAndSave', {zoomFactor: this.$zoomFactorSelect.val()}); }); this.$nativeTitleBarSelect.on('change', () => { const nativeTitleBarVisible = this.$nativeTitleBarSelect.val() === 'show' ? 'true' : 'false'; diff --git a/src/public/javascripts/dialogs/options/code_notes.js b/src/public/javascripts/dialogs/options/code_notes.js index fc5544342..6ed785e10 100644 --- a/src/public/javascripts/dialogs/options/code_notes.js +++ b/src/public/javascripts/dialogs/options/code_notes.js @@ -1,6 +1,5 @@ -import server from "../../services/server.js"; import mimeTypesService from "../../services/mime_types.js"; -import optionsService from "../../services/options.js"; +import options from "../../services/options.js"; const TPL = `

Available MIME types in the dropdown

@@ -14,7 +13,7 @@ export default class CodeNotesOptions { this.$mimeTypes = $("#options-mime-types"); } - async optionsLoaded(options) { + async optionsLoaded() { this.$mimeTypes.empty(); let idCtr = 1; @@ -42,10 +41,8 @@ export default class CodeNotesOptions { this.$mimeTypes.find("input:checked").each( (i, el) => enabledMimeTypes.push($(el).attr("data-mime-type"))); - const opts = { codeNotesMimeTypes: JSON.stringify(enabledMimeTypes) }; + await options.save('codeNotesMimeTypes', JSON.stringify(enabledMimeTypes)); - await server.put('options', opts); - - await optionsService.reloadOptions(); + mimeTypesService.loadMimeTypes(); } } \ No newline at end of file diff --git a/src/public/javascripts/dialogs/options/other.js b/src/public/javascripts/dialogs/options/other.js index dc8bec45e..5d366eb3e 100644 --- a/src/public/javascripts/dialogs/options/other.js +++ b/src/public/javascripts/dialogs/options/other.js @@ -101,8 +101,6 @@ export default class ProtectedSessionOptions { const eraseNotesAfterTimeInSeconds = this.$eraseNotesAfterTimeInSeconds.val(); server.put('options', { 'eraseNotesAfterTimeInSeconds': eraseNotesAfterTimeInSeconds }).then(() => { - optionsService.reloadOptions(); - toastService.showMessage("Options change have been saved."); }); @@ -115,8 +113,6 @@ export default class ProtectedSessionOptions { const protectedSessionTimeout = this.$protectedSessionTimeout.val(); server.put('options', { 'protectedSessionTimeout': protectedSessionTimeout }).then(() => { - optionsService.reloadOptions(); - toastService.showMessage("Options change have been saved."); }); diff --git a/src/public/javascripts/dialogs/options/sidebar.js b/src/public/javascripts/dialogs/options/sidebar.js index 9ba36d264..ff7eaeff1 100644 --- a/src/public/javascripts/dialogs/options/sidebar.js +++ b/src/public/javascripts/dialogs/options/sidebar.js @@ -104,8 +104,6 @@ export default class SidebarOptions { }); await server.put('options', opts); - - optionsService.reloadOptions(); } parseJsonSafely(str) { diff --git a/src/public/javascripts/services/app_context.js b/src/public/javascripts/services/app_context.js index 79c7a3e0e..f9ecd9b84 100644 --- a/src/public/javascripts/services/app_context.js +++ b/src/public/javascripts/services/app_context.js @@ -29,10 +29,12 @@ import bundleService from "./bundle.js"; import DialogEventComponent from "./dialog_events.js"; import Entrypoints from "./entrypoints.js"; import CalendarWidget from "../widgets/calendar.js"; -import optionsService from "./options.js"; +import options from "./options.js"; import utils from "./utils.js"; import treeService from "./tree.js"; import SidePaneContainer from "../widgets/side_pane_container.js"; +import ZoomService from "./zoom.js"; +import SidebarToggle from "../widgets/sidebar_toggle.js"; class AppContext { constructor() { @@ -45,7 +47,9 @@ class AppContext { this.activeTabId = null; } - start() { + async start() { + options.load(await server.get('options')); + this.showWidgets(); this.loadTabs(); @@ -54,8 +58,6 @@ class AppContext { } async loadTabs() { - const options = await optionsService.waitForOptions(); - const openTabs = options.getJson('openTabs') || []; await treeCache.initializedPromise; @@ -186,14 +188,25 @@ class AppContext { $centerPane.after(rightPaneContainer.render()); + const sidebarToggleWidget = new SidebarToggle(this); + + $centerPane.after(sidebarToggleWidget.render()); + this.components = [ new Entrypoints(), new DialogEventComponent(this), ...topPaneWidgets, leftPaneContainer, ...centerPaneWidgets, - rightPaneContainer + rightPaneContainer, + sidebarToggleWidget ]; + + if (utils.isElectron()) { + this.components.push(new ZoomService(this)); + + import("./spell_check.js").then(spellCheckService => spellCheckService.initSpellCheck()); + } } trigger(name, data, sync = false) { diff --git a/src/public/javascripts/services/clipboard.js b/src/public/javascripts/services/clipboard.js index 45489d6d4..eac84ab3a 100644 --- a/src/public/javascripts/services/clipboard.js +++ b/src/public/javascripts/services/clipboard.js @@ -70,7 +70,7 @@ function copy(nodes) { function cut(nodes) { clipboardBranchIds = nodes - .filter(node => node.data.noteId !== hoistedNoteService.getHoistedNoteNoPromise()) + .filter(node => node.data.noteId !== hoistedNoteService.getHoistedNoteId()) .filter(node => node.getParent().data.noteType !== 'search') .map(node => node.key); diff --git a/src/public/javascripts/services/entrypoints.js b/src/public/javascripts/services/entrypoints.js index 0f9f23fab..0873eeb39 100644 --- a/src/public/javascripts/services/entrypoints.js +++ b/src/public/javascripts/services/entrypoints.js @@ -57,13 +57,7 @@ export default class Entrypoints extends Component { }); } - zoomOutListener() { - zoomService.decreaseZoomFactor(); - } - zoomInListener() { - zoomService.increaseZoomFactor(); - } async createNoteIntoDayNoteListener() { const todayNote = await dateNoteService.getTodayNote(); diff --git a/src/public/javascripts/services/hoisted_note.js b/src/public/javascripts/services/hoisted_note.js index d7d42ed9c..3d74d1d56 100644 --- a/src/public/javascripts/services/hoisted_note.js +++ b/src/public/javascripts/services/hoisted_note.js @@ -1,30 +1,16 @@ -import optionsService from './options.js'; -import server from "./server.js"; +import options from './options.js'; import appContext from "./app_context.js"; import treeService from "./tree.js"; -let hoistedNoteId = 'root'; - -optionsService.waitForOptions().then(options => { - hoistedNoteId = options.get('hoistedNoteId'); -}); - -function getHoistedNoteNoPromise() { - return hoistedNoteId; -} - -async function getHoistedNoteId() { - await optionsService.waitForOptions(); - - return hoistedNoteId; +function getHoistedNoteId() { + return options.get('hoistedNoteId'); } async function setHoistedNoteId(noteId) { - hoistedNoteId = noteId; + await options.save('hoistedNoteId', noteId); - await server.put('options/hoistedNoteId/' + noteId); - - appContext.trigger('hoistedNoteChanged', {hoistedNoteId}); + // FIXME - just use option load event + appContext.trigger('hoistedNoteChanged', {noteId}); } async function unhoist() { @@ -69,7 +55,6 @@ async function checkNoteAccess(notePath) { export default { getHoistedNoteId, - getHoistedNoteNoPromise, setHoistedNoteId, unhoist, isTopLevelNode, diff --git a/src/public/javascripts/services/load_results.js b/src/public/javascripts/services/load_results.js index 93ed405eb..eb2c410dd 100644 --- a/src/public/javascripts/services/load_results.js +++ b/src/public/javascripts/services/load_results.js @@ -14,6 +14,8 @@ export class LoadResults { this.noteRevisions = []; this.contentNoteIdToSourceId = []; + + this.options = []; } addNote(noteId, sourceId) { @@ -90,4 +92,12 @@ export class LoadResults { return this.contentNoteIdToSourceId.find(l => l.noteId === noteId && l.sourceId !== sourceId); } + + addOption(name) { + this.options.push(name); + } + + isOptionReloaded(name) { + this.options.includes(name); + } } \ No newline at end of file diff --git a/src/public/javascripts/services/mime_types.js b/src/public/javascripts/services/mime_types.js index 1b9229a10..a4919f03b 100644 --- a/src/public/javascripts/services/mime_types.js +++ b/src/public/javascripts/services/mime_types.js @@ -1,4 +1,4 @@ -import optionsService from "./options.js"; +import options from "./options.js"; const MIME_TYPES_DICT = [ { default: true, title: "Plain text", mime: "text/plain" }, @@ -161,7 +161,7 @@ const MIME_TYPES_DICT = [ let mimeTypes = null; -function loadMimeTypes(options) { +function loadMimeTypes() { mimeTypes = JSON.parse(JSON.stringify(MIME_TYPES_DICT)); // clone const enabledMimeTypes = options.getJson('codeNotesMimeTypes') @@ -172,16 +172,15 @@ function loadMimeTypes(options) { } } -optionsService.addLoadListener(loadMimeTypes); - async function getMimeTypes() { if (mimeTypes === null) { - loadMimeTypes(await options.waitForOptions()); + loadMimeTypes(); } return mimeTypes; } export default { - getMimeTypes + getMimeTypes, + loadMimeTypes } \ No newline at end of file diff --git a/src/public/javascripts/services/options.js b/src/public/javascripts/services/options.js index 67059f1c0..a92b8151e 100644 --- a/src/public/javascripts/services/options.js +++ b/src/public/javascripts/services/options.js @@ -1,11 +1,9 @@ import server from "./server.js"; -let optionsReady; - const loadListeners = []; class Options { - constructor(arr) { + load(arr) { this.arr = arr; } @@ -37,45 +35,21 @@ class Options { is(key) { return this.arr[key] === 'true'; } + + set(key, value) { + this.arr[key] = value; + } + + async save(key, value) { + this.set(key, value); + + const payload = {}; + payload[key] = value; + + await server.put(`options`, payload); + } } -function reloadOptions() { - optionsReady = new Promise((resolve, reject) => { - server.get('options').then(optionArr => { - const options = new Options(optionArr); +const options = new Options(); - resolve(options); - - for (const listener of loadListeners) { - listener(options); - } - }); - }); - - return optionsReady; -} - -/** - * just waits for some options without triggering reload - * - * @return {Options} - */ -async function waitForOptions() { - return await optionsReady; -} - -reloadOptions(); // initial load - -function addLoadListener(listener) { - loadListeners.push(listener); - - // useful when listener has been added after the promise resolved, but can cause double emit if not yet - // that should not be an issue though - optionsReady.then(listener); -} - -export default { - addLoadListener, - reloadOptions, - waitForOptions -} \ No newline at end of file +export default options; \ No newline at end of file diff --git a/src/public/javascripts/services/protected_session_holder.js b/src/public/javascripts/services/protected_session_holder.js index 1920c88c8..0c180e7fe 100644 --- a/src/public/javascripts/services/protected_session_holder.js +++ b/src/public/javascripts/services/protected_session_holder.js @@ -1,23 +1,19 @@ import utils from "./utils.js"; -import optionsService from './options.js'; +import options from './options.js'; const PROTECTED_SESSION_ID_KEY = 'protectedSessionId'; -let lastProtectedSessionOperationDate = null; -let protectedSessionTimeout = null; - -optionsService.addLoadListener(options => setProtectedSessionTimeout(options.getInt('protectedSessionTimeout'))); +let lastProtectedSessionOperationDate = 0; setInterval(() => { - if (lastProtectedSessionOperationDate !== null && Date.now() - lastProtectedSessionOperationDate.getTime() > protectedSessionTimeout * 1000) { + const protectedSessionTimeout = options.getInt('protectedSessionTimeout'); + if (lastProtectedSessionOperationDate + && Date.now() - lastProtectedSessionOperationDate > protectedSessionTimeout * 1000) { + resetProtectedSession(); } }, 5000); -function setProtectedSessionTimeout(encSessTimeout) { - protectedSessionTimeout = encSessTimeout; -} - function setProtectedSessionId(id) { // using session cookie so that it disappears after browser/tab is closed utils.setSessionCookie(PROTECTED_SESSION_ID_KEY, id); @@ -37,7 +33,7 @@ function isProtectedSessionAvailable() { function touchProtectedSession() { if (isProtectedSessionAvailable()) { - lastProtectedSessionOperationDate = new Date(); + lastProtectedSessionOperationDate = Date.now(); setProtectedSessionId(utils.getCookie(PROTECTED_SESSION_ID_KEY)); } @@ -47,6 +43,5 @@ export default { setProtectedSessionId, resetProtectedSession, isProtectedSessionAvailable, - setProtectedSessionTimeout, touchProtectedSession }; \ No newline at end of file diff --git a/src/public/javascripts/services/search_notes.js b/src/public/javascripts/services/search_notes.js index e02a77a77..de67ececa 100644 --- a/src/public/javascripts/services/search_notes.js +++ b/src/public/javascripts/services/search_notes.js @@ -1,9 +1,21 @@ -import treeService from './tree.js'; -import treeCache from "./tree_cache.js"; -import server from './server.js'; import toastService from "./toast.js"; import appContext from "./app_context.js"; +const helpText = ` +Search tips - also see +

+

+

`; + async function refreshSearch() { const activeNode = appContext.getMainNoteTree().getActiveNode(); @@ -23,10 +35,6 @@ function init() { } export default { - // toggleSearch, - // resetSearch, - // showSearch, - // doSearch, refreshSearch, init, getHelpText: () => helpText diff --git a/src/public/javascripts/services/spell_check.js b/src/public/javascripts/services/spell_check.js index aee0f379c..8407d37ec 100644 --- a/src/public/javascripts/services/spell_check.js +++ b/src/public/javascripts/services/spell_check.js @@ -1,8 +1,6 @@ -import optionsService from "./options.js"; +import options from "./options.js"; export async function initSpellCheck() { - const options = await optionsService.waitForOptions(); - const {SpellCheckHandler, ContextMenuListener, ContextMenuBuilder} = require('electron-spellchecker'); const {remote, shell} = require('electron'); diff --git a/src/public/javascripts/services/split.js b/src/public/javascripts/services/split.js index 5e51809f1..29a5814f4 100644 --- a/src/public/javascripts/services/split.js +++ b/src/public/javascripts/services/split.js @@ -1,17 +1,7 @@ -import server from "./server.js"; -import optionService from "./options.js"; +import options from "./options.js"; let instance; -async function getPaneWidths() { - const options = await optionService.waitForOptions(); - - return { - leftPaneWidth: options.getInt('leftPaneWidth'), - rightPaneWidth: options.getInt('rightPaneWidth') - }; -} - async function setupSplit(left, right) { if (instance) { instance.destroy(); @@ -24,15 +14,16 @@ async function setupSplit(left, right) { return; } - const {leftPaneWidth, rightPaneWidth} = await getPaneWidths(); + const leftPaneWidth = options.getInt('leftPaneWidth'); + const rightPaneWidth = options.getInt('rightPaneWidth'); if (left && right) { instance = Split(['#left-pane', '#center-pane', '#right-pane'], { sizes: [leftPaneWidth, 100 - leftPaneWidth - rightPaneWidth, rightPaneWidth], gutterSize: 5, onDragEnd: sizes => { - server.put('options/leftPaneWidth/' + Math.round(sizes[0])); - server.put('options/rightPaneWidth/' + Math.round(sizes[2])); + options.save('leftPaneWidth', Math.round(sizes[0])); + options.save('rightPaneWidth', Math.round(sizes[2])); } }); } @@ -41,7 +32,7 @@ async function setupSplit(left, right) { sizes: [leftPaneWidth, 100 - leftPaneWidth], gutterSize: 5, onDragEnd: sizes => { - server.put('options/leftPaneWidth/' + Math.round(sizes[0])); + options.save('leftPaneWidth', Math.round(sizes[0])); } }); } @@ -50,7 +41,7 @@ async function setupSplit(left, right) { sizes: [100 - rightPaneWidth, rightPaneWidth], gutterSize: 5, onDragEnd: sizes => { - server.put('options/rightPaneWidth/' + Math.round(sizes[1])); + options.save('rightPaneWidth', Math.round(sizes[1])); } }); } diff --git a/src/public/javascripts/services/tab_context.js b/src/public/javascripts/services/tab_context.js index 0c691afe9..5a3709a69 100644 --- a/src/public/javascripts/services/tab_context.js +++ b/src/public/javascripts/services/tab_context.js @@ -9,12 +9,6 @@ import Component from "../widgets/component.js"; import treeCache from "./tree_cache.js"; import hoistedNoteService from "./hoisted_note.js"; -let showSidebarInNewTab = true; - -optionsService.addLoadListener(options => { - showSidebarInNewTab = options.is('showSidebarInNewTab'); -}); - class TabContext extends Component { /** * @param {AppContext} appContext diff --git a/src/public/javascripts/services/tree_cache.js b/src/public/javascripts/services/tree_cache.js index 7fe0a54ba..e29d1411a 100644 --- a/src/public/javascripts/services/tree_cache.js +++ b/src/public/javascripts/services/tree_cache.js @@ -5,6 +5,7 @@ import server from "./server.js"; import {LoadResults} from "./load_results.js"; import NoteComplement from "../entities/note_complement.js"; import appContext from "./app_context.js"; +import options from "./options.js"; /** * TreeCache keeps a read only cache of note tree structure in frontend's memory. @@ -351,6 +352,12 @@ class TreeCache { loadResults.addNoteRevision(sync.entityId, sync.noteId, sync.sourceId); }); + syncRows.filter(sync => sync.entityName === 'options').forEach(sync => { + options.set(sync.entity.name, sync.entity.value); + + loadResults.addOption(sync.entity.name); + }); + appContext.trigger('entitiesReloaded', {loadResults}); } } diff --git a/src/public/javascripts/services/ws.js b/src/public/javascripts/services/ws.js index d85ba5860..4c74daa55 100644 --- a/src/public/javascripts/services/ws.js +++ b/src/public/javascripts/services/ws.js @@ -130,7 +130,7 @@ async function consumeSyncData() { await treeCache.processSyncRows(allSyncData); } catch (e) { - logError(`Encountered error ${e.message}, reloading frontend.`); + logError(`Encountered error ${e.message}: ${e.stack}, reloading frontend.`); // if there's an error in updating the frontend then the easy option to recover is to reload the frontend completely utils.reloadApp(); diff --git a/src/public/javascripts/services/zoom.js b/src/public/javascripts/services/zoom.js index a47837295..781189f01 100644 --- a/src/public/javascripts/services/zoom.js +++ b/src/public/javascripts/services/zoom.js @@ -1,51 +1,47 @@ -import server from "./server.js"; -import utils from "./utils.js"; -import optionsService from "./options.js"; +import options from "./options.js"; +import Component from "../widgets/component.js"; const MIN_ZOOM = 0.5; const MAX_ZOOM = 2.0; -async function decreaseZoomFactor() { - await setZoomFactorAndSave(getCurrentZoom() - 0.1); -} +export default class ZoomService extends Component { + constructor(appContext) { + super(appContext); -async function increaseZoomFactor() { - await setZoomFactorAndSave(getCurrentZoom() + 0.1); -} - -function setZoomFactor(zoomFactor) { - zoomFactor = parseFloat(zoomFactor); - - const webFrame = require('electron').webFrame; - webFrame.setZoomFactor(zoomFactor); -} - -async function setZoomFactorAndSave(zoomFactor) { - if (!utils.isElectron()) { - return; + this.setZoomFactor(options.getFloat('zoomFactor')); } - if (zoomFactor >= MIN_ZOOM && zoomFactor <= MAX_ZOOM) { - setZoomFactor(zoomFactor); - - await server.put('options/zoomFactor/' + zoomFactor); + setZoomFactor(zoomFactor) { + zoomFactor = parseFloat(zoomFactor); + + const webFrame = require('electron').webFrame; + webFrame.setZoomFactor(zoomFactor); } - else { - console.log(`Zoom factor ${zoomFactor} outside of the range, ignored.`); + + async setZoomFactorAndSave(zoomFactor) { + if (zoomFactor >= MIN_ZOOM && zoomFactor <= MAX_ZOOM) { + this.setZoomFactor(zoomFactor); + + await options.save('zoomFactor', zoomFactor); + } + else { + console.log(`Zoom factor ${zoomFactor} outside of the range, ignored.`); + } + } + + getCurrentZoom() { + return require('electron').webFrame.getZoomFactor(); } -} -function getCurrentZoom() { - return require('electron').webFrame.getZoomFactor(); -} + zoomOutListener() { + this.setZoomFactorAndSave(this.getCurrentZoom() - 0.1); + } -if (utils.isElectron()) { - optionsService.addLoadListener(options => setZoomFactor(options.getFloat('zoomFactor'))) -} + zoomInListener() { + this.setZoomFactorAndSave(this.getCurrentZoom() + 0.1); + } -export default { - decreaseZoomFactor, - increaseZoomFactor, - setZoomFactor, - setZoomFactorAndSave + setZoomFactorAndSaveListener({zoomFactor}) { + this.setZoomFactorAndSave(zoomFactor); + } } \ No newline at end of file diff --git a/src/public/javascripts/widgets/note_tree.js b/src/public/javascripts/widgets/note_tree.js index 275b7952f..567eff5f6 100644 --- a/src/public/javascripts/widgets/note_tree.js +++ b/src/public/javascripts/widgets/note_tree.js @@ -118,7 +118,7 @@ export default class NoteTreeWidget extends TabAwareWidget { autoExpandMS: 600, dragStart: (node, data) => { // don't allow dragging root node - if (node.data.noteId === hoistedNoteService.getHoistedNoteNoPromise() + if (node.data.noteId === hoistedNoteService.getHoistedNoteId() || node.getParent().data.noteType === 'search') { return false; } @@ -141,7 +141,7 @@ export default class NoteTreeWidget extends TabAwareWidget { dragDrop: async (node, data) => { if ((data.hitMode === 'over' && node.data.noteType === 'search') || (['after', 'before'].includes(data.hitMode) - && (node.data.noteId === hoistedNoteService.getHoistedNoteNoPromise() || node.getParent().data.noteType === 'search'))) { + && (node.data.noteId === hoistedNoteService.getHoistedNoteId() || node.getParent().data.noteType === 'search'))) { const infoDialog = await import('../dialogs/info.js'); diff --git a/src/public/javascripts/widgets/search_box.js b/src/public/javascripts/widgets/search_box.js index 55c9ba8a7..432601411 100644 --- a/src/public/javascripts/widgets/search_box.js +++ b/src/public/javascripts/widgets/search_box.js @@ -1,25 +1,10 @@ import BasicWidget from "./basic_widget.js"; -import treeService from "../services/tree.js"; +import searchService from "../services/search_notes.js"; import treeCache from "../services/tree_cache.js"; import toastService from "../services/toast.js"; import appContext from "../services/app_context.js"; import noteCreateService from "../services/note_create.js"; -const helpText = ` -Search tips - also see -

-

-

`; - const TPL = ` +`; + +export default class SidebarToggle extends BasicWidget { + constructor(appContext) { + super(appContext); + + this.paneVisible = {}; + } + + doRender() { + this.$widget = $(TPL); + + this.toggleSidebar('left', options.is('leftPaneVisible')); + this.toggleSidebar('right', options.is('rightPaneVisible')); + + $("#show-right-pane-button").on('click', () => toggleAndSave('right', true)); + $("#hide-right-pane-button").on('click', () => toggleAndSave('right', false)); + + $("#show-left-pane-button").on('click', () => toggleAndSave('left', true)); + $("#hide-left-pane-button").on('click', () => toggleAndSave('left', false)); + + splitService.setupSplit(this.paneVisible.left, this.paneVisible.right); + + return this.$widget; + } + + toggleSidebar(side, show) { + $(`#${side}-pane`).toggle(show); + $(`#show-${side}-pane-button`).toggle(!show); + $(`#hide-${side}-pane-button`).toggle(show); + + this.paneVisible[side] = show; + } + + async toggleAndSave(side, show) { + this.toggleSidebar(side, show); + + await options.save(`${side}PaneVisible`, show.toString()); + + splitService.setupSplit(this.paneVisible.left, this.paneVisible.right); + + this.trigger('sidebarVisibilityChanged', {side, show}); + } +} diff --git a/src/public/javascripts/widgets/title_bar_buttons.js b/src/public/javascripts/widgets/title_bar_buttons.js index a958a2002..2addedab0 100644 --- a/src/public/javascripts/widgets/title_bar_buttons.js +++ b/src/public/javascripts/widgets/title_bar_buttons.js @@ -1,5 +1,5 @@ import BasicWidget from "./basic_widget.js"; -import optionService from "../services/options.js"; +import options from "../services/options.js"; import utils from "../services/utils.js"; const TPL = ` @@ -23,41 +23,41 @@ export default class TitleBarButtonsWidget extends BasicWidget { return; } - this.$widget = $(TPL); + if (!options.is('nativeTitleBarVisible')) { + this.$widget = $(TPL); + this.$widget.show(); - optionService.waitForOptions().then(options => { - if (!options.is('nativeTitleBarVisible')) { - this.$widget.show(); + const $minimizeBtn = this.$widget.find(".minimize-btn"); + const $maximizeBtn = this.$widget.find(".maximize-btn"); + const $closeBtn = this.$widget.find(".close-btn"); - const $minimizeBtn = this.$widget.find(".minimize-btn"); - const $maximizeBtn = this.$widget.find(".maximize-btn"); - const $closeBtn = this.$widget.find(".close-btn"); + $minimizeBtn.on('click', () => { + $minimizeBtn.trigger('blur'); + const {remote} = require('electron'); + remote.BrowserWindow.getFocusedWindow().minimize(); + }); - $minimizeBtn.on('click', () => { - $minimizeBtn.trigger('blur'); - const {remote} = require('electron'); - remote.BrowserWindow.getFocusedWindow().minimize(); - }); + $maximizeBtn.on('click', () => { + $maximizeBtn.trigger('blur'); + const {remote} = require('electron'); + const focusedWindow = remote.BrowserWindow.getFocusedWindow(); - $maximizeBtn.on('click', () => { - $maximizeBtn.trigger('blur'); - const {remote} = require('electron'); - const focusedWindow = remote.BrowserWindow.getFocusedWindow(); + if (focusedWindow.isMaximized()) { + focusedWindow.unmaximize(); + } else { + focusedWindow.maximize(); + } + }); - if (focusedWindow.isMaximized()) { - focusedWindow.unmaximize(); - } else { - focusedWindow.maximize(); - } - }); - - $closeBtn.on('click', () => { - $closeBtn.trigger('blur'); - const {remote} = require('electron'); - remote.BrowserWindow.getFocusedWindow().close(); - }); - } - }); + $closeBtn.on('click', () => { + $closeBtn.trigger('blur'); + const {remote} = require('electron'); + remote.BrowserWindow.getFocusedWindow().close(); + }); + } + else { + this.$widget = $('
'); + } return this.$widget; } diff --git a/src/public/stylesheets/desktop.css b/src/public/stylesheets/desktop.css index b312cd648..8fc5eede3 100644 --- a/src/public/stylesheets/desktop.css +++ b/src/public/stylesheets/desktop.css @@ -146,20 +146,6 @@ body { border-color: var(--button-border-color); } -#hide-right-pane-button, #show-right-pane-button { - position: fixed; - bottom: 10px; - right: 10px; - z-index: 1000; -} - -#hide-left-pane-button, #show-left-pane-button { - position: fixed; - bottom: 10px; - left: 10px; - z-index: 1000; -} - #right-pane { overflow: auto; padding-top: 4px; diff --git a/src/services/repository.js b/src/services/repository.js index 38d835f35..c36cb7782 100644 --- a/src/services/repository.js +++ b/src/services/repository.js @@ -110,9 +110,7 @@ async function updateEntity(entity) { const primaryKey = entity[primaryKeyName]; if (entity.isChanged) { - if (entityName !== 'options' || entity.isSynced) { - await syncTableService.addEntitySync(entityName, primaryKey); - } + await syncTableService.addEntitySync(entityName, primaryKey); if (!cls.isEntityEventsDisabled()) { const eventPayload = { diff --git a/src/services/sync.js b/src/services/sync.js index 8cb12339c..bc6b259fa 100644 --- a/src/services/sync.js +++ b/src/services/sync.js @@ -315,10 +315,13 @@ async function getSyncRecords(syncs) { let length = 0; for (const sync of syncs) { - const record = { - sync: sync, - entity: await getEntityRow(sync.entityName, sync.entityId) - }; + const entity = await getEntityRow(sync.entityName, sync.entityId); + + if (sync.entityName === 'options' && !entity.isSynced) { + continue; + } + + const record = { sync, entity }; records.push(record); diff --git a/src/services/ws.js b/src/services/ws.js index 58bf3ffa0..c0b838ac4 100644 --- a/src/services/ws.js +++ b/src/services/ws.js @@ -92,6 +92,9 @@ async function fillInAdditionalProperties(sync) { } else if (sync.entityName === 'note_reordering') { sync.positions = await sql.getMap(`SELECT branchId, notePosition FROM branches WHERE isDeleted = 0 AND parentNoteId = ?`, [sync.entityId]); } + else if (sync.entityName === 'options') { + sync.entity = await sql.getRow(`SELECT * FROM options WHERE name = ?`, [sync.entityId]); + } } async function sendPing(client) { diff --git a/src/views/desktop.ejs b/src/views/desktop.ejs index a8af7c11a..b9f415ecd 100644 --- a/src/views/desktop.ejs +++ b/src/views/desktop.ejs @@ -14,13 +14,7 @@
- - -
- - -