mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
refactor: remove info overlay component and related files
This commit is contained in:
1 parent
dd58e4a529
commit
bf427f7226
4 files changed
+1
-412
No files matched your search
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user