@cloudflare/realtimekit-react-native 0.1.1-staging.1 → 0.1.2-staging.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.
@@ -620,7 +620,11 @@ RCT_EXPORT_METHOD(isBluetoothHeadsetConnected: (RCTPromiseResolveBlock) resolve
620
620
  [self audioSessionSetMode:audioMode
621
621
  callerMemo:NSStringFromSelector(_cmd)];
622
622
 
623
- [self sendEventWithName:@"onAudioDeviceChanged" body:@{@"devices": audioMode}];
623
+ @try {
624
+ [self sendEventWithName:@"onAudioDeviceChanged" body:@{@"devices": audioMode}];
625
+ } @catch (NSException *exception) {
626
+ NSLog(@"Error sending event 'onAudioDeviceChanged': %@", exception.reason);
627
+ }
624
628
  // NSLog(@"👨‍💻 RNInCallManager.updateAudioRoute() audio mode has changed to %@", audioMode);
625
629
  return true;
626
630
  } else {
@@ -806,7 +810,11 @@ RCT_EXPORT_METHOD(isBluetoothHeadsetConnected: (RCTPromiseResolveBlock) resolve
806
810
  if (state != self->_proximityIsNear) {
807
811
  // NSLog(@"RNInCallManager.UIDeviceProximityStateDidChangeNotification(): isNear: %@", state ? @"YES" : @"NO");
808
812
  self->_proximityIsNear = state;
809
- [self sendEventWithName:@"Proximity" body:@{@"isNear": state ? @YES : @NO}];
813
+ @try {
814
+ [self sendEventWithName:@"Proximity" body:@{@"isNear": state ? @YES : @NO}];
815
+ } @catch (NSException *exception) {
816
+ NSLog(@"Error sending event 'Proximity': %@", exception.reason);
817
+ }
810
818
  }
811
819
  }];
812
820
 
@@ -936,62 +944,90 @@ RCT_EXPORT_METHOD(isBluetoothHeadsetConnected: (RCTPromiseResolveBlock) resolve
936
944
  if ([self checkAudioRoute:@[AVAudioSessionPortHeadsetMic]
937
945
  routeType:@"input"]) {
938
946
  _forceSpeakerOn = 0;
939
- [self sendEventWithName:@"onAudioDeviceChanged"
940
- body:@{
941
- @"availableAudioDeviceList": @"[speaker, earpiece, wired]",
942
- @"selectedAudioDevice": AVAudioSessionPortHeadsetMic,
943
- }];
947
+ @try {
948
+ [self sendEventWithName:@"onAudioDeviceChanged"
949
+ body:@{
950
+ @"availableAudioDeviceList": @"[speaker, earpiece, wired]",
951
+ @"selectedAudioDevice": AVAudioSessionPortHeadsetMic,
952
+ }];
953
+ } @catch (NSException *exception) {
954
+ NSLog(@"Error sending event 'onAudioDeviceChanged' (HeadsetMic): %@", exception.reason);
955
+ }
944
956
  } else if ([self checkAudioRoute:@[AVAudioSessionPortHeadphones]
945
957
  routeType:@"output"]) {
946
958
  _forceSpeakerOn = 0;
947
- [self sendEventWithName:@"onAudioDeviceChanged"
948
- body:@{
949
- @"availableAudioDeviceList": @"[speaker, earpiece, wired]",
950
- @"selectedAudioDevice": AVAudioSessionPortHeadphones,
951
- }];
959
+ @try {
960
+ [self sendEventWithName:@"onAudioDeviceChanged"
961
+ body:@{
962
+ @"availableAudioDeviceList": @"[speaker, earpiece, wired]",
963
+ @"selectedAudioDevice": AVAudioSessionPortHeadphones,
964
+ }];
965
+ } @catch (NSException *exception) {
966
+ NSLog(@"Error sending event 'onAudioDeviceChanged' (Headphones): %@", exception.reason);
967
+ }
952
968
  } else if ([self checkAudioRoute:@[AVAudioSessionPortBluetoothHFP]
953
969
  routeType:@"input"]) {
954
970
  _forceSpeakerOn = 0;
955
- [self sendEventWithName:@"onAudioDeviceChanged"
956
- body:@{
957
- @"availableAudioDeviceList": @"[speaker, earpiece, bluetooth]",
958
- @"selectedAudioDevice": AVAudioSessionPortBluetoothHFP,
959
- }];
971
+ @try {
972
+ [self sendEventWithName:@"onAudioDeviceChanged"
973
+ body:@{
974
+ @"availableAudioDeviceList": @"[speaker, earpiece, bluetooth]",
975
+ @"selectedAudioDevice": AVAudioSessionPortBluetoothHFP,
976
+ }];
977
+ } @catch (NSException *exception) {
978
+ NSLog(@"Error sending event 'onAudioDeviceChanged' (BluetoothHFP): %@", exception.reason);
979
+ }
960
980
  } else if ([self checkAudioRoute:@[AVAudioSessionPortBluetoothA2DP]
961
981
  routeType:@"input"]) {
962
982
  _forceSpeakerOn = 0;
963
- [self sendEventWithName:@"onAudioDeviceChanged"
964
- body:@{
965
- @"availableAudioDeviceList": @"[speaker, earpiece, bluetooth]",
966
- @"selectedAudioDevice": AVAudioSessionPortBluetoothA2DP,
967
- }];
983
+ @try {
984
+ [self sendEventWithName:@"onAudioDeviceChanged"
985
+ body:@{
986
+ @"availableAudioDeviceList": @"[speaker, earpiece, bluetooth]",
987
+ @"selectedAudioDevice": AVAudioSessionPortBluetoothA2DP,
988
+ }];
989
+ } @catch (NSException *exception) {
990
+ NSLog(@"Error sending event 'onAudioDeviceChanged' (BluetoothA2DP): %@", exception.reason);
991
+ }
968
992
  } else if ([self checkAudioRoute:@[AVAudioSessionPortBluetoothLE]
969
993
  routeType:@"input"]) {
970
994
  _forceSpeakerOn = 0;
971
- [self sendEventWithName:@"onAudioDeviceChanged"
972
- body:@{
973
- @"availableAudioDeviceList": @"[speaker, earpiece, bluetooth]",
974
- @"selectedAudioDevice": AVAudioSessionPortBluetoothLE,
975
- }];
995
+ @try {
996
+ [self sendEventWithName:@"onAudioDeviceChanged"
997
+ body:@{
998
+ @"availableAudioDeviceList": @"[speaker, earpiece, bluetooth]",
999
+ @"selectedAudioDevice": AVAudioSessionPortBluetoothLE,
1000
+ }];
1001
+ } @catch (NSException *exception) {
1002
+ NSLog(@"Error sending event 'onAudioDeviceChanged' (BluetoothLE): %@", exception.reason);
1003
+ }
976
1004
  }
977
1005
  break;
978
1006
  case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
979
- [self sendEventWithName:@"onAudioDeviceChanged"
980
- body:@{
1007
+ @try {
1008
+ [self sendEventWithName:@"onAudioDeviceChanged"
1009
+ body:@{
981
1010
  @"availableAudioDeviceList": @"[speaker, earpiece]",
982
1011
  @"selectedAudioDevice": @"speaker",
983
- }];
1012
+ }];
1013
+ } @catch (NSException *exception) {
1014
+ NSLog(@"Error sending event 'onAudioDeviceChanged' (OldDeviceUnavailable): %@", exception.reason);
1015
+ }
984
1016
  break;
985
1017
  case AVAudioSessionRouteChangeReasonCategoryChange:
986
1018
  // NSLog(@"🧑‍💻 RNInCallManager.AudioRouteChange.Reason: CategoryChange. category=%@ mode=%@", self->_audioSession.category, self->_audioSession.mode);
987
1019
  break;
988
1020
  case AVAudioSessionRouteChangeReasonOverride:
989
- // NSLog(@"🧑‍💻 RNInCallManager.AudioRouteChange.Reason: Override");
990
- [self sendEventWithName:@"onAudioDeviceChanged"
991
- body:@{
992
- @"availableAudioDeviceList": @"[speaker, earpiece]",
993
- @"selectedAudioDevice": @"speaker",
994
- }];
1021
+ @try {
1022
+ // NSLog(@"🧑‍💻 RNInCallManager.AudioRouteChange.Reason: Override");
1023
+ [self sendEventWithName:@"onAudioDeviceChanged"
1024
+ body:@{
1025
+ @"availableAudioDeviceList": @"[speaker, earpiece]",
1026
+ @"selectedAudioDevice": @"speaker",
1027
+ }];
1028
+ } @catch (NSException *exception) {
1029
+ NSLog(@"Error sending event 'onAudioDeviceChanged' (Override): %@", exception.reason);
1030
+ }
995
1031
  break;
996
1032
  case AVAudioSessionRouteChangeReasonWakeFromSleep:
997
1033
  // NSLog(@"RNInCallManager.AudioRouteChange.Reason: WakeFromSleep");
@@ -77,11 +77,11 @@ export default class LocalMediaHandler extends EventEmitter {
77
77
  private setVideoTrack;
78
78
  private setScreenShareTracks;
79
79
  private onScreenShareEnded;
80
- disableAudio(): void;
80
+ disableAudio(): Promise<void>;
81
81
  enableAudio(): Promise<void>;
82
+ toggleAudio(): Promise<void>;
82
83
  enableVideo(): Promise<void>;
83
84
  disableVideo(): Promise<void>;
84
- toggleAudio(): Promise<void>;
85
85
  toggleVideo(): Promise<void>;
86
86
  setAudioDevice(device: MediaDeviceInfo): Promise<void>;
87
87
  setVideoDevice(device: MediaDeviceInfo): Promise<void>;
@@ -102,7 +102,14 @@ export default class LocalMediaHandler extends EventEmitter {
102
102
  getDeviceById(deviceId: string, kind: 'audioinput' | 'audiooutput' | 'videoinput'): MediaDeviceInfo;
103
103
  getAudioDevices(): MediaDeviceInfo[];
104
104
  getVideoDevices(): MediaDeviceInfo[];
105
- getSpeakerDevices(): MediaDeviceInfo[];
105
+ getSpeakerDevices(): {
106
+ deviceId: string;
107
+ label: string;
108
+ kind: string;
109
+ groupId: string;
110
+ facing: string;
111
+ toJSON(): any;
112
+ }[];
106
113
  onDeviceChange(changedDevices: {
107
114
  added: MediaDeviceInfo[];
108
115
  removed: MediaDeviceInfo[];
@@ -28,6 +28,7 @@ import InCallManger from './NativeAudioManager';
28
28
  import { setupPermissions } from './PermissionHandler';
29
29
  const { DyteHelper, BroadcastEventEmitter } = NativeModules;
30
30
  const broadcastEmitter = new NativeEventEmitter(BroadcastEventEmitter);
31
+ // eslint-disable-next-line no-shadow
31
32
  export var MediaEvents;
32
33
  (function (MediaEvents) {
33
34
  MediaEvents[MediaEvents["AUDIO_TRACK_CHANGE"] = 0] = "AUDIO_TRACK_CHANGE";
@@ -98,8 +99,7 @@ class LocalMediaHandler extends EventEmitter {
98
99
  video: undefined,
99
100
  speaker: undefined,
100
101
  };
101
- // NOTE(roerohan): The arrow function is required so that the reference to "this" is proper.
102
- //if (Platform.OS === 'android')
102
+ // LocalMediaUtils onDeviceChange with callback from native onAudioDeviceChanged events
103
103
  __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").onDeviceChange((changedDevices) => __awaiter(this, void 0, void 0, function* () {
104
104
  yield this.onDeviceChange(changedDevices, false);
105
105
  }));
@@ -241,7 +241,13 @@ class LocalMediaHandler extends EventEmitter {
241
241
  (_b = this.audioTrack) === null || _b === void 0 ? void 0 : _b.stop();
242
242
  (_c = this.rawAudioTrack) === null || _c === void 0 ? void 0 : _c.stop();
243
243
  }
244
- this.rawAudioTrack = yield LocalMediaUtils.getAudioTrack();
244
+ try {
245
+ this.rawAudioTrack = yield LocalMediaUtils.getAudioTrack();
246
+ }
247
+ catch (err) {
248
+ console.warn('Failed to acquire audio track: ', err);
249
+ this.rawAudioTrack = undefined;
250
+ }
245
251
  this.setAudioTrack(this.rawAudioTrack);
246
252
  this.addMediaStreamTrackListeners(this.audioTrack);
247
253
  this.setDevice(this.currentDevices.audio);
@@ -254,11 +260,6 @@ class LocalMediaHandler extends EventEmitter {
254
260
  if (this.audioTrack && this.permissions.audio === 'NOT_REQUESTED') {
255
261
  yield __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").repopulateAvailableDevices();
256
262
  }
257
- this.permissions.audio = 'ACCEPTED';
258
- this.emit('MEDIA_PERMISSION_UPDATE', {
259
- kind: 'audio',
260
- message: this.permissions.audio,
261
- });
262
263
  this.emit('DEVICE_CHANGE', {
263
264
  device: this.currentDevices.audio,
264
265
  });
@@ -294,7 +295,13 @@ class LocalMediaHandler extends EventEmitter {
294
295
  videoDeviceId,
295
296
  });
296
297
  if (!this.rawVideoTrack) {
297
- this.rawVideoTrack = yield LocalMediaUtils.getVideoTrack(videoConstraints);
298
+ try {
299
+ this.rawVideoTrack = yield LocalMediaUtils.getVideoTrack(videoConstraints);
300
+ }
301
+ catch (err) {
302
+ console.warn('Failed to acquire video track: ', err);
303
+ this.rawVideoTrack = undefined;
304
+ }
298
305
  }
299
306
  this.setVideoTrack(this.rawVideoTrack);
300
307
  this.addMediaStreamTrackListeners(this.rawVideoTrack);
@@ -310,11 +317,6 @@ class LocalMediaHandler extends EventEmitter {
310
317
  if (this.videoTrack && this.permissions.video === 'NOT_REQUESTED') {
311
318
  yield __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").repopulateAvailableDevices();
312
319
  }
313
- this.permissions.video = 'ACCEPTED';
314
- this.emit('MEDIA_PERMISSION_UPDATE', {
315
- kind: 'video',
316
- message: this.permissions.video,
317
- });
318
320
  this.emit('DEVICE_CHANGE', {
319
321
  device: this.currentDevices.video,
320
322
  });
@@ -327,23 +329,17 @@ class LocalMediaHandler extends EventEmitter {
327
329
  if (!speakerDevices.length) {
328
330
  return;
329
331
  }
330
- const speakerDeviceId = deviceId !== null && deviceId !== void 0 ? deviceId : speakerDevices[0].deviceId;
332
+ const lastDeviceIndex = speakerDevices.length - 1;
333
+ const speakerDeviceId = deviceId !== null && deviceId !== void 0 ? deviceId : speakerDevices[lastDeviceIndex].deviceId;
331
334
  if (((_a = this.currentDevices.speaker) === null || _a === void 0 ? void 0 : _a.deviceId) !== speakerDeviceId) {
332
- this.currentDevices.speaker = __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").getDeviceById(speakerDeviceId, 'audiooutput');
335
+ // Audio Input devices are same as output devices in mobile
336
+ this.currentDevices.audio = __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").getDeviceById(speakerDeviceId, 'audioinput');
337
+ this.currentDevices.speaker = this.currentDevices.audio;
333
338
  }
339
+ yield this.setDevice(this.currentDevices.audio);
334
340
  this.emit('DEVICE_CHANGE', {
335
341
  device: this.currentDevices.speaker,
336
342
  });
337
- /*
338
- TODO
339
- const speakerDeviceId = deviceId ?? speakerDevices[0].deviceId;
340
- if (this.currentDevices.speaker?.deviceId !== speakerDeviceId) {
341
- this.currentDevices.speaker = this.#localMediaUtils.getDeviceById(
342
- speakerDeviceId,
343
- 'audiooutput'
344
- );
345
- callStats.selectedDevice('SPEAKER', this.currentDevices.speaker);
346
- } */
347
343
  });
348
344
  }
349
345
  getOperationalWebcamDeviceId() {
@@ -374,10 +370,6 @@ class LocalMediaHandler extends EventEmitter {
374
370
  getOperationalMicDeviceId() {
375
371
  return __awaiter(this, void 0, void 0, function* () {
376
372
  let audioDevices = __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").getAudioDevices();
377
- // console.log(
378
- // 'getOperationalMicDeviceId: ',
379
- // JSON.stringify(audioDevices, null, 2)
380
- // );
381
373
  if (!audioDevices.length) {
382
374
  return { isOperational: false, deviceId: null };
383
375
  }
@@ -387,10 +379,6 @@ class LocalMediaHandler extends EventEmitter {
387
379
  We are giving preference to external device
388
380
  */
389
381
  audioDevices = [...audioDevices].reverse();
390
- // console.log(
391
- // 'getOperationalMicDeviceId Reverse: ',
392
- // JSON.stringify(audioDevices, null, 2)
393
- // );
394
382
  let operationalDeviceId = null;
395
383
  for (let deviceIndex = 0; deviceIndex < audioDevices.length; deviceIndex += 1) {
396
384
  try {
@@ -403,7 +391,9 @@ class LocalMediaHandler extends EventEmitter {
403
391
  break;
404
392
  }
405
393
  }
406
- catch (err) { }
394
+ catch (err) {
395
+ console.warn('Failed to acquire audio track: ', err);
396
+ }
407
397
  }
408
398
  return operationalDeviceId
409
399
  ? { isOperational: true, deviceId: operationalDeviceId }
@@ -412,7 +402,7 @@ class LocalMediaHandler extends EventEmitter {
412
402
  }
413
403
  setupStreams(_a) {
414
404
  return __awaiter(this, arguments, void 0, function* ({ audio, video, }) {
415
- var _b, _c, _d;
405
+ var _b, _c;
416
406
  try {
417
407
  const permResult = yield setupPermissions(audio, video);
418
408
  this.permissions.video = permResult.camera;
@@ -422,6 +412,14 @@ class LocalMediaHandler extends EventEmitter {
422
412
  this.permissions.video = (_b = val.camera) !== null && _b !== void 0 ? _b : false;
423
413
  this.permissions.audio = (_c = val.microphone) !== null && _c !== void 0 ? _c : false;
424
414
  }
415
+ this.emit('MEDIA_PERMISSION_UPDATE', {
416
+ kind: 'audio',
417
+ message: this.permissions.audio,
418
+ });
419
+ this.emit('MEDIA_PERMISSION_UPDATE', {
420
+ kind: 'video',
421
+ message: this.permissions.video,
422
+ });
425
423
  const constraints = {};
426
424
  if (!this.currentDevices) {
427
425
  this.currentDevices = {
@@ -450,37 +448,20 @@ class LocalMediaHandler extends EventEmitter {
450
448
  constraints.video = videoConstraints.video;
451
449
  }
452
450
  }
453
- const { audioTrack, videoTrack } = yield LocalMediaUtils.getAudioAndVideoTrack(constraints);
454
- if (audio && audioTrack) {
455
- this.rawAudioTrack = audioTrack;
456
- this.permissions.audio = 'ACCEPTED';
457
- }
458
- this.emit('MEDIA_PERMISSION_UPDATE', {
459
- kind: 'audio',
460
- message: this.permissions.audio,
461
- });
462
- if (video && videoTrack) {
463
- this.rawVideoTrack = videoTrack;
464
- this.permissions.video = 'ACCEPTED';
465
- }
466
- this.emit('MEDIA_PERMISSION_UPDATE', {
467
- kind: 'video',
468
- message: this.permissions.video,
469
- });
470
- if (((_d = Object.keys(constraints)) === null || _d === void 0 ? void 0 : _d.length) === 0) {
471
- this.audioEnabled = false;
472
- this.videoEnabled = false;
473
- return;
474
- }
475
451
  if (audio)
476
452
  yield this.setupAudioStream();
477
453
  if (video)
478
454
  yield this.setupVideoStream();
479
- if (audioTrack)
455
+ if (this.audioTrack)
480
456
  this.audioEnabled = true;
481
- if (videoTrack)
457
+ else
458
+ this.audioEnabled = false;
459
+ if (this.videoTrack)
482
460
  this.videoEnabled = true;
461
+ else
462
+ this.videoEnabled = false;
483
463
  this.screenShareEnabled = false;
464
+ // Setting default audio output to speaker
484
465
  yield this.setupSpeaker();
485
466
  // Setup Foreground Service after handling permissions
486
467
  if (Platform.OS === 'android') {
@@ -570,12 +551,16 @@ class LocalMediaHandler extends EventEmitter {
570
551
  this.emit('SCREENSHARE_ENDED');
571
552
  }
572
553
  disableAudio() {
573
- this.audioEnabled = false;
574
- if (this.audioTrack) {
575
- this.audioTrack.enabled = this.audioEnabled;
576
- }
554
+ return __awaiter(this, void 0, void 0, function* () {
555
+ yield this.toggleAudio();
556
+ });
577
557
  }
578
558
  enableAudio() {
559
+ return __awaiter(this, void 0, void 0, function* () {
560
+ yield this.toggleAudio();
561
+ });
562
+ }
563
+ toggleAudio() {
579
564
  return __awaiter(this, void 0, void 0, function* () {
580
565
  var _a, _b;
581
566
  if (this.permissions.audio !== 'ACCEPTED') {
@@ -591,8 +576,16 @@ class LocalMediaHandler extends EventEmitter {
591
576
  kind: 'audio',
592
577
  message: this.permissions.audio,
593
578
  });
594
- if (this.permissions.audio === 'DENIED') {
579
+ if (this.permissions.audio === 'DENIED' ||
580
+ this.permissions.audio === 'NOT_REQUESTED') {
581
+ this.audioEnabled = false;
582
+ return;
583
+ }
584
+ if (this.audioEnabled) {
595
585
  this.audioEnabled = false;
586
+ if (this.audioTrack) {
587
+ this.audioTrack.enabled = this.audioEnabled;
588
+ }
596
589
  return;
597
590
  }
598
591
  try {
@@ -631,20 +624,6 @@ class LocalMediaHandler extends EventEmitter {
631
624
  yield this.toggleVideo();
632
625
  });
633
626
  }
634
- toggleAudio() {
635
- return __awaiter(this, void 0, void 0, function* () {
636
- var _a;
637
- if (this.permissions.audio === 'DENIED') {
638
- this.audioEnabled = false;
639
- return;
640
- }
641
- if (!this.audioTrack) {
642
- yield this.setupAudioStream((_a = this.currentDevices.audio) === null || _a === void 0 ? void 0 : _a.deviceId);
643
- }
644
- this.audioEnabled = !this.audioEnabled;
645
- this.audioTrack.enabled = this.audioEnabled;
646
- });
647
- }
648
627
  toggleVideo() {
649
628
  return __awaiter(this, void 0, void 0, function* () {
650
629
  var _a, _b;
@@ -661,7 +640,8 @@ class LocalMediaHandler extends EventEmitter {
661
640
  kind: 'video',
662
641
  message: this.permissions.video,
663
642
  });
664
- if (this.permissions.video === 'DENIED') {
643
+ if (this.permissions.video === 'DENIED' ||
644
+ this.permissions.video === 'NOT_REQUESTED') {
665
645
  this.videoEnabled = false;
666
646
  return;
667
647
  }
@@ -794,12 +774,16 @@ class LocalMediaHandler extends EventEmitter {
794
774
  */
795
775
  if (addedDevice) {
796
776
  if (addedDevice.kind === 'audioinput') {
797
- yield this.setupAudioStream(addedDevice.deviceId);
798
- this.emit('DEVICE_CHANGE', this.currentDevices.audio);
777
+ if (this.permissions.audio === 'DENIED' ||
778
+ this.permissions.audio === 'NOT_REQUESTED') {
779
+ yield this.setupSpeaker(addedDevice.deviceId);
780
+ }
781
+ else {
782
+ yield this.setupAudioStream(addedDevice.deviceId);
783
+ }
799
784
  }
800
785
  else if (addedDevice.kind === 'videoinput') {
801
786
  yield this.setupVideoStream(addedDevice.deviceId);
802
- this.emit('DEVICE_CHANGE', this.currentDevices.video);
803
787
  }
804
788
  else if (addedDevice.kind === 'audiooutput') {
805
789
  yield this.setupSpeaker(addedDevice.deviceId);
@@ -808,7 +792,13 @@ class LocalMediaHandler extends EventEmitter {
808
792
  }
809
793
  if (removedDevice) {
810
794
  if (removedDevice.kind === 'audioinput') {
811
- yield this.setupAudioStream();
795
+ if (this.permissions.audio === 'DENIED' ||
796
+ this.permissions.audio === 'NOT_REQUESTED') {
797
+ yield this.setupSpeaker();
798
+ }
799
+ else {
800
+ yield this.setupAudioStream();
801
+ }
812
802
  }
813
803
  else if (removedDevice.kind === 'videoinput') {
814
804
  yield this.setupVideoStream();
@@ -818,7 +808,9 @@ class LocalMediaHandler extends EventEmitter {
818
808
  }
819
809
  this.emit('AUDIO_TRACK_CHANGE');
820
810
  }
821
- this.emit('DEVICE_LIST_UPDATED', changedDevices);
811
+ if (changedDevices.added.length || changedDevices.removed.length) {
812
+ this.emit('DEVICE_LIST_UPDATED', changedDevices);
813
+ }
822
814
  });
823
815
  }
824
816
  cleanUpTracks() {
@@ -837,7 +829,6 @@ class LocalMediaHandler extends EventEmitter {
837
829
  }
838
830
  static init(_) {
839
831
  return __awaiter(this, void 0, void 0, function* () {
840
- yield setupPermissions(true, true);
841
832
  const localMediaUtils = yield LocalMediaUtils.init();
842
833
  return new LocalMediaHandler(localMediaUtils);
843
834
  });
@@ -68,6 +68,7 @@ export interface RealtimeKitMediaTrackConstraints extends MediaTrackConstraints
68
68
  }
69
69
  export interface RealtimeKitMediaStreamConstraints extends MediaStreamConstraints {
70
70
  audio?: boolean | RealtimeKitMediaTrackConstraints;
71
+ timeout?: number;
71
72
  }
72
73
  export interface VideoConstraints {
73
74
  [key: string]: RealtimeKitMediaTrackConstraints;
@@ -89,5 +89,12 @@ export default class LocalMediaUtils {
89
89
  /**
90
90
  * Returns all audio output devices (speaker devices).
91
91
  */
92
- getSpeakerDevices(): MediaDeviceInfo[];
92
+ getSpeakerDevices(): {
93
+ deviceId: string;
94
+ label: string;
95
+ kind: string;
96
+ groupId: string;
97
+ facing: string;
98
+ toJSON(): any;
99
+ }[];
93
100
  }
@@ -143,7 +143,7 @@ class LocalMediaUtils {
143
143
  return yield mediaDevices.getUserMedia(constraints);
144
144
  }
145
145
  // NOTE(roerohan): The await is required to catch errors (if any).
146
- return yield Promise.race([
146
+ return Promise.race([
147
147
  mediaDevices.getUserMedia(constraints),
148
148
  new Promise((_resolve, reject) => {
149
149
  setTimeout(() => reject(new LocalMediaError('Get user media timed out', 'ERR_GUM_TIMEOUT')), timeout);
@@ -159,13 +159,14 @@ class LocalMediaUtils {
159
159
  return __awaiter(this, void 0, void 0, function* () {
160
160
  const mediaStream = (yield LocalMediaUtils.getUserMediaWithTimeout({
161
161
  audio: true,
162
+ timeout: 5000,
162
163
  }));
163
164
  return mediaStream.getAudioTracks()[0];
164
165
  });
165
166
  }
166
167
  static getVideoTrack(constraints) {
167
168
  return __awaiter(this, void 0, void 0, function* () {
168
- const mediaStream = (yield LocalMediaUtils.getUserMediaWithTimeout(constraints));
169
+ const mediaStream = (yield LocalMediaUtils.getUserMediaWithTimeout(Object.assign(Object.assign({}, constraints), { timeout: 5000 })));
169
170
  return mediaStream.getVideoTracks()[0];
170
171
  });
171
172
  }
@@ -186,11 +187,16 @@ class LocalMediaUtils {
186
187
  }
187
188
  static getAudioAndVideoTrack(constraints) {
188
189
  return __awaiter(this, void 0, void 0, function* () {
189
- const mediaStream = (yield LocalMediaUtils.getUserMediaWithTimeout(constraints));
190
- return {
191
- audioTrack: mediaStream.getAudioTracks()[0],
192
- videoTrack: mediaStream.getVideoTracks()[0],
193
- };
190
+ try {
191
+ const mediaStream = (yield LocalMediaUtils.getUserMediaWithTimeout(Object.assign(Object.assign({}, constraints), { timeout: 5000 })));
192
+ return {
193
+ audioTrack: mediaStream.getAudioTracks()[0],
194
+ videoTrack: mediaStream.getVideoTracks()[0],
195
+ };
196
+ }
197
+ catch (err) {
198
+ throw err;
199
+ }
194
200
  });
195
201
  }
196
202
  onDeviceChange(callback) {
@@ -212,7 +218,9 @@ class LocalMediaUtils {
212
218
  removed: previouslyAvailableDevices.filter((device) => !currentlyAvailableDeviceIds.includes(device.deviceId)),
213
219
  };
214
220
  this.repopulateAvailableDevices().then(() => {
215
- callback(changedDevices, false);
221
+ if (changedDevices.added.length || changedDevices.removed.length) {
222
+ callback(changedDevices, false);
223
+ }
216
224
  });
217
225
  }
218
226
  else {
@@ -224,7 +232,9 @@ class LocalMediaUtils {
224
232
  removed: previouslyAvailableDevices.filter((device) => !currentlyAvailableDeviceIds.includes(device.deviceId)),
225
233
  };
226
234
  this.repopulateAvailableDevices().then(() => {
227
- callback(changedDevices, false);
235
+ if (changedDevices.added.length || changedDevices.removed.length) {
236
+ callback(changedDevices, false);
237
+ }
228
238
  });
229
239
  }
230
240
  });
@@ -269,7 +279,11 @@ class LocalMediaUtils {
269
279
  * Returns all audio output devices (speaker devices).
270
280
  */
271
281
  getSpeakerDevices() {
272
- return this.getAvailableDevicesByKind('audiooutput');
282
+ const speakerDevices = __classPrivateFieldGet(this, _LocalMediaUtils_availableDevices, "f");
283
+ // In Mobile, speaker devices are the same as audio input devices
284
+ return speakerDevices
285
+ .filter((device) => device.kind === 'audioinput')
286
+ .map((device) => (Object.assign(Object.assign({}, device), { deviceId: device.deviceId, label: device.label, kind: 'audiooutput' })));
273
287
  }
274
288
  }
275
289
  _LocalMediaUtils_availableDevices = new WeakMap(), _LocalMediaUtils_nativeEventEmitter = new WeakMap();
@@ -22,16 +22,11 @@ export declare const RealtimeKitContext: React.Context<{
22
22
  * @param renderBeforeLoad The provider will not render children by default
23
23
  * until it gets a client object, renderBeforeLoad can be used to override that
24
24
  */
25
- export declare function RealtimeKitProvider({ value, children, renderBeforeLoad, }: {
25
+ export declare function RealtimeKitProvider({ value, children, fallback, }: {
26
26
  value: RealtimeKitClient | undefined;
27
27
  children: ReactChild | ReactChild[];
28
- renderBeforeLoad?: boolean;
28
+ fallback?: ReactChild | ReactChild[];
29
29
  }): JSX.Element;
30
- export declare namespace RealtimeKitProvider {
31
- var defaultProps: {
32
- renderBeforeLoad: boolean;
33
- };
34
- }
35
30
  /**
36
31
  * Hook which returns the reference to the DyteClient object
37
32
  * @returns meeting instance from `useDyteClient()`
@@ -59,31 +59,25 @@ export const RealtimeKitContext = createContext({
59
59
  * @param renderBeforeLoad The provider will not render children by default
60
60
  * until it gets a client object, renderBeforeLoad can be used to override that
61
61
  */
62
- export function RealtimeKitProvider({ value, children,
63
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
64
- renderBeforeLoad, }) {
62
+ export function RealtimeKitProvider({ value, children, fallback = null, }) {
65
63
  const [updates, setUpdates] = useState();
66
64
  const updatesRef = useRef();
67
65
  useEffect(() => {
68
66
  if (value) {
69
- setUpdates(new RealtimeKitUpdates(value));
67
+ const newUpdate = new RealtimeKitUpdates(value);
68
+ setUpdates(newUpdate);
69
+ updatesRef.current = newUpdate;
70
70
  }
71
71
  return () => {
72
72
  var _a;
73
- // eslint-disable-next-line react-hooks/exhaustive-deps
74
73
  (_a = updatesRef.current) === null || _a === void 0 ? void 0 : _a.clean();
75
74
  };
76
75
  }, [value]);
77
- if (!value)
78
- return null;
79
76
  return (React.createElement(RealtimeKitContext.Provider, { value: {
80
77
  meeting: value,
81
78
  updates,
82
- } }, children));
79
+ } }, value ? children : fallback));
83
80
  }
84
- RealtimeKitProvider.defaultProps = {
85
- renderBeforeLoad: false,
86
- };
87
81
  /**
88
82
  * Hook which returns the reference to the DyteClient object
89
83
  * @returns meeting instance from `useDyteClient()`
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@cloudflare/realtimekit-react-native",
3
- "version": "0.1.1-staging.1",
3
+ "version": "0.1.2-staging.1",
4
4
  "description": "Cloudflare RealtimeKit SDK for react native",
5
5
  "main": "lib/index.js",
6
- "author": "Dyte <dev@dyte.io> (https://github.com/dyte-in/)",
7
- "homepage": "https://github.com/dyte-in/react-native-core#readme",
6
+ "author": "Cloudflare",
7
+ "homepage": "https://realtime.cloudflare.com",
8
8
  "types": "lib/index.d.ts",
9
9
  "files": [
10
10
  "lib",
@@ -21,7 +21,7 @@
21
21
  "!**/__mocks__"
22
22
  ],
23
23
  "bugs": {
24
- "url": "https://github.com/dyte-in/react-native-core/issues"
24
+ "url": "https://realtime.cloudflare.com/issues"
25
25
  },
26
26
  "private": false,
27
27
  "dependencies": {