feat: router & block preview

This commit is contained in:
DarkSky
2022-07-24 21:56:03 +08:00
parent 4f1c47a3bc
commit a4132f5f8a
8 changed files with 153 additions and 100 deletions

View File

@@ -32,6 +32,7 @@
"code-example": "^3.3.6",
"codemirror": "6.0.1",
"keymap": "link:@codemirror/next/keymap",
"nanoid": "^4.0.0",
"react-resizable": "^3.0.4",
"react-window": "^1.8.7",
"slate": "^0.81.1",

View File

@@ -0,0 +1,132 @@
import { memo, useEffect, useRef, useState } from 'react';
import { nanoid } from 'nanoid';
import { StyledBlockPreview } from '@toeverything/components/common';
import { services } from '@toeverything/datasource/db-service';
import { AsyncBlock } from '@toeverything/framework/virgo';
import { debounce, sleep } from '@toeverything/utils';
const updateTitle = async (
workspace: string,
blockId: string,
onFinal: (title?: string) => void,
retry = 0
) => {
const [page] = await services.api.editorBlock.search(workspace, {
tag: `id:${blockId}`,
});
if (page?.content) {
onFinal(page?.content);
} else if (retry < 20) {
await sleep(500);
updateTitle(workspace, blockId, onFinal, retry + 1);
} else {
onFinal('Untitled');
}
};
const bindBlock = async (
handleId: string,
workspace: string,
blockId: string,
onFinal: (title: string) => void,
retry = 0
): Promise<() => void> | undefined => {
const block = await services.api.editorBlock.getBlock(workspace, blockId);
if (block.id === blockId) {
const debouncedOnFinal = debounce(onFinal, 100, { maxWait: 500 });
const onUpdated = () =>
updateTitle(workspace, blockId, debouncedOnFinal);
block.on('content', handleId, onUpdated);
onUpdated();
return () => block.off('content', handleId);
} else if (retry < 20) {
await sleep(500);
return bindBlock(handleId, workspace, blockId, onFinal, retry + 1);
} else {
onFinal('Untitled');
return undefined;
}
};
const useBlockTitle = (block: AsyncBlock, blockId: string) => {
const [title, setTitle] = useState('Loading...');
useEffect(() => {
let callback: any = undefined;
bindBlock(
block.id + nanoid(8),
block.workspace,
blockId,
setTitle
).then(cb => {
callback = cb;
});
return () => callback?.();
}, [block.id, block.workspace, blockId]);
return title;
};
type BlockPreviewProps = {
block: AsyncBlock;
blockId: string;
editorElement?: () => JSX.Element;
};
const InternalBlockPreview = (props: BlockPreviewProps) => {
const container = useRef<HTMLDivElement>();
const [preview, setPreview] = useState(true);
const title = useBlockTitle(props.block, props.blockId);
const AffineEditor = props.editorElement as any;
useEffect(() => {
if (container?.current) {
const element = container?.current;
const resizeObserver = new IntersectionObserver(entries => {
const height = entries?.[0]?.intersectionRect.height;
setPreview(height < 174);
});
resizeObserver.observe(element);
return () => resizeObserver.unobserve(element);
}
return undefined;
}, [container, props]);
return (
<div ref={container}>
<StyledBlockPreview title={title}>
{preview ? (
<span
style={{
display: 'flex',
justifyContent: 'center',
fontSize: '128px',
height: '480px',
alignItems: 'center',
color: '#5591ff',
}}
>
Preview
</span>
) : AffineEditor ? (
<AffineEditor
workspace={props.block.workspace}
rootBlockId={props.blockId}
/>
) : null}
</StyledBlockPreview>
</div>
);
};
export const BlockPreview = memo(InternalBlockPreview, (prev, next) => {
return (
prev.block.workspace === next.block.workspace &&
prev.blockId === next.blockId
);
});

View File

@@ -1,13 +1,11 @@
import { FC, memo, useEffect, useMemo, useRef, useState } from 'react';
import { FC } from 'react';
import { StyledBlockPreview } from '@toeverything/components/common';
import { styled } from '@toeverything/components/ui';
import { AsyncBlock, useRecastBlockScene } from '@toeverything/framework/virgo';
import { formatUrl } from './format-url';
import { SCENE_CONFIG } from '../../blocks/group/config';
import { services } from '@toeverything/datasource/db-service';
import { debounce } from '@toeverything/utils';
import { BlockPreview } from './BlockView';
const MouseMaskContainer = styled('div')({
position: 'absolute',
zIndex: 1,
@@ -47,96 +45,6 @@ const _getLinkStyle = (scene: string) => {
}
};
type BlockPreviewProps = {
block: AsyncBlock;
blockId: string;
editorElement?: () => JSX.Element;
};
const BlockPreview = (props: BlockPreviewProps) => {
const container = useRef<HTMLDivElement>();
const [preview, setPreview] = useState(true);
const [title, setTitle] = useState('Loading...');
useEffect(() => {
let callback: any = undefined;
services.api.editorBlock
.getBlock(props.block.workspace, props.blockId)
.then(block => {
if (block.id === props.blockId) {
const updateTitle = debounce(
async () => {
const [page] =
await services.api.editorBlock.search(
props.block.workspace,
{ tag: 'id:affine67Uz4DstDk6PKUbz' }
);
console.log(page);
setTitle(page?.content || 'Untitled');
},
100,
{ maxWait: 500 }
);
block.on('content', props.block.id, updateTitle);
callback = () => block.off('content', props.block.id);
updateTitle();
} else {
setTitle('Untitled');
}
});
return () => callback?.();
}, [props.block.id, props.block.workspace, props.blockId]);
const AffineEditor = props.editorElement as any;
useEffect(() => {
if (container?.current) {
const element = container?.current;
const resizeObserver = new IntersectionObserver(entries => {
const height = entries?.[0]?.intersectionRect.height;
setPreview(height < 174);
});
resizeObserver.observe(element);
return () => resizeObserver.unobserve(element);
}
return undefined;
}, [container]);
return (
<div ref={container}>
<StyledBlockPreview title={title}>
{preview ? (
<span
style={{
display: 'flex',
justifyContent: 'center',
fontSize: '128px',
height: '480px',
alignItems: 'center',
color: '#5591ff',
}}
>
Preview
</span>
) : AffineEditor ? (
<AffineEditor
workspace={props.block.workspace}
rootBlockId={props.blockId}
/>
) : null}
</StyledBlockPreview>
</div>
);
};
const MemoBlockPreview = memo(BlockPreview, (prev, next) => {
return (
prev.block.workspace === next.block.workspace &&
prev.blockId === next.blockId
);
});
const SourceViewContainer = styled('div')<{
isSelected: boolean;
scene: string;
@@ -186,7 +94,7 @@ export const SourceView: FC<Props> = props => {
scene={SCENE_CONFIG.REFLINK}
style={{ padding: '0' }}
>
<MemoBlockPreview
<BlockPreview
block={block}
editorElement={editorElement}
blockId={src}