Repository navigation
[LegendList 9/12] feat: migrate report actions to chronological LegendList - #100733
chrispader wants to merge 126 commits into
Conversation
1bac411 to
c067b0c
Compare
c067b0c to
e784ebe
Compare
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f616302cdb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
f616302 to
2a3323a
Compare
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dmkt9 @roryabraham i will confirm all previous bugs again today, now that the PR is much slimmer. |
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Treat a cleared message link as absent when choosing the initial unread target. Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
✅ Fixed! Screen.Recording.2026-11-03.at.18.50.16.mov
✅ Fixed! @dmkt9 please confirm on a physical Android device. Screen_Recording_20261103_194822_Expensify.Debug.legendlist.mp4
✅ Fixed! Screen.Recording.2026-11-04.at.09.45.11.mov
✅ Fixed! Scrolling/pagination keeps the list anchored. The jumpiness on initial report opening is handled separately. Screen.Recording.2026-11-04.at.20.24.45.mov
✅ Fixed! Screen_Recording_20261104_215846_Expensify.Debug.legendlist.mp4
This isn't caused by LegendList. It also happens with main's list code on the same build. The keyboard library resets its keyboard state from the JS thread when a screen mounts. When a chat opens with a draft, that reset reaches the UI thread after the keyboard has already opened, so the composer never gets its padding and stays hidden. I reused the change from the still-open upstream PR kirillzyusko/react-native-keyboard-controller#1685 as a patch. The composer now ends up above the keyboard for both the draft case and focusing while the chat loads. Since it's a separate bug that main has too, i'll create a separate PR for this fix. bug6-draft-after-fix.mp4bug6-focus-and-send-while-loading-after-fix.mp4
✅ Fixed! nab-new-messages-badge.mp4
✅ Fixed! bug7-concierge-no-badge-flash.mp4 |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…esize Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The keyboard avoiding view set its starting keyboard state from the JS thread on mount. That write reached the UI thread after the keyboard had already opened and reset the view to a closed keyboard, so it never added bottom padding. Apply the upstream fix from kirillzyusko/react-native-keyboard-controller#1685, which sets the starting state on the UI thread and skips it once a keyboard event arrived. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A chat opened at its unread marker can settle within 250 pixels of the end. The next scroll event then made the list follow the end, so the rows measured below the marker scrolled the New divider out of view a moment after opening. Lists that start at an unread or linked message now follow the end only once they reach it or the user drags near it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After a cache clear, OpenApp stores a few scattered actions for a chat, such as thread parents and its last message, but no page. The chat opened at those old messages and jumped once OpenReport loaded the newest page. While OpenReport is pending for a chat without a loaded page, show the loading skeleton instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Chris's AI understudy reporting in. 🤖 On web, the messages on screen could still move in three cases: when older messages loaded at the top, when the oldest loaded message lost its sender header because older messages from the same sender loaded, and when older messages arrived right after a fast scrollbar drag. All three come from how LegendList keeps visible items in place on web. This PR fixes them with patches to LegendList, and we opened the same fixes upstream:
We can remove the patches once a LegendList release includes these fixes. |
|
@dmkt9 i've addressed all your issues, merged main and resolved conflicts. I'm checking right now whether there is anything else that needs fine-tuning, but feel free to already give this another review! 🙌🏼 |
…nd-list-report-actions Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The report-actions list currently uses inversion, so rendering order and scroll coordinates differ from chronological message order. This PR moves the main chat list to LegendList and updates pagination, unread tracking, message editing, recycling, and initial loading behavior to work with chronological data.
@roryabraham @dmkt9
@Expensify/design Please review the initial chat-loading skeleton shown below.
Explanation of Change
This is layer 9 of the twelve-part LegendList rollout. It moves the main report-actions list to chronological LegendList data and updates pagination, unread tracking, linked-message positioning, end following, and the initial loading cover. LegendList 3.7.1 includes the upstream Android end-clamp and follow-threshold fixes. This PR also patches three web fixes into LegendList that keep the messages on screen in place. They're open upstream as LegendApp/legend-list#596, #597, and #598. The React Native scroll-preservation patch remains with this migration.
Composer focus and notification preparation landed in layer 4, #104977. Shared list refs, row state, and money-report edit scrolling landed in layer 5, #104978. Layers 6 to 8 split out the FlashList module split, #105473, the LegendList 3.7.0 upgrade, #105474, and the unread-tracking tests, #105475.
The first time a chat opens online after the app starts, the skeleton stays over its cached messages until OpenReport finishes and the list is positioned, so estimated row positions never show. The warm open-report span ends when the positioned messages replace that cover.
Fixed Issues
$ #98994
This PR is part of the migration split from #98270. No approved proposal is linked.
Tests
All platforms means desktop web, mobile web, Android, and iOS.
Use a second test account in the same chat for the incoming-message cases.
Web, first open and return to a chat
Android and iOS apps, first open and return to a chat
All platforms, message history and incoming messages
All platforms, a link to an older message
All platforms, reopening a chat with a recent unread message
All platforms, edit the newest and an older message
All platforms, formatted messages and attachments after scrolling
*bold* _italic_ ~strike~ https://example.com. The formatting should render correctly.Web, opening a chat URL after signing in
Android and iOS apps, keyboard and message-box resizing
Offline tests
All platforms, cached chats offline and history after reconnecting
QA Steps
Run the All platforms cases and the blocks for the platform being tested, including Offline tests.
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Cold chat open and initial loading
FlashList is on the left and LegendList is on the right. FlashList leaves the report body blank during loading; LegendList shows the report-action skeleton before messages appear.
ColdOpenLoading.mp4
Cold expense-chat open
FlashList is on the left and LegendList is on the right. Both progress from the loading skeleton to an expense preview and its surrounding report actions.
Android screen recording TBD.
Warm expense-chat open
FlashList is on the left and LegendList is on the right. Both open the cached expense preview and report actions from the inbox.
Android screen recording TBD.
Warm chat open
Side-by-side opens of the same cached chat, ending on the same report-action history. The implementations are not labeled in this recording.
Android screen recording TBD.
Android: mWeb Chrome
iOS: Native
Cold chat open and initial loading
FlashList is on the left and LegendList is on the right. FlashList leaves the report body blank during loading; LegendList shows the report-action skeleton before messages appear.
LegendListLoadingSkeletonVsFlashList.mp4
Cold expense-chat open
FlashList is on the left and LegendList is on the right. Both progress from the loading skeleton to an expense preview and its surrounding report actions.
ExpenseChatCold.mp4
Warm expense-chat open
FlashList is on the left and LegendList is on the right. Both open the cached expense preview and report actions from the inbox.
ExpenseChatWarm.mp4
Warm chat open
Side-by-side opens of the same cached chat, ending on the same report-action history. The implementations are not labeled in this recording.
LegendListWarmReportOpen.mp4
iOS: mWeb Safari
MacOS: Chrome / Safari