"use client"; import { useState, useRef, useEffect, useCallback } from "react"; import { useRouter } from "next/navigation"; import { motion, AnimatePresence } from "framer-motion"; import { Plus, LogIn, Loader2, MapPin, Navigation, X, Users, Heart, Sparkles, ChevronRight, Flame, User } from "lucide-react"; import { getUserId, getCachedProfile, getCachedPreferences } from "@/lib/userId"; import { getAvatarBg } from "@/lib/avatars"; import AuthModal from "@/components/AuthModal"; import type { UserProfile } from "@/types"; interface LocationSuggestion { id: string; name: string; district: string; address: string; lat: number; lng: number; } const SHANGHAI_COORDS = { lat: 31.2222, lng: 121.4764 }; const DISTANCE_OPTIONS = [ { label: "1km", value: 1000 }, { label: "3km", value: 3000 }, { label: "5km", value: 5000 }, ] as const; const PRICE_OPTIONS = [ { label: "不限", value: "any" }, { label: "¥50以下", value: "under50" }, { label: "¥50-100", value: "50to100" }, { label: "¥100+", value: "over100" }, ] as const; const HOT_CUISINES = ["火锅", "日料", "烧烤", "西餐", "川菜", "咖啡甜品", "小吃快餐"] as const; function getLocation(): Promise<{ lat: number; lng: number }> { if (process.env.NODE_ENV === "development") { return Promise.resolve(SHANGHAI_COORDS); } return new Promise((resolve) => { if (!navigator.geolocation) { resolve(SHANGHAI_COORDS); return; } navigator.geolocation.getCurrentPosition( (pos) => resolve({ lat: pos.coords.latitude, lng: pos.coords.longitude }), () => resolve(SHANGHAI_COORDS), { timeout: 5000, enableHighAccuracy: false }, ); }); } export default function LandingPage() { const router = useRouter(); const [roomCode, setRoomCode] = useState(""); const [loading, setLoading] = useState(false); const [loadingText, setLoadingText] = useState(""); const [error, setError] = useState(""); const [locationQuery, setLocationQuery] = useState(""); const [suggestions, setSuggestions] = useState([]); const [showSuggestions, setShowSuggestions] = useState(false); const [selectedLocation, setSelectedLocation] = useState(null); const [fetchingSuggestions, setFetchingSuggestions] = useState(false); const [radius, setRadius] = useState(3000); const [priceRange, setPriceRange] = useState("any"); const [cuisine, setCuisine] = useState(""); const suggestRef = useRef(null); const debounceRef = useRef>(null); const [profile, setProfile] = useState(null); const [authModalOpen, setAuthModalOpen] = useState(false); useEffect(() => { const cached = getCachedProfile(); if (cached) setProfile(cached); const prefs = getCachedPreferences(); if (prefs.cuisine) setCuisine(prefs.cuisine); if (prefs.priceRange) setPriceRange(prefs.priceRange); if (prefs.radius) setRadius(prefs.radius); }, []); const fetchSuggestions = useCallback(async (query: string) => { if (query.length < 1) { setSuggestions([]); setShowSuggestions(false); return; } setFetchingSuggestions(true); try { const res = await fetch(`/api/location/suggest?keywords=${encodeURIComponent(query)}`); const data: LocationSuggestion[] = await res.json(); setSuggestions(data); setShowSuggestions(data.length > 0); } catch { setSuggestions([]); } finally { setFetchingSuggestions(false); } }, []); const handleLocationInput = (val: string) => { setLocationQuery(val); setSelectedLocation(null); if (debounceRef.current) clearTimeout(debounceRef.current); debounceRef.current = setTimeout(() => fetchSuggestions(val), 300); }; const handleSelectLocation = (loc: LocationSuggestion) => { setSelectedLocation(loc); setLocationQuery(loc.name); setShowSuggestions(false); setSuggestions([]); }; const clearLocation = () => { setSelectedLocation(null); setLocationQuery(""); setSuggestions([]); setShowSuggestions(false); }; useEffect(() => { const handleClickOutside = (e: MouseEvent) => { if (suggestRef.current && !suggestRef.current.contains(e.target as Node)) { setShowSuggestions(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, []); const joinRoom = async (roomId: string) => { const userId = getUserId(); const res = await fetch(`/api/room/${roomId}/join`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ userId }), }); if (!res.ok) throw new Error("房间不存在"); return roomId; }; const handleCreate = async () => { setLoading(true); setError(""); try { let coords: { lat: number; lng: number }; if (selectedLocation) { coords = { lat: selectedLocation.lat, lng: selectedLocation.lng }; setLoadingText("正在搜索周边美食..."); } else { setLoadingText("正在获取位置..."); coords = await getLocation(); setLoadingText("正在搜索周边美食..."); } const res = await fetch("/api/room/create", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...coords, radius, priceRange, cuisine, userId: getUserId() }), }); const data = await res.json(); if (!res.ok) { throw new Error(data.error || "创建房间失败"); } if (!data.roomId) { throw new Error("创建房间失败"); } setLoadingText("正在进入房间..."); await joinRoom(data.roomId); router.push(`/room/${data.roomId}`); } catch (e) { setError(e instanceof Error ? e.message : "创建失败,请重试"); setLoading(false); setLoadingText(""); } }; const handleJoin = async (e: React.FormEvent) => { e.preventDefault(); if (roomCode.length !== 4) { setError("请输入 4 位房间号"); return; } setLoading(true); setError(""); try { await joinRoom(roomCode); router.push(`/room/${roomCode}`); } catch { setError("房间不存在,请检查房间号"); setLoading(false); } }; return (
{/* Profile / Auth button */}
{profile ? ( ) : ( )}

NoWhatever

别说随便

和朋友一起滑卡片,再也不用纠结吃什么

创建房间 邀请朋友加入
各自滑卡 右滑喜欢的店
匹配结果 滑中同一家就去
handleLocationInput(e.target.value)} onFocus={() => suggestions.length > 0 && setShowSuggestions(true)} disabled={loading} className="h-10 w-full rounded-xl border border-zinc-200 bg-white pl-9 pr-9 text-sm text-zinc-700 outline-none transition-colors placeholder:text-zinc-300 focus:border-emerald-400 focus:ring-2 focus:ring-emerald-100 disabled:opacity-50" /> {(selectedLocation || locationQuery) && !loading && ( )} {fetchingSuggestions && ( )}
{selectedLocation && (
{selectedLocation.district} {selectedLocation.address || selectedLocation.name}
)} {!selectedLocation && !locationQuery && (
将使用当前定位
)} {showSuggestions && ( {suggestions.map((s) => (
  • ))}
    )}
    美食
    setCuisine(e.target.value)} disabled={loading} className="h-7 w-full rounded-full border-none bg-white pl-3 pr-7 text-xs text-zinc-700 outline-none ring-1 ring-zinc-200 transition-colors placeholder:text-zinc-300 focus:ring-2 focus:ring-emerald-300 disabled:opacity-50" /> {cuisine && !loading && ( )}
    {HOT_CUISINES.map((tag) => ( ))}
    距离
    {DISTANCE_OPTIONS.map((opt) => ( ))}
    人均
    {PRICE_OPTIONS.map((opt) => ( ))}
    或加入已有房间
    { setRoomCode(e.target.value.replace(/\D/g, "").slice(0, 4)); setError(""); }} disabled={loading} className="h-12 flex-1 rounded-xl border border-zinc-200 bg-white px-4 text-center text-lg font-semibold tracking-[0.3em] text-zinc-900 outline-none transition-colors placeholder:text-sm placeholder:tracking-normal placeholder:text-zinc-300 focus:border-emerald-400 focus:ring-2 focus:ring-emerald-100 disabled:opacity-50" />
    {error && ( {error} )} setAuthModalOpen(false)} onAuth={(p) => setProfile(p)} />
    ); }