Add a condition on version id for document updates

This commit is contained in:
Hakan Shehu
2024-10-29 00:48:46 +01:00
parent de9a7ffb21
commit 87ea22f85c
3 changed files with 211 additions and 157 deletions

View File

@@ -7,6 +7,7 @@ import { generateId, IdType } from '@/lib/id';
import { LocalUpdateNodeChangeData } from '@/types/sync';
import { applyChangeToYDoc, mapContentsToBlocks } from '@/lib/editor';
import { LocalNodeAttributes, NodeBlock } from '@/types/nodes';
import { hasUpdateChanges } from '@/main/utils';
export class DocumentSaveMutationHandler
implements MutationHandler<DocumentSaveMutationInput>
@@ -18,112 +19,132 @@ export class DocumentSaveMutationHandler
input.userId,
);
const document = await workspaceDatabase
.selectFrom('nodes')
.selectAll()
.where('id', '=', input.documentId)
.executeTakeFirst();
let count = 0;
while (count++ < 10) {
const document = await workspaceDatabase
.selectFrom('nodes')
.selectAll()
.where('id', '=', input.documentId)
.executeTakeFirst();
if (!document) {
return {
output: {
success: false,
},
changes: [],
if (!document) {
return {
output: {
success: false,
},
changes: [],
};
}
const versionId = generateId(IdType.Version);
const updatedAt = new Date().toISOString();
const updates: string[] = [];
const doc = new Y.Doc({
guid: document.id,
});
Y.applyUpdate(doc, toUint8Array(document.state));
doc.on('update', (update) => {
updates.push(fromUint8Array(update));
});
const attributes = JSON.parse(document.attributes) as LocalNodeAttributes;
const blocksMap = new Map<string, NodeBlock>();
if (attributes.content) {
for (const [key, value] of Object.entries(attributes.content)) {
blocksMap.set(key, value);
}
}
const blocks = mapContentsToBlocks(
document.id,
input.content.content,
blocksMap,
);
doc.transact(() => {
applyChangeToYDoc(doc, blocks);
});
if (updates.length === 0) {
return {
output: {
success: true,
},
changes: [],
};
}
const attributesMap = doc.getMap('attributes');
const attributesJson = JSON.stringify(attributesMap.toJSON());
const encodedState = fromUint8Array(Y.encodeStateAsUpdate(doc));
const changeData: LocalUpdateNodeChangeData = {
type: 'node_update',
id: document.id,
updatedAt: updatedAt,
updatedBy: input.userId,
versionId: versionId,
updates: updates,
};
}
const versionId = generateId(IdType.Version);
const updatedAt = new Date().toISOString();
const updates: string[] = [];
const result = await workspaceDatabase
.transaction()
.execute(async (trx) => {
const result = await trx
.updateTable('nodes')
.set({
attributes: attributesJson,
state: encodedState,
updated_at: updatedAt,
updated_by: input.userId,
version_id: versionId,
})
.where('id', '=', input.documentId)
.where('version_id', '=', document.version_id)
.execute();
const doc = new Y.Doc({
guid: document.id,
});
Y.applyUpdate(doc, toUint8Array(document.state));
const hasChanges = hasUpdateChanges(result);
doc.on('update', (update) => {
updates.push(fromUint8Array(update));
});
if (hasChanges) {
await trx
.insertInto('changes')
.values({
data: JSON.stringify(changeData),
created_at: updatedAt,
})
.execute();
}
const attributes = JSON.parse(document.attributes) as LocalNodeAttributes;
const blocksMap = new Map<string, NodeBlock>();
if (attributes.content) {
for (const [key, value] of Object.entries(attributes.content)) {
blocksMap.set(key, value);
return hasChanges;
});
if (result) {
return {
output: {
success: true,
},
changes: [
{
type: 'workspace',
table: 'nodes',
userId: input.userId,
},
{
type: 'workspace',
table: 'changes',
userId: input.userId,
},
],
};
}
}
const blocks = mapContentsToBlocks(
document.id,
input.content.content,
blocksMap,
);
doc.transact(() => {
applyChangeToYDoc(doc, blocks);
});
if (updates.length === 0) {
return {
output: {
success: true,
},
changes: [],
};
}
const attributesMap = doc.getMap('attributes');
const attributesJson = JSON.stringify(attributesMap.toJSON());
const encodedState = fromUint8Array(Y.encodeStateAsUpdate(doc));
const changeData: LocalUpdateNodeChangeData = {
type: 'node_update',
id: document.id,
updatedAt: updatedAt,
updatedBy: input.userId,
versionId: versionId,
updates: updates,
};
await workspaceDatabase.transaction().execute(async (trx) => {
await trx
.updateTable('nodes')
.set({
attributes: attributesJson,
state: encodedState,
updated_at: updatedAt,
updated_by: input.userId,
version_id: versionId,
})
.where('id', '=', input.documentId)
.execute();
await trx
.insertInto('changes')
.values({
data: JSON.stringify(changeData),
created_at: updatedAt,
})
.execute();
});
return {
output: {
success: true,
success: false,
},
changes: [
{
type: 'workspace',
table: 'nodes',
userId: input.userId,
},
{
type: 'workspace',
table: 'changes',
userId: input.userId,
},
],
};
}
}

View File

@@ -1,4 +1,5 @@
import { app } from 'electron';
import { DeleteResult, InsertResult, UpdateResult } from 'kysely';
import path from 'path';
export const appPath = app.getPath('userData');
@@ -16,3 +17,25 @@ export const getWorkspaceFilesDirectoryPath = (userId: string): string => {
export const getAccountAvatarsDirectoryPath = (accountId: string): string => {
return path.join(appPath, 'avatars', accountId);
};
export const hasInsertChanges = (result: InsertResult[]): boolean => {
if (result.length === 0) {
return false;
}
return result.some(
(r) => r.numInsertedOrUpdatedRows && r.numInsertedOrUpdatedRows > 0n,
);
};
export const hasUpdateChanges = (result: UpdateResult[]): boolean => {
if (result.length === 0) {
return false;
}
return result.some((r) => r.numUpdatedRows && r.numUpdatedRows > 0n);
};
export const hasDeleteChanges = (result: DeleteResult[]): boolean => {
return result.some((r) => r.numDeletedRows && r.numDeletedRows > 0n);
};