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

grain.social/grain

photography-focused photo sharing platform

grain.social
1 collaborator
0
2
110 lines · 3.9 KB Raw History
# Stories Design

## Overview

Ephemeral photo posts that disappear after 24 hours. Lower friction than galleries — just pick a photo, optionally tag a location, and post. Stories encourage casual, frequent sharing alongside the more intentional gallery format.

## Data Model

### Record: `social.grain.story`

```json
{
  "type": "record",
  "key": "tid",
  "record": {
    "required": ["media", "aspectRatio", "createdAt"],
    "properties": {
      "media": { "type": "blob", "accept": ["image/*", "video/*"], "maxSize": 5000000 },
      "aspectRatio": { "ref": "social.grain.defs#aspectRatio" },
      "location": { "ref": "social.grain.defs#location" },
      "createdAt": { "type": "string", "format": "datetime" }
    }
  }
}
```

- One record = one photo (or video in the future).
- **Active stories** = records where `createdAt > now - 24h`. No expiry field — computed at query time.
- Records stay in the repo permanently. The app view only serves active ones by default.
- V1 is images only. Video support requires no schema changes.

### View: `social.grain.story.defs#storyView`

```
uri, cid, creator (profileView), thumb, fullsize, aspectRatio, location?, createdAt
```

## Server

### XRPC Queries

**`social.grain.unspecced.getStoryAuthors`**

- Returns authors with active stories, ordered by most recent post.
- Each entry: `profileView` + story count.
- Used by the story strip.

**`social.grain.unspecced.getStories`**

- Params: `did` (required).
- Returns that author's active stories in chronological order as `storyView[]`.
- Used by the full-screen viewer.

### New Lexicons

- `lexicons/social/grain/story.json`
- `lexicons/social/grain/story/defs.json`
- `lexicons/social/grain/unspecced/getStoryAuthors.json`
- `lexicons/social/grain/unspecced/getStories.json`

### Cleanup

Old stories remain in the AT Protocol repo (no auto-deletion). The app view simply stops serving them after 24h. A background cleanup job is not needed for v1.

## Client UI

### Story Strip

- Horizontal scrollable row at the top of the home feed.
- First circle: "+" with the user's own avatar for creating a story.
- Remaining circles: authors with active stories, avatar with a colored ring.
- Tap opens the full-screen story viewer for that author.

### Story Viewer (Full-Screen Overlay)

- Photo fills the screen with a dark background.
- **Progress bars at top**: thin segmented bar, one segment per story from that author. Current segment fills left-to-right over 5 seconds, then auto-advances.
- Author name/avatar overlaid at top below progress bars.
- Location tag overlaid if present.
- **Navigation**: tap right side = next, tap left side = previous. Last tap advances to next author. Swipe to skip author.
- Close button (X) top-right.
- No interactions (hearts, replies) in v1. View-only.

### Story Creation

- Triggered from the "+" circle in the story strip.
- Opens a file picker (single photo for v1).
- Optional location tag (reuses `LocationInput` component).
- Post button. No title, no description.
- Could be a modal or a minimal page.

### New Components

- `StoryStrip.svelte` — horizontal avatar row for the feed.
- `StoryViewer.svelte` — full-screen overlay with progress bars and navigation.
- `StoryCreate.svelte` — simplified create flow (modal or page).

### Queries (in `queries.ts`)

- `storyAuthorsQuery()` — feeds the story strip.
- `storiesQuery(did)` — feeds the viewer.

## Decisions

- **24-hour expiry**, computed not stored.
- **No grouping/containers** — a "story" in the viewing sense is just all of a user's story records from the last 24h.
- **`social.grain.story`** namespace (not beta/unspecced) — ephemeral nature makes namespace caution less important.
- **Images only for v1** — schema accepts video MIME types for future support.
- **No interactions for v1** — view count could be added later, reactions/replies after that.
- **No dedicated route** — viewer is an overlay, not a page.