From 012076cb7015de5bd8522f403e016d87cf2255fd Mon Sep 17 00:00:00 2001 From: lin onetwo Date: Sun, 30 Jun 2024 01:52:22 +0800 Subject: [PATCH] refactor: BrowserView -> WebContentsView --- src/components/TokenForm/gitTokenHooks.ts | 2 +- src/main.ts | 3 +- .../AddWorkspace/useCallWikiInitialization.ts | 2 +- src/pages/Main/useInitialPage.ts | 2 +- src/preload/common/services.ts | 2 +- src/services/libs/getFromRenderer.ts | 6 +- src/services/libs/sendToMainWindow.ts | 2 +- src/services/pages/Readme.md | 4 +- src/services/sync/index.ts | 2 +- src/services/view/handleNewWindow.ts | 4 +- src/services/view/index.ts | 106 +++++++++--------- src/services/view/interface.ts | 20 ++-- .../view/setupIpcServerRoutesHandlers.ts | 4 +- src/services/view/setupViewEventHandlers.ts | 16 +-- src/services/view/setupViewFileProtocol.ts | 4 +- src/services/view/setupViewSession.ts | 2 +- src/services/windows/WindowProperties.ts | 2 +- src/services/windows/handleAttachToMenuBar.ts | 10 +- .../windows/handleCreateBasicWindow.ts | 2 +- src/services/windows/index.ts | 41 ++++--- src/services/windows/interface.ts | 14 +-- .../registerBrowserViewWindowListeners.ts | 18 ++- src/services/windows/registerMenu.ts | 19 ++-- src/services/workspacesView/index.ts | 65 +++++------ src/services/workspacesView/interface.ts | 4 +- src/services/workspacesView/registerMenu.ts | 4 +- 26 files changed, 181 insertions(+), 179 deletions(-) diff --git a/src/components/TokenForm/gitTokenHooks.ts b/src/components/TokenForm/gitTokenHooks.ts index f4f631f3..a8dc9f0a 100644 --- a/src/components/TokenForm/gitTokenHooks.ts +++ b/src/components/TokenForm/gitTokenHooks.ts @@ -9,7 +9,7 @@ export function useAuth(storageService: SupportedStorageServices): [() => Promis const onClickLogout = useCallback(async () => { try { await window.service.auth.set(`${storageService}-token`, ''); - await window.service.window.clearStorageData(); + // await window.service.window.clearStorageData(); } catch (error) { console.error(error); } diff --git a/src/main.ts b/src/main.ts index b69982f0..8395de49 100755 --- a/src/main.ts +++ b/src/main.ts @@ -7,7 +7,6 @@ import './helpers/singleInstance'; import './services/database/configSetting'; import { app, ipcMain, powerMonitor, protocol } from 'electron'; import unhandled from 'electron-unhandled'; -import fs from 'fs-extra'; import inspector from 'node:inspector'; import { MainChannel } from '@/constants/channels'; @@ -101,7 +100,7 @@ const commonInit = async (): Promise => { // https://github.com/atomery/webcatalog/issues/561 // run it here not in mainWindow.createAsync() // because if the `mainWindow` is maximized or minimized - // before the workspaces's BrowserView fully loaded + // before the workspaces's WebContentsView fully loaded // error will occur // see https://github.com/atomery/webcatalog/issues/637 // eslint-disable-next-line promise/always-return diff --git a/src/pages/AddWorkspace/useCallWikiInitialization.ts b/src/pages/AddWorkspace/useCallWikiInitialization.ts index bd9d13c0..c0b0f3c0 100644 --- a/src/pages/AddWorkspace/useCallWikiInitialization.ts +++ b/src/pages/AddWorkspace/useCallWikiInitialization.ts @@ -22,7 +22,7 @@ export async function callWikiInitialization( } // start wiki on startup, or on sub-wiki creation wikiCreationMessageSetter(t('Log.InitializeWorkspaceView')); - /** create workspace from workspaceService to store workspace configs, and create a BrowserView to actually display wiki web content from viewService */ + /** create workspace from workspaceService to store workspace configs, and create a WebContentsView to actually display wiki web content from viewService */ await window.service.workspaceView.initializeWorkspaceView(newWorkspace, { isNew: true, from: configs.from }); wikiCreationMessageSetter(t('Log.InitializeWorkspaceViewDone')); await window.service.workspaceView.setActiveWorkspaceView(newWorkspace.id); diff --git a/src/pages/Main/useInitialPage.ts b/src/pages/Main/useInitialPage.ts index 19a6e6eb..5d44da2e 100644 --- a/src/pages/Main/useInitialPage.ts +++ b/src/pages/Main/useInitialPage.ts @@ -7,7 +7,7 @@ import { useLocation } from 'wouter'; export function useInitialPage() { const [, setLocation] = useLocation(); - // when first open the TidGi and no workspace is active (so no BrowserView will be on top of the React), goto the active pages route + // when first open the TidGi and no workspace is active (so no WebContentsView will be on top of the React), goto the active pages route const initialActivePage = usePromiseValue(async () => await window.service.pages.getActivePage(), null); const initialActiveWorkspace = usePromiseValue(async () => await window.service.workspace.getActiveWorkspace(), null); // only do this once, and not triggering unnecessary rerender by using ref. diff --git a/src/preload/common/services.ts b/src/preload/common/services.ts index 4185b729..9f39d851 100644 --- a/src/preload/common/services.ts +++ b/src/preload/common/services.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/ban-types */ /** * Provide API from main services to GUI (for example, preference window), and tiddlywiki - * This file should be required by BrowserView's preload script to work + * This file should be required by WebContentsView's preload script to work */ import { createProxy } from 'electron-ipc-cat/client'; diff --git a/src/services/libs/getFromRenderer.ts b/src/services/libs/getFromRenderer.ts index 87160c78..83201dbe 100644 --- a/src/services/libs/getFromRenderer.ts +++ b/src/services/libs/getFromRenderer.ts @@ -1,12 +1,12 @@ import { Channels } from '@/constants/channels'; -import { BrowserView, BrowserWindow, ipcMain } from 'electron'; +import { BrowserWindow, ipcMain, WebContentsView } from 'electron'; /** - * Get data from a BrowserView + * Get data from a WebContentsView * @param channel * @param viewToGetData */ -export default async function getFromRenderer(channel: Channels, viewToGetData: BrowserView | BrowserWindow): Promise { +export default async function getFromRenderer(channel: Channels, viewToGetData: WebContentsView | BrowserWindow): Promise { // prevent several ipc happened together, and later return too early so first get the result that is for later one const ipcToken = String(Math.random()); viewToGetData.webContents.send(channel, { ipcToken }); diff --git a/src/services/libs/sendToMainWindow.ts b/src/services/libs/sendToMainWindow.ts index 8c8dd369..f433db62 100644 --- a/src/services/libs/sendToMainWindow.ts +++ b/src/services/libs/sendToMainWindow.ts @@ -21,7 +21,7 @@ export function sendToMainWindowNoWait(type: WikiChannel, workspaceID: string, m /** * Send to main window renderer (preload script) and wait for response. * - * Will throw error when on Windows and App is at background (BrowserView will disappear and not accessible.) https://github.com/tiddly-gittly/TidGi-Desktop/issues/398 + * Will throw error when on Windows and App is at background (WebContentsView will disappear and not accessible.) https://github.com/tiddly-gittly/TidGi-Desktop/issues/398 * * @param type The handler on renderer (preload script) side should implement `ipcRenderer.send(WikiChannel.xxx, nonceReceived, result);`, where `result` is usually `string[]` (the default type for `` in function signature) * @returns undefined if main window webContents is not found diff --git a/src/services/pages/Readme.md b/src/services/pages/Readme.md index 2de293e2..59c181c6 100644 --- a/src/services/pages/Readme.md +++ b/src/services/pages/Readme.md @@ -8,6 +8,6 @@ Guide and Help are two kind of other pages, which are build-in utility pages. ## Developer point of view -When click on a Wiki workspace on sidebar, we switch to `src/pages/WikiBackground` page, and put a BrowserView on top of it (by realign this BrowserView). If BrowserView load url with error, we realign the BrowserView to hide it, and reveal the WikiBackground below of it, show error message on the WikiBackground page. +When click on a Wiki workspace on sidebar, we switch to `src/pages/WikiBackground` page, and put a WebContentsView on top of it (by realign this WebContentsView). If WebContentsView load url with error, we realign the WebContentsView to hide it, and reveal the WikiBackground below of it, show error message on the WikiBackground page. -When click on other pages like Guide page, we realign the BrowserView to hide it, and show the Guide page in the `src/pages/Main/index.tsx`. +When click on other pages like Guide page, we realign the WebContentsView to hide it, and show the Guide page in the `src/pages/Main/index.tsx`. diff --git a/src/services/sync/index.ts b/src/services/sync/index.ts index bc759651..bb81c919 100644 --- a/src/services/sync/index.ts +++ b/src/services/sync/index.ts @@ -110,7 +110,7 @@ export class Sync implements ISyncService { logger.error( `${(error as Error).message} when checking draft titles. ${ (error as Error).stack ?? '' - }\n This might because it just will throw error when on Windows and App is at background (BrowserView will disappear and not accessible.)`, + }\n This might because it just will throw error when on Windows and App is at background (WebContentsView will disappear and not accessible.)`, ); // when app is on background, might have no draft, because user won't edit it. So just return true return true; diff --git a/src/services/view/handleNewWindow.ts b/src/services/view/handleNewWindow.ts index a0e8e88a..383a8726 100644 --- a/src/services/view/handleNewWindow.ts +++ b/src/services/view/handleNewWindow.ts @@ -1,4 +1,4 @@ -import { BrowserView, BrowserWindowConstructorOptions, shell } from 'electron'; +import { BrowserWindowConstructorOptions, shell, WebContentsView } from 'electron'; import windowStateKeeper from 'electron-window-state'; import { SETTINGS_FOLDER } from '@/constants/appPaths'; @@ -17,7 +17,7 @@ import { handleOpenFileExternalLink } from './setupViewFileProtocol'; export interface INewWindowContext { meta: IViewMeta; sharedWebPreferences: BrowserWindowConstructorOptions['webPreferences']; - view: BrowserView; + view: WebContentsView; workspace: IWorkspace; } diff --git a/src/services/view/index.ts b/src/services/view/index.ts index 0811cca0..7d8b48b0 100644 --- a/src/services/view/index.ts +++ b/src/services/view/index.ts @@ -1,7 +1,8 @@ +/* eslint-disable @typescript-eslint/strict-boolean-expressions */ /* eslint-disable n/no-callback-literal */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/consistent-type-assertions */ -import { BrowserView, BrowserWindow, ipcMain, WebPreferences } from 'electron'; +import { BrowserWindow, ipcMain, WebContentsView, WebPreferences } from 'electron'; import { injectable } from 'inversify'; import type { IMenuService } from '@services/menu/interface'; @@ -100,7 +101,7 @@ export class View implements IViewService { }, }, // same behavior as BrowserWindow with autoHideMenuBar: true - // but with addition to readjust BrowserView so it won't cover the menu bar + // but with addition to readjust WebContentsView so it won't cover the menu bar { label: () => i18n.t('Preference.ToggleMenuBar'), visible: false, @@ -134,9 +135,10 @@ export class View implements IViewService { contents.zoomFactor = 1; return; } + // browserWindow above is for the main window's react UI // modify browser view in the main window - const mainWindow = this.windowService.get(WindowNames.main); - mainWindow?.getBrowserView()?.webContents?.setZoomFactor(1); + const view = await this.getActiveBrowserView(); + view?.webContents?.setZoomFactor?.(1); }, enabled: hasWorkspaces, }, @@ -154,10 +156,8 @@ export class View implements IViewService { return; } // modify browser view in the main window - const mainWindow = this.windowService.get(WindowNames.main); - const webContent = mainWindow?.getBrowserView()?.webContents; - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - webContent?.setZoomFactor(webContent.getZoomFactor() + 0.05); + const view = await this.getActiveBrowserView(); + view?.webContents?.setZoomFactor?.(view.webContents.getZoomFactor() + 0.05); }, enabled: hasWorkspaces, }, @@ -175,10 +175,8 @@ export class View implements IViewService { return; } // modify browser view in the main window - const mainWindow = this.windowService.get(WindowNames.main); - const webContent = mainWindow?.getBrowserView()?.webContents; - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - webContent?.setZoomFactor(webContent.getZoomFactor() - 0.05); + const view = await this.getActiveBrowserView(); + view?.webContents?.setZoomFactor?.(view.webContents.getZoomFactor() - 0.05); }, enabled: hasWorkspaces, }, @@ -196,8 +194,7 @@ export class View implements IViewService { return; } // refresh the main window browser view's wiki content, instead of sidebar's react content - const mainWindow = this.windowService.get(WindowNames.main); - mainWindow?.getBrowserView()?.webContents?.reload(); + await this.reloadActiveBrowserView(); }, enabled: hasWorkspaces, }, @@ -206,21 +203,21 @@ export class View implements IViewService { } /** - * Record> + * Record> * * Each workspace can have several windows to render its view (main window and menu bar) */ - private readonly views = new Map | undefined>(); + private readonly views = new Map | undefined>(); public async getViewCount(): Promise { // eslint-disable-next-line @typescript-eslint/return-await return await Promise.resolve(Object.keys(this.views).length); } - public getView(workspaceID: string, windowName: WindowNames): BrowserView | undefined { + public getView(workspaceID: string, windowName: WindowNames): WebContentsView | undefined { return this.views.get(workspaceID)?.get(windowName); } - public setView(workspaceID: string, windowName: WindowNames, newView: BrowserView): void { + public setView(workspaceID: string, windowName: WindowNames, newView: WebContentsView): void { const workspaceOwnedViews = this.views.get(workspaceID); if (workspaceOwnedViews === undefined) { this.views.set(workspaceID, new Map([[windowName, newView]])); @@ -289,13 +286,13 @@ export class View implements IViewService { } satisfies WebPreferences; } - public async createViewAddToWindow(workspace: IWorkspace, browserWindow: BrowserWindow, sharedWebPreferences: WebPreferences, windowName: WindowNames): Promise { - // create a new BrowserView - const view = new BrowserView({ + public async createViewAddToWindow(workspace: IWorkspace, browserWindow: BrowserWindow, sharedWebPreferences: WebPreferences, windowName: WindowNames): Promise { + // create a new WebContentsView + const view = new WebContentsView({ webPreferences: sharedWebPreferences, }); // background needs to explicitly set - // if not, by default, the background of BrowserView is transparent + // if not, by default, the background of WebContentsView is transparent // which would break the CSS of certain websites // even with dark mode, all major browsers // always use #FFF as default page background @@ -307,20 +304,21 @@ export class View implements IViewService { view.webContents.audioMuted = this.shouldMuteAudio; } if (workspace.active || windowName === WindowNames.secondary) { - browserWindow.setBrowserView(view); + browserWindow.contentView.addChildView(view); const contentSize = browserWindow.getContentSize(); - view.setBounds(await getViewBounds(contentSize as [number, number], { windowName })); - view.setAutoResize({ - width: true, - height: true, - }); + const newViewBounds = await getViewBounds(contentSize as [number, number], { windowName }); + view.setBounds(newViewBounds); + // view.setAutoResize({ + // width: true, + // height: true, + // }); } return view; } public async initializeWorkspaceViewHandlersAndLoad( browserWindow: BrowserWindow, - view: BrowserView, + view: WebContentsView, configs: { sharedWebPreferences: WebPreferences; uri?: string; windowName: WindowNames; workspace: IWorkspace }, ) { const { sharedWebPreferences, uri, workspace, windowName } = configs; @@ -337,7 +335,7 @@ export class View implements IViewService { await this.loadUrlForView(workspace, view, uri); } - public async loadUrlForView(workspace: IWorkspace, view: BrowserView, uri?: string): Promise { + public async loadUrlForView(workspace: IWorkspace, view: WebContentsView, uri?: string): Promise { const { rememberLastPageVisited } = this.preferenceService.getPreferences(); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/strict-boolean-expressions const urlToLoad = uri || (rememberLastPageVisited ? workspace.lastUrl : workspace.homeUrl) || workspace.homeUrl || getDefaultTidGiUrl(workspace.id); @@ -368,7 +366,7 @@ export class View implements IViewService { } } - public forEachView(functionToRun: (view: BrowserView, workspaceID: string, windowName: WindowNames) => unknown): void { + public forEachView(functionToRun: (view: WebContentsView, workspaceID: string, windowName: WindowNames) => unknown): void { [...this.views.keys()].forEach((workspaceID) => { const workspaceOwnedViews = this.views.get(workspaceID); if (workspaceOwnedViews !== undefined) { @@ -407,19 +405,16 @@ export class View implements IViewService { await this.addView(workspace, windowName); } } else { - browserWindow.setBrowserView(view); - logger.debug(`setActiveView() setBrowserView`); + browserWindow.contentView.addChildView(view); + logger.debug(`setActiveView() contentView.addChildView`); const contentSize = browserWindow.getContentSize(); if (workspace !== undefined && (await this.workspaceService.workspaceDidFailLoad(workspace.id))) { view.setBounds(await getViewBounds(contentSize as [number, number], { findInPage: false, windowName }, 0, 0)); // hide browserView to show error message } else { - logger.debug(`setActiveView() contentSize ${JSON.stringify(contentSize)}`); - view.setBounds(await getViewBounds(contentSize as [number, number], { windowName })); + const newViewBounds = await getViewBounds(contentSize as [number, number], { windowName }); + logger.debug(`setActiveView() contentSize ${JSON.stringify(newViewBounds)}`); + view.setBounds(newViewBounds); } - view.setAutoResize({ - width: true, - height: true, - }); // focus on webview // https://github.com/quanglam2807/webcatalog/issues/398 view.webContents.focus(); @@ -436,8 +431,8 @@ export class View implements IViewService { view.webContents.stopFindInPage('clearSelection'); view.webContents.send(WindowChannel.closeFindInPage); - // don't set activate browserView to null here `browserWindow.setBrowserView(null);`, the "current browser view" may point to other workspace's view now, it will close other workspace's view when switching workspaces. - browserWindow.removeBrowserView(view); + // don't clear contentView here `browserWindow.contentView.children = [];`, the "current contentView" may point to other workspace's view now, it will close other workspace's view when switching workspaces. + browserWindow.contentView.removeChildView(view); } else { logger.error(`removeView() view or browserWindow is undefined for workspaceID ${workspaceID} windowName ${windowName}, not destroying view properly.`); } @@ -525,7 +520,7 @@ export class View implements IViewService { return view.webContents.getURL(); } - public async getActiveBrowserView(): Promise { + public async getActiveBrowserView(): Promise { const workspace = await this.workspaceService.getActiveWorkspace(); if (workspace !== undefined) { const isMenubarOpen = await this.windowService.isMenubarOpen(); @@ -537,7 +532,7 @@ export class View implements IViewService { } } - public async getActiveBrowserViews(): Promise> { + public async getActiveBrowserViews(): Promise> { const workspace = await this.workspaceService.getActiveWorkspace(); if (workspace !== undefined) { return [this.getView(workspace.id, WindowNames.main), this.getView(workspace.id, WindowNames.menuBar)]; @@ -560,15 +555,17 @@ export class View implements IViewService { } public async realignActiveView(browserWindow: BrowserWindow, activeId: string, windowName: WindowNames, isRetry?: boolean): Promise { - const view = browserWindow.getBrowserView(); - if (view?.webContents !== null && view?.webContents !== undefined) { + // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression + const view = this.getView(activeId, windowName); + if (view?.webContents) { const contentSize = browserWindow.getContentSize(); if (await this.workspaceService.workspaceDidFailLoad(activeId)) { logger.warn(`realignActiveView() hide because didFailLoad`); - await this.hideView(browserWindow); + await this.hideView(browserWindow, windowName, activeId); } else { - logger.debug(`realignActiveView() contentSize set to ${JSON.stringify(contentSize)}`); - view?.setBounds(await getViewBounds(contentSize as [number, number], { windowName })); + const newViewBounds = await getViewBounds(contentSize as [number, number], { windowName }); + logger.debug(`realignActiveView() contentSize set to ${JSON.stringify(newViewBounds)}`); + view?.setBounds(newViewBounds); } } else if (isRetry === true) { logger.error( @@ -583,20 +580,21 @@ export class View implements IViewService { } } - public async hideView(browserWindow: BrowserWindow): Promise { - const view = browserWindow.getBrowserView(); - if (view !== null) { + public async hideView(browserWindow: BrowserWindow, windowName: WindowNames, idToDeactivate: string): Promise { + logger.debug('Hide view', { idToDeactivate, windowName }); + if (!idToDeactivate) return; + const view = this.getView(idToDeactivate, windowName); + if (view) { const contentSize = browserWindow.getContentSize(); // disable view features - view?.setAutoResize({ horizontal: false, vertical: false }); view.webContents.stopFindInPage('clearSelection'); view.webContents.send(WindowChannel.closeFindInPage); // make view small, hide browserView to show error message or other pages view?.setBounds({ x: -contentSize[0], y: -contentSize[1], - width: 0, - height: 0, + width: contentSize[0], + height: contentSize[1], }); } } diff --git a/src/services/view/interface.ts b/src/services/view/interface.ts index 6daad6ce..9ebcdc61 100644 --- a/src/services/view/interface.ts +++ b/src/services/view/interface.ts @@ -1,4 +1,4 @@ -import type { BrowserView, BrowserWindow, WebPreferences } from 'electron'; +import type { BrowserWindow, WebContentsView, WebPreferences } from 'electron'; import { ProxyPropertyType } from 'electron-ipc-cat/common'; import { ViewChannel } from '@/constants/channels'; @@ -15,7 +15,7 @@ export type INewWindowAction = }; /** - * BrowserView related things, the BrowserView is the webview like frame that renders our wiki website. + * WebContentsView related things, the WebContentsView is the webview like frame that renders our wiki website. */ export interface IViewService { /** @@ -26,35 +26,35 @@ export interface IViewService { * Check if we can skip the addView() for a workspace */ alreadyHaveView(workspace: IWorkspace): Promise; - createViewAddToWindow(workspace: IWorkspace, browserWindow: BrowserWindow, sharedWebPreferences: WebPreferences, windowName: WindowNames): Promise; - forEachView: (functionToRun: (view: BrowserView, workspaceID: string, windowName: WindowNames) => void) => void; + createViewAddToWindow(workspace: IWorkspace, browserWindow: BrowserWindow, sharedWebPreferences: WebPreferences, windowName: WindowNames): Promise; + forEachView: (functionToRun: (view: WebContentsView, workspaceID: string, windowName: WindowNames) => void) => void; /** * If menubar is open, we get menubar browser view, else we get main window browser view */ - getActiveBrowserView: () => Promise; + getActiveBrowserView: () => Promise; /** * Get active workspace's main window and menubar browser view. */ - getActiveBrowserViews: () => Promise>; + getActiveBrowserViews: () => Promise>; getSharedWebPreferences(workspace: IWorkspace): Promise; - getView: (workspaceID: string, windowName: WindowNames) => BrowserView | undefined; + getView: (workspaceID: string, windowName: WindowNames) => WebContentsView | undefined; getViewCount(): Promise; getViewCurrentUrl(workspaceID?: string): Promise; /** * Move the view to the side to hide it. * This won't destroy view or remove it from the window, but if you add another view to the window now, this will be replaced safely. To completely remove the view, use `removeView`. */ - hideView(browserWindow: BrowserWindow): Promise; + hideView(browserWindow: BrowserWindow, windowName: WindowNames, idToDeactivate: string): Promise; initializeWorkspaceViewHandlersAndLoad( browserWindow: BrowserWindow, - view: BrowserView, + view: WebContentsView, configs: { sharedWebPreferences: WebPreferences; uri?: string; windowName: WindowNames; workspace: IWorkspace }, ): Promise; /** * Try catch loadUrl, other wise it will throw unhandled promise rejection Error: ERR_CONNECTION_REFUSED (-102) loading 'http://localhost:5212/ * We will set `didFailLoadErrorMessage`, it will set didFailLoadErrorMessage, and we throw actuarial error after that */ - loadUrlForView(workspace: IWorkspace, view: BrowserView): Promise; + loadUrlForView(workspace: IWorkspace, view: WebContentsView): Promise; realignActiveView(browserWindow: BrowserWindow, activeId: string, windowName: WindowNames, isRetry?: boolean): Promise; reloadActiveBrowserView: () => Promise; reloadViewsWebContents(workspaceID?: string | undefined): Promise; diff --git a/src/services/view/setupIpcServerRoutesHandlers.ts b/src/services/view/setupIpcServerRoutesHandlers.ts index 05956848..622ad9d1 100644 --- a/src/services/view/setupIpcServerRoutesHandlers.ts +++ b/src/services/view/setupIpcServerRoutesHandlers.ts @@ -1,4 +1,4 @@ -import { BrowserView } from 'electron'; +import { WebContentsView } from 'electron'; import { IAuthenticationService } from '@services/auth/interface'; import { container } from '@services/container'; @@ -8,7 +8,7 @@ import { IWikiService } from '@services/wiki/interface'; import { IWorkspaceService } from '@services/workspaces/interface'; import type { ITiddlerFields } from 'tiddlywiki'; -export function setupIpcServerRoutesHandlers(view: BrowserView, workspaceID: string) { +export function setupIpcServerRoutesHandlers(view: WebContentsView, workspaceID: string) { const workspaceService = container.get(serviceIdentifier.Workspace); const authService = container.get(serviceIdentifier.Authentication); const wikiService = container.get(serviceIdentifier.Wiki); diff --git a/src/services/view/setupViewEventHandlers.ts b/src/services/view/setupViewEventHandlers.ts index c1ecf9e4..cc1324cc 100644 --- a/src/services/view/setupViewEventHandlers.ts +++ b/src/services/view/setupViewEventHandlers.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-misused-promises */ /* eslint-disable unicorn/consistent-destructuring */ -import { app, BrowserView, BrowserWindow, BrowserWindowConstructorOptions, nativeImage, shell } from 'electron'; +import { app, BrowserWindow, BrowserWindowConstructorOptions, nativeImage, shell, WebContentsView } from 'electron'; import fsExtra from 'fs-extra'; import { throttle } from 'lodash'; import path from 'path'; @@ -40,11 +40,11 @@ export interface IViewMeta { * Bind workspace related event handler to view.webContent */ export default function setupViewEventHandlers( - view: BrowserView, + view: WebContentsView, browserWindow: BrowserWindow, { workspace, sharedWebPreferences, loadInitialUrlWithCatch, windowName }: IViewContext, ): void { - // metadata and state about current BrowserView + // metadata and state about current WebContentsView const viewMeta: IViewMeta = { forceNewWindow: false, }; @@ -58,7 +58,7 @@ export default function setupViewEventHandlers( view.webContents.on('did-start-loading', async () => { const workspaceObject = await workspaceService.get(workspace.id); // this event might be triggered - // even after the workspace obj and BrowserView + // even after the workspace obj and WebContentsView // are destroyed. See https://github.com/atomery/webcatalog/issues/836 if (workspaceObject === undefined) { return; @@ -145,7 +145,7 @@ export default function setupViewEventHandlers( workspaceService.workspaceDidFailLoad(workspace.id), ]); // this event might be triggered - // even after the workspace obj and BrowserView + // even after the workspace obj and WebContentsView // are destroyed. See https://github.com/atomery/webcatalog/issues/836 if (workspaceObject === undefined) { return; @@ -182,7 +182,7 @@ export default function setupViewEventHandlers( logger.debug(`did-navigate called ${url}`); const workspaceObject = await workspaceService.get(workspace.id); // this event might be triggered - // even after the workspace obj and BrowserView + // even after the workspace obj and WebContentsView // are destroyed. See https://github.com/atomery/webcatalog/issues/836 if (workspaceObject === undefined) { return; @@ -197,7 +197,7 @@ export default function setupViewEventHandlers( await workspaceViewService.updateLastUrl(workspace.id, view); const workspaceObject = await workspaceService.get(workspace.id); // this event might be triggered - // even after the workspace obj and BrowserView + // even after the workspace obj and WebContentsView // are destroyed. See https://github.com/atomery/webcatalog/issues/836 if (workspaceObject === undefined) { return; @@ -210,7 +210,7 @@ export default function setupViewEventHandlers( view.webContents.on('page-title-updated', async (_event, title) => { const workspaceObject = await workspaceService.get(workspace.id); // this event might be triggered - // even after the workspace obj and BrowserView + // even after the workspace obj and WebContentsView // are destroyed. See https://github.com/atomery/webcatalog/issues/836 if (workspaceObject === undefined) { return; diff --git a/src/services/view/setupViewFileProtocol.ts b/src/services/view/setupViewFileProtocol.ts index e8ff14a7..28ca90eb 100644 --- a/src/services/view/setupViewFileProtocol.ts +++ b/src/services/view/setupViewFileProtocol.ts @@ -5,7 +5,7 @@ import { logger } from '@services/libs/log'; import { INativeService } from '@services/native/interface'; import serviceIdentifier from '@services/serviceIdentifier'; import type { IWikiService } from '@services/wiki/interface'; -import { BrowserView, shell } from 'electron'; +import { shell, WebContentsView } from 'electron'; import fs from 'fs-extra'; import type { INewWindowContext } from './handleNewWindow'; import { INewWindowAction } from './interface'; @@ -54,7 +54,7 @@ export function handleOpenFileExternalLink(nextUrl: string, newWindowContext: IN /** * Handle file protocol in webview to request file content and show in the view. */ -export function handleViewFileContentLoading(view: BrowserView) { +export function handleViewFileContentLoading(view: WebContentsView) { view.webContents.session.webRequest.onBeforeRequest((details, callback) => { if (details.url.startsWith('file://')) { handleFileLink(details, callback); diff --git a/src/services/view/setupViewSession.ts b/src/services/view/setupViewSession.ts index 93047427..e9d9fcce 100644 --- a/src/services/view/setupViewSession.ts +++ b/src/services/view/setupViewSession.ts @@ -10,7 +10,7 @@ export function setupViewSession(workspace: IWorkspace, preferences: IPreference // configure session, proxy & ad blocker const partitionId = shareWorkspaceBrowsingData ? 'persist:shared' : `persist:${workspace.id}`; - // prepare configs for start a BrowserView that loads wiki's web content + // prepare configs for start a WebContentsView that loads wiki's web content // session const sessionOfView = session.fromPartition(partitionId); // spellchecker diff --git a/src/services/windows/WindowProperties.ts b/src/services/windows/WindowProperties.ts index bda0ef1f..28cbf6ab 100644 --- a/src/services/windows/WindowProperties.ts +++ b/src/services/windows/WindowProperties.ts @@ -112,7 +112,7 @@ export type IPossibleWindowMeta { const menuService = container.get(serviceIdentifier.MenuService); const windowService = container.get(serviceIdentifier.Window); + const viewService = container.get(serviceIdentifier.View); // setImage after Tray instance is created to avoid // "Segmentation fault (core dumped)" bug on Linux @@ -40,7 +42,7 @@ export async function handleAttachToMenuBar(windowConfig: BrowserWindowConstruct menuBar.on('after-create-window', () => { if (menuBar.window !== undefined) { - menuBar.window.on('focus', () => { + menuBar.window.on('focus', async () => { logger.debug('restore window position'); if (windowWithBrowserViewState === undefined) { logger.debug('windowWithBrowserViewState is undefined for menuBar'); @@ -58,10 +60,8 @@ export async function handleAttachToMenuBar(windowConfig: BrowserWindowConstruct } } } - const view = menuBar.window?.getBrowserView(); - if (view?.webContents !== undefined) { - view.webContents.focus(); - } + const view = await viewService.getActiveBrowserView(); + view?.webContents?.focus?.(); }); menuBar.window.removeAllListeners('close'); menuBar.window.on('close', (event) => { diff --git a/src/services/windows/handleCreateBasicWindow.ts b/src/services/windows/handleCreateBasicWindow.ts index 75295d90..e3602394 100644 --- a/src/services/windows/handleCreateBasicWindow.ts +++ b/src/services/windows/handleCreateBasicWindow.ts @@ -54,7 +54,7 @@ export async function handleCreateBasicWindow( }); } await updateWindowBackground(newWindow); - // Not loading main window (like sidebar and background) here. Only load wiki in browserView in the secondary window. Secondary window will use a BrowserView to load content, and without main content like sidebar and Guide. + // Not loading main window (like sidebar and background) here. Only load wiki in browserView in the secondary window. Secondary window will use a WebContentsView to load content, and without main content like sidebar and Guide. const isWindowToLoadURL = windowName !== WindowNames.secondary; if (isWindowToLoadURL) { // This loading will wait for a while diff --git a/src/services/windows/index.ts b/src/services/windows/index.ts index b1e188cc..a083afcf 100644 --- a/src/services/windows/index.ts +++ b/src/services/windows/index.ts @@ -22,13 +22,14 @@ import { getDefaultTidGiUrl } from '@/constants/urls'; import { isMac } from '@/helpers/system'; import { lazyInject } from '@services/container'; import getViewBounds from '@services/libs/getViewBounds'; +import { logger } from '@services/libs/log'; import { IThemeService } from '@services/theme/interface'; +import { IViewService } from '@services/view/interface'; import { handleAttachToMenuBar } from './handleAttachToMenuBar'; import { handleCreateBasicWindow } from './handleCreateBasicWindow'; import { IWindowOpenConfig, IWindowService } from './interface'; import { registerBrowserViewWindowListeners } from './registerBrowserViewWindowListeners'; import { registerMenu } from './registerMenu'; -import { logger } from '@services/libs/log'; @injectable() export class Window implements IWindowService { @@ -52,15 +53,17 @@ export class Window implements IWindowService { @lazyInject(serviceIdentifier.ThemeService) private readonly themeService!: IThemeService; + @lazyInject(serviceIdentifier.View) + private readonly viewService!: IViewService; + constructor() { setTimeout(() => { void registerMenu(); }, DELAY_MENU_REGISTER); } - public async findInPage(text: string, forward?: boolean, windowName: WindowNames = WindowNames.main): Promise { - const mainWindow = this.get(windowName); - const contents = mainWindow?.getBrowserView()?.webContents; + public async findInPage(text: string, forward?: boolean): Promise { + const contents = (await this.viewService.getActiveBrowserView())?.webContents; if (contents !== undefined) { contents.findInPage(text, { forward, @@ -70,7 +73,7 @@ export class Window implements IWindowService { public async stopFindInPage(close?: boolean, windowName: WindowNames = WindowNames.main): Promise { const mainWindow = this.get(windowName); - const view = mainWindow?.getBrowserView(); + const view = await this.viewService.getActiveBrowserView(); // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (view) { const contents = view.webContents; @@ -230,9 +233,9 @@ export class Window implements IWindowService { newWindow = await handleCreateBasicWindow(windowName, windowConfig, meta, config); if (isWindowWithBrowserView) { registerBrowserViewWindowListeners(newWindow, windowName); - // calling this to redundantly setBounds BrowserView + // calling this to redundantly setBounds WebContentsView // after the UI is fully loaded - // if not, BrowserView mouseover event won't work correctly + // if not, WebContentsView mouseover event won't work correctly // https://github.com/atomery/webcatalog/issues/812 // await this.workspaceViewService.realignActiveWorkspace(); } else { @@ -281,20 +284,18 @@ export class Window implements IWindowService { }); }; - public async goHome(windowName: WindowNames = WindowNames.main): Promise { - const win = this.get(windowName); - const contents = win?.getBrowserView()?.webContents; + public async goHome(): Promise { + const contents = (await this.viewService.getActiveBrowserView())?.webContents; const activeWorkspace = await this.workspaceService.getActiveWorkspace(); - if (contents !== undefined && activeWorkspace !== undefined && win !== undefined) { + if (contents !== undefined && activeWorkspace !== undefined) { await contents.loadURL(getDefaultTidGiUrl(activeWorkspace.id)); contents.send(WindowChannel.updateCanGoBack, contents.canGoBack()); contents.send(WindowChannel.updateCanGoForward, contents.canGoForward()); } } - public async goBack(windowName: WindowNames = WindowNames.main): Promise { - const win = this.get(windowName); - const contents = win?.getBrowserView()?.webContents; + public async goBack(): Promise { + const contents = (await this.viewService.getActiveBrowserView())?.webContents; if (contents?.canGoBack() === true) { contents.goBack(); contents.send(WindowChannel.updateCanGoBack, contents.canGoBack()); @@ -302,9 +303,8 @@ export class Window implements IWindowService { } } - public async goForward(windowName: WindowNames = WindowNames.main): Promise { - const win = this.get(windowName); - const contents = win?.getBrowserView()?.webContents; + public async goForward(): Promise { + const contents = (await this.viewService.getActiveBrowserView())?.webContents; if (contents?.canGoForward() === true) { contents.goForward(); contents.send(WindowChannel.updateCanGoBack, contents.canGoBack()); @@ -315,7 +315,6 @@ export class Window implements IWindowService { public async reload(windowName: WindowNames = WindowNames.main): Promise { const win = this.get(windowName); if (win !== undefined) { - win.getBrowserView()?.webContents?.reload?.(); await this.pushWindowMetaToWindow(win, this.windowMeta[windowName]); } } @@ -328,9 +327,9 @@ export class Window implements IWindowService { } } - public async clearStorageData(windowName: WindowNames = WindowNames.main): Promise { - const win = this.get(windowName); - const session = win?.getBrowserView()?.webContents?.session; + public async clearStorageData(workspaceID: string, windowName: WindowNames = WindowNames.main): Promise { + const view = this.viewService.getView(workspaceID, windowName); + const session = view?.webContents?.session; if (session !== undefined) { await session.clearStorageData(); await session.clearAuthCache(); diff --git a/src/services/windows/interface.ts b/src/services/windows/interface.ts index cde0e865..9ab1e718 100644 --- a/src/services/windows/interface.ts +++ b/src/services/windows/interface.ts @@ -18,22 +18,22 @@ export interface IWindowOpenConfig { * Create and manage window open and destroy, you can get all opened electron window instance here */ export interface IWindowService { - clearStorageData(windowName?: WindowNames): Promise; + clearStorageData(workspaceID: string, windowName?: WindowNames): Promise; /** cleanup all window references for GC */ clearWindowsReference(): Promise; /** - * Completely close a window, destroy its all state and BrowserView. Need more time to restore. Use `hide` if you want to hide it temporarily. + * Completely close a window, destroy its all state and WebContentsView. Need more time to restore. Use `hide` if you want to hide it temporarily. */ close(windowName: WindowNames): Promise; - findInPage(text: string, forward?: boolean | undefined, windowName?: WindowNames): Promise; + findInPage(text: string, forward?: boolean | undefined): Promise; /** get window, this should not be called in renderer side */ get(windowName: WindowNames): BrowserWindow | undefined; getWindowMeta(windowName: N): Promise; - goBack(windowName?: WindowNames): Promise; - goForward(windowName?: WindowNames): Promise; - goHome(windowName?: WindowNames): Promise; + goBack(): Promise; + goForward(): Promise; + goHome(): Promise; /** - * Temporarily hide window, it will not be destroyed, and can be shown again very quick, with BrowserView restored immediately. + * Temporarily hide window, it will not be destroyed, and can be shown again very quick, with WebContentsView restored immediately. */ hide(windowName: WindowNames): Promise; isFullScreen(windowName?: WindowNames): Promise; diff --git a/src/services/windows/registerBrowserViewWindowListeners.ts b/src/services/windows/registerBrowserViewWindowListeners.ts index 2e9a4479..7558d9ee 100644 --- a/src/services/windows/registerBrowserViewWindowListeners.ts +++ b/src/services/windows/registerBrowserViewWindowListeners.ts @@ -1,8 +1,11 @@ import { container } from '@services/container'; +import { logger } from '@services/libs/log'; import { IPreferenceService } from '@services/preferences/interface'; import serviceIdentifier from '@services/serviceIdentifier'; +import { IViewService } from '@services/view/interface'; import { IWorkspaceViewService } from '@services/workspacesView/interface'; import { BrowserWindow } from 'electron'; +import debounce from 'lodash/debounce'; import { IWindowService } from './interface'; import { WindowNames } from './WindowProperties'; @@ -10,13 +13,14 @@ export function registerBrowserViewWindowListeners(newWindow: BrowserWindow, win const preferenceService = container.get(serviceIdentifier.Preference); const windowService = container.get(serviceIdentifier.Window); const workspaceViewService = container.get(serviceIdentifier.WorkspaceView); + const viewService = container.get(serviceIdentifier.View); // Enable swipe to navigate void preferenceService.get('swipeToNavigate').then((swipeToNavigate) => { if (swipeToNavigate) { if (newWindow === undefined) return; - newWindow.on('swipe', (_event, direction) => { - const view = newWindow?.getBrowserView?.(); + newWindow.on('swipe', async (_event, direction) => { + const view = await viewService.getActiveBrowserView(); // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (view) { if (direction === 'left') { @@ -40,9 +44,9 @@ export function registerBrowserViewWindowListeners(newWindow: BrowserWindow, win } }); - newWindow.on('focus', () => { + newWindow.on('focus', async () => { if (windowName !== WindowNames.main || newWindow === undefined) return; - const view = newWindow?.getBrowserView?.(); + const view = await viewService.getActiveBrowserView(); // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions view?.webContents?.focus?.(); }); @@ -57,4 +61,10 @@ export function registerBrowserViewWindowListeners(newWindow: BrowserWindow, win newWindow?.webContents?.send?.('is-fullscreen-updated', false); await workspaceViewService.realignActiveWorkspace(); }); + const debouncedOnResize = debounce(() => { + logger.debug('debouncedOnResize'); + if (windowName !== WindowNames.main || newWindow === undefined) return; + void workspaceViewService.realignActiveWorkspace(); + }, 250); + newWindow.on('resize', debouncedOnResize); } diff --git a/src/services/windows/registerMenu.ts b/src/services/windows/registerMenu.ts index df044c23..554293fe 100644 --- a/src/services/windows/registerMenu.ts +++ b/src/services/windows/registerMenu.ts @@ -1,20 +1,21 @@ -import { MetaDataChannel, WindowChannel } from '@/constants/channels'; +import { WindowChannel } from '@/constants/channels'; import { isMac } from '@/helpers/system'; import { container } from '@services/container'; -import getFromRenderer from '@services/libs/getFromRenderer'; import getViewBounds from '@services/libs/getViewBounds'; import { i18n } from '@services/libs/i18n'; import { IMenuService } from '@services/menu/interface'; import { IPreferenceService } from '@services/preferences/interface'; import serviceIdentifier from '@services/serviceIdentifier'; +import { IViewService } from '@services/view/interface'; import { IWorkspaceService } from '@services/workspaces/interface'; import { ipcMain } from 'electron'; import { IWindowService } from './interface'; -import { IBrowserViewMetaData, WindowNames } from './WindowProperties'; +import { WindowNames } from './WindowProperties'; export async function registerMenu(): Promise { const menuService = container.get(serviceIdentifier.MenuService); const windowService = container.get(serviceIdentifier.Window); + const viewService = container.get(serviceIdentifier.View); const workspaceService = container.get(serviceIdentifier.Workspace); const preferenceService = container.get(serviceIdentifier.Preference); @@ -47,7 +48,7 @@ export async function registerMenu(): Promise { mainWindow.webContents.focus(); mainWindow.webContents.send(WindowChannel.openFindInPage); const contentSize = mainWindow.getContentSize(); - const view = mainWindow.getBrowserView(); + const view = await viewService.getActiveBrowserView(); view?.setBounds(await getViewBounds(contentSize as [number, number], { findInPage: true })); } }, @@ -102,9 +103,7 @@ export async function registerMenu(): Promise { // if back is called in popup window // navigate in the popup window instead if (browserWindow !== undefined) { - // TODO: test if we really can get this isPopup value - const { isPopup = false } = await getFromRenderer(MetaDataChannel.getViewMetaData, browserWindow); - await windowService.goBack(isPopup ? WindowNames.menuBar : WindowNames.main); + await windowService.goBack(); } ipcMain.emit('request-go-back'); }, @@ -117,8 +116,10 @@ export async function registerMenu(): Promise { // if back is called in popup window // navigate in the popup window instead if (browserWindow !== undefined) { - const { isPopup = false } = await getFromRenderer(MetaDataChannel.getViewMetaData, browserWindow); - await windowService.goForward(isPopup ? WindowNames.menuBar : WindowNames.main); + // TODO: test if we really can get this isPopup value, and it works for help page popup and menubar window + // const { isPopup = false } = await getFromRenderer(MetaDataChannel.getViewMetaData, browserWindow); + // const windowName = isPopup ? WindowNames.menuBar : WindowNames.main + await windowService.goForward(); } ipcMain.emit('request-go-forward'); }, diff --git a/src/services/workspacesView/index.ts b/src/services/workspacesView/index.ts index 5ed7f843..30672461 100644 --- a/src/services/workspacesView/index.ts +++ b/src/services/workspacesView/index.ts @@ -155,7 +155,7 @@ export class WorkspaceView implements IWorkspaceViewService { }; const addViewWhenInitializeWorkspaceView = async (): Promise => { - // adding BrowserView for each workspace + // adding WebContentsView for each workspace // skip view initialize if this is a sub wiki if (workspace.isSubWiki) { return; @@ -219,7 +219,7 @@ export class WorkspaceView implements IWorkspaceViewService { public async updateLastUrl( workspaceID: string, - view: Electron.CrossProcessExports.BrowserView | undefined = this.viewService.getView(workspaceID, WindowNames.main), + view: Electron.CrossProcessExports.WebContentsView | undefined = this.viewService.getView(workspaceID, WindowNames.main), ): Promise { if (view?.webContents) { const currentUrl = view.webContents.getURL(); @@ -323,9 +323,12 @@ export class WorkspaceView implements IWorkspaceViewService { logger.error(`Error while setActiveWorkspaceView(): ${(error as Error).message}`, error); throw error; } - // if we are switching to a new workspace, we hibernate old view, and activate new view - if (oldActiveWorkspace !== undefined && oldActiveWorkspace.id !== nextWorkspaceID && oldActiveWorkspace.hibernateWhenUnused) { - await this.hibernateWorkspaceView(oldActiveWorkspace.id); + // if we are switching to a new workspace, we hide and/or hibernate old view, and activate new view + if (oldActiveWorkspace !== undefined && oldActiveWorkspace.id !== nextWorkspaceID) { + await this.hideWorkspaceView(oldActiveWorkspace.id); + if (oldActiveWorkspace.hibernateWhenUnused) { + await this.hibernateWorkspaceView(oldActiveWorkspace.id); + } } } @@ -341,7 +344,7 @@ export class WorkspaceView implements IWorkspaceViewService { return; } try { - await this.hideWorkspaceView(); + await this.hideWorkspaceView(activeWorkspace.id); } catch (error) { logger.error(`Error while setActiveWorkspaceView(): ${(error as Error).message}`, error); throw error; @@ -448,16 +451,16 @@ export class WorkspaceView implements IWorkspaceViewService { const activeWorkspace = await this.workspaceService.getActiveWorkspace(); const activeWorkspaceID = id ?? activeWorkspace?.id; if (mainWindow !== undefined && activeWorkspaceID !== undefined) { - const browserView = mainWindow.getBrowserView(); - if (browserView?.webContents) { - browserView.webContents.focus(); - await browserView.webContents.loadURL(url); + const view = this.viewService.getView(activeWorkspaceID, WindowNames.main); + if (view?.webContents) { + view.webContents.focus(); + await view.webContents.loadURL(url); } } } /** - * Seems this is for relocating BrowserView in the electron window + * Seems this is for relocating WebContentsView in the electron window */ public async realignActiveWorkspace(id?: string): Promise { // this function only call browserView.setBounds @@ -484,15 +487,9 @@ export class WorkspaceView implements IWorkspaceViewService { } const mainWindow = this.windowService.get(WindowNames.main); const menuBarWindow = this.windowService.get(WindowNames.menuBar); - const mainBrowserViewWebContent = mainWindow?.getBrowserView()?.webContents; - const menuBarBrowserViewWebContent = menuBarWindow?.getBrowserView()?.webContents; /* eslint-disable @typescript-eslint/strict-boolean-expressions */ logger.info( - `realignActiveWorkspaceView: id ${workspaceToRealign?.id ?? 'undefined'} mainWindow: ${String(!!mainBrowserViewWebContent)} menuBarWindow: ${ - String( - !!menuBarBrowserViewWebContent, - ) - }`, + `realignActiveWorkspaceView: id ${workspaceToRealign?.id ?? 'undefined'}`, ); if (workspaceToRealign === undefined) { logger.warn('realignActiveWorkspaceView: no active workspace'); @@ -503,38 +500,36 @@ export class WorkspaceView implements IWorkspaceViewService { return; } const tasks = []; - if (mainBrowserViewWebContent) { + if (mainWindow === undefined) { + logger.warn(`realignActiveWorkspaceView: no mainBrowserViewWebContent, skip main window for ${workspaceToRealign.id}.`); + } else { tasks.push(this.viewService.realignActiveView(mainWindow, workspaceToRealign.id, WindowNames.main)); logger.debug(`realignActiveWorkspaceView: realign main window for ${workspaceToRealign.id}.`); - } else { - logger.warn(`realignActiveWorkspaceView: no mainBrowserViewWebContent, skip main window for ${workspaceToRealign.id}.`); } - if (menuBarBrowserViewWebContent) { + if (menuBarWindow === undefined) { + logger.info(`realignActiveWorkspaceView: no menuBarBrowserViewWebContent, skip menu bar window for ${workspaceToRealign.id}.`); + } else { logger.debug(`realignActiveWorkspaceView: realign menu bar window for ${workspaceToRealign.id}.`); tasks.push(this.viewService.realignActiveView(menuBarWindow, workspaceToRealign.id, WindowNames.menuBar)); - } else { - logger.info(`realignActiveWorkspaceView: no menuBarBrowserViewWebContent, skip menu bar window for ${workspaceToRealign.id}.`); } await Promise.all(tasks); } - private async hideWorkspaceView(): Promise { + private async hideWorkspaceView(idToDeactivate: string): Promise { const mainWindow = this.windowService.get(WindowNames.main); const menuBarWindow = this.windowService.get(WindowNames.menuBar); - const mainBrowserViewWebContent = mainWindow?.getBrowserView()?.webContents; - const menuBarBrowserViewWebContent = menuBarWindow?.getBrowserView()?.webContents; const tasks = []; - if (mainBrowserViewWebContent) { + if (mainWindow === undefined) { + logger.warn(`hideWorkspaceView: no mainBrowserWindow, skip main window browserView.`); + } else { logger.info(`hideWorkspaceView: hide main window browserView.`); - tasks.push(this.viewService.hideView(mainWindow)); - } else { - logger.warn(`hideWorkspaceView: no mainBrowserViewWebContent, skip main window browserView.`); + tasks.push(this.viewService.hideView(mainWindow, WindowNames.main, idToDeactivate)); } - if (menuBarBrowserViewWebContent) { - logger.info(`hideWorkspaceView: hide menu bar window browserView.`); - tasks.push(this.viewService.hideView(menuBarWindow)); + if (menuBarWindow === undefined) { + logger.debug(`hideWorkspaceView: no menuBarBrowserWindow, skip menu bar window browserView.`); } else { - logger.debug(`hideWorkspaceView: no menuBarBrowserViewWebContent, skip menu bar window browserView.`); + logger.info(`hideWorkspaceView: hide menu bar window browserView.`); + tasks.push(this.viewService.hideView(menuBarWindow, WindowNames.menuBar, idToDeactivate)); } await Promise.all(tasks); logger.info(`hideWorkspaceView: done.`); diff --git a/src/services/workspacesView/interface.ts b/src/services/workspacesView/interface.ts index b57d3e49..000a53b1 100644 --- a/src/services/workspacesView/interface.ts +++ b/src/services/workspacesView/interface.ts @@ -24,7 +24,7 @@ export interface IInitializeWorkspaceOptions { export interface IWorkspaceViewService { addViewForAllBrowserViews(workspace: IWorkspace): Promise; /** - * Hide BrowserView, so page below it will show up. + * Hide WebContentsView, so page below it will show up. */ clearActiveWorkspaceView(): Promise; clearBrowsingData(): Promise; @@ -75,7 +75,7 @@ export interface IWorkspaceViewService { setWorkspaceView(workspaceID: string, workspaceOptions: IWorkspace): Promise; setWorkspaceViews(workspaces: Record): Promise; /** get view's current url, store into the workspace. Can provide a designated view to operate */ - updateLastUrl(workspaceID: string, view?: Electron.CrossProcessExports.BrowserView | undefined): Promise; + updateLastUrl(workspaceID: string, view?: Electron.CrossProcessExports.WebContentsView | undefined): Promise; wakeUpWorkspaceView(workspaceID: string): Promise; } export const WorkspaceViewServiceIPCDescriptor = { diff --git a/src/services/workspacesView/registerMenu.ts b/src/services/workspacesView/registerMenu.ts index 68a544a1..4bb2747c 100644 --- a/src/services/workspacesView/registerMenu.ts +++ b/src/services/workspacesView/registerMenu.ts @@ -112,8 +112,8 @@ export async function registerMenu(): Promise { return; } } - const mainWindow = windowService.get(WindowNames.main); - const url = mainWindow?.getBrowserView()?.webContents?.getURL(); + const view = await viewService.getActiveBrowserView(); + const url = view?.webContents?.getURL(); if (typeof url === 'string') { clipboard.writeText(url); }