@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
@@ -5,150 +5,233 @@ import { Platform } from 'react-native';
5
5
  import { Logger, matchesOneOf, sleep } from '@sendbird/uikit-utils';
6
6
  import VoiceMessageConfig from '../libs/VoiceMessageConfig';
7
7
  import nativePermissionGranted from '../utils/nativePermissionGranted';
8
- const createNativeRecorderService = ({
9
- audioRecorderModule,
10
- permissionModule
11
- }) => {
12
- const module = new audioRecorderModule.default();
13
- class VoiceRecorder {
14
- constructor() {
15
- _defineProperty(this, "uri", undefined);
16
- _defineProperty(this, "state", 'idle');
17
- _defineProperty(this, "options", {
18
- minDuration: VoiceMessageConfig.DEFAULT.RECORDER.MIN_DURATION,
19
- maxDuration: VoiceMessageConfig.DEFAULT.RECORDER.MAX_DURATION,
20
- extension: VoiceMessageConfig.DEFAULT.RECORDER.EXTENSION
21
- });
22
- // NOTE: In Android, even when startRecorder() is awaited, if stop() is executed immediately afterward, an error occurs
23
- _defineProperty(this, "_recordStartedAt", 0);
24
- _defineProperty(this, "_getRecorderStopSafeBuffer", () => {
25
- const minWaitingTime = 500;
26
- const elapsedTime = Date.now() - this._recordStartedAt;
27
- if (elapsedTime > minWaitingTime) return 0;else return minWaitingTime - elapsedTime;
28
- });
29
- _defineProperty(this, "recordingSubscribers", new Set());
30
- _defineProperty(this, "stateSubscribers", new Set());
31
- _defineProperty(this, "audioSettings", {
32
- sampleRate: VoiceMessageConfig.DEFAULT.RECORDER.SAMPLE_RATE,
33
- bitRate: VoiceMessageConfig.DEFAULT.RECORDER.BIT_RATE,
34
- audioChannels: VoiceMessageConfig.DEFAULT.RECORDER.CHANNELS
35
- // encoding: mpeg4_aac
8
+ class AudioRecorderPlayerAdapter {
9
+ constructor(audioRecorderModule) {
10
+ _defineProperty(this, "module", void 0);
11
+ _defineProperty(this, "audioOptions", void 0);
12
+ this.module = new audioRecorderModule.default();
13
+ this.audioOptions = Platform.select({
14
+ android: {
15
+ AudioEncodingBitRateAndroid: VoiceMessageConfig.DEFAULT.RECORDER.BIT_RATE,
16
+ AudioChannelsAndroid: VoiceMessageConfig.DEFAULT.RECORDER.CHANNELS,
17
+ AudioSamplingRateAndroid: VoiceMessageConfig.DEFAULT.RECORDER.SAMPLE_RATE,
18
+ AudioEncoderAndroid: audioRecorderModule.AudioEncoderAndroidType.AAC,
19
+ OutputFormatAndroid: audioRecorderModule.OutputFormatAndroidType.MPEG_4,
20
+ AudioSourceAndroid: audioRecorderModule.AudioSourceAndroidType.VOICE_RECOGNITION
21
+ },
22
+ ios: {
23
+ AVEncoderBitRateKeyIOS: VoiceMessageConfig.DEFAULT.RECORDER.BIT_RATE,
24
+ AVNumberOfChannelsKeyIOS: VoiceMessageConfig.DEFAULT.RECORDER.CHANNELS,
25
+ AVSampleRateKeyIOS: VoiceMessageConfig.DEFAULT.RECORDER.SAMPLE_RATE,
26
+ AVFormatIDKeyIOS: audioRecorderModule.AVEncodingOption.mp4,
27
+ // same with aac
28
+ AVEncoderAudioQualityKeyIOS: audioRecorderModule.AVEncoderAudioQualityIOSType.high
29
+ },
30
+ default: {}
31
+ });
32
+ }
33
+ async setSubscriptionDuration(duration) {
34
+ await this.module.setSubscriptionDuration(duration);
35
+ }
36
+ addRecordBackListener(callback) {
37
+ this.module.addRecordBackListener(callback);
38
+ }
39
+ convertRecordPath(uri) {
40
+ return Platform.OS === 'ios' ? uri.split('/').pop() || uri : uri;
41
+ }
42
+ async startRecorder(uri) {
43
+ await this.module.startRecorder(uri, this.audioOptions);
44
+ }
45
+ async stopRecorder() {
46
+ await this.module.stopRecorder();
47
+ }
48
+ }
49
+ class NitroSoundOrLegacyV4Adapter {
50
+ constructor(audioRecorderModule) {
51
+ _defineProperty(this, "module", void 0);
52
+ _defineProperty(this, "audioOptions", void 0);
53
+ this.module = audioRecorderModule.default;
54
+ this.audioOptions = Platform.select({
55
+ android: {
56
+ AudioEncodingBitRateAndroid: VoiceMessageConfig.DEFAULT.RECORDER.BIT_RATE,
57
+ AudioChannelsAndroid: VoiceMessageConfig.DEFAULT.RECORDER.CHANNELS,
58
+ AudioSamplingRateAndroid: VoiceMessageConfig.DEFAULT.RECORDER.SAMPLE_RATE,
59
+ AudioEncoderAndroid: audioRecorderModule.AudioEncoderAndroidType.AAC,
60
+ OutputFormatAndroid: audioRecorderModule.OutputFormatAndroidType.MPEG_4,
61
+ AudioSourceAndroid: audioRecorderModule.AudioSourceAndroidType.VOICE_RECOGNITION
62
+ },
63
+ ios: {
64
+ AVEncoderBitRateKeyIOS: VoiceMessageConfig.DEFAULT.RECORDER.BIT_RATE,
65
+ AVNumberOfChannelsKeyIOS: VoiceMessageConfig.DEFAULT.RECORDER.CHANNELS,
66
+ AVSampleRateKeyIOS: VoiceMessageConfig.DEFAULT.RECORDER.SAMPLE_RATE,
67
+ AVFormatIDKeyIOS: 'mp4',
68
+ // same with aac
69
+ AVEncoderAudioQualityKeyIOS: audioRecorderModule.AVEncoderAudioQualityIOSType.high
70
+ },
71
+ default: {}
72
+ });
73
+ }
74
+ setSubscriptionDuration(duration) {
75
+ try {
76
+ this.module.setSubscriptionDuration(duration);
77
+ } catch (error) {
78
+ Logger.warn('[RecorderService.Native] Failed to set subscription duration', error);
79
+ }
80
+ }
81
+ addRecordBackListener(callback) {
82
+ this.module.addRecordBackListener(callback);
83
+ }
84
+ convertRecordPath(uri) {
85
+ return uri;
86
+ }
87
+ async startRecorder(uri) {
88
+ await this.module.startRecorder(uri, this.audioOptions);
89
+ }
90
+ async stopRecorder() {
91
+ await this.module.stopRecorder();
92
+ }
93
+ }
94
+ class VoiceRecorder {
95
+ constructor(adapter, permissionModule) {
96
+ this.adapter = adapter;
97
+ this.permissionModule = permissionModule;
98
+ _defineProperty(this, "uri", undefined);
99
+ _defineProperty(this, "state", 'idle');
100
+ _defineProperty(this, "options", {
101
+ minDuration: VoiceMessageConfig.DEFAULT.RECORDER.MIN_DURATION,
102
+ maxDuration: VoiceMessageConfig.DEFAULT.RECORDER.MAX_DURATION,
103
+ extension: VoiceMessageConfig.DEFAULT.RECORDER.EXTENSION
104
+ });
105
+ _defineProperty(this, "_recordStartedAt", 0);
106
+ _defineProperty(this, "_stopping", false);
107
+ _defineProperty(this, "recordingSubscribers", new Set());
108
+ _defineProperty(this, "stateSubscribers", new Set());
109
+ _defineProperty(this, "setState", state => {
110
+ this.state = state;
111
+ this.stateSubscribers.forEach(callback => {
112
+ callback(state);
36
113
  });
37
- _defineProperty(this, "audioOptions", Platform.select({
38
- android: {
39
- AudioEncodingBitRateAndroid: this.audioSettings.bitRate,
40
- AudioChannelsAndroid: this.audioSettings.audioChannels,
41
- AudioSamplingRateAndroid: this.audioSettings.sampleRate,
42
- AudioEncoderAndroid: audioRecorderModule.AudioEncoderAndroidType.AAC,
43
- OutputFormatAndroid: audioRecorderModule.OutputFormatAndroidType.MPEG_4,
44
- AudioSourceAndroid: audioRecorderModule.AudioSourceAndroidType.VOICE_RECOGNITION
45
- },
46
- ios: {
47
- AVEncoderBitRateKeyIOS: this.audioSettings.bitRate,
48
- AVNumberOfChannelsKeyIOS: this.audioSettings.audioChannels,
49
- AVSampleRateKeyIOS: this.audioSettings.sampleRate,
50
- AVFormatIDKeyIOS: audioRecorderModule.AVEncodingOption.mp4,
51
- // same with aac
52
- AVEncoderAudioQualityKeyIOS: audioRecorderModule.AVEncoderAudioQualityIOSType.high
53
- },
54
- default: {}
55
- }));
56
- _defineProperty(this, "setState", state => {
57
- this.state = state;
58
- this.stateSubscribers.forEach(callback => {
59
- callback(state);
60
- });
114
+ });
115
+ _defineProperty(this, "getRecorderStopSafeBuffer", () => {
116
+ const minWaitingTime = 500;
117
+ const elapsedTime = Date.now() - this._recordStartedAt;
118
+ if (elapsedTime > minWaitingTime) return 0;else return minWaitingTime - elapsedTime;
119
+ });
120
+ _defineProperty(this, "requestPermission", async () => {
121
+ const permission = Platform.select({
122
+ android: [this.permissionModule.PERMISSIONS.ANDROID.RECORD_AUDIO],
123
+ ios: [this.permissionModule.PERMISSIONS.IOS.MICROPHONE],
124
+ windows: [this.permissionModule.PERMISSIONS.WINDOWS.MICROPHONE],
125
+ default: undefined
61
126
  });
62
- _defineProperty(this, "requestPermission", async () => {
63
- const permission = Platform.select({
64
- android: [permissionModule.PERMISSIONS.ANDROID.RECORD_AUDIO],
65
- ios: [permissionModule.PERMISSIONS.IOS.MICROPHONE],
66
- windows: [permissionModule.PERMISSIONS.WINDOWS.MICROPHONE],
67
- default: undefined
68
- });
69
- if (Platform.OS === 'android' && Platform.Version <= 28) {
70
- permission === null || permission === void 0 || permission.push(permissionModule.PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE);
71
- }
72
- if (permission) {
73
- const status = await permissionModule.checkMultiple(permission);
74
- if (nativePermissionGranted(status)) {
75
- return true;
76
- } else {
77
- const status = await permissionModule.requestMultiple(permission);
78
- return nativePermissionGranted(status);
79
- }
80
- } else {
127
+ if (Platform.OS === 'android' && Platform.Version <= 28) {
128
+ permission === null || permission === void 0 || permission.push(this.permissionModule.PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE);
129
+ }
130
+ if (permission) {
131
+ const status = await this.permissionModule.checkMultiple(permission);
132
+ if (nativePermissionGranted(status)) {
81
133
  return true;
134
+ } else {
135
+ const status = await this.permissionModule.requestMultiple(permission);
136
+ return nativePermissionGranted(status);
82
137
  }
83
- });
84
- _defineProperty(this, "addRecordingListener", callback => {
85
- this.recordingSubscribers.add(callback);
86
- return () => {
87
- this.recordingSubscribers.delete(callback);
88
- };
89
- });
90
- _defineProperty(this, "addStateListener", callback => {
91
- this.stateSubscribers.add(callback);
92
- return () => {
93
- this.stateSubscribers.delete(callback);
94
- };
95
- });
96
- _defineProperty(this, "record", async uri => {
97
- if (matchesOneOf(this.state, ['idle', 'completed'])) {
98
- try {
99
- this.setState('preparing');
100
- await module.startRecorder(uri, {
101
- ...this.audioOptions
102
- });
103
- if (Platform.OS === 'android') {
104
- this._recordStartedAt = Date.now();
105
- }
106
- this.uri = uri;
107
- this.setState('recording');
108
- } catch (e) {
109
- this.setState('idle');
110
- throw e;
138
+ } else {
139
+ return true;
140
+ }
141
+ });
142
+ _defineProperty(this, "addRecordingListener", callback => {
143
+ this.recordingSubscribers.add(callback);
144
+ return () => {
145
+ this.recordingSubscribers.delete(callback);
146
+ };
147
+ });
148
+ _defineProperty(this, "addStateListener", callback => {
149
+ this.stateSubscribers.add(callback);
150
+ return () => {
151
+ this.stateSubscribers.delete(callback);
152
+ };
153
+ });
154
+ _defineProperty(this, "record", async uri => {
155
+ if (matchesOneOf(this.state, ['idle', 'completed'])) {
156
+ try {
157
+ this.setState('preparing');
158
+ await this.adapter.startRecorder(uri);
159
+ if (Platform.OS === 'android') {
160
+ this._recordStartedAt = Date.now();
111
161
  }
162
+ this.uri = uri;
163
+ this.setState('recording');
164
+ } catch (e) {
165
+ this.setState('idle');
166
+ throw e;
112
167
  }
113
- });
114
- _defineProperty(this, "stop", async () => {
115
- if (matchesOneOf(this.state, ['recording'])) {
168
+ }
169
+ });
170
+ _defineProperty(this, "stop", async () => {
171
+ if (matchesOneOf(this.state, ['recording']) && !this._stopping) {
172
+ this._stopping = true;
173
+ try {
116
174
  if (Platform.OS === 'android') {
117
- const buffer = this._getRecorderStopSafeBuffer();
175
+ const buffer = this.getRecorderStopSafeBuffer();
118
176
  if (buffer > 0) await sleep(buffer);
119
177
  }
120
- await module.stopRecorder();
178
+ await this.adapter.stopRecorder();
121
179
  this.setState('completed');
180
+ } catch (error) {
181
+ Logger.error('[RecorderService.Native] Failed to stop recorder', error);
182
+ throw error;
183
+ } finally {
184
+ this._stopping = false;
122
185
  }
123
- });
124
- _defineProperty(this, "reset", async () => {
125
- await this.stop();
126
- this.uri = undefined;
127
- this.recordingSubscribers.clear();
128
- this.setState('idle');
129
- });
130
- module.setSubscriptionDuration(0.1).catch(error => {
186
+ }
187
+ });
188
+ _defineProperty(this, "reset", async () => {
189
+ await this.stop();
190
+ this.uri = undefined;
191
+ this.recordingSubscribers.clear();
192
+ this.setState('idle');
193
+ });
194
+ _defineProperty(this, "convertRecordPath", uri => {
195
+ return this.adapter.convertRecordPath(uri);
196
+ });
197
+ this.initialize();
198
+ }
199
+ initialize() {
200
+ const setDurationResult = this.adapter.setSubscriptionDuration(0.1);
201
+ if (setDurationResult instanceof Promise) {
202
+ setDurationResult.catch(error => {
131
203
  Logger.warn('[RecorderService.Native] Failed to set subscription duration', error);
132
204
  });
133
- module.addRecordBackListener(data => {
134
- const completed = data.currentPosition >= this.options.maxDuration;
135
- if (completed) {
136
- this.stop().catch(error => {
137
- Logger.warn('[RecorderService.Native] Failed to stop in RecordBackListener', error);
138
- });
139
- }
140
- if (this.state === 'recording') {
141
- this.recordingSubscribers.forEach(callback => {
142
- callback({
143
- currentTime: data.currentPosition,
144
- completed
145
- });
146
- });
147
- }
148
- });
149
205
  }
206
+ this.adapter.addRecordBackListener(data => {
207
+ const completed = data.currentPosition >= this.options.maxDuration;
208
+ if (completed) {
209
+ this.stop().catch(error => {
210
+ Logger.warn('[RecorderService.Native] Failed to stop in RecordBackListener', error);
211
+ });
212
+ }
213
+ if (this.state === 'recording') {
214
+ this.recordingSubscribers.forEach(callback => {
215
+ callback({
216
+ currentTime: data.currentPosition,
217
+ completed
218
+ });
219
+ });
220
+ }
221
+ });
150
222
  }
151
- return new VoiceRecorder();
223
+ }
224
+ const createNativeRecorderService = modules => {
225
+ const adapter = isNitroSoundOrLegacyV4Module(modules.audioRecorderModule) ? new NitroSoundOrLegacyV4Adapter(modules.audioRecorderModule) : new AudioRecorderPlayerAdapter(modules.audioRecorderModule);
226
+ return new VoiceRecorder(adapter, modules.permissionModule);
152
227
  };
228
+ function isNitroSoundOrLegacyV4Module(module) {
229
+ const isNitroSound = 'createSound' in module && typeof module.createSound === 'function';
230
+ const isLegacyV4 = 'default' in module && 'getHybridObject' in module.default && typeof module.default.getHybridObject === 'function';
231
+ if (isLegacyV4) {
232
+ Logger.warn('react-native-audio-recorder-player is deprecated. Please use react-native-nitro-sound instead.');
233
+ }
234
+ return isNitroSound || isLegacyV4;
235
+ }
153
236
  export default createNativeRecorderService;
154
237
  //# sourceMappingURL=createRecorderService.native.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["Platform","Logger","matchesOneOf","sleep","VoiceMessageConfig","nativePermissionGranted","createNativeRecorderService","audioRecorderModule","permissionModule","module","default","VoiceRecorder","constructor","_defineProperty","undefined","minDuration","DEFAULT","RECORDER","MIN_DURATION","maxDuration","MAX_DURATION","extension","EXTENSION","minWaitingTime","elapsedTime","Date","now","_recordStartedAt","Set","sampleRate","SAMPLE_RATE","bitRate","BIT_RATE","audioChannels","CHANNELS","select","android","AudioEncodingBitRateAndroid","audioSettings","AudioChannelsAndroid","AudioSamplingRateAndroid","AudioEncoderAndroid","AudioEncoderAndroidType","AAC","OutputFormatAndroid","OutputFormatAndroidType","MPEG_4","AudioSourceAndroid","AudioSourceAndroidType","VOICE_RECOGNITION","ios","AVEncoderBitRateKeyIOS","AVNumberOfChannelsKeyIOS","AVSampleRateKeyIOS","AVFormatIDKeyIOS","AVEncodingOption","mp4","AVEncoderAudioQualityKeyIOS","AVEncoderAudioQualityIOSType","high","state","stateSubscribers","forEach","callback","permission","PERMISSIONS","ANDROID","RECORD_AUDIO","IOS","MICROPHONE","windows","WINDOWS","OS","Version","push","WRITE_EXTERNAL_STORAGE","status","checkMultiple","requestMultiple","recordingSubscribers","add","delete","uri","setState","startRecorder","audioOptions","e","buffer","_getRecorderStopSafeBuffer","stopRecorder","stop","clear","setSubscriptionDuration","catch","error","warn","addRecordBackListener","data","completed","currentPosition","options","currentTime"],"sources":["createRecorderService.native.tsx"],"sourcesContent":["import { Platform } from 'react-native';\nimport * as RNAudioRecorder from 'react-native-audio-recorder-player';\nimport * as Permissions from 'react-native-permissions';\nimport { Permission } from 'react-native-permissions/src/types';\n\nimport { Logger, matchesOneOf, sleep } from '@sendbird/uikit-utils';\n\nimport VoiceMessageConfig from '../libs/VoiceMessageConfig';\nimport nativePermissionGranted from '../utils/nativePermissionGranted';\nimport type { RecorderServiceInterface, Unsubscribe } from './types';\n\ntype RecordingListener = Parameters<RecorderServiceInterface['addRecordingListener']>[number];\ntype StateListener = Parameters<RecorderServiceInterface['addStateListener']>[number];\ntype Modules = {\n audioRecorderModule: typeof RNAudioRecorder;\n permissionModule: typeof Permissions;\n};\nconst createNativeRecorderService = ({ audioRecorderModule, permissionModule }: Modules): RecorderServiceInterface => {\n const module = new audioRecorderModule.default();\n\n class VoiceRecorder implements RecorderServiceInterface {\n public uri: RecorderServiceInterface['uri'] = undefined;\n public state: RecorderServiceInterface['state'] = 'idle';\n public options: RecorderServiceInterface['options'] = {\n minDuration: VoiceMessageConfig.DEFAULT.RECORDER.MIN_DURATION,\n maxDuration: VoiceMessageConfig.DEFAULT.RECORDER.MAX_DURATION,\n extension: VoiceMessageConfig.DEFAULT.RECORDER.EXTENSION,\n };\n\n // NOTE: In Android, even when startRecorder() is awaited, if stop() is executed immediately afterward, an error occurs\n private _recordStartedAt = 0;\n private _getRecorderStopSafeBuffer = () => {\n const minWaitingTime = 500;\n const elapsedTime = Date.now() - this._recordStartedAt;\n if (elapsedTime > minWaitingTime) return 0;\n else return minWaitingTime - elapsedTime;\n };\n\n private readonly recordingSubscribers = new Set<RecordingListener>();\n private readonly stateSubscribers = new Set<StateListener>();\n private readonly audioSettings = {\n sampleRate: VoiceMessageConfig.DEFAULT.RECORDER.SAMPLE_RATE,\n bitRate: VoiceMessageConfig.DEFAULT.RECORDER.BIT_RATE,\n audioChannels: VoiceMessageConfig.DEFAULT.RECORDER.CHANNELS,\n // encoding: mpeg4_aac\n };\n private readonly audioOptions = Platform.select({\n android: {\n AudioEncodingBitRateAndroid: this.audioSettings.bitRate,\n AudioChannelsAndroid: this.audioSettings.audioChannels,\n AudioSamplingRateAndroid: this.audioSettings.sampleRate,\n AudioEncoderAndroid: audioRecorderModule.AudioEncoderAndroidType.AAC,\n OutputFormatAndroid: audioRecorderModule.OutputFormatAndroidType.MPEG_4,\n AudioSourceAndroid: audioRecorderModule.AudioSourceAndroidType.VOICE_RECOGNITION,\n },\n ios: {\n AVEncoderBitRateKeyIOS: this.audioSettings.bitRate,\n AVNumberOfChannelsKeyIOS: this.audioSettings.audioChannels,\n AVSampleRateKeyIOS: this.audioSettings.sampleRate,\n AVFormatIDKeyIOS: audioRecorderModule.AVEncodingOption.mp4, // same with aac\n AVEncoderAudioQualityKeyIOS: audioRecorderModule.AVEncoderAudioQualityIOSType.high,\n },\n default: {},\n });\n\n constructor() {\n module.setSubscriptionDuration(0.1).catch((error) => {\n Logger.warn('[RecorderService.Native] Failed to set subscription duration', error);\n });\n module.addRecordBackListener((data) => {\n const completed = data.currentPosition >= this.options.maxDuration;\n\n if (completed) {\n this.stop().catch((error) => {\n Logger.warn('[RecorderService.Native] Failed to stop in RecordBackListener', error);\n });\n }\n if (this.state === 'recording') {\n this.recordingSubscribers.forEach((callback) => {\n callback({ currentTime: data.currentPosition, completed });\n });\n }\n });\n }\n\n private setState = (state: RecorderServiceInterface['state']) => {\n this.state = state;\n this.stateSubscribers.forEach((callback) => {\n callback(state);\n });\n };\n\n public requestPermission = async (): Promise<boolean> => {\n const permission: Permission[] | undefined = Platform.select({\n android: [permissionModule.PERMISSIONS.ANDROID.RECORD_AUDIO],\n ios: [permissionModule.PERMISSIONS.IOS.MICROPHONE],\n windows: [permissionModule.PERMISSIONS.WINDOWS.MICROPHONE],\n default: undefined,\n });\n\n if (Platform.OS === 'android' && Platform.Version <= 28) {\n permission?.push(permissionModule.PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE);\n }\n\n if (permission) {\n const status = await permissionModule.checkMultiple(permission);\n if (nativePermissionGranted(status)) {\n return true;\n } else {\n const status = await permissionModule.requestMultiple(permission);\n return nativePermissionGranted(status);\n }\n } else {\n return true;\n }\n };\n\n public addRecordingListener = (callback: RecordingListener): Unsubscribe => {\n this.recordingSubscribers.add(callback);\n return () => {\n this.recordingSubscribers.delete(callback);\n };\n };\n\n public addStateListener = (callback: StateListener): Unsubscribe => {\n this.stateSubscribers.add(callback);\n return () => {\n this.stateSubscribers.delete(callback);\n };\n };\n\n public record = async (uri: string): Promise<void> => {\n if (matchesOneOf(this.state, ['idle', 'completed'])) {\n try {\n this.setState('preparing');\n await module.startRecorder(uri, {\n ...this.audioOptions,\n });\n\n if (Platform.OS === 'android') {\n this._recordStartedAt = Date.now();\n }\n\n this.uri = uri;\n this.setState('recording');\n } catch (e) {\n this.setState('idle');\n throw e;\n }\n }\n };\n\n public stop = async (): Promise<void> => {\n if (matchesOneOf(this.state, ['recording'])) {\n if (Platform.OS === 'android') {\n const buffer = this._getRecorderStopSafeBuffer();\n if (buffer > 0) await sleep(buffer);\n }\n\n await module.stopRecorder();\n this.setState('completed');\n }\n };\n\n public reset = async (): Promise<void> => {\n await this.stop();\n this.uri = undefined;\n this.recordingSubscribers.clear();\n this.setState('idle');\n };\n }\n\n return new VoiceRecorder();\n};\n\nexport default createNativeRecorderService;\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["Platform","Logger","matchesOneOf","sleep","VoiceMessageConfig","nativePermissionGranted","AudioRecorderPlayerAdapter","constructor","audioRecorderModule","_defineProperty","module","default","audioOptions","select","android","AudioEncodingBitRateAndroid","DEFAULT","RECORDER","BIT_RATE","AudioChannelsAndroid","CHANNELS","AudioSamplingRateAndroid","SAMPLE_RATE","AudioEncoderAndroid","AudioEncoderAndroidType","AAC","OutputFormatAndroid","OutputFormatAndroidType","MPEG_4","AudioSourceAndroid","AudioSourceAndroidType","VOICE_RECOGNITION","ios","AVEncoderBitRateKeyIOS","AVNumberOfChannelsKeyIOS","AVSampleRateKeyIOS","AVFormatIDKeyIOS","AVEncodingOption","mp4","AVEncoderAudioQualityKeyIOS","AVEncoderAudioQualityIOSType","high","setSubscriptionDuration","duration","addRecordBackListener","callback","convertRecordPath","uri","OS","split","pop","startRecorder","stopRecorder","NitroSoundOrLegacyV4Adapter","error","warn","VoiceRecorder","adapter","permissionModule","undefined","minDuration","MIN_DURATION","maxDuration","MAX_DURATION","extension","EXTENSION","Set","state","stateSubscribers","forEach","minWaitingTime","elapsedTime","Date","now","_recordStartedAt","permission","PERMISSIONS","ANDROID","RECORD_AUDIO","IOS","MICROPHONE","windows","WINDOWS","Version","push","WRITE_EXTERNAL_STORAGE","status","checkMultiple","requestMultiple","recordingSubscribers","add","delete","setState","e","_stopping","buffer","getRecorderStopSafeBuffer","stop","clear","initialize","setDurationResult","Promise","catch","data","completed","currentPosition","options","currentTime","createNativeRecorderService","modules","isNitroSoundOrLegacyV4Module","isNitroSound","createSound","isLegacyV4","getHybridObject"],"sources":["createRecorderService.native.tsx"],"sourcesContent":["import { Platform } from 'react-native';\nimport type * as LegacyModule from 'react-native-audio-recorder-player';\nimport type * as NitroSoundOrLegacyV4Module from 'react-native-nitro-sound';\nimport * as Permissions from 'react-native-permissions';\nimport { Permission } from 'react-native-permissions/src/types';\n\nimport { Logger, matchesOneOf, sleep } from '@sendbird/uikit-utils';\n\nimport VoiceMessageConfig from '../libs/VoiceMessageConfig';\nimport nativePermissionGranted from '../utils/nativePermissionGranted';\nimport type { RecorderServiceInterface, Unsubscribe } from './types';\n\nexport type AudioRecorderModule = typeof LegacyModule | typeof NitroSoundOrLegacyV4Module;\n\ntype RecordingListener = Parameters<RecorderServiceInterface['addRecordingListener']>[number];\ntype StateListener = Parameters<RecorderServiceInterface['addStateListener']>[number];\ntype Modules = {\n audioRecorderModule: AudioRecorderModule;\n permissionModule: typeof Permissions;\n};\n\ninterface RecordBackData {\n currentPosition: number;\n duration?: number;\n}\n\ninterface RecorderModuleAdapter {\n setSubscriptionDuration(duration: number): Promise<void> | void;\n addRecordBackListener(callback: (data: RecordBackData) => void): void;\n convertRecordPath(uri: string): string;\n startRecorder(uri: string): Promise<void>;\n stopRecorder(): Promise<void>;\n}\n\nclass AudioRecorderPlayerAdapter implements RecorderModuleAdapter {\n private module: InstanceType<typeof LegacyModule.default>;\n private readonly audioOptions;\n\n constructor(audioRecorderModule: typeof LegacyModule) {\n this.module = new audioRecorderModule.default();\n\n this.audioOptions = Platform.select({\n android: {\n AudioEncodingBitRateAndroid: VoiceMessageConfig.DEFAULT.RECORDER.BIT_RATE,\n AudioChannelsAndroid: VoiceMessageConfig.DEFAULT.RECORDER.CHANNELS,\n AudioSamplingRateAndroid: VoiceMessageConfig.DEFAULT.RECORDER.SAMPLE_RATE,\n AudioEncoderAndroid: audioRecorderModule.AudioEncoderAndroidType.AAC,\n OutputFormatAndroid: audioRecorderModule.OutputFormatAndroidType.MPEG_4,\n AudioSourceAndroid: audioRecorderModule.AudioSourceAndroidType.VOICE_RECOGNITION,\n },\n ios: {\n AVEncoderBitRateKeyIOS: VoiceMessageConfig.DEFAULT.RECORDER.BIT_RATE,\n AVNumberOfChannelsKeyIOS: VoiceMessageConfig.DEFAULT.RECORDER.CHANNELS,\n AVSampleRateKeyIOS: VoiceMessageConfig.DEFAULT.RECORDER.SAMPLE_RATE,\n AVFormatIDKeyIOS: audioRecorderModule.AVEncodingOption.mp4, // same with aac\n AVEncoderAudioQualityKeyIOS: audioRecorderModule.AVEncoderAudioQualityIOSType.high,\n },\n default: {},\n });\n }\n\n async setSubscriptionDuration(duration: number): Promise<void> {\n await this.module.setSubscriptionDuration(duration);\n }\n\n addRecordBackListener(callback: (data: RecordBackData) => void): void {\n this.module.addRecordBackListener(callback);\n }\n\n convertRecordPath(uri: string): string {\n return Platform.OS === 'ios' ? uri.split('/').pop() || uri : uri;\n }\n\n async startRecorder(uri: string): Promise<void> {\n await this.module.startRecorder(uri, this.audioOptions as Parameters<typeof this.module.startRecorder>[1]);\n }\n\n async stopRecorder(): Promise<void> {\n await this.module.stopRecorder();\n }\n}\n\nclass NitroSoundOrLegacyV4Adapter implements RecorderModuleAdapter {\n private module;\n private readonly audioOptions;\n\n constructor(audioRecorderModule: typeof NitroSoundOrLegacyV4Module) {\n this.module = audioRecorderModule.default;\n this.audioOptions = Platform.select({\n android: {\n AudioEncodingBitRateAndroid: VoiceMessageConfig.DEFAULT.RECORDER.BIT_RATE,\n AudioChannelsAndroid: VoiceMessageConfig.DEFAULT.RECORDER.CHANNELS,\n AudioSamplingRateAndroid: VoiceMessageConfig.DEFAULT.RECORDER.SAMPLE_RATE,\n AudioEncoderAndroid: audioRecorderModule.AudioEncoderAndroidType.AAC,\n OutputFormatAndroid: audioRecorderModule.OutputFormatAndroidType.MPEG_4,\n AudioSourceAndroid: audioRecorderModule.AudioSourceAndroidType.VOICE_RECOGNITION,\n },\n ios: {\n AVEncoderBitRateKeyIOS: VoiceMessageConfig.DEFAULT.RECORDER.BIT_RATE,\n AVNumberOfChannelsKeyIOS: VoiceMessageConfig.DEFAULT.RECORDER.CHANNELS,\n AVSampleRateKeyIOS: VoiceMessageConfig.DEFAULT.RECORDER.SAMPLE_RATE,\n AVFormatIDKeyIOS: 'mp4', // same with aac\n AVEncoderAudioQualityKeyIOS: audioRecorderModule.AVEncoderAudioQualityIOSType.high,\n },\n default: {},\n });\n }\n\n setSubscriptionDuration(duration: number): void {\n try {\n this.module.setSubscriptionDuration(duration);\n } catch (error) {\n Logger.warn('[RecorderService.Native] Failed to set subscription duration', error);\n }\n }\n\n addRecordBackListener(callback: (data: RecordBackData) => void): void {\n this.module.addRecordBackListener(callback);\n }\n\n convertRecordPath(uri: string): string {\n return uri;\n }\n\n async startRecorder(uri: string): Promise<void> {\n await this.module.startRecorder(uri, this.audioOptions as Parameters<typeof this.module.startRecorder>[1]);\n }\n\n async stopRecorder(): Promise<void> {\n await this.module.stopRecorder();\n }\n}\n\nclass VoiceRecorder implements RecorderServiceInterface {\n public uri: RecorderServiceInterface['uri'] = undefined;\n public state: RecorderServiceInterface['state'] = 'idle';\n public options: RecorderServiceInterface['options'] = {\n minDuration: VoiceMessageConfig.DEFAULT.RECORDER.MIN_DURATION,\n maxDuration: VoiceMessageConfig.DEFAULT.RECORDER.MAX_DURATION,\n extension: VoiceMessageConfig.DEFAULT.RECORDER.EXTENSION,\n };\n\n private _recordStartedAt = 0;\n private _stopping = false;\n private readonly recordingSubscribers = new Set<RecordingListener>();\n private readonly stateSubscribers = new Set<StateListener>();\n\n constructor(private readonly adapter: RecorderModuleAdapter, private readonly permissionModule: typeof Permissions) {\n this.initialize();\n }\n\n private initialize(): void {\n const setDurationResult = this.adapter.setSubscriptionDuration(0.1);\n if (setDurationResult instanceof Promise) {\n setDurationResult.catch((error) => {\n Logger.warn('[RecorderService.Native] Failed to set subscription duration', error);\n });\n }\n\n this.adapter.addRecordBackListener((data) => {\n const completed = data.currentPosition >= this.options.maxDuration;\n\n if (completed) {\n this.stop().catch((error) => {\n Logger.warn('[RecorderService.Native] Failed to stop in RecordBackListener', error);\n });\n }\n if (this.state === 'recording') {\n this.recordingSubscribers.forEach((callback) => {\n callback({ currentTime: data.currentPosition, completed });\n });\n }\n });\n }\n\n private setState = (state: RecorderServiceInterface['state']): void => {\n this.state = state;\n this.stateSubscribers.forEach((callback) => {\n callback(state);\n });\n };\n\n private getRecorderStopSafeBuffer = (): number => {\n const minWaitingTime = 500;\n const elapsedTime = Date.now() - this._recordStartedAt;\n if (elapsedTime > minWaitingTime) return 0;\n else return minWaitingTime - elapsedTime;\n };\n\n public requestPermission = async (): Promise<boolean> => {\n const permission: Permission[] | undefined = Platform.select({\n android: [this.permissionModule.PERMISSIONS.ANDROID.RECORD_AUDIO],\n ios: [this.permissionModule.PERMISSIONS.IOS.MICROPHONE],\n windows: [this.permissionModule.PERMISSIONS.WINDOWS.MICROPHONE],\n default: undefined,\n });\n\n if (Platform.OS === 'android' && Platform.Version <= 28) {\n permission?.push(this.permissionModule.PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE);\n }\n\n if (permission) {\n const status = await this.permissionModule.checkMultiple(permission);\n if (nativePermissionGranted(status)) {\n return true;\n } else {\n const status = await this.permissionModule.requestMultiple(permission);\n return nativePermissionGranted(status);\n }\n } else {\n return true;\n }\n };\n\n public addRecordingListener = (callback: RecordingListener): Unsubscribe => {\n this.recordingSubscribers.add(callback);\n return () => {\n this.recordingSubscribers.delete(callback);\n };\n };\n\n public addStateListener = (callback: StateListener): Unsubscribe => {\n this.stateSubscribers.add(callback);\n return () => {\n this.stateSubscribers.delete(callback);\n };\n };\n\n public record = async (uri: string): Promise<void> => {\n if (matchesOneOf(this.state, ['idle', 'completed'])) {\n try {\n this.setState('preparing');\n await this.adapter.startRecorder(uri);\n\n if (Platform.OS === 'android') {\n this._recordStartedAt = Date.now();\n }\n\n this.uri = uri;\n this.setState('recording');\n } catch (e) {\n this.setState('idle');\n throw e;\n }\n }\n };\n\n public stop = async (): Promise<void> => {\n if (matchesOneOf(this.state, ['recording']) && !this._stopping) {\n this._stopping = true;\n try {\n if (Platform.OS === 'android') {\n const buffer = this.getRecorderStopSafeBuffer();\n if (buffer > 0) await sleep(buffer);\n }\n\n await this.adapter.stopRecorder();\n this.setState('completed');\n } catch (error) {\n Logger.error('[RecorderService.Native] Failed to stop recorder', error);\n throw error;\n } finally {\n this._stopping = false;\n }\n }\n };\n\n public reset = async (): Promise<void> => {\n await this.stop();\n this.uri = undefined;\n this.recordingSubscribers.clear();\n this.setState('idle');\n };\n\n public convertRecordPath = (uri: string): string => {\n return this.adapter.convertRecordPath(uri);\n };\n}\n\nconst createNativeRecorderService = (modules: Modules): RecorderServiceInterface => {\n const adapter = isNitroSoundOrLegacyV4Module(modules.audioRecorderModule)\n ? new NitroSoundOrLegacyV4Adapter(modules.audioRecorderModule)\n : new AudioRecorderPlayerAdapter(modules.audioRecorderModule as typeof LegacyModule);\n\n return new VoiceRecorder(adapter, modules.permissionModule);\n};\n\nfunction isNitroSoundOrLegacyV4Module(module: AudioRecorderModule): module is typeof NitroSoundOrLegacyV4Module {\n const isNitroSound = 'createSound' in module && typeof module.createSound === 'function';\n const isLegacyV4 =\n 'default' in module && 'getHybridObject' in module.default && typeof module.default.getHybridObject === 'function';\n if (isLegacyV4) {\n Logger.warn('react-native-audio-recorder-player is deprecated. Please use react-native-nitro-sound instead.');\n }\n return isNitroSound || isLegacyV4;\n}\n\nexport default createNativeRecorderService;\n"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["import type { ReactNode } from 'react';\n\nimport type SBUError from '../libs/SBUError';\n\nexport type Unsubscribe = () => void | undefined;\nexport type DownloadedPath = string;\nexport type FilePickerResponse = FileType | null;\n\n/**\n * We are following the file format of react-native FormData\n * @see https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Network/FormData.js#L37-L41\n * */\nexport type FileType = { name: string; uri: string; size: number; type: string };\n\n// ---------- NotificationService ---------- //\nexport interface NotificationServiceInterface {\n hasPushPermission(): Promise<boolean>;\n requestPushPermission(): Promise<boolean>;\n\n getAPNSToken(): Promise<string | null>;\n getFCMToken(): Promise<string | null>;\n onTokenRefresh(handler: (token: string) => void): Unsubscribe;\n}\n\n// ---------- ClipboardService ---------- //\nexport interface ClipboardServiceInterface {\n setString(text: string): void;\n getString(): Promise<string>;\n}\n\n// ---------- FileService ---------- //\nexport interface FileServiceInterface extends FilePickerServiceInterface, FileSystemServiceInterface {}\n\nexport interface OpenResultListener {\n onOpenFailure?: (error: SBUError, originError?: unknown) => void;\n}\nexport interface OpenMediaLibraryOptions extends OpenResultListener {\n selectionLimit?: number;\n mediaType?: 'photo' | 'video' | 'all';\n}\nexport interface OpenCameraOptions extends OpenResultListener {\n cameraType?: 'front' | 'back';\n mediaType?: 'photo' | 'video' | 'all';\n}\nexport type OpenDocumentOptions = OpenResultListener;\nexport interface SaveOptions {\n fileUrl: string;\n fileName: string;\n fileType?: string | null;\n}\n\nexport interface FilePickerServiceInterface {\n openMediaLibrary(options?: OpenMediaLibraryOptions): Promise<null | FilePickerResponse[]>;\n openCamera(options?: OpenCameraOptions): Promise<FilePickerResponse>;\n openDocument(options?: OpenDocumentOptions): Promise<FilePickerResponse>;\n}\n\nexport interface FileSystemServiceInterface {\n // NOTE: On iOS, You can access the downloaded files by providing options below to info.plist\n // - Supports opening documents in place\n // - Application supports iTunes file sharing\n save(options?: SaveOptions): Promise<DownloadedPath | null>;\n createRecordFilePath(customExtension?: string): { recordFilePath: string; uri: string };\n}\n\n// ---------- MediaService ---------- //\nexport type VideoProps = {\n source: { uri: string } | number;\n resizeMode?: 'cover' | 'contain' | 'stretch';\n onLoad?: () => void;\n};\n\nexport type GetVideoThumbnailOptions = {\n url: string;\n timeMills?: number;\n quality?: number;\n};\nexport type GetVideoThumbnailResult = Promise<{ path: string } | null>;\n\nexport type CompressImageOptions = {\n /**\n * A uri of image file to compress\n * */\n uri: string;\n\n /**\n * A resize width, apply only to downscale\n * */\n maxWidth?: number;\n\n /**\n * A resize height, apply only to downscale\n * */\n maxHeight?: number;\n\n /**\n * A value in range 0.0 - 1.0 specifying compression level of the result image.\n * 1 means highest quality and 0 the lowest quality.\n * */\n compressionRate?: number;\n};\nexport type CompressImageResult = Promise<{ uri: string; size: number } | null>;\n\nexport interface MediaServiceInterface {\n VideoComponent<Props = {}>(props: VideoProps & Props): ReactNode;\n getVideoThumbnail(options: GetVideoThumbnailOptions): GetVideoThumbnailResult;\n compressImage(options: CompressImageOptions): CompressImageResult;\n}\n\n// ---------- PlayerService ---------- //\nexport interface PlayerServiceInterface {\n uri?: string;\n state: 'idle' | 'preparing' | 'playing' | 'paused' | 'stopped';\n\n /**\n * Check and request permission for the player.\n * */\n requestPermission(): Promise<boolean>;\n\n /**\n * Add a playback listener.\n * */\n addPlaybackListener(\n callback: (params: { currentTime: number; duration: number; stopped: boolean }) => void,\n ): Unsubscribe;\n\n /**\n * Add a state listener.\n * */\n addStateListener(callback: (state: PlayerServiceInterface['state']) => void): Unsubscribe;\n\n /**\n * State transition:\n * [idle, stopped] to [playing] - start from the beginning\n * [paused] to [playing] - resume\n * */\n play(uri: string): Promise<void>;\n\n /**\n * State transition:\n * [playing] to [paused]\n * */\n pause(): Promise<void>;\n\n /**\n * State transition:\n * [preparing, playing, paused] to [stop]\n * */\n stop(): Promise<void>;\n\n /**\n * State transition:\n * [*] to [idle]\n * */\n reset(): Promise<void>;\n\n /**\n * Seek time, only available when the state is [playing, paused].\n * */\n seek(time: number): Promise<void>;\n}\n\n// ---------- RecorderService ---------- //\nexport interface RecorderOptions {\n /**\n * Minimum recording duration (milliseconds).\n * */\n minDuration: number;\n\n /**\n * Maximum recording duration (milliseconds).\n * */\n maxDuration: number;\n\n /**\n * File extension for recorded audio file\n * */\n extension: string;\n}\n\nexport interface RecorderServiceInterface {\n uri?: string;\n options: RecorderOptions;\n state: 'idle' | 'preparing' | 'recording' | 'completed';\n\n /**\n * Check and request permission for the recorder.\n * */\n requestPermission(): Promise<boolean>;\n\n /**\n * Add recording listener.\n * */\n addRecordingListener(callback: (params: { currentTime: number; completed: boolean }) => void): Unsubscribe;\n\n /**\n * Add state listener.\n * */\n addStateListener(callback: (state: RecorderServiceInterface['state']) => void): Unsubscribe;\n\n /**\n * State transition:\n * [idle, completed] to [recording]\n * */\n record(uri?: string): Promise<void>;\n\n /**\n * State transition:\n * [recording] to [completed]\n * */\n stop(): Promise<void>;\n\n /**\n * State transition:\n * [*] to [idle]\n * */\n reset(): Promise<void>;\n}\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["import type { ReactNode } from 'react';\n\nimport type SBUError from '../libs/SBUError';\n\nexport type Unsubscribe = () => void | undefined;\nexport type DownloadedPath = string;\nexport type FilePickerResponse = FileType | null;\n\n/**\n * We are following the file format of react-native FormData\n * @see https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Network/FormData.js#L37-L41\n * */\nexport type FileType = { name: string; uri: string; size: number; type: string };\n\n// ---------- NotificationService ---------- //\nexport interface NotificationServiceInterface {\n hasPushPermission(): Promise<boolean>;\n requestPushPermission(): Promise<boolean>;\n\n getAPNSToken(): Promise<string | null>;\n getFCMToken(): Promise<string | null>;\n onTokenRefresh(handler: (token: string) => void): Unsubscribe;\n}\n\n// ---------- ClipboardService ---------- //\nexport interface ClipboardServiceInterface {\n setString(text: string): void;\n getString(): Promise<string>;\n}\n\n// ---------- FileService ---------- //\nexport interface FileServiceInterface extends FilePickerServiceInterface, FileSystemServiceInterface {}\n\nexport interface OpenResultListener {\n onOpenFailure?: (error: SBUError, originError?: unknown) => void;\n}\nexport interface OpenMediaLibraryOptions extends OpenResultListener {\n selectionLimit?: number;\n mediaType?: 'photo' | 'video' | 'all';\n}\nexport interface OpenCameraOptions extends OpenResultListener {\n cameraType?: 'front' | 'back';\n mediaType?: 'photo' | 'video' | 'all';\n}\nexport type OpenDocumentOptions = OpenResultListener;\nexport interface SaveOptions {\n fileUrl: string;\n fileName: string;\n fileType?: string | null;\n}\n\nexport interface FilePickerServiceInterface {\n openMediaLibrary(options?: OpenMediaLibraryOptions): Promise<null | FilePickerResponse[]>;\n openCamera(options?: OpenCameraOptions): Promise<FilePickerResponse>;\n openDocument(options?: OpenDocumentOptions): Promise<FilePickerResponse>;\n}\n\nexport interface FileSystemServiceInterface {\n // NOTE: On iOS, You can access the downloaded files by providing options below to info.plist\n // - Supports opening documents in place\n // - Application supports iTunes file sharing\n save(options?: SaveOptions): Promise<DownloadedPath | null>;\n createRecordFilePath(customExtension?: string): { recordFilePath: string; uri: string };\n}\n\n// ---------- MediaService ---------- //\nexport type VideoProps = {\n source: { uri: string } | number;\n resizeMode?: 'cover' | 'contain' | 'stretch';\n onLoad?: () => void;\n};\n\nexport type GetVideoThumbnailOptions = {\n url: string;\n timeMills?: number;\n quality?: number;\n};\nexport type GetVideoThumbnailResult = Promise<{ path: string } | null>;\n\nexport type CompressImageOptions = {\n /**\n * A uri of image file to compress\n * */\n uri: string;\n\n /**\n * A resize width, apply only to downscale\n * */\n maxWidth?: number;\n\n /**\n * A resize height, apply only to downscale\n * */\n maxHeight?: number;\n\n /**\n * A value in range 0.0 - 1.0 specifying compression level of the result image.\n * 1 means highest quality and 0 the lowest quality.\n * */\n compressionRate?: number;\n};\nexport type CompressImageResult = Promise<{ uri: string; size: number } | null>;\n\nexport interface MediaServiceInterface {\n VideoComponent<Props = {}>(props: VideoProps & Props): ReactNode;\n getVideoThumbnail(options: GetVideoThumbnailOptions): GetVideoThumbnailResult;\n compressImage(options: CompressImageOptions): CompressImageResult;\n}\n\n// ---------- PlayerService ---------- //\nexport interface PlayerServiceInterface {\n uri?: string;\n state: 'idle' | 'preparing' | 'playing' | 'paused' | 'stopped';\n\n /**\n * Check and request permission for the player.\n * */\n requestPermission(): Promise<boolean>;\n\n /**\n * Add a playback listener.\n * */\n addPlaybackListener(\n callback: (params: { currentTime: number; duration: number; stopped: boolean }) => void,\n ): Unsubscribe;\n\n /**\n * Add a state listener.\n * */\n addStateListener(callback: (state: PlayerServiceInterface['state']) => void): Unsubscribe;\n\n /**\n * State transition:\n * [idle, stopped] to [playing] - start from the beginning\n * [paused] to [playing] - resume\n * */\n play(uri: string): Promise<void>;\n\n /**\n * State transition:\n * [playing] to [paused]\n * */\n pause(): Promise<void>;\n\n /**\n * State transition:\n * [preparing, playing, paused] to [stop]\n * */\n stop(): Promise<void>;\n\n /**\n * State transition:\n * [*] to [idle]\n * */\n reset(): Promise<void>;\n\n /**\n * Seek time, only available when the state is [playing, paused].\n * */\n seek(time: number): Promise<void>;\n}\n\n// ---------- RecorderService ---------- //\nexport interface RecorderOptions {\n /**\n * Minimum recording duration (milliseconds).\n * */\n minDuration: number;\n\n /**\n * Maximum recording duration (milliseconds).\n * */\n maxDuration: number;\n\n /**\n * File extension for recorded audio file\n * */\n extension: string;\n}\n\nexport interface RecorderServiceInterface {\n uri?: string;\n options: RecorderOptions;\n state: 'idle' | 'preparing' | 'recording' | 'completed';\n\n /**\n * Check and request permission for the recorder.\n * */\n requestPermission(): Promise<boolean>;\n\n /**\n * Add recording listener.\n * */\n addRecordingListener(callback: (params: { currentTime: number; completed: boolean }) => void): Unsubscribe;\n\n /**\n * Add state listener.\n * */\n addStateListener(callback: (state: RecorderServiceInterface['state']) => void): Unsubscribe;\n\n /**\n * State transition:\n * [idle, completed] to [recording]\n * */\n record(uri?: string): Promise<void>;\n\n /**\n * State transition:\n * [recording] to [completed]\n * */\n stop(): Promise<void>;\n\n /**\n * State transition:\n * [*] to [idle]\n * */\n reset(): Promise<void>;\n\n /**\n * Get the record path processed for the specific platform and adapter.\n * */\n convertRecordPath(uri: string): string;\n}\n"],"mappings":"","ignoreList":[]}
@@ -1,3 +1,3 @@
1
- const VERSION = '3.10.3';
1
+ const VERSION = '3.11.0';
2
2
  export default VERSION;
3
3
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["VERSION"],"sources":["version.ts"],"sourcesContent":["const VERSION = '3.10.3';\nexport default VERSION;\n"],"mappings":"AAAA,MAAMA,OAAO,GAAG,QAAQ;AACxB,eAAeA,OAAO","ignoreList":[]}
1
+ {"version":3,"names":["VERSION"],"sources":["version.ts"],"sourcesContent":["const VERSION = '3.11.0';\nexport default VERSION;\n"],"mappings":"AAAA,MAAMA,OAAO,GAAG,QAAQ;AACxB,eAAeA,OAAO","ignoreList":[]}
@@ -14,7 +14,7 @@ import type { StringSet } from '../localization/StringSet.type';
14
14
  import type { ClipboardServiceInterface, FileServiceInterface, MediaServiceInterface, NotificationServiceInterface, PlayerServiceInterface, RecorderServiceInterface } from '../platform/types';
15
15
  import { ErrorBoundaryProps, LocalCacheStorage } from '../types';
16
16
  export declare const SendbirdUIKit: Readonly<{
17
- VERSION: "3.10.3";
17
+ VERSION: "3.11.0";
18
18
  PLATFORM: string;
19
19
  DEFAULT: {
20
20
  AUTO_PUSH_TOKEN_REGISTRATION: boolean;
@@ -1,9 +1,11 @@
1
- import type * as RNAudioRecorder from 'react-native-audio-recorder-player';
1
+ import type * as LegacyModule from 'react-native-audio-recorder-player';
2
+ import type * as NitroSoundOrLegacyV4Module from 'react-native-nitro-sound';
2
3
  import * as Permissions from 'react-native-permissions';
3
4
  import type { PlayerServiceInterface } from './types';
5
+ export type AudioPlayerModule = typeof LegacyModule | typeof NitroSoundOrLegacyV4Module;
4
6
  type Modules = {
5
- audioRecorderModule: typeof RNAudioRecorder;
7
+ audioRecorderModule: AudioPlayerModule;
6
8
  permissionModule: typeof Permissions;
7
9
  };
8
- declare const createNativePlayerService: ({ audioRecorderModule, permissionModule }: Modules) => PlayerServiceInterface;
10
+ declare const createNativePlayerService: (modules: Modules) => PlayerServiceInterface;
9
11
  export default createNativePlayerService;
@@ -1,9 +1,11 @@
1
- import * as RNAudioRecorder from 'react-native-audio-recorder-player';
1
+ import type * as LegacyModule from 'react-native-audio-recorder-player';
2
+ import type * as NitroSoundOrLegacyV4Module from 'react-native-nitro-sound';
2
3
  import * as Permissions from 'react-native-permissions';
3
4
  import type { RecorderServiceInterface } from './types';
5
+ export type AudioRecorderModule = typeof LegacyModule | typeof NitroSoundOrLegacyV4Module;
4
6
  type Modules = {
5
- audioRecorderModule: typeof RNAudioRecorder;
7
+ audioRecorderModule: AudioRecorderModule;
6
8
  permissionModule: typeof Permissions;
7
9
  };
8
- declare const createNativeRecorderService: ({ audioRecorderModule, permissionModule }: Modules) => RecorderServiceInterface;
10
+ declare const createNativeRecorderService: (modules: Modules) => RecorderServiceInterface;
9
11
  export default createNativeRecorderService;
@@ -191,4 +191,8 @@ export interface RecorderServiceInterface {
191
191
  * [*] to [idle]
192
192
  * */
193
193
  reset(): Promise<void>;
194
+ /**
195
+ * Get the record path processed for the specific platform and adapter.
196
+ * */
197
+ convertRecordPath(uri: string): string;
194
198
  }
@@ -1,2 +1,2 @@
1
- declare const VERSION = "3.10.3";
1
+ declare const VERSION = "3.11.0";
2
2
  export default VERSION;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendbird/uikit-react-native",
3
- "version": "3.10.3",
3
+ "version": "3.11.0",
4
4
  "description": "Sendbird UIKit for React Native: A feature-rich and customizable chat UI kit with messaging, channel management, and user authentication.",
5
5
  "keywords": [
6
6
  "sendbird",
@@ -60,10 +60,10 @@
60
60
  },
61
61
  "dependencies": {
62
62
  "@openspacelabs/react-native-zoomable-view": "^2.1.5",
63
- "@sendbird/uikit-chat-hooks": "3.10.3",
64
- "@sendbird/uikit-react-native-foundation": "3.10.3",
63
+ "@sendbird/uikit-chat-hooks": "3.11.0",
64
+ "@sendbird/uikit-react-native-foundation": "3.11.0",
65
65
  "@sendbird/uikit-tools": "0.0.15",
66
- "@sendbird/uikit-utils": "3.10.3"
66
+ "@sendbird/uikit-utils": "3.11.0"
67
67
  },
68
68
  "devDependencies": {
69
69
  "@bam.tech/react-native-image-resizer": "^3.0.4",
@@ -98,6 +98,8 @@
98
98
  "react-native-file-access": "^3.1.0",
99
99
  "react-native-image-picker": "^7.1.2",
100
100
  "react-native-mmkv": "^2.12.2",
101
+ "react-native-nitro-modules": "^0.29.4",
102
+ "react-native-nitro-sound": "^0.2.0",
101
103
  "react-native-permissions": "^3.10.1",
102
104
  "react-native-safe-area-context": "^4.10.8",
103
105
  "react-native-video": "^6.3.0",
@@ -132,6 +134,8 @@
132
134
  "react-native-file-access": ">=2.4.3",
133
135
  "react-native-image-picker": ">=4.7.1",
134
136
  "react-native-mmkv": ">=2.0.0",
137
+ "react-native-nitro-modules": "*",
138
+ "react-native-nitro-sound": ">=0.2.0",
135
139
  "react-native-permissions": ">=3.6.0",
136
140
  "react-native-safe-area-context": ">=3.3.2",
137
141
  "react-native-video": ">=5.2.0"
@@ -202,6 +206,15 @@
202
206
  },
203
207
  "react-native-video": {
204
208
  "optional": true
209
+ },
210
+ "react-native-audio-recorder-player": {
211
+ "optional": true
212
+ },
213
+ "react-native-nitro-modules": {
214
+ "optional": true
215
+ },
216
+ "react-native-nitro-sound": {
217
+ "optional": true
205
218
  }
206
219
  },
207
220
  "react-native-builder-bob": {
@@ -218,5 +231,5 @@
218
231
  ]
219
232
  ]
220
233
  },
221
- "gitHead": "2f85a74e995069845ec6876d1339a66b299b642b"
234
+ "gitHead": "f0ffc478a073fe16f571a3f298fe101cf8359d4a"
222
235
  }
@@ -75,7 +75,7 @@ const VoiceMessageInput = ({ onClose, onSend }: VoiceMessageInputProps) => {
75
75
  break;
76
76
  }
77
77
  } catch (error) {
78
- Logger.warn('Failed to run voice message action.', state);
78
+ Logger.warn('Failed to run voice message action.', state, error);
79
79
  }
80
80
  };
81
81
  const renderActionIcon = () => {