mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-10-25 18:43:20 +00:00
(chore): Resolving circular references
This commit is contained in:
14 files changed
+216
-301
No files matched your search
@@ -8,6 +8,7 @@ import {
|
|||||||
diff_match_patch,
|
diff_match_patch,
|
||||||
Platform,
|
Platform,
|
||||||
addIcon,
|
addIcon,
|
||||||
|
type App,
|
||||||
} from "@/deps.ts";
|
} from "@/deps.ts";
|
||||||
import type { EntryDoc } from "@lib/common/models/db.definition";
|
import type { EntryDoc } from "@lib/common/models/db.definition";
|
||||||
import type {
|
import type {
|
||||||
@@ -58,7 +59,7 @@ import { base64ToArrayBuffer, base64ToString } from "octagonal-wheels/binary/bas
|
|||||||
import { ConflictResolveModal } from "@/modules/features/InteractiveConflictResolving/ConflictResolveModal.ts";
|
import { ConflictResolveModal } from "@/modules/features/InteractiveConflictResolving/ConflictResolveModal.ts";
|
||||||
import { Semaphore } from "octagonal-wheels/concurrency/semaphore";
|
import { Semaphore } from "octagonal-wheels/concurrency/semaphore";
|
||||||
import { EVENT_REQUEST_OPEN_PLUGIN_SYNC_DIALOG, eventHub } from "@/common/events.ts";
|
import { EVENT_REQUEST_OPEN_PLUGIN_SYNC_DIALOG, eventHub } from "@/common/events.ts";
|
||||||
import { PluginDialogModal } from "./PluginDialogModal.ts";
|
import type { PluginDialogModal } from "./PluginDialogModal.ts";
|
||||||
import { $msg } from "@/lib/src/common/i18n.ts";
|
import { $msg } from "@/lib/src/common/i18n.ts";
|
||||||
import type { InjectableServiceHub } from "@lib/services/InjectableServices.ts";
|
import type { InjectableServiceHub } from "@lib/services/InjectableServices.ts";
|
||||||
import type { LiveSyncCore } from "@/main.ts";
|
import type { LiveSyncCore } from "@/main.ts";
|
||||||
@@ -384,8 +385,14 @@ export type PluginDataEx = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class ConfigSync extends LiveSyncCommands {
|
export class ConfigSync extends LiveSyncCommands {
|
||||||
constructor(plugin: ObsidianLiveSyncPlugin, core: LiveSyncCore) {
|
pluginDialogClass: new (app: App, plugin: ObsidianLiveSyncPlugin) => PluginDialogModal;
|
||||||
|
constructor(
|
||||||
|
plugin: ObsidianLiveSyncPlugin,
|
||||||
|
core: LiveSyncCore,
|
||||||
|
pluginDialogClass: new (app: App, plugin: ObsidianLiveSyncPlugin) => PluginDialogModal
|
||||||
|
) {
|
||||||
super(plugin, core);
|
super(plugin, core);
|
||||||
|
this.pluginDialogClass = pluginDialogClass;
|
||||||
pluginScanningCount.onChanged((e) => {
|
pluginScanningCount.onChanged((e) => {
|
||||||
const total = e.value;
|
const total = e.value;
|
||||||
pluginIsEnumerating.set(total != 0);
|
pluginIsEnumerating.set(total != 0);
|
||||||
@@ -419,7 +426,7 @@ export class ConfigSync extends LiveSyncCommands {
|
|||||||
if (this.pluginDialog) {
|
if (this.pluginDialog) {
|
||||||
this.pluginDialog.open();
|
this.pluginDialog.open();
|
||||||
} else {
|
} else {
|
||||||
this.pluginDialog = new PluginDialogModal(this.app, this.plugin);
|
this.pluginDialog = new this.pluginDialogClass(this.app, this.plugin);
|
||||||
this.pluginDialog.open();
|
this.pluginDialog.open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { mount, unmount } from "svelte";
|
import { mount, unmount } from "svelte";
|
||||||
import { App, Modal } from "@/deps.ts";
|
import { App, Modal } from "@/deps.ts";
|
||||||
import ObsidianLiveSyncPlugin from "@/main.ts";
|
import type ObsidianLiveSyncPlugin from "@/main.ts";
|
||||||
import PluginPane from "./PluginPane.svelte";
|
import PluginPane from "./PluginPane.svelte";
|
||||||
export class PluginDialogModal extends Modal {
|
export class PluginDialogModal extends Modal {
|
||||||
plugin: ObsidianLiveSyncPlugin;
|
plugin: ObsidianLiveSyncPlugin;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import ObsidianLiveSyncPlugin from "@/main";
|
import type ObsidianLiveSyncPlugin from "@/main";
|
||||||
import {
|
import {
|
||||||
ConfigSync,
|
ConfigSync,
|
||||||
type IPluginDataExDisplay,
|
type IPluginDataExDisplay,
|
||||||
|
|||||||
+1
-1
Submodule src/lib updated: d82503b665...66c9ebaa1e.
+2
-1
@@ -4,6 +4,7 @@ setGetLanguage(getLanguage);
|
|||||||
import { LiveSyncCommands } from "./features/LiveSyncCommands.ts";
|
import { LiveSyncCommands } from "./features/LiveSyncCommands.ts";
|
||||||
import { HiddenFileSync } from "./features/HiddenFileSync/CmdHiddenFileSync.ts";
|
import { HiddenFileSync } from "./features/HiddenFileSync/CmdHiddenFileSync.ts";
|
||||||
import { ConfigSync } from "./features/ConfigSync/CmdConfigSync.ts";
|
import { ConfigSync } from "./features/ConfigSync/CmdConfigSync.ts";
|
||||||
|
import { PluginDialogModal } from "./features/ConfigSync/PluginDialogModal.ts";
|
||||||
import { ModuleDev } from "./modules/extras/ModuleDev.ts";
|
import { ModuleDev } from "./modules/extras/ModuleDev.ts";
|
||||||
|
|
||||||
import { ModuleInteractiveConflictResolver } from "./modules/features/ModuleInteractiveConflictResolver.ts";
|
import { ModuleInteractiveConflictResolver } from "./modules/features/ModuleInteractiveConflictResolver.ts";
|
||||||
@@ -161,7 +162,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
},
|
},
|
||||||
(core) => {
|
(core) => {
|
||||||
const addOns = [
|
const addOns = [
|
||||||
new ConfigSync(this, core),
|
new ConfigSync(this, core, PluginDialogModal),
|
||||||
new HiddenFileSync(this, core),
|
new HiddenFileSync(this, core),
|
||||||
new LocalDatabaseMaintenance(this, core),
|
new LocalDatabaseMaintenance(this, core),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { delay, fireAndForget } from "octagonal-wheels/promises";
|
import { delay } from "octagonal-wheels/promises";
|
||||||
import { __onMissingTranslation } from "@lib/common/i18n";
|
import { __onMissingTranslation } from "@lib/common/i18n";
|
||||||
import { AbstractObsidianModule } from "@/modules/AbstractObsidianModule.ts";
|
import { AbstractObsidianModule } from "@/modules/AbstractObsidianModule.ts";
|
||||||
import { LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
import { LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
||||||
import { eventHub } from "@/common/events";
|
|
||||||
import { enableTestFunction } from "./devUtil/testUtils.ts";
|
import { enableTestFunction } from "./devUtil/testUtils.ts";
|
||||||
import { TestPaneView, VIEW_TYPE_TEST } from "./devUtil/TestPaneView.ts";
|
import { TestPaneView, VIEW_TYPE_TEST } from "./devUtil/TestPaneView.ts";
|
||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
@@ -41,50 +40,6 @@ export class ModuleDev extends AbstractObsidianModule {
|
|||||||
__onMissingTranslation((key) => {
|
__onMissingTranslation((key) => {
|
||||||
void this.onMissingTranslation(key);
|
void this.onMissingTranslation(key);
|
||||||
});
|
});
|
||||||
type STUB = {
|
|
||||||
toc: Set<string>;
|
|
||||||
stub: { [key: string]: { [key: string]: Map<string, Record<string, string>> } };
|
|
||||||
};
|
|
||||||
eventHub.onEvent("document-stub-created", (detail: STUB) => {
|
|
||||||
fireAndForget(async () => {
|
|
||||||
const stub = detail.stub;
|
|
||||||
const toc = detail.toc;
|
|
||||||
|
|
||||||
const stubDocX = Object.entries(stub)
|
|
||||||
.map(([key, value]) => {
|
|
||||||
return [
|
|
||||||
`## ${key}`,
|
|
||||||
Object.entries(value)
|
|
||||||
.map(([key2, value2]) => {
|
|
||||||
return [
|
|
||||||
`### ${key2}`,
|
|
||||||
[...value2.entries()].map(([key3, value3]) => {
|
|
||||||
// return `#### ${key3}` + "\n" + JSON.stringify(value3);
|
|
||||||
const isObsolete = value3["is_obsolete"] ? " (obsolete)" : "";
|
|
||||||
const desc = value3["desc"] ?? "";
|
|
||||||
const key = value3["key"] ? "Setting key: " + value3["key"] + "\n" : "";
|
|
||||||
return `#### ${key3}${isObsolete}\n${key}${desc}\n`;
|
|
||||||
}),
|
|
||||||
].flat();
|
|
||||||
})
|
|
||||||
.flat(),
|
|
||||||
].flat();
|
|
||||||
})
|
|
||||||
.flat();
|
|
||||||
const stubDocMD =
|
|
||||||
`
|
|
||||||
| Icon | Description |
|
|
||||||
| :---: | ----------------------------------------------------------------- |
|
|
||||||
` +
|
|
||||||
[...toc.values()].map((e) => `${e}`).join("\n") +
|
|
||||||
"\n\n" +
|
|
||||||
stubDocX.join("\n");
|
|
||||||
await this.core.storageAccess.writeHiddenFileAuto(
|
|
||||||
this.app.vault.configDir + "/ls-debug/stub-doc.md",
|
|
||||||
stubDocMD
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
enableTestFunction(this.plugin);
|
enableTestFunction(this.plugin);
|
||||||
this.registerView(VIEW_TYPE_TEST, (leaf) => new TestPaneView(leaf, this.plugin, this));
|
this.registerView(VIEW_TYPE_TEST, (leaf) => new TestPaneView(leaf, this.plugin, this));
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { TFile, Modal, App, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "@/deps.ts";
|
import { TFile, Modal, App, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "@/deps.ts";
|
||||||
import { getPathFromTFile, isValidPath } from "@/common/utils.ts";
|
import { getPathFromTFile, isValidPath } from "@/common/utils.ts";
|
||||||
import { decodeBinary, readString } from "@lib/string_and_binary/convert.ts";
|
import { decodeBinary, readString } from "@lib/string_and_binary/convert.ts";
|
||||||
import ObsidianLiveSyncPlugin from "@/main.ts";
|
import type ObsidianLiveSyncPlugin from "@/main.ts";
|
||||||
import type { DocumentID, FilePathWithPrefix, LoadedEntry } from "@lib/common/models/db.type";
|
import type { DocumentID, FilePathWithPrefix, LoadedEntry } from "@lib/common/models/db.type";
|
||||||
import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "@lib/common/logger";
|
import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "@lib/common/logger";
|
||||||
import { Logger } from "@lib/common/logger.ts";
|
import { Logger } from "@lib/common/logger.ts";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import ObsidianLiveSyncPlugin from "@/main.ts";
|
import type ObsidianLiveSyncPlugin from "@/main.ts";
|
||||||
import { onDestroy, onMount } from "svelte";
|
import { onDestroy, onMount } from "svelte";
|
||||||
import type { AnyEntry, FilePathWithPrefix } from "@lib/common/types.ts";
|
import type { AnyEntry, FilePathWithPrefix } from "@lib/common/types.ts";
|
||||||
import { getDocData, isAnyNote, isDocContentSame, readAsBlob } from "@lib/common/utils.ts";
|
import { getDocData, isAnyNote, isDocContentSame, readAsBlob } from "@lib/common/utils.ts";
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
import { unique } from "octagonal-wheels/collection";
|
import { unique } from "octagonal-wheels/collection";
|
||||||
import type { ConfigurationItem } from "@lib/common/models/shared.definition.configNames";
|
import type { ConfigurationItem } from "@lib/common/models/shared.definition.configNames";
|
||||||
import { LEVEL_ADVANCED, LEVEL_POWER_USER, statusDisplay } from "@lib/common/models/shared.definition.configNames";
|
import { LEVEL_ADVANCED, LEVEL_POWER_USER, statusDisplay } from "@lib/common/models/shared.definition.configNames";
|
||||||
import { createStub, type ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
|
import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
|
||||||
import {
|
import {
|
||||||
type AllSettingItemKey,
|
type AllSettingItemKey,
|
||||||
getConfig,
|
getConfig,
|
||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
type AllBooleanItemKey,
|
type AllBooleanItemKey,
|
||||||
} from "./settingConstants.ts";
|
} from "./settingConstants.ts";
|
||||||
import { $msg } from "@lib/common/i18n.ts";
|
import { $msg } from "@lib/common/i18n.ts";
|
||||||
import { findAttrFromParent, wrapMemo, type AutoWireOption, type OnUpdateResult } from "./SettingPane.ts";
|
import { wrapMemo, type AutoWireOption, type OnUpdateResult } from "./SettingPane.ts";
|
||||||
|
|
||||||
export class LiveSyncSetting extends Setting {
|
export class LiveSyncSetting extends Setting {
|
||||||
autoWiredComponent?: TextComponent | ToggleComponent | DropdownComponent | ButtonComponent | TextAreaComponent;
|
autoWiredComponent?: TextComponent | ToggleComponent | DropdownComponent | ButtonComponent | TextAreaComponent;
|
||||||
@@ -42,31 +42,13 @@ export class LiveSyncSetting extends Setting {
|
|||||||
LiveSyncSetting.env.settingComponents.push(this);
|
LiveSyncSetting.env.settingComponents.push(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
_createDocStub(key: string, value: string | DocumentFragment) {
|
|
||||||
DEV: {
|
|
||||||
const paneName = findAttrFromParent(this.settingEl, "data-pane");
|
|
||||||
const panelName = findAttrFromParent(this.settingEl, "data-panel");
|
|
||||||
const itemName =
|
|
||||||
typeof this.nameBuf == "string" ? this.nameBuf : (this.nameBuf.textContent?.toString() ?? "");
|
|
||||||
const strValue = typeof value == "string" ? value : (value.textContent?.toString() ?? "");
|
|
||||||
|
|
||||||
createStub(itemName, key, strValue, panelName, paneName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override setDesc(desc: string | DocumentFragment): this {
|
override setDesc(desc: string | DocumentFragment): this {
|
||||||
this.descBuf = desc;
|
this.descBuf = desc;
|
||||||
DEV: {
|
|
||||||
this._createDocStub("desc", desc);
|
|
||||||
}
|
|
||||||
super.setDesc(desc);
|
super.setDesc(desc);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
override setName(name: string | DocumentFragment): this {
|
override setName(name: string | DocumentFragment): this {
|
||||||
this.nameBuf = name;
|
this.nameBuf = name;
|
||||||
DEV: {
|
|
||||||
this._createDocStub("name", name);
|
|
||||||
}
|
|
||||||
super.setName(name);
|
super.setName(name);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -85,11 +67,6 @@ export class LiveSyncSetting extends Setting {
|
|||||||
if (conf.desc) {
|
if (conf.desc) {
|
||||||
this.setDesc(conf.desc);
|
this.setDesc(conf.desc);
|
||||||
}
|
}
|
||||||
DEV: {
|
|
||||||
this._createDocStub("key", key);
|
|
||||||
if (conf.obsolete) this._createDocStub("is_obsolete", "true");
|
|
||||||
if (conf.level) this._createDocStub("level", conf.level);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.holdValue = opt?.holdValue || this.holdValue;
|
this.holdValue = opt?.holdValue || this.holdValue;
|
||||||
this.selfKey = key;
|
this.selfKey = key;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { versionNumberString2Number } from "@lib/string_and_binary/convert.ts";
|
|||||||
import { Logger } from "@lib/common/logger.ts";
|
import { Logger } from "@lib/common/logger.ts";
|
||||||
import { checkSyncInfo } from "@lib/pouchdb/negotiation.ts";
|
import { checkSyncInfo } from "@lib/pouchdb/negotiation.ts";
|
||||||
import { testCrypt } from "octagonal-wheels/encryption/encryption";
|
import { testCrypt } from "octagonal-wheels/encryption/encryption";
|
||||||
import ObsidianLiveSyncPlugin from "@/main.ts";
|
import type ObsidianLiveSyncPlugin from "@/main.ts";
|
||||||
import { scheduleTask } from "@/common/utils.ts";
|
import { scheduleTask } from "@/common/utils.ts";
|
||||||
import { LiveSyncCouchDBReplicator } from "@lib/replication/couchdb/LiveSyncReplicator.ts";
|
import { LiveSyncCouchDBReplicator } from "@lib/replication/couchdb/LiveSyncReplicator.ts";
|
||||||
import {
|
import {
|
||||||
@@ -32,8 +32,6 @@ import { JournalSyncMinio } from "@lib/replication/journal/objectstore/JournalSy
|
|||||||
import { paneChangeLog } from "./PaneChangeLog.ts";
|
import { paneChangeLog } from "./PaneChangeLog.ts";
|
||||||
import {
|
import {
|
||||||
enableOnly,
|
enableOnly,
|
||||||
findAttrFromParent,
|
|
||||||
getLevelStr,
|
|
||||||
setLevelClass,
|
setLevelClass,
|
||||||
setStyle,
|
setStyle,
|
||||||
visibleOnly,
|
visibleOnly,
|
||||||
@@ -56,27 +54,6 @@ import { panePatches } from "./PanePatches.ts";
|
|||||||
import { paneMaintenance } from "./PaneMaintenance.ts";
|
import { paneMaintenance } from "./PaneMaintenance.ts";
|
||||||
import { compatGlobal } from "@lib/common/coreEnvFunctions.ts";
|
import { compatGlobal } from "@lib/common/coreEnvFunctions.ts";
|
||||||
|
|
||||||
// For creating a document
|
|
||||||
const toc = new Set<string>();
|
|
||||||
const stubs = {} as {
|
|
||||||
[key: string]: { [key: string]: Map<string, Record<string, string>> };
|
|
||||||
};
|
|
||||||
export function createStub(name: string, key: string, value: string, panel: string, pane: string) {
|
|
||||||
DEV: {
|
|
||||||
if (!(pane in stubs)) {
|
|
||||||
stubs[pane] = {};
|
|
||||||
}
|
|
||||||
if (!(panel in stubs[pane])) {
|
|
||||||
stubs[pane][panel] = new Map<string, Record<string, string>>();
|
|
||||||
}
|
|
||||||
const old = stubs[pane][panel].get(name) ?? {};
|
|
||||||
stubs[pane][panel].set(name, { ...old, [key]: value });
|
|
||||||
scheduleTask("update-stub", 100, () => {
|
|
||||||
eventHub.emitEvent("document-stub-created", { toc: toc, stub: stubs });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||||
plugin: ObsidianLiveSyncPlugin;
|
plugin: ObsidianLiveSyncPlugin;
|
||||||
get core() {
|
get core() {
|
||||||
@@ -711,7 +688,6 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
visibleOnly(() => this.isNeedRebuildLocal() || this.isNeedRebuildRemote())
|
visibleOnly(() => this.isNeedRebuildLocal() || this.isNeedRebuildRemote())
|
||||||
);
|
);
|
||||||
|
|
||||||
let paneNo = 0;
|
|
||||||
const addPane = (
|
const addPane = (
|
||||||
parentEl: HTMLElement,
|
parentEl: HTMLElement,
|
||||||
title: string,
|
title: string,
|
||||||
@@ -721,16 +697,6 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
level?: ConfigLevel
|
level?: ConfigLevel
|
||||||
) => {
|
) => {
|
||||||
const el = this.createEl(parentEl, "div", { text: "" });
|
const el = this.createEl(parentEl, "div", { text: "" });
|
||||||
DEV: {
|
|
||||||
const mdTitle = `${paneNo++}. ${title}${getLevelStr(level ?? "")}`;
|
|
||||||
el.setAttribute("data-pane", mdTitle);
|
|
||||||
toc.add(
|
|
||||||
`| ${icon} | [${mdTitle}](#${mdTitle
|
|
||||||
.toLowerCase()
|
|
||||||
.replace(/ /g, "-")
|
|
||||||
.replace(/[^\w\s-]/g, "")}) | `
|
|
||||||
);
|
|
||||||
}
|
|
||||||
setLevelClass(el, level);
|
setLevelClass(el, level);
|
||||||
// TODO: Refactor to use Obsidian's recommended way to create heading.
|
// TODO: Refactor to use Obsidian's recommended way to create heading.
|
||||||
// eslint-disable-next-line obsidianmd/settings-tab/no-manual-html-headings
|
// eslint-disable-next-line obsidianmd/settings-tab/no-manual-html-headings
|
||||||
@@ -765,7 +731,6 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
// });
|
// });
|
||||||
return p;
|
return p;
|
||||||
};
|
};
|
||||||
const panelNoMap = {} as { [key: string]: number };
|
|
||||||
const addPanel = (
|
const addPanel = (
|
||||||
parentEl: HTMLElement,
|
parentEl: HTMLElement,
|
||||||
title: string,
|
title: string,
|
||||||
@@ -774,15 +739,6 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
level?: ConfigLevel
|
level?: ConfigLevel
|
||||||
) => {
|
) => {
|
||||||
const el = this.createEl(parentEl, "div", { text: "" }, callback, func);
|
const el = this.createEl(parentEl, "div", { text: "" }, callback, func);
|
||||||
DEV: {
|
|
||||||
const paneNo = findAttrFromParent(parentEl, "data-pane");
|
|
||||||
if (!(paneNo in panelNoMap)) {
|
|
||||||
panelNoMap[paneNo] = 0;
|
|
||||||
}
|
|
||||||
panelNoMap[paneNo] += 1;
|
|
||||||
const panelNo = panelNoMap[paneNo];
|
|
||||||
el.setAttribute("data-panel", `${panelNo}. ${title}${getLevelStr(level ?? "")}`);
|
|
||||||
}
|
|
||||||
setLevelClass(el, level);
|
setLevelClass(el, level);
|
||||||
this.createEl(el, "h4", { text: title, cls: "sls-setting-panel-title" });
|
this.createEl(el, "h4", { text: title, cls: "sls-setting-panel-title" });
|
||||||
const p = Promise.resolve(el);
|
const p = Promise.resolve(el);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
synchroniseAllFilesBetweenDBandStorage,
|
synchroniseAllFilesBetweenDBandStorage,
|
||||||
type FullScanOptions,
|
type FullScanOptions,
|
||||||
} from "@lib/serviceFeatures/offlineScanner";
|
} from "@lib/serviceFeatures/offlineScanner";
|
||||||
import { adjustSettingToRemoteIfNeeded, processVaultInitialisation } from "./redFlag";
|
import { adjustSettingToRemoteIfNeeded, processVaultInitialisation } from "./redFlag.utils";
|
||||||
|
|
||||||
export const SIMPLE_FETCH_STAGE1_REMOTE_WINS = "Overwrite all with remote files";
|
export const SIMPLE_FETCH_STAGE1_REMOTE_WINS = "Overwrite all with remote files";
|
||||||
export const SIMPLE_FETCH_STAGE1_NEWER_WINS = "Compare time and take newer";
|
export const SIMPLE_FETCH_STAGE1_NEWER_WINS = "Compare time and take newer";
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE } from "octagonal-wheels/common/logger";
|
||||||
import type { NecessaryServices } from "@lib/interfaces/ServiceModule";
|
import type { NecessaryServices } from "@lib/interfaces/ServiceModule";
|
||||||
import { createInstanceLogFunction, type LogFunction } from "@lib/services/lib/logUtils";
|
import { createInstanceLogFunction, type LogFunction } from "@lib/services/lib/logUtils";
|
||||||
import { FlagFilesHumanReadable, FlagFilesOriginal } from "@lib/common/models/redflag.const";
|
import { FlagFilesHumanReadable, FlagFilesOriginal } from "@lib/common/models/redflag.const";
|
||||||
import FetchEverything from "@/modules/features/SetupWizard/dialogs/FetchEverything.svelte";
|
import FetchEverything from "@/modules/features/SetupWizard/dialogs/FetchEverything.svelte";
|
||||||
import RebuildEverything from "@/modules/features/SetupWizard/dialogs/RebuildEverything.svelte";
|
import RebuildEverything from "@/modules/features/SetupWizard/dialogs/RebuildEverything.svelte";
|
||||||
import { extractObject } from "octagonal-wheels/object";
|
import { REMOTE_MINIO } from "@lib/common/models/setting.const";
|
||||||
import { REMOTE_MINIO, REMOTE_P2P } from "@lib/common/models/setting.const";
|
|
||||||
import type { ObsidianLiveSyncSettings } from "@lib/common/models/setting.type";
|
|
||||||
import { TweakValuesShouldMatchedTemplate } from "@lib/common/models/tweak.definition";
|
|
||||||
import type {
|
import type {
|
||||||
FetchEverythingResult,
|
FetchEverythingResult,
|
||||||
RebuildEverythingResult,
|
RebuildEverythingResult,
|
||||||
@@ -15,6 +12,13 @@ import type {
|
|||||||
import { askAndPerformFastSetupOnScheduledFetchAll } from "./redFlag.simpleFetch";
|
import { askAndPerformFastSetupOnScheduledFetchAll } from "./redFlag.simpleFetch";
|
||||||
import { ConnectionStringParser } from "@lib/common/ConnectionString";
|
import { ConnectionStringParser } from "@lib/common/ConnectionString";
|
||||||
import { activateRemoteConfiguration } from "@lib/serviceFeatures/remoteConfig";
|
import { activateRemoteConfiguration } from "@lib/serviceFeatures/remoteConfig";
|
||||||
|
import {
|
||||||
|
isFlagFileExist,
|
||||||
|
deleteFlagFile,
|
||||||
|
adjustSettingToRemoteIfNeeded,
|
||||||
|
processVaultInitialisation,
|
||||||
|
verifyAndUnlockSuspension,
|
||||||
|
} from "./redFlag.utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag file handler interface, similar to target filter pattern.
|
* Flag file handler interface, similar to target filter pattern.
|
||||||
@@ -25,29 +29,6 @@ interface FlagFileHandler {
|
|||||||
handle: () => Promise<boolean>;
|
handle: () => Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isFlagFileExist(host: NecessaryServices<never, "storageAccess">, path: string) {
|
|
||||||
const redFlagExist = await host.serviceModules.storageAccess.isExists(
|
|
||||||
host.serviceModules.storageAccess.normalisePath(path)
|
|
||||||
);
|
|
||||||
if (redFlagExist) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function deleteFlagFile(host: NecessaryServices<never, "storageAccess">, log: LogFunction, path: string) {
|
|
||||||
try {
|
|
||||||
const isFlagged = await host.serviceModules.storageAccess.isExists(
|
|
||||||
host.serviceModules.storageAccess.normalisePath(path)
|
|
||||||
);
|
|
||||||
if (isFlagged) {
|
|
||||||
await host.serviceModules.storageAccess.delete(path, true);
|
|
||||||
}
|
|
||||||
} catch (ex) {
|
|
||||||
log(`Could not delete ${path}`);
|
|
||||||
log(ex, LOG_LEVEL_VERBOSE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const REMOTE_KEEP_CURRENT = "Use active remote";
|
const REMOTE_KEEP_CURRENT = "Use active remote";
|
||||||
const REMOTE_CANCEL = "Cancel";
|
const REMOTE_CANCEL = "Cancel";
|
||||||
async function askAndActivateRemoteDatabase(host: NecessaryServices<"UI" | "setting", never>, log: LogFunction) {
|
async function askAndActivateRemoteDatabase(host: NecessaryServices<"UI" | "setting", never>, log: LogFunction) {
|
||||||
@@ -215,151 +196,6 @@ export function createFetchAllFlagHandler(
|
|||||||
* @param config current configuration to retrieve remote preferred config
|
* @param config current configuration to retrieve remote preferred config
|
||||||
* @returns updated configuration if applied, otherwise null.
|
* @returns updated configuration if applied, otherwise null.
|
||||||
*/
|
*/
|
||||||
export async function adjustSettingToRemote(
|
|
||||||
host: NecessaryServices<"tweakValue" | "UI" | "setting", never>,
|
|
||||||
log: LogFunction,
|
|
||||||
config: ObsidianLiveSyncSettings
|
|
||||||
) {
|
|
||||||
// Fetch remote configuration unless prevented.
|
|
||||||
const SKIP_FETCH = "Skip and proceed";
|
|
||||||
const RETRY_FETCH = "Retry (recommended)";
|
|
||||||
let canProceed = false;
|
|
||||||
do {
|
|
||||||
const remoteTweaks = await host.services.tweakValue.fetchRemotePreferred(config);
|
|
||||||
if (!remoteTweaks) {
|
|
||||||
const choice = await host.services.UI.confirm.askSelectStringDialogue(
|
|
||||||
"Could not fetch configuration from remote. If you are new to the Self-hosted LiveSync, this might be expected. If not, you should check your network or server settings.",
|
|
||||||
[SKIP_FETCH, RETRY_FETCH] as const,
|
|
||||||
{
|
|
||||||
defaultAction: RETRY_FETCH,
|
|
||||||
timeout: 0,
|
|
||||||
title: "Fetch Remote Configuration Failed",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (choice === SKIP_FETCH) {
|
|
||||||
canProceed = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const necessary = extractObject(TweakValuesShouldMatchedTemplate, remoteTweaks);
|
|
||||||
// Check if any necessary tweak value is different from current config.
|
|
||||||
const differentItems = Object.entries(necessary).filter(([key, value]) => {
|
|
||||||
return config[key as keyof ObsidianLiveSyncSettings] !== value;
|
|
||||||
});
|
|
||||||
if (differentItems.length === 0) {
|
|
||||||
log("Remote configuration matches local configuration. No changes applied.", LOG_LEVEL_NOTICE);
|
|
||||||
} else {
|
|
||||||
await host.services.UI.confirm.askSelectStringDialogue(
|
|
||||||
"Your settings differed slightly from the server's. The plug-in has supplemented the incompatible parts with the server settings!",
|
|
||||||
["OK"] as const,
|
|
||||||
{
|
|
||||||
defaultAction: "OK",
|
|
||||||
timeout: 0,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
config = {
|
|
||||||
...config,
|
|
||||||
...Object.fromEntries(differentItems),
|
|
||||||
};
|
|
||||||
await host.services.setting.applyExternalSettings(config, true);
|
|
||||||
log("Remote configuration applied.", LOG_LEVEL_NOTICE);
|
|
||||||
canProceed = true;
|
|
||||||
const updatedConfig = host.services.setting.currentSettings();
|
|
||||||
return updatedConfig;
|
|
||||||
}
|
|
||||||
} while (!canProceed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adjust setting to remote if needed.
|
|
||||||
* @param extra result of dialogues that may contain preventFetchingConfig flag (e.g, from FetchEverything or RebuildEverything)
|
|
||||||
* @param config current configuration to retrieve remote preferred config
|
|
||||||
*/
|
|
||||||
export async function adjustSettingToRemoteIfNeeded(
|
|
||||||
host: NecessaryServices<"tweakValue" | "UI" | "setting", never>,
|
|
||||||
log: LogFunction,
|
|
||||||
extra: { preventFetchingConfig: boolean },
|
|
||||||
config: ObsidianLiveSyncSettings
|
|
||||||
) {
|
|
||||||
if (extra && extra.preventFetchingConfig) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// P2P has no centralised remote configuration; skip to avoid a spurious
|
|
||||||
// "Failed to connect to the remote server" error dialog.
|
|
||||||
if (config.remoteType === REMOTE_P2P) {
|
|
||||||
log("Remote configuration fetch skipped (P2P mode).", LOG_LEVEL_INFO);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remote configuration fetched and applied.
|
|
||||||
if (await adjustSettingToRemote(host, log, config)) {
|
|
||||||
config = host.services.setting.currentSettings();
|
|
||||||
} else {
|
|
||||||
log("Remote configuration not applied.", LOG_LEVEL_NOTICE);
|
|
||||||
}
|
|
||||||
// log(JSON.stringify(config), LOG_LEVEL_VERBOSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process vault initialisation with suspending file watching and sync.
|
|
||||||
* @param proc process to be executed during initialisation, should return true if can be continued, false if app is unable to continue the process.
|
|
||||||
* @param keepSuspending whether to keep suspending file watching after the process.
|
|
||||||
* @returns result of the process, or false if error occurs.
|
|
||||||
*/
|
|
||||||
export async function processVaultInitialisation(
|
|
||||||
host: NecessaryServices<"setting", never>,
|
|
||||||
log: LogFunction,
|
|
||||||
proc: () => Promise<boolean>,
|
|
||||||
keepSuspending = false
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
// Disable batch saving and file watching during initialisation.
|
|
||||||
await host.services.setting.applyPartial({ batchSave: false }, false);
|
|
||||||
await host.services.setting.suspendAllSync();
|
|
||||||
await host.services.setting.suspendExtraSync();
|
|
||||||
await host.services.setting.applyPartial({ suspendFileWatching: true }, true);
|
|
||||||
try {
|
|
||||||
const result = await proc();
|
|
||||||
return result;
|
|
||||||
} catch (ex) {
|
|
||||||
log("Error during vault initialisation process.", LOG_LEVEL_NOTICE);
|
|
||||||
log(ex, LOG_LEVEL_VERBOSE);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (ex) {
|
|
||||||
log("Error during vault initialisation.", LOG_LEVEL_NOTICE);
|
|
||||||
log(ex, LOG_LEVEL_VERBOSE);
|
|
||||||
return false;
|
|
||||||
} finally {
|
|
||||||
if (!keepSuspending) {
|
|
||||||
// Re-enable file watching after initialisation.
|
|
||||||
await host.services.setting.applyPartial({ suspendFileWatching: false }, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function verifyAndUnlockSuspension(
|
|
||||||
host: NecessaryServices<"setting" | "appLifecycle" | "UI", never>,
|
|
||||||
log: LogFunction
|
|
||||||
) {
|
|
||||||
if (!host.services.setting.currentSettings().suspendFileWatching) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
(await host.services.UI.confirm.askYesNoDialog(
|
|
||||||
"Do you want to resume file and database processing, and restart obsidian now?",
|
|
||||||
{ defaultOption: "Yes", timeout: 15 }
|
|
||||||
)) != "yes"
|
|
||||||
) {
|
|
||||||
// TODO: Confirm actually proceed to next process.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
await host.services.setting.applyPartial({ suspendFileWatching: false }, true);
|
|
||||||
host.services.appLifecycle.performRestart();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory function to create a rebuild flag handler.
|
* Factory function to create a rebuild flag handler.
|
||||||
|
|||||||
@@ -6,14 +6,16 @@ import {
|
|||||||
createFetchAllFlagHandler,
|
createFetchAllFlagHandler,
|
||||||
createRebuildFlagHandler,
|
createRebuildFlagHandler,
|
||||||
createSuspendFlagHandler,
|
createSuspendFlagHandler,
|
||||||
|
flagHandlerToEventHandler,
|
||||||
|
} from "./redFlag";
|
||||||
|
import {
|
||||||
isFlagFileExist,
|
isFlagFileExist,
|
||||||
deleteFlagFile,
|
deleteFlagFile,
|
||||||
adjustSettingToRemote,
|
adjustSettingToRemote,
|
||||||
adjustSettingToRemoteIfNeeded,
|
adjustSettingToRemoteIfNeeded,
|
||||||
processVaultInitialisation,
|
processVaultInitialisation,
|
||||||
verifyAndUnlockSuspension,
|
verifyAndUnlockSuspension,
|
||||||
flagHandlerToEventHandler,
|
} from "./redFlag.utils";
|
||||||
} from "./redFlag";
|
|
||||||
import {
|
import {
|
||||||
TweakValuesRecommendedTemplate,
|
TweakValuesRecommendedTemplate,
|
||||||
TweakValuesShouldMatchedTemplate,
|
TweakValuesShouldMatchedTemplate,
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
||||||
|
import type { NecessaryServices } from "@lib/interfaces/ServiceModule";
|
||||||
|
import { type LogFunction } from "@lib/services/lib/logUtils";
|
||||||
|
import { extractObject } from "octagonal-wheels/object";
|
||||||
|
import { REMOTE_P2P } from "@lib/common/models/setting.const";
|
||||||
|
import type { ObsidianLiveSyncSettings } from "@lib/common/models/setting.type";
|
||||||
|
import { TweakValuesShouldMatchedTemplate } from "@lib/common/models/tweak.definition";
|
||||||
|
|
||||||
|
export async function isFlagFileExist(host: NecessaryServices<never, "storageAccess">, path: string) {
|
||||||
|
const redFlagExist = await host.serviceModules.storageAccess.isExists(
|
||||||
|
host.serviceModules.storageAccess.normalisePath(path)
|
||||||
|
);
|
||||||
|
if (redFlagExist) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteFlagFile(host: NecessaryServices<never, "storageAccess">, log: LogFunction, path: string) {
|
||||||
|
try {
|
||||||
|
const isFlagged = await host.serviceModules.storageAccess.isExists(
|
||||||
|
host.serviceModules.storageAccess.normalisePath(path)
|
||||||
|
);
|
||||||
|
if (isFlagged) {
|
||||||
|
await host.serviceModules.storageAccess.delete(path, true);
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
log(`Could not delete ${path}`);
|
||||||
|
log(ex, LOG_LEVEL_VERBOSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjust setting to remote configuration.
|
||||||
|
* @param config current configuration to retrieve remote preferred config
|
||||||
|
* @returns updated configuration if applied, otherwise null.
|
||||||
|
*/
|
||||||
|
export async function adjustSettingToRemote(
|
||||||
|
host: NecessaryServices<"tweakValue" | "UI" | "setting", never>,
|
||||||
|
log: LogFunction,
|
||||||
|
config: ObsidianLiveSyncSettings
|
||||||
|
) {
|
||||||
|
// Fetch remote configuration unless prevented.
|
||||||
|
const SKIP_FETCH = "Skip and proceed";
|
||||||
|
const RETRY_FETCH = "Retry (recommended)";
|
||||||
|
let canProceed = false;
|
||||||
|
do {
|
||||||
|
const remoteTweaks = await host.services.tweakValue.fetchRemotePreferred(config);
|
||||||
|
if (!remoteTweaks) {
|
||||||
|
const choice = await host.services.UI.confirm.askSelectStringDialogue(
|
||||||
|
"Could not fetch configuration from remote. If you are new to the Self-hosted LiveSync, this might be expected. If not, you should check your network or server settings.",
|
||||||
|
[SKIP_FETCH, RETRY_FETCH] as const,
|
||||||
|
{
|
||||||
|
defaultAction: RETRY_FETCH,
|
||||||
|
timeout: 0,
|
||||||
|
title: "Fetch Remote Configuration Failed",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (choice === SKIP_FETCH) {
|
||||||
|
canProceed = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const necessary = extractObject(TweakValuesShouldMatchedTemplate, remoteTweaks);
|
||||||
|
// Check if any necessary tweak value is different from current config.
|
||||||
|
const differentItems = Object.entries(necessary).filter(([key, value]) => {
|
||||||
|
return config[key as keyof ObsidianLiveSyncSettings] !== value;
|
||||||
|
});
|
||||||
|
if (differentItems.length === 0) {
|
||||||
|
log("Remote configuration matches local configuration. No changes applied.", LOG_LEVEL_NOTICE);
|
||||||
|
} else {
|
||||||
|
await host.services.UI.confirm.askSelectStringDialogue(
|
||||||
|
"Your settings differed slightly from the server's. The plug-in has supplemented the incompatible parts with the server settings!",
|
||||||
|
["OK"] as const,
|
||||||
|
{
|
||||||
|
defaultAction: "OK",
|
||||||
|
timeout: 0,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
config = {
|
||||||
|
...config,
|
||||||
|
...Object.fromEntries(differentItems),
|
||||||
|
};
|
||||||
|
await host.services.setting.applyExternalSettings(config, true);
|
||||||
|
log("Remote configuration applied.", LOG_LEVEL_NOTICE);
|
||||||
|
canProceed = true;
|
||||||
|
const updatedConfig = host.services.setting.currentSettings();
|
||||||
|
return updatedConfig;
|
||||||
|
}
|
||||||
|
} while (!canProceed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjust setting to remote if needed.
|
||||||
|
* @param extra result of dialogues that may contain preventFetchingConfig flag (e.g, from FetchEverything or RebuildEverything)
|
||||||
|
* @param config current configuration to retrieve remote preferred config
|
||||||
|
*/
|
||||||
|
export async function adjustSettingToRemoteIfNeeded(
|
||||||
|
host: NecessaryServices<"tweakValue" | "UI" | "setting", never>,
|
||||||
|
log: LogFunction,
|
||||||
|
extra: { preventFetchingConfig: boolean },
|
||||||
|
config: ObsidianLiveSyncSettings
|
||||||
|
) {
|
||||||
|
if (extra && extra.preventFetchingConfig) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// P2P has no centralised remote configuration; skip to avoid a spurious
|
||||||
|
// "Failed to connect to the remote server" error dialog.
|
||||||
|
if (config.remoteType === REMOTE_P2P) {
|
||||||
|
log("Remote configuration fetch skipped (P2P mode).", LOG_LEVEL_INFO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remote configuration fetched and applied.
|
||||||
|
if (await adjustSettingToRemote(host, log, config)) {
|
||||||
|
config = host.services.setting.currentSettings();
|
||||||
|
} else {
|
||||||
|
log("Remote configuration not applied.", LOG_LEVEL_NOTICE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process vault initialisation with suspending file watching and sync.
|
||||||
|
* @param proc process to be executed during initialisation, should return true if can be continued, false if app is unable to continue the process.
|
||||||
|
* @param keepSuspending whether to keep suspending file watching after the process.
|
||||||
|
* @returns result of the process, or false if error occurs.
|
||||||
|
*/
|
||||||
|
export async function processVaultInitialisation(
|
||||||
|
host: NecessaryServices<"setting", never>,
|
||||||
|
log: LogFunction,
|
||||||
|
proc: () => Promise<boolean>,
|
||||||
|
keepSuspending = false
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
// Disable batch saving and file watching during initialisation.
|
||||||
|
await host.services.setting.applyPartial({ batchSave: false }, false);
|
||||||
|
await host.services.setting.suspendAllSync();
|
||||||
|
await host.services.setting.suspendExtraSync();
|
||||||
|
await host.services.setting.applyPartial({ suspendFileWatching: true }, true);
|
||||||
|
try {
|
||||||
|
const result = await proc();
|
||||||
|
return result;
|
||||||
|
} catch (ex) {
|
||||||
|
log("Error during vault initialisation process.", LOG_LEVEL_NOTICE);
|
||||||
|
log(ex, LOG_LEVEL_VERBOSE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
log("Error during vault initialisation.", LOG_LEVEL_NOTICE);
|
||||||
|
log(ex, LOG_LEVEL_VERBOSE);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
if (!keepSuspending) {
|
||||||
|
// Re-enable file watching after initialisation.
|
||||||
|
await host.services.setting.applyPartial({ suspendFileWatching: false }, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function verifyAndUnlockSuspension(
|
||||||
|
host: NecessaryServices<"setting" | "appLifecycle" | "UI", never>,
|
||||||
|
log: LogFunction
|
||||||
|
) {
|
||||||
|
if (!host.services.setting.currentSettings().suspendFileWatching) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(await host.services.UI.confirm.askYesNoDialog(
|
||||||
|
"Do you want to resume file and database processing, and restart obsidian now?",
|
||||||
|
{ defaultOption: "Yes", timeout: 15 }
|
||||||
|
)) != "yes"
|
||||||
|
) {
|
||||||
|
// TODO: Confirm actually proceed to next process.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
await host.services.setting.applyPartial({ suspendFileWatching: false }, true);
|
||||||
|
host.services.appLifecycle.performRestart();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user