stream-chat-react-native-core 9.0.0-beta.12 → 9.0.0-beta.14

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 (198) hide show
  1. package/lib/commonjs/components/AttachmentPicker/AttachmentPicker.js +12 -0
  2. package/lib/commonjs/components/AttachmentPicker/AttachmentPicker.js.map +1 -1
  3. package/lib/commonjs/components/AttachmentPicker/components/AttachmentMediaPicker/AttachmentPickerItem.js +30 -30
  4. package/lib/commonjs/components/AttachmentPicker/components/AttachmentMediaPicker/AttachmentPickerItem.js.map +1 -1
  5. package/lib/commonjs/components/ImageGallery/ImageGallery.js +2 -0
  6. package/lib/commonjs/components/ImageGallery/ImageGallery.js.map +1 -1
  7. package/lib/commonjs/components/Message/Message.js +3 -2
  8. package/lib/commonjs/components/Message/Message.js.map +1 -1
  9. package/lib/commonjs/components/Message/MessageItemView/MessageBubble.js +6 -3
  10. package/lib/commonjs/components/Message/MessageItemView/MessageBubble.js.map +1 -1
  11. package/lib/commonjs/components/Message/MessageItemView/MessageReplies.js +17 -11
  12. package/lib/commonjs/components/Message/MessageItemView/MessageReplies.js.map +1 -1
  13. package/lib/commonjs/components/MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList.js +52 -10
  14. package/lib/commonjs/components/MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList.js.map +1 -1
  15. package/lib/commonjs/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.js +10 -8
  16. package/lib/commonjs/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.js.map +1 -1
  17. package/lib/commonjs/components/MessageInput/components/AttachmentPreview/VideoAttachmentUploadPreview.js +8 -2
  18. package/lib/commonjs/components/MessageInput/components/AttachmentPreview/VideoAttachmentUploadPreview.js.map +1 -1
  19. package/lib/commonjs/components/Poll/CreatePollContent.js +11 -5
  20. package/lib/commonjs/components/Poll/CreatePollContent.js.map +1 -1
  21. package/lib/commonjs/components/Poll/Poll.js +4 -2
  22. package/lib/commonjs/components/Poll/Poll.js.map +1 -1
  23. package/lib/commonjs/components/Poll/components/CreatePollOptions.js +6 -3
  24. package/lib/commonjs/components/Poll/components/CreatePollOptions.js.map +1 -1
  25. package/lib/commonjs/components/Poll/components/MultipleAnswersField.js +7 -3
  26. package/lib/commonjs/components/Poll/components/MultipleAnswersField.js.map +1 -1
  27. package/lib/commonjs/components/Poll/components/MultipleVotesSettings.js +9 -4
  28. package/lib/commonjs/components/Poll/components/MultipleVotesSettings.js.map +1 -1
  29. package/lib/commonjs/components/Poll/components/PollAnswersList.js +2 -2
  30. package/lib/commonjs/components/Poll/components/PollAnswersList.js.map +1 -1
  31. package/lib/commonjs/components/Poll/components/PollInputDialog.js +2 -1
  32. package/lib/commonjs/components/Poll/components/PollInputDialog.js.map +1 -1
  33. package/lib/commonjs/components/Poll/components/PollOption.js +12 -5
  34. package/lib/commonjs/components/Poll/components/PollOption.js.map +1 -1
  35. package/lib/commonjs/components/Poll/components/PollResults/PollOptionFullResults.js +7 -5
  36. package/lib/commonjs/components/Poll/components/PollResults/PollOptionFullResults.js.map +1 -1
  37. package/lib/commonjs/components/Poll/components/PollResults/PollResultItem.js +6 -3
  38. package/lib/commonjs/components/Poll/components/PollResults/PollResultItem.js.map +1 -1
  39. package/lib/commonjs/components/Poll/components/PollResults/PollResults.js +8 -3
  40. package/lib/commonjs/components/Poll/components/PollResults/PollResults.js.map +1 -1
  41. package/lib/commonjs/components/Poll/components/PollResults/PollVote.js +4 -2
  42. package/lib/commonjs/components/Poll/components/PollResults/PollVote.js.map +1 -1
  43. package/lib/commonjs/components/ProgressControl/ProgressControl.js +7 -5
  44. package/lib/commonjs/components/ProgressControl/ProgressControl.js.map +1 -1
  45. package/lib/commonjs/components/ProgressControl/WaveProgressBar.js +6 -4
  46. package/lib/commonjs/components/ProgressControl/WaveProgressBar.js.map +1 -1
  47. package/lib/commonjs/components/Reply/Reply.js +14 -7
  48. package/lib/commonjs/components/Reply/Reply.js.map +1 -1
  49. package/lib/commonjs/components/Reply/ReplyMessageView.js +2 -1
  50. package/lib/commonjs/components/Reply/ReplyMessageView.js.map +1 -1
  51. package/lib/commonjs/components/ThreadList/ThreadListItem.js +2 -1
  52. package/lib/commonjs/components/ThreadList/ThreadListItem.js.map +1 -1
  53. package/lib/commonjs/components/UIComponents/SwipableWrapper.js +41 -12
  54. package/lib/commonjs/components/UIComponents/SwipableWrapper.js.map +1 -1
  55. package/lib/commonjs/components/ui/Input/Input.js +8 -4
  56. package/lib/commonjs/components/ui/Input/Input.js.map +1 -1
  57. package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js +1 -0
  58. package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js.map +1 -1
  59. package/lib/commonjs/contexts/overlayContext/MessageOverlayHostLayer.js +14 -6
  60. package/lib/commonjs/contexts/overlayContext/MessageOverlayHostLayer.js.map +1 -1
  61. package/lib/commonjs/index.js +12 -0
  62. package/lib/commonjs/index.js.map +1 -1
  63. package/lib/commonjs/middlewares/attachments.js +33 -1
  64. package/lib/commonjs/middlewares/attachments.js.map +1 -1
  65. package/lib/commonjs/utils/createGenerateVideoThumbnails.js +41 -0
  66. package/lib/commonjs/utils/createGenerateVideoThumbnails.js.map +1 -0
  67. package/lib/commonjs/version.json +1 -1
  68. package/lib/module/components/AttachmentPicker/AttachmentPicker.js +12 -0
  69. package/lib/module/components/AttachmentPicker/AttachmentPicker.js.map +1 -1
  70. package/lib/module/components/AttachmentPicker/components/AttachmentMediaPicker/AttachmentPickerItem.js +30 -30
  71. package/lib/module/components/AttachmentPicker/components/AttachmentMediaPicker/AttachmentPickerItem.js.map +1 -1
  72. package/lib/module/components/ImageGallery/ImageGallery.js +2 -0
  73. package/lib/module/components/ImageGallery/ImageGallery.js.map +1 -1
  74. package/lib/module/components/Message/Message.js +3 -2
  75. package/lib/module/components/Message/Message.js.map +1 -1
  76. package/lib/module/components/Message/MessageItemView/MessageBubble.js +6 -3
  77. package/lib/module/components/Message/MessageItemView/MessageBubble.js.map +1 -1
  78. package/lib/module/components/Message/MessageItemView/MessageReplies.js +17 -11
  79. package/lib/module/components/Message/MessageItemView/MessageReplies.js.map +1 -1
  80. package/lib/module/components/MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList.js +52 -10
  81. package/lib/module/components/MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList.js.map +1 -1
  82. package/lib/module/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.js +10 -8
  83. package/lib/module/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.js.map +1 -1
  84. package/lib/module/components/MessageInput/components/AttachmentPreview/VideoAttachmentUploadPreview.js +8 -2
  85. package/lib/module/components/MessageInput/components/AttachmentPreview/VideoAttachmentUploadPreview.js.map +1 -1
  86. package/lib/module/components/Poll/CreatePollContent.js +11 -5
  87. package/lib/module/components/Poll/CreatePollContent.js.map +1 -1
  88. package/lib/module/components/Poll/Poll.js +4 -2
  89. package/lib/module/components/Poll/Poll.js.map +1 -1
  90. package/lib/module/components/Poll/components/CreatePollOptions.js +6 -3
  91. package/lib/module/components/Poll/components/CreatePollOptions.js.map +1 -1
  92. package/lib/module/components/Poll/components/MultipleAnswersField.js +7 -3
  93. package/lib/module/components/Poll/components/MultipleAnswersField.js.map +1 -1
  94. package/lib/module/components/Poll/components/MultipleVotesSettings.js +9 -4
  95. package/lib/module/components/Poll/components/MultipleVotesSettings.js.map +1 -1
  96. package/lib/module/components/Poll/components/PollAnswersList.js +2 -2
  97. package/lib/module/components/Poll/components/PollAnswersList.js.map +1 -1
  98. package/lib/module/components/Poll/components/PollInputDialog.js +2 -1
  99. package/lib/module/components/Poll/components/PollInputDialog.js.map +1 -1
  100. package/lib/module/components/Poll/components/PollOption.js +12 -5
  101. package/lib/module/components/Poll/components/PollOption.js.map +1 -1
  102. package/lib/module/components/Poll/components/PollResults/PollOptionFullResults.js +7 -5
  103. package/lib/module/components/Poll/components/PollResults/PollOptionFullResults.js.map +1 -1
  104. package/lib/module/components/Poll/components/PollResults/PollResultItem.js +6 -3
  105. package/lib/module/components/Poll/components/PollResults/PollResultItem.js.map +1 -1
  106. package/lib/module/components/Poll/components/PollResults/PollResults.js +8 -3
  107. package/lib/module/components/Poll/components/PollResults/PollResults.js.map +1 -1
  108. package/lib/module/components/Poll/components/PollResults/PollVote.js +4 -2
  109. package/lib/module/components/Poll/components/PollResults/PollVote.js.map +1 -1
  110. package/lib/module/components/ProgressControl/ProgressControl.js +7 -5
  111. package/lib/module/components/ProgressControl/ProgressControl.js.map +1 -1
  112. package/lib/module/components/ProgressControl/WaveProgressBar.js +6 -4
  113. package/lib/module/components/ProgressControl/WaveProgressBar.js.map +1 -1
  114. package/lib/module/components/Reply/Reply.js +14 -7
  115. package/lib/module/components/Reply/Reply.js.map +1 -1
  116. package/lib/module/components/Reply/ReplyMessageView.js +2 -1
  117. package/lib/module/components/Reply/ReplyMessageView.js.map +1 -1
  118. package/lib/module/components/ThreadList/ThreadListItem.js +2 -1
  119. package/lib/module/components/ThreadList/ThreadListItem.js.map +1 -1
  120. package/lib/module/components/UIComponents/SwipableWrapper.js +41 -12
  121. package/lib/module/components/UIComponents/SwipableWrapper.js.map +1 -1
  122. package/lib/module/components/ui/Input/Input.js +8 -4
  123. package/lib/module/components/ui/Input/Input.js.map +1 -1
  124. package/lib/module/contexts/messageInputContext/MessageInputContext.js +1 -0
  125. package/lib/module/contexts/messageInputContext/MessageInputContext.js.map +1 -1
  126. package/lib/module/contexts/overlayContext/MessageOverlayHostLayer.js +14 -6
  127. package/lib/module/contexts/overlayContext/MessageOverlayHostLayer.js.map +1 -1
  128. package/lib/module/index.js +12 -0
  129. package/lib/module/index.js.map +1 -1
  130. package/lib/module/middlewares/attachments.js +33 -1
  131. package/lib/module/middlewares/attachments.js.map +1 -1
  132. package/lib/module/utils/createGenerateVideoThumbnails.js +41 -0
  133. package/lib/module/utils/createGenerateVideoThumbnails.js.map +1 -0
  134. package/lib/module/version.json +1 -1
  135. package/lib/typescript/components/AttachmentPicker/AttachmentPicker.d.ts.map +1 -1
  136. package/lib/typescript/components/AttachmentPicker/components/AttachmentMediaPicker/AttachmentPickerItem.d.ts.map +1 -1
  137. package/lib/typescript/components/ImageGallery/ImageGallery.d.ts.map +1 -1
  138. package/lib/typescript/components/Message/Message.d.ts.map +1 -1
  139. package/lib/typescript/components/Message/MessageItemView/MessageBubble.d.ts.map +1 -1
  140. package/lib/typescript/components/Message/MessageItemView/MessageReplies.d.ts +1 -1
  141. package/lib/typescript/components/Message/MessageItemView/MessageReplies.d.ts.map +1 -1
  142. package/lib/typescript/components/MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList.d.ts.map +1 -1
  143. package/lib/typescript/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.d.ts.map +1 -1
  144. package/lib/typescript/components/MessageInput/components/AttachmentPreview/VideoAttachmentUploadPreview.d.ts.map +1 -1
  145. package/lib/typescript/components/Poll/components/CreatePollOptions.d.ts.map +1 -1
  146. package/lib/typescript/components/Poll/components/PollInputDialog.d.ts.map +1 -1
  147. package/lib/typescript/components/Poll/components/PollOption.d.ts.map +1 -1
  148. package/lib/typescript/components/Poll/components/PollResults/PollResults.d.ts.map +1 -1
  149. package/lib/typescript/components/ProgressControl/ProgressControl.d.ts.map +1 -1
  150. package/lib/typescript/components/ProgressControl/WaveProgressBar.d.ts.map +1 -1
  151. package/lib/typescript/components/Reply/Reply.d.ts.map +1 -1
  152. package/lib/typescript/components/UIComponents/SwipableWrapper.d.ts +4 -1
  153. package/lib/typescript/components/UIComponents/SwipableWrapper.d.ts.map +1 -1
  154. package/lib/typescript/contexts/messageInputContext/MessageInputContext.d.ts.map +1 -1
  155. package/lib/typescript/contexts/overlayContext/MessageOverlayHostLayer.d.ts.map +1 -1
  156. package/lib/typescript/index.d.ts +1 -0
  157. package/lib/typescript/index.d.ts.map +1 -1
  158. package/lib/typescript/middlewares/attachments.d.ts +1 -0
  159. package/lib/typescript/middlewares/attachments.d.ts.map +1 -1
  160. package/lib/typescript/utils/createGenerateVideoThumbnails.d.ts +8 -0
  161. package/lib/typescript/utils/createGenerateVideoThumbnails.d.ts.map +1 -0
  162. package/package.json +1 -1
  163. package/src/components/AttachmentPicker/AttachmentPicker.tsx +14 -0
  164. package/src/components/AttachmentPicker/components/AttachmentMediaPicker/AttachmentPickerItem.tsx +32 -34
  165. package/src/components/ImageGallery/ImageGallery.tsx +5 -1
  166. package/src/components/ImageGallery/__tests__/ImageGallery.test.tsx +33 -0
  167. package/src/components/Message/Message.tsx +15 -3
  168. package/src/components/Message/MessageItemView/MessageBubble.tsx +15 -4
  169. package/src/components/Message/MessageItemView/MessageReplies.tsx +24 -12
  170. package/src/components/MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList.tsx +62 -11
  171. package/src/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.tsx +10 -9
  172. package/src/components/MessageInput/components/AttachmentPreview/VideoAttachmentUploadPreview.tsx +12 -2
  173. package/src/components/Poll/CreatePollContent.tsx +10 -2
  174. package/src/components/Poll/Poll.tsx +2 -0
  175. package/src/components/Poll/components/CreatePollOptions.tsx +12 -1
  176. package/src/components/Poll/components/MultipleAnswersField.tsx +4 -0
  177. package/src/components/Poll/components/MultipleVotesSettings.tsx +6 -1
  178. package/src/components/Poll/components/PollAnswersList.tsx +1 -2
  179. package/src/components/Poll/components/PollInputDialog.tsx +2 -0
  180. package/src/components/Poll/components/PollOption.tsx +47 -39
  181. package/src/components/Poll/components/PollResults/PollOptionFullResults.tsx +41 -37
  182. package/src/components/Poll/components/PollResults/PollResultItem.tsx +63 -62
  183. package/src/components/Poll/components/PollResults/PollResults.tsx +39 -33
  184. package/src/components/Poll/components/PollResults/PollVote.tsx +27 -27
  185. package/src/components/ProgressControl/ProgressControl.tsx +8 -6
  186. package/src/components/ProgressControl/WaveProgressBar.tsx +9 -7
  187. package/src/components/Reply/Reply.tsx +19 -7
  188. package/src/components/Reply/ReplyMessageView.tsx +1 -0
  189. package/src/components/ThreadList/ThreadListItem.tsx +1 -0
  190. package/src/components/UIComponents/SwipableWrapper.tsx +54 -16
  191. package/src/components/UIComponents/__tests__/SwipableWrapper.test.tsx +98 -0
  192. package/src/components/ui/Input/Input.tsx +4 -0
  193. package/src/contexts/messageInputContext/MessageInputContext.tsx +3 -0
  194. package/src/contexts/overlayContext/MessageOverlayHostLayer.tsx +14 -3
  195. package/src/index.ts +1 -0
  196. package/src/middlewares/attachments.ts +34 -0
  197. package/src/utils/createGenerateVideoThumbnails.ts +45 -0
  198. package/src/version.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
 
3
- import { Alert, ImageBackground, StyleSheet, Text, View } from 'react-native';
3
+ import { Alert, Image, StyleSheet, Text, View } from 'react-native';
4
4
 
5
5
  import { FileReference, isLocalImageAttachment, isLocalVideoAttachment } from 'stream-chat';
6
6
 
@@ -68,23 +68,22 @@ const AttachmentVideo = (props: AttachmentPickerItemType) => {
68
68
  };
69
69
 
70
70
  return (
71
- <BottomSheetTouchableOpacity onPress={onPressVideo}>
72
- <ImageBackground
73
- source={{ uri: thumb_url }}
74
- style={[
75
- {
76
- height: size,
77
- margin: 1,
78
- width: size,
79
- },
80
- image,
81
- ]}
82
- >
83
- <View style={[styles.overlay, imageOverlay]}>
84
- <ImageOverlaySelectedComponent index={selectedIndex} />
85
- </View>
86
- <VideoAttachmentMetadataPill duration={videoDuration} format='timer' />
87
- </ImageBackground>
71
+ <BottomSheetTouchableOpacity
72
+ onPress={onPressVideo}
73
+ style={[
74
+ {
75
+ height: size,
76
+ margin: 1,
77
+ width: size,
78
+ },
79
+ image,
80
+ ]}
81
+ >
82
+ <Image source={{ uri: thumb_url }} style={StyleSheet.absoluteFillObject} />
83
+ <View style={[styles.overlay, imageOverlay]}>
84
+ <ImageOverlaySelectedComponent index={selectedIndex} />
85
+ </View>
86
+ <VideoAttachmentMetadataPill duration={videoDuration} format='timer' />
88
87
  </BottomSheetTouchableOpacity>
89
88
  );
90
89
  };
@@ -128,22 +127,21 @@ const AttachmentImage = (props: AttachmentPickerItemType) => {
128
127
  };
129
128
 
130
129
  return (
131
- <BottomSheetTouchableOpacity onPress={onPressImage}>
132
- <ImageBackground
133
- source={{ uri }}
134
- style={[
135
- {
136
- height: size,
137
- margin: 1,
138
- width: size,
139
- },
140
- image,
141
- ]}
142
- >
143
- <View style={[styles.overlay, imageOverlay]}>
144
- <ImageOverlaySelectedComponent index={selectedIndex} />
145
- </View>
146
- </ImageBackground>
130
+ <BottomSheetTouchableOpacity
131
+ onPress={onPressImage}
132
+ style={[
133
+ {
134
+ height: size,
135
+ margin: 1,
136
+ width: size,
137
+ },
138
+ image,
139
+ ]}
140
+ >
141
+ <Image source={{ uri }} style={StyleSheet.absoluteFillObject} />
142
+ <View style={[styles.overlay, imageOverlay]}>
143
+ <ImageOverlaySelectedComponent index={selectedIndex} />
144
+ </View>
147
145
  </BottomSheetTouchableOpacity>
148
146
  );
149
147
  };
@@ -284,7 +284,10 @@ export const ImageGalleryWithContext = (props: ImageGalleryWithContextProps) =>
284
284
  <Animated.View style={[StyleSheet.absoluteFillObject, containerBackground]} />
285
285
  <GestureDetector gesture={Gesture.Simultaneous(singleTap, doubleTap, pinch, pan)}>
286
286
  <Animated.View style={StyleSheet.absoluteFillObject}>
287
- <Animated.View style={[styles.animatedContainer, pagerStyle, pager]}>
287
+ <Animated.View
288
+ testID='image-gallery-pager'
289
+ style={[styles.animatedContainer, pagerStyle, pager]}
290
+ >
288
291
  {assets.map((photo, i) =>
289
292
  photo.type === FileTypes.Video ? (
290
293
  <AnimatedGalleryVideo
@@ -399,6 +402,7 @@ export const clamp = (value: number, lowerBound: number, upperBound: number) =>
399
402
  const styles = StyleSheet.create({
400
403
  animatedContainer: {
401
404
  alignItems: 'center',
405
+ direction: 'ltr',
402
406
  flexDirection: 'row',
403
407
  },
404
408
  });
@@ -1,4 +1,5 @@
1
1
  import React, { useEffect, useState } from 'react';
2
+ import { I18nManager, StyleSheet } from 'react-native';
2
3
 
3
4
  import type { SharedValue } from 'react-native-reanimated';
4
5
 
@@ -79,6 +80,19 @@ const ImageGalleryComponent = (props: ImageGalleryProps & { message: LocalMessag
79
80
  };
80
81
 
81
82
  describe('ImageGallery', () => {
83
+ const originalIsRTL = I18nManager.isRTL;
84
+
85
+ const setRTL = (value: boolean) => {
86
+ Object.defineProperty(I18nManager, 'isRTL', {
87
+ configurable: true,
88
+ value,
89
+ });
90
+ };
91
+
92
+ afterEach(() => {
93
+ setRTL(originalIsRTL);
94
+ });
95
+
82
96
  it('render image gallery component', async () => {
83
97
  render(
84
98
  <ImageGalleryComponent
@@ -99,4 +113,23 @@ describe('ImageGallery', () => {
99
113
  expect(screen.queryAllByLabelText('Image Gallery Video')).toHaveLength(1);
100
114
  });
101
115
  });
116
+
117
+ it('keeps the pager in ltr coordinates when rtl is enabled', async () => {
118
+ setRTL(true);
119
+
120
+ render(
121
+ <ImageGalleryComponent
122
+ message={
123
+ generateMessage({
124
+ attachments: [generateImageAttachment()],
125
+ }) as unknown as LocalMessage
126
+ }
127
+ />,
128
+ );
129
+
130
+ await waitFor(() => {
131
+ const pagerStyle = StyleSheet.flatten(screen.getByTestId('image-gallery-pager').props.style);
132
+ expect(pagerStyle.direction).toBe('ltr');
133
+ });
134
+ });
102
135
  });
@@ -1,5 +1,12 @@
1
1
  import React, { useEffect, useMemo, useRef, useState } from 'react';
2
- import { GestureResponderEvent, StyleProp, StyleSheet, View, ViewStyle } from 'react-native';
2
+ import {
3
+ GestureResponderEvent,
4
+ I18nManager,
5
+ StyleProp,
6
+ StyleSheet,
7
+ View,
8
+ ViewStyle,
9
+ } from 'react-native';
3
10
 
4
11
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
12
  import { Portal } from 'react-native-teleport';
@@ -415,6 +422,11 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
415
422
  : isMyMessage
416
423
  ? 'right'
417
424
  : 'left';
425
+ const overlayItemAlignment = I18nManager.isRTL
426
+ ? alignment === 'right'
427
+ ? 'left'
428
+ : 'right'
429
+ : alignment;
418
430
 
419
431
  /**
420
432
  * attachments contain files/images or other attachments
@@ -847,7 +859,7 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
847
859
  h,
848
860
  w,
849
861
  x:
850
- alignment === 'right'
862
+ overlayItemAlignment === 'right'
851
863
  ? overlayItemsAnchorRect.x + overlayItemsAnchorRect.w - w
852
864
  : overlayItemsAnchorRect.x,
853
865
  y: rect.y - h,
@@ -893,7 +905,7 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
893
905
  h,
894
906
  w,
895
907
  x:
896
- alignment === 'right'
908
+ overlayItemAlignment === 'right'
897
909
  ? overlayItemsAnchorRect.x + overlayItemsAnchorRect.w - w
898
910
  : overlayItemsAnchorRect.x,
899
911
  y: rect.y + rect.h,
@@ -1,5 +1,5 @@
1
1
  import React, { ReactNode, useMemo, useState } from 'react';
2
- import { StyleSheet, View } from 'react-native';
2
+ import { I18nManager, StyleSheet, View } from 'react-native';
3
3
  import { Gesture, GestureDetector } from 'react-native-gesture-handler';
4
4
 
5
5
  import Animated, {
@@ -26,6 +26,8 @@ type SwipableMessageWrapperProps = Pick<MessagesContextValue, 'MessageSwipeConte
26
26
 
27
27
  export const SwipableMessageWrapper = React.memo((props: SwipableMessageWrapperProps) => {
28
28
  const { MessageSwipeContent, children, messageSwipeToReplyHitSlop, onSwipe } = props;
29
+ const isRTL = I18nManager.isRTL;
30
+ const swipeDirectionMultiplier = isRTL ? -1 : 1;
29
31
 
30
32
  const styles = useStyles();
31
33
 
@@ -73,8 +75,9 @@ export const SwipableMessageWrapper = React.memo((props: SwipableMessageWrapperP
73
75
  translateX.value = 0;
74
76
  })
75
77
  .onChange(({ translationX }) => {
76
- if (translationX > 0) {
77
- translateX.value = translationX;
78
+ const swipeDistance = translationX * swipeDirectionMultiplier;
79
+ if (swipeDistance > 0) {
80
+ translateX.value = swipeDistance;
78
81
  }
79
82
  })
80
83
  .onEnd(() => {
@@ -98,7 +101,15 @@ export const SwipableMessageWrapper = React.memo((props: SwipableMessageWrapperP
98
101
  },
99
102
  );
100
103
  }),
101
- [messageSwipeToReplyHitSlop, touchStart, isSwiping, translateX, onSwipe, triggerHaptic],
104
+ [
105
+ messageSwipeToReplyHitSlop,
106
+ onSwipe,
107
+ swipeDirectionMultiplier,
108
+ touchStart,
109
+ isSwiping,
110
+ translateX,
111
+ triggerHaptic,
112
+ ],
102
113
  );
103
114
 
104
115
  const swipeContentAnimatedStyle = useAnimatedStyle(
@@ -1,5 +1,5 @@
1
1
  import React, { useMemo } from 'react';
2
- import { Pressable, StyleSheet, Text, View } from 'react-native';
2
+ import { I18nManager, Pressable, StyleSheet, Text, View } from 'react-native';
3
3
 
4
4
  import {
5
5
  MessageContextValue,
@@ -22,6 +22,7 @@ import { useShouldUseOverlayStyles } from '../hooks/useShouldUseOverlayStyles';
22
22
  export type MessageRepliesPropsWithContext = Pick<
23
23
  MessageContextValue,
24
24
  | 'alignment'
25
+ | 'isMyMessage'
25
26
  | 'message'
26
27
  | 'onLongPress'
27
28
  | 'onPress'
@@ -36,6 +37,7 @@ export type MessageRepliesPropsWithContext = Pick<
36
37
  const MessageRepliesWithContext = (props: MessageRepliesPropsWithContext) => {
37
38
  const {
38
39
  alignment,
40
+ isMyMessage,
39
41
  message,
40
42
  MessageRepliesAvatars,
41
43
  onLongPress,
@@ -57,15 +59,29 @@ const MessageRepliesWithContext = (props: MessageRepliesPropsWithContext) => {
57
59
  } = useTheme();
58
60
  const styles = useStyles();
59
61
 
62
+ const physicalAlignment = I18nManager.isRTL
63
+ ? alignment === 'left'
64
+ ? 'right'
65
+ : 'left'
66
+ : alignment;
67
+ const connectorStroke = isMyMessage
68
+ ? semantics.chatThreadConnectorOutgoing
69
+ : semantics.chatThreadConnectorIncoming;
70
+
71
+ const connector =
72
+ physicalAlignment === 'left' ? (
73
+ <ReplyConnectorLeft height={48} width={16} stroke={connectorStroke} />
74
+ ) : (
75
+ <ReplyConnectorRight height={48} width={16} stroke={connectorStroke} />
76
+ );
77
+
60
78
  if (threadList || !message.reply_count) {
61
79
  return null;
62
80
  }
63
81
 
64
82
  return (
65
83
  <View style={[styles.container, container]}>
66
- {alignment === 'left' && (
67
- <ReplyConnectorLeft height={48} width={16} stroke={semantics.chatThreadConnectorIncoming} />
68
- )}
84
+ {alignment === 'left' ? connector : null}
69
85
  <Pressable
70
86
  disabled={preventPress}
71
87
  onLongPress={(event) => {
@@ -96,7 +112,7 @@ const MessageRepliesWithContext = (props: MessageRepliesPropsWithContext) => {
96
112
  }}
97
113
  style={[
98
114
  styles.content,
99
- { flexDirection: alignment === 'left' ? 'row' : 'row-reverse' },
115
+ { flexDirection: physicalAlignment === 'left' ? 'row' : 'row-reverse' },
100
116
  content,
101
117
  ]}
102
118
  testID='message-replies'
@@ -110,13 +126,7 @@ const MessageRepliesWithContext = (props: MessageRepliesPropsWithContext) => {
110
126
  })}
111
127
  </Text>
112
128
  </Pressable>
113
- {alignment === 'right' && (
114
- <ReplyConnectorRight
115
- height={48}
116
- width={16}
117
- stroke={semantics.chatThreadConnectorOutgoing}
118
- />
119
- )}
129
+ {alignment === 'right' ? connector : null}
120
130
  </View>
121
131
  );
122
132
  };
@@ -171,6 +181,7 @@ export type MessageRepliesProps = Partial<MessageRepliesPropsWithContext>;
171
181
  export const MessageReplies = (props: MessageRepliesProps) => {
172
182
  const {
173
183
  alignment,
184
+ isMyMessage,
174
185
  message,
175
186
  onLongPress,
176
187
  onOpenThread,
@@ -186,6 +197,7 @@ export const MessageReplies = (props: MessageRepliesProps) => {
186
197
  <MemoizedMessageReplies
187
198
  {...{
188
199
  alignment,
200
+ isMyMessage,
189
201
  message,
190
202
  MessageRepliesAvatars,
191
203
  onLongPress,
@@ -1,6 +1,7 @@
1
- import React, { useCallback, useEffect, useRef } from 'react';
1
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import {
3
3
  FlatList,
4
+ I18nManager,
4
5
  LayoutChangeEvent,
5
6
  NativeScrollEvent,
6
7
  NativeSyntheticEvent,
@@ -96,14 +97,18 @@ const UnMemoizedAttachmentUploadPreviewList = (
96
97
  const { audioRecordingSendOnComplete } = useMessageInputContext();
97
98
  const { attachmentManager } = useMessageComposer();
98
99
  const { attachments } = useAttachmentManagerState();
100
+ const isRTL = I18nManager.isRTL;
99
101
  const attachmentListRef = useRef<FlatList<LocalAttachment>>(null);
100
102
  const soundPackageAvailable = isSoundPackageAvailable();
101
103
  const isAudioAttachmentPreview = getIsAudioAttachmentPreview(soundPackageAvailable);
102
104
  const previousNonAudioAttachmentsLengthRef = useRef(0);
103
105
  const contentWidthRef = useRef(0);
106
+ const itemsContentWidthRef = useRef(0);
104
107
  const viewportWidthRef = useRef(0);
105
108
  const scrollOffsetXRef = useRef(0);
109
+ const rtlLeadingSpacerWidthRef = useRef(0);
106
110
  const endShrinkCompensationX = useSharedValue(0);
111
+ const [rtlLeadingSpacerWidth, setRtlLeadingSpacerWidth] = useState(0);
107
112
  const previewAttachments = attachments.filter(
108
113
  (attachment) => !(audioRecordingSendOnComplete && isLocalVoiceRecordingAttachment(attachment)),
109
114
  );
@@ -111,6 +116,7 @@ const UnMemoizedAttachmentUploadPreviewList = (
111
116
  const nonAudioAttachments = previewAttachments.filter(
112
117
  (attachment) => !isAudioAttachmentPreview(attachment),
113
118
  );
119
+ const data = isRTL ? nonAudioAttachments.toReversed() : nonAudioAttachments;
114
120
 
115
121
  const {
116
122
  theme: {
@@ -120,6 +126,27 @@ const UnMemoizedAttachmentUploadPreviewList = (
120
126
  },
121
127
  } = useTheme();
122
128
 
129
+ const updateRtlLeadingSpacerWidth = useCallback(
130
+ (itemsWidth: number, viewportWidth: number) => {
131
+ if (!isRTL || !viewportWidth) {
132
+ if (rtlLeadingSpacerWidthRef.current !== 0) {
133
+ rtlLeadingSpacerWidthRef.current = 0;
134
+ setRtlLeadingSpacerWidth(0);
135
+ }
136
+ return;
137
+ }
138
+
139
+ const nextSpacerWidth = Math.max(0, viewportWidth - itemsWidth);
140
+ if (rtlLeadingSpacerWidthRef.current === nextSpacerWidth) {
141
+ return;
142
+ }
143
+
144
+ rtlLeadingSpacerWidthRef.current = nextSpacerWidth;
145
+ setRtlLeadingSpacerWidth(nextSpacerWidth);
146
+ },
147
+ [isRTL],
148
+ );
149
+
123
150
  const renderItem = useCallback(
124
151
  ({ item }: { item: LocalAttachment }) => {
125
152
  if (isLocalImageAttachment(item)) {
@@ -200,21 +227,31 @@ const UnMemoizedAttachmentUploadPreviewList = (
200
227
  scrollOffsetXRef.current = event.nativeEvent.contentOffset.x;
201
228
  }, []);
202
229
 
203
- const onLayoutHandler = useCallback((event: LayoutChangeEvent) => {
204
- viewportWidthRef.current = event.nativeEvent.layout.width;
205
- }, []);
230
+ const onLayoutHandler = useCallback(
231
+ (event: LayoutChangeEvent) => {
232
+ const viewportWidth = event.nativeEvent.layout.width;
233
+ viewportWidthRef.current = viewportWidth;
234
+ updateRtlLeadingSpacerWidth(itemsContentWidthRef.current, viewportWidth);
235
+ },
236
+ [updateRtlLeadingSpacerWidth],
237
+ );
206
238
 
207
239
  const onContentSizeChangeHandler = useCallback(
208
240
  (width: number) => {
241
+ const itemsContentWidth = isRTL
242
+ ? Math.max(0, width - rtlLeadingSpacerWidthRef.current)
243
+ : width;
209
244
  const previousContentWidth = contentWidthRef.current;
210
- contentWidthRef.current = width;
245
+ contentWidthRef.current = itemsContentWidth;
246
+ itemsContentWidthRef.current = itemsContentWidth;
247
+ updateRtlLeadingSpacerWidth(itemsContentWidth, viewportWidthRef.current);
211
248
 
212
- if (!previousContentWidth || width >= previousContentWidth) {
249
+ if (!previousContentWidth || itemsContentWidth >= previousContentWidth) {
213
250
  return;
214
251
  }
215
252
 
216
253
  const oldMaxOffset = Math.max(0, previousContentWidth - viewportWidthRef.current);
217
- const newMaxOffset = Math.max(0, width - viewportWidthRef.current);
254
+ const newMaxOffset = Math.max(0, itemsContentWidth - viewportWidthRef.current);
218
255
  const offsetBefore = scrollOffsetXRef.current;
219
256
  const wasNearEnd = oldMaxOffset - offsetBefore <= END_ANCHOR_THRESHOLD;
220
257
  const overshoot = Math.max(0, offsetBefore - newMaxOffset);
@@ -232,6 +269,10 @@ const UnMemoizedAttachmentUploadPreviewList = (
232
269
  scrollOffsetXRef.current = newMaxOffset;
233
270
  }
234
271
 
272
+ if (isRTL) {
273
+ return;
274
+ }
275
+
235
276
  const compensation = newMaxOffset - oldMaxOffset;
236
277
  if (compensation !== 0) {
237
278
  cancelAnimation(endShrinkCompensationX);
@@ -241,7 +282,7 @@ const UnMemoizedAttachmentUploadPreviewList = (
241
282
  });
242
283
  }
243
284
  },
244
- [endShrinkCompensationX],
285
+ [endShrinkCompensationX, isRTL, updateRtlLeadingSpacerWidth],
245
286
  );
246
287
 
247
288
  useEffect(() => {
@@ -257,9 +298,13 @@ const UnMemoizedAttachmentUploadPreviewList = (
257
298
  cancelAnimation(endShrinkCompensationX);
258
299
  endShrinkCompensationX.value = 0;
259
300
  requestAnimationFrame(() => {
301
+ if (isRTL) {
302
+ return;
303
+ }
304
+
260
305
  attachmentListRef.current?.scrollToEnd({ animated: true });
261
306
  });
262
- }, [endShrinkCompensationX, nonAudioAttachments.length]);
307
+ }, [endShrinkCompensationX, isRTL, nonAudioAttachments.length]);
263
308
 
264
309
  const animatedListWrapperStyle = useAnimatedStyle(() => ({
265
310
  transform: [{ translateX: endShrinkCompensationX.value }],
@@ -290,7 +335,7 @@ const UnMemoizedAttachmentUploadPreviewList = (
290
335
  </Animated.View>
291
336
  ) : null}
292
337
 
293
- {nonAudioAttachments.length ? (
338
+ {data.length ? (
294
339
  <Animated.View
295
340
  entering={ZoomIn.duration(200)}
296
341
  exiting={ZoomOut.duration(200)}
@@ -298,10 +343,15 @@ const UnMemoizedAttachmentUploadPreviewList = (
298
343
  >
299
344
  <Animated.View style={animatedListWrapperStyle}>
300
345
  <FlatList
301
- data={nonAudioAttachments}
346
+ data={data}
302
347
  horizontal
303
348
  ItemSeparatorComponent={ItemSeparatorComponent}
304
349
  keyExtractor={(item) => item.localMetadata.id}
350
+ ListHeaderComponent={
351
+ isRTL && rtlLeadingSpacerWidth > 0 ? (
352
+ <View style={{ width: rtlLeadingSpacerWidth }} />
353
+ ) : null
354
+ }
305
355
  onContentSizeChange={onContentSizeChangeHandler}
306
356
  onLayout={onLayoutHandler}
307
357
  onScroll={onScrollHandler}
@@ -357,6 +407,7 @@ const styles = StyleSheet.create({
357
407
  },
358
408
  flatList: {
359
409
  overflow: 'visible',
410
+ direction: 'ltr',
360
411
  },
361
412
  itemSeparator: {
362
413
  width: primitives.spacingXs,
@@ -1,6 +1,6 @@
1
1
  import React, { useCallback, useMemo, useState } from 'react';
2
2
 
3
- import { ImageBackground, StyleSheet, View } from 'react-native';
3
+ import { Image, StyleSheet, View } from 'react-native';
4
4
 
5
5
  import { LocalImageAttachment } from 'stream-chat';
6
6
 
@@ -61,13 +61,14 @@ export const ImageAttachmentUploadPreview = ({
61
61
 
62
62
  return (
63
63
  <View style={[styles.wrapper, wrapper]} testID={'image-attachment-upload-preview'}>
64
- <ImageBackground
65
- onError={onErrorHandler}
66
- onLoadEnd={onLoadEndHandler}
67
- source={{ uri: attachment.localMetadata.previewUri ?? attachment.image_url }}
68
- style={[styles.image, upload]}
69
- testID={'image-attachment-upload-preview-image'}
70
- >
64
+ <View style={[styles.image, upload]}>
65
+ <Image
66
+ onError={onErrorHandler}
67
+ onLoadEnd={onLoadEndHandler}
68
+ source={{ uri: attachment.localMetadata.previewUri ?? attachment.image_url }}
69
+ style={StyleSheet.absoluteFillObject}
70
+ testID={'image-attachment-upload-preview-image'}
71
+ />
71
72
  {indicatorType === ProgressIndicatorTypes.IN_PROGRESS && <ImageUploadInProgressIndicator />}
72
73
  {indicatorType === ProgressIndicatorTypes.RETRY && (
73
74
  <ImageUploadRetryIndicator onRetryHandler={onRetryHandler} />
@@ -75,7 +76,7 @@ export const ImageAttachmentUploadPreview = ({
75
76
  {indicatorType === ProgressIndicatorTypes.NOT_SUPPORTED && (
76
77
  <ImageUploadNotSupportedIndicator />
77
78
  )}
78
- </ImageBackground>
79
+ </View>
79
80
 
80
81
  <View style={styles.dismissWrapper}>
81
82
  <AttachmentRemoveControl onPress={onDismissHandler} />
@@ -21,10 +21,20 @@ export const VideoAttachmentUploadPreview = ({
21
21
  handleRetry,
22
22
  removeAttachments,
23
23
  }: VideoAttachmentUploadPreviewProps) => {
24
- return attachment.localMetadata.previewUri ? (
24
+ const previewUri = attachment.thumb_url ?? attachment.localMetadata.previewUri;
25
+
26
+ return previewUri ? (
25
27
  <>
26
28
  <ImageAttachmentUploadPreview
27
- attachment={attachment as unknown as LocalImageAttachment}
29
+ attachment={
30
+ {
31
+ ...attachment,
32
+ localMetadata: {
33
+ ...attachment.localMetadata,
34
+ previewUri,
35
+ },
36
+ } as unknown as LocalImageAttachment
37
+ }
28
38
  handleRetry={handleRetry}
29
39
  removeAttachments={removeAttachments}
30
40
  />
@@ -281,24 +281,32 @@ const useStyles = () => {
281
281
  return StyleSheet.create({
282
282
  scrollView: {
283
283
  flex: 1,
284
- padding: primitives.spacingMd,
285
284
  backgroundColor: semantics.backgroundCoreElevation1,
286
285
  },
287
- contentContainerStyle: { paddingBottom: 70 },
286
+ contentContainerStyle: {
287
+ alignItems: 'stretch',
288
+ padding: primitives.spacingMd,
289
+ paddingBottom: 70,
290
+ width: '100%',
291
+ },
288
292
  title: {
289
293
  color: semantics.textPrimary,
290
294
  fontSize: primitives.typographyFontSizeMd,
291
295
  fontWeight: primitives.typographyFontWeightSemiBold,
292
296
  lineHeight: primitives.typographyLineHeightNormal,
297
+ textAlign: 'left',
293
298
  },
294
299
  description: {
295
300
  color: semantics.textTertiary,
296
301
  fontSize: primitives.typographyFontSizeSm,
297
302
  fontWeight: primitives.typographyFontWeightRegular,
298
303
  lineHeight: primitives.typographyLineHeightNormal,
304
+ textAlign: 'left',
299
305
  },
300
306
  optionCardContent: {
301
307
  gap: primitives.spacingXxs,
308
+ flex: 1,
309
+ alignItems: 'flex-start',
302
310
  },
303
311
  optionCard: {
304
312
  flex: 1,
@@ -119,12 +119,14 @@ const useStyles = () => {
119
119
  fontSize: primitives.typographyFontSizeSm,
120
120
  fontWeight: primitives.typographyFontWeightRegular,
121
121
  lineHeight: primitives.typographyLineHeightTight,
122
+ textAlign: 'left',
122
123
  },
123
124
  headerTitle: {
124
125
  color: semantics.chatTextIncoming,
125
126
  fontSize: primitives.typographyFontSizeMd,
126
127
  fontWeight: primitives.typographyFontWeightSemiBold,
127
128
  lineHeight: primitives.typographyLineHeightNormal,
129
+ textAlign: 'left',
128
130
  },
129
131
  optionsWrapper: {
130
132
  gap: primitives.spacingMd,
@@ -1,5 +1,13 @@
1
1
  import React, { useCallback, useEffect, useMemo, useRef } from 'react';
2
- import { LayoutChangeEvent, Pressable, StyleSheet, Text, TextInput, View } from 'react-native';
2
+ import {
3
+ I18nManager,
4
+ LayoutChangeEvent,
5
+ Pressable,
6
+ StyleSheet,
7
+ Text,
8
+ TextInput,
9
+ View,
10
+ } from 'react-native';
3
11
  import { Gesture, GestureDetector } from 'react-native-gesture-handler';
4
12
  import Animated, {
5
13
  interpolate,
@@ -746,6 +754,7 @@ const useStyles = () => {
746
754
  fontWeight: primitives.typographyFontWeightRegular,
747
755
  color: semantics.inputTextDefault,
748
756
  paddingVertical: 0, // android is adding extra padding so we remove it
757
+ textAlign: I18nManager.isRTL ? 'right' : 'left',
749
758
  },
750
759
  optionValidationErrorContainer: {
751
760
  flexDirection: 'row',
@@ -759,6 +768,7 @@ const useStyles = () => {
759
768
  fontSize: primitives.typographyFontSizeSm,
760
769
  lineHeight: primitives.typographyLineHeightNormal,
761
770
  fontWeight: primitives.typographyFontWeightRegular,
771
+ textAlign: 'left',
762
772
  },
763
773
  optionWrapper: {
764
774
  width: '100%',
@@ -778,6 +788,7 @@ const useStyles = () => {
778
788
  fontSize: primitives.typographyFontSizeMd,
779
789
  fontWeight: primitives.typographyFontWeightSemiBold,
780
790
  lineHeight: primitives.typographyLineHeightNormal,
791
+ textAlign: 'left',
781
792
  },
782
793
  });
783
794
  }, [semantics]);
@@ -74,15 +74,19 @@ const useStyles = () => {
74
74
  fontSize: primitives.typographyFontSizeMd,
75
75
  fontWeight: primitives.typographyFontWeightSemiBold,
76
76
  lineHeight: primitives.typographyLineHeightNormal,
77
+ textAlign: 'left',
77
78
  },
78
79
  description: {
79
80
  color: semantics.textTertiary,
80
81
  fontSize: primitives.typographyFontSizeSm,
81
82
  fontWeight: primitives.typographyFontWeightRegular,
82
83
  lineHeight: primitives.typographyLineHeightNormal,
84
+ textAlign: 'left',
83
85
  },
84
86
  optionCardContent: {
85
87
  gap: primitives.spacingXxs,
88
+ flex: 1,
89
+ alignItems: 'flex-start',
86
90
  },
87
91
  optionCard: {
88
92
  alignItems: 'center',