Skip to content

Fix cache synchronization and UI responsiveness for anime progress#43

Merged
FiberJW merged 1 commit intosenpaifrom
fiberjw/fix-cache-sync-ui-responsiveness
Feb 7, 2026
Merged

Fix cache synchronization and UI responsiveness for anime progress#43
FiberJW merged 1 commit intosenpaifrom
fiberjw/fix-cache-sync-ui-responsiveness

Conversation

@FiberJW
Copy link
Owner

@FiberJW FiberJW commented Jan 26, 2026

Why

The score/progress steppers could feel out of sync with what users just tapped because the UI waited on cache/network updates before reflecting changes. This update keeps the UI responsive while preserving cache as the source of truth.

How

  • Kept local display state for stepper values on details (displayScore, displayProgress) and list item containers (displayProgress) so taps render immediately.
  • Synced local display state back to cached AniList values via useEffect whenever cache updates arrive.
  • Added clamping and no-op guards:
    • Score constrained to 0..10.
    • Progress constrained to 0..episodes when episode count exists, otherwise >=0.
    • Skip mutation dispatch if there is no mediaListEntryId or the value does not actually change.
  • Updated the stepper handlers to route through shared changeScore / changeProgress logic and retained haptic feedback.
  • Set the debounced score/progress mutations to wait: 0 for immediate dispatch in these interactions.
  • Added explicit error handling around mutation calls (try/catch + console.error).

Test Plan

Actions

  • bun run lint
  • bunx tsc --noEmit
  • Manual QA on device/simulator:
    • Open an anime details screen.
    • Increment/decrement score and progress.
    • Navigate back to the list and confirm values stay in sync.

Assertions

  • Stepper values update instantly after each tap.
  • Score never goes below 0 or above 10.
  • Progress never goes below 0 and does not exceed known episode count.
  • Returning between details and list does not show stale values.

Test Reproduction

$ bun run lint
$ eslint . --ext .ts,.js,.tsx --fix

$ bunx tsc --noEmit
# exits successfully

Manual verification evidence is still needed (screen recording or screenshots from details -> list sync behavior).

@vercel
Copy link

vercel bot commented Jan 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
goodweebs Ready Ready Preview, Comment Feb 7, 2026 0:45am

@coderabbitai
Copy link

coderabbitai bot commented Jan 26, 2026

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (16)
  • app/(tabs)/anime.tsx is excluded by none and included by none
  • app/details/[id].tsx is excluded by none and included by none
  • bun.lock is excluded by !**/*.lock, !**/*.lock and included by none
  • codegen.ts is excluded by none and included by none
  • codegen.yml is excluded by !**/*.yml and included by none
  • containers/AnimeListItemContainer.tsx is excluded by none and included by none
  • graphql.schema.json is excluded by !**/*.json and included by none
  • graphql/fragments/Anime.ts is excluded by none and included by none
  • graphql/generated.tsx is excluded by none and included by none
  • graphql/mutations/UpdateProgress.ts is excluded by none and included by none
  • graphql/mutations/UpdateScore.ts is excluded by none and included by none
  • graphql/mutations/UpdateStatus.ts is excluded by none and included by none
  • graphql/queries/AnimeList.ts is excluded by none and included by none
  • hooks/helpers.ts is excluded by none and included by none
  • package.json is excluded by !**/*.json and included by none
  • screens/DetailsScreen/Stepper.tsx is excluded by none and included by none

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • 🔍 Trigger a full review
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fiberjw/fix-cache-sync-ui-responsiveness

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link

greptile-apps bot commented Jan 26, 2026

Greptile Overview

Greptile Summary

This PR successfully addresses cache synchronization issues between the anime details page and list view by implementing Apollo Client cache normalization instead of manual refetching.

Key improvements:

  • Mutations now return complete MediaList fields (progress, status, score) enabling Apollo to normalize responses by id + __typename
  • Replaced fetchPolicy: "no-cache" with "cache-and-network" for instant cached data display
  • Removed all refetchQueries calls in favor of cache.modify() for optimistic updates
  • Simplified component state management (AnimeListItemContainer uses local display state synced via useEffect)
  • Converted Stepper from uncontrolled to controlled component pattern
  • Upgraded GraphQL codegen from v2 to v6 (fixes pre-existing visitFn.call error)

The implementation properly leverages Apollo's normalized cache, ensuring UI updates propagate automatically across all components without manual synchronization.

Confidence Score: 4/5

  • This PR is safe to merge with low risk - well-architected cache synchronization improvements
  • The implementation correctly uses Apollo cache normalization patterns and simplifies state management. Minor risk from the AnimeListItemContainer's dual-state approach (local displayProgress + cache cacheProgress) which could theoretically cause brief inconsistencies during rapid updates, but the debouncing and useEffect synchronization should handle this appropriately.
  • Pay attention to containers/AnimeListItemContainer.tsx - verify the displayProgress/cacheProgress synchronization works correctly during rapid increment/decrement operations

Important Files Changed

Filename Overview
hooks/helpers.ts Removed refetchQueries parameter from useDebouncedMutation - now relies on cache normalization
app/(tabs)/anime.tsx Changed fetch policy from no-cache to cache-and-network, removed refetchList props from container
screens/DetailsScreen/Stepper.tsx Converted from uncontrolled to controlled component - removed internal state, uses value prop and separate increment/decrement callbacks
containers/AnimeListItemContainer.tsx Simplified to use local displayProgress state with cache sync via useEffect, replaced refetchQueries with cache.modify()
app/details/[id].tsx All mutations migrated from refetchQueries to cache.modify(), Stepper components converted to controlled pattern

Sequence Diagram

sequenceDiagram
    participant User
    participant DetailsPage as Details Page<br/>(app/details/[id].tsx)
    participant Stepper as Stepper Component
    participant Mutation as useDebouncedMutation
    participant Cache as Apollo Cache
    participant AnimeList as Anime List<br/>(app/(tabs)/anime.tsx)
    participant Container as AnimeListItemContainer

    User->>DetailsPage: Tap increment on Progress stepper
    DetailsPage->>Stepper: onIncrement callback
    DetailsPage->>Mutation: updateProgress({ progress: currentProgress + 1 })
    Mutation->>Cache: cache.modify() - update MediaList.progress field
    Cache-->>DetailsPage: Cache updated optimistically
    DetailsPage->>User: UI shows new value instantly
    
    Mutation->>Cache: SaveMediaListEntry mutation (GraphQL)
    Cache-->>Mutation: Server response with { id, progress, status, score }
    Cache->>Cache: Normalize response into cache (by id + __typename)
    
    Note over Cache,AnimeList: Cache update triggers re-render
    
    Cache-->>Container: mediaListEntry.progress updated
    Container->>Container: useEffect detects cacheProgress change
    Container->>Container: setDisplayProgress(cacheProgress)
    Container->>User: Anime list item shows synced value
    
    User->>DetailsPage: Navigate back to list
    AnimeList->>Cache: Query with fetchPolicy: "cache-and-network"
    Cache-->>AnimeList: Return cached data immediately
    AnimeList->>User: Show updated progress (no flicker)
    Cache->>AnimeList: Fetch fresh data in background
    Cache-->>AnimeList: Server data (already matches cache)
Loading

@expo
Copy link

expo bot commented Jan 26, 2026

🎉 Your workflow run has finished!

Check out the details below.


Builds

ID Platform Status Actions
7c02f60 Android ✅ Success (logs) Install Download
c8b165b iOS ✅ Success (logs) Install Download

@expo-staging
Copy link

expo-staging bot commented Jan 26, 2026

🎉 Your workflow run has finished!

Check out the details below.


Builds

ID Platform Status Actions
f0c5900 iOS ❌ Error (logs)
914dfa4 Android ✅ Success (logs) Install Download

@FiberJW FiberJW force-pushed the fiberjw/fix-cache-sync-ui-responsiveness branch from 6dfd6c9 to 3f34ca5 Compare February 7, 2026 12:43
@FiberJW FiberJW merged commit 7c1915b into senpai Feb 7, 2026
6 checks passed
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