diff --git a/plugin/src/database/database.ts b/plugin/src/database/database.ts index d95f1fb..05f1e16 100644 --- a/plugin/src/database/database.ts +++ b/plugin/src/database/database.ts @@ -90,14 +90,17 @@ export class Database { relativePath, documentId, parentVersionId, + hash, }: { relativePath: RelativePath; documentId: DocumentId; parentVersionId: DocumentVersionId; + hash: string; }): Promise { this._documents.set(relativePath, { documentId, parentVersionId, + hash, }); await this.save(); } @@ -107,16 +110,19 @@ export class Database { relativePath, documentId, parentVersionId, + hash, }: { oldRelativePath: RelativePath; relativePath: RelativePath; documentId: DocumentId; parentVersionId: DocumentVersionId; + hash: string; }): Promise { this._documents.delete(oldRelativePath); this._documents.set(relativePath, { documentId, parentVersionId, + hash, }); await this.save(); } diff --git a/plugin/src/database/document-metadata.ts b/plugin/src/database/document-metadata.ts index 1166797..b015d83 100644 --- a/plugin/src/database/document-metadata.ts +++ b/plugin/src/database/document-metadata.ts @@ -5,4 +5,5 @@ export type RelativePath = string; export interface DocumentMetadata { documentId: DocumentId; parentVersionId: DocumentVersionId; + hash: string; } diff --git a/plugin/src/services/sync_service.ts b/plugin/src/services/sync_service.ts index fc4c808..2a39485 100644 --- a/plugin/src/services/sync_service.ts +++ b/plugin/src/services/sync_service.ts @@ -1,4 +1,4 @@ -import * as plugin from "../../../backend/sync_lib/pkg/sync_lib.js"; +import * as lib from "../../../backend/sync_lib/pkg/sync_lib.js"; import createClient, { Client } from "openapi-fetch"; import type { components, paths } from "./types"; // generated by openapi-typescript @@ -48,14 +48,13 @@ export class SyncServer { public async create({ relativePath, - content, + contentBytes, createdDate, }: { - content: ArrayBuffer; + contentBytes: Uint8Array; relativePath: string; createdDate: Date; }): Promise { - let contentBytes = new Uint8Array(content); let response = await this.client.POST("/vaults/{vault_id}/documents", { params: { path: { vault_id: SyncServer.VAULT_ID }, @@ -65,9 +64,8 @@ export class SyncServer { }, }, body: { - contentBase64: plugin.bytes_to_base64(contentBytes), + contentBase64: lib.bytes_to_base64(contentBytes), createdDate: createdDate.toISOString(), - isBinary: plugin.is_binary(contentBytes), relativePath, }, }); @@ -87,17 +85,15 @@ export class SyncServer { documentId, parentVersionId, relativePath, - content, + contentBytes, createdDate, }: { documentId: DocumentId; parentVersionId: DocumentVersionId; relativePath: string; - content: ArrayBuffer; + contentBytes: Uint8Array; createdDate: Date; }): Promise { - let contentBytes = new Uint8Array(content); - let response = await this.client.PUT( "/vaults/{vault_id}/documents/{document_id}", { @@ -113,9 +109,8 @@ export class SyncServer { }, body: { parentVersionId, - contentBase64: plugin.bytes_to_base64(contentBytes), + contentBase64: lib.bytes_to_base64(contentBytes), createdDate: createdDate.toISOString(), - isBinary: plugin.is_binary(contentBytes), relativePath, }, } diff --git a/plugin/src/sync-functions/sync-locally-deleted-file.ts b/plugin/src/sync-functions/sync-locally-deleted-file.ts index f0d8507..f439e61 100644 --- a/plugin/src/sync-functions/sync-locally-deleted-file.ts +++ b/plugin/src/sync-functions/sync-locally-deleted-file.ts @@ -1,4 +1,3 @@ -import { TFile } from "obsidian"; import { Database } from "src/database/database"; import { RelativePath } from "src/database/document-metadata"; import { SyncServer } from "src/services/sync_service"; @@ -15,7 +14,8 @@ export async function syncLocallyDeletedFile( await syncServer.delete({ documentId: metadata.documentId, - createdDate: new Date(), // We got the event now, so it must have been deleted now + // We got the event now, so it must have been deleted just now + createdDate: new Date(), }); await database.removeDocument(path); diff --git a/plugin/src/sync-functions/sync-locally-renamed-file.ts b/plugin/src/sync-functions/sync-locally-renamed-file.ts index cd7f4fa..d6d4bc8 100644 --- a/plugin/src/sync-functions/sync-locally-renamed-file.ts +++ b/plugin/src/sync-functions/sync-locally-renamed-file.ts @@ -1,6 +1,9 @@ +import * as lib from "../../../backend/sync_lib/pkg/sync_lib.js"; import { TFile } from "obsidian"; import { Database } from "src/database/database"; +import { Logger } from "src/logger"; import { SyncServer } from "src/services/sync_service"; +import { hash } from "src/utils"; export async function syncLocallyRenamedFile( database: Database, @@ -13,18 +16,42 @@ export async function syncLocallyRenamedFile( throw `Document metadata not found for ${oldPath}`; } - const response = await syncServer.update({ + const contentBytes = new Uint8Array(await file.vault.readBinary(file)); + const responsePromise = syncServer.update({ documentId: metadata.documentId, parentVersionId: metadata.parentVersionId, relativePath: file.path, - content: await file.vault.readBinary(file), + contentBytes, createdDate: new Date(file.stat.ctime), }); - await database.moveDocument({ + const contentHash = hash(contentBytes); + const response = await responsePromise; + + const localDbUpdatePromise = database.moveDocument({ oldRelativePath: oldPath, relativePath: file.path, documentId: response.documentId, parentVersionId: response.versionId, + hash: contentHash, }); + + if (file.path !== response.relativePath) { + await file.vault.rename(file, response.relativePath); + } + + const newContentBytes = new Uint8Array(await file.vault.readBinary(file)); + const responseBytes = lib.base64_to_bytes(response.contentBase64); + + if (contentBytes !== newContentBytes) { + Logger.getInstance().info( + `Content changed since sending original update request for ${file.path}` + ); + + const result = lib.merge(contentBytes, newContentBytes, responseBytes); + + await file.vault.modifyBinary(file, result); + } + + await localDbUpdatePromise; } diff --git a/plugin/src/sync-functions/sync-locally-updated-file.ts b/plugin/src/sync-functions/sync-locally-updated-file.ts index 43182cf..ac7cf2c 100644 --- a/plugin/src/sync-functions/sync-locally-updated-file.ts +++ b/plugin/src/sync-functions/sync-locally-updated-file.ts @@ -1,36 +1,13 @@ import { TFile } from "obsidian"; import { Database } from "src/database/database"; import { SyncServer } from "src/services/sync_service"; +import { hash } from "src/utils"; +import { syncLocallyRenamedFile } from "./sync-locally-renamed-file"; export async function syncLocallyUpdatedFile( database: Database, syncServer: SyncServer, file: TFile ) { - const metadata = database.getDocument(file.path); - if (!metadata) { - throw `Document metadata not found for ${file.path}`; - } - - const response = await syncServer.update({ - documentId: metadata.documentId, - parentVersionId: metadata.parentVersionId, - relativePath: file.path, - content: await file.vault.readBinary(file), - createdDate: new Date(file.stat.ctime), - }); - - if (file.path !== response.relativePath) { - file.vault.rename(file, response.relativePath); - } - - if ((await file.vault.read(file)) !== response.contentBase64) { - // todo - reconcile - } - - await database.setDocument({ - relativePath: file.path, - documentId: response.documentId, - parentVersionId: response.versionId, - }); + syncLocallyRenamedFile(database, syncServer, file, file.path); } diff --git a/plugin/src/sync-functions/sync-new-local-file.ts b/plugin/src/sync-functions/sync-new-local-file.ts index 656d796..2cc6baf 100644 --- a/plugin/src/sync-functions/sync-new-local-file.ts +++ b/plugin/src/sync-functions/sync-new-local-file.ts @@ -1,29 +1,48 @@ +import * as lib from "../../../backend/sync_lib/pkg/sync_lib.js"; import { TFile } from "obsidian"; import { Database } from "src/database/database"; +import { Logger } from "src/logger.js"; import { SyncServer } from "src/services/sync_service"; +import { hash } from "src/utils"; export async function syncNewLocalFile( database: Database, syncServer: SyncServer, file: TFile ) { - const response = await syncServer.create({ + const contentBytes = new Uint8Array(await file.vault.readBinary(file)); + const responsePromise = syncServer.create({ relativePath: file.path, - content: await file.vault.readBinary(file), + contentBytes, createdDate: new Date(file.stat.ctime), }); - if (file.path !== response.relativePath) { - file.vault.rename(file, response.relativePath); - } + const contentHash = hash(contentBytes); + const response = await responsePromise; - if ((await file.vault.read(file)) !== response.contentBase64) { - // todo - reconcile - } - - await database.setDocument({ + const localDbUpdatePromise = database.setDocument({ relativePath: response.relativePath, documentId: response.documentId, parentVersionId: response.versionId, + hash: contentHash, }); + + if (file.path !== response.relativePath) { + await file.vault.rename(file, response.relativePath); + } + + const newContentBytes = new Uint8Array(await file.vault.readBinary(file)); + const responseBytes = lib.base64_to_bytes(response.contentBase64); + + if (contentBytes !== newContentBytes) { + Logger.getInstance().info( + `Content changed since sending original create request for ${file.path}` + ); + + const result = lib.merge(contentBytes, newContentBytes, responseBytes); + + await file.vault.modifyBinary(file, result); + } + + await localDbUpdatePromise; }