mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-14 21:27:20 +00:00
feat: init data from cloud
This commit is contained in:
@@ -37,7 +37,6 @@ export class DataCenter {
|
||||
readonly signals = {
|
||||
listAdd: new Signal<WorkspaceLoadEvent>(),
|
||||
listRemove: new Signal<string>(),
|
||||
workspaceLoaded: new Signal<WorkspaceLoadEvent>(),
|
||||
};
|
||||
|
||||
static async init(debug: boolean): Promise<DataCenter> {
|
||||
@@ -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<Apis> {
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<void>((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<void>((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<ConfigStore<string>>, 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<ConfigStore<string>>,
|
||||
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);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,8 +61,12 @@ export class BaseProvider {
|
||||
return this._workspace;
|
||||
}
|
||||
|
||||
static async auth(_config: Readonly<ConfigStore>, _logger: Logger) {
|
||||
throw Error('Not implemented: auth');
|
||||
static async auth(
|
||||
_config: Readonly<ConfigStore>,
|
||||
logger: Logger,
|
||||
_signals: DataCenterSignals
|
||||
) {
|
||||
logger("This provider doesn't require authentication");
|
||||
}
|
||||
|
||||
// get workspace list,return a map of workspace id and boolean
|
||||
|
||||
@@ -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<ConfigStore>, logger: Logger) {
|
||||
logger("Local provider doesn't require authentication");
|
||||
}
|
||||
|
||||
static async list(
|
||||
config: Readonly<ConfigStore<boolean>>
|
||||
): Promise<Map<string, boolean> | undefined> {
|
||||
|
||||
Reference in New Issue
Block a user