mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-04-09 10:28:43 +00:00
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:
@@ -1,34 +1,34 @@
|
||||
import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, Logger } from "octagonal-wheels/common/logger";
|
||||
import type { LOG_LEVEL } from "../lib/src/common/types";
|
||||
import type { LiveSyncCore } from "../main";
|
||||
import { unique } from "octagonal-wheels/collection";
|
||||
import type { IObsidianModule } from "./AbstractObsidianModule.ts";
|
||||
import type {
|
||||
ICoreModuleBase,
|
||||
AllInjectableProps,
|
||||
AllExecuteProps,
|
||||
EveryExecuteProps,
|
||||
AnyExecuteProps,
|
||||
ICoreModule,
|
||||
} from "./ModuleTypes";
|
||||
// import { unique } from "octagonal-wheels/collection";
|
||||
// import type { IObsidianModule } from "./AbstractObsidianModule.ts";
|
||||
// import type {
|
||||
// ICoreModuleBase,
|
||||
// AllInjectableProps,
|
||||
// AllExecuteProps,
|
||||
// EveryExecuteProps,
|
||||
// AnyExecuteProps,
|
||||
// ICoreModule,
|
||||
// } from "./ModuleTypes";
|
||||
|
||||
function isOverridableKey(key: string): key is keyof ICoreModuleBase {
|
||||
return key.startsWith("$");
|
||||
}
|
||||
// function isOverridableKey(key: string): key is keyof ICoreModuleBase {
|
||||
// return key.startsWith("$");
|
||||
// }
|
||||
|
||||
function isInjectableKey(key: string): key is keyof AllInjectableProps {
|
||||
return key.startsWith("$$");
|
||||
}
|
||||
// function isInjectableKey(key: string): key is keyof AllInjectableProps {
|
||||
// return key.startsWith("$$");
|
||||
// }
|
||||
|
||||
function isAllExecuteKey(key: string): key is keyof AllExecuteProps {
|
||||
return key.startsWith("$all");
|
||||
}
|
||||
function isEveryExecuteKey(key: string): key is keyof EveryExecuteProps {
|
||||
return key.startsWith("$every");
|
||||
}
|
||||
function isAnyExecuteKey(key: string): key is keyof AnyExecuteProps {
|
||||
return key.startsWith("$any");
|
||||
}
|
||||
// function isAllExecuteKey(key: string): key is keyof AllExecuteProps {
|
||||
// return key.startsWith("$all");
|
||||
// }
|
||||
// function isEveryExecuteKey(key: string): key is keyof EveryExecuteProps {
|
||||
// return key.startsWith("$every");
|
||||
// }
|
||||
// function isAnyExecuteKey(key: string): key is keyof AnyExecuteProps {
|
||||
// return key.startsWith("$any");
|
||||
// }
|
||||
/**
|
||||
* All $prefixed functions are hooked by the modules. Be careful to call them directly.
|
||||
* Please refer to the module's source code to understand the function.
|
||||
@@ -39,100 +39,100 @@ function isAnyExecuteKey(key: string): key is keyof AnyExecuteProps {
|
||||
* $ : Other interceptive points. You should manually assign the module
|
||||
* All of above performed on injectModules function.
|
||||
*/
|
||||
export function injectModules<T extends ICoreModule>(target: T, modules: ICoreModule[]) {
|
||||
const allKeys = unique([
|
||||
...Object.keys(Object.getOwnPropertyDescriptors(target)),
|
||||
...Object.keys(Object.getOwnPropertyDescriptors(Object.getPrototypeOf(target))),
|
||||
]).filter((e) => e.startsWith("$")) as (keyof ICoreModule)[];
|
||||
const moduleMap = new Map<string, IObsidianModule[]>();
|
||||
for (const module of modules) {
|
||||
for (const key of allKeys) {
|
||||
if (isOverridableKey(key)) {
|
||||
if (key in module) {
|
||||
const list = moduleMap.get(key) || [];
|
||||
if (typeof module[key] === "function") {
|
||||
module[key] = module[key].bind(module) as any;
|
||||
}
|
||||
list.push(module);
|
||||
moduleMap.set(key, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Logger(`Injecting modules for ${target.constructor.name}`, LOG_LEVEL_VERBOSE);
|
||||
for (const key of allKeys) {
|
||||
const modules = moduleMap.get(key) || [];
|
||||
if (isInjectableKey(key)) {
|
||||
if (modules.length == 0) {
|
||||
throw new Error(`No module injected for ${key}. This is a fatal error.`);
|
||||
}
|
||||
target[key] = modules[0][key]! as any;
|
||||
Logger(`[${modules[0].constructor.name}]: Injected ${key} `, LOG_LEVEL_VERBOSE);
|
||||
} else if (isAllExecuteKey(key)) {
|
||||
const modules = moduleMap.get(key) || [];
|
||||
target[key] = async (...args: any) => {
|
||||
for (const module of modules) {
|
||||
try {
|
||||
//@ts-ignore
|
||||
await module[key]!(...args);
|
||||
} catch (ex) {
|
||||
Logger(`[${module.constructor.name}]: All handler for ${key} failed`, LOG_LEVEL_VERBOSE);
|
||||
Logger(ex, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
for (const module of modules) {
|
||||
Logger(`[${module.constructor.name}]: Injected (All) ${key} `, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
} else if (isEveryExecuteKey(key)) {
|
||||
target[key] = async (...args: any) => {
|
||||
for (const module of modules) {
|
||||
try {
|
||||
//@ts-ignore:2556
|
||||
const ret = await module[key]!(...args);
|
||||
if (ret !== undefined && !ret) {
|
||||
// Failed then return that falsy value.
|
||||
return ret;
|
||||
}
|
||||
} catch (ex) {
|
||||
Logger(`[${module.constructor.name}]: Every handler for ${key} failed`);
|
||||
Logger(ex, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
for (const module of modules) {
|
||||
Logger(`[${module.constructor.name}]: Injected (Every) ${key} `, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
} else if (isAnyExecuteKey(key)) {
|
||||
//@ts-ignore
|
||||
target[key] = async (...args: any[]) => {
|
||||
for (const module of modules) {
|
||||
try {
|
||||
//@ts-ignore:2556
|
||||
const ret = await module[key](...args);
|
||||
// If truly value returned, then return that value.
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
} catch (ex) {
|
||||
Logger(`[${module.constructor.name}]: Any handler for ${key} failed`);
|
||||
Logger(ex, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
for (const module of modules) {
|
||||
Logger(`[${module.constructor.name}]: Injected (Any) ${key} `, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
} else {
|
||||
Logger(`No injected handler for ${key} `, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
}
|
||||
Logger(`Injected modules for ${target.constructor.name}`, LOG_LEVEL_VERBOSE);
|
||||
return true;
|
||||
}
|
||||
// export function injectModules<T extends ICoreModule>(target: T, modules: ICoreModule[]) {
|
||||
// const allKeys = unique([
|
||||
// ...Object.keys(Object.getOwnPropertyDescriptors(target)),
|
||||
// ...Object.keys(Object.getOwnPropertyDescriptors(Object.getPrototypeOf(target))),
|
||||
// ]).filter((e) => e.startsWith("$")) as (keyof ICoreModule)[];
|
||||
// const moduleMap = new Map<string, IObsidianModule[]>();
|
||||
// for (const module of modules) {
|
||||
// for (const key of allKeys) {
|
||||
// if (isOverridableKey(key)) {
|
||||
// if (key in module) {
|
||||
// const list = moduleMap.get(key) || [];
|
||||
// if (typeof module[key] === "function") {
|
||||
// module[key] = module[key].bind(module) as any;
|
||||
// }
|
||||
// list.push(module);
|
||||
// moduleMap.set(key, list);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Logger(`Injecting modules for ${target.constructor.name}`, LOG_LEVEL_VERBOSE);
|
||||
// for (const key of allKeys) {
|
||||
// const modules = moduleMap.get(key) || [];
|
||||
// if (isInjectableKey(key)) {
|
||||
// if (modules.length == 0) {
|
||||
// throw new Error(`No module injected for ${key}. This is a fatal error.`);
|
||||
// }
|
||||
// target[key] = modules[0][key]! as any;
|
||||
// Logger(`[${modules[0].constructor.name}]: Injected ${key} `, LOG_LEVEL_VERBOSE);
|
||||
// } else if (isAllExecuteKey(key)) {
|
||||
// const modules = moduleMap.get(key) || [];
|
||||
// target[key] = async (...args: any) => {
|
||||
// for (const module of modules) {
|
||||
// try {
|
||||
// //@ts-ignore
|
||||
// await module[key]!(...args);
|
||||
// } catch (ex) {
|
||||
// Logger(`[${module.constructor.name}]: All handler for ${key} failed`, LOG_LEVEL_VERBOSE);
|
||||
// Logger(ex, LOG_LEVEL_VERBOSE);
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
// };
|
||||
// for (const module of modules) {
|
||||
// Logger(`[${module.constructor.name}]: Injected (All) ${key} `, LOG_LEVEL_VERBOSE);
|
||||
// }
|
||||
// } else if (isEveryExecuteKey(key)) {
|
||||
// target[key] = async (...args: any) => {
|
||||
// for (const module of modules) {
|
||||
// try {
|
||||
// //@ts-ignore:2556
|
||||
// const ret = await module[key]!(...args);
|
||||
// if (ret !== undefined && !ret) {
|
||||
// // Failed then return that falsy value.
|
||||
// return ret;
|
||||
// }
|
||||
// } catch (ex) {
|
||||
// Logger(`[${module.constructor.name}]: Every handler for ${key} failed`);
|
||||
// Logger(ex, LOG_LEVEL_VERBOSE);
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
// };
|
||||
// for (const module of modules) {
|
||||
// Logger(`[${module.constructor.name}]: Injected (Every) ${key} `, LOG_LEVEL_VERBOSE);
|
||||
// }
|
||||
// } else if (isAnyExecuteKey(key)) {
|
||||
// //@ts-ignore
|
||||
// target[key] = async (...args: any[]) => {
|
||||
// for (const module of modules) {
|
||||
// try {
|
||||
// //@ts-ignore:2556
|
||||
// const ret = await module[key](...args);
|
||||
// // If truly value returned, then return that value.
|
||||
// if (ret) {
|
||||
// return ret;
|
||||
// }
|
||||
// } catch (ex) {
|
||||
// Logger(`[${module.constructor.name}]: Any handler for ${key} failed`);
|
||||
// Logger(ex, LOG_LEVEL_VERBOSE);
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// };
|
||||
// for (const module of modules) {
|
||||
// Logger(`[${module.constructor.name}]: Injected (Any) ${key} `, LOG_LEVEL_VERBOSE);
|
||||
// }
|
||||
// } else {
|
||||
// Logger(`No injected handler for ${key} `, LOG_LEVEL_VERBOSE);
|
||||
// }
|
||||
// }
|
||||
// Logger(`Injected modules for ${target.constructor.name}`, LOG_LEVEL_VERBOSE);
|
||||
// return true;
|
||||
// }
|
||||
|
||||
export abstract class AbstractModule {
|
||||
_log = (msg: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key?: string) => {
|
||||
@@ -153,14 +153,17 @@ export abstract class AbstractModule {
|
||||
this.core.settings = value;
|
||||
}
|
||||
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services) {
|
||||
// Override if needed.
|
||||
}
|
||||
constructor(public core: LiveSyncCore) {
|
||||
this.onBindFunction(core, core.services);
|
||||
Logger(`[${this.constructor.name}] Loaded`, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
saveSettings = this.core.saveSettings.bind(this.core);
|
||||
|
||||
// abstract $everyTest(): Promise<boolean>;
|
||||
addTestResult(key: string, value: boolean, summary?: string, message?: string) {
|
||||
this.core.$$addTestResult(`${this.constructor.name}`, key, value, summary, message);
|
||||
this.services.test.addTestResult(`${this.constructor.name}`, key, value, summary, message);
|
||||
}
|
||||
testDone(result: boolean = true) {
|
||||
return Promise.resolve(result);
|
||||
@@ -185,4 +188,8 @@ export abstract class AbstractModule {
|
||||
}
|
||||
return this.testDone();
|
||||
}
|
||||
|
||||
get services() {
|
||||
return this.core._services;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,13 +38,13 @@ export abstract class AbstractObsidianModule extends AbstractModule {
|
||||
saveSettings = this.plugin.saveSettings.bind(this.plugin);
|
||||
|
||||
_isMainReady() {
|
||||
return this.core.$$isReady();
|
||||
return this.services.appLifecycle.isReady();
|
||||
}
|
||||
_isMainSuspended() {
|
||||
return this.core.$$isSuspended();
|
||||
return this.services.appLifecycle.isSuspended();
|
||||
}
|
||||
_isDatabaseReady() {
|
||||
return this.core.$$isDatabaseReady();
|
||||
return this.services.database.isDatabaseReady();
|
||||
}
|
||||
|
||||
//should be overridden
|
||||
|
||||
@@ -17,7 +17,6 @@ import type {
|
||||
DocumentID,
|
||||
} from "../../lib/src/common/types";
|
||||
import type { DatabaseFileAccess } from "../interfaces/DatabaseFileAccess";
|
||||
import { type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { isPlainText, shouldBeIgnored, stripAllPrefixes } from "../../lib/src/string_and_binary/path";
|
||||
import {
|
||||
createBlob,
|
||||
@@ -30,14 +29,15 @@ import {
|
||||
import { serialized } from "octagonal-wheels/concurrency/lock";
|
||||
import { AbstractModule } from "../AbstractModule.ts";
|
||||
import { ICHeader } from "../../common/types.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
export class ModuleDatabaseFileAccess extends AbstractModule implements IObsidianModule, DatabaseFileAccess {
|
||||
$everyOnload(): Promise<boolean> {
|
||||
export class ModuleDatabaseFileAccess extends AbstractModule implements DatabaseFileAccess {
|
||||
private _everyOnload(): Promise<boolean> {
|
||||
this.core.databaseFileAccess = this;
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
async $everyModuleTest(): Promise<boolean> {
|
||||
private async _everyModuleTest(): Promise<boolean> {
|
||||
if (!this.settings.enableDebugTools) return Promise.resolve(true);
|
||||
const testString = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nec purus nec nunc";
|
||||
// Before test, we need to delete completely.
|
||||
@@ -75,7 +75,7 @@ export class ModuleDatabaseFileAccess extends AbstractModule implements IObsidia
|
||||
|
||||
async checkIsTargetFile(file: UXFileInfoStub | FilePathWithPrefix): Promise<boolean> {
|
||||
const path = getStoragePathFromUXFileInfo(file);
|
||||
if (!(await this.core.$$isTargetFile(path))) {
|
||||
if (!(await this.services.vault.isTargetFile(path))) {
|
||||
this._log(`File is not target`, LOG_LEVEL_VERBOSE);
|
||||
return false;
|
||||
}
|
||||
@@ -177,7 +177,7 @@ export class ModuleDatabaseFileAccess extends AbstractModule implements IObsidia
|
||||
}
|
||||
}
|
||||
|
||||
const idMain = await this.core.$$path2id(fullPath);
|
||||
const idMain = await this.services.path.path2id(fullPath);
|
||||
|
||||
const id = (idPrefix + idMain) as DocumentID;
|
||||
const d: SavingEntry = {
|
||||
@@ -345,4 +345,8 @@ export class ModuleDatabaseFileAccess extends AbstractModule implements IObsidia
|
||||
eventHub.emitEvent(EVENT_FILE_SAVED);
|
||||
return ret;
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this));
|
||||
services.test.handleTest(this._everyModuleTest.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,11 @@ import {
|
||||
} from "../../common/utils";
|
||||
import { getDocDataAsArray, isDocContentSame, readAsBlob, readContent } from "../../lib/src/common/utils";
|
||||
import { shouldBeIgnored } from "../../lib/src/string_and_binary/path";
|
||||
import type { ICoreModule } from "../ModuleTypes";
|
||||
import { Semaphore } from "octagonal-wheels/concurrency/semaphore";
|
||||
import { eventHub } from "../../common/events.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
export class ModuleFileHandler extends AbstractModule implements ICoreModule {
|
||||
export class ModuleFileHandler extends AbstractModule {
|
||||
get db() {
|
||||
return this.core.databaseFileAccess;
|
||||
}
|
||||
@@ -32,7 +32,7 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule {
|
||||
return this.core.storageAccess;
|
||||
}
|
||||
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
_everyOnloadStart(): Promise<boolean> {
|
||||
this.core.fileHandler = this;
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
@@ -52,7 +52,7 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule {
|
||||
info: UXFileInfoStub | UXFileInfo | UXInternalFileInfoStub | FilePathWithPrefix,
|
||||
force: boolean = false,
|
||||
onlyChunks: boolean = false
|
||||
): Promise<boolean | undefined> {
|
||||
): Promise<boolean> {
|
||||
const file = typeof info === "string" ? this.storage.getFileStub(info) : info;
|
||||
if (file == null) {
|
||||
this._log(`File ${info} is not exist on the storage`, LOG_LEVEL_VERBOSE);
|
||||
@@ -125,7 +125,7 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule {
|
||||
}
|
||||
}
|
||||
|
||||
async deleteFileFromDB(info: UXFileInfoStub | UXInternalFileInfoStub | FilePath): Promise<boolean | undefined> {
|
||||
async deleteFileFromDB(info: UXFileInfoStub | UXInternalFileInfoStub | FilePath): Promise<boolean> {
|
||||
const file = typeof info === "string" ? this.storage.getFileStub(info) : info;
|
||||
if (file == null) {
|
||||
this._log(`File ${info} is not exist on the storage`, LOG_LEVEL_VERBOSE);
|
||||
@@ -222,7 +222,7 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule {
|
||||
// NO OP
|
||||
} else {
|
||||
// If not, then it should be checked. and will be processed later (i.e., after the conflict is resolved).
|
||||
await this.core.$$queueConflictCheckIfOpen(path);
|
||||
await this.services.conflict.queueCheckForIfOpen(path);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -313,11 +313,11 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule {
|
||||
return ret;
|
||||
}
|
||||
|
||||
async $anyHandlerProcessesFileEvent(item: FileEventItem): Promise<boolean | undefined> {
|
||||
private async _anyHandlerProcessesFileEvent(item: FileEventItem): Promise<boolean> {
|
||||
const eventItem = item.args;
|
||||
const type = item.type;
|
||||
const path = eventItem.file.path;
|
||||
if (!(await this.core.$$isTargetFile(path))) {
|
||||
if (!(await this.services.vault.isTargetFile(path))) {
|
||||
this._log(`File ${path} is not the target file`, LOG_LEVEL_VERBOSE);
|
||||
return false;
|
||||
}
|
||||
@@ -343,9 +343,9 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule {
|
||||
});
|
||||
}
|
||||
|
||||
async $anyProcessReplicatedDoc(entry: MetaEntry): Promise<boolean | undefined> {
|
||||
async _anyProcessReplicatedDoc(entry: MetaEntry): Promise<boolean> {
|
||||
return await serialized(entry.path, async () => {
|
||||
if (!(await this.core.$$isTargetFile(entry.path))) {
|
||||
if (!(await this.services.vault.isTargetFile(entry.path))) {
|
||||
this._log(`File ${entry.path} is not the target file`, LOG_LEVEL_VERBOSE);
|
||||
return false;
|
||||
}
|
||||
@@ -362,7 +362,7 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule {
|
||||
return true;
|
||||
} else {
|
||||
this._log(
|
||||
`Processing ${path} (${entry._id.substring(0, 8)}: ${entry._rev?.substring(0, 5)}) :Started...`,
|
||||
`Processing ${path} (${entry._id.substring(0, 8)} :${entry._rev?.substring(0, 5)}) : Started...`,
|
||||
LOG_LEVEL_VERBOSE
|
||||
);
|
||||
// Before writing (or skipped ), merging dialogue should be cancelled.
|
||||
@@ -391,7 +391,7 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule {
|
||||
};
|
||||
const total = filesStorageSrc.length;
|
||||
const procAllChunks = filesStorageSrc.map(async (file) => {
|
||||
if (!(await this.core.$$isTargetFile(file))) {
|
||||
if (!(await this.services.vault.isTargetFile(file))) {
|
||||
incProcessed();
|
||||
return true;
|
||||
}
|
||||
@@ -416,4 +416,9 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule {
|
||||
"chunkCreation"
|
||||
);
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this));
|
||||
services.fileProcessing.handleProcessFileEvent(this._anyHandlerProcessesFileEvent.bind(this));
|
||||
services.replication.handleProcessSynchroniseResult(this._anyProcessReplicatedDoc.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,18 @@ import { $msg } from "../../lib/src/common/i18n";
|
||||
import { LiveSyncLocalDB } from "../../lib/src/pouchdb/LiveSyncLocalDB.ts";
|
||||
import { initializeStores } from "../../common/stores.ts";
|
||||
import { AbstractModule } from "../AbstractModule.ts";
|
||||
import type { ICoreModule } from "../ModuleTypes.ts";
|
||||
import { LiveSyncManagers } from "../../lib/src/managers/LiveSyncManagers.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
export class ModuleLocalDatabaseObsidian extends AbstractModule implements ICoreModule {
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
export class ModuleLocalDatabaseObsidian extends AbstractModule {
|
||||
_everyOnloadStart(): Promise<boolean> {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
async $$openDatabase(): Promise<boolean> {
|
||||
private async _openDatabase(): Promise<boolean> {
|
||||
if (this.localDatabase != null) {
|
||||
await this.localDatabase.close();
|
||||
}
|
||||
const vaultName = this.core.$$getVaultName();
|
||||
const vaultName = this.services.vault.getVaultName();
|
||||
this._log($msg("moduleLocalDatabase.logWaitingForReady"));
|
||||
const getDB = () => this.core.localDatabase.localDatabase;
|
||||
const getSettings = () => this.core.settings;
|
||||
@@ -22,8 +22,9 @@ export class ModuleLocalDatabaseObsidian extends AbstractModule implements ICore
|
||||
return getDB();
|
||||
},
|
||||
getActiveReplicator: () => this.core.replicator,
|
||||
id2path: this.core.$$id2path.bind(this.core),
|
||||
path2id: this.core.$$path2id.bind(this.core),
|
||||
id2path: this.services.path.id2path,
|
||||
// path2id: this.core.$$path2id.bind(this.core),
|
||||
path2id: this.services.path.path2id,
|
||||
get settings() {
|
||||
return getSettings();
|
||||
},
|
||||
@@ -34,7 +35,12 @@ export class ModuleLocalDatabaseObsidian extends AbstractModule implements ICore
|
||||
return await this.localDatabase.initializeDatabase();
|
||||
}
|
||||
|
||||
$$isDatabaseReady(): boolean {
|
||||
_isDatabaseReady(): boolean {
|
||||
return this.localDatabase != null && this.localDatabase.isReady;
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.database.handleIsDatabaseReady(this._isDatabaseReady.bind(this));
|
||||
services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this));
|
||||
services.database.handleOpenDatabase(this._openDatabase.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { PeriodicProcessor } from "../../common/utils";
|
||||
import type { LiveSyncCore } from "../../main";
|
||||
import { AbstractModule } from "../AbstractModule";
|
||||
import type { ICoreModule } from "../ModuleTypes";
|
||||
|
||||
export class ModulePeriodicProcess extends AbstractModule implements ICoreModule {
|
||||
periodicSyncProcessor = new PeriodicProcessor(this.core, async () => await this.core.$$replicate());
|
||||
export class ModulePeriodicProcess extends AbstractModule {
|
||||
periodicSyncProcessor = new PeriodicProcessor(this.core, async () => await this.services.replication.replicate());
|
||||
|
||||
_disablePeriodic() {
|
||||
this.periodicSyncProcessor?.disable();
|
||||
@@ -15,19 +15,27 @@ export class ModulePeriodicProcess extends AbstractModule implements ICoreModule
|
||||
);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
$allOnUnload() {
|
||||
private _allOnUnload() {
|
||||
return this._disablePeriodic();
|
||||
}
|
||||
$everyBeforeRealizeSetting(): Promise<boolean> {
|
||||
_everyBeforeRealizeSetting(): Promise<boolean> {
|
||||
return this._disablePeriodic();
|
||||
}
|
||||
$everyBeforeSuspendProcess(): Promise<boolean> {
|
||||
_everyBeforeSuspendProcess(): Promise<boolean> {
|
||||
return this._disablePeriodic();
|
||||
}
|
||||
$everyAfterResumeProcess(): Promise<boolean> {
|
||||
_everyAfterResumeProcess(): Promise<boolean> {
|
||||
return this._resumePeriodic();
|
||||
}
|
||||
$everyAfterRealizeSetting(): Promise<boolean> {
|
||||
_everyAfterRealizeSetting(): Promise<boolean> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { AbstractModule } from "../AbstractModule";
|
||||
import type { ICoreModule } from "../ModuleTypes";
|
||||
import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser";
|
||||
import type { LiveSyncCore } from "../../main";
|
||||
|
||||
export class ModulePouchDB extends AbstractModule implements ICoreModule {
|
||||
$$createPouchDBInstance<T extends object>(
|
||||
export class ModulePouchDB extends AbstractModule {
|
||||
_createPouchDBInstance<T extends object>(
|
||||
name?: string,
|
||||
options?: PouchDB.Configuration.DatabaseConfiguration
|
||||
): PouchDB.Database<T> {
|
||||
@@ -16,4 +16,7 @@ export class ModulePouchDB extends AbstractModule implements ICoreModule {
|
||||
}
|
||||
return new PouchDB(name, optionPass);
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.database.handleCreatePouchDBInstance(this._createPouchDBInstance.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@ import {
|
||||
} from "../../lib/src/common/types.ts";
|
||||
import { AbstractModule } from "../AbstractModule.ts";
|
||||
import type { Rebuilder } from "../interfaces/DatabaseRebuilder.ts";
|
||||
import type { ICoreModule } from "../ModuleTypes.ts";
|
||||
import type { LiveSyncCouchDBReplicator } from "../../lib/src/replication/couchdb/LiveSyncReplicator.ts";
|
||||
import { fetchAllUsedChunks } from "@/lib/src/pouchdb/chunks.ts";
|
||||
import { EVENT_DATABASE_REBUILT, eventHub } from "src/common/events.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebuilder {
|
||||
$everyOnload(): Promise<boolean> {
|
||||
export class ModuleRebuilder extends AbstractModule implements Rebuilder {
|
||||
private _everyOnload(): Promise<boolean> {
|
||||
this.core.rebuilder = this;
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
@@ -43,47 +43,47 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
|
||||
{ title: "Enable extra features", defaultOption: "No", timeout: 15 }
|
||||
)) == "yes"
|
||||
) {
|
||||
await this.core.$allAskUsingOptionalSyncFeature(opt);
|
||||
await this.services.setting.suggestOptionalFeatures(opt);
|
||||
}
|
||||
}
|
||||
|
||||
async rebuildRemote() {
|
||||
await this.core.$allSuspendExtraSync();
|
||||
await this.services.setting.suspendExtraSync();
|
||||
this.core.settings.isConfigured = true;
|
||||
|
||||
await this.core.$$realizeSettingSyncMode();
|
||||
await this.core.$$markRemoteLocked();
|
||||
await this.core.$$tryResetRemoteDatabase();
|
||||
await this.core.$$markRemoteLocked();
|
||||
await this.services.setting.onRealiseSetting();
|
||||
await this.services.remote.markLocked();
|
||||
await this.services.remote.tryResetDatabase();
|
||||
await this.services.remote.markLocked();
|
||||
await delay(500);
|
||||
await this.askUsingOptionalFeature({ enableOverwrite: true });
|
||||
await delay(1000);
|
||||
await this.core.$$replicateAllToServer(true);
|
||||
await this.services.remote.replicateAllToRemote(true);
|
||||
await delay(1000);
|
||||
await this.core.$$replicateAllToServer(true, true);
|
||||
await this.services.remote.replicateAllToRemote(true, true);
|
||||
}
|
||||
$rebuildRemote(): Promise<void> {
|
||||
return this.rebuildRemote();
|
||||
}
|
||||
|
||||
async rebuildEverything() {
|
||||
await this.core.$allSuspendExtraSync();
|
||||
await this.services.setting.suspendExtraSync();
|
||||
await this.askUseNewAdapter();
|
||||
this.core.settings.isConfigured = true;
|
||||
await this.core.$$realizeSettingSyncMode();
|
||||
await this.services.setting.onRealiseSetting();
|
||||
await this.resetLocalDatabase();
|
||||
await delay(1000);
|
||||
await this.core.$$initializeDatabase(true, true, true);
|
||||
await this.core.$$markRemoteLocked();
|
||||
await this.core.$$tryResetRemoteDatabase();
|
||||
await this.core.$$markRemoteLocked();
|
||||
await this.services.databaseEvents.initialiseDatabase(true, true, true);
|
||||
await this.services.remote.markLocked();
|
||||
await this.services.remote.tryResetDatabase();
|
||||
await this.services.remote.markLocked();
|
||||
await delay(500);
|
||||
// We do not have any other devices' data, so we do not need to ask for overwriting.
|
||||
await this.askUsingOptionalFeature({ enableOverwrite: false });
|
||||
await delay(1000);
|
||||
await this.core.$$replicateAllToServer(true);
|
||||
await this.services.remote.replicateAllToRemote(true);
|
||||
await delay(1000);
|
||||
await this.core.$$replicateAllToServer(true, true);
|
||||
await this.services.remote.replicateAllToRemote(true, true);
|
||||
}
|
||||
|
||||
$rebuildEverything(): Promise<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(ex, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
this.core.$$performRestart();
|
||||
this.services.appLifecycle.performRestart();
|
||||
}
|
||||
async scheduleFetch(): Promise<void> {
|
||||
try {
|
||||
@@ -110,20 +110,20 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
|
||||
this._log("Could not create red_flag_fetch.md", LOG_LEVEL_NOTICE);
|
||||
this._log(ex, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
this.core.$$performRestart();
|
||||
this.services.appLifecycle.performRestart();
|
||||
}
|
||||
|
||||
async $$tryResetRemoteDatabase(): Promise<void> {
|
||||
private async _tryResetRemoteDatabase(): Promise<void> {
|
||||
await this.core.replicator.tryResetRemoteDatabase(this.settings);
|
||||
}
|
||||
|
||||
async $$tryCreateRemoteDatabase(): Promise<void> {
|
||||
private async _tryCreateRemoteDatabase(): Promise<void> {
|
||||
await this.core.replicator.tryCreateRemoteDatabase(this.settings);
|
||||
}
|
||||
|
||||
async $$resetLocalDatabase(): Promise<void> {
|
||||
private async _resetLocalDatabase(): Promise<boolean> {
|
||||
this.core.storageAccess.clearTouched();
|
||||
await this.localDatabase.resetDatabase();
|
||||
return await this.localDatabase.resetDatabase();
|
||||
}
|
||||
|
||||
async suspendAllSync() {
|
||||
@@ -134,7 +134,7 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
|
||||
this.core.settings.syncOnStart = false;
|
||||
this.core.settings.syncOnFileOpen = false;
|
||||
this.core.settings.syncAfterMerge = false;
|
||||
await this.core.$allSuspendExtraSync();
|
||||
await this.services.setting.suspendExtraSync();
|
||||
}
|
||||
async suspendReflectingDatabase() {
|
||||
if (this.core.settings.doNotSuspendOnFetching) return;
|
||||
@@ -153,8 +153,8 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
|
||||
this._log(`Database and storage reflection has been resumed!`, LOG_LEVEL_NOTICE);
|
||||
this.core.settings.suspendParseReplicationResult = false;
|
||||
this.core.settings.suspendFileWatching = false;
|
||||
await this.core.$$performFullScan(true);
|
||||
await this.core.$everyBeforeReplicate(false); //TODO: Check actual need of this.
|
||||
await this.services.vault.scanVault(true);
|
||||
await this.services.replication.onBeforeReplicate(false); //TODO: Check actual need of this.
|
||||
await this.core.saveSettings();
|
||||
}
|
||||
async askUseNewAdapter() {
|
||||
@@ -177,28 +177,28 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
|
||||
}
|
||||
}
|
||||
async fetchLocal(makeLocalChunkBeforeSync?: boolean, preventMakeLocalFilesBeforeSync?: boolean) {
|
||||
await this.core.$allSuspendExtraSync();
|
||||
await this.services.setting.suspendExtraSync();
|
||||
await this.askUseNewAdapter();
|
||||
this.core.settings.isConfigured = true;
|
||||
await this.suspendReflectingDatabase();
|
||||
await this.core.$$realizeSettingSyncMode();
|
||||
await this.services.setting.onRealiseSetting();
|
||||
await this.resetLocalDatabase();
|
||||
await delay(1000);
|
||||
await this.core.$$openDatabase();
|
||||
await this.services.database.openDatabase();
|
||||
// this.core.isReady = true;
|
||||
this.core.$$markIsReady();
|
||||
this.services.appLifecycle.markIsReady();
|
||||
if (makeLocalChunkBeforeSync) {
|
||||
await this.core.fileHandler.createAllChunks(true);
|
||||
} else if (!preventMakeLocalFilesBeforeSync) {
|
||||
await this.core.$$initializeDatabase(true, true, true);
|
||||
await this.services.databaseEvents.initialiseDatabase(true, true, true);
|
||||
} else {
|
||||
// Do not create local file entries before sync (Means use remote information)
|
||||
}
|
||||
await this.core.$$markRemoteResolved();
|
||||
await this.services.remote.markResolved();
|
||||
await delay(500);
|
||||
await this.core.$$replicateAllFromServer(true);
|
||||
await this.services.remote.replicateAllFromRemote(true);
|
||||
await delay(1000);
|
||||
await this.core.$$replicateAllFromServer(true);
|
||||
await this.services.remote.replicateAllFromRemote(true);
|
||||
await this.resumeReflectingDatabase();
|
||||
await this.askUsingOptionalFeature({ enableFetch: true });
|
||||
}
|
||||
@@ -206,7 +206,7 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
|
||||
return await this.fetchLocal(true);
|
||||
}
|
||||
|
||||
async $allSuspendAllSync(): Promise<boolean> {
|
||||
private async _allSuspendAllSync(): Promise<boolean> {
|
||||
await this.suspendAllSync();
|
||||
return true;
|
||||
}
|
||||
@@ -214,11 +214,11 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
|
||||
async resetLocalDatabase() {
|
||||
if (this.core.settings.isConfigured && this.core.settings.additionalSuffixOfDatabaseName == "") {
|
||||
// Discard the non-suffixed database
|
||||
await this.core.$$resetLocalDatabase();
|
||||
await this.services.database.resetDatabase();
|
||||
}
|
||||
const suffix = (await this.core.$anyGetAppId()) || "";
|
||||
const suffix = this.services.API.getAppID() || "";
|
||||
this.core.settings.additionalSuffixOfDatabaseName = suffix;
|
||||
await this.core.$$resetLocalDatabase();
|
||||
await this.services.database.resetDatabase();
|
||||
eventHub.emitEvent(EVENT_DATABASE_REBUILT);
|
||||
}
|
||||
async fetchRemoteChunks() {
|
||||
@@ -228,10 +228,10 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
|
||||
this.core.settings.remoteType == REMOTE_COUCHDB
|
||||
) {
|
||||
this._log(`Fetching chunks`, LOG_LEVEL_NOTICE);
|
||||
const replicator = this.core.$$getReplicator() as LiveSyncCouchDBReplicator;
|
||||
const replicator = this.services.replicator.getActiveReplicator() as LiveSyncCouchDBReplicator;
|
||||
const remoteDB = await replicator.connectRemoteCouchDBWithSetting(
|
||||
this.settings,
|
||||
this.core.$$isMobile(),
|
||||
this.services.API.isMobile(),
|
||||
true
|
||||
);
|
||||
if (typeof remoteDB == "string") {
|
||||
@@ -254,8 +254,15 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
|
||||
LOG_LEVEL_NOTICE,
|
||||
"resolveAllConflictedFilesByNewerOnes"
|
||||
);
|
||||
await this.core.$anyResolveConflictByNewest(file);
|
||||
await this.services.conflict.resolveByNewest(file);
|
||||
}
|
||||
this._log(`Done!`, LOG_LEVEL_NOTICE, "resolveAllConflictedFilesByNewerOnes");
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this));
|
||||
services.database.handleResetDatabase(this._resetLocalDatabase.bind(this));
|
||||
services.remote.handleTryResetDatabase(this._tryResetRemoteDatabase.bind(this));
|
||||
services.remote.handleTryCreateDatabase(this._tryCreateRemoteDatabase.bind(this));
|
||||
services.setting.handleSuspendAllSync(this._allSuspendAllSync.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { fireAndForget, yieldMicrotask } from "octagonal-wheels/promises";
|
||||
import type { LiveSyncLocalDB } from "../../lib/src/pouchdb/LiveSyncLocalDB";
|
||||
import { AbstractModule } from "../AbstractModule";
|
||||
import type { ICoreModule } from "../ModuleTypes";
|
||||
import { Logger, LOG_LEVEL_NOTICE, LOG_LEVEL_INFO, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
||||
import { isLockAcquired, shareRunningResult, skipIfDuplicated } from "octagonal-wheels/concurrency/lock";
|
||||
import { balanceChunkPurgedDBs } from "@/lib/src/pouchdb/chunks";
|
||||
@@ -34,17 +33,18 @@ import type { LiveSyncAbstractReplicator } from "../../lib/src/replication/LiveS
|
||||
|
||||
import { $msg } from "../../lib/src/common/i18n";
|
||||
import { clearHandlers } from "../../lib/src/replication/SyncParamsHandler";
|
||||
import type { LiveSyncCore } from "../../main";
|
||||
|
||||
const KEY_REPLICATION_ON_EVENT = "replicationOnEvent";
|
||||
const REPLICATION_ON_EVENT_FORECASTED_TIME = 5000;
|
||||
|
||||
export class ModuleReplicator extends AbstractModule implements ICoreModule {
|
||||
export class ModuleReplicator extends AbstractModule {
|
||||
_replicatorType?: RemoteType;
|
||||
|
||||
$everyOnloadAfterLoadSettings(): Promise<boolean> {
|
||||
private _everyOnloadAfterLoadSettings(): Promise<boolean> {
|
||||
eventHub.onEvent(EVENT_FILE_SAVED, () => {
|
||||
if (this.settings.syncOnSave && !this.core.$$isSuspended()) {
|
||||
scheduleTask("perform-replicate-after-save", 250, () => this.core.$$replicateByEvent());
|
||||
if (this.settings.syncOnSave && !this.core.services.appLifecycle.isSuspended()) {
|
||||
scheduleTask("perform-replicate-after-save", 250, () => this.services.replication.replicateByEvent());
|
||||
}
|
||||
});
|
||||
eventHub.onEvent(EVENT_SETTING_SAVED, (setting) => {
|
||||
@@ -57,7 +57,7 @@ export class ModuleReplicator extends AbstractModule implements ICoreModule {
|
||||
}
|
||||
|
||||
async setReplicator() {
|
||||
const replicator = await this.core.$anyNewReplicator();
|
||||
const replicator = await this.services.replicator.getNewReplicator();
|
||||
if (!replicator) {
|
||||
this._log($msg("Replicator.Message.InitialiseFatalError"), LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
@@ -74,24 +74,28 @@ export class ModuleReplicator extends AbstractModule implements ICoreModule {
|
||||
return true;
|
||||
}
|
||||
|
||||
$$getReplicator(): LiveSyncAbstractReplicator {
|
||||
_getReplicator(): LiveSyncAbstractReplicator {
|
||||
return this.core.replicator;
|
||||
}
|
||||
|
||||
$everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise<boolean> {
|
||||
_everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise<boolean> {
|
||||
return this.setReplicator();
|
||||
}
|
||||
|
||||
$everyOnResetDatabase(db: LiveSyncLocalDB): Promise<boolean> {
|
||||
_everyOnResetDatabase(db: LiveSyncLocalDB): Promise<boolean> {
|
||||
return this.setReplicator();
|
||||
}
|
||||
async ensureReplicatorPBKDF2Salt(showMessage: boolean = false): Promise<boolean> {
|
||||
// Checking salt
|
||||
const replicator = this.core.$$getReplicator();
|
||||
const replicator = this.services.replicator.getActiveReplicator();
|
||||
if (!replicator) {
|
||||
this._log($msg("Replicator.Message.InitialiseFatalError"), LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
return await replicator.ensurePBKDF2Salt(this.settings, showMessage, true);
|
||||
}
|
||||
|
||||
async $everyBeforeReplicate(showMessage: boolean): Promise<boolean> {
|
||||
async _everyBeforeReplicate(showMessage: boolean): Promise<boolean> {
|
||||
// Checking salt
|
||||
if (!this.core.managers.networkManager.isOnline) {
|
||||
this._log("Network is offline", showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO);
|
||||
@@ -106,7 +110,7 @@ export class ModuleReplicator extends AbstractModule implements ICoreModule {
|
||||
return true;
|
||||
}
|
||||
|
||||
async $$replicate(showMessage: boolean = false): Promise<boolean | void> {
|
||||
private async _replicate(showMessage: boolean = false): Promise<boolean | void> {
|
||||
try {
|
||||
updatePreviousExecutionTime(KEY_REPLICATION_ON_EVENT, REPLICATION_ON_EVENT_FORECASTED_TIME);
|
||||
return await this.$$_replicate(showMessage);
|
||||
@@ -143,11 +147,11 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
|
||||
await this.core.rebuilder.$performRebuildDB("localOnly");
|
||||
}
|
||||
if (ret == CHOICE_CLEAN) {
|
||||
const replicator = this.core.$$getReplicator();
|
||||
const replicator = this.services.replicator.getActiveReplicator();
|
||||
if (!(replicator instanceof LiveSyncCouchDBReplicator)) return;
|
||||
const remoteDB = await replicator.connectRemoteCouchDBWithSetting(
|
||||
this.settings,
|
||||
this.core.$$isMobile(),
|
||||
this.services.API.isMobile(),
|
||||
true
|
||||
);
|
||||
if (typeof remoteDB == "string") {
|
||||
@@ -162,7 +166,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
|
||||
await balanceChunkPurgedDBs(this.localDatabase.localDatabase, remoteDB.db);
|
||||
await purgeUnreferencedChunks(this.localDatabase.localDatabase, false);
|
||||
this.localDatabase.clearCaches();
|
||||
await this.core.$$getReplicator().markRemoteResolved(this.settings);
|
||||
await this.services.replicator.getActiveReplicator()?.markRemoteResolved(this.settings);
|
||||
Logger("The local database has been cleaned up.", showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO);
|
||||
} else {
|
||||
Logger(
|
||||
@@ -174,8 +178,8 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
|
||||
});
|
||||
}
|
||||
|
||||
async $$canReplicate(showMessage: boolean = false): Promise<boolean> {
|
||||
if (!this.core.$$isReady()) {
|
||||
async _canReplicate(showMessage: boolean = false): Promise<boolean> {
|
||||
if (!this.services.appLifecycle.isReady()) {
|
||||
Logger(`Not ready`);
|
||||
return false;
|
||||
}
|
||||
@@ -190,7 +194,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(await this.core.$everyCommitPendingFileEvent())) {
|
||||
if (!(await this.services.fileProcessing.commitPendingFileEvents())) {
|
||||
Logger($msg("Replicator.Message.Pending"), LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
@@ -199,7 +203,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
|
||||
this._log("Network is offline", showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO);
|
||||
return false;
|
||||
}
|
||||
if (!(await this.core.$everyBeforeReplicate(showMessage))) {
|
||||
if (!(await this.services.replication.onBeforeReplicate(showMessage))) {
|
||||
Logger($msg("Replicator.Message.SomeModuleFailed"), LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
@@ -207,14 +211,14 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
|
||||
}
|
||||
|
||||
async $$_replicate(showMessage: boolean = false): Promise<boolean | void> {
|
||||
const checkBeforeReplicate = await this.$$canReplicate(showMessage);
|
||||
const checkBeforeReplicate = await this.services.replication.isReplicationReady(showMessage);
|
||||
if (!checkBeforeReplicate) return false;
|
||||
|
||||
//<-- Here could be an module.
|
||||
const ret = await this.core.replicator.openReplication(this.settings, false, showMessage, false);
|
||||
if (!ret) {
|
||||
if (this.core.replicator.tweakSettingsMismatched && this.core.replicator.preferredTweakValue) {
|
||||
await this.core.$$askResolvingMismatchedTweaks(this.core.replicator.preferredTweakValue);
|
||||
await this.services.tweakValue.askResolvingMismatched(this.core.replicator.preferredTweakValue);
|
||||
} else {
|
||||
if (this.core.replicator?.remoteLockedAndDeviceNotAccepted) {
|
||||
if (this.core.replicator.remoteCleaned && this.settings.useIndexedDBAdapter) {
|
||||
@@ -236,7 +240,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
|
||||
if (ret == CHOICE_FETCH) {
|
||||
this._log($msg("Replicator.Dialogue.Locked.Message.Fetch"), LOG_LEVEL_NOTICE);
|
||||
await this.core.rebuilder.scheduleFetch();
|
||||
this.core.$$scheduleAppReload();
|
||||
this.services.appLifecycle.scheduleRestart();
|
||||
return;
|
||||
} else if (ret == CHOICE_UNLOCK) {
|
||||
await this.core.replicator.markRemoteResolved(this.settings);
|
||||
@@ -250,16 +254,16 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
|
||||
return ret;
|
||||
}
|
||||
|
||||
async $$replicateByEvent(): Promise<boolean | void> {
|
||||
private async _replicateByEvent(): Promise<boolean | void> {
|
||||
const least = this.settings.syncMinimumInterval;
|
||||
if (least > 0) {
|
||||
return rateLimitedSharedExecution(KEY_REPLICATION_ON_EVENT, least, async () => {
|
||||
return await this.$$replicate();
|
||||
return await this.services.replication.replicate();
|
||||
});
|
||||
}
|
||||
return await shareRunningResult(`replication`, () => this.core.$$replicate());
|
||||
return await shareRunningResult(`replication`, () => this.services.replication.replicate());
|
||||
}
|
||||
$$parseReplicationResult(docs: Array<PouchDB.Core.ExistingDocument<EntryDoc>>): void {
|
||||
_parseReplicationResult(docs: Array<PouchDB.Core.ExistingDocument<EntryDoc>>): void {
|
||||
if (this.settings.suspendParseReplicationResult && !this.replicationResultProcessor.isSuspended) {
|
||||
this.replicationResultProcessor.suspend();
|
||||
}
|
||||
@@ -336,7 +340,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
|
||||
this.localDatabase.onNewLeaf(change as EntryLeaf);
|
||||
return;
|
||||
}
|
||||
if (await this.core.$anyModuleParsedReplicationResultItem(change)) return;
|
||||
if (await this.services.replication.processVirtualDocument(change)) return;
|
||||
// any addon needs this item?
|
||||
// for (const proc of this.core.addOns) {
|
||||
// if (await proc.parseReplicationResultItem(change)) {
|
||||
@@ -361,7 +365,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
|
||||
}
|
||||
if (isAnyNote(change)) {
|
||||
const docPath = getPath(change);
|
||||
if (!(await this.core.$$isTargetFile(docPath))) {
|
||||
if (!(await this.services.vault.isTargetFile(docPath))) {
|
||||
Logger(`Skipped: ${docPath}`, LOG_LEVEL_VERBOSE);
|
||||
return;
|
||||
}
|
||||
@@ -369,7 +373,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
|
||||
Logger(`Processing scheduled: ${docPath}`, LOG_LEVEL_INFO);
|
||||
}
|
||||
const size = change.size;
|
||||
if (this.core.$$isFileSizeExceeded(size)) {
|
||||
if (this.services.vault.isFileSizeTooLarge(size)) {
|
||||
Logger(
|
||||
`Processing ${docPath} has been skipped due to file size exceeding the limit`,
|
||||
LOG_LEVEL_NOTICE
|
||||
@@ -413,7 +417,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
|
||||
return;
|
||||
}
|
||||
|
||||
if (await this.core.$anyProcessOptionalSyncFiles(dbDoc)) {
|
||||
if (await this.services.replication.processOptionalSynchroniseResult(dbDoc)) {
|
||||
// Already processed
|
||||
} else if (isValidPath(getPath(doc))) {
|
||||
this.storageApplyingProcessor.enqueue(doc as MetaEntry);
|
||||
@@ -440,7 +444,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
|
||||
storageApplyingProcessor = new QueueProcessor(
|
||||
async (docs: MetaEntry[]) => {
|
||||
const entry = docs[0];
|
||||
await this.core.$anyProcessReplicatedDoc(entry);
|
||||
await this.services.replication.processSynchroniseResult(entry);
|
||||
return;
|
||||
},
|
||||
{
|
||||
@@ -458,17 +462,17 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
|
||||
})
|
||||
.startPipeline();
|
||||
|
||||
$everyBeforeSuspendProcess(): Promise<boolean> {
|
||||
this.core.replicator.closeReplication();
|
||||
_everyBeforeSuspendProcess(): Promise<boolean> {
|
||||
this.core.replicator?.closeReplication();
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
async $$replicateAllToServer(
|
||||
private async _replicateAllToServer(
|
||||
showingNotice: boolean = false,
|
||||
sendChunksInBulkDisabled: boolean = false
|
||||
): Promise<boolean> {
|
||||
if (!this.core.$$isReady()) return false;
|
||||
if (!(await this.core.$everyBeforeReplicate(showingNotice))) {
|
||||
if (!this.services.appLifecycle.isReady()) return false;
|
||||
if (!(await this.services.replication.onBeforeReplicate(showingNotice))) {
|
||||
Logger($msg("Replicator.Message.SomeModuleFailed"), LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
@@ -486,16 +490,31 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
|
||||
}
|
||||
const ret = await this.core.replicator.replicateAllToServer(this.settings, showingNotice);
|
||||
if (ret) return true;
|
||||
const checkResult = await this.core.$anyAfterConnectCheckFailed();
|
||||
if (checkResult == "CHECKAGAIN") return await this.core.$$replicateAllToServer(showingNotice);
|
||||
const checkResult = await this.services.replication.checkConnectionFailure();
|
||||
if (checkResult == "CHECKAGAIN") return await this.services.remote.replicateAllToRemote(showingNotice);
|
||||
return !checkResult;
|
||||
}
|
||||
async $$replicateAllFromServer(showingNotice: boolean = false): Promise<boolean> {
|
||||
if (!this.core.$$isReady()) return false;
|
||||
async _replicateAllFromServer(showingNotice: boolean = false): Promise<boolean> {
|
||||
if (!this.services.appLifecycle.isReady()) return false;
|
||||
const ret = await this.core.replicator.replicateAllFromServer(this.settings, showingNotice);
|
||||
if (ret) return true;
|
||||
const checkResult = await this.core.$anyAfterConnectCheckFailed();
|
||||
if (checkResult == "CHECKAGAIN") return await this.core.$$replicateAllFromServer(showingNotice);
|
||||
const checkResult = await this.services.replication.checkConnectionFailure();
|
||||
if (checkResult == "CHECKAGAIN") return await this.services.remote.replicateAllFromRemote(showingNotice);
|
||||
return !checkResult;
|
||||
}
|
||||
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.replicator.handleGetActiveReplicator(this._getReplicator.bind(this));
|
||||
services.databaseEvents.handleOnDatabaseInitialisation(this._everyOnInitializeDatabase.bind(this));
|
||||
services.databaseEvents.handleOnResetDatabase(this._everyOnResetDatabase.bind(this));
|
||||
services.appLifecycle.handleOnSettingLoaded(this._everyOnloadAfterLoadSettings.bind(this));
|
||||
services.replication.handleParseSynchroniseResult(this._parseReplicationResult.bind(this));
|
||||
services.appLifecycle.handleOnSuspending(this._everyBeforeSuspendProcess.bind(this));
|
||||
services.replication.handleBeforeReplicate(this._everyBeforeReplicate.bind(this));
|
||||
services.replication.handleIsReplicationReady(this._canReplicate.bind(this));
|
||||
services.replication.handleReplicate(this._replicate.bind(this));
|
||||
services.replication.handleReplicateByEvent(this._replicateByEvent.bind(this));
|
||||
services.remote.handleReplicateAllToRemote(this._replicateAllToServer.bind(this));
|
||||
services.remote.handleReplicateAllFromRemote(this._replicateAllFromServer.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,20 +3,20 @@ import { REMOTE_MINIO, REMOTE_P2P, type RemoteDBSettings } from "../../lib/src/c
|
||||
import { LiveSyncCouchDBReplicator } from "../../lib/src/replication/couchdb/LiveSyncReplicator";
|
||||
import type { LiveSyncAbstractReplicator } from "../../lib/src/replication/LiveSyncAbstractReplicator";
|
||||
import { AbstractModule } from "../AbstractModule";
|
||||
import type { ICoreModule } from "../ModuleTypes";
|
||||
import type { LiveSyncCore } from "../../main";
|
||||
|
||||
export class ModuleReplicatorCouchDB extends AbstractModule implements ICoreModule {
|
||||
$anyNewReplicator(settingOverride: Partial<RemoteDBSettings> = {}): Promise<LiveSyncAbstractReplicator> {
|
||||
export class ModuleReplicatorCouchDB extends AbstractModule {
|
||||
_anyNewReplicator(settingOverride: Partial<RemoteDBSettings> = {}): Promise<LiveSyncAbstractReplicator | false> {
|
||||
const settings = { ...this.settings, ...settingOverride };
|
||||
// If new remote types were added, add them here. Do not use `REMOTE_COUCHDB` directly for the safety valve.
|
||||
if (settings.remoteType == REMOTE_MINIO || settings.remoteType == REMOTE_P2P) {
|
||||
return undefined!;
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
return Promise.resolve(new LiveSyncCouchDBReplicator(this.core));
|
||||
}
|
||||
$everyAfterResumeProcess(): Promise<boolean> {
|
||||
if (!this.core.$$isSuspended) return Promise.resolve(true);
|
||||
if (!this.core.$$isReady) return Promise.resolve(true);
|
||||
_everyAfterResumeProcess(): Promise<boolean> {
|
||||
if (!this.services.appLifecycle.isSuspended()) return Promise.resolve(true);
|
||||
if (!this.services.appLifecycle.isReady()) return Promise.resolve(true);
|
||||
if (this.settings.remoteType != REMOTE_MINIO && this.settings.remoteType != REMOTE_P2P) {
|
||||
const LiveSyncEnabled = this.settings.liveSync;
|
||||
const continuous = LiveSyncEnabled;
|
||||
@@ -27,7 +27,7 @@ export class ModuleReplicatorCouchDB extends AbstractModule implements ICoreModu
|
||||
// And note that we do not open the conflict detection dialogue directly during this process.
|
||||
// This should be raised explicitly if needed.
|
||||
fireAndForget(async () => {
|
||||
const canReplicate = await this.core.$$canReplicate(false);
|
||||
const canReplicate = await this.services.replication.isReplicationReady(false);
|
||||
if (!canReplicate) return;
|
||||
void this.core.replicator.openReplication(this.settings, continuous, false, false);
|
||||
});
|
||||
@@ -36,4 +36,8 @@ export class ModuleReplicatorCouchDB extends AbstractModule implements ICoreModu
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.replicator.handleGetNewReplicator(this._anyNewReplicator.bind(this));
|
||||
services.appLifecycle.handleOnResumed(this._everyAfterResumeProcess.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { REMOTE_MINIO, type RemoteDBSettings } from "../../lib/src/common/types";
|
||||
import { LiveSyncJournalReplicator } from "../../lib/src/replication/journal/LiveSyncJournalReplicator";
|
||||
import type { LiveSyncAbstractReplicator } from "../../lib/src/replication/LiveSyncAbstractReplicator";
|
||||
import type { LiveSyncCore } from "../../main";
|
||||
import { AbstractModule } from "../AbstractModule";
|
||||
import type { ICoreModule } from "../ModuleTypes";
|
||||
|
||||
export class ModuleReplicatorMinIO extends AbstractModule implements ICoreModule {
|
||||
$anyNewReplicator(settingOverride: Partial<RemoteDBSettings> = {}): Promise<LiveSyncAbstractReplicator> {
|
||||
export class ModuleReplicatorMinIO extends AbstractModule {
|
||||
_anyNewReplicator(settingOverride: Partial<RemoteDBSettings> = {}): Promise<LiveSyncAbstractReplicator | false> {
|
||||
const settings = { ...this.settings, ...settingOverride };
|
||||
if (settings.remoteType == REMOTE_MINIO) {
|
||||
return Promise.resolve(new LiveSyncJournalReplicator(this.core));
|
||||
}
|
||||
return undefined!;
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.replicator.handleGetNewReplicator(this._anyNewReplicator.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { REMOTE_P2P, type RemoteDBSettings } from "../../lib/src/common/types";
|
||||
import type { LiveSyncAbstractReplicator } from "../../lib/src/replication/LiveSyncAbstractReplicator";
|
||||
import { AbstractModule } from "../AbstractModule";
|
||||
import type { ICoreModule } from "../ModuleTypes";
|
||||
import { LiveSyncTrysteroReplicator } from "../../lib/src/replication/trystero/LiveSyncTrysteroReplicator";
|
||||
import type { LiveSyncCore } from "../../main";
|
||||
|
||||
export class ModuleReplicatorP2P extends AbstractModule implements ICoreModule {
|
||||
$anyNewReplicator(settingOverride: Partial<RemoteDBSettings> = {}): Promise<LiveSyncAbstractReplicator> {
|
||||
export class ModuleReplicatorP2P extends AbstractModule {
|
||||
_anyNewReplicator(settingOverride: Partial<RemoteDBSettings> = {}): Promise<LiveSyncAbstractReplicator | false> {
|
||||
const settings = { ...this.settings, ...settingOverride };
|
||||
if (settings.remoteType == REMOTE_P2P) {
|
||||
return Promise.resolve(new LiveSyncTrysteroReplicator(this.core));
|
||||
}
|
||||
return undefined!;
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
$everyAfterResumeProcess(): Promise<boolean> {
|
||||
_everyAfterResumeProcess(): Promise<boolean> {
|
||||
if (this.settings.remoteType == REMOTE_P2P) {
|
||||
// // If LiveSync enabled, open replication
|
||||
// if (this.settings.liveSync) {
|
||||
@@ -27,4 +27,8 @@ export class ModuleReplicatorP2P extends AbstractModule implements ICoreModule {
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.replicator.handleGetNewReplicator(this._anyNewReplicator.bind(this));
|
||||
services.appLifecycle.handleOnResumed(this._everyAfterResumeProcess.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,14 @@ import {
|
||||
} from "../../lib/src/common/types";
|
||||
import { addPrefix, isAcceptedAll } from "../../lib/src/string_and_binary/path";
|
||||
import { AbstractModule } from "../AbstractModule";
|
||||
import type { ICoreModule } from "../ModuleTypes";
|
||||
import { EVENT_REQUEST_RELOAD_SETTING_TAB, EVENT_SETTING_SAVED, eventHub } from "../../common/events";
|
||||
import { isDirty } from "../../lib/src/common/utils";
|
||||
export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
|
||||
import type { LiveSyncCore } from "../../main";
|
||||
export class ModuleTargetFilter extends AbstractModule {
|
||||
reloadIgnoreFiles() {
|
||||
this.ignoreFiles = this.settings.ignoreFiles.split(",").map((e) => e.trim());
|
||||
}
|
||||
$everyOnload(): Promise<boolean> {
|
||||
private _everyOnload(): Promise<boolean> {
|
||||
eventHub.onEvent(EVENT_SETTING_SAVED, (evt: ObsidianLiveSyncSettings) => {
|
||||
this.reloadIgnoreFiles();
|
||||
});
|
||||
@@ -35,7 +35,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
$$id2path(id: DocumentID, entry?: EntryHasPath, stripPrefix?: boolean): FilePathWithPrefix {
|
||||
_id2path(id: DocumentID, entry?: EntryHasPath, stripPrefix?: boolean): FilePathWithPrefix {
|
||||
const tempId = id2path(id, entry);
|
||||
if (stripPrefix && isInternalMetadata(tempId)) {
|
||||
const out = stripInternalMetadataPrefix(tempId);
|
||||
@@ -43,7 +43,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
|
||||
}
|
||||
return tempId;
|
||||
}
|
||||
async $$path2id(filename: FilePathWithPrefix | FilePath, prefix?: string): Promise<DocumentID> {
|
||||
async _path2id(filename: FilePathWithPrefix | FilePath, prefix?: string): Promise<DocumentID> {
|
||||
const destPath = addPrefix(filename, prefix ?? "");
|
||||
return await path2id(
|
||||
destPath,
|
||||
@@ -52,7 +52,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
|
||||
);
|
||||
}
|
||||
|
||||
$$isFileSizeExceeded(size: number) {
|
||||
private _isFileSizeExceeded(size: number) {
|
||||
if (this.settings.syncMaxSizeInMB > 0 && size > 0) {
|
||||
if (this.settings.syncMaxSizeInMB * 1024 * 1024 < size) {
|
||||
return true;
|
||||
@@ -61,7 +61,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
|
||||
return false;
|
||||
}
|
||||
|
||||
$$markFileListPossiblyChanged(): void {
|
||||
_markFileListPossiblyChanged(): void {
|
||||
this.totalFileEventCount++;
|
||||
}
|
||||
totalFileEventCount = 0;
|
||||
@@ -72,7 +72,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
|
||||
return false;
|
||||
}
|
||||
|
||||
async $$isTargetFile(file: string | UXFileInfoStub, keepFileCheckList = false) {
|
||||
private async _isTargetFile(file: string | UXFileInfoStub, keepFileCheckList = false) {
|
||||
const fileCount = useMemo<Record<string, number>>(
|
||||
{
|
||||
key: "fileCount", // forceUpdate: !keepFileCheckList,
|
||||
@@ -109,7 +109,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
|
||||
|
||||
const filepath = getStoragePathFromUXFileInfo(file);
|
||||
const lc = filepath.toLowerCase();
|
||||
if (this.core.$$shouldCheckCaseInsensitive()) {
|
||||
if (this.services.setting.shouldCheckCaseInsensitively()) {
|
||||
if (lc in fileCount && fileCount[lc] > 1) {
|
||||
return false;
|
||||
}
|
||||
@@ -120,7 +120,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
|
||||
// We must reload ignore files due to the its change.
|
||||
await this.readIgnoreFile(filepath);
|
||||
}
|
||||
if (await this.core.$$isIgnoredByIgnoreFiles(file)) {
|
||||
if (await this.services.vault.isIgnoredByIgnoreFile(file)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -150,7 +150,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
|
||||
return await this.readIgnoreFile(path);
|
||||
}
|
||||
}
|
||||
async $$isIgnoredByIgnoreFiles(file: string | UXFileInfoStub): Promise<boolean> {
|
||||
private async _isIgnoredByIgnoreFiles(file: string | UXFileInfoStub): Promise<boolean> {
|
||||
if (!this.settings.useIgnoreFiles) {
|
||||
return false;
|
||||
}
|
||||
@@ -164,4 +164,14 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.vault.handleMarkFileListPossiblyChanged(this._markFileListPossiblyChanged.bind(this));
|
||||
services.path.handleId2Path(this._id2path.bind(this));
|
||||
services.path.handlePath2Id(this._path2id.bind(this));
|
||||
services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this));
|
||||
services.vault.handleIsFileSizeTooLarge(this._isFileSizeExceeded.bind(this));
|
||||
services.vault.handleIsIgnoredByIgnoreFile(this._isIgnoredByIgnoreFiles.bind(this));
|
||||
services.vault.handleIsTargetFile(this._isTargetFile.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
||||
import { AbstractModule } from "../AbstractModule.ts";
|
||||
import { sizeToHumanReadable } from "octagonal-wheels/number";
|
||||
import type { ICoreModule } from "../ModuleTypes.ts";
|
||||
import { $msg } from "src/lib/src/common/i18n.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
export class ModuleCheckRemoteSize extends AbstractModule implements ICoreModule {
|
||||
async $allScanStat(): Promise<boolean> {
|
||||
export class ModuleCheckRemoteSize extends AbstractModule {
|
||||
async _allScanStat(): Promise<boolean> {
|
||||
if (this.core.managers.networkManager.isOnline === false) {
|
||||
this._log("Network is offline, skipping remote size check.", LOG_LEVEL_INFO);
|
||||
return true;
|
||||
@@ -109,4 +109,7 @@ export class ModuleCheckRemoteSize extends AbstractModule implements ICoreModule
|
||||
}
|
||||
return true;
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.appLifecycle.handleOnScanningStartupIssues(this._allScanStat.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,35 +2,36 @@ import { AbstractModule } from "../AbstractModule.ts";
|
||||
import { LOG_LEVEL_NOTICE, type FilePathWithPrefix } from "../../lib/src/common/types";
|
||||
import { QueueProcessor } from "octagonal-wheels/concurrency/processor";
|
||||
import { sendValue } from "octagonal-wheels/messagepassing/signal";
|
||||
import type { ICoreModule } from "../ModuleTypes.ts";
|
||||
import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
export class ModuleConflictChecker extends AbstractModule implements ICoreModule {
|
||||
async $$queueConflictCheckIfOpen(file: FilePathWithPrefix): Promise<void> {
|
||||
export class ModuleConflictChecker extends AbstractModule {
|
||||
async _queueConflictCheckIfOpen(file: FilePathWithPrefix): Promise<void> {
|
||||
const path = file;
|
||||
if (this.settings.checkConflictOnlyOnOpen) {
|
||||
const af = this.core.$$getActiveFilePath();
|
||||
const af = this.services.vault.getActiveFilePath();
|
||||
if (af && af != path) {
|
||||
this._log(`${file} is conflicted, merging process has been postponed.`, LOG_LEVEL_NOTICE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
await this.core.$$queueConflictCheck(path);
|
||||
await this.services.conflict.queueCheckFor(path);
|
||||
}
|
||||
|
||||
async $$queueConflictCheck(file: FilePathWithPrefix): Promise<void> {
|
||||
const optionalConflictResult = await this.core.$anyGetOptionalConflictCheckMethod(file);
|
||||
async _queueConflictCheck(file: FilePathWithPrefix): Promise<void> {
|
||||
const optionalConflictResult = await this.services.conflict.getOptionalConflictCheckMethod(file);
|
||||
if (optionalConflictResult == true) {
|
||||
// The conflict has been resolved by another process.
|
||||
return;
|
||||
} else if (optionalConflictResult === "newer") {
|
||||
// The conflict should be resolved by the newer entry.
|
||||
await this.core.$anyResolveConflictByNewest(file);
|
||||
await this.services.conflict.resolveByNewest(file);
|
||||
} else {
|
||||
this.conflictCheckQueue.enqueue(file);
|
||||
}
|
||||
}
|
||||
|
||||
$$waitForAllConflictProcessed(): Promise<boolean> {
|
||||
_waitForAllConflictProcessed(): Promise<boolean> {
|
||||
return this.conflictResolveQueue.waitForAllProcessed();
|
||||
}
|
||||
|
||||
@@ -38,7 +39,7 @@ export class ModuleConflictChecker extends AbstractModule implements ICoreModule
|
||||
conflictResolveQueue = new QueueProcessor(
|
||||
async (filenames: FilePathWithPrefix[]) => {
|
||||
const filename = filenames[0];
|
||||
return await this.core.$$resolveConflict(filename);
|
||||
return await this.services.conflict.resolve(filename);
|
||||
},
|
||||
{
|
||||
suspended: false,
|
||||
@@ -73,4 +74,9 @@ export class ModuleConflictChecker extends AbstractModule implements ICoreModule
|
||||
totalRemainingReactiveSource: this.core.conflictProcessQueueCount,
|
||||
}
|
||||
);
|
||||
onBindFunction(core: LiveSyncCore, services: InjectableServiceHub): void {
|
||||
services.conflict.handleQueueCheckForIfOpen(this._queueConflictCheckIfOpen.bind(this));
|
||||
services.conflict.handleQueueCheckFor(this._queueConflictCheck.bind(this));
|
||||
services.conflict.handleEnsureAllProcessed(this._waitForAllConflictProcessed.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,9 @@ import {
|
||||
} from "../../common/utils";
|
||||
import diff_match_patch from "diff-match-patch";
|
||||
import { stripAllPrefixes, isPlainText } from "../../lib/src/string_and_binary/path";
|
||||
import type { ICoreModule } from "../ModuleTypes.ts";
|
||||
import { eventHub } from "../../common/events.ts";
|
||||
import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
declare global {
|
||||
interface LSEvents {
|
||||
@@ -29,8 +30,8 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
export class ModuleConflictResolver extends AbstractModule implements ICoreModule {
|
||||
async $$resolveConflictByDeletingRev(
|
||||
export class ModuleConflictResolver extends AbstractModule {
|
||||
private async _resolveConflictByDeletingRev(
|
||||
path: FilePathWithPrefix,
|
||||
deleteRevision: string,
|
||||
subTitle = ""
|
||||
@@ -82,7 +83,7 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul
|
||||
return MISSING_OR_ERROR;
|
||||
}
|
||||
// 2. As usual, delete the conflicted revision and if there are no conflicts, write the resolved content to the storage.
|
||||
return await this.core.$$resolveConflictByDeletingRev(path, ret.conflictedRev, "Sensible");
|
||||
return await this.services.conflict.resolveByDeletingRevision(path, ret.conflictedRev, "Sensible");
|
||||
}
|
||||
|
||||
const { rightRev, leftLeaf, rightLeaf } = ret;
|
||||
@@ -95,7 +96,7 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul
|
||||
}
|
||||
if (rightLeaf == false) {
|
||||
// Conflicted item could not load, delete this.
|
||||
return await this.core.$$resolveConflictByDeletingRev(path, rightRev, "MISSING OLD REV");
|
||||
return await this.services.conflict.resolveByDeletingRevision(path, rightRev, "MISSING OLD REV");
|
||||
}
|
||||
|
||||
const isSame = leftLeaf.data == rightLeaf.data && leftLeaf.deleted == rightLeaf.deleted;
|
||||
@@ -115,7 +116,7 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul
|
||||
]
|
||||
.filter((e) => e.trim())
|
||||
.join(",");
|
||||
return await this.core.$$resolveConflictByDeletingRev(path, loser.rev, subTitle);
|
||||
return await this.services.conflict.resolveByDeletingRevision(path, loser.rev, subTitle);
|
||||
}
|
||||
// make diff.
|
||||
const dmp = new diff_match_patch();
|
||||
@@ -129,7 +130,7 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul
|
||||
};
|
||||
}
|
||||
|
||||
async $$resolveConflict(filename: FilePathWithPrefix): Promise<void> {
|
||||
private async _resolveConflict(filename: FilePathWithPrefix): Promise<void> {
|
||||
// const filename = filenames[0];
|
||||
return await serialized(`conflict-resolve:${filename}`, async () => {
|
||||
const conflictCheckResult = await this.checkConflictAndPerformAutoMerge(filename);
|
||||
@@ -144,16 +145,16 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul
|
||||
}
|
||||
if (conflictCheckResult === AUTO_MERGED) {
|
||||
//auto resolved, but need check again;
|
||||
if (this.settings.syncAfterMerge && !this.core.$$isSuspended()) {
|
||||
if (this.settings.syncAfterMerge && !this.services.appLifecycle.isSuspended()) {
|
||||
//Wait for the running replication, if not running replication, run it once.
|
||||
await this.core.$$replicateByEvent();
|
||||
await this.services.replication.replicateByEvent();
|
||||
}
|
||||
this._log("[conflict] Automatically merged, but we have to check it again");
|
||||
await this.core.$$queueConflictCheck(filename);
|
||||
await this.services.conflict.queueCheckFor(filename);
|
||||
return;
|
||||
}
|
||||
if (this.settings.showMergeDialogOnlyOnActive) {
|
||||
const af = this.core.$$getActiveFilePath();
|
||||
const af = this.services.vault.getActiveFilePath();
|
||||
if (af && af != filename) {
|
||||
this._log(
|
||||
`[conflict] ${filename} is conflicted. Merging process has been postponed to the file have got opened.`,
|
||||
@@ -164,11 +165,11 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul
|
||||
}
|
||||
this._log("[conflict] Manual merge required!");
|
||||
eventHub.emitEvent("conflict-cancelled", filename);
|
||||
await this.core.$anyResolveConflictByUI(filename, conflictCheckResult);
|
||||
await this.services.conflict.resolveByUserInteraction(filename, conflictCheckResult);
|
||||
});
|
||||
}
|
||||
|
||||
async $anyResolveConflictByNewest(filename: FilePathWithPrefix): Promise<boolean> {
|
||||
private async _anyResolveConflictByNewest(filename: FilePathWithPrefix): Promise<boolean> {
|
||||
const currentRev = await this.core.databaseFileAccess.fetchEntryMeta(filename, undefined, true);
|
||||
if (currentRev == false) {
|
||||
this._log(`Could not get current revision of ${filename}`);
|
||||
@@ -206,8 +207,14 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul
|
||||
this._log(
|
||||
`conflict: Deleting the older revision ${mTimeAndRev[i][1]} (${new Date(mTimeAndRev[i][0]).toLocaleString()}) of ${filename}`
|
||||
);
|
||||
await this.core.$$resolveConflictByDeletingRev(filename, mTimeAndRev[i][1], "NEWEST");
|
||||
await this.services.conflict.resolveByDeletingRevision(filename, mTimeAndRev[i][1], "NEWEST");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
onBindFunction(core: LiveSyncCore, services: InjectableServiceHub): void {
|
||||
services.conflict.handleResolveByDeletingRevision(this._resolveConflictByDeletingRev.bind(this));
|
||||
services.conflict.handleResolve(this._resolveConflict.bind(this));
|
||||
services.conflict.handleResolveByNewest(this._anyResolveConflictByNewest.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ import {
|
||||
type ObsidianLiveSyncSettings,
|
||||
} from "../../lib/src/common/types.ts";
|
||||
import { AbstractModule } from "../AbstractModule.ts";
|
||||
import type { ICoreModule } from "../ModuleTypes.ts";
|
||||
import { $msg } from "../../lib/src/common/i18n.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
export class ModuleRedFlag extends AbstractModule implements ICoreModule {
|
||||
export class ModuleRedFlag extends AbstractModule {
|
||||
async isFlagFileExist(path: string) {
|
||||
const redflag = await this.core.storageAccess.isExists(normalizePath(path));
|
||||
if (redflag) {
|
||||
@@ -48,7 +48,7 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule {
|
||||
await this.deleteFlagFile(FLAGMD_REDFLAG3);
|
||||
await this.deleteFlagFile(FLAGMD_REDFLAG3_HR);
|
||||
}
|
||||
async $everyOnLayoutReady(): Promise<boolean> {
|
||||
async _everyOnLayoutReady(): Promise<boolean> {
|
||||
try {
|
||||
const isRedFlagRaised = await this.isRedFlagRaised();
|
||||
const isRedFlag2Raised = await this.isRedFlag2Raised();
|
||||
@@ -63,7 +63,7 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule {
|
||||
)) !== "yes"
|
||||
) {
|
||||
await this.deleteRedFlag2();
|
||||
await this.core.$$performRestart();
|
||||
this.services.appLifecycle.performRestart();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -75,13 +75,13 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule {
|
||||
})) !== "yes"
|
||||
) {
|
||||
await this.deleteRedFlag3();
|
||||
await this.core.$$performRestart();
|
||||
this.services.appLifecycle.performRestart();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this.settings.batchSave = false;
|
||||
await this.core.$allSuspendAllSync();
|
||||
await this.core.$allSuspendExtraSync();
|
||||
await this.services.setting.suspendAllSync();
|
||||
await this.services.setting.suspendExtraSync();
|
||||
this.settings.suspendFileWatching = true;
|
||||
await this.saveSettings();
|
||||
if (isRedFlag2Raised) {
|
||||
@@ -99,7 +99,7 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule {
|
||||
) {
|
||||
this.settings.suspendFileWatching = false;
|
||||
await this.saveSettings();
|
||||
this.core.$$performRestart();
|
||||
this.services.appLifecycle.performRestart();
|
||||
return false;
|
||||
}
|
||||
} else if (isRedFlag3Raised) {
|
||||
@@ -148,7 +148,7 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule {
|
||||
if (fetchRemote === optionFetchRemoteConf) {
|
||||
this._log("Fetching remote configuration", LOG_LEVEL_NOTICE);
|
||||
const newSettings = JSON.parse(JSON.stringify(this.core.settings)) as ObsidianLiveSyncSettings;
|
||||
const remoteConfig = await this.core.$$fetchRemotePreferredTweakValues(newSettings);
|
||||
const remoteConfig = await this.services.tweakValue.fetchRemotePreferred(newSettings);
|
||||
if (remoteConfig) {
|
||||
this._log("Remote configuration found.", LOG_LEVEL_NOTICE);
|
||||
const mergedSettings = {
|
||||
@@ -174,7 +174,7 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule {
|
||||
) {
|
||||
this.settings.suspendFileWatching = false;
|
||||
await this.saveSettings();
|
||||
this.core.$$performRestart();
|
||||
this.services.appLifecycle.performRestart();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
@@ -198,4 +198,8 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
super.onBindFunction(core, services);
|
||||
services.appLifecycle.handleLayoutReady(this._everyOnLayoutReady.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
import { AbstractModule } from "../AbstractModule.ts";
|
||||
import type { ICoreModule } from "../ModuleTypes.ts";
|
||||
|
||||
export class ModuleRemoteGovernor extends AbstractModule implements ICoreModule {
|
||||
async $$markRemoteLocked(lockByClean: boolean = false): Promise<void> {
|
||||
export class ModuleRemoteGovernor extends AbstractModule {
|
||||
private async _markRemoteLocked(lockByClean: boolean = false): Promise<void> {
|
||||
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);
|
||||
}
|
||||
|
||||
async $$markRemoteResolved(): Promise<void> {
|
||||
private async _markRemoteResolved(): Promise<void> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,21 +11,22 @@ import {
|
||||
} from "../../lib/src/common/types.ts";
|
||||
import { escapeMarkdownValue } from "../../lib/src/common/utils.ts";
|
||||
import { AbstractModule } from "../AbstractModule.ts";
|
||||
import type { ICoreModule } from "../ModuleTypes.ts";
|
||||
import { $msg } from "../../lib/src/common/i18n.ts";
|
||||
import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
export class ModuleResolvingMismatchedTweaks extends AbstractModule implements ICoreModule {
|
||||
async $anyAfterConnectCheckFailed(): Promise<boolean | "CHECKAGAIN" | undefined> {
|
||||
export class ModuleResolvingMismatchedTweaks extends AbstractModule {
|
||||
async _anyAfterConnectCheckFailed(): Promise<boolean | "CHECKAGAIN" | undefined> {
|
||||
if (!this.core.replicator.tweakSettingsMismatched && !this.core.replicator.preferredTweakValue) return false;
|
||||
const preferred = this.core.replicator.preferredTweakValue;
|
||||
if (!preferred) return false;
|
||||
const ret = await this.core.$$askResolvingMismatchedTweaks(preferred);
|
||||
const ret = await this.services.tweakValue.askResolvingMismatched(preferred);
|
||||
if (ret == "OK") return false;
|
||||
if (ret == "CHECKAGAIN") return "CHECKAGAIN";
|
||||
if (ret == "IGNORE") return true;
|
||||
}
|
||||
|
||||
async $$checkAndAskResolvingMismatchedTweaks(
|
||||
async _checkAndAskResolvingMismatchedTweaks(
|
||||
preferred: Partial<TweakValues>
|
||||
): Promise<[TweakValues | boolean, boolean]> {
|
||||
const mine = extractObject(TweakValuesShouldMatchedTemplate, this.settings);
|
||||
@@ -127,7 +128,7 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule implements I
|
||||
return CHOICES[retKey];
|
||||
}
|
||||
|
||||
async $$askResolvingMismatchedTweaks(): Promise<"OK" | "CHECKAGAIN" | "IGNORE"> {
|
||||
async _askResolvingMismatchedTweaks(): Promise<"OK" | "CHECKAGAIN" | "IGNORE"> {
|
||||
if (!this.core.replicator.tweakSettingsMismatched) {
|
||||
return "OK";
|
||||
}
|
||||
@@ -137,7 +138,7 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule implements I
|
||||
}
|
||||
const preferred = extractObject(TweakValuesShouldMatchedTemplate, tweaks);
|
||||
|
||||
const [conf, rebuildRequired] = await this.core.$$checkAndAskResolvingMismatchedTweaks(preferred);
|
||||
const [conf, rebuildRequired] = await this.services.tweakValue.checkAndAskResolvingMismatched(preferred);
|
||||
if (!conf) return "IGNORE";
|
||||
|
||||
if (conf === true) {
|
||||
@@ -154,7 +155,7 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule implements I
|
||||
if (conf) {
|
||||
this.settings = { ...this.settings, ...conf };
|
||||
await this.core.replicator.setPreferredRemoteTweakSettings(this.settings);
|
||||
await this.core.$$saveSettingData();
|
||||
await this.services.setting.saveSettingData();
|
||||
if (rebuildRequired) {
|
||||
await this.core.rebuilder.$fetchLocal();
|
||||
}
|
||||
@@ -164,8 +165,12 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule implements I
|
||||
return "IGNORE";
|
||||
}
|
||||
|
||||
async $$fetchRemotePreferredTweakValues(trialSetting: RemoteDBSettings): Promise<TweakValues | false> {
|
||||
const replicator = await this.core.$anyNewReplicator();
|
||||
async _fetchRemotePreferredTweakValues(trialSetting: RemoteDBSettings): Promise<TweakValues | false> {
|
||||
const replicator = await this.services.replicator.getNewReplicator(trialSetting);
|
||||
if (!replicator) {
|
||||
this._log("The remote type is not supported for fetching preferred tweak values.", LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
if (await replicator.tryConnectRemote(trialSetting)) {
|
||||
const preferred = await replicator.getRemotePreferredTweakValues(trialSetting);
|
||||
if (preferred) {
|
||||
@@ -178,17 +183,17 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule implements I
|
||||
return false;
|
||||
}
|
||||
|
||||
async $$checkAndAskUseRemoteConfiguration(
|
||||
async _checkAndAskUseRemoteConfiguration(
|
||||
trialSetting: RemoteDBSettings
|
||||
): Promise<{ result: false | TweakValues; requireFetch: boolean }> {
|
||||
const preferred = await this.core.$$fetchRemotePreferredTweakValues(trialSetting);
|
||||
const preferred = await this.services.tweakValue.fetchRemotePreferred(trialSetting);
|
||||
if (preferred) {
|
||||
return await this.$$askUseRemoteConfiguration(trialSetting, preferred);
|
||||
return await this.services.tweakValue.askUseRemoteConfiguration(trialSetting, preferred);
|
||||
}
|
||||
return { result: false, requireFetch: false };
|
||||
}
|
||||
|
||||
async $$askUseRemoteConfiguration(
|
||||
async _askUseRemoteConfiguration(
|
||||
trialSetting: RemoteDBSettings,
|
||||
preferred: TweakValues
|
||||
): Promise<{ result: false | TweakValues; requireFetch: boolean }> {
|
||||
@@ -278,4 +283,13 @@ export class ModuleResolvingMismatchedTweaks extends AbstractModule implements I
|
||||
}
|
||||
return { result: false, requireFetch: false };
|
||||
}
|
||||
|
||||
onBindFunction(core: LiveSyncCore, services: InjectableServiceHub): void {
|
||||
services.tweakValue.handleFetchRemotePreferred(this._fetchRemotePreferredTweakValues.bind(this));
|
||||
services.tweakValue.handleCheckAndAskResolvingMismatched(this._checkAndAskResolvingMismatchedTweaks.bind(this));
|
||||
services.tweakValue.handleAskResolvingMismatched(this._askResolvingMismatchedTweaks.bind(this));
|
||||
services.tweakValue.handleCheckAndAskUseRemoteConfiguration(this._checkAndAskUseRemoteConfiguration.bind(this));
|
||||
services.tweakValue.handleAskUseRemoteConfiguration(this._askUseRemoteConfiguration.bind(this));
|
||||
services.replication.handleCheckConnectionFailure(this._anyAfterConnectCheckFailed.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { TFile, TFolder, type ListedFiles } from "obsidian";
|
||||
import { SerializedFileAccess } from "./storageLib/SerializedFileAccess";
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { LOG_LEVEL_INFO, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
||||
import type {
|
||||
FilePath,
|
||||
@@ -16,10 +16,13 @@ import { StorageEventManagerObsidian, type StorageEventManager } from "./storage
|
||||
import type { StorageAccess } from "../interfaces/StorageAccess";
|
||||
import { createBlob, type CustomRegExp } from "../../lib/src/common/utils";
|
||||
import { serialized } from "octagonal-wheels/concurrency/lock_v2";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
import type ObsidianLiveSyncPlugin from "../../main.ts";
|
||||
import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts";
|
||||
|
||||
const fileLockPrefix = "file-lock:";
|
||||
|
||||
export class ModuleFileAccessObsidian extends AbstractObsidianModule implements IObsidianModule, StorageAccess {
|
||||
export class ModuleFileAccessObsidian extends AbstractObsidianModule implements StorageAccess {
|
||||
processingFiles: Set<FilePathWithPrefix> = new Set();
|
||||
processWriteFile<T>(file: UXFileInfoStub | FilePathWithPrefix, proc: () => Promise<T>): Promise<T> {
|
||||
const path = typeof file === "string" ? file : file.path;
|
||||
@@ -49,39 +52,35 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements
|
||||
}
|
||||
vaultAccess!: SerializedFileAccess;
|
||||
vaultManager: StorageEventManager = new StorageEventManagerObsidian(this.plugin, this.core, this);
|
||||
$everyOnload(): Promise<boolean> {
|
||||
private _everyOnload(): Promise<boolean> {
|
||||
this.core.storageAccess = this;
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
$everyOnFirstInitialize(): Promise<boolean> {
|
||||
_everyOnFirstInitialize(): Promise<boolean> {
|
||||
this.vaultManager.beginWatch();
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
$allOnUnload(): Promise<boolean> {
|
||||
// this.vaultManager.
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
// $$flushFileEventQueue(): void {
|
||||
// this.vaultManager.flushQueue();
|
||||
// }
|
||||
|
||||
$everyCommitPendingFileEvent(): Promise<boolean> {
|
||||
_everyCommitPendingFileEvent(): Promise<boolean> {
|
||||
this.vaultManager.flushQueue();
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
_everyOnloadStart(): Promise<boolean> {
|
||||
this.vaultAccess = new SerializedFileAccess(this.app, this.plugin, this);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
$$isStorageInsensitive(): boolean {
|
||||
_isStorageInsensitive(): boolean {
|
||||
return this.vaultAccess.isStorageInsensitive();
|
||||
}
|
||||
|
||||
$$shouldCheckCaseInsensitive(): boolean {
|
||||
if (this.$$isStorageInsensitive()) return false;
|
||||
_shouldCheckCaseInsensitive(): boolean {
|
||||
if (this.services.vault.isStorageInsensitive()) return false;
|
||||
return !this.settings.handleFilenameCaseSensitive;
|
||||
}
|
||||
|
||||
@@ -275,7 +274,7 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements
|
||||
if (excludeFilter && excludeFilter.some((ee) => ee.test(file))) {
|
||||
continue;
|
||||
}
|
||||
if (await this.plugin.$$isIgnoredByIgnoreFiles(file)) continue;
|
||||
if (await this.services.vault.isIgnoredByIgnoreFile(file)) continue;
|
||||
files.push(file);
|
||||
}
|
||||
|
||||
@@ -288,7 +287,7 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements
|
||||
if (excludeFilter && excludeFilter.some((e) => e.test(v))) {
|
||||
continue;
|
||||
}
|
||||
if (await this.plugin.$$isIgnoredByIgnoreFiles(v)) {
|
||||
if (await this.services.vault.isIgnoredByIgnoreFile(v)) {
|
||||
continue;
|
||||
}
|
||||
// OK, deep dive!
|
||||
@@ -346,7 +345,7 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements
|
||||
|
||||
async _deleteVaultItem(file: TFile | TFolder) {
|
||||
if (file instanceof TFile) {
|
||||
if (!(await this.core.$$isTargetFile(file.path))) return;
|
||||
if (!(await this.services.vault.isTargetFile(file.path))) return;
|
||||
}
|
||||
const dir = file.parent;
|
||||
if (this.settings.trashInsteadDelete) {
|
||||
@@ -376,4 +375,16 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements
|
||||
return await this._deleteVaultItem(file);
|
||||
}
|
||||
}
|
||||
|
||||
constructor(plugin: ObsidianLiveSyncPlugin, core: LiveSyncCore) {
|
||||
super(plugin, core);
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: InjectableServiceHub): void {
|
||||
services.vault.handleIsStorageInsensitive(this._isStorageInsensitive.bind(this));
|
||||
services.setting.handleShouldCheckCaseInsensitively(this._shouldCheckCaseInsensitive.bind(this));
|
||||
services.appLifecycle.handleFirstInitialise(this._everyOnFirstInitialize.bind(this));
|
||||
services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this));
|
||||
services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this));
|
||||
services.fileProcessing.handleCommitPendingFileEvents(this._everyCommitPendingFileEvent.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// ModuleInputUIObsidian.ts
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { scheduleTask } from "octagonal-wheels/concurrency/task";
|
||||
import { disposeMemoObject, memoIfNotExist, memoObject, retrieveMemoObject } from "../../common/utils.ts";
|
||||
import {
|
||||
@@ -13,12 +13,13 @@ import { Notice } from "../../deps.ts";
|
||||
import type { Confirm } from "../../lib/src/interfaces/Confirm.ts";
|
||||
import { setConfirmInstance } from "../../lib/src/PlatformAPIs/obsidian/Confirm.ts";
|
||||
import { $msg } from "src/lib/src/common/i18n.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
// This module cannot be a common module because it depends on Obsidian's API.
|
||||
// However, we have to make compatible one for other platform.
|
||||
|
||||
export class ModuleInputUIObsidian extends AbstractObsidianModule implements IObsidianModule, Confirm {
|
||||
$everyOnload(): Promise<boolean> {
|
||||
export class ModuleInputUIObsidian extends AbstractObsidianModule implements Confirm {
|
||||
private _everyOnload(): Promise<boolean> {
|
||||
this.core.confirm = this;
|
||||
setConfirmInstance(this);
|
||||
return Promise.resolve(true);
|
||||
@@ -110,4 +111,8 @@ export class ModuleInputUIObsidian extends AbstractObsidianModule implements IOb
|
||||
): Promise<(typeof buttons)[number] | false> {
|
||||
return confirmWithMessage(this.plugin, title, contentMd, buttons, defaultAction, timeout);
|
||||
}
|
||||
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,14 @@ import {
|
||||
LOG_LEVEL_INFO,
|
||||
LOG_LEVEL_NOTICE,
|
||||
LOG_LEVEL_VERBOSE,
|
||||
type FileEventType,
|
||||
type FilePath,
|
||||
type FilePathWithPrefix,
|
||||
type UXFileInfoStub,
|
||||
type UXInternalFileInfoStub,
|
||||
} from "../../../lib/src/common/types.ts";
|
||||
import { delay, fireAndForget, getFileRegExp } from "../../../lib/src/common/utils.ts";
|
||||
import { type FileEventItem, type FileEventType } from "../../../common/types.ts";
|
||||
import { type FileEventItem } from "../../../common/types.ts";
|
||||
import { serialized, skipIfDuplicated } from "octagonal-wheels/concurrency/lock";
|
||||
import {
|
||||
finishAllWaitingForTimeout,
|
||||
@@ -48,6 +49,9 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
||||
plugin: ObsidianLiveSyncPlugin;
|
||||
core: LiveSyncCore;
|
||||
storageAccess: StorageAccess;
|
||||
get services() {
|
||||
return this.core.services;
|
||||
}
|
||||
|
||||
get shouldBatchSave() {
|
||||
return this.core.settings?.batchSave && this.core.settings?.liveSync != true;
|
||||
@@ -175,7 +179,7 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
||||
if (this.plugin.settings.useIgnoreFiles) {
|
||||
// If it is one of ignore files, refresh the cached one.
|
||||
// (Calling$$isTargetFile will refresh the cache)
|
||||
void this.plugin.$$isTargetFile(path).then(() => this._watchVaultRawEvents(path));
|
||||
void this.services.vault.isTargetFile(path).then(() => this._watchVaultRawEvents(path));
|
||||
} else {
|
||||
this._watchVaultRawEvents(path);
|
||||
}
|
||||
@@ -209,7 +213,7 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
||||
async appendQueue(params: FileEvent[], ctx?: any) {
|
||||
if (!this.core.settings.isConfigured) return;
|
||||
if (this.core.settings.suspendFileWatching) return;
|
||||
this.core.$$markFileListPossiblyChanged();
|
||||
this.core.services.vault.markFileListPossiblyChanged();
|
||||
// Flag up to be reload
|
||||
const processFiles = new Set<FilePath>();
|
||||
for (const param of params) {
|
||||
@@ -222,7 +226,7 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
||||
const oldPath = param.oldPath;
|
||||
if (type !== "INTERNAL") {
|
||||
const size = (file as UXFileInfoStub).stat.size;
|
||||
if (this.core.$$isFileSizeExceeded(size) && (type == "CREATE" || type == "CHANGED")) {
|
||||
if (this.services.vault.isFileSizeTooLarge(size) && (type == "CREATE" || type == "CHANGED")) {
|
||||
Logger(
|
||||
`The storage file has been changed but exceeds the maximum size. Skipping: ${param.file.path}`,
|
||||
LOG_LEVEL_NOTICE
|
||||
@@ -234,7 +238,7 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
||||
// TODO: Confirm why only the TFolder skipping
|
||||
// Possibly following line is needed...
|
||||
// if (file?.isFolder) continue;
|
||||
if (!(await this.core.$$isTargetFile(file.path))) continue;
|
||||
if (!(await this.services.vault.isTargetFile(file.path))) continue;
|
||||
|
||||
// Stop cache using to prevent the corruption;
|
||||
// let cache: null | string | ArrayBuffer;
|
||||
@@ -411,12 +415,12 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
||||
const lockKey = `handleFile:${file.path}`;
|
||||
return await serialized(lockKey, async () => {
|
||||
if (queue.type == "INTERNAL" || file.isInternal) {
|
||||
await this.core.$anyProcessOptionalFileEvent(file.path as unknown as FilePath);
|
||||
await this.core.services.fileProcessing.processOptionalFileEvent(file.path as unknown as FilePath);
|
||||
} else {
|
||||
const key = `file-last-proc-${queue.type}-${file.path}`;
|
||||
const last = Number((await this.core.kvDB.get(key)) || 0);
|
||||
if (queue.type == "DELETE") {
|
||||
await this.core.$anyHandlerProcessesFileEvent(queue);
|
||||
await this.core.services.fileProcessing.processFileEvent(queue);
|
||||
} else {
|
||||
if (file.stat.mtime == last) {
|
||||
Logger(`File has been already scanned on ${queue.type}, skip: ${file.path}`, LOG_LEVEL_VERBOSE);
|
||||
@@ -424,7 +428,7 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
||||
// this.cancelRelativeEvent(queue);
|
||||
return;
|
||||
}
|
||||
if (!(await this.core.$anyHandlerProcessesFileEvent(queue))) {
|
||||
if (!(await this.core.services.fileProcessing.processFileEvent(queue))) {
|
||||
Logger(
|
||||
`STORAGE -> DB: Handler failed, cancel the relative operations: ${file.path}`,
|
||||
LOG_LEVEL_INFO
|
||||
|
||||
@@ -17,10 +17,11 @@ import {
|
||||
import { isAnyNote } from "../../lib/src/common/utils.ts";
|
||||
import { stripAllPrefixes } from "../../lib/src/string_and_binary/path.ts";
|
||||
import { AbstractModule } from "../AbstractModule.ts";
|
||||
import type { ICoreModule } from "../ModuleTypes.ts";
|
||||
import { withConcurrency } from "octagonal-wheels/iterable/map";
|
||||
export class ModuleInitializerFile extends AbstractModule implements ICoreModule {
|
||||
async $$performFullScan(showingNotice?: boolean, ignoreSuspending: boolean = false): Promise<void> {
|
||||
import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
export class ModuleInitializerFile extends AbstractModule {
|
||||
private async _performFullScan(showingNotice?: boolean, ignoreSuspending: boolean = false): Promise<boolean> {
|
||||
this._log("Opening the key-value database", LOG_LEVEL_VERBOSE);
|
||||
const isInitialized = (await this.core.kvDB.get<boolean>("initialized")) || false;
|
||||
// synchronize all files between database and storage.
|
||||
@@ -32,7 +33,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
||||
"syncAll"
|
||||
);
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (!ignoreSuspending && this.settings.suspendFileWatching) {
|
||||
if (showingNotice) {
|
||||
@@ -42,7 +43,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
||||
"syncAll"
|
||||
);
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (showingNotice) {
|
||||
@@ -59,7 +60,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
||||
const _filesStorage = [] as typeof filesStorageSrc;
|
||||
|
||||
for (const f of filesStorageSrc) {
|
||||
if (await this.core.$$isTargetFile(f.path, f != filesStorageSrc[0])) {
|
||||
if (await this.services.vault.isTargetFile(f.path, f != filesStorageSrc[0])) {
|
||||
_filesStorage.push(f);
|
||||
}
|
||||
}
|
||||
@@ -103,7 +104,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
||||
);
|
||||
const path = getPath(doc);
|
||||
|
||||
if (isValidPath(path) && (await this.core.$$isTargetFile(path, true))) {
|
||||
if (isValidPath(path) && (await this.services.vault.isTargetFile(path, true))) {
|
||||
if (!isMetaEntry(doc)) {
|
||||
this._log(`Invalid entry: ${path}`, LOG_LEVEL_INFO);
|
||||
continue;
|
||||
@@ -133,7 +134,6 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
||||
this._log(`Total files in the database: ${databaseFileNames.length}`, LOG_LEVEL_VERBOSE, "syncAll");
|
||||
this._log(`Total files in the storage: ${storageFileNames.length}`, LOG_LEVEL_VERBOSE, "syncAll");
|
||||
this._log(`Total files: ${allFiles.length}`, LOG_LEVEL_VERBOSE, "syncAll");
|
||||
|
||||
const filesExistOnlyInStorage = allFiles.filter((e) => !databaseFileNameCI2CS[e]);
|
||||
const filesExistOnlyInDatabase = allFiles.filter((e) => !storageFileNameCI2CS[e]);
|
||||
const filesExistBoth = allFiles.filter((e) => databaseFileNameCI2CS[e] && storageFileNameCI2CS[e]);
|
||||
@@ -192,7 +192,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
||||
runAll("UPDATE DATABASE", filesExistOnlyInStorage, async (e) => {
|
||||
// Exists in storage but not in database.
|
||||
const file = storageFileNameMap[storageFileNameCI2CS[e]];
|
||||
if (!this.core.$$isFileSizeExceeded(file.stat.size)) {
|
||||
if (!this.services.vault.isFileSizeTooLarge(file.stat.size)) {
|
||||
const path = file.path;
|
||||
await this.core.fileHandler.storeFileToDB(file);
|
||||
// fireAndForget(() => this.checkAndApplySettingFromMarkdown(path, true));
|
||||
@@ -208,7 +208,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
||||
// Exists in database but not in storage.
|
||||
const path = getPath(w) ?? e;
|
||||
if (w && !(w.deleted || w._deleted)) {
|
||||
if (!this.core.$$isFileSizeExceeded(w.size)) {
|
||||
if (!this.services.vault.isFileSizeTooLarge(w.size)) {
|
||||
// Prevent applying the conflicted state to the storage.
|
||||
if (w._conflicts?.length ?? 0 > 0) {
|
||||
this._log(`UPDATE STORAGE: ${path} has conflicts. skipped (x)`, LOG_LEVEL_INFO);
|
||||
@@ -250,7 +250,10 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
||||
this._log(`SYNC DATABASE AND STORAGE: ${file.path} has conflicts. skipped`, LOG_LEVEL_INFO);
|
||||
return;
|
||||
}
|
||||
if (!this.core.$$isFileSizeExceeded(file.stat.size) && !this.core.$$isFileSizeExceeded(doc.size)) {
|
||||
if (
|
||||
!this.services.vault.isFileSizeTooLarge(file.stat.size) &&
|
||||
!this.services.vault.isFileSizeTooLarge(doc.size)
|
||||
) {
|
||||
await this.syncFileBetweenDBandStorage(file, doc);
|
||||
} else {
|
||||
this._log(
|
||||
@@ -271,6 +274,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
||||
if (showingNotice) {
|
||||
this._log("Initialize done!", LOG_LEVEL_NOTICE, "syncAll");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async syncFileBetweenDBandStorage(file: UXFileInfoStub, doc: MetaEntry) {
|
||||
@@ -289,7 +293,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
||||
const compareResult = compareFileFreshness(file, doc);
|
||||
switch (compareResult) {
|
||||
case BASE_IS_NEW:
|
||||
if (!this.core.$$isFileSizeExceeded(file.stat.size)) {
|
||||
if (!this.services.vault.isFileSizeTooLarge(file.stat.size)) {
|
||||
this._log("STORAGE -> DB :" + file.path);
|
||||
await this.core.fileHandler.storeFileToDB(file);
|
||||
} else {
|
||||
@@ -300,7 +304,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
||||
}
|
||||
break;
|
||||
case TARGET_IS_NEW:
|
||||
if (!this.core.$$isFileSizeExceeded(doc.size)) {
|
||||
if (!this.services.vault.isFileSizeTooLarge(doc.size)) {
|
||||
this._log("STORAGE <- DB :" + file.path);
|
||||
if (await this.core.fileHandler.dbToStorage(doc, stripAllPrefixes(file.path), true)) {
|
||||
eventHub.emitEvent("event-file-changed", {
|
||||
@@ -365,27 +369,31 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
||||
this._log(`Checking expired file history done`);
|
||||
}
|
||||
|
||||
async $$initializeDatabase(
|
||||
private async _initializeDatabase(
|
||||
showingNotice: boolean = false,
|
||||
reopenDatabase = true,
|
||||
ignoreSuspending: boolean = false
|
||||
): Promise<boolean> {
|
||||
this.core.$$resetIsReady();
|
||||
if (!reopenDatabase || (await this.core.$$openDatabase())) {
|
||||
this.services.appLifecycle.resetIsReady();
|
||||
if (!reopenDatabase || (await this.services.database.openDatabase())) {
|
||||
if (this.localDatabase.isReady) {
|
||||
await this.core.$$performFullScan(showingNotice, ignoreSuspending);
|
||||
await this.services.vault.scanVault(showingNotice, ignoreSuspending);
|
||||
}
|
||||
if (!(await this.core.$everyOnDatabaseInitialized(showingNotice))) {
|
||||
this._log(`Initializing database has been failed on some module`, LOG_LEVEL_NOTICE);
|
||||
if (!(await this.services.databaseEvents.onDatabaseInitialised(showingNotice))) {
|
||||
this._log(`Initializing database has been failed on some module!`, LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
this.core.$$markIsReady();
|
||||
this.services.appLifecycle.markIsReady();
|
||||
// run queued event once.
|
||||
await this.core.$everyCommitPendingFileEvent();
|
||||
await this.services.fileProcessing.commitPendingFileEvents();
|
||||
return true;
|
||||
} else {
|
||||
this.core.$$resetIsReady();
|
||||
this.services.appLifecycle.resetIsReady();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: InjectableServiceHub): void {
|
||||
services.databaseEvents.handleInitialiseDatabase(this._initializeDatabase.bind(this));
|
||||
services.vault.handleScanVault(this._performFullScan.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ import { OpenKeyValueDatabase } from "../../common/KeyValueDB.ts";
|
||||
import type { LiveSyncLocalDB } from "../../lib/src/pouchdb/LiveSyncLocalDB.ts";
|
||||
import { LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
||||
import { AbstractModule } from "../AbstractModule.ts";
|
||||
import type { ICoreModule } from "../ModuleTypes.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
export class ModuleKeyValueDB extends AbstractModule implements ICoreModule {
|
||||
export class ModuleKeyValueDB extends AbstractModule {
|
||||
tryCloseKvDB() {
|
||||
try {
|
||||
this.core.kvDB?.close();
|
||||
@@ -22,7 +22,7 @@ export class ModuleKeyValueDB extends AbstractModule implements ICoreModule {
|
||||
this.tryCloseKvDB();
|
||||
await delay(10);
|
||||
await yieldMicrotask();
|
||||
this.core.kvDB = await OpenKeyValueDatabase(this.core.$$getVaultName() + "-livesync-kv");
|
||||
this.core.kvDB = await OpenKeyValueDatabase(this.services.vault.getVaultName() + "-livesync-kv");
|
||||
await yieldMicrotask();
|
||||
await delay(100);
|
||||
} catch (e) {
|
||||
@@ -33,21 +33,23 @@ export class ModuleKeyValueDB extends AbstractModule implements ICoreModule {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
$allOnDBUnload(db: LiveSyncLocalDB): void {
|
||||
_onDBUnload(db: LiveSyncLocalDB) {
|
||||
if (this.core.kvDB) this.core.kvDB.close();
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
$allOnDBClose(db: LiveSyncLocalDB): void {
|
||||
_onDBClose(db: LiveSyncLocalDB) {
|
||||
if (this.core.kvDB) this.core.kvDB.close();
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
async $everyOnloadAfterLoadSettings(): Promise<boolean> {
|
||||
private async _everyOnloadAfterLoadSettings(): Promise<boolean> {
|
||||
if (!(await this.openKeyValueDB())) {
|
||||
return false;
|
||||
}
|
||||
this.core.simpleStore = this.core.$$getSimpleStore<any>("os");
|
||||
this.core.simpleStore = this.services.database.openSimpleStore<any>("os");
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
$$getSimpleStore<T>(kind: string) {
|
||||
_getSimpleStore<T>(kind: string) {
|
||||
const prefix = `${kind}-`;
|
||||
return {
|
||||
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();
|
||||
}
|
||||
|
||||
async $everyOnResetDatabase(db: LiveSyncLocalDB): Promise<boolean> {
|
||||
async _everyOnResetDatabase(db: LiveSyncLocalDB): Promise<boolean> {
|
||||
try {
|
||||
const kvDBKey = "queued-files";
|
||||
await this.core.kvDB.del(kvDBKey);
|
||||
// localStorage.removeItem(lsKey);
|
||||
await this.core.kvDB.destroy();
|
||||
await yieldMicrotask();
|
||||
this.core.kvDB = await OpenKeyValueDatabase(this.core.$$getVaultName() + "-livesync-kv");
|
||||
this.core.kvDB = await OpenKeyValueDatabase(this.services.vault.getVaultName() + "-livesync-kv");
|
||||
await delay(100);
|
||||
} catch (e) {
|
||||
this.core.kvDB = undefined!;
|
||||
@@ -96,4 +98,12 @@ export class ModuleKeyValueDB extends AbstractModule implements ICoreModule {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.databaseEvents.handleOnUnloadDatabase(this._onDBUnload.bind(this));
|
||||
services.databaseEvents.handleOnCloseDatabase(this._onDBClose.bind(this));
|
||||
services.databaseEvents.handleOnDatabaseInitialisation(this._everyOnInitializeDatabase.bind(this));
|
||||
services.databaseEvents.handleOnResetDatabase(this._everyOnResetDatabase.bind(this));
|
||||
services.database.handleOpenSimpleStore(this._getSimpleStore.bind(this));
|
||||
services.appLifecycle.handleOnSettingLoaded(this._everyOnloadAfterLoadSettings.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@ import {
|
||||
eventHub,
|
||||
} from "../../common/events.ts";
|
||||
import { AbstractModule } from "../AbstractModule.ts";
|
||||
import type { ICoreModule } from "../ModuleTypes.ts";
|
||||
import { $msg } from "src/lib/src/common/i18n.ts";
|
||||
import { performDoctorConsultation, RebuildOptions } from "../../lib/src/common/configForDoc.ts";
|
||||
import { getPath, isValidPath } from "../../common/utils.ts";
|
||||
import { isMetaEntry } from "../../lib/src/common/types.ts";
|
||||
import { isDeletedEntry, isDocContentSame, isLoadedEntry, readAsBlob } from "../../lib/src/common/utils.ts";
|
||||
import { countCompromisedChunks } from "../../lib/src/pouchdb/negotiation.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
type ErrorInfo = {
|
||||
path: string;
|
||||
@@ -26,7 +26,7 @@ type ErrorInfo = {
|
||||
isConflicted?: boolean;
|
||||
};
|
||||
|
||||
export class ModuleMigration extends AbstractModule implements ICoreModule {
|
||||
export class ModuleMigration extends AbstractModule {
|
||||
async migrateUsingDoctor(skipRebuild: boolean = false, activateReason = "updated", forceRescan = false) {
|
||||
const { shouldRebuild, shouldRebuildLocal, isModified, settings } = await performDoctorConsultation(
|
||||
this.core,
|
||||
@@ -45,11 +45,11 @@ export class ModuleMigration extends AbstractModule implements ICoreModule {
|
||||
if (!skipRebuild) {
|
||||
if (shouldRebuild) {
|
||||
await this.core.rebuilder.scheduleRebuild();
|
||||
await this.core.$$performRestart();
|
||||
this.services.appLifecycle.performRestart();
|
||||
return false;
|
||||
} else if (shouldRebuildLocal) {
|
||||
await this.core.rebuilder.scheduleFetch();
|
||||
await this.core.$$performRestart();
|
||||
this.services.appLifecycle.performRestart();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -129,7 +129,7 @@ export class ModuleMigration extends AbstractModule implements ICoreModule {
|
||||
if (!isValidPath(path)) {
|
||||
continue;
|
||||
}
|
||||
if (!(await this.core.$$isTargetFile(path, true))) {
|
||||
if (!(await this.services.vault.isTargetFile(path, true))) {
|
||||
continue;
|
||||
}
|
||||
if (!isMetaEntry(metaDoc)) {
|
||||
@@ -257,9 +257,9 @@ export class ModuleMigration extends AbstractModule implements ICoreModule {
|
||||
}
|
||||
// Check local database for compromised chunks
|
||||
const localCompromised = await countCompromisedChunks(this.localDatabase.localDatabase);
|
||||
const remote = this.core.$$getReplicator();
|
||||
const remote = this.services.replicator.getActiveReplicator();
|
||||
const remoteCompromised = this.core.managers.networkManager.isOnline
|
||||
? await remote.countCompromisedChunks()
|
||||
? await remote?.countCompromisedChunks()
|
||||
: 0;
|
||||
if (localCompromised === false) {
|
||||
Logger(`Failed to count compromised chunks in local database`, LOG_LEVEL_NOTICE);
|
||||
@@ -293,12 +293,12 @@ export class ModuleMigration extends AbstractModule implements ICoreModule {
|
||||
if (result === REBUILD) {
|
||||
// Rebuild the database
|
||||
await this.core.rebuilder.scheduleRebuild();
|
||||
await this.core.$$performRestart();
|
||||
this.services.appLifecycle.performRestart();
|
||||
return false;
|
||||
} else if (result === FETCH) {
|
||||
// Fetch the latest data from remote
|
||||
await this.core.rebuilder.scheduleFetch();
|
||||
await this.core.$$performRestart();
|
||||
this.services.appLifecycle.performRestart();
|
||||
return false;
|
||||
} else {
|
||||
// User chose to dismiss the issue
|
||||
@@ -307,7 +307,7 @@ export class ModuleMigration extends AbstractModule implements ICoreModule {
|
||||
return true;
|
||||
}
|
||||
|
||||
async $everyOnFirstInitialize(): Promise<boolean> {
|
||||
async _everyOnFirstInitialize(): Promise<boolean> {
|
||||
if (!this.localDatabase.isReady) {
|
||||
this._log($msg("moduleMigration.logLocalDatabaseNotReady"), LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
@@ -337,7 +337,7 @@ export class ModuleMigration extends AbstractModule implements ICoreModule {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
$everyOnLayoutReady(): Promise<boolean> {
|
||||
_everyOnLayoutReady(): Promise<boolean> {
|
||||
eventHub.onEvent(EVENT_REQUEST_RUN_DOCTOR, async (reason) => {
|
||||
await this.migrateUsingDoctor(false, reason, true);
|
||||
});
|
||||
@@ -346,4 +346,9 @@ export class ModuleMigration extends AbstractModule implements ICoreModule {
|
||||
});
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
super.onBindFunction(core, services);
|
||||
services.appLifecycle.handleLayoutReady(this._everyOnLayoutReady.bind(this));
|
||||
services.appLifecycle.handleFirstInitialise(this._everyOnFirstInitialize.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { LOG_LEVEL_DEBUG, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
||||
import { Notice, requestUrl, type RequestUrlParam, type RequestUrlResponse } from "../../deps.ts";
|
||||
import { type CouchDBCredentials, type EntryDoc, type FilePathWithPrefix } from "../../lib/src/common/types.ts";
|
||||
import { type CouchDBCredentials, type EntryDoc, type FilePath } from "../../lib/src/common/types.ts";
|
||||
import { getPathFromTFile } from "../../common/utils.ts";
|
||||
import { isCloudantURI, isValidRemoteCouchDBURI } from "../../lib/src/pouchdb/utils_couchdb.ts";
|
||||
import { replicationFilter } from "@/lib/src/pouchdb/compress.ts";
|
||||
@@ -11,6 +11,7 @@ import { setNoticeClass } from "../../lib/src/mock_and_interop/wrapper.ts";
|
||||
import { ObsHttpHandler } from "./APILib/ObsHttpHandler.ts";
|
||||
import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser.ts";
|
||||
import { AuthorizationHeaderGenerator } from "../../lib/src/replication/httplib.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
setNoticeClass(Notice);
|
||||
|
||||
@@ -19,17 +20,17 @@ async function fetchByAPI(request: RequestUrlParam, errorAsResult = false): Prom
|
||||
return ret;
|
||||
}
|
||||
|
||||
export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidianModule {
|
||||
export class ModuleObsidianAPI extends AbstractObsidianModule {
|
||||
_customHandler!: ObsHttpHandler;
|
||||
|
||||
_authHeader = new AuthorizationHeaderGenerator();
|
||||
|
||||
last_successful_post = false;
|
||||
$$customFetchHandler(): ObsHttpHandler {
|
||||
_customFetchHandler(): ObsHttpHandler {
|
||||
if (!this._customHandler) this._customHandler = new ObsHttpHandler(undefined, undefined);
|
||||
return this._customHandler;
|
||||
}
|
||||
$$getLastPostFailedBySize(): boolean {
|
||||
_getLastPostFailedBySize(): boolean {
|
||||
return !this.last_successful_post;
|
||||
}
|
||||
|
||||
@@ -90,7 +91,7 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
|
||||
}
|
||||
}
|
||||
|
||||
async $$connectRemoteCouchDB(
|
||||
async _connectRemoteCouchDB(
|
||||
uri: string,
|
||||
auth: CouchDBCredentials,
|
||||
disableRequestURI: boolean,
|
||||
@@ -252,21 +253,21 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
|
||||
}
|
||||
}
|
||||
|
||||
$$isMobile(): boolean {
|
||||
_isMobile(): boolean {
|
||||
//@ts-ignore : internal API
|
||||
return this.app.isMobile;
|
||||
}
|
||||
|
||||
$$vaultName(): string {
|
||||
_vaultName(): string {
|
||||
return this.app.vault.getName();
|
||||
}
|
||||
$$getVaultName(): string {
|
||||
_getVaultName(): string {
|
||||
return (
|
||||
this.core.$$vaultName() +
|
||||
this.services.vault.vaultName() +
|
||||
(this.settings?.additionalSuffixOfDatabaseName ? "-" + this.settings.additionalSuffixOfDatabaseName : "")
|
||||
);
|
||||
}
|
||||
$$getActiveFilePath(): FilePathWithPrefix | undefined {
|
||||
_getActiveFilePath(): FilePath | undefined {
|
||||
const file = this.app.workspace.getActiveFile();
|
||||
if (file) {
|
||||
return getPathFromTFile(file);
|
||||
@@ -274,7 +275,18 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
|
||||
return undefined;
|
||||
}
|
||||
|
||||
$anyGetAppId(): Promise<string | undefined> {
|
||||
return Promise.resolve(`${"appId" in this.app ? this.app.appId : ""}`);
|
||||
_anyGetAppId(): string {
|
||||
return `${"appId" in this.app ? this.app.appId : ""}`;
|
||||
}
|
||||
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services) {
|
||||
services.API.handleGetCustomFetchHandler(this._customFetchHandler.bind(this));
|
||||
services.API.handleIsLastPostFailedDueToPayloadSize(this._getLastPostFailedBySize.bind(this));
|
||||
services.remote.handleConnect(this._connectRemoteCouchDB.bind(this));
|
||||
services.API.handleIsMobile(this._isMobile.bind(this));
|
||||
services.vault.handleGetVaultName(this._getVaultName.bind(this));
|
||||
services.vault.handleVaultName(this._vaultName.bind(this));
|
||||
services.vault.handleGetActiveFilePath(this._getActiveFilePath.bind(this));
|
||||
services.API.handleGetAppID(this._anyGetAppId.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { EVENT_FILE_RENAMED, EVENT_LEAF_ACTIVE_CHANGED, eventHub } from "../../common/events.js";
|
||||
import { LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
||||
import { scheduleTask } from "octagonal-wheels/concurrency/task";
|
||||
@@ -12,9 +12,10 @@ import {
|
||||
hiddenFilesEventCount,
|
||||
hiddenFilesProcessingCount,
|
||||
} from "../../lib/src/mock_and_interop/stores.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
export class ModuleObsidianEvents extends AbstractObsidianModule implements IObsidianModule {
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
export class ModuleObsidianEvents extends AbstractObsidianModule {
|
||||
_everyOnloadStart(): Promise<boolean> {
|
||||
// this.registerEvent(this.app.workspace.on("editor-change", ));
|
||||
this.plugin.registerEvent(
|
||||
this.app.vault.on("rename", (file, oldPath) => {
|
||||
@@ -30,7 +31,7 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
$$performRestart(): void {
|
||||
private _performRestart(): void {
|
||||
this._performAppReload();
|
||||
}
|
||||
|
||||
@@ -49,14 +50,14 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs
|
||||
this.initialCallback = save;
|
||||
saveCommandDefinition.callback = () => {
|
||||
scheduleTask("syncOnEditorSave", 250, () => {
|
||||
if (this.core.$$isUnloaded()) {
|
||||
if (this.services.appLifecycle.hasUnloaded()) {
|
||||
this._log("Unload and remove the handler.", LOG_LEVEL_VERBOSE);
|
||||
saveCommandDefinition.callback = this.initialCallback;
|
||||
this.initialCallback = undefined;
|
||||
} else {
|
||||
if (this.settings.syncOnEditorSave) {
|
||||
this._log("Sync on Editor Save.", LOG_LEVEL_VERBOSE);
|
||||
fireAndForget(() => this.core.$$replicateByEvent());
|
||||
fireAndForget(() => this.services.replication.replicateByEvent());
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -106,14 +107,14 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs
|
||||
// TODO:FIXME AT V0.17.31, this logic has been disabled.
|
||||
if (navigator.onLine && this.localDatabase.needScanning) {
|
||||
this.localDatabase.needScanning = false;
|
||||
await this.core.$$performFullScan();
|
||||
await this.services.vault.scanVault();
|
||||
}
|
||||
}
|
||||
|
||||
async watchWindowVisibilityAsync() {
|
||||
if (this.settings.suspendFileWatching) return;
|
||||
if (!this.settings.isConfigured) return;
|
||||
if (!this.core.$$isReady()) return;
|
||||
if (!this.services.appLifecycle.isReady()) return;
|
||||
|
||||
if (this.isLastHidden && !this.hasFocus) {
|
||||
// NO OP while non-focused after made hidden;
|
||||
@@ -126,22 +127,22 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs
|
||||
}
|
||||
this.isLastHidden = isHidden;
|
||||
|
||||
await this.core.$everyCommitPendingFileEvent();
|
||||
await this.services.fileProcessing.commitPendingFileEvents();
|
||||
|
||||
if (isHidden) {
|
||||
await this.core.$everyBeforeSuspendProcess();
|
||||
await this.services.appLifecycle.onSuspending();
|
||||
} else {
|
||||
// suspend all temporary.
|
||||
if (this.core.$$isSuspended()) return;
|
||||
if (this.services.appLifecycle.isSuspended()) return;
|
||||
if (!this.hasFocus) return;
|
||||
await this.core.$everyOnResumeProcess();
|
||||
await this.core.$everyAfterResumeProcess();
|
||||
await this.services.appLifecycle.onResuming();
|
||||
await this.services.appLifecycle.onResumed();
|
||||
}
|
||||
}
|
||||
watchWorkspaceOpen(file: TFile | null) {
|
||||
if (this.settings.suspendFileWatching) return;
|
||||
if (!this.settings.isConfigured) return;
|
||||
if (!this.core.$$isReady()) return;
|
||||
if (!this.services.appLifecycle.isReady()) return;
|
||||
if (!file) return;
|
||||
scheduleTask("watch-workspace-open", 500, () => fireAndForget(() => this.watchWorkspaceOpenAsync(file)));
|
||||
}
|
||||
@@ -149,25 +150,25 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs
|
||||
async watchWorkspaceOpenAsync(file: TFile) {
|
||||
if (this.settings.suspendFileWatching) return;
|
||||
if (!this.settings.isConfigured) return;
|
||||
if (!this.core.$$isReady()) return;
|
||||
await this.core.$everyCommitPendingFileEvent();
|
||||
if (!this.services.appLifecycle.isReady()) return;
|
||||
await this.services.fileProcessing.commitPendingFileEvents();
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
if (this.settings.syncOnFileOpen && !this.core.$$isSuspended()) {
|
||||
await this.core.$$replicateByEvent();
|
||||
if (this.settings.syncOnFileOpen && !this.services.appLifecycle.isSuspended()) {
|
||||
await this.services.replication.replicateByEvent();
|
||||
}
|
||||
await this.core.$$queueConflictCheckIfOpen(file.path as FilePathWithPrefix);
|
||||
await this.services.conflict.queueCheckForIfOpen(file.path as FilePathWithPrefix);
|
||||
}
|
||||
|
||||
$everyOnLayoutReady(): Promise<boolean> {
|
||||
_everyOnLayoutReady(): Promise<boolean> {
|
||||
this.swapSaveCommand();
|
||||
this.registerWatchEvents();
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
$$askReload(message?: string) {
|
||||
if (this.core.$$isReloadingScheduled()) {
|
||||
private _askReload(message?: string) {
|
||||
if (this.services.appLifecycle.isReloadingScheduled()) {
|
||||
this._log(`Reloading is already scheduled`, LOG_LEVEL_VERBOSE);
|
||||
return;
|
||||
}
|
||||
@@ -183,11 +184,11 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs
|
||||
if (ret == RESTART_NOW) {
|
||||
this._performAppReload();
|
||||
} else if (ret == RESTART_AFTER_STABLE) {
|
||||
this.core.$$scheduleAppReload();
|
||||
this.services.appLifecycle.scheduleRestart();
|
||||
}
|
||||
});
|
||||
}
|
||||
$$scheduleAppReload() {
|
||||
private _scheduleAppReload() {
|
||||
if (!this.core._totalProcessingCount) {
|
||||
const __tick = reactiveSource(0);
|
||||
this.core._totalProcessingCount = reactive(() => {
|
||||
@@ -237,4 +238,11 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs
|
||||
});
|
||||
}
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.appLifecycle.handleLayoutReady(this._everyOnLayoutReady.bind(this));
|
||||
services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this));
|
||||
services.appLifecycle.handlePerformRestart(this._performRestart.bind(this));
|
||||
services.appLifecycle.handleAskRestart(this._askReload.bind(this));
|
||||
services.appLifecycle.handleScheduleRestart(this._scheduleAppReload.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { fireAndForget } from "octagonal-wheels/promises";
|
||||
import { addIcon, type Editor, type MarkdownFileInfo, type MarkdownView } from "../../deps.ts";
|
||||
import { LOG_LEVEL_NOTICE, type FilePathWithPrefix } from "../../lib/src/common/types.ts";
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { $msg } from "src/lib/src/common/i18n.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsidianModule {
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
export class ModuleObsidianMenu extends AbstractObsidianModule {
|
||||
_everyOnloadStart(): Promise<boolean> {
|
||||
// UI
|
||||
addIcon(
|
||||
"replicate",
|
||||
@@ -18,21 +19,21 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid
|
||||
);
|
||||
|
||||
this.addRibbonIcon("replicate", $msg("moduleObsidianMenu.replicate"), async () => {
|
||||
await this.core.$$replicate(true);
|
||||
await this.services.replication.replicate(true);
|
||||
}).addClass("livesync-ribbon-replicate");
|
||||
|
||||
this.addCommand({
|
||||
id: "livesync-replicate",
|
||||
name: "Replicate now",
|
||||
callback: async () => {
|
||||
await this.core.$$replicate();
|
||||
await this.services.replication.replicate();
|
||||
},
|
||||
});
|
||||
this.addCommand({
|
||||
id: "livesync-dump",
|
||||
name: "Dump information of this doc ",
|
||||
callback: () => {
|
||||
const file = this.core.$$getActiveFilePath();
|
||||
const file = this.services.vault.getActiveFilePath();
|
||||
if (!file) return;
|
||||
fireAndForget(() => this.localDatabase.getDBEntry(file, {}, true, false));
|
||||
},
|
||||
@@ -43,7 +44,7 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid
|
||||
editorCallback: (editor: Editor, view: MarkdownView | MarkdownFileInfo) => {
|
||||
const file = view.file;
|
||||
if (!file) return;
|
||||
void this.core.$$queueConflictCheckIfOpen(file.path as FilePathWithPrefix);
|
||||
void this.services.conflict.queueCheckForIfOpen(file.path as FilePathWithPrefix);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -58,23 +59,23 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid
|
||||
this.settings.liveSync = true;
|
||||
this._log("LiveSync Enabled.", LOG_LEVEL_NOTICE);
|
||||
}
|
||||
await this.core.$$realizeSettingSyncMode();
|
||||
await this.core.$$saveSettingData();
|
||||
await this.services.setting.onRealiseSetting();
|
||||
await this.services.setting.saveSettingData();
|
||||
},
|
||||
});
|
||||
this.addCommand({
|
||||
id: "livesync-suspendall",
|
||||
name: "Toggle All Sync.",
|
||||
callback: async () => {
|
||||
if (this.core.$$isSuspended()) {
|
||||
this.core.$$setSuspended(false);
|
||||
if (this.services.appLifecycle.isSuspended()) {
|
||||
this.services.appLifecycle.setSuspended(false);
|
||||
this._log("Self-hosted LiveSync resumed", LOG_LEVEL_NOTICE);
|
||||
} else {
|
||||
this.core.$$setSuspended(true);
|
||||
this.services.appLifecycle.setSuspended(true);
|
||||
this._log("Self-hosted LiveSync suspended", LOG_LEVEL_NOTICE);
|
||||
}
|
||||
await this.core.$$realizeSettingSyncMode();
|
||||
await this.core.$$saveSettingData();
|
||||
await this.services.setting.onRealiseSetting();
|
||||
await this.services.setting.saveSettingData();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -82,7 +83,7 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid
|
||||
id: "livesync-scan-files",
|
||||
name: "Scan storage and database again",
|
||||
callback: async () => {
|
||||
await this.core.$$performFullScan(true);
|
||||
await this.services.vault.scanVault(true);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -90,7 +91,7 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid
|
||||
id: "livesync-runbatch",
|
||||
name: "Run pended batch processes",
|
||||
callback: async () => {
|
||||
await this.core.$everyCommitPendingFileEvent();
|
||||
await this.services.fileProcessing.commitPendingFileEvents();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -104,12 +105,15 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid
|
||||
});
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
$everyOnload(): Promise<boolean> {
|
||||
this.app.workspace.onLayoutReady(this.core.$$onLiveSyncReady.bind(this.core));
|
||||
_onWorkspaceReady() {
|
||||
void this.services.appLifecycle.onReady();
|
||||
}
|
||||
private _everyOnload(): Promise<boolean> {
|
||||
this.app.workspace.onLayoutReady(this._onWorkspaceReady.bind(this));
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
async $$showView(viewType: string) {
|
||||
private async _showView(viewType: string) {
|
||||
const leaves = this.app.workspace.getLeavesOfType(viewType);
|
||||
if (leaves.length == 0) {
|
||||
await this.app.workspace.getLeaf(true).setViewState({
|
||||
@@ -126,4 +130,9 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid
|
||||
await this.app.workspace.revealLeaf(leaves[0]);
|
||||
}
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this));
|
||||
services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this));
|
||||
services.API.handleShowWindow(this._showView.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
|
||||
export class ModuleExtraSyncObsidian extends AbstractObsidianModule implements IObsidianModule {
|
||||
export class ModuleExtraSyncObsidian extends AbstractObsidianModule {
|
||||
deviceAndVaultName: string = "";
|
||||
|
||||
$$getDeviceAndVaultName(): string {
|
||||
_getDeviceAndVaultName(): string {
|
||||
return this.deviceAndVaultName;
|
||||
}
|
||||
$$setDeviceAndVaultName(name: string): void {
|
||||
_setDeviceAndVaultName(name: string): void {
|
||||
this.deviceAndVaultName = name;
|
||||
}
|
||||
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.setting.handleGetDeviceAndVaultName(this._getDeviceAndVaultName.bind(this));
|
||||
services.setting.handleSetDeviceAndVaultName(this._setDeviceAndVaultName.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { delay, fireAndForget } from "octagonal-wheels/promises";
|
||||
import { __onMissingTranslation } from "../../lib/src/common/i18n";
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
||||
import { eventHub } from "../../common/events";
|
||||
import { enableTestFunction } from "./devUtil/testUtils.ts";
|
||||
import { TestPaneView, VIEW_TYPE_TEST } from "./devUtil/TestPaneView.ts";
|
||||
import { writable } from "svelte/store";
|
||||
import type { FilePathWithPrefix } from "../../lib/src/common/types.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
export class ModuleDev extends AbstractObsidianModule implements IObsidianModule {
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
export class ModuleDev extends AbstractObsidianModule {
|
||||
_everyOnloadStart(): Promise<boolean> {
|
||||
__onMissingTranslation(() => {});
|
||||
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);
|
||||
this.onMissingTranslation = this.onMissingTranslation.bind(this);
|
||||
__onMissingTranslation((key) => {
|
||||
@@ -92,12 +93,12 @@ export class ModuleDev extends AbstractObsidianModule implements IObsidianModule
|
||||
id: "view-test",
|
||||
name: "Open Test dialogue",
|
||||
callback: () => {
|
||||
void this.core.$$showView(VIEW_TYPE_TEST);
|
||||
void this.services.API.showWindow(VIEW_TYPE_TEST);
|
||||
},
|
||||
});
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
async $everyOnLayoutReady(): Promise<boolean> {
|
||||
async _everyOnLayoutReady(): Promise<boolean> {
|
||||
if (!this.settings.enableDebugTools) return Promise.resolve(true);
|
||||
// if (await this.core.storageAccess.isExistsIncludeHidden("_SHOWDIALOGAUTO.md")) {
|
||||
// void this.core.$$showView(VIEW_TYPE_TEST);
|
||||
@@ -121,7 +122,7 @@ export class ModuleDev extends AbstractObsidianModule implements IObsidianModule
|
||||
},
|
||||
});
|
||||
if (w) {
|
||||
const id = await this.core.$$path2id(filename as FilePathWithPrefix);
|
||||
const id = await this.services.path.path2id(filename as FilePathWithPrefix);
|
||||
const f = await this.core.localDatabase.getRaw(id);
|
||||
console.log(f);
|
||||
console.log(f._rev);
|
||||
@@ -139,14 +140,14 @@ export class ModuleDev extends AbstractObsidianModule implements IObsidianModule
|
||||
testResults = writable<[boolean, string, string][]>([]);
|
||||
// testResults: string[] = [];
|
||||
|
||||
$$addTestResult(name: string, key: string, result: boolean, summary?: string, message?: string): void {
|
||||
private _addTestResult(name: string, key: string, result: boolean, summary?: string, message?: string): void {
|
||||
const logLine = `${name}: ${key} ${summary ?? ""}`;
|
||||
this.testResults.update((results) => {
|
||||
results.push([result, logLine, message ?? ""]);
|
||||
return results;
|
||||
});
|
||||
}
|
||||
$everyModuleTest(): Promise<boolean> {
|
||||
private _everyModuleTest(): Promise<boolean> {
|
||||
if (!this.settings.enableDebugTools) return Promise.resolve(true);
|
||||
// this.core.$$addTestResult("DevModule", "Test", true);
|
||||
// return Promise.resolve(true);
|
||||
@@ -155,4 +156,11 @@ export class ModuleDev extends AbstractObsidianModule implements IObsidianModule
|
||||
// this.addTestResult("Test of test3", true);
|
||||
return this.testDone();
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.appLifecycle.handleLayoutReady(this._everyOnLayoutReady.bind(this));
|
||||
services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this));
|
||||
services.appLifecycle.handleOnSettingLoaded(this._everyOnloadAfterLoadSettings.bind(this));
|
||||
services.test.handleTest(this._everyModuleTest.bind(this));
|
||||
services.test.handleAddTestResult(this._addTestResult.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { delay } from "octagonal-wheels/promises";
|
||||
import { LOG_LEVEL_NOTICE, REMOTE_MINIO, type FilePathWithPrefix } from "src/lib/src/common/types";
|
||||
import { shareRunningResult } from "octagonal-wheels/concurrency/lock";
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule";
|
||||
|
||||
export class ModuleIntegratedTest extends AbstractObsidianModule implements IObsidianModule {
|
||||
export class ModuleIntegratedTest extends AbstractObsidianModule {
|
||||
async waitFor(proc: () => Promise<boolean>, timeout = 10000): Promise<boolean> {
|
||||
await delay(100);
|
||||
const start = Date.now();
|
||||
@@ -54,7 +54,7 @@ export class ModuleIntegratedTest extends AbstractObsidianModule implements IObs
|
||||
tryReplicate() {
|
||||
if (!this.settings.liveSync) {
|
||||
return shareRunningResult("replicate-test", async () => {
|
||||
await this.core.$$replicate();
|
||||
await this.services.replication.replicate();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ export class ModuleIntegratedTest extends AbstractObsidianModule implements IObs
|
||||
const stepFile = "_STEP.md" as FilePathWithPrefix;
|
||||
const stepAckFile = "_STEP_ACK.md" as FilePathWithPrefix;
|
||||
const stepContent = `Step ${no}`;
|
||||
await this.core.$anyResolveConflictByNewest(stepFile);
|
||||
await this.services.conflict.resolveByNewest(stepFile);
|
||||
await this.core.storageAccess.writeFileAuto(stepFile, stepContent);
|
||||
await this._orDie(`Wait for acknowledge ${no}`, async () => {
|
||||
if (
|
||||
@@ -96,7 +96,7 @@ export class ModuleIntegratedTest extends AbstractObsidianModule implements IObs
|
||||
return false;
|
||||
return true;
|
||||
});
|
||||
await this.core.$anyResolveConflictByNewest(stepAckFile);
|
||||
await this.services.conflict.resolveByNewest(stepAckFile);
|
||||
await this.core.storageAccess.writeFileAuto(stepAckFile, stepContent);
|
||||
await this.tryReplicate();
|
||||
return true;
|
||||
@@ -424,9 +424,9 @@ Line4:D`;
|
||||
await this._test("basic", async () => await this.nonLiveTestRunner(isLeader, (t, l) => this.testBasic(t, l)));
|
||||
}
|
||||
|
||||
async $everyModuleTestMultiDevice(): Promise<boolean> {
|
||||
async _everyModuleTestMultiDevice(): Promise<boolean> {
|
||||
if (!this.settings.enableDebugTools) return Promise.resolve(true);
|
||||
const isLeader = this.core.$$vaultName().indexOf("recv") === -1;
|
||||
const isLeader = this.core.services.vault.vaultName().indexOf("recv") === -1;
|
||||
this.addTestResult("-------", true, `Test as ${isLeader ? "Leader" : "Receiver"}`);
|
||||
try {
|
||||
this._log(`Starting Test`);
|
||||
@@ -440,4 +440,7 @@ Line4:D`;
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
onBindFunction(core: typeof this.core, services: typeof core.services): void {
|
||||
services.test.handleTestMultiDevice(this._everyModuleTestMultiDevice.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { delay } from "octagonal-wheels/promises";
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
||||
import { eventHub } from "../../common/events";
|
||||
import { getWebCrypto } from "../../lib/src/mods.ts";
|
||||
@@ -8,6 +8,7 @@ import { parseYaml, requestUrl, stringifyYaml } from "obsidian";
|
||||
import type { FilePath } from "../../lib/src/common/types.ts";
|
||||
import { scheduleTask } from "octagonal-wheels/concurrency/task";
|
||||
import { getFileRegExp } from "../../lib/src/common/utils.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
declare global {
|
||||
interface LSEvents {
|
||||
@@ -15,12 +16,15 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
export class ModuleReplicateTest extends AbstractObsidianModule implements IObsidianModule {
|
||||
export class ModuleReplicateTest extends AbstractObsidianModule {
|
||||
testRootPath = "_test/";
|
||||
testInfoPath = "_testinfo/";
|
||||
|
||||
get isLeader() {
|
||||
return this.core.$$getVaultName().indexOf("dev") >= 0 && this.core.$$vaultName().indexOf("recv") < 0;
|
||||
return (
|
||||
this.services.vault.getVaultName().indexOf("dev") >= 0 &&
|
||||
this.services.vault.vaultName().indexOf("recv") < 0
|
||||
);
|
||||
}
|
||||
|
||||
get nameByKind() {
|
||||
@@ -58,12 +62,12 @@ export class ModuleReplicateTest extends AbstractObsidianModule implements IObsi
|
||||
await this._dumpFileList("files.md");
|
||||
}
|
||||
}
|
||||
async $everyBeforeReplicate(showMessage: boolean): Promise<boolean> {
|
||||
async _everyBeforeReplicate(showMessage: boolean): Promise<boolean> {
|
||||
if (!this.settings.enableDebugTools) return Promise.resolve(true);
|
||||
await this.dumpList();
|
||||
return true;
|
||||
}
|
||||
$everyOnloadAfterLoadSettings(): Promise<boolean> {
|
||||
private _everyOnloadAfterLoadSettings(): Promise<boolean> {
|
||||
if (!this.settings.enableDebugTools) return Promise.resolve(true);
|
||||
this.addCommand({
|
||||
id: "dump-file-structure-normal",
|
||||
@@ -169,7 +173,7 @@ export class ModuleReplicateTest extends AbstractObsidianModule implements IObsi
|
||||
const out = [] as any[];
|
||||
const webcrypto = await getWebCrypto();
|
||||
for (const file of files) {
|
||||
if (!(await this.core.$$isTargetFile(file.path))) {
|
||||
if (!(await this.services.vault.isTargetFile(file.path))) {
|
||||
continue;
|
||||
}
|
||||
if (file.path.startsWith(this.testInfoPath)) continue;
|
||||
@@ -316,7 +320,7 @@ export class ModuleReplicateTest extends AbstractObsidianModule implements IObsi
|
||||
}
|
||||
|
||||
async testConflictedManually1() {
|
||||
await this.core.$$replicate();
|
||||
await this.services.replication.replicate();
|
||||
|
||||
const commonFile = `Resolve!
|
||||
*****, the amazing chocolatier!!`;
|
||||
@@ -325,8 +329,8 @@ export class ModuleReplicateTest extends AbstractObsidianModule implements IObsi
|
||||
await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "wonka.md", commonFile);
|
||||
}
|
||||
|
||||
await this.core.$$replicate();
|
||||
await this.core.$$replicate();
|
||||
await this.services.replication.replicate();
|
||||
await this.services.replication.replicate();
|
||||
if (
|
||||
(await this.core.confirm.askYesNoDialog("Ready to begin the test conflict Manually 1?", {
|
||||
timeout: 30,
|
||||
@@ -356,12 +360,12 @@ Charlie Bucket, Charlie Bucket, the amazing chocolatier!!`;
|
||||
) {
|
||||
return;
|
||||
}
|
||||
await this.core.$$replicate();
|
||||
await this.core.$$replicate();
|
||||
await this.services.replication.replicate();
|
||||
await this.services.replication.replicate();
|
||||
|
||||
if (
|
||||
!(await this.waitFor(async () => {
|
||||
await this.core.$$replicate();
|
||||
await this.services.replication.replicate();
|
||||
return (
|
||||
(await this.__assertStorageContent(
|
||||
(this.testRootPath + "wonka.md") as FilePath,
|
||||
@@ -379,7 +383,7 @@ Charlie Bucket, Charlie Bucket, the amazing chocolatier!!`;
|
||||
}
|
||||
|
||||
async testConflictedManually2() {
|
||||
await this.core.$$replicate();
|
||||
await this.services.replication.replicate();
|
||||
|
||||
const commonFile = `Resolve To concatenate
|
||||
ABCDEFG`;
|
||||
@@ -388,8 +392,8 @@ ABCDEFG`;
|
||||
await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "concat.md", commonFile);
|
||||
}
|
||||
|
||||
await this.core.$$replicate();
|
||||
await this.core.$$replicate();
|
||||
await this.services.replication.replicate();
|
||||
await this.services.replication.replicate();
|
||||
if (
|
||||
(await this.core.confirm.askYesNoDialog("Ready to begin the test conflict Manually 2?", {
|
||||
timeout: 30,
|
||||
@@ -420,12 +424,12 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ`;
|
||||
) {
|
||||
return;
|
||||
}
|
||||
await this.core.$$replicate();
|
||||
await this.core.$$replicate();
|
||||
await this.services.replication.replicate();
|
||||
await this.services.replication.replicate();
|
||||
|
||||
if (
|
||||
!(await this.waitFor(async () => {
|
||||
await this.core.$$replicate();
|
||||
await this.services.replication.replicate();
|
||||
return (
|
||||
(await this.__assertStorageContent(
|
||||
(this.testRootPath + "concat.md") as FilePath,
|
||||
@@ -457,8 +461,8 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ`;
|
||||
await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "task.md", baseDoc);
|
||||
}
|
||||
await delay(100);
|
||||
await this.core.$$replicate();
|
||||
await this.core.$$replicate();
|
||||
await this.services.replication.replicate();
|
||||
await this.services.replication.replicate();
|
||||
|
||||
if (
|
||||
(await this.core.confirm.askYesNoDialog("Ready to test conflict?", {
|
||||
@@ -487,8 +491,8 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ`;
|
||||
await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "task.md", mod2Doc);
|
||||
}
|
||||
|
||||
await this.core.$$replicate();
|
||||
await this.core.$$replicate();
|
||||
await this.services.replication.replicate();
|
||||
await this.services.replication.replicate();
|
||||
await delay(1000);
|
||||
if (
|
||||
(await this.core.confirm.askYesNoDialog("Ready to check result?", { timeout: 30, defaultOption: "Yes" })) ==
|
||||
@@ -496,8 +500,8 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ`;
|
||||
) {
|
||||
return;
|
||||
}
|
||||
await this.core.$$replicate();
|
||||
await this.core.$$replicate();
|
||||
await this.services.replication.replicate();
|
||||
await this.services.replication.replicate();
|
||||
const mergedDoc = `Tasks!
|
||||
- [ ] Task 1
|
||||
- [v] Task 2
|
||||
@@ -511,7 +515,7 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ`;
|
||||
this._log("Before testing conflicted files, resolve all once", LOG_LEVEL_NOTICE);
|
||||
await this.core.rebuilder.resolveAllConflictedFilesByNewerOnes();
|
||||
await this.core.rebuilder.resolveAllConflictedFilesByNewerOnes();
|
||||
await this.core.$$replicate();
|
||||
await this.services.replication.replicate();
|
||||
await delay(1000);
|
||||
if (!(await this.testConflictAutomatic())) {
|
||||
this._log("Conflict resolution (Auto) failed", LOG_LEVEL_NOTICE);
|
||||
@@ -569,11 +573,16 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ`;
|
||||
// return results;
|
||||
// });
|
||||
// }
|
||||
async $everyModuleTestMultiDevice(): Promise<boolean> {
|
||||
private async _everyModuleTestMultiDevice(): Promise<boolean> {
|
||||
if (!this.settings.enableDebugTools) return Promise.resolve(true);
|
||||
// this.core.$$addTestResult("DevModule", "Test", true);
|
||||
// return Promise.resolve(true);
|
||||
await this._test("Conflict resolution", async () => await this.checkConflictResolution());
|
||||
return this.testDone();
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.appLifecycle.handleOnSettingLoaded(this._everyOnloadAfterLoadSettings.bind(this));
|
||||
services.replication.handleBeforeReplicate(this._everyBeforeReplicate.bind(this));
|
||||
services.test.handleTestMultiDevice(this._everyModuleTestMultiDevice.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,14 +57,14 @@
|
||||
function moduleMultiDeviceTest() {
|
||||
if (moduleTesting) return;
|
||||
moduleTesting = true;
|
||||
plugin.$everyModuleTestMultiDevice().finally(() => {
|
||||
plugin.services.test.testMultiDevice().finally(() => {
|
||||
moduleTesting = false;
|
||||
});
|
||||
}
|
||||
function moduleSingleDeviceTest() {
|
||||
if (moduleTesting) return;
|
||||
moduleTesting = true;
|
||||
plugin.$everyModuleTest().finally(() => {
|
||||
plugin.services.test.test().finally(() => {
|
||||
moduleTesting = false;
|
||||
});
|
||||
}
|
||||
@@ -72,8 +72,8 @@
|
||||
if (moduleTesting) return;
|
||||
moduleTesting = true;
|
||||
try {
|
||||
await plugin.$everyModuleTest();
|
||||
await plugin.$everyModuleTestMultiDevice();
|
||||
await plugin.services.test.test();
|
||||
await plugin.services.test.testMultiDevice();
|
||||
} finally {
|
||||
moduleTesting = false;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,9 @@ function readDocument(w: LoadedEntry) {
|
||||
}
|
||||
export class DocumentHistoryModal extends Modal {
|
||||
plugin: ObsidianLiveSyncPlugin;
|
||||
get services() {
|
||||
return this.plugin.services;
|
||||
}
|
||||
range!: HTMLInputElement;
|
||||
contentView!: HTMLDivElement;
|
||||
info!: HTMLDivElement;
|
||||
@@ -74,7 +77,7 @@ export class DocumentHistoryModal extends Modal {
|
||||
this.id = id;
|
||||
this.initialRev = revision;
|
||||
if (!file && id) {
|
||||
this.file = this.plugin.$$id2path(id);
|
||||
this.file = this.services.path.id2path(id);
|
||||
}
|
||||
if (localStorage.getItem("ols-history-highlightdiff") == "1") {
|
||||
this.showDiff = true;
|
||||
@@ -83,7 +86,7 @@ export class DocumentHistoryModal extends Modal {
|
||||
|
||||
async loadFile(initialRev?: string) {
|
||||
if (!this.id) {
|
||||
this.id = await this.plugin.$$path2id(this.file);
|
||||
this.id = await this.services.path.path2id(this.file);
|
||||
}
|
||||
const db = this.plugin.localDatabase;
|
||||
try {
|
||||
@@ -126,7 +129,7 @@ export class DocumentHistoryModal extends Modal {
|
||||
}
|
||||
this.BlobURLs.delete(key);
|
||||
}
|
||||
generateBlobURL(key: string, data: Uint8Array) {
|
||||
generateBlobURL(key: string, data: Uint8Array<ArrayBuffer>) {
|
||||
this.revokeURL(key);
|
||||
const v = URL.createObjectURL(new Blob([data], { endings: "transparent", type: "application/octet-stream" }));
|
||||
this.BlobURLs.set(key, v);
|
||||
@@ -175,7 +178,10 @@ export class DocumentHistoryModal extends Modal {
|
||||
result = result.replace(/\n/g, "<br>");
|
||||
} else if (isImage(this.file)) {
|
||||
const src = this.generateBlobURL("base", w1data);
|
||||
const overlay = this.generateBlobURL("overlay", readDocument(w2) as Uint8Array);
|
||||
const overlay = this.generateBlobURL(
|
||||
"overlay",
|
||||
readDocument(w2) as Uint8Array<ArrayBuffer>
|
||||
);
|
||||
result = `<div class='ls-imgdiff-wrap'>
|
||||
<div class='overlay'>
|
||||
<img class='img-base' src="${src}">
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { VIEW_TYPE_GLOBAL_HISTORY, GlobalHistoryView } from "./GlobalHistory/GlobalHistoryView.ts";
|
||||
|
||||
export class ModuleObsidianGlobalHistory extends AbstractObsidianModule implements IObsidianModule {
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
export class ModuleObsidianGlobalHistory extends AbstractObsidianModule {
|
||||
_everyOnloadStart(): Promise<boolean> {
|
||||
this.addCommand({
|
||||
id: "livesync-global-history",
|
||||
name: "Show vault history",
|
||||
@@ -17,6 +17,9 @@ export class ModuleObsidianGlobalHistory extends AbstractObsidianModule implemen
|
||||
}
|
||||
|
||||
showGlobalHistory() {
|
||||
void this.core.$$showView(VIEW_TYPE_GLOBAL_HISTORY);
|
||||
void this.services.API.showWindow(VIEW_TYPE_GLOBAL_HISTORY);
|
||||
}
|
||||
onBindFunction(core: typeof this.core, services: typeof core.services): void {
|
||||
services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,14 @@ import {
|
||||
type diff_result,
|
||||
} from "../../lib/src/common/types.ts";
|
||||
import { ConflictResolveModal } from "./InteractiveConflictResolving/ConflictResolveModal.ts";
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { displayRev, getPath, getPathWithoutPrefix } from "../../common/utils.ts";
|
||||
import { fireAndForget } from "octagonal-wheels/promises";
|
||||
import { serialized } from "octagonal-wheels/concurrency/lock";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
export class ModuleInteractiveConflictResolver extends AbstractObsidianModule implements IObsidianModule {
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
export class ModuleInteractiveConflictResolver extends AbstractObsidianModule {
|
||||
_everyOnloadStart(): Promise<boolean> {
|
||||
this.addCommand({
|
||||
id: "livesync-conflictcheck",
|
||||
name: "Pick a file to resolve conflict",
|
||||
@@ -34,7 +35,7 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
async $anyResolveConflictByUI(filename: FilePathWithPrefix, conflictCheckResult: diff_result): Promise<boolean> {
|
||||
async _anyResolveConflictByUI(filename: FilePathWithPrefix, conflictCheckResult: diff_result): Promise<boolean> {
|
||||
// UI for resolving conflicts should one-by-one.
|
||||
return await serialized(`conflict-resolve-ui`, async () => {
|
||||
this._log("Merge:open conflict dialog", LOG_LEVEL_VERBOSE);
|
||||
@@ -68,7 +69,7 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
|
||||
}
|
||||
// 2. As usual, delete the conflicted revision and if there are no conflicts, write the resolved content to the storage.
|
||||
if (
|
||||
(await this.core.$$resolveConflictByDeletingRev(filename, delRev, "UI Concatenated")) ==
|
||||
(await this.services.conflict.resolveByDeletingRevision(filename, delRev, "UI Concatenated")) ==
|
||||
MISSING_OR_ERROR
|
||||
) {
|
||||
this._log(
|
||||
@@ -80,7 +81,7 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
|
||||
} else if (typeof toDelete === "string") {
|
||||
// Select one of the conflicted revision to delete.
|
||||
if (
|
||||
(await this.core.$$resolveConflictByDeletingRev(filename, toDelete, "UI Selected")) ==
|
||||
(await this.services.conflict.resolveByDeletingRevision(filename, toDelete, "UI Selected")) ==
|
||||
MISSING_OR_ERROR
|
||||
) {
|
||||
this._log(`Merge: Something went wrong: ${filename}, (${toDelete})`, LOG_LEVEL_NOTICE);
|
||||
@@ -93,11 +94,11 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
|
||||
// In here, some merge has been processed.
|
||||
// So we have to run replication if configured.
|
||||
// TODO: Make this is as a event request
|
||||
if (this.settings.syncAfterMerge && !this.core.$$isSuspended()) {
|
||||
await this.core.$$replicateByEvent();
|
||||
if (this.settings.syncAfterMerge && !this.services.appLifecycle.isSuspended()) {
|
||||
await this.services.replication.replicateByEvent();
|
||||
}
|
||||
// And, check it again.
|
||||
await this.core.$$queueConflictCheck(filename);
|
||||
await this.services.conflict.queueCheckFor(filename);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
@@ -120,14 +121,14 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
|
||||
const target = await this.core.confirm.askSelectString("File to resolve conflict", notesList);
|
||||
if (target) {
|
||||
const targetItem = notes.find((e) => e.dispPath == target)!;
|
||||
await this.core.$$queueConflictCheck(targetItem.path);
|
||||
await this.core.$$waitForAllConflictProcessed();
|
||||
await this.services.conflict.queueCheckFor(targetItem.path);
|
||||
await this.services.conflict.ensureAllProcessed();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async $allScanStat(): Promise<boolean> {
|
||||
async _allScanStat(): Promise<boolean> {
|
||||
const notes: { path: string; mtime: number }[] = [];
|
||||
this._log(`Checking conflicted files`, LOG_LEVEL_VERBOSE);
|
||||
for await (const doc of this.localDatabase.findAllDocs({ conflicts: true })) {
|
||||
@@ -157,4 +158,9 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
|
||||
}
|
||||
return true;
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.appLifecycle.handleOnScanningStartupIssues(this._allScanStat.bind(this));
|
||||
services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this));
|
||||
services.conflict.handleResolveByUserInteraction(this._anyResolveConflictByUI.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
} from "../../lib/src/mock_and_interop/stores.ts";
|
||||
import { eventHub } from "../../lib/src/hub/hub.ts";
|
||||
import { EVENT_FILE_RENAMED, EVENT_LAYOUT_READY, EVENT_LEAF_ACTIVE_CHANGED } from "../../common/events.ts";
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { addIcon, normalizePath, Notice } from "../../deps.ts";
|
||||
import { LOG_LEVEL_NOTICE, setGlobalLogFunction } from "octagonal-wheels/common/logger";
|
||||
import { QueueProcessor } from "octagonal-wheels/concurrency/processor";
|
||||
@@ -28,6 +28,7 @@ import { LogPaneView, VIEW_TYPE_LOG } from "./Log/LogPaneView.ts";
|
||||
import { serialized } from "octagonal-wheels/concurrency/lock";
|
||||
import { $msg } from "src/lib/src/common/i18n.ts";
|
||||
import { P2PLogCollector } from "../../lib/src/replication/trystero/P2PReplicatorCore.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
// This module cannot be a core module because it depends on the Obsidian UI.
|
||||
|
||||
@@ -50,7 +51,7 @@ const recentLogProcessor = new QueueProcessor(
|
||||
|
||||
const showDebugLog = false;
|
||||
export const MARK_DONE = "\u{2009}\u{2009}";
|
||||
export class ModuleLog extends AbstractObsidianModule implements IObsidianModule {
|
||||
export class ModuleLog extends AbstractObsidianModule {
|
||||
registerView = this.plugin.registerView.bind(this.plugin);
|
||||
|
||||
statusBar?: HTMLElement;
|
||||
@@ -178,7 +179,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
});
|
||||
|
||||
const statusBarLabels = reactive(() => {
|
||||
const scheduleMessage = this.core.$$isReloadingScheduled()
|
||||
const scheduleMessage = this.services.appLifecycle.isReloadingScheduled()
|
||||
? `WARNING! RESTARTING OBSIDIAN IS SCHEDULED!\n`
|
||||
: "";
|
||||
const { message } = statusLineLabel();
|
||||
@@ -199,7 +200,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
statusBarLabels.onChanged((label) => applyToDisplay(label.value));
|
||||
}
|
||||
|
||||
$everyOnload(): Promise<boolean> {
|
||||
private _everyOnload(): Promise<boolean> {
|
||||
eventHub.onEvent(EVENT_LEAF_ACTIVE_CHANGED, () => this.onActiveLeafChange());
|
||||
eventHub.onceEvent(EVENT_LAYOUT_READY, () => this.onActiveLeafChange());
|
||||
|
||||
@@ -219,15 +220,15 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
const thisFile = this.app.workspace.getActiveFile();
|
||||
if (!thisFile) return "";
|
||||
// Case Sensitivity
|
||||
if (this.core.$$shouldCheckCaseInsensitive()) {
|
||||
if (this.services.setting.shouldCheckCaseInsensitively()) {
|
||||
const f = this.core.storageAccess
|
||||
.getFiles()
|
||||
.map((e) => e.path)
|
||||
.filter((e) => e.toLowerCase() == thisFile.path.toLowerCase());
|
||||
if (f.length > 1) return "Not synchronised: There are multiple files with the same name";
|
||||
}
|
||||
if (!(await this.core.$$isTargetFile(thisFile.path))) return "Not synchronised: not a target file";
|
||||
if (this.core.$$isFileSizeExceeded(thisFile.stat.size)) return "Not synchronised: File size exceeded";
|
||||
if (!(await this.services.vault.isTargetFile(thisFile.path))) return "Not synchronised: not a target file";
|
||||
if (this.services.vault.isFileSizeTooLarge(thisFile.stat.size)) return "Not synchronised: File size exceeded";
|
||||
return "";
|
||||
}
|
||||
async setFileStatus() {
|
||||
@@ -287,14 +288,14 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
});
|
||||
}
|
||||
|
||||
$allStartOnUnload(): Promise<boolean> {
|
||||
private _allStartOnUnload(): Promise<boolean> {
|
||||
if (this.statusDiv) {
|
||||
this.statusDiv.remove();
|
||||
}
|
||||
document.querySelectorAll(`.livesync-status`)?.forEach((e) => e.remove());
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
_everyOnloadStart(): Promise<boolean> {
|
||||
addIcon(
|
||||
"view-log",
|
||||
`<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>`
|
||||
);
|
||||
this.addRibbonIcon("view-log", $msg("moduleLog.showLog"), () => {
|
||||
void this.core.$$showView(VIEW_TYPE_LOG);
|
||||
void this.services.API.showWindow(VIEW_TYPE_LOG);
|
||||
}).addClass("livesync-ribbon-showlog");
|
||||
|
||||
this.addCommand({
|
||||
id: "view-log",
|
||||
name: "Show log",
|
||||
callback: () => {
|
||||
void this.core.$$showView(VIEW_TYPE_LOG);
|
||||
void this.services.API.showWindow(VIEW_TYPE_LOG);
|
||||
},
|
||||
});
|
||||
this.registerView(VIEW_TYPE_LOG, (leaf) => new LogPaneView(leaf, this.plugin));
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
$everyOnloadAfterLoadSettings(): Promise<boolean> {
|
||||
private _everyOnloadAfterLoadSettings(): Promise<boolean> {
|
||||
logStore
|
||||
.pipeTo(
|
||||
new QueueProcessor((logs) => logs.forEach((e) => this.core.$$addLog(e.message, e.level, e.key)), {
|
||||
new QueueProcessor((logs) => logs.forEach((e) => this._addLog(e.message, e.level, e.key)), {
|
||||
suspended: false,
|
||||
batchSize: 20,
|
||||
concurrentLimit: 1,
|
||||
@@ -366,7 +367,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
})
|
||||
);
|
||||
}
|
||||
$$addLog(message: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key = ""): void {
|
||||
_addLog(message: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key = ""): void {
|
||||
if (level == LOG_LEVEL_DEBUG && !showDebugLog) {
|
||||
return;
|
||||
}
|
||||
@@ -376,7 +377,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
if (this.settings && !this.settings.showVerboseLog && level == LOG_LEVEL_VERBOSE) {
|
||||
return;
|
||||
}
|
||||
const vaultName = this.core.$$getVaultName();
|
||||
const vaultName = this.services.vault.getVaultName();
|
||||
const now = new Date();
|
||||
const timestamp = now.toLocaleString();
|
||||
const messageContent =
|
||||
@@ -437,4 +438,10 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
||||
}
|
||||
}
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this));
|
||||
services.appLifecycle.handleOnSettingLoaded(this._everyOnloadAfterLoadSettings.bind(this));
|
||||
services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this));
|
||||
services.appLifecycle.handleOnBeforeUnload(this._allStartOnUnload.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,18 @@ import { type TFile } from "obsidian";
|
||||
import { eventHub } from "../../common/events.ts";
|
||||
import { EVENT_REQUEST_SHOW_HISTORY } from "../../common/obsidianEvents.ts";
|
||||
import type { FilePathWithPrefix, LoadedEntry, DocumentID } from "../../lib/src/common/types.ts";
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { DocumentHistoryModal } from "./DocumentHistory/DocumentHistoryModal.ts";
|
||||
import { getPath } from "../../common/utils.ts";
|
||||
import { fireAndForget } from "octagonal-wheels/promises";
|
||||
|
||||
export class ModuleObsidianDocumentHistory extends AbstractObsidianModule implements IObsidianModule {
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
export class ModuleObsidianDocumentHistory extends AbstractObsidianModule {
|
||||
_everyOnloadStart(): Promise<boolean> {
|
||||
this.addCommand({
|
||||
id: "livesync-history",
|
||||
name: "Show history",
|
||||
callback: () => {
|
||||
const file = this.core.$$getActiveFilePath();
|
||||
const file = this.services.vault.getActiveFilePath();
|
||||
if (file) this.showHistory(file, undefined);
|
||||
},
|
||||
});
|
||||
@@ -51,4 +51,7 @@ export class ModuleObsidianDocumentHistory extends AbstractObsidianModule implem
|
||||
this.showHistory(targetId.path, targetId.id);
|
||||
}
|
||||
}
|
||||
onBindFunction(core: typeof this.core, services: typeof core.services): void {
|
||||
services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type IObsidianModule, AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
// import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser";
|
||||
import { EVENT_REQUEST_RELOAD_SETTING_TAB, EVENT_SETTING_SAVED, eventHub } from "../../common/events.ts";
|
||||
import {
|
||||
@@ -16,8 +16,9 @@ import { isCloudantURI } from "../../lib/src/pouchdb/utils_couchdb.ts";
|
||||
import { getLanguage } from "obsidian";
|
||||
import { SUPPORTED_I18N_LANGS, type I18N_LANGS } from "../../lib/src/common/rosetta.ts";
|
||||
import { decryptString, encryptString } from "@/lib/src/encryption/stringEncryption.ts";
|
||||
export class ModuleObsidianSettings extends AbstractObsidianModule implements IObsidianModule {
|
||||
async $everyOnLayoutReady(): Promise<boolean> {
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
export class ModuleObsidianSettings extends AbstractObsidianModule {
|
||||
async _everyOnLayoutReady(): Promise<boolean> {
|
||||
let isChanged = false;
|
||||
if (this.settings.displayLanguage == "") {
|
||||
const obsidianLanguage = getLanguage();
|
||||
@@ -32,7 +33,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
} else if (this.settings.displayLanguage == "") {
|
||||
this.settings.displayLanguage = "def";
|
||||
setLang(this.settings.displayLanguage);
|
||||
await this.core.$$saveSettingData();
|
||||
await this.services.setting.saveSettingData();
|
||||
}
|
||||
}
|
||||
if (isChanged) {
|
||||
@@ -46,7 +47,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
this.settings.displayLanguage = "def";
|
||||
setLang(this.settings.displayLanguage);
|
||||
}
|
||||
await this.core.$$saveSettingData();
|
||||
await this.services.setting.saveSettingData();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -61,13 +62,13 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
return methodFunc();
|
||||
}
|
||||
|
||||
$$saveDeviceAndVaultName(): void {
|
||||
const lsKey = "obsidian-live-sync-vaultanddevicename-" + this.core.$$getVaultName();
|
||||
localStorage.setItem(lsKey, this.core.$$getDeviceAndVaultName() || "");
|
||||
_saveDeviceAndVaultName(): void {
|
||||
const lsKey = "obsidian-live-sync-vaultanddevicename-" + this.services.vault.getVaultName();
|
||||
localStorage.setItem(lsKey, this.services.setting.getDeviceAndVaultName() || "");
|
||||
}
|
||||
|
||||
usedPassphrase = "";
|
||||
$$clearUsedPassphrase(): void {
|
||||
private _clearUsedPassphrase(): void {
|
||||
this.usedPassphrase = "";
|
||||
}
|
||||
|
||||
@@ -106,8 +107,8 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
return `${"appId" in this.app ? this.app.appId : ""}`;
|
||||
}
|
||||
|
||||
async $$saveSettingData() {
|
||||
this.core.$$saveDeviceAndVaultName();
|
||||
async _saveSettingData() {
|
||||
this.services.setting.saveDeviceAndVaultName();
|
||||
const settings = { ...this.settings };
|
||||
settings.deviceAndVaultName = "";
|
||||
if (this.usedPassphrase == "" && !(await this.getPassphrase(settings))) {
|
||||
@@ -174,7 +175,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
}
|
||||
}
|
||||
|
||||
async $$decryptSettings(settings: ObsidianLiveSyncSettings): Promise<ObsidianLiveSyncSettings> {
|
||||
async _decryptSettings(settings: ObsidianLiveSyncSettings): Promise<ObsidianLiveSyncSettings> {
|
||||
const passphrase = await this.getPassphrase(settings);
|
||||
if (passphrase === false) {
|
||||
this._log("No passphrase found for data.json! Verify configuration before syncing.", LOG_LEVEL_URGENT);
|
||||
@@ -234,7 +235,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
* @param settings
|
||||
* @returns
|
||||
*/
|
||||
$$adjustSettings(settings: ObsidianLiveSyncSettings): Promise<ObsidianLiveSyncSettings> {
|
||||
_adjustSettings(settings: ObsidianLiveSyncSettings): Promise<ObsidianLiveSyncSettings> {
|
||||
// Adjust settings as needed
|
||||
|
||||
// Delete this feature to avoid problems on mobile.
|
||||
@@ -264,7 +265,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
return Promise.resolve(settings);
|
||||
}
|
||||
|
||||
async $$loadSettings(): Promise<void> {
|
||||
async _loadSettings(): Promise<void> {
|
||||
const settings = Object.assign({}, DEFAULT_SETTINGS, await this.core.loadData()) as ObsidianLiveSyncSettings;
|
||||
|
||||
if (typeof settings.isConfigured == "undefined") {
|
||||
@@ -277,17 +278,17 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
}
|
||||
}
|
||||
|
||||
this.settings = await this.core.$$decryptSettings(settings);
|
||||
this.settings = await this.services.setting.decryptSettings(settings);
|
||||
|
||||
setLang(this.settings.displayLanguage);
|
||||
|
||||
await this.core.$$adjustSettings(this.settings);
|
||||
await this.services.setting.adjustSettings(this.settings);
|
||||
|
||||
const lsKey = "obsidian-live-sync-vaultanddevicename-" + this.core.$$getVaultName();
|
||||
const lsKey = "obsidian-live-sync-vaultanddevicename-" + this.services.vault.getVaultName();
|
||||
if (this.settings.deviceAndVaultName != "") {
|
||||
if (!localStorage.getItem(lsKey)) {
|
||||
this.core.$$setDeviceAndVaultName(this.settings.deviceAndVaultName);
|
||||
this.$$saveDeviceAndVaultName();
|
||||
this.services.setting.setDeviceAndVaultName(this.settings.deviceAndVaultName);
|
||||
this.services.setting.saveDeviceAndVaultName();
|
||||
this.settings.deviceAndVaultName = "";
|
||||
}
|
||||
}
|
||||
@@ -298,8 +299,8 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
);
|
||||
this.settings.customChunkSize = 0;
|
||||
}
|
||||
this.core.$$setDeviceAndVaultName(localStorage.getItem(lsKey) || "");
|
||||
if (this.core.$$getDeviceAndVaultName() == "") {
|
||||
this.services.setting.setDeviceAndVaultName(localStorage.getItem(lsKey) || "");
|
||||
if (this.services.setting.getDeviceAndVaultName() == "") {
|
||||
if (this.settings.usePluginSync) {
|
||||
this._log("Device name missing. Disabling plug-in sync.", LOG_LEVEL_NOTICE);
|
||||
this.settings.usePluginSync = false;
|
||||
@@ -309,4 +310,14 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
||||
// this.core.ignoreFiles = this.settings.ignoreFiles.split(",").map(e => e.trim());
|
||||
eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB);
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
super.onBindFunction(core, services);
|
||||
services.appLifecycle.handleLayoutReady(this._everyOnLayoutReady.bind(this));
|
||||
services.setting.handleClearUsedPassphrase(this._clearUsedPassphrase.bind(this));
|
||||
services.setting.handleDecryptSettings(this._decryptSettings.bind(this));
|
||||
services.setting.handleAdjustSettings(this._adjustSettings.bind(this));
|
||||
services.setting.handleLoadSettings(this._loadSettings.bind(this));
|
||||
services.setting.handleSaveDeviceAndVaultName(this._saveDeviceAndVaultName.bind(this));
|
||||
services.setting.handleSaveSettingData(this._saveSettingData.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type IObsidianModule, AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
// import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser";
|
||||
import { isObjectDifferent } from "octagonal-wheels/object";
|
||||
import { EVENT_SETTING_SAVED, eventHub } from "../../common/events";
|
||||
@@ -8,8 +8,8 @@ import { parseYaml, stringifyYaml } from "../../deps";
|
||||
import { LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
||||
const SETTING_HEADER = "````yaml:livesync-setting\n";
|
||||
const SETTING_FOOTER = "\n````";
|
||||
export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule implements IObsidianModule {
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule {
|
||||
_everyOnloadStart(): Promise<boolean> {
|
||||
this.addCommand({
|
||||
id: "livesync-export-config",
|
||||
name: "Write setting markdown manually",
|
||||
@@ -18,7 +18,7 @@ export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule imp
|
||||
return this.settings.settingSyncFile != "";
|
||||
}
|
||||
fireAndForget(async () => {
|
||||
await this.core.$$saveSettingData();
|
||||
await this.services.setting.saveSettingData();
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -160,7 +160,7 @@ export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule imp
|
||||
result == APPLY_AND_FETCH
|
||||
) {
|
||||
this.core.settings = settingToApply;
|
||||
await this.core.$$saveSettingData();
|
||||
await this.services.setting.saveSettingData();
|
||||
if (result == APPLY_ONLY) {
|
||||
this._log("Loaded settings have been applied!", LOG_LEVEL_NOTICE);
|
||||
return;
|
||||
@@ -171,7 +171,7 @@ export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule imp
|
||||
if (result == APPLY_AND_FETCH) {
|
||||
await this.core.rebuilder.scheduleFetch();
|
||||
}
|
||||
this.core.$$performRestart();
|
||||
this.services.appLifecycle.performRestart();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -242,4 +242,7 @@ We can perform a command in this file.
|
||||
this._log(`Markdown setting: ${filename} has been updated!`, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
}
|
||||
onBindFunction(core: typeof this.plugin, services: typeof core.services): void {
|
||||
services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { ObsidianLiveSyncSettingTab } from "./SettingDialogue/ObsidianLiveSyncSettingTab.ts";
|
||||
import { type IObsidianModule, AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
// import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser";
|
||||
import { EVENT_REQUEST_OPEN_SETTING_WIZARD, EVENT_REQUEST_OPEN_SETTINGS, eventHub } from "../../common/events.ts";
|
||||
|
||||
export class ModuleObsidianSettingDialogue extends AbstractObsidianModule implements IObsidianModule {
|
||||
export class ModuleObsidianSettingDialogue extends AbstractObsidianModule {
|
||||
settingTab!: ObsidianLiveSyncSettingTab;
|
||||
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
_everyOnloadStart(): Promise<boolean> {
|
||||
this.settingTab = new ObsidianLiveSyncSettingTab(this.app, this.plugin);
|
||||
this.plugin.addSettingTab(this.settingTab);
|
||||
eventHub.onEvent(EVENT_REQUEST_OPEN_SETTINGS, () => this.openSetting());
|
||||
@@ -29,4 +29,7 @@ export class ModuleObsidianSettingDialogue extends AbstractObsidianModule implem
|
||||
get appId() {
|
||||
return `${"appId" in this.app ? this.app.appId : ""}`;
|
||||
}
|
||||
onBindFunction(core: typeof this.plugin, services: typeof core.services): void {
|
||||
services.appLifecycle.handleOnInitialise(this._everyOnloadStart.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,15 +14,16 @@ import {
|
||||
EVENT_REQUEST_SHOW_SETUP_QR,
|
||||
eventHub,
|
||||
} from "../../common/events.ts";
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { decodeAnyArray, encodeAnyArray } from "../../common/utils.ts";
|
||||
import qrcode from "qrcode-generator";
|
||||
import { $msg } from "../../lib/src/common/i18n.ts";
|
||||
import { performDoctorConsultation, RebuildOptions } from "@/lib/src/common/configForDoc.ts";
|
||||
import { encryptString, decryptString } from "@/lib/src/encryption/stringEncryption.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsidianModule {
|
||||
$everyOnload(): Promise<boolean> {
|
||||
export class ModuleSetupObsidian extends AbstractObsidianModule {
|
||||
private _everyOnload(): Promise<boolean> {
|
||||
this.registerObsidianProtocolHandler("setuplivesync", async (conf: any) => {
|
||||
if (conf.settings) {
|
||||
await this.setupWizard(conf.settings);
|
||||
@@ -182,7 +183,7 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
|
||||
}
|
||||
|
||||
const newSettings = JSON.parse(JSON.stringify(tryingSettings)) as ObsidianLiveSyncSettings;
|
||||
const remoteConfig = await this.core.$$fetchRemotePreferredTweakValues(newSettings);
|
||||
const remoteConfig = await this.services.tweakValue.fetchRemotePreferred(newSettings);
|
||||
if (remoteConfig) {
|
||||
this._log("Remote configuration found.", LOG_LEVEL_NOTICE);
|
||||
const resultSettings = {
|
||||
@@ -282,16 +283,16 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
|
||||
);
|
||||
if (setupType == setupJustImport) {
|
||||
this.core.settings = newSettingW;
|
||||
this.core.$$clearUsedPassphrase();
|
||||
this.services.setting.clearUsedPassphrase();
|
||||
await this.core.saveSettings();
|
||||
} else if (setupType == setupAsNew) {
|
||||
this.core.settings = newSettingW;
|
||||
this.core.$$clearUsedPassphrase();
|
||||
this.services.setting.clearUsedPassphrase();
|
||||
await this.core.saveSettings();
|
||||
await this.core.rebuilder.$fetchLocal();
|
||||
} else if (setupType == setupAsMerge) {
|
||||
this.core.settings = newSettingW;
|
||||
this.core.$$clearUsedPassphrase();
|
||||
this.services.setting.clearUsedPassphrase();
|
||||
await this.core.saveSettings();
|
||||
await this.core.rebuilder.$fetchLocal(true);
|
||||
} else if (setupType == setupAgain) {
|
||||
@@ -308,7 +309,7 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
|
||||
}
|
||||
this.core.settings = newSettingW;
|
||||
await this.core.saveSettings();
|
||||
this.core.$$clearUsedPassphrase();
|
||||
this.services.setting.clearUsedPassphrase();
|
||||
await this.core.rebuilder.$rebuildEverything();
|
||||
} else {
|
||||
// Explicitly cancel the operation or the dialog was closed.
|
||||
@@ -345,4 +346,7 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi
|
||||
this._log(ex, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
}
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
services.appLifecycle.handleOnLoaded(this._everyOnload.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,9 @@ export function createStub(name: string, key: string, value: string, panel: stri
|
||||
|
||||
export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
plugin: ObsidianLiveSyncPlugin;
|
||||
get services() {
|
||||
return this.plugin.services;
|
||||
}
|
||||
selectedScreen = "";
|
||||
|
||||
_editingSettings?: AllSettings;
|
||||
@@ -139,8 +142,8 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
return await Promise.resolve();
|
||||
}
|
||||
if (key == "deviceAndVaultName") {
|
||||
this.plugin.$$setDeviceAndVaultName(this.editingSettings?.[key] ?? "");
|
||||
this.plugin.$$saveDeviceAndVaultName();
|
||||
this.services.setting.setDeviceAndVaultName(this.editingSettings?.[key] ?? "");
|
||||
this.services.setting.saveDeviceAndVaultName();
|
||||
return await Promise.resolve();
|
||||
}
|
||||
}
|
||||
@@ -210,7 +213,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
const ret = { ...OnDialogSettingsDefault };
|
||||
ret.configPassphrase = localStorage.getItem("ls-setting-passphrase") || "";
|
||||
ret.preset = "";
|
||||
ret.deviceAndVaultName = this.plugin.$$getDeviceAndVaultName();
|
||||
ret.deviceAndVaultName = this.services.setting.getDeviceAndVaultName();
|
||||
return ret;
|
||||
}
|
||||
computeAllLocalSettings(): Partial<OnDialogSettings> {
|
||||
@@ -295,7 +298,11 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
|
||||
async testConnection(settingOverride: Partial<ObsidianLiveSyncSettings> = {}): Promise<void> {
|
||||
const trialSetting = { ...this.editingSettings, ...settingOverride };
|
||||
const replicator = await this.plugin.$anyNewReplicator(trialSetting);
|
||||
const replicator = await this.services.replicator.getNewReplicator(trialSetting);
|
||||
if (!replicator) {
|
||||
Logger("No replicator available for the current settings.", LOG_LEVEL_NOTICE);
|
||||
return;
|
||||
}
|
||||
await replicator.tryConnectRemote(trialSetting);
|
||||
const status = await replicator.getRemoteStatus(trialSetting);
|
||||
if (status) {
|
||||
@@ -546,10 +553,14 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
const settingForCheck: RemoteDBSettings = {
|
||||
...this.editingSettings,
|
||||
};
|
||||
const replicator = this.plugin.$anyNewReplicator(settingForCheck);
|
||||
const replicator = this.services.replicator.getNewReplicator(settingForCheck);
|
||||
if (!(replicator instanceof LiveSyncCouchDBReplicator)) return true;
|
||||
|
||||
const db = await replicator.connectRemoteCouchDBWithSetting(settingForCheck, this.plugin.$$isMobile(), true);
|
||||
const db = await replicator.connectRemoteCouchDBWithSetting(
|
||||
settingForCheck,
|
||||
this.services.API.isMobile(),
|
||||
true
|
||||
);
|
||||
if (typeof db === "string") {
|
||||
Logger($msg("obsidianLiveSyncSettingTab.logCheckPassphraseFailed", { db }), LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
@@ -588,8 +599,8 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
this.editingSettings.passphrase = "";
|
||||
}
|
||||
this.applyAllSettings();
|
||||
await this.plugin.$allSuspendAllSync();
|
||||
await this.plugin.$allSuspendExtraSync();
|
||||
await this.services.setting.suspendAllSync();
|
||||
await this.services.setting.suspendExtraSync();
|
||||
this.reloadAllSettings();
|
||||
this.editingSettings.isConfigured = true;
|
||||
Logger($msg("obsidianLiveSyncSettingTab.logRebuildNote"), LOG_LEVEL_NOTICE);
|
||||
@@ -638,12 +649,12 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
await this.applyAllSettings();
|
||||
if (result == OPTION_FETCH) {
|
||||
await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG3_HR, "");
|
||||
this.plugin.$$scheduleAppReload();
|
||||
this.services.appLifecycle.scheduleRestart();
|
||||
this.closeSetting();
|
||||
// await rebuildDB("localOnly");
|
||||
} else if (result == OPTION_REBUILD_BOTH) {
|
||||
await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG2_HR, "");
|
||||
this.plugin.$$scheduleAppReload();
|
||||
this.services.appLifecycle.scheduleRestart();
|
||||
this.closeSetting();
|
||||
} else if (result == OPTION_ONLY_SETTING) {
|
||||
await this.plugin.saveSettings();
|
||||
|
||||
@@ -156,7 +156,7 @@ export function paneHatch(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement,
|
||||
}
|
||||
const obsidianInfo = {
|
||||
navigator: navigator.userAgent,
|
||||
fileSystem: this.plugin.$$isStorageInsensitive() ? "insensitive" : "sensitive",
|
||||
fileSystem: this.plugin.services.vault.isStorageInsensitive() ? "insensitive" : "sensitive",
|
||||
};
|
||||
const msgConfig = `# ---- Obsidian info ----
|
||||
${stringifyYaml(obsidianInfo)}
|
||||
@@ -182,10 +182,10 @@ ${stringifyYaml({
|
||||
|
||||
void addPanel(paneEl, "Scram Switches").then((paneEl) => {
|
||||
new Setting(paneEl).autoWireToggle("suspendFileWatching");
|
||||
this.addOnSaved("suspendFileWatching", () => this.plugin.$$askReload());
|
||||
this.addOnSaved("suspendFileWatching", () => this.services.appLifecycle.askRestart());
|
||||
|
||||
new Setting(paneEl).autoWireToggle("suspendParseReplicationResult");
|
||||
this.addOnSaved("suspendParseReplicationResult", () => this.plugin.$$askReload());
|
||||
this.addOnSaved("suspendParseReplicationResult", () => this.services.appLifecycle.askRestart());
|
||||
});
|
||||
|
||||
void addPanel(paneEl, "Recovery and Repair").then((paneEl) => {
|
||||
@@ -384,15 +384,16 @@ ${stringifyYaml({
|
||||
? await this.plugin.storageAccess.statHidden(path)
|
||||
: false;
|
||||
const fileOnStorage = stat != null ? stat : false;
|
||||
if (!(await this.plugin.$$isTargetFile(path))) return incProc();
|
||||
if (!(await this.services.vault.isTargetFile(path))) return incProc();
|
||||
const releaser = await semaphore.acquire(1);
|
||||
if (fileOnStorage && this.plugin.$$isFileSizeExceeded(fileOnStorage.size))
|
||||
if (fileOnStorage && this.services.vault.isFileSizeTooLarge(fileOnStorage.size))
|
||||
return incProc();
|
||||
try {
|
||||
const isHiddenFile = path.startsWith(".");
|
||||
const dbPath = isHiddenFile ? addPrefix(path, ICHeader) : path;
|
||||
const fileOnDB = await this.plugin.localDatabase.getDBEntry(dbPath);
|
||||
if (fileOnDB && this.plugin.$$isFileSizeExceeded(fileOnDB.size)) return incProc();
|
||||
if (fileOnDB && this.services.vault.isFileSizeTooLarge(fileOnDB.size))
|
||||
return incProc();
|
||||
|
||||
if (!fileOnDB && fileOnStorage) {
|
||||
Logger(`Compare: Not found on the local database: ${path}`, LOG_LEVEL_NOTICE);
|
||||
@@ -436,7 +437,7 @@ ${stringifyYaml({
|
||||
.onClick(async () => {
|
||||
for await (const docName of this.plugin.localDatabase.findAllDocNames()) {
|
||||
if (!docName.startsWith("f:")) {
|
||||
const idEncoded = await this.plugin.$$path2id(docName as FilePathWithPrefix);
|
||||
const idEncoded = await this.services.path.path2id(docName as FilePathWithPrefix);
|
||||
const doc = await this.plugin.localDatabase.getRaw(docName as DocumentID);
|
||||
if (!doc) continue;
|
||||
if (doc.type != "newnote" && doc.type != "plain") {
|
||||
@@ -477,7 +478,7 @@ ${stringifyYaml({
|
||||
if ((await this.plugin.localDatabase.putRaw(doc)).ok) {
|
||||
Logger(`Old ${docName} has been deleted`, LOG_LEVEL_NOTICE);
|
||||
}
|
||||
await this.plugin.$$queueConflictCheckIfOpen(docName as FilePathWithPrefix);
|
||||
await this.services.conflict.queueCheckForIfOpen(docName as FilePathWithPrefix);
|
||||
} else {
|
||||
Logger(`Converting ${docName} Failed!`, LOG_LEVEL_NOTICE);
|
||||
Logger(ret, LOG_LEVEL_VERBOSE);
|
||||
@@ -512,7 +513,7 @@ ${stringifyYaml({
|
||||
.onClick(async () => {
|
||||
this.editingSettings.isConfigured = false;
|
||||
await this.saveAllDirtySettings();
|
||||
this.plugin.$$askReload();
|
||||
this.services.appLifecycle.askRestart();
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ export function paneMaintenance(
|
||||
(e) => {
|
||||
e.addEventListener("click", () => {
|
||||
fireAndForget(async () => {
|
||||
await this.plugin.$$markRemoteResolved();
|
||||
await this.services.remote.markResolved();
|
||||
this.display();
|
||||
});
|
||||
});
|
||||
@@ -59,7 +59,7 @@ export function paneMaintenance(
|
||||
(e) => {
|
||||
e.addEventListener("click", () => {
|
||||
fireAndForget(async () => {
|
||||
await this.plugin.$$markRemoteUnlocked();
|
||||
await this.services.remote.markUnlocked();
|
||||
this.display();
|
||||
});
|
||||
});
|
||||
@@ -78,7 +78,7 @@ export function paneMaintenance(
|
||||
.setDisabled(false)
|
||||
.setWarning()
|
||||
.onClick(async () => {
|
||||
await this.plugin.$$markRemoteLocked();
|
||||
await this.services.remote.markLocked();
|
||||
})
|
||||
)
|
||||
.addOnUpdate(this.onlyOnCouchDBOrMinIO);
|
||||
@@ -93,7 +93,7 @@ export function paneMaintenance(
|
||||
.setWarning()
|
||||
.onClick(async () => {
|
||||
await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG, "");
|
||||
this.plugin.$$performRestart();
|
||||
this.services.appLifecycle.performRestart();
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -255,7 +255,7 @@ export function paneMaintenance(
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG3_HR, "");
|
||||
this.plugin.$$performRestart();
|
||||
this.services.appLifecycle.performRestart();
|
||||
})
|
||||
)
|
||||
.addButton((button) =>
|
||||
@@ -294,7 +294,7 @@ export function paneMaintenance(
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
await this.plugin.storageAccess.writeFileAuto(FLAGMD_REDFLAG2_HR, "");
|
||||
this.plugin.$$performRestart();
|
||||
this.services.appLifecycle.performRestart();
|
||||
})
|
||||
)
|
||||
.addButton((button) =>
|
||||
@@ -405,8 +405,8 @@ export function paneMaintenance(
|
||||
.setWarning()
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
await this.plugin.$$resetLocalDatabase();
|
||||
await this.plugin.$$initializeDatabase();
|
||||
await this.services.database.resetDatabase();
|
||||
await this.services.databaseEvents.initialiseDatabase();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -63,7 +63,7 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
|
||||
|
||||
this.addOnSaved("additionalSuffixOfDatabaseName", async (key) => {
|
||||
Logger("Suffix has been changed. Reopening database...", LOG_LEVEL_NOTICE);
|
||||
await this.plugin.$$initializeDatabase();
|
||||
await this.services.databaseEvents.initialiseDatabase();
|
||||
});
|
||||
|
||||
new Setting(paneEl).autoWireDropDown("hashAlg", {
|
||||
|
||||
@@ -403,7 +403,7 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
|
||||
|
||||
void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleCouchDB"), undefined, this.onlyOnCouchDB).then(
|
||||
(paneEl) => {
|
||||
if (this.plugin.$$isMobile()) {
|
||||
if (this.services.API.isMobile()) {
|
||||
this.createEl(
|
||||
paneEl,
|
||||
"div",
|
||||
@@ -630,7 +630,8 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
const trialSetting = { ...this.initialSettings, ...this.editingSettings };
|
||||
const newTweaks = await this.plugin.$$checkAndAskUseRemoteConfiguration(trialSetting);
|
||||
const newTweaks =
|
||||
await this.services.tweakValue.checkAndAskUseRemoteConfiguration(trialSetting);
|
||||
if (newTweaks.result !== false) {
|
||||
if (this.inWizard) {
|
||||
this.editingSettings = { ...this.editingSettings, ...newTweaks.result };
|
||||
@@ -648,15 +649,15 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
|
||||
}
|
||||
)) == "no"
|
||||
) {
|
||||
await this.plugin.$$saveSettingData();
|
||||
await this.services.setting.saveSettingData();
|
||||
return;
|
||||
}
|
||||
await this.plugin.$$saveSettingData();
|
||||
await this.services.setting.saveSettingData();
|
||||
await this.plugin.rebuilder.scheduleFetch();
|
||||
await this.plugin.$$scheduleAppReload();
|
||||
this.services.appLifecycle.scheduleRestart();
|
||||
return;
|
||||
} else {
|
||||
await this.plugin.$$saveSettingData();
|
||||
await this.services.setting.saveSettingData();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -727,7 +728,7 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal
|
||||
)) == "yes"
|
||||
) {
|
||||
const trialSetting = { ...this.initialSettings, ...this.editingSettings };
|
||||
const newTweaks = await this.plugin.$$checkAndAskUseRemoteConfiguration(trialSetting);
|
||||
const newTweaks = await this.services.tweakValue.checkAndAskUseRemoteConfiguration(trialSetting);
|
||||
if (newTweaks.result !== false) {
|
||||
this.editingSettings = { ...this.editingSettings, ...newTweaks.result };
|
||||
this.requestUpdate();
|
||||
|
||||
@@ -46,7 +46,7 @@ export function paneSetup(
|
||||
text.setButtonText($msg("obsidianLiveSyncSettingTab.btnEnable")).onClick(async () => {
|
||||
this.editingSettings.isConfigured = true;
|
||||
await this.saveAllDirtySettings();
|
||||
this.plugin.$$askReload();
|
||||
this.services.appLifecycle.askRestart();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -91,10 +91,10 @@ export function paneSetup(
|
||||
this.editingSettings = { ...this.editingSettings, ...DEFAULT_SETTINGS };
|
||||
await this.saveAllDirtySettings();
|
||||
this.plugin.settings = { ...DEFAULT_SETTINGS };
|
||||
await this.plugin.$$saveSettingData();
|
||||
await this.plugin.$$resetLocalDatabase();
|
||||
await this.services.setting.saveSettingData();
|
||||
await this.services.database.resetDatabase();
|
||||
// await this.plugin.initializeDatabase();
|
||||
this.plugin.$$askReload();
|
||||
this.services.appLifecycle.askRestart();
|
||||
}
|
||||
})
|
||||
.setWarning();
|
||||
|
||||
@@ -105,7 +105,7 @@ export function paneSyncSettings(
|
||||
if (!this.editingSettings.isConfigured) {
|
||||
this.editingSettings.isConfigured = true;
|
||||
await this.saveAllDirtySettings();
|
||||
await this.plugin.$$realizeSettingSyncMode();
|
||||
await this.services.setting.onRealiseSetting();
|
||||
await this.rebuildDB("localOnly");
|
||||
// this.resetEditingSettings();
|
||||
if (
|
||||
@@ -124,13 +124,13 @@ export function paneSyncSettings(
|
||||
await this.confirmRebuild();
|
||||
} else {
|
||||
await this.saveAllDirtySettings();
|
||||
await this.plugin.$$realizeSettingSyncMode();
|
||||
this.plugin.$$askReload();
|
||||
await this.services.setting.onRealiseSetting();
|
||||
this.services.appLifecycle.askRestart();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await this.saveAllDirtySettings();
|
||||
await this.plugin.$$realizeSettingSyncMode();
|
||||
await this.services.setting.onRealiseSetting();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -169,7 +169,7 @@ export function paneSyncSettings(
|
||||
}
|
||||
await this.saveSettings(["liveSync", "periodicReplication"]);
|
||||
|
||||
await this.plugin.$$realizeSettingSyncMode();
|
||||
await this.services.setting.onRealiseSetting();
|
||||
});
|
||||
|
||||
new Setting(paneEl)
|
||||
@@ -289,21 +289,21 @@ export function paneSyncSettings(
|
||||
button.setButtonText("Merge").onClick(async () => {
|
||||
this.closeSetting();
|
||||
// this.resetEditingSettings();
|
||||
await this.plugin.$anyConfigureOptionalSyncFeature("MERGE");
|
||||
await this.services.setting.enableOptionalFeature("MERGE");
|
||||
});
|
||||
})
|
||||
.addButton((button) => {
|
||||
button.setButtonText("Fetch").onClick(async () => {
|
||||
this.closeSetting();
|
||||
// this.resetEditingSettings();
|
||||
await this.plugin.$anyConfigureOptionalSyncFeature("FETCH");
|
||||
await this.services.setting.enableOptionalFeature("FETCH");
|
||||
});
|
||||
})
|
||||
.addButton((button) => {
|
||||
button.setButtonText("Overwrite").onClick(async () => {
|
||||
this.closeSetting();
|
||||
// this.resetEditingSettings();
|
||||
await this.plugin.$anyConfigureOptionalSyncFeature("OVERWRITE");
|
||||
await this.services.setting.enableOptionalFeature("OVERWRITE");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -13,12 +13,13 @@ import { versionNumberString2Number } from "../../lib/src/string_and_binary/conv
|
||||
import { cancelAllPeriodicTask, cancelAllTasks } from "octagonal-wheels/concurrency/task";
|
||||
import { stopAllRunningProcessors } from "octagonal-wheels/concurrency/processor";
|
||||
import { AbstractModule } from "../AbstractModule.ts";
|
||||
import type { ICoreModule } from "../ModuleTypes.ts";
|
||||
import { EVENT_PLATFORM_UNLOADED } from "../../lib/src/PlatformAPIs/base/APIBase.ts";
|
||||
import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
|
||||
export class ModuleLiveSyncMain extends AbstractModule implements ICoreModule {
|
||||
async $$onLiveSyncReady() {
|
||||
if (!(await this.core.$everyOnLayoutReady())) return;
|
||||
export class ModuleLiveSyncMain extends AbstractModule {
|
||||
async _onLiveSyncReady() {
|
||||
if (!(await this.core.services.appLifecycle.onLayoutReady())) return false;
|
||||
eventHub.emitEvent(EVENT_LAYOUT_READY);
|
||||
if (this.settings.suspendFileWatching || this.settings.suspendParseReplicationResult) {
|
||||
const ANSWER_KEEP = $msg("moduleLiveSyncMain.optionKeepLiveSyncDisabled");
|
||||
@@ -36,46 +37,49 @@ export class ModuleLiveSyncMain extends AbstractModule implements ICoreModule {
|
||||
this.settings.suspendFileWatching = false;
|
||||
this.settings.suspendParseReplicationResult = false;
|
||||
await this.saveSettings();
|
||||
await this.core.$$scheduleAppReload();
|
||||
return;
|
||||
this.services.appLifecycle.scheduleRestart();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const isInitialized = await this.core.$$initializeDatabase(false, false);
|
||||
const isInitialized = await this.services.databaseEvents.initialiseDatabase(false, false);
|
||||
if (!isInitialized) {
|
||||
//TODO:stop all sync.
|
||||
return false;
|
||||
}
|
||||
if (!(await this.core.$everyOnFirstInitialize())) return;
|
||||
await this.core.$$realizeSettingSyncMode();
|
||||
if (!(await this.core.services.appLifecycle.onFirstInitialise())) return false;
|
||||
// await this.core.$$realizeSettingSyncMode();
|
||||
await this.services.setting.onRealiseSetting();
|
||||
fireAndForget(async () => {
|
||||
this._log($msg("moduleLiveSyncMain.logAdditionalSafetyScan"), LOG_LEVEL_VERBOSE);
|
||||
if (!(await this.core.$allScanStat())) {
|
||||
if (!(await this.services.appLifecycle.onScanningStartupIssues())) {
|
||||
this._log($msg("moduleLiveSyncMain.logSafetyScanFailed"), LOG_LEVEL_NOTICE);
|
||||
} else {
|
||||
this._log($msg("moduleLiveSyncMain.logSafetyScanCompleted"), LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
$$wireUpEvents(): void {
|
||||
_wireUpEvents() {
|
||||
eventHub.onEvent(EVENT_SETTING_SAVED, (settings: ObsidianLiveSyncSettings) => {
|
||||
this.localDatabase.settings = settings;
|
||||
setLang(settings.displayLanguage);
|
||||
eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB);
|
||||
});
|
||||
eventHub.onEvent(EVENT_SETTING_SAVED, (settings: ObsidianLiveSyncSettings) => {
|
||||
fireAndForget(() => this.core.$$realizeSettingSyncMode());
|
||||
fireAndForget(() => this.core.services.setting.onRealiseSetting());
|
||||
});
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
async $$onLiveSyncLoad(): Promise<void> {
|
||||
this.$$wireUpEvents();
|
||||
async _onLiveSyncLoad(): Promise<boolean> {
|
||||
await this.services.appLifecycle.onWireUpEvents();
|
||||
// debugger;
|
||||
eventHub.emitEvent(EVENT_PLUGIN_LOADED, this.core);
|
||||
this._log($msg("moduleLiveSyncMain.logLoadingPlugin"));
|
||||
if (!(await this.core.$everyOnloadStart())) {
|
||||
if (!(await this.services.appLifecycle.onInitialise())) {
|
||||
this._log($msg("moduleLiveSyncMain.logPluginInitCancelled"), LOG_LEVEL_NOTICE);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
// this.addUIs();
|
||||
//@ts-ignore
|
||||
@@ -84,12 +88,12 @@ export class ModuleLiveSyncMain extends AbstractModule implements ICoreModule {
|
||||
const packageVersion: string = PACKAGE_VERSION || "0.0.0";
|
||||
|
||||
this._log($msg("moduleLiveSyncMain.logPluginVersion", { manifestVersion, packageVersion }));
|
||||
await this.core.$$loadSettings();
|
||||
if (!(await this.core.$everyOnloadAfterLoadSettings())) {
|
||||
await this.services.setting.loadSettings();
|
||||
if (!(await this.services.appLifecycle.onSettingLoaded())) {
|
||||
this._log($msg("moduleLiveSyncMain.logPluginInitCancelled"), LOG_LEVEL_NOTICE);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
const lsKey = "obsidian-live-sync-ver" + this.core.$$getVaultName();
|
||||
const lsKey = "obsidian-live-sync-ver" + this.services.vault.getVaultName();
|
||||
const last_version = localStorage.getItem(lsKey);
|
||||
|
||||
const lastVersion = ~~(versionNumberString2Number(manifestVersion) / 1000);
|
||||
@@ -113,22 +117,23 @@ export class ModuleLiveSyncMain extends AbstractModule implements ICoreModule {
|
||||
await this.saveSettings();
|
||||
}
|
||||
localStorage.setItem(lsKey, `${VER}`);
|
||||
await this.core.$$openDatabase();
|
||||
this.core.$$realizeSettingSyncMode = this.core.$$realizeSettingSyncMode.bind(this);
|
||||
await this.services.database.openDatabase();
|
||||
// this.core.$$realizeSettingSyncMode = this.core.$$realizeSettingSyncMode.bind(this);
|
||||
// this.$$parseReplicationResult = this.$$parseReplicationResult.bind(this);
|
||||
// this.$$replicate = this.$$replicate.bind(this);
|
||||
this.core.$$onLiveSyncReady = this.core.$$onLiveSyncReady.bind(this);
|
||||
await this.core.$everyOnload();
|
||||
// this.core.$$onLiveSyncReady = this.core.$$onLiveSyncReady.bind(this);
|
||||
await this.core.services.appLifecycle.onLoaded();
|
||||
await Promise.all(this.core.addOns.map((e) => e.onload()));
|
||||
return true;
|
||||
}
|
||||
|
||||
async $$onLiveSyncUnload(): Promise<void> {
|
||||
async _onLiveSyncUnload(): Promise<void> {
|
||||
eventHub.emitEvent(EVENT_PLUGIN_UNLOADED);
|
||||
await this.core.$allStartOnUnload();
|
||||
await this.services.appLifecycle.onBeforeUnload();
|
||||
cancelAllPeriodicTask();
|
||||
cancelAllTasks();
|
||||
stopAllRunningProcessors();
|
||||
await this.core.$allOnUnload();
|
||||
await this.services.appLifecycle.onUnload();
|
||||
this._unloaded = true;
|
||||
for (const addOn of this.core.addOns) {
|
||||
addOn.onunload();
|
||||
@@ -143,49 +148,68 @@ export class ModuleLiveSyncMain extends AbstractModule implements ICoreModule {
|
||||
eventHub.emitEvent(EVENT_PLATFORM_UNLOADED);
|
||||
eventHub.offAll();
|
||||
this._log($msg("moduleLiveSyncMain.logUnloadingPlugin"));
|
||||
return;
|
||||
}
|
||||
|
||||
async $$realizeSettingSyncMode(): Promise<void> {
|
||||
await this.core.$everyBeforeSuspendProcess();
|
||||
await this.core.$everyBeforeRealizeSetting();
|
||||
private async _realizeSettingSyncMode(): Promise<void> {
|
||||
await this.services.appLifecycle.onSuspending();
|
||||
await this.services.setting.onBeforeRealiseSetting();
|
||||
this.localDatabase.refreshSettings();
|
||||
await this.core.$everyCommitPendingFileEvent();
|
||||
await this.core.$everyRealizeSettingSyncMode();
|
||||
await this.services.fileProcessing.commitPendingFileEvents();
|
||||
await this.services.setting.onRealiseSetting();
|
||||
// disable all sync temporary.
|
||||
if (this.core.$$isSuspended()) return;
|
||||
await this.core.$everyOnResumeProcess();
|
||||
await this.core.$everyAfterResumeProcess();
|
||||
await this.core.$everyAfterRealizeSetting();
|
||||
if (this.services.appLifecycle.isSuspended()) return;
|
||||
await this.services.appLifecycle.onResuming();
|
||||
await this.services.appLifecycle.onResumed();
|
||||
await this.services.setting.onSettingRealised();
|
||||
return;
|
||||
}
|
||||
|
||||
$$isReloadingScheduled(): boolean {
|
||||
_isReloadingScheduled(): boolean {
|
||||
return this.core._totalProcessingCount !== undefined;
|
||||
}
|
||||
|
||||
isReady = false;
|
||||
|
||||
$$isReady(): boolean {
|
||||
_isReady(): boolean {
|
||||
return this.isReady;
|
||||
}
|
||||
|
||||
$$markIsReady(): void {
|
||||
_markIsReady(): void {
|
||||
this.isReady = true;
|
||||
}
|
||||
|
||||
$$resetIsReady(): void {
|
||||
_resetIsReady(): void {
|
||||
this.isReady = false;
|
||||
}
|
||||
|
||||
_suspended = false;
|
||||
$$isSuspended(): boolean {
|
||||
_isSuspended(): boolean {
|
||||
return this._suspended || !this.settings?.isConfigured;
|
||||
}
|
||||
$$setSuspended(value: boolean) {
|
||||
|
||||
_setSuspended(value: boolean) {
|
||||
this._suspended = value;
|
||||
}
|
||||
|
||||
_unloaded = false;
|
||||
$$isUnloaded(): boolean {
|
||||
_isUnloaded(): boolean {
|
||||
return this._unloaded;
|
||||
}
|
||||
|
||||
onBindFunction(core: LiveSyncCore, services: InjectableServiceHub): void {
|
||||
super.onBindFunction(core, services);
|
||||
services.appLifecycle.handleIsSuspended(this._isSuspended.bind(this));
|
||||
services.appLifecycle.handleSetSuspended(this._setSuspended.bind(this));
|
||||
services.appLifecycle.handleIsReady(this._isReady.bind(this));
|
||||
services.appLifecycle.handleMarkIsReady(this._markIsReady.bind(this));
|
||||
services.appLifecycle.handleResetIsReady(this._resetIsReady.bind(this));
|
||||
services.appLifecycle.handleHasUnloaded(this._isUnloaded.bind(this));
|
||||
services.appLifecycle.handleIsReloadingScheduled(this._isReloadingScheduled.bind(this));
|
||||
services.appLifecycle.handleOnReady(this._onLiveSyncReady.bind(this));
|
||||
services.appLifecycle.handleOnWireUpEvents(this._wireUpEvents.bind(this));
|
||||
services.appLifecycle.handleOnLoad(this._onLiveSyncLoad.bind(this));
|
||||
services.appLifecycle.handleOnAppUnload(this._onLiveSyncUnload.bind(this));
|
||||
services.setting.handleRealiseSetting(this._realizeSettingSyncMode.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
76
src/modules/services/ObsidianServices.ts
Normal file
76
src/modules/services/ObsidianServices.ts
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user