diff --git a/packages/hooks/src/__tests__/index.spec.ts b/packages/hooks/src/__tests__/index.spec.ts index 63e12f7898..6436142416 100644 --- a/packages/hooks/src/__tests__/index.spec.ts +++ b/packages/hooks/src/__tests__/index.spec.ts @@ -9,8 +9,10 @@ import type { Page } from '@blocksuite/store'; import { assertExists } from '@blocksuite/store'; import { Workspace as BlockSuiteWorkspace } from '@blocksuite/store'; import { renderHook } from '@testing-library/react'; +import { useBlockSuitePagePreview } from '@toeverything/hooks/use-block-suite-page-preview'; import { useBlockSuiteWorkspacePageIsPublic } from '@toeverything/hooks/use-block-suite-workspace-page-is-public'; import { useBlockSuiteWorkspacePageTitle } from '@toeverything/hooks/use-block-suite-workspace-page-title'; +import { useAtomValue } from 'jotai'; import { describe, expect, test, vitest } from 'vitest'; import { beforeEach } from 'vitest'; @@ -86,3 +88,24 @@ describe('useBlockSuiteWorkspacePageIsPublic', () => { expect(hook.result.current[0]).toBe(true); }); }); + +describe('useBlockSuitePagePreview', () => { + test('basic', async () => { + const page = blockSuiteWorkspace.getPage('page0') as Page; + const id = page.addBlock( + 'affine:paragraph', + { + text: new page.Text('Hello, world!'), + }, + page.getBlockByFlavour('affine:frame')[0].id + ); + const hook = renderHook(() => useAtomValue(useBlockSuitePagePreview(page))); + expect(hook.result.current).toBe('\nHello, 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!'); + }); +}); diff --git a/packages/hooks/src/use-block-suite-page-preview.ts b/packages/hooks/src/use-block-suite-page-preview.ts new file mode 100644 index 0000000000..276aafc77e --- /dev/null +++ b/packages/hooks/src/use-block-suite-page-preview.ts @@ -0,0 +1,32 @@ +import type { ParagraphBlockModel } from '@blocksuite/blocks/models'; +import type { Page } from '@blocksuite/store'; +import type { Atom } from 'jotai'; +import { atom } from 'jotai'; + +const weakMap = new WeakMap>(); + +const getPagePreviewText = (page: Page) => { + const paragraphBlocks = page.getBlockByFlavour( + 'affine:paragraph' + ) as ParagraphBlockModel[]; + const text = paragraphBlocks.map(block => block.text.toString()).join('\n'); + return text.slice(0, 30); +}; + +export function useBlockSuitePagePreview(page: Page): Atom { + if (weakMap.has(page)) { + return weakMap.get(page) as Atom; + } else { + const baseAtom = atom(getPagePreviewText(page)); + baseAtom.onMount = set => { + const disposable = page.slots.yUpdated.on(() => { + set(getPagePreviewText(page)); + }); + return () => { + disposable.dispose(); + }; + }; + weakMap.set(page, baseAtom); + return baseAtom; + } +}