From 76b052a4232a30d8355f784aee4a51b833df7de2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Fri, 11 Sep 2026 19:48:47 +0000 Subject: [PATCH] fix(mobile): add recyclingKey to PR review comment avatars Surface: the mobile app (apps/mobile). # Problem On the PR review Discussion tab, comment author avatars render inside a FlashList whose rows are recycled. `apps/mobile/src/components/pr-review/discussion/pr-review-discussion-list.tsx` renders `CommentRow` for every `kind: 'comment'` item from a `FlashList` (see its `renderItem` at lines 138-152). `apps/mobile/src/components/pr-review/discussion/comment-row.tsx` lines 232-240 render the avatar as: ```tsx ``` There is no `recyclingKey`. Expo Image documents this prop as: "Changing this prop resets the image view content to blank or a placeholder before loading and rendering the final image. This is especially useful for any kinds of recycling views like FlashList to prevent showing the previous source before the new one fully loads." With `cachePolicy="memory"` the previous commenter's avatar can render for a frame when a cell is recycled onto a different comment. The repository already sets this prop on the other recycled images (`apps/mobile/src/components/agents/markdown-image.tsx:266` uses `recyclingKey={uri}` and `apps/mobile/src/components/agents/attachment-preview-strip.tsx:302` uses `recyclingKey={attachment.id}`), so the comment avatar is the outlier. # Requested behavior Set `recyclingKey={comment.author.avatarUrl}` on the avatar `Image` in `comment- --- .../pr-review/discussion/comment-row.test.tsx | 14 ++++++++++++++ .../pr-review/discussion/comment-row.tsx | 1 + 2 files changed, 15 insertions(+) diff --git a/apps/mobile/src/components/pr-review/discussion/comment-row.test.tsx b/apps/mobile/src/components/pr-review/discussion/comment-row.test.tsx index c6e6481844..324ce2e56b 100644 --- a/apps/mobile/src/components/pr-review/discussion/comment-row.test.tsx +++ b/apps/mobile/src/components/pr-review/discussion/comment-row.test.tsx @@ -302,3 +302,17 @@ describe('CommentRow overflow actions', () => { renderer.unmount(); }); }); + +describe('CommentRow avatar recycling', () => { + it('sets recyclingKey to the author avatar URL so a recycled row clears the previous image', async () => { + const avatarUrl = 'https://example.com/alice.png'; + const renderer = await render(makeComment({ author: { login: 'alice', avatarUrl } })); + + const image = renderer.root.find( + node => typeof node.type === 'string' && (node.type as string) === 'Image' + ); + expect((image.props as Record).recyclingKey).toBe(avatarUrl); + + renderer.unmount(); + }); +}); diff --git a/apps/mobile/src/components/pr-review/discussion/comment-row.tsx b/apps/mobile/src/components/pr-review/discussion/comment-row.tsx index 18fa553fc4..2a31dcf347 100644 --- a/apps/mobile/src/components/pr-review/discussion/comment-row.tsx +++ b/apps/mobile/src/components/pr-review/discussion/comment-row.tsx @@ -235,6 +235,7 @@ export function CommentRow({ className="size-6 rounded-full" transition={0} cachePolicy="memory" + recyclingKey={comment.author.avatarUrl} accessibilityIgnoresInvertColors /> ) : (