mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-11-04 15:11:31 +08:00 
			
		
		
		
	refactored access to options on frontend
This commit is contained in:
		
							parent
							
								
									786bbbc160
								
							
						
					
					
						commit
						42017fde5f
					
				
							
								
								
									
										18
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										18
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -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"
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
@ -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",
 | 
			
		||||
 | 
			
		||||
@ -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";
 | 
			
		||||
 | 
			
		||||
@ -140,42 +140,3 @@ 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));
 | 
			
		||||
 | 
			
		||||
@ -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 = `
 | 
			
		||||
<p><strong>Settings on this options tab are saved automatically after each change.</strong></p>
 | 
			
		||||
@ -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';
 | 
			
		||||
 | 
			
		||||
@ -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 = `
 | 
			
		||||
<h4>Available MIME types in the dropdown</h4>
 | 
			
		||||
@ -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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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.");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -104,8 +104,6 @@ export default class SidebarOptions {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await server.put('options', opts);
 | 
			
		||||
 | 
			
		||||
        optionsService.reloadOptions();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    parseJsonSafely(str) {
 | 
			
		||||
 | 
			
		||||
@ -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) {
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -57,13 +57,7 @@ export default class Entrypoints extends Component {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    zoomOutListener() {
 | 
			
		||||
        zoomService.decreaseZoomFactor();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    zoomInListener() {
 | 
			
		||||
        zoomService.increaseZoomFactor();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async createNoteIntoDayNoteListener() {
 | 
			
		||||
        const todayNote = await dateNoteService.getTodayNote();
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
function reloadOptions() {
 | 
			
		||||
    optionsReady = new Promise((resolve, reject) => {
 | 
			
		||||
        server.get('options').then(optionArr => {
 | 
			
		||||
            const options = new Options(optionArr);
 | 
			
		||||
    async save(key, value) {
 | 
			
		||||
        this.set(key, value);
 | 
			
		||||
 | 
			
		||||
            resolve(options);
 | 
			
		||||
        const payload = {};
 | 
			
		||||
        payload[key] = value;
 | 
			
		||||
 | 
			
		||||
            for (const listener of loadListeners) {
 | 
			
		||||
                listener(options);
 | 
			
		||||
        await server.put(`options`, payload);
 | 
			
		||||
    }
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return optionsReady;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * just waits for some options without triggering reload
 | 
			
		||||
 *
 | 
			
		||||
 * @return {Options}
 | 
			
		||||
 */
 | 
			
		||||
async function waitForOptions() {
 | 
			
		||||
    return await optionsReady;
 | 
			
		||||
}
 | 
			
		||||
const options = new Options();
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
}
 | 
			
		||||
export default options;
 | 
			
		||||
@ -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
 | 
			
		||||
};
 | 
			
		||||
@ -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 = `
 | 
			
		||||
<strong>Search tips</strong> - also see <button class="btn btn-sm" type="button" data-help-page="Search">complete help on search</button>
 | 
			
		||||
<p>
 | 
			
		||||
<ul>
 | 
			
		||||
    <li>Just enter any text for full text search</li>
 | 
			
		||||
    <li><code>@abc</code> - returns notes with label abc</li>
 | 
			
		||||
    <li><code>@year=2019</code> - matches notes with label <code>year</code> having value <code>2019</code></li>
 | 
			
		||||
    <li><code>@rock @pop</code> - matches notes which have both <code>rock</code> and <code>pop</code> labels</li>
 | 
			
		||||
    <li><code>@rock or @pop</code> - only one of the labels must be present</li>
 | 
			
		||||
    <li><code>@year<=2000</code> - numerical comparison (also >, >=, <).</li>
 | 
			
		||||
    <li><code>@dateCreated>=MONTH-1</code> - notes created in the last month</li>
 | 
			
		||||
    <li><code>=handler</code> - will execute script defined in <code>handler</code> relation to get results</li>
 | 
			
		||||
</ul>
 | 
			
		||||
</p>`;
 | 
			
		||||
 | 
			
		||||
async function refreshSearch() {
 | 
			
		||||
    const activeNode = appContext.getMainNoteTree().getActiveNode();
 | 
			
		||||
 | 
			
		||||
@ -23,10 +35,6 @@ function init() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    // toggleSearch,
 | 
			
		||||
    // resetSearch,
 | 
			
		||||
    // showSearch,
 | 
			
		||||
    // doSearch,
 | 
			
		||||
    refreshSearch,
 | 
			
		||||
    init,
 | 
			
		||||
    getHelpText: () => helpText
 | 
			
		||||
 | 
			
		||||
@ -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');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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]));
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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});
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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();
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
        this.setZoomFactor(options.getFloat('zoomFactor'));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
async function increaseZoomFactor() {
 | 
			
		||||
    await setZoomFactorAndSave(getCurrentZoom() + 0.1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function setZoomFactor(zoomFactor) {
 | 
			
		||||
    setZoomFactor(zoomFactor) {
 | 
			
		||||
        zoomFactor = parseFloat(zoomFactor);
 | 
			
		||||
    
 | 
			
		||||
        const webFrame = require('electron').webFrame;
 | 
			
		||||
        webFrame.setZoomFactor(zoomFactor);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
async function setZoomFactorAndSave(zoomFactor) {
 | 
			
		||||
    if (!utils.isElectron()) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async setZoomFactorAndSave(zoomFactor) {
 | 
			
		||||
        if (zoomFactor >= MIN_ZOOM && zoomFactor <= MAX_ZOOM) {
 | 
			
		||||
        setZoomFactor(zoomFactor);
 | 
			
		||||
            this.setZoomFactor(zoomFactor);
 | 
			
		||||
    
 | 
			
		||||
        await server.put('options/zoomFactor/' + zoomFactor);
 | 
			
		||||
            await options.save('zoomFactor', zoomFactor);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            console.log(`Zoom factor ${zoomFactor} outside of the range, ignored.`);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
function getCurrentZoom() {
 | 
			
		||||
    getCurrentZoom() {
 | 
			
		||||
        return require('electron').webFrame.getZoomFactor();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
if (utils.isElectron()) {
 | 
			
		||||
    optionsService.addLoadListener(options => setZoomFactor(options.getFloat('zoomFactor')))
 | 
			
		||||
    zoomOutListener() {
 | 
			
		||||
        this.setZoomFactorAndSave(this.getCurrentZoom() - 0.1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    decreaseZoomFactor,
 | 
			
		||||
    increaseZoomFactor,
 | 
			
		||||
    setZoomFactor,
 | 
			
		||||
    setZoomFactorAndSave
 | 
			
		||||
    zoomInListener() {
 | 
			
		||||
        this.setZoomFactorAndSave(this.getCurrentZoom() + 0.1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setZoomFactorAndSaveListener({zoomFactor}) {
 | 
			
		||||
        this.setZoomFactorAndSave(zoomFactor);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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 = `
 | 
			
		||||
<strong>Search tips</strong> - also see <button class="btn btn-sm" type="button" data-help-page="Search">complete help on search</button>
 | 
			
		||||
<p>
 | 
			
		||||
<ul>
 | 
			
		||||
    <li>Just enter any text for full text search</li>
 | 
			
		||||
    <li><code>@abc</code> - returns notes with label abc</li>
 | 
			
		||||
    <li><code>@year=2019</code> - matches notes with label <code>year</code> having value <code>2019</code></li>
 | 
			
		||||
    <li><code>@rock @pop</code> - matches notes which have both <code>rock</code> and <code>pop</code> labels</li>
 | 
			
		||||
    <li><code>@rock or @pop</code> - only one of the labels must be present</li>
 | 
			
		||||
    <li><code>@year<=2000</code> - numerical comparison (also >, >=, <).</li>
 | 
			
		||||
    <li><code>@dateCreated>=MONTH-1</code> - notes created in the last month</li>
 | 
			
		||||
    <li><code>=handler</code> - will execute script defined in <code>handler</code> relation to get results</li>
 | 
			
		||||
</ul>
 | 
			
		||||
</p>`;
 | 
			
		||||
 | 
			
		||||
const TPL = `
 | 
			
		||||
<div class="search-box">
 | 
			
		||||
    <style>
 | 
			
		||||
@ -145,7 +130,7 @@ export default class SearchBoxWidget extends BasicWidget {
 | 
			
		||||
        this.$searchBox.tooltip({
 | 
			
		||||
            trigger: 'focus',
 | 
			
		||||
            html: true,
 | 
			
		||||
            title: helpText,
 | 
			
		||||
            title: searchService.getHelpText(),
 | 
			
		||||
            placement: 'right',
 | 
			
		||||
            delay: {
 | 
			
		||||
                show: 500, // necessary because sliding out may cause wrong position
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import BasicWidget from "./basic_widget.js";
 | 
			
		||||
import optionService from "../services/options.js";
 | 
			
		||||
import options from "../services/options.js";
 | 
			
		||||
 | 
			
		||||
export default class SidePaneContainer extends BasicWidget {
 | 
			
		||||
    constructor(appContext, side, widgets) {
 | 
			
		||||
@ -19,9 +19,7 @@ export default class SidePaneContainer extends BasicWidget {
 | 
			
		||||
        return this.$widget;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async eventReceived(name, data, sync = false) {
 | 
			
		||||
        const options = await optionService.waitForOptions();
 | 
			
		||||
 | 
			
		||||
    eventReceived(name, data, sync = false) {
 | 
			
		||||
        if (options.is(this.side + 'PaneVisible')) {
 | 
			
		||||
            super.eventReceived(name, data, sync);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										72
									
								
								src/public/javascripts/widgets/sidebar_toggle.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/public/javascripts/widgets/sidebar_toggle.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,72 @@
 | 
			
		||||
import options from "../services/options.js";
 | 
			
		||||
import splitService from "../services/split.js";
 | 
			
		||||
import BasicWidget from "./basic_widget.js";
 | 
			
		||||
 | 
			
		||||
const TPL = `
 | 
			
		||||
<div>
 | 
			
		||||
    <style>
 | 
			
		||||
    #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;
 | 
			
		||||
    }
 | 
			
		||||
    </style>
 | 
			
		||||
    
 | 
			
		||||
    <button id="hide-left-pane-button" class="btn btn-sm icon-button bx bx-chevrons-left hide-in-zen-mode" title="Show sidebar"></button>
 | 
			
		||||
    <button id="show-left-pane-button" class="btn btn-sm icon-button bx bx-chevrons-right hide-in-zen-mode" title="Hide sidebar"></button>
 | 
			
		||||
            
 | 
			
		||||
    <button id="hide-right-pane-button" class="btn btn-sm icon-button bx bx-chevrons-right hide-in-zen-mode" title="Hide sidebar"></button>
 | 
			
		||||
    <button id="show-right-pane-button" class="btn btn-sm icon-button bx bx-chevrons-left hide-in-zen-mode" title="Show sidebar"></button>
 | 
			
		||||
</div>
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
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});
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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,10 +23,8 @@ export default class TitleBarButtonsWidget extends BasicWidget {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.$widget = $(TPL);
 | 
			
		||||
 | 
			
		||||
        optionService.waitForOptions().then(options => {
 | 
			
		||||
        if (!options.is('nativeTitleBarVisible')) {
 | 
			
		||||
            this.$widget = $(TPL);
 | 
			
		||||
            this.$widget.show();
 | 
			
		||||
 | 
			
		||||
            const $minimizeBtn = this.$widget.find(".minimize-btn");
 | 
			
		||||
@ -57,7 +55,9 @@ export default class TitleBarButtonsWidget extends BasicWidget {
 | 
			
		||||
                remote.BrowserWindow.getFocusedWindow().close();
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        });
 | 
			
		||||
        else {
 | 
			
		||||
            this.$widget = $('<div>');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.$widget;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!cls.isEntityEventsDisabled()) {
 | 
			
		||||
                const eventPayload = {
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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) {
 | 
			
		||||
 | 
			
		||||
@ -14,13 +14,7 @@
 | 
			
		||||
    <div id="top-pane"></div>
 | 
			
		||||
 | 
			
		||||
    <div id="main-pane" style="display: flex; flex-grow: 1; flex-shrink: 1; min-height: 0;">
 | 
			
		||||
        <button id="hide-left-pane-button" class="btn btn-sm icon-button bx bx-chevrons-left hide-in-zen-mode" title="Show sidebar"></button>
 | 
			
		||||
        <button id="show-left-pane-button" class="btn btn-sm icon-button bx bx-chevrons-right hide-in-zen-mode" title="Hide sidebar"></button>
 | 
			
		||||
 | 
			
		||||
        <div id="center-pane"></div>
 | 
			
		||||
 | 
			
		||||
        <button id="hide-right-pane-button" class="btn btn-sm icon-button bx bx-chevrons-right hide-in-zen-mode" title="Hide sidebar"></button>
 | 
			
		||||
        <button id="show-right-pane-button" class="btn btn-sm icon-button bx bx-chevrons-left hide-in-zen-mode" title="Show sidebar"></button>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="dropdown-menu dropdown-menu-sm" id="context-menu-container"></div>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user