@sendbird/uikit-react-native 3.10.3 → 3.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +2 -2
  2. package/lib/commonjs/components/ChannelInput/VoiceMessageInput.js +1 -1
  3. package/lib/commonjs/components/ChannelInput/VoiceMessageInput.js.map +1 -1
  4. package/lib/commonjs/components/ChannelInput/index.js +2 -25
  5. package/lib/commonjs/components/ChannelInput/index.js.map +1 -1
  6. package/lib/commonjs/hooks/useVoiceMessageInput.js +10 -2
  7. package/lib/commonjs/hooks/useVoiceMessageInput.js.map +1 -1
  8. package/lib/commonjs/platform/createFileService.native.js +1 -1
  9. package/lib/commonjs/platform/createFileService.native.js.map +1 -1
  10. package/lib/commonjs/platform/createPlayerService.native.js +191 -114
  11. package/lib/commonjs/platform/createPlayerService.native.js.map +1 -1
  12. package/lib/commonjs/platform/createRecorderService.expo.js +3 -0
  13. package/lib/commonjs/platform/createRecorderService.expo.js.map +1 -1
  14. package/lib/commonjs/platform/createRecorderService.native.js +212 -129
  15. package/lib/commonjs/platform/createRecorderService.native.js.map +1 -1
  16. package/lib/commonjs/platform/types.js.map +1 -1
  17. package/lib/commonjs/version.js +1 -1
  18. package/lib/commonjs/version.js.map +1 -1
  19. package/lib/module/components/ChannelInput/VoiceMessageInput.js +1 -1
  20. package/lib/module/components/ChannelInput/VoiceMessageInput.js.map +1 -1
  21. package/lib/module/components/ChannelInput/index.js +3 -26
  22. package/lib/module/components/ChannelInput/index.js.map +1 -1
  23. package/lib/module/hooks/useVoiceMessageInput.js +10 -2
  24. package/lib/module/hooks/useVoiceMessageInput.js.map +1 -1
  25. package/lib/module/platform/createFileService.native.js +1 -1
  26. package/lib/module/platform/createFileService.native.js.map +1 -1
  27. package/lib/module/platform/createPlayerService.native.js +191 -114
  28. package/lib/module/platform/createPlayerService.native.js.map +1 -1
  29. package/lib/module/platform/createRecorderService.expo.js +3 -0
  30. package/lib/module/platform/createRecorderService.expo.js.map +1 -1
  31. package/lib/module/platform/createRecorderService.native.js +212 -129
  32. package/lib/module/platform/createRecorderService.native.js.map +1 -1
  33. package/lib/module/platform/types.js.map +1 -1
  34. package/lib/module/version.js +1 -1
  35. package/lib/module/version.js.map +1 -1
  36. package/lib/typescript/src/containers/SendbirdUIKitContainer.d.ts +1 -1
  37. package/lib/typescript/src/platform/createPlayerService.native.d.ts +5 -3
  38. package/lib/typescript/src/platform/createRecorderService.native.d.ts +5 -3
  39. package/lib/typescript/src/platform/types.d.ts +4 -0
  40. package/lib/typescript/src/version.d.ts +1 -1
  41. package/package.json +18 -5
  42. package/src/components/ChannelInput/VoiceMessageInput.tsx +1 -1
  43. package/src/components/ChannelInput/index.tsx +6 -36
  44. package/src/hooks/useVoiceMessageInput.ts +7 -2
  45. package/src/platform/createFileService.native.ts +1 -1
  46. package/src/platform/createPlayerService.native.tsx +237 -113
  47. package/src/platform/createRecorderService.expo.tsx +4 -0
  48. package/src/platform/createRecorderService.native.tsx +240 -118
  49. package/src/platform/types.ts +5 -0
  50. package/src/version.ts +1 -1
@@ -1,6 +1,5 @@
1
1
  import React, { useEffect, useMemo, useState } from 'react';
2
2
  import {
3
- Keyboard,
4
3
  KeyboardAvoidingView,
5
4
  Platform,
6
5
  StyleProp,
@@ -95,12 +94,7 @@ export type ChannelInputProps = {
95
94
  };
96
95
 
97
96
  const AUTO_FOCUS = Platform.select({ ios: false, android: true, default: false });
98
- const isAndroidApi35Plus = Platform.OS === 'android' && Platform.Version >= 35;
99
- const KEYBOARD_AVOID_VIEW_BEHAVIOR = Platform.select({
100
- ios: 'padding' as const,
101
- android: isAndroidApi35Plus ? ('padding' as const) : undefined,
102
- default: undefined,
103
- });
97
+ const KEYBOARD_AVOID_VIEW_BEHAVIOR = Platform.select({ ios: 'padding' as const, default: undefined });
104
98
 
105
99
  // FIXME(iOS): Dynamic style does not work properly when typing the CJK. (https://github.com/facebook/react-native/issues/26107)
106
100
  // To workaround temporarily, change the key for re-mount the component.
@@ -115,16 +109,6 @@ const ChannelInput = (props: ChannelInputProps) => {
115
109
 
116
110
  const safeArea = useSafeAreaPadding(['top', 'left', 'right', 'bottom']);
117
111
 
118
- // Android API 35+ keyboard avoidance handling
119
- /**
120
- * Android API 35+ introduced edge-to-edge layouts, which changed how keyboard avoidance should be handled.
121
- * For API 35+, the system manages insets automatically, so we use the provided keyboardAvoidOffset directly.
122
- * For older Android versions, we manually subtract the safe area bottom padding to avoid overlapping with system UI.
123
- * See: https://developer.android.com/develop/ui/views/layout/edge-to-edge
124
- */
125
- const keyboardVerticalOffset = isAndroidApi35Plus
126
- ? keyboardAvoidOffset
127
- : -safeArea.paddingBottom + keyboardAvoidOffset;
128
112
  const { colors, typography } = useUIKitTheme();
129
113
  const { sbOptions, mentionManager } = useSendbirdChat();
130
114
 
@@ -161,30 +145,16 @@ const ChannelInput = (props: ChannelInputProps) => {
161
145
  onChangeText(replace(text, searchStringRange.start, searchStringRange.end, mentionedMessageText), { user, range });
162
146
  };
163
147
 
164
- const [keyboardShown, setKeyboardShown] = useState(false);
165
-
166
- useEffect(() => {
167
- const keyboardDidShow = () => setKeyboardShown(true);
168
- const keyboardDidHide = () => setKeyboardShown(false);
169
-
170
- const showSubscription = Keyboard.addListener('keyboardDidShow', keyboardDidShow);
171
- const hideSubscription = Keyboard.addListener('keyboardDidHide', keyboardDidHide);
172
-
173
- return () => {
174
- showSubscription.remove();
175
- hideSubscription.remove();
176
- };
177
- }, []);
178
-
179
- const shouldShowSafeAreaBottom = !isAndroidApi35Plus || (isAndroidApi35Plus && !keyboardShown);
180
-
181
148
  if (!props.shouldRenderInput) {
182
149
  return <SafeAreaBottom height={safeArea.paddingBottom} />;
183
150
  }
184
151
 
185
152
  return (
186
153
  <>
187
- <KeyboardAvoidingView keyboardVerticalOffset={keyboardVerticalOffset} behavior={KEYBOARD_AVOID_VIEW_BEHAVIOR}>
154
+ <KeyboardAvoidingView
155
+ keyboardVerticalOffset={-safeArea.paddingBottom + keyboardAvoidOffset}
156
+ behavior={KEYBOARD_AVOID_VIEW_BEHAVIOR}
157
+ >
188
158
  <View
189
159
  style={{
190
160
  paddingStart: safeArea.paddingStart,
@@ -224,7 +194,7 @@ const ChannelInput = (props: ChannelInputProps) => {
224
194
  />
225
195
  )}
226
196
  </View>
227
- {shouldShowSafeAreaBottom && <SafeAreaBottom height={safeArea.paddingBottom} />}
197
+ <SafeAreaBottom height={safeArea.paddingBottom} />
228
198
  </View>
229
199
  </KeyboardAvoidingView>
230
200
  {mentionAvailable && props.SuggestedMentionList && (
@@ -161,8 +161,10 @@ const useVoiceMessageInput = ({ onSend, onClose }: Props): VoiceMessageInputResu
161
161
  setVoiceMessageRecordingPath({ recordFilePath: recorderService.uri, uri: recorderService.uri });
162
162
  }
163
163
  } else {
164
- setVoiceMessageRecordingPath(fileService.createRecordFilePath(recorderService.options.extension));
165
- await recorderService.record(getVoiceMessageRecordingPath().recordFilePath);
164
+ const recordFilePath = fileService.createRecordFilePath(recorderService.options.extension);
165
+ const convertedRecordPath = recorderService.convertRecordPath(recordFilePath.recordFilePath);
166
+ setVoiceMessageRecordingPath({ ...recordFilePath, recordFilePath: convertedRecordPath });
167
+ await recorderService.record(convertedRecordPath);
166
168
  }
167
169
  }
168
170
  },
@@ -225,6 +227,9 @@ const useVoiceMessageInput = ({ onSend, onClose }: Props): VoiceMessageInputResu
225
227
  matchesOneOf(status, ['recording', 'recording_completed', 'playing', 'playing_paused']) &&
226
228
  recordingPath.current
227
229
  ) {
230
+ if (status === 'recording') {
231
+ await recorderService.stop();
232
+ }
228
233
  const voiceFile = getVoiceMessageFileObject(recordingPath.current.uri, recorderService.options.extension);
229
234
  onSend(voiceFile, Math.floor(recordingTime.currentTime));
230
235
  await clear();
@@ -242,7 +242,7 @@ const createNativeFileService = ({
242
242
  return Platform.select({
243
243
  ios: {
244
244
  uri: path,
245
- recordFilePath: filename,
245
+ recordFilePath: path,
246
246
  },
247
247
  android: {
248
248
  uri: path.startsWith('file://') ? path : 'file://' + path,
@@ -1,155 +1,279 @@
1
1
  import { Platform } from 'react-native';
2
- import type * as RNAudioRecorder from 'react-native-audio-recorder-player';
2
+ import type * as LegacyModule from 'react-native-audio-recorder-player';
3
+ import type * as NitroSoundOrLegacyV4Module from 'react-native-nitro-sound';
3
4
  import * as Permissions from 'react-native-permissions';
4
5
 
5
6
  import { Logger, matchesOneOf, sleep } from '@sendbird/uikit-utils';
6
7
 
8
+ import { AudioRecorderModule } from './createRecorderService.native';
7
9
  import type { PlayerServiceInterface, Unsubscribe } from './types';
8
10
 
11
+ export type AudioPlayerModule = typeof LegacyModule | typeof NitroSoundOrLegacyV4Module;
12
+
9
13
  type Modules = {
10
- audioRecorderModule: typeof RNAudioRecorder;
14
+ audioRecorderModule: AudioPlayerModule;
11
15
  permissionModule: typeof Permissions;
12
16
  };
13
17
  type PlaybackListener = Parameters<PlayerServiceInterface['addPlaybackListener']>[number];
14
18
  type StateListener = Parameters<PlayerServiceInterface['addStateListener']>[number];
15
- const createNativePlayerService = ({ audioRecorderModule, permissionModule }: Modules): PlayerServiceInterface => {
16
- const module = new audioRecorderModule.default();
17
19
 
18
- class VoicePlayer implements PlayerServiceInterface {
19
- uri?: string;
20
- state: PlayerServiceInterface['state'] = 'idle';
20
+ interface PlayBackData {
21
+ currentPosition: number;
22
+ duration: number;
23
+ }
24
+
25
+ interface PlayerModuleAdapter {
26
+ setSubscriptionDuration(duration: number): Promise<void> | void;
27
+ addPlayBackListener(callback: (data: PlayBackData) => void): void;
28
+ removePlayBackListener(): void;
29
+ startPlayer(uri: string): Promise<void>;
30
+ pausePlayer(): Promise<void>;
31
+ resumePlayer(): Promise<void>;
32
+ stopPlayer(): Promise<void>;
33
+ seekToPlayer(time: number): Promise<void>;
34
+ }
35
+
36
+ class AudioRecorderPlayerAdapter implements PlayerModuleAdapter {
37
+ private module: InstanceType<typeof LegacyModule.default>;
38
+
39
+ constructor(audioRecorderModule: typeof LegacyModule) {
40
+ this.module = new audioRecorderModule.default();
41
+ }
42
+
43
+ async setSubscriptionDuration(duration: number): Promise<void> {
44
+ await this.module.setSubscriptionDuration(duration);
45
+ }
46
+
47
+ addPlayBackListener(callback: (data: PlayBackData) => void): void {
48
+ this.module.addPlayBackListener(callback);
49
+ }
50
+
51
+ removePlayBackListener(): void {
52
+ this.module.removePlayBackListener();
53
+ }
54
+
55
+ async startPlayer(uri: string): Promise<void> {
56
+ await this.module.startPlayer(uri);
57
+ }
58
+
59
+ async pausePlayer(): Promise<void> {
60
+ await this.module.pausePlayer();
61
+ }
62
+
63
+ async resumePlayer(): Promise<void> {
64
+ await this.module.resumePlayer();
65
+ }
66
+
67
+ async stopPlayer(): Promise<void> {
68
+ await this.module.stopPlayer();
69
+ }
70
+
71
+ async seekToPlayer(time: number): Promise<void> {
72
+ await this.module.seekToPlayer(time);
73
+ }
74
+ }
75
+
76
+ class NitroSoundOrLegacyV4Adapter implements PlayerModuleAdapter {
77
+ private module;
78
+
79
+ constructor(audioRecorderModule: typeof NitroSoundOrLegacyV4Module) {
80
+ this.module = audioRecorderModule.default;
81
+ }
82
+
83
+ setSubscriptionDuration(duration: number): void {
84
+ try {
85
+ this.module.setSubscriptionDuration(duration);
86
+ } catch (error) {
87
+ Logger.warn('[PlayerService.Native] Failed to set subscription duration', error);
88
+ }
89
+ }
90
+
91
+ addPlayBackListener(callback: (data: PlayBackData) => void): void {
92
+ this.module.addPlayBackListener(callback);
93
+ }
94
+
95
+ removePlayBackListener(): void {
96
+ this.module.removePlayBackListener();
97
+ }
98
+
99
+ async startPlayer(uri: string): Promise<void> {
100
+ await this.module.startPlayer(uri);
101
+ }
102
+
103
+ async pausePlayer(): Promise<void> {
104
+ await this.module.pausePlayer();
105
+ }
106
+
107
+ async resumePlayer(): Promise<void> {
108
+ await this.module.resumePlayer();
109
+ }
110
+
111
+ async stopPlayer(): Promise<void> {
112
+ await this.module.stopPlayer();
113
+ }
114
+
115
+ async seekToPlayer(time: number): Promise<void> {
116
+ await this.module.seekToPlayer(time);
117
+ }
118
+ }
119
+
120
+ class VoicePlayer implements PlayerServiceInterface {
121
+ public uri?: string;
122
+ public state: PlayerServiceInterface['state'] = 'idle';
123
+
124
+ private readonly playbackSubscribers = new Set<PlaybackListener>();
125
+ private readonly stateSubscribers = new Set<StateListener>();
21
126
 
22
- private readonly playbackSubscribers = new Set<PlaybackListener>();
23
- private readonly stateSubscribers = new Set<StateListener>();
127
+ constructor(private readonly adapter: PlayerModuleAdapter, private readonly permissionModule: typeof Permissions) {
128
+ this.initialize();
129
+ }
24
130
 
25
- constructor() {
26
- module.setSubscriptionDuration(0.1).catch((error) => {
131
+ private initialize(): void {
132
+ const setDurationResult = this.adapter.setSubscriptionDuration(0.1);
133
+ if (setDurationResult instanceof Promise) {
134
+ setDurationResult.catch((error) => {
27
135
  Logger.warn('[PlayerService.Native] Failed to set subscription duration', error);
28
136
  });
29
137
  }
138
+ }
30
139
 
31
- private setState = (state: PlayerServiceInterface['state']) => {
32
- this.state = state;
33
- this.stateSubscribers.forEach((callback) => {
34
- callback(state);
35
- });
36
- };
140
+ private setState = (state: PlayerServiceInterface['state']) => {
141
+ this.state = state;
142
+ this.stateSubscribers.forEach((callback) => {
143
+ callback(state);
144
+ });
145
+ };
37
146
 
38
- private setListener = () => {
39
- module.addPlayBackListener(async (data) => {
40
- const stopped = data.currentPosition >= data.duration;
41
-
42
- if (stopped) {
43
- this.stop().catch((error) => {
44
- Logger.warn('[PlayerService.Native] Failed to stop in PlayBackListener', error);
45
- });
46
- }
47
- if (this.state === 'playing') {
48
- this.playbackSubscribers.forEach((callback) => {
49
- callback({ currentTime: data.currentPosition, duration: data.duration, stopped });
50
- });
51
- }
52
- });
53
- };
147
+ private setListener = () => {
148
+ this.adapter.addPlayBackListener(async (data) => {
149
+ const stopped = data.currentPosition >= data.duration;
54
150
 
55
- private removeListener = () => {
56
- module.removePlayBackListener();
57
- };
151
+ if (stopped) {
152
+ this.stop().catch((error) => {
153
+ Logger.warn('[PlayerService.Native] Failed to stop in PlayBackListener', error);
154
+ });
155
+ }
156
+ if (this.state === 'playing') {
157
+ this.playbackSubscribers.forEach((callback) => {
158
+ callback({ currentTime: data.currentPosition, duration: data.duration, stopped });
159
+ });
160
+ }
161
+ });
162
+ };
58
163
 
59
- public requestPermission = async (): Promise<boolean> => {
60
- if (Platform.OS === 'android') {
61
- if (Platform.Version > 32) return true;
164
+ private removeListener = () => {
165
+ this.adapter.removePlayBackListener();
166
+ };
62
167
 
63
- const { READ_EXTERNAL_STORAGE } = permissionModule.PERMISSIONS.ANDROID;
168
+ public requestPermission = async (): Promise<boolean> => {
169
+ if (Platform.OS === 'android') {
170
+ if (Platform.Version > 32) return true;
64
171
 
65
- const status = await permissionModule.check(READ_EXTERNAL_STORAGE);
66
- if (status === 'granted') {
67
- return true;
68
- } else {
69
- const status = await permissionModule.request(READ_EXTERNAL_STORAGE);
70
- return status === 'granted';
71
- }
72
- } else {
172
+ const { READ_EXTERNAL_STORAGE } = this.permissionModule.PERMISSIONS.ANDROID;
173
+
174
+ const status = await this.permissionModule.check(READ_EXTERNAL_STORAGE);
175
+ if (status === 'granted') {
73
176
  return true;
177
+ } else {
178
+ const status = await this.permissionModule.request(READ_EXTERNAL_STORAGE);
179
+ return status === 'granted';
74
180
  }
75
- };
181
+ } else {
182
+ return true;
183
+ }
184
+ };
76
185
 
77
- public addPlaybackListener = (callback: PlaybackListener): Unsubscribe => {
78
- this.playbackSubscribers.add(callback);
79
- return () => {
80
- this.playbackSubscribers.delete(callback);
81
- };
186
+ public addPlaybackListener = (callback: PlaybackListener): Unsubscribe => {
187
+ this.playbackSubscribers.add(callback);
188
+ return () => {
189
+ this.playbackSubscribers.delete(callback);
82
190
  };
191
+ };
83
192
 
84
- public addStateListener = (callback: (state: PlayerServiceInterface['state']) => void): Unsubscribe => {
85
- this.stateSubscribers.add(callback);
86
- return () => {
87
- this.stateSubscribers.delete(callback);
88
- };
193
+ public addStateListener = (callback: (state: PlayerServiceInterface['state']) => void): Unsubscribe => {
194
+ this.stateSubscribers.add(callback);
195
+ return () => {
196
+ this.stateSubscribers.delete(callback);
89
197
  };
198
+ };
90
199
 
91
- public play = async (uri: string): Promise<void> => {
92
- if (matchesOneOf(this.state, ['idle', 'stopped'])) {
93
- try {
94
- this.setState('preparing');
95
- this.uri = uri;
96
- this.setListener();
97
-
98
- // FIXME: Workaround, `module.startPlayer()` caused a significant frame-drop and prevented the 'preparing' UI transition.
99
- await sleep(0);
100
- await module.startPlayer(uri);
101
-
102
- this.setState('playing');
103
- } catch (e) {
104
- this.setState('idle');
105
- this.uri = undefined;
106
- this.removeListener();
107
- throw e;
108
- }
109
- } else if (matchesOneOf(this.state, ['paused']) && this.uri === uri) {
110
- try {
111
- this.setListener();
112
- await module.resumePlayer();
113
- this.setState('playing');
114
- } catch (e) {
115
- this.removeListener();
116
- throw e;
117
- }
118
- }
119
- };
200
+ public play = async (uri: string): Promise<void> => {
201
+ if (matchesOneOf(this.state, ['idle', 'stopped'])) {
202
+ try {
203
+ this.setState('preparing');
204
+ this.uri = uri;
205
+ this.setListener();
206
+
207
+ // FIXME: Workaround, `module.startPlayer()` caused a significant frame-drop and prevented the 'preparing' UI transition.
208
+ await sleep(0);
209
+ await this.adapter.startPlayer(uri);
120
210
 
121
- public pause = async (): Promise<void> => {
122
- if (matchesOneOf(this.state, ['playing'])) {
123
- await module.pausePlayer();
211
+ this.setState('playing');
212
+ } catch (e) {
213
+ this.setState('idle');
214
+ this.uri = undefined;
124
215
  this.removeListener();
125
- this.setState('paused');
216
+ throw e;
126
217
  }
127
- };
128
-
129
- public stop = async (): Promise<void> => {
130
- if (matchesOneOf(this.state, ['preparing', 'playing', 'paused'])) {
131
- await module.stopPlayer();
218
+ } else if (matchesOneOf(this.state, ['paused']) && this.uri === uri) {
219
+ try {
220
+ this.setListener();
221
+ await this.adapter.resumePlayer();
222
+ this.setState('playing');
223
+ } catch (e) {
132
224
  this.removeListener();
133
- this.setState('stopped');
225
+ throw e;
134
226
  }
135
- };
227
+ }
228
+ };
136
229
 
137
- public reset = async (): Promise<void> => {
138
- await this.stop();
139
- this.setState('idle');
140
- this.uri = undefined;
141
- this.playbackSubscribers.clear();
142
- this.stateSubscribers.clear();
143
- };
230
+ public pause = async (): Promise<void> => {
231
+ if (matchesOneOf(this.state, ['playing'])) {
232
+ await this.adapter.pausePlayer();
233
+ this.removeListener();
234
+ this.setState('paused');
235
+ }
236
+ };
144
237
 
145
- public seek = async (time: number): Promise<void> => {
146
- if (matchesOneOf(this.state, ['playing', 'paused'])) {
147
- await module.seekToPlayer(time);
148
- }
149
- };
150
- }
238
+ public stop = async (): Promise<void> => {
239
+ if (matchesOneOf(this.state, ['preparing', 'playing', 'paused'])) {
240
+ await this.adapter.stopPlayer();
241
+ this.removeListener();
242
+ this.setState('stopped');
243
+ }
244
+ };
151
245
 
152
- return new VoicePlayer();
246
+ public reset = async (): Promise<void> => {
247
+ await this.stop();
248
+ this.setState('idle');
249
+ this.uri = undefined;
250
+ this.playbackSubscribers.clear();
251
+ this.stateSubscribers.clear();
252
+ };
253
+
254
+ public seek = async (time: number): Promise<void> => {
255
+ if (matchesOneOf(this.state, ['playing', 'paused'])) {
256
+ await this.adapter.seekToPlayer(time);
257
+ }
258
+ };
259
+ }
260
+
261
+ const createNativePlayerService = (modules: Modules): PlayerServiceInterface => {
262
+ const adapter = isNitroSoundOrLegacyV4Module(modules.audioRecorderModule)
263
+ ? new NitroSoundOrLegacyV4Adapter(modules.audioRecorderModule)
264
+ : new AudioRecorderPlayerAdapter(modules.audioRecorderModule as typeof LegacyModule);
265
+
266
+ return new VoicePlayer(adapter, modules.permissionModule);
153
267
  };
154
268
 
269
+ function isNitroSoundOrLegacyV4Module(module: AudioRecorderModule): module is typeof NitroSoundOrLegacyV4Module {
270
+ const isNitroSound = 'createSound' in module && typeof module.createSound === 'function';
271
+ const isLegacyV4 =
272
+ 'default' in module && 'getHybridObject' in module.default && typeof module.default.getHybridObject === 'function';
273
+ if (isLegacyV4) {
274
+ Logger.warn('react-native-audio-recorder-player is deprecated. Please use react-native-nitro-sound instead.');
275
+ }
276
+ return isNitroSound || isLegacyV4;
277
+ }
278
+
155
279
  export default createNativePlayerService;
@@ -152,6 +152,10 @@ const createExpoRecorderService = ({ avModule }: Modules): RecorderServiceInterf
152
152
  this._recorder = new avModule.Audio.Recording();
153
153
  this.setState('idle');
154
154
  };
155
+
156
+ public convertRecordPath = (uri: string): string => {
157
+ return uri;
158
+ };
155
159
  }
156
160
 
157
161
  return new VoiceRecorder();