| 
									
										
										
										
											2022-12-01 13:07:23 +01:00
										 |  |  | import froca from "../services/froca.js"; | 
					
						
							|  |  |  | import bundleService from "../services/bundle.js"; | 
					
						
							| 
									
										
										
										
											2021-04-24 22:18:25 +02:00
										 |  |  | import RootCommandExecutor from "./root_command_executor.js"; | 
					
						
							| 
									
										
										
										
											2020-01-21 22:54:16 +01:00
										 |  |  | import Entrypoints from "./entrypoints.js"; | 
					
						
							| 
									
										
										
										
											2022-12-01 13:07:23 +01:00
										 |  |  | import options from "../services/options.js"; | 
					
						
							|  |  |  | import utils from "../services/utils.js"; | 
					
						
							|  |  |  | import zoomComponent from "./zoom.js"; | 
					
						
							| 
									
										
										
										
											2020-02-07 21:08:55 +01:00
										 |  |  | import TabManager from "./tab_manager.js"; | 
					
						
							| 
									
										
										
										
											2022-12-01 13:07:23 +01:00
										 |  |  | import Component from "./component.js"; | 
					
						
							|  |  |  | import keyboardActionsService from "../services/keyboard_actions.js"; | 
					
						
							| 
									
										
										
										
											2023-05-07 21:18:21 +02:00
										 |  |  | import linkService from "../services/link.js"; | 
					
						
							| 
									
										
										
										
											2022-12-01 13:07:23 +01:00
										 |  |  | import MobileScreenSwitcherExecutor from "./mobile_screen_switcher.js"; | 
					
						
							| 
									
										
										
										
											2020-03-17 12:28:02 +01:00
										 |  |  | import MainTreeExecutors from "./main_tree_executors.js"; | 
					
						
							| 
									
										
										
										
											2022-12-01 13:07:23 +01:00
										 |  |  | import toast from "../services/toast.js"; | 
					
						
							| 
									
										
										
										
											2022-12-01 13:24:34 +01:00
										 |  |  | import ShortcutComponent from "./shortcut_component.js"; | 
					
						
							| 
									
										
										
										
											2024-10-15 15:46:34 +08:00
										 |  |  | import { t, initLocale } from "../services/i18n.js"; | 
					
						
							| 
									
										
										
										
											2024-12-19 22:06:42 +02:00
										 |  |  | import NoteDetailWidget from "../widgets/note_detail.js"; | 
					
						
							| 
									
										
										
										
											2020-01-11 21:19:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-25 20:55:04 +03:00
										 |  |  | interface Layout { | 
					
						
							|  |  |  |     getRootWidget: (appContext: AppContext) => RootWidget; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | interface RootWidget extends Component { | 
					
						
							|  |  |  |     render: () => JQuery<HTMLElement>;     | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | interface BeforeUploadListener extends Component { | 
					
						
							|  |  |  |     beforeUnloadEvent(): boolean; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-19 22:06:42 +02:00
										 |  |  | export type TriggerData = { | 
					
						
							| 
									
										
										
										
											2024-10-26 10:29:15 +03:00
										 |  |  |     noteId?: string; | 
					
						
							| 
									
										
										
										
											2024-07-25 20:55:04 +03:00
										 |  |  |     noteIds?: string[]; | 
					
						
							| 
									
										
										
										
											2024-12-19 22:06:42 +02:00
										 |  |  |     messages?: unknown[];     | 
					
						
							| 
									
										
										
										
											2024-07-25 20:55:04 +03:00
										 |  |  |     callback?: () => void; | 
					
						
							| 
									
										
										
										
											2024-12-19 22:06:42 +02:00
										 |  |  | } | { | 
					
						
							|  |  |  |     ntxId: string; | 
					
						
							|  |  |  |     notePath: string; | 
					
						
							|  |  |  | } | { | 
					
						
							|  |  |  |     text: string; | 
					
						
							|  |  |  | } | { | 
					
						
							|  |  |  |     callback: (value: NoteDetailWidget | PromiseLike<NoteDetailWidget>) => void | 
					
						
							| 
									
										
										
										
											2024-12-21 14:34:16 +02:00
										 |  |  | } | { | 
					
						
							|  |  |  |     // For "searchNotes"
 | 
					
						
							|  |  |  |     searchString: string | undefined; | 
					
						
							| 
									
										
										
										
											2024-07-25 20:55:04 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-16 19:21:17 +01:00
										 |  |  | class AppContext extends Component { | 
					
						
							| 
									
										
										
										
											2024-07-25 20:55:04 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     isMainWindow: boolean; | 
					
						
							|  |  |  |     components: Component[]; | 
					
						
							|  |  |  |     beforeUnloadListeners: WeakRef<BeforeUploadListener>[]; | 
					
						
							|  |  |  |     tabManager!: TabManager; | 
					
						
							|  |  |  |     layout?: Layout; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     constructor(isMainWindow: boolean) { | 
					
						
							| 
									
										
										
										
											2020-04-25 23:52:13 +02:00
										 |  |  |         super(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.isMainWindow = isMainWindow; | 
					
						
							| 
									
										
										
										
											2022-12-01 13:07:23 +01:00
										 |  |  |         // non-widget/layout components needed for the application
 | 
					
						
							|  |  |  |         this.components = []; | 
					
						
							| 
									
										
										
										
											2021-02-27 23:39:02 +01:00
										 |  |  |         this.beforeUnloadListeners = []; | 
					
						
							| 
									
										
										
										
											2020-04-25 23:52:13 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-11 08:12:01 +03:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Must be called as soon as possible, before the creation of any components since this method is in charge of initializing the locale. Any attempts to read translation before this method is called will result in `undefined`. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     async earlyInit() { | 
					
						
							|  |  |  |         await options.initializedPromise; | 
					
						
							| 
									
										
										
										
											2024-08-11 14:22:37 +03:00
										 |  |  |         await initLocale(); | 
					
						
							| 
									
										
										
										
											2024-08-11 08:12:01 +03:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-25 20:55:04 +03:00
										 |  |  |     setLayout(layout: Layout) { | 
					
						
							| 
									
										
										
										
											2020-02-06 21:47:31 +01:00
										 |  |  |         this.layout = layout; | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-25 23:52:13 +02:00
										 |  |  |     async start() { | 
					
						
							| 
									
										
										
										
											2022-12-01 13:07:23 +01:00
										 |  |  |         this.initComponents(); | 
					
						
							|  |  |  |         this.renderWidgets(); | 
					
						
							| 
									
										
										
										
											2020-03-29 23:10:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-18 16:12:29 +01:00
										 |  |  |         await froca.initializedPromise; | 
					
						
							| 
									
										
										
										
											2022-11-22 22:45:50 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-25 23:52:13 +02:00
										 |  |  |         this.tabManager.loadTabs(); | 
					
						
							| 
									
										
										
										
											2020-02-02 22:32:44 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-18 15:09:08 -05:00
										 |  |  |         setTimeout(() => bundleService.executeStartupBundles(), 2000); | 
					
						
							| 
									
										
										
										
											2020-02-02 22:04:28 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-01 13:07:23 +01:00
										 |  |  |     initComponents() { | 
					
						
							|  |  |  |         this.tabManager = new TabManager(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.components = [ | 
					
						
							|  |  |  |             this.tabManager, | 
					
						
							|  |  |  |             new RootCommandExecutor(), | 
					
						
							|  |  |  |             new Entrypoints(), | 
					
						
							| 
									
										
										
										
											2022-12-01 13:24:34 +01:00
										 |  |  |             new MainTreeExecutors(), | 
					
						
							|  |  |  |             new ShortcutComponent() | 
					
						
							| 
									
										
										
										
											2022-12-01 13:07:23 +01:00
										 |  |  |         ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (utils.isMobile()) { | 
					
						
							|  |  |  |             this.components.push(new MobileScreenSwitcherExecutor()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (const component of this.components) { | 
					
						
							|  |  |  |             this.child(component); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (utils.isElectron()) { | 
					
						
							|  |  |  |             this.child(zoomComponent); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     renderWidgets() { | 
					
						
							| 
									
										
										
										
											2024-07-25 20:55:04 +03:00
										 |  |  |         if (!this.layout) { | 
					
						
							|  |  |  |             throw new Error("Missing layout."); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-16 19:21:17 +01:00
										 |  |  |         const rootWidget = this.layout.getRootWidget(this); | 
					
						
							|  |  |  |         const $renderedWidget = rootWidget.render(); | 
					
						
							| 
									
										
										
										
											2020-01-14 21:23:32 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-16 20:09:59 +01:00
										 |  |  |         keyboardActionsService.updateDisplayedShortcuts($renderedWidget); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-09 22:31:52 +01:00
										 |  |  |         $("body").append($renderedWidget); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-05 23:58:52 +02:00
										 |  |  |         $renderedWidget.on('click', "[data-trigger-command]", function() { | 
					
						
							| 
									
										
										
										
											2023-05-07 10:43:51 +02:00
										 |  |  |             if ($(this).hasClass("disabled")) { | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-05 23:58:52 +02:00
										 |  |  |             const commandName = $(this).attr('data-trigger-command'); | 
					
						
							|  |  |  |             const $component = $(this).closest(".component"); | 
					
						
							|  |  |  |             const component = $component.prop("component"); | 
					
						
							| 
									
										
										
										
											2020-02-09 22:31:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-06 21:24:51 +02:00
										 |  |  |             component.triggerCommand(commandName, {$el: $(this)}); | 
					
						
							| 
									
										
										
										
											2020-02-09 22:31:52 +01:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2020-02-05 22:08:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-27 10:03:14 +01:00
										 |  |  |         this.child(rootWidget); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-16 19:21:17 +01:00
										 |  |  |         this.triggerEvent('initialRenderComplete'); | 
					
						
							| 
									
										
										
										
											2020-01-11 21:19:56 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-25 20:55:04 +03:00
										 |  |  |     triggerEvent(name: string, data: TriggerData = {}) { | 
					
						
							| 
									
										
										
										
											2020-02-29 19:43:19 +01:00
										 |  |  |         return this.handleEvent(name, data); | 
					
						
							| 
									
										
										
										
											2020-02-01 22:29:32 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-02-15 10:41:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-25 20:55:04 +03:00
										 |  |  |     triggerCommand(name: string, data: TriggerData = {}) { | 
					
						
							| 
									
										
										
										
											2022-12-01 13:07:23 +01:00
										 |  |  |         for (const executor of this.components) { | 
					
						
							| 
									
										
										
										
											2024-07-25 20:55:04 +03:00
										 |  |  |             const fun = (executor as any)[`${name}Command`]; | 
					
						
							| 
									
										
										
										
											2020-02-15 10:41:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-29 19:43:19 +01:00
										 |  |  |             if (fun) { | 
					
						
							|  |  |  |                 return executor.callMethod(fun, data); | 
					
						
							| 
									
										
										
										
											2020-02-15 10:41:21 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-30 11:18:34 +02:00
										 |  |  |         // this might hint at error, but sometimes this is used by components which are at different places
 | 
					
						
							| 
									
										
										
										
											2020-05-05 23:58:52 +02:00
										 |  |  |         // in the component tree to communicate with each other
 | 
					
						
							| 
									
										
										
										
											2020-02-17 22:38:46 +01:00
										 |  |  |         console.debug(`Unhandled command ${name}, converting to event.`); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-29 19:43:19 +01:00
										 |  |  |         return this.triggerEvent(name, data); | 
					
						
							| 
									
										
										
										
											2020-02-15 10:41:21 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-25 20:55:04 +03:00
										 |  |  |     getComponentByEl(el: HTMLElement) { | 
					
						
							| 
									
										
										
										
											2020-02-16 19:21:17 +01:00
										 |  |  |         return $(el).closest(".component").prop('component'); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-02-27 23:39:02 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-25 20:55:04 +03:00
										 |  |  |     addBeforeUnloadListener(obj: BeforeUploadListener) { | 
					
						
							| 
									
										
										
										
											2021-02-27 23:39:02 +01:00
										 |  |  |         if (typeof WeakRef !== "function") { | 
					
						
							|  |  |  |             // older browsers don't support WeakRef
 | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-25 20:55:04 +03:00
										 |  |  |         this.beforeUnloadListeners.push(new WeakRef<BeforeUploadListener>(obj)); | 
					
						
							| 
									
										
										
										
											2021-02-27 23:39:02 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-25 23:52:13 +02:00
										 |  |  | const appContext = new AppContext(window.glob.isMainWindow); | 
					
						
							| 
									
										
										
										
											2020-01-12 12:48:17 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 10:41:43 +01:00
										 |  |  | // we should save all outstanding changes before the page/app is closed
 | 
					
						
							| 
									
										
										
										
											2022-01-10 20:37:33 +01:00
										 |  |  | $(window).on('beforeunload', () => { | 
					
						
							| 
									
										
										
										
											2021-02-27 23:39:02 +01:00
										 |  |  |     let allSaved = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     appContext.beforeUnloadListeners = appContext.beforeUnloadListeners.filter(wr => !!wr.deref()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (const weakRef of appContext.beforeUnloadListeners) { | 
					
						
							|  |  |  |         const component = weakRef.deref(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!component) { | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!component.beforeUnloadEvent()) { | 
					
						
							|  |  |  |             console.log(`Component ${component.componentId} is not finished saving its state.`); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-15 15:46:34 +08:00
										 |  |  |             toast.showMessage(t("app_context.please_wait_for_save"), 10000); | 
					
						
							| 
									
										
										
										
											2021-02-27 23:39:02 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             allSaved = false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!allSaved) { | 
					
						
							|  |  |  |         return "some string"; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-02-02 10:41:43 +01:00
										 |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  | $(window).on('hashchange', function() { | 
					
						
							| 
									
										
										
										
											2023-05-07 21:18:21 +02:00
										 |  |  |     const {notePath, ntxId, viewScope} = linkService.parseNavigationStateFromUrl(window.location.href); | 
					
						
							| 
									
										
										
										
											2020-03-21 21:04:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-07 21:18:21 +02:00
										 |  |  |     if (notePath || ntxId) { | 
					
						
							| 
									
										
										
										
											2023-04-11 21:41:55 +02:00
										 |  |  |         appContext.tabManager.switchToNoteContext(ntxId, notePath, viewScope); | 
					
						
							| 
									
										
										
										
											2020-02-03 20:07:34 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-11 20:08:55 +02:00
										 |  |  | export default appContext; |