Hosted rooms: the hub's side
CI / test (push) Successful in 1m5s

Rooms made on the website (hosted_room, migration 0005), the REST routes in
docs/hosted-rooms.md, and the realtime room.* protocol: joining with the
capacity, adult and linked-account rules, seats, numbered messages with
forged senders dropped and a rate limit, closing. The Rooms page lists them,
with a form to make one and the owner's edit and close.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
dhkatzandClaude Opus 5.5 committed 2026-10-30 03:21:21 -07:00
1 parent 0c67232425
commit 2a1fc94df6
18 files changed
+3138 -52

No files matched your search

+61
View File
@@ -0,0 +1,61 @@
import type { Metadata } from "next";
import Link from "next/link";
import { redirect } from "next/navigation";
import { ActionForm, SubmitButton } from "@/components/forms";
import { PageHeader } from "@/components/status";
import { Card, CardContent } from "@/components/ui/card";
import { Field, FieldDescription, FieldGroup, FieldLabel } from "@/components/ui/field";
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
import { api, getMe, unwrap } from "@/lib/api";
import { createRoom } from "../actions";
import { RoomFields } from "../room-fields";
export const metadata: Metadata = { title: "New room" };
export default async function NewRoom() {
const me = await getMe();
if (!me) redirect("/signin?next=/rooms/new");
const { links } = await api.account.get().then(unwrap);
return (
<div className="space-y-6">
<PageHeader title="New room">
<Link href="/rooms" className="underline-offset-4 hover:underline">
Back to rooms
</Link>
</PageHeader>
<Card>
<CardContent>
{links.length === 0 ? (
<p className="py-2 text-sm text-muted-foreground">
<Link href="/account" className="text-foreground underline underline-offset-4">
Link an IMVU account
</Link>{" "}
first: it&apos;s shown as the room&apos;s owner.
</p>
) : (
<ActionForm action={createRoom}>
<FieldGroup>
<RoomFields canAdult={links.some((l) => l.adult)} />
<Field>
<FieldLabel htmlFor="imvuId">Owner</FieldLabel>
<NativeSelect id="imvuId" name="imvuId" defaultValue={String(links[0]!.imvuId)} className="w-64">
{links.map((l) => (
<NativeSelectOption key={l.imvuId} value={String(l.imvuId)}>
{l.username}
</NativeSelectOption>
))}
</NativeSelect>
<FieldDescription>The IMVU account shown as owner. It can decorate the room.</FieldDescription>
</Field>
<div>
<SubmitButton pendingLabel="Making…">Make room</SubmitButton>
</div>
</FieldGroup>
</ActionForm>
)}
</CardContent>
</Card>
</div>
);
}