NewTube/src/components/home/home.component.ts

134 lines
4.9 KiB
TypeScript

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<Video[]>([]);
loading = signal(true);
busyMore = signal(false);
nextCursor = signal<string | null>(null);
notice = signal<string | null>(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<string, any> | 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;
}
}