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,