Harden onion boot flow and deepen site surfaces
Add persistent onion key backup and restore, improve startup resilience, and flesh out the major site verticals with richer navigation, search coverage, and operator documentation. Made-with: Cursor
This commit is contained in:
105
app/forum/submit/page.tsx
Normal file
105
app/forum/submit/page.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
"use client";
|
||||
|
||||
import { FormEvent, useEffect, useRef, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useAccount } from "@/contexts/AccountContext";
|
||||
import { FORUM_RINGS } from "@/lib/forumRings";
|
||||
import { addThread } from "@/lib/forumState";
|
||||
|
||||
export default function VoidAggregateSubmitPage() {
|
||||
const router = useRouter();
|
||||
const { user, hydrated } = useAccount();
|
||||
const prefilledAuthor = useRef(false);
|
||||
const [topicSlug, setTopicSlug] = useState(FORUM_RINGS[0]!.slug);
|
||||
const [title, setTitle] = useState("");
|
||||
const [body, setBody] = useState("");
|
||||
const [author, setAuthor] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
if (!hydrated || !user || prefilledAuthor.current) return;
|
||||
setAuthor(user.displayName);
|
||||
prefilledAuthor.current = true;
|
||||
}, [hydrated, user]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined") return;
|
||||
const q = new URLSearchParams(window.location.search).get("ring");
|
||||
if (q && FORUM_RINGS.some((r) => r.slug === q)) setTopicSlug(q);
|
||||
}, []);
|
||||
|
||||
const onSubmit = (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!title.trim() || !body.trim()) return;
|
||||
const t = addThread({
|
||||
topicSlug,
|
||||
title,
|
||||
body,
|
||||
author,
|
||||
});
|
||||
router.push(`/forum/post/${t.id}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-2xl">
|
||||
<Link href="/forum" className="font-mono text-[11px] uppercase tracking-widest text-[#ff9a3c] hover:text-[#ffcb8a]">
|
||||
← Back to feed
|
||||
</Link>
|
||||
<h2 className="mt-6 font-mono text-xl font-bold text-[#ffcb8a]">Create submission</h2>
|
||||
<p className="mt-2 text-sm text-[#b8a995]">Pick a ring, write a title, drop your text. Unsigned posts are untrusted.</p>
|
||||
|
||||
<form onSubmit={onSubmit} className="mt-8 space-y-5 rounded-lg border border-[#3d3228] bg-[#120e0a]/90 p-6">
|
||||
<div>
|
||||
<label className="block font-mono text-[10px] uppercase tracking-widest text-[#ff9a3c]/80">Ring</label>
|
||||
<select
|
||||
value={topicSlug}
|
||||
onChange={(e) => setTopicSlug(e.target.value)}
|
||||
className="mt-2 w-full rounded border-2 border-[#3d3228] bg-[#0d0a07] px-4 py-3 font-mono text-sm text-[#e7d9c8] focus:border-[#ff9a3c] focus:outline-none"
|
||||
>
|
||||
{FORUM_RINGS.map((r) => (
|
||||
<option key={r.slug} value={r.slug} className="bg-[#0d0a07]">
|
||||
r/{r.shortName} — {r.title}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block font-mono text-[10px] uppercase tracking-widest text-[#ff9a3c]/80">Display name</label>
|
||||
<input
|
||||
value={author}
|
||||
onChange={(e) => setAuthor(e.target.value)}
|
||||
className="mt-2 w-full rounded border-2 border-[#3d3228] bg-[#0d0a07] px-4 py-3 font-mono text-sm text-[#e7d9c8] placeholder:text-[#5c5044] focus:border-[#ff9a3c] focus:outline-none"
|
||||
placeholder="Alias"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block font-mono text-[10px] uppercase tracking-widest text-[#ff9a3c]/80">Title</label>
|
||||
<input
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
required
|
||||
className="mt-2 w-full rounded border-2 border-[#3d3228] bg-[#0d0a07] px-4 py-3 font-mono text-sm text-[#e7d9c8] placeholder:text-[#5c5044] focus:border-[#ff9a3c] focus:outline-none"
|
||||
placeholder="Be specific — searchable titles get better replies"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block font-mono text-[10px] uppercase tracking-widest text-[#ff9a3c]/80">Body</label>
|
||||
<textarea
|
||||
value={body}
|
||||
onChange={(e) => setBody(e.target.value)}
|
||||
required
|
||||
rows={8}
|
||||
className="mt-2 w-full rounded border-2 border-[#3d3228] bg-[#0d0a07] px-4 py-3 font-mono text-sm text-[#e7d9c8] placeholder:text-[#5c5044] focus:border-[#ff9a3c] focus:outline-none"
|
||||
placeholder="Context, logs (redact IPs), PGP block optional…"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full rounded border-2 border-[#ff9a3c] bg-[#ff9a3c] py-3 font-mono text-sm font-black uppercase tracking-wider text-black hover:bg-[#ffcb8a]"
|
||||
>
|
||||
Publish
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user