From 13b4cf39929de0a4485983fd6f6570e1781c41ed Mon Sep 17 00:00:00 2001 From: 4gray Date: Fri, 20 Dec 2024 22:59:22 +0100 Subject: [PATCH] refactor: update settings store --- .../video-player/video-player.component.ts | 6 +- src/app/services/player.service.ts | 7 +- src/app/services/settings-store.service.ts | 79 +++++++++++-------- src/app/settings/settings.component.html | 2 + src/app/settings/settings.component.ts | 14 +++- .../live-stream-layout.component.ts | 6 +- .../vod-details/vod-details.component.html | 2 +- .../vod-details/vod-details.component.ts | 6 +- .../xtream/xtream-main-container.component.ts | 34 ++++---- 9 files changed, 87 insertions(+), 69 deletions(-) diff --git a/src/app/player/components/video-player/video-player.component.ts b/src/app/player/components/video-player/video-player.component.ts index 9f17a07d..7939ba7f 100644 --- a/src/app/player/components/video-player/video-player.component.ts +++ b/src/app/player/components/video-player/video-player.component.ts @@ -9,6 +9,7 @@ import { OnDestroy, OnInit, effect, + inject, } from '@angular/core'; import { MatSidenavModule } from '@angular/material/sidenav'; import { MatSnackBar } from '@angular/material/snack-bar'; @@ -133,6 +134,8 @@ export class VideoPlayerComponent implements OnInit, OnDestroy { volume = 1; + private settingsStore = inject(SettingsStore); + constructor( private activatedRoute: ActivatedRoute, private dataService: DataService, @@ -142,8 +145,7 @@ export class VideoPlayerComponent implements OnInit, OnDestroy { private router: Router, private snackBar: MatSnackBar, private storage: StorageMap, - private store: Store, - private settingsStore: SettingsStore + private store: Store ) { // Initialize volume from localStorage in constructor const savedVolume = localStorage.getItem('volume'); diff --git a/src/app/services/player.service.ts b/src/app/services/player.service.ts index 39cdfe4d..fb8774f7 100644 --- a/src/app/services/player.service.ts +++ b/src/app/services/player.service.ts @@ -17,7 +17,6 @@ export class PlayerService { private dialog = inject(MatDialog); private dataService = inject(DataService); private settingsStore = inject(SettingsStore); - private settings = this.settingsStore.getSettings(); openPlayer( streamUrl: string, @@ -26,7 +25,7 @@ export class PlayerService { hideExternalInfoDialog = false, isLiveContent = false ) { - const player = this.settings()?.player ?? VideoPlayer.VideoJs; + const player = this.settingsStore.player() ?? VideoPlayer.VideoJs; if (player === VideoPlayer.MPV) { if (!hideExternalInfoDialog) { @@ -34,7 +33,7 @@ export class PlayerService { } this.dataService.sendIpcEvent(OPEN_MPV_PLAYER, { url: streamUrl, - mpvPlayerPath: this.settings()?.mpvPlayerPath, + mpvPlayerPath: this.settingsStore.mpvPlayerPath(), title, thumbnail, }); @@ -44,7 +43,7 @@ export class PlayerService { } this.dataService.sendIpcEvent(OPEN_VLC_PLAYER, { url: streamUrl, - vlcPlayerPath: this.settings()?.vlcPlayerPath, + vlcPlayerPath: this.settingsStore.vlcPlayerPath(), }); } else if (!isLiveContent) { this.dialog.open( diff --git a/src/app/services/settings-store.service.ts b/src/app/services/settings-store.service.ts index db817cf1..6117eb1f 100644 --- a/src/app/services/settings-store.service.ts +++ b/src/app/services/settings-store.service.ts @@ -1,5 +1,13 @@ -import { Injectable, computed, signal } from '@angular/core'; +import { computed, inject } from '@angular/core'; +import { + patchState, + signalStore, + withComputed, + withMethods, + withState, +} from '@ngrx/signals'; import { StorageMap } from '@ngx-pwa/local-storage'; +import { firstValueFrom } from 'rxjs'; import { Language } from '../settings/language.enum'; import { Settings, VideoPlayer } from '../settings/settings.interface'; import { Theme } from '../settings/theme.enum'; @@ -17,35 +25,44 @@ const DEFAULT_SETTINGS: Settings = { epgUrl: [], }; -@Injectable({ - providedIn: 'root', -}) -export class SettingsStore { - private settings = signal(DEFAULT_SETTINGS); +export const SettingsStore = signalStore( + { providedIn: 'root' }, + withState(DEFAULT_SETTINGS), + withComputed((store) => ({ + player: computed(() => store.player()), + showCaptions: computed(() => store.showCaptions()), + theme: computed(() => store.theme()), + })), + withMethods((store, storage = inject(StorageMap)) => ({ + async loadSettings() { + const stored = await firstValueFrom( + storage.get(STORE_KEY.Settings) + ); + if (stored) { + patchState(store, { + ...DEFAULT_SETTINGS, + ...(stored as Settings), + }); + } + }, - // Computed values for commonly used settings - readonly player = computed(() => this.settings().player); - readonly showCaptions = computed(() => this.settings().showCaptions); - readonly theme = computed(() => this.settings().theme); + async updateSettings(settings: Partial) { + patchState(store, settings); + await firstValueFrom(storage.set(STORE_KEY.Settings, settings)); + }, - constructor(private storage: StorageMap) { - this.loadSettings(); - } - - async loadSettings() { - const stored = await this.storage.get(STORE_KEY.Settings).toPromise(); - if (stored) { - this.settings.set({ ...DEFAULT_SETTINGS, ...(stored as Settings) }); - } - } - - async updateSettings(settings: Partial) { - const newSettings = { ...this.settings(), ...settings }; - this.settings.set(newSettings); - await this.storage.set(STORE_KEY.Settings, newSettings).toPromise(); - } - - getSettings() { - return this.settings; - } -} + getSettings() { + return { + player: store.player(), + language: store.language(), + showCaptions: store.showCaptions(), + theme: store.theme(), + mpvPlayerPath: store.mpvPlayerPath(), + vlcPlayerPath: store.vlcPlayerPath(), + remoteControl: store.remoteControl(), + remoteControlPort: store.remoteControlPort(), + epgUrl: store.epgUrl(), + }; + }, + })) +); diff --git a/src/app/settings/settings.component.html b/src/app/settings/settings.component.html index e6fcdae5..6fc0e8cb 100644 --- a/src/app/settings/settings.component.html +++ b/src/app/settings/settings.component.html @@ -209,6 +209,7 @@

{{ 'SETTINGS.TITLE' | translate }}

> + @if (settingsForm.value.remoteControl === true) {
diff --git a/src/app/settings/settings.component.ts b/src/app/settings/settings.component.ts index 0afce774..6f780332 100644 --- a/src/app/settings/settings.component.ts +++ b/src/app/settings/settings.component.ts @@ -1,6 +1,13 @@ /* eslint-disable @typescript-eslint/no-base-to-string */ import { CommonModule } from '@angular/common'; -import { Component, Inject, Input, OnInit, Optional } from '@angular/core'; +import { + Component, + inject, + Inject, + Input, + OnInit, + Optional, +} from '@angular/core'; import { FormArray, FormBuilder, @@ -136,6 +143,8 @@ export class SettingsComponent implements OnInit { /** Form array with epg sources */ epgUrl = this.settingsForm.get('epgUrl') as FormArray; + private settingsStore = inject(SettingsStore); + /** * Creates an instance of SettingsComponent and injects * required dependencies into the component @@ -152,7 +161,6 @@ export class SettingsComponent implements OnInit { private store: Store, private translate: TranslateService, private matDialog: MatDialog, - private settingsStore: SettingsStore, @Optional() @Inject(MAT_DIALOG_DATA) data?: { isDialog: boolean } ) { this.isDialog = data?.isDialog ?? false; @@ -172,7 +180,7 @@ export class SettingsComponent implements OnInit { */ async setSettings() { await this.settingsStore.loadSettings(); - const currentSettings = this.settingsStore.getSettings()(); + const currentSettings = this.settingsStore.getSettings(); this.settingsForm.patchValue(currentSettings); if (this.isTauri && currentSettings.epgUrl) { diff --git a/src/app/xtream-tauri/live-stream-layout/live-stream-layout.component.ts b/src/app/xtream-tauri/live-stream-layout/live-stream-layout.component.ts index 72b6ebc2..ff9228e9 100644 --- a/src/app/xtream-tauri/live-stream-layout/live-stream-layout.component.ts +++ b/src/app/xtream-tauri/live-stream-layout/live-stream-layout.component.ts @@ -106,13 +106,13 @@ export class LiveStreamLayoutComponent implements OnInit { openPlayer(streamUrl: string, title: string, thumbnail: string) { this.streamUrl = streamUrl; - this.player = this.settings()?.player ?? VideoPlayer.VideoJs; + this.player = this.settingsStore.player() ?? VideoPlayer.VideoJs; if (this.player === VideoPlayer.MPV) { if (!this.hideExternalInfoDialog()) this.dialog.open(ExternalPlayerInfoDialogComponent); this.dataService.sendIpcEvent(OPEN_MPV_PLAYER, { url: streamUrl, - mpvPlayerPath: this.settings()?.mpvPlayerPath, + mpvPlayerPath: this.settingsStore.mpvPlayerPath(), title, thumbnail, }); @@ -121,7 +121,7 @@ export class LiveStreamLayoutComponent implements OnInit { this.dialog.open(ExternalPlayerInfoDialogComponent); this.dataService.sendIpcEvent(OPEN_VLC_PLAYER, { url: streamUrl, - vlcPlayerPath: this.settings()?.vlcPlayerPath, + vlcPlayerPath: this.settingsStore.vlcPlayerPath(), }); } else { if (this.selectedContentType() !== 'live') { diff --git a/src/app/xtream-tauri/vod-details/vod-details.component.html b/src/app/xtream-tauri/vod-details/vod-details.component.html index 2ffa76e1..0e93791f 100644 --- a/src/app/xtream-tauri/vod-details/vod-details.component.html +++ b/src/app/xtream-tauri/vod-details/vod-details.component.html @@ -4,7 +4,7 @@ class="container" [style.background]=" item?.info?.backdrop_path && item?.info?.backdrop_path.length > 0 - ? settings().theme === 'DARK_THEME' + ? theme() === 'DARK_THEME' ? 'linear-gradient(to top, rgba(29,29,29,1) 0%, rgba(0,0,0,0.5) 100%), url(' + item?.info?.backdrop_path[0] + ') repeat-x' diff --git a/src/app/xtream-tauri/vod-details/vod-details.component.ts b/src/app/xtream-tauri/vod-details/vod-details.component.ts index 67a9b3af..72f716db 100644 --- a/src/app/xtream-tauri/vod-details/vod-details.component.ts +++ b/src/app/xtream-tauri/vod-details/vod-details.component.ts @@ -1,13 +1,11 @@ import { Component, OnDestroy, OnInit, inject } from '@angular/core'; import { MatButton } from '@angular/material/button'; -import { MatDialog } from '@angular/material/dialog'; import { MatIcon } from '@angular/material/icon'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { ActivatedRoute } from '@angular/router'; import { Store } from '@ngrx/store'; import { TranslateModule } from '@ngx-translate/core'; import { XtreamVodDetails } from '../../../../shared/xtream-vod-details.interface'; -import { DataService } from '../../services/data.service'; import { PlayerService } from '../../services/player.service'; import { SettingsStore } from '../../services/settings-store.service'; import { selectActivePlaylist } from '../../state/selectors'; @@ -27,15 +25,13 @@ import { SafePipe } from './safe.pipe'; ], }) export class VodDetailsComponent implements OnInit, OnDestroy { - private dataService = inject(DataService); - private dialog = inject(MatDialog); private settingsStore = inject(SettingsStore); private route = inject(ActivatedRoute); private store = inject(Store); private readonly xtreamStore = inject(XtreamStore); private playerService = inject(PlayerService); - readonly settings = this.settingsStore.getSettings(); + readonly theme = this.settingsStore.theme; private readonly selectedContentType = this.xtreamStore.selectedContentType; private readonly hideExternalInfoDialog = this.xtreamStore.hideExternalInfoDialog; diff --git a/src/app/xtream/xtream-main-container.component.ts b/src/app/xtream/xtream-main-container.component.ts index 54cba6f4..64ba0247 100644 --- a/src/app/xtream/xtream-main-container.component.ts +++ b/src/app/xtream/xtream-main-container.component.ts @@ -31,8 +31,6 @@ import { VodDetailsComponent } from './vod-details/vod-details.component'; import { MatDialog } from '@angular/material/dialog'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; -import { Router } from '@angular/router'; -import { StorageMap } from '@ngx-pwa/local-storage'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { Observable } from 'rxjs'; import { @@ -40,7 +38,6 @@ import { XtreamSerieEpisode, } from '../../../shared/xtream-serie-details.interface'; import { LiveStreamLayoutComponent } from '../portals/live-stream-layout/live-stream-layout.component'; -import { DialogService } from '../services/dialog.service'; import { PlaylistsService } from '../services/playlists.service'; import { SettingsStore } from '../services/settings-store.service'; import { VideoPlayer } from '../settings/settings.interface'; @@ -115,18 +112,15 @@ type LayoutView = ], }) export class XtreamMainContainerComponent implements OnInit { - dataService = inject(DataService); - dialog = inject(MatDialog); - dialogService = inject(DialogService); - ngZone = inject(NgZone); - playlistService = inject(PlaylistsService); - portalStore = inject(PortalStore); - router = inject(Router); - settingsStore = inject(SettingsStore); - snackBar = inject(MatSnackBar); - storage = inject(StorageMap); - store = inject(Store); - translate = inject(TranslateService); + private readonly dataService = inject(DataService); + private readonly dialog = inject(MatDialog); + private readonly ngZone = inject(NgZone); + private readonly playlistService = inject(PlaylistsService); + private readonly portalStore = inject(PortalStore); + private readonly settingsStore = inject(SettingsStore); + private readonly snackBar = inject(MatSnackBar); + private readonly store = inject(Store); + private readonly translate = inject(TranslateService); currentPlaylist = this.store.selectSignal(selectCurrentPlaylist); navigationContentTypes: ContentTypeNavigationItem[] = [ @@ -156,7 +150,7 @@ export class XtreamMainContainerComponent implements OnInit { selectedContentType = ContentType.VODS; currentLayout: LayoutView = 'category'; vodDetails!: XtreamVodDetails | XtreamSerieDetails; - settings = this.settingsStore.getSettings(); + settings = this.settingsStore; isLoading = true; searchPhrase = this.portalStore.searchPhrase(); contentId: number; @@ -344,20 +338,20 @@ export class XtreamMainContainerComponent implements OnInit { openPlayer(streamUrl: string, title: string) { this.streamUrl = streamUrl; - this.player = this.settings()?.player ?? VideoPlayer.VideoJs; + this.player = this.settingsStore.player() ?? VideoPlayer.VideoJs; if (this.player === VideoPlayer.MPV) { if (!this.hideExternalInfoDialog()) this.dialog.open(ExternalPlayerInfoDialogComponent); this.dataService.sendIpcEvent(OPEN_MPV_PLAYER, { url: streamUrl, - mpvPlayerPath: this.settings()?.mpvPlayerPath, + mpvPlayerPath: this.settingsStore.mpvPlayerPath(), }); } else if (this.player === VideoPlayer.VLC) { if (!this.hideExternalInfoDialog()) this.dialog.open(ExternalPlayerInfoDialogComponent); this.dataService.sendIpcEvent(OPEN_VLC_PLAYER, { url: streamUrl, - vlcPlayerPath: this.settings()?.vlcPlayerPath, + vlcPlayerPath: this.settingsStore.vlcPlayerPath(), }); } else { if (this.selectedContentType !== ContentType.ITV) { @@ -382,7 +376,7 @@ export class XtreamMainContainerComponent implements OnInit { playEpisode(episode: XtreamSerieEpisode) { const { serverUrl, username, password } = this.currentPlaylist(); - const player = this.settings().player; + const player = this.settingsStore.player(); const streamUrl = `${serverUrl}/series/${username}/${password}/${episode.id}.${episode.container_extension}`; if (player === VideoPlayer.MPV) { this.dataService.sendIpcEvent(OPEN_MPV_PLAYER, { url: streamUrl });