fix(core): hide the footer that blocks the toolbar in shared page (#8091)

close PD-1405 CLOUD-64

https://github.com/user-attachments/assets/f6ed2dfc-d238-41d8-abaf-684193a080ff
This commit is contained in:
JimmFly
2024-09-05 07:14:23 +00:00
parent 03c2051926
commit f452414952
11 changed files with 279 additions and 404 deletions

View File

@@ -1,4 +1,3 @@
import { EditorService } from '@affine/core/modules/editor';
import type { ReferenceInfo } from '@blocksuite/affine-model';
import type { DocMode } from '@blocksuite/blocks';
import type {
@@ -8,13 +7,12 @@ import type {
PageEditor,
} from '@blocksuite/presets';
import { type Doc, Slot } from '@blocksuite/store';
import { useService } from '@toeverything/infra';
import clsx from 'clsx';
import type React from 'react';
import {
forwardRef,
useCallback,
useEffect,
useImperativeHandle,
useLayoutEffect,
useMemo,
useRef,
@@ -60,7 +58,6 @@ export const BlocksuiteEditorContainer = forwardRef<
{ page, mode, className, style, shared },
ref
) {
const editorService = useService(EditorService);
const rootRef = useRef<HTMLDivElement>(null);
const docRef = useRef<PageEditor>(null);
const docTitleRef = useRef<DocTitle>(null);
@@ -112,6 +109,9 @@ export const BlocksuiteEditorContainer = forwardRef<
get doc() {
return page;
},
get docTitle() {
return docTitleRef.current;
},
get host() {
return mode === 'page'
? docRef.current?.host
@@ -159,35 +159,9 @@ export const BlocksuiteEditorContainer = forwardRef<
return proxy;
}, [mode, page, slots]);
useEffect(() => {
if (ref) {
if (typeof ref === 'function') {
ref(affineEditorContainerProxy);
} else {
ref.current = affineEditorContainerProxy;
}
}
}, [affineEditorContainerProxy, ref]);
useEffect(() => {
let canceled = false;
let unsubscribe: () => void = () => {};
affineEditorContainerProxy.updateComplete
.then(() => {
if (!canceled) {
unsubscribe = editorService.editor.bindEditorContainer(
affineEditorContainerProxy,
docTitleRef.current as DocTitle
);
}
})
.catch(console.error);
return () => {
canceled = true;
unsubscribe();
};
}, [affineEditorContainerProxy, mode, editorService]);
useImperativeHandle(ref, () => affineEditorContainerProxy, [
affineEditorContainerProxy,
]);
const handleClickPageModeBlank = useCallback(() => {
affineEditorContainerProxy.host?.std.command.exec(

View File

@@ -1,32 +1,30 @@
import { useRefEffect } from '@affine/component';
import { EditorLoading } from '@affine/component/page-detail-skeleton';
import type { DocMode } from '@blocksuite/blocks';
import { assertExists } from '@blocksuite/global/utils';
import {
BookmarkBlockService,
customImageProxyMiddleware,
type DocMode,
EmbedGithubBlockService,
EmbedLoomBlockService,
EmbedYoutubeBlockService,
ImageBlockService,
} from '@blocksuite/blocks';
import { DisposableGroup } from '@blocksuite/global/utils';
import type { AffineEditorContainer } from '@blocksuite/presets';
import type { Doc } from '@blocksuite/store';
import { use } from 'foxact/use';
import type { CSSProperties, ReactElement } from 'react';
import {
forwardRef,
memo,
Suspense,
useCallback,
useEffect,
useRef,
} from 'react';
import type { CSSProperties } from 'react';
import { Suspense, useEffect } from 'react';
import { BlocksuiteEditorContainer } from './blocksuite-editor-container';
import { NoPageRootError } from './no-page-error';
export type ErrorBoundaryProps = {
onReset?: () => void;
};
export type EditorProps = {
page: Doc;
mode: DocMode;
shared?: boolean;
// on Editor instance instantiated
onLoadEditor?: (editor: AffineEditorContainer) => () => void;
// on Editor ready
onEditorReady?: (editor: AffineEditorContainer) => (() => void) | void;
style?: CSSProperties;
className?: string;
};
@@ -53,73 +51,100 @@ function usePageRoot(page: Doc) {
return page.root;
}
const BlockSuiteEditorImpl = forwardRef<AffineEditorContainer, EditorProps>(
function BlockSuiteEditorImpl(
{ mode, page, className, onLoadEditor, shared, style },
ref
) {
usePageRoot(page);
assertExists(page, 'page should not be null');
const editorDisposeRef = useRef<() => void>(() => {});
const editorRef = useRef<AffineEditorContainer | null>(null);
const BlockSuiteEditorImpl = ({
mode,
page,
className,
shared,
style,
onEditorReady,
}: EditorProps) => {
usePageRoot(page);
const onRefChange = useCallback(
(editor: AffineEditorContainer | null) => {
editorRef.current = editor;
if (ref) {
if (typeof ref === 'function') {
ref(editor);
} else {
ref.current = editor;
}
}
if (editor && onLoadEditor) {
editorDisposeRef.current = onLoadEditor(editor);
}
},
[onLoadEditor, ref]
);
useEffect(() => {
const disposable = page.slots.blockUpdated.once(() => {
page.collection.setDocMeta(page.id, {
updatedDate: Date.now(),
});
useEffect(() => {
const disposable = page.slots.blockUpdated.once(() => {
page.collection.setDocMeta(page.id, {
updatedDate: Date.now(),
});
});
return () => {
disposable.dispose();
};
}, [page]);
const editorRef = useRefEffect(
(editor: AffineEditorContainer) => {
globalThis.currentEditor = editor;
let canceled = false;
const disposableGroup = new DisposableGroup();
if (onEditorReady) {
// Invoke onLoad once the editor has been mounted to the DOM.
editor.updateComplete
.then(() => {
if (canceled) {
return;
}
// host should be ready
// provide image proxy endpoint to blocksuite
editor.host?.std.clipboard.use(
customImageProxyMiddleware(runtimeConfig.imageProxyUrl)
);
ImageBlockService.setImageProxyURL(runtimeConfig.imageProxyUrl);
// provide link preview endpoint to blocksuite
BookmarkBlockService.setLinkPreviewEndpoint(
runtimeConfig.linkPreviewUrl
);
EmbedGithubBlockService.setLinkPreviewEndpoint(
runtimeConfig.linkPreviewUrl
);
EmbedYoutubeBlockService.setLinkPreviewEndpoint(
runtimeConfig.linkPreviewUrl
);
EmbedLoomBlockService.setLinkPreviewEndpoint(
runtimeConfig.linkPreviewUrl
);
return editor.host?.updateComplete;
})
.then(() => {
if (canceled) {
return;
}
const dispose = onEditorReady(editor);
if (dispose) {
disposableGroup.add(dispose);
}
})
.catch(console.error);
}
return () => {
disposable.dispose();
canceled = true;
disposableGroup.dispose();
};
}, [page]);
},
[onEditorReady, page]
);
useEffect(() => {
return () => {
editorDisposeRef.current();
};
}, []);
return (
<BlocksuiteEditorContainer
mode={mode}
page={page}
shared={shared}
ref={editorRef}
className={className}
style={style}
/>
);
};
return (
<BlocksuiteEditorContainer
mode={mode}
page={page}
shared={shared}
ref={onRefChange}
className={className}
style={style}
/>
);
}
);
export const BlockSuiteEditor = memo(
forwardRef<AffineEditorContainer, EditorProps>(
function BlockSuiteEditor(props, ref): ReactElement {
return (
<Suspense fallback={<EditorLoading />}>
<BlockSuiteEditorImpl key={props.page.id} ref={ref} {...props} />
</Suspense>
);
}
)
);
BlockSuiteEditor.displayName = 'BlockSuiteEditor';
export const BlockSuiteEditor = (props: EditorProps) => {
return (
<Suspense fallback={<EditorLoading />}>
<BlockSuiteEditorImpl key={props.page.id} {...props} />
</Suspense>
);
};

View File

@@ -1,16 +1,16 @@
import { IconButton } from '@affine/component';
import { EditorService } from '@affine/core/modules/editor';
import { PresentationIcon } from '@blocksuite/icons/rc';
import { usePresent } from './use-present';
import { useService } from '@toeverything/infra';
export const DetailPageHeaderPresentButton = () => {
const { isPresent, handlePresent } = usePresent();
const editorService = useService(EditorService);
return (
<IconButton
style={{ flexShrink: 0 }}
size="24"
onClick={() => handlePresent(!isPresent)}
onClick={() => editorService.editor.togglePresentation()}
>
<PresentationIcon />
</IconButton>

View File

@@ -1,59 +0,0 @@
import { useActiveBlocksuiteEditor } from '@affine/core/hooks/use-block-suite-editor';
import type { EdgelessRootService } from '@blocksuite/blocks';
import { useCallback, useEffect, useState } from 'react';
export const usePresent = () => {
const [isPresent, setIsPresent] = useState(false);
const [editor] = useActiveBlocksuiteEditor();
const handlePresent = useCallback(
(enable = true) => {
isPresent;
const editorHost = editor?.host;
if (!editorHost) return;
// TODO(@catsjuice): use surfaceService subAtom
const enterOrLeavePresentationMode = () => {
const edgelessRootService = editorHost.std.getService(
'affine:page'
) as EdgelessRootService;
if (!edgelessRootService) {
return;
}
const activeTool = edgelessRootService.tool.edgelessTool.type;
const isFrameNavigator = activeTool === 'frameNavigator';
if ((enable && isFrameNavigator) || (!enable && !isFrameNavigator))
return;
edgelessRootService.tool.setEdgelessTool({
type: enable ? 'frameNavigator' : 'default',
});
};
enterOrLeavePresentationMode();
setIsPresent(enable);
},
[editor?.host, isPresent]
);
useEffect(() => {
if (!isPresent) return;
const editorHost = editor?.host;
if (!editorHost) return;
const edgelessPage = editorHost?.querySelector('affine-edgeless-root');
if (!edgelessPage) return;
return edgelessPage.slots.edgelessToolUpdated.on(() => {
setIsPresent(edgelessPage.edgelessTool.type === 'frameNavigator');
}).dispose;
}, [editor?.host, isPresent]);
return {
isPresent,
handlePresent,
};
};

View File

@@ -1,19 +1,21 @@
import { Button } from '@affine/component/ui/button';
import { EditorService } from '@affine/core/modules/editor';
import { useI18n } from '@affine/i18n';
import { PresentationIcon } from '@blocksuite/icons/rc';
import { useLiveData, useService } from '@toeverything/infra';
import { usePresent } from '../../blocksuite/block-suite-header/present/use-present';
import * as styles from './styles.css';
export const PresentButton = () => {
const t = useI18n();
const { isPresent, handlePresent } = usePresent();
const editorService = useService(EditorService);
const isPresent = useLiveData(editorService.editor.isPresenting$);
return (
<Button
prefix={<PresentationIcon />}
className={styles.presentButton}
onClick={() => handlePresent()}
onClick={() => editorService.editor.togglePresentation()}
disabled={isPresent}
>
{t['com.affine.share-page.header.present']()}

View File

@@ -79,9 +79,7 @@ export const PublishPageUserAvatar = () => {
<Menu
items={menuItem}
contentOptions={{
style: {
transform: 'translateX(-16px)',
},
align: 'end',
}}
>
<div className={styles.iconWrapper} data-testid="share-page-user-avatar">

View File

@@ -1,15 +1,11 @@
import './page-detail-editor.css';
import { useDocCollectionPage } from '@affine/core/hooks/use-block-suite-workspace-page';
import type { DocMode } from '@blocksuite/blocks';
import { DisposableGroup } from '@blocksuite/global/utils';
import type { AffineEditorContainer } from '@blocksuite/presets';
import type { Doc as BlockSuiteDoc, DocCollection } from '@blocksuite/store';
import { useLiveData, useService } from '@toeverything/infra';
import { cssVar } from '@toeverything/theme';
import clsx from 'clsx';
import type { CSSProperties } from 'react';
import { memo, useCallback, useMemo } from 'react';
import { useMemo } from 'react';
import { EditorService } from '../modules/editor';
import {
@@ -25,22 +21,14 @@ declare global {
}
export type OnLoadEditor = (
page: BlockSuiteDoc,
editor: AffineEditorContainer
) => () => void;
) => (() => void) | void;
export interface PageDetailEditorProps {
isPublic?: boolean;
publishMode?: DocMode;
docCollection: DocCollection;
pageId: string;
onLoad?: OnLoadEditor;
}
const PageDetailEditorMain = memo(function PageDetailEditorMain({
page,
onLoad,
}: PageDetailEditorProps & { page: BlockSuiteDoc }) {
export const PageDetailEditor = ({ onLoad }: PageDetailEditorProps) => {
const editor = useService(EditorService).editor;
const mode = useLiveData(editor.mode$);
@@ -68,30 +56,6 @@ const PageDetailEditorMain = memo(function PageDetailEditorMain({
: fontStyle.value;
}, [settings.customFontFamily, settings.fontFamily]);
const onLoadEditor = useCallback(
(editor: AffineEditorContainer) => {
// debug current detail editor
globalThis.currentEditor = editor;
const disposableGroup = new DisposableGroup();
localStorage.setItem('last_page_id', page.id);
if (onLoad) {
// Invoke onLoad once the editor has been mounted to the DOM.
editor.updateComplete
.then(() => editor.host?.updateComplete)
.then(() => {
disposableGroup.add(onLoad(page, editor));
})
.catch(console.error);
}
return () => {
disposableGroup.dispose();
};
},
[onLoad, page]
);
return (
<Editor
className={clsx(styles.editor, {
@@ -104,18 +68,9 @@ const PageDetailEditorMain = memo(function PageDetailEditorMain({
} as CSSProperties
}
mode={mode}
page={page}
page={editor.doc.blockSuiteDoc}
shared={isSharedMode}
onLoadEditor={onLoadEditor}
onEditorReady={onLoad}
/>
);
});
export const PageDetailEditor = (props: PageDetailEditorProps) => {
const { docCollection, pageId } = props;
const page = useDocCollectionPage(docCollection, pageId);
if (!page) {
return null;
}
return <PageDetailEditorMain {...props} page={page} />;
};

View File

@@ -1,4 +1,4 @@
import type { DocMode } from '@blocksuite/blocks';
import type { DocMode, EdgelessRootService } from '@blocksuite/blocks';
import type { InlineEditor } from '@blocksuite/inline/inline-editor';
import type { AffineEditorContainer, DocTitle } from '@blocksuite/presets';
import type { DocService, WorkspaceService } from '@toeverything/infra';
@@ -23,6 +23,20 @@ export class Editor extends Entity {
readonly editorContainer$ = new LiveData<AffineEditorContainer | null>(null);
isPresenting$ = new LiveData<boolean>(false);
togglePresentation() {
const edgelessRootService =
this.editorContainer$.value?.host?.std.getService(
'affine:page'
) as EdgelessRootService;
if (!edgelessRootService) return;
edgelessRootService.tool.setEdgelessTool({
type: !this.isPresenting$.value ? 'frameNavigator' : 'default',
});
}
setSelector(selector: EditorSelector | undefined) {
this.selector$.next(selector);
}
@@ -145,8 +159,10 @@ export class Editor extends Entity {
bindEditorContainer(
editorContainer: AffineEditorContainer,
docTitle: DocTitle
docTitle: DocTitle | null
) {
const unsubs: (() => void)[] = [];
const focusAt$ = LiveData.computed(get => {
const selector = get(this.selector$);
const id =
@@ -159,26 +175,49 @@ export class Editor extends Entity {
return null;
}
});
if (focusAt$.value === null) {
if (focusAt$.value === null && docTitle) {
const title = docTitle.querySelector<
HTMLElement & { inlineEditor: InlineEditor }
>('rich-text');
title?.inlineEditor.focusEnd();
}
const unsubscribe = focusAt$
.distinctUntilChanged(
(a, b) => a?.id === b?.id && a?.refreshKey === b?.refreshKey
)
.subscribe(params => {
if (params?.id) {
const std = editorContainer.host?.std;
if (std) {
scrollAnchoring(std, this.mode$.value, params.id);
unsubs.push(
focusAt$
.distinctUntilChanged(
(a, b) => a?.id === b?.id && a?.refreshKey === b?.refreshKey
)
.subscribe(params => {
if (params?.id) {
const std = editorContainer.host?.std;
if (std) {
scrollAnchoring(std, this.mode$.value, params.id);
}
}
}
});
}).unsubscribe
);
const edgelessPage = editorContainer.host?.querySelector(
'affine-edgeless-root'
);
if (!edgelessPage) {
this.isPresenting$.next(false);
} else {
this.isPresenting$.next(
edgelessPage.edgelessTool.type === 'frameNavigator'
);
unsubs.push(
edgelessPage.slots.edgelessToolUpdated.on(() => {
this.isPresenting$.next(
edgelessPage.edgelessTool.type === 'frameNavigator'
);
}).dispose
);
}
return () => {
unsubscribe.unsubscribe();
for (const unsub of unsubs) {
unsub();
}
};
}

View File

@@ -4,7 +4,6 @@ import { AIProvider } from '@affine/core/blocksuite/presets/ai';
import { AffineErrorBoundary } from '@affine/core/components/affine/affine-error-boundary';
import { BlockSuiteEditor } from '@affine/core/components/blocksuite/block-suite-editor';
import { EditorOutlineViewer } from '@affine/core/components/blocksuite/outline-viewer';
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
import { EditorService } from '@affine/core/modules/editor';
import { PageNotFound } from '@affine/core/pages/404';
import { DebugLogger } from '@affine/debug';
@@ -18,7 +17,7 @@ import {
useServices,
} from '@toeverything/infra';
import clsx from 'clsx';
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect } from 'react';
import { WorkbenchService } from '../../../workbench';
import { PeekViewService } from '../../services/peek-view';
@@ -75,26 +74,48 @@ function DocPeekPreviewEditor({
const doc = editor.doc;
const workspace = editor.doc.workspace;
const mode = useLiveData(editor.mode$);
const { jumpToTag } = useNavigateHelper();
const workbench = useService(WorkbenchService).workbench;
const peekView = useService(PeekViewService).peekView;
const [editorElement, setEditorElement] =
useState<AffineEditorContainer | null>(null);
const editorElement = useLiveData(editor.editorContainer$);
const onRef = (editor: AffineEditorContainer) => {
setEditorElement(editor);
};
const handleOnEditorReady = useCallback(
(editorContainer: AffineEditorContainer) => {
if (!editorContainer.host) {
return;
}
const disposableGroup = new DisposableGroup();
const rootService = editorContainer.host.std.getService('affine:page');
// doc change event inside peek view should be handled by peek view
disposableGroup.add(
rootService.slots.docLinkClicked.on(options => {
peekView
.open({
type: 'doc',
docId: options.pageId,
...options.params,
})
.catch(console.error);
})
);
useEffect(() => {
editorElement?.updateComplete
.then(() => {
if (mode === 'edgeless') {
fitViewport(editorElement, xywh);
}
})
.catch(console.error);
return;
}, [editorElement, mode, xywh]);
editor.setEditorContainer(editorContainer);
const unbind = editor.bindEditorContainer(
editorContainer,
(editorContainer as any).title
);
if (mode === 'edgeless') {
fitViewport(editorContainer, xywh);
}
return () => {
unbind();
editor.setEditorContainer(null);
disposableGroup.dispose();
};
},
[editor, mode, peekView, xywh]
);
useEffect(() => {
const disposable = AIProvider.slots.requestOpenWithChat.on(() => {
@@ -110,43 +131,6 @@ function DocPeekPreviewEditor({
};
}, [doc, peekView, workbench, workspace.id]);
useEffect(() => {
const disposableGroup = new DisposableGroup();
if (editorElement) {
editorElement.updateComplete
.then(() => {
if (!editorElement.host) {
return;
}
const rootService = editorElement.host.std.getService('affine:page');
// doc change event inside peek view should be handled by peek view
disposableGroup.add(
rootService.slots.docLinkClicked.on(options => {
peekView
.open({
type: 'doc',
docId: options.pageId,
...options.params,
})
.catch(console.error);
})
);
// TODO(@Peng): no tag peek view yet
disposableGroup.add(
rootService.slots.tagClicked.on(({ tagId }) => {
jumpToTag(workspace.id, tagId);
peekView.close();
})
);
})
.catch(console.error);
}
return () => {
disposableGroup.dispose();
};
}, [editorElement, jumpToTag, peekView, workspace.id]);
const openOutlinePanel = useCallback(() => {
workbench.openDoc(doc.id);
workbench.openSidebar();
@@ -161,10 +145,10 @@ function DocPeekPreviewEditor({
className={clsx('affine-page-viewport', styles.affineDocViewport)}
>
<BlockSuiteEditor
ref={onRef}
className={styles.editor}
mode={mode}
page={doc.blockSuiteDoc}
onEditorReady={handleOnEditorReady}
/>
<EditorOutlineViewer
editor={editorElement}

View File

@@ -11,22 +11,12 @@ import { EditorSettingService } from '@affine/core/modules/editor-settting';
import { RecentDocsService } from '@affine/core/modules/quicksearch';
import { ViewService } from '@affine/core/modules/workbench/services/view';
import type { PageRootService } from '@blocksuite/blocks';
import {
BookmarkBlockService,
customImageProxyMiddleware,
EmbedGithubBlockService,
EmbedLoomBlockService,
EmbedYoutubeBlockService,
ImageBlockService,
} from '@blocksuite/blocks';
import { DisposableGroup } from '@blocksuite/global/utils';
import { AiIcon, FrameIcon, TocIcon, TodayIcon } from '@blocksuite/icons/rc';
import { type AffineEditorContainer } from '@blocksuite/presets';
import type { Doc as BlockSuiteDoc } from '@blocksuite/store';
import {
DocService,
FrameworkScope,
globalBlockSuiteSchema,
GlobalContextService,
useLiveData,
useService,
@@ -36,7 +26,6 @@ import {
import clsx from 'clsx';
import { memo, useCallback, useEffect, useRef } from 'react';
import { useParams } from 'react-router-dom';
import type { Map as YMap } from 'yjs';
import { AffineErrorBoundary } from '../../../components/affine/affine-error-boundary';
import { GlobalPageHistoryModal } from '../../../components/affine/page-history-modal';
@@ -94,7 +83,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
const activeSidebarTab = useLiveData(view.activeSidebarTab$);
const isInTrash = useLiveData(doc.meta$.map(meta => meta.trash));
const { openPage, jumpToPageBlock, jumpToTag } = useNavigateHelper();
const { openPage, jumpToPageBlock } = useNavigateHelper();
const editorContainer = useLiveData(editor.editorContainer$);
const isSideBarOpen = useLiveData(workbench.sidebarOpen$);
@@ -171,49 +160,10 @@ const DetailPageImpl = memo(function DetailPageImpl() {
usePageDocumentTitle(title);
const onLoad = useCallback(
(bsPage: BlockSuiteDoc, editorContainer: AffineEditorContainer) => {
try {
// todo(joooye34): improve the following migration code
const surfaceBlock = bsPage.getBlockByFlavour('affine:surface')[0];
// hotfix for old page
if (
surfaceBlock &&
(surfaceBlock.yBlock.get('prop:elements') as YMap<any>).get(
'type'
) !== '$blocksuite:internal:native$'
) {
globalBlockSuiteSchema.upgradeDoc(
0,
{
'affine:surface': 3,
},
bsPage.spaceDoc
);
}
} catch {}
(editorContainer: AffineEditorContainer) => {
// blocksuite editor host
const editorHost = editorContainer.host;
// provide image proxy endpoint to blocksuite
editorHost?.std.clipboard.use(
customImageProxyMiddleware(runtimeConfig.imageProxyUrl)
);
ImageBlockService.setImageProxyURL(runtimeConfig.imageProxyUrl);
// provide link preview endpoint to blocksuite
BookmarkBlockService.setLinkPreviewEndpoint(runtimeConfig.linkPreviewUrl);
EmbedGithubBlockService.setLinkPreviewEndpoint(
runtimeConfig.linkPreviewUrl
);
EmbedYoutubeBlockService.setLinkPreviewEndpoint(
runtimeConfig.linkPreviewUrl
);
EmbedLoomBlockService.setLinkPreviewEndpoint(
runtimeConfig.linkPreviewUrl
);
// provide page mode and updated date to blocksuite
const pageService =
editorHost?.std.getService<PageRootService>('affine:page');
const disposable = new DisposableGroup();
@@ -234,27 +184,21 @@ const DetailPageImpl = memo(function DetailPageImpl() {
return openPage(docCollection.id, pageId);
})
);
disposable.add(
pageService.slots.tagClicked.on(({ tagId }) => {
jumpToTag(workspace.id, tagId);
})
);
}
editor.setEditorContainer(editorContainer);
const unbind = editor.bindEditorContainer(
editorContainer,
(editorContainer as any).docTitle // set from proxy
);
return () => {
unbind();
editor.setEditorContainer(null);
disposable.dispose();
};
},
[
editor,
jumpToPageBlock,
docCollection.id,
openPage,
jumpToTag,
workspace.id,
]
[editor, openPage, docCollection.id, jumpToPageBlock]
);
const [refCallback, hasScrollTop] = useHasScrollTop();
@@ -288,11 +232,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
styles.editorContainer
)}
>
<PageDetailEditor
pageId={doc.id}
onLoad={onLoad}
docCollection={docCollection}
/>
<PageDetailEditor onLoad={onLoad} />
</Scrollable.Viewport>
<Scrollable.Scrollbar
className={clsx({

View File

@@ -6,17 +6,19 @@ import { AppContainer, MainContainer } from '@affine/core/components/workspace';
import { useActiveBlocksuiteEditor } from '@affine/core/hooks/use-block-suite-editor';
import { usePageDocumentTitle } from '@affine/core/hooks/use-global-state';
import { AuthService } from '@affine/core/modules/cloud';
import { type Editor, EditorsService } from '@affine/core/modules/editor';
import {
type Editor,
EditorService,
EditorsService,
} from '@affine/core/modules/editor';
import { PeekViewManagerModal } from '@affine/core/modules/peek-view';
import { ShareReaderService } from '@affine/core/modules/share-doc';
import { CloudBlobStorage } from '@affine/core/modules/workspace-engine';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useI18n } from '@affine/i18n';
import { type DocMode, DocModes } from '@blocksuite/blocks';
import { noop } from '@blocksuite/global/utils';
import { Logo1Icon } from '@blocksuite/icons/rc';
import type { AffineEditorContainer } from '@blocksuite/presets';
import type { Doc as BlockSuiteDoc } from '@blocksuite/store';
import type { Doc, Workspace } from '@toeverything/infra';
import {
DocsService,
@@ -105,7 +107,6 @@ const SharePageInner = ({
docBinary: Uint8Array;
publishMode?: DocMode;
}) => {
const t = useI18n();
const workspacesService = useService(WorkspacesService);
const [workspace, setWorkspace] = useState<Workspace | null>(null);
@@ -179,15 +180,24 @@ const SharePageInner = ({
const pageTitle = useLiveData(page?.title$);
usePageDocumentTitle(pageTitle);
const authService = useService(AuthService);
const loginStatus = useLiveData(authService.session.status$);
const onEditorLoad = useCallback(
(_: BlockSuiteDoc, editor: AffineEditorContainer) => {
setActiveBlocksuiteEditor(editor);
return noop;
(editorContainer: AffineEditorContainer) => {
setActiveBlocksuiteEditor(editorContainer);
if (!editor) {
return;
}
editor.setEditorContainer(editorContainer);
const unbind = editor.bindEditorContainer(
editorContainer,
(editorContainer as any).docTitle
);
return () => {
unbind();
editor.setEditorContainer(null);
};
},
[setActiveBlocksuiteEditor]
[editor, setActiveBlocksuiteEditor]
);
if (!workspace || !page || !editor) {
@@ -214,30 +224,12 @@ const SharePageInner = ({
styles.editorContainer
)}
>
<PageDetailEditor
isPublic
publishMode={publishMode}
docCollection={page.blockSuiteDoc.collection}
pageId={page.id}
onLoad={onEditorLoad}
/>
<PageDetailEditor onLoad={onEditorLoad} />
{publishMode === 'page' ? <ShareFooter /> : null}
</Scrollable.Viewport>
<Scrollable.Scrollbar />
</Scrollable.Root>
{loginStatus !== 'authenticated' ? (
<a
href="https://affine.pro"
target="_blank"
className={styles.link}
rel="noreferrer"
>
<span className={styles.linkText}>
{t['com.affine.share-page.footer.built-with']()}
</span>
<Logo1Icon fontSize={20} />
</a>
) : null}
<SharePageFooter />
</div>
</div>
</MainContainer>
@@ -248,3 +240,28 @@ const SharePageInner = ({
</FrameworkScope>
);
};
const SharePageFooter = () => {
const t = useI18n();
const editorService = useService(EditorService);
const isPresent = useLiveData(editorService.editor.isPresenting$);
const authService = useService(AuthService);
const loginStatus = useLiveData(authService.session.status$);
if (isPresent || loginStatus === 'authenticated') {
return null;
}
return (
<a
href="https://affine.pro"
target="_blank"
className={styles.link}
rel="noreferrer"
>
<span className={styles.linkText}>
{t['com.affine.share-page.footer.built-with']()}
</span>
<Logo1Icon fontSize={20} />
</a>
);
};