import { Injectable, signal, computed, type Signal } from '@angular/core'; import { STORAGE_KEY_ICE_SERVERS } from '../../core/constants'; import { ICE_SERVERS } from './realtime.constants'; export interface IceServerEntry { id: string; type: 'stun' | 'turn'; urls: string; username?: string; credential?: string; } const DEFAULT_ENTRIES: IceServerEntry[] = ICE_SERVERS.map((server, index) => ({ id: `default-stun-${index}`, type: 'stun' as const, urls: Array.isArray(server.urls) ? server.urls[0] : server.urls })); @Injectable({ providedIn: 'root' }) export class IceServerSettingsService { readonly entries: Signal; readonly rtcIceServers: Signal; private readonly _entries = signal(this.load()); constructor() { this.entries = this._entries.asReadonly(); this.rtcIceServers = computed(() => this._entries().map((entry) => { if (entry.type === 'turn') { return { urls: entry.urls, username: entry.username ?? '', credential: entry.credential ?? '' }; } return { urls: entry.urls }; }) ); } addEntry(entry: Omit): void { const id = `${entry.type}-${Date.now()}-${Math.random().toString(36) .slice(2, 8)}`; const updated = [...this._entries(), { ...entry, id }]; this._entries.set(updated); this.save(updated); } removeEntry(id: string): void { const updated = this._entries().filter((entry) => entry.id !== id); this._entries.set(updated); this.save(updated); } updateEntry(id: string, changes: Partial>): void { const updated = this._entries().map((entry) => entry.id === id ? { ...entry, ...changes } : entry ); this._entries.set(updated); this.save(updated); } moveEntry(fromIndex: number, toIndex: number): void { const entries = [...this._entries()]; if (fromIndex < 0 || fromIndex >= entries.length || toIndex < 0 || toIndex >= entries.length) { return; } const [moved] = entries.splice(fromIndex, 1); entries.splice(toIndex, 0, moved); this._entries.set(entries); this.save(entries); } restoreDefaults(): void { this._entries.set([...DEFAULT_ENTRIES]); this.save(DEFAULT_ENTRIES); } private load(): IceServerEntry[] { try { const raw = localStorage.getItem(STORAGE_KEY_ICE_SERVERS); if (!raw) { return [...DEFAULT_ENTRIES]; } const parsed = JSON.parse(raw); if (!Array.isArray(parsed) || parsed.length === 0) { return [...DEFAULT_ENTRIES]; } return parsed.filter( (entry: unknown): entry is IceServerEntry => typeof entry === 'object' && entry !== null && typeof (entry as IceServerEntry).id === 'string' && ((entry as IceServerEntry).type === 'stun' || (entry as IceServerEntry).type === 'turn') && typeof (entry as IceServerEntry).urls === 'string' ); } catch { return [...DEFAULT_ENTRIES]; } } private save(entries: IceServerEntry[]): void { localStorage.setItem(STORAGE_KEY_ICE_SERVERS, JSON.stringify(entries)); } }