diff --git a/src/features/ConfigSync/CmdConfigSync.ts b/src/features/ConfigSync/CmdConfigSync.ts index 50b9d46..8007e57 100644 --- a/src/features/ConfigSync/CmdConfigSync.ts +++ b/src/features/ConfigSync/CmdConfigSync.ts @@ -1306,7 +1306,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { ); return; } - const docXDoc = await this.localDatabase.getDBEntryFromMeta(old, {}, false, false); + const docXDoc = await this.localDatabase.getDBEntryFromMeta(old, false, false); if (docXDoc == false) { throw "Could not load the document"; } @@ -1440,7 +1440,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { // this._log(`STORAGE --> DB:${prefixedFileName}: (config) Skipped (Same time)`, LOG_LEVEL_VERBOSE); return true; } - const oldC = await this.localDatabase.getDBEntryFromMeta(old, {}, false, false); + const oldC = await this.localDatabase.getDBEntryFromMeta(old, false, false); if (oldC) { const d = (await deserialize(getDocDataAsArray(oldC.data), {})) as PluginDataEx; if (d.files.length == dt.files.length) { diff --git a/src/features/HiddenFileSync/CmdHiddenFileSync.ts b/src/features/HiddenFileSync/CmdHiddenFileSync.ts index fcc09db..5599120 100644 --- a/src/features/HiddenFileSync/CmdHiddenFileSync.ts +++ b/src/features/HiddenFileSync/CmdHiddenFileSync.ts @@ -1490,7 +1490,7 @@ Offline Changed files: ${files.length}`; } return false; } else { - const fileOnDB = await this.localDatabase.getDBEntryFromMeta(metaOnDB, {}, false, true, true); + const fileOnDB = await this.localDatabase.getDBEntryFromMeta(metaOnDB, false, true); if (fileOnDB === false) { throw new Error(`Failed to read file from database:${storageFilePath}`); } diff --git a/src/lib b/src/lib index a5d21af..1e24d2a 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit a5d21afb61bec76fb6ca931e5cfae8fcb11ec3a6 +Subproject commit 1e24d2af19ebd66ea77b5c736153300c7dc53f04 diff --git a/src/modules/core/ModuleDatabaseFileAccess.ts b/src/modules/core/ModuleDatabaseFileAccess.ts index 0077f1e..d186c62 100644 --- a/src/modules/core/ModuleDatabaseFileAccess.ts +++ b/src/modules/core/ModuleDatabaseFileAccess.ts @@ -313,13 +313,7 @@ export class ModuleDatabaseFileAccess extends AbstractModule implements IObsidia if (skipCheck && !(await this.checkIsTargetFile(meta.path))) { return false; } - const doc = await this.localDatabase.getDBEntryFromMeta( - meta as LoadedEntry, - undefined, - false, - waitForReady, - true - ); + const doc = await this.localDatabase.getDBEntryFromMeta(meta as LoadedEntry, false, waitForReady); if (doc === false) { return false; } diff --git a/src/modules/core/ModuleRebuilder.ts b/src/modules/core/ModuleRebuilder.ts index f571c26..b382938 100644 --- a/src/modules/core/ModuleRebuilder.ts +++ b/src/modules/core/ModuleRebuilder.ts @@ -176,7 +176,7 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu } } } - async fetchLocal(makeLocalChunkBeforeSync?: boolean) { + async fetchLocal(makeLocalChunkBeforeSync?: boolean, preventMakeLocalFilesBeforeSync?: boolean) { await this.core.$allSuspendExtraSync(); await this.askUseNewAdapter(); this.core.settings.isConfigured = true; @@ -189,6 +189,10 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu this.core.$$markIsReady(); if (makeLocalChunkBeforeSync) { await this.core.fileHandler.createAllChunks(true); + } else if (!preventMakeLocalFilesBeforeSync) { + await this.core.$$initializeDatabase(true); + } else { + // Do not create local file entries before sync (Means use remote information) } await this.core.$$markRemoteResolved(); await delay(500); diff --git a/src/modules/core/ModuleReplicator.ts b/src/modules/core/ModuleReplicator.ts index 2bce704..65f76da 100644 --- a/src/modules/core/ModuleReplicator.ts +++ b/src/modules/core/ModuleReplicator.ts @@ -338,7 +338,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid // If `Read chunks online` is disabled, chunks should be transferred before here. // However, in some cases, chunks are after that. So, if missing chunks exist, we have to wait for them. - const doc = await this.localDatabase.getDBEntryFromMeta({ ...dbDoc }, {}, false, true, true); + const doc = await this.localDatabase.getDBEntryFromMeta({ ...dbDoc }, false, true); if (!doc) { Logger( `Something went wrong while gathering content of ${path} (${dbDoc._id.substring(0, 8)}, ${dbDoc._rev?.substring(0, 10)}) `, diff --git a/src/modules/coreFeatures/ModuleConflictResolver.ts b/src/modules/coreFeatures/ModuleConflictResolver.ts index 1d5c0f1..ca35d9f 100644 --- a/src/modules/coreFeatures/ModuleConflictResolver.ts +++ b/src/modules/coreFeatures/ModuleConflictResolver.ts @@ -44,13 +44,16 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul return MISSING_OR_ERROR; } eventHub.emitEvent("conflict-cancelled", path); - this._log(`${title} Conflicted revision deleted ${displayRev(deleteRevision)} ${path}`, LOG_LEVEL_INFO); + this._log( + `${title} Conflicted revision has been deleted ${displayRev(deleteRevision)} ${path}`, + LOG_LEVEL_INFO + ); if ((await this.core.databaseFileAccess.getConflictedRevs(path)).length != 0) { this._log(`${title} some conflicts are left in ${path}`, LOG_LEVEL_INFO); return AUTO_MERGED; } - this._log(`${title} ${path} is a plugin metadata file, no need to write to storage`, LOG_LEVEL_INFO); if (isPluginMetadata(path) || isCustomisationSyncMetadata(path)) { + this._log(`${title} ${path} is a plugin metadata file, no need to write to storage`, LOG_LEVEL_INFO); return AUTO_MERGED; } // If no conflicts were found, write the resolved content to the storage. @@ -58,7 +61,8 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul this._log(`Could not write the resolved content to the storage: ${path}`, LOG_LEVEL_NOTICE); return MISSING_OR_ERROR; } - this._log(`${path} Has been merged automatically`, LOG_LEVEL_NOTICE); + const level = subTitle.indexOf("same") !== -1 ? LOG_LEVEL_INFO : LOG_LEVEL_NOTICE; + this._log(`${path} has been merged automatically`, level); return AUTO_MERGED; } @@ -108,7 +112,9 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul `${isSame ? "same" : ""}`, `${isBinary ? "binary" : ""}`, `${alwaysNewer ? "alwaysNewer" : ""}`, - ].join(","); + ] + .filter((e) => e.trim()) + .join(","); return await this.core.$$resolveConflictByDeletingRev(path, loser.rev, subTitle); } // make diff. diff --git a/src/modules/coreFeatures/ModuleRedFlag.ts b/src/modules/coreFeatures/ModuleRedFlag.ts index 320d00b..565a655 100644 --- a/src/modules/coreFeatures/ModuleRedFlag.ts +++ b/src/modules/coreFeatures/ModuleRedFlag.ts @@ -9,6 +9,7 @@ import { } from "../../lib/src/common/types.ts"; import { AbstractModule } from "../AbstractModule.ts"; import type { ICoreModule } from "../ModuleTypes.ts"; +import { $msg } from "../../lib/src/common/i18n.ts"; export class ModuleRedFlag extends AbstractModule implements ICoreModule { async isFlagFileExist(path: string) { @@ -105,16 +106,35 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule { `${FLAGMD_REDFLAG3} or ${FLAGMD_REDFLAG3_HR} has been detected! Self-hosted LiveSync will discard the local database and fetch everything from the remote once again.`, LOG_LEVEL_NOTICE ); - const makeLocalChunkBeforeSync = - (await this.core.confirm.askYesNoDialog( - `Do you want to create local chunks before fetching? -> [!MORE]- -> If creating local chunks before fetching, only the difference between the local and remote will be fetched. + const method1 = $msg("RedFlag.Fetch.Method.FetchSafer"); + const method2 = $msg("RedFlag.Fetch.Method.FetchSmoother"); + const method3 = $msg("RedFlag.Fetch.Method.FetchTraditional"); + + const methods = [method1, method2, method3] as const; + const chunkMode = await this.core.confirm.askSelectStringDialogue( + $msg("RedFlag.Fetch.Method.Desc"), + methods, + { + defaultAction: method1, + timeout: 0, + title: $msg("RedFlag.Fetch.Method.Title"), + } + ); + let makeLocalChunkBeforeSync = false; + let preventMakeLocalFilesBeforeSync = false; + if (chunkMode === method1) { + preventMakeLocalFilesBeforeSync = true; + } else if (chunkMode === method2) { + makeLocalChunkBeforeSync = true; + } else if (chunkMode === method3) { + // Do nothing. + } else { + this._log("Cancelled the fetch operation", LOG_LEVEL_NOTICE); + return false; + } + + await this.core.rebuilder.$fetchLocal(makeLocalChunkBeforeSync, preventMakeLocalFilesBeforeSync); -`, - { defaultOption: "Yes", title: "Trick to transfer efficiently" } - )) == "yes"; - await this.core.rebuilder.$fetchLocal(makeLocalChunkBeforeSync); await this.deleteRedFlag3(); if (this.settings.suspendFileWatching) { if ( diff --git a/src/modules/essential/ModuleInitializerFile.ts b/src/modules/essential/ModuleInitializerFile.ts index 33baa98..4281354 100644 --- a/src/modules/essential/ModuleInitializerFile.ts +++ b/src/modules/essential/ModuleInitializerFile.ts @@ -180,7 +180,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule }; initProcess.push( runAll("UPDATE DATABASE", filesExistOnlyInStorage, async (e) => { - // console.warn("UPDATE DATABASE", e); + // Exists in storage but not in database. const file = storageFileNameMap[storageFileNameCI2CS[e]]; if (!this.core.$$isFileSizeExceeded(file.stat.size)) { const path = file.path; @@ -195,9 +195,16 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule initProcess.push( runAll("UPDATE STORAGE", filesExistOnlyInDatabase, async (e) => { const w = databaseFileNameMap[databaseFileNameCI2CS[e]]; + // Exists in database but not in storage. const path = getPath(w) ?? e; if (w && !(w.deleted || w._deleted)) { if (!this.core.$$isFileSizeExceeded(w.size)) { + // Prevent applying the conflicted state to the storage. + const conflicted = await this.core.databaseFileAccess.getConflictedRevs(path); + if (conflicted.length > 0) { + this._log(`UPDATE STORAGE: ${path} has conflicts. skipped`, LOG_LEVEL_INFO); + return; + } // await this.pullFile(path, undefined, false, undefined, false); // Memo: No need to force await this.core.fileHandler.dbToStorage(path, null, true); @@ -229,6 +236,12 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule initProcess.push( runAll("SYNC DATABASE AND STORAGE", fileMap, async (e) => { const { file, doc } = e; + // Prevent applying the conflicted state to the storage. + const conflicted = await this.core.databaseFileAccess.getConflictedRevs(file.path); + if (conflicted.length > 0) { + this._log(`SYNC DATABASE AND STORAGE: ${file.path} has conflicts. skipped`, LOG_LEVEL_INFO); + return; + } if (!this.core.$$isFileSizeExceeded(file.stat.size) && !this.core.$$isFileSizeExceeded(doc.size)) { await this.syncFileBetweenDBandStorage(file, doc); } else { diff --git a/src/modules/interfaces/DatabaseRebuilder.ts b/src/modules/interfaces/DatabaseRebuilder.ts index d55398b..4dbf477 100644 --- a/src/modules/interfaces/DatabaseRebuilder.ts +++ b/src/modules/interfaces/DatabaseRebuilder.ts @@ -4,7 +4,7 @@ export interface Rebuilder { ): Promise; $rebuildRemote(): Promise; $rebuildEverything(): Promise; - $fetchLocal(makeLocalChunkBeforeSync?: boolean): Promise; + $fetchLocal(makeLocalChunkBeforeSync?: boolean, preventMakeLocalFilesBeforeSync?: boolean): Promise; scheduleRebuild(): Promise; scheduleFetch(): Promise;