Notes/src/public/app/services/app_context.js

168 lines
4.8 KiB
JavaScript
Raw Normal View History

2021-04-16 23:01:56 +02:00
import froca from "./froca.js";
2020-01-19 21:12:53 +01:00
import bundleService from "./bundle.js";
import RootCommandExecutor from "./root_command_executor.js";
2020-01-21 22:54:16 +01:00
import Entrypoints from "./entrypoints.js";
import options from "./options.js";
2020-02-02 22:32:44 +01:00
import utils from "./utils.js";
import zoomService from "./zoom.js";
import TabManager from "./tab_manager.js";
2020-02-09 21:13:05 +01:00
import treeService from "./tree.js";
2020-02-16 19:21:17 +01:00
import Component from "../widgets/component.js";
2020-02-16 20:09:59 +01:00
import keyboardActionsService from "./keyboard_actions.js";
2020-04-26 09:40:02 +02:00
import MobileScreenSwitcherExecutor from "../widgets/mobile_widgets/mobile_screen_switcher.js";
import MainTreeExecutors from "./main_tree_executors.js";
import toast from "./toast.js";
2020-02-16 19:21:17 +01:00
class AppContext extends Component {
2020-04-25 23:52:13 +02:00
constructor(isMainWindow) {
super();
this.isMainWindow = isMainWindow;
2020-05-20 08:54:37 +02:00
this.executors = [];
this.beforeUnloadListeners = [];
2020-04-25 23:52:13 +02:00
}
2020-03-01 10:41:23 +01:00
setLayout(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() {
2021-04-16 22:57:37 +02:00
await Promise.all([froca.initializedPromise, options.initializedPromise]);
2020-03-16 21:16:09 +01:00
2020-03-29 23:10:45 +02:00
this.showWidgets();
2020-04-25 23:52:13 +02:00
this.tabManager.loadTabs();
2020-02-02 22:32:44 +01:00
setTimeout(() => bundleService.executeStartupBundles(), 2000);
2020-02-02 22:04:28 +01:00
}
2020-01-12 19:05:09 +01:00
2020-02-02 22:04:28 +01:00
showWidgets() {
2020-02-16 19:21:17 +01:00
const rootWidget = this.layout.getRootWidget(this);
const $renderedWidget = rootWidget.render();
2020-02-16 20:09:59 +01:00
keyboardActionsService.updateDisplayedShortcuts($renderedWidget);
2020-02-09 22:31:52 +01:00
$("body").append($renderedWidget);
$renderedWidget.on('click', "[data-trigger-command]", function() {
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-27 10:03:14 +01:00
this.tabManager = new TabManager();
2020-02-15 10:41:21 +01:00
this.executors = [
this.tabManager,
new RootCommandExecutor(),
new Entrypoints(),
new MainTreeExecutors()
2020-01-13 21:48:44 +01:00
];
2020-03-01 15:19:16 +01:00
if (utils.isMobile()) {
this.executors.push(new MobileScreenSwitcherExecutor());
}
2020-02-27 10:03:14 +01:00
this.child(rootWidget);
for (const executor of this.executors) {
this.child(executor);
}
2020-02-17 22:38:46 +01:00
if (utils.isElectron()) {
this.child(zoomService);
}
2020-02-16 19:21:17 +01:00
this.triggerEvent('initialRenderComplete');
}
/** @returns {Promise} */
triggerEvent(name, data) {
return this.handleEvent(name, data);
}
2020-02-15 10:41:21 +01:00
/** @returns {Promise} */
triggerCommand(name, data = {}) {
2020-02-15 10:41:21 +01:00
for (const executor of this.executors) {
const fun = executor[name + "Command"];
2020-02-15 10:41:21 +01:00
if (fun) {
return executor.callMethod(fun, data);
2020-02-15 10:41:21 +01:00
}
}
// this might hint at error but sometimes this is used by components which are at different places
// 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.`);
return this.triggerEvent(name, data);
2020-02-15 10:41:21 +01:00
}
2020-02-16 19:21:17 +01:00
getComponentByEl(el) {
return $(el).closest(".component").prop('component');
}
addBeforeUnloadListener(obj) {
if (typeof WeakRef !== "function") {
// older browsers don't support WeakRef
return;
}
this.beforeUnloadListeners.push(new WeakRef(obj));
}
2020-01-12 19:05:09 +01:00
}
2020-04-25 23:52:13 +02:00
const appContext = new AppContext(window.glob.isMainWindow);
2020-02-02 10:41:43 +01:00
// we should save all outstanding changes before the page/app is closed
$(window).on('beforeunload', () => {
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.`);
toast.showMessage("Please wait for a couple of seconds for the save to finish, then you can try again.", 10000);
allSaved = false;
}
}
if (!allSaved) {
return "some string";
}
2020-02-02 10:41:43 +01:00
});
function isNotePathInAddress() {
2021-05-22 12:26:45 +02:00
const [notePath, ntxId] = treeService.getHashValueFromAddress();
return notePath.startsWith("root")
// empty string is for empty/uninitialized tab
2021-05-22 12:26:45 +02:00
|| (notePath === '' && !!ntxId);
}
$(window).on('hashchange', function() {
if (isNotePathInAddress()) {
2021-05-22 12:26:45 +02:00
const [notePath, ntxId] = treeService.getHashValueFromAddress();
if (!notePath) {
console.log(`Invalid hash value "${document.location.hash}", ignoring.`);
return;
}
2021-05-22 12:35:41 +02:00
appContext.tabManager.switchToNoteContext(ntxId, notePath);
}
});
export default appContext;