mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
refactor: optimize progress calculation and enhance enriched channel retrieval in favorites and groups tabs
This commit is contained in:
1 parent
bffcc24f67
commit
bb1f3af082
5 files changed
+70
-22
No files matched your search
+16
-4
@@ -139,14 +139,26 @@ export class AllChannelsTabComponent implements OnDestroy {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const now = new Date().getTime();
|
||||
const now = Date.now();
|
||||
const start = new Date(epgProgram.start).getTime();
|
||||
const stop = new Date(epgProgram.stop).getTime();
|
||||
|
||||
const total = stop - start;
|
||||
const elapsed = now - start;
|
||||
// Validate start/stop are finite numbers
|
||||
if (!Number.isFinite(start) || !Number.isFinite(stop)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Math.min(100, Math.max(0, (elapsed / total) * 100));
|
||||
const total = stop - start;
|
||||
|
||||
// Bail out if duration is zero or negative
|
||||
if (total <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Clamp elapsed to [0, total]
|
||||
const elapsed = Math.min(total, Math.max(0, now - start));
|
||||
|
||||
return Math.round((elapsed / total) * 100);
|
||||
}
|
||||
|
||||
trackByFn(_: number, channel: Channel): string {
|
||||
|
||||
+2
-2
@@ -4,9 +4,9 @@
|
||||
(cdkDropListDropped)="onDrop($event)"
|
||||
id="favorites-list"
|
||||
>
|
||||
@if (enrichedFavorites.length > 0) {
|
||||
@if (enrichedFavorites().length > 0) {
|
||||
@for (
|
||||
channel of enrichedFavorites;
|
||||
channel of enrichedFavorites();
|
||||
track trackByFn($index, channel);
|
||||
let i = $index
|
||||
) {
|
||||
|
||||
+15
-13
@@ -6,24 +6,21 @@ import {
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
input,
|
||||
output,
|
||||
} from '@angular/core';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { Channel, EpgProgram } from 'shared-interfaces';
|
||||
import { ChannelListItemComponent } from '../channel-list-item/channel-list-item.component';
|
||||
import { EnrichedChannel } from '../all-channels-tab/all-channels-tab.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'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [
|
||||
ChannelListItemComponent,
|
||||
DragDropModule,
|
||||
TranslatePipe,
|
||||
],
|
||||
imports: [ChannelListItemComponent, DragDropModule, TranslatePipe],
|
||||
})
|
||||
export class FavoritesTabComponent {
|
||||
/** Favorite channels */
|
||||
@@ -45,21 +42,24 @@ export class FavoritesTabComponent {
|
||||
readonly channelSelected = output<Channel>();
|
||||
|
||||
/** Emits when favorite is toggled (removed) */
|
||||
readonly favoriteToggled = output<{ channel: Channel; event: MouseEvent }>();
|
||||
readonly favoriteToggled = output<{
|
||||
channel: Channel;
|
||||
event: MouseEvent;
|
||||
}>();
|
||||
|
||||
/** Emits when favorites order changes via drag-drop */
|
||||
readonly favoritesReordered = output<string[]>();
|
||||
|
||||
/**
|
||||
* Gets enriched favorites with EPG data
|
||||
* Computed signal for enriched favorites with EPG data
|
||||
*/
|
||||
get enrichedFavorites(): EnrichedChannel[] {
|
||||
readonly enrichedFavorites = computed(() => {
|
||||
const favorites = this.favorites();
|
||||
const epgMap = this.channelEpgMap();
|
||||
// Read progressTick to trigger recalculation
|
||||
this.progressTick();
|
||||
|
||||
return favorites.map(channel => {
|
||||
return favorites.map((channel) => {
|
||||
const channelId = channel?.tvg?.id?.trim() || channel?.name?.trim();
|
||||
const epgProgram = channelId ? epgMap.get(channelId) : null;
|
||||
return {
|
||||
@@ -68,12 +68,14 @@ export class FavoritesTabComponent {
|
||||
progressPercentage: this.calculateProgress(epgProgram),
|
||||
} as EnrichedChannel;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Calculates progress percentage for an EPG program
|
||||
*/
|
||||
private calculateProgress(epgProgram: EpgProgram | null | undefined): number {
|
||||
private calculateProgress(
|
||||
epgProgram: EpgProgram | null | undefined
|
||||
): number {
|
||||
if (!epgProgram) {
|
||||
return 0;
|
||||
}
|
||||
@@ -103,6 +105,6 @@ export class FavoritesTabComponent {
|
||||
onDrop(event: CdkDragDrop<Channel[]>): void {
|
||||
const favorites = [...this.favorites()];
|
||||
moveItemInArray(favorites, event.previousIndex, event.currentIndex);
|
||||
this.favoritesReordered.emit(favorites.map(item => item.url));
|
||||
this.favoritesReordered.emit(favorites.map((item) => item.url));
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
|
||||
<ng-template matExpansionPanelContent>
|
||||
@let enrichedGroupChannels =
|
||||
getEnrichedGroupChannels(groups.value);
|
||||
getEnrichedGroupChannels(groups.value, groups.key);
|
||||
@let limit = getGroupLimit(groups.key);
|
||||
<div class="group-channels">
|
||||
@for (
|
||||
|
||||
+36
-2
@@ -4,6 +4,7 @@ import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
computed,
|
||||
inject,
|
||||
input,
|
||||
NgZone,
|
||||
@@ -176,13 +177,46 @@ export class GroupsTabComponent implements AfterViewInit, OnDestroy {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets enriched channels for a specific group
|
||||
* Computed signal that memoizes enriched channels for all groups
|
||||
*/
|
||||
getEnrichedGroupChannels(channels: Channel[]): EnrichedChannel[] {
|
||||
private readonly enrichedGroupChannelsMap = computed(() => {
|
||||
const grouped = this.groupedChannels();
|
||||
const epgMap = this.channelEpgMap();
|
||||
// Read progressTick to create dependency for progress refresh
|
||||
this.progressTick();
|
||||
|
||||
const result = new Map<string, EnrichedChannel[]>();
|
||||
for (const [groupKey, channels] of Object.entries(grouped)) {
|
||||
result.set(
|
||||
groupKey,
|
||||
channels.map((channel) => {
|
||||
const channelId =
|
||||
channel?.tvg?.id?.trim() || channel?.name?.trim();
|
||||
const epgProgram = channelId ? epgMap.get(channelId) : null;
|
||||
return {
|
||||
...channel,
|
||||
epgProgram,
|
||||
progressPercentage: this.calculateProgress(epgProgram),
|
||||
} as EnrichedChannel;
|
||||
})
|
||||
);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
/**
|
||||
* Gets enriched channels for a specific group from the memoized map
|
||||
*/
|
||||
getEnrichedGroupChannels(
|
||||
channels: Channel[],
|
||||
groupKey?: string
|
||||
): EnrichedChannel[] {
|
||||
// If groupKey provided, use memoized map
|
||||
if (groupKey !== undefined) {
|
||||
return this.enrichedGroupChannelsMap().get(groupKey) || [];
|
||||
}
|
||||
// Fallback for direct channel array (shouldn't happen with proper template usage)
|
||||
const epgMap = this.channelEpgMap();
|
||||
return channels.map((channel) => {
|
||||
const channelId = channel?.tvg?.id?.trim() || channel?.name?.trim();
|
||||
const epgProgram = channelId ? epgMap.get(channelId) : null;
|
||||
|
||||
Reference in new issue
Block a user