import { config } from '@affine/env'; import { assertEquals, nanoid } from '@blocksuite/store'; import React from 'react'; import { IndexeddbPersistence } from 'y-indexeddb'; import { createLocalProviders } from '../../blocksuite'; import { PageNotFoundError } from '../../components/affine/affine-error-eoundary'; import { WorkspaceSettingDetail } from '../../components/affine/workspace-setting-detail'; import { BlockSuitePageList } from '../../components/blocksuite/block-suite-page-list'; import { PageDetailEditor } from '../../components/page-detail-editor'; import { LoadPriority, LocalWorkspace, RemWorkspaceFlavour, } from '../../shared'; import { createEmptyBlockSuiteWorkspace } from '../../utils'; import { WorkspacePlugin } from '..'; export const kStoreKey = 'affine-local-workspace'; export const LocalPlugin: WorkspacePlugin = { flavour: RemWorkspaceFlavour.LOCAL, loadPriority: LoadPriority.LOW, createWorkspace: async blockSuiteWorkspace => { let ids: string[] = []; try { ids = JSON.parse(localStorage.getItem(kStoreKey) ?? '[]'); if (!Array.isArray(ids)) { localStorage.setItem(kStoreKey, '[]'); ids = []; } } catch (e) { localStorage.setItem(kStoreKey, '[]'); ids = []; } const id = nanoid(); const persistence = new IndexeddbPersistence(id, blockSuiteWorkspace.doc); await persistence.whenSynced.then(() => { persistence.destroy(); }); ids.push(id); localStorage.setItem(kStoreKey, JSON.stringify(ids)); return id; }, deleteWorkspace: async workspace => { const id = workspace.id; let ids: string[]; try { ids = JSON.parse(localStorage.getItem(kStoreKey) ?? '[]'); if (!Array.isArray(ids)) { localStorage.setItem(kStoreKey, '[]'); ids = []; } } catch (e) { localStorage.setItem(kStoreKey, '[]'); ids = []; } const idx = ids.findIndex(x => x === id); if (idx === -1) { throw new Error('cannot find local workspace from localStorage'); } workspace.providers.forEach(p => p.cleanup()); ids.splice(idx, 1); assertEquals( ids.every(id => typeof id === 'string'), true ); localStorage.setItem(kStoreKey, JSON.stringify(ids)); }, prefetchData: async (dataCenter, signal) => { if (typeof window === 'undefined') { // SSR mode, no local data return; } if (signal?.aborted) { return; } let ids: string[]; try { ids = JSON.parse(localStorage.getItem(kStoreKey) ?? '[]'); if (!Array.isArray(ids)) { localStorage.setItem(kStoreKey, '[]'); ids = []; } } catch (e) { localStorage.setItem(kStoreKey, '[]'); ids = []; } if (config.enableIndexedDBProvider) { const workspaces = await Promise.all( ids.map(id => { const blockSuiteWorkspace = createEmptyBlockSuiteWorkspace(id); const workspace: LocalWorkspace = { id, flavour: RemWorkspaceFlavour.LOCAL, blockSuiteWorkspace: blockSuiteWorkspace, providers: [...createLocalProviders(blockSuiteWorkspace)], syncBinary: async () => { if (!config.enableIndexedDBProvider) { return { ...workspace, }; } const persistence = new IndexeddbPersistence( blockSuiteWorkspace.room as string, blockSuiteWorkspace.doc ); return persistence.whenSynced.then(() => { persistence.destroy(); return { ...workspace, }; }); }, }; return workspace.syncBinary(); }) ); workspaces.forEach(workspace => { if (workspace) { const exist = dataCenter.workspaces.findIndex( w => w.id === workspace.id ); if (exist === -1) { dataCenter.workspaces = [...dataCenter.workspaces, workspace]; } else { dataCenter.workspaces[exist] = workspace; dataCenter.workspaces = [...dataCenter.workspaces]; } } }); } if (dataCenter.workspaces.length === 0) { if (signal?.aborted) { return; } console.info('no local workspace found, create a new one'); const workspaceId = nanoid(); const blockSuiteWorkspace = createEmptyBlockSuiteWorkspace(workspaceId); blockSuiteWorkspace.meta.setName('Untitled Workspace'); localStorage.setItem(kStoreKey, JSON.stringify([workspaceId])); blockSuiteWorkspace.createPage(nanoid()); const workspace: LocalWorkspace = { id: workspaceId, flavour: RemWorkspaceFlavour.LOCAL, blockSuiteWorkspace: blockSuiteWorkspace, providers: [...createLocalProviders(blockSuiteWorkspace)], syncBinary: async () => { if (!config.enableIndexedDBProvider) { return { ...workspace, }; } const persistence = new IndexeddbPersistence( blockSuiteWorkspace.room as string, blockSuiteWorkspace.doc ); return persistence.whenSynced.then(() => { persistence.destroy(); return { ...workspace, }; }); }, }; await workspace.syncBinary(); if (signal?.aborted) { const persistence = new IndexeddbPersistence( blockSuiteWorkspace.room as string, blockSuiteWorkspace.doc ); await persistence.clearData(); return; } dataCenter.workspaces = [workspace]; } }, PageDetail: ({ currentWorkspace, currentPageId }) => { const page = currentWorkspace.blockSuiteWorkspace.getPage(currentPageId); if (!page) { throw new PageNotFoundError( currentWorkspace.blockSuiteWorkspace, currentPageId ); } return ( <> ); }, PageList: ({ blockSuiteWorkspace, onOpenPage }) => { return ( ); }, SettingsDetail: ({ currentWorkspace, onChangeTab, currentTab, onDeleteWorkspace, onTransformWorkspace, }) => { return ( ); }, };