diff --git a/plugin/src/sync-operations/apply-local-changes-remotely.ts b/plugin/src/sync-operations/apply-local-changes-remotely.ts new file mode 100644 index 00000000..982d1f6a --- /dev/null +++ b/plugin/src/sync-operations/apply-local-changes-remotely.ts @@ -0,0 +1,131 @@ +import { Database } from "../database/database"; +import { SyncService } from "../services/sync_service"; +import { Logger } from "../logger"; +import { FileOperations } from "../file-operations/file-operations"; +import { syncLocallyCreatedFile } from "./sync-locally-created-file"; +import { EMPTY_HASH, hash } from "src/utils/hash"; +import { syncLocallyUpdatedFile } from "./sync-locally-updated-file"; +import { syncLocallyDeletedFile } from "./sync-locally-deleted-file"; +import { Notice } from "obsidian"; +import PQueue from "p-queue"; + +let isRunning = false; + +export interface Progress { + processedFiles: number; + totalFiles: number; +} + +export async function applyLocalChangesRemotely( + database: Database, + syncServer: SyncService, + operations: FileOperations +) { + console.log("applyLocalChangesRemotely"); + if (isRunning) { + Logger.getInstance().info("Push sync already in progress, skipping"); + return; + } + + let tasks: Promise[] = []; + + const allLocalFiles = await operations.listAllFiles(); + console.log(allLocalFiles); + const deletedFiles = [...database.getDocuments().entries()].filter( + ([path, _]) => !allLocalFiles.includes(path) + ); + + console.log(deletedFiles); + + const promiseQueue = new PQueue({ + concurrency: 1, + }); + + await Promise.all( + allLocalFiles.map((path) => + promiseQueue.add(async () => { + const syncedState = database.getDocument(path); + if (!syncedState) { + Logger.getInstance().info( + `Document ${path} not found in database` + ); + const contentHash = hash(await operations.read(path)); + if (contentHash != EMPTY_HASH) { + const match = deletedFiles.find( + ([path, doc]) => doc.hash === contentHash + ); + if (match) { + const oldPath = match[0]; + Logger.getInstance().info( + `Document ${path} found remotely under a different path (${oldPath}), moving` + ); + tasks.push( + syncLocallyUpdatedFile({ + database, + syncServer, + operations, + oldPath, + filePath: path, + updateTime: + await operations.getModificationTime( + path + ), + }) + ); + deletedFiles.remove(match); + return; + } + } + tasks.push( + syncLocallyCreatedFile({ + database, + syncServer, + operations, + updateTime: await operations.getModificationTime( + path + ), + filePath: path, + }) + ); + return; + } + + const content = await operations.read(path); + if (syncedState.hash !== hash(content)) { + Logger.getInstance().info( + `Document ${path} has local changes, updating` + ); + tasks.push( + syncLocallyUpdatedFile({ + database, + syncServer, + operations, + filePath: path, + updateTime: await operations.getModificationTime( + path + ), + }) + ); + return; + } + }) + ) + ); + + deletedFiles.forEach(([relativePath, _]) => { + Logger.getInstance().info( + `Document ${relativePath} deleted locally, deleting` + ); + tasks.push( + syncLocallyDeletedFile({ + database, + syncServer, + relativePath, + }) + ); + }); + + await Promise.all(tasks); + + new Notice("Local changes synced remotely"); +} diff --git a/plugin/src/apply-remote-changes-locally.ts b/plugin/src/sync-operations/apply-remote-changes-locally.ts similarity index 70% rename from plugin/src/apply-remote-changes-locally.ts rename to plugin/src/sync-operations/apply-remote-changes-locally.ts index 7bb28020..e20e0f7f 100644 --- a/plugin/src/apply-remote-changes-locally.ts +++ b/plugin/src/sync-operations/apply-remote-changes-locally.ts @@ -1,18 +1,18 @@ -import { Database } from "./database/database"; -import { SyncServer } from "./services/sync_service"; -import { syncRemotelyUpdatedFile } from "./sync-operations/sync-remotely-updated-file"; -import { Logger } from "./logger"; -import { FileOperations } from "./file-operations/file-operations"; +import { Database } from "src/database/database"; +import { FileOperations } from "src/file-operations/file-operations"; +import { Logger } from "src/logger"; +import { SyncService } from "src/services/sync_service"; +import { syncRemotelyUpdatedFile } from "./sync-remotely-updated-file"; let isRunning = false; export async function applyRemoteChangesLocally( database: Database, - syncServer: SyncServer, + syncServer: SyncService, operations: FileOperations ) { if (isRunning) { - Logger.getInstance().info("Sync already in progress, skipping"); + Logger.getInstance().info("Pull sync already in progress, skipping"); return; } diff --git a/plugin/src/utils/hash.ts b/plugin/src/utils/hash.ts index 317e274f..92f92e5a 100644 --- a/plugin/src/utils/hash.ts +++ b/plugin/src/utils/hash.ts @@ -7,3 +7,5 @@ export function hash(content: Uint8Array): string { } return hash.toString(16); } + +export const EMPTY_HASH = hash(new Uint8Array(0));