agora-appbuilder-core 4.1.7 → 4.1.8-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/package.json +2 -2
  2. package/template/_package-lock.json +30671 -5376
  3. package/template/bridge/rtc/webNg/RtcEngine.ts +4 -4
  4. package/template/defaultConfig.js +2 -2
  5. package/template/esbuild.rsdk.go +1 -2
  6. package/template/package.json +0 -1
  7. package/template/src/AppWrapper.tsx +30 -35
  8. package/template/src/auth/AuthProvider.tsx +28 -35
  9. package/template/src/auth/IDPAuth.tsx +1 -14
  10. package/template/src/components/Controls.tsx +45 -15
  11. package/template/src/components/common/GenericModal.tsx +143 -0
  12. package/template/src/components/common/GenericPopup.tsx +152 -0
  13. package/template/src/components/common/data-table.tsx +385 -0
  14. package/template/src/components/controls/useControlPermissionMatrix.tsx +6 -7
  15. package/template/src/components/precall/usePreCall.tsx +1 -2
  16. package/template/src/components/room-info/useRoomInfo.tsx +1 -0
  17. package/template/src/components/stt-transcript/STTTranscriptTable.tsx +295 -0
  18. package/template/src/components/stt-transcript/ViewSTTTranscriptModal.tsx +44 -0
  19. package/template/src/components/stt-transcript/useFetchSTTTranscript.tsx +193 -0
  20. package/template/src/components/useUserPreference.tsx +0 -11
  21. package/template/src/language/default-labels/videoCallScreenLabels.ts +7 -0
  22. package/template/src/logger/AppBuilderLogger.tsx +1 -0
  23. package/template/src/pages/Create.tsx +2 -2
  24. package/template/src/pages/VideoCall.tsx +1 -6
  25. package/template/src/subComponents/LogoutButton.tsx +1 -11
  26. package/template/src/subComponents/recording/useRecordingLayoutQuery.tsx +83 -78
  27. package/template/src/utils/common.tsx +79 -1
  28. package/template/src/utils/useCreateRoom.ts +94 -112
  29. package/template/src/utils/useEndCall.ts +16 -3
  30. package/template/src/utils/useGetMeetingPhrase.ts +67 -76
  31. package/template/src/utils/useJoinRoom.ts +5 -3
  32. package/template/src/utils/useMutePSTN.ts +47 -45
  33. package/template/webpack.rsdk.config.js +1 -2
  34. package/template/src/components/GraphQLProvider.tsx +0 -122
@@ -13,14 +13,8 @@ import React, {useContext} from 'react';
13
13
  import {TouchableOpacity, Text, StyleSheet} from 'react-native';
14
14
  import StorageContext, {initStoreValue} from '../components/StorageContext';
15
15
  import {useHistory} from '../components/Router';
16
- import {gql, useMutation} from '@apollo/client';
17
16
  import {useString} from '../utils/useString';
18
17
 
19
- const LOGOUT = gql`
20
- mutation logoutSession($token: String!) {
21
- logoutSession(token: $token)
22
- }
23
- `;
24
18
  /**
25
19
  * Sends a logout request to the backend and logs out the user from the frontend.
26
20
  */
@@ -28,7 +22,6 @@ const LogoutButton = () => {
28
22
  const {store, setStore} = useContext(StorageContext);
29
23
  const {token} = store;
30
24
  const history = useHistory();
31
- const [logoutQuery] = useMutation(LOGOUT);
32
25
  //commented for v1 release
33
26
  // const oauthLoginLabel = useString('oauthLoginLabel')();
34
27
  // const logoutButton = useString('logoutButton')();
@@ -41,16 +34,13 @@ const LogoutButton = () => {
41
34
  * User stored some data in localstorage we don't want to remove their on logout.
42
35
  * so setting prevstate with store default value
43
36
  */
44
- setStore((prevState) => {
37
+ setStore(prevState => {
45
38
  return {
46
39
  ...prevState,
47
40
  ...initStoreValue,
48
41
  };
49
42
  });
50
43
  }
51
- logoutQuery({variables: {token}}).catch((e) => {
52
- console.log(e);
53
- });
54
44
  };
55
45
 
56
46
  const login = () => {
@@ -1,26 +1,15 @@
1
- import React from 'react';
1
+ import React, {useContext} from 'react';
2
2
  import {useParams} from '../../components/Router';
3
3
 
4
- import {gql, useMutation} from '@apollo/client';
5
4
  import {UidType} from '../../../agora-rn-uikit';
6
5
  import {logger, LogSource} from '../../logger/AppBuilderLogger';
7
6
  import getUniqueID from '../../utils/getUniqueID';
7
+ import StorageContext from '../../components/StorageContext';
8
8
 
9
- const SET_PRESENTER = gql`
10
- mutation setPresenter($uid: Int!, $passphrase: String!) {
11
- setPresenter(uid: $uid, passphrase: $passphrase)
12
- }
13
- `;
14
-
15
- const SET_NORMAL = gql`
16
- mutation setNormal($passphrase: String!) {
17
- setNormal(passphrase: $passphrase)
18
- }
19
- `;
9
+ const RECORDING_LAYOUT_URL = `${$config.BACKEND_ENDPOINT}/v1/recording/layout/update`;
20
10
 
21
11
  function useRecordingLayoutQuery() {
22
- const [setPresenterQuery] = useMutation(SET_PRESENTER);
23
- const [setNormalQuery] = useMutation(SET_NORMAL);
12
+ const {store} = useContext(StorageContext);
24
13
  const {phrase} = useParams<any>();
25
14
  /**
26
15
  * @param screenShareUid
@@ -30,104 +19,120 @@ function useRecordingLayoutQuery() {
30
19
  * and we want that as the main view
31
20
  * https://docs.agora.io/en/cloud-recording/cloud_recording_layout?platform=RESTful
32
21
  */
33
- const executePresenterQuery = (screenShareUid: UidType) => {
22
+ const executePresenterQuery = async (screenShareUid: UidType) => {
34
23
  const requestId = getUniqueID();
35
24
  const startReqTs = Date.now();
36
- setPresenterQuery({
37
- variables: {
38
- uid: screenShareUid,
25
+
26
+ try {
27
+ const payload = JSON.stringify({
28
+ uid: screenShareUid?.toString(),
39
29
  passphrase: phrase,
40
- },
41
- context: {
30
+ preset: 'presenter',
31
+ });
32
+ const res = await fetch(`${RECORDING_LAYOUT_URL}`, {
33
+ method: 'POST',
42
34
  headers: {
35
+ 'Content-Type': 'application/json',
36
+ authorization: store.token ? `Bearer ${store.token}` : '',
43
37
  'X-Request-Id': requestId,
44
38
  'X-Session-Id': logger.getSessionId(),
45
39
  },
46
- },
47
- })
48
- .then(res => {
49
- if (res?.data?.setPresenter === 'success') {
50
- const endReqTs = Date.now();
51
- logger.log(
52
- LogSource.Internals,
53
- 'RECORDING',
54
- 'setPresenterQuery success',
55
- {
56
- responseData: res,
57
- startReqTs,
58
- endReqTs,
59
- latency: endReqTs - startReqTs,
60
- requestId,
61
- },
62
- );
63
- }
64
- })
65
- .catch(error => {
40
+ body: payload,
41
+ });
42
+ const response = await res.json();
43
+
44
+ if (response?.error) {
45
+ throw response?.error;
46
+ } else {
66
47
  const endReqTs = Date.now();
67
- logger.error(
48
+ logger.log(
68
49
  LogSource.Internals,
69
50
  'RECORDING',
70
- 'setPresenterQuery failure',
71
- JSON.stringify(error || {}),
51
+ 'setPresenterQuery success',
72
52
  {
53
+ responseData: res,
73
54
  startReqTs,
74
55
  endReqTs,
75
56
  latency: endReqTs - startReqTs,
76
57
  requestId,
77
58
  },
78
59
  );
79
- });
60
+ }
61
+ } catch (error) {
62
+ const endReqTs = Date.now();
63
+ logger.error(
64
+ LogSource.Internals,
65
+ 'RECORDING',
66
+ 'setPresenterQuery failure',
67
+ JSON.stringify(error || {}),
68
+ {
69
+ startReqTs,
70
+ endReqTs,
71
+ latency: endReqTs - startReqTs,
72
+ requestId,
73
+ },
74
+ );
75
+ }
80
76
  };
81
77
 
82
- const executeNormalQuery = () => {
78
+ const executeNormalQuery = async () => {
83
79
  const requestId = getUniqueID();
84
80
  const startReqTs = Date.now();
85
- setNormalQuery({
86
- variables: {passphrase: phrase},
87
- context: {
81
+
82
+ const payload = JSON.stringify({
83
+ passphrase: phrase,
84
+ preset: 'normal',
85
+ });
86
+ try {
87
+ const res = await fetch(`${RECORDING_LAYOUT_URL}`, {
88
+ method: 'POST',
88
89
  headers: {
90
+ 'Content-Type': 'application/json',
91
+ authorization: store.token ? `Bearer ${store.token}` : '',
89
92
  'X-Request-Id': requestId,
90
93
  'X-Session-Id': logger.getSessionId(),
91
94
  },
92
- },
93
- })
94
- .then(res => {
95
- if (res?.data?.stopRecordingSession === 'success') {
96
- const endReqTs = Date.now();
97
- logger.log(
98
- LogSource.Internals,
99
- 'RECORDING',
100
- 'executeNormalQuery success',
101
- {
102
- responseData: res,
103
- startReqTs,
104
- endReqTs,
105
- latency: endReqTs - startReqTs,
106
- requestId,
107
- },
108
- );
109
- // Once the backend sucessfuly stops recording,
110
- // send a control message to everbody in the channel indicating that cloud recording is now inactive.
111
- // sendControlMessage(controlMessageEnum.cloudRecordingUnactive);
112
- // set the local recording state to false to update the UI
113
- // setScreenshareActive(false);
114
- }
115
- })
116
- .catch(error => {
95
+ body: payload,
96
+ });
97
+ const response = await res.json();
98
+
99
+ if (response?.error) {
100
+ throw response?.error;
101
+ } else {
117
102
  const endReqTs = Date.now();
118
- logger.error(
103
+ logger.log(
119
104
  LogSource.Internals,
120
105
  'RECORDING',
121
- 'executeNormalQuery failure',
122
- JSON.stringify(error || {}),
106
+ 'executeNormalQuery success',
123
107
  {
108
+ responseData: res,
124
109
  startReqTs,
125
110
  endReqTs,
126
111
  latency: endReqTs - startReqTs,
127
112
  requestId,
128
113
  },
129
114
  );
130
- });
115
+ // Once the backend sucessfuly stops recording,
116
+ // send a control message to everbody in the channel indicating that cloud recording is now inactive.
117
+ // sendControlMessage(controlMessageEnum.cloudRecordingUnactive);
118
+ // set the local recording state to false to update the UI
119
+ // setScreenshareActive(false);
120
+ }
121
+ } catch (error) {
122
+ const endReqTs = Date.now();
123
+ logger.error(
124
+ LogSource.Internals,
125
+ 'RECORDING',
126
+ 'executeNormalQuery failure',
127
+ JSON.stringify(error || {}),
128
+ {
129
+ startReqTs,
130
+ endReqTs,
131
+ latency: endReqTs - startReqTs,
132
+ requestId,
133
+ },
134
+ );
135
+ }
131
136
  };
132
137
 
133
138
  return {
@@ -254,7 +254,7 @@ const debounceFn = (fn: Function, ms = 300) => {
254
254
  };
255
255
 
256
256
  const capitalizeFirstLetter = (word: string): string => {
257
- return word.charAt(0).toUpperCase() + word.slice(1);
257
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
258
258
  };
259
259
 
260
260
  const CustomToolbarSort = (a, b) =>
@@ -382,6 +382,82 @@ function MergeMoreButtonFields(sourceArray, updateObject) {
382
382
  return result;
383
383
  }
384
384
 
385
+ function getFormattedDateTime(ipDate: string) {
386
+ try {
387
+ let rdate = new Date(ipDate);
388
+ let ryear = rdate.getFullYear();
389
+ if (ryear === 1) {
390
+ throw Error(`Invalid end date, ${ipDate}`);
391
+ }
392
+ let rmonth = rdate.getMonth() + 1;
393
+ let rdt = rdate.getDate();
394
+ let hour = rdate.getHours();
395
+ let minute = rdate.getMinutes();
396
+ let ampm = hour >= 12 ? 'pm' : 'am';
397
+ hour = hour % 12;
398
+ hour = hour ? hour : 12; // the hour '0' should be '12'
399
+ minute = minute < 10 ? minute : minute;
400
+
401
+ const formattedHHMM = `${String(hour)}:${String(minute).padStart(
402
+ 2,
403
+ '0',
404
+ )} ${ampm}`;
405
+
406
+ let today = new Date();
407
+ today.setHours(0);
408
+ today.setMinutes(0);
409
+ today.setSeconds(0);
410
+ today.setMilliseconds(0);
411
+
412
+ let compDate = new Date(ryear, rmonth - 1, rdt); // month - 1 because January == 0
413
+ let diff = today.getTime() - compDate.getTime(); // get the difference between today(at 00:00:00) and the date
414
+
415
+ if (compDate.getTime() == today.getTime()) {
416
+ return ['Today', `${formattedHHMM}`];
417
+ } else if (diff <= 24 * 60 * 60 * 1000) {
418
+ return ['Yesterday', `${formattedHHMM}`];
419
+ } else {
420
+ let fulldate = rdate.toDateString();
421
+ fulldate = fulldate.substring(fulldate.indexOf(' ') + 1);
422
+ return [fulldate, `${formattedHHMM}`];
423
+ }
424
+ } catch (error) {
425
+ console.error('error while converting recorded time: ', error);
426
+ return ipDate;
427
+ }
428
+ }
429
+
430
+ const getFileName = (url: string) => {
431
+ return url.split('#')[0].split('?')[0].split('/').pop();
432
+ };
433
+
434
+ const downloadS3Link = (url: string): Promise<void> => {
435
+ return new Promise((resolve, reject) => {
436
+ if (typeof url !== 'string' || !url.trim()) {
437
+ return reject(new Error('Invalid URL provided for download'));
438
+ }
439
+
440
+ try {
441
+ const fileName = getFileName(url);
442
+ const a = document.createElement('a');
443
+
444
+ a.href = url;
445
+ a.download = fileName;
446
+ a.target = '_blank';
447
+ a.rel = 'noopener noreferrer';
448
+
449
+ // Append to body to make `click()` work in all browsers
450
+ document.body.appendChild(a);
451
+ a.click();
452
+ // Clean up
453
+ document.body.removeChild(a);
454
+ resolve();
455
+ } catch (err) {
456
+ reject(err instanceof Error ? err : new Error('Download failed'));
457
+ }
458
+ });
459
+ };
460
+
385
461
  export {
386
462
  updateToolbarDefaultConfig,
387
463
  useIsDesktop,
@@ -415,4 +491,6 @@ export {
415
491
  CustomToolbarMerge,
416
492
  MergeMoreButtonFields,
417
493
  AuthErrorCodes,
494
+ downloadS3Link,
495
+ getFormattedDateTime,
418
496
  };
@@ -1,31 +1,13 @@
1
- import {gql, useMutation} from '@apollo/client';
2
1
  import {RoomInfoContextInterface} from '../components/room-info/useRoomInfo';
3
2
  import {useSetRoomInfo} from '../components/room-info/useSetRoomInfo';
4
3
  import SDKEvents from '../utils/SdkEvents';
5
- import isSDK from './isSDK';
6
4
  import {LogSource, logger} from '../logger/AppBuilderLogger';
7
5
  import getUniqueID from './getUniqueID';
6
+ import {useContext} from 'react';
7
+ import StorageContext from '../components/StorageContext';
8
+
9
+ const CREATE_ROOM_URL = `${$config.BACKEND_ENDPOINT}/v1/channel`;
8
10
 
9
- const CREATE_CHANNEL = gql`
10
- mutation CreateChannel($title: String!, $enablePSTN: Boolean) {
11
- createChannel(title: $title, enablePSTN: $enablePSTN) {
12
- passphrase {
13
- host
14
- view
15
- }
16
- channel
17
- title
18
- pstn {
19
- number
20
- dtmf
21
- error {
22
- code
23
- message
24
- }
25
- }
26
- }
27
- }
28
- `;
29
11
  /**
30
12
  * Returns an asynchronous function to create a meeting with the given options.
31
13
  */
@@ -34,8 +16,8 @@ export type createRoomFun = (
34
16
  enablePSTN?: boolean,
35
17
  ) => Promise<void>;
36
18
  export default function useCreateRoom(): createRoomFun {
37
- const [createChannel, {error}] = useMutation(CREATE_CHANNEL);
38
19
  const {setRoomInfo} = useSetRoomInfo();
20
+ const {store} = useContext(StorageContext);
39
21
  return async (
40
22
  roomTitle: string,
41
23
  enablePSTN?: boolean,
@@ -56,25 +38,99 @@ export default function useCreateRoom(): createRoomFun {
56
38
  requestId,
57
39
  },
58
40
  );
59
- const res = await createChannel({
60
- context: {
41
+ try {
42
+ const payload = JSON.stringify({
43
+ title: roomTitle,
44
+ enablePSTN: enablePSTN,
45
+ });
46
+ const response = await fetch(`${CREATE_ROOM_URL}`, {
47
+ method: 'POST',
61
48
  headers: {
49
+ 'Content-Type': 'application/json',
50
+ authorization: store.token ? `Bearer ${store.token}` : '',
62
51
  'X-Request-Id': requestId,
63
52
  'X-Session-Id': logger.getSessionId(),
64
53
  },
65
- },
66
- variables: {
67
- title: roomTitle,
68
- enablePSTN: enablePSTN,
69
- },
70
- });
71
- const endReqTs = Date.now();
72
- const latency = endReqTs - startReqTs;
73
- // in React-SDK mode, we use a more recent package of @apollo/client
74
- // which is compatible with react18, long term we should be looking to
75
- // upgrade core dependency as well. The following condition accounts
76
- // for differences in the way the two version function.
77
- if (error && !isSDK) {
54
+ body: payload,
55
+ });
56
+ const res = await response.json();
57
+ if (res?.error) {
58
+ throw res.error;
59
+ } else if (res?.channel) {
60
+ const endReqTs = Date.now();
61
+ const latency = endReqTs - startReqTs;
62
+ logger.log(
63
+ LogSource.NetworkRest,
64
+ 'createChannel',
65
+ 'API createChannel. Channel created successfully',
66
+ {
67
+ responseData: res,
68
+ startReqTs,
69
+ endReqTs,
70
+ latency: latency,
71
+ requestId,
72
+ },
73
+ );
74
+ let roomInfo: Partial<RoomInfoContextInterface['data']> = {
75
+ roomId: {
76
+ attendee: '',
77
+ },
78
+ };
79
+ if (res?.viewer_pass_phrase) {
80
+ roomInfo.roomId.attendee = res.viewer_pass_phrase;
81
+ }
82
+ if (res?.host_pass_phrase) {
83
+ roomInfo.roomId.host = res.host_pass_phrase;
84
+ }
85
+ if (enablePSTN === true && res?.pstn) {
86
+ if (res.pstn?.error?.code || res.pstn?.error?.message) {
87
+ roomInfo.pstn = {
88
+ number: '',
89
+ pin: '',
90
+ error: {
91
+ code: res.pstn?.error?.code,
92
+ message: res.pstn?.error?.message,
93
+ },
94
+ };
95
+ } else {
96
+ roomInfo.pstn = {
97
+ number: res.pstn?.number,
98
+ pin: res.pstn?.dtmf,
99
+ error: null,
100
+ };
101
+ }
102
+ }
103
+ logger.log(LogSource.Internals, 'CREATE_MEETING', 'Room created', {
104
+ isHost: true,
105
+ isSeparateHostLink: isSeparateHostLink ? true : false,
106
+ meetingTitle: roomTitle,
107
+ roomId: roomInfo?.roomId,
108
+ pstn: roomInfo?.pstn,
109
+ });
110
+ setRoomInfo(prev => {
111
+ return {
112
+ ...prev,
113
+ data: {
114
+ isHost: true,
115
+ isSeparateHostLink: isSeparateHostLink ? true : false,
116
+ meetingTitle: roomTitle,
117
+ roomId: roomInfo?.roomId,
118
+ pstn: roomInfo?.pstn,
119
+ },
120
+ };
121
+ });
122
+ SDKEvents.emit(
123
+ 'create',
124
+ roomInfo.roomId.host,
125
+ roomInfo.roomId.attendee,
126
+ roomInfo?.pstn,
127
+ );
128
+ } else {
129
+ throw new Error(`An error occurred in parsing the channel data.`);
130
+ }
131
+ } catch (error) {
132
+ const endReqTs = Date.now();
133
+ const latency = endReqTs - startReqTs;
78
134
  logger.error(
79
135
  LogSource.NetworkRest,
80
136
  'createChannel',
@@ -89,79 +145,5 @@ export default function useCreateRoom(): createRoomFun {
89
145
  );
90
146
  throw error;
91
147
  }
92
-
93
- if (res && res?.data && res?.data?.createChannel) {
94
- logger.log(
95
- LogSource.NetworkRest,
96
- 'createChannel',
97
- 'API createChannel. Channel created successfully',
98
- {
99
- responseData: res.data.createChannel,
100
- startReqTs,
101
- endReqTs,
102
- latency: latency,
103
- requestId,
104
- },
105
- );
106
- let roomInfo: Partial<RoomInfoContextInterface['data']> = {
107
- roomId: {
108
- attendee: '',
109
- },
110
- };
111
- if (res?.data?.createChannel?.passphrase?.view) {
112
- roomInfo.roomId.attendee = res.data.createChannel.passphrase.view;
113
- }
114
- if (res?.data?.createChannel?.passphrase?.host) {
115
- roomInfo.roomId.host = res.data.createChannel.passphrase.host;
116
- }
117
- if (enablePSTN === true && res?.data?.createChannel?.pstn) {
118
- if (
119
- res.data.createChannel.pstn?.error?.code ||
120
- res.data.createChannel.pstn?.error?.message
121
- ) {
122
- roomInfo.pstn = {
123
- number: '',
124
- pin: '',
125
- error: {
126
- code: res.data.createChannel.pstn?.error?.code,
127
- message: res.data.createChannel.pstn?.error?.message,
128
- },
129
- };
130
- } else {
131
- roomInfo.pstn = {
132
- number: res.data.createChannel.pstn?.number,
133
- pin: res.data.createChannel.pstn?.dtmf,
134
- error: null,
135
- };
136
- }
137
- }
138
- logger.log(LogSource.Internals, 'CREATE_MEETING', 'Room created', {
139
- isHost: true,
140
- isSeparateHostLink: isSeparateHostLink ? true : false,
141
- meetingTitle: roomTitle,
142
- roomId: roomInfo?.roomId,
143
- pstn: roomInfo?.pstn,
144
- });
145
- setRoomInfo(prev => {
146
- return {
147
- ...prev,
148
- data: {
149
- isHost: true,
150
- isSeparateHostLink: isSeparateHostLink ? true : false,
151
- meetingTitle: roomTitle,
152
- roomId: roomInfo?.roomId,
153
- pstn: roomInfo?.pstn,
154
- },
155
- };
156
- });
157
- SDKEvents.emit(
158
- 'create',
159
- roomInfo.roomId.host,
160
- roomInfo.roomId.attendee,
161
- roomInfo?.pstn,
162
- );
163
- } else {
164
- throw new Error(`An error occurred in parsing the channel data.`);
165
- }
166
148
  };
167
149
  }
@@ -1,6 +1,11 @@
1
1
  import {useContext} from 'react';
2
2
  import {useCustomization} from 'customization-implementation';
3
- import {useCaption, useContent, useRoomInfo} from 'customization-api';
3
+ import {
4
+ useCaption,
5
+ useContent,
6
+ useRoomInfo,
7
+ useSTTAPI,
8
+ } from 'customization-api';
4
9
  import {PropsContext, DispatchContext} from '../../agora-rn-uikit';
5
10
  import {useHistory} from '../components/Router';
6
11
  import {stopForegroundService} from '../subComponents/LocalEndCall';
@@ -18,6 +23,7 @@ const useEndCall = () => {
18
23
  } = useRoomInfo();
19
24
  const {authLogin} = useAuth();
20
25
  const {deleteChatUser} = useChatConfigure();
26
+ const {stop: stopSTTAPI} = useSTTAPI();
21
27
 
22
28
  const {rtcProps} = useContext(PropsContext);
23
29
  const {dispatch} = useContext(DispatchContext);
@@ -49,9 +55,16 @@ const useEndCall = () => {
49
55
  stopForegroundService();
50
56
  // stopping STT on call end,if only last user is remaining in call
51
57
  const usersInCall = Object.entries(defaultContent).filter(
52
- item => item[1].type === 'rtc',
58
+ item =>
59
+ item[1].type === 'rtc' && item[1].isHost === 'true' && !item[1].offline,
53
60
  );
54
- usersInCall.length === 1 && isSTTActive && stop();
61
+ if (usersInCall.length === 1 && isSTTActive) {
62
+ console.log('Stopping stt api as only one host is in the call');
63
+ stopSTTAPI().catch(error => {
64
+ console.log('Error stopping stt', error);
65
+ });
66
+ }
67
+
55
68
  // removing user from chat server
56
69
  if ($config.CHAT) {
57
70
  deleteChatUser();