diff --git a/src/main.ts b/src/main.ts index 903cecfd..69319d44 100755 --- a/src/main.ts +++ b/src/main.ts @@ -63,15 +63,17 @@ if (!gotTheLock) { // would return error // https://github.com/nathanbuchar/electron-settings/issues/111 if (fs.existsSync(settings.file())) { - const useHardwareAcceleration = preferenceService.get('useHardwareAcceleration'); - if (!useHardwareAcceleration) { - app.disableHardwareAcceleration(); - } - const ignoreCertificateErrors = preferenceService.get('ignoreCertificateErrors'); - if (ignoreCertificateErrors) { - // https://www.electronjs.org/docs/api/command-line-switches - app.commandLine.appendSwitch('ignore-certificate-errors'); - } + void preferenceService.get('useHardwareAcceleration').then((useHardwareAcceleration) => { + if (!useHardwareAcceleration) { + app.disableHardwareAcceleration(); + } + }); + void preferenceService.get('ignoreCertificateErrors').then((ignoreCertificateErrors) => { + if (ignoreCertificateErrors) { + // https://www.electronjs.org/docs/api/command-line-switches + app.commandLine.appendSwitch('ignore-certificate-errors'); + } + }); } let commonInitFinished = false; /** mock app.whenReady */ diff --git a/src/preload/common/services.ts b/src/preload/common/services.ts index 332ab2d9..a6062dd0 100644 --- a/src/preload/common/services.ts +++ b/src/preload/common/services.ts @@ -25,12 +25,12 @@ import { IWorkspaceService, WorkspaceServiceIPCDescriptor } from '@services/work import { IWorkspaceViewService, WorkspaceViewServiceIPCDescriptor } from '@services/workspacesView/interface'; export const auth = createProxy>(AuthenticationServiceIPCDescriptor); -export const context = createProxy>(ContextServiceIPCDescriptor); -export const git = createProxy>(GitServiceIPCDescriptor); +export const context = createProxy(ContextServiceIPCDescriptor); +export const git = createProxy(GitServiceIPCDescriptor); export const menu = createProxy>(MenuServiceIPCDescriptor); -export const native = createProxy>(NativeServiceIPCDescriptor); -export const notification = createProxy>(NotificationServiceIPCDescriptor); -export const preference = createProxy>(PreferenceServiceIPCDescriptor); +export const native = createProxy(NativeServiceIPCDescriptor); +export const notification = createProxy(NotificationServiceIPCDescriptor); +export const preference = createProxy(PreferenceServiceIPCDescriptor); export const systemPreference = createProxy>(SystemPreferenceServiceIPCDescriptor); export const theme = createProxy>(ThemeServiceIPCDescriptor); export const updater = createProxy>(UpdaterServiceIPCDescriptor); @@ -39,7 +39,7 @@ export const wiki = createProxy>(WikiServiceIPCDescr export const wikiGitWorkspace = createProxy(WikiGitWorkspaceServiceIPCDescriptor); export const window = createProxy>(WindowServiceIPCDescriptor); export const workspace = createProxy>(WorkspaceServiceIPCDescriptor); -export const workspaceView = createProxy>(WorkspaceViewServiceIPCDescriptor); +export const workspaceView = createProxy(WorkspaceViewServiceIPCDescriptor); export const descriptors = { auth: AuthenticationServiceIPCDescriptor, diff --git a/src/renderer.tsx b/src/renderer.tsx index 801a71ca..97c3a16d 100644 --- a/src/renderer.tsx +++ b/src/renderer.tsx @@ -42,7 +42,7 @@ async function runApp(): Promise { document.title = 'Preferred Spell Checking Languages'; } - const attachToMenubar = (await window.service.preference.get('attachToMenubar')) as boolean; + const attachToMenubar = await window.service.preference.get('attachToMenubar'); if (window.meta.windowName !== WindowNames.main && attachToMenubar) { document.addEventListener('keydown', (_event) => { void (async () => { diff --git a/src/services/context/index.ts b/src/services/context/index.ts index 55ec8fc3..90350688 100644 --- a/src/services/context/index.ts +++ b/src/services/context/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/require-await */ import { app } from 'electron'; import process from 'process'; import os from 'os'; @@ -27,7 +28,7 @@ export class ContextService implements IContextService { }; } - public get(key: K): IContext[K] { + public async get(key: K): Promise { if (key in this.context) { return this.context[key]; } diff --git a/src/services/context/interface.ts b/src/services/context/interface.ts index 3d270b17..75444387 100644 --- a/src/services/context/interface.ts +++ b/src/services/context/interface.ts @@ -32,7 +32,7 @@ export interface IContext extends IPaths, IConstants {} * Manage constant value like `isDevelopment` and many else, so you can know about about running environment in main and renderer process easily. */ export interface IContextService { - get(key: K): IContext[K]; + get(key: K): Promise; } export const ContextServiceIPCDescriptor = { channel: ContextChannel.name, diff --git a/src/services/git/index.ts b/src/services/git/index.ts index 34880e2b..5002586b 100644 --- a/src/services/git/index.ts +++ b/src/services/git/index.ts @@ -23,8 +23,10 @@ export class Git implements IGitService { @inject(serviceIdentifier.View) private readonly viewService: IViewService, @inject(serviceIdentifier.Preference) private readonly preferenceService: IPreferenceService, ) { - const syncDebounceInterval = this.preferenceService.get('syncDebounceInterval'); - this.debounceCommitAndSync = debounce(this.commitAndSync.bind(this), syncDebounceInterval); + this.debounceCommitAndSync = this.commitAndSync.bind(this); + void this.preferenceService.get('syncDebounceInterval').then((syncDebounceInterval) => { + this.debounceCommitAndSync = debounce(this.commitAndSync.bind(this), syncDebounceInterval); + }); } public debounceCommitAndSync: (wikiFolderPath: string, githubRepoUrl: string, userInfo: IGitUserInfos) => Promise | undefined; diff --git a/src/services/libs/getViewBounds.ts b/src/services/libs/getViewBounds.ts index e6a4ff98..dd65376d 100644 --- a/src/services/libs/getViewBounds.ts +++ b/src/services/libs/getViewBounds.ts @@ -1,17 +1,15 @@ import { container } from '@services/container'; -import type { IWindowService } from '@services/windows/interface'; import type { IPreferenceService } from '@services/preferences/interface'; -import { WindowNames } from '@services/windows/WindowProperties'; import serviceIdentifier from '@services/serviceIdentifier'; -export default function getViewBounds( +export default async function getViewBounds( contentSize: [number, number], findInPage = false, height?: number, width?: number, -): { x: number; y: number; height: number; width: number } { +): Promise<{ x: number; y: number; height: number; width: number }> { const preferencesService = container.get(serviceIdentifier.Preference); - const showSidebar = preferencesService.get('sidebar'); + const showSidebar = await preferencesService.get('sidebar'); const x = showSidebar ? 68 : 0; const y = 0; diff --git a/src/services/libs/i18n/useDefaultLanguage.ts b/src/services/libs/i18n/useDefaultLanguage.ts index 3ef134ce..8ddd680d 100644 --- a/src/services/libs/i18n/useDefaultLanguage.ts +++ b/src/services/libs/i18n/useDefaultLanguage.ts @@ -6,7 +6,7 @@ import serviceIdentifier from '@services/serviceIdentifier'; export default async function changeToDefaultLanguage(i18next: typeof i18n): Promise { const preferences = container.get(serviceIdentifier.Preference); - const language = preferences.get('language'); + const language = await preferences.get('language'); if (typeof language === 'string') { await i18next.changeLanguage(language); } diff --git a/src/services/native/index.ts b/src/services/native/index.ts index 4502b4e7..5938da92 100644 --- a/src/services/native/index.ts +++ b/src/services/native/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/require-await */ import { app, dialog, shell, MessageBoxOptions } from 'electron'; import { injectable, inject } from 'inversify'; @@ -42,7 +43,7 @@ export class NativeService implements INativeService { return isDirectory ? shell.showItemInFolder(uri) : await shell.openExternal(uri); } - public quit(): void { + public async quit(): Promise { app.quit(); } } diff --git a/src/services/notifications/index.ts b/src/services/notifications/index.ts index da3828a7..a54e6815 100644 --- a/src/services/notifications/index.ts +++ b/src/services/notifications/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/require-await */ import { Notification, NotificationConstructorOptions } from 'electron'; import { injectable } from 'inversify'; import { lazyInject } from '@services/container'; @@ -6,7 +7,6 @@ import type { IPreferenceService } from '@services/preferences/interface'; import type { IViewService } from '@services/view/interface'; import { INotificationService, IPauseNotificationsInfo } from './interface'; import { IWindowService } from '@services/windows/interface'; -import { NotificationChannel } from '@/constants/channels'; import { BehaviorSubject } from 'rxjs'; @injectable() @@ -26,17 +26,17 @@ export class NotificationService implements INotificationService { this.pauseNotificationsInfo$.next(this.pauseNotificationsInfo); } - public show(options: NotificationConstructorOptions): void { + public async show(options: NotificationConstructorOptions): Promise { if (Notification.isSupported()) { const notification = new Notification(options); notification.show(); } } - private getCurrentScheduledDateTime(): { from: Date; to: Date } | undefined { - const pauseNotificationsBySchedule = this.preferenceService.get('pauseNotificationsBySchedule'); - const pauseNotificationsByScheduleFrom = this.preferenceService.get('pauseNotificationsByScheduleFrom'); - const pauseNotificationsByScheduleTo = this.preferenceService.get('pauseNotificationsByScheduleTo'); + private async getCurrentScheduledDateTime(): Promise<{ from: Date; to: Date } | undefined> { + const pauseNotificationsBySchedule = await this.preferenceService.get('pauseNotificationsBySchedule'); + const pauseNotificationsByScheduleFrom = await this.preferenceService.get('pauseNotificationsByScheduleFrom'); + const pauseNotificationsByScheduleTo = await this.preferenceService.get('pauseNotificationsByScheduleTo'); if (!pauseNotificationsBySchedule) return; @@ -95,10 +95,10 @@ export class NotificationService implements INotificationService { /** * return reason why notifications are paused */ - private calcPauseNotificationsInfo(): IPauseNotificationsInfo | undefined { - const pauseNotifications = this.preferenceService.get('pauseNotifications'); + private async calcPauseNotificationsInfo(): Promise { + const pauseNotifications = await this.preferenceService.get('pauseNotifications'); - const schedule = this.getCurrentScheduledDateTime(); + const schedule = await this.getCurrentScheduledDateTime(); const currentDate = new Date(); @@ -137,21 +137,21 @@ export class NotificationService implements INotificationService { private timeouts: NodeJS.Timeout[] = []; /* lock to avoid multiple timeouts running at the same time */ private updating = false; - public updatePauseNotificationsInfo(): void { + public async updatePauseNotificationsInfo(): Promise { if (this.updating) return; this.updating = true; - this.pauseNotificationsInfo = this.calcPauseNotificationsInfo(); + this.pauseNotificationsInfo = await this.calcPauseNotificationsInfo(); // Send update to webview const shouldPauseNotifications = this.pauseNotificationsInfo !== undefined; - const shouldMuteAudio = shouldPauseNotifications && this.preferenceService.get('pauseNotificationsMuteAudio'); + const shouldMuteAudio = shouldPauseNotifications && (await this.preferenceService.get('pauseNotificationsMuteAudio')); this.viewService.setViewsAudioPref(shouldMuteAudio); this.viewService.setViewsNotificationsPref(shouldPauseNotifications); // set schedule for re-updating - const pauseNotifications = this.preferenceService.get('pauseNotifications'); - const schedule = this.getCurrentScheduledDateTime(); + const pauseNotifications = await this.preferenceService.get('pauseNotifications'); + const schedule = await this.getCurrentScheduledDateTime(); // clear old timeouts this.timeouts.forEach((timeout: NodeJS.Timeout) => { @@ -166,7 +166,7 @@ export class NotificationService implements INotificationService { // https://github.com/nodejs/node-v0.x-archive/issues/8656 if (t > 0 && t < 2147483647) { const newTimeout = setTimeout(() => { - this.updatePauseNotificationsInfo(); + void this.updatePauseNotificationsInfo(); }, t); this.timeouts.push(newTimeout); } @@ -188,5 +188,5 @@ export class NotificationService implements INotificationService { this.updateNotificationsInfoSubject(); } - public getPauseNotificationsInfo = (): IPauseNotificationsInfo | undefined => this.pauseNotificationsInfo; + public getPauseNotificationsInfo = async (): Promise => this.pauseNotificationsInfo; } diff --git a/src/services/notifications/interface.ts b/src/services/notifications/interface.ts index 0129d328..2414f217 100644 --- a/src/services/notifications/interface.ts +++ b/src/services/notifications/interface.ts @@ -14,9 +14,9 @@ export interface IPauseNotificationsInfo { */ export interface INotificationService { pauseNotificationsInfo$: BehaviorSubject; - show(options: NotificationConstructorOptions): void; - updatePauseNotificationsInfo(): void; - getPauseNotificationsInfo: () => IPauseNotificationsInfo | undefined; + show(options: NotificationConstructorOptions): Promise; + updatePauseNotificationsInfo(): Promise; + getPauseNotificationsInfo: () => Promise; } export const NotificationServiceIPCDescriptor = { channel: NotificationChannel.name, diff --git a/src/services/preferences/index.ts b/src/services/preferences/index.ts index 44f62678..f48fd313 100755 --- a/src/services/preferences/index.ts +++ b/src/services/preferences/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/require-await */ import { BehaviorSubject } from 'rxjs'; import { injectable } from 'inversify'; import { dialog, nativeTheme } from 'electron'; @@ -77,11 +78,11 @@ export class Preference implements IPreferenceService { return preferenceToSanitize; } - public set(key: K, value: IPreferences[K]): void { + public async set(key: K, value: IPreferences[K]): Promise { this.cachedPreferences[key] = value; this.cachedPreferences = { ...this.cachedPreferences, ...this.sanitizePreference(this.cachedPreferences) }; - void settings.set(`preferences.${key}`, this.cachedPreferences[key] as any); + await settings.set(`preferences.${key}`, this.cachedPreferences[key] as any); this.reactWhenPreferencesChanged(key, value); this.updatePreferenceSubject(); @@ -94,7 +95,7 @@ export class Preference implements IPreferenceService { private reactWhenPreferencesChanged(key: K, value: IPreferences[K]): void { // maybe pauseNotificationsBySchedule or pauseNotifications or ... if (key.startsWith('pauseNotifications')) { - this.notificationService.updatePauseNotificationsInfo(); + void this.notificationService.updatePauseNotificationsInfo(); } if (key === 'themeSource') { nativeTheme.themeSource = value as IPreferences['themeSource']; @@ -110,7 +111,7 @@ export class Preference implements IPreferenceService { this.updatePreferenceSubject(); } - public getPreferences = (): IPreferences => { + public getPreferences = async (): Promise => { // store in memory to boost performance if (this.cachedPreferences === undefined) { return this.getInitPreferencesForCache(); @@ -118,13 +119,13 @@ export class Preference implements IPreferenceService { return this.cachedPreferences; }; - public get(key: K): IPreferences[K] { + public async get(key: K): Promise { return this.cachedPreferences[key]; } public async reset(): Promise { await settings.unset(); - const preferences = this.getPreferences(); + const preferences = await this.getPreferences(); this.cachedPreferences = preferences; await this.setPreferences(preferences); } diff --git a/src/services/preferences/interface.ts b/src/services/preferences/interface.ts index 97c50e69..03193a95 100644 --- a/src/services/preferences/interface.ts +++ b/src/services/preferences/interface.ts @@ -58,12 +58,12 @@ export interface IPreferenceService { /** * Update preferences, update cache and observable */ - set(key: K, value: IPreferences[K]): void; + set(key: K, value: IPreferences[K]): Promise; /** * get preferences, may return cached version */ - getPreferences: () => IPreferences; - get(key: K): IPreferences[K]; + getPreferences: () => Promise; + get(key: K): Promise; reset(): Promise; resetWithConfirm(): Promise; } diff --git a/src/services/theme/index.ts b/src/services/theme/index.ts index a17b1852..060ec311 100644 --- a/src/services/theme/index.ts +++ b/src/services/theme/index.ts @@ -6,16 +6,14 @@ import type { IPreferenceService } from '@services/preferences/interface'; import { ITheme, IThemeService } from './interface'; import serviceIdentifier from '@services/serviceIdentifier'; import { lazyInject } from '@services/container'; -import { IViewService } from '@services/view/interface'; @injectable() export class ThemeService implements IThemeService { @lazyInject(serviceIdentifier.Preference) private readonly preferenceService!: IPreferenceService; - @lazyInject(serviceIdentifier.View) private readonly viewService!: IViewService; public theme$: BehaviorSubject; constructor() { - this.init(); + void this.init(); this.theme$ = new BehaviorSubject({ shouldUseDarkColors: this.shouldUseDarkColors() }); } @@ -23,8 +21,8 @@ export class ThemeService implements IThemeService { this.theme$.next(newTheme); } - private init(): void { - const themeSource = this.preferenceService.get('themeSource'); + private async init(): Promise { + const themeSource = await this.preferenceService.get('themeSource'); // apply theme nativeTheme.themeSource = themeSource; nativeTheme.addListener('updated', () => { @@ -32,7 +30,7 @@ export class ThemeService implements IThemeService { }); } - public shouldUseDarkColors(): boolean { + private shouldUseDarkColors(): boolean { return nativeTheme.shouldUseDarkColors; } } diff --git a/src/services/theme/interface.ts b/src/services/theme/interface.ts index 23b8dcbe..219fdca8 100644 --- a/src/services/theme/interface.ts +++ b/src/services/theme/interface.ts @@ -11,12 +11,10 @@ export interface ITheme { */ export interface IThemeService { theme$: BehaviorSubject; - shouldUseDarkColors(): boolean; } export const ThemeServiceIPCDescriptor = { channel: ThemeChannel.name, properties: { theme$: ProxyPropertyType.Value$, - shouldUseDarkColors: ProxyPropertyType.Function, }, }; diff --git a/src/services/view/index.ts b/src/services/view/index.ts index 64c58796..42cd52d8 100644 --- a/src/services/view/index.ts +++ b/src/services/view/index.ts @@ -33,7 +33,7 @@ export class View implements IViewService { constructor() { this.initIPCHandlers(); - this.registerMenu(); + void this.registerMenu(); } private initIPCHandlers(): void { @@ -45,24 +45,28 @@ export class View implements IViewService { }); } - private registerMenu(): void { + private async registerMenu(): Promise { const hasWorkspaces = this.workspaceService.countWorkspaces() > 0; + const sidebar = await this.preferenceService.get('sidebar'); + const titleBar = await this.preferenceService.get('titleBar'); this.menuService.insertMenu('View', [ { - label: () => (this.preferenceService.get('sidebar') ? 'Hide Sidebar' : 'Show Sidebar'), + label: () => (sidebar ? 'Hide Sidebar' : 'Show Sidebar'), accelerator: 'CmdOrCtrl+Alt+S', - click: () => { - void this.preferenceService.set('sidebar', !this.preferenceService.get('sidebar')); + click: async () => { + const sidebarLatest = await this.preferenceService.get('sidebar'); + void this.preferenceService.set('sidebar', !sidebarLatest); void this.workspaceViewService.realignActiveWorkspace(); }, }, { - label: () => (this.preferenceService.get('titleBar') ? 'Hide Title Bar' : 'Show Title Bar'), + label: () => (titleBar ? 'Hide Title Bar' : 'Show Title Bar'), accelerator: 'CmdOrCtrl+Alt+T', enabled: process.platform === 'darwin', visible: process.platform === 'darwin', - click: () => { - void this.preferenceService.set('titleBar', !this.preferenceService.get('titleBar')); + click: async () => { + const titleBarLatest = await this.preferenceService.get('titleBar'); + void this.preferenceService.set('titleBar', !titleBarLatest); void this.workspaceViewService.realignActiveWorkspace(); }, }, @@ -204,7 +208,7 @@ export class View implements IViewService { if (workspace.isSubWiki) { return; } - const { rememberLastPageVisited, shareWorkspaceBrowsingData, spellcheck, spellcheckLanguages } = this.preferenceService.getPreferences(); + const { rememberLastPageVisited, shareWorkspaceBrowsingData, spellcheck, spellcheckLanguages } = await this.preferenceService.getPreferences(); // configure session, proxy & ad blocker const partitionId = shareWorkspaceBrowsingData ? 'persist:shared' : `persist:${workspace.id}`; if (workspace.storageService !== SupportedStorageServices.local) { @@ -260,7 +264,7 @@ export class View implements IViewService { if (workspace.active) { browserWindow.setBrowserView(view); const contentSize = browserWindow.getContentSize(); - view.setBounds(getViewBounds(contentSize as [number, number])); + view.setBounds(await getViewBounds(contentSize as [number, number])); view.setAutoResize({ width: true, height: true, @@ -300,9 +304,9 @@ export class View implements IViewService { browserWindow.setBrowserView(view); const contentSize = browserWindow.getContentSize(); if (typeof this.workspaceService.getMetaData(id).didFailLoadErrorMessage !== 'string') { - view.setBounds(getViewBounds(contentSize as [number, number], false, 0, 0)); // hide browserView to show error message + view.setBounds(await getViewBounds(contentSize as [number, number], false, 0, 0)); // hide browserView to show error message } else { - view.setBounds(getViewBounds(contentSize as [number, number])); + view.setBounds(await getViewBounds(contentSize as [number, number])); } view.setAutoResize({ width: true, @@ -385,14 +389,14 @@ export class View implements IViewService { } } - public realignActiveView = (browserWindow: BrowserWindow, activeId: string): void => { + public realignActiveView = async (browserWindow: BrowserWindow, activeId: string): Promise => { const view = browserWindow.getBrowserView(); if (view?.webContents !== null) { const contentSize = browserWindow.getContentSize(); if (typeof this.workspaceService.getMetaData(activeId).didFailLoadErrorMessage === 'string') { - view?.setBounds(getViewBounds(contentSize as [number, number], false, 0, 0)); // hide browserView to show error message + view?.setBounds(await getViewBounds(contentSize as [number, number], false, 0, 0)); // hide browserView to show error message } else { - view?.setBounds(getViewBounds(contentSize as [number, number])); + view?.setBounds(await getViewBounds(contentSize as [number, number])); } } }; diff --git a/src/services/view/interface.ts b/src/services/view/interface.ts index 2f5d28fe..a152108e 100644 --- a/src/services/view/interface.ts +++ b/src/services/view/interface.ts @@ -19,7 +19,7 @@ export interface IViewService { reloadViewsWebContentsIfDidFailLoad: () => void; reloadViewsWebContents: () => void; getActiveBrowserView: () => BrowserView | undefined; - realignActiveView: (browserWindow: BrowserWindow, activeId: string) => void; + realignActiveView: (browserWindow: BrowserWindow, activeId: string) => Promise; } export const ViewServiceIPCDescriptor = { channel: ViewChannel.name, diff --git a/src/services/view/setupViewEventHandlers.ts b/src/services/view/setupViewEventHandlers.ts index afc09ff9..ed8d6bf4 100644 --- a/src/services/view/setupViewEventHandlers.ts +++ b/src/services/view/setupViewEventHandlers.ts @@ -1,5 +1,6 @@ +/* eslint-disable @typescript-eslint/no-misused-promises */ /* eslint-disable unicorn/consistent-destructuring */ -import { app, BrowserView, ipcMain, WebContents, shell, NativeImage, BrowserWindowConstructorOptions, BrowserWindow } from 'electron'; +import { app, BrowserView, shell, NativeImage, BrowserWindowConstructorOptions, BrowserWindow } from 'electron'; import path from 'path'; import fsExtra from 'fs-extra'; @@ -15,7 +16,7 @@ import type { IWorkspaceViewService } from '@services/workspacesView/interface'; import type { IWindowService } from '@services/windows/interface'; import { WindowNames, IBrowserViewMetaData } from '@services/windows/WindowProperties'; import { container } from '@services/container'; -import { NotificationChannel, ViewChannel, WindowChannel } from '@/constants/channels'; +import { ViewChannel, WindowChannel } from '@/constants/channels'; export interface IViewContext { workspace: IWorkspace; @@ -63,7 +64,7 @@ export default function setupViewEventHandlers(view: BrowserView, browserWindow: } } }); - view.webContents.on('did-start-loading', () => { + view.webContents.on('did-start-loading', async () => { const workspaceObject = workspaceService.get(workspace.id); // this event might be triggered // even after the workspace obj and BrowserView @@ -79,15 +80,15 @@ export default function setupViewEventHandlers(view: BrowserView, browserWindow: ) { // fix https://github.com/atomery/singlebox/issues/228 const contentSize = browserWindow.getContentSize(); - view.setBounds(getViewBounds(contentSize as [number, number])); + view.setBounds(await getViewBounds(contentSize as [number, number])); } - workspaceService.updateMetaData(workspace.id, { + await workspaceService.updateMetaData(workspace.id, { // eslint-disable-next-line unicorn/no-null didFailLoadErrorMessage: null, isLoading: true, }); }); - view.webContents.on('did-stop-loading', () => { + view.webContents.on('did-stop-loading', async () => { const workspaceObject = workspaceService.get(workspace.id); // this event might be triggered // even after the workspace obj and BrowserView @@ -95,15 +96,15 @@ export default function setupViewEventHandlers(view: BrowserView, browserWindow: if (workspaceObject === undefined) { return; } - workspaceService.updateMetaData(workspace.id, { + await workspaceService.updateMetaData(workspace.id, { isLoading: false, }); const currentUrl = view.webContents.getURL(); - workspaceService.update(workspace.id, { + await workspaceService.update(workspace.id, { lastUrl: currentUrl, }); // fix https://github.com/atomery/webcatalog/issues/870 - workspaceViewService.realignActiveWorkspace(); + await workspaceViewService.realignActiveWorkspace(); }); // focus on initial load // https://github.com/atomery/webcatalog/issues/398 @@ -115,7 +116,7 @@ export default function setupViewEventHandlers(view: BrowserView, browserWindow: }); } // https://electronjs.org/docs/api/web-contents#event-did-fail-load - view.webContents.on('did-fail-load', (_event, errorCode, errorDesc, _validateUrl, isMainFrame) => { + view.webContents.on('did-fail-load', async (_event, errorCode, errorDesc, _validateUrl, isMainFrame) => { const workspaceObject = workspaceService.get(workspace.id); // this event might be triggered // even after the workspace obj and BrowserView @@ -124,13 +125,13 @@ export default function setupViewEventHandlers(view: BrowserView, browserWindow: return; } if (isMainFrame && errorCode < 0 && errorCode !== -3) { - workspaceService.updateMetaData(workspace.id, { + await workspaceService.updateMetaData(workspace.id, { didFailLoadErrorMessage: errorDesc, }); if (workspaceObject.active && browserWindow !== undefined && !browserWindow.isDestroyed()) { // fix https://github.com/atomery/singlebox/issues/228 const contentSize = browserWindow.getContentSize(); - view.setBounds(getViewBounds(contentSize as [number, number], false, 0, 0)); // hide browserView to show error message + view.setBounds(await getViewBounds(contentSize as [number, number], false, 0, 0)); // hide browserView to show error message } } // edge case to handle failed auth @@ -211,8 +212,8 @@ export default function setupViewEventHandlers(view: BrowserView, browserWindow: ); // Handle downloads // https://electronjs.org/docs/api/download-item - view.webContents.session.on('will-download', (_event, item) => { - const { askForDownloadPath, downloadPath } = preferenceService.getPreferences(); + view.webContents.session.on('will-download', async (_event, item) => { + const { askForDownloadPath, downloadPath } = await preferenceService.getPreferences(); // Set the save path, making Electron not to prompt a save dialog. if (!askForDownloadPath) { const finalFilePath = path.join(downloadPath, item.getFilename()); @@ -230,35 +231,37 @@ export default function setupViewEventHandlers(view: BrowserView, browserWindow: } }); // Unread count badge - if (preferenceService.get('unreadCountBadge')) { - view.webContents.on('page-title-updated', (_event, title) => { - const itemCountRegex = /[([{](\d*?)[)\]}]/; - const match = itemCountRegex.exec(title); - const incString = match !== null ? match[1] : ''; - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - const inc = Number.parseInt(incString, 10) || 0; - workspaceService.updateMetaData(workspace.id, { - badgeCount: inc, - }); - let count = 0; - const workspaceMetaData = workspaceService.getAllMetaData(); - Object.values(workspaceMetaData).forEach((metaData) => { - if (typeof metaData?.badgeCount === 'number') { - count += metaData.badgeCount; + void preferenceService.get('unreadCountBadge').then((unreadCountBadge) => { + if (unreadCountBadge) { + view.webContents.on('page-title-updated', async (_event, title) => { + const itemCountRegex = /[([{](\d*?)[)\]}]/; + const match = itemCountRegex.exec(title); + const incString = match !== null ? match[1] : ''; + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions + const inc = Number.parseInt(incString, 10) || 0; + await workspaceService.updateMetaData(workspace.id, { + badgeCount: inc, + }); + let count = 0; + const workspaceMetaData = workspaceService.getAllMetaData(); + Object.values(workspaceMetaData).forEach((metaData) => { + if (typeof metaData?.badgeCount === 'number') { + count += metaData.badgeCount; + } + }); + app.badgeCount = count; + if (process.platform === 'win32') { + if (count > 0) { + const icon = NativeImage.createFromPath(path.resolve(buildResourcePath, 'overlay-icon.png')); + browserWindow.setOverlayIcon(icon, `You have ${count} new messages.`); + } else { + // eslint-disable-next-line unicorn/no-null + browserWindow.setOverlayIcon(null, ''); + } } }); - app.badgeCount = count; - if (process.platform === 'win32') { - if (count > 0) { - const icon = NativeImage.createFromPath(path.resolve(buildResourcePath, 'overlay-icon.png')); - browserWindow.setOverlayIcon(icon, `You have ${count} new messages.`); - } else { - // eslint-disable-next-line unicorn/no-null - browserWindow.setOverlayIcon(null, ''); - } - } - }); - } + } + }); // Find In Page view.webContents.on('found-in-page', (_event, result) => { windowService.sendToAllWindows(ViewChannel.updateFindInPageMatches, result.activeMatchOrdinal, result.matches); diff --git a/src/services/windows/index.ts b/src/services/windows/index.ts index 3c64631b..5fabdc43 100644 --- a/src/services/windows/index.ts +++ b/src/services/windows/index.ts @@ -46,7 +46,7 @@ export class Window implements IWindowService { } } - public stopFindInPage(close?: boolean, windowName: WindowNames = WindowNames.main): void { + public async stopFindInPage(close?: boolean, windowName: WindowNames = WindowNames.main): Promise { const mainWindow = this.get(windowName); const view = mainWindow?.getBrowserView(); // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions @@ -58,7 +58,7 @@ export class Window implements IWindowService { // adjust bounds to hide the gap for find in page if (close === true && mainWindow !== undefined) { const contentSize = mainWindow.getContentSize(); - view.setBounds(getViewBounds(contentSize as [number, number])); + view.setBounds(await getViewBounds(contentSize as [number, number])); } } } @@ -101,8 +101,8 @@ export class Window implements IWindowService { // update window meta this.setWindowMeta(windowName, meta); const existedWindowMeta = this.getWindowMeta(windowName); - const attachToMenubar: boolean = this.preferenceService.get('attachToMenubar'); - const titleBar: boolean = this.preferenceService.get('titleBar'); + const attachToMenubar: boolean = await this.preferenceService.get('attachToMenubar'); + const titleBar: boolean = await this.preferenceService.get('titleBar'); if (existedWindow !== undefined) { // TODO: handle this menubar logic @@ -212,23 +212,23 @@ export class Window implements IWindowService { private registerMainWindowListeners(newWindow: BrowserWindow): void { // Enable swipe to navigate - const swipeToNavigate = this.preferenceService.get('swipeToNavigate'); - if (swipeToNavigate) { - const mainWindow = this.get(WindowNames.main); - if (mainWindow === undefined) return; - mainWindow.on('swipe', (_event, direction) => { - const view = mainWindow?.getBrowserView(); - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - if (view) { - if (direction === 'left') { - view.webContents.goBack(); - } else if (direction === 'right') { - view.webContents.goForward(); + void this.preferenceService.get('swipeToNavigate').then((swipeToNavigate) => { + if (swipeToNavigate) { + const mainWindow = this.get(WindowNames.main); + if (mainWindow === undefined) return; + mainWindow.on('swipe', (_event, direction) => { + const view = mainWindow?.getBrowserView(); + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions + if (view) { + if (direction === 'left') { + view.webContents.goBack(); + } else if (direction === 'right') { + view.webContents.goForward(); + } } - } - }); - } - + }); + } + }); // Hide window instead closing on macos newWindow.on('close', (event) => { const mainWindow = this.get(WindowNames.main); @@ -376,14 +376,14 @@ export class Window implements IWindowService { { label: 'Find', accelerator: 'CmdOrCtrl+F', - click: () => { + click: async () => { const mainWindow = this.get(WindowNames.main); if (mainWindow !== undefined) { mainWindow.webContents.focus(); mainWindow.webContents.send(WindowChannel.openFindInPage); const contentSize = mainWindow.getContentSize(); const view = mainWindow.getBrowserView(); - view?.setBounds(getViewBounds(contentSize as [number, number], true)); + view?.setBounds(await getViewBounds(contentSize as [number, number], true)); } }, enabled: () => this.workspaceService.countWorkspaces() > 0, diff --git a/src/services/windows/interface.ts b/src/services/windows/interface.ts index 29fac8f5..e992f8a3 100644 --- a/src/services/windows/interface.ts +++ b/src/services/windows/interface.ts @@ -23,7 +23,7 @@ export interface IWindowService { reload(windowName: WindowNames): void; clearStorageData(windowName?: WindowNames): Promise; findInPage(text: string, forward?: boolean | undefined, windowName?: WindowNames): void; - stopFindInPage(close?: boolean | undefined, windowName?: WindowNames): void; + stopFindInPage(close?: boolean | undefined, windowName?: WindowNames): Promise; } export const WindowServiceIPCDescriptor = { channel: WindowChannel.name, diff --git a/src/services/workspaces/index.ts b/src/services/workspaces/index.ts index 2462fe63..6b349840 100644 --- a/src/services/workspaces/index.ts +++ b/src/services/workspaces/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable unicorn/no-null */ import { injectable } from 'inversify'; import { app } from 'electron'; @@ -175,12 +176,12 @@ export class Workspace implements IWorkspaceService { this.updateWorkspaceMenuItems(); } - public update(id: string, workspaceSetting: Partial): void { + public async update(id: string, workspaceSetting: Partial): Promise { const workspace = this.get(id); if (workspace === undefined) { return; } - void this.set(id, { ...workspace, ...workspaceSetting }); + await this.set(id, { ...workspace, ...workspaceSetting }); } public async setWorkspaces(newWorkspaces: Record): Promise { @@ -307,7 +308,7 @@ export class Workspace implements IWorkspaceService { newImage.clone().resize(128, 128).quality(100).write(destinationPicturePath, resolve); }); const currentPicturePath = this.get(id)?.picturePath; - this.update(id, { + await this.update(id, { picturePath: destinationPicturePath, }); // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions @@ -386,7 +387,7 @@ export class Workspace implements IWorkspaceService { public getAllMetaData = (): Record> => this.metaData; - public updateMetaData = (id: string, options: Partial): void => { + public updateMetaData = async (id: string, options: Partial): Promise => { this.metaData[id] = { ...this.metaData[id], ...options, diff --git a/src/services/workspaces/interface.ts b/src/services/workspaces/interface.ts index 08761c54..c14eab49 100644 --- a/src/services/workspaces/interface.ts +++ b/src/services/workspaces/interface.ts @@ -89,9 +89,9 @@ export interface IWorkspaceService { countWorkspaces(): number; getMetaData: (id: string) => Partial; getAllMetaData: () => Record>; - updateMetaData: (id: string, options: Partial) => void; + updateMetaData: (id: string, options: Partial) => Promise; set(id: string, workspace: IWorkspace): Promise; - update(id: string, workspaceSetting: Partial): void; + update(id: string, workspaceSetting: Partial): Promise; setWorkspaces(newWorkspaces: Record): Promise; setActiveWorkspace(id: string): Promise; setWorkspacePicture(id: string, sourcePicturePath: string): Promise; diff --git a/src/services/workspacesView/index.ts b/src/services/workspacesView/index.ts index de73ddd4..e23edb01 100644 --- a/src/services/workspacesView/index.ts +++ b/src/services/workspacesView/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable unicorn/consistent-destructuring */ import { app, session, dialog } from 'electron'; import { injectable } from 'inversify'; @@ -38,9 +39,9 @@ export class WorkspaceView implements IWorkspaceViewService { const workspaces = this.workspaceService.getWorkspaces(); for (const workspaceID in workspaces) { const workspace = workspaces[workspaceID]; - if ((this.preferenceService.get('hibernateUnusedWorkspacesAtLaunch') || workspace.hibernateWhenUnused) && !workspace.active) { + if (((await this.preferenceService.get('hibernateUnusedWorkspacesAtLaunch')) || workspace.hibernateWhenUnused) && !workspace.active) { if (!workspace.hibernated && !workspace.isSubWiki) { - this.workspaceService.update(workspaceID, { hibernated: true }); + await this.workspaceService.update(workspaceID, { hibernated: true }); } return; } @@ -245,7 +246,7 @@ export class WorkspaceView implements IWorkspaceViewService { * Seems this is for relocating BrowserView in the electron window * // TODO: why we need this? */ - public realignActiveWorkspace(): void { + public async realignActiveWorkspace(): Promise { // this function only call browserView.setBounds // do not attempt to recall browserView.webContents.focus() // as it breaks page focus (cursor, scroll bar not visible) @@ -258,7 +259,7 @@ export class WorkspaceView implements IWorkspaceViewService { const activeWorkspace = this.workspaceService.getActiveWorkspace(); const mainWindow = this.windowService.get(WindowNames.main); if (activeWorkspace !== undefined && mainWindow !== undefined) { - this.viewService.realignActiveView(mainWindow, activeWorkspace.id); + void this.viewService.realignActiveView(mainWindow, activeWorkspace.id); } } } diff --git a/src/services/workspacesView/interface.ts b/src/services/workspacesView/interface.ts index 4fb72143..e00e0fdb 100644 --- a/src/services/workspacesView/interface.ts +++ b/src/services/workspacesView/interface.ts @@ -23,7 +23,7 @@ export interface IWorkspaceViewService { * @param id workspace id, if omit, will load url in active workspace if existed */ loadURL(url: string, id?: string): Promise; - realignActiveWorkspace(): void; + realignActiveWorkspace(): Promise; openUrlInWorkspace(url: string, id: string): Promise; } export const WorkspaceViewServiceIPCDescriptor = {