fix(electron): share page in electron issues (#8703)

fix AF-1592
fix AF-1612
This commit is contained in:
pengx17
2024-11-05 11:46:03 +00:00
parent ef82b9d3e7
commit 029654f45e
10 changed files with 141 additions and 69 deletions

View File

@@ -116,6 +116,12 @@ window.addEventListener('focus', () => {
frameworkProvider.get(LifecycleService).applicationFocus(); frameworkProvider.get(LifecycleService).applicationFocus();
}); });
frameworkProvider.get(LifecycleService).applicationStart(); frameworkProvider.get(LifecycleService).applicationStart();
window.addEventListener('unload', () => {
frameworkProvider
.get(DesktopApiService)
.api.handler.ui.pingAppLayoutReady(false)
.catch(console.error);
});
events?.applicationMenu.openAboutPageInSettingModal(() => events?.applicationMenu.openAboutPageInSettingModal(() =>
frameworkProvider.get(GlobalDialogService).open('setting', { frameworkProvider.get(GlobalDialogService).open('setting', {

View File

@@ -1,4 +1,5 @@
import { useAppSettingHelper } from '@affine/core/components/hooks/affine/use-app-setting-helper'; 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 { ThemeProvider } from '@affine/core/components/theme-provider';
import { configureAppSidebarModule } from '@affine/core/modules/app-sidebar'; import { configureAppSidebarModule } from '@affine/core/modules/app-sidebar';
import { ShellAppSidebarFallback } from '@affine/core/modules/app-sidebar/views'; import { ShellAppSidebarFallback } from '@affine/core/modules/app-sidebar/views';
@@ -43,6 +44,11 @@ export function App() {
<div className={styles.body}> <div className={styles.body}>
<ShellAppSidebarFallback /> <ShellAppSidebarFallback />
</div> </div>
{environment.isWindows && (
<div style={{ position: 'fixed', right: 0, top: 0, zIndex: 5 }}>
<WindowsAppControls />
</div>
)}
</div> </div>
</I18nProvider> </I18nProvider>
</ThemeProvider> </ThemeProvider>

View File

@@ -47,7 +47,9 @@ if (process.env.SKIP_ONBOARDING) {
*/ */
const isSingleInstance = app.requestSingleInstanceLock(); const isSingleInstance = app.requestSingleInstanceLock();
if (!isSingleInstance) { if (!isSingleInstance) {
logger.info('Another instance is running, exiting...'); logger.info(
'Another instance is running or responding deep link, exiting...'
);
app.quit(); app.quit();
process.exit(0); process.exit(0);
} }

View File

@@ -21,11 +21,13 @@ import {
pingAppLayoutReady, pingAppLayoutReady,
showDevTools, showDevTools,
showTab, showTab,
updateActiveViewMeta,
updateWorkbenchMeta, updateWorkbenchMeta,
updateWorkbenchViewMeta, updateWorkbenchViewMeta,
} from '../windows-manager'; } from '../windows-manager';
import { showTabContextMenu } from '../windows-manager/context-menu'; import { showTabContextMenu } from '../windows-manager/context-menu';
import { getOrCreateCustomThemeWindow } from '../windows-manager/custom-theme-window'; import { getOrCreateCustomThemeWindow } from '../windows-manager/custom-theme-window';
import type { WorkbenchViewMeta } from '../windows-manager/tab-views-meta-schema';
import { getChallengeResponse } from './challenge'; import { getChallengeResponse } from './challenge';
import { uiSubjects } from './subject'; import { uiSubjects } from './subject';
@@ -173,6 +175,9 @@ export const uiHandlers = {
getTabViewsMeta: async () => { getTabViewsMeta: async () => {
return getTabViewsMeta(); return getTabViewsMeta();
}, },
updateActiveViewMeta: async (e, meta: Partial<WorkbenchViewMeta>) => {
return updateActiveViewMeta(e.sender, meta);
},
getTabsStatus: async () => { getTabsStatus: async () => {
return getTabsStatus(); return getTabsStatus();
}, },
@@ -197,8 +202,8 @@ export const uiHandlers = {
uiSubjects.onToggleRightSidebar$.next(tabId); uiSubjects.onToggleRightSidebar$.next(tabId);
} }
}, },
pingAppLayoutReady: async e => { pingAppLayoutReady: async (e, ready = true) => {
pingAppLayoutReady(e.sender); pingAppLayoutReady(e.sender, ready);
}, },
showDevTools: async (_, ...args: Parameters<typeof showDevTools>) => { showDevTools: async (_, ...args: Parameters<typeof showDevTools>) => {
return showDevTools(...args); return showDevTools(...args);

View File

@@ -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( return Array.from(this.tabViewsMap.entries()).find(
([, view]) => view.webContents.id === id ([, view]) => view.webContents.id === id
)?.[0]; )?.[0];
@@ -303,7 +310,7 @@ export class WebContentViewsManager {
updateWorkbenchViewMeta = ( updateWorkbenchViewMeta = (
workbenchId: string, workbenchId: string,
viewId: string, viewId: string | number,
patch: Partial<WorkbenchViewMeta> patch: Partial<WorkbenchViewMeta>
) => { ) => {
const workbench = this.tabViewsMeta.workbenches.find( const workbench = this.tabViewsMeta.workbenches.find(
@@ -313,7 +320,10 @@ export class WebContentViewsManager {
return; return;
} }
const views = workbench.views; 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) { if (viewIndex === -1) {
return; return;
} }
@@ -821,12 +831,6 @@ export class WebContentViewsManager {
view.webContents.on('did-finish-load', () => { view.webContents.on('did-finish-load', () => {
unsub = helperProcessManager.connectRenderer(view.webContents); 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 { } else {
view.webContents.on('focus', () => { view.webContents.on('focus', () => {
globalThis.setTimeout(() => { globalThis.setTimeout(() => {
@@ -943,7 +947,7 @@ export const updateWorkbenchMeta = (
export const updateWorkbenchViewMeta = ( export const updateWorkbenchViewMeta = (
workbenchId: string, workbenchId: string,
viewId: string, viewId: string | number,
meta: Partial<WorkbenchViewMeta> meta: Partial<WorkbenchViewMeta>
) => { ) => {
WebContentViewsManager.instance.updateWorkbenchViewMeta( WebContentViewsManager.instance.updateWorkbenchViewMeta(
@@ -956,6 +960,24 @@ export const updateWorkbenchViewMeta = (
export const getWorkbenchMeta = (id: string) => { export const getWorkbenchMeta = (id: string) => {
return TabViewsMetaState.value.workbenches.find(w => w.id === id); return TabViewsMetaState.value.workbenches.find(w => w.id === id);
}; };
export const updateActiveViewMeta = (
wc: WebContents,
meta: Partial<WorkbenchViewMeta>
) => {
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 getTabViewsMeta = () => TabViewsMetaState.value;
export const isActiveTab = (wc: WebContents) => { export const isActiveTab = (wc: WebContents) => {
return ( return (
@@ -1042,12 +1064,15 @@ export const showDevTools = (id?: string) => {
.catch(console.error); .catch(console.error);
}; };
export const pingAppLayoutReady = (wc: WebContents) => { export const pingAppLayoutReady = (wc: WebContents, ready: boolean) => {
const viewId = WebContentViewsManager.instance.getViewIdFromWebContentsId( const viewId =
wc.id WebContentViewsManager.instance.getWorkbenchIdFromWebContentsId(wc.id);
);
if (viewId) { if (viewId) {
WebContentViewsManager.instance.setTabUIReady(viewId); if (ready) {
WebContentViewsManager.instance.setTabUIReady(viewId);
} else {
WebContentViewsManager.instance.setTabUIUnready(viewId);
}
} }
}; };

View File

@@ -23,6 +23,7 @@ export const SharePageNotFoundError = () => {
left: '16px', left: '16px',
fontSize: '24px', fontSize: '24px',
cursor: 'pointer', cursor: 'pointer',
color: 'inherit',
}} }}
> >
<Logo1Icon /> <Logo1Icon />

View File

@@ -19,6 +19,7 @@ import {
} from '@affine/core/modules/editor'; } from '@affine/core/modules/editor';
import { PeekViewManagerModal } from '@affine/core/modules/peek-view'; import { PeekViewManagerModal } from '@affine/core/modules/peek-view';
import { ShareReaderService } from '@affine/core/modules/share-doc'; 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 { CloudBlobStorage } from '@affine/core/modules/workspace-engine';
import { WorkspaceFlavour } from '@affine/env/workspace'; import { WorkspaceFlavour } from '@affine/env/workspace';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
@@ -42,7 +43,13 @@ import {
WorkspacesService, WorkspacesService,
} from '@toeverything/infra'; } from '@toeverything/infra';
import clsx from 'clsx'; 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 { useLocation } from 'react-router-dom';
import { PageNotFound } from '../../404'; import { PageNotFound } from '../../404';
@@ -100,17 +107,12 @@ export const SharePage = ({
shareReaderService.reader.loadShare({ workspaceId, docId }); shareReaderService.reader.loadShare({ workspaceId, docId });
}, [shareReaderService, docId, workspaceId]); }, [shareReaderService, docId, workspaceId]);
let element: ReactNode = null;
if (isLoading) { if (isLoading) {
return <AppContainer fallback />; element = null;
} } else if (data) {
element = (
if (error) {
// TODO(@eyhn): show error details
return <SharePageNotFoundError />;
}
if (data) {
return (
<SharePageInner <SharePageInner
workspaceId={data.workspaceId} workspaceId={data.workspaceId}
docId={data.docId} docId={data.docId}
@@ -123,9 +125,13 @@ export const SharePage = ({
templateSnapshotUrl={templateSnapshotUrl} templateSnapshotUrl={templateSnapshotUrl}
/> />
); );
} else if (error) {
element = <SharePageNotFoundError />;
} else { } else {
return <PageNotFound noPermission />; element = <PageNotFound noPermission />;
} }
return <AppContainer fallback={!element}>{element}</AppContainer>;
}; };
const SharePageInner = ({ const SharePageInner = ({
@@ -230,7 +236,8 @@ const SharePageInner = ({
graphQLService, graphQLService,
]); ]);
const pageTitle = useLiveData(page?.title$); const t = useI18n();
const pageTitle = useLiveData(page?.title$) ?? t['unnamed']();
const { jumpToPageBlock, openPage } = useNavigateHelper(); const { jumpToPageBlock, openPage } = useNavigateHelper();
usePageDocumentTitle(pageTitle); usePageDocumentTitle(pageTitle);
@@ -276,43 +283,45 @@ const SharePageInner = ({
} }
return ( return (
<AppContainer> <FrameworkScope scope={workspace.scope}>
<FrameworkScope scope={workspace.scope}> <FrameworkScope scope={page.scope}>
<FrameworkScope scope={page.scope}> <FrameworkScope scope={editor.scope}>
<FrameworkScope scope={editor.scope}> <ViewIcon icon={publishMode === 'page' ? 'doc' : 'edgeless'} />
<div className={styles.root}> <ViewTitle title={pageTitle} />
<div className={styles.mainContainer}> <div className={styles.root}>
<ShareHeader <div className={styles.mainContainer}>
pageId={page.id} <ShareHeader
publishMode={publishMode} pageId={page.id}
isTemplate={isTemplate} publishMode={publishMode}
templateName={templateName} isTemplate={isTemplate}
snapshotUrl={templateSnapshotUrl} templateName={templateName}
/> snapshotUrl={templateSnapshotUrl}
<Scrollable.Root> />
<Scrollable.Viewport <Scrollable.Root>
className={clsx( <Scrollable.Viewport
'affine-page-viewport', className={clsx(
styles.editorContainer 'affine-page-viewport',
)} styles.editorContainer
> )}
<PageDetailEditor onLoad={onEditorLoad} /> >
{publishMode === 'page' ? <ShareFooter /> : null} <PageDetailEditor onLoad={onEditorLoad} />
</Scrollable.Viewport> {publishMode === 'page' && !BUILD_CONFIG.isElectron ? (
<Scrollable.Scrollbar /> <ShareFooter />
</Scrollable.Root> ) : null}
<EditorOutlineViewer </Scrollable.Viewport>
editor={editorContainer} <Scrollable.Scrollbar />
show={publishMode === 'page'} </Scrollable.Root>
/> <EditorOutlineViewer
<SharePageFooter /> editor={editorContainer}
</div> show={publishMode === 'page'}
/>
{!BUILD_CONFIG.isElectron && <SharePageFooter />}
</div> </div>
<PeekViewManagerModal /> </div>
</FrameworkScope> <PeekViewManagerModal />
</FrameworkScope> </FrameworkScope>
</FrameworkScope> </FrameworkScope>
</AppContainer> </FrameworkScope>
); );
}; };

View File

@@ -1,7 +1,6 @@
import { AffineErrorBoundary } from '@affine/core/components/affine/affine-error-boundary'; import { AffineErrorBoundary } from '@affine/core/components/affine/affine-error-boundary';
import { AffineErrorComponent } from '@affine/core/components/affine/affine-error-boundary/affine-error-fallback'; import { AffineErrorComponent } from '@affine/core/components/affine/affine-error-boundary/affine-error-fallback';
import { PageNotFound } from '@affine/core/desktop/pages/404'; 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 { workbenchRoutes } from '@affine/core/mobile/workbench-router';
import { import {
useLiveData, useLiveData,
@@ -23,6 +22,7 @@ import {
} from 'react-router-dom'; } from 'react-router-dom';
import { WorkspaceLayout } from './layout'; import { WorkspaceLayout } from './layout';
import { MobileWorkbenchRoot } from './workbench-root';
type Route = { Component: React.ComponentType }; type Route = { Component: React.ComponentType };
/** /**

View File

@@ -1,29 +1,47 @@
import { useServiceOptional } from '@toeverything/infra'; import { useServiceOptional } from '@toeverything/infra';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { DesktopApiService } from '../../desktop-api';
import type { ViewIconName } from '../constants'; import type { ViewIconName } from '../constants';
import { ViewService } from '../services/view'; import { ViewService } from '../services/view';
export const ViewTitle = ({ title }: { title: string }) => { export const ViewTitle = ({ title }: { title: string }) => {
const view = useServiceOptional(ViewService)?.view; const view = useServiceOptional(ViewService)?.view;
const desktopApi = useServiceOptional(DesktopApiService);
useEffect(() => { useEffect(() => {
if (view) { if (view) {
view.setTitle(title); view.setTitle(title);
} else if (desktopApi) {
desktopApi.handler.ui
.updateActiveViewMeta({
title,
})
.catch(e => {
console.error(e);
});
} }
}, [title, view]); }, [desktopApi, title, view]);
return null; return null;
}; };
export const ViewIcon = ({ icon }: { icon: ViewIconName }) => { export const ViewIcon = ({ icon }: { icon: ViewIconName }) => {
const view = useServiceOptional(ViewService)?.view; const view = useServiceOptional(ViewService)?.view;
const desktopApi = useServiceOptional(DesktopApiService);
useEffect(() => { useEffect(() => {
if (view) { if (view) {
view.setIcon(icon); view.setIcon(icon);
} else if (desktopApi) {
desktopApi.handler.ui
.updateActiveViewMeta({
iconName: icon,
})
.catch(e => {
console.error(e);
});
} }
}, [icon, view]); }, [desktopApi, icon, view]);
return null; return null;
}; };