disenchanting and dispelling from the nightmarish implicit something

Indeed, even though if this changeset is mostly another nightmare. It might be in beta for a while.
This commit is contained in:
vorotamoroz
2025-10-03 14:59:14 +01:00
parent ca5a7ae18c
commit 81362816d6
64 changed files with 1852 additions and 1245 deletions

View File

@@ -1,11 +1,6 @@
import { type PluginManifest, TFile } from "../deps.ts"; import { type PluginManifest, TFile } from "../deps.ts";
import { import { type DatabaseEntry, type EntryBody, type FilePath } from "../lib/src/common/types.ts";
type DatabaseEntry, export type { CacheData, FileEventItem } from "../lib/src/common/types.ts";
type EntryBody,
type FilePath,
type UXFileInfoStub,
type UXInternalFileInfoStub,
} from "../lib/src/common/types.ts";
export interface PluginDataEntry extends DatabaseEntry { export interface PluginDataEntry extends DatabaseEntry {
deviceVaultName: string; deviceVaultName: string;
@@ -54,23 +49,6 @@ export type queueItem = {
warned?: boolean; 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`) // Hidden items (Now means `chunk`)
export const CHeader = "h:"; export const CHeader = "h:";

View File

@@ -189,7 +189,7 @@ export class PeriodicProcessor {
() => () =>
fireAndForget(async () => { fireAndForget(async () => {
await this.process(); await this.process();
if (this._plugin.$$isUnloaded()) { if (this._plugin.services?.appLifecycle?.hasUnloaded()) {
this.disable(); this.disable();
} }
}), }),

View File

@@ -68,14 +68,23 @@ import type ObsidianLiveSyncPlugin from "../../main.ts";
import { base64ToArrayBuffer, base64ToString } from "octagonal-wheels/binary/base64"; import { base64ToArrayBuffer, base64ToString } from "octagonal-wheels/binary/base64";
import { ConflictResolveModal } from "../../modules/features/InteractiveConflictResolving/ConflictResolveModal.ts"; import { ConflictResolveModal } from "../../modules/features/InteractiveConflictResolving/ConflictResolveModal.ts";
import { Semaphore } from "octagonal-wheels/concurrency/semaphore"; import { Semaphore } from "octagonal-wheels/concurrency/semaphore";
import type { IObsidianModule } from "../../modules/AbstractObsidianModule.ts";
import { EVENT_REQUEST_OPEN_PLUGIN_SYNC_DIALOG, eventHub } from "../../common/events.ts"; import { EVENT_REQUEST_OPEN_PLUGIN_SYNC_DIALOG, eventHub } from "../../common/events.ts";
import { PluginDialogModal } from "./PluginDialogModal.ts"; import { PluginDialogModal } from "./PluginDialogModal.ts";
import { $msg } from "src/lib/src/common/i18n.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 d = "\u200b";
const d2 = "\n"; const d2 = "\n";
declare global {
interface OPTIONAL_SYNC_FEATURES {
DISABLE: "DISABLE";
CUSTOMIZE: "CUSTOMIZE";
DISABLE_CUSTOM: "DISABLE_CUSTOM";
}
}
function serialize(data: PluginDataEx): string { function serialize(data: PluginDataEx): string {
// For higher performance, create custom plug-in data strings. // 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. // 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; mtime: number;
}; };
export class ConfigSync extends LiveSyncCommands implements IObsidianModule { export class ConfigSync extends LiveSyncCommands {
constructor(plugin: ObsidianLiveSyncPlugin) { constructor(plugin: ObsidianLiveSyncPlugin) {
super(plugin); super(plugin);
pluginScanningCount.onChanged((e) => { pluginScanningCount.onChanged((e) => {
@@ -482,7 +491,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
// Idea non-filter option? // Idea non-filter option?
return this.getFileCategory(filePath) != ""; return this.getFileCategory(filePath) != "";
} }
async $everyOnDatabaseInitialized(showNotice: boolean) { private async _everyOnDatabaseInitialized(showNotice: boolean) {
if (!this._isThisModuleEnabled()) return true; if (!this._isThisModuleEnabled()) return true;
try { try {
this._log("Scanning customizations..."); this._log("Scanning customizations...");
@@ -494,7 +503,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
} }
return true; return true;
} }
async $everyBeforeReplicate(showNotice: boolean) { async _everyBeforeReplicate(showNotice: boolean) {
if (!this._isThisModuleEnabled()) return true; if (!this._isThisModuleEnabled()) return true;
if (this.settings.autoSweepPlugins) { if (this.settings.autoSweepPlugins) {
await this.scanAllConfigFiles(showNotice); await this.scanAllConfigFiles(showNotice);
@@ -502,7 +511,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
} }
return true; return true;
} }
async $everyOnResumeProcess(): Promise<boolean> { async _everyOnResumeProcess(): Promise<boolean> {
if (!this._isThisModuleEnabled()) return true; if (!this._isThisModuleEnabled()) return true;
if (this._isMainSuspended()) { if (this._isMainSuspended()) {
return true; return true;
@@ -517,7 +526,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
); );
return true; return true;
} }
$everyAfterResumeProcess(): Promise<boolean> { _everyAfterResumeProcess(): Promise<boolean> {
const q = activeDocument.querySelector(`.livesync-ribbon-showcustom`); const q = activeDocument.querySelector(`.livesync-ribbon-showcustom`);
q?.toggleClass("sls-hidden", !this._isThisModuleEnabled()); q?.toggleClass("sls-hidden", !this._isThisModuleEnabled());
return Promise.resolve(true); return Promise.resolve(true);
@@ -633,7 +642,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
).startPipeline(); ).startPipeline();
filenameToUnifiedKey(path: string, termOverRide?: string) { filenameToUnifiedKey(path: string, termOverRide?: string) {
const term = termOverRide || this.plugin.$$getDeviceAndVaultName(); const term = termOverRide || this.services.setting.getDeviceAndVaultName();
const category = this.getFileCategory(path); const category = this.getFileCategory(path);
const name = const name =
category == "CONFIG" || category == "SNIPPET" category == "CONFIG" || category == "SNIPPET"
@@ -645,7 +654,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
} }
filenameWithUnifiedKey(path: string, termOverRide?: string) { filenameWithUnifiedKey(path: string, termOverRide?: string) {
const term = termOverRide || this.plugin.$$getDeviceAndVaultName(); const term = termOverRide || this.services.setting.getDeviceAndVaultName();
const category = this.getFileCategory(path); const category = this.getFileCategory(path);
const name = const name =
category == "CONFIG" || category == "SNIPPET" ? path.split("/").slice(-1)[0] : path.split("/").slice(-2)[0]; 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) { unifiedKeyPrefixOfTerminal(termOverRide?: string) {
const term = termOverRide || this.plugin.$$getDeviceAndVaultName(); const term = termOverRide || this.services.setting.getDeviceAndVaultName();
return `${ICXHeader}${term}/` as FilePathWithPrefix; return `${ICXHeader}${term}/` as FilePathWithPrefix;
} }
@@ -831,7 +840,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
const v2Path = (prefixPath + relativeFilename) as FilePathWithPrefix; const v2Path = (prefixPath + relativeFilename) as FilePathWithPrefix;
// console.warn(`Migrating ${v1Path} / ${relativeFilename} to ${v2Path}`); // console.warn(`Migrating ${v1Path} / ${relativeFilename} to ${v2Path}`);
this._log(`Migrating ${v1Path} / ${relativeFilename} to ${v2Path}`, LOG_LEVEL_VERBOSE); 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 buf =
const data = createBlob([DUMMY_HEAD, DUMMY_END, ...getDocDataAsArray(f.data)]); 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); await this.plugin.storageAccess.ensureDir(path);
// If the content has applied, modified time will be updated to the current time. // If the content has applied, modified time will be updated to the current time.
await this.plugin.storageAccess.writeHiddenFileAuto(path, content); await this.plugin.storageAccess.writeHiddenFileAuto(path, content);
await this.storeCustomisationFileV2(path, this.plugin.$$getDeviceAndVaultName()); await this.storeCustomisationFileV2(path, this.services.setting.getDeviceAndVaultName());
} else { } else {
const files = data.files; const files = data.files;
for (const f of 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); await this.plugin.storageAccess.writeHiddenFileAuto(path, content, stat);
} }
this._log(`Applied ${f.filename} of ${data.displayName || data.name}..`); 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) { } catch (ex) {
@@ -1114,7 +1123,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
); );
} }
} else if (data.category == "CONFIG") { } else if (data.category == "CONFIG") {
this.plugin.$$askReload(); this.services.appLifecycle.askRestart();
} }
return true; return true;
} catch (ex) { } catch (ex) {
@@ -1157,8 +1166,8 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
return false; return false;
} }
} }
async $anyModuleParsedReplicationResultItem(docs: PouchDB.Core.ExistingDocument<EntryDoc>) { async _anyModuleParsedReplicationResultItem(docs: PouchDB.Core.ExistingDocument<EntryDoc>) {
if (!docs._id.startsWith(ICXHeader)) return undefined; if (!docs._id.startsWith(ICXHeader)) return false;
if (this._isThisModuleEnabled()) { if (this._isThisModuleEnabled()) {
await this.updatePluginList( await this.updatePluginList(
false, false,
@@ -1205,7 +1214,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
} }
return true; return true;
} }
async $everyRealizeSettingSyncMode(): Promise<boolean> { async _everyRealizeSettingSyncMode(): Promise<boolean> {
this.periodicPluginSweepProcessor?.disable(); this.periodicPluginSweepProcessor?.disable();
if (!this._isMainReady) return true; if (!this._isMainReady) return true;
if (!this._isMainSuspended()) return true; if (!this._isMainSuspended()) return true;
@@ -1345,7 +1354,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
}); });
} }
async storeCustomizationFiles(path: FilePath, termOverRide?: string) { async storeCustomizationFiles(path: FilePath, termOverRide?: string) {
const term = termOverRide || this.plugin.$$getDeviceAndVaultName(); const term = termOverRide || this.services.setting.getDeviceAndVaultName();
if (term == "") { if (term == "") {
this._log("We have to configure the device name", LOG_LEVEL_NOTICE); this._log("We have to configure the device name", LOG_LEVEL_NOTICE);
return; return;
@@ -1488,7 +1497,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
} }
}); });
} }
async $anyProcessOptionalFileEvent(path: FilePath): Promise<boolean | undefined> { async _anyProcessOptionalFileEvent(path: FilePath): Promise<boolean> {
return await this.watchVaultRawEventsAsync(path); return await this.watchVaultRawEventsAsync(path);
} }
@@ -1535,7 +1544,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
await shareRunningResult("scanAllConfigFiles", async () => { await shareRunningResult("scanAllConfigFiles", async () => {
const logLevel = showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO; const logLevel = showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO;
this._log("Scanning customizing files.", logLevel, "scan-all-config"); this._log("Scanning customizing files.", logLevel, "scan-all-config");
const term = this.plugin.$$getDeviceAndVaultName(); const term = this.services.setting.getDeviceAndVaultName();
if (term == "") { if (term == "") {
this._log("We have to configure the device name", LOG_LEVEL_NOTICE); this._log("We have to configure the device name", LOG_LEVEL_NOTICE);
return; return;
@@ -1673,7 +1682,10 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
return filenames as FilePath[]; return filenames as FilePath[];
} }
async $allAskUsingOptionalSyncFeature(opt: { enableFetch?: boolean; enableOverwrite?: boolean }): Promise<boolean> { private async _allAskUsingOptionalSyncFeature(opt: {
enableFetch?: boolean;
enableOverwrite?: boolean;
}): Promise<boolean> {
await this._askHiddenFileConfiguration(opt); await this._askHiddenFileConfiguration(opt);
return true; return true;
} }
@@ -1707,7 +1719,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
} }
} }
$anyGetOptionalConflictCheckMethod(path: FilePathWithPrefix): Promise<boolean | "newer"> { _anyGetOptionalConflictCheckMethod(path: FilePathWithPrefix): Promise<boolean | "newer"> {
if (isPluginMetadata(path)) { if (isPluginMetadata(path)) {
return Promise.resolve("newer"); return Promise.resolve("newer");
} }
@@ -1717,7 +1729,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
return Promise.resolve(false); return Promise.resolve(false);
} }
$allSuspendExtraSync(): Promise<boolean> { private _allSuspendExtraSync(): Promise<boolean> {
if (this.plugin.settings.usePluginSync || this.plugin.settings.autoSweepPlugins) { if (this.plugin.settings.usePluginSync || this.plugin.settings.autoSweepPlugins) {
this._log( this._log(
"Customisation sync have been temporarily disabled. Please enable them after the fetching, if you need them.", "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); return Promise.resolve(true);
} }
async $anyConfigureOptionalSyncFeature(mode: "CUSTOMIZE" | "DISABLE" | "DISABLE_CUSTOM") { private async _anyConfigureOptionalSyncFeature(mode: keyof OPTIONAL_SYNC_FEATURES) {
await this.configureHiddenFileSync(mode); await this.configureHiddenFileSync(mode);
return true;
} }
async configureHiddenFileSync(mode: "CUSTOMIZE" | "DISABLE" | "DISABLE_CUSTOM") { async configureHiddenFileSync(mode: keyof OPTIONAL_SYNC_FEATURES) {
if (mode == "DISABLE") { if (mode == "DISABLE") {
this.plugin.settings.usePluginSync = false; this.plugin.settings.usePluginSync = false;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
@@ -1740,7 +1753,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
} }
if (mode == "CUSTOMIZE") { 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`); let name = await this.plugin.confirm.askString("Device name", "Please set this device name", `desktop`);
if (!name) { if (!name) {
if (Platform.isAndroidApp) { if (Platform.isAndroidApp) {
@@ -1764,7 +1777,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
} }
name = name + Math.random().toString(36).slice(-4); 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.usePluginSync = true;
this.plugin.settings.useAdvancedMode = true; this.plugin.settings.useAdvancedMode = true;
@@ -1789,4 +1802,17 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
} }
return files; 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));
}
} }

View File

@@ -25,7 +25,7 @@
export let plugin: ObsidianLiveSyncPlugin; export let plugin: ObsidianLiveSyncPlugin;
$: hideNotApplicable = false; $: hideNotApplicable = false;
$: thisTerm = plugin.$$getDeviceAndVaultName(); $: thisTerm = plugin.services.setting.getDeviceAndVaultName();
const addOn = plugin.getAddOn(ConfigSync.name) as ConfigSync; const addOn = plugin.getAddOn(ConfigSync.name) as ConfigSync;
if (!addOn) { if (!addOn) {
@@ -98,7 +98,7 @@
await requestUpdate(); await requestUpdate();
} }
async function replicate() { async function replicate() {
await plugin.$$replicate(true); await plugin.services.replication.replicate(true);
} }
function selectAllNewest(selectMode: boolean) { function selectAllNewest(selectMode: boolean) {
selectNewestPulse++; selectNewestPulse++;
@@ -237,7 +237,7 @@
plugin.settings.pluginSyncExtendedSetting[key].files = files; plugin.settings.pluginSyncExtendedSetting[key].files = files;
plugin.settings.pluginSyncExtendedSetting[key].mode = mode; plugin.settings.pluginSyncExtendedSetting[key].mode = mode;
} }
plugin.$$saveSettingData(); plugin.services.setting.saveSettingData();
} }
function getIcon(mode: SYNC_MODE) { function getIcon(mode: SYNC_MODE) {
if (mode in ICONS) { if (mode in ICONS) {

View File

@@ -51,12 +51,20 @@ import { LiveSyncCommands } from "../LiveSyncCommands.ts";
import { addPrefix, stripAllPrefixes } from "../../lib/src/string_and_binary/path.ts"; import { addPrefix, stripAllPrefixes } from "../../lib/src/string_and_binary/path.ts";
import { QueueProcessor } from "octagonal-wheels/concurrency/processor"; import { QueueProcessor } from "octagonal-wheels/concurrency/processor";
import { hiddenFilesEventCount, hiddenFilesProcessingCount } from "../../lib/src/mock_and_interop/stores.ts"; 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 { 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 { Semaphore } from "octagonal-wheels/concurrency/semaphore";
import type { LiveSyncCore } from "../../main.ts";
type SyncDirection = "push" | "pull" | "safe" | "pullForce" | "pushForce"; 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( function getComparingMTime(
doc: (MetaEntry | LoadedEntry | false) | UXFileInfo | UXStat | null | undefined, doc: (MetaEntry | LoadedEntry | false) | UXFileInfo | UXStat | null | undefined,
includeDeleted = false includeDeleted = false
@@ -72,7 +80,7 @@ function getComparingMTime(
return doc.mtime ?? 0; return doc.mtime ?? 0;
} }
export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule { export class HiddenFileSync extends LiveSyncCommands {
_isThisModuleEnabled() { _isThisModuleEnabled() {
return this.plugin.settings.syncInternalFiles; return this.plugin.settings.syncInternalFiles;
} }
@@ -132,13 +140,17 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule
this.updateSettingCache(); this.updateSettingCache();
}); });
} }
async $everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise<boolean> { // We cannot initialise autosaveCache because kvDB is not ready yet
// async _everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise<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;
// }
private async _everyOnDatabaseInitialized(showNotice: boolean) {
this._fileInfoLastProcessed = await autosaveCache(this.kvDB, "hidden-file-lastProcessed"); this._fileInfoLastProcessed = await autosaveCache(this.kvDB, "hidden-file-lastProcessed");
this._databaseInfoLastProcessed = await autosaveCache(this.kvDB, "hidden-file-lastProcessed-database"); this._databaseInfoLastProcessed = await autosaveCache(this.kvDB, "hidden-file-lastProcessed-database");
this._fileInfoLastKnown = await autosaveCache(this.kvDB, "hidden-file-lastKnown"); this._fileInfoLastKnown = await autosaveCache(this.kvDB, "hidden-file-lastKnown");
return true;
}
async $everyOnDatabaseInitialized(showNotice: boolean) {
if (this._isThisModuleEnabled()) { if (this._isThisModuleEnabled()) {
if (this._fileInfoLastProcessed.size == 0 && this._fileInfoLastProcessed.size == 0) { if (this._fileInfoLastProcessed.size == 0 && this._fileInfoLastProcessed.size == 0) {
this._log(`No cache found. Performing startup scan.`, LOG_LEVEL_VERBOSE); this._log(`No cache found. Performing startup scan.`, LOG_LEVEL_VERBOSE);
@@ -149,7 +161,7 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule
} }
return true; return true;
} }
async $everyBeforeReplicate(showNotice: boolean) { async _everyBeforeReplicate(showNotice: boolean) {
if ( if (
this._isThisModuleEnabled() && this._isThisModuleEnabled() &&
this._isDatabaseReady() && this._isDatabaseReady() &&
@@ -161,7 +173,7 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule
return true; return true;
} }
$everyOnloadAfterLoadSettings(): Promise<boolean> { private _everyOnloadAfterLoadSettings(): Promise<boolean> {
this.updateSettingCache(); this.updateSettingCache();
return Promise.resolve(true); return Promise.resolve(true);
} }
@@ -197,7 +209,7 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule
await this.applyOfflineChanges(showNotice); await this.applyOfflineChanges(showNotice);
} }
async $everyOnResumeProcess(): Promise<boolean> { async _everyOnResumeProcess(): Promise<boolean> {
this.periodicInternalFileScanProcessor?.disable(); this.periodicInternalFileScanProcessor?.disable();
if (this._isMainSuspended()) return true; if (this._isMainSuspended()) return true;
if (this._isThisModuleEnabled()) { if (this._isThisModuleEnabled()) {
@@ -211,10 +223,10 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule
return true; return true;
} }
$everyRealizeSettingSyncMode(): Promise<boolean> { _everyRealizeSettingSyncMode(): Promise<boolean> {
this.periodicInternalFileScanProcessor?.disable(); this.periodicInternalFileScanProcessor?.disable();
if (this._isMainSuspended()) return Promise.resolve(true); 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.periodicInternalFileScanProcessor.enable(
this._isThisModuleEnabled() && this.settings.syncInternalFilesInterval this._isThisModuleEnabled() && this.settings.syncInternalFilesInterval
? this.settings.syncInternalFilesInterval * 1000 ? this.settings.syncInternalFilesInterval * 1000
@@ -227,13 +239,14 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule
return Promise.resolve(true); return Promise.resolve(true);
} }
async $anyProcessOptionalFileEvent(path: FilePath): Promise<boolean | undefined> { async _anyProcessOptionalFileEvent(path: FilePath): Promise<boolean> {
if (this.isReady()) { if (this.isReady()) {
return await this.trackStorageFileModification(path); return (await this.trackStorageFileModification(path)) || false;
} }
return false;
} }
$anyGetOptionalConflictCheckMethod(path: FilePathWithPrefix): Promise<boolean | "newer"> { _anyGetOptionalConflictCheckMethod(path: FilePathWithPrefix): Promise<boolean | "newer"> {
if (isInternalMetadata(path)) { if (isInternalMetadata(path)) {
this.queueConflictCheck(path); this.queueConflictCheck(path);
return Promise.resolve(true); return Promise.resolve(true);
@@ -241,12 +254,12 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule
return Promise.resolve(false); return Promise.resolve(false);
} }
async $anyProcessOptionalSyncFiles(doc: LoadedEntry): Promise<boolean | undefined> { async _anyProcessOptionalSyncFiles(doc: LoadedEntry): Promise<boolean> {
if (isInternalMetadata(doc._id)) { if (isInternalMetadata(doc._id)) {
if (this._isThisModuleEnabled()) { if (this._isThisModuleEnabled()) {
//system file //system file
const filename = getPath(doc); const filename = getPath(doc);
if (await this.plugin.$$isTargetFile(filename)) { if (await this.services.vault.isTargetFile(filename)) {
// this.procInternalFile(filename); // this.procInternalFile(filename);
await this.processReplicationResult(doc); await this.processReplicationResult(doc);
return true; return true;
@@ -1091,14 +1104,14 @@ Offline Changed files: ${files.length}`;
// If something changes left, notify for reloading Obsidian. // If something changes left, notify for reloading Obsidian.
if (updatedFolders.indexOf(this.plugin.app.vault.configDir) >= 0) { if (updatedFolders.indexOf(this.plugin.app.vault.configDir) >= 0) {
if (!this.plugin.$$isReloadingScheduled()) { if (!this.services.appLifecycle.isReloadingScheduled()) {
this.plugin.confirm.askInPopup( this.plugin.confirm.askInPopup(
`updated-any-hidden`, `updated-any-hidden`,
`Some setting files have been modified\nPress {HERE} to schedule a reload of Obsidian, or press elsewhere to dismiss this message.`, `Some setting files have been modified\nPress {HERE} to schedule a reload of Obsidian, or press elsewhere to dismiss this message.`,
(anchor) => { (anchor) => {
anchor.text = "HERE"; anchor.text = "HERE";
anchor.addEventListener("click", () => { 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) { async storeInternalFileToDatabase(file: InternalFileInfo | UXFileInfo, forceWrite = false) {
const storeFilePath = stripAllPrefixes(file.path as FilePath); const storeFilePath = stripAllPrefixes(file.path as FilePath);
const storageFilePath = file.path; const storageFilePath = file.path;
if (await this.plugin.$$isIgnoredByIgnoreFiles(storageFilePath)) { if (await this.services.vault.isIgnoredByIgnoreFile(storageFilePath)) {
return undefined; return undefined;
} }
const prefixedFileName = addPrefix(storeFilePath, ICHeader); const prefixedFileName = addPrefix(storeFilePath, ICHeader);
@@ -1372,7 +1385,7 @@ Offline Changed files: ${files.length}`;
const displayFileName = filenameSrc; const displayFileName = filenameSrc;
const prefixedFileName = addPrefix(storeFilePath, ICHeader); const prefixedFileName = addPrefix(storeFilePath, ICHeader);
const mtime = new Date().getTime(); const mtime = new Date().getTime();
if (await this.plugin.$$isIgnoredByIgnoreFiles(storageFilePath)) { if (await this.services.vault.isIgnoredByIgnoreFile(storageFilePath)) {
return undefined; return undefined;
} }
return await serialized("file-" + prefixedFileName, async () => { return await serialized("file-" + prefixedFileName, async () => {
@@ -1432,7 +1445,7 @@ Offline Changed files: ${files.length}`;
includeDeletion = true includeDeletion = true
) { ) {
const prefixedFileName = addPrefix(storageFilePath, ICHeader); const prefixedFileName = addPrefix(storageFilePath, ICHeader);
if (await this.plugin.$$isIgnoredByIgnoreFiles(storageFilePath)) { if (await this.services.vault.isIgnoredByIgnoreFile(storageFilePath)) {
return undefined; return undefined;
} }
return await serialized("file-" + prefixedFileName, async () => { return await serialized("file-" + prefixedFileName, async () => {
@@ -1582,7 +1595,7 @@ Offline Changed files: ${files.length}`;
// <-- Database To Storage Functions // <-- Database To Storage Functions
async $allAskUsingOptionalSyncFeature(opt: { enableFetch?: boolean; enableOverwrite?: boolean }) { private async _allAskUsingOptionalSyncFeature(opt: { enableFetch?: boolean; enableOverwrite?: boolean }) {
await this._askHiddenFileConfiguration(opt); await this._askHiddenFileConfiguration(opt);
return true; return true;
} }
@@ -1632,7 +1645,7 @@ ${messageFetch}${messageOverwrite}${messageMerge}
} }
} }
$allSuspendExtraSync(): Promise<boolean> { private _allSuspendExtraSync(): Promise<boolean> {
if (this.plugin.settings.syncInternalFiles) { if (this.plugin.settings.syncInternalFiles) {
this._log( this._log(
"Hidden file synchronization have been temporarily disabled. Please enable them after the fetching, if you need them.", "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 // --> Configuration handling
async $anyConfigureOptionalSyncFeature(mode: "FETCH" | "OVERWRITE" | "MERGE" | "DISABLE" | "DISABLE_HIDDEN") { private async _anyConfigureOptionalSyncFeature(mode: keyof OPTIONAL_SYNC_FEATURES) {
await this.configureHiddenFileSync(mode); await this.configureHiddenFileSync(mode);
return true;
} }
async configureHiddenFileSync(mode: "FETCH" | "OVERWRITE" | "MERGE" | "DISABLE" | "DISABLE_HIDDEN") { async configureHiddenFileSync(mode: keyof OPTIONAL_SYNC_FEATURES) {
if ( if (
mode != "FETCH" && mode != "FETCH" &&
mode != "OVERWRITE" && mode != "OVERWRITE" &&
@@ -1718,7 +1732,7 @@ ${messageFetch}${messageOverwrite}${messageMerge}
const result: InternalFileInfo[] = []; const result: InternalFileInfo[] = [];
for (const f of files) { for (const f of files) {
const w = await f; const w = await f;
if (await this.plugin.$$isIgnoredByIgnoreFiles(w.path)) { if (await this.services.vault.isIgnoredByIgnoreFile(w.path)) {
continue; continue;
} }
const mtime = w.stat?.mtime ?? 0; const mtime = w.stat?.mtime ?? 0;
@@ -1756,7 +1770,7 @@ ${messageFetch}${messageOverwrite}${messageMerge}
if (ignoreFilter && ignoreFilter.some((ee) => ee.test(file))) { if (ignoreFilter && ignoreFilter.some((ee) => ee.test(file))) {
continue; continue;
} }
if (await this.plugin.$$isIgnoredByIgnoreFiles(file)) continue; if (await this.services.vault.isIgnoredByIgnoreFile(file)) continue;
files.push(file); files.push(file);
} }
L1: for (const v of w.folders) { L1: for (const v of w.folders) {
@@ -1768,7 +1782,7 @@ ${messageFetch}${messageOverwrite}${messageMerge}
if (ignoreFilter && ignoreFilter.some((e) => e.test(v))) { if (ignoreFilter && ignoreFilter.some((e) => e.test(v))) {
continue L1; continue L1;
} }
if (await this.plugin.$$isIgnoredByIgnoreFiles(v)) { if (await this.services.vault.isIgnoredByIgnoreFile(v)) {
continue L1; continue L1;
} }
files = files.concat(await this.getFiles(v, ignoreList, filter, ignoreFilter)); files = files.concat(await this.getFiles(v, ignoreList, filter, ignoreFilter));
@@ -1777,4 +1791,20 @@ ${messageFetch}${messageOverwrite}${messageMerge}
} }
// <-- Local Storage SubFunctions // <-- 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));
}
} }

View File

@@ -5,13 +5,13 @@ import {
LOG_LEVEL_NOTICE, LOG_LEVEL_NOTICE,
type AnyEntry, type AnyEntry,
type DocumentID, type DocumentID,
type EntryHasPath,
type FilePath, type FilePath,
type FilePathWithPrefix, type FilePathWithPrefix,
type LOG_LEVEL, type LOG_LEVEL,
} from "../lib/src/common/types.ts"; } from "../lib/src/common/types.ts";
import type ObsidianLiveSyncPlugin from "../main.ts"; import type ObsidianLiveSyncPlugin from "../main.ts";
import { MARK_DONE } from "../modules/features/ModuleLog.ts"; import { MARK_DONE } from "../modules/features/ModuleLog.ts";
import type { LiveSyncCore } from "../main.ts";
let noticeIndex = 0; let noticeIndex = 0;
export abstract class LiveSyncCommands { export abstract class LiveSyncCommands {
@@ -25,12 +25,15 @@ export abstract class LiveSyncCommands {
get localDatabase() { get localDatabase() {
return this.plugin.localDatabase; return this.plugin.localDatabase;
} }
get services() {
id2path(id: DocumentID, entry?: EntryHasPath, stripPrefix?: boolean): FilePathWithPrefix { return this.plugin.services;
return this.plugin.$$id2path(id, entry, stripPrefix);
} }
// id2path(id: DocumentID, entry?: EntryHasPath, stripPrefix?: boolean): FilePathWithPrefix {
// return this.plugin.$$id2path(id, entry, stripPrefix);
// }
async path2id(filename: FilePathWithPrefix | FilePath, prefix?: string): Promise<DocumentID> { async path2id(filename: FilePathWithPrefix | FilePath, prefix?: string): Promise<DocumentID> {
return await this.plugin.$$path2id(filename, prefix); return await this.services.path.path2id(filename, prefix);
} }
getPath(entry: AnyEntry): FilePathWithPrefix { getPath(entry: AnyEntry): FilePathWithPrefix {
return getPath(entry); return getPath(entry);
@@ -38,18 +41,19 @@ export abstract class LiveSyncCommands {
constructor(plugin: ObsidianLiveSyncPlugin) { constructor(plugin: ObsidianLiveSyncPlugin) {
this.plugin = plugin; this.plugin = plugin;
this.onBindFunction(plugin, plugin.services);
} }
abstract onunload(): void; abstract onunload(): void;
abstract onload(): void | Promise<void>; abstract onload(): void | Promise<void>;
_isMainReady() { _isMainReady() {
return this.plugin.$$isReady(); return this.plugin.services.appLifecycle.isReady();
} }
_isMainSuspended() { _isMainSuspended() {
return this.plugin.$$isSuspended(); return this.services.appLifecycle.isSuspended();
} }
_isDatabaseReady() { _isDatabaseReady() {
return this.plugin.$$isDatabaseReady(); return this.services.database.isDatabaseReady();
} }
_log = (msg: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key?: string) => { _log = (msg: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key?: string) => {
@@ -89,4 +93,8 @@ export abstract class LiveSyncCommands {
_debug = (msg: any, key?: string) => { _debug = (msg: any, key?: string) => {
this._log(msg, LOG_LEVEL_VERBOSE, key); this._log(msg, LOG_LEVEL_VERBOSE, key);
}; };
onBindFunction(core: LiveSyncCore, services: typeof core.services) {
// Override if needed.
}
} }

View File

@@ -10,7 +10,6 @@ import {
type MetaEntry, type MetaEntry,
} from "../../lib/src/common/types"; } from "../../lib/src/common/types";
import { getNoFromRev } from "../../lib/src/pouchdb/LiveSyncLocalDB"; import { getNoFromRev } from "../../lib/src/pouchdb/LiveSyncLocalDB";
import type { IObsidianModule } from "../../modules/AbstractObsidianModule";
import { LiveSyncCommands } from "../LiveSyncCommands"; import { LiveSyncCommands } from "../LiveSyncCommands";
import { serialized } from "octagonal-wheels/concurrency/lock_v2"; import { serialized } from "octagonal-wheels/concurrency/lock_v2";
import { arrayToChunkedArray } from "octagonal-wheels/collection"; import { arrayToChunkedArray } from "octagonal-wheels/collection";
@@ -22,10 +21,7 @@ type NoteDocumentID = DocumentID;
type Rev = string; type Rev = string;
type ChunkUsageMap = Map<NoteDocumentID, Map<Rev, Set<ChunkID>>>; type ChunkUsageMap = Map<NoteDocumentID, Map<Rev, Set<ChunkID>>>;
export class LocalDatabaseMaintenance extends LiveSyncCommands implements IObsidianModule { export class LocalDatabaseMaintenance extends LiveSyncCommands {
$everyOnload(): Promise<boolean> {
return Promise.resolve(true);
}
onunload(): void { onunload(): void {
// NO OP. // NO OP.
} }

View File

@@ -1,21 +1,27 @@
import type { IObsidianModule } from "../../modules/AbstractObsidianModule";
import { P2PReplicatorPaneView, VIEW_TYPE_P2P } from "./P2PReplicator/P2PReplicatorPaneView.ts"; import { P2PReplicatorPaneView, VIEW_TYPE_P2P } from "./P2PReplicator/P2PReplicatorPaneView.ts";
import { import {
AutoAccepting, AutoAccepting,
LOG_LEVEL_NOTICE, LOG_LEVEL_NOTICE,
P2P_DEFAULT_SETTINGS,
REMOTE_P2P, REMOTE_P2P,
type EntryDoc, type EntryDoc,
type P2PSyncSetting, type P2PSyncSetting,
type RemoteDBSettings, type RemoteDBSettings,
} from "../../lib/src/common/types.ts"; } from "../../lib/src/common/types.ts";
import { LiveSyncCommands } from "../LiveSyncCommands.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 { EVENT_REQUEST_OPEN_P2P, eventHub } from "../../common/events.ts";
import type { LiveSyncAbstractReplicator } from "../../lib/src/replication/LiveSyncAbstractReplicator.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 type { CommandShim } from "../../lib/src/replication/trystero/P2PReplicatorPaneCommon.ts";
import { import {
P2PReplicatorMixIn, addP2PEventHandlers,
closeP2PReplicator,
openP2PReplicator,
P2PLogCollector,
removeP2PReplicatorInstance, removeP2PReplicatorInstance,
type P2PReplicatorBase, type P2PReplicatorBase,
} from "../../lib/src/replication/trystero/P2PReplicatorCore.ts"; } 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 ObsidianLiveSyncPlugin from "../../main.ts";
import type { SimpleStore } from "octagonal-wheels/databases/SimpleStoreBase"; import type { SimpleStore } from "octagonal-wheels/databases/SimpleStoreBase";
import { getPlatformName } from "../../lib/src/PlatformAPIs/obsidian/Environment.ts"; 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<any>;
// simpleStore(): SimpleStore<any> {
// return this._simpleStore;
// }
// constructor(plugin: ObsidianLiveSyncPlugin) {
// super(plugin);
// this.onBindFunction(plugin, plugin.services);
// }
// async handleReplicatedDocuments(docs: EntryDoc[]): Promise<void> {
// // console.log("Processing Replicated Docs", docs);
// return await this.services.replication.parseSynchroniseResult(
// docs as PouchDB.Core.ExistingDocument<EntryDoc>[]
// );
// }
// onunload(): void {
// throw new Error("Method not implemented.");
// }
// onload(): void | Promise<void> {
// 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<RemoteDBSettings> = {}): Promise<LiveSyncAbstractReplicator> {
// 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<void> {
// eventHub.onEvent(EVENT_REQUEST_OPEN_P2P, () => {
// void this.openPane();
// });
// this.p2pLogCollector.p2pReplicationLine.onChanged((line) => {
// this.storeP2PStatusLine.value = line.value;
// });
// }
// async _everyOnInitializeDatabase(): Promise<boolean> {
// 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<boolean> {
// // 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<boolean> {
// 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<boolean> {
// 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(""); storeP2PStatusLine = reactiveSource("");
getSettings(): P2PSyncSetting { getSettings(): P2PSyncSetting {
@@ -49,47 +227,126 @@ class P2PReplicatorCommandBase extends LiveSyncCommands implements P2PReplicator
constructor(plugin: ObsidianLiveSyncPlugin) { constructor(plugin: ObsidianLiveSyncPlugin) {
super(plugin); super(plugin);
setReplicatorFunc(() => this._replicatorInstance);
addP2PEventHandlers(this);
this.afterConstructor();
this.onBindFunction(plugin, plugin.services);
} }
async handleReplicatedDocuments(docs: EntryDoc[]): Promise<void> { async handleReplicatedDocuments(docs: EntryDoc[]): Promise<void> {
// console.log("Processing Replicated Docs", docs); // console.log("Processing Replicated Docs", docs);
return await this.plugin.$$parseReplicationResult(docs as PouchDB.Core.ExistingDocument<EntryDoc>[]); return await this.services.replication.parseSynchroniseResult(
} docs as PouchDB.Core.ExistingDocument<EntryDoc>[]
onunload(): void { );
throw new Error("Method not implemented.");
}
onload(): void | Promise<void> {
throw new Error("Method not implemented.");
} }
init() { _anyNewReplicator(settingOverride: Partial<RemoteDBSettings> = {}): Promise<LiveSyncAbstractReplicator> {
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<RemoteDBSettings> = {}): Promise<LiveSyncAbstractReplicator> {
const settings = { ...this.settings, ...settingOverride }; const settings = { ...this.settings, ...settingOverride };
if (settings.remoteType == REMOTE_P2P) { if (settings.remoteType == REMOTE_P2P) {
return Promise.resolve(new LiveSyncTrysteroReplicator(this.plugin)); return Promise.resolve(new LiveSyncTrysteroReplicator(this.plugin));
} }
return undefined!; 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<TrysteroReplicator> {
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<void> => {
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(); return getPlatformName();
} }
override onunload(): void { onunload(): void {
removeP2PReplicatorInstance(); removeP2PReplicatorInstance();
void this.close(); void this.close();
} }
override onload(): void | Promise<void> { onload(): void | Promise<void> {
eventHub.onEvent(EVENT_REQUEST_OPEN_P2P, () => { eventHub.onEvent(EVENT_REQUEST_OPEN_P2P, () => {
void this.openPane(); void this.openPane();
}); });
@@ -97,12 +354,12 @@ export class P2PReplicator
this.storeP2PStatusLine.value = line.value; this.storeP2PStatusLine.value = line.value;
}); });
} }
async $everyOnInitializeDatabase(): Promise<boolean> { async _everyOnInitializeDatabase(): Promise<boolean> {
await this.initialiseP2PReplicator(); await this.initialiseP2PReplicator();
return Promise.resolve(true); return Promise.resolve(true);
} }
async $allSuspendExtraSync() { private async _allSuspendExtraSync() {
this.plugin.settings.P2P_Enabled = false; this.plugin.settings.P2P_Enabled = false;
this.plugin.settings.P2P_AutoAccepting = AutoAccepting.NONE; this.plugin.settings.P2P_AutoAccepting = AutoAccepting.NONE;
this.plugin.settings.P2P_AutoBroadcast = false; this.plugin.settings.P2P_AutoBroadcast = false;
@@ -112,15 +369,15 @@ export class P2PReplicator
return await Promise.resolve(true); return await Promise.resolve(true);
} }
async $everyOnLoadStart() { // async $everyOnLoadStart() {
return await Promise.resolve(); // return await Promise.resolve();
} // }
async openPane() { async openPane() {
await this.plugin.$$showView(VIEW_TYPE_P2P); await this.services.API.showWindow(VIEW_TYPE_P2P);
} }
async $everyOnloadStart(): Promise<boolean> { async _everyOnloadStart(): Promise<boolean> {
this.plugin.registerView(VIEW_TYPE_P2P, (leaf) => new P2PReplicatorPaneView(leaf, this.plugin)); this.plugin.registerView(VIEW_TYPE_P2P, (leaf) => new P2PReplicatorPaneView(leaf, this.plugin));
this.plugin.addCommand({ this.plugin.addCommand({
id: "open-p2p-replicator", id: "open-p2p-replicator",
@@ -170,7 +427,7 @@ export class P2PReplicator
return await Promise.resolve(true); return await Promise.resolve(true);
} }
$everyAfterResumeProcess(): Promise<boolean> { _everyAfterResumeProcess(): Promise<boolean> {
if (this.settings.P2P_Enabled && this.settings.P2P_AutoStart) { if (this.settings.P2P_Enabled && this.settings.P2P_AutoStart) {
setTimeout(() => void this.open(), 100); setTimeout(() => void this.open(), 100);
} }
@@ -178,9 +435,18 @@ export class P2PReplicator
rep?.allowReconnection(); rep?.allowReconnection();
return Promise.resolve(true); return Promise.resolve(true);
} }
$everyBeforeSuspendProcess(): Promise<boolean> { _everyBeforeSuspendProcess(): Promise<boolean> {
const rep = this._replicatorInstance; const rep = this._replicatorInstance;
rep?.disconnectFromServer(); rep?.disconnectFromServer();
return Promise.resolve(true); 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));
}
} }

View File

@@ -32,10 +32,10 @@
const initialSettings = { ...plugin.settings }; const initialSettings = { ...plugin.settings };
let settings = $state<P2PSyncSetting>(initialSettings); let settings = $state<P2PSyncSetting>(initialSettings);
// const vaultName = plugin.$$getVaultName(); // const vaultName = service.vault.getVaultName();
// const dbKey = `${vaultName}-p2p-device-name`; // 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<string>(initialDeviceName); let deviceName = $state<string>(initialDeviceName);
let eP2PEnabled = $state<boolean>(initialSettings.P2P_Enabled); let eP2PEnabled = $state<boolean>(initialSettings.P2P_Enabled);

View File

@@ -95,7 +95,7 @@ And you can also drop the local database to rebuild from the remote device.`,
if (yn === DROP) { if (yn === DROP) {
await this.plugin.rebuilder.scheduleFetch(); await this.plugin.rebuilder.scheduleFetch();
} else { } else {
await this.plugin.$$scheduleAppReload(); this.plugin.services.appLifecycle.scheduleRestart();
} }
} else { } else {
Logger(`Cancelled\nRemote config for ${peer.name} is not applied`, LOG_LEVEL_NOTICE); Logger(`Cancelled\nRemote config for ${peer.name} is not applied`, LOG_LEVEL_NOTICE);

Submodule src/lib updated: 21ca077163...868590e814

View File

@@ -1,26 +1,10 @@
import { Plugin } from "./deps"; import { Plugin } from "./deps";
import { import {
type EntryDoc, type EntryDoc,
type LoadedEntry,
type ObsidianLiveSyncSettings, type ObsidianLiveSyncSettings,
type LOG_LEVEL,
type diff_result,
type DatabaseConnectingStatus, type DatabaseConnectingStatus,
type EntryHasPath,
type DocumentID,
type FilePathWithPrefix,
type FilePath,
LOG_LEVEL_INFO,
type HasSettings, 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"; } from "./lib/src/common/types.ts";
import { type FileEventItem } from "./common/types.ts";
import { type SimpleStore } from "./lib/src/common/utils.ts"; import { type SimpleStore } from "./lib/src/common/utils.ts";
import { LiveSyncLocalDB, type LiveSyncLocalDBEnv } from "./lib/src/pouchdb/LiveSyncLocalDB.ts"; import { LiveSyncLocalDB, type LiveSyncLocalDBEnv } from "./lib/src/pouchdb/LiveSyncLocalDB.ts";
import { 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 LiveSyncJournalReplicatorEnv } from "./lib/src/replication/journal/LiveSyncJournalReplicator.js";
import { type LiveSyncCouchDBReplicatorEnv } from "./lib/src/replication/couchdb/LiveSyncReplicator.js"; import { type LiveSyncCouchDBReplicatorEnv } from "./lib/src/replication/couchdb/LiveSyncReplicator.js";
import type { CheckPointInfo } from "./lib/src/replication/journal/JournalSyncTypes.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 type { IObsidianModule } from "./modules/AbstractObsidianModule.ts";
import { ModuleDev } from "./modules/extras/ModuleDev.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 { ModuleFileHandler } from "./modules/core/ModuleFileHandler.ts";
import { ModuleObsidianAPI } from "./modules/essentialObsidian/ModuleObsidianAPI.ts"; import { ModuleObsidianAPI } from "./modules/essentialObsidian/ModuleObsidianAPI.ts";
import { ModuleObsidianEvents } from "./modules/essentialObsidian/ModuleObsidianEvents.ts"; import { ModuleObsidianEvents } from "./modules/essentialObsidian/ModuleObsidianEvents.ts";
import { injectModules, type AbstractModule } from "./modules/AbstractModule.ts"; import { type AbstractModule } from "./modules/AbstractModule.ts";
import type { ICoreModule } from "./modules/ModuleTypes.ts";
import { ModuleObsidianSettingDialogue } from "./modules/features/ModuleObsidianSettingTab.ts"; import { ModuleObsidianSettingDialogue } from "./modules/features/ModuleObsidianSettingTab.ts";
import { ModuleObsidianDocumentHistory } from "./modules/features/ModuleObsidianDocumentHistory.ts"; import { ModuleObsidianDocumentHistory } from "./modules/features/ModuleObsidianDocumentHistory.ts";
import { ModuleObsidianGlobalHistory } from "./modules/features/ModuleGlobalHistory.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 { LocalDatabaseMaintenance } from "./features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts";
import { P2PReplicator } from "./features/P2PSync/CmdP2PReplicator.ts"; import { P2PReplicator } from "./features/P2PSync/CmdP2PReplicator.ts";
import type { LiveSyncManagers } from "./lib/src/managers/LiveSyncManagers.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. * 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. * 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. * $any : Process all modules until the first success.
* $ : Other interceptive points. You should manually assign the module * $ : Other interceptive points. You should manually assign the module
* All of above performed on injectModules function. * 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 export default class ObsidianLiveSyncPlugin
@@ -112,6 +104,18 @@ export default class ObsidianLiveSyncPlugin
LiveSyncCouchDBReplicatorEnv, LiveSyncCouchDBReplicatorEnv,
HasSettings<ObsidianLiveSyncSettings> HasSettings<ObsidianLiveSyncSettings>
{ {
/**
* 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 // --> Module System
getAddOn<T extends LiveSyncCommands>(cls: string) { getAddOn<T extends LiveSyncCommands>(cls: string) {
for (const addon of this.addOns) { for (const addon of this.addOns) {
@@ -173,41 +177,9 @@ export default class ObsidianLiveSyncPlugin
new ModuleReplicateTest(this, this), new ModuleReplicateTest(this, this),
new ModuleIntegratedTest(this, this), new ModuleIntegratedTest(this, this),
] as (IObsidianModule | AbstractModule)[]; ] as (IObsidianModule | AbstractModule)[];
injected = injectModules(this, [...this.modules, ...this.addOns] as ICoreModule[]); // injected = injectModules(this, [...this.modules, ...this.addOns] as ICoreModule[]);
// <-- Module System // <-- 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. // Following are plugged by the modules.
settings!: ObsidianLiveSyncSettings; settings!: ObsidianLiveSyncSettings;
@@ -229,30 +201,6 @@ export default class ObsidianLiveSyncPlugin
return this.settings; return this.settings;
} }
$$markFileListPossiblyChanged(): void {
throwShouldBeOverridden();
}
$$customFetchHandler(): ObsHttpHandler {
throwShouldBeOverridden();
}
$$getLastPostFailedBySize(): boolean {
throwShouldBeOverridden();
}
$$isStorageInsensitive(): boolean {
throwShouldBeOverridden();
}
$$shouldCheckCaseInsensitive(): boolean {
throwShouldBeOverridden();
}
$$isUnloaded(): boolean {
throwShouldBeOverridden();
}
requestCount = reactiveSource(0); requestCount = reactiveSource(0);
responseCount = reactiveSource(0); responseCount = reactiveSource(0);
totalQueued = reactiveSource(0); totalQueued = reactiveSource(0);
@@ -277,101 +225,6 @@ export default class ObsidianLiveSyncPlugin
syncStatus: "CLOSED" as DatabaseConnectingStatus, 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<string, string>,
useRequestAPI: boolean,
getPBKDF2Salt: () => Promise<Uint8Array>
): Promise<
| string
| {
db: PouchDB.Database<EntryDoc>;
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<DocumentID> {
throwShouldBeOverridden();
}
// <!-- Path conversion
// --> Database
$$createPouchDBInstance<T extends object>(
name?: string,
options?: PouchDB.Configuration.DatabaseConfiguration
): PouchDB.Database<T> {
throwShouldBeOverridden();
}
$allOnDBUnload(db: LiveSyncLocalDB): void {
return;
}
$allOnDBClose(db: LiveSyncLocalDB): void {
return;
}
// <!-- Database
$anyNewReplicator(settingOverride: Partial<ObsidianLiveSyncSettings> = {}): Promise<LiveSyncAbstractReplicator> {
throwShouldBeOverridden();
}
$everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise<boolean> {
return InterceptiveEvery;
}
$everyOnResetDatabase(db: LiveSyncLocalDB): Promise<boolean> {
return InterceptiveEvery;
}
// end interfaces
$$getVaultName(): string {
throwShouldBeOverridden();
}
$$getSimpleStore<T>(kind: string): SimpleStore<T> {
throwShouldBeOverridden();
}
// trench!: Trench;
// --> Events // --> Events
/* /*
@@ -412,331 +265,404 @@ export default class ObsidianLiveSyncPlugin
*/ */
$everyOnLayoutReady(): Promise<boolean> { // $everyOnLayoutReady(): Promise<boolean> {
return InterceptiveEvery; // //TODO: AppLifecycleService.onLayoutReady
} // return InterceptiveEvery;
$everyOnFirstInitialize(): Promise<boolean> { // }
return InterceptiveEvery; // $everyOnFirstInitialize(): Promise<boolean> {
} // //TODO: AppLifecycleService.onFirstInitialize
// return InterceptiveEvery;
// }
// Some Module should call this function to start the plugin. // Some Module should call this function to start the plugin.
$$onLiveSyncReady(): Promise<false | undefined> { // $$onLiveSyncReady(): Promise<false | undefined> {
throwShouldBeOverridden(); // //TODO: AppLifecycleService.onLiveSyncReady
} // throwShouldBeOverridden();
$$wireUpEvents(): void { // }
throwShouldBeOverridden(); // $$wireUpEvents(): void {
} // //TODO: AppLifecycleService.wireUpEvents
$$onLiveSyncLoad(): Promise<void> { // throwShouldBeOverridden();
throwShouldBeOverridden(); // }
} // $$onLiveSyncLoad(): Promise<void> {
// //TODO: AppLifecycleService.onLoad
// throwShouldBeOverridden();
// }
$$onLiveSyncUnload(): Promise<void> { // $$onLiveSyncUnload(): Promise<void> {
throwShouldBeOverridden(); // //TODO: AppLifecycleService.onAppUnload
} // throwShouldBeOverridden();
// }
$allScanStat(): Promise<boolean> { // $allScanStat(): Promise<boolean> {
return InterceptiveAll; // //TODO: AppLifecycleService.scanStartupIssues
} // return InterceptiveAll;
$everyOnloadStart(): Promise<boolean> { // }
return InterceptiveEvery; // $everyOnloadStart(): Promise<boolean> {
} // //TODO: AppLifecycleService.onInitialise
// return InterceptiveEvery;
// }
$everyOnloadAfterLoadSettings(): Promise<boolean> { // $everyOnloadAfterLoadSettings(): Promise<boolean> {
return InterceptiveEvery; // //TODO: AppLifecycleService.onApplyStartupLoaded
} // return InterceptiveEvery;
// }
$everyOnload(): Promise<boolean> { // $everyOnload(): Promise<boolean> {
return InterceptiveEvery; // //TODO: AppLifecycleService.onLoaded
} // return InterceptiveEvery;
// }
$anyHandlerProcessesFileEvent(item: FileEventItem): Promise<boolean | undefined> { // $anyHandlerProcessesFileEvent(item: FileEventItem): Promise<boolean | undefined> {
return InterceptiveAny; // //TODO: FileProcessingService.processFileEvent
} // return InterceptiveAny;
// }
$allStartOnUnload(): Promise<boolean> { // $allStartOnUnload(): Promise<boolean> {
return InterceptiveAll; // //TODO: AppLifecycleService.onBeforeUnload
} // return InterceptiveAll;
$allOnUnload(): Promise<boolean> { // }
return InterceptiveAll; // $allOnUnload(): Promise<boolean> {
} // //TODO: AppLifecycleService.onUnload
// return InterceptiveAll;
// }
$$openDatabase(): Promise<boolean> { // $$openDatabase(): Promise<boolean> {
throwShouldBeOverridden(); // // DatabaseService.openDatabase
} // throwShouldBeOverridden();
// }
$$realizeSettingSyncMode(): Promise<void> { // $$realizeSettingSyncMode(): Promise<void> {
throwShouldBeOverridden(); // // SettingService.realiseSetting
} // throwShouldBeOverridden();
$$performRestart() { // }
throwShouldBeOverridden(); // $$performRestart() {
} // // AppLifecycleService.performRestart
// throwShouldBeOverridden();
// }
$$clearUsedPassphrase(): void { // $$clearUsedPassphrase(): void {
throwShouldBeOverridden(); // // SettingService.clearUsedPassphrase
} // throwShouldBeOverridden();
// }
$$decryptSettings(settings: ObsidianLiveSyncSettings): Promise<ObsidianLiveSyncSettings> { // $$decryptSettings(settings: ObsidianLiveSyncSettings): Promise<ObsidianLiveSyncSettings> {
throwShouldBeOverridden(); // // SettingService.decryptSettings
} // throwShouldBeOverridden();
$$adjustSettings(settings: ObsidianLiveSyncSettings): Promise<ObsidianLiveSyncSettings> { // }
throwShouldBeOverridden(); // $$adjustSettings(settings: ObsidianLiveSyncSettings): Promise<ObsidianLiveSyncSettings> {
} // // SettingService.adjustSettings
// throwShouldBeOverridden();
// }
$$loadSettings(): Promise<void> { // $$loadSettings(): Promise<void> {
throwShouldBeOverridden(); // // SettingService.loadSettings
} // throwShouldBeOverridden();
// }
$$saveDeviceAndVaultName(): void { // $$saveDeviceAndVaultName(): void {
throwShouldBeOverridden(); // // SettingService.saveDeviceAndVaultName
} // throwShouldBeOverridden();
// }
$$saveSettingData(): Promise<void> { // $$saveSettingData(): Promise<void> {
throwShouldBeOverridden(); // // SettingService.saveSettingData
} // throwShouldBeOverridden();
// }
$anyProcessOptionalFileEvent(path: FilePath): Promise<boolean | undefined> { // $anyProcessOptionalFileEvent(path: FilePath): Promise<boolean | undefined> {
return InterceptiveAny; // // FileProcessingService.processOptionalFileEvent
} // return InterceptiveAny;
// }
$everyCommitPendingFileEvent(): Promise<boolean> { // $everyCommitPendingFileEvent(): Promise<boolean> {
return InterceptiveEvery; // // FileProcessingService.commitPendingFileEvent
} // return InterceptiveEvery;
// }
// -> // ->
$anyGetOptionalConflictCheckMethod(path: FilePathWithPrefix): Promise<boolean | undefined | "newer"> { // $anyGetOptionalConflictCheckMethod(path: FilePathWithPrefix): Promise<boolean | undefined | "newer"> {
return InterceptiveAny; // return InterceptiveAny;
} // }
$$queueConflictCheckIfOpen(file: FilePathWithPrefix): Promise<void> { // $$queueConflictCheckIfOpen(file: FilePathWithPrefix): Promise<void> {
throwShouldBeOverridden(); // // ConflictEventManager.queueCheckForConflictIfOpen
} // throwShouldBeOverridden();
// }
$$queueConflictCheck(file: FilePathWithPrefix): Promise<void> { // $$queueConflictCheck(file: FilePathWithPrefix): Promise<void> {
throwShouldBeOverridden(); // // ConflictEventManager.queueCheckForConflict
} // throwShouldBeOverridden();
// }
$$waitForAllConflictProcessed(): Promise<boolean> { // $$waitForAllConflictProcessed(): Promise<boolean> {
throwShouldBeOverridden(); // // ConflictEventManager.ensureAllConflictProcessed
} // throwShouldBeOverridden();
// }
//<-- Conflict Check //<-- Conflict Check
$anyProcessOptionalSyncFiles(doc: LoadedEntry): Promise<boolean | undefined> { // $anyProcessOptionalSyncFiles(doc: LoadedEntry): Promise<boolean | undefined> {
return InterceptiveAny; // // ReplicationService.processOptionalSyncFile
} // return InterceptiveAny;
// }
$anyProcessReplicatedDoc(doc: MetaEntry): Promise<boolean | undefined> { // $anyProcessReplicatedDoc(doc: MetaEntry): Promise<boolean | undefined> {
return InterceptiveAny; // // ReplicationService.processReplicatedDocument
} // return InterceptiveAny;
// }
//---> Sync //---> Sync
$$parseReplicationResult(docs: Array<PouchDB.Core.ExistingDocument<EntryDoc>>): void { // $$parseReplicationResult(docs: Array<PouchDB.Core.ExistingDocument<EntryDoc>>): void {
throwShouldBeOverridden(); // // ReplicationService.parseSynchroniseResult
} // throwShouldBeOverridden();
// }
$anyModuleParsedReplicationResultItem(docs: PouchDB.Core.ExistingDocument<EntryDoc>): Promise<boolean | undefined> { // $anyModuleParsedReplicationResultItem(docs: PouchDB.Core.ExistingDocument<EntryDoc>): Promise<boolean | undefined> {
return InterceptiveAny; // // ReplicationService.processVirtualDocument
} // return InterceptiveAny;
$everyBeforeRealizeSetting(): Promise<boolean> { // }
return InterceptiveEvery; // $everyBeforeRealizeSetting(): Promise<boolean> {
} // // SettingEventManager.beforeRealiseSetting
$everyAfterRealizeSetting(): Promise<boolean> { // return InterceptiveEvery;
return InterceptiveEvery; // }
} // $everyAfterRealizeSetting(): Promise<boolean> {
$everyRealizeSettingSyncMode(): Promise<boolean> { // // SettingEventManager.onSettingRealised
return InterceptiveEvery; // return InterceptiveEvery;
} // }
// $everyRealizeSettingSyncMode(): Promise<boolean> {
// // SettingEventManager.onRealiseSetting
// return InterceptiveEvery;
// }
$everyBeforeSuspendProcess(): Promise<boolean> { // $everyBeforeSuspendProcess(): Promise<boolean> {
return InterceptiveEvery; // // AppLifecycleService.onSuspending
} // return InterceptiveEvery;
$everyOnResumeProcess(): Promise<boolean> { // }
return InterceptiveEvery; // $everyOnResumeProcess(): Promise<boolean> {
} // // AppLifecycleService.onResuming
$everyAfterResumeProcess(): Promise<boolean> { // return InterceptiveEvery;
return InterceptiveEvery; // }
} // $everyAfterResumeProcess(): Promise<boolean> {
// // AppLifecycleService.onResumed
// return InterceptiveEvery;
// }
$$fetchRemotePreferredTweakValues(trialSetting: RemoteDBSettings): Promise<TweakValues | false> { // $$fetchRemotePreferredTweakValues(trialSetting: RemoteDBSettings): Promise<TweakValues | false> {
throwShouldBeOverridden(); // //TODO:TweakValueService.fetchRemotePreferred
} // throwShouldBeOverridden();
$$checkAndAskResolvingMismatchedTweaks(preferred: Partial<TweakValues>): Promise<[TweakValues | boolean, boolean]> { // }
throwShouldBeOverridden(); // $$checkAndAskResolvingMismatchedTweaks(preferred: Partial<TweakValues>): Promise<[TweakValues | boolean, boolean]> {
} // //TODO:TweakValueService.checkAndAskResolvingMismatched
$$askResolvingMismatchedTweaks(preferredSource: TweakValues): Promise<"OK" | "CHECKAGAIN" | "IGNORE"> { // throwShouldBeOverridden();
throwShouldBeOverridden(); // }
} // $$askResolvingMismatchedTweaks(preferredSource: TweakValues): Promise<"OK" | "CHECKAGAIN" | "IGNORE"> {
// //TODO:TweakValueService.askResolvingMismatched
// throwShouldBeOverridden();
// }
$$checkAndAskUseRemoteConfiguration( // $$checkAndAskUseRemoteConfiguration(
settings: RemoteDBSettings // settings: RemoteDBSettings
): Promise<{ result: false | TweakValues; requireFetch: boolean }> { // ): Promise<{ result: false | TweakValues; requireFetch: boolean }> {
throwShouldBeOverridden(); // // TweakValueService.checkAndAskUseRemoteConfiguration
} // throwShouldBeOverridden();
// }
$$askUseRemoteConfiguration( // $$askUseRemoteConfiguration(
trialSetting: RemoteDBSettings, // trialSetting: RemoteDBSettings,
preferred: TweakValues // preferred: TweakValues
): Promise<{ result: false | TweakValues; requireFetch: boolean }> { // ): Promise<{ result: false | TweakValues; requireFetch: boolean }> {
throwShouldBeOverridden(); // // TweakValueService.askUseRemoteConfiguration
} // throwShouldBeOverridden();
$everyBeforeReplicate(showMessage: boolean): Promise<boolean> { // }
return InterceptiveEvery; // $everyBeforeReplicate(showMessage: boolean): Promise<boolean> {
} // // ReplicationService.beforeReplicate
// return InterceptiveEvery;
// }
$$canReplicate(showMessage: boolean = false): Promise<boolean> { // $$canReplicate(showMessage: boolean = false): Promise<boolean> {
throwShouldBeOverridden(); // // ReplicationService.isReplicationReady
} // throwShouldBeOverridden();
// }
$$replicate(showMessage: boolean = false): Promise<boolean | void> { // $$replicate(showMessage: boolean = false): Promise<boolean | void> {
throwShouldBeOverridden(); // // ReplicationService.replicate
} // throwShouldBeOverridden();
$$replicateByEvent(showMessage: boolean = false): Promise<boolean | void> { // }
throwShouldBeOverridden(); // $$replicateByEvent(showMessage: boolean = false): Promise<boolean | void> {
} // // ReplicationService.replicateByEvent
// throwShouldBeOverridden();
// }
$everyOnDatabaseInitialized(showingNotice: boolean): Promise<boolean> { // $everyOnDatabaseInitialized(showingNotice: boolean): Promise<boolean> {
throwShouldBeOverridden(); // // DatabaseEventService.onDatabaseInitialised
} // throwShouldBeOverridden();
// }
$$initializeDatabase( // $$initializeDatabase(
showingNotice: boolean = false, // showingNotice: boolean = false,
reopenDatabase = true, // reopenDatabase = true,
ignoreSuspending: boolean = false // ignoreSuspending: boolean = false
): Promise<boolean> { // ): Promise<boolean> {
throwShouldBeOverridden(); // // DatabaseEventService.initializeDatabase
} // throwShouldBeOverridden();
// }
$anyAfterConnectCheckFailed(): Promise<boolean | "CHECKAGAIN" | undefined> { // $anyAfterConnectCheckFailed(): Promise<boolean | "CHECKAGAIN" | undefined> {
return InterceptiveAny; // // ReplicationService.checkConnectionFailure
} // return InterceptiveAny;
// }
$$replicateAllToServer( // $$replicateAllToServer(
showingNotice: boolean = false, // showingNotice: boolean = false,
sendChunksInBulkDisabled: boolean = false // sendChunksInBulkDisabled: boolean = false
): Promise<boolean> { // ): Promise<boolean> {
throwShouldBeOverridden(); // // RemoteService.replicateAllToRemote
} // throwShouldBeOverridden();
$$replicateAllFromServer(showingNotice: boolean = false): Promise<boolean> { // }
throwShouldBeOverridden(); // $$replicateAllFromServer(showingNotice: boolean = false): Promise<boolean> {
} // // RemoteService.replicateAllFromRemote
// throwShouldBeOverridden();
// }
// Remote Governing // Remote Governing
$$markRemoteLocked(lockByClean: boolean = false): Promise<void> { // $$markRemoteLocked(lockByClean: boolean = false): Promise<void> {
throwShouldBeOverridden(); // // RemoteService.markLocked;
} // throwShouldBeOverridden();
// }
$$markRemoteUnlocked(): Promise<void> { // $$markRemoteUnlocked(): Promise<void> {
throwShouldBeOverridden(); // // RemoteService.markUnlocked;
} // throwShouldBeOverridden();
// }
$$markRemoteResolved(): Promise<void> { // $$markRemoteResolved(): Promise<void> {
throwShouldBeOverridden(); // // RemoteService.markResolved;
} // throwShouldBeOverridden();
// }
// <-- Remote Governing // <-- Remote Governing
$$isFileSizeExceeded(size: number): boolean { // $$isFileSizeExceeded(size: number): boolean {
throwShouldBeOverridden(); // // VaultService.isFileSizeTooLarge
} // throwShouldBeOverridden();
// }
$$performFullScan(showingNotice?: boolean, ignoreSuspending?: boolean): Promise<void> { // $$performFullScan(showingNotice?: boolean, ignoreSuspending?: boolean): Promise<void> {
throwShouldBeOverridden(); // // VaultService.scanVault
} // throwShouldBeOverridden();
// }
$anyResolveConflictByUI( // $anyResolveConflictByUI(
filename: FilePathWithPrefix, // filename: FilePathWithPrefix,
conflictCheckResult: diff_result // conflictCheckResult: diff_result
): Promise<boolean | undefined> { // ): Promise<boolean | undefined> {
return InterceptiveAny; // // ConflictService.resolveConflictByUserInteraction
} // return InterceptiveAny;
$$resolveConflictByDeletingRev( // }
path: FilePathWithPrefix, // $$resolveConflictByDeletingRev(
deleteRevision: string, // path: FilePathWithPrefix,
subTitle = "" // deleteRevision: string,
): Promise<typeof MISSING_OR_ERROR | typeof AUTO_MERGED> { // subTitle = ""
throwShouldBeOverridden(); // ): Promise<typeof MISSING_OR_ERROR | typeof AUTO_MERGED> {
} // // ConflictService.resolveByDeletingRevision
$$resolveConflict(filename: FilePathWithPrefix): Promise<void> { // throwShouldBeOverridden();
throwShouldBeOverridden(); // }
} // $$resolveConflict(filename: FilePathWithPrefix): Promise<void> {
$anyResolveConflictByNewest(filename: FilePathWithPrefix): Promise<boolean> { // // ConflictService.resolveConflict
throwShouldBeOverridden(); // throwShouldBeOverridden();
} // }
// $anyResolveConflictByNewest(filename: FilePathWithPrefix): Promise<boolean> {
// // ConflictService.resolveByNewest
// throwShouldBeOverridden();
// }
$$resetLocalDatabase(): Promise<void> { // $$resetLocalDatabase(): Promise<void> {
throwShouldBeOverridden(); // // DatabaseService.resetDatabase;
} // throwShouldBeOverridden();
// }
$$tryResetRemoteDatabase(): Promise<void> { // $$tryResetRemoteDatabase(): Promise<void> {
throwShouldBeOverridden(); // // RemoteService.tryResetDatabase;
} // throwShouldBeOverridden();
// }
$$tryCreateRemoteDatabase(): Promise<void> { // $$tryCreateRemoteDatabase(): Promise<void> {
throwShouldBeOverridden(); // // RemoteService.tryCreateDatabase;
} // throwShouldBeOverridden();
// }
$$isIgnoredByIgnoreFiles(file: string | UXFileInfoStub): Promise<boolean> { // $$isIgnoredByIgnoreFiles(file: string | UXFileInfoStub): Promise<boolean> {
throwShouldBeOverridden(); // // VaultService.isIgnoredByIgnoreFiles
} // throwShouldBeOverridden();
// }
$$isTargetFile(file: string | UXFileInfoStub, keepFileCheckList = false): Promise<boolean> { // $$isTargetFile(file: string | UXFileInfoStub, keepFileCheckList = false): Promise<boolean> {
throwShouldBeOverridden(); // // VaultService.isTargetFile
} // throwShouldBeOverridden();
// }
$$askReload(message?: string) { // $$askReload(message?: string) {
throwShouldBeOverridden(); // // AppLifecycleService.askRestart
} // throwShouldBeOverridden();
$$scheduleAppReload() { // }
throwShouldBeOverridden(); // $$scheduleAppReload() {
} // // AppLifecycleService.scheduleRestart
// throwShouldBeOverridden();
// }
//--- Setup //--- Setup
$allSuspendAllSync(): Promise<boolean> { // $allSuspendAllSync(): Promise<boolean> {
return InterceptiveAll; // // SettingEventManager.suspendAllSync
} // return InterceptiveAll;
$allSuspendExtraSync(): Promise<boolean> { // }
return InterceptiveAll; // $allSuspendExtraSync(): Promise<boolean> {
} // // SettingEventManager.suspendExtraSync
// return InterceptiveAll;
// }
$allAskUsingOptionalSyncFeature(opt: { enableFetch?: boolean; enableOverwrite?: boolean }): Promise<boolean> { // $allAskUsingOptionalSyncFeature(opt: { enableFetch?: boolean; enableOverwrite?: boolean }): Promise<boolean> {
throwShouldBeOverridden(); // // SettingEventManager.suggestOptionalFeatures
} // throwShouldBeOverridden();
$anyConfigureOptionalSyncFeature(mode: string): Promise<void> { // }
throwShouldBeOverridden(); // $anyConfigureOptionalSyncFeature(mode: string): Promise<void> {
} // // SettingEventManager.enableOptionalFeature
// throwShouldBeOverridden();
// }
$$showView(viewType: string): Promise<void> { // $$showView(viewType: string): Promise<void> {
throwShouldBeOverridden(); // // UIManager.showWindow //
} // throwShouldBeOverridden();
// }
// For Development: Ensure reliability MORE AND MORE. May the this plug-in helps all of us. // For Development: Ensure reliability MORE AND MORE. May the this plug-in helps all of us.
$everyModuleTest(): Promise<boolean> { // $everyModuleTest(): Promise<boolean> {
return InterceptiveEvery; // return InterceptiveEvery;
} // }
$everyModuleTestMultiDevice(): Promise<boolean> { // $everyModuleTestMultiDevice(): Promise<boolean> {
return InterceptiveEvery; // return InterceptiveEvery;
} // }
$$addTestResult(name: string, key: string, result: boolean, summary?: string, message?: string): void { // $$addTestResult(name: string, key: string, result: boolean, summary?: string, message?: string): void {
throwShouldBeOverridden(); // throwShouldBeOverridden();
} // }
_isThisModuleEnabled(): boolean { // _isThisModuleEnabled(): boolean {
return true; // return true;
} // }
$anyGetAppId(): Promise<string | undefined> { // $anyGetAppId(): Promise<string | undefined> {
return InterceptiveAny; // // APIService.getAppId
} // return InterceptiveAny;
// }
// Plug-in's overrideable functions // Plug-in's overrideable functions
onload() { onload() {
void this.$$onLiveSyncLoad(); void this.services.appLifecycle.onLoad();
} }
async saveSettings() { async saveSettings() {
await this.$$saveSettingData(); await this.services.setting.saveSettingData();
} }
onunload() { onunload() {
return void this.$$onLiveSyncUnload(); return void this.services.appLifecycle.onAppUnload();
} }
// <-- Plug-in's overrideable functions // <-- Plug-in's overrideable functions
} }

View File

@@ -1,34 +1,34 @@
import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, Logger } from "octagonal-wheels/common/logger"; 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 { LOG_LEVEL } from "../lib/src/common/types";
import type { LiveSyncCore } from "../main"; import type { LiveSyncCore } from "../main";
import { unique } from "octagonal-wheels/collection"; // import { unique } from "octagonal-wheels/collection";
import type { IObsidianModule } from "./AbstractObsidianModule.ts"; // import type { IObsidianModule } from "./AbstractObsidianModule.ts";
import type { // import type {
ICoreModuleBase, // ICoreModuleBase,
AllInjectableProps, // AllInjectableProps,
AllExecuteProps, // AllExecuteProps,
EveryExecuteProps, // EveryExecuteProps,
AnyExecuteProps, // AnyExecuteProps,
ICoreModule, // ICoreModule,
} from "./ModuleTypes"; // } from "./ModuleTypes";
function isOverridableKey(key: string): key is keyof ICoreModuleBase { // function isOverridableKey(key: string): key is keyof ICoreModuleBase {
return key.startsWith("$"); // return key.startsWith("$");
} // }
function isInjectableKey(key: string): key is keyof AllInjectableProps { // function isInjectableKey(key: string): key is keyof AllInjectableProps {
return key.startsWith("$$"); // return key.startsWith("$$");
} // }
function isAllExecuteKey(key: string): key is keyof AllExecuteProps { // function isAllExecuteKey(key: string): key is keyof AllExecuteProps {
return key.startsWith("$all"); // return key.startsWith("$all");
} // }
function isEveryExecuteKey(key: string): key is keyof EveryExecuteProps { // function isEveryExecuteKey(key: string): key is keyof EveryExecuteProps {
return key.startsWith("$every"); // return key.startsWith("$every");
} // }
function isAnyExecuteKey(key: string): key is keyof AnyExecuteProps { // function isAnyExecuteKey(key: string): key is keyof AnyExecuteProps {
return key.startsWith("$any"); // return key.startsWith("$any");
} // }
/** /**
* All $prefixed functions are hooked by the modules. Be careful to call them directly. * 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. * 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 * $ : Other interceptive points. You should manually assign the module
* All of above performed on injectModules function. * All of above performed on injectModules function.
*/ */
export function injectModules<T extends ICoreModule>(target: T, modules: ICoreModule[]) { // export function injectModules<T extends ICoreModule>(target: T, modules: ICoreModule[]) {
const allKeys = unique([ // const allKeys = unique([
...Object.keys(Object.getOwnPropertyDescriptors(target)), // ...Object.keys(Object.getOwnPropertyDescriptors(target)),
...Object.keys(Object.getOwnPropertyDescriptors(Object.getPrototypeOf(target))), // ...Object.keys(Object.getOwnPropertyDescriptors(Object.getPrototypeOf(target))),
]).filter((e) => e.startsWith("$")) as (keyof ICoreModule)[]; // ]).filter((e) => e.startsWith("$")) as (keyof ICoreModule)[];
const moduleMap = new Map<string, IObsidianModule[]>(); // const moduleMap = new Map<string, IObsidianModule[]>();
for (const module of modules) { // for (const module of modules) {
for (const key of allKeys) { // for (const key of allKeys) {
if (isOverridableKey(key)) { // if (isOverridableKey(key)) {
if (key in module) { // if (key in module) {
const list = moduleMap.get(key) || []; // const list = moduleMap.get(key) || [];
if (typeof module[key] === "function") { // if (typeof module[key] === "function") {
module[key] = module[key].bind(module) as any; // module[key] = module[key].bind(module) as any;
} // }
list.push(module); // list.push(module);
moduleMap.set(key, list); // moduleMap.set(key, list);
} // }
} // }
} // }
} // }
Logger(`Injecting modules for ${target.constructor.name}`, LOG_LEVEL_VERBOSE); // Logger(`Injecting modules for ${target.constructor.name}`, LOG_LEVEL_VERBOSE);
for (const key of allKeys) { // for (const key of allKeys) {
const modules = moduleMap.get(key) || []; // const modules = moduleMap.get(key) || [];
if (isInjectableKey(key)) { // if (isInjectableKey(key)) {
if (modules.length == 0) { // if (modules.length == 0) {
throw new Error(`No module injected for ${key}. This is a fatal error.`); // throw new Error(`No module injected for ${key}. This is a fatal error.`);
} // }
target[key] = modules[0][key]! as any; // target[key] = modules[0][key]! as any;
Logger(`[${modules[0].constructor.name}]: Injected ${key} `, LOG_LEVEL_VERBOSE); // Logger(`[${modules[0].constructor.name}]: Injected ${key} `, LOG_LEVEL_VERBOSE);
} else if (isAllExecuteKey(key)) { // } else if (isAllExecuteKey(key)) {
const modules = moduleMap.get(key) || []; // const modules = moduleMap.get(key) || [];
target[key] = async (...args: any) => { // target[key] = async (...args: any) => {
for (const module of modules) { // for (const module of modules) {
try { // try {
//@ts-ignore // //@ts-ignore
await module[key]!(...args); // await module[key]!(...args);
} catch (ex) { // } catch (ex) {
Logger(`[${module.constructor.name}]: All handler for ${key} failed`, LOG_LEVEL_VERBOSE); // Logger(`[${module.constructor.name}]: All handler for ${key} failed`, LOG_LEVEL_VERBOSE);
Logger(ex, LOG_LEVEL_VERBOSE); // Logger(ex, LOG_LEVEL_VERBOSE);
} // }
} // }
return true; // return true;
}; // };
for (const module of modules) { // for (const module of modules) {
Logger(`[${module.constructor.name}]: Injected (All) ${key} `, LOG_LEVEL_VERBOSE); // Logger(`[${module.constructor.name}]: Injected (All) ${key} `, LOG_LEVEL_VERBOSE);
} // }
} else if (isEveryExecuteKey(key)) { // } else if (isEveryExecuteKey(key)) {
target[key] = async (...args: any) => { // target[key] = async (...args: any) => {
for (const module of modules) { // for (const module of modules) {
try { // try {
//@ts-ignore:2556 // //@ts-ignore:2556
const ret = await module[key]!(...args); // const ret = await module[key]!(...args);
if (ret !== undefined && !ret) { // if (ret !== undefined && !ret) {
// Failed then return that falsy value. // // Failed then return that falsy value.
return ret; // return ret;
} // }
} catch (ex) { // } catch (ex) {
Logger(`[${module.constructor.name}]: Every handler for ${key} failed`); // Logger(`[${module.constructor.name}]: Every handler for ${key} failed`);
Logger(ex, LOG_LEVEL_VERBOSE); // Logger(ex, LOG_LEVEL_VERBOSE);
} // }
} // }
return true; // return true;
}; // };
for (const module of modules) { // for (const module of modules) {
Logger(`[${module.constructor.name}]: Injected (Every) ${key} `, LOG_LEVEL_VERBOSE); // Logger(`[${module.constructor.name}]: Injected (Every) ${key} `, LOG_LEVEL_VERBOSE);
} // }
} else if (isAnyExecuteKey(key)) { // } else if (isAnyExecuteKey(key)) {
//@ts-ignore // //@ts-ignore
target[key] = async (...args: any[]) => { // target[key] = async (...args: any[]) => {
for (const module of modules) { // for (const module of modules) {
try { // try {
//@ts-ignore:2556 // //@ts-ignore:2556
const ret = await module[key](...args); // const ret = await module[key](...args);
// If truly value returned, then return that value. // // If truly value returned, then return that value.
if (ret) { // if (ret) {
return ret; // return ret;
} // }
} catch (ex) { // } catch (ex) {
Logger(`[${module.constructor.name}]: Any handler for ${key} failed`); // Logger(`[${module.constructor.name}]: Any handler for ${key} failed`);
Logger(ex, LOG_LEVEL_VERBOSE); // Logger(ex, LOG_LEVEL_VERBOSE);
} // }
} // }
return false; // return false;
}; // };
for (const module of modules) { // for (const module of modules) {
Logger(`[${module.constructor.name}]: Injected (Any) ${key} `, LOG_LEVEL_VERBOSE); // Logger(`[${module.constructor.name}]: Injected (Any) ${key} `, LOG_LEVEL_VERBOSE);
} // }
} else { // } else {
Logger(`No injected handler for ${key} `, LOG_LEVEL_VERBOSE); // Logger(`No injected handler for ${key} `, LOG_LEVEL_VERBOSE);
} // }
} // }
Logger(`Injected modules for ${target.constructor.name}`, LOG_LEVEL_VERBOSE); // Logger(`Injected modules for ${target.constructor.name}`, LOG_LEVEL_VERBOSE);
return true; // return true;
} // }
export abstract class AbstractModule { export abstract class AbstractModule {
_log = (msg: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key?: string) => { _log = (msg: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key?: string) => {
@@ -153,14 +153,17 @@ export abstract class AbstractModule {
this.core.settings = value; this.core.settings = value;
} }
onBindFunction(core: LiveSyncCore, services: typeof core.services) {
// Override if needed.
}
constructor(public core: LiveSyncCore) { constructor(public core: LiveSyncCore) {
this.onBindFunction(core, core.services);
Logger(`[${this.constructor.name}] Loaded`, LOG_LEVEL_VERBOSE); Logger(`[${this.constructor.name}] Loaded`, LOG_LEVEL_VERBOSE);
} }
saveSettings = this.core.saveSettings.bind(this.core); saveSettings = this.core.saveSettings.bind(this.core);
// abstract $everyTest(): Promise<boolean>;
addTestResult(key: string, value: boolean, summary?: string, message?: string) { 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) { testDone(result: boolean = true) {
return Promise.resolve(result); return Promise.resolve(result);
@@ -185,4 +188,8 @@ export abstract class AbstractModule {
} }
return this.testDone(); return this.testDone();
} }
get services() {
return this.core._services;
}
} }

View File

@@ -38,13 +38,13 @@ export abstract class AbstractObsidianModule extends AbstractModule {
saveSettings = this.plugin.saveSettings.bind(this.plugin); saveSettings = this.plugin.saveSettings.bind(this.plugin);
_isMainReady() { _isMainReady() {
return this.core.$$isReady(); return this.services.appLifecycle.isReady();
} }
_isMainSuspended() { _isMainSuspended() {
return this.core.$$isSuspended(); return this.services.appLifecycle.isSuspended();
} }
_isDatabaseReady() { _isDatabaseReady() {
return this.core.$$isDatabaseReady(); return this.services.database.isDatabaseReady();
} }
//should be overridden //should be overridden

View File

@@ -17,7 +17,6 @@ import type {
DocumentID, DocumentID,
} from "../../lib/src/common/types"; } from "../../lib/src/common/types";
import type { DatabaseFileAccess } from "../interfaces/DatabaseFileAccess"; import type { DatabaseFileAccess } from "../interfaces/DatabaseFileAccess";
import { type IObsidianModule } from "../AbstractObsidianModule.ts";
import { isPlainText, shouldBeIgnored, stripAllPrefixes } from "../../lib/src/string_and_binary/path"; import { isPlainText, shouldBeIgnored, stripAllPrefixes } from "../../lib/src/string_and_binary/path";
import { import {
createBlob, createBlob,
@@ -30,14 +29,15 @@ import {
import { serialized } from "octagonal-wheels/concurrency/lock"; import { serialized } from "octagonal-wheels/concurrency/lock";
import { AbstractModule } from "../AbstractModule.ts"; import { AbstractModule } from "../AbstractModule.ts";
import { ICHeader } from "../../common/types.ts"; import { ICHeader } from "../../common/types.ts";
import type { LiveSyncCore } from "../../main.ts";
export class ModuleDatabaseFileAccess extends AbstractModule implements IObsidianModule, DatabaseFileAccess { export class ModuleDatabaseFileAccess extends AbstractModule implements DatabaseFileAccess {
$everyOnload(): Promise<boolean> { private _everyOnload(): Promise<boolean> {
this.core.databaseFileAccess = this; this.core.databaseFileAccess = this;
return Promise.resolve(true); return Promise.resolve(true);
} }
async $everyModuleTest(): Promise<boolean> { private async _everyModuleTest(): Promise<boolean> {
if (!this.settings.enableDebugTools) return Promise.resolve(true); if (!this.settings.enableDebugTools) return Promise.resolve(true);
const testString = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nec purus nec nunc"; const testString = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nec purus nec nunc";
// Before test, we need to delete completely. // Before test, we need to delete completely.
@@ -75,7 +75,7 @@ export class ModuleDatabaseFileAccess extends AbstractModule implements IObsidia
async checkIsTargetFile(file: UXFileInfoStub | FilePathWithPrefix): Promise<boolean> { async checkIsTargetFile(file: UXFileInfoStub | FilePathWithPrefix): Promise<boolean> {
const path = getStoragePathFromUXFileInfo(file); 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); this._log(`File is not target`, LOG_LEVEL_VERBOSE);
return false; 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 id = (idPrefix + idMain) as DocumentID;
const d: SavingEntry = { const d: SavingEntry = {
@@ -345,4 +345,8 @@ export class ModuleDatabaseFileAccess extends AbstractModule implements IObsidia
eventHub.emitEvent(EVENT_FILE_SAVED); eventHub.emitEvent(EVENT_FILE_SAVED);
return ret; return ret;
} }
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this));
services.test.handleTest(this._everyModuleTest.bind(this));
}
} }

View File

@@ -20,11 +20,11 @@ import {
} from "../../common/utils"; } from "../../common/utils";
import { getDocDataAsArray, isDocContentSame, readAsBlob, readContent } from "../../lib/src/common/utils"; import { getDocDataAsArray, isDocContentSame, readAsBlob, readContent } from "../../lib/src/common/utils";
import { shouldBeIgnored } from "../../lib/src/string_and_binary/path"; import { shouldBeIgnored } from "../../lib/src/string_and_binary/path";
import type { ICoreModule } from "../ModuleTypes";
import { Semaphore } from "octagonal-wheels/concurrency/semaphore"; import { Semaphore } from "octagonal-wheels/concurrency/semaphore";
import { eventHub } from "../../common/events.ts"; 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() { get db() {
return this.core.databaseFileAccess; return this.core.databaseFileAccess;
} }
@@ -32,7 +32,7 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule {
return this.core.storageAccess; return this.core.storageAccess;
} }
$everyOnloadStart(): Promise<boolean> { _everyOnloadStart(): Promise<boolean> {
this.core.fileHandler = this; this.core.fileHandler = this;
return Promise.resolve(true); return Promise.resolve(true);
} }
@@ -52,7 +52,7 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule {
info: UXFileInfoStub | UXFileInfo | UXInternalFileInfoStub | FilePathWithPrefix, info: UXFileInfoStub | UXFileInfo | UXInternalFileInfoStub | FilePathWithPrefix,
force: boolean = false, force: boolean = false,
onlyChunks: boolean = false onlyChunks: boolean = false
): Promise<boolean | undefined> { ): Promise<boolean> {
const file = typeof info === "string" ? this.storage.getFileStub(info) : info; const file = typeof info === "string" ? this.storage.getFileStub(info) : info;
if (file == null) { if (file == null) {
this._log(`File ${info} is not exist on the storage`, LOG_LEVEL_VERBOSE); 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<boolean | undefined> { async deleteFileFromDB(info: UXFileInfoStub | UXInternalFileInfoStub | FilePath): Promise<boolean> {
const file = typeof info === "string" ? this.storage.getFileStub(info) : info; const file = typeof info === "string" ? this.storage.getFileStub(info) : info;
if (file == null) { if (file == null) {
this._log(`File ${info} is not exist on the storage`, LOG_LEVEL_VERBOSE); 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 // NO OP
} else { } else {
// If not, then it should be checked. and will be processed later (i.e., after the conflict is resolved). // 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; return true;
} }
} }
@@ -313,11 +313,11 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule {
return ret; return ret;
} }
async $anyHandlerProcessesFileEvent(item: FileEventItem): Promise<boolean | undefined> { private async _anyHandlerProcessesFileEvent(item: FileEventItem): Promise<boolean> {
const eventItem = item.args; const eventItem = item.args;
const type = item.type; const type = item.type;
const path = eventItem.file.path; 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); this._log(`File ${path} is not the target file`, LOG_LEVEL_VERBOSE);
return false; return false;
} }
@@ -343,9 +343,9 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule {
}); });
} }
async $anyProcessReplicatedDoc(entry: MetaEntry): Promise<boolean | undefined> { async _anyProcessReplicatedDoc(entry: MetaEntry): Promise<boolean> {
return await serialized(entry.path, async () => { 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); this._log(`File ${entry.path} is not the target file`, LOG_LEVEL_VERBOSE);
return false; return false;
} }
@@ -362,7 +362,7 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule {
return true; return true;
} else { } else {
this._log( 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 LOG_LEVEL_VERBOSE
); );
// Before writing (or skipped ), merging dialogue should be cancelled. // 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 total = filesStorageSrc.length;
const procAllChunks = filesStorageSrc.map(async (file) => { const procAllChunks = filesStorageSrc.map(async (file) => {
if (!(await this.core.$$isTargetFile(file))) { if (!(await this.services.vault.isTargetFile(file))) {
incProcessed(); incProcessed();
return true; return true;
} }
@@ -416,4 +416,9 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule {
"chunkCreation" "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));
}
} }

View File

@@ -2,18 +2,18 @@ import { $msg } from "../../lib/src/common/i18n";
import { LiveSyncLocalDB } from "../../lib/src/pouchdb/LiveSyncLocalDB.ts"; import { LiveSyncLocalDB } from "../../lib/src/pouchdb/LiveSyncLocalDB.ts";
import { initializeStores } from "../../common/stores.ts"; import { initializeStores } from "../../common/stores.ts";
import { AbstractModule } from "../AbstractModule.ts"; import { AbstractModule } from "../AbstractModule.ts";
import type { ICoreModule } from "../ModuleTypes.ts";
import { LiveSyncManagers } from "../../lib/src/managers/LiveSyncManagers.ts"; import { LiveSyncManagers } from "../../lib/src/managers/LiveSyncManagers.ts";
import type { LiveSyncCore } from "../../main.ts";
export class ModuleLocalDatabaseObsidian extends AbstractModule implements ICoreModule { export class ModuleLocalDatabaseObsidian extends AbstractModule {
$everyOnloadStart(): Promise<boolean> { _everyOnloadStart(): Promise<boolean> {
return Promise.resolve(true); return Promise.resolve(true);
} }
async $$openDatabase(): Promise<boolean> { private async _openDatabase(): Promise<boolean> {
if (this.localDatabase != null) { if (this.localDatabase != null) {
await this.localDatabase.close(); await this.localDatabase.close();
} }
const vaultName = this.core.$$getVaultName(); const vaultName = this.services.vault.getVaultName();
this._log($msg("moduleLocalDatabase.logWaitingForReady")); this._log($msg("moduleLocalDatabase.logWaitingForReady"));
const getDB = () => this.core.localDatabase.localDatabase; const getDB = () => this.core.localDatabase.localDatabase;
const getSettings = () => this.core.settings; const getSettings = () => this.core.settings;
@@ -22,8 +22,9 @@ export class ModuleLocalDatabaseObsidian extends AbstractModule implements ICore
return getDB(); return getDB();
}, },
getActiveReplicator: () => this.core.replicator, getActiveReplicator: () => this.core.replicator,
id2path: this.core.$$id2path.bind(this.core), id2path: this.services.path.id2path,
path2id: this.core.$$path2id.bind(this.core), // path2id: this.core.$$path2id.bind(this.core),
path2id: this.services.path.path2id,
get settings() { get settings() {
return getSettings(); return getSettings();
}, },
@@ -34,7 +35,12 @@ export class ModuleLocalDatabaseObsidian extends AbstractModule implements ICore
return await this.localDatabase.initializeDatabase(); return await this.localDatabase.initializeDatabase();
} }
$$isDatabaseReady(): boolean { _isDatabaseReady(): boolean {
return this.localDatabase != null && this.localDatabase.isReady; 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));
}
} }

View File

@@ -1,9 +1,9 @@
import { PeriodicProcessor } from "../../common/utils"; import { PeriodicProcessor } from "../../common/utils";
import type { LiveSyncCore } from "../../main";
import { AbstractModule } from "../AbstractModule"; import { AbstractModule } from "../AbstractModule";
import type { ICoreModule } from "../ModuleTypes";
export class ModulePeriodicProcess extends AbstractModule implements ICoreModule { export class ModulePeriodicProcess extends AbstractModule {
periodicSyncProcessor = new PeriodicProcessor(this.core, async () => await this.core.$$replicate()); periodicSyncProcessor = new PeriodicProcessor(this.core, async () => await this.services.replication.replicate());
_disablePeriodic() { _disablePeriodic() {
this.periodicSyncProcessor?.disable(); this.periodicSyncProcessor?.disable();
@@ -15,19 +15,27 @@ export class ModulePeriodicProcess extends AbstractModule implements ICoreModule
); );
return Promise.resolve(true); return Promise.resolve(true);
} }
$allOnUnload() { private _allOnUnload() {
return this._disablePeriodic(); return this._disablePeriodic();
} }
$everyBeforeRealizeSetting(): Promise<boolean> { _everyBeforeRealizeSetting(): Promise<boolean> {
return this._disablePeriodic(); return this._disablePeriodic();
} }
$everyBeforeSuspendProcess(): Promise<boolean> { _everyBeforeSuspendProcess(): Promise<boolean> {
return this._disablePeriodic(); return this._disablePeriodic();
} }
$everyAfterResumeProcess(): Promise<boolean> { _everyAfterResumeProcess(): Promise<boolean> {
return this._resumePeriodic(); return this._resumePeriodic();
} }
$everyAfterRealizeSetting(): Promise<boolean> { _everyAfterRealizeSetting(): Promise<boolean> {
return this._resumePeriodic(); 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));
}
} }

View File

@@ -1,9 +1,9 @@
import { AbstractModule } from "../AbstractModule"; import { AbstractModule } from "../AbstractModule";
import type { ICoreModule } from "../ModuleTypes";
import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser"; import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser";
import type { LiveSyncCore } from "../../main";
export class ModulePouchDB extends AbstractModule implements ICoreModule { export class ModulePouchDB extends AbstractModule {
$$createPouchDBInstance<T extends object>( _createPouchDBInstance<T extends object>(
name?: string, name?: string,
options?: PouchDB.Configuration.DatabaseConfiguration options?: PouchDB.Configuration.DatabaseConfiguration
): PouchDB.Database<T> { ): PouchDB.Database<T> {
@@ -16,4 +16,7 @@ export class ModulePouchDB extends AbstractModule implements ICoreModule {
} }
return new PouchDB(name, optionPass); return new PouchDB(name, optionPass);
} }
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
services.database.handleCreatePouchDBInstance(this._createPouchDBInstance.bind(this));
}
} }

View File

@@ -9,13 +9,13 @@ import {
} from "../../lib/src/common/types.ts"; } from "../../lib/src/common/types.ts";
import { AbstractModule } from "../AbstractModule.ts"; import { AbstractModule } from "../AbstractModule.ts";
import type { Rebuilder } from "../interfaces/DatabaseRebuilder.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 type { LiveSyncCouchDBReplicator } from "../../lib/src/replication/couchdb/LiveSyncReplicator.ts";
import { fetchAllUsedChunks } from "@/lib/src/pouchdb/chunks.ts"; import { fetchAllUsedChunks } from "@/lib/src/pouchdb/chunks.ts";
import { EVENT_DATABASE_REBUILT, eventHub } from "src/common/events.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 { export class ModuleRebuilder extends AbstractModule implements Rebuilder {
$everyOnload(): Promise<boolean> { private _everyOnload(): Promise<boolean> {
this.core.rebuilder = this; this.core.rebuilder = this;
return Promise.resolve(true); return Promise.resolve(true);
} }
@@ -43,47 +43,47 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
{ title: "Enable extra features", defaultOption: "No", timeout: 15 } { title: "Enable extra features", defaultOption: "No", timeout: 15 }
)) == "yes" )) == "yes"
) { ) {
await this.core.$allAskUsingOptionalSyncFeature(opt); await this.services.setting.suggestOptionalFeatures(opt);
} }
} }
async rebuildRemote() { async rebuildRemote() {
await this.core.$allSuspendExtraSync(); await this.services.setting.suspendExtraSync();
this.core.settings.isConfigured = true; this.core.settings.isConfigured = true;
await this.core.$$realizeSettingSyncMode(); await this.services.setting.onRealiseSetting();
await this.core.$$markRemoteLocked(); await this.services.remote.markLocked();
await this.core.$$tryResetRemoteDatabase(); await this.services.remote.tryResetDatabase();
await this.core.$$markRemoteLocked(); await this.services.remote.markLocked();
await delay(500); await delay(500);
await this.askUsingOptionalFeature({ enableOverwrite: true }); await this.askUsingOptionalFeature({ enableOverwrite: true });
await delay(1000); await delay(1000);
await this.core.$$replicateAllToServer(true); await this.services.remote.replicateAllToRemote(true);
await delay(1000); await delay(1000);
await this.core.$$replicateAllToServer(true, true); await this.services.remote.replicateAllToRemote(true, true);
} }
$rebuildRemote(): Promise<void> { $rebuildRemote(): Promise<void> {
return this.rebuildRemote(); return this.rebuildRemote();
} }
async rebuildEverything() { async rebuildEverything() {
await this.core.$allSuspendExtraSync(); await this.services.setting.suspendExtraSync();
await this.askUseNewAdapter(); await this.askUseNewAdapter();
this.core.settings.isConfigured = true; this.core.settings.isConfigured = true;
await this.core.$$realizeSettingSyncMode(); await this.services.setting.onRealiseSetting();
await this.resetLocalDatabase(); await this.resetLocalDatabase();
await delay(1000); await delay(1000);
await this.core.$$initializeDatabase(true, true, true); await this.services.databaseEvents.initialiseDatabase(true, true, true);
await this.core.$$markRemoteLocked(); await this.services.remote.markLocked();
await this.core.$$tryResetRemoteDatabase(); await this.services.remote.tryResetDatabase();
await this.core.$$markRemoteLocked(); await this.services.remote.markLocked();
await delay(500); await delay(500);
// We do not have any other devices' data, so we do not need to ask for overwriting. // We do not have any other devices' data, so we do not need to ask for overwriting.
await this.askUsingOptionalFeature({ enableOverwrite: false }); await this.askUsingOptionalFeature({ enableOverwrite: false });
await delay(1000); await delay(1000);
await this.core.$$replicateAllToServer(true); await this.services.remote.replicateAllToRemote(true);
await delay(1000); await delay(1000);
await this.core.$$replicateAllToServer(true, true); await this.services.remote.replicateAllToRemote(true, true);
} }
$rebuildEverything(): Promise<void> { $rebuildEverything(): Promise<void> {
@@ -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("Could not create red_flag_rebuild.md", LOG_LEVEL_NOTICE);
this._log(ex, LOG_LEVEL_VERBOSE); this._log(ex, LOG_LEVEL_VERBOSE);
} }
this.core.$$performRestart(); this.services.appLifecycle.performRestart();
} }
async scheduleFetch(): Promise<void> { async scheduleFetch(): Promise<void> {
try { 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("Could not create red_flag_fetch.md", LOG_LEVEL_NOTICE);
this._log(ex, LOG_LEVEL_VERBOSE); this._log(ex, LOG_LEVEL_VERBOSE);
} }
this.core.$$performRestart(); this.services.appLifecycle.performRestart();
} }
async $$tryResetRemoteDatabase(): Promise<void> { private async _tryResetRemoteDatabase(): Promise<void> {
await this.core.replicator.tryResetRemoteDatabase(this.settings); await this.core.replicator.tryResetRemoteDatabase(this.settings);
} }
async $$tryCreateRemoteDatabase(): Promise<void> { private async _tryCreateRemoteDatabase(): Promise<void> {
await this.core.replicator.tryCreateRemoteDatabase(this.settings); await this.core.replicator.tryCreateRemoteDatabase(this.settings);
} }
async $$resetLocalDatabase(): Promise<void> { private async _resetLocalDatabase(): Promise<boolean> {
this.core.storageAccess.clearTouched(); this.core.storageAccess.clearTouched();
await this.localDatabase.resetDatabase(); return await this.localDatabase.resetDatabase();
} }
async suspendAllSync() { async suspendAllSync() {
@@ -134,7 +134,7 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
this.core.settings.syncOnStart = false; this.core.settings.syncOnStart = false;
this.core.settings.syncOnFileOpen = false; this.core.settings.syncOnFileOpen = false;
this.core.settings.syncAfterMerge = false; this.core.settings.syncAfterMerge = false;
await this.core.$allSuspendExtraSync(); await this.services.setting.suspendExtraSync();
} }
async suspendReflectingDatabase() { async suspendReflectingDatabase() {
if (this.core.settings.doNotSuspendOnFetching) return; 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._log(`Database and storage reflection has been resumed!`, LOG_LEVEL_NOTICE);
this.core.settings.suspendParseReplicationResult = false; this.core.settings.suspendParseReplicationResult = false;
this.core.settings.suspendFileWatching = false; this.core.settings.suspendFileWatching = false;
await this.core.$$performFullScan(true); await this.services.vault.scanVault(true);
await this.core.$everyBeforeReplicate(false); //TODO: Check actual need of this. await this.services.replication.onBeforeReplicate(false); //TODO: Check actual need of this.
await this.core.saveSettings(); await this.core.saveSettings();
} }
async askUseNewAdapter() { async askUseNewAdapter() {
@@ -177,28 +177,28 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
} }
} }
async fetchLocal(makeLocalChunkBeforeSync?: boolean, preventMakeLocalFilesBeforeSync?: boolean) { async fetchLocal(makeLocalChunkBeforeSync?: boolean, preventMakeLocalFilesBeforeSync?: boolean) {
await this.core.$allSuspendExtraSync(); await this.services.setting.suspendExtraSync();
await this.askUseNewAdapter(); await this.askUseNewAdapter();
this.core.settings.isConfigured = true; this.core.settings.isConfigured = true;
await this.suspendReflectingDatabase(); await this.suspendReflectingDatabase();
await this.core.$$realizeSettingSyncMode(); await this.services.setting.onRealiseSetting();
await this.resetLocalDatabase(); await this.resetLocalDatabase();
await delay(1000); await delay(1000);
await this.core.$$openDatabase(); await this.services.database.openDatabase();
// this.core.isReady = true; // this.core.isReady = true;
this.core.$$markIsReady(); this.services.appLifecycle.markIsReady();
if (makeLocalChunkBeforeSync) { if (makeLocalChunkBeforeSync) {
await this.core.fileHandler.createAllChunks(true); await this.core.fileHandler.createAllChunks(true);
} else if (!preventMakeLocalFilesBeforeSync) { } else if (!preventMakeLocalFilesBeforeSync) {
await this.core.$$initializeDatabase(true, true, true); await this.services.databaseEvents.initialiseDatabase(true, true, true);
} else { } else {
// Do not create local file entries before sync (Means use remote information) // 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 delay(500);
await this.core.$$replicateAllFromServer(true); await this.services.remote.replicateAllFromRemote(true);
await delay(1000); await delay(1000);
await this.core.$$replicateAllFromServer(true); await this.services.remote.replicateAllFromRemote(true);
await this.resumeReflectingDatabase(); await this.resumeReflectingDatabase();
await this.askUsingOptionalFeature({ enableFetch: true }); await this.askUsingOptionalFeature({ enableFetch: true });
} }
@@ -206,7 +206,7 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
return await this.fetchLocal(true); return await this.fetchLocal(true);
} }
async $allSuspendAllSync(): Promise<boolean> { private async _allSuspendAllSync(): Promise<boolean> {
await this.suspendAllSync(); await this.suspendAllSync();
return true; return true;
} }
@@ -214,11 +214,11 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
async resetLocalDatabase() { async resetLocalDatabase() {
if (this.core.settings.isConfigured && this.core.settings.additionalSuffixOfDatabaseName == "") { if (this.core.settings.isConfigured && this.core.settings.additionalSuffixOfDatabaseName == "") {
// Discard the non-suffixed database // 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; this.core.settings.additionalSuffixOfDatabaseName = suffix;
await this.core.$$resetLocalDatabase(); await this.services.database.resetDatabase();
eventHub.emitEvent(EVENT_DATABASE_REBUILT); eventHub.emitEvent(EVENT_DATABASE_REBUILT);
} }
async fetchRemoteChunks() { async fetchRemoteChunks() {
@@ -228,10 +228,10 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
this.core.settings.remoteType == REMOTE_COUCHDB this.core.settings.remoteType == REMOTE_COUCHDB
) { ) {
this._log(`Fetching chunks`, LOG_LEVEL_NOTICE); 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( const remoteDB = await replicator.connectRemoteCouchDBWithSetting(
this.settings, this.settings,
this.core.$$isMobile(), this.services.API.isMobile(),
true true
); );
if (typeof remoteDB == "string") { if (typeof remoteDB == "string") {
@@ -254,8 +254,15 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
LOG_LEVEL_NOTICE, LOG_LEVEL_NOTICE,
"resolveAllConflictedFilesByNewerOnes" "resolveAllConflictedFilesByNewerOnes"
); );
await this.core.$anyResolveConflictByNewest(file); await this.services.conflict.resolveByNewest(file);
} }
this._log(`Done!`, LOG_LEVEL_NOTICE, "resolveAllConflictedFilesByNewerOnes"); 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));
}
} }

View File

@@ -1,7 +1,6 @@
import { fireAndForget, yieldMicrotask } from "octagonal-wheels/promises"; import { fireAndForget, yieldMicrotask } from "octagonal-wheels/promises";
import type { LiveSyncLocalDB } from "../../lib/src/pouchdb/LiveSyncLocalDB"; import type { LiveSyncLocalDB } from "../../lib/src/pouchdb/LiveSyncLocalDB";
import { AbstractModule } from "../AbstractModule"; 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 { 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 { isLockAcquired, shareRunningResult, skipIfDuplicated } from "octagonal-wheels/concurrency/lock";
import { balanceChunkPurgedDBs } from "@/lib/src/pouchdb/chunks"; 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 { $msg } from "../../lib/src/common/i18n";
import { clearHandlers } from "../../lib/src/replication/SyncParamsHandler"; import { clearHandlers } from "../../lib/src/replication/SyncParamsHandler";
import type { LiveSyncCore } from "../../main";
const KEY_REPLICATION_ON_EVENT = "replicationOnEvent"; const KEY_REPLICATION_ON_EVENT = "replicationOnEvent";
const REPLICATION_ON_EVENT_FORECASTED_TIME = 5000; const REPLICATION_ON_EVENT_FORECASTED_TIME = 5000;
export class ModuleReplicator extends AbstractModule implements ICoreModule { export class ModuleReplicator extends AbstractModule {
_replicatorType?: RemoteType; _replicatorType?: RemoteType;
$everyOnloadAfterLoadSettings(): Promise<boolean> { private _everyOnloadAfterLoadSettings(): Promise<boolean> {
eventHub.onEvent(EVENT_FILE_SAVED, () => { eventHub.onEvent(EVENT_FILE_SAVED, () => {
if (this.settings.syncOnSave && !this.core.$$isSuspended()) { if (this.settings.syncOnSave && !this.core.services.appLifecycle.isSuspended()) {
scheduleTask("perform-replicate-after-save", 250, () => this.core.$$replicateByEvent()); scheduleTask("perform-replicate-after-save", 250, () => this.services.replication.replicateByEvent());
} }
}); });
eventHub.onEvent(EVENT_SETTING_SAVED, (setting) => { eventHub.onEvent(EVENT_SETTING_SAVED, (setting) => {
@@ -57,7 +57,7 @@ export class ModuleReplicator extends AbstractModule implements ICoreModule {
} }
async setReplicator() { async setReplicator() {
const replicator = await this.core.$anyNewReplicator(); const replicator = await this.services.replicator.getNewReplicator();
if (!replicator) { if (!replicator) {
this._log($msg("Replicator.Message.InitialiseFatalError"), LOG_LEVEL_NOTICE); this._log($msg("Replicator.Message.InitialiseFatalError"), LOG_LEVEL_NOTICE);
return false; return false;
@@ -74,24 +74,28 @@ export class ModuleReplicator extends AbstractModule implements ICoreModule {
return true; return true;
} }
$$getReplicator(): LiveSyncAbstractReplicator { _getReplicator(): LiveSyncAbstractReplicator {
return this.core.replicator; return this.core.replicator;
} }
$everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise<boolean> { _everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise<boolean> {
return this.setReplicator(); return this.setReplicator();
} }
$everyOnResetDatabase(db: LiveSyncLocalDB): Promise<boolean> { _everyOnResetDatabase(db: LiveSyncLocalDB): Promise<boolean> {
return this.setReplicator(); return this.setReplicator();
} }
async ensureReplicatorPBKDF2Salt(showMessage: boolean = false): Promise<boolean> { async ensureReplicatorPBKDF2Salt(showMessage: boolean = false): Promise<boolean> {
// Checking salt // 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); return await replicator.ensurePBKDF2Salt(this.settings, showMessage, true);
} }
async $everyBeforeReplicate(showMessage: boolean): Promise<boolean> { async _everyBeforeReplicate(showMessage: boolean): Promise<boolean> {
// Checking salt // Checking salt
if (!this.core.managers.networkManager.isOnline) { if (!this.core.managers.networkManager.isOnline) {
this._log("Network is offline", showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO); 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; return true;
} }
async $$replicate(showMessage: boolean = false): Promise<boolean | void> { private async _replicate(showMessage: boolean = false): Promise<boolean | void> {
try { try {
updatePreviousExecutionTime(KEY_REPLICATION_ON_EVENT, REPLICATION_ON_EVENT_FORECASTED_TIME); updatePreviousExecutionTime(KEY_REPLICATION_ON_EVENT, REPLICATION_ON_EVENT_FORECASTED_TIME);
return await this.$$_replicate(showMessage); 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"); await this.core.rebuilder.$performRebuildDB("localOnly");
} }
if (ret == CHOICE_CLEAN) { if (ret == CHOICE_CLEAN) {
const replicator = this.core.$$getReplicator(); const replicator = this.services.replicator.getActiveReplicator();
if (!(replicator instanceof LiveSyncCouchDBReplicator)) return; if (!(replicator instanceof LiveSyncCouchDBReplicator)) return;
const remoteDB = await replicator.connectRemoteCouchDBWithSetting( const remoteDB = await replicator.connectRemoteCouchDBWithSetting(
this.settings, this.settings,
this.core.$$isMobile(), this.services.API.isMobile(),
true true
); );
if (typeof remoteDB == "string") { 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 balanceChunkPurgedDBs(this.localDatabase.localDatabase, remoteDB.db);
await purgeUnreferencedChunks(this.localDatabase.localDatabase, false); await purgeUnreferencedChunks(this.localDatabase.localDatabase, false);
this.localDatabase.clearCaches(); 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); Logger("The local database has been cleaned up.", showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO);
} else { } else {
Logger( 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<boolean> { async _canReplicate(showMessage: boolean = false): Promise<boolean> {
if (!this.core.$$isReady()) { if (!this.services.appLifecycle.isReady()) {
Logger(`Not ready`); Logger(`Not ready`);
return false; 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; return false;
} }
if (!(await this.core.$everyCommitPendingFileEvent())) { if (!(await this.services.fileProcessing.commitPendingFileEvents())) {
Logger($msg("Replicator.Message.Pending"), LOG_LEVEL_NOTICE); Logger($msg("Replicator.Message.Pending"), LOG_LEVEL_NOTICE);
return false; 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); this._log("Network is offline", showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO);
return false; return false;
} }
if (!(await this.core.$everyBeforeReplicate(showMessage))) { if (!(await this.services.replication.onBeforeReplicate(showMessage))) {
Logger($msg("Replicator.Message.SomeModuleFailed"), LOG_LEVEL_NOTICE); Logger($msg("Replicator.Message.SomeModuleFailed"), LOG_LEVEL_NOTICE);
return false; 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<boolean | void> { async $$_replicate(showMessage: boolean = false): Promise<boolean | void> {
const checkBeforeReplicate = await this.$$canReplicate(showMessage); const checkBeforeReplicate = await this.services.replication.isReplicationReady(showMessage);
if (!checkBeforeReplicate) return false; if (!checkBeforeReplicate) return false;
//<-- Here could be an module. //<-- Here could be an module.
const ret = await this.core.replicator.openReplication(this.settings, false, showMessage, false); const ret = await this.core.replicator.openReplication(this.settings, false, showMessage, false);
if (!ret) { if (!ret) {
if (this.core.replicator.tweakSettingsMismatched && this.core.replicator.preferredTweakValue) { 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 { } else {
if (this.core.replicator?.remoteLockedAndDeviceNotAccepted) { if (this.core.replicator?.remoteLockedAndDeviceNotAccepted) {
if (this.core.replicator.remoteCleaned && this.settings.useIndexedDBAdapter) { 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) { if (ret == CHOICE_FETCH) {
this._log($msg("Replicator.Dialogue.Locked.Message.Fetch"), LOG_LEVEL_NOTICE); this._log($msg("Replicator.Dialogue.Locked.Message.Fetch"), LOG_LEVEL_NOTICE);
await this.core.rebuilder.scheduleFetch(); await this.core.rebuilder.scheduleFetch();
this.core.$$scheduleAppReload(); this.services.appLifecycle.scheduleRestart();
return; return;
} else if (ret == CHOICE_UNLOCK) { } else if (ret == CHOICE_UNLOCK) {
await this.core.replicator.markRemoteResolved(this.settings); 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; return ret;
} }
async $$replicateByEvent(): Promise<boolean | void> { private async _replicateByEvent(): Promise<boolean | void> {
const least = this.settings.syncMinimumInterval; const least = this.settings.syncMinimumInterval;
if (least > 0) { if (least > 0) {
return rateLimitedSharedExecution(KEY_REPLICATION_ON_EVENT, least, async () => { 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<PouchDB.Core.ExistingDocument<EntryDoc>>): void { _parseReplicationResult(docs: Array<PouchDB.Core.ExistingDocument<EntryDoc>>): void {
if (this.settings.suspendParseReplicationResult && !this.replicationResultProcessor.isSuspended) { if (this.settings.suspendParseReplicationResult && !this.replicationResultProcessor.isSuspended) {
this.replicationResultProcessor.suspend(); 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); this.localDatabase.onNewLeaf(change as EntryLeaf);
return; return;
} }
if (await this.core.$anyModuleParsedReplicationResultItem(change)) return; if (await this.services.replication.processVirtualDocument(change)) return;
// any addon needs this item? // any addon needs this item?
// for (const proc of this.core.addOns) { // for (const proc of this.core.addOns) {
// if (await proc.parseReplicationResultItem(change)) { // 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)) { if (isAnyNote(change)) {
const docPath = getPath(change); const docPath = getPath(change);
if (!(await this.core.$$isTargetFile(docPath))) { if (!(await this.services.vault.isTargetFile(docPath))) {
Logger(`Skipped: ${docPath}`, LOG_LEVEL_VERBOSE); Logger(`Skipped: ${docPath}`, LOG_LEVEL_VERBOSE);
return; 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); Logger(`Processing scheduled: ${docPath}`, LOG_LEVEL_INFO);
} }
const size = change.size; const size = change.size;
if (this.core.$$isFileSizeExceeded(size)) { if (this.services.vault.isFileSizeTooLarge(size)) {
Logger( Logger(
`Processing ${docPath} has been skipped due to file size exceeding the limit`, `Processing ${docPath} has been skipped due to file size exceeding the limit`,
LOG_LEVEL_NOTICE 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; return;
} }
if (await this.core.$anyProcessOptionalSyncFiles(dbDoc)) { if (await this.services.replication.processOptionalSynchroniseResult(dbDoc)) {
// Already processed // Already processed
} else if (isValidPath(getPath(doc))) { } else if (isValidPath(getPath(doc))) {
this.storageApplyingProcessor.enqueue(doc as MetaEntry); 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( storageApplyingProcessor = new QueueProcessor(
async (docs: MetaEntry[]) => { async (docs: MetaEntry[]) => {
const entry = docs[0]; const entry = docs[0];
await this.core.$anyProcessReplicatedDoc(entry); await this.services.replication.processSynchroniseResult(entry);
return; return;
}, },
{ {
@@ -458,17 +462,17 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
}) })
.startPipeline(); .startPipeline();
$everyBeforeSuspendProcess(): Promise<boolean> { _everyBeforeSuspendProcess(): Promise<boolean> {
this.core.replicator.closeReplication(); this.core.replicator?.closeReplication();
return Promise.resolve(true); return Promise.resolve(true);
} }
async $$replicateAllToServer( private async _replicateAllToServer(
showingNotice: boolean = false, showingNotice: boolean = false,
sendChunksInBulkDisabled: boolean = false sendChunksInBulkDisabled: boolean = false
): Promise<boolean> { ): Promise<boolean> {
if (!this.core.$$isReady()) return false; if (!this.services.appLifecycle.isReady()) return false;
if (!(await this.core.$everyBeforeReplicate(showingNotice))) { if (!(await this.services.replication.onBeforeReplicate(showingNotice))) {
Logger($msg("Replicator.Message.SomeModuleFailed"), LOG_LEVEL_NOTICE); Logger($msg("Replicator.Message.SomeModuleFailed"), LOG_LEVEL_NOTICE);
return false; 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); const ret = await this.core.replicator.replicateAllToServer(this.settings, showingNotice);
if (ret) return true; if (ret) return true;
const checkResult = await this.core.$anyAfterConnectCheckFailed(); const checkResult = await this.services.replication.checkConnectionFailure();
if (checkResult == "CHECKAGAIN") return await this.core.$$replicateAllToServer(showingNotice); if (checkResult == "CHECKAGAIN") return await this.services.remote.replicateAllToRemote(showingNotice);
return !checkResult; return !checkResult;
} }
async $$replicateAllFromServer(showingNotice: boolean = false): Promise<boolean> { async _replicateAllFromServer(showingNotice: boolean = false): Promise<boolean> {
if (!this.core.$$isReady()) return false; if (!this.services.appLifecycle.isReady()) return false;
const ret = await this.core.replicator.replicateAllFromServer(this.settings, showingNotice); const ret = await this.core.replicator.replicateAllFromServer(this.settings, showingNotice);
if (ret) return true; if (ret) return true;
const checkResult = await this.core.$anyAfterConnectCheckFailed(); const checkResult = await this.services.replication.checkConnectionFailure();
if (checkResult == "CHECKAGAIN") return await this.core.$$replicateAllFromServer(showingNotice); if (checkResult == "CHECKAGAIN") return await this.services.remote.replicateAllFromRemote(showingNotice);
return !checkResult; 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));
}
} }

View File

@@ -3,20 +3,20 @@ import { REMOTE_MINIO, REMOTE_P2P, type RemoteDBSettings } from "../../lib/src/c
import { LiveSyncCouchDBReplicator } from "../../lib/src/replication/couchdb/LiveSyncReplicator"; import { LiveSyncCouchDBReplicator } from "../../lib/src/replication/couchdb/LiveSyncReplicator";
import type { LiveSyncAbstractReplicator } from "../../lib/src/replication/LiveSyncAbstractReplicator"; import type { LiveSyncAbstractReplicator } from "../../lib/src/replication/LiveSyncAbstractReplicator";
import { AbstractModule } from "../AbstractModule"; import { AbstractModule } from "../AbstractModule";
import type { ICoreModule } from "../ModuleTypes"; import type { LiveSyncCore } from "../../main";
export class ModuleReplicatorCouchDB extends AbstractModule implements ICoreModule { export class ModuleReplicatorCouchDB extends AbstractModule {
$anyNewReplicator(settingOverride: Partial<RemoteDBSettings> = {}): Promise<LiveSyncAbstractReplicator> { _anyNewReplicator(settingOverride: Partial<RemoteDBSettings> = {}): Promise<LiveSyncAbstractReplicator | false> {
const settings = { ...this.settings, ...settingOverride }; 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 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) { if (settings.remoteType == REMOTE_MINIO || settings.remoteType == REMOTE_P2P) {
return undefined!; return Promise.resolve(false);
} }
return Promise.resolve(new LiveSyncCouchDBReplicator(this.core)); return Promise.resolve(new LiveSyncCouchDBReplicator(this.core));
} }
$everyAfterResumeProcess(): Promise<boolean> { _everyAfterResumeProcess(): Promise<boolean> {
if (!this.core.$$isSuspended) return Promise.resolve(true); if (!this.services.appLifecycle.isSuspended()) return Promise.resolve(true);
if (!this.core.$$isReady) return Promise.resolve(true); if (!this.services.appLifecycle.isReady()) return Promise.resolve(true);
if (this.settings.remoteType != REMOTE_MINIO && this.settings.remoteType != REMOTE_P2P) { if (this.settings.remoteType != REMOTE_MINIO && this.settings.remoteType != REMOTE_P2P) {
const LiveSyncEnabled = this.settings.liveSync; const LiveSyncEnabled = this.settings.liveSync;
const continuous = LiveSyncEnabled; 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. // And note that we do not open the conflict detection dialogue directly during this process.
// This should be raised explicitly if needed. // This should be raised explicitly if needed.
fireAndForget(async () => { fireAndForget(async () => {
const canReplicate = await this.core.$$canReplicate(false); const canReplicate = await this.services.replication.isReplicationReady(false);
if (!canReplicate) return; if (!canReplicate) return;
void this.core.replicator.openReplication(this.settings, continuous, false, false); 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); 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));
}
} }

View File

@@ -1,15 +1,18 @@
import { REMOTE_MINIO, type RemoteDBSettings } from "../../lib/src/common/types"; import { REMOTE_MINIO, type RemoteDBSettings } from "../../lib/src/common/types";
import { LiveSyncJournalReplicator } from "../../lib/src/replication/journal/LiveSyncJournalReplicator"; import { LiveSyncJournalReplicator } from "../../lib/src/replication/journal/LiveSyncJournalReplicator";
import type { LiveSyncAbstractReplicator } from "../../lib/src/replication/LiveSyncAbstractReplicator"; import type { LiveSyncAbstractReplicator } from "../../lib/src/replication/LiveSyncAbstractReplicator";
import type { LiveSyncCore } from "../../main";
import { AbstractModule } from "../AbstractModule"; import { AbstractModule } from "../AbstractModule";
import type { ICoreModule } from "../ModuleTypes";
export class ModuleReplicatorMinIO extends AbstractModule implements ICoreModule { export class ModuleReplicatorMinIO extends AbstractModule {
$anyNewReplicator(settingOverride: Partial<RemoteDBSettings> = {}): Promise<LiveSyncAbstractReplicator> { _anyNewReplicator(settingOverride: Partial<RemoteDBSettings> = {}): Promise<LiveSyncAbstractReplicator | false> {
const settings = { ...this.settings, ...settingOverride }; const settings = { ...this.settings, ...settingOverride };
if (settings.remoteType == REMOTE_MINIO) { if (settings.remoteType == REMOTE_MINIO) {
return Promise.resolve(new LiveSyncJournalReplicator(this.core)); 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));
} }
} }

View File

@@ -1,18 +1,18 @@
import { REMOTE_P2P, type RemoteDBSettings } from "../../lib/src/common/types"; import { REMOTE_P2P, type RemoteDBSettings } from "../../lib/src/common/types";
import type { LiveSyncAbstractReplicator } from "../../lib/src/replication/LiveSyncAbstractReplicator"; import type { LiveSyncAbstractReplicator } from "../../lib/src/replication/LiveSyncAbstractReplicator";
import { AbstractModule } from "../AbstractModule"; import { AbstractModule } from "../AbstractModule";
import type { ICoreModule } from "../ModuleTypes";
import { LiveSyncTrysteroReplicator } from "../../lib/src/replication/trystero/LiveSyncTrysteroReplicator"; import { LiveSyncTrysteroReplicator } from "../../lib/src/replication/trystero/LiveSyncTrysteroReplicator";
import type { LiveSyncCore } from "../../main";
export class ModuleReplicatorP2P extends AbstractModule implements ICoreModule { export class ModuleReplicatorP2P extends AbstractModule {
$anyNewReplicator(settingOverride: Partial<RemoteDBSettings> = {}): Promise<LiveSyncAbstractReplicator> { _anyNewReplicator(settingOverride: Partial<RemoteDBSettings> = {}): Promise<LiveSyncAbstractReplicator | false> {
const settings = { ...this.settings, ...settingOverride }; const settings = { ...this.settings, ...settingOverride };
if (settings.remoteType == REMOTE_P2P) { if (settings.remoteType == REMOTE_P2P) {
return Promise.resolve(new LiveSyncTrysteroReplicator(this.core)); return Promise.resolve(new LiveSyncTrysteroReplicator(this.core));
} }
return undefined!; return Promise.resolve(false);
} }
$everyAfterResumeProcess(): Promise<boolean> { _everyAfterResumeProcess(): Promise<boolean> {
if (this.settings.remoteType == REMOTE_P2P) { if (this.settings.remoteType == REMOTE_P2P) {
// // If LiveSync enabled, open replication // // If LiveSync enabled, open replication
// if (this.settings.liveSync) { // if (this.settings.liveSync) {
@@ -27,4 +27,8 @@ export class ModuleReplicatorP2P extends AbstractModule implements ICoreModule {
return Promise.resolve(true); 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));
}
} }

View File

@@ -18,14 +18,14 @@ import {
} from "../../lib/src/common/types"; } from "../../lib/src/common/types";
import { addPrefix, isAcceptedAll } from "../../lib/src/string_and_binary/path"; import { addPrefix, isAcceptedAll } from "../../lib/src/string_and_binary/path";
import { AbstractModule } from "../AbstractModule"; import { AbstractModule } from "../AbstractModule";
import type { ICoreModule } from "../ModuleTypes";
import { EVENT_REQUEST_RELOAD_SETTING_TAB, EVENT_SETTING_SAVED, eventHub } from "../../common/events"; import { EVENT_REQUEST_RELOAD_SETTING_TAB, EVENT_SETTING_SAVED, eventHub } from "../../common/events";
import { isDirty } from "../../lib/src/common/utils"; 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() { reloadIgnoreFiles() {
this.ignoreFiles = this.settings.ignoreFiles.split(",").map((e) => e.trim()); this.ignoreFiles = this.settings.ignoreFiles.split(",").map((e) => e.trim());
} }
$everyOnload(): Promise<boolean> { private _everyOnload(): Promise<boolean> {
eventHub.onEvent(EVENT_SETTING_SAVED, (evt: ObsidianLiveSyncSettings) => { eventHub.onEvent(EVENT_SETTING_SAVED, (evt: ObsidianLiveSyncSettings) => {
this.reloadIgnoreFiles(); this.reloadIgnoreFiles();
}); });
@@ -35,7 +35,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
return Promise.resolve(true); 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); const tempId = id2path(id, entry);
if (stripPrefix && isInternalMetadata(tempId)) { if (stripPrefix && isInternalMetadata(tempId)) {
const out = stripInternalMetadataPrefix(tempId); const out = stripInternalMetadataPrefix(tempId);
@@ -43,7 +43,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
} }
return tempId; return tempId;
} }
async $$path2id(filename: FilePathWithPrefix | FilePath, prefix?: string): Promise<DocumentID> { async _path2id(filename: FilePathWithPrefix | FilePath, prefix?: string): Promise<DocumentID> {
const destPath = addPrefix(filename, prefix ?? ""); const destPath = addPrefix(filename, prefix ?? "");
return await path2id( return await path2id(
destPath, 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 > 0 && size > 0) {
if (this.settings.syncMaxSizeInMB * 1024 * 1024 < size) { if (this.settings.syncMaxSizeInMB * 1024 * 1024 < size) {
return true; return true;
@@ -61,7 +61,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
return false; return false;
} }
$$markFileListPossiblyChanged(): void { _markFileListPossiblyChanged(): void {
this.totalFileEventCount++; this.totalFileEventCount++;
} }
totalFileEventCount = 0; totalFileEventCount = 0;
@@ -72,7 +72,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
return false; return false;
} }
async $$isTargetFile(file: string | UXFileInfoStub, keepFileCheckList = false) { private async _isTargetFile(file: string | UXFileInfoStub, keepFileCheckList = false) {
const fileCount = useMemo<Record<string, number>>( const fileCount = useMemo<Record<string, number>>(
{ {
key: "fileCount", // forceUpdate: !keepFileCheckList, key: "fileCount", // forceUpdate: !keepFileCheckList,
@@ -109,7 +109,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
const filepath = getStoragePathFromUXFileInfo(file); const filepath = getStoragePathFromUXFileInfo(file);
const lc = filepath.toLowerCase(); const lc = filepath.toLowerCase();
if (this.core.$$shouldCheckCaseInsensitive()) { if (this.services.setting.shouldCheckCaseInsensitively()) {
if (lc in fileCount && fileCount[lc] > 1) { if (lc in fileCount && fileCount[lc] > 1) {
return false; return false;
} }
@@ -120,7 +120,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
// We must reload ignore files due to the its change. // We must reload ignore files due to the its change.
await this.readIgnoreFile(filepath); await this.readIgnoreFile(filepath);
} }
if (await this.core.$$isIgnoredByIgnoreFiles(file)) { if (await this.services.vault.isIgnoredByIgnoreFile(file)) {
return false; return false;
} }
} }
@@ -150,7 +150,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
return await this.readIgnoreFile(path); return await this.readIgnoreFile(path);
} }
} }
async $$isIgnoredByIgnoreFiles(file: string | UXFileInfoStub): Promise<boolean> { private async _isIgnoredByIgnoreFiles(file: string | UXFileInfoStub): Promise<boolean> {
if (!this.settings.useIgnoreFiles) { if (!this.settings.useIgnoreFiles) {
return false; return false;
} }
@@ -164,4 +164,14 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
} }
return false; 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));
}
} }

View File

@@ -1,11 +1,11 @@
import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger"; import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
import { AbstractModule } from "../AbstractModule.ts"; import { AbstractModule } from "../AbstractModule.ts";
import { sizeToHumanReadable } from "octagonal-wheels/number"; import { sizeToHumanReadable } from "octagonal-wheels/number";
import type { ICoreModule } from "../ModuleTypes.ts";
import { $msg } from "src/lib/src/common/i18n.ts"; import { $msg } from "src/lib/src/common/i18n.ts";
import type { LiveSyncCore } from "../../main.ts";
export class ModuleCheckRemoteSize extends AbstractModule implements ICoreModule { export class ModuleCheckRemoteSize extends AbstractModule {
async $allScanStat(): Promise<boolean> { async _allScanStat(): Promise<boolean> {
if (this.core.managers.networkManager.isOnline === false) { if (this.core.managers.networkManager.isOnline === false) {
this._log("Network is offline, skipping remote size check.", LOG_LEVEL_INFO); this._log("Network is offline, skipping remote size check.", LOG_LEVEL_INFO);
return true; return true;
@@ -109,4 +109,7 @@ export class ModuleCheckRemoteSize extends AbstractModule implements ICoreModule
} }
return true; return true;
} }
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
services.appLifecycle.handleOnScanningStartupIssues(this._allScanStat.bind(this));
}
} }

View File

@@ -2,35 +2,36 @@ import { AbstractModule } from "../AbstractModule.ts";
import { LOG_LEVEL_NOTICE, type FilePathWithPrefix } from "../../lib/src/common/types"; import { LOG_LEVEL_NOTICE, type FilePathWithPrefix } from "../../lib/src/common/types";
import { QueueProcessor } from "octagonal-wheels/concurrency/processor"; import { QueueProcessor } from "octagonal-wheels/concurrency/processor";
import { sendValue } from "octagonal-wheels/messagepassing/signal"; 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 { export class ModuleConflictChecker extends AbstractModule {
async $$queueConflictCheckIfOpen(file: FilePathWithPrefix): Promise<void> { async _queueConflictCheckIfOpen(file: FilePathWithPrefix): Promise<void> {
const path = file; const path = file;
if (this.settings.checkConflictOnlyOnOpen) { if (this.settings.checkConflictOnlyOnOpen) {
const af = this.core.$$getActiveFilePath(); const af = this.services.vault.getActiveFilePath();
if (af && af != path) { if (af && af != path) {
this._log(`${file} is conflicted, merging process has been postponed.`, LOG_LEVEL_NOTICE); this._log(`${file} is conflicted, merging process has been postponed.`, LOG_LEVEL_NOTICE);
return; return;
} }
} }
await this.core.$$queueConflictCheck(path); await this.services.conflict.queueCheckFor(path);
} }
async $$queueConflictCheck(file: FilePathWithPrefix): Promise<void> { async _queueConflictCheck(file: FilePathWithPrefix): Promise<void> {
const optionalConflictResult = await this.core.$anyGetOptionalConflictCheckMethod(file); const optionalConflictResult = await this.services.conflict.getOptionalConflictCheckMethod(file);
if (optionalConflictResult == true) { if (optionalConflictResult == true) {
// The conflict has been resolved by another process. // The conflict has been resolved by another process.
return; return;
} else if (optionalConflictResult === "newer") { } else if (optionalConflictResult === "newer") {
// The conflict should be resolved by the newer entry. // The conflict should be resolved by the newer entry.
await this.core.$anyResolveConflictByNewest(file); await this.services.conflict.resolveByNewest(file);
} else { } else {
this.conflictCheckQueue.enqueue(file); this.conflictCheckQueue.enqueue(file);
} }
} }
$$waitForAllConflictProcessed(): Promise<boolean> { _waitForAllConflictProcessed(): Promise<boolean> {
return this.conflictResolveQueue.waitForAllProcessed(); return this.conflictResolveQueue.waitForAllProcessed();
} }
@@ -38,7 +39,7 @@ export class ModuleConflictChecker extends AbstractModule implements ICoreModule
conflictResolveQueue = new QueueProcessor( conflictResolveQueue = new QueueProcessor(
async (filenames: FilePathWithPrefix[]) => { async (filenames: FilePathWithPrefix[]) => {
const filename = filenames[0]; const filename = filenames[0];
return await this.core.$$resolveConflict(filename); return await this.services.conflict.resolve(filename);
}, },
{ {
suspended: false, suspended: false,
@@ -73,4 +74,9 @@ export class ModuleConflictChecker extends AbstractModule implements ICoreModule
totalRemainingReactiveSource: this.core.conflictProcessQueueCount, 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));
}
} }

View File

@@ -20,8 +20,9 @@ import {
} from "../../common/utils"; } from "../../common/utils";
import diff_match_patch from "diff-match-patch"; import diff_match_patch from "diff-match-patch";
import { stripAllPrefixes, isPlainText } from "../../lib/src/string_and_binary/path"; import { stripAllPrefixes, isPlainText } from "../../lib/src/string_and_binary/path";
import type { ICoreModule } from "../ModuleTypes.ts";
import { eventHub } from "../../common/events.ts"; import { eventHub } from "../../common/events.ts";
import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts";
import type { LiveSyncCore } from "../../main.ts";
declare global { declare global {
interface LSEvents { interface LSEvents {
@@ -29,8 +30,8 @@ declare global {
} }
} }
export class ModuleConflictResolver extends AbstractModule implements ICoreModule { export class ModuleConflictResolver extends AbstractModule {
async $$resolveConflictByDeletingRev( private async _resolveConflictByDeletingRev(
path: FilePathWithPrefix, path: FilePathWithPrefix,
deleteRevision: string, deleteRevision: string,
subTitle = "" subTitle = ""
@@ -82,7 +83,7 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul
return MISSING_OR_ERROR; return MISSING_OR_ERROR;
} }
// 2. As usual, delete the conflicted revision and if there are no conflicts, write the resolved content to the storage. // 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; const { rightRev, leftLeaf, rightLeaf } = ret;
@@ -95,7 +96,7 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul
} }
if (rightLeaf == false) { if (rightLeaf == false) {
// Conflicted item could not load, delete this. // 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; 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()) .filter((e) => e.trim())
.join(","); .join(",");
return await this.core.$$resolveConflictByDeletingRev(path, loser.rev, subTitle); return await this.services.conflict.resolveByDeletingRevision(path, loser.rev, subTitle);
} }
// make diff. // make diff.
const dmp = new diff_match_patch(); const dmp = new diff_match_patch();
@@ -129,7 +130,7 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul
}; };
} }
async $$resolveConflict(filename: FilePathWithPrefix): Promise<void> { private async _resolveConflict(filename: FilePathWithPrefix): Promise<void> {
// const filename = filenames[0]; // const filename = filenames[0];
return await serialized(`conflict-resolve:${filename}`, async () => { return await serialized(`conflict-resolve:${filename}`, async () => {
const conflictCheckResult = await this.checkConflictAndPerformAutoMerge(filename); const conflictCheckResult = await this.checkConflictAndPerformAutoMerge(filename);
@@ -144,16 +145,16 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul
} }
if (conflictCheckResult === AUTO_MERGED) { if (conflictCheckResult === AUTO_MERGED) {
//auto resolved, but need check again; //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. //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"); this._log("[conflict] Automatically merged, but we have to check it again");
await this.core.$$queueConflictCheck(filename); await this.services.conflict.queueCheckFor(filename);
return; return;
} }
if (this.settings.showMergeDialogOnlyOnActive) { if (this.settings.showMergeDialogOnlyOnActive) {
const af = this.core.$$getActiveFilePath(); const af = this.services.vault.getActiveFilePath();
if (af && af != filename) { if (af && af != filename) {
this._log( this._log(
`[conflict] ${filename} is conflicted. Merging process has been postponed to the file have got opened.`, `[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!"); this._log("[conflict] Manual merge required!");
eventHub.emitEvent("conflict-cancelled", filename); eventHub.emitEvent("conflict-cancelled", filename);
await this.core.$anyResolveConflictByUI(filename, conflictCheckResult); await this.services.conflict.resolveByUserInteraction(filename, conflictCheckResult);
}); });
} }
async $anyResolveConflictByNewest(filename: FilePathWithPrefix): Promise<boolean> { private async _anyResolveConflictByNewest(filename: FilePathWithPrefix): Promise<boolean> {
const currentRev = await this.core.databaseFileAccess.fetchEntryMeta(filename, undefined, true); const currentRev = await this.core.databaseFileAccess.fetchEntryMeta(filename, undefined, true);
if (currentRev == false) { if (currentRev == false) {
this._log(`Could not get current revision of ${filename}`); this._log(`Could not get current revision of ${filename}`);
@@ -206,8 +207,14 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul
this._log( this._log(
`conflict: Deleting the older revision ${mTimeAndRev[i][1]} (${new Date(mTimeAndRev[i][0]).toLocaleString()}) of ${filename}` `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; 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));
}
} }

View File

@@ -9,10 +9,10 @@ import {
type ObsidianLiveSyncSettings, type ObsidianLiveSyncSettings,
} from "../../lib/src/common/types.ts"; } from "../../lib/src/common/types.ts";
import { AbstractModule } from "../AbstractModule.ts"; import { AbstractModule } from "../AbstractModule.ts";
import type { ICoreModule } from "../ModuleTypes.ts";
import { $msg } from "../../lib/src/common/i18n.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) { async isFlagFileExist(path: string) {
const redflag = await this.core.storageAccess.isExists(normalizePath(path)); const redflag = await this.core.storageAccess.isExists(normalizePath(path));
if (redflag) { if (redflag) {
@@ -48,7 +48,7 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule {
await this.deleteFlagFile(FLAGMD_REDFLAG3); await this.deleteFlagFile(FLAGMD_REDFLAG3);
await this.deleteFlagFile(FLAGMD_REDFLAG3_HR); await this.deleteFlagFile(FLAGMD_REDFLAG3_HR);
} }
async $everyOnLayoutReady(): Promise<boolean> { async _everyOnLayoutReady(): Promise<boolean> {
try { try {
const isRedFlagRaised = await this.isRedFlagRaised(); const isRedFlagRaised = await this.isRedFlagRaised();
const isRedFlag2Raised = await this.isRedFlag2Raised(); const isRedFlag2Raised = await this.isRedFlag2Raised();
@@ -63,7 +63,7 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule {
)) !== "yes" )) !== "yes"
) { ) {
await this.deleteRedFlag2(); await this.deleteRedFlag2();
await this.core.$$performRestart(); this.services.appLifecycle.performRestart();
return false; return false;
} }
} }
@@ -75,13 +75,13 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule {
})) !== "yes" })) !== "yes"
) { ) {
await this.deleteRedFlag3(); await this.deleteRedFlag3();
await this.core.$$performRestart(); this.services.appLifecycle.performRestart();
return false; return false;
} }
} }
this.settings.batchSave = false; this.settings.batchSave = false;
await this.core.$allSuspendAllSync(); await this.services.setting.suspendAllSync();
await this.core.$allSuspendExtraSync(); await this.services.setting.suspendExtraSync();
this.settings.suspendFileWatching = true; this.settings.suspendFileWatching = true;
await this.saveSettings(); await this.saveSettings();
if (isRedFlag2Raised) { if (isRedFlag2Raised) {
@@ -99,7 +99,7 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule {
) { ) {
this.settings.suspendFileWatching = false; this.settings.suspendFileWatching = false;
await this.saveSettings(); await this.saveSettings();
this.core.$$performRestart(); this.services.appLifecycle.performRestart();
return false; return false;
} }
} else if (isRedFlag3Raised) { } else if (isRedFlag3Raised) {
@@ -148,7 +148,7 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule {
if (fetchRemote === optionFetchRemoteConf) { if (fetchRemote === optionFetchRemoteConf) {
this._log("Fetching remote configuration", LOG_LEVEL_NOTICE); this._log("Fetching remote configuration", LOG_LEVEL_NOTICE);
const newSettings = JSON.parse(JSON.stringify(this.core.settings)) as ObsidianLiveSyncSettings; 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) { if (remoteConfig) {
this._log("Remote configuration found.", LOG_LEVEL_NOTICE); this._log("Remote configuration found.", LOG_LEVEL_NOTICE);
const mergedSettings = { const mergedSettings = {
@@ -174,7 +174,7 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule {
) { ) {
this.settings.suspendFileWatching = false; this.settings.suspendFileWatching = false;
await this.saveSettings(); await this.saveSettings();
this.core.$$performRestart(); this.services.appLifecycle.performRestart();
return false; return false;
} }
} else { } else {
@@ -198,4 +198,8 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule {
} }
return true; return true;
} }
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
super.onBindFunction(core, services);
services.appLifecycle.handleLayoutReady(this._everyOnLayoutReady.bind(this));
}
} }

View File

@@ -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 { AbstractModule } from "../AbstractModule.ts";
import type { ICoreModule } from "../ModuleTypes.ts";
export class ModuleRemoteGovernor extends AbstractModule implements ICoreModule { export class ModuleRemoteGovernor extends AbstractModule {
async $$markRemoteLocked(lockByClean: boolean = false): Promise<void> { private async _markRemoteLocked(lockByClean: boolean = false): Promise<void> {
return await this.core.replicator.markRemoteLocked(this.settings, true, lockByClean); return await this.core.replicator.markRemoteLocked(this.settings, true, lockByClean);
} }
async $$markRemoteUnlocked(): Promise<void> { private async _markRemoteUnlocked(): Promise<void> {
return await this.core.replicator.markRemoteLocked(this.settings, false, false); return await this.core.replicator.markRemoteLocked(this.settings, false, false);
} }
async $$markRemoteResolved(): Promise<void> { private async _markRemoteResolved(): Promise<void> {
return await this.core.replicator.markRemoteResolved(this.settings); 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));
}
} }

View File

@@ -11,21 +11,22 @@ import {
} from "../../lib/src/common/types.ts"; } from "../../lib/src/common/types.ts";
import { escapeMarkdownValue } from "../../lib/src/common/utils.ts"; import { escapeMarkdownValue } from "../../lib/src/common/utils.ts";
import { AbstractModule } from "../AbstractModule.ts"; import { AbstractModule } from "../AbstractModule.ts";
import type { ICoreModule } from "../ModuleTypes.ts";
import { $msg } from "../../lib/src/common/i18n.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 { export class ModuleResolvingMismatchedTweaks extends AbstractModule {
async $anyAfterConnectCheckFailed(): Promise<boolean | "CHECKAGAIN" | undefined> { async _anyAfterConnectCheckFailed(): Promise<boolean | "CHECKAGAIN" | undefined> {
if (!this.core.replicator.tweakSettingsMismatched && !this.core.replicator.preferredTweakValue) return false; if (!this.core.replicator.tweakSettingsMismatched && !this.core.replicator.preferredTweakValue) return false;
const preferred = this.core.replicator.preferredTweakValue; const preferred = this.core.replicator.preferredTweakValue;
if (!preferred) return false; 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 == "OK") return false;
if (ret == "CHECKAGAIN") return "CHECKAGAIN"; if (ret == "CHECKAGAIN") return "CHECKAGAIN";
if (ret == "IGNORE") return true; if (ret == "IGNORE") return true;
} }
async $$checkAndAskResolvingMismatchedTweaks( async _checkAndAskResolvingMismatchedTweaks(
preferred: Partial<TweakValues> preferred: Partial<TweakValues>
): Promise<[TweakValues | boolean, boolean]> { ): Promise<[TweakValues | boolean, boolean]> {
const mine = extractObject(TweakValuesShouldMatchedTemplate, this.settings); const mine = extractObject(TweakValuesShouldMatchedTemplate, this.settings);
@@ -127,7 +128,7 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule implements I
return CHOICES[retKey]; return CHOICES[retKey];
} }
async $$askResolvingMismatchedTweaks(): Promise<"OK" | "CHECKAGAIN" | "IGNORE"> { async _askResolvingMismatchedTweaks(): Promise<"OK" | "CHECKAGAIN" | "IGNORE"> {
if (!this.core.replicator.tweakSettingsMismatched) { if (!this.core.replicator.tweakSettingsMismatched) {
return "OK"; return "OK";
} }
@@ -137,7 +138,7 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule implements I
} }
const preferred = extractObject(TweakValuesShouldMatchedTemplate, tweaks); 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) return "IGNORE";
if (conf === true) { if (conf === true) {
@@ -154,7 +155,7 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule implements I
if (conf) { if (conf) {
this.settings = { ...this.settings, ...conf }; this.settings = { ...this.settings, ...conf };
await this.core.replicator.setPreferredRemoteTweakSettings(this.settings); await this.core.replicator.setPreferredRemoteTweakSettings(this.settings);
await this.core.$$saveSettingData(); await this.services.setting.saveSettingData();
if (rebuildRequired) { if (rebuildRequired) {
await this.core.rebuilder.$fetchLocal(); await this.core.rebuilder.$fetchLocal();
} }
@@ -164,8 +165,12 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule implements I
return "IGNORE"; return "IGNORE";
} }
async $$fetchRemotePreferredTweakValues(trialSetting: RemoteDBSettings): Promise<TweakValues | false> { async _fetchRemotePreferredTweakValues(trialSetting: RemoteDBSettings): Promise<TweakValues | false> {
const replicator = await this.core.$anyNewReplicator(); 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)) { if (await replicator.tryConnectRemote(trialSetting)) {
const preferred = await replicator.getRemotePreferredTweakValues(trialSetting); const preferred = await replicator.getRemotePreferredTweakValues(trialSetting);
if (preferred) { if (preferred) {
@@ -178,17 +183,17 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule implements I
return false; return false;
} }
async $$checkAndAskUseRemoteConfiguration( async _checkAndAskUseRemoteConfiguration(
trialSetting: RemoteDBSettings trialSetting: RemoteDBSettings
): Promise<{ result: false | TweakValues; requireFetch: boolean }> { ): Promise<{ result: false | TweakValues; requireFetch: boolean }> {
const preferred = await this.core.$$fetchRemotePreferredTweakValues(trialSetting); const preferred = await this.services.tweakValue.fetchRemotePreferred(trialSetting);
if (preferred) { if (preferred) {
return await this.$$askUseRemoteConfiguration(trialSetting, preferred); return await this.services.tweakValue.askUseRemoteConfiguration(trialSetting, preferred);
} }
return { result: false, requireFetch: false }; return { result: false, requireFetch: false };
} }
async $$askUseRemoteConfiguration( async _askUseRemoteConfiguration(
trialSetting: RemoteDBSettings, trialSetting: RemoteDBSettings,
preferred: TweakValues preferred: TweakValues
): Promise<{ result: false | TweakValues; requireFetch: boolean }> { ): Promise<{ result: false | TweakValues; requireFetch: boolean }> {
@@ -278,4 +283,13 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule implements I
} }
return { result: false, requireFetch: false }; 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));
}
} }

View File

@@ -1,6 +1,6 @@
import { TFile, TFolder, type ListedFiles } from "obsidian"; import { TFile, TFolder, type ListedFiles } from "obsidian";
import { SerializedFileAccess } from "./storageLib/SerializedFileAccess"; 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 { LOG_LEVEL_INFO, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
import type { import type {
FilePath, FilePath,
@@ -16,10 +16,13 @@ import { StorageEventManagerObsidian, type StorageEventManager } from "./storage
import type { StorageAccess } from "../interfaces/StorageAccess"; import type { StorageAccess } from "../interfaces/StorageAccess";
import { createBlob, type CustomRegExp } from "../../lib/src/common/utils"; import { createBlob, type CustomRegExp } from "../../lib/src/common/utils";
import { serialized } from "octagonal-wheels/concurrency/lock_v2"; 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:"; const fileLockPrefix = "file-lock:";
export class ModuleFileAccessObsidian extends AbstractObsidianModule implements IObsidianModule, StorageAccess { export class ModuleFileAccessObsidian extends AbstractObsidianModule implements StorageAccess {
processingFiles: Set<FilePathWithPrefix> = new Set(); processingFiles: Set<FilePathWithPrefix> = new Set();
processWriteFile<T>(file: UXFileInfoStub | FilePathWithPrefix, proc: () => Promise<T>): Promise<T> { processWriteFile<T>(file: UXFileInfoStub | FilePathWithPrefix, proc: () => Promise<T>): Promise<T> {
const path = typeof file === "string" ? file : file.path; const path = typeof file === "string" ? file : file.path;
@@ -49,39 +52,35 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements
} }
vaultAccess!: SerializedFileAccess; vaultAccess!: SerializedFileAccess;
vaultManager: StorageEventManager = new StorageEventManagerObsidian(this.plugin, this.core, this); vaultManager: StorageEventManager = new StorageEventManagerObsidian(this.plugin, this.core, this);
$everyOnload(): Promise<boolean> { private _everyOnload(): Promise<boolean> {
this.core.storageAccess = this; this.core.storageAccess = this;
return Promise.resolve(true); return Promise.resolve(true);
} }
$everyOnFirstInitialize(): Promise<boolean> { _everyOnFirstInitialize(): Promise<boolean> {
this.vaultManager.beginWatch(); this.vaultManager.beginWatch();
return Promise.resolve(true); return Promise.resolve(true);
} }
$allOnUnload(): Promise<boolean> {
// this.vaultManager.
return Promise.resolve(true);
}
// $$flushFileEventQueue(): void { // $$flushFileEventQueue(): void {
// this.vaultManager.flushQueue(); // this.vaultManager.flushQueue();
// } // }
$everyCommitPendingFileEvent(): Promise<boolean> { _everyCommitPendingFileEvent(): Promise<boolean> {
this.vaultManager.flushQueue(); this.vaultManager.flushQueue();
return Promise.resolve(true); return Promise.resolve(true);
} }
$everyOnloadStart(): Promise<boolean> { _everyOnloadStart(): Promise<boolean> {
this.vaultAccess = new SerializedFileAccess(this.app, this.plugin, this); this.vaultAccess = new SerializedFileAccess(this.app, this.plugin, this);
return Promise.resolve(true); return Promise.resolve(true);
} }
$$isStorageInsensitive(): boolean { _isStorageInsensitive(): boolean {
return this.vaultAccess.isStorageInsensitive(); return this.vaultAccess.isStorageInsensitive();
} }
$$shouldCheckCaseInsensitive(): boolean { _shouldCheckCaseInsensitive(): boolean {
if (this.$$isStorageInsensitive()) return false; if (this.services.vault.isStorageInsensitive()) return false;
return !this.settings.handleFilenameCaseSensitive; return !this.settings.handleFilenameCaseSensitive;
} }
@@ -275,7 +274,7 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements
if (excludeFilter && excludeFilter.some((ee) => ee.test(file))) { if (excludeFilter && excludeFilter.some((ee) => ee.test(file))) {
continue; continue;
} }
if (await this.plugin.$$isIgnoredByIgnoreFiles(file)) continue; if (await this.services.vault.isIgnoredByIgnoreFile(file)) continue;
files.push(file); files.push(file);
} }
@@ -288,7 +287,7 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements
if (excludeFilter && excludeFilter.some((e) => e.test(v))) { if (excludeFilter && excludeFilter.some((e) => e.test(v))) {
continue; continue;
} }
if (await this.plugin.$$isIgnoredByIgnoreFiles(v)) { if (await this.services.vault.isIgnoredByIgnoreFile(v)) {
continue; continue;
} }
// OK, deep dive! // OK, deep dive!
@@ -346,7 +345,7 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements
async _deleteVaultItem(file: TFile | TFolder) { async _deleteVaultItem(file: TFile | TFolder) {
if (file instanceof TFile) { 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; const dir = file.parent;
if (this.settings.trashInsteadDelete) { if (this.settings.trashInsteadDelete) {
@@ -376,4 +375,16 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements
return await this._deleteVaultItem(file); 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));
}
} }

View File

@@ -1,5 +1,5 @@
// ModuleInputUIObsidian.ts // ModuleInputUIObsidian.ts
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts"; import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
import { scheduleTask } from "octagonal-wheels/concurrency/task"; import { scheduleTask } from "octagonal-wheels/concurrency/task";
import { disposeMemoObject, memoIfNotExist, memoObject, retrieveMemoObject } from "../../common/utils.ts"; import { disposeMemoObject, memoIfNotExist, memoObject, retrieveMemoObject } from "../../common/utils.ts";
import { import {
@@ -13,12 +13,13 @@ import { Notice } from "../../deps.ts";
import type { Confirm } from "../../lib/src/interfaces/Confirm.ts"; import type { Confirm } from "../../lib/src/interfaces/Confirm.ts";
import { setConfirmInstance } from "../../lib/src/PlatformAPIs/obsidian/Confirm.ts"; import { setConfirmInstance } from "../../lib/src/PlatformAPIs/obsidian/Confirm.ts";
import { $msg } from "src/lib/src/common/i18n.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. // This module cannot be a common module because it depends on Obsidian's API.
// However, we have to make compatible one for other platform. // However, we have to make compatible one for other platform.
export class ModuleInputUIObsidian extends AbstractObsidianModule implements IObsidianModule, Confirm { export class ModuleInputUIObsidian extends AbstractObsidianModule implements Confirm {
$everyOnload(): Promise<boolean> { private _everyOnload(): Promise<boolean> {
this.core.confirm = this; this.core.confirm = this;
setConfirmInstance(this); setConfirmInstance(this);
return Promise.resolve(true); return Promise.resolve(true);
@@ -110,4 +111,8 @@ export class ModuleInputUIObsidian extends AbstractObsidianModule implements IOb
): Promise<(typeof buttons)[number] | false> { ): Promise<(typeof buttons)[number] | false> {
return confirmWithMessage(this.plugin, title, contentMd, buttons, defaultAction, timeout); return confirmWithMessage(this.plugin, title, contentMd, buttons, defaultAction, timeout);
} }
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this));
}
} }

View File

@@ -7,13 +7,14 @@ import {
LOG_LEVEL_INFO, LOG_LEVEL_INFO,
LOG_LEVEL_NOTICE, LOG_LEVEL_NOTICE,
LOG_LEVEL_VERBOSE, LOG_LEVEL_VERBOSE,
type FileEventType,
type FilePath, type FilePath,
type FilePathWithPrefix, type FilePathWithPrefix,
type UXFileInfoStub, type UXFileInfoStub,
type UXInternalFileInfoStub, type UXInternalFileInfoStub,
} from "../../../lib/src/common/types.ts"; } from "../../../lib/src/common/types.ts";
import { delay, fireAndForget, getFileRegExp } from "../../../lib/src/common/utils.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 { serialized, skipIfDuplicated } from "octagonal-wheels/concurrency/lock";
import { import {
finishAllWaitingForTimeout, finishAllWaitingForTimeout,
@@ -48,6 +49,9 @@ export class StorageEventManagerObsidian extends StorageEventManager {
plugin: ObsidianLiveSyncPlugin; plugin: ObsidianLiveSyncPlugin;
core: LiveSyncCore; core: LiveSyncCore;
storageAccess: StorageAccess; storageAccess: StorageAccess;
get services() {
return this.core.services;
}
get shouldBatchSave() { get shouldBatchSave() {
return this.core.settings?.batchSave && this.core.settings?.liveSync != true; 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 (this.plugin.settings.useIgnoreFiles) {
// If it is one of ignore files, refresh the cached one. // If it is one of ignore files, refresh the cached one.
// (Calling$$isTargetFile will refresh the cache) // (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 { } else {
this._watchVaultRawEvents(path); this._watchVaultRawEvents(path);
} }
@@ -209,7 +213,7 @@ export class StorageEventManagerObsidian extends StorageEventManager {
async appendQueue(params: FileEvent[], ctx?: any) { async appendQueue(params: FileEvent[], ctx?: any) {
if (!this.core.settings.isConfigured) return; if (!this.core.settings.isConfigured) return;
if (this.core.settings.suspendFileWatching) return; if (this.core.settings.suspendFileWatching) return;
this.core.$$markFileListPossiblyChanged(); this.core.services.vault.markFileListPossiblyChanged();
// Flag up to be reload // Flag up to be reload
const processFiles = new Set<FilePath>(); const processFiles = new Set<FilePath>();
for (const param of params) { for (const param of params) {
@@ -222,7 +226,7 @@ export class StorageEventManagerObsidian extends StorageEventManager {
const oldPath = param.oldPath; const oldPath = param.oldPath;
if (type !== "INTERNAL") { if (type !== "INTERNAL") {
const size = (file as UXFileInfoStub).stat.size; 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( Logger(
`The storage file has been changed but exceeds the maximum size. Skipping: ${param.file.path}`, `The storage file has been changed but exceeds the maximum size. Skipping: ${param.file.path}`,
LOG_LEVEL_NOTICE LOG_LEVEL_NOTICE
@@ -234,7 +238,7 @@ export class StorageEventManagerObsidian extends StorageEventManager {
// TODO: Confirm why only the TFolder skipping // TODO: Confirm why only the TFolder skipping
// Possibly following line is needed... // Possibly following line is needed...
// if (file?.isFolder) continue; // 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; // Stop cache using to prevent the corruption;
// let cache: null | string | ArrayBuffer; // let cache: null | string | ArrayBuffer;
@@ -411,12 +415,12 @@ export class StorageEventManagerObsidian extends StorageEventManager {
const lockKey = `handleFile:${file.path}`; const lockKey = `handleFile:${file.path}`;
return await serialized(lockKey, async () => { return await serialized(lockKey, async () => {
if (queue.type == "INTERNAL" || file.isInternal) { 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 { } else {
const key = `file-last-proc-${queue.type}-${file.path}`; const key = `file-last-proc-${queue.type}-${file.path}`;
const last = Number((await this.core.kvDB.get(key)) || 0); const last = Number((await this.core.kvDB.get(key)) || 0);
if (queue.type == "DELETE") { if (queue.type == "DELETE") {
await this.core.$anyHandlerProcessesFileEvent(queue); await this.core.services.fileProcessing.processFileEvent(queue);
} else { } else {
if (file.stat.mtime == last) { if (file.stat.mtime == last) {
Logger(`File has been already scanned on ${queue.type}, skip: ${file.path}`, LOG_LEVEL_VERBOSE); 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); // this.cancelRelativeEvent(queue);
return; return;
} }
if (!(await this.core.$anyHandlerProcessesFileEvent(queue))) { if (!(await this.core.services.fileProcessing.processFileEvent(queue))) {
Logger( Logger(
`STORAGE -> DB: Handler failed, cancel the relative operations: ${file.path}`, `STORAGE -> DB: Handler failed, cancel the relative operations: ${file.path}`,
LOG_LEVEL_INFO LOG_LEVEL_INFO

View File

@@ -17,10 +17,11 @@ import {
import { isAnyNote } from "../../lib/src/common/utils.ts"; import { isAnyNote } from "../../lib/src/common/utils.ts";
import { stripAllPrefixes } from "../../lib/src/string_and_binary/path.ts"; import { stripAllPrefixes } from "../../lib/src/string_and_binary/path.ts";
import { AbstractModule } from "../AbstractModule.ts"; import { AbstractModule } from "../AbstractModule.ts";
import type { ICoreModule } from "../ModuleTypes.ts";
import { withConcurrency } from "octagonal-wheels/iterable/map"; import { withConcurrency } from "octagonal-wheels/iterable/map";
export class ModuleInitializerFile extends AbstractModule implements ICoreModule { import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts";
async $$performFullScan(showingNotice?: boolean, ignoreSuspending: boolean = false): Promise<void> { import type { LiveSyncCore } from "../../main.ts";
export class ModuleInitializerFile extends AbstractModule {
private async _performFullScan(showingNotice?: boolean, ignoreSuspending: boolean = false): Promise<boolean> {
this._log("Opening the key-value database", LOG_LEVEL_VERBOSE); this._log("Opening the key-value database", LOG_LEVEL_VERBOSE);
const isInitialized = (await this.core.kvDB.get<boolean>("initialized")) || false; const isInitialized = (await this.core.kvDB.get<boolean>("initialized")) || false;
// synchronize all files between database and storage. // synchronize all files between database and storage.
@@ -32,7 +33,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
"syncAll" "syncAll"
); );
} }
return; return false;
} }
if (!ignoreSuspending && this.settings.suspendFileWatching) { if (!ignoreSuspending && this.settings.suspendFileWatching) {
if (showingNotice) { if (showingNotice) {
@@ -42,7 +43,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
"syncAll" "syncAll"
); );
} }
return; return false;
} }
if (showingNotice) { if (showingNotice) {
@@ -59,7 +60,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
const _filesStorage = [] as typeof filesStorageSrc; const _filesStorage = [] as typeof filesStorageSrc;
for (const f of 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); _filesStorage.push(f);
} }
} }
@@ -103,7 +104,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
); );
const path = getPath(doc); 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)) { if (!isMetaEntry(doc)) {
this._log(`Invalid entry: ${path}`, LOG_LEVEL_INFO); this._log(`Invalid entry: ${path}`, LOG_LEVEL_INFO);
continue; 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 database: ${databaseFileNames.length}`, LOG_LEVEL_VERBOSE, "syncAll");
this._log(`Total files in the storage: ${storageFileNames.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"); this._log(`Total files: ${allFiles.length}`, LOG_LEVEL_VERBOSE, "syncAll");
const filesExistOnlyInStorage = allFiles.filter((e) => !databaseFileNameCI2CS[e]); const filesExistOnlyInStorage = allFiles.filter((e) => !databaseFileNameCI2CS[e]);
const filesExistOnlyInDatabase = allFiles.filter((e) => !storageFileNameCI2CS[e]); const filesExistOnlyInDatabase = allFiles.filter((e) => !storageFileNameCI2CS[e]);
const filesExistBoth = allFiles.filter((e) => databaseFileNameCI2CS[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) => { runAll("UPDATE DATABASE", filesExistOnlyInStorage, async (e) => {
// Exists in storage but not in database. // Exists in storage but not in database.
const file = storageFileNameMap[storageFileNameCI2CS[e]]; const file = storageFileNameMap[storageFileNameCI2CS[e]];
if (!this.core.$$isFileSizeExceeded(file.stat.size)) { if (!this.services.vault.isFileSizeTooLarge(file.stat.size)) {
const path = file.path; const path = file.path;
await this.core.fileHandler.storeFileToDB(file); await this.core.fileHandler.storeFileToDB(file);
// fireAndForget(() => this.checkAndApplySettingFromMarkdown(path, true)); // fireAndForget(() => this.checkAndApplySettingFromMarkdown(path, true));
@@ -208,7 +208,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
// Exists in database but not in storage. // Exists in database but not in storage.
const path = getPath(w) ?? e; const path = getPath(w) ?? e;
if (w && !(w.deleted || w._deleted)) { 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. // Prevent applying the conflicted state to the storage.
if (w._conflicts?.length ?? 0 > 0) { if (w._conflicts?.length ?? 0 > 0) {
this._log(`UPDATE STORAGE: ${path} has conflicts. skipped (x)`, LOG_LEVEL_INFO); 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); this._log(`SYNC DATABASE AND STORAGE: ${file.path} has conflicts. skipped`, LOG_LEVEL_INFO);
return; 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); await this.syncFileBetweenDBandStorage(file, doc);
} else { } else {
this._log( this._log(
@@ -271,6 +274,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
if (showingNotice) { if (showingNotice) {
this._log("Initialize done!", LOG_LEVEL_NOTICE, "syncAll"); this._log("Initialize done!", LOG_LEVEL_NOTICE, "syncAll");
} }
return true;
} }
async syncFileBetweenDBandStorage(file: UXFileInfoStub, doc: MetaEntry) { async syncFileBetweenDBandStorage(file: UXFileInfoStub, doc: MetaEntry) {
@@ -289,7 +293,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
const compareResult = compareFileFreshness(file, doc); const compareResult = compareFileFreshness(file, doc);
switch (compareResult) { switch (compareResult) {
case BASE_IS_NEW: 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); this._log("STORAGE -> DB :" + file.path);
await this.core.fileHandler.storeFileToDB(file); await this.core.fileHandler.storeFileToDB(file);
} else { } else {
@@ -300,7 +304,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
} }
break; break;
case TARGET_IS_NEW: case TARGET_IS_NEW:
if (!this.core.$$isFileSizeExceeded(doc.size)) { if (!this.services.vault.isFileSizeTooLarge(doc.size)) {
this._log("STORAGE <- DB :" + file.path); this._log("STORAGE <- DB :" + file.path);
if (await this.core.fileHandler.dbToStorage(doc, stripAllPrefixes(file.path), true)) { if (await this.core.fileHandler.dbToStorage(doc, stripAllPrefixes(file.path), true)) {
eventHub.emitEvent("event-file-changed", { eventHub.emitEvent("event-file-changed", {
@@ -365,27 +369,31 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
this._log(`Checking expired file history done`); this._log(`Checking expired file history done`);
} }
async $$initializeDatabase( private async _initializeDatabase(
showingNotice: boolean = false, showingNotice: boolean = false,
reopenDatabase = true, reopenDatabase = true,
ignoreSuspending: boolean = false ignoreSuspending: boolean = false
): Promise<boolean> { ): Promise<boolean> {
this.core.$$resetIsReady(); this.services.appLifecycle.resetIsReady();
if (!reopenDatabase || (await this.core.$$openDatabase())) { if (!reopenDatabase || (await this.services.database.openDatabase())) {
if (this.localDatabase.isReady) { if (this.localDatabase.isReady) {
await this.core.$$performFullScan(showingNotice, ignoreSuspending); await this.services.vault.scanVault(showingNotice, ignoreSuspending);
} }
if (!(await this.core.$everyOnDatabaseInitialized(showingNotice))) { if (!(await this.services.databaseEvents.onDatabaseInitialised(showingNotice))) {
this._log(`Initializing database has been failed on some module`, LOG_LEVEL_NOTICE); this._log(`Initializing database has been failed on some module!`, LOG_LEVEL_NOTICE);
return false; return false;
} }
this.core.$$markIsReady(); this.services.appLifecycle.markIsReady();
// run queued event once. // run queued event once.
await this.core.$everyCommitPendingFileEvent(); await this.services.fileProcessing.commitPendingFileEvents();
return true; return true;
} else { } else {
this.core.$$resetIsReady(); this.services.appLifecycle.resetIsReady();
return false; return false;
} }
} }
onBindFunction(core: LiveSyncCore, services: InjectableServiceHub): void {
services.databaseEvents.handleInitialiseDatabase(this._initializeDatabase.bind(this));
services.vault.handleScanVault(this._performFullScan.bind(this));
}
} }

View File

@@ -3,9 +3,9 @@ import { OpenKeyValueDatabase } from "../../common/KeyValueDB.ts";
import type { LiveSyncLocalDB } from "../../lib/src/pouchdb/LiveSyncLocalDB.ts"; import type { LiveSyncLocalDB } from "../../lib/src/pouchdb/LiveSyncLocalDB.ts";
import { LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger"; import { LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
import { AbstractModule } from "../AbstractModule.ts"; 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() { tryCloseKvDB() {
try { try {
this.core.kvDB?.close(); this.core.kvDB?.close();
@@ -22,7 +22,7 @@ export class ModuleKeyValueDB extends AbstractModule implements ICoreModule {
this.tryCloseKvDB(); this.tryCloseKvDB();
await delay(10); await delay(10);
await yieldMicrotask(); 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 yieldMicrotask();
await delay(100); await delay(100);
} catch (e) { } catch (e) {
@@ -33,21 +33,23 @@ export class ModuleKeyValueDB extends AbstractModule implements ICoreModule {
} }
return true; return true;
} }
$allOnDBUnload(db: LiveSyncLocalDB): void { _onDBUnload(db: LiveSyncLocalDB) {
if (this.core.kvDB) this.core.kvDB.close(); 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(); if (this.core.kvDB) this.core.kvDB.close();
return Promise.resolve(true);
} }
async $everyOnloadAfterLoadSettings(): Promise<boolean> { private async _everyOnloadAfterLoadSettings(): Promise<boolean> {
if (!(await this.openKeyValueDB())) { if (!(await this.openKeyValueDB())) {
return false; return false;
} }
this.core.simpleStore = this.core.$$getSimpleStore<any>("os"); this.core.simpleStore = this.services.database.openSimpleStore<any>("os");
return Promise.resolve(true); return Promise.resolve(true);
} }
$$getSimpleStore<T>(kind: string) { _getSimpleStore<T>(kind: string) {
const prefix = `${kind}-`; const prefix = `${kind}-`;
return { return {
get: async (key: string): Promise<T> => { get: async (key: string): Promise<T> => {
@@ -75,18 +77,18 @@ export class ModuleKeyValueDB extends AbstractModule implements ICoreModule {
}, },
}; };
} }
$everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise<boolean> { _everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise<boolean> {
return this.openKeyValueDB(); return this.openKeyValueDB();
} }
async $everyOnResetDatabase(db: LiveSyncLocalDB): Promise<boolean> { async _everyOnResetDatabase(db: LiveSyncLocalDB): Promise<boolean> {
try { try {
const kvDBKey = "queued-files"; const kvDBKey = "queued-files";
await this.core.kvDB.del(kvDBKey); await this.core.kvDB.del(kvDBKey);
// localStorage.removeItem(lsKey); // localStorage.removeItem(lsKey);
await this.core.kvDB.destroy(); await this.core.kvDB.destroy();
await yieldMicrotask(); 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); await delay(100);
} catch (e) { } catch (e) {
this.core.kvDB = undefined!; this.core.kvDB = undefined!;
@@ -96,4 +98,12 @@ export class ModuleKeyValueDB extends AbstractModule implements ICoreModule {
} }
return true; 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));
}
} }

View File

@@ -9,13 +9,13 @@ import {
eventHub, eventHub,
} from "../../common/events.ts"; } from "../../common/events.ts";
import { AbstractModule } from "../AbstractModule.ts"; import { AbstractModule } from "../AbstractModule.ts";
import type { ICoreModule } from "../ModuleTypes.ts";
import { $msg } from "src/lib/src/common/i18n.ts"; import { $msg } from "src/lib/src/common/i18n.ts";
import { performDoctorConsultation, RebuildOptions } from "../../lib/src/common/configForDoc.ts"; import { performDoctorConsultation, RebuildOptions } from "../../lib/src/common/configForDoc.ts";
import { getPath, isValidPath } from "../../common/utils.ts"; import { getPath, isValidPath } from "../../common/utils.ts";
import { isMetaEntry } from "../../lib/src/common/types.ts"; import { isMetaEntry } from "../../lib/src/common/types.ts";
import { isDeletedEntry, isDocContentSame, isLoadedEntry, readAsBlob } from "../../lib/src/common/utils.ts"; import { isDeletedEntry, isDocContentSame, isLoadedEntry, readAsBlob } from "../../lib/src/common/utils.ts";
import { countCompromisedChunks } from "../../lib/src/pouchdb/negotiation.ts"; import { countCompromisedChunks } from "../../lib/src/pouchdb/negotiation.ts";
import type { LiveSyncCore } from "../../main.ts";
type ErrorInfo = { type ErrorInfo = {
path: string; path: string;
@@ -26,7 +26,7 @@ type ErrorInfo = {
isConflicted?: boolean; isConflicted?: boolean;
}; };
export class ModuleMigration extends AbstractModule implements ICoreModule { export class ModuleMigration extends AbstractModule {
async migrateUsingDoctor(skipRebuild: boolean = false, activateReason = "updated", forceRescan = false) { async migrateUsingDoctor(skipRebuild: boolean = false, activateReason = "updated", forceRescan = false) {
const { shouldRebuild, shouldRebuildLocal, isModified, settings } = await performDoctorConsultation( const { shouldRebuild, shouldRebuildLocal, isModified, settings } = await performDoctorConsultation(
this.core, this.core,
@@ -45,11 +45,11 @@ export class ModuleMigration extends AbstractModule implements ICoreModule {
if (!skipRebuild) { if (!skipRebuild) {
if (shouldRebuild) { if (shouldRebuild) {
await this.core.rebuilder.scheduleRebuild(); await this.core.rebuilder.scheduleRebuild();
await this.core.$$performRestart(); this.services.appLifecycle.performRestart();
return false; return false;
} else if (shouldRebuildLocal) { } else if (shouldRebuildLocal) {
await this.core.rebuilder.scheduleFetch(); await this.core.rebuilder.scheduleFetch();
await this.core.$$performRestart(); this.services.appLifecycle.performRestart();
return false; return false;
} }
} }
@@ -129,7 +129,7 @@ export class ModuleMigration extends AbstractModule implements ICoreModule {
if (!isValidPath(path)) { if (!isValidPath(path)) {
continue; continue;
} }
if (!(await this.core.$$isTargetFile(path, true))) { if (!(await this.services.vault.isTargetFile(path, true))) {
continue; continue;
} }
if (!isMetaEntry(metaDoc)) { if (!isMetaEntry(metaDoc)) {
@@ -257,9 +257,9 @@ export class ModuleMigration extends AbstractModule implements ICoreModule {
} }
// Check local database for compromised chunks // Check local database for compromised chunks
const localCompromised = await countCompromisedChunks(this.localDatabase.localDatabase); 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 const remoteCompromised = this.core.managers.networkManager.isOnline
? await remote.countCompromisedChunks() ? await remote?.countCompromisedChunks()
: 0; : 0;
if (localCompromised === false) { if (localCompromised === false) {
Logger(`Failed to count compromised chunks in local database`, LOG_LEVEL_NOTICE); 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) { if (result === REBUILD) {
// Rebuild the database // Rebuild the database
await this.core.rebuilder.scheduleRebuild(); await this.core.rebuilder.scheduleRebuild();
await this.core.$$performRestart(); this.services.appLifecycle.performRestart();
return false; return false;
} else if (result === FETCH) { } else if (result === FETCH) {
// Fetch the latest data from remote // Fetch the latest data from remote
await this.core.rebuilder.scheduleFetch(); await this.core.rebuilder.scheduleFetch();
await this.core.$$performRestart(); this.services.appLifecycle.performRestart();
return false; return false;
} else { } else {
// User chose to dismiss the issue // User chose to dismiss the issue
@@ -307,7 +307,7 @@ export class ModuleMigration extends AbstractModule implements ICoreModule {
return true; return true;
} }
async $everyOnFirstInitialize(): Promise<boolean> { async _everyOnFirstInitialize(): Promise<boolean> {
if (!this.localDatabase.isReady) { if (!this.localDatabase.isReady) {
this._log($msg("moduleMigration.logLocalDatabaseNotReady"), LOG_LEVEL_NOTICE); this._log($msg("moduleMigration.logLocalDatabaseNotReady"), LOG_LEVEL_NOTICE);
return false; return false;
@@ -337,7 +337,7 @@ export class ModuleMigration extends AbstractModule implements ICoreModule {
} }
return true; return true;
} }
$everyOnLayoutReady(): Promise<boolean> { _everyOnLayoutReady(): Promise<boolean> {
eventHub.onEvent(EVENT_REQUEST_RUN_DOCTOR, async (reason) => { eventHub.onEvent(EVENT_REQUEST_RUN_DOCTOR, async (reason) => {
await this.migrateUsingDoctor(false, reason, true); await this.migrateUsingDoctor(false, reason, true);
}); });
@@ -346,4 +346,9 @@ export class ModuleMigration extends AbstractModule implements ICoreModule {
}); });
return Promise.resolve(true); 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));
}
} }

View File

@@ -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 { 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 { 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 { getPathFromTFile } from "../../common/utils.ts";
import { isCloudantURI, isValidRemoteCouchDBURI } from "../../lib/src/pouchdb/utils_couchdb.ts"; import { isCloudantURI, isValidRemoteCouchDBURI } from "../../lib/src/pouchdb/utils_couchdb.ts";
import { replicationFilter } from "@/lib/src/pouchdb/compress.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 { ObsHttpHandler } from "./APILib/ObsHttpHandler.ts";
import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser.ts"; import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser.ts";
import { AuthorizationHeaderGenerator } from "../../lib/src/replication/httplib.ts"; import { AuthorizationHeaderGenerator } from "../../lib/src/replication/httplib.ts";
import type { LiveSyncCore } from "../../main.ts";
setNoticeClass(Notice); setNoticeClass(Notice);
@@ -19,17 +20,17 @@ async function fetchByAPI(request: RequestUrlParam, errorAsResult = false): Prom
return ret; return ret;
} }
export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidianModule { export class ModuleObsidianAPI extends AbstractObsidianModule {
_customHandler!: ObsHttpHandler; _customHandler!: ObsHttpHandler;
_authHeader = new AuthorizationHeaderGenerator(); _authHeader = new AuthorizationHeaderGenerator();
last_successful_post = false; last_successful_post = false;
$$customFetchHandler(): ObsHttpHandler { _customFetchHandler(): ObsHttpHandler {
if (!this._customHandler) this._customHandler = new ObsHttpHandler(undefined, undefined); if (!this._customHandler) this._customHandler = new ObsHttpHandler(undefined, undefined);
return this._customHandler; return this._customHandler;
} }
$$getLastPostFailedBySize(): boolean { _getLastPostFailedBySize(): boolean {
return !this.last_successful_post; return !this.last_successful_post;
} }
@@ -90,7 +91,7 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
} }
} }
async $$connectRemoteCouchDB( async _connectRemoteCouchDB(
uri: string, uri: string,
auth: CouchDBCredentials, auth: CouchDBCredentials,
disableRequestURI: boolean, disableRequestURI: boolean,
@@ -252,21 +253,21 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
} }
} }
$$isMobile(): boolean { _isMobile(): boolean {
//@ts-ignore : internal API //@ts-ignore : internal API
return this.app.isMobile; return this.app.isMobile;
} }
$$vaultName(): string { _vaultName(): string {
return this.app.vault.getName(); return this.app.vault.getName();
} }
$$getVaultName(): string { _getVaultName(): string {
return ( return (
this.core.$$vaultName() + this.services.vault.vaultName() +
(this.settings?.additionalSuffixOfDatabaseName ? "-" + this.settings.additionalSuffixOfDatabaseName : "") (this.settings?.additionalSuffixOfDatabaseName ? "-" + this.settings.additionalSuffixOfDatabaseName : "")
); );
} }
$$getActiveFilePath(): FilePathWithPrefix | undefined { _getActiveFilePath(): FilePath | undefined {
const file = this.app.workspace.getActiveFile(); const file = this.app.workspace.getActiveFile();
if (file) { if (file) {
return getPathFromTFile(file); return getPathFromTFile(file);
@@ -274,7 +275,18 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
return undefined; return undefined;
} }
$anyGetAppId(): Promise<string | undefined> { _anyGetAppId(): string {
return Promise.resolve(`${"appId" in this.app ? this.app.appId : ""}`); 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));
} }
} }

View File

@@ -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 { 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 { LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
import { scheduleTask } from "octagonal-wheels/concurrency/task"; import { scheduleTask } from "octagonal-wheels/concurrency/task";
@@ -12,9 +12,10 @@ import {
hiddenFilesEventCount, hiddenFilesEventCount,
hiddenFilesProcessingCount, hiddenFilesProcessingCount,
} from "../../lib/src/mock_and_interop/stores.ts"; } from "../../lib/src/mock_and_interop/stores.ts";
import type { LiveSyncCore } from "../../main.ts";
export class ModuleObsidianEvents extends AbstractObsidianModule implements IObsidianModule { export class ModuleObsidianEvents extends AbstractObsidianModule {
$everyOnloadStart(): Promise<boolean> { _everyOnloadStart(): Promise<boolean> {
// this.registerEvent(this.app.workspace.on("editor-change", )); // this.registerEvent(this.app.workspace.on("editor-change", ));
this.plugin.registerEvent( this.plugin.registerEvent(
this.app.vault.on("rename", (file, oldPath) => { this.app.vault.on("rename", (file, oldPath) => {
@@ -30,7 +31,7 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs
return Promise.resolve(true); return Promise.resolve(true);
} }
$$performRestart(): void { private _performRestart(): void {
this._performAppReload(); this._performAppReload();
} }
@@ -49,14 +50,14 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs
this.initialCallback = save; this.initialCallback = save;
saveCommandDefinition.callback = () => { saveCommandDefinition.callback = () => {
scheduleTask("syncOnEditorSave", 250, () => { scheduleTask("syncOnEditorSave", 250, () => {
if (this.core.$$isUnloaded()) { if (this.services.appLifecycle.hasUnloaded()) {
this._log("Unload and remove the handler.", LOG_LEVEL_VERBOSE); this._log("Unload and remove the handler.", LOG_LEVEL_VERBOSE);
saveCommandDefinition.callback = this.initialCallback; saveCommandDefinition.callback = this.initialCallback;
this.initialCallback = undefined; this.initialCallback = undefined;
} else { } else {
if (this.settings.syncOnEditorSave) { if (this.settings.syncOnEditorSave) {
this._log("Sync on Editor Save.", LOG_LEVEL_VERBOSE); 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. // TODO:FIXME AT V0.17.31, this logic has been disabled.
if (navigator.onLine && this.localDatabase.needScanning) { if (navigator.onLine && this.localDatabase.needScanning) {
this.localDatabase.needScanning = false; this.localDatabase.needScanning = false;
await this.core.$$performFullScan(); await this.services.vault.scanVault();
} }
} }
async watchWindowVisibilityAsync() { async watchWindowVisibilityAsync() {
if (this.settings.suspendFileWatching) return; if (this.settings.suspendFileWatching) return;
if (!this.settings.isConfigured) return; if (!this.settings.isConfigured) return;
if (!this.core.$$isReady()) return; if (!this.services.appLifecycle.isReady()) return;
if (this.isLastHidden && !this.hasFocus) { if (this.isLastHidden && !this.hasFocus) {
// NO OP while non-focused after made hidden; // NO OP while non-focused after made hidden;
@@ -126,22 +127,22 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs
} }
this.isLastHidden = isHidden; this.isLastHidden = isHidden;
await this.core.$everyCommitPendingFileEvent(); await this.services.fileProcessing.commitPendingFileEvents();
if (isHidden) { if (isHidden) {
await this.core.$everyBeforeSuspendProcess(); await this.services.appLifecycle.onSuspending();
} else { } else {
// suspend all temporary. // suspend all temporary.
if (this.core.$$isSuspended()) return; if (this.services.appLifecycle.isSuspended()) return;
if (!this.hasFocus) return; if (!this.hasFocus) return;
await this.core.$everyOnResumeProcess(); await this.services.appLifecycle.onResuming();
await this.core.$everyAfterResumeProcess(); await this.services.appLifecycle.onResumed();
} }
} }
watchWorkspaceOpen(file: TFile | null) { watchWorkspaceOpen(file: TFile | null) {
if (this.settings.suspendFileWatching) return; if (this.settings.suspendFileWatching) return;
if (!this.settings.isConfigured) return; if (!this.settings.isConfigured) return;
if (!this.core.$$isReady()) return; if (!this.services.appLifecycle.isReady()) return;
if (!file) return; if (!file) return;
scheduleTask("watch-workspace-open", 500, () => fireAndForget(() => this.watchWorkspaceOpenAsync(file))); scheduleTask("watch-workspace-open", 500, () => fireAndForget(() => this.watchWorkspaceOpenAsync(file)));
} }
@@ -149,25 +150,25 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs
async watchWorkspaceOpenAsync(file: TFile) { async watchWorkspaceOpenAsync(file: TFile) {
if (this.settings.suspendFileWatching) return; if (this.settings.suspendFileWatching) return;
if (!this.settings.isConfigured) return; if (!this.settings.isConfigured) return;
if (!this.core.$$isReady()) return; if (!this.services.appLifecycle.isReady()) return;
await this.core.$everyCommitPendingFileEvent(); await this.services.fileProcessing.commitPendingFileEvents();
if (file == null) { if (file == null) {
return; return;
} }
if (this.settings.syncOnFileOpen && !this.core.$$isSuspended()) { if (this.settings.syncOnFileOpen && !this.services.appLifecycle.isSuspended()) {
await this.core.$$replicateByEvent(); await this.services.replication.replicateByEvent();
} }
await this.core.$$queueConflictCheckIfOpen(file.path as FilePathWithPrefix); await this.services.conflict.queueCheckForIfOpen(file.path as FilePathWithPrefix);
} }
$everyOnLayoutReady(): Promise<boolean> { _everyOnLayoutReady(): Promise<boolean> {
this.swapSaveCommand(); this.swapSaveCommand();
this.registerWatchEvents(); this.registerWatchEvents();
return Promise.resolve(true); return Promise.resolve(true);
} }
$$askReload(message?: string) { private _askReload(message?: string) {
if (this.core.$$isReloadingScheduled()) { if (this.services.appLifecycle.isReloadingScheduled()) {
this._log(`Reloading is already scheduled`, LOG_LEVEL_VERBOSE); this._log(`Reloading is already scheduled`, LOG_LEVEL_VERBOSE);
return; return;
} }
@@ -183,11 +184,11 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs
if (ret == RESTART_NOW) { if (ret == RESTART_NOW) {
this._performAppReload(); this._performAppReload();
} else if (ret == RESTART_AFTER_STABLE) { } else if (ret == RESTART_AFTER_STABLE) {
this.core.$$scheduleAppReload(); this.services.appLifecycle.scheduleRestart();
} }
}); });
} }
$$scheduleAppReload() { private _scheduleAppReload() {
if (!this.core._totalProcessingCount) { if (!this.core._totalProcessingCount) {
const __tick = reactiveSource(0); const __tick = reactiveSource(0);
this.core._totalProcessingCount = reactive(() => { 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));
}
} }

View File

@@ -1,11 +1,12 @@
import { fireAndForget } from "octagonal-wheels/promises"; import { fireAndForget } from "octagonal-wheels/promises";
import { addIcon, type Editor, type MarkdownFileInfo, type MarkdownView } from "../../deps.ts"; import { addIcon, type Editor, type MarkdownFileInfo, type MarkdownView } from "../../deps.ts";
import { LOG_LEVEL_NOTICE, type FilePathWithPrefix } from "../../lib/src/common/types.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 { $msg } from "src/lib/src/common/i18n.ts";
import type { LiveSyncCore } from "../../main.ts";
export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsidianModule { export class ModuleObsidianMenu extends AbstractObsidianModule {
$everyOnloadStart(): Promise<boolean> { _everyOnloadStart(): Promise<boolean> {
// UI // UI
addIcon( addIcon(
"replicate", "replicate",
@@ -18,21 +19,21 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid
); );
this.addRibbonIcon("replicate", $msg("moduleObsidianMenu.replicate"), async () => { this.addRibbonIcon("replicate", $msg("moduleObsidianMenu.replicate"), async () => {
await this.core.$$replicate(true); await this.services.replication.replicate(true);
}).addClass("livesync-ribbon-replicate"); }).addClass("livesync-ribbon-replicate");
this.addCommand({ this.addCommand({
id: "livesync-replicate", id: "livesync-replicate",
name: "Replicate now", name: "Replicate now",
callback: async () => { callback: async () => {
await this.core.$$replicate(); await this.services.replication.replicate();
}, },
}); });
this.addCommand({ this.addCommand({
id: "livesync-dump", id: "livesync-dump",
name: "Dump information of this doc ", name: "Dump information of this doc ",
callback: () => { callback: () => {
const file = this.core.$$getActiveFilePath(); const file = this.services.vault.getActiveFilePath();
if (!file) return; if (!file) return;
fireAndForget(() => this.localDatabase.getDBEntry(file, {}, true, false)); fireAndForget(() => this.localDatabase.getDBEntry(file, {}, true, false));
}, },
@@ -43,7 +44,7 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid
editorCallback: (editor: Editor, view: MarkdownView | MarkdownFileInfo) => { editorCallback: (editor: Editor, view: MarkdownView | MarkdownFileInfo) => {
const file = view.file; const file = view.file;
if (!file) return; 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.settings.liveSync = true;
this._log("LiveSync Enabled.", LOG_LEVEL_NOTICE); this._log("LiveSync Enabled.", LOG_LEVEL_NOTICE);
} }
await this.core.$$realizeSettingSyncMode(); await this.services.setting.onRealiseSetting();
await this.core.$$saveSettingData(); await this.services.setting.saveSettingData();
}, },
}); });
this.addCommand({ this.addCommand({
id: "livesync-suspendall", id: "livesync-suspendall",
name: "Toggle All Sync.", name: "Toggle All Sync.",
callback: async () => { callback: async () => {
if (this.core.$$isSuspended()) { if (this.services.appLifecycle.isSuspended()) {
this.core.$$setSuspended(false); this.services.appLifecycle.setSuspended(false);
this._log("Self-hosted LiveSync resumed", LOG_LEVEL_NOTICE); this._log("Self-hosted LiveSync resumed", LOG_LEVEL_NOTICE);
} else { } else {
this.core.$$setSuspended(true); this.services.appLifecycle.setSuspended(true);
this._log("Self-hosted LiveSync suspended", LOG_LEVEL_NOTICE); this._log("Self-hosted LiveSync suspended", LOG_LEVEL_NOTICE);
} }
await this.core.$$realizeSettingSyncMode(); await this.services.setting.onRealiseSetting();
await this.core.$$saveSettingData(); await this.services.setting.saveSettingData();
}, },
}); });
@@ -82,7 +83,7 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid
id: "livesync-scan-files", id: "livesync-scan-files",
name: "Scan storage and database again", name: "Scan storage and database again",
callback: async () => { 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", id: "livesync-runbatch",
name: "Run pended batch processes", name: "Run pended batch processes",
callback: async () => { 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); return Promise.resolve(true);
} }
$everyOnload(): Promise<boolean> { _onWorkspaceReady() {
this.app.workspace.onLayoutReady(this.core.$$onLiveSyncReady.bind(this.core)); void this.services.appLifecycle.onReady();
}
private _everyOnload(): Promise<boolean> {
this.app.workspace.onLayoutReady(this._onWorkspaceReady.bind(this));
return Promise.resolve(true); return Promise.resolve(true);
} }
async $$showView(viewType: string) { private async _showView(viewType: string) {
const leaves = this.app.workspace.getLeavesOfType(viewType); const leaves = this.app.workspace.getLeavesOfType(viewType);
if (leaves.length == 0) { if (leaves.length == 0) {
await this.app.workspace.getLeaf(true).setViewState({ 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]); 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));
}
} }

View File

@@ -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 = ""; deviceAndVaultName: string = "";
$$getDeviceAndVaultName(): string { _getDeviceAndVaultName(): string {
return this.deviceAndVaultName; return this.deviceAndVaultName;
} }
$$setDeviceAndVaultName(name: string): void { _setDeviceAndVaultName(name: string): void {
this.deviceAndVaultName = name; 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));
}
} }

View File

@@ -1,15 +1,16 @@
import { delay, fireAndForget } from "octagonal-wheels/promises"; import { delay, fireAndForget } from "octagonal-wheels/promises";
import { __onMissingTranslation } from "../../lib/src/common/i18n"; 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 { LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
import { eventHub } from "../../common/events"; import { eventHub } from "../../common/events";
import { enableTestFunction } from "./devUtil/testUtils.ts"; import { enableTestFunction } from "./devUtil/testUtils.ts";
import { TestPaneView, VIEW_TYPE_TEST } from "./devUtil/TestPaneView.ts"; import { TestPaneView, VIEW_TYPE_TEST } from "./devUtil/TestPaneView.ts";
import { writable } from "svelte/store"; import { writable } from "svelte/store";
import type { FilePathWithPrefix } from "../../lib/src/common/types.ts"; import type { FilePathWithPrefix } from "../../lib/src/common/types.ts";
import type { LiveSyncCore } from "../../main.ts";
export class ModuleDev extends AbstractObsidianModule implements IObsidianModule { export class ModuleDev extends AbstractObsidianModule {
$everyOnloadStart(): Promise<boolean> { _everyOnloadStart(): Promise<boolean> {
__onMissingTranslation(() => {}); __onMissingTranslation(() => {});
return Promise.resolve(true); return Promise.resolve(true);
} }
@@ -35,7 +36,7 @@ export class ModuleDev extends AbstractObsidianModule implements IObsidianModule
} }
} }
$everyOnloadAfterLoadSettings(): Promise<boolean> { private _everyOnloadAfterLoadSettings(): Promise<boolean> {
if (!this.settings.enableDebugTools) return Promise.resolve(true); if (!this.settings.enableDebugTools) return Promise.resolve(true);
this.onMissingTranslation = this.onMissingTranslation.bind(this); this.onMissingTranslation = this.onMissingTranslation.bind(this);
__onMissingTranslation((key) => { __onMissingTranslation((key) => {
@@ -92,12 +93,12 @@ export class ModuleDev extends AbstractObsidianModule implements IObsidianModule
id: "view-test", id: "view-test",
name: "Open Test dialogue", name: "Open Test dialogue",
callback: () => { callback: () => {
void this.core.$$showView(VIEW_TYPE_TEST); void this.services.API.showWindow(VIEW_TYPE_TEST);
}, },
}); });
return Promise.resolve(true); return Promise.resolve(true);
} }
async $everyOnLayoutReady(): Promise<boolean> { async _everyOnLayoutReady(): Promise<boolean> {
if (!this.settings.enableDebugTools) return Promise.resolve(true); if (!this.settings.enableDebugTools) return Promise.resolve(true);
// if (await this.core.storageAccess.isExistsIncludeHidden("_SHOWDIALOGAUTO.md")) { // if (await this.core.storageAccess.isExistsIncludeHidden("_SHOWDIALOGAUTO.md")) {
// void this.core.$$showView(VIEW_TYPE_TEST); // void this.core.$$showView(VIEW_TYPE_TEST);
@@ -121,7 +122,7 @@ export class ModuleDev extends AbstractObsidianModule implements IObsidianModule
}, },
}); });
if (w) { 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); const f = await this.core.localDatabase.getRaw(id);
console.log(f); console.log(f);
console.log(f._rev); console.log(f._rev);
@@ -139,14 +140,14 @@ export class ModuleDev extends AbstractObsidianModule implements IObsidianModule
testResults = writable<[boolean, string, string][]>([]); testResults = writable<[boolean, string, string][]>([]);
// testResults: 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 ?? ""}`; const logLine = `${name}: ${key} ${summary ?? ""}`;
this.testResults.update((results) => { this.testResults.update((results) => {
results.push([result, logLine, message ?? ""]); results.push([result, logLine, message ?? ""]);
return results; return results;
}); });
} }
$everyModuleTest(): Promise<boolean> { private _everyModuleTest(): Promise<boolean> {
if (!this.settings.enableDebugTools) return Promise.resolve(true); if (!this.settings.enableDebugTools) return Promise.resolve(true);
// this.core.$$addTestResult("DevModule", "Test", true); // this.core.$$addTestResult("DevModule", "Test", true);
// return Promise.resolve(true); // return Promise.resolve(true);
@@ -155,4 +156,11 @@ export class ModuleDev extends AbstractObsidianModule implements IObsidianModule
// this.addTestResult("Test of test3", true); // this.addTestResult("Test of test3", true);
return this.testDone(); 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));
}
} }

View File

@@ -1,9 +1,9 @@
import { delay } from "octagonal-wheels/promises"; import { delay } from "octagonal-wheels/promises";
import { LOG_LEVEL_NOTICE, REMOTE_MINIO, type FilePathWithPrefix } from "src/lib/src/common/types"; import { LOG_LEVEL_NOTICE, REMOTE_MINIO, type FilePathWithPrefix } from "src/lib/src/common/types";
import { shareRunningResult } from "octagonal-wheels/concurrency/lock"; 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<boolean>, timeout = 10000): Promise<boolean> { async waitFor(proc: () => Promise<boolean>, timeout = 10000): Promise<boolean> {
await delay(100); await delay(100);
const start = Date.now(); const start = Date.now();
@@ -54,7 +54,7 @@ export class ModuleIntegratedTest extends AbstractObsidianModule implements IObs
tryReplicate() { tryReplicate() {
if (!this.settings.liveSync) { if (!this.settings.liveSync) {
return shareRunningResult("replicate-test", async () => { 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 stepFile = "_STEP.md" as FilePathWithPrefix;
const stepAckFile = "_STEP_ACK.md" as FilePathWithPrefix; const stepAckFile = "_STEP_ACK.md" as FilePathWithPrefix;
const stepContent = `Step ${no}`; const stepContent = `Step ${no}`;
await this.core.$anyResolveConflictByNewest(stepFile); await this.services.conflict.resolveByNewest(stepFile);
await this.core.storageAccess.writeFileAuto(stepFile, stepContent); await this.core.storageAccess.writeFileAuto(stepFile, stepContent);
await this._orDie(`Wait for acknowledge ${no}`, async () => { await this._orDie(`Wait for acknowledge ${no}`, async () => {
if ( if (
@@ -96,7 +96,7 @@ export class ModuleIntegratedTest extends AbstractObsidianModule implements IObs
return false; return false;
return true; return true;
}); });
await this.core.$anyResolveConflictByNewest(stepAckFile); await this.services.conflict.resolveByNewest(stepAckFile);
await this.core.storageAccess.writeFileAuto(stepAckFile, stepContent); await this.core.storageAccess.writeFileAuto(stepAckFile, stepContent);
await this.tryReplicate(); await this.tryReplicate();
return true; return true;
@@ -424,9 +424,9 @@ Line4:D`;
await this._test("basic", async () => await this.nonLiveTestRunner(isLeader, (t, l) => this.testBasic(t, l))); await this._test("basic", async () => await this.nonLiveTestRunner(isLeader, (t, l) => this.testBasic(t, l)));
} }
async $everyModuleTestMultiDevice(): Promise<boolean> { async _everyModuleTestMultiDevice(): Promise<boolean> {
if (!this.settings.enableDebugTools) return Promise.resolve(true); 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"}`); this.addTestResult("-------", true, `Test as ${isLeader ? "Leader" : "Receiver"}`);
try { try {
this._log(`Starting Test`); this._log(`Starting Test`);
@@ -440,4 +440,7 @@ Line4:D`;
return Promise.resolve(true); return Promise.resolve(true);
} }
onBindFunction(core: typeof this.core, services: typeof core.services): void {
services.test.handleTestMultiDevice(this._everyModuleTestMultiDevice.bind(this));
}
} }

View File

@@ -1,5 +1,5 @@
import { delay } from "octagonal-wheels/promises"; 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 { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
import { eventHub } from "../../common/events"; import { eventHub } from "../../common/events";
import { getWebCrypto } from "../../lib/src/mods.ts"; 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 type { FilePath } from "../../lib/src/common/types.ts";
import { scheduleTask } from "octagonal-wheels/concurrency/task"; import { scheduleTask } from "octagonal-wheels/concurrency/task";
import { getFileRegExp } from "../../lib/src/common/utils.ts"; import { getFileRegExp } from "../../lib/src/common/utils.ts";
import type { LiveSyncCore } from "../../main.ts";
declare global { declare global {
interface LSEvents { interface LSEvents {
@@ -15,12 +16,15 @@ declare global {
} }
} }
export class ModuleReplicateTest extends AbstractObsidianModule implements IObsidianModule { export class ModuleReplicateTest extends AbstractObsidianModule {
testRootPath = "_test/"; testRootPath = "_test/";
testInfoPath = "_testinfo/"; testInfoPath = "_testinfo/";
get isLeader() { 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() { get nameByKind() {
@@ -58,12 +62,12 @@ export class ModuleReplicateTest extends AbstractObsidianModule implements IObsi
await this._dumpFileList("files.md"); await this._dumpFileList("files.md");
} }
} }
async $everyBeforeReplicate(showMessage: boolean): Promise<boolean> { async _everyBeforeReplicate(showMessage: boolean): Promise<boolean> {
if (!this.settings.enableDebugTools) return Promise.resolve(true); if (!this.settings.enableDebugTools) return Promise.resolve(true);
await this.dumpList(); await this.dumpList();
return true; return true;
} }
$everyOnloadAfterLoadSettings(): Promise<boolean> { private _everyOnloadAfterLoadSettings(): Promise<boolean> {
if (!this.settings.enableDebugTools) return Promise.resolve(true); if (!this.settings.enableDebugTools) return Promise.resolve(true);
this.addCommand({ this.addCommand({
id: "dump-file-structure-normal", id: "dump-file-structure-normal",
@@ -169,7 +173,7 @@ export class ModuleReplicateTest extends AbstractObsidianModule implements IObsi
const out = [] as any[]; const out = [] as any[];
const webcrypto = await getWebCrypto(); const webcrypto = await getWebCrypto();
for (const file of files) { for (const file of files) {
if (!(await this.core.$$isTargetFile(file.path))) { if (!(await this.services.vault.isTargetFile(file.path))) {
continue; continue;
} }
if (file.path.startsWith(this.testInfoPath)) continue; if (file.path.startsWith(this.testInfoPath)) continue;
@@ -316,7 +320,7 @@ export class ModuleReplicateTest extends AbstractObsidianModule implements IObsi
} }
async testConflictedManually1() { async testConflictedManually1() {
await this.core.$$replicate(); await this.services.replication.replicate();
const commonFile = `Resolve! const commonFile = `Resolve!
*****, the amazing chocolatier!!`; *****, 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.storageAccess.writeHiddenFileAuto(this.testRootPath + "wonka.md", commonFile);
} }
await this.core.$$replicate(); await this.services.replication.replicate();
await this.core.$$replicate(); await this.services.replication.replicate();
if ( if (
(await this.core.confirm.askYesNoDialog("Ready to begin the test conflict Manually 1?", { (await this.core.confirm.askYesNoDialog("Ready to begin the test conflict Manually 1?", {
timeout: 30, timeout: 30,
@@ -356,12 +360,12 @@ Charlie Bucket, Charlie Bucket, the amazing chocolatier!!`;
) { ) {
return; return;
} }
await this.core.$$replicate(); await this.services.replication.replicate();
await this.core.$$replicate(); await this.services.replication.replicate();
if ( if (
!(await this.waitFor(async () => { !(await this.waitFor(async () => {
await this.core.$$replicate(); await this.services.replication.replicate();
return ( return (
(await this.__assertStorageContent( (await this.__assertStorageContent(
(this.testRootPath + "wonka.md") as FilePath, (this.testRootPath + "wonka.md") as FilePath,
@@ -379,7 +383,7 @@ Charlie Bucket, Charlie Bucket, the amazing chocolatier!!`;
} }
async testConflictedManually2() { async testConflictedManually2() {
await this.core.$$replicate(); await this.services.replication.replicate();
const commonFile = `Resolve To concatenate const commonFile = `Resolve To concatenate
ABCDEFG`; ABCDEFG`;
@@ -388,8 +392,8 @@ ABCDEFG`;
await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "concat.md", commonFile); await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "concat.md", commonFile);
} }
await this.core.$$replicate(); await this.services.replication.replicate();
await this.core.$$replicate(); await this.services.replication.replicate();
if ( if (
(await this.core.confirm.askYesNoDialog("Ready to begin the test conflict Manually 2?", { (await this.core.confirm.askYesNoDialog("Ready to begin the test conflict Manually 2?", {
timeout: 30, timeout: 30,
@@ -420,12 +424,12 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ`;
) { ) {
return; return;
} }
await this.core.$$replicate(); await this.services.replication.replicate();
await this.core.$$replicate(); await this.services.replication.replicate();
if ( if (
!(await this.waitFor(async () => { !(await this.waitFor(async () => {
await this.core.$$replicate(); await this.services.replication.replicate();
return ( return (
(await this.__assertStorageContent( (await this.__assertStorageContent(
(this.testRootPath + "concat.md") as FilePath, (this.testRootPath + "concat.md") as FilePath,
@@ -457,8 +461,8 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ`;
await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "task.md", baseDoc); await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "task.md", baseDoc);
} }
await delay(100); await delay(100);
await this.core.$$replicate(); await this.services.replication.replicate();
await this.core.$$replicate(); await this.services.replication.replicate();
if ( if (
(await this.core.confirm.askYesNoDialog("Ready to test conflict?", { (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.storageAccess.writeHiddenFileAuto(this.testRootPath + "task.md", mod2Doc);
} }
await this.core.$$replicate(); await this.services.replication.replicate();
await this.core.$$replicate(); await this.services.replication.replicate();
await delay(1000); await delay(1000);
if ( if (
(await this.core.confirm.askYesNoDialog("Ready to check result?", { timeout: 30, defaultOption: "Yes" })) == (await this.core.confirm.askYesNoDialog("Ready to check result?", { timeout: 30, defaultOption: "Yes" })) ==
@@ -496,8 +500,8 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ`;
) { ) {
return; return;
} }
await this.core.$$replicate(); await this.services.replication.replicate();
await this.core.$$replicate(); await this.services.replication.replicate();
const mergedDoc = `Tasks! const mergedDoc = `Tasks!
- [ ] Task 1 - [ ] Task 1
- [v] Task 2 - [v] Task 2
@@ -511,7 +515,7 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ`;
this._log("Before testing conflicted files, resolve all once", LOG_LEVEL_NOTICE); 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.rebuilder.resolveAllConflictedFilesByNewerOnes(); await this.core.rebuilder.resolveAllConflictedFilesByNewerOnes();
await this.core.$$replicate(); await this.services.replication.replicate();
await delay(1000); await delay(1000);
if (!(await this.testConflictAutomatic())) { if (!(await this.testConflictAutomatic())) {
this._log("Conflict resolution (Auto) failed", LOG_LEVEL_NOTICE); this._log("Conflict resolution (Auto) failed", LOG_LEVEL_NOTICE);
@@ -569,11 +573,16 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ`;
// return results; // return results;
// }); // });
// } // }
async $everyModuleTestMultiDevice(): Promise<boolean> { private async _everyModuleTestMultiDevice(): Promise<boolean> {
if (!this.settings.enableDebugTools) return Promise.resolve(true); if (!this.settings.enableDebugTools) return Promise.resolve(true);
// this.core.$$addTestResult("DevModule", "Test", true); // this.core.$$addTestResult("DevModule", "Test", true);
// return Promise.resolve(true); // return Promise.resolve(true);
await this._test("Conflict resolution", async () => await this.checkConflictResolution()); await this._test("Conflict resolution", async () => await this.checkConflictResolution());
return this.testDone(); 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));
}
} }

View File

@@ -57,14 +57,14 @@
function moduleMultiDeviceTest() { function moduleMultiDeviceTest() {
if (moduleTesting) return; if (moduleTesting) return;
moduleTesting = true; moduleTesting = true;
plugin.$everyModuleTestMultiDevice().finally(() => { plugin.services.test.testMultiDevice().finally(() => {
moduleTesting = false; moduleTesting = false;
}); });
} }
function moduleSingleDeviceTest() { function moduleSingleDeviceTest() {
if (moduleTesting) return; if (moduleTesting) return;
moduleTesting = true; moduleTesting = true;
plugin.$everyModuleTest().finally(() => { plugin.services.test.test().finally(() => {
moduleTesting = false; moduleTesting = false;
}); });
} }
@@ -72,8 +72,8 @@
if (moduleTesting) return; if (moduleTesting) return;
moduleTesting = true; moduleTesting = true;
try { try {
await plugin.$everyModuleTest(); await plugin.services.test.test();
await plugin.$everyModuleTestMultiDevice(); await plugin.services.test.testMultiDevice();
} finally { } finally {
moduleTesting = false; moduleTesting = false;
} }

View File

@@ -46,6 +46,9 @@ function readDocument(w: LoadedEntry) {
} }
export class DocumentHistoryModal extends Modal { export class DocumentHistoryModal extends Modal {
plugin: ObsidianLiveSyncPlugin; plugin: ObsidianLiveSyncPlugin;
get services() {
return this.plugin.services;
}
range!: HTMLInputElement; range!: HTMLInputElement;
contentView!: HTMLDivElement; contentView!: HTMLDivElement;
info!: HTMLDivElement; info!: HTMLDivElement;
@@ -74,7 +77,7 @@ export class DocumentHistoryModal extends Modal {
this.id = id; this.id = id;
this.initialRev = revision; this.initialRev = revision;
if (!file && id) { if (!file && id) {
this.file = this.plugin.$$id2path(id); this.file = this.services.path.id2path(id);
} }
if (localStorage.getItem("ols-history-highlightdiff") == "1") { if (localStorage.getItem("ols-history-highlightdiff") == "1") {
this.showDiff = true; this.showDiff = true;
@@ -83,7 +86,7 @@ export class DocumentHistoryModal extends Modal {
async loadFile(initialRev?: string) { async loadFile(initialRev?: string) {
if (!this.id) { 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; const db = this.plugin.localDatabase;
try { try {
@@ -126,7 +129,7 @@ export class DocumentHistoryModal extends Modal {
} }
this.BlobURLs.delete(key); this.BlobURLs.delete(key);
} }
generateBlobURL(key: string, data: Uint8Array) { generateBlobURL(key: string, data: Uint8Array<ArrayBuffer>) {
this.revokeURL(key); this.revokeURL(key);
const v = URL.createObjectURL(new Blob([data], { endings: "transparent", type: "application/octet-stream" })); const v = URL.createObjectURL(new Blob([data], { endings: "transparent", type: "application/octet-stream" }));
this.BlobURLs.set(key, v); this.BlobURLs.set(key, v);
@@ -175,7 +178,10 @@ export class DocumentHistoryModal extends Modal {
result = result.replace(/\n/g, "<br>"); result = result.replace(/\n/g, "<br>");
} else if (isImage(this.file)) { } else if (isImage(this.file)) {
const src = this.generateBlobURL("base", w1data); const src = this.generateBlobURL("base", w1data);
const overlay = this.generateBlobURL("overlay", readDocument(w2) as Uint8Array); const overlay = this.generateBlobURL(
"overlay",
readDocument(w2) as Uint8Array<ArrayBuffer>
);
result = `<div class='ls-imgdiff-wrap'> result = `<div class='ls-imgdiff-wrap'>
<div class='overlay'> <div class='overlay'>
<img class='img-base' src="${src}"> <img class='img-base' src="${src}">

View File

@@ -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"; import { VIEW_TYPE_GLOBAL_HISTORY, GlobalHistoryView } from "./GlobalHistory/GlobalHistoryView.ts";
export class ModuleObsidianGlobalHistory extends AbstractObsidianModule implements IObsidianModule { export class ModuleObsidianGlobalHistory extends AbstractObsidianModule {
$everyOnloadStart(): Promise<boolean> { _everyOnloadStart(): Promise<boolean> {
this.addCommand({ this.addCommand({
id: "livesync-global-history", id: "livesync-global-history",
name: "Show vault history", name: "Show vault history",
@@ -17,6 +17,9 @@ export class ModuleObsidianGlobalHistory extends AbstractObsidianModule implemen
} }
showGlobalHistory() { 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));
} }
} }

View File

@@ -10,13 +10,14 @@ import {
type diff_result, type diff_result,
} from "../../lib/src/common/types.ts"; } from "../../lib/src/common/types.ts";
import { ConflictResolveModal } from "./InteractiveConflictResolving/ConflictResolveModal.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 { displayRev, getPath, getPathWithoutPrefix } from "../../common/utils.ts";
import { fireAndForget } from "octagonal-wheels/promises"; import { fireAndForget } from "octagonal-wheels/promises";
import { serialized } from "octagonal-wheels/concurrency/lock"; import { serialized } from "octagonal-wheels/concurrency/lock";
import type { LiveSyncCore } from "../../main.ts";
export class ModuleInteractiveConflictResolver extends AbstractObsidianModule implements IObsidianModule { export class ModuleInteractiveConflictResolver extends AbstractObsidianModule {
$everyOnloadStart(): Promise<boolean> { _everyOnloadStart(): Promise<boolean> {
this.addCommand({ this.addCommand({
id: "livesync-conflictcheck", id: "livesync-conflictcheck",
name: "Pick a file to resolve conflict", name: "Pick a file to resolve conflict",
@@ -34,7 +35,7 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
return Promise.resolve(true); return Promise.resolve(true);
} }
async $anyResolveConflictByUI(filename: FilePathWithPrefix, conflictCheckResult: diff_result): Promise<boolean> { async _anyResolveConflictByUI(filename: FilePathWithPrefix, conflictCheckResult: diff_result): Promise<boolean> {
// UI for resolving conflicts should one-by-one. // UI for resolving conflicts should one-by-one.
return await serialized(`conflict-resolve-ui`, async () => { return await serialized(`conflict-resolve-ui`, async () => {
this._log("Merge:open conflict dialog", LOG_LEVEL_VERBOSE); 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. // 2. As usual, delete the conflicted revision and if there are no conflicts, write the resolved content to the storage.
if ( if (
(await this.core.$$resolveConflictByDeletingRev(filename, delRev, "UI Concatenated")) == (await this.services.conflict.resolveByDeletingRevision(filename, delRev, "UI Concatenated")) ==
MISSING_OR_ERROR MISSING_OR_ERROR
) { ) {
this._log( this._log(
@@ -80,7 +81,7 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
} else if (typeof toDelete === "string") { } else if (typeof toDelete === "string") {
// Select one of the conflicted revision to delete. // Select one of the conflicted revision to delete.
if ( if (
(await this.core.$$resolveConflictByDeletingRev(filename, toDelete, "UI Selected")) == (await this.services.conflict.resolveByDeletingRevision(filename, toDelete, "UI Selected")) ==
MISSING_OR_ERROR MISSING_OR_ERROR
) { ) {
this._log(`Merge: Something went wrong: ${filename}, (${toDelete})`, LOG_LEVEL_NOTICE); 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. // In here, some merge has been processed.
// So we have to run replication if configured. // So we have to run replication if configured.
// TODO: Make this is as a event request // TODO: Make this is as a event request
if (this.settings.syncAfterMerge && !this.core.$$isSuspended()) { if (this.settings.syncAfterMerge && !this.services.appLifecycle.isSuspended()) {
await this.core.$$replicateByEvent(); await this.services.replication.replicateByEvent();
} }
// And, check it again. // And, check it again.
await this.core.$$queueConflictCheck(filename); await this.services.conflict.queueCheckFor(filename);
return false; return false;
}); });
} }
@@ -120,14 +121,14 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
const target = await this.core.confirm.askSelectString("File to resolve conflict", notesList); const target = await this.core.confirm.askSelectString("File to resolve conflict", notesList);
if (target) { if (target) {
const targetItem = notes.find((e) => e.dispPath == target)!; const targetItem = notes.find((e) => e.dispPath == target)!;
await this.core.$$queueConflictCheck(targetItem.path); await this.services.conflict.queueCheckFor(targetItem.path);
await this.core.$$waitForAllConflictProcessed(); await this.services.conflict.ensureAllProcessed();
return true; return true;
} }
return false; return false;
} }
async $allScanStat(): Promise<boolean> { async _allScanStat(): Promise<boolean> {
const notes: { path: string; mtime: number }[] = []; const notes: { path: string; mtime: number }[] = [];
this._log(`Checking conflicted files`, LOG_LEVEL_VERBOSE); this._log(`Checking conflicted files`, LOG_LEVEL_VERBOSE);
for await (const doc of this.localDatabase.findAllDocs({ conflicts: true })) { for await (const doc of this.localDatabase.findAllDocs({ conflicts: true })) {
@@ -157,4 +158,9 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
} }
return true; 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));
}
} }

View File

@@ -20,7 +20,7 @@ import {
} from "../../lib/src/mock_and_interop/stores.ts"; } from "../../lib/src/mock_and_interop/stores.ts";
import { eventHub } from "../../lib/src/hub/hub.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 { 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 { addIcon, normalizePath, Notice } from "../../deps.ts";
import { LOG_LEVEL_NOTICE, setGlobalLogFunction } from "octagonal-wheels/common/logger"; import { LOG_LEVEL_NOTICE, setGlobalLogFunction } from "octagonal-wheels/common/logger";
import { QueueProcessor } from "octagonal-wheels/concurrency/processor"; 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 { serialized } from "octagonal-wheels/concurrency/lock";
import { $msg } from "src/lib/src/common/i18n.ts"; import { $msg } from "src/lib/src/common/i18n.ts";
import { P2PLogCollector } from "../../lib/src/replication/trystero/P2PReplicatorCore.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. // 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; const showDebugLog = false;
export const MARK_DONE = "\u{2009}\u{2009}"; 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); registerView = this.plugin.registerView.bind(this.plugin);
statusBar?: HTMLElement; statusBar?: HTMLElement;
@@ -178,7 +179,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
}); });
const statusBarLabels = reactive(() => { const statusBarLabels = reactive(() => {
const scheduleMessage = this.core.$$isReloadingScheduled() const scheduleMessage = this.services.appLifecycle.isReloadingScheduled()
? `WARNING! RESTARTING OBSIDIAN IS SCHEDULED!\n` ? `WARNING! RESTARTING OBSIDIAN IS SCHEDULED!\n`
: ""; : "";
const { message } = statusLineLabel(); const { message } = statusLineLabel();
@@ -199,7 +200,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
statusBarLabels.onChanged((label) => applyToDisplay(label.value)); statusBarLabels.onChanged((label) => applyToDisplay(label.value));
} }
$everyOnload(): Promise<boolean> { private _everyOnload(): Promise<boolean> {
eventHub.onEvent(EVENT_LEAF_ACTIVE_CHANGED, () => this.onActiveLeafChange()); eventHub.onEvent(EVENT_LEAF_ACTIVE_CHANGED, () => this.onActiveLeafChange());
eventHub.onceEvent(EVENT_LAYOUT_READY, () => 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(); const thisFile = this.app.workspace.getActiveFile();
if (!thisFile) return ""; if (!thisFile) return "";
// Case Sensitivity // Case Sensitivity
if (this.core.$$shouldCheckCaseInsensitive()) { if (this.services.setting.shouldCheckCaseInsensitively()) {
const f = this.core.storageAccess const f = this.core.storageAccess
.getFiles() .getFiles()
.map((e) => e.path) .map((e) => e.path)
.filter((e) => e.toLowerCase() == thisFile.path.toLowerCase()); .filter((e) => e.toLowerCase() == thisFile.path.toLowerCase());
if (f.length > 1) return "Not synchronised: There are multiple files with the same name"; 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 (!(await this.services.vault.isTargetFile(thisFile.path))) return "Not synchronised: not a target file";
if (this.core.$$isFileSizeExceeded(thisFile.stat.size)) return "Not synchronised: File size exceeded"; if (this.services.vault.isFileSizeTooLarge(thisFile.stat.size)) return "Not synchronised: File size exceeded";
return ""; return "";
} }
async setFileStatus() { async setFileStatus() {
@@ -287,14 +288,14 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
}); });
} }
$allStartOnUnload(): Promise<boolean> { private _allStartOnUnload(): Promise<boolean> {
if (this.statusDiv) { if (this.statusDiv) {
this.statusDiv.remove(); this.statusDiv.remove();
} }
document.querySelectorAll(`.livesync-status`)?.forEach((e) => e.remove()); document.querySelectorAll(`.livesync-status`)?.forEach((e) => e.remove());
return Promise.resolve(true); return Promise.resolve(true);
} }
$everyOnloadStart(): Promise<boolean> { _everyOnloadStart(): Promise<boolean> {
addIcon( addIcon(
"view-log", "view-log",
`<g transform="matrix(1.28 0 0 1.28 -131 -411)" fill="currentColor" fill-rule="evenodd"> `<g transform="matrix(1.28 0 0 1.28 -131 -411)" fill="currentColor" fill-rule="evenodd">
@@ -303,23 +304,23 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
</g>` </g>`
); );
this.addRibbonIcon("view-log", $msg("moduleLog.showLog"), () => { 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"); }).addClass("livesync-ribbon-showlog");
this.addCommand({ this.addCommand({
id: "view-log", id: "view-log",
name: "Show log", name: "Show log",
callback: () => { 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)); this.registerView(VIEW_TYPE_LOG, (leaf) => new LogPaneView(leaf, this.plugin));
return Promise.resolve(true); return Promise.resolve(true);
} }
$everyOnloadAfterLoadSettings(): Promise<boolean> { private _everyOnloadAfterLoadSettings(): Promise<boolean> {
logStore logStore
.pipeTo( .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, suspended: false,
batchSize: 20, batchSize: 20,
concurrentLimit: 1, 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) { if (level == LOG_LEVEL_DEBUG && !showDebugLog) {
return; return;
} }
@@ -376,7 +377,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
if (this.settings && !this.settings.showVerboseLog && level == LOG_LEVEL_VERBOSE) { if (this.settings && !this.settings.showVerboseLog && level == LOG_LEVEL_VERBOSE) {
return; return;
} }
const vaultName = this.core.$$getVaultName(); const vaultName = this.services.vault.getVaultName();
const now = new Date(); const now = new Date();
const timestamp = now.toLocaleString(); const timestamp = now.toLocaleString();
const messageContent = 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));
}
} }

View File

@@ -2,18 +2,18 @@ import { type TFile } from "obsidian";
import { eventHub } from "../../common/events.ts"; import { eventHub } from "../../common/events.ts";
import { EVENT_REQUEST_SHOW_HISTORY } from "../../common/obsidianEvents.ts"; import { EVENT_REQUEST_SHOW_HISTORY } from "../../common/obsidianEvents.ts";
import type { FilePathWithPrefix, LoadedEntry, DocumentID } from "../../lib/src/common/types.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 { DocumentHistoryModal } from "./DocumentHistory/DocumentHistoryModal.ts";
import { getPath } from "../../common/utils.ts"; import { getPath } from "../../common/utils.ts";
import { fireAndForget } from "octagonal-wheels/promises"; import { fireAndForget } from "octagonal-wheels/promises";
export class ModuleObsidianDocumentHistory extends AbstractObsidianModule implements IObsidianModule { export class ModuleObsidianDocumentHistory extends AbstractObsidianModule {
$everyOnloadStart(): Promise<boolean> { _everyOnloadStart(): Promise<boolean> {
this.addCommand({ this.addCommand({
id: "livesync-history", id: "livesync-history",
name: "Show history", name: "Show history",
callback: () => { callback: () => {
const file = this.core.$$getActiveFilePath(); const file = this.services.vault.getActiveFilePath();
if (file) this.showHistory(file, undefined); if (file) this.showHistory(file, undefined);
}, },
}); });
@@ -51,4 +51,7 @@ export class ModuleObsidianDocumentHistory extends AbstractObsidianModule implem
this.showHistory(targetId.path, targetId.id); this.showHistory(targetId.path, targetId.id);
} }
} }
onBindFunction(core: typeof this.core, services: typeof core.services): void {
services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this));
}
} }

View File

@@ -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 { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser";
import { EVENT_REQUEST_RELOAD_SETTING_TAB, EVENT_SETTING_SAVED, eventHub } from "../../common/events.ts"; import { EVENT_REQUEST_RELOAD_SETTING_TAB, EVENT_SETTING_SAVED, eventHub } from "../../common/events.ts";
import { import {
@@ -16,8 +16,9 @@ import { isCloudantURI } from "../../lib/src/pouchdb/utils_couchdb.ts";
import { getLanguage } from "obsidian"; import { getLanguage } from "obsidian";
import { SUPPORTED_I18N_LANGS, type I18N_LANGS } from "../../lib/src/common/rosetta.ts"; import { SUPPORTED_I18N_LANGS, type I18N_LANGS } from "../../lib/src/common/rosetta.ts";
import { decryptString, encryptString } from "@/lib/src/encryption/stringEncryption.ts"; import { decryptString, encryptString } from "@/lib/src/encryption/stringEncryption.ts";
export class ModuleObsidianSettings extends AbstractObsidianModule implements IObsidianModule { import type { LiveSyncCore } from "../../main.ts";
async $everyOnLayoutReady(): Promise<boolean> { export class ModuleObsidianSettings extends AbstractObsidianModule {
async _everyOnLayoutReady(): Promise<boolean> {
let isChanged = false; let isChanged = false;
if (this.settings.displayLanguage == "") { if (this.settings.displayLanguage == "") {
const obsidianLanguage = getLanguage(); const obsidianLanguage = getLanguage();
@@ -32,7 +33,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
} else if (this.settings.displayLanguage == "") { } else if (this.settings.displayLanguage == "") {
this.settings.displayLanguage = "def"; this.settings.displayLanguage = "def";
setLang(this.settings.displayLanguage); setLang(this.settings.displayLanguage);
await this.core.$$saveSettingData(); await this.services.setting.saveSettingData();
} }
} }
if (isChanged) { if (isChanged) {
@@ -46,7 +47,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
this.settings.displayLanguage = "def"; this.settings.displayLanguage = "def";
setLang(this.settings.displayLanguage); setLang(this.settings.displayLanguage);
} }
await this.core.$$saveSettingData(); await this.services.setting.saveSettingData();
} }
return true; return true;
} }
@@ -61,13 +62,13 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
return methodFunc(); return methodFunc();
} }
$$saveDeviceAndVaultName(): void { _saveDeviceAndVaultName(): void {
const lsKey = "obsidian-live-sync-vaultanddevicename-" + this.core.$$getVaultName(); const lsKey = "obsidian-live-sync-vaultanddevicename-" + this.services.vault.getVaultName();
localStorage.setItem(lsKey, this.core.$$getDeviceAndVaultName() || ""); localStorage.setItem(lsKey, this.services.setting.getDeviceAndVaultName() || "");
} }
usedPassphrase = ""; usedPassphrase = "";
$$clearUsedPassphrase(): void { private _clearUsedPassphrase(): void {
this.usedPassphrase = ""; this.usedPassphrase = "";
} }
@@ -106,8 +107,8 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
return `${"appId" in this.app ? this.app.appId : ""}`; return `${"appId" in this.app ? this.app.appId : ""}`;
} }
async $$saveSettingData() { async _saveSettingData() {
this.core.$$saveDeviceAndVaultName(); this.services.setting.saveDeviceAndVaultName();
const settings = { ...this.settings }; const settings = { ...this.settings };
settings.deviceAndVaultName = ""; settings.deviceAndVaultName = "";
if (this.usedPassphrase == "" && !(await this.getPassphrase(settings))) { if (this.usedPassphrase == "" && !(await this.getPassphrase(settings))) {
@@ -174,7 +175,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
} }
} }
async $$decryptSettings(settings: ObsidianLiveSyncSettings): Promise<ObsidianLiveSyncSettings> { async _decryptSettings(settings: ObsidianLiveSyncSettings): Promise<ObsidianLiveSyncSettings> {
const passphrase = await this.getPassphrase(settings); const passphrase = await this.getPassphrase(settings);
if (passphrase === false) { if (passphrase === false) {
this._log("No passphrase found for data.json! Verify configuration before syncing.", LOG_LEVEL_URGENT); 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 * @param settings
* @returns * @returns
*/ */
$$adjustSettings(settings: ObsidianLiveSyncSettings): Promise<ObsidianLiveSyncSettings> { _adjustSettings(settings: ObsidianLiveSyncSettings): Promise<ObsidianLiveSyncSettings> {
// Adjust settings as needed // Adjust settings as needed
// Delete this feature to avoid problems on mobile. // Delete this feature to avoid problems on mobile.
@@ -264,7 +265,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
return Promise.resolve(settings); return Promise.resolve(settings);
} }
async $$loadSettings(): Promise<void> { async _loadSettings(): Promise<void> {
const settings = Object.assign({}, DEFAULT_SETTINGS, await this.core.loadData()) as ObsidianLiveSyncSettings; const settings = Object.assign({}, DEFAULT_SETTINGS, await this.core.loadData()) as ObsidianLiveSyncSettings;
if (typeof settings.isConfigured == "undefined") { 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); 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 (this.settings.deviceAndVaultName != "") {
if (!localStorage.getItem(lsKey)) { if (!localStorage.getItem(lsKey)) {
this.core.$$setDeviceAndVaultName(this.settings.deviceAndVaultName); this.services.setting.setDeviceAndVaultName(this.settings.deviceAndVaultName);
this.$$saveDeviceAndVaultName(); this.services.setting.saveDeviceAndVaultName();
this.settings.deviceAndVaultName = ""; this.settings.deviceAndVaultName = "";
} }
} }
@@ -298,8 +299,8 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
); );
this.settings.customChunkSize = 0; this.settings.customChunkSize = 0;
} }
this.core.$$setDeviceAndVaultName(localStorage.getItem(lsKey) || ""); this.services.setting.setDeviceAndVaultName(localStorage.getItem(lsKey) || "");
if (this.core.$$getDeviceAndVaultName() == "") { if (this.services.setting.getDeviceAndVaultName() == "") {
if (this.settings.usePluginSync) { if (this.settings.usePluginSync) {
this._log("Device name missing. Disabling plug-in sync.", LOG_LEVEL_NOTICE); this._log("Device name missing. Disabling plug-in sync.", LOG_LEVEL_NOTICE);
this.settings.usePluginSync = false; 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()); // this.core.ignoreFiles = this.settings.ignoreFiles.split(",").map(e => e.trim());
eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB); 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));
}
} }

View File

@@ -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 { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser";
import { isObjectDifferent } from "octagonal-wheels/object"; import { isObjectDifferent } from "octagonal-wheels/object";
import { EVENT_SETTING_SAVED, eventHub } from "../../common/events"; 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"; 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_HEADER = "````yaml:livesync-setting\n";
const SETTING_FOOTER = "\n````"; const SETTING_FOOTER = "\n````";
export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule implements IObsidianModule { export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule {
$everyOnloadStart(): Promise<boolean> { _everyOnloadStart(): Promise<boolean> {
this.addCommand({ this.addCommand({
id: "livesync-export-config", id: "livesync-export-config",
name: "Write setting markdown manually", name: "Write setting markdown manually",
@@ -18,7 +18,7 @@ export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule imp
return this.settings.settingSyncFile != ""; return this.settings.settingSyncFile != "";
} }
fireAndForget(async () => { 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 result == APPLY_AND_FETCH
) { ) {
this.core.settings = settingToApply; this.core.settings = settingToApply;
await this.core.$$saveSettingData(); await this.services.setting.saveSettingData();
if (result == APPLY_ONLY) { if (result == APPLY_ONLY) {
this._log("Loaded settings have been applied!", LOG_LEVEL_NOTICE); this._log("Loaded settings have been applied!", LOG_LEVEL_NOTICE);
return; return;
@@ -171,7 +171,7 @@ export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule imp
if (result == APPLY_AND_FETCH) { if (result == APPLY_AND_FETCH) {
await this.core.rebuilder.scheduleFetch(); 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); 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));
}
} }

View File

@@ -1,12 +1,12 @@
import { ObsidianLiveSyncSettingTab } from "./SettingDialogue/ObsidianLiveSyncSettingTab.ts"; 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 { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser";
import { EVENT_REQUEST_OPEN_SETTING_WIZARD, EVENT_REQUEST_OPEN_SETTINGS, eventHub } from "../../common/events.ts"; 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; settingTab!: ObsidianLiveSyncSettingTab;
$everyOnloadStart(): Promise<boolean> { _everyOnloadStart(): Promise<boolean> {
this.settingTab = new ObsidianLiveSyncSettingTab(this.app, this.plugin); this.settingTab = new ObsidianLiveSyncSettingTab(this.app, this.plugin);
this.plugin.addSettingTab(this.settingTab); this.plugin.addSettingTab(this.settingTab);
eventHub.onEvent(EVENT_REQUEST_OPEN_SETTINGS, () => this.openSetting()); eventHub.onEvent(EVENT_REQUEST_OPEN_SETTINGS, () => this.openSetting());
@@ -29,4 +29,7 @@ export class ModuleObsidianSettingDialogue extends AbstractObsidianModule implem
get appId() { get appId() {
return `${"appId" in this.app ? this.app.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));
}
} }

View File

@@ -14,15 +14,16 @@ import {
EVENT_REQUEST_SHOW_SETUP_QR, EVENT_REQUEST_SHOW_SETUP_QR,
eventHub, eventHub,
} from "../../common/events.ts"; } from "../../common/events.ts";
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts"; import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
import { decodeAnyArray, encodeAnyArray } from "../../common/utils.ts"; import { decodeAnyArray, encodeAnyArray } from "../../common/utils.ts";
import qrcode from "qrcode-generator"; import qrcode from "qrcode-generator";
import { $msg } from "../../lib/src/common/i18n.ts"; import { $msg } from "../../lib/src/common/i18n.ts";
import { performDoctorConsultation, RebuildOptions } from "@/lib/src/common/configForDoc.ts"; import { performDoctorConsultation, RebuildOptions } from "@/lib/src/common/configForDoc.ts";
import { encryptString, decryptString } from "@/lib/src/encryption/stringEncryption.ts"; import { encryptString, decryptString } from "@/lib/src/encryption/stringEncryption.ts";
import type { LiveSyncCore } from "../../main.ts";
export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsidianModule { export class ModuleSetupObsidian extends AbstractObsidianModule {
$everyOnload(): Promise<boolean> { private _everyOnload(): Promise<boolean> {
this.registerObsidianProtocolHandler("setuplivesync", async (conf: any) => { this.registerObsidianProtocolHandler("setuplivesync", async (conf: any) => {
if (conf.settings) { if (conf.settings) {
await this.setupWizard(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 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) { if (remoteConfig) {
this._log("Remote configuration found.", LOG_LEVEL_NOTICE); this._log("Remote configuration found.", LOG_LEVEL_NOTICE);
const resultSettings = { const resultSettings = {
@@ -282,16 +283,16 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
); );
if (setupType == setupJustImport) { if (setupType == setupJustImport) {
this.core.settings = newSettingW; this.core.settings = newSettingW;
this.core.$$clearUsedPassphrase(); this.services.setting.clearUsedPassphrase();
await this.core.saveSettings(); await this.core.saveSettings();
} else if (setupType == setupAsNew) { } else if (setupType == setupAsNew) {
this.core.settings = newSettingW; this.core.settings = newSettingW;
this.core.$$clearUsedPassphrase(); this.services.setting.clearUsedPassphrase();
await this.core.saveSettings(); await this.core.saveSettings();
await this.core.rebuilder.$fetchLocal(); await this.core.rebuilder.$fetchLocal();
} else if (setupType == setupAsMerge) { } else if (setupType == setupAsMerge) {
this.core.settings = newSettingW; this.core.settings = newSettingW;
this.core.$$clearUsedPassphrase(); this.services.setting.clearUsedPassphrase();
await this.core.saveSettings(); await this.core.saveSettings();
await this.core.rebuilder.$fetchLocal(true); await this.core.rebuilder.$fetchLocal(true);
} else if (setupType == setupAgain) { } else if (setupType == setupAgain) {
@@ -308,7 +309,7 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
} }
this.core.settings = newSettingW; this.core.settings = newSettingW;
await this.core.saveSettings(); await this.core.saveSettings();
this.core.$$clearUsedPassphrase(); this.services.setting.clearUsedPassphrase();
await this.core.rebuilder.$rebuildEverything(); await this.core.rebuilder.$rebuildEverything();
} else { } else {
// Explicitly cancel the operation or the dialog was closed. // 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); this._log(ex, LOG_LEVEL_VERBOSE);
} }
} }
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this));
}
} }

View File

@@ -86,6 +86,9 @@ export function createStub(name: string, key: string, value: string, panel: stri
export class ObsidianLiveSyncSettingTab extends PluginSettingTab { export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
plugin: ObsidianLiveSyncPlugin; plugin: ObsidianLiveSyncPlugin;
get services() {
return this.plugin.services;
}
selectedScreen = ""; selectedScreen = "";
_editingSettings?: AllSettings; _editingSettings?: AllSettings;
@@ -139,8 +142,8 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
return await Promise.resolve(); return await Promise.resolve();
} }
if (key == "deviceAndVaultName") { if (key == "deviceAndVaultName") {
this.plugin.$$setDeviceAndVaultName(this.editingSettings?.[key] ?? ""); this.services.setting.setDeviceAndVaultName(this.editingSettings?.[key] ?? "");
this.plugin.$$saveDeviceAndVaultName(); this.services.setting.saveDeviceAndVaultName();
return await Promise.resolve(); return await Promise.resolve();
} }
} }
@@ -210,7 +213,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
const ret = { ...OnDialogSettingsDefault }; const ret = { ...OnDialogSettingsDefault };
ret.configPassphrase = localStorage.getItem("ls-setting-passphrase") || ""; ret.configPassphrase = localStorage.getItem("ls-setting-passphrase") || "";
ret.preset = ""; ret.preset = "";
ret.deviceAndVaultName = this.plugin.$$getDeviceAndVaultName(); ret.deviceAndVaultName = this.services.setting.getDeviceAndVaultName();
return ret; return ret;
} }
computeAllLocalSettings(): Partial<OnDialogSettings> { computeAllLocalSettings(): Partial<OnDialogSettings> {
@@ -295,7 +298,11 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
async testConnection(settingOverride: Partial<ObsidianLiveSyncSettings> = {}): Promise<void> { async testConnection(settingOverride: Partial<ObsidianLiveSyncSettings> = {}): Promise<void> {
const trialSetting = { ...this.editingSettings, ...settingOverride }; 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); await replicator.tryConnectRemote(trialSetting);
const status = await replicator.getRemoteStatus(trialSetting); const status = await replicator.getRemoteStatus(trialSetting);
if (status) { if (status) {
@@ -546,10 +553,14 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
const settingForCheck: RemoteDBSettings = { const settingForCheck: RemoteDBSettings = {
...this.editingSettings, ...this.editingSettings,
}; };
const replicator = this.plugin.$anyNewReplicator(settingForCheck); const replicator = this.services.replicator.getNewReplicator(settingForCheck);
if (!(replicator instanceof LiveSyncCouchDBReplicator)) return true; 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") { if (typeof db === "string") {
Logger($msg("obsidianLiveSyncSettingTab.logCheckPassphraseFailed", { db }), LOG_LEVEL_NOTICE); Logger($msg("obsidianLiveSyncSettingTab.logCheckPassphraseFailed", { db }), LOG_LEVEL_NOTICE);
return false; return false;
@@ -588,8 +599,8 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
this.editingSettings.passphrase = ""; this.editingSettings.passphrase = "";
} }
this.applyAllSettings(); this.applyAllSettings();
await this.plugin.$allSuspendAllSync(); await this.services.setting.suspendAllSync();
await this.plugin.$allSuspendExtraSync(); await this.services.setting.suspendExtraSync();
this.reloadAllSettings(); this.reloadAllSettings();
this.editingSettings.isConfigured = true; this.editingSettings.isConfigured = true;
Logger($msg("obsidianLiveSyncSettingTab.logRebuildNote"), LOG_LEVEL_NOTICE); Logger($msg("obsidianLiveSyncSettingTab.logRebuildNote"), LOG_LEVEL_NOTICE);
@@ -638,12 +649,12 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
await this.applyAllSettings(); await this.applyAllSettings();
if (result == OPTION_FETCH) { if (result == OPTION_FETCH) {
await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG3_HR, ""); await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG3_HR, "");
this.plugin.$$scheduleAppReload(); this.services.appLifecycle.scheduleRestart();
this.closeSetting(); this.closeSetting();
// await rebuildDB("localOnly"); // await rebuildDB("localOnly");
} else if (result == OPTION_REBUILD_BOTH) { } else if (result == OPTION_REBUILD_BOTH) {
await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG2_HR, ""); await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG2_HR, "");
this.plugin.$$scheduleAppReload(); this.services.appLifecycle.scheduleRestart();
this.closeSetting(); this.closeSetting();
} else if (result == OPTION_ONLY_SETTING) { } else if (result == OPTION_ONLY_SETTING) {
await this.plugin.saveSettings(); await this.plugin.saveSettings();

View File

@@ -156,7 +156,7 @@ export function paneHatch(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement,
} }
const obsidianInfo = { const obsidianInfo = {
navigator: navigator.userAgent, navigator: navigator.userAgent,
fileSystem: this.plugin.$$isStorageInsensitive() ? "insensitive" : "sensitive", fileSystem: this.plugin.services.vault.isStorageInsensitive() ? "insensitive" : "sensitive",
}; };
const msgConfig = `# ---- Obsidian info ---- const msgConfig = `# ---- Obsidian info ----
${stringifyYaml(obsidianInfo)} ${stringifyYaml(obsidianInfo)}
@@ -182,10 +182,10 @@ ${stringifyYaml({
void addPanel(paneEl, "Scram Switches").then((paneEl) => { void addPanel(paneEl, "Scram Switches").then((paneEl) => {
new Setting(paneEl).autoWireToggle("suspendFileWatching"); new Setting(paneEl).autoWireToggle("suspendFileWatching");
this.addOnSaved("suspendFileWatching", () => this.plugin.$$askReload()); this.addOnSaved("suspendFileWatching", () => this.services.appLifecycle.askRestart());
new Setting(paneEl).autoWireToggle("suspendParseReplicationResult"); 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) => { void addPanel(paneEl, "Recovery and Repair").then((paneEl) => {
@@ -384,15 +384,16 @@ ${stringifyYaml({
? await this.plugin.storageAccess.statHidden(path) ? await this.plugin.storageAccess.statHidden(path)
: false; : false;
const fileOnStorage = stat != null ? stat : 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); const releaser = await semaphore.acquire(1);
if (fileOnStorage && this.plugin.$$isFileSizeExceeded(fileOnStorage.size)) if (fileOnStorage && this.services.vault.isFileSizeTooLarge(fileOnStorage.size))
return incProc(); return incProc();
try { try {
const isHiddenFile = path.startsWith("."); const isHiddenFile = path.startsWith(".");
const dbPath = isHiddenFile ? addPrefix(path, ICHeader) : path; const dbPath = isHiddenFile ? addPrefix(path, ICHeader) : path;
const fileOnDB = await this.plugin.localDatabase.getDBEntry(dbPath); 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) { if (!fileOnDB && fileOnStorage) {
Logger(`Compare: Not found on the local database: ${path}`, LOG_LEVEL_NOTICE); Logger(`Compare: Not found on the local database: ${path}`, LOG_LEVEL_NOTICE);
@@ -436,7 +437,7 @@ ${stringifyYaml({
.onClick(async () => { .onClick(async () => {
for await (const docName of this.plugin.localDatabase.findAllDocNames()) { for await (const docName of this.plugin.localDatabase.findAllDocNames()) {
if (!docName.startsWith("f:")) { 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); const doc = await this.plugin.localDatabase.getRaw(docName as DocumentID);
if (!doc) continue; if (!doc) continue;
if (doc.type != "newnote" && doc.type != "plain") { if (doc.type != "newnote" && doc.type != "plain") {
@@ -477,7 +478,7 @@ ${stringifyYaml({
if ((await this.plugin.localDatabase.putRaw(doc)).ok) { if ((await this.plugin.localDatabase.putRaw(doc)).ok) {
Logger(`Old ${docName} has been deleted`, LOG_LEVEL_NOTICE); 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 { } else {
Logger(`Converting ${docName} Failed!`, LOG_LEVEL_NOTICE); Logger(`Converting ${docName} Failed!`, LOG_LEVEL_NOTICE);
Logger(ret, LOG_LEVEL_VERBOSE); Logger(ret, LOG_LEVEL_VERBOSE);
@@ -512,7 +513,7 @@ ${stringifyYaml({
.onClick(async () => { .onClick(async () => {
this.editingSettings.isConfigured = false; this.editingSettings.isConfigured = false;
await this.saveAllDirtySettings(); await this.saveAllDirtySettings();
this.plugin.$$askReload(); this.services.appLifecycle.askRestart();
}) })
); );

View File

@@ -32,7 +32,7 @@ export function paneMaintenance(
(e) => { (e) => {
e.addEventListener("click", () => { e.addEventListener("click", () => {
fireAndForget(async () => { fireAndForget(async () => {
await this.plugin.$$markRemoteResolved(); await this.services.remote.markResolved();
this.display(); this.display();
}); });
}); });
@@ -59,7 +59,7 @@ export function paneMaintenance(
(e) => { (e) => {
e.addEventListener("click", () => { e.addEventListener("click", () => {
fireAndForget(async () => { fireAndForget(async () => {
await this.plugin.$$markRemoteUnlocked(); await this.services.remote.markUnlocked();
this.display(); this.display();
}); });
}); });
@@ -78,7 +78,7 @@ export function paneMaintenance(
.setDisabled(false) .setDisabled(false)
.setWarning() .setWarning()
.onClick(async () => { .onClick(async () => {
await this.plugin.$$markRemoteLocked(); await this.services.remote.markLocked();
}) })
) )
.addOnUpdate(this.onlyOnCouchDBOrMinIO); .addOnUpdate(this.onlyOnCouchDBOrMinIO);
@@ -93,7 +93,7 @@ export function paneMaintenance(
.setWarning() .setWarning()
.onClick(async () => { .onClick(async () => {
await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG, ""); await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG, "");
this.plugin.$$performRestart(); this.services.appLifecycle.performRestart();
}) })
); );
}); });
@@ -255,7 +255,7 @@ export function paneMaintenance(
.setDisabled(false) .setDisabled(false)
.onClick(async () => { .onClick(async () => {
await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG3_HR, ""); await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG3_HR, "");
this.plugin.$$performRestart(); this.services.appLifecycle.performRestart();
}) })
) )
.addButton((button) => .addButton((button) =>
@@ -294,7 +294,7 @@ export function paneMaintenance(
.setDisabled(false) .setDisabled(false)
.onClick(async () => { .onClick(async () => {
await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG2_HR, ""); await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG2_HR, "");
this.plugin.$$performRestart(); this.services.appLifecycle.performRestart();
}) })
) )
.addButton((button) => .addButton((button) =>
@@ -405,8 +405,8 @@ export function paneMaintenance(
.setWarning() .setWarning()
.setDisabled(false) .setDisabled(false)
.onClick(async () => { .onClick(async () => {
await this.plugin.$$resetLocalDatabase(); await this.services.database.resetDatabase();
await this.plugin.$$initializeDatabase(); await this.services.databaseEvents.initialiseDatabase();
}) })
); );
}); });

View File

@@ -63,7 +63,7 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
this.addOnSaved("additionalSuffixOfDatabaseName", async (key) => { this.addOnSaved("additionalSuffixOfDatabaseName", async (key) => {
Logger("Suffix has been changed. Reopening database...", LOG_LEVEL_NOTICE); Logger("Suffix has been changed. Reopening database...", LOG_LEVEL_NOTICE);
await this.plugin.$$initializeDatabase(); await this.services.databaseEvents.initialiseDatabase();
}); });
new Setting(paneEl).autoWireDropDown("hashAlg", { new Setting(paneEl).autoWireDropDown("hashAlg", {

View File

@@ -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( void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleCouchDB"), undefined, this.onlyOnCouchDB).then(
(paneEl) => { (paneEl) => {
if (this.plugin.$$isMobile()) { if (this.services.API.isMobile()) {
this.createEl( this.createEl(
paneEl, paneEl,
"div", "div",
@@ -630,7 +630,8 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
.setDisabled(false) .setDisabled(false)
.onClick(async () => { .onClick(async () => {
const trialSetting = { ...this.initialSettings, ...this.editingSettings }; 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 (newTweaks.result !== false) {
if (this.inWizard) { if (this.inWizard) {
this.editingSettings = { ...this.editingSettings, ...newTweaks.result }; 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" )) == "no"
) { ) {
await this.plugin.$$saveSettingData(); await this.services.setting.saveSettingData();
return; return;
} }
await this.plugin.$$saveSettingData(); await this.services.setting.saveSettingData();
await this.plugin.rebuilder.scheduleFetch(); await this.plugin.rebuilder.scheduleFetch();
await this.plugin.$$scheduleAppReload(); this.services.appLifecycle.scheduleRestart();
return; return;
} else { } 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" )) == "yes"
) { ) {
const trialSetting = { ...this.initialSettings, ...this.editingSettings }; 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 (newTweaks.result !== false) {
this.editingSettings = { ...this.editingSettings, ...newTweaks.result }; this.editingSettings = { ...this.editingSettings, ...newTweaks.result };
this.requestUpdate(); this.requestUpdate();

View File

@@ -46,7 +46,7 @@ export function paneSetup(
text.setButtonText($msg("obsidianLiveSyncSettingTab.btnEnable")).onClick(async () => { text.setButtonText($msg("obsidianLiveSyncSettingTab.btnEnable")).onClick(async () => {
this.editingSettings.isConfigured = true; this.editingSettings.isConfigured = true;
await this.saveAllDirtySettings(); await this.saveAllDirtySettings();
this.plugin.$$askReload(); this.services.appLifecycle.askRestart();
}); });
}); });
}); });
@@ -91,10 +91,10 @@ export function paneSetup(
this.editingSettings = { ...this.editingSettings, ...DEFAULT_SETTINGS }; this.editingSettings = { ...this.editingSettings, ...DEFAULT_SETTINGS };
await this.saveAllDirtySettings(); await this.saveAllDirtySettings();
this.plugin.settings = { ...DEFAULT_SETTINGS }; this.plugin.settings = { ...DEFAULT_SETTINGS };
await this.plugin.$$saveSettingData(); await this.services.setting.saveSettingData();
await this.plugin.$$resetLocalDatabase(); await this.services.database.resetDatabase();
// await this.plugin.initializeDatabase(); // await this.plugin.initializeDatabase();
this.plugin.$$askReload(); this.services.appLifecycle.askRestart();
} }
}) })
.setWarning(); .setWarning();

View File

@@ -105,7 +105,7 @@ export function paneSyncSettings(
if (!this.editingSettings.isConfigured) { if (!this.editingSettings.isConfigured) {
this.editingSettings.isConfigured = true; this.editingSettings.isConfigured = true;
await this.saveAllDirtySettings(); await this.saveAllDirtySettings();
await this.plugin.$$realizeSettingSyncMode(); await this.services.setting.onRealiseSetting();
await this.rebuildDB("localOnly"); await this.rebuildDB("localOnly");
// this.resetEditingSettings(); // this.resetEditingSettings();
if ( if (
@@ -124,13 +124,13 @@ export function paneSyncSettings(
await this.confirmRebuild(); await this.confirmRebuild();
} else { } else {
await this.saveAllDirtySettings(); await this.saveAllDirtySettings();
await this.plugin.$$realizeSettingSyncMode(); await this.services.setting.onRealiseSetting();
this.plugin.$$askReload(); this.services.appLifecycle.askRestart();
} }
} }
} else { } else {
await this.saveAllDirtySettings(); 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.saveSettings(["liveSync", "periodicReplication"]);
await this.plugin.$$realizeSettingSyncMode(); await this.services.setting.onRealiseSetting();
}); });
new Setting(paneEl) new Setting(paneEl)
@@ -289,21 +289,21 @@ export function paneSyncSettings(
button.setButtonText("Merge").onClick(async () => { button.setButtonText("Merge").onClick(async () => {
this.closeSetting(); this.closeSetting();
// this.resetEditingSettings(); // this.resetEditingSettings();
await this.plugin.$anyConfigureOptionalSyncFeature("MERGE"); await this.services.setting.enableOptionalFeature("MERGE");
}); });
}) })
.addButton((button) => { .addButton((button) => {
button.setButtonText("Fetch").onClick(async () => { button.setButtonText("Fetch").onClick(async () => {
this.closeSetting(); this.closeSetting();
// this.resetEditingSettings(); // this.resetEditingSettings();
await this.plugin.$anyConfigureOptionalSyncFeature("FETCH"); await this.services.setting.enableOptionalFeature("FETCH");
}); });
}) })
.addButton((button) => { .addButton((button) => {
button.setButtonText("Overwrite").onClick(async () => { button.setButtonText("Overwrite").onClick(async () => {
this.closeSetting(); this.closeSetting();
// this.resetEditingSettings(); // this.resetEditingSettings();
await this.plugin.$anyConfigureOptionalSyncFeature("OVERWRITE"); await this.services.setting.enableOptionalFeature("OVERWRITE");
}); });
}); });
} }

View File

@@ -13,12 +13,13 @@ import { versionNumberString2Number } from "../../lib/src/string_and_binary/conv
import { cancelAllPeriodicTask, cancelAllTasks } from "octagonal-wheels/concurrency/task"; import { cancelAllPeriodicTask, cancelAllTasks } from "octagonal-wheels/concurrency/task";
import { stopAllRunningProcessors } from "octagonal-wheels/concurrency/processor"; import { stopAllRunningProcessors } from "octagonal-wheels/concurrency/processor";
import { AbstractModule } from "../AbstractModule.ts"; import { AbstractModule } from "../AbstractModule.ts";
import type { ICoreModule } from "../ModuleTypes.ts";
import { EVENT_PLATFORM_UNLOADED } from "../../lib/src/PlatformAPIs/base/APIBase.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 { export class ModuleLiveSyncMain extends AbstractModule {
async $$onLiveSyncReady() { async _onLiveSyncReady() {
if (!(await this.core.$everyOnLayoutReady())) return; if (!(await this.core.services.appLifecycle.onLayoutReady())) return false;
eventHub.emitEvent(EVENT_LAYOUT_READY); eventHub.emitEvent(EVENT_LAYOUT_READY);
if (this.settings.suspendFileWatching || this.settings.suspendParseReplicationResult) { if (this.settings.suspendFileWatching || this.settings.suspendParseReplicationResult) {
const ANSWER_KEEP = $msg("moduleLiveSyncMain.optionKeepLiveSyncDisabled"); const ANSWER_KEEP = $msg("moduleLiveSyncMain.optionKeepLiveSyncDisabled");
@@ -36,46 +37,49 @@ export class ModuleLiveSyncMain extends AbstractModule implements ICoreModule {
this.settings.suspendFileWatching = false; this.settings.suspendFileWatching = false;
this.settings.suspendParseReplicationResult = false; this.settings.suspendParseReplicationResult = false;
await this.saveSettings(); await this.saveSettings();
await this.core.$$scheduleAppReload(); this.services.appLifecycle.scheduleRestart();
return; return false;
} }
} }
const isInitialized = await this.core.$$initializeDatabase(false, false); const isInitialized = await this.services.databaseEvents.initialiseDatabase(false, false);
if (!isInitialized) { if (!isInitialized) {
//TODO:stop all sync. //TODO:stop all sync.
return false; return false;
} }
if (!(await this.core.$everyOnFirstInitialize())) return; if (!(await this.core.services.appLifecycle.onFirstInitialise())) return false;
await this.core.$$realizeSettingSyncMode(); // await this.core.$$realizeSettingSyncMode();
await this.services.setting.onRealiseSetting();
fireAndForget(async () => { fireAndForget(async () => {
this._log($msg("moduleLiveSyncMain.logAdditionalSafetyScan"), LOG_LEVEL_VERBOSE); 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); this._log($msg("moduleLiveSyncMain.logSafetyScanFailed"), LOG_LEVEL_NOTICE);
} else { } else {
this._log($msg("moduleLiveSyncMain.logSafetyScanCompleted"), LOG_LEVEL_VERBOSE); this._log($msg("moduleLiveSyncMain.logSafetyScanCompleted"), LOG_LEVEL_VERBOSE);
} }
}); });
return true;
} }
$$wireUpEvents(): void { _wireUpEvents() {
eventHub.onEvent(EVENT_SETTING_SAVED, (settings: ObsidianLiveSyncSettings) => { eventHub.onEvent(EVENT_SETTING_SAVED, (settings: ObsidianLiveSyncSettings) => {
this.localDatabase.settings = settings; this.localDatabase.settings = settings;
setLang(settings.displayLanguage); setLang(settings.displayLanguage);
eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB); eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB);
}); });
eventHub.onEvent(EVENT_SETTING_SAVED, (settings: ObsidianLiveSyncSettings) => { eventHub.onEvent(EVENT_SETTING_SAVED, (settings: ObsidianLiveSyncSettings) => {
fireAndForget(() => this.core.$$realizeSettingSyncMode()); fireAndForget(() => this.core.services.setting.onRealiseSetting());
}); });
return Promise.resolve(true);
} }
async $$onLiveSyncLoad(): Promise<void> { async _onLiveSyncLoad(): Promise<boolean> {
this.$$wireUpEvents(); await this.services.appLifecycle.onWireUpEvents();
// debugger; // debugger;
eventHub.emitEvent(EVENT_PLUGIN_LOADED, this.core); eventHub.emitEvent(EVENT_PLUGIN_LOADED, this.core);
this._log($msg("moduleLiveSyncMain.logLoadingPlugin")); this._log($msg("moduleLiveSyncMain.logLoadingPlugin"));
if (!(await this.core.$everyOnloadStart())) { if (!(await this.services.appLifecycle.onInitialise())) {
this._log($msg("moduleLiveSyncMain.logPluginInitCancelled"), LOG_LEVEL_NOTICE); this._log($msg("moduleLiveSyncMain.logPluginInitCancelled"), LOG_LEVEL_NOTICE);
return; return false;
} }
// this.addUIs(); // this.addUIs();
//@ts-ignore //@ts-ignore
@@ -84,12 +88,12 @@ export class ModuleLiveSyncMain extends AbstractModule implements ICoreModule {
const packageVersion: string = PACKAGE_VERSION || "0.0.0"; const packageVersion: string = PACKAGE_VERSION || "0.0.0";
this._log($msg("moduleLiveSyncMain.logPluginVersion", { manifestVersion, packageVersion })); this._log($msg("moduleLiveSyncMain.logPluginVersion", { manifestVersion, packageVersion }));
await this.core.$$loadSettings(); await this.services.setting.loadSettings();
if (!(await this.core.$everyOnloadAfterLoadSettings())) { if (!(await this.services.appLifecycle.onSettingLoaded())) {
this._log($msg("moduleLiveSyncMain.logPluginInitCancelled"), LOG_LEVEL_NOTICE); 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 last_version = localStorage.getItem(lsKey);
const lastVersion = ~~(versionNumberString2Number(manifestVersion) / 1000); const lastVersion = ~~(versionNumberString2Number(manifestVersion) / 1000);
@@ -113,22 +117,23 @@ export class ModuleLiveSyncMain extends AbstractModule implements ICoreModule {
await this.saveSettings(); await this.saveSettings();
} }
localStorage.setItem(lsKey, `${VER}`); localStorage.setItem(lsKey, `${VER}`);
await this.core.$$openDatabase(); await this.services.database.openDatabase();
this.core.$$realizeSettingSyncMode = this.core.$$realizeSettingSyncMode.bind(this); // this.core.$$realizeSettingSyncMode = this.core.$$realizeSettingSyncMode.bind(this);
// this.$$parseReplicationResult = this.$$parseReplicationResult.bind(this); // this.$$parseReplicationResult = this.$$parseReplicationResult.bind(this);
// this.$$replicate = this.$$replicate.bind(this); // this.$$replicate = this.$$replicate.bind(this);
this.core.$$onLiveSyncReady = this.core.$$onLiveSyncReady.bind(this); // this.core.$$onLiveSyncReady = this.core.$$onLiveSyncReady.bind(this);
await this.core.$everyOnload(); await this.core.services.appLifecycle.onLoaded();
await Promise.all(this.core.addOns.map((e) => e.onload())); await Promise.all(this.core.addOns.map((e) => e.onload()));
return true;
} }
async $$onLiveSyncUnload(): Promise<void> { async _onLiveSyncUnload(): Promise<void> {
eventHub.emitEvent(EVENT_PLUGIN_UNLOADED); eventHub.emitEvent(EVENT_PLUGIN_UNLOADED);
await this.core.$allStartOnUnload(); await this.services.appLifecycle.onBeforeUnload();
cancelAllPeriodicTask(); cancelAllPeriodicTask();
cancelAllTasks(); cancelAllTasks();
stopAllRunningProcessors(); stopAllRunningProcessors();
await this.core.$allOnUnload(); await this.services.appLifecycle.onUnload();
this._unloaded = true; this._unloaded = true;
for (const addOn of this.core.addOns) { for (const addOn of this.core.addOns) {
addOn.onunload(); addOn.onunload();
@@ -143,49 +148,68 @@ export class ModuleLiveSyncMain extends AbstractModule implements ICoreModule {
eventHub.emitEvent(EVENT_PLATFORM_UNLOADED); eventHub.emitEvent(EVENT_PLATFORM_UNLOADED);
eventHub.offAll(); eventHub.offAll();
this._log($msg("moduleLiveSyncMain.logUnloadingPlugin")); this._log($msg("moduleLiveSyncMain.logUnloadingPlugin"));
return;
} }
async $$realizeSettingSyncMode(): Promise<void> { private async _realizeSettingSyncMode(): Promise<void> {
await this.core.$everyBeforeSuspendProcess(); await this.services.appLifecycle.onSuspending();
await this.core.$everyBeforeRealizeSetting(); await this.services.setting.onBeforeRealiseSetting();
this.localDatabase.refreshSettings(); this.localDatabase.refreshSettings();
await this.core.$everyCommitPendingFileEvent(); await this.services.fileProcessing.commitPendingFileEvents();
await this.core.$everyRealizeSettingSyncMode(); await this.services.setting.onRealiseSetting();
// disable all sync temporary. // disable all sync temporary.
if (this.core.$$isSuspended()) return; if (this.services.appLifecycle.isSuspended()) return;
await this.core.$everyOnResumeProcess(); await this.services.appLifecycle.onResuming();
await this.core.$everyAfterResumeProcess(); await this.services.appLifecycle.onResumed();
await this.core.$everyAfterRealizeSetting(); await this.services.setting.onSettingRealised();
return;
} }
$$isReloadingScheduled(): boolean { _isReloadingScheduled(): boolean {
return this.core._totalProcessingCount !== undefined; return this.core._totalProcessingCount !== undefined;
} }
isReady = false; isReady = false;
$$isReady(): boolean { _isReady(): boolean {
return this.isReady; return this.isReady;
} }
$$markIsReady(): void { _markIsReady(): void {
this.isReady = true; this.isReady = true;
} }
$$resetIsReady(): void { _resetIsReady(): void {
this.isReady = false; this.isReady = false;
} }
_suspended = false; _suspended = false;
$$isSuspended(): boolean { _isSuspended(): boolean {
return this._suspended || !this.settings?.isConfigured; return this._suspended || !this.settings?.isConfigured;
} }
$$setSuspended(value: boolean) {
_setSuspended(value: boolean) {
this._suspended = value; this._suspended = value;
} }
_unloaded = false; _unloaded = false;
$$isUnloaded(): boolean { _isUnloaded(): boolean {
return this._unloaded; 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));
}
} }

View File

@@ -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);
}

View File

@@ -31,12 +31,12 @@ const terserOption = {
evaluate: true, evaluate: true,
dead_code: true, dead_code: true,
// directives: true, // directives: true,
inline: 3, inline: false,
join_vars: true, join_vars: true,
loops: true, loops: true,
passes: 4, passes: 4,
reduce_vars: true, reduce_vars: true,
reduce_funcs: true, reduce_funcs: false,
arrows: true, arrows: true,
collapse_vars: true, collapse_vars: true,
comparisons: true, comparisons: true,