import { useState, type FormEvent } from "react"; import { useNavigate, useParams } from "react-router"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { api, errMsg } from "../api"; import { useAuthChanged, useMe } from "../auth"; import { avatarUrl } from "../format"; import { Feed } from "../components/Feed"; import type { Profile } from "../types"; // Beim Wechsel zu einem anderen Profil frisch anfangen (Meldungen, Formulare). export function UserPage() { const { username = "" } = useParams(); return ; } function ProfileView({ username }: { username: string }) { const profile = useQuery({ queryKey: ["profile", username], queryFn: () => api.get(`/u/${encodeURIComponent(username)}/info`), }); const me = useMe().data; // Nach einem Upload das neue Bild sofort zeigen: Zeitstempel am URL // umgeht den Browser-Cache. const [avatarBust, setAvatarBust] = useState(""); const info = profile.data; let name = ""; if (profile.isError) name = errMsg(profile.error, "Profil konnte nicht geladen werden."); else if (info) name = info.username; const isOwner = !!me && !!info && me.username === info.username; return (

{name}

{info && `Mitglied seit ${new Date(info.created_at * 1000).toLocaleDateString("de-DE")}`}

{info && ( `/u/${encodeURIComponent(username)}/feed/${page}`} /> )}
); } // useFormAction verdrahtet ein Einstellungsformular: POST auf path, bei Erfolg // onSuccess mit Formular und Antwort, bei Fehler die Meldung unter dem Formular. function useFormAction(path: string, fallback: string, onSuccess: (form: HTMLFormElement, data: T) => void) { const m = useMutation({ mutationFn: (fd: FormData) => api.post(path, fd) }); const onSubmit = (e: FormEvent) => { e.preventDefault(); const form = e.currentTarget; m.mutate(new FormData(form), { onSuccess: (data) => onSuccess(form, data) }); }; return { onSubmit, busy: m.isPending, msg: m.isError ? errMsg(m.error, fallback) : "" }; } // Settings: nur für den Eigentümer. Die {" "} bilden die Leerzeichen des alten // Markups nach -- Inputs und Knöpfe sind inline. function Settings({ onAvatar }: { onAvatar: () => void }) { const qc = useQueryClient(); const navigate = useNavigate(); const authChanged = useAuthChanged(); // Neues Bild und neuer Name tauchen auch in den Beitragskarten auf -> die // Feeds und das eigene Konto neu laden. const refresh = () => { qc.invalidateQueries({ queryKey: ["feed"] }); qc.invalidateQueries({ queryKey: ["thread"] }); qc.invalidateQueries({ queryKey: ["profile"] }); return authChanged(); }; const avatar = useFormAction<{ avatar: string }>("/user/avatar", "Hochladen fehlgeschlagen.", (form) => { form.reset(); onAvatar(); refresh(); }); const rename = useFormAction<{ username: string }>("/user/rename", "Umbenennen fehlgeschlagen.", async (_form, data) => { await refresh(); navigate(`/u/${encodeURIComponent(data.username)}`); }); const del = useFormAction("/user/delete", "Löschen fehlgeschlagen.", async () => { await refresh(); navigate("/"); }); return ( <>

Einstellungen

Profilbild ändern
{" "}

{avatar.msg}

Name ändern
{" "}

{rename.msg}

Konto löschen
{ if (confirm("Konto wirklich unwiderruflich löschen?")) del.onSubmit(e); else e.preventDefault(); }} >

Löscht dein Konto samt Beiträgen und Votes unwiderruflich.

{" "} {" "}

{del.msg}

); }