diff --git a/src/app/room/[id]/page.tsx b/src/app/room/[id]/page.tsx index 1665216..94a067d 100644 --- a/src/app/room/[id]/page.tsx +++ b/src/app/room/[id]/page.tsx @@ -58,6 +58,7 @@ export default function RoomPage() { matchType={matchType} matchLikes={matchLikes} likeCounts={likeCounts} + swipeCounts={swipeCounts} userCount={userCount} onReset={handleReset} /> diff --git a/src/components/SwipeDeck.tsx b/src/components/SwipeDeck.tsx index 3706138..75060bc 100644 --- a/src/components/SwipeDeck.tsx +++ b/src/components/SwipeDeck.tsx @@ -7,7 +7,134 @@ import ActionButtons from "./ActionButtons"; import MatchResult from "./MatchResult"; import SwipeGuide from "./SwipeGuide"; import { Restaurant, SwipeDirection, MatchType } from "@/types"; -import { Heart, Undo2 } from "lucide-react"; +import { Heart, Undo2, Check } from "lucide-react"; + +const AVATARS = [ + { emoji: "🐱", bg: "bg-amber-100" }, + { emoji: "🐶", bg: "bg-orange-100" }, + { emoji: "🦊", bg: "bg-red-100" }, + { emoji: "🐰", bg: "bg-pink-100" }, + { emoji: "🐼", bg: "bg-zinc-100" }, + { emoji: "🐨", bg: "bg-sky-100" }, + { emoji: "🦁", bg: "bg-yellow-100" }, + { emoji: "🐸", bg: "bg-lime-100" }, + { emoji: "🐵", bg: "bg-stone-100" }, + { emoji: "🐷", bg: "bg-rose-100" }, + { emoji: "🐙", bg: "bg-purple-100" }, + { emoji: "🦄", bg: "bg-violet-100" }, +] as const; + +function getAvatar(uid: string) { + let hash = 0; + for (let i = 0; i < uid.length; i++) { + hash = (hash * 31 + uid.charCodeAt(i)) | 0; + } + return AVATARS[((hash % AVATARS.length) + AVATARS.length) % AVATARS.length]; +} + +function UserProgressBar({ + userId, + swipeCounts, + localIndex, + total, +}: { + userId: string; + swipeCounts: Record; + localIndex: number; + total: number; +}) { + const others = Object.entries(swipeCounts).filter(([id]) => id !== userId); + if (others.length === 0) return null; + + return ( +
+ + + {getAvatar(userId).emoji} + + 你 {localIndex}/{total} + + {others.map(([id, count]) => { + const finished = count >= total; + const avatar = getAvatar(id); + return ( + + + {avatar.emoji} + + {count}/{total} + {finished && } + + ); + })} +
+ ); +} + +function WaitingProgress({ + userId, + swipeCounts, + total, +}: { + userId: string; + swipeCounts: Record; + total: number; +}) { + const entries = Object.entries(swipeCounts); + if (entries.length <= 1) return null; + + const others = entries.filter(([id]) => id !== userId); + const finishedCount = others.filter(([, c]) => c >= total).length; + + return ( +
+
+ + + {getAvatar(userId).emoji} + + 你 {total}/{total} + + +
+ + {others.map(([id, count]) => { + const finished = count >= total; + const pct = Math.min((count / total) * 100, 100); + const avatar = getAvatar(id); + return ( +
+
+ + + {avatar.emoji} + + {count}/{total} + + {finished && } +
+ {!finished && ( +
+ +
+ )} +
+ ); + })} + +

+ {finishedCount}/{others.length} 人已完成 +

+
+ ); +} interface SwipeDeckProps { restaurants: Restaurant[]; @@ -18,6 +145,7 @@ interface SwipeDeckProps { matchType: MatchType; matchLikes: number; likeCounts: Record; + swipeCounts: Record; userCount: number; onReset: () => Promise; } @@ -31,6 +159,7 @@ export default function SwipeDeck({ matchType, matchLikes, likeCounts, + swipeCounts, userCount, onReset, }: SwipeDeckProps) { @@ -175,28 +304,36 @@ export default function SwipeDeck({ return ( <> {!allSwiped && !resolvedMatchId && ( -
-
- +
+
+
+ +
+ + {currentIndex}/{restaurants.length} + +
- - {currentIndex}/{restaurants.length} - - +
)} @@ -226,9 +363,14 @@ export default function SwipeDeck({ )} {showWaiting && ( -
+
-

等待其他人完成选择...

+

等待其他人完成选择

+
)}