diff --git a/packages/app/package.json b/packages/app/package.json index 35bdd7d1a7..23118065a8 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -9,10 +9,10 @@ "lint": "next lint" }, "dependencies": { - "@blocksuite/blocks": "0.3.0-alpha.6", - "@blocksuite/editor": "0.3.0-alpha.6", + "@blocksuite/blocks": "0.3.0-alpha.7", + "@blocksuite/editor": "0.3.0-alpha.7", "@blocksuite/icons": "^1.0.5", - "@blocksuite/store": "0.3.0-alpha.6", + "@blocksuite/store": "0.3.0-alpha.7", "@emotion/css": "^11.10.0", "@emotion/react": "^11.10.4", "@emotion/server": "^11.10.0", diff --git a/packages/app/src/components/workspace-slider-bar/index.tsx b/packages/app/src/components/workspace-slider-bar/index.tsx index 4291d310d2..44603c990c 100644 --- a/packages/app/src/components/workspace-slider-bar/index.tsx +++ b/packages/app/src/components/workspace-slider-bar/index.tsx @@ -50,7 +50,7 @@ export const WorkSpaceSliderBar = () => { const { triggerQuickSearchModal, triggerImportModal } = useModal(); const [show, setShow] = useState(false); const [showSubFavorite, setShowSubFavorite] = useState(false); - const { createPage } = useEditor(); + const { createPage, getPageMeta, openPage } = useEditor(); const router = useRouter(); return ( @@ -116,8 +116,10 @@ export const WorkSpaceSliderBar = () => { { - createPage(); + onClick={async () => { + const page = await createPage(); + const pageMeta = getPageMeta(page.id); + pageMeta && openPage(pageMeta.id); }} > New Page diff --git a/packages/app/src/providers/editor-provider/editor-provider.tsx b/packages/app/src/providers/editor-provider/editor-provider.tsx index 8d1931e967..212427b0ae 100644 --- a/packages/app/src/providers/editor-provider/editor-provider.tsx +++ b/packages/app/src/providers/editor-provider/editor-provider.tsx @@ -1,69 +1,34 @@ import type { EditorContainer } from '@blocksuite/editor'; -import { createContext, useContext, useEffect, useRef, useState } from 'react'; +import { createContext, useContext, useEffect, useState } from 'react'; import type { PropsWithChildren } from 'react'; import dynamic from 'next/dynamic'; import Loading from './loading'; import { Page, Workspace } from '@blocksuite/store'; -import { BlockSchema } from '@blocksuite/editor/dist/block-loader'; -import { useRouter } from 'next/router'; -import { QueryContent } from '@blocksuite/store/dist/workspace/search'; -export interface PageMeta { - id: string; - title: string; - favorite: boolean; - trash: boolean; - createDate: number; - trashDate: number | null; -} +import useEditorHandler from './useEditorHandler'; +import { EditorHandlers, PageMeta } from './interface'; + // Blocksuite has to be imported dynamically since it has a lot of effects -const DynamicEditor = dynamic(() => import('./initial-editor'), { +const DynamicEditor = dynamic(() => import('./editor-reactor'), { ssr: false, }); type EditorContextValue = { mode: EditorContainer['mode']; setMode: (mode: EditorContainer['mode']) => void; - currentPage: Page | null; - editor: EditorContainer | null; + currentPage: Page | void; + editor: EditorContainer | void; pageList: PageMeta[]; } & EditorHandlers; -type EditorHandlers = { - createPage: (pageId?: string) => void; - openPage: ( - pageId: string, - query?: { [key: string]: string } - ) => Promise; - deletePage: (pageId: string) => void; - recyclePage: (pageId: string) => void; - toggleDeletePage: (pageId: string) => void; - favoritePage: (pageId: string) => void; - unFavoritePage: (pageId: string) => void; - toggleFavoritePage: (pageId: string) => void; - permanentlyDeletePage: (pageId: string) => void; - search: (query: QueryContent) => any; -}; - type EditorContextProps = PropsWithChildren<{}>; export const EditorContext = createContext({ mode: 'page', setMode: () => {}, pageList: [], - currentPage: null, - editor: null, - createPage: () => {}, - openPage: async () => { - return false; - }, - deletePage: () => {}, - recyclePage: () => {}, - toggleDeletePage: () => {}, - favoritePage: () => {}, - unFavoritePage: () => {}, - toggleFavoritePage: () => {}, - permanentlyDeletePage: () => {}, - search: () => {}, + currentPage: undefined, + editor: undefined, + ...({} as EditorHandlers), }); export const useEditor = () => useContext(EditorContext); @@ -71,14 +36,14 @@ export const useEditor = () => useContext(EditorContext); export const EditorProvider = ({ children, }: PropsWithChildren) => { - const router = useRouter(); - const blockSchemaRef = useRef(null); - const [workspace, setWorkspace] = useState(null); - const [currentPage, setCurrentPage] = useState(null); + const [workspace, setWorkspace] = useState(); + const [currentPage, setCurrentPage] = useState(); const [pageList, setPageList] = useState([]); - const [editor, setEditor] = useState(null); + const [editor, setEditor] = useState(); const [mode, setMode] = useState('page'); + const editorHandlers = useEditorHandler(workspace); + useEffect(() => { const event = new CustomEvent('affine.switch-mode', { detail: mode }); window.dispatchEvent(event); @@ -88,9 +53,9 @@ export const EditorProvider = ({ if (!workspace) { return; } - setPageList(workspace.meta.pages as PageMeta[]); + setPageList(workspace.meta.pageMetas as PageMeta[]); workspace.meta.pagesUpdated.on(res => { - setPageList(workspace.meta.pages as PageMeta[]); + setPageList(workspace.meta.pageMetas as PageMeta[]); }); return () => { // TODO: Does it need to be removed? @@ -98,67 +63,20 @@ export const EditorProvider = ({ }; }, [workspace]); - const editorHandler: EditorHandlers = { - createPage: (pageId = new Date().getTime().toString()) => { - blockSchemaRef.current && - workspace?.createPage(pageId).register(blockSchemaRef.current); - }, - openPage: (pageId, query = {}) => { - return router.push({ - pathname: '/', - query: { - pageId, - ...query, - }, - }); - }, - deletePage: pageId => { - workspace?.setPageMeta(pageId, { trash: true }); - }, - recyclePage: pageId => { - workspace?.setPageMeta(pageId, { trash: false }); - }, - toggleDeletePage: pageId => { - const pageMeta = workspace?.meta.pages.find(p => p.id === pageId); - if (pageMeta) { - workspace?.setPageMeta(pageId, { trash: !pageMeta.trash }); - } - }, - favoritePage: pageId => { - workspace?.setPageMeta(pageId, { favorite: true }); - }, - permanentlyDeletePage: pageId => { - // TODO: workspace.meta.removePage or workspace.removePage? - workspace?.meta.removePage(pageId); - }, - unFavoritePage: pageId => { - workspace?.setPageMeta(pageId, { favorite: true }); - }, - toggleFavoritePage: pageId => { - const pageMeta = workspace?.meta.pages.find(p => p.id === pageId); - if (pageMeta) { - workspace?.setPageMeta(pageId, { favorite: !pageMeta.favorite }); - } - }, - search: (query: QueryContent) => { - if (query) { - return workspace?.search(query); - } - }, - }; - return ( { - if (!blockSchemaRef.current) { - blockSchemaRef.current = blockSchema; - } - }} setEditor={setEditor} setWorkspace={setWorkspace} setCurrentPage={setCurrentPage} diff --git a/packages/app/src/providers/editor-provider/initial-editor.tsx b/packages/app/src/providers/editor-provider/editor-reactor.tsx similarity index 71% rename from packages/app/src/providers/editor-provider/initial-editor.tsx rename to packages/app/src/providers/editor-provider/editor-reactor.tsx index 7cff95d876..982030d892 100644 --- a/packages/app/src/providers/editor-provider/initial-editor.tsx +++ b/packages/app/src/providers/editor-provider/editor-reactor.tsx @@ -2,6 +2,7 @@ import '@blocksuite/blocks'; import '@blocksuite/blocks/style'; import type { EditorContainer } from '@blocksuite/editor'; import { BlockSchema, createEditor } from '@blocksuite/editor'; +import { generateDefaultPageId, initialPage } from './utils'; import { useEffect } from 'react'; import pkg from '../../../package.json'; import { @@ -11,6 +12,7 @@ import { Page, } from '@blocksuite/store'; import { useRouter } from 'next/router'; +import { createPage } from '@/providers/editor-provider/utils'; const getEditorParams = () => { const providers = []; @@ -22,9 +24,8 @@ const getEditorParams = () => { ); providers.push(WebsocketDocProvider); } - if (params.get('syncMode') === 'indexeddb') { - providers.push(IndexedDBDocProvider); - } + + providers.push(IndexedDBDocProvider); return { room, @@ -32,29 +33,25 @@ const getEditorParams = () => { }; }; -const InitialEditor = ({ +const EditorReactor = ({ workspace, currentPage, - setBlockSchema, setEditor, setWorkspace, setCurrentPage, }: { - workspace: Workspace | null; - currentPage: Page | null; - setBlockSchema: (blockSchema: typeof BlockSchema) => void; + workspace: Workspace | void; + currentPage: Page | void; setEditor: (editor: EditorContainer) => void; setWorkspace: (workspace: Workspace) => void; setCurrentPage: (Page: Page) => void; }) => { - setBlockSchema(BlockSchema); - const router = useRouter(); useEffect(() => { const workspace = new Workspace({ ...getEditorParams(), - }); + }).register(BlockSchema); //@ts-ignore window.workspace = workspace; const indexDBProvider = workspace.providers.find( @@ -74,21 +71,20 @@ const InitialEditor = ({ return; } - // const initialPageId = - // workspace.meta.pages.find(p => p.id === (router.query.pageId as string)) - // ?.id ?? - // workspace.meta.pages[0]?.id ?? - // 'page0'; - const initialPageId = - workspace.meta.pages.find(p => p.id === (router.query.pageId as string)) - ?.id ?? 'page0'; - console.log('initialPageId', initialPageId); + workspace.meta.pageMetas.find( + p => p.id === (router.query.pageId as string) + )?.id ?? workspace.meta.pageMetas[0]?.id; - const initialPage = workspace - .createPage(initialPageId) - .register(BlockSchema); - setCurrentPage(initialPage); + if (!initialPageId) { + createPage(workspace!, generateDefaultPageId()).then(page => { + initialPage(page); + setCurrentPage(page); + }); + } else { + const page = workspace.getPage(initialPageId); + setCurrentPage(page); + } }, [workspace, router.query.pageId, setCurrentPage]); useEffect(() => { @@ -106,4 +102,4 @@ const InitialEditor = ({ return
; }; -export default InitialEditor; +export default EditorReactor; diff --git a/packages/app/src/providers/editor-provider/interface.ts b/packages/app/src/providers/editor-provider/interface.ts new file mode 100644 index 0000000000..3b5a2290ce --- /dev/null +++ b/packages/app/src/providers/editor-provider/interface.ts @@ -0,0 +1,28 @@ +import { Page } from '@blocksuite/store'; +import { QueryContent } from '@blocksuite/store/dist/workspace/search'; + +export interface PageMeta { + id: string; + title: string; + favorite: boolean; + trash: boolean; + createDate: number; + trashDate: number | null; +} + +export type EditorHandlers = { + createPage: (pageId?: string) => Promise; + openPage: ( + pageId: string, + query?: { [key: string]: string } + ) => Promise; + getPageMeta: (pageId: string) => PageMeta | void; + deletePage: (pageId: string) => void; + recyclePage: (pageId: string) => void; + toggleDeletePage: (pageId: string) => void; + favoritePage: (pageId: string) => void; + unFavoritePage: (pageId: string) => void; + toggleFavoritePage: (pageId: string) => void; + permanentlyDeletePage: (pageId: string) => void; + search: (query: QueryContent) => any; +}; diff --git a/packages/app/src/providers/editor-provider/useEditorHandler.ts b/packages/app/src/providers/editor-provider/useEditorHandler.ts new file mode 100644 index 0000000000..b5cf1f114a --- /dev/null +++ b/packages/app/src/providers/editor-provider/useEditorHandler.ts @@ -0,0 +1,65 @@ +import { QueryContent } from '@blocksuite/store/dist/workspace/search'; +import { createPage, initialPage, generateDefaultPageId } from './utils'; +import { Workspace } from '@blocksuite/store'; +import { useRouter } from 'next/router'; +import { EditorHandlers } from './interface'; + +export const useEditorHandler = (workspace?: Workspace): EditorHandlers => { + const router = useRouter(); + + return { + createPage: async (pageId = generateDefaultPageId()) => { + const page = await createPage(workspace!, pageId); + initialPage(page); + return page; + }, + getPageMeta(pageId: string) { + pageId = pageId.replace('space:', ''); + return workspace!.meta.pageMetas.find(page => page.id === pageId); + }, + openPage: (pageId, query = {}) => { + return router.push({ + pathname: '/', + query: { + pageId, + ...query, + }, + }); + }, + deletePage: pageId => { + workspace!.setPageMeta(pageId, { trash: true }); + }, + recyclePage: pageId => { + workspace!.setPageMeta(pageId, { trash: false }); + }, + toggleDeletePage: pageId => { + const pageMeta = workspace!.meta.pageMetas.find(p => p.id === pageId); + if (pageMeta) { + workspace!.setPageMeta(pageId, { trash: !pageMeta.trash }); + } + }, + favoritePage: pageId => { + workspace!.setPageMeta(pageId, { favorite: true }); + }, + permanentlyDeletePage: pageId => { + // TODO: workspace.meta.removePage or workspace.removePage? + workspace!.meta.removePage(pageId); + }, + unFavoritePage: pageId => { + workspace!.setPageMeta(pageId, { favorite: true }); + }, + toggleFavoritePage: pageId => { + const pageMeta = workspace?.meta.pageMetas.find(p => p.id === pageId); + if (pageMeta) { + workspace!.setPageMeta(pageId, { favorite: !pageMeta.favorite }); + } + }, + search: (query: QueryContent) => { + if (query) { + return workspace!.search(query); + } + }, + }; +}; + +export default useEditorHandler; diff --git a/packages/app/src/providers/editor-provider/utils.ts b/packages/app/src/providers/editor-provider/utils.ts index 262836b380..39bb4b68b0 100644 --- a/packages/app/src/providers/editor-provider/utils.ts +++ b/packages/app/src/providers/editor-provider/utils.ts @@ -1,5 +1,6 @@ import { EditorContainer } from '@blocksuite/editor'; import exampleMarkdown from '@/providers/editor-provider/example-markdown'; +import { Page, Workspace } from '@blocksuite/store'; export const initDefaultContent = (editor: EditorContainer) => { const { page } = editor; @@ -11,3 +12,27 @@ export const initDefaultContent = (editor: EditorContainer) => { editor.clipboard.importMarkdown(exampleMarkdown, `${groupId}`); page.resetHistory(); }; + +export const createPage = ( + workspace: Workspace, + pageId: string +): Promise => { + return new Promise(resolve => { + workspace.createPage(pageId); + workspace.signals.pageAdded.once(addedPageId => { + const page = workspace!.getPage(addedPageId); + resolve(page); + }); + }); +}; + +export const initialPage = (page: Page) => { + const pageBlockId = page.addBlock({ flavour: 'affine:page' }); + const groupId = page.addBlock({ flavour: 'affine:group' }, pageBlockId); + page.addBlock({ flavour: 'affine:paragraph' }, groupId); + return page; +}; + +export const generateDefaultPageId = () => { + return new Date().getTime().toString(); +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b822c6637e..8457a583e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,10 +22,10 @@ importers: packages/app: specifiers: - '@blocksuite/blocks': 0.3.0-alpha.6 - '@blocksuite/editor': 0.3.0-alpha.6 + '@blocksuite/blocks': 0.3.0-alpha.7 + '@blocksuite/editor': 0.3.0-alpha.7 '@blocksuite/icons': ^1.0.5 - '@blocksuite/store': 0.3.0-alpha.6 + '@blocksuite/store': 0.3.0-alpha.7 '@emotion/css': ^11.10.0 '@emotion/react': ^11.10.4 '@emotion/server': ^11.10.0 @@ -54,10 +54,10 @@ importers: react-dom: 18.2.0 typescript: 4.8.3 dependencies: - '@blocksuite/blocks': 0.3.0-alpha.6 - '@blocksuite/editor': 0.3.0-alpha.6 + '@blocksuite/blocks': 0.3.0-alpha.7 + '@blocksuite/editor': 0.3.0-alpha.7 '@blocksuite/icons': 1.0.5_w5j4k42lgipnm43s3brx6h3c34 - '@blocksuite/store': 0.3.0-alpha.6 + '@blocksuite/store': 0.3.0-alpha.7 '@emotion/css': 11.10.0 '@emotion/react': 11.10.4_w5j4k42lgipnm43s3brx6h3c34 '@emotion/server': 11.10.0_@emotion+css@11.10.0 @@ -174,12 +174,14 @@ packages: to-fast-properties: 2.0.0 dev: false - /@blocksuite/blocks/0.3.0-alpha.6: - resolution: {integrity: sha512-pa8wP0hRIDlufae+ABAQFLW8TNOGsfh8AqyTKF/+LVYFCGr+9tlW55X3OlWtU51vFxawst1Wd3FnjCAW6XiaKQ==} + /@blocksuite/blocks/0.3.0-alpha.7: + resolution: {integrity: sha512-RV6yiKSVIr3hRkT1MYp15Bt7i615aahqQ7CEJZQAuDMr2OVRzAGeXJpjt3e69HWW5os4fs4yOsC1PiS8VuuWRg==} dependencies: - '@blocksuite/store': 0.3.0-alpha.6 + '@blocksuite/store': 0.3.0-alpha.7 + '@tldraw/vec': 1.8.0 hotkeys-js: 3.10.0 lit: 2.4.0 + perfect-freehand: 1.2.0 quill: 1.3.7 quill-cursors: 4.0.0 transitivePeerDependencies: @@ -188,11 +190,11 @@ packages: - utf-8-validate dev: false - /@blocksuite/editor/0.3.0-alpha.6: - resolution: {integrity: sha512-2wFkOlCKqC4BrfCIzuxjUyVnE/mmGOyUnqVTQNmHrToDS81RLIq0r+lj1IrzLv+LZQCfTyhoAlJyaxUNrUw3sw==} + /@blocksuite/editor/0.3.0-alpha.7: + resolution: {integrity: sha512-vq5xgg2Ea1kbYDCYogBnmccg3Opw2DfQeiEQM5JRxqRBe6E2R4lZqZvksVj5fekY5Pl2Nrr73VgxTutjGS1u2g==} dependencies: - '@blocksuite/blocks': 0.3.0-alpha.6 - '@blocksuite/store': 0.3.0-alpha.6 + '@blocksuite/blocks': 0.3.0-alpha.7 + '@blocksuite/store': 0.3.0-alpha.7 lit: 2.4.0 marked: 4.1.1 turndown: 7.1.1 @@ -212,8 +214,8 @@ packages: react: 18.2.0 dev: false - /@blocksuite/store/0.3.0-alpha.6: - resolution: {integrity: sha512-rI7cUVSqoMh1bsNLwL8MAgyaUq0Ix+SRBqweYwVjeDoqFIbHjnRSwkVw48K/FgxN6ekN96HLSf3Sj/HK1ivPpw==} + /@blocksuite/store/0.3.0-alpha.7: + resolution: {integrity: sha512-dO+hx/6V5aun5+U1M7qRSQLdHpXYNa2TDr9747f+Xy8CoToyYQ5jTJ/KcjKEdNm36xg66N3sNBDarhAUzs3QOg==} dependencies: buffer: 6.0.3 flexsearch: 0.7.21 @@ -1087,6 +1089,10 @@ packages: tslib: 2.4.0 dev: false + /@tldraw/vec/1.8.0: + resolution: {integrity: sha512-GiS5Df3CzXY/fPBFcM0CKFERZfI4Cg1X33VPZX+NLo7Fwm/h9zu/aU24N1mG75Q9LuMnwKm7woxKr8BiUXGYCg==} + dev: false + /@types/json5/0.0.29: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true @@ -1871,7 +1877,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.38.0_76twfck5d7crjqrmw4yltga7zm + '@typescript-eslint/parser': 5.38.0_eslint@8.22.0 debug: 3.2.7 eslint: 8.22.0 eslint-import-resolver-node: 0.3.6 @@ -1890,7 +1896,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.38.0_76twfck5d7crjqrmw4yltga7zm + '@typescript-eslint/parser': 5.38.0_eslint@8.22.0 array-includes: 3.1.5 array.prototype.flat: 1.3.0 debug: 2.6.9 @@ -3033,6 +3039,10 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + /perfect-freehand/1.2.0: + resolution: {integrity: sha512-h/0ikF1M3phW7CwpZ5MMvKnfpHficWoOEyr//KVNTxV4F6deRK1eYMtHyBKEAKFK0aXIEUK9oBvlF6PNXMDsAw==} + dev: false + /picocolors/1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} dev: false