~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain

photography-focused photo sharing platform

grain.social
1 collaborator
0
2
78 lines · 2.8 KB Raw History
# Comments Feature Design

## Overview

Add gallery detail page with a comment bottom sheet, matching grain-next's UX adapted to SvelteKit + hatk.

## Gallery Detail Page

**Route:** `/profile/[did]/gallery/[rkey]`

- Auth required (server-side redirect like `/create`)
- Reuses existing `GalleryCard` component at the top
- Comment icon on card opens the bottom sheet
- Server-side load fetches gallery data + comment thread

## Comment Sheet (Bottom Sheet)

Slides up from bottom, overlays the page.

### Layout

- **Header:** "Comments" title + close button
- **Comment list:** Scrollable, oldest-first. Roots first, replies indented below parent (single-level threading)
- **Load earlier comments** button at top (cursor-based pagination, 20 per page)
- **Empty state:** "No comments yet. Be the first!"
- **Input bar** fixed at bottom: user avatar, optional focus photo thumbnail (with clear button), text input, Post button

### Each Comment

- Avatar, handle, comment text (with rich text facets), relative time
- Reply button, Delete button (owner only)
- Focus photo thumbnail (40x40) on the right if present

### Posting

1. User types text, taps Post
2. `callXrpc('dev.hatk.createRecord', { collection: 'social.grain.comment', record: { text, subject, focus?, replyTo?, facets?, createdAt } })`
3. Optimistically add comment to list
4. Parse text for facets (reuse `rich-text.ts`)

### Replies

- Tap Reply → prefill input with `@handle `, set `replyToUri`, focus input
- Reply appears indented below parent

### Deletion

- Tap Delete → `callXrpc('dev.hatk.deleteRecord', { collection: 'social.grain.comment', rkey })` → remove from list

## Server-Side: `social.grain.unspecced.getGalleryThread`

New XRPC endpoint in `server/xrpc/`:

- **Params:** `galleryUri`, `limit` (default 20), `cursor`
- **Query:** `social.grain.comment` records where `subject = galleryUri`, sorted `createdAt ASC`
- **Hydration:** Author profile (displayName, avatar), focus photo thumbnail
- **Response:** `{ comments: CommentView[], cursor?, totalCount }`

## New Files

- `server/xrpc/getGalleryThread.ts` — XRPC endpoint
- `lexicons/social/grain/unspecced/getGalleryThread.json` — Lexicon definition
- `app/routes/profile/[did]/gallery/[rkey]/+page.svelte` — Gallery detail page
- `app/routes/profile/[did]/gallery/[rkey]/+page.server.ts` — Auth guard + data loading
- `app/lib/components/organisms/CommentSheet.svelte` — Bottom sheet with comment list + input
- `app/lib/components/molecules/Comment.svelte` — Single comment display

## Modified Files

- `app/lib/components/molecules/GalleryCard.svelte` — Make comment button accept onclick handler
- `app/lib/queries.ts` — Add query for getGalleryThread

## Constraints

- Comment text max 300 graphemes / 3000 bytes
- Single-level threading only (replies to comments, not replies to replies)
- 20 comments per page
- Auth required to view and post