From 00dd7e9621fea93ada6fd3a020cef3758ecc357b Mon Sep 17 00:00:00 2001 From: DarkSky Date: Wed, 4 Jan 2023 22:14:48 +0800 Subject: [PATCH] feat: init data from cloud --- packages/data-center/src/datacenter.ts | 9 +- .../data-center/src/provider/affine/index.ts | 101 ++++++++++++++---- packages/data-center/src/provider/base.ts | 8 +- .../data-center/src/provider/local/index.ts | 10 +- 4 files changed, 90 insertions(+), 38 deletions(-) diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index 942720869..3e31e9f1d 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -37,7 +37,6 @@ export class DataCenter { readonly signals = { listAdd: new Signal(), listRemove: new Signal(), - workspaceLoaded: new Signal(), }; static async init(debug: boolean): Promise { @@ -63,12 +62,6 @@ export class DataCenter { this.signals.listRemove.on(workspace => { this._config.delete(`list:${workspace}`); }); - this.signals.workspaceLoaded.on(e => { - this._config.set(`list:${e.workspace}`, { - provider: e.provider, - locally: e.locally, - }); - }); } get apis(): Readonly { @@ -141,7 +134,7 @@ export class DataCenter { const logger = this._logger.extend(`auth:${providerId}`); logger.enabled = this._logger.enabled; - await Provider.auth(config, logger); + await Provider.auth(config, logger, this.signals); } } diff --git a/packages/data-center/src/provider/affine/index.ts b/packages/data-center/src/provider/affine/index.ts index 6f1824a15..d2323f4ae 100644 --- a/packages/data-center/src/provider/affine/index.ts +++ b/packages/data-center/src/provider/affine/index.ts @@ -1,11 +1,17 @@ import assert from 'assert'; -import { applyUpdate } from 'yjs'; +import { applyUpdate, Doc } from 'yjs'; -import type { ConfigStore, InitialParams, Logger } from '../index.js'; +import type { + ConfigStore, + DataCenterSignals, + InitialParams, + Logger, +} from '../index.js'; import { token, Callback, getApis } from '../../apis/index.js'; import { LocalProvider } from '../local/index.js'; import { WebsocketProvider } from './sync.js'; +import { IndexedDBProvider } from '../local/indexeddb.js'; export class AffineProvider extends LocalProvider { static id = 'affine'; @@ -55,7 +61,14 @@ export class AffineProvider extends LocalProvider { } async initData() { - await super.initData(); + const databases = await indexedDB.databases(); + await super.initData( + // set locally to true if exists a same name db + databases + .map(db => db.name) + .filter(v => v) + .includes(this._workspace.room) + ); const workspace = this._workspace; const doc = workspace.doc; @@ -64,23 +77,29 @@ export class AffineProvider extends LocalProvider { if (workspace.room && token.isLogin) { try { - const updates = await this._apis.downloadWorkspace(workspace.room); - if (updates) { - await new Promise(resolve => { - doc.once('update', resolve); - applyUpdate(doc, new Uint8Array(updates)); - }); - // Wait for ws synchronization to complete, otherwise the data will be modified in reverse, which can be optimized later - this._ws = new WebsocketProvider('/', workspace.room, doc); - await new Promise((resolve, reject) => { - // TODO: synced will also be triggered on reconnection after losing sync - // There needs to be an event mechanism to emit the synchronization state to the upper layer - assert(this._ws); - this._ws.once('synced', () => resolve()); - this._ws.once('lost-connection', () => resolve()); - this._ws.once('connection-error', () => reject()); - }); - } + // init data from cloud + await AffineProvider._initCloudDoc( + workspace.room, + doc, + this._logger, + this._signals + ); + + // Wait for ws synchronization to complete, otherwise the data will be modified in reverse, which can be optimized later + this._ws = new WebsocketProvider('/', workspace.room, doc); + await new Promise((resolve, reject) => { + // TODO: synced will also be triggered on reconnection after losing sync + // There needs to be an event mechanism to emit the synchronization state to the upper layer + assert(this._ws); + this._ws.once('synced', () => resolve()); + this._ws.once('lost-connection', () => resolve()); + this._ws.once('connection-error', () => reject()); + }); + this._signals.listAdd.emit({ + workspace: workspace.room, + provider: this.id, + locally: true, + }); } catch (e) { this._logger('Failed to init cloud workspace', e); } @@ -92,7 +111,38 @@ export class AffineProvider extends LocalProvider { doc.getMap('space:meta'); } - static async auth(config: Readonly>, logger: Logger) { + private static async _initCloudDoc( + workspace: string, + doc: Doc, + logger: Logger, + signals: DataCenterSignals + ) { + const apis = getApis(); + logger(`Loading ${workspace}...`); + const updates = await apis.downloadWorkspace(workspace); + if (updates) { + await new Promise(resolve => { + doc.once('update', resolve); + applyUpdate(doc, new Uint8Array(updates)); + }); + logger(`Loaded: ${workspace}`); + + // only add to list as online workspace + signals.listAdd.emit({ + workspace, + provider: this.id, + // at this time we always download full workspace + // but after we support sub doc, we can only download metadata + locally: false, + }); + } + } + + static async auth( + config: Readonly>, + logger: Logger, + signals: DataCenterSignals + ) { const refreshToken = await config.get('token'); if (refreshToken) { await token.refreshToken(refreshToken); @@ -112,5 +162,14 @@ export class AffineProvider extends LocalProvider { logger(`login success: ${user.displayName}`); // TODO: refresh local workspace data + const workspaces = await apis.getWorkspaces(); + await Promise.all( + workspaces.map(async ({ id }) => { + const doc = new Doc(); + const idb = new IndexedDBProvider(id, doc); + await idb.whenSynced; + await this._initCloudDoc(id, doc, logger, signals); + }) + ); } } diff --git a/packages/data-center/src/provider/base.ts b/packages/data-center/src/provider/base.ts index 3874ecc48..bd8d8a4e0 100644 --- a/packages/data-center/src/provider/base.ts +++ b/packages/data-center/src/provider/base.ts @@ -61,8 +61,12 @@ export class BaseProvider { return this._workspace; } - static async auth(_config: Readonly, _logger: Logger) { - throw Error('Not implemented: auth'); + static async auth( + _config: Readonly, + logger: Logger, + _signals: DataCenterSignals + ) { + logger("This provider doesn't require authentication"); } // get workspace list,return a map of workspace id and boolean diff --git a/packages/data-center/src/provider/local/index.ts b/packages/data-center/src/provider/local/index.ts index 9e6a695a5..24bf9584a 100644 --- a/packages/data-center/src/provider/local/index.ts +++ b/packages/data-center/src/provider/local/index.ts @@ -1,7 +1,7 @@ import type { BlobStorage } from '@blocksuite/store'; import assert from 'assert'; -import type { ConfigStore, InitialParams, Logger } from '../index.js'; +import type { ConfigStore, InitialParams } from '../index.js'; import { BaseProvider } from '../base.js'; import { IndexedDBProvider } from './indexeddb.js'; @@ -21,7 +21,7 @@ export class LocalProvider extends BaseProvider { this._blobs = blobs; } - async initData() { + async initData(locally = true) { assert(this._workspace.room); this._logger('Loading local data'); this._idb = new IndexedDBProvider( @@ -35,7 +35,7 @@ export class LocalProvider extends BaseProvider { this._signals.listAdd.emit({ workspace: this._workspace.room, provider: this.id, - locally: true, + locally, }); } @@ -60,10 +60,6 @@ export class LocalProvider extends BaseProvider { return this._blobs.set(blob); } - static async auth(_config: Readonly, logger: Logger) { - logger("Local provider doesn't require authentication"); - } - static async list( config: Readonly> ): Promise | undefined> {