feat: support migration (#2852)

This commit is contained in:
Alex Yang
2023-06-26 15:55:44 +08:00
committed by GitHub
parent 002e64c819
commit 8e82d1e02c
14 changed files with 395 additions and 119 deletions

View File

@@ -1,58 +1,119 @@
import { BlockSuiteEditor } from '@affine/component/block-suite-editor';
import { migrateToSubdoc } from '@affine/env/blocksuite';
import type {
LocalIndexedDBDownloadProvider,
LocalWorkspace,
} from '@affine/env/workspace';
import { WorkspaceFlavour } from '@affine/env/workspace';
import {
migrateLocalBlobStorage,
upgradeV1ToV2,
} from '@affine/workspace/migration';
import { createIndexedDBDownloadProvider } from '@affine/workspace/providers';
import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models';
import { Workspace } from '@blocksuite/store';
import { NoSsr } from '@mui/material';
import {
atom,
createStore,
Provider,
useAtom,
useAtomValue,
useSetAtom,
} from 'jotai';
import type { ReactElement } from 'react';
import { use, useCallback } from 'react';
import * as Y from 'yjs';
const { default: json } = await import('@affine-test/fixtures/output.json');
import { Suspense, use, useCallback } from 'react';
const workspace = new Workspace({
id: 'test-migration',
isSSR: typeof window === 'undefined',
const store = createStore();
const workspaceIdsAtom = atom<Promise<string[]>>(async () => {
if (typeof window === 'undefined') {
return [];
} else {
const idb = await import('idb');
const db = await idb.openDB('affine-local', 1);
return (await db
.transaction('workspace')
.objectStore('workspace')
.getAllKeys()) as string[];
}
});
const finalWorkspace = new Workspace({
id: 'test-migration-final',
isSSR: typeof window === 'undefined',
});
const targetIdAtom = atom<string | null>(null);
finalWorkspace.register(AffineSchemas).register(__unstableSchemas);
workspace.register(AffineSchemas).register(__unstableSchemas);
if (typeof window !== 'undefined') {
const length = Object.keys(json).length;
const binary = new Uint8Array(length);
for (let i = 0; i < length; i++) {
binary[i] = (json as any)[i];
const workspaceAtom = atom<Promise<Workspace>>(async get => {
const id = get(targetIdAtom);
if (!id) {
throw new Error('no id');
}
Y.applyUpdate(workspace.doc, binary);
{
// invoke data
workspace.doc.getMap('space:hello-world');
workspace.doc.getMap('space:meta');
}
const newDoc = migrateToSubdoc(workspace.doc);
Y.applyUpdate(finalWorkspace.doc, Y.encodeStateAsUpdate(newDoc));
finalWorkspace.doc.subdocs.forEach(finalSubdoc => {
newDoc.subdocs.forEach(subdoc => {
if (subdoc.guid === finalSubdoc.guid) {
Y.applyUpdate(finalSubdoc, Y.encodeStateAsUpdate(subdoc));
}
});
const workspace = new Workspace({
id,
isSSR: typeof window === 'undefined',
});
}
const MigrationInner = () => {
const page = finalWorkspace.getPage('hello-world');
workspace.register(AffineSchemas).register(__unstableSchemas);
const provider = createIndexedDBDownloadProvider(
workspace.id,
workspace.doc,
{
awareness: workspace.awarenessStore.awareness,
}
) as LocalIndexedDBDownloadProvider;
provider.sync();
await provider.whenReady;
const localWorkspace = {
id: workspace.id,
blockSuiteWorkspace: workspace,
flavour: WorkspaceFlavour.LOCAL,
} satisfies LocalWorkspace;
const newWorkspace = upgradeV1ToV2(localWorkspace);
await migrateLocalBlobStorage(localWorkspace.id, newWorkspace.id);
newWorkspace.blockSuiteWorkspace;
return newWorkspace.blockSuiteWorkspace;
});
const pageIdAtom = atom('hello-world');
const PageListSelect = () => {
const workspace = useAtomValue(workspaceAtom);
const setPageId = useSetAtom(pageIdAtom);
return (
<ul>
{workspace.meta.pageMetas.map(meta => (
<li
key={meta.id}
onClick={() => {
setPageId(meta.id);
}}
>
{meta.id}
</li>
))}
</ul>
);
};
const WorkspaceInner = () => {
const workspace = useAtomValue(workspaceAtom);
const pageId = useAtomValue(pageIdAtom);
const page = workspace.getPage(pageId);
const onInit = useCallback(() => {}, []);
if (!page) {
return <>loading...</>;
return <PageListSelect />;
}
if (!page.loaded) {
use(page.waitForLoaded());
}
return (
<>
<PageListSelect />
<BlockSuiteEditor page={page} mode="page" onInit={onInit} />;
</>
);
};
const MigrationInner = () => {
const ids = useAtomValue(workspaceIdsAtom);
const [id, setId] = useAtom(targetIdAtom);
return (
<div
style={{
@@ -60,15 +121,31 @@ const MigrationInner = () => {
height: '100vh',
}}
>
<BlockSuiteEditor page={page} mode="page" onInit={onInit} />
<ul>
{ids.map(id => (
<li
onClick={() => {
setId(id);
}}
key={id}
>
{id}
</li>
))}
</ul>
<Suspense fallback="loading...">{id && <WorkspaceInner />}</Suspense>
</div>
);
};
export default function MigrationPage(): ReactElement {
return (
<NoSsr>
<MigrationInner />
</NoSsr>
<Provider store={store}>
<NoSsr>
<Suspense>
<MigrationInner />
</Suspense>
</NoSsr>
</Provider>
);
}