Skip to content

Commit

Permalink
feat(core): center peek open doc should only load doc when idle (#10023)
Browse files Browse the repository at this point in the history
  • Loading branch information
pengx17 authored and donteatfriedrice committed Feb 11, 2025
1 parent 8766a69 commit c1c4ed4
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 47 deletions.
8 changes: 8 additions & 0 deletions packages/frontend/core/src/modules/doc/services/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ export class DocsService extends Service {
super();
}

loaded(docId: string) {
const exists = this.pool.get(docId);
if (exists) {
return { doc: exists.obj, release: exists.release };
}
return null;
}

open(docId: string) {
const docRecord = this.list.doc$(docId).value;
if (!docRecord) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,13 @@ function DocPeekPreviewEditor({
);
}

export function DocPeekPreview({ docRef }: { docRef: DocReferenceInfo }) {
export function DocPeekPreview({
docRef,
animating,
}: {
docRef: DocReferenceInfo;
animating?: boolean;
}) {
const {
docId,
blockIds,
Expand All @@ -204,7 +210,8 @@ export function DocPeekPreview({ docRef }: { docRef: DocReferenceInfo }) {
databaseRowId,
type: 'database',
}
: undefined
: undefined,
!animating
);

// if sync engine has been synced and the page is null, show 404 page.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export type PeekViewModalContainerProps = PropsWithChildren<{
target?: HTMLElement;
controls?: React.ReactNode;
onAnimationStart?: () => void;
onAnimateEnd?: () => void;
onAnimationEnd?: () => void;
mode?: PeekViewMode;
animation?: PeekViewAnimation;
testId?: string;
Expand All @@ -76,17 +76,15 @@ export const PeekViewModalContainer = forwardRef<
controls,
children,
onAnimationStart,
onAnimateEnd,
onAnimationEnd,
animation = 'zoom',
mode = 'fit',
dialogFrame = true,
},
ref
) {
const [vtOpen, setVtOpen] = useState(open);
const [animeState, setAnimeState] = useState<'idle' | 'ready' | 'animating'>(
'idle'
);
const [animeState, setAnimeState] = useState<'idle' | 'animating'>('idle');
const contentClipRef = useRef<HTMLDivElement>(null);
const contentRef = useRef<HTMLDivElement>(null);
const overlayRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -143,6 +141,7 @@ export const PeekViewModalContainer = forwardRef<
if (!contentClip || !content || !target || !overlay) {
resolve();
setAnimeState('idle');
onAnimationEnd?.();
return;
}
const targets = contentClip;
Expand Down Expand Up @@ -196,6 +195,7 @@ export const PeekViewModalContainer = forwardRef<
complete: (ins: AnimeInstance) => {
paramsMap?.contentWrapper?.complete?.(ins);
setAnimeState('idle');
onAnimationEnd?.();
overlay.style.pointerEvents = '';
if (zoomIn) {
Object.assign(targets.style, {
Expand Down Expand Up @@ -238,6 +238,7 @@ export const PeekViewModalContainer = forwardRef<
*/
const animateZoomIn = useCallback(() => {
setAnimeState('animating');
onAnimationStart?.();
setVtOpen(true);
setTimeout(() => {
zoomAnimate(true, {
Expand All @@ -257,9 +258,10 @@ export const PeekViewModalContainer = forwardRef<
// controls delay: to make sure the time interval for animations of dialog and controls is 150ms.
400 - 230 + 150
);
}, [animateControls, zoomAnimate]);
}, [animateControls, onAnimationStart, zoomAnimate]);
const animateZoomOut = useCallback(() => {
setAnimeState('animating');
onAnimationStart?.();
animateControls(false);
zoomAnimate(false, {
contentWrapper: {
Expand All @@ -275,33 +277,38 @@ export const PeekViewModalContainer = forwardRef<
})
.then(() => setVtOpen(false))
.catch(console.error);
}, [animateControls, zoomAnimate]);
}, [animateControls, onAnimationStart, zoomAnimate]);

const animateFade = useCallback((animateIn: boolean) => {
setAnimeState('animating');
return new Promise<void>(resolve => {
if (animateIn) setVtOpen(true);
setTimeout(() => {
const overlay = overlayRef.current;
const contentClip = contentClipRef.current;
if (!overlay || !contentClip) {
resolve();
return;
}
anime({
targets: [overlay, contentClip],
opacity: animateIn ? [0, 1] : [1, 0],
easing: 'easeOutQuad',
duration: 230,
complete: () => {
if (!animateIn) setVtOpen(false);
setAnimeState('idle');
const animateFade = useCallback(
(animateIn: boolean) => {
setAnimeState('animating');
onAnimationStart?.();
return new Promise<void>(resolve => {
if (animateIn) setVtOpen(true);
setTimeout(() => {
const overlay = overlayRef.current;
const contentClip = contentClipRef.current;
if (!overlay || !contentClip) {
resolve();
},
return;
}
anime({
targets: [overlay, contentClip],
opacity: animateIn ? [0, 1] : [1, 0],
easing: 'easeOutQuad',
duration: 230,
complete: () => {
if (!animateIn) setVtOpen(false);
setAnimeState('idle');
onAnimationEnd?.();
resolve();
},
});
});
});
});
}, []);
},
[onAnimationEnd, onAnimationStart]
);

useEffect(() => {
const onKeyDown = (e: KeyboardEvent) => {
Expand Down Expand Up @@ -332,8 +339,6 @@ export const PeekViewModalContainer = forwardRef<
<PeekViewModalOverlay
ref={overlayRef}
className={styles.modalOverlay}
onAnimationStart={onAnimationStart}
onAnimationEnd={onAnimateEnd}
data-anime-state={animeState}
/>
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { toReactNode } from '@affine/component';
import { AIChatBlockPeekViewTemplate } from '@affine/core/blocksuite/presets/ai';
import { BlockComponent } from '@blocksuite/affine/block-std';
import { useLiveData, useService } from '@toeverything/infra';
import { useEffect, useMemo } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';

import type { ActivePeekView } from '../entities/peek-view';
import { PeekViewService } from '../services/peek-view';
Expand All @@ -19,12 +19,12 @@ import {
DocPeekViewControls,
} from './peek-view-controls';

function renderPeekView({ info }: ActivePeekView) {
function renderPeekView({ info }: ActivePeekView, animating?: boolean) {
if (info.type === 'template') {
return toReactNode(info.template);
}
if (info.type === 'doc') {
return <DocPeekPreview docRef={info.docRef} />;
return <DocPeekPreview docRef={info.docRef} animating={animating} />;
}

if (info.type === 'attachment' && info.docRef.blockIds?.[0]) {
Expand Down Expand Up @@ -77,13 +77,14 @@ const getMode = (info: ActivePeekView['info']) => {
};

const getRendererProps = (
activePeekView?: ActivePeekView
activePeekView?: ActivePeekView,
animating?: boolean
): Partial<PeekViewModalContainerProps> | undefined => {
if (!activePeekView) {
return;
}

const preview = renderPeekView(activePeekView);
const preview = renderPeekView(activePeekView, animating);
const controls = renderControls(activePeekView);
return {
children: preview,
Expand All @@ -106,12 +107,24 @@ export const PeekViewManagerModal = () => {
const activePeekView = useLiveData(peekViewEntity.active$);
const show = useLiveData(peekViewEntity.show$);

const [animating, setAnimating] = useState(false);

const onAnimationStart = useCallback(() => {
console.log('onAnimationStart');
setAnimating(true);
}, []);

const onAnimationEnd = useCallback(() => {
console.log('onAnimationEnd');
setAnimating(false);
}, []);

const renderProps = useMemo(() => {
if (!activePeekView) {
return;
}
return getRendererProps(activePeekView);
}, [activePeekView]);
return getRendererProps(activePeekView, animating);
}, [activePeekView, animating]);

useEffect(() => {
const subscription = peekViewEntity.show$.subscribe(() => {
Expand All @@ -135,6 +148,8 @@ export const PeekViewManagerModal = () => {
peekViewEntity.close();
}
}}
onAnimationStart={onAnimationStart}
onAnimationEnd={onAnimationEnd}
>
{renderProps?.children}
</PeekViewModalContainer>
Expand Down
45 changes: 38 additions & 7 deletions packages/frontend/core/src/modules/peek-view/view/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ export const useEditor = (
pageId: string,
preferMode?: DocMode,
preferSelector?: EditorSelector,
defaultOpenProperty?: DefaultOpenProperty
defaultOpenProperty?: DefaultOpenProperty,
canLoad?: boolean
) => {
const currentWorkspace = useService(WorkspaceService).workspace;
const docsService = useService(DocsService);
const docRecordList = docsService.list;
const [loading, setLoading] = useState(false);
const docListReady = useLiveData(docRecordList.isReady$);
const docRecord = docRecordList.doc$(pageId).value;
const preferModeRef = useRef(preferMode);
Expand All @@ -25,16 +27,40 @@ export const useEditor = (
const [doc, setDoc] = useState<Doc | null>(null);
const [editor, setEditor] = useState<Editor | null>(null);

useLayoutEffect(() => {
useEffect(() => {
if (!docRecord) {
return;
}
const { doc: opened, release } = docsService.open(pageId);
setDoc(opened);
let canceled = false;
let release: () => void;
setLoading(true);
const loaded = docsService.loaded(pageId);
if (loaded) {
setDoc(loaded.doc);
release = loaded.release;
setLoading(false);
} else if (canLoad) {
requestIdleCallback(
() => {
if (canceled) {
return;
}
const { doc: opened, release: _release } = docsService.open(pageId);
setDoc(opened);
release = _release;
setLoading(false);
},
{
timeout: 1000,
}
);
}
return () => {
release();
canceled = true;
release?.();
setLoading(false);
};
}, [docRecord, docsService, pageId]);
}, [canLoad, docRecord, docsService, pageId]);

useLayoutEffect(() => {
if (!doc) {
Expand All @@ -55,5 +81,10 @@ export const useEditor = (
return currentWorkspace.engine.doc.addPriority(pageId, 10);
}, [currentWorkspace, pageId]);

return { doc, editor, workspace: currentWorkspace, loading: !docListReady };
return {
doc,
editor,
workspace: currentWorkspace,
loading: !docListReady || loading,
};
};

0 comments on commit c1c4ed4

Please sign in to comment.