Move toju-app into own its folder
This commit is contained in:
222
toju-app/src/app/features/settings/settings.component.ts
Normal file
222
toju-app/src/app/features/settings/settings.component.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
import {
|
||||
Component,
|
||||
inject,
|
||||
signal,
|
||||
OnInit,
|
||||
computed
|
||||
} from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { NgIcon, provideIcons } from '@ng-icons/core';
|
||||
import {
|
||||
lucideServer,
|
||||
lucidePlus,
|
||||
lucideTrash2,
|
||||
lucideCheck,
|
||||
lucideX,
|
||||
lucideSettings,
|
||||
lucideRefreshCw,
|
||||
lucideGlobe,
|
||||
lucideArrowLeft,
|
||||
lucideAudioLines
|
||||
} from '@ng-icons/lucide';
|
||||
|
||||
import { ServerDirectoryFacade } from '../../domains/server-directory';
|
||||
import { VoiceConnectionFacade } from '../../domains/voice-connection';
|
||||
import { NotificationAudioService, AppSound } from '../../core/services/notification-audio.service';
|
||||
import { STORAGE_KEY_CONNECTION_SETTINGS, STORAGE_KEY_VOICE_SETTINGS } from '../../core/constants';
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
NgIcon
|
||||
],
|
||||
viewProviders: [
|
||||
provideIcons({
|
||||
lucideServer,
|
||||
lucidePlus,
|
||||
lucideTrash2,
|
||||
lucideCheck,
|
||||
lucideX,
|
||||
lucideSettings,
|
||||
lucideRefreshCw,
|
||||
lucideGlobe,
|
||||
lucideArrowLeft,
|
||||
lucideAudioLines
|
||||
})
|
||||
],
|
||||
templateUrl: './settings.component.html'
|
||||
})
|
||||
/**
|
||||
* Settings page for managing signaling servers and connection preferences.
|
||||
*/
|
||||
export class SettingsComponent implements OnInit {
|
||||
private serverDirectory = inject(ServerDirectoryFacade);
|
||||
private voiceConnection = inject(VoiceConnectionFacade);
|
||||
private router = inject(Router);
|
||||
audioService = inject(NotificationAudioService);
|
||||
|
||||
servers = this.serverDirectory.servers;
|
||||
activeServers = this.serverDirectory.activeServers;
|
||||
hasMissingDefaultServers = this.serverDirectory.hasMissingDefaultServers;
|
||||
hasMultipleServers = computed(() => this.servers().length > 1);
|
||||
hasMultipleActiveServers = computed(() => this.activeServers().length > 1);
|
||||
isTesting = signal(false);
|
||||
addError = signal<string | null>(null);
|
||||
|
||||
newServerName = '';
|
||||
newServerUrl = '';
|
||||
autoReconnect = true;
|
||||
searchAllServers = true;
|
||||
noiseReduction = true;
|
||||
|
||||
/** Load persisted connection settings on component init. */
|
||||
ngOnInit(): void {
|
||||
this.loadConnectionSettings();
|
||||
this.loadVoiceSettings();
|
||||
}
|
||||
|
||||
/** Add a new signaling server after URL validation and duplicate checking. */
|
||||
addServer(): void {
|
||||
this.addError.set(null);
|
||||
|
||||
// Validate URL
|
||||
try {
|
||||
new URL(this.newServerUrl);
|
||||
} catch {
|
||||
this.addError.set('Please enter a valid URL');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for duplicates
|
||||
if (this.servers().some((server) => server.url === this.newServerUrl)) {
|
||||
this.addError.set('This server URL already exists');
|
||||
return;
|
||||
}
|
||||
|
||||
this.serverDirectory.addServer({
|
||||
name: this.newServerName.trim(),
|
||||
url: this.newServerUrl.trim().replace(/\/$/, '') // Remove trailing slash
|
||||
});
|
||||
|
||||
// Clear form
|
||||
this.newServerName = '';
|
||||
this.newServerUrl = '';
|
||||
|
||||
// Test the new server
|
||||
const servers = this.servers();
|
||||
const newServer = servers[servers.length - 1];
|
||||
|
||||
if (newServer) {
|
||||
this.serverDirectory.testServer(newServer.id);
|
||||
}
|
||||
}
|
||||
|
||||
/** Remove a signaling server by its ID. */
|
||||
removeServer(id: string): void {
|
||||
this.serverDirectory.removeServer(id);
|
||||
}
|
||||
|
||||
/** Set the active signaling server used for connections. */
|
||||
setActiveServer(id: string): void {
|
||||
this.serverDirectory.setActiveServer(id);
|
||||
}
|
||||
|
||||
deactivateServer(id: string): void {
|
||||
this.serverDirectory.deactivateServer(id);
|
||||
}
|
||||
|
||||
restoreDefaultServers(): void {
|
||||
this.serverDirectory.restoreDefaultServers();
|
||||
}
|
||||
|
||||
/** Test connectivity to all configured servers. */
|
||||
async testAllServers(): Promise<void> {
|
||||
this.isTesting.set(true);
|
||||
await this.serverDirectory.testAllServers();
|
||||
this.isTesting.set(false);
|
||||
}
|
||||
|
||||
/** Load connection settings (auto-reconnect, search scope) from localStorage. */
|
||||
loadConnectionSettings(): void {
|
||||
const settings = localStorage.getItem(STORAGE_KEY_CONNECTION_SETTINGS);
|
||||
|
||||
if (settings) {
|
||||
const parsed = JSON.parse(settings);
|
||||
|
||||
this.autoReconnect = parsed.autoReconnect ?? true;
|
||||
this.searchAllServers = parsed.searchAllServers ?? true;
|
||||
this.serverDirectory.setSearchAllServers(this.searchAllServers);
|
||||
}
|
||||
}
|
||||
|
||||
/** Persist current connection settings to localStorage. */
|
||||
saveConnectionSettings(): void {
|
||||
localStorage.setItem(
|
||||
STORAGE_KEY_CONNECTION_SETTINGS,
|
||||
JSON.stringify({
|
||||
autoReconnect: this.autoReconnect,
|
||||
searchAllServers: this.searchAllServers
|
||||
})
|
||||
);
|
||||
|
||||
this.serverDirectory.setSearchAllServers(this.searchAllServers);
|
||||
}
|
||||
|
||||
/** Navigate back to the main page. */
|
||||
goBack(): void {
|
||||
this.router.navigate(['/']);
|
||||
}
|
||||
|
||||
/** Load voice settings (noise reduction) from localStorage. */
|
||||
loadVoiceSettings(): void {
|
||||
const settings = localStorage.getItem(STORAGE_KEY_VOICE_SETTINGS);
|
||||
|
||||
if (settings) {
|
||||
const parsed = JSON.parse(settings);
|
||||
|
||||
this.noiseReduction = parsed.noiseReduction ?? false;
|
||||
}
|
||||
|
||||
// Sync the live WebRTC state with the persisted preference
|
||||
if (this.noiseReduction !== this.voiceConnection.isNoiseReductionEnabled()) {
|
||||
this.voiceConnection.toggleNoiseReduction(this.noiseReduction);
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when the notification volume slider changes. */
|
||||
onNotificationVolumeChange(value: number): void {
|
||||
this.audioService.setNotificationVolume(value);
|
||||
}
|
||||
|
||||
/** Play a preview of the notification sound at the current volume. */
|
||||
previewNotificationSound(): void {
|
||||
this.audioService.play(AppSound.Notification);
|
||||
}
|
||||
|
||||
/** Persist noise reduction preference (merged into existing voice settings) and apply immediately. */
|
||||
async saveVoiceSettings(): Promise<void> {
|
||||
// Merge into existing voice settings so we don't overwrite device/volume prefs
|
||||
let existing: Record<string, unknown> = {};
|
||||
|
||||
try {
|
||||
const raw = localStorage.getItem(STORAGE_KEY_VOICE_SETTINGS);
|
||||
|
||||
if (raw)
|
||||
existing = JSON.parse(raw);
|
||||
} catch {}
|
||||
|
||||
localStorage.setItem(
|
||||
STORAGE_KEY_VOICE_SETTINGS,
|
||||
JSON.stringify({ ...existing,
|
||||
noiseReduction: this.noiseReduction })
|
||||
);
|
||||
|
||||
await this.voiceConnection.toggleNoiseReduction(this.noiseReduction);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user