feat(shorts): bouton « Enregistrer » (à regarder plus tard) + panneau ⓘ honnête
CI / build-and-test (push) Canceled after 0s

- Rail + menu FAB : « Enregistrer » branché sur la liste réelle
  « À regarder plus tard » (LikesService, tag `watch-later`, page
  /library/watch-later) — état hydraté par vidéo comme J'aime, désactivé
  hors connexion (la liste est serveur). C'est le bouton « Enregistrer » de la
  description du rail, en vrai plutôt qu'en décoration.
- Panneau ⓘ : plus de promesse de commentaires — l'app n'en a aucune source
  (le champ du modèle est un tableau vide sur /watch) ; le bloc renvoie
  explicitement vers la page complète (description, téléchargements, transcript).
This commit is contained in:
2026-10-02 14:48:58 -04:00
parent c45e04af42
commit 7aea8e2c7b
2 changed files with 57 additions and 5 deletions
@@ -103,6 +103,11 @@
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" [attr.fill]="liked() ? 'currentColor' : 'none'" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M7 10v12M15 5.88L14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88z"/></svg>
<span class="pm-item-label">{{ liked() ? 'Je n’aime plus' : 'J’aime' }}</span>
</button>
<button type="button" (click)="toggleSave()" class="pm-item" [disabled]="!isLoggedIn() || saveBusy()"
[title]="isLoggedIn() ? (saved() ? 'Retirer de « À regarder plus tard »' : 'Enregistrer') : 'Connectez-vous pour enregistrer'">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" [attr.fill]="saved() ? 'currentColor' : 'none'" stroke="currentColor" stroke-width="2" stroke-linejoin="round" aria-hidden="true"><path d="M6 3.5h12a1 1 0 0 1 1 1V21l-7-4.2L5 21V4.5a1 1 0 0 1 1-1z"/></svg>
<span class="pm-item-label">{{ saved() ? 'Retirer de « À regarder plus tard »' : 'Enregistrer' }}</span>
</button>
<button type="button" (click)="toggleMute()" class="pm-item" [title]="muted() ? 'Activer le son' : 'Couper le son'">
@if (muted()) {
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M11 5L6 9H2v6h4l5 4V5z"/><line x1="23" y1="9" x2="17" y2="15" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><line x1="17" y1="9" x2="23" y2="15" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
@@ -204,6 +209,13 @@
</a>
}
<button type="button" class="shorts-rail-btn" (click)="toggleSave(); $event.stopPropagation()"
[disabled]="!isLoggedIn() || saveBusy()" [class.shorts-rail-btn--on]="saved()"
[title]="isLoggedIn() ? (saved() ? 'Retirer de « À regarder plus tard »' : 'Enregistrer') : 'Connectez-vous pour enregistrer'"
[attr.aria-label]="isLoggedIn() ? (saved() ? 'Retirer de « À regarder plus tard »' : 'Enregistrer') : 'Connectez-vous pour enregistrer'">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" [attr.fill]="saved() ? 'currentColor' : 'none'" stroke="currentColor" stroke-width="2" stroke-linejoin="round" aria-hidden="true"><path d="M6 3.5h12a1 1 0 0 1 1 1V21l-7-4.2L5 21V4.5a1 1 0 0 1 1-1z"/></svg>
</button>
<button type="button" class="shorts-rail-btn" (click)="share(); $event.stopPropagation()" title="Partager" aria-label="Partager">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.6" y1="13.5" x2="15.4" y2="17.5"/><line x1="15.4" y1="6.5" x2="8.6" y2="10.5"/></svg>
</button>
@@ -358,16 +370,17 @@
</a>
}
<!-- Commentaires : le flux Shorts n'a pas d'API commentaires, on
renvoie vers la page Watch qui les affiche. -->
<!-- Commentaires : le flux Shorts (comme le reste de l'app) n'a
aucune source de commentaires — on ne promet pas ce qui
n'existe pas, on renvoie vers la page complète. -->
<div class="mt-4 border-t border-white/10 pt-3">
<div class="text-[12px] font-semibold text-white/80">Commentaires</div>
<p class="mt-1 text-[11px] leading-relaxed text-white/45">Les commentaires s’affichent dans la page Watch, avec la description complète.</p>
<p class="mt-1 text-[11px] leading-relaxed text-white/45">Aucune source de commentaires pour ce fournisseur. La page complète donne la description, les pistes de téléchargement et le transcript.</p>
@if (watchLink()) {
<a [routerLink]="watchLink()" [queryParams]="watchQueryParams()" (click)="toggleInfo()"
class="mt-2 inline-flex w-full items-center justify-center gap-2 rounded-full bg-white px-3 py-2 text-[12px] font-semibold text-black hover:bg-slate-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
Voir les commentaires
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M10 9l5 3-5 3V9z"/></svg>
Ouvrir la page complète
</a>
}
</div>
@@ -157,6 +157,10 @@ export class WatchShortComponent implements OnDestroy {
liked = signal(false);
private likedCheckedFor = '';
likeBusy = signal(false);
/** « Enregistrer » = liste « À regarder plus tard » (même stockage que J'aime). */
saved = signal(false);
private savedCheckedFor = '';
saveBusy = signal(false);
/** Abonnement à la chaîne du short courant. */
subBusy = signal(false);
/** Partage : "copié" éphémère. */
@@ -750,9 +754,20 @@ export class WatchShortComponent implements OnDestroy {
error: () => {},
});
}
// État « Enregistré » (tag watch-later) : même tuyau que J'aime.
if (key !== this.savedCheckedFor) {
this.savedCheckedFor = key;
this.saved.set(false);
this.likes.isLiked(this.providerOf(v), v.videoId, 'watch-later').subscribe({
next: (r) => { if (this.savedCheckedFor === key) this.saved.set(!!(r as any)?.liked); },
error: () => {},
});
}
} else {
this.liked.set(false);
this.likedCheckedFor = '';
this.saved.set(false);
this.savedCheckedFor = '';
}
} catch {}
});
@@ -1154,6 +1169,30 @@ export class WatchShortComponent implements OnDestroy {
}
}
/**
* « Enregistrer » = « À regarder plus tard » (même stockage que J'aime, tag
* `watch-later`, page /library/watch-later). Connecté uniquement : la liste
* est serveur, un invité n'a nulle part où l'écrire.
*/
toggleSave(): void {
const v = this.current();
if (!v?.videoId || !this.isLoggedIn() || this.saveBusy()) return;
const p = this.providerOf(v);
this.saveBusy.set(true);
const done = (saved: boolean) => { this.saved.set(saved); this.saveBusy.set(false); };
if (this.saved()) {
this.likes.unlike(p, v.videoId, 'watch-later').subscribe({
next: () => done(false),
error: () => this.saveBusy.set(false),
});
} else {
this.likes.like(p, v.videoId, v.title, v.thumbnail, 'watch-later').subscribe({
next: () => done(true),
error: () => this.saveBusy.set(false),
});
}
}
toggleSubscribe(): void {
const key = this.currentChannelKey();
if (!key || !this.isLoggedIn() || this.subBusy()) return;