Files
AFFiNE-Mirror/apps/web/src/plugins/local/index.tsx
2023-03-01 19:26:55 -06:00

221 lines
6.7 KiB
TypeScript

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<RemWorkspaceFlavour.LOCAL> = {
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 (
<>
<PageDetailEditor
pageId={currentPageId}
blockSuiteWorkspace={currentWorkspace.blockSuiteWorkspace}
/>
</>
);
},
PageList: ({ blockSuiteWorkspace, onOpenPage }) => {
return (
<BlockSuitePageList
onOpenPage={onOpenPage}
blockSuiteWorkspace={blockSuiteWorkspace}
/>
);
},
SettingsDetail: ({
currentWorkspace,
onChangeTab,
currentTab,
onDeleteWorkspace,
onTransformWorkspace,
}) => {
return (
<WorkspaceSettingDetail
onDeleteWorkspace={onDeleteWorkspace}
onChangeTab={onChangeTab}
currentTab={currentTab}
workspace={currentWorkspace}
onTransferWorkspace={onTransformWorkspace}
/>
);
},
};