@sendbird/uikit-react-native 3.11.0 → 3.11.2
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 +7 -5
- package/lib/commonjs/domain/groupChannel/component/GroupChannelMessageList.js +0 -3
- package/lib/commonjs/domain/groupChannel/component/GroupChannelMessageList.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/createNotificationService.native.js +31 -6
- package/lib/commonjs/platform/createNotificationService.native.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/createRecorderService.expo.js +248 -130
- package/lib/commonjs/platform/createRecorderService.expo.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/domain/groupChannel/component/GroupChannelMessageList.js +0 -3
- package/lib/module/domain/groupChannel/component/GroupChannelMessageList.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/createNotificationService.native.js +32 -6
- package/lib/module/platform/createNotificationService.native.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/createRecorderService.expo.js +249 -131
- package/lib/module/platform/createRecorderService.expo.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/createNotificationService.native.d.ts +13 -1
- package/lib/typescript/src/platform/createPlayerService.expo.d.ts +2 -2
- package/lib/typescript/src/platform/createRecorderService.expo.d.ts +2 -2
- 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 +16 -5
- package/src/domain/groupChannel/component/GroupChannelMessageList.tsx +0 -3
- package/src/platform/createMediaService.expo.tsx +87 -9
- package/src/platform/createNotificationService.native.ts +53 -7
- package/src/platform/createPlayerService.expo.tsx +242 -109
- package/src/platform/createRecorderService.expo.tsx +267 -110
- package/src/utils/expoBackwardUtils.ts +29 -0
- package/src/utils/expoPermissionGranted.ts +3 -1
- package/src/version.ts +1 -1
|
@@ -1,47 +1,102 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import type
|
|
1
|
+
import type * as ExpoAudio from 'expo-audio';
|
|
2
|
+
import type * as ExpoAV from 'expo-av';
|
|
3
3
|
import { Platform } from 'react-native';
|
|
4
4
|
|
|
5
|
-
import { matchesOneOf, sleep } from '@sendbird/uikit-utils';
|
|
5
|
+
import { Logger, matchesOneOf, sleep } from '@sendbird/uikit-utils';
|
|
6
6
|
|
|
7
7
|
import VoiceMessageConfig from '../libs/VoiceMessageConfig';
|
|
8
|
+
import expoBackwardUtils from '../utils/expoBackwardUtils';
|
|
9
|
+
import type { ExpoAudioModule } from '../utils/expoBackwardUtils';
|
|
8
10
|
import expoPermissionGranted from '../utils/expoPermissionGranted';
|
|
9
11
|
import type { RecorderServiceInterface, Unsubscribe } from './types';
|
|
10
12
|
|
|
11
13
|
type RecordingListener = Parameters<RecorderServiceInterface['addRecordingListener']>[number];
|
|
12
14
|
type StateListener = Parameters<RecorderServiceInterface['addStateListener']>[number];
|
|
13
15
|
type Modules = {
|
|
14
|
-
avModule:
|
|
16
|
+
avModule: ExpoAudioModule;
|
|
15
17
|
};
|
|
16
|
-
const createExpoRecorderService = ({ avModule }: Modules): RecorderServiceInterface => {
|
|
17
|
-
class VoiceRecorder implements RecorderServiceInterface {
|
|
18
|
-
public uri: RecorderServiceInterface['uri'] = undefined;
|
|
19
|
-
public state: RecorderServiceInterface['state'] = 'idle';
|
|
20
|
-
public options: RecorderServiceInterface['options'] = {
|
|
21
|
-
minDuration: VoiceMessageConfig.DEFAULT.RECORDER.MIN_DURATION,
|
|
22
|
-
maxDuration: VoiceMessageConfig.DEFAULT.RECORDER.MAX_DURATION,
|
|
23
|
-
extension: VoiceMessageConfig.DEFAULT.RECORDER.EXTENSION,
|
|
24
|
-
};
|
|
25
18
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
19
|
+
interface AudioRecorderAdapter {
|
|
20
|
+
requestPermission(): Promise<boolean>;
|
|
21
|
+
record(): Promise<void>;
|
|
22
|
+
stop(): Promise<void>;
|
|
23
|
+
reset(): Promise<void>;
|
|
24
|
+
addRecordingListener(callback: RecordingListener): Unsubscribe;
|
|
25
|
+
addStateListener(callback: StateListener): Unsubscribe;
|
|
26
|
+
convertRecordPath(uri: string): string;
|
|
27
|
+
readonly state: RecorderServiceInterface['state'];
|
|
28
|
+
readonly options: RecorderServiceInterface['options'];
|
|
29
|
+
uri?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
abstract class BaseAudioRecorderAdapter implements AudioRecorderAdapter {
|
|
33
|
+
public uri: RecorderServiceInterface['uri'] = undefined;
|
|
34
|
+
public state: RecorderServiceInterface['state'] = 'idle';
|
|
35
|
+
public options: RecorderServiceInterface['options'] = {
|
|
36
|
+
minDuration: VoiceMessageConfig.DEFAULT.RECORDER.MIN_DURATION,
|
|
37
|
+
maxDuration: VoiceMessageConfig.DEFAULT.RECORDER.MAX_DURATION,
|
|
38
|
+
extension: VoiceMessageConfig.DEFAULT.RECORDER.EXTENSION,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
protected readonly _audioSettings = {
|
|
42
|
+
sampleRate: VoiceMessageConfig.DEFAULT.RECORDER.SAMPLE_RATE,
|
|
43
|
+
bitRate: VoiceMessageConfig.DEFAULT.RECORDER.BIT_RATE,
|
|
44
|
+
numberOfChannels: VoiceMessageConfig.DEFAULT.RECORDER.CHANNELS,
|
|
45
|
+
// encoding: mpeg4_aac
|
|
46
|
+
};
|
|
47
|
+
protected readonly _recordingSubscribers = new Set<RecordingListener>();
|
|
48
|
+
protected readonly _stateSubscribers = new Set<StateListener>();
|
|
49
|
+
|
|
50
|
+
// NOTE: In Android, even when startRecorder() is awaited, if stop() is executed immediately afterward, an error occurs
|
|
51
|
+
protected _recordStartedAt = 0;
|
|
52
|
+
protected _getRecorderStopSafeBuffer = () => {
|
|
53
|
+
const minWaitingTime = 500;
|
|
54
|
+
const elapsedTime = Date.now() - this._recordStartedAt;
|
|
55
|
+
if (elapsedTime > minWaitingTime) return 0;
|
|
56
|
+
else return minWaitingTime - elapsedTime;
|
|
57
|
+
};
|
|
58
|
+
protected setState = (state: RecorderServiceInterface['state']) => {
|
|
59
|
+
this.state = state;
|
|
60
|
+
this._stateSubscribers.forEach((callback) => {
|
|
61
|
+
callback(state);
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
public addRecordingListener = (callback: RecordingListener): Unsubscribe => {
|
|
66
|
+
this._recordingSubscribers.add(callback);
|
|
67
|
+
return () => {
|
|
68
|
+
this._recordingSubscribers.delete(callback);
|
|
33
69
|
};
|
|
70
|
+
};
|
|
34
71
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
sampleRate: VoiceMessageConfig.DEFAULT.RECORDER.SAMPLE_RATE,
|
|
40
|
-
bitRate: VoiceMessageConfig.DEFAULT.RECORDER.BIT_RATE,
|
|
41
|
-
numberOfChannels: VoiceMessageConfig.DEFAULT.RECORDER.CHANNELS,
|
|
42
|
-
// encoding: mpeg4_aac
|
|
72
|
+
public addStateListener = (callback: StateListener): Unsubscribe => {
|
|
73
|
+
this._stateSubscribers.add(callback);
|
|
74
|
+
return () => {
|
|
75
|
+
this._stateSubscribers.delete(callback);
|
|
43
76
|
};
|
|
44
|
-
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
public convertRecordPath = (uri: string): string => {
|
|
80
|
+
return uri;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
abstract requestPermission(): Promise<boolean>;
|
|
84
|
+
abstract record(): Promise<void>;
|
|
85
|
+
abstract stop(): Promise<void>;
|
|
86
|
+
abstract reset(): Promise<void>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
class LegacyExpoAVRecorderAdapter extends BaseAudioRecorderAdapter {
|
|
90
|
+
private readonly avModule: typeof ExpoAV;
|
|
91
|
+
|
|
92
|
+
private _recorder: ExpoAV.Audio.Recording;
|
|
93
|
+
private readonly _audioOptions: ExpoAV.Audio.RecordingOptions;
|
|
94
|
+
|
|
95
|
+
constructor(avModule: typeof ExpoAV) {
|
|
96
|
+
super();
|
|
97
|
+
this.avModule = avModule;
|
|
98
|
+
this._recorder = new avModule.Audio.Recording();
|
|
99
|
+
this._audioOptions = {
|
|
45
100
|
android: {
|
|
46
101
|
...this._audioSettings,
|
|
47
102
|
extension: `.${this.options.extension}`,
|
|
@@ -56,109 +111,211 @@ const createExpoRecorderService = ({ avModule }: Modules): RecorderServiceInterf
|
|
|
56
111
|
},
|
|
57
112
|
web: {},
|
|
58
113
|
};
|
|
114
|
+
}
|
|
59
115
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
116
|
+
private prepare = async () => {
|
|
117
|
+
this.setState('preparing');
|
|
118
|
+
if (Platform.OS === 'ios') {
|
|
119
|
+
await this.avModule.Audio.setAudioModeAsync({ allowsRecordingIOS: true, playsInSilentModeIOS: true });
|
|
120
|
+
}
|
|
65
121
|
|
|
66
|
-
|
|
67
|
-
|
|
122
|
+
if (this._recorder._isDoneRecording) {
|
|
123
|
+
this._recorder = new this.avModule.Audio.Recording();
|
|
124
|
+
}
|
|
125
|
+
this._recorder.setProgressUpdateInterval(100);
|
|
126
|
+
this._recorder.setOnRecordingStatusUpdate((status) => {
|
|
127
|
+
const completed = status.durationMillis >= this.options.maxDuration;
|
|
128
|
+
if (completed) this.stop();
|
|
129
|
+
if (status.isRecording) {
|
|
130
|
+
this._recordingSubscribers.forEach((callback) => {
|
|
131
|
+
callback({ currentTime: status.durationMillis, completed: completed });
|
|
132
|
+
});
|
|
68
133
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
134
|
+
});
|
|
135
|
+
await this._recorder.prepareToRecordAsync(this._audioOptions);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
public requestPermission = async (): Promise<boolean> => {
|
|
139
|
+
const status = await this.avModule.Audio.getPermissionsAsync();
|
|
140
|
+
if (expoPermissionGranted([status])) {
|
|
141
|
+
return true;
|
|
142
|
+
} else {
|
|
143
|
+
const status = await this.avModule.Audio.requestPermissionsAsync();
|
|
144
|
+
return expoPermissionGranted([status]);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
public record = async (): Promise<void> => {
|
|
149
|
+
if (matchesOneOf(this.state, ['idle', 'completed'])) {
|
|
150
|
+
try {
|
|
151
|
+
await this.prepare();
|
|
152
|
+
await this._recorder.startAsync();
|
|
153
|
+
|
|
154
|
+
if (Platform.OS === 'android') {
|
|
155
|
+
this._recordStartedAt = Date.now();
|
|
77
156
|
}
|
|
78
|
-
});
|
|
79
|
-
await this._recorder.prepareToRecordAsync(this._audioOptions);
|
|
80
|
-
};
|
|
81
157
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
158
|
+
const uri = this._recorder.getURI();
|
|
159
|
+
if (uri) this.uri = uri;
|
|
160
|
+
this.setState('recording');
|
|
161
|
+
} catch (e) {
|
|
162
|
+
this.setState('idle');
|
|
163
|
+
throw e;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
};
|
|
88
167
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const status = await avModule.Audio.requestPermissionsAsync();
|
|
95
|
-
return expoPermissionGranted([status]);
|
|
168
|
+
public stop = async (): Promise<void> => {
|
|
169
|
+
if (matchesOneOf(this.state, ['recording'])) {
|
|
170
|
+
if (Platform.OS === 'android') {
|
|
171
|
+
const buffer = this._getRecorderStopSafeBuffer();
|
|
172
|
+
if (buffer > 0) await sleep(buffer);
|
|
96
173
|
}
|
|
97
|
-
};
|
|
98
174
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
175
|
+
await this._recorder.stopAndUnloadAsync();
|
|
176
|
+
if (Platform.OS === 'ios') {
|
|
177
|
+
await this.avModule.Audio.setAudioModeAsync({ allowsRecordingIOS: false, playsInSilentModeIOS: false });
|
|
178
|
+
}
|
|
179
|
+
this.setState('completed');
|
|
180
|
+
}
|
|
181
|
+
};
|
|
105
182
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
183
|
+
public reset = async (): Promise<void> => {
|
|
184
|
+
await this.stop();
|
|
185
|
+
this.uri = undefined;
|
|
186
|
+
this._recordingSubscribers.clear();
|
|
187
|
+
this._recorder = new this.avModule.Audio.Recording();
|
|
188
|
+
this.setState('idle');
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
class ExpoAudioRecorderAdapter extends BaseAudioRecorderAdapter {
|
|
193
|
+
private readonly audioModule: typeof ExpoAudio;
|
|
194
|
+
private recorder: ExpoAudio.AudioRecorder | null = null;
|
|
195
|
+
private recordingUpdateInterval: NodeJS.Timeout | null = null;
|
|
196
|
+
|
|
197
|
+
constructor(audioModule: typeof ExpoAudio) {
|
|
198
|
+
super();
|
|
199
|
+
this.audioModule = audioModule;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private setListener = () => {
|
|
203
|
+
if (!this.recorder) return;
|
|
204
|
+
|
|
205
|
+
this.recordingUpdateInterval = setInterval(() => {
|
|
206
|
+
if (this.recorder && this.recorder.isRecording) {
|
|
207
|
+
const currentTime = this.recorder.currentTime * 1000;
|
|
208
|
+
const completed = currentTime >= this.options.maxDuration;
|
|
112
209
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
await this._recorder.startAsync();
|
|
118
|
-
|
|
119
|
-
if (Platform.OS === 'android') {
|
|
120
|
-
this._recordStartedAt = Date.now();
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const uri = this._recorder.getURI();
|
|
124
|
-
if (uri) this.uri = uri;
|
|
125
|
-
this.setState('recording');
|
|
126
|
-
} catch (e) {
|
|
127
|
-
this.setState('idle');
|
|
128
|
-
throw e;
|
|
210
|
+
if (completed) {
|
|
211
|
+
this.stop().catch((error) => {
|
|
212
|
+
Logger.warn('[RecorderService.Expo] Failed to stop in update interval', error);
|
|
213
|
+
});
|
|
129
214
|
}
|
|
215
|
+
|
|
216
|
+
this._recordingSubscribers.forEach((callback) => {
|
|
217
|
+
callback({ currentTime, completed });
|
|
218
|
+
});
|
|
130
219
|
}
|
|
220
|
+
}, 100);
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
private removeListener = () => {
|
|
224
|
+
if (this.recordingUpdateInterval) {
|
|
225
|
+
clearInterval(this.recordingUpdateInterval);
|
|
226
|
+
this.recordingUpdateInterval = null;
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
private prepare = async () => {
|
|
231
|
+
this.setState('preparing');
|
|
232
|
+
if (Platform.OS === 'ios') {
|
|
233
|
+
await this.audioModule.setAudioModeAsync({
|
|
234
|
+
allowsRecording: true,
|
|
235
|
+
playsInSilentMode: true,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const recordingOptions = {
|
|
240
|
+
...this._audioSettings,
|
|
241
|
+
extension: `.${this.options.extension}`,
|
|
131
242
|
};
|
|
132
243
|
|
|
133
|
-
|
|
134
|
-
|
|
244
|
+
this.recorder = new this.audioModule.AudioModule.AudioRecorder(recordingOptions);
|
|
245
|
+
await this.recorder.prepareToRecordAsync();
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
public requestPermission = async (): Promise<boolean> => {
|
|
249
|
+
const status = await this.audioModule.getRecordingPermissionsAsync();
|
|
250
|
+
if (expoPermissionGranted([status])) {
|
|
251
|
+
return true;
|
|
252
|
+
} else {
|
|
253
|
+
const status = await this.audioModule.requestRecordingPermissionsAsync();
|
|
254
|
+
return expoPermissionGranted([status]);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
public record = async (): Promise<void> => {
|
|
259
|
+
if (matchesOneOf(this.state, ['idle', 'completed'])) {
|
|
260
|
+
try {
|
|
261
|
+
await this.prepare();
|
|
262
|
+
this.setListener();
|
|
263
|
+
this.recorder?.record();
|
|
264
|
+
|
|
135
265
|
if (Platform.OS === 'android') {
|
|
136
|
-
|
|
137
|
-
if (buffer > 0) await sleep(buffer);
|
|
266
|
+
this._recordStartedAt = Date.now();
|
|
138
267
|
}
|
|
139
268
|
|
|
140
|
-
|
|
141
|
-
if (
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
this.setState('
|
|
269
|
+
const uri = this.recorder?.uri;
|
|
270
|
+
if (uri) this.uri = uri;
|
|
271
|
+
this.setState('recording');
|
|
272
|
+
} catch (e) {
|
|
273
|
+
this.setState('idle');
|
|
274
|
+
this.removeListener();
|
|
275
|
+
throw e;
|
|
145
276
|
}
|
|
146
|
-
}
|
|
277
|
+
}
|
|
278
|
+
};
|
|
147
279
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
};
|
|
280
|
+
public stop = async (): Promise<void> => {
|
|
281
|
+
if (matchesOneOf(this.state, ['recording'])) {
|
|
282
|
+
if (Platform.OS === 'android') {
|
|
283
|
+
const buffer = this._getRecorderStopSafeBuffer();
|
|
284
|
+
if (buffer > 0) await sleep(buffer);
|
|
285
|
+
}
|
|
155
286
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
287
|
+
await this.recorder?.stop();
|
|
288
|
+
this.removeListener();
|
|
289
|
+
if (Platform.OS === 'ios') {
|
|
290
|
+
await this.audioModule.setAudioModeAsync({
|
|
291
|
+
allowsRecording: false,
|
|
292
|
+
playsInSilentMode: false,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
this.setState('completed');
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
public reset = async (): Promise<void> => {
|
|
300
|
+
await this.stop();
|
|
301
|
+
this.recorder = null;
|
|
302
|
+
this.uri = undefined;
|
|
303
|
+
this._recordingSubscribers.clear();
|
|
304
|
+
this._stateSubscribers.clear();
|
|
305
|
+
this.setState('idle');
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const createExpoRecorderService = ({ avModule }: Modules): RecorderServiceInterface => {
|
|
310
|
+
if (expoBackwardUtils.expoAV.isLegacyAVModule(avModule)) {
|
|
311
|
+
Logger.warn(
|
|
312
|
+
'[RecorderService.Expo] expo-av is deprecated and will be removed in Expo 54. Please migrate to expo-audio.',
|
|
313
|
+
);
|
|
159
314
|
}
|
|
160
315
|
|
|
161
|
-
return
|
|
316
|
+
return expoBackwardUtils.expoAV.isAudioModule(avModule)
|
|
317
|
+
? new ExpoAudioRecorderAdapter(avModule)
|
|
318
|
+
: new LegacyExpoAVRecorderAdapter(avModule);
|
|
162
319
|
};
|
|
163
320
|
|
|
164
321
|
export default createExpoRecorderService;
|
|
@@ -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.11.
|
|
1
|
+
const VERSION = '3.11.2';
|
|
2
2
|
export default VERSION;
|