@sendbird/uikit-react-native 3.10.3 → 3.11.1
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.
- package/README.md +9 -7
- package/lib/commonjs/components/ChannelInput/VoiceMessageInput.js +1 -1
- package/lib/commonjs/components/ChannelInput/VoiceMessageInput.js.map +1 -1
- package/lib/commonjs/components/ChannelInput/index.js +2 -25
- package/lib/commonjs/components/ChannelInput/index.js.map +1 -1
- package/lib/commonjs/hooks/useVoiceMessageInput.js +10 -2
- package/lib/commonjs/hooks/useVoiceMessageInput.js.map +1 -1
- package/lib/commonjs/platform/createFileService.native.js +1 -1
- package/lib/commonjs/platform/createFileService.native.js.map +1 -1
- package/lib/commonjs/platform/createMediaService.expo.js +83 -12
- package/lib/commonjs/platform/createMediaService.expo.js.map +1 -1
- package/lib/commonjs/platform/createPlayerService.expo.js +214 -113
- package/lib/commonjs/platform/createPlayerService.expo.js.map +1 -1
- package/lib/commonjs/platform/createPlayerService.native.js +191 -114
- package/lib/commonjs/platform/createPlayerService.native.js.map +1 -1
- package/lib/commonjs/platform/createRecorderService.expo.js +248 -127
- package/lib/commonjs/platform/createRecorderService.expo.js.map +1 -1
- package/lib/commonjs/platform/createRecorderService.native.js +212 -129
- package/lib/commonjs/platform/createRecorderService.native.js.map +1 -1
- package/lib/commonjs/platform/types.js.map +1 -1
- package/lib/commonjs/utils/expoBackwardUtils.js +23 -0
- package/lib/commonjs/utils/expoBackwardUtils.js.map +1 -1
- package/lib/commonjs/utils/expoPermissionGranted.js.map +1 -1
- package/lib/commonjs/version.js +1 -1
- package/lib/commonjs/version.js.map +1 -1
- package/lib/module/components/ChannelInput/VoiceMessageInput.js +1 -1
- package/lib/module/components/ChannelInput/VoiceMessageInput.js.map +1 -1
- package/lib/module/components/ChannelInput/index.js +3 -26
- package/lib/module/components/ChannelInput/index.js.map +1 -1
- package/lib/module/hooks/useVoiceMessageInput.js +10 -2
- package/lib/module/hooks/useVoiceMessageInput.js.map +1 -1
- package/lib/module/platform/createFileService.native.js +1 -1
- package/lib/module/platform/createFileService.native.js.map +1 -1
- package/lib/module/platform/createMediaService.expo.js +82 -13
- package/lib/module/platform/createMediaService.expo.js.map +1 -1
- package/lib/module/platform/createPlayerService.expo.js +214 -113
- package/lib/module/platform/createPlayerService.expo.js.map +1 -1
- package/lib/module/platform/createPlayerService.native.js +191 -114
- package/lib/module/platform/createPlayerService.native.js.map +1 -1
- package/lib/module/platform/createRecorderService.expo.js +249 -128
- package/lib/module/platform/createRecorderService.expo.js.map +1 -1
- package/lib/module/platform/createRecorderService.native.js +212 -129
- package/lib/module/platform/createRecorderService.native.js.map +1 -1
- package/lib/module/platform/types.js.map +1 -1
- package/lib/module/utils/expoBackwardUtils.js +23 -0
- package/lib/module/utils/expoBackwardUtils.js.map +1 -1
- package/lib/module/utils/expoPermissionGranted.js.map +1 -1
- package/lib/module/version.js +1 -1
- package/lib/module/version.js.map +1 -1
- package/lib/typescript/src/containers/SendbirdUIKitContainer.d.ts +1 -1
- package/lib/typescript/src/platform/createMediaService.expo.d.ts +2 -2
- package/lib/typescript/src/platform/createPlayerService.expo.d.ts +2 -2
- package/lib/typescript/src/platform/createPlayerService.native.d.ts +5 -3
- package/lib/typescript/src/platform/createRecorderService.expo.d.ts +2 -2
- package/lib/typescript/src/platform/createRecorderService.native.d.ts +5 -3
- package/lib/typescript/src/platform/types.d.ts +4 -0
- package/lib/typescript/src/utils/expoBackwardUtils.d.ts +10 -0
- package/lib/typescript/src/utils/expoPermissionGranted.d.ts +1 -1
- package/lib/typescript/src/version.d.ts +1 -1
- package/package.json +29 -5
- package/src/components/ChannelInput/VoiceMessageInput.tsx +1 -1
- package/src/components/ChannelInput/index.tsx +6 -36
- package/src/hooks/useVoiceMessageInput.ts +7 -2
- package/src/platform/createFileService.native.ts +1 -1
- package/src/platform/createMediaService.expo.tsx +87 -9
- package/src/platform/createPlayerService.expo.tsx +242 -109
- package/src/platform/createPlayerService.native.tsx +237 -113
- package/src/platform/createRecorderService.expo.tsx +268 -107
- package/src/platform/createRecorderService.native.tsx +240 -118
- package/src/platform/types.ts +5 -0
- package/src/utils/expoBackwardUtils.ts +29 -0
- package/src/utils/expoPermissionGranted.ts +3 -1
- package/src/version.ts +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Platform } from 'react-native';
|
|
2
|
-
import * as
|
|
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
|
import { Permission } from 'react-native-permissions/src/types';
|
|
5
6
|
|
|
@@ -9,168 +10,289 @@ import VoiceMessageConfig from '../libs/VoiceMessageConfig';
|
|
|
9
10
|
import nativePermissionGranted from '../utils/nativePermissionGranted';
|
|
10
11
|
import type { RecorderServiceInterface, Unsubscribe } from './types';
|
|
11
12
|
|
|
13
|
+
export type AudioRecorderModule = typeof LegacyModule | typeof NitroSoundOrLegacyV4Module;
|
|
14
|
+
|
|
12
15
|
type RecordingListener = Parameters<RecorderServiceInterface['addRecordingListener']>[number];
|
|
13
16
|
type StateListener = Parameters<RecorderServiceInterface['addStateListener']>[number];
|
|
14
17
|
type Modules = {
|
|
15
|
-
audioRecorderModule:
|
|
18
|
+
audioRecorderModule: AudioRecorderModule;
|
|
16
19
|
permissionModule: typeof Permissions;
|
|
17
20
|
};
|
|
18
|
-
const createNativeRecorderService = ({ audioRecorderModule, permissionModule }: Modules): RecorderServiceInterface => {
|
|
19
|
-
const module = new audioRecorderModule.default();
|
|
20
|
-
|
|
21
|
-
class VoiceRecorder implements RecorderServiceInterface {
|
|
22
|
-
public uri: RecorderServiceInterface['uri'] = undefined;
|
|
23
|
-
public state: RecorderServiceInterface['state'] = 'idle';
|
|
24
|
-
public options: RecorderServiceInterface['options'] = {
|
|
25
|
-
minDuration: VoiceMessageConfig.DEFAULT.RECORDER.MIN_DURATION,
|
|
26
|
-
maxDuration: VoiceMessageConfig.DEFAULT.RECORDER.MAX_DURATION,
|
|
27
|
-
extension: VoiceMessageConfig.DEFAULT.RECORDER.EXTENSION,
|
|
28
|
-
};
|
|
29
21
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const elapsedTime = Date.now() - this._recordStartedAt;
|
|
35
|
-
if (elapsedTime > minWaitingTime) return 0;
|
|
36
|
-
else return minWaitingTime - elapsedTime;
|
|
37
|
-
};
|
|
22
|
+
interface RecordBackData {
|
|
23
|
+
currentPosition: number;
|
|
24
|
+
duration?: number;
|
|
25
|
+
}
|
|
38
26
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
27
|
+
interface RecorderModuleAdapter {
|
|
28
|
+
setSubscriptionDuration(duration: number): Promise<void> | void;
|
|
29
|
+
addRecordBackListener(callback: (data: RecordBackData) => void): void;
|
|
30
|
+
convertRecordPath(uri: string): string;
|
|
31
|
+
startRecorder(uri: string): Promise<void>;
|
|
32
|
+
stopRecorder(): Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class AudioRecorderPlayerAdapter implements RecorderModuleAdapter {
|
|
36
|
+
private module: InstanceType<typeof LegacyModule.default>;
|
|
37
|
+
private readonly audioOptions;
|
|
38
|
+
|
|
39
|
+
constructor(audioRecorderModule: typeof LegacyModule) {
|
|
40
|
+
this.module = new audioRecorderModule.default();
|
|
41
|
+
|
|
42
|
+
this.audioOptions = Platform.select({
|
|
48
43
|
android: {
|
|
49
|
-
AudioEncodingBitRateAndroid:
|
|
50
|
-
AudioChannelsAndroid:
|
|
51
|
-
AudioSamplingRateAndroid:
|
|
44
|
+
AudioEncodingBitRateAndroid: VoiceMessageConfig.DEFAULT.RECORDER.BIT_RATE,
|
|
45
|
+
AudioChannelsAndroid: VoiceMessageConfig.DEFAULT.RECORDER.CHANNELS,
|
|
46
|
+
AudioSamplingRateAndroid: VoiceMessageConfig.DEFAULT.RECORDER.SAMPLE_RATE,
|
|
52
47
|
AudioEncoderAndroid: audioRecorderModule.AudioEncoderAndroidType.AAC,
|
|
53
48
|
OutputFormatAndroid: audioRecorderModule.OutputFormatAndroidType.MPEG_4,
|
|
54
49
|
AudioSourceAndroid: audioRecorderModule.AudioSourceAndroidType.VOICE_RECOGNITION,
|
|
55
50
|
},
|
|
56
51
|
ios: {
|
|
57
|
-
AVEncoderBitRateKeyIOS:
|
|
58
|
-
AVNumberOfChannelsKeyIOS:
|
|
59
|
-
AVSampleRateKeyIOS:
|
|
52
|
+
AVEncoderBitRateKeyIOS: VoiceMessageConfig.DEFAULT.RECORDER.BIT_RATE,
|
|
53
|
+
AVNumberOfChannelsKeyIOS: VoiceMessageConfig.DEFAULT.RECORDER.CHANNELS,
|
|
54
|
+
AVSampleRateKeyIOS: VoiceMessageConfig.DEFAULT.RECORDER.SAMPLE_RATE,
|
|
60
55
|
AVFormatIDKeyIOS: audioRecorderModule.AVEncodingOption.mp4, // same with aac
|
|
61
56
|
AVEncoderAudioQualityKeyIOS: audioRecorderModule.AVEncoderAudioQualityIOSType.high,
|
|
62
57
|
},
|
|
63
58
|
default: {},
|
|
64
59
|
});
|
|
60
|
+
}
|
|
65
61
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
});
|
|
70
|
-
module.addRecordBackListener((data) => {
|
|
71
|
-
const completed = data.currentPosition >= this.options.maxDuration;
|
|
62
|
+
async setSubscriptionDuration(duration: number): Promise<void> {
|
|
63
|
+
await this.module.setSubscriptionDuration(duration);
|
|
64
|
+
}
|
|
72
65
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
66
|
+
addRecordBackListener(callback: (data: RecordBackData) => void): void {
|
|
67
|
+
this.module.addRecordBackListener(callback);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
convertRecordPath(uri: string): string {
|
|
71
|
+
return Platform.OS === 'ios' ? uri.split('/').pop() || uri : uri;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async startRecorder(uri: string): Promise<void> {
|
|
75
|
+
await this.module.startRecorder(uri, this.audioOptions as Parameters<typeof this.module.startRecorder>[1]);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async stopRecorder(): Promise<void> {
|
|
79
|
+
await this.module.stopRecorder();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
class NitroSoundOrLegacyV4Adapter implements RecorderModuleAdapter {
|
|
84
|
+
private module;
|
|
85
|
+
private readonly audioOptions;
|
|
86
|
+
|
|
87
|
+
constructor(audioRecorderModule: typeof NitroSoundOrLegacyV4Module) {
|
|
88
|
+
this.module = audioRecorderModule.default;
|
|
89
|
+
this.audioOptions = Platform.select({
|
|
90
|
+
android: {
|
|
91
|
+
AudioEncodingBitRateAndroid: VoiceMessageConfig.DEFAULT.RECORDER.BIT_RATE,
|
|
92
|
+
AudioChannelsAndroid: VoiceMessageConfig.DEFAULT.RECORDER.CHANNELS,
|
|
93
|
+
AudioSamplingRateAndroid: VoiceMessageConfig.DEFAULT.RECORDER.SAMPLE_RATE,
|
|
94
|
+
AudioEncoderAndroid: audioRecorderModule.AudioEncoderAndroidType.AAC,
|
|
95
|
+
OutputFormatAndroid: audioRecorderModule.OutputFormatAndroidType.MPEG_4,
|
|
96
|
+
AudioSourceAndroid: audioRecorderModule.AudioSourceAndroidType.VOICE_RECOGNITION,
|
|
97
|
+
},
|
|
98
|
+
ios: {
|
|
99
|
+
AVEncoderBitRateKeyIOS: VoiceMessageConfig.DEFAULT.RECORDER.BIT_RATE,
|
|
100
|
+
AVNumberOfChannelsKeyIOS: VoiceMessageConfig.DEFAULT.RECORDER.CHANNELS,
|
|
101
|
+
AVSampleRateKeyIOS: VoiceMessageConfig.DEFAULT.RECORDER.SAMPLE_RATE,
|
|
102
|
+
AVFormatIDKeyIOS: 'mp4', // same with aac
|
|
103
|
+
AVEncoderAudioQualityKeyIOS: audioRecorderModule.AVEncoderAudioQualityIOSType.high,
|
|
104
|
+
},
|
|
105
|
+
default: {},
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
setSubscriptionDuration(duration: number): void {
|
|
110
|
+
try {
|
|
111
|
+
this.module.setSubscriptionDuration(duration);
|
|
112
|
+
} catch (error) {
|
|
113
|
+
Logger.warn('[RecorderService.Native] Failed to set subscription duration', error);
|
|
84
114
|
}
|
|
115
|
+
}
|
|
85
116
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
117
|
+
addRecordBackListener(callback: (data: RecordBackData) => void): void {
|
|
118
|
+
this.module.addRecordBackListener(callback);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
convertRecordPath(uri: string): string {
|
|
122
|
+
return uri;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async startRecorder(uri: string): Promise<void> {
|
|
126
|
+
await this.module.startRecorder(uri, this.audioOptions as Parameters<typeof this.module.startRecorder>[1]);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async stopRecorder(): Promise<void> {
|
|
130
|
+
await this.module.stopRecorder();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
92
133
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
134
|
+
class VoiceRecorder implements RecorderServiceInterface {
|
|
135
|
+
public uri: RecorderServiceInterface['uri'] = undefined;
|
|
136
|
+
public state: RecorderServiceInterface['state'] = 'idle';
|
|
137
|
+
public options: RecorderServiceInterface['options'] = {
|
|
138
|
+
minDuration: VoiceMessageConfig.DEFAULT.RECORDER.MIN_DURATION,
|
|
139
|
+
maxDuration: VoiceMessageConfig.DEFAULT.RECORDER.MAX_DURATION,
|
|
140
|
+
extension: VoiceMessageConfig.DEFAULT.RECORDER.EXTENSION,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
private _recordStartedAt = 0;
|
|
144
|
+
private _stopping = false;
|
|
145
|
+
private readonly recordingSubscribers = new Set<RecordingListener>();
|
|
146
|
+
private readonly stateSubscribers = new Set<StateListener>();
|
|
147
|
+
|
|
148
|
+
constructor(private readonly adapter: RecorderModuleAdapter, private readonly permissionModule: typeof Permissions) {
|
|
149
|
+
this.initialize();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private initialize(): void {
|
|
153
|
+
const setDurationResult = this.adapter.setSubscriptionDuration(0.1);
|
|
154
|
+
if (setDurationResult instanceof Promise) {
|
|
155
|
+
setDurationResult.catch((error) => {
|
|
156
|
+
Logger.warn('[RecorderService.Native] Failed to set subscription duration', error);
|
|
99
157
|
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
this.adapter.addRecordBackListener((data) => {
|
|
161
|
+
const completed = data.currentPosition >= this.options.maxDuration;
|
|
100
162
|
|
|
101
|
-
if (
|
|
102
|
-
|
|
163
|
+
if (completed) {
|
|
164
|
+
this.stop().catch((error) => {
|
|
165
|
+
Logger.warn('[RecorderService.Native] Failed to stop in RecordBackListener', error);
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (this.state === 'recording') {
|
|
169
|
+
this.recordingSubscribers.forEach((callback) => {
|
|
170
|
+
callback({ currentTime: data.currentPosition, completed });
|
|
171
|
+
});
|
|
103
172
|
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
104
175
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
176
|
+
private setState = (state: RecorderServiceInterface['state']): void => {
|
|
177
|
+
this.state = state;
|
|
178
|
+
this.stateSubscribers.forEach((callback) => {
|
|
179
|
+
callback(state);
|
|
180
|
+
});
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
private getRecorderStopSafeBuffer = (): number => {
|
|
184
|
+
const minWaitingTime = 500;
|
|
185
|
+
const elapsedTime = Date.now() - this._recordStartedAt;
|
|
186
|
+
if (elapsedTime > minWaitingTime) return 0;
|
|
187
|
+
else return minWaitingTime - elapsedTime;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
public requestPermission = async (): Promise<boolean> => {
|
|
191
|
+
const permission: Permission[] | undefined = Platform.select({
|
|
192
|
+
android: [this.permissionModule.PERMISSIONS.ANDROID.RECORD_AUDIO],
|
|
193
|
+
ios: [this.permissionModule.PERMISSIONS.IOS.MICROPHONE],
|
|
194
|
+
windows: [this.permissionModule.PERMISSIONS.WINDOWS.MICROPHONE],
|
|
195
|
+
default: undefined,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
if (Platform.OS === 'android' && Platform.Version <= 28) {
|
|
199
|
+
permission?.push(this.permissionModule.PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (permission) {
|
|
203
|
+
const status = await this.permissionModule.checkMultiple(permission);
|
|
204
|
+
if (nativePermissionGranted(status)) {
|
|
114
205
|
return true;
|
|
206
|
+
} else {
|
|
207
|
+
const status = await this.permissionModule.requestMultiple(permission);
|
|
208
|
+
return nativePermissionGranted(status);
|
|
115
209
|
}
|
|
116
|
-
}
|
|
210
|
+
} else {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
};
|
|
117
214
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
};
|
|
215
|
+
public addRecordingListener = (callback: RecordingListener): Unsubscribe => {
|
|
216
|
+
this.recordingSubscribers.add(callback);
|
|
217
|
+
return () => {
|
|
218
|
+
this.recordingSubscribers.delete(callback);
|
|
123
219
|
};
|
|
220
|
+
};
|
|
124
221
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
};
|
|
222
|
+
public addStateListener = (callback: StateListener): Unsubscribe => {
|
|
223
|
+
this.stateSubscribers.add(callback);
|
|
224
|
+
return () => {
|
|
225
|
+
this.stateSubscribers.delete(callback);
|
|
130
226
|
};
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
public record = async (uri: string): Promise<void> => {
|
|
230
|
+
if (matchesOneOf(this.state, ['idle', 'completed'])) {
|
|
231
|
+
try {
|
|
232
|
+
this.setState('preparing');
|
|
233
|
+
await this.adapter.startRecorder(uri);
|
|
131
234
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
try {
|
|
135
|
-
this.setState('preparing');
|
|
136
|
-
await module.startRecorder(uri, {
|
|
137
|
-
...this.audioOptions,
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
if (Platform.OS === 'android') {
|
|
141
|
-
this._recordStartedAt = Date.now();
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
this.uri = uri;
|
|
145
|
-
this.setState('recording');
|
|
146
|
-
} catch (e) {
|
|
147
|
-
this.setState('idle');
|
|
148
|
-
throw e;
|
|
235
|
+
if (Platform.OS === 'android') {
|
|
236
|
+
this._recordStartedAt = Date.now();
|
|
149
237
|
}
|
|
238
|
+
|
|
239
|
+
this.uri = uri;
|
|
240
|
+
this.setState('recording');
|
|
241
|
+
} catch (e) {
|
|
242
|
+
this.setState('idle');
|
|
243
|
+
throw e;
|
|
150
244
|
}
|
|
151
|
-
}
|
|
245
|
+
}
|
|
246
|
+
};
|
|
152
247
|
|
|
153
|
-
|
|
154
|
-
|
|
248
|
+
public stop = async (): Promise<void> => {
|
|
249
|
+
if (matchesOneOf(this.state, ['recording']) && !this._stopping) {
|
|
250
|
+
this._stopping = true;
|
|
251
|
+
try {
|
|
155
252
|
if (Platform.OS === 'android') {
|
|
156
|
-
const buffer = this.
|
|
253
|
+
const buffer = this.getRecorderStopSafeBuffer();
|
|
157
254
|
if (buffer > 0) await sleep(buffer);
|
|
158
255
|
}
|
|
159
256
|
|
|
160
|
-
await
|
|
257
|
+
await this.adapter.stopRecorder();
|
|
161
258
|
this.setState('completed');
|
|
259
|
+
} catch (error) {
|
|
260
|
+
Logger.error('[RecorderService.Native] Failed to stop recorder', error);
|
|
261
|
+
throw error;
|
|
262
|
+
} finally {
|
|
263
|
+
this._stopping = false;
|
|
162
264
|
}
|
|
163
|
-
}
|
|
265
|
+
}
|
|
266
|
+
};
|
|
164
267
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
268
|
+
public reset = async (): Promise<void> => {
|
|
269
|
+
await this.stop();
|
|
270
|
+
this.uri = undefined;
|
|
271
|
+
this.recordingSubscribers.clear();
|
|
272
|
+
this.setState('idle');
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
public convertRecordPath = (uri: string): string => {
|
|
276
|
+
return this.adapter.convertRecordPath(uri);
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const createNativeRecorderService = (modules: Modules): RecorderServiceInterface => {
|
|
281
|
+
const adapter = isNitroSoundOrLegacyV4Module(modules.audioRecorderModule)
|
|
282
|
+
? new NitroSoundOrLegacyV4Adapter(modules.audioRecorderModule)
|
|
283
|
+
: new AudioRecorderPlayerAdapter(modules.audioRecorderModule as typeof LegacyModule);
|
|
172
284
|
|
|
173
|
-
return new VoiceRecorder();
|
|
285
|
+
return new VoiceRecorder(adapter, modules.permissionModule);
|
|
174
286
|
};
|
|
175
287
|
|
|
288
|
+
function isNitroSoundOrLegacyV4Module(module: AudioRecorderModule): module is typeof NitroSoundOrLegacyV4Module {
|
|
289
|
+
const isNitroSound = 'createSound' in module && typeof module.createSound === 'function';
|
|
290
|
+
const isLegacyV4 =
|
|
291
|
+
'default' in module && 'getHybridObject' in module.default && typeof module.default.getHybridObject === 'function';
|
|
292
|
+
if (isLegacyV4) {
|
|
293
|
+
Logger.warn('react-native-audio-recorder-player is deprecated. Please use react-native-nitro-sound instead.');
|
|
294
|
+
}
|
|
295
|
+
return isNitroSound || isLegacyV4;
|
|
296
|
+
}
|
|
297
|
+
|
|
176
298
|
export default createNativeRecorderService;
|
package/src/platform/types.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import type * as ExpoAudio from 'expo-audio';
|
|
2
|
+
import type * as ExpoAV from 'expo-av';
|
|
1
3
|
import type * as ExpoDocumentPicker from 'expo-document-picker';
|
|
2
4
|
import type * as ExpoFs from 'expo-file-system';
|
|
3
5
|
import type * as ExpoImagePicker from 'expo-image-picker';
|
|
6
|
+
import type * as ExpoVideo from 'expo-video';
|
|
4
7
|
|
|
5
8
|
import type { FilePickerResponse } from '../platform/types';
|
|
6
9
|
import normalizeFile from './normalizeFile';
|
|
@@ -55,6 +58,29 @@ const expoBackwardUtils = {
|
|
|
55
58
|
}
|
|
56
59
|
},
|
|
57
60
|
},
|
|
61
|
+
expoAV: {
|
|
62
|
+
isLegacyAVModule(module: ExpoAudioModule | ExpoVideoModule): module is typeof ExpoAV {
|
|
63
|
+
try {
|
|
64
|
+
return 'Video' in module && 'Audio' in module && typeof module.Video === 'function';
|
|
65
|
+
} catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
isAudioModule(module: ExpoAudioModule): module is typeof ExpoAudio {
|
|
70
|
+
try {
|
|
71
|
+
return 'useAudioRecorder' in module && typeof module.useAudioRecorder === 'function';
|
|
72
|
+
} catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
isVideoModule(module: ExpoVideoModule): module is typeof ExpoVideo {
|
|
77
|
+
try {
|
|
78
|
+
return 'VideoView' in module && 'useVideoPlayer' in module && typeof module.useVideoPlayer === 'function';
|
|
79
|
+
} catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
},
|
|
58
84
|
toFileSize(info: ExpoFs.FileInfo) {
|
|
59
85
|
if ('size' in info) {
|
|
60
86
|
return info.size;
|
|
@@ -64,4 +90,7 @@ const expoBackwardUtils = {
|
|
|
64
90
|
},
|
|
65
91
|
};
|
|
66
92
|
|
|
93
|
+
export type ExpoAudioModule = typeof ExpoAV | typeof ExpoAudio;
|
|
94
|
+
export type ExpoVideoModule = typeof ExpoAV | typeof ExpoVideo;
|
|
95
|
+
|
|
67
96
|
export default expoBackwardUtils;
|
|
@@ -9,7 +9,9 @@ export interface ExpoPermissionResponse {
|
|
|
9
9
|
export interface ExpoMediaLibraryPermissionResponse extends ExpoPermissionResponse {
|
|
10
10
|
accessPrivileges?: 'all' | 'limited' | 'none';
|
|
11
11
|
}
|
|
12
|
-
export interface ExpoPushPermissionResponse
|
|
12
|
+
export interface ExpoPushPermissionResponse
|
|
13
|
+
extends Omit<ExpoPermissionResponse, 'status'>,
|
|
14
|
+
NotificationPermissionsStatus {}
|
|
13
15
|
|
|
14
16
|
const expoPermissionGranted = (
|
|
15
17
|
stats: Array<ExpoMediaLibraryPermissionResponse | ExpoPushPermissionResponse | ExpoPermissionResponse>,
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const VERSION = '3.
|
|
1
|
+
const VERSION = '3.11.1';
|
|
2
2
|
export default VERSION;
|