stream-chat-react-native-core 4.5.0 → 4.6.0

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 (156) hide show
  1. package/README.md +3 -3
  2. package/lib/commonjs/components/Attachment/FileIcon.js +9 -1
  3. package/lib/commonjs/components/Attachment/FileIcon.js.map +1 -1
  4. package/lib/commonjs/components/AttachmentPicker/AttachmentPicker.js +232 -48
  5. package/lib/commonjs/components/AttachmentPicker/AttachmentPicker.js.map +1 -1
  6. package/lib/commonjs/components/Channel/Channel.js.map +1 -1
  7. package/lib/commonjs/components/Chat/Chat.js.map +1 -1
  8. package/lib/commonjs/components/Message/Message.js.map +1 -1
  9. package/lib/commonjs/components/Message/MessageSimple/MessageContent.js +39 -21
  10. package/lib/commonjs/components/Message/MessageSimple/MessageContent.js.map +1 -1
  11. package/lib/commonjs/components/Message/MessageSimple/utils/renderText.md +1 -1
  12. package/lib/commonjs/components/MessageInput/FileUploadPreview.js +1 -1
  13. package/lib/commonjs/components/MessageInput/FileUploadPreview.js.map +1 -1
  14. package/lib/commonjs/components/MessageInput/MessageInput.js +129 -44
  15. package/lib/commonjs/components/MessageInput/MessageInput.js.map +1 -1
  16. package/lib/commonjs/components/Reply/Reply.js +25 -9
  17. package/lib/commonjs/components/Reply/Reply.js.map +1 -1
  18. package/lib/commonjs/contexts/attachmentPickerContext/AttachmentPickerContext.js +15 -8
  19. package/lib/commonjs/contexts/attachmentPickerContext/AttachmentPickerContext.js.map +1 -1
  20. package/lib/commonjs/contexts/channelContext/ChannelContext.js.map +1 -1
  21. package/lib/commonjs/contexts/channelContext/ChannelContext.md +1 -1
  22. package/lib/commonjs/contexts/channelsContext/ChannelsContext.js.map +1 -1
  23. package/lib/commonjs/contexts/channelsContext/ChannelsContext.md +1 -1
  24. package/lib/commonjs/contexts/chatContext/ChatContext.md +1 -1
  25. package/lib/commonjs/contexts/imageGalleryContext/ImageGalleryContext.md +1 -1
  26. package/lib/commonjs/contexts/keyboardContext/KeyboardContext.md +1 -1
  27. package/lib/commonjs/contexts/messageContext/MessageContext.js.map +1 -1
  28. package/lib/commonjs/contexts/messageContext/MessageContext.md +1 -1
  29. package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js +10 -7
  30. package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js.map +1 -1
  31. package/lib/commonjs/contexts/messageInputContext/MessageInputContext.md +1 -1
  32. package/lib/commonjs/contexts/messageInputContext/hooks/useMessageDetailsForState.js +16 -1
  33. package/lib/commonjs/contexts/messageInputContext/hooks/useMessageDetailsForState.js.map +1 -1
  34. package/lib/commonjs/contexts/messageOverlayContext/MessageOverlayContext.js.map +1 -1
  35. package/lib/commonjs/contexts/messagesContext/MessagesContext.js.map +1 -1
  36. package/lib/commonjs/contexts/messagesContext/MessagesContext.md +1 -1
  37. package/lib/commonjs/contexts/overlayContext/OverlayContext.md +1 -1
  38. package/lib/commonjs/contexts/paginatedMessageListContext/PaginatedMessageListContext.md +1 -1
  39. package/lib/commonjs/contexts/suggestionsContext/SuggestionsContext.md +1 -1
  40. package/lib/commonjs/contexts/themeContext/utils/theme.js +1 -0
  41. package/lib/commonjs/contexts/themeContext/utils/theme.js.map +1 -1
  42. package/lib/commonjs/contexts/typingContext/TypingContext.md +1 -1
  43. package/lib/commonjs/icons/Recorder.js +47 -0
  44. package/lib/commonjs/icons/Recorder.js.map +1 -0
  45. package/lib/commonjs/icons/Video.js +82 -0
  46. package/lib/commonjs/icons/Video.js.map +1 -0
  47. package/lib/commonjs/icons/index.js +26 -0
  48. package/lib/commonjs/icons/index.js.map +1 -1
  49. package/lib/commonjs/native.js.map +1 -1
  50. package/lib/commonjs/version.json +1 -1
  51. package/lib/module/components/Attachment/FileIcon.js +9 -1
  52. package/lib/module/components/Attachment/FileIcon.js.map +1 -1
  53. package/lib/module/components/AttachmentPicker/AttachmentPicker.js +232 -48
  54. package/lib/module/components/AttachmentPicker/AttachmentPicker.js.map +1 -1
  55. package/lib/module/components/Channel/Channel.js.map +1 -1
  56. package/lib/module/components/Chat/Chat.js.map +1 -1
  57. package/lib/module/components/Message/Message.js.map +1 -1
  58. package/lib/module/components/Message/MessageSimple/MessageContent.js +39 -21
  59. package/lib/module/components/Message/MessageSimple/MessageContent.js.map +1 -1
  60. package/lib/module/components/Message/MessageSimple/utils/renderText.md +1 -1
  61. package/lib/module/components/MessageInput/FileUploadPreview.js +1 -1
  62. package/lib/module/components/MessageInput/FileUploadPreview.js.map +1 -1
  63. package/lib/module/components/MessageInput/MessageInput.js +129 -44
  64. package/lib/module/components/MessageInput/MessageInput.js.map +1 -1
  65. package/lib/module/components/Reply/Reply.js +25 -9
  66. package/lib/module/components/Reply/Reply.js.map +1 -1
  67. package/lib/module/contexts/attachmentPickerContext/AttachmentPickerContext.js +15 -8
  68. package/lib/module/contexts/attachmentPickerContext/AttachmentPickerContext.js.map +1 -1
  69. package/lib/module/contexts/channelContext/ChannelContext.js.map +1 -1
  70. package/lib/module/contexts/channelContext/ChannelContext.md +1 -1
  71. package/lib/module/contexts/channelsContext/ChannelsContext.js.map +1 -1
  72. package/lib/module/contexts/channelsContext/ChannelsContext.md +1 -1
  73. package/lib/module/contexts/chatContext/ChatContext.md +1 -1
  74. package/lib/module/contexts/imageGalleryContext/ImageGalleryContext.md +1 -1
  75. package/lib/module/contexts/keyboardContext/KeyboardContext.md +1 -1
  76. package/lib/module/contexts/messageContext/MessageContext.js.map +1 -1
  77. package/lib/module/contexts/messageContext/MessageContext.md +1 -1
  78. package/lib/module/contexts/messageInputContext/MessageInputContext.js +10 -7
  79. package/lib/module/contexts/messageInputContext/MessageInputContext.js.map +1 -1
  80. package/lib/module/contexts/messageInputContext/MessageInputContext.md +1 -1
  81. package/lib/module/contexts/messageInputContext/hooks/useMessageDetailsForState.js +16 -1
  82. package/lib/module/contexts/messageInputContext/hooks/useMessageDetailsForState.js.map +1 -1
  83. package/lib/module/contexts/messageOverlayContext/MessageOverlayContext.js.map +1 -1
  84. package/lib/module/contexts/messagesContext/MessagesContext.js.map +1 -1
  85. package/lib/module/contexts/messagesContext/MessagesContext.md +1 -1
  86. package/lib/module/contexts/overlayContext/OverlayContext.md +1 -1
  87. package/lib/module/contexts/paginatedMessageListContext/PaginatedMessageListContext.md +1 -1
  88. package/lib/module/contexts/suggestionsContext/SuggestionsContext.md +1 -1
  89. package/lib/module/contexts/themeContext/utils/theme.js +1 -0
  90. package/lib/module/contexts/themeContext/utils/theme.js.map +1 -1
  91. package/lib/module/contexts/typingContext/TypingContext.md +1 -1
  92. package/lib/module/icons/Recorder.js +47 -0
  93. package/lib/module/icons/Recorder.js.map +1 -0
  94. package/lib/module/icons/Video.js +82 -0
  95. package/lib/module/icons/Video.js.map +1 -0
  96. package/lib/module/icons/index.js +26 -0
  97. package/lib/module/icons/index.js.map +1 -1
  98. package/lib/module/native.js.map +1 -1
  99. package/lib/module/version.json +1 -1
  100. package/lib/typescript/components/AttachmentPicker/AttachmentPicker.d.ts +6 -6
  101. package/lib/typescript/components/Channel/Channel.d.ts +2 -2
  102. package/lib/typescript/components/Chat/Chat.d.ts +1 -1
  103. package/lib/typescript/components/Message/Message.d.ts +2 -2
  104. package/lib/typescript/components/MessageInput/MessageInput.d.ts +1 -1
  105. package/lib/typescript/contexts/attachmentPickerContext/AttachmentPickerContext.d.ts +9 -8
  106. package/lib/typescript/contexts/channelContext/ChannelContext.d.ts +2 -2
  107. package/lib/typescript/contexts/channelsContext/ChannelsContext.d.ts +6 -6
  108. package/lib/typescript/contexts/messageContext/MessageContext.d.ts +2 -2
  109. package/lib/typescript/contexts/messageInputContext/MessageInputContext.d.ts +10 -21
  110. package/lib/typescript/contexts/messageOverlayContext/MessageOverlayContext.d.ts +6 -6
  111. package/lib/typescript/contexts/messagesContext/MessagesContext.d.ts +20 -20
  112. package/lib/typescript/contexts/themeContext/utils/theme.d.ts +2 -1
  113. package/lib/typescript/icons/Recorder.d.ts +3 -0
  114. package/lib/typescript/icons/Video.d.ts +3 -0
  115. package/lib/typescript/icons/index.d.ts +2 -0
  116. package/lib/typescript/native.d.ts +3 -14
  117. package/lib/typescript/types/types.d.ts +24 -0
  118. package/package.json +1 -1
  119. package/src/components/Attachment/FileIcon.tsx +45 -0
  120. package/src/components/AttachmentPicker/AttachmentPicker.tsx +210 -60
  121. package/src/components/Channel/Channel.tsx +2 -2
  122. package/src/components/Chat/Chat.tsx +1 -1
  123. package/src/components/Message/Message.tsx +2 -2
  124. package/src/components/Message/MessageSimple/MessageContent.tsx +23 -12
  125. package/src/components/Message/MessageSimple/utils/renderText.md +1 -1
  126. package/src/components/MessageInput/FileUploadPreview.tsx +1 -1
  127. package/src/components/MessageInput/MessageInput.tsx +106 -19
  128. package/src/components/Reply/Reply.tsx +31 -14
  129. package/src/components/Thread/__tests__/__snapshots__/Thread.test.js.snap +12 -0
  130. package/src/contexts/attachmentPickerContext/AttachmentPickerContext.tsx +12 -8
  131. package/src/contexts/channelContext/ChannelContext.md +1 -1
  132. package/src/contexts/channelContext/ChannelContext.tsx +2 -2
  133. package/src/contexts/channelsContext/ChannelsContext.md +1 -1
  134. package/src/contexts/channelsContext/ChannelsContext.tsx +6 -6
  135. package/src/contexts/chatContext/ChatContext.md +1 -1
  136. package/src/contexts/imageGalleryContext/ImageGalleryContext.md +1 -1
  137. package/src/contexts/keyboardContext/KeyboardContext.md +1 -1
  138. package/src/contexts/messageContext/MessageContext.md +1 -1
  139. package/src/contexts/messageContext/MessageContext.tsx +2 -2
  140. package/src/contexts/messageInputContext/MessageInputContext.md +1 -1
  141. package/src/contexts/messageInputContext/MessageInputContext.tsx +23 -30
  142. package/src/contexts/messageInputContext/hooks/useMessageDetailsForState.ts +17 -1
  143. package/src/contexts/messageOverlayContext/MessageOverlayContext.tsx +6 -6
  144. package/src/contexts/messagesContext/MessagesContext.md +1 -1
  145. package/src/contexts/messagesContext/MessagesContext.tsx +20 -20
  146. package/src/contexts/overlayContext/OverlayContext.md +1 -1
  147. package/src/contexts/paginatedMessageListContext/PaginatedMessageListContext.md +1 -1
  148. package/src/contexts/suggestionsContext/SuggestionsContext.md +1 -1
  149. package/src/contexts/themeContext/utils/theme.ts +3 -1
  150. package/src/contexts/typingContext/TypingContext.md +1 -1
  151. package/src/icons/Recorder.tsx +16 -0
  152. package/src/icons/Video.tsx +37 -0
  153. package/src/icons/index.ts +2 -0
  154. package/src/native.ts +4 -14
  155. package/src/types/types.ts +27 -0
  156. package/src/version.json +1 -1
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useMemo, useState } from 'react';
1
+ import React, { useEffect, useMemo, useRef, useState } from 'react';
2
2
  import {
3
3
  BackHandler,
4
4
  Dimensions,
@@ -7,6 +7,7 @@ import {
7
7
  Platform,
8
8
  StatusBar,
9
9
  StyleSheet,
10
+ Text,
10
11
  View,
11
12
  } from 'react-native';
12
13
 
@@ -15,42 +16,161 @@ import BottomSheet, {
15
16
  BottomSheetHandleProps,
16
17
  TouchableOpacity,
17
18
  } from '@gorhom/bottom-sheet';
19
+ import dayjs from 'dayjs';
20
+ import duration from 'dayjs/plugin/duration';
21
+ import { lookup } from 'mime-types';
18
22
 
19
23
  import type { AttachmentPickerErrorProps } from './components/AttachmentPickerError';
20
24
 
21
25
  import { useAttachmentPickerContext } from '../../contexts/attachmentPickerContext/AttachmentPickerContext';
22
26
  import { useTheme } from '../../contexts/themeContext/ThemeContext';
23
- import { Asset, getPhotos } from '../../native';
27
+ import { Recorder } from '../../icons';
28
+ import { getPhotos } from '../../native';
29
+ import type { Asset, File } from '../../types/types';
24
30
  import { vh, vw } from '../../utils/utils';
25
31
 
32
+ dayjs.extend(duration);
33
+
26
34
  const styles = StyleSheet.create({
27
35
  container: {
28
36
  flexGrow: 1,
29
37
  },
38
+ durationText: {
39
+ fontWeight: 'bold',
40
+ },
30
41
  overlay: {
31
42
  alignItems: 'flex-end',
32
43
  flex: 1,
33
44
  },
45
+ videoView: {
46
+ bottom: 5,
47
+ display: 'flex',
48
+ flexDirection: 'row',
49
+ justifyContent: 'space-between',
50
+ paddingHorizontal: 5,
51
+ position: 'absolute',
52
+ width: '100%',
53
+ },
34
54
  });
35
55
 
36
56
  const screenHeight = vh(100);
37
57
  const fullScreenHeight = Dimensions.get('window').height;
38
58
 
39
59
  type AttachmentImageProps = {
60
+ asset: Asset;
40
61
  ImageOverlaySelectedComponent: React.ComponentType;
41
- onPress: () => void;
62
+ maxNumberOfFiles: number;
42
63
  selected: boolean;
43
- uri: string;
64
+ setSelectedImages: React.Dispatch<React.SetStateAction<Asset[]>>;
44
65
  numberOfAttachmentPickerImageColumns?: number;
45
66
  };
46
67
 
68
+ type AttachmentVideoProps = {
69
+ asset: Asset;
70
+ ImageOverlaySelectedComponent: React.ComponentType;
71
+ maxNumberOfFiles: number;
72
+ selected: boolean;
73
+ setSelectedFiles: React.Dispatch<React.SetStateAction<File[]>>;
74
+ numberOfAttachmentPickerImageColumns?: number;
75
+ };
76
+
77
+ const AttachmentVideo: React.FC<AttachmentVideoProps> = (props) => {
78
+ const {
79
+ asset,
80
+ ImageOverlaySelectedComponent,
81
+ maxNumberOfFiles,
82
+ numberOfAttachmentPickerImageColumns,
83
+ selected,
84
+ setSelectedFiles,
85
+ } = props;
86
+
87
+ const {
88
+ theme: {
89
+ attachmentPicker: { durationText, image, imageOverlay },
90
+ colors: { overlay, white },
91
+ },
92
+ } = useTheme();
93
+
94
+ const { duration, playableDuration, uri } = asset;
95
+
96
+ const videoDuration = duration ? duration : playableDuration;
97
+
98
+ const ONE_HOUR_IN_SECONDS = 3600;
99
+
100
+ let durationLabel = '00:00';
101
+
102
+ if (videoDuration) {
103
+ const isDurationLongerThanHour = videoDuration / ONE_HOUR_IN_SECONDS >= 1;
104
+ const formattedDurationParam = isDurationLongerThanHour ? 'HH:mm:ss' : 'mm:ss';
105
+ const formattedVideoDuration = dayjs
106
+ .duration(videoDuration, 'second')
107
+ .format(formattedDurationParam);
108
+ durationLabel = formattedVideoDuration;
109
+ }
110
+
111
+ const size = vw(100) / (numberOfAttachmentPickerImageColumns || 3) - 2;
112
+
113
+ const onPressVideo = () => {
114
+ if (selected) {
115
+ setSelectedFiles((files) => files.filter((file) => file.uri !== asset.uri));
116
+ } else {
117
+ setSelectedFiles((files) => {
118
+ if (files.length >= maxNumberOfFiles) {
119
+ return files;
120
+ }
121
+ return [
122
+ ...files,
123
+ {
124
+ duration: durationLabel,
125
+ name: asset.filename,
126
+ size: asset.fileSize,
127
+ type: 'video',
128
+ uri: asset.uri,
129
+ },
130
+ ];
131
+ });
132
+ }
133
+ };
134
+
135
+ return (
136
+ <TouchableOpacity onPress={onPressVideo}>
137
+ <ImageBackground
138
+ source={{ uri }}
139
+ style={[
140
+ {
141
+ height: size,
142
+ margin: 1,
143
+ width: size,
144
+ },
145
+ image,
146
+ ]}
147
+ >
148
+ {selected && (
149
+ <View style={[styles.overlay, { backgroundColor: overlay }, imageOverlay]}>
150
+ <ImageOverlaySelectedComponent />
151
+ </View>
152
+ )}
153
+ <View style={styles.videoView}>
154
+ <Recorder height={20} pathFill={white} width={25} />
155
+ {videoDuration ? (
156
+ <Text style={[styles.durationText, durationText, { color: white }]}>
157
+ {durationLabel}
158
+ </Text>
159
+ ) : null}
160
+ </View>
161
+ </ImageBackground>
162
+ </TouchableOpacity>
163
+ );
164
+ };
165
+
47
166
  const AttachmentImage: React.FC<AttachmentImageProps> = (props) => {
48
167
  const {
168
+ asset,
49
169
  ImageOverlaySelectedComponent,
170
+ maxNumberOfFiles,
50
171
  numberOfAttachmentPickerImageColumns,
51
- onPress,
52
172
  selected,
53
- uri,
173
+ setSelectedImages,
54
174
  } = props;
55
175
  const {
56
176
  theme: {
@@ -61,8 +181,23 @@ const AttachmentImage: React.FC<AttachmentImageProps> = (props) => {
61
181
 
62
182
  const size = vw(100) / (numberOfAttachmentPickerImageColumns || 3) - 2;
63
183
 
184
+ const { uri } = asset;
185
+
186
+ const onPressImage = () => {
187
+ if (selected) {
188
+ setSelectedImages((images) => images.filter((image) => image.uri !== asset.uri));
189
+ } else {
190
+ setSelectedImages((images) => {
191
+ if (images.length >= maxNumberOfFiles) {
192
+ return images;
193
+ }
194
+ return [...images, asset];
195
+ });
196
+ }
197
+ };
198
+
64
199
  return (
65
- <TouchableOpacity onPress={onPress}>
200
+ <TouchableOpacity onPress={onPressImage}>
66
201
  <ImageBackground
67
202
  source={{ uri }}
68
203
  style={[
@@ -92,6 +227,7 @@ const renderImage = ({
92
227
  ImageOverlaySelectedComponent: React.ComponentType;
93
228
  maxNumberOfFiles: number;
94
229
  selected: boolean;
230
+ setSelectedFiles: React.Dispatch<React.SetStateAction<File[]>>;
95
231
  setSelectedImages: React.Dispatch<React.SetStateAction<Asset[]>>;
96
232
  numberOfAttachmentPickerImageColumns?: number;
97
233
  };
@@ -102,55 +238,64 @@ const renderImage = ({
102
238
  maxNumberOfFiles,
103
239
  numberOfAttachmentPickerImageColumns,
104
240
  selected,
241
+ setSelectedFiles,
105
242
  setSelectedImages,
106
243
  } = item;
107
- const onPress = () => {
108
- if (selected) {
109
- setSelectedImages((images) => images.filter((image) => image.uri !== asset.uri));
110
- } else {
111
- setSelectedImages((images) => {
112
- if (images.length >= maxNumberOfFiles) {
113
- return images;
114
- }
115
- return [...images, asset];
116
- });
117
- }
118
- };
119
244
 
120
- return (
245
+ const contentType = lookup(asset.filename) || 'multipart/form-data';
246
+
247
+ const fileType = asset.filename
248
+ ? contentType.startsWith('image/')
249
+ ? 'image'
250
+ : 'video'
251
+ : asset.type === 'video'
252
+ ? 'video'
253
+ : 'image';
254
+
255
+ return fileType === 'image' ? (
121
256
  <AttachmentImage
257
+ asset={asset}
258
+ ImageOverlaySelectedComponent={ImageOverlaySelectedComponent}
259
+ maxNumberOfFiles={maxNumberOfFiles}
260
+ numberOfAttachmentPickerImageColumns={numberOfAttachmentPickerImageColumns}
261
+ selected={selected}
262
+ setSelectedImages={setSelectedImages}
263
+ />
264
+ ) : (
265
+ <AttachmentVideo
266
+ asset={asset}
122
267
  ImageOverlaySelectedComponent={ImageOverlaySelectedComponent}
268
+ maxNumberOfFiles={maxNumberOfFiles}
123
269
  numberOfAttachmentPickerImageColumns={numberOfAttachmentPickerImageColumns}
124
- onPress={onPress}
125
270
  selected={selected}
126
- uri={asset.uri}
271
+ setSelectedFiles={setSelectedFiles}
127
272
  />
128
273
  );
129
274
  };
130
275
 
131
276
  export type AttachmentPickerProps = {
132
277
  /**
133
- * Custom UI component to render [draggable handle](https://github.com/GetStream/stream-chat-react-native/blob/master/screenshots/docs/1.png) of attachment picker.
278
+ * Custom UI component to render [draggable handle](https://github.com/GetStream/stream-chat-react-native/blob/main/screenshots/docs/1.png) of attachment picker.
134
279
  *
135
- * **Default** [AttachmentPickerBottomSheetHandle](https://github.com/GetStream/stream-chat-react-native/blob/master/package/src/components/AttachmentPicker/components/AttachmentPickerBottomSheetHandle.tsx)
280
+ * **Default** [AttachmentPickerBottomSheetHandle](https://github.com/GetStream/stream-chat-react-native/blob/main/package/src/components/AttachmentPicker/components/AttachmentPickerBottomSheetHandle.tsx)
136
281
  */
137
282
  AttachmentPickerBottomSheetHandle: React.FC<BottomSheetHandleProps>;
138
283
  /**
139
284
  * Custom UI component to render error component while opening attachment picker.
140
285
  *
141
- * **Default** [AttachmentPickerError](https://github.com/GetStream/stream-chat-react-native/blob/master/package/src/components/AttachmentPicker/components/AttachmentPickerError.tsx)
286
+ * **Default** [AttachmentPickerError](https://github.com/GetStream/stream-chat-react-native/blob/main/package/src/components/AttachmentPicker/components/AttachmentPickerError.tsx)
142
287
  */
143
288
  AttachmentPickerError: React.ComponentType<AttachmentPickerErrorProps>;
144
289
  /**
145
290
  * Custom UI component to render error image for attachment picker
146
291
  *
147
- * **Default** [AttachmentPickerErrorImage](https://github.com/GetStream/stream-chat-react-native/blob/master/package/src/components/AttachmentPicker/components/AttachmentPickerErrorImage.tsx)
292
+ * **Default** [AttachmentPickerErrorImage](https://github.com/GetStream/stream-chat-react-native/blob/main/package/src/components/AttachmentPicker/components/AttachmentPickerErrorImage.tsx)
148
293
  */
149
294
  AttachmentPickerErrorImage: React.ComponentType;
150
295
  /**
151
- * Custom UI component to render overlay component, that shows up on top of [selected image](https://github.com/GetStream/stream-chat-react-native/blob/master/screenshots/docs/1.png) (with tick mark)
296
+ * Custom UI component to render overlay component, that shows up on top of [selected image](https://github.com/GetStream/stream-chat-react-native/blob/main/screenshots/docs/1.png) (with tick mark)
152
297
  *
153
- * **Default** [ImageOverlaySelectedComponent](https://github.com/GetStream/stream-chat-react-native/blob/master/package/src/components/AttachmentPicker/components/ImageOverlaySelectedComponent.tsx)
298
+ * **Default** [ImageOverlaySelectedComponent](https://github.com/GetStream/stream-chat-react-native/blob/main/package/src/components/AttachmentPicker/components/ImageOverlaySelectedComponent.tsx)
154
299
  */
155
300
  ImageOverlaySelectedComponent: React.ComponentType;
156
301
  attachmentPickerBottomSheetHandleHeight?: number;
@@ -187,8 +332,10 @@ export const AttachmentPicker = React.forwardRef(
187
332
  const {
188
333
  closePicker,
189
334
  maxNumberOfFiles,
335
+ selectedFiles,
190
336
  selectedImages,
191
337
  selectedPicker,
338
+ setSelectedFiles,
192
339
  setSelectedImages,
193
340
  setSelectedPicker,
194
341
  topInset,
@@ -200,13 +347,7 @@ export const AttachmentPicker = React.forwardRef(
200
347
  const [hasNextPage, setHasNextPage] = useState(true);
201
348
  const [loadingPhotos, setLoadingPhotos] = useState(false);
202
349
  const [photos, setPhotos] = useState<Asset[]>([]);
203
-
204
- const hideAttachmentPicker = () => {
205
- setSelectedPicker(undefined);
206
- if ((ref as React.MutableRefObject<BottomSheet>)?.current) {
207
- (ref as React.MutableRefObject<BottomSheet>).current.close();
208
- }
209
- };
350
+ const bottomSheetCloseOnKeyboardShowTimeout = useRef<NodeJS.Timeout>();
210
351
 
211
352
  const getMorePhotos = async () => {
212
353
  if (hasNextPage && !loadingPhotos && currentIndex > -1 && selectedPicker === 'images') {
@@ -248,6 +389,20 @@ export const AttachmentPicker = React.forwardRef(
248
389
  }, [selectedPicker]);
249
390
 
250
391
  useEffect(() => {
392
+ const hideAttachmentPicker = () => {
393
+ if (bottomSheetCloseOnKeyboardShowTimeout.current) {
394
+ clearTimeout(bottomSheetCloseOnKeyboardShowTimeout.current);
395
+ }
396
+ setSelectedPicker(undefined);
397
+ // This short timeout is to prevent a race condition
398
+ // where the close function is called during the point when a internal container layout happens within the bottomsheet due to keyboard affecting the layout
399
+ // If the container layout measures a shorter height than previous but if the close snapped to the previous height's position, the bottom sheet will show up
400
+ // this short delay ensures that close function is always called after a container layout due to keyboard change
401
+ bottomSheetCloseOnKeyboardShowTimeout.current = setTimeout(
402
+ () => (ref as React.MutableRefObject<BottomSheet | undefined>).current?.close(),
403
+ 150,
404
+ );
405
+ };
251
406
  const keyboardSubscription =
252
407
  Platform.OS === 'ios'
253
408
  ? Keyboard.addListener('keyboardWillShow', hideAttachmentPicker)
@@ -265,6 +420,9 @@ export const AttachmentPicker = React.forwardRef(
265
420
  } else {
266
421
  Keyboard.removeListener('keyboardDidShow', hideAttachmentPicker);
267
422
  }
423
+ if (bottomSheetCloseOnKeyboardShowTimeout.current) {
424
+ clearTimeout(bottomSheetCloseOnKeyboardShowTimeout.current);
425
+ }
268
426
  };
269
427
  }, []);
270
428
 
@@ -295,7 +453,10 @@ export const AttachmentPicker = React.forwardRef(
295
453
  ImageOverlaySelectedComponent,
296
454
  maxNumberOfFiles,
297
455
  numberOfAttachmentPickerImageColumns,
298
- selected: selectedImages.some((image) => image.uri === asset.uri),
456
+ selected:
457
+ selectedImages.some((image) => image.uri === asset.uri) ||
458
+ selectedFiles.some((file) => file.uri === asset.uri),
459
+ setSelectedFiles,
299
460
  setSelectedImages,
300
461
  }));
301
462
 
@@ -326,35 +487,24 @@ export const AttachmentPicker = React.forwardRef(
326
487
  : statusBarHeight
327
488
  : 0;
328
489
 
329
- const initialSnapPoint = useMemo(
330
- () =>
331
- attachmentPickerBottomSheetHeight ?? Platform.OS === 'android'
332
- ? 308 +
333
- (fullScreenHeight - screenHeight + androidBottomBarHeightAdjustment) -
334
- handleHeight
335
- : 308 + (fullScreenHeight - screenHeight + androidBottomBarHeightAdjustment),
336
- [
337
- attachmentPickerBottomSheetHeight,
338
- androidBottomBarHeightAdjustment,
339
- fullScreenHeight,
340
- handleHeight,
341
- screenHeight,
342
- ],
343
- );
490
+ const initialSnapPoint =
491
+ attachmentPickerBottomSheetHeight ?? Platform.OS === 'android'
492
+ ? 308 + (fullScreenHeight - screenHeight + androidBottomBarHeightAdjustment) - handleHeight
493
+ : 308 + (fullScreenHeight - screenHeight + androidBottomBarHeightAdjustment);
344
494
 
345
- const finalSnapPoint = useMemo(
346
- () =>
347
- Platform.OS === 'android'
348
- ? fullScreenHeight - topInset - handleHeight
349
- : fullScreenHeight - topInset,
350
- [fullScreenHeight, handleHeight, topInset],
351
- );
495
+ const finalSnapPoint =
496
+ Platform.OS === 'android'
497
+ ? fullScreenHeight - topInset - handleHeight
498
+ : fullScreenHeight - topInset;
352
499
 
353
500
  /**
354
501
  * Snap points changing cause a rerender of the position,
355
502
  * this is an issue if you are calling close on the bottom sheet.
356
503
  */
357
- const snapPoints = [initialSnapPoint, finalSnapPoint];
504
+ const snapPoints = useMemo(
505
+ () => [initialSnapPoint, finalSnapPoint],
506
+ [initialSnapPoint, finalSnapPoint],
507
+ );
358
508
 
359
509
  return (
360
510
  <>
@@ -370,7 +520,7 @@ export const AttachmentPicker = React.forwardRef(
370
520
  }
371
521
  handleHeight={handleHeight}
372
522
  index={-1}
373
- onChange={(index: number) => setCurrentIndex(index)}
523
+ onChange={setCurrentIndex}
374
524
  ref={ref}
375
525
  snapPoints={snapPoints}
376
526
  >
@@ -304,7 +304,7 @@ export type ChannelPropsWithContext<
304
304
  /**
305
305
  * When true, disables the KeyboardCompatibleView wrapper
306
306
  *
307
- * Channel internally uses the [KeyboardCompatibleView](https://github.com/GetStream/stream-chat-react-native/blob/master/package/src/components/KeyboardCompatibleView/KeyboardCompatibleView.tsx)
307
+ * Channel internally uses the [KeyboardCompatibleView](https://github.com/GetStream/stream-chat-react-native/blob/main/package/src/components/KeyboardCompatibleView/KeyboardCompatibleView.tsx)
308
308
  * component to adjust the height of Channel when the keyboard is opened or dismissed. This prop provides the ability to disable this functionality in case you
309
309
  * want to use [KeyboardAvoidingView](https://facebook.github.io/react-native/docs/keyboardavoidingview) or handle dismissal yourself.
310
310
  * KeyboardAvoidingView works well when your component occupies 100% of screen height, otherwise it may raise some issues.
@@ -345,7 +345,7 @@ export type ChannelPropsWithContext<
345
345
  keyboardBehavior?: KeyboardAvoidingViewProps['behavior'];
346
346
  /**
347
347
  * Custom wrapper component that handles height adjustment of Channel component when keyboard is opened or dismissed
348
- * Default component (accepts the same props): [KeyboardCompatibleView](https://github.com/GetStream/stream-chat-react-native/blob/master/package/src/components/KeyboardCompatibleView/KeyboardCompatibleView.tsx)
348
+ * Default component (accepts the same props): [KeyboardCompatibleView](https://github.com/GetStream/stream-chat-react-native/blob/main/package/src/components/KeyboardCompatibleView/KeyboardCompatibleView.tsx)
349
349
  *
350
350
  * **Example:**
351
351
  *
@@ -91,7 +91,7 @@ export type ChatProps<
91
91
  */
92
92
  i18nInstance?: Streami18n;
93
93
  /**
94
- * You can pass the theme object to customize the styles of Chat components. You can check the default theme in [theme.ts](https://github.com/GetStream/stream-chat-react-native/blob/master/package/src/contexts/themeContext/utils/theme.ts)
94
+ * You can pass the theme object to customize the styles of Chat components. You can check the default theme in [theme.ts](https://github.com/GetStream/stream-chat-react-native/blob/main/package/src/contexts/themeContext/utils/theme.ts)
95
95
  *
96
96
  * Please check section about [themes in cookbook](https://github.com/GetStream/stream-chat-react-native/wiki/Cookbook-v3.0#theme) for details.
97
97
  *
@@ -160,7 +160,7 @@ export type MessagePropsWithContext<
160
160
  * You can call methods available on the Message
161
161
  * component such as handleEdit, handleDelete, handleAction etc.
162
162
  *
163
- * Source - [Message](https://github.com/GetStream/stream-chat-react-native/blob/master/package/src/components/Message/Message.tsx)
163
+ * Source - [Message](https://github.com/GetStream/stream-chat-react-native/blob/main/package/src/components/Message/Message.tsx)
164
164
  *
165
165
  * By default, we show the overlay with all the message actions on long press.
166
166
  *
@@ -173,7 +173,7 @@ export type MessagePropsWithContext<
173
173
  * You can call methods available on the Message
174
174
  * component such as handleEdit, handleDelete, handleAction etc.
175
175
  *
176
- * Source - [Message](https://github.com/GetStream/stream-chat-react-native/blob/master/package/src/components/Message/Message.tsx)
176
+ * Source - [Message](https://github.com/GetStream/stream-chat-react-native/blob/main/package/src/components/Message/Message.tsx)
177
177
  *
178
178
  * By default, we will dismiss the keyboard on press.
179
179
  *
@@ -143,7 +143,7 @@ const MessageContentWithContext = <
143
143
  colors: { accent_red, blue_alice, grey_gainsboro, grey_whisper, transparent, white },
144
144
  messageSimple: {
145
145
  content: {
146
- container: { borderRadiusL, borderRadiusS, ...container },
146
+ container: { borderRadius, borderRadiusL, borderRadiusS, ...container },
147
147
  containerInner,
148
148
  errorContainer,
149
149
  errorIcon,
@@ -223,6 +223,22 @@ const MessageContentWithContext = <
223
223
 
224
224
  const repliesCurveColor = isMyMessage && !error ? backgroundColor : grey_whisper;
225
225
 
226
+ const isBorderColor = isMyMessage && !error;
227
+
228
+ const shouldApplyBorderRadius = (firstGroupStyle: string, secondGroupStyle: string): boolean =>
229
+ (groupStyle === firstGroupStyle || groupStyle === secondGroupStyle) &&
230
+ (!hasThreadReplies || threadList);
231
+
232
+ const applyBorderRadius = (
233
+ firstGroupStyle: string,
234
+ secondGroupStyle: string,
235
+ ): number | undefined => {
236
+ if (shouldApplyBorderRadius(firstGroupStyle, secondGroupStyle)) {
237
+ return borderRadiusS;
238
+ }
239
+ return borderRadiusL;
240
+ };
241
+
226
242
  return (
227
243
  <TouchableOpacity
228
244
  activeOpacity={0.7}
@@ -297,17 +313,12 @@ const MessageContentWithContext = <
297
313
  styles.containerInner,
298
314
  {
299
315
  backgroundColor,
300
- borderBottomLeftRadius:
301
- (groupStyle === 'left_bottom' || groupStyle === 'left_single') &&
302
- (!hasThreadReplies || threadList)
303
- ? borderRadiusS
304
- : borderRadiusL,
305
- borderBottomRightRadius:
306
- (groupStyle === 'right_bottom' || groupStyle === 'right_single') &&
307
- (!hasThreadReplies || threadList)
308
- ? borderRadiusS
309
- : borderRadiusL,
310
- borderColor: isMyMessage && !error ? backgroundColor : grey_whisper,
316
+ borderBottomLeftRadius: applyBorderRadius('left_bottom', 'left_single'),
317
+ borderBottomRightRadius: applyBorderRadius('right_bottom', 'right_single'),
318
+ borderColor: isBorderColor ? backgroundColor : grey_whisper,
319
+ borderRadius,
320
+ borderTopLeftRadius: applyBorderRadius('left_top', 'right_single'),
321
+ borderTopRightRadius: applyBorderRadius('right_top', 'right_single'),
311
322
  },
312
323
  noBorder ? { borderWidth: 0 } : {},
313
324
  containerInner,
@@ -1,4 +1,4 @@
1
- [src/components/Message/MessageSimple/utils/renderText.ts](https://github.com/GetStream/stream-chat-react-native/blob/master/package/src/components/Message/MessageSimple/utils/renderText.ts)
1
+ [src/components/Message/MessageSimple/utils/renderText.ts](https://github.com/GetStream/stream-chat-react-native/blob/main/package/src/components/Message/MessageSimple/utils/renderText.ts)
2
2
 
3
3
  Function that handles markdown and parsing for the text of message.
4
4
 
@@ -105,7 +105,7 @@ const UnsupportedFileTypeOrFileSizeIndicator = ({
105
105
  </View>
106
106
  ) : (
107
107
  <Text style={[styles.fileSizeText, { color: grey }, fileSizeText]}>
108
- {getFileSizeDisplayText(item.file.size)}
108
+ {item.file.duration ? item.file.duration : getFileSizeDisplayText(item.file.size)}
109
109
  </Text>
110
110
  );
111
111
  };