diff --git a/src/app/api/circles/[id]/posts/[postId]/route.ts b/src/app/api/circles/[id]/posts/[postId]/route.ts index 7b4f909..d1d93c6 100644 --- a/src/app/api/circles/[id]/posts/[postId]/route.ts +++ b/src/app/api/circles/[id]/posts/[postId]/route.ts @@ -3,6 +3,36 @@ import { sql } from "@/db"; import { requireFamily } from "@/lib/auth"; import { S3Client, DeleteObjectCommand } from "@aws-sdk/client-s3"; +// PATCH — author can edit the text body of their own post +export async function PATCH( + req: Request, + { params }: { params: Promise<{ id: string; postId: string }> } +) { + try { + const auth = await requireFamily(); + if (!auth.success) return NextResponse.json({ error: auth.error }, { status: auth.status }); + + const familyId = auth.session!.familyId!; + const { id: circleId, postId } = await params; + + const [post] = await sql.unsafe( + `SELECT author_family_id as "authorFamilyId" FROM circle_posts WHERE id = $1 AND circle_id = $2`, + [postId, circleId] + ); + if (!post) return NextResponse.json({ error: "Post not found" }, { status: 404 }); + if (post.authorFamilyId !== familyId) + return NextResponse.json({ error: "Only the author can edit this post" }, { status: 403 }); + + const body = await req.json(); + const newBody = (body.body ?? "").trim(); + + await sql.unsafe(`UPDATE circle_posts SET body = $1 WHERE id = $2`, [newBody || null, postId]); + return NextResponse.json({ success: true }); + } catch (err: unknown) { + return NextResponse.json({ error: err instanceof Error ? err.message : String(err) }, { status: 500 }); + } +} + function makeR2Client() { return new S3Client({ region: "auto", diff --git a/src/app/circle/[id]/page.tsx b/src/app/circle/[id]/page.tsx index 3901208..ea934d0 100644 --- a/src/app/circle/[id]/page.tsx +++ b/src/app/circle/[id]/page.tsx @@ -36,6 +36,10 @@ function PostCard({ const [showMenu, setShowMenu] = useState(false); const [reportSent, setReportSent] = useState(false); const [deleteConfirm, setDeleteConfirm] = useState(false); + const [editMode, setEditMode] = useState(false); + const [editBody, setEditBody] = useState(post.body ?? ""); + const [savingEdit, setSavingEdit] = useState(false); + const [lightbox, setLightbox] = useState(false); const loadComments = async () => { const res = await fetch(`/api/circles/${circleId}/posts/${post.id}/comments`); @@ -76,10 +80,38 @@ function PostCard({ onDeleted(post.id); }; + const saveEdit = async () => { + setSavingEdit(true); + await fetch(`/api/circles/${circleId}/posts/${post.id}`, { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ body: editBody }), + }); + post.body = editBody; // optimistic local update + setEditMode(false); + setSavingEdit(false); + }; + const isOwn = post.authorFamilyId === myFamilyId; return ( -
{timeAgo(post.createdAt)}{post.sourceKind ? ` · shared a ${post.sourceKind}` : ""}
{post.body}
} + {/* Body — inline edit or display */} + {editMode ? ( +{post.body}
+ )} - {/* Image */} + {/* Image — contain (no crop) + tap for fullscreen */} {post.imageUrl && ( -Tap to view full size
+