Repository navigation
[LegendList 4/7] feat: migrate report actions to chronological LegendList #100733
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
chrispader
wants to merge
69
commits into
Expensify:main
Choose a base branch
from
margelo:@chrispader/feat/legend-list-report-actions
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+3,014
−785
Open
Changes from all commits
Commits
Show all changes
69 commits
Select commit
Hold shift + click to select a range
7d4be68
feat: migrate report actions to chronological LegendList
chrispader 2a3323a
chore: merge main into LegendList report actions
chrispader d815c1a
chore: merge LegendList foundation updates
chrispader ba852b2
fix: address LegendList report action review
chrispader 15312bb
chore: merge main into LegendList report actions
chrispader 0a811f9
chore: merge main into LegendList report actions
chrispader 750e92d
chore: merge main into LegendList report actions
chrispader 332a7b3
fix: stabilize LegendList initial loading
chrispader e385011
chore: merge main into LegendList report actions
chrispader 9117baa
chore: merge main into LegendList report actions
chrispader e5c4ec3
chore: merge main into LegendList report actions
chrispader 3a782f9
test: model content growth in pagination test
chrispader fa5194f
fix: stabilize report actions loading behavior
chrispader 52df1e9
refactor: remove compiled report memoization
chrispader c80ea5a
fix: allow report action menus to overflow list rows
chrispader 5cb81be
chore: merge main into LegendList report actions
chrispader c873dca
chore: merge main into LegendList report actions
chrispader f91ebe0
chore: merge main into LegendList report actions
chrispader 255f4d3
chore: merge main into LegendList report actions
chrispader 62a7c0d
fix: supply report name in modified expense tests
chrispader 0964d53
Merge branch 'main' into @chrispader/feat/legend-list-report-actions
chrispader 09057d5
fix: address report actions list review feedback
chrispader 61dc6ad
chore: merge main into report actions branch
chrispader ffa18ae
docs: explain report action recycling state
chrispader 53f3b9f
Merge branch 'main' into @chrispader/feat/legend-list-report-actions
chrispader f12b575
fix: remove duplicate report list spacing
chrispader 34b345d
fix: synchronize sent comment scrolling
chrispader da3b443
fix: preserve hydrated report list state
chrispader 55cb72a
fix: refresh recycled unread marker rows
chrispader a5670fa
chore: merge main into report actions branch
chrispader 8188df1
fix: keep report actions live across incomplete loads
chrispader 9bd382a
fix: preserve local action scrolling without realtime echo jumps
chrispader f0b046a
fix: key recycled media state by action and image
chrispader a8fc4fa
fix: keep highlighted final action in view
chrispader 6e7f045
test: verify unread and read thresholds from saved offset
chrispader ca78b55
refactor: share web report scroll-to-end implementation
chrispader 59a5397
docs: note Android visible-position patch scope
chrispader d2f2cc8
fix: scroll after edited action layout settles
chrispader 08e1f69
test: type captured list load callback
chrispader 5c7ffc8
chore: merge main into LegendList report actions
chrispader 84b0f19
fix: preserve Concierge tail when revealing history
chrispader a1e0d25
test: remove nonvirtualized report list benchmark
chrispader e37edb4
test: mock edited-action draft clearing
chrispader b43b59e
Merge branch 'main' into @chrispader/feat/legend-list-report-actions
chrispader e904b99
test: wait for edited action scroll frame
chrispader 40f7719
chore: update LegendList to 3.4.0
chrispader a68ee3c
fix: cover warm report actions during initial layout
chrispader e117ffc
fix: render Concierge notification banner before chat scrolls to end
chrispader 4adbdaa
fix: keep latest chat action visible after list measurement
chrispader c92613d
fix: preserve Concierge scroll position when showing history
chrispader 7096033
fix: preserve unread message alignment at the latest action
chrispader c0751dd
fix: cancel report end corrections when the reader scrolls
chrispader 0cb9d7b
fix: keep report scroll events flowing on web
chrispader 0f53485
fix: keep chat skeleton visible until initial scrolling settles
chrispader b611f38
fix: resume end following after scrolling past a linked message
chrispader 779eaac
fix: preserve chat content when clearing linked messages
chrispader 5dab08a
chore: merge main into LegendList report actions
chrispader 519cb5b
fix: prevent Android LegendList viewport flashes
chrispader 8229bce
fix: keep linked messages visible after navigation and layout changes
chrispader ed4e31a
fix: emit web scroll events for chronological lists
chrispader d25f5b6
fix: preserve linked-message position through late layout
chrispader e83aec2
chore: merge main into LegendList report actions
chrispader b6c6df2
fix: preserve LegendList web scroll completion
chrispader 40a2338
fix: resolve the current composer after focus delays
chrispader 49ab56d
fix: avoid duplicate Android linked-message adjustments
chrispader e1bbfda
chore: merge main and resolve report scrolling conflicts
chrispader e8974de
chore: merge main into LegendList report actions
chrispader 1de8343
fix: keyboard focus
chrispader bd9ce7c
fix: restore Android composer blur on keyboard dismissal
chrispader File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
patches/@legendapp/list/@legendapp+list+3.6.0+001+synchronize-fabric-content-size.patch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| diff --git a/node_modules/@legendapp/list/react-native.js b/node_modules/@legendapp/list/react-native.js | ||
| index 0b730ec..9e14f1d 100644 | ||
| --- a/node_modules/@legendapp/list/react-native.js | ||
| +++ b/node_modules/@legendapp/list/react-native.js | ||
| @@ -6097,4 +6097,12 @@ function useFreshDataTransitionVisibility(readyToRender, transitionEpoch) { | ||
| // src/components/Containers.native.tsx | ||
| +// Fabric positions and the MVCP anchor update through React. Commit the content | ||
| +// size with them so an imperative Animated update cannot clamp the old viewport. | ||
| +var useContainersSize = IsNewArchitecture ? function useContainersSizeState() { | ||
| + const [size = 0] = useArr$(["totalSize"]); | ||
| + return size; | ||
| +} : function useContainersSizeAnimated() { | ||
| + return useValue$("totalSize"); | ||
| +}; | ||
| var ContainersLayer = typedMemo(function ContainersLayer2({ | ||
| children, | ||
| freshDataTransitionEpoch, | ||
| @@ -6102,7 +6110,7 @@ var ContainersLayer = typedMemo(function ContainersLayer2({ | ||
| }) { | ||
| const ctx = useStateContext(); | ||
| const columnWrapperStyle = ctx.columnWrapperStyle; | ||
| - const animSize = useValue$("totalSize"); | ||
| + const animSize = useContainersSize(); | ||
| const [readyToRender, numColumns, otherAxisSize = 0] = useArr$(["readyToRender", "numColumns", "otherAxisSize"]); | ||
| const isVisible = useFreshDataTransitionVisibility(!!readyToRender, freshDataTransitionEpoch); | ||
| const style = horizontal ? { | ||
| diff --git a/node_modules/@legendapp/list/react-native.mjs b/node_modules/@legendapp/list/react-native.mjs | ||
| index a3d8d9a..1f2bcf9 100644 | ||
| --- a/node_modules/@legendapp/list/react-native.mjs | ||
| +++ b/node_modules/@legendapp/list/react-native.mjs | ||
| @@ -6076,4 +6076,12 @@ function useFreshDataTransitionVisibility(readyToRender, transitionEpoch) { | ||
| // src/components/Containers.native.tsx | ||
| +// Fabric positions and the MVCP anchor update through React. Commit the content | ||
| +// size with them so an imperative Animated update cannot clamp the old viewport. | ||
| +var useContainersSize = IsNewArchitecture ? function useContainersSizeState() { | ||
| + const [size = 0] = useArr$(["totalSize"]); | ||
| + return size; | ||
| +} : function useContainersSizeAnimated() { | ||
| + return useValue$("totalSize"); | ||
| +}; | ||
| var ContainersLayer = typedMemo(function ContainersLayer2({ | ||
| children, | ||
| freshDataTransitionEpoch, | ||
| @@ -6081,7 +6089,7 @@ var ContainersLayer = typedMemo(function ContainersLayer2({ | ||
| }) { | ||
| const ctx = useStateContext(); | ||
| const columnWrapperStyle = ctx.columnWrapperStyle; | ||
| - const animSize = useValue$("totalSize"); | ||
| + const animSize = useContainersSize(); | ||
| const [readyToRender, numColumns, otherAxisSize = 0] = useArr$(["readyToRender", "numColumns", "otherAxisSize"]); | ||
| const isVisible = useFreshDataTransitionVisibility(!!readyToRender, freshDataTransitionEpoch); | ||
| const style = horizontal ? { |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| # `@legendapp/list` patches | ||
|
|
||
| ### [@legendapp+list+3.6.0+001+synchronize-fabric-content-size.patch](@legendapp+list+3.6.0+001+synchronize-fabric-content-size.patch) | ||
|
|
||
| - Reason: On Android, the entire report-action list can briefly jump to older rows while item measurements settle. LegendList updates its content size through an imperative `Animated.Value`, while Fabric row positions and the scroll-preservation anchor update through React. An early content-size update can clamp the old viewport before the matching positions mount. Read the content size through the same React store on the New Architecture so these layout changes commit together. | ||
| - Scope: Both native module formats use the synchronized size on the New Architecture. The legacy architecture retains its animated size updates, and the web entries are unchanged. Native scroll preservation and the initial loading skeleton remain enabled. | ||
| - Upstream PR/issue: Pending submission after local validation. This patch is being developed locally as part of the LegendList migration. | ||
| - E/App issue: https://github.com/Expensify/App/pull/100733#issuecomment-5886543887 | ||
| - PR introducing patch: https://github.com/Expensify/App/pull/100733 | ||
| - Reproduction: Open an expense chat containing a report preview and messages with different heights. Wait for the actual rows to replace the skeleton. The highlighted preview should stay visible while measurements settle, without a frame displaying older rows. Also scroll through older history and return to the latest messages to check scroll preservation. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 0 additions & 30 deletions
30
src/components/FlashList/InvertedFlashList/CellRendererComponent.tsx
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import {isRecord} from '@libs/ObjectUtils'; | ||
|
|
||
| import type {LegendListRef} from '@legendapp/list/react-native'; | ||
|
|
||
| /** | ||
| * LegendList uses paint containment for web item containers, which clips descendants that render | ||
| * outside their row. Remove only paint containment so inline menus can overflow while layout and | ||
| * style containment keep the rest of the row isolated. | ||
| */ | ||
| function allowLegendListItemOverflow(list: LegendListRef | null, index: number): void { | ||
| const itemContainer: unknown = list?.getState().elementAtIndex(index); | ||
| if (!isRecord(itemContainer) || !isRecord(itemContainer.style) || typeof itemContainer.style.contain !== 'string') { | ||
| return; | ||
| } | ||
|
|
||
| itemContainer.style.contain = 'layout style'; | ||
| } | ||
|
|
||
| export default allowLegendListItemOverflow; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MelvinBot Is the change to this patch reflected in the upstream react/react-native#46247?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No — none of this patch is upstream, and the new change in this PR isn't even part of #46247's diff.
Two separate gaps:
maintainVisibleContentPosition fixes on Android was never merged.
react-native-botclosed it as stale on 2026-04-05 after 180 days with no activity (merged: false, no merge commit). RNmaintoday still has the original naivecomputeTargetView()— first-match loop, called fromwillMountItems. So points 1 and 2 indetails.mdare still patch-only.The change added here has no upstream counterpart at all. Allow
Searchcommand to run in support mode #46247 touches only view selection (z-index-aware) and when it runs (on scroll). It never special-cases a zero-sized anchor. Both new bits here are new behavior:react-native+0.86.0+002+fixMVCPAndroid.patch:41-47— pick a zero-sized positioned first child as the anchorreact-native+0.86.0+002+fixMVCPAndroid.patch:80-81— accept a zero-area frame whenleft/topis non-zeroSuggestion: the
Upstream PR/issue:line indetails.mdnow points at a closed PR, which reads as "this is on its way upstream" when it isn't. Worth marking it closed/not-landed, and either filing a new upstream PR for the LegendList anchor case or setting it to 🛑 like theE/App issue:line.How I checked
gh api repos/facebook/react-native/pulls/46247→state: closed,merged: false,merged_at: null,closed_at: 2026-04-05T05:31:12Zreact-native-bot: This PR was closed because it has been stalled for 7 days with no activity.Searchcommand to run in support mode #46247's diff ofMaintainVisibleScrollPositionHelper.javaagainst this PR's patch hunks — nowidth == 0 && height == 0anchor branch, andwillMountItemsInternal()stores the hit rect unconditionally with no zero-area guard.MaintainVisibleScrollPositionHelper.ktfrom RNmainto confirm the upstream refactor (onScroll()/didMountItemsInternal()/firstVisibleViewPosition) is absent.view run
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@chrispader at some point we need to get this merged upstream. I talked to Janic about this about a month ago, but Expensify didn't want to deal with the overhead of onboarding a new agency just for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll look into it!