Skip to content

[feat] Add genre and language metadata to storylines with discovery filters #265

@realproject7

Description

@realproject7

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

  • Genre dropdown on create page — required, no default pick
  • Language dropdown on create page — defaults to English
  • Genre and language stored in Supabase on storyline creation
  • Discovery page has genre filter (default: All) and language filter (default: English)
  • Filters apply to all four tabs (New, Trending, Rising, Completed)
  • Genre badge displayed on story cards
  • Genre + language shown on story detail page
  • Existing storylines show as uncategorized (genre=null) and English (language default)
  • npm run typecheck passes

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent/T3Assigned to T3 builder agent

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions