@sendbird/uikit-react-native
Version:
Sendbird UIKit for React Native: A feature-rich and customizable chat UI kit with messaging, channel management, and user authentication.
277 lines (276 loc) • 11.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _reactNative = require("react-native");
var _uikitUtils = require("@sendbird/uikit-utils");
var _VoiceMessageConfig = _interopRequireDefault(require("../libs/VoiceMessageConfig"));
var _expoBackwardUtils = _interopRequireDefault(require("../utils/expoBackwardUtils"));
var _expoPermissionGranted = _interopRequireDefault(require("../utils/expoPermissionGranted"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
class BaseAudioRecorderAdapter {
constructor() {
_defineProperty(this, "uri", undefined);
_defineProperty(this, "state", 'idle');
_defineProperty(this, "options", {
minDuration: _VoiceMessageConfig.default.DEFAULT.RECORDER.MIN_DURATION,
maxDuration: _VoiceMessageConfig.default.DEFAULT.RECORDER.MAX_DURATION,
extension: _VoiceMessageConfig.default.DEFAULT.RECORDER.EXTENSION
});
_defineProperty(this, "_audioSettings", {
sampleRate: _VoiceMessageConfig.default.DEFAULT.RECORDER.SAMPLE_RATE,
bitRate: _VoiceMessageConfig.default.DEFAULT.RECORDER.BIT_RATE,
numberOfChannels: _VoiceMessageConfig.default.DEFAULT.RECORDER.CHANNELS
// encoding: mpeg4_aac
});
_defineProperty(this, "_recordingSubscribers", new Set());
_defineProperty(this, "_stateSubscribers", new Set());
// NOTE: In Android, even when startRecorder() is awaited, if stop() is executed immediately afterward, an error occurs
_defineProperty(this, "_recordStartedAt", 0);
_defineProperty(this, "_getRecorderStopSafeBuffer", () => {
const minWaitingTime = 500;
const elapsedTime = Date.now() - this._recordStartedAt;
if (elapsedTime > minWaitingTime) return 0;else return minWaitingTime - elapsedTime;
});
_defineProperty(this, "setState", state => {
this.state = state;
this._stateSubscribers.forEach(callback => {
callback(state);
});
});
_defineProperty(this, "addRecordingListener", callback => {
this._recordingSubscribers.add(callback);
return () => {
this._recordingSubscribers.delete(callback);
};
});
_defineProperty(this, "addStateListener", callback => {
this._stateSubscribers.add(callback);
return () => {
this._stateSubscribers.delete(callback);
};
});
_defineProperty(this, "convertRecordPath", uri => {
return uri;
});
}
}
class LegacyExpoAVRecorderAdapter extends BaseAudioRecorderAdapter {
constructor(avModule) {
super();
_defineProperty(this, "avModule", void 0);
_defineProperty(this, "_recorder", void 0);
_defineProperty(this, "_audioOptions", void 0);
_defineProperty(this, "prepare", async () => {
this.setState('preparing');
if (_reactNative.Platform.OS === 'ios') {
await this.avModule.Audio.setAudioModeAsync({
allowsRecordingIOS: true,
playsInSilentModeIOS: true
});
}
if (this._recorder._isDoneRecording) {
this._recorder = new this.avModule.Audio.Recording();
}
this._recorder.setProgressUpdateInterval(100);
this._recorder.setOnRecordingStatusUpdate(status => {
const completed = status.durationMillis >= this.options.maxDuration;
if (completed) this.stop();
if (status.isRecording) {
this._recordingSubscribers.forEach(callback => {
callback({
currentTime: status.durationMillis,
completed: completed
});
});
}
});
await this._recorder.prepareToRecordAsync(this._audioOptions);
});
_defineProperty(this, "requestPermission", async () => {
const status = await this.avModule.Audio.getPermissionsAsync();
if ((0, _expoPermissionGranted.default)([status])) {
return true;
} else {
const status = await this.avModule.Audio.requestPermissionsAsync();
return (0, _expoPermissionGranted.default)([status]);
}
});
_defineProperty(this, "record", async () => {
if ((0, _uikitUtils.matchesOneOf)(this.state, ['idle', 'completed'])) {
try {
await this.prepare();
await this._recorder.startAsync();
if (_reactNative.Platform.OS === 'android') {
this._recordStartedAt = Date.now();
}
const uri = this._recorder.getURI();
if (uri) this.uri = uri;
this.setState('recording');
} catch (e) {
this.setState('idle');
throw e;
}
}
});
_defineProperty(this, "stop", async () => {
if ((0, _uikitUtils.matchesOneOf)(this.state, ['recording'])) {
if (_reactNative.Platform.OS === 'android') {
const buffer = this._getRecorderStopSafeBuffer();
if (buffer > 0) await (0, _uikitUtils.sleep)(buffer);
}
await this._recorder.stopAndUnloadAsync();
if (_reactNative.Platform.OS === 'ios') {
await this.avModule.Audio.setAudioModeAsync({
allowsRecordingIOS: false,
playsInSilentModeIOS: false
});
}
this.setState('completed');
}
});
_defineProperty(this, "reset", async () => {
await this.stop();
this.uri = undefined;
this._recordingSubscribers.clear();
this._recorder = new this.avModule.Audio.Recording();
this.setState('idle');
});
this.avModule = avModule;
this._recorder = new avModule.Audio.Recording();
this._audioOptions = {
android: {
...this._audioSettings,
extension: `.${this.options.extension}`,
audioEncoder: avModule.Audio.AndroidAudioEncoder.AAC,
outputFormat: avModule.Audio.AndroidOutputFormat.MPEG_4
},
ios: {
...this._audioSettings,
extension: `.${this.options.extension}`,
outputFormat: avModule.Audio.IOSOutputFormat.MPEG4AAC,
audioQuality: avModule.Audio.IOSAudioQuality.HIGH
},
web: {}
};
}
}
class ExpoAudioRecorderAdapter extends BaseAudioRecorderAdapter {
constructor(audioModule) {
super();
_defineProperty(this, "audioModule", void 0);
_defineProperty(this, "recorder", null);
_defineProperty(this, "recordingUpdateInterval", null);
_defineProperty(this, "setListener", () => {
if (!this.recorder) return;
this.recordingUpdateInterval = setInterval(() => {
if (this.recorder && this.recorder.isRecording) {
const currentTime = this.recorder.currentTime * 1000;
const completed = currentTime >= this.options.maxDuration;
if (completed) {
this.stop().catch(error => {
_uikitUtils.Logger.warn('[RecorderService.Expo] Failed to stop in update interval', error);
});
}
this._recordingSubscribers.forEach(callback => {
callback({
currentTime,
completed
});
});
}
}, 100);
});
_defineProperty(this, "removeListener", () => {
if (this.recordingUpdateInterval) {
clearInterval(this.recordingUpdateInterval);
this.recordingUpdateInterval = null;
}
});
_defineProperty(this, "prepare", async () => {
this.setState('preparing');
if (_reactNative.Platform.OS === 'ios') {
await this.audioModule.setAudioModeAsync({
allowsRecording: true,
playsInSilentMode: true
});
}
const recordingOptions = {
...this._audioSettings,
extension: `.${this.options.extension}`
};
this.recorder = new this.audioModule.AudioModule.AudioRecorder(recordingOptions);
await this.recorder.prepareToRecordAsync();
});
_defineProperty(this, "requestPermission", async () => {
const status = await this.audioModule.getRecordingPermissionsAsync();
if ((0, _expoPermissionGranted.default)([status])) {
return true;
} else {
const status = await this.audioModule.requestRecordingPermissionsAsync();
return (0, _expoPermissionGranted.default)([status]);
}
});
_defineProperty(this, "record", async () => {
if ((0, _uikitUtils.matchesOneOf)(this.state, ['idle', 'completed'])) {
try {
var _this$recorder, _this$recorder2;
await this.prepare();
this.setListener();
(_this$recorder = this.recorder) === null || _this$recorder === void 0 || _this$recorder.record();
if (_reactNative.Platform.OS === 'android') {
this._recordStartedAt = Date.now();
}
const uri = (_this$recorder2 = this.recorder) === null || _this$recorder2 === void 0 ? void 0 : _this$recorder2.uri;
if (uri) this.uri = uri;
this.setState('recording');
} catch (e) {
this.setState('idle');
this.removeListener();
throw e;
}
}
});
_defineProperty(this, "stop", async () => {
if ((0, _uikitUtils.matchesOneOf)(this.state, ['recording'])) {
var _this$recorder3;
if (_reactNative.Platform.OS === 'android') {
const buffer = this._getRecorderStopSafeBuffer();
if (buffer > 0) await (0, _uikitUtils.sleep)(buffer);
}
await ((_this$recorder3 = this.recorder) === null || _this$recorder3 === void 0 ? void 0 : _this$recorder3.stop());
this.removeListener();
if (_reactNative.Platform.OS === 'ios') {
await this.audioModule.setAudioModeAsync({
allowsRecording: false,
playsInSilentMode: false
});
}
this.setState('completed');
}
});
_defineProperty(this, "reset", async () => {
await this.stop();
this.recorder = null;
this.uri = undefined;
this._recordingSubscribers.clear();
this._stateSubscribers.clear();
this.setState('idle');
});
this.audioModule = audioModule;
}
}
const createExpoRecorderService = ({
avModule
}) => {
if (_expoBackwardUtils.default.expoAV.isLegacyAVModule(avModule)) {
_uikitUtils.Logger.warn('[RecorderService.Expo] expo-av is deprecated and will be removed in Expo 54. Please migrate to expo-audio.');
}
return _expoBackwardUtils.default.expoAV.isAudioModule(avModule) ? new ExpoAudioRecorderAdapter(avModule) : new LegacyExpoAVRecorderAdapter(avModule);
};
var _default = exports.default = createExpoRecorderService;
//# sourceMappingURL=createRecorderService.expo.js.map