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.
- package/lib/commonjs/components/Message/MessageItemView/MessageBubble.js +7 -23
- package/lib/commonjs/components/Message/MessageItemView/MessageBubble.js.map +1 -1
- package/lib/commonjs/components/Message/MessageItemView/MessageContent.js +2 -8
- package/lib/commonjs/components/Message/MessageItemView/MessageContent.js.map +1 -1
- package/lib/commonjs/components/Message/MessageItemView/MessageItemView.js +41 -66
- package/lib/commonjs/components/Message/MessageItemView/MessageItemView.js.map +1 -1
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/Message/MessageItemView/MessageBubble.js +7 -23
- package/lib/module/components/Message/MessageItemView/MessageBubble.js.map +1 -1
- package/lib/module/components/Message/MessageItemView/MessageContent.js +2 -8
- package/lib/module/components/Message/MessageItemView/MessageContent.js.map +1 -1
- package/lib/module/components/Message/MessageItemView/MessageItemView.js +41 -66
- package/lib/module/components/Message/MessageItemView/MessageItemView.js.map +1 -1
- package/lib/module/version.json +1 -1
- package/lib/typescript/components/Message/MessageItemView/MessageBubble.d.ts +8 -7
- package/lib/typescript/components/Message/MessageItemView/MessageBubble.d.ts.map +1 -1
- package/lib/typescript/components/Message/MessageItemView/MessageContent.d.ts +1 -2
- package/lib/typescript/components/Message/MessageItemView/MessageContent.d.ts.map +1 -1
- package/lib/typescript/components/Message/MessageItemView/MessageItemView.d.ts +1 -10
- package/lib/typescript/components/Message/MessageItemView/MessageItemView.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Message/MessageItemView/MessageBubble.tsx +103 -129
- package/src/components/Message/MessageItemView/MessageContent.tsx +3 -23
- package/src/components/Message/MessageItemView/MessageItemView.tsx +59 -85
- package/src/components/Message/MessageItemView/__tests__/MessageContent.test.js +2 -13
- package/src/components/Message/MessageItemView/__tests__/MessageItemView.test.js +14 -0
- package/src/components/Message/MessageItemView/__tests__/ReactionListTop.test.js +0 -3
- package/src/components/Thread/__tests__/__snapshots__/Thread.test.js.snap +369 -381
- 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<
|
|
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,
|
|
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,
|
|
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.
|
|
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, {
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
|
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
|
|
1
|
+
import React, { forwardRef, useMemo } from 'react';
|
|
2
2
|
import { Dimensions, StyleSheet, View, ViewStyle } from 'react-native';
|
|
3
3
|
|
|
4
|
-
import { 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
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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
|
|
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}
|
|
363
|
+
<Channel channel={channel}>
|
|
375
364
|
<Message groupStyles={['bottom']} message={message} reactionsEnabled />
|
|
376
365
|
</Channel>
|
|
377
366
|
</Chat>
|