Every infinite list set maxPages: 10 without getPreviousPageParam, so TanStack Query dropped the first page once an 11th was fetched and it could never be scrolled back to. These lists are forward-only, so remove maxPages.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* feat: reorganize API around operation domains
Restructure the oRPC contract from 14 resource-oriented routers to 8
domain-oriented routers for a cleaner public API surface.
- Consolidate 7 watch procedures into unified tracking.watch/unwatch
with scope + ids input (movie, episode, season, series)
- Split dashboard across tracking (stats, history), library
(continueWatching, upcoming), and discover (recommendations)
- Merge explore + search + discover into single discover router
- Absorb integrations into account.integrations
- Merge system.authConfig into system.publicInfo
- Collapse 6 admin setting endpoints into admin.settings.get/update
- Deduplicate platforms.list + explore.watchProviders into
discover.platforms
- Add symmetric unwatchMovie/unwatchSeries core functions
- Rename titles.detail→get, titles.recommendations→similar,
people.detail→get
BREAKING CHANGE: All client API paths have changed. REST paths now
mirror router structure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address review feedback on API reorganization
- Fix updateRating invalidation in native app — was only invalidating
title queries, now calls invalidateTitleQueries() to also refresh
tracking.userInfo (drives the rating UI)
- Make unwatchMovie status revert consistent with unwatchSeries — revert
any non-watchlist status, not just "completed"
- Add sync invariant comment on handleWatch/handleUnwatch loops
- Remove unused queryClient import in native use-title-actions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: updateStatus NOT_FOUND error + rate() invalidation in native
- updateStatus now throws NOT_FOUND when quickAddTitle returns null
(title doesn't exist), instead of silently succeeding
- Add NOT_FOUND error to updateStatus contract definition
- Fix rate() in native title-actions.ts to call invalidateTitleQueries()
instead of only invalidating orpc.titles.key() — mirrors the fix
already applied to the hook-based path in d2ddc0f
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: expand mobile app docs and add Play Store badge
- Add the Google Play badge to the README alongside the App Store badge
- Expand the mobile app docs with the Android/Play Store release details
- Refresh docs site dependencies and related package versions for the updated docs stack
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add filtering and sorting across library, explore, and upcoming
Add comprehensive filtering and sorting to all list views on both web and mobile.
Library (new dedicated page):
- New /library route (web) with URL search param persistence
- New Library tab (5th tab, mobile) with FlashList grid
- Filters: status (multi-select), type, genre (library-only), user rating range,
release year (decade presets + custom), content rating, available to stream
- Sort by: title, date added, release date, popularity, user rating, TMDB rating
- Text search within collection
- Filter popover (web) / bottom sheet with Apply (mobile)
- Remove old dashboard.library endpoint in favor of library.list
Explore/Discover:
- New Discover section on Explore page with inline filter controls
- TMDB filters: genre (now optional), year range, min rating, sort order,
original language, streaming provider
- Watch provider list endpoint (WATCH_REGION env var, default "US")
- Default to popular content when no filters selected
Upcoming:
- Type filter (All/Movies/TV Shows) and status filter (All/Watching/Watchlist)
- Toggle chips on both web (URL params) and mobile
- Backend: mediaType and statusFilter params on upcoming endpoint
Navigation:
- Library added to web nav bar (desktop + mobile tab bar)
- Library added as 5th tab on mobile (between Home and Explore)
- Dashboard library section simplified to compact preview with "See all" link
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add user streaming platform subscriptions and redesign filter UI
Replace the denormalized availabilityOffers table with a normalized
platforms + titleAvailability + userPlatforms schema. Users can now
declare which streaming services they subscribe to, enabling
personalized "On your services" availability display and filtered
library/explore results.
Backend: new platforms table seeded from TMDB provider data on startup,
new API endpoints (platforms.list, account.platforms, account.updatePlatforms),
title detail annotates availability with isUserSubscribed flag, library
"on my services" filter checks user's subscribed platforms.
Web UI: post-registration onboarding page for platform selection,
streaming services settings section, title detail split into "On your
services" / "Also available on", explore dropdown uses platforms table
with active filter highlighting. Library filters redesigned from
cluttered popover to clean collapsible inline strip with consolidated
dropdowns.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address race conditions, stale state, and edge cases in platforms feature
- Fix debounced autosave race in streaming services settings: use ref
for latest selection + counter guard so only the most recent save
updates UI. Clean up timers on unmount.
- Fix explore provider dropdown desyncing from query filter when
switching Movie/TV type: reset selectedPlatformId alongside providerId.
- Fix platformIdsExist rejecting duplicate valid IDs by deduplicating
input before comparing against DB results.
- Fix stale platform metadata: always upsert name/logo on TMDB refresh
instead of skipping when platform already exists.
- Fix invisible ratingMax filter: clear ratingMax when user changes
ratingMin since the UI only exposes a single rating dropdown now.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: clear stale saved-state timer and preserve falsy filter values
- Clear previous savedTimerRef timeout before scheduling a new one in
streaming-services-section, preventing an older timer from hiding the
"Saved" indicator too early on rapid successive saves
- Replace `value || undefined` with explicit empty-value check in
library filter handler so numeric 0 (e.g. ratingMin=0) is not
incorrectly dropped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add TanStack Form and migrate auth + change password forms
Set up TanStack Form v1 composition layer with createFormHook, pre-bound
field components (TextField, TextareaField, CheckboxField, SwitchField,
SubmitButton), and migrate the two traditional submit-based forms:
- AuthForm: replace 4 useState calls with useAppForm, use form.Field
for each input while preserving motion animation layout
- ChangePasswordDialog: replace 6 useState calls with useAppForm + Zod
schema validation (cross-field password match, min length), per-field
error display, form.reset() on dialog close
Also adds @tanstack/react-form to the monorepo catalog and updates both
web and native apps to reference it via catalog:.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add `RouteError` component for route-level error boundaries (web) with "Try again" and "Dashboard" links; wire it into dashboard, explore, upcoming, people, and settings routes
- Add `await queryClient.cancelQueries()` before every optimistic update in `useTitleActions` to prevent in-flight refetches from overwriting local state
- Cap all `useInfiniteQuery` calls at `maxPages: 10` (web and native) to bound memory growth on scroll-heavy pages
- Replace `ActivityIndicator` with `Spinner` in search, person detail, and list footer components (native)
- Tune native query client defaults: add `staleTime: 60_000` and lower `gcTime` to `300_000` (was 24h)
- Darken `--color-muted-foreground` for better contrast in the native theme
- Improve error message copy in `error-messages.ts` for clarity (TMDB, import, registration errors)
- Update i18n `.po` files across all 12 locales with new strings (route error, improved error messages)
- Replace root `eslint-plugin-lingui` jsPlugin with `@e18e/eslint-plugin`; move lingui rules into per-app `.oxlintrc.json` overrides for native and web
- Add `jsx-a11y`, `react-hooks-js` (native), and expanded lingui rule sets to per-app configs
- Add `oxc/no-barrel-file` warning at threshold 0 to root config
- Fix `e18e/prefer-url-canparse`: replace `try { new URL() } catch` with `URL.canParse()` in server-url screen and server lib
- Fix `e18e/prefer-timer-args`: pass callback args directly to `setTimeout` instead of wrapping in arrow functions (use-debounce, integration-card)
- Fix `e18e/prefer-static-regex`: hoist `/\/+$/` to module-level constant in server-url screen
- Fix `react-hooks-js/refs` and `react-hooks-js/set-state-in-effect`: convert `useRef` tracking patterns to `useState` + render-time derived updates in `use-server-connection`, `expandable-text`, and settings screen
- Fix `react-hooks-js/immutability`: extract stable palette sub-values before `useMemo` deps in title detail screen
- Apply `e18e` and other rule fixes across core, tmdb, web components, and i18n packages