Merge remote-tracking branch 'refs/remotes/origin/feat/datacenter-dev'

Conflicts:
	packages/app/src/components/create-workspace/index.tsx
	packages/data-center/package.json
	packages/data-center/src/index.ts
	pnpm-lock.yaml
This commit is contained in:
linonetwo
2023-01-12 17:11:27 +08:00
76 changed files with 1477 additions and 3796 deletions
+154 -159
View File
@@ -6,17 +6,30 @@ import type {
} from '../base';
import type { User } from '../../types';
import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store';
import { BlockSchema } from '@blocksuite/blocks/models';
import { storage } from './storage.js';
import assert from 'assert';
import { WebsocketProvider } from './sync.js';
// import { IndexedDBProvider } from '../local/indexeddb';
import { getApis } from './apis/index.js';
import { getApis, Workspace } from './apis/index.js';
import type { Apis, WorkspaceDetail, Callback } from './apis';
import { setDefaultAvatar } from '../utils.js';
import { MessageCode } from '../../message';
import { MessageCode } from '../../message/index.js';
import { token } from './apis/token.js';
import { WebsocketClient } from './channel';
import {
loadWorkspaceUnit,
createWorkspaceUnit,
syncToCloud,
} from './utils.js';
import { WorkspaceUnit } from '../../workspace-unit.js';
import { createBlocksuiteWorkspace, applyUpdate } from '../../utils/index.js';
import type { SyncMode } from '../../workspace-unit';
type ChannelMessage = {
ws_list: Workspace[];
ws_details: Record<string, WorkspaceDetail>;
metadata: Record<string, { avatar: string; name: string }>;
};
export interface AffineProviderConstructorParams
extends ProviderConstructorParams {
@@ -24,7 +37,7 @@ export interface AffineProviderConstructorParams
}
const {
Y: { applyUpdate, encodeStateAsUpdate },
Y: { encodeStateAsUpdate },
} = BlocksuiteWorkspace;
export class AffineProvider extends BaseProvider {
@@ -33,21 +46,12 @@ export class AffineProvider extends BaseProvider {
private _onTokenRefresh?: Callback = undefined;
private _wsMap: Map<string, WebsocketProvider> = new Map();
private _apis: Apis;
private _channel: WebsocketClient;
private _channel?: WebsocketClient;
// private _idbMap: Map<string, IndexedDBProvider> = new Map();
constructor({ apis, ...params }: AffineProviderConstructorParams) {
super(params);
this._apis = apis || getApis();
this._channel = new WebsocketClient(
`${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${
window.location.host
}/global/sync/`,
this._logger
);
if (token.isLogin) {
this._connectChannel();
}
}
override async init() {
@@ -76,14 +80,59 @@ export class AffineProvider extends BaseProvider {
} else {
storage.setItem('token', this._apis.token.refresh);
}
if (token.isLogin) {
this._connectChannel();
}
}
private _connectChannel() {
this._channel.connect();
if (!this._channel) {
this._channel = new WebsocketClient(
`${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${
window.location.host
}/api/global/sync/`,
this._logger,
{
params: {
token: this._apis.token.refresh,
},
}
);
}
this._channel.on('message', (msg: ChannelMessage) => {
this._handlerAffineListMessage(msg);
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this._channel.on('message', (message: any) => {
console.log('message', message);
private _handlerAffineListMessage({ ws_details, metadata }: ChannelMessage) {
this._logger('receive server message');
Object.entries(ws_details).forEach(async ([id, detail]) => {
const { name, avatar } = metadata[id];
assert(name);
const workspace = {
name: name,
avatar,
owner: {
name: detail.owner.name,
id: detail.owner.id,
email: detail.owner.email,
avatar: detail.owner.avatar_url,
},
published: detail.public,
memberCount: detail.member_count,
provider: 'affine',
syncMode: 'core' as SyncMode,
};
if (this._workspaces.get(id)) {
this._workspaces.update(id, workspace);
} else {
const workspaceUnit = await loadWorkspaceUnit(
{ id, ...workspace },
this._apis
);
this._workspaces.add(workspaceUnit);
}
});
}
@@ -104,16 +153,19 @@ export class AffineProvider extends BaseProvider {
return ws;
}
private async _applyCloudUpdates(blocksuiteWorkspace: BlocksuiteWorkspace) {
const { doc, room: workspaceId } = blocksuiteWorkspace;
private async _applyCloudUpdates(
blocksuiteWorkspace: BlocksuiteWorkspace,
published = false
) {
const { room: workspaceId } = blocksuiteWorkspace;
assert(workspaceId, 'Blocksuite Workspace without room(workspaceId).');
const updates = await this._apis.downloadWorkspace(workspaceId);
if (updates && updates.byteLength) {
await new Promise(resolve => {
doc.once('update', resolve);
BlocksuiteWorkspace.Y.applyUpdate(doc, new Uint8Array(updates));
});
}
const updates = await this._apis.downloadWorkspace(workspaceId, published);
await applyUpdate(blocksuiteWorkspace, new Uint8Array(updates));
}
override async loadPublicWorkspace(blocksuiteWorkspace: BlocksuiteWorkspace) {
await this._applyCloudUpdates(blocksuiteWorkspace, true);
return blocksuiteWorkspace;
}
override async warpWorkspace(workspace: BlocksuiteWorkspace) {
@@ -145,78 +197,25 @@ export class AffineProvider extends BaseProvider {
return [];
}
const workspacesList = await this._apis.getWorkspaces();
const workspaces: WorkspaceMeta0[] = workspacesList.map(w => {
return {
...w,
memberCount: 0,
name: '',
provider: 'affine',
syncMode: 'core',
};
});
const workspaceInstances = workspaces.map(({ id }) => {
const workspace =
this._workspacesCache.get(id) ||
new BlocksuiteWorkspace({
room: id,
}).register(BlockSchema);
this._workspacesCache.set(id, workspace);
if (workspace) {
return new Promise<BlocksuiteWorkspace>(resolve => {
this._apis.downloadWorkspace(id).then(data => {
applyUpdate(workspace.doc, new Uint8Array(data));
resolve(workspace);
});
});
} else {
return Promise.resolve(null);
}
});
(await Promise.all(workspaceInstances)).forEach((workspace, i) => {
if (workspace) {
workspaces[i] = {
...workspaces[i],
name: workspace.meta.name,
avatar: workspace.meta.avatar,
};
}
});
const getDetailList = workspacesList.map(w => {
const { id } = w;
return new Promise<{ id: string; detail: WorkspaceDetail | null }>(
resolve => {
this._apis.getWorkspaceDetail({ id }).then(data => {
resolve({ id, detail: data || null });
});
}
);
});
const ownerList = await Promise.all(getDetailList);
(await Promise.all(ownerList)).forEach(detail => {
if (detail) {
const { id, detail: workspaceDetail } = detail;
if (workspaceDetail) {
const { owner, member_count } = workspaceDetail;
const currentWorkspace = workspaces.find(w => w.id === id);
if (currentWorkspace) {
currentWorkspace.owner = {
id: owner.id,
name: owner.name,
avatar: owner.avatar_url,
email: owner.email,
};
currentWorkspace.memberCount = member_count;
}
}
}
});
workspaces.forEach(workspace => {
this._workspaces.add(workspace);
});
return workspaces;
const workspaceUnits = await Promise.all(
workspacesList.map(w => {
return loadWorkspaceUnit(
{
id: w.id,
name: '',
avatar: undefined,
owner: undefined,
published: w.public,
memberCount: 1,
provider: 'affine',
syncMode: 'core',
},
this._apis
);
})
);
this._workspaces.add(workspaceUnits);
return workspaceUnits;
}
override async auth() {
@@ -229,7 +228,7 @@ export class AffineProvider extends BaseProvider {
}
}
const user = await this._apis.signInWithGoogle?.();
if (!this._channel.connected) {
if (!this._channel?.connected) {
this._connectChannel();
}
if (!user) {
@@ -238,6 +237,7 @@ export class AffineProvider extends BaseProvider {
}
public override async getUserInfo(): Promise<User | undefined> {
await this.init();
const user = this._apis.token.user;
await this.init;
return user
@@ -302,52 +302,29 @@ export class AffineProvider extends BaseProvider {
// return workspace;
}
public override async createWorkspaceInfo(
public override async createWorkspace(
meta: CreateWorkspaceInfoParams
): Promise<WorkspaceMeta0> {
): Promise<WorkspaceUnit | undefined> {
const { id } = await this._apis.createWorkspace(meta);
const workspaceInfo: WorkspaceMeta0 = {
const workspaceUnit = await createWorkspaceUnit({
id,
name: meta.name,
id: id,
published: false,
avatar: '',
avatar: undefined,
owner: await this.getUserInfo(),
syncMode: 'core',
memberCount: 1,
provider: 'affine',
};
return workspaceInfo;
}
public override async createWorkspace(
blocksuiteWorkspace: BlocksuiteWorkspace,
meta: WorkspaceMeta0
): Promise<BlocksuiteWorkspace | undefined> {
const workspaceId = blocksuiteWorkspace.room;
assert(workspaceId, 'Blocksuite Workspace without room(workspaceId).');
this._logger('Creating affine workspace');
this._applyCloudUpdates(blocksuiteWorkspace);
this.linkLocal(blocksuiteWorkspace);
const workspaceInfo: WorkspaceMeta0 = {
name: meta.name,
id: workspaceId,
published: false,
avatar: '',
owner: undefined,
syncMode: 'core',
memberCount: 1,
provider: 'affine',
};
syncMode: 'core',
});
if (!blocksuiteWorkspace.meta.avatar) {
await setDefaultAvatar(blocksuiteWorkspace);
workspaceInfo.avatar = blocksuiteWorkspace.meta.avatar;
}
this._workspaces.add(workspaceInfo);
return blocksuiteWorkspace;
await syncToCloud(
workspaceUnit.blocksuiteWorkspace!,
this._apis.token.refresh
);
this._workspaces.add(workspaceUnit);
return workspaceUnit;
}
public override async publish(id: string, isPublish: boolean): Promise<void> {
@@ -358,38 +335,52 @@ export class AffineProvider extends BaseProvider {
workspace_id: string,
email: string
): Promise<User | null> {
const user = await this._apis.getUserByEmail({ workspace_id, email });
return user
const users = await this._apis.getUserByEmail({ workspace_id, email });
return users?.length
? {
id: user.id,
name: user.name,
avatar: user.avatar_url,
email: user.email,
id: users[0].id,
name: users[0].name,
avatar: users[0].avatar_url,
email: users[0].email,
}
: null;
}
public override async assign(
to: BlocksuiteWorkspace,
from: BlocksuiteWorkspace
): Promise<BlocksuiteWorkspace> {
assert(to.room, 'Blocksuite Workspace without room(workspaceId).');
const ws = this._getWebsocketProvider(to);
applyUpdate(to.doc, encodeStateAsUpdate(from.doc));
// TODO: upload blobs and make sure doc is synced
await new Promise<void>((resolve, reject) => {
ws.once('synced', () => {
setTimeout(() => resolve(), 1000);
});
ws.once('lost-connection', () => reject());
ws.once('connection-error', () => reject());
public override async extendWorkspace(
workspaceUnit: WorkspaceUnit
): Promise<WorkspaceUnit> {
const { id } = await this._apis.createWorkspace({
name: workspaceUnit.name,
});
return to;
const newWorkspaceUnit = new WorkspaceUnit({
id,
name: workspaceUnit.name,
avatar: undefined,
owner: await this.getUserInfo(),
published: false,
memberCount: 1,
provider: 'affine',
syncMode: 'core',
});
const blocksuiteWorkspace = createBlocksuiteWorkspace(id);
assert(workspaceUnit.blocksuiteWorkspace);
await applyUpdate(
blocksuiteWorkspace,
encodeStateAsUpdate(workspaceUnit.blocksuiteWorkspace.doc)
);
await syncToCloud(blocksuiteWorkspace, this._apis.token.refresh);
newWorkspaceUnit.setBlocksuiteWorkspace(blocksuiteWorkspace);
this._workspaces.add(newWorkspaceUnit);
return newWorkspaceUnit;
}
public override async logout(): Promise<void> {
token.clear();
this._channel.disconnect();
this._channel?.disconnect();
this._wsMap.forEach(ws => ws.disconnect());
storage.removeItem('token');
}
@@ -397,4 +388,8 @@ export class AffineProvider extends BaseProvider {
public override async getWorkspaceMembers(id: string) {
return this._apis.getWorkspaceMembers({ id });
}
public override async acceptInvitation(invitingCode: string): Promise<void> {
await this._apis.acceptInviting({ invitingCode });
}
}
@@ -15,7 +15,7 @@ export interface User {
export async function getUserByEmail(
params: GetUserByEmailParams
): Promise<User | null> {
): Promise<User[] | null> {
const searchParams = new URLSearchParams({ ...params });
return client.get('api/user', { searchParams }).json<User | null>();
return client.get('api/user', { searchParams }).json<User[] | null>();
}
@@ -84,7 +84,10 @@ export interface CreateWorkspaceParams {
export async function createWorkspace(
params: CreateWorkspaceParams
): Promise<{ id: string }> {
return client.post('api/workspace', { json: params }).json();
// FIXME: avatar should be optional
return client
.post('api/workspace', { json: { ...params, avatar: '123' } })
.json();
}
export interface UpdateWorkspaceParams {
@@ -169,7 +172,11 @@ export async function leaveWorkspace({ id }: LeaveWorkspaceParams) {
}
export async function downloadWorkspace(
workspaceId: string
workspaceId: string,
published = false
): Promise<ArrayBuffer> {
if (published) {
return bareClient.get(`api/public/doc/${workspaceId}`).arrayBuffer();
}
return client.get(`api/workspace/${workspaceId}/doc`).arrayBuffer();
}
@@ -1,6 +1,7 @@
import * as websocket from 'lib0/websocket';
import { Logger } from 'src/types';
import { token } from './apis/token';
import * as url from 'lib0/url';
const RECONNECT_INTERVAL_TIME = 5000;
const MAX_RECONNECT_TIMES = 50;
@@ -10,11 +11,21 @@ export class WebsocketClient extends websocket.WebsocketClient {
private _reconnectInterval: number | null = null;
private _logger: Logger;
constructor(
url: string,
serverUrl: string,
logger: Logger,
options?: { binaryType: 'arraybuffer' | 'blob' | null }
options?: ConstructorParameters<typeof websocket.WebsocketClient>[1] & {
params: Record<string, string>;
}
) {
super(url, options);
const params = options?.params || {};
// ensure that url is always ends with /
while (serverUrl[serverUrl.length - 1] === '/') {
serverUrl = serverUrl.slice(0, serverUrl.length - 1);
}
const encodedParams = url.encodeQueryParams(params);
const newUrl =
serverUrl + '/' + (encodedParams.length === 0 ? '' : '?' + encodedParams);
super(newUrl, options);
this._logger = logger;
this._setupChannel();
}
@@ -0,0 +1,90 @@
import assert from 'assert';
import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store';
import { WorkspaceUnit } from '../../workspace-unit.js';
import type { WorkspaceUnitCtorParams } from '../../workspace-unit';
import { createBlocksuiteWorkspace } from '../../utils/index.js';
import type { Apis } from './apis';
import { WebsocketProvider } from './sync.js';
import { setDefaultAvatar } from '../utils.js';
import { applyUpdate } from '../../utils/index.js';
export const loadWorkspaceUnit = async (
params: WorkspaceUnitCtorParams,
apis: Apis
) => {
const workspaceUnit = new WorkspaceUnit(params);
const blocksuiteWorkspace = createBlocksuiteWorkspace(workspaceUnit.id);
const updates = await apis.downloadWorkspace(
workspaceUnit.id,
params.published
);
applyUpdate(blocksuiteWorkspace, new Uint8Array(updates));
const details = await apis.getWorkspaceDetail({ id: workspaceUnit.id });
const owner = details?.owner;
workspaceUnit.setBlocksuiteWorkspace(blocksuiteWorkspace);
workspaceUnit.update({
name: blocksuiteWorkspace.meta.name,
avatar: blocksuiteWorkspace.meta.avatar,
memberCount: details?.member_count || 1,
owner: owner
? {
id: owner.id,
name: owner.name,
avatar: owner.avatar_url,
email: owner.email,
}
: undefined,
});
return workspaceUnit;
};
export const syncToCloud = async (
blocksuiteWorkspace: BlocksuiteWorkspace,
refreshToken: string
) => {
const workspaceId = blocksuiteWorkspace.room;
assert(workspaceId, 'Blocksuite workspace without room(workspaceId).');
const wsUrl = `${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${
window.location.host
}/api/sync/`;
const ws = new WebsocketProvider(
wsUrl,
workspaceId,
blocksuiteWorkspace.doc,
{
params: { token: refreshToken },
}
);
await new Promise((resolve, reject) => {
ws.once('synced', () => {
// FIXME: we don't when send local data to cloud successfully, so hack to wait 1s.
// Server will support this by add a new api.
setTimeout(resolve, 1000);
});
ws.once('lost-connection', () => reject());
ws.once('connection-error', () => reject());
});
};
export const createWorkspaceUnit = async (params: WorkspaceUnitCtorParams) => {
const workspaceUnit = new WorkspaceUnit(params);
const blocksuiteWorkspace = createBlocksuiteWorkspace(workspaceUnit.id);
blocksuiteWorkspace.meta.setName(workspaceUnit.name);
if (!workspaceUnit.avatar) {
await setDefaultAvatar(blocksuiteWorkspace);
workspaceUnit.update({ avatar: blocksuiteWorkspace.meta.avatar });
}
workspaceUnit.setBlocksuiteWorkspace(blocksuiteWorkspace);
return workspaceUnit;
};
+32 -24
View File
@@ -1,8 +1,8 @@
import { Workspace as BlocksuiteWorkspace, uuidv4 } from '@blocksuite/store';
import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store';
import { MessageCenter } from '../message';
import { Logger, User } from '../types';
import type { WorkspaceUnitCollectionScope } from '../workspace-unit-collection';
import type { WorkspaceUnitCtorParams } from '../workspace-unit';
import type { WorkspaceUnitCtorParams, WorkspaceUnit } from '../workspace-unit';
import { Member } from './affine/apis';
const defaultLogger = () => {
@@ -44,12 +44,6 @@ export class BaseProvider {
return;
}
public async createWorkspaceInfo(
params: CreateWorkspaceInfoParams
): Promise<WorkspaceMeta0> {
throw new Error(`provider: ${this.id} createWorkspaceInfo Not implemented`);
}
/**
* auth provider
*/
@@ -75,10 +69,19 @@ export class BaseProvider {
return workspace;
}
/**
* @deprecated Temporary for public workspace
* @param blocksuiteWorkspace
* @returns
*/
public async loadPublicWorkspace(blocksuiteWorkspace: BlocksuiteWorkspace) {
return blocksuiteWorkspace;
}
/**
* load workspaces
**/
public async loadWorkspaces(): Promise<WorkspaceMeta0[]> {
public async loadWorkspaces(): Promise<WorkspaceUnit[]> {
throw new Error(`provider: ${this.id} loadWorkSpace Not implemented`);
}
@@ -174,13 +177,18 @@ export class BaseProvider {
/**
* create workspace by workspace meta
* @param {WorkspaceMeta} meta
* @param {CreateWorkspaceInfoParams} meta
*/
public async createWorkspace(
blocksuiteWorkspace: BlocksuiteWorkspace,
meta: WorkspaceMeta0
): Promise<BlocksuiteWorkspace | undefined> {
return blocksuiteWorkspace;
meta: CreateWorkspaceInfoParams
): Promise<WorkspaceUnit | undefined> {
throw new Error(`provider: ${this.id} createWorkspace not implemented`);
}
public async extendWorkspace(
workspaceUnit: WorkspaceUnit
): Promise<WorkspaceUnit | undefined> {
throw new Error(`provider: ${this.id} extendWorkspace not implemented`);
}
/**
@@ -205,16 +213,6 @@ export class BaseProvider {
return workspace;
}
/**
* merge one workspaces to another
* @param workspace
* @returns
*/
public async assign(to: BlocksuiteWorkspace, from: BlocksuiteWorkspace) {
from;
return to;
}
/**
* get workspace members
* @param {string} workspaceId
@@ -224,4 +222,14 @@ export class BaseProvider {
workspaceId;
return Promise.resolve([]);
}
/**
* accept invitation
* @param {string} inviteCode
* @returns
*/
public async acceptInvitation(inviteCode: string): Promise<void> {
inviteCode;
return;
}
}
@@ -1,10 +1,13 @@
import assert from 'assert';
import * as idb from 'lib0/indexeddb.js';
import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store';
import { applyUpdate } from '../../../utils/index.js';
const { encodeStateAsUpdate } = BlocksuiteWorkspace.Y;
const { encodeStateAsUpdate, mergeUpdates } = BlocksuiteWorkspace.Y;
export const initStore = async (blocksuiteWorkspace: BlocksuiteWorkspace) => {
export const writeUpdatesToLocal = async (
blocksuiteWorkspace: BlocksuiteWorkspace
) => {
const workspaceId = blocksuiteWorkspace.room;
assert(workspaceId);
await idb.deleteDB(workspaceId);
@@ -18,3 +21,21 @@ export const initStore = async (blocksuiteWorkspace: BlocksuiteWorkspace) => {
await idb.addAutoKey(updatesStore, currState);
}
};
export const applyLocalUpdates = async (
blocksuiteWorkspace: BlocksuiteWorkspace
) => {
const workspaceId = blocksuiteWorkspace.room;
assert(workspaceId, 'Blocksuite workspace without room(workspaceId).');
const db = await idb.openDB(workspaceId, db =>
idb.createStores(db, [['updates', { autoIncrement: true }], ['custom']])
);
const [updatesStore] = idb.transact(db, ['updates']); // , 'readonly')
if (updatesStore) {
const updates = await idb.getAll(updatesStore);
const mergedUpdates = mergeUpdates(updates);
await applyUpdate(blocksuiteWorkspace, mergedUpdates);
}
return blocksuiteWorkspace;
};
@@ -16,12 +16,10 @@ test.describe.serial('local provider', () => {
let workspaceId: string | undefined;
test('create workspace', async () => {
const workspaceInfo = await provider.createWorkspaceInfo({
const workspaceUnit = await provider.createWorkspace({
name: workspaceName,
});
workspaceId = workspaceInfo.id;
const blocksuiteWorkspace = createBlocksuiteWorkspace(workspaceId);
await provider.createWorkspace(blocksuiteWorkspace, workspaceInfo);
workspaceId = workspaceUnit?.id;
expect(workspaceMetaCollection.workspaces.length).toEqual(1);
expect(workspaceMetaCollection.workspaces[0].name).toEqual(workspaceName);
@@ -8,9 +8,9 @@ import type {
import { varStorage as storage } from 'lib0/storage';
import { Workspace as BlocksuiteWorkspace, uuidv4 } from '@blocksuite/store';
import { IndexedDBProvider } from './indexeddb/indexeddb.js';
import { initStore } from './indexeddb/utils.js';
import assert from 'assert';
import { setDefaultAvatar } from '../utils.js';
import { loadWorkspaceUnit, createWorkspaceUnit } from './utils.js';
import type { WorkspaceUnit } from '../../workspace-unit';
const WORKSPACE_KEY = 'workspaces';
@@ -22,21 +22,12 @@ export class LocalProvider extends BaseProvider {
super(params);
}
private _storeWorkspaces(workspaces: WorkspaceMeta0[]) {
private _storeWorkspaces(workspaceUnits: WorkspaceUnit[]) {
storage.setItem(
WORKSPACE_KEY,
JSON.stringify(
workspaces.map(w => {
return {
id: w.id,
name: w.name,
avatar: w.avatar,
owner: w.owner,
published: w.published,
memberCount: w.memberCount,
provider: w.provider,
syncMode: w.syncMode,
};
workspaceUnits.map(w => {
return w.toJSON();
})
)
);
@@ -61,20 +52,23 @@ export class LocalProvider extends BaseProvider {
return workspace;
}
override loadWorkspaces(): Promise<WorkspaceMeta0[]> {
override async loadWorkspaces(): Promise<WorkspaceUnit[]> {
const workspaceStr = storage.getItem(WORKSPACE_KEY);
let workspaces: WorkspaceMeta0[] = [];
if (workspaceStr) {
try {
workspaces = JSON.parse(workspaceStr) as WorkspaceMeta0[];
workspaces.forEach(workspace => {
this._workspaces.add(workspace);
});
const workspaceMetas = JSON.parse(workspaceStr) as WorkspaceMeta0[];
const workspaceUnits = await Promise.all(
workspaceMetas.map(meta => {
return loadWorkspaceUnit(meta);
})
);
this._workspaces.add(workspaceUnits);
return workspaceUnits;
} catch (error) {
this._logger(`Failed to parse workspaces from storage`);
}
}
return Promise.resolve(workspaces);
return [];
}
public override async deleteWorkspace(id: string): Promise<void> {
@@ -96,10 +90,10 @@ export class LocalProvider extends BaseProvider {
this._storeWorkspaces(this._workspaces.list());
}
public override async createWorkspaceInfo(
public override async createWorkspace(
meta: CreateWorkspaceInfoParams
): Promise<WorkspaceMeta0> {
const workspaceInfo: WorkspaceMeta0 = {
): Promise<WorkspaceUnit | undefined> {
const workspaceUnit = await createWorkspaceUnit({
name: meta.name,
id: uuidv4(),
published: false,
@@ -108,35 +102,10 @@ export class LocalProvider extends BaseProvider {
syncMode: 'core',
memberCount: 1,
provider: 'local',
};
return Promise.resolve(workspaceInfo);
}
public override async createWorkspace(
blocksuiteWorkspace: BlocksuiteWorkspace,
meta: WorkspaceMeta0
): Promise<BlocksuiteWorkspace | undefined> {
const workspaceId = blocksuiteWorkspace.room;
assert(workspaceId, 'Blocksuite Workspace without room(workspaceId).');
this._logger('Creating affine workspace');
const workspaceInfo: WorkspaceMeta0 = {
...meta,
};
blocksuiteWorkspace.meta.setName(meta.name);
if (!meta.avatar) {
await setDefaultAvatar(blocksuiteWorkspace);
workspaceInfo.avatar = blocksuiteWorkspace.meta.avatar;
}
await initStore(blocksuiteWorkspace);
this._workspaces.add(workspaceInfo);
});
this._workspaces.add(workspaceUnit);
this._storeWorkspaces(this._workspaces.list());
return blocksuiteWorkspace;
return workspaceUnit;
}
public override async clear(): Promise<void> {
@@ -0,0 +1,34 @@
import { WorkspaceUnit } from '../../workspace-unit.js';
import type { WorkspaceUnitCtorParams } from '../../workspace-unit';
import { createBlocksuiteWorkspace } from '../../utils/index.js';
import { applyLocalUpdates, writeUpdatesToLocal } from './indexeddb/utils.js';
import { setDefaultAvatar } from '../utils.js';
export const loadWorkspaceUnit = async (params: WorkspaceUnitCtorParams) => {
const workspaceUnit = new WorkspaceUnit(params);
const blocksuiteWorkspace = createBlocksuiteWorkspace(workspaceUnit.id);
await applyLocalUpdates(blocksuiteWorkspace);
workspaceUnit.setBlocksuiteWorkspace(blocksuiteWorkspace);
return workspaceUnit;
};
export const createWorkspaceUnit = async (params: WorkspaceUnitCtorParams) => {
const workspaceUnit = new WorkspaceUnit(params);
const blocksuiteWorkspace = createBlocksuiteWorkspace(workspaceUnit.id);
blocksuiteWorkspace.meta.setName(workspaceUnit.name);
if (!workspaceUnit.avatar) {
await setDefaultAvatar(blocksuiteWorkspace);
workspaceUnit.update({ avatar: blocksuiteWorkspace.meta.avatar });
}
await writeUpdatesToLocal(blocksuiteWorkspace);
workspaceUnit.setBlocksuiteWorkspace(blocksuiteWorkspace);
return workspaceUnit;
};