mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-07-01 10:25:19 +00:00
Refactor: Migrate the outdated, unstable platform abstraction layer to Services
This commit is contained in:
Generated
+7
-7
@@ -19,7 +19,7 @@
|
|||||||
"fflate": "^0.8.2",
|
"fflate": "^0.8.2",
|
||||||
"idb": "^8.0.3",
|
"idb": "^8.0.3",
|
||||||
"minimatch": "^10.0.2",
|
"minimatch": "^10.0.2",
|
||||||
"octagonal-wheels": "^0.1.44",
|
"octagonal-wheels": "^0.1.45",
|
||||||
"qrcode-generator": "^1.4.4",
|
"qrcode-generator": "^1.4.4",
|
||||||
"trystero": "^0.22.0",
|
"trystero": "^0.22.0",
|
||||||
"xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2"
|
"xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2"
|
||||||
@@ -11151,9 +11151,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/octagonal-wheels": {
|
"node_modules/octagonal-wheels": {
|
||||||
"version": "0.1.44",
|
"version": "0.1.45",
|
||||||
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.44.tgz",
|
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.45.tgz",
|
||||||
"integrity": "sha512-sUn/dkYQ2AbMB0R8CubVd75BjkcsteW9B14ArO99F6wM5JRwOo/yPIBBoxCUFE7JjBFOfuWG21C9E3NTga6XrA==",
|
"integrity": "sha512-gXoCrwoUIXhmu57YN4BxAtBe+JaYNJNaXaZuVjqjopwYKpH5p2mn1om6KjA22rgGPiIJFXkse2U28FFXoT3/0Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"idb": "^8.0.3"
|
"idb": "^8.0.3"
|
||||||
@@ -23036,9 +23036,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"octagonal-wheels": {
|
"octagonal-wheels": {
|
||||||
"version": "0.1.44",
|
"version": "0.1.45",
|
||||||
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.44.tgz",
|
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.45.tgz",
|
||||||
"integrity": "sha512-sUn/dkYQ2AbMB0R8CubVd75BjkcsteW9B14ArO99F6wM5JRwOo/yPIBBoxCUFE7JjBFOfuWG21C9E3NTga6XrA==",
|
"integrity": "sha512-gXoCrwoUIXhmu57YN4BxAtBe+JaYNJNaXaZuVjqjopwYKpH5p2mn1om6KjA22rgGPiIJFXkse2U28FFXoT3/0Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"idb": "^8.0.3"
|
"idb": "^8.0.3"
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -129,7 +129,7 @@
|
|||||||
"fflate": "^0.8.2",
|
"fflate": "^0.8.2",
|
||||||
"idb": "^8.0.3",
|
"idb": "^8.0.3",
|
||||||
"minimatch": "^10.0.2",
|
"minimatch": "^10.0.2",
|
||||||
"octagonal-wheels": "^0.1.44",
|
"octagonal-wheels": "^0.1.45",
|
||||||
"qrcode-generator": "^1.4.4",
|
"qrcode-generator": "^1.4.4",
|
||||||
"trystero": "^0.22.0",
|
"trystero": "^0.22.0",
|
||||||
"xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2"
|
"xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2"
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import { reactiveSource } from "octagonal-wheels/dataobject/reactive_v2";
|
|||||||
import type { Confirm } from "../../lib/src/interfaces/Confirm.ts";
|
import type { Confirm } from "../../lib/src/interfaces/Confirm.ts";
|
||||||
import type ObsidianLiveSyncPlugin from "../../main.ts";
|
import type ObsidianLiveSyncPlugin from "../../main.ts";
|
||||||
import type { SimpleStore } from "octagonal-wheels/databases/SimpleStoreBase";
|
import type { SimpleStore } from "octagonal-wheels/databases/SimpleStoreBase";
|
||||||
import { getPlatformName } from "../../lib/src/PlatformAPIs/obsidian/Environment.ts";
|
// import { getPlatformName } from "../../lib/src/PlatformAPIs/obsidian/Environment.ts";
|
||||||
import type { LiveSyncCore } from "../../main.ts";
|
import type { LiveSyncCore } from "../../main.ts";
|
||||||
import { TrysteroReplicator } from "../../lib/src/replication/trystero/TrysteroReplicator.ts";
|
import { TrysteroReplicator } from "../../lib/src/replication/trystero/TrysteroReplicator.ts";
|
||||||
import { SETTING_KEY_P2P_DEVICE_NAME } from "../../lib/src/common/types.ts";
|
import { SETTING_KEY_P2P_DEVICE_NAME } from "../../lib/src/common/types.ts";
|
||||||
@@ -130,7 +130,7 @@ export class P2PReplicator extends LiveSyncCommands implements P2PReplicatorBase
|
|||||||
const getDB = () => this.getDB();
|
const getDB = () => this.getDB();
|
||||||
|
|
||||||
const getConfirm = () => this.confirm;
|
const getConfirm = () => this.confirm;
|
||||||
const getPlatform = () => this.getPlatform();
|
const getPlatform = () => this.services.API.getPlatform();
|
||||||
const env = {
|
const env = {
|
||||||
get db() {
|
get db() {
|
||||||
return getDB();
|
return getDB();
|
||||||
@@ -166,9 +166,6 @@ export class P2PReplicator extends LiveSyncCommands implements P2PReplicatorBase
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getPlatform(): string {
|
|
||||||
return getPlatformName();
|
|
||||||
}
|
|
||||||
|
|
||||||
onunload(): void {
|
onunload(): void {
|
||||||
removeP2PReplicatorInstance();
|
removeP2PReplicatorInstance();
|
||||||
|
|||||||
+1
-1
Submodule src/lib updated: cd32d3d326...7c275d50ae
+4
-3
@@ -67,9 +67,10 @@ import { ModuleExtraSyncObsidian } from "./modules/extraFeaturesObsidian/ModuleE
|
|||||||
import { LocalDatabaseMaintenance } from "./features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts";
|
import { LocalDatabaseMaintenance } from "./features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts";
|
||||||
import { P2PReplicator } from "./features/P2PSync/CmdP2PReplicator.ts";
|
import { P2PReplicator } from "./features/P2PSync/CmdP2PReplicator.ts";
|
||||||
import type { LiveSyncManagers } from "./lib/src/managers/LiveSyncManagers.ts";
|
import type { LiveSyncManagers } from "./lib/src/managers/LiveSyncManagers.ts";
|
||||||
import { ObsidianServiceHub } from "./modules/services/ObsidianServices.ts";
|
import type { InjectableServiceHub } from "./lib/src/services/implements/injectable/InjectableServiceHub.ts";
|
||||||
import type { InjectableServiceHub } from "./lib/src/services/InjectableServices.ts";
|
import { ObsidianServiceHub } from "./modules/services/ObsidianServiceHub.ts";
|
||||||
import type { ServiceContext } from "./lib/src/services/ServiceHub.ts";
|
import type { ServiceContext } from "./lib/src/services/base/ServiceBase.ts";
|
||||||
|
// import type { InjectableServiceHub } from "./lib/src/services/InjectableServices.ts";
|
||||||
|
|
||||||
export default class ObsidianLiveSyncPlugin
|
export default class ObsidianLiveSyncPlugin
|
||||||
extends Plugin
|
extends Plugin
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ import {
|
|||||||
} from "../../lib/src/common/types.ts";
|
} from "../../lib/src/common/types.ts";
|
||||||
import { AbstractModule } from "../AbstractModule.ts";
|
import { AbstractModule } from "../AbstractModule.ts";
|
||||||
import type { LiveSyncCore } from "../../main.ts";
|
import type { LiveSyncCore } from "../../main.ts";
|
||||||
import { SvelteDialogManager } from "../features/SetupWizard/ObsidianSvelteDialog.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 type { ServiceContext } from "@/lib/src/services/base/ServiceBase.ts";
|
||||||
|
|
||||||
export class ModuleRedFlag extends AbstractModule {
|
export class ModuleRedFlag extends AbstractModule {
|
||||||
async isFlagFileExist(path: string) {
|
async isFlagFileExist(path: string) {
|
||||||
@@ -52,7 +53,10 @@ export class ModuleRedFlag extends AbstractModule {
|
|||||||
await this.deleteFlagFile(FlagFilesOriginal.FETCH_ALL);
|
await this.deleteFlagFile(FlagFilesOriginal.FETCH_ALL);
|
||||||
await this.deleteFlagFile(FlagFilesHumanReadable.FETCH_ALL);
|
await this.deleteFlagFile(FlagFilesHumanReadable.FETCH_ALL);
|
||||||
}
|
}
|
||||||
dialogManager = new SvelteDialogManager(this.core);
|
// dialogManager = new SvelteDialogManagerBase(this.core);
|
||||||
|
get dialogManager(): SvelteDialogManagerBase<ServiceContext> {
|
||||||
|
return this.core.services.UI.dialogManager;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adjust setting to remote if needed.
|
* Adjust setting to remote if needed.
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/log
|
|||||||
import { AbstractModule } from "../AbstractModule.ts";
|
import { AbstractModule } from "../AbstractModule.ts";
|
||||||
import type { LiveSyncCore } from "../../main.ts";
|
import type { LiveSyncCore } from "../../main.ts";
|
||||||
import type { SimpleStore } from "octagonal-wheels/databases/SimpleStoreBase";
|
import type { SimpleStore } from "octagonal-wheels/databases/SimpleStoreBase";
|
||||||
|
import type { InjectableServiceHub } from "@/lib/src/services/InjectableServices.ts";
|
||||||
|
import type { ObsidianDatabaseService } from "../services/ObsidianServices.ts";
|
||||||
|
|
||||||
export class ModuleKeyValueDB extends AbstractModule {
|
export class ModuleKeyValueDB extends AbstractModule {
|
||||||
async tryCloseKvDB() {
|
async tryCloseKvDB() {
|
||||||
@@ -77,6 +79,7 @@ export class ModuleKeyValueDB extends AbstractModule {
|
|||||||
.filter((e) => e.startsWith(prefix))
|
.filter((e) => e.startsWith(prefix))
|
||||||
.map((e) => e.substring(prefix.length));
|
.map((e) => e.substring(prefix.length));
|
||||||
},
|
},
|
||||||
|
db: Promise.resolve(getDB()),
|
||||||
} satisfies SimpleStore<T>;
|
} satisfies SimpleStore<T>;
|
||||||
}
|
}
|
||||||
_everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise<boolean> {
|
_everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise<boolean> {
|
||||||
@@ -100,12 +103,12 @@ export class ModuleKeyValueDB extends AbstractModule {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
onBindFunction(core: LiveSyncCore, services: InjectableServiceHub): void {
|
||||||
services.databaseEvents.onUnloadDatabase.addHandler(this._onDBUnload.bind(this));
|
services.databaseEvents.onUnloadDatabase.addHandler(this._onDBUnload.bind(this));
|
||||||
services.databaseEvents.onCloseDatabase.addHandler(this._onDBClose.bind(this));
|
services.databaseEvents.onCloseDatabase.addHandler(this._onDBClose.bind(this));
|
||||||
services.databaseEvents.onDatabaseInitialisation.addHandler(this._everyOnInitializeDatabase.bind(this));
|
services.databaseEvents.onDatabaseInitialisation.addHandler(this._everyOnInitializeDatabase.bind(this));
|
||||||
services.databaseEvents.onResetDatabase.addHandler(this._everyOnResetDatabase.bind(this));
|
services.databaseEvents.onResetDatabase.addHandler(this._everyOnResetDatabase.bind(this));
|
||||||
services.database.openSimpleStore.setHandler(this._getSimpleStore.bind(this));
|
(services.database as ObsidianDatabaseService).openSimpleStore.setHandler(this._getSimpleStore.bind(this));
|
||||||
services.appLifecycle.onSettingLoaded.addHandler(this._everyOnloadAfterLoadSettings.bind(this));
|
services.appLifecycle.onSettingLoaded.addHandler(this._everyOnloadAfterLoadSettings.bind(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
} from "../../lib/src/common/types.ts";
|
} from "../../lib/src/common/types.ts";
|
||||||
import { generatePatchObj, isObjectDifferent } from "../../lib/src/common/utils.ts";
|
import { generatePatchObj, isObjectDifferent } from "../../lib/src/common/utils.ts";
|
||||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||||
import { SvelteDialogManager } from "./SetupWizard/ObsidianSvelteDialog.ts";
|
|
||||||
import Intro from "./SetupWizard/dialogs/Intro.svelte";
|
import Intro from "./SetupWizard/dialogs/Intro.svelte";
|
||||||
import SelectMethodNewUser from "./SetupWizard/dialogs/SelectMethodNewUser.svelte";
|
import SelectMethodNewUser from "./SetupWizard/dialogs/SelectMethodNewUser.svelte";
|
||||||
import SelectMethodExisting from "./SetupWizard/dialogs/SelectMethodExisting.svelte";
|
import SelectMethodExisting from "./SetupWizard/dialogs/SelectMethodExisting.svelte";
|
||||||
@@ -52,10 +51,13 @@ export const enum UserMode {
|
|||||||
* Setup Manager to handle onboarding and configuration setup
|
* Setup Manager to handle onboarding and configuration setup
|
||||||
*/
|
*/
|
||||||
export class SetupManager extends AbstractObsidianModule {
|
export class SetupManager extends AbstractObsidianModule {
|
||||||
/**
|
// /**
|
||||||
* Dialog manager for handling Svelte dialogs
|
// * Dialog manager for handling Svelte dialogs
|
||||||
*/
|
// */
|
||||||
private dialogManager: SvelteDialogManager = new SvelteDialogManager(this.plugin);
|
// private dialogManager: SvelteDialogManager = new SvelteDialogManager(this.plugin);
|
||||||
|
get dialogManager() {
|
||||||
|
return this.services.UI.dialogManager;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the onboarding process
|
* Starts the onboarding process
|
||||||
|
|||||||
@@ -1,141 +0,0 @@
|
|||||||
import { eventHub, EVENT_PLUGIN_UNLOADED } from "@/common/events";
|
|
||||||
import { Modal } from "@/deps";
|
|
||||||
import type ObsidianLiveSyncPlugin from "@/main";
|
|
||||||
import { mount, unmount } from "svelte";
|
|
||||||
import DialogHost from "@lib/UI/DialogHost.svelte";
|
|
||||||
import { fireAndForget, promiseWithResolvers, type PromiseWithResolvers } from "octagonal-wheels/promises";
|
|
||||||
import { LOG_LEVEL_NOTICE, Logger } from "octagonal-wheels/common/logger";
|
|
||||||
import {
|
|
||||||
type DialogControlBase,
|
|
||||||
type DialogSvelteComponentBaseProps,
|
|
||||||
type ComponentHasResult,
|
|
||||||
setupDialogContext,
|
|
||||||
getDialogContext,
|
|
||||||
type SvelteDialogManagerBase,
|
|
||||||
} from "@/lib/src/UI/svelteDialog.ts";
|
|
||||||
|
|
||||||
export type DialogSvelteComponentProps = DialogSvelteComponentBaseProps & {
|
|
||||||
plugin: ObsidianLiveSyncPlugin;
|
|
||||||
services: ObsidianLiveSyncPlugin["services"];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type DialogControls<T = any, U = any> = DialogControlBase<T, U> & {
|
|
||||||
plugin: ObsidianLiveSyncPlugin;
|
|
||||||
services: ObsidianLiveSyncPlugin["services"];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type DialogMessageProps = Record<string, any>;
|
|
||||||
// type DialogSvelteComponent<T extends DialogSvelteComponentProps = DialogSvelteComponentProps> = Component<SvelteComponent<T>,any>;
|
|
||||||
|
|
||||||
export class SvelteDialog<T, U> extends Modal {
|
|
||||||
plugin: ObsidianLiveSyncPlugin;
|
|
||||||
mountedComponent?: ReturnType<typeof mount>;
|
|
||||||
component: ComponentHasResult<T, U>;
|
|
||||||
result?: T;
|
|
||||||
initialData?: U;
|
|
||||||
title: string = "Obsidian LiveSync - Setup Wizard";
|
|
||||||
constructor(plugin: ObsidianLiveSyncPlugin, component: ComponentHasResult<T, U>, initialData?: U) {
|
|
||||||
super(plugin.app);
|
|
||||||
this.plugin = plugin;
|
|
||||||
this.component = component;
|
|
||||||
this.initialData = initialData;
|
|
||||||
}
|
|
||||||
resolveResult() {
|
|
||||||
this.resultPromiseWithResolvers?.resolve(this.result);
|
|
||||||
this.resultPromiseWithResolvers = undefined;
|
|
||||||
}
|
|
||||||
resultPromiseWithResolvers?: PromiseWithResolvers<T | undefined>;
|
|
||||||
onOpen() {
|
|
||||||
const { contentEl } = this;
|
|
||||||
contentEl.empty();
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
||||||
const dialog = this;
|
|
||||||
|
|
||||||
if (this.resultPromiseWithResolvers) {
|
|
||||||
this.resultPromiseWithResolvers.reject("Dialog opened again");
|
|
||||||
}
|
|
||||||
const pr = promiseWithResolvers<any>();
|
|
||||||
eventHub.once(EVENT_PLUGIN_UNLOADED, () => {
|
|
||||||
if (this.resultPromiseWithResolvers === pr) {
|
|
||||||
pr.reject("Plugin unloaded");
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.resultPromiseWithResolvers = pr;
|
|
||||||
this.mountedComponent = mount(DialogHost, {
|
|
||||||
target: contentEl,
|
|
||||||
props: {
|
|
||||||
onSetupContext: (props: DialogSvelteComponentBaseProps) => {
|
|
||||||
setupDialogContext({
|
|
||||||
...props,
|
|
||||||
plugin: this.plugin,
|
|
||||||
services: this.plugin.services,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
setTitle: (title: string) => {
|
|
||||||
dialog.setTitle(title);
|
|
||||||
},
|
|
||||||
closeDialog: () => {
|
|
||||||
dialog.close();
|
|
||||||
},
|
|
||||||
setResult: (result: T) => {
|
|
||||||
this.result = result;
|
|
||||||
},
|
|
||||||
getInitialData: () => this.initialData,
|
|
||||||
mountComponent: this.component,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
waitForClose(): Promise<T | undefined> {
|
|
||||||
if (!this.resultPromiseWithResolvers) {
|
|
||||||
throw new Error("Dialog not opened yet");
|
|
||||||
}
|
|
||||||
return this.resultPromiseWithResolvers.promise;
|
|
||||||
}
|
|
||||||
onClose() {
|
|
||||||
this.resolveResult();
|
|
||||||
fireAndForget(async () => {
|
|
||||||
if (this.mountedComponent) {
|
|
||||||
await unmount(this.mountedComponent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function openSvelteDialog<T, U>(
|
|
||||||
plugin: ObsidianLiveSyncPlugin,
|
|
||||||
component: ComponentHasResult<T, U>,
|
|
||||||
initialData?: U
|
|
||||||
): Promise<T | undefined> {
|
|
||||||
const dialog = new SvelteDialog<T, U>(plugin, component, initialData);
|
|
||||||
dialog.open();
|
|
||||||
|
|
||||||
return await dialog.waitForClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SvelteDialogManager implements SvelteDialogManagerBase {
|
|
||||||
plugin: ObsidianLiveSyncPlugin;
|
|
||||||
constructor(plugin: ObsidianLiveSyncPlugin) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
|
||||||
async open<T, U>(component: ComponentHasResult<T, U>, initialData?: U): Promise<T | undefined> {
|
|
||||||
return await openSvelteDialog<T, U>(this.plugin, component, initialData);
|
|
||||||
}
|
|
||||||
async openWithExplicitCancel<T, U>(component: ComponentHasResult<T, U>, initialData?: U): Promise<T> {
|
|
||||||
for (let i = 0; i < 10; i++) {
|
|
||||||
const ret = await openSvelteDialog<T, U>(this.plugin, component, initialData);
|
|
||||||
if (ret !== undefined) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (this.plugin.services.appLifecycle.hasUnloaded()) {
|
|
||||||
throw new Error("Operation cancelled due to app shutdown.");
|
|
||||||
}
|
|
||||||
Logger("Please select 'Cancel' explicitly to cancel this operation.", LOG_LEVEL_NOTICE);
|
|
||||||
}
|
|
||||||
throw new Error("Operation Forcibly cancelled by user.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getObsidianDialogContext<T = any>(): DialogControls<T> {
|
|
||||||
return getDialogContext<T>() as DialogControls<T>;
|
|
||||||
}
|
|
||||||
@@ -16,8 +16,7 @@
|
|||||||
} from "../../../../lib/src/common/types";
|
} from "../../../../lib/src/common/types";
|
||||||
|
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import type { GuestDialogProps } from "../../../../lib/src/UI/svelteDialog";
|
import { getDialogContext, type GuestDialogProps } from "../../../../lib/src/UI/svelteDialog";
|
||||||
import { getObsidianDialogContext } from "../ObsidianSvelteDialog";
|
|
||||||
import { copyTo, pickBucketSyncSettings } from "../../../../lib/src/common/utils";
|
import { copyTo, pickBucketSyncSettings } from "../../../../lib/src/common/utils";
|
||||||
|
|
||||||
const default_setting = pickBucketSyncSettings(DEFAULT_SETTINGS);
|
const default_setting = pickBucketSyncSettings(DEFAULT_SETTINGS);
|
||||||
@@ -39,7 +38,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
let error = $state("");
|
let error = $state("");
|
||||||
const context = getObsidianDialogContext();
|
const context = getDialogContext();
|
||||||
const isEndpointSecure = $derived.by(() => {
|
const isEndpointSecure = $derived.by(() => {
|
||||||
return syncSetting.endpoint.trim().toLowerCase().startsWith("https://");
|
return syncSetting.endpoint.trim().toLowerCase().startsWith("https://");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,9 +17,8 @@
|
|||||||
} from "../../../../lib/src/common/types";
|
} from "../../../../lib/src/common/types";
|
||||||
import { isCloudantURI } from "../../../../lib/src/pouchdb/utils_couchdb";
|
import { isCloudantURI } from "../../../../lib/src/pouchdb/utils_couchdb";
|
||||||
|
|
||||||
import { getObsidianDialogContext } from "../ObsidianSvelteDialog";
|
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import type { GuestDialogProps } from "../../../../lib/src/UI/svelteDialog";
|
import { getDialogContext, type GuestDialogProps } from "../../../../lib/src/UI/svelteDialog";
|
||||||
import { copyTo, pickCouchDBSyncSettings } from "../../../../lib/src/common/utils";
|
import { copyTo, pickCouchDBSyncSettings } from "../../../../lib/src/common/utils";
|
||||||
import PanelCouchDBCheck from "./PanelCouchDBCheck.svelte";
|
import PanelCouchDBCheck from "./PanelCouchDBCheck.svelte";
|
||||||
|
|
||||||
@@ -40,7 +39,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
let error = $state("");
|
let error = $state("");
|
||||||
const context = getObsidianDialogContext();
|
const context = getDialogContext();
|
||||||
|
|
||||||
function generateSetting() {
|
function generateSetting() {
|
||||||
const connSetting: CouchDBConnection = {
|
const connSetting: CouchDBConnection = {
|
||||||
|
|||||||
@@ -22,16 +22,15 @@
|
|||||||
import { TrysteroReplicator } from "../../../../lib/src/replication/trystero/TrysteroReplicator";
|
import { TrysteroReplicator } from "../../../../lib/src/replication/trystero/TrysteroReplicator";
|
||||||
import type { ReplicatorHostEnv } from "../../../../lib/src/replication/trystero/types";
|
import type { ReplicatorHostEnv } from "../../../../lib/src/replication/trystero/types";
|
||||||
import { copyTo, pickP2PSyncSettings, type SimpleStore } from "../../../../lib/src/common/utils";
|
import { copyTo, pickP2PSyncSettings, type SimpleStore } from "../../../../lib/src/common/utils";
|
||||||
import { getObsidianDialogContext } from "../ObsidianSvelteDialog";
|
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import type { GuestDialogProps } from "../../../../lib/src/UI/svelteDialog";
|
import { getDialogContext, type GuestDialogProps } from "../../../../lib/src/UI/svelteDialog";
|
||||||
import { SETTING_KEY_P2P_DEVICE_NAME } from "../../../../lib/src/common/types";
|
import { SETTING_KEY_P2P_DEVICE_NAME } from "../../../../lib/src/common/types";
|
||||||
import ExtraItems from "../../../../lib/src/UI/components/ExtraItems.svelte";
|
import ExtraItems from "../../../../lib/src/UI/components/ExtraItems.svelte";
|
||||||
|
|
||||||
const default_setting = pickP2PSyncSettings(DEFAULT_SETTINGS);
|
const default_setting = pickP2PSyncSettings(DEFAULT_SETTINGS);
|
||||||
let syncSetting = $state<P2PConnectionInfo>({ ...default_setting });
|
let syncSetting = $state<P2PConnectionInfo>({ ...default_setting });
|
||||||
|
|
||||||
const context = getObsidianDialogContext();
|
const context = getDialogContext();
|
||||||
let error = $state("");
|
let error = $state("");
|
||||||
const TYPE_CANCELLED = "cancelled";
|
const TYPE_CANCELLED = "cancelled";
|
||||||
type SettingInfo = P2PConnectionInfo;
|
type SettingInfo = P2PConnectionInfo;
|
||||||
@@ -104,7 +103,7 @@
|
|||||||
processReplicatedDocs: async (docs: any[]) => {
|
processReplicatedDocs: async (docs: any[]) => {
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
confirm: context.plugin.confirm,
|
confirm: context.services.confirm,
|
||||||
db: dummyPouch,
|
db: dummyPouch,
|
||||||
simpleStore: store,
|
simpleStore: store,
|
||||||
deviceName: syncSetting.P2P_DevicePeerName || "unnamed-device",
|
deviceName: syncSetting.P2P_DevicePeerName || "unnamed-device",
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import { versionNumberString2Number } from "../../lib/src/string_and_binary/conv
|
|||||||
import { cancelAllPeriodicTask, cancelAllTasks } from "octagonal-wheels/concurrency/task";
|
import { cancelAllPeriodicTask, cancelAllTasks } from "octagonal-wheels/concurrency/task";
|
||||||
import { stopAllRunningProcessors } from "octagonal-wheels/concurrency/processor";
|
import { stopAllRunningProcessors } from "octagonal-wheels/concurrency/processor";
|
||||||
import { AbstractModule } from "../AbstractModule.ts";
|
import { AbstractModule } from "../AbstractModule.ts";
|
||||||
import { EVENT_PLATFORM_UNLOADED } from "../../lib/src/PlatformAPIs/base/APIBase.ts";
|
import { EVENT_PLATFORM_UNLOADED } from "@lib/events/coreEvents";
|
||||||
import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts";
|
import type { InjectableServiceHub } from "@lib/services/implements/injectable/InjectableServiceHub.ts";
|
||||||
import type { LiveSyncCore } from "../../main.ts";
|
import type { LiveSyncCore } from "../../main.ts";
|
||||||
import { initialiseWorkerModule } from "@/lib/src/worker/bgWorker.ts";
|
import { initialiseWorkerModule } from "@/lib/src/worker/bgWorker.ts";
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import { type App, type Plugin, Notice } from "@/deps";
|
||||||
|
import { scheduleTask, memoIfNotExist, memoObject, retrieveMemoObject, disposeMemoObject } from "@/common/utils";
|
||||||
|
import { $msg } from "@/lib/src/common/i18n";
|
||||||
|
import type { Confirm } from "@/lib/src/interfaces/Confirm";
|
||||||
|
import type { ObsidianServiceContext } from "@/lib/src/services/implements/obsidian/ObsidianServiceContext";
|
||||||
|
import {
|
||||||
|
askYesNo,
|
||||||
|
askString,
|
||||||
|
confirmWithMessageWithWideButton,
|
||||||
|
askSelectString,
|
||||||
|
confirmWithMessage,
|
||||||
|
} from "../coreObsidian/UILib/dialogs";
|
||||||
|
|
||||||
|
export class ObsidianConfirm<T extends ObsidianServiceContext = ObsidianServiceContext> implements Confirm {
|
||||||
|
private _context: T;
|
||||||
|
get _app(): App {
|
||||||
|
return this._context.app;
|
||||||
|
}
|
||||||
|
get _plugin(): Plugin {
|
||||||
|
return this._context.plugin;
|
||||||
|
}
|
||||||
|
constructor(context: T) {
|
||||||
|
this._context = context;
|
||||||
|
}
|
||||||
|
askYesNo(message: string): Promise<"yes" | "no"> {
|
||||||
|
return askYesNo(this._app, message);
|
||||||
|
}
|
||||||
|
askString(title: string, key: string, placeholder: string, isPassword: boolean = false): Promise<string | false> {
|
||||||
|
return askString(this._app, title, key, placeholder, isPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
async askYesNoDialog(
|
||||||
|
message: string,
|
||||||
|
opt: { title?: string; defaultOption?: "Yes" | "No"; timeout?: number } = { title: "Confirmation" }
|
||||||
|
): Promise<"yes" | "no"> {
|
||||||
|
const defaultTitle = $msg("moduleInputUIObsidian.defaultTitleConfirmation");
|
||||||
|
const yesLabel = $msg("moduleInputUIObsidian.optionYes");
|
||||||
|
const noLabel = $msg("moduleInputUIObsidian.optionNo");
|
||||||
|
const defaultOption = opt.defaultOption === "Yes" ? yesLabel : noLabel;
|
||||||
|
const ret = await confirmWithMessageWithWideButton(
|
||||||
|
this._plugin,
|
||||||
|
opt.title || defaultTitle,
|
||||||
|
message,
|
||||||
|
[yesLabel, noLabel],
|
||||||
|
defaultOption,
|
||||||
|
opt.timeout
|
||||||
|
);
|
||||||
|
return ret === yesLabel ? "yes" : "no";
|
||||||
|
}
|
||||||
|
|
||||||
|
askSelectString(message: string, items: string[]): Promise<string> {
|
||||||
|
return askSelectString(this._app, message, items);
|
||||||
|
}
|
||||||
|
|
||||||
|
askSelectStringDialogue<T extends readonly string[]>(
|
||||||
|
message: string,
|
||||||
|
buttons: T,
|
||||||
|
opt: { title?: string; defaultAction: T[number]; timeout?: number }
|
||||||
|
): Promise<T[number] | false> {
|
||||||
|
const defaultTitle = $msg("moduleInputUIObsidian.defaultTitleSelect");
|
||||||
|
return confirmWithMessageWithWideButton(
|
||||||
|
this._plugin,
|
||||||
|
opt.title || defaultTitle,
|
||||||
|
message,
|
||||||
|
buttons,
|
||||||
|
opt.defaultAction,
|
||||||
|
opt.timeout
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
askInPopup(key: string, dialogText: string, anchorCallback: (anchor: HTMLAnchorElement) => void) {
|
||||||
|
const fragment = createFragment((doc) => {
|
||||||
|
const [beforeText, afterText] = dialogText.split("{HERE}", 2);
|
||||||
|
doc.createEl("span", undefined, (a) => {
|
||||||
|
a.appendText(beforeText);
|
||||||
|
a.appendChild(
|
||||||
|
a.createEl("a", undefined, (anchor) => {
|
||||||
|
anchorCallback(anchor);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
a.appendText(afterText);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const popupKey = "popup-" + key;
|
||||||
|
scheduleTask(popupKey, 1000, async () => {
|
||||||
|
const popup = await memoIfNotExist(popupKey, () => new Notice(fragment, 0));
|
||||||
|
const isShown = popup?.noticeEl?.isShown();
|
||||||
|
if (!isShown) {
|
||||||
|
memoObject(popupKey, new Notice(fragment, 0));
|
||||||
|
}
|
||||||
|
scheduleTask(popupKey + "-close", 20000, () => {
|
||||||
|
const popup = retrieveMemoObject<Notice>(popupKey);
|
||||||
|
if (!popup) return;
|
||||||
|
if (popup?.noticeEl?.isShown()) {
|
||||||
|
popup.hide();
|
||||||
|
}
|
||||||
|
disposeMemoObject(popupKey);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmWithMessage(
|
||||||
|
title: string,
|
||||||
|
contentMd: string,
|
||||||
|
buttons: string[],
|
||||||
|
defaultAction: (typeof buttons)[number],
|
||||||
|
timeout?: number
|
||||||
|
): Promise<(typeof buttons)[number] | false> {
|
||||||
|
return confirmWithMessage(this._plugin, title, contentMd, buttons, defaultAction, timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { InjectableServiceHub } from "@/lib/src/services/implements/injectable/InjectableServiceHub";
|
||||||
|
import { ObsidianServiceContext } from "@/lib/src/services/implements/obsidian/ObsidianServiceContext";
|
||||||
|
import type { ServiceInstances } from "@/lib/src/services/ServiceHub";
|
||||||
|
import type ObsidianLiveSyncPlugin from "@/main";
|
||||||
|
import {
|
||||||
|
ObsidianAPIService,
|
||||||
|
ObsidianAppLifecycleService,
|
||||||
|
ObsidianConflictService,
|
||||||
|
ObsidianDatabaseService,
|
||||||
|
ObsidianFileProcessingService,
|
||||||
|
ObsidianReplicationService,
|
||||||
|
ObsidianReplicatorService,
|
||||||
|
ObsidianRemoteService,
|
||||||
|
ObsidianSettingService,
|
||||||
|
ObsidianTweakValueService,
|
||||||
|
ObsidianVaultService,
|
||||||
|
ObsidianTestService,
|
||||||
|
ObsidianDatabaseEventService,
|
||||||
|
ObsidianPathService,
|
||||||
|
ObsidianConfigService,
|
||||||
|
} from "./ObsidianServices";
|
||||||
|
import { ObsidianUIService } from "./ObsidianUIService";
|
||||||
|
|
||||||
|
// InjectableServiceHub
|
||||||
|
|
||||||
|
export class ObsidianServiceHub extends InjectableServiceHub<ObsidianServiceContext> {
|
||||||
|
constructor(plugin: ObsidianLiveSyncPlugin) {
|
||||||
|
const context = new ObsidianServiceContext(plugin.app, plugin, plugin);
|
||||||
|
|
||||||
|
const API = new ObsidianAPIService(context);
|
||||||
|
const appLifecycle = new ObsidianAppLifecycleService(context);
|
||||||
|
const conflict = new ObsidianConflictService(context);
|
||||||
|
const database = new ObsidianDatabaseService(context);
|
||||||
|
const fileProcessing = new ObsidianFileProcessingService(context);
|
||||||
|
const replication = new ObsidianReplicationService(context);
|
||||||
|
const replicator = new ObsidianReplicatorService(context);
|
||||||
|
const remote = new ObsidianRemoteService(context);
|
||||||
|
const setting = new ObsidianSettingService(context);
|
||||||
|
const tweakValue = new ObsidianTweakValueService(context);
|
||||||
|
const vault = new ObsidianVaultService(context);
|
||||||
|
const test = new ObsidianTestService(context);
|
||||||
|
const databaseEvents = new ObsidianDatabaseEventService(context);
|
||||||
|
const path = new ObsidianPathService(context);
|
||||||
|
const config = new ObsidianConfigService(context, vault);
|
||||||
|
const ui = new ObsidianUIService(context, {
|
||||||
|
appLifecycle,
|
||||||
|
config,
|
||||||
|
replicator,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Using 'satisfies' to ensure all services are provided
|
||||||
|
const serviceInstancesToInit = {
|
||||||
|
appLifecycle: appLifecycle,
|
||||||
|
conflict: conflict,
|
||||||
|
database: database,
|
||||||
|
databaseEvents: databaseEvents,
|
||||||
|
fileProcessing: fileProcessing,
|
||||||
|
replication: replication,
|
||||||
|
replicator: replicator,
|
||||||
|
remote: remote,
|
||||||
|
setting: setting,
|
||||||
|
tweakValue: tweakValue,
|
||||||
|
vault: vault,
|
||||||
|
test: test,
|
||||||
|
ui: ui,
|
||||||
|
path: path,
|
||||||
|
API: API,
|
||||||
|
config: config,
|
||||||
|
} satisfies Required<ServiceInstances<ObsidianServiceContext>>;
|
||||||
|
|
||||||
|
super(context, serviceInstancesToInit);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,48 +1,56 @@
|
|||||||
import { ServiceContext, type ServiceInstances } from "@/lib/src/services/ServiceHub.ts";
|
import { InjectableAPIService } from "@lib/services/implements/injectable/InjectableAPIService";
|
||||||
import {
|
import { InjectableAppLifecycleService } from "@lib/services/implements/injectable/InjectableAppLifecycleService";
|
||||||
InjectableAPIService,
|
import { InjectableConflictService } from "@lib/services/implements/injectable/InjectableConflictService";
|
||||||
InjectableAppLifecycleService,
|
import { InjectableDatabaseEventService } from "@lib/services/implements/injectable/InjectableDatabaseEventService";
|
||||||
InjectableConflictService,
|
import { InjectableDatabaseService } from "@lib/services/implements/injectable/InjectableDatabaseService";
|
||||||
InjectableDatabaseEventService,
|
import { InjectableFileProcessingService } from "@lib/services/implements/injectable/InjectableFileProcessingService";
|
||||||
InjectableDatabaseService,
|
import { InjectablePathService } from "@lib/services/implements/injectable/InjectablePathService";
|
||||||
InjectableFileProcessingService,
|
import { InjectableRemoteService } from "@lib/services/implements/injectable/InjectableRemoteService";
|
||||||
InjectablePathService,
|
import { InjectableReplicationService } from "@lib/services/implements/injectable/InjectableReplicationService";
|
||||||
InjectableRemoteService,
|
import { InjectableReplicatorService } from "@lib/services/implements/injectable/InjectableReplicatorService";
|
||||||
InjectableReplicationService,
|
import { InjectableSettingService } from "@lib/services/implements/injectable/InjectableSettingService";
|
||||||
InjectableReplicatorService,
|
import { InjectableTestService } from "@lib/services/implements/injectable/InjectableTestService";
|
||||||
InjectableSettingService,
|
import { InjectableTweakValueService } from "@lib/services/implements/injectable/InjectableTweakValueService";
|
||||||
InjectableTestService,
|
import { InjectableVaultService } from "@lib/services/implements/injectable/InjectableVaultService";
|
||||||
InjectableTweakValueService,
|
import { ConfigServiceBrowserCompat } from "@lib/services/implements/browser/ConfigServiceBrowserCompat";
|
||||||
InjectableVaultService,
|
import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext.ts";
|
||||||
} from "../../lib/src/services/InjectableServices.ts";
|
import { Platform } from "@/deps";
|
||||||
import { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts";
|
import type { SimpleStore } from "@/lib/src/common/utils";
|
||||||
import { ConfigServiceBrowserCompat } from "../../lib/src/services/Services.ts";
|
import type { IDatabaseService } from "@/lib/src/services/base/IService";
|
||||||
import type ObsidianLiveSyncPlugin from "../../main.ts";
|
import { handlers } from "@/lib/src/services/lib/HandlerUtils";
|
||||||
import { ObsidianUIService } from "./ObsidianUIService.ts";
|
|
||||||
import type { App, Plugin } from "@/deps";
|
|
||||||
|
|
||||||
export class ObsidianServiceContext extends ServiceContext {
|
|
||||||
app: App;
|
|
||||||
plugin: Plugin;
|
|
||||||
liveSyncPlugin: ObsidianLiveSyncPlugin;
|
|
||||||
constructor(app: App, plugin: Plugin, liveSyncPlugin: ObsidianLiveSyncPlugin) {
|
|
||||||
super();
|
|
||||||
this.app = app;
|
|
||||||
this.plugin = plugin;
|
|
||||||
this.liveSyncPlugin = liveSyncPlugin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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> {
|
||||||
getPlatform(): string {
|
getPlatform(): string {
|
||||||
return "obsidian";
|
if (Platform.isAndroidApp) {
|
||||||
|
return "android-app";
|
||||||
|
} else if (Platform.isIosApp) {
|
||||||
|
return "ios";
|
||||||
|
} else if (Platform.isMacOS) {
|
||||||
|
return "macos";
|
||||||
|
} else if (Platform.isMobileApp) {
|
||||||
|
return "mobile-app";
|
||||||
|
} else if (Platform.isMobile) {
|
||||||
|
return "mobile";
|
||||||
|
} else if (Platform.isSafari) {
|
||||||
|
return "safari";
|
||||||
|
} else if (Platform.isDesktop) {
|
||||||
|
return "desktop";
|
||||||
|
} else if (Platform.isDesktopApp) {
|
||||||
|
return "desktop-app";
|
||||||
|
} else {
|
||||||
|
return "unknown-obsidian";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export class ObsidianPathService extends InjectablePathService<ObsidianServiceContext> {}
|
export class ObsidianPathService extends InjectablePathService<ObsidianServiceContext> {}
|
||||||
export class ObsidianDatabaseService extends InjectableDatabaseService<ObsidianServiceContext> {}
|
export class ObsidianDatabaseService extends InjectableDatabaseService<ObsidianServiceContext> {
|
||||||
|
openSimpleStore = handlers<IDatabaseService>().binder("openSimpleStore") as (<T>(
|
||||||
|
kind: string
|
||||||
|
) => SimpleStore<T>) & { setHandler: (handler: IDatabaseService["openSimpleStore"], override?: boolean) => void };
|
||||||
|
}
|
||||||
export class ObsidianDatabaseEventService extends InjectableDatabaseEventService<ObsidianServiceContext> {}
|
export class ObsidianDatabaseEventService extends InjectableDatabaseEventService<ObsidianServiceContext> {}
|
||||||
|
|
||||||
// InjectableReplicatorService
|
// InjectableReplicatorService
|
||||||
@@ -66,49 +74,3 @@ export class ObsidianVaultService extends InjectableVaultService<ObsidianService
|
|||||||
// InjectableTestService
|
// InjectableTestService
|
||||||
export class ObsidianTestService extends InjectableTestService<ObsidianServiceContext> {}
|
export class ObsidianTestService extends InjectableTestService<ObsidianServiceContext> {}
|
||||||
export class ObsidianConfigService extends ConfigServiceBrowserCompat<ObsidianServiceContext> {}
|
export class ObsidianConfigService extends ConfigServiceBrowserCompat<ObsidianServiceContext> {}
|
||||||
|
|
||||||
// InjectableServiceHub
|
|
||||||
|
|
||||||
export class ObsidianServiceHub extends InjectableServiceHub<ObsidianServiceContext> {
|
|
||||||
constructor(plugin: ObsidianLiveSyncPlugin) {
|
|
||||||
const context = new ObsidianServiceContext(plugin.app, plugin, plugin);
|
|
||||||
|
|
||||||
const API = new ObsidianAPIService(context);
|
|
||||||
const appLifecycle = new ObsidianAppLifecycleService(context);
|
|
||||||
const conflict = new ObsidianConflictService(context);
|
|
||||||
const database = new ObsidianDatabaseService(context);
|
|
||||||
const fileProcessing = new ObsidianFileProcessingService(context);
|
|
||||||
const replication = new ObsidianReplicationService(context);
|
|
||||||
const replicator = new ObsidianReplicatorService(context);
|
|
||||||
const remote = new ObsidianRemoteService(context);
|
|
||||||
const setting = new ObsidianSettingService(context);
|
|
||||||
const tweakValue = new ObsidianTweakValueService(context);
|
|
||||||
const vault = new ObsidianVaultService(context);
|
|
||||||
const test = new ObsidianTestService(context);
|
|
||||||
const databaseEvents = new ObsidianDatabaseEventService(context);
|
|
||||||
const path = new ObsidianPathService(context);
|
|
||||||
const ui = new ObsidianUIService(context);
|
|
||||||
const config = new ObsidianConfigService(context, vault);
|
|
||||||
// Using 'satisfies' to ensure all services are provided
|
|
||||||
const serviceInstancesToInit = {
|
|
||||||
appLifecycle: appLifecycle,
|
|
||||||
conflict: conflict,
|
|
||||||
database: database,
|
|
||||||
databaseEvents: databaseEvents,
|
|
||||||
fileProcessing: fileProcessing,
|
|
||||||
replication: replication,
|
|
||||||
replicator: replicator,
|
|
||||||
remote: remote,
|
|
||||||
setting: setting,
|
|
||||||
tweakValue: tweakValue,
|
|
||||||
vault: vault,
|
|
||||||
test: test,
|
|
||||||
ui: ui,
|
|
||||||
path: path,
|
|
||||||
API: API,
|
|
||||||
config: config,
|
|
||||||
} satisfies Required<ServiceInstances<ObsidianServiceContext>>;
|
|
||||||
|
|
||||||
super(context, serviceInstancesToInit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,156 +1,30 @@
|
|||||||
import { UIService } from "../../lib/src/services/Services";
|
import type { ConfigService } from "@lib/services/base/ConfigService";
|
||||||
import { Notice, type App, type Plugin } from "@/deps";
|
import type { AppLifecycleService } from "@lib/services/base/AppLifecycleService";
|
||||||
import { SvelteDialogManager } from "../features/SetupWizard/ObsidianSvelteDialog";
|
import type { ReplicatorService } from "@lib/services/base/ReplicatorService";
|
||||||
import DialogueToCopy from "../../lib/src/UI/dialogues/DialogueToCopy.svelte";
|
import { UIService } from "@lib/services//implements/base/UIService";
|
||||||
import type { ObsidianServiceContext } from "./ObsidianServices";
|
import { ObsidianServiceContext } from "@/lib/src/services/implements/obsidian/ObsidianServiceContext";
|
||||||
import type ObsidianLiveSyncPlugin from "@/main";
|
import { ObsidianSvelteDialogManager } from "./SvelteDialogObsidian";
|
||||||
import type { Confirm } from "@/lib/src/interfaces/Confirm";
|
import { ObsidianConfirm } from "./ObsidianConfirm";
|
||||||
import {
|
|
||||||
askSelectString,
|
|
||||||
askString,
|
|
||||||
askYesNo,
|
|
||||||
confirmWithMessage,
|
|
||||||
confirmWithMessageWithWideButton,
|
|
||||||
} from "../coreObsidian/UILib/dialogs";
|
|
||||||
import { $msg } from "@/lib/src/common/i18n";
|
|
||||||
import { disposeMemoObject, memoIfNotExist, memoObject, retrieveMemoObject, scheduleTask } from "@/common/utils";
|
|
||||||
export class ObsidianConfirm implements Confirm {
|
|
||||||
private _app: App;
|
|
||||||
private _plugin: Plugin;
|
|
||||||
constructor(app: App, plugin: Plugin) {
|
|
||||||
this._app = app;
|
|
||||||
this._plugin = plugin;
|
|
||||||
}
|
|
||||||
askYesNo(message: string): Promise<"yes" | "no"> {
|
|
||||||
return askYesNo(this._app, message);
|
|
||||||
}
|
|
||||||
askString(title: string, key: string, placeholder: string, isPassword: boolean = false): Promise<string | false> {
|
|
||||||
return askString(this._app, title, key, placeholder, isPassword);
|
|
||||||
}
|
|
||||||
|
|
||||||
async askYesNoDialog(
|
export type ObsidianUIServiceDependencies<T extends ObsidianServiceContext = ObsidianServiceContext> = {
|
||||||
message: string,
|
appLifecycle: AppLifecycleService<T>;
|
||||||
opt: { title?: string; defaultOption?: "Yes" | "No"; timeout?: number } = { title: "Confirmation" }
|
config: ConfigService<T>;
|
||||||
): Promise<"yes" | "no"> {
|
replicator: ReplicatorService<T>;
|
||||||
const defaultTitle = $msg("moduleInputUIObsidian.defaultTitleConfirmation");
|
};
|
||||||
const yesLabel = $msg("moduleInputUIObsidian.optionYes");
|
|
||||||
const noLabel = $msg("moduleInputUIObsidian.optionNo");
|
|
||||||
const defaultOption = opt.defaultOption === "Yes" ? yesLabel : noLabel;
|
|
||||||
const ret = await confirmWithMessageWithWideButton(
|
|
||||||
this._plugin,
|
|
||||||
opt.title || defaultTitle,
|
|
||||||
message,
|
|
||||||
[yesLabel, noLabel],
|
|
||||||
defaultOption,
|
|
||||||
opt.timeout
|
|
||||||
);
|
|
||||||
return ret === yesLabel ? "yes" : "no";
|
|
||||||
}
|
|
||||||
|
|
||||||
askSelectString(message: string, items: string[]): Promise<string> {
|
|
||||||
return askSelectString(this._app, message, items);
|
|
||||||
}
|
|
||||||
|
|
||||||
askSelectStringDialogue<T extends readonly string[]>(
|
|
||||||
message: string,
|
|
||||||
buttons: T,
|
|
||||||
opt: { title?: string; defaultAction: T[number]; timeout?: number }
|
|
||||||
): Promise<T[number] | false> {
|
|
||||||
const defaultTitle = $msg("moduleInputUIObsidian.defaultTitleSelect");
|
|
||||||
return confirmWithMessageWithWideButton(
|
|
||||||
this._plugin,
|
|
||||||
opt.title || defaultTitle,
|
|
||||||
message,
|
|
||||||
buttons,
|
|
||||||
opt.defaultAction,
|
|
||||||
opt.timeout
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
askInPopup(key: string, dialogText: string, anchorCallback: (anchor: HTMLAnchorElement) => void) {
|
|
||||||
const fragment = createFragment((doc) => {
|
|
||||||
const [beforeText, afterText] = dialogText.split("{HERE}", 2);
|
|
||||||
doc.createEl("span", undefined, (a) => {
|
|
||||||
a.appendText(beforeText);
|
|
||||||
a.appendChild(
|
|
||||||
a.createEl("a", undefined, (anchor) => {
|
|
||||||
anchorCallback(anchor);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
a.appendText(afterText);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
const popupKey = "popup-" + key;
|
|
||||||
scheduleTask(popupKey, 1000, async () => {
|
|
||||||
const popup = await memoIfNotExist(popupKey, () => new Notice(fragment, 0));
|
|
||||||
const isShown = popup?.noticeEl?.isShown();
|
|
||||||
if (!isShown) {
|
|
||||||
memoObject(popupKey, new Notice(fragment, 0));
|
|
||||||
}
|
|
||||||
scheduleTask(popupKey + "-close", 20000, () => {
|
|
||||||
const popup = retrieveMemoObject<Notice>(popupKey);
|
|
||||||
if (!popup) return;
|
|
||||||
if (popup?.noticeEl?.isShown()) {
|
|
||||||
popup.hide();
|
|
||||||
}
|
|
||||||
disposeMemoObject(popupKey);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
confirmWithMessage(
|
|
||||||
title: string,
|
|
||||||
contentMd: string,
|
|
||||||
buttons: string[],
|
|
||||||
defaultAction: (typeof buttons)[number],
|
|
||||||
timeout?: number
|
|
||||||
): Promise<(typeof buttons)[number] | false> {
|
|
||||||
return confirmWithMessage(this._plugin, title, contentMd, buttons, defaultAction, timeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export class ObsidianUIService extends UIService<ObsidianServiceContext> {
|
export class ObsidianUIService extends UIService<ObsidianServiceContext> {
|
||||||
private _dialogManager: SvelteDialogManager;
|
constructor(context: ObsidianServiceContext, dependents: ObsidianUIServiceDependencies<ObsidianServiceContext>) {
|
||||||
private _plugin: Plugin;
|
const obsidianConfirm = new ObsidianConfirm(context);
|
||||||
private _liveSyncPlugin: ObsidianLiveSyncPlugin;
|
const obsidianSvelteDialogManager = new ObsidianSvelteDialogManager<ObsidianServiceContext>(context, {
|
||||||
private _confirmInstance: ObsidianConfirm;
|
appLifecycle: dependents.appLifecycle,
|
||||||
get dialogManager() {
|
config: dependents.config,
|
||||||
return this._dialogManager;
|
replicator: dependents.replicator,
|
||||||
}
|
confirm: obsidianConfirm,
|
||||||
constructor(context: ObsidianServiceContext) {
|
});
|
||||||
super(context);
|
super(context, {
|
||||||
this._liveSyncPlugin = context.liveSyncPlugin;
|
appLifecycle: dependents.appLifecycle,
|
||||||
this._dialogManager = new SvelteDialogManager(this._liveSyncPlugin);
|
dialogManager: obsidianSvelteDialogManager,
|
||||||
this._plugin = context.plugin;
|
confirm: obsidianConfirm,
|
||||||
this._confirmInstance = new ObsidianConfirm(this._plugin.app, this._plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
async promptCopyToClipboard(title: string, value: string): Promise<boolean> {
|
|
||||||
const param = {
|
|
||||||
title: title,
|
|
||||||
dataToCopy: value,
|
|
||||||
};
|
|
||||||
const result = await this._dialogManager.open(DialogueToCopy, param);
|
|
||||||
if (result !== "ok") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
showMarkdownDialog<T extends string[]>(
|
|
||||||
title: string,
|
|
||||||
contentMD: string,
|
|
||||||
buttons: T,
|
|
||||||
defaultAction?: (typeof buttons)[number]
|
|
||||||
): Promise<(typeof buttons)[number] | false> {
|
|
||||||
// TODO: implement `confirm` to this service
|
|
||||||
return this._liveSyncPlugin.confirm.askSelectStringDialogue(contentMD, buttons, {
|
|
||||||
title,
|
|
||||||
defaultAction: defaultAction ?? buttons[0],
|
|
||||||
timeout: 0,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get confirm(): Confirm {
|
|
||||||
return this._confirmInstance;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { Modal } from "@/deps";
|
||||||
|
|
||||||
|
import {
|
||||||
|
SvelteDialogManagerBase,
|
||||||
|
SvelteDialogMixIn,
|
||||||
|
type ComponentHasResult,
|
||||||
|
type SvelteDialogManagerDependencies,
|
||||||
|
} from "@lib/services/implements/base/SvelteDialog";
|
||||||
|
import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext";
|
||||||
|
|
||||||
|
export const SvelteDialogBase = SvelteDialogMixIn(Modal);
|
||||||
|
export class SvelteDialogObsidian<
|
||||||
|
T,
|
||||||
|
U,
|
||||||
|
C extends ObsidianServiceContext = ObsidianServiceContext,
|
||||||
|
> extends SvelteDialogBase<T, U, C> {
|
||||||
|
constructor(
|
||||||
|
context: C,
|
||||||
|
dependents: SvelteDialogManagerDependencies<C>,
|
||||||
|
component: ComponentHasResult<T, U>,
|
||||||
|
initialData?: U
|
||||||
|
) {
|
||||||
|
super(context.app);
|
||||||
|
this.initDialog(context, dependents, component, initialData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ObsidianSvelteDialogManager<T extends ObsidianServiceContext> extends SvelteDialogManagerBase<T> {
|
||||||
|
override async openSvelteDialog<TT, TU>(
|
||||||
|
component: ComponentHasResult<TT, TU>,
|
||||||
|
initialData?: TU
|
||||||
|
): Promise<TT | undefined> {
|
||||||
|
const dialog = new SvelteDialogObsidian<TT, TU, T>(this.context, this.dependents, component, initialData);
|
||||||
|
dialog.open();
|
||||||
|
return await dialog.waitForClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
-8
@@ -3,9 +3,10 @@ import ObsidianLiveSyncPlugin from "@/main";
|
|||||||
import { DEFAULT_SETTINGS, type ObsidianLiveSyncSettings } from "@/lib/src/common/types";
|
import { DEFAULT_SETTINGS, type ObsidianLiveSyncSettings } from "@/lib/src/common/types";
|
||||||
import { LOG_LEVEL_VERBOSE, setGlobalLogFunction } from "@lib/common/logger";
|
import { LOG_LEVEL_VERBOSE, setGlobalLogFunction } from "@lib/common/logger";
|
||||||
import { SettingCache } from "./obsidian-mock";
|
import { SettingCache } from "./obsidian-mock";
|
||||||
import { delay, promiseWithResolvers } from "octagonal-wheels/promises";
|
import { delay, fireAndForget, promiseWithResolvers } from "octagonal-wheels/promises";
|
||||||
|
import { EVENT_PLATFORM_UNLOADED } from "@lib/events/coreEvents";
|
||||||
import { EVENT_LAYOUT_READY, eventHub } from "@/common/events";
|
import { EVENT_LAYOUT_READY, eventHub } from "@/common/events";
|
||||||
import { EVENT_PLATFORM_UNLOADED } from "@/lib/src/PlatformAPIs/base/APIBase";
|
|
||||||
import { env } from "../suite/variables";
|
import { env } from "../suite/variables";
|
||||||
|
|
||||||
export type LiveSyncHarness = {
|
export type LiveSyncHarness = {
|
||||||
@@ -79,12 +80,14 @@ export async function generateHarness(
|
|||||||
await plugin.onload();
|
await plugin.onload();
|
||||||
let isDisposed = false;
|
let isDisposed = false;
|
||||||
const waitPromise = promiseWithResolvers<void>();
|
const waitPromise = promiseWithResolvers<void>();
|
||||||
eventHub.once(EVENT_PLATFORM_UNLOADED, async () => {
|
eventHub.once(EVENT_PLATFORM_UNLOADED, () => {
|
||||||
console.log(`Harness for vault '${vaultName}' disposed.`);
|
fireAndForget(async () => {
|
||||||
await delay(100);
|
console.log(`Harness for vault '${vaultName}' disposed.`);
|
||||||
eventHub.offAll();
|
await delay(100);
|
||||||
isDisposed = true;
|
eventHub.offAll();
|
||||||
waitPromise.resolve();
|
isDisposed = true;
|
||||||
|
waitPromise.resolve();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
eventHub.once(EVENT_LAYOUT_READY, () => {
|
eventHub.once(EVENT_LAYOUT_READY, () => {
|
||||||
plugin.app.vault.trigger("layout-ready");
|
plugin.app.vault.trigger("layout-ready");
|
||||||
|
|||||||
Reference in New Issue
Block a user