NewTube/src/components/themes/provider-theme-page.component.html

173 lines
9.2 KiB
HTML

<div class="container mx-auto p-4 sm:p-6">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center gap-2 text-slate-200">
<a [routerLink]="['/t', theme()]" class="px-3 py-1 rounded bg-slate-800 hover:bg-slate-700 border border-slate-700">← {{ 'themes.backToThemes' | t }}</a>
<h2 class="text-2xl font-bold">
{{ themes.bySlug(theme())?.emoji }} {{ themes.bySlug(theme())?.label }}
<span class="text-slate-400 text-base">— {{ provider() | titlecase }}</span>
</h2>
</div>
<div class="flex items-center gap-2">
<label class="text-sm text-slate-400">{{ 'themes.changeProvider' | t }}</label>
<select [ngModel]="provider()" (ngModelChange)="changeProvider($event)" class="bg-gray-800 text-white rounded px-2 py-1">
<option *ngFor="let p of providersList()" [value]="p.id">{{ p.label }}</option>
</select>
</div>
</div>
<!-- Filters -->
<div class="grid sm:grid-cols-4 gap-3 mb-6 bg-slate-800/50 p-3 rounded-lg border border-slate-700">
<div>
<label class="block text-xs text-slate-400 mb-1">{{ 'filter.sort' | t }}</label>
<select [ngModel]="sort()" (ngModelChange)="onSortChange($event)" class="w-full bg-gray-800 text-white rounded px-2 py-1">
<option value="recent">{{ 'filter.sort.recent' | t }}</option>
<option value="viewed">{{ 'filter.sort.viewed' | t }}</option>
<option value="longest">{{ 'filter.sort.longest' | t }}</option>
</select>
</div>
<div>
<label class="block text-xs text-slate-400 mb-1">{{ 'filter.duration' | t }}</label>
<select [ngModel]="duration()" (ngModelChange)="onDurationChange($event)" class="w-full bg-gray-800 text-white rounded px-2 py-1">
<option value="any">{{ 'filter.duration.any' | t }}</option>
<option value="short">{{ 'filter.duration.short' | t }}</option>
<option value="medium">{{ 'filter.duration.medium' | t }}</option>
<option value="long">{{ 'filter.duration.long' | t }}</option>
</select>
</div>
<div>
<label class="block text-xs text-slate-400 mb-1">{{ 'filter.language' | t }}</label>
<select [ngModel]="language()" (ngModelChange)="onLanguageChange($event)" class="w-full bg-gray-800 text-white rounded px-2 py-1">
<option value="any">Any</option>
<option value="en">English</option>
<option value="fr">Français</option>
</select>
</div>
<div>
<label class="block text-xs text-slate-400 mb-1">{{ 'filter.date' | t }}</label>
<select [ngModel]="date()" (ngModelChange)="onDateChange($event)" class="w-full bg-gray-800 text-white rounded px-2 py-1">
<option value="any">Any</option>
<option value="day">Last 24h</option>
<option value="week">Last week</option>
<option value="month">Last month</option>
<option value="year">Last year</option>
</select>
</div>
</div>
<!-- Sections Tabs mimic (simple headings) -->
<div class="space-y-10">
<!-- Recorded Streams -->
<section>
<h3 class="text-xl font-semibold mb-3">{{ 'themes.recorded' | t }}</h3>
<div *ngIf="recorded.loading && !recorded.items.length" class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4">
<div *ngFor="let i of [1,2,3,4,5,6,7,8]" class="animate-pulse bg-slate-800/50 rounded-lg overflow-hidden border border-slate-800">
<div class="w-full h-32 bg-slate-800"></div>
<div class="p-2">
<div class="h-3 bg-slate-700 rounded w-11/12 mb-2"></div>
<div class="h-3 bg-slate-700 rounded w-8/12"></div>
</div>
</div>
</div>
<div *ngIf="!recorded.loading && recorded.items.length === 0" class="text-slate-400">{{ 'empty.noItems' | t }}</div>
<div *ngIf="recorded.error" class="mt-2 p-3 bg-red-900/30 border border-red-700 text-red-200 rounded">
{{ recorded.error }}
<button class="ml-2 underline hover:text-red-100" (click)="retry(recorded)">{{ 'action.retry' | t }}</button>
</div>
<div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4" *ngIf="recorded.items.length">
<a *ngFor="let v of (recorded.items | slice:0:recorded.visibleCount); trackBy: trackByVideo"
[routerLink]="['/watch', v.videoId]" [queryParams]="watchQueryParams(v)" [state]="{ video: v }"
class="group bg-slate-800/50 rounded-lg overflow-hidden hover:bg-slate-700/50">
<div class="relative">
<img [src]="v.thumbnail" [alt]="v.title" loading="lazy" decoding="async" class="w-full h-32 object-cover">
<div class="absolute bottom-2 left-2">
<app-like-button [videoId]="v.videoId" [provider]="provider()"></app-like-button>
</div>
</div>
<div class="p-2 text-sm line-clamp-2">{{ v.title }}</div>
</a>
</div>
<!-- Infinite scroll anchor -->
<app-infinite-anchor
*ngIf="recorded.items.length && (recorded.items.length >= recorded.visibleCount)"
[busy]="recorded.loading"
[disabled]="!recorded.nextCursor && recorded.items.length <= recorded.visibleCount"
[rootMargin]="'1000px 0px 1000px 0px'"
(loadMore)="showMore(recorded)">
</app-infinite-anchor>
<div class="mt-2 flex items-center justify-center text-slate-400 text-sm" *ngIf="recorded.loading">
<span class="inline-block h-4 w-4 border-2 border-slate-500 border-t-transparent rounded-full animate-spin mr-2"></span>
{{ 'loading.more' | t }}
</div>
<div class="mt-3" *ngIf="recorded.items.length && (recorded.items.length > recorded.visibleCount || recorded.nextCursor)">
<button class="px-4 py-2 bg-slate-800 hover:bg-slate-700 rounded border border-slate-700 text-slate-200 disabled:opacity-60"
[disabled]="recorded.loading"
(click)="showMore(recorded)">
{{ 'loading.more' | t }} (20)
</button>
</div>
</section>
<!-- Videos -->
<section>
<h3 class="text-xl font-semibold mb-3">{{ 'themes.videos' | t }}</h3>
<div *ngIf="videos.loading && !videos.items.length" class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4">
<div *ngFor="let i of [1,2,3,4,5,6,7,8]" class="animate-pulse bg-slate-800/50 rounded-lg overflow-hidden border border-slate-800">
<div class="w-full h-32 bg-slate-800"></div>
<div class="p-2">
<div class="h-3 bg-slate-700 rounded w-11/12 mb-2"></div>
<div class="h-3 bg-slate-700 rounded w-8/12"></div>
</div>
</div>
</div>
<div *ngIf="!videos.loading && videos.items.length === 0" class="text-slate-400">{{ 'empty.noItems' | t }}</div>
<div *ngIf="videos.error" class="mt-2 p-3 bg-red-900/30 border border-red-700 text-red-200 rounded">
{{ videos.error }}
<button class="ml-2 underline hover:text-red-100" (click)="retry(videos)">{{ 'action.retry' | t }}</button>
</div>
<div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4" *ngIf="videos.items.length">
<a *ngFor="let v of (videos.items | slice:0:videos.visibleCount); trackBy: trackByVideo"
[routerLink]="['/watch', v.videoId]" [queryParams]="watchQueryParams(v)" [state]="{ video: v }"
class="group bg-slate-800/50 rounded-lg overflow-hidden hover:bg-slate-700/50">
<div class="relative">
<img [src]="v.thumbnail" [alt]="v.title" loading="lazy" decoding="async" class="w-full h-32 object-cover">
<div class="absolute bottom-2 left-2">
<app-like-button [videoId]="v.videoId" [provider]="provider()"></app-like-button>
</div>
</div>
<div class="p-2 text-sm line-clamp-2">{{ v.title }}</div>
</a>
</div>
<!-- Infinite scroll anchor -->
<app-infinite-anchor
*ngIf="videos.items.length && (videos.items.length >= videos.visibleCount)"
[busy]="videos.loading"
[disabled]="!videos.nextCursor && videos.items.length <= videos.visibleCount"
[rootMargin]="'1000px 0px 1000px 0px'"
(loadMore)="showMore(videos)">
</app-infinite-anchor>
<div class="mt-2 flex items-center justify-center text-slate-400 text-sm" *ngIf="videos.loading">
<span class="inline-block h-4 w-4 border-2 border-slate-500 border-t-transparent rounded-full animate-spin mr-2"></span>
{{ 'loading.more' | t }}
</div>
<div class="mt-3" *ngIf="videos.items.length && (videos.items.length > videos.visibleCount || videos.nextCursor)">
<button class="px-4 py-2 bg-slate-800 hover:bg-slate-700 rounded border border-slate-700 text-slate-200 disabled:opacity-60"
[disabled]="videos.loading"
(click)="showMore(videos)">
{{ 'loading.more' | t }} (20)
</button>
</div>
</section>
<!-- Categories (static recommended) -->
<section>
<h3 class="text-xl font-semibold mb-3">{{ 'themes.categories' | t }}</h3>
<div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4">
<div *ngFor="let c of categories()" class="bg-slate-800/60 rounded-xl p-4 border border-slate-700">
<div class="text-4xl">{{ c.emoji }}</div>
<div class="mt-2 font-semibold">{{ c.label }}</div>
</div>
</div>
</section>
</div>
</div>