"use client"; import { useState, useCallback, useEffect, useRef } from "react"; import { Link, useLocale } from "@/i18n/navigation"; import { useTranslation } from "@/i18n/navigation"; import JoinModal from "./JoinModal"; import { getPayEnv, redirectToPay, getDeviceFromEnv, } from "@/app/lib/payment"; import type { PayChannel } from "@/app/lib/payment"; import { apiFetch, fetchAuthMe, syncAuthSession } from "@/app/lib/api-client"; import { mediaUrl } from "@/app/lib/media"; import { avatarForIndex } from "@/app/data/avatars"; import { useDevDebug } from "@/app/context/DevDebugContext"; const MEDIA_NAMES_ZH = ["36氪", "少数派", "极客公园", "虎嗅", "创业邦", "澎湃新闻"]; const MEDIA_NAMES_EN = ["36Kr", "sspai", "GeekPark", "Huxiu", "Cyzone", "The Paper"]; const avatarPhotos = [ avatarForIndex(0), avatarForIndex(1), avatarForIndex(2), avatarForIndex(3), avatarForIndex(4), avatarForIndex(5), avatarForIndex(6), avatarForIndex(7), avatarForIndex(8), avatarForIndex(9), avatarForIndex(10), ]; const features = [ { icon: "🍹", key: "feature1" as const }, { icon: "❤️", key: "feature2" as const }, { icon: "🧪", key: "feature3" as const }, { icon: "🌎", key: "feature4" as const }, { icon: "💬", key: "feature5" as const }, ]; function isPrivateDevHost(hostname: string): boolean { if (!hostname) return false; if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1") { return true; } if (/^10\./.test(hostname) || /^192\.168\./.test(hostname)) { return true; } const match = hostname.match(/^172\.(\d{1,2})\./); if (!match) return false; const secondOctet = Number(match[1]); return secondOctet >= 16 && secondOctet <= 31; } function buildPendingUserId(email: string): string { return ( "pending_" + Array.from(new TextEncoder().encode(email)) .map((b) => b.toString(16).padStart(2, "0")) .join("") ); } export default function HeroSection() { const { t: tHero } = useTranslation("hero"); const locale = useLocale(); const { isFeatureVisible } = useDevDebug(); const showMediaPress = isFeatureVisible("home.mediaPress"); const [email, setEmail] = useState(""); const [joinModalOpen, setJoinModalOpen] = useState(false); const [joinModalVipOnly, setJoinModalVipOnly] = useState(false); const [checking, setChecking] = useState(false); const [error, setError] = useState(null); const [isLoggedIn, setIsLoggedIn] = useState(false); useEffect(() => { const checkAuth = async () => { const data = await fetchAuthMe(); setIsLoggedIn(!!data?.user); }; checkAuth(); const onAuth = () => checkAuth(); window.addEventListener("auth:updated", onAuth); return () => window.removeEventListener("auth:updated", onAuth); }, []); const checkUserCacheRef = useRef<{ email: string; data: { exists: boolean; vip: boolean }; ts: number } | null>(null); const CACHE_TTL_MS = 60_000; const handleCheckAndProceed = useCallback( async (em: string) => { if (checking) return; const trimmed = em.trim(); if (!trimmed) { setError(locale === "zh" ? "请输入邮箱" : "Please enter your email"); return; } setError(null); setChecking(true); try { const cached = checkUserCacheRef.current; if (cached && cached.email === trimmed && Date.now() - cached.ts < CACHE_TTL_MS) { const { exists, vip } = cached.data; if (exists) { setJoinModalVipOnly(!!vip); setJoinModalOpen(true); return; } } const checkData = await apiFetch<{ ok?: boolean; exists?: boolean; vip?: boolean; error?: string }>( "/api/meetup/check-user", { method: "POST", body: JSON.stringify({ email: trimmed }) } ); if (!checkData?.ok) { throw new Error(checkData?.error || (locale === "zh" ? "操作失败" : "Operation failed")); } if (checkData.exists) { checkUserCacheRef.current = { email: trimmed, data: { exists: true, vip: !!checkData.vip }, ts: Date.now() }; if (checkData.vip) { setJoinModalOpen(true); setJoinModalVipOnly(true); return; } setJoinModalVipOnly(false); setJoinModalOpen(true); return; } // 新用户:支付成功后才创建,用 pending_hex(email) 作为 user_id const shouldPrecreateUser = typeof window !== "undefined" && isPrivateDevHost(window.location.hostname); let user_id = buildPendingUserId(trimmed); if (shouldPrecreateUser) { const ensureData = await apiFetch<{ ok?: boolean; user_id?: string; token?: string; record?: Record; error?: string; }>("/api/meetup/ensure-user", { method: "POST", body: JSON.stringify({ email: trimmed }), }); if (!ensureData?.ok || !ensureData?.user_id) { throw new Error( ensureData?.error || (locale === "zh" ? "创建用户失败" : "Failed to create user") ); } user_id = String(ensureData.user_id).trim(); if (ensureData?.token && ensureData?.record) { await syncAuthSession(ensureData.token, ensureData.record); } } const returnUrl = typeof window !== "undefined" ? `${window.location.origin}/${locale}/join/paid` : "/zh/join/paid"; const env = getPayEnv(); const channel: PayChannel = "wxpay"; redirectToPay({ user_id, return_url: returnUrl, channel, device: getDeviceFromEnv(env), useJoinConfig: true, }); } catch (err) { setError(err instanceof Error ? err.message : "操作失败"); } finally { setChecking(false); } }, [checking, locale] ); const handleJoinClick = (e: React.FormEvent) => { e.preventDefault(); handleCheckAndProceed(email); }; return (
{/* Left Column */}
🏆 {tHero("badge")}

{tHero("title")}
{tHero("titleSuffix")}

{tHero("subtitle")}

{features.map((f) => (
{f.icon} {tHero(f.key)}
))}
{avatarPhotos.slice(0, 8).map((src, i) => ( ))}
{tHero("membersJoined")}
{showMediaPress && (

{tHero("mediaPress")}

{(locale === "zh" ? MEDIA_NAMES_ZH : MEDIA_NAMES_EN).map( (name) => ( {name} ) )}
)}
{/* Right Column: Video + Email */}
{!isLoggedIn ? (
{ setEmail(e.target.value); setError(null); }} placeholder={tHero("emailPlaceholder")} className="w-full border border-gray-200 dark:border-gray-700 rounded-lg px-4 py-3 text-sm placeholder-gray-400 dark:placeholder-gray-500 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-[#ff4d4f]/20 focus:border-[#ff4d4f] transition-colors mb-3" /> {error && (

{error}

)}
) : (

{locale === "zh" ? "您已登录,可前往社区参与互动" : "You are logged in"}

)}
{ setJoinModalOpen(false); setJoinModalVipOnly(false); }} initialEmail={email.trim()} vipOnly={joinModalVipOnly} />
📍 {tHero("quickLinks.nextStop")} 🍹 {tHero("quickLinks.meetups")} ❤️ {tHero("quickLinks.dating")} 📊 {tHero("quickLinks.tools")} 🤖 {tHero("quickLinks.ai")} 💰 {tHero("quickLinks.gigs")} 📊 {tHero("quickLinks.report")} 💬 {tHero("quickLinks.join")}
); }