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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/state-store/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,qBAAqB,CAAC"}
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "stream-chat-react-native-core",
3
3
  "description": "The official React Native and Expo components for Stream Chat, a service for building chat applications",
4
- "version": "8.10.1",
4
+ "version": "8.11.0-beta.3",
5
5
  "author": {
6
6
  "company": "Stream.io Inc",
7
7
  "name": "Stream.io Inc"
8
8
  },
9
9
  "repository": {
10
10
  "type": "git",
11
- "url": "https://github.com/GetStream/stream-chat-react-native.git"
11
+ "url": "https://github.com/GetStream/stream-chat-react-native.git",
12
+ "directory": "package"
12
13
  },
13
14
  "license": "SEE LICENSE IN LICENSE",
14
15
  "main": "lib/commonjs/index.js",
@@ -24,7 +25,7 @@
24
25
  "scripts": {
25
26
  "install-all": "(yarn install --force && (cd native-package && yarn install --force) && (cd expo-package && yarn install --force))",
26
27
  "build": "rimraf lib && yarn run --silent build-translations && bob build && yarn run --silent copy-translations",
27
- "build-translations": "i18next",
28
+ "build-translations": "i18next-cli sync",
28
29
  "copy-translations": "echo '\u001b[34mℹ\u001b[0m Copying translation files to \u001b[34mlib/typescript/i18n\u001b[0m' && cp -R -f ./src/i18n ./lib/typescript/i18n && echo '\u001b[32m✓\u001b[0m Done Copying Translations'",
29
30
  "eslint": "eslint 'src/**/*.{js,md,ts,jsx,tsx}' --max-warnings 0",
30
31
  "lint": "prettier --ignore-path ../.prettierignore --list-different 'src/**/*.{js,ts,tsx,md,json}' eslint.config.mjs ../.prettierrc babel.config.js && eslint 'src/**/*.{js,ts,tsx,md}' --max-warnings 0 && yarn run validate-translations",
@@ -112,11 +113,11 @@
112
113
  "@babel/core": "^7.27.4",
113
114
  "@babel/runtime": "^7.27.6",
114
115
  "@op-engineering/op-sqlite": "^14.0.3",
115
- "@shopify/flash-list": "^2.1.0",
116
116
  "@react-native-community/eslint-config": "3.2.0",
117
117
  "@react-native-community/eslint-plugin": "1.3.0",
118
118
  "@react-native-community/netinfo": "^11.4.1",
119
119
  "@react-native/babel-preset": "0.79.3",
120
+ "@shopify/flash-list": "^2.1.0",
120
121
  "@testing-library/jest-native": "^5.4.3",
121
122
  "@testing-library/react-native": "13.2.0",
122
123
  "@types/better-sqlite3": "^7.6.13",
@@ -145,7 +146,7 @@
145
146
  "eslint-plugin-react": "^7.37.5",
146
147
  "eslint-plugin-react-hooks": "^5.2.0",
147
148
  "eslint-plugin-react-native": "^5.0.0",
148
- "i18next-parser": "^9.3.0",
149
+ "i18next-cli": "^1.31.0",
149
150
  "jest": "^30.0.0",
150
151
  "moment-timezone": "^0.6.0",
151
152
  "prettier": "^3.5.3",
@@ -1,254 +1,179 @@
1
- import React, { RefObject, useEffect, useMemo, useState } from 'react';
1
+ import React, { RefObject, useEffect, useMemo } from 'react';
2
2
  import { I18nManager, Pressable, StyleSheet, Text, View } from 'react-native';
3
3
 
4
4
  import dayjs from 'dayjs';
5
5
  import duration from 'dayjs/plugin/duration';
6
6
 
7
- import { AudioAttachment as StreamAudioAttachment } from 'stream-chat';
7
+ import {
8
+ isVoiceRecordingAttachment,
9
+ LocalMessage,
10
+ AudioAttachment as StreamAudioAttachment,
11
+ VoiceRecordingAttachment as StreamVoiceRecordingAttachment,
12
+ } from 'stream-chat';
8
13
 
9
14
  import { useTheme } from '../../contexts';
10
- import { useAudioPlayer } from '../../hooks/useAudioPlayer';
15
+ import { useStateStore } from '../../hooks';
16
+ import { useAudioPlayerControl } from '../../hooks/useAudioPlayerControl';
11
17
  import { Audio, Pause, Play } from '../../icons';
12
18
  import {
13
19
  NativeHandlers,
14
- PlaybackStatus,
15
20
  SoundReturnType,
16
21
  VideoPayloadData,
17
22
  VideoProgressData,
18
23
  VideoSeekResponse,
19
24
  } from '../../native';
20
- import { AudioConfig, FileTypes } from '../../types/types';
25
+ import { AudioPlayerState } from '../../state-store/audio-player';
26
+ import { AudioConfig } from '../../types/types';
21
27
  import { getTrimmedAttachmentTitle } from '../../utils/getTrimmedAttachmentTitle';
22
28
  import { ProgressControl } from '../ProgressControl/ProgressControl';
23
29
  import { WaveProgressBar } from '../ProgressControl/WaveProgressBar';
24
30
 
31
+ const ONE_HOUR_IN_MILLISECONDS = 3600 * 1000;
32
+ const ONE_SECOND_IN_MILLISECONDS = 1000;
33
+
25
34
  dayjs.extend(duration);
26
35
 
27
36
  export type AudioAttachmentType = AudioConfig &
28
- Pick<StreamAudioAttachment, 'waveform_data' | 'asset_url' | 'title'> & {
37
+ Pick<
38
+ StreamAudioAttachment | StreamVoiceRecordingAttachment,
39
+ 'waveform_data' | 'asset_url' | 'title' | 'mime_type'
40
+ > & {
29
41
  id: string;
30
42
  type: 'audio' | 'voiceRecording';
31
43
  };
32
44
 
33
45
  export type AudioAttachmentProps = {
34
46
  item: AudioAttachmentType;
35
- onLoad: (index: string, duration: number) => void;
36
- onPlayPause: (index: string, pausedStatus?: boolean) => void;
37
- onProgress: (index: string, progress: number) => void;
47
+ message?: LocalMessage;
38
48
  titleMaxLength?: number;
39
49
  hideProgressBar?: boolean;
50
+ /**
51
+ * If true, the speed settings button will be shown.
52
+ */
40
53
  showSpeedSettings?: boolean;
41
54
  testID?: string;
55
+ /**
56
+ * If true, the audio attachment is in preview mode in the message input.
57
+ */
58
+ isPreview?: boolean;
59
+ /**
60
+ * Callback to be called when the audio is loaded
61
+ * @deprecated This is deprecated and will be removed in the future.
62
+ */
63
+ onLoad?: (index: string, duration: number) => void;
64
+ /**
65
+ * Callback to be called when the audio is played or paused
66
+ * @deprecated This is deprecated and will be removed in the future.
67
+ */
68
+ onPlayPause?: (index: string, pausedStatus?: boolean) => void;
69
+ /**
70
+ * Callback to be called when the audio progresses
71
+ * @deprecated This is deprecated and will be removed in the future.
72
+ */
73
+ onProgress?: (index: string, progress: number) => void;
42
74
  };
43
75
 
76
+ const audioPlayerSelector = (state: AudioPlayerState) => ({
77
+ currentPlaybackRate: state.currentPlaybackRate,
78
+ duration: state.duration,
79
+ isPlaying: state.isPlaying,
80
+ position: state.position,
81
+ progress: state.progress,
82
+ });
83
+
44
84
  /**
45
85
  * AudioAttachment
46
86
  * UI Component to preview the audio files
47
87
  */
48
88
  export const AudioAttachment = (props: AudioAttachmentProps) => {
49
- const [currentSpeed, setCurrentSpeed] = useState<number>(1.0);
50
- const [audioFinished, setAudioFinished] = useState(false);
51
89
  const soundRef = React.useRef<SoundReturnType | null>(null);
90
+
52
91
  const {
53
92
  hideProgressBar = false,
54
93
  item,
55
- onLoad,
56
- onPlayPause,
57
- onProgress,
94
+ message,
58
95
  showSpeedSettings = false,
59
96
  testID,
60
97
  titleMaxLength,
98
+ isPreview = false,
61
99
  } = props;
62
- const { changeAudioSpeed, pauseAudio, playAudio, seekAudio } = useAudioPlayer({ soundRef });
63
- const isExpoCLI = NativeHandlers.SDK === 'stream-chat-expo';
64
- const isVoiceRecording = item.type === FileTypes.VoiceRecording;
100
+ const isVoiceRecording = isVoiceRecordingAttachment(item);
101
+
102
+ const audioPlayer = useAudioPlayerControl({
103
+ duration: item.duration ?? 0,
104
+ mimeType: item.mime_type ?? '',
105
+ requester: isPreview
106
+ ? 'preview'
107
+ : message?.id && `${message?.parent_id ?? message?.id}${message?.id}`,
108
+ type: isVoiceRecording ? 'voiceRecording' : 'audio',
109
+ uri: item.asset_url ?? '',
110
+ });
111
+ const { duration, isPlaying, position, progress, currentPlaybackRate } = useStateStore(
112
+ audioPlayer.state,
113
+ audioPlayerSelector,
114
+ );
115
+
116
+ // Initialize the player for native cli apps
117
+ useEffect(() => {
118
+ if (soundRef.current) {
119
+ audioPlayer.initPlayer({ playerRef: soundRef.current });
120
+ }
121
+ }, [audioPlayer]);
122
+
123
+ // When a audio attachment in preview is removed, we need to remove the player from the pool
124
+ useEffect(
125
+ () => () => {
126
+ if (isPreview) {
127
+ audioPlayer.onRemove();
128
+ }
129
+ },
130
+ [audioPlayer, isPreview],
131
+ );
65
132
 
66
133
  /** This is for Native CLI Apps */
67
134
  const handleLoad = (payload: VideoPayloadData) => {
68
- // The duration given by the rn-video is not same as the one of the voice recording, so we take the actual duration for voice recording.
69
- if (isVoiceRecording && item.duration) {
70
- onLoad(item.id, item.duration);
71
- } else {
72
- onLoad(item.id, item.duration || payload.duration);
135
+ // If the attachment is a voice recording, we rely on the duration from the attachment as the one from the react-native-video is incorrect.
136
+ if (isVoiceRecording) {
137
+ return;
73
138
  }
139
+ audioPlayer.duration = payload.duration * ONE_SECOND_IN_MILLISECONDS;
74
140
  };
75
141
 
76
142
  /** This is for Native CLI Apps */
77
143
  const handleProgress = (data: VideoProgressData) => {
78
- const { currentTime, seekableDuration } = data;
79
- // The duration given by the rn-video is not same as the one of the voice recording, so we take the actual duration for voice recording.
80
- if (isVoiceRecording && item.duration) {
81
- if (currentTime < item.duration && !audioFinished) {
82
- onProgress(item.id, currentTime / item.duration);
83
- } else {
84
- setAudioFinished(true);
85
- }
86
- } else {
87
- if (currentTime < seekableDuration && !audioFinished) {
88
- onProgress(item.id, currentTime / seekableDuration);
89
- } else {
90
- setAudioFinished(true);
91
- }
92
- }
144
+ const { currentTime } = data;
145
+ audioPlayer.position = currentTime * ONE_SECOND_IN_MILLISECONDS;
93
146
  };
94
147
 
95
148
  /** This is for Native CLI Apps */
96
149
  const onSeek = (seekResponse: VideoSeekResponse) => {
97
- setAudioFinished(false);
98
- onProgress(item.id, seekResponse.currentTime / (item.duration as number));
150
+ audioPlayer.position = seekResponse.currentTime * ONE_SECOND_IN_MILLISECONDS;
99
151
  };
100
152
 
101
- const handlePlayPause = async () => {
102
- if (item.paused) {
103
- if (isExpoCLI) {
104
- await playAudio();
105
- }
106
- onPlayPause(item.id, false);
107
- } else {
108
- if (isExpoCLI) {
109
- await pauseAudio();
110
- }
111
- onPlayPause(item.id, true);
112
- }
153
+ const handlePlayPause = () => {
154
+ audioPlayer.toggle();
113
155
  };
114
156
 
115
157
  const handleEnd = async () => {
116
- setAudioFinished(false);
117
- await pauseAudio();
118
- onPlayPause(item.id, true);
119
- await seekAudio(0);
120
- };
121
-
122
- const dragStart = async () => {
123
- if (isExpoCLI) {
124
- await pauseAudio();
125
- }
126
- onPlayPause(item.id, true);
158
+ await audioPlayer.stop();
127
159
  };
128
160
 
129
- const dragProgress = (progress: number) => {
130
- onProgress(item.id, progress);
161
+ const dragStart = () => {
162
+ audioPlayer.pause();
131
163
  };
132
164
 
133
- const dragEnd = async (progress: number) => {
134
- await seekAudio(progress * (item.duration as number));
135
- if (isExpoCLI) {
136
- await playAudio();
137
- }
138
- onPlayPause(item.id, false);
165
+ const dragProgress = (currentProgress: number) => {
166
+ audioPlayer.progress = currentProgress;
139
167
  };
140
168
 
141
- /** For Expo CLI */
142
- const onPlaybackStatusUpdate = (playbackStatus: PlaybackStatus) => {
143
- if (!playbackStatus.isLoaded) {
144
- // Update your UI for the unloaded state
145
- if (playbackStatus.error) {
146
- console.log(`Encountered a fatal error during playback: ${playbackStatus.error}`);
147
- }
148
- } else {
149
- const { durationMillis, positionMillis } = playbackStatus;
150
- // This is done for Expo CLI where we don't get file duration from file picker
151
- if (item.duration === 0) {
152
- onLoad(item.id, durationMillis / 1000);
153
- } else {
154
- // The duration given by the expo-av is not same as the one of the voice recording, so we take the actual duration for voice recording.
155
- if (isVoiceRecording && item.duration) {
156
- onLoad(item.id, item.duration);
157
- } else {
158
- onLoad(item.id, durationMillis / 1000);
159
- }
160
- }
161
- // Update your UI for the loaded state
162
- if (playbackStatus.isPlaying) {
163
- if (isVoiceRecording && item.duration) {
164
- if (positionMillis <= item.duration * 1000) {
165
- onProgress(item.id, positionMillis / (item.duration * 1000));
166
- }
167
- } else {
168
- if (positionMillis <= durationMillis) {
169
- onProgress(item.id, positionMillis / durationMillis);
170
- }
171
- }
172
- } else {
173
- // Update your UI for the paused state
174
- }
175
-
176
- if (playbackStatus.isBuffering) {
177
- // Update your UI for the buffering state
178
- }
179
-
180
- if (playbackStatus.didJustFinish && !playbackStatus.isLooping) {
181
- onProgress(item.id, 1);
182
- // The player has just finished playing and will stop. Maybe you want to play something else?
183
- // status: opposite of pause,says i am playing
184
- handleEnd();
185
- }
186
- }
169
+ const dragEnd = async (currentProgress: number) => {
170
+ const positionInSeconds = (currentProgress * duration) / ONE_SECOND_IN_MILLISECONDS;
171
+ await audioPlayer.seek(positionInSeconds);
172
+ audioPlayer.play();
187
173
  };
188
174
 
189
- // This is for Expo CLI, sound initialization is done here.
190
- useEffect(() => {
191
- if (isExpoCLI) {
192
- const initiateSound = async () => {
193
- if (item && item.asset_url && NativeHandlers.Sound?.initializeSound) {
194
- soundRef.current = await NativeHandlers.Sound.initializeSound(
195
- { uri: item.asset_url },
196
- {
197
- pitchCorrectionQuality: 'high',
198
- progressUpdateIntervalMillis: 100,
199
- shouldCorrectPitch: true,
200
- },
201
- onPlaybackStatusUpdate,
202
- );
203
- }
204
- };
205
- initiateSound();
206
- }
207
-
208
- return () => {
209
- if (soundRef.current?.stopAsync && soundRef.current.unloadAsync) {
210
- soundRef.current.stopAsync();
211
- soundRef.current.unloadAsync();
212
- }
213
- };
214
- // eslint-disable-next-line react-hooks/exhaustive-deps
215
- }, []);
216
-
217
- // This is needed for expo applications where the rerender doesn't occur on time thefore you need to update the state of the sound.
218
- useEffect(() => {
219
- const initalPlayPause = async () => {
220
- if (!isExpoCLI) {
221
- return;
222
- }
223
- try {
224
- if (item.paused) {
225
- await pauseAudio();
226
- } else {
227
- await playAudio();
228
- }
229
- } catch (e) {
230
- console.log('An error has occurred while trying to interact with the audio. ', e);
231
- }
232
- };
233
- // For expo CLI
234
- if (!NativeHandlers.Sound?.Player) {
235
- initalPlayPause();
236
- }
237
- }, [item.paused, isExpoCLI, pauseAudio, playAudio]);
238
-
239
175
  const onSpeedChangeHandler = async () => {
240
- if (currentSpeed === 2.0) {
241
- setCurrentSpeed(1.0);
242
- await changeAudioSpeed(1.0);
243
- } else {
244
- if (currentSpeed === 1.0) {
245
- setCurrentSpeed(1.5);
246
- await changeAudioSpeed(1.5);
247
- } else if (currentSpeed === 1.5) {
248
- setCurrentSpeed(2.0);
249
- await changeAudioSpeed(2.0);
250
- }
251
- }
176
+ await audioPlayer.changePlaybackRate();
252
177
  };
253
178
 
254
179
  const {
@@ -270,19 +195,14 @@ export const AudioAttachment = (props: AudioAttachmentProps) => {
270
195
  },
271
196
  } = useTheme();
272
197
 
273
- const progressValueInSeconds = useMemo(
274
- () => (item.duration as number) * (item.progress as number),
275
- [item.duration, item.progress],
276
- );
277
-
278
198
  const progressDuration = useMemo(
279
199
  () =>
280
- progressValueInSeconds
281
- ? progressValueInSeconds / 3600 >= 1
282
- ? dayjs.duration(progressValueInSeconds, 'second').format('HH:mm:ss')
283
- : dayjs.duration(progressValueInSeconds, 'second').format('mm:ss')
284
- : dayjs.duration(item.duration ?? 0, 'second').format('mm:ss'),
285
- [progressValueInSeconds, item.duration],
200
+ position
201
+ ? position / ONE_HOUR_IN_MILLISECONDS >= 1
202
+ ? dayjs.duration(position, 'milliseconds').format('HH:mm:ss')
203
+ : dayjs.duration(position, 'milliseconds').format('mm:ss')
204
+ : dayjs.duration(duration, 'milliseconds').format('mm:ss'),
205
+ [duration, position],
286
206
  );
287
207
 
288
208
  return (
@@ -308,7 +228,7 @@ export const AudioAttachment = (props: AudioAttachmentProps) => {
308
228
  playPauseButton,
309
229
  ]}
310
230
  >
311
- {item.paused ? (
231
+ {!isPlaying ? (
312
232
  <Play fill={static_black} height={32} width={32} />
313
233
  ) : (
314
234
  <Pause fill={static_black} height={32} width={32} />
@@ -328,7 +248,7 @@ export const AudioAttachment = (props: AudioAttachmentProps) => {
328
248
  filenameText,
329
249
  ]}
330
250
  >
331
- {item.type === FileTypes.VoiceRecording
251
+ {isVoiceRecordingAttachment(item)
332
252
  ? 'Recording'
333
253
  : getTrimmedAttachmentTitle(item.title, titleMaxLength)}
334
254
  </Text>
@@ -344,17 +264,17 @@ export const AudioAttachment = (props: AudioAttachmentProps) => {
344
264
  onEndDrag={dragEnd}
345
265
  onProgressDrag={dragProgress}
346
266
  onStartDrag={dragStart}
347
- progress={item.progress as number}
267
+ progress={progress}
348
268
  waveformData={item.waveform_data}
349
269
  />
350
270
  ) : (
351
271
  <ProgressControl
352
- duration={item.duration as number}
272
+ duration={duration}
353
273
  filledColor={accent_blue}
354
274
  onEndDrag={dragEnd}
355
275
  onProgressDrag={dragProgress}
356
276
  onStartDrag={dragStart}
357
- progress={item.progress as number}
277
+ progress={progress}
358
278
  testID='progress-control'
359
279
  />
360
280
  )}
@@ -367,8 +287,8 @@ export const AudioAttachment = (props: AudioAttachmentProps) => {
367
287
  onLoad={handleLoad}
368
288
  onProgress={handleProgress}
369
289
  onSeek={onSeek}
370
- paused={item.paused}
371
- rate={currentSpeed}
290
+ paused={!isPlaying}
291
+ rate={currentPlaybackRate}
372
292
  soundRef={soundRef as RefObject<SoundReturnType>}
373
293
  testID='sound-player'
374
294
  uri={item.asset_url}
@@ -377,7 +297,7 @@ export const AudioAttachment = (props: AudioAttachmentProps) => {
377
297
  </View>
378
298
  {showSpeedSettings ? (
379
299
  <View style={[styles.rightContainer, rightContainer]}>
380
- {item.paused ? (
300
+ {!isPlaying ? (
381
301
  <Audio fill={'#ffffff'} />
382
302
  ) : (
383
303
  <Pressable
@@ -390,7 +310,7 @@ export const AudioAttachment = (props: AudioAttachmentProps) => {
390
310
  >
391
311
  <Text
392
312
  style={[styles.speedChangeButtonText, speedChangeButtonText]}
393
- >{`x${currentSpeed.toFixed(1)}`}</Text>
313
+ >{`x${currentPlaybackRate.toFixed(1)}`}</Text>
394
314
  </Pressable>
395
315
  )}
396
316
  </View>
@@ -1,7 +1,7 @@
1
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';
4
+ import { Attachment, isAudioAttachment, isVoiceRecordingAttachment } from 'stream-chat';
5
5
 
6
6
  import { Attachment as AttachmentDefault } from './Attachment';
7
7
 
@@ -17,11 +17,10 @@ import {
17
17
  import { useTheme } from '../../contexts/themeContext/ThemeContext';
18
18
  import { isSoundPackageAvailable } from '../../native';
19
19
 
20
- import { FileTypes } from '../../types/types';
21
-
22
- export type FileAttachmentGroupPropsWithContext = Pick<MessageContextValue, 'files'> &
20
+ export type FileAttachmentGroupPropsWithContext = Pick<MessageContextValue, 'files' | 'message'> &
23
21
  Pick<MessagesContextValue, 'Attachment' | 'AudioAttachment'> & {
24
22
  /**
23
+ * @deprecated Use message instead
25
24
  * The unique id for the message with file attachments
26
25
  */
27
26
  messageId: string;
@@ -38,7 +37,8 @@ type FilesToDisplayType = Attachment & {
38
37
  };
39
38
 
40
39
  const FileAttachmentGroupWithContext = (props: FileAttachmentGroupPropsWithContext) => {
41
- const { Attachment, AudioAttachment, files, messageId, styles: stylesProp = {} } = props;
40
+ const { Attachment, AudioAttachment, files, message, styles: stylesProp = {} } = props;
41
+
42
42
  const [filesToDisplay, setFilesToDisplay] = useState<FilesToDisplayType[]>(() =>
43
43
  files.map((file) => ({ ...file, duration: file.duration || 0, paused: true, progress: 0 })),
44
44
  );
@@ -49,7 +49,14 @@ const FileAttachmentGroupWithContext = (props: FileAttachmentGroupPropsWithConte
49
49
  );
50
50
  }, [files]);
51
51
 
52
- // 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.
52
+ /**
53
+ * 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.
54
+ * @param index - The index of the audio
55
+ * @param duration - The duration of the audio
56
+ *
57
+ * @deprecated This is deprecated and will be removed in the future.
58
+ * FIXME: Remove this in the next major version.
59
+ */
53
60
  const onLoad = (index: string, duration: number) => {
54
61
  setFilesToDisplay((prevFilesToDisplay) =>
55
62
  prevFilesToDisplay.map((fileToDisplay, id) => ({
@@ -59,7 +66,14 @@ const FileAttachmentGroupWithContext = (props: FileAttachmentGroupPropsWithConte
59
66
  );
60
67
  };
61
68
 
62
- // The handler which is triggered when the audio progresses/ the thumb is dragged in the progress control. The progressed duration is set here.
69
+ /**
70
+ * Handler which is triggered when the audio progresses/ the thumb is dragged in the progress control. The progressed duration is set here.
71
+ * @param index - The index of the audio
72
+ * @param progress - The progress of the audio
73
+ *
74
+ * @deprecated This is deprecated and will be removed in the future.
75
+ * FIXME: Remove this in the next major version.
76
+ */
63
77
  const onProgress = (index: string, progress: number) => {
64
78
  setFilesToDisplay((prevFilesToDisplay) =>
65
79
  prevFilesToDisplay.map((filesToDisplay, id) => ({
@@ -69,7 +83,14 @@ const FileAttachmentGroupWithContext = (props: FileAttachmentGroupPropsWithConte
69
83
  );
70
84
  };
71
85
 
72
- // The handler which controls or sets the paused/played state of the audio.
86
+ /**
87
+ * Handler which controls or sets the paused/played state of the audio.
88
+ * @param index - The index of the audio
89
+ * @param pausedStatus - The paused status of the audio
90
+ *
91
+ * @deprecated This is deprecated and will be removed in the future.
92
+ * FIXME: Remove this in the next major version.
93
+ */
73
94
  const onPlayPause = (index: string, pausedStatus?: boolean) => {
74
95
  if (pausedStatus === false) {
75
96
  // If the status is false we set the audio with the index as playing and the others as paused.
@@ -102,21 +123,18 @@ const FileAttachmentGroupWithContext = (props: FileAttachmentGroupPropsWithConte
102
123
  <View style={[styles.container, container, stylesProp.container]}>
103
124
  {filesToDisplay.map((file, index) => (
104
125
  <View
105
- key={`file-by-attachment-group-${messageId}-${index}`}
126
+ key={`file-by-attachment-group-${message.id}-${index}`}
106
127
  style={[
107
128
  { paddingBottom: index !== files.length - 1 ? 4 : 0 },
108
129
  stylesProp.attachmentContainer,
109
130
  attachmentContainer,
110
131
  ]}
111
132
  >
112
- {(file.type === FileTypes.Audio || file.type === FileTypes.VoiceRecording) &&
133
+ {(isAudioAttachment(file) || isVoiceRecordingAttachment(file)) &&
113
134
  isSoundPackageAvailable() ? (
114
135
  <AudioAttachment
115
- item={{
116
- ...file,
117
- id: index.toString(),
118
- type: file.type,
119
- }}
136
+ item={{ ...file, id: index.toString(), type: file.type }}
137
+ message={message}
120
138
  onLoad={onLoad}
121
139
  onPlayPause={onPlayPause}
122
140
  onProgress={onProgress}
@@ -154,7 +172,7 @@ export type FileAttachmentGroupProps = Partial<
154
172
  export const FileAttachmentGroup = (props: FileAttachmentGroupProps) => {
155
173
  const { files: propFiles, messageId } = props;
156
174
 
157
- const { files: contextFiles } = useMessageContext();
175
+ const { files: contextFiles, message } = useMessageContext();
158
176
 
159
177
  const { Attachment = AttachmentDefault, AudioAttachment } = useMessagesContext();
160
178
 
@@ -170,6 +188,7 @@ export const FileAttachmentGroup = (props: FileAttachmentGroupProps) => {
170
188
  Attachment,
171
189
  AudioAttachment,
172
190
  files,
191
+ message,
173
192
  messageId,
174
193
  }}
175
194
  />
@@ -24,6 +24,9 @@ type Props = {
24
24
  export const AttachmentPickerBottomSheetHandle = ({ animatedIndex }: Props) => {
25
25
  const {
26
26
  theme: {
27
+ attachmentPicker: {
28
+ handle: { container, indicator },
29
+ },
27
30
  colors: { black, white },
28
31
  },
29
32
  } = useTheme();
@@ -34,8 +37,8 @@ export const AttachmentPickerBottomSheetHandle = ({ animatedIndex }: Props) => {
34
37
  }));
35
38
 
36
39
  return (
37
- <Animated.View style={[styles.container, { backgroundColor: white }, style]}>
38
- <View style={[styles.handle, { backgroundColor: `${black}1A` }]} />
40
+ <Animated.View style={[styles.container, { backgroundColor: white }, style, container]}>
41
+ <View style={[styles.handle, { backgroundColor: `${black}1A` }, indicator]} />
39
42
  {/* ^ 1A = 10% opacity */}
40
43
  </Animated.View>
41
44
  );