From 9253e9af0b3d69530dc8d830fa984104fe14c5e7 Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Wed, 14 Dec 2022 16:47:33 +0800 Subject: [PATCH] feat: add lifecycle hook in editor provider --- packages/app/next.config.js | 2 +- .../src/components/edgeless-toolbar/index.tsx | 20 +++----- packages/app/src/components/editor.tsx | 2 +- packages/app/src/components/header/header.tsx | 6 +-- .../app/src/components/header/page-header.tsx | 19 ++++--- .../src/components/page-list/date-cell.tsx | 4 +- .../app/src/components/page-list/index.tsx | 2 +- .../editor-provider/editor-provider.tsx | 49 ++++++++++++------- .../editor-provider/editor-reactor.tsx | 2 +- .../{ => hooks}/useEditorHandler.ts | 14 ++++-- .../hooks/useHistoryUpdated.ts | 34 +++++++++++++ .../editor-provider/hooks/usePropsUpdated.ts | 36 ++++++++++++++ .../providers/editor-provider/interface.ts | 26 +++++++--- 13 files changed, 155 insertions(+), 61 deletions(-) rename packages/app/src/providers/editor-provider/{ => hooks}/useEditorHandler.ts (84%) create mode 100644 packages/app/src/providers/editor-provider/hooks/useHistoryUpdated.ts create mode 100644 packages/app/src/providers/editor-provider/hooks/usePropsUpdated.ts diff --git a/packages/app/next.config.js b/packages/app/next.config.js index 93226e064f..8e3c3d3fe7 100644 --- a/packages/app/next.config.js +++ b/packages/app/next.config.js @@ -4,7 +4,7 @@ const { getGitVersion, getCommitHash } = require('./scripts/gitInfo'); /** @type {import('next').NextConfig} */ const nextConfig = { productionBrowserSourceMaps: true, - reactStrictMode: true, + reactStrictMode: false, swcMinify: false, publicRuntimeConfig: { NODE_ENV: process.env.NODE_ENV, diff --git a/packages/app/src/components/edgeless-toolbar/index.tsx b/packages/app/src/components/edgeless-toolbar/index.tsx index e5f0db27d9..30a2d43573 100644 --- a/packages/app/src/components/edgeless-toolbar/index.tsx +++ b/packages/app/src/components/edgeless-toolbar/index.tsx @@ -75,18 +75,14 @@ const toolbarList2 = [ const UndoRedo = () => { const [canUndo, setCanUndo] = useState(false); const [canRedo, setCanRedo] = useState(false); - const { currentPage } = useEditor(); - useEffect(() => { - if (!currentPage) return; + const { onHistoryUpdated, page } = useEditor(); - currentPage.signals.historyUpdated.on(() => { - setCanUndo(currentPage.canUndo); - setCanRedo(currentPage.canRedo); + useEffect(() => { + onHistoryUpdated(page => { + setCanUndo(page.canUndo); + setCanRedo(page.canRedo); }); - return () => { - currentPage.signals.historyUpdated.dispose(); - }; - }, [currentPage]); + }, [onHistoryUpdated]); return ( @@ -94,7 +90,7 @@ const UndoRedo = () => { { - currentPage?.undo(); + page?.undo(); }} > @@ -104,7 +100,7 @@ const UndoRedo = () => { { - currentPage?.redo(); + page?.redo(); }} > diff --git a/packages/app/src/components/editor.tsx b/packages/app/src/components/editor.tsx index dfb2a924c5..d1aa9f639b 100644 --- a/packages/app/src/components/editor.tsx +++ b/packages/app/src/components/editor.tsx @@ -3,7 +3,7 @@ import { useEditor, initDefaultContent } from '@/providers/editor-provider'; export const Editor = () => { const editorContainer = useRef(null); - const { editor } = useEditor(); + const { editor, onHistoryUpdated } = useEditor(); const ref = useRef(); useEffect(() => { if (editor && ref.current?.page.id !== editor?.page.id) { diff --git a/packages/app/src/components/header/header.tsx b/packages/app/src/components/header/header.tsx index a33b24e84b..7859953dbc 100644 --- a/packages/app/src/components/header/header.tsx +++ b/packages/app/src/components/header/header.tsx @@ -142,12 +142,8 @@ const HeaderRight = () => { }; export const Header = ({ children }: PropsWithChildren<{}>) => { - const { pageList } = useEditor(); - const [showWarning, setShowWarning] = useState(shouldShowWarning()); - const router = useRouter(); - const currentPageMeta = pageList.find(p => p.id === router.query.pageId); - const isTrash = !!currentPageMeta?.trash; + return ( { const [title, setTitle] = useState(''); const [isHover, setIsHover] = useState(false); - const { editor } = useEditor(); + const { editor, onPropsUpdated } = useEditor(); const { triggerQuickSearchModal } = useModal(); + useEffect(() => { - if (editor?.model) { - setTitle(editor.model.title || 'Untitled'); - editor.model.propsUpdated.on(() => { - setTitle(editor.model.title); - }); - } - return () => { - editor?.model?.propsUpdated.dispose(); - }; + onPropsUpdated(editor => { + setTitle(editor.model.title); + }); + }, [onPropsUpdated]); + + useEffect(() => { + setTitle(editor?.model.title || 'Untitled'); }, [editor]); return ( diff --git a/packages/app/src/components/page-list/date-cell.tsx b/packages/app/src/components/page-list/date-cell.tsx index 3b01a8bea8..e041bd79ac 100644 --- a/packages/app/src/components/page-list/date-cell.tsx +++ b/packages/app/src/components/page-list/date-cell.tsx @@ -16,7 +16,9 @@ export const DateCell = ({ // dayjs().format('L LT'); return ( - {dayjs(pageMeta[dateKey] as string).format('YYYY-MM-DD HH:MM')} + {pageMeta[dateKey] === undefined + ? '--' + : dayjs(pageMeta[dateKey] as string).format('YYYY-MM-DD HH:MM')} ); }; diff --git a/packages/app/src/components/page-list/index.tsx b/packages/app/src/components/page-list/index.tsx index 20030c06bc..3512694338 100644 --- a/packages/app/src/components/page-list/index.tsx +++ b/packages/app/src/components/page-list/index.tsx @@ -74,7 +74,7 @@ export const PageList = ({ {isTrash ? ( diff --git a/packages/app/src/providers/editor-provider/editor-provider.tsx b/packages/app/src/providers/editor-provider/editor-provider.tsx index 212427b0ae..72086fc89e 100644 --- a/packages/app/src/providers/editor-provider/editor-provider.tsx +++ b/packages/app/src/providers/editor-provider/editor-provider.tsx @@ -4,29 +4,28 @@ import type { PropsWithChildren } from 'react'; import dynamic from 'next/dynamic'; import Loading from './loading'; import { Page, Workspace } from '@blocksuite/store'; -import useEditorHandler from './useEditorHandler'; -import { EditorHandlers, PageMeta } from './interface'; +import useEditorHandler from './hooks/useEditorHandler'; +import { + EditorHandlers, + PageMeta, + EditorContextValue, + EditorContextProps, +} from './interface'; +import usePropsUpdated from '@/providers/editor-provider/hooks/usePropsUpdated'; +import useHistoryUpdated from '@/providers/editor-provider/hooks/useHistoryUpdated'; // Blocksuite has to be imported dynamically since it has a lot of effects const DynamicEditor = dynamic(() => import('./editor-reactor'), { ssr: false, }); -type EditorContextValue = { - mode: EditorContainer['mode']; - setMode: (mode: EditorContainer['mode']) => void; - currentPage: Page | void; - editor: EditorContainer | void; - pageList: PageMeta[]; -} & EditorHandlers; - -type EditorContextProps = PropsWithChildren<{}>; - export const EditorContext = createContext({ mode: 'page', setMode: () => {}, pageList: [], - currentPage: undefined, + page: undefined, + onPropsUpdated: () => {}, + onHistoryUpdated: () => {}, editor: undefined, ...({} as EditorHandlers), }); @@ -37,12 +36,24 @@ export const EditorProvider = ({ children, }: PropsWithChildren) => { const [workspace, setWorkspace] = useState(); - const [currentPage, setCurrentPage] = useState(); + const [page, setPage] = useState(); const [pageList, setPageList] = useState([]); const [editor, setEditor] = useState(); const [mode, setMode] = useState('page'); const editorHandlers = useEditorHandler(workspace); + const onPropsUpdated = usePropsUpdated(editor); + const onHistoryUpdated = useHistoryUpdated(page); + + // Modify the updatedDate when history change + useEffect(() => { + if (!workspace) { + return; + } + onHistoryUpdated(page => { + workspace.setPageMeta(page.id, { updatedDate: +new Date() }); + }); + }, [workspace, onHistoryUpdated]); useEffect(() => { const event = new CustomEvent('affine.switch-mode', { detail: mode }); @@ -67,21 +78,23 @@ export const EditorProvider = ({ - {workspace && currentPage && editor ? children : } + {workspace && page && editor ? children : } ); }; diff --git a/packages/app/src/providers/editor-provider/editor-reactor.tsx b/packages/app/src/providers/editor-provider/editor-reactor.tsx index 93019b5634..1637d8f278 100644 --- a/packages/app/src/providers/editor-provider/editor-reactor.tsx +++ b/packages/app/src/providers/editor-provider/editor-reactor.tsx @@ -89,7 +89,7 @@ const EditorReactor = ({ const savedPageId = workspace.meta.pageMetas[0]?.id; if (savedPageId) { - setCurrentPage(workspace.getPage(savedPageId)); + setCurrentPage(workspace.getPage(savedPageId) as Page); return; } diff --git a/packages/app/src/providers/editor-provider/useEditorHandler.ts b/packages/app/src/providers/editor-provider/hooks/useEditorHandler.ts similarity index 84% rename from packages/app/src/providers/editor-provider/useEditorHandler.ts rename to packages/app/src/providers/editor-provider/hooks/useEditorHandler.ts index 04199ff486..25c14ad7c2 100644 --- a/packages/app/src/providers/editor-provider/useEditorHandler.ts +++ b/packages/app/src/providers/editor-provider/hooks/useEditorHandler.ts @@ -1,8 +1,8 @@ import { QueryContent } from '@blocksuite/store/dist/workspace/search'; -import { createPage, initialPage, generateDefaultPageId } from './utils'; +import { createPage, initialPage, generateDefaultPageId } from '../utils'; import { Workspace } from '@blocksuite/store'; import { useRouter } from 'next/router'; -import { EditorHandlers } from './interface'; +import { EditorHandlers, PageMeta } from '../interface'; export const useEditorHandler = (workspace?: Workspace): EditorHandlers => { const router = useRouter(); @@ -15,7 +15,9 @@ export const useEditorHandler = (workspace?: Workspace): EditorHandlers => { }, getPageMeta(pageId: string) { pageId = pageId.replace('space:', ''); - return workspace!.meta.pageMetas.find(page => page.id === pageId); + return workspace!.meta.pageMetas.find( + page => page.id === pageId + ) as PageMeta; }, openPage: (pageId, query = {}) => { return router.push({ @@ -29,8 +31,10 @@ export const useEditorHandler = (workspace?: Workspace): EditorHandlers => { toggleDeletePage: pageId => { const pageMeta = workspace!.meta.pageMetas.find(p => p.id === pageId); if (pageMeta) { - workspace!.meta.setPage(pageId, { trashDate: new Date().getTime() }); - workspace!.setPageMeta(pageId, { trash: !pageMeta.trash }); + workspace!.setPageMeta(pageId, { + trash: !pageMeta.trash, + trashDate: +new Date(), + }); } }, favoritePage: pageId => { diff --git a/packages/app/src/providers/editor-provider/hooks/useHistoryUpdated.ts b/packages/app/src/providers/editor-provider/hooks/useHistoryUpdated.ts new file mode 100644 index 0000000000..558925d32b --- /dev/null +++ b/packages/app/src/providers/editor-provider/hooks/useHistoryUpdated.ts @@ -0,0 +1,34 @@ +import { useEffect, useRef } from 'react'; +import { Page } from '@blocksuite/store'; +import { EventCallBack } from '../interface'; + +export type UseHistoryUpdated = (page?: Page) => EventCallBack; + +export const useHistoryUpdate: UseHistoryUpdated = page => { + const callbackQueue = useRef<((page: Page) => void)[]>([]); + + useEffect(() => { + if (!page) { + return; + } + + setTimeout(() => { + page.signals.historyUpdated.on(() => { + callbackQueue.current.forEach(callback => { + callback(page); + }); + }); + }, 300); + + return () => { + callbackQueue.current = []; + page.signals.historyUpdated.dispose(); + }; + }, [page]); + + return callback => { + callbackQueue.current.push(callback); + }; +}; + +export default useHistoryUpdate; diff --git a/packages/app/src/providers/editor-provider/hooks/usePropsUpdated.ts b/packages/app/src/providers/editor-provider/hooks/usePropsUpdated.ts new file mode 100644 index 0000000000..e48eec882d --- /dev/null +++ b/packages/app/src/providers/editor-provider/hooks/usePropsUpdated.ts @@ -0,0 +1,36 @@ +import { useEffect, useRef } from 'react'; +import { EditorContainer } from '@blocksuite/editor'; +import { EventCallBack } from '../interface'; + +export type UsePropsUpdated = ( + editor?: EditorContainer +) => EventCallBack; + +export const usePropsUpdated: UsePropsUpdated = editor => { + const callbackQueue = useRef<((editor: EditorContainer) => void)[]>([]); + + useEffect(() => { + if (!editor?.model) { + return; + } + + setTimeout(() => { + editor.model?.propsUpdated.on(() => { + callbackQueue.current.forEach(callback => { + callback(editor); + }); + }); + }, 300); + + return () => { + callbackQueue.current = []; + editor?.model?.propsUpdated.dispose(); + }; + }, [editor]); + + return callback => { + callbackQueue.current.push(callback); + }; +}; + +export default usePropsUpdated; diff --git a/packages/app/src/providers/editor-provider/interface.ts b/packages/app/src/providers/editor-provider/interface.ts index e1fbfe514d..c0c8a2907f 100644 --- a/packages/app/src/providers/editor-provider/interface.ts +++ b/packages/app/src/providers/editor-provider/interface.ts @@ -1,14 +1,28 @@ +import { PropsWithChildren } from 'react'; import { Page } from '@blocksuite/store'; import { QueryContent } from '@blocksuite/store/dist/workspace/search'; +import { PageMeta as OriginalPageMeta } from '@blocksuite/store'; +import { EditorContainer } from '@blocksuite/editor'; -export interface PageMeta { - id: string; - title: string; +export type EventCallBack = (callback: (props: T) => void) => void; +export type EditorContextProps = PropsWithChildren<{}>; + +export type EditorContextValue = { + mode: EditorContainer['mode']; + setMode: (mode: EditorContainer['mode']) => void; + page: Page | void; + editor: EditorContainer | void; + pageList: PageMeta[]; + onHistoryUpdated: EventCallBack; + onPropsUpdated: EventCallBack; +} & EditorHandlers; + +export type PageMeta = { favorite: boolean; trash: boolean; - createDate: number; - trashDate: number | null; -} + trashDate: number | void; + updatedDate: number | void; +} & OriginalPageMeta; export type EditorHandlers = { createPage: (pageId?: string) => Promise;