feat: improve global favorites handling and channel display logic in the UI

This commit is contained in:
4gray committed 2026-02-08 19:18:56 +01:00
1 parent c253db4cef
commit edcd7cb148
5 files changed
+38 -18

No files matched your search

@@ -1,4 +1,5 @@
import { Component, input, output } from '@angular/core';
import { MatIconButton } from '@angular/material/button';
import { MatIcon } from '@angular/material/icon';
import { TranslatePipe } from '@ngx-translate/core';
import { PlaylistSwitcherComponent, ResizableDirective } from 'components';
@@ -16,6 +17,7 @@ import { ContentCardComponent } from '../content-card/content-card.component';
CategoryViewComponent,
ContentCardComponent,
MatIcon,
MatIconButton,
PlaylistSwitcherComponent,
ResizableDirective,
TranslatePipe,
+4
View File
@@ -57,6 +57,10 @@ export class PlaylistEffects {
this.store.select(selectFavorites),
this.store.select(selectActivePlaylistId)
),
filter(
([, , playlistId]) =>
playlistId !== GLOBAL_FAVORITES_PLAYLIST_ID
),
switchMap(([, favorites, playlistId]) =>
this.playlistsService.updateFavorites(playlistId, favorites)
),
@@ -44,6 +44,7 @@ export function createFavoritesPlaylist(
playlist: {
items: channels,
},
favorites: channels.map((channel) => channel.url),
filename: 'Global favorites',
};
}
@@ -1,4 +1,4 @@
@if (_channelList) {
@if (displayedChannels().length) {
<mat-tab-group color="primary" backgroundColor="primary">
<!-- All Channels Tab -->
<mat-tab>
@@ -9,7 +9,7 @@
</div>
</ng-template>
<app-all-channels-tab
[channels]="_channelList"
[channels]="displayedChannels()"
[channelEpgMap]="channelEpgMap()"
[progressTick]="progressTick()"
[shouldShowEpg]="shouldShowEpg()"
@@ -30,7 +30,7 @@
</div>
</ng-template>
<app-groups-tab
[groupedChannels]="groupedChannels"
[groupedChannels]="groupedChannels()"
[channelEpgMap]="channelEpgMap()"
[progressTick]="progressTick()"
[shouldShowEpg]="shouldShowEpg()"
@@ -10,11 +10,10 @@ import {
signal,
} from '@angular/core';
import { MatIconModule } from '@angular/material/icon';
import { MatSnackBar } from '@angular/material/snack-bar';
import { MatTabsModule } from '@angular/material/tabs';
import { Store } from '@ngrx/store';
import { StorageMap } from '@ngx-pwa/local-storage';
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
import { TranslatePipe } from '@ngx-translate/core';
import * as _ from 'lodash';
import {
ChannelActions,
@@ -25,7 +24,13 @@ import {
} from 'm3u-state';
import { BehaviorSubject, combineLatest, map, skipWhile } from 'rxjs';
import { EpgService } from 'services';
import { Channel, EpgProgram, Settings, STORE_KEY } from 'shared-interfaces';
import {
Channel,
EpgProgram,
GLOBAL_FAVORITES_PLAYLIST_ID,
Settings,
STORE_KEY,
} from 'shared-interfaces';
import { AllChannelsTabComponent } from './all-channels-tab/all-channels-tab.component';
import { FavoritesTabComponent } from './favorites-tab/favorites-tab.component';
import { GroupsTabComponent } from './groups-tab/groups-tab.component';
@@ -47,10 +52,8 @@ import { GroupsTabComponent } from './groups-tab/groups-tab.component';
})
export class ChannelListContainerComponent implements OnInit, OnDestroy {
private readonly epgService = inject(EpgService);
private readonly snackBar = inject(MatSnackBar);
private readonly storage = inject(StorageMap);
private readonly store = inject(Store);
private readonly translateService = inject(TranslateService);
/** Map of channel ID to current EPG program */
readonly channelEpgMap = signal(new Map<string, EpgProgram | null>());
@@ -72,6 +75,7 @@ export class ChannelListContainerComponent implements OnInit, OnDestroy {
/** Channels array */
_channelList: Channel[] = [];
private readonly channelListSignal = signal<Channel[]>([]);
private channelList$ = new BehaviorSubject<Channel[]>([]);
get channelList(): Channel[] {
@@ -81,13 +85,28 @@ export class ChannelListContainerComponent implements OnInit, OnDestroy {
@Input()
set channelList(value: Channel[]) {
this._channelList = value;
this.channelListSignal.set(value);
this.channelList$.next(value);
this.groupedChannels = _.default.groupBy(value, 'group.title');
this.fetchEpgForChannels(value);
}
/** Active playlist ID as signal */
private readonly activePlaylistIdSignal =
this.store.selectSignal(selectActivePlaylistId);
/** Displayed channels - filters out unfavorited channels in global favorites view */
readonly displayedChannels = computed(() => {
const channels = this.channelListSignal();
if (this.activePlaylistIdSignal() === GLOBAL_FAVORITES_PLAYLIST_ID) {
return channels.filter((ch) => this.favoriteIds().has(ch.url));
}
return channels;
});
/** Object with channels sorted by groups */
groupedChannels: { [key: string]: Channel[] } = {};
readonly groupedChannels = computed(() =>
_.default.groupBy(this.displayedChannels(), 'group.title')
);
/** Selected channel */
readonly activeChannel = this.store.selectSignal(selectActive);
@@ -105,7 +124,8 @@ export class ChannelListContainerComponent implements OnInit, OnDestroy {
);
/** Set of favorite channel URLs for quick lookup */
readonly favoriteIds = signal<Set<string>>(new Set());
private readonly _favorites = this.store.selectSignal(selectFavorites);
readonly favoriteIds = computed(() => new Set(this._favorites()));
/** List with favorites */
favorites$ = combineLatest([
@@ -113,8 +133,6 @@ export class ChannelListContainerComponent implements OnInit, OnDestroy {
this.channelList$,
]).pipe(
map(([favoriteChannelIds, channelList]) => {
// Update the favoriteIds signal for quick lookup
this.favoriteIds.set(new Set(favoriteChannelIds));
return favoriteChannelIds
.map((favoriteChannelId) =>
channelList.find(
@@ -205,11 +223,6 @@ export class ChannelListContainerComponent implements OnInit, OnDestroy {
*/
onFavoriteToggled(event: { channel: Channel; event: MouseEvent }): void {
event.event.stopPropagation();
this.snackBar.open(
this.translateService.instant('CHANNELS.FAVORITES_UPDATED'),
undefined,
{ duration: 2000 }
);
this.store.dispatch(
FavoritesActions.updateFavorites({ channel: event.channel })
);