From 3dc11ccfd8023999d6295b06e88ebfe9c1d28be6 Mon Sep 17 00:00:00 2001 From: Ivan Sekovanikj Date: Mon, 13 Apr 2026 22:32:15 +0200 Subject: [PATCH 1/7] perf: improve animation performance --- .../MessageOverlayHostLayer.tsx | 45 +++++++------------ .../MessageOverlayHostLayer.test.tsx | 19 ++++---- 2 files changed, 23 insertions(+), 41 deletions(-) diff --git a/package/src/contexts/overlayContext/MessageOverlayHostLayer.tsx b/package/src/contexts/overlayContext/MessageOverlayHostLayer.tsx index 67a10eb656..0b729cbb89 100644 --- a/package/src/contexts/overlayContext/MessageOverlayHostLayer.tsx +++ b/package/src/contexts/overlayContext/MessageOverlayHostLayer.tsx @@ -166,8 +166,7 @@ export const MessageOverlayHostLayer = ({ BackgroundComponent }: MessageOverlayH useAnimatedReaction( () => { if (!topH.value) return undefined; - const correction = isActive ? (closing ? closeCorrectionY.value : messageShiftY.value) : 0; - return topH.value.y + correction; + return isActive ? (closing ? closeCorrectionY.value : messageShiftY.value) : 0; }, (next, previous) => { if (next === undefined) { @@ -188,8 +187,7 @@ export const MessageOverlayHostLayer = ({ BackgroundComponent }: MessageOverlayH useAnimatedReaction( () => { if (!messageH.value) return undefined; - const correction = isActive ? (closing ? closeCorrectionY.value : messageShiftY.value) : 0; - return messageH.value.y + correction; + return isActive ? (closing ? closeCorrectionY.value : messageShiftY.value) : 0; }, (next, previous) => { if (next === undefined) { @@ -210,8 +208,7 @@ export const MessageOverlayHostLayer = ({ BackgroundComponent }: MessageOverlayH useAnimatedReaction( () => { if (!bottomH.value) return undefined; - const correction = isActive ? (closing ? closeCorrectionY.value : bottomShiftY.value) : 0; - return bottomH.value.y + correction; + return isActive ? (closing ? closeCorrectionY.value : bottomShiftY.value) : 0; }, (next, previous) => { if (next === undefined) { @@ -230,52 +227,43 @@ export const MessageOverlayHostLayer = ({ BackgroundComponent }: MessageOverlayH ); const topItemStyle = useAnimatedStyle(() => { - if (!topVisualY.value || !topH.value) return { opacity: 0, height: 0, width: 0 }; + if (!topH.value) return { opacity: 0, height: 0, width: 0 }; const horizontalPosition = I18nManager.isRTL ? { right: topH.value.x } : { left: topH.value.x }; return { height: topH.value.h, - position: 'absolute', - top: topVisualY.value, opacity: 1, + position: 'absolute', + top: topH.value.y, + transform: [{ scale: backdrop.value }, { translateY: topVisualY.value }], width: topH.value.w, ...horizontalPosition, }; }); - const topItemTranslateStyle = useAnimatedStyle(() => { - return { - transform: [{ scale: backdrop.value }], - }; - }); - const bottomItemStyle = useAnimatedStyle(() => { - if (!bottomVisualY.value || !bottomH.value) return { opacity: 0, height: 0, width: 0 }; + if (!bottomH.value) return { opacity: 0, height: 0, width: 0 }; const horizontalPosition = I18nManager.isRTL ? { right: bottomH.value.x } : { left: bottomH.value.x }; return { height: bottomH.value.h, - position: 'absolute', - top: bottomVisualY.value, opacity: 1, + position: 'absolute', + top: bottomH.value.y, + transform: [{ scale: backdrop.value }, { translateY: bottomVisualY.value }], width: bottomH.value.w, ...horizontalPosition, }; }); - const bottomItemTranslateStyle = useAnimatedStyle(() => { - return { - transform: [{ scale: backdrop.value }], - }; - }); - const hostStyle = useAnimatedStyle(() => { if (!messageH.value) return { height: 0 }; return { height: messageH.value.h, left: messageH.value.x, position: 'absolute', - top: messageVisualY.value, + top: messageH.value.y, + transform: [{ translateY: messageVisualY.value }], width: messageH.value.w, }; }); @@ -334,7 +322,7 @@ export const MessageOverlayHostLayer = ({ BackgroundComponent }: MessageOverlayH /> ) : null} - + @@ -346,10 +334,7 @@ export const MessageOverlayHostLayer = ({ BackgroundComponent }: MessageOverlayH - + diff --git a/package/src/contexts/overlayContext/__tests__/MessageOverlayHostLayer.test.tsx b/package/src/contexts/overlayContext/__tests__/MessageOverlayHostLayer.test.tsx index 64d2abc5af..4ec91f1914 100644 --- a/package/src/contexts/overlayContext/__tests__/MessageOverlayHostLayer.test.tsx +++ b/package/src/contexts/overlayContext/__tests__/MessageOverlayHostLayer.test.tsx @@ -181,26 +181,25 @@ describe('MessageOverlayHostLayer', () => { expect(StyleSheet.flatten(topSlot.props.style)).toMatchObject({ height: TOP_RECT.h, left: TOP_RECT.x, - opacity: 1, position: 'absolute', - top: 38, - transform: [{ scale: 1 }], + top: TOP_RECT.y, + transform: [{ scale: 1 }, { translateY: 38 }], width: TOP_RECT.w, }); expect(StyleSheet.flatten(messageSlot.props.style)).toMatchObject({ height: MESSAGE_RECT.h, left: MESSAGE_RECT.x, position: 'absolute', - top: 38, + top: MESSAGE_RECT.y, + transform: [{ translateY: 38 }], width: MESSAGE_RECT.w, }); expect(StyleSheet.flatten(bottomSlot.props.style)).toMatchObject({ height: BOTTOM_RECT.h, left: BOTTOM_RECT.x, - opacity: 1, position: 'absolute', - top: 88, - transform: [{ scale: 1 }], + top: BOTTOM_RECT.y, + transform: [{ scale: 1 }, { translateY: -12 }], width: BOTTOM_RECT.w, }); }); @@ -234,8 +233,7 @@ describe('MessageOverlayHostLayer', () => { expect(StyleSheet.flatten(screen.getByTestId('message-overlay-top').props.style)).toMatchObject( { - opacity: 0, - transform: [{ scale: 0 }], + height: 0, }, ); expect( @@ -244,8 +242,7 @@ describe('MessageOverlayHostLayer', () => { expect( StyleSheet.flatten(screen.getByTestId('message-overlay-bottom').props.style), ).toMatchObject({ - opacity: 0, - transform: [{ scale: 0 }], + height: 0, }); }); }); From 03fd05e1cf26e970d4f0dad650b6b22f25db30b9 Mon Sep 17 00:00:00 2001 From: Ivan Sekovanikj Date: Tue, 14 Apr 2026 01:32:18 +0200 Subject: [PATCH 2/7] fix: properly utilize preventPress --- .../src/components/Message/MessageItemView/MessageContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/src/components/Message/MessageItemView/MessageContent.tsx b/package/src/components/Message/MessageItemView/MessageContent.tsx index 429e299e03..a66c2e0e5b 100644 --- a/package/src/components/Message/MessageItemView/MessageContent.tsx +++ b/package/src/components/Message/MessageItemView/MessageContent.tsx @@ -241,7 +241,7 @@ const MessageContentWithContext = (props: MessageContentPropsWithContext) => { return ( message.quoted_message && ( { if (onLongPress) { From e9eed454567d70728e87641d057c4eccddf7103e Mon Sep 17 00:00:00 2001 From: Ivan Sekovanikj Date: Tue, 14 Apr 2026 01:40:27 +0200 Subject: [PATCH 3/7] perf: improve message performance --- .../MessageItemView/MessageContent.tsx | 73 +++++++++---------- .../src/contexts/themeContext/utils/theme.ts | 2 - .../src/state-store/message-overlay-store.ts | 8 +- 3 files changed, 42 insertions(+), 41 deletions(-) diff --git a/package/src/components/Message/MessageItemView/MessageContent.tsx b/package/src/components/Message/MessageItemView/MessageContent.tsx index a66c2e0e5b..dc068c2689 100644 --- a/package/src/components/Message/MessageItemView/MessageContent.tsx +++ b/package/src/components/Message/MessageItemView/MessageContent.tsx @@ -167,7 +167,6 @@ const MessageContentWithContext = (props: MessageContentPropsWithContext) => { messageGroupedSingleOrBottomContainer, messageGroupedTopContainer, replyContainer, - wrapper, }, }, }, @@ -368,43 +367,41 @@ const MessageContentWithContext = (props: MessageContentPropsWithContext) => { } }} > - - - {MessageContentTopView ? : null} - {hasContentSideViews ? ( - - {MessageContentLeadingView ? : null} - {contentBody} - {MessageContentTrailingView ? : null} - - ) : ( - contentBody - )} - {MessageContentBottomView ? : null} - + + {MessageContentTopView ? : null} + {hasContentSideViews ? ( + + {MessageContentLeadingView ? : null} + {contentBody} + {MessageContentTrailingView ? : null} + + ) : ( + contentBody + )} + {MessageContentBottomView ? : null} ); diff --git a/package/src/contexts/themeContext/utils/theme.ts b/package/src/contexts/themeContext/utils/theme.ts index 73c4659430..d722ea2a81 100644 --- a/package/src/contexts/themeContext/utils/theme.ts +++ b/package/src/contexts/themeContext/utils/theme.ts @@ -641,7 +641,6 @@ export type Theme = { textContainer: ViewStyle & { onlyEmojiMarkdown: MarkdownStyle; }; - wrapper: ViewStyle; timestampText?: TextStyle; }; deleted: { @@ -1568,7 +1567,6 @@ export const defaultTheme: Theme = { onlyEmojiMarkdown: { text: { fontSize: 50 } }, }, timestampText: {}, - wrapper: {}, }, deleted: { containerInner: {}, diff --git a/package/src/state-store/message-overlay-store.ts b/package/src/state-store/message-overlay-store.ts index 6acbb9eb95..beafbb5ba7 100644 --- a/package/src/state-store/message-overlay-store.ts +++ b/package/src/state-store/message-overlay-store.ts @@ -94,7 +94,13 @@ export const closeOverlay = () => { return; } - overlayStore.partialNext({ closing: true }); + requestAnimationFrame(() => { + if (!overlayStore.getLatestValue().id) { + return; + } + + overlayStore.partialNext({ closing: true }); + }); }; let actionQueue: Array<() => void | Promise> = []; From 791801ba7e3767bebd6bb0bb41353138061a6e22 Mon Sep 17 00:00:00 2001 From: Ivan Sekovanikj Date: Tue, 14 Apr 2026 02:10:01 +0200 Subject: [PATCH 4/7] perf: remove useAnimatedReaction in favor of inline decision tree --- .../MessageOverlayHostLayer.tsx | 87 +++---------------- 1 file changed, 13 insertions(+), 74 deletions(-) diff --git a/package/src/contexts/overlayContext/MessageOverlayHostLayer.tsx b/package/src/contexts/overlayContext/MessageOverlayHostLayer.tsx index 0b729cbb89..ffe82957dd 100644 --- a/package/src/contexts/overlayContext/MessageOverlayHostLayer.tsx +++ b/package/src/contexts/overlayContext/MessageOverlayHostLayer.tsx @@ -11,7 +11,6 @@ import { Gesture, GestureDetector } from 'react-native-gesture-handler'; import Animated, { clamp, runOnJS, - useAnimatedReaction, useAnimatedStyle, useDerivedValue, useSharedValue, @@ -65,9 +64,6 @@ export const MessageOverlayHostLayer = ({ BackgroundComponent }: MessageOverlayH const topH = useSharedValue(undefined); const bottomH = useSharedValue(undefined); const closeCorrectionY = useSharedValue(0); - const topVisualY = useSharedValue(0); - const messageVisualY = useSharedValue(0); - const bottomVisualY = useSharedValue(0); const topInset = insets.top; // Due to edge-to-edge in combination with various libraries, Android sometimes reports @@ -99,9 +95,6 @@ export const MessageOverlayHostLayer = ({ BackgroundComponent }: MessageOverlayH topH.value = undefined; bottomH.value = undefined; closeCorrectionY.value = 0; - topVisualY.value = 0; - messageVisualY.value = 0; - bottomVisualY.value = 0; }, setBottomH: (rect) => { bottomH.value = rect; @@ -113,7 +106,7 @@ export const MessageOverlayHostLayer = ({ BackgroundComponent }: MessageOverlayH topH.value = rect; }, }), - [bottomH, bottomVisualY, closeCorrectionY, messageH, messageVisualY, topH, topVisualY], + [bottomH, closeCorrectionY, messageH, topH], ); useEffect(() => { @@ -163,78 +156,19 @@ export const MessageOverlayHostLayer = ({ BackgroundComponent }: MessageOverlayH return solvedBottomTop - bottomH.value.y; }); - useAnimatedReaction( - () => { - if (!topH.value) return undefined; - return isActive ? (closing ? closeCorrectionY.value : messageShiftY.value) : 0; - }, - (next, previous) => { - if (next === undefined) { - topVisualY.value = 0; - return; - } - - if (previous === undefined) { - topVisualY.value = next; - return; - } - - topVisualY.value = withSpring(next, { duration: DURATION }); - }, - [isActive, closing], - ); - - useAnimatedReaction( - () => { - if (!messageH.value) return undefined; - return isActive ? (closing ? closeCorrectionY.value : messageShiftY.value) : 0; - }, - (next, previous) => { - if (next === undefined) { - messageVisualY.value = 0; - return; - } - - if (previous === undefined) { - messageVisualY.value = next; - return; - } - - messageVisualY.value = withSpring(next, { duration: DURATION }); - }, - [isActive, closing], - ); - - useAnimatedReaction( - () => { - if (!bottomH.value) return undefined; - return isActive ? (closing ? closeCorrectionY.value : bottomShiftY.value) : 0; - }, - (next, previous) => { - if (next === undefined) { - bottomVisualY.value = 0; - return; - } - - if (previous === undefined) { - bottomVisualY.value = next; - return; - } - - bottomVisualY.value = withSpring(next, { duration: DURATION }); - }, - [isActive, closing], - ); - const topItemStyle = useAnimatedStyle(() => { if (!topH.value) return { opacity: 0, height: 0, width: 0 }; + const translateY = isActive ? (closing ? closeCorrectionY.value : messageShiftY.value) : 0; const horizontalPosition = I18nManager.isRTL ? { right: topH.value.x } : { left: topH.value.x }; return { height: topH.value.h, opacity: 1, position: 'absolute', top: topH.value.y, - transform: [{ scale: backdrop.value }, { translateY: topVisualY.value }], + transform: [ + { scale: backdrop.value }, + { translateY: withSpring(translateY, { duration: DURATION }) }, + ], width: topH.value.w, ...horizontalPosition, }; @@ -242,6 +176,7 @@ export const MessageOverlayHostLayer = ({ BackgroundComponent }: MessageOverlayH const bottomItemStyle = useAnimatedStyle(() => { if (!bottomH.value) return { opacity: 0, height: 0, width: 0 }; + const translateY = isActive ? (closing ? closeCorrectionY.value : bottomShiftY.value) : 0; const horizontalPosition = I18nManager.isRTL ? { right: bottomH.value.x } : { left: bottomH.value.x }; @@ -250,7 +185,10 @@ export const MessageOverlayHostLayer = ({ BackgroundComponent }: MessageOverlayH opacity: 1, position: 'absolute', top: bottomH.value.y, - transform: [{ scale: backdrop.value }, { translateY: bottomVisualY.value }], + transform: [ + { scale: backdrop.value }, + { translateY: withSpring(translateY, { duration: DURATION }) }, + ], width: bottomH.value.w, ...horizontalPosition, }; @@ -258,12 +196,13 @@ export const MessageOverlayHostLayer = ({ BackgroundComponent }: MessageOverlayH const hostStyle = useAnimatedStyle(() => { if (!messageH.value) return { height: 0 }; + const translateY = isActive ? (closing ? closeCorrectionY.value : messageShiftY.value) : 0; return { height: messageH.value.h, left: messageH.value.x, position: 'absolute', top: messageH.value.y, - transform: [{ translateY: messageVisualY.value }], + transform: [{ translateY: withSpring(translateY, { duration: DURATION }) }], width: messageH.value.w, }; }); From f1c9f29dbf087f0f411f27dc6d5839a66774d184 Mon Sep 17 00:00:00 2001 From: Ivan Sekovanikj Date: Tue, 14 Apr 2026 09:55:48 +0200 Subject: [PATCH 5/7] fix: remove postinstall --- package/expo-package/package.json | 1 - package/native-package/package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/package/expo-package/package.json b/package/expo-package/package.json index 766e09ec37..061e47719f 100644 --- a/package/expo-package/package.json +++ b/package/expo-package/package.json @@ -85,7 +85,6 @@ "expo-audio": "~0.4.6" }, "scripts": { - "postinstall": "if [ -f ../scripts/sync-shared-native.sh ] && [ -d ../shared-native/ios ]; then bash ../scripts/sync-shared-native.sh expo-package; fi", "prepack": "bash ../scripts/sync-shared-native.sh expo-package && cp ../../README.md .", "postpack": "rm README.md && bash ../scripts/clean-shared-native-copies.sh expo-package" }, diff --git a/package/native-package/package.json b/package/native-package/package.json index c4a182187d..3f8484b761 100644 --- a/package/native-package/package.json +++ b/package/native-package/package.json @@ -82,7 +82,6 @@ } }, "scripts": { - "postinstall": "if [ -f ../scripts/sync-shared-native.sh ] && [ -d ../shared-native/ios ]; then bash ../scripts/sync-shared-native.sh native-package; fi", "prepack": "bash ../scripts/sync-shared-native.sh native-package && cp ../../README.md .", "postpack": "rm README.md && bash ../scripts/clean-shared-native-copies.sh native-package" }, From 651ce5688b8e03f37262f03eefc880119dfad4ad Mon Sep 17 00:00:00 2001 From: Ivan Sekovanikj Date: Tue, 14 Apr 2026 10:01:47 +0200 Subject: [PATCH 6/7] fix: tests --- .../__snapshots__/AttachButton.test.js.snap | 114 ++--- .../__snapshots__/SendButton.test.js.snap | 76 +--- .../__snapshots__/Thread.test.js.snap | 400 +++++++++--------- 3 files changed, 242 insertions(+), 348 deletions(-) diff --git a/package/src/components/MessageInput/__tests__/__snapshots__/AttachButton.test.js.snap b/package/src/components/MessageInput/__tests__/__snapshots__/AttachButton.test.js.snap index 99456ca166..8e9c202573 100644 --- a/package/src/components/MessageInput/__tests__/__snapshots__/AttachButton.test.js.snap +++ b/package/src/components/MessageInput/__tests__/__snapshots__/AttachButton.test.js.snap @@ -798,20 +798,11 @@ exports[`AttachButton should call handleAttachButtonPress when the button is cli > @@ -852,20 +843,11 @@ exports[`AttachButton should call handleAttachButtonPress when the button is cli @@ -1685,20 +1667,11 @@ exports[`AttachButton should render a enabled AttachButton 1`] = ` > @@ -1739,20 +1712,11 @@ exports[`AttachButton should render a enabled AttachButton 1`] = ` @@ -2572,20 +2536,11 @@ exports[`AttachButton should render an disabled AttachButton 1`] = ` > @@ -2626,20 +2581,11 @@ exports[`AttachButton should render an disabled AttachButton 1`] = ` diff --git a/package/src/components/MessageInput/__tests__/__snapshots__/SendButton.test.js.snap b/package/src/components/MessageInput/__tests__/__snapshots__/SendButton.test.js.snap index 1c74a43ca1..0a10afb966 100644 --- a/package/src/components/MessageInput/__tests__/__snapshots__/SendButton.test.js.snap +++ b/package/src/components/MessageInput/__tests__/__snapshots__/SendButton.test.js.snap @@ -787,20 +787,11 @@ exports[`SendButton should render a SendButton 1`] = ` > @@ -841,20 +832,11 @@ exports[`SendButton should render a SendButton 1`] = ` @@ -1663,20 +1645,11 @@ exports[`SendButton should render a disabled SendButton 1`] = ` > @@ -1717,20 +1690,11 @@ exports[`SendButton should render a disabled SendButton 1`] = ` diff --git a/package/src/components/Thread/__tests__/__snapshots__/Thread.test.js.snap b/package/src/components/Thread/__tests__/__snapshots__/Thread.test.js.snap index c9e1e0cc1a..eb72439ea0 100644 --- a/package/src/components/Thread/__tests__/__snapshots__/Thread.test.js.snap +++ b/package/src/components/Thread/__tests__/__snapshots__/Thread.test.js.snap @@ -514,91 +514,87 @@ exports[`Thread should match thread snapshot 1`] = ` style={{}} > + - - - - Message6 - + Message6 - + @@ -851,91 +847,87 @@ exports[`Thread should match thread snapshot 1`] = ` style={{}} > + - - - - Message5 - + Message5 - + @@ -1221,91 +1213,87 @@ exports[`Thread should match thread snapshot 1`] = ` style={{}} > + - - - - Message4 - + Message4 - + @@ -1549,91 +1537,87 @@ exports[`Thread should match thread snapshot 1`] = ` style={{}} > + - - - - Message3 - + Message3 - + From 4042c8692ff469f86069d2e4e08d92899eac1544 Mon Sep 17 00:00:00 2001 From: Ivan Sekovanikj Date: Tue, 14 Apr 2026 10:51:08 +0200 Subject: [PATCH 7/7] fix: ci builds --- examples/ExpoMessaging/package.json | 1 + examples/SampleApp/package.json | 1 + examples/TypeScriptMessaging/package.json | 1 + package/package.json | 2 +- 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/ExpoMessaging/package.json b/examples/ExpoMessaging/package.json index 9f64205e0f..1664d6fd02 100644 --- a/examples/ExpoMessaging/package.json +++ b/examples/ExpoMessaging/package.json @@ -4,6 +4,7 @@ "main": "expo-router/entry", "scripts": { "sync-native": "bash ../../package/scripts/reconcile-shared-native.sh expo-package && bash ../../package/scripts/sync-shared-native.sh expo-package", + "preinstall": "yarn sync-native", "prestart": "yarn sync-native", "preandroid": "yarn sync-native", "preios": "yarn sync-native", diff --git a/examples/SampleApp/package.json b/examples/SampleApp/package.json index 68aae30b99..5cacc802f0 100644 --- a/examples/SampleApp/package.json +++ b/examples/SampleApp/package.json @@ -8,6 +8,7 @@ }, "scripts": { "sync-native": "bash ../../package/scripts/reconcile-shared-native.sh native-package && bash ../../package/scripts/sync-shared-native.sh native-package", + "preinstall": "yarn sync-native", "preandroid": "yarn sync-native", "android": "react-native run-android", "preios": "yarn sync-native", diff --git a/examples/TypeScriptMessaging/package.json b/examples/TypeScriptMessaging/package.json index c2efffc829..6dba7c5ad9 100644 --- a/examples/TypeScriptMessaging/package.json +++ b/examples/TypeScriptMessaging/package.json @@ -4,6 +4,7 @@ "private": true, "scripts": { "sync-native": "bash ../../package/scripts/reconcile-shared-native.sh native-package && bash ../../package/scripts/sync-shared-native.sh native-package", + "preinstall": "yarn sync-native", "preandroid": "yarn sync-native", "android": "react-native run-android", "preios": "yarn sync-native", diff --git a/package/package.json b/package/package.json index d6e52235da..52f941ae89 100644 --- a/package/package.json +++ b/package/package.json @@ -23,7 +23,7 @@ "README.md" ], "scripts": { - "install-all": "(yarn install --force && (cd native-package && yarn install --force) && (cd expo-package && yarn install --force))", + "install-all": "(yarn run shared-native:sync && yarn install --force && (cd native-package && yarn install --force) && (cd expo-package && yarn install --force))", "shared-native:sync": "bash ./scripts/sync-shared-native.sh all", "shared-native:clean-copies": "bash ./scripts/clean-shared-native-copies.sh all", "build": "rimraf lib && yarn run --silent build-translations && bob build && yarn run --silent copy-translations",