"use client"; import { useState, useCallback, useEffect, useRef } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { useRouter } from "next/navigation"; import { MapPin, Star, PartyPopper, Navigation, Phone, Clock, Trophy, RotateCcw, SearchX, Home, ChevronDown, Swords, RefreshCw, Share2, Zap, } from "lucide-react"; import { Restaurant, MatchType, RunnerUp, SceneType } from "@/types"; import { fireCelebration, playChime } from "@/lib/celebrate"; import { isRegistered } from "@/lib/userId"; interface MatchResultProps { restaurant: Restaurant; matchType: MatchType; matchLikes: number; runnerUps: RunnerUp[]; allRestaurants: Restaurant[]; userCount: number; roomId: string; userId: string; onReset: () => Promise; onNarrow: (restaurantIds: string[]) => Promise; resetting: boolean; scene?: SceneType; } function buildNavUrl(restaurant: Restaurant): string { if (restaurant.location) { const [lng, lat] = restaurant.location.split(","); return `https://uri.amap.com/marker?position=${lng},${lat}&name=${encodeURIComponent(restaurant.name)}&callnative=1`; } return `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(restaurant.name)}`; } function NoMatchResult({ onReset, resetting, }: { onReset: () => Promise; resetting: boolean; }) { const router = useRouter(); return ( 都不太满意 这一轮没有店被选中,换个范围或类型再试试? {resetting ? "重置中..." : "再来一轮"} router.push("/")} className="flex items-center justify-center gap-2 rounded-full bg-surface px-8 py-3 text-sm font-bold text-muted ring-1 ring-border transition-colors hover:bg-elevated" whileTap={{ scale: 0.95 }} > 换个条件重新搜 ); } function RunnerUpCard({ restaurant, likes, userCount, }: { restaurant: Restaurant; likes: number; userCount: number; }) { return ( {restaurant.images?.[0] && ( {restaurant.name} )}

{restaurant.name}

{restaurant.rating} {restaurant.price} {restaurant.distance && ( {restaurant.distance} )}

{likes}/{userCount} 人想去

); } export default function MatchResult({ restaurant, matchType, matchLikes, runnerUps, allRestaurants, userCount, roomId, userId, onReset, onNarrow, resetting, scene = "eat", }: MatchResultProps) { const router = useRouter(); const [showRunnerUps, setShowRunnerUps] = useState(false); const [toast, setToast] = useState(""); const celebratedRef = useRef(false); const historySavedRef = useRef(false); const isUnanimous = matchType === "unanimous"; const showToast = useCallback((msg: string) => { setToast(msg); setTimeout(() => setToast(""), 2200); }, []); useEffect(() => { if (isUnanimous && !celebratedRef.current) { const timer = setTimeout(() => { celebratedRef.current = true; fireCelebration(); playChime(); }, 500); return () => clearTimeout(timer); } }, [isUnanimous]); useEffect(() => { if (historySavedRef.current) return; if (!isRegistered()) return; if (matchType === "no_match") return; historySavedRef.current = true; fetch("/api/user/history", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ userId, roomId, restaurant, matchType, participants: userCount, }), }).catch(() => {}); }, [userId, roomId, restaurant, matchType, userCount]); const handleShare = useCallback(async () => { const verb = scene === "drink" ? "喝" : "吃"; const lines = [ isUnanimous ? `🎉 默契度 100%!${userCount} 人全员一致选了同一家!` : `🎉 我们用 NoWhatever 选好了去哪${verb}!`, ``, `📍 ${restaurant.name}`, restaurant.rating ? `⭐ ${restaurant.rating}` : "", restaurant.price && restaurant.price !== "未知" ? `💰 人均${restaurant.price}` : "", restaurant.address ? `📮 ${restaurant.address}` : "", ``, isUnanimous ? `✨ 这就是心有灵犀吧~` : "", ].filter(Boolean); const text = lines.join("\n"); const navUrl = buildNavUrl(restaurant); const shareData = { title: `我们选了${restaurant.name}!`, text, url: navUrl, }; try { if (navigator.share && navigator.canShare?.(shareData)) { await navigator.share(shareData); return; } } catch (e) { if (e instanceof Error && e.name === "AbortError") return; } try { await navigator.clipboard.writeText(`${text}\n\n${navUrl}`); showToast("已复制,快去发给朋友吧!"); } catch { showToast("复制失败,请手动复制"); } }, [restaurant, showToast, isUnanimous, userCount, scene]); if (matchType === "no_match") { return ; } const runnerUpRestaurants = runnerUps .map((ru) => { const r = allRestaurants.find((rest) => rest.id === ru.id); return r ? { restaurant: r, likes: ru.likes } : null; }) .filter((x): x is { restaurant: Restaurant; likes: number } => x !== null); const canNarrow = !isUnanimous && runnerUpRestaurants.length > 0; const narrowIds = canNarrow ? [restaurant.id, ...runnerUps.map((ru) => ru.id)] : []; return ( {/* Accent glow behind icon */}
{isUnanimous ? ( ) : ( )} 就去这了! {isUnanimous ? "大家一拍即合!" : `${matchLikes}/${userCount} 人想去这家`} {isUnanimous && ( 默契度 100% · {userCount} 人全员一致 )} {/* Result card */} {restaurant.images?.[0] && ( {restaurant.name} )}

{restaurant.name}

{restaurant.category && ( {restaurant.category} )}
{restaurant.rating} {restaurant.price} {restaurant.distance && ( {restaurant.distance} )}
{restaurant.address && (

{restaurant.address}

)} {restaurant.openTime && (
{restaurant.openTime}
)} {restaurant.tag && (
{restaurant.tag .split(",") .slice(0, 4) .map((t) => ( {t.trim()} ))}
)}
{/* Action buttons */} 导航过去 {restaurant.tel && ( 打电话订位 )} 分享结果到群里 {/* Runner ups */} {!isUnanimous && runnerUpRestaurants.length > 0 && ( {showRunnerUps && ( {runnerUpRestaurants.map(({ restaurant: r, likes }) => ( ))} )} )} {/* Bottom actions */} {canNarrow ? ( <> onNarrow(narrowIds)} disabled={resetting} className="flex w-full items-center justify-center gap-2 rounded-full bg-elevated px-8 py-3 text-sm font-bold text-gray-300 ring-1 ring-border transition-colors hover:bg-subtle disabled:opacity-50" whileTap={{ scale: 0.95 }} > {resetting ? "加载中..." : `Top ${narrowIds.length} 决赛`} router.push("/")} className={`flex items-center gap-1.5 text-sm font-medium underline underline-offset-2 hover:text-white ${ isUnanimous ? "text-emerald-400" : "text-amber-400" }`} > 换一批店 ) : ( <> {resetting ? "重置中..." : "再来一轮"} router.push("/")} className={`flex items-center gap-1.5 text-sm font-medium underline underline-offset-2 hover:text-white ${ isUnanimous ? "text-emerald-400" : "text-amber-400" }`} > 换一批店 )}
{toast && ( {toast} )} ); }