import { ChangeDetectionStrategy, Component, effect, inject, signal, untracked } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterLink } from '@angular/router'; import { YoutubeApiService } from '../../services/youtube-api.service'; import { Video } from '../../models/video.model'; import { InstanceService } from '../../services/instance.service'; import { InfiniteAnchorComponent } from '../shared/infinite-anchor/infinite-anchor.component'; import { formatRelativeFr } from '../../utils/date.util'; import { TranslatePipe } from '../../pipes/translate.pipe'; import { LikeButtonComponent } from '../shared/components/like-button/like-button.component'; @Component({ selector: 'app-home', templateUrl: './home.component.html', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ CommonModule, RouterLink, InfiniteAnchorComponent, TranslatePipe, LikeButtonComponent ] }) export class HomeComponent { private apiService = inject(YoutubeApiService); private instances = inject(InstanceService); trendingVideos = signal([]); loading = signal(true); busyMore = signal(false); nextCursor = signal(null); notice = signal(null); constructor() { this.reloadTrending(); // React to provider/region changes to refresh trending automatically effect(() => { // Rerun when these signals change const provider = this.instances.selectedProvider(); const region = this.instances.region(); const ptInstance = this.instances.activePeerTubeInstance(); // Reset notice and trigger reload, but without re-triggering the effect untracked(() => { this.notice.set(null); this.reloadTrending(); }); }, { allowSignalWrites: true }); } reloadTrending() { const readiness = this.instances.getProviderReadiness(); if (!readiness.ready) { this.notice.set(readiness.reason || 'Le provider sélectionné n\'est pas prêt.'); this.trendingVideos.set([]); this.nextCursor.set(null); this.loading.set(false); return; } this.loading.set(true); this.trendingVideos.set([]); this.nextCursor.set(null); // Important: reset busy flag so a previous pending state doesn't block new loads this.busyMore.set(false); this.fetchNextPage(); } fetchNextPage() { if (this.busyMore()) return; const readiness = this.instances.getProviderReadiness(); if (!readiness.ready) { if (!this.notice()) this.notice.set(readiness.reason || 'Le provider sélectionné n\'est pas prêt.'); return; } this.busyMore.set(true); this.apiService.getTrendingPage(this.nextCursor()).subscribe(res => { const merged = [...this.trendingVideos(), ...res.items]; this.trendingVideos.set(merged); this.nextCursor.set(res.nextCursor || null); this.busyMore.set(false); this.loading.set(false); const provider = this.instances.selectedProvider(); if (merged.length === 0 && !this.notice()) { const readiness2 = this.instances.getProviderReadiness(); if (!readiness2.ready) { this.notice.set(readiness2.reason || 'Le provider sélectionné n\'est pas prêt.'); } else if (provider === 'youtube') { this.notice.set('Aucune vidéo tendance YouTube chargée. Vérifiez que votre YOUTUBE_API_KEY est valide et que les restrictions HTTP referrer incluent http://localhost:4200/*.'); } else if (provider === 'peertube') { const inst = this.instances.activePeerTubeInstance(); this.notice.set(`PeerTube: les vidéos ne sont pas disponibles depuis l'instance "${inst}" pour le moment. Essayez une autre instance dans l'en-tête.`); } else if (provider === 'rumble') { const label = this.instances.selectedProviderLabel(); this.notice.set(`Les vidéos ne sont pas disponibles pour le provider "${label}" pour le moment. Réessayez plus tard ou choisissez un autre provider.`); } } }); } formatViews(views: number): string { if (views >= 1_000_000_000) { return (views / 1_000_000_000).toFixed(1) + 'B'; } if (views >= 1_000_000) { return (views / 1_000_000).toFixed(1) + 'M'; } if (views >= 1_000) { return (views / 1_000).toFixed(1) + 'K'; } return views.toString(); } // Relative date for cards (e.g., "il y a 2 heures") formatRelative(dateIso?: string): string { if (!dateIso) return ''; return formatRelativeFr(dateIso); } // Build query params for Watch page (provider + optional odysee slug) watchQueryParams(v: Video): Record | null { const p = this.instances.selectedProvider(); const qp: any = { p }; if (p === 'odysee' && v.url?.startsWith('https://odysee.com/')) { let slug = v.url.substring('https://odysee.com/'.length); if (slug.startsWith('/')) slug = slug.slice(1); qp.slug = slug; } return qp; } }