mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	refactored access to options on frontend
This commit is contained in:
		
							
								
								
									
										18
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										18
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -2157,9 +2157,9 @@ | |||||||
|       "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=" |       "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=" | ||||||
|     }, |     }, | ||||||
|     "dayjs": { |     "dayjs": { | ||||||
|       "version": "1.8.19", |       "version": "1.8.20", | ||||||
|       "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.19.tgz", |       "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.20.tgz", | ||||||
|       "integrity": "sha512-7kqOoj3oQSmqbvtvGFLU5iYqies+SqUiEGNT0UtUPPxcPYgY1BrkXR0Cq2R9HYSimBXN+xHkEN4Hi399W+Ovlg==" |       "integrity": "sha512-mH0MCDxw6UCGJYxVN78h8ugWycZAO8thkj3bW6vApL5tS0hQplIDdAQcmbvl7n35H0AKdCJQaArTrIQw2xt4Qg==" | ||||||
|     }, |     }, | ||||||
|     "debug": { |     "debug": { | ||||||
|       "version": "4.1.1", |       "version": "4.1.1", | ||||||
| @@ -3782,9 +3782,9 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "file-type": { |     "file-type": { | ||||||
|       "version": "14.0.0", |       "version": "14.1.0", | ||||||
|       "resolved": "https://registry.npmjs.org/file-type/-/file-type-14.0.0.tgz", |       "resolved": "https://registry.npmjs.org/file-type/-/file-type-14.1.0.tgz", | ||||||
|       "integrity": "sha512-+gxNvurlwHfTohZC6gqf0ybMl+cXYB9f1x++kw9AgKItdFx20J0fV9wCVR38a5/jphL5EUcusJ9tLYkPRtGHaw==", |       "integrity": "sha512-HfxnzrPH+LLClSAsno88/0frRtamu1XfqEP4IP/8RqBmqQnBQkemv3Udde0t53wZmrdOtc70aaR9WUHyQhjCUQ==", | ||||||
|       "requires": { |       "requires": { | ||||||
|         "readable-web-to-node-stream": "^2.0.0", |         "readable-web-to-node-stream": "^2.0.0", | ||||||
|         "strtok3": "^6.0.0", |         "strtok3": "^6.0.0", | ||||||
| @@ -6859,9 +6859,9 @@ | |||||||
|       "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==" |       "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==" | ||||||
|     }, |     }, | ||||||
|     "node-abi": { |     "node-abi": { | ||||||
|       "version": "2.13.0", |       "version": "2.14.0", | ||||||
|       "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.13.0.tgz", |       "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.14.0.tgz", | ||||||
|       "integrity": "sha512-9HrZGFVTR5SOu3PZAnAY2hLO36aW1wmA+FDsVkr85BTST32TLCA1H/AEcatVRAsWLyXS3bqUDYCAjq5/QGuSTA==", |       "integrity": "sha512-y54KGgEOHnRHlGQi7E5UiryRkH8bmksmQLj/9iLAjoje743YS+KaKB/sDYXgqtT0J16JT3c3AYJZNI98aU/kYg==", | ||||||
|       "requires": { |       "requires": { | ||||||
|         "semver": "^5.4.1" |         "semver": "^5.4.1" | ||||||
|       }, |       }, | ||||||
|   | |||||||
| @@ -37,7 +37,7 @@ | |||||||
|     "electron-window-state": "5.0.3", |     "electron-window-state": "5.0.3", | ||||||
|     "express": "4.17.1", |     "express": "4.17.1", | ||||||
|     "express-session": "1.17.0", |     "express-session": "1.17.0", | ||||||
|     "file-type": "14.0.0", |     "file-type": "14.1.0", | ||||||
|     "fs-extra": "8.1.0", |     "fs-extra": "8.1.0", | ||||||
|     "helmet": "3.21.2", |     "helmet": "3.21.2", | ||||||
|     "html": "1.0.0", |     "html": "1.0.0", | ||||||
| @@ -53,7 +53,7 @@ | |||||||
|     "jimp": "0.9.3", |     "jimp": "0.9.3", | ||||||
|     "mime-types": "2.1.26", |     "mime-types": "2.1.26", | ||||||
|     "multer": "1.4.2", |     "multer": "1.4.2", | ||||||
|     "node-abi": "2.13.0", |     "node-abi": "2.14.0", | ||||||
|     "open": "7.0.2", |     "open": "7.0.2", | ||||||
|     "pngjs": "3.4.0", |     "pngjs": "3.4.0", | ||||||
|     "portscanner": "2.2.0", |     "portscanner": "2.2.0", | ||||||
|   | |||||||
| @@ -28,7 +28,7 @@ import dateNoteService from './services/date_notes.js'; | |||||||
| import importService from './services/import.js'; | import importService from './services/import.js'; | ||||||
| import keyboardActionService from "./services/keyboard_actions.js"; | import keyboardActionService from "./services/keyboard_actions.js"; | ||||||
| import splitService from "./services/split.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 noteContentRenderer from "./services/note_content_renderer.js"; | ||||||
| import appContext from "./services/app_context.js"; | import appContext from "./services/app_context.js"; | ||||||
|  |  | ||||||
| @@ -140,42 +140,3 @@ appContext.start(); | |||||||
| noteTooltipService.setupGlobalTooltip(); | noteTooltipService.setupGlobalTooltip(); | ||||||
|  |  | ||||||
| noteAutocompleteService.init(); | 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 cssLoader from "../../services/css_loader.js"; | ||||||
| import zoomService from "../../services/zoom.js"; | import zoomService from "../../services/zoom.js"; | ||||||
| import optionsService from "../../services/options.js"; | import optionsService from "../../services/options.js"; | ||||||
|  | import appContext from "../../services/app_context.js"; | ||||||
|  |  | ||||||
| const TPL = ` | const TPL = ` | ||||||
| <p><strong>Settings on this options tab are saved automatically after each change.</strong></p> | <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); |             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', () => { |         this.$nativeTitleBarSelect.on('change', () => { | ||||||
|             const nativeTitleBarVisible = this.$nativeTitleBarSelect.val() === 'show' ? 'true' : 'false'; |             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 mimeTypesService from "../../services/mime_types.js"; | ||||||
| import optionsService from "../../services/options.js"; | import options from "../../services/options.js"; | ||||||
|  |  | ||||||
| const TPL = ` | const TPL = ` | ||||||
| <h4>Available MIME types in the dropdown</h4> | <h4>Available MIME types in the dropdown</h4> | ||||||
| @@ -14,7 +13,7 @@ export default class CodeNotesOptions { | |||||||
|         this.$mimeTypes = $("#options-mime-types"); |         this.$mimeTypes = $("#options-mime-types"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async optionsLoaded(options) { |     async optionsLoaded() { | ||||||
|         this.$mimeTypes.empty(); |         this.$mimeTypes.empty(); | ||||||
|  |  | ||||||
|         let idCtr = 1; |         let idCtr = 1; | ||||||
| @@ -42,10 +41,8 @@ export default class CodeNotesOptions { | |||||||
|         this.$mimeTypes.find("input:checked").each( |         this.$mimeTypes.find("input:checked").each( | ||||||
|             (i, el) => enabledMimeTypes.push($(el).attr("data-mime-type"))); |             (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); |         mimeTypesService.loadMimeTypes(); | ||||||
|  |  | ||||||
|         await optionsService.reloadOptions(); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -101,8 +101,6 @@ export default class ProtectedSessionOptions { | |||||||
|             const eraseNotesAfterTimeInSeconds = this.$eraseNotesAfterTimeInSeconds.val(); |             const eraseNotesAfterTimeInSeconds = this.$eraseNotesAfterTimeInSeconds.val(); | ||||||
|  |  | ||||||
|             server.put('options', { 'eraseNotesAfterTimeInSeconds': eraseNotesAfterTimeInSeconds }).then(() => { |             server.put('options', { 'eraseNotesAfterTimeInSeconds': eraseNotesAfterTimeInSeconds }).then(() => { | ||||||
|                 optionsService.reloadOptions(); |  | ||||||
|  |  | ||||||
|                 toastService.showMessage("Options change have been saved."); |                 toastService.showMessage("Options change have been saved."); | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
| @@ -115,8 +113,6 @@ export default class ProtectedSessionOptions { | |||||||
|             const protectedSessionTimeout = this.$protectedSessionTimeout.val(); |             const protectedSessionTimeout = this.$protectedSessionTimeout.val(); | ||||||
|  |  | ||||||
|             server.put('options', { 'protectedSessionTimeout': protectedSessionTimeout }).then(() => { |             server.put('options', { 'protectedSessionTimeout': protectedSessionTimeout }).then(() => { | ||||||
|                 optionsService.reloadOptions(); |  | ||||||
|  |  | ||||||
|                 toastService.showMessage("Options change have been saved."); |                 toastService.showMessage("Options change have been saved."); | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -104,8 +104,6 @@ export default class SidebarOptions { | |||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         await server.put('options', opts); |         await server.put('options', opts); | ||||||
|  |  | ||||||
|         optionsService.reloadOptions(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     parseJsonSafely(str) { |     parseJsonSafely(str) { | ||||||
|   | |||||||
| @@ -29,10 +29,12 @@ import bundleService from "./bundle.js"; | |||||||
| import DialogEventComponent from "./dialog_events.js"; | import DialogEventComponent from "./dialog_events.js"; | ||||||
| import Entrypoints from "./entrypoints.js"; | import Entrypoints from "./entrypoints.js"; | ||||||
| import CalendarWidget from "../widgets/calendar.js"; | import CalendarWidget from "../widgets/calendar.js"; | ||||||
| import optionsService from "./options.js"; | import options from "./options.js"; | ||||||
| import utils from "./utils.js"; | import utils from "./utils.js"; | ||||||
| import treeService from "./tree.js"; | import treeService from "./tree.js"; | ||||||
| import SidePaneContainer from "../widgets/side_pane_container.js"; | import SidePaneContainer from "../widgets/side_pane_container.js"; | ||||||
|  | import ZoomService from "./zoom.js"; | ||||||
|  | import SidebarToggle from "../widgets/sidebar_toggle.js"; | ||||||
|  |  | ||||||
| class AppContext { | class AppContext { | ||||||
|     constructor() { |     constructor() { | ||||||
| @@ -45,7 +47,9 @@ class AppContext { | |||||||
|         this.activeTabId = null; |         this.activeTabId = null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     start() { |     async start() { | ||||||
|  |         options.load(await server.get('options')); | ||||||
|  |  | ||||||
|         this.showWidgets(); |         this.showWidgets(); | ||||||
|  |  | ||||||
|         this.loadTabs(); |         this.loadTabs(); | ||||||
| @@ -54,8 +58,6 @@ class AppContext { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     async loadTabs() { |     async loadTabs() { | ||||||
|         const options = await optionsService.waitForOptions(); |  | ||||||
|  |  | ||||||
|         const openTabs = options.getJson('openTabs') || []; |         const openTabs = options.getJson('openTabs') || []; | ||||||
|  |  | ||||||
|         await treeCache.initializedPromise; |         await treeCache.initializedPromise; | ||||||
| @@ -186,14 +188,25 @@ class AppContext { | |||||||
|  |  | ||||||
|         $centerPane.after(rightPaneContainer.render()); |         $centerPane.after(rightPaneContainer.render()); | ||||||
|  |  | ||||||
|  |         const sidebarToggleWidget = new SidebarToggle(this); | ||||||
|  |  | ||||||
|  |         $centerPane.after(sidebarToggleWidget.render()); | ||||||
|  |  | ||||||
|         this.components = [ |         this.components = [ | ||||||
|             new Entrypoints(), |             new Entrypoints(), | ||||||
|             new DialogEventComponent(this), |             new DialogEventComponent(this), | ||||||
|             ...topPaneWidgets, |             ...topPaneWidgets, | ||||||
|             leftPaneContainer, |             leftPaneContainer, | ||||||
|             ...centerPaneWidgets, |             ...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) { |     trigger(name, data, sync = false) { | ||||||
|   | |||||||
| @@ -70,7 +70,7 @@ function copy(nodes) { | |||||||
|  |  | ||||||
| function cut(nodes) { | function cut(nodes) { | ||||||
|     clipboardBranchIds = nodes |     clipboardBranchIds = nodes | ||||||
|         .filter(node => node.data.noteId !== hoistedNoteService.getHoistedNoteNoPromise()) |         .filter(node => node.data.noteId !== hoistedNoteService.getHoistedNoteId()) | ||||||
|         .filter(node => node.getParent().data.noteType !== 'search') |         .filter(node => node.getParent().data.noteType !== 'search') | ||||||
|         .map(node => node.key); |         .map(node => node.key); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -57,13 +57,7 @@ export default class Entrypoints extends Component { | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     zoomOutListener() { |  | ||||||
|         zoomService.decreaseZoomFactor(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     zoomInListener() { |  | ||||||
|         zoomService.increaseZoomFactor(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async createNoteIntoDayNoteListener() { |     async createNoteIntoDayNoteListener() { | ||||||
|         const todayNote = await dateNoteService.getTodayNote(); |         const todayNote = await dateNoteService.getTodayNote(); | ||||||
|   | |||||||
| @@ -1,30 +1,16 @@ | |||||||
| import optionsService from './options.js'; | import options from './options.js'; | ||||||
| import server from "./server.js"; |  | ||||||
| import appContext from "./app_context.js"; | import appContext from "./app_context.js"; | ||||||
| import treeService from "./tree.js"; | import treeService from "./tree.js"; | ||||||
|  |  | ||||||
| let hoistedNoteId = 'root'; | function getHoistedNoteId() { | ||||||
|  |     return options.get('hoistedNoteId'); | ||||||
| optionsService.waitForOptions().then(options => { |  | ||||||
|     hoistedNoteId = options.get('hoistedNoteId'); |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| function getHoistedNoteNoPromise() { |  | ||||||
|     return hoistedNoteId; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function getHoistedNoteId() { |  | ||||||
|     await optionsService.waitForOptions(); |  | ||||||
|  |  | ||||||
|     return hoistedNoteId; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| async function setHoistedNoteId(noteId) { | async function setHoistedNoteId(noteId) { | ||||||
|     hoistedNoteId = noteId; |     await options.save('hoistedNoteId', noteId); | ||||||
|  |  | ||||||
|     await server.put('options/hoistedNoteId/' + noteId); |     // FIXME - just use option load event | ||||||
|  |     appContext.trigger('hoistedNoteChanged', {noteId}); | ||||||
|     appContext.trigger('hoistedNoteChanged', {hoistedNoteId}); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| async function unhoist() { | async function unhoist() { | ||||||
| @@ -69,7 +55,6 @@ async function checkNoteAccess(notePath) { | |||||||
|  |  | ||||||
| export default { | export default { | ||||||
|     getHoistedNoteId, |     getHoistedNoteId, | ||||||
|     getHoistedNoteNoPromise, |  | ||||||
|     setHoistedNoteId, |     setHoistedNoteId, | ||||||
|     unhoist, |     unhoist, | ||||||
|     isTopLevelNode, |     isTopLevelNode, | ||||||
|   | |||||||
| @@ -14,6 +14,8 @@ export class LoadResults { | |||||||
|         this.noteRevisions = []; |         this.noteRevisions = []; | ||||||
|  |  | ||||||
|         this.contentNoteIdToSourceId = []; |         this.contentNoteIdToSourceId = []; | ||||||
|  |  | ||||||
|  |         this.options = []; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     addNote(noteId, sourceId) { |     addNote(noteId, sourceId) { | ||||||
| @@ -90,4 +92,12 @@ export class LoadResults { | |||||||
|  |  | ||||||
|         return this.contentNoteIdToSourceId.find(l => l.noteId === noteId && l.sourceId !== sourceId); |         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 = [ | const MIME_TYPES_DICT = [ | ||||||
|     { default: true, title: "Plain text", mime: "text/plain" }, |     { default: true, title: "Plain text", mime: "text/plain" }, | ||||||
| @@ -161,7 +161,7 @@ const MIME_TYPES_DICT = [ | |||||||
|  |  | ||||||
| let mimeTypes = null; | let mimeTypes = null; | ||||||
|  |  | ||||||
| function loadMimeTypes(options) { | function loadMimeTypes() { | ||||||
|     mimeTypes = JSON.parse(JSON.stringify(MIME_TYPES_DICT)); // clone |     mimeTypes = JSON.parse(JSON.stringify(MIME_TYPES_DICT)); // clone | ||||||
|  |  | ||||||
|     const enabledMimeTypes = options.getJson('codeNotesMimeTypes') |     const enabledMimeTypes = options.getJson('codeNotesMimeTypes') | ||||||
| @@ -172,16 +172,15 @@ function loadMimeTypes(options) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| optionsService.addLoadListener(loadMimeTypes); |  | ||||||
|  |  | ||||||
| async function getMimeTypes() { | async function getMimeTypes() { | ||||||
|     if (mimeTypes === null) { |     if (mimeTypes === null) { | ||||||
|         loadMimeTypes(await options.waitForOptions()); |         loadMimeTypes(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return mimeTypes; |     return mimeTypes; | ||||||
| } | } | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|     getMimeTypes |     getMimeTypes, | ||||||
|  |     loadMimeTypes | ||||||
| } | } | ||||||
| @@ -1,11 +1,9 @@ | |||||||
| import server from "./server.js"; | import server from "./server.js"; | ||||||
|  |  | ||||||
| let optionsReady; |  | ||||||
|  |  | ||||||
| const loadListeners = []; | const loadListeners = []; | ||||||
|  |  | ||||||
| class Options { | class Options { | ||||||
|     constructor(arr) { |     load(arr) { | ||||||
|         this.arr = arr; |         this.arr = arr; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -37,45 +35,21 @@ class Options { | |||||||
|     is(key) { |     is(key) { | ||||||
|         return this.arr[key] === 'true'; |         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() { | const options = new Options(); | ||||||
|     optionsReady = new Promise((resolve, reject) => { |  | ||||||
|         server.get('options').then(optionArr => { |  | ||||||
|             const options = new Options(optionArr); |  | ||||||
|  |  | ||||||
|             resolve(options); | export default 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 |  | ||||||
| } |  | ||||||
| @@ -1,23 +1,19 @@ | |||||||
| import utils from "./utils.js"; | import utils from "./utils.js"; | ||||||
| import optionsService from './options.js'; | import options from './options.js'; | ||||||
|  |  | ||||||
| const PROTECTED_SESSION_ID_KEY = 'protectedSessionId'; | const PROTECTED_SESSION_ID_KEY = 'protectedSessionId'; | ||||||
|  |  | ||||||
| let lastProtectedSessionOperationDate = null; | let lastProtectedSessionOperationDate = 0; | ||||||
| let protectedSessionTimeout = null; |  | ||||||
|  |  | ||||||
| optionsService.addLoadListener(options => setProtectedSessionTimeout(options.getInt('protectedSessionTimeout'))); |  | ||||||
|  |  | ||||||
| setInterval(() => { | setInterval(() => { | ||||||
|     if (lastProtectedSessionOperationDate !== null && Date.now() - lastProtectedSessionOperationDate.getTime() > protectedSessionTimeout * 1000) { |     const protectedSessionTimeout = options.getInt('protectedSessionTimeout'); | ||||||
|  |     if (lastProtectedSessionOperationDate | ||||||
|  |         && Date.now() - lastProtectedSessionOperationDate > protectedSessionTimeout * 1000) { | ||||||
|  |  | ||||||
|         resetProtectedSession(); |         resetProtectedSession(); | ||||||
|     } |     } | ||||||
| }, 5000); | }, 5000); | ||||||
|  |  | ||||||
| function setProtectedSessionTimeout(encSessTimeout) { |  | ||||||
|     protectedSessionTimeout = encSessTimeout; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function setProtectedSessionId(id) { | function setProtectedSessionId(id) { | ||||||
|     // using session cookie so that it disappears after browser/tab is closed |     // using session cookie so that it disappears after browser/tab is closed | ||||||
|     utils.setSessionCookie(PROTECTED_SESSION_ID_KEY, id); |     utils.setSessionCookie(PROTECTED_SESSION_ID_KEY, id); | ||||||
| @@ -37,7 +33,7 @@ function isProtectedSessionAvailable() { | |||||||
|  |  | ||||||
| function touchProtectedSession() { | function touchProtectedSession() { | ||||||
|     if (isProtectedSessionAvailable()) { |     if (isProtectedSessionAvailable()) { | ||||||
|         lastProtectedSessionOperationDate = new Date(); |         lastProtectedSessionOperationDate = Date.now(); | ||||||
|  |  | ||||||
|         setProtectedSessionId(utils.getCookie(PROTECTED_SESSION_ID_KEY)); |         setProtectedSessionId(utils.getCookie(PROTECTED_SESSION_ID_KEY)); | ||||||
|     } |     } | ||||||
| @@ -47,6 +43,5 @@ export default { | |||||||
|     setProtectedSessionId, |     setProtectedSessionId, | ||||||
|     resetProtectedSession, |     resetProtectedSession, | ||||||
|     isProtectedSessionAvailable, |     isProtectedSessionAvailable, | ||||||
|     setProtectedSessionTimeout, |  | ||||||
|     touchProtectedSession |     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 toastService from "./toast.js"; | ||||||
| import appContext from "./app_context.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() { | async function refreshSearch() { | ||||||
|     const activeNode = appContext.getMainNoteTree().getActiveNode(); |     const activeNode = appContext.getMainNoteTree().getActiveNode(); | ||||||
|  |  | ||||||
| @@ -23,10 +35,6 @@ function init() { | |||||||
| } | } | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|     // toggleSearch, |  | ||||||
|     // resetSearch, |  | ||||||
|     // showSearch, |  | ||||||
|     // doSearch, |  | ||||||
|     refreshSearch, |     refreshSearch, | ||||||
|     init, |     init, | ||||||
|     getHelpText: () => helpText |     getHelpText: () => helpText | ||||||
|   | |||||||
| @@ -1,8 +1,6 @@ | |||||||
| import optionsService from "./options.js"; | import options from "./options.js"; | ||||||
|  |  | ||||||
| export async function initSpellCheck() { | export async function initSpellCheck() { | ||||||
|     const options = await optionsService.waitForOptions(); |  | ||||||
|  |  | ||||||
|     const {SpellCheckHandler, ContextMenuListener, ContextMenuBuilder} = require('electron-spellchecker'); |     const {SpellCheckHandler, ContextMenuListener, ContextMenuBuilder} = require('electron-spellchecker'); | ||||||
|     const {remote, shell} = require('electron'); |     const {remote, shell} = require('electron'); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,17 +1,7 @@ | |||||||
| import server from "./server.js"; | import options from "./options.js"; | ||||||
| import optionService from "./options.js"; |  | ||||||
|  |  | ||||||
| let instance; | let instance; | ||||||
|  |  | ||||||
| async function getPaneWidths() { |  | ||||||
|     const options = await optionService.waitForOptions(); |  | ||||||
|  |  | ||||||
|     return { |  | ||||||
|         leftPaneWidth: options.getInt('leftPaneWidth'), |  | ||||||
|         rightPaneWidth: options.getInt('rightPaneWidth') |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function setupSplit(left, right) { | async function setupSplit(left, right) { | ||||||
|     if (instance) { |     if (instance) { | ||||||
|         instance.destroy(); |         instance.destroy(); | ||||||
| @@ -24,15 +14,16 @@ async function setupSplit(left, right) { | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const {leftPaneWidth, rightPaneWidth} = await getPaneWidths(); |     const leftPaneWidth = options.getInt('leftPaneWidth'); | ||||||
|  |     const rightPaneWidth = options.getInt('rightPaneWidth'); | ||||||
|  |  | ||||||
|     if (left && right) { |     if (left && right) { | ||||||
|         instance = Split(['#left-pane', '#center-pane', '#right-pane'], { |         instance = Split(['#left-pane', '#center-pane', '#right-pane'], { | ||||||
|             sizes: [leftPaneWidth, 100 - leftPaneWidth - rightPaneWidth, rightPaneWidth], |             sizes: [leftPaneWidth, 100 - leftPaneWidth - rightPaneWidth, rightPaneWidth], | ||||||
|             gutterSize: 5, |             gutterSize: 5, | ||||||
|             onDragEnd: sizes => { |             onDragEnd: sizes => { | ||||||
|                 server.put('options/leftPaneWidth/' + Math.round(sizes[0])); |                 options.save('leftPaneWidth', Math.round(sizes[0])); | ||||||
|                 server.put('options/rightPaneWidth/' + Math.round(sizes[2])); |                 options.save('rightPaneWidth', Math.round(sizes[2])); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| @@ -41,7 +32,7 @@ async function setupSplit(left, right) { | |||||||
|             sizes: [leftPaneWidth, 100 - leftPaneWidth], |             sizes: [leftPaneWidth, 100 - leftPaneWidth], | ||||||
|             gutterSize: 5, |             gutterSize: 5, | ||||||
|             onDragEnd: sizes => { |             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], |             sizes: [100 - rightPaneWidth, rightPaneWidth], | ||||||
|             gutterSize: 5, |             gutterSize: 5, | ||||||
|             onDragEnd: sizes => { |             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 treeCache from "./tree_cache.js"; | ||||||
| import hoistedNoteService from "./hoisted_note.js"; | import hoistedNoteService from "./hoisted_note.js"; | ||||||
|  |  | ||||||
| let showSidebarInNewTab = true; |  | ||||||
|  |  | ||||||
| optionsService.addLoadListener(options => { |  | ||||||
|     showSidebarInNewTab = options.is('showSidebarInNewTab'); |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| class TabContext extends Component { | class TabContext extends Component { | ||||||
|     /** |     /** | ||||||
|      * @param {AppContext} appContext |      * @param {AppContext} appContext | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ import server from "./server.js"; | |||||||
| import {LoadResults} from "./load_results.js"; | import {LoadResults} from "./load_results.js"; | ||||||
| import NoteComplement from "../entities/note_complement.js"; | import NoteComplement from "../entities/note_complement.js"; | ||||||
| import appContext from "./app_context.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. |  * 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); |             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}); |         appContext.trigger('entitiesReloaded', {loadResults}); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -130,7 +130,7 @@ async function consumeSyncData() { | |||||||
|             await treeCache.processSyncRows(allSyncData); |             await treeCache.processSyncRows(allSyncData); | ||||||
|         } |         } | ||||||
|         catch (e) { |         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 |             // if there's an error in updating the frontend then the easy option to recover is to reload the frontend completely | ||||||
|             utils.reloadApp(); |             utils.reloadApp(); | ||||||
|   | |||||||
| @@ -1,51 +1,47 @@ | |||||||
| import server from "./server.js"; | import options from "./options.js"; | ||||||
| import utils from "./utils.js"; | import Component from "../widgets/component.js"; | ||||||
| import optionsService from "./options.js"; |  | ||||||
|  |  | ||||||
| const MIN_ZOOM = 0.5; | const MIN_ZOOM = 0.5; | ||||||
| const MAX_ZOOM = 2.0; | const MAX_ZOOM = 2.0; | ||||||
|  |  | ||||||
| async function decreaseZoomFactor() { | export default class ZoomService extends Component { | ||||||
|     await setZoomFactorAndSave(getCurrentZoom() - 0.1); |     constructor(appContext) { | ||||||
| } |         super(appContext); | ||||||
|  |  | ||||||
| async function increaseZoomFactor() { |         this.setZoomFactor(options.getFloat('zoomFactor')); | ||||||
|     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; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (zoomFactor >= MIN_ZOOM && zoomFactor <= MAX_ZOOM) { |     setZoomFactor(zoomFactor) { | ||||||
|         setZoomFactor(zoomFactor); |         zoomFactor = parseFloat(zoomFactor); | ||||||
|      |      | ||||||
|         await server.put('options/zoomFactor/' + 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(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     zoomOutListener() { | ||||||
|  |         this.setZoomFactorAndSave(this.getCurrentZoom() - 0.1); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     zoomInListener() { | ||||||
|  |         this.setZoomFactorAndSave(this.getCurrentZoom() + 0.1); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     setZoomFactorAndSaveListener({zoomFactor}) { | ||||||
|  |         this.setZoomFactorAndSave(zoomFactor); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| function getCurrentZoom() { |  | ||||||
|     return require('electron').webFrame.getZoomFactor(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| if (utils.isElectron()) { |  | ||||||
|     optionsService.addLoadListener(options => setZoomFactor(options.getFloat('zoomFactor'))) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export default { |  | ||||||
|     decreaseZoomFactor, |  | ||||||
|     increaseZoomFactor, |  | ||||||
|     setZoomFactor, |  | ||||||
|     setZoomFactorAndSave |  | ||||||
| } |  | ||||||
| @@ -118,7 +118,7 @@ export default class NoteTreeWidget extends TabAwareWidget { | |||||||
|                 autoExpandMS: 600, |                 autoExpandMS: 600, | ||||||
|                 dragStart: (node, data) => { |                 dragStart: (node, data) => { | ||||||
|                     // don't allow dragging root node |                     // don't allow dragging root node | ||||||
|                     if (node.data.noteId === hoistedNoteService.getHoistedNoteNoPromise() |                     if (node.data.noteId === hoistedNoteService.getHoistedNoteId() | ||||||
|                         || node.getParent().data.noteType === 'search') { |                         || node.getParent().data.noteType === 'search') { | ||||||
|                         return false; |                         return false; | ||||||
|                     } |                     } | ||||||
| @@ -141,7 +141,7 @@ export default class NoteTreeWidget extends TabAwareWidget { | |||||||
|                 dragDrop: async (node, data) => { |                 dragDrop: async (node, data) => { | ||||||
|                     if ((data.hitMode === 'over' && node.data.noteType === 'search') || |                     if ((data.hitMode === 'over' && node.data.noteType === 'search') || | ||||||
|                         (['after', 'before'].includes(data.hitMode) |                         (['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'); |                         const infoDialog = await import('../dialogs/info.js'); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,25 +1,10 @@ | |||||||
| import BasicWidget from "./basic_widget.js"; | 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 treeCache from "../services/tree_cache.js"; | ||||||
| import toastService from "../services/toast.js"; | import toastService from "../services/toast.js"; | ||||||
| import appContext from "../services/app_context.js"; | import appContext from "../services/app_context.js"; | ||||||
| import noteCreateService from "../services/note_create.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 = ` | const TPL = ` | ||||||
| <div class="search-box"> | <div class="search-box"> | ||||||
|     <style> |     <style> | ||||||
| @@ -145,7 +130,7 @@ export default class SearchBoxWidget extends BasicWidget { | |||||||
|         this.$searchBox.tooltip({ |         this.$searchBox.tooltip({ | ||||||
|             trigger: 'focus', |             trigger: 'focus', | ||||||
|             html: true, |             html: true, | ||||||
|             title: helpText, |             title: searchService.getHelpText(), | ||||||
|             placement: 'right', |             placement: 'right', | ||||||
|             delay: { |             delay: { | ||||||
|                 show: 500, // necessary because sliding out may cause wrong position |                 show: 500, // necessary because sliding out may cause wrong position | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import BasicWidget from "./basic_widget.js"; | import BasicWidget from "./basic_widget.js"; | ||||||
| import optionService from "../services/options.js"; | import options from "../services/options.js"; | ||||||
|  |  | ||||||
| export default class SidePaneContainer extends BasicWidget { | export default class SidePaneContainer extends BasicWidget { | ||||||
|     constructor(appContext, side, widgets) { |     constructor(appContext, side, widgets) { | ||||||
| @@ -19,9 +19,7 @@ export default class SidePaneContainer extends BasicWidget { | |||||||
|         return this.$widget; |         return this.$widget; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async eventReceived(name, data, sync = false) { |     eventReceived(name, data, sync = false) { | ||||||
|         const options = await optionService.waitForOptions(); |  | ||||||
|  |  | ||||||
|         if (options.is(this.side + 'PaneVisible')) { |         if (options.is(this.side + 'PaneVisible')) { | ||||||
|             super.eventReceived(name, data, sync); |             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 BasicWidget from "./basic_widget.js"; | ||||||
| import optionService from "../services/options.js"; | import options from "../services/options.js"; | ||||||
| import utils from "../services/utils.js"; | import utils from "../services/utils.js"; | ||||||
|  |  | ||||||
| const TPL = ` | const TPL = ` | ||||||
| @@ -23,41 +23,41 @@ export default class TitleBarButtonsWidget extends BasicWidget { | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         this.$widget = $(TPL); |         if (!options.is('nativeTitleBarVisible')) { | ||||||
|  |             this.$widget = $(TPL); | ||||||
|  |             this.$widget.show(); | ||||||
|  |  | ||||||
|         optionService.waitForOptions().then(options => { |             const $minimizeBtn = this.$widget.find(".minimize-btn"); | ||||||
|             if (!options.is('nativeTitleBarVisible')) { |             const $maximizeBtn = this.$widget.find(".maximize-btn"); | ||||||
|                 this.$widget.show(); |             const $closeBtn = this.$widget.find(".close-btn"); | ||||||
|  |  | ||||||
|                 const $minimizeBtn = this.$widget.find(".minimize-btn"); |             $minimizeBtn.on('click', () => { | ||||||
|                 const $maximizeBtn = this.$widget.find(".maximize-btn"); |                 $minimizeBtn.trigger('blur'); | ||||||
|                 const $closeBtn = this.$widget.find(".close-btn"); |                 const {remote} = require('electron'); | ||||||
|  |                 remote.BrowserWindow.getFocusedWindow().minimize(); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|                 $minimizeBtn.on('click', () => { |             $maximizeBtn.on('click', () => { | ||||||
|                     $minimizeBtn.trigger('blur'); |                 $maximizeBtn.trigger('blur'); | ||||||
|                     const {remote} = require('electron'); |                 const {remote} = require('electron'); | ||||||
|                     remote.BrowserWindow.getFocusedWindow().minimize(); |                 const focusedWindow = remote.BrowserWindow.getFocusedWindow(); | ||||||
|                 }); |  | ||||||
|  |  | ||||||
|                 $maximizeBtn.on('click', () => { |                 if (focusedWindow.isMaximized()) { | ||||||
|                     $maximizeBtn.trigger('blur'); |                     focusedWindow.unmaximize(); | ||||||
|                     const {remote} = require('electron'); |                 } else { | ||||||
|                     const focusedWindow = remote.BrowserWindow.getFocusedWindow(); |                     focusedWindow.maximize(); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |  | ||||||
|                     if (focusedWindow.isMaximized()) { |             $closeBtn.on('click', () => { | ||||||
|                         focusedWindow.unmaximize(); |                 $closeBtn.trigger('blur'); | ||||||
|                     } else { |                 const {remote} = require('electron'); | ||||||
|                         focusedWindow.maximize(); |                 remote.BrowserWindow.getFocusedWindow().close(); | ||||||
|                     } |             }); | ||||||
|                 }); |         } | ||||||
|  |         else { | ||||||
|                 $closeBtn.on('click', () => { |             this.$widget = $('<div>'); | ||||||
|                     $closeBtn.trigger('blur'); |         } | ||||||
|                     const {remote} = require('electron'); |  | ||||||
|                     remote.BrowserWindow.getFocusedWindow().close(); |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         return this.$widget; |         return this.$widget; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -146,20 +146,6 @@ body { | |||||||
|     border-color: var(--button-border-color); |     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 { | #right-pane { | ||||||
|     overflow: auto; |     overflow: auto; | ||||||
|     padding-top: 4px; |     padding-top: 4px; | ||||||
|   | |||||||
| @@ -110,9 +110,7 @@ async function updateEntity(entity) { | |||||||
|         const primaryKey = entity[primaryKeyName]; |         const primaryKey = entity[primaryKeyName]; | ||||||
|  |  | ||||||
|         if (entity.isChanged) { |         if (entity.isChanged) { | ||||||
|             if (entityName !== 'options' || entity.isSynced) { |             await syncTableService.addEntitySync(entityName, primaryKey); | ||||||
|                 await syncTableService.addEntitySync(entityName, primaryKey); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if (!cls.isEntityEventsDisabled()) { |             if (!cls.isEntityEventsDisabled()) { | ||||||
|                 const eventPayload = { |                 const eventPayload = { | ||||||
|   | |||||||
| @@ -315,10 +315,13 @@ async function getSyncRecords(syncs) { | |||||||
|     let length = 0; |     let length = 0; | ||||||
|  |  | ||||||
|     for (const sync of syncs) { |     for (const sync of syncs) { | ||||||
|         const record = { |         const entity = await getEntityRow(sync.entityName, sync.entityId); | ||||||
|             sync: sync, |  | ||||||
|             entity: await getEntityRow(sync.entityName, sync.entityId) |         if (sync.entityName === 'options' && !entity.isSynced) { | ||||||
|         }; |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const record = { sync, entity }; | ||||||
|  |  | ||||||
|         records.push(record); |         records.push(record); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -92,6 +92,9 @@ async function fillInAdditionalProperties(sync) { | |||||||
|     } else if (sync.entityName === 'note_reordering') { |     } else if (sync.entityName === 'note_reordering') { | ||||||
|         sync.positions = await sql.getMap(`SELECT branchId, notePosition FROM branches WHERE isDeleted = 0 AND parentNoteId = ?`, [sync.entityId]); |         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) { | async function sendPing(client) { | ||||||
|   | |||||||
| @@ -14,13 +14,7 @@ | |||||||
|     <div id="top-pane"></div> |     <div id="top-pane"></div> | ||||||
|  |  | ||||||
|     <div id="main-pane" style="display: flex; flex-grow: 1; flex-shrink: 1; min-height: 0;"> |     <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> |         <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> | ||||||
|  |  | ||||||
|     <div class="dropdown-menu dropdown-menu-sm" id="context-menu-container"></div> |     <div class="dropdown-menu dropdown-menu-sm" id="context-menu-container"></div> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user