"use client" import type React from "react" import { useEffect, useRef, useState } from "react" import Link from "next/link" import { ArrowLeft, ImagePlus, Lock, Send, Trash2, X } from "lucide-react" import { Button } from "@/components/ui/button" import { cn } from "@/lib/utils" // --- Types (matcher API-kontrakten i app/routers/messaging.py) ------------- type ApiMessage = { id: string author_user_id: string author_display_name: string body: string | null image_url: string | null created_at: string } const TIME_FMT = new Intl.DateTimeFormat("no-NO", { hour: "2-digit", minute: "2-digit" }) function wsUrl(path: string): string { const protocol = window.location.protocol === "https:" ? "wss:" : "ws:" return `${protocol}//${window.location.host}${path}` } export function TeamChat({ organizationId, tournamentId, teamId, teamName, tournamentName, }: { organizationId: string tournamentId: string teamId: string teamName: string tournamentName: string }) { const [currentUserId, setCurrentUserId] = useState(null) const [messages, setMessages] = useState([]) const [loading, setLoading] = useState(true) const [accessError, setAccessError] = useState(null) const [error, setError] = useState(null) const [text, setText] = useState("") const [image, setImage] = useState(null) const [sending, setSending] = useState(false) const bottomRef = useRef(null) const fileInputRef = useRef(null) useEffect(() => { let cancelled = false async function load() { try { const [meRes, messagesRes] = await Promise.all([ fetch("/auth/me", { credentials: "include" }), fetch(`/orgs/${organizationId}/teams/${teamId}/messages`, { credentials: "include" }), ]) if (meRes.ok) { const me: { id: string } = await meRes.json() if (!cancelled) setCurrentUserId(me.id) } if (messagesRes.status === 403) { if (!cancelled) { setAccessError( "Du er ikke rostret på dette laget -- lagchatten er privat, kun for spillerne på laget (ikke engang organisasjonens eiere/administratorer har tilgang).", ) } return } if (!messagesRes.ok) throw new Error(`messages: ${messagesRes.status}`) const data: ApiMessage[] = await messagesRes.json() if (!cancelled) setMessages(data) } catch { if (!cancelled) setAccessError("Klarte ikke å laste chatten. Prøv å laste siden på nytt.") } finally { if (!cancelled) setLoading(false) } } void load() return () => { cancelled = true } }, [organizationId, teamId]) useEffect(() => { if (accessError) return const socket = new WebSocket(wsUrl(`/ws/orgs/${organizationId}/teams/${teamId}/messages`)) socket.onmessage = (event) => { try { const msg: ApiMessage = JSON.parse(event.data) setMessages((prev) => (prev.some((m) => m.id === msg.id) ? prev : [...prev, msg])) } catch { // ignorer ugyldig payload } } return () => socket.close() }, [organizationId, teamId, accessError]) useEffect(() => { bottomRef.current?.scrollIntoView({ block: "end" }) }, [messages.length]) async function send(e: React.FormEvent) { e.preventDefault() if (!text.trim() && !image) return setSending(true) setError(null) try { const form = new FormData() if (text.trim()) form.set("body", text.trim()) if (image) form.set("image", image) const res = await fetch(`/orgs/${organizationId}/teams/${teamId}/messages`, { method: "POST", credentials: "include", body: form, }) if (!res.ok) throw new Error(`send: ${res.status}`) const msg: ApiMessage = await res.json() setMessages((prev) => (prev.some((m) => m.id === msg.id) ? prev : [...prev, msg])) setText("") setImage(null) if (fileInputRef.current) fileInputRef.current.value = "" } catch { setError("Klarte ikke å sende meldingen. Prøv igjen.") } finally { setSending(false) } } async function remove(messageId: string) { setError(null) try { const res = await fetch(`/orgs/${organizationId}/teams/${teamId}/messages/${messageId}`, { method: "DELETE", credentials: "include", }) if (!res.ok && res.status !== 204) throw new Error(`delete: ${res.status}`) setMessages((prev) => prev.filter((m) => m.id !== messageId)) } catch { setError("Klarte ikke å slette meldingen. Prøv igjen.") } } const backHref = `/tournaments/${tournamentId}?org=${organizationId}&name=${encodeURIComponent(tournamentName)}` return (
{loading ? (
) : accessError ? (

{accessError}

) : ( <>
{messages.length === 0 && (

Ingen meldinger ennå -- her er kun laget deres, ingen andre kan lese med.

)} {messages.map((m) => { const own = m.author_user_id === currentUserId return (
{!own && ( {m.author_display_name} )} {m.image_url && ( // eslint-disable-next-line @next/next/no-img-element -- images.unoptimized er alt satt )} {m.body &&

{m.body}

} {own && ( )}
{TIME_FMT.format(new Date(m.created_at))}
) })}
{error && (

{error}

)} {image && (
{image.name}
)}
setImage(e.target.files?.[0] ?? null)} /> setText(e.target.value)} placeholder="Skriv en melding …" className="h-11 flex-1 rounded-2xl border border-input bg-background px-4 text-base outline-none focus-visible:ring-2 focus-visible:ring-ring" />
)}
) }