From 81362816d6236847ab2db5976e6d97aae00aca72 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Fri, 3 Oct 2025 14:59:14 +0100 Subject: [PATCH 1/8] disenchanting and dispelling from the nightmarish `implicit something` Indeed, even though if this changeset is mostly another nightmare. It might be in beta for a while. --- src/common/types.ts | 26 +- src/common/utils.ts | 2 +- src/features/ConfigSync/CmdConfigSync.ts | 78 +- src/features/ConfigSync/PluginPane.svelte | 6 +- .../HiddenFileSync/CmdHiddenFileSync.ts | 88 +- src/features/LiveSyncCommands.ts | 24 +- .../CmdLocalDatabaseMainte.ts | 6 +- src/features/P2PSync/CmdP2PReplicator.ts | 338 +++++++- .../P2PReplicator/P2PReplicatorPane.svelte | 4 +- .../P2PReplicator/P2PReplicatorPaneView.ts | 2 +- src/lib | 2 +- src/main.ts | 792 ++++++++---------- src/modules/AbstractModule.ts | 249 +++--- src/modules/AbstractObsidianModule.ts | 6 +- src/modules/core/ModuleDatabaseFileAccess.ts | 16 +- src/modules/core/ModuleFileHandler.ts | 29 +- .../core/ModuleLocalDatabaseObsidian.ts | 22 +- src/modules/core/ModulePeriodicProcess.ts | 24 +- src/modules/core/ModulePouchDB.ts | 9 +- src/modules/core/ModuleRebuilder.ts | 93 +- src/modules/core/ModuleReplicator.ts | 103 ++- src/modules/core/ModuleReplicatorCouchDB.ts | 20 +- src/modules/core/ModuleReplicatorMinIO.ts | 11 +- src/modules/core/ModuleReplicatorP2P.ts | 14 +- src/modules/core/ModuleTargetFilter.ts | 32 +- .../coreFeatures/ModuleCheckRemoteSize.ts | 9 +- .../coreFeatures/ModuleConflictChecker.ts | 26 +- .../coreFeatures/ModuleConflictResolver.ts | 35 +- src/modules/coreFeatures/ModuleRedFlag.ts | 24 +- .../coreFeatures/ModuleRemoteGovernor.ts | 16 +- .../ModuleResolveMismatchedTweaks.ts | 42 +- .../coreObsidian/ModuleFileAccessObsidian.ts | 43 +- .../coreObsidian/ModuleInputUIObsidian.ts | 11 +- .../storageLib/StorageEventManager.ts | 20 +- .../essential/ModuleInitializerFile.ts | 52 +- src/modules/essential/ModuleKeyValueDB.ts | 32 +- src/modules/essential/ModuleMigration.ts | 27 +- .../essentialObsidian/ModuleObsidianAPI.ts | 38 +- .../essentialObsidian/ModuleObsidianEvents.ts | 56 +- .../essentialObsidian/ModuleObsidianMenu.ts | 47 +- .../ModuleExtraSyncObsidian.ts | 14 +- src/modules/extras/ModuleDev.ts | 26 +- src/modules/extras/ModuleIntegratedTest.ts | 17 +- src/modules/extras/ModuleReplicateTest.ts | 61 +- src/modules/extras/devUtil/TestPane.svelte | 8 +- .../DocumentHistory/DocumentHistoryModal.ts | 14 +- src/modules/features/ModuleGlobalHistory.ts | 11 +- .../ModuleInteractiveConflictResolver.ts | 30 +- src/modules/features/ModuleLog.ts | 37 +- .../features/ModuleObsidianDocumentHistory.ts | 11 +- src/modules/features/ModuleObsidianSetting.ts | 53 +- .../ModuleObsidianSettingAsMarkdown.ts | 15 +- .../features/ModuleObsidianSettingTab.ts | 9 +- src/modules/features/ModuleSetupObsidian.ts | 20 +- .../ObsidianLiveSyncSettingTab.ts | 31 +- .../features/SettingDialogue/PaneHatch.ts | 19 +- .../SettingDialogue/PaneMaintenance.ts | 16 +- .../features/SettingDialogue/PanePatches.ts | 2 +- .../SettingDialogue/PaneRemoteConfig.ts | 15 +- .../features/SettingDialogue/PaneSetup.ts | 8 +- .../SettingDialogue/PaneSyncSettings.ts | 16 +- src/modules/main/ModuleLiveSyncMain.ts | 110 ++- src/modules/services/ObsidianServices.ts | 76 ++ terser.config.mjs | 4 +- 64 files changed, 1852 insertions(+), 1245 deletions(-) create mode 100644 src/modules/services/ObsidianServices.ts diff --git a/src/common/types.ts b/src/common/types.ts index 7cfbd0b..ebb10b4 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -1,11 +1,6 @@ import { type PluginManifest, TFile } from "../deps.ts"; -import { - type DatabaseEntry, - type EntryBody, - type FilePath, - type UXFileInfoStub, - type UXInternalFileInfoStub, -} from "../lib/src/common/types.ts"; +import { type DatabaseEntry, type EntryBody, type FilePath } from "../lib/src/common/types.ts"; +export type { CacheData, FileEventItem } from "../lib/src/common/types.ts"; export interface PluginDataEntry extends DatabaseEntry { deviceVaultName: string; @@ -54,23 +49,6 @@ export type queueItem = { warned?: boolean; }; -export type CacheData = string | ArrayBuffer; -export type FileEventType = "CREATE" | "DELETE" | "CHANGED" | "INTERNAL"; -export type FileEventArgs = { - file: UXFileInfoStub | UXInternalFileInfoStub; - cache?: CacheData; - oldPath?: string; - ctx?: any; -}; -export type FileEventItem = { - type: FileEventType; - args: FileEventArgs; - key: string; - skipBatchWait?: boolean; - cancelled?: boolean; - batched?: boolean; -}; - // Hidden items (Now means `chunk`) export const CHeader = "h:"; diff --git a/src/common/utils.ts b/src/common/utils.ts index def180c..ac80031 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -189,7 +189,7 @@ export class PeriodicProcessor { () => fireAndForget(async () => { await this.process(); - if (this._plugin.$$isUnloaded()) { + if (this._plugin.services?.appLifecycle?.hasUnloaded()) { this.disable(); } }), diff --git a/src/features/ConfigSync/CmdConfigSync.ts b/src/features/ConfigSync/CmdConfigSync.ts index bb8da26..5036743 100644 --- a/src/features/ConfigSync/CmdConfigSync.ts +++ b/src/features/ConfigSync/CmdConfigSync.ts @@ -68,14 +68,23 @@ import type ObsidianLiveSyncPlugin from "../../main.ts"; import { base64ToArrayBuffer, base64ToString } from "octagonal-wheels/binary/base64"; import { ConflictResolveModal } from "../../modules/features/InteractiveConflictResolving/ConflictResolveModal.ts"; import { Semaphore } from "octagonal-wheels/concurrency/semaphore"; -import type { IObsidianModule } from "../../modules/AbstractObsidianModule.ts"; import { EVENT_REQUEST_OPEN_PLUGIN_SYNC_DIALOG, eventHub } from "../../common/events.ts"; import { PluginDialogModal } from "./PluginDialogModal.ts"; import { $msg } from "src/lib/src/common/i18n.ts"; +import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts"; +import type { LiveSyncCore } from "../../main.ts"; const d = "\u200b"; const d2 = "\n"; +declare global { + interface OPTIONAL_SYNC_FEATURES { + DISABLE: "DISABLE"; + CUSTOMIZE: "CUSTOMIZE"; + DISABLE_CUSTOM: "DISABLE_CUSTOM"; + } +} + function serialize(data: PluginDataEx): string { // For higher performance, create custom plug-in data strings. // Self-hosted LiveSync uses `\n` to split chunks. Therefore, grouping together those with similar entropy would work nicely. @@ -384,7 +393,7 @@ export type PluginDataEx = { mtime: number; }; -export class ConfigSync extends LiveSyncCommands implements IObsidianModule { +export class ConfigSync extends LiveSyncCommands { constructor(plugin: ObsidianLiveSyncPlugin) { super(plugin); pluginScanningCount.onChanged((e) => { @@ -482,7 +491,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { // Idea non-filter option? return this.getFileCategory(filePath) != ""; } - async $everyOnDatabaseInitialized(showNotice: boolean) { + private async _everyOnDatabaseInitialized(showNotice: boolean) { if (!this._isThisModuleEnabled()) return true; try { this._log("Scanning customizations..."); @@ -494,7 +503,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { } return true; } - async $everyBeforeReplicate(showNotice: boolean) { + async _everyBeforeReplicate(showNotice: boolean) { if (!this._isThisModuleEnabled()) return true; if (this.settings.autoSweepPlugins) { await this.scanAllConfigFiles(showNotice); @@ -502,7 +511,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { } return true; } - async $everyOnResumeProcess(): Promise { + async _everyOnResumeProcess(): Promise { if (!this._isThisModuleEnabled()) return true; if (this._isMainSuspended()) { return true; @@ -517,7 +526,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { ); return true; } - $everyAfterResumeProcess(): Promise { + _everyAfterResumeProcess(): Promise { const q = activeDocument.querySelector(`.livesync-ribbon-showcustom`); q?.toggleClass("sls-hidden", !this._isThisModuleEnabled()); return Promise.resolve(true); @@ -633,7 +642,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { ).startPipeline(); filenameToUnifiedKey(path: string, termOverRide?: string) { - const term = termOverRide || this.plugin.$$getDeviceAndVaultName(); + const term = termOverRide || this.services.setting.getDeviceAndVaultName(); const category = this.getFileCategory(path); const name = category == "CONFIG" || category == "SNIPPET" @@ -645,7 +654,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { } filenameWithUnifiedKey(path: string, termOverRide?: string) { - const term = termOverRide || this.plugin.$$getDeviceAndVaultName(); + const term = termOverRide || this.services.setting.getDeviceAndVaultName(); const category = this.getFileCategory(path); const name = category == "CONFIG" || category == "SNIPPET" ? path.split("/").slice(-1)[0] : path.split("/").slice(-2)[0]; @@ -654,7 +663,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { } unifiedKeyPrefixOfTerminal(termOverRide?: string) { - const term = termOverRide || this.plugin.$$getDeviceAndVaultName(); + const term = termOverRide || this.services.setting.getDeviceAndVaultName(); return `${ICXHeader}${term}/` as FilePathWithPrefix; } @@ -831,7 +840,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { const v2Path = (prefixPath + relativeFilename) as FilePathWithPrefix; // console.warn(`Migrating ${v1Path} / ${relativeFilename} to ${v2Path}`); this._log(`Migrating ${v1Path} / ${relativeFilename} to ${v2Path}`, LOG_LEVEL_VERBOSE); - const newId = await this.plugin.$$path2id(v2Path); + const newId = await this.services.path.path2id(v2Path); // const buf = const data = createBlob([DUMMY_HEAD, DUMMY_END, ...getDocDataAsArray(f.data)]); @@ -999,7 +1008,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { await this.plugin.storageAccess.ensureDir(path); // If the content has applied, modified time will be updated to the current time. await this.plugin.storageAccess.writeHiddenFileAuto(path, content); - await this.storeCustomisationFileV2(path, this.plugin.$$getDeviceAndVaultName()); + await this.storeCustomisationFileV2(path, this.services.setting.getDeviceAndVaultName()); } else { const files = data.files; for (const f of files) { @@ -1042,7 +1051,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { await this.plugin.storageAccess.writeHiddenFileAuto(path, content, stat); } this._log(`Applied ${f.filename} of ${data.displayName || data.name}..`); - await this.storeCustomisationFileV2(path, this.plugin.$$getDeviceAndVaultName()); + await this.storeCustomisationFileV2(path, this.services.setting.getDeviceAndVaultName()); } } } catch (ex) { @@ -1114,7 +1123,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { ); } } else if (data.category == "CONFIG") { - this.plugin.$$askReload(); + this.services.appLifecycle.askRestart(); } return true; } catch (ex) { @@ -1157,8 +1166,8 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { return false; } } - async $anyModuleParsedReplicationResultItem(docs: PouchDB.Core.ExistingDocument) { - if (!docs._id.startsWith(ICXHeader)) return undefined; + async _anyModuleParsedReplicationResultItem(docs: PouchDB.Core.ExistingDocument) { + if (!docs._id.startsWith(ICXHeader)) return false; if (this._isThisModuleEnabled()) { await this.updatePluginList( false, @@ -1205,7 +1214,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { } return true; } - async $everyRealizeSettingSyncMode(): Promise { + async _everyRealizeSettingSyncMode(): Promise { this.periodicPluginSweepProcessor?.disable(); if (!this._isMainReady) return true; if (!this._isMainSuspended()) return true; @@ -1345,7 +1354,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { }); } async storeCustomizationFiles(path: FilePath, termOverRide?: string) { - const term = termOverRide || this.plugin.$$getDeviceAndVaultName(); + const term = termOverRide || this.services.setting.getDeviceAndVaultName(); if (term == "") { this._log("We have to configure the device name", LOG_LEVEL_NOTICE); return; @@ -1488,7 +1497,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { } }); } - async $anyProcessOptionalFileEvent(path: FilePath): Promise { + async _anyProcessOptionalFileEvent(path: FilePath): Promise { return await this.watchVaultRawEventsAsync(path); } @@ -1535,7 +1544,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { await shareRunningResult("scanAllConfigFiles", async () => { const logLevel = showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO; this._log("Scanning customizing files.", logLevel, "scan-all-config"); - const term = this.plugin.$$getDeviceAndVaultName(); + const term = this.services.setting.getDeviceAndVaultName(); if (term == "") { this._log("We have to configure the device name", LOG_LEVEL_NOTICE); return; @@ -1673,7 +1682,10 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { return filenames as FilePath[]; } - async $allAskUsingOptionalSyncFeature(opt: { enableFetch?: boolean; enableOverwrite?: boolean }): Promise { + private async _allAskUsingOptionalSyncFeature(opt: { + enableFetch?: boolean; + enableOverwrite?: boolean; + }): Promise { await this._askHiddenFileConfiguration(opt); return true; } @@ -1707,7 +1719,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { } } - $anyGetOptionalConflictCheckMethod(path: FilePathWithPrefix): Promise { + _anyGetOptionalConflictCheckMethod(path: FilePathWithPrefix): Promise { if (isPluginMetadata(path)) { return Promise.resolve("newer"); } @@ -1717,7 +1729,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { return Promise.resolve(false); } - $allSuspendExtraSync(): Promise { + private _allSuspendExtraSync(): Promise { if (this.plugin.settings.usePluginSync || this.plugin.settings.autoSweepPlugins) { this._log( "Customisation sync have been temporarily disabled. Please enable them after the fetching, if you need them.", @@ -1729,10 +1741,11 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { return Promise.resolve(true); } - async $anyConfigureOptionalSyncFeature(mode: "CUSTOMIZE" | "DISABLE" | "DISABLE_CUSTOM") { + private async _anyConfigureOptionalSyncFeature(mode: keyof OPTIONAL_SYNC_FEATURES) { await this.configureHiddenFileSync(mode); + return true; } - async configureHiddenFileSync(mode: "CUSTOMIZE" | "DISABLE" | "DISABLE_CUSTOM") { + async configureHiddenFileSync(mode: keyof OPTIONAL_SYNC_FEATURES) { if (mode == "DISABLE") { this.plugin.settings.usePluginSync = false; await this.plugin.saveSettings(); @@ -1740,7 +1753,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { } if (mode == "CUSTOMIZE") { - if (!this.plugin.$$getDeviceAndVaultName()) { + if (!this.services.setting.getDeviceAndVaultName()) { let name = await this.plugin.confirm.askString("Device name", "Please set this device name", `desktop`); if (!name) { if (Platform.isAndroidApp) { @@ -1764,7 +1777,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { } name = name + Math.random().toString(36).slice(-4); } - this.plugin.$$setDeviceAndVaultName(name); + this.services.setting.setDeviceAndVaultName(name); } this.plugin.settings.usePluginSync = true; this.plugin.settings.useAdvancedMode = true; @@ -1789,4 +1802,17 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule { } return files; } + onBindFunction(core: LiveSyncCore, services: InjectableServiceHub): void { + services.fileProcessing.handleOptionalFileEvent(this._anyProcessOptionalFileEvent.bind(this)); + services.conflict.handleGetOptionalConflictCheckMethod(this._anyGetOptionalConflictCheckMethod.bind(this)); + services.replication.handleProcessVirtualDocuments(this._anyModuleParsedReplicationResultItem.bind(this)); + services.setting.handleOnRealiseSetting(this._everyRealizeSettingSyncMode.bind(this)); + services.appLifecycle.handleOnResuming(this._everyOnResumeProcess.bind(this)); + services.appLifecycle.handleOnResumed(this._everyAfterResumeProcess.bind(this)); + services.replication.handleBeforeReplicate(this._everyBeforeReplicate.bind(this)); + services.databaseEvents.handleDatabaseInitialised(this._everyOnDatabaseInitialized.bind(this)); + services.setting.handleSuspendExtraSync(this._allSuspendExtraSync.bind(this)); + services.setting.handleSuggestOptionalFeatures(this._allAskUsingOptionalSyncFeature.bind(this)); + services.setting.handleEnableOptionalFeature(this._anyConfigureOptionalSyncFeature.bind(this)); + } } diff --git a/src/features/ConfigSync/PluginPane.svelte b/src/features/ConfigSync/PluginPane.svelte index b821548..07df53c 100644 --- a/src/features/ConfigSync/PluginPane.svelte +++ b/src/features/ConfigSync/PluginPane.svelte @@ -25,7 +25,7 @@ export let plugin: ObsidianLiveSyncPlugin; $: hideNotApplicable = false; - $: thisTerm = plugin.$$getDeviceAndVaultName(); + $: thisTerm = plugin.services.setting.getDeviceAndVaultName(); const addOn = plugin.getAddOn(ConfigSync.name) as ConfigSync; if (!addOn) { @@ -98,7 +98,7 @@ await requestUpdate(); } async function replicate() { - await plugin.$$replicate(true); + await plugin.services.replication.replicate(true); } function selectAllNewest(selectMode: boolean) { selectNewestPulse++; @@ -237,7 +237,7 @@ plugin.settings.pluginSyncExtendedSetting[key].files = files; plugin.settings.pluginSyncExtendedSetting[key].mode = mode; } - plugin.$$saveSettingData(); + plugin.services.setting.saveSettingData(); } function getIcon(mode: SYNC_MODE) { if (mode in ICONS) { diff --git a/src/features/HiddenFileSync/CmdHiddenFileSync.ts b/src/features/HiddenFileSync/CmdHiddenFileSync.ts index 8fdd217..a7f2143 100644 --- a/src/features/HiddenFileSync/CmdHiddenFileSync.ts +++ b/src/features/HiddenFileSync/CmdHiddenFileSync.ts @@ -51,12 +51,20 @@ import { LiveSyncCommands } from "../LiveSyncCommands.ts"; import { addPrefix, stripAllPrefixes } from "../../lib/src/string_and_binary/path.ts"; import { QueueProcessor } from "octagonal-wheels/concurrency/processor"; import { hiddenFilesEventCount, hiddenFilesProcessingCount } from "../../lib/src/mock_and_interop/stores.ts"; -import type { IObsidianModule } from "../../modules/AbstractObsidianModule.ts"; import { EVENT_SETTING_SAVED, eventHub } from "../../common/events.ts"; -import type { LiveSyncLocalDB } from "../../lib/src/pouchdb/LiveSyncLocalDB.ts"; import { Semaphore } from "octagonal-wheels/concurrency/semaphore"; +import type { LiveSyncCore } from "../../main.ts"; type SyncDirection = "push" | "pull" | "safe" | "pullForce" | "pushForce"; +declare global { + interface OPTIONAL_SYNC_FEATURES { + FETCH: "FETCH"; + OVERWRITE: "OVERWRITE"; + MERGE: "MERGE"; + DISABLE: "DISABLE"; + DISABLE_HIDDEN: "DISABLE_HIDDEN"; + } +} function getComparingMTime( doc: (MetaEntry | LoadedEntry | false) | UXFileInfo | UXStat | null | undefined, includeDeleted = false @@ -72,7 +80,7 @@ function getComparingMTime( return doc.mtime ?? 0; } -export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule { +export class HiddenFileSync extends LiveSyncCommands { _isThisModuleEnabled() { return this.plugin.settings.syncInternalFiles; } @@ -132,13 +140,17 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule this.updateSettingCache(); }); } - async $everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise { + // We cannot initialise autosaveCache because kvDB is not ready yet + // async _everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise { + // this._fileInfoLastProcessed = await autosaveCache(this.kvDB, "hidden-file-lastProcessed"); + // this._databaseInfoLastProcessed = await autosaveCache(this.kvDB, "hidden-file-lastProcessed-database"); + // this._fileInfoLastKnown = await autosaveCache(this.kvDB, "hidden-file-lastKnown"); + // return true; + // } + private async _everyOnDatabaseInitialized(showNotice: boolean) { this._fileInfoLastProcessed = await autosaveCache(this.kvDB, "hidden-file-lastProcessed"); this._databaseInfoLastProcessed = await autosaveCache(this.kvDB, "hidden-file-lastProcessed-database"); this._fileInfoLastKnown = await autosaveCache(this.kvDB, "hidden-file-lastKnown"); - return true; - } - async $everyOnDatabaseInitialized(showNotice: boolean) { if (this._isThisModuleEnabled()) { if (this._fileInfoLastProcessed.size == 0 && this._fileInfoLastProcessed.size == 0) { this._log(`No cache found. Performing startup scan.`, LOG_LEVEL_VERBOSE); @@ -149,7 +161,7 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule } return true; } - async $everyBeforeReplicate(showNotice: boolean) { + async _everyBeforeReplicate(showNotice: boolean) { if ( this._isThisModuleEnabled() && this._isDatabaseReady() && @@ -161,7 +173,7 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule return true; } - $everyOnloadAfterLoadSettings(): Promise { + private _everyOnloadAfterLoadSettings(): Promise { this.updateSettingCache(); return Promise.resolve(true); } @@ -197,7 +209,7 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule await this.applyOfflineChanges(showNotice); } - async $everyOnResumeProcess(): Promise { + async _everyOnResumeProcess(): Promise { this.periodicInternalFileScanProcessor?.disable(); if (this._isMainSuspended()) return true; if (this._isThisModuleEnabled()) { @@ -211,10 +223,10 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule return true; } - $everyRealizeSettingSyncMode(): Promise { + _everyRealizeSettingSyncMode(): Promise { this.periodicInternalFileScanProcessor?.disable(); if (this._isMainSuspended()) return Promise.resolve(true); - if (!this.plugin.$$isReady()) return Promise.resolve(true); + if (!this.services.appLifecycle.isReady()) return Promise.resolve(true); this.periodicInternalFileScanProcessor.enable( this._isThisModuleEnabled() && this.settings.syncInternalFilesInterval ? this.settings.syncInternalFilesInterval * 1000 @@ -227,13 +239,14 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule return Promise.resolve(true); } - async $anyProcessOptionalFileEvent(path: FilePath): Promise { + async _anyProcessOptionalFileEvent(path: FilePath): Promise { if (this.isReady()) { - return await this.trackStorageFileModification(path); + return (await this.trackStorageFileModification(path)) || false; } + return false; } - $anyGetOptionalConflictCheckMethod(path: FilePathWithPrefix): Promise { + _anyGetOptionalConflictCheckMethod(path: FilePathWithPrefix): Promise { if (isInternalMetadata(path)) { this.queueConflictCheck(path); return Promise.resolve(true); @@ -241,12 +254,12 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule return Promise.resolve(false); } - async $anyProcessOptionalSyncFiles(doc: LoadedEntry): Promise { + async _anyProcessOptionalSyncFiles(doc: LoadedEntry): Promise { if (isInternalMetadata(doc._id)) { if (this._isThisModuleEnabled()) { //system file const filename = getPath(doc); - if (await this.plugin.$$isTargetFile(filename)) { + if (await this.services.vault.isTargetFile(filename)) { // this.procInternalFile(filename); await this.processReplicationResult(doc); return true; @@ -1091,14 +1104,14 @@ Offline Changed files: ${files.length}`; // If something changes left, notify for reloading Obsidian. if (updatedFolders.indexOf(this.plugin.app.vault.configDir) >= 0) { - if (!this.plugin.$$isReloadingScheduled()) { + if (!this.services.appLifecycle.isReloadingScheduled()) { this.plugin.confirm.askInPopup( `updated-any-hidden`, `Some setting files have been modified\nPress {HERE} to schedule a reload of Obsidian, or press elsewhere to dismiss this message.`, (anchor) => { anchor.text = "HERE"; anchor.addEventListener("click", () => { - this.plugin.$$scheduleAppReload(); + this.services.appLifecycle.scheduleRestart(); }); } ); @@ -1318,7 +1331,7 @@ Offline Changed files: ${files.length}`; async storeInternalFileToDatabase(file: InternalFileInfo | UXFileInfo, forceWrite = false) { const storeFilePath = stripAllPrefixes(file.path as FilePath); const storageFilePath = file.path; - if (await this.plugin.$$isIgnoredByIgnoreFiles(storageFilePath)) { + if (await this.services.vault.isIgnoredByIgnoreFile(storageFilePath)) { return undefined; } const prefixedFileName = addPrefix(storeFilePath, ICHeader); @@ -1372,7 +1385,7 @@ Offline Changed files: ${files.length}`; const displayFileName = filenameSrc; const prefixedFileName = addPrefix(storeFilePath, ICHeader); const mtime = new Date().getTime(); - if (await this.plugin.$$isIgnoredByIgnoreFiles(storageFilePath)) { + if (await this.services.vault.isIgnoredByIgnoreFile(storageFilePath)) { return undefined; } return await serialized("file-" + prefixedFileName, async () => { @@ -1432,7 +1445,7 @@ Offline Changed files: ${files.length}`; includeDeletion = true ) { const prefixedFileName = addPrefix(storageFilePath, ICHeader); - if (await this.plugin.$$isIgnoredByIgnoreFiles(storageFilePath)) { + if (await this.services.vault.isIgnoredByIgnoreFile(storageFilePath)) { return undefined; } return await serialized("file-" + prefixedFileName, async () => { @@ -1582,7 +1595,7 @@ Offline Changed files: ${files.length}`; // <-- Database To Storage Functions - async $allAskUsingOptionalSyncFeature(opt: { enableFetch?: boolean; enableOverwrite?: boolean }) { + private async _allAskUsingOptionalSyncFeature(opt: { enableFetch?: boolean; enableOverwrite?: boolean }) { await this._askHiddenFileConfiguration(opt); return true; } @@ -1632,7 +1645,7 @@ ${messageFetch}${messageOverwrite}${messageMerge} } } - $allSuspendExtraSync(): Promise { + private _allSuspendExtraSync(): Promise { if (this.plugin.settings.syncInternalFiles) { this._log( "Hidden file synchronization have been temporarily disabled. Please enable them after the fetching, if you need them.", @@ -1644,11 +1657,12 @@ ${messageFetch}${messageOverwrite}${messageMerge} } // --> Configuration handling - async $anyConfigureOptionalSyncFeature(mode: "FETCH" | "OVERWRITE" | "MERGE" | "DISABLE" | "DISABLE_HIDDEN") { + private async _anyConfigureOptionalSyncFeature(mode: keyof OPTIONAL_SYNC_FEATURES) { await this.configureHiddenFileSync(mode); + return true; } - async configureHiddenFileSync(mode: "FETCH" | "OVERWRITE" | "MERGE" | "DISABLE" | "DISABLE_HIDDEN") { + async configureHiddenFileSync(mode: keyof OPTIONAL_SYNC_FEATURES) { if ( mode != "FETCH" && mode != "OVERWRITE" && @@ -1718,7 +1732,7 @@ ${messageFetch}${messageOverwrite}${messageMerge} const result: InternalFileInfo[] = []; for (const f of files) { const w = await f; - if (await this.plugin.$$isIgnoredByIgnoreFiles(w.path)) { + if (await this.services.vault.isIgnoredByIgnoreFile(w.path)) { continue; } const mtime = w.stat?.mtime ?? 0; @@ -1756,7 +1770,7 @@ ${messageFetch}${messageOverwrite}${messageMerge} if (ignoreFilter && ignoreFilter.some((ee) => ee.test(file))) { continue; } - if (await this.plugin.$$isIgnoredByIgnoreFiles(file)) continue; + if (await this.services.vault.isIgnoredByIgnoreFile(file)) continue; files.push(file); } L1: for (const v of w.folders) { @@ -1768,7 +1782,7 @@ ${messageFetch}${messageOverwrite}${messageMerge} if (ignoreFilter && ignoreFilter.some((e) => e.test(v))) { continue L1; } - if (await this.plugin.$$isIgnoredByIgnoreFiles(v)) { + if (await this.services.vault.isIgnoredByIgnoreFile(v)) { continue L1; } files = files.concat(await this.getFiles(v, ignoreList, filter, ignoreFilter)); @@ -1777,4 +1791,20 @@ ${messageFetch}${messageOverwrite}${messageMerge} } // <-- Local Storage SubFunctions + + onBindFunction(core: LiveSyncCore, services: typeof core.services) { + // No longer needed on initialisation + // services.databaseEvents.handleOnDatabaseInitialisation(this._everyOnInitializeDatabase.bind(this)); + services.appLifecycle.handleOnSettingLoaded(this._everyOnloadAfterLoadSettings.bind(this)); + services.fileProcessing.handleOptionalFileEvent(this._anyProcessOptionalFileEvent.bind(this)); + services.conflict.handleGetOptionalConflictCheckMethod(this._anyGetOptionalConflictCheckMethod.bind(this)); + services.replication.handleProcessOptionalSynchroniseResult(this._anyProcessOptionalSyncFiles.bind(this)); + services.setting.handleOnRealiseSetting(this._everyRealizeSettingSyncMode.bind(this)); + services.appLifecycle.handleOnResuming(this._everyOnResumeProcess.bind(this)); + services.replication.handleBeforeReplicate(this._everyBeforeReplicate.bind(this)); + services.databaseEvents.handleDatabaseInitialised(this._everyOnDatabaseInitialized.bind(this)); + services.setting.handleSuspendExtraSync(this._allSuspendExtraSync.bind(this)); + services.setting.handleSuggestOptionalFeatures(this._allAskUsingOptionalSyncFeature.bind(this)); + services.setting.handleEnableOptionalFeature(this._anyConfigureOptionalSyncFeature.bind(this)); + } } diff --git a/src/features/LiveSyncCommands.ts b/src/features/LiveSyncCommands.ts index 4ab188a..ea1088f 100644 --- a/src/features/LiveSyncCommands.ts +++ b/src/features/LiveSyncCommands.ts @@ -5,13 +5,13 @@ import { LOG_LEVEL_NOTICE, type AnyEntry, type DocumentID, - type EntryHasPath, type FilePath, type FilePathWithPrefix, type LOG_LEVEL, } from "../lib/src/common/types.ts"; import type ObsidianLiveSyncPlugin from "../main.ts"; import { MARK_DONE } from "../modules/features/ModuleLog.ts"; +import type { LiveSyncCore } from "../main.ts"; let noticeIndex = 0; export abstract class LiveSyncCommands { @@ -25,12 +25,15 @@ export abstract class LiveSyncCommands { get localDatabase() { return this.plugin.localDatabase; } - - id2path(id: DocumentID, entry?: EntryHasPath, stripPrefix?: boolean): FilePathWithPrefix { - return this.plugin.$$id2path(id, entry, stripPrefix); + get services() { + return this.plugin.services; } + + // id2path(id: DocumentID, entry?: EntryHasPath, stripPrefix?: boolean): FilePathWithPrefix { + // return this.plugin.$$id2path(id, entry, stripPrefix); + // } async path2id(filename: FilePathWithPrefix | FilePath, prefix?: string): Promise { - return await this.plugin.$$path2id(filename, prefix); + return await this.services.path.path2id(filename, prefix); } getPath(entry: AnyEntry): FilePathWithPrefix { return getPath(entry); @@ -38,18 +41,19 @@ export abstract class LiveSyncCommands { constructor(plugin: ObsidianLiveSyncPlugin) { this.plugin = plugin; + this.onBindFunction(plugin, plugin.services); } abstract onunload(): void; abstract onload(): void | Promise; _isMainReady() { - return this.plugin.$$isReady(); + return this.plugin.services.appLifecycle.isReady(); } _isMainSuspended() { - return this.plugin.$$isSuspended(); + return this.services.appLifecycle.isSuspended(); } _isDatabaseReady() { - return this.plugin.$$isDatabaseReady(); + return this.services.database.isDatabaseReady(); } _log = (msg: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key?: string) => { @@ -89,4 +93,8 @@ export abstract class LiveSyncCommands { _debug = (msg: any, key?: string) => { this._log(msg, LOG_LEVEL_VERBOSE, key); }; + + onBindFunction(core: LiveSyncCore, services: typeof core.services) { + // Override if needed. + } } diff --git a/src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts b/src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts index 79c9a52..dddadaa 100644 --- a/src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts +++ b/src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts @@ -10,7 +10,6 @@ import { type MetaEntry, } from "../../lib/src/common/types"; import { getNoFromRev } from "../../lib/src/pouchdb/LiveSyncLocalDB"; -import type { IObsidianModule } from "../../modules/AbstractObsidianModule"; import { LiveSyncCommands } from "../LiveSyncCommands"; import { serialized } from "octagonal-wheels/concurrency/lock_v2"; import { arrayToChunkedArray } from "octagonal-wheels/collection"; @@ -22,10 +21,7 @@ type NoteDocumentID = DocumentID; type Rev = string; type ChunkUsageMap = Map>>; -export class LocalDatabaseMaintenance extends LiveSyncCommands implements IObsidianModule { - $everyOnload(): Promise { - return Promise.resolve(true); - } +export class LocalDatabaseMaintenance extends LiveSyncCommands { onunload(): void { // NO OP. } diff --git a/src/features/P2PSync/CmdP2PReplicator.ts b/src/features/P2PSync/CmdP2PReplicator.ts index 39d7e77..cc99f7e 100644 --- a/src/features/P2PSync/CmdP2PReplicator.ts +++ b/src/features/P2PSync/CmdP2PReplicator.ts @@ -1,21 +1,27 @@ -import type { IObsidianModule } from "../../modules/AbstractObsidianModule"; import { P2PReplicatorPaneView, VIEW_TYPE_P2P } from "./P2PReplicator/P2PReplicatorPaneView.ts"; import { AutoAccepting, LOG_LEVEL_NOTICE, + P2P_DEFAULT_SETTINGS, REMOTE_P2P, type EntryDoc, type P2PSyncSetting, type RemoteDBSettings, } from "../../lib/src/common/types.ts"; import { LiveSyncCommands } from "../LiveSyncCommands.ts"; -import { LiveSyncTrysteroReplicator } from "../../lib/src/replication/trystero/LiveSyncTrysteroReplicator.ts"; +import { + LiveSyncTrysteroReplicator, + setReplicatorFunc, +} from "../../lib/src/replication/trystero/LiveSyncTrysteroReplicator.ts"; import { EVENT_REQUEST_OPEN_P2P, eventHub } from "../../common/events.ts"; import type { LiveSyncAbstractReplicator } from "../../lib/src/replication/LiveSyncAbstractReplicator.ts"; -import { Logger } from "octagonal-wheels/common/logger"; +import { LOG_LEVEL_INFO, LOG_LEVEL_VERBOSE, Logger } from "octagonal-wheels/common/logger"; import type { CommandShim } from "../../lib/src/replication/trystero/P2PReplicatorPaneCommon.ts"; import { - P2PReplicatorMixIn, + addP2PEventHandlers, + closeP2PReplicator, + openP2PReplicator, + P2PLogCollector, removeP2PReplicatorInstance, type P2PReplicatorBase, } from "../../lib/src/replication/trystero/P2PReplicatorCore.ts"; @@ -24,8 +30,180 @@ import type { Confirm } from "../../lib/src/interfaces/Confirm.ts"; import type ObsidianLiveSyncPlugin from "../../main.ts"; import type { SimpleStore } from "octagonal-wheels/databases/SimpleStoreBase"; import { getPlatformName } from "../../lib/src/PlatformAPIs/obsidian/Environment.ts"; +import type { LiveSyncCore } from "../../main.ts"; +import { TrysteroReplicator } from "../../lib/src/replication/trystero/TrysteroReplicator.ts"; -class P2PReplicatorCommandBase extends LiveSyncCommands implements P2PReplicatorBase { +// class P2PReplicatorCommandBase extends LiveSyncCommands implements P2PReplicatorBase { +// storeP2PStatusLine = reactiveSource(""); + +// getSettings(): P2PSyncSetting { +// return this.plugin.settings; +// } +// get settings() { +// return this.plugin.settings; +// } +// getDB() { +// return this.plugin.localDatabase.localDatabase; +// } + +// get confirm(): Confirm { +// return this.plugin.confirm; +// } +// _simpleStore!: SimpleStore; + +// simpleStore(): SimpleStore { +// return this._simpleStore; +// } + +// constructor(plugin: ObsidianLiveSyncPlugin) { +// super(plugin); +// this.onBindFunction(plugin, plugin.services); +// } + +// async handleReplicatedDocuments(docs: EntryDoc[]): Promise { +// // console.log("Processing Replicated Docs", docs); +// return await this.services.replication.parseSynchroniseResult( +// docs as PouchDB.Core.ExistingDocument[] +// ); +// } +// onunload(): void { +// throw new Error("Method not implemented."); +// } +// onload(): void | Promise { +// throw new Error("Method not implemented."); +// } + +// init() { +// this._simpleStore = this.services.database.openSimpleStore("p2p-sync"); +// return Promise.resolve(this); +// } + +// } + +// export class P2PReplicator extends P2PReplicatorMixIn(P2PReplicatorCommandBase) implements CommandShim { +// storeP2PStatusLine = reactiveSource(""); +// _anyNewReplicator(settingOverride: Partial = {}): Promise { +// const settings = { ...this.settings, ...settingOverride }; +// if (settings.remoteType == REMOTE_P2P) { +// return Promise.resolve(new LiveSyncTrysteroReplicator(this.plugin)); +// } +// return undefined!; +// } +// override getPlatform(): string { +// return getPlatformName(); +// } + +// override onunload(): void { +// removeP2PReplicatorInstance(); +// void this.close(); +// } + +// override onload(): void | Promise { +// eventHub.onEvent(EVENT_REQUEST_OPEN_P2P, () => { +// void this.openPane(); +// }); +// this.p2pLogCollector.p2pReplicationLine.onChanged((line) => { +// this.storeP2PStatusLine.value = line.value; +// }); +// } +// async _everyOnInitializeDatabase(): Promise { +// await this.initialiseP2PReplicator(); +// return Promise.resolve(true); +// } + +// private async _allSuspendExtraSync() { +// this.plugin.settings.P2P_Enabled = false; +// this.plugin.settings.P2P_AutoAccepting = AutoAccepting.NONE; +// this.plugin.settings.P2P_AutoBroadcast = false; +// this.plugin.settings.P2P_AutoStart = false; +// this.plugin.settings.P2P_AutoSyncPeers = ""; +// this.plugin.settings.P2P_AutoWatchPeers = ""; +// return await Promise.resolve(true); +// } + +// // async $everyOnLoadStart() { +// // return await Promise.resolve(); +// // } + +// async openPane() { +// await this.services.API.showWindow(VIEW_TYPE_P2P); +// } + +// async _everyOnloadStart(): Promise { +// // this.plugin.registerView(VIEW_TYPE_P2P, (leaf) => new P2PReplicatorPaneView(leaf, this.plugin)); +// this.plugin.addCommand({ +// id: "open-p2p-replicator", +// name: "P2P Sync : Open P2P Replicator", +// callback: async () => { +// await this.openPane(); +// }, +// }); +// this.plugin.addCommand({ +// id: "p2p-establish-connection", +// name: "P2P Sync : Connect to the Signalling Server", +// checkCallback: (isChecking) => { +// if (isChecking) { +// return !(this._replicatorInstance?.server?.isServing ?? false); +// } +// void this.open(); +// }, +// }); +// this.plugin.addCommand({ +// id: "p2p-close-connection", +// name: "P2P Sync : Disconnect from the Signalling Server", +// checkCallback: (isChecking) => { +// if (isChecking) { +// return this._replicatorInstance?.server?.isServing ?? false; +// } +// Logger(`Closing P2P Connection`, LOG_LEVEL_NOTICE); +// void this.close(); +// }, +// }); +// this.plugin.addCommand({ +// id: "replicate-now-by-p2p", +// name: "Replicate now by P2P", +// checkCallback: (isChecking) => { +// if (isChecking) { +// if (this.settings.remoteType == REMOTE_P2P) return false; +// if (!this._replicatorInstance?.server?.isServing) return false; +// return true; +// } +// void this._replicatorInstance?.replicateFromCommand(false); +// }, +// }); +// this.plugin +// .addRibbonIcon("waypoints", "P2P Replicator", async () => { +// await this.openPane(); +// }) +// .addClass("livesync-ribbon-replicate-p2p"); + +// return await Promise.resolve(true); +// } +// _everyAfterResumeProcess(): Promise { +// if (this.settings.P2P_Enabled && this.settings.P2P_AutoStart) { +// setTimeout(() => void this.open(), 100); +// } +// const rep = this._replicatorInstance; +// rep?.allowReconnection(); +// return Promise.resolve(true); +// } +// _everyBeforeSuspendProcess(): Promise { +// const rep = this._replicatorInstance; +// rep?.disconnectFromServer(); +// return Promise.resolve(true); +// } + +// override onBindFunction(core: LiveSyncCore, services: typeof core.services): void { +// services.replicator.handleGetNewReplicator(this._anyNewReplicator.bind(this)); +// services.databaseEvents.handleOnDatabaseInitialisation(this._everyOnInitializeDatabase.bind(this)); +// services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this)); +// services.appLifecycle.handleOnSuspending(this._everyBeforeSuspendProcess.bind(this)); +// services.appLifecycle.handleOnResumed(this._everyAfterResumeProcess.bind(this)); +// services.setting.handleSuspendExtraSync(this._allSuspendExtraSync.bind(this)); +// } +// } + +export class P2PReplicator extends LiveSyncCommands implements P2PReplicatorBase, CommandShim { storeP2PStatusLine = reactiveSource(""); getSettings(): P2PSyncSetting { @@ -49,47 +227,126 @@ class P2PReplicatorCommandBase extends LiveSyncCommands implements P2PReplicator constructor(plugin: ObsidianLiveSyncPlugin) { super(plugin); + setReplicatorFunc(() => this._replicatorInstance); + addP2PEventHandlers(this); + this.afterConstructor(); + this.onBindFunction(plugin, plugin.services); } async handleReplicatedDocuments(docs: EntryDoc[]): Promise { // console.log("Processing Replicated Docs", docs); - return await this.plugin.$$parseReplicationResult(docs as PouchDB.Core.ExistingDocument[]); - } - onunload(): void { - throw new Error("Method not implemented."); - } - onload(): void | Promise { - throw new Error("Method not implemented."); + return await this.services.replication.parseSynchroniseResult( + docs as PouchDB.Core.ExistingDocument[] + ); } - init() { - this._simpleStore = this.plugin.$$getSimpleStore("p2p-sync"); - return Promise.resolve(this); - } -} - -export class P2PReplicator - extends P2PReplicatorMixIn(P2PReplicatorCommandBase) - implements IObsidianModule, CommandShim -{ - storeP2PStatusLine = reactiveSource(""); - $anyNewReplicator(settingOverride: Partial = {}): Promise { + _anyNewReplicator(settingOverride: Partial = {}): Promise { const settings = { ...this.settings, ...settingOverride }; if (settings.remoteType == REMOTE_P2P) { return Promise.resolve(new LiveSyncTrysteroReplicator(this.plugin)); } return undefined!; } - override getPlatform(): string { + _replicatorInstance?: TrysteroReplicator; + p2pLogCollector = new P2PLogCollector(); + + afterConstructor() { + return; + } + + async open() { + await openP2PReplicator(this); + } + async close() { + await closeP2PReplicator(this); + } + + getConfig(key: string) { + const vaultName = this.services.vault.getVaultName(); + const dbKey = `${vaultName}-${key}`; + return localStorage.getItem(dbKey); + } + setConfig(key: string, value: string) { + const vaultName = this.services.vault.getVaultName(); + const dbKey = `${vaultName}-${key}`; + localStorage.setItem(dbKey, value); + } + enableBroadcastCastings() { + return this?._replicatorInstance?.enableBroadcastChanges(); + } + disableBroadcastCastings() { + return this?._replicatorInstance?.disableBroadcastChanges(); + } + + init() { + this._simpleStore = this.services.database.openSimpleStore("p2p-sync"); + return Promise.resolve(this); + } + + async initialiseP2PReplicator(): Promise { + await this.init(); + try { + if (this._replicatorInstance) { + await this._replicatorInstance.close(); + this._replicatorInstance = undefined; + } + + if (!this.settings.P2P_AppID) { + this.settings.P2P_AppID = P2P_DEFAULT_SETTINGS.P2P_AppID; + } + const getInitialDeviceName = () => this.getConfig("p2p_device_name") || this.services.vault.getVaultName(); + + const getSettings = () => this.settings; + const store = () => this.simpleStore(); + const getDB = () => this.getDB(); + + const getConfirm = () => this.confirm; + const getPlatform = () => this.getPlatform(); + const env = { + get db() { + return getDB(); + }, + get confirm() { + return getConfirm(); + }, + get deviceName() { + return getInitialDeviceName(); + }, + get platform() { + return getPlatform(); + }, + get settings() { + return getSettings(); + }, + processReplicatedDocs: async (docs: EntryDoc[]): Promise => { + await this.handleReplicatedDocuments(docs); + // No op. This is a client and does not need to process the docs + }, + get simpleStore() { + return store(); + }, + }; + this._replicatorInstance = new TrysteroReplicator(env); + return this._replicatorInstance; + } catch (e) { + this._log( + e instanceof Error ? e.message : "Something occurred on Initialising P2P Replicator", + LOG_LEVEL_INFO + ); + this._log(e, LOG_LEVEL_VERBOSE); + throw e; + } + } + getPlatform(): string { return getPlatformName(); } - override onunload(): void { + onunload(): void { removeP2PReplicatorInstance(); void this.close(); } - override onload(): void | Promise { + onload(): void | Promise { eventHub.onEvent(EVENT_REQUEST_OPEN_P2P, () => { void this.openPane(); }); @@ -97,12 +354,12 @@ export class P2PReplicator this.storeP2PStatusLine.value = line.value; }); } - async $everyOnInitializeDatabase(): Promise { + async _everyOnInitializeDatabase(): Promise { await this.initialiseP2PReplicator(); return Promise.resolve(true); } - async $allSuspendExtraSync() { + private async _allSuspendExtraSync() { this.plugin.settings.P2P_Enabled = false; this.plugin.settings.P2P_AutoAccepting = AutoAccepting.NONE; this.plugin.settings.P2P_AutoBroadcast = false; @@ -112,15 +369,15 @@ export class P2PReplicator return await Promise.resolve(true); } - async $everyOnLoadStart() { - return await Promise.resolve(); - } + // async $everyOnLoadStart() { + // return await Promise.resolve(); + // } async openPane() { - await this.plugin.$$showView(VIEW_TYPE_P2P); + await this.services.API.showWindow(VIEW_TYPE_P2P); } - async $everyOnloadStart(): Promise { + async _everyOnloadStart(): Promise { this.plugin.registerView(VIEW_TYPE_P2P, (leaf) => new P2PReplicatorPaneView(leaf, this.plugin)); this.plugin.addCommand({ id: "open-p2p-replicator", @@ -170,7 +427,7 @@ export class P2PReplicator return await Promise.resolve(true); } - $everyAfterResumeProcess(): Promise { + _everyAfterResumeProcess(): Promise { if (this.settings.P2P_Enabled && this.settings.P2P_AutoStart) { setTimeout(() => void this.open(), 100); } @@ -178,9 +435,18 @@ export class P2PReplicator rep?.allowReconnection(); return Promise.resolve(true); } - $everyBeforeSuspendProcess(): Promise { + _everyBeforeSuspendProcess(): Promise { const rep = this._replicatorInstance; rep?.disconnectFromServer(); return Promise.resolve(true); } + + override onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.replicator.handleGetNewReplicator(this._anyNewReplicator.bind(this)); + services.databaseEvents.handleOnDatabaseInitialisation(this._everyOnInitializeDatabase.bind(this)); + services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this)); + services.appLifecycle.handleOnSuspending(this._everyBeforeSuspendProcess.bind(this)); + services.appLifecycle.handleOnResumed(this._everyAfterResumeProcess.bind(this)); + services.setting.handleSuspendExtraSync(this._allSuspendExtraSync.bind(this)); + } } diff --git a/src/features/P2PSync/P2PReplicator/P2PReplicatorPane.svelte b/src/features/P2PSync/P2PReplicator/P2PReplicatorPane.svelte index ef633dd..344b1b5 100644 --- a/src/features/P2PSync/P2PReplicator/P2PReplicatorPane.svelte +++ b/src/features/P2PSync/P2PReplicator/P2PReplicatorPane.svelte @@ -32,10 +32,10 @@ const initialSettings = { ...plugin.settings }; let settings = $state(initialSettings); - // const vaultName = plugin.$$getVaultName(); + // const vaultName = service.vault.getVaultName(); // const dbKey = `${vaultName}-p2p-device-name`; - const initialDeviceName = cmdSync.getConfig("p2p_device_name") ?? plugin.$$getVaultName(); + const initialDeviceName = cmdSync.getConfig("p2p_device_name") ?? plugin.services.vault.getVaultName(); let deviceName = $state(initialDeviceName); let eP2PEnabled = $state(initialSettings.P2P_Enabled); diff --git a/src/features/P2PSync/P2PReplicator/P2PReplicatorPaneView.ts b/src/features/P2PSync/P2PReplicator/P2PReplicatorPaneView.ts index e4e5fe6..b401ece 100644 --- a/src/features/P2PSync/P2PReplicator/P2PReplicatorPaneView.ts +++ b/src/features/P2PSync/P2PReplicator/P2PReplicatorPaneView.ts @@ -95,7 +95,7 @@ And you can also drop the local database to rebuild from the remote device.`, if (yn === DROP) { await this.plugin.rebuilder.scheduleFetch(); } else { - await this.plugin.$$scheduleAppReload(); + this.plugin.services.appLifecycle.scheduleRestart(); } } else { Logger(`Cancelled\nRemote config for ${peer.name} is not applied`, LOG_LEVEL_NOTICE); diff --git a/src/lib b/src/lib index 21ca077..868590e 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit 21ca077163561cd34d17a98451ae2ee8030c2796 +Subproject commit 868590e814e9a4fce61ab6e2eaac9be211a2cbd1 diff --git a/src/main.ts b/src/main.ts index eb71eb1..d0e37b7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,26 +1,10 @@ import { Plugin } from "./deps"; import { type EntryDoc, - type LoadedEntry, type ObsidianLiveSyncSettings, - type LOG_LEVEL, - type diff_result, type DatabaseConnectingStatus, - type EntryHasPath, - type DocumentID, - type FilePathWithPrefix, - type FilePath, - LOG_LEVEL_INFO, type HasSettings, - type MetaEntry, - type UXFileInfoStub, - type MISSING_OR_ERROR, - type AUTO_MERGED, - type RemoteDBSettings, - type TweakValues, - type CouchDBCredentials, } from "./lib/src/common/types.ts"; -import { type FileEventItem } from "./common/types.ts"; import { type SimpleStore } from "./lib/src/common/utils.ts"; import { LiveSyncLocalDB, type LiveSyncLocalDBEnv } from "./lib/src/pouchdb/LiveSyncLocalDB.ts"; import { @@ -35,7 +19,6 @@ import { reactiveSource, type ReactiveValue } from "octagonal-wheels/dataobject/ import { type LiveSyncJournalReplicatorEnv } from "./lib/src/replication/journal/LiveSyncJournalReplicator.js"; import { type LiveSyncCouchDBReplicatorEnv } from "./lib/src/replication/couchdb/LiveSyncReplicator.js"; import type { CheckPointInfo } from "./lib/src/replication/journal/JournalSyncTypes.js"; -import { ObsHttpHandler } from "./modules/essentialObsidian/APILib/ObsHttpHandler.js"; import type { IObsidianModule } from "./modules/AbstractObsidianModule.ts"; import { ModuleDev } from "./modules/extras/ModuleDev.ts"; @@ -59,8 +42,7 @@ import { ModuleDatabaseFileAccess } from "./modules/core/ModuleDatabaseFileAcces import { ModuleFileHandler } from "./modules/core/ModuleFileHandler.ts"; import { ModuleObsidianAPI } from "./modules/essentialObsidian/ModuleObsidianAPI.ts"; import { ModuleObsidianEvents } from "./modules/essentialObsidian/ModuleObsidianEvents.ts"; -import { injectModules, type AbstractModule } from "./modules/AbstractModule.ts"; -import type { ICoreModule } from "./modules/ModuleTypes.ts"; +import { type AbstractModule } from "./modules/AbstractModule.ts"; import { ModuleObsidianSettingDialogue } from "./modules/features/ModuleObsidianSettingTab.ts"; import { ModuleObsidianDocumentHistory } from "./modules/features/ModuleObsidianDocumentHistory.ts"; import { ModuleObsidianGlobalHistory } from "./modules/features/ModuleGlobalHistory.ts"; @@ -85,13 +67,16 @@ import { ModuleExtraSyncObsidian } from "./modules/extraFeaturesObsidian/ModuleE import { LocalDatabaseMaintenance } from "./features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts"; import { P2PReplicator } from "./features/P2PSync/CmdP2PReplicator.ts"; import type { LiveSyncManagers } from "./lib/src/managers/LiveSyncManagers.ts"; +import { ObsidianServiceHub } from "./modules/services/ObsidianServices.ts"; +import type { InjectableServiceHub } from "./lib/src/services/InjectableServices.ts"; + +// function throwShouldBeOverridden(): never { +// throw new Error("This function should be overridden by the module."); +// } +// const InterceptiveAll = Promise.resolve(true); +// const InterceptiveEvery = Promise.resolve(true); +// const InterceptiveAny = Promise.resolve(undefined); -function throwShouldBeOverridden(): never { - throw new Error("This function should be overridden by the module."); -} -const InterceptiveAll = Promise.resolve(true); -const InterceptiveEvery = Promise.resolve(true); -const InterceptiveAny = Promise.resolve(undefined); /** * All $prefixed functions are hooked by the modules. Be careful to call them directly. * Please refer to the module's source code to understand the function. @@ -101,6 +86,13 @@ const InterceptiveAny = Promise.resolve(undefined); * $any : Process all modules until the first success. * $ : Other interceptive points. You should manually assign the module * All of above performed on injectModules function. + * + * No longer used! See AppLifecycleService in Services.ts. + * For a while, just commented out some previously used code. (sorry, some are deleted...) + * 'Convention over configuration' was a lie for me. At least, very lack of refactor-ability. + * + * Still some modules are separated, and connected by `ThroughHole` class. + * However, it is not a good design. I am going to manage the modules in a more explicit way. */ export default class ObsidianLiveSyncPlugin @@ -112,6 +104,18 @@ export default class ObsidianLiveSyncPlugin LiveSyncCouchDBReplicatorEnv, HasSettings { + /** + * The service hub for managing all services. + */ + _services: InjectableServiceHub = new ObsidianServiceHub(); + get services() { + return this._services; + } + /** + * Bind functions to the service hub (for migration purpose). + */ + // bindFunctions = (this.serviceHub as ObsidianServiceHub).bindFunctions.bind(this.serviceHub); + // --> Module System getAddOn(cls: string) { for (const addon of this.addOns) { @@ -173,41 +177,9 @@ export default class ObsidianLiveSyncPlugin new ModuleReplicateTest(this, this), new ModuleIntegratedTest(this, this), ] as (IObsidianModule | AbstractModule)[]; - injected = injectModules(this, [...this.modules, ...this.addOns] as ICoreModule[]); + // injected = injectModules(this, [...this.modules, ...this.addOns] as ICoreModule[]); // <-- Module System - $$isSuspended(): boolean { - throwShouldBeOverridden(); - } - - $$setSuspended(value: boolean): void { - throwShouldBeOverridden(); - } - - $$isDatabaseReady(): boolean { - throwShouldBeOverridden(); - } - - $$getDeviceAndVaultName(): string { - throwShouldBeOverridden(); - } - $$setDeviceAndVaultName(name: string): void { - throwShouldBeOverridden(); - } - - $$addLog(message: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key = ""): void { - throwShouldBeOverridden(); - } - $$isReady(): boolean { - throwShouldBeOverridden(); - } - $$markIsReady(): void { - throwShouldBeOverridden(); - } - $$resetIsReady(): void { - throwShouldBeOverridden(); - } - // Following are plugged by the modules. settings!: ObsidianLiveSyncSettings; @@ -229,30 +201,6 @@ export default class ObsidianLiveSyncPlugin return this.settings; } - $$markFileListPossiblyChanged(): void { - throwShouldBeOverridden(); - } - - $$customFetchHandler(): ObsHttpHandler { - throwShouldBeOverridden(); - } - - $$getLastPostFailedBySize(): boolean { - throwShouldBeOverridden(); - } - - $$isStorageInsensitive(): boolean { - throwShouldBeOverridden(); - } - - $$shouldCheckCaseInsensitive(): boolean { - throwShouldBeOverridden(); - } - - $$isUnloaded(): boolean { - throwShouldBeOverridden(); - } - requestCount = reactiveSource(0); responseCount = reactiveSource(0); totalQueued = reactiveSource(0); @@ -277,101 +225,6 @@ export default class ObsidianLiveSyncPlugin syncStatus: "CLOSED" as DatabaseConnectingStatus, }); - $$isReloadingScheduled(): boolean { - throwShouldBeOverridden(); - } - $$getReplicator(): LiveSyncAbstractReplicator { - throwShouldBeOverridden(); - } - - $$connectRemoteCouchDB( - uri: string, - auth: CouchDBCredentials, - disableRequestURI: boolean, - passphrase: string | false, - useDynamicIterationCount: boolean, - performSetup: boolean, - skipInfo: boolean, - compression: boolean, - customHeaders: Record, - useRequestAPI: boolean, - getPBKDF2Salt: () => Promise - ): Promise< - | string - | { - db: PouchDB.Database; - info: PouchDB.Core.DatabaseInfo; - } - > { - throwShouldBeOverridden(); - } - - $$isMobile(): boolean { - throwShouldBeOverridden(); - } - $$vaultName(): string { - throwShouldBeOverridden(); - } - - // --> Path - - $$getActiveFilePath(): FilePathWithPrefix | undefined { - throwShouldBeOverridden(); - } - - // <-- Path - - // --> Path conversion - $$id2path(id: DocumentID, entry?: EntryHasPath, stripPrefix?: boolean): FilePathWithPrefix { - throwShouldBeOverridden(); - } - - $$path2id(filename: FilePathWithPrefix | FilePath, prefix?: string): Promise { - throwShouldBeOverridden(); - } - - // Database - $$createPouchDBInstance( - name?: string, - options?: PouchDB.Configuration.DatabaseConfiguration - ): PouchDB.Database { - throwShouldBeOverridden(); - } - - $allOnDBUnload(db: LiveSyncLocalDB): void { - return; - } - $allOnDBClose(db: LiveSyncLocalDB): void { - return; - } - - // Events /* @@ -412,331 +265,404 @@ export default class ObsidianLiveSyncPlugin */ - $everyOnLayoutReady(): Promise { - return InterceptiveEvery; - } - $everyOnFirstInitialize(): Promise { - return InterceptiveEvery; - } + // $everyOnLayoutReady(): Promise { + // //TODO: AppLifecycleService.onLayoutReady + // return InterceptiveEvery; + // } + // $everyOnFirstInitialize(): Promise { + // //TODO: AppLifecycleService.onFirstInitialize + // return InterceptiveEvery; + // } // Some Module should call this function to start the plugin. - $$onLiveSyncReady(): Promise { - throwShouldBeOverridden(); - } - $$wireUpEvents(): void { - throwShouldBeOverridden(); - } - $$onLiveSyncLoad(): Promise { - throwShouldBeOverridden(); - } + // $$onLiveSyncReady(): Promise { + // //TODO: AppLifecycleService.onLiveSyncReady + // throwShouldBeOverridden(); + // } + // $$wireUpEvents(): void { + // //TODO: AppLifecycleService.wireUpEvents + // throwShouldBeOverridden(); + // } + // $$onLiveSyncLoad(): Promise { + // //TODO: AppLifecycleService.onLoad + // throwShouldBeOverridden(); + // } - $$onLiveSyncUnload(): Promise { - throwShouldBeOverridden(); - } + // $$onLiveSyncUnload(): Promise { + // //TODO: AppLifecycleService.onAppUnload + // throwShouldBeOverridden(); + // } - $allScanStat(): Promise { - return InterceptiveAll; - } - $everyOnloadStart(): Promise { - return InterceptiveEvery; - } + // $allScanStat(): Promise { + // //TODO: AppLifecycleService.scanStartupIssues + // return InterceptiveAll; + // } + // $everyOnloadStart(): Promise { + // //TODO: AppLifecycleService.onInitialise + // return InterceptiveEvery; + // } - $everyOnloadAfterLoadSettings(): Promise { - return InterceptiveEvery; - } + // $everyOnloadAfterLoadSettings(): Promise { + // //TODO: AppLifecycleService.onApplyStartupLoaded + // return InterceptiveEvery; + // } - $everyOnload(): Promise { - return InterceptiveEvery; - } + // $everyOnload(): Promise { + // //TODO: AppLifecycleService.onLoaded + // return InterceptiveEvery; + // } - $anyHandlerProcessesFileEvent(item: FileEventItem): Promise { - return InterceptiveAny; - } + // $anyHandlerProcessesFileEvent(item: FileEventItem): Promise { + // //TODO: FileProcessingService.processFileEvent + // return InterceptiveAny; + // } - $allStartOnUnload(): Promise { - return InterceptiveAll; - } - $allOnUnload(): Promise { - return InterceptiveAll; - } + // $allStartOnUnload(): Promise { + // //TODO: AppLifecycleService.onBeforeUnload + // return InterceptiveAll; + // } + // $allOnUnload(): Promise { + // //TODO: AppLifecycleService.onUnload + // return InterceptiveAll; + // } - $$openDatabase(): Promise { - throwShouldBeOverridden(); - } + // $$openDatabase(): Promise { + // // DatabaseService.openDatabase + // throwShouldBeOverridden(); + // } - $$realizeSettingSyncMode(): Promise { - throwShouldBeOverridden(); - } - $$performRestart() { - throwShouldBeOverridden(); - } + // $$realizeSettingSyncMode(): Promise { + // // SettingService.realiseSetting + // throwShouldBeOverridden(); + // } + // $$performRestart() { + // // AppLifecycleService.performRestart + // throwShouldBeOverridden(); + // } - $$clearUsedPassphrase(): void { - throwShouldBeOverridden(); - } + // $$clearUsedPassphrase(): void { + // // SettingService.clearUsedPassphrase + // throwShouldBeOverridden(); + // } - $$decryptSettings(settings: ObsidianLiveSyncSettings): Promise { - throwShouldBeOverridden(); - } - $$adjustSettings(settings: ObsidianLiveSyncSettings): Promise { - throwShouldBeOverridden(); - } + // $$decryptSettings(settings: ObsidianLiveSyncSettings): Promise { + // // SettingService.decryptSettings + // throwShouldBeOverridden(); + // } + // $$adjustSettings(settings: ObsidianLiveSyncSettings): Promise { + // // SettingService.adjustSettings + // throwShouldBeOverridden(); + // } - $$loadSettings(): Promise { - throwShouldBeOverridden(); - } + // $$loadSettings(): Promise { + // // SettingService.loadSettings + // throwShouldBeOverridden(); + // } - $$saveDeviceAndVaultName(): void { - throwShouldBeOverridden(); - } + // $$saveDeviceAndVaultName(): void { + // // SettingService.saveDeviceAndVaultName + // throwShouldBeOverridden(); + // } - $$saveSettingData(): Promise { - throwShouldBeOverridden(); - } + // $$saveSettingData(): Promise { + // // SettingService.saveSettingData + // throwShouldBeOverridden(); + // } - $anyProcessOptionalFileEvent(path: FilePath): Promise { - return InterceptiveAny; - } + // $anyProcessOptionalFileEvent(path: FilePath): Promise { + // // FileProcessingService.processOptionalFileEvent + // return InterceptiveAny; + // } - $everyCommitPendingFileEvent(): Promise { - return InterceptiveEvery; - } + // $everyCommitPendingFileEvent(): Promise { + // // FileProcessingService.commitPendingFileEvent + // return InterceptiveEvery; + // } // -> - $anyGetOptionalConflictCheckMethod(path: FilePathWithPrefix): Promise { - return InterceptiveAny; - } + // $anyGetOptionalConflictCheckMethod(path: FilePathWithPrefix): Promise { + // return InterceptiveAny; + // } - $$queueConflictCheckIfOpen(file: FilePathWithPrefix): Promise { - throwShouldBeOverridden(); - } + // $$queueConflictCheckIfOpen(file: FilePathWithPrefix): Promise { + // // ConflictEventManager.queueCheckForConflictIfOpen + // throwShouldBeOverridden(); + // } - $$queueConflictCheck(file: FilePathWithPrefix): Promise { - throwShouldBeOverridden(); - } + // $$queueConflictCheck(file: FilePathWithPrefix): Promise { + // // ConflictEventManager.queueCheckForConflict + // throwShouldBeOverridden(); + // } - $$waitForAllConflictProcessed(): Promise { - throwShouldBeOverridden(); - } + // $$waitForAllConflictProcessed(): Promise { + // // ConflictEventManager.ensureAllConflictProcessed + // throwShouldBeOverridden(); + // } //<-- Conflict Check - $anyProcessOptionalSyncFiles(doc: LoadedEntry): Promise { - return InterceptiveAny; - } + // $anyProcessOptionalSyncFiles(doc: LoadedEntry): Promise { + // // ReplicationService.processOptionalSyncFile + // return InterceptiveAny; + // } - $anyProcessReplicatedDoc(doc: MetaEntry): Promise { - return InterceptiveAny; - } + // $anyProcessReplicatedDoc(doc: MetaEntry): Promise { + // // ReplicationService.processReplicatedDocument + // return InterceptiveAny; + // } //---> Sync - $$parseReplicationResult(docs: Array>): void { - throwShouldBeOverridden(); - } + // $$parseReplicationResult(docs: Array>): void { + // // ReplicationService.parseSynchroniseResult + // throwShouldBeOverridden(); + // } - $anyModuleParsedReplicationResultItem(docs: PouchDB.Core.ExistingDocument): Promise { - return InterceptiveAny; - } - $everyBeforeRealizeSetting(): Promise { - return InterceptiveEvery; - } - $everyAfterRealizeSetting(): Promise { - return InterceptiveEvery; - } - $everyRealizeSettingSyncMode(): Promise { - return InterceptiveEvery; - } + // $anyModuleParsedReplicationResultItem(docs: PouchDB.Core.ExistingDocument): Promise { + // // ReplicationService.processVirtualDocument + // return InterceptiveAny; + // } + // $everyBeforeRealizeSetting(): Promise { + // // SettingEventManager.beforeRealiseSetting + // return InterceptiveEvery; + // } + // $everyAfterRealizeSetting(): Promise { + // // SettingEventManager.onSettingRealised + // return InterceptiveEvery; + // } + // $everyRealizeSettingSyncMode(): Promise { + // // SettingEventManager.onRealiseSetting + // return InterceptiveEvery; + // } - $everyBeforeSuspendProcess(): Promise { - return InterceptiveEvery; - } - $everyOnResumeProcess(): Promise { - return InterceptiveEvery; - } - $everyAfterResumeProcess(): Promise { - return InterceptiveEvery; - } + // $everyBeforeSuspendProcess(): Promise { + // // AppLifecycleService.onSuspending + // return InterceptiveEvery; + // } + // $everyOnResumeProcess(): Promise { + // // AppLifecycleService.onResuming + // return InterceptiveEvery; + // } + // $everyAfterResumeProcess(): Promise { + // // AppLifecycleService.onResumed + // return InterceptiveEvery; + // } - $$fetchRemotePreferredTweakValues(trialSetting: RemoteDBSettings): Promise { - throwShouldBeOverridden(); - } - $$checkAndAskResolvingMismatchedTweaks(preferred: Partial): Promise<[TweakValues | boolean, boolean]> { - throwShouldBeOverridden(); - } - $$askResolvingMismatchedTweaks(preferredSource: TweakValues): Promise<"OK" | "CHECKAGAIN" | "IGNORE"> { - throwShouldBeOverridden(); - } + // $$fetchRemotePreferredTweakValues(trialSetting: RemoteDBSettings): Promise { + // //TODO:TweakValueService.fetchRemotePreferred + // throwShouldBeOverridden(); + // } + // $$checkAndAskResolvingMismatchedTweaks(preferred: Partial): Promise<[TweakValues | boolean, boolean]> { + // //TODO:TweakValueService.checkAndAskResolvingMismatched + // throwShouldBeOverridden(); + // } + // $$askResolvingMismatchedTweaks(preferredSource: TweakValues): Promise<"OK" | "CHECKAGAIN" | "IGNORE"> { + // //TODO:TweakValueService.askResolvingMismatched + // throwShouldBeOverridden(); + // } - $$checkAndAskUseRemoteConfiguration( - settings: RemoteDBSettings - ): Promise<{ result: false | TweakValues; requireFetch: boolean }> { - throwShouldBeOverridden(); - } + // $$checkAndAskUseRemoteConfiguration( + // settings: RemoteDBSettings + // ): Promise<{ result: false | TweakValues; requireFetch: boolean }> { + // // TweakValueService.checkAndAskUseRemoteConfiguration + // throwShouldBeOverridden(); + // } - $$askUseRemoteConfiguration( - trialSetting: RemoteDBSettings, - preferred: TweakValues - ): Promise<{ result: false | TweakValues; requireFetch: boolean }> { - throwShouldBeOverridden(); - } - $everyBeforeReplicate(showMessage: boolean): Promise { - return InterceptiveEvery; - } + // $$askUseRemoteConfiguration( + // trialSetting: RemoteDBSettings, + // preferred: TweakValues + // ): Promise<{ result: false | TweakValues; requireFetch: boolean }> { + // // TweakValueService.askUseRemoteConfiguration + // throwShouldBeOverridden(); + // } + // $everyBeforeReplicate(showMessage: boolean): Promise { + // // ReplicationService.beforeReplicate + // return InterceptiveEvery; + // } - $$canReplicate(showMessage: boolean = false): Promise { - throwShouldBeOverridden(); - } + // $$canReplicate(showMessage: boolean = false): Promise { + // // ReplicationService.isReplicationReady + // throwShouldBeOverridden(); + // } - $$replicate(showMessage: boolean = false): Promise { - throwShouldBeOverridden(); - } - $$replicateByEvent(showMessage: boolean = false): Promise { - throwShouldBeOverridden(); - } + // $$replicate(showMessage: boolean = false): Promise { + // // ReplicationService.replicate + // throwShouldBeOverridden(); + // } + // $$replicateByEvent(showMessage: boolean = false): Promise { + // // ReplicationService.replicateByEvent + // throwShouldBeOverridden(); + // } - $everyOnDatabaseInitialized(showingNotice: boolean): Promise { - throwShouldBeOverridden(); - } + // $everyOnDatabaseInitialized(showingNotice: boolean): Promise { + // // DatabaseEventService.onDatabaseInitialised + // throwShouldBeOverridden(); + // } - $$initializeDatabase( - showingNotice: boolean = false, - reopenDatabase = true, - ignoreSuspending: boolean = false - ): Promise { - throwShouldBeOverridden(); - } + // $$initializeDatabase( + // showingNotice: boolean = false, + // reopenDatabase = true, + // ignoreSuspending: boolean = false + // ): Promise { + // // DatabaseEventService.initializeDatabase + // throwShouldBeOverridden(); + // } - $anyAfterConnectCheckFailed(): Promise { - return InterceptiveAny; - } + // $anyAfterConnectCheckFailed(): Promise { + // // ReplicationService.checkConnectionFailure + // return InterceptiveAny; + // } - $$replicateAllToServer( - showingNotice: boolean = false, - sendChunksInBulkDisabled: boolean = false - ): Promise { - throwShouldBeOverridden(); - } - $$replicateAllFromServer(showingNotice: boolean = false): Promise { - throwShouldBeOverridden(); - } + // $$replicateAllToServer( + // showingNotice: boolean = false, + // sendChunksInBulkDisabled: boolean = false + // ): Promise { + // // RemoteService.replicateAllToRemote + // throwShouldBeOverridden(); + // } + // $$replicateAllFromServer(showingNotice: boolean = false): Promise { + // // RemoteService.replicateAllFromRemote + // throwShouldBeOverridden(); + // } // Remote Governing - $$markRemoteLocked(lockByClean: boolean = false): Promise { - throwShouldBeOverridden(); - } + // $$markRemoteLocked(lockByClean: boolean = false): Promise { + // // RemoteService.markLocked; + // throwShouldBeOverridden(); + // } - $$markRemoteUnlocked(): Promise { - throwShouldBeOverridden(); - } + // $$markRemoteUnlocked(): Promise { + // // RemoteService.markUnlocked; + // throwShouldBeOverridden(); + // } - $$markRemoteResolved(): Promise { - throwShouldBeOverridden(); - } + // $$markRemoteResolved(): Promise { + // // RemoteService.markResolved; + // throwShouldBeOverridden(); + // } // <-- Remote Governing - $$isFileSizeExceeded(size: number): boolean { - throwShouldBeOverridden(); - } + // $$isFileSizeExceeded(size: number): boolean { + // // VaultService.isFileSizeTooLarge + // throwShouldBeOverridden(); + // } - $$performFullScan(showingNotice?: boolean, ignoreSuspending?: boolean): Promise { - throwShouldBeOverridden(); - } + // $$performFullScan(showingNotice?: boolean, ignoreSuspending?: boolean): Promise { + // // VaultService.scanVault + // throwShouldBeOverridden(); + // } - $anyResolveConflictByUI( - filename: FilePathWithPrefix, - conflictCheckResult: diff_result - ): Promise { - return InterceptiveAny; - } - $$resolveConflictByDeletingRev( - path: FilePathWithPrefix, - deleteRevision: string, - subTitle = "" - ): Promise { - throwShouldBeOverridden(); - } - $$resolveConflict(filename: FilePathWithPrefix): Promise { - throwShouldBeOverridden(); - } - $anyResolveConflictByNewest(filename: FilePathWithPrefix): Promise { - throwShouldBeOverridden(); - } + // $anyResolveConflictByUI( + // filename: FilePathWithPrefix, + // conflictCheckResult: diff_result + // ): Promise { + // // ConflictService.resolveConflictByUserInteraction + // return InterceptiveAny; + // } + // $$resolveConflictByDeletingRev( + // path: FilePathWithPrefix, + // deleteRevision: string, + // subTitle = "" + // ): Promise { + // // ConflictService.resolveByDeletingRevision + // throwShouldBeOverridden(); + // } + // $$resolveConflict(filename: FilePathWithPrefix): Promise { + // // ConflictService.resolveConflict + // throwShouldBeOverridden(); + // } + // $anyResolveConflictByNewest(filename: FilePathWithPrefix): Promise { + // // ConflictService.resolveByNewest + // throwShouldBeOverridden(); + // } - $$resetLocalDatabase(): Promise { - throwShouldBeOverridden(); - } + // $$resetLocalDatabase(): Promise { + // // DatabaseService.resetDatabase; + // throwShouldBeOverridden(); + // } - $$tryResetRemoteDatabase(): Promise { - throwShouldBeOverridden(); - } + // $$tryResetRemoteDatabase(): Promise { + // // RemoteService.tryResetDatabase; + // throwShouldBeOverridden(); + // } - $$tryCreateRemoteDatabase(): Promise { - throwShouldBeOverridden(); - } + // $$tryCreateRemoteDatabase(): Promise { + // // RemoteService.tryCreateDatabase; + // throwShouldBeOverridden(); + // } - $$isIgnoredByIgnoreFiles(file: string | UXFileInfoStub): Promise { - throwShouldBeOverridden(); - } + // $$isIgnoredByIgnoreFiles(file: string | UXFileInfoStub): Promise { + // // VaultService.isIgnoredByIgnoreFiles + // throwShouldBeOverridden(); + // } - $$isTargetFile(file: string | UXFileInfoStub, keepFileCheckList = false): Promise { - throwShouldBeOverridden(); - } + // $$isTargetFile(file: string | UXFileInfoStub, keepFileCheckList = false): Promise { + // // VaultService.isTargetFile + // throwShouldBeOverridden(); + // } - $$askReload(message?: string) { - throwShouldBeOverridden(); - } - $$scheduleAppReload() { - throwShouldBeOverridden(); - } + // $$askReload(message?: string) { + // // AppLifecycleService.askRestart + // throwShouldBeOverridden(); + // } + // $$scheduleAppReload() { + // // AppLifecycleService.scheduleRestart + // throwShouldBeOverridden(); + // } //--- Setup - $allSuspendAllSync(): Promise { - return InterceptiveAll; - } - $allSuspendExtraSync(): Promise { - return InterceptiveAll; - } + // $allSuspendAllSync(): Promise { + // // SettingEventManager.suspendAllSync + // return InterceptiveAll; + // } + // $allSuspendExtraSync(): Promise { + // // SettingEventManager.suspendExtraSync + // return InterceptiveAll; + // } - $allAskUsingOptionalSyncFeature(opt: { enableFetch?: boolean; enableOverwrite?: boolean }): Promise { - throwShouldBeOverridden(); - } - $anyConfigureOptionalSyncFeature(mode: string): Promise { - throwShouldBeOverridden(); - } + // $allAskUsingOptionalSyncFeature(opt: { enableFetch?: boolean; enableOverwrite?: boolean }): Promise { + // // SettingEventManager.suggestOptionalFeatures + // throwShouldBeOverridden(); + // } + // $anyConfigureOptionalSyncFeature(mode: string): Promise { + // // SettingEventManager.enableOptionalFeature + // throwShouldBeOverridden(); + // } - $$showView(viewType: string): Promise { - throwShouldBeOverridden(); - } + // $$showView(viewType: string): Promise { + // // UIManager.showWindow // + // throwShouldBeOverridden(); + // } // For Development: Ensure reliability MORE AND MORE. May the this plug-in helps all of us. - $everyModuleTest(): Promise { - return InterceptiveEvery; - } - $everyModuleTestMultiDevice(): Promise { - return InterceptiveEvery; - } - $$addTestResult(name: string, key: string, result: boolean, summary?: string, message?: string): void { - throwShouldBeOverridden(); - } + // $everyModuleTest(): Promise { + // return InterceptiveEvery; + // } + // $everyModuleTestMultiDevice(): Promise { + // return InterceptiveEvery; + // } + // $$addTestResult(name: string, key: string, result: boolean, summary?: string, message?: string): void { + // throwShouldBeOverridden(); + // } - _isThisModuleEnabled(): boolean { - return true; - } + // _isThisModuleEnabled(): boolean { + // return true; + // } - $anyGetAppId(): Promise { - return InterceptiveAny; - } + // $anyGetAppId(): Promise { + // // APIService.getAppId + // return InterceptiveAny; + // } // Plug-in's overrideable functions onload() { - void this.$$onLiveSyncLoad(); + void this.services.appLifecycle.onLoad(); } async saveSettings() { - await this.$$saveSettingData(); + await this.services.setting.saveSettingData(); } onunload() { - return void this.$$onLiveSyncUnload(); + return void this.services.appLifecycle.onAppUnload(); } // <-- Plug-in's overrideable functions } diff --git a/src/modules/AbstractModule.ts b/src/modules/AbstractModule.ts index 0860ad5..64ad6e4 100644 --- a/src/modules/AbstractModule.ts +++ b/src/modules/AbstractModule.ts @@ -1,34 +1,34 @@ import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, Logger } from "octagonal-wheels/common/logger"; import type { LOG_LEVEL } from "../lib/src/common/types"; import type { LiveSyncCore } from "../main"; -import { unique } from "octagonal-wheels/collection"; -import type { IObsidianModule } from "./AbstractObsidianModule.ts"; -import type { - ICoreModuleBase, - AllInjectableProps, - AllExecuteProps, - EveryExecuteProps, - AnyExecuteProps, - ICoreModule, -} from "./ModuleTypes"; +// import { unique } from "octagonal-wheels/collection"; +// import type { IObsidianModule } from "./AbstractObsidianModule.ts"; +// import type { +// ICoreModuleBase, +// AllInjectableProps, +// AllExecuteProps, +// EveryExecuteProps, +// AnyExecuteProps, +// ICoreModule, +// } from "./ModuleTypes"; -function isOverridableKey(key: string): key is keyof ICoreModuleBase { - return key.startsWith("$"); -} +// function isOverridableKey(key: string): key is keyof ICoreModuleBase { +// return key.startsWith("$"); +// } -function isInjectableKey(key: string): key is keyof AllInjectableProps { - return key.startsWith("$$"); -} +// function isInjectableKey(key: string): key is keyof AllInjectableProps { +// return key.startsWith("$$"); +// } -function isAllExecuteKey(key: string): key is keyof AllExecuteProps { - return key.startsWith("$all"); -} -function isEveryExecuteKey(key: string): key is keyof EveryExecuteProps { - return key.startsWith("$every"); -} -function isAnyExecuteKey(key: string): key is keyof AnyExecuteProps { - return key.startsWith("$any"); -} +// function isAllExecuteKey(key: string): key is keyof AllExecuteProps { +// return key.startsWith("$all"); +// } +// function isEveryExecuteKey(key: string): key is keyof EveryExecuteProps { +// return key.startsWith("$every"); +// } +// function isAnyExecuteKey(key: string): key is keyof AnyExecuteProps { +// return key.startsWith("$any"); +// } /** * All $prefixed functions are hooked by the modules. Be careful to call them directly. * Please refer to the module's source code to understand the function. @@ -39,100 +39,100 @@ function isAnyExecuteKey(key: string): key is keyof AnyExecuteProps { * $ : Other interceptive points. You should manually assign the module * All of above performed on injectModules function. */ -export function injectModules(target: T, modules: ICoreModule[]) { - const allKeys = unique([ - ...Object.keys(Object.getOwnPropertyDescriptors(target)), - ...Object.keys(Object.getOwnPropertyDescriptors(Object.getPrototypeOf(target))), - ]).filter((e) => e.startsWith("$")) as (keyof ICoreModule)[]; - const moduleMap = new Map(); - for (const module of modules) { - for (const key of allKeys) { - if (isOverridableKey(key)) { - if (key in module) { - const list = moduleMap.get(key) || []; - if (typeof module[key] === "function") { - module[key] = module[key].bind(module) as any; - } - list.push(module); - moduleMap.set(key, list); - } - } - } - } - Logger(`Injecting modules for ${target.constructor.name}`, LOG_LEVEL_VERBOSE); - for (const key of allKeys) { - const modules = moduleMap.get(key) || []; - if (isInjectableKey(key)) { - if (modules.length == 0) { - throw new Error(`No module injected for ${key}. This is a fatal error.`); - } - target[key] = modules[0][key]! as any; - Logger(`[${modules[0].constructor.name}]: Injected ${key} `, LOG_LEVEL_VERBOSE); - } else if (isAllExecuteKey(key)) { - const modules = moduleMap.get(key) || []; - target[key] = async (...args: any) => { - for (const module of modules) { - try { - //@ts-ignore - await module[key]!(...args); - } catch (ex) { - Logger(`[${module.constructor.name}]: All handler for ${key} failed`, LOG_LEVEL_VERBOSE); - Logger(ex, LOG_LEVEL_VERBOSE); - } - } - return true; - }; - for (const module of modules) { - Logger(`[${module.constructor.name}]: Injected (All) ${key} `, LOG_LEVEL_VERBOSE); - } - } else if (isEveryExecuteKey(key)) { - target[key] = async (...args: any) => { - for (const module of modules) { - try { - //@ts-ignore:2556 - const ret = await module[key]!(...args); - if (ret !== undefined && !ret) { - // Failed then return that falsy value. - return ret; - } - } catch (ex) { - Logger(`[${module.constructor.name}]: Every handler for ${key} failed`); - Logger(ex, LOG_LEVEL_VERBOSE); - } - } - return true; - }; - for (const module of modules) { - Logger(`[${module.constructor.name}]: Injected (Every) ${key} `, LOG_LEVEL_VERBOSE); - } - } else if (isAnyExecuteKey(key)) { - //@ts-ignore - target[key] = async (...args: any[]) => { - for (const module of modules) { - try { - //@ts-ignore:2556 - const ret = await module[key](...args); - // If truly value returned, then return that value. - if (ret) { - return ret; - } - } catch (ex) { - Logger(`[${module.constructor.name}]: Any handler for ${key} failed`); - Logger(ex, LOG_LEVEL_VERBOSE); - } - } - return false; - }; - for (const module of modules) { - Logger(`[${module.constructor.name}]: Injected (Any) ${key} `, LOG_LEVEL_VERBOSE); - } - } else { - Logger(`No injected handler for ${key} `, LOG_LEVEL_VERBOSE); - } - } - Logger(`Injected modules for ${target.constructor.name}`, LOG_LEVEL_VERBOSE); - return true; -} +// export function injectModules(target: T, modules: ICoreModule[]) { +// const allKeys = unique([ +// ...Object.keys(Object.getOwnPropertyDescriptors(target)), +// ...Object.keys(Object.getOwnPropertyDescriptors(Object.getPrototypeOf(target))), +// ]).filter((e) => e.startsWith("$")) as (keyof ICoreModule)[]; +// const moduleMap = new Map(); +// for (const module of modules) { +// for (const key of allKeys) { +// if (isOverridableKey(key)) { +// if (key in module) { +// const list = moduleMap.get(key) || []; +// if (typeof module[key] === "function") { +// module[key] = module[key].bind(module) as any; +// } +// list.push(module); +// moduleMap.set(key, list); +// } +// } +// } +// } +// Logger(`Injecting modules for ${target.constructor.name}`, LOG_LEVEL_VERBOSE); +// for (const key of allKeys) { +// const modules = moduleMap.get(key) || []; +// if (isInjectableKey(key)) { +// if (modules.length == 0) { +// throw new Error(`No module injected for ${key}. This is a fatal error.`); +// } +// target[key] = modules[0][key]! as any; +// Logger(`[${modules[0].constructor.name}]: Injected ${key} `, LOG_LEVEL_VERBOSE); +// } else if (isAllExecuteKey(key)) { +// const modules = moduleMap.get(key) || []; +// target[key] = async (...args: any) => { +// for (const module of modules) { +// try { +// //@ts-ignore +// await module[key]!(...args); +// } catch (ex) { +// Logger(`[${module.constructor.name}]: All handler for ${key} failed`, LOG_LEVEL_VERBOSE); +// Logger(ex, LOG_LEVEL_VERBOSE); +// } +// } +// return true; +// }; +// for (const module of modules) { +// Logger(`[${module.constructor.name}]: Injected (All) ${key} `, LOG_LEVEL_VERBOSE); +// } +// } else if (isEveryExecuteKey(key)) { +// target[key] = async (...args: any) => { +// for (const module of modules) { +// try { +// //@ts-ignore:2556 +// const ret = await module[key]!(...args); +// if (ret !== undefined && !ret) { +// // Failed then return that falsy value. +// return ret; +// } +// } catch (ex) { +// Logger(`[${module.constructor.name}]: Every handler for ${key} failed`); +// Logger(ex, LOG_LEVEL_VERBOSE); +// } +// } +// return true; +// }; +// for (const module of modules) { +// Logger(`[${module.constructor.name}]: Injected (Every) ${key} `, LOG_LEVEL_VERBOSE); +// } +// } else if (isAnyExecuteKey(key)) { +// //@ts-ignore +// target[key] = async (...args: any[]) => { +// for (const module of modules) { +// try { +// //@ts-ignore:2556 +// const ret = await module[key](...args); +// // If truly value returned, then return that value. +// if (ret) { +// return ret; +// } +// } catch (ex) { +// Logger(`[${module.constructor.name}]: Any handler for ${key} failed`); +// Logger(ex, LOG_LEVEL_VERBOSE); +// } +// } +// return false; +// }; +// for (const module of modules) { +// Logger(`[${module.constructor.name}]: Injected (Any) ${key} `, LOG_LEVEL_VERBOSE); +// } +// } else { +// Logger(`No injected handler for ${key} `, LOG_LEVEL_VERBOSE); +// } +// } +// Logger(`Injected modules for ${target.constructor.name}`, LOG_LEVEL_VERBOSE); +// return true; +// } export abstract class AbstractModule { _log = (msg: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key?: string) => { @@ -153,14 +153,17 @@ export abstract class AbstractModule { this.core.settings = value; } + onBindFunction(core: LiveSyncCore, services: typeof core.services) { + // Override if needed. + } constructor(public core: LiveSyncCore) { + this.onBindFunction(core, core.services); Logger(`[${this.constructor.name}] Loaded`, LOG_LEVEL_VERBOSE); } saveSettings = this.core.saveSettings.bind(this.core); - // abstract $everyTest(): Promise; addTestResult(key: string, value: boolean, summary?: string, message?: string) { - this.core.$$addTestResult(`${this.constructor.name}`, key, value, summary, message); + this.services.test.addTestResult(`${this.constructor.name}`, key, value, summary, message); } testDone(result: boolean = true) { return Promise.resolve(result); @@ -185,4 +188,8 @@ export abstract class AbstractModule { } return this.testDone(); } + + get services() { + return this.core._services; + } } diff --git a/src/modules/AbstractObsidianModule.ts b/src/modules/AbstractObsidianModule.ts index 99a4d1f..23021a9 100644 --- a/src/modules/AbstractObsidianModule.ts +++ b/src/modules/AbstractObsidianModule.ts @@ -38,13 +38,13 @@ export abstract class AbstractObsidianModule extends AbstractModule { saveSettings = this.plugin.saveSettings.bind(this.plugin); _isMainReady() { - return this.core.$$isReady(); + return this.services.appLifecycle.isReady(); } _isMainSuspended() { - return this.core.$$isSuspended(); + return this.services.appLifecycle.isSuspended(); } _isDatabaseReady() { - return this.core.$$isDatabaseReady(); + return this.services.database.isDatabaseReady(); } //should be overridden diff --git a/src/modules/core/ModuleDatabaseFileAccess.ts b/src/modules/core/ModuleDatabaseFileAccess.ts index 1f0d36a..a026487 100644 --- a/src/modules/core/ModuleDatabaseFileAccess.ts +++ b/src/modules/core/ModuleDatabaseFileAccess.ts @@ -17,7 +17,6 @@ import type { DocumentID, } from "../../lib/src/common/types"; import type { DatabaseFileAccess } from "../interfaces/DatabaseFileAccess"; -import { type IObsidianModule } from "../AbstractObsidianModule.ts"; import { isPlainText, shouldBeIgnored, stripAllPrefixes } from "../../lib/src/string_and_binary/path"; import { createBlob, @@ -30,14 +29,15 @@ import { import { serialized } from "octagonal-wheels/concurrency/lock"; import { AbstractModule } from "../AbstractModule.ts"; import { ICHeader } from "../../common/types.ts"; +import type { LiveSyncCore } from "../../main.ts"; -export class ModuleDatabaseFileAccess extends AbstractModule implements IObsidianModule, DatabaseFileAccess { - $everyOnload(): Promise { +export class ModuleDatabaseFileAccess extends AbstractModule implements DatabaseFileAccess { + private _everyOnload(): Promise { this.core.databaseFileAccess = this; return Promise.resolve(true); } - async $everyModuleTest(): Promise { + private async _everyModuleTest(): Promise { if (!this.settings.enableDebugTools) return Promise.resolve(true); const testString = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nec purus nec nunc"; // Before test, we need to delete completely. @@ -75,7 +75,7 @@ export class ModuleDatabaseFileAccess extends AbstractModule implements IObsidia async checkIsTargetFile(file: UXFileInfoStub | FilePathWithPrefix): Promise { const path = getStoragePathFromUXFileInfo(file); - if (!(await this.core.$$isTargetFile(path))) { + if (!(await this.services.vault.isTargetFile(path))) { this._log(`File is not target`, LOG_LEVEL_VERBOSE); return false; } @@ -177,7 +177,7 @@ export class ModuleDatabaseFileAccess extends AbstractModule implements IObsidia } } - const idMain = await this.core.$$path2id(fullPath); + const idMain = await this.services.path.path2id(fullPath); const id = (idPrefix + idMain) as DocumentID; const d: SavingEntry = { @@ -345,4 +345,8 @@ export class ModuleDatabaseFileAccess extends AbstractModule implements IObsidia eventHub.emitEvent(EVENT_FILE_SAVED); return ret; } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this)); + services.test.handleTest(this._everyModuleTest.bind(this)); + } } diff --git a/src/modules/core/ModuleFileHandler.ts b/src/modules/core/ModuleFileHandler.ts index c293d2d..ca48540 100644 --- a/src/modules/core/ModuleFileHandler.ts +++ b/src/modules/core/ModuleFileHandler.ts @@ -20,11 +20,11 @@ import { } from "../../common/utils"; import { getDocDataAsArray, isDocContentSame, readAsBlob, readContent } from "../../lib/src/common/utils"; import { shouldBeIgnored } from "../../lib/src/string_and_binary/path"; -import type { ICoreModule } from "../ModuleTypes"; import { Semaphore } from "octagonal-wheels/concurrency/semaphore"; import { eventHub } from "../../common/events.ts"; +import type { LiveSyncCore } from "../../main.ts"; -export class ModuleFileHandler extends AbstractModule implements ICoreModule { +export class ModuleFileHandler extends AbstractModule { get db() { return this.core.databaseFileAccess; } @@ -32,7 +32,7 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule { return this.core.storageAccess; } - $everyOnloadStart(): Promise { + _everyOnloadStart(): Promise { this.core.fileHandler = this; return Promise.resolve(true); } @@ -52,7 +52,7 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule { info: UXFileInfoStub | UXFileInfo | UXInternalFileInfoStub | FilePathWithPrefix, force: boolean = false, onlyChunks: boolean = false - ): Promise { + ): Promise { const file = typeof info === "string" ? this.storage.getFileStub(info) : info; if (file == null) { this._log(`File ${info} is not exist on the storage`, LOG_LEVEL_VERBOSE); @@ -125,7 +125,7 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule { } } - async deleteFileFromDB(info: UXFileInfoStub | UXInternalFileInfoStub | FilePath): Promise { + async deleteFileFromDB(info: UXFileInfoStub | UXInternalFileInfoStub | FilePath): Promise { const file = typeof info === "string" ? this.storage.getFileStub(info) : info; if (file == null) { this._log(`File ${info} is not exist on the storage`, LOG_LEVEL_VERBOSE); @@ -222,7 +222,7 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule { // NO OP } else { // If not, then it should be checked. and will be processed later (i.e., after the conflict is resolved). - await this.core.$$queueConflictCheckIfOpen(path); + await this.services.conflict.queueCheckForIfOpen(path); return true; } } @@ -313,11 +313,11 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule { return ret; } - async $anyHandlerProcessesFileEvent(item: FileEventItem): Promise { + private async _anyHandlerProcessesFileEvent(item: FileEventItem): Promise { const eventItem = item.args; const type = item.type; const path = eventItem.file.path; - if (!(await this.core.$$isTargetFile(path))) { + if (!(await this.services.vault.isTargetFile(path))) { this._log(`File ${path} is not the target file`, LOG_LEVEL_VERBOSE); return false; } @@ -343,9 +343,9 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule { }); } - async $anyProcessReplicatedDoc(entry: MetaEntry): Promise { + async _anyProcessReplicatedDoc(entry: MetaEntry): Promise { return await serialized(entry.path, async () => { - if (!(await this.core.$$isTargetFile(entry.path))) { + if (!(await this.services.vault.isTargetFile(entry.path))) { this._log(`File ${entry.path} is not the target file`, LOG_LEVEL_VERBOSE); return false; } @@ -362,7 +362,7 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule { return true; } else { this._log( - `Processing ${path} (${entry._id.substring(0, 8)}: ${entry._rev?.substring(0, 5)}) :Started...`, + `Processing ${path} (${entry._id.substring(0, 8)} :${entry._rev?.substring(0, 5)}) : Started...`, LOG_LEVEL_VERBOSE ); // Before writing (or skipped ), merging dialogue should be cancelled. @@ -391,7 +391,7 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule { }; const total = filesStorageSrc.length; const procAllChunks = filesStorageSrc.map(async (file) => { - if (!(await this.core.$$isTargetFile(file))) { + if (!(await this.services.vault.isTargetFile(file))) { incProcessed(); return true; } @@ -416,4 +416,9 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule { "chunkCreation" ); } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this)); + services.fileProcessing.handleProcessFileEvent(this._anyHandlerProcessesFileEvent.bind(this)); + services.replication.handleProcessSynchroniseResult(this._anyProcessReplicatedDoc.bind(this)); + } } diff --git a/src/modules/core/ModuleLocalDatabaseObsidian.ts b/src/modules/core/ModuleLocalDatabaseObsidian.ts index b0e7463..09d0ef3 100644 --- a/src/modules/core/ModuleLocalDatabaseObsidian.ts +++ b/src/modules/core/ModuleLocalDatabaseObsidian.ts @@ -2,18 +2,18 @@ import { $msg } from "../../lib/src/common/i18n"; import { LiveSyncLocalDB } from "../../lib/src/pouchdb/LiveSyncLocalDB.ts"; import { initializeStores } from "../../common/stores.ts"; import { AbstractModule } from "../AbstractModule.ts"; -import type { ICoreModule } from "../ModuleTypes.ts"; import { LiveSyncManagers } from "../../lib/src/managers/LiveSyncManagers.ts"; +import type { LiveSyncCore } from "../../main.ts"; -export class ModuleLocalDatabaseObsidian extends AbstractModule implements ICoreModule { - $everyOnloadStart(): Promise { +export class ModuleLocalDatabaseObsidian extends AbstractModule { + _everyOnloadStart(): Promise { return Promise.resolve(true); } - async $$openDatabase(): Promise { + private async _openDatabase(): Promise { if (this.localDatabase != null) { await this.localDatabase.close(); } - const vaultName = this.core.$$getVaultName(); + const vaultName = this.services.vault.getVaultName(); this._log($msg("moduleLocalDatabase.logWaitingForReady")); const getDB = () => this.core.localDatabase.localDatabase; const getSettings = () => this.core.settings; @@ -22,8 +22,9 @@ export class ModuleLocalDatabaseObsidian extends AbstractModule implements ICore return getDB(); }, getActiveReplicator: () => this.core.replicator, - id2path: this.core.$$id2path.bind(this.core), - path2id: this.core.$$path2id.bind(this.core), + id2path: this.services.path.id2path, + // path2id: this.core.$$path2id.bind(this.core), + path2id: this.services.path.path2id, get settings() { return getSettings(); }, @@ -34,7 +35,12 @@ export class ModuleLocalDatabaseObsidian extends AbstractModule implements ICore return await this.localDatabase.initializeDatabase(); } - $$isDatabaseReady(): boolean { + _isDatabaseReady(): boolean { return this.localDatabase != null && this.localDatabase.isReady; } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.database.handleIsDatabaseReady(this._isDatabaseReady.bind(this)); + services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this)); + services.database.handleOpenDatabase(this._openDatabase.bind(this)); + } } diff --git a/src/modules/core/ModulePeriodicProcess.ts b/src/modules/core/ModulePeriodicProcess.ts index f8f8dd3..080ac91 100644 --- a/src/modules/core/ModulePeriodicProcess.ts +++ b/src/modules/core/ModulePeriodicProcess.ts @@ -1,9 +1,9 @@ import { PeriodicProcessor } from "../../common/utils"; +import type { LiveSyncCore } from "../../main"; import { AbstractModule } from "../AbstractModule"; -import type { ICoreModule } from "../ModuleTypes"; -export class ModulePeriodicProcess extends AbstractModule implements ICoreModule { - periodicSyncProcessor = new PeriodicProcessor(this.core, async () => await this.core.$$replicate()); +export class ModulePeriodicProcess extends AbstractModule { + periodicSyncProcessor = new PeriodicProcessor(this.core, async () => await this.services.replication.replicate()); _disablePeriodic() { this.periodicSyncProcessor?.disable(); @@ -15,19 +15,27 @@ export class ModulePeriodicProcess extends AbstractModule implements ICoreModule ); return Promise.resolve(true); } - $allOnUnload() { + private _allOnUnload() { return this._disablePeriodic(); } - $everyBeforeRealizeSetting(): Promise { + _everyBeforeRealizeSetting(): Promise { return this._disablePeriodic(); } - $everyBeforeSuspendProcess(): Promise { + _everyBeforeSuspendProcess(): Promise { return this._disablePeriodic(); } - $everyAfterResumeProcess(): Promise { + _everyAfterResumeProcess(): Promise { return this._resumePeriodic(); } - $everyAfterRealizeSetting(): Promise { + _everyAfterRealizeSetting(): Promise { return this._resumePeriodic(); } + + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.appLifecycle.handleOnUnload(this._allOnUnload.bind(this)); + services.setting.handleBeforeRealiseSetting(this._everyBeforeRealizeSetting.bind(this)); + services.setting.handleSettingRealised(this._everyAfterRealizeSetting.bind(this)); + services.appLifecycle.handleOnSuspending(this._everyBeforeSuspendProcess.bind(this)); + services.appLifecycle.handleOnResumed(this._everyAfterResumeProcess.bind(this)); + } } diff --git a/src/modules/core/ModulePouchDB.ts b/src/modules/core/ModulePouchDB.ts index 61a8d38..3b156ac 100644 --- a/src/modules/core/ModulePouchDB.ts +++ b/src/modules/core/ModulePouchDB.ts @@ -1,9 +1,9 @@ import { AbstractModule } from "../AbstractModule"; -import type { ICoreModule } from "../ModuleTypes"; import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser"; +import type { LiveSyncCore } from "../../main"; -export class ModulePouchDB extends AbstractModule implements ICoreModule { - $$createPouchDBInstance( +export class ModulePouchDB extends AbstractModule { + _createPouchDBInstance( name?: string, options?: PouchDB.Configuration.DatabaseConfiguration ): PouchDB.Database { @@ -16,4 +16,7 @@ export class ModulePouchDB extends AbstractModule implements ICoreModule { } return new PouchDB(name, optionPass); } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.database.handleCreatePouchDBInstance(this._createPouchDBInstance.bind(this)); + } } diff --git a/src/modules/core/ModuleRebuilder.ts b/src/modules/core/ModuleRebuilder.ts index 1979e81..c60705f 100644 --- a/src/modules/core/ModuleRebuilder.ts +++ b/src/modules/core/ModuleRebuilder.ts @@ -9,13 +9,13 @@ import { } from "../../lib/src/common/types.ts"; import { AbstractModule } from "../AbstractModule.ts"; import type { Rebuilder } from "../interfaces/DatabaseRebuilder.ts"; -import type { ICoreModule } from "../ModuleTypes.ts"; import type { LiveSyncCouchDBReplicator } from "../../lib/src/replication/couchdb/LiveSyncReplicator.ts"; import { fetchAllUsedChunks } from "@/lib/src/pouchdb/chunks.ts"; import { EVENT_DATABASE_REBUILT, eventHub } from "src/common/events.ts"; +import type { LiveSyncCore } from "../../main.ts"; -export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebuilder { - $everyOnload(): Promise { +export class ModuleRebuilder extends AbstractModule implements Rebuilder { + private _everyOnload(): Promise { this.core.rebuilder = this; return Promise.resolve(true); } @@ -43,47 +43,47 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu { title: "Enable extra features", defaultOption: "No", timeout: 15 } )) == "yes" ) { - await this.core.$allAskUsingOptionalSyncFeature(opt); + await this.services.setting.suggestOptionalFeatures(opt); } } async rebuildRemote() { - await this.core.$allSuspendExtraSync(); + await this.services.setting.suspendExtraSync(); this.core.settings.isConfigured = true; - await this.core.$$realizeSettingSyncMode(); - await this.core.$$markRemoteLocked(); - await this.core.$$tryResetRemoteDatabase(); - await this.core.$$markRemoteLocked(); + await this.services.setting.onRealiseSetting(); + await this.services.remote.markLocked(); + await this.services.remote.tryResetDatabase(); + await this.services.remote.markLocked(); await delay(500); await this.askUsingOptionalFeature({ enableOverwrite: true }); await delay(1000); - await this.core.$$replicateAllToServer(true); + await this.services.remote.replicateAllToRemote(true); await delay(1000); - await this.core.$$replicateAllToServer(true, true); + await this.services.remote.replicateAllToRemote(true, true); } $rebuildRemote(): Promise { return this.rebuildRemote(); } async rebuildEverything() { - await this.core.$allSuspendExtraSync(); + await this.services.setting.suspendExtraSync(); await this.askUseNewAdapter(); this.core.settings.isConfigured = true; - await this.core.$$realizeSettingSyncMode(); + await this.services.setting.onRealiseSetting(); await this.resetLocalDatabase(); await delay(1000); - await this.core.$$initializeDatabase(true, true, true); - await this.core.$$markRemoteLocked(); - await this.core.$$tryResetRemoteDatabase(); - await this.core.$$markRemoteLocked(); + await this.services.databaseEvents.initialiseDatabase(true, true, true); + await this.services.remote.markLocked(); + await this.services.remote.tryResetDatabase(); + await this.services.remote.markLocked(); await delay(500); // We do not have any other devices' data, so we do not need to ask for overwriting. await this.askUsingOptionalFeature({ enableOverwrite: false }); await delay(1000); - await this.core.$$replicateAllToServer(true); + await this.services.remote.replicateAllToRemote(true); await delay(1000); - await this.core.$$replicateAllToServer(true, true); + await this.services.remote.replicateAllToRemote(true, true); } $rebuildEverything(): Promise { @@ -101,7 +101,7 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu this._log("Could not create red_flag_rebuild.md", LOG_LEVEL_NOTICE); this._log(ex, LOG_LEVEL_VERBOSE); } - this.core.$$performRestart(); + this.services.appLifecycle.performRestart(); } async scheduleFetch(): Promise { try { @@ -110,20 +110,20 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu this._log("Could not create red_flag_fetch.md", LOG_LEVEL_NOTICE); this._log(ex, LOG_LEVEL_VERBOSE); } - this.core.$$performRestart(); + this.services.appLifecycle.performRestart(); } - async $$tryResetRemoteDatabase(): Promise { + private async _tryResetRemoteDatabase(): Promise { await this.core.replicator.tryResetRemoteDatabase(this.settings); } - async $$tryCreateRemoteDatabase(): Promise { + private async _tryCreateRemoteDatabase(): Promise { await this.core.replicator.tryCreateRemoteDatabase(this.settings); } - async $$resetLocalDatabase(): Promise { + private async _resetLocalDatabase(): Promise { this.core.storageAccess.clearTouched(); - await this.localDatabase.resetDatabase(); + return await this.localDatabase.resetDatabase(); } async suspendAllSync() { @@ -134,7 +134,7 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu this.core.settings.syncOnStart = false; this.core.settings.syncOnFileOpen = false; this.core.settings.syncAfterMerge = false; - await this.core.$allSuspendExtraSync(); + await this.services.setting.suspendExtraSync(); } async suspendReflectingDatabase() { if (this.core.settings.doNotSuspendOnFetching) return; @@ -153,8 +153,8 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu this._log(`Database and storage reflection has been resumed!`, LOG_LEVEL_NOTICE); this.core.settings.suspendParseReplicationResult = false; this.core.settings.suspendFileWatching = false; - await this.core.$$performFullScan(true); - await this.core.$everyBeforeReplicate(false); //TODO: Check actual need of this. + await this.services.vault.scanVault(true); + await this.services.replication.onBeforeReplicate(false); //TODO: Check actual need of this. await this.core.saveSettings(); } async askUseNewAdapter() { @@ -177,28 +177,28 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu } } async fetchLocal(makeLocalChunkBeforeSync?: boolean, preventMakeLocalFilesBeforeSync?: boolean) { - await this.core.$allSuspendExtraSync(); + await this.services.setting.suspendExtraSync(); await this.askUseNewAdapter(); this.core.settings.isConfigured = true; await this.suspendReflectingDatabase(); - await this.core.$$realizeSettingSyncMode(); + await this.services.setting.onRealiseSetting(); await this.resetLocalDatabase(); await delay(1000); - await this.core.$$openDatabase(); + await this.services.database.openDatabase(); // this.core.isReady = true; - this.core.$$markIsReady(); + this.services.appLifecycle.markIsReady(); if (makeLocalChunkBeforeSync) { await this.core.fileHandler.createAllChunks(true); } else if (!preventMakeLocalFilesBeforeSync) { - await this.core.$$initializeDatabase(true, true, true); + await this.services.databaseEvents.initialiseDatabase(true, true, true); } else { // Do not create local file entries before sync (Means use remote information) } - await this.core.$$markRemoteResolved(); + await this.services.remote.markResolved(); await delay(500); - await this.core.$$replicateAllFromServer(true); + await this.services.remote.replicateAllFromRemote(true); await delay(1000); - await this.core.$$replicateAllFromServer(true); + await this.services.remote.replicateAllFromRemote(true); await this.resumeReflectingDatabase(); await this.askUsingOptionalFeature({ enableFetch: true }); } @@ -206,7 +206,7 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu return await this.fetchLocal(true); } - async $allSuspendAllSync(): Promise { + private async _allSuspendAllSync(): Promise { await this.suspendAllSync(); return true; } @@ -214,11 +214,11 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu async resetLocalDatabase() { if (this.core.settings.isConfigured && this.core.settings.additionalSuffixOfDatabaseName == "") { // Discard the non-suffixed database - await this.core.$$resetLocalDatabase(); + await this.services.database.resetDatabase(); } - const suffix = (await this.core.$anyGetAppId()) || ""; + const suffix = this.services.API.getAppID() || ""; this.core.settings.additionalSuffixOfDatabaseName = suffix; - await this.core.$$resetLocalDatabase(); + await this.services.database.resetDatabase(); eventHub.emitEvent(EVENT_DATABASE_REBUILT); } async fetchRemoteChunks() { @@ -228,10 +228,10 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu this.core.settings.remoteType == REMOTE_COUCHDB ) { this._log(`Fetching chunks`, LOG_LEVEL_NOTICE); - const replicator = this.core.$$getReplicator() as LiveSyncCouchDBReplicator; + const replicator = this.services.replicator.getActiveReplicator() as LiveSyncCouchDBReplicator; const remoteDB = await replicator.connectRemoteCouchDBWithSetting( this.settings, - this.core.$$isMobile(), + this.services.API.isMobile(), true ); if (typeof remoteDB == "string") { @@ -254,8 +254,15 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu LOG_LEVEL_NOTICE, "resolveAllConflictedFilesByNewerOnes" ); - await this.core.$anyResolveConflictByNewest(file); + await this.services.conflict.resolveByNewest(file); } this._log(`Done!`, LOG_LEVEL_NOTICE, "resolveAllConflictedFilesByNewerOnes"); } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this)); + services.database.handleResetDatabase(this._resetLocalDatabase.bind(this)); + services.remote.handleTryResetDatabase(this._tryResetRemoteDatabase.bind(this)); + services.remote.handleTryCreateDatabase(this._tryCreateRemoteDatabase.bind(this)); + services.setting.handleSuspendAllSync(this._allSuspendAllSync.bind(this)); + } } diff --git a/src/modules/core/ModuleReplicator.ts b/src/modules/core/ModuleReplicator.ts index 712a50e..6340d99 100644 --- a/src/modules/core/ModuleReplicator.ts +++ b/src/modules/core/ModuleReplicator.ts @@ -1,7 +1,6 @@ import { fireAndForget, yieldMicrotask } from "octagonal-wheels/promises"; import type { LiveSyncLocalDB } from "../../lib/src/pouchdb/LiveSyncLocalDB"; import { AbstractModule } from "../AbstractModule"; -import type { ICoreModule } from "../ModuleTypes"; import { Logger, LOG_LEVEL_NOTICE, LOG_LEVEL_INFO, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger"; import { isLockAcquired, shareRunningResult, skipIfDuplicated } from "octagonal-wheels/concurrency/lock"; import { balanceChunkPurgedDBs } from "@/lib/src/pouchdb/chunks"; @@ -34,17 +33,18 @@ import type { LiveSyncAbstractReplicator } from "../../lib/src/replication/LiveS import { $msg } from "../../lib/src/common/i18n"; import { clearHandlers } from "../../lib/src/replication/SyncParamsHandler"; +import type { LiveSyncCore } from "../../main"; const KEY_REPLICATION_ON_EVENT = "replicationOnEvent"; const REPLICATION_ON_EVENT_FORECASTED_TIME = 5000; -export class ModuleReplicator extends AbstractModule implements ICoreModule { +export class ModuleReplicator extends AbstractModule { _replicatorType?: RemoteType; - $everyOnloadAfterLoadSettings(): Promise { + private _everyOnloadAfterLoadSettings(): Promise { eventHub.onEvent(EVENT_FILE_SAVED, () => { - if (this.settings.syncOnSave && !this.core.$$isSuspended()) { - scheduleTask("perform-replicate-after-save", 250, () => this.core.$$replicateByEvent()); + if (this.settings.syncOnSave && !this.core.services.appLifecycle.isSuspended()) { + scheduleTask("perform-replicate-after-save", 250, () => this.services.replication.replicateByEvent()); } }); eventHub.onEvent(EVENT_SETTING_SAVED, (setting) => { @@ -57,7 +57,7 @@ export class ModuleReplicator extends AbstractModule implements ICoreModule { } async setReplicator() { - const replicator = await this.core.$anyNewReplicator(); + const replicator = await this.services.replicator.getNewReplicator(); if (!replicator) { this._log($msg("Replicator.Message.InitialiseFatalError"), LOG_LEVEL_NOTICE); return false; @@ -74,24 +74,28 @@ export class ModuleReplicator extends AbstractModule implements ICoreModule { return true; } - $$getReplicator(): LiveSyncAbstractReplicator { + _getReplicator(): LiveSyncAbstractReplicator { return this.core.replicator; } - $everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise { + _everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise { return this.setReplicator(); } - $everyOnResetDatabase(db: LiveSyncLocalDB): Promise { + _everyOnResetDatabase(db: LiveSyncLocalDB): Promise { return this.setReplicator(); } async ensureReplicatorPBKDF2Salt(showMessage: boolean = false): Promise { // Checking salt - const replicator = this.core.$$getReplicator(); + const replicator = this.services.replicator.getActiveReplicator(); + if (!replicator) { + this._log($msg("Replicator.Message.InitialiseFatalError"), LOG_LEVEL_NOTICE); + return false; + } return await replicator.ensurePBKDF2Salt(this.settings, showMessage, true); } - async $everyBeforeReplicate(showMessage: boolean): Promise { + async _everyBeforeReplicate(showMessage: boolean): Promise { // Checking salt if (!this.core.managers.networkManager.isOnline) { this._log("Network is offline", showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO); @@ -106,7 +110,7 @@ export class ModuleReplicator extends AbstractModule implements ICoreModule { return true; } - async $$replicate(showMessage: boolean = false): Promise { + private async _replicate(showMessage: boolean = false): Promise { try { updatePreviousExecutionTime(KEY_REPLICATION_ON_EVENT, REPLICATION_ON_EVENT_FORECASTED_TIME); return await this.$$_replicate(showMessage); @@ -143,11 +147,11 @@ Even if you choose to clean up, you will see this option again if you exit Obsid await this.core.rebuilder.$performRebuildDB("localOnly"); } if (ret == CHOICE_CLEAN) { - const replicator = this.core.$$getReplicator(); + const replicator = this.services.replicator.getActiveReplicator(); if (!(replicator instanceof LiveSyncCouchDBReplicator)) return; const remoteDB = await replicator.connectRemoteCouchDBWithSetting( this.settings, - this.core.$$isMobile(), + this.services.API.isMobile(), true ); if (typeof remoteDB == "string") { @@ -162,7 +166,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid await balanceChunkPurgedDBs(this.localDatabase.localDatabase, remoteDB.db); await purgeUnreferencedChunks(this.localDatabase.localDatabase, false); this.localDatabase.clearCaches(); - await this.core.$$getReplicator().markRemoteResolved(this.settings); + await this.services.replicator.getActiveReplicator()?.markRemoteResolved(this.settings); Logger("The local database has been cleaned up.", showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO); } else { Logger( @@ -174,8 +178,8 @@ Even if you choose to clean up, you will see this option again if you exit Obsid }); } - async $$canReplicate(showMessage: boolean = false): Promise { - if (!this.core.$$isReady()) { + async _canReplicate(showMessage: boolean = false): Promise { + if (!this.services.appLifecycle.isReady()) { Logger(`Not ready`); return false; } @@ -190,7 +194,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid return false; } - if (!(await this.core.$everyCommitPendingFileEvent())) { + if (!(await this.services.fileProcessing.commitPendingFileEvents())) { Logger($msg("Replicator.Message.Pending"), LOG_LEVEL_NOTICE); return false; } @@ -199,7 +203,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid this._log("Network is offline", showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO); return false; } - if (!(await this.core.$everyBeforeReplicate(showMessage))) { + if (!(await this.services.replication.onBeforeReplicate(showMessage))) { Logger($msg("Replicator.Message.SomeModuleFailed"), LOG_LEVEL_NOTICE); return false; } @@ -207,14 +211,14 @@ Even if you choose to clean up, you will see this option again if you exit Obsid } async $$_replicate(showMessage: boolean = false): Promise { - const checkBeforeReplicate = await this.$$canReplicate(showMessage); + const checkBeforeReplicate = await this.services.replication.isReplicationReady(showMessage); if (!checkBeforeReplicate) return false; //<-- Here could be an module. const ret = await this.core.replicator.openReplication(this.settings, false, showMessage, false); if (!ret) { if (this.core.replicator.tweakSettingsMismatched && this.core.replicator.preferredTweakValue) { - await this.core.$$askResolvingMismatchedTweaks(this.core.replicator.preferredTweakValue); + await this.services.tweakValue.askResolvingMismatched(this.core.replicator.preferredTweakValue); } else { if (this.core.replicator?.remoteLockedAndDeviceNotAccepted) { if (this.core.replicator.remoteCleaned && this.settings.useIndexedDBAdapter) { @@ -236,7 +240,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid if (ret == CHOICE_FETCH) { this._log($msg("Replicator.Dialogue.Locked.Message.Fetch"), LOG_LEVEL_NOTICE); await this.core.rebuilder.scheduleFetch(); - this.core.$$scheduleAppReload(); + this.services.appLifecycle.scheduleRestart(); return; } else if (ret == CHOICE_UNLOCK) { await this.core.replicator.markRemoteResolved(this.settings); @@ -250,16 +254,16 @@ Even if you choose to clean up, you will see this option again if you exit Obsid return ret; } - async $$replicateByEvent(): Promise { + private async _replicateByEvent(): Promise { const least = this.settings.syncMinimumInterval; if (least > 0) { return rateLimitedSharedExecution(KEY_REPLICATION_ON_EVENT, least, async () => { - return await this.$$replicate(); + return await this.services.replication.replicate(); }); } - return await shareRunningResult(`replication`, () => this.core.$$replicate()); + return await shareRunningResult(`replication`, () => this.services.replication.replicate()); } - $$parseReplicationResult(docs: Array>): void { + _parseReplicationResult(docs: Array>): void { if (this.settings.suspendParseReplicationResult && !this.replicationResultProcessor.isSuspended) { this.replicationResultProcessor.suspend(); } @@ -336,7 +340,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid this.localDatabase.onNewLeaf(change as EntryLeaf); return; } - if (await this.core.$anyModuleParsedReplicationResultItem(change)) return; + if (await this.services.replication.processVirtualDocument(change)) return; // any addon needs this item? // for (const proc of this.core.addOns) { // if (await proc.parseReplicationResultItem(change)) { @@ -361,7 +365,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid } if (isAnyNote(change)) { const docPath = getPath(change); - if (!(await this.core.$$isTargetFile(docPath))) { + if (!(await this.services.vault.isTargetFile(docPath))) { Logger(`Skipped: ${docPath}`, LOG_LEVEL_VERBOSE); return; } @@ -369,7 +373,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid Logger(`Processing scheduled: ${docPath}`, LOG_LEVEL_INFO); } const size = change.size; - if (this.core.$$isFileSizeExceeded(size)) { + if (this.services.vault.isFileSizeTooLarge(size)) { Logger( `Processing ${docPath} has been skipped due to file size exceeding the limit`, LOG_LEVEL_NOTICE @@ -413,7 +417,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid return; } - if (await this.core.$anyProcessOptionalSyncFiles(dbDoc)) { + if (await this.services.replication.processOptionalSynchroniseResult(dbDoc)) { // Already processed } else if (isValidPath(getPath(doc))) { this.storageApplyingProcessor.enqueue(doc as MetaEntry); @@ -440,7 +444,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid storageApplyingProcessor = new QueueProcessor( async (docs: MetaEntry[]) => { const entry = docs[0]; - await this.core.$anyProcessReplicatedDoc(entry); + await this.services.replication.processSynchroniseResult(entry); return; }, { @@ -458,17 +462,17 @@ Even if you choose to clean up, you will see this option again if you exit Obsid }) .startPipeline(); - $everyBeforeSuspendProcess(): Promise { - this.core.replicator.closeReplication(); + _everyBeforeSuspendProcess(): Promise { + this.core.replicator?.closeReplication(); return Promise.resolve(true); } - async $$replicateAllToServer( + private async _replicateAllToServer( showingNotice: boolean = false, sendChunksInBulkDisabled: boolean = false ): Promise { - if (!this.core.$$isReady()) return false; - if (!(await this.core.$everyBeforeReplicate(showingNotice))) { + if (!this.services.appLifecycle.isReady()) return false; + if (!(await this.services.replication.onBeforeReplicate(showingNotice))) { Logger($msg("Replicator.Message.SomeModuleFailed"), LOG_LEVEL_NOTICE); return false; } @@ -486,16 +490,31 @@ Even if you choose to clean up, you will see this option again if you exit Obsid } const ret = await this.core.replicator.replicateAllToServer(this.settings, showingNotice); if (ret) return true; - const checkResult = await this.core.$anyAfterConnectCheckFailed(); - if (checkResult == "CHECKAGAIN") return await this.core.$$replicateAllToServer(showingNotice); + const checkResult = await this.services.replication.checkConnectionFailure(); + if (checkResult == "CHECKAGAIN") return await this.services.remote.replicateAllToRemote(showingNotice); return !checkResult; } - async $$replicateAllFromServer(showingNotice: boolean = false): Promise { - if (!this.core.$$isReady()) return false; + async _replicateAllFromServer(showingNotice: boolean = false): Promise { + if (!this.services.appLifecycle.isReady()) return false; const ret = await this.core.replicator.replicateAllFromServer(this.settings, showingNotice); if (ret) return true; - const checkResult = await this.core.$anyAfterConnectCheckFailed(); - if (checkResult == "CHECKAGAIN") return await this.core.$$replicateAllFromServer(showingNotice); + const checkResult = await this.services.replication.checkConnectionFailure(); + if (checkResult == "CHECKAGAIN") return await this.services.remote.replicateAllFromRemote(showingNotice); return !checkResult; } + + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.replicator.handleGetActiveReplicator(this._getReplicator.bind(this)); + services.databaseEvents.handleOnDatabaseInitialisation(this._everyOnInitializeDatabase.bind(this)); + services.databaseEvents.handleOnResetDatabase(this._everyOnResetDatabase.bind(this)); + services.appLifecycle.handleOnSettingLoaded(this._everyOnloadAfterLoadSettings.bind(this)); + services.replication.handleParseSynchroniseResult(this._parseReplicationResult.bind(this)); + services.appLifecycle.handleOnSuspending(this._everyBeforeSuspendProcess.bind(this)); + services.replication.handleBeforeReplicate(this._everyBeforeReplicate.bind(this)); + services.replication.handleIsReplicationReady(this._canReplicate.bind(this)); + services.replication.handleReplicate(this._replicate.bind(this)); + services.replication.handleReplicateByEvent(this._replicateByEvent.bind(this)); + services.remote.handleReplicateAllToRemote(this._replicateAllToServer.bind(this)); + services.remote.handleReplicateAllFromRemote(this._replicateAllFromServer.bind(this)); + } } diff --git a/src/modules/core/ModuleReplicatorCouchDB.ts b/src/modules/core/ModuleReplicatorCouchDB.ts index b5d836c..2e7a126 100644 --- a/src/modules/core/ModuleReplicatorCouchDB.ts +++ b/src/modules/core/ModuleReplicatorCouchDB.ts @@ -3,20 +3,20 @@ import { REMOTE_MINIO, REMOTE_P2P, type RemoteDBSettings } from "../../lib/src/c import { LiveSyncCouchDBReplicator } from "../../lib/src/replication/couchdb/LiveSyncReplicator"; import type { LiveSyncAbstractReplicator } from "../../lib/src/replication/LiveSyncAbstractReplicator"; import { AbstractModule } from "../AbstractModule"; -import type { ICoreModule } from "../ModuleTypes"; +import type { LiveSyncCore } from "../../main"; -export class ModuleReplicatorCouchDB extends AbstractModule implements ICoreModule { - $anyNewReplicator(settingOverride: Partial = {}): Promise { +export class ModuleReplicatorCouchDB extends AbstractModule { + _anyNewReplicator(settingOverride: Partial = {}): Promise { const settings = { ...this.settings, ...settingOverride }; // If new remote types were added, add them here. Do not use `REMOTE_COUCHDB` directly for the safety valve. if (settings.remoteType == REMOTE_MINIO || settings.remoteType == REMOTE_P2P) { - return undefined!; + return Promise.resolve(false); } return Promise.resolve(new LiveSyncCouchDBReplicator(this.core)); } - $everyAfterResumeProcess(): Promise { - if (!this.core.$$isSuspended) return Promise.resolve(true); - if (!this.core.$$isReady) return Promise.resolve(true); + _everyAfterResumeProcess(): Promise { + if (!this.services.appLifecycle.isSuspended()) return Promise.resolve(true); + if (!this.services.appLifecycle.isReady()) return Promise.resolve(true); if (this.settings.remoteType != REMOTE_MINIO && this.settings.remoteType != REMOTE_P2P) { const LiveSyncEnabled = this.settings.liveSync; const continuous = LiveSyncEnabled; @@ -27,7 +27,7 @@ export class ModuleReplicatorCouchDB extends AbstractModule implements ICoreModu // And note that we do not open the conflict detection dialogue directly during this process. // This should be raised explicitly if needed. fireAndForget(async () => { - const canReplicate = await this.core.$$canReplicate(false); + const canReplicate = await this.services.replication.isReplicationReady(false); if (!canReplicate) return; void this.core.replicator.openReplication(this.settings, continuous, false, false); }); @@ -36,4 +36,8 @@ export class ModuleReplicatorCouchDB extends AbstractModule implements ICoreModu return Promise.resolve(true); } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.replicator.handleGetNewReplicator(this._anyNewReplicator.bind(this)); + services.appLifecycle.handleOnResumed(this._everyAfterResumeProcess.bind(this)); + } } diff --git a/src/modules/core/ModuleReplicatorMinIO.ts b/src/modules/core/ModuleReplicatorMinIO.ts index 1ad65c6..188db57 100644 --- a/src/modules/core/ModuleReplicatorMinIO.ts +++ b/src/modules/core/ModuleReplicatorMinIO.ts @@ -1,15 +1,18 @@ import { REMOTE_MINIO, type RemoteDBSettings } from "../../lib/src/common/types"; import { LiveSyncJournalReplicator } from "../../lib/src/replication/journal/LiveSyncJournalReplicator"; import type { LiveSyncAbstractReplicator } from "../../lib/src/replication/LiveSyncAbstractReplicator"; +import type { LiveSyncCore } from "../../main"; import { AbstractModule } from "../AbstractModule"; -import type { ICoreModule } from "../ModuleTypes"; -export class ModuleReplicatorMinIO extends AbstractModule implements ICoreModule { - $anyNewReplicator(settingOverride: Partial = {}): Promise { +export class ModuleReplicatorMinIO extends AbstractModule { + _anyNewReplicator(settingOverride: Partial = {}): Promise { const settings = { ...this.settings, ...settingOverride }; if (settings.remoteType == REMOTE_MINIO) { return Promise.resolve(new LiveSyncJournalReplicator(this.core)); } - return undefined!; + return Promise.resolve(false); + } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.replicator.handleGetNewReplicator(this._anyNewReplicator.bind(this)); } } diff --git a/src/modules/core/ModuleReplicatorP2P.ts b/src/modules/core/ModuleReplicatorP2P.ts index 1779f96..699671f 100644 --- a/src/modules/core/ModuleReplicatorP2P.ts +++ b/src/modules/core/ModuleReplicatorP2P.ts @@ -1,18 +1,18 @@ import { REMOTE_P2P, type RemoteDBSettings } from "../../lib/src/common/types"; import type { LiveSyncAbstractReplicator } from "../../lib/src/replication/LiveSyncAbstractReplicator"; import { AbstractModule } from "../AbstractModule"; -import type { ICoreModule } from "../ModuleTypes"; import { LiveSyncTrysteroReplicator } from "../../lib/src/replication/trystero/LiveSyncTrysteroReplicator"; +import type { LiveSyncCore } from "../../main"; -export class ModuleReplicatorP2P extends AbstractModule implements ICoreModule { - $anyNewReplicator(settingOverride: Partial = {}): Promise { +export class ModuleReplicatorP2P extends AbstractModule { + _anyNewReplicator(settingOverride: Partial = {}): Promise { const settings = { ...this.settings, ...settingOverride }; if (settings.remoteType == REMOTE_P2P) { return Promise.resolve(new LiveSyncTrysteroReplicator(this.core)); } - return undefined!; + return Promise.resolve(false); } - $everyAfterResumeProcess(): Promise { + _everyAfterResumeProcess(): Promise { if (this.settings.remoteType == REMOTE_P2P) { // // If LiveSync enabled, open replication // if (this.settings.liveSync) { @@ -27,4 +27,8 @@ export class ModuleReplicatorP2P extends AbstractModule implements ICoreModule { return Promise.resolve(true); } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.replicator.handleGetNewReplicator(this._anyNewReplicator.bind(this)); + services.appLifecycle.handleOnResumed(this._everyAfterResumeProcess.bind(this)); + } } diff --git a/src/modules/core/ModuleTargetFilter.ts b/src/modules/core/ModuleTargetFilter.ts index 5d7b5d5..bffcbc8 100644 --- a/src/modules/core/ModuleTargetFilter.ts +++ b/src/modules/core/ModuleTargetFilter.ts @@ -18,14 +18,14 @@ import { } from "../../lib/src/common/types"; import { addPrefix, isAcceptedAll } from "../../lib/src/string_and_binary/path"; import { AbstractModule } from "../AbstractModule"; -import type { ICoreModule } from "../ModuleTypes"; import { EVENT_REQUEST_RELOAD_SETTING_TAB, EVENT_SETTING_SAVED, eventHub } from "../../common/events"; import { isDirty } from "../../lib/src/common/utils"; -export class ModuleTargetFilter extends AbstractModule implements ICoreModule { +import type { LiveSyncCore } from "../../main"; +export class ModuleTargetFilter extends AbstractModule { reloadIgnoreFiles() { this.ignoreFiles = this.settings.ignoreFiles.split(",").map((e) => e.trim()); } - $everyOnload(): Promise { + private _everyOnload(): Promise { eventHub.onEvent(EVENT_SETTING_SAVED, (evt: ObsidianLiveSyncSettings) => { this.reloadIgnoreFiles(); }); @@ -35,7 +35,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule { return Promise.resolve(true); } - $$id2path(id: DocumentID, entry?: EntryHasPath, stripPrefix?: boolean): FilePathWithPrefix { + _id2path(id: DocumentID, entry?: EntryHasPath, stripPrefix?: boolean): FilePathWithPrefix { const tempId = id2path(id, entry); if (stripPrefix && isInternalMetadata(tempId)) { const out = stripInternalMetadataPrefix(tempId); @@ -43,7 +43,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule { } return tempId; } - async $$path2id(filename: FilePathWithPrefix | FilePath, prefix?: string): Promise { + async _path2id(filename: FilePathWithPrefix | FilePath, prefix?: string): Promise { const destPath = addPrefix(filename, prefix ?? ""); return await path2id( destPath, @@ -52,7 +52,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule { ); } - $$isFileSizeExceeded(size: number) { + private _isFileSizeExceeded(size: number) { if (this.settings.syncMaxSizeInMB > 0 && size > 0) { if (this.settings.syncMaxSizeInMB * 1024 * 1024 < size) { return true; @@ -61,7 +61,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule { return false; } - $$markFileListPossiblyChanged(): void { + _markFileListPossiblyChanged(): void { this.totalFileEventCount++; } totalFileEventCount = 0; @@ -72,7 +72,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule { return false; } - async $$isTargetFile(file: string | UXFileInfoStub, keepFileCheckList = false) { + private async _isTargetFile(file: string | UXFileInfoStub, keepFileCheckList = false) { const fileCount = useMemo>( { key: "fileCount", // forceUpdate: !keepFileCheckList, @@ -109,7 +109,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule { const filepath = getStoragePathFromUXFileInfo(file); const lc = filepath.toLowerCase(); - if (this.core.$$shouldCheckCaseInsensitive()) { + if (this.services.setting.shouldCheckCaseInsensitively()) { if (lc in fileCount && fileCount[lc] > 1) { return false; } @@ -120,7 +120,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule { // We must reload ignore files due to the its change. await this.readIgnoreFile(filepath); } - if (await this.core.$$isIgnoredByIgnoreFiles(file)) { + if (await this.services.vault.isIgnoredByIgnoreFile(file)) { return false; } } @@ -150,7 +150,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule { return await this.readIgnoreFile(path); } } - async $$isIgnoredByIgnoreFiles(file: string | UXFileInfoStub): Promise { + private async _isIgnoredByIgnoreFiles(file: string | UXFileInfoStub): Promise { if (!this.settings.useIgnoreFiles) { return false; } @@ -164,4 +164,14 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule { } return false; } + + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.vault.handleMarkFileListPossiblyChanged(this._markFileListPossiblyChanged.bind(this)); + services.path.handleId2Path(this._id2path.bind(this)); + services.path.handlePath2Id(this._path2id.bind(this)); + services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this)); + services.vault.handleIsFileSizeTooLarge(this._isFileSizeExceeded.bind(this)); + services.vault.handleIsIgnoredByIgnoreFile(this._isIgnoredByIgnoreFiles.bind(this)); + services.vault.handleIsTargetFile(this._isTargetFile.bind(this)); + } } diff --git a/src/modules/coreFeatures/ModuleCheckRemoteSize.ts b/src/modules/coreFeatures/ModuleCheckRemoteSize.ts index 798f3f5..55363ed 100644 --- a/src/modules/coreFeatures/ModuleCheckRemoteSize.ts +++ b/src/modules/coreFeatures/ModuleCheckRemoteSize.ts @@ -1,11 +1,11 @@ import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger"; import { AbstractModule } from "../AbstractModule.ts"; import { sizeToHumanReadable } from "octagonal-wheels/number"; -import type { ICoreModule } from "../ModuleTypes.ts"; import { $msg } from "src/lib/src/common/i18n.ts"; +import type { LiveSyncCore } from "../../main.ts"; -export class ModuleCheckRemoteSize extends AbstractModule implements ICoreModule { - async $allScanStat(): Promise { +export class ModuleCheckRemoteSize extends AbstractModule { + async _allScanStat(): Promise { if (this.core.managers.networkManager.isOnline === false) { this._log("Network is offline, skipping remote size check.", LOG_LEVEL_INFO); return true; @@ -109,4 +109,7 @@ export class ModuleCheckRemoteSize extends AbstractModule implements ICoreModule } return true; } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.appLifecycle.handleOnScanningStartupIssues(this._allScanStat.bind(this)); + } } diff --git a/src/modules/coreFeatures/ModuleConflictChecker.ts b/src/modules/coreFeatures/ModuleConflictChecker.ts index 46d201b..3bc30ea 100644 --- a/src/modules/coreFeatures/ModuleConflictChecker.ts +++ b/src/modules/coreFeatures/ModuleConflictChecker.ts @@ -2,35 +2,36 @@ import { AbstractModule } from "../AbstractModule.ts"; import { LOG_LEVEL_NOTICE, type FilePathWithPrefix } from "../../lib/src/common/types"; import { QueueProcessor } from "octagonal-wheels/concurrency/processor"; import { sendValue } from "octagonal-wheels/messagepassing/signal"; -import type { ICoreModule } from "../ModuleTypes.ts"; +import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts"; +import type { LiveSyncCore } from "../../main.ts"; -export class ModuleConflictChecker extends AbstractModule implements ICoreModule { - async $$queueConflictCheckIfOpen(file: FilePathWithPrefix): Promise { +export class ModuleConflictChecker extends AbstractModule { + async _queueConflictCheckIfOpen(file: FilePathWithPrefix): Promise { const path = file; if (this.settings.checkConflictOnlyOnOpen) { - const af = this.core.$$getActiveFilePath(); + const af = this.services.vault.getActiveFilePath(); if (af && af != path) { this._log(`${file} is conflicted, merging process has been postponed.`, LOG_LEVEL_NOTICE); return; } } - await this.core.$$queueConflictCheck(path); + await this.services.conflict.queueCheckFor(path); } - async $$queueConflictCheck(file: FilePathWithPrefix): Promise { - const optionalConflictResult = await this.core.$anyGetOptionalConflictCheckMethod(file); + async _queueConflictCheck(file: FilePathWithPrefix): Promise { + const optionalConflictResult = await this.services.conflict.getOptionalConflictCheckMethod(file); if (optionalConflictResult == true) { // The conflict has been resolved by another process. return; } else if (optionalConflictResult === "newer") { // The conflict should be resolved by the newer entry. - await this.core.$anyResolveConflictByNewest(file); + await this.services.conflict.resolveByNewest(file); } else { this.conflictCheckQueue.enqueue(file); } } - $$waitForAllConflictProcessed(): Promise { + _waitForAllConflictProcessed(): Promise { return this.conflictResolveQueue.waitForAllProcessed(); } @@ -38,7 +39,7 @@ export class ModuleConflictChecker extends AbstractModule implements ICoreModule conflictResolveQueue = new QueueProcessor( async (filenames: FilePathWithPrefix[]) => { const filename = filenames[0]; - return await this.core.$$resolveConflict(filename); + return await this.services.conflict.resolve(filename); }, { suspended: false, @@ -73,4 +74,9 @@ export class ModuleConflictChecker extends AbstractModule implements ICoreModule totalRemainingReactiveSource: this.core.conflictProcessQueueCount, } ); + onBindFunction(core: LiveSyncCore, services: InjectableServiceHub): void { + services.conflict.handleQueueCheckForIfOpen(this._queueConflictCheckIfOpen.bind(this)); + services.conflict.handleQueueCheckFor(this._queueConflictCheck.bind(this)); + services.conflict.handleEnsureAllProcessed(this._waitForAllConflictProcessed.bind(this)); + } } diff --git a/src/modules/coreFeatures/ModuleConflictResolver.ts b/src/modules/coreFeatures/ModuleConflictResolver.ts index ca35d9f..5d4046a 100644 --- a/src/modules/coreFeatures/ModuleConflictResolver.ts +++ b/src/modules/coreFeatures/ModuleConflictResolver.ts @@ -20,8 +20,9 @@ import { } from "../../common/utils"; import diff_match_patch from "diff-match-patch"; import { stripAllPrefixes, isPlainText } from "../../lib/src/string_and_binary/path"; -import type { ICoreModule } from "../ModuleTypes.ts"; import { eventHub } from "../../common/events.ts"; +import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts"; +import type { LiveSyncCore } from "../../main.ts"; declare global { interface LSEvents { @@ -29,8 +30,8 @@ declare global { } } -export class ModuleConflictResolver extends AbstractModule implements ICoreModule { - async $$resolveConflictByDeletingRev( +export class ModuleConflictResolver extends AbstractModule { + private async _resolveConflictByDeletingRev( path: FilePathWithPrefix, deleteRevision: string, subTitle = "" @@ -82,7 +83,7 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul return MISSING_OR_ERROR; } // 2. As usual, delete the conflicted revision and if there are no conflicts, write the resolved content to the storage. - return await this.core.$$resolveConflictByDeletingRev(path, ret.conflictedRev, "Sensible"); + return await this.services.conflict.resolveByDeletingRevision(path, ret.conflictedRev, "Sensible"); } const { rightRev, leftLeaf, rightLeaf } = ret; @@ -95,7 +96,7 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul } if (rightLeaf == false) { // Conflicted item could not load, delete this. - return await this.core.$$resolveConflictByDeletingRev(path, rightRev, "MISSING OLD REV"); + return await this.services.conflict.resolveByDeletingRevision(path, rightRev, "MISSING OLD REV"); } const isSame = leftLeaf.data == rightLeaf.data && leftLeaf.deleted == rightLeaf.deleted; @@ -115,7 +116,7 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul ] .filter((e) => e.trim()) .join(","); - return await this.core.$$resolveConflictByDeletingRev(path, loser.rev, subTitle); + return await this.services.conflict.resolveByDeletingRevision(path, loser.rev, subTitle); } // make diff. const dmp = new diff_match_patch(); @@ -129,7 +130,7 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul }; } - async $$resolveConflict(filename: FilePathWithPrefix): Promise { + private async _resolveConflict(filename: FilePathWithPrefix): Promise { // const filename = filenames[0]; return await serialized(`conflict-resolve:${filename}`, async () => { const conflictCheckResult = await this.checkConflictAndPerformAutoMerge(filename); @@ -144,16 +145,16 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul } if (conflictCheckResult === AUTO_MERGED) { //auto resolved, but need check again; - if (this.settings.syncAfterMerge && !this.core.$$isSuspended()) { + if (this.settings.syncAfterMerge && !this.services.appLifecycle.isSuspended()) { //Wait for the running replication, if not running replication, run it once. - await this.core.$$replicateByEvent(); + await this.services.replication.replicateByEvent(); } this._log("[conflict] Automatically merged, but we have to check it again"); - await this.core.$$queueConflictCheck(filename); + await this.services.conflict.queueCheckFor(filename); return; } if (this.settings.showMergeDialogOnlyOnActive) { - const af = this.core.$$getActiveFilePath(); + const af = this.services.vault.getActiveFilePath(); if (af && af != filename) { this._log( `[conflict] ${filename} is conflicted. Merging process has been postponed to the file have got opened.`, @@ -164,11 +165,11 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul } this._log("[conflict] Manual merge required!"); eventHub.emitEvent("conflict-cancelled", filename); - await this.core.$anyResolveConflictByUI(filename, conflictCheckResult); + await this.services.conflict.resolveByUserInteraction(filename, conflictCheckResult); }); } - async $anyResolveConflictByNewest(filename: FilePathWithPrefix): Promise { + private async _anyResolveConflictByNewest(filename: FilePathWithPrefix): Promise { const currentRev = await this.core.databaseFileAccess.fetchEntryMeta(filename, undefined, true); if (currentRev == false) { this._log(`Could not get current revision of ${filename}`); @@ -206,8 +207,14 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul this._log( `conflict: Deleting the older revision ${mTimeAndRev[i][1]} (${new Date(mTimeAndRev[i][0]).toLocaleString()}) of ${filename}` ); - await this.core.$$resolveConflictByDeletingRev(filename, mTimeAndRev[i][1], "NEWEST"); + await this.services.conflict.resolveByDeletingRevision(filename, mTimeAndRev[i][1], "NEWEST"); } return true; } + + onBindFunction(core: LiveSyncCore, services: InjectableServiceHub): void { + services.conflict.handleResolveByDeletingRevision(this._resolveConflictByDeletingRev.bind(this)); + services.conflict.handleResolve(this._resolveConflict.bind(this)); + services.conflict.handleResolveByNewest(this._anyResolveConflictByNewest.bind(this)); + } } diff --git a/src/modules/coreFeatures/ModuleRedFlag.ts b/src/modules/coreFeatures/ModuleRedFlag.ts index 3a4f915..31e0b53 100644 --- a/src/modules/coreFeatures/ModuleRedFlag.ts +++ b/src/modules/coreFeatures/ModuleRedFlag.ts @@ -9,10 +9,10 @@ import { type ObsidianLiveSyncSettings, } from "../../lib/src/common/types.ts"; import { AbstractModule } from "../AbstractModule.ts"; -import type { ICoreModule } from "../ModuleTypes.ts"; import { $msg } from "../../lib/src/common/i18n.ts"; +import type { LiveSyncCore } from "../../main.ts"; -export class ModuleRedFlag extends AbstractModule implements ICoreModule { +export class ModuleRedFlag extends AbstractModule { async isFlagFileExist(path: string) { const redflag = await this.core.storageAccess.isExists(normalizePath(path)); if (redflag) { @@ -48,7 +48,7 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule { await this.deleteFlagFile(FLAGMD_REDFLAG3); await this.deleteFlagFile(FLAGMD_REDFLAG3_HR); } - async $everyOnLayoutReady(): Promise { + async _everyOnLayoutReady(): Promise { try { const isRedFlagRaised = await this.isRedFlagRaised(); const isRedFlag2Raised = await this.isRedFlag2Raised(); @@ -63,7 +63,7 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule { )) !== "yes" ) { await this.deleteRedFlag2(); - await this.core.$$performRestart(); + this.services.appLifecycle.performRestart(); return false; } } @@ -75,13 +75,13 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule { })) !== "yes" ) { await this.deleteRedFlag3(); - await this.core.$$performRestart(); + this.services.appLifecycle.performRestart(); return false; } } this.settings.batchSave = false; - await this.core.$allSuspendAllSync(); - await this.core.$allSuspendExtraSync(); + await this.services.setting.suspendAllSync(); + await this.services.setting.suspendExtraSync(); this.settings.suspendFileWatching = true; await this.saveSettings(); if (isRedFlag2Raised) { @@ -99,7 +99,7 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule { ) { this.settings.suspendFileWatching = false; await this.saveSettings(); - this.core.$$performRestart(); + this.services.appLifecycle.performRestart(); return false; } } else if (isRedFlag3Raised) { @@ -148,7 +148,7 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule { if (fetchRemote === optionFetchRemoteConf) { this._log("Fetching remote configuration", LOG_LEVEL_NOTICE); const newSettings = JSON.parse(JSON.stringify(this.core.settings)) as ObsidianLiveSyncSettings; - const remoteConfig = await this.core.$$fetchRemotePreferredTweakValues(newSettings); + const remoteConfig = await this.services.tweakValue.fetchRemotePreferred(newSettings); if (remoteConfig) { this._log("Remote configuration found.", LOG_LEVEL_NOTICE); const mergedSettings = { @@ -174,7 +174,7 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule { ) { this.settings.suspendFileWatching = false; await this.saveSettings(); - this.core.$$performRestart(); + this.services.appLifecycle.performRestart(); return false; } } else { @@ -198,4 +198,8 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule { } return true; } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + super.onBindFunction(core, services); + services.appLifecycle.handleLayoutReady(this._everyOnLayoutReady.bind(this)); + } } diff --git a/src/modules/coreFeatures/ModuleRemoteGovernor.ts b/src/modules/coreFeatures/ModuleRemoteGovernor.ts index 6f3cbbf..18c33b7 100644 --- a/src/modules/coreFeatures/ModuleRemoteGovernor.ts +++ b/src/modules/coreFeatures/ModuleRemoteGovernor.ts @@ -1,16 +1,22 @@ +import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts"; +import type { LiveSyncCore } from "../../main.ts"; import { AbstractModule } from "../AbstractModule.ts"; -import type { ICoreModule } from "../ModuleTypes.ts"; -export class ModuleRemoteGovernor extends AbstractModule implements ICoreModule { - async $$markRemoteLocked(lockByClean: boolean = false): Promise { +export class ModuleRemoteGovernor extends AbstractModule { + private async _markRemoteLocked(lockByClean: boolean = false): Promise { return await this.core.replicator.markRemoteLocked(this.settings, true, lockByClean); } - async $$markRemoteUnlocked(): Promise { + private async _markRemoteUnlocked(): Promise { return await this.core.replicator.markRemoteLocked(this.settings, false, false); } - async $$markRemoteResolved(): Promise { + private async _markRemoteResolved(): Promise { return await this.core.replicator.markRemoteResolved(this.settings); } + onBindFunction(core: LiveSyncCore, services: InjectableServiceHub): void { + services.remote.handleMarkLocked(this._markRemoteLocked.bind(this)); + services.remote.handleMarkUnlocked(this._markRemoteUnlocked.bind(this)); + services.remote.handleMarkResolved(this._markRemoteResolved.bind(this)); + } } diff --git a/src/modules/coreFeatures/ModuleResolveMismatchedTweaks.ts b/src/modules/coreFeatures/ModuleResolveMismatchedTweaks.ts index da344aa..0141cc8 100644 --- a/src/modules/coreFeatures/ModuleResolveMismatchedTweaks.ts +++ b/src/modules/coreFeatures/ModuleResolveMismatchedTweaks.ts @@ -11,21 +11,22 @@ import { } from "../../lib/src/common/types.ts"; import { escapeMarkdownValue } from "../../lib/src/common/utils.ts"; import { AbstractModule } from "../AbstractModule.ts"; -import type { ICoreModule } from "../ModuleTypes.ts"; import { $msg } from "../../lib/src/common/i18n.ts"; +import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts"; +import type { LiveSyncCore } from "../../main.ts"; -export class ModuleResolvingMismatchedTweaks extends AbstractModule implements ICoreModule { - async $anyAfterConnectCheckFailed(): Promise { +export class ModuleResolvingMismatchedTweaks extends AbstractModule { + async _anyAfterConnectCheckFailed(): Promise { if (!this.core.replicator.tweakSettingsMismatched && !this.core.replicator.preferredTweakValue) return false; const preferred = this.core.replicator.preferredTweakValue; if (!preferred) return false; - const ret = await this.core.$$askResolvingMismatchedTweaks(preferred); + const ret = await this.services.tweakValue.askResolvingMismatched(preferred); if (ret == "OK") return false; if (ret == "CHECKAGAIN") return "CHECKAGAIN"; if (ret == "IGNORE") return true; } - async $$checkAndAskResolvingMismatchedTweaks( + async _checkAndAskResolvingMismatchedTweaks( preferred: Partial ): Promise<[TweakValues | boolean, boolean]> { const mine = extractObject(TweakValuesShouldMatchedTemplate, this.settings); @@ -127,7 +128,7 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule implements I return CHOICES[retKey]; } - async $$askResolvingMismatchedTweaks(): Promise<"OK" | "CHECKAGAIN" | "IGNORE"> { + async _askResolvingMismatchedTweaks(): Promise<"OK" | "CHECKAGAIN" | "IGNORE"> { if (!this.core.replicator.tweakSettingsMismatched) { return "OK"; } @@ -137,7 +138,7 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule implements I } const preferred = extractObject(TweakValuesShouldMatchedTemplate, tweaks); - const [conf, rebuildRequired] = await this.core.$$checkAndAskResolvingMismatchedTweaks(preferred); + const [conf, rebuildRequired] = await this.services.tweakValue.checkAndAskResolvingMismatched(preferred); if (!conf) return "IGNORE"; if (conf === true) { @@ -154,7 +155,7 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule implements I if (conf) { this.settings = { ...this.settings, ...conf }; await this.core.replicator.setPreferredRemoteTweakSettings(this.settings); - await this.core.$$saveSettingData(); + await this.services.setting.saveSettingData(); if (rebuildRequired) { await this.core.rebuilder.$fetchLocal(); } @@ -164,8 +165,12 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule implements I return "IGNORE"; } - async $$fetchRemotePreferredTweakValues(trialSetting: RemoteDBSettings): Promise { - const replicator = await this.core.$anyNewReplicator(); + async _fetchRemotePreferredTweakValues(trialSetting: RemoteDBSettings): Promise { + const replicator = await this.services.replicator.getNewReplicator(trialSetting); + if (!replicator) { + this._log("The remote type is not supported for fetching preferred tweak values.", LOG_LEVEL_NOTICE); + return false; + } if (await replicator.tryConnectRemote(trialSetting)) { const preferred = await replicator.getRemotePreferredTweakValues(trialSetting); if (preferred) { @@ -178,17 +183,17 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule implements I return false; } - async $$checkAndAskUseRemoteConfiguration( + async _checkAndAskUseRemoteConfiguration( trialSetting: RemoteDBSettings ): Promise<{ result: false | TweakValues; requireFetch: boolean }> { - const preferred = await this.core.$$fetchRemotePreferredTweakValues(trialSetting); + const preferred = await this.services.tweakValue.fetchRemotePreferred(trialSetting); if (preferred) { - return await this.$$askUseRemoteConfiguration(trialSetting, preferred); + return await this.services.tweakValue.askUseRemoteConfiguration(trialSetting, preferred); } return { result: false, requireFetch: false }; } - async $$askUseRemoteConfiguration( + async _askUseRemoteConfiguration( trialSetting: RemoteDBSettings, preferred: TweakValues ): Promise<{ result: false | TweakValues; requireFetch: boolean }> { @@ -278,4 +283,13 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule implements I } return { result: false, requireFetch: false }; } + + onBindFunction(core: LiveSyncCore, services: InjectableServiceHub): void { + services.tweakValue.handleFetchRemotePreferred(this._fetchRemotePreferredTweakValues.bind(this)); + services.tweakValue.handleCheckAndAskResolvingMismatched(this._checkAndAskResolvingMismatchedTweaks.bind(this)); + services.tweakValue.handleAskResolvingMismatched(this._askResolvingMismatchedTweaks.bind(this)); + services.tweakValue.handleCheckAndAskUseRemoteConfiguration(this._checkAndAskUseRemoteConfiguration.bind(this)); + services.tweakValue.handleAskUseRemoteConfiguration(this._askUseRemoteConfiguration.bind(this)); + services.replication.handleCheckConnectionFailure(this._anyAfterConnectCheckFailed.bind(this)); + } } diff --git a/src/modules/coreObsidian/ModuleFileAccessObsidian.ts b/src/modules/coreObsidian/ModuleFileAccessObsidian.ts index 3c1306d..1229060 100644 --- a/src/modules/coreObsidian/ModuleFileAccessObsidian.ts +++ b/src/modules/coreObsidian/ModuleFileAccessObsidian.ts @@ -1,6 +1,6 @@ import { TFile, TFolder, type ListedFiles } from "obsidian"; import { SerializedFileAccess } from "./storageLib/SerializedFileAccess"; -import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts"; +import { AbstractObsidianModule } from "../AbstractObsidianModule.ts"; import { LOG_LEVEL_INFO, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger"; import type { FilePath, @@ -16,10 +16,13 @@ import { StorageEventManagerObsidian, type StorageEventManager } from "./storage import type { StorageAccess } from "../interfaces/StorageAccess"; import { createBlob, type CustomRegExp } from "../../lib/src/common/utils"; import { serialized } from "octagonal-wheels/concurrency/lock_v2"; +import type { LiveSyncCore } from "../../main.ts"; +import type ObsidianLiveSyncPlugin from "../../main.ts"; +import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts"; const fileLockPrefix = "file-lock:"; -export class ModuleFileAccessObsidian extends AbstractObsidianModule implements IObsidianModule, StorageAccess { +export class ModuleFileAccessObsidian extends AbstractObsidianModule implements StorageAccess { processingFiles: Set = new Set(); processWriteFile(file: UXFileInfoStub | FilePathWithPrefix, proc: () => Promise): Promise { const path = typeof file === "string" ? file : file.path; @@ -49,39 +52,35 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements } vaultAccess!: SerializedFileAccess; vaultManager: StorageEventManager = new StorageEventManagerObsidian(this.plugin, this.core, this); - $everyOnload(): Promise { + private _everyOnload(): Promise { this.core.storageAccess = this; return Promise.resolve(true); } - $everyOnFirstInitialize(): Promise { + _everyOnFirstInitialize(): Promise { this.vaultManager.beginWatch(); return Promise.resolve(true); } - $allOnUnload(): Promise { - // this.vaultManager. - return Promise.resolve(true); - } // $$flushFileEventQueue(): void { // this.vaultManager.flushQueue(); // } - $everyCommitPendingFileEvent(): Promise { + _everyCommitPendingFileEvent(): Promise { this.vaultManager.flushQueue(); return Promise.resolve(true); } - $everyOnloadStart(): Promise { + _everyOnloadStart(): Promise { this.vaultAccess = new SerializedFileAccess(this.app, this.plugin, this); return Promise.resolve(true); } - $$isStorageInsensitive(): boolean { + _isStorageInsensitive(): boolean { return this.vaultAccess.isStorageInsensitive(); } - $$shouldCheckCaseInsensitive(): boolean { - if (this.$$isStorageInsensitive()) return false; + _shouldCheckCaseInsensitive(): boolean { + if (this.services.vault.isStorageInsensitive()) return false; return !this.settings.handleFilenameCaseSensitive; } @@ -275,7 +274,7 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements if (excludeFilter && excludeFilter.some((ee) => ee.test(file))) { continue; } - if (await this.plugin.$$isIgnoredByIgnoreFiles(file)) continue; + if (await this.services.vault.isIgnoredByIgnoreFile(file)) continue; files.push(file); } @@ -288,7 +287,7 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements if (excludeFilter && excludeFilter.some((e) => e.test(v))) { continue; } - if (await this.plugin.$$isIgnoredByIgnoreFiles(v)) { + if (await this.services.vault.isIgnoredByIgnoreFile(v)) { continue; } // OK, deep dive! @@ -346,7 +345,7 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements async _deleteVaultItem(file: TFile | TFolder) { if (file instanceof TFile) { - if (!(await this.core.$$isTargetFile(file.path))) return; + if (!(await this.services.vault.isTargetFile(file.path))) return; } const dir = file.parent; if (this.settings.trashInsteadDelete) { @@ -376,4 +375,16 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements return await this._deleteVaultItem(file); } } + + constructor(plugin: ObsidianLiveSyncPlugin, core: LiveSyncCore) { + super(plugin, core); + } + onBindFunction(core: LiveSyncCore, services: InjectableServiceHub): void { + services.vault.handleIsStorageInsensitive(this._isStorageInsensitive.bind(this)); + services.setting.handleShouldCheckCaseInsensitively(this._shouldCheckCaseInsensitive.bind(this)); + services.appLifecycle.handleFirstInitialise(this._everyOnFirstInitialize.bind(this)); + services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this)); + services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this)); + services.fileProcessing.handleCommitPendingFileEvents(this._everyCommitPendingFileEvent.bind(this)); + } } diff --git a/src/modules/coreObsidian/ModuleInputUIObsidian.ts b/src/modules/coreObsidian/ModuleInputUIObsidian.ts index 60f0ca3..7ddcdc1 100644 --- a/src/modules/coreObsidian/ModuleInputUIObsidian.ts +++ b/src/modules/coreObsidian/ModuleInputUIObsidian.ts @@ -1,5 +1,5 @@ // ModuleInputUIObsidian.ts -import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts"; +import { AbstractObsidianModule } from "../AbstractObsidianModule.ts"; import { scheduleTask } from "octagonal-wheels/concurrency/task"; import { disposeMemoObject, memoIfNotExist, memoObject, retrieveMemoObject } from "../../common/utils.ts"; import { @@ -13,12 +13,13 @@ import { Notice } from "../../deps.ts"; import type { Confirm } from "../../lib/src/interfaces/Confirm.ts"; import { setConfirmInstance } from "../../lib/src/PlatformAPIs/obsidian/Confirm.ts"; import { $msg } from "src/lib/src/common/i18n.ts"; +import type { LiveSyncCore } from "../../main.ts"; // This module cannot be a common module because it depends on Obsidian's API. // However, we have to make compatible one for other platform. -export class ModuleInputUIObsidian extends AbstractObsidianModule implements IObsidianModule, Confirm { - $everyOnload(): Promise { +export class ModuleInputUIObsidian extends AbstractObsidianModule implements Confirm { + private _everyOnload(): Promise { this.core.confirm = this; setConfirmInstance(this); return Promise.resolve(true); @@ -110,4 +111,8 @@ export class ModuleInputUIObsidian extends AbstractObsidianModule implements IOb ): Promise<(typeof buttons)[number] | false> { return confirmWithMessage(this.plugin, title, contentMd, buttons, defaultAction, timeout); } + + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this)); + } } diff --git a/src/modules/coreObsidian/storageLib/StorageEventManager.ts b/src/modules/coreObsidian/storageLib/StorageEventManager.ts index 043468d..ab977a7 100644 --- a/src/modules/coreObsidian/storageLib/StorageEventManager.ts +++ b/src/modules/coreObsidian/storageLib/StorageEventManager.ts @@ -7,13 +7,14 @@ import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, + type FileEventType, type FilePath, type FilePathWithPrefix, type UXFileInfoStub, type UXInternalFileInfoStub, } from "../../../lib/src/common/types.ts"; import { delay, fireAndForget, getFileRegExp } from "../../../lib/src/common/utils.ts"; -import { type FileEventItem, type FileEventType } from "../../../common/types.ts"; +import { type FileEventItem } from "../../../common/types.ts"; import { serialized, skipIfDuplicated } from "octagonal-wheels/concurrency/lock"; import { finishAllWaitingForTimeout, @@ -48,6 +49,9 @@ export class StorageEventManagerObsidian extends StorageEventManager { plugin: ObsidianLiveSyncPlugin; core: LiveSyncCore; storageAccess: StorageAccess; + get services() { + return this.core.services; + } get shouldBatchSave() { return this.core.settings?.batchSave && this.core.settings?.liveSync != true; @@ -175,7 +179,7 @@ export class StorageEventManagerObsidian extends StorageEventManager { if (this.plugin.settings.useIgnoreFiles) { // If it is one of ignore files, refresh the cached one. // (Calling$$isTargetFile will refresh the cache) - void this.plugin.$$isTargetFile(path).then(() => this._watchVaultRawEvents(path)); + void this.services.vault.isTargetFile(path).then(() => this._watchVaultRawEvents(path)); } else { this._watchVaultRawEvents(path); } @@ -209,7 +213,7 @@ export class StorageEventManagerObsidian extends StorageEventManager { async appendQueue(params: FileEvent[], ctx?: any) { if (!this.core.settings.isConfigured) return; if (this.core.settings.suspendFileWatching) return; - this.core.$$markFileListPossiblyChanged(); + this.core.services.vault.markFileListPossiblyChanged(); // Flag up to be reload const processFiles = new Set(); for (const param of params) { @@ -222,7 +226,7 @@ export class StorageEventManagerObsidian extends StorageEventManager { const oldPath = param.oldPath; if (type !== "INTERNAL") { const size = (file as UXFileInfoStub).stat.size; - if (this.core.$$isFileSizeExceeded(size) && (type == "CREATE" || type == "CHANGED")) { + if (this.services.vault.isFileSizeTooLarge(size) && (type == "CREATE" || type == "CHANGED")) { Logger( `The storage file has been changed but exceeds the maximum size. Skipping: ${param.file.path}`, LOG_LEVEL_NOTICE @@ -234,7 +238,7 @@ export class StorageEventManagerObsidian extends StorageEventManager { // TODO: Confirm why only the TFolder skipping // Possibly following line is needed... // if (file?.isFolder) continue; - if (!(await this.core.$$isTargetFile(file.path))) continue; + if (!(await this.services.vault.isTargetFile(file.path))) continue; // Stop cache using to prevent the corruption; // let cache: null | string | ArrayBuffer; @@ -411,12 +415,12 @@ export class StorageEventManagerObsidian extends StorageEventManager { const lockKey = `handleFile:${file.path}`; return await serialized(lockKey, async () => { if (queue.type == "INTERNAL" || file.isInternal) { - await this.core.$anyProcessOptionalFileEvent(file.path as unknown as FilePath); + await this.core.services.fileProcessing.processOptionalFileEvent(file.path as unknown as FilePath); } else { const key = `file-last-proc-${queue.type}-${file.path}`; const last = Number((await this.core.kvDB.get(key)) || 0); if (queue.type == "DELETE") { - await this.core.$anyHandlerProcessesFileEvent(queue); + await this.core.services.fileProcessing.processFileEvent(queue); } else { if (file.stat.mtime == last) { Logger(`File has been already scanned on ${queue.type}, skip: ${file.path}`, LOG_LEVEL_VERBOSE); @@ -424,7 +428,7 @@ export class StorageEventManagerObsidian extends StorageEventManager { // this.cancelRelativeEvent(queue); return; } - if (!(await this.core.$anyHandlerProcessesFileEvent(queue))) { + if (!(await this.core.services.fileProcessing.processFileEvent(queue))) { Logger( `STORAGE -> DB: Handler failed, cancel the relative operations: ${file.path}`, LOG_LEVEL_INFO diff --git a/src/modules/essential/ModuleInitializerFile.ts b/src/modules/essential/ModuleInitializerFile.ts index 57f6985..7d4c66c 100644 --- a/src/modules/essential/ModuleInitializerFile.ts +++ b/src/modules/essential/ModuleInitializerFile.ts @@ -17,10 +17,11 @@ import { import { isAnyNote } from "../../lib/src/common/utils.ts"; import { stripAllPrefixes } from "../../lib/src/string_and_binary/path.ts"; import { AbstractModule } from "../AbstractModule.ts"; -import type { ICoreModule } from "../ModuleTypes.ts"; import { withConcurrency } from "octagonal-wheels/iterable/map"; -export class ModuleInitializerFile extends AbstractModule implements ICoreModule { - async $$performFullScan(showingNotice?: boolean, ignoreSuspending: boolean = false): Promise { +import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts"; +import type { LiveSyncCore } from "../../main.ts"; +export class ModuleInitializerFile extends AbstractModule { + private async _performFullScan(showingNotice?: boolean, ignoreSuspending: boolean = false): Promise { this._log("Opening the key-value database", LOG_LEVEL_VERBOSE); const isInitialized = (await this.core.kvDB.get("initialized")) || false; // synchronize all files between database and storage. @@ -32,7 +33,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule "syncAll" ); } - return; + return false; } if (!ignoreSuspending && this.settings.suspendFileWatching) { if (showingNotice) { @@ -42,7 +43,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule "syncAll" ); } - return; + return false; } if (showingNotice) { @@ -59,7 +60,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule const _filesStorage = [] as typeof filesStorageSrc; for (const f of filesStorageSrc) { - if (await this.core.$$isTargetFile(f.path, f != filesStorageSrc[0])) { + if (await this.services.vault.isTargetFile(f.path, f != filesStorageSrc[0])) { _filesStorage.push(f); } } @@ -103,7 +104,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule ); const path = getPath(doc); - if (isValidPath(path) && (await this.core.$$isTargetFile(path, true))) { + if (isValidPath(path) && (await this.services.vault.isTargetFile(path, true))) { if (!isMetaEntry(doc)) { this._log(`Invalid entry: ${path}`, LOG_LEVEL_INFO); continue; @@ -133,7 +134,6 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule this._log(`Total files in the database: ${databaseFileNames.length}`, LOG_LEVEL_VERBOSE, "syncAll"); this._log(`Total files in the storage: ${storageFileNames.length}`, LOG_LEVEL_VERBOSE, "syncAll"); this._log(`Total files: ${allFiles.length}`, LOG_LEVEL_VERBOSE, "syncAll"); - const filesExistOnlyInStorage = allFiles.filter((e) => !databaseFileNameCI2CS[e]); const filesExistOnlyInDatabase = allFiles.filter((e) => !storageFileNameCI2CS[e]); const filesExistBoth = allFiles.filter((e) => databaseFileNameCI2CS[e] && storageFileNameCI2CS[e]); @@ -192,7 +192,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule runAll("UPDATE DATABASE", filesExistOnlyInStorage, async (e) => { // Exists in storage but not in database. const file = storageFileNameMap[storageFileNameCI2CS[e]]; - if (!this.core.$$isFileSizeExceeded(file.stat.size)) { + if (!this.services.vault.isFileSizeTooLarge(file.stat.size)) { const path = file.path; await this.core.fileHandler.storeFileToDB(file); // fireAndForget(() => this.checkAndApplySettingFromMarkdown(path, true)); @@ -208,7 +208,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule // Exists in database but not in storage. const path = getPath(w) ?? e; if (w && !(w.deleted || w._deleted)) { - if (!this.core.$$isFileSizeExceeded(w.size)) { + if (!this.services.vault.isFileSizeTooLarge(w.size)) { // Prevent applying the conflicted state to the storage. if (w._conflicts?.length ?? 0 > 0) { this._log(`UPDATE STORAGE: ${path} has conflicts. skipped (x)`, LOG_LEVEL_INFO); @@ -250,7 +250,10 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule this._log(`SYNC DATABASE AND STORAGE: ${file.path} has conflicts. skipped`, LOG_LEVEL_INFO); return; } - if (!this.core.$$isFileSizeExceeded(file.stat.size) && !this.core.$$isFileSizeExceeded(doc.size)) { + if ( + !this.services.vault.isFileSizeTooLarge(file.stat.size) && + !this.services.vault.isFileSizeTooLarge(doc.size) + ) { await this.syncFileBetweenDBandStorage(file, doc); } else { this._log( @@ -271,6 +274,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule if (showingNotice) { this._log("Initialize done!", LOG_LEVEL_NOTICE, "syncAll"); } + return true; } async syncFileBetweenDBandStorage(file: UXFileInfoStub, doc: MetaEntry) { @@ -289,7 +293,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule const compareResult = compareFileFreshness(file, doc); switch (compareResult) { case BASE_IS_NEW: - if (!this.core.$$isFileSizeExceeded(file.stat.size)) { + if (!this.services.vault.isFileSizeTooLarge(file.stat.size)) { this._log("STORAGE -> DB :" + file.path); await this.core.fileHandler.storeFileToDB(file); } else { @@ -300,7 +304,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule } break; case TARGET_IS_NEW: - if (!this.core.$$isFileSizeExceeded(doc.size)) { + if (!this.services.vault.isFileSizeTooLarge(doc.size)) { this._log("STORAGE <- DB :" + file.path); if (await this.core.fileHandler.dbToStorage(doc, stripAllPrefixes(file.path), true)) { eventHub.emitEvent("event-file-changed", { @@ -365,27 +369,31 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule this._log(`Checking expired file history done`); } - async $$initializeDatabase( + private async _initializeDatabase( showingNotice: boolean = false, reopenDatabase = true, ignoreSuspending: boolean = false ): Promise { - this.core.$$resetIsReady(); - if (!reopenDatabase || (await this.core.$$openDatabase())) { + this.services.appLifecycle.resetIsReady(); + if (!reopenDatabase || (await this.services.database.openDatabase())) { if (this.localDatabase.isReady) { - await this.core.$$performFullScan(showingNotice, ignoreSuspending); + await this.services.vault.scanVault(showingNotice, ignoreSuspending); } - if (!(await this.core.$everyOnDatabaseInitialized(showingNotice))) { - this._log(`Initializing database has been failed on some module`, LOG_LEVEL_NOTICE); + if (!(await this.services.databaseEvents.onDatabaseInitialised(showingNotice))) { + this._log(`Initializing database has been failed on some module!`, LOG_LEVEL_NOTICE); return false; } - this.core.$$markIsReady(); + this.services.appLifecycle.markIsReady(); // run queued event once. - await this.core.$everyCommitPendingFileEvent(); + await this.services.fileProcessing.commitPendingFileEvents(); return true; } else { - this.core.$$resetIsReady(); + this.services.appLifecycle.resetIsReady(); return false; } } + onBindFunction(core: LiveSyncCore, services: InjectableServiceHub): void { + services.databaseEvents.handleInitialiseDatabase(this._initializeDatabase.bind(this)); + services.vault.handleScanVault(this._performFullScan.bind(this)); + } } diff --git a/src/modules/essential/ModuleKeyValueDB.ts b/src/modules/essential/ModuleKeyValueDB.ts index b5d3209..7d835f1 100644 --- a/src/modules/essential/ModuleKeyValueDB.ts +++ b/src/modules/essential/ModuleKeyValueDB.ts @@ -3,9 +3,9 @@ import { OpenKeyValueDatabase } from "../../common/KeyValueDB.ts"; import type { LiveSyncLocalDB } from "../../lib/src/pouchdb/LiveSyncLocalDB.ts"; import { LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger"; import { AbstractModule } from "../AbstractModule.ts"; -import type { ICoreModule } from "../ModuleTypes.ts"; +import type { LiveSyncCore } from "../../main.ts"; -export class ModuleKeyValueDB extends AbstractModule implements ICoreModule { +export class ModuleKeyValueDB extends AbstractModule { tryCloseKvDB() { try { this.core.kvDB?.close(); @@ -22,7 +22,7 @@ export class ModuleKeyValueDB extends AbstractModule implements ICoreModule { this.tryCloseKvDB(); await delay(10); await yieldMicrotask(); - this.core.kvDB = await OpenKeyValueDatabase(this.core.$$getVaultName() + "-livesync-kv"); + this.core.kvDB = await OpenKeyValueDatabase(this.services.vault.getVaultName() + "-livesync-kv"); await yieldMicrotask(); await delay(100); } catch (e) { @@ -33,21 +33,23 @@ export class ModuleKeyValueDB extends AbstractModule implements ICoreModule { } return true; } - $allOnDBUnload(db: LiveSyncLocalDB): void { + _onDBUnload(db: LiveSyncLocalDB) { if (this.core.kvDB) this.core.kvDB.close(); + return Promise.resolve(true); } - $allOnDBClose(db: LiveSyncLocalDB): void { + _onDBClose(db: LiveSyncLocalDB) { if (this.core.kvDB) this.core.kvDB.close(); + return Promise.resolve(true); } - async $everyOnloadAfterLoadSettings(): Promise { + private async _everyOnloadAfterLoadSettings(): Promise { if (!(await this.openKeyValueDB())) { return false; } - this.core.simpleStore = this.core.$$getSimpleStore("os"); + this.core.simpleStore = this.services.database.openSimpleStore("os"); return Promise.resolve(true); } - $$getSimpleStore(kind: string) { + _getSimpleStore(kind: string) { const prefix = `${kind}-`; return { get: async (key: string): Promise => { @@ -75,18 +77,18 @@ export class ModuleKeyValueDB extends AbstractModule implements ICoreModule { }, }; } - $everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise { + _everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise { return this.openKeyValueDB(); } - async $everyOnResetDatabase(db: LiveSyncLocalDB): Promise { + async _everyOnResetDatabase(db: LiveSyncLocalDB): Promise { try { const kvDBKey = "queued-files"; await this.core.kvDB.del(kvDBKey); // localStorage.removeItem(lsKey); await this.core.kvDB.destroy(); await yieldMicrotask(); - this.core.kvDB = await OpenKeyValueDatabase(this.core.$$getVaultName() + "-livesync-kv"); + this.core.kvDB = await OpenKeyValueDatabase(this.services.vault.getVaultName() + "-livesync-kv"); await delay(100); } catch (e) { this.core.kvDB = undefined!; @@ -96,4 +98,12 @@ export class ModuleKeyValueDB extends AbstractModule implements ICoreModule { } return true; } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.databaseEvents.handleOnUnloadDatabase(this._onDBUnload.bind(this)); + services.databaseEvents.handleOnCloseDatabase(this._onDBClose.bind(this)); + services.databaseEvents.handleOnDatabaseInitialisation(this._everyOnInitializeDatabase.bind(this)); + services.databaseEvents.handleOnResetDatabase(this._everyOnResetDatabase.bind(this)); + services.database.handleOpenSimpleStore(this._getSimpleStore.bind(this)); + services.appLifecycle.handleOnSettingLoaded(this._everyOnloadAfterLoadSettings.bind(this)); + } } diff --git a/src/modules/essential/ModuleMigration.ts b/src/modules/essential/ModuleMigration.ts index 1cd0655..b2bb1ca 100644 --- a/src/modules/essential/ModuleMigration.ts +++ b/src/modules/essential/ModuleMigration.ts @@ -9,13 +9,13 @@ import { eventHub, } from "../../common/events.ts"; import { AbstractModule } from "../AbstractModule.ts"; -import type { ICoreModule } from "../ModuleTypes.ts"; import { $msg } from "src/lib/src/common/i18n.ts"; import { performDoctorConsultation, RebuildOptions } from "../../lib/src/common/configForDoc.ts"; import { getPath, isValidPath } from "../../common/utils.ts"; import { isMetaEntry } from "../../lib/src/common/types.ts"; import { isDeletedEntry, isDocContentSame, isLoadedEntry, readAsBlob } from "../../lib/src/common/utils.ts"; import { countCompromisedChunks } from "../../lib/src/pouchdb/negotiation.ts"; +import type { LiveSyncCore } from "../../main.ts"; type ErrorInfo = { path: string; @@ -26,7 +26,7 @@ type ErrorInfo = { isConflicted?: boolean; }; -export class ModuleMigration extends AbstractModule implements ICoreModule { +export class ModuleMigration extends AbstractModule { async migrateUsingDoctor(skipRebuild: boolean = false, activateReason = "updated", forceRescan = false) { const { shouldRebuild, shouldRebuildLocal, isModified, settings } = await performDoctorConsultation( this.core, @@ -45,11 +45,11 @@ export class ModuleMigration extends AbstractModule implements ICoreModule { if (!skipRebuild) { if (shouldRebuild) { await this.core.rebuilder.scheduleRebuild(); - await this.core.$$performRestart(); + this.services.appLifecycle.performRestart(); return false; } else if (shouldRebuildLocal) { await this.core.rebuilder.scheduleFetch(); - await this.core.$$performRestart(); + this.services.appLifecycle.performRestart(); return false; } } @@ -129,7 +129,7 @@ export class ModuleMigration extends AbstractModule implements ICoreModule { if (!isValidPath(path)) { continue; } - if (!(await this.core.$$isTargetFile(path, true))) { + if (!(await this.services.vault.isTargetFile(path, true))) { continue; } if (!isMetaEntry(metaDoc)) { @@ -257,9 +257,9 @@ export class ModuleMigration extends AbstractModule implements ICoreModule { } // Check local database for compromised chunks const localCompromised = await countCompromisedChunks(this.localDatabase.localDatabase); - const remote = this.core.$$getReplicator(); + const remote = this.services.replicator.getActiveReplicator(); const remoteCompromised = this.core.managers.networkManager.isOnline - ? await remote.countCompromisedChunks() + ? await remote?.countCompromisedChunks() : 0; if (localCompromised === false) { Logger(`Failed to count compromised chunks in local database`, LOG_LEVEL_NOTICE); @@ -293,12 +293,12 @@ export class ModuleMigration extends AbstractModule implements ICoreModule { if (result === REBUILD) { // Rebuild the database await this.core.rebuilder.scheduleRebuild(); - await this.core.$$performRestart(); + this.services.appLifecycle.performRestart(); return false; } else if (result === FETCH) { // Fetch the latest data from remote await this.core.rebuilder.scheduleFetch(); - await this.core.$$performRestart(); + this.services.appLifecycle.performRestart(); return false; } else { // User chose to dismiss the issue @@ -307,7 +307,7 @@ export class ModuleMigration extends AbstractModule implements ICoreModule { return true; } - async $everyOnFirstInitialize(): Promise { + async _everyOnFirstInitialize(): Promise { if (!this.localDatabase.isReady) { this._log($msg("moduleMigration.logLocalDatabaseNotReady"), LOG_LEVEL_NOTICE); return false; @@ -337,7 +337,7 @@ export class ModuleMigration extends AbstractModule implements ICoreModule { } return true; } - $everyOnLayoutReady(): Promise { + _everyOnLayoutReady(): Promise { eventHub.onEvent(EVENT_REQUEST_RUN_DOCTOR, async (reason) => { await this.migrateUsingDoctor(false, reason, true); }); @@ -346,4 +346,9 @@ export class ModuleMigration extends AbstractModule implements ICoreModule { }); return Promise.resolve(true); } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + super.onBindFunction(core, services); + services.appLifecycle.handleLayoutReady(this._everyOnLayoutReady.bind(this)); + services.appLifecycle.handleFirstInitialise(this._everyOnFirstInitialize.bind(this)); + } } diff --git a/src/modules/essentialObsidian/ModuleObsidianAPI.ts b/src/modules/essentialObsidian/ModuleObsidianAPI.ts index 816c2dc..61cfc79 100644 --- a/src/modules/essentialObsidian/ModuleObsidianAPI.ts +++ b/src/modules/essentialObsidian/ModuleObsidianAPI.ts @@ -1,7 +1,7 @@ -import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts"; +import { AbstractObsidianModule } from "../AbstractObsidianModule.ts"; import { LOG_LEVEL_DEBUG, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger"; import { Notice, requestUrl, type RequestUrlParam, type RequestUrlResponse } from "../../deps.ts"; -import { type CouchDBCredentials, type EntryDoc, type FilePathWithPrefix } from "../../lib/src/common/types.ts"; +import { type CouchDBCredentials, type EntryDoc, type FilePath } from "../../lib/src/common/types.ts"; import { getPathFromTFile } from "../../common/utils.ts"; import { isCloudantURI, isValidRemoteCouchDBURI } from "../../lib/src/pouchdb/utils_couchdb.ts"; import { replicationFilter } from "@/lib/src/pouchdb/compress.ts"; @@ -11,6 +11,7 @@ import { setNoticeClass } from "../../lib/src/mock_and_interop/wrapper.ts"; import { ObsHttpHandler } from "./APILib/ObsHttpHandler.ts"; import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser.ts"; import { AuthorizationHeaderGenerator } from "../../lib/src/replication/httplib.ts"; +import type { LiveSyncCore } from "../../main.ts"; setNoticeClass(Notice); @@ -19,17 +20,17 @@ async function fetchByAPI(request: RequestUrlParam, errorAsResult = false): Prom return ret; } -export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidianModule { +export class ModuleObsidianAPI extends AbstractObsidianModule { _customHandler!: ObsHttpHandler; _authHeader = new AuthorizationHeaderGenerator(); last_successful_post = false; - $$customFetchHandler(): ObsHttpHandler { + _customFetchHandler(): ObsHttpHandler { if (!this._customHandler) this._customHandler = new ObsHttpHandler(undefined, undefined); return this._customHandler; } - $$getLastPostFailedBySize(): boolean { + _getLastPostFailedBySize(): boolean { return !this.last_successful_post; } @@ -90,7 +91,7 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi } } - async $$connectRemoteCouchDB( + async _connectRemoteCouchDB( uri: string, auth: CouchDBCredentials, disableRequestURI: boolean, @@ -252,21 +253,21 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi } } - $$isMobile(): boolean { + _isMobile(): boolean { //@ts-ignore : internal API return this.app.isMobile; } - $$vaultName(): string { + _vaultName(): string { return this.app.vault.getName(); } - $$getVaultName(): string { + _getVaultName(): string { return ( - this.core.$$vaultName() + + this.services.vault.vaultName() + (this.settings?.additionalSuffixOfDatabaseName ? "-" + this.settings.additionalSuffixOfDatabaseName : "") ); } - $$getActiveFilePath(): FilePathWithPrefix | undefined { + _getActiveFilePath(): FilePath | undefined { const file = this.app.workspace.getActiveFile(); if (file) { return getPathFromTFile(file); @@ -274,7 +275,18 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi return undefined; } - $anyGetAppId(): Promise { - return Promise.resolve(`${"appId" in this.app ? this.app.appId : ""}`); + _anyGetAppId(): string { + return `${"appId" in this.app ? this.app.appId : ""}`; + } + + onBindFunction(core: LiveSyncCore, services: typeof core.services) { + services.API.handleGetCustomFetchHandler(this._customFetchHandler.bind(this)); + services.API.handleIsLastPostFailedDueToPayloadSize(this._getLastPostFailedBySize.bind(this)); + services.remote.handleConnect(this._connectRemoteCouchDB.bind(this)); + services.API.handleIsMobile(this._isMobile.bind(this)); + services.vault.handleGetVaultName(this._getVaultName.bind(this)); + services.vault.handleVaultName(this._vaultName.bind(this)); + services.vault.handleGetActiveFilePath(this._getActiveFilePath.bind(this)); + services.API.handleGetAppID(this._anyGetAppId.bind(this)); } } diff --git a/src/modules/essentialObsidian/ModuleObsidianEvents.ts b/src/modules/essentialObsidian/ModuleObsidianEvents.ts index 45ebda1..d64a95e 100644 --- a/src/modules/essentialObsidian/ModuleObsidianEvents.ts +++ b/src/modules/essentialObsidian/ModuleObsidianEvents.ts @@ -1,4 +1,4 @@ -import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts"; +import { AbstractObsidianModule } from "../AbstractObsidianModule.ts"; import { EVENT_FILE_RENAMED, EVENT_LEAF_ACTIVE_CHANGED, eventHub } from "../../common/events.js"; import { LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger"; import { scheduleTask } from "octagonal-wheels/concurrency/task"; @@ -12,9 +12,10 @@ import { hiddenFilesEventCount, hiddenFilesProcessingCount, } from "../../lib/src/mock_and_interop/stores.ts"; +import type { LiveSyncCore } from "../../main.ts"; -export class ModuleObsidianEvents extends AbstractObsidianModule implements IObsidianModule { - $everyOnloadStart(): Promise { +export class ModuleObsidianEvents extends AbstractObsidianModule { + _everyOnloadStart(): Promise { // this.registerEvent(this.app.workspace.on("editor-change", )); this.plugin.registerEvent( this.app.vault.on("rename", (file, oldPath) => { @@ -30,7 +31,7 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs return Promise.resolve(true); } - $$performRestart(): void { + private _performRestart(): void { this._performAppReload(); } @@ -49,14 +50,14 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs this.initialCallback = save; saveCommandDefinition.callback = () => { scheduleTask("syncOnEditorSave", 250, () => { - if (this.core.$$isUnloaded()) { + if (this.services.appLifecycle.hasUnloaded()) { this._log("Unload and remove the handler.", LOG_LEVEL_VERBOSE); saveCommandDefinition.callback = this.initialCallback; this.initialCallback = undefined; } else { if (this.settings.syncOnEditorSave) { this._log("Sync on Editor Save.", LOG_LEVEL_VERBOSE); - fireAndForget(() => this.core.$$replicateByEvent()); + fireAndForget(() => this.services.replication.replicateByEvent()); } } }); @@ -106,14 +107,14 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs // TODO:FIXME AT V0.17.31, this logic has been disabled. if (navigator.onLine && this.localDatabase.needScanning) { this.localDatabase.needScanning = false; - await this.core.$$performFullScan(); + await this.services.vault.scanVault(); } } async watchWindowVisibilityAsync() { if (this.settings.suspendFileWatching) return; if (!this.settings.isConfigured) return; - if (!this.core.$$isReady()) return; + if (!this.services.appLifecycle.isReady()) return; if (this.isLastHidden && !this.hasFocus) { // NO OP while non-focused after made hidden; @@ -126,22 +127,22 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs } this.isLastHidden = isHidden; - await this.core.$everyCommitPendingFileEvent(); + await this.services.fileProcessing.commitPendingFileEvents(); if (isHidden) { - await this.core.$everyBeforeSuspendProcess(); + await this.services.appLifecycle.onSuspending(); } else { // suspend all temporary. - if (this.core.$$isSuspended()) return; + if (this.services.appLifecycle.isSuspended()) return; if (!this.hasFocus) return; - await this.core.$everyOnResumeProcess(); - await this.core.$everyAfterResumeProcess(); + await this.services.appLifecycle.onResuming(); + await this.services.appLifecycle.onResumed(); } } watchWorkspaceOpen(file: TFile | null) { if (this.settings.suspendFileWatching) return; if (!this.settings.isConfigured) return; - if (!this.core.$$isReady()) return; + if (!this.services.appLifecycle.isReady()) return; if (!file) return; scheduleTask("watch-workspace-open", 500, () => fireAndForget(() => this.watchWorkspaceOpenAsync(file))); } @@ -149,25 +150,25 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs async watchWorkspaceOpenAsync(file: TFile) { if (this.settings.suspendFileWatching) return; if (!this.settings.isConfigured) return; - if (!this.core.$$isReady()) return; - await this.core.$everyCommitPendingFileEvent(); + if (!this.services.appLifecycle.isReady()) return; + await this.services.fileProcessing.commitPendingFileEvents(); if (file == null) { return; } - if (this.settings.syncOnFileOpen && !this.core.$$isSuspended()) { - await this.core.$$replicateByEvent(); + if (this.settings.syncOnFileOpen && !this.services.appLifecycle.isSuspended()) { + await this.services.replication.replicateByEvent(); } - await this.core.$$queueConflictCheckIfOpen(file.path as FilePathWithPrefix); + await this.services.conflict.queueCheckForIfOpen(file.path as FilePathWithPrefix); } - $everyOnLayoutReady(): Promise { + _everyOnLayoutReady(): Promise { this.swapSaveCommand(); this.registerWatchEvents(); return Promise.resolve(true); } - $$askReload(message?: string) { - if (this.core.$$isReloadingScheduled()) { + private _askReload(message?: string) { + if (this.services.appLifecycle.isReloadingScheduled()) { this._log(`Reloading is already scheduled`, LOG_LEVEL_VERBOSE); return; } @@ -183,11 +184,11 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs if (ret == RESTART_NOW) { this._performAppReload(); } else if (ret == RESTART_AFTER_STABLE) { - this.core.$$scheduleAppReload(); + this.services.appLifecycle.scheduleRestart(); } }); } - $$scheduleAppReload() { + private _scheduleAppReload() { if (!this.core._totalProcessingCount) { const __tick = reactiveSource(0); this.core._totalProcessingCount = reactive(() => { @@ -237,4 +238,11 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs }); } } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.appLifecycle.handleLayoutReady(this._everyOnLayoutReady.bind(this)); + services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this)); + services.appLifecycle.handlePerformRestart(this._performRestart.bind(this)); + services.appLifecycle.handleAskRestart(this._askReload.bind(this)); + services.appLifecycle.handleScheduleRestart(this._scheduleAppReload.bind(this)); + } } diff --git a/src/modules/essentialObsidian/ModuleObsidianMenu.ts b/src/modules/essentialObsidian/ModuleObsidianMenu.ts index 30c6c1b..ff89efb 100644 --- a/src/modules/essentialObsidian/ModuleObsidianMenu.ts +++ b/src/modules/essentialObsidian/ModuleObsidianMenu.ts @@ -1,11 +1,12 @@ 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 { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts"; +import { AbstractObsidianModule } from "../AbstractObsidianModule.ts"; import { $msg } from "src/lib/src/common/i18n.ts"; +import type { LiveSyncCore } from "../../main.ts"; -export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsidianModule { - $everyOnloadStart(): Promise { +export class ModuleObsidianMenu extends AbstractObsidianModule { + _everyOnloadStart(): Promise { // UI addIcon( "replicate", @@ -18,21 +19,21 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid ); this.addRibbonIcon("replicate", $msg("moduleObsidianMenu.replicate"), async () => { - await this.core.$$replicate(true); + await this.services.replication.replicate(true); }).addClass("livesync-ribbon-replicate"); this.addCommand({ id: "livesync-replicate", name: "Replicate now", callback: async () => { - await this.core.$$replicate(); + await this.services.replication.replicate(); }, }); this.addCommand({ id: "livesync-dump", name: "Dump information of this doc ", callback: () => { - const file = this.core.$$getActiveFilePath(); + const file = this.services.vault.getActiveFilePath(); if (!file) return; fireAndForget(() => this.localDatabase.getDBEntry(file, {}, true, false)); }, @@ -43,7 +44,7 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid editorCallback: (editor: Editor, view: MarkdownView | MarkdownFileInfo) => { const file = view.file; if (!file) return; - void this.core.$$queueConflictCheckIfOpen(file.path as FilePathWithPrefix); + void this.services.conflict.queueCheckForIfOpen(file.path as FilePathWithPrefix); }, }); @@ -58,23 +59,23 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid this.settings.liveSync = true; this._log("LiveSync Enabled.", LOG_LEVEL_NOTICE); } - await this.core.$$realizeSettingSyncMode(); - await this.core.$$saveSettingData(); + await this.services.setting.onRealiseSetting(); + await this.services.setting.saveSettingData(); }, }); this.addCommand({ id: "livesync-suspendall", name: "Toggle All Sync.", callback: async () => { - if (this.core.$$isSuspended()) { - this.core.$$setSuspended(false); + if (this.services.appLifecycle.isSuspended()) { + this.services.appLifecycle.setSuspended(false); this._log("Self-hosted LiveSync resumed", LOG_LEVEL_NOTICE); } else { - this.core.$$setSuspended(true); + this.services.appLifecycle.setSuspended(true); this._log("Self-hosted LiveSync suspended", LOG_LEVEL_NOTICE); } - await this.core.$$realizeSettingSyncMode(); - await this.core.$$saveSettingData(); + await this.services.setting.onRealiseSetting(); + await this.services.setting.saveSettingData(); }, }); @@ -82,7 +83,7 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid id: "livesync-scan-files", name: "Scan storage and database again", callback: async () => { - await this.core.$$performFullScan(true); + await this.services.vault.scanVault(true); }, }); @@ -90,7 +91,7 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid id: "livesync-runbatch", name: "Run pended batch processes", callback: async () => { - await this.core.$everyCommitPendingFileEvent(); + await this.services.fileProcessing.commitPendingFileEvents(); }, }); @@ -104,12 +105,15 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid }); return Promise.resolve(true); } - $everyOnload(): Promise { - this.app.workspace.onLayoutReady(this.core.$$onLiveSyncReady.bind(this.core)); + _onWorkspaceReady() { + void this.services.appLifecycle.onReady(); + } + private _everyOnload(): Promise { + this.app.workspace.onLayoutReady(this._onWorkspaceReady.bind(this)); return Promise.resolve(true); } - async $$showView(viewType: string) { + private async _showView(viewType: string) { const leaves = this.app.workspace.getLeavesOfType(viewType); if (leaves.length == 0) { await this.app.workspace.getLeaf(true).setViewState({ @@ -126,4 +130,9 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid await this.app.workspace.revealLeaf(leaves[0]); } } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this)); + services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this)); + services.API.handleShowWindow(this._showView.bind(this)); + } } diff --git a/src/modules/extraFeaturesObsidian/ModuleExtraSyncObsidian.ts b/src/modules/extraFeaturesObsidian/ModuleExtraSyncObsidian.ts index e1fc597..715e71f 100644 --- a/src/modules/extraFeaturesObsidian/ModuleExtraSyncObsidian.ts +++ b/src/modules/extraFeaturesObsidian/ModuleExtraSyncObsidian.ts @@ -1,12 +1,18 @@ -import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts"; +import type { LiveSyncCore } from "../../main.ts"; +import { AbstractObsidianModule } from "../AbstractObsidianModule.ts"; -export class ModuleExtraSyncObsidian extends AbstractObsidianModule implements IObsidianModule { +export class ModuleExtraSyncObsidian extends AbstractObsidianModule { deviceAndVaultName: string = ""; - $$getDeviceAndVaultName(): string { + _getDeviceAndVaultName(): string { return this.deviceAndVaultName; } - $$setDeviceAndVaultName(name: string): void { + _setDeviceAndVaultName(name: string): void { this.deviceAndVaultName = name; } + + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.setting.handleGetDeviceAndVaultName(this._getDeviceAndVaultName.bind(this)); + services.setting.handleSetDeviceAndVaultName(this._setDeviceAndVaultName.bind(this)); + } } diff --git a/src/modules/extras/ModuleDev.ts b/src/modules/extras/ModuleDev.ts index 12f39a4..352d0d1 100644 --- a/src/modules/extras/ModuleDev.ts +++ b/src/modules/extras/ModuleDev.ts @@ -1,15 +1,16 @@ import { delay, fireAndForget } from "octagonal-wheels/promises"; import { __onMissingTranslation } from "../../lib/src/common/i18n"; -import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts"; +import { AbstractObsidianModule } from "../AbstractObsidianModule.ts"; import { LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger"; import { eventHub } from "../../common/events"; import { enableTestFunction } from "./devUtil/testUtils.ts"; import { TestPaneView, VIEW_TYPE_TEST } from "./devUtil/TestPaneView.ts"; import { writable } from "svelte/store"; import type { FilePathWithPrefix } from "../../lib/src/common/types.ts"; +import type { LiveSyncCore } from "../../main.ts"; -export class ModuleDev extends AbstractObsidianModule implements IObsidianModule { - $everyOnloadStart(): Promise { +export class ModuleDev extends AbstractObsidianModule { + _everyOnloadStart(): Promise { __onMissingTranslation(() => {}); return Promise.resolve(true); } @@ -35,7 +36,7 @@ export class ModuleDev extends AbstractObsidianModule implements IObsidianModule } } - $everyOnloadAfterLoadSettings(): Promise { + private _everyOnloadAfterLoadSettings(): Promise { if (!this.settings.enableDebugTools) return Promise.resolve(true); this.onMissingTranslation = this.onMissingTranslation.bind(this); __onMissingTranslation((key) => { @@ -92,12 +93,12 @@ export class ModuleDev extends AbstractObsidianModule implements IObsidianModule id: "view-test", name: "Open Test dialogue", callback: () => { - void this.core.$$showView(VIEW_TYPE_TEST); + void this.services.API.showWindow(VIEW_TYPE_TEST); }, }); return Promise.resolve(true); } - async $everyOnLayoutReady(): Promise { + async _everyOnLayoutReady(): Promise { if (!this.settings.enableDebugTools) return Promise.resolve(true); // if (await this.core.storageAccess.isExistsIncludeHidden("_SHOWDIALOGAUTO.md")) { // void this.core.$$showView(VIEW_TYPE_TEST); @@ -121,7 +122,7 @@ export class ModuleDev extends AbstractObsidianModule implements IObsidianModule }, }); if (w) { - const id = await this.core.$$path2id(filename as FilePathWithPrefix); + const id = await this.services.path.path2id(filename as FilePathWithPrefix); const f = await this.core.localDatabase.getRaw(id); console.log(f); console.log(f._rev); @@ -139,14 +140,14 @@ export class ModuleDev extends AbstractObsidianModule implements IObsidianModule testResults = writable<[boolean, string, string][]>([]); // testResults: string[] = []; - $$addTestResult(name: string, key: string, result: boolean, summary?: string, message?: string): void { + private _addTestResult(name: string, key: string, result: boolean, summary?: string, message?: string): void { const logLine = `${name}: ${key} ${summary ?? ""}`; this.testResults.update((results) => { results.push([result, logLine, message ?? ""]); return results; }); } - $everyModuleTest(): Promise { + private _everyModuleTest(): Promise { if (!this.settings.enableDebugTools) return Promise.resolve(true); // this.core.$$addTestResult("DevModule", "Test", true); // return Promise.resolve(true); @@ -155,4 +156,11 @@ export class ModuleDev extends AbstractObsidianModule implements IObsidianModule // this.addTestResult("Test of test3", true); return this.testDone(); } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.appLifecycle.handleLayoutReady(this._everyOnLayoutReady.bind(this)); + services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this)); + services.appLifecycle.handleOnSettingLoaded(this._everyOnloadAfterLoadSettings.bind(this)); + services.test.handleTest(this._everyModuleTest.bind(this)); + services.test.handleAddTestResult(this._addTestResult.bind(this)); + } } diff --git a/src/modules/extras/ModuleIntegratedTest.ts b/src/modules/extras/ModuleIntegratedTest.ts index 1e0710d..6099114 100644 --- a/src/modules/extras/ModuleIntegratedTest.ts +++ b/src/modules/extras/ModuleIntegratedTest.ts @@ -1,9 +1,9 @@ import { delay } from "octagonal-wheels/promises"; import { LOG_LEVEL_NOTICE, REMOTE_MINIO, type FilePathWithPrefix } from "src/lib/src/common/types"; import { shareRunningResult } from "octagonal-wheels/concurrency/lock"; -import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule"; +import { AbstractObsidianModule } from "../AbstractObsidianModule"; -export class ModuleIntegratedTest extends AbstractObsidianModule implements IObsidianModule { +export class ModuleIntegratedTest extends AbstractObsidianModule { async waitFor(proc: () => Promise, timeout = 10000): Promise { await delay(100); const start = Date.now(); @@ -54,7 +54,7 @@ export class ModuleIntegratedTest extends AbstractObsidianModule implements IObs tryReplicate() { if (!this.settings.liveSync) { return shareRunningResult("replicate-test", async () => { - await this.core.$$replicate(); + await this.services.replication.replicate(); }); } } @@ -68,7 +68,7 @@ export class ModuleIntegratedTest extends AbstractObsidianModule implements IObs const stepFile = "_STEP.md" as FilePathWithPrefix; const stepAckFile = "_STEP_ACK.md" as FilePathWithPrefix; const stepContent = `Step ${no}`; - await this.core.$anyResolveConflictByNewest(stepFile); + await this.services.conflict.resolveByNewest(stepFile); await this.core.storageAccess.writeFileAuto(stepFile, stepContent); await this._orDie(`Wait for acknowledge ${no}`, async () => { if ( @@ -96,7 +96,7 @@ export class ModuleIntegratedTest extends AbstractObsidianModule implements IObs return false; return true; }); - await this.core.$anyResolveConflictByNewest(stepAckFile); + await this.services.conflict.resolveByNewest(stepAckFile); await this.core.storageAccess.writeFileAuto(stepAckFile, stepContent); await this.tryReplicate(); return true; @@ -424,9 +424,9 @@ Line4:D`; await this._test("basic", async () => await this.nonLiveTestRunner(isLeader, (t, l) => this.testBasic(t, l))); } - async $everyModuleTestMultiDevice(): Promise { + async _everyModuleTestMultiDevice(): Promise { if (!this.settings.enableDebugTools) return Promise.resolve(true); - const isLeader = this.core.$$vaultName().indexOf("recv") === -1; + const isLeader = this.core.services.vault.vaultName().indexOf("recv") === -1; this.addTestResult("-------", true, `Test as ${isLeader ? "Leader" : "Receiver"}`); try { this._log(`Starting Test`); @@ -440,4 +440,7 @@ Line4:D`; return Promise.resolve(true); } + onBindFunction(core: typeof this.core, services: typeof core.services): void { + services.test.handleTestMultiDevice(this._everyModuleTestMultiDevice.bind(this)); + } } diff --git a/src/modules/extras/ModuleReplicateTest.ts b/src/modules/extras/ModuleReplicateTest.ts index dd9ebd7..3288c79 100644 --- a/src/modules/extras/ModuleReplicateTest.ts +++ b/src/modules/extras/ModuleReplicateTest.ts @@ -1,5 +1,5 @@ import { delay } from "octagonal-wheels/promises"; -import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts"; +import { AbstractObsidianModule } from "../AbstractObsidianModule.ts"; import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger"; import { eventHub } from "../../common/events"; import { getWebCrypto } from "../../lib/src/mods.ts"; @@ -8,6 +8,7 @@ import { parseYaml, requestUrl, stringifyYaml } from "obsidian"; import type { FilePath } from "../../lib/src/common/types.ts"; import { scheduleTask } from "octagonal-wheels/concurrency/task"; import { getFileRegExp } from "../../lib/src/common/utils.ts"; +import type { LiveSyncCore } from "../../main.ts"; declare global { interface LSEvents { @@ -15,12 +16,15 @@ declare global { } } -export class ModuleReplicateTest extends AbstractObsidianModule implements IObsidianModule { +export class ModuleReplicateTest extends AbstractObsidianModule { testRootPath = "_test/"; testInfoPath = "_testinfo/"; get isLeader() { - return this.core.$$getVaultName().indexOf("dev") >= 0 && this.core.$$vaultName().indexOf("recv") < 0; + return ( + this.services.vault.getVaultName().indexOf("dev") >= 0 && + this.services.vault.vaultName().indexOf("recv") < 0 + ); } get nameByKind() { @@ -58,12 +62,12 @@ export class ModuleReplicateTest extends AbstractObsidianModule implements IObsi await this._dumpFileList("files.md"); } } - async $everyBeforeReplicate(showMessage: boolean): Promise { + async _everyBeforeReplicate(showMessage: boolean): Promise { if (!this.settings.enableDebugTools) return Promise.resolve(true); await this.dumpList(); return true; } - $everyOnloadAfterLoadSettings(): Promise { + private _everyOnloadAfterLoadSettings(): Promise { if (!this.settings.enableDebugTools) return Promise.resolve(true); this.addCommand({ id: "dump-file-structure-normal", @@ -169,7 +173,7 @@ export class ModuleReplicateTest extends AbstractObsidianModule implements IObsi const out = [] as any[]; const webcrypto = await getWebCrypto(); for (const file of files) { - if (!(await this.core.$$isTargetFile(file.path))) { + if (!(await this.services.vault.isTargetFile(file.path))) { continue; } if (file.path.startsWith(this.testInfoPath)) continue; @@ -316,7 +320,7 @@ export class ModuleReplicateTest extends AbstractObsidianModule implements IObsi } async testConflictedManually1() { - await this.core.$$replicate(); + await this.services.replication.replicate(); const commonFile = `Resolve! *****, the amazing chocolatier!!`; @@ -325,8 +329,8 @@ export class ModuleReplicateTest extends AbstractObsidianModule implements IObsi await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "wonka.md", commonFile); } - await this.core.$$replicate(); - await this.core.$$replicate(); + await this.services.replication.replicate(); + await this.services.replication.replicate(); if ( (await this.core.confirm.askYesNoDialog("Ready to begin the test conflict Manually 1?", { timeout: 30, @@ -356,12 +360,12 @@ Charlie Bucket, Charlie Bucket, the amazing chocolatier!!`; ) { return; } - await this.core.$$replicate(); - await this.core.$$replicate(); + await this.services.replication.replicate(); + await this.services.replication.replicate(); if ( !(await this.waitFor(async () => { - await this.core.$$replicate(); + await this.services.replication.replicate(); return ( (await this.__assertStorageContent( (this.testRootPath + "wonka.md") as FilePath, @@ -379,7 +383,7 @@ Charlie Bucket, Charlie Bucket, the amazing chocolatier!!`; } async testConflictedManually2() { - await this.core.$$replicate(); + await this.services.replication.replicate(); const commonFile = `Resolve To concatenate ABCDEFG`; @@ -388,8 +392,8 @@ ABCDEFG`; await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "concat.md", commonFile); } - await this.core.$$replicate(); - await this.core.$$replicate(); + await this.services.replication.replicate(); + await this.services.replication.replicate(); if ( (await this.core.confirm.askYesNoDialog("Ready to begin the test conflict Manually 2?", { timeout: 30, @@ -420,12 +424,12 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ`; ) { return; } - await this.core.$$replicate(); - await this.core.$$replicate(); + await this.services.replication.replicate(); + await this.services.replication.replicate(); if ( !(await this.waitFor(async () => { - await this.core.$$replicate(); + await this.services.replication.replicate(); return ( (await this.__assertStorageContent( (this.testRootPath + "concat.md") as FilePath, @@ -457,8 +461,8 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ`; await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "task.md", baseDoc); } await delay(100); - await this.core.$$replicate(); - await this.core.$$replicate(); + await this.services.replication.replicate(); + await this.services.replication.replicate(); if ( (await this.core.confirm.askYesNoDialog("Ready to test conflict?", { @@ -487,8 +491,8 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ`; await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "task.md", mod2Doc); } - await this.core.$$replicate(); - await this.core.$$replicate(); + await this.services.replication.replicate(); + await this.services.replication.replicate(); await delay(1000); if ( (await this.core.confirm.askYesNoDialog("Ready to check result?", { timeout: 30, defaultOption: "Yes" })) == @@ -496,8 +500,8 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ`; ) { return; } - await this.core.$$replicate(); - await this.core.$$replicate(); + await this.services.replication.replicate(); + await this.services.replication.replicate(); const mergedDoc = `Tasks! - [ ] Task 1 - [v] Task 2 @@ -511,7 +515,7 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ`; this._log("Before testing conflicted files, resolve all once", LOG_LEVEL_NOTICE); await this.core.rebuilder.resolveAllConflictedFilesByNewerOnes(); await this.core.rebuilder.resolveAllConflictedFilesByNewerOnes(); - await this.core.$$replicate(); + await this.services.replication.replicate(); await delay(1000); if (!(await this.testConflictAutomatic())) { this._log("Conflict resolution (Auto) failed", LOG_LEVEL_NOTICE); @@ -569,11 +573,16 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ`; // return results; // }); // } - async $everyModuleTestMultiDevice(): Promise { + private async _everyModuleTestMultiDevice(): Promise { if (!this.settings.enableDebugTools) return Promise.resolve(true); // this.core.$$addTestResult("DevModule", "Test", true); // return Promise.resolve(true); await this._test("Conflict resolution", async () => await this.checkConflictResolution()); return this.testDone(); } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.appLifecycle.handleOnSettingLoaded(this._everyOnloadAfterLoadSettings.bind(this)); + services.replication.handleBeforeReplicate(this._everyBeforeReplicate.bind(this)); + services.test.handleTestMultiDevice(this._everyModuleTestMultiDevice.bind(this)); + } } diff --git a/src/modules/extras/devUtil/TestPane.svelte b/src/modules/extras/devUtil/TestPane.svelte index 1088427..f7d6bbb 100644 --- a/src/modules/extras/devUtil/TestPane.svelte +++ b/src/modules/extras/devUtil/TestPane.svelte @@ -57,14 +57,14 @@ function moduleMultiDeviceTest() { if (moduleTesting) return; moduleTesting = true; - plugin.$everyModuleTestMultiDevice().finally(() => { + plugin.services.test.testMultiDevice().finally(() => { moduleTesting = false; }); } function moduleSingleDeviceTest() { if (moduleTesting) return; moduleTesting = true; - plugin.$everyModuleTest().finally(() => { + plugin.services.test.test().finally(() => { moduleTesting = false; }); } @@ -72,8 +72,8 @@ if (moduleTesting) return; moduleTesting = true; try { - await plugin.$everyModuleTest(); - await plugin.$everyModuleTestMultiDevice(); + await plugin.services.test.test(); + await plugin.services.test.testMultiDevice(); } finally { moduleTesting = false; } diff --git a/src/modules/features/DocumentHistory/DocumentHistoryModal.ts b/src/modules/features/DocumentHistory/DocumentHistoryModal.ts index 9327235..35e53cb 100644 --- a/src/modules/features/DocumentHistory/DocumentHistoryModal.ts +++ b/src/modules/features/DocumentHistory/DocumentHistoryModal.ts @@ -46,6 +46,9 @@ function readDocument(w: LoadedEntry) { } export class DocumentHistoryModal extends Modal { plugin: ObsidianLiveSyncPlugin; + get services() { + return this.plugin.services; + } range!: HTMLInputElement; contentView!: HTMLDivElement; info!: HTMLDivElement; @@ -74,7 +77,7 @@ export class DocumentHistoryModal extends Modal { this.id = id; this.initialRev = revision; if (!file && id) { - this.file = this.plugin.$$id2path(id); + this.file = this.services.path.id2path(id); } if (localStorage.getItem("ols-history-highlightdiff") == "1") { this.showDiff = true; @@ -83,7 +86,7 @@ export class DocumentHistoryModal extends Modal { async loadFile(initialRev?: string) { if (!this.id) { - this.id = await this.plugin.$$path2id(this.file); + this.id = await this.services.path.path2id(this.file); } const db = this.plugin.localDatabase; try { @@ -126,7 +129,7 @@ export class DocumentHistoryModal extends Modal { } this.BlobURLs.delete(key); } - generateBlobURL(key: string, data: Uint8Array) { + generateBlobURL(key: string, data: Uint8Array) { this.revokeURL(key); const v = URL.createObjectURL(new Blob([data], { endings: "transparent", type: "application/octet-stream" })); this.BlobURLs.set(key, v); @@ -175,7 +178,10 @@ export class DocumentHistoryModal extends Modal { result = result.replace(/\n/g, "
"); } else if (isImage(this.file)) { const src = this.generateBlobURL("base", w1data); - const overlay = this.generateBlobURL("overlay", readDocument(w2) as Uint8Array); + const overlay = this.generateBlobURL( + "overlay", + readDocument(w2) as Uint8Array + ); result = `
diff --git a/src/modules/features/ModuleGlobalHistory.ts b/src/modules/features/ModuleGlobalHistory.ts index 261cd7d..fcc7581 100644 --- a/src/modules/features/ModuleGlobalHistory.ts +++ b/src/modules/features/ModuleGlobalHistory.ts @@ -1,8 +1,8 @@ -import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts"; +import { AbstractObsidianModule } from "../AbstractObsidianModule.ts"; import { VIEW_TYPE_GLOBAL_HISTORY, GlobalHistoryView } from "./GlobalHistory/GlobalHistoryView.ts"; -export class ModuleObsidianGlobalHistory extends AbstractObsidianModule implements IObsidianModule { - $everyOnloadStart(): Promise { +export class ModuleObsidianGlobalHistory extends AbstractObsidianModule { + _everyOnloadStart(): Promise { this.addCommand({ id: "livesync-global-history", name: "Show vault history", @@ -17,6 +17,9 @@ export class ModuleObsidianGlobalHistory extends AbstractObsidianModule implemen } showGlobalHistory() { - void this.core.$$showView(VIEW_TYPE_GLOBAL_HISTORY); + void this.services.API.showWindow(VIEW_TYPE_GLOBAL_HISTORY); + } + onBindFunction(core: typeof this.core, services: typeof core.services): void { + services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this)); } } diff --git a/src/modules/features/ModuleInteractiveConflictResolver.ts b/src/modules/features/ModuleInteractiveConflictResolver.ts index deaef9d..6bbeb47 100644 --- a/src/modules/features/ModuleInteractiveConflictResolver.ts +++ b/src/modules/features/ModuleInteractiveConflictResolver.ts @@ -10,13 +10,14 @@ import { type diff_result, } from "../../lib/src/common/types.ts"; import { ConflictResolveModal } from "./InteractiveConflictResolving/ConflictResolveModal.ts"; -import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts"; +import { AbstractObsidianModule } from "../AbstractObsidianModule.ts"; import { displayRev, getPath, getPathWithoutPrefix } from "../../common/utils.ts"; import { fireAndForget } from "octagonal-wheels/promises"; import { serialized } from "octagonal-wheels/concurrency/lock"; +import type { LiveSyncCore } from "../../main.ts"; -export class ModuleInteractiveConflictResolver extends AbstractObsidianModule implements IObsidianModule { - $everyOnloadStart(): Promise { +export class ModuleInteractiveConflictResolver extends AbstractObsidianModule { + _everyOnloadStart(): Promise { this.addCommand({ id: "livesync-conflictcheck", name: "Pick a file to resolve conflict", @@ -34,7 +35,7 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im return Promise.resolve(true); } - async $anyResolveConflictByUI(filename: FilePathWithPrefix, conflictCheckResult: diff_result): Promise { + async _anyResolveConflictByUI(filename: FilePathWithPrefix, conflictCheckResult: diff_result): Promise { // UI for resolving conflicts should one-by-one. return await serialized(`conflict-resolve-ui`, async () => { this._log("Merge:open conflict dialog", LOG_LEVEL_VERBOSE); @@ -68,7 +69,7 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im } // 2. As usual, delete the conflicted revision and if there are no conflicts, write the resolved content to the storage. if ( - (await this.core.$$resolveConflictByDeletingRev(filename, delRev, "UI Concatenated")) == + (await this.services.conflict.resolveByDeletingRevision(filename, delRev, "UI Concatenated")) == MISSING_OR_ERROR ) { this._log( @@ -80,7 +81,7 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im } else if (typeof toDelete === "string") { // Select one of the conflicted revision to delete. if ( - (await this.core.$$resolveConflictByDeletingRev(filename, toDelete, "UI Selected")) == + (await this.services.conflict.resolveByDeletingRevision(filename, toDelete, "UI Selected")) == MISSING_OR_ERROR ) { this._log(`Merge: Something went wrong: ${filename}, (${toDelete})`, LOG_LEVEL_NOTICE); @@ -93,11 +94,11 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im // In here, some merge has been processed. // So we have to run replication if configured. // TODO: Make this is as a event request - if (this.settings.syncAfterMerge && !this.core.$$isSuspended()) { - await this.core.$$replicateByEvent(); + if (this.settings.syncAfterMerge && !this.services.appLifecycle.isSuspended()) { + await this.services.replication.replicateByEvent(); } // And, check it again. - await this.core.$$queueConflictCheck(filename); + await this.services.conflict.queueCheckFor(filename); return false; }); } @@ -120,14 +121,14 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im const target = await this.core.confirm.askSelectString("File to resolve conflict", notesList); if (target) { const targetItem = notes.find((e) => e.dispPath == target)!; - await this.core.$$queueConflictCheck(targetItem.path); - await this.core.$$waitForAllConflictProcessed(); + await this.services.conflict.queueCheckFor(targetItem.path); + await this.services.conflict.ensureAllProcessed(); return true; } return false; } - async $allScanStat(): Promise { + async _allScanStat(): Promise { const notes: { path: string; mtime: number }[] = []; this._log(`Checking conflicted files`, LOG_LEVEL_VERBOSE); for await (const doc of this.localDatabase.findAllDocs({ conflicts: true })) { @@ -157,4 +158,9 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im } return true; } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.appLifecycle.handleOnScanningStartupIssues(this._allScanStat.bind(this)); + services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this)); + services.conflict.handleResolveByUserInteraction(this._anyResolveConflictByUI.bind(this)); + } } diff --git a/src/modules/features/ModuleLog.ts b/src/modules/features/ModuleLog.ts index 3150590..35ecb3a 100644 --- a/src/modules/features/ModuleLog.ts +++ b/src/modules/features/ModuleLog.ts @@ -20,7 +20,7 @@ import { } from "../../lib/src/mock_and_interop/stores.ts"; import { eventHub } from "../../lib/src/hub/hub.ts"; import { EVENT_FILE_RENAMED, EVENT_LAYOUT_READY, EVENT_LEAF_ACTIVE_CHANGED } from "../../common/events.ts"; -import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts"; +import { AbstractObsidianModule } from "../AbstractObsidianModule.ts"; import { addIcon, normalizePath, Notice } from "../../deps.ts"; import { LOG_LEVEL_NOTICE, setGlobalLogFunction } from "octagonal-wheels/common/logger"; import { QueueProcessor } from "octagonal-wheels/concurrency/processor"; @@ -28,6 +28,7 @@ import { LogPaneView, VIEW_TYPE_LOG } from "./Log/LogPaneView.ts"; import { serialized } from "octagonal-wheels/concurrency/lock"; import { $msg } from "src/lib/src/common/i18n.ts"; import { P2PLogCollector } from "../../lib/src/replication/trystero/P2PReplicatorCore.ts"; +import type { LiveSyncCore } from "../../main.ts"; // This module cannot be a core module because it depends on the Obsidian UI. @@ -50,7 +51,7 @@ const recentLogProcessor = new QueueProcessor( const showDebugLog = false; export const MARK_DONE = "\u{2009}\u{2009}"; -export class ModuleLog extends AbstractObsidianModule implements IObsidianModule { +export class ModuleLog extends AbstractObsidianModule { registerView = this.plugin.registerView.bind(this.plugin); statusBar?: HTMLElement; @@ -178,7 +179,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule }); const statusBarLabels = reactive(() => { - const scheduleMessage = this.core.$$isReloadingScheduled() + const scheduleMessage = this.services.appLifecycle.isReloadingScheduled() ? `WARNING! RESTARTING OBSIDIAN IS SCHEDULED!\n` : ""; const { message } = statusLineLabel(); @@ -199,7 +200,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule statusBarLabels.onChanged((label) => applyToDisplay(label.value)); } - $everyOnload(): Promise { + private _everyOnload(): Promise { eventHub.onEvent(EVENT_LEAF_ACTIVE_CHANGED, () => this.onActiveLeafChange()); eventHub.onceEvent(EVENT_LAYOUT_READY, () => this.onActiveLeafChange()); @@ -219,15 +220,15 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule const thisFile = this.app.workspace.getActiveFile(); if (!thisFile) return ""; // Case Sensitivity - if (this.core.$$shouldCheckCaseInsensitive()) { + if (this.services.setting.shouldCheckCaseInsensitively()) { const f = this.core.storageAccess .getFiles() .map((e) => e.path) .filter((e) => e.toLowerCase() == thisFile.path.toLowerCase()); if (f.length > 1) return "Not synchronised: There are multiple files with the same name"; } - if (!(await this.core.$$isTargetFile(thisFile.path))) return "Not synchronised: not a target file"; - if (this.core.$$isFileSizeExceeded(thisFile.stat.size)) return "Not synchronised: File size exceeded"; + if (!(await this.services.vault.isTargetFile(thisFile.path))) return "Not synchronised: not a target file"; + if (this.services.vault.isFileSizeTooLarge(thisFile.stat.size)) return "Not synchronised: File size exceeded"; return ""; } async setFileStatus() { @@ -287,14 +288,14 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule }); } - $allStartOnUnload(): Promise { + private _allStartOnUnload(): Promise { if (this.statusDiv) { this.statusDiv.remove(); } document.querySelectorAll(`.livesync-status`)?.forEach((e) => e.remove()); return Promise.resolve(true); } - $everyOnloadStart(): Promise { + _everyOnloadStart(): Promise { addIcon( "view-log", ` @@ -303,23 +304,23 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule ` ); this.addRibbonIcon("view-log", $msg("moduleLog.showLog"), () => { - void this.core.$$showView(VIEW_TYPE_LOG); + void this.services.API.showWindow(VIEW_TYPE_LOG); }).addClass("livesync-ribbon-showlog"); this.addCommand({ id: "view-log", name: "Show log", callback: () => { - void this.core.$$showView(VIEW_TYPE_LOG); + void this.services.API.showWindow(VIEW_TYPE_LOG); }, }); this.registerView(VIEW_TYPE_LOG, (leaf) => new LogPaneView(leaf, this.plugin)); return Promise.resolve(true); } - $everyOnloadAfterLoadSettings(): Promise { + private _everyOnloadAfterLoadSettings(): Promise { logStore .pipeTo( - new QueueProcessor((logs) => logs.forEach((e) => this.core.$$addLog(e.message, e.level, e.key)), { + new QueueProcessor((logs) => logs.forEach((e) => this._addLog(e.message, e.level, e.key)), { suspended: false, batchSize: 20, concurrentLimit: 1, @@ -366,7 +367,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule }) ); } - $$addLog(message: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key = ""): void { + _addLog(message: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key = ""): void { if (level == LOG_LEVEL_DEBUG && !showDebugLog) { return; } @@ -376,7 +377,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule if (this.settings && !this.settings.showVerboseLog && level == LOG_LEVEL_VERBOSE) { return; } - const vaultName = this.core.$$getVaultName(); + const vaultName = this.services.vault.getVaultName(); const now = new Date(); const timestamp = now.toLocaleString(); const messageContent = @@ -437,4 +438,10 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule } } } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this)); + services.appLifecycle.handleOnSettingLoaded(this._everyOnloadAfterLoadSettings.bind(this)); + services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this)); + services.appLifecycle.handleOnBeforeUnload(this._allStartOnUnload.bind(this)); + } } diff --git a/src/modules/features/ModuleObsidianDocumentHistory.ts b/src/modules/features/ModuleObsidianDocumentHistory.ts index 752aeba..95f2211 100644 --- a/src/modules/features/ModuleObsidianDocumentHistory.ts +++ b/src/modules/features/ModuleObsidianDocumentHistory.ts @@ -2,18 +2,18 @@ import { type TFile } from "obsidian"; import { eventHub } from "../../common/events.ts"; import { EVENT_REQUEST_SHOW_HISTORY } from "../../common/obsidianEvents.ts"; import type { FilePathWithPrefix, LoadedEntry, DocumentID } from "../../lib/src/common/types.ts"; -import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts"; +import { AbstractObsidianModule } from "../AbstractObsidianModule.ts"; import { DocumentHistoryModal } from "./DocumentHistory/DocumentHistoryModal.ts"; import { getPath } from "../../common/utils.ts"; import { fireAndForget } from "octagonal-wheels/promises"; -export class ModuleObsidianDocumentHistory extends AbstractObsidianModule implements IObsidianModule { - $everyOnloadStart(): Promise { +export class ModuleObsidianDocumentHistory extends AbstractObsidianModule { + _everyOnloadStart(): Promise { this.addCommand({ id: "livesync-history", name: "Show history", callback: () => { - const file = this.core.$$getActiveFilePath(); + const file = this.services.vault.getActiveFilePath(); if (file) this.showHistory(file, undefined); }, }); @@ -51,4 +51,7 @@ export class ModuleObsidianDocumentHistory extends AbstractObsidianModule implem this.showHistory(targetId.path, targetId.id); } } + onBindFunction(core: typeof this.core, services: typeof core.services): void { + services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this)); + } } diff --git a/src/modules/features/ModuleObsidianSetting.ts b/src/modules/features/ModuleObsidianSetting.ts index bba7a8d..1817041 100644 --- a/src/modules/features/ModuleObsidianSetting.ts +++ b/src/modules/features/ModuleObsidianSetting.ts @@ -1,4 +1,4 @@ -import { type IObsidianModule, AbstractObsidianModule } from "../AbstractObsidianModule.ts"; +import { AbstractObsidianModule } from "../AbstractObsidianModule.ts"; // import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser"; import { EVENT_REQUEST_RELOAD_SETTING_TAB, EVENT_SETTING_SAVED, eventHub } from "../../common/events.ts"; import { @@ -16,8 +16,9 @@ import { isCloudantURI } from "../../lib/src/pouchdb/utils_couchdb.ts"; import { getLanguage } from "obsidian"; import { SUPPORTED_I18N_LANGS, type I18N_LANGS } from "../../lib/src/common/rosetta.ts"; import { decryptString, encryptString } from "@/lib/src/encryption/stringEncryption.ts"; -export class ModuleObsidianSettings extends AbstractObsidianModule implements IObsidianModule { - async $everyOnLayoutReady(): Promise { +import type { LiveSyncCore } from "../../main.ts"; +export class ModuleObsidianSettings extends AbstractObsidianModule { + async _everyOnLayoutReady(): Promise { let isChanged = false; if (this.settings.displayLanguage == "") { const obsidianLanguage = getLanguage(); @@ -32,7 +33,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO } else if (this.settings.displayLanguage == "") { this.settings.displayLanguage = "def"; setLang(this.settings.displayLanguage); - await this.core.$$saveSettingData(); + await this.services.setting.saveSettingData(); } } if (isChanged) { @@ -46,7 +47,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO this.settings.displayLanguage = "def"; setLang(this.settings.displayLanguage); } - await this.core.$$saveSettingData(); + await this.services.setting.saveSettingData(); } return true; } @@ -61,13 +62,13 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO return methodFunc(); } - $$saveDeviceAndVaultName(): void { - const lsKey = "obsidian-live-sync-vaultanddevicename-" + this.core.$$getVaultName(); - localStorage.setItem(lsKey, this.core.$$getDeviceAndVaultName() || ""); + _saveDeviceAndVaultName(): void { + const lsKey = "obsidian-live-sync-vaultanddevicename-" + this.services.vault.getVaultName(); + localStorage.setItem(lsKey, this.services.setting.getDeviceAndVaultName() || ""); } usedPassphrase = ""; - $$clearUsedPassphrase(): void { + private _clearUsedPassphrase(): void { this.usedPassphrase = ""; } @@ -106,8 +107,8 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO return `${"appId" in this.app ? this.app.appId : ""}`; } - async $$saveSettingData() { - this.core.$$saveDeviceAndVaultName(); + async _saveSettingData() { + this.services.setting.saveDeviceAndVaultName(); const settings = { ...this.settings }; settings.deviceAndVaultName = ""; if (this.usedPassphrase == "" && !(await this.getPassphrase(settings))) { @@ -174,7 +175,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO } } - async $$decryptSettings(settings: ObsidianLiveSyncSettings): Promise { + async _decryptSettings(settings: ObsidianLiveSyncSettings): Promise { const passphrase = await this.getPassphrase(settings); if (passphrase === false) { this._log("No passphrase found for data.json! Verify configuration before syncing.", LOG_LEVEL_URGENT); @@ -234,7 +235,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO * @param settings * @returns */ - $$adjustSettings(settings: ObsidianLiveSyncSettings): Promise { + _adjustSettings(settings: ObsidianLiveSyncSettings): Promise { // Adjust settings as needed // Delete this feature to avoid problems on mobile. @@ -264,7 +265,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO return Promise.resolve(settings); } - async $$loadSettings(): Promise { + async _loadSettings(): Promise { const settings = Object.assign({}, DEFAULT_SETTINGS, await this.core.loadData()) as ObsidianLiveSyncSettings; if (typeof settings.isConfigured == "undefined") { @@ -277,17 +278,17 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO } } - this.settings = await this.core.$$decryptSettings(settings); + this.settings = await this.services.setting.decryptSettings(settings); setLang(this.settings.displayLanguage); - await this.core.$$adjustSettings(this.settings); + await this.services.setting.adjustSettings(this.settings); - const lsKey = "obsidian-live-sync-vaultanddevicename-" + this.core.$$getVaultName(); + const lsKey = "obsidian-live-sync-vaultanddevicename-" + this.services.vault.getVaultName(); if (this.settings.deviceAndVaultName != "") { if (!localStorage.getItem(lsKey)) { - this.core.$$setDeviceAndVaultName(this.settings.deviceAndVaultName); - this.$$saveDeviceAndVaultName(); + this.services.setting.setDeviceAndVaultName(this.settings.deviceAndVaultName); + this.services.setting.saveDeviceAndVaultName(); this.settings.deviceAndVaultName = ""; } } @@ -298,8 +299,8 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO ); this.settings.customChunkSize = 0; } - this.core.$$setDeviceAndVaultName(localStorage.getItem(lsKey) || ""); - if (this.core.$$getDeviceAndVaultName() == "") { + this.services.setting.setDeviceAndVaultName(localStorage.getItem(lsKey) || ""); + if (this.services.setting.getDeviceAndVaultName() == "") { if (this.settings.usePluginSync) { this._log("Device name missing. Disabling plug-in sync.", LOG_LEVEL_NOTICE); this.settings.usePluginSync = false; @@ -309,4 +310,14 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO // this.core.ignoreFiles = this.settings.ignoreFiles.split(",").map(e => e.trim()); eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB); } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + super.onBindFunction(core, services); + services.appLifecycle.handleLayoutReady(this._everyOnLayoutReady.bind(this)); + services.setting.handleClearUsedPassphrase(this._clearUsedPassphrase.bind(this)); + services.setting.handleDecryptSettings(this._decryptSettings.bind(this)); + services.setting.handleAdjustSettings(this._adjustSettings.bind(this)); + services.setting.handleLoadSettings(this._loadSettings.bind(this)); + services.setting.handleSaveDeviceAndVaultName(this._saveDeviceAndVaultName.bind(this)); + services.setting.handleSaveSettingData(this._saveSettingData.bind(this)); + } } diff --git a/src/modules/features/ModuleObsidianSettingAsMarkdown.ts b/src/modules/features/ModuleObsidianSettingAsMarkdown.ts index accbf55..d9c4ff7 100644 --- a/src/modules/features/ModuleObsidianSettingAsMarkdown.ts +++ b/src/modules/features/ModuleObsidianSettingAsMarkdown.ts @@ -1,4 +1,4 @@ -import { type IObsidianModule, AbstractObsidianModule } from "../AbstractObsidianModule.ts"; +import { AbstractObsidianModule } from "../AbstractObsidianModule.ts"; // import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser"; import { isObjectDifferent } from "octagonal-wheels/object"; import { EVENT_SETTING_SAVED, eventHub } from "../../common/events"; @@ -8,8 +8,8 @@ import { parseYaml, stringifyYaml } from "../../deps"; import { LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger"; const SETTING_HEADER = "````yaml:livesync-setting\n"; const SETTING_FOOTER = "\n````"; -export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule implements IObsidianModule { - $everyOnloadStart(): Promise { +export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule { + _everyOnloadStart(): Promise { this.addCommand({ id: "livesync-export-config", name: "Write setting markdown manually", @@ -18,7 +18,7 @@ export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule imp return this.settings.settingSyncFile != ""; } fireAndForget(async () => { - await this.core.$$saveSettingData(); + await this.services.setting.saveSettingData(); }); }, }); @@ -160,7 +160,7 @@ export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule imp result == APPLY_AND_FETCH ) { this.core.settings = settingToApply; - await this.core.$$saveSettingData(); + await this.services.setting.saveSettingData(); if (result == APPLY_ONLY) { this._log("Loaded settings have been applied!", LOG_LEVEL_NOTICE); return; @@ -171,7 +171,7 @@ export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule imp if (result == APPLY_AND_FETCH) { await this.core.rebuilder.scheduleFetch(); } - this.core.$$performRestart(); + this.services.appLifecycle.performRestart(); } }); }); @@ -242,4 +242,7 @@ We can perform a command in this file. this._log(`Markdown setting: ${filename} has been updated!`, LOG_LEVEL_VERBOSE); } } + onBindFunction(core: typeof this.plugin, services: typeof core.services): void { + services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this)); + } } diff --git a/src/modules/features/ModuleObsidianSettingTab.ts b/src/modules/features/ModuleObsidianSettingTab.ts index 994859f..48997ab 100644 --- a/src/modules/features/ModuleObsidianSettingTab.ts +++ b/src/modules/features/ModuleObsidianSettingTab.ts @@ -1,12 +1,12 @@ import { ObsidianLiveSyncSettingTab } from "./SettingDialogue/ObsidianLiveSyncSettingTab.ts"; -import { type IObsidianModule, AbstractObsidianModule } from "../AbstractObsidianModule.ts"; +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"; -export class ModuleObsidianSettingDialogue extends AbstractObsidianModule implements IObsidianModule { +export class ModuleObsidianSettingDialogue extends AbstractObsidianModule { settingTab!: ObsidianLiveSyncSettingTab; - $everyOnloadStart(): Promise { + _everyOnloadStart(): Promise { this.settingTab = new ObsidianLiveSyncSettingTab(this.app, this.plugin); this.plugin.addSettingTab(this.settingTab); eventHub.onEvent(EVENT_REQUEST_OPEN_SETTINGS, () => this.openSetting()); @@ -29,4 +29,7 @@ export class ModuleObsidianSettingDialogue extends AbstractObsidianModule implem get appId() { return `${"appId" in this.app ? this.app.appId : ""}`; } + onBindFunction(core: typeof this.plugin, services: typeof core.services): void { + services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this)); + } } diff --git a/src/modules/features/ModuleSetupObsidian.ts b/src/modules/features/ModuleSetupObsidian.ts index cb9031e..4d7489c 100644 --- a/src/modules/features/ModuleSetupObsidian.ts +++ b/src/modules/features/ModuleSetupObsidian.ts @@ -14,15 +14,16 @@ import { EVENT_REQUEST_SHOW_SETUP_QR, eventHub, } from "../../common/events.ts"; -import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts"; +import { AbstractObsidianModule } from "../AbstractObsidianModule.ts"; import { decodeAnyArray, encodeAnyArray } from "../../common/utils.ts"; import qrcode from "qrcode-generator"; import { $msg } from "../../lib/src/common/i18n.ts"; import { performDoctorConsultation, RebuildOptions } from "@/lib/src/common/configForDoc.ts"; import { encryptString, decryptString } from "@/lib/src/encryption/stringEncryption.ts"; +import type { LiveSyncCore } from "../../main.ts"; -export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsidianModule { - $everyOnload(): Promise { +export class ModuleSetupObsidian extends AbstractObsidianModule { + private _everyOnload(): Promise { this.registerObsidianProtocolHandler("setuplivesync", async (conf: any) => { if (conf.settings) { await this.setupWizard(conf.settings); @@ -182,7 +183,7 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi } const newSettings = JSON.parse(JSON.stringify(tryingSettings)) as ObsidianLiveSyncSettings; - const remoteConfig = await this.core.$$fetchRemotePreferredTweakValues(newSettings); + const remoteConfig = await this.services.tweakValue.fetchRemotePreferred(newSettings); if (remoteConfig) { this._log("Remote configuration found.", LOG_LEVEL_NOTICE); const resultSettings = { @@ -282,16 +283,16 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi ); if (setupType == setupJustImport) { this.core.settings = newSettingW; - this.core.$$clearUsedPassphrase(); + this.services.setting.clearUsedPassphrase(); await this.core.saveSettings(); } else if (setupType == setupAsNew) { this.core.settings = newSettingW; - this.core.$$clearUsedPassphrase(); + this.services.setting.clearUsedPassphrase(); await this.core.saveSettings(); await this.core.rebuilder.$fetchLocal(); } else if (setupType == setupAsMerge) { this.core.settings = newSettingW; - this.core.$$clearUsedPassphrase(); + this.services.setting.clearUsedPassphrase(); await this.core.saveSettings(); await this.core.rebuilder.$fetchLocal(true); } else if (setupType == setupAgain) { @@ -308,7 +309,7 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi } this.core.settings = newSettingW; await this.core.saveSettings(); - this.core.$$clearUsedPassphrase(); + this.services.setting.clearUsedPassphrase(); await this.core.rebuilder.$rebuildEverything(); } else { // Explicitly cancel the operation or the dialog was closed. @@ -345,4 +346,7 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi this._log(ex, LOG_LEVEL_VERBOSE); } } + onBindFunction(core: LiveSyncCore, services: typeof core.services): void { + services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this)); + } } diff --git a/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts b/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts index 3335958..2fb26e8 100644 --- a/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts +++ b/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts @@ -86,6 +86,9 @@ export function createStub(name: string, key: string, value: string, panel: stri export class ObsidianLiveSyncSettingTab extends PluginSettingTab { plugin: ObsidianLiveSyncPlugin; + get services() { + return this.plugin.services; + } selectedScreen = ""; _editingSettings?: AllSettings; @@ -139,8 +142,8 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { return await Promise.resolve(); } if (key == "deviceAndVaultName") { - this.plugin.$$setDeviceAndVaultName(this.editingSettings?.[key] ?? ""); - this.plugin.$$saveDeviceAndVaultName(); + this.services.setting.setDeviceAndVaultName(this.editingSettings?.[key] ?? ""); + this.services.setting.saveDeviceAndVaultName(); return await Promise.resolve(); } } @@ -210,7 +213,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { const ret = { ...OnDialogSettingsDefault }; ret.configPassphrase = localStorage.getItem("ls-setting-passphrase") || ""; ret.preset = ""; - ret.deviceAndVaultName = this.plugin.$$getDeviceAndVaultName(); + ret.deviceAndVaultName = this.services.setting.getDeviceAndVaultName(); return ret; } computeAllLocalSettings(): Partial { @@ -295,7 +298,11 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { async testConnection(settingOverride: Partial = {}): Promise { const trialSetting = { ...this.editingSettings, ...settingOverride }; - const replicator = await this.plugin.$anyNewReplicator(trialSetting); + const replicator = await this.services.replicator.getNewReplicator(trialSetting); + if (!replicator) { + Logger("No replicator available for the current settings.", LOG_LEVEL_NOTICE); + return; + } await replicator.tryConnectRemote(trialSetting); const status = await replicator.getRemoteStatus(trialSetting); if (status) { @@ -546,10 +553,14 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { const settingForCheck: RemoteDBSettings = { ...this.editingSettings, }; - const replicator = this.plugin.$anyNewReplicator(settingForCheck); + const replicator = this.services.replicator.getNewReplicator(settingForCheck); if (!(replicator instanceof LiveSyncCouchDBReplicator)) return true; - const db = await replicator.connectRemoteCouchDBWithSetting(settingForCheck, this.plugin.$$isMobile(), true); + const db = await replicator.connectRemoteCouchDBWithSetting( + settingForCheck, + this.services.API.isMobile(), + true + ); if (typeof db === "string") { Logger($msg("obsidianLiveSyncSettingTab.logCheckPassphraseFailed", { db }), LOG_LEVEL_NOTICE); return false; @@ -588,8 +599,8 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { this.editingSettings.passphrase = ""; } this.applyAllSettings(); - await this.plugin.$allSuspendAllSync(); - await this.plugin.$allSuspendExtraSync(); + await this.services.setting.suspendAllSync(); + await this.services.setting.suspendExtraSync(); this.reloadAllSettings(); this.editingSettings.isConfigured = true; Logger($msg("obsidianLiveSyncSettingTab.logRebuildNote"), LOG_LEVEL_NOTICE); @@ -638,12 +649,12 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { await this.applyAllSettings(); if (result == OPTION_FETCH) { await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG3_HR, ""); - this.plugin.$$scheduleAppReload(); + this.services.appLifecycle.scheduleRestart(); this.closeSetting(); // await rebuildDB("localOnly"); } else if (result == OPTION_REBUILD_BOTH) { await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG2_HR, ""); - this.plugin.$$scheduleAppReload(); + this.services.appLifecycle.scheduleRestart(); this.closeSetting(); } else if (result == OPTION_ONLY_SETTING) { await this.plugin.saveSettings(); diff --git a/src/modules/features/SettingDialogue/PaneHatch.ts b/src/modules/features/SettingDialogue/PaneHatch.ts index c42f297..cfc6a01 100644 --- a/src/modules/features/SettingDialogue/PaneHatch.ts +++ b/src/modules/features/SettingDialogue/PaneHatch.ts @@ -156,7 +156,7 @@ export function paneHatch(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, } const obsidianInfo = { navigator: navigator.userAgent, - fileSystem: this.plugin.$$isStorageInsensitive() ? "insensitive" : "sensitive", + fileSystem: this.plugin.services.vault.isStorageInsensitive() ? "insensitive" : "sensitive", }; const msgConfig = `# ---- Obsidian info ---- ${stringifyYaml(obsidianInfo)} @@ -182,10 +182,10 @@ ${stringifyYaml({ void addPanel(paneEl, "Scram Switches").then((paneEl) => { new Setting(paneEl).autoWireToggle("suspendFileWatching"); - this.addOnSaved("suspendFileWatching", () => this.plugin.$$askReload()); + this.addOnSaved("suspendFileWatching", () => this.services.appLifecycle.askRestart()); new Setting(paneEl).autoWireToggle("suspendParseReplicationResult"); - this.addOnSaved("suspendParseReplicationResult", () => this.plugin.$$askReload()); + this.addOnSaved("suspendParseReplicationResult", () => this.services.appLifecycle.askRestart()); }); void addPanel(paneEl, "Recovery and Repair").then((paneEl) => { @@ -384,15 +384,16 @@ ${stringifyYaml({ ? await this.plugin.storageAccess.statHidden(path) : false; const fileOnStorage = stat != null ? stat : false; - if (!(await this.plugin.$$isTargetFile(path))) return incProc(); + if (!(await this.services.vault.isTargetFile(path))) return incProc(); const releaser = await semaphore.acquire(1); - if (fileOnStorage && this.plugin.$$isFileSizeExceeded(fileOnStorage.size)) + if (fileOnStorage && this.services.vault.isFileSizeTooLarge(fileOnStorage.size)) return incProc(); try { const isHiddenFile = path.startsWith("."); const dbPath = isHiddenFile ? addPrefix(path, ICHeader) : path; const fileOnDB = await this.plugin.localDatabase.getDBEntry(dbPath); - if (fileOnDB && this.plugin.$$isFileSizeExceeded(fileOnDB.size)) return incProc(); + if (fileOnDB && this.services.vault.isFileSizeTooLarge(fileOnDB.size)) + return incProc(); if (!fileOnDB && fileOnStorage) { Logger(`Compare: Not found on the local database: ${path}`, LOG_LEVEL_NOTICE); @@ -436,7 +437,7 @@ ${stringifyYaml({ .onClick(async () => { for await (const docName of this.plugin.localDatabase.findAllDocNames()) { if (!docName.startsWith("f:")) { - const idEncoded = await this.plugin.$$path2id(docName as FilePathWithPrefix); + const idEncoded = await this.services.path.path2id(docName as FilePathWithPrefix); const doc = await this.plugin.localDatabase.getRaw(docName as DocumentID); if (!doc) continue; if (doc.type != "newnote" && doc.type != "plain") { @@ -477,7 +478,7 @@ ${stringifyYaml({ if ((await this.plugin.localDatabase.putRaw(doc)).ok) { Logger(`Old ${docName} has been deleted`, LOG_LEVEL_NOTICE); } - await this.plugin.$$queueConflictCheckIfOpen(docName as FilePathWithPrefix); + await this.services.conflict.queueCheckForIfOpen(docName as FilePathWithPrefix); } else { Logger(`Converting ${docName} Failed!`, LOG_LEVEL_NOTICE); Logger(ret, LOG_LEVEL_VERBOSE); @@ -512,7 +513,7 @@ ${stringifyYaml({ .onClick(async () => { this.editingSettings.isConfigured = false; await this.saveAllDirtySettings(); - this.plugin.$$askReload(); + this.services.appLifecycle.askRestart(); }) ); diff --git a/src/modules/features/SettingDialogue/PaneMaintenance.ts b/src/modules/features/SettingDialogue/PaneMaintenance.ts index 205dddc..9afa213 100644 --- a/src/modules/features/SettingDialogue/PaneMaintenance.ts +++ b/src/modules/features/SettingDialogue/PaneMaintenance.ts @@ -32,7 +32,7 @@ export function paneMaintenance( (e) => { e.addEventListener("click", () => { fireAndForget(async () => { - await this.plugin.$$markRemoteResolved(); + await this.services.remote.markResolved(); this.display(); }); }); @@ -59,7 +59,7 @@ export function paneMaintenance( (e) => { e.addEventListener("click", () => { fireAndForget(async () => { - await this.plugin.$$markRemoteUnlocked(); + await this.services.remote.markUnlocked(); this.display(); }); }); @@ -78,7 +78,7 @@ export function paneMaintenance( .setDisabled(false) .setWarning() .onClick(async () => { - await this.plugin.$$markRemoteLocked(); + await this.services.remote.markLocked(); }) ) .addOnUpdate(this.onlyOnCouchDBOrMinIO); @@ -93,7 +93,7 @@ export function paneMaintenance( .setWarning() .onClick(async () => { await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG, ""); - this.plugin.$$performRestart(); + this.services.appLifecycle.performRestart(); }) ); }); @@ -255,7 +255,7 @@ export function paneMaintenance( .setDisabled(false) .onClick(async () => { await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG3_HR, ""); - this.plugin.$$performRestart(); + this.services.appLifecycle.performRestart(); }) ) .addButton((button) => @@ -294,7 +294,7 @@ export function paneMaintenance( .setDisabled(false) .onClick(async () => { await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG2_HR, ""); - this.plugin.$$performRestart(); + this.services.appLifecycle.performRestart(); }) ) .addButton((button) => @@ -405,8 +405,8 @@ export function paneMaintenance( .setWarning() .setDisabled(false) .onClick(async () => { - await this.plugin.$$resetLocalDatabase(); - await this.plugin.$$initializeDatabase(); + await this.services.database.resetDatabase(); + await this.services.databaseEvents.initialiseDatabase(); }) ); }); diff --git a/src/modules/features/SettingDialogue/PanePatches.ts b/src/modules/features/SettingDialogue/PanePatches.ts index 9abde61..3a46a16 100644 --- a/src/modules/features/SettingDialogue/PanePatches.ts +++ b/src/modules/features/SettingDialogue/PanePatches.ts @@ -63,7 +63,7 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen this.addOnSaved("additionalSuffixOfDatabaseName", async (key) => { Logger("Suffix has been changed. Reopening database...", LOG_LEVEL_NOTICE); - await this.plugin.$$initializeDatabase(); + await this.services.databaseEvents.initialiseDatabase(); }); new Setting(paneEl).autoWireDropDown("hashAlg", { diff --git a/src/modules/features/SettingDialogue/PaneRemoteConfig.ts b/src/modules/features/SettingDialogue/PaneRemoteConfig.ts index 7686fb9..b501dd1 100644 --- a/src/modules/features/SettingDialogue/PaneRemoteConfig.ts +++ b/src/modules/features/SettingDialogue/PaneRemoteConfig.ts @@ -403,7 +403,7 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleCouchDB"), undefined, this.onlyOnCouchDB).then( (paneEl) => { - if (this.plugin.$$isMobile()) { + if (this.services.API.isMobile()) { this.createEl( paneEl, "div", @@ -630,7 +630,8 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal .setDisabled(false) .onClick(async () => { const trialSetting = { ...this.initialSettings, ...this.editingSettings }; - const newTweaks = await this.plugin.$$checkAndAskUseRemoteConfiguration(trialSetting); + const newTweaks = + await this.services.tweakValue.checkAndAskUseRemoteConfiguration(trialSetting); if (newTweaks.result !== false) { if (this.inWizard) { this.editingSettings = { ...this.editingSettings, ...newTweaks.result }; @@ -648,15 +649,15 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal } )) == "no" ) { - await this.plugin.$$saveSettingData(); + await this.services.setting.saveSettingData(); return; } - await this.plugin.$$saveSettingData(); + await this.services.setting.saveSettingData(); await this.plugin.rebuilder.scheduleFetch(); - await this.plugin.$$scheduleAppReload(); + this.services.appLifecycle.scheduleRestart(); return; } else { - await this.plugin.$$saveSettingData(); + await this.services.setting.saveSettingData(); } } } @@ -727,7 +728,7 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal )) == "yes" ) { const trialSetting = { ...this.initialSettings, ...this.editingSettings }; - const newTweaks = await this.plugin.$$checkAndAskUseRemoteConfiguration(trialSetting); + const newTweaks = await this.services.tweakValue.checkAndAskUseRemoteConfiguration(trialSetting); if (newTweaks.result !== false) { this.editingSettings = { ...this.editingSettings, ...newTweaks.result }; this.requestUpdate(); diff --git a/src/modules/features/SettingDialogue/PaneSetup.ts b/src/modules/features/SettingDialogue/PaneSetup.ts index 4dd8aab..eade8c6 100644 --- a/src/modules/features/SettingDialogue/PaneSetup.ts +++ b/src/modules/features/SettingDialogue/PaneSetup.ts @@ -46,7 +46,7 @@ export function paneSetup( text.setButtonText($msg("obsidianLiveSyncSettingTab.btnEnable")).onClick(async () => { this.editingSettings.isConfigured = true; await this.saveAllDirtySettings(); - this.plugin.$$askReload(); + this.services.appLifecycle.askRestart(); }); }); }); @@ -91,10 +91,10 @@ export function paneSetup( this.editingSettings = { ...this.editingSettings, ...DEFAULT_SETTINGS }; await this.saveAllDirtySettings(); this.plugin.settings = { ...DEFAULT_SETTINGS }; - await this.plugin.$$saveSettingData(); - await this.plugin.$$resetLocalDatabase(); + await this.services.setting.saveSettingData(); + await this.services.database.resetDatabase(); // await this.plugin.initializeDatabase(); - this.plugin.$$askReload(); + this.services.appLifecycle.askRestart(); } }) .setWarning(); diff --git a/src/modules/features/SettingDialogue/PaneSyncSettings.ts b/src/modules/features/SettingDialogue/PaneSyncSettings.ts index 2cdbc74..b39b945 100644 --- a/src/modules/features/SettingDialogue/PaneSyncSettings.ts +++ b/src/modules/features/SettingDialogue/PaneSyncSettings.ts @@ -105,7 +105,7 @@ export function paneSyncSettings( if (!this.editingSettings.isConfigured) { this.editingSettings.isConfigured = true; await this.saveAllDirtySettings(); - await this.plugin.$$realizeSettingSyncMode(); + await this.services.setting.onRealiseSetting(); await this.rebuildDB("localOnly"); // this.resetEditingSettings(); if ( @@ -124,13 +124,13 @@ export function paneSyncSettings( await this.confirmRebuild(); } else { await this.saveAllDirtySettings(); - await this.plugin.$$realizeSettingSyncMode(); - this.plugin.$$askReload(); + await this.services.setting.onRealiseSetting(); + this.services.appLifecycle.askRestart(); } } } else { await this.saveAllDirtySettings(); - await this.plugin.$$realizeSettingSyncMode(); + await this.services.setting.onRealiseSetting(); } }); }); @@ -169,7 +169,7 @@ export function paneSyncSettings( } await this.saveSettings(["liveSync", "periodicReplication"]); - await this.plugin.$$realizeSettingSyncMode(); + await this.services.setting.onRealiseSetting(); }); new Setting(paneEl) @@ -289,21 +289,21 @@ export function paneSyncSettings( button.setButtonText("Merge").onClick(async () => { this.closeSetting(); // this.resetEditingSettings(); - await this.plugin.$anyConfigureOptionalSyncFeature("MERGE"); + await this.services.setting.enableOptionalFeature("MERGE"); }); }) .addButton((button) => { button.setButtonText("Fetch").onClick(async () => { this.closeSetting(); // this.resetEditingSettings(); - await this.plugin.$anyConfigureOptionalSyncFeature("FETCH"); + await this.services.setting.enableOptionalFeature("FETCH"); }); }) .addButton((button) => { button.setButtonText("Overwrite").onClick(async () => { this.closeSetting(); // this.resetEditingSettings(); - await this.plugin.$anyConfigureOptionalSyncFeature("OVERWRITE"); + await this.services.setting.enableOptionalFeature("OVERWRITE"); }); }); } diff --git a/src/modules/main/ModuleLiveSyncMain.ts b/src/modules/main/ModuleLiveSyncMain.ts index 07dcf5f..d1e1ba0 100644 --- a/src/modules/main/ModuleLiveSyncMain.ts +++ b/src/modules/main/ModuleLiveSyncMain.ts @@ -13,12 +13,13 @@ import { versionNumberString2Number } from "../../lib/src/string_and_binary/conv import { cancelAllPeriodicTask, cancelAllTasks } from "octagonal-wheels/concurrency/task"; import { stopAllRunningProcessors } from "octagonal-wheels/concurrency/processor"; import { AbstractModule } from "../AbstractModule.ts"; -import type { ICoreModule } from "../ModuleTypes.ts"; import { EVENT_PLATFORM_UNLOADED } from "../../lib/src/PlatformAPIs/base/APIBase.ts"; +import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts"; +import type { LiveSyncCore } from "../../main.ts"; -export class ModuleLiveSyncMain extends AbstractModule implements ICoreModule { - async $$onLiveSyncReady() { - if (!(await this.core.$everyOnLayoutReady())) return; +export class ModuleLiveSyncMain extends AbstractModule { + async _onLiveSyncReady() { + if (!(await this.core.services.appLifecycle.onLayoutReady())) return false; eventHub.emitEvent(EVENT_LAYOUT_READY); if (this.settings.suspendFileWatching || this.settings.suspendParseReplicationResult) { const ANSWER_KEEP = $msg("moduleLiveSyncMain.optionKeepLiveSyncDisabled"); @@ -36,46 +37,49 @@ export class ModuleLiveSyncMain extends AbstractModule implements ICoreModule { this.settings.suspendFileWatching = false; this.settings.suspendParseReplicationResult = false; await this.saveSettings(); - await this.core.$$scheduleAppReload(); - return; + this.services.appLifecycle.scheduleRestart(); + return false; } } - const isInitialized = await this.core.$$initializeDatabase(false, false); + const isInitialized = await this.services.databaseEvents.initialiseDatabase(false, false); if (!isInitialized) { //TODO:stop all sync. return false; } - if (!(await this.core.$everyOnFirstInitialize())) return; - await this.core.$$realizeSettingSyncMode(); + if (!(await this.core.services.appLifecycle.onFirstInitialise())) return false; + // await this.core.$$realizeSettingSyncMode(); + await this.services.setting.onRealiseSetting(); fireAndForget(async () => { this._log($msg("moduleLiveSyncMain.logAdditionalSafetyScan"), LOG_LEVEL_VERBOSE); - if (!(await this.core.$allScanStat())) { + if (!(await this.services.appLifecycle.onScanningStartupIssues())) { this._log($msg("moduleLiveSyncMain.logSafetyScanFailed"), LOG_LEVEL_NOTICE); } else { this._log($msg("moduleLiveSyncMain.logSafetyScanCompleted"), LOG_LEVEL_VERBOSE); } }); + return true; } - $$wireUpEvents(): void { + _wireUpEvents() { eventHub.onEvent(EVENT_SETTING_SAVED, (settings: ObsidianLiveSyncSettings) => { this.localDatabase.settings = settings; setLang(settings.displayLanguage); eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB); }); eventHub.onEvent(EVENT_SETTING_SAVED, (settings: ObsidianLiveSyncSettings) => { - fireAndForget(() => this.core.$$realizeSettingSyncMode()); + fireAndForget(() => this.core.services.setting.onRealiseSetting()); }); + return Promise.resolve(true); } - async $$onLiveSyncLoad(): Promise { - this.$$wireUpEvents(); + async _onLiveSyncLoad(): Promise { + await this.services.appLifecycle.onWireUpEvents(); // debugger; eventHub.emitEvent(EVENT_PLUGIN_LOADED, this.core); this._log($msg("moduleLiveSyncMain.logLoadingPlugin")); - if (!(await this.core.$everyOnloadStart())) { + if (!(await this.services.appLifecycle.onInitialise())) { this._log($msg("moduleLiveSyncMain.logPluginInitCancelled"), LOG_LEVEL_NOTICE); - return; + return false; } // this.addUIs(); //@ts-ignore @@ -84,12 +88,12 @@ export class ModuleLiveSyncMain extends AbstractModule implements ICoreModule { const packageVersion: string = PACKAGE_VERSION || "0.0.0"; this._log($msg("moduleLiveSyncMain.logPluginVersion", { manifestVersion, packageVersion })); - await this.core.$$loadSettings(); - if (!(await this.core.$everyOnloadAfterLoadSettings())) { + await this.services.setting.loadSettings(); + if (!(await this.services.appLifecycle.onSettingLoaded())) { this._log($msg("moduleLiveSyncMain.logPluginInitCancelled"), LOG_LEVEL_NOTICE); - return; + return false; } - const lsKey = "obsidian-live-sync-ver" + this.core.$$getVaultName(); + const lsKey = "obsidian-live-sync-ver" + this.services.vault.getVaultName(); const last_version = localStorage.getItem(lsKey); const lastVersion = ~~(versionNumberString2Number(manifestVersion) / 1000); @@ -113,22 +117,23 @@ export class ModuleLiveSyncMain extends AbstractModule implements ICoreModule { await this.saveSettings(); } localStorage.setItem(lsKey, `${VER}`); - await this.core.$$openDatabase(); - this.core.$$realizeSettingSyncMode = this.core.$$realizeSettingSyncMode.bind(this); + await this.services.database.openDatabase(); + // this.core.$$realizeSettingSyncMode = this.core.$$realizeSettingSyncMode.bind(this); // this.$$parseReplicationResult = this.$$parseReplicationResult.bind(this); // this.$$replicate = this.$$replicate.bind(this); - this.core.$$onLiveSyncReady = this.core.$$onLiveSyncReady.bind(this); - await this.core.$everyOnload(); + // this.core.$$onLiveSyncReady = this.core.$$onLiveSyncReady.bind(this); + await this.core.services.appLifecycle.onLoaded(); await Promise.all(this.core.addOns.map((e) => e.onload())); + return true; } - async $$onLiveSyncUnload(): Promise { + async _onLiveSyncUnload(): Promise { eventHub.emitEvent(EVENT_PLUGIN_UNLOADED); - await this.core.$allStartOnUnload(); + await this.services.appLifecycle.onBeforeUnload(); cancelAllPeriodicTask(); cancelAllTasks(); stopAllRunningProcessors(); - await this.core.$allOnUnload(); + await this.services.appLifecycle.onUnload(); this._unloaded = true; for (const addOn of this.core.addOns) { addOn.onunload(); @@ -143,49 +148,68 @@ export class ModuleLiveSyncMain extends AbstractModule implements ICoreModule { eventHub.emitEvent(EVENT_PLATFORM_UNLOADED); eventHub.offAll(); this._log($msg("moduleLiveSyncMain.logUnloadingPlugin")); + return; } - async $$realizeSettingSyncMode(): Promise { - await this.core.$everyBeforeSuspendProcess(); - await this.core.$everyBeforeRealizeSetting(); + private async _realizeSettingSyncMode(): Promise { + await this.services.appLifecycle.onSuspending(); + await this.services.setting.onBeforeRealiseSetting(); this.localDatabase.refreshSettings(); - await this.core.$everyCommitPendingFileEvent(); - await this.core.$everyRealizeSettingSyncMode(); + await this.services.fileProcessing.commitPendingFileEvents(); + await this.services.setting.onRealiseSetting(); // disable all sync temporary. - if (this.core.$$isSuspended()) return; - await this.core.$everyOnResumeProcess(); - await this.core.$everyAfterResumeProcess(); - await this.core.$everyAfterRealizeSetting(); + if (this.services.appLifecycle.isSuspended()) return; + await this.services.appLifecycle.onResuming(); + await this.services.appLifecycle.onResumed(); + await this.services.setting.onSettingRealised(); + return; } - $$isReloadingScheduled(): boolean { + _isReloadingScheduled(): boolean { return this.core._totalProcessingCount !== undefined; } isReady = false; - $$isReady(): boolean { + _isReady(): boolean { return this.isReady; } - $$markIsReady(): void { + _markIsReady(): void { this.isReady = true; } - $$resetIsReady(): void { + _resetIsReady(): void { this.isReady = false; } _suspended = false; - $$isSuspended(): boolean { + _isSuspended(): boolean { return this._suspended || !this.settings?.isConfigured; } - $$setSuspended(value: boolean) { + + _setSuspended(value: boolean) { this._suspended = value; } _unloaded = false; - $$isUnloaded(): boolean { + _isUnloaded(): boolean { return this._unloaded; } + + onBindFunction(core: LiveSyncCore, services: InjectableServiceHub): void { + super.onBindFunction(core, services); + services.appLifecycle.handleIsSuspended(this._isSuspended.bind(this)); + services.appLifecycle.handleSetSuspended(this._setSuspended.bind(this)); + services.appLifecycle.handleIsReady(this._isReady.bind(this)); + services.appLifecycle.handleMarkIsReady(this._markIsReady.bind(this)); + services.appLifecycle.handleResetIsReady(this._resetIsReady.bind(this)); + services.appLifecycle.handleHasUnloaded(this._isUnloaded.bind(this)); + services.appLifecycle.handleIsReloadingScheduled(this._isReloadingScheduled.bind(this)); + services.appLifecycle.handleOnReady(this._onLiveSyncReady.bind(this)); + services.appLifecycle.handleOnWireUpEvents(this._wireUpEvents.bind(this)); + services.appLifecycle.handleOnLoad(this._onLiveSyncLoad.bind(this)); + services.appLifecycle.handleOnAppUnload(this._onLiveSyncUnload.bind(this)); + services.setting.handleRealiseSetting(this._realizeSettingSyncMode.bind(this)); + } } diff --git a/src/modules/services/ObsidianServices.ts b/src/modules/services/ObsidianServices.ts new file mode 100644 index 0000000..3972752 --- /dev/null +++ b/src/modules/services/ObsidianServices.ts @@ -0,0 +1,76 @@ +import { + InjectableAPIService, + InjectableAppLifecycleService, + InjectableConflictService, + InjectableDatabaseService, + InjectableFileProcessingService, + InjectablePathService, + InjectableRemoteService, + InjectableReplicationService, + InjectableReplicatorService, + InjectableSettingService, + InjectableTestService, + InjectableTweakValueService, + InjectableVaultService, +} from "../../lib/src/services/InjectableServices.ts"; +import { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts"; +// All Services will be migrated to be based on Plain Services, not Injectable Services. +// This is a migration step. + +export class ObsidianAPIService extends InjectableAPIService {} +export class ObsidianPathService extends InjectablePathService {} +export class ObsidianDatabaseService extends InjectableDatabaseService {} + +// InjectableReplicatorService +export class ObsidianReplicatorService extends InjectableReplicatorService {} +// InjectableFileProcessingService +export class ObsidianFileProcessingService extends InjectableFileProcessingService {} +// InjectableReplicationService +export class ObsidianReplicationService extends InjectableReplicationService {} +// InjectableRemoteService +export class ObsidianRemoteService extends InjectableRemoteService {} +// InjectableConflictService +export class ObsidianConflictService extends InjectableConflictService {} +// InjectableAppLifecycleService +export class ObsidianAppLifecycleService extends InjectableAppLifecycleService {} +// InjectableSettingService +export class ObsidianSettingService extends InjectableSettingService {} +// InjectableTweakValueService +export class ObsidianTweakValueService extends InjectableTweakValueService {} +// InjectableVaultService +export class ObsidianVaultService extends InjectableVaultService {} +// InjectableTestService +export class ObsidianTestService extends InjectableTestService {} + +// InjectableServiceHub + +export class ObsidianServiceHub extends InjectableServiceHub { + protected _api: ObsidianAPIService = new ObsidianAPIService(this._serviceBackend, this._throughHole); + protected _path: ObsidianPathService = new ObsidianPathService(this._serviceBackend, this._throughHole); + protected _database: ObsidianDatabaseService = new ObsidianDatabaseService(this._serviceBackend, this._throughHole); + protected _replicator: ObsidianReplicatorService = new ObsidianReplicatorService( + this._serviceBackend, + this._throughHole + ); + protected _fileProcessing: ObsidianFileProcessingService = new ObsidianFileProcessingService( + this._serviceBackend, + this._throughHole + ); + protected _replication: ObsidianReplicationService = new ObsidianReplicationService( + this._serviceBackend, + this._throughHole + ); + protected _remote: ObsidianRemoteService = new ObsidianRemoteService(this._serviceBackend, this._throughHole); + protected _conflict: ObsidianConflictService = new ObsidianConflictService(this._serviceBackend, this._throughHole); + protected _appLifecycle: ObsidianAppLifecycleService = new ObsidianAppLifecycleService( + this._serviceBackend, + this._throughHole + ); + protected _setting: ObsidianSettingService = new ObsidianSettingService(this._serviceBackend, this._throughHole); + protected _tweakValue: ObsidianTweakValueService = new ObsidianTweakValueService( + this._serviceBackend, + this._throughHole + ); + protected _vault: ObsidianVaultService = new ObsidianVaultService(this._serviceBackend, this._throughHole); + protected _test: ObsidianTestService = new ObsidianTestService(this._serviceBackend, this._throughHole); +} diff --git a/terser.config.mjs b/terser.config.mjs index 9014ddf..171fd32 100644 --- a/terser.config.mjs +++ b/terser.config.mjs @@ -31,12 +31,12 @@ const terserOption = { evaluate: true, dead_code: true, // directives: true, - inline: 3, + inline: false, join_vars: true, loops: true, passes: 4, reduce_vars: true, - reduce_funcs: true, + reduce_funcs: false, arrows: true, collapse_vars: true, comparisons: true, From 48088769681570851e4f17ca5885dc633db6b294 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Mon, 6 Oct 2025 10:40:25 +0100 Subject: [PATCH 2/8] - Rename methods for automatic binging checking. - Add automatic binging checks. --- src/features/ConfigSync/CmdConfigSync.ts | 26 +-- .../HiddenFileSync/CmdHiddenFileSync.ts | 30 +-- src/features/LiveSyncCommands.ts | 2 + src/features/P2PSync/CmdP2PReplicator.ts | 173 +----------------- src/lib | 2 +- src/modules/AbstractModule.ts | 2 + src/modules/AbstractObsidianModule.ts | 8 +- src/modules/core/ModuleDatabaseFileAccess.ts | 8 +- src/modules/core/ModulePeriodicProcess.ts | 22 +-- .../coreObsidian/ModuleFileAccessObsidian.ts | 6 +- .../essentialObsidian/ModuleObsidianAPI.ts | 6 +- .../essentialObsidian/ModuleObsidianEvents.ts | 8 +- .../essentialObsidian/ModuleObsidianMenu.ts | 4 +- src/modules/extras/ModuleIntegratedTest.ts | 18 +- src/modules/extras/ModuleReplicateTest.ts | 12 +- src/modules/features/ModuleLog.ts | 4 +- 16 files changed, 83 insertions(+), 248 deletions(-) diff --git a/src/features/ConfigSync/CmdConfigSync.ts b/src/features/ConfigSync/CmdConfigSync.ts index 5036743..2fdb005 100644 --- a/src/features/ConfigSync/CmdConfigSync.ts +++ b/src/features/ConfigSync/CmdConfigSync.ts @@ -411,7 +411,7 @@ export class ConfigSync extends LiveSyncCommands { get useSyncPluginEtc() { return this.plugin.settings.usePluginEtc; } - _isThisModuleEnabled() { + isThisModuleEnabled() { return this.plugin.settings.usePluginSync; } @@ -420,7 +420,7 @@ export class ConfigSync extends LiveSyncCommands { pluginList: IPluginDataExDisplay[] = []; showPluginSyncModal() { - if (!this._isThisModuleEnabled()) { + if (!this.isThisModuleEnabled()) { return; } if (this.pluginDialog) { @@ -492,7 +492,7 @@ export class ConfigSync extends LiveSyncCommands { return this.getFileCategory(filePath) != ""; } private async _everyOnDatabaseInitialized(showNotice: boolean) { - if (!this._isThisModuleEnabled()) return true; + if (!this.isThisModuleEnabled()) return true; try { this._log("Scanning customizations..."); await this.scanAllConfigFiles(showNotice); @@ -504,7 +504,7 @@ export class ConfigSync extends LiveSyncCommands { return true; } async _everyBeforeReplicate(showNotice: boolean) { - if (!this._isThisModuleEnabled()) return true; + if (!this.isThisModuleEnabled()) return true; if (this.settings.autoSweepPlugins) { await this.scanAllConfigFiles(showNotice); return true; @@ -512,7 +512,7 @@ export class ConfigSync extends LiveSyncCommands { return true; } async _everyOnResumeProcess(): Promise { - if (!this._isThisModuleEnabled()) return true; + if (!this.isThisModuleEnabled()) return true; if (this._isMainSuspended()) { return true; } @@ -528,7 +528,7 @@ export class ConfigSync extends LiveSyncCommands { } _everyAfterResumeProcess(): Promise { const q = activeDocument.querySelector(`.livesync-ribbon-showcustom`); - q?.toggleClass("sls-hidden", !this._isThisModuleEnabled()); + q?.toggleClass("sls-hidden", !this.isThisModuleEnabled()); return Promise.resolve(true); } async reloadPluginList(showMessage: boolean) { @@ -870,7 +870,7 @@ export class ConfigSync extends LiveSyncCommands { } async updatePluginList(showMessage: boolean, updatedDocumentPath?: FilePathWithPrefix): Promise { - if (!this._isThisModuleEnabled()) { + if (!this.isThisModuleEnabled()) { this.pluginScanProcessor.clearQueue(); this.pluginList = []; pluginList.set(this.pluginList); @@ -1168,13 +1168,13 @@ export class ConfigSync extends LiveSyncCommands { } async _anyModuleParsedReplicationResultItem(docs: PouchDB.Core.ExistingDocument) { if (!docs._id.startsWith(ICXHeader)) return false; - if (this._isThisModuleEnabled()) { + if (this.isThisModuleEnabled()) { await this.updatePluginList( false, (docs as AnyEntry).path ? (docs as AnyEntry).path : this.getPath(docs as AnyEntry) ); } - if (this._isThisModuleEnabled() && this.plugin.settings.notifyPluginOrSettingUpdated) { + if (this.isThisModuleEnabled() && this.plugin.settings.notifyPluginOrSettingUpdated) { if (!this.pluginDialog || (this.pluginDialog && !this.pluginDialog.isOpened())) { const fragment = createFragment((doc) => { doc.createEl("span", undefined, (a) => { @@ -1218,7 +1218,7 @@ export class ConfigSync extends LiveSyncCommands { this.periodicPluginSweepProcessor?.disable(); if (!this._isMainReady) return true; if (!this._isMainSuspended()) return true; - if (!this._isThisModuleEnabled()) return true; + if (!this.isThisModuleEnabled()) return true; if (this.settings.autoSweepPlugins) { await this.scanAllConfigFiles(false); } @@ -1504,7 +1504,7 @@ export class ConfigSync extends LiveSyncCommands { async watchVaultRawEventsAsync(path: FilePath) { if (!this._isMainReady) return false; if (this._isMainSuspended()) return false; - if (!this._isThisModuleEnabled()) return false; + if (!this.isThisModuleEnabled()) return false; // if (!this.isTargetPath(path)) return false; const stat = await this.plugin.storageAccess.statHidden(path); // Make sure that target is a file. @@ -1686,10 +1686,10 @@ export class ConfigSync extends LiveSyncCommands { enableFetch?: boolean; enableOverwrite?: boolean; }): Promise { - await this._askHiddenFileConfiguration(opt); + await this.__askHiddenFileConfiguration(opt); return true; } - async _askHiddenFileConfiguration(opt: { enableFetch?: boolean; enableOverwrite?: boolean }) { + private async __askHiddenFileConfiguration(opt: { enableFetch?: boolean; enableOverwrite?: boolean }) { const message = `Would you like to enable **Customization sync**? > [!DETAILS]- diff --git a/src/features/HiddenFileSync/CmdHiddenFileSync.ts b/src/features/HiddenFileSync/CmdHiddenFileSync.ts index a7f2143..e27e562 100644 --- a/src/features/HiddenFileSync/CmdHiddenFileSync.ts +++ b/src/features/HiddenFileSync/CmdHiddenFileSync.ts @@ -81,13 +81,13 @@ function getComparingMTime( } export class HiddenFileSync extends LiveSyncCommands { - _isThisModuleEnabled() { + isThisModuleEnabled() { return this.plugin.settings.syncInternalFiles; } periodicInternalFileScanProcessor: PeriodicProcessor = new PeriodicProcessor( this.plugin, - async () => this._isThisModuleEnabled() && this._isDatabaseReady() && (await this.scanAllStorageChanges(false)) + async () => this.isThisModuleEnabled() && this._isDatabaseReady() && (await this.scanAllStorageChanges(false)) ); get kvDB() { @@ -151,7 +151,7 @@ export class HiddenFileSync extends LiveSyncCommands { this._fileInfoLastProcessed = await autosaveCache(this.kvDB, "hidden-file-lastProcessed"); this._databaseInfoLastProcessed = await autosaveCache(this.kvDB, "hidden-file-lastProcessed-database"); this._fileInfoLastKnown = await autosaveCache(this.kvDB, "hidden-file-lastKnown"); - if (this._isThisModuleEnabled()) { + if (this.isThisModuleEnabled()) { if (this._fileInfoLastProcessed.size == 0 && this._fileInfoLastProcessed.size == 0) { this._log(`No cache found. Performing startup scan.`, LOG_LEVEL_VERBOSE); await this.performStartupScan(true); @@ -163,7 +163,7 @@ export class HiddenFileSync extends LiveSyncCommands { } async _everyBeforeReplicate(showNotice: boolean) { if ( - this._isThisModuleEnabled() && + this.isThisModuleEnabled() && this._isDatabaseReady() && this.settings.syncInternalFilesBeforeReplication && !this.settings.watchInternalFileChanges @@ -200,7 +200,7 @@ export class HiddenFileSync extends LiveSyncCommands { isReady() { if (!this._isMainReady) return false; if (this._isMainSuspended()) return false; - if (!this._isThisModuleEnabled()) return false; + if (!this.isThisModuleEnabled()) return false; return true; } shouldSkipFile = [] as FilePathWithPrefixLC[]; @@ -212,11 +212,11 @@ export class HiddenFileSync extends LiveSyncCommands { async _everyOnResumeProcess(): Promise { this.periodicInternalFileScanProcessor?.disable(); if (this._isMainSuspended()) return true; - if (this._isThisModuleEnabled()) { + if (this.isThisModuleEnabled()) { await this.performStartupScan(false); } this.periodicInternalFileScanProcessor.enable( - this._isThisModuleEnabled() && this.settings.syncInternalFilesInterval + this.isThisModuleEnabled() && this.settings.syncInternalFilesInterval ? this.settings.syncInternalFilesInterval * 1000 : 0 ); @@ -228,7 +228,7 @@ export class HiddenFileSync extends LiveSyncCommands { if (this._isMainSuspended()) return Promise.resolve(true); if (!this.services.appLifecycle.isReady()) return Promise.resolve(true); this.periodicInternalFileScanProcessor.enable( - this._isThisModuleEnabled() && this.settings.syncInternalFilesInterval + this.isThisModuleEnabled() && this.settings.syncInternalFilesInterval ? this.settings.syncInternalFilesInterval * 1000 : 0 ); @@ -256,7 +256,7 @@ export class HiddenFileSync extends LiveSyncCommands { async _anyProcessOptionalSyncFiles(doc: LoadedEntry): Promise { if (isInternalMetadata(doc._id)) { - if (this._isThisModuleEnabled()) { + if (this.isThisModuleEnabled()) { //system file const filename = getPath(doc); if (await this.services.vault.isTargetFile(filename)) { @@ -1492,7 +1492,7 @@ Offline Changed files: ${files.length}`; } const deleted = metaOnDB.deleted || metaOnDB._deleted || false; if (deleted) { - const result = await this._deleteFile(storageFilePath); + const result = await this.__deleteFile(storageFilePath); if (result == "OK") { this.updateLastProcessedDeletion(storageFilePath, metaOnDB); return true; @@ -1506,7 +1506,7 @@ Offline Changed files: ${files.length}`; if (fileOnDB === false) { throw new Error(`Failed to read file from database:${storageFilePath}`); } - const resultStat = await this._writeFile(storageFilePath, fileOnDB, force); + const resultStat = await this.__writeFile(storageFilePath, fileOnDB, force); if (resultStat) { this.updateLastProcessed(storageFilePath, metaOnDB, resultStat); this.queueNotification(storageFilePath); @@ -1539,7 +1539,7 @@ Offline Changed files: ${files.length}`; } } - async _writeFile(storageFilePath: FilePath, fileOnDB: LoadedEntry, force: boolean): Promise { + async __writeFile(storageFilePath: FilePath, fileOnDB: LoadedEntry, force: boolean): Promise { try { const statBefore = await this.plugin.storageAccess.statHidden(storageFilePath); const isExist = statBefore != null; @@ -1578,7 +1578,7 @@ Offline Changed files: ${files.length}`; } } - async _deleteFile(storageFilePath: FilePath): Promise { + async __deleteFile(storageFilePath: FilePath): Promise { const result = await this.__removeFile(storageFilePath); if (result === false) { this._log(`STORAGE - Fetch: Use the files stored from other devices. Choose this option if you have already configured hidden file synchronization on those devices and wish to accept their files.\n` : ""}`; const messageOverwrite = `${opt.enableOverwrite ? `> - Overwrite: Use the files from this device. Select this option if you want to overwrite the files stored on other devices.\n` : ""}`; const messageMerge = `> - Merge: Merge the files from this device with those on other devices. Choose this option if you wish to combine files from multiple sources. diff --git a/src/features/LiveSyncCommands.ts b/src/features/LiveSyncCommands.ts index ea1088f..9732d94 100644 --- a/src/features/LiveSyncCommands.ts +++ b/src/features/LiveSyncCommands.ts @@ -12,6 +12,7 @@ import { import type ObsidianLiveSyncPlugin from "../main.ts"; import { MARK_DONE } from "../modules/features/ModuleLog.ts"; import type { LiveSyncCore } from "../main.ts"; +import { __$checkInstanceBinding } from "../lib/src/dev/checks.ts"; let noticeIndex = 0; export abstract class LiveSyncCommands { @@ -42,6 +43,7 @@ export abstract class LiveSyncCommands { constructor(plugin: ObsidianLiveSyncPlugin) { this.plugin = plugin; this.onBindFunction(plugin, plugin.services); + __$checkInstanceBinding(this); } abstract onunload(): void; abstract onload(): void | Promise; diff --git a/src/features/P2PSync/CmdP2PReplicator.ts b/src/features/P2PSync/CmdP2PReplicator.ts index cc99f7e..16c6298 100644 --- a/src/features/P2PSync/CmdP2PReplicator.ts +++ b/src/features/P2PSync/CmdP2PReplicator.ts @@ -33,176 +33,6 @@ import { getPlatformName } from "../../lib/src/PlatformAPIs/obsidian/Environment import type { LiveSyncCore } from "../../main.ts"; import { TrysteroReplicator } from "../../lib/src/replication/trystero/TrysteroReplicator.ts"; -// class P2PReplicatorCommandBase extends LiveSyncCommands implements P2PReplicatorBase { -// storeP2PStatusLine = reactiveSource(""); - -// getSettings(): P2PSyncSetting { -// return this.plugin.settings; -// } -// get settings() { -// return this.plugin.settings; -// } -// getDB() { -// return this.plugin.localDatabase.localDatabase; -// } - -// get confirm(): Confirm { -// return this.plugin.confirm; -// } -// _simpleStore!: SimpleStore; - -// simpleStore(): SimpleStore { -// return this._simpleStore; -// } - -// constructor(plugin: ObsidianLiveSyncPlugin) { -// super(plugin); -// this.onBindFunction(plugin, plugin.services); -// } - -// async handleReplicatedDocuments(docs: EntryDoc[]): Promise { -// // console.log("Processing Replicated Docs", docs); -// return await this.services.replication.parseSynchroniseResult( -// docs as PouchDB.Core.ExistingDocument[] -// ); -// } -// onunload(): void { -// throw new Error("Method not implemented."); -// } -// onload(): void | Promise { -// throw new Error("Method not implemented."); -// } - -// init() { -// this._simpleStore = this.services.database.openSimpleStore("p2p-sync"); -// return Promise.resolve(this); -// } - -// } - -// export class P2PReplicator extends P2PReplicatorMixIn(P2PReplicatorCommandBase) implements CommandShim { -// storeP2PStatusLine = reactiveSource(""); -// _anyNewReplicator(settingOverride: Partial = {}): Promise { -// const settings = { ...this.settings, ...settingOverride }; -// if (settings.remoteType == REMOTE_P2P) { -// return Promise.resolve(new LiveSyncTrysteroReplicator(this.plugin)); -// } -// return undefined!; -// } -// override getPlatform(): string { -// return getPlatformName(); -// } - -// override onunload(): void { -// removeP2PReplicatorInstance(); -// void this.close(); -// } - -// override onload(): void | Promise { -// eventHub.onEvent(EVENT_REQUEST_OPEN_P2P, () => { -// void this.openPane(); -// }); -// this.p2pLogCollector.p2pReplicationLine.onChanged((line) => { -// this.storeP2PStatusLine.value = line.value; -// }); -// } -// async _everyOnInitializeDatabase(): Promise { -// await this.initialiseP2PReplicator(); -// return Promise.resolve(true); -// } - -// private async _allSuspendExtraSync() { -// this.plugin.settings.P2P_Enabled = false; -// this.plugin.settings.P2P_AutoAccepting = AutoAccepting.NONE; -// this.plugin.settings.P2P_AutoBroadcast = false; -// this.plugin.settings.P2P_AutoStart = false; -// this.plugin.settings.P2P_AutoSyncPeers = ""; -// this.plugin.settings.P2P_AutoWatchPeers = ""; -// return await Promise.resolve(true); -// } - -// // async $everyOnLoadStart() { -// // return await Promise.resolve(); -// // } - -// async openPane() { -// await this.services.API.showWindow(VIEW_TYPE_P2P); -// } - -// async _everyOnloadStart(): Promise { -// // this.plugin.registerView(VIEW_TYPE_P2P, (leaf) => new P2PReplicatorPaneView(leaf, this.plugin)); -// this.plugin.addCommand({ -// id: "open-p2p-replicator", -// name: "P2P Sync : Open P2P Replicator", -// callback: async () => { -// await this.openPane(); -// }, -// }); -// this.plugin.addCommand({ -// id: "p2p-establish-connection", -// name: "P2P Sync : Connect to the Signalling Server", -// checkCallback: (isChecking) => { -// if (isChecking) { -// return !(this._replicatorInstance?.server?.isServing ?? false); -// } -// void this.open(); -// }, -// }); -// this.plugin.addCommand({ -// id: "p2p-close-connection", -// name: "P2P Sync : Disconnect from the Signalling Server", -// checkCallback: (isChecking) => { -// if (isChecking) { -// return this._replicatorInstance?.server?.isServing ?? false; -// } -// Logger(`Closing P2P Connection`, LOG_LEVEL_NOTICE); -// void this.close(); -// }, -// }); -// this.plugin.addCommand({ -// id: "replicate-now-by-p2p", -// name: "Replicate now by P2P", -// checkCallback: (isChecking) => { -// if (isChecking) { -// if (this.settings.remoteType == REMOTE_P2P) return false; -// if (!this._replicatorInstance?.server?.isServing) return false; -// return true; -// } -// void this._replicatorInstance?.replicateFromCommand(false); -// }, -// }); -// this.plugin -// .addRibbonIcon("waypoints", "P2P Replicator", async () => { -// await this.openPane(); -// }) -// .addClass("livesync-ribbon-replicate-p2p"); - -// return await Promise.resolve(true); -// } -// _everyAfterResumeProcess(): Promise { -// if (this.settings.P2P_Enabled && this.settings.P2P_AutoStart) { -// setTimeout(() => void this.open(), 100); -// } -// const rep = this._replicatorInstance; -// rep?.allowReconnection(); -// return Promise.resolve(true); -// } -// _everyBeforeSuspendProcess(): Promise { -// const rep = this._replicatorInstance; -// rep?.disconnectFromServer(); -// return Promise.resolve(true); -// } - -// override onBindFunction(core: LiveSyncCore, services: typeof core.services): void { -// services.replicator.handleGetNewReplicator(this._anyNewReplicator.bind(this)); -// services.databaseEvents.handleOnDatabaseInitialisation(this._everyOnInitializeDatabase.bind(this)); -// services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this)); -// services.appLifecycle.handleOnSuspending(this._everyBeforeSuspendProcess.bind(this)); -// services.appLifecycle.handleOnResumed(this._everyAfterResumeProcess.bind(this)); -// services.setting.handleSuspendExtraSync(this._allSuspendExtraSync.bind(this)); -// } -// } - export class P2PReplicator extends LiveSyncCommands implements P2PReplicatorBase, CommandShim { storeP2PStatusLine = reactiveSource(""); @@ -230,7 +60,8 @@ export class P2PReplicator extends LiveSyncCommands implements P2PReplicatorBase setReplicatorFunc(() => this._replicatorInstance); addP2PEventHandlers(this); this.afterConstructor(); - this.onBindFunction(plugin, plugin.services); + // onBindFunction is called in super class + // this.onBindFunction(plugin, plugin.services); } async handleReplicatedDocuments(docs: EntryDoc[]): Promise { diff --git a/src/lib b/src/lib index 6f58b4f..6972cf4 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit 6f58b4f07634b99a4359166e2d0ed93198d37302 +Subproject commit 6972cf45b3e60e04d52e1185c3c5451c8ccb8aa5 diff --git a/src/modules/AbstractModule.ts b/src/modules/AbstractModule.ts index 64ad6e4..08bb2d4 100644 --- a/src/modules/AbstractModule.ts +++ b/src/modules/AbstractModule.ts @@ -1,6 +1,7 @@ import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, Logger } from "octagonal-wheels/common/logger"; import type { LOG_LEVEL } from "../lib/src/common/types"; import type { LiveSyncCore } from "../main"; +import { __$checkInstanceBinding } from "../lib/src/dev/checks"; // import { unique } from "octagonal-wheels/collection"; // import type { IObsidianModule } from "./AbstractObsidianModule.ts"; // import type { @@ -159,6 +160,7 @@ export abstract class AbstractModule { constructor(public core: LiveSyncCore) { this.onBindFunction(core, core.services); Logger(`[${this.constructor.name}] Loaded`, LOG_LEVEL_VERBOSE); + __$checkInstanceBinding(this); } saveSettings = this.core.saveSettings.bind(this.core); diff --git a/src/modules/AbstractObsidianModule.ts b/src/modules/AbstractObsidianModule.ts index 23021a9..c25db21 100644 --- a/src/modules/AbstractObsidianModule.ts +++ b/src/modules/AbstractObsidianModule.ts @@ -37,18 +37,18 @@ export abstract class AbstractObsidianModule extends AbstractModule { saveSettings = this.plugin.saveSettings.bind(this.plugin); - _isMainReady() { + isMainReady() { return this.services.appLifecycle.isReady(); } - _isMainSuspended() { + isMainSuspended() { return this.services.appLifecycle.isSuspended(); } - _isDatabaseReady() { + isDatabaseReady() { return this.services.database.isDatabaseReady(); } //should be overridden - _isThisModuleEnabled() { + isThisModuleEnabled() { return true; } } diff --git a/src/modules/core/ModuleDatabaseFileAccess.ts b/src/modules/core/ModuleDatabaseFileAccess.ts index a026487..0344d1b 100644 --- a/src/modules/core/ModuleDatabaseFileAccess.ts +++ b/src/modules/core/ModuleDatabaseFileAccess.ts @@ -102,11 +102,11 @@ export class ModuleDatabaseFileAccess extends AbstractModule implements Database } async createChunks(file: UXFileInfo, force: boolean = false, skipCheck?: boolean): Promise { - return await this._store(file, force, skipCheck, true); + return await this.__store(file, force, skipCheck, true); } async store(file: UXFileInfo, force: boolean = false, skipCheck?: boolean): Promise { - return await this._store(file, force, skipCheck, false); + return await this.__store(file, force, skipCheck, false); } async storeContent(path: FilePathWithPrefix, content: string): Promise { const blob = createTextBlob(content); @@ -124,10 +124,10 @@ export class ModuleDatabaseFileAccess extends AbstractModule implements Database body: blob, isInternal, }; - return await this._store(dummyUXFileInfo, true, false, false); + return await this.__store(dummyUXFileInfo, true, false, false); } - async _store( + private async __store( file: UXFileInfo, force: boolean = false, skipCheck?: boolean, diff --git a/src/modules/core/ModulePeriodicProcess.ts b/src/modules/core/ModulePeriodicProcess.ts index 080ac91..32ccb37 100644 --- a/src/modules/core/ModulePeriodicProcess.ts +++ b/src/modules/core/ModulePeriodicProcess.ts @@ -5,30 +5,30 @@ import { AbstractModule } from "../AbstractModule"; export class ModulePeriodicProcess extends AbstractModule { periodicSyncProcessor = new PeriodicProcessor(this.core, async () => await this.services.replication.replicate()); - _disablePeriodic() { + disablePeriodic() { this.periodicSyncProcessor?.disable(); return Promise.resolve(true); } - _resumePeriodic() { + resumePeriodic() { this.periodicSyncProcessor.enable( this.settings.periodicReplication ? this.settings.periodicReplicationInterval * 1000 : 0 ); return Promise.resolve(true); } private _allOnUnload() { - return this._disablePeriodic(); + return this.disablePeriodic(); } - _everyBeforeRealizeSetting(): Promise { - return this._disablePeriodic(); + private _everyBeforeRealizeSetting(): Promise { + return this.disablePeriodic(); } - _everyBeforeSuspendProcess(): Promise { - return this._disablePeriodic(); + private _everyBeforeSuspendProcess(): Promise { + return this.disablePeriodic(); } - _everyAfterResumeProcess(): Promise { - return this._resumePeriodic(); + private _everyAfterResumeProcess(): Promise { + return this.resumePeriodic(); } - _everyAfterRealizeSetting(): Promise { - return this._resumePeriodic(); + private _everyAfterRealizeSetting(): Promise { + return this.resumePeriodic(); } onBindFunction(core: LiveSyncCore, services: typeof core.services): void { diff --git a/src/modules/coreObsidian/ModuleFileAccessObsidian.ts b/src/modules/coreObsidian/ModuleFileAccessObsidian.ts index 1229060..b884c09 100644 --- a/src/modules/coreObsidian/ModuleFileAccessObsidian.ts +++ b/src/modules/coreObsidian/ModuleFileAccessObsidian.ts @@ -343,7 +343,7 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements // } // } - async _deleteVaultItem(file: TFile | TFolder) { + async __deleteVaultItem(file: TFile | TFolder) { if (file instanceof TFile) { if (!(await this.services.vault.isTargetFile(file.path))) return; } @@ -361,7 +361,7 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements this._log( `All files under the parent directory (${dir.path}) have been deleted, so delete this one.` ); - await this._deleteVaultItem(dir); + await this.__deleteVaultItem(dir); } } } @@ -372,7 +372,7 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements const file = this.vaultAccess.getAbstractFileByPath(path); if (file === null) return; if (file instanceof TFile || file instanceof TFolder) { - return await this._deleteVaultItem(file); + return await this.__deleteVaultItem(file); } } diff --git a/src/modules/essentialObsidian/ModuleObsidianAPI.ts b/src/modules/essentialObsidian/ModuleObsidianAPI.ts index 61cfc79..ac015b8 100644 --- a/src/modules/essentialObsidian/ModuleObsidianAPI.ts +++ b/src/modules/essentialObsidian/ModuleObsidianAPI.ts @@ -34,7 +34,7 @@ export class ModuleObsidianAPI extends AbstractObsidianModule { return !this.last_successful_post; } - async _fetchByAPI(url: string, authHeader: string, opts?: RequestInit): Promise { + async __fetchByAPI(url: string, authHeader: string, opts?: RequestInit): Promise { const body = opts?.body as string; const transformedHeaders = { ...(opts?.headers as Record) }; @@ -69,7 +69,7 @@ export class ModuleObsidianAPI extends AbstractObsidianModule { const body = opts?.body as string; const size = body ? ` (${body.length})` : ""; try { - const r = await this._fetchByAPI(url, authHeader, opts); + const r = await this.__fetchByAPI(url, authHeader, opts); this.plugin.requestCount.value = this.plugin.requestCount.value + 1; if (method == "POST" || method == "PUT") { this.last_successful_post = r.status - (r.status % 100) == 200; @@ -149,7 +149,7 @@ export class ModuleObsidianAPI extends AbstractObsidianModule { try { this.plugin.requestCount.value = this.plugin.requestCount.value + 1; const response: Response = await (useRequestAPI - ? this._fetchByAPI(url.toString(), authHeader, { ...opts, headers }) + ? this.__fetchByAPI(url.toString(), authHeader, { ...opts, headers }) : fetch(url, { ...opts, headers })); if (method == "POST" || method == "PUT") { this.last_successful_post = response.ok; diff --git a/src/modules/essentialObsidian/ModuleObsidianEvents.ts b/src/modules/essentialObsidian/ModuleObsidianEvents.ts index d64a95e..4682eb1 100644 --- a/src/modules/essentialObsidian/ModuleObsidianEvents.ts +++ b/src/modules/essentialObsidian/ModuleObsidianEvents.ts @@ -32,10 +32,10 @@ export class ModuleObsidianEvents extends AbstractObsidianModule { } private _performRestart(): void { - this._performAppReload(); + this.__performAppReload(); } - _performAppReload() { + __performAppReload() { //@ts-ignore this.app.commands.executeCommandById("app:reload"); } @@ -182,7 +182,7 @@ export class ModuleObsidianEvents extends AbstractObsidianModule { { defaultAction: RETRY_LATER } ); if (ret == RESTART_NOW) { - this._performAppReload(); + this.__performAppReload(); } else if (ret == RESTART_AFTER_STABLE) { this.services.appLifecycle.scheduleRestart(); } @@ -225,7 +225,7 @@ export class ModuleObsidianEvents extends AbstractObsidianModule { this.core._totalProcessingCount.onChanged((e) => { if (e.value == 0) { if (stableCheck-- <= 0) { - this._performAppReload(); + this.__performAppReload(); } this._log( `Obsidian will be restarted soon! (Within ${stableCheck} seconds)`, diff --git a/src/modules/essentialObsidian/ModuleObsidianMenu.ts b/src/modules/essentialObsidian/ModuleObsidianMenu.ts index ff89efb..79dd990 100644 --- a/src/modules/essentialObsidian/ModuleObsidianMenu.ts +++ b/src/modules/essentialObsidian/ModuleObsidianMenu.ts @@ -105,11 +105,11 @@ export class ModuleObsidianMenu extends AbstractObsidianModule { }); return Promise.resolve(true); } - _onWorkspaceReady() { + private __onWorkspaceReady() { void this.services.appLifecycle.onReady(); } private _everyOnload(): Promise { - this.app.workspace.onLayoutReady(this._onWorkspaceReady.bind(this)); + this.app.workspace.onLayoutReady(this.__onWorkspaceReady.bind(this)); return Promise.resolve(true); } diff --git a/src/modules/extras/ModuleIntegratedTest.ts b/src/modules/extras/ModuleIntegratedTest.ts index 6099114..fa2934f 100644 --- a/src/modules/extras/ModuleIntegratedTest.ts +++ b/src/modules/extras/ModuleIntegratedTest.ts @@ -45,7 +45,7 @@ export class ModuleIntegratedTest extends AbstractObsidianModule { } return true; } - async _orDie(key: string, proc: () => Promise): Promise | never { + async __orDie(key: string, proc: () => Promise): Promise | never { if (!(await this._test(key, proc))) { throw new Error(`${key}`); } @@ -64,13 +64,13 @@ export class ModuleIntegratedTest extends AbstractObsidianModule { } return await this.core.storageAccess.readHiddenFileText(file); } - async _proceed(no: number, title: string): Promise { + async __proceed(no: number, title: string): Promise { const stepFile = "_STEP.md" as FilePathWithPrefix; const stepAckFile = "_STEP_ACK.md" as FilePathWithPrefix; const stepContent = `Step ${no}`; await this.services.conflict.resolveByNewest(stepFile); await this.core.storageAccess.writeFileAuto(stepFile, stepContent); - await this._orDie(`Wait for acknowledge ${no}`, async () => { + await this.__orDie(`Wait for acknowledge ${no}`, async () => { if ( !(await this.waitWithReplicating(async () => { return await this.storageContentIsEqual(stepAckFile, stepContent); @@ -81,13 +81,13 @@ export class ModuleIntegratedTest extends AbstractObsidianModule { }); return true; } - async _join(no: number, title: string): Promise { + async __join(no: number, title: string): Promise { const stepFile = "_STEP.md" as FilePathWithPrefix; const stepAckFile = "_STEP_ACK.md" as FilePathWithPrefix; // const otherStepFile = `_STEP_${isLeader ? "R" : "L"}.md` as FilePathWithPrefix; const stepContent = `Step ${no}`; - await this._orDie(`Wait for step ${no} (${title})`, async () => { + await this.__orDie(`Wait for step ${no} (${title})`, async () => { if ( !(await this.waitWithReplicating(async () => { return await this.storageContentIsEqual(stepFile, stepContent); @@ -116,16 +116,16 @@ export class ModuleIntegratedTest extends AbstractObsidianModule { check: () => Promise; }): Promise { if (isGameChanger) { - await this._proceed(step, title); + await this.__proceed(step, title); try { await proc(); } catch (e) { this._log(`Error: ${e}`); return false; } - return await this._orDie(`Step ${step} - ${title}`, async () => await this.waitWithReplicating(check)); + return await this.__orDie(`Step ${step} - ${title}`, async () => await this.waitWithReplicating(check)); } else { - return await this._join(step, title); + return await this.__join(step, title); } } // // see scenario.md @@ -151,7 +151,7 @@ export class ModuleIntegratedTest extends AbstractObsidianModule { `Test as ${isLeader ? "Leader" : "Receiver"} command file ${testCommandFile}` ); if (isLeader) { - await this._proceed(0, "start"); + await this.__proceed(0, "start"); } await this.tryReplicate(); diff --git a/src/modules/extras/ModuleReplicateTest.ts b/src/modules/extras/ModuleReplicateTest.ts index 3288c79..cb71def 100644 --- a/src/modules/extras/ModuleReplicateTest.ts +++ b/src/modules/extras/ModuleReplicateTest.ts @@ -56,10 +56,10 @@ export class ModuleReplicateTest extends AbstractObsidianModule { async dumpList() { if (this.settings.syncInternalFiles) { this._log("Write file list (Include Hidden)"); - await this._dumpFileListIncludeHidden("files.md"); + await this.__dumpFileListIncludeHidden("files.md"); } else { this._log("Write file list"); - await this._dumpFileList("files.md"); + await this.__dumpFileList("files.md"); } } async _everyBeforeReplicate(showMessage: boolean): Promise { @@ -73,7 +73,7 @@ export class ModuleReplicateTest extends AbstractObsidianModule { id: "dump-file-structure-normal", name: `Dump Structure (Normal)`, callback: () => { - void this._dumpFileList("files.md").finally(() => { + void this.__dumpFileList("files.md").finally(() => { void this.refreshSyncStatus(); }); }, @@ -83,7 +83,7 @@ export class ModuleReplicateTest extends AbstractObsidianModule { name: "Dump Structure (Include Hidden)", callback: () => { const d = "files.md"; - void this._dumpFileListIncludeHidden(d); + void this.__dumpFileListIncludeHidden(d); }, }); this.addCommand({ @@ -164,7 +164,7 @@ export class ModuleReplicateTest extends AbstractObsidianModule { } } - async _dumpFileList(outFile?: string) { + async __dumpFileList(outFile?: string) { if (!this.core || !this.core.storageAccess) { this._log("No storage access", LOG_LEVEL_INFO); return; @@ -204,7 +204,7 @@ export class ModuleReplicateTest extends AbstractObsidianModule { this._log(`Dumped ${out.length} files`, LOG_LEVEL_INFO); } - async _dumpFileListIncludeHidden(outFile?: string) { + async __dumpFileListIncludeHidden(outFile?: string) { const ignorePatterns = getFileRegExp(this.plugin.settings, "syncInternalFilesIgnorePatterns"); const targetPatterns = getFileRegExp(this.plugin.settings, "syncInternalFilesTargetPatterns"); const out = [] as any[]; diff --git a/src/modules/features/ModuleLog.ts b/src/modules/features/ModuleLog.ts index 35ecb3a..48928e4 100644 --- a/src/modules/features/ModuleLog.ts +++ b/src/modules/features/ModuleLog.ts @@ -320,7 +320,7 @@ export class ModuleLog extends AbstractObsidianModule { private _everyOnloadAfterLoadSettings(): Promise { logStore .pipeTo( - new QueueProcessor((logs) => logs.forEach((e) => this._addLog(e.message, e.level, e.key)), { + new QueueProcessor((logs) => logs.forEach((e) => this.__addLog(e.message, e.level, e.key)), { suspended: false, batchSize: 20, concurrentLimit: 1, @@ -367,7 +367,7 @@ export class ModuleLog extends AbstractObsidianModule { }) ); } - _addLog(message: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key = ""): void { + __addLog(message: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key = ""): void { if (level == LOG_LEVEL_DEBUG && !showDebugLog) { return; } From 67c9b4cf06ceffa165d189720425b60e258c5476 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Mon, 6 Oct 2025 10:45:59 +0100 Subject: [PATCH 3/8] bump for beta --- manifest-beta.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/manifest-beta.json b/manifest-beta.json index 3b64801..a1e7c06 100644 --- a/manifest-beta.json +++ b/manifest-beta.json @@ -1,7 +1,7 @@ { "id": "obsidian-livesync", "name": "Self-hosted LiveSync", - "version": "0.25.2", + "version": "0.25.21.beta1", "minAppVersion": "0.9.12", "description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "author": "vorotamoroz", diff --git a/package-lock.json b/package-lock.json index 04253d9..882674d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "obsidian-livesync", - "version": "0.25.20", + "version": "0.25.21b1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "obsidian-livesync", - "version": "0.25.20", + "version": "0.25.21b1", "license": "MIT", "dependencies": { "@aws-sdk/client-s3": "^3.808.0", diff --git a/package.json b/package.json index cfcc4ce..f16d5d5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-livesync", - "version": "0.25.20", + "version": "0.25.21.beta1", "description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "main": "main.js", "type": "module", From 7c4f2bf78a7c478330b916d80822c134acf01f72 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Wed, 8 Oct 2025 05:00:42 +0100 Subject: [PATCH 4/8] ### Fixed - Fixed wrong event type bindings (which caused some events not to be handled correctly). - Fixed detected a timing issue in StorageEventManager - When multiple events for the same file are fired in quick succession, metadata has been kept older information. This induces unexpected wrong notifications and write prevention. --- src/lib | 2 +- src/modules/core/ModuleFileHandler.ts | 6 +++++- src/modules/coreObsidian/ModuleFileAccessObsidian.ts | 2 ++ src/modules/coreObsidian/storageLib/StorageEventManager.ts | 3 ++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/lib b/src/lib index 6972cf4..6c2a72d 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit 6972cf45b3e60e04d52e1185c3c5451c8ccb8aa5 +Subproject commit 6c2a72d9cc4d467cf6f3dab75f04e54782db0373 diff --git a/src/modules/core/ModuleFileHandler.ts b/src/modules/core/ModuleFileHandler.ts index ca48540..909b670 100644 --- a/src/modules/core/ModuleFileHandler.ts +++ b/src/modules/core/ModuleFileHandler.ts @@ -94,10 +94,14 @@ export class ModuleFileHandler extends AbstractModule { let readFile: UXFileInfo | undefined = undefined; if (!shouldApplied) { readFile = await this.readFileFromStub(file); + if (!readFile) { + this._log(`File ${file.path} is not exist on the storage`, LOG_LEVEL_NOTICE); + return false; + } if (await isDocContentSame(getDocDataAsArray(entry.data), readFile.body)) { // Timestamp is different but the content is same. therefore, two timestamps should be handled as same. // So, mark the changes are same. - markChangesAreSame(file, file.stat.mtime, entry.mtime); + markChangesAreSame(readFile, readFile.stat.mtime, entry.mtime); } else { shouldApplied = true; } diff --git a/src/modules/coreObsidian/ModuleFileAccessObsidian.ts b/src/modules/coreObsidian/ModuleFileAccessObsidian.ts index b884c09..75f1469 100644 --- a/src/modules/coreObsidian/ModuleFileAccessObsidian.ts +++ b/src/modules/coreObsidian/ModuleFileAccessObsidian.ts @@ -222,6 +222,7 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements return null; } } + async readStubContent(stub: UXFileInfoStub): Promise { const file = this.vaultAccess.getAbstractFileByPath(stub.path); if (!(file instanceof TFile)) { @@ -231,6 +232,7 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements const data = await this.vaultAccess.vaultReadAuto(file); return { ...stub, + ...TFileToUXFileInfoStub(file), body: createBlob(data), }; } diff --git a/src/modules/coreObsidian/storageLib/StorageEventManager.ts b/src/modules/coreObsidian/storageLib/StorageEventManager.ts index ab977a7..5f7f327 100644 --- a/src/modules/coreObsidian/storageLib/StorageEventManager.ts +++ b/src/modules/coreObsidian/storageLib/StorageEventManager.ts @@ -295,7 +295,7 @@ export class StorageEventManagerObsidian extends StorageEventManager { concurrentProcessing = Semaphore(5); waitedSince = new Map(); async startStandingBy(filename: FilePath) { - // If waited, cancel previous waiting. + // If waited, no need to start again (looping inside the function) await skipIfDuplicated(`storage-event-manager-${filename}`, async () => { Logger(`Processing ${filename}: Starting`, LOG_LEVEL_DEBUG); const release = await this.concurrentProcessing.acquire(); @@ -315,6 +315,7 @@ export class StorageEventManagerObsidian extends StorageEventManager { // continue; // } const type = target.type; + // If already cancelled by other operation, skip this. if (target.cancelled) { Logger(`Processing ${filename}: Cancelled (scheduled): ${operationType}`, LOG_LEVEL_DEBUG); this.cancelStandingBy(target); From 51dc44bfb06d8b6a8d9cb7856c4b68ad77a921b8 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Wed, 8 Oct 2025 05:01:07 +0100 Subject: [PATCH 5/8] bump 0.25.21.beta2 --- manifest-beta.json | 2 +- package-lock.json | 18 +++++++++--------- package.json | 4 ++-- updates.md | 19 +++++++++++++++++++ 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/manifest-beta.json b/manifest-beta.json index a1e7c06..7885ae5 100644 --- a/manifest-beta.json +++ b/manifest-beta.json @@ -1,7 +1,7 @@ { "id": "obsidian-livesync", "name": "Self-hosted LiveSync", - "version": "0.25.21.beta1", + "version": "0.25.21.beta2", "minAppVersion": "0.9.12", "description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "author": "vorotamoroz", diff --git a/package-lock.json b/package-lock.json index 882674d..6b50947 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "obsidian-livesync", - "version": "0.25.21b1", + "version": "0.25.21.beta2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "obsidian-livesync", - "version": "0.25.21b1", + "version": "0.25.21.beta2", "license": "MIT", "dependencies": { "@aws-sdk/client-s3": "^3.808.0", @@ -19,7 +19,7 @@ "fflate": "^0.8.2", "idb": "^8.0.3", "minimatch": "^10.0.2", - "octagonal-wheels": "^0.1.40", + "octagonal-wheels": "^0.1.41", "qrcode-generator": "^1.4.4", "trystero": "github:vrtmrz/trystero#9e892a93ec14eeb57ce806d272fbb7c3935256d8", "xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2" @@ -8605,9 +8605,9 @@ } }, "node_modules/octagonal-wheels": { - "version": "0.1.40", - "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.40.tgz", - "integrity": "sha512-qZkPnuVGCqpfLfu8xtZIxfQRVvmE5BmdzMF/rySriGi5JoctGhMNDjF0aLU/4GWUD5yW1X3io6VhJW4a7k1ieA==", + "version": "0.1.41", + "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.41.tgz", + "integrity": "sha512-cIvdXsyiSCxknyxTwGrNnDKsaYpgZdXeKAy9cXIAk2Jy7T1z6bLjU4s5z47OySNPVPSr32x5r8hSz7hAYYv7qA==", "license": "MIT", "dependencies": { "idb": "^8.0.3" @@ -17148,9 +17148,9 @@ } }, "octagonal-wheels": { - "version": "0.1.40", - "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.40.tgz", - "integrity": "sha512-qZkPnuVGCqpfLfu8xtZIxfQRVvmE5BmdzMF/rySriGi5JoctGhMNDjF0aLU/4GWUD5yW1X3io6VhJW4a7k1ieA==", + "version": "0.1.41", + "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.41.tgz", + "integrity": "sha512-cIvdXsyiSCxknyxTwGrNnDKsaYpgZdXeKAy9cXIAk2Jy7T1z6bLjU4s5z47OySNPVPSr32x5r8hSz7hAYYv7qA==", "requires": { "idb": "^8.0.3" } diff --git a/package.json b/package.json index f16d5d5..167e509 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-livesync", - "version": "0.25.21.beta1", + "version": "0.25.21.beta2", "description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "main": "main.js", "type": "module", @@ -93,7 +93,7 @@ "fflate": "^0.8.2", "idb": "^8.0.3", "minimatch": "^10.0.2", - "octagonal-wheels": "^0.1.40", + "octagonal-wheels": "^0.1.41", "qrcode-generator": "^1.4.4", "trystero": "github:vrtmrz/trystero#9e892a93ec14eeb57ce806d272fbb7c3935256d8", "xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2" diff --git a/updates.md b/updates.md index f306777..08450e5 100644 --- a/updates.md +++ b/updates.md @@ -8,6 +8,25 @@ I have now rewritten the E2EE code to be more robust and easier to understand. I As a result, this is the first time in a while that forward compatibility has been broken. We have also taken the opportunity to change all metadata to use encryption rather than obfuscation. Furthermore, the `Dynamic Iteration Count` setting is now redundant and has been moved to the `Patches` pane in the settings. Thanks to Rabin-Karp, the eden setting is also no longer necessary and has been relocated accordingly. Therefore, v0.25.0 represents a legitimate and correct evolution. +## 0.25.21.beta2 + +8th October, 2025 + +### Fixed + +- Fixed wrong event type bindings (which caused some events not to be handled correctly). +- Fixed detected a timing issue in StorageEventManager + - When multiple events for the same file are fired in quick succession, metadata has been kept older information. This induces unexpected wrong notifications and write prevention. + +## 0.25.21.beta1 + +6th October, 2025 + +### Refactored + +- Event handling now does not rely on 'convention over configuration'. + - Services.ts now have a proper event handler registration system. + ## 0.25.20 26th September, 2025 From 6f938d5f54bb19b05c7aace13a54598d3b330c08 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Mon, 13 Oct 2025 09:48:25 +0900 Subject: [PATCH 6/8] Update submodule commit reference --- src/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib b/src/lib index 6c2a72d..422194f 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit 6c2a72d9cc4d467cf6f3dab75f04e54782db0373 +Subproject commit 422194fb1b38e10234c942e5037e859c245ec4a5 From 40508822cf85ecab2f4afdf21aa3fd937bcff2a8 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Mon, 13 Oct 2025 10:13:45 +0900 Subject: [PATCH 7/8] Bump and add readme --- manifest.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- updates.md | 19 +++++++++++++++---- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/manifest.json b/manifest.json index 1b214ce..5cf53c8 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-livesync", "name": "Self-hosted LiveSync", - "version": "0.25.20", + "version": "0.25.21", "minAppVersion": "0.9.12", "description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "author": "vorotamoroz", diff --git a/package-lock.json b/package-lock.json index 6b50947..4ee3c15 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "obsidian-livesync", - "version": "0.25.21.beta2", + "version": "0.25.21", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "obsidian-livesync", - "version": "0.25.21.beta2", + "version": "0.25.21", "license": "MIT", "dependencies": { "@aws-sdk/client-s3": "^3.808.0", diff --git a/package.json b/package.json index 167e509..cf9ab56 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-livesync", - "version": "0.25.21.beta2", + "version": "0.25.21", "description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "main": "main.js", "type": "module", diff --git a/updates.md b/updates.md index 08450e5..a0bd6f4 100644 --- a/updates.md +++ b/updates.md @@ -1,12 +1,23 @@ -## 0.25 +# 0.25 Since 19th July, 2025 (beta1 in 0.25.0-beta1, 13th July, 2025) -After reading Issue #668, I conducted another self-review of the E2EE-related code. In retrospect, it was clearly written by someone inexperienced, which is understandable, but it is still rather embarrassing. Three years is certainly enough time for growth. +The head note of 0.25 is now in [updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md). Because 0.25 got a lot of updates, thankfully, compatibility is kept and we do not need breaking changes! In other words, when get enough stabled. The next version will be v1.0.0. Even though it my hope. -I have now rewritten the E2EE code to be more robust and easier to understand. It is significantly more readable and should be easier to maintain in the future. The performance issue, previously considered a concern, has been addressed by introducing a master key and deriving keys using HKDF. This approach is both fast and robust, and it provides protection against rainbow table attacks. (In addition, this implementation has been [a dedicated package on the npm registry](https://github.com/vrtmrz/octagonal-wheels), and tested in 100% branch-coverage). +## 0.25.21 -As a result, this is the first time in a while that forward compatibility has been broken. We have also taken the opportunity to change all metadata to use encryption rather than obfuscation. Furthermore, the `Dynamic Iteration Count` setting is now redundant and has been moved to the `Patches` pane in the settings. Thanks to Rabin-Karp, the eden setting is also no longer necessary and has been relocated accordingly. Therefore, v0.25.0 represents a legitimate and correct evolution. +13th October, 2025 + +This release including 0.25.21.beta1 and 0.25.21.beta2. + +Apologies for taking a little time. I was seriously tackling this. +(Of course, being caught up in an unfamiliar structure due to personnel changes on my workplace played a part, but fortunately I have returned to a place where I can do research and development rather than production. Completely beside the point, though). +Now then, this time, moving away from 'convention over configuration', I have changed to a mechanism for manually binding events. This makes it much easier to leverage IDE assistance. + +If you have found any issues, please let me know. I am now on the following: +- GitHub [Issues](https://github.com/vrtmrz/obsidian-livesync/issues) Excellent! May the other contributors will help you too. +- Twitter [@vorotamoroz](https://twitter.com/vorotamoroz) Quickest! +- Matrix [@vrtmrz:matrix.org](https://matrix.to/#/@vrtmrz:matrix.org) If you need to keep it private! ## 0.25.21.beta2 From 2b9ded60f75e2e9eca5bd95cd18efae4923657b2 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Mon, 13 Oct 2025 10:38:32 +0900 Subject: [PATCH 8/8] Add notes --- updates.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/updates.md b/updates.md index a0bd6f4..38d0416 100644 --- a/updates.md +++ b/updates.md @@ -13,12 +13,13 @@ This release including 0.25.21.beta1 and 0.25.21.beta2. Apologies for taking a little time. I was seriously tackling this. (Of course, being caught up in an unfamiliar structure due to personnel changes on my workplace played a part, but fortunately I have returned to a place where I can do research and development rather than production. Completely beside the point, though). Now then, this time, moving away from 'convention over configuration', I have changed to a mechanism for manually binding events. This makes it much easier to leverage IDE assistance. +And, also, we are ready to separate `Features` and `APIs` from `Module`. Features are still in the module, but APIs will be moved to a Service layer. This will make it easier to maintain and extend the codebase in the future. If you have found any issues, please let me know. I am now on the following: - GitHub [Issues](https://github.com/vrtmrz/obsidian-livesync/issues) Excellent! May the other contributors will help you too. - Twitter [@vorotamoroz](https://twitter.com/vorotamoroz) Quickest! -- Matrix [@vrtmrz:matrix.org](https://matrix.to/#/@vrtmrz:matrix.org) If you need to keep it private! - +- Matrix [@vrtmrz:matrix.org](https://matrix.to/#/@vrtmrz:matrix.org) Also quick, and if you need to keep it private! + I am creating rooms too, but I'm struggling to figure out how to use them effectively because I cannot tell the difference of use-case between them and discussions. However, if you want to use Discord, this is a answer; We should on E2E encrypted platform. ## 0.25.21.beta2 8th October, 2025