nomadweb/frontend/src/components/CommunityNewClient.tsx
eric bdf0516197 Fix dating match gate and expand seed data for usable demos.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-04 06:18:25 -05:00

158 lines
5.2 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { api } from "@/lib/api";
import { useAuth } from "@/lib/auth";
import { useToast } from "@/lib/toast";
import { useI18n } from "@/lib/i18n";
import RingNext from "@/components/RingNext";
import { useRingSteps } from "@/lib/rings";
import {
clearCommunityDraft,
loadCommunityDraft,
saveCommunityDraft,
} from "@/lib/communityDraft";
import { inferMeetupCity, meetupCityHref } from "@/lib/meetupLinks";
import type { Destination } from "@/lib/types";
export default function CommunityNewClient() {
const { token } = useAuth();
const router = useRouter();
const { toast } = useToast();
const { t } = useI18n();
const rings = useRingSteps();
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const [category, setCategory] = useState("社区");
const [busy, setBusy] = useState(false);
const [draftReady, setDraftReady] = useState(false);
const [cities, setCities] = useState<Destination[]>([]);
useEffect(() => {
const draft = loadCommunityDraft();
if (draft) {
setTitle(draft.title || "");
setContent(draft.content || "");
setCategory(draft.category || "社区");
if (draft.title || draft.content) {
toast(t.community.draftRestored, "info");
}
}
setDraftReady(true);
api
.getDestinations()
.then(setCities)
.catch(() => setCities([]));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (!draftReady || !token) return;
const id = window.setTimeout(() => {
if (!title.trim() && !content.trim()) {
clearCommunityDraft();
return;
}
saveCommunityDraft({ title, content, category });
}, 400);
return () => window.clearTimeout(id);
}, [title, content, category, draftReady, token]);
if (!token) {
return (
<div className="community-page">
<div className="container">
<nav className="detail-nav"><Link href="/community">← {t.community.back}</Link></nav>
<div className="dating-gate reveal">
<h2>{t.community.newTitle}</h2>
<p>{t.community.loginDesc}</p>
<div className="dating-empty-actions">
<Link href="/login?next=/community/new" className="btn btn-primary">{t.nav.login}</Link>
<Link href="/meetups" className="btn btn-ghost">{t.dating.meetAtEvents}</Link>
</div>
</div>
<RingNext steps={rings.afterCommunity()} />
</div>
</div>
);
}
const submit = async () => {
if (!token) {
router.push("/login?next=/community/new");
return;
}
const tTitle = title.trim();
const body = content.trim();
if (!tTitle || body.length < 10) {
toast(t.community.draftNeed, "info");
return;
}
setBusy(true);
try {
const excerpt = body.slice(0, 1000);
const res = await api.createDiscussion(token, {
title: tTitle,
content: body,
excerpt,
category,
});
clearCommunityDraft();
const city = inferMeetupCity(
`${tTitle} ${body} ${category}`,
cities.map((d) => d.name)
);
toast(t.community.createOk, "success", {
href: city ? meetupCityHref(city) : "/meetups",
label: city ? t.common.cityMeetups : t.dating.meetAtEvents,
});
const id = res.discussion?.id;
router.push(id ? `/community/${id}` : "/community");
} catch {
toast(t.community.createFail, "error");
} finally {
setBusy(false);
}
};
return (
<div className="community-page">
<div className="container">
<nav className="detail-nav"><Link href="/community">← {t.community.back}</Link></nav>
<div className="section-header reveal">
<h1>{t.community.newTitle}</h1>
<p className="section-desc">{t.community.draftHint}</p>
</div>
<div className="join-card reveal">
<label>{t.community.newSubject}</label>
<input value={title} onChange={(e) => setTitle(e.target.value)} maxLength={120} />
<label>{t.community.newCategory}</label>
<select value={category} onChange={(e) => setCategory(e.target.value)}>
{(
[
["签证", t.community.catVisa],
["远程工作", t.community.catRemote],
["住宿", t.community.catHousing],
["安全", t.community.catSafety],
["社区", t.community.catCommunity],
] as const
).map(([value, label]) => (
<option key={value} value={value}>
{label}
</option>
))}
</select>
<label>{t.community.newBody}</label>
<textarea value={content} onChange={(e) => setContent(e.target.value)} rows={6} maxLength={4000} />
<button type="button" className="btn btn-primary" disabled={busy} onClick={() => void submit()}>
{busy ? t.meetups.hosting : t.community.newSubmit}
</button>
</div>
<RingNext steps={rings.afterCommunity()} />
</div>
</div>
);
}