stream-chat-react-native-core 4.11.1-beta.4 → 4.12.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/lib/commonjs/components/Attachment/AudioAttachment.js +541 -0
  2. package/lib/commonjs/components/Attachment/AudioAttachment.js.map +1 -0
  3. package/lib/commonjs/components/Attachment/FileAttachmentGroup.js +113 -10
  4. package/lib/commonjs/components/Attachment/FileAttachmentGroup.js.map +1 -1
  5. package/lib/commonjs/components/Attachment/Gallery.js +2 -2
  6. package/lib/commonjs/components/Attachment/Gallery.js.map +1 -1
  7. package/lib/commonjs/components/Attachment/{GallaryImage.js → GalleryImage.js} +3 -3
  8. package/lib/{module/components/Attachment/GallaryImage.js.map → commonjs/components/Attachment/GalleryImage.js.map} +1 -1
  9. package/lib/commonjs/components/Attachment/Giphy.js +1 -1
  10. package/lib/commonjs/components/Attachment/Giphy.js.map +1 -1
  11. package/lib/commonjs/components/Attachment/VideoThumbnail.js +10 -5
  12. package/lib/commonjs/components/Attachment/VideoThumbnail.js.map +1 -1
  13. package/lib/commonjs/components/Avatar/Avatar.js +1 -1
  14. package/lib/commonjs/components/Avatar/Avatar.js.map +1 -1
  15. package/lib/commonjs/components/Avatar/GroupAvatar.js +1 -1
  16. package/lib/commonjs/components/Avatar/GroupAvatar.js.map +1 -1
  17. package/lib/commonjs/components/Channel/Channel.js +18 -13
  18. package/lib/commonjs/components/Channel/Channel.js.map +1 -1
  19. package/lib/commonjs/components/Channel/hooks/useCreateInputMessageInputContext.js +2 -0
  20. package/lib/commonjs/components/Channel/hooks/useCreateInputMessageInputContext.js.map +1 -1
  21. package/lib/commonjs/components/ImageGallery/ImageGallery.js +37 -30
  22. package/lib/commonjs/components/ImageGallery/ImageGallery.js.map +1 -1
  23. package/lib/commonjs/components/ImageGallery/components/AnimatedGalleryImage.js +5 -3
  24. package/lib/commonjs/components/ImageGallery/components/AnimatedGalleryImage.js.map +1 -1
  25. package/lib/commonjs/components/ImageGallery/components/AnimatedGalleryVideo.js +10 -6
  26. package/lib/commonjs/components/ImageGallery/components/AnimatedGalleryVideo.js.map +1 -1
  27. package/lib/commonjs/components/ImageGallery/components/ImageGalleryFooter.js +17 -14
  28. package/lib/commonjs/components/ImageGallery/components/ImageGalleryFooter.js.map +1 -1
  29. package/lib/commonjs/components/ImageGallery/components/ImageGalleryHeader.js +1 -0
  30. package/lib/commonjs/components/ImageGallery/components/ImageGalleryHeader.js.map +1 -1
  31. package/lib/commonjs/components/ImageGallery/components/ImageGalleryOverlay.js +2 -1
  32. package/lib/commonjs/components/ImageGallery/components/ImageGalleryOverlay.js.map +1 -1
  33. package/lib/commonjs/components/ImageGallery/components/ImageGalleryVideoControl.js +37 -18
  34. package/lib/commonjs/components/ImageGallery/components/ImageGalleryVideoControl.js.map +1 -1
  35. package/lib/commonjs/components/ImageGallery/components/ImageGrid.js +3 -1
  36. package/lib/commonjs/components/ImageGallery/components/ImageGrid.js.map +1 -1
  37. package/lib/commonjs/components/ImageGallery/components/ImageGridHandle.js +5 -4
  38. package/lib/commonjs/components/ImageGallery/components/ImageGridHandle.js.map +1 -1
  39. package/lib/commonjs/components/Message/Message.js +8 -6
  40. package/lib/commonjs/components/Message/Message.js.map +1 -1
  41. package/lib/commonjs/components/Message/MessageSimple/utils/parseLinks.js +1 -1
  42. package/lib/commonjs/components/Message/MessageSimple/utils/parseLinks.js.map +1 -1
  43. package/lib/commonjs/components/Message/MessageSimple/utils/parseLinks.test.js +1 -1
  44. package/lib/commonjs/components/Message/MessageSimple/utils/parseLinks.test.js.map +1 -1
  45. package/lib/commonjs/components/MessageInput/FileUploadPreview.js +125 -35
  46. package/lib/commonjs/components/MessageInput/FileUploadPreview.js.map +1 -1
  47. package/lib/{module/components/ImageGallery/components → commonjs/components/ProgressControl}/ProgressControl.js +37 -24
  48. package/lib/commonjs/components/ProgressControl/ProgressControl.js.map +1 -0
  49. package/lib/commonjs/components/index.js +26 -0
  50. package/lib/commonjs/components/index.js.map +1 -1
  51. package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js +5 -2
  52. package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js.map +1 -1
  53. package/lib/commonjs/contexts/messageInputContext/hooks/useCreateMessageInputContext.js +7 -2
  54. package/lib/commonjs/contexts/messageInputContext/hooks/useCreateMessageInputContext.js.map +1 -1
  55. package/lib/commonjs/contexts/themeContext/utils/theme.js +9 -0
  56. package/lib/commonjs/contexts/themeContext/utils/theme.js.map +1 -1
  57. package/lib/commonjs/icons/Pause.js +2 -2
  58. package/lib/commonjs/icons/Pause.js.map +1 -1
  59. package/lib/commonjs/mock-builders/api/channelMocks.js +1 -0
  60. package/lib/commonjs/mock-builders/api/channelMocks.js.map +1 -1
  61. package/lib/commonjs/native.js +13 -5
  62. package/lib/commonjs/native.js.map +1 -1
  63. package/lib/commonjs/version.json +1 -1
  64. package/lib/module/components/Attachment/AudioAttachment.js +541 -0
  65. package/lib/module/components/Attachment/AudioAttachment.js.map +1 -0
  66. package/lib/module/components/Attachment/FileAttachmentGroup.js +113 -10
  67. package/lib/module/components/Attachment/FileAttachmentGroup.js.map +1 -1
  68. package/lib/module/components/Attachment/Gallery.js +2 -2
  69. package/lib/module/components/Attachment/Gallery.js.map +1 -1
  70. package/lib/module/components/Attachment/{GallaryImage.js → GalleryImage.js} +3 -3
  71. package/lib/{commonjs/components/Attachment/GallaryImage.js.map → module/components/Attachment/GalleryImage.js.map} +1 -1
  72. package/lib/module/components/Attachment/Giphy.js +1 -1
  73. package/lib/module/components/Attachment/Giphy.js.map +1 -1
  74. package/lib/module/components/Attachment/VideoThumbnail.js +10 -5
  75. package/lib/module/components/Attachment/VideoThumbnail.js.map +1 -1
  76. package/lib/module/components/Avatar/Avatar.js +1 -1
  77. package/lib/module/components/Avatar/Avatar.js.map +1 -1
  78. package/lib/module/components/Avatar/GroupAvatar.js +1 -1
  79. package/lib/module/components/Avatar/GroupAvatar.js.map +1 -1
  80. package/lib/module/components/Channel/Channel.js +18 -13
  81. package/lib/module/components/Channel/Channel.js.map +1 -1
  82. package/lib/module/components/Channel/hooks/useCreateInputMessageInputContext.js +2 -0
  83. package/lib/module/components/Channel/hooks/useCreateInputMessageInputContext.js.map +1 -1
  84. package/lib/module/components/ImageGallery/ImageGallery.js +37 -30
  85. package/lib/module/components/ImageGallery/ImageGallery.js.map +1 -1
  86. package/lib/module/components/ImageGallery/components/AnimatedGalleryImage.js +5 -3
  87. package/lib/module/components/ImageGallery/components/AnimatedGalleryImage.js.map +1 -1
  88. package/lib/module/components/ImageGallery/components/AnimatedGalleryVideo.js +10 -6
  89. package/lib/module/components/ImageGallery/components/AnimatedGalleryVideo.js.map +1 -1
  90. package/lib/module/components/ImageGallery/components/ImageGalleryFooter.js +17 -14
  91. package/lib/module/components/ImageGallery/components/ImageGalleryFooter.js.map +1 -1
  92. package/lib/module/components/ImageGallery/components/ImageGalleryHeader.js +1 -0
  93. package/lib/module/components/ImageGallery/components/ImageGalleryHeader.js.map +1 -1
  94. package/lib/module/components/ImageGallery/components/ImageGalleryOverlay.js +2 -1
  95. package/lib/module/components/ImageGallery/components/ImageGalleryOverlay.js.map +1 -1
  96. package/lib/module/components/ImageGallery/components/ImageGalleryVideoControl.js +37 -18
  97. package/lib/module/components/ImageGallery/components/ImageGalleryVideoControl.js.map +1 -1
  98. package/lib/module/components/ImageGallery/components/ImageGrid.js +3 -1
  99. package/lib/module/components/ImageGallery/components/ImageGrid.js.map +1 -1
  100. package/lib/module/components/ImageGallery/components/ImageGridHandle.js +5 -4
  101. package/lib/module/components/ImageGallery/components/ImageGridHandle.js.map +1 -1
  102. package/lib/module/components/Message/Message.js +8 -6
  103. package/lib/module/components/Message/Message.js.map +1 -1
  104. package/lib/module/components/Message/MessageSimple/utils/parseLinks.js +1 -1
  105. package/lib/module/components/Message/MessageSimple/utils/parseLinks.js.map +1 -1
  106. package/lib/module/components/Message/MessageSimple/utils/parseLinks.test.js +1 -1
  107. package/lib/module/components/Message/MessageSimple/utils/parseLinks.test.js.map +1 -1
  108. package/lib/module/components/MessageInput/FileUploadPreview.js +125 -35
  109. package/lib/module/components/MessageInput/FileUploadPreview.js.map +1 -1
  110. package/lib/{commonjs/components/ImageGallery/components → module/components/ProgressControl}/ProgressControl.js +37 -24
  111. package/lib/module/components/ProgressControl/ProgressControl.js.map +1 -0
  112. package/lib/module/components/index.js +26 -0
  113. package/lib/module/components/index.js.map +1 -1
  114. package/lib/module/contexts/messageInputContext/MessageInputContext.js +5 -2
  115. package/lib/module/contexts/messageInputContext/MessageInputContext.js.map +1 -1
  116. package/lib/module/contexts/messageInputContext/hooks/useCreateMessageInputContext.js +7 -2
  117. package/lib/module/contexts/messageInputContext/hooks/useCreateMessageInputContext.js.map +1 -1
  118. package/lib/module/contexts/themeContext/utils/theme.js +9 -0
  119. package/lib/module/contexts/themeContext/utils/theme.js.map +1 -1
  120. package/lib/module/icons/Pause.js +2 -2
  121. package/lib/module/icons/Pause.js.map +1 -1
  122. package/lib/module/mock-builders/api/channelMocks.js +1 -0
  123. package/lib/module/mock-builders/api/channelMocks.js.map +1 -1
  124. package/lib/module/native.js +13 -5
  125. package/lib/module/native.js.map +1 -1
  126. package/lib/module/version.json +1 -1
  127. package/lib/typescript/components/Attachment/AudioAttachment.d.ts +26 -0
  128. package/lib/typescript/components/Attachment/FileAttachmentGroup.d.ts +2 -1
  129. package/lib/typescript/components/Attachment/{GallaryImage.d.ts → GalleryImage.d.ts} +0 -0
  130. package/lib/typescript/components/Channel/hooks/useCreateInputMessageInputContext.d.ts +1 -1
  131. package/lib/typescript/components/ImageGallery/__tests__/AnimatedVideoGallery.test.d.ts +1 -0
  132. package/lib/typescript/components/ImageGallery/__tests__/ImageGallery.test.d.ts +1 -0
  133. package/lib/typescript/components/ImageGallery/__tests__/ImageGalleryFooter.test.d.ts +1 -0
  134. package/lib/typescript/components/ImageGallery/__tests__/ImageGalleryGrid.test.d.ts +1 -0
  135. package/lib/typescript/components/ImageGallery/__tests__/ImageGalleryGridHandle.test.d.ts +1 -0
  136. package/lib/typescript/components/ImageGallery/__tests__/ImageGalleryHeader.test.d.ts +1 -0
  137. package/lib/typescript/components/ImageGallery/__tests__/ImageGalleryOverlay.test.d.ts +1 -0
  138. package/lib/typescript/components/ImageGallery/__tests__/ImageGalleryVideoControl.test.d.ts +1 -0
  139. package/lib/typescript/components/ImageGallery/components/AnimatedGalleryImage.d.ts +1 -0
  140. package/lib/typescript/components/ImageGallery/components/AnimatedGalleryVideo.d.ts +2 -3
  141. package/lib/typescript/components/ImageGallery/components/ImageGalleryFooter.d.ts +2 -1
  142. package/lib/typescript/components/ImageGallery/components/ImageGrid.d.ts +2 -3
  143. package/lib/typescript/components/Message/hooks/useMessageActionHandlers.d.ts +1 -1
  144. package/lib/typescript/components/Message/hooks/useMessageActions.d.ts +1 -1
  145. package/lib/typescript/components/MessageInput/FileUploadPreview.d.ts +1 -1
  146. package/lib/typescript/components/MessageInput/__tests__/AudioAttachmentUploadPreviewExpo.test.d.ts +1 -0
  147. package/lib/typescript/components/MessageInput/__tests__/AudioAttachmentUploadPreviewNative.test.d.ts +1 -0
  148. package/lib/typescript/components/ProgressControl/ProgressControl.d.ts +11 -0
  149. package/lib/typescript/components/index.d.ts +2 -0
  150. package/lib/typescript/contexts/messageContext/MessageContext.d.ts +1 -1
  151. package/lib/typescript/contexts/messageInputContext/MessageInputContext.d.ts +7 -1
  152. package/lib/typescript/contexts/messageInputContext/hooks/useCreateMessageInputContext.d.ts +1 -1
  153. package/lib/typescript/contexts/themeContext/utils/theme.d.ts +9 -0
  154. package/lib/typescript/native.d.ts +59 -2
  155. package/package.json +1 -1
  156. package/src/components/Attachment/AudioAttachment.tsx +357 -0
  157. package/src/components/Attachment/FileAttachmentGroup.tsx +96 -7
  158. package/src/components/Attachment/Gallery.tsx +1 -1
  159. package/src/components/Attachment/{GallaryImage.tsx → GalleryImage.tsx} +1 -1
  160. package/src/components/Attachment/Giphy.tsx +1 -1
  161. package/src/components/Attachment/VideoThumbnail.tsx +4 -4
  162. package/src/components/Attachment/__tests__/Gallery.test.js +6 -6
  163. package/src/components/Attachment/__tests__/Giphy.test.js +4 -4
  164. package/src/components/Avatar/Avatar.tsx +1 -1
  165. package/src/components/Avatar/GroupAvatar.tsx +1 -1
  166. package/src/components/Channel/Channel.tsx +3 -0
  167. package/src/components/Channel/hooks/useCreateInputMessageInputContext.ts +2 -0
  168. package/src/components/ImageGallery/ImageGallery.tsx +21 -11
  169. package/src/components/ImageGallery/__tests__/AnimatedVideoGallery.test.tsx +241 -0
  170. package/src/components/ImageGallery/__tests__/ImageGallery.test.tsx +192 -0
  171. package/src/components/ImageGallery/__tests__/ImageGalleryFooter.test.tsx +190 -0
  172. package/src/components/ImageGallery/__tests__/ImageGalleryGrid.test.tsx +98 -0
  173. package/src/components/ImageGallery/__tests__/ImageGalleryGridHandle.test.tsx +79 -0
  174. package/src/components/ImageGallery/__tests__/ImageGalleryHeader.test.tsx +172 -0
  175. package/src/components/ImageGallery/__tests__/ImageGalleryOverlay.test.tsx +94 -0
  176. package/src/components/ImageGallery/__tests__/ImageGalleryVideoControl.test.tsx +84 -0
  177. package/src/components/ImageGallery/components/AnimatedGalleryImage.tsx +3 -0
  178. package/src/components/ImageGallery/components/AnimatedGalleryVideo.tsx +11 -3
  179. package/src/components/ImageGallery/components/ImageGalleryFooter.tsx +9 -2
  180. package/src/components/ImageGallery/components/ImageGalleryHeader.tsx +1 -1
  181. package/src/components/ImageGallery/components/ImageGalleryOverlay.tsx +6 -1
  182. package/src/components/ImageGallery/components/ImageGalleryVideoControl.tsx +27 -9
  183. package/src/components/ImageGallery/components/ImageGrid.tsx +19 -17
  184. package/src/components/ImageGallery/components/ImageGridHandle.tsx +4 -1
  185. package/src/components/Message/Message.tsx +2 -1
  186. package/src/components/Message/MessageSimple/__tests__/MessageContent.test.js +11 -3
  187. package/src/components/Message/MessageSimple/__tests__/__snapshots__/MessageAvatar.test.js.snap +1 -1
  188. package/src/components/Message/MessageSimple/utils/parseLinks.test.ts +4 -0
  189. package/src/components/Message/MessageSimple/utils/parseLinks.ts +1 -1
  190. package/src/components/MessageInput/FileUploadPreview.tsx +162 -66
  191. package/src/components/MessageInput/__tests__/AudioAttachmentUploadPreviewExpo.test.tsx +196 -0
  192. package/src/components/MessageInput/__tests__/AudioAttachmentUploadPreviewNative.test.tsx +212 -0
  193. package/src/components/MessageInput/__tests__/FileUploadPreview.test.js +64 -0
  194. package/src/components/{ImageGallery/components → ProgressControl}/ProgressControl.tsx +37 -19
  195. package/src/components/Thread/__tests__/__snapshots__/Thread.test.js.snap +4 -4
  196. package/src/components/index.ts +3 -0
  197. package/src/contexts/messageInputContext/MessageInputContext.tsx +11 -0
  198. package/src/contexts/messageInputContext/hooks/useCreateMessageInputContext.ts +5 -1
  199. package/src/contexts/themeContext/utils/theme.ts +15 -0
  200. package/src/icons/Pause.tsx +2 -2
  201. package/src/mock-builders/api/channelMocks.tsx +1 -0
  202. package/src/native.ts +67 -7
  203. package/src/version.json +1 -1
  204. package/lib/commonjs/components/ImageGallery/components/ProgressControl.js.map +0 -1
  205. package/lib/module/components/ImageGallery/components/ProgressControl.js.map +0 -1
  206. package/lib/typescript/components/ImageGallery/components/ProgressControl.d.ts +0 -5
@@ -0,0 +1,357 @@
1
+ import React, { useEffect } from 'react';
2
+ import { I18nManager, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
3
+
4
+ import dayjs from 'dayjs';
5
+ import duration from 'dayjs/plugin/duration';
6
+
7
+ import {
8
+ FileUpload,
9
+ MessageInputContextValue,
10
+ useMessageInputContext,
11
+ useTheme,
12
+ } from '../../contexts';
13
+ import { Pause, Play } from '../../icons';
14
+ import {
15
+ PlaybackStatus,
16
+ Sound,
17
+ SoundReturnType,
18
+ VideoPayloadData,
19
+ VideoProgressData,
20
+ } from '../../native';
21
+ import type { DefaultStreamChatGenerics } from '../../types/types';
22
+ import { ProgressControl } from '../ProgressControl/ProgressControl';
23
+
24
+ dayjs.extend(duration);
25
+
26
+ const FILE_PREVIEW_HEIGHT = 70;
27
+
28
+ const styles = StyleSheet.create({
29
+ fileContainer: {
30
+ borderRadius: 12,
31
+ borderWidth: 1,
32
+ flexDirection: 'row',
33
+ height: FILE_PREVIEW_HEIGHT,
34
+ paddingLeft: 8,
35
+ paddingRight: 8,
36
+ },
37
+ fileContentContainer: { flexDirection: 'row', paddingRight: 40 },
38
+ filenameText: {
39
+ fontSize: 14,
40
+ fontWeight: 'bold',
41
+ paddingLeft: 10,
42
+ },
43
+ fileTextContainer: {
44
+ justifyContent: 'space-around',
45
+ },
46
+ flatList: { marginBottom: 12, maxHeight: FILE_PREVIEW_HEIGHT * 2.5 + 16 },
47
+ overlay: {
48
+ borderRadius: 12,
49
+ marginLeft: 8,
50
+ marginRight: 8,
51
+ },
52
+ progressControlView: {
53
+ flex: 8,
54
+ },
55
+ progressDurationText: {
56
+ flex: 4,
57
+ fontSize: 12,
58
+ paddingLeft: 10,
59
+ paddingRight: 8,
60
+ },
61
+ roundedView: {
62
+ alignItems: 'center',
63
+ alignSelf: 'center',
64
+ borderRadius: 50,
65
+ display: 'flex',
66
+ elevation: 4,
67
+ height: 36,
68
+ justifyContent: 'center',
69
+ shadowOffset: {
70
+ height: 2,
71
+ width: 0,
72
+ },
73
+ shadowOpacity: 0.23,
74
+ shadowRadius: 2.62,
75
+ width: 36,
76
+ },
77
+ });
78
+
79
+ export type AudioAttachmentPropsWithContext<
80
+ StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
81
+ > = Pick<
82
+ MessageInputContextValue<StreamChatGenerics>,
83
+ 'fileUploads' | 'removeFile' | 'uploadFile'
84
+ > & {
85
+ index: number;
86
+ item: Omit<FileUpload, 'state'>;
87
+ onLoad: (index: string, duration: number) => void;
88
+ onPlayPause: (index: string, pausedStatus?: boolean) => void;
89
+ onProgress: (index: string, currentTime?: number, hasEnd?: boolean) => void;
90
+ testID?: string;
91
+ };
92
+
93
+ const AudioAttachmentWithContext = <
94
+ StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
95
+ >(
96
+ props: AudioAttachmentPropsWithContext<StreamChatGenerics>,
97
+ ) => {
98
+ const soundRef = React.useRef<SoundReturnType | null>(null);
99
+ const { fileUploads, index, item, onLoad, onPlayPause, onProgress } = props;
100
+
101
+ const handleLoad = (payload: VideoPayloadData) => {
102
+ onLoad(item.id, payload.duration);
103
+ };
104
+
105
+ const handleProgress = (data: VideoProgressData) => {
106
+ if (data.currentTime && data.seekableDuration) {
107
+ onProgress(item.id, data.currentTime);
108
+ }
109
+ };
110
+
111
+ const handlePlayPause = async (isPausedStatusAvailable?: boolean) => {
112
+ if (soundRef.current) {
113
+ if (isPausedStatusAvailable === undefined) {
114
+ if (item.progress === 1) {
115
+ // For native CLI
116
+ if (soundRef.current.seek) soundRef.current.seek(0);
117
+ // For expo CLI
118
+ if (soundRef.current.setPositionAsync) soundRef.current.setPositionAsync(0);
119
+ }
120
+ if (item.paused) {
121
+ if (soundRef.current.playAsync) await soundRef.current.playAsync();
122
+ onPlayPause(item.id, false);
123
+ } else {
124
+ if (soundRef.current.pauseAsync) await soundRef.current.pauseAsync();
125
+ onPlayPause(item.id, true);
126
+ }
127
+ } else {
128
+ onPlayPause(item.id, isPausedStatusAvailable);
129
+ }
130
+ }
131
+ };
132
+
133
+ const handleProgressDrag = async (position: number) => {
134
+ onProgress(item.id, position);
135
+ if (soundRef.current?.seek) soundRef.current.seek(position);
136
+ if (soundRef.current?.setPositionAsync) {
137
+ await soundRef.current.setPositionAsync(position * 1000);
138
+ }
139
+ };
140
+
141
+ const handleEnd = () => {
142
+ onPlayPause(item.id, true);
143
+ onProgress(item.id, item.duration, true);
144
+ };
145
+
146
+ const onPlaybackStatusUpdate = (playbackStatus: PlaybackStatus) => {
147
+ if (!playbackStatus.isLoaded) {
148
+ // Update your UI for the unloaded state
149
+ if (playbackStatus.error) {
150
+ console.log(`Encountered a fatal error during playback: ${playbackStatus.error}`);
151
+ }
152
+ } else {
153
+ const { durationMillis, positionMillis } = playbackStatus;
154
+ onLoad(item.id, durationMillis / 1000);
155
+ // Update your UI for the loaded state
156
+ if (playbackStatus.isPlaying) {
157
+ // Update your UI for the playing state
158
+ onProgress(item.id, positionMillis / 1000);
159
+ } else {
160
+ // Update your UI for the paused state
161
+ }
162
+
163
+ if (playbackStatus.isBuffering) {
164
+ // Update your UI for the buffering state
165
+ }
166
+
167
+ if (playbackStatus.didJustFinish && !playbackStatus.isLooping) {
168
+ // The player has just finished playing and will stop. Maybe you want to play something else?
169
+ // status: opposite of pause,says i am playing
170
+ handleEnd();
171
+ }
172
+ }
173
+ };
174
+
175
+ useEffect(() => {
176
+ if (Sound.Player === null) {
177
+ const initiateSound = async () => {
178
+ if (item && item.file && item.file.uri) {
179
+ soundRef.current = await Sound.initializeSound(
180
+ { uri: item.file.uri },
181
+ {},
182
+ onPlaybackStatusUpdate,
183
+ );
184
+ }
185
+ };
186
+ initiateSound();
187
+ }
188
+
189
+ return () => {
190
+ if (soundRef.current?.stopAsync && soundRef.current.unloadAsync) {
191
+ soundRef.current.stopAsync();
192
+ soundRef.current.unloadAsync();
193
+ }
194
+ };
195
+ }, []);
196
+
197
+ // This is needed for expo applications where the rerender doesn't occur on time thefore you need to update the state of the sound.
198
+ useEffect(() => {
199
+ const initalPlayPause = async () => {
200
+ if (soundRef.current) {
201
+ if (item.paused) {
202
+ if (soundRef.current.pauseAsync) await soundRef.current.pauseAsync();
203
+ } else {
204
+ if (soundRef.current.playAsync) await soundRef.current.playAsync();
205
+ }
206
+ }
207
+ };
208
+ if (!Sound.Player) {
209
+ initalPlayPause();
210
+ }
211
+ }, [item.paused]);
212
+
213
+ const {
214
+ theme: {
215
+ colors: { accent_blue, black, grey_dark, grey_whisper, static_black, static_white, white },
216
+ messageInput: {
217
+ fileUploadPreview: {
218
+ audioAttachment: { progressControlView, progressDurationText, roundedView },
219
+ fileContainer,
220
+ fileContentContainer,
221
+ filenameText,
222
+ fileTextContainer,
223
+ },
224
+ },
225
+ },
226
+ } = useTheme();
227
+
228
+ const progressValueInSeconds = (item.duration as number) * (item.progress as number);
229
+
230
+ const progressDuration = progressValueInSeconds
231
+ ? progressValueInSeconds / 3600 >= 1
232
+ ? dayjs.duration(progressValueInSeconds, 'second').format('HH:mm:ss')
233
+ : dayjs.duration(progressValueInSeconds, 'second').format('mm:ss')
234
+ : '00:00';
235
+
236
+ const lastIndexOfDot = item.file.name.lastIndexOf('.');
237
+
238
+ return (
239
+ <View
240
+ style={[
241
+ styles.fileContainer,
242
+ index === fileUploads.length - 1
243
+ ? {
244
+ marginBottom: 0,
245
+ }
246
+ : {},
247
+ {
248
+ backgroundColor: white,
249
+ borderColor: grey_whisper,
250
+ width: -16,
251
+ },
252
+ fileContainer,
253
+ ]}
254
+ testID='audio-attachment-upload-preview'
255
+ >
256
+ <View style={[styles.fileContentContainer, fileContentContainer]}>
257
+ <TouchableOpacity
258
+ accessibilityLabel='Play Pause Button'
259
+ onPress={() => handlePlayPause()}
260
+ style={[
261
+ styles.roundedView,
262
+ roundedView,
263
+ { backgroundColor: static_white, shadowColor: black },
264
+ ]}
265
+ >
266
+ {item.paused ? (
267
+ <Play height={24} pathFill={static_black} width={24} />
268
+ ) : (
269
+ <Pause height={24} pathFill={static_black} width={24} />
270
+ )}
271
+ </TouchableOpacity>
272
+ <View style={[styles.fileTextContainer, fileTextContainer]}>
273
+ <Text
274
+ accessibilityLabel='File Name'
275
+ numberOfLines={1}
276
+ style={[
277
+ styles.filenameText,
278
+ {
279
+ color: black,
280
+ width:
281
+ 16 - // 16 = horizontal padding
282
+ 40 - // 40 = file icon size
283
+ 24 - // 24 = close icon size
284
+ 24, // 24 = internal padding
285
+ },
286
+ I18nManager.isRTL ? { writingDirection: 'rtl' } : { writingDirection: 'ltr' },
287
+ filenameText,
288
+ ]}
289
+ >
290
+ {item.file.name.slice(0, 12) + '...' + item.file.name.slice(lastIndexOfDot)}
291
+ </Text>
292
+ <View
293
+ style={{
294
+ alignItems: 'center',
295
+ display: 'flex',
296
+ flexDirection: 'row',
297
+ }}
298
+ >
299
+ {/* <ExpoSoundPlayer filePaused={!!item.paused} soundRef={soundRef} /> */}
300
+ {Sound.Player && (
301
+ <Sound.Player
302
+ onEnd={handleEnd}
303
+ onLoad={handleLoad}
304
+ onProgress={handleProgress}
305
+ paused={item.paused as boolean}
306
+ soundRef={soundRef}
307
+ testID='sound-player'
308
+ uri={item.file.uri}
309
+ />
310
+ )}
311
+ <Text style={[styles.progressDurationText, { color: grey_dark }, progressDurationText]}>
312
+ {progressDuration}
313
+ </Text>
314
+ <View style={[styles.progressControlView, progressControlView]}>
315
+ <ProgressControl
316
+ duration={item.duration as number}
317
+ filledColor={accent_blue}
318
+ onPlayPause={handlePlayPause}
319
+ onProgressDrag={handleProgressDrag}
320
+ progress={item.progress as number}
321
+ testID='progress-control'
322
+ width={120}
323
+ />
324
+ </View>
325
+ </View>
326
+ </View>
327
+ </View>
328
+ </View>
329
+ );
330
+ };
331
+
332
+ export type AudioAttachmentProps<
333
+ StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
334
+ > = Partial<AudioAttachmentPropsWithContext<StreamChatGenerics>> & {
335
+ index: number;
336
+ item: Omit<FileUpload, 'state'>;
337
+ onLoad: (index: string, duration: number) => void;
338
+ onPlayPause: (index: string, pausedStatus?: boolean) => void;
339
+ onProgress: (index: string, currentTime?: number, hasEnd?: boolean) => void;
340
+ testID: string;
341
+ };
342
+
343
+ /**
344
+ * AudioAttachment
345
+ * UI Component to preview the audio files
346
+ */
347
+ export const AudioAttachment = <
348
+ StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
349
+ >(
350
+ props: AudioAttachmentProps<StreamChatGenerics>,
351
+ ) => {
352
+ const { fileUploads, removeFile, uploadFile } = useMessageInputContext<StreamChatGenerics>();
353
+
354
+ return <AudioAttachmentWithContext {...{ fileUploads, removeFile, uploadFile }} {...props} />;
355
+ };
356
+
357
+ AudioAttachment.displayName = 'AudioAttachment{messageInput{autoAttachment}}';
@@ -1,17 +1,24 @@
1
- import React from 'react';
1
+ import React, { useEffect, useState } from 'react';
2
2
  import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native';
3
3
 
4
+ import type { Attachment } from 'stream-chat';
5
+
4
6
  import { Attachment as AttachmentDefault } from './Attachment';
5
7
 
6
8
  import {
7
9
  MessageContextValue,
8
10
  useMessageContext,
9
11
  } from '../../contexts/messageContext/MessageContext';
12
+ import {
13
+ MessageInputContextValue,
14
+ useMessageInputContext,
15
+ } from '../../contexts/messageInputContext/MessageInputContext';
10
16
  import {
11
17
  MessagesContextValue,
12
18
  useMessagesContext,
13
19
  } from '../../contexts/messagesContext/MessagesContext';
14
20
  import { useTheme } from '../../contexts/themeContext/ThemeContext';
21
+ import { isAudioPackageAvailable } from '../../native';
15
22
 
16
23
  import type { DefaultStreamChatGenerics } from '../../types/types';
17
24
 
@@ -24,6 +31,7 @@ const styles = StyleSheet.create({
24
31
  export type FileAttachmentGroupPropsWithContext<
25
32
  StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
26
33
  > = Pick<MessageContextValue<StreamChatGenerics>, 'files'> &
34
+ Pick<MessageInputContextValue<StreamChatGenerics>, 'AudioAttachment'> &
27
35
  Pick<MessagesContextValue<StreamChatGenerics>, 'Attachment'> & {
28
36
  /**
29
37
  * The unique id for the message with file attachments
@@ -35,12 +43,73 @@ export type FileAttachmentGroupPropsWithContext<
35
43
  }>;
36
44
  };
37
45
 
46
+ type FilesToDisplayType<
47
+ StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
48
+ > = Attachment<StreamChatGenerics> & {
49
+ duration: number;
50
+ paused: boolean;
51
+ progress: number;
52
+ };
53
+
38
54
  const FileAttachmentGroupWithContext = <
39
55
  StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
40
56
  >(
41
57
  props: FileAttachmentGroupPropsWithContext<StreamChatGenerics>,
42
58
  ) => {
43
- const { Attachment, files, messageId, styles: stylesProp = {} } = props;
59
+ const { Attachment, AudioAttachment, files, messageId, styles: stylesProp = {} } = props;
60
+ const [filesToDisplay, setFilesToDisplay] = useState<FilesToDisplayType[]>([]);
61
+
62
+ useEffect(() => {
63
+ setFilesToDisplay(files.map((file) => ({ ...file, duration: 0, paused: true, progress: 0 })));
64
+ }, [files]);
65
+
66
+ // Handler triggered when an audio is loaded in the message input. The initial state is defined for the audio here and the duration is set.
67
+ const onLoad = (index: string, duration: number) => {
68
+ setFilesToDisplay((prevFilesToDisplay) =>
69
+ prevFilesToDisplay.map((fileToDisplay, id) => ({
70
+ ...fileToDisplay,
71
+ duration: id.toString() === index ? duration : fileToDisplay.duration,
72
+ })),
73
+ );
74
+ };
75
+
76
+ // The handler which is triggered when the audio progresses/ the thumb is dragged in the progress control. The progressed duration is set here.
77
+ const onProgress = (index: string, currentTime?: number, hasEnd?: boolean) => {
78
+ setFilesToDisplay((prevFileUploads) =>
79
+ prevFileUploads.map((fileUpload, id) => ({
80
+ ...fileUpload,
81
+ progress:
82
+ id.toString() === index
83
+ ? hasEnd
84
+ ? 1
85
+ : currentTime
86
+ ? currentTime / (fileUpload.duration as number)
87
+ : 0
88
+ : fileUpload.progress,
89
+ })),
90
+ );
91
+ };
92
+
93
+ // The handler which controls or sets the paused/played state of the audio.
94
+ const onPlayPause = (index: string, pausedStatus?: boolean) => {
95
+ if (pausedStatus === false) {
96
+ // If the status is false we set the audio with the index as playing and the others as paused.
97
+ setFilesToDisplay((prevFileUploads) =>
98
+ prevFileUploads.map((fileUpload, id) => ({
99
+ ...fileUpload,
100
+ paused: id.toString() === index ? false : true,
101
+ })),
102
+ );
103
+ } else {
104
+ // If the status is true we simply set all the audio's paused state as true.
105
+ setFilesToDisplay((prevFileUploads) =>
106
+ prevFileUploads.map((fileUpload) => ({
107
+ ...fileUpload,
108
+ paused: true,
109
+ })),
110
+ );
111
+ }
112
+ };
44
113
 
45
114
  const {
46
115
  theme: {
@@ -52,7 +121,7 @@ const FileAttachmentGroupWithContext = <
52
121
 
53
122
  return (
54
123
  <View style={[styles.container, container, stylesProp.container]}>
55
- {files.map((file, index) => (
124
+ {filesToDisplay.map((file, index) => (
56
125
  <View
57
126
  key={`${messageId}-${index}`}
58
127
  style={[
@@ -60,7 +129,26 @@ const FileAttachmentGroupWithContext = <
60
129
  stylesProp.attachmentContainer,
61
130
  ]}
62
131
  >
63
- <Attachment attachment={file} />
132
+ {file.type === 'audio' && isAudioPackageAvailable() ? (
133
+ <View accessibilityLabel='audio-attachment-preview'>
134
+ <AudioAttachment
135
+ index={index}
136
+ item={{
137
+ duration: file.duration,
138
+ file: { name: file.title as string, uri: file.asset_url },
139
+ id: index.toString(),
140
+ paused: file.paused,
141
+ progress: file.progress,
142
+ }}
143
+ onLoad={onLoad}
144
+ onPlayPause={onPlayPause}
145
+ onProgress={onProgress}
146
+ testID='audio-attachment-preview'
147
+ />
148
+ </View>
149
+ ) : (
150
+ <Attachment attachment={file} />
151
+ )}
64
152
  </View>
65
153
  ))}
66
154
  </View>
@@ -74,9 +162,7 @@ const areEqual = <StreamChatGenerics extends DefaultStreamChatGenerics = Default
74
162
  const { files: prevFiles } = prevProps;
75
163
  const { files: nextFiles } = nextProps;
76
164
 
77
- const filesEqual = prevFiles.length === nextFiles.length;
78
-
79
- return filesEqual;
165
+ return prevFiles.length === nextFiles.length;
80
166
  };
81
167
 
82
168
  const MemoizedFileAttachmentGroup = React.memo(
@@ -100,6 +186,8 @@ export const FileAttachmentGroup = <
100
186
 
101
187
  const { Attachment = AttachmentDefault } = useMessagesContext<StreamChatGenerics>();
102
188
 
189
+ const { AudioAttachment } = useMessageInputContext<StreamChatGenerics>();
190
+
103
191
  const files = propFiles || contextFiles;
104
192
 
105
193
  if (!files.length) return null;
@@ -108,6 +196,7 @@ export const FileAttachmentGroup = <
108
196
  <MemoizedFileAttachmentGroup
109
197
  {...{
110
198
  Attachment,
199
+ AudioAttachment,
111
200
  files,
112
201
  messageId,
113
202
  }}
@@ -3,7 +3,7 @@ import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
3
3
 
4
4
  import type { Attachment } from 'stream-chat';
5
5
 
6
- import { GalleryImage } from './GallaryImage';
6
+ import { GalleryImage } from './GalleryImage';
7
7
  import { useLoadingImage } from './hooks/useLoadingImage';
8
8
  import { buildGallery } from './utils/buildGallery/buildGallery';
9
9
 
@@ -13,7 +13,7 @@ export const GalleryImage: React.FC<
13
13
  return (
14
14
  <ImageBackground
15
15
  {...rest}
16
- accessibilityLabel='gallery-image'
16
+ accessibilityLabel='Gallery Image'
17
17
  source={{
18
18
  uri: makeImageCompatibleUrl(uri),
19
19
  }}
@@ -341,7 +341,7 @@ const GiphyWithContext = <
341
341
  ]}
342
342
  >
343
343
  <Image
344
- accessibilityLabel='giphy-attachment-image'
344
+ accessibilityLabel='Giphy Attachment Image'
345
345
  onError={(error) => {
346
346
  console.warn(error);
347
347
  setLoadingImage(false);
@@ -15,7 +15,6 @@ const styles = StyleSheet.create({
15
15
  },
16
16
  roundedView: {
17
17
  alignItems: 'center',
18
- backgroundColor: '#fff',
19
18
  borderRadius: 50,
20
19
  display: 'flex',
21
20
  elevation: 6,
@@ -41,6 +40,7 @@ export type VideoThumbnailProps = {
41
40
  export const VideoThumbnail: React.FC<VideoThumbnailProps> = (props) => {
42
41
  const {
43
42
  theme: {
43
+ colors: { static_black, static_white },
44
44
  messageSimple: {
45
45
  videoThumbnail: { container, roundedView },
46
46
  },
@@ -49,13 +49,13 @@ export const VideoThumbnail: React.FC<VideoThumbnailProps> = (props) => {
49
49
  const { imageStyle, style, thumb_url } = props;
50
50
  return (
51
51
  <ImageBackground
52
- accessibilityLabel='video-thumbnail'
52
+ accessibilityLabel='Video Thumbnail'
53
53
  imageStyle={imageStyle}
54
54
  source={{ uri: thumb_url }}
55
55
  style={[styles.container, container, style]}
56
56
  >
57
- <View style={[styles.roundedView, roundedView]}>
58
- <Play height={24} pathFill={'#000'} width={24} />
57
+ <View style={[styles.roundedView, roundedView, { backgroundColor: static_white }]}>
58
+ <Play height={24} pathFill={static_black} width={24} />
59
59
  </View>
60
60
  </ImageBackground>
61
61
  );
@@ -93,7 +93,7 @@ describe('Gallery', () => {
93
93
  expect(queryAllByTestId('gallery-row-0').length).toBe(1);
94
94
 
95
95
  expect(queryAllByTestId('gallery-row-0-item-0').length).toBe(1);
96
- expect(getAllByA11yLabel('video-thumbnail').length).toBe(1);
96
+ expect(getAllByA11yLabel('Video Thumbnail').length).toBe(1);
97
97
  });
98
98
  });
99
99
 
@@ -267,7 +267,7 @@ describe('Gallery', () => {
267
267
  const component = await getComponent([image1]);
268
268
  const { getByA11yLabel, getByAccessibilityHint } = render(component);
269
269
 
270
- fireEvent(getByA11yLabel('gallery-image'), 'error');
270
+ fireEvent(getByA11yLabel('Gallery Image'), 'error');
271
271
  expect(getByAccessibilityHint('image-loading-error')).toBeTruthy();
272
272
  });
273
273
 
@@ -280,11 +280,11 @@ describe('Gallery', () => {
280
280
  const component = await getComponent([image1]);
281
281
  const { getByA11yLabel, getByAccessibilityHint } = render(component);
282
282
 
283
- fireEvent(getByA11yLabel('gallery-image'), 'onLoadStart');
283
+ fireEvent(getByA11yLabel('Gallery Image'), 'onLoadStart');
284
284
  expect(getByAccessibilityHint('image-loading')).toBeTruthy();
285
285
 
286
- fireEvent(getByA11yLabel('gallery-image'), 'onLoadFinish');
287
- waitForElementToBeRemoved(() => getByAccessibilityHint('image-loading'));
288
- expect(getByA11yLabel('gallery-image')).toBeTruthy();
286
+ fireEvent(getByA11yLabel('Gallery Image'), 'onLoadFinish');
287
+ waitForElementToBeRemoved(() => getByAccessibilityHint('Image Loading'));
288
+ expect(getByA11yLabel('Gallery Image')).toBeTruthy();
289
289
  });
290
290
  });
@@ -285,7 +285,7 @@ describe('Giphy', () => {
285
285
  </OverlayProvider>,
286
286
  );
287
287
 
288
- fireEvent(getByA11yLabel('giphy-attachment-image'), 'error');
288
+ fireEvent(getByA11yLabel('Giphy Attachment Image'), 'error');
289
289
  expect(getByAccessibilityHint('image-loading-error')).toBeTruthy();
290
290
  });
291
291
 
@@ -302,13 +302,13 @@ describe('Giphy', () => {
302
302
 
303
303
  expect(getByAccessibilityHint('image-loading')).toBeTruthy();
304
304
 
305
- fireEvent(getByA11yLabel('giphy-attachment-image'), 'onLoadStart');
305
+ fireEvent(getByA11yLabel('Giphy Attachment Image'), 'onLoadStart');
306
306
 
307
307
  expect(getByAccessibilityHint('image-loading')).toBeTruthy();
308
308
 
309
- fireEvent(getByA11yLabel('giphy-attachment-image'), 'onLoadFinish');
309
+ fireEvent(getByA11yLabel('Giphy Attachment Image'), 'onLoadFinish');
310
310
 
311
311
  waitForElementToBeRemoved(() => getByAccessibilityHint('image-loading'));
312
- expect(getByA11yLabel('giphy-attachment-image')).toBeTruthy();
312
+ expect(getByA11yLabel('Giphy Attachment Image')).toBeTruthy();
313
313
  });
314
314
  });
@@ -85,7 +85,7 @@ export const Avatar: React.FC<AvatarProps> = (props) => {
85
85
  ]}
86
86
  >
87
87
  <Image
88
- accessibilityLabel={testID || 'avatar-image'}
88
+ accessibilityLabel={testID || 'Avatar Image'}
89
89
  onError={() => setImageError(true)}
90
90
  source={{
91
91
  uri:
@@ -127,7 +127,7 @@ export const GroupAvatar: React.FC<GroupAvatarProps> = (props) => {
127
127
  >
128
128
  {column.map(({ height, name, url, width }, rowIndex) => (
129
129
  <Image
130
- accessibilityLabel={testID || 'avatar-image'}
130
+ accessibilityLabel={testID || 'Avatar Image'}
131
131
  key={`avatar-${url}-${rowIndex}`}
132
132
  onError={() => setImageError(true)}
133
133
  source={{