();
diff --git a/libs/ui/components/src/lib/channel-list-container/channel-list-container.component.html b/libs/ui/components/src/lib/channel-list-container/channel-list-container.component.html
index d165d02f5..a9f69725f 100644
--- a/libs/ui/components/src/lib/channel-list-container/channel-list-container.component.html
+++ b/libs/ui/components/src/lib/channel-list-container/channel-list-container.component.html
@@ -1,14 +1,16 @@
@if (displayedChannels().length) {
-
-
-
-
- list
-
- {{ 'CHANNELS.ALL_CHANNELS' | translate }}
-
-
-
-
-
-
-
- category
-
- {{ 'CHANNELS.GROUPS' | translate }}
-
-
-
-
-
-
- @let showFavorites = (playlistId$ | async) !== 'GLOBAL_FAVORITES';
- @if (showFavorites) {
-
-
- star
-
- {{ 'CHANNELS.FAVORITES' | translate }}
-
-
- @let favorites = favorites$ | async;
- @if (favorites) {
-
- }
-
}
-
+ @case ('favorites') {
+ @let favorites = favorites$ | async;
+ @if (favorites) {
+
+ }
+ }
+ }
} @else {
sync
diff --git a/libs/ui/components/src/lib/channel-list-container/channel-list-container.component.scss b/libs/ui/components/src/lib/channel-list-container/channel-list-container.component.scss
index 7615cfc41..8a852b2ba 100644
--- a/libs/ui/components/src/lib/channel-list-container/channel-list-container.component.scss
+++ b/libs/ui/components/src/lib/channel-list-container/channel-list-container.component.scss
@@ -1,5 +1,6 @@
:host {
- display: block;
+ display: flex;
+ flex-direction: column;
width: 100%;
height: 100%;
container-type: inline-size;
@@ -52,3 +53,33 @@
display: block;
}
}
+
+// ─── Header ───────────────────────────────────────────────────────────────────
+.context-header {
+ padding: 12px 16px 8px;
+ flex-shrink: 0;
+}
+
+.context-header__top {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+
+ h2 {
+ margin: 0;
+ font-size: 0.94rem;
+ font-weight: 600;
+ letter-spacing: -0.01em;
+ color: var(--mat-sys-on-surface);
+ }
+}
+
+// ─── Divider ──────────────────────────────────────────────────────────────────
+.context-divider {
+ height: 1px;
+ background: var(--mat-sys-outline-variant);
+ margin: 0 0 6px;
+ opacity: 0.6;
+ flex-shrink: 0;
+}
diff --git a/libs/ui/components/src/lib/channel-list-container/channel-list-container.component.ts b/libs/ui/components/src/lib/channel-list-container/channel-list-container.component.ts
index 23b1e18be..bd9c1facb 100644
--- a/libs/ui/components/src/lib/channel-list-container/channel-list-container.component.ts
+++ b/libs/ui/components/src/lib/channel-list-container/channel-list-container.component.ts
@@ -5,12 +5,14 @@ import {
computed,
inject,
Input,
+ input,
OnDestroy,
OnInit,
signal,
} from '@angular/core';
+import { toSignal } from '@angular/core/rxjs-interop';
import { MatIconModule } from '@angular/material/icon';
-import { MatTabsModule } from '@angular/material/tabs';
+import { NavigationEnd, Router } from '@angular/router';
import { Store } from '@ngrx/store';
import { StorageMap } from '@ngx-pwa/local-storage';
import { TranslatePipe } from '@ngx-translate/core';
@@ -22,18 +24,12 @@ import {
selectActivePlaylistId,
selectFavorites,
} from 'm3u-state';
-import { BehaviorSubject, combineLatest, map, skipWhile } from 'rxjs';
+import { BehaviorSubject, combineLatest, filter, map, skipWhile } from 'rxjs';
import { EpgService } from 'services';
-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';
+import { Channel, EpgProgram, Settings, STORE_KEY } from 'shared-interfaces';
+import { AllChannelsViewComponent } from './all-channels-view/all-channels-view.component';
+import { FavoritesViewComponent } from './favorites-view/favorites-view.component';
+import { GroupsViewComponent } from './groups-view/groups-view.component';
@Component({
selector: 'app-channel-list-container',
@@ -41,12 +37,11 @@ import { GroupsTabComponent } from './groups-tab/groups-tab.component';
styleUrls: ['./channel-list-container.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
- AllChannelsTabComponent,
+ AllChannelsViewComponent,
CommonModule,
- FavoritesTabComponent,
- GroupsTabComponent,
+ FavoritesViewComponent,
+ GroupsViewComponent,
MatIconModule,
- MatTabsModule,
TranslatePipe,
],
})
@@ -54,6 +49,7 @@ export class ChannelListContainerComponent implements OnInit, OnDestroy {
private readonly epgService = inject(EpgService);
private readonly storage = inject(StorageMap);
private readonly store = inject(Store);
+ private readonly router = inject(Router);
/** Map of channel ID to current EPG program */
readonly channelEpgMap = signal(new Map());
@@ -73,6 +69,30 @@ export class ChannelListContainerComponent implements OnInit, OnDestroy {
/** Item size for virtual scroll - compact when no EPG */
readonly itemSize = computed(() => (this.shouldShowEpg() ? 68 : 48));
+ /** Active view (all, groups, favorites) */
+ readonly activeView = input('all');
+
+ readonly currentUrl = toSignal(
+ this.router.events.pipe(
+ filter((event) => event instanceof NavigationEnd),
+ map((event) => (event as NavigationEnd).urlAfterRedirects)
+ ),
+ { initialValue: this.router.url }
+ );
+
+ readonly viewTitle = computed(() => {
+ const view = this.activeView();
+ const url = this.currentUrl();
+ if (view === 'all') return 'CHANNELS.ALL_CHANNELS';
+ if (view === 'groups') return 'CHANNELS.GROUPS';
+ if (view === 'favorites') {
+ return url.includes('/workspace/global-favorites')
+ ? 'HOME.PLAYLISTS.GLOBAL_FAVORITES'
+ : 'CHANNELS.FAVORITES';
+ }
+ return '';
+ });
+
/** Channels array */
_channelList: Channel[] = [];
private readonly channelListSignal = signal([]);
@@ -98,11 +118,7 @@ export class ChannelListContainerComponent implements OnInit, OnDestroy {
/** 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;
+ return this.channelListSignal();
});
/** Object with channels sorted by groups */
diff --git a/libs/ui/components/src/lib/channel-list-container/favorites-tab/favorites-tab.component.html b/libs/ui/components/src/lib/channel-list-container/favorites-view/favorites-view.component.html
similarity index 100%
rename from libs/ui/components/src/lib/channel-list-container/favorites-tab/favorites-tab.component.html
rename to libs/ui/components/src/lib/channel-list-container/favorites-view/favorites-view.component.html
diff --git a/libs/ui/components/src/lib/channel-list-container/favorites-tab/favorites-tab.component.scss b/libs/ui/components/src/lib/channel-list-container/favorites-view/favorites-view.component.scss
similarity index 67%
rename from libs/ui/components/src/lib/channel-list-container/favorites-tab/favorites-tab.component.scss
rename to libs/ui/components/src/lib/channel-list-container/favorites-view/favorites-view.component.scss
index 9cbd2f7b0..3e8caad02 100644
--- a/libs/ui/components/src/lib/channel-list-container/favorites-tab/favorites-tab.component.scss
+++ b/libs/ui/components/src/lib/channel-list-container/favorites-view/favorites-view.component.scss
@@ -1,6 +1,15 @@
+:host {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ min-height: 0;
+}
+
#favorites-list {
- height: calc(100vh - 105px);
+ flex: 1;
+ min-height: 0;
overflow-x: hidden;
+ overflow-y: auto;
}
.channel-list {
diff --git a/libs/ui/components/src/lib/channel-list-container/favorites-tab/favorites-tab.component.ts b/libs/ui/components/src/lib/channel-list-container/favorites-view/favorites-view.component.ts
similarity index 92%
rename from libs/ui/components/src/lib/channel-list-container/favorites-tab/favorites-tab.component.ts
rename to libs/ui/components/src/lib/channel-list-container/favorites-view/favorites-view.component.ts
index ce882a472..a8d45f25d 100644
--- a/libs/ui/components/src/lib/channel-list-container/favorites-tab/favorites-tab.component.ts
+++ b/libs/ui/components/src/lib/channel-list-container/favorites-view/favorites-view.component.ts
@@ -12,17 +12,17 @@ import {
} from '@angular/core';
import { TranslatePipe } from '@ngx-translate/core';
import { Channel, EpgProgram } from 'shared-interfaces';
-import { EnrichedChannel } from '../all-channels-tab/all-channels-tab.component';
+import { EnrichedChannel } from '../all-channels-view/all-channels-view.component';
import { ChannelListItemComponent } from '../channel-list-item/channel-list-item.component';
@Component({
- selector: 'app-favorites-tab',
- templateUrl: './favorites-tab.component.html',
- styleUrls: ['./favorites-tab.component.scss'],
+ selector: 'app-favorites-view',
+ templateUrl: './favorites-view.component.html',
+ styleUrls: ['./favorites-view.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ChannelListItemComponent, DragDropModule, TranslatePipe],
})
-export class FavoritesTabComponent {
+export class FavoritesViewComponent {
/** Favorite channels */
readonly favorites = input.required();
diff --git a/libs/ui/components/src/lib/channel-list-container/groups-tab/groups-tab.component.html b/libs/ui/components/src/lib/channel-list-container/groups-view/groups-view.component.html
similarity index 100%
rename from libs/ui/components/src/lib/channel-list-container/groups-tab/groups-tab.component.html
rename to libs/ui/components/src/lib/channel-list-container/groups-view/groups-view.component.html
diff --git a/libs/ui/components/src/lib/channel-list-container/groups-tab/groups-tab.component.scss b/libs/ui/components/src/lib/channel-list-container/groups-view/groups-view.component.scss
similarity index 83%
rename from libs/ui/components/src/lib/channel-list-container/groups-tab/groups-tab.component.scss
rename to libs/ui/components/src/lib/channel-list-container/groups-view/groups-view.component.scss
index 90ea1a540..b5fca9076 100644
--- a/libs/ui/components/src/lib/channel-list-container/groups-tab/groups-tab.component.scss
+++ b/libs/ui/components/src/lib/channel-list-container/groups-view/groups-view.component.scss
@@ -1,6 +1,15 @@
+:host {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ min-height: 0;
+}
+
#groups-list {
- height: calc(100vh - 105px);
+ flex: 1;
+ min-height: 0;
overflow-x: hidden;
+ overflow-y: auto;
}
.channel-list {
diff --git a/libs/ui/components/src/lib/channel-list-container/groups-tab/groups-tab.component.ts b/libs/ui/components/src/lib/channel-list-container/groups-view/groups-view.component.ts
similarity index 96%
rename from libs/ui/components/src/lib/channel-list-container/groups-tab/groups-tab.component.ts
rename to libs/ui/components/src/lib/channel-list-container/groups-view/groups-view.component.ts
index 0c9bef53a..922e4dd9a 100644
--- a/libs/ui/components/src/lib/channel-list-container/groups-tab/groups-tab.component.ts
+++ b/libs/ui/components/src/lib/channel-list-container/groups-view/groups-view.component.ts
@@ -16,13 +16,13 @@ import { MatExpansionModule } from '@angular/material/expansion';
import { MatIconModule } from '@angular/material/icon';
import { TranslatePipe } from '@ngx-translate/core';
import { Channel, EpgProgram } from 'shared-interfaces';
-import { EnrichedChannel } from '../all-channels-tab/all-channels-tab.component';
+import { EnrichedChannel } from '../all-channels-view/all-channels-view.component';
import { ChannelListItemComponent } from '../channel-list-item/channel-list-item.component';
@Component({
- selector: 'app-groups-tab',
- templateUrl: './groups-tab.component.html',
- styleUrls: ['./groups-tab.component.scss'],
+ selector: 'app-groups-view',
+ templateUrl: './groups-view.component.html',
+ styleUrls: ['./groups-view.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
ChannelListItemComponent,
@@ -33,7 +33,7 @@ import { ChannelListItemComponent } from '../channel-list-item/channel-list-item
TranslatePipe,
],
})
-export class GroupsTabComponent implements AfterViewInit, OnDestroy {
+export class GroupsViewComponent implements AfterViewInit, OnDestroy {
private readonly cdr = inject(ChangeDetectorRef);
private readonly ngZone = inject(NgZone);
diff --git a/libs/ui/components/src/lib/video-player/sidebar/sidebar.component.html b/libs/ui/components/src/lib/video-player/sidebar/sidebar.component.html
index 1dabc5fd1..6fbdcb0df 100644
--- a/libs/ui/components/src/lib/video-player/sidebar/sidebar.component.html
+++ b/libs/ui/components/src/lib/video-player/sidebar/sidebar.component.html
@@ -17,4 +17,7 @@
}
-
+
diff --git a/libs/ui/components/src/lib/video-player/sidebar/sidebar.component.ts b/libs/ui/components/src/lib/video-player/sidebar/sidebar.component.ts
index 0b899a43d..7635ea230 100644
--- a/libs/ui/components/src/lib/video-player/sidebar/sidebar.component.ts
+++ b/libs/ui/components/src/lib/video-player/sidebar/sidebar.component.ts
@@ -27,6 +27,7 @@ import { ChannelListContainerComponent } from './../../channel-list-container/ch
export class SidebarComponent {
readonly channels = input([]);
readonly showPlaylistHeader = input(true);
+ readonly activeView = input('all');
private readonly store = inject(Store);
private readonly translate = inject(TranslateService);
diff --git a/libs/ui/components/src/lib/video-player/toolbar/toolbar.component.html b/libs/ui/components/src/lib/video-player/toolbar/toolbar.component.html
index 8b14a972a..9db983449 100644
--- a/libs/ui/components/src/lib/video-player/toolbar/toolbar.component.html
+++ b/libs/ui/components/src/lib/video-player/toolbar/toolbar.component.html
@@ -7,7 +7,7 @@
>
{{ isLeftDrawerOpened ? 'menu' : 'menu_open' }}