mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-08 12:45:55 +08:00
7463e87742
At the moment on desktop the user's local blob data will be lost after enable cloud. This is because blob data is only synced from old idb to new idb, but not sync into sqlitedb. This pr will simply clone the db file for desktop app. It should also speed up the time when enabling cloud for a large local workspace.
168 lines
4.6 KiB
TypeScript
168 lines
4.6 KiB
TypeScript
import type {
|
|
AffineCloudWorkspace,
|
|
WorkspaceCRUD,
|
|
} from '@affine/env/workspace';
|
|
import { WorkspaceFlavour } from '@affine/env/workspace';
|
|
import {
|
|
createWorkspaceMutation,
|
|
deleteWorkspaceMutation,
|
|
getWorkspaceQuery,
|
|
getWorkspacesQuery,
|
|
} from '@affine/graphql';
|
|
import { createIndexeddbStorage, Workspace } from '@blocksuite/store';
|
|
import { migrateLocalBlobStorage } from '@toeverything/infra/blocksuite';
|
|
import { getSession } from 'next-auth/react';
|
|
import { proxy } from 'valtio/vanilla';
|
|
|
|
import { getOrCreateWorkspace } from '../manager';
|
|
import { fetcher } from './gql';
|
|
|
|
const Y = Workspace.Y;
|
|
|
|
async function deleteLocalBlobStorage(id: string) {
|
|
const storage = createIndexeddbStorage(id);
|
|
const keys = await storage.crud.list();
|
|
for (const key of keys) {
|
|
await storage.crud.delete(key);
|
|
}
|
|
}
|
|
|
|
// we don't need to persistence the state into local storage
|
|
// because if a user clicks create multiple time and nothing happened
|
|
// because of the server delay or something, he/she will wait.
|
|
// and also the user journey of creating workspace is long.
|
|
const createdWorkspaces = proxy<string[]>([]);
|
|
|
|
export const CRUD: WorkspaceCRUD<WorkspaceFlavour.AFFINE_CLOUD> = {
|
|
create: async upstreamWorkspace => {
|
|
if (createdWorkspaces.some(id => id === upstreamWorkspace.id)) {
|
|
throw new Error('workspace already created');
|
|
}
|
|
const { createWorkspace } = await fetcher({
|
|
query: createWorkspaceMutation,
|
|
variables: {
|
|
init: new File(
|
|
[Y.encodeStateAsUpdate(upstreamWorkspace.doc)],
|
|
'initBinary.yDoc'
|
|
),
|
|
},
|
|
});
|
|
createdWorkspaces.push(upstreamWorkspace.id);
|
|
const newBlockSuiteWorkspace = getOrCreateWorkspace(
|
|
createWorkspace.id,
|
|
WorkspaceFlavour.AFFINE_CLOUD
|
|
);
|
|
|
|
if (environment.isDesktop) {
|
|
// this will clone all data from existing db to new db file, including docs and blobs
|
|
await window.apis.workspace.clone(
|
|
upstreamWorkspace.id,
|
|
createWorkspace.id
|
|
);
|
|
|
|
// skip apply updates in memory and we will use providers to sync data from db
|
|
} else {
|
|
Y.applyUpdate(
|
|
newBlockSuiteWorkspace.doc,
|
|
Y.encodeStateAsUpdate(upstreamWorkspace.doc)
|
|
);
|
|
|
|
await Promise.all(
|
|
[...upstreamWorkspace.doc.subdocs].map(async subdoc => {
|
|
subdoc.load();
|
|
return subdoc.whenLoaded.then(() => {
|
|
newBlockSuiteWorkspace.doc.subdocs.forEach(newSubdoc => {
|
|
if (newSubdoc.guid === subdoc.guid) {
|
|
Y.applyUpdate(newSubdoc, Y.encodeStateAsUpdate(subdoc));
|
|
}
|
|
});
|
|
});
|
|
})
|
|
);
|
|
|
|
migrateLocalBlobStorage(upstreamWorkspace.id, createWorkspace.id)
|
|
.then(() => deleteLocalBlobStorage(upstreamWorkspace.id))
|
|
.catch(e => {
|
|
console.error('error when moving blob storage:', e);
|
|
});
|
|
// todo(himself65): delete old workspace in the future
|
|
}
|
|
|
|
return createWorkspace.id;
|
|
},
|
|
delete: async workspace => {
|
|
await fetcher({
|
|
query: deleteWorkspaceMutation,
|
|
variables: {
|
|
id: workspace.id,
|
|
},
|
|
});
|
|
},
|
|
get: async id => {
|
|
if (!environment.isServer && !navigator.onLine) {
|
|
// no network
|
|
return null;
|
|
}
|
|
if (
|
|
!(await getSession()
|
|
.then(() => true)
|
|
.catch(() => false))
|
|
) {
|
|
return null;
|
|
}
|
|
try {
|
|
await fetcher({
|
|
query: getWorkspaceQuery,
|
|
variables: {
|
|
id,
|
|
},
|
|
});
|
|
return {
|
|
id,
|
|
flavour: WorkspaceFlavour.AFFINE_CLOUD,
|
|
blockSuiteWorkspace: getOrCreateWorkspace(
|
|
id,
|
|
WorkspaceFlavour.AFFINE_CLOUD
|
|
),
|
|
} satisfies AffineCloudWorkspace;
|
|
} catch (e) {
|
|
console.error('error when fetching cloud workspace:', e);
|
|
return null;
|
|
}
|
|
},
|
|
list: async () => {
|
|
if (!environment.isServer && !navigator.onLine) {
|
|
// no network
|
|
return [];
|
|
}
|
|
if (
|
|
!(await getSession()
|
|
.then(() => true)
|
|
.catch(() => false))
|
|
) {
|
|
return [];
|
|
}
|
|
try {
|
|
const { workspaces } = await fetcher({
|
|
query: getWorkspacesQuery,
|
|
});
|
|
const ids = workspaces.map(({ id }) => id);
|
|
|
|
return ids.map(
|
|
id =>
|
|
({
|
|
id,
|
|
flavour: WorkspaceFlavour.AFFINE_CLOUD,
|
|
blockSuiteWorkspace: getOrCreateWorkspace(
|
|
id,
|
|
WorkspaceFlavour.AFFINE_CLOUD
|
|
),
|
|
}) satisfies AffineCloudWorkspace
|
|
);
|
|
} catch (e) {
|
|
console.error('error when fetching cloud workspaces:', e);
|
|
return [];
|
|
}
|
|
},
|
|
};
|