- @if ( - currentLayout === 'vod-details' || - currentLayout === 'serial-details' - ) { - + } @else if (!isItvCategorySelected) { + } @if (!isItvCategorySelected) { - {{ selectedCategoryTitle() }} } diff --git a/apps/web/src/app/stalker/stalker-main-container.component.ts b/apps/web/src/app/stalker/stalker-main-container.component.ts index 16d39fe1c..5396e8223 100644 --- a/apps/web/src/app/stalker/stalker-main-container.component.ts +++ b/apps/web/src/app/stalker/stalker-main-container.component.ts @@ -236,4 +236,8 @@ export class StalkerMainContainerComponent { backToCategories() { this.stalkerStore.setSelectedCategory(null); } + + goBackToList() { + this.stalkerStore.clearSelectedItem(); + } } diff --git a/apps/web/src/app/stalker/stalker.store.ts b/apps/web/src/app/stalker/stalker.store.ts index 1da396a13..62335697b 100644 --- a/apps/web/src/app/stalker/stalker.store.ts +++ b/apps/web/src/app/stalker/stalker.store.ts @@ -332,6 +332,14 @@ export const StalkerStore = signalStore( selectedItem, }); }, + clearSelectedItem() { + patchState(store, { + selectedVodId: undefined, + selectedSerialId: undefined, + selectedItvId: undefined, + selectedItem: undefined, + }); + }, setCategories(type: 'vod' | 'series' | 'itv', categories: any[]) { if (type === 'vod') { patchState(store, { vodCategories: categories }); diff --git a/apps/web/src/app/xtream-tauri/detail-view.scss b/apps/web/src/app/xtream-tauri/detail-view.scss index f789ce642..fabb0fd35 100644 --- a/apps/web/src/app/xtream-tauri/detail-view.scss +++ b/apps/web/src/app/xtream-tauri/detail-view.scss @@ -23,7 +23,6 @@ max-width: 100%; box-sizing: border-box; height: auto; - min-height: calc(100vh - 73px); &::before { content: ''; diff --git a/apps/web/src/app/xtream/detail-view.scss b/apps/web/src/app/xtream/detail-view.scss index 994a61046..c53623e16 100644 --- a/apps/web/src/app/xtream/detail-view.scss +++ b/apps/web/src/app/xtream/detail-view.scss @@ -2,10 +2,11 @@ width: 100%; display: flex; flex-direction: column; - overflow: auto; } .details { + overflow: visible; + h2 { word-wrap: break-word; } @@ -16,6 +17,7 @@ grid-gap: 10px; grid-template-columns: 320px 1fr; padding: 0 10px; + overflow: visible; &::before { content: ''; diff --git a/libs/ui/components/src/lib/art-player/art-player.component.ts b/libs/ui/components/src/lib/art-player/art-player.component.ts index 890d12ece..4f6580452 100644 --- a/libs/ui/components/src/lib/art-player/art-player.component.ts +++ b/libs/ui/components/src/lib/art-player/art-player.component.ts @@ -38,6 +38,7 @@ export class ArtPlayerComponent implements OnInit, OnDestroy, OnChanges { @Input() showCaptions = false; private player!: Artplayer; + private hls: Hls | null = null; private readonly elementRef = inject(ElementRef); @@ -46,20 +47,26 @@ export class ArtPlayerComponent implements OnInit, OnDestroy, OnChanges { } ngOnDestroy(): void { - if (this.player) { - this.player.destroy(); - } + this.destroyPlayer(); } ngOnChanges(changes: SimpleChanges): void { if (changes['channel'] && !changes['channel'].firstChange) { - if (this.player) { - this.player.destroy(); - } + this.destroyPlayer(); this.initPlayer(); } } + private destroyPlayer(): void { + if (this.hls) { + this.hls.destroy(); + this.hls = null; + } + if (this.player) { + this.player.destroy(); + } + } + private initPlayer(): void { const el = this.elementRef.nativeElement.querySelector( '.artplayer-container' @@ -89,11 +96,14 @@ export class ArtPlayerComponent implements OnInit, OnDestroy, OnChanges { mutex: true, theme: '#ff0000', customType: { - m3u8: function (video: HTMLVideoElement, url: string) { + m3u8: (video: HTMLVideoElement, url: string) => { if (Hls.isSupported()) { - const hls = new Hls(); - hls.loadSource(url); - hls.attachMedia(video); + if (this.hls) { + this.hls.destroy(); + } + this.hls = new Hls(); + this.hls.loadSource(url); + this.hls.attachMedia(video); } else if ( video.canPlayType('application/vnd.apple.mpegurl') ) { diff --git a/libs/ui/components/src/lib/epg-list/epg-list-item/epg-list-item.component.scss b/libs/ui/components/src/lib/epg-list/epg-list-item/epg-list-item.component.scss index 62700d796..fb8d24679 100644 --- a/libs/ui/components/src/lib/epg-list/epg-list-item/epg-list-item.component.scss +++ b/libs/ui/components/src/lib/epg-list/epg-list-item/epg-list-item.component.scss @@ -1,30 +1,106 @@ +:host { + display: flex; + align-items: center; + gap: 8px; + width: 100%; +} + .time { - color: #666; - border-bottom: 1px solid #ccc; - font-size: 0.8em; - display: inline-block; - margin-bottom: 5px; - margin-left: 5px; + font-family: 'DM Sans', sans-serif; + font-size: 0.7rem; + opacity: 0.6; + letter-spacing: 0.02em; + line-height: 1; + white-space: nowrap; } .badge { - border-radius: 2px; - padding: 2px 6px; - color: #fff; + border-radius: 4px; + padding: 3px 8px; + color: white; text-transform: uppercase; - margin-left: 6px; - font-size: 0.8em; + font-size: 10px; + font-weight: 600; + letter-spacing: 0.08em; + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 40px; + transition: all 0.2s ease; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); } .live { - background-color: red; + background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); + animation: livePulse 2s ease-in-out infinite; + box-shadow: 0 0 12px rgba(239, 68, 68, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3); + + &::after { + content: '●'; + margin-left: 4px; + animation: blink 1.5s ease-in-out infinite; + } } .rec { - background-color: grey; + background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%); + box-shadow: 0 0 12px rgba(139, 92, 246, 0.4), 0 2px 8px rgba(0, 0, 0, 0.3); + + &::after { + content: '◉'; + margin-left: 4px; + } } .description-icon { - float: right; + margin-left: auto; cursor: pointer; + opacity: 0.6; + transition: all 0.2s ease; + padding: 4px; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + + mat-icon { + font-size: 18px; + width: 18px; + height: 18px; + } + + &:hover { + opacity: 1; + background: rgba(0, 0, 0, 0.04); + } + + &:active { + transform: scale(0.95); + } +} + +// Dark theme support +::ng-deep .dark-theme { + .description-icon:hover { + background: rgba(255, 255, 255, 0.04); + } +} + +// Animations +@keyframes livePulse { + 0%, 100% { + box-shadow: 0 0 12px rgba(239, 68, 68, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3); + } + 50% { + box-shadow: 0 0 20px rgba(239, 68, 68, 0.8), 0 2px 12px rgba(0, 0, 0, 0.4); + } +} + +@keyframes blink { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.3; + } } diff --git a/libs/ui/components/src/lib/epg-list/epg-list.component.html b/libs/ui/components/src/lib/epg-list/epg-list.component.html index 260047d49..58cfeb869 100644 --- a/libs/ui/components/src/lib/epg-list/epg-list.component.html +++ b/libs/ui/components/src/lib/epg-list/epg-list.component.html @@ -23,7 +23,7 @@