refactor: remove info overlay component and related files

This commit is contained in:
4gray committed 2026-04-11 10:42:31 +02:00
1 parent dd58e4a529
commit bf427f7226
4 files changed
+1 -412

No files matched your search

+1 -2
View File
@@ -4,8 +4,7 @@ export * from './lib/confirm-dialog/confirm-dialog.component';
export * from './lib/confirm-dialog/dialog.service';
export * from './lib/content-hero/content-hero.component';
export * from './lib/external-playback-dock/external-playback-dock.component';
export * from './lib/info-overlay/info-overlay.component';
export * from './lib/progress-capsule/progress-capsule.component';
export * from './lib/resizable/resizable.directive';
export * from './lib/season-container/season-container.component';
export * from './lib/watched-badge/watched-badge.component';
export * from './lib/resizable/resizable.directive';
@@ -1,71 +0,0 @@
@if (channel) {
<div
class="channel-overlay"
[class.visible]="isVisible"
[class.hidden]="!isVisible"
>
@if (epgProgram) {
<!-- Layout with EPG data -->
<div class="epg-layout">
<div class="channel-logo">
<img
[src]="epgProgram?.iconUrl || channel?.tvg?.logo"
onerror="this.style.display='none'"
alt="channel icon"
/>
</div>
<div class="content">
<div class="content-header">
<div class="program-title">
{{ epgProgram?.title }}
</div>
<div class="channel-name">
{{ channel.name }}
</div>
</div>
@if (epgProgram?.desc) {
<div class="program-description">
{{ epgProgram?.desc }}
</div>
}
<div class="program-timeline">
<div class="timeline-info">
<span class="time-start">{{ start | momentDate: 'HH:mm' }}</span>
<span class="time-separator">—</span>
<span class="time-end">{{ stop | momentDate: 'HH:mm' }}</span>
</div>
<div class="progress-track">
<div
class="progress-fill"
[ngStyle]="{
width: (finishedDuration * 100) / generalDuration + '%',
}"
></div>
</div>
</div>
</div>
</div>
} @else {
<!-- Layout without EPG data - same structure, minimal content -->
<div class="epg-layout">
<div class="channel-logo">
<img
[src]="channel?.tvg?.logo"
onerror="this.style.display='none'"
alt="channel icon"
/>
</div>
<div class="content">
<div class="content-header">
<div class="program-title no-epg-title">
{{ channel.name }}
</div>
<div class="channel-name no-epg-subtitle">
{{ 'EPG.NO_PROGRAM_INFO' | translate }}
</div>
</div>
</div>
</div>
}
</div>
}
@@ -1,235 +0,0 @@
.channel-overlay {
position: absolute;
bottom: 35px;
width: 100%;
min-height: 130px;
max-height: 200px;
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
box-sizing: border-box;
display: flex;
isolation: isolate;
padding: 0 40px;
// Gradient blur effect using pseudo-element
&::before {
content: '';
position: absolute;
inset: 0;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
mask-image: linear-gradient(
to bottom,
transparent 0%,
rgba(0, 0, 0, 0.3) 20%,
rgba(0, 0, 0, 0.8) 60%,
black 100%
);
-webkit-mask-image: linear-gradient(
to bottom,
transparent 0%,
rgba(0, 0, 0, 0.3) 20%,
rgba(0, 0, 0, 0.8) 60%,
black 100%
);
z-index: -1;
}
}
// EPG Layout with data
.epg-layout {
display: flex;
gap: 28px;
width: 100%;
align-items: flex-start;
padding: 24px 0;
animation: fadeInUp 0.5s ease-out;
}
.channel-logo {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
width: 90px;
height: 90px;
img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.4));
}
}
.content {
flex: 1;
display: flex;
flex-direction: column;
gap: 12px;
min-width: 0; // Allow text truncation
}
.content-header {
display: flex;
flex-direction: column;
gap: 6px;
animation: fadeIn 0.6s ease-out 0.1s backwards;
}
.program-title {
font-family: 'Crimson Pro', serif;
font-size: 2.25rem;
font-weight: 700;
line-height: 1.2;
color: #ffffff;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.6);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
letter-spacing: -0.02em;
}
.channel-name {
font-family: 'DM Sans', sans-serif;
font-size: 0.95rem;
font-weight: 500;
color: rgba(255, 255, 255, 0.75);
text-transform: uppercase;
letter-spacing: 0.08em;
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
}
.program-description {
font-family: 'DM Sans', sans-serif;
font-size: 1.05rem;
line-height: 1.5;
color: rgba(255, 255, 255, 0.88);
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
animation: fadeIn 0.6s ease-out 0.2s backwards;
}
.program-timeline {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 4px;
animation: fadeIn 0.6s ease-out 0.3s backwards;
}
.timeline-info {
display: flex;
align-items: center;
gap: 8px;
font-family: 'DM Sans', sans-serif;
font-size: 0.875rem;
font-weight: 500;
color: rgba(255, 255, 255, 0.9);
letter-spacing: 0.02em;
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
.time-separator {
opacity: 0.5;
margin: 0 2px;
}
.progress-track {
position: relative;
width: 100%;
height: 4px;
background: rgba(255, 255, 255, 0.15);
border-radius: 2px;
overflow: hidden;
backdrop-filter: blur(4px);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3);
}
.progress-fill {
position: absolute;
left: 0;
top: 0;
height: 100%;
background: linear-gradient(90deg, #4f46e5 0%, #7c3aed 50%, #ec4899 100%);
border-radius: 2px;
transition: width 0.3s ease-out;
box-shadow: 0 0 12px rgba(124, 58, 237, 0.6);
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(
90deg,
transparent 0%,
rgba(255, 255, 255, 0.3) 50%,
transparent 100%
);
animation: shimmer 2s ease-in-out infinite;
}
}
// Animations
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
.visible {
visibility: visible;
opacity: 1;
}
.hidden {
visibility: hidden;
opacity: 0;
transition:
visibility 0s 2s,
opacity 2s linear;
}
// No EPG state variations
.no-epg-title {
// When there's no EPG, the channel name becomes the main title
font-size: 2.5rem; // Slightly larger since it's the only content
}
.no-epg-subtitle {
// Subtle message indicating no EPG data
font-style: italic;
opacity: 0.6;
}
@@ -1,104 +0,0 @@
import { NgStyle } from '@angular/common';
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { MomentDatePipe } from '@iptvnator/pipes';
import { TranslatePipe } from '@ngx-translate/core';
import moment from 'moment';
import { Channel, EpgProgram } from 'shared-interfaces';
@Component({
imports: [MomentDatePipe, NgStyle, TranslatePipe],
selector: 'app-info-overlay',
templateUrl: './info-overlay.component.html',
styleUrls: ['./info-overlay.component.scss'],
})
export class InfoOverlayComponent implements OnChanges {
/** Active channel */
@Input() channel: Channel | undefined;
/** Current EPG program */
@Input() epgProgram: EpgProgram | undefined;
/** Visibility flag of the overlay popup */
isVisible = false;
/** Program duration */
generalDuration!: number;
/** Finished duration */
finishedDuration!: number;
/** Program start time */
start: string | undefined;
/** Program end time */
stop: string | undefined;
/** Timeout for the overlay visibility */
private currentTimeout: ReturnType<typeof setTimeout> | undefined;
/**
* Calculates the necessary information for the visualization in the overview popup
* @param changes component input changes
*/
ngOnChanges(changes: SimpleChanges): void {
if (changes['channel'] && changes['channel'].currentValue) {
clearTimeout(this.currentTimeout);
this.isVisible = true;
this.currentTimeout = setTimeout(() => {
this.isVisible = false;
}, 10000);
}
if (changes['epgProgram']) {
if (changes['epgProgram'].currentValue) {
const { stop, start } = changes['epgProgram'].currentValue;
this.setProgramDuration(start, stop);
} else {
// Reset EPG data when no program is available
this.start = undefined;
this.stop = undefined;
this.generalDuration = 0;
this.finishedDuration = 0;
}
}
}
/**
* Calculates and sets the duration of the program for the progress bar visualization
* @param start program start time
* @param stop program stop time
*/
setProgramDuration(start: string | number, stop: string | number): void {
const stopMoment = moment(stop, 'YYYYMMDDHHmm ZZ');
const startMoment = moment(start, 'YYYYMMDDHHmm ZZ');
this.stop = stopMoment.toISOString();
this.start = startMoment.toISOString();
const timeNow = moment(Date.now());
this.generalDuration = moment
.duration(stopMoment.diff(startMoment))
.asMilliseconds();
this.finishedDuration = moment
.duration(stopMoment.diff(timeNow))
.asMilliseconds();
}
/**
* Manually shows the overlay (triggered by user action like 'I' key or info button)
* Toggles visibility if already shown, or shows for 10 seconds if hidden
*/
showOverlay(): void {
if (this.isVisible) {
// If already visible, hide it (toggle behavior)
clearTimeout(this.currentTimeout);
this.isVisible = false;
} else {
// Show overlay for 10 seconds
clearTimeout(this.currentTimeout);
this.isVisible = true;
this.currentTimeout = setTimeout(() => {
this.isVisible = false;
}, 10000);
}
}
}