UNPKG

@launchmenu/core

Version:

An environment for visual keyboard controlled applets

348 lines 27 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LMSession = void 0; const model_react_1 = require("model-react"); const react_1 = __importDefault(require("react")); const IOContext_1 = require("../../context/IOContext"); const SettingsContext_1 = require("../../settings/SettingsContext"); const TextField_1 = require("../../textFields/TextField"); const createHighlighterWithSearchPattern_1 = require("../../uiLayers/types/menuSearch/createHighlighterWithSearchPattern"); const UndoRedoFacility_1 = require("../../undoRedo/UndoRedoFacility"); const ApplicationLayout_1 = require("../components/ApplicationLayout"); const uuid_1 = require("uuid"); const LMSessionMenu_1 = require("./LMSessionMenu"); const adjustSearchable_1 = require("../../utils/searchExecuter/adjustSearchable"); const withSession_1 = require("../applets/declaration/withSession"); const emitContextEvent_1 = require("../../context/uiExtracters/emitContextEvent"); const createStandardMenuKeyHandler_1 = require("../../menus/menu/interaction/keyHandler/createStandardMenuKeyHandler"); const getCategoryAction_1 = require("../../actions/types/category/getCategoryAction"); const adjustSubscribable_1 = require("../../utils/subscribables/adjustSubscribable"); const SearchExecuter_1 = require("../../utils/searchExecuter/SearchExecuter"); const MainMenuView_1 = require("../components/MainMenuView"); const LMSessionLayer_1 = require("./LMSessionLayer"); const useLMSession_1 = require("../hooks/useLMSession"); const TextFieldView_1 = require("../../components/fields/TextFieldView"); const InstantOpenTransition_1 = require("../../components/context/stacks/transitions/open/InstantOpenTransition"); /** * An application session */ class LMSession { /** * Creates a new app session * @param lm The LM instance this is a session for */ constructor(lm) { /** The unique runtime id of this session */ this.ID = uuid_1.v4(); /** Additional applets for this session */ this.extraApplets = new model_react_1.Field([]); /** Additional searchable sources for this session */ this.extraSearchables = new model_react_1.Field([]); /** The applet that's currently selected */ this.selectedApplet = new model_react_1.Field(null); /* Observers that track changes */ this.observers = {}; /** Listeners that listen for close events */ this.closeListeners = []; /** * The applet data obtained from the applet manager */ this.appletData = new model_react_1.DataCacher((h, currentAppletData = []) => { const managerApplets = this.LM.getAppletManager().getAppletsData(h); // Dispose the sessions related data for any applet that will no longer exist currentAppletData.map(appletData => { var _a, _b; const stillExists = managerApplets.find(({ applet }) => applet == appletData.applet); if (!stillExists) (_b = (_a = appletData.initializedApplet).onCloseSession) === null || _b === void 0 ? void 0 : _b.call(_a); }); // Obtain the new applet data list const newData = managerApplets.map(({ applet, version }) => { var _a; let updated = false; let searchableID = `${applet.ID}-${version}`; // Find the current data for this applet const current = currentAppletData.find(({ applet: { ID } }) => ID == applet.ID); if (current) { updated = current.version != version; if (!updated) return current; // If it has updated, change the search id if (((_a = current.searchable) === null || _a === void 0 ? void 0 : _a.ID) == searchableID) searchableID = "updated-" + searchableID; } // Initialize the new applet const initializedApplet = withSession_1.withSession(applet, this); if (updated && this.LM.isInDevMode()) this.callAppletReload(initializedApplet); // Obtain the searchable and return the data const searchable = initializedApplet.search && this.getAppletSearchWithCategory(initializedApplet, searchableID); return { initializedApplet, applet, searchable, version }; }); return newData; }); this.LM = lm; this.setupContext(); this.setupView(); this.setupUI(); } /** * Disposes of all data attached to this session */ destroy() { var _a, _b, _c, _d; (_a = this.observers.search) === null || _a === void 0 ? void 0 : _a.destroy(); (_b = this.observers.settingsContext) === null || _b === void 0 ? void 0 : _b.destroy(); (_c = this.observers.menuCursor) === null || _c === void 0 ? void 0 : _c.destroy(); (_d = this.context) === null || _d === void 0 ? void 0 : _d.destroy(); } /** * Emits a key event to the session * @param event The event to dispatch * @returns Whether the event was caught */ emit(event) { return emitContextEvent_1.emitContextEvent(this.context, event); } /** * Initializes the ioContext for this session */ setupContext() { this.context = new IOContext_1.IOContext({ isInDevMode: h => this.LM.isInDevMode(h), undoRedo: new UndoRedoFacility_1.UndoRedoFacility(), settings: new SettingsContext_1.SettingsContext(), contextMenuBindings: this.getGlobalContextMenuBindings(), session: this, }); // Retrieve the settings context from LM which includes all base settings data, and listen for changes this.observers.settingsContext = new model_react_1.Observer(h => { this.LM.getAppletManager().getApplets(h); return this.LM.getSettingsManager().getSettingsContext(h); }).listen(settingsContext => { this.context.settings = settingsContext; }, true); } /** * Retrieves the context menu items that should be global in this session * @returns The menu items */ getGlobalContextMenuBindings() { return hook => this.getApplets(hook).flatMap(applet => { var _a; return applet.globalContextMenuBindings instanceof Function ? applet.globalContextMenuBindings(hook) : (_a = applet.globalContextMenuBindings) !== null && _a !== void 0 ? _a : []; }); } /** * Initializes the view for this session */ setupView() { this.view = (react_1.default.createElement(useLMSession_1.LMSessionProvider, { value: this }, react_1.default.createElement(ApplicationLayout_1.ApplicationLayout, { key: this.ID, context: this.context }))); } // Sets up the interface /** * Initializes all the UI */ async setupUI() { const menu = await this.setupMenu(); const field = await this.setupField(); this.homeLayer = new LMSessionLayer_1.LMSessionLayer([...menu, ...field]); this.context.open(this.homeLayer); } /** * Initializes the menu to be displayed */ async setupMenu() { this.menu = new LMSessionMenu_1.LMSessionMenu(this.context); // Update the selected applet based on what category a given item belongs to const appletManager = this.LM.getAppletManager(); this.observers.menuCursor = new model_react_1.Observer(h => this.menu.getCursor(h)).listen(item => { if (item) { const category = getCategoryAction_1.getCategoryAction.getCategory(item); const appletData = appletManager .getAppletCategories() .find(({ category: cat }) => cat == category); if (appletData) this.selectedApplet.set(appletData.applet); } }, true); // Setup a search executer this.searchExecuter = new SearchExecuter_1.SearchExecuter({ searchable: { ID: "root", search: async (query, hook) => ({ children: this.getSearchables(hook), }), }, onAdd: item => { // console.log("Added", item); this.menu.addItem(item); }, onRemove: item => { // console.log("Removed", item); this.menu.removeItem(item); }, }); // Create the menu key handler const { handler, destroy } = createStandardMenuKeyHandler_1.createStandardMenuKeyHandler(this.menu, { onExit: () => { if (this.searchField.get() == "") this.emitClose(); else this.searchField.set(""); }, }); // Return the UI to be shown: return [ { menu: this.menu, menuView: react_1.default.createElement(MainMenuView_1.MainMenuView, { menu: this.menu }), searchable: false, menuHandler: handler, onClose: destroy, }, ]; } /** * Initializes the field to be displayed */ async setupField() { // Create a text field and connect it to the search executer this.searchField = new TextField_1.TextField(); this.observers.search = new model_react_1.Observer(h => this.searchField.get(h)).listen(search => { var _a; const query = { search, context: this.context }; this.searchExecuter.setQuery(query); (_a = this.menu) === null || _a === void 0 ? void 0 : _a.setQuery(query); }); const highlighter = createHighlighterWithSearchPattern_1.createHighlighterWithSearchPattern(h => this.searchExecuter.getPatterns(h)); // Return the UI to be shown return [ { field: this.searchField, highlighter, icon: "search", fieldView: { view: (react_1.default.createElement(TextFieldView_1.TextFieldView, { highlighter: highlighter, icon: "search", field: this.searchField })), transitions: { Open: InstantOpenTransition_1.InstantOpenTransition }, }, }, ]; } // Close listeners /** * Adds a listener that listens for close events * @param listener The listener to be added */ addCloseListener(listener) { if (!this.closeListeners.includes(listener)) this.closeListeners.push(listener); } /** * Removes a listener that listens for close events * @param listener The listener to be removed */ removeCloseListener(listener) { const index = this.closeListeners.indexOf(listener); if (index != -1) this.closeListeners.splice(index, 1); } /** * Emits the close event */ emitClose() { this.closeListeners.forEach(listener => listener()); } /** * Checks whether this session is on the "home screen", I.e. has no menu opens on top * @param hook The hook to subscribe to changes * @returns Whether on the home screen */ isHome(hook) { var _a; const layers = this.context.getUI(hook); return (layers[layers.length - 1] == this.homeLayer && ((_a = this.searchField) === null || _a === void 0 ? void 0 : _a.get(hook)) == ""); } /** * Closes all layers and removes the search input * @param close Whether to also exit LM */ async goHome(close) { this.searchField.set(""); if (close) this.emitClose(); await this.context.closeAll(); } // Applet management /** * Retrieves the applets initialized with this session data * @param hook The hook to subscribe to changes * @returns The initialized applets */ getApplets(hook) { return [ ...this.appletData.get(hook).map(({ initializedApplet }) => initializedApplet), ...this.extraApplets.get(hook), ]; } /** * Retrieves the searchables in this session * @param hook The hook to subscribe to changes * @returns The searchables */ getSearchables(hook) { return [ ...this.appletData.get(hook).flatMap(({ searchable }) => searchable !== null && searchable !== void 0 ? searchable : []), ...this.extraSearchables.get(hook), ]; } /** * Calls the reload of the given applet and sets up the disposers (Modifies the onCloseSession of the applet) * @param applet The applet to call reload on */ callAppletReload(applet) { var _a, _b; const disposer = (_b = (_a = applet === null || applet === void 0 ? void 0 : applet.development) === null || _a === void 0 ? void 0 : _a.onReload) === null || _b === void 0 ? void 0 : _b.call(_a); if (disposer) { const oldOnClose = applet.onCloseSession; applet.onCloseSession = () => { disposer(); oldOnClose === null || oldOnClose === void 0 ? void 0 : oldOnClose(); }; } } /** * Wraps the search method of an applet to inject the applets category into the results * @param applet The applet to retrieve the searchable with results for * @param ID The ID of the searchable * @returns The menu searchable */ getAppletSearchWithCategory(applet, ID = applet.ID) { const category = this.LM.getAppletManager().getAppletCategory(applet); if (!category) return { ...applet, ID }; const categoryBinding = getCategoryAction_1.getCategoryAction.createBinding(category); return adjustSearchable_1.adjustSearchable({ ...applet, ID }, { item: result => result ? { ...result, item: { ...result.item, actionBindings: adjustSubscribable_1.adjustSubscribable(result.item.actionBindings, bindings => [...bindings, categoryBinding] //search category takes priority ), }, } : result, }); } } exports.LMSession = LMSession; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTE1TZXNzaW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2FwcGxpY2F0aW9uL0xNU2Vzc2lvbi9MTVNlc3Npb24udHN4Il0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLDZDQUFtRjtBQUNuRixrREFBMEI7QUFDMUIsdURBQWtEO0FBR2xELG9FQUErRDtBQUUvRCwwREFBcUQ7QUFDckQsMkhBQXNIO0FBQ3RILHNFQUFpRTtBQUNqRSx1RUFBa0U7QUFFbEUsK0JBQWdDO0FBRWhDLG1EQUE4QztBQUM5QyxrRkFBNkU7QUFHN0Usb0VBQStEO0FBRS9ELGtGQUE2RTtBQUM3RSx1SEFBa0g7QUFDbEgsc0ZBQWlGO0FBR2pGLHFGQUFnRjtBQUVoRiw4RUFBeUU7QUFDekUsNkRBQXdEO0FBQ3hELHFEQUFnRDtBQUNoRCx3REFBd0Q7QUFDeEQseUVBQW9FO0FBQ3BFLGtIQUE2RztBQUU3Rzs7R0FFRztBQUNILE1BQWEsU0FBUztJQXVDbEI7OztPQUdHO0lBQ0gsWUFBbUIsRUFBYztRQXBDakMsNENBQTRDO1FBQzVCLE9BQUUsR0FBRyxTQUFJLEVBQUUsQ0FBQztRQVU1QiwwQ0FBMEM7UUFDbkMsaUJBQVksR0FBRyxJQUFJLG1CQUFLLENBQUMsRUFBZSxDQUFDLENBQUM7UUFDakQscURBQXFEO1FBQzlDLHFCQUFnQixHQUFHLElBQUksbUJBQUssQ0FBQyxFQUF1QixDQUFDLENBQUM7UUFLN0QsMkNBQTJDO1FBQ3BDLG1CQUFjLEdBQUcsSUFBSSxtQkFBSyxDQUFDLElBQXNCLENBQUMsQ0FBQztRQUUxRCxrQ0FBa0M7UUFDM0IsY0FBUyxHQUlaLEVBQUUsQ0FBQztRQUVQLDZDQUE2QztRQUNuQyxtQkFBYyxHQUFtQixFQUFFLENBQUM7UUF1UTlDOztXQUVHO1FBQ0ksZUFBVSxHQUFHLElBQUksd0JBQVUsQ0FPaEMsQ0FBQyxDQUFDLEVBQUUsaUJBQWlCLEdBQUcsRUFBRSxFQUFFLEVBQUU7WUFDNUIsTUFBTSxjQUFjLEdB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