mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 12:28:42 +00:00
fix: some improvements to electron app (#2089)
This commit is contained in:
@@ -14,6 +14,10 @@ let provider: SQLiteProvider;
|
||||
|
||||
let offlineYdoc: YType.Doc;
|
||||
|
||||
let triggerDBUpdate: ((_: string) => void) | null = null;
|
||||
|
||||
const mockedAddBlob = vi.fn();
|
||||
|
||||
vi.stubGlobal('window', {
|
||||
apis: {
|
||||
db: {
|
||||
@@ -24,8 +28,16 @@ vi.stubGlobal('window', {
|
||||
Y.applyUpdate(offlineYdoc, update, 'sqlite');
|
||||
},
|
||||
getPersistedBlobs: async (id: string) => {
|
||||
// todo: may need to hack the way to get hash keys of blobs
|
||||
return [];
|
||||
},
|
||||
onDBUpdate: (fn: (id: string) => void) => {
|
||||
triggerDBUpdate = fn;
|
||||
return () => {
|
||||
triggerDBUpdate = null;
|
||||
};
|
||||
},
|
||||
addBlob: mockedAddBlob,
|
||||
} satisfies Partial<typeof window.apis.db>,
|
||||
},
|
||||
});
|
||||
@@ -43,7 +55,7 @@ beforeEach(() => {
|
||||
workspace.register(AffineSchemas).register(__unstableSchemas);
|
||||
provider = createSQLiteProvider(workspace);
|
||||
offlineYdoc = new Y.Doc();
|
||||
offlineYdoc.getText('text').insert(0, '');
|
||||
offlineYdoc.getText('text').insert(0, 'sqlite-hello');
|
||||
});
|
||||
|
||||
describe('SQLite provider', () => {
|
||||
@@ -53,19 +65,71 @@ describe('SQLite provider', () => {
|
||||
// Workspace.Y.applyUpdate(workspace.doc);
|
||||
workspace.doc.getText('text').insert(0, 'mem-hello');
|
||||
|
||||
expect(offlineYdoc.getText('text').toString()).toBe('');
|
||||
expect(offlineYdoc.getText('text').toString()).toBe('sqlite-hello');
|
||||
|
||||
await provider.connect();
|
||||
|
||||
expect(offlineYdoc.getText('text').toString()).toBe('mem-hello');
|
||||
expect(workspace.doc.getText('text').toString()).toBe('mem-hello');
|
||||
// depending on the nature of the sync, the data can be sync'ed in either direction
|
||||
const options = ['mem-hellosqlite-hello', 'sqlite-hellomem-hello'];
|
||||
const synced = options.filter(
|
||||
o => o === offlineYdoc.getText('text').toString()
|
||||
);
|
||||
expect(synced.length).toBe(1);
|
||||
expect(workspace.doc.getText('text').toString()).toBe(synced[0]);
|
||||
|
||||
workspace.doc.getText('text').insert(0, 'world');
|
||||
|
||||
// check if the data are sync'ed
|
||||
expect(offlineYdoc.getText('text').toString()).toBe('worldmem-hello');
|
||||
expect(offlineYdoc.getText('text').toString()).toBe('world' + synced[0]);
|
||||
});
|
||||
|
||||
// todo: test disconnect
|
||||
// todo: test blob sync
|
||||
test('blobs will be synced to sqlite on connect', async () => {
|
||||
// mock bs.list
|
||||
const bin = new Uint8Array([1, 2, 3]);
|
||||
const blob = new Blob([bin]);
|
||||
workspace.blobs.list = vi.fn(async () => ['blob1']);
|
||||
workspace.blobs.get = vi.fn(async (key: string) => {
|
||||
return blob;
|
||||
});
|
||||
|
||||
await provider.connect();
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
expect(mockedAddBlob).toBeCalledWith(id, 'blob1', bin);
|
||||
});
|
||||
|
||||
test('on db update', async () => {
|
||||
vi.useFakeTimers();
|
||||
await provider.connect();
|
||||
|
||||
offlineYdoc.getText('text').insert(0, 'sqlite-world');
|
||||
|
||||
triggerDBUpdate?.(id);
|
||||
|
||||
// not yet updated
|
||||
expect(workspace.doc.getText('text').toString()).toBe('sqlite-hello');
|
||||
|
||||
// wait for the update to be sync'ed
|
||||
await vi.advanceTimersByTimeAsync(1000);
|
||||
|
||||
expect(workspace.doc.getText('text').toString()).toBe(
|
||||
'sqlite-worldsqlite-hello'
|
||||
);
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
test('disconnect handlers', async () => {
|
||||
const offHandler = vi.fn();
|
||||
let handleUpdate = () => {};
|
||||
workspace.doc.on = (_: string, fn: () => void) => {
|
||||
handleUpdate = fn;
|
||||
};
|
||||
workspace.doc.off = offHandler;
|
||||
await provider.connect();
|
||||
|
||||
provider.disconnect();
|
||||
|
||||
expect(triggerDBUpdate).toBe(null);
|
||||
expect(offHandler).toBeCalledWith('update', handleUpdate);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -169,7 +169,7 @@ const createSQLiteProvider = (
|
||||
keysToPersist.forEach(async k => {
|
||||
const blob = await bs.get(k);
|
||||
if (!blob) {
|
||||
logger.warn('blob url not found', k);
|
||||
logger.warn('blob not found for', k);
|
||||
return;
|
||||
}
|
||||
window.apis.db.addBlob(
|
||||
@@ -180,6 +180,29 @@ const createSQLiteProvider = (
|
||||
});
|
||||
}
|
||||
|
||||
async function syncUpdates() {
|
||||
logger.info('syncing updates from sqlite', blockSuiteWorkspace.id);
|
||||
const updates = await window.apis.db.getDoc(blockSuiteWorkspace.id);
|
||||
|
||||
if (updates) {
|
||||
Y.applyUpdate(blockSuiteWorkspace.doc, updates, sqliteOrigin);
|
||||
}
|
||||
|
||||
const mergeUpdates = Y.encodeStateAsUpdate(blockSuiteWorkspace.doc);
|
||||
|
||||
// also apply updates to sqlite
|
||||
window.apis.db.applyDocUpdate(blockSuiteWorkspace.id, mergeUpdates);
|
||||
|
||||
const bs = blockSuiteWorkspace.blobs;
|
||||
|
||||
if (bs) {
|
||||
// this can be non-blocking
|
||||
syncBlobIntoSQLite(bs);
|
||||
}
|
||||
}
|
||||
|
||||
let unsubscribe = () => {};
|
||||
|
||||
const provider = {
|
||||
flavour: 'sqlite',
|
||||
background: true,
|
||||
@@ -188,31 +211,32 @@ const createSQLiteProvider = (
|
||||
},
|
||||
connect: async () => {
|
||||
logger.info('connecting sqlite provider', blockSuiteWorkspace.id);
|
||||
const updates = await window.apis.db.getDoc(blockSuiteWorkspace.id);
|
||||
|
||||
if (updates) {
|
||||
Y.applyUpdate(blockSuiteWorkspace.doc, updates, sqliteOrigin);
|
||||
}
|
||||
|
||||
const mergeUpdates = Y.encodeStateAsUpdate(blockSuiteWorkspace.doc);
|
||||
|
||||
// also apply updates to sqlite
|
||||
window.apis.db.applyDocUpdate(blockSuiteWorkspace.id, mergeUpdates);
|
||||
await syncUpdates();
|
||||
|
||||
blockSuiteWorkspace.doc.on('update', handleUpdate);
|
||||
|
||||
const bs = blockSuiteWorkspace.blobs;
|
||||
|
||||
if (bs) {
|
||||
// this can be non-blocking
|
||||
syncBlobIntoSQLite(bs);
|
||||
}
|
||||
let timer = 0;
|
||||
unsubscribe = window.apis.db.onDBUpdate(workspaceId => {
|
||||
if (workspaceId === blockSuiteWorkspace.id) {
|
||||
// throttle
|
||||
logger.debug('on db update', workspaceId);
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
// @ts-expect-error ignore the type
|
||||
timer = setTimeout(() => {
|
||||
syncUpdates();
|
||||
timer = 0;
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
// blockSuiteWorkspace.doc.on('destroy', ...);
|
||||
logger.info('connecting sqlite done', blockSuiteWorkspace.id);
|
||||
},
|
||||
disconnect: () => {
|
||||
// todo: not implemented
|
||||
unsubscribe();
|
||||
blockSuiteWorkspace.doc.off('update', handleUpdate);
|
||||
},
|
||||
} satisfies SQLiteProvider;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user