Overview
Add genre and language fields to storylines. Writers set these when creating a storyline; readers filter by them on the discovery page.
These are DB-only fields (not on-chain) — genre and language are discovery metadata, not protocol state.
Genre List
Romance, Fantasy, Science Fiction, Mystery, Thriller, Horror, Adventure,
Historical Fiction, Contemporary Lit, Humor, Poetry, Non-Fiction,
Fanfiction, Short Story, Paranormal, Werewolf, LGBTQ+, New Adult,
Teen Fiction, Diverse Lit, Others
(Based on Wattpad categories, minus platform-specific ones like "Wattpad Originals" / "The Wattys" / "Editor's Picks", plus "Others")
Language List
English, Chinese, Korean, Japanese, Spanish, French, Hindi, Arabic,
Portuguese, Russian, Others
Implementation
1. Supabase Migration (supabase/migrations/00014_genre_language.sql)
Add two columns to storylines:
ALTER TABLE storylines
ADD COLUMN genre text,
ADD COLUMN language text NOT NULL DEFAULT 'English';
CREATE INDEX idx_storylines_genre ON storylines (genre);
CREATE INDEX idx_storylines_language ON storylines (language);
genre: nullable (existing storylines get NULL, treated as uncategorized)
language: defaults to 'English' for existing and new storylines
2. Update TypeScript types (lib/supabase.ts)
Add genre: string | null and language: string to the storylines row type.
3. Create storyline form (src/app/create/page.tsx)
Add two <select> dropdowns to the form, between the title input and the content textarea:
- Genre:
<select> with no default selection (placeholder: "Select genre..."). Required — form cannot submit without a genre pick.
- Language:
<select> defaulting to "English". Writer can change.
4. Storyline indexer (src/app/api/index/storyline/route.ts)
The indexer receives txHash + optional metadata from the client. Update:
- Accept
genre and language in the POST body (alongside existing txHash, content)
- Store them in the storylines upsert
5. Backfill cron (src/app/api/cron/backfill/route.ts)
When backfill processes StorylineCreated events, it won't have genre/language metadata (no client context). Leave them as defaults (genre=NULL, language='English').
6. Discovery page filters (src/app/page.tsx)
Add two filter dropdowns alongside the existing writer type filter:
- Genre filter: "All" (default) + all genre options. URL param:
?genre=Fantasy
- Language filter: "All" → shows all, "English" (default) + all language options. URL param:
?lang=English
Apply .eq("genre", genre) and .eq("language", language) to Supabase queries when not "All". Apply to all four tabs (New, Trending, Rising, Completed).
7. Story card (src/components/StoryCard.tsx)
Display genre as a small badge/tag on the card (e.g., Fantasy in muted text). Language badge only if not English (to avoid visual noise on the default case).
8. Story detail page (src/app/story/[storylineId]/page.tsx)
Display genre and language in the story header metadata area, alongside plot count, view count, etc.
9. usePublish hook (src/hooks/usePublish.ts)
The publish hook currently sends { txHash, content } to the indexer route. Extend the PublishOptions interface to accept optional metadata: Record<string, string> that gets forwarded to the indexer POST body. The create page passes { genre, language } as metadata.
Files to modify
supabase/migrations/00014_genre_language.sql — new migration
lib/supabase.ts — add types
src/app/create/page.tsx — add genre/language dropdowns to form
src/hooks/usePublish.ts — extend PublishOptions to forward metadata to indexer
src/app/api/index/storyline/route.ts — accept and store genre/language
src/app/api/cron/backfill/route.ts — leave defaults for backfilled storylines
src/app/page.tsx — add genre/language filter dropdowns + query filters
src/components/StoryCard.tsx — display genre badge
src/app/story/[storylineId]/page.tsx — display genre + language in header
Acceptance criteria
Overview
Add genre and language fields to storylines. Writers set these when creating a storyline; readers filter by them on the discovery page.
These are DB-only fields (not on-chain) — genre and language are discovery metadata, not protocol state.
Genre List
(Based on Wattpad categories, minus platform-specific ones like "Wattpad Originals" / "The Wattys" / "Editor's Picks", plus "Others")
Language List
Implementation
1. Supabase Migration (
supabase/migrations/00014_genre_language.sql)Add two columns to
storylines:genre: nullable (existing storylines get NULL, treated as uncategorized)language: defaults to'English'for existing and new storylines2. Update TypeScript types (
lib/supabase.ts)Add
genre: string | nullandlanguage: stringto the storylines row type.3. Create storyline form (
src/app/create/page.tsx)Add two
<select>dropdowns to the form, between the title input and the content textarea:<select>with no default selection (placeholder: "Select genre..."). Required — form cannot submit without a genre pick.<select>defaulting to "English". Writer can change.4. Storyline indexer (
src/app/api/index/storyline/route.ts)The indexer receives
txHash+ optional metadata from the client. Update:genreandlanguagein the POST body (alongside existingtxHash,content)5. Backfill cron (
src/app/api/cron/backfill/route.ts)When backfill processes
StorylineCreatedevents, it won't have genre/language metadata (no client context). Leave them as defaults (genre=NULL, language='English').6. Discovery page filters (
src/app/page.tsx)Add two filter dropdowns alongside the existing writer type filter:
?genre=Fantasy?lang=EnglishApply
.eq("genre", genre)and.eq("language", language)to Supabase queries when not "All". Apply to all four tabs (New, Trending, Rising, Completed).7. Story card (
src/components/StoryCard.tsx)Display genre as a small badge/tag on the card (e.g.,
Fantasyin muted text). Language badge only if not English (to avoid visual noise on the default case).8. Story detail page (
src/app/story/[storylineId]/page.tsx)Display genre and language in the story header metadata area, alongside plot count, view count, etc.
9.
usePublishhook (src/hooks/usePublish.ts)The publish hook currently sends
{ txHash, content }to the indexer route. Extend thePublishOptionsinterface to accept optionalmetadata: Record<string, string>that gets forwarded to the indexer POST body. The create page passes{ genre, language }as metadata.Files to modify
supabase/migrations/00014_genre_language.sql— new migrationlib/supabase.ts— add typessrc/app/create/page.tsx— add genre/language dropdowns to formsrc/hooks/usePublish.ts— extend PublishOptions to forward metadata to indexersrc/app/api/index/storyline/route.ts— accept and store genre/languagesrc/app/api/cron/backfill/route.ts— leave defaults for backfilled storylinessrc/app/page.tsx— add genre/language filter dropdowns + query filterssrc/components/StoryCard.tsx— display genre badgesrc/app/story/[storylineId]/page.tsx— display genre + language in headerAcceptance criteria
npm run typecheckpasses