@@ -45,14 +45,7 @@ import {
4545import { TouchableOpacity } from "react-native-gesture-handler" ;
4646import ImageViewing from "react-native-image-viewing" ;
4747import { useSafeAreaInsets } from "react-native-safe-area-context" ;
48- import Animated , {
49- FadeIn ,
50- FadeInUp ,
51- useSharedValue ,
52- withTiming ,
53- type LayoutAnimationsValues ,
54- type SharedValue ,
55- } from "react-native-reanimated" ;
48+ import Animated , { FadeIn , FadeInUp , type SharedValue } from "react-native-reanimated" ;
5649import { useThemeColor } from "../../lib/useThemeColor" ;
5750import { useFontFamily } from "../../lib/useFontFamily" ;
5851import { copyTextWithHaptic } from "../../lib/copyTextWithHaptic" ;
@@ -117,14 +110,6 @@ function formatMessageTime(input: string): string {
117110 return MESSAGE_TIME_FORMATTER . format ( timestamp ) ;
118111}
119112
120- // Rows shift when content above them grows (streaming text, work-log folds);
121- // animating the container position turns those jumps into slides. Applied
122- // conditionally — see the gated transition in ThreadFeed: while browsing
123- // history the animation must NOT run, or every estimate→actual size
124- // correction plays as a visible slide against the instant scroll-offset
125- // compensation from maintainVisibleContentPosition.
126- const FEED_ITEM_LAYOUT_DURATION_MS = 180 ;
127-
128113// Pre-measurement heights for getFixedItemSize, mirroring renderFeedEntry's
129114// classNames. The fold row's min-h-11 (44px) stays taller than its single
130115// text-sm line at every supported base font size (26px at the 22pt maximum),
@@ -1457,22 +1442,13 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) {
14571442 } ,
14581443 [ props . onHeaderMaterialVisibilityChange ] ,
14591444 ) ;
1460- // True while the viewport sits within ~one screen of the list end — the
1461- // only region where layout shifts should animate. Starts true because the
1462- // list opens pinned to the end.
1463- const nearListEnd = useSharedValue ( true ) ;
1464-
14651445 const handleScroll = useCallback (
14661446 ( event : NativeSyntheticEvent < NativeScrollEvent > ) => {
14671447 // anchorTopInset, not topContentInset: under automatic insets the list
14681448 // rests at contentOffset.y = -headerHeight (the inset lives only in
14691449 // UIKit's adjustedContentInset, so topContentInset is 0 here). Add the
14701450 // header height back or the material toggles a full header too late.
14711451 reportHeaderMaterialVisibility ( event . nativeEvent . contentOffset . y + anchorTopInset > 6 ) ;
1472- const { contentOffset, contentSize, layoutMeasurement } = event . nativeEvent ;
1473- nearListEnd . value =
1474- contentSize . height - layoutMeasurement . height - contentOffset . y < layoutMeasurement . height ;
1475-
14761452 // Latch bookkeeping. LegendList recomputes its inset-aware end distance
14771453 // before invoking this handler, so getState() is current. Returning to
14781454 // the end re-arms follow no matter who scrolled (the user, or our own
@@ -1488,7 +1464,7 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) {
14881464 }
14891465 }
14901466 } ,
1491- [ reportHeaderMaterialVisibility , anchorTopInset , nearListEnd , props . listRef , setEndFollow ] ,
1467+ [ reportHeaderMaterialVisibility , anchorTopInset , props . listRef , setEndFollow ] ,
14921468 ) ;
14931469 const handleScrollBeginDrag = useCallback ( ( ) => {
14941470 userScrollSessionRef . current = true ;
@@ -1508,33 +1484,6 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) {
15081484 userScrollSessionRef . current = false ;
15091485 } , [ ] ) ;
15101486
1511- // Gated variant of the 180ms feed layout slide. Instant while browsing
1512- // history: maintainVisibleContentPosition compensates the scroll offset in
1513- // the same frame a row's measured size lands, so an instant reposition is
1514- // invisible — animating it is exactly what made cold upward scrolls slide
1515- // and jump. Near the end the slide stays on: streaming growth and sends
1516- // shift rows at rest, where the animation is the thing preventing a hard
1517- // visual snap.
1518- const feedItemLayoutTransition = useMemo ( ( ) => {
1519- return ( values : LayoutAnimationsValues ) => {
1520- "worklet" ;
1521- const duration = nearListEnd . value ? FEED_ITEM_LAYOUT_DURATION_MS : 0 ;
1522- return {
1523- initialValues : {
1524- originX : values . currentOriginX ,
1525- originY : values . currentOriginY ,
1526- width : values . currentWidth ,
1527- height : values . currentHeight ,
1528- } ,
1529- animations : {
1530- originX : withTiming ( values . targetOriginX , { duration } ) ,
1531- originY : withTiming ( values . targetOriginY , { duration } ) ,
1532- width : withTiming ( values . targetWidth , { duration } ) ,
1533- height : withTiming ( values . targetHeight , { duration } ) ,
1534- } ,
1535- } ;
1536- } ;
1537- } , [ nearListEnd ] ) ;
15381487 const handleViewportLayout = useCallback ( ( event : LayoutChangeEvent ) => {
15391488 const nextWidth = Math . round ( event . nativeEvent . layout . width ) ;
15401489 const nextHeight = Math . round ( event . nativeEvent . layout . height ) ;
@@ -1886,7 +1835,6 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) {
18861835 }
18871836 : { scrollIndicatorInsets : { top : topContentInset , bottom : 0 } } ) }
18881837 { ...( anchoredEndSpace ? { anchoredEndSpace } : { } ) }
1889- itemLayoutAnimation = { feedItemLayoutTransition }
18901838 // Patched LegendList prop (patches/@legendapp__list@3.2.0.patch):
18911839 // lets its scroll math clamp programmatic scrolls to -headerInset
18921840 // instead of 0, so initialScrollAtEnd/maintainScrollAtEnd on short
0 commit comments