From dc4a69593bbd4dff4659219c74a58422e3950104 Mon Sep 17 00:00:00 2001 From: JimmFly Date: Tue, 10 Jan 2023 18:11:28 +0800 Subject: [PATCH 01/28] feat: export Trans component --- packages/i18n/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/i18n/src/index.ts b/packages/i18n/src/index.ts index a3fd46a1d2..cef066bb29 100644 --- a/packages/i18n/src/index.ts +++ b/packages/i18n/src/index.ts @@ -1,5 +1,5 @@ import i18next, { Resource } from 'i18next'; -import { initReactI18next, useTranslation } from 'react-i18next'; +import { Trans, initReactI18next, useTranslation } from 'react-i18next'; import { LOCALES } from './resources/index.js'; import type en_US from './resources/en.json'; @@ -23,7 +23,7 @@ declare module 'react-i18next' { // const STORAGE_KEY = 'i18n_lng'; -export { i18n, useTranslation, LOCALES }; +export { Trans, i18n, useTranslation, LOCALES }; const resources = LOCALES.reduce( (acc, { tag, res }) => ({ ...acc, [tag]: { translation: res } }), From d0da2a16c3105b47e4d9f85d3ba45665b3b2c1f6 Mon Sep 17 00:00:00 2001 From: JimmFly Date: Tue, 10 Jan 2023 18:14:17 +0800 Subject: [PATCH 02/28] feat: init languageDetector --- packages/i18n/package.json | 1 + packages/i18n/src/index.ts | 22 +++++++++++++--------- pnpm-lock.yaml | 8 ++++++++ 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/i18n/package.json b/packages/i18n/package.json index cc559528e3..d4754700fb 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -19,6 +19,7 @@ }, "dependencies": { "i18next": "^21.9.1", + "i18next-browser-languagedetector": "^7.0.1", "prettier": "^2.7.1", "react-i18next": "^11.18.4" }, diff --git a/packages/i18n/src/index.ts b/packages/i18n/src/index.ts index cef066bb29..4095c417ad 100644 --- a/packages/i18n/src/index.ts +++ b/packages/i18n/src/index.ts @@ -1,5 +1,6 @@ import i18next, { Resource } from 'i18next'; import { Trans, initReactI18next, useTranslation } from 'react-i18next'; +import detector from 'i18next-browser-languagedetector'; import { LOCALES } from './resources/index.js'; import type en_US from './resources/en.json'; @@ -45,15 +46,18 @@ const language = standardizeLocale( ); const i18n = i18next.createInstance(); -i18n.use(initReactI18next).init({ - lng: language, - fallbackLng, - debug: false, - resources, - interpolation: { - escapeValue: false, // not needed for react as it escapes by default - }, -}); +i18n + .use(detector) + .use(initReactI18next) + .init({ + lng: language, + fallbackLng, + debug: false, + resources, + interpolation: { + escapeValue: false, // not needed for react as it escapes by default + }, + }); i18n.on('languageChanged', () => { // localStorage.setItem(STORAGE_KEY, lng); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 76ec8caa50..f37413be2d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -162,11 +162,13 @@ importers: specifiers: '@types/prettier': ^2.7.2 i18next: ^21.9.1 + i18next-browser-languagedetector: ^7.0.1 prettier: ^2.7.1 react-i18next: ^11.18.4 typescript: ^4.8.4 dependencies: i18next: 21.10.0 + i18next-browser-languagedetector: 7.0.1 prettier: 2.7.1 react-i18next: 11.18.6_i18next@21.10.0 devDependencies: @@ -5610,6 +5612,12 @@ packages: hasBin: true dev: true + /i18next-browser-languagedetector/7.0.1: + resolution: {integrity: sha512-Pa5kFwaczXJAeHE56CHG2aWzFBMJNUNghf0Pm4SwSrEMps/PTKqW90EYWlIvhuYStf3Sn1K0vw+gH3+TLdkH1g==} + dependencies: + '@babel/runtime': 7.20.7 + dev: false + /i18next/21.10.0: resolution: {integrity: sha512-YeuIBmFsGjUfO3qBmMOc0rQaun4mIpGKET5WDwvu8lU7gvwpcariZLNtL0Fzj+zazcHUrlXHiptcFhBMFaxzfg==} dependencies: From c28407f77dd30c194edb9b84fec14cd00b40600d Mon Sep 17 00:00:00 2001 From: MingLiang Wang Date: Tue, 10 Jan 2023 18:19:40 +0800 Subject: [PATCH 03/28] feat: synced socket upload --- packages/data-center/src/datacenter.ts | 2 +- .../data-center/src/provider/affine/affine.ts | 50 ++++++++++++++----- packages/data-center/src/provider/base.ts | 10 ++++ 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index 8350013952..06c2a8dbab 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -306,7 +306,7 @@ export class DataCenter { this._logger( `update workspace data from ${workspaceInfo.provider} to ${providerId}` ); - applyUpdate(newWorkspace.doc, encodeStateAsUpdate(workspace.doc)); + await newProvider.assign(newWorkspace, workspace); assert(newWorkspace, 'Create workspace failed'); await currentProvider.deleteWorkspace(workspace.room); return newWorkspace.room; diff --git a/packages/data-center/src/provider/affine/affine.ts b/packages/data-center/src/provider/affine/affine.ts index 478aa583d5..0a3599c22c 100644 --- a/packages/data-center/src/provider/affine/affine.ts +++ b/packages/data-center/src/provider/affine/affine.ts @@ -3,7 +3,7 @@ import type { ProviderConstructorParams } from '../base'; import type { User, WorkspaceInfo, WorkspaceMeta } from '../../types'; import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store'; import { BlockSchema } from '@blocksuite/blocks/models'; -import { applyUpdate } from 'yjs'; +import { applyUpdate, encodeStateAsUpdate } from 'yjs'; import { storage } from './storage.js'; import assert from 'assert'; import { WebsocketProvider } from './sync.js'; @@ -64,6 +64,23 @@ export class AffineProvider extends BaseProvider { } } + private _getWebsocketProvider(workspace: BlocksuiteWorkspace) { + const { doc, room } = workspace; + assert(room); + assert(doc); + let ws = this._wsMap.get(room); + if (!ws) { + const wsUrl = `${ + window.location.protocol === 'https:' ? 'wss' : 'ws' + }://${window.location.host}/api/sync/`; + ws = new WebsocketProvider(wsUrl, room, doc, { + params: { token: this._apis.token.refresh }, + }); + this._wsMap.set(room, ws); + } + return ws; + } + private async _applyCloudUpdates(blocksuiteWorkspace: BlocksuiteWorkspace) { const { doc, room: workspaceId } = blocksuiteWorkspace; assert(workspaceId, 'Blocksuite Workspace without room(workspaceId).'); @@ -78,20 +95,10 @@ export class AffineProvider extends BaseProvider { override async warpWorkspace(workspace: BlocksuiteWorkspace) { await this._applyCloudUpdates(workspace); - const { doc, room } = workspace; + const { room } = workspace; assert(room); this.linkLocal(workspace); - - let ws = this._wsMap.get(room); - if (!ws) { - const wsUrl = `${ - window.location.protocol === 'https:' ? 'wss' : 'ws' - }://${window.location.host}/api/sync/`; - ws = new WebsocketProvider(wsUrl, room, doc, { - params: { token: this._apis.token.refresh }, - }); - this._wsMap.set(room, ws); - } + const ws = this._getWebsocketProvider(workspace); // close all websocket links Array.from(this._wsMap.entries()).forEach(([id, ws]) => { if (id !== room) { @@ -335,4 +342,21 @@ export class AffineProvider extends BaseProvider { } : null; } + + public override async assign( + to: BlocksuiteWorkspace, + from: BlocksuiteWorkspace + ): Promise { + assert(to.room, 'Blocksuite Workspace without room(workspaceId).'); + const ws = this._getWebsocketProvider(to); + applyUpdate(to.doc, encodeStateAsUpdate(from.doc)); + await new Promise((resolve, reject) => { + ws.once('synced', () => { + resolve(); + }); + ws.once('lost-connection', () => reject()); + ws.once('connection-error', () => reject()); + }); + return to; + } } diff --git a/packages/data-center/src/provider/base.ts b/packages/data-center/src/provider/base.ts index f9851098df..8a6668edd5 100644 --- a/packages/data-center/src/provider/base.ts +++ b/packages/data-center/src/provider/base.ts @@ -196,4 +196,14 @@ export class BaseProvider { ): Promise { return workspace; } + + /** + * merge one workspaces to another + * @param workspace + * @returns + */ + public async assign(to: BlocksuiteWorkspace, from: BlocksuiteWorkspace) { + from; + return to; + } } From 22606966759cc5ca806fe44ba65c56cb1dd1e947 Mon Sep 17 00:00:00 2001 From: MingLiang Wang Date: Tue, 10 Jan 2023 18:32:06 +0800 Subject: [PATCH 04/28] feat: remove not used --- packages/data-center/src/datacenter.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index 06c2a8dbab..349dab9e17 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -7,7 +7,6 @@ import { AffineProvider } from './provider'; import type { Message, WorkspaceMeta } from './types'; import assert from 'assert'; import { getLogger } from './logger'; -import { applyUpdate, encodeStateAsUpdate } from 'yjs'; import { createBlocksuiteWorkspace } from './utils/index.js'; import { MessageCenter } from './message'; From e13d27ad9c31fdccdbbe1e4023630fe14fd26623 Mon Sep 17 00:00:00 2001 From: alt0 Date: Tue, 10 Jan 2023 18:37:24 +0800 Subject: [PATCH 05/28] refactor: add workspace unit --- packages/data-center/src/datacenter.ts | 55 ++++--- packages/data-center/src/index.ts | 2 +- packages/data-center/src/message/index.ts | 4 +- packages/data-center/src/message/message.ts | 2 +- .../data-center/src/provider/affine/affine.ts | 34 ++-- .../src/provider/affine/apis/workspace.ts | 1 - packages/data-center/src/provider/base.ts | 27 ++-- .../src/provider/local/local.spec.ts | 15 +- .../data-center/src/provider/local/local.ts | 36 +++-- packages/data-center/src/provider/utils.ts | 3 + packages/data-center/src/types/index.ts | 22 +-- .../src/workspace-meta-collection.spec.ts | 50 ------ .../src/workspace-meta-collection.ts | 127 --------------- .../src/workspace-unit-collection.spec.ts | 60 +++++++ .../src/workspace-unit-collection.ts | 152 ++++++++++++++++++ packages/data-center/src/workspace-unit.ts | 64 ++++++++ 16 files changed, 386 insertions(+), 268 deletions(-) delete mode 100644 packages/data-center/src/workspace-meta-collection.spec.ts delete mode 100644 packages/data-center/src/workspace-meta-collection.ts create mode 100644 packages/data-center/src/workspace-unit-collection.spec.ts create mode 100644 packages/data-center/src/workspace-unit-collection.ts create mode 100644 packages/data-center/src/workspace-unit.ts diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index 8350013952..d65ddc3be4 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -1,10 +1,14 @@ -import { WorkspaceMetaCollection } from './workspace-meta-collection.js'; -import type { WorkspaceMetaCollectionChangeEvent } from './workspace-meta-collection'; +import { WorkspaceUnitCollection } from './workspace-unit-collection.js'; +import type { WorkspaceUnitCollectionChangeEvent } from './workspace-unit-collection'; import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store'; -import { BaseProvider } from './provider/base'; +import type { + BaseProvider, + CreateWorkspaceInfoParams, + UpdateWorkspaceMetaParams, +} from './provider/base'; import { LocalProvider } from './provider/local/local'; import { AffineProvider } from './provider'; -import type { Message, WorkspaceMeta } from './types'; +import type { Message } from './types'; import assert from 'assert'; import { getLogger } from './logger'; import { applyUpdate, encodeStateAsUpdate } from 'yjs'; @@ -16,7 +20,7 @@ import { MessageCenter } from './message'; * @classdesc Data center is made for managing different providers for business */ export class DataCenter { - private readonly _workspaceMetaCollection = new WorkspaceMetaCollection(); + private readonly _workspaceUnitCollection = new WorkspaceUnitCollection(); private readonly _logger = getLogger('dc'); private _workspaceInstances: Map = new Map(); private _messageCenter = new MessageCenter(); @@ -35,7 +39,7 @@ export class DataCenter { const getInitParams = () => { return { logger: dc._logger, - workspaces: dc._workspaceMetaCollection.createScope(), + workspaces: dc._workspaceUnitCollection.createScope(), messageCenter: dc._messageCenter, }; }; @@ -68,7 +72,7 @@ export class DataCenter { } public get workspaces() { - return this._workspaceMetaCollection.workspaces; + return this._workspaceUnitCollection.workspaces; } public async refreshWorkspaces() { @@ -82,17 +86,15 @@ export class DataCenter { * @param {string} name workspace name * @returns {Promise} */ - public async createWorkspace(workspaceMeta: WorkspaceMeta) { + public async createWorkspace(params: CreateWorkspaceInfoParams) { assert( this._mainProvider, 'There is no provider. You should add provider first.' ); - const workspaceInfo = await this._mainProvider.createWorkspaceInfo( - workspaceMeta - ); + const workspaceMeta = await this._mainProvider.createWorkspaceInfo(params); - const workspace = createBlocksuiteWorkspace(workspaceInfo.id); + const workspace = createBlocksuiteWorkspace(workspaceMeta.id); await this._mainProvider.createWorkspace(workspace, workspaceMeta); return workspace; @@ -103,7 +105,7 @@ export class DataCenter { * @param {string} workspaceId workspace id */ public async deleteWorkspace(workspaceId: string) { - const workspaceInfo = this._workspaceMetaCollection.find(workspaceId); + const workspaceInfo = this._workspaceUnitCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); assert(provider, `Workspace exists, but we couldn't find its provider.`); @@ -115,7 +117,7 @@ export class DataCenter { * @param {string} workspaceId workspace id */ private _getBlocksuiteWorkspace(workspaceId: string) { - const workspaceInfo = this._workspaceMetaCollection.find(workspaceId); + const workspaceInfo = this._workspaceUnitCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); return ( this._workspaceInstances.get(workspaceId) || @@ -149,7 +151,7 @@ export class DataCenter { * @returns {Promise} */ public async loadWorkspace(workspaceId: string) { - const workspaceInfo = this._workspaceMetaCollection.find(workspaceId); + const workspaceInfo = this._workspaceUnitCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); const currentProvider = this.providerMap.get(workspaceInfo.provider); if (currentProvider) { @@ -180,9 +182,9 @@ export class DataCenter { * @param {Function} callback callback function */ public async onWorkspacesChange( - callback: (workspaces: WorkspaceMetaCollectionChangeEvent) => void + callback: (workspaces: WorkspaceUnitCollectionChangeEvent) => void ) { - this._workspaceMetaCollection.on('change', callback); + this._workspaceUnitCollection.on('change', callback); } /** @@ -191,11 +193,11 @@ export class DataCenter { * @param {BlocksuiteWorkspace} workspace workspace instance */ public async updateWorkspaceMeta( - { name, avatar }: Partial, + { name, avatar }: UpdateWorkspaceMetaParams, workspace: BlocksuiteWorkspace ) { assert(workspace?.room, 'No workspace to set meta'); - const update: Partial = {}; + const update: Partial = {}; if (name) { workspace.meta.setName(name); update.name = name; @@ -205,7 +207,7 @@ export class DataCenter { update.avatar = avatar; } // may run for change workspace meta - const workspaceInfo = this._workspaceMetaCollection.find(workspace.room); + const workspaceInfo = this._workspaceUnitCollection.find(workspace.room); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); provider?.updateWorkspaceMeta(workspace.room, update); @@ -217,7 +219,7 @@ export class DataCenter { * @param id workspace id */ public async leaveWorkspace(workspaceId: string) { - const workspaceInfo = this._workspaceMetaCollection.find(workspaceId); + const workspaceInfo = this._workspaceUnitCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); if (provider) { @@ -227,7 +229,7 @@ export class DataCenter { } public async setWorkspacePublish(workspaceId: string, isPublish: boolean) { - const workspaceInfo = this._workspaceMetaCollection.find(workspaceId); + const workspaceInfo = this._workspaceUnitCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); if (provider) { @@ -236,7 +238,7 @@ export class DataCenter { } public async inviteMember(id: string, email: string) { - const workspaceInfo = this._workspaceMetaCollection.find(id); + const workspaceInfo = this._workspaceUnitCollection.find(id); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); if (provider) { @@ -249,7 +251,7 @@ export class DataCenter { * @param {number} permissionId permission id */ public async removeMember(workspaceId: string, permissionId: number) { - const workspaceInfo = this._workspaceMetaCollection.find(workspaceId); + const workspaceInfo = this._workspaceUnitCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); if (provider) { @@ -280,7 +282,7 @@ export class DataCenter { providerId: string ) { assert(workspace.room, 'No workspace id'); - const workspaceInfo = this._workspaceMetaCollection.find(workspace.room); + const workspaceInfo = this._workspaceUnitCollection.find(workspace.room); assert(workspaceInfo, 'Workspace not found'); if (workspaceInfo.provider === providerId) { this._logger('Workspace provider is same'); @@ -293,11 +295,12 @@ export class DataCenter { this._logger(`create ${providerId} workspace: `, workspaceInfo.name); const newWorkspaceInfo = await newProvider.createWorkspaceInfo({ name: workspaceInfo.name, - avatar: workspaceInfo.avatar, + // avatar: workspaceInfo.avatar, }); const newWorkspace = createBlocksuiteWorkspace(newWorkspaceInfo.id); // TODO optimize this function await newProvider.createWorkspace(newWorkspace, { + ...newWorkspaceInfo, name: workspaceInfo.name, avatar: workspaceInfo.avatar, }); diff --git a/packages/data-center/src/index.ts b/packages/data-center/src/index.ts index f758d9c336..029dffd7ea 100644 --- a/packages/data-center/src/index.ts +++ b/packages/data-center/src/index.ts @@ -26,6 +26,6 @@ const _initializeDataCenter = () => { export const getDataCenter = _initializeDataCenter(); export type { AccessTokenMessage } from './provider/affine/apis'; -export type { WorkspaceInfo } from './types'; +export { WorkspaceUnit } from './workspace-unit'; export { getLogger } from './logger'; export * from './message'; diff --git a/packages/data-center/src/message/index.ts b/packages/data-center/src/message/index.ts index b04581df15..7d42b90d8b 100644 --- a/packages/data-center/src/message/index.ts +++ b/packages/data-center/src/message/index.ts @@ -1,2 +1,2 @@ -export { MessageCenter } from './message'; -export { MessageCode } from './code'; +export { MessageCenter } from './message.js'; +export { MessageCode } from './code.js'; diff --git a/packages/data-center/src/message/message.ts b/packages/data-center/src/message/message.ts index 34d6870211..6de9c75795 100644 --- a/packages/data-center/src/message/message.ts +++ b/packages/data-center/src/message/message.ts @@ -1,6 +1,6 @@ import { Observable } from 'lib0/observable'; import { Message } from '../types'; -import { MessageCode } from './code'; +import { MessageCode } from './code.js'; export class MessageCenter extends Observable { constructor() { diff --git a/packages/data-center/src/provider/affine/affine.ts b/packages/data-center/src/provider/affine/affine.ts index 478aa583d5..52a14a60a0 100644 --- a/packages/data-center/src/provider/affine/affine.ts +++ b/packages/data-center/src/provider/affine/affine.ts @@ -1,6 +1,11 @@ import { BaseProvider } from '../base.js'; -import type { ProviderConstructorParams } from '../base'; -import type { User, WorkspaceInfo, WorkspaceMeta } from '../../types'; +import type { + ProviderConstructorParams, + CreateWorkspaceInfoParams, + UpdateWorkspaceMetaParams, + WorkspaceMeta0, +} from '../base'; +import type { User } from '../../types'; import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store'; import { BlockSchema } from '@blocksuite/blocks/models'; import { applyUpdate } from 'yjs'; @@ -115,12 +120,13 @@ export class AffineProvider extends BaseProvider { return []; } const workspacesList = await this._apis.getWorkspaces(); - const workspaces: WorkspaceInfo[] = workspacesList.map(w => { + const workspaces: WorkspaceMeta0[] = workspacesList.map(w => { return { ...w, memberCount: 0, name: '', provider: 'affine', + syncMode: 'core', }; }); const workspaceInstances = workspaces.map(({ id }) => { @@ -268,19 +274,17 @@ export class AffineProvider extends BaseProvider { } public override async createWorkspaceInfo( - meta: WorkspaceMeta - ): Promise { - const { id } = await this._apis.createWorkspace( - meta as Required - ); + meta: CreateWorkspaceInfoParams + ): Promise { + const { id } = await this._apis.createWorkspace(meta); - const workspaceInfo: WorkspaceInfo = { + const workspaceInfo: WorkspaceMeta0 = { name: meta.name, id: id, - isPublish: false, + published: false, avatar: '', owner: await this.getUserInfo(), - isLocal: true, + syncMode: 'core', memberCount: 1, provider: 'affine', }; @@ -289,7 +293,7 @@ export class AffineProvider extends BaseProvider { public override async createWorkspace( blocksuiteWorkspace: BlocksuiteWorkspace, - meta: WorkspaceMeta + meta: WorkspaceMeta0 ): Promise { const workspaceId = blocksuiteWorkspace.room; assert(workspaceId, 'Blocksuite Workspace without room(workspaceId).'); @@ -298,13 +302,13 @@ export class AffineProvider extends BaseProvider { this._applyCloudUpdates(blocksuiteWorkspace); this.linkLocal(blocksuiteWorkspace); - const workspaceInfo: WorkspaceInfo = { + const workspaceInfo: WorkspaceMeta0 = { name: meta.name, id: workspaceId, - isPublish: false, + published: false, avatar: '', owner: undefined, - isLocal: true, + syncMode: 'core', memberCount: 1, provider: 'affine', }; diff --git a/packages/data-center/src/provider/affine/apis/workspace.ts b/packages/data-center/src/provider/affine/apis/workspace.ts index 0c145928d5..8bdc25b21f 100644 --- a/packages/data-center/src/provider/affine/apis/workspace.ts +++ b/packages/data-center/src/provider/affine/apis/workspace.ts @@ -79,7 +79,6 @@ export async function getWorkspaceMembers( export interface CreateWorkspaceParams { name: string; - avatar: string; } export async function createWorkspace( diff --git a/packages/data-center/src/provider/base.ts b/packages/data-center/src/provider/base.ts index f9851098df..0f6a968a69 100644 --- a/packages/data-center/src/provider/base.ts +++ b/packages/data-center/src/provider/base.ts @@ -1,7 +1,8 @@ import { Workspace as BlocksuiteWorkspace, uuidv4 } from '@blocksuite/store'; import { MessageCenter } from '../message'; -import { Logger, User, WorkspaceInfo, WorkspaceMeta } from '../types'; -import type { WorkspaceMetaCollectionScope } from '../workspace-meta-collection'; +import { Logger, User } from '../types'; +import type { WorkspaceUnitCollectionScope } from '../workspace-unit-collection'; +import type { WorkspaceUnitCtorParams } from '../workspace-unit'; const defaultLogger = () => { return; @@ -9,13 +10,19 @@ const defaultLogger = () => { export interface ProviderConstructorParams { logger?: Logger; - workspaces: WorkspaceMetaCollectionScope; + workspaces: WorkspaceUnitCollectionScope; messageCenter: MessageCenter; } +export type WorkspaceMeta0 = WorkspaceUnitCtorParams; +export type CreateWorkspaceInfoParams = Pick; +export type UpdateWorkspaceMetaParams = Partial< + Pick +>; + export class BaseProvider { public readonly id: string = 'base'; - protected _workspaces!: WorkspaceMetaCollectionScope; + protected _workspaces!: WorkspaceUnitCollectionScope; protected _logger!: Logger; protected _messageCenter!: MessageCenter; @@ -37,8 +44,8 @@ export class BaseProvider { } public async createWorkspaceInfo( - meta: WorkspaceMeta - ): Promise { + params: CreateWorkspaceInfoParams + ): Promise { throw new Error(`provider: ${this.id} createWorkspaceInfo Not implemented`); } @@ -70,7 +77,7 @@ export class BaseProvider { /** * load workspaces **/ - public async loadWorkspaces(): Promise { + public async loadWorkspaces(): Promise { throw new Error(`provider: ${this.id} loadWorkSpace Not implemented`); } @@ -157,10 +164,10 @@ export class BaseProvider { */ public async updateWorkspaceMeta( id: string, - meta: Partial + params: UpdateWorkspaceMetaParams ): Promise { id; - meta; + params; return; } @@ -170,7 +177,7 @@ export class BaseProvider { */ public async createWorkspace( blocksuiteWorkspace: BlocksuiteWorkspace, - meta: WorkspaceMeta + meta: WorkspaceMeta0 ): Promise { return blocksuiteWorkspace; } diff --git a/packages/data-center/src/provider/local/local.spec.ts b/packages/data-center/src/provider/local/local.spec.ts index eff462ef86..e60ce31aae 100644 --- a/packages/data-center/src/provider/local/local.spec.ts +++ b/packages/data-center/src/provider/local/local.spec.ts @@ -1,13 +1,15 @@ import { test, expect } from '@playwright/test'; -import { WorkspaceMetaCollection } from '../../workspace-meta-collection.js'; +import { WorkspaceUnitCollection } from '../../workspace-unit-collection.js'; import { LocalProvider } from './local.js'; import { createBlocksuiteWorkspace } from '../../utils/index.js'; +import { MessageCenter } from '../../message/index.js'; import 'fake-indexeddb/auto'; test.describe.serial('local provider', () => { - const workspaceMetaCollection = new WorkspaceMetaCollection(); + const workspaceMetaCollection = new WorkspaceUnitCollection(); const provider = new LocalProvider({ workspaces: workspaceMetaCollection.createScope(), + messageCenter: new MessageCenter(), }); const workspaceName = 'workspace-test'; @@ -16,23 +18,20 @@ test.describe.serial('local provider', () => { test('create workspace', async () => { const workspaceInfo = await provider.createWorkspaceInfo({ name: workspaceName, - avatar: 'avatar-url-test', }); workspaceId = workspaceInfo.id; const blocksuiteWorkspace = createBlocksuiteWorkspace(workspaceId); - await provider.createWorkspace(blocksuiteWorkspace, { - name: workspaceName, - avatar: 'avatar-url-test', - }); + await provider.createWorkspace(blocksuiteWorkspace, workspaceInfo); expect(workspaceMetaCollection.workspaces.length).toEqual(1); expect(workspaceMetaCollection.workspaces[0].name).toEqual(workspaceName); }); test('workspace list cache', async () => { - const workspacesMetaCollection1 = new WorkspaceMetaCollection(); + const workspacesMetaCollection1 = new WorkspaceUnitCollection(); const provider1 = new LocalProvider({ workspaces: workspacesMetaCollection1.createScope(), + messageCenter: new MessageCenter(), }); await provider1.loadWorkspaces(); expect(workspacesMetaCollection1.workspaces.length).toEqual(1); diff --git a/packages/data-center/src/provider/local/local.ts b/packages/data-center/src/provider/local/local.ts index bf8aa31941..f87d7c95fb 100644 --- a/packages/data-center/src/provider/local/local.ts +++ b/packages/data-center/src/provider/local/local.ts @@ -1,7 +1,11 @@ import { BaseProvider } from '../base.js'; -import type { ProviderConstructorParams } from '../base'; +import type { + ProviderConstructorParams, + WorkspaceMeta0, + UpdateWorkspaceMetaParams, + CreateWorkspaceInfoParams, +} from '../base'; import { varStorage as storage } from 'lib0/storage'; -import { WorkspaceInfo, WorkspaceMeta } from '../../types'; import { Workspace as BlocksuiteWorkspace, uuidv4 } from '@blocksuite/store'; import { IndexedDBProvider } from './indexeddb.js'; import assert from 'assert'; @@ -18,7 +22,7 @@ export class LocalProvider extends BaseProvider { this.loadWorkspaces(); } - private _storeWorkspaces(workspaces: WorkspaceInfo[]) { + private _storeWorkspaces(workspaces: WorkspaceMeta0[]) { storage.setItem(WORKSPACE_KEY, JSON.stringify(workspaces)); } @@ -40,12 +44,12 @@ export class LocalProvider extends BaseProvider { return workspace; } - override loadWorkspaces(): Promise { + override loadWorkspaces(): Promise { const workspaceStr = storage.getItem(WORKSPACE_KEY); - let workspaces: WorkspaceInfo[] = []; + let workspaces: WorkspaceMeta0[] = []; if (workspaceStr) { try { - workspaces = JSON.parse(workspaceStr) as WorkspaceInfo[]; + workspaces = JSON.parse(workspaceStr) as WorkspaceMeta0[]; workspaces.forEach(workspace => { this._workspaces.add(workspace); }); @@ -69,22 +73,22 @@ export class LocalProvider extends BaseProvider { public override async updateWorkspaceMeta( id: string, - meta: Partial + meta: UpdateWorkspaceMetaParams ) { this._workspaces.update(id, meta); this._storeWorkspaces(this._workspaces.list()); } public override async createWorkspaceInfo( - meta: WorkspaceMeta - ): Promise { - const workspaceInfo: WorkspaceInfo = { + meta: CreateWorkspaceInfoParams + ): Promise { + const workspaceInfo: WorkspaceMeta0 = { name: meta.name, id: uuidv4(), - isPublish: false, + published: false, avatar: '', owner: undefined, - isLocal: true, + syncMode: 'core', memberCount: 1, provider: 'local', }; @@ -93,20 +97,20 @@ export class LocalProvider extends BaseProvider { public override async createWorkspace( blocksuiteWorkspace: BlocksuiteWorkspace, - meta: WorkspaceMeta + meta: WorkspaceMeta0 ): Promise { const workspaceId = blocksuiteWorkspace.room; assert(workspaceId, 'Blocksuite Workspace without room(workspaceId).'); assert(meta.name, 'Workspace name is required'); this._logger('Creating affine workspace'); - const workspaceInfo: WorkspaceInfo = { + const workspaceInfo: WorkspaceMeta0 = { name: meta.name, id: workspaceId, - isPublish: false, + published: false, avatar: '', owner: undefined, - isLocal: true, + syncMode: 'core', memberCount: 1, provider: 'local', }; diff --git a/packages/data-center/src/provider/utils.ts b/packages/data-center/src/provider/utils.ts index d8e5f6d058..c193719cc2 100644 --- a/packages/data-center/src/provider/utils.ts +++ b/packages/data-center/src/provider/utils.ts @@ -5,6 +5,9 @@ import { getDefaultHeadImgBlob } from '../utils/index.js'; export const setDefaultAvatar = async ( blocksuiteWorkspace: BlocksuiteWorkspace ) => { + if (typeof document === 'undefined') { + return; + } const blob = await getDefaultHeadImgBlob(blocksuiteWorkspace.meta.name); const blobStorage = await blocksuiteWorkspace.blobs; assert(blobStorage, 'No blob storage'); diff --git a/packages/data-center/src/types/index.ts b/packages/data-center/src/types/index.ts index 29e3a2f255..ea85ded82f 100644 --- a/packages/data-center/src/types/index.ts +++ b/packages/data-center/src/types/index.ts @@ -1,15 +1,15 @@ import { getLogger } from '../logger'; -export type WorkspaceInfo = { - name: string; - id: string; - isPublish?: boolean; - avatar?: string; - owner?: User; - isLocal?: boolean; - memberCount: number; - provider: string; -}; +// export type WorkspaceInfo = { +// name: string; +// id: string; +// isPublish?: boolean; +// avatar?: string; +// owner?: User; +// isLocal?: boolean; +// memberCount: number; +// provider: string; +// }; export type User = { name: string; @@ -18,7 +18,7 @@ export type User = { avatar: string; }; -export type WorkspaceMeta = Pick; +// export type WorkspaceMeta = Pick; export type Logger = ReturnType; diff --git a/packages/data-center/src/workspace-meta-collection.spec.ts b/packages/data-center/src/workspace-meta-collection.spec.ts deleted file mode 100644 index afb3eb65f5..0000000000 --- a/packages/data-center/src/workspace-meta-collection.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { test, expect } from '@playwright/test'; -import { WorkspaceMetaCollection } from './workspace-meta-collection.js'; -import type { WorkspaceMetaCollectionChangeEvent } from './workspace-meta-collection'; - -test.describe.serial('workspace meta collection observable', () => { - const workspaces = new WorkspaceMetaCollection(); - - const scope = workspaces.createScope(); - - test('add workspace', () => { - workspaces.once('change', (event: WorkspaceMetaCollectionChangeEvent) => { - expect(event.added?.id).toEqual('123'); - }); - scope.add({ - id: '123', - name: 'test', - memberCount: 1, - provider: '', - }); - }); - - test('list workspace', () => { - const list = scope.list(); - expect(list.length).toEqual(1); - expect(list[0].id).toEqual('123'); - }); - - test('get workspace', () => { - expect(scope.get('123')?.id).toEqual('123'); - }); - - test('update workspace', () => { - workspaces.once('change', (event: WorkspaceMetaCollectionChangeEvent) => { - expect(event.updated?.name).toEqual('demo'); - }); - scope.update('123', { name: 'demo' }); - }); - - test('get workspace form other scope', () => { - const scope1 = workspaces.createScope(); - expect(scope1.get('123')).toBeFalsy(); - }); - - test('delete workspace', () => { - workspaces.once('change', (event: WorkspaceMetaCollectionChangeEvent) => { - expect(event.deleted?.id).toEqual('123'); - }); - scope.remove('123'); - }); -}); diff --git a/packages/data-center/src/workspace-meta-collection.ts b/packages/data-center/src/workspace-meta-collection.ts deleted file mode 100644 index 6b7ce4a350..0000000000 --- a/packages/data-center/src/workspace-meta-collection.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { Observable } from 'lib0/observable'; -import type { WorkspaceInfo, WorkspaceMeta } from './types'; - -export interface WorkspaceMetaCollectionScope { - get: (workspaceId: string) => WorkspaceInfo | undefined; - list: () => WorkspaceInfo[]; - add: (workspace: WorkspaceInfo) => void; - remove: (workspaceId: string) => boolean; - clear: () => void; - update: (workspaceId: string, workspaceMeta: Partial) => void; -} - -export interface WorkspaceMetaCollectionChangeEvent { - added?: WorkspaceInfo; - deleted?: WorkspaceInfo; - updated?: WorkspaceInfo; -} - -export class WorkspaceMetaCollection extends Observable<'change'> { - private _workspacesMap = new Map(); - - get workspaces(): WorkspaceInfo[] { - return Array.from(this._workspacesMap.values()); - } - - find(workspaceId: string) { - return this._workspacesMap.get(workspaceId); - } - - createScope(): WorkspaceMetaCollectionScope { - const scopedWorkspaceIds = new Set(); - - const get = (workspaceId: string) => { - if (!scopedWorkspaceIds.has(workspaceId)) { - return; - } - return this._workspacesMap.get(workspaceId); - }; - - const add = (workspace: WorkspaceInfo) => { - if (this._workspacesMap.has(workspace.id)) { - throw new Error(`Duplicate workspace id.`); - } - this._workspacesMap.set(workspace.id, workspace); - scopedWorkspaceIds.add(workspace.id); - - this.emit('change', [ - { - added: workspace, - } as WorkspaceMetaCollectionChangeEvent, - ]); - }; - - const remove = (workspaceId: string) => { - if (!scopedWorkspaceIds.has(workspaceId)) { - return true; - } - - const workspace = this._workspacesMap.get(workspaceId); - if (workspace) { - const ret = this._workspacesMap.delete(workspaceId); - // If deletion failed, return. - if (!ret) { - return ret; - } - - scopedWorkspaceIds.delete(workspaceId); - - this.emit('change', [ - { - deleted: workspace, - } as WorkspaceMetaCollectionChangeEvent, - ]); - } - return true; - }; - - const clear = () => { - scopedWorkspaceIds.forEach(id => { - remove(id); - }); - }; - - const update = ( - workspaceId: string, - workspaceMeta: Partial - ) => { - if (!scopedWorkspaceIds.has(workspaceId)) { - return true; - } - - const workspace = this._workspacesMap.get(workspaceId); - if (!workspace) { - return true; - } - - this._workspacesMap.set(workspaceId, { ...workspace, ...workspaceMeta }); - - this.emit('change', [ - { - updated: this._workspacesMap.get(workspaceId), - } as WorkspaceMetaCollectionChangeEvent, - ]); - }; - - // TODO: need to optimize - const list = () => { - const workspaces: WorkspaceInfo[] = []; - scopedWorkspaceIds.forEach(id => { - const workspace = this._workspacesMap.get(id); - if (workspace) { - workspaces.push(workspace); - } - }); - return workspaces; - }; - - return { - get, - list, - add, - remove, - clear, - update, - }; - } -} diff --git a/packages/data-center/src/workspace-unit-collection.spec.ts b/packages/data-center/src/workspace-unit-collection.spec.ts new file mode 100644 index 0000000000..06eee95bd0 --- /dev/null +++ b/packages/data-center/src/workspace-unit-collection.spec.ts @@ -0,0 +1,60 @@ +import { test, expect } from '@playwright/test'; +import { WorkspaceUnitCollection } from './workspace-unit-collection.js'; +import type { WorkspaceUnitCollectionChangeEvent } from './workspace-unit-collection'; + +test.describe.serial('workspace meta collection observable', () => { + const workspaceUnitCollection = new WorkspaceUnitCollection(); + + const scope = workspaceUnitCollection.createScope(); + + test('add workspace', () => { + workspaceUnitCollection.once( + 'change', + (event: WorkspaceUnitCollectionChangeEvent) => { + expect(event.added?.id).toEqual('123'); + } + ); + scope.add({ + id: '123', + name: 'test', + memberCount: 1, + provider: '', + syncMode: 'core', + }); + }); + + test('list workspace', () => { + const list = scope.list(); + expect(list.length).toEqual(1); + expect(list[0].id).toEqual('123'); + }); + + test('get workspace', () => { + expect(scope.get('123')?.id).toEqual('123'); + }); + + test('update workspace', () => { + workspaceUnitCollection.once( + 'change', + (event: WorkspaceUnitCollectionChangeEvent) => { + expect(event.updated?.name).toEqual('demo'); + } + ); + scope.update('123', { name: 'demo' }); + }); + + test('get workspace form other scope', () => { + const scope1 = workspaceUnitCollection.createScope(); + expect(scope1.get('123')).toBeFalsy(); + }); + + test('delete workspace', () => { + workspaceUnitCollection.once( + 'change', + (event: WorkspaceUnitCollectionChangeEvent) => { + expect(event.deleted?.id).toEqual('123'); + } + ); + scope.remove('123'); + }); +}); diff --git a/packages/data-center/src/workspace-unit-collection.ts b/packages/data-center/src/workspace-unit-collection.ts new file mode 100644 index 0000000000..e4dedad8c7 --- /dev/null +++ b/packages/data-center/src/workspace-unit-collection.ts @@ -0,0 +1,152 @@ +import { Observable } from 'lib0/observable'; +import { WorkspaceUnit } from './workspace-unit.js'; +import type { + WorkspaceUnitCtorParams, + UpdateWorkspaceUnitParams, +} from './workspace-unit'; + +export interface WorkspaceUnitCollectionScope { + get: (workspaceId: string) => WorkspaceUnit | undefined; + list: () => WorkspaceUnit[]; + add: (workspace: WorkspaceUnitCtorParams) => void; + remove: (workspaceId: string) => boolean; + clear: () => void; + update: ( + workspaceId: string, + workspaceMeta: UpdateWorkspaceUnitParams + ) => void; +} + +export interface WorkspaceUnitCollectionChangeEvent { + added?: WorkspaceUnit; + deleted?: WorkspaceUnit; + updated?: WorkspaceUnit; +} + +export class WorkspaceUnitCollection { + private _events = new Observable(); + private _workspaceUnitMap = new Map(); + + get workspaces(): WorkspaceUnit[] { + return Array.from(this._workspaceUnitMap.values()); + } + + public on( + type: 'change', + callback: (event: WorkspaceUnitCollectionChangeEvent) => void + ) { + this._events.on(type, callback); + } + + public once( + type: 'change', + callback: (event: WorkspaceUnitCollectionChangeEvent) => void + ) { + this._events.once(type, callback); + } + + find(workspaceId: string) { + return this._workspaceUnitMap.get(workspaceId); + } + + createScope(): WorkspaceUnitCollectionScope { + const scopedWorkspaceIds = new Set(); + + const get = (workspaceId: string) => { + if (!scopedWorkspaceIds.has(workspaceId)) { + return; + } + return this._workspaceUnitMap.get(workspaceId); + }; + + const add = (workspace: WorkspaceUnitCtorParams) => { + if (this._workspaceUnitMap.has(workspace.id)) { + throw new Error(`Duplicate workspace id.`); + } + + const workspaceUnit = new WorkspaceUnit(workspace); + this._workspaceUnitMap.set(workspace.id, workspaceUnit); + + scopedWorkspaceIds.add(workspace.id); + + this._events.emit('change', [ + { + added: workspaceUnit, + } as WorkspaceUnitCollectionChangeEvent, + ]); + }; + + const remove = (workspaceId: string) => { + if (!scopedWorkspaceIds.has(workspaceId)) { + return true; + } + + const workspaceUnit = this._workspaceUnitMap.get(workspaceId); + if (workspaceUnit) { + const ret = this._workspaceUnitMap.delete(workspaceId); + // If deletion failed, return. + if (!ret) { + return ret; + } + + scopedWorkspaceIds.delete(workspaceId); + + this._events.emit('change', [ + { + deleted: workspaceUnit, + } as WorkspaceUnitCollectionChangeEvent, + ]); + } + return true; + }; + + const clear = () => { + scopedWorkspaceIds.forEach(id => { + remove(id); + }); + }; + + const update = ( + workspaceId: string, + workspaceMeta: UpdateWorkspaceUnitParams + ) => { + if (!scopedWorkspaceIds.has(workspaceId)) { + return true; + } + + const workspaceUnit = this._workspaceUnitMap.get(workspaceId); + if (!workspaceUnit) { + return true; + } + + workspaceUnit.update(workspaceMeta); + + this._events.emit('change', [ + { + updated: workspaceUnit, + } as WorkspaceUnitCollectionChangeEvent, + ]); + }; + + // TODO: need to optimize + const list = () => { + const workspaceUnits: WorkspaceUnit[] = []; + scopedWorkspaceIds.forEach(id => { + const workspaceUnit = this._workspaceUnitMap.get(id); + if (workspaceUnit) { + workspaceUnits.push(workspaceUnit); + } + }); + return workspaceUnits; + }; + + return { + get, + list, + add, + remove, + clear, + update, + }; + } +} diff --git a/packages/data-center/src/workspace-unit.ts b/packages/data-center/src/workspace-unit.ts new file mode 100644 index 0000000000..527e8647ef --- /dev/null +++ b/packages/data-center/src/workspace-unit.ts @@ -0,0 +1,64 @@ +import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store'; +import type { User } from './types'; + +export type SyncMode = 'all' | 'core'; + +export interface WorkspaceUnitCtorParams { + id: string; + name: string; + avatar?: string; + owner?: User; + published?: boolean; + memberCount: number; + provider: string; + syncMode: SyncMode; + + blocksuiteWorkspace?: BlocksuiteWorkspace; +} + +export type UpdateWorkspaceUnitParams = Partial< + Omit +>; + +export class WorkspaceUnit { + public readonly id: string; + public name!: string; + public avatar?: string; + public owner?: User; + public published?: boolean; + public memberCount!: number; + public provider!: string; + public syncMode: 'all' | 'core' = 'core'; + + private _blocksuiteWorkspace?: BlocksuiteWorkspace; + + constructor(params: WorkspaceUnitCtorParams) { + this.id = params.id; + this.update(params); + } + + get blocksuiteWorkspace() { + return this._blocksuiteWorkspace; + } + + setBlocksuiteWorkspace(blocksuiteWorkspace: BlocksuiteWorkspace) { + if (blocksuiteWorkspace?.room !== this.id) { + throw new Error('Workspace id inconsistent.'); + } + this._blocksuiteWorkspace = blocksuiteWorkspace; + } + + update(params: UpdateWorkspaceUnitParams) { + Object.assign(this, params); + } + + get isPublish() { + console.error('Suggest changing to published'); + return this.published; + } + + get isLocal() { + console.error('Suggest changing to syncMode'); + return this.syncMode === 'all'; + } +} From b3e703b6562d3ffed59098e7a829a94f37ca95aa Mon Sep 17 00:00:00 2001 From: JimmFly Date: Tue, 10 Jan 2023 18:45:48 +0800 Subject: [PATCH 06/28] chore: remove language detector --- packages/i18n/package.json | 1 - pnpm-lock.yaml | 8 -------- 2 files changed, 9 deletions(-) diff --git a/packages/i18n/package.json b/packages/i18n/package.json index d4754700fb..cc559528e3 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -19,7 +19,6 @@ }, "dependencies": { "i18next": "^21.9.1", - "i18next-browser-languagedetector": "^7.0.1", "prettier": "^2.7.1", "react-i18next": "^11.18.4" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f37413be2d..76ec8caa50 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -162,13 +162,11 @@ importers: specifiers: '@types/prettier': ^2.7.2 i18next: ^21.9.1 - i18next-browser-languagedetector: ^7.0.1 prettier: ^2.7.1 react-i18next: ^11.18.4 typescript: ^4.8.4 dependencies: i18next: 21.10.0 - i18next-browser-languagedetector: 7.0.1 prettier: 2.7.1 react-i18next: 11.18.6_i18next@21.10.0 devDependencies: @@ -5612,12 +5610,6 @@ packages: hasBin: true dev: true - /i18next-browser-languagedetector/7.0.1: - resolution: {integrity: sha512-Pa5kFwaczXJAeHE56CHG2aWzFBMJNUNghf0Pm4SwSrEMps/PTKqW90EYWlIvhuYStf3Sn1K0vw+gH3+TLdkH1g==} - dependencies: - '@babel/runtime': 7.20.7 - dev: false - /i18next/21.10.0: resolution: {integrity: sha512-YeuIBmFsGjUfO3qBmMOc0rQaun4mIpGKET5WDwvu8lU7gvwpcariZLNtL0Fzj+zazcHUrlXHiptcFhBMFaxzfg==} dependencies: From d0d0955c0e811ce951640cc7ec0e82ef64ff6276 Mon Sep 17 00:00:00 2001 From: JimmFly Date: Tue, 10 Jan 2023 18:47:13 +0800 Subject: [PATCH 07/28] feat: add language local storage --- packages/i18n/src/index.ts | 49 +++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/packages/i18n/src/index.ts b/packages/i18n/src/index.ts index 4095c417ad..714cc40f31 100644 --- a/packages/i18n/src/index.ts +++ b/packages/i18n/src/index.ts @@ -1,6 +1,5 @@ import i18next, { Resource } from 'i18next'; import { Trans, initReactI18next, useTranslation } from 'react-i18next'; -import detector from 'i18next-browser-languagedetector'; import { LOCALES } from './resources/index.js'; import type en_US from './resources/en.json'; @@ -22,7 +21,7 @@ declare module 'react-i18next' { } } -// const STORAGE_KEY = 'i18n_lng'; +const STORAGE_KEY = 'i18n_lng'; export { Trans, i18n, useTranslation, LOCALES }; @@ -34,33 +33,39 @@ const resources = LOCALES.reduce( const fallbackLng = LOCALES[0].tag; const standardizeLocale = (language: string) => { if (LOCALES.find(locale => locale.tag === language)) return language; + if (language === 'zh-CN' || language === 'zh') { + return 'zh-Hans'; + } + if (language.slice(0, 2).toLowerCase() === 'zh') { + return 'zh-Hant'; + } if (LOCALES.find(locale => locale.tag === language.slice(0, 2).toLowerCase())) return language; return fallbackLng; }; +let language = 'en'; -const language = standardizeLocale( - // localStorage.getItem(STORAGE_KEY) ?? - // (typeof navigator !== 'undefined' ? navigator.language : 'en') - 'en' -); - +if (typeof window !== 'undefined') { + const localStorageLanguage = localStorage.getItem(STORAGE_KEY); + if (localStorageLanguage) { + language = standardizeLocale(localStorageLanguage); + } else { + language = standardizeLocale(navigator.language); + } +} const i18n = i18next.createInstance(); -i18n - .use(detector) - .use(initReactI18next) - .init({ - lng: language, - fallbackLng, - debug: false, - resources, - interpolation: { - escapeValue: false, // not needed for react as it escapes by default - }, - }); +i18n.use(initReactI18next).init({ + lng: language, + fallbackLng, + debug: false, + resources, + interpolation: { + escapeValue: false, // not needed for react as it escapes by default + }, +}); -i18n.on('languageChanged', () => { - // localStorage.setItem(STORAGE_KEY, lng); +i18n.on('languageChanged', lng => { + localStorage.setItem(STORAGE_KEY, lng); }); // const I18nProvider = I18nextProvider; From 7860c563c73193f527b42ea40da029bf05299b56 Mon Sep 17 00:00:00 2001 From: JimmFly Date: Tue, 10 Jan 2023 18:49:04 +0800 Subject: [PATCH 08/28] chore: update i18n json --- packages/i18n/src/resources/en.json | 68 +++++++++++++++++++++++- packages/i18n/src/resources/zh-Hans.json | 66 +---------------------- 2 files changed, 67 insertions(+), 67 deletions(-) diff --git a/packages/i18n/src/resources/en.json b/packages/i18n/src/resources/en.json index 78dcb2fb28..49a6bf3ab1 100644 --- a/packages/i18n/src/resources/en.json +++ b/packages/i18n/src/resources/en.json @@ -1,8 +1,11 @@ { "Quick search": "Quick search", + "Quick search placeholder": "Quick Search...", + "Quick search placeholder2": "Search in {{workspace}}", "All pages": "All pages", "Favourites": "Favourites", "No item": "No item", + "Settings": "Settings", "Import": "Import", "Trash": "Trash", "New Page": "New Page", @@ -54,7 +57,7 @@ "Strikethrough": "Strikethrough", "Inline code": "Inline code", "Code block": "Code block", - "Link": "Hyperlink(with selected text)", + "Hyperlink(with selected text)": "Hyperlink(with selected text)", "Body text": "Body text", "Heading": "Heading {{number}}", "Increase indent": "Increase indent", @@ -75,5 +78,66 @@ "Restore it": "Restore it", "TrashButtonGroupTitle": "Permanently delete", "TrashButtonGroupDescription": "Once deleted, you can't undo this action. Do you confirm?", - "Delete permanently": "Delete permanently" + "Delete permanently": "Delete permanently", + "recommendBrowser": " We recommend the <1>Chrome browser for optimal experience.", + "upgradeBrowser": "Please upgrade to the latest version of Chrome for the best experience.", + "Invite Members": "Invite Members", + "Invite placeholder": "Search mail (Gmail support only)", + "Non-Gmail": "Non-Gmail is not supported", + "Invite": "Invite", + "Loading": "Loading...", + "NotLoggedIn": "Currently not logged in", + "ClearData": "Clear local data", + "Continue with Google": "Continue with Google", + "Set up an AFFiNE account to sync data": "Set up an AFFiNE account to sync data", + "Stay logged out": "Stay logged out", + "All changes are saved locally": "All changes are saved locally", + "Ooops!": "Ooops!", + "mobile device": "Looks like you are browsing on a mobile device.", + "mobile device description": "We are still working on mobile support and recommend you use a desktop device.", + "Got it": "Got it", + "emptyAllPages": "This workspace is empty. Create a new page to begin editing.", + "emptyFavourite": "Click Add to Favourites and the page will appear here.", + "emptyTrash": "Click Add to Trash and the page will appear here.", + "still designed": "(This page is still being designed.)", + "My Workspaces": "My Workspaces", + "Create Or Import": "Create Or Import", + "Tips": "Tips: ", + "login success": "Login success", + "Sign in": "Sign in AFFiNE Cloud", + "Sign out": "Sign out of AFFiNE Cloud", + "Delete Workspace": "Delete Workspace", + "Delete Workspace Description": "Deleting (<1>{{workspace}}) cannot be undone, please proceed with caution. along with all its content.", + "Delete Workspace Description2": "Deleting (<1>{{workspace}}) will delete both local and cloud data, this operation cannot be undone, please proceed with caution.", + "Delete Workspace placeholder": "Please type “Delete” to confirm", + "Leave Workspace": "Leave Workspace", + "Leave Workspace Description": "After you leave, you will not be able to access all the contents of this workspace.", + "Leave": "Leave", + "Workspace Icon": "Workspace Icon", + "Workspace Name": "Workspace Name", + "Workspace Type": "Workspace Type", + "Export Workspace": "Export Workspace <1>{{workspace}} Is Coming", + "Users": "Users", + "Access level": "Access level", + "Pending": "Pending", + "Collaboration Description": "Collaborating with other members requires AFFiNE Cloud service.", + "Enable AFFiNE Cloud": "Enable AFFiNE Cloud", + "Enable AFFiNE Cloud Description": "If enabled, the data in this workspace will be backed up and synchronised via AFFiNE Cloud.", + "Enable": "Enable", + "Sign in and Enable": "Sign in and Enable", + "Skip": "Skip", + "Publishing": "Publishing to web requires AFFiNE Cloud service.", + "Share with link": "Share with link", + "Copy Link": "Copy Link", + "Publishing Description": "After publishing to the web, everyone can view the content of this workspace through the link.", + "Stop publishing": "Stop publishing", + "Publish to web": "Publish to web", + "Sync Description": "{{workspaceName}} is Local Workspace. All data is stored on the current device. You can enable AFFiNE Cloud for this workspace to keep data in sync with the cloud.", + "Sync Description2": "<1>{{workspaceName}} is Cloud Workspace. All data will be synchronized and saved to the AFFiNE", + "Download data to device": "Download {{CoreOrAll}} data to device", + "General": "General", + "Sync": "Sync", + "Collaboration": "Collaboration", + "Publish": "Publish", + "Workspace Settings": "Workspace Settings" } diff --git a/packages/i18n/src/resources/zh-Hans.json b/packages/i18n/src/resources/zh-Hans.json index 47095233d9..0967ef424b 100644 --- a/packages/i18n/src/resources/zh-Hans.json +++ b/packages/i18n/src/resources/zh-Hans.json @@ -1,65 +1 @@ -{ - "Quick search": "快速搜索", - "All pages": "全部页面", - "Favourites": "收藏夹", - "No item": "没有项目", - "Import": "导入", - "Trash": "回收站", - "New Page": "新建文章", - "New Keyword Page": "新建 '{{query}}' 为标题的文章", - "Find 0 result": "找到 0 个结果", - "Find results": "找到 {{number}} 个结果", - "Collapse sidebar": "关闭侧边栏", - "Expand sidebar": "展开侧边栏", - "Removed from Favourites": "已从收藏中移除", - "Remove from favourites": "从收藏中移除", - "Added to Favourites": "已添加到收藏", - "Add to favourites": "添加到收藏", - "Paper": "文章", - "Edgeless": "无边模式", - "Switch to": "跳转到", - "Convert to ": "转换成 ", - "Page": "文章", - "Export": "导出", - "Export to HTML": "导出到 HTML", - "Export to Markdown": "导出到 Markdown", - "Delete": "删除", - "Title": "标题", - "Untitled": "无标题", - "Created": "创建时间", - "Updated": "更新时间", - "Open in new tab": "在新页面打开", - "Favourite": "收藏", - "Favourited": "已收藏", - "Delete page?": "删除文章?", - "Delete permanently?": "永久删除?", - "will be moved to Trash": "{{title}} 将被移动到回收站", - "Once deleted, you can't undo this action.": "一次性删除,无法恢复。", - "Moved to Trash": "已移动到回收站", - "Permanently deleted": "已永久删除", - "restored": "{{title}} 已恢复", - "Cancel": "取消", - "Keyboard Shortcuts": "快捷键", - "Contact Us": "联系我们", - "Official Website": "官网", - "Get in touch!": "Get in touch!", - "AFFiNE Community": "AFFiNE Community", - "How is AFFiNE Alpha different?": "How is AFFiNE Alpha different?", - "Shortcuts": "Shortcuts", - "Undo": "Undo", - "Redo": "Redo", - "Bold": "Bold", - "Italic": "Italic", - "Underline": "Underline", - "Strikethrough": "Strikethrough", - "Inline code": "Inline code", - "Code block": "Code block", - "Link": "Link", - "Body text": "Body text", - "Heading": "Heading {{number}}", - "Increase indent": "Increase indent", - "Reduce indent": "Reduce indent", - "Markdown Syntax": "Markdown Syntax", - "Divider": "Divider", - "404 - Page Not Found": "404 - Page Not Found" -} +{} From 5189086c1b663d59fe32fd2ad4bf0ae9ef96fa73 Mon Sep 17 00:00:00 2001 From: JimmFly Date: Tue, 10 Jan 2023 19:03:20 +0800 Subject: [PATCH 09/28] chore: update keys --- packages/i18n/src/resources/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/i18n/src/resources/en.json b/packages/i18n/src/resources/en.json index 49a6bf3ab1..635c083af3 100644 --- a/packages/i18n/src/resources/en.json +++ b/packages/i18n/src/resources/en.json @@ -57,7 +57,7 @@ "Strikethrough": "Strikethrough", "Inline code": "Inline code", "Code block": "Code block", - "Hyperlink(with selected text)": "Hyperlink(with selected text)", + "Link": "Hyperlink(with selected text)", "Body text": "Body text", "Heading": "Heading {{number}}", "Increase indent": "Increase indent", From c8ac8c5738342b87c7bafd71cbe5ee35fafae2e7 Mon Sep 17 00:00:00 2001 From: JimmFly Date: Tue, 10 Jan 2023 19:05:55 +0800 Subject: [PATCH 10/28] chore: update json --- packages/i18n/src/resources/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/i18n/src/resources/en.json b/packages/i18n/src/resources/en.json index 635c083af3..761f649c18 100644 --- a/packages/i18n/src/resources/en.json +++ b/packages/i18n/src/resources/en.json @@ -57,7 +57,7 @@ "Strikethrough": "Strikethrough", "Inline code": "Inline code", "Code block": "Code block", - "Link": "Hyperlink(with selected text)", + "Link": "Hyperlink (with selected text)", "Body text": "Body text", "Heading": "Heading {{number}}", "Increase indent": "Increase indent", From 6c792d0e6121fc0e943172a6c48b23c050a988e0 Mon Sep 17 00:00:00 2001 From: x1a0t <405028157@qq.com> Date: Tue, 10 Jan 2023 19:31:57 +0800 Subject: [PATCH 11/28] chore: update BlockSuite version --- packages/app/package.json | 6 ++--- packages/data-center/package.json | 4 +-- pnpm-lock.yaml | 44 +++++++++++++++---------------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index aece2897f9..9bc91a9259 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -11,10 +11,10 @@ "dependencies": { "@affine/datacenter": "workspace:*", "@affine/i18n": "workspace:*", - "@blocksuite/blocks": "0.3.1-20230109032243-37ad3ba", - "@blocksuite/editor": "0.3.1-20230109032243-37ad3ba", + "@blocksuite/blocks": "0.4.0-20230110112105-ef07332", + "@blocksuite/editor": "0.4.0-20230110112105-ef07332", "@blocksuite/icons": "^2.0.2", - "@blocksuite/store": "0.3.1-20230109032243-37ad3ba", + "@blocksuite/store": "0.4.0-20230110112105-ef07332", "@emotion/css": "^11.10.0", "@emotion/react": "^11.10.4", "@emotion/server": "^11.10.0", diff --git a/packages/data-center/package.json b/packages/data-center/package.json index 9f9fc2d860..606f6e196c 100644 --- a/packages/data-center/package.json +++ b/packages/data-center/package.json @@ -26,8 +26,8 @@ "typescript": "^4.8.4" }, "dependencies": { - "@blocksuite/blocks": "^0.3.1-20230109032243-37ad3ba", - "@blocksuite/store": "^0.3.1-20230109032243-37ad3ba", + "@blocksuite/blocks": "0.4.0-20230110112105-ef07332", + "@blocksuite/store": "0.4.0-20230110112105-ef07332", "debug": "^4.3.4", "encoding": "^0.1.13", "firebase": "^9.15.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 76ec8caa50..722b006877 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,10 +40,10 @@ importers: specifiers: '@affine/datacenter': workspace:* '@affine/i18n': workspace:* - '@blocksuite/blocks': 0.3.1-20230109032243-37ad3ba - '@blocksuite/editor': 0.3.1-20230109032243-37ad3ba + '@blocksuite/blocks': 0.4.0-20230110112105-ef07332 + '@blocksuite/editor': 0.4.0-20230110112105-ef07332 '@blocksuite/icons': ^2.0.2 - '@blocksuite/store': 0.3.1-20230109032243-37ad3ba + '@blocksuite/store': 0.4.0-20230110112105-ef07332 '@emotion/css': ^11.10.0 '@emotion/react': ^11.10.4 '@emotion/server': ^11.10.0 @@ -81,10 +81,10 @@ importers: dependencies: '@affine/datacenter': link:../data-center '@affine/i18n': link:../i18n - '@blocksuite/blocks': 0.3.1-20230109032243-37ad3ba_yjs@13.5.44 - '@blocksuite/editor': 0.3.1-20230109032243-37ad3ba_yjs@13.5.44 + '@blocksuite/blocks': 0.4.0-20230110112105-ef07332_yjs@13.5.44 + '@blocksuite/editor': 0.4.0-20230110112105-ef07332_yjs@13.5.44 '@blocksuite/icons': 2.0.4_w5j4k42lgipnm43s3brx6h3c34 - '@blocksuite/store': 0.3.1-20230109032243-37ad3ba_yjs@13.5.44 + '@blocksuite/store': 0.4.0-20230110112105-ef07332_yjs@13.5.44 '@emotion/css': 11.10.0 '@emotion/react': 11.10.4_w5j4k42lgipnm43s3brx6h3c34 '@emotion/server': 11.10.0_@emotion+css@11.10.0 @@ -123,8 +123,8 @@ importers: packages/data-center: specifiers: - '@blocksuite/blocks': ^0.3.1-20230109032243-37ad3ba - '@blocksuite/store': ^0.3.1-20230109032243-37ad3ba + '@blocksuite/blocks': 0.4.0-20230110112105-ef07332 + '@blocksuite/store': 0.4.0-20230110112105-ef07332 '@playwright/test': ^1.29.1 '@types/debug': ^4.1.7 debug: ^4.3.4 @@ -140,8 +140,8 @@ importers: y-protocols: ^1.0.5 yjs: ^13.5.44 dependencies: - '@blocksuite/blocks': 0.3.1-20230109032243-37ad3ba_yjs@13.5.44 - '@blocksuite/store': 0.3.1-20230109032243-37ad3ba_yjs@13.5.44 + '@blocksuite/blocks': 0.4.0-20230110112105-ef07332_yjs@13.5.44 + '@blocksuite/store': 0.4.0-20230110112105-ef07332_yjs@13.5.44 debug: 4.3.4 encoding: 0.1.13 firebase: 9.15.0_encoding@0.1.13 @@ -1453,11 +1453,11 @@ packages: to-fast-properties: 2.0.0 dev: true - /@blocksuite/blocks/0.3.1-20230109032243-37ad3ba_yjs@13.5.44: - resolution: {integrity: sha512-UTlbk0Is7TMRBbvUyM2nivbqM/TLwRj1qArMYbOmvDGUNYadWo68cTwv/Ej2WwiKn22q4/4JHryGsv3gTCRz1Q==} + /@blocksuite/blocks/0.4.0-20230110112105-ef07332_yjs@13.5.44: + resolution: {integrity: sha512-dtwZRCWtirmheRQaITPOC/D9LZ3yFuYztqL/y2mz/BRSfHj8I71OVcX0HjFXx2TUdzhkea1GNYbp4226zZIiRA==} dependencies: - '@blocksuite/phasor': 0.3.1-20230109032243-37ad3ba_yjs@13.5.44 - '@blocksuite/store': 0.3.1-20230109032243-37ad3ba_yjs@13.5.44 + '@blocksuite/phasor': 0.4.0-20230110112105-ef07332_yjs@13.5.44 + '@blocksuite/store': 0.4.0-20230110112105-ef07332_yjs@13.5.44 '@tldraw/intersect': 1.8.0 autosize: 5.0.2 highlight.js: 11.7.0 @@ -1473,11 +1473,11 @@ packages: - yjs dev: false - /@blocksuite/editor/0.3.1-20230109032243-37ad3ba_yjs@13.5.44: - resolution: {integrity: sha512-bYbMn4EL/od+xP4K3u2kJT08kJBpK6H7b4cbRb9No3SUwgNHvvVNxia/QH1AQXyKaZQj/DHFgVxrw9GKo2GIPA==} + /@blocksuite/editor/0.4.0-20230110112105-ef07332_yjs@13.5.44: + resolution: {integrity: sha512-UqLVEZq/bKRJfe3e/Teur7grlnSo6oG55s00WSWu+nTIHnvgasIePJ6rpGC/pXwYdsuvVQnQzGesnrapNVtXqA==} dependencies: - '@blocksuite/blocks': 0.3.1-20230109032243-37ad3ba_yjs@13.5.44 - '@blocksuite/store': 0.3.1-20230109032243-37ad3ba_yjs@13.5.44 + '@blocksuite/blocks': 0.4.0-20230110112105-ef07332_yjs@13.5.44 + '@blocksuite/store': 0.4.0-20230110112105-ef07332_yjs@13.5.44 lit: 2.5.0 marked: 4.2.5 turndown: 7.1.1 @@ -1498,16 +1498,16 @@ packages: react: 18.2.0 dev: false - /@blocksuite/phasor/0.3.1-20230109032243-37ad3ba_yjs@13.5.44: - resolution: {integrity: sha512-mL1gSQ3rzrjdQSbWPtgyMXpbbl266UUjw26d0aIjkOh+iMMI6rWtmKWDoiDkO7tejIjwSNQ4w5zJOjJRIj+mSA==} + /@blocksuite/phasor/0.4.0-20230110112105-ef07332_yjs@13.5.44: + resolution: {integrity: sha512-R5j/iK7WBFSk7vSk8HEAsxDwr+xDeY7JuzdGzzwnkcOaxuM6Eea6g0vMw7DPuWDAkvlcP7JsgXLnzWgFZh7qmA==} peerDependencies: yjs: ^13 dependencies: yjs: 13.5.44 dev: false - /@blocksuite/store/0.3.1-20230109032243-37ad3ba_yjs@13.5.44: - resolution: {integrity: sha512-zOUz19jfhuhsUkx9BGEQPZWbPyD/AgX0LB7ShVRdd3YM73x25hD6tPLLz1HEV2b69XokC0P9oSru4aNomm4jkg==} + /@blocksuite/store/0.4.0-20230110112105-ef07332_yjs@13.5.44: + resolution: {integrity: sha512-NisHLf0uSyFu5DUZD13QSsp33C9vnd7Jf7jdLOAPztQ2U05NcGFopjM2InhwBmtmQSHrd/qi25PjgnAJ7/HSNQ==} peerDependencies: yjs: ^13 dependencies: From 104693916f7c4706b2362ceba5a1ce0f46f4a797 Mon Sep 17 00:00:00 2001 From: MingLiang Wang Date: Tue, 10 Jan 2023 20:11:46 +0800 Subject: [PATCH 12/28] feat: change the order of load workspaces --- packages/data-center/src/datacenter.ts | 12 +++++++++++- packages/data-center/src/provider/affine/affine.ts | 6 ------ packages/data-center/src/provider/local/local.ts | 1 - .../data-center/src/workspace-unit-collection.ts | 4 ++-- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index 487e82cd5f..e4fe394c71 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -46,6 +46,10 @@ export class DataCenter { dc.registerProvider(new LocalProvider(getInitParams())); dc.registerProvider(new AffineProvider(getInitParams())); + for (const provider of dc.providerMap.values()) { + await provider.loadWorkspaces(); + } + return dc; } @@ -181,8 +185,14 @@ export class DataCenter { * @param {Function} callback callback function */ public async onWorkspacesChange( - callback: (workspaces: WorkspaceUnitCollectionChangeEvent) => void + callback: (workspaces: WorkspaceUnitCollectionChangeEvent) => void, + { immediate = true }: { immediate?: boolean } ) { + if (immediate) { + callback({ + added: this._workspaceUnitCollection.workspaces, + }); + } this._workspaceUnitCollection.on('change', callback); } diff --git a/packages/data-center/src/provider/affine/affine.ts b/packages/data-center/src/provider/affine/affine.ts index b98a08cf25..cbe5adef18 100644 --- a/packages/data-center/src/provider/affine/affine.ts +++ b/packages/data-center/src/provider/affine/affine.ts @@ -2,7 +2,6 @@ import { BaseProvider } from '../base.js'; import type { ProviderConstructorParams, CreateWorkspaceInfoParams, - UpdateWorkspaceMetaParams, WorkspaceMeta0, } from '../base'; import type { User } from '../../types'; @@ -34,11 +33,6 @@ export class AffineProvider extends BaseProvider { constructor({ apis, ...params }: AffineProviderConstructorParams) { super(params); this._apis = apis || getApis(); - this.init().then(() => { - if (this._apis.token.isLogin) { - this.loadWorkspaces(); - } - }); } override async init() { diff --git a/packages/data-center/src/provider/local/local.ts b/packages/data-center/src/provider/local/local.ts index f87d7c95fb..2bc6bae718 100644 --- a/packages/data-center/src/provider/local/local.ts +++ b/packages/data-center/src/provider/local/local.ts @@ -19,7 +19,6 @@ export class LocalProvider extends BaseProvider { constructor(params: ProviderConstructorParams) { super(params); - this.loadWorkspaces(); } private _storeWorkspaces(workspaces: WorkspaceMeta0[]) { diff --git a/packages/data-center/src/workspace-unit-collection.ts b/packages/data-center/src/workspace-unit-collection.ts index e4dedad8c7..58770e2122 100644 --- a/packages/data-center/src/workspace-unit-collection.ts +++ b/packages/data-center/src/workspace-unit-collection.ts @@ -18,7 +18,7 @@ export interface WorkspaceUnitCollectionScope { } export interface WorkspaceUnitCollectionChangeEvent { - added?: WorkspaceUnit; + added?: WorkspaceUnit[]; deleted?: WorkspaceUnit; updated?: WorkspaceUnit; } @@ -71,7 +71,7 @@ export class WorkspaceUnitCollection { this._events.emit('change', [ { - added: workspaceUnit, + added: [workspaceUnit], } as WorkspaceUnitCollectionChangeEvent, ]); }; From b111c411bd0414f67550b87ff842d9950bf51b3a Mon Sep 17 00:00:00 2001 From: alt0 Date: Tue, 10 Jan 2023 20:22:14 +0800 Subject: [PATCH 13/28] fix: local provider create workspace should wait sync to idb --- .../local/{ => indexeddb}/indexeddb.ts | 0 .../src/provider/local/indexeddb/utils.ts | 20 +++++++++++++++++++ .../data-center/src/provider/local/local.ts | 16 +++++---------- 3 files changed, 25 insertions(+), 11 deletions(-) rename packages/data-center/src/provider/local/{ => indexeddb}/indexeddb.ts (100%) create mode 100644 packages/data-center/src/provider/local/indexeddb/utils.ts diff --git a/packages/data-center/src/provider/local/indexeddb.ts b/packages/data-center/src/provider/local/indexeddb/indexeddb.ts similarity index 100% rename from packages/data-center/src/provider/local/indexeddb.ts rename to packages/data-center/src/provider/local/indexeddb/indexeddb.ts diff --git a/packages/data-center/src/provider/local/indexeddb/utils.ts b/packages/data-center/src/provider/local/indexeddb/utils.ts new file mode 100644 index 0000000000..8ca5c9b46d --- /dev/null +++ b/packages/data-center/src/provider/local/indexeddb/utils.ts @@ -0,0 +1,20 @@ +import assert from 'assert'; +import * as idb from 'lib0/indexeddb.js'; +import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store'; + +const { encodeStateAsUpdate } = BlocksuiteWorkspace.Y; + +export const initStore = async (blocksuiteWorkspace: BlocksuiteWorkspace) => { + const workspaceId = blocksuiteWorkspace.room; + assert(workspaceId); + await idb.deleteDB(workspaceId); + const db = await idb.openDB(workspaceId, db => + idb.createStores(db, [['updates', { autoIncrement: true }], ['custom']]) + ); + const currState = encodeStateAsUpdate(blocksuiteWorkspace.doc); + const [updatesStore] = idb.transact(db, ['updates']); // , 'readonly') + + if (updatesStore) { + await idb.addAutoKey(updatesStore, currState); + } +}; diff --git a/packages/data-center/src/provider/local/local.ts b/packages/data-center/src/provider/local/local.ts index f87d7c95fb..e662320b64 100644 --- a/packages/data-center/src/provider/local/local.ts +++ b/packages/data-center/src/provider/local/local.ts @@ -7,7 +7,8 @@ import type { } from '../base'; import { varStorage as storage } from 'lib0/storage'; import { Workspace as BlocksuiteWorkspace, uuidv4 } from '@blocksuite/store'; -import { IndexedDBProvider } from './indexeddb.js'; +import { IndexedDBProvider } from './indexeddb/indexeddb.js'; +import { initStore } from './indexeddb/utils.js'; import assert from 'assert'; import { setDefaultAvatar } from '../utils.js'; @@ -101,21 +102,12 @@ export class LocalProvider extends BaseProvider { ): Promise { const workspaceId = blocksuiteWorkspace.room; assert(workspaceId, 'Blocksuite Workspace without room(workspaceId).'); - assert(meta.name, 'Workspace name is required'); this._logger('Creating affine workspace'); const workspaceInfo: WorkspaceMeta0 = { - name: meta.name, - id: workspaceId, - published: false, - avatar: '', - owner: undefined, - syncMode: 'core', - memberCount: 1, - provider: 'local', + ...meta, }; - this.linkLocal(blocksuiteWorkspace); blocksuiteWorkspace.meta.setName(meta.name); if (!meta.avatar) { @@ -123,6 +115,8 @@ export class LocalProvider extends BaseProvider { workspaceInfo.avatar = blocksuiteWorkspace.meta.avatar; } + await initStore(blocksuiteWorkspace); + this._workspaces.add(workspaceInfo); this._storeWorkspaces(this._workspaces.list()); From 412d5205c83d1eacf9c1bee03e9ef5c2444768bf Mon Sep 17 00:00:00 2001 From: MingLiang Wang Date: Tue, 10 Jan 2023 21:35:22 +0800 Subject: [PATCH 14/28] feat: add a time out --- packages/data-center/src/provider/affine/affine.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/data-center/src/provider/affine/affine.ts b/packages/data-center/src/provider/affine/affine.ts index cbe5adef18..890fc6dc00 100644 --- a/packages/data-center/src/provider/affine/affine.ts +++ b/packages/data-center/src/provider/affine/affine.ts @@ -16,6 +16,7 @@ import { getApis } from './apis/index.js'; import type { Apis, WorkspaceDetail, Callback } from './apis'; import { setDefaultAvatar } from '../utils.js'; import { MessageCode } from '../../message'; +import { blob } from 'stream/consumers'; export interface AffineProviderConstructorParams extends ProviderConstructorParams { @@ -348,9 +349,10 @@ export class AffineProvider extends BaseProvider { 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((resolve, reject) => { ws.once('synced', () => { - resolve(); + setTimeout(() => resolve(), 1000); }); ws.once('lost-connection', () => reject()); ws.once('connection-error', () => reject()); From ad5e7cb2f4ee4606e6f0c96a12262661416f067a Mon Sep 17 00:00:00 2001 From: MingLiang Wang Date: Tue, 10 Jan 2023 21:44:40 +0800 Subject: [PATCH 15/28] feat: fix data center --- packages/data-center/src/datacenter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index e4fe394c71..8e70718c36 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -186,7 +186,7 @@ export class DataCenter { */ public async onWorkspacesChange( callback: (workspaces: WorkspaceUnitCollectionChangeEvent) => void, - { immediate = true }: { immediate?: boolean } + { immediate = true }: { immediate?: boolean } = {} ) { if (immediate) { callback({ From eab86dffb8a8ce01b1a6333cd2353d7f6980d82d Mon Sep 17 00:00:00 2001 From: MingLiang Wang Date: Tue, 10 Jan 2023 22:44:49 +0800 Subject: [PATCH 16/28] feat: add logout --- packages/data-center/src/datacenter.ts | 8 ++++---- packages/data-center/src/provider/affine/affine.ts | 8 +++++++- packages/data-center/src/provider/affine/apis/token.ts | 4 ++++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index 8e70718c36..64ead6cfd9 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -43,8 +43,8 @@ export class DataCenter { }; }; // TODO: switch different provider - dc.registerProvider(new LocalProvider(getInitParams())); - dc.registerProvider(new AffineProvider(getInitParams())); + await dc.registerProvider(new LocalProvider(getInitParams())); + await dc.registerProvider(new AffineProvider(getInitParams())); for (const provider of dc.providerMap.values()) { await provider.loadWorkspaces(); @@ -57,12 +57,12 @@ export class DataCenter { * Register provider. * We will automatically set the first provider to default provider. */ - registerProvider(provider: BaseProvider) { + async registerProvider(provider: BaseProvider) { if (!this._mainProvider) { this._mainProvider = provider; } - provider.init(); + await provider.init(); this.providerMap.set(provider.id, provider); } diff --git a/packages/data-center/src/provider/affine/affine.ts b/packages/data-center/src/provider/affine/affine.ts index 890fc6dc00..0480a60a1b 100644 --- a/packages/data-center/src/provider/affine/affine.ts +++ b/packages/data-center/src/provider/affine/affine.ts @@ -16,7 +16,7 @@ import { getApis } from './apis/index.js'; import type { Apis, WorkspaceDetail, Callback } from './apis'; import { setDefaultAvatar } from '../utils.js'; import { MessageCode } from '../../message'; -import { blob } from 'stream/consumers'; +import { token } from './apis/token.js'; export interface AffineProviderConstructorParams extends ProviderConstructorParams { @@ -213,6 +213,7 @@ export class AffineProvider extends BaseProvider { public override async getUserInfo(): Promise { const user = this._apis.token.user; + await this.init; return user ? { id: user.id, @@ -359,4 +360,9 @@ export class AffineProvider extends BaseProvider { }); return to; } + + public override async logout(): Promise { + token.clear(); + storage.removeItem('token'); + } } diff --git a/packages/data-center/src/provider/affine/apis/token.ts b/packages/data-center/src/provider/affine/apis/token.ts index 62da8def94..4df48b3075 100644 --- a/packages/data-center/src/provider/affine/apis/token.ts +++ b/packages/data-center/src/provider/affine/apis/token.ts @@ -140,6 +140,10 @@ class Token { this.callbacks.splice(index, 1); } } + + clear() { + this._setToken(); + } } export const token = new Token(); From cdf07fd7c2ecce72e60bc97050c725e9965a5435 Mon Sep 17 00:00:00 2001 From: MingLiang Wang Date: Tue, 10 Jan 2023 23:01:50 +0800 Subject: [PATCH 17/28] feat: remove workspaces pool --- packages/data-center/src/datacenter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index 64ead6cfd9..76499b11e5 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -123,7 +123,7 @@ export class DataCenter { const workspaceInfo = this._workspaceUnitCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); return ( - this._workspaceInstances.get(workspaceId) || + // this._workspaceInstances.get(workspaceId) || createBlocksuiteWorkspace(workspaceId) ); } From 48264edf9184bd409b443d6014a6a2c2dab0e3d1 Mon Sep 17 00:00:00 2001 From: MingLiang Wang Date: Wed, 11 Jan 2023 12:34:57 +0800 Subject: [PATCH 18/28] feat: add query members --- packages/data-center/src/datacenter.ts | 14 ++++++++++++++ packages/data-center/src/provider/affine/affine.ts | 6 +++++- packages/data-center/src/provider/base.ts | 11 +++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index 76499b11e5..1221b114f3 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -384,6 +384,20 @@ export class DataCenter { return (await blobStorage?.set(blob)) || ''; } + /** + * get members of a workspace + * @param workspaceId + */ + async getMembers(workspaceId: string) { + const workspaceInfo = this._workspaceUnitCollection.find(workspaceId); + assert(workspaceInfo, 'Workspace not found'); + const provider = this.providerMap.get(workspaceInfo.provider); + if (provider) { + return await provider.getWorkspaceMembers(workspaceId); + } + return []; + } + onMessage(cb: (message: Message) => void) { return this._messageCenter.onMessage(cb); } diff --git a/packages/data-center/src/provider/affine/affine.ts b/packages/data-center/src/provider/affine/affine.ts index 0480a60a1b..0794ca10e9 100644 --- a/packages/data-center/src/provider/affine/affine.ts +++ b/packages/data-center/src/provider/affine/affine.ts @@ -12,7 +12,7 @@ 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, Member } from './apis/index.js'; import type { Apis, WorkspaceDetail, Callback } from './apis'; import { setDefaultAvatar } from '../utils.js'; import { MessageCode } from '../../message'; @@ -365,4 +365,8 @@ export class AffineProvider extends BaseProvider { token.clear(); storage.removeItem('token'); } + + public override async getWorkspaceMembers(id: string) { + return this._apis.getWorkspaceMembers({ id }); + } } diff --git a/packages/data-center/src/provider/base.ts b/packages/data-center/src/provider/base.ts index 6542b279f1..17834abf34 100644 --- a/packages/data-center/src/provider/base.ts +++ b/packages/data-center/src/provider/base.ts @@ -3,6 +3,7 @@ import { MessageCenter } from '../message'; import { Logger, User } from '../types'; import type { WorkspaceUnitCollectionScope } from '../workspace-unit-collection'; import type { WorkspaceUnitCtorParams } from '../workspace-unit'; +import { Member } from './affine/apis'; const defaultLogger = () => { return; @@ -213,4 +214,14 @@ export class BaseProvider { from; return to; } + + /** + * get workspace members + * @param {string} workspaceId + * @returns + */ + public getWorkspaceMembers(workspaceId: string): Promise { + workspaceId; + return Promise.resolve([]); + } } From b49a32c9c764368d8170efdb687030bce7c52b9b Mon Sep 17 00:00:00 2001 From: DarkSky Date: Wed, 11 Jan 2023 12:53:54 +0800 Subject: [PATCH 19/28] fix: dev server endpoint fix --- packages/app/next.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/next.config.js b/packages/app/next.config.js index df91a70108..478bbfa12e 100644 --- a/packages/app/next.config.js +++ b/packages/app/next.config.js @@ -11,7 +11,7 @@ const EDITOR_VERSION = enableDebugLocal const profileTarget = { ac: '100.85.73.88:12001', - dev: '100.85.73.88:12001', + dev: '100.77.180.48:11001', local: '127.0.0.1:3000', }; From 57b204b6e2b7daacc385ea5d75b618cc40cfc03a Mon Sep 17 00:00:00 2001 From: alt0 Date: Wed, 11 Jan 2023 13:52:56 +0800 Subject: [PATCH 20/28] test: fix test case --- packages/data-center/src/workspace-unit-collection.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/data-center/src/workspace-unit-collection.spec.ts b/packages/data-center/src/workspace-unit-collection.spec.ts index 06eee95bd0..f010c31d12 100644 --- a/packages/data-center/src/workspace-unit-collection.spec.ts +++ b/packages/data-center/src/workspace-unit-collection.spec.ts @@ -11,7 +11,7 @@ test.describe.serial('workspace meta collection observable', () => { workspaceUnitCollection.once( 'change', (event: WorkspaceUnitCollectionChangeEvent) => { - expect(event.added?.id).toEqual('123'); + expect(event.added?.[0]?.id).toEqual('123'); } ); scope.add({ From db74706eca35ee74f5d3c64c00789c8aebf2d7c9 Mon Sep 17 00:00:00 2001 From: alt0 Date: Wed, 11 Jan 2023 15:07:04 +0800 Subject: [PATCH 21/28] refactor: datacenter only export workspaceUnit --- packages/data-center/src/datacenter.ts | 22 ++++++++++++++-------- packages/data-center/src/workspace-unit.ts | 4 ++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index 1221b114f3..4b58662382 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -100,7 +100,8 @@ export class DataCenter { const workspace = createBlocksuiteWorkspace(workspaceMeta.id); await this._mainProvider.createWorkspace(workspace, workspaceMeta); - return workspace; + const workspaceUnit = this._workspaceUnitCollection.find(workspaceMeta.id); + return workspaceUnit; } /** @@ -154,18 +155,23 @@ export class DataCenter { * @returns {Promise} */ public async loadWorkspace(workspaceId: string) { - const workspaceInfo = this._workspaceUnitCollection.find(workspaceId); - assert(workspaceInfo, 'Workspace not found'); - const currentProvider = this.providerMap.get(workspaceInfo.provider); + const workspaceUnit = this._workspaceUnitCollection.find(workspaceId); + assert(workspaceUnit, 'Workspace not found'); + const currentProvider = this.providerMap.get(workspaceUnit.provider); if (currentProvider) { currentProvider.closeWorkspace(workspaceId); } - const provider = this.providerMap.get(workspaceInfo.provider); - assert(provider, `provide '${workspaceInfo.provider}' is not registered`); - this._logger(`Loading ${workspaceInfo.provider} workspace: `, workspaceId); + const provider = this.providerMap.get(workspaceUnit.provider); + assert(provider, `provide '${workspaceUnit.provider}' is not registered`); + this._logger(`Loading ${workspaceUnit.provider} workspace: `, workspaceId); const workspace = this._getBlocksuiteWorkspace(workspaceId); this._workspaceInstances.set(workspaceId, workspace); - return await provider.warpWorkspace(workspace); + await provider.warpWorkspace(workspace); + this._workspaceUnitCollection.workspaces.forEach(workspaceUnit => { + workspaceUnit.setBlocksuiteWorkspace(null); + }); + workspaceUnit.setBlocksuiteWorkspace(workspace); + return workspaceUnit; } /** diff --git a/packages/data-center/src/workspace-unit.ts b/packages/data-center/src/workspace-unit.ts index 527e8647ef..4069b4200a 100644 --- a/packages/data-center/src/workspace-unit.ts +++ b/packages/data-center/src/workspace-unit.ts @@ -30,7 +30,7 @@ export class WorkspaceUnit { public provider!: string; public syncMode: 'all' | 'core' = 'core'; - private _blocksuiteWorkspace?: BlocksuiteWorkspace; + private _blocksuiteWorkspace?: BlocksuiteWorkspace | null; constructor(params: WorkspaceUnitCtorParams) { this.id = params.id; @@ -41,7 +41,7 @@ export class WorkspaceUnit { return this._blocksuiteWorkspace; } - setBlocksuiteWorkspace(blocksuiteWorkspace: BlocksuiteWorkspace) { + setBlocksuiteWorkspace(blocksuiteWorkspace: BlocksuiteWorkspace | null) { if (blocksuiteWorkspace?.room !== this.id) { throw new Error('Workspace id inconsistent.'); } From 8e091a37e2c8e3f68dd5dbb11581c979706acd71 Mon Sep 17 00:00:00 2001 From: alt0 Date: Wed, 11 Jan 2023 15:14:17 +0800 Subject: [PATCH 22/28] fix: workspace unit type --- packages/data-center/src/workspace-unit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/data-center/src/workspace-unit.ts b/packages/data-center/src/workspace-unit.ts index 4069b4200a..2fc4935d51 100644 --- a/packages/data-center/src/workspace-unit.ts +++ b/packages/data-center/src/workspace-unit.ts @@ -13,7 +13,7 @@ export interface WorkspaceUnitCtorParams { provider: string; syncMode: SyncMode; - blocksuiteWorkspace?: BlocksuiteWorkspace; + blocksuiteWorkspace?: BlocksuiteWorkspace | null; } export type UpdateWorkspaceUnitParams = Partial< From 7e608e48ecd9e439ecab79c69f5d395628a6e13f Mon Sep 17 00:00:00 2001 From: alt0 Date: Wed, 11 Jan 2023 15:23:03 +0800 Subject: [PATCH 23/28] fix: workspace unit setBlocksuiteWorkspace support null --- packages/data-center/src/workspace-unit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/data-center/src/workspace-unit.ts b/packages/data-center/src/workspace-unit.ts index 2fc4935d51..b7244c0b5e 100644 --- a/packages/data-center/src/workspace-unit.ts +++ b/packages/data-center/src/workspace-unit.ts @@ -42,7 +42,7 @@ export class WorkspaceUnit { } setBlocksuiteWorkspace(blocksuiteWorkspace: BlocksuiteWorkspace | null) { - if (blocksuiteWorkspace?.room !== this.id) { + if (blocksuiteWorkspace && blocksuiteWorkspace?.room !== this.id) { throw new Error('Workspace id inconsistent.'); } this._blocksuiteWorkspace = blocksuiteWorkspace; From a11f411f6cef56beefd674f0b5fb25603996631d Mon Sep 17 00:00:00 2001 From: alt0 Date: Wed, 11 Jan 2023 15:27:31 +0800 Subject: [PATCH 24/28] fix: enable and update workspace use workspaceUnit as params --- packages/data-center/src/datacenter.ts | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index 4b58662382..616345be27 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -13,6 +13,7 @@ import assert from 'assert'; import { getLogger } from './logger'; import { createBlocksuiteWorkspace } from './utils/index.js'; import { MessageCenter } from './message'; +import type { WorkspaceUnit } from './workspace-unit'; /** * @class DataCenter @@ -209,9 +210,11 @@ export class DataCenter { */ public async updateWorkspaceMeta( { name, avatar }: UpdateWorkspaceMetaParams, - workspace: BlocksuiteWorkspace + workspaceUnit: WorkspaceUnit ) { - assert(workspace?.room, 'No workspace to set meta'); + assert(workspaceUnit?.id, 'No workspace to set meta'); + const workspace = workspaceUnit.blocksuiteWorkspace; + assert(workspace); const update: Partial = {}; if (name) { workspace.meta.setName(name); @@ -222,10 +225,10 @@ export class DataCenter { update.avatar = avatar; } // may run for change workspace meta - const workspaceInfo = this._workspaceUnitCollection.find(workspace.room); + const workspaceInfo = this._workspaceUnitCollection.find(workspaceUnit.id); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); - provider?.updateWorkspaceMeta(workspace.room, update); + provider?.updateWorkspaceMeta(workspaceUnit.id, update); } /** @@ -334,9 +337,13 @@ export class DataCenter { * Enable workspace cloud * @param {string} id ID of workspace. */ - public async enableWorkspaceCloud(workspace: BlocksuiteWorkspace) { - assert(workspace?.room, 'No workspace to enable cloud'); - return await this._transWorkspaceProvider(workspace, 'affine'); + public async enableWorkspaceCloud(workspace: WorkspaceUnit) { + assert(workspace?.id, 'No workspace to enable cloud'); + assert(workspace.blocksuiteWorkspace); + return await this._transWorkspaceProvider( + workspace.blocksuiteWorkspace, + 'affine' + ); } /** From 250ac0e2eaddb8b47f76fd32499c06a7e0dfd4f2 Mon Sep 17 00:00:00 2001 From: alt0 Date: Wed, 11 Jan 2023 15:36:13 +0800 Subject: [PATCH 25/28] fix: setBlob and getBlob use WorkspaceUnit --- packages/data-center/src/datacenter.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index 616345be27..f1eb556ecf 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -153,7 +153,7 @@ export class DataCenter { /** * load workspace instance by id * @param {string} workspaceId workspace id - * @returns {Promise} + * @returns {Promise} */ public async loadWorkspace(workspaceId: string) { const workspaceUnit = this._workspaceUnitCollection.find(workspaceId); @@ -206,7 +206,7 @@ export class DataCenter { /** * change workspaces meta * @param {WorkspaceMeta} workspaceMeta workspace meta - * @param {BlocksuiteWorkspace} workspace workspace instance + * @param {WorkspaceUnit} workspace workspace instance */ public async updateWorkspaceMeta( { name, avatar }: UpdateWorkspaceMetaParams, @@ -380,10 +380,10 @@ export class DataCenter { * @returns {Promise} blob url */ async getBlob( - workspace: BlocksuiteWorkspace, + workspaceUnit: WorkspaceUnit, id: string ): Promise { - const blob = await workspace.blobs; + const blob = await workspaceUnit.blocksuiteWorkspace?.blobs; return (await blob?.get(id)) || ''; } @@ -392,8 +392,8 @@ export class DataCenter { * @param id * @returns {Promise} blob url */ - async setBlob(workspace: BlocksuiteWorkspace, blob: Blob): Promise { - const blobStorage = await workspace.blobs; + async setBlob(workspace: WorkspaceUnit, blob: Blob): Promise { + const blobStorage = await workspace.blocksuiteWorkspace?.blobs; return (await blobStorage?.set(blob)) || ''; } From 6459faeeb908378a20fa8adbfd62ff2a5509ae4b Mon Sep 17 00:00:00 2001 From: alt0 Date: Wed, 11 Jan 2023 16:50:40 +0800 Subject: [PATCH 26/28] fix: local provider save workspaces info --- .../data-center/src/provider/local/local.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/data-center/src/provider/local/local.ts b/packages/data-center/src/provider/local/local.ts index 59ce59a337..ec07ef568e 100644 --- a/packages/data-center/src/provider/local/local.ts +++ b/packages/data-center/src/provider/local/local.ts @@ -23,7 +23,23 @@ export class LocalProvider extends BaseProvider { } private _storeWorkspaces(workspaces: WorkspaceMeta0[]) { - storage.setItem(WORKSPACE_KEY, JSON.stringify(workspaces)); + 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, + }; + }) + ) + ); } public override async linkLocal(workspace: BlocksuiteWorkspace) { From 62826f7ab772239791308dea4c79d2ea253b9be3 Mon Sep 17 00:00:00 2001 From: MingLiang Wang Date: Wed, 11 Jan 2023 17:21:41 +0800 Subject: [PATCH 27/28] feat: add channel --- packages/data-center/package.json | 1 - .../data-center/src/provider/affine/affine.ts | 32 ++++++++++- .../src/provider/affine/channel.ts | 53 +++++++++++++++++++ .../src/provider/local/indexeddb/indexeddb.ts | 9 +++- pnpm-lock.yaml | 52 ++++++++++++++++-- 5 files changed, 138 insertions(+), 9 deletions(-) create mode 100644 packages/data-center/src/provider/affine/channel.ts diff --git a/packages/data-center/package.json b/packages/data-center/package.json index 606f6e196c..15fdc9ba61 100644 --- a/packages/data-center/package.json +++ b/packages/data-center/package.json @@ -36,7 +36,6 @@ "ky-universal": "^0.11.0", "lib0": "^0.2.58", "swr": "^2.0.0", - "yjs": "^13.5.44", "y-protocols": "^1.0.5" }, "peerDependencies": { diff --git a/packages/data-center/src/provider/affine/affine.ts b/packages/data-center/src/provider/affine/affine.ts index 0794ca10e9..977edb557e 100644 --- a/packages/data-center/src/provider/affine/affine.ts +++ b/packages/data-center/src/provider/affine/affine.ts @@ -7,33 +7,47 @@ import type { import type { User } from '../../types'; import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store'; import { BlockSchema } from '@blocksuite/blocks/models'; -import { applyUpdate, encodeStateAsUpdate } from 'yjs'; import { storage } from './storage.js'; import assert from 'assert'; import { WebsocketProvider } from './sync.js'; // import { IndexedDBProvider } from '../local/indexeddb'; -import { getApis, Member } from './apis/index.js'; +import { getApis } from './apis/index.js'; import type { Apis, WorkspaceDetail, Callback } from './apis'; import { setDefaultAvatar } from '../utils.js'; import { MessageCode } from '../../message'; import { token } from './apis/token.js'; +import { WebsocketClient } from './channel'; export interface AffineProviderConstructorParams extends ProviderConstructorParams { apis?: Apis; } +const { + Y: { applyUpdate, encodeStateAsUpdate }, +} = BlocksuiteWorkspace; + export class AffineProvider extends BaseProvider { public id = 'affine'; private _workspacesCache: Map = new Map(); private _onTokenRefresh?: Callback = undefined; private _wsMap: Map = new Map(); private _apis: Apis; + private _channel: WebsocketClient; // private _idbMap: Map = 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() { @@ -64,6 +78,15 @@ export class AffineProvider extends BaseProvider { } } + private _connectChannel() { + this._channel.connect(); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this._channel.on('message', (message: any) => { + console.log('message', message); + }); + } + private _getWebsocketProvider(workspace: BlocksuiteWorkspace) { const { doc, room } = workspace; assert(room); @@ -206,6 +229,9 @@ export class AffineProvider extends BaseProvider { } } const user = await this._apis.signInWithGoogle?.(); + if (!this._channel.connected) { + this._connectChannel(); + } if (!user) { this._messageCenter.send(MessageCode.loginError); } @@ -363,6 +389,8 @@ export class AffineProvider extends BaseProvider { public override async logout(): Promise { token.clear(); + this._channel.disconnect(); + this._wsMap.forEach(ws => ws.disconnect()); storage.removeItem('token'); } diff --git a/packages/data-center/src/provider/affine/channel.ts b/packages/data-center/src/provider/affine/channel.ts new file mode 100644 index 0000000000..8d4ae85b09 --- /dev/null +++ b/packages/data-center/src/provider/affine/channel.ts @@ -0,0 +1,53 @@ +import websocket from 'lib0/websocket'; +import { Logger } from 'src/types'; +import { token } from './apis/token'; + +const RECONNECT_INTERVAL_TIME = 5000; +const MAX_RECONNECT_TIMES = 50; + +export class WebsocketClient extends websocket.WebsocketClient { + public shouldReconnect = false; + private _reconnectInterval: number | null = null; + private _logger: Logger; + constructor( + url: string, + logger: Logger, + options?: { binaryType: 'arraybuffer' | 'blob' | null } + ) { + super(url, options); + this._logger = logger; + this._setupChannel(); + } + + private _setupChannel() { + this.on('connect', () => { + this._logger('Affine channel connected'); + this.shouldReconnect = true; + if (this._reconnectInterval) { + window.clearInterval(this._reconnectInterval); + } + }); + + this.on('disconnect', ({ error }: { error: Error }) => { + if (error) { + let times = 0; + // Try reconnect if connect error has occurred + this._reconnectInterval = window.setInterval(() => { + if (this.shouldReconnect && token.isLogin && !this.connected) { + try { + this.connect(); + this._logger(`try reconnect channel ${++times} times`); + if (times > MAX_RECONNECT_TIMES) { + this._logger('reconnect failed, max reconnect times reached'); + this._reconnectInterval && + window.clearInterval(this._reconnectInterval); + } + } catch (e) { + this._logger('reconnect failed', e); + } + } + }, RECONNECT_INTERVAL_TIME); + } + }); + } +} diff --git a/packages/data-center/src/provider/local/indexeddb/indexeddb.ts b/packages/data-center/src/provider/local/indexeddb/indexeddb.ts index bece021b13..cc79a7d906 100644 --- a/packages/data-center/src/provider/local/indexeddb/indexeddb.ts +++ b/packages/data-center/src/provider/local/indexeddb/indexeddb.ts @@ -1,14 +1,19 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import * as idb from 'lib0/indexeddb.js'; import { Observable } from 'lib0/observable.js'; -import type { Doc } from 'yjs'; -import { applyUpdate, encodeStateAsUpdate, transact } from 'yjs'; +import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store'; const customStoreName = 'custom'; const updatesStoreName = 'updates'; const PREFERRED_TRIM_SIZE = 500; +const { + Y: { applyUpdate, transact, encodeStateAsUpdate }, +} = BlocksuiteWorkspace; + +type Doc = Parameters[0]; + const fetchUpdates = async (provider: IndexedDBProvider) => { const [updatesStore] = idb.transact(provider.db as IDBDatabase, [ updatesStoreName, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f7b81fb5d..e401acf9ae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -140,10 +140,9 @@ importers: swr: ^2.0.0 typescript: ^4.8.4 y-protocols: ^1.0.5 - yjs: ^13.5.44 dependencies: - '@blocksuite/blocks': 0.4.0-20230110112105-ef07332_yjs@13.5.44 - '@blocksuite/store': 0.4.0-20230110112105-ef07332_yjs@13.5.44 + '@blocksuite/blocks': 0.4.0-20230110112105-ef07332 + '@blocksuite/store': 0.4.0-20230110112105-ef07332 debug: 4.3.4 encoding: 0.1.13 firebase: 9.15.0_encoding@0.1.13 @@ -153,7 +152,6 @@ importers: lib0: 0.2.58 swr: 2.0.0 y-protocols: 1.0.5 - yjs: 13.5.44 devDependencies: '@playwright/test': 1.29.1 '@types/debug': 4.1.7 @@ -1455,6 +1453,26 @@ packages: to-fast-properties: 2.0.0 dev: true + /@blocksuite/blocks/0.4.0-20230110112105-ef07332: + resolution: {integrity: sha512-dtwZRCWtirmheRQaITPOC/D9LZ3yFuYztqL/y2mz/BRSfHj8I71OVcX0HjFXx2TUdzhkea1GNYbp4226zZIiRA==} + dependencies: + '@blocksuite/phasor': 0.4.0-20230110112105-ef07332 + '@blocksuite/store': 0.4.0-20230110112105-ef07332 + '@tldraw/intersect': 1.8.0 + autosize: 5.0.2 + highlight.js: 11.7.0 + hotkeys-js: 3.10.1 + lit: 2.5.0 + perfect-freehand: 1.2.0 + quill: 1.3.7 + quill-cursors: 4.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + - yjs + dev: false + /@blocksuite/blocks/0.4.0-20230110112105-ef07332_yjs@13.5.44: resolution: {integrity: sha512-dtwZRCWtirmheRQaITPOC/D9LZ3yFuYztqL/y2mz/BRSfHj8I71OVcX0HjFXx2TUdzhkea1GNYbp4226zZIiRA==} dependencies: @@ -1500,6 +1518,12 @@ packages: react: 18.2.0 dev: false + /@blocksuite/phasor/0.4.0-20230110112105-ef07332: + resolution: {integrity: sha512-R5j/iK7WBFSk7vSk8HEAsxDwr+xDeY7JuzdGzzwnkcOaxuM6Eea6g0vMw7DPuWDAkvlcP7JsgXLnzWgFZh7qmA==} + peerDependencies: + yjs: ^13 + dev: false + /@blocksuite/phasor/0.4.0-20230110112105-ef07332_yjs@13.5.44: resolution: {integrity: sha512-R5j/iK7WBFSk7vSk8HEAsxDwr+xDeY7JuzdGzzwnkcOaxuM6Eea6g0vMw7DPuWDAkvlcP7JsgXLnzWgFZh7qmA==} peerDependencies: @@ -1508,6 +1532,26 @@ packages: yjs: 13.5.44 dev: false + /@blocksuite/store/0.4.0-20230110112105-ef07332: + resolution: {integrity: sha512-NisHLf0uSyFu5DUZD13QSsp33C9vnd7Jf7jdLOAPztQ2U05NcGFopjM2InhwBmtmQSHrd/qi25PjgnAJ7/HSNQ==} + peerDependencies: + yjs: ^13 + dependencies: + '@types/flexsearch': 0.7.3 + '@types/quill': 1.3.10 + buffer: 6.0.3 + flexsearch: 0.7.21 + idb-keyval: 6.2.0 + ky: 0.33.1 + lib0: 0.2.58 + y-protocols: 1.0.5 + y-webrtc: 10.2.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: false + /@blocksuite/store/0.4.0-20230110112105-ef07332_yjs@13.5.44: resolution: {integrity: sha512-NisHLf0uSyFu5DUZD13QSsp33C9vnd7Jf7jdLOAPztQ2U05NcGFopjM2InhwBmtmQSHrd/qi25PjgnAJ7/HSNQ==} peerDependencies: From 7e771756793faee5eb480f9c053c7b9f9ce8f3d2 Mon Sep 17 00:00:00 2001 From: MingLiang Wang Date: Wed, 11 Jan 2023 17:30:16 +0800 Subject: [PATCH 28/28] feat: update invite function name --- packages/data-center/src/datacenter.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index f1eb556ecf..d592d2f480 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -255,12 +255,17 @@ export class DataCenter { } } - public async inviteMember(id: string, email: string) { - const workspaceInfo = this._workspaceUnitCollection.find(id); + /** + * invite the new member to the workspace + * @param {string} workspaceId workspace id + * @param {string} email + */ + public async inviteMember(workspaceId: string, email: string) { + const workspaceInfo = this._workspaceUnitCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); if (provider) { - await provider.invite(id, email); + await provider.invite(workspaceId, email); } }