Skip to content

[LegendList 6/9] feat: migrate report actions to chronological LegendList - #100733

Open
chrispader wants to merge 106 commits into
Expensify:mainfrom
margelo:@chrispader/feat/legend-list-report-actions
Open

chrispader wants to merge 106 commits into
Expensify:mainfrom
margelo:@chrispader/feat/legend-list-report-actions

Conversation

@chrispader

@chrispader chrispader commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor

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 6 of the nine-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.0 includes the upstream Fabric content-size fix. The Android end-clamp and React Native scroll-preservation patches remain 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.

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

  1. Reload the app.
  2. Open a chat. A single loading placeholder should give way to correctly positioned messages.
  3. Open a different chat.
  4. Return to the first chat. Its messages should settle without jumping to older rows.

Android and iOS apps, first open and return to a chat

  1. Close the app.
  2. Reopen the app.
  3. Open a chat. A single loading placeholder should give way to correctly positioned messages.
  4. Open a different chat.
  5. Return to the first chat. Its messages should settle without jumping to older rows.

All platforms, message history and incoming messages

  1. Open a chat with a long history shared with the second test account.
  2. Scroll upward until older messages load. The message you were reading should stay in place.
  3. Scroll to the newest message. Messages should remain in order without duplicates, and consecutive messages from the same sender should stay correctly grouped.
  4. Send a message from the second account. The new message should become visible automatically.
  5. Scroll upward until the button for returning to the latest messages appears.
  6. Send another message from the second account. The message you were reading should stay in place.
  7. Use the button to return to the latest messages. The new message should be visible.
  8. Open a different chat on the account being tested.
  9. Send another message to the shared chat from the second account.
  10. Open the shared chat on the account being tested. The unread marker should identify the new message.

All platforms, a link to an older message

  1. Open a long chat.
  2. Open an older message's action menu.
  3. Copy the message link.
  4. Open a different chat.
  5. Paste the link into the message box.
  6. Send the link.
  7. Open the sent link. The linked message should be visible in the correct chat.

All platforms, reopening a chat with a recent unread message

  1. Open a chat with a long history.
  2. Open the third-newest message's action menu.
  3. Choose Mark as unread.
  4. Return to the inbox.
  5. Open the chat again. The newest messages should appear without briefly showing older messages.

All platforms, edit the newest and an older message

  1. Open a chat containing an older message that you sent.
  2. Send a short message.
  3. Open that message's action menu.
  4. Choose Edit.
  5. Change the message text.
  6. Save the edit. The updated message should remain visible at the bottom.
  7. Scroll to an older message that you sent.
  8. Open its action menu.
  9. Choose Edit.
  10. Change the message text.
  11. Save the edit. The updated message should remain at its original place in the conversation.

All platforms, formatted messages and attachments after scrolling

  1. Open a chat with scrollable history.
  2. Send *bold* _italic_ ~strike~ https://example.com. The formatting should render correctly.
  3. Send an image.
  4. Send a PDF.
  5. Scroll the formatted message and attachments off-screen.
  6. Scroll back to them. The formatting should remain correct, and each preview should show the correct attachment.
  7. Open the image. The correct image should open.
  8. Close the image viewer.
  9. Open the PDF. The correct document should open.

Web, opening a chat URL after signing in

  1. Open a chat.
  2. Copy its URL from the browser address bar.
  3. Sign out of the app.
  4. Open the copied URL. The sign-in screen should appear.
  5. Sign in. The requested chat should open.

Android and iOS apps, keyboard and message-box resizing

  1. Open a chat.
  2. Tap the message box. The keyboard should open.
  3. Enter several lines of text. The newest messages should remain correctly positioned above the message box.
  4. Dismiss the keyboard. The list should keep its position.
  5. Tap the message box. The keyboard should reopen.
  6. Send the draft. The new message should be visible at the bottom.
  • Verify that no errors appear in the JS console

Offline tests

All platforms, cached chats offline and history after reconnecting

  1. Open a chat with a long history while online.
  2. Disconnect the internet.
  3. Return to the inbox.
  4. Reopen the chat. The cached messages should remain visible in the correct order.
  5. Scroll to the beginning of the available history. Existing messages should remain visible.
  6. Reconnect the internet.
  7. Scroll upward. Older messages should load without duplicates or a jump in the message you were reading.

QA Steps

Run the All platforms cases and the blocks for the platform being tested, including Offline tests.

  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

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

@chrispader
chrispader force-pushed the @chrispader/feat/legend-list-report-actions branch from 1bac411 to c067b0c Compare September 9, 2026 18:27
@chrispader
chrispader force-pushed the @chrispader/feat/legend-list-report-actions branch from c067b0c to e784ebe Compare September 9, 2026 20:24
@chrispader chrispader changed the title feat: migrate report actions to chronological LegendList [LegendList 2/5] feat: migrate report actions to chronological LegendList Sep 9, 2026
@chrispader
chrispader marked this pull request as ready for review September 10, 2026 09:44
@chrispader
chrispader requested review from a team as code owners September 10, 2026 09:44
@melvin-bot
melvin-bot Bot requested review from TaduJR and garrettmknight and removed request for a team and TaduJR September 10, 2026 09:44
@melvin-bot

melvin-bot Bot commented Sep 10, 2026

Copy link
Copy Markdown

@TaduJR Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@melvin-bot
melvin-bot Bot requested review from a team and removed request for a team September 10, 2026 09:44
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-10T10:07:15.590070Z f616302 Draft marked ready
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@codecov

codecov Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

Comment thread src/pages/inbox/report/ReportActionsList.tsx Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 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".

Comment thread src/pages/inbox/report/ReportActionsList.tsx Outdated
Comment thread src/pages/inbox/report/ReportActionCompose/useEditMessage.ts Outdated
@chrispader chrispader changed the title [LegendList 2/5] feat: migrate report actions to chronological LegendList [HOLD on #100732][LegendList 2/5] feat: migrate report actions to chronological LegendList Sep 10, 2026
@chrispader
chrispader force-pushed the @chrispader/feat/legend-list-report-actions branch from f616302 to 2a3323a Compare September 10, 2026 11:11
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ This PR is possibly changing native code and/or updating libraries, it may cause problems with HybridApp. Please check if any patch updates are required in the HybridApp repo and run an AdHoc build to verify that HybridApp will not break. Ask Contributor Plus for help if you are not sure how to handle this. ⚠️

@chrispader chrispader changed the title [HOLD on #100732][LegendList 2/5] feat: migrate report actions to chronological LegendList [HOLD on #100732] [LegendList 2/5] feat: migrate report actions to chronological LegendList Sep 10, 2026
@dmkt9

dmkt9 commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

Yep. This PR has quite a broad impact. I asked Codex to generate a testing checklist, and it came up with a lot of test cases.

## Manual Testing Checklist

**Priority levels:**
- **P0:** Critical flows that must be tested in this PR.
- **P1:** Important regression tests.
- **P2:** Additional checks.

### A. Initial Loading, Hydration, and Navigation

| ID | Test Steps | Expected Result | Priority |
|---|---|---|---|
| A01 | Open a DM for the first time with no cache. | The skeleton disappears once the list is ready, and the latest messages are displayed. | P0 |
| A02 | Open a cached DM and refresh the page. | The list does not jump to older messages before scrolling to the bottom. | P0 |
| A03 | Throttle the network, open a cached DM, and delay `OpenReport`. | No blank spaces or incorrectly ordered messages appear during hydration. | P0 |
| A04 | Open a chat containing only 1–2 messages. | Content is positioned correctly, with no unexpected scrollable area. | P0 |
| A05 | Open a chat containing hundreds of messages with varying heights. | No layout jumps occur while row dimensions are being measured. | P0 |
| A06 | Switch repeatedly between DM A → DM B → DM A. | Scroll positions and messages do not get mixed up between reports. | P0 |
| A07 | Open a chat, open the RHP, then close it. | The chat viewport is not unexpectedly reset. | P1 |
| A08 | Open a report in a narrow layout, then switch to a wide layout. | The initial scroll position remains correct after the layout changes. | P1 |
| A09 | Open a chat from a search result or notification. | Navigation opens the correct report at the requested position. | P1 |
| A10 | Open a chat when `OpenReport` fails or is interrupted. | The skeleton does not remain indefinitely, and available content remains usable. | P1 |

Pay particular attention to A02–A05 on Android, as the two LegendList patches added in this PR are intended to address viewport jumps caused by changes in content dimensions.

### B. Deep Links to Messages — `useLinkedReportActionScroll`

This section directly relates to the file reviewed.

| ID | Test Steps | Expected Result | Priority |
|---|---|---|---|
| B01 | Copy a link to a message in the middle of a chat and open it. | The message is positioned correctly and highlighted. | P0 |
| B02 | Open a link to the latest message. | The entire message is visible and is not obscured by the footer or composer. | P0 |
| B03 | Open a link to the first or a very old message. | Older messages are loaded if necessary, and the correct message is located. | P0 |
| B04 | Open a link to a message containing a slowly loading PDF or image. | The message remains at the intended position after the preview changes height. | P0 |
| B05 | Open a link, then manually scroll using the mouse or touch. | Automatic positioning stops and does not pull the user back to the linked message. | P0 |
| B06 | On web, scroll using the mouse wheel, trackpad, ArrowUp/Down, PageUp/Down, Home/End, or Space. | The linked-message positioning mechanism does not override user scrolling. | P1 |
| B07 | Open a link → open an attachment or thread → return. | The user's current viewport is preserved instead of jumping back to the previously linked message. | P0 |
| B08 | Send a new message while viewing a linked message. | The list switches to live-tail behavior at the appropriate time and stops pulling back to the linked message. | P0 |
| B09 | Open two different message links within the same report. | Each link correctly navigates to its respective message. | P0 |
| B10 | Reopen the same link after returning to the latest messages. | The linked message is positioned correctly again. | P1 |
| B11 | Delete the linked message or open a link to a deleted message. | The app does not crash, and navigation recovers appropriately. | P1 |
| B12 | Open a linked report in the background, then focus it. | Scrolling occurs only when the screen is actually focused. | P1 |

**Important edge case:** Combine B04 and B05. Open a deep link to a PDF, manually scroll away before the PDF finishes loading, and wait for the PDF preview to change height. The list must not pull the user back to the linked message.

### C. Bidirectional Pagination and Scroll Position Preservation

| ID | Test Steps | Expected Result | Priority |
|---|---|---|---|
| C01 | Scroll to the beginning of a long chat history. | `GetOlderActions` is called, and older messages are loaded. | P0 |
| C02 | Repeatedly scroll near the beginning while a pagination request is pending. | No duplicate requests are sent for the same cursor. | P0 |
| C03 | Load older messages containing images or PDFs that change height. | The message currently being read does not jump. | P0 |
| C04 | Open a report in the middle of its history, then scroll to the bottom. | Newer actions are loaded in the correct order. | P0 |
| C05 | Scroll from older messages back to the latest messages. | No duplicated or missing actions appear. | P0 |
| C06 | While reading older messages, have another user send a new message. | The list does not automatically jump to the bottom while the user is reading older content. | P0 |
| C07 | While at the bottom, have another user send a new message. | The list automatically follows the new message according to the existing behavior. | P0 |
| C08 | Stay near the bottom within the autoscroll threshold, then receive a new message. | The list follows the new message to the bottom instead of remaining at the previous position. | P1 |
| C09 | Simulate a `GetOlderActions` failure, restore the network, and retry loading. | Retrying is possible, and pagination is not blocked by the previous cursor. | P0 |
| C10 | Go offline while reading older messages, then reconnect. | The scroll position is preserved, and messages are not duplicated. | P1 |
| C11 | Open a list shorter than the viewport height that still has older actions. | Older actions can still be loaded through pagination. | P1 |
| C12 | Switch reports while a pagination request is still pending. | The previous report's request does not affect the new report's scroll position. | P1 |


### D. New Messages, Optimistic Updates, and Live-Tail Behavior

| ID | Test Steps | Expected Result | Priority |
|---|---|---|---|
| D01 | Send a text message in a DM while at the bottom. | The optimistic message appears, and the viewport follows it to the bottom. | P0 |
| D02 | Send multiple messages consecutively. | No incorrect scrolling or duplicated messages occur. | P0 |
| D03 | Send a message while reading older messages. | Follow/no-follow behavior remains consistent with the app's existing policy. | P0 |
| D04 | Open the same chat with two accounts and send a message from account B. | Account A receives the message in real time and displays/scrolls correctly. | P0 |
| D05 | Send a message offline, then reconnect. | The optimistic message is preserved and does not disappear during synchronization. | P0 |
| D06 | Send a message from a historical page that does not contain the newest actions. | The latest page is loaded, the list scrolls to the bottom, and pagination is pruned correctly. | P0 |
| D07 | Send a message while `OpenReport` is pending. | The optimistic message is not missed or stuck behind the loading state. | P0 |
| D08 | Edit or delete a message from another device. | The real-time update does not cause an unexpected scroll to the bottom. | P1 |
| D09 | Send a money request or an action without a report-action payload. | Live-tail behavior continues to work correctly. | P1 |
| D10 | Send an action, then quickly switch to another report. | Callbacks from the previous report do not scroll the new report to the bottom. | P1 |
| D11 | Send a message whose optimistic action fails to render or encounters an error. | The list does not unexpectedly scroll several seconds later when the action reappears. | P1 |
| D12 | Have Concierge or a custom agent stream a long response. | The draft grows smoothly without repeatedly jumping or resetting the viewport. | P0 |


### E. Unread Markers, Latest Messages, and Action Badges

| ID | Test Steps | Expected Result | Priority |
|---|---|---|---|
| E01 | Mark a message in the middle of the chat history as unread, close the chat, and reopen it. | The chat opens at the unread marker instead of incorrectly jumping to the beginning or bottom. | P0 |
| E02 | Scroll past the unread marker. | The read state updates correctly, and the marker/counter disappears at the appropriate time. | P0 |
| E03 | Scroll far up in a fully read chat. | The Latest messages pill appears after crossing the threshold. | P0 |
| E04 | Click Latest messages. | The list scrolls to the newest action and updates the read status. | P0 |
| E05 | Open a chat containing unread actions when the latest page has not been loaded. | Clicking the pill loads the newest actions and scrolls to the bottom. | P0 |
| E06 | Expand the composer to fullscreen, scroll or click the pill, then collapse it. | The pill is displayed correctly based on the current scroll position. | P1 |
| E07 | Create an action badge through an actionable preview, then click the badge. | The list scrolls to the correct actionable item. | P0 |
| E08 | Complete the first actionable item while another remains. | The badge can follow the next actionable item without jumping backward. | P1 |
| E09 | Load older actions through pagination while an action badge is present. | The list does not automatically scroll to the badge simply because its index changes. | P1 |
| E10 | Position the badge target above the viewport. | The badge is displayed correctly and is not hidden due to reversed indices. | P1 |

### F. Message Actions and Cell Recycling

Prepare a long chat containing different message types, including text, attachments, emoji reactions, expense previews, links, and payment actions.

| ID | Test Steps | Expected Result | Priority |
|---|---|---|---|
| F01 | Open the context menu using right-click or long-press. | The entire menu is visible and is not clipped by the row. | P0 |
| F02 | Open and close the menu, scroll far away, then return. | The menu does not reopen automatically or appear on another message. | P0 |
| F03 | Open the emoji picker and add/remove a reaction. | The picker and reaction are associated with the correct message. | P0 |
| F04 | Open the reaction list, then scroll quickly. | The reaction list state does not become associated with another recycled cell. | P1 |
| F05 | Open the payment method popover for an actionable item. | The popover appears correctly and does not leak into another row. | P1 |
| F06 | Edit an older message outside the viewport. | The list scrolls to the correct message for editing. | P0 |
| F07 | Edit the latest message and save the changes. | The editor closes, and the message remains fully visible at the correct scroll position. | P0 |
| F08 | Edit an older message, save, and continue reading. | The viewport does not unexpectedly jump to the bottom. | P1 |
| F09 | Delete or hide a message, then scroll. | No stale content appears in recycled cells, and no persistent blank spaces remain. | P1 |
| F10 | Scroll rapidly through hundreds of short and long messages. | No incorrect content, stale menu state, or missing messages appear. | P0 |
| F11 | Switch between messages containing previews or attachments. | Each row displays the correct preview with the correct dimensions. | P0 |
| F12 | Open a context menu near the top or bottom edge of the viewport. | The menu is not clipped and remains fully interactive. | P1 |

### G. Images, PDFs, and Dynamic Layout Changes

| ID | Test Steps | Expected Result | Priority |
|---|---|---|---|
| G01 | Send landscape and portrait images in a chat. | Image aspect ratios and heights are correct. | P0 |
| G02 | Open a chat containing images that have not finished loading. | No unexpected viewport jumps occur when image dimensions are measured. | P0 |
| G03 | Upload or open a PDF receipt in an expense/chat. | Loading, preview, and hover zoom work correctly. | P0 |
| G04 | Open a very long or multi-page PDF. | The preview does not become blank or crash, and the viewport remains correctly positioned. | P1 |
| G05 | Simulate a PDF loading failure, then open another PDF. | The error state does not leak into the new PDF. | P0 |
| G06 | Open multiple attachments consecutively while the list recycles cells. | Images or PDFs from previous messages do not appear in other messages. | P0 |
| G07 | Replace an expense receipt, then return to the chat. | The thumbnail updates to the new URL without retaining stale dimensions. | P1 |
| G08 | Change the height of a PDF preview or image above a linked message. | The linked target maintains its position unless the user has manually scrolled away. | P0 |

### H. Expense Reports, Transaction Threads, and Invoices

This section should not be skipped simply because most changes are in `ReportActionsList`.

| ID | Test Steps | Expected Result | Priority |
|---|---|---|---|
| H01 | Open an expense report containing only one expense. | Content starts at the correct position without scrolling incorrectly to the bottom. | P0 |
| H02 | Open an expense report containing multiple expenses. | The unified list displays all transactions and report actions. | P0 |
| H03 | Open a transaction thread and read its comments. | Scrolling and divider lines are correct. | P0 |
| H04 | Click X Replies from an expense/report preview. | The thread opens at the latest reply instead of the beginning of the report. | P0 |
| H05 | Edit the latest comment in a multi-expense report. | The list waits for the FlashList commit before scrolling so that the message is fully visible. | P0 |
| H06 | Edit an older comment in a multi-expense report. | No incorrect viewport jumps occur. | P1 |
| H07 | Reject, hold, or approve an expense from a report. | New actions and actionable previews update correctly. | P1 |
| H08 | Create a new money request, then navigate to its report. | The content does not jump when the server replaces the optimistic CREATED action. | P1 |
| H09 | Open an invoice report and a task thread. | Initial top alignment, dividers, and scrolling are correct. | P1 |
| H10 | Send another comment while a Concierge thinking indicator is displayed in an expense report. | The footer remains correctly positioned, and scrolling does not obscure the indicator. | P1 |

### I. Concierge and Special Report Types

| ID | Test Steps | Expected Result | Priority |
|---|---|---|---|
| I01 | Open Concierge from the main DM when older history is hidden. | Only actions belonging to the current session are displayed. | P0 |
| I02 | Click Show previous messages / Show history. | History appears correctly, and the scroll position is preserved if the user was at the bottom. | P0 |
| I03 | Click Show history, then scroll up before the request completes. | The list does not pull the user back to the bottom after loading finishes. | P0 |
| I04 | Open Concierge in a side panel. | The welcome state and chat content are displayed correctly. | P1 |
| I05 | Have Concierge stream a response and finish streaming. | The draft transitions into a persisted message without duplication. | P0 |
| I06 | Finish a Concierge response and display the feedback prompt. | The prompt appears next to the correct response. | P1 |
| I07 | Open the feedback prompt and submit thumbs down. | No incorrect prompt appears in the feedback thread. | P1 |
| I08 | Open a report containing a merchant rule suggestion banner. | The banner does not scroll incorrectly or obscure content. | P1 |
| I09 | Test web notification permissions with `default`, `granted`, and `denied`. | The banner is displayed correctly without causing an initial layout jump. | P1 |
| I10 | Open a resolved or reopened support ticket. | Survey actions are filtered and displayed correctly. | P2 |

### J. Composer, Keyboard, and Shared Lists

| ID | Test Steps | Expected Result | Priority |
|---|---|---|---|
| J01 | Focus the composer and open the keyboard on iOS/Android. | The latest message remains visible and is not obscured by the keyboard. | P0 |
| J02 | Scroll the chat while the keyboard is open. | Keyboard dismissal and scrolling work correctly. | P0 |
| J03 | Open and close a modal/RHP, then focus the composer. | The current input receives focus without using a stale ref. | P1 |
| J04 | Start editing a message and switch focus between the composer and editor. | Focus does not move to the wrong input, and the caret does not jump. | P1 |
| J05 | Expand the composer to full size, then collapse it. | The chat list and Latest messages pill remain synchronized. | P1 |
| J06 | On web, scroll the chat while composer suggestions/autocomplete are open. | Scroll events do not break suggestions or focus. | P1 |
| J07 | Scroll another screen that still uses a non-inverted FlashList/FlatList. | No unexpected composer scroll events occur. | P2 |
| J08 | Scroll an inverted list that still exists on another screen. | Existing scroll event behavior continues to work correctly. | P2 |

### K. Android Native Scroll Anchoring

This section requires testing on a physical Android device or emulator. Unit tests alone are insufficient to validate Fabric/native scroll clamping behavior.

| ID | Test Steps | Expected Result | Priority |
|---|---|---|---|
| K01 | Open a long chat at the newest messages. | Older messages do not briefly flash before the list returns to the bottom. | P0 |
| K02 | Open a chat at an unread marker near the bottom. | The exact position is preserved after row dimensions are measured. | P0 |
| K03 | Scroll to the middle of a chat and load older actions. | No large scroll jumps occur. | P0 |
| K04 | While at the bottom, increase the height of the latest message or preview. | The viewport correctly follows the latest message. | P0 |
| K05 | While reading the middle of a chat, change the heights of multiple images/PDFs above the viewport. | The content being read remains stable. | P0 |
| K06 | Open a deep link to a PDF in the middle of a chat. | No double compensation occurs that causes excessive scrolling. | P0 |
| K07 | Test another FlatList/ScrollView using `maintainVisibleContentPosition`. | No regressions occur due to the globally applied React Native patch. | P1 |
| K08 | Change the viewport height by opening the keyboard, rotating the device, or resizing the window. | No incorrect scroll clamping occurs. | P1 |

Co-authored-by: Codex <noreply@openai.com>
@chrispader chrispader changed the title [LegendList 4/7] feat: migrate report actions to chronological LegendList [WIP] [HOLD on #104977, #104978] [LegendList 6/9] feat: migrate report actions to chronological LegendList Oct 26, 2026
chrispader and others added 3 commits October 26, 2026 09:10
Merge the independently reviewable composer and row-state preparation branches without rewriting the existing migration history. The migration-only comparison is 35 files, with the legacy FlashList type bridge as the only shared path. Drop the unrelated telemetry cleanup and restore the existing iOS callback structure.

Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Infer the action subscriber callback type without exporting its event source.

Co-authored-by: Codex <noreply@openai.com>
chrispader and others added 3 commits October 26, 2026 12:15
Remove the upstreamed Fabric content-size patch and port the Android end-clamp patch to the new release.

Co-authored-by: Codex <noreply@openai.com>
Preserve chronological scrolling and main's live-range badge tracking.

Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>

Copy link
Copy Markdown
Contributor Author

Chris's AI pit crew checking in. 🤖

@roryabraham The MVCP anchor-selection fix, including LegendList's zero-sized anchor handling, has merged upstream in react-native#57959 as 9bc1a28. It is expected in React Native 0.89.0, unless backported earlier. The RN patch details.md now records this.

Co-authored-by: Codex <noreply@openai.com>
@chrispader chrispader changed the title [WIP] [HOLD on #104977, #104978] [LegendList 6/9] feat: migrate report actions to chronological LegendList [WIP] [HOLD on #104978] [LegendList 6/9] feat: migrate report actions to chronological LegendList Oct 28, 2026
Preserve collapsed system messages, focus handoff, and scroll targets with chronological row indexes.

Co-authored-by: Codex <noreply@openai.com>
@chrispader chrispader changed the title [WIP] [HOLD on #104978] [LegendList 6/9] feat: migrate report actions to chronological LegendList [WIP] [LegendList 6/9] feat: migrate report actions to chronological LegendList Oct 29, 2026
chrispader and others added 3 commits October 29, 2026 09:53
Resolve preparation-layer conflicts and keep scrolling through the chronological list API.

Co-authored-by: Codex <noreply@openai.com>
Name the requested scroll destination and explain the FlashList layout dependency.

Co-authored-by: Codex <noreply@openai.com>
Keep the collapsed-target regression test aligned with the chronological report list.

Co-authored-by: Codex <noreply@openai.com>
@chrispader
chrispader requested a review from dmkt9 October 29, 2026 11:22
chrispader and others added 2 commits October 29, 2026 12:38
Older browsers lack Array.prototype.toReversed and the app has no polyfill
for it, so copy and reverse the newest-first actions instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Document why each effect in useLinkedReportActionScroll is needed and
correct the LegendList version in the React Native MVCP patch notes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chrispader chrispader changed the title [WIP] [LegendList 6/9] feat: migrate report actions to chronological LegendList [LegendList 6/9] feat: migrate report actions to chronological LegendList Oct 29, 2026
chrispader and others added 4 commits October 29, 2026 19:21
Preserve Concierge welcome and history controls with the chronological list.

Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Carry the Concierge feedback fix into the extracted list component and preserve pagination IDs for history restoration.

Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
@dmkt9

dmkt9 commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

@chrispader Could you confirm whether these bugs have been fixed? I just noticed that Bug 1 is still reproducible

Keep cached messages covered until LegendList finishes positioning them. Offline reports still skip waiting for the queued OpenReport request.

Co-authored-by: Codex <noreply@openai.com>

This branch has not been deployed

No deployments
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.

4 participants