stream-chat-react-native-core 9.0.0-beta.2 → 9.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/lib/commonjs/components/Message/MessageItemView/MessageBubble.js +7 -23
  2. package/lib/commonjs/components/Message/MessageItemView/MessageBubble.js.map +1 -1
  3. package/lib/commonjs/components/Message/MessageItemView/MessageContent.js +2 -8
  4. package/lib/commonjs/components/Message/MessageItemView/MessageContent.js.map +1 -1
  5. package/lib/commonjs/components/Message/MessageItemView/MessageItemView.js +41 -66
  6. package/lib/commonjs/components/Message/MessageItemView/MessageItemView.js.map +1 -1
  7. package/lib/commonjs/version.json +1 -1
  8. package/lib/module/components/Message/MessageItemView/MessageBubble.js +7 -23
  9. package/lib/module/components/Message/MessageItemView/MessageBubble.js.map +1 -1
  10. package/lib/module/components/Message/MessageItemView/MessageContent.js +2 -8
  11. package/lib/module/components/Message/MessageItemView/MessageContent.js.map +1 -1
  12. package/lib/module/components/Message/MessageItemView/MessageItemView.js +41 -66
  13. package/lib/module/components/Message/MessageItemView/MessageItemView.js.map +1 -1
  14. package/lib/module/version.json +1 -1
  15. package/lib/typescript/components/Message/MessageItemView/MessageBubble.d.ts +8 -7
  16. package/lib/typescript/components/Message/MessageItemView/MessageBubble.d.ts.map +1 -1
  17. package/lib/typescript/components/Message/MessageItemView/MessageContent.d.ts +1 -2
  18. package/lib/typescript/components/Message/MessageItemView/MessageContent.d.ts.map +1 -1
  19. package/lib/typescript/components/Message/MessageItemView/MessageItemView.d.ts +1 -10
  20. package/lib/typescript/components/Message/MessageItemView/MessageItemView.d.ts.map +1 -1
  21. package/package.json +1 -1
  22. package/src/components/Message/MessageItemView/MessageBubble.tsx +103 -129
  23. package/src/components/Message/MessageItemView/MessageContent.tsx +3 -23
  24. package/src/components/Message/MessageItemView/MessageItemView.tsx +59 -85
  25. package/src/components/Message/MessageItemView/__tests__/MessageContent.test.js +2 -13
  26. package/src/components/Message/MessageItemView/__tests__/MessageItemView.test.js +14 -0
  27. package/src/components/Message/MessageItemView/__tests__/ReactionListTop.test.js +0 -3
  28. package/src/components/Thread/__tests__/__snapshots__/Thread.test.js.snap +369 -381
  29. package/src/version.json +1 -1
@@ -4,7 +4,6 @@ import { MessageContextValue } from '../../../contexts/messageContext/MessageCon
4
4
  import { MessagesContextValue } from '../../../contexts/messagesContext/MessagesContext';
5
5
  import { TranslationContextValue } from '../../../contexts/translationContext/TranslationContext';
6
6
  export type MessageContentPropsWithContext = Pick<MessageContextValue, 'alignment' | 'goToMessage' | 'groupStyles' | 'isMyMessage' | 'message' | 'messageContentOrder' | 'onLongPress' | 'onPress' | 'onPressIn' | 'otherAttachments' | 'preventPress' | 'threadList' | 'isMessageAIGenerated'> & Pick<MessagesContextValue, 'additionalPressableProps' | 'Attachment' | 'enableMessageGroupingByUser' | 'FileAttachmentGroup' | 'Gallery' | 'isAttachmentEqual' | 'MessageContentBottomView' | 'MessageContentLeadingView' | 'MessageLocation' | 'MessageContentTrailingView' | 'MessageContentTopView' | 'myMessageTheme' | 'Reply' | 'StreamingMessageView'> & Pick<TranslationContextValue, 't'> & {
7
- setMessageContentWidth: React.Dispatch<React.SetStateAction<number>>;
8
7
  /**
9
8
  * Background color for the message content
10
9
  */
@@ -29,7 +28,7 @@ export type MessageContentPropsWithContext = Pick<MessageContextValue, 'alignmen
29
28
  hidePaddingHorizontal?: boolean;
30
29
  hidePaddingBottom?: boolean;
31
30
  };
32
- export type MessageContentProps = Partial<Omit<MessageContentPropsWithContext, 'setMessageContentWidth'>> & Pick<MessageContentPropsWithContext, 'setMessageContentWidth'>;
31
+ export type MessageContentProps = Partial<MessageContentPropsWithContext>;
33
32
  /**
34
33
  * Child of MessageItemView that displays a message's content
35
34
  */
@@ -1 +1 @@
1
- {"version":3,"file":"MessageContent.d.ts","sourceRoot":"","sources":["../../../../../src/components/Message/MessageItemView/MessageContent.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AACvC,OAAO,EAEL,UAAU,EAKX,MAAM,cAAc,CAAC;AAKtB,OAAO,EACL,mBAAmB,EAEpB,MAAM,iDAAiD,CAAC;AAEzD,OAAO,EACL,oBAAoB,EAErB,MAAM,mDAAmD,CAAC;AAE3D,OAAO,EACL,uBAAuB,EAExB,MAAM,yDAAyD,CAAC;AA+BjE,MAAM,MAAM,8BAA8B,GAAG,IAAI,CAC/C,mBAAmB,EACjB,WAAW,GACX,aAAa,GACb,aAAa,GACb,aAAa,GACb,SAAS,GACT,qBAAqB,GACrB,aAAa,GACb,SAAS,GACT,WAAW,GACX,kBAAkB,GAClB,cAAc,GACd,YAAY,GACZ,sBAAsB,CACzB,GACC,IAAI,CACF,oBAAoB,EAClB,0BAA0B,GAC1B,YAAY,GACZ,6BAA6B,GAC7B,qBAAqB,GACrB,SAAS,GACT,mBAAmB,GACnB,0BAA0B,GAC1B,2BAA2B,GAC3B,iBAAiB,GACjB,4BAA4B,GAC5B,uBAAuB,GACvB,gBAAgB,GAChB,OAAO,GACP,sBAAsB,CACzB,GACD,IAAI,CAAC,uBAAuB,EAAE,GAAG,CAAC,GAAG;IACnC,sBAAsB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IACrE;;OAEG;IACH,eAAe,CAAC,EAAE,UAAU,CAAC;IAC7B;;OAEG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,4BAA4B,CAAC,EAAE,OAAO,CAAC;IAEvC;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAqbJ,MAAM,MAAM,mBAAmB,GAAG,OAAO,CACvC,IAAI,CAAC,8BAA8B,EAAE,wBAAwB,CAAC,CAC/D,GACC,IAAI,CAAC,8BAA8B,EAAE,wBAAwB,CAAC,CAAC;AAEjE;;GAEG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,mBAAmB,sBA2GxD,CAAC"}
1
+ {"version":3,"file":"MessageContent.d.ts","sourceRoot":"","sources":["../../../../../src/components/Message/MessageItemView/MessageContent.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AACvC,OAAO,EAA0B,UAAU,EAA+B,MAAM,cAAc,CAAC;AAK/F,OAAO,EACL,mBAAmB,EAEpB,MAAM,iDAAiD,CAAC;AAEzD,OAAO,EACL,oBAAoB,EAErB,MAAM,mDAAmD,CAAC;AAE3D,OAAO,EACL,uBAAuB,EAExB,MAAM,yDAAyD,CAAC;AA+BjE,MAAM,MAAM,8BAA8B,GAAG,IAAI,CAC/C,mBAAmB,EACjB,WAAW,GACX,aAAa,GACb,aAAa,GACb,aAAa,GACb,SAAS,GACT,qBAAqB,GACrB,aAAa,GACb,SAAS,GACT,WAAW,GACX,kBAAkB,GAClB,cAAc,GACd,YAAY,GACZ,sBAAsB,CACzB,GACC,IAAI,CACF,oBAAoB,EAClB,0BAA0B,GAC1B,YAAY,GACZ,6BAA6B,GAC7B,qBAAqB,GACrB,SAAS,GACT,mBAAmB,GACnB,0BAA0B,GAC1B,2BAA2B,GAC3B,iBAAiB,GACjB,4BAA4B,GAC5B,uBAAuB,GACvB,gBAAgB,GAChB,OAAO,GACP,sBAAsB,CACzB,GACD,IAAI,CAAC,uBAAuB,EAAE,GAAG,CAAC,GAAG;IACnC;;OAEG;IACH,eAAe,CAAC,EAAE,UAAU,CAAC;IAC7B;;OAEG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,4BAA4B,CAAC,EAAE,OAAO,CAAC;IAEvC;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AA4aJ,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,8BAA8B,CAAC,CAAC;AAE1E;;GAEG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,mBAAmB,sBA2GxD,CAAC"}
@@ -2,16 +2,7 @@ import React from 'react';
2
2
  import { View } from 'react-native';
3
3
  import { MessageContextValue } from '../../../contexts/messageContext/MessageContext';
4
4
  import { MessagesContextValue } from '../../../contexts/messagesContext/MessagesContext';
5
- export type MessageItemViewPropsWithContext = Pick<MessageContextValue, 'alignment' | 'channel' | 'groupStyles' | 'isMyMessage' | 'message' | 'onlyEmojis' | 'otherAttachments' | 'setQuotedMessage' | 'lastGroupMessage' | 'members'> & Pick<MessagesContextValue, 'customMessageSwipeAction' | 'enableMessageGroupingByUser' | 'enableSwipeToReply' | 'myMessageTheme' | 'MessageAuthor' | 'MessageContent' | 'MessageDeleted' | 'MessageError' | 'MessageFooter' | 'MessageHeader' | 'MessageReplies' | 'MessageSpacer' | 'MessageSwipeContent' | 'messageSwipeToReplyHitSlop' | 'ReactionListBottom' | 'reactionListPosition' | 'reactionListType' | 'ReactionListTop'> & {
6
- /**
7
- * Will determine whether the swipeable wrapper is always rendered for each
8
- * message. If set to false, the animated wrapper will be rendered only when
9
- * a swiping gesture is active and not otherwise.
10
- * Since stateful components would lose their state if we remount them while
11
- * an animation is happening, this should always be set to true in those instances.
12
- */
13
- shouldRenderSwipeableWrapper: boolean;
14
- };
5
+ export type MessageItemViewPropsWithContext = Pick<MessageContextValue, 'alignment' | 'channel' | 'groupStyles' | 'isMyMessage' | 'message' | 'onlyEmojis' | 'otherAttachments' | 'setQuotedMessage' | 'lastGroupMessage' | 'members'> & Pick<MessagesContextValue, 'customMessageSwipeAction' | 'enableMessageGroupingByUser' | 'enableSwipeToReply' | 'myMessageTheme' | 'MessageAuthor' | 'MessageContent' | 'MessageDeleted' | 'MessageError' | 'MessageFooter' | 'MessageHeader' | 'MessageReplies' | 'MessageSpacer' | 'MessageSwipeContent' | 'messageSwipeToReplyHitSlop' | 'ReactionListBottom' | 'reactionListPosition' | 'reactionListType' | 'ReactionListTop'>;
15
6
  export type MessageItemViewProps = Partial<MessageItemViewPropsWithContext>;
16
7
  /**
17
8
  *
@@ -1 +1 @@
1
- {"version":3,"file":"MessageItemView.d.ts","sourceRoot":"","sources":["../../../../../src/components/Message/MessageItemView/MessageItemView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAwC,MAAM,OAAO,CAAC;AAC7D,OAAO,EAA0B,IAAI,EAAa,MAAM,cAAc,CAAC;AAIvE,OAAO,EAEL,mBAAmB,EAEpB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EACL,oBAAoB,EAErB,MAAM,mDAAmD,CAAC;AAkJ3D,MAAM,MAAM,+BAA+B,GAAG,IAAI,CAChD,mBAAmB,EACjB,WAAW,GACX,SAAS,GACT,aAAa,GACb,aAAa,GACb,SAAS,GACT,YAAY,GACZ,kBAAkB,GAClB,kBAAkB,GAClB,kBAAkB,GAClB,SAAS,CACZ,GACC,IAAI,CACF,oBAAoB,EAClB,0BAA0B,GAC1B,6BAA6B,GAC7B,oBAAoB,GACpB,gBAAgB,GAChB,eAAe,GACf,gBAAgB,GAChB,gBAAgB,GAChB,cAAc,GACd,eAAe,GACf,eAAe,GACf,gBAAgB,GAChB,eAAe,GACf,qBAAqB,GACrB,4BAA4B,GAC5B,oBAAoB,GACpB,sBAAsB,GACtB,kBAAkB,GAClB,iBAAiB,CACpB,GAAG;IACF;;;;;;OAMG;IACH,4BAA4B,EAAE,OAAO,CAAC;CACvC,CAAC;AAkSJ,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC,+BAA+B,CAAC,CAAC;AAE5E;;;GAGG;AACH,eAAO,MAAM,eAAe,uGA8E1B,CAAC"}
1
+ {"version":3,"file":"MessageItemView.d.ts","sourceRoot":"","sources":["../../../../../src/components/Message/MessageItemView/MessageItemView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AACnD,OAAO,EAA0B,IAAI,EAAa,MAAM,cAAc,CAAC;AAIvE,OAAO,EAEL,mBAAmB,EAEpB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EACL,oBAAoB,EAErB,MAAM,mDAAmD,CAAC;AAmJ3D,MAAM,MAAM,+BAA+B,GAAG,IAAI,CAChD,mBAAmB,EACjB,WAAW,GACX,SAAS,GACT,aAAa,GACb,aAAa,GACb,SAAS,GACT,YAAY,GACZ,kBAAkB,GAClB,kBAAkB,GAClB,kBAAkB,GAClB,SAAS,CACZ,GACC,IAAI,CACF,oBAAoB,EAClB,0BAA0B,GAC1B,6BAA6B,GAC7B,oBAAoB,GACpB,gBAAgB,GAChB,eAAe,GACf,gBAAgB,GAChB,gBAAgB,GAChB,cAAc,GACd,eAAe,GACf,eAAe,GACf,gBAAgB,GAChB,eAAe,GACf,qBAAqB,GACrB,4BAA4B,GAC5B,oBAAoB,GACpB,sBAAsB,GACtB,kBAAkB,GAClB,iBAAiB,CACpB,CAAC;AAuRJ,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC,+BAA+B,CAAC,CAAC;AAE5E;;;GAGG;AACH,eAAO,MAAM,eAAe,uGAuE1B,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "stream-chat-react-native-core",
3
3
  "description": "The official React Native and Expo components for Stream Chat, a service for building chat applications",
4
- "version": "9.0.0-beta.2",
4
+ "version": "9.0.0-beta.3",
5
5
  "author": {
6
6
  "company": "Stream.io Inc",
7
7
  "name": "Stream.io Inc"
@@ -1,4 +1,4 @@
1
- import React, { SetStateAction, useMemo, useState } from 'react';
1
+ import React, { ReactNode, useMemo, useState } from 'react';
2
2
  import { StyleSheet, View } from 'react-native';
3
3
  import { Gesture, GestureDetector } from 'react-native-gesture-handler';
4
4
 
@@ -15,7 +15,6 @@ import { MessageItemViewPropsWithContext } from './MessageItemView';
15
15
 
16
16
  import { MessagesContextValue, useTheme } from '../../../contexts';
17
17
 
18
- import { useStableCallback } from '../../../hooks';
19
18
  import { NativeHandlers } from '../../../native';
20
19
  import { MessageStatusTypes } from '../../../utils/utils';
21
20
 
@@ -34,18 +33,14 @@ export type MessageBubbleProps = Pick<
34
33
  | 'messageGroupedSingleOrBottom'
35
34
  | 'noBorder'
36
35
  | 'message'
37
- | 'setMessageContentWidth'
38
36
  > &
39
- Pick<MessageItemViewPropsWithContext, 'alignment'> & {
40
- messageContentWidth: number;
41
- };
37
+ Pick<MessageItemViewPropsWithContext, 'alignment'>;
42
38
 
43
39
  export const MessageBubble = React.memo(
44
40
  ({
45
41
  alignment,
46
42
  reactionListPosition,
47
43
  reactionListType,
48
- setMessageContentWidth,
49
44
  MessageContent,
50
45
  ReactionListTop,
51
46
  backgroundColor,
@@ -72,7 +67,6 @@ export const MessageBubble = React.memo(
72
67
  isVeryLastMessage={isVeryLastMessage}
73
68
  messageGroupedSingleOrBottom={messageGroupedSingleOrBottom}
74
69
  noBorder={noBorder}
75
- setMessageContentWidth={setMessageContentWidth}
76
70
  />
77
71
 
78
72
  {isMessageErrorType ? (
@@ -88,130 +82,110 @@ export const MessageBubble = React.memo(
88
82
 
89
83
  const AnimatedWrapper = Animated.createAnimatedComponent(View);
90
84
 
91
- export const SwipableMessageBubble = React.memo(
92
- (
93
- props: MessageBubbleProps &
94
- Pick<MessagesContextValue, 'MessageSwipeContent'> &
95
- Pick<
96
- MessageItemViewPropsWithContext,
97
- 'shouldRenderSwipeableWrapper' | 'messageSwipeToReplyHitSlop'
98
- > & { onSwipe: () => void },
99
- ) => {
100
- const { MessageSwipeContent, messageSwipeToReplyHitSlop, onSwipe, ...messageBubbleProps } =
101
- props;
102
-
103
- const styles = useStyles({ alignment: props.alignment });
104
-
105
- const translateX = useSharedValue(0);
106
- const touchStart = useSharedValue<{ x: number; y: number } | null>(null);
107
- const isSwiping = useSharedValue<boolean>(false);
108
- const [shouldRenderAnimatedWrapper, setShouldRenderAnimatedWrapper] = useState<boolean>(false);
109
-
110
- const SWIPABLE_THRESHOLD = 25;
111
- const MINIMUM_DISTANCE = 8;
112
-
113
- const triggerHaptic = NativeHandlers.triggerHaptic;
114
-
115
- const setMessageContentWidth = useStableCallback((valueOrCallback: SetStateAction<number>) => {
116
- if (typeof valueOrCallback === 'number') {
117
- props.setMessageContentWidth(Math.ceil(valueOrCallback));
118
- return;
119
- }
120
- props.setMessageContentWidth(valueOrCallback);
121
- });
122
-
123
- const swipeGesture = useMemo(
124
- () =>
125
- Gesture.Pan()
126
- .hitSlop(messageSwipeToReplyHitSlop)
127
- .onBegin((event) => {
128
- touchStart.value = { x: event.x, y: event.y };
129
- })
130
- .onTouchesMove((event, state) => {
131
- if (!touchStart.value || !event.changedTouches.length) {
132
- state.fail();
133
- return;
134
- }
135
-
136
- const xDiff = Math.abs(event.changedTouches[0].x - touchStart.value.x);
137
- const yDiff = Math.abs(event.changedTouches[0].y - touchStart.value.y);
138
- const isHorizontalPanning = xDiff > yDiff;
139
- const hasMinimumDistance = xDiff > MINIMUM_DISTANCE || yDiff > MINIMUM_DISTANCE;
85
+ type SwipableMessageWrapperProps = Pick<MessagesContextValue, 'MessageSwipeContent'> &
86
+ Pick<MessageItemViewPropsWithContext, 'alignment' | 'messageSwipeToReplyHitSlop'> & {
87
+ children: ReactNode;
88
+ onSwipe: () => void;
89
+ };
140
90
 
141
- // Only activate if there's significant horizontal movement
142
- if (isHorizontalPanning && hasMinimumDistance) {
143
- state.activate();
144
- if (!isSwiping.value) {
145
- runOnJS(setShouldRenderAnimatedWrapper)(true);
146
- }
147
- isSwiping.value = true;
148
- } else if (hasMinimumDistance) {
149
- // If there's significant movement but not horizontal, fail the gesture
150
- state.fail();
151
- }
152
- })
153
- .onStart(() => {
154
- translateX.value = 0;
155
- })
156
- .onChange(({ translationX }) => {
157
- if (translationX > 0) {
158
- translateX.value = translationX;
91
+ export const SwipableMessageWrapper = React.memo((props: SwipableMessageWrapperProps) => {
92
+ const { MessageSwipeContent, children, messageSwipeToReplyHitSlop, onSwipe } = props;
93
+
94
+ const styles = useStyles({ alignment: props.alignment });
95
+
96
+ const translateX = useSharedValue(0);
97
+ const touchStart = useSharedValue<{ x: number; y: number } | null>(null);
98
+ const isSwiping = useSharedValue<boolean>(false);
99
+ const [shouldRenderAnimatedWrapper, setShouldRenderAnimatedWrapper] = useState<boolean>(false);
100
+
101
+ const SWIPABLE_THRESHOLD = 25;
102
+ const MINIMUM_DISTANCE = 8;
103
+
104
+ const triggerHaptic = NativeHandlers.triggerHaptic;
105
+
106
+ const swipeGesture = useMemo(
107
+ () =>
108
+ Gesture.Pan()
109
+ .hitSlop(messageSwipeToReplyHitSlop)
110
+ .onBegin((event) => {
111
+ touchStart.value = { x: event.x, y: event.y };
112
+ })
113
+ .onTouchesMove((event, state) => {
114
+ if (!touchStart.value || !event.changedTouches.length) {
115
+ state.fail();
116
+ return;
117
+ }
118
+
119
+ const xDiff = Math.abs(event.changedTouches[0].x - touchStart.value.x);
120
+ const yDiff = Math.abs(event.changedTouches[0].y - touchStart.value.y);
121
+ const isHorizontalPanning = xDiff > yDiff;
122
+ const hasMinimumDistance = xDiff > MINIMUM_DISTANCE || yDiff > MINIMUM_DISTANCE;
123
+
124
+ // Only activate if there's significant horizontal movement
125
+ if (isHorizontalPanning && hasMinimumDistance) {
126
+ state.activate();
127
+ if (!isSwiping.value) {
128
+ runOnJS(setShouldRenderAnimatedWrapper)(true);
159
129
  }
160
- })
161
- .onEnd(() => {
162
- if (translateX.value >= SWIPABLE_THRESHOLD) {
163
- runOnJS(onSwipe)();
164
- if (triggerHaptic) {
165
- runOnJS(triggerHaptic)('impactMedium');
166
- }
130
+ isSwiping.value = true;
131
+ } else if (hasMinimumDistance) {
132
+ // If there's significant movement but not horizontal, fail the gesture
133
+ state.fail();
134
+ }
135
+ })
136
+ .onStart(() => {
137
+ translateX.value = 0;
138
+ })
139
+ .onChange(({ translationX }) => {
140
+ if (translationX > 0) {
141
+ translateX.value = translationX;
142
+ }
143
+ })
144
+ .onEnd(() => {
145
+ if (translateX.value >= SWIPABLE_THRESHOLD) {
146
+ runOnJS(onSwipe)();
147
+ if (triggerHaptic) {
148
+ runOnJS(triggerHaptic)('impactMedium');
167
149
  }
168
- isSwiping.value = false;
169
- translateX.value = withSpring(
170
- 0,
171
- {
172
- dampingRatio: 1,
173
- duration: 500,
174
- overshootClamping: true,
175
- stiffness: 1,
176
- },
177
- () => {
178
- runOnJS(setShouldRenderAnimatedWrapper)(false);
179
- },
180
- );
181
- }),
182
- [messageSwipeToReplyHitSlop, touchStart, isSwiping, translateX, onSwipe, triggerHaptic],
183
- );
184
-
185
- const swipeContentAnimatedStyle = useAnimatedStyle(
186
- () => ({
187
- opacity: interpolate(translateX.value, [0, SWIPABLE_THRESHOLD], [0, 1]),
188
- width: translateX.value,
189
- }),
190
- [],
191
- );
192
-
193
- return (
194
- <GestureDetector gesture={swipeGesture}>
195
- <View
196
- hitSlop={messageSwipeToReplyHitSlop}
197
- style={[
198
- styles.contentWrapper,
199
- props.messageContentWidth > 0 && shouldRenderAnimatedWrapper
200
- ? { width: props.messageContentWidth }
201
- : {},
202
- ]}
203
- >
204
- {shouldRenderAnimatedWrapper ? (
205
- <AnimatedWrapper style={[styles.swipeContentContainer, swipeContentAnimatedStyle]}>
206
- {MessageSwipeContent ? <MessageSwipeContent /> : null}
207
- </AnimatedWrapper>
208
- ) : null}
209
- <MessageBubble {...messageBubbleProps} setMessageContentWidth={setMessageContentWidth} />
210
- </View>
211
- </GestureDetector>
212
- );
213
- },
214
- );
150
+ }
151
+ isSwiping.value = false;
152
+ translateX.value = withSpring(
153
+ 0,
154
+ {
155
+ dampingRatio: 1,
156
+ duration: 500,
157
+ overshootClamping: true,
158
+ stiffness: 1,
159
+ },
160
+ () => {
161
+ runOnJS(setShouldRenderAnimatedWrapper)(false);
162
+ },
163
+ );
164
+ }),
165
+ [messageSwipeToReplyHitSlop, touchStart, isSwiping, translateX, onSwipe, triggerHaptic],
166
+ );
167
+
168
+ const swipeContentAnimatedStyle = useAnimatedStyle(
169
+ () => ({
170
+ opacity: interpolate(translateX.value, [0, SWIPABLE_THRESHOLD], [0, 1]),
171
+ width: translateX.value,
172
+ }),
173
+ [],
174
+ );
175
+
176
+ return (
177
+ <GestureDetector gesture={swipeGesture}>
178
+ <View hitSlop={messageSwipeToReplyHitSlop} style={styles.contentWrapper}>
179
+ {shouldRenderAnimatedWrapper ? (
180
+ <AnimatedWrapper style={[styles.swipeContentContainer, swipeContentAnimatedStyle]}>
181
+ {MessageSwipeContent ? <MessageSwipeContent /> : null}
182
+ </AnimatedWrapper>
183
+ ) : null}
184
+ {children}
185
+ </View>
186
+ </GestureDetector>
187
+ );
188
+ });
215
189
 
216
190
  const useStyles = ({ alignment }: { alignment?: 'left' | 'right' }) => {
217
191
  const {
@@ -1,12 +1,5 @@
1
1
  import React, { useMemo } from 'react';
2
- import {
3
- AnimatableNumericValue,
4
- ColorValue,
5
- LayoutChangeEvent,
6
- Pressable,
7
- StyleSheet,
8
- View,
9
- } from 'react-native';
2
+ import { AnimatableNumericValue, ColorValue, Pressable, StyleSheet, View } from 'react-native';
10
3
 
11
4
  import { MessageTextContainer } from './MessageTextContainer';
12
5
 
@@ -89,7 +82,6 @@ export type MessageContentPropsWithContext = Pick<
89
82
  | 'StreamingMessageView'
90
83
  > &
91
84
  Pick<TranslationContextValue, 't'> & {
92
- setMessageContentWidth: React.Dispatch<React.SetStateAction<number>>;
93
85
  /**
94
86
  * Background color for the message content
95
87
  */
@@ -147,7 +139,6 @@ const MessageContentWithContext = (props: MessageContentPropsWithContext) => {
147
139
  otherAttachments,
148
140
  preventPress,
149
141
  Reply,
150
- setMessageContentWidth,
151
142
  StreamingMessageView,
152
143
  hidePaddingTop,
153
144
  hidePaddingHorizontal,
@@ -181,14 +172,6 @@ const MessageContentWithContext = (props: MessageContentPropsWithContext) => {
181
172
  },
182
173
  } = useTheme();
183
174
 
184
- const onLayout: (event: LayoutChangeEvent) => void = ({
185
- nativeEvent: {
186
- layout: { width },
187
- },
188
- }) => {
189
- setMessageContentWidth(width);
190
- };
191
-
192
175
  const isAIGenerated = useMemo(
193
176
  () => isMessageAIGenerated(message),
194
177
  [message, isMessageAIGenerated],
@@ -352,7 +335,7 @@ const MessageContentWithContext = (props: MessageContentPropsWithContext) => {
352
335
  }
353
336
  }}
354
337
  >
355
- <View onLayout={onLayout} style={wrapper}>
338
+ <View style={wrapper}>
356
339
  <View
357
340
  style={[
358
341
  styles.containerInner,
@@ -551,10 +534,7 @@ const MemoizedMessageContent = React.memo(
551
534
  areEqual,
552
535
  ) as typeof MessageContentWithContext;
553
536
 
554
- export type MessageContentProps = Partial<
555
- Omit<MessageContentPropsWithContext, 'setMessageContentWidth'>
556
- > &
557
- Pick<MessageContentPropsWithContext, 'setMessageContentWidth'>;
537
+ export type MessageContentProps = Partial<MessageContentPropsWithContext>;
558
538
 
559
539
  /**
560
540
  * Child of MessageItemView that displays a message's content
@@ -1,7 +1,7 @@
1
- import React, { forwardRef, useMemo, useState } from 'react';
1
+ import React, { forwardRef, useMemo } from 'react';
2
2
  import { Dimensions, StyleSheet, View, ViewStyle } from 'react-native';
3
3
 
4
- import { MessageBubble, SwipableMessageBubble } from './MessageBubble';
4
+ import { MessageBubble, SwipableMessageWrapper } from './MessageBubble';
5
5
 
6
6
  import {
7
7
  Alignment,
@@ -71,6 +71,7 @@ const useStyles = ({
71
71
  alignItems: 'flex-end',
72
72
  gap: primitives.spacingXs,
73
73
  flexDirection: alignment === 'left' ? 'row' : 'row-reverse',
74
+ width: '100%',
74
75
  ...container,
75
76
  },
76
77
  contentContainer: {
@@ -190,20 +191,10 @@ export type MessageItemViewPropsWithContext = Pick<
190
191
  | 'reactionListPosition'
191
192
  | 'reactionListType'
192
193
  | 'ReactionListTop'
193
- > & {
194
- /**
195
- * Will determine whether the swipeable wrapper is always rendered for each
196
- * message. If set to false, the animated wrapper will be rendered only when
197
- * a swiping gesture is active and not otherwise.
198
- * Since stateful components would lose their state if we remount them while
199
- * an animation is happening, this should always be set to true in those instances.
200
- */
201
- shouldRenderSwipeableWrapper: boolean;
202
- };
194
+ >;
203
195
 
204
196
  const MessageItemViewWithContext = forwardRef<View, MessageItemViewPropsWithContext>(
205
197
  (props, ref) => {
206
- const [messageContentWidth, setMessageContentWidth] = useState(0);
207
198
  const { width } = Dimensions.get('screen');
208
199
  const {
209
200
  alignment,
@@ -230,7 +221,6 @@ const MessageItemViewWithContext = forwardRef<View, MessageItemViewPropsWithCont
230
221
  reactionListPosition,
231
222
  reactionListType,
232
223
  ReactionListTop,
233
- shouldRenderSwipeableWrapper,
234
224
  setQuotedMessage,
235
225
  } = props;
236
226
 
@@ -295,72 +285,63 @@ const MessageItemViewWithContext = forwardRef<View, MessageItemViewPropsWithCont
295
285
  setQuotedMessage(message);
296
286
  });
297
287
 
288
+ const itemViewContent = (
289
+ <View pointerEvents='box-none' style={styles.container} testID='message-item-view-wrapper'>
290
+ {alignment === 'left' ? <MessageAuthor /> : null}
291
+ {isMessageTypeDeleted ? (
292
+ <MessageDeleted date={message.created_at} groupStyle={groupStyle} />
293
+ ) : (
294
+ <View
295
+ style={[
296
+ styles.contentContainer,
297
+ isMyMessage ? styles.rightAlignItems : styles.leftAlignItems,
298
+ isMessageErrorType ? errorContainer : {},
299
+ ]}
300
+ testID='message-components'
301
+ >
302
+ <MessageHeader />
303
+ <MessageBubble
304
+ alignment={alignment}
305
+ backgroundColor={backgroundColor}
306
+ isVeryLastMessage={isVeryLastMessage}
307
+ MessageContent={MessageContent}
308
+ MessageError={MessageError}
309
+ messageGroupedSingleOrBottom={messageGroupedSingleOrBottom}
310
+ noBorder={noBorder}
311
+ reactionListPosition={reactionListPosition}
312
+ ReactionListTop={ReactionListTop}
313
+ reactionListType={reactionListType}
314
+ message={message}
315
+ />
316
+
317
+ <View style={styles.repliesContainer}>
318
+ <MessageReplies />
319
+ </View>
320
+
321
+ {reactionListPosition === 'bottom' && ReactionListBottom ? (
322
+ <ReactionListBottom type={reactionListType} />
323
+ ) : null}
324
+ <MessageFooter date={message.created_at} />
325
+ </View>
326
+ )}
327
+ {MessageSpacer ? <MessageSpacer /> : null}
328
+ </View>
329
+ );
330
+
298
331
  return (
299
332
  <View ref={ref}>
300
- <View pointerEvents='box-none' style={styles.container} testID='message-item-view-wrapper'>
301
- {alignment === 'left' ? <MessageAuthor /> : null}
302
- {isMessageTypeDeleted ? (
303
- <MessageDeleted date={message.created_at} groupStyle={groupStyle} />
304
- ) : (
305
- <View
306
- style={[
307
- styles.contentContainer,
308
- isMyMessage ? styles.rightAlignItems : styles.leftAlignItems,
309
- isMessageErrorType ? errorContainer : {},
310
- ]}
311
- testID='message-components'
312
- >
313
- <MessageHeader />
314
- {enableSwipeToReply ? (
315
- <SwipableMessageBubble
316
- alignment={alignment}
317
- backgroundColor={backgroundColor}
318
- isVeryLastMessage={isVeryLastMessage}
319
- MessageContent={MessageContent}
320
- messageContentWidth={messageContentWidth}
321
- messageGroupedSingleOrBottom={messageGroupedSingleOrBottom}
322
- MessageSwipeContent={MessageSwipeContent}
323
- MessageError={MessageError}
324
- messageSwipeToReplyHitSlop={messageSwipeToReplyHitSlop}
325
- noBorder={noBorder}
326
- onSwipe={onSwipeActionHandler}
327
- reactionListPosition={reactionListPosition}
328
- reactionListType={reactionListType}
329
- ReactionListTop={ReactionListTop}
330
- setMessageContentWidth={setMessageContentWidth}
331
- shouldRenderSwipeableWrapper={shouldRenderSwipeableWrapper}
332
- message={message}
333
- />
334
- ) : (
335
- <MessageBubble
336
- alignment={alignment}
337
- backgroundColor={backgroundColor}
338
- isVeryLastMessage={isVeryLastMessage}
339
- MessageContent={MessageContent}
340
- MessageError={MessageError}
341
- messageContentWidth={messageContentWidth}
342
- messageGroupedSingleOrBottom={messageGroupedSingleOrBottom}
343
- noBorder={noBorder}
344
- reactionListPosition={reactionListPosition}
345
- ReactionListTop={ReactionListTop}
346
- reactionListType={reactionListType}
347
- setMessageContentWidth={setMessageContentWidth}
348
- message={message}
349
- />
350
- )}
351
-
352
- <View style={styles.repliesContainer}>
353
- <MessageReplies />
354
- </View>
355
-
356
- {reactionListPosition === 'bottom' && ReactionListBottom ? (
357
- <ReactionListBottom type={reactionListType} />
358
- ) : null}
359
- <MessageFooter date={message.created_at} />
360
- </View>
361
- )}
362
- {MessageSpacer ? <MessageSpacer /> : null}
363
- </View>
333
+ {enableSwipeToReply && !isMessageTypeDeleted ? (
334
+ <SwipableMessageWrapper
335
+ alignment={alignment}
336
+ MessageSwipeContent={MessageSwipeContent}
337
+ messageSwipeToReplyHitSlop={messageSwipeToReplyHitSlop}
338
+ onSwipe={onSwipeActionHandler}
339
+ >
340
+ {itemViewContent}
341
+ </SwipableMessageWrapper>
342
+ ) : (
343
+ itemViewContent
344
+ )}
364
345
  </View>
365
346
  );
366
347
  },
@@ -504,7 +485,6 @@ export const MessageItemView = forwardRef<View, MessageItemViewProps>((props, re
504
485
  message,
505
486
  onlyEmojis,
506
487
  otherAttachments,
507
- isMessageAIGenerated,
508
488
  setQuotedMessage,
509
489
  lastGroupMessage,
510
490
  members,
@@ -530,11 +510,6 @@ export const MessageItemView = forwardRef<View, MessageItemViewProps>((props, re
530
510
  reactionListType,
531
511
  ReactionListTop,
532
512
  } = useMessagesContext();
533
- const isAIGenerated = useMemo(
534
- () => isMessageAIGenerated(message),
535
- [message, isMessageAIGenerated],
536
- );
537
- const shouldRenderSwipeableWrapper = (message?.attachments || []).length > 0 || isAIGenerated;
538
513
 
539
514
  return (
540
515
  <MemoizedMessageItemView
@@ -565,7 +540,6 @@ export const MessageItemView = forwardRef<View, MessageItemViewProps>((props, re
565
540
  reactionListType,
566
541
  ReactionListTop,
567
542
  setQuotedMessage,
568
- shouldRenderSwipeableWrapper,
569
543
  lastGroupMessage,
570
544
  members,
571
545
  }}
@@ -1,4 +1,4 @@
1
- import React, { useEffect } from 'react';
1
+ import React from 'react';
2
2
  import { StyleSheet, View } from 'react-native';
3
3
 
4
4
  import { cleanup, render, screen, waitFor } from '@testing-library/react-native';
@@ -17,8 +17,6 @@ import { getTestClientWithUser } from '../../../../mock-builders/mock';
17
17
  import { Channel } from '../../../Channel/Channel';
18
18
  import { Chat } from '../../../Chat/Chat';
19
19
  import { Message } from '../../Message';
20
- import { MessageContent } from '../MessageContent';
21
-
22
20
  describe('MessageContent', () => {
23
21
  let channel;
24
22
  let chatClient;
@@ -359,19 +357,10 @@ describe('MessageContent', () => {
359
357
  user,
360
358
  });
361
359
 
362
- // This needs to be mocked like that cause native onLayout on MessageContent would never
363
- // trigger.
364
- const MessageContentWithMockedMessageContentWidth = (props) => {
365
- useEffect(() => {
366
- props.setMessageContentWidth(100);
367
- }, [props]);
368
- return <MessageContent {...props} />;
369
- };
370
-
371
360
  render(
372
361
  <ChannelsStateProvider>
373
362
  <Chat client={chatClient}>
374
- <Channel channel={channel} MessageContent={MessageContentWithMockedMessageContentWidth}>
363
+ <Channel channel={channel}>
375
364
  <Message groupStyles={['bottom']} message={message} reactionsEnabled />
376
365
  </Channel>
377
366
  </Chat>