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

grain.social/grain

photography-focused photo sharing platform

grain.social
1 collaborator
0
2
143 lines · 4.3 KB Raw History

Grain hatk Template — MVP Design

Goal

Rebuild grain-next (Lit Web Components + GraphQL/Quickslice) as a hatk-powered SvelteKit app, using hatk-template-teal as the scaffold. MVP: shell + auth + timeline with gallery cards, running end-to-end.

Source Projects

  • hatk-template-teal (~/code/hatk-template-teal): SvelteKit 5 + hatk scaffold for teal.fm music tracker. Provides the application shell, auth flow, component architecture, and build tooling.
  • grain-next (~/code/grain-next): Lit Web Components PWA for Grain photo sharing. Provides lexicons, data models, and feature reference.

Architecture

Stack

  • SvelteKit 5 + Svelte 5 (from teal template)
  • hatk framework (XRPC endpoints, feeds, OAuth, SQLite)
  • TanStack Svelte Query (client-side data fetching/caching)
  • Vite build tooling

Key Translation

grain-nexthatk-template-grain
Lit Web ComponentsSvelte 5 components
GraphQL via Quickslicehatk XRPC + feeds
Client-side SPA routerSvelteKit file-based routing
Custom query/record cacheTanStack Svelte Query
Quickslice OAuthhatk OAuth

Project Setup

  1. Copy hatk-template-teal structure wholesale into hatk-template-grain.
  2. Replace lexicons/fm/teal/ with lexicons/social/grain/ (copied from grain-next).
  3. Keep lexicons/app/bsky/, lexicons/com/atproto/, lexicons/dev/hatk/.
  4. Regenerate types with npx hatk generate types.
  5. Update hatk.config.ts:
    • signalCollections: ["social.grain.actor.profile"]
    • OAuth scopes for all Grain collections (request upfront).
  6. Update package.json name/description.

Styling

Keep teal's dark theme and CSS variables unchanged. Tweak later.

Routes (MVP)

app/routes/
├── +layout.svelte          # Shell (3-column, from teal)
├── +layout.server.ts       # Session parsing
├── +layout.ts              # Query client setup
├── +page.svelte            # Timeline (gallery cards)
├── +page.ts                # Timeline feed query
├── oauth/callback/         # OAuth redirect
└── profile/[did]/          # Profile page (placeholder)

Server (MVP)

server/
├── feeds/
│   └── recent.ts           # Recent galleries feed
└── xrpc/                   # Empty for now

Components

Keep from teal (as-is)

  • Shell.svelte — 3-column layout
  • Sidebar.svelte — left nav (logo, home, auth)
  • SidebarRight.svelte — right sidebar (strip trending artists/genres, keep search bar placeholder)
  • Avatar.svelte, Button.svelte, Skeleton.svelte, Modal.svelte — generic atoms
  • AuthBar.svelte, LoginModal.svelte — auth organisms

Replace

tealgrain
PlayCard.svelteGalleryCard.svelte
ScrobbleModal.svelteRemove
BookmarkButton.svelteRemove
FeedTabs.svelteSimplify to single "Recent" tab

GalleryCard.svelte

Displays:

  • Thumbnail grid (first ~4 photos from gallery)
  • Gallery title
  • Author chip (avatar + handle)
  • Photo count

Feed: Recent Galleries

Server (server/feeds/recent.ts):

  • Query social.grain.gallery ordered by created_at DESC
  • Hydrate with author profile (social.grain.actor.profile)
  • Join social.grain.gallery.item for photo refs, then social.grain.photo for blobs/aspect ratios
  • Cursor-based pagination

Client:

  • +page.ts calls dev.hatk.getFeed with feed recent
  • TanStack Query caches + paginates
  • +page.svelte renders GalleryCard list

Auth

Identical to teal's hatk OAuth flow. Update scopes to:

atproto
repo:social.grain.gallery
repo:social.grain.gallery.item
repo:social.grain.photo
repo:social.grain.actor.profile
repo:social.grain.graph.follow
repo:social.grain.favorite
repo:social.grain.comment

hatk.config.ts

signalCollections: ["social.grain.actor.profile"];

What's Deferred

  • Comments, favorites, notifications
  • Search / explore
  • Gallery create/edit UI
  • Photo upload
  • EXIF extraction
  • Profile editing
  • Follow/unfollow
  • Rich text / facets
  • PWA / service worker
  • Custom accent color / branding