mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-07-12 07:43:12 +00:00
for 0.25.43-patched-7, please refer to the updates.md
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Self-hosted LiveSync",
|
"name": "Self-hosted LiveSync",
|
||||||
"version": "0.25.43-patched-6",
|
"version": "0.25.43-patched-7",
|
||||||
"minAppVersion": "0.9.12",
|
"minAppVersion": "0.9.12",
|
||||||
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"author": "vorotamoroz",
|
"author": "vorotamoroz",
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.25.43-patched-6",
|
"version": "0.25.43-patched-7",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.25.43-patched-6",
|
"version": "0.25.43-patched-7",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.808.0",
|
"@aws-sdk/client-s3": "^3.808.0",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.25.43-patched-6",
|
"version": "0.25.43-patched-7",
|
||||||
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -34,9 +34,11 @@ import { TrysteroReplicator } from "@lib/replication/trystero/TrysteroReplicator
|
|||||||
import { SETTING_KEY_P2P_DEVICE_NAME } from "@lib/common/types";
|
import { SETTING_KEY_P2P_DEVICE_NAME } from "@lib/common/types";
|
||||||
import { ServiceContext } from "@lib/services/base/ServiceBase";
|
import { ServiceContext } from "@lib/services/base/ServiceBase";
|
||||||
import type { InjectableServiceHub } from "@lib/services/InjectableServices";
|
import type { InjectableServiceHub } from "@lib/services/InjectableServices";
|
||||||
import { Menu } from "@/lib/src/services/implements/browser/Menu";
|
import { Menu } from "@lib/services/implements/browser/Menu";
|
||||||
import type { InjectableVaultServiceCompat } from "@/lib/src/services/implements/injectable/InjectableVaultService";
|
import type { InjectableVaultServiceCompat } from "@lib/services/implements/injectable/InjectableVaultService";
|
||||||
import { SimpleStoreIDBv2 } from "octagonal-wheels/databases/SimpleStoreIDBv2";
|
import { SimpleStoreIDBv2 } from "octagonal-wheels/databases/SimpleStoreIDBv2";
|
||||||
|
import type { InjectableAPIService } from "@/lib/src/services/implements/injectable/InjectableAPIService";
|
||||||
|
import type { BrowserAPIService } from "@/lib/src/services/implements/browser/BrowserAPIService";
|
||||||
|
|
||||||
function addToList(item: string, list: string) {
|
function addToList(item: string, list: string) {
|
||||||
return unique(
|
return unique(
|
||||||
@@ -81,13 +83,13 @@ export class P2PReplicatorShim implements P2PReplicatorBase, CommandShim {
|
|||||||
constructor() {
|
constructor() {
|
||||||
const browserServiceHub = new BrowserServiceHub<ServiceContext>();
|
const browserServiceHub = new BrowserServiceHub<ServiceContext>();
|
||||||
this.services = browserServiceHub;
|
this.services = browserServiceHub;
|
||||||
(this.services.vault as InjectableVaultServiceCompat<ServiceContext>).vaultName.setHandler(
|
|
||||||
|
(this.services.API as BrowserAPIService<ServiceContext>).getSystemVaultName.setHandler(
|
||||||
() => "p2p-livesync-web-peer"
|
() => "p2p-livesync-web-peer"
|
||||||
);
|
);
|
||||||
|
// this.services.setting.currentSettings.setHandler(() => {
|
||||||
this.services.setting.currentSettings.setHandler(() => {
|
// return this.settings as any;
|
||||||
return this.settings as any;
|
// });
|
||||||
});
|
|
||||||
}
|
}
|
||||||
async init() {
|
async init() {
|
||||||
// const { simpleStoreAPI } = await getWrappedSynchromesh();
|
// const { simpleStoreAPI } = await getWrappedSynchromesh();
|
||||||
@@ -106,7 +108,7 @@ export class P2PReplicatorShim implements P2PReplicatorBase, CommandShim {
|
|||||||
const repStore = SimpleStoreIDBv2.open<any>("p2p-livesync-web-peer");
|
const repStore = SimpleStoreIDBv2.open<any>("p2p-livesync-web-peer");
|
||||||
this._simpleStore = repStore;
|
this._simpleStore = repStore;
|
||||||
let _settings = (await repStore.get("settings")) || ({ ...P2P_DEFAULT_SETTINGS } as P2PSyncSetting);
|
let _settings = (await repStore.get("settings")) || ({ ...P2P_DEFAULT_SETTINGS } as P2PSyncSetting);
|
||||||
|
this.services.setting.settings = _settings as any;
|
||||||
this.plugin = {
|
this.plugin = {
|
||||||
saveSettings: async () => {
|
saveSettings: async () => {
|
||||||
await repStore.set("settings", _settings);
|
await repStore.set("settings", _settings);
|
||||||
|
|||||||
+1
-1
Submodule src/lib updated: b59a1deba8...b2bb66970c
+103
-53
@@ -2,7 +2,6 @@ import { Plugin, type App, type PluginManifest } from "./deps";
|
|||||||
import {
|
import {
|
||||||
type EntryDoc,
|
type EntryDoc,
|
||||||
type ObsidianLiveSyncSettings,
|
type ObsidianLiveSyncSettings,
|
||||||
type DatabaseConnectingStatus,
|
|
||||||
type HasSettings,
|
type HasSettings,
|
||||||
LOG_LEVEL_INFO,
|
LOG_LEVEL_INFO,
|
||||||
} from "./lib/src/common/types.ts";
|
} from "./lib/src/common/types.ts";
|
||||||
@@ -12,7 +11,6 @@ import { type LiveSyncReplicatorEnv } from "./lib/src/replication/LiveSyncAbstra
|
|||||||
import { LiveSyncCommands } from "./features/LiveSyncCommands.ts";
|
import { LiveSyncCommands } from "./features/LiveSyncCommands.ts";
|
||||||
import { HiddenFileSync } from "./features/HiddenFileSync/CmdHiddenFileSync.ts";
|
import { HiddenFileSync } from "./features/HiddenFileSync/CmdHiddenFileSync.ts";
|
||||||
import { ConfigSync } from "./features/ConfigSync/CmdConfigSync.ts";
|
import { ConfigSync } from "./features/ConfigSync/CmdConfigSync.ts";
|
||||||
import { reactiveSource, type ReactiveValue } from "octagonal-wheels/dataobject/reactive";
|
|
||||||
import { type LiveSyncJournalReplicatorEnv } from "./lib/src/replication/journal/LiveSyncJournalReplicator.js";
|
import { type LiveSyncJournalReplicatorEnv } from "./lib/src/replication/journal/LiveSyncJournalReplicator.js";
|
||||||
import { type LiveSyncCouchDBReplicatorEnv } from "./lib/src/replication/couchdb/LiveSyncReplicator.js";
|
import { type LiveSyncCouchDBReplicatorEnv } from "./lib/src/replication/couchdb/LiveSyncReplicator.js";
|
||||||
import type { CheckPointInfo } from "./lib/src/replication/journal/JournalSyncTypes.js";
|
import type { CheckPointInfo } from "./lib/src/replication/journal/JournalSyncTypes.js";
|
||||||
@@ -24,7 +22,6 @@ import { ModuleCheckRemoteSize } from "./modules/essentialObsidian/ModuleCheckRe
|
|||||||
import { ModuleConflictResolver } from "./modules/coreFeatures/ModuleConflictResolver.ts";
|
import { ModuleConflictResolver } from "./modules/coreFeatures/ModuleConflictResolver.ts";
|
||||||
import { ModuleInteractiveConflictResolver } from "./modules/features/ModuleInteractiveConflictResolver.ts";
|
import { ModuleInteractiveConflictResolver } from "./modules/features/ModuleInteractiveConflictResolver.ts";
|
||||||
import { ModuleLog } from "./modules/features/ModuleLog.ts";
|
import { ModuleLog } from "./modules/features/ModuleLog.ts";
|
||||||
import { ModuleObsidianSettings } from "./modules/features/ModuleObsidianSetting.ts";
|
|
||||||
import { ModuleRedFlag } from "./modules/coreFeatures/ModuleRedFlag.ts";
|
import { ModuleRedFlag } from "./modules/coreFeatures/ModuleRedFlag.ts";
|
||||||
import { ModuleObsidianMenu } from "./modules/essentialObsidian/ModuleObsidianMenu.ts";
|
import { ModuleObsidianMenu } from "./modules/essentialObsidian/ModuleObsidianMenu.ts";
|
||||||
import { ModuleSetupObsidian } from "./modules/features/ModuleSetupObsidian.ts";
|
import { ModuleSetupObsidian } from "./modules/features/ModuleSetupObsidian.ts";
|
||||||
@@ -66,6 +63,8 @@ import { __$checkInstanceBinding } from "./lib/src/dev/checks.ts";
|
|||||||
import { ServiceFileHandler } from "./serviceModules/FileHandler.ts";
|
import { ServiceFileHandler } from "./serviceModules/FileHandler.ts";
|
||||||
import { FileAccessObsidian } from "./serviceModules/FileAccessObsidian.ts";
|
import { FileAccessObsidian } from "./serviceModules/FileAccessObsidian.ts";
|
||||||
import { StorageEventManagerObsidian } from "./managers/StorageEventManagerObsidian.ts";
|
import { StorageEventManagerObsidian } from "./managers/StorageEventManagerObsidian.ts";
|
||||||
|
import { onLayoutReadyFeatures } from "./serviceFeatures/onLayoutReady.ts";
|
||||||
|
import type { ServiceModules } from "./types.ts";
|
||||||
|
|
||||||
export default class ObsidianLiveSyncPlugin
|
export default class ObsidianLiveSyncPlugin
|
||||||
extends Plugin
|
extends Plugin
|
||||||
@@ -88,12 +87,27 @@ export default class ObsidianLiveSyncPlugin
|
|||||||
return this._services;
|
return this._services;
|
||||||
}
|
}
|
||||||
|
|
||||||
private initialiseServices() {
|
/**
|
||||||
this._services = new ObsidianServiceHub(this);
|
* Service Modules
|
||||||
|
*/
|
||||||
|
protected _serviceModules: ServiceModules;
|
||||||
|
|
||||||
|
get serviceModules() {
|
||||||
|
return this._serviceModules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* addOns: Non-essential and graphically features
|
||||||
|
*/
|
||||||
addOns = [] as LiveSyncCommands[];
|
addOns = [] as LiveSyncCommands[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The modules of the plug-in. Modules are responsible for specific features or functionalities of the plug-in, such as file handling, conflict resolution, replication, etc.
|
||||||
|
*/
|
||||||
|
private modules = [
|
||||||
|
// Move to registerModules
|
||||||
|
] as (IObsidianModule | AbstractModule)[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* register an add-onn to the plug-in.
|
* register an add-onn to the plug-in.
|
||||||
* Add-ons are features that are not essential to the core functionality of the plugin,
|
* Add-ons are features that are not essential to the core functionality of the plugin,
|
||||||
@@ -122,13 +136,6 @@ export default class ObsidianLiveSyncPlugin
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The modules of the plug-in. Modules are responsible for specific features or functionalities of the plug-in, such as file handling, conflict resolution, replication, etc.
|
|
||||||
*/
|
|
||||||
private modules = [
|
|
||||||
// Move to registerModules
|
|
||||||
] as (IObsidianModule | AbstractModule)[];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a module by its class. Throws an error if not found.
|
* Get a module by its class. Throws an error if not found.
|
||||||
* Mostly used for getting SetupManager.
|
* Mostly used for getting SetupManager.
|
||||||
@@ -162,7 +169,6 @@ export default class ObsidianLiveSyncPlugin
|
|||||||
this._registerModule(new ModuleInitializerFile(this));
|
this._registerModule(new ModuleInitializerFile(this));
|
||||||
this._registerModule(new ModuleObsidianAPI(this, this));
|
this._registerModule(new ModuleObsidianAPI(this, this));
|
||||||
this._registerModule(new ModuleObsidianEvents(this, this));
|
this._registerModule(new ModuleObsidianEvents(this, this));
|
||||||
this._registerModule(new ModuleObsidianSettings(this));
|
|
||||||
this._registerModule(new ModuleResolvingMismatchedTweaks(this));
|
this._registerModule(new ModuleResolvingMismatchedTweaks(this));
|
||||||
this._registerModule(new ModuleObsidianSettingsAsMarkdown(this));
|
this._registerModule(new ModuleObsidianSettingsAsMarkdown(this));
|
||||||
this._registerModule(new ModuleObsidianSettingDialogue(this, this));
|
this._registerModule(new ModuleObsidianSettingDialogue(this, this));
|
||||||
@@ -206,9 +212,24 @@ export default class ObsidianLiveSyncPlugin
|
|||||||
return this.services.UI.confirm;
|
return this.services.UI.confirm;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This property will be changed from outside often, so will be set later.
|
/**
|
||||||
settings!: ObsidianLiveSyncSettings;
|
* @obsolete Use services.setting.currentSettings instead. The current settings of the plug-in.
|
||||||
|
*/
|
||||||
|
get settings() {
|
||||||
|
return this.services.setting.settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @obsolete Use services.setting.settings instead. Set the settings of the plug-in.
|
||||||
|
*/
|
||||||
|
set settings(value: ObsidianLiveSyncSettings) {
|
||||||
|
this.services.setting.settings = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @obsolete Use services.setting.currentSettings instead. Get the settings of the plug-in.
|
||||||
|
* @returns The current settings of the plug-in.
|
||||||
|
*/
|
||||||
getSettings(): ObsidianLiveSyncSettings {
|
getSettings(): ObsidianLiveSyncSettings {
|
||||||
return this.settings;
|
return this.settings;
|
||||||
}
|
}
|
||||||
@@ -259,46 +280,63 @@ export default class ObsidianLiveSyncPlugin
|
|||||||
/// Modules which were relied on services
|
/// Modules which were relied on services
|
||||||
/**
|
/**
|
||||||
* Storage Accessor for handling file operations.
|
* Storage Accessor for handling file operations.
|
||||||
|
* @obsolete Use serviceModules.storageAccess instead.
|
||||||
*/
|
*/
|
||||||
storageAccess: StorageAccess;
|
get storageAccess(): StorageAccess {
|
||||||
|
return this.serviceModules.storageAccess;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Database File Accessor for handling file operations related to the database, such as exporting the database, importing from a file, etc.
|
* Database File Accessor for handling file operations related to the database, such as exporting the database, importing from a file, etc.
|
||||||
|
* @obsolete Use serviceModules.databaseFileAccess instead.
|
||||||
*/
|
*/
|
||||||
databaseFileAccess: DatabaseFileAccess;
|
get databaseFileAccess(): DatabaseFileAccess {
|
||||||
|
return this.serviceModules.databaseFileAccess;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* File Handler for handling file operations related to replication, such as resolving conflicts, applying changes from replication, etc.
|
* File Handler for handling file operations related to replication, such as resolving conflicts, applying changes from replication, etc.
|
||||||
|
* @obsolete Use serviceModules.fileHandler instead.
|
||||||
*/
|
*/
|
||||||
fileHandler: IFileHandler;
|
get fileHandler(): IFileHandler {
|
||||||
|
return this.serviceModules.fileHandler;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Rebuilder for handling database rebuilding operations.
|
* Rebuilder for handling database rebuilding operations.
|
||||||
|
* @obsolete Use serviceModules.rebuilder instead.
|
||||||
*/
|
*/
|
||||||
rebuilder: Rebuilder;
|
get rebuilder(): Rebuilder {
|
||||||
|
return this.serviceModules.rebuilder;
|
||||||
|
}
|
||||||
|
|
||||||
requestCount = reactiveSource(0);
|
// requestCount = reactiveSource(0);
|
||||||
responseCount = reactiveSource(0);
|
// responseCount = reactiveSource(0);
|
||||||
totalQueued = reactiveSource(0);
|
// totalQueued = reactiveSource(0);
|
||||||
batched = reactiveSource(0);
|
// batched = reactiveSource(0);
|
||||||
processing = reactiveSource(0);
|
// processing = reactiveSource(0);
|
||||||
databaseQueueCount = reactiveSource(0);
|
// databaseQueueCount = reactiveSource(0);
|
||||||
storageApplyingCount = reactiveSource(0);
|
// storageApplyingCount = reactiveSource(0);
|
||||||
replicationResultCount = reactiveSource(0);
|
// replicationResultCount = reactiveSource(0);
|
||||||
conflictProcessQueueCount = reactiveSource(0);
|
|
||||||
pendingFileEventCount = reactiveSource(0);
|
|
||||||
processingFileEventCount = reactiveSource(0);
|
|
||||||
|
|
||||||
_totalProcessingCount?: ReactiveValue<number>;
|
// pendingFileEventCount = reactiveSource(0);
|
||||||
|
// processingFileEventCount = reactiveSource(0);
|
||||||
|
|
||||||
replicationStat = reactiveSource({
|
// _totalProcessingCount?: ReactiveValue<number>;
|
||||||
sent: 0,
|
|
||||||
arrived: 0,
|
|
||||||
maxPullSeq: 0,
|
|
||||||
maxPushSeq: 0,
|
|
||||||
lastSyncPullSeq: 0,
|
|
||||||
lastSyncPushSeq: 0,
|
|
||||||
syncStatus: "CLOSED" as DatabaseConnectingStatus,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// replicationStat = reactiveSource({
|
||||||
|
// sent: 0,
|
||||||
|
// arrived: 0,
|
||||||
|
// maxPullSeq: 0,
|
||||||
|
// maxPushSeq: 0,
|
||||||
|
// lastSyncPullSeq: 0,
|
||||||
|
// lastSyncPushSeq: 0,
|
||||||
|
// syncStatus: "CLOSED" as DatabaseConnectingStatus,
|
||||||
|
// });
|
||||||
|
|
||||||
|
private initialiseServices() {
|
||||||
|
this._services = new ObsidianServiceHub(this);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Initialise service modules.
|
||||||
|
*/
|
||||||
private initialiseServiceModules() {
|
private initialiseServiceModules() {
|
||||||
const storageAccessManager = new StorageAccessManager();
|
const storageAccessManager = new StorageAccessManager();
|
||||||
// If we want to implement to the other platform, implement ObsidianXXXXXService.
|
// If we want to implement to the other platform, implement ObsidianXXXXXService.
|
||||||
@@ -358,6 +396,7 @@ export default class ObsidianLiveSyncPlugin
|
|||||||
vault: this.services.vault,
|
vault: this.services.vault,
|
||||||
fileHandler: fileHandler,
|
fileHandler: fileHandler,
|
||||||
storageAccess: storageAccess,
|
storageAccess: storageAccess,
|
||||||
|
control: this.services.control,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
rebuilder,
|
rebuilder,
|
||||||
@@ -367,34 +406,45 @@ export default class ObsidianLiveSyncPlugin
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @obsolete Use services.setting.saveSettingData instead. Save the settings to the disk. This is usually called after changing the settings in the code, to persist the changes.
|
||||||
|
*/
|
||||||
|
async saveSettings() {
|
||||||
|
await this.services.setting.saveSettingData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise ServiceFeatures.
|
||||||
|
* (Please refer `serviceFeatures` for more details)
|
||||||
|
*/
|
||||||
|
initialiseServiceFeatures() {
|
||||||
|
for (const feature of onLayoutReadyFeatures) {
|
||||||
|
const curriedFeature = () => feature(this);
|
||||||
|
this.services.appLifecycle.onLayoutReady.addHandler(curriedFeature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constructor(app: App, manifest: PluginManifest) {
|
constructor(app: App, manifest: PluginManifest) {
|
||||||
super(app, manifest);
|
super(app, manifest);
|
||||||
this.initialiseServices();
|
this.initialiseServices();
|
||||||
this.registerModules();
|
this.registerModules();
|
||||||
this.registerAddOns();
|
this.registerAddOns();
|
||||||
const instances = this.initialiseServiceModules();
|
this._serviceModules = this.initialiseServiceModules();
|
||||||
this.rebuilder = instances.rebuilder;
|
this.initialiseServiceFeatures();
|
||||||
this.fileHandler = instances.fileHandler;
|
|
||||||
this.databaseFileAccess = instances.databaseFileAccess;
|
|
||||||
this.storageAccess = instances.storageAccess;
|
|
||||||
this.bindModuleFunctions();
|
this.bindModuleFunctions();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _startUp() {
|
private async _startUp() {
|
||||||
await this.services.appLifecycle.onLoad();
|
if (!(await this.services.control.onLoad())) return;
|
||||||
const onReady = this.services.appLifecycle.onReady.bind(this.services.appLifecycle);
|
const onReady = this.services.control.onReady.bind(this.services.control);
|
||||||
this.app.workspace.onLayoutReady(onReady);
|
this.app.workspace.onLayoutReady(onReady);
|
||||||
}
|
}
|
||||||
onload() {
|
onload() {
|
||||||
void this._startUp();
|
void this._startUp();
|
||||||
}
|
}
|
||||||
async saveSettings() {
|
|
||||||
await this.services.setting.saveSettingData();
|
|
||||||
}
|
|
||||||
onunload() {
|
onunload() {
|
||||||
return void this.services.appLifecycle.onAppUnload();
|
return void this.services.control.onUnload();
|
||||||
}
|
}
|
||||||
// <-- Plug-in's overrideable functions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For now,
|
// For now,
|
||||||
|
|||||||
@@ -203,8 +203,8 @@ export class StorageEventManagerObsidian extends StorageEventManagerBase {
|
|||||||
const totalItems = allItems.length + this.concurrentProcessing.waiting;
|
const totalItems = allItems.length + this.concurrentProcessing.waiting;
|
||||||
const processing = this.processingCount;
|
const processing = this.processingCount;
|
||||||
const batchedCount = this._waitingMap.size;
|
const batchedCount = this._waitingMap.size;
|
||||||
this.core.batched.value = batchedCount;
|
this.fileProcessing.batched.value = batchedCount;
|
||||||
this.core.processing.value = processing;
|
this.fileProcessing.processing.value = processing;
|
||||||
this.core.totalQueued.value = totalItems + batchedCount + processing;
|
this.fileProcessing.totalQueued.value = totalItems + batchedCount + processing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, Logger } from "octagonal-wheels/co
|
|||||||
import type { AnyEntry, FilePathWithPrefix } from "@lib/common/types";
|
import type { AnyEntry, FilePathWithPrefix } from "@lib/common/types";
|
||||||
import type { LiveSyncCore } from "@/main";
|
import type { LiveSyncCore } from "@/main";
|
||||||
import { stripAllPrefixes } from "@lib/string_and_binary/path";
|
import { stripAllPrefixes } from "@lib/string_and_binary/path";
|
||||||
import { createInstanceLogFunction } from "@/lib/src/services/lib/logUtils";
|
import { createInstanceLogFunction } from "@lib/services/lib/logUtils";
|
||||||
|
|
||||||
export abstract class AbstractModule {
|
export abstract class AbstractModule {
|
||||||
_log = createInstanceLogFunction(this.constructor.name, this.services.API);
|
_log = createInstanceLogFunction(this.constructor.name, this.services.API);
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { fireAndForget } from "octagonal-wheels/promises";
|
|||||||
import { AbstractModule } from "../AbstractModule";
|
import { AbstractModule } from "../AbstractModule";
|
||||||
import { Logger, LOG_LEVEL_NOTICE, LOG_LEVEL_INFO, LEVEL_NOTICE, type LOG_LEVEL } from "octagonal-wheels/common/logger";
|
import { Logger, LOG_LEVEL_NOTICE, LOG_LEVEL_INFO, LEVEL_NOTICE, type LOG_LEVEL } from "octagonal-wheels/common/logger";
|
||||||
import { isLockAcquired, shareRunningResult, skipIfDuplicated } from "octagonal-wheels/concurrency/lock";
|
import { isLockAcquired, shareRunningResult, skipIfDuplicated } from "octagonal-wheels/concurrency/lock";
|
||||||
import { balanceChunkPurgedDBs } from "@/lib/src/pouchdb/chunks";
|
import { balanceChunkPurgedDBs } from "@lib/pouchdb/chunks";
|
||||||
import { purgeUnreferencedChunks } from "@/lib/src/pouchdb/chunks";
|
import { purgeUnreferencedChunks } from "@lib/pouchdb/chunks";
|
||||||
import { LiveSyncCouchDBReplicator } from "../../lib/src/replication/couchdb/LiveSyncReplicator";
|
import { LiveSyncCouchDBReplicator } from "../../lib/src/replication/couchdb/LiveSyncReplicator";
|
||||||
import { type EntryDoc, type RemoteType } from "../../lib/src/common/types";
|
import { type EntryDoc, type RemoteType } from "../../lib/src/common/types";
|
||||||
import { rateLimitedSharedExecution, scheduleTask, updatePreviousExecutionTime } from "../../common/utils";
|
import { rateLimitedSharedExecution, scheduleTask, updatePreviousExecutionTime } from "../../common/utils";
|
||||||
@@ -12,8 +12,8 @@ import { EVENT_FILE_SAVED, EVENT_SETTING_SAVED, eventHub } from "../../common/ev
|
|||||||
import { $msg } from "../../lib/src/common/i18n";
|
import { $msg } from "../../lib/src/common/i18n";
|
||||||
import type { LiveSyncCore } from "../../main";
|
import type { LiveSyncCore } from "../../main";
|
||||||
import { ReplicateResultProcessor } from "./ReplicateResultProcessor";
|
import { ReplicateResultProcessor } from "./ReplicateResultProcessor";
|
||||||
import { UnresolvedErrorManager } from "@/lib/src/services/base/UnresolvedErrorManager";
|
import { UnresolvedErrorManager } from "@lib/services/base/UnresolvedErrorManager";
|
||||||
import { clearHandlers } from "@/lib/src/replication/SyncParamsHandler";
|
import { clearHandlers } from "@lib/replication/SyncParamsHandler";
|
||||||
|
|
||||||
const KEY_REPLICATION_ON_EVENT = "replicationOnEvent";
|
const KEY_REPLICATION_ON_EVENT = "replicationOnEvent";
|
||||||
const REPLICATION_ON_EVENT_FORECASTED_TIME = 5000;
|
const REPLICATION_ON_EVENT_FORECASTED_TIME = 5000;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
type EntryLeaf,
|
type EntryLeaf,
|
||||||
type LoadedEntry,
|
type LoadedEntry,
|
||||||
type MetaEntry,
|
type MetaEntry,
|
||||||
} from "@/lib/src/common/types";
|
} from "@lib/common/types";
|
||||||
import type { ModuleReplicator } from "./ModuleReplicator";
|
import type { ModuleReplicator } from "./ModuleReplicator";
|
||||||
import { isChunk, isValidPath } from "@/common/utils";
|
import { isChunk, isValidPath } from "@/common/utils";
|
||||||
import type { LiveSyncCore } from "@/main";
|
import type { LiveSyncCore } from "@/main";
|
||||||
@@ -17,8 +17,8 @@ import {
|
|||||||
LOG_LEVEL_VERBOSE,
|
LOG_LEVEL_VERBOSE,
|
||||||
Logger,
|
Logger,
|
||||||
type LOG_LEVEL,
|
type LOG_LEVEL,
|
||||||
} from "@/lib/src/common/logger";
|
} from "@lib/common/logger";
|
||||||
import { fireAndForget, isAnyNote, throttle } from "@/lib/src/common/utils";
|
import { fireAndForget, isAnyNote, throttle } from "@lib/common/utils";
|
||||||
import { Semaphore } from "octagonal-wheels/concurrency/semaphore_v2";
|
import { Semaphore } from "octagonal-wheels/concurrency/semaphore_v2";
|
||||||
import { serialized } from "octagonal-wheels/concurrency/lock";
|
import { serialized } from "octagonal-wheels/concurrency/lock";
|
||||||
import type { ReactiveSource } from "octagonal-wheels/dataobject/reactive_v2";
|
import type { ReactiveSource } from "octagonal-wheels/dataobject/reactive_v2";
|
||||||
@@ -162,7 +162,8 @@ export class ReplicateResultProcessor {
|
|||||||
* Report the current status.
|
* Report the current status.
|
||||||
*/
|
*/
|
||||||
protected reportStatus() {
|
protected reportStatus() {
|
||||||
this.core.replicationResultCount.value = this._queuedChanges.length + this._processingChanges.length;
|
this.services.replication.replicationResultCount.value =
|
||||||
|
this._queuedChanges.length + this._processingChanges.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -381,7 +382,7 @@ export class ReplicateResultProcessor {
|
|||||||
releaser();
|
releaser();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, this.replicator.core.databaseQueueCount);
|
}, this.services.replication.databaseQueueCount);
|
||||||
}
|
}
|
||||||
// Phase 2.1: process the document and apply to storage
|
// Phase 2.1: process the document and apply to storage
|
||||||
// This function is serialized per document to avoid race-condition for the same document.
|
// This function is serialized per document to avoid race-condition for the same document.
|
||||||
@@ -432,7 +433,7 @@ export class ReplicateResultProcessor {
|
|||||||
protected applyToStorage(entry: MetaEntry) {
|
protected applyToStorage(entry: MetaEntry) {
|
||||||
return this.withCounting(async () => {
|
return this.withCounting(async () => {
|
||||||
await this.services.replication.processSynchroniseResult(entry);
|
await this.services.replication.processSynchroniseResult(entry);
|
||||||
}, this.replicator.core.storageApplyingCount);
|
}, this.services.replication.storageApplyingCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export class ModuleConflictChecker extends AbstractModule {
|
|||||||
delay: 0,
|
delay: 0,
|
||||||
keepResultUntilDownstreamConnected: true,
|
keepResultUntilDownstreamConnected: true,
|
||||||
pipeTo: this.conflictResolveQueue,
|
pipeTo: this.conflictResolveQueue,
|
||||||
totalRemainingReactiveSource: this.core.conflictProcessQueueCount,
|
totalRemainingReactiveSource: this.services.conflict.conflictProcessQueueCount,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
onBindFunction(core: LiveSyncCore, services: InjectableServiceHub): void {
|
onBindFunction(core: LiveSyncCore, services: InjectableServiceHub): void {
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import type { LiveSyncCore } from "../../main.ts";
|
|||||||
import FetchEverything from "../features/SetupWizard/dialogs/FetchEverything.svelte";
|
import FetchEverything from "../features/SetupWizard/dialogs/FetchEverything.svelte";
|
||||||
import RebuildEverything from "../features/SetupWizard/dialogs/RebuildEverything.svelte";
|
import RebuildEverything from "../features/SetupWizard/dialogs/RebuildEverything.svelte";
|
||||||
import { extractObject } from "octagonal-wheels/object";
|
import { extractObject } from "octagonal-wheels/object";
|
||||||
import { SvelteDialogManagerBase } from "@/lib/src/UI/svelteDialog.ts";
|
import { SvelteDialogManagerBase } from "@lib/UI/svelteDialog.ts";
|
||||||
import type { ServiceContext } from "@/lib/src/services/base/ServiceBase.ts";
|
import type { ServiceContext } from "@lib/services/base/ServiceBase.ts";
|
||||||
|
|
||||||
export class ModuleRedFlag extends AbstractModule {
|
export class ModuleRedFlag extends AbstractModule {
|
||||||
async isFlagFileExist(path: string) {
|
async isFlagFileExist(path: string) {
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import {
|
|||||||
import { Notice, requestUrl, type RequestUrlParam, type RequestUrlResponse } from "../../deps.ts";
|
import { Notice, requestUrl, type RequestUrlParam, type RequestUrlResponse } from "../../deps.ts";
|
||||||
import { type CouchDBCredentials, type EntryDoc } from "../../lib/src/common/types.ts";
|
import { type CouchDBCredentials, type EntryDoc } from "../../lib/src/common/types.ts";
|
||||||
import { isCloudantURI, isValidRemoteCouchDBURI } from "../../lib/src/pouchdb/utils_couchdb.ts";
|
import { isCloudantURI, isValidRemoteCouchDBURI } from "../../lib/src/pouchdb/utils_couchdb.ts";
|
||||||
import { replicationFilter } from "@/lib/src/pouchdb/compress.ts";
|
import { replicationFilter } from "@lib/pouchdb/compress.ts";
|
||||||
import { disableEncryption } from "@/lib/src/pouchdb/encryption.ts";
|
import { disableEncryption } from "@lib/pouchdb/encryption.ts";
|
||||||
import { enableEncryption } from "@/lib/src/pouchdb/encryption.ts";
|
import { enableEncryption } from "@lib/pouchdb/encryption.ts";
|
||||||
import { setNoticeClass } from "../../lib/src/mock_and_interop/wrapper.ts";
|
import { setNoticeClass } from "../../lib/src/mock_and_interop/wrapper.ts";
|
||||||
import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser.ts";
|
import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser.ts";
|
||||||
import { AuthorizationHeaderGenerator } from "../../lib/src/replication/httplib.ts";
|
import { AuthorizationHeaderGenerator } from "../../lib/src/replication/httplib.ts";
|
||||||
@@ -96,7 +96,7 @@ export class ModuleObsidianAPI extends AbstractObsidianModule {
|
|||||||
const size = body ? ` (${body.length})` : "";
|
const size = body ? ` (${body.length})` : "";
|
||||||
try {
|
try {
|
||||||
const r = await this.__fetchByAPI(url, authHeader, opts);
|
const r = await this.__fetchByAPI(url, authHeader, opts);
|
||||||
this.plugin.requestCount.value = this.plugin.requestCount.value + 1;
|
this.services.API.requestCount.value = this.services.API.requestCount.value + 1;
|
||||||
if (method == "POST" || method == "PUT") {
|
if (method == "POST" || method == "PUT") {
|
||||||
this.last_successful_post = r.status - (r.status % 100) == 200;
|
this.last_successful_post = r.status - (r.status % 100) == 200;
|
||||||
} else {
|
} else {
|
||||||
@@ -113,7 +113,7 @@ export class ModuleObsidianAPI extends AbstractObsidianModule {
|
|||||||
this._log(ex);
|
this._log(ex);
|
||||||
throw ex;
|
throw ex;
|
||||||
} finally {
|
} finally {
|
||||||
this.plugin.responseCount.value = this.plugin.responseCount.value + 1;
|
this.services.API.responseCount.value = this.services.API.responseCount.value + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +171,7 @@ export class ModuleObsidianAPI extends AbstractObsidianModule {
|
|||||||
headers.append("authorization", authHeader);
|
headers.append("authorization", authHeader);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this.plugin.requestCount.value = this.plugin.requestCount.value + 1;
|
this.services.API.requestCount.value = this.services.API.requestCount.value + 1;
|
||||||
const response: Response = await (useRequestAPI
|
const response: Response = await (useRequestAPI
|
||||||
? this.__fetchByAPI(url.toString(), authHeader, { ...opts, headers })
|
? this.__fetchByAPI(url.toString(), authHeader, { ...opts, headers })
|
||||||
: fetch(url, { ...opts, headers }));
|
: fetch(url, { ...opts, headers }));
|
||||||
@@ -245,7 +245,7 @@ export class ModuleObsidianAPI extends AbstractObsidianModule {
|
|||||||
this._log(ex);
|
this._log(ex);
|
||||||
throw ex;
|
throw ex;
|
||||||
} finally {
|
} finally {
|
||||||
this.plugin.responseCount.value = this.plugin.responseCount.value + 1;
|
this.services.API.responseCount.value = this.services.API.responseCount.value + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// return await fetch(url, opts);
|
// return await fetch(url, opts);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { scheduleTask } from "octagonal-wheels/concurrency/task";
|
|||||||
import { type TFile } from "../../deps.ts";
|
import { type TFile } from "../../deps.ts";
|
||||||
import { fireAndForget } from "octagonal-wheels/promises";
|
import { fireAndForget } from "octagonal-wheels/promises";
|
||||||
import { type FilePathWithPrefix } from "../../lib/src/common/types.ts";
|
import { type FilePathWithPrefix } from "../../lib/src/common/types.ts";
|
||||||
import { reactive, reactiveSource } from "octagonal-wheels/dataobject/reactive";
|
import { reactive, reactiveSource, type ReactiveSource } from "octagonal-wheels/dataobject/reactive";
|
||||||
import {
|
import {
|
||||||
collectingChunks,
|
collectingChunks,
|
||||||
pluginScanningCount,
|
pluginScanningCount,
|
||||||
@@ -188,20 +188,25 @@ export class ModuleObsidianEvents extends AbstractObsidianModule {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// TODO: separate
|
|
||||||
|
// Process counting for app reload scheduling
|
||||||
|
_totalProcessingCount?: ReactiveSource<number> = undefined;
|
||||||
private _scheduleAppReload() {
|
private _scheduleAppReload() {
|
||||||
if (!this.core._totalProcessingCount) {
|
if (!this._totalProcessingCount) {
|
||||||
const __tick = reactiveSource(0);
|
const __tick = reactiveSource(0);
|
||||||
this.core._totalProcessingCount = reactive(() => {
|
this._totalProcessingCount = reactive(() => {
|
||||||
const dbCount = this.core.databaseQueueCount.value;
|
const dbCount = this.services.replication.databaseQueueCount.value;
|
||||||
const replicationCount = this.core.replicationResultCount.value;
|
const replicationCount = this.services.replication.replicationResultCount.value;
|
||||||
const storageApplyingCount = this.core.storageApplyingCount.value;
|
const storageApplyingCount = this.services.replication.storageApplyingCount.value;
|
||||||
const chunkCount = collectingChunks.value;
|
const chunkCount = collectingChunks.value;
|
||||||
const pluginScanCount = pluginScanningCount.value;
|
const pluginScanCount = pluginScanningCount.value;
|
||||||
const hiddenFilesCount = hiddenFilesEventCount.value + hiddenFilesProcessingCount.value;
|
const hiddenFilesCount = hiddenFilesEventCount.value + hiddenFilesProcessingCount.value;
|
||||||
const conflictProcessCount = this.core.conflictProcessQueueCount.value;
|
const conflictProcessCount = this.services.conflict.conflictProcessQueueCount.value;
|
||||||
const e = this.core.pendingFileEventCount.value;
|
// Now no longer `pendingFileEventCount` and `processingFileEventCount` is used
|
||||||
const proc = this.core.processingFileEventCount.value;
|
// const e = this.core.pendingFileEventCount.value;
|
||||||
|
// const proc = this.core.processingFileEventCount.value;
|
||||||
|
const e = 0;
|
||||||
|
const proc = 0;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const __ = __tick.value;
|
const __ = __tick.value;
|
||||||
return (
|
return (
|
||||||
@@ -223,7 +228,7 @@ export class ModuleObsidianEvents extends AbstractObsidianModule {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let stableCheck = 3;
|
let stableCheck = 3;
|
||||||
this.core._totalProcessingCount.onChanged((e) => {
|
this._totalProcessingCount.onChanged((e) => {
|
||||||
if (e.value == 0) {
|
if (e.value == 0) {
|
||||||
if (stableCheck-- <= 0) {
|
if (stableCheck-- <= 0) {
|
||||||
this.__performAppReload();
|
this.__performAppReload();
|
||||||
@@ -239,10 +244,14 @@ export class ModuleObsidianEvents extends AbstractObsidianModule {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_isReloadingScheduled(): boolean {
|
||||||
|
return this._totalProcessingCount !== undefined;
|
||||||
|
}
|
||||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||||
services.appLifecycle.onLayoutReady.addHandler(this._everyOnLayoutReady.bind(this));
|
services.appLifecycle.onLayoutReady.addHandler(this._everyOnLayoutReady.bind(this));
|
||||||
services.appLifecycle.onInitialise.addHandler(this._everyOnloadStart.bind(this));
|
services.appLifecycle.onInitialise.addHandler(this._everyOnloadStart.bind(this));
|
||||||
services.appLifecycle.askRestart.setHandler(this._askReload.bind(this));
|
services.appLifecycle.askRestart.setHandler(this._askReload.bind(this));
|
||||||
services.appLifecycle.scheduleRestart.setHandler(this._scheduleAppReload.bind(this));
|
services.appLifecycle.scheduleRestart.setHandler(this._scheduleAppReload.bind(this));
|
||||||
|
services.appLifecycle.isReloadingScheduled.setHandler(this._isReloadingScheduled.bind(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export class ModuleObsidianMenu extends AbstractModule {
|
|||||||
this.settings.liveSync = true;
|
this.settings.liveSync = true;
|
||||||
this._log("LiveSync Enabled.", LOG_LEVEL_NOTICE);
|
this._log("LiveSync Enabled.", LOG_LEVEL_NOTICE);
|
||||||
}
|
}
|
||||||
await this.services.setting.realiseSetting();
|
await this.services.control.applySettings();
|
||||||
await this.services.setting.saveSettingData();
|
await this.services.setting.saveSettingData();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -74,7 +74,7 @@ export class ModuleObsidianMenu extends AbstractModule {
|
|||||||
this.services.appLifecycle.setSuspended(true);
|
this.services.appLifecycle.setSuspended(true);
|
||||||
this._log("Self-hosted LiveSync suspended", LOG_LEVEL_NOTICE);
|
this._log("Self-hosted LiveSync suspended", LOG_LEVEL_NOTICE);
|
||||||
}
|
}
|
||||||
await this.services.setting.realiseSetting();
|
await this.services.control.applySettings();
|
||||||
await this.services.setting.saveSettingData();
|
await this.services.setting.saveSettingData();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -32,14 +32,14 @@ import { serialized } from "octagonal-wheels/concurrency/lock";
|
|||||||
import { $msg } from "src/lib/src/common/i18n.ts";
|
import { $msg } from "src/lib/src/common/i18n.ts";
|
||||||
import { P2PLogCollector } from "../../lib/src/replication/trystero/P2PReplicatorCore.ts";
|
import { P2PLogCollector } from "../../lib/src/replication/trystero/P2PReplicatorCore.ts";
|
||||||
import type { LiveSyncCore } from "../../main.ts";
|
import type { LiveSyncCore } from "../../main.ts";
|
||||||
import { LiveSyncError } from "@/lib/src/common/LSError.ts";
|
import { LiveSyncError } from "@lib/common/LSError.ts";
|
||||||
import { isValidPath } from "@/common/utils.ts";
|
import { isValidPath } from "@/common/utils.ts";
|
||||||
import {
|
import {
|
||||||
isValidFilenameInAndroid,
|
isValidFilenameInAndroid,
|
||||||
isValidFilenameInDarwin,
|
isValidFilenameInDarwin,
|
||||||
isValidFilenameInWidows,
|
isValidFilenameInWidows,
|
||||||
} from "@/lib/src/string_and_binary/path.ts";
|
} from "@lib/string_and_binary/path.ts";
|
||||||
import { MARK_LOG_SEPARATOR } from "@/lib/src/services/lib/logUtils.ts";
|
import { MARK_LOG_SEPARATOR } from "@lib/services/lib/logUtils.ts";
|
||||||
|
|
||||||
// This module cannot be a core module because it depends on the Obsidian UI.
|
// This module cannot be a core module because it depends on the Obsidian UI.
|
||||||
|
|
||||||
@@ -102,12 +102,12 @@ export class ModuleLog extends AbstractObsidianModule {
|
|||||||
});
|
});
|
||||||
return computed(() => formatted.value);
|
return computed(() => formatted.value);
|
||||||
}
|
}
|
||||||
const labelReplication = padLeftSpComputed(this.core.replicationResultCount, `📥`);
|
const labelReplication = padLeftSpComputed(this.services.replication.replicationResultCount, `📥`);
|
||||||
const labelDBCount = padLeftSpComputed(this.core.databaseQueueCount, `📄`);
|
const labelDBCount = padLeftSpComputed(this.services.replication.databaseQueueCount, `📄`);
|
||||||
const labelStorageCount = padLeftSpComputed(this.core.storageApplyingCount, `💾`);
|
const labelStorageCount = padLeftSpComputed(this.services.replication.storageApplyingCount, `💾`);
|
||||||
const labelChunkCount = padLeftSpComputed(collectingChunks, `🧩`);
|
const labelChunkCount = padLeftSpComputed(collectingChunks, `🧩`);
|
||||||
const labelPluginScanCount = padLeftSpComputed(pluginScanningCount, `🔌`);
|
const labelPluginScanCount = padLeftSpComputed(pluginScanningCount, `🔌`);
|
||||||
const labelConflictProcessCount = padLeftSpComputed(this.core.conflictProcessQueueCount, `🔩`);
|
const labelConflictProcessCount = padLeftSpComputed(this.services.conflict.conflictProcessQueueCount, `🔩`);
|
||||||
const hiddenFilesCount = reactive(() => hiddenFilesEventCount.value - hiddenFilesProcessingCount.value);
|
const hiddenFilesCount = reactive(() => hiddenFilesEventCount.value - hiddenFilesProcessingCount.value);
|
||||||
const labelHiddenFilesCount = padLeftSpComputed(hiddenFilesCount, `⚙️`);
|
const labelHiddenFilesCount = padLeftSpComputed(hiddenFilesCount, `⚙️`);
|
||||||
const queueCountLabelX = reactive(() => {
|
const queueCountLabelX = reactive(() => {
|
||||||
@@ -116,12 +116,12 @@ export class ModuleLog extends AbstractObsidianModule {
|
|||||||
const queueCountLabel = () => queueCountLabelX.value;
|
const queueCountLabel = () => queueCountLabelX.value;
|
||||||
|
|
||||||
const requestingStatLabel = computed(() => {
|
const requestingStatLabel = computed(() => {
|
||||||
const diff = this.core.requestCount.value - this.core.responseCount.value;
|
const diff = this.services.API.requestCount.value - this.services.API.responseCount.value;
|
||||||
return diff != 0 ? "📲 " : "";
|
return diff != 0 ? "📲 " : "";
|
||||||
});
|
});
|
||||||
|
|
||||||
const replicationStatLabel = computed(() => {
|
const replicationStatLabel = computed(() => {
|
||||||
const e = this.core.replicationStat.value;
|
const e = this.services.replicator.replicationStatics.value;
|
||||||
const sent = e.sent;
|
const sent = e.sent;
|
||||||
const arrived = e.arrived;
|
const arrived = e.arrived;
|
||||||
const maxPullSeq = e.maxPullSeq;
|
const maxPullSeq = e.maxPullSeq;
|
||||||
@@ -173,9 +173,9 @@ export class ModuleLog extends AbstractObsidianModule {
|
|||||||
}
|
}
|
||||||
return { w, sent, pushLast, arrived, pullLast };
|
return { w, sent, pushLast, arrived, pullLast };
|
||||||
});
|
});
|
||||||
const labelProc = padLeftSpComputed(this.core.processing, `⏳`);
|
const labelProc = padLeftSpComputed(this.services.fileProcessing.processing, `⏳`);
|
||||||
const labelPend = padLeftSpComputed(this.core.totalQueued, `🛫`);
|
const labelPend = padLeftSpComputed(this.services.fileProcessing.totalQueued, `🛫`);
|
||||||
const labelInBatchDelay = padLeftSpComputed(this.core.batched, `📬`);
|
const labelInBatchDelay = padLeftSpComputed(this.services.fileProcessing.batched, `📬`);
|
||||||
const waitingLabel = computed(() => {
|
const waitingLabel = computed(() => {
|
||||||
return `${labelProc()}${labelPend()}${labelInBatchDelay()}`;
|
return `${labelProc()}${labelPend()}${labelInBatchDelay()}`;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,356 +0,0 @@
|
|||||||
// import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser";
|
|
||||||
import { EVENT_REQUEST_RELOAD_SETTING_TAB, EVENT_SETTING_SAVED, eventHub } from "../../common/events.ts";
|
|
||||||
import {
|
|
||||||
type BucketSyncSetting,
|
|
||||||
ChunkAlgorithmNames,
|
|
||||||
type ConfigPassphraseStore,
|
|
||||||
type CouchDBConnection,
|
|
||||||
DEFAULT_SETTINGS,
|
|
||||||
type ObsidianLiveSyncSettings,
|
|
||||||
SALT_OF_PASSPHRASE,
|
|
||||||
SETTING_KEY_P2P_DEVICE_NAME,
|
|
||||||
} from "../../lib/src/common/types";
|
|
||||||
import { LOG_LEVEL_NOTICE, LOG_LEVEL_URGENT } from "octagonal-wheels/common/logger";
|
|
||||||
import { $msg, setLang } from "../../lib/src/common/i18n.ts";
|
|
||||||
import { isCloudantURI } from "../../lib/src/pouchdb/utils_couchdb.ts";
|
|
||||||
import { getLanguage } from "@/deps.ts";
|
|
||||||
import { SUPPORTED_I18N_LANGS, type I18N_LANGS } from "../../lib/src/common/rosetta.ts";
|
|
||||||
import { decryptString, encryptString } from "@/lib/src/encryption/stringEncryption.ts";
|
|
||||||
import type { LiveSyncCore } from "../../main.ts";
|
|
||||||
import { AbstractModule } from "../AbstractModule.ts";
|
|
||||||
export class ModuleObsidianSettings extends AbstractModule {
|
|
||||||
async _everyOnLayoutReady(): Promise<boolean> {
|
|
||||||
let isChanged = false;
|
|
||||||
if (this.settings.displayLanguage == "") {
|
|
||||||
const obsidianLanguage = getLanguage();
|
|
||||||
if (
|
|
||||||
SUPPORTED_I18N_LANGS.indexOf(obsidianLanguage) !== -1 && // Check if the language is supported
|
|
||||||
obsidianLanguage != this.settings.displayLanguage // Check if the language is different from the current setting
|
|
||||||
) {
|
|
||||||
// Check if the current setting is not empty (Means migrated or installed).
|
|
||||||
this.settings.displayLanguage = obsidianLanguage as I18N_LANGS;
|
|
||||||
isChanged = true;
|
|
||||||
setLang(this.settings.displayLanguage);
|
|
||||||
} else if (this.settings.displayLanguage == "") {
|
|
||||||
this.settings.displayLanguage = "def";
|
|
||||||
setLang(this.settings.displayLanguage);
|
|
||||||
await this.services.setting.saveSettingData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isChanged) {
|
|
||||||
const revert = $msg("dialog.yourLanguageAvailable.btnRevertToDefault");
|
|
||||||
if (
|
|
||||||
(await this.core.confirm.askSelectStringDialogue($msg(`dialog.yourLanguageAvailable`), ["OK", revert], {
|
|
||||||
defaultAction: "OK",
|
|
||||||
title: $msg(`dialog.yourLanguageAvailable.Title`),
|
|
||||||
})) == revert
|
|
||||||
) {
|
|
||||||
this.settings.displayLanguage = "def";
|
|
||||||
setLang(this.settings.displayLanguage);
|
|
||||||
}
|
|
||||||
await this.services.setting.saveSettingData();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
getPassphrase(settings: ObsidianLiveSyncSettings) {
|
|
||||||
const methods: Record<ConfigPassphraseStore, () => Promise<string | false>> = {
|
|
||||||
"": () => Promise.resolve("*"),
|
|
||||||
LOCALSTORAGE: () => Promise.resolve(localStorage.getItem("ls-setting-passphrase") ?? false),
|
|
||||||
ASK_AT_LAUNCH: () => this.core.confirm.askString("Passphrase", "passphrase", ""),
|
|
||||||
};
|
|
||||||
const method = settings.configPassphraseStore;
|
|
||||||
const methodFunc = method in methods ? methods[method] : methods[""];
|
|
||||||
return methodFunc();
|
|
||||||
}
|
|
||||||
|
|
||||||
_saveDeviceAndVaultName(): void {
|
|
||||||
const lsKey = "obsidian-live-sync-vaultanddevicename-" + this.services.vault.getVaultName();
|
|
||||||
localStorage.setItem(lsKey, this.services.setting.getDeviceAndVaultName() || "");
|
|
||||||
}
|
|
||||||
|
|
||||||
usedPassphrase = "";
|
|
||||||
private _clearUsedPassphrase(): void {
|
|
||||||
this.usedPassphrase = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
async decryptConfigurationItem(encrypted: string, passphrase: string) {
|
|
||||||
const dec = await decryptString(encrypted, passphrase + SALT_OF_PASSPHRASE);
|
|
||||||
if (dec) {
|
|
||||||
this.usedPassphrase = passphrase;
|
|
||||||
return dec;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async encryptConfigurationItem(src: string, settings: ObsidianLiveSyncSettings) {
|
|
||||||
if (this.usedPassphrase != "") {
|
|
||||||
return await encryptString(src, this.usedPassphrase + SALT_OF_PASSPHRASE);
|
|
||||||
}
|
|
||||||
|
|
||||||
const passphrase = await this.getPassphrase(settings);
|
|
||||||
if (passphrase === false) {
|
|
||||||
this._log(
|
|
||||||
"Failed to obtain passphrase when saving data.json! Please verify the configuration.",
|
|
||||||
LOG_LEVEL_URGENT
|
|
||||||
);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
const dec = await encryptString(src, passphrase + SALT_OF_PASSPHRASE);
|
|
||||||
if (dec) {
|
|
||||||
this.usedPassphrase = passphrase;
|
|
||||||
return dec;
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
get appId() {
|
|
||||||
return this.services.API.getAppID();
|
|
||||||
}
|
|
||||||
|
|
||||||
async _saveSettingData() {
|
|
||||||
this.services.setting.saveDeviceAndVaultName();
|
|
||||||
const settings = { ...this.settings };
|
|
||||||
settings.deviceAndVaultName = "";
|
|
||||||
if (settings.P2P_DevicePeerName && settings.P2P_DevicePeerName.trim() !== "") {
|
|
||||||
console.log("Saving device peer name to small config");
|
|
||||||
this.services.config.setSmallConfig(SETTING_KEY_P2P_DEVICE_NAME, settings.P2P_DevicePeerName.trim());
|
|
||||||
settings.P2P_DevicePeerName = "";
|
|
||||||
}
|
|
||||||
if (this.usedPassphrase == "" && !(await this.getPassphrase(settings))) {
|
|
||||||
this._log("Failed to retrieve passphrase. data.json contains unencrypted items!", LOG_LEVEL_NOTICE);
|
|
||||||
} else {
|
|
||||||
if (
|
|
||||||
settings.couchDB_PASSWORD != "" ||
|
|
||||||
settings.couchDB_URI != "" ||
|
|
||||||
settings.couchDB_USER != "" ||
|
|
||||||
settings.couchDB_DBNAME
|
|
||||||
) {
|
|
||||||
const connectionSetting: CouchDBConnection & BucketSyncSetting = {
|
|
||||||
couchDB_DBNAME: settings.couchDB_DBNAME,
|
|
||||||
couchDB_PASSWORD: settings.couchDB_PASSWORD,
|
|
||||||
couchDB_URI: settings.couchDB_URI,
|
|
||||||
couchDB_USER: settings.couchDB_USER,
|
|
||||||
accessKey: settings.accessKey,
|
|
||||||
bucket: settings.bucket,
|
|
||||||
endpoint: settings.endpoint,
|
|
||||||
region: settings.region,
|
|
||||||
secretKey: settings.secretKey,
|
|
||||||
useCustomRequestHandler: settings.useCustomRequestHandler,
|
|
||||||
bucketCustomHeaders: settings.bucketCustomHeaders,
|
|
||||||
couchDB_CustomHeaders: settings.couchDB_CustomHeaders,
|
|
||||||
useJWT: settings.useJWT,
|
|
||||||
jwtKey: settings.jwtKey,
|
|
||||||
jwtAlgorithm: settings.jwtAlgorithm,
|
|
||||||
jwtKid: settings.jwtKid,
|
|
||||||
jwtExpDuration: settings.jwtExpDuration,
|
|
||||||
jwtSub: settings.jwtSub,
|
|
||||||
useRequestAPI: settings.useRequestAPI,
|
|
||||||
bucketPrefix: settings.bucketPrefix,
|
|
||||||
forcePathStyle: settings.forcePathStyle,
|
|
||||||
};
|
|
||||||
settings.encryptedCouchDBConnection = await this.encryptConfigurationItem(
|
|
||||||
JSON.stringify(connectionSetting),
|
|
||||||
settings
|
|
||||||
);
|
|
||||||
settings.couchDB_PASSWORD = "";
|
|
||||||
settings.couchDB_DBNAME = "";
|
|
||||||
settings.couchDB_URI = "";
|
|
||||||
settings.couchDB_USER = "";
|
|
||||||
settings.accessKey = "";
|
|
||||||
settings.bucket = "";
|
|
||||||
settings.region = "";
|
|
||||||
settings.secretKey = "";
|
|
||||||
settings.endpoint = "";
|
|
||||||
}
|
|
||||||
if (settings.encrypt && settings.passphrase != "") {
|
|
||||||
settings.encryptedPassphrase = await this.encryptConfigurationItem(settings.passphrase, settings);
|
|
||||||
settings.passphrase = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await this.core.saveData(settings);
|
|
||||||
eventHub.emitEvent(EVENT_SETTING_SAVED, settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
tryDecodeJson(encoded: string | false): object | false {
|
|
||||||
try {
|
|
||||||
if (!encoded) return false;
|
|
||||||
return JSON.parse(encoded);
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
} else {
|
|
||||||
if (settings.encryptedCouchDBConnection) {
|
|
||||||
const keys = [
|
|
||||||
"couchDB_URI",
|
|
||||||
"couchDB_USER",
|
|
||||||
"couchDB_PASSWORD",
|
|
||||||
"couchDB_DBNAME",
|
|
||||||
"accessKey",
|
|
||||||
"bucket",
|
|
||||||
"endpoint",
|
|
||||||
"region",
|
|
||||||
"secretKey",
|
|
||||||
] as (keyof CouchDBConnection | keyof BucketSyncSetting)[];
|
|
||||||
const decrypted = this.tryDecodeJson(
|
|
||||||
await this.decryptConfigurationItem(settings.encryptedCouchDBConnection, passphrase)
|
|
||||||
) as CouchDBConnection & BucketSyncSetting;
|
|
||||||
if (decrypted) {
|
|
||||||
for (const key of keys) {
|
|
||||||
if (key in decrypted) {
|
|
||||||
//@ts-ignore
|
|
||||||
settings[key] = decrypted[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._log(
|
|
||||||
"Failed to decrypt passphrase from data.json! Ensure configuration is correct before syncing with remote.",
|
|
||||||
LOG_LEVEL_URGENT
|
|
||||||
);
|
|
||||||
for (const key of keys) {
|
|
||||||
//@ts-ignore
|
|
||||||
settings[key] = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (settings.encrypt && settings.encryptedPassphrase) {
|
|
||||||
const encrypted = settings.encryptedPassphrase;
|
|
||||||
const decrypted = await this.decryptConfigurationItem(encrypted, passphrase);
|
|
||||||
if (decrypted) {
|
|
||||||
settings.passphrase = decrypted;
|
|
||||||
} else {
|
|
||||||
this._log(
|
|
||||||
"Failed to decrypt passphrase from data.json! Ensure configuration is correct before syncing with remote.",
|
|
||||||
LOG_LEVEL_URGENT
|
|
||||||
);
|
|
||||||
settings.passphrase = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method mutates the settings object.
|
|
||||||
* @param settings
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
_adjustSettings(settings: ObsidianLiveSyncSettings): Promise<ObsidianLiveSyncSettings> {
|
|
||||||
// Adjust settings as needed
|
|
||||||
|
|
||||||
// Delete this feature to avoid problems on mobile.
|
|
||||||
settings.disableRequestURI = true;
|
|
||||||
|
|
||||||
// GC is disabled.
|
|
||||||
settings.gcDelay = 0;
|
|
||||||
// So, use history is always enabled.
|
|
||||||
settings.useHistory = true;
|
|
||||||
|
|
||||||
if ("workingEncrypt" in settings) delete settings.workingEncrypt;
|
|
||||||
if ("workingPassphrase" in settings) delete settings.workingPassphrase;
|
|
||||||
// Splitter configurations have been replaced with chunkSplitterVersion.
|
|
||||||
if (settings.chunkSplitterVersion == "") {
|
|
||||||
if (settings.enableChunkSplitterV2) {
|
|
||||||
if (settings.useSegmenter) {
|
|
||||||
settings.chunkSplitterVersion = "v2-segmenter";
|
|
||||||
} else {
|
|
||||||
settings.chunkSplitterVersion = "v2";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
settings.chunkSplitterVersion = "";
|
|
||||||
}
|
|
||||||
} else if (!(settings.chunkSplitterVersion in ChunkAlgorithmNames)) {
|
|
||||||
settings.chunkSplitterVersion = "";
|
|
||||||
}
|
|
||||||
return Promise.resolve(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _loadSettings(): Promise<void> {
|
|
||||||
const settings = Object.assign({}, DEFAULT_SETTINGS, await this.core.loadData()) as ObsidianLiveSyncSettings;
|
|
||||||
|
|
||||||
if (typeof settings.isConfigured == "undefined") {
|
|
||||||
// If migrated, mark true
|
|
||||||
if (JSON.stringify(settings) !== JSON.stringify(DEFAULT_SETTINGS)) {
|
|
||||||
settings.isConfigured = true;
|
|
||||||
} else {
|
|
||||||
settings.additionalSuffixOfDatabaseName = this.appId;
|
|
||||||
settings.isConfigured = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.settings = await this.services.setting.decryptSettings(settings);
|
|
||||||
|
|
||||||
setLang(this.settings.displayLanguage);
|
|
||||||
|
|
||||||
await this.services.setting.adjustSettings(this.settings);
|
|
||||||
|
|
||||||
const lsKey = "obsidian-live-sync-vaultanddevicename-" + this.services.vault.getVaultName();
|
|
||||||
if (this.settings.deviceAndVaultName != "") {
|
|
||||||
if (!localStorage.getItem(lsKey)) {
|
|
||||||
this.services.setting.setDeviceAndVaultName(this.settings.deviceAndVaultName);
|
|
||||||
this.services.setting.saveDeviceAndVaultName();
|
|
||||||
this.settings.deviceAndVaultName = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isCloudantURI(this.settings.couchDB_URI) && this.settings.customChunkSize != 0) {
|
|
||||||
this._log(
|
|
||||||
"Configuration issues detected and automatically resolved. However, unsynchronized data may exist. Consider rebuilding if necessary.",
|
|
||||||
LOG_LEVEL_NOTICE
|
|
||||||
);
|
|
||||||
this.settings.customChunkSize = 0;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this.core.ignoreFiles = this.settings.ignoreFiles.split(",").map(e => e.trim());
|
|
||||||
eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _currentSettings(): ObsidianLiveSyncSettings {
|
|
||||||
return this.settings;
|
|
||||||
}
|
|
||||||
private _updateSettings(updateFn: (settings: ObsidianLiveSyncSettings) => ObsidianLiveSyncSettings): Promise<void> {
|
|
||||||
try {
|
|
||||||
const updated = updateFn(this.settings);
|
|
||||||
this.settings = updated;
|
|
||||||
} catch (ex) {
|
|
||||||
this._log("Error in update function: " + ex, LOG_LEVEL_URGENT);
|
|
||||||
return Promise.reject(ex);
|
|
||||||
}
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
private _applyPartial(partial: Partial<ObsidianLiveSyncSettings>): Promise<void> {
|
|
||||||
try {
|
|
||||||
this.settings = { ...this.settings, ...partial };
|
|
||||||
} catch (ex) {
|
|
||||||
this._log("Error in applying partial settings: " + ex, LOG_LEVEL_URGENT);
|
|
||||||
return Promise.reject(ex);
|
|
||||||
}
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
|
||||||
super.onBindFunction(core, services);
|
|
||||||
services.appLifecycle.onLayoutReady.addHandler(this._everyOnLayoutReady.bind(this));
|
|
||||||
services.setting.clearUsedPassphrase.setHandler(this._clearUsedPassphrase.bind(this));
|
|
||||||
services.setting.decryptSettings.setHandler(this._decryptSettings.bind(this));
|
|
||||||
services.setting.adjustSettings.setHandler(this._adjustSettings.bind(this));
|
|
||||||
services.setting.loadSettings.setHandler(this._loadSettings.bind(this));
|
|
||||||
services.setting.currentSettings.setHandler(this._currentSettings.bind(this));
|
|
||||||
services.setting.updateSettings.setHandler(this._updateSettings.bind(this));
|
|
||||||
services.setting.applyPartial.setHandler(this._applyPartial.bind(this));
|
|
||||||
services.setting.saveDeviceAndVaultName.setHandler(this._saveDeviceAndVaultName.bind(this));
|
|
||||||
services.setting.saveSettingData.setHandler(this._saveSettingData.bind(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,8 +6,8 @@ import { DEFAULT_SETTINGS, type FilePathWithPrefix, type ObsidianLiveSyncSetting
|
|||||||
import { parseYaml, stringifyYaml } from "../../deps";
|
import { parseYaml, stringifyYaml } from "../../deps";
|
||||||
import { LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
import { LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
||||||
import { AbstractModule } from "../AbstractModule.ts";
|
import { AbstractModule } from "../AbstractModule.ts";
|
||||||
import type { ServiceContext } from "@/lib/src/services/base/ServiceBase.ts";
|
import type { ServiceContext } from "@lib/services/base/ServiceBase.ts";
|
||||||
import type { InjectableServiceHub } from "@/lib/src/services/InjectableServices.ts";
|
import type { InjectableServiceHub } from "@lib/services/InjectableServices.ts";
|
||||||
import type { LiveSyncCore } from "@/main.ts";
|
import type { LiveSyncCore } from "@/main.ts";
|
||||||
const SETTING_HEADER = "````yaml:livesync-setting\n";
|
const SETTING_HEADER = "````yaml:livesync-setting\n";
|
||||||
const SETTING_FOOTER = "\n````";
|
const SETTING_FOOTER = "\n````";
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
import { delay, isObjectDifferent, sizeToHumanReadable } from "../../../lib/src/common/utils.ts";
|
import { delay, isObjectDifferent, sizeToHumanReadable } from "../../../lib/src/common/utils.ts";
|
||||||
import { versionNumberString2Number } from "../../../lib/src/string_and_binary/convert.ts";
|
import { versionNumberString2Number } from "../../../lib/src/string_and_binary/convert.ts";
|
||||||
import { Logger } from "../../../lib/src/common/logger.ts";
|
import { Logger } from "../../../lib/src/common/logger.ts";
|
||||||
import { checkSyncInfo } from "@/lib/src/pouchdb/negotiation.ts";
|
import { checkSyncInfo } from "@lib/pouchdb/negotiation.ts";
|
||||||
import { testCrypt } from "octagonal-wheels/encryption/encryption";
|
import { testCrypt } from "octagonal-wheels/encryption/encryption";
|
||||||
import ObsidianLiveSyncPlugin from "../../../main.ts";
|
import ObsidianLiveSyncPlugin from "../../../main.ts";
|
||||||
import { scheduleTask } from "../../../common/utils.ts";
|
import { scheduleTask } from "../../../common/utils.ts";
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export function paneSyncSettings(
|
|||||||
if (!this.editingSettings.isConfigured) {
|
if (!this.editingSettings.isConfigured) {
|
||||||
this.editingSettings.isConfigured = true;
|
this.editingSettings.isConfigured = true;
|
||||||
await this.saveAllDirtySettings();
|
await this.saveAllDirtySettings();
|
||||||
await this.services.setting.realiseSetting();
|
await this.services.control.applySettings();
|
||||||
await this.rebuildDB("localOnly");
|
await this.rebuildDB("localOnly");
|
||||||
// this.resetEditingSettings();
|
// this.resetEditingSettings();
|
||||||
if (
|
if (
|
||||||
@@ -124,13 +124,13 @@ export function paneSyncSettings(
|
|||||||
await this.confirmRebuild();
|
await this.confirmRebuild();
|
||||||
} else {
|
} else {
|
||||||
await this.saveAllDirtySettings();
|
await this.saveAllDirtySettings();
|
||||||
await this.services.setting.realiseSetting();
|
await this.services.control.applySettings();
|
||||||
this.services.appLifecycle.askRestart();
|
this.services.appLifecycle.askRestart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await this.saveAllDirtySettings();
|
await this.saveAllDirtySettings();
|
||||||
await this.services.setting.realiseSetting();
|
await this.services.control.applySettings();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -169,7 +169,7 @@ export function paneSyncSettings(
|
|||||||
}
|
}
|
||||||
await this.saveSettings(["liveSync", "periodicReplication"]);
|
await this.saveSettings(["liveSync", "periodicReplication"]);
|
||||||
|
|
||||||
await this.services.setting.realiseSetting();
|
await this.services.control.applySettings();
|
||||||
});
|
});
|
||||||
|
|
||||||
new Setting(paneEl)
|
new Setting(paneEl)
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export class ModuleLiveSyncMain extends AbstractModule {
|
|||||||
}
|
}
|
||||||
if (!(await this.core.services.appLifecycle.onFirstInitialise())) return false;
|
if (!(await this.core.services.appLifecycle.onFirstInitialise())) return false;
|
||||||
// await this.core.$$realizeSettingSyncMode();
|
// await this.core.$$realizeSettingSyncMode();
|
||||||
await this.services.setting.realiseSetting();
|
await this.services.control.applySettings();
|
||||||
fireAndForget(async () => {
|
fireAndForget(async () => {
|
||||||
this._log($msg("moduleLiveSyncMain.logAdditionalSafetyScan"), LOG_LEVEL_VERBOSE);
|
this._log($msg("moduleLiveSyncMain.logAdditionalSafetyScan"), LOG_LEVEL_VERBOSE);
|
||||||
if (!(await this.services.appLifecycle.onScanningStartupIssues())) {
|
if (!(await this.services.appLifecycle.onScanningStartupIssues())) {
|
||||||
@@ -65,7 +65,7 @@ export class ModuleLiveSyncMain extends AbstractModule {
|
|||||||
eventHub.onEvent(EVENT_SETTING_SAVED, (settings: ObsidianLiveSyncSettings) => {
|
eventHub.onEvent(EVENT_SETTING_SAVED, (settings: ObsidianLiveSyncSettings) => {
|
||||||
fireAndForget(async () => {
|
fireAndForget(async () => {
|
||||||
try {
|
try {
|
||||||
await this.core.services.setting.realiseSetting();
|
await this.core.services.control.applySettings();
|
||||||
const lang = this.core.services.setting.currentSettings()?.displayLanguage ?? undefined;
|
const lang = this.core.services.setting.currentSettings()?.displayLanguage ?? undefined;
|
||||||
if (lang !== undefined) {
|
if (lang !== undefined) {
|
||||||
setLang(this.core.services.setting.currentSettings()?.displayLanguage);
|
setLang(this.core.services.setting.currentSettings()?.displayLanguage);
|
||||||
@@ -163,23 +163,19 @@ export class ModuleLiveSyncMain extends AbstractModule {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _realizeSettingSyncMode(): Promise<void> {
|
// private async _realizeSettingSyncMode(): Promise<void> {
|
||||||
await this.services.appLifecycle.onSuspending();
|
// await this.services.appLifecycle.onSuspending();
|
||||||
await this.services.setting.onBeforeRealiseSetting();
|
// await this.services.setting.onBeforeRealiseSetting();
|
||||||
this.localDatabase.refreshSettings();
|
// this.localDatabase.refreshSettings();
|
||||||
await this.services.fileProcessing.commitPendingFileEvents();
|
// await this.services.fileProcessing.commitPendingFileEvents();
|
||||||
await this.services.setting.onRealiseSetting();
|
// await this.services.setting.onRealiseSetting();
|
||||||
// disable all sync temporary.
|
// // disable all sync temporary.
|
||||||
if (this.services.appLifecycle.isSuspended()) return;
|
// if (this.services.appLifecycle.isSuspended()) return;
|
||||||
await this.services.appLifecycle.onResuming();
|
// await this.services.appLifecycle.onResuming();
|
||||||
await this.services.appLifecycle.onResumed();
|
// await this.services.appLifecycle.onResumed();
|
||||||
await this.services.setting.onSettingRealised();
|
// await this.services.setting.onSettingRealised();
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
_isReloadingScheduled(): boolean {
|
|
||||||
return this.core._totalProcessingCount !== undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
isReady = false;
|
isReady = false;
|
||||||
|
|
||||||
@@ -217,11 +213,9 @@ export class ModuleLiveSyncMain extends AbstractModule {
|
|||||||
services.appLifecycle.markIsReady.setHandler(this._markIsReady.bind(this));
|
services.appLifecycle.markIsReady.setHandler(this._markIsReady.bind(this));
|
||||||
services.appLifecycle.resetIsReady.setHandler(this._resetIsReady.bind(this));
|
services.appLifecycle.resetIsReady.setHandler(this._resetIsReady.bind(this));
|
||||||
services.appLifecycle.hasUnloaded.setHandler(this._isUnloaded.bind(this));
|
services.appLifecycle.hasUnloaded.setHandler(this._isUnloaded.bind(this));
|
||||||
services.appLifecycle.isReloadingScheduled.setHandler(this._isReloadingScheduled.bind(this));
|
|
||||||
services.appLifecycle.onReady.addHandler(this._onLiveSyncReady.bind(this));
|
services.appLifecycle.onReady.addHandler(this._onLiveSyncReady.bind(this));
|
||||||
services.appLifecycle.onWireUpEvents.addHandler(this._wireUpEvents.bind(this));
|
services.appLifecycle.onWireUpEvents.addHandler(this._wireUpEvents.bind(this));
|
||||||
services.appLifecycle.onLoad.addHandler(this._onLiveSyncLoad.bind(this));
|
services.appLifecycle.onLoad.addHandler(this._onLiveSyncLoad.bind(this));
|
||||||
services.appLifecycle.onAppUnload.addHandler(this._onLiveSyncUnload.bind(this));
|
services.appLifecycle.onAppUnload.addHandler(this._onLiveSyncUnload.bind(this));
|
||||||
services.setting.realiseSetting.setHandler(this._realizeSettingSyncMode.bind(this));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
import { InjectableAPIService } from "@/lib/src/services/implements/injectable/InjectableAPIService";
|
import { InjectableAPIService } from "@lib/services/implements/injectable/InjectableAPIService";
|
||||||
import type { ObsidianServiceContext } from "@/lib/src/services/implements/obsidian/ObsidianServiceContext";
|
import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext";
|
||||||
import { Platform, type Command, type ViewCreator } from "obsidian";
|
import { Platform, type Command, type ViewCreator } from "obsidian";
|
||||||
import { ObsHttpHandler } from "../essentialObsidian/APILib/ObsHttpHandler";
|
import { ObsHttpHandler } from "../essentialObsidian/APILib/ObsHttpHandler";
|
||||||
|
import { ObsidianConfirm } from "./ObsidianConfirm";
|
||||||
|
import type { Confirm } from "@lib/interfaces/Confirm";
|
||||||
|
|
||||||
// All Services will be migrated to be based on Plain Services, not Injectable Services.
|
// All Services will be migrated to be based on Plain Services, not Injectable Services.
|
||||||
// This is a migration step.
|
// This is a migration step.
|
||||||
|
|
||||||
export class ObsidianAPIService extends InjectableAPIService<ObsidianServiceContext> {
|
export class ObsidianAPIService extends InjectableAPIService<ObsidianServiceContext> {
|
||||||
_customHandler: ObsHttpHandler | undefined;
|
_customHandler: ObsHttpHandler | undefined;
|
||||||
|
_confirmInstance: Confirm;
|
||||||
|
constructor(context: ObsidianServiceContext) {
|
||||||
|
super(context);
|
||||||
|
this._confirmInstance = new ObsidianConfirm(context);
|
||||||
|
}
|
||||||
getCustomFetchHandler(): ObsHttpHandler {
|
getCustomFetchHandler(): ObsHttpHandler {
|
||||||
if (!this._customHandler) this._customHandler = new ObsHttpHandler(undefined, undefined);
|
if (!this._customHandler) this._customHandler = new ObsHttpHandler(undefined, undefined);
|
||||||
return this._customHandler;
|
return this._customHandler;
|
||||||
@@ -63,6 +70,11 @@ export class ObsidianAPIService extends InjectableAPIService<ObsidianServiceCont
|
|||||||
override getAppID(): string {
|
override getAppID(): string {
|
||||||
return `${"appId" in this.app ? this.app.appId : ""}`;
|
return `${"appId" in this.app ? this.app.appId : ""}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override getSystemVaultName(): string {
|
||||||
|
return this.app.vault.getName();
|
||||||
|
}
|
||||||
|
|
||||||
override getAppVersion(): string {
|
override getAppVersion(): string {
|
||||||
const navigatorString = globalThis.navigator?.userAgent ?? "";
|
const navigatorString = globalThis.navigator?.userAgent ?? "";
|
||||||
const match = navigatorString.match(/obsidian\/([0-9]+\.[0-9]+\.[0-9]+)/);
|
const match = navigatorString.match(/obsidian\/([0-9]+\.[0-9]+\.[0-9]+)/);
|
||||||
@@ -76,6 +88,10 @@ export class ObsidianAPIService extends InjectableAPIService<ObsidianServiceCont
|
|||||||
return this.context.plugin.manifest.version;
|
return this.context.plugin.manifest.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get confirm(): Confirm {
|
||||||
|
return this._confirmInstance;
|
||||||
|
}
|
||||||
|
|
||||||
addCommand<TCommand extends Command>(command: TCommand): TCommand {
|
addCommand<TCommand extends Command>(command: TCommand): TCommand {
|
||||||
return this.context.plugin.addCommand(command) as TCommand;
|
return this.context.plugin.addCommand(command) as TCommand;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { InjectableServiceHub } from "@/lib/src/services/implements/injectable/InjectableServiceHub";
|
import { InjectableServiceHub } from "@lib/services/implements/injectable/InjectableServiceHub";
|
||||||
import { ObsidianServiceContext } from "@/lib/src/services/implements/obsidian/ObsidianServiceContext";
|
import { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext";
|
||||||
import type { ServiceInstances } from "@/lib/src/services/ServiceHub";
|
import type { ServiceInstances } from "@lib/services/ServiceHub";
|
||||||
import type ObsidianLiveSyncPlugin from "@/main";
|
import type ObsidianLiveSyncPlugin from "@/main";
|
||||||
import {
|
import {
|
||||||
ObsidianConflictService,
|
ObsidianConflictService,
|
||||||
@@ -8,13 +8,14 @@ import {
|
|||||||
ObsidianReplicationService,
|
ObsidianReplicationService,
|
||||||
ObsidianReplicatorService,
|
ObsidianReplicatorService,
|
||||||
ObsidianRemoteService,
|
ObsidianRemoteService,
|
||||||
ObsidianSettingService,
|
|
||||||
ObsidianTweakValueService,
|
ObsidianTweakValueService,
|
||||||
ObsidianTestService,
|
ObsidianTestService,
|
||||||
ObsidianDatabaseEventService,
|
ObsidianDatabaseEventService,
|
||||||
ObsidianConfigService,
|
ObsidianConfigService,
|
||||||
ObsidianKeyValueDBService,
|
ObsidianKeyValueDBService,
|
||||||
|
ObsidianControlService,
|
||||||
} from "./ObsidianServices";
|
} from "./ObsidianServices";
|
||||||
|
import { ObsidianSettingService } from "./ObsidianSettingService";
|
||||||
import { ObsidianDatabaseService } from "./ObsidianDatabaseService";
|
import { ObsidianDatabaseService } from "./ObsidianDatabaseService";
|
||||||
import { ObsidianAPIService } from "./ObsidianAPIService";
|
import { ObsidianAPIService } from "./ObsidianAPIService";
|
||||||
import { ObsidianAppLifecycleService } from "./ObsidianAppLifecycleService";
|
import { ObsidianAppLifecycleService } from "./ObsidianAppLifecycleService";
|
||||||
@@ -35,10 +36,14 @@ export class ObsidianServiceHub extends InjectableServiceHub<ObsidianServiceCont
|
|||||||
const replication = new ObsidianReplicationService(context);
|
const replication = new ObsidianReplicationService(context);
|
||||||
|
|
||||||
const remote = new ObsidianRemoteService(context);
|
const remote = new ObsidianRemoteService(context);
|
||||||
const setting = new ObsidianSettingService(context);
|
|
||||||
const tweakValue = new ObsidianTweakValueService(context);
|
const tweakValue = new ObsidianTweakValueService(context);
|
||||||
|
|
||||||
|
const setting = new ObsidianSettingService(context, {
|
||||||
|
APIService: API,
|
||||||
|
});
|
||||||
const vault = new ObsidianVaultService(context, {
|
const vault = new ObsidianVaultService(context, {
|
||||||
settingService: setting,
|
settingService: setting,
|
||||||
|
APIService: API,
|
||||||
});
|
});
|
||||||
const test = new ObsidianTestService(context);
|
const test = new ObsidianTestService(context);
|
||||||
const databaseEvents = new ObsidianDatabaseEventService(context);
|
const databaseEvents = new ObsidianDatabaseEventService(context);
|
||||||
@@ -56,7 +61,6 @@ export class ObsidianServiceHub extends InjectableServiceHub<ObsidianServiceCont
|
|||||||
vault: vault,
|
vault: vault,
|
||||||
});
|
});
|
||||||
const config = new ObsidianConfigService(context, {
|
const config = new ObsidianConfigService(context, {
|
||||||
vaultService: vault,
|
|
||||||
settingService: setting,
|
settingService: setting,
|
||||||
APIService: API,
|
APIService: API,
|
||||||
});
|
});
|
||||||
@@ -69,6 +73,14 @@ export class ObsidianServiceHub extends InjectableServiceHub<ObsidianServiceCont
|
|||||||
appLifecycle,
|
appLifecycle,
|
||||||
config,
|
config,
|
||||||
replicator,
|
replicator,
|
||||||
|
APIService: API,
|
||||||
|
});
|
||||||
|
const control = new ObsidianControlService(context, {
|
||||||
|
appLifecycleService: appLifecycle,
|
||||||
|
databaseService: database,
|
||||||
|
fileProcessingService: fileProcessing,
|
||||||
|
settingService: setting,
|
||||||
|
APIService: API,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Using 'satisfies' to ensure all services are provided
|
// Using 'satisfies' to ensure all services are provided
|
||||||
@@ -90,6 +102,7 @@ export class ObsidianServiceHub extends InjectableServiceHub<ObsidianServiceCont
|
|||||||
API: API,
|
API: API,
|
||||||
config: config,
|
config: config,
|
||||||
keyValueDB: keyValueDB,
|
keyValueDB: keyValueDB,
|
||||||
|
control: control,
|
||||||
} satisfies Required<ServiceInstances<ObsidianServiceContext>>;
|
} satisfies Required<ServiceInstances<ObsidianServiceContext>>;
|
||||||
|
|
||||||
super(context, serviceInstancesToInit);
|
super(context, serviceInstancesToInit);
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import { InjectableFileProcessingService } from "@lib/services/implements/inject
|
|||||||
import { InjectableRemoteService } from "@lib/services/implements/injectable/InjectableRemoteService";
|
import { InjectableRemoteService } from "@lib/services/implements/injectable/InjectableRemoteService";
|
||||||
import { InjectableReplicationService } from "@lib/services/implements/injectable/InjectableReplicationService";
|
import { InjectableReplicationService } from "@lib/services/implements/injectable/InjectableReplicationService";
|
||||||
import { InjectableReplicatorService } from "@lib/services/implements/injectable/InjectableReplicatorService";
|
import { InjectableReplicatorService } from "@lib/services/implements/injectable/InjectableReplicatorService";
|
||||||
import { InjectableSettingService } from "@lib/services/implements/injectable/InjectableSettingService";
|
|
||||||
import { InjectableTestService } from "@lib/services/implements/injectable/InjectableTestService";
|
import { InjectableTestService } from "@lib/services/implements/injectable/InjectableTestService";
|
||||||
import { InjectableTweakValueService } from "@lib/services/implements/injectable/InjectableTweakValueService";
|
import { InjectableTweakValueService } from "@lib/services/implements/injectable/InjectableTweakValueService";
|
||||||
import { ConfigServiceBrowserCompat } from "@lib/services/implements/browser/ConfigServiceBrowserCompat";
|
import { ConfigServiceBrowserCompat } from "@lib/services/implements/browser/ConfigServiceBrowserCompat";
|
||||||
import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext.ts";
|
import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext.ts";
|
||||||
import { KeyValueDBService } from "@/lib/src/services/base/KeyValueDBService";
|
import { KeyValueDBService } from "@lib/services/base/KeyValueDBService";
|
||||||
|
import { ControlService } from "@lib/services/base/ControlService";
|
||||||
|
|
||||||
export class ObsidianDatabaseEventService extends InjectableDatabaseEventService<ObsidianServiceContext> {}
|
export class ObsidianDatabaseEventService extends InjectableDatabaseEventService<ObsidianServiceContext> {}
|
||||||
|
|
||||||
@@ -23,8 +23,6 @@ export class ObsidianReplicationService extends InjectableReplicationService<Obs
|
|||||||
export class ObsidianRemoteService extends InjectableRemoteService<ObsidianServiceContext> {}
|
export class ObsidianRemoteService extends InjectableRemoteService<ObsidianServiceContext> {}
|
||||||
// InjectableConflictService
|
// InjectableConflictService
|
||||||
export class ObsidianConflictService extends InjectableConflictService<ObsidianServiceContext> {}
|
export class ObsidianConflictService extends InjectableConflictService<ObsidianServiceContext> {}
|
||||||
// InjectableSettingService
|
|
||||||
export class ObsidianSettingService extends InjectableSettingService<ObsidianServiceContext> {}
|
|
||||||
// InjectableTweakValueService
|
// InjectableTweakValueService
|
||||||
export class ObsidianTweakValueService extends InjectableTweakValueService<ObsidianServiceContext> {}
|
export class ObsidianTweakValueService extends InjectableTweakValueService<ObsidianServiceContext> {}
|
||||||
// InjectableTestService
|
// InjectableTestService
|
||||||
@@ -32,3 +30,5 @@ export class ObsidianTestService extends InjectableTestService<ObsidianServiceCo
|
|||||||
export class ObsidianConfigService extends ConfigServiceBrowserCompat<ObsidianServiceContext> {}
|
export class ObsidianConfigService extends ConfigServiceBrowserCompat<ObsidianServiceContext> {}
|
||||||
|
|
||||||
export class ObsidianKeyValueDBService extends KeyValueDBService<ObsidianServiceContext> {}
|
export class ObsidianKeyValueDBService extends KeyValueDBService<ObsidianServiceContext> {}
|
||||||
|
|
||||||
|
export class ObsidianControlService extends ControlService<ObsidianServiceContext> {}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { type ObsidianLiveSyncSettings } from "@lib/common/types";
|
||||||
|
import { EVENT_REQUEST_RELOAD_SETTING_TAB, EVENT_SETTING_SAVED } from "@lib/events/coreEvents";
|
||||||
|
import { eventHub } from "@lib/hub/hub";
|
||||||
|
import { SettingService, type SettingServiceDependencies } from "@lib/services/base/SettingService";
|
||||||
|
import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext";
|
||||||
|
|
||||||
|
export class ObsidianSettingService<T extends ObsidianServiceContext> extends SettingService<T> {
|
||||||
|
constructor(context: T, dependencies: SettingServiceDependencies) {
|
||||||
|
super(context, dependencies);
|
||||||
|
this.onSettingSaved.addHandler((settings) => {
|
||||||
|
eventHub.emitEvent(EVENT_SETTING_SAVED, settings);
|
||||||
|
return Promise.resolve(true);
|
||||||
|
});
|
||||||
|
this.onSettingLoaded.addHandler((settings) => {
|
||||||
|
eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB);
|
||||||
|
return Promise.resolve(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
protected setItem(key: string, value: string) {
|
||||||
|
return localStorage.setItem(key, value);
|
||||||
|
}
|
||||||
|
protected getItem(key: string): string {
|
||||||
|
return localStorage.getItem(key) ?? "";
|
||||||
|
}
|
||||||
|
protected deleteItem(key: string): void {
|
||||||
|
localStorage.removeItem(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async saveData(data: ObsidianLiveSyncSettings): Promise<void> {
|
||||||
|
return await this.context.liveSyncPlugin.saveData(data);
|
||||||
|
}
|
||||||
|
protected override async loadData(): Promise<ObsidianLiveSyncSettings | undefined> {
|
||||||
|
return await this.context.liveSyncPlugin.loadData();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,14 +2,15 @@ import type { ConfigService } from "@lib/services/base/ConfigService";
|
|||||||
import type { AppLifecycleService } from "@lib/services/base/AppLifecycleService";
|
import type { AppLifecycleService } from "@lib/services/base/AppLifecycleService";
|
||||||
import type { ReplicatorService } from "@lib/services/base/ReplicatorService";
|
import type { ReplicatorService } from "@lib/services/base/ReplicatorService";
|
||||||
import { UIService } from "@lib/services//implements/base/UIService";
|
import { UIService } from "@lib/services//implements/base/UIService";
|
||||||
import { ObsidianServiceContext } from "@/lib/src/services/implements/obsidian/ObsidianServiceContext";
|
import { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext";
|
||||||
import { ObsidianSvelteDialogManager } from "./SvelteDialogObsidian";
|
import { ObsidianSvelteDialogManager } from "./SvelteDialogObsidian";
|
||||||
import { ObsidianConfirm } from "./ObsidianConfirm";
|
import DialogToCopy from "@lib/UI/dialogues/DialogueToCopy.svelte";
|
||||||
import DialogToCopy from "@/lib/src/UI/dialogues/DialogueToCopy.svelte";
|
import type { IAPIService } from "@lib/services/base/IService";
|
||||||
export type ObsidianUIServiceDependencies<T extends ObsidianServiceContext = ObsidianServiceContext> = {
|
export type ObsidianUIServiceDependencies<T extends ObsidianServiceContext = ObsidianServiceContext> = {
|
||||||
appLifecycle: AppLifecycleService<T>;
|
appLifecycle: AppLifecycleService<T>;
|
||||||
config: ConfigService<T>;
|
config: ConfigService<T>;
|
||||||
replicator: ReplicatorService<T>;
|
replicator: ReplicatorService<T>;
|
||||||
|
APIService: IAPIService;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class ObsidianUIService extends UIService<ObsidianServiceContext> {
|
export class ObsidianUIService extends UIService<ObsidianServiceContext> {
|
||||||
@@ -17,7 +18,7 @@ export class ObsidianUIService extends UIService<ObsidianServiceContext> {
|
|||||||
return DialogToCopy;
|
return DialogToCopy;
|
||||||
}
|
}
|
||||||
constructor(context: ObsidianServiceContext, dependents: ObsidianUIServiceDependencies<ObsidianServiceContext>) {
|
constructor(context: ObsidianServiceContext, dependents: ObsidianUIServiceDependencies<ObsidianServiceContext>) {
|
||||||
const obsidianConfirm = new ObsidianConfirm(context);
|
const obsidianConfirm = dependents.APIService.confirm;
|
||||||
const obsidianSvelteDialogManager = new ObsidianSvelteDialogManager<ObsidianServiceContext>(context, {
|
const obsidianSvelteDialogManager = new ObsidianSvelteDialogManager<ObsidianServiceContext>(context, {
|
||||||
appLifecycle: dependents.appLifecycle,
|
appLifecycle: dependents.appLifecycle,
|
||||||
config: dependents.config,
|
config: dependents.config,
|
||||||
@@ -27,7 +28,7 @@ export class ObsidianUIService extends UIService<ObsidianServiceContext> {
|
|||||||
super(context, {
|
super(context, {
|
||||||
appLifecycle: dependents.appLifecycle,
|
appLifecycle: dependents.appLifecycle,
|
||||||
dialogManager: obsidianSvelteDialogManager,
|
dialogManager: obsidianSvelteDialogManager,
|
||||||
confirm: obsidianConfirm,
|
APIService: dependents.APIService,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { enableI18nFeature } from "./onLayoutReady/enablei18n";
|
||||||
|
|
||||||
|
export const onLayoutReadyFeatures = [enableI18nFeature];
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { getLanguage } from "@/deps";
|
||||||
|
import { createServiceFeature } from "../types.ts";
|
||||||
|
import { SUPPORTED_I18N_LANGS, type I18N_LANGS } from "@lib/common/rosetta";
|
||||||
|
import { $msg, setLang } from "@lib/common/i18n";
|
||||||
|
|
||||||
|
export const enableI18nFeature = createServiceFeature(async ({ services: { setting, API } }) => {
|
||||||
|
let isChanged = false;
|
||||||
|
const settings = setting.currentSettings();
|
||||||
|
if (settings.displayLanguage == "") {
|
||||||
|
const obsidianLanguage = getLanguage();
|
||||||
|
if (
|
||||||
|
SUPPORTED_I18N_LANGS.indexOf(obsidianLanguage) !== -1 && // Check if the language is supported
|
||||||
|
obsidianLanguage != settings.displayLanguage // Check if the language is different from the current setting
|
||||||
|
) {
|
||||||
|
// Check if the current setting is not empty (Means migrated or installed).
|
||||||
|
// settings.displayLanguage = obsidianLanguage as I18N_LANGS;
|
||||||
|
await setting.applyPartial({ displayLanguage: obsidianLanguage as I18N_LANGS });
|
||||||
|
isChanged = true;
|
||||||
|
setLang(settings.displayLanguage);
|
||||||
|
} else if (settings.displayLanguage == "") {
|
||||||
|
// settings.displayLanguage = "def";
|
||||||
|
await setting.applyPartial({ displayLanguage: "def" });
|
||||||
|
setLang(settings.displayLanguage);
|
||||||
|
await setting.saveSettingData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isChanged) {
|
||||||
|
const revert = $msg("dialog.yourLanguageAvailable.btnRevertToDefault");
|
||||||
|
if (
|
||||||
|
(await API.confirm.askSelectStringDialogue($msg(`dialog.yourLanguageAvailable`), ["OK", revert], {
|
||||||
|
defaultAction: "OK",
|
||||||
|
title: $msg(`dialog.yourLanguageAvailable.Title`),
|
||||||
|
})) == revert
|
||||||
|
) {
|
||||||
|
await setting.applyPartial({ displayLanguage: "def" });
|
||||||
|
setLang(settings.displayLanguage);
|
||||||
|
}
|
||||||
|
await setting.saveSettingData();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import type { IServiceHub } from "@lib/services/base/IService";
|
||||||
|
import type { DatabaseFileAccess } from "@lib/interfaces/DatabaseFileAccess";
|
||||||
|
import type { Rebuilder } from "@lib/interfaces/DatabaseRebuilder";
|
||||||
|
import type { IFileHandler } from "@lib/interfaces/FileHandler";
|
||||||
|
import type { StorageAccess } from "@lib/interfaces/StorageAccess";
|
||||||
|
|
||||||
|
export interface ServiceModules {
|
||||||
|
storageAccess: StorageAccess;
|
||||||
|
/**
|
||||||
|
* Database File Accessor for handling file operations related to the database, such as exporting the database, importing from a file, etc.
|
||||||
|
*/
|
||||||
|
databaseFileAccess: DatabaseFileAccess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File Handler for handling file operations related to replication, such as resolving conflicts, applying changes from replication, etc.
|
||||||
|
*/
|
||||||
|
fileHandler: IFileHandler;
|
||||||
|
/**
|
||||||
|
* Rebuilder for handling database rebuilding operations.
|
||||||
|
*/
|
||||||
|
rebuilder: Rebuilder;
|
||||||
|
}
|
||||||
|
export type RequiredServices<T extends keyof IServiceHub> = Pick<IServiceHub, T>;
|
||||||
|
export type RequiredServiceModules<T extends keyof ServiceModules> = Pick<ServiceModules, T>;
|
||||||
|
|
||||||
|
export type NecessaryServices<T extends keyof IServiceHub, U extends keyof ServiceModules> = {
|
||||||
|
services: RequiredServices<T>;
|
||||||
|
serviceModules: RequiredServiceModules<U>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ServiceFeatureFunction<T extends keyof IServiceHub, U extends keyof ServiceModules, TR> = (
|
||||||
|
host: NecessaryServices<T, U>
|
||||||
|
) => TR;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to create a service feature with proper typing.
|
||||||
|
* @param featureFunction The feature function to be wrapped.
|
||||||
|
* @returns The same feature function with proper typing.
|
||||||
|
* @example
|
||||||
|
* const myFeatureDef = createServiceFeature(({ services: { API }, serviceModules: { storageAccess } }) => {
|
||||||
|
* // ...
|
||||||
|
* });
|
||||||
|
* const myFeature = myFeatureDef.bind(null, this); // <- `this` may `ObsidianLiveSyncPlugin` or a custom context object
|
||||||
|
* appLifecycle.onLayoutReady(myFeature);
|
||||||
|
*/
|
||||||
|
export function createServiceFeature<T extends keyof IServiceHub, U extends keyof ServiceModules, TR>(
|
||||||
|
featureFunction: ServiceFeatureFunction<T, U, TR>
|
||||||
|
): ServiceFeatureFunction<T, U, TR> {
|
||||||
|
return featureFunction;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import type { DatabaseFileAccess } from "@/lib/src/interfaces/DatabaseFileAccess";
|
||||||
|
import type { Rebuilder } from "@/lib/src/interfaces/DatabaseRebuilder";
|
||||||
|
import type { IFileHandler } from "@/lib/src/interfaces/FileHandler";
|
||||||
|
import type { StorageAccess } from "@/lib/src/interfaces/StorageAccess";
|
||||||
|
import type { IServiceHub } from "./lib/src/services/base/IService";
|
||||||
|
|
||||||
|
export interface ServiceModules {
|
||||||
|
storageAccess: StorageAccess;
|
||||||
|
/**
|
||||||
|
* Database File Accessor for handling file operations related to the database, such as exporting the database, importing from a file, etc.
|
||||||
|
*/
|
||||||
|
databaseFileAccess: DatabaseFileAccess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File Handler for handling file operations related to replication, such as resolving conflicts, applying changes from replication, etc.
|
||||||
|
*/
|
||||||
|
fileHandler: IFileHandler;
|
||||||
|
/**
|
||||||
|
* Rebuilder for handling database rebuilding operations.
|
||||||
|
*/
|
||||||
|
rebuilder: Rebuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LiveSyncHost {
|
||||||
|
services: IServiceHub;
|
||||||
|
serviceModules: ServiceModules;
|
||||||
|
}
|
||||||
@@ -122,13 +122,11 @@ export async function waitForIdle(harness: LiveSyncHarness): Promise<void> {
|
|||||||
for (let i = 0; i < 20; i++) {
|
for (let i = 0; i < 20; i++) {
|
||||||
await delay(25);
|
await delay(25);
|
||||||
const processing =
|
const processing =
|
||||||
harness.plugin.databaseQueueCount.value +
|
harness.plugin.services.replication.databaseQueueCount.value +
|
||||||
harness.plugin.processingFileEventCount.value +
|
harness.plugin.services.fileProcessing.totalQueued.value +
|
||||||
harness.plugin.pendingFileEventCount.value +
|
harness.plugin.services.fileProcessing.batched.value +
|
||||||
harness.plugin.totalQueued.value +
|
harness.plugin.services.fileProcessing.processing.value +
|
||||||
harness.plugin.batched.value +
|
harness.plugin.services.replication.storageApplyingCount.value;
|
||||||
harness.plugin.processing.value +
|
|
||||||
harness.plugin.storageApplyingCount.value;
|
|
||||||
|
|
||||||
if (processing === 0) {
|
if (processing === 0) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
|
|||||||
+19
-2
@@ -5,11 +5,28 @@ The head note of 0.25 is now in [updates_old.md](https://github.com/vrtmrz/obsid
|
|||||||
|
|
||||||
## 0.25.43-patched-7
|
## 0.25.43-patched-7
|
||||||
|
|
||||||
-- Unreleased --
|
19th February, 2026
|
||||||
|
|
||||||
|
Right then, let us make a decision already.
|
||||||
|
|
||||||
|
Last time, since I found a bug, I ended up doing a few other things as well, but next time I intend to release it with just the bug fix. It is quite substantial, after all.
|
||||||
|
|
||||||
|
Customisation Sync has mostly been verified. Hidden file synchronisation has not been done yet.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed an issue where the StorageEventManager was not correctly the loading the settings.
|
- Fixed an issue where the StorageEventManager was not correctly loading the settings.
|
||||||
|
- Replication statistics are now correctly reset after switching replicators.
|
||||||
|
|
||||||
|
### Refactored
|
||||||
|
|
||||||
|
- Now, many reactive values which keep the state or statistics of the plugin are moved to the services which have the responsibility for these states.
|
||||||
|
- `serviceFeatures` are now able to be added to the services; this is not a class module, but a function which accepts dependencies and returns an addHandler-able function. This is for better separation of concerns, better maintainability, and testability.
|
||||||
|
- `control` service; is a meta-service which is responsible for orchestrating services has been added.
|
||||||
|
- Don't you think stopping replication or something occurs during `settingService.realiseSetting` is quite weird? It may be done by the control service, which can orchestrate the setting service and the replicator service.
|
||||||
|
-
|
||||||
|
- Some functions on services have been moved. e.g., `getSystemVaultName` is now on the API service.
|
||||||
|
- Setting Service is now responsible for the setting, no longer using dynamic binding for the modules.
|
||||||
|
|
||||||
## 0.25.43-patched-6
|
## 0.25.43-patched-6
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user