diff --git a/apps/mobile/package.json b/apps/mobile/package.json index c12ca979bf2..8b6834c9714 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -138,5 +138,10 @@ "@react-native-menu/menu" ] } + }, + "reanimated": { + "staticFeatureFlags": { + "DISABLE_COMMIT_PAUSING_MECHANISM": true + } } } diff --git a/apps/mobile/src/features/threads/ThreadFeed.tsx b/apps/mobile/src/features/threads/ThreadFeed.tsx index 8ad117c8635..63a5e6bbce3 100644 --- a/apps/mobile/src/features/threads/ThreadFeed.tsx +++ b/apps/mobile/src/features/threads/ThreadFeed.tsx @@ -45,14 +45,7 @@ import { import { TouchableOpacity } from "react-native-gesture-handler"; import ImageViewing from "react-native-image-viewing"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import Animated, { - FadeIn, - FadeInUp, - useSharedValue, - withTiming, - type LayoutAnimationsValues, - type SharedValue, -} from "react-native-reanimated"; +import Animated, { FadeIn, FadeInUp, type SharedValue } from "react-native-reanimated"; import { useThemeColor } from "../../lib/useThemeColor"; import { useFontFamily } from "../../lib/useFontFamily"; import { copyTextWithHaptic } from "../../lib/copyTextWithHaptic"; @@ -117,14 +110,6 @@ function formatMessageTime(input: string): string { return MESSAGE_TIME_FORMATTER.format(timestamp); } -// Rows shift when content above them grows (streaming text, work-log folds); -// animating the container position turns those jumps into slides. Applied -// conditionally — see the gated transition in ThreadFeed: while browsing -// history the animation must NOT run, or every estimate→actual size -// correction plays as a visible slide against the instant scroll-offset -// compensation from maintainVisibleContentPosition. -const FEED_ITEM_LAYOUT_DURATION_MS = 180; - // Pre-measurement heights for getFixedItemSize, mirroring renderFeedEntry's // classNames. The fold row's min-h-11 (44px) stays taller than its single // text-sm line at every supported base font size (26px at the 22pt maximum), @@ -1434,11 +1419,6 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) { }, [props.onHeaderMaterialVisibilityChange], ); - // True while the viewport sits within ~one screen of the list end — the - // only region where layout shifts should animate. Starts true because the - // list opens pinned to the end. - const nearListEnd = useSharedValue(true); - const handleScroll = useCallback( (event: NativeSyntheticEvent) => { // anchorTopInset, not topContentInset: under automatic insets the list @@ -1446,40 +1426,9 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) { // UIKit's adjustedContentInset, so topContentInset is 0 here). Add the // header height back or the material toggles a full header too late. reportHeaderMaterialVisibility(event.nativeEvent.contentOffset.y + anchorTopInset > 6); - const { contentOffset, contentSize, layoutMeasurement } = event.nativeEvent; - nearListEnd.value = - contentSize.height - layoutMeasurement.height - contentOffset.y < layoutMeasurement.height; }, - [reportHeaderMaterialVisibility, anchorTopInset, nearListEnd], + [reportHeaderMaterialVisibility, anchorTopInset], ); - - // Gated variant of the 180ms feed layout slide. Instant while browsing - // history: maintainVisibleContentPosition compensates the scroll offset in - // the same frame a row's measured size lands, so an instant reposition is - // invisible — animating it is exactly what made cold upward scrolls slide - // and jump. Near the end the slide stays on: streaming growth and sends - // shift rows at rest, where the animation is the thing preventing a hard - // visual snap. - const feedItemLayoutTransition = useMemo(() => { - return (values: LayoutAnimationsValues) => { - "worklet"; - const duration = nearListEnd.value ? FEED_ITEM_LAYOUT_DURATION_MS : 0; - return { - initialValues: { - originX: values.currentOriginX, - originY: values.currentOriginY, - width: values.currentWidth, - height: values.currentHeight, - }, - animations: { - originX: withTiming(values.targetOriginX, { duration }), - originY: withTiming(values.targetOriginY, { duration }), - width: withTiming(values.targetWidth, { duration }), - height: withTiming(values.targetHeight, { duration }), - }, - }; - }; - }, [nearListEnd]); const handleViewportLayout = useCallback((event: LayoutChangeEvent) => { const nextWidth = Math.round(event.nativeEvent.layout.width); const nextHeight = Math.round(event.nativeEvent.layout.height); @@ -1817,7 +1766,6 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) { } : { scrollIndicatorInsets: { top: topContentInset, bottom: 0 } })} {...(anchoredEndSpace ? { anchoredEndSpace } : {})} - itemLayoutAnimation={feedItemLayoutTransition} // Patched LegendList prop (patches/@legendapp__list@3.2.0.patch): // lets its scroll math clamp programmatic scrolls to -headerInset // instead of 0, so initialScrollAtEnd/maintainScrollAtEnd on short