mirror of
https://github.com/colanode/colanode.git
synced 2025-12-29 00:25:03 +01:00
Add a condition on version id for document updates
This commit is contained in:
@@ -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,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user