import type { ObsidianLiveSyncSettings } from "@vrtmrz/livesync-commonlib/compat/common/types"; import type { KeyValueDatabaseFactory } from "@vrtmrz/livesync-commonlib/compat/interfaces/KeyValueDatabase"; import { PouchDB } from "@vrtmrz/livesync-commonlib/compat/pouchdb/pouchdb-browser"; import { ConfigService } from "@vrtmrz/livesync-commonlib/compat/services/base/ConfigService"; import { ControlService } from "@vrtmrz/livesync-commonlib/compat/services/base/ControlService"; import { DatabaseService } from "@vrtmrz/livesync-commonlib/compat/services/base/DatabaseService"; import { KeyValueDBService } from "@vrtmrz/livesync-commonlib/compat/services/base/KeyValueDBService"; import type { ISettingService } from "@vrtmrz/livesync-commonlib/compat/services/base/IService"; import { ServiceContext } from "@vrtmrz/livesync-commonlib/context"; import { InjectableAppLifecycleService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableAppLifecycleService"; import { InjectableConflictService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableConflictService"; import { InjectableDatabaseEventService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableDatabaseEventService"; import { InjectableFileProcessingService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableFileProcessingService"; import { PathServiceCompat } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectablePathService"; import { InjectableRemoteService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableRemoteService"; import { InjectableReplicationService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableReplicationService"; import { InjectableReplicatorService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableReplicatorService"; import { InjectableServiceHub } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableServiceHub"; import { InjectableSettingService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableSettingService"; import { InjectableTestService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableTestService"; import { InjectableTweakValueService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableTweakValueService"; import { InjectableVaultServiceCompat } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableVaultService"; import { setLang, translateLiveSyncMessage } from "@/common/translation"; import { BrowserConfirm } from "./BrowserConfirm"; import { createBrowserKeyValueDatabaseFactory } from "./BrowserKeyValueDatabase"; import { LiveSyncBrowserAPIService, type LiveSyncBrowserAPIServiceOptions, } from "./LiveSyncBrowserAPIService"; import { LiveSyncBrowserUIService } from "./LiveSyncBrowserUIService"; export interface LiveSyncBrowserSettingsPersistence { load(): Promise; save(settings: ObsidianLiveSyncSettings): Promise; } export interface LiveSyncBrowserRestartPolicy { schedule(): void; perform?: () => void; ask?: (message?: string) => void; isScheduled?: () => boolean; } export interface LiveSyncBrowserServiceHubOptions { context?: T; getSystemVaultName?: () => string; settings?: LiveSyncBrowserSettingsPersistence; restart?: LiveSyncBrowserRestartPolicy; openKeyValueDatabase?: KeyValueDatabaseFactory; API?: Omit; } class LiveSyncBrowserAppLifecycleService< T extends ServiceContext, > extends InjectableAppLifecycleService {} class LiveSyncBrowserDatabaseService extends DatabaseService {} class LiveSyncBrowserKeyValueDBService extends KeyValueDBService {} class LiveSyncBrowserConfigService extends ConfigService { constructor( context: T, private readonly setting: ISettingService ) { super(context); } getSmallConfig(key: string): string | null { return this.setting.getSmallConfig(key); } setSmallConfig(key: string, value: string): void { this.setting.setSmallConfig(key, value); } deleteSmallConfig(key: string): void { this.setting.deleteSmallConfig(key); } } /** LiveSync-owned service composition shared by WebApp and WebPeer. */ export class LiveSyncBrowserServiceHub extends InjectableServiceHub { constructor(options: LiveSyncBrowserServiceHubOptions) { const context = options.context ?? (new ServiceContext({ translate: translateLiveSyncMessage, }) as T); const API = new LiveSyncBrowserAPIService(context, { ...options.API, confirm: new BrowserConfirm(context), getSystemVaultName: options.getSystemVaultName ?? (() => "livesync-browser"), }); const conflict = new InjectableConflictService(context); const fileProcessing = new InjectableFileProcessingService(context); const setting = new InjectableSettingService(context, { APIService: API, onDisplayLanguageChanged: setLang, }); const settingsPersistence = options.settings; setting.loadData.setHandler( settingsPersistence ? () => settingsPersistence.load() : () => Promise.resolve(undefined) ); setting.saveData.setHandler( settingsPersistence ? (settings) => settingsPersistence.save(settings) : () => Promise.resolve() ); const appLifecycle = new LiveSyncBrowserAppLifecycleService(context, { settingService: setting, }); const restartPolicy = options.restart; const scheduleRestart = restartPolicy ? () => restartPolicy.schedule() : () => {}; appLifecycle.scheduleRestart.setHandler(scheduleRestart); appLifecycle.performRestart.setHandler( restartPolicy?.perform ? () => restartPolicy.perform?.() : scheduleRestart ); appLifecycle.askRestart.setHandler( restartPolicy?.ask ? (message) => restartPolicy.ask?.(message) : scheduleRestart ); appLifecycle.isReloadingScheduled.setHandler( restartPolicy?.isScheduled ? () => restartPolicy.isScheduled?.() ?? false : () => false ); const databaseEvents = new InjectableDatabaseEventService(context); const path = new PathServiceCompat(context, { settingService: setting, }); const vault = new InjectableVaultServiceCompat(context, { settingService: setting, APIService: API, }); const database = new LiveSyncBrowserDatabaseService(context, { pouchDB: PouchDB, path, vault, setting, API, }); const config = new LiveSyncBrowserConfigService(context, setting); const replicator = new InjectableReplicatorService(context, { settingService: setting, appLifecycleService: appLifecycle, databaseEventService: databaseEvents, }); const remote = new InjectableRemoteService(context, { pouchDB: PouchDB, APIService: API, appLifecycle, setting, }); const replication = new InjectableReplicationService(context, { APIService: API, appLifecycleService: appLifecycle, replicatorService: replicator, settingService: setting, fileProcessingService: fileProcessing, databaseService: database, }); const keyValueDB = new LiveSyncBrowserKeyValueDBService(context, { openKeyValueDatabase: options.openKeyValueDatabase ?? createBrowserKeyValueDatabaseFactory(), appLifecycle, databaseEvents, vault, }); const control = new ControlService(context, { appLifecycleService: appLifecycle, databaseService: database, fileProcessingService: fileProcessing, settingService: setting, APIService: API, replicatorService: replicator, }); const ui = new LiveSyncBrowserUIService(context, { API, appLifecycle, config, control, replicator, }); super(context, { API, appLifecycle, conflict, config, control, database, databaseEvents, fileProcessing, keyValueDB, path, remote, replication, replicator, setting, test: new InjectableTestService(context), tweakValue: new InjectableTweakValueService(context), ui, vault, }); } } export function createLiveSyncBrowserServiceHub( options: LiveSyncBrowserServiceHubOptions = {} ): LiveSyncBrowserServiceHub { return new LiveSyncBrowserServiceHub(options); }