mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-04-11 11:28:44 +00:00
11th March, 2026
Now, Self-hosted LiveSync has finally begun to be split into the Self-hosted LiveSync plugin for Obsidian, and a properly abstracted version of it. This may not offer much benefit to Obsidian plugin users, or might even cause a slight inconvenience, but I believe it will certainly help improve testability and make the ecosystem better. However, I do not see the point in putting something with little benefit into beta, so I am handling this on the alpha branch. I would actually preferred to create an R&D branch, but I was not keen on the ampersand, and I feel it will eventually become a proper beta anyway. ### Refactored - Separated `ObsidianLiveSyncPlugin` into `ObsidianLiveSyncPlugin` and `LiveSyncBaseCore`. - Now `LiveSyncCore` indicates the type specified version of `LiveSyncBaseCore`. - Referencing `plugin.xxx` has been rewritten to referencing the corresponding service or `core.xxx`. ### Internal API changes - Storage Access APIs are now yielding Promises. This is to allow more limited storage platforms to be supported. ### R&D - Browser-version of Self-hosted LiveSync is now in development. This is not intended for public use now, but I will eventually make it available for testing. - We can see the code in `src/apps/webapp` for the browser version.
This commit is contained in:
@@ -1,10 +1,16 @@
|
||||
import { LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, Logger } from "octagonal-wheels/common/logger";
|
||||
import type { AnyEntry, FilePathWithPrefix } from "@lib/common/types";
|
||||
import type { LiveSyncCore } from "@/main";
|
||||
import type { IMinimumLiveSyncCommands, LiveSyncBaseCore } from "@/LiveSyncBaseCore";
|
||||
import { stripAllPrefixes } from "@lib/string_and_binary/path";
|
||||
import { createInstanceLogFunction } from "@lib/services/lib/logUtils";
|
||||
import type { ServiceContext } from "@/lib/src/services/base/ServiceBase";
|
||||
|
||||
export abstract class AbstractModule {
|
||||
export abstract class AbstractModule<
|
||||
T extends LiveSyncBaseCore<ServiceContext, IMinimumLiveSyncCommands> = LiveSyncBaseCore<
|
||||
ServiceContext,
|
||||
IMinimumLiveSyncCommands
|
||||
>,
|
||||
> {
|
||||
_log = createInstanceLogFunction(this.constructor.name, this.services.API);
|
||||
get services() {
|
||||
if (!this.core._services) {
|
||||
@@ -36,13 +42,13 @@ export abstract class AbstractModule {
|
||||
return stripAllPrefixes(this.services.path.getPath(entry));
|
||||
}
|
||||
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services) {
|
||||
onBindFunction(core: T, services: typeof core.services) {
|
||||
// Override if needed.
|
||||
}
|
||||
constructor(public core: LiveSyncCore) {
|
||||
constructor(public core: T) {
|
||||
Logger(`[${this.constructor.name}] Loaded`, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
saveSettings = this.core.saveSettings.bind(this.core);
|
||||
saveSettings = this.core.services.setting.saveSettingData.bind(this.core.services.setting);
|
||||
|
||||
addTestResult(key: string, value: boolean, summary?: string, message?: string) {
|
||||
this.services.test.addTestResult(`${this.constructor.name}`, key, value, summary, message);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PeriodicProcessor } from "../../common/utils";
|
||||
import { PeriodicProcessor } from "@/common/PeriodicProcessor";
|
||||
import type { LiveSyncCore } from "../../main";
|
||||
import { AbstractModule } from "../AbstractModule";
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ import { balanceChunkPurgedDBs } from "@lib/pouchdb/chunks";
|
||||
import { purgeUnreferencedChunks } from "@lib/pouchdb/chunks";
|
||||
import { LiveSyncCouchDBReplicator } from "../../lib/src/replication/couchdb/LiveSyncReplicator";
|
||||
import { type EntryDoc, type RemoteType } from "../../lib/src/common/types";
|
||||
import { scheduleTask } from "../../common/utils";
|
||||
|
||||
import { scheduleTask } from "octagonal-wheels/concurrency/task";
|
||||
import { EVENT_FILE_SAVED, EVENT_SETTING_SAVED, eventHub } from "../../common/events";
|
||||
|
||||
import { $msg } from "../../lib/src/common/i18n";
|
||||
|
||||
@@ -8,8 +8,7 @@ import {
|
||||
type MetaEntry,
|
||||
} from "@lib/common/types";
|
||||
import type { ModuleReplicator } from "./ModuleReplicator";
|
||||
import { isChunk, isValidPath } from "@/common/utils";
|
||||
import type { LiveSyncCore } from "@/main";
|
||||
import { isChunk } from "@/lib/src/common/typeUtils";
|
||||
import {
|
||||
LOG_LEVEL_DEBUG,
|
||||
LOG_LEVEL_INFO,
|
||||
@@ -22,6 +21,7 @@ import { fireAndForget, isAnyNote, throttle } from "@lib/common/utils";
|
||||
import { Semaphore } from "octagonal-wheels/concurrency/semaphore_v2";
|
||||
import { serialized } from "octagonal-wheels/concurrency/lock";
|
||||
import type { ReactiveSource } from "octagonal-wheels/dataobject/reactive_v2";
|
||||
import type { LiveSyncBaseCore } from "@/LiveSyncBaseCore";
|
||||
|
||||
const KV_KEY_REPLICATION_RESULT_PROCESSOR_SNAPSHOT = "replicationResultProcessorSnapshot";
|
||||
type ReplicateResultProcessorState = {
|
||||
@@ -54,7 +54,7 @@ export class ReplicateResultProcessor {
|
||||
get services() {
|
||||
return this.replicator.core.services;
|
||||
}
|
||||
get core(): LiveSyncCore {
|
||||
get core(): LiveSyncBaseCore {
|
||||
return this.replicator.core;
|
||||
}
|
||||
|
||||
@@ -414,7 +414,7 @@ export class ReplicateResultProcessor {
|
||||
if (await this.services.replication.processOptionalSynchroniseResult(dbDoc)) {
|
||||
// Already processed
|
||||
this.log(`Processed by other processor: ${docNote}`, LOG_LEVEL_DEBUG);
|
||||
} else if (isValidPath(this.getPath(doc))) {
|
||||
} else if (this.services.vault.isValidPath(this.getPath(doc))) {
|
||||
// Apply to storage if the path is valid
|
||||
await this.applyToStorage(doc as MetaEntry);
|
||||
this.log(`Processed: ${docNote}`, LOG_LEVEL_DEBUG);
|
||||
|
||||
@@ -11,13 +11,9 @@ import {
|
||||
type diff_check_result,
|
||||
type FilePathWithPrefix,
|
||||
} from "../../lib/src/common/types";
|
||||
import {
|
||||
compareMTime,
|
||||
displayRev,
|
||||
isCustomisationSyncMetadata,
|
||||
isPluginMetadata,
|
||||
TARGET_IS_NEW,
|
||||
} from "../../common/utils";
|
||||
import { isCustomisationSyncMetadata, isPluginMetadata } from "@lib/common/typeUtils.ts";
|
||||
import { TARGET_IS_NEW } from "@lib/common/models/shared.const.symbols.ts";
|
||||
import { compareMTime, displayRev } from "@lib/common/utils.ts";
|
||||
import diff_match_patch from "diff-match-patch";
|
||||
import { stripAllPrefixes, isPlainText } from "../../lib/src/string_and_binary/path";
|
||||
import { eventHub } from "../../common/events.ts";
|
||||
@@ -214,7 +210,7 @@ export class ModuleConflictResolver extends AbstractModule {
|
||||
private async _resolveAllConflictedFilesByNewerOnes() {
|
||||
this._log(`Resolving conflicts by newer ones`, LOG_LEVEL_NOTICE);
|
||||
|
||||
const files = this.core.storageAccess.getFileNames();
|
||||
const files = await this.core.storageAccess.getFileNames();
|
||||
|
||||
let i = 0;
|
||||
for (const file of files) {
|
||||
|
||||
86
src/modules/essential/ModuleBasicMenu.ts
Normal file
86
src/modules/essential/ModuleBasicMenu.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import type { LiveSyncCore } from "@/main";
|
||||
import { LOG_LEVEL_NOTICE } from "octagonal-wheels/common/logger";
|
||||
import { fireAndForget } from "octagonal-wheels/promises";
|
||||
import { AbstractModule } from "../AbstractModule";
|
||||
// Separated Module for basic menu commands, which are not related to obsidian specific features. It is expected to be used in other platforms with minimal changes.
|
||||
// However, it is odd that it has here at all; it really ought to be in each respective feature. It will likely be moved eventually. Until now, addCommand pointed to Obsidian's version.
|
||||
export class ModuleBasicMenu extends AbstractModule {
|
||||
_everyOnloadStart(): Promise<boolean> {
|
||||
this.addCommand({
|
||||
id: "livesync-replicate",
|
||||
name: "Replicate now",
|
||||
callback: async () => {
|
||||
await this.services.replication.replicate();
|
||||
},
|
||||
});
|
||||
this.addCommand({
|
||||
id: "livesync-dump",
|
||||
name: "Dump information of this doc ",
|
||||
callback: () => {
|
||||
const file = this.services.vault.getActiveFilePath();
|
||||
if (!file) return;
|
||||
fireAndForget(() => this.localDatabase.getDBEntry(file, {}, true, false));
|
||||
},
|
||||
});
|
||||
this.addCommand({
|
||||
id: "livesync-toggle",
|
||||
name: "Toggle LiveSync",
|
||||
callback: async () => {
|
||||
if (this.settings.liveSync) {
|
||||
this.settings.liveSync = false;
|
||||
this._log("LiveSync Disabled.", LOG_LEVEL_NOTICE);
|
||||
} else {
|
||||
this.settings.liveSync = true;
|
||||
this._log("LiveSync Enabled.", LOG_LEVEL_NOTICE);
|
||||
}
|
||||
await this.services.control.applySettings();
|
||||
await this.services.setting.saveSettingData();
|
||||
},
|
||||
});
|
||||
this.addCommand({
|
||||
id: "livesync-suspendall",
|
||||
name: "Toggle All Sync.",
|
||||
callback: async () => {
|
||||
if (this.services.appLifecycle.isSuspended()) {
|
||||
this.services.appLifecycle.setSuspended(false);
|
||||
this._log("Self-hosted LiveSync resumed", LOG_LEVEL_NOTICE);
|
||||
} else {
|
||||
this.services.appLifecycle.setSuspended(true);
|
||||
this._log("Self-hosted LiveSync suspended", LOG_LEVEL_NOTICE);
|
||||
}
|
||||
await this.services.control.applySettings();
|
||||
await this.services.setting.saveSettingData();
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "livesync-scan-files",
|
||||
name: "Scan storage and database again",
|
||||
callback: async () => {
|
||||
await this.services.vault.scanVault(true);
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "livesync-runbatch",
|
||||
name: "Run pended batch processes",
|
||||
callback: async () => {
|
||||
await this.services.fileProcessing.commitPendingFileEvents();
|
||||
},
|
||||
});
|
||||
|
||||
// TODO, Replicator is possibly one of features. It should be moved to features.
|
||||
this.addCommand({
|
||||
id: "livesync-abortsync",
|
||||
name: "Abort synchronization immediately",
|
||||
callback: () => {
|
||||
this.core.replicator.terminateSync();
|
||||
},
|
||||
});
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
override onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.appLifecycle.onInitialise.addHandler(this._everyOnloadStart.bind(this));
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ export class ModuleInitializerFile extends AbstractModule {
|
||||
await this.collectDeletedFiles();
|
||||
|
||||
this._log("Collecting local files on the storage", LOG_LEVEL_VERBOSE);
|
||||
const filesStorageSrc = this.core.storageAccess.getFiles();
|
||||
const filesStorageSrc = await this.core.storageAccess.getFiles();
|
||||
|
||||
const _filesStorage = [] as typeof filesStorageSrc;
|
||||
|
||||
@@ -300,7 +300,7 @@ export class ModuleInitializerFile extends AbstractModule {
|
||||
throw new Error(`Missing doc:${(file as any).path}`);
|
||||
}
|
||||
if ("path" in file) {
|
||||
const w = this.core.storageAccess.getFileStub((file as any).path);
|
||||
const w = await this.core.storageAccess.getFileStub((file as any).path);
|
||||
if (w) {
|
||||
file = w;
|
||||
} else {
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
eventHub,
|
||||
} from "../../common/events.ts";
|
||||
import { AbstractModule } from "../AbstractModule.ts";
|
||||
import { $msg } from "src/lib/src/common/i18n.ts";
|
||||
import { $msg } from "@lib/common/i18n.ts";
|
||||
import { performDoctorConsultation, RebuildOptions } from "../../lib/src/common/configForDoc.ts";
|
||||
import { isValidPath } from "../../common/utils.ts";
|
||||
import { isMetaEntry } from "../../lib/src/common/types.ts";
|
||||
@@ -40,7 +40,7 @@ export class ModuleMigration extends AbstractModule {
|
||||
);
|
||||
if (isModified) {
|
||||
this.settings = settings;
|
||||
await this.core.saveSettings();
|
||||
await this.saveSettings();
|
||||
}
|
||||
if (!skipRebuild) {
|
||||
if (shouldRebuild) {
|
||||
@@ -231,7 +231,7 @@ export class ModuleMigration extends AbstractModule {
|
||||
if (ret == FIX) {
|
||||
for (const file of recoverable) {
|
||||
// Overwrite the database with the files on the storage
|
||||
const stubFile = this.core.storageAccess.getFileStub(file.path);
|
||||
const stubFile = await this.core.storageAccess.getFileStub(file.path);
|
||||
if (stubFile == null) {
|
||||
Logger(`Could not find stub file for ${file.path}`, LOG_LEVEL_NOTICE);
|
||||
continue;
|
||||
|
||||
@@ -35,13 +35,13 @@ export class ModuleCheckRemoteSize extends AbstractModule {
|
||||
);
|
||||
if (ret == ANSWER_0) {
|
||||
this.settings.notifyThresholdOfRemoteStorageSize = 0;
|
||||
await this.core.saveSettings();
|
||||
await this.saveSettings();
|
||||
} else if (ret == ANSWER_800) {
|
||||
this.settings.notifyThresholdOfRemoteStorageSize = 800;
|
||||
await this.core.saveSettings();
|
||||
await this.saveSettings();
|
||||
} else if (ret == ANSWER_2000) {
|
||||
this.settings.notifyThresholdOfRemoteStorageSize = 2000;
|
||||
await this.core.saveSettings();
|
||||
await this.saveSettings();
|
||||
}
|
||||
}
|
||||
if (this.settings.notifyThresholdOfRemoteStorageSize > 0) {
|
||||
@@ -88,7 +88,8 @@ export class ModuleCheckRemoteSize extends AbstractModule {
|
||||
}),
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
await this.core.saveSettings();
|
||||
// await this.core.saveSettings();
|
||||
await this.core.services.setting.saveSettingData();
|
||||
} else {
|
||||
// Dismiss or Close the dialog
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { fireAndForget } from "octagonal-wheels/promises";
|
||||
import { addIcon, type Editor, type MarkdownFileInfo, type MarkdownView } from "../../deps.ts";
|
||||
import { LOG_LEVEL_NOTICE, type FilePathWithPrefix } from "../../lib/src/common/types.ts";
|
||||
import { $msg } from "src/lib/src/common/i18n.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
import { type Editor, type MarkdownFileInfo, type MarkdownView } from "@/deps.ts";
|
||||
import { addIcon } from "@/deps.ts";
|
||||
import { type FilePathWithPrefix } from "@lib/common/types.ts";
|
||||
import { $msg } from "@lib/common/i18n.ts";
|
||||
import type { LiveSyncCore } from "@/main.ts";
|
||||
import { AbstractModule } from "../AbstractModule.ts";
|
||||
|
||||
// Obsidian specific menu commands.
|
||||
export class ModuleObsidianMenu extends AbstractModule {
|
||||
_everyOnloadStart(): Promise<boolean> {
|
||||
// UI
|
||||
@@ -22,22 +22,6 @@ export class ModuleObsidianMenu extends AbstractModule {
|
||||
await this.services.replication.replicate(true);
|
||||
}).addClass("livesync-ribbon-replicate");
|
||||
|
||||
this.addCommand({
|
||||
id: "livesync-replicate",
|
||||
name: "Replicate now",
|
||||
callback: async () => {
|
||||
await this.services.replication.replicate();
|
||||
},
|
||||
});
|
||||
this.addCommand({
|
||||
id: "livesync-dump",
|
||||
name: "Dump information of this doc ",
|
||||
callback: () => {
|
||||
const file = this.services.vault.getActiveFilePath();
|
||||
if (!file) return;
|
||||
fireAndForget(() => this.localDatabase.getDBEntry(file, {}, true, false));
|
||||
},
|
||||
});
|
||||
this.addCommand({
|
||||
id: "livesync-checkdoc-conflicted",
|
||||
name: "Resolve if conflicted.",
|
||||
@@ -48,61 +32,6 @@ export class ModuleObsidianMenu extends AbstractModule {
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "livesync-toggle",
|
||||
name: "Toggle LiveSync",
|
||||
callback: async () => {
|
||||
if (this.settings.liveSync) {
|
||||
this.settings.liveSync = false;
|
||||
this._log("LiveSync Disabled.", LOG_LEVEL_NOTICE);
|
||||
} else {
|
||||
this.settings.liveSync = true;
|
||||
this._log("LiveSync Enabled.", LOG_LEVEL_NOTICE);
|
||||
}
|
||||
await this.services.control.applySettings();
|
||||
await this.services.setting.saveSettingData();
|
||||
},
|
||||
});
|
||||
this.addCommand({
|
||||
id: "livesync-suspendall",
|
||||
name: "Toggle All Sync.",
|
||||
callback: async () => {
|
||||
if (this.services.appLifecycle.isSuspended()) {
|
||||
this.services.appLifecycle.setSuspended(false);
|
||||
this._log("Self-hosted LiveSync resumed", LOG_LEVEL_NOTICE);
|
||||
} else {
|
||||
this.services.appLifecycle.setSuspended(true);
|
||||
this._log("Self-hosted LiveSync suspended", LOG_LEVEL_NOTICE);
|
||||
}
|
||||
await this.services.control.applySettings();
|
||||
await this.services.setting.saveSettingData();
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "livesync-scan-files",
|
||||
name: "Scan storage and database again",
|
||||
callback: async () => {
|
||||
await this.services.vault.scanVault(true);
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "livesync-runbatch",
|
||||
name: "Run pended batch processes",
|
||||
callback: async () => {
|
||||
await this.services.fileProcessing.commitPendingFileEvents();
|
||||
},
|
||||
});
|
||||
|
||||
// TODO, Replicator is possibly one of features. It should be moved to features.
|
||||
this.addCommand({
|
||||
id: "livesync-abortsync",
|
||||
name: "Abort synchronization immediately",
|
||||
callback: () => {
|
||||
this.core.replicator.terminateSync();
|
||||
},
|
||||
});
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// I intend to discontinue maintenance of this class. It seems preferable to test it externally.
|
||||
import { delay } from "octagonal-wheels/promises";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
||||
@@ -169,7 +170,7 @@ export class ModuleReplicateTest extends AbstractObsidianModule {
|
||||
this._log("No storage access", LOG_LEVEL_INFO);
|
||||
return;
|
||||
}
|
||||
const files = this.core.storageAccess.getFiles();
|
||||
const files = await this.core.storageAccess.getFiles();
|
||||
const out = [] as any[];
|
||||
const webcrypto = await getWebCrypto();
|
||||
for (const file of files) {
|
||||
@@ -205,8 +206,8 @@ export class ModuleReplicateTest extends AbstractObsidianModule {
|
||||
}
|
||||
|
||||
async __dumpFileListIncludeHidden(outFile?: string) {
|
||||
const ignorePatterns = getFileRegExp(this.plugin.settings, "syncInternalFilesIgnorePatterns");
|
||||
const targetPatterns = getFileRegExp(this.plugin.settings, "syncInternalFilesTargetPatterns");
|
||||
const ignorePatterns = getFileRegExp(this.core.settings, "syncInternalFilesIgnorePatterns");
|
||||
const targetPatterns = getFileRegExp(this.core.settings, "syncInternalFilesTargetPatterns");
|
||||
const out = [] as any[];
|
||||
const files = await this.core.storageAccess.getFilesIncludeHidden("", targetPatterns, ignorePatterns);
|
||||
// console.dir(files);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import { writable } from "svelte/store";
|
||||
export let plugin: ObsidianLiveSyncPlugin;
|
||||
export let moduleDev: ModuleDev;
|
||||
$: core = plugin.core;
|
||||
let performanceTestResult = "";
|
||||
let functionCheckResult = "";
|
||||
let testRunning = false;
|
||||
@@ -42,7 +43,7 @@
|
||||
// performTest();
|
||||
|
||||
eventHub.onceEvent(EVENT_LAYOUT_READY, async () => {
|
||||
if (await plugin.storageAccess.isExistsIncludeHidden("_AUTO_TEST.md")) {
|
||||
if (await core.storageAccess.isExistsIncludeHidden("_AUTO_TEST.md")) {
|
||||
new Notice("Auto test file found, running tests...");
|
||||
fireAndForget(async () => {
|
||||
await allTest();
|
||||
@@ -57,14 +58,14 @@
|
||||
function moduleMultiDeviceTest() {
|
||||
if (moduleTesting) return;
|
||||
moduleTesting = true;
|
||||
plugin.services.test.testMultiDevice().finally(() => {
|
||||
core.services.test.testMultiDevice().finally(() => {
|
||||
moduleTesting = false;
|
||||
});
|
||||
}
|
||||
function moduleSingleDeviceTest() {
|
||||
if (moduleTesting) return;
|
||||
moduleTesting = true;
|
||||
plugin.services.test.test().finally(() => {
|
||||
core.services.test.test().finally(() => {
|
||||
moduleTesting = false;
|
||||
});
|
||||
}
|
||||
@@ -72,8 +73,8 @@
|
||||
if (moduleTesting) return;
|
||||
moduleTesting = true;
|
||||
try {
|
||||
await plugin.services.test.test();
|
||||
await plugin.services.test.testMultiDevice();
|
||||
await core.services.test.test();
|
||||
await core.services.test.testMultiDevice();
|
||||
} finally {
|
||||
moduleTesting = false;
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@ export function addDebugFileLog(message: any, stackLog = false) {
|
||||
// const out = "--" + timestamp + "--\n" + messageContent + " " + (stack || "");
|
||||
// const out
|
||||
try {
|
||||
await plugin.storageAccess.appendHiddenFile(
|
||||
plugin.app.vault.configDir + "/ls-debug/" + outFile,
|
||||
await plugin.core.storageAccess.appendHiddenFile(
|
||||
plugin.core.services.API.getSystemConfigDir() + "/ls-debug/" + outFile,
|
||||
JSON.stringify(out) + "\n"
|
||||
);
|
||||
} catch {
|
||||
|
||||
@@ -48,7 +48,7 @@ async function formatPerfResults(items: NamedMeasureResult[]) {
|
||||
}
|
||||
export async function perf_trench(plugin: ObsidianLiveSyncPlugin) {
|
||||
clearResult("trench");
|
||||
const trench = new Trench(plugin.simpleStore);
|
||||
const trench = new Trench(plugin.core.simpleStore);
|
||||
const result = [] as NamedMeasureResult[];
|
||||
result.push(
|
||||
await measure("trench-short-string", async () => {
|
||||
@@ -57,7 +57,7 @@ export async function perf_trench(plugin: ObsidianLiveSyncPlugin) {
|
||||
})
|
||||
);
|
||||
{
|
||||
const testBinary = await plugin.storageAccess.readHiddenFileBinary("testdata/10kb.png");
|
||||
const testBinary = await plugin.core.storageAccess.readHiddenFileBinary("testdata/10kb.png");
|
||||
const uint8Array = new Uint8Array(testBinary);
|
||||
result.push(
|
||||
await measure("trench-binary-10kb", async () => {
|
||||
@@ -67,7 +67,7 @@ export async function perf_trench(plugin: ObsidianLiveSyncPlugin) {
|
||||
);
|
||||
}
|
||||
{
|
||||
const testBinary = await plugin.storageAccess.readHiddenFileBinary("testdata/100kb.jpeg");
|
||||
const testBinary = await plugin.core.storageAccess.readHiddenFileBinary("testdata/100kb.jpeg");
|
||||
const uint8Array = new Uint8Array(testBinary);
|
||||
result.push(
|
||||
await measure("trench-binary-100kb", async () => {
|
||||
@@ -77,7 +77,7 @@ export async function perf_trench(plugin: ObsidianLiveSyncPlugin) {
|
||||
);
|
||||
}
|
||||
{
|
||||
const testBinary = await plugin.storageAccess.readHiddenFileBinary("testdata/1mb.png");
|
||||
const testBinary = await plugin.core.storageAccess.readHiddenFileBinary("testdata/1mb.png");
|
||||
const uint8Array = new Uint8Array(testBinary);
|
||||
result.push(
|
||||
await measure("trench-binary-1mb", async () => {
|
||||
|
||||
@@ -15,6 +15,7 @@ import { isErrorOfMissingDoc } from "../../../lib/src/pouchdb/utils_couchdb.ts";
|
||||
import { fireAndForget, getDocData, readContent } from "../../../lib/src/common/utils.ts";
|
||||
import { isPlainText, stripPrefix } from "../../../lib/src/string_and_binary/path.ts";
|
||||
import { scheduleOnceIfDuplicated } from "octagonal-wheels/concurrency/lock";
|
||||
import type { LiveSyncBaseCore } from "@/LiveSyncBaseCore.ts";
|
||||
|
||||
function isImage(path: string) {
|
||||
const ext = path.split(".").splice(-1)[0].toLowerCase();
|
||||
@@ -46,8 +47,9 @@ function readDocument(w: LoadedEntry) {
|
||||
}
|
||||
export class DocumentHistoryModal extends Modal {
|
||||
plugin: ObsidianLiveSyncPlugin;
|
||||
core: LiveSyncBaseCore;
|
||||
get services() {
|
||||
return this.plugin.services;
|
||||
return this.core.services;
|
||||
}
|
||||
range!: HTMLInputElement;
|
||||
contentView!: HTMLDivElement;
|
||||
@@ -66,6 +68,7 @@ export class DocumentHistoryModal extends Modal {
|
||||
|
||||
constructor(
|
||||
app: App,
|
||||
core: LiveSyncBaseCore,
|
||||
plugin: ObsidianLiveSyncPlugin,
|
||||
file: TFile | FilePathWithPrefix,
|
||||
id?: DocumentID,
|
||||
@@ -73,6 +76,7 @@ export class DocumentHistoryModal extends Modal {
|
||||
) {
|
||||
super(app);
|
||||
this.plugin = plugin;
|
||||
this.core = core;
|
||||
this.file = file instanceof TFile ? getPathFromTFile(file) : file;
|
||||
this.id = id;
|
||||
this.initialRev = revision;
|
||||
@@ -88,7 +92,7 @@ export class DocumentHistoryModal extends Modal {
|
||||
if (!this.id) {
|
||||
this.id = await this.services.path.path2id(this.file);
|
||||
}
|
||||
const db = this.plugin.localDatabase;
|
||||
const db = this.core.localDatabase;
|
||||
try {
|
||||
const w = await db.getRaw(this.id, { revs_info: true });
|
||||
this.revs_info = w._revs_info?.filter((e) => e?.status == "available") ?? [];
|
||||
@@ -137,7 +141,7 @@ export class DocumentHistoryModal extends Modal {
|
||||
}
|
||||
|
||||
async showExactRev(rev: string) {
|
||||
const db = this.plugin.localDatabase;
|
||||
const db = this.core.localDatabase;
|
||||
const w = await db.getDBEntry(this.file, { rev: rev }, false, false, true);
|
||||
this.currentText = "";
|
||||
this.currentDeleted = false;
|
||||
@@ -292,7 +296,7 @@ export class DocumentHistoryModal extends Modal {
|
||||
return;
|
||||
}
|
||||
const d = readContent(this.currentDoc);
|
||||
await this.plugin.storageAccess.writeHiddenFileAuto(pathToWrite, d);
|
||||
await this.core.storageAccess.writeHiddenFileAuto(pathToWrite, d);
|
||||
await focusFile(pathToWrite);
|
||||
this.close();
|
||||
});
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
import { diff_match_patch } from "../../../deps.ts";
|
||||
import { DocumentHistoryModal } from "../DocumentHistory/DocumentHistoryModal.ts";
|
||||
import { isPlainText, stripAllPrefixes } from "../../../lib/src/string_and_binary/path.ts";
|
||||
import type { LiveSyncBaseCore } from "@/LiveSyncBaseCore.ts";
|
||||
export let plugin: ObsidianLiveSyncPlugin;
|
||||
export let core: LiveSyncBaseCore;
|
||||
|
||||
let showDiffInfo = false;
|
||||
let showChunkCorrected = false;
|
||||
@@ -44,12 +46,12 @@
|
||||
let history = [] as HistoryData[];
|
||||
let loading = false;
|
||||
function getPath(entry: AnyEntry): FilePathWithPrefix {
|
||||
return plugin.services.path.getPath(entry);
|
||||
return core.services.path.getPath(entry);
|
||||
}
|
||||
|
||||
async function fetchChanges(): Promise<HistoryData[]> {
|
||||
try {
|
||||
const db = plugin.localDatabase;
|
||||
const db = core.localDatabase;
|
||||
let result = [] as typeof history;
|
||||
for await (const docA of db.findAllNormalDocs()) {
|
||||
if (docA.mtime < range_from_epoch) {
|
||||
@@ -112,11 +114,11 @@
|
||||
}
|
||||
if (rev == docA._rev) {
|
||||
if (checkStorageDiff) {
|
||||
const isExist = await plugin.storageAccess.isExistsIncludeHidden(
|
||||
const isExist = await core.storageAccess.isExistsIncludeHidden(
|
||||
stripAllPrefixes(getPath(docA))
|
||||
);
|
||||
if (isExist) {
|
||||
const data = await plugin.storageAccess.readHiddenFileBinary(
|
||||
const data = await core.storageAccess.readHiddenFileBinary(
|
||||
stripAllPrefixes(getPath(docA))
|
||||
);
|
||||
const d = readAsBlob(doc);
|
||||
@@ -189,7 +191,7 @@
|
||||
onDestroy(() => {});
|
||||
|
||||
function showHistory(file: string, rev: string) {
|
||||
new DocumentHistoryModal(plugin.app, plugin, file as unknown as FilePathWithPrefix, undefined, rev).open();
|
||||
new DocumentHistoryModal(plugin.app, plugin.core, plugin, file as unknown as FilePathWithPrefix, undefined, rev).open();
|
||||
}
|
||||
function openFile(file: string) {
|
||||
plugin.app.workspace.openLinkText(file, file);
|
||||
|
||||
@@ -11,6 +11,7 @@ export class GlobalHistoryView extends SvelteItemView {
|
||||
target: target,
|
||||
props: {
|
||||
plugin: this.plugin,
|
||||
core: this.plugin.core,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -254,8 +254,7 @@ export class ModuleLog extends AbstractObsidianModule {
|
||||
}
|
||||
// Case Sensitivity
|
||||
if (this.services.vault.shouldCheckCaseInsensitively()) {
|
||||
const f = this.core.storageAccess
|
||||
.getFiles()
|
||||
const f = (await this.core.storageAccess.getFiles())
|
||||
.map((e) => e.path)
|
||||
.filter((e) => e.toLowerCase() == thisFile.path.toLowerCase());
|
||||
if (f.length > 1) {
|
||||
@@ -405,8 +404,8 @@ export class ModuleLog extends AbstractObsidianModule {
|
||||
this.logHistory = this.statusDiv.createDiv({ cls: "livesync-status-loghistory" });
|
||||
eventHub.onEvent(EVENT_LAYOUT_READY, () => this.adjustStatusDivPosition());
|
||||
if (this.settings?.showStatusOnStatusbar) {
|
||||
this.statusBar = this.core.addStatusBarItem();
|
||||
this.statusBar.addClass("syncstatusbar");
|
||||
this.statusBar = this.services.API.addStatusBarItem();
|
||||
this.statusBar?.addClass("syncstatusbar");
|
||||
}
|
||||
this.adjustStatusDivPosition();
|
||||
return Promise.resolve(true);
|
||||
|
||||
@@ -34,7 +34,7 @@ export class ModuleObsidianDocumentHistory extends AbstractObsidianModule {
|
||||
}
|
||||
|
||||
showHistory(file: TFile | FilePathWithPrefix, id?: DocumentID) {
|
||||
new DocumentHistoryModal(this.app, this.plugin, file, id).open();
|
||||
new DocumentHistoryModal(this.app, this.core, this.plugin, file, id).open();
|
||||
}
|
||||
|
||||
async fileHistory() {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { ObsidianLiveSyncSettingTab } from "./SettingDialogue/ObsidianLiveSyncSe
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
// import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser";
|
||||
import { EVENT_REQUEST_OPEN_SETTING_WIZARD, EVENT_REQUEST_OPEN_SETTINGS, eventHub } from "../../common/events.ts";
|
||||
import type { LiveSyncCore } from "@/main.ts";
|
||||
|
||||
export class ModuleObsidianSettingDialogue extends AbstractObsidianModule {
|
||||
settingTab!: ObsidianLiveSyncSettingTab;
|
||||
@@ -29,7 +30,7 @@ export class ModuleObsidianSettingDialogue extends AbstractObsidianModule {
|
||||
get appId() {
|
||||
return `${"appId" in this.app ? this.app.appId : ""}`;
|
||||
}
|
||||
override onBindFunction(core: typeof this.plugin, services: typeof core.services): void {
|
||||
override onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.appLifecycle.onInitialise.addHandler(this._everyOnloadStart.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,8 +86,11 @@ export function createStub(name: string, key: string, value: string, panel: stri
|
||||
|
||||
export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
plugin: ObsidianLiveSyncPlugin;
|
||||
get core() {
|
||||
return this.plugin.core;
|
||||
}
|
||||
get services() {
|
||||
return this.plugin.services;
|
||||
return this.core.services;
|
||||
}
|
||||
selectedScreen = "";
|
||||
|
||||
@@ -122,9 +125,9 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
continue;
|
||||
}
|
||||
//@ts-ignore
|
||||
this.plugin.settings[k] = this.editingSettings[k];
|
||||
this.core.settings[k] = this.editingSettings[k];
|
||||
//@ts-ignore
|
||||
this.initialSettings[k] = this.plugin.settings[k];
|
||||
this.initialSettings[k] = this.core.settings[k];
|
||||
}
|
||||
keys.forEach((e) => this.refreshSetting(e));
|
||||
}
|
||||
@@ -164,14 +167,14 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
continue;
|
||||
}
|
||||
//@ts-ignore
|
||||
this.plugin.settings[k] = this.editingSettings[k];
|
||||
this.core.settings[k] = this.editingSettings[k];
|
||||
//@ts-ignore
|
||||
this.initialSettings[k] = this.plugin.settings[k];
|
||||
this.initialSettings[k] = this.core.settings[k];
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
if (hasChanged) {
|
||||
await this.plugin.saveSettings();
|
||||
await this.services.setting.saveSettingData();
|
||||
}
|
||||
|
||||
// if (runOnSaved) {
|
||||
@@ -231,7 +234,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
*/
|
||||
reloadAllSettings(skipUpdate: boolean = false) {
|
||||
const localSetting = this.reloadAllLocalSettings();
|
||||
this._editingSettings = { ...this.plugin.settings, ...localSetting };
|
||||
this._editingSettings = { ...this.core.settings, ...localSetting };
|
||||
this._editingSettings = { ...this.editingSettings, ...this.computeAllLocalSettings() };
|
||||
this.initialSettings = { ...this.editingSettings };
|
||||
if (!skipUpdate) this.requestUpdate();
|
||||
@@ -242,7 +245,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
*/
|
||||
refreshSetting(key: AllSettingItemKey) {
|
||||
const localSetting = this.reloadAllLocalSettings();
|
||||
if (key in this.plugin.settings) {
|
||||
if (key in this.core.settings) {
|
||||
if (key in localSetting) {
|
||||
//@ts-ignore
|
||||
this.initialSettings[key] = localSetting[key];
|
||||
@@ -250,7 +253,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
this.editingSettings[key] = localSetting[key];
|
||||
} else {
|
||||
//@ts-ignore
|
||||
this.initialSettings[key] = this.plugin.settings[key];
|
||||
this.initialSettings[key] = this.core.settings[key];
|
||||
//@ts-ignore
|
||||
this.editingSettings[key] = this.initialSettings[key];
|
||||
}
|
||||
@@ -319,7 +322,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
|
||||
closeSetting() {
|
||||
// @ts-ignore
|
||||
this.plugin.app.setting.close();
|
||||
this.core.app.setting.close();
|
||||
}
|
||||
|
||||
handleElement(element: HTMLElement, func: OnUpdateFunc) {
|
||||
@@ -381,7 +384,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
|
||||
requestReload() {
|
||||
if (this.isShown) {
|
||||
const newConf = this.plugin.settings;
|
||||
const newConf = this.core.settings;
|
||||
const keys = Object.keys(newConf) as (keyof ObsidianLiveSyncSettings)[];
|
||||
let hasLoaded = false;
|
||||
for (const k of keys) {
|
||||
@@ -389,7 +392,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
// Something has changed
|
||||
if (this.isDirty(k as AllSettingItemKey)) {
|
||||
// And modified.
|
||||
this.plugin.confirm.askInPopup(
|
||||
this.core.confirm.askInPopup(
|
||||
`config-reloaded-${k}`,
|
||||
$msg("obsidianLiveSyncSettingTab.msgSettingModified", {
|
||||
setting: getConfName(k as AllSettingItemKey),
|
||||
@@ -457,7 +460,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
this.editingSettings.syncOnStart = false;
|
||||
this.editingSettings.syncOnFileOpen = false;
|
||||
this.editingSettings.syncAfterMerge = false;
|
||||
this.plugin.replicator.closeReplication();
|
||||
this.core.replicator.closeReplication();
|
||||
await this.saveAllDirtySettings();
|
||||
this.containerEl.addClass("isWizard");
|
||||
this.inWizard = true;
|
||||
@@ -514,8 +517,8 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
if (this.isConfiguredAs("syncOnStart", true)) return true;
|
||||
if (this.isConfiguredAs("syncAfterMerge", true)) return true;
|
||||
if (this.isConfiguredAs("syncOnFileOpen", true)) return true;
|
||||
if (this.plugin?.replicator?.syncStatus == "CONNECTED") return true;
|
||||
if (this.plugin?.replicator?.syncStatus == "PAUSED") return true;
|
||||
if (this.core?.replicator?.syncStatus == "CONNECTED") return true;
|
||||
if (this.core?.replicator?.syncStatus == "PAUSED") return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -605,7 +608,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
await this.saveAllDirtySettings();
|
||||
this.closeSetting();
|
||||
await delay(2000);
|
||||
await this.plugin.rebuilder.$performRebuildDB(method);
|
||||
await this.core.rebuilder.$performRebuildDB(method);
|
||||
};
|
||||
async confirmRebuild() {
|
||||
if (!(await this.isPassphraseValid())) {
|
||||
@@ -633,7 +636,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
if (result == OPTION_FETCH) {
|
||||
if (!(await this.checkWorkingPassphrase())) {
|
||||
if (
|
||||
(await this.plugin.confirm.askYesNoDialog($msg("obsidianLiveSyncSettingTab.msgAreYouSureProceed"), {
|
||||
(await this.core.confirm.askYesNoDialog($msg("obsidianLiveSyncSettingTab.msgAreYouSureProceed"), {
|
||||
defaultOption: "No",
|
||||
})) != "yes"
|
||||
)
|
||||
@@ -646,16 +649,16 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
await this.saveAllDirtySettings();
|
||||
await this.applyAllSettings();
|
||||
if (result == OPTION_FETCH) {
|
||||
await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG3_HR, "");
|
||||
await this.core.storageAccess.writeFileAuto(FLAGMD_REDFLAG3_HR, "");
|
||||
this.services.appLifecycle.scheduleRestart();
|
||||
this.closeSetting();
|
||||
// await rebuildDB("localOnly");
|
||||
} else if (result == OPTION_REBUILD_BOTH) {
|
||||
await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG2_HR, "");
|
||||
await this.core.storageAccess.writeFileAuto(FLAGMD_REDFLAG2_HR, "");
|
||||
this.services.appLifecycle.scheduleRestart();
|
||||
this.closeSetting();
|
||||
} else if (result == OPTION_ONLY_SETTING) {
|
||||
await this.plugin.saveSettings();
|
||||
await this.services.setting.saveSettingData();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -868,7 +871,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
}
|
||||
|
||||
getMinioJournalSyncClient() {
|
||||
return new JournalSyncMinio(this.plugin.settings, this.plugin.simpleStore, this.plugin);
|
||||
return new JournalSyncMinio(this.core.settings, this.core.simpleStore, this.core);
|
||||
}
|
||||
async resetRemoteBucket() {
|
||||
const minioJournal = this.getMinioJournalSyncClient();
|
||||
|
||||
@@ -165,7 +165,7 @@ export function paneHatch(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement,
|
||||
}
|
||||
const obsidianInfo = {
|
||||
navigator: navigator.userAgent,
|
||||
fileSystem: this.plugin.services.vault.isStorageInsensitive() ? "insensitive" : "sensitive",
|
||||
fileSystem: this.core.services.vault.isStorageInsensitive() ? "insensitive" : "sensitive",
|
||||
};
|
||||
const msgConfig = `# ---- Obsidian info ----
|
||||
${stringifyYaml(obsidianInfo)}
|
||||
@@ -221,7 +221,7 @@ ${stringifyYaml({
|
||||
|
||||
void addPanel(paneEl, "Recovery and Repair").then((paneEl) => {
|
||||
const addResult = async (path: string, file: FilePathWithPrefix | false, fileOnDB: LoadedEntry | false) => {
|
||||
const storageFileStat = file ? await this.plugin.storageAccess.statHidden(file) : null;
|
||||
const storageFileStat = file ? await this.core.storageAccess.statHidden(file) : null;
|
||||
resultArea.appendChild(
|
||||
this.createEl(resultArea, "div", {}, (el) => {
|
||||
el.appendChild(this.createEl(el, "h6", { text: path }));
|
||||
@@ -256,7 +256,7 @@ ${stringifyYaml({
|
||||
this.createEl(el, "button", { text: "Storage -> Database" }, (buttonEl) => {
|
||||
buttonEl.onClickEvent(async () => {
|
||||
if (file.startsWith(".")) {
|
||||
const addOn = this.plugin.getAddOn<HiddenFileSync>(HiddenFileSync.name);
|
||||
const addOn = this.core.getAddOn<HiddenFileSync>(HiddenFileSync.name);
|
||||
if (addOn) {
|
||||
const file = (await addOn.scanInternalFiles()).find((e) => e.path == path);
|
||||
if (!file) {
|
||||
@@ -275,7 +275,7 @@ ${stringifyYaml({
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!(await this.plugin.fileHandler.storeFileToDB(file as FilePath, true))) {
|
||||
if (!(await this.core.fileHandler.storeFileToDB(file as FilePath, true))) {
|
||||
Logger(
|
||||
`Failed to store the file to the database: ${file}`,
|
||||
LOG_LEVEL_NOTICE
|
||||
@@ -293,7 +293,7 @@ ${stringifyYaml({
|
||||
this.createEl(el, "button", { text: "Database -> Storage" }, (buttonEl) => {
|
||||
buttonEl.onClickEvent(async () => {
|
||||
if (fileOnDB.path.startsWith(ICHeader)) {
|
||||
const addOn = this.plugin.getAddOn<HiddenFileSync>(HiddenFileSync.name);
|
||||
const addOn = this.core.getAddOn<HiddenFileSync>(HiddenFileSync.name);
|
||||
if (addOn) {
|
||||
if (
|
||||
!(await addOn.extractInternalFileFromDatabase(path as FilePath, true))
|
||||
@@ -307,7 +307,7 @@ ${stringifyYaml({
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
!(await this.plugin.fileHandler.dbToStorage(
|
||||
!(await this.core.fileHandler.dbToStorage(
|
||||
fileOnDB as MetaEntry,
|
||||
null,
|
||||
true
|
||||
@@ -332,7 +332,7 @@ ${stringifyYaml({
|
||||
|
||||
const checkBetweenStorageAndDatabase = async (file: FilePathWithPrefix, fileOnDB: LoadedEntry) => {
|
||||
const dataContent = readAsBlob(fileOnDB);
|
||||
const content = createBlob(await this.plugin.storageAccess.readHiddenFileBinary(file));
|
||||
const content = createBlob(await this.core.storageAccess.readHiddenFileBinary(file));
|
||||
if (await isDocContentSame(content, dataContent)) {
|
||||
Logger(`Compare: SAME: ${file}`);
|
||||
} else {
|
||||
@@ -348,7 +348,7 @@ ${stringifyYaml({
|
||||
.setButtonText("Recreate all")
|
||||
.setCta()
|
||||
.onClick(async () => {
|
||||
await this.plugin.fileHandler.createAllChunks(true);
|
||||
await this.core.fileHandler.createAllChunks(true);
|
||||
})
|
||||
);
|
||||
new Setting(paneEl)
|
||||
@@ -377,21 +377,21 @@ ${stringifyYaml({
|
||||
.setCta()
|
||||
.onClick(async () => {
|
||||
Logger("Start verifying all files", LOG_LEVEL_NOTICE, "verify");
|
||||
const ignorePatterns = getFileRegExp(this.plugin.settings, "syncInternalFilesIgnorePatterns");
|
||||
const targetPatterns = getFileRegExp(this.plugin.settings, "syncInternalFilesTargetPatterns");
|
||||
this.plugin.localDatabase.clearCaches();
|
||||
const ignorePatterns = getFileRegExp(this.core.settings, "syncInternalFilesIgnorePatterns");
|
||||
const targetPatterns = getFileRegExp(this.core.settings, "syncInternalFilesTargetPatterns");
|
||||
this.core.localDatabase.clearCaches();
|
||||
Logger("Start verifying all files", LOG_LEVEL_NOTICE, "verify");
|
||||
const files = this.plugin.settings.syncInternalFiles
|
||||
? await this.plugin.storageAccess.getFilesIncludeHidden("/", targetPatterns, ignorePatterns)
|
||||
: await this.plugin.storageAccess.getFileNames();
|
||||
const files = this.core.settings.syncInternalFiles
|
||||
? await this.core.storageAccess.getFilesIncludeHidden("/", targetPatterns, ignorePatterns)
|
||||
: await this.core.storageAccess.getFileNames();
|
||||
const documents = [] as FilePath[];
|
||||
|
||||
const adn = this.plugin.localDatabase.findAllDocs();
|
||||
const adn = this.core.localDatabase.findAllDocs();
|
||||
for await (const i of adn) {
|
||||
const path = this.services.path.getPath(i);
|
||||
if (path.startsWith(ICXHeader)) continue;
|
||||
if (path.startsWith(PSCHeader)) continue;
|
||||
if (!this.plugin.settings.syncInternalFiles && path.startsWith(ICHeader)) continue;
|
||||
if (!this.core.settings.syncInternalFiles && path.startsWith(ICHeader)) continue;
|
||||
documents.push(stripAllPrefixes(path));
|
||||
}
|
||||
const allPaths = [...new Set([...documents, ...files])];
|
||||
@@ -411,8 +411,8 @@ ${stringifyYaml({
|
||||
if (shouldBeIgnored(path)) {
|
||||
return incProc();
|
||||
}
|
||||
const stat = (await this.plugin.storageAccess.isExistsIncludeHidden(path))
|
||||
? await this.plugin.storageAccess.statHidden(path)
|
||||
const stat = (await this.core.storageAccess.isExistsIncludeHidden(path))
|
||||
? await this.core.storageAccess.statHidden(path)
|
||||
: false;
|
||||
const fileOnStorage = stat != null ? stat : false;
|
||||
if (!(await this.services.vault.isTargetFile(path))) return incProc();
|
||||
@@ -422,7 +422,7 @@ ${stringifyYaml({
|
||||
try {
|
||||
const isHiddenFile = path.startsWith(".");
|
||||
const dbPath = isHiddenFile ? addPrefix(path, ICHeader) : path;
|
||||
const fileOnDB = await this.plugin.localDatabase.getDBEntry(dbPath);
|
||||
const fileOnDB = await this.core.localDatabase.getDBEntry(dbPath);
|
||||
if (fileOnDB && this.services.vault.isFileSizeTooLarge(fileOnDB.size))
|
||||
return incProc();
|
||||
|
||||
@@ -466,10 +466,10 @@ ${stringifyYaml({
|
||||
.setDisabled(false)
|
||||
.setWarning()
|
||||
.onClick(async () => {
|
||||
for await (const docName of this.plugin.localDatabase.findAllDocNames()) {
|
||||
for await (const docName of this.core.localDatabase.findAllDocNames()) {
|
||||
if (!docName.startsWith("f:")) {
|
||||
const idEncoded = await this.services.path.path2id(docName as FilePathWithPrefix);
|
||||
const doc = await this.plugin.localDatabase.getRaw(docName as DocumentID);
|
||||
const doc = await this.core.localDatabase.getRaw(docName as DocumentID);
|
||||
if (!doc) continue;
|
||||
if (doc.type != "newnote" && doc.type != "plain") {
|
||||
continue;
|
||||
@@ -482,7 +482,7 @@ ${stringifyYaml({
|
||||
// @ts-ignore
|
||||
delete newDoc._rev;
|
||||
try {
|
||||
const obfuscatedDoc = await this.plugin.localDatabase.getRaw(idEncoded, {
|
||||
const obfuscatedDoc = await this.core.localDatabase.getRaw(idEncoded, {
|
||||
revs_info: true,
|
||||
});
|
||||
// Unfortunately we have to delete one of them.
|
||||
@@ -499,14 +499,14 @@ ${stringifyYaml({
|
||||
-32
|
||||
);
|
||||
}
|
||||
const ret = await this.plugin.localDatabase.putRaw(newDoc, { force: true });
|
||||
const ret = await this.core.localDatabase.putRaw(newDoc, { force: true });
|
||||
if (ret.ok) {
|
||||
Logger(
|
||||
`${docName} has been converted as conflicted document`,
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
doc._deleted = true;
|
||||
if ((await this.plugin.localDatabase.putRaw(doc)).ok) {
|
||||
if ((await this.core.localDatabase.putRaw(doc)).ok) {
|
||||
Logger(`Old ${docName} has been deleted`, LOG_LEVEL_NOTICE);
|
||||
}
|
||||
await this.services.conflict.queueCheckForIfOpen(docName as FilePathWithPrefix);
|
||||
@@ -517,10 +517,10 @@ ${stringifyYaml({
|
||||
} catch (ex: any) {
|
||||
if (ex?.status == 404) {
|
||||
// We can perform this safely
|
||||
if ((await this.plugin.localDatabase.putRaw(newDoc)).ok) {
|
||||
if ((await this.core.localDatabase.putRaw(newDoc)).ok) {
|
||||
Logger(`${docName} has been converted`, LOG_LEVEL_NOTICE);
|
||||
doc._deleted = true;
|
||||
if ((await this.plugin.localDatabase.putRaw(doc)).ok) {
|
||||
if ((await this.core.localDatabase.putRaw(doc)).ok) {
|
||||
Logger(`Old ${docName} has been deleted`, LOG_LEVEL_NOTICE);
|
||||
}
|
||||
}
|
||||
@@ -555,7 +555,7 @@ ${stringifyYaml({
|
||||
.setWarning()
|
||||
.onClick(async () => {
|
||||
Logger(`Deleting customization sync data`, LOG_LEVEL_NOTICE);
|
||||
const entriesToDelete = await this.plugin.localDatabase.allDocsRaw({
|
||||
const entriesToDelete = await this.core.localDatabase.allDocsRaw({
|
||||
startkey: "ix:",
|
||||
endkey: "ix:\u{10ffff}",
|
||||
include_docs: true,
|
||||
@@ -564,7 +564,7 @@ ${stringifyYaml({
|
||||
...e.doc,
|
||||
_deleted: true,
|
||||
}));
|
||||
const r = await this.plugin.localDatabase.bulkDocsRaw(newData as any[]);
|
||||
const r = await this.core.localDatabase.bulkDocsRaw(newData as any[]);
|
||||
// Do not care about the result.
|
||||
Logger(
|
||||
`${r.length} items have been removed, to confirm how many items are left, please perform it again.`,
|
||||
|
||||
@@ -11,8 +11,8 @@ export function paneMaintenance(
|
||||
paneEl: HTMLElement,
|
||||
{ addPanel }: PageFunctions
|
||||
): void {
|
||||
const isRemoteLockedAndDeviceNotAccepted = () => this.plugin?.replicator?.remoteLockedAndDeviceNotAccepted;
|
||||
const isRemoteLocked = () => this.plugin?.replicator?.remoteLocked;
|
||||
const isRemoteLockedAndDeviceNotAccepted = () => this.core?.replicator?.remoteLockedAndDeviceNotAccepted;
|
||||
const isRemoteLocked = () => this.core?.replicator?.remoteLocked;
|
||||
// if (this.plugin?.replicator?.remoteLockedAndDeviceNotAccepted) {
|
||||
this.createEl(
|
||||
paneEl,
|
||||
@@ -92,7 +92,7 @@ export function paneMaintenance(
|
||||
.setDisabled(false)
|
||||
.setWarning()
|
||||
.onClick(async () => {
|
||||
await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG, "");
|
||||
await this.core.storageAccess.writeFileAuto(FLAGMD_REDFLAG, "");
|
||||
this.services.appLifecycle.performRestart();
|
||||
})
|
||||
);
|
||||
@@ -108,7 +108,7 @@ export function paneMaintenance(
|
||||
.setCta()
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
await this.plugin.storageAccess.writeFileAuto(FlagFilesHumanReadable.FETCH_ALL, "");
|
||||
await this.core.storageAccess.writeFileAuto(FlagFilesHumanReadable.FETCH_ALL, "");
|
||||
this.services.appLifecycle.performRestart();
|
||||
})
|
||||
);
|
||||
@@ -121,7 +121,7 @@ export function paneMaintenance(
|
||||
.setCta()
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
await this.plugin.storageAccess.writeFileAuto(FlagFilesHumanReadable.REBUILD_ALL, "");
|
||||
await this.core.storageAccess.writeFileAuto(FlagFilesHumanReadable.REBUILD_ALL, "");
|
||||
this.services.appLifecycle.performRestart();
|
||||
})
|
||||
);
|
||||
@@ -137,8 +137,8 @@ export function paneMaintenance(
|
||||
.setWarning()
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
if (this.plugin.replicator instanceof LiveSyncCouchDBReplicator) {
|
||||
await this.plugin.replicator.sendChunks(this.plugin.settings, undefined, true, 0);
|
||||
if (this.core.replicator instanceof LiveSyncCouchDBReplicator) {
|
||||
await this.core.replicator.sendChunks(this.core.settings, undefined, true, 0);
|
||||
}
|
||||
})
|
||||
)
|
||||
@@ -299,7 +299,7 @@ export function paneMaintenance(
|
||||
.setButtonText("Perform")
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
const replicator = this.plugin.replicator as LiveSyncCouchDBReplicator;
|
||||
const replicator = this.core.replicator as LiveSyncCouchDBReplicator;
|
||||
Logger(`Cleanup has been began`, LOG_LEVEL_NOTICE, "compaction");
|
||||
if (await replicator.compactRemote(this.editingSettings)) {
|
||||
Logger(`Cleanup has been completed!`, LOG_LEVEL_NOTICE, "compaction");
|
||||
|
||||
@@ -31,7 +31,7 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
|
||||
|
||||
void addPanel(paneEl, "Compatibility (Database structure)").then((paneEl) => {
|
||||
const migrateAllToIndexedDB = async () => {
|
||||
const dbToName = this.plugin.localDatabase.dbname + SuffixDatabaseName + ExtraSuffixIndexedDB;
|
||||
const dbToName = this.core.localDatabase.dbname + SuffixDatabaseName + ExtraSuffixIndexedDB;
|
||||
const options = {
|
||||
adapter: "indexeddb",
|
||||
//@ts-ignore :missing def
|
||||
@@ -42,18 +42,19 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
|
||||
const openTo = () => {
|
||||
return new PouchDB(dbToName, options);
|
||||
};
|
||||
if (await migrateDatabases("to IndexedDB", this.plugin.localDatabase.localDatabase, openTo)) {
|
||||
if (await migrateDatabases("to IndexedDB", this.core.localDatabase.localDatabase, openTo)) {
|
||||
Logger(
|
||||
"Migration to IndexedDB completed. Obsidian will be restarted with new configuration immediately.",
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
this.plugin.settings.useIndexedDBAdapter = true;
|
||||
await this.services.setting.saveSettingData();
|
||||
// this.plugin.settings.useIndexedDBAdapter = true;
|
||||
// await this.services.setting.saveSettingData();
|
||||
await this.core.services.setting.applyPartial({ useIndexedDBAdapter: true }, true);
|
||||
this.services.appLifecycle.performRestart();
|
||||
}
|
||||
};
|
||||
const migrateAllToIDB = async () => {
|
||||
const dbToName = this.plugin.localDatabase.dbname + SuffixDatabaseName;
|
||||
const dbToName = this.core.localDatabase.dbname + SuffixDatabaseName;
|
||||
const options = {
|
||||
adapter: "idb",
|
||||
auto_compaction: false,
|
||||
@@ -62,13 +63,14 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
|
||||
const openTo = () => {
|
||||
return new PouchDB(dbToName, options);
|
||||
};
|
||||
if (await migrateDatabases("to IDB", this.plugin.localDatabase.localDatabase, openTo)) {
|
||||
if (await migrateDatabases("to IDB", this.core.localDatabase.localDatabase, openTo)) {
|
||||
Logger(
|
||||
"Migration to IDB completed. Obsidian will be restarted with new configuration immediately.",
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
this.plugin.settings.useIndexedDBAdapter = false;
|
||||
await this.services.setting.saveSettingData();
|
||||
await this.core.services.setting.applyPartial({ useIndexedDBAdapter: false }, true);
|
||||
// this.core.settings.useIndexedDBAdapter = false;
|
||||
// await this.services.setting.saveSettingData();
|
||||
this.services.appLifecycle.performRestart();
|
||||
}
|
||||
};
|
||||
@@ -151,7 +153,7 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
|
||||
} as Record<HashAlgorithm, string>,
|
||||
});
|
||||
this.addOnSaved("hashAlg", async () => {
|
||||
await this.plugin.localDatabase._prepareHashFunctions();
|
||||
await this.core.localDatabase._prepareHashFunctions();
|
||||
});
|
||||
});
|
||||
void addPanel(paneEl, "Edge case addressing (Behaviour)").then((paneEl) => {
|
||||
@@ -215,7 +217,7 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
|
||||
|
||||
this.addOnSaved("maxMTimeForReflectEvents", async (key) => {
|
||||
const buttons = ["Restart Now", "Later"] as const;
|
||||
const reboot = await this.plugin.confirm.askSelectStringDialogue(
|
||||
const reboot = await this.core.confirm.askSelectStringDialogue(
|
||||
"Restarting Obsidian is strongly recommended. Until restart, some changes may not take effect, and display may be inconsistent. Are you sure to restart now?",
|
||||
buttons,
|
||||
{
|
||||
|
||||
@@ -68,7 +68,7 @@ export function paneRemoteConfig(
|
||||
.addButton((button) =>
|
||||
button
|
||||
.onClick(async () => {
|
||||
const setupManager = this.plugin.getModule(SetupManager);
|
||||
const setupManager = this.core.getModule(SetupManager);
|
||||
const originalSettings = getSettingsFromEditingSettings(this.editingSettings);
|
||||
await setupManager.onlyE2EEConfiguration(UserMode.Update, originalSettings);
|
||||
updateE2EESummary();
|
||||
@@ -79,7 +79,7 @@ export function paneRemoteConfig(
|
||||
.addButton((button) =>
|
||||
button
|
||||
.onClick(async () => {
|
||||
const setupManager = this.plugin.getModule(SetupManager);
|
||||
const setupManager = this.core.getModule(SetupManager);
|
||||
const originalSettings = getSettingsFromEditingSettings(this.editingSettings);
|
||||
await setupManager.onConfigureManually(originalSettings, UserMode.Update);
|
||||
updateE2EESummary();
|
||||
@@ -101,7 +101,7 @@ export function paneRemoteConfig(
|
||||
.setButtonText("Change Remote and Setup")
|
||||
.setCta()
|
||||
.onClick(async () => {
|
||||
const setupManager = this.plugin.getModule(SetupManager);
|
||||
const setupManager = this.core.getModule(SetupManager);
|
||||
const originalSettings = getSettingsFromEditingSettings(this.editingSettings);
|
||||
await setupManager.onSelectServer(originalSettings, UserMode.Update);
|
||||
})
|
||||
@@ -127,7 +127,7 @@ export function paneRemoteConfig(
|
||||
.setButtonText("Configure")
|
||||
.setCta()
|
||||
.onClick(async () => {
|
||||
const setupManager = this.plugin.getModule(SetupManager);
|
||||
const setupManager = this.core.getModule(SetupManager);
|
||||
const originalSettings = getSettingsFromEditingSettings(this.editingSettings);
|
||||
await setupManager.onCouchDBManualSetup(
|
||||
UserMode.Update,
|
||||
@@ -162,7 +162,7 @@ export function paneRemoteConfig(
|
||||
.setButtonText("Configure")
|
||||
.setCta()
|
||||
.onClick(async () => {
|
||||
const setupManager = this.plugin.getModule(SetupManager);
|
||||
const setupManager = this.core.getModule(SetupManager);
|
||||
const originalSettings = getSettingsFromEditingSettings(this.editingSettings);
|
||||
await setupManager.onBucketManualSetup(
|
||||
UserMode.Update,
|
||||
@@ -202,7 +202,7 @@ export function paneRemoteConfig(
|
||||
.setButtonText("Configure")
|
||||
.setCta()
|
||||
.onClick(async () => {
|
||||
const setupManager = this.plugin.getModule(SetupManager);
|
||||
const setupManager = this.core.getModule(SetupManager);
|
||||
const originalSettings = getSettingsFromEditingSettings(this.editingSettings);
|
||||
await setupManager.onP2PManualSetup(
|
||||
UserMode.Update,
|
||||
|
||||
@@ -35,7 +35,7 @@ export function paneSetup(
|
||||
.setDesc($msg("Rerun the onboarding wizard to set up Self-hosted LiveSync again."))
|
||||
.addButton((text) => {
|
||||
text.setButtonText($msg("Rerun Wizard")).onClick(async () => {
|
||||
const setupManager = this.plugin.getModule(SetupManager);
|
||||
const setupManager = this.core.getModule(SetupManager);
|
||||
await setupManager.onOnboard(UserMode.ExistingUser);
|
||||
// await this.plugin.moduleSetupObsidian.onBoardingWizard(true);
|
||||
});
|
||||
@@ -86,14 +86,14 @@ export function paneSetup(
|
||||
text.setButtonText($msg("obsidianLiveSyncSettingTab.btnDiscard"))
|
||||
.onClick(async () => {
|
||||
if (
|
||||
(await this.plugin.confirm.askYesNoDialog(
|
||||
(await this.core.confirm.askYesNoDialog(
|
||||
$msg("obsidianLiveSyncSettingTab.msgDiscardConfirmation"),
|
||||
{ defaultOption: "No" }
|
||||
)) == "yes"
|
||||
) {
|
||||
this.editingSettings = { ...this.editingSettings, ...DEFAULT_SETTINGS };
|
||||
await this.saveAllDirtySettings();
|
||||
this.plugin.settings = { ...DEFAULT_SETTINGS };
|
||||
this.core.settings = { ...DEFAULT_SETTINGS };
|
||||
await this.services.setting.saveSettingData();
|
||||
await this.services.database.resetDatabase();
|
||||
// await this.plugin.initializeDatabase();
|
||||
|
||||
@@ -109,7 +109,7 @@ export function paneSyncSettings(
|
||||
await this.rebuildDB("localOnly");
|
||||
// this.resetEditingSettings();
|
||||
if (
|
||||
(await this.plugin.confirm.askYesNoDialog(
|
||||
(await this.core.confirm.askYesNoDialog(
|
||||
$msg("obsidianLiveSyncSettingTab.msgGenerateSetupURI"),
|
||||
{
|
||||
defaultOption: "Yes",
|
||||
|
||||
@@ -80,7 +80,7 @@ export class ModuleLiveSyncMain extends AbstractModule {
|
||||
initialiseWorkerModule();
|
||||
await this.services.appLifecycle.onWireUpEvents();
|
||||
// debugger;
|
||||
eventHub.emitEvent(EVENT_PLUGIN_LOADED, this.core);
|
||||
eventHub.emitEvent(EVENT_PLUGIN_LOADED);
|
||||
this._log($msg("moduleLiveSyncMain.logLoadingPlugin"));
|
||||
if (!(await this.services.appLifecycle.onInitialise())) {
|
||||
this._log($msg("moduleLiveSyncMain.logPluginInitCancelled"), LOG_LEVEL_NOTICE);
|
||||
|
||||
@@ -171,4 +171,18 @@ export class ObsidianAPIService extends InjectableAPIService<ObsidianServiceCont
|
||||
statusText: `${r.status}`,
|
||||
});
|
||||
}
|
||||
|
||||
override addStatusBarItem(): HTMLElement | undefined {
|
||||
return this.context.plugin.addStatusBarItem();
|
||||
}
|
||||
|
||||
override setInterval(handler: () => void, timeout: number): number {
|
||||
const timerId = globalThis.setInterval(handler, timeout) as unknown as number;
|
||||
this.context.plugin.registerInterval(timerId);
|
||||
return timerId;
|
||||
}
|
||||
|
||||
override getSystemConfigDir() {
|
||||
return this.app.vault.configDir;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user