refactor(infra): migrate to new infra (#5565)
This commit is contained in:
@@ -5,18 +5,26 @@ import {
|
||||
getWorkspacesQuery,
|
||||
} from '@affine/graphql';
|
||||
import { fetcher } from '@affine/graphql';
|
||||
import type { WorkspaceListProvider } from '@affine/workspace';
|
||||
import { globalBlockSuiteSchema } from '@affine/workspace';
|
||||
import { Workspace as BlockSuiteWorkspace } from '@blocksuite/store';
|
||||
import type { WorkspaceListProvider } from '@toeverything/infra';
|
||||
import {
|
||||
type BlobStorage,
|
||||
type SyncStorage,
|
||||
type WorkspaceInfo,
|
||||
type WorkspaceMetadata,
|
||||
} from '@toeverything/infra';
|
||||
import { globalBlockSuiteSchema } from '@toeverything/infra';
|
||||
import { difference } from 'lodash-es';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { getSession } from 'next-auth/react';
|
||||
import { applyUpdate, encodeStateAsUpdate } from 'yjs';
|
||||
|
||||
import { createLocalBlobStorage } from '../local/blob';
|
||||
import { createLocalStorage } from '../local/sync';
|
||||
import { IndexedDBBlobStorage } from '../local/blob-indexeddb';
|
||||
import { SQLiteBlobStorage } from '../local/blob-sqlite';
|
||||
import { IndexedDBSyncStorage } from '../local/sync-indexeddb';
|
||||
import { SQLiteSyncStorage } from '../local/sync-sqlite';
|
||||
import { CLOUD_WORKSPACE_CHANGED_BROADCAST_CHANNEL_KEY } from './consts';
|
||||
import { createAffineStaticStorage } from './sync';
|
||||
import { AffineStaticSyncStorage } from './sync';
|
||||
|
||||
async function getCloudWorkspaceList() {
|
||||
const session = await getSession();
|
||||
@@ -41,120 +49,134 @@ async function getCloudWorkspaceList() {
|
||||
}
|
||||
}
|
||||
|
||||
export function createCloudWorkspaceListProvider(): WorkspaceListProvider {
|
||||
const notifyChannel = new BroadcastChannel(
|
||||
export class CloudWorkspaceListProvider implements WorkspaceListProvider {
|
||||
name = WorkspaceFlavour.AFFINE_CLOUD;
|
||||
notifyChannel = new BroadcastChannel(
|
||||
CLOUD_WORKSPACE_CHANGED_BROADCAST_CHANNEL_KEY
|
||||
);
|
||||
|
||||
return {
|
||||
name: WorkspaceFlavour.AFFINE_CLOUD,
|
||||
async getList() {
|
||||
return getCloudWorkspaceList();
|
||||
},
|
||||
async create(initial) {
|
||||
const tempId = nanoid();
|
||||
getList(): Promise<WorkspaceMetadata[]> {
|
||||
return getCloudWorkspaceList();
|
||||
}
|
||||
async delete(workspaceId: string): Promise<void> {
|
||||
await fetcher({
|
||||
query: deleteWorkspaceMutation,
|
||||
variables: {
|
||||
id: workspaceId,
|
||||
},
|
||||
});
|
||||
// notify all browser tabs, so they can update their workspace list
|
||||
this.notifyChannel.postMessage(null);
|
||||
}
|
||||
async create(
|
||||
initial: (
|
||||
workspace: BlockSuiteWorkspace,
|
||||
blobStorage: BlobStorage
|
||||
) => Promise<void>
|
||||
): Promise<WorkspaceMetadata> {
|
||||
const tempId = nanoid();
|
||||
|
||||
const workspace = new BlockSuiteWorkspace({
|
||||
id: tempId,
|
||||
idGenerator: () => nanoid(),
|
||||
schema: globalBlockSuiteSchema,
|
||||
});
|
||||
const workspace = new BlockSuiteWorkspace({
|
||||
id: tempId,
|
||||
idGenerator: () => nanoid(),
|
||||
schema: globalBlockSuiteSchema,
|
||||
});
|
||||
|
||||
// create workspace on cloud, get workspace id
|
||||
const {
|
||||
createWorkspace: { id: workspaceId },
|
||||
} = await fetcher({
|
||||
query: createWorkspaceMutation,
|
||||
});
|
||||
// create workspace on cloud, get workspace id
|
||||
const {
|
||||
createWorkspace: { id: workspaceId },
|
||||
} = await fetcher({
|
||||
query: createWorkspaceMutation,
|
||||
});
|
||||
|
||||
// save the initial state to local storage, then sync to cloud
|
||||
const blobStorage = createLocalBlobStorage(workspaceId);
|
||||
const syncStorage = createLocalStorage(workspaceId);
|
||||
// save the initial state to local storage, then sync to cloud
|
||||
const blobStorage = environment.isDesktop
|
||||
? new SQLiteBlobStorage(workspaceId)
|
||||
: new IndexedDBBlobStorage(workspaceId);
|
||||
const syncStorage = environment.isDesktop
|
||||
? new SQLiteSyncStorage(workspaceId)
|
||||
: new IndexedDBSyncStorage(workspaceId);
|
||||
|
||||
// apply initial state
|
||||
await initial(workspace, blobStorage);
|
||||
// apply initial state
|
||||
await initial(workspace, blobStorage);
|
||||
|
||||
// save workspace to local storage, should be vary fast
|
||||
await syncStorage.push(workspaceId, encodeStateAsUpdate(workspace.doc));
|
||||
for (const subdocs of workspace.doc.getSubdocs()) {
|
||||
await syncStorage.push(subdocs.guid, encodeStateAsUpdate(subdocs));
|
||||
}
|
||||
// save workspace to local storage, should be vary fast
|
||||
await syncStorage.push(workspaceId, encodeStateAsUpdate(workspace.doc));
|
||||
for (const subdocs of workspace.doc.getSubdocs()) {
|
||||
await syncStorage.push(subdocs.guid, encodeStateAsUpdate(subdocs));
|
||||
}
|
||||
|
||||
// notify all browser tabs, so they can update their workspace list
|
||||
notifyChannel.postMessage(null);
|
||||
// notify all browser tabs, so they can update their workspace list
|
||||
this.notifyChannel.postMessage(null);
|
||||
|
||||
return workspaceId;
|
||||
},
|
||||
async delete(id) {
|
||||
await fetcher({
|
||||
query: deleteWorkspaceMutation,
|
||||
variables: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
// notify all browser tabs, so they can update their workspace list
|
||||
notifyChannel.postMessage(null);
|
||||
},
|
||||
subscribe(callback) {
|
||||
let lastWorkspaceIDs: string[] = [];
|
||||
return { id: workspaceId, flavour: WorkspaceFlavour.AFFINE_CLOUD };
|
||||
}
|
||||
subscribe(
|
||||
callback: (changed: {
|
||||
added?: WorkspaceMetadata[] | undefined;
|
||||
deleted?: WorkspaceMetadata[] | undefined;
|
||||
}) => void
|
||||
): () => void {
|
||||
let lastWorkspaceIDs: string[] = [];
|
||||
|
||||
function scan() {
|
||||
(async () => {
|
||||
const allWorkspaceIDs = (await getCloudWorkspaceList()).map(
|
||||
workspace => workspace.id
|
||||
);
|
||||
const added = difference(allWorkspaceIDs, lastWorkspaceIDs);
|
||||
const deleted = difference(lastWorkspaceIDs, allWorkspaceIDs);
|
||||
lastWorkspaceIDs = allWorkspaceIDs;
|
||||
callback({
|
||||
added: added.map(id => ({
|
||||
id,
|
||||
flavour: WorkspaceFlavour.AFFINE_CLOUD,
|
||||
})),
|
||||
deleted: deleted.map(id => ({
|
||||
id,
|
||||
flavour: WorkspaceFlavour.AFFINE_CLOUD,
|
||||
})),
|
||||
});
|
||||
})().catch(err => {
|
||||
console.error(err);
|
||||
function scan() {
|
||||
(async () => {
|
||||
const allWorkspaceIDs = (await getCloudWorkspaceList()).map(
|
||||
workspace => workspace.id
|
||||
);
|
||||
const added = difference(allWorkspaceIDs, lastWorkspaceIDs);
|
||||
const deleted = difference(lastWorkspaceIDs, allWorkspaceIDs);
|
||||
lastWorkspaceIDs = allWorkspaceIDs;
|
||||
callback({
|
||||
added: added.map(id => ({
|
||||
id,
|
||||
flavour: WorkspaceFlavour.AFFINE_CLOUD,
|
||||
})),
|
||||
deleted: deleted.map(id => ({
|
||||
id,
|
||||
flavour: WorkspaceFlavour.AFFINE_CLOUD,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
scan();
|
||||
|
||||
// rescan if other tabs notify us
|
||||
notifyChannel.addEventListener('message', scan);
|
||||
return () => {
|
||||
notifyChannel.removeEventListener('message', scan);
|
||||
};
|
||||
},
|
||||
async getInformation(id) {
|
||||
// get information from both cloud and local storage
|
||||
|
||||
// we use affine 'static' storage here, which use http protocol, no need to websocket.
|
||||
const cloudStorage = createAffineStaticStorage(id);
|
||||
const localStorage = createLocalStorage(id);
|
||||
// download root doc
|
||||
const localData = await localStorage.pull(id, new Uint8Array([]));
|
||||
const cloudData = await cloudStorage.pull(id, new Uint8Array([]));
|
||||
|
||||
if (!cloudData && !localData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bs = new BlockSuiteWorkspace({
|
||||
id,
|
||||
schema: globalBlockSuiteSchema,
|
||||
})().catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
if (localData) applyUpdate(bs.doc, localData.data);
|
||||
if (cloudData) applyUpdate(bs.doc, cloudData.data);
|
||||
scan();
|
||||
|
||||
return {
|
||||
name: bs.meta.name,
|
||||
avatar: bs.meta.avatar,
|
||||
};
|
||||
},
|
||||
};
|
||||
// rescan if other tabs notify us
|
||||
this.notifyChannel.addEventListener('message', scan);
|
||||
return () => {
|
||||
this.notifyChannel.removeEventListener('message', scan);
|
||||
};
|
||||
}
|
||||
async getInformation(id: string): Promise<WorkspaceInfo | undefined> {
|
||||
// get information from both cloud and local storage
|
||||
|
||||
// we use affine 'static' storage here, which use http protocol, no need to websocket.
|
||||
const cloudStorage: SyncStorage = new AffineStaticSyncStorage(id);
|
||||
const localStorage = environment.isDesktop
|
||||
? new SQLiteSyncStorage(id)
|
||||
: new IndexedDBSyncStorage(id);
|
||||
// download root doc
|
||||
const localData = await localStorage.pull(id, new Uint8Array([]));
|
||||
const cloudData = await cloudStorage.pull(id, new Uint8Array([]));
|
||||
|
||||
if (!cloudData && !localData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bs = new BlockSuiteWorkspace({
|
||||
id,
|
||||
schema: globalBlockSuiteSchema,
|
||||
});
|
||||
|
||||
if (localData) applyUpdate(bs.doc, localData.data);
|
||||
if (cloudData) applyUpdate(bs.doc, cloudData.data);
|
||||
|
||||
return {
|
||||
name: bs.meta.name,
|
||||
avatar: bs.meta.avatar,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user