"use client"; import { useMemo, useState, useEffect } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { api } from "@/lib/api"; import { useToast } from "@/lib/toast"; import { useAuth } from "@/lib/auth"; import { useI18n } from "@/lib/i18n"; import type { Meetup } from "@/lib/types"; import NewsletterSubscribe from "@/components/NewsletterSubscribe"; import RingNext from "@/components/RingNext"; import { useRingSteps } from "@/lib/rings"; const MODE_LABELS: Record = { online: "πŸ’» 线上", offline: "πŸ“ ηΊΏδΈ‹", hybrid: "πŸ”€ 混合", }; interface Props { meetups: Meetup[]; } export default function MeetupsClient({ meetups }: Props) { const { t } = useI18n(); const rings = useRingSteps(); const { toast } = useToast(); const { token } = useAuth(); const router = useRouter(); const [mode, setMode] = useState<"all" | Meetup["mode"]>("all"); const [rsvpIds, setRsvpIds] = useState>(new Set()); const [busyId, setBusyId] = useState(null); useEffect(() => { if (!token) { setRsvpIds(new Set()); return; } api.getMyRsvps(token).then((r) => setRsvpIds(new Set(r.ids))).catch(() => {}); }, [token]); const filtered = useMemo(() => { if (mode === "all") return meetups; return meetups.filter((m) => m.mode === mode); }, [meetups, mode]); const handleRsvp = async (meetup: Meetup) => { if (!token) { toast("η™»ε½•εŽζŠ₯εοΌŒει’ζ‰δΌšδΏη•™", "info"); router.push(`/login?next=${encodeURIComponent("/meetups")}`); return; } if (rsvpIds.has(meetup.id)) { setBusyId(meetup.id); try { await api.cancelMeetupRsvp(token, meetup.id); const next = new Set(rsvpIds); next.delete(meetup.id); setRsvpIds(next); toast("ε·²ε–ζΆˆζŠ₯名", "success"); } catch { toast(t.meetups.rsvpFail, "error"); } finally { setBusyId(null); } return; } setBusyId(meetup.id); try { const res = await api.rsvpMeetup(meetup.id, undefined, token); setRsvpIds(new Set(rsvpIds).add(meetup.id)); toast(res.message || t.meetups.rsvpOk, "success"); } catch { toast(t.meetups.rsvpFail, "error"); } finally { setBusyId(null); } }; return (
{t.meetups.tag}

{t.meetups.title}

{t.meetups.subtitle}

{!token && (

登录 后ζŠ₯名,名钝会同ζ­₯εˆ°δ½ ηš„θ΄¦ε·

)}
{t.meetups.hostBtn} {(["all", "online", "offline", "hybrid"] as const).map((m) => ( ))}
{filtered.map((m) => { const joined = rsvpIds.has(m.id); return (
{m.emoji}

{m.title}

{MODE_LABELS[m.mode]} Β· {m.city} Β· {m.date} {m.time}

{m.description}

πŸ“ {m.venue}

{m.tags.map((tag) => ( {tag} ))}
{m.rsvp_count}/{m.max_attendees} {t.meetups.rsvp} {m.organizer}
{(m.mode === "online" || m.mode === "hybrid") && ( {t.meetups.liveBtn} )}
{m.destination_slug && ( {t.meetups.viewCity} β†’ )}
); })}
{filtered.length === 0 && (

{t.meetups.empty}

{mode !== "all" && ( )} {t.meetups.hostBtn} {t.nav.community}
)}
); }