Skip to content

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

Merged
rohan-pandeyy merged 5 commits into
AOSSIE-Org:mainfrom
tanmaysachann:fix/1315-ai-tagging-page-flash
Jun 16, 2026
Merged

fix(person-images): prevent flash of previous page's images (#1315)#1321
rohan-pandeyy merged 5 commits into
AOSSIE-Org:mainfrom
tanmaysachann:fix/1315-ai-tagging-page-flash

Conversation

@tanmaysachann

@tanmaysachann tanmaysachann commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

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 a flash of previously viewed images when switching between clusters by only displaying image data for the currently selected cluster and keeping the displayed cluster title synchronized as new images load.
    • Improved image grid and media modal to reflect the correct cluster-specific name and images.
  • Tests
    • Added Jest + React Testing Library regression coverage to ensure stale cluster images never render during navigation and that the correct content appears 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

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

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: aa853f60-5c8f-47d7-b9e0-f8798419f155

📥 Commits

Reviewing files that changed from the base of the PR and between 0fe7282 and 62a2414.

📒 Files selected for processing (1)
  • frontend/src/pages/PersonImages/PersonImages.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. Parallel displayName derivation uses clusterName or query cluster_name with the same synchronization check.
Update rendering to use derived images and display name
frontend/src/pages/PersonImages/PersonImages.tsx
Page title and image grid header switch to derived displayName. Image grid map and MediaView modal both switch from Redux images to personImages for rendering. Container layout classes around edit/back controls are adjusted for styling.
Test setup: mocks, fixtures, and helpers
frontend/src/pages/__tests__/PersonImages.test.tsx
Jest mocks for Tauri and cluster image fetching. Deterministic image fixtures for "Person A" and "Person B" clusters. CommitProbe captures rendered heading text. renderPerson helper mounts PersonImages with preloaded Redux state, and DOM helpers detect which cluster images are visible.
Regression tests for stale image flash
frontend/src/pages/__tests__/PersonImages.test.tsx
Navigation test: renders on cluster "A", navigates to "B", asserts stale images absent and new images present. Direct-load test: renders directly for cluster "B" with preloaded "Person A" state, asserts no stale content, then validates correct images appear 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 summarizes the main fix: preventing flash of previous page's images during cluster navigation. It is concise, clear, and directly related to the primary change.
Linked Issues check ✅ Passed The implementation fully addresses issue #1315 by eliminating the stale image flash through loadedClusterId tracking and fallback to scoped query data, plus gating the heading display with the same pattern.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the image/heading flash issue in PersonImages.tsx and its regression test, with no unrelated modifications.

✏️ 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
Contributor 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.

@@ -109,7 +123,7 @@ export const PersonImages = () => {
</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
Contributor 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.

@tanmaysachann

Copy link
Copy Markdown
Contributor Author

@VanshajPoonia Updated, thanks. I gated the heading with the same loadedClusterId / query-data fallback used for personImages, and updated the regression test to cover Alice -> cached Bob so the old heading/images are never rendered. Please take a look when you get a chance.

@VanshajPoonia

Copy link
Copy Markdown
Contributor

LGTM 👍 Pulled the latest and went through it properly - the heading's gated with the same loadedClusterId check as the images now, so on a cached A→B jump it shows the right name from the first frame instead of the old one. Using the layout-effect probe to record the <h1> on every commit is a clean way to prove it never flashes the previous name mid-transition, not just at the end. Edit/rename still reads clusterName while synced, so that keeps working too.

Couple of optional things, nothing blocking:

  • personImages and displayName repeat the same clusterId !== undefined && loadedClusterId === clusterId check — could lift it into one isClusterSynced const to tidy up.
  • The probe checks the heading on every frame but only checks the grid after it settles. If you wanted it fully symmetric, it could also note whether the personA images are on screen per-commit so the image side is pinned frame-by-frame too - the cold-mount test already covers that base though, so your call.

Nice work, this fixes what I flagged.

@rohan-pandeyy

Copy link
Copy Markdown
Member

Thanks for your contribution @tanmaysachann,
Thanks for helping review this PR @VanshajPoonia!

@rohan-pandeyy rohan-pandeyy merged commit 08bc026 into AOSSIE-Org:main Jun 16, 2026
4 checks passed
@VanshajPoonia

Copy link
Copy Markdown
Contributor

Great experience working on this

@tanmaysachann

Copy link
Copy Markdown
Contributor Author

Thank you for the review and for merging! Great learning experience :)

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