From 029654f45ee6201ec689c6a732dadf3eed530fae Mon Sep 17 00:00:00 2001 From: pengx17 Date: Tue, 5 Nov 2024 11:46:03 +0000 Subject: [PATCH] fix(electron): share page in electron issues (#8703) fix AF-1592 fix AF-1612 --- .../frontend/apps/electron/renderer/app.tsx | 6 + .../apps/electron/renderer/shell/app.tsx | 6 + .../frontend/apps/electron/src/main/index.ts | 4 +- .../apps/electron/src/main/ui/handlers.ts | 9 +- .../src/main/windows-manager/tab-views.ts | 55 +++++++--- .../components/share-page-not-found-error.tsx | 1 + .../pages/workspace/share/share-page.tsx | 103 ++++++++++-------- .../core/src/mobile/pages/workspace/index.tsx | 2 +- .../pages/workspace/workbench-root.tsx | 0 .../src/modules/workbench/view/view-meta.tsx | 24 +++- 10 files changed, 141 insertions(+), 69 deletions(-) rename packages/frontend/core/src/{desktop => mobile}/pages/workspace/workbench-root.tsx (100%) diff --git a/packages/frontend/apps/electron/renderer/app.tsx b/packages/frontend/apps/electron/renderer/app.tsx index 8c1b077352..a9afa1d32a 100644 --- a/packages/frontend/apps/electron/renderer/app.tsx +++ b/packages/frontend/apps/electron/renderer/app.tsx @@ -116,6 +116,12 @@ window.addEventListener('focus', () => { frameworkProvider.get(LifecycleService).applicationFocus(); }); frameworkProvider.get(LifecycleService).applicationStart(); +window.addEventListener('unload', () => { + frameworkProvider + .get(DesktopApiService) + .api.handler.ui.pingAppLayoutReady(false) + .catch(console.error); +}); events?.applicationMenu.openAboutPageInSettingModal(() => frameworkProvider.get(GlobalDialogService).open('setting', { diff --git a/packages/frontend/apps/electron/renderer/shell/app.tsx b/packages/frontend/apps/electron/renderer/shell/app.tsx index 5ceb79b082..6c0767a0d7 100644 --- a/packages/frontend/apps/electron/renderer/shell/app.tsx +++ b/packages/frontend/apps/electron/renderer/shell/app.tsx @@ -1,4 +1,5 @@ import { useAppSettingHelper } from '@affine/core/components/hooks/affine/use-app-setting-helper'; +import { WindowsAppControls } from '@affine/core/components/pure/header/windows-app-controls'; import { ThemeProvider } from '@affine/core/components/theme-provider'; import { configureAppSidebarModule } from '@affine/core/modules/app-sidebar'; import { ShellAppSidebarFallback } from '@affine/core/modules/app-sidebar/views'; @@ -43,6 +44,11 @@ export function App() {
+ {environment.isWindows && ( +
+ +
+ )} diff --git a/packages/frontend/apps/electron/src/main/index.ts b/packages/frontend/apps/electron/src/main/index.ts index 7d6dfe8940..a4b691bd02 100644 --- a/packages/frontend/apps/electron/src/main/index.ts +++ b/packages/frontend/apps/electron/src/main/index.ts @@ -47,7 +47,9 @@ if (process.env.SKIP_ONBOARDING) { */ const isSingleInstance = app.requestSingleInstanceLock(); if (!isSingleInstance) { - logger.info('Another instance is running, exiting...'); + logger.info( + 'Another instance is running or responding deep link, exiting...' + ); app.quit(); process.exit(0); } diff --git a/packages/frontend/apps/electron/src/main/ui/handlers.ts b/packages/frontend/apps/electron/src/main/ui/handlers.ts index 22ec4bee8f..903546584d 100644 --- a/packages/frontend/apps/electron/src/main/ui/handlers.ts +++ b/packages/frontend/apps/electron/src/main/ui/handlers.ts @@ -21,11 +21,13 @@ import { pingAppLayoutReady, showDevTools, showTab, + updateActiveViewMeta, updateWorkbenchMeta, updateWorkbenchViewMeta, } from '../windows-manager'; import { showTabContextMenu } from '../windows-manager/context-menu'; import { getOrCreateCustomThemeWindow } from '../windows-manager/custom-theme-window'; +import type { WorkbenchViewMeta } from '../windows-manager/tab-views-meta-schema'; import { getChallengeResponse } from './challenge'; import { uiSubjects } from './subject'; @@ -173,6 +175,9 @@ export const uiHandlers = { getTabViewsMeta: async () => { return getTabViewsMeta(); }, + updateActiveViewMeta: async (e, meta: Partial) => { + return updateActiveViewMeta(e.sender, meta); + }, getTabsStatus: async () => { return getTabsStatus(); }, @@ -197,8 +202,8 @@ export const uiHandlers = { uiSubjects.onToggleRightSidebar$.next(tabId); } }, - pingAppLayoutReady: async e => { - pingAppLayoutReady(e.sender); + pingAppLayoutReady: async (e, ready = true) => { + pingAppLayoutReady(e.sender, ready); }, showDevTools: async (_, ...args: Parameters) => { return showDevTools(...args); diff --git a/packages/frontend/apps/electron/src/main/windows-manager/tab-views.ts b/packages/frontend/apps/electron/src/main/windows-manager/tab-views.ts index 59aaa49f3d..7c5b02f7ad 100644 --- a/packages/frontend/apps/electron/src/main/windows-manager/tab-views.ts +++ b/packages/frontend/apps/electron/src/main/windows-manager/tab-views.ts @@ -270,7 +270,14 @@ export class WebContentViewsManager { } }; - getViewIdFromWebContentsId = (id: number) => { + setTabUIUnready = (tabId: string) => { + this.appTabsUIReady$.next( + new Set([...this.appTabsUIReady$.value].filter(key => key !== tabId)) + ); + this.reorderViews(); + }; + + getWorkbenchIdFromWebContentsId = (id: number) => { return Array.from(this.tabViewsMap.entries()).find( ([, view]) => view.webContents.id === id )?.[0]; @@ -303,7 +310,7 @@ export class WebContentViewsManager { updateWorkbenchViewMeta = ( workbenchId: string, - viewId: string, + viewId: string | number, patch: Partial ) => { const workbench = this.tabViewsMeta.workbenches.find( @@ -313,7 +320,10 @@ export class WebContentViewsManager { return; } const views = workbench.views; - const viewIndex = views.findIndex(v => v.id === viewId); + const viewIndex = + typeof viewId === 'string' + ? views.findIndex(v => v.id === viewId) + : viewId; if (viewIndex === -1) { return; } @@ -821,12 +831,6 @@ export class WebContentViewsManager { view.webContents.on('did-finish-load', () => { unsub = helperProcessManager.connectRenderer(view.webContents); }); - view.webContents.on('will-navigate', () => { - // means the view is reloaded - this.appTabsUIReady$.next( - new Set([...this.appTabsUIReady$.value].filter(key => key !== viewId)) - ); - }); } else { view.webContents.on('focus', () => { globalThis.setTimeout(() => { @@ -943,7 +947,7 @@ export const updateWorkbenchMeta = ( export const updateWorkbenchViewMeta = ( workbenchId: string, - viewId: string, + viewId: string | number, meta: Partial ) => { WebContentViewsManager.instance.updateWorkbenchViewMeta( @@ -956,6 +960,24 @@ export const updateWorkbenchViewMeta = ( export const getWorkbenchMeta = (id: string) => { return TabViewsMetaState.value.workbenches.find(w => w.id === id); }; + +export const updateActiveViewMeta = ( + wc: WebContents, + meta: Partial +) => { + const workbenchId = + WebContentViewsManager.instance.getWorkbenchIdFromWebContentsId(wc.id); + const workbench = workbenchId ? getWorkbenchMeta(workbenchId) : undefined; + + if (workbench && workbenchId) { + return WebContentViewsManager.instance.updateWorkbenchViewMeta( + workbenchId, + workbench.activeViewIndex, + meta + ); + } +}; + export const getTabViewsMeta = () => TabViewsMetaState.value; export const isActiveTab = (wc: WebContents) => { return ( @@ -1042,12 +1064,15 @@ export const showDevTools = (id?: string) => { .catch(console.error); }; -export const pingAppLayoutReady = (wc: WebContents) => { - const viewId = WebContentViewsManager.instance.getViewIdFromWebContentsId( - wc.id - ); +export const pingAppLayoutReady = (wc: WebContents, ready: boolean) => { + const viewId = + WebContentViewsManager.instance.getWorkbenchIdFromWebContentsId(wc.id); if (viewId) { - WebContentViewsManager.instance.setTabUIReady(viewId); + if (ready) { + WebContentViewsManager.instance.setTabUIReady(viewId); + } else { + WebContentViewsManager.instance.setTabUIUnready(viewId); + } } }; diff --git a/packages/frontend/core/src/components/share-page-not-found-error.tsx b/packages/frontend/core/src/components/share-page-not-found-error.tsx index 441e38cb32..5350581a53 100644 --- a/packages/frontend/core/src/components/share-page-not-found-error.tsx +++ b/packages/frontend/core/src/components/share-page-not-found-error.tsx @@ -23,6 +23,7 @@ export const SharePageNotFoundError = () => { left: '16px', fontSize: '24px', cursor: 'pointer', + color: 'inherit', }} > diff --git a/packages/frontend/core/src/desktop/pages/workspace/share/share-page.tsx b/packages/frontend/core/src/desktop/pages/workspace/share/share-page.tsx index fbb4398d34..69849f9c8b 100644 --- a/packages/frontend/core/src/desktop/pages/workspace/share/share-page.tsx +++ b/packages/frontend/core/src/desktop/pages/workspace/share/share-page.tsx @@ -19,6 +19,7 @@ import { } from '@affine/core/modules/editor'; import { PeekViewManagerModal } from '@affine/core/modules/peek-view'; import { ShareReaderService } from '@affine/core/modules/share-doc'; +import { ViewIcon, ViewTitle } from '@affine/core/modules/workbench'; import { CloudBlobStorage } from '@affine/core/modules/workspace-engine'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { useI18n } from '@affine/i18n'; @@ -42,7 +43,13 @@ import { WorkspacesService, } from '@toeverything/infra'; import clsx from 'clsx'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { + type ReactNode, + useCallback, + useEffect, + useMemo, + useState, +} from 'react'; import { useLocation } from 'react-router-dom'; import { PageNotFound } from '../../404'; @@ -100,17 +107,12 @@ export const SharePage = ({ shareReaderService.reader.loadShare({ workspaceId, docId }); }, [shareReaderService, docId, workspaceId]); + let element: ReactNode = null; + if (isLoading) { - return ; - } - - if (error) { - // TODO(@eyhn): show error details - return ; - } - - if (data) { - return ( + element = null; + } else if (data) { + element = ( ); + } else if (error) { + element = ; } else { - return ; + element = ; } + + return {element}; }; const SharePageInner = ({ @@ -230,7 +236,8 @@ const SharePageInner = ({ graphQLService, ]); - const pageTitle = useLiveData(page?.title$); + const t = useI18n(); + const pageTitle = useLiveData(page?.title$) ?? t['unnamed'](); const { jumpToPageBlock, openPage } = useNavigateHelper(); usePageDocumentTitle(pageTitle); @@ -276,43 +283,45 @@ const SharePageInner = ({ } return ( - - - - -
-
- - - - - {publishMode === 'page' ? : null} - - - - - -
+ + + + + +
+
+ + + + + {publishMode === 'page' && !BUILD_CONFIG.isElectron ? ( + + ) : null} + + + + + {!BUILD_CONFIG.isElectron && }
- - +
+
- +
); }; diff --git a/packages/frontend/core/src/mobile/pages/workspace/index.tsx b/packages/frontend/core/src/mobile/pages/workspace/index.tsx index c8a000b32b..9604627895 100644 --- a/packages/frontend/core/src/mobile/pages/workspace/index.tsx +++ b/packages/frontend/core/src/mobile/pages/workspace/index.tsx @@ -1,7 +1,6 @@ import { AffineErrorBoundary } from '@affine/core/components/affine/affine-error-boundary'; import { AffineErrorComponent } from '@affine/core/components/affine/affine-error-boundary/affine-error-fallback'; import { PageNotFound } from '@affine/core/desktop/pages/404'; -import { MobileWorkbenchRoot } from '@affine/core/desktop/pages/workspace/workbench-root'; import { workbenchRoutes } from '@affine/core/mobile/workbench-router'; import { useLiveData, @@ -23,6 +22,7 @@ import { } from 'react-router-dom'; import { WorkspaceLayout } from './layout'; +import { MobileWorkbenchRoot } from './workbench-root'; type Route = { Component: React.ComponentType }; /** diff --git a/packages/frontend/core/src/desktop/pages/workspace/workbench-root.tsx b/packages/frontend/core/src/mobile/pages/workspace/workbench-root.tsx similarity index 100% rename from packages/frontend/core/src/desktop/pages/workspace/workbench-root.tsx rename to packages/frontend/core/src/mobile/pages/workspace/workbench-root.tsx diff --git a/packages/frontend/core/src/modules/workbench/view/view-meta.tsx b/packages/frontend/core/src/modules/workbench/view/view-meta.tsx index 80762c3483..1de2e168a5 100644 --- a/packages/frontend/core/src/modules/workbench/view/view-meta.tsx +++ b/packages/frontend/core/src/modules/workbench/view/view-meta.tsx @@ -1,29 +1,47 @@ import { useServiceOptional } from '@toeverything/infra'; import { useEffect } from 'react'; +import { DesktopApiService } from '../../desktop-api'; import type { ViewIconName } from '../constants'; import { ViewService } from '../services/view'; export const ViewTitle = ({ title }: { title: string }) => { const view = useServiceOptional(ViewService)?.view; + const desktopApi = useServiceOptional(DesktopApiService); useEffect(() => { if (view) { view.setTitle(title); + } else if (desktopApi) { + desktopApi.handler.ui + .updateActiveViewMeta({ + title, + }) + .catch(e => { + console.error(e); + }); } - }, [title, view]); + }, [desktopApi, title, view]); return null; }; export const ViewIcon = ({ icon }: { icon: ViewIconName }) => { const view = useServiceOptional(ViewService)?.view; - + const desktopApi = useServiceOptional(DesktopApiService); useEffect(() => { if (view) { view.setIcon(icon); + } else if (desktopApi) { + desktopApi.handler.ui + .updateActiveViewMeta({ + iconName: icon, + }) + .catch(e => { + console.error(e); + }); } - }, [icon, view]); + }, [desktopApi, icon, view]); return null; };