"use client"; import { useState, useEffect, useRef, useCallback } from "react"; import Link from "next/link"; import { useAccount } from "@/contexts/AccountContext"; type Msg = { id: string; from: string; text: string; ts: number; }; function now(ts: number) { return new Date(ts).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); } export default function ChatWidget() { const { user } = useAccount(); const handle = user?.displayName ?? user?.username ?? "anon"; const [messages, setMessages] = useState([]); const [input, setInput] = useState(""); const [hydrated, setHydrated] = useState(false); const endRef = useRef(null); const fetchMessages = useCallback(async () => { try { const res = await fetch("/api/messages?channel=hub"); const data = await res.json(); if (data.ok && data.messages) { setMessages(data.messages); } } catch (e) { // ignore } }, []); useEffect(() => { fetchMessages(); setHydrated(true); const interval = setInterval(fetchMessages, 3000); return () => clearInterval(interval); }, [fetchMessages]); useEffect(() => { if (hydrated && messages.length > 0) { endRef.current?.scrollIntoView({ behavior: "smooth" }); } }, [messages, hydrated]); const send = async () => { const text = input.trim(); if (!text) return; setInput(""); // Optimistic update const optimisticMsg: Msg = { id: "temp-" + Date.now(), from: handle, text, ts: Date.now() }; setMessages((p) => [...p, optimisticMsg]); try { await fetch("/api/messages", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ from: handle, text, channel: "hub" }), }); fetchMessages(); } catch (e) { // ignore } }; return (

CHANNEL CHAT

Global hub channel — synced live.{" "} {user ? ( Posting as @{user.username} ) : ( Sign in to tag your handle )}

channel active live sync
{messages.length === 0 ? (
No messages yet. Be the first!
) : ( messages.map((msg) => { const mine = msg.from === handle || (msg.from === "anon" && !user); return (
{mine ? `@${handle}` : msg.from === "System" ? "⚡ System" : `@${msg.from}`} {now(msg.ts)}
{msg.text}
); }) )}
setInput(e.target.value)} onKeyDown={(e) => e.key === "Enter" && send()} maxLength={500} />
{[ { icon: "🌍", label: "Global Sync", sub: "Talk to everyone" }, { icon: "💬", label: "Open channel", sub: "Hub-wide thread" }, { icon: "📬", label: "Forum threads", sub: "/forum for persistence" }, { icon: "📦", label: "Market drops", sub: "/drops for schedule" }, ].map((f) => (
{f.icon}
{f.label}
{f.sub}
))}
); }