@snapcall/stream-ui 1.28.0 → 1.29.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.
@@ -503,19 +503,17 @@ const $67e45b2e30dcc030$var$audioContextConstructor = window.AudioContext || win
503
503
  const $67e45b2e30dcc030$var$AUDIO_MUTE_DETECTION_TIME = 2000;
504
504
  const $67e45b2e30dcc030$var$START_SPEAK_DETECTION_TIME = 50;
505
505
  const $67e45b2e30dcc030$var$STOP_SPEAK_DETECTION_TIME = 500;
506
+ const $67e45b2e30dcc030$var$AUDIO_DATA_RATE = 60;
506
507
  class $67e45b2e30dcc030$export$ea669869acd8f177 {
507
508
  constructor(stream, listener){
508
509
  this.speakStartEventTime = 0;
509
510
  this.speakStopEventTime = 0;
510
511
  this.isSpeaking = false;
511
- this.audioMedianInterval = -1;
512
- this.audioLevelInterval = -1;
513
- this.audioLevelAverageInterval = -1;
512
+ this.audioIntervals = {};
514
513
  this.audioLevelAverage = {
515
514
  count: 0,
516
515
  average: 0
517
516
  };
518
- this.audioLevels = [];
519
517
  this.id = stream.id;
520
518
  this.listener = listener;
521
519
  this.stream = stream;
@@ -532,15 +530,16 @@ class $67e45b2e30dcc030$export$ea669869acd8f177 {
532
530
  this.analyser.minDecibels = -90;
533
531
  this.analyser.maxDecibels = -10;
534
532
  this.audioStreamSource.connect(this.analyser);
533
+ this.audioLevels = new Array(Math.round(1000 / $67e45b2e30dcc030$var$AUDIO_DATA_RATE)).fill(0);
535
534
  }
536
535
  /**
537
- * return the median of the audio levels (60 per second) for the
536
+ * return the average (mean) of the audio levels (AUDIO_DATA_RATE per second) for the
538
537
  * current second
539
- */ getAudioLevelMedian() {
538
+ */ getRecentAudioLevelAverage() {
540
539
  const { length: length } = this.audioLevels;
541
540
  const audioLevelsSum = this.audioLevels.reduce((sum, level)=>sum + level, 0);
542
- const audioLevelMedian = audioLevelsSum / length;
543
- return audioLevelMedian;
541
+ const audioLevelAverage = audioLevelsSum / length;
542
+ return audioLevelAverage;
544
543
  }
545
544
  getCurrentAudioLevel() {
546
545
  const { length: length } = this.audioLevels;
@@ -548,8 +547,8 @@ class $67e45b2e30dcc030$export$ea669869acd8f177 {
548
547
  return 0;
549
548
  }
550
549
  audioMuteDetection() {
551
- const audioLevelMedian = this.getAudioLevelMedian();
552
- if (audioLevelMedian === 0 && this.audioLevels.length > 0) this.listener?.onMuteDetection?.(this.id);
550
+ const audioLevelAverage = this.getRecentAudioLevelAverage();
551
+ if (audioLevelAverage === 0 && this.audioLevels.length > 0) this.listener?.onMuteDetection?.(this.id);
553
552
  }
554
553
  detectSpeakingChange(audioLevel) {
555
554
  const threshold = 5;
@@ -588,24 +587,24 @@ class $67e45b2e30dcc030$export$ea669869acd8f177 {
588
587
  }
589
588
  loopCalcul() {
590
589
  const audioLevel = this.getInstantAudioLevel();
591
- this.audioLevels = this.audioLevels.splice(-59);
592
- this.detectSpeakingChange(audioLevel);
590
+ this.audioLevels.shift();
593
591
  this.audioLevels.push(audioLevel);
592
+ this.detectSpeakingChange(audioLevel);
594
593
  this.listener?.onAudioLevel?.(audioLevel, this.id);
595
594
  }
596
595
  analyse() {
597
- this.audioLevelInterval = window.setInterval(()=>this.loopCalcul(), 1000 / 60);
598
- this.audioMedianInterval = window.setInterval(()=>this.audioMuteDetection(), $67e45b2e30dcc030$var$AUDIO_MUTE_DETECTION_TIME);
596
+ this.audioIntervals.level = setInterval(()=>this.loopCalcul(), 1000 / $67e45b2e30dcc030$var$AUDIO_DATA_RATE);
597
+ this.audioIntervals.muteDetection = setInterval(()=>this.audioMuteDetection(), $67e45b2e30dcc030$var$AUDIO_MUTE_DETECTION_TIME);
599
598
  }
600
599
  startAverageAnalysis() {
601
600
  this.audioLevelAverage = {
602
601
  count: 0,
603
602
  average: 0
604
603
  };
605
- clearInterval(this.audioLevelAverageInterval);
606
- this.audioLevelAverageInterval = window.setInterval(()=>{
604
+ clearInterval(this.audioIntervals.levelAverage);
605
+ this.audioIntervals.levelAverage = setInterval(()=>{
607
606
  const { count: count, average: average } = this.audioLevelAverage;
608
- const nextAverage = (average * count + this.getAudioLevelMedian()) / (count + 1);
607
+ const nextAverage = (average * count + this.getRecentAudioLevelAverage()) / (count + 1);
609
608
  this.audioLevelAverage = {
610
609
  count: count + 1,
611
610
  average: nextAverage
@@ -613,7 +612,8 @@ class $67e45b2e30dcc030$export$ea669869acd8f177 {
613
612
  }, 1000);
614
613
  }
615
614
  stopAverageAnalysis() {
616
- clearInterval(this.audioLevelAverageInterval);
615
+ clearInterval(this.audioIntervals.levelAverage);
616
+ delete this.audioIntervals.levelAverage;
617
617
  return this.audioLevelAverage.average;
618
618
  }
619
619
  getFrequencyData() {
@@ -621,14 +621,18 @@ class $67e45b2e30dcc030$export$ea669869acd8f177 {
621
621
  this.analyser.getByteFrequencyData(FFTDataArray);
622
622
  return FFTDataArray;
623
623
  }
624
- clearListeners() {
625
- clearInterval(this.audioLevelInterval);
626
- clearInterval(this.audioMedianInterval);
627
- this.audioLevels = [];
624
+ clearIntervals() {
625
+ clearInterval(this.audioIntervals.level);
626
+ clearInterval(this.audioIntervals.levelAverage);
627
+ clearInterval(this.audioIntervals.muteDetection);
628
+ delete this.audioIntervals.level;
629
+ delete this.audioIntervals.levelAverage;
630
+ delete this.audioIntervals.muteDetection;
631
+ this.audioLevels.fill(0);
628
632
  }
629
633
  release() {
630
634
  this.listener = undefined;
631
- this.clearListeners();
635
+ this.clearIntervals();
632
636
  this.stream = undefined;
633
637
  try {
634
638
  this.audioStreamSource.disconnect(this.analyser);
@@ -982,8 +986,14 @@ class $ab40fd7a219a4259$export$2e2bcd8739ae039 {
982
986
  let needToAskVideo = Boolean(constraints.video && (!this.video.stream || !this.video.track || this.video.track.readyState === 'ended'));
983
987
  const audioDeviceId = $ab40fd7a219a4259$export$fceddabcb6d8e584(constraints.audio);
984
988
  const videoDeviceId = $ab40fd7a219a4259$export$fceddabcb6d8e584(constraints.video);
985
- if (audioDeviceId && audioDeviceId !== this.audio.track?.getSettings().deviceId) needToAskAudio = true;
986
- if (videoDeviceId && videoDeviceId !== this.video.track?.getSettings().deviceId) needToAskVideo = true;
989
+ const oldAudioDeviceId = this.audio.track?.getSettings().deviceId;
990
+ const oldVideoDeviceId = this.video.track?.getSettings().deviceId;
991
+ /*
992
+ If deviceId is 'default', we cant differentiate if it's the same device or not. https://issues.chromium.org/issues/40199570#comment23
993
+ A possible solution would be to compare the label of the track. There is no guarantee that it will be unique or match the deviceId.
994
+ ex track label: Default - Microphone (USB Audio Device) (046d:0992)
995
+ */ if (audioDeviceId && (audioDeviceId === 'default' || audioDeviceId !== oldAudioDeviceId)) needToAskAudio = true;
996
+ if (videoDeviceId && (videoDeviceId === 'default' || videoDeviceId !== oldVideoDeviceId)) needToAskVideo = true;
987
997
  if (typeof constraints.video === 'object' && constraints.video.facingMode && constraints.video.facingMode !== this.video.track?.getSettings().facingMode) needToAskVideo = true;
988
998
  return {
989
999
  needToAskAudio: needToAskAudio,
@@ -1880,7 +1890,7 @@ class $c31e3fb4360572af$export$2e2bcd8739ae039 extends $c31e3fb4360572af$var$Str
1880
1890
  }
1881
1891
  async getTransport(kind, direction) {
1882
1892
  if (!this.streamerMediasoup || !this.protoo) throw new Error('streamerMediasoup not initialized');
1883
- let transport = this.mediasoupTransport[`${kind}-${direction}`];
1893
+ let transport = this.mediasoupTransport[direction];
1884
1894
  if (!transport) {
1885
1895
  try {
1886
1896
  const promise = this.streamerMediasoup.createTransport(this.protoo, direction);
@@ -1889,7 +1899,7 @@ class $c31e3fb4360572af$export$2e2bcd8739ae039 extends $c31e3fb4360572af$var$Str
1889
1899
  kind: kind,
1890
1900
  createPromise: promise
1891
1901
  };
1892
- this.mediasoupTransport[`${kind}-${direction}`] = transport;
1902
+ this.mediasoupTransport[direction] = transport;
1893
1903
  transport.transport = await promise;
1894
1904
  if (transport.transport) this.listenWebRTCTransportStates(transport);
1895
1905
  } catch (err) {
@@ -2072,6 +2082,7 @@ class $c31e3fb4360572af$export$2e2bcd8739ae039 extends $c31e3fb4360572af$var$Str
2072
2082
  await this.micProducer?.replaceTrack({
2073
2083
  track: track
2074
2084
  });
2085
+ if (this.micProducer?.paused) track.enabled = false;
2075
2086
  $c31e3fb4360572af$var$log.log('switchMicrophone', 'switched Microphone');
2076
2087
  const trackSettings = track.getSettings();
2077
2088
  const device = this.devicesList.find((it)=>it.deviceId === trackSettings.deviceId);
@@ -2497,7 +2508,6 @@ class $c31e3fb4360572af$export$2e2bcd8739ae039 extends $c31e3fb4360572af$var$Str
2497
2508
  this.webcam.facingMode = config.facingMode;
2498
2509
  } else {
2499
2510
  if (config?.device) this.webcam.device = config.device;
2500
- $c31e3fb4360572af$var$log.warn('enableVideo', 'default deviceId', this.webcam.device?.deviceId);
2501
2511
  optionalParam.deviceId = {
2502
2512
  ideal: this.webcam.device?.deviceId
2503
2513
  };
@@ -2622,6 +2632,7 @@ class $c31e3fb4360572af$export$2e2bcd8739ae039 extends $c31e3fb4360572af$var$Str
2622
2632
  audio: false,
2623
2633
  video: true
2624
2634
  });
2635
+ this.webcam.device = null;
2625
2636
  this.webcamProducer = null;
2626
2637
  this.webcamTrack = null;
2627
2638
  if (this.useVideoBackground) {
@@ -3980,6 +3991,7 @@ class $e96d119a19ed0c6c$export$45fabd1ce5e673de {
3980
3991
  this.video.addEventListener('enterpictureinpicture', this.onEnterPictureInPicture.bind(this));
3981
3992
  this.video.addEventListener('leavepictureinpicture', this.onLeavePictureInPicture.bind(this));
3982
3993
  this.tiles = [];
3994
+ this.missingVideoWarnings = {};
3983
3995
  }
3984
3996
  async start() {
3985
3997
  if (this.tiles.length) {
@@ -4010,6 +4022,12 @@ class $e96d119a19ed0c6c$export$45fabd1ce5e673de {
4010
4022
  this.started = false;
4011
4023
  this.video.srcObject = null;
4012
4024
  }
4025
+ warnMissingVideo(tileId) {
4026
+ if (!this.missingVideoWarnings[tileId]) {
4027
+ this.missingVideoWarnings[tileId] = '1';
4028
+ console.warn(`MultiPiP: No video found for peer ${tileId}`);
4029
+ }
4030
+ }
4013
4031
  draw() {
4014
4032
  if (this.ctx && this.started) {
4015
4033
  this.ctx.fillStyle = '#1f1f1f';
@@ -4017,7 +4035,10 @@ class $e96d119a19ed0c6c$export$45fabd1ce5e673de {
4017
4035
  if (this.tiles.length) {
4018
4036
  this.tiles.forEach((tile, index)=>{
4019
4037
  const tileVideo = document.body.querySelectorAll(`[data-peer-id='${tile.id}'] video`)[0];
4020
- if (!tileVideo) return console.warn(`MultiPiP: No video found for peer ${tile.id}`);
4038
+ if (!tileVideo) {
4039
+ this.warnMissingVideo(tile.id);
4040
+ return;
4041
+ }
4021
4042
  const tileRect = this.layout?.tileRects[index];
4022
4043
  if (!tileRect) return;
4023
4044
  this.ctx?.save();
@@ -6352,6 +6373,12 @@ const $580bffc02c115932$export$12201ede76c4c5e1 = ()=>{
6352
6373
  const base64Image = await (0, $c9e496369b59be7a$export$2f377c2162fd02b2).captureVideo(videoElementRef.current);
6353
6374
  try {
6354
6375
  const uploaded = await (0, $c9e496369b59be7a$export$2f377c2162fd02b2).saveCapture(base64Image);
6376
+ await (0, $c9e496369b59be7a$export$2f377c2162fd02b2).createEvents([
6377
+ {
6378
+ filename: uploaded.filename,
6379
+ type: 'image'
6380
+ }
6381
+ ]);
6355
6382
  await (0, $c9e496369b59be7a$export$2f377c2162fd02b2).sendCustomMessageToAll({
6356
6383
  type: 'snapshot',
6357
6384
  imageSrc: uploaded.url
@@ -7125,6 +7152,7 @@ const $0707965973625e01$export$f36e83cafa401ab5 = ({ stream: stream, width: widt
7125
7152
  onSnapshot: ()=>snapshot.trigger({
7126
7153
  videoElementRef: videoElementRef
7127
7154
  }),
7155
+ "data-peer-id": stream.id,
7128
7156
  children: [
7129
7157
  /*#__PURE__*/ (0, $3Sbms$jsx)((0, $7ec04c1818c68245$export$d574f5511ec1d79a), {
7130
7158
  ref: videoElementRef,
@@ -12113,14 +12141,14 @@ const $64dee502cbd6331e$export$1ea93f9eface5983 = ({ duration: duration = 0, ave
12113
12141
  duotone: true
12114
12142
  })
12115
12143
  });
12116
- if (averageAudioLevel <= 5) return /*#__PURE__*/ (0, $3Sbms$jsx)($64dee502cbd6331e$var$Notification, {
12144
+ if (averageAudioLevel <= 1) return /*#__PURE__*/ (0, $3Sbms$jsx)($64dee502cbd6331e$var$Notification, {
12117
12145
  title: t('recorder.audioLevelNotification.spokenWordsWarning.title'),
12118
12146
  description: t('recorder.audioLevelNotification.spokenWordsWarning.description'),
12119
12147
  icon: /*#__PURE__*/ (0, $3Sbms$jsx)((0, $3Sbms$AlertTriangleIcon), {
12120
12148
  duotone: true
12121
12149
  })
12122
12150
  });
12123
- if (averageAudioLevel <= 25) return /*#__PURE__*/ (0, $3Sbms$jsx)($64dee502cbd6331e$var$Notification, {
12151
+ if (averageAudioLevel <= 3) return /*#__PURE__*/ (0, $3Sbms$jsx)($64dee502cbd6331e$var$Notification, {
12124
12152
  title: t('recorder.audioLevelNotification.spokenWordsQuestion.title'),
12125
12153
  description: t('recorder.audioLevelNotification.spokenWordsQuestion.description'),
12126
12154
  icon: /*#__PURE__*/ (0, $3Sbms$jsx)((0, $3Sbms$InfoCircleIcon), {
@@ -12261,7 +12289,7 @@ const $a5dd8f67439dd9eb$var$getStoredAssets = async ()=>{
12261
12289
  const storedAssets = JSON.parse(localStorage.getItem('snapcall_assets') || '[]');
12262
12290
  const assets = await Promise.all(storedAssets.map(async (storedAsset)=>{
12263
12291
  try {
12264
- let { url: url, thumbnailUrl: thumbnailUrl } = await (0, $c9e496369b59be7a$export$2f377c2162fd02b2).readAsset(storedAsset.filename);
12292
+ let { url: url, thumbnailUrl: thumbnailUrl, error: error } = await (0, $c9e496369b59be7a$export$2f377c2162fd02b2).readAsset(storedAsset.filename);
12265
12293
  if (url) {
12266
12294
  if (storedAsset.mode === 'photo') thumbnailUrl = url;
12267
12295
  return {
@@ -12269,7 +12297,7 @@ const $a5dd8f67439dd9eb$var$getStoredAssets = async ()=>{
12269
12297
  url: url,
12270
12298
  thumbnailUrl: thumbnailUrl
12271
12299
  };
12272
- }
12300
+ } else if (error) $a5dd8f67439dd9eb$export$df987b50509121ea(storedAsset.filename);
12273
12301
  } catch (error) {
12274
12302
  $a5dd8f67439dd9eb$export$df987b50509121ea(storedAsset.filename);
12275
12303
  }
@@ -12315,6 +12343,31 @@ const $a5dd8f67439dd9eb$export$931d641a2a152cf = ()=>{
12315
12343
 
12316
12344
 
12317
12345
 
12346
+
12347
+
12348
+ const $7c2ec1087f88cbe4$export$f2bc44ad94513462 = ()=>{
12349
+ const { clientInitResult: clientInitResult } = (0, $3Sbms$useContext)((0, $5f30d8bf4f04621e$export$2e2bcd8739ae039));
12350
+ const flowSource = clientInitResult.flow?.steps?.[0].config?.source;
12351
+ const onRecorderEnd = ()=>{
12352
+ if (flowSource === 'smooch') // @ts-ignore
12353
+ window.WebviewSdk?.close?.();
12354
+ else if (flowSource === 'whatsapp') window.location.href = 'https://wa.me';
12355
+ };
12356
+ (0, $3Sbms$useEffect)(()=>{
12357
+ if (flowSource === 'smooch') {
12358
+ const smoochScript = document.createElement('script');
12359
+ smoochScript.src = 'https://cdn.smooch.io/webview-sdk.min.js';
12360
+ if (!document.body.contains(smoochScript)) document.body.appendChild(smoochScript);
12361
+ }
12362
+ }, [
12363
+ flowSource
12364
+ ]);
12365
+ return {
12366
+ onRecorderEnd: onRecorderEnd
12367
+ };
12368
+ };
12369
+
12370
+
12318
12371
  const $3ecacdd28d707ec1$var$eventTypes = {
12319
12372
  screen: 'screenshare',
12320
12373
  photo: 'image',
@@ -12324,6 +12377,7 @@ const $3ecacdd28d707ec1$var$eventTypes = {
12324
12377
  const $3ecacdd28d707ec1$export$c01bb29adf88f117 = ({ state: state })=>{
12325
12378
  const { t: t } = (0, $3Sbms$useTranslation)();
12326
12379
  const { assets: assets, incomingAsset: incomingAsset } = (0, $75812785bb101fee$export$2174f25d572f9f31)();
12380
+ const flowSourceAction = (0, $7c2ec1087f88cbe4$export$f2bc44ad94513462)();
12327
12381
  const sendMutation = (0, $3Sbms$useMutation)({
12328
12382
  mutationFn: async ()=>{
12329
12383
  try {
@@ -12348,7 +12402,6 @@ const $3ecacdd28d707ec1$export$c01bb29adf88f117 = ({ state: state })=>{
12348
12402
  });
12349
12403
  throw new Error('some media not saved');
12350
12404
  }
12351
- (0, $c9e496369b59be7a$export$2f377c2162fd02b2).release();
12352
12405
  return results;
12353
12406
  } catch (error) {
12354
12407
  reportError({
@@ -12358,6 +12411,10 @@ const $3ecacdd28d707ec1$export$c01bb29adf88f117 = ({ state: state })=>{
12358
12411
  console.log(error);
12359
12412
  throw error;
12360
12413
  }
12414
+ },
12415
+ onSuccess: ()=>{
12416
+ (0, $c9e496369b59be7a$export$2f377c2162fd02b2).release();
12417
+ flowSourceAction.onRecorderEnd();
12361
12418
  }
12362
12419
  });
12363
12420
  const reportError = (0, $946223bbb2c552ef$export$5a5695b638d078e7)();
package/dist/stream-ui.js CHANGED
@@ -509,19 +509,17 @@ const $6a90fae7e584afd4$var$audioContextConstructor = window.AudioContext || win
509
509
  const $6a90fae7e584afd4$var$AUDIO_MUTE_DETECTION_TIME = 2000;
510
510
  const $6a90fae7e584afd4$var$START_SPEAK_DETECTION_TIME = 50;
511
511
  const $6a90fae7e584afd4$var$STOP_SPEAK_DETECTION_TIME = 500;
512
+ const $6a90fae7e584afd4$var$AUDIO_DATA_RATE = 60;
512
513
  class $6a90fae7e584afd4$export$ea669869acd8f177 {
513
514
  constructor(stream, listener){
514
515
  this.speakStartEventTime = 0;
515
516
  this.speakStopEventTime = 0;
516
517
  this.isSpeaking = false;
517
- this.audioMedianInterval = -1;
518
- this.audioLevelInterval = -1;
519
- this.audioLevelAverageInterval = -1;
518
+ this.audioIntervals = {};
520
519
  this.audioLevelAverage = {
521
520
  count: 0,
522
521
  average: 0
523
522
  };
524
- this.audioLevels = [];
525
523
  this.id = stream.id;
526
524
  this.listener = listener;
527
525
  this.stream = stream;
@@ -538,15 +536,16 @@ class $6a90fae7e584afd4$export$ea669869acd8f177 {
538
536
  this.analyser.minDecibels = -90;
539
537
  this.analyser.maxDecibels = -10;
540
538
  this.audioStreamSource.connect(this.analyser);
539
+ this.audioLevels = new Array(Math.round(1000 / $6a90fae7e584afd4$var$AUDIO_DATA_RATE)).fill(0);
541
540
  }
542
541
  /**
543
- * return the median of the audio levels (60 per second) for the
542
+ * return the average (mean) of the audio levels (AUDIO_DATA_RATE per second) for the
544
543
  * current second
545
- */ getAudioLevelMedian() {
544
+ */ getRecentAudioLevelAverage() {
546
545
  const { length: length } = this.audioLevels;
547
546
  const audioLevelsSum = this.audioLevels.reduce((sum, level)=>sum + level, 0);
548
- const audioLevelMedian = audioLevelsSum / length;
549
- return audioLevelMedian;
547
+ const audioLevelAverage = audioLevelsSum / length;
548
+ return audioLevelAverage;
550
549
  }
551
550
  getCurrentAudioLevel() {
552
551
  const { length: length } = this.audioLevels;
@@ -554,8 +553,8 @@ class $6a90fae7e584afd4$export$ea669869acd8f177 {
554
553
  return 0;
555
554
  }
556
555
  audioMuteDetection() {
557
- const audioLevelMedian = this.getAudioLevelMedian();
558
- if (audioLevelMedian === 0 && this.audioLevels.length > 0) this.listener?.onMuteDetection?.(this.id);
556
+ const audioLevelAverage = this.getRecentAudioLevelAverage();
557
+ if (audioLevelAverage === 0 && this.audioLevels.length > 0) this.listener?.onMuteDetection?.(this.id);
559
558
  }
560
559
  detectSpeakingChange(audioLevel) {
561
560
  const threshold = 5;
@@ -594,24 +593,24 @@ class $6a90fae7e584afd4$export$ea669869acd8f177 {
594
593
  }
595
594
  loopCalcul() {
596
595
  const audioLevel = this.getInstantAudioLevel();
597
- this.audioLevels = this.audioLevels.splice(-59);
598
- this.detectSpeakingChange(audioLevel);
596
+ this.audioLevels.shift();
599
597
  this.audioLevels.push(audioLevel);
598
+ this.detectSpeakingChange(audioLevel);
600
599
  this.listener?.onAudioLevel?.(audioLevel, this.id);
601
600
  }
602
601
  analyse() {
603
- this.audioLevelInterval = window.setInterval(()=>this.loopCalcul(), 1000 / 60);
604
- this.audioMedianInterval = window.setInterval(()=>this.audioMuteDetection(), $6a90fae7e584afd4$var$AUDIO_MUTE_DETECTION_TIME);
602
+ this.audioIntervals.level = setInterval(()=>this.loopCalcul(), 1000 / $6a90fae7e584afd4$var$AUDIO_DATA_RATE);
603
+ this.audioIntervals.muteDetection = setInterval(()=>this.audioMuteDetection(), $6a90fae7e584afd4$var$AUDIO_MUTE_DETECTION_TIME);
605
604
  }
606
605
  startAverageAnalysis() {
607
606
  this.audioLevelAverage = {
608
607
  count: 0,
609
608
  average: 0
610
609
  };
611
- clearInterval(this.audioLevelAverageInterval);
612
- this.audioLevelAverageInterval = window.setInterval(()=>{
610
+ clearInterval(this.audioIntervals.levelAverage);
611
+ this.audioIntervals.levelAverage = setInterval(()=>{
613
612
  const { count: count, average: average } = this.audioLevelAverage;
614
- const nextAverage = (average * count + this.getAudioLevelMedian()) / (count + 1);
613
+ const nextAverage = (average * count + this.getRecentAudioLevelAverage()) / (count + 1);
615
614
  this.audioLevelAverage = {
616
615
  count: count + 1,
617
616
  average: nextAverage
@@ -619,7 +618,8 @@ class $6a90fae7e584afd4$export$ea669869acd8f177 {
619
618
  }, 1000);
620
619
  }
621
620
  stopAverageAnalysis() {
622
- clearInterval(this.audioLevelAverageInterval);
621
+ clearInterval(this.audioIntervals.levelAverage);
622
+ delete this.audioIntervals.levelAverage;
623
623
  return this.audioLevelAverage.average;
624
624
  }
625
625
  getFrequencyData() {
@@ -627,14 +627,18 @@ class $6a90fae7e584afd4$export$ea669869acd8f177 {
627
627
  this.analyser.getByteFrequencyData(FFTDataArray);
628
628
  return FFTDataArray;
629
629
  }
630
- clearListeners() {
631
- clearInterval(this.audioLevelInterval);
632
- clearInterval(this.audioMedianInterval);
633
- this.audioLevels = [];
630
+ clearIntervals() {
631
+ clearInterval(this.audioIntervals.level);
632
+ clearInterval(this.audioIntervals.levelAverage);
633
+ clearInterval(this.audioIntervals.muteDetection);
634
+ delete this.audioIntervals.level;
635
+ delete this.audioIntervals.levelAverage;
636
+ delete this.audioIntervals.muteDetection;
637
+ this.audioLevels.fill(0);
634
638
  }
635
639
  release() {
636
640
  this.listener = undefined;
637
- this.clearListeners();
641
+ this.clearIntervals();
638
642
  this.stream = undefined;
639
643
  try {
640
644
  this.audioStreamSource.disconnect(this.analyser);
@@ -988,8 +992,14 @@ class $d582bbc5717818b4$export$2e2bcd8739ae039 {
988
992
  let needToAskVideo = Boolean(constraints.video && (!this.video.stream || !this.video.track || this.video.track.readyState === 'ended'));
989
993
  const audioDeviceId = $d582bbc5717818b4$export$fceddabcb6d8e584(constraints.audio);
990
994
  const videoDeviceId = $d582bbc5717818b4$export$fceddabcb6d8e584(constraints.video);
991
- if (audioDeviceId && audioDeviceId !== this.audio.track?.getSettings().deviceId) needToAskAudio = true;
992
- if (videoDeviceId && videoDeviceId !== this.video.track?.getSettings().deviceId) needToAskVideo = true;
995
+ const oldAudioDeviceId = this.audio.track?.getSettings().deviceId;
996
+ const oldVideoDeviceId = this.video.track?.getSettings().deviceId;
997
+ /*
998
+ If deviceId is 'default', we cant differentiate if it's the same device or not. https://issues.chromium.org/issues/40199570#comment23
999
+ A possible solution would be to compare the label of the track. There is no guarantee that it will be unique or match the deviceId.
1000
+ ex track label: Default - Microphone (USB Audio Device) (046d:0992)
1001
+ */ if (audioDeviceId && (audioDeviceId === 'default' || audioDeviceId !== oldAudioDeviceId)) needToAskAudio = true;
1002
+ if (videoDeviceId && (videoDeviceId === 'default' || videoDeviceId !== oldVideoDeviceId)) needToAskVideo = true;
993
1003
  if (typeof constraints.video === 'object' && constraints.video.facingMode && constraints.video.facingMode !== this.video.track?.getSettings().facingMode) needToAskVideo = true;
994
1004
  return {
995
1005
  needToAskAudio: needToAskAudio,
@@ -1886,7 +1896,7 @@ class $1dedebd5ff3002eb$export$2e2bcd8739ae039 extends $1dedebd5ff3002eb$var$Str
1886
1896
  }
1887
1897
  async getTransport(kind, direction) {
1888
1898
  if (!this.streamerMediasoup || !this.protoo) throw new Error('streamerMediasoup not initialized');
1889
- let transport = this.mediasoupTransport[`${kind}-${direction}`];
1899
+ let transport = this.mediasoupTransport[direction];
1890
1900
  if (!transport) {
1891
1901
  try {
1892
1902
  const promise = this.streamerMediasoup.createTransport(this.protoo, direction);
@@ -1895,7 +1905,7 @@ class $1dedebd5ff3002eb$export$2e2bcd8739ae039 extends $1dedebd5ff3002eb$var$Str
1895
1905
  kind: kind,
1896
1906
  createPromise: promise
1897
1907
  };
1898
- this.mediasoupTransport[`${kind}-${direction}`] = transport;
1908
+ this.mediasoupTransport[direction] = transport;
1899
1909
  transport.transport = await promise;
1900
1910
  if (transport.transport) this.listenWebRTCTransportStates(transport);
1901
1911
  } catch (err) {
@@ -2078,6 +2088,7 @@ class $1dedebd5ff3002eb$export$2e2bcd8739ae039 extends $1dedebd5ff3002eb$var$Str
2078
2088
  await this.micProducer?.replaceTrack({
2079
2089
  track: track
2080
2090
  });
2091
+ if (this.micProducer?.paused) track.enabled = false;
2081
2092
  $1dedebd5ff3002eb$var$log.log('switchMicrophone', 'switched Microphone');
2082
2093
  const trackSettings = track.getSettings();
2083
2094
  const device = this.devicesList.find((it)=>it.deviceId === trackSettings.deviceId);
@@ -2503,7 +2514,6 @@ class $1dedebd5ff3002eb$export$2e2bcd8739ae039 extends $1dedebd5ff3002eb$var$Str
2503
2514
  this.webcam.facingMode = config.facingMode;
2504
2515
  } else {
2505
2516
  if (config?.device) this.webcam.device = config.device;
2506
- $1dedebd5ff3002eb$var$log.warn('enableVideo', 'default deviceId', this.webcam.device?.deviceId);
2507
2517
  optionalParam.deviceId = {
2508
2518
  ideal: this.webcam.device?.deviceId
2509
2519
  };
@@ -2628,6 +2638,7 @@ class $1dedebd5ff3002eb$export$2e2bcd8739ae039 extends $1dedebd5ff3002eb$var$Str
2628
2638
  audio: false,
2629
2639
  video: true
2630
2640
  });
2641
+ this.webcam.device = null;
2631
2642
  this.webcamProducer = null;
2632
2643
  this.webcamTrack = null;
2633
2644
  if (this.useVideoBackground) {
@@ -3986,6 +3997,7 @@ class $21395e477f83709c$export$45fabd1ce5e673de {
3986
3997
  this.video.addEventListener('enterpictureinpicture', this.onEnterPictureInPicture.bind(this));
3987
3998
  this.video.addEventListener('leavepictureinpicture', this.onLeavePictureInPicture.bind(this));
3988
3999
  this.tiles = [];
4000
+ this.missingVideoWarnings = {};
3989
4001
  }
3990
4002
  async start() {
3991
4003
  if (this.tiles.length) {
@@ -4016,6 +4028,12 @@ class $21395e477f83709c$export$45fabd1ce5e673de {
4016
4028
  this.started = false;
4017
4029
  this.video.srcObject = null;
4018
4030
  }
4031
+ warnMissingVideo(tileId) {
4032
+ if (!this.missingVideoWarnings[tileId]) {
4033
+ this.missingVideoWarnings[tileId] = '1';
4034
+ console.warn(`MultiPiP: No video found for peer ${tileId}`);
4035
+ }
4036
+ }
4019
4037
  draw() {
4020
4038
  if (this.ctx && this.started) {
4021
4039
  this.ctx.fillStyle = '#1f1f1f';
@@ -4023,7 +4041,10 @@ class $21395e477f83709c$export$45fabd1ce5e673de {
4023
4041
  if (this.tiles.length) {
4024
4042
  this.tiles.forEach((tile, index)=>{
4025
4043
  const tileVideo = document.body.querySelectorAll(`[data-peer-id='${tile.id}'] video`)[0];
4026
- if (!tileVideo) return console.warn(`MultiPiP: No video found for peer ${tile.id}`);
4044
+ if (!tileVideo) {
4045
+ this.warnMissingVideo(tile.id);
4046
+ return;
4047
+ }
4027
4048
  const tileRect = this.layout?.tileRects[index];
4028
4049
  if (!tileRect) return;
4029
4050
  this.ctx?.save();
@@ -6358,6 +6379,12 @@ const $b7b8f2e5b7552341$export$12201ede76c4c5e1 = ()=>{
6358
6379
  const base64Image = await (0, $c48c1ecc38fed4e9$export$2f377c2162fd02b2).captureVideo(videoElementRef.current);
6359
6380
  try {
6360
6381
  const uploaded = await (0, $c48c1ecc38fed4e9$export$2f377c2162fd02b2).saveCapture(base64Image);
6382
+ await (0, $c48c1ecc38fed4e9$export$2f377c2162fd02b2).createEvents([
6383
+ {
6384
+ filename: uploaded.filename,
6385
+ type: 'image'
6386
+ }
6387
+ ]);
6361
6388
  await (0, $c48c1ecc38fed4e9$export$2f377c2162fd02b2).sendCustomMessageToAll({
6362
6389
  type: 'snapshot',
6363
6390
  imageSrc: uploaded.url
@@ -7131,6 +7158,7 @@ const $75b49da54f729405$export$f36e83cafa401ab5 = ({ stream: stream, width: widt
7131
7158
  onSnapshot: ()=>snapshot.trigger({
7132
7159
  videoElementRef: videoElementRef
7133
7160
  }),
7161
+ "data-peer-id": stream.id,
7134
7162
  children: [
7135
7163
  /*#__PURE__*/ (0, $jQDcL$reactjsxruntime.jsx)((0, $66c45b20958474ec$export$d574f5511ec1d79a), {
7136
7164
  ref: videoElementRef,
@@ -12119,14 +12147,14 @@ const $c42fbc742adf8306$export$1ea93f9eface5983 = ({ duration: duration = 0, ave
12119
12147
  duotone: true
12120
12148
  })
12121
12149
  });
12122
- if (averageAudioLevel <= 5) return /*#__PURE__*/ (0, $jQDcL$reactjsxruntime.jsx)($c42fbc742adf8306$var$Notification, {
12150
+ if (averageAudioLevel <= 1) return /*#__PURE__*/ (0, $jQDcL$reactjsxruntime.jsx)($c42fbc742adf8306$var$Notification, {
12123
12151
  title: t('recorder.audioLevelNotification.spokenWordsWarning.title'),
12124
12152
  description: t('recorder.audioLevelNotification.spokenWordsWarning.description'),
12125
12153
  icon: /*#__PURE__*/ (0, $jQDcL$reactjsxruntime.jsx)((0, $jQDcL$snapcalldesignsystemicons.AlertTriangleIcon), {
12126
12154
  duotone: true
12127
12155
  })
12128
12156
  });
12129
- if (averageAudioLevel <= 25) return /*#__PURE__*/ (0, $jQDcL$reactjsxruntime.jsx)($c42fbc742adf8306$var$Notification, {
12157
+ if (averageAudioLevel <= 3) return /*#__PURE__*/ (0, $jQDcL$reactjsxruntime.jsx)($c42fbc742adf8306$var$Notification, {
12130
12158
  title: t('recorder.audioLevelNotification.spokenWordsQuestion.title'),
12131
12159
  description: t('recorder.audioLevelNotification.spokenWordsQuestion.description'),
12132
12160
  icon: /*#__PURE__*/ (0, $jQDcL$reactjsxruntime.jsx)((0, $jQDcL$snapcalldesignsystemicons.InfoCircleIcon), {
@@ -12267,7 +12295,7 @@ const $8a7a24ac08dbc187$var$getStoredAssets = async ()=>{
12267
12295
  const storedAssets = JSON.parse(localStorage.getItem('snapcall_assets') || '[]');
12268
12296
  const assets = await Promise.all(storedAssets.map(async (storedAsset)=>{
12269
12297
  try {
12270
- let { url: url, thumbnailUrl: thumbnailUrl } = await (0, $c48c1ecc38fed4e9$export$2f377c2162fd02b2).readAsset(storedAsset.filename);
12298
+ let { url: url, thumbnailUrl: thumbnailUrl, error: error } = await (0, $c48c1ecc38fed4e9$export$2f377c2162fd02b2).readAsset(storedAsset.filename);
12271
12299
  if (url) {
12272
12300
  if (storedAsset.mode === 'photo') thumbnailUrl = url;
12273
12301
  return {
@@ -12275,7 +12303,7 @@ const $8a7a24ac08dbc187$var$getStoredAssets = async ()=>{
12275
12303
  url: url,
12276
12304
  thumbnailUrl: thumbnailUrl
12277
12305
  };
12278
- }
12306
+ } else if (error) $8a7a24ac08dbc187$export$df987b50509121ea(storedAsset.filename);
12279
12307
  } catch (error) {
12280
12308
  $8a7a24ac08dbc187$export$df987b50509121ea(storedAsset.filename);
12281
12309
  }
@@ -12321,6 +12349,31 @@ const $8a7a24ac08dbc187$export$931d641a2a152cf = ()=>{
12321
12349
 
12322
12350
 
12323
12351
 
12352
+
12353
+
12354
+ const $663f53e6b55f0d5a$export$f2bc44ad94513462 = ()=>{
12355
+ const { clientInitResult: clientInitResult } = (0, $jQDcL$react.useContext)((0, $8b39f32976a7698a$export$2e2bcd8739ae039));
12356
+ const flowSource = clientInitResult.flow?.steps?.[0].config?.source;
12357
+ const onRecorderEnd = ()=>{
12358
+ if (flowSource === 'smooch') // @ts-ignore
12359
+ window.WebviewSdk?.close?.();
12360
+ else if (flowSource === 'whatsapp') window.location.href = 'https://wa.me';
12361
+ };
12362
+ (0, $jQDcL$react.useEffect)(()=>{
12363
+ if (flowSource === 'smooch') {
12364
+ const smoochScript = document.createElement('script');
12365
+ smoochScript.src = 'https://cdn.smooch.io/webview-sdk.min.js';
12366
+ if (!document.body.contains(smoochScript)) document.body.appendChild(smoochScript);
12367
+ }
12368
+ }, [
12369
+ flowSource
12370
+ ]);
12371
+ return {
12372
+ onRecorderEnd: onRecorderEnd
12373
+ };
12374
+ };
12375
+
12376
+
12324
12377
  const $e399416dd32d3252$var$eventTypes = {
12325
12378
  screen: 'screenshare',
12326
12379
  photo: 'image',
@@ -12330,6 +12383,7 @@ const $e399416dd32d3252$var$eventTypes = {
12330
12383
  const $e399416dd32d3252$export$c01bb29adf88f117 = ({ state: state })=>{
12331
12384
  const { t: t } = (0, $jQDcL$reacti18next.useTranslation)();
12332
12385
  const { assets: assets, incomingAsset: incomingAsset } = (0, $098350f721a0bb52$export$2174f25d572f9f31)();
12386
+ const flowSourceAction = (0, $663f53e6b55f0d5a$export$f2bc44ad94513462)();
12333
12387
  const sendMutation = (0, $jQDcL$tanstackreactquery.useMutation)({
12334
12388
  mutationFn: async ()=>{
12335
12389
  try {
@@ -12354,7 +12408,6 @@ const $e399416dd32d3252$export$c01bb29adf88f117 = ({ state: state })=>{
12354
12408
  });
12355
12409
  throw new Error('some media not saved');
12356
12410
  }
12357
- (0, $c48c1ecc38fed4e9$export$2f377c2162fd02b2).release();
12358
12411
  return results;
12359
12412
  } catch (error) {
12360
12413
  reportError({
@@ -12364,6 +12417,10 @@ const $e399416dd32d3252$export$c01bb29adf88f117 = ({ state: state })=>{
12364
12417
  console.log(error);
12365
12418
  throw error;
12366
12419
  }
12420
+ },
12421
+ onSuccess: ()=>{
12422
+ (0, $c48c1ecc38fed4e9$export$2f377c2162fd02b2).release();
12423
+ flowSourceAction.onRecorderEnd();
12367
12424
  }
12368
12425
  });
12369
12426
  const reportError = (0, $8dfcca373a03b9e8$export$5a5695b638d078e7)();
package/dist/types.d.ts CHANGED
@@ -18,6 +18,7 @@ interface Step {
18
18
  config?: {
19
19
  description: string;
20
20
  image_url: string;
21
+ source?: string;
21
22
  };
22
23
  translation: {
23
24
  title?: string;
@@ -192,16 +193,16 @@ declare class AudioLevel {
192
193
  readonly id: string;
193
194
  constructor(stream: MediaStream, listener?: AudioLevelListener);
194
195
  /**
195
- * return the median of the audio levels (60 per second) for the
196
+ * return the average (mean) of the audio levels (AUDIO_DATA_RATE per second) for the
196
197
  * current second
197
198
  */
198
- getAudioLevelMedian(): number;
199
+ getRecentAudioLevelAverage(): number;
199
200
  getCurrentAudioLevel(): number;
200
201
  analyse(): void;
201
202
  startAverageAnalysis(): void;
202
203
  stopAverageAnalysis(): number;
203
204
  getFrequencyData(): Uint8Array;
204
- clearListeners(): void;
205
+ clearIntervals(): void;
205
206
  release(): void;
206
207
  static isAPIAvailable(): boolean;
207
208
  }
@@ -599,6 +600,7 @@ declare class StreamerClient extends StreamerEventTargetType implements AudioLev
599
600
  readAsset(file: string): Promise<{
600
601
  url: string;
601
602
  thumbnailUrl?: string;
603
+ error?: string;
602
604
  }>;
603
605
  deleteAsset(file: string): Promise<{
604
606
  status: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snapcall/stream-ui",
3
- "version": "1.28.0",
3
+ "version": "1.29.0",
4
4
  "description": "",
5
5
  "source": "src/index.tsx",
6
6
  "main": "dist/stream-ui.js",