refactor(infra): migrate to new infra (#5565)

This commit is contained in:
EYHN
2024-01-30 07:16:39 +00:00
parent 1e3499c323
commit 329fc19852
170 changed files with 2007 additions and 4354 deletions

View File

@@ -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,
};
}
}