+
Share
void;
+
type YjsProviders = {
awareness: Awareness;
- idb: IndexedDBProvider;
- binariesIdb: IndexedDBProvider;
+ binaries: Doc;
+ doc: Doc;
gatekeeper: GateKeeper;
connListener: { listeners?: ConnectivityListener };
userId: string;
remoteToken?: string; // remote storage token
};
+
const _yjsDatabaseInstance = new Map();
const _asyncInitLoading = new Set();
@@ -90,13 +90,9 @@ async function _initYjsDatabase(
const { userId, token } = options;
const doc = new Doc({ autoLoad: true, shouldLoad: true });
- const idb = await new IndexedDBProvider(workspace, doc).whenSynced;
+ // const idb = await new IndexedDBProvider(workspace, doc).whenSynced;
const binaries = new Doc({ autoLoad: true, shouldLoad: true });
- const binariesIdb = await new IndexedDBProvider(
- `${workspace}_binaries`,
- binaries
- ).whenSynced;
const awareness = new Awareness(doc);
@@ -114,16 +110,24 @@ async function _initYjsDatabase(
const emitState = (c: Connectivity) =>
connListener.listeners?.(workspace, c);
await Promise.all(
- Object.entries(options.provider).map(async ([, p]) =>
- p({ awareness, doc, token, workspace, emitState })
- )
+ Object.entries(options.provider).flatMap(async ([, p]) => [
+ p({ awareness, doc, token, workspace, emitState }),
+ p({
+ awareness,
+ doc: binaries,
+ token,
+ workspace: `${workspace}_binaries`,
+ emitState,
+ }),
+ ])
);
}
const newInstance = {
awareness,
- idb,
- binariesIdb,
+ binaries,
+ doc,
gatekeeper,
+
connListener,
userId,
remoteToken: token,
@@ -183,7 +187,7 @@ export class YjsAdapter implements AsyncDatabaseAdapter {
private constructor(providers: YjsProviders) {
this._provider = providers;
- this._doc = providers.idb.doc;
+ this._doc = providers.doc;
this._awareness = providers.awareness;
this._gatekeeper = providers.gatekeeper;
this._reload = () => {
@@ -201,7 +205,7 @@ export class YjsAdapter implements AsyncDatabaseAdapter {
});
// @ts-ignore
this._binaries = new YjsRemoteBinaries(
- providers.binariesIdb.doc.getMap(),
+ providers.binaries.getMap(),
providers.remoteToken
);
// @ts-ignore
@@ -336,7 +340,7 @@ export class YjsAdapter implements AsyncDatabaseAdapter {
});
const [file] = (await fromEvent(handles)) as File[];
const binary = await file.arrayBuffer();
- await this._provider.idb.clearData();
+ // await this._provider.idb.clearData();
const doc = new Doc({ autoLoad: true, shouldLoad: true });
let updated = 0;
let isUpdated = false;
@@ -357,8 +361,8 @@ export class YjsAdapter implements AsyncDatabaseAdapter {
};
check();
});
- await new IndexedDBProvider(this._provider.idb.name, doc)
- .whenSynced;
+ // await new IndexedDBProvider(this._provider.idb.name, doc)
+ // .whenSynced;
applyUpdate(doc, new Uint8Array(binary));
await update_check;
console.log('load success');
diff --git a/libs/datasource/jwt/src/adapter/yjs/provider.ts b/libs/datasource/jwt/src/adapter/yjs/provider.ts
index f7068288fd..e4cebd1f01 100644
--- a/libs/datasource/jwt/src/adapter/yjs/provider.ts
+++ b/libs/datasource/jwt/src/adapter/yjs/provider.ts
@@ -2,6 +2,7 @@ import { Doc } from 'yjs';
import { Awareness } from 'y-protocols/awareness.js';
import {
+ IndexedDBProvider,
SQLiteProvider,
WebsocketProvider,
} from '@toeverything/datasource/jwt-rpc';
@@ -19,7 +20,10 @@ type YjsDefaultInstances = {
export type YjsProvider = (instances: YjsDefaultInstances) => Promise;
+type ProviderType = 'idb' | 'sqlite' | 'ws';
+
export type YjsProviderOptions = {
+ enabled: ProviderType[];
backend: typeof BucketBackend[keyof typeof BucketBackend];
params?: Record;
importData?: () => Promise | Uint8Array | undefined;
@@ -30,53 +34,64 @@ export type YjsProviderOptions = {
export const getYjsProviders = (
options: YjsProviderOptions
): Record => {
+ console.log('getYjsProviders', options);
return {
+ indexeddb: async (instances: YjsDefaultInstances) => {
+ if (options.enabled.includes('idb')) {
+ await new IndexedDBProvider(instances.workspace, instances.doc)
+ .whenSynced;
+ }
+ },
sqlite: async (instances: YjsDefaultInstances) => {
- const fsHandle = setInterval(async () => {
- if (options.hasExporter?.()) {
- clearInterval(fsHandle);
- const fs = new SQLiteProvider(
- instances.workspace,
- instances.doc,
- await options.importData?.()
- );
- if (options.exportData) {
- fs.registerExporter(options.exportData);
+ if (options.enabled.includes('sqlite')) {
+ const fsHandle = setInterval(async () => {
+ if (options.hasExporter?.()) {
+ clearInterval(fsHandle);
+ const fs = new SQLiteProvider(
+ instances.workspace,
+ instances.doc,
+ await options.importData?.()
+ );
+ if (options.exportData) {
+ fs.registerExporter(options.exportData);
+ }
+ await fs.whenSynced;
}
- await fs.whenSynced;
- }
- }, 500);
+ }, 500);
+ }
},
ws: async (instances: YjsDefaultInstances) => {
- if (instances.token) {
- const ws = new WebsocketProvider(
- instances.token,
- options.backend,
- instances.workspace,
- instances.doc,
- {
- awareness: instances.awareness,
- params: options.params,
- }
- ) as any; // TODO: type is erased after cascading references
+ if (options.enabled.includes('ws')) {
+ if (instances.token) {
+ const ws = new WebsocketProvider(
+ instances.token,
+ options.backend,
+ instances.workspace,
+ instances.doc,
+ {
+ awareness: instances.awareness,
+ params: options.params,
+ }
+ ) as any; // TODO: type is erased after cascading references
- // Wait for ws synchronization to complete, otherwise the data will be modified in reverse, which can be optimized later
- return 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
- ws.once('synced', () => resolve());
- ws.once('lost-connection', () => resolve());
- ws.once('connection-error', () => reject());
- ws.on('synced', () => instances.emitState('connected'));
- ws.on('lost-connection', () =>
- instances.emitState('retry')
- );
- ws.on('connection-error', () =>
- instances.emitState('retry')
- );
- });
- } else {
- return;
+ // Wait for ws synchronization to complete, otherwise the data will be modified in reverse, which can be optimized later
+ return 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
+ ws.once('synced', () => resolve());
+ ws.once('lost-connection', () => resolve());
+ ws.once('connection-error', () => reject());
+ ws.on('synced', () => instances.emitState('connected'));
+ ws.on('lost-connection', () =>
+ instances.emitState('retry')
+ );
+ ws.on('connection-error', () =>
+ instances.emitState('retry')
+ );
+ });
+ } else {
+ return;
+ }
}
},
};
diff --git a/libs/datasource/jwt/src/index.ts b/libs/datasource/jwt/src/index.ts
index ecad7aebf9..38bc11396e 100644
--- a/libs/datasource/jwt/src/index.ts
+++ b/libs/datasource/jwt/src/index.ts
@@ -618,6 +618,7 @@ export class BlockClient<
const instance = await YjsAdapter.init(workspace, {
provider: getYjsProviders({
+ enabled: [],
backend: BucketBackend.YjsWebSocketAffine,
importData,
exportData,
diff --git a/libs/datasource/state/src/user.ts b/libs/datasource/state/src/user.ts
index fefb689f0a..d32fef2c56 100644
--- a/libs/datasource/state/src/user.ts
+++ b/libs/datasource/state/src/user.ts
@@ -1,3 +1,4 @@
+import { useNavigate } from 'react-router';
import {
getAuth,
onAuthStateChanged,
@@ -62,11 +63,17 @@ const BRAND_ID = 'AFFiNE';
const _localTrigger = atom(false);
const _useUserAndSpacesForFreeLogin = () => {
+ const navigate = useNavigate();
const [user, setUser] = useAtom(_userAtom);
const [loading, setLoading] = useAtom(_loadingAtom);
const [localTrigger] = useAtom(_localTrigger);
- useEffect(() => setLoading(false), []);
+ useEffect(() => {
+ if (loading) {
+ navigate('/demo');
+ setLoading(false);
+ }
+ }, []);
useEffect(() => {
if (localTrigger) {
@@ -77,6 +84,14 @@ const _useUserAndSpacesForFreeLogin = () => {
nickname: BRAND_ID,
email: '',
});
+ } else {
+ setUser({
+ photo: '',
+ id: 'demo',
+ username: 'demo',
+ nickname: 'demo',
+ email: '',
+ });
}
}, [localTrigger, setLoading, setUser]);
@@ -86,8 +101,8 @@ const _useUserAndSpacesForFreeLogin = () => {
};
export const useLocalTrigger = () => {
- const [, setTrigger] = useAtom(_localTrigger);
- return () => setTrigger(true);
+ const [trigger, setTrigger] = useAtom(_localTrigger);
+ return [trigger, () => setTrigger(true)] as [boolean, () => void];
};
export const useUserAndSpaces = process.env['NX_LOCAL']
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index dacfe065aa..a632fb99bd 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -194,7 +194,7 @@ importers:
yjs: ^13.5.41
dependencies:
authing-js-sdk: 4.23.35
- firebase-admin: 11.0.1_@firebase+app-types@0.7.0
+ firebase-admin: 11.0.1
lib0: 0.2.52
lru-cache: 7.13.2
nanoid: 4.0.0
@@ -571,6 +571,9 @@ importers:
dependencies:
ffc-js-client-side-sdk: 1.1.5
+ libs/datasource/jwst/pkg:
+ specifiers: {}
+
libs/datasource/jwt:
specifiers:
'@types/debug': ^4.1.7
@@ -3291,6 +3294,15 @@ packages:
- utf-8-validate
dev: true
+ /@firebase/auth-interop-types/0.1.6_@firebase+util@1.6.3:
+ resolution: {integrity: sha512-etIi92fW3CctsmR9e3sYM3Uqnoq861M0Id9mdOPF6PWIg38BXL5k4upCNBggGUpLIS0H1grMOvy/wn1xymwe2g==}
+ peerDependencies:
+ '@firebase/app-types': 0.x
+ '@firebase/util': 1.x
+ dependencies:
+ '@firebase/util': 1.6.3
+ dev: false
+
/@firebase/auth-interop-types/0.1.6_pbfwexsq7uf6mrzcwnikj3g37m:
resolution: {integrity: sha512-etIi92fW3CctsmR9e3sYM3Uqnoq861M0Id9mdOPF6PWIg38BXL5k4upCNBggGUpLIS0H1grMOvy/wn1xymwe2g==}
peerDependencies:
@@ -3299,6 +3311,7 @@ packages:
dependencies:
'@firebase/app-types': 0.7.0
'@firebase/util': 1.6.3
+ dev: true
/@firebase/auth-types/0.11.0_pbfwexsq7uf6mrzcwnikj3g37m:
resolution: {integrity: sha512-q7Bt6cx+ySj9elQHTsKulwk3+qDezhzRBFC9zlQ1BjgMueUOnGMcvqmU0zuKlQ4RhLSH7MNAdBV2znVaoN3Vxw==}
@@ -3334,6 +3347,19 @@ packages:
'@firebase/util': 1.6.3
tslib: 2.4.0
+ /@firebase/database-compat/0.2.4:
+ resolution: {integrity: sha512-VtsGixO5mTjNMJn6PwxAJEAR70fj+3blCXIdQKel3q+eYGZAfdqxox1+tzZDnf9NWBJpaOgAHPk3JVDxEo9NFQ==}
+ dependencies:
+ '@firebase/component': 0.5.17
+ '@firebase/database': 0.13.4
+ '@firebase/database-types': 0.9.12
+ '@firebase/logger': 0.3.3
+ '@firebase/util': 1.6.3
+ tslib: 2.4.0
+ transitivePeerDependencies:
+ - '@firebase/app-types'
+ dev: false
+
/@firebase/database-compat/0.2.4_@firebase+app-types@0.7.0:
resolution: {integrity: sha512-VtsGixO5mTjNMJn6PwxAJEAR70fj+3blCXIdQKel3q+eYGZAfdqxox1+tzZDnf9NWBJpaOgAHPk3JVDxEo9NFQ==}
dependencies:
@@ -3345,6 +3371,7 @@ packages:
tslib: 2.4.0
transitivePeerDependencies:
- '@firebase/app-types'
+ dev: true
/@firebase/database-types/0.9.10:
resolution: {integrity: sha512-2ji6nXRRsY+7hgU6zRhUtK0RmSjVWM71taI7Flgaw+BnopCo/lDF5HSwxp8z7LtiHlvQqeRA3Ozqx5VhlAbiKg==}
@@ -3359,6 +3386,19 @@ packages:
'@firebase/app-types': 0.7.0
'@firebase/util': 1.6.3
+ /@firebase/database/0.13.4:
+ resolution: {integrity: sha512-NW7bOoiaC4sJCj6DY/m9xHoFNa0CK32YPMCh6FiMweLCDQbOZM8Ql/Kn6yyuxCb7K7ypz9eSbRlCWQJsJRQjhg==}
+ dependencies:
+ '@firebase/auth-interop-types': 0.1.6_@firebase+util@1.6.3
+ '@firebase/component': 0.5.17
+ '@firebase/logger': 0.3.3
+ '@firebase/util': 1.6.3
+ faye-websocket: 0.11.4
+ tslib: 2.4.0
+ transitivePeerDependencies:
+ - '@firebase/app-types'
+ dev: false
+
/@firebase/database/0.13.4_@firebase+app-types@0.7.0:
resolution: {integrity: sha512-NW7bOoiaC4sJCj6DY/m9xHoFNa0CK32YPMCh6FiMweLCDQbOZM8Ql/Kn6yyuxCb7K7ypz9eSbRlCWQJsJRQjhg==}
dependencies:
@@ -3370,6 +3410,7 @@ packages:
tslib: 2.4.0
transitivePeerDependencies:
- '@firebase/app-types'
+ dev: true
/@firebase/firestore-compat/0.1.23_53yvy43rwpg2c45kgeszsxtrca:
resolution: {integrity: sha512-QfcuyMAavp//fQnjSfCEpnbWi7spIdKaXys1kOLu7395fLr+U6ykmto1HUMCSz8Yus9cEr/03Ujdi2SUl2GUAA==}
@@ -10863,12 +10904,12 @@ packages:
semver-regex: 2.0.0
dev: true
- /firebase-admin/11.0.1_@firebase+app-types@0.7.0:
+ /firebase-admin/11.0.1:
resolution: {integrity: sha512-rL3wlZbi2Kb/KJgcmj1YHlD4ZhfmhfgRO2YJialxAllm0tj1IQea878hHuBLGmv4DpbW9t9nLvX9kddNR2Y65Q==}
engines: {node: '>=14'}
dependencies:
'@fastify/busboy': 1.1.0
- '@firebase/database-compat': 0.2.4_@firebase+app-types@0.7.0
+ '@firebase/database-compat': 0.2.4
'@firebase/database-types': 0.9.10
'@types/node': 18.0.1
jsonwebtoken: 8.5.1