From c0a4340bae1ac0e1f9103f5699c68f402655c2a6 Mon Sep 17 00:00:00 2001 From: AyoubBenziza Date: Wed, 10 Jun 2026 17:31:38 +0200 Subject: [PATCH] feat(web): Spotify export on the end screen (roadmap V1 step 8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the blindtest tracks revealed at game end: - per-track Spotify search link (green button) built from "title artist" - "Playlist" button copies the whole tracklist ("title — artist" per line) to the clipboard for pasting into Spotify - YouTube open link kept alongside Pure client-side, derived from the tracks already sent in game:end. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/web/src/components/game-end-view.tsx | 113 ++++++++++++++-------- 1 file changed, 74 insertions(+), 39 deletions(-) diff --git a/apps/web/src/components/game-end-view.tsx b/apps/web/src/components/game-end-view.tsx index f071f52..19c9f16 100644 --- a/apps/web/src/components/game-end-view.tsx +++ b/apps/web/src/components/game-end-view.tsx @@ -1,11 +1,18 @@ +import { useState } from "react" import { Link } from "wouter" import { motion } from "framer-motion" -import { Crown, ExternalLink, Music } from "lucide-react" -import type { PlayerScore, RoomSnapshot } from "@nerdware/shared" +import { Check, ClipboardList, Crown, ExternalLink, Music, Music2 } from "lucide-react" +import type { BlindtestTrackInfo, PlayerScore, RoomSnapshot } from "@nerdware/shared" import { Button } from "@workspace/ui/components/button" import { useRoomStore } from "@/store/room" import { Avatar } from "@/components/avatar" +/** Lien de recherche Spotify (le titre vidéo contient en général artiste + chanson). */ +function spotifySearch(track: BlindtestTrackInfo): string { + const query = `${track.title} ${track.artist}`.trim() + return `https://open.spotify.com/search/${encodeURIComponent(query)}` +} + type Place = 1 | 2 | 3 // Hauteur de marche, couleurs (or / argent / bronze) et taille d'avatar par place. @@ -198,43 +205,7 @@ export function GameEndView({ snapshot }: { snapshot: RoomSnapshot }) { )} {gameTracks && gameTracks.length > 0 && ( -
-

- Les musiques de la partie -

-
    - {gameTracks.map((t) => ( -
  • - - - - {t.title} - - - ajouté par {t.submittedByName} - - - - - -
  • - ))} -
-
+ )}
@@ -256,3 +227,67 @@ export function GameEndView({ snapshot }: { snapshot: RoomSnapshot }) {
) } + +function TracksRecap({ tracks }: { tracks: BlindtestTrackInfo[] }) { + const [copied, setCopied] = useState(false) + + async function copyPlaylist() { + const text = tracks.map((t) => `${t.title} — ${t.artist}`).join("\n") + try { + await navigator.clipboard.writeText(text) + setCopied(true) + setTimeout(() => setCopied(false), 1500) + } catch { + // presse-papier indisponible : on ignore + } + } + + return ( +
+
+

+ Les musiques de la partie +

+ +
+
    + {tracks.map((t) => ( +
  • + + + {t.title} + + ajouté par {t.submittedByName} + + + + + + + + +
  • + ))} +
+
+ ) +}