@@ -89,7 +89,16 @@ export type MessageSimplePropsWithContext = Pick<
8989 | 'ReactionListBottom'
9090 | 'reactionListPosition'
9191 | 'ReactionListTop'
92- > ;
92+ > & {
93+ /**
94+ * Will determine whether the swipeable wrapper is always rendered for each
95+ * message. If set to false, the animated wrapper will be rendered only when
96+ * a swiping gesture is active and not otherwise.
97+ * Since stateful components would lose their state if we remount them while
98+ * an animation is happening, this should always be set to true in those instances.
99+ */
100+ shouldRenderSwipeableWrapper : boolean ;
101+ } ;
93102
94103const MessageSimpleWithContext = ( props : MessageSimplePropsWithContext ) => {
95104 const [ messageContentWidth , setMessageContentWidth ] = useState ( 0 ) ;
@@ -120,6 +129,7 @@ const MessageSimpleWithContext = (props: MessageSimplePropsWithContext) => {
120129 reactionListPosition,
121130 ReactionListTop,
122131 showMessageStatus,
132+ shouldRenderSwipeableWrapper,
123133 } = props ;
124134
125135 const {
@@ -200,7 +210,9 @@ const MessageSimpleWithContext = (props: MessageSimplePropsWithContext) => {
200210 const translateX = useSharedValue ( 0 ) ;
201211 const touchStart = useSharedValue < { x : number ; y : number } | null > ( null ) ;
202212 const isSwiping = useSharedValue < boolean > ( false ) ;
203- const [ isBeingSwiped , setIsBeingSwiped ] = useState < boolean > ( false ) ;
213+ const [ shouldRenderAnimatedWrapper , setShouldRenderAnimatedWrapper ] = useState < boolean > (
214+ shouldRenderSwipeableWrapper ,
215+ ) ;
204216
205217 const onSwipeToReply = useCallback ( ( ) => {
206218 messageComposer . setQuotedMessage ( message ) ;
@@ -230,7 +242,9 @@ const MessageSimpleWithContext = (props: MessageSimplePropsWithContext) => {
230242 if ( isHorizontalPanning ) {
231243 state . activate ( ) ;
232244 isSwiping . value = true ;
233- runOnJS ( setIsBeingSwiped ) ( true ) ;
245+ if ( ! shouldRenderSwipeableWrapper ) {
246+ runOnJS ( setShouldRenderAnimatedWrapper ) ( isSwiping . value ) ;
247+ }
234248 } else {
235249 state . fail ( ) ;
236250 }
@@ -250,6 +264,7 @@ const MessageSimpleWithContext = (props: MessageSimplePropsWithContext) => {
250264 runOnJS ( triggerHaptic ) ( 'impactMedium' ) ;
251265 }
252266 }
267+ isSwiping . value = false ;
253268 translateX . value = withSpring (
254269 0 ,
255270 {
@@ -259,41 +274,44 @@ const MessageSimpleWithContext = (props: MessageSimplePropsWithContext) => {
259274 stiffness : 1 ,
260275 } ,
261276 ( ) => {
262- isSwiping . value = false ;
263- runOnJS ( setIsBeingSwiped ) ( false ) ;
277+ if ( ! shouldRenderSwipeableWrapper ) {
278+ runOnJS ( setShouldRenderAnimatedWrapper ) ( isSwiping . value ) ;
279+ }
264280 } ,
265281 ) ;
266282 } ) ,
267- [ isSwiping , messageSwipeToReplyHitSlop , onSwipeToReply , touchStart , translateX , triggerHaptic ] ,
283+ [
284+ isSwiping ,
285+ messageSwipeToReplyHitSlop ,
286+ onSwipeToReply ,
287+ touchStart ,
288+ translateX ,
289+ triggerHaptic ,
290+ shouldRenderSwipeableWrapper ,
291+ ] ,
268292 ) ;
269293
270294 const messageBubbleAnimatedStyle = useAnimatedStyle (
271- ( ) =>
272- isSwiping . value
273- ? {
274- transform : [ { translateX : translateX . value } ] ,
275- }
276- : { } ,
295+ ( ) => ( {
296+ transform : [ { translateX : translateX . value } ] ,
297+ } ) ,
277298 [ ] ,
278299 ) ;
279300
280301 const swipeContentAnimatedStyle = useAnimatedStyle (
281- ( ) =>
282- isSwiping . value
283- ? {
284- opacity : interpolate ( translateX . value , [ 0 , THRESHOLD ] , [ 0 , 1 ] ) ,
285- transform : [
286- {
287- translateX : interpolate (
288- translateX . value ,
289- [ 0 , THRESHOLD ] ,
290- [ - THRESHOLD , 0 ] ,
291- Extrapolation . CLAMP ,
292- ) ,
293- } ,
294- ] ,
295- }
296- : { } ,
302+ ( ) => ( {
303+ opacity : interpolate ( translateX . value , [ 0 , THRESHOLD ] , [ 0 , 1 ] ) ,
304+ transform : [
305+ {
306+ translateX : interpolate (
307+ translateX . value ,
308+ [ 0 , THRESHOLD ] ,
309+ [ - THRESHOLD , 0 ] ,
310+ Extrapolation . CLAMP ,
311+ ) ,
312+ } ,
313+ ] ,
314+ } ) ,
297315 [ ] ,
298316 ) ;
299317
@@ -329,7 +347,7 @@ const MessageSimpleWithContext = (props: MessageSimplePropsWithContext) => {
329347 ( ) => (
330348 < GestureDetector gesture = { swipeGesture } >
331349 < View hitSlop = { messageSwipeToReplyHitSlop } style = { [ styles . contentWrapper , contentWrapper ] } >
332- { isBeingSwiped ? (
350+ { shouldRenderAnimatedWrapper ? (
333351 < >
334352 < AnimatedWrapper
335353 style = { [
@@ -353,7 +371,7 @@ const MessageSimpleWithContext = (props: MessageSimplePropsWithContext) => {
353371 [
354372 MessageSwipeContent ,
355373 contentWrapper ,
356- isBeingSwiped ,
374+ shouldRenderAnimatedWrapper ,
357375 messageBubbleAnimatedStyle ,
358376 messageSwipeToReplyHitSlop ,
359377 renderMessageBubble ,
@@ -589,6 +607,7 @@ export const MessageSimple = (props: MessageSimpleProps) => {
589607 onlyEmojis,
590608 otherAttachments,
591609 showMessageStatus,
610+ isMessageAIGenerated,
592611 } = useMessageContext ( ) ;
593612 const {
594613 enableMessageGroupingByUser,
@@ -608,6 +627,11 @@ export const MessageSimple = (props: MessageSimpleProps) => {
608627 reactionListPosition,
609628 ReactionListTop,
610629 } = useMessagesContext ( ) ;
630+ const isAIGenerated = useMemo (
631+ ( ) => isMessageAIGenerated ( message ) ,
632+ [ message , isMessageAIGenerated ] ,
633+ ) ;
634+ const shouldRenderSwipeableWrapper = ( message ?. attachments || [ ] ) . length > 0 || isAIGenerated ;
611635
612636 return (
613637 < MemoizedMessageSimple
@@ -638,6 +662,7 @@ export const MessageSimple = (props: MessageSimpleProps) => {
638662 ReactionListBottom,
639663 reactionListPosition,
640664 ReactionListTop,
665+ shouldRenderSwipeableWrapper,
641666 showMessageStatus,
642667 } }
643668 { ...props }
0 commit comments