mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 04:18:54 +00:00
feat: support force sync by click (#4089)
Co-authored-by: JimmFly <yangjinfei001@gmail.com>
This commit is contained in:
10
packages/env/src/workspace.ts
vendored
10
packages/env/src/workspace.ts
vendored
@@ -1,4 +1,4 @@
|
||||
import type { StatusAdapter } from '@affine/y-provider';
|
||||
import type { DataSourceAdapter } from '@affine/y-provider';
|
||||
import type { EditorContainer } from '@blocksuite/editor';
|
||||
import type { Page } from '@blocksuite/store';
|
||||
import type {
|
||||
@@ -32,7 +32,7 @@ export interface BroadCastChannelProvider extends PassiveDocProvider {
|
||||
* Long polling provider with local IndexedDB
|
||||
*/
|
||||
export interface LocalIndexedDBBackgroundProvider
|
||||
extends StatusAdapter,
|
||||
extends DataSourceAdapter,
|
||||
PassiveDocProvider {
|
||||
flavour: 'local-indexeddb-background';
|
||||
}
|
||||
@@ -41,7 +41,7 @@ export interface LocalIndexedDBDownloadProvider extends ActiveDocProvider {
|
||||
flavour: 'local-indexeddb';
|
||||
}
|
||||
|
||||
export interface SQLiteProvider extends PassiveDocProvider, StatusAdapter {
|
||||
export interface SQLiteProvider extends PassiveDocProvider, DataSourceAdapter {
|
||||
flavour: 'sqlite';
|
||||
}
|
||||
|
||||
@@ -49,7 +49,9 @@ export interface SQLiteDBDownloadProvider extends ActiveDocProvider {
|
||||
flavour: 'sqlite-download';
|
||||
}
|
||||
|
||||
export interface AffineSocketIOProvider extends PassiveDocProvider {
|
||||
export interface AffineSocketIOProvider
|
||||
extends PassiveDocProvider,
|
||||
DataSourceAdapter {
|
||||
flavour: 'affine-socket-io';
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Status, StatusAdapter } from '@affine/y-provider';
|
||||
import type { DataSourceAdapter, Status } from '@affine/y-provider';
|
||||
import { useCallback, useSyncExternalStore } from 'react';
|
||||
|
||||
type UIStatus =
|
||||
@@ -7,9 +7,9 @@ type UIStatus =
|
||||
type: 'unknown';
|
||||
};
|
||||
|
||||
export function useDataSourceStatus(datasource: StatusAdapter): UIStatus {
|
||||
export function useDataSourceStatus(provider: DataSourceAdapter): UIStatus {
|
||||
return useSyncExternalStore(
|
||||
datasource.subscribeStatusChange,
|
||||
useCallback(() => datasource.status, [datasource])
|
||||
provider.subscribeStatusChange,
|
||||
useCallback(() => provider.status, [provider])
|
||||
);
|
||||
}
|
||||
|
||||
@@ -35,9 +35,15 @@ const createAffineSocketIOProvider: DocProviderCreator = (
|
||||
{ awareness }
|
||||
): AffineSocketIOProvider => {
|
||||
const dataSource = createAffineDataSource(id, doc, awareness);
|
||||
const lazyProvider = createLazyProvider(doc, dataSource, {
|
||||
origin: 'affine-socket-io',
|
||||
});
|
||||
return {
|
||||
flavour: 'affine-socket-io',
|
||||
...createLazyProvider(doc, dataSource),
|
||||
...lazyProvider,
|
||||
get status() {
|
||||
return lazyProvider.status;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -50,6 +56,7 @@ const createIndexedDBBackgroundProvider: DocProviderCreator = (
|
||||
let connected = false;
|
||||
return {
|
||||
flavour: 'local-indexeddb-background',
|
||||
datasource: indexeddbProvider.datasource,
|
||||
passive: true,
|
||||
get status() {
|
||||
return indexeddbProvider.status;
|
||||
|
||||
@@ -54,11 +54,12 @@ export const createSQLiteProvider: DocProviderCreator = (
|
||||
id,
|
||||
rootDoc
|
||||
): SQLiteProvider => {
|
||||
let datasource: ReturnType<typeof createDatasource> | null = null;
|
||||
const datasource = createDatasource(id);
|
||||
let provider: ReturnType<typeof createLazyProvider> | null = null;
|
||||
let connected = false;
|
||||
return {
|
||||
flavour: 'sqlite',
|
||||
datasource,
|
||||
passive: true,
|
||||
get status() {
|
||||
assertExists(provider);
|
||||
@@ -69,14 +70,12 @@ export const createSQLiteProvider: DocProviderCreator = (
|
||||
return provider.subscribeStatusChange(onStatusChange);
|
||||
},
|
||||
connect: () => {
|
||||
datasource = createDatasource(id);
|
||||
provider = createLazyProvider(rootDoc, datasource, { origin: 'sqlite' });
|
||||
provider.connect();
|
||||
connected = true;
|
||||
},
|
||||
disconnect: () => {
|
||||
provider?.disconnect();
|
||||
datasource = null;
|
||||
provider = null;
|
||||
connected = false;
|
||||
},
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
writeOperation,
|
||||
} from '@affine/y-provider';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
import type { IDBPDatabase } from 'idb';
|
||||
import { openDB } from 'idb';
|
||||
import type { Doc } from 'yjs';
|
||||
import { diffUpdate, encodeStateVectorFromUpdate } from 'yjs';
|
||||
@@ -31,13 +32,20 @@ export const createIndexedDBDatasource = ({
|
||||
dbName: string;
|
||||
mergeCount?: number;
|
||||
}) => {
|
||||
const dbPromise = openDB<BlockSuiteBinaryDB>(dbName, dbVersion, {
|
||||
upgrade: upgradeDB,
|
||||
});
|
||||
let dbPromise: Promise<IDBPDatabase<BlockSuiteBinaryDB>> | null = null;
|
||||
const getDb = async () => {
|
||||
if (dbPromise === null) {
|
||||
dbPromise = openDB<BlockSuiteBinaryDB>(dbName, dbVersion, {
|
||||
upgrade: upgradeDB,
|
||||
});
|
||||
}
|
||||
return dbPromise;
|
||||
};
|
||||
|
||||
const adapter = {
|
||||
queryDocState: async (guid, options) => {
|
||||
try {
|
||||
const db = await dbPromise;
|
||||
const db = await getDb();
|
||||
const store = db
|
||||
.transaction('workspace', 'readonly')
|
||||
.objectStore('workspace');
|
||||
@@ -64,7 +72,7 @@ export const createIndexedDBDatasource = ({
|
||||
},
|
||||
sendDocUpdate: async (guid, update) => {
|
||||
try {
|
||||
const db = await dbPromise;
|
||||
const db = await getDb();
|
||||
const store = db
|
||||
.transaction('workspace', 'readwrite')
|
||||
.objectStore('workspace');
|
||||
@@ -96,10 +104,15 @@ export const createIndexedDBDatasource = ({
|
||||
return {
|
||||
...adapter,
|
||||
disconnect: () => {
|
||||
dbPromise.then(db => db.close()).catch(console.error);
|
||||
getDb()
|
||||
.then(db => db.close())
|
||||
.then(() => {
|
||||
dbPromise = null;
|
||||
})
|
||||
.catch(console.error);
|
||||
},
|
||||
cleanup: async () => {
|
||||
const db = await dbPromise;
|
||||
const db = await getDb();
|
||||
await db.clear('workspace');
|
||||
},
|
||||
};
|
||||
@@ -112,7 +125,7 @@ export const createIndexedDBProvider = (
|
||||
doc: Doc,
|
||||
dbName: string = DEFAULT_DB_NAME
|
||||
): IndexedDBProvider => {
|
||||
let datasource: ReturnType<typeof createIndexedDBDatasource> | null = null;
|
||||
const datasource = createIndexedDBDatasource({ dbName, mergeCount });
|
||||
let provider: ReturnType<typeof createLazyProvider> | null = null;
|
||||
|
||||
const apis = {
|
||||
@@ -128,14 +141,12 @@ export const createIndexedDBProvider = (
|
||||
if (apis.connected) {
|
||||
apis.disconnect();
|
||||
}
|
||||
datasource = createIndexedDBDatasource({ dbName, mergeCount });
|
||||
provider = createLazyProvider(doc, datasource, { origin: 'idb' });
|
||||
provider.connect();
|
||||
},
|
||||
disconnect: () => {
|
||||
datasource?.disconnect();
|
||||
provider?.disconnect();
|
||||
datasource = null;
|
||||
provider = null;
|
||||
},
|
||||
cleanup: async () => {
|
||||
@@ -144,6 +155,7 @@ export const createIndexedDBProvider = (
|
||||
get connected() {
|
||||
return provider?.connected || false;
|
||||
},
|
||||
datasource,
|
||||
} satisfies IndexedDBProvider;
|
||||
|
||||
return apis;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { StatusAdapter } from '@affine/y-provider';
|
||||
import type { DataSourceAdapter } from '@affine/y-provider';
|
||||
import type { DBSchema, IDBPDatabase } from 'idb';
|
||||
|
||||
export const dbVersion = 1;
|
||||
@@ -9,7 +9,7 @@ export function upgradeDB(db: IDBPDatabase<BlockSuiteBinaryDB>) {
|
||||
db.createObjectStore('milestone', { keyPath: 'id' });
|
||||
}
|
||||
|
||||
export interface IndexedDBProvider extends StatusAdapter {
|
||||
export interface IndexedDBProvider extends DataSourceAdapter {
|
||||
connect: () => void;
|
||||
disconnect: () => void;
|
||||
cleanup: () => Promise<void>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { DocState, StatusAdapter } from './types';
|
||||
import type { DocState } from './types';
|
||||
|
||||
export interface DatasourceDocAdapter extends Partial<StatusAdapter> {
|
||||
export interface DatasourceDocAdapter {
|
||||
/**
|
||||
* request diff update from other clients
|
||||
*/
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from 'yjs';
|
||||
|
||||
import type { DatasourceDocAdapter } from './data-source';
|
||||
import type { StatusAdapter } from './types';
|
||||
import type { DataSourceAdapter } from './types';
|
||||
import type { Status } from './types';
|
||||
|
||||
function getDoc(doc: Doc, guid: string): Doc | undefined {
|
||||
@@ -45,7 +45,7 @@ export const createLazyProvider = (
|
||||
rootDoc: Doc,
|
||||
datasource: DatasourceDocAdapter,
|
||||
options: LazyProviderOptions = {}
|
||||
): DocProvider & StatusAdapter => {
|
||||
): DocProvider & DataSourceAdapter => {
|
||||
let connected = false;
|
||||
const pendingMap = new Map<string, Uint8Array[]>(); // guid -> pending-updates
|
||||
const disposableMap = new Map<string, Set<() => void>>();
|
||||
@@ -62,21 +62,17 @@ export const createLazyProvider = (
|
||||
const callbackSet = new Set<() => void>();
|
||||
const changeStatus = (newStatus: Status) => {
|
||||
// simulate a stack, each syncing and synced should be paired
|
||||
if (newStatus.type === 'idle') {
|
||||
if (connected && syncingStack !== 0) {
|
||||
console.error('syncingStatus !== 0, this should not happen');
|
||||
}
|
||||
syncingStack = 0;
|
||||
}
|
||||
if (newStatus.type === 'syncing') {
|
||||
syncingStack++;
|
||||
}
|
||||
if (newStatus.type === 'synced' || newStatus.type === 'error') {
|
||||
} else if (newStatus.type === 'synced' || newStatus.type === 'error') {
|
||||
syncingStack--;
|
||||
}
|
||||
|
||||
if (syncingStack < 0) {
|
||||
console.error('syncingStatus < 0, this should not happen');
|
||||
console.error(
|
||||
'syncingStatus < 0, this should not happen',
|
||||
options.origin
|
||||
);
|
||||
}
|
||||
|
||||
if (syncingStack === 0) {
|
||||
@@ -85,6 +81,17 @@ export const createLazyProvider = (
|
||||
if (newStatus.type !== 'synced') {
|
||||
currentStatus = newStatus;
|
||||
}
|
||||
if (syncingStack === 0) {
|
||||
if (!connected) {
|
||||
currentStatus = {
|
||||
type: 'idle',
|
||||
};
|
||||
} else {
|
||||
currentStatus = {
|
||||
type: 'synced',
|
||||
};
|
||||
}
|
||||
}
|
||||
callbackSet.forEach(cb => cb());
|
||||
};
|
||||
|
||||
@@ -102,12 +109,6 @@ export const createLazyProvider = (
|
||||
stateVector: encodeStateVector(doc),
|
||||
})
|
||||
.then(remoteUpdate => {
|
||||
if (!connected) {
|
||||
changeStatus({
|
||||
type: 'idle',
|
||||
});
|
||||
return;
|
||||
}
|
||||
changeStatus({
|
||||
type: 'synced',
|
||||
});
|
||||
@@ -201,9 +202,6 @@ export const createLazyProvider = (
|
||||
function setupDatasourceListeners() {
|
||||
assertExists(abortController, 'abortController should be defined');
|
||||
const unsubscribe = datasource.onDocUpdate?.((guid, update) => {
|
||||
if (!connected) {
|
||||
return;
|
||||
}
|
||||
changeStatus({
|
||||
type: 'syncing',
|
||||
});
|
||||
@@ -283,12 +281,6 @@ export const createLazyProvider = (
|
||||
// but we want to populate the cache for later update events
|
||||
connectDoc(rootDoc)
|
||||
.then(() => {
|
||||
if (!connected) {
|
||||
changeStatus({
|
||||
type: 'idle',
|
||||
});
|
||||
return;
|
||||
}
|
||||
changeStatus({
|
||||
type: 'synced',
|
||||
});
|
||||
@@ -305,9 +297,6 @@ export const createLazyProvider = (
|
||||
|
||||
async function disconnect() {
|
||||
connected = false;
|
||||
changeStatus({
|
||||
type: 'idle',
|
||||
});
|
||||
disposeAll();
|
||||
assertExists(abortController, 'abortController should be defined');
|
||||
abortController.abort();
|
||||
@@ -349,5 +338,7 @@ export const createLazyProvider = (
|
||||
passive: true,
|
||||
connect,
|
||||
disconnect,
|
||||
|
||||
datasource,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { DatasourceDocAdapter } from './data-source';
|
||||
|
||||
export type Status =
|
||||
| {
|
||||
type: 'idle';
|
||||
@@ -10,11 +12,13 @@ export type Status =
|
||||
}
|
||||
| {
|
||||
type: 'error';
|
||||
error: Error;
|
||||
error: unknown;
|
||||
};
|
||||
|
||||
export interface StatusAdapter {
|
||||
export interface DataSourceAdapter {
|
||||
datasource: DatasourceDocAdapter;
|
||||
readonly status: Status;
|
||||
|
||||
subscribeStatusChange(onStatusChange: () => void): () => void;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user