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 { 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 { ValueDeleter, ValueStore } from "@vrtmrz/value-store"; 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 { createBrowserUiScope, type BrowserUiOptions, type BrowserUiScope } from "./BrowserUiScope"; 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 { deviceValues: ValueStore & ValueDeleter; context?: T; ui?: BrowserUiOptions; getSystemVaultName?: () => string; settings?: LiveSyncBrowserSettingsPersistence; restart?: LiveSyncBrowserRestartPolicy; openKeyValueDatabase?: KeyValueDatabaseFactory; API?: Omit; } class LiveSyncBrowserAppLifecycleService extends InjectableAppLifecycleService {} class LiveSyncBrowserDatabaseService extends DatabaseService {} class LiveSyncBrowserKeyValueDBService extends KeyValueDBService {} /** LiveSync-owned service composition shared by WebApp and WebPeer. */ export class LiveSyncBrowserServiceHub extends InjectableServiceHub { readonly browserUi: BrowserUiScope; constructor(options: LiveSyncBrowserServiceHubOptions) { const context = options.context ?? (new ServiceContext({ translate: translateLiveSyncMessage, }) as T); const browserUi = createBrowserUiScope(context, options.ui); const API = new LiveSyncBrowserAPIService(context, { ...options.API, confirm: new BrowserConfirm(context, browserUi), getSystemVaultName: options.getSystemVaultName ?? (() => "livesync-browser"), }); const conflict = new InjectableConflictService(context); const fileProcessing = new InjectableFileProcessingService(context); const setting = new InjectableSettingService(context, { APIService: API, onDisplayLanguageChanged: setLang, deviceValues: options.deviceValues, }); 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 = setting.config; 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, }, browserUi ); 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, }); this.browserUi = browserUi; } } export function createLiveSyncBrowserServiceHub( options: LiveSyncBrowserServiceHubOptions ): LiveSyncBrowserServiceHub { return new LiveSyncBrowserServiceHub(options); }