stream-chat-react-native-core 8.10.1 → 8.11.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 (189) hide show
  1. package/lib/commonjs/components/Attachment/AudioAttachment.js +84 -209
  2. package/lib/commonjs/components/Attachment/AudioAttachment.js.map +1 -1
  3. package/lib/commonjs/components/Attachment/FileAttachmentGroup.js +8 -5
  4. package/lib/commonjs/components/Attachment/FileAttachmentGroup.js.map +1 -1
  5. package/lib/commonjs/components/AttachmentPicker/components/AttachmentPickerBottomSheetHandle.js +7 -3
  6. package/lib/commonjs/components/AttachmentPicker/components/AttachmentPickerBottomSheetHandle.js.map +1 -1
  7. package/lib/commonjs/components/Channel/Channel.js +20 -9
  8. package/lib/commonjs/components/Channel/Channel.js.map +1 -1
  9. package/lib/commonjs/components/Message/hooks/useMessageActionHandlers.js +4 -1
  10. package/lib/commonjs/components/Message/hooks/useMessageActionHandlers.js.map +1 -1
  11. package/lib/commonjs/components/MessageInput/MessageInput.js +2 -0
  12. package/lib/commonjs/components/MessageInput/MessageInput.js.map +1 -1
  13. package/lib/commonjs/components/MessageInput/components/AttachmentPreview/AudioAttachmentUploadPreview.js +9 -3
  14. package/lib/commonjs/components/MessageInput/components/AttachmentPreview/AudioAttachmentUploadPreview.js.map +1 -1
  15. package/lib/commonjs/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.js +44 -8
  16. package/lib/commonjs/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.js.map +1 -1
  17. package/lib/commonjs/components/MessageInput/hooks/useAudioController.js +5 -0
  18. package/lib/commonjs/components/MessageInput/hooks/useAudioController.js.map +1 -1
  19. package/lib/commonjs/components/MessageInput/hooks/useAudioPreviewManager.js.map +1 -1
  20. package/lib/commonjs/components/ProgressControl/ProgressControl.js +25 -33
  21. package/lib/commonjs/components/ProgressControl/ProgressControl.js.map +1 -1
  22. package/lib/commonjs/components/ProgressControl/WaveProgressBar.js +21 -29
  23. package/lib/commonjs/components/ProgressControl/WaveProgressBar.js.map +1 -1
  24. package/lib/commonjs/contexts/audioPlayerContext/AudioPlayerContext.js +56 -0
  25. package/lib/commonjs/contexts/audioPlayerContext/AudioPlayerContext.js.map +1 -0
  26. package/lib/commonjs/contexts/index.js +11 -0
  27. package/lib/commonjs/contexts/index.js.map +1 -1
  28. package/lib/commonjs/contexts/themeContext/utils/theme.js +4 -0
  29. package/lib/commonjs/contexts/themeContext/utils/theme.js.map +1 -1
  30. package/lib/commonjs/hooks/index.js +11 -0
  31. package/lib/commonjs/hooks/index.js.map +1 -1
  32. package/lib/commonjs/hooks/useAudioPlayer.js +18 -25
  33. package/lib/commonjs/hooks/useAudioPlayer.js.map +1 -1
  34. package/lib/commonjs/hooks/useAudioPlayerControl.js +43 -0
  35. package/lib/commonjs/hooks/useAudioPlayerControl.js.map +1 -0
  36. package/lib/commonjs/hooks/useInAppNotificationsState.js +1 -1
  37. package/lib/commonjs/i18n/es.json +1 -2
  38. package/lib/commonjs/i18n/he.json +5 -5
  39. package/lib/commonjs/i18n/ru.json +0 -5
  40. package/lib/commonjs/index.js +4 -4
  41. package/lib/commonjs/index.js.map +1 -1
  42. package/lib/commonjs/native.js.map +1 -1
  43. package/lib/commonjs/state-store/audio-player-pool.js +99 -0
  44. package/lib/commonjs/state-store/audio-player-pool.js.map +1 -0
  45. package/lib/commonjs/state-store/audio-player.js +373 -0
  46. package/lib/commonjs/state-store/audio-player.js.map +1 -0
  47. package/lib/commonjs/state-store/in-app-notifications-store.js.map +1 -0
  48. package/lib/commonjs/state-store/index.js +37 -0
  49. package/lib/commonjs/state-store/index.js.map +1 -0
  50. package/lib/commonjs/version.json +1 -1
  51. package/lib/module/components/Attachment/AudioAttachment.js +84 -209
  52. package/lib/module/components/Attachment/AudioAttachment.js.map +1 -1
  53. package/lib/module/components/Attachment/FileAttachmentGroup.js +8 -5
  54. package/lib/module/components/Attachment/FileAttachmentGroup.js.map +1 -1
  55. package/lib/module/components/AttachmentPicker/components/AttachmentPickerBottomSheetHandle.js +7 -3
  56. package/lib/module/components/AttachmentPicker/components/AttachmentPickerBottomSheetHandle.js.map +1 -1
  57. package/lib/module/components/Channel/Channel.js +20 -9
  58. package/lib/module/components/Channel/Channel.js.map +1 -1
  59. package/lib/module/components/Message/hooks/useMessageActionHandlers.js +4 -1
  60. package/lib/module/components/Message/hooks/useMessageActionHandlers.js.map +1 -1
  61. package/lib/module/components/MessageInput/MessageInput.js +2 -0
  62. package/lib/module/components/MessageInput/MessageInput.js.map +1 -1
  63. package/lib/module/components/MessageInput/components/AttachmentPreview/AudioAttachmentUploadPreview.js +9 -3
  64. package/lib/module/components/MessageInput/components/AttachmentPreview/AudioAttachmentUploadPreview.js.map +1 -1
  65. package/lib/module/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.js +44 -8
  66. package/lib/module/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.js.map +1 -1
  67. package/lib/module/components/MessageInput/hooks/useAudioController.js +5 -0
  68. package/lib/module/components/MessageInput/hooks/useAudioController.js.map +1 -1
  69. package/lib/module/components/MessageInput/hooks/useAudioPreviewManager.js.map +1 -1
  70. package/lib/module/components/ProgressControl/ProgressControl.js +25 -33
  71. package/lib/module/components/ProgressControl/ProgressControl.js.map +1 -1
  72. package/lib/module/components/ProgressControl/WaveProgressBar.js +21 -29
  73. package/lib/module/components/ProgressControl/WaveProgressBar.js.map +1 -1
  74. package/lib/module/contexts/audioPlayerContext/AudioPlayerContext.js +56 -0
  75. package/lib/module/contexts/audioPlayerContext/AudioPlayerContext.js.map +1 -0
  76. package/lib/module/contexts/index.js +11 -0
  77. package/lib/module/contexts/index.js.map +1 -1
  78. package/lib/module/contexts/themeContext/utils/theme.js +4 -0
  79. package/lib/module/contexts/themeContext/utils/theme.js.map +1 -1
  80. package/lib/module/hooks/index.js +11 -0
  81. package/lib/module/hooks/index.js.map +1 -1
  82. package/lib/module/hooks/useAudioPlayer.js +18 -25
  83. package/lib/module/hooks/useAudioPlayer.js.map +1 -1
  84. package/lib/module/hooks/useAudioPlayerControl.js +43 -0
  85. package/lib/module/hooks/useAudioPlayerControl.js.map +1 -0
  86. package/lib/module/hooks/useInAppNotificationsState.js +1 -1
  87. package/lib/module/i18n/es.json +1 -2
  88. package/lib/module/i18n/he.json +5 -5
  89. package/lib/module/i18n/ru.json +0 -5
  90. package/lib/module/index.js +4 -4
  91. package/lib/module/index.js.map +1 -1
  92. package/lib/module/native.js.map +1 -1
  93. package/lib/module/state-store/audio-player-pool.js +99 -0
  94. package/lib/module/state-store/audio-player-pool.js.map +1 -0
  95. package/lib/module/state-store/audio-player.js +373 -0
  96. package/lib/module/state-store/audio-player.js.map +1 -0
  97. package/lib/module/state-store/in-app-notifications-store.js.map +1 -0
  98. package/lib/module/state-store/index.js +37 -0
  99. package/lib/module/state-store/index.js.map +1 -0
  100. package/lib/module/version.json +1 -1
  101. package/lib/typescript/components/Attachment/AudioAttachment.d.ts +25 -5
  102. package/lib/typescript/components/Attachment/AudioAttachment.d.ts.map +1 -1
  103. package/lib/typescript/components/Attachment/FileAttachmentGroup.d.ts +2 -1
  104. package/lib/typescript/components/Attachment/FileAttachmentGroup.d.ts.map +1 -1
  105. package/lib/typescript/components/AttachmentPicker/components/AttachmentPickerBottomSheetHandle.d.ts.map +1 -1
  106. package/lib/typescript/components/Channel/Channel.d.ts +5 -0
  107. package/lib/typescript/components/Channel/Channel.d.ts.map +1 -1
  108. package/lib/typescript/components/Message/hooks/useMessageActionHandlers.d.ts.map +1 -1
  109. package/lib/typescript/components/MessageInput/MessageInput.d.ts.map +1 -1
  110. package/lib/typescript/components/MessageInput/components/AttachmentPreview/AudioAttachmentUploadPreview.d.ts +17 -0
  111. package/lib/typescript/components/MessageInput/components/AttachmentPreview/AudioAttachmentUploadPreview.d.ts.map +1 -1
  112. package/lib/typescript/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.d.ts +18 -4
  113. package/lib/typescript/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.d.ts.map +1 -1
  114. package/lib/typescript/components/MessageInput/hooks/useAudioController.d.ts +2 -0
  115. package/lib/typescript/components/MessageInput/hooks/useAudioController.d.ts.map +1 -1
  116. package/lib/typescript/components/MessageInput/hooks/useAudioPreviewManager.d.ts +2 -0
  117. package/lib/typescript/components/MessageInput/hooks/useAudioPreviewManager.d.ts.map +1 -1
  118. package/lib/typescript/components/ProgressControl/ProgressControl.d.ts +2 -0
  119. package/lib/typescript/components/ProgressControl/ProgressControl.d.ts.map +1 -1
  120. package/lib/typescript/components/ProgressControl/WaveProgressBar.d.ts.map +1 -1
  121. package/lib/typescript/contexts/audioPlayerContext/AudioPlayerContext.d.ts +15 -0
  122. package/lib/typescript/contexts/audioPlayerContext/AudioPlayerContext.d.ts.map +1 -0
  123. package/lib/typescript/contexts/index.d.ts +1 -0
  124. package/lib/typescript/contexts/index.d.ts.map +1 -1
  125. package/lib/typescript/contexts/themeContext/utils/theme.d.ts +4 -0
  126. package/lib/typescript/contexts/themeContext/utils/theme.d.ts.map +1 -1
  127. package/lib/typescript/hooks/index.d.ts +1 -0
  128. package/lib/typescript/hooks/index.d.ts.map +1 -1
  129. package/lib/typescript/hooks/useAudioPlayer.d.ts +3 -1
  130. package/lib/typescript/hooks/useAudioPlayer.d.ts.map +1 -1
  131. package/lib/typescript/hooks/useAudioPlayerControl.d.ts +18 -0
  132. package/lib/typescript/hooks/useAudioPlayerControl.d.ts.map +1 -0
  133. package/lib/typescript/i18n/es.json +1 -2
  134. package/lib/typescript/i18n/he.json +5 -5
  135. package/lib/typescript/i18n/ru.json +0 -5
  136. package/lib/typescript/index.d.ts +1 -1
  137. package/lib/typescript/index.d.ts.map +1 -1
  138. package/lib/typescript/native.d.ts +1 -0
  139. package/lib/typescript/native.d.ts.map +1 -1
  140. package/lib/typescript/state-store/audio-player-pool.d.ts +24 -0
  141. package/lib/typescript/state-store/audio-player-pool.d.ts.map +1 -0
  142. package/lib/typescript/state-store/audio-player.d.ts +62 -0
  143. package/lib/typescript/state-store/audio-player.d.ts.map +1 -0
  144. package/lib/typescript/state-store/in-app-notifications-store.d.ts.map +1 -0
  145. package/lib/typescript/state-store/index.d.ts +4 -0
  146. package/lib/typescript/state-store/index.d.ts.map +1 -0
  147. package/package.json +6 -5
  148. package/src/components/Attachment/AudioAttachment.tsx +118 -198
  149. package/src/components/Attachment/FileAttachmentGroup.tsx +35 -16
  150. package/src/components/AttachmentPicker/components/AttachmentPickerBottomSheetHandle.tsx +5 -2
  151. package/src/components/Channel/Channel.tsx +21 -4
  152. package/src/components/Message/hooks/useMessageActionHandlers.ts +3 -1
  153. package/src/components/MessageInput/MessageInput.tsx +6 -0
  154. package/src/components/MessageInput/components/AttachmentPreview/AudioAttachmentUploadPreview.tsx +29 -2
  155. package/src/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.tsx +73 -9
  156. package/src/components/MessageInput/hooks/useAudioController.tsx +62 -1
  157. package/src/components/MessageInput/hooks/useAudioPreviewManager.tsx +28 -5
  158. package/src/components/ProgressControl/ProgressControl.tsx +45 -47
  159. package/src/components/ProgressControl/WaveProgressBar.tsx +37 -36
  160. package/src/contexts/audioPlayerContext/AudioPlayerContext.tsx +55 -0
  161. package/src/contexts/index.ts +1 -0
  162. package/src/contexts/themeContext/utils/theme.ts +8 -0
  163. package/src/hooks/index.ts +1 -0
  164. package/src/hooks/useAudioPlayer.ts +7 -8
  165. package/src/hooks/useAudioPlayerControl.ts +59 -0
  166. package/src/hooks/useInAppNotificationsState.ts +2 -2
  167. package/src/i18n/es.json +1 -2
  168. package/src/i18n/he.json +5 -5
  169. package/src/i18n/ru.json +0 -5
  170. package/src/index.ts +1 -1
  171. package/src/native.ts +1 -0
  172. package/src/state-store/audio-player-pool.ts +94 -0
  173. package/src/state-store/audio-player.ts +372 -0
  174. package/src/state-store/index.ts +3 -0
  175. package/src/version.json +1 -1
  176. package/lib/commonjs/store/in-app-notifications-store.js.map +0 -1
  177. package/lib/commonjs/store/index.js +0 -15
  178. package/lib/commonjs/store/index.js.map +0 -1
  179. package/lib/module/store/in-app-notifications-store.js.map +0 -1
  180. package/lib/module/store/index.js +0 -15
  181. package/lib/module/store/index.js.map +0 -1
  182. package/lib/typescript/store/in-app-notifications-store.d.ts.map +0 -1
  183. package/lib/typescript/store/index.d.ts +0 -2
  184. package/lib/typescript/store/index.d.ts.map +0 -1
  185. package/src/store/index.ts +0 -1
  186. /package/lib/commonjs/{store → state-store}/in-app-notifications-store.js +0 -0
  187. /package/lib/module/{store → state-store}/in-app-notifications-store.js +0 -0
  188. /package/lib/typescript/{store → state-store}/in-app-notifications-store.d.ts +0 -0
  189. /package/src/{store → state-store}/in-app-notifications-store.ts +0 -0
@@ -52,6 +52,10 @@ import {
52
52
  AttachmentPickerProvider,
53
53
  MessageContextValue,
54
54
  } from '../../contexts';
55
+ import {
56
+ AudioPlayerContextProps,
57
+ AudioPlayerProvider,
58
+ } from '../../contexts/audioPlayerContext/AudioPlayerContext';
55
59
  import { ChannelContextValue, ChannelProvider } from '../../contexts/channelContext/ChannelContext';
56
60
  import type { UseChannelStateValue } from '../../contexts/channelsStateContext/useChannelState';
57
61
  import { useChannelState } from '../../contexts/channelsStateContext/useChannelState';
@@ -500,6 +504,11 @@ export type ChannelPropsWithContext = Pick<ChannelContextValue, 'channel'> &
500
504
  */
501
505
  newMessageStateUpdateThrottleInterval?: number;
502
506
  overrideOwnCapabilities?: Partial<OwnCapabilitiesContextValue>;
507
+ /**
508
+ * If true, multiple audio players will be allowed to play simultaneously
509
+ * @default true
510
+ */
511
+ allowConcurrentAudioPlayback?: boolean;
503
512
  stateUpdateThrottleInterval?: number;
504
513
  /**
505
514
  * Tells if channel is rendering a thread list
@@ -533,6 +542,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
533
542
  additionalKeyboardAvoidingViewProps,
534
543
  additionalPressableProps,
535
544
  additionalTextInputProps,
545
+ allowConcurrentAudioPlayback = false,
536
546
  allowThreadMessagesInChannel = true,
537
547
  asyncMessagesLockDistance = 50,
538
548
  asyncMessagesMinimumPressDuration = 500,
@@ -2009,6 +2019,11 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
2009
2019
  typing: channelState.typing ?? {},
2010
2020
  });
2011
2021
 
2022
+ const audioPlayerContext = useMemo<AudioPlayerContextProps>(
2023
+ () => ({ allowConcurrentAudioPlayback }),
2024
+ [allowConcurrentAudioPlayback],
2025
+ );
2026
+
2012
2027
  const messageComposerContext = useMemo(
2013
2028
  () => ({ channel, thread, threadInstance }),
2014
2029
  [channel, thread, threadInstance],
@@ -2047,10 +2062,12 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
2047
2062
  <AttachmentPickerProvider value={attachmentPickerContext}>
2048
2063
  <MessageComposerProvider value={messageComposerContext}>
2049
2064
  <MessageInputProvider value={inputMessageInputContext}>
2050
- <View style={{ height: '100%' }}>{children}</View>
2051
- {!disableAttachmentPicker && (
2052
- <AttachmentPicker ref={bottomSheetRef} {...attachmentPickerProps} />
2053
- )}
2065
+ <AudioPlayerProvider value={audioPlayerContext}>
2066
+ <View style={{ height: '100%' }}>{children}</View>
2067
+ {!disableAttachmentPicker && (
2068
+ <AttachmentPicker ref={bottomSheetRef} {...attachmentPickerProps} />
2069
+ )}
2070
+ </AudioPlayerProvider>
2054
2071
  </MessageInputProvider>
2055
2072
  </MessageComposerProvider>
2056
2073
  </AttachmentPickerProvider>
@@ -7,6 +7,7 @@ import type { MessageContextValue } from '../../../contexts/messageContext/Messa
7
7
  import type { MessagesContextValue } from '../../../contexts/messagesContext/MessagesContext';
8
8
 
9
9
  import { useTranslationContext } from '../../../contexts/translationContext/TranslationContext';
10
+ import { useTranslatedMessage } from '../../../hooks/useTranslatedMessage';
10
11
  import { NativeHandlers } from '../../../native';
11
12
 
12
13
  export const useMessageActionHandlers = ({
@@ -29,6 +30,7 @@ export const useMessageActionHandlers = ({
29
30
  Pick<MessageComposerAPIContextValue, 'setEditingState' | 'setQuotedMessage'>) => {
30
31
  const { t } = useTranslationContext();
31
32
  const handleResendMessage = () => retrySendMessage(message);
33
+ const translatedMessage = useTranslatedMessage(message);
32
34
 
33
35
  const handleQuotedReplyMessage = () => {
34
36
  setQuotedMessage(message);
@@ -42,7 +44,7 @@ export const useMessageActionHandlers = ({
42
44
  if (!message.text) {
43
45
  return;
44
46
  }
45
- NativeHandlers.setClipboardString(message.text);
47
+ NativeHandlers.setClipboardString(translatedMessage?.text ?? message.text);
46
48
  };
47
49
 
48
50
  const handleDeleteMessage = () => {
@@ -453,6 +453,12 @@ const MessageInputWithContext = (props: MessageInputPropsWithContext) => {
453
453
  paused={paused}
454
454
  position={position}
455
455
  progress={progress}
456
+ recordingDuration={recordingDuration}
457
+ uri={
458
+ typeof recording !== 'string'
459
+ ? (recording?.getURI() as string)
460
+ : (recording as string)
461
+ }
456
462
  waveformData={waveformData}
457
463
  />
458
464
  ) : micLocked ? (
@@ -10,6 +10,7 @@ import { DismissAttachmentUpload } from './DismissAttachmentUpload';
10
10
 
11
11
  import { AudioAttachment } from '../../../../components/Attachment/AudioAttachment';
12
12
  import { useChatContext } from '../../../../contexts/chatContext/ChatContext';
13
+ import { useMessageComposer } from '../../../../contexts/messageInputContext/hooks/useMessageComposer';
13
14
  import { AudioConfig, UploadAttachmentPreviewProps } from '../../../../types/types';
14
15
  import { getIndicatorTypeForFileState, ProgressIndicatorTypes } from '../../../../utils/utils';
15
16
 
@@ -17,9 +18,26 @@ export type AudioAttachmentUploadPreviewProps<CustomLocalMetadata = Record<strin
17
18
  UploadAttachmentPreviewProps<
18
19
  LocalAudioAttachment<CustomLocalMetadata> | LocalVoiceRecordingAttachment<CustomLocalMetadata>
19
20
  > & {
21
+ /**
22
+ * The audio attachment config
23
+ *
24
+ * @deprecated This is deprecated and will be removed in the future.
25
+ */
20
26
  audioAttachmentConfig: AudioConfig;
27
+ /**
28
+ * Callback to be called when the audio is loaded
29
+ * @deprecated This is deprecated and will be removed in the future.
30
+ */
21
31
  onLoad: (index: string, duration: number) => void;
32
+ /**
33
+ * Callback to be called when the audio is played or paused
34
+ * @deprecated This is deprecated and will be removed in the future.
35
+ */
22
36
  onPlayPause: (index: string, pausedStatus?: boolean) => void;
37
+ /**
38
+ * Callback to be called when the audio progresses
39
+ * @deprecated This is deprecated and will be removed in the future.
40
+ */
23
41
  onProgress: (index: string, progress: number) => void;
24
42
  };
25
43
 
@@ -37,15 +55,23 @@ export const AudioAttachmentUploadPreview = ({
37
55
  attachment.localMetadata.uploadState,
38
56
  enableOfflineSupport,
39
57
  );
58
+ const messageComposer = useMessageComposer();
59
+ const isDraft = messageComposer.draftId;
60
+ const isEditing = messageComposer.editedMessage;
61
+ const assetUrl =
62
+ (isDraft || isEditing
63
+ ? attachment.asset_url
64
+ : (attachment.localMetadata.file as FileReference).uri) ??
65
+ (attachment.localMetadata.file as FileReference).uri;
40
66
 
41
67
  const finalAttachment = useMemo(
42
68
  () => ({
43
69
  ...attachment,
44
- asset_url: attachment.asset_url ?? (attachment.localMetadata.file as FileReference).uri,
70
+ asset_url: assetUrl,
45
71
  id: attachment.localMetadata.id,
46
72
  ...audioAttachmentConfig,
47
73
  }),
48
- [attachment, audioAttachmentConfig],
74
+ [attachment, assetUrl, audioAttachmentConfig],
49
75
  );
50
76
 
51
77
  const onRetryHandler = useCallback(() => {
@@ -65,6 +91,7 @@ export const AudioAttachmentUploadPreview = ({
65
91
  >
66
92
  <AudioAttachment
67
93
  hideProgressBar={true}
94
+ isPreview={true}
68
95
  item={finalAttachment}
69
96
  onLoad={onLoad}
70
97
  onPlayPause={onPlayPause}
@@ -1,41 +1,91 @@
1
- import React from 'react';
1
+ import React, { useEffect, useMemo } from 'react';
2
2
  import { Pressable, StyleSheet, Text, View } from 'react-native';
3
3
 
4
4
  import dayjs from 'dayjs';
5
5
 
6
6
  import { useTheme } from '../../../../contexts/themeContext/ThemeContext';
7
+ import { useAudioPlayerControl } from '../../../../hooks/useAudioPlayerControl';
8
+ import { useStateStore } from '../../../../hooks/useStateStore';
7
9
  import { Pause, Play } from '../../../../icons';
8
10
 
11
+ import { NativeHandlers } from '../../../../native';
12
+ import { AudioPlayerState } from '../../../../state-store/audio-player';
9
13
  import { WaveProgressBar } from '../../../ProgressControl/WaveProgressBar';
10
14
 
15
+ const ONE_SECOND_IN_MILLISECONDS = 1000;
16
+ const ONE_HOUR_IN_MILLISECONDS = 3600 * 1000;
17
+
11
18
  export type AudioRecordingPreviewProps = {
19
+ recordingDuration: number;
20
+ uri: string;
21
+ /**
22
+ * The waveform data to be presented to show the audio levels.
23
+ */
24
+ waveformData: number[];
12
25
  /**
13
26
  * Boolean used to show the paused state of the player.
27
+ *
28
+ * @deprecated This is deprecated and will be removed in the future in favour of the global audio manager.
29
+ * FIXME: Remove this in the next major version.
14
30
  */
15
31
  paused: boolean;
16
32
  /**
17
33
  * Number used to show the current position of the audio being played.
34
+ *
35
+ * @deprecated This is deprecated and will be removed in the future in favour of the global audio manager.
36
+ * FIXME: Remove this in the next major version.
18
37
  */
19
38
  position: number;
20
39
  /**
21
40
  * Number used to show the percentage of progress of the audio being played. It should be in 0-1 range.
41
+ *
42
+ * @deprecated This is deprecated and will be removed in the future in favour of the global audio manager.
43
+ * FIXME: Remove this in the next major version.
22
44
  */
23
45
  progress: number;
24
- /**
25
- * The waveform data to be presented to show the audio levels.
26
- */
27
- waveformData: number[];
28
46
  /**
29
47
  * Function to play or pause the audio player.
48
+ *
49
+ * @deprecated This is deprecated and will be removed in the future in favour of the global audio manager.
50
+ * FIXME: Remove this in the next major version.
30
51
  */
31
52
  onVoicePlayerPlayPause?: () => Promise<void>;
32
53
  };
33
54
 
55
+ const audioPlayerSelector = (state: AudioPlayerState) => ({
56
+ duration: state.duration,
57
+ isPlaying: state.isPlaying,
58
+ position: state.position,
59
+ progress: state.progress,
60
+ });
61
+
34
62
  /**
35
63
  * Component displayed when the audio is recorded and can be previewed.
36
64
  */
37
65
  export const AudioRecordingPreview = (props: AudioRecordingPreviewProps) => {
38
- const { onVoicePlayerPlayPause, paused, position, progress, waveformData } = props;
66
+ const { recordingDuration, uri, waveformData } = props;
67
+
68
+ const audioPlayer = useAudioPlayerControl({
69
+ duration: recordingDuration / ONE_SECOND_IN_MILLISECONDS,
70
+ mimeType: 'audio/aac',
71
+ // This is a temporary flag to manage audio player for voice recording in preview as the one in message list uses react-native-video.
72
+ previewVoiceRecording: !(NativeHandlers.SDK === 'stream-chat-expo'),
73
+ type: 'voiceRecording',
74
+ uri,
75
+ });
76
+
77
+ const { duration, isPlaying, position, progress } = useStateStore(
78
+ audioPlayer.state,
79
+ audioPlayerSelector,
80
+ );
81
+
82
+ // When a audio attachment in preview is removed, we need to remove the player from the pool
83
+ useEffect(
84
+ () => () => {
85
+ audioPlayer.onRemove();
86
+ },
87
+ [audioPlayer],
88
+ );
39
89
 
40
90
  const {
41
91
  theme: {
@@ -53,11 +103,25 @@ export const AudioRecordingPreview = (props: AudioRecordingPreviewProps) => {
53
103
  },
54
104
  } = useTheme();
55
105
 
106
+ const handlePlayPause = () => {
107
+ audioPlayer.toggle();
108
+ };
109
+
110
+ const progressDuration = useMemo(
111
+ () =>
112
+ position
113
+ ? position / ONE_HOUR_IN_MILLISECONDS >= 1
114
+ ? dayjs.duration(position, 'milliseconds').format('HH:mm:ss')
115
+ : dayjs.duration(position, 'milliseconds').format('mm:ss')
116
+ : dayjs.duration(duration, 'milliseconds').format('mm:ss'),
117
+ [duration, position],
118
+ );
119
+
56
120
  return (
57
121
  <View style={[styles.container, container]}>
58
122
  <View style={[styles.infoContainer, infoContainer]}>
59
- <Pressable onPress={onVoicePlayerPlayPause}>
60
- {paused ? (
123
+ <Pressable onPress={handlePlayPause}>
124
+ {!isPlaying ? (
61
125
  <Play fill={accent_blue} height={32} width={32} {...playIcon} />
62
126
  ) : (
63
127
  <Pause fill={accent_blue} height={32} width={32} {...pauseIcon} />
@@ -65,7 +129,7 @@ export const AudioRecordingPreview = (props: AudioRecordingPreviewProps) => {
65
129
  </Pressable>
66
130
  {/* `durationMillis` is for Expo apps, `currentPosition` is for Native CLI apps. */}
67
131
  <Text style={[styles.currentTime, { color: grey_dark }, currentTime]}>
68
- {dayjs.duration(position).format('mm:ss')}
132
+ {progressDuration}
69
133
  </Text>
70
134
  </View>
71
135
  <View style={[styles.progressBar, progressBar]}>
@@ -4,6 +4,7 @@ import { Alert, Platform } from 'react-native';
4
4
 
5
5
  import { LocalVoiceRecordingAttachment } from 'stream-chat';
6
6
 
7
+ import { useActiveAudioPlayer } from '../../../contexts/audioPlayerContext/AudioPlayerContext';
7
8
  import { useMessageComposer } from '../../../contexts/messageInputContext/hooks/useMessageComposer';
8
9
 
9
10
  import { useMessageInputContext } from '../../../contexts/messageInputContext/MessageInputContext';
@@ -24,12 +25,29 @@ export type RecordingStatusStates = 'idle' | 'recording' | 'stopped';
24
25
 
25
26
  /**
26
27
  * The hook that controls all the async audio core features including start/stop or recording, player, upload/delete of the recorded audio.
28
+ *
29
+ * FIXME: Change the name to `useAudioRecorder` in the next major version as the hook will only be used for audio recording.
27
30
  */
28
31
  export const useAudioController = () => {
29
32
  const [micLocked, setMicLocked] = useState(false);
30
33
  const [permissionsGranted, setPermissionsGranted] = useState(true);
34
+ /**
35
+ * @deprecated This is deprecated and will be removed in the future in favour of the global audio manager.
36
+ * Check: https://github.com/GetStream/stream-chat-react-native/blob/develop/package/src/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.tsx on how to use the global audio manager.
37
+ * FIXME: Remove this in the next major version.
38
+ */
31
39
  const [paused, setPaused] = useState<boolean>(true);
40
+ /**
41
+ * @deprecated This is deprecated and will be removed in the future in favour of the global audio manager.
42
+ * Check: https://github.com/GetStream/stream-chat-react-native/blob/develop/package/src/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.tsx on how to use the global audio manager.
43
+ * FIXME: Remove this in the next major version.
44
+ */
32
45
  const [position, setPosition] = useState<number>(0);
46
+ /**
47
+ * @deprecated This is deprecated and will be removed in the future in favour of the global audio manager.
48
+ * Check: https://github.com/GetStream/stream-chat-react-native/blob/develop/package/src/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.tsx on how to use the global audio manager.
49
+ * FIXME: Remove this in the next major version.
50
+ */
33
51
  const [progress, setProgress] = useState<number>(0);
34
52
  const [waveformData, setWaveformData] = useState<number[]>([]);
35
53
  const [isScheduledForSubmit, setIsScheduleForSubmit] = useState(false);
@@ -37,10 +55,18 @@ export const useAudioController = () => {
37
55
  const [recordingDuration, setRecordingDuration] = useState<number>(0);
38
56
  const [recordingStatus, setRecordingStatus] = useState<RecordingStatusStates>('idle');
39
57
  const { attachmentManager } = useMessageComposer();
58
+ const activeAudioPlayer = useActiveAudioPlayer();
40
59
 
41
60
  const { sendMessage } = useMessageInputContext();
42
61
 
43
- // For playback support in Expo CLI apps
62
+ /**
63
+ * Reference to the sound object for playback support in Expo CLI apps
64
+ *
65
+ * @deprecated This is deprecated and will be removed in the future in favour of the global audio manager.
66
+ * Check: https://github.com/GetStream/stream-chat-react-native/blob/develop/package/src/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.tsx on how to use the global audio manager.
67
+ *
68
+ * FIXME: Remove this in the next major version.
69
+ */
44
70
  const soundRef = useRef<SoundReturnType | null>(null);
45
71
 
46
72
  // This effect stop the player from playing and stops audio recording on
@@ -60,6 +86,12 @@ export const useAudioController = () => {
60
86
  }
61
87
  }, [isScheduledForSubmit, sendMessage]);
62
88
 
89
+ /**
90
+ * Function to update the progress of the voice recording.
91
+ *
92
+ * @deprecated This is deprecated and will be removed in the future in favour of the global audio manager.
93
+ * FIXME: Remove this in the next major version.
94
+ */
63
95
  const onVoicePlayerProgressHandler = (currentPosition: number, playbackDuration: number) => {
64
96
  const currentProgress = currentPosition / playbackDuration;
65
97
  if (currentProgress === 1) {
@@ -70,6 +102,14 @@ export const useAudioController = () => {
70
102
  }
71
103
  };
72
104
 
105
+ /**
106
+ * Function to update the playback status of the voice recording.
107
+ *
108
+ * @deprecated This is deprecated and will be removed in the future in favour of the global audio manager.
109
+ * Check: https://github.com/GetStream/stream-chat-react-native/blob/develop/package/src/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.tsx on how to use the global audio manager.
110
+ *
111
+ * FIXME: Remove this in the next major version.
112
+ */
73
113
  const onVoicePlayerPlaybackStatusUpdate = (status: PlaybackStatus) => {
74
114
  if (status.shouldPlay === undefined || status.shouldPlay === true) {
75
115
  setPosition(status?.currentPosition || status?.positionMillis);
@@ -90,6 +130,14 @@ export const useAudioController = () => {
90
130
  }
91
131
  };
92
132
 
133
+ /**
134
+ * Function to play or pause voice recording.
135
+ *
136
+ * @deprecated This is deprecated and will be removed in the future in favour of the global audio manager.
137
+ * Check: https://github.com/GetStream/stream-chat-react-native/blob/develop/package/src/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.tsx on how to use the global audio manager.
138
+ *
139
+ * FIXME: Remove this in the next major version.
140
+ */
93
141
  const onVoicePlayerPlayPause = async () => {
94
142
  if (paused) {
95
143
  if (progress === 0) {
@@ -119,6 +167,11 @@ export const useAudioController = () => {
119
167
 
120
168
  /**
121
169
  * Function to start playing voice recording to preview it after recording.
170
+ *
171
+ * @deprecated This is deprecated and will be removed in the future in favour of the global audio manager.
172
+ * Check: https://github.com/GetStream/stream-chat-react-native/blob/develop/package/src/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.tsx on how to use the global audio manager.
173
+ *
174
+ * FIXME: Remove this in the next major version.
122
175
  */
123
176
  const startVoicePlayer = async () => {
124
177
  if (!recording) {
@@ -150,6 +203,11 @@ export const useAudioController = () => {
150
203
 
151
204
  /**
152
205
  * Function to stop playing voice recording.
206
+ *
207
+ * @deprecated This is deprecated and will be removed in the future in favour of the global audio manager.
208
+ * Check: https://github.com/GetStream/stream-chat-react-native/blob/develop/package/src/components/MessageInput/components/AudioRecorder/AudioRecordingPreview.tsx on how to use the global audio manager.
209
+ *
210
+ * FIXME: Remove this in the next major version.
153
211
  */
154
212
  const stopVoicePlayer = async () => {
155
213
  // For Native CLI
@@ -200,6 +258,9 @@ export const useAudioController = () => {
200
258
  }
201
259
  setRecording(recording);
202
260
  setRecordingStatus('recording');
261
+ if (activeAudioPlayer?.isPlaying) {
262
+ await activeAudioPlayer?.pause();
263
+ }
203
264
  await stopVoicePlayer();
204
265
  } else {
205
266
  setPermissionsGranted(false);
@@ -8,6 +8,8 @@ import { AudioConfig } from '../../../types/types';
8
8
  * Manages the state of audio attachments for preview and playback.
9
9
  * @param files The audio files to manage.
10
10
  * @returns An object containing the state and handlers for audio attachments.
11
+ *
12
+ * @deprecated This is deprecated and will be removed in the future.
11
13
  */
12
14
  export const useAudioPreviewManager = (files: LocalAttachment[]) => {
13
15
  const [audioAttachmentsStateMap, setAudioAttachmentsStateMap] = useState<
@@ -34,8 +36,15 @@ export const useAudioPreviewManager = (files: LocalAttachment[]) => {
34
36
  });
35
37
  }, [files]);
36
38
 
37
- // Handler triggered when an audio is loaded in the message input. The initial state is defined for the audio here
38
- // and the duration is set.
39
+ /**
40
+ * Handler triggered when an audio is loaded in the message input. The initial state is defined for the audio here
41
+ * and the duration is set.
42
+ * @param index - The index of the audio
43
+ * @param duration - The duration of the audio
44
+ *
45
+ * @deprecated This is deprecated and will be removed in the future.
46
+ * FIXME: Remove this in the next major version.
47
+ */
39
48
  const onLoad = useCallback((index: string, duration: number) => {
40
49
  setAudioAttachmentsStateMap((prevState) => ({
41
50
  ...prevState,
@@ -46,8 +55,15 @@ export const useAudioPreviewManager = (files: LocalAttachment[]) => {
46
55
  }));
47
56
  }, []);
48
57
 
49
- // The handler which is triggered when the audio progresses/ the thumb is dragged in the progress control. The
50
- // progressed duration is set here.
58
+ /**
59
+ * Handler which is triggered when the audio progresses/ the thumb is dragged in the progress control. The
60
+ * progressed duration is set here.
61
+ * @param index - The index of the audio
62
+ * @param progress - The progress of the audio
63
+ *
64
+ * @deprecated This is deprecated and will be removed in the future.
65
+ * FIXME: Remove this in the next major version.
66
+ */
51
67
  const onProgress = useCallback((index: string, progress: number) => {
52
68
  setAudioAttachmentsStateMap((prevState) => ({
53
69
  ...prevState,
@@ -58,7 +74,14 @@ export const useAudioPreviewManager = (files: LocalAttachment[]) => {
58
74
  }));
59
75
  }, []);
60
76
 
61
- // The handler which controls or sets the paused/played state of the audio.
77
+ /**
78
+ * Handler which controls or sets the paused/played state of the audio.
79
+ * @param index - The index of the audio
80
+ * @param pausedStatus - The paused status of the audio
81
+ *
82
+ * @deprecated This is deprecated and will be removed in the future.
83
+ * FIXME: Remove this in the next major version.
84
+ */
62
85
  const onPlayPause = useCallback((index: string, pausedStatus?: boolean) => {
63
86
  if (pausedStatus === false) {
64
87
  // In this case, all others except the index are set to paused.
@@ -1,12 +1,18 @@
1
- import React, { useState } from 'react';
1
+ import React, { useMemo, useState } from 'react';
2
2
  import { StyleSheet, View } from 'react-native';
3
3
  import { Gesture, GestureDetector } from 'react-native-gesture-handler';
4
- import Animated, { runOnJS, useAnimatedStyle, useSharedValue } from 'react-native-reanimated';
4
+ import Animated, {
5
+ runOnJS,
6
+ useAnimatedReaction,
7
+ useAnimatedStyle,
8
+ useSharedValue,
9
+ } from 'react-native-reanimated';
5
10
 
6
11
  import { useTheme } from '../../contexts/themeContext/ThemeContext';
7
12
 
8
13
  export type ProgressControlProps = {
9
14
  /**
15
+ * @deprecated unused prop.
10
16
  * The duration of the audio in seconds
11
17
  */
12
18
  duration: number;
@@ -33,6 +39,7 @@ export type ProgressControlProps = {
33
39
  onPlayPause?: (status?: boolean) => void;
34
40
  /**
35
41
  * The function to be called when the user is dragging the progress bar
42
+ * @deprecated This is not used anymore and is handled locally
36
43
  */
37
44
  onProgressDrag?: (progress: number) => void;
38
45
  /**
@@ -67,17 +74,9 @@ const ProgressControlThumb = () => {
67
74
 
68
75
  export const ProgressControl = (props: ProgressControlProps) => {
69
76
  const [widthInNumbers, setWidthInNumbers] = useState(0);
70
- const {
71
- filledColor: filledColorFromProp,
72
- onEndDrag,
73
- onPlayPause,
74
- onProgressDrag,
75
- onStartDrag,
76
- progress,
77
- testID,
78
- } = props;
77
+ const { filledColor: filledColorFromProp, onEndDrag, onStartDrag, progress, testID } = props;
79
78
 
80
- const progressValue = useSharedValue(progress);
79
+ const state = useSharedValue(progress);
81
80
  const {
82
81
  theme: {
83
82
  colors: { grey_dark },
@@ -85,51 +84,50 @@ export const ProgressControl = (props: ProgressControlProps) => {
85
84
  },
86
85
  } = useTheme();
87
86
 
88
- const pan = Gesture.Pan()
89
- .maxPointers(1)
90
- .onStart((event) => {
91
- const currentProgress = (progressValue.value + event.x) / widthInNumbers;
92
- progressValue.value = Math.max(0, Math.min(currentProgress, 1));
93
- if (onStartDrag) {
94
- runOnJS(onStartDrag)(progressValue.value);
95
- }
96
- if (onPlayPause) {
97
- runOnJS(onPlayPause)(true);
98
- }
99
- })
100
- .onUpdate((event) => {
101
- const currentProgress = (progressValue.value + event.x) / widthInNumbers;
102
- progressValue.value = Math.max(0, Math.min(currentProgress, 1));
103
- if (onProgressDrag) {
104
- runOnJS(onProgressDrag)(progressValue.value);
105
- }
106
- })
107
- .onEnd((event) => {
108
- const currentProgress = (progressValue.value + event.x) / widthInNumbers;
109
- progressValue.value = Math.max(0, Math.min(currentProgress, 1));
110
- if (onEndDrag) {
111
- runOnJS(onEndDrag)(progressValue.value);
112
- }
113
- if (onPlayPause) {
114
- runOnJS(onPlayPause)(false);
115
- }
116
- })
117
- .withTestId(testID);
87
+ useAnimatedReaction(
88
+ () => progress,
89
+ (newProgress) => {
90
+ state.value = newProgress;
91
+ },
92
+ [progress],
93
+ );
94
+
95
+ const pan = useMemo(
96
+ () =>
97
+ Gesture.Pan()
98
+ .maxPointers(1)
99
+ .onStart(() => {
100
+ if (onStartDrag) {
101
+ runOnJS(onStartDrag)(state.value);
102
+ }
103
+ })
104
+ .onUpdate((event) => {
105
+ const newProgress = Math.max(0, Math.min(event.x / widthInNumbers, 1));
106
+ state.value = newProgress;
107
+ })
108
+ .onEnd(() => {
109
+ if (onEndDrag) {
110
+ runOnJS(onEndDrag)(state.value);
111
+ }
112
+ })
113
+ .withTestId(testID),
114
+ [onEndDrag, onStartDrag, state, testID, widthInNumbers],
115
+ );
118
116
 
119
117
  const filledColor = filledColorFromProp || filledColorFromTheme;
120
118
 
121
119
  const thumbStyles = useAnimatedStyle(
122
120
  () => ({
123
- transform: [{ translateX: progress * widthInNumbers - THUMB_WIDTH / 2 }],
121
+ transform: [{ translateX: state.value * widthInNumbers - THUMB_WIDTH / 2 }],
124
122
  }),
125
- [progress],
123
+ [widthInNumbers],
126
124
  );
127
125
 
128
126
  const animatedFilledStyles = useAnimatedStyle(
129
127
  () => ({
130
- width: progress * widthInNumbers,
128
+ width: state.value * widthInNumbers,
131
129
  }),
132
- [progress],
130
+ [widthInNumbers],
133
131
  );
134
132
 
135
133
  return (
@@ -151,7 +149,7 @@ export const ProgressControl = (props: ProgressControlProps) => {
151
149
  ]}
152
150
  />
153
151
  <Animated.View style={[thumbStyles, thumb]}>
154
- {onEndDrag || onProgressDrag ? <ProgressControlThumb /> : null}
152
+ {onEndDrag ? <ProgressControlThumb /> : null}
155
153
  </Animated.View>
156
154
  </View>
157
155
  </GestureDetector>