diff --git a/apps/web/package.json b/apps/web/package.json
index 7a975fc..b9a9752 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -14,6 +14,7 @@
"dependencies": {
"@dicebear/core": "^10",
"@dicebear/styles": "^10",
+ "@icons-pack/react-simple-icons": "^13",
"@nerdware/shared": "workspace:*",
"@tanstack/react-query": "^5.101.0",
"@workspace/ui": "workspace:*",
diff --git a/apps/web/src/components/game-end-view.tsx b/apps/web/src/components/game-end-view.tsx
index f071f52..5708d87 100644
--- a/apps/web/src/components/game-end-view.tsx
+++ b/apps/web/src/components/game-end-view.tsx
@@ -1,11 +1,19 @@
+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, Music } from "lucide-react"
+import { SiSpotify, SiYoutube } from "@icons-pack/react-simple-icons"
+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 +206,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 +228,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}
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+ )
+}
diff --git a/apps/web/src/components/lobby-view.tsx b/apps/web/src/components/lobby-view.tsx
index a34afb0..360c277 100644
--- a/apps/web/src/components/lobby-view.tsx
+++ b/apps/web/src/components/lobby-view.tsx
@@ -11,6 +11,7 @@ import {
Trash2,
type LucideIcon,
} from "lucide-react"
+import { SiYoutube } from "@icons-pack/react-simple-icons"
import type {
BlindtestMode,
GameType,
@@ -361,6 +362,16 @@ function TrackSubmission({
className="h-10 w-16 shrink-0 rounded object-cover"
/>
{t.title}
+
+
+
)}
diff --git a/bun.lock b/bun.lock
index 0583dab..687265d 100644
--- a/bun.lock
+++ b/bun.lock
@@ -40,6 +40,7 @@
"dependencies": {
"@dicebear/core": "^10",
"@dicebear/styles": "^10",
+ "@icons-pack/react-simple-icons": "^13",
"@nerdware/shared": "workspace:*",
"@tanstack/react-query": "^5.101.0",
"@workspace/ui": "workspace:*",
@@ -295,6 +296,8 @@
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
+ "@icons-pack/react-simple-icons": ["@icons-pack/react-simple-icons@13.13.0", "", { "peerDependencies": { "react": "^16.13 || ^17 || ^18 || ^19" } }, "sha512-B5HhQMIpcSH4z8IZ8HFhD59CboHceKYMpPC9kAwGyKntvPdyJJv26DLu4Z1wAjcCLyrJhf11tMhiQGom9Rxb9g=="],
+
"@inquirer/ansi": ["@inquirer/ansi@1.0.2", "", {}, "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ=="],
"@inquirer/checkbox": ["@inquirer/checkbox@4.3.2", "", { "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/core": "^10.3.2", "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "yoctocolors-cjs": "^2.1.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA=="],