mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-14 13:25:12 +00:00
135 lines
3.3 KiB
TypeScript
135 lines
3.3 KiB
TypeScript
import { openDB } from 'idb';
|
|
import {
|
|
applyUpdate,
|
|
Doc,
|
|
encodeStateAsUpdate,
|
|
encodeStateVector,
|
|
UndoManager,
|
|
} from 'yjs';
|
|
|
|
import type { BlockSuiteBinaryDB, WorkspaceMilestone } from './shared';
|
|
import { dbVersion, DEFAULT_DB_NAME, upgradeDB } from './shared';
|
|
|
|
const snapshotOrigin = 'snapshot-origin';
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
const saveAlert = (event: BeforeUnloadEvent) => {
|
|
event.preventDefault();
|
|
return (event.returnValue =
|
|
'Data is not saved. Are you sure you want to leave?');
|
|
};
|
|
|
|
export const writeOperation = async (op: Promise<unknown>) => {
|
|
window.addEventListener('beforeunload', saveAlert, {
|
|
capture: true,
|
|
});
|
|
await op;
|
|
window.removeEventListener('beforeunload', saveAlert, {
|
|
capture: true,
|
|
});
|
|
};
|
|
|
|
export function revertUpdate(
|
|
doc: Doc,
|
|
snapshotUpdate: Uint8Array,
|
|
getMetadata: (key: string) => 'Text' | 'Map' | 'Array'
|
|
) {
|
|
const snapshotDoc = new Doc();
|
|
applyUpdate(snapshotDoc, snapshotUpdate, snapshotOrigin);
|
|
|
|
const currentStateVector = encodeStateVector(doc);
|
|
const snapshotStateVector = encodeStateVector(snapshotDoc);
|
|
|
|
const changesSinceSnapshotUpdate = encodeStateAsUpdate(
|
|
doc,
|
|
snapshotStateVector
|
|
);
|
|
const undoManager = new UndoManager(
|
|
[...snapshotDoc.share.keys()].map(key => {
|
|
const type = getMetadata(key);
|
|
if (type === 'Text') {
|
|
return snapshotDoc.getText(key);
|
|
} else if (type === 'Map') {
|
|
return snapshotDoc.getMap(key);
|
|
} else if (type === 'Array') {
|
|
return snapshotDoc.getArray(key);
|
|
}
|
|
throw new Error('Unknown type');
|
|
}),
|
|
{
|
|
trackedOrigins: new Set([snapshotOrigin]),
|
|
}
|
|
);
|
|
applyUpdate(snapshotDoc, changesSinceSnapshotUpdate, snapshotOrigin);
|
|
undoManager.undo();
|
|
const revertChangesSinceSnapshotUpdate = encodeStateAsUpdate(
|
|
snapshotDoc,
|
|
currentStateVector
|
|
);
|
|
applyUpdate(doc, revertChangesSinceSnapshotUpdate, snapshotOrigin);
|
|
}
|
|
|
|
export class EarlyDisconnectError extends Error {
|
|
constructor() {
|
|
super('Early disconnect');
|
|
}
|
|
}
|
|
|
|
export class CleanupWhenConnectingError extends Error {
|
|
constructor() {
|
|
super('Cleanup when connecting');
|
|
}
|
|
}
|
|
|
|
export const markMilestone = async (
|
|
id: string,
|
|
doc: Doc,
|
|
name: string,
|
|
dbName = DEFAULT_DB_NAME
|
|
): Promise<void> => {
|
|
const dbPromise = openDB<BlockSuiteBinaryDB>(dbName, dbVersion, {
|
|
upgrade: upgradeDB,
|
|
});
|
|
const db = await dbPromise;
|
|
const store = db
|
|
.transaction('milestone', 'readwrite')
|
|
.objectStore('milestone');
|
|
const milestone = await store.get('id');
|
|
const binary = encodeStateAsUpdate(doc);
|
|
if (!milestone) {
|
|
await store.put({
|
|
id,
|
|
milestone: {
|
|
[name]: binary,
|
|
},
|
|
});
|
|
} else {
|
|
milestone.milestone[name] = binary;
|
|
await store.put(milestone);
|
|
}
|
|
};
|
|
|
|
export const getMilestones = async (
|
|
id: string,
|
|
dbName: string = DEFAULT_DB_NAME
|
|
): Promise<null | WorkspaceMilestone['milestone']> => {
|
|
const dbPromise = openDB<BlockSuiteBinaryDB>(dbName, dbVersion, {
|
|
upgrade: upgradeDB,
|
|
});
|
|
const db = await dbPromise;
|
|
const store = db
|
|
.transaction('milestone', 'readonly')
|
|
.objectStore('milestone');
|
|
const milestone = await store.get(id);
|
|
if (!milestone) {
|
|
return null;
|
|
}
|
|
return milestone.milestone;
|
|
};
|
|
|
|
export * from './provider';
|
|
export * from './shared';
|
|
export * from './utils';
|