mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-07-25 05:53:00 +00:00
Group Hidden File Sync initialisation progress
This commit is contained in:
@@ -54,11 +54,20 @@ import { EVENT_SETTING_SAVED, eventHub } from "@/common/events.ts";
|
||||
import { Semaphore } from "octagonal-wheels/concurrency/semaphore";
|
||||
import type { LiveSyncCore } from "@/main.ts";
|
||||
import { tryGetFilePath } from "@vrtmrz/livesync-commonlib/compat/common/utils.doc";
|
||||
import { configureHiddenFileSyncMode } from "./configureHiddenFileSyncMode.ts";
|
||||
import {
|
||||
configureHiddenFileSyncMode,
|
||||
type ConfigureHiddenFileSyncResult,
|
||||
} from "./configureHiddenFileSyncMode.ts";
|
||||
import type { OptionalSyncFeatureMode } from "@/features/optionalSyncFeatures.ts";
|
||||
import { getObsidianCommunityPluginManager } from "@/common/obsidianCommunityPlugins.ts";
|
||||
type SyncDirection = "push" | "pull" | "safe" | "pullForce" | "pushForce";
|
||||
|
||||
type HiddenFileInitialisationProgress = {
|
||||
log(message: string): void;
|
||||
once(message: string): void;
|
||||
done(message?: string): void;
|
||||
};
|
||||
|
||||
const HIDDEN_FILE_NOTICE_GROUP = "hidden-file-changes";
|
||||
const HIDDEN_FILE_NOTICE_DURATION_MS = 20_000;
|
||||
|
||||
@@ -1421,13 +1430,18 @@ Offline Changed files: ${files.length}`;
|
||||
direction: SyncDirection,
|
||||
showMessage: boolean,
|
||||
// filesAll: InternalFileInfo[] | false = false,
|
||||
targetFilesSrc: string[] | false = false
|
||||
targetFilesSrc: string[] | false = false,
|
||||
initialisationProgress?: HiddenFileInitialisationProgress
|
||||
) {
|
||||
const logLevel = showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO;
|
||||
const p = this._progress("[⚙ Initialise]\n", logLevel);
|
||||
const p = initialisationProgress ?? this._progress("[⚙ Initialise]\n", logLevel);
|
||||
// p.log("Resolving conflicts before starting...");
|
||||
// await this.resolveConflictOnInternalFiles();
|
||||
p.log("Initialising hidden files sync...");
|
||||
// The initialisation progress owns the user-visible Notice. Its child
|
||||
// rebuild and scan operations still write ordinary log entries, but
|
||||
// must not each create another keep-alive Notice.
|
||||
const showChildNotices = false;
|
||||
// TODO: Handling ignore files cannot be performed to the hidden files.
|
||||
|
||||
const targetFiles = targetFilesSrc
|
||||
@@ -1436,35 +1450,38 @@ Offline Changed files: ${files.length}`;
|
||||
if (direction == "pushForce" || direction == "push") {
|
||||
const onlyNew = direction == "push";
|
||||
p.log(`Started: Storage --> Database ${onlyNew ? "(Only New)" : ""}`);
|
||||
const updatedFiles = await this.rebuildFromStorage(showMessage, targetFiles, onlyNew);
|
||||
const updatedFiles = await this.rebuildFromStorage(showChildNotices, targetFiles, onlyNew);
|
||||
// making doubly sure, No more losing files.
|
||||
// I did so many times during the development.
|
||||
await this.adoptCurrentStorageFilesAsProcessed(updatedFiles);
|
||||
await this.adoptCurrentDatabaseFilesAsProcessed(updatedFiles);
|
||||
// And, scan other changes on the database (i.e. files which are on only other devices)
|
||||
await this.scanAllStorageChanges(showMessage, true, false);
|
||||
await this.scanAllDatabaseChanges(showMessage, true, false);
|
||||
p.log("Checking for remaining storage and database changes...");
|
||||
await this.scanAllStorageChanges(showChildNotices, true, false);
|
||||
await this.scanAllDatabaseChanges(showChildNotices, true, false);
|
||||
}
|
||||
if (direction == "pullForce" || direction == "pull") {
|
||||
const onlyNew = direction == "pull";
|
||||
p.log(`Started: Database --> Storage ${onlyNew ? "(Only New)" : ""}`);
|
||||
const updatedEntries = await this.rebuildFromDatabase(showMessage, targetFiles, onlyNew);
|
||||
const updatedEntries = await this.rebuildFromDatabase(showChildNotices, targetFiles, onlyNew);
|
||||
const updatedFiles = updatedEntries.map((e) => stripAllPrefixes(this.getPath(e)));
|
||||
// making doubly sure, No more losing files.
|
||||
await this.adoptCurrentStorageFilesAsProcessed(updatedFiles);
|
||||
await this.adoptCurrentDatabaseFilesAsProcessed(updatedFiles);
|
||||
// And, scan other changes on the database (i.e. files which are on only other devices)
|
||||
await this.scanAllDatabaseChanges(showMessage, true, false);
|
||||
await this.scanAllStorageChanges(showMessage, true, false);
|
||||
p.log("Checking for remaining database and storage changes...");
|
||||
await this.scanAllDatabaseChanges(showChildNotices, true, false);
|
||||
await this.scanAllStorageChanges(showChildNotices, true, false);
|
||||
}
|
||||
if (direction == "safe") {
|
||||
p.log(`Started: Database <--> Storage (by modified date)`);
|
||||
const updatedFiles = await this.rebuildMerging(showMessage, targetFiles);
|
||||
const updatedFiles = await this.rebuildMerging(showChildNotices, targetFiles);
|
||||
await this.adoptCurrentStorageFilesAsProcessed(updatedFiles);
|
||||
await this.adoptCurrentDatabaseFilesAsProcessed(updatedFiles);
|
||||
// And, scan other changes on the database (i.e. files which are on only other devices)
|
||||
await this.scanAllStorageChanges(showMessage, true, false);
|
||||
await this.scanAllDatabaseChanges(showMessage, true, false);
|
||||
p.log("Checking for remaining storage and database changes...");
|
||||
await this.scanAllStorageChanges(showChildNotices, true, false);
|
||||
await this.scanAllDatabaseChanges(showChildNotices, true, false);
|
||||
}
|
||||
p.done();
|
||||
}
|
||||
@@ -1789,32 +1806,44 @@ Offline Changed files: ${files.length}`;
|
||||
}
|
||||
|
||||
async configureHiddenFileSync(mode: OptionalSyncFeatureMode) {
|
||||
const result = await configureHiddenFileSyncMode(mode, {
|
||||
disable: async () => {
|
||||
// await this.core.$allSuspendExtraSync();
|
||||
await this.core.services.setting.applyPartial(
|
||||
{
|
||||
syncInternalFiles: false,
|
||||
},
|
||||
true
|
||||
);
|
||||
// this.core.settings.syncInternalFiles = false;
|
||||
// await this.core.saveSettings();
|
||||
},
|
||||
enable: async () => {
|
||||
this._log("Gathering files for enabling Hidden File Sync", LOG_LEVEL_NOTICE);
|
||||
await this.core.services.setting.applyPartial(
|
||||
{
|
||||
useAdvancedMode: true,
|
||||
syncInternalFiles: true,
|
||||
},
|
||||
true
|
||||
);
|
||||
},
|
||||
initialise: async (direction) => {
|
||||
await this.initialiseInternalFileSync(direction, true);
|
||||
},
|
||||
});
|
||||
let initialisationProgress: HiddenFileInitialisationProgress | undefined;
|
||||
let result: ConfigureHiddenFileSyncResult;
|
||||
try {
|
||||
result = await configureHiddenFileSyncMode(mode, {
|
||||
disable: async () => {
|
||||
// await this.core.$allSuspendExtraSync();
|
||||
await this.core.services.setting.applyPartial(
|
||||
{
|
||||
syncInternalFiles: false,
|
||||
},
|
||||
true
|
||||
);
|
||||
// this.core.settings.syncInternalFiles = false;
|
||||
// await this.core.saveSettings();
|
||||
},
|
||||
enable: async () => {
|
||||
// Open the one user-visible progress Notice before saving
|
||||
// the setting. Large Vaults can otherwise appear idle
|
||||
// before the initial file enumeration begins.
|
||||
initialisationProgress = this._progress("[⚙ Initialise]\n", LOG_LEVEL_NOTICE);
|
||||
initialisationProgress.log("Preparing Hidden File Sync...");
|
||||
await this.core.services.setting.applyPartial(
|
||||
{
|
||||
useAdvancedMode: true,
|
||||
syncInternalFiles: true,
|
||||
},
|
||||
true
|
||||
);
|
||||
},
|
||||
initialise: async (direction) => {
|
||||
await this.initialiseInternalFileSync(direction, true, false, initialisationProgress);
|
||||
initialisationProgress = undefined;
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
initialisationProgress?.done("Failed");
|
||||
throw error;
|
||||
}
|
||||
if (result == "ignored" || result == "disabled") {
|
||||
return;
|
||||
}
|
||||
@@ -1822,7 +1851,7 @@ Offline Changed files: ${files.length}`;
|
||||
// this.plugin.settings.syncInternalFiles = true;
|
||||
|
||||
// await this.plugin.saveSettings();
|
||||
this._log(`Done! Restarting the app is strongly recommended!`, LOG_LEVEL_NOTICE);
|
||||
this._log("Hidden File Sync initialisation completed.", LOG_LEVEL_INFO);
|
||||
}
|
||||
// <-- Configuration handling
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { LOG_LEVEL_NOTICE } from "@vrtmrz/livesync-commonlib/compat/common/types";
|
||||
|
||||
vi.mock("@/deps.ts", () => ({}));
|
||||
vi.mock("@/features/HiddenFileCommon/JsonResolveModal.ts", () => ({
|
||||
@@ -21,6 +22,7 @@ vi.mock("./configureHiddenFileSyncMode.ts", () => ({
|
||||
}));
|
||||
|
||||
import { HiddenFileSync } from "./CmdHiddenFileSync.ts";
|
||||
import { configureHiddenFileSyncMode } from "./configureHiddenFileSyncMode.ts";
|
||||
|
||||
describe("HiddenFileSync configuration-change notices", () => {
|
||||
it("groups plug-in reloads and an Obsidian restart into one finished Notice", async () => {
|
||||
@@ -102,4 +104,106 @@ describe("HiddenFileSync configuration-change notices", () => {
|
||||
expect(core.services.appLifecycle.scheduleRestart).toHaveBeenCalledOnce();
|
||||
expect(noticeGroups.removeItem).toHaveBeenCalledWith("hidden-file-changes", "restart");
|
||||
});
|
||||
|
||||
it("keeps subordinate initialisation phases below Notice level so one progress Notice owns the scan", async () => {
|
||||
const progress = {
|
||||
log: vi.fn(),
|
||||
once: vi.fn(),
|
||||
done: vi.fn(),
|
||||
};
|
||||
const rebuildMerging = vi.fn(async () => []);
|
||||
const adoptCurrentStorageFilesAsProcessed = vi.fn(async () => undefined);
|
||||
const adoptCurrentDatabaseFilesAsProcessed = vi.fn(async () => undefined);
|
||||
const scanAllStorageChanges = vi.fn(async () => undefined);
|
||||
const scanAllDatabaseChanges = vi.fn(async () => undefined);
|
||||
const hiddenFileSync = Object.create(HiddenFileSync.prototype) as HiddenFileSync;
|
||||
Object.assign(hiddenFileSync, {
|
||||
_progress: vi.fn(() => progress),
|
||||
rebuildMerging,
|
||||
adoptCurrentStorageFilesAsProcessed,
|
||||
adoptCurrentDatabaseFilesAsProcessed,
|
||||
scanAllStorageChanges,
|
||||
scanAllDatabaseChanges,
|
||||
});
|
||||
|
||||
await hiddenFileSync.initialiseInternalFileSync("safe", true);
|
||||
|
||||
expect(rebuildMerging).toHaveBeenCalledWith(false, false);
|
||||
expect(scanAllStorageChanges).toHaveBeenCalledWith(false, true, false);
|
||||
expect(scanAllDatabaseChanges).toHaveBeenCalledWith(false, true, false);
|
||||
expect(progress.done).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("does not surround the initialisation progress with separate gathering and restart Notices", async () => {
|
||||
vi.mocked(configureHiddenFileSyncMode).mockImplementation(async (_mode, handlers) => {
|
||||
await handlers.enable();
|
||||
await handlers.initialise("safe");
|
||||
return "enabled";
|
||||
});
|
||||
const events: string[] = [];
|
||||
const progress = {
|
||||
log: vi.fn((message: string) => {
|
||||
events.push(`progress:${message}`);
|
||||
}),
|
||||
once: vi.fn(),
|
||||
done: vi.fn(),
|
||||
};
|
||||
const createProgress = vi.fn(() => progress);
|
||||
const applyPartial = vi.fn(async () => {
|
||||
events.push("apply-settings");
|
||||
});
|
||||
const initialiseInternalFileSync = vi.fn(async () => undefined);
|
||||
const log = vi.fn();
|
||||
const hiddenFileSync = Object.create(HiddenFileSync.prototype) as HiddenFileSync;
|
||||
Object.assign(hiddenFileSync, {
|
||||
core: {
|
||||
services: {
|
||||
setting: { applyPartial },
|
||||
},
|
||||
},
|
||||
initialiseInternalFileSync,
|
||||
_progress: createProgress,
|
||||
_log: log,
|
||||
});
|
||||
|
||||
await hiddenFileSync.configureHiddenFileSync("MERGE");
|
||||
|
||||
expect(createProgress).toHaveBeenCalledWith("[⚙ Initialise]\n", LOG_LEVEL_NOTICE);
|
||||
expect(events[0]).toBe("progress:Preparing Hidden File Sync...");
|
||||
expect(initialiseInternalFileSync).toHaveBeenCalledWith("safe", true, false, progress);
|
||||
expect(log).not.toHaveBeenCalledWith("Gathering files for enabling Hidden File Sync", LOG_LEVEL_NOTICE);
|
||||
expect(log).not.toHaveBeenCalledWith("Done! Restarting the app is strongly recommended!", LOG_LEVEL_NOTICE);
|
||||
expect(log).toHaveBeenCalledWith("Hidden File Sync initialisation completed.", expect.any(Number));
|
||||
});
|
||||
|
||||
it("closes the preparation Notice when enabling Hidden File Sync fails", async () => {
|
||||
vi.mocked(configureHiddenFileSyncMode).mockImplementation(async (_mode, handlers) => {
|
||||
await handlers.enable();
|
||||
return "enabled";
|
||||
});
|
||||
const error = new Error("setting persistence failed");
|
||||
const progress = {
|
||||
log: vi.fn(),
|
||||
once: vi.fn(),
|
||||
done: vi.fn(),
|
||||
};
|
||||
const hiddenFileSync = Object.create(HiddenFileSync.prototype) as HiddenFileSync;
|
||||
Object.assign(hiddenFileSync, {
|
||||
core: {
|
||||
services: {
|
||||
setting: {
|
||||
applyPartial: vi.fn(async () => {
|
||||
throw error;
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
_progress: vi.fn(() => progress),
|
||||
_log: vi.fn(),
|
||||
});
|
||||
|
||||
await expect(hiddenFileSync.configureHiddenFileSync("MERGE")).rejects.toBe(error);
|
||||
|
||||
expect(progress.done).toHaveBeenCalledWith("Failed");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user