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).applicationStart();
window.addEventListener('unload', () => {
frameworkProvider
.get(DesktopApiService)
.api.handler.ui.pingAppLayoutReady(false)
.catch(console.error);
});
events?.applicationMenu.openAboutPageInSettingModal(() =>
frameworkProvider.get(GlobalDialogService).open('setting', {

View File

@@ -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() {
<div className={styles.body}>
<ShellAppSidebarFallback />
</div>
{environment.isWindows && (
<div style={{ position: 'fixed', right: 0, top: 0, zIndex: 5 }}>
<WindowsAppControls />
</div>
)}
</div>
</I18nProvider>
</ThemeProvider>

View File

@@ -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);
}

View File

@@ -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<WorkbenchViewMeta>) => {
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<typeof showDevTools>) => {
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(
([, view]) => view.webContents.id === id
)?.[0];
@@ -303,7 +310,7 @@ export class WebContentViewsManager {
updateWorkbenchViewMeta = (
workbenchId: string,
viewId: string,
viewId: string | number,
patch: Partial<WorkbenchViewMeta>
) => {
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<WorkbenchViewMeta>
) => {
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<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 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);
}
}
};

View File

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

View File

@@ -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 <AppContainer fallback />;
}
if (error) {
// TODO(@eyhn): show error details
return <SharePageNotFoundError />;
}
if (data) {
return (
element = null;
} else if (data) {
element = (
<SharePageInner
workspaceId={data.workspaceId}
docId={data.docId}
@@ -123,9 +125,13 @@ export const SharePage = ({
templateSnapshotUrl={templateSnapshotUrl}
/>
);
} else if (error) {
element = <SharePageNotFoundError />;
} else {
return <PageNotFound noPermission />;
element = <PageNotFound noPermission />;
}
return <AppContainer fallback={!element}>{element}</AppContainer>;
};
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 (
<AppContainer>
<FrameworkScope scope={workspace.scope}>
<FrameworkScope scope={page.scope}>
<FrameworkScope scope={editor.scope}>
<div className={styles.root}>
<div className={styles.mainContainer}>
<ShareHeader
pageId={page.id}
publishMode={publishMode}
isTemplate={isTemplate}
templateName={templateName}
snapshotUrl={templateSnapshotUrl}
/>
<Scrollable.Root>
<Scrollable.Viewport
className={clsx(
'affine-page-viewport',
styles.editorContainer
)}
>
<PageDetailEditor onLoad={onEditorLoad} />
{publishMode === 'page' ? <ShareFooter /> : null}
</Scrollable.Viewport>
<Scrollable.Scrollbar />
</Scrollable.Root>
<EditorOutlineViewer
editor={editorContainer}
show={publishMode === 'page'}
/>
<SharePageFooter />
</div>
<FrameworkScope scope={workspace.scope}>
<FrameworkScope scope={page.scope}>
<FrameworkScope scope={editor.scope}>
<ViewIcon icon={publishMode === 'page' ? 'doc' : 'edgeless'} />
<ViewTitle title={pageTitle} />
<div className={styles.root}>
<div className={styles.mainContainer}>
<ShareHeader
pageId={page.id}
publishMode={publishMode}
isTemplate={isTemplate}
templateName={templateName}
snapshotUrl={templateSnapshotUrl}
/>
<Scrollable.Root>
<Scrollable.Viewport
className={clsx(
'affine-page-viewport',
styles.editorContainer
)}
>
<PageDetailEditor onLoad={onEditorLoad} />
{publishMode === 'page' && !BUILD_CONFIG.isElectron ? (
<ShareFooter />
) : null}
</Scrollable.Viewport>
<Scrollable.Scrollbar />
</Scrollable.Root>
<EditorOutlineViewer
editor={editorContainer}
show={publishMode === 'page'}
/>
{!BUILD_CONFIG.isElectron && <SharePageFooter />}
</div>
<PeekViewManagerModal />
</FrameworkScope>
</div>
<PeekViewManagerModal />
</FrameworkScope>
</FrameworkScope>
</AppContainer>
</FrameworkScope>
);
};

View File

@@ -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 };
/**

View File

@@ -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;
};