import { Injectable, Inject, signal, computed } from '@angular/core'; import { SettingsService } from './settings.service'; declare const p3xr: any; /** * Runtime state service using Angular signals. * * Reads from the global p3xr.state object and exposes it through signals * for reactive Angular components. */ @Injectable({ providedIn: 'root' }) export class RedisStateService { // --- Writable signals for runtime state --- readonly theme = signal(this.p3xrState?.theme); readonly connection = signal(this.p3xrState?.connection); readonly currentDatabase = signal(this.p3xrState?.currentDatabase); readonly databaseIndexes = signal(this.p3xrState?.databaseIndexes ?? [0]); readonly connections = signal(this.p3xrState?.connections ?? { list: [] }); readonly redisConnections = signal>(this.p3xrState?.redisConnections ?? {}); readonly keysRaw = signal(this.p3xrState?.keysRaw ?? []); readonly keysInfo = signal(this.p3xrState?.keysInfo); readonly search = signal(this.p3xrState?.search ?? ''); readonly page = signal(1); readonly info = signal(this.p3xrState?.info); readonly dbsize = signal(this.p3xrState?.dbsize); readonly redisChanged = signal(false); readonly failed = signal(false); readonly monitor = signal(false); readonly monitorPattern = signal('*'); readonly commands = signal([]); readonly cfg = signal(this.p3xrState?.cfg); readonly version = signal(this.p3xrState?.version); readonly donated = signal(this.p3xrState?.donated ?? false); readonly hasProOrEnterpriseJsonBinary = signal(this.p3xrState?.hasProOrEnterpriseJsonBinary ?? false); readonly license = signal(this.p3xrState?.license ?? { tier: 'free', valid: false, reason: 'LICENSE_MISSING', features: [], }); // --- Computed values --- readonly themeLayout = computed(() => { const t = this.theme(); return t ? t + 'Layout' : undefined; }); readonly themeCommon = computed(() => { const t = this.theme(); return t ? t + 'Common' : undefined; }); readonly filteredKeys = computed(() => { let keys = this.keysRaw().slice(); const search = this.search(); const settings = this.settings; // Apply client-side search filter if (settings.searchClientSide() && typeof search === 'string' && search.length > 0) { if (settings.searchStartsWith()) { keys = keys.filter((key) => key.startsWith(search)); } else { keys = keys.filter((key) => key.includes(search)); } } return keys; }); readonly paginatedKeys = computed(() => { const keys = this.filteredKeys(); const pageSize = this.settings.pageCount(); if (keys.length <= pageSize) { return keys; } const start = (this.page() - 1) * pageSize; return keys.slice(start, start + pageSize); }); readonly pages = computed(() => { return Math.ceil(this.filteredKeys().length / this.settings.pageCount()); }); constructor(@Inject(SettingsService) private settings: SettingsService) {} // --- Helper to access global p3xr.state during hybrid mode --- private get p3xrState(): any { return p3xr?.state; } /** * Syncs the signals from the global p3xr.state object. * Call this after AngularJS modifies p3xr.state (e.g. after a socket response) * to keep Angular components up-to-date during hybrid mode. */ syncFromGlobal(): void { const state = this.p3xrState; if (!state) return; this.theme.set(state.theme); this.connection.set(state.connection); this.currentDatabase.set(state.currentDatabase); this.databaseIndexes.set(state.databaseIndexes ?? [0]); this.connections.set(state.connections); this.redisConnections.set(state.redisConnections ?? {}); this.keysRaw.set(state.keysRaw ?? []); this.keysInfo.set(state.keysInfo); this.info.set(state.info); this.dbsize.set(state.dbsize); this.failed.set(state.failed ?? false); this.monitor.set(state.monitor ?? false); this.cfg.set(state.cfg); this.version.set(state.version); this.donated.set(state.donated ?? false); this.hasProOrEnterpriseJsonBinary.set(state.hasProOrEnterpriseJsonBinary ?? false); this.license.set(state.license); } /** * Resets connections to default state. */ resetConnections(): void { this.connections.set({ list: [] }); } }