diff --git a/src/CmdSetupLiveSync.ts b/src/CmdSetupLiveSync.ts index 028b59f..92ec0fb 100644 --- a/src/CmdSetupLiveSync.ts +++ b/src/CmdSetupLiveSync.ts @@ -99,6 +99,7 @@ export class SetupLiveSync extends LiveSyncCommands { newSettingW.encryptedCouchDBConnection = ""; const setupJustImport = "Just import setting"; const setupAsNew = "Set it up as secondary or subsequent device"; + const setupAsMerge = "Secondary device but try keeping local changes"; const setupAgain = "Reconfigure and reconstitute the data"; const setupManually = "Leave everything to me"; newSettingW.syncInternalFiles = false; @@ -108,7 +109,7 @@ export class SetupLiveSync extends LiveSyncCommands { newSettingW.useIndexedDBAdapter = true; } - const setupType = await askSelectString(this.app, "How would you like to set it up?", [setupAsNew, setupAgain, setupJustImport, setupManually]); + const setupType = await askSelectString(this.app, "How would you like to set it up?", [setupAsNew, setupAgain, setupAsMerge, setupJustImport, setupManually]); if (setupType == setupJustImport) { this.plugin.settings = newSettingW; this.plugin.usedPassphrase = ""; @@ -117,6 +118,10 @@ export class SetupLiveSync extends LiveSyncCommands { this.plugin.settings = newSettingW; this.plugin.usedPassphrase = ""; await this.fetchLocal(); + } else if (setupType == setupAsMerge) { + this.plugin.settings = newSettingW; + this.plugin.usedPassphrase = ""; + await this.fetchLocalWithKeepLocal(); } else if (setupType == setupAgain) { const confirm = "I know this operation will rebuild all my databases with files on this device, and files that are on the remote database and I didn't synchronize to any other devices will be lost and want to proceed indeed."; if (await askSelectString(this.app, "Do you really want to do this?", ["Cancel", confirm]) != confirm) { @@ -302,13 +307,11 @@ Of course, we are able to disable these features.` Logger(`Database and storage reflection has been resumed!`, LOG_LEVEL_NOTICE); this.plugin.settings.suspendParseReplicationResult = false; this.plugin.settings.suspendFileWatching = false; + await this.plugin.syncAllFiles(true); + await this.plugin.loadQueuedFiles(); + this.plugin.procQueuedFiles(); await this.plugin.saveSettings(); - if (this.plugin.settings.readChunksOnline) { - await this.plugin.syncAllFiles(true); - await this.plugin.loadQueuedFiles(); - // Start processing - this.plugin.procQueuedFiles(); - } + } async askUseNewAdapter() { if (!this.plugin.settings.useIndexedDBAdapter) { @@ -353,6 +356,25 @@ Of course, we are able to disable these features.` await this.resumeReflectingDatabase(); await this.askHiddenFileConfiguration({ enableFetch: true }); } + async fetchLocalWithKeepLocal() { + this.suspendExtraSync(); + this.askUseNewAdapter(); + await this.suspendReflectingDatabase(); + await this.plugin.realizeSettingSyncMode(); + await this.plugin.resetLocalDatabase(); + await delay(1000); + await this.plugin.initializeDatabase(true); + await this.plugin.markRemoteResolved(); + await this.plugin.openDatabase(); + this.plugin.isReady = true; + await delay(500); + await this.plugin.replicateAllFromServer(true); + await delay(1000); + await this.plugin.replicateAllFromServer(true); + await this.fetchRemoteChunks(); + await this.resumeReflectingDatabase(); + await this.askHiddenFileConfiguration({ enableFetch: true }); + } async rebuildRemote() { this.suspendExtraSync(); await this.plugin.realizeSettingSyncMode(); diff --git a/src/lib b/src/lib index 8872807..70eb916 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit 8872807f470cec6d6e7deb4a4ea76ad3664b4bfc +Subproject commit 70eb916288d0f62df40dd4fe4341afb31e4730a3 diff --git a/src/main.ts b/src/main.ts index 9c72a58..0217db8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1167,17 +1167,25 @@ export default class ObsidianLiveSyncPlugin extends Plugin if (docEntry._deleted || docEntry.deleted) { // This occurs not only when files are deleted, but also when conflicts are resolved. // We have to check no other revisions are left. - const lastDocs = await this.localDatabase.getDBEntry(path); + const existDoc = await this.localDatabase.getDBEntry(path, { conflicts: true }); if (path != file.path) { Logger(`delete skipped: ${file.path} :Not exactly matched`, LOG_LEVEL_VERBOSE); } - if (lastDocs === false) { + if (existDoc === false) { await this.deleteVaultItem(file); } else { - // it perhaps delete some revisions. - // may be we have to reload this - await this.pullFile(path, null, true); - Logger(`delete skipped:${file.path}`, LOG_LEVEL_VERBOSE); + if (existDoc._conflicts) { + if (this.settings.writeDocumentsIfConflicted) { + Logger(`Delete: ${file.path}: Conflicted revision has been deleted, but there were more conflicts. `, LOG_LEVEL_INFO); + await this.pullFile(path, null, true); + } else { + Logger(`Delete: ${file.path}: Conflicted revision has been deleted, but there were more conflicts...`); + this.queueConflictedOnlyActiveFile(file); + } + } else { + Logger(`Delete: ${file.path}: Conflict revision has been deleted and resolved`); + await this.pullFile(path, null, true); + } } return; } @@ -1274,6 +1282,19 @@ export default class ObsidianLiveSyncPlugin extends Plugin this.dbChangeProcRunning = false; } } + queueConflictedOnlyActiveFile(file: TFile) { + if (!this.settings.checkConflictOnlyOnOpen) { + this.queueConflictedCheck(file); + return true; + } else { + const af = this.app.workspace.getActiveFile(); + if (af && af.path == file.path) { + this.queueConflictedCheck(file); + return true; + } + } + return false; + } async handleDBChangedAsync(change: EntryBody) { const targetFile = getAbstractFileByPath(this.getPathWithoutPrefix(change)); @@ -1286,29 +1307,16 @@ export default class ObsidianLiveSyncPlugin extends Plugin } else if (targetFile instanceof TFile) { const doc = change; const file = targetFile; - const queueConflictCheck = () => { - if (!this.settings.checkConflictOnlyOnOpen) { - this.queueConflictedCheck(file); - return true; - } else { - const af = app.workspace.getActiveFile(); - if (af && af.path == file.path) { - this.queueConflictedCheck(file); - return true; - } - } - return false; - } if (this.settings.writeDocumentsIfConflicted) { await this.doc2storage(doc, file); - queueConflictCheck(); + this.queueConflictedOnlyActiveFile(file); } else { const d = await this.localDatabase.getDBEntryMeta(this.getPath(change), { conflicts: true }, true); if (d && !d._conflicts) { await this.doc2storage(doc, file); } else { - if (!queueConflictCheck()) { - Logger(`${this.getPath(change)} is conflicted, write to the storage has been pended.`, LOG_LEVEL_NOTICE); + if (!this.queueConflictedOnlyActiveFile(file)) { + Logger(`${this.getPath(change)} is conflicted, write to the storage has been postponed.`, LOG_LEVEL_NOTICE); } } } diff --git a/src/utils.ts b/src/utils.ts index 921df00..0389fd3 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -477,7 +477,7 @@ export const requestToCouchDB = async (baseUri: string, username: string, passwo export async function performRebuildDB(plugin: ObsidianLiveSyncPlugin, method: "localOnly" | "remoteOnly" | "rebuildBothByThisDevice") { if (method == "localOnly") { - await plugin.addOnSetup.fetchLocal(); + await plugin.addOnSetup.fetchLocalWithKeepLocal(); } if (method == "remoteOnly") { await plugin.addOnSetup.rebuildRemote(); diff --git a/tsconfig.json b/tsconfig.json index a7369d6..aeeea68 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,6 +15,7 @@ // "importsNotUsedAsValues": "error", "importHelpers": false, "alwaysStrict": true, + "allowImportingTsExtensions": true, "lib": [ "es2018", "DOM",