diff --git a/packages/frontend/hooks/src/__tests__/index.spec.ts b/packages/frontend/hooks/src/__tests__/index.spec.ts index 612f67eea1..88ecbfb611 100644 --- a/packages/frontend/hooks/src/__tests__/index.spec.ts +++ b/packages/frontend/hooks/src/__tests__/index.spec.ts @@ -76,12 +76,25 @@ describe('useBlockSuitePagePreview', () => { page.getBlockByFlavour('affine:note')[0].id ); const hook = renderHook(() => useAtomValue(useBlockSuitePagePreview(page))); - expect(hook.result.current).toBe('\nHello, world!'); + expect(hook.result.current).toBe('Hello, world!'); page.transact(() => { page.getBlockById(id)!.text!.insert('Test', 0); }); await new Promise(resolve => setTimeout(resolve, 100)); hook.rerender(); - expect(hook.result.current).toBe('\nTestHello, world!'); + expect(hook.result.current).toBe('TestHello, world!'); + + // Insert before + page.addBlock( + 'affine:paragraph', + { + text: new page.Text('First block!'), + }, + page.getBlockByFlavour('affine:note')[0].id, + 0 + ); + await new Promise(resolve => setTimeout(resolve, 100)); + hook.rerender(); + expect(hook.result.current).toBe('First block! TestHello, world!'); }); }); diff --git a/packages/frontend/hooks/src/use-block-suite-page-preview.ts b/packages/frontend/hooks/src/use-block-suite-page-preview.ts index 3947839f67..869e56b555 100644 --- a/packages/frontend/hooks/src/use-block-suite-page-preview.ts +++ b/packages/frontend/hooks/src/use-block-suite-page-preview.ts @@ -1,20 +1,49 @@ -import type { ParagraphBlockModel } from '@blocksuite/blocks/models'; import type { Page } from '@blocksuite/store'; import type { Atom } from 'jotai'; import { atom } from 'jotai'; +const MAX_PREVIEW_LENGTH = 150; +const MAX_SEARCH_BLOCK_COUNT = 30; + const weakMap = new WeakMap>(); export const getPagePreviewText = (page: Page) => { - // TODO this is incorrect, since the order of blocks is not guaranteed - const paragraphBlocks = page.getBlockByFlavour( - 'affine:paragraph' - ) as ParagraphBlockModel[]; - const text = paragraphBlocks - .slice(0, 10) - .map(block => block.text.toString()) - .join('\n'); - return text.slice(0, 300); + const pageRoot = page.root; + if (!pageRoot) { + return ''; + } + const preview: string[] = []; + // DFS + const queue = [pageRoot]; + let previewLenNeeded = MAX_PREVIEW_LENGTH; + let count = MAX_SEARCH_BLOCK_COUNT; + while (queue.length && previewLenNeeded > 0 && count-- > 0) { + const block = queue.shift(); + if (!block) { + console.error('Unexpected empty block'); + break; + } + if (block.children) { + queue.unshift(...block.children); + } + if (block.role !== 'content') { + continue; + } + if (block.text) { + const text = block.text.toString(); + if (!text.length) { + continue; + } + previewLenNeeded -= text.length; + preview.push(text); + } else { + // image/attachment/bookmark + const type = block.flavour.split('affine:')[1] ?? null; + previewLenNeeded -= type.length + 2; + type && preview.push(`[${type}]`); + } + } + return preview.join(' '); }; const emptyAtom = atom('');