From 3deaa14b7a01d5613d42b807bee7f312b89d6dfd Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sun, 31 Dec 2023 16:24:14 +0800 Subject: [PATCH] fix: delay 500ms for window menu, which won't be use in start, but will require CPU resources. And if not in settimeout, getting service itself from ioc container will cause infinite loop. Causing `RangeError: Maximum call stack size exceededException in PromiseRejectCallback` --- src/constants/parameters.ts | 2 + src/services/menu/index.ts | 122 +----------------- src/services/menu/loadDefaultMenuTemplate.ts | 126 +++++++++++++++++++ src/services/windows/index.ts | 5 +- src/services/workspaces/index.ts | 5 +- src/services/workspacesView/index.ts | 5 +- 6 files changed, 145 insertions(+), 120 deletions(-) create mode 100644 src/services/menu/loadDefaultMenuTemplate.ts diff --git a/src/constants/parameters.ts b/src/constants/parameters.ts index 1cc56510..b36db2aa 100644 --- a/src/constants/parameters.ts +++ b/src/constants/parameters.ts @@ -1 +1,3 @@ export const LOAD_VIEW_MAX_RETRIES = 10; +// delay 500ms for window menu, which won't be use in start, but will require CPU resources. And if not in settimeout, getting service itself from ioc container will cause infinite loop. Causing `RangeError: Maximum call stack size exceededException in PromiseRejectCallback` +export const DELAY_MENU_REGISTER = 500; diff --git a/src/services/menu/index.ts b/src/services/menu/index.ts index 40ed898d..b4c31cd8 100644 --- a/src/services/menu/index.ts +++ b/src/services/menu/index.ts @@ -27,6 +27,7 @@ import { IpcSafeMenuItem, mainMenuItemProxy } from './contextMenu/rendererMenuIt import { InsertMenuAfterSubMenuIndexError } from './error'; import type { IMenuService, IOnContextMenuInfo } from './interface'; import { DeferredMenuItemConstructorOptions } from './interface'; +import { loadDefaultMenuTemplate } from './loadDefaultMenuTemplate'; @injectable() export class MenuService implements IMenuService { @@ -69,12 +70,12 @@ export class MenuService implements IMenuService { @lazyInject(serviceIdentifier.WorkspaceView) private readonly workspaceViewService!: IWorkspaceViewService; - private _menuTemplate?: DeferredMenuItemConstructorOptions[]; + #menuTemplate?: DeferredMenuItemConstructorOptions[]; private get menuTemplate(): DeferredMenuItemConstructorOptions[] { - if (this._menuTemplate === undefined) { - this.loadDefaultMenuTemplate(); + if (this.#menuTemplate === undefined) { + this.#menuTemplate = loadDefaultMenuTemplate(); } - return this._menuTemplate!; + return this.#menuTemplate; } /** @@ -147,119 +148,6 @@ export class MenuService implements IMenuService { ); } - /** - * Defer to i18next ready to call this - */ - private loadDefaultMenuTemplate(): void { - this._menuTemplate = [ - { - label: () => i18n.t('Menu.TidGi'), - id: 'TidGi', - submenu: [ - { - label: () => i18n.t('ContextMenu.About'), - click: async () => { - await this.windowService.open(WindowNames.about); - }, - }, - { type: 'separator' }, - { - id: 'update', - label: () => i18n.t('Updater.CheckUpdate'), - click: async () => { - await this.updaterService.checkForUpdates(); - }, - }, - { - label: () => i18n.t('ContextMenu.Preferences'), - accelerator: 'CmdOrCtrl+,', - click: async () => { - await this.windowService.open(WindowNames.preferences); - }, - }, - { type: 'separator' }, - { - label: () => i18n.t('Preference.Notifications'), - click: async () => { - await this.windowService.open(WindowNames.notifications); - }, - accelerator: 'CmdOrCtrl+Shift+N', - }, - { type: 'separator' }, - { role: 'services', submenu: [] }, - { type: 'separator' }, - { role: 'hide' }, - { role: 'hideOthers' }, - { role: 'unhide' }, - { label: () => i18n.t('ContextMenu.Quit') + i18n.t('Menu.TidGi'), role: 'quit' }, - ], - }, - { - label: () => i18n.t('Menu.Edit'), - id: 'Edit', - role: 'editMenu', - }, - { - label: () => i18n.t('Menu.View'), - id: 'View', - }, - { - label: () => i18n.t('Menu.Language'), - id: 'Language', - }, - { - label: () => i18n.t('Menu.History'), - id: 'History', - }, - { - label: () => i18n.t('Menu.Workspaces'), - id: 'Workspaces', - submenu: [], - }, - { - label: () => i18n.t('Menu.Wiki'), - id: 'Wiki', - submenu: [], - }, - { - label: () => i18n.t('Menu.Window'), - role: 'windowMenu', - id: 'Window', - }, - { - label: () => i18n.t('Menu.Help'), - role: 'help', - id: 'help', - submenu: [ - { - label: () => i18n.t('ContextMenu.TidGiSupport'), - click: async () => { - await shell.openExternal('https://github.com/tiddly-gittly/TidGi-desktop/issues'); - }, - }, - { - label: () => i18n.t('Menu.ReportBugViaGithub'), - click: async () => { - await shell.openExternal('https://github.com/tiddly-gittly/TidGi-desktop/issues'); - }, - }, - { - label: () => i18n.t('Menu.RequestFeatureViaGithub'), - click: async () => { - await shell.openExternal('https://github.com/tiddly-gittly/TidGi-desktop/issues/new?template=feature.md&title=feature%3A+'); - }, - }, - { - label: () => i18n.t('Menu.LearnMore'), - click: async () => { - await shell.openExternal('https://github.com/tiddly-gittly/TidGi-desktop/'); - }, - }, - ], - }, - ]; - } - constructor() { // debounce so build menu won't be call very frequently on app launch, where every services are registering menu items this.buildMenu = debounce(this.buildMenu.bind(this), 50) as () => Promise; diff --git a/src/services/menu/loadDefaultMenuTemplate.ts b/src/services/menu/loadDefaultMenuTemplate.ts new file mode 100644 index 00000000..16b06257 --- /dev/null +++ b/src/services/menu/loadDefaultMenuTemplate.ts @@ -0,0 +1,126 @@ +/* eslint-disable @typescript-eslint/no-misused-promises */ +/* eslint-disable @typescript-eslint/require-await */ +import { container } from '@services/container'; +import { i18n } from '@services/libs/i18n'; +import serviceIdentifier from '@services/serviceIdentifier'; +import { IUpdaterService } from '@services/updater/interface'; +import type { IWindowService } from '@services/windows/interface'; +import { WindowNames } from '@services/windows/WindowProperties'; +import { shell } from 'electron'; +import { DeferredMenuItemConstructorOptions } from './interface'; + +/** + * Defer to i18next ready to call this + */ +export function loadDefaultMenuTemplate(): DeferredMenuItemConstructorOptions[] { + const windowService = container.get(serviceIdentifier.Window); + const updaterService = container.get(serviceIdentifier.View); + + return [ + { + label: () => i18n.t('Menu.TidGi'), + id: 'TidGi', + submenu: [ + { + label: () => i18n.t('ContextMenu.About'), + click: async () => { + await windowService.open(WindowNames.about); + }, + }, + { type: 'separator' }, + { + id: 'update', + label: () => i18n.t('Updater.CheckUpdate'), + click: async () => { + await updaterService.checkForUpdates(); + }, + }, + { + label: () => i18n.t('ContextMenu.Preferences'), + accelerator: 'CmdOrCtrl+,', + click: async () => { + await windowService.open(WindowNames.preferences); + }, + }, + { type: 'separator' }, + { + label: () => i18n.t('Preference.Notifications'), + click: async () => { + await windowService.open(WindowNames.notifications); + }, + accelerator: 'CmdOrCtrl+Shift+N', + }, + { type: 'separator' }, + { role: 'services', submenu: [] }, + { type: 'separator' }, + { role: 'hide' }, + { role: 'hideOthers' }, + { role: 'unhide' }, + { label: () => i18n.t('ContextMenu.Quit') + i18n.t('Menu.TidGi'), role: 'quit' }, + ], + }, + { + label: () => i18n.t('Menu.Edit'), + id: 'Edit', + role: 'editMenu', + }, + { + label: () => i18n.t('Menu.View'), + id: 'View', + }, + { + label: () => i18n.t('Menu.Language'), + id: 'Language', + }, + { + label: () => i18n.t('Menu.History'), + id: 'History', + }, + { + label: () => i18n.t('Menu.Workspaces'), + id: 'Workspaces', + submenu: [], + }, + { + label: () => i18n.t('Menu.Wiki'), + id: 'Wiki', + submenu: [], + }, + { + label: () => i18n.t('Menu.Window'), + role: 'windowMenu', + id: 'Window', + }, + { + label: () => i18n.t('Menu.Help'), + role: 'help', + id: 'help', + submenu: [ + { + label: () => i18n.t('ContextMenu.TidGiSupport'), + click: async () => { + await shell.openExternal('https://github.com/tiddly-gittly/TidGi-desktop/issues'); + }, + }, + { + label: () => i18n.t('Menu.ReportBugViaGithub'), + click: async () => { + await shell.openExternal('https://github.com/tiddly-gittly/TidGi-desktop/issues'); + }, + }, + { + label: () => i18n.t('Menu.RequestFeatureViaGithub'), + click: async () => { + await shell.openExternal('https://github.com/tiddly-gittly/TidGi-desktop/issues/new?template=feature.md&title=feature%3A+'); + }, + }, + { + label: () => i18n.t('Menu.LearnMore'), + click: async () => { + await shell.openExternal('https://github.com/tiddly-gittly/TidGi-desktop/'); + }, + }, + ], + }, + ] satisfies DeferredMenuItemConstructorOptions[]; +} diff --git a/src/services/windows/index.ts b/src/services/windows/index.ts index 418a33f1..22961760 100644 --- a/src/services/windows/index.ts +++ b/src/services/windows/index.ts @@ -26,6 +26,7 @@ import { handleCreateBasicWindow } from './handleCreateBasicWindow'; import { IWindowOpenConfig, IWindowService } from './interface'; import { registerBrowserViewWindowListeners } from './registerBrowserViewWindowListeners'; import { registerMenu } from './registerMenu'; +import { DELAY_MENU_REGISTER } from '@/constants/parameters'; @injectable() export class Window implements IWindowService { @@ -50,7 +51,9 @@ export class Window implements IWindowService { private readonly themeService!: IThemeService; constructor() { - void registerMenu(); + setTimeout(() => { + void registerMenu(); + }, DELAY_MENU_REGISTER); } public async findInPage(text: string, forward?: boolean, windowName: WindowNames = WindowNames.main): Promise { diff --git a/src/services/workspaces/index.ts b/src/services/workspaces/index.ts index 407d2afe..51a4bbd1 100644 --- a/src/services/workspaces/index.ts +++ b/src/services/workspaces/index.ts @@ -33,6 +33,7 @@ import { debouncedSetSettingFile } from './debouncedSetSettingFile'; import type { INewWorkspaceConfig, IWorkspace, IWorkspaceMetaData, IWorkspaceService, IWorkspaceWithMetadata } from './interface'; import { registerMenu } from './registerMenu'; import { workspaceSorter } from './utils'; +import { DELAY_MENU_REGISTER } from '@/constants/parameters'; @injectable() export class Workspace implements IWorkspaceService { @@ -68,8 +69,10 @@ export class Workspace implements IWorkspaceService { constructor() { this.workspaces = this.getInitWorkspacesForCache(); - void registerMenu(); this.workspaces$ = new BehaviorSubject>(this.getWorkspacesWithMetadata()); + setTimeout(() => { + void registerMenu(); + }, DELAY_MENU_REGISTER); } private getWorkspacesWithMetadata(): Record { diff --git a/src/services/workspacesView/index.ts b/src/services/workspacesView/index.ts index a708eb6c..cfb25d98 100644 --- a/src/services/workspacesView/index.ts +++ b/src/services/workspacesView/index.ts @@ -25,6 +25,7 @@ import type { IWindowService } from '@services/windows/interface'; import { WindowNames } from '@services/windows/WindowProperties'; import type { IWorkspace, IWorkspaceService } from '@services/workspaces/interface'; +import { DELAY_MENU_REGISTER } from '@/constants/parameters'; import type { IInitializeWorkspaceOptions, IWorkspaceViewService } from './interface'; import { registerMenu } from './registerMenu'; @@ -64,7 +65,9 @@ export class WorkspaceView implements IWorkspaceViewService { private readonly nativeService!: INativeService; constructor() { - void registerMenu(); + setTimeout(() => { + void registerMenu(); + }, DELAY_MENU_REGISTER); } public async initializeAllWorkspaceView(): Promise {