diff --git a/src/app/api/posts/route.ts b/src/app/api/posts/route.ts new file mode 100644 index 0000000..0164581 --- /dev/null +++ b/src/app/api/posts/route.ts @@ -0,0 +1,26 @@ +import { NextResponse, type NextRequest } from "next/server"; +import { SESSION_COOKIE } from "@/lib/cookie"; +import { createTextPost } from "@/lib/post-create"; + +// POST /api/posts { content } +// Creates a public text draft on the feed, then publishes it. Returns { id }. +// The create call can report status "draft" for a post already on the home +// feed, so the browser reloads the first page and confirms the id there. +// 401 { error: "sign_in" } without a session. + +export const runtime = "nodejs"; + +export async function POST(req: NextRequest) { + const sessionId = req.cookies.get(SESSION_COOKIE)?.value; + let content: unknown = null; + try { + content = ((await req.json()) as { content?: unknown }).content; + } catch { + content = null; + } + const result = await createTextPost(sessionId, content); + if (!result.ok) { + return NextResponse.json({ error: result.error }, { status: result.status }); + } + return NextResponse.json({ id: result.id }); +} diff --git a/src/app/globals.css b/src/app/globals.css index 8897492..2c90508 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -42,3 +42,8 @@ button:disabled { opacity: .5; cursor: not-allowed; } .like-btn:disabled:not(.liked) { color: var(--grey); } .like-error { color: #a8141b; font-size: 13px; align-self: center; } .post-stats { align-items: center; } +.composer { background: #fff; border: 1px solid var(--line); border-radius: 0; padding: 16px; display: flex; flex-direction: column; gap: 8px; } +.composer-label { margin: 0; } +.composer textarea { font: inherit; width: 100%; padding: 12px; border: 1px solid #999; border-radius: 0; resize: vertical; background: #fff; color: var(--ink); } +.composer textarea:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; } +.composer .composer-post { align-self: flex-end; margin: 0; padding: 10px 24px; border-radius: 0; } diff --git a/src/components/Composer.tsx b/src/components/Composer.tsx new file mode 100644 index 0000000..02b367b --- /dev/null +++ b/src/components/Composer.tsx @@ -0,0 +1,36 @@ +import type { FormEvent } from "react"; + +export interface ComposerProps { + value: string; + busy: boolean; + error: boolean; + onChange: (value: string) => void; + onSubmit: () => void; +} + +export function Composer({ value, busy, error, onChange, onSubmit }: ComposerProps) { + const empty = value.trim().length === 0; + function submit(e: FormEvent) { + e.preventDefault(); + if (!empty && !busy) onSubmit(); + } + return ( +
+ +