@memori.ai/memori-react 1.0.0-alpha.21 → 1.0.0-alpha.23

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.
@@ -31,7 +31,7 @@ import React, {
31
31
  } from 'react';
32
32
  import { useTranslation } from 'react-i18next';
33
33
  import memoriApiClient from '@memori.ai/memori-api-client';
34
- import { AudioContext } from 'standardized-audio-context';
34
+ import { AudioContext, IAudioContext } from 'standardized-audio-context';
35
35
  import * as speechSdk from 'microsoft-cognitiveservices-speech-sdk';
36
36
  import cx from 'classnames';
37
37
 
@@ -143,6 +143,7 @@ let recognizer: SpeechRecognizer | null;
143
143
  let speechConfig: SpeechConfig;
144
144
  let speechSynthesizer: SpeechSynthesizer | null;
145
145
  let audioDestination: SpeakerAudioDestination;
146
+ let audioContext: IAudioContext;
146
147
 
147
148
  export interface Props {
148
149
  memori: Memori;
@@ -254,7 +255,7 @@ const MemoriWidget = ({
254
255
  const [showPositionDrawer, setShowPositionDrawer] = useState(false);
255
256
  const [showSettingsDrawer, setShowSettingsDrawer] = useState(false);
256
257
  const [muteSpeaker, setMuteSpeaker] = useState(false);
257
- const [continuousSpeech, setContinuousSpeech] = useState(false);
258
+ const [continuousSpeech, setContinuousSpeech] = useState(true);
258
259
  const [continuousSpeechTimeout, setContinuousSpeechTimeout] = useState(3);
259
260
  const [isPlayingAudio, setIsPlayingAudio] = useState(false);
260
261
  useEffect(() => {
@@ -264,7 +265,7 @@ const MemoriWidget = ({
264
265
 
265
266
  useEffect(() => {
266
267
  setMuteSpeaker(getLocalConfig('muteSpeaker', false));
267
- setContinuousSpeech(getLocalConfig('continuousSpeech', false));
268
+ setContinuousSpeech(getLocalConfig('continuousSpeech', true));
268
269
  setContinuousSpeechTimeout(getLocalConfig('continuousSpeechTimeout', 3));
269
270
  }, []);
270
271
 
@@ -379,6 +380,7 @@ const MemoriWidget = ({
379
380
  media: currentState.media,
380
381
  fromUser: false,
381
382
  });
383
+ speak(currentState.emission);
382
384
  }
383
385
  } else {
384
386
  console.error(response, resp);
@@ -405,6 +407,7 @@ const MemoriWidget = ({
405
407
  media: currentState.media,
406
408
  fromUser: false,
407
409
  });
410
+ speak(currentState.emission);
408
411
  }
409
412
  } else {
410
413
  console.error(response, resp);
@@ -420,19 +423,25 @@ const MemoriWidget = ({
420
423
  !instruct &&
421
424
  isMultilanguageEnabled
422
425
  ) {
423
- translateDialogState(currentState, userLang);
426
+ translateDialogState(currentState, userLang).then(ts => {
427
+ if (ts.emission) {
428
+ speak(ts.emission);
429
+ }
430
+ });
424
431
  } else {
425
432
  setCurrentDialogState({
426
433
  ...currentState,
427
434
  emission,
428
435
  });
429
436
 
430
- if (emission)
437
+ if (emission) {
431
438
  pushMessage({
432
439
  text: emission,
433
440
  media: currentState.media,
434
441
  fromUser: false,
435
442
  });
443
+ speak(emission);
444
+ }
436
445
  }
437
446
  } else if (response.resultCode === 404) {
438
447
  // remove last sent message, will set it as initial
@@ -447,15 +456,16 @@ const MemoriWidget = ({
447
456
  instruct && memori.giverPIN ? memori.giverPIN : undefined,
448
457
  initialContextVars,
449
458
  initialQuestion
450
- ).then(sessionID => {
459
+ ).then(state => {
451
460
  console.info('session timeout');
452
- if (sessionID) {
461
+ if (state?.sessionID) {
453
462
  setTimeout(() => {
454
- sendMessage(text, media, sessionID);
463
+ sendMessage(text, media, state?.sessionID);
455
464
  }, 500);
456
465
  }
457
466
  });
458
467
  }
468
+
459
469
  setMemoriTyping(false);
460
470
  };
461
471
 
@@ -577,53 +587,6 @@ const MemoriWidget = ({
577
587
  session.resultCode === 0
578
588
  ) {
579
589
  setSessionId(session.sessionID);
580
- const language =
581
- memori.culture?.split('-')?.[0] ?? i18n.language ?? 'IT';
582
-
583
- if (
584
- !instruct &&
585
- isMultilanguageEnabled &&
586
- userLang.toLowerCase() !== language.toLowerCase()
587
- ) {
588
- translateDialogState(session.currentState, userLang).then(state => {
589
- if (state?.emission) {
590
- history.length <= 1
591
- ? setHistory([
592
- {
593
- text: state.emission,
594
- media: state.media,
595
- fromUser: false,
596
- initial: true,
597
- },
598
- ])
599
- : pushMessage({
600
- text: state.emission,
601
- media: state.media,
602
- fromUser: false,
603
- initial: true,
604
- });
605
- }
606
- });
607
- } else {
608
- setCurrentDialogState(session.currentState);
609
- if (session.currentState.emission) {
610
- history.length <= 1
611
- ? setHistory([
612
- {
613
- text: session.currentState.emission,
614
- media: session.currentState.media,
615
- fromUser: false,
616
- initial: true,
617
- },
618
- ])
619
- : pushMessage({
620
- text: session.currentState.emission,
621
- media: session.currentState.media,
622
- fromUser: false,
623
- initial: true,
624
- });
625
- }
626
- }
627
590
 
628
591
  if (position) applyPosition(position, session.sessionID);
629
592
 
@@ -688,7 +651,10 @@ const MemoriWidget = ({
688
651
  if (position) applyPosition(position, sessionID);
689
652
 
690
653
  setLoading(false);
691
- return sessionID;
654
+ return {
655
+ dialogState: currentState,
656
+ sessionID,
657
+ };
692
658
  } else {
693
659
  console.error(response);
694
660
  message.error(t(getErrori18nKey(response.resultCode)));
@@ -721,7 +687,6 @@ const MemoriWidget = ({
721
687
 
722
688
  if (resultCode === 0) {
723
689
  let textResult = 0;
724
- // console.debug('[APPCONTEXT/CHANGETAG]', currentState);
725
690
  if (
726
691
  tag !== anonTag &&
727
692
  pin &&
@@ -846,13 +811,18 @@ const MemoriWidget = ({
846
811
  translateDialogState(
847
812
  { ...currentState, emission: emission },
848
813
  userLang
849
- );
814
+ ).then(ts => {
815
+ if (ts.emission) {
816
+ speak(ts.emission);
817
+ }
818
+ });
850
819
  } else if (emission && emission.length > 0) {
851
820
  pushMessage({
852
821
  text: emission,
853
822
  media: currentState.media,
854
823
  fromUser: false,
855
824
  });
825
+ speak(emission);
856
826
  setCurrentDialogState(currentState);
857
827
  }
858
828
  }
@@ -916,40 +886,19 @@ const MemoriWidget = ({
916
886
  speechConfig.speechSynthesisOutputFormat =
917
887
  speechSdk.SpeechSynthesisOutputFormat.Audio16Khz32KBitRateMonoMp3;
918
888
 
919
- let memoriAudioElement = document.getElementById(
920
- 'memori-audio'
921
- ) as HTMLAudioElement;
922
- if (memoriAudioElement && window.navigator.userAgent.includes('Safari')) {
923
- memoriAudioElement.muted = false;
924
- memoriAudioElement
925
- .play()
926
- .then(() => {
927
- console.log('played intro audio');
928
- try {
929
- const context = new AudioContext();
930
- let buffer = context.createBuffer(1, 1, 22050);
931
- let source = context.createBufferSource();
932
- source.buffer = buffer;
933
- source.connect(context.destination);
934
- } catch (e) {
935
- console.error(e);
936
- }
937
- })
938
- .catch((e: any) => {
939
- console.error('error playing intro audio', e);
940
- });
941
- }
889
+ audioContext = new AudioContext();
890
+ let buffer = audioContext.createBuffer(1, 10000, 22050);
891
+ let source = audioContext.createBufferSource();
892
+ source.buffer = buffer;
893
+ source.connect(audioContext.destination);
894
+
895
+ audioDestination = new speechSdk.SpeakerAudioDestination();
896
+ let audioConfig = speechSdk.AudioConfig.fromSpeakerOutput(audioDestination);
897
+ speechSynthesizer = new speechSdk.SpeechSynthesizer(
898
+ speechConfig,
899
+ audioConfig
900
+ );
942
901
  };
943
- useEffect(() => {
944
- return () => {
945
- if (audioDestination) audioDestination.pause();
946
- if (speechSynthesizer) {
947
- speechSynthesizer.close();
948
- speechSynthesizer = null;
949
- }
950
- };
951
- // eslint-disable-next-line react-hooks/exhaustive-deps
952
- }, []);
953
902
 
954
903
  const getTTSVoice = useCallback(
955
904
  (lang?: string): string => {
@@ -1032,8 +981,12 @@ const MemoriWidget = ({
1032
981
 
1033
982
  const getCultureCodeByLanguage = (lang?: string): string => {
1034
983
  let voice = '';
1035
- let voiceLang = (lang ?? memori.culture?.split('-')?.[0] ?? i18n.language,
1036
- 'IT').toUpperCase();
984
+ let voiceLang = (
985
+ lang ||
986
+ memori.culture?.split('-')?.[0] ||
987
+ i18n.language ||
988
+ 'IT'
989
+ ).toUpperCase();
1037
990
  switch (voiceLang) {
1038
991
  case 'IT':
1039
992
  voice = 'it-IT';
@@ -1121,7 +1074,7 @@ const MemoriWidget = ({
1121
1074
  // .replace(/qfe/gi, `<sub alias="Quota Filo Erba">QFE</sub>`)
1122
1075
  };
1123
1076
 
1124
- const speak = (text: string, fireListeningEvent = true): void => {
1077
+ const speak = (text: string): void => {
1125
1078
  console.log(
1126
1079
  AZURE_COGNITIVE_SERVICES_TTS_KEY,
1127
1080
  hasUserActivatedSpeak,
@@ -1129,59 +1082,62 @@ const MemoriWidget = ({
1129
1082
  );
1130
1083
  if (!AZURE_COGNITIVE_SERVICES_TTS_KEY) return;
1131
1084
 
1132
- if (preview || !hasUserActivatedSpeak) return;
1085
+ if (listening) {
1086
+ stopListening();
1087
+ }
1088
+
1089
+ if (preview) return;
1090
+
1133
1091
  if (audioDestination) audioDestination.pause();
1134
- if (speechSynthesizer) {
1135
- speechSynthesizer.close();
1136
- speechSynthesizer = null;
1092
+
1093
+ let isSafari =
1094
+ window.navigator.userAgent.includes('Safari') &&
1095
+ !window.navigator.userAgent.includes('Chrome');
1096
+ let isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
1097
+ if (isIOS && isSafari) {
1098
+ audioContext.suspend();
1099
+ } else if (audioContext.state === 'suspended') {
1100
+ stopAudio();
1101
+ audioContext = new AudioContext();
1102
+ let buffer = audioContext.createBuffer(1, 10000, 22050);
1103
+ let source = audioContext.createBufferSource();
1104
+ source.buffer = buffer;
1105
+ source.connect(audioContext.destination);
1137
1106
  }
1138
1107
 
1139
- if (muteSpeaker && fireListeningEvent) {
1140
- setTimeout(() => {
1141
- // trigger start continuous listening if set, see MemoriChat
1142
- document.dispatchEvent(new Event('endSpeakStartListen'));
1143
- }, 3000);
1144
- return;
1145
- } else if (muteSpeaker) {
1146
- return;
1108
+ if (!speechSynthesizer) {
1109
+ audioDestination = new speechSdk.SpeakerAudioDestination();
1110
+ let audioConfig =
1111
+ speechSdk.AudioConfig.fromSpeakerOutput(audioDestination);
1112
+ speechSynthesizer = new speechSdk.SpeechSynthesizer(
1113
+ speechConfig,
1114
+ audioConfig
1115
+ );
1147
1116
  }
1148
1117
 
1149
- audioDestination = new speechSdk.SpeakerAudioDestination();
1150
- let audioConfig = speechSdk.AudioConfig.fromSpeakerOutput(audioDestination);
1151
- speechSynthesizer = new speechSdk.SpeechSynthesizer(
1152
- speechConfig,
1153
- audioConfig
1154
- );
1118
+ if (muteSpeaker) {
1119
+ // trigger start continuous listening if set, see MemoriChat
1120
+ if (continuousSpeech) {
1121
+ setListeningTimeout();
1122
+ }
1123
+ return;
1124
+ }
1155
1125
 
1156
1126
  audioDestination.onAudioEnd = () => {
1157
1127
  setIsPlayingAudio(false);
1158
1128
 
1159
- if (fireListeningEvent) {
1160
- // trigger start continuous listening if set, see MemoriChat
1129
+ if (continuousSpeech) {
1130
+ // trigger start continuous listening if set
1161
1131
  document.dispatchEvent(new Event('endSpeakStartListen'));
1162
1132
  }
1163
1133
  };
1164
1134
 
1165
- // speechSynthesizer.visemeReceived = function (s, e) {
1166
- // window.console.log(
1167
- // '(Viseme), Audio offset: ' +
1168
- // e.audioOffset / 10000 +
1169
- // 'ms. Viseme ID: ' +
1170
- // e.visemeId,
1171
- // e,
1172
- // );
1173
-
1174
- // // `Animation` is an xml string for SVG or a json string for blend shapes
1175
- // // var animation = e.Animation;
1176
- // };
1177
-
1178
1135
  setIsPlayingAudio(true);
1179
- console.log('speaking', text);
1180
- console.log('speechSynthesizer', speechSynthesizer);
1181
- console.log('audioDestination', audioDestination);
1182
- console.log('speechConfig', speechConfig);
1183
- console.log('audioConfig', audioConfig);
1184
- // window.speechSynthesis.speak(new SpeechSynthesisUtterance(text));
1136
+
1137
+ speechSynthesizer.synthesisCompleted = (s, e) => {
1138
+ console.log('synthesisCompleted', s, e);
1139
+ setIsPlayingAudio(false);
1140
+ };
1185
1141
  speechSynthesizer.speakSsmlAsync(
1186
1142
  `<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts" xmlns:emo="http://www.w3.org/2009/10/emotionml" xml:lang="${getCultureCodeByLanguage(
1187
1143
  userLang
@@ -1191,7 +1147,19 @@ const MemoriWidget = ({
1191
1147
  )}</s></voice></speak>`,
1192
1148
  result => {
1193
1149
  if (result) {
1150
+ console.log('result', result);
1194
1151
  try {
1152
+ audioContext.decodeAudioData(result.audioData, function (buffer) {
1153
+ const source = audioContext.createBufferSource();
1154
+ source.buffer = buffer;
1155
+ source.connect(audioContext.destination);
1156
+
1157
+ if (history.length < 1 || (isSafari && isIOS)) {
1158
+ source.start(0);
1159
+ }
1160
+ });
1161
+ audioContext.resume();
1162
+
1195
1163
  if (speechSynthesizer) {
1196
1164
  speechSynthesizer.close();
1197
1165
  speechSynthesizer = null;
@@ -1200,9 +1168,14 @@ const MemoriWidget = ({
1200
1168
  console.error('speak error: ', e);
1201
1169
  window.speechSynthesis.speak(new SpeechSynthesisUtterance(text));
1202
1170
  setIsPlayingAudio(false);
1171
+
1172
+ if (speechSynthesizer) {
1173
+ speechSynthesizer.close();
1174
+ speechSynthesizer = null;
1175
+ }
1203
1176
  }
1204
1177
  } else {
1205
- window.speechSynthesis.speak(new SpeechSynthesisUtterance(text));
1178
+ audioContext.resume();
1206
1179
  setIsPlayingAudio(false);
1207
1180
  }
1208
1181
  },
@@ -1214,12 +1187,20 @@ const MemoriWidget = ({
1214
1187
  );
1215
1188
 
1216
1189
  setIsPlayingAudio(false);
1190
+ setMemoriTyping(false);
1217
1191
  };
1218
1192
  const stopAudio = () => {
1219
1193
  if (speechSynthesizer) {
1220
1194
  speechSynthesizer.close();
1221
1195
  speechSynthesizer = null;
1222
1196
  }
1197
+ if (audioContext) {
1198
+ audioContext.close();
1199
+ }
1200
+ if (audioDestination) {
1201
+ audioDestination.pause();
1202
+ audioDestination.close();
1203
+ }
1223
1204
  };
1224
1205
 
1225
1206
  /**
@@ -1271,10 +1252,7 @@ const MemoriWidget = ({
1271
1252
  useEffect(() => {
1272
1253
  resetListeningTimeout();
1273
1254
  resetInteractionTimeout();
1274
- if (transcript?.length > 0) {
1275
- const transcriptMessage = stripDuplicates(transcript);
1276
- if (transcriptMessage.length > 0) setUserMessage(transcriptMessage);
1277
- }
1255
+
1278
1256
  // eslint-disable-next-line react-hooks/exhaustive-deps
1279
1257
  }, [transcript]);
1280
1258
 
@@ -1317,7 +1295,13 @@ const MemoriWidget = ({
1317
1295
  setListening(true);
1318
1296
  recognizer.recognized = (_s, e) => {
1319
1297
  if (e.result.reason === speechSdk.ResultReason.RecognizedSpeech) {
1320
- setTranscript(e.result.text ?? '');
1298
+ let transcript = e.result.text;
1299
+ setTranscript(transcript || '');
1300
+ if (transcript?.length > 0) {
1301
+ const transcriptMessage = stripDuplicates(transcript);
1302
+ if (transcriptMessage.length > 0)
1303
+ setUserMessage(transcriptMessage);
1304
+ }
1321
1305
  } else if (e.result.reason === speechSdk.ResultReason.NoMatch) {
1322
1306
  console.debug('NOMATCH: Speech could not be recognized.');
1323
1307
  }
@@ -1335,7 +1319,7 @@ const MemoriWidget = ({
1335
1319
  };
1336
1320
 
1337
1321
  recognizer.sessionStopped = (_s, _e) => {
1338
- if (recognizer) recognizer.stopContinuousRecognitionAsync();
1322
+ stopListening();
1339
1323
  };
1340
1324
  recognizer.startContinuousRecognitionAsync();
1341
1325
  })
@@ -1349,8 +1333,8 @@ const MemoriWidget = ({
1349
1333
  recognizer.stopContinuousRecognitionAsync();
1350
1334
  recognizer.close();
1351
1335
  recognizer = null;
1352
- setListening(false);
1353
1336
  }
1337
+ setListening(false);
1354
1338
  };
1355
1339
  const clearListening = () => {
1356
1340
  setHasUserActivatedListening(false);
@@ -1374,19 +1358,6 @@ const MemoriWidget = ({
1374
1358
  // eslint-disable-next-line react-hooks/exhaustive-deps
1375
1359
  }, [currentDialogState?.state]);
1376
1360
 
1377
- useEffect(() => {
1378
- if (
1379
- hasUserActivatedSpeak &&
1380
- !preview &&
1381
- !muteSpeaker &&
1382
- history.length > 0 &&
1383
- currentDialogState?.emission
1384
- ) {
1385
- speak(currentDialogState.emission, currentDialogState.state !== 'Z0');
1386
- }
1387
- // eslint-disable-next-line react-hooks/exhaustive-deps
1388
- }, [currentDialogState, hasUserActivatedSpeak]);
1389
-
1390
1361
  /**
1391
1362
  * Speech recognition event handlers
1392
1363
  */
@@ -1658,7 +1629,19 @@ const MemoriWidget = ({
1658
1629
  const sessionID = session?.sessionID || sessionId;
1659
1630
  const dialogState = session?.dialogState || currentDialogState;
1660
1631
  setClickedStart(true);
1661
- console.log('onClickStart');
1632
+
1633
+ let memoriAudioElement = document.getElementById(
1634
+ 'memori-audio'
1635
+ ) as HTMLAudioElement;
1636
+ let isSafari =
1637
+ window.navigator.userAgent.includes('Safari') &&
1638
+ !window.navigator.userAgent.includes('Chrome');
1639
+ if (memoriAudioElement && isSafari) {
1640
+ memoriAudioElement.muted = false;
1641
+ memoriAudioElement.play().catch((e: any) => {
1642
+ console.error('error playing intro audio', e);
1643
+ });
1644
+ }
1662
1645
 
1663
1646
  if (
1664
1647
  (!sessionID &&
@@ -1682,7 +1665,7 @@ const MemoriWidget = ({
1682
1665
  initialContextVars,
1683
1666
  initialQuestion,
1684
1667
  });
1685
- onClickStart(session || undefined);
1668
+ await onClickStart(session || undefined);
1686
1669
  return;
1687
1670
  } else if (initialSessionID) {
1688
1671
  // check if session is valid and not expired
@@ -1692,7 +1675,7 @@ const MemoriWidget = ({
1692
1675
  setGotErrorInOpening(true);
1693
1676
  setSessionId(undefined);
1694
1677
  setClickedStart(false);
1695
- onClickStart();
1678
+ await onClickStart();
1696
1679
  return;
1697
1680
  }
1698
1681
 
@@ -1722,9 +1705,15 @@ const MemoriWidget = ({
1722
1705
  );
1723
1706
 
1724
1707
  if (session && session.resultCode === 0) {
1725
- translateDialogState(session.currentState, userLang).finally(() => {
1726
- setHasUserActivatedSpeak(true);
1727
- });
1708
+ translateDialogState(session.currentState, userLang)
1709
+ .then(ts => {
1710
+ if (ts.emission) {
1711
+ speak(ts.emission);
1712
+ }
1713
+ })
1714
+ .finally(() => {
1715
+ setHasUserActivatedSpeak(true);
1716
+ });
1728
1717
  } else {
1729
1718
  console.error('session #1', session);
1730
1719
  throw new Error('No session');
@@ -1762,9 +1751,15 @@ const MemoriWidget = ({
1762
1751
  );
1763
1752
 
1764
1753
  if (session && session.resultCode === 0) {
1765
- translateDialogState(session.currentState, userLang).finally(() => {
1766
- setHasUserActivatedSpeak(true);
1767
- });
1754
+ translateDialogState(session.currentState, userLang)
1755
+ .then(ts => {
1756
+ if (ts.emission) {
1757
+ speak(ts.emission);
1758
+ }
1759
+ })
1760
+ .finally(() => {
1761
+ setHasUserActivatedSpeak(true);
1762
+ });
1768
1763
  } else {
1769
1764
  console.error('session #4', session);
1770
1765
  throw new Error('No session');
@@ -1802,9 +1797,15 @@ const MemoriWidget = ({
1802
1797
  );
1803
1798
 
1804
1799
  if (session && session.resultCode === 0) {
1805
- translateDialogState(session.currentState, userLang).finally(() => {
1806
- setHasUserActivatedSpeak(true);
1807
- });
1800
+ translateDialogState(session.currentState, userLang)
1801
+ .then(ts => {
1802
+ if (ts.emission) {
1803
+ speak(ts.emission);
1804
+ }
1805
+ })
1806
+ .finally(() => {
1807
+ setHasUserActivatedSpeak(true);
1808
+ });
1808
1809
  } else {
1809
1810
  console.error('session #7', session);
1810
1811
  throw new Error('No session');
@@ -1825,18 +1826,30 @@ const MemoriWidget = ({
1825
1826
  }
1826
1827
  } else {
1827
1828
  // no need to change tag
1828
- translateDialogState(currentState, userLang).finally(() => {
1829
- setHasUserActivatedSpeak(true);
1830
- });
1829
+ translateDialogState(currentState, userLang)
1830
+ .then(ts => {
1831
+ if (ts.emission) {
1832
+ speak(ts.emission);
1833
+ }
1834
+ })
1835
+ .finally(() => {
1836
+ setHasUserActivatedSpeak(true);
1837
+ });
1831
1838
  }
1832
1839
  } else {
1833
1840
  // reset history
1834
1841
  setHistory([]);
1835
1842
 
1836
1843
  // everything is fine, just translate dialog state and activate chat
1837
- translateDialogState(dialogState!, userLang).finally(() => {
1838
- setHasUserActivatedSpeak(true);
1839
- });
1844
+ translateDialogState(dialogState!, userLang)
1845
+ .then(ts => {
1846
+ if (ts.emission) {
1847
+ speak(ts.emission);
1848
+ }
1849
+ })
1850
+ .finally(() => {
1851
+ setHasUserActivatedSpeak(true);
1852
+ });
1840
1853
  }
1841
1854
  };
1842
1855
 
@@ -1890,7 +1903,18 @@ const MemoriWidget = ({
1890
1903
  setShowPositionDrawer={setShowPositionDrawer}
1891
1904
  setShowSettingsDrawer={setShowSettingsDrawer}
1892
1905
  speakerMuted={muteSpeaker}
1893
- setSpeakerMuted={setMuteSpeaker}
1906
+ setSpeakerMuted={mute => {
1907
+ setMuteSpeaker(mute);
1908
+ if (mute) {
1909
+ stopAudio();
1910
+ } else {
1911
+ audioContext = new AudioContext();
1912
+ let buffer = audioContext.createBuffer(1, 10000, 22050);
1913
+ let source = audioContext.createBufferSource();
1914
+ source.buffer = buffer;
1915
+ source.connect(audioContext.destination);
1916
+ }
1917
+ }}
1894
1918
  showSettings={showSettings}
1895
1919
  hasUserActivatedSpeak={hasUserActivatedSpeak}
1896
1920
  />
@@ -2070,9 +2094,9 @@ const MemoriWidget = ({
2070
2094
  initialContextVars,
2071
2095
  initialQuestion
2072
2096
  )
2073
- .then(() => {
2097
+ .then(state => {
2074
2098
  setAuthModalState(null);
2075
- setHasUserActivatedSpeak(true);
2099
+ onClickStart(state || undefined);
2076
2100
  })
2077
2101
  .catch(() => {
2078
2102
  setAuthModalState(null);
@@ -2119,6 +2143,7 @@ const MemoriWidget = ({
2119
2143
  media: currentState.media,
2120
2144
  fromUser: false,
2121
2145
  });
2146
+ speak(currentState.emission);
2122
2147
  }
2123
2148
  } else {
2124
2149
  console.error(resp, currentState, medium);
@@ -2174,6 +2199,7 @@ const MemoriWidget = ({
2174
2199
  media: currentState.media,
2175
2200
  fromUser: false,
2176
2201
  });
2202
+ speak(currentState.emission);
2177
2203
  }
2178
2204
  } else {
2179
2205
  console.error(resp, currentState, medium);
@@ -213,6 +213,9 @@ const StartPanel: React.FC<Props> = ({
213
213
  disabled={!!memori.blockedUntil && !memori.isGiver}
214
214
  loading={clickedStart}
215
215
  onClick={_e => {
216
+ speechSynthesis.speak(
217
+ new SpeechSynthesisUtterance('') // This is needed to enable the speech synthesis on iOS
218
+ );
216
219
  if (initializeTTS) initializeTTS();
217
220
  if (onClickStart) onClickStart();
218
221
  }}