Skip to content

KSN: migrate catalog and user data to Supabase#13

Open
codebend3r wants to merge 12 commits into
mainfrom
migrate-to-supabase
Open

KSN: migrate catalog and user data to Supabase#13
codebend3r wants to merge 12 commits into
mainfrom
migrate-to-supabase

Conversation

@codebend3r

Copy link
Copy Markdown
Owner

Moves kasane's static data off bundled JSON and into Supabase (project obtgldkascmxbtpnvscn), so mappings update without an app release, the bundle shrinks, and user data syncs across devices.

Schema

  • New migration adds five public read-only catalog tables (series, arc_mappings, movies, search_aliases, genre_filters) and two owner-scoped user tables (user_progress, user_preferences), all under RLS. Security advisors report zero issues.

Seeding

  • One-time seeder loaded the 578 mappings in original ALL_MAPPINGS order, preserving the shared-manga-id tie-break (first-listed wins).
  • Live counts: 578 series, 2,767 arcs, 221 movies, 30 aliases, 27 genre filters.

Client

  • src/data/catalog.ts fetches everything in one nested query and exposes useCatalog / useMapping / useGenreFilters via React Query.
  • Catalog query persisted to AsyncStorage (kept a week) for instant, offline cold starts; AniList/MangaDex stay in-memory.
  • All consumers (SeriesCard, home, continue, and the 6 series/anime/manga screens) read from the DB. Synthetic-mapping fallback waits for the catalog to load to avoid an auto-estimated flash.

User sync

  • src/state/sync.ts: pull + last-write-wins merge on login, debounced push on change, delete propagation. Local zustand stays the immediate source of truth; logged-out users are unaffected. Merge logic has unit tests.

Cleanup

  • Deleted src/data/mappings/ (578 files), top100-manifest.json, and the import wall / ALL_MAPPINGS / findMappingByMediaId / normalizeMapping from index.ts (1,385 → 145 lines).
  • Web bundle: 2.6 MB / 1,688 modules → 2.0 MB / 1,110 modules.
  • README updated; the kasane-arc-mapping skill now documents the DB-insert workflow.

Verification

  • bun run system-check (typecheck + prettier + eslint + 39 tests + web build) green on every commit.
  • Verified the real read path via the publishable key (One Piece returns 9 arcs + 15 movies, Elbaf arc has null episodes) and that anonymous writes to user_progress are blocked by RLS.

Notes

  • The authenticated sync-push path (upsert as a signed-in user) is covered by unit-tested merge logic and verified owner-only RLS, but was not exercised against a live session (no test credentials).

- Add migration creating catalog tables: `series`, `arc_mappings`,
  `movies`, `search_aliases`, `genre_filters`
- Add owner-scoped `user_progress` and `user_preferences` tables
- Public read-only RLS on catalog; `auth.uid() = user_id` on user tables
- Add `react-query-persist-client` + async-storage persister for the
  upcoming offline catalog cache
- Read the 578 mapping JSONs in `ALL_MAPPINGS` order and load `series`,
  `arc_mappings`, and `movies` via the PostgREST client
- Preserve insertion order so identity ids keep the shared-manga-id
  tie-break (first-listed series wins), matching `findMappingByMediaId`
- Seeded 578 series, 2767 arcs, 221 movies, 30 aliases, 27 genre filters
- Add `src/data/catalog.ts`: fetch series + arcs + movies + aliases +
  genre filters from Supabase in one pass, assemble into `SeriesMapping`
- Expose `useCatalog`/`useMapping`/`useGenreFilters` via React Query,
  cached 1h and kept a week for offline cold starts
- Preserve the shared-manga-id tie-break by fetching series in id order
  and letting the first-listed win
- Type the Supabase client with generated `Database` types
- Make search aliases a runtime-hydratable singleton
- Wrap the app in `PersistQueryClientProvider` backed by AsyncStorage
- Dehydrate only the `catalog` query (AniList/MangaDex stay in-memory),
  kept for up to a week
- Warm the catalog at launch so mappings and search aliases are ready
  before the first screen renders
- Drop the static `GENRE_FILTERS` array; `splitHiddenForAniList` now takes
  the filter list as an argument
- Home screen and the mobile filter sheet render `useGenreFilters()`
- Keep the always-hidden Ecchi/Hentai policy in code
- Rework the genre-filters test to exercise the pure bucketing logic
  against a fixture
- `SeriesCard` and the arc screens read `useMapping`/`useCatalog`
- Series/anime/manga detail screens resolve the curated mapping via
  `useCatalog().findMapping` and only fall back to a synthetic mapping
  once the catalog has loaded (avoids an auto-estimated flash)
- Arc screens show a spinner until the catalog resolves, then the
  not-found state
- Stamp `updatedAt` on every japanese/hidden-genre toggle and persist it
- Enables last-write-wins reconciliation against the Supabase copy
- `mergeProgress`: last-write-wins union of local and server progress,
  per (routeId, side), plus the rows that need pushing
- `diffProgress`: upsert/delete deltas to move the server between states
- `mergePreferences`: last-write-wins for the single preferences row
- Cover all three with unit tests
- Add a cloud-sync controller: pull + last-write-wins merge on login,
  debounced push of local changes, delete propagation for cleared entries
- Local zustand stores stay the immediate source of truth; sync is
  best-effort and never blocks the UI (logged-out users are unaffected)
- Guard against pull->push echo with an applying-remote flag
- Start the controller once from the root layout
- Include aliases in the catalog query result and push them into the
  alias singleton via an effect keyed on the data
- Fixes aliases staying empty on a cache-restored start, where the query
  function (which previously hydrated them) never runs
- Delete `src/data/mappings/` (578 files) and `top100-manifest.json`
- Strip the import wall, `ALL_MAPPINGS`, `findMappingByMediaId`, and the
  `normalizeMapping` layer from `src/data/index.ts`; keep only the pure
  helpers (`pairResults`, `buildSyntheticMapping`, episode/chapter lookup)
- Empty the bundled search-alias defaults (loaded from the DB now)
- Reword detail-screen copy that pointed contributors at the JSON files
- Drops ~578 modules and ~0.6 MB from the web bundle
- Document mappings/aliases/genre filters living in Supabase
- Rewrite "Contributing a mapping" as a SQL insert into `series` +
  `arc_mappings` instead of a JSON file + `index.ts` import
- Refresh the stack list and project layout
@cursor

cursor Bot commented Jul 16, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@netlify

netlify Bot commented Jul 16, 2026

Copy link
Copy Markdown

Deploy Preview for kasane-guide ready!

Name Link
🔨 Latest commit 1ebed34
🔍 Latest deploy log https://app.netlify.com/projects/kasane-guide/deploys/6a585d838a840400087c1498
😎 Deploy Preview https://deploy-preview-13--kasane-guide.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@supabase

supabase Bot commented Jul 16, 2026

Copy link
Copy Markdown

Updates to Preview Branch (migrate-to-supabase) ↗︎

Deployments Status Updated
Database Thu, 16 Jul 2026 04:27:28 UTC
Services Thu, 16 Jul 2026 04:27:28 UTC
APIs Thu, 16 Jul 2026 04:27:28 UTC

Tasks are run on every commit but only new migration files are pushed.
Close and reopen this PR if you want to apply changes from existing seed or migration files.

Tasks Status Updated
Configurations Thu, 16 Jul 2026 04:27:29 UTC
Migrations Thu, 16 Jul 2026 04:27:29 UTC
Seeding Thu, 16 Jul 2026 04:27:29 UTC
Edge Functions Thu, 16 Jul 2026 04:27:29 UTC

View logs for this Workflow Run ↗︎.
Learn more about Supabase for Git ↗︎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant