fix(electron): share page in electron issues (#8703)
fix AF-1592 fix AF-1612
This commit is contained in:
@@ -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', {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export const SharePageNotFoundError = () => {
|
|||||||
left: '16px',
|
left: '16px',
|
||||||
fontSize: '24px',
|
fontSize: '24px',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
|
color: 'inherit',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Logo1Icon />
|
<Logo1Icon />
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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 };
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user