Skip to content

fix(person-images): prevent flash of previous page's images (#1315)#1321

Open
tanmaysachann wants to merge 3 commits into
AOSSIE-Org:mainfrom
tanmaysachann:fix/1315-ai-tagging-page-flash
Open

fix(person-images): prevent flash of previous page's images (#1315)#1321
tanmaysachann wants to merge 3 commits into
AOSSIE-Org:mainfrom
tanmaysachann:fix/1315-ai-tagging-page-flash

Conversation

@tanmaysachann

@tanmaysachann tanmaysachann commented Jun 13, 2026

Copy link
Copy Markdown

When a face cluster is opened from the Navbar search panel, PersonImages briefly rendered the previous page's images before the person's photos loaded, producing the flash described in the issue. This PR scopes the grid to the active cluster so the stale images can never paint.

Root cause: the original "redirect to /ai-tagging" hypothesis did not match the code; PersonImages has no such redirect (it only navigates on the user-clicked "Back to AI Tagging" button). The real cause is the shared images Redux slice: the grid renders from selectImages, which still holds whatever the previous page left there until fetchClusterImages resolves and setImages runs. For the first frame(s) that stale set is on screen. Thanks to @VanshajPoonia for independently confirming this and flagging the cached-navigation case.

The fix (scoped to PersonImages.tsx):

  • Track which cluster the shared slice holds via a loadedClusterId marker, set alongside setImages when the query succeeds.
  • Once the slice is in sync for the active clusterId, read the slice (so the optimistic favourite toggle keeps working); during the brief window before it syncs, fall back to the ['person-images', clusterId] React Query data, which is already scoped to the active id and can only ever be the correct person's photos.
  • Point the grid and media viewer at this derived list instead of the raw slice.

This does not rely on a loading flag, so it also covers person A to person B when B is already cached (isLoading is false immediately while the slice still holds A for a tick), which was @VanshajPoonia's review point.

Addressed Issues:

Fixes #1315

Screenshots/Recordings:

This is a sub-frame rendering flash, so it is covered by an automated regression test rather than a recording. PersonImages.test.tsx pre-seeds the shared slice with a previous page's images, navigates to a different cluster, and asserts the previous images never paint (on the first frame and after the query resolves). The test fails against the pre-fix code and passes with the fix.

Additional Notes:

Reading the slice once synced (rather than reading query data everywhere) is deliberate: the favourite/heart toggle updates the slice, and its ['images'] cache invalidation does not match the ['person-images', id] key, so reading query data everywhere would fix the flash but stop the heart icon from updating. The hybrid keeps both correct. Scope is limited to PersonImages.tsx and its test. Verified locally: full frontend suite (133 tests), tsc, and ESLint all pass.

AI Usage Disclosure:

We encourage contributors to use AI tools responsibly when creating Pull Requests. While AI can be a valuable aid, it is essential to ensure that your contributions meet the task requirements, build successfully, include relevant tests, and pass all linters. Submissions that do not meet these standards may be closed without warning to maintain the quality and integrity of the project. Please take the time to understand the changes you are proposing and their impact. AI slop is strongly discouraged and may lead to banning and blocking. Do not spam our repos with AI slop.

Check one of the checkboxes below:

  • This PR does not contain AI-generated code at all.
  • This PR contains AI-generated code. I have read the AI Usage Policy and this PR complies with this policy. I
    have tested the code locally and I am responsible for it.

I have used the following AI models and tools: Claude Code (Anthropic Claude). I used it to investigate the root cause, draft the fix in PersonImages.tsx, and write the regression test. I reviewed every line, verified the diagnosis against the code myself, and confirmed the full frontend test suite, tsc, and ESLint pass locally before submitting.

Checklist

  • My PR addresses a single issue, fixes a single bug or makes a single improvement.
  • My code follows the project's code style and conventions
  • If applicable, I have made corresponding changes or additions to the documentation
  • If applicable, I have made corresponding changes or additions to tests
  • My changes generate no new warnings or errors
  • I have joined the Discord server and I will share a link to this PR with
    the project maintainers there
  • I have read the Contribution Guidelines
  • Once I submit my PR, CodeRabbit AI will automatically review it and I will address CodeRabbit's comments.
  • I have filled this PR template completely and carefully, and I understand that my PR may be closed without
    review otherwise.

Summary by CodeRabbit

  • Bug Fixes
    • Prevented stale images from briefly appearing when navigating between clusters by only displaying images for the currently selected cluster during loading and after transition.
  • Tests
    • Added a regression test suite to verify previous-cluster images never flash during navigation, and that the correct cluster name and images render after loading completes.

…rg#1315)

Scope the grid to the active cluster so the shared `images` slice left by the
previous page can no longer paint before the person's photos load. Track which
cluster the slice holds via `loadedClusterId`; until it syncs, render from the
`['person-images', clusterId]` query data, which is always scoped to the active
id. Reading the slice once synced keeps the optimistic favourite toggle working.

Adds a regression test that fails when the previous page's images render.
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a4064393-7399-486e-b8a7-c9b9e9487e89

📥 Commits

Reviewing files that changed from the base of the PR and between 1eebcba and 88187a7.

📒 Files selected for processing (2)
  • frontend/src/pages/PersonImages/PersonImages.tsx
  • frontend/src/pages/__tests__/PersonImages.test.tsx
💤 Files with no reviewable changes (1)
  • frontend/src/pages/tests/PersonImages.test.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/pages/PersonImages/PersonImages.tsx

Walkthrough

PersonImages now prevents stale image flash during cluster navigation by tracking which cluster's images are loaded in Redux via loadedClusterId, deriving the correct image source that falls back to query data until the slice syncs, and updating rendering to use derived personImages. Regression tests validate the fix prevents stale images during transitions.

Changes

Prevent stale images when navigating between person clusters

Layer / File(s) Summary
Track loaded cluster and derive current images
frontend/src/pages/PersonImages/PersonImages.tsx
loadedClusterId state records which cluster's images are in the Redux images slice. Derived personImages selects Redux images when loadedClusterId === clusterId, otherwise falls back to current query data.images to avoid stale contents during cluster transitions.
Update rendering to use derived images
frontend/src/pages/PersonImages/PersonImages.tsx
Image grid map and MediaView modal both switch from Redux images to personImages for rendering.
Regression tests for stale image flash
frontend/src/pages/__tests__/PersonImages.test.tsx
Full Jest/React Testing Library suite for issue #1315. Mocks Tauri and cluster image fetching, renders PersonImages with preloaded state seeded with "Person A" images, navigates to cluster "B", and validates that stale images do not render while new images appear correctly after async load.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

TypeScript/JavaScript

Poem

🐰 A hop, a gaze, no stale frame in sight,
Redux remembers which cluster's light,
Derived images hold the newest view,
No flashing ghosts — the pictures stay true,
I nibble bugs and cheer this code's delight.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: preventing a flash of previous page's images in PersonImages, which is the core change across both modified files.
Linked Issues check ✅ Passed The PR addresses the visual flash issue in #1315 by implementing a hybrid approach using loadedClusterId and fallback to React Query data, which differs from but successfully resolves the reported problem.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing the image flash issue. The author deliberately excluded unrelated cleanup (redundant setClusterName call) to maintain focus on the primary bug fix.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@github-actions github-actions Bot added bug Something isn't working frontend good first issue Good for newcomers labels Jun 13, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
frontend/src/pages/PersonImages/PersonImages.tsx (1)

64-65: 💤 Low value

Remove redundant assignment.

setClusterName(clusterName) sets the state to its current value, which is redundant.

♻️ Proposed simplification
   const handleEditName = () => {
-    setClusterName(clusterName);
     setIsEditing(true);
   };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/PersonImages/PersonImages.tsx` around lines 64 - 65,
Remove the redundant state call setClusterName(clusterName) inside the handler
and only call setIsEditing(true); if the intent was to update the cluster name
to a different value, replace the current argument with the new value (e.g.,
setClusterName(newValue))—otherwise delete the setClusterName(clusterName) line
so only setIsEditing(true) remains.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/pages/PersonImages/PersonImages.tsx`:
- Around line 55-61: The code uses a triple-cast to any when reading images from
data; replace that with an explicit BackendRes<{ images: Image[] }> typed
access. Change the expression for personImages to use (data as BackendRes<{
images: Image[] }> | undefined)?.data?.images ?? [] so you avoid "any" and
preserve types for images, leaving loadedClusterId, clusterId and images logic
unchanged; add an import or reference to BackendRes and Image if not already
present.
- Line 50: The component is calling setLoadedClusterId with clusterId from
useParams which can be undefined; update the logic in PersonImages (where
clusterId and setLoadedClusterId are used) to guard against undefined by
returning early if clusterId is undefined (or at least do not call
setLoadedClusterId when clusterId is undefined) so the derived check that picks
between Redux images and query data doesn't mistakenly treat a stale
loadedClusterId; ensure the early-return or conditional prevents running the
downstream image-selection logic when clusterId is missing.

---

Nitpick comments:
In `@frontend/src/pages/PersonImages/PersonImages.tsx`:
- Around line 64-65: Remove the redundant state call setClusterName(clusterName)
inside the handler and only call setIsEditing(true); if the intent was to update
the cluster name to a different value, replace the current argument with the new
value (e.g., setClusterName(newValue))—otherwise delete the
setClusterName(clusterName) line so only setIsEditing(true) remains.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c6bba0b1-9ef7-4259-8319-f80129c377e5

📥 Commits

Reviewing files that changed from the base of the PR and between 0d0cbec and c85b57f.

📒 Files selected for processing (2)
  • frontend/src/pages/PersonImages/PersonImages.tsx
  • frontend/src/pages/__tests__/PersonImages.test.tsx

Comment thread frontend/src/pages/PersonImages/PersonImages.tsx
Comment thread frontend/src/pages/PersonImages/PersonImages.tsx Outdated
Addresses CodeRabbit review on the PR: only treat the slice as synced when
clusterId is defined, and type the query-data fallback explicitly instead of
casting through any.
@tanmaysachann

Copy link
Copy Markdown
Author

Re: the nitpick about the redundant setClusterName(clusterName) in handleEditName. This is pre-existing code that isn't part of the flash fix, so I've left it out to keep this PR scoped to #1315. Happy to remove it in a separate cleanup if preferred.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a UI flash in the PersonImages page where images from the previously visited page could briefly render while the target person’s cluster images are still loading (due to a shared images Redux slice being reused across pages).

Changes:

  • Track which clusterId the shared images slice currently represents (loadedClusterId) and only render from the slice once it matches the active route param.
  • While the slice is still “out of sync”, render images from the cluster-scoped React Query response (['person-images', clusterId]) to prevent stale images from ever painting.
  • Add a regression test that pre-seeds the shared slice with previous-page images and asserts they never appear when navigating to a different cluster.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
frontend/src/pages/PersonImages/PersonImages.tsx Adds cluster-scoping logic to prevent stale Redux-slice images from rendering during navigation/loading.
frontend/src/pages/__tests__/PersonImages.test.tsx Adds a regression test to ensure previous-page images never paint when opening a person cluster.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

</Button>
)}
</div>
<h1 className="mb-6 text-2xl font-bold">{clusterName}</h1>

@rohan-pandeyy rohan-pandeyy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please try to avoid leaving messy comments in the codebase

Comment on lines +55 to +57
// Until the slice is synced for this cluster it still holds the previous
// page's images (issue #1315). Fall back to the cluster-scoped query data for
// that window; read the slice once synced so favourite toggles still work.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please try to avoid leaving messy comments in the codebase. Comments are definitely appreciated, but it’s best to keep them concise and clean. Single-line comments are preferred, and at most two lines in rare cases.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed your feedback, I've removed the verbose comments and kept only a concise single-line one. Please take a look!

@VanshajPoonia

VanshajPoonia commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Image gating with loadedClusterId works well. One thing though - I think the name in the heading still flashes the same way the images used to.

clusterName only gets set inside the success effect, so it lags a frame behind. Going from one person to another (even when the next one's already cached and the grid is correct), you briefly see the previous person's name in the <h1> before the new one loads. Copilot refered to this one too.

Should just be the same trick you already used for personImages:

const displayName =
clusterId !== undefined && loadedClusterId === clusterId
? clusterName
: ((data?.data as { cluster_name?: string })?.cluster_name ?? 'random_name');

then render {displayName} in the heading.

Also the test doesn't really catch this yet — it checks that "Bob" shows up but not that "Alice" never flashes first (and since it mounts fresh, clusterName starts as random_name anyway). A case that actually navigates A→B and asserts the old name never paints would cover it.

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

Labels

bug Something isn't working frontend good first issue Good for newcomers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Brief Flash of AI Tagging Page When Navigating to a Person's Images from the Search Bar

4 participants