mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-02-01 18:07:12 +00:00
Refactor: Migrate the outdated, unstable platform abstraction layer to Services
This commit is contained in:
14
package-lock.json
generated
14
package-lock.json
generated
@@ -19,7 +19,7 @@
|
||||
"fflate": "^0.8.2",
|
||||
"idb": "^8.0.3",
|
||||
"minimatch": "^10.0.2",
|
||||
"octagonal-wheels": "^0.1.44",
|
||||
"octagonal-wheels": "^0.1.45",
|
||||
"qrcode-generator": "^1.4.4",
|
||||
"trystero": "^0.22.0",
|
||||
"xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2"
|
||||
@@ -11151,9 +11151,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/octagonal-wheels": {
|
||||
"version": "0.1.44",
|
||||
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.44.tgz",
|
||||
"integrity": "sha512-sUn/dkYQ2AbMB0R8CubVd75BjkcsteW9B14ArO99F6wM5JRwOo/yPIBBoxCUFE7JjBFOfuWG21C9E3NTga6XrA==",
|
||||
"version": "0.1.45",
|
||||
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.45.tgz",
|
||||
"integrity": "sha512-gXoCrwoUIXhmu57YN4BxAtBe+JaYNJNaXaZuVjqjopwYKpH5p2mn1om6KjA22rgGPiIJFXkse2U28FFXoT3/0Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"idb": "^8.0.3"
|
||||
@@ -23036,9 +23036,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"octagonal-wheels": {
|
||||
"version": "0.1.44",
|
||||
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.44.tgz",
|
||||
"integrity": "sha512-sUn/dkYQ2AbMB0R8CubVd75BjkcsteW9B14ArO99F6wM5JRwOo/yPIBBoxCUFE7JjBFOfuWG21C9E3NTga6XrA==",
|
||||
"version": "0.1.45",
|
||||
"resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.45.tgz",
|
||||
"integrity": "sha512-gXoCrwoUIXhmu57YN4BxAtBe+JaYNJNaXaZuVjqjopwYKpH5p2mn1om6KjA22rgGPiIJFXkse2U28FFXoT3/0Q==",
|
||||
"requires": {
|
||||
"idb": "^8.0.3"
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
"fflate": "^0.8.2",
|
||||
"idb": "^8.0.3",
|
||||
"minimatch": "^10.0.2",
|
||||
"octagonal-wheels": "^0.1.44",
|
||||
"octagonal-wheels": "^0.1.45",
|
||||
"qrcode-generator": "^1.4.4",
|
||||
"trystero": "^0.22.0",
|
||||
"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 ObsidianLiveSyncPlugin from "../../main.ts";
|
||||
import type { SimpleStore } from "octagonal-wheels/databases/SimpleStoreBase";
|
||||
import { getPlatformName } from "../../lib/src/PlatformAPIs/obsidian/Environment.ts";
|
||||
// import { getPlatformName } from "../../lib/src/PlatformAPIs/obsidian/Environment.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
import { TrysteroReplicator } from "../../lib/src/replication/trystero/TrysteroReplicator.ts";
|
||||
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 getConfirm = () => this.confirm;
|
||||
const getPlatform = () => this.getPlatform();
|
||||
const getPlatform = () => this.services.API.getPlatform();
|
||||
const env = {
|
||||
get db() {
|
||||
return getDB();
|
||||
@@ -166,9 +166,6 @@ export class P2PReplicator extends LiveSyncCommands implements P2PReplicatorBase
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
getPlatform(): string {
|
||||
return getPlatformName();
|
||||
}
|
||||
|
||||
onunload(): void {
|
||||
removeP2PReplicatorInstance();
|
||||
|
||||
2
src/lib
2
src/lib
Submodule src/lib updated: cd32d3d326...7c275d50ae
@@ -67,9 +67,10 @@ import { ModuleExtraSyncObsidian } from "./modules/extraFeaturesObsidian/ModuleE
|
||||
import { LocalDatabaseMaintenance } from "./features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts";
|
||||
import { P2PReplicator } from "./features/P2PSync/CmdP2PReplicator.ts";
|
||||
import type { LiveSyncManagers } from "./lib/src/managers/LiveSyncManagers.ts";
|
||||
import { ObsidianServiceHub } from "./modules/services/ObsidianServices.ts";
|
||||
import type { InjectableServiceHub } from "./lib/src/services/InjectableServices.ts";
|
||||
import type { ServiceContext } from "./lib/src/services/ServiceHub.ts";
|
||||
import type { InjectableServiceHub } from "./lib/src/services/implements/injectable/InjectableServiceHub.ts";
|
||||
import { ObsidianServiceHub } from "./modules/services/ObsidianServiceHub.ts";
|
||||
import type { ServiceContext } from "./lib/src/services/base/ServiceBase.ts";
|
||||
// import type { InjectableServiceHub } from "./lib/src/services/InjectableServices.ts";
|
||||
|
||||
export default class ObsidianLiveSyncPlugin
|
||||
extends Plugin
|
||||
|
||||
@@ -9,10 +9,11 @@ import {
|
||||
} from "../../lib/src/common/types.ts";
|
||||
import { AbstractModule } from "../AbstractModule.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
import { SvelteDialogManager } from "../features/SetupWizard/ObsidianSvelteDialog.ts";
|
||||
import FetchEverything from "../features/SetupWizard/dialogs/FetchEverything.svelte";
|
||||
import RebuildEverything from "../features/SetupWizard/dialogs/RebuildEverything.svelte";
|
||||
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 {
|
||||
async isFlagFileExist(path: string) {
|
||||
@@ -52,7 +53,10 @@ export class ModuleRedFlag extends AbstractModule {
|
||||
await this.deleteFlagFile(FlagFilesOriginal.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.
|
||||
|
||||
@@ -5,6 +5,8 @@ import { LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/log
|
||||
import { AbstractModule } from "../AbstractModule.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
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 {
|
||||
async tryCloseKvDB() {
|
||||
@@ -77,6 +79,7 @@ export class ModuleKeyValueDB extends AbstractModule {
|
||||
.filter((e) => e.startsWith(prefix))
|
||||
.map((e) => e.substring(prefix.length));
|
||||
},
|
||||
db: Promise.resolve(getDB()),
|
||||
} satisfies SimpleStore<T>;
|
||||
}
|
||||
_everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise<boolean> {
|
||||
@@ -100,12 +103,12 @@ export class ModuleKeyValueDB extends AbstractModule {
|
||||
}
|
||||
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.onCloseDatabase.addHandler(this._onDBClose.bind(this));
|
||||
services.databaseEvents.onDatabaseInitialisation.addHandler(this._everyOnInitializeDatabase.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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
} from "../../lib/src/common/types.ts";
|
||||
import { generatePatchObj, isObjectDifferent } from "../../lib/src/common/utils.ts";
|
||||
import { AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { SvelteDialogManager } from "./SetupWizard/ObsidianSvelteDialog.ts";
|
||||
import Intro from "./SetupWizard/dialogs/Intro.svelte";
|
||||
import SelectMethodNewUser from "./SetupWizard/dialogs/SelectMethodNewUser.svelte";
|
||||
import SelectMethodExisting from "./SetupWizard/dialogs/SelectMethodExisting.svelte";
|
||||
@@ -52,10 +51,13 @@ export const enum UserMode {
|
||||
* Setup Manager to handle onboarding and configuration setup
|
||||
*/
|
||||
export class SetupManager extends AbstractObsidianModule {
|
||||
/**
|
||||
* Dialog manager for handling Svelte dialogs
|
||||
*/
|
||||
private dialogManager: SvelteDialogManager = new SvelteDialogManager(this.plugin);
|
||||
// /**
|
||||
// * Dialog manager for handling Svelte dialogs
|
||||
// */
|
||||
// private dialogManager: SvelteDialogManager = new SvelteDialogManager(this.plugin);
|
||||
get dialogManager() {
|
||||
return this.services.UI.dialogManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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";
|
||||
|
||||
import { onMount } from "svelte";
|
||||
import type { GuestDialogProps } from "../../../../lib/src/UI/svelteDialog";
|
||||
import { getObsidianDialogContext } from "../ObsidianSvelteDialog";
|
||||
import { getDialogContext, type GuestDialogProps } from "../../../../lib/src/UI/svelteDialog";
|
||||
import { copyTo, pickBucketSyncSettings } from "../../../../lib/src/common/utils";
|
||||
|
||||
const default_setting = pickBucketSyncSettings(DEFAULT_SETTINGS);
|
||||
@@ -39,7 +38,7 @@
|
||||
}
|
||||
});
|
||||
let error = $state("");
|
||||
const context = getObsidianDialogContext();
|
||||
const context = getDialogContext();
|
||||
const isEndpointSecure = $derived.by(() => {
|
||||
return syncSetting.endpoint.trim().toLowerCase().startsWith("https://");
|
||||
});
|
||||
|
||||
@@ -17,9 +17,8 @@
|
||||
} from "../../../../lib/src/common/types";
|
||||
import { isCloudantURI } from "../../../../lib/src/pouchdb/utils_couchdb";
|
||||
|
||||
import { getObsidianDialogContext } from "../ObsidianSvelteDialog";
|
||||
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 PanelCouchDBCheck from "./PanelCouchDBCheck.svelte";
|
||||
|
||||
@@ -40,7 +39,7 @@
|
||||
});
|
||||
|
||||
let error = $state("");
|
||||
const context = getObsidianDialogContext();
|
||||
const context = getDialogContext();
|
||||
|
||||
function generateSetting() {
|
||||
const connSetting: CouchDBConnection = {
|
||||
|
||||
@@ -22,16 +22,15 @@
|
||||
import { TrysteroReplicator } from "../../../../lib/src/replication/trystero/TrysteroReplicator";
|
||||
import type { ReplicatorHostEnv } from "../../../../lib/src/replication/trystero/types";
|
||||
import { copyTo, pickP2PSyncSettings, type SimpleStore } from "../../../../lib/src/common/utils";
|
||||
import { getObsidianDialogContext } from "../ObsidianSvelteDialog";
|
||||
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 ExtraItems from "../../../../lib/src/UI/components/ExtraItems.svelte";
|
||||
|
||||
const default_setting = pickP2PSyncSettings(DEFAULT_SETTINGS);
|
||||
let syncSetting = $state<P2PConnectionInfo>({ ...default_setting });
|
||||
|
||||
const context = getObsidianDialogContext();
|
||||
const context = getDialogContext();
|
||||
let error = $state("");
|
||||
const TYPE_CANCELLED = "cancelled";
|
||||
type SettingInfo = P2PConnectionInfo;
|
||||
@@ -104,7 +103,7 @@
|
||||
processReplicatedDocs: async (docs: any[]) => {
|
||||
return;
|
||||
},
|
||||
confirm: context.plugin.confirm,
|
||||
confirm: context.services.confirm,
|
||||
db: dummyPouch,
|
||||
simpleStore: store,
|
||||
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 { stopAllRunningProcessors } from "octagonal-wheels/concurrency/processor";
|
||||
import { AbstractModule } from "../AbstractModule.ts";
|
||||
import { EVENT_PLATFORM_UNLOADED } from "../../lib/src/PlatformAPIs/base/APIBase.ts";
|
||||
import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts";
|
||||
import { EVENT_PLATFORM_UNLOADED } from "@lib/events/coreEvents";
|
||||
import type { InjectableServiceHub } from "@lib/services/implements/injectable/InjectableServiceHub.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
import { initialiseWorkerModule } from "@/lib/src/worker/bgWorker.ts";
|
||||
|
||||
|
||||
111
src/modules/services/ObsidianConfirm.ts
Normal file
111
src/modules/services/ObsidianConfirm.ts
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
73
src/modules/services/ObsidianServiceHub.ts
Normal file
73
src/modules/services/ObsidianServiceHub.ts
Normal file
@@ -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,
|
||||
InjectableAppLifecycleService,
|
||||
InjectableConflictService,
|
||||
InjectableDatabaseEventService,
|
||||
InjectableDatabaseService,
|
||||
InjectableFileProcessingService,
|
||||
InjectablePathService,
|
||||
InjectableRemoteService,
|
||||
InjectableReplicationService,
|
||||
InjectableReplicatorService,
|
||||
InjectableSettingService,
|
||||
InjectableTestService,
|
||||
InjectableTweakValueService,
|
||||
InjectableVaultService,
|
||||
} from "../../lib/src/services/InjectableServices.ts";
|
||||
import { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts";
|
||||
import { ConfigServiceBrowserCompat } from "../../lib/src/services/Services.ts";
|
||||
import type ObsidianLiveSyncPlugin from "../../main.ts";
|
||||
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;
|
||||
}
|
||||
}
|
||||
import { InjectableAPIService } from "@lib/services/implements/injectable/InjectableAPIService";
|
||||
import { InjectableAppLifecycleService } from "@lib/services/implements/injectable/InjectableAppLifecycleService";
|
||||
import { InjectableConflictService } from "@lib/services/implements/injectable/InjectableConflictService";
|
||||
import { InjectableDatabaseEventService } from "@lib/services/implements/injectable/InjectableDatabaseEventService";
|
||||
import { InjectableDatabaseService } from "@lib/services/implements/injectable/InjectableDatabaseService";
|
||||
import { InjectableFileProcessingService } from "@lib/services/implements/injectable/InjectableFileProcessingService";
|
||||
import { InjectablePathService } from "@lib/services/implements/injectable/InjectablePathService";
|
||||
import { InjectableRemoteService } from "@lib/services/implements/injectable/InjectableRemoteService";
|
||||
import { InjectableReplicationService } from "@lib/services/implements/injectable/InjectableReplicationService";
|
||||
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 { InjectableTweakValueService } from "@lib/services/implements/injectable/InjectableTweakValueService";
|
||||
import { InjectableVaultService } from "@lib/services/implements/injectable/InjectableVaultService";
|
||||
import { ConfigServiceBrowserCompat } from "@lib/services/implements/browser/ConfigServiceBrowserCompat";
|
||||
import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext.ts";
|
||||
import { Platform } from "@/deps";
|
||||
import type { SimpleStore } from "@/lib/src/common/utils";
|
||||
import type { IDatabaseService } from "@/lib/src/services/base/IService";
|
||||
import { handlers } from "@/lib/src/services/lib/HandlerUtils";
|
||||
|
||||
// All Services will be migrated to be based on Plain Services, not Injectable Services.
|
||||
// This is a migration step.
|
||||
|
||||
export class ObsidianAPIService extends InjectableAPIService<ObsidianServiceContext> {
|
||||
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 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> {}
|
||||
|
||||
// InjectableReplicatorService
|
||||
@@ -66,49 +74,3 @@ export class ObsidianVaultService extends InjectableVaultService<ObsidianService
|
||||
// InjectableTestService
|
||||
export class ObsidianTestService extends InjectableTestService<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 { Notice, type App, type Plugin } from "@/deps";
|
||||
import { SvelteDialogManager } from "../features/SetupWizard/ObsidianSvelteDialog";
|
||||
import DialogueToCopy from "../../lib/src/UI/dialogues/DialogueToCopy.svelte";
|
||||
import type { ObsidianServiceContext } from "./ObsidianServices";
|
||||
import type ObsidianLiveSyncPlugin from "@/main";
|
||||
import type { Confirm } from "@/lib/src/interfaces/Confirm";
|
||||
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);
|
||||
}
|
||||
import type { ConfigService } from "@lib/services/base/ConfigService";
|
||||
import type { AppLifecycleService } from "@lib/services/base/AppLifecycleService";
|
||||
import type { ReplicatorService } from "@lib/services/base/ReplicatorService";
|
||||
import { UIService } from "@lib/services//implements/base/UIService";
|
||||
import { ObsidianServiceContext } from "@/lib/src/services/implements/obsidian/ObsidianServiceContext";
|
||||
import { ObsidianSvelteDialogManager } from "./SvelteDialogObsidian";
|
||||
import { ObsidianConfirm } from "./ObsidianConfirm";
|
||||
|
||||
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";
|
||||
}
|
||||
export type ObsidianUIServiceDependencies<T extends ObsidianServiceContext = ObsidianServiceContext> = {
|
||||
appLifecycle: AppLifecycleService<T>;
|
||||
config: ConfigService<T>;
|
||||
replicator: ReplicatorService<T>;
|
||||
};
|
||||
|
||||
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> {
|
||||
private _dialogManager: SvelteDialogManager;
|
||||
private _plugin: Plugin;
|
||||
private _liveSyncPlugin: ObsidianLiveSyncPlugin;
|
||||
private _confirmInstance: ObsidianConfirm;
|
||||
get dialogManager() {
|
||||
return this._dialogManager;
|
||||
}
|
||||
constructor(context: ObsidianServiceContext) {
|
||||
super(context);
|
||||
this._liveSyncPlugin = context.liveSyncPlugin;
|
||||
this._dialogManager = new SvelteDialogManager(this._liveSyncPlugin);
|
||||
this._plugin = context.plugin;
|
||||
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,
|
||||
constructor(context: ObsidianServiceContext, dependents: ObsidianUIServiceDependencies<ObsidianServiceContext>) {
|
||||
const obsidianConfirm = new ObsidianConfirm(context);
|
||||
const obsidianSvelteDialogManager = new ObsidianSvelteDialogManager<ObsidianServiceContext>(context, {
|
||||
appLifecycle: dependents.appLifecycle,
|
||||
config: dependents.config,
|
||||
replicator: dependents.replicator,
|
||||
confirm: obsidianConfirm,
|
||||
});
|
||||
super(context, {
|
||||
appLifecycle: dependents.appLifecycle,
|
||||
dialogManager: obsidianSvelteDialogManager,
|
||||
confirm: obsidianConfirm,
|
||||
});
|
||||
}
|
||||
|
||||
get confirm(): Confirm {
|
||||
return this._confirmInstance;
|
||||
}
|
||||
}
|
||||
|
||||
37
src/modules/services/SvelteDialogObsidian.ts
Normal file
37
src/modules/services/SvelteDialogObsidian.ts
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,10 @@ import ObsidianLiveSyncPlugin from "@/main";
|
||||
import { DEFAULT_SETTINGS, type ObsidianLiveSyncSettings } from "@/lib/src/common/types";
|
||||
import { LOG_LEVEL_VERBOSE, setGlobalLogFunction } from "@lib/common/logger";
|
||||
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_PLATFORM_UNLOADED } from "@/lib/src/PlatformAPIs/base/APIBase";
|
||||
|
||||
import { env } from "../suite/variables";
|
||||
|
||||
export type LiveSyncHarness = {
|
||||
@@ -79,12 +80,14 @@ export async function generateHarness(
|
||||
await plugin.onload();
|
||||
let isDisposed = false;
|
||||
const waitPromise = promiseWithResolvers<void>();
|
||||
eventHub.once(EVENT_PLATFORM_UNLOADED, async () => {
|
||||
console.log(`Harness for vault '${vaultName}' disposed.`);
|
||||
await delay(100);
|
||||
eventHub.offAll();
|
||||
isDisposed = true;
|
||||
waitPromise.resolve();
|
||||
eventHub.once(EVENT_PLATFORM_UNLOADED, () => {
|
||||
fireAndForget(async () => {
|
||||
console.log(`Harness for vault '${vaultName}' disposed.`);
|
||||
await delay(100);
|
||||
eventHub.offAll();
|
||||
isDisposed = true;
|
||||
waitPromise.resolve();
|
||||
});
|
||||
});
|
||||
eventHub.once(EVENT_LAYOUT_READY, () => {
|
||||
plugin.app.vault.trigger("layout-ready");
|
||||
|
||||
Reference in New Issue
Block a user