mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-15 21:56:18 -08:00
Update info-overlay template to read simplified EPG fields and avoid indexing into arrays for title, icon and description so bindings are resilient and clearer: - Use epgProgram?.iconUrl instead of epgProgram?.icon?.[0]?.src - Use epgProgram?.title instead of epgProgram?.title?.[0]?.value - Use epgProgram?.desc instead of epgProgram?.desc?.[0]?.value - Guard description rendering with epgProgram?.desc Adjust multi-epg header layout and add search UI styles to support desktop window chrome and draggable regions: - Add left padding to #epg-navigation for macOS traffic lights, enable backdrop drag via -webkit-app-region: drag and set z-index to keep header above content. - Exclude interactive elements (buttons, search inputs) from drag by applying -webkit-app-region: no-drag to maintain native interactions. - Add extensive styles for a channel search container (search input, icons, clear button, filter count, animations) and scaffold a program search container. These changes improve robustness of EPG data rendering and add styled search controls while ensuring proper window dragging behavior on desktop builds.
93 lines
2.8 KiB
TypeScript
93 lines
2.8 KiB
TypeScript
import { Injectable, signal, computed } from '@angular/core';
|
|
|
|
export interface EpgImportProgress {
|
|
url: string;
|
|
status: 'queued' | 'loading' | 'complete' | 'error';
|
|
stats?: { totalChannels: number; totalPrograms: number };
|
|
error?: string;
|
|
queuePosition?: number;
|
|
}
|
|
|
|
/**
|
|
* Service to track EPG import progress across multiple sources.
|
|
* Uses signals for reactive state management.
|
|
*/
|
|
@Injectable({ providedIn: 'root' })
|
|
export class EpgProgressService {
|
|
private _imports = signal<Map<string, EpgImportProgress>>(new Map());
|
|
private _initialized = false;
|
|
|
|
/** All current import progress entries */
|
|
readonly imports = computed(() => Array.from(this._imports().values()));
|
|
|
|
/** Whether any imports are currently loading */
|
|
readonly hasActiveImports = computed(() =>
|
|
this.imports().some((i) => i.status === 'loading')
|
|
);
|
|
|
|
/** Count of active imports */
|
|
readonly activeCount = computed(() =>
|
|
this.imports().filter((i) => i.status === 'loading').length
|
|
);
|
|
|
|
/** Queued imports waiting to be processed */
|
|
readonly queuedImports = computed(() =>
|
|
this.imports()
|
|
.filter((i) => i.status === 'queued')
|
|
.sort((a, b) => (a.queuePosition ?? 0) - (b.queuePosition ?? 0))
|
|
);
|
|
|
|
/** Count of queued imports */
|
|
readonly queuedCount = computed(() => this.queuedImports().length);
|
|
|
|
/** Whether the panel should be visible */
|
|
readonly isVisible = computed(() => this.imports().length > 0);
|
|
|
|
constructor() {
|
|
this.initializeListener();
|
|
}
|
|
|
|
private initializeListener(): void {
|
|
if (this._initialized) return;
|
|
this._initialized = true;
|
|
|
|
// Listen for EPG progress updates from Electron
|
|
if (window.electron?.onEpgProgress) {
|
|
window.electron.onEpgProgress((data) => {
|
|
this.updateProgress(data);
|
|
});
|
|
}
|
|
}
|
|
|
|
private updateProgress(progress: EpgImportProgress): void {
|
|
this._imports.update((current) => {
|
|
const updated = new Map(current);
|
|
updated.set(progress.url, progress);
|
|
return updated;
|
|
});
|
|
|
|
// Auto-remove completed/error entries after delay
|
|
if (progress.status === 'complete' || progress.status === 'error') {
|
|
setTimeout(() => this.removeImport(progress.url), 5000);
|
|
}
|
|
}
|
|
|
|
private removeImport(url: string): void {
|
|
this._imports.update((current) => {
|
|
const updated = new Map(current);
|
|
updated.delete(url);
|
|
return updated;
|
|
});
|
|
}
|
|
|
|
/** Manually dismiss an import notification */
|
|
dismiss(url: string): void {
|
|
this.removeImport(url);
|
|
}
|
|
|
|
/** Dismiss all notifications */
|
|
dismissAll(): void {
|
|
this._imports.set(new Map());
|
|
}
|
|
}
|