agora-appbuilder-core 4.0.31 → 4.0.32

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agora-appbuilder-core",
3
- "version": "4.0.31",
3
+ "version": "4.0.32",
4
4
  "description": "React Native template for RTE app builder",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -288,7 +288,7 @@ const webSdk = {
288
288
  .pipe(concat('index.d.ts'))
289
289
  .pipe(
290
290
  replace(
291
- `declare module "${config.PRODUCT_ID}/index.wsdk"`,
291
+ 'declare module "index.wsdk"',
292
292
  `declare module "${PACKAGE_NAME}"`,
293
293
  ),
294
294
  )
@@ -1170,14 +1170,14 @@ export default class RtcEngine {
1170
1170
  `RTC [createClient] creating user and screen client with profile ${profile}`,
1171
1171
  );
1172
1172
  this.client = AgoraRTC.createClient({
1173
- codec: 'vp9',
1173
+ codec: 'vp8',
1174
1174
  mode:
1175
1175
  profile === ChannelProfileType.ChannelProfileLiveBroadcasting
1176
1176
  ? mode.live
1177
1177
  : mode.rtc,
1178
1178
  });
1179
1179
  this.screenClient = AgoraRTC.createClient({
1180
- codec: 'vp9',
1180
+ codec: 'vp8',
1181
1181
  mode:
1182
1182
  profile === ChannelProfileType.ChannelProfileLiveBroadcasting
1183
1183
  ? mode.live
@@ -76,8 +76,8 @@ const DefaultConfig = {
76
76
  CHAT_ORG_NAME: '',
77
77
  CHAT_APP_NAME: '',
78
78
  CHAT_URL: '',
79
- CLI_VERSION: '3.0.31',
80
- CORE_VERSION: '4.0.31',
79
+ CLI_VERSION: '3.0.32',
80
+ CORE_VERSION: '4.0.32',
81
81
  DISABLE_LANDSCAPE_MODE: false,
82
82
  STT_AUTO_START: false,
83
83
  CLOUD_RECORDING_AUTO_START: false,
@@ -55,6 +55,7 @@ import {
55
55
  useLayout,
56
56
  useRecording,
57
57
  useSidePanel,
58
+ useSpeechToText,
58
59
  } from 'customization-api';
59
60
  import {useVideoCall} from './useVideoCall';
60
61
  import {useScreenshare} from '../subComponents/screenshare/useScreenshare';
@@ -110,6 +111,7 @@ import {useModal} from '../utils/useModal';
110
111
  import ViewRecordingsModal from './recordings/ViewRecordingsModal';
111
112
  import {filterObject} from '../utils/index';
112
113
  import {useLanguage} from '../language/useLanguage';
114
+ import RecordingDeletePopup from './recordings/RecordingDeletePopup';
113
115
 
114
116
  export const useToggleWhiteboard = () => {
115
117
  const {
@@ -253,6 +255,12 @@ export const WhiteboardListener = () => {
253
255
  };
254
256
 
255
257
  const MoreButton = (props: {fields: ToolbarMoreButtonDefaultFields}) => {
258
+ //recording delete
259
+ const [isRecordingDeletePopupVisible, setRecordingDeletePopupVisible] =
260
+ React.useState<boolean>(false);
261
+ const [recordingIdToDelete, setRecordingIdToDelete] = useState(0);
262
+ //recording delete
263
+
256
264
  const {label} = useToolbarProps();
257
265
  const {data} = useRoomInfo();
258
266
  const noiseCancellationLabel = useString(toolbarItemNoiseCancellationText)();
@@ -312,7 +320,8 @@ const MoreButton = (props: {fields: ToolbarMoreButtonDefaultFields}) => {
312
320
  useVideoCall();
313
321
  const {isScreenshareActive, startScreenshare, stopScreenshare} =
314
322
  useScreenshare();
315
- const {isRecordingActive, startRecording, inProgress} = useRecording();
323
+ const {isRecordingActive, startRecording, inProgress, deleteRecording} =
324
+ useRecording();
316
325
  const {setChatType} = useChatUIControls();
317
326
  const actionMenuitems: ActionMenuItem[] = [];
318
327
 
@@ -849,6 +858,51 @@ const MoreButton = (props: {fields: ToolbarMoreButtonDefaultFields}) => {
849
858
  ?.filter(i => !isHidden(i))
850
859
  ?.sort(CustomToolbarSort);
851
860
 
861
+ const onRecordingDeleteConfirmation = () => {
862
+ setRecordingDeletePopupVisible(false);
863
+ deleteRecording(recordingIdToDelete)
864
+ .then(() => {
865
+ //To inform other user -> refresh the recording list
866
+ //in case recording list opened
867
+ events.send(
868
+ EventNames.RECORDING_DELETED,
869
+ JSON.stringify({recordingId: recordingIdToDelete}),
870
+ PersistanceLevel.None,
871
+ );
872
+ Toast.show({
873
+ leadingIconName: 'alert',
874
+ type: 'success',
875
+ text1: 'Recording has been deleted successfully.',
876
+ visibilityTime: 3000,
877
+ primaryBtn: null,
878
+ secondaryBtn: null,
879
+ leadingIcon: null,
880
+ });
881
+ setRecordingIdToDelete(0);
882
+ //to reopen recording list again
883
+ setTimeout(() => {
884
+ setVRModalOpen(true);
885
+ }, 3000);
886
+ })
887
+ .catch(() => {
888
+ Toast.show({
889
+ leadingIconName: 'alert',
890
+ type: 'error',
891
+ text1: 'Failed to delete the recording. Please try again later',
892
+ visibilityTime: 1000 * 10,
893
+ primaryBtn: null,
894
+ secondaryBtn: null,
895
+ leadingIcon: null,
896
+ });
897
+ setRecordingIdToDelete(0);
898
+ });
899
+ };
900
+
901
+ const onRecordingDeleteCancel = () => {
902
+ setRecordingIdToDelete(0);
903
+ toggleVRModal();
904
+ };
905
+
852
906
  return (
853
907
  <>
854
908
  <LanguageSelectorPopup
@@ -857,8 +911,27 @@ const MoreButton = (props: {fields: ToolbarMoreButtonDefaultFields}) => {
857
911
  onConfirm={onConfirm}
858
912
  isFirstTimePopupOpen={isFirstTimePopupOpen.current}
859
913
  />
860
- {$config.CLOUD_RECORDING && isHost && isWeb() && isVRModalOpen && (
861
- <ViewRecordingsModal setModalOpen={setVRModalOpen} />
914
+ {$config.CLOUD_RECORDING && isHost && isWeb() && (
915
+ <>
916
+ <RecordingDeletePopup
917
+ modalVisible={isRecordingDeletePopupVisible}
918
+ setModalVisible={setRecordingDeletePopupVisible}
919
+ onConfirm={onRecordingDeleteConfirmation}
920
+ onCancel={onRecordingDeleteCancel}
921
+ />
922
+ {isVRModalOpen ? (
923
+ <ViewRecordingsModal
924
+ setModalOpen={setVRModalOpen}
925
+ onDeleteAction={id => {
926
+ setRecordingIdToDelete(id);
927
+ toggleVRModal();
928
+ setRecordingDeletePopupVisible(true);
929
+ }}
930
+ />
931
+ ) : (
932
+ <></>
933
+ )}
934
+ </>
862
935
  )}
863
936
  <ActionMenu
864
937
  containerStyle={globalWidth < 720 ? {width: 180} : {width: 260}}
@@ -1141,6 +1214,7 @@ const Controls = (props: ControlsProps) => {
1141
1214
  }>(sttSpokenLanguageToastSubHeading);
1142
1215
 
1143
1216
  const {sttLanguage, isSTTActive} = useRoomInfo();
1217
+ const {addStreamMessageListener} = useSpeechToText();
1144
1218
 
1145
1219
  React.useEffect(() => {
1146
1220
  defaultContentRef.current = defaultContent;
@@ -1212,6 +1286,8 @@ const Controls = (props: ControlsProps) => {
1212
1286
  },
1213
1287
  ];
1214
1288
  });
1289
+ // start listening to stream Message callback
1290
+ addStreamMessageListener();
1215
1291
  }, [sttLanguage]);
1216
1292
 
1217
1293
  React.useEffect(() => {
@@ -268,7 +268,8 @@ const EventsConfigure: React.FC<Props> = ({
268
268
  }, [permissionStatus]);
269
269
 
270
270
  const {hasUserJoinedRTM, isInitialQueueCompleted} = useContext(ChatContext);
271
- const {startSpeechToText} = useSpeechToText();
271
+ const {startSpeechToText, addStreamMessageListener} = useSpeechToText();
272
+
272
273
  //auto start stt
273
274
  useEffect(() => {
274
275
  if (
@@ -285,6 +286,10 @@ const EventsConfigure: React.FC<Props> = ({
285
286
  logger.log(LogSource.Internals, 'STT', 'STT_AUTO_START triggered', {
286
287
  uidWhoTriggered: localUid,
287
288
  });
289
+
290
+ // add stream message callback listener
291
+ addStreamMessageListener();
292
+
288
293
  //start with default language
289
294
  startSpeechToText(['en-US'])
290
295
  .then(() => {
@@ -0,0 +1,115 @@
1
+ import React, {SetStateAction} from 'react';
2
+ import {StyleSheet, Text, View} from 'react-native';
3
+ import Spacer from '../../atoms/Spacer';
4
+ import Popup from '../../atoms/Popup';
5
+ import TertiaryButton from '../../atoms/TertiaryButton';
6
+ import PrimaryButton from '../../atoms/PrimaryButton';
7
+ import ThemeConfig from '../../theme';
8
+ import {useIsDesktop} from '../../utils/common';
9
+ import {useString} from '../../utils/useString';
10
+ import {cancelText} from '../../language/default-labels/commonLabels';
11
+
12
+ interface RecordingDeletePopupProps {
13
+ modalVisible: boolean;
14
+ setModalVisible: React.Dispatch<SetStateAction<boolean>>;
15
+ onConfirm: () => void;
16
+ onCancel: () => void;
17
+ }
18
+ const RecordingDeletePopup = (props: RecordingDeletePopupProps) => {
19
+ const isDesktop = useIsDesktop()('popup');
20
+ const heading = 'DELETE';
21
+ const subheading =
22
+ "Are you sure want to delete the recording? This action can't be undone.";
23
+
24
+ const cancelBtnLabel = useString(cancelText)();
25
+ const deleteBtnLabel = 'DELETE';
26
+ return (
27
+ <Popup
28
+ modalVisible={props.modalVisible}
29
+ setModalVisible={props.setModalVisible}
30
+ showCloseIcon={false}
31
+ contentContainerStyle={styles.contentContainer}>
32
+ <Text style={styles.heading}>{heading}</Text>
33
+ <Spacer size={8} />
34
+ <Text style={styles.subHeading}>{subheading}</Text>
35
+ <Spacer size={32} />
36
+ <View style={isDesktop ? styles.btnContainer : styles.btnContainerMobile}>
37
+ <View style={isDesktop && {flex: 1}}>
38
+ <TertiaryButton
39
+ containerStyle={{
40
+ width: '100%',
41
+ height: 48,
42
+ paddingVertical: 12,
43
+ paddingHorizontal: 12,
44
+ borderRadius: ThemeConfig.BorderRadius.medium,
45
+ }}
46
+ textStyle={styles.btnText}
47
+ text={cancelBtnLabel}
48
+ onPress={() => {
49
+ props?.setModalVisible(false);
50
+ props?.onCancel();
51
+ }}
52
+ />
53
+ </View>
54
+ <Spacer
55
+ size={isDesktop ? 10 : 20}
56
+ horizontal={isDesktop ? true : false}
57
+ />
58
+ <View style={isDesktop && {flex: 1}}>
59
+ <PrimaryButton
60
+ containerStyle={{
61
+ minWidth: 'auto',
62
+ width: '100%',
63
+ borderRadius: ThemeConfig.BorderRadius.medium,
64
+ height: 48,
65
+ backgroundColor: $config.SEMANTIC_ERROR,
66
+ paddingVertical: 12,
67
+ paddingHorizontal: 12,
68
+ }}
69
+ textStyle={styles.btnText}
70
+ text={deleteBtnLabel}
71
+ onPress={() => {
72
+ props?.setModalVisible(false);
73
+ props?.onConfirm();
74
+ }}
75
+ />
76
+ </View>
77
+ </View>
78
+ </Popup>
79
+ );
80
+ };
81
+
82
+ export default RecordingDeletePopup;
83
+
84
+ const styles = StyleSheet.create({
85
+ btnContainer: {
86
+ flex: 1,
87
+ flexDirection: 'row',
88
+ justifyContent: 'center',
89
+ alignItems: 'center',
90
+ },
91
+ contentContainer: {
92
+ padding: 24,
93
+ maxWidth: 342,
94
+ },
95
+ btnText: {
96
+ fontWeight: '600',
97
+ fontSize: 16,
98
+ lineHeight: 24,
99
+ },
100
+ btnContainerMobile: {
101
+ flexDirection: 'column-reverse',
102
+ },
103
+ heading: {
104
+ fontFamily: ThemeConfig.FontFamily.sansPro,
105
+ fontWeight: '600',
106
+ fontSize: 22,
107
+ color: $config.SEMANTIC_ERROR,
108
+ },
109
+ subHeading: {
110
+ fontFamily: ThemeConfig.FontFamily.sansPro,
111
+ fontWeight: '400',
112
+ fontSize: 14,
113
+ color: $config.FONT_COLOR,
114
+ },
115
+ });
@@ -3,8 +3,10 @@ import {View, Text} from 'react-native';
3
3
  import {style} from './style';
4
4
  import {RTableHeader, RTableBody, RTableFooter} from './recording-table';
5
5
  import {useRecording} from '../../subComponents/recording/useRecording';
6
+ import events from '../../rtm-events-api';
7
+ import {EventNames} from '../../rtm-events';
6
8
 
7
- function RecordingsDateTable() {
9
+ function RecordingsDateTable(props) {
8
10
  const [state, setState] = React.useState({
9
11
  status: 'idle',
10
12
  data: {
@@ -21,11 +23,24 @@ function RecordingsDateTable() {
21
23
 
22
24
  const {fetchRecordings} = useRecording();
23
25
 
24
- const [currentPage, setCurrentPage] = useState(1);
26
+ const defaultPageNumber = 1;
27
+ const [currentPage, setCurrentPage] = useState(defaultPageNumber);
28
+
29
+ const onRecordingDeleteCallback = () => {
30
+ setCurrentPage(defaultPageNumber);
31
+ getRecordings(defaultPageNumber);
32
+ };
25
33
 
26
34
  useEffect(() => {
35
+ events.on(EventNames.RECORDING_DELETED, onRecordingDeleteCallback);
36
+ return () => {
37
+ events.off(EventNames.RECORDING_DELETED, onRecordingDeleteCallback);
38
+ };
39
+ }, []);
40
+
41
+ const getRecordings = pageNumber => {
27
42
  setState(prev => ({...prev, status: 'pending'}));
28
- fetchRecordings(currentPage).then(
43
+ fetchRecordings(pageNumber).then(
29
44
  response =>
30
45
  setState(prev => ({
31
46
  ...prev,
@@ -37,7 +52,11 @@ function RecordingsDateTable() {
37
52
  })),
38
53
  error => setState(prev => ({...prev, status: 'rejected', error})),
39
54
  );
40
- }, [fetchRecordings, currentPage]);
55
+ };
56
+
57
+ useEffect(() => {
58
+ getRecordings(currentPage);
59
+ }, [currentPage]);
41
60
 
42
61
  if (status === 'rejected') {
43
62
  return (
@@ -49,7 +68,11 @@ function RecordingsDateTable() {
49
68
  return (
50
69
  <View style={style.ttable}>
51
70
  <RTableHeader />
52
- <RTableBody status={status} recordings={recordings} />
71
+ <RTableBody
72
+ status={status}
73
+ recordings={recordings}
74
+ onDeleteAction={props?.onDeleteAction}
75
+ />
53
76
  <RTableFooter
54
77
  currentPage={currentPage}
55
78
  setCurrentPage={setCurrentPage}
@@ -8,6 +8,7 @@ import {recordingModalTitleIntn} from '../../language/default-labels/videoCallSc
8
8
 
9
9
  interface ViewRecordingsModalProps {
10
10
  setModalOpen: Dispatch<SetStateAction<boolean>>;
11
+ onDeleteAction: (recordingId: number) => void;
11
12
  }
12
13
 
13
14
  // interface FetchRecordingResponse {
@@ -31,7 +32,7 @@ interface ViewRecordingsModalProps {
31
32
  // error: null;
32
33
  // }
33
34
  export default function ViewRecordingsModal(props: ViewRecordingsModalProps) {
34
- const {setModalOpen} = props;
35
+ const {setModalOpen, onDeleteAction} = props;
35
36
 
36
37
  const recordingModalTitle = useString(recordingModalTitleIntn)();
37
38
 
@@ -44,7 +45,7 @@ export default function ViewRecordingsModal(props: ViewRecordingsModalProps) {
44
45
  cancelable={false}
45
46
  contentContainerStyle={style.mContainer}>
46
47
  <View style={style.mbody}>
47
- <RecordingsDateTable />
48
+ <RecordingsDateTable onDeleteAction={onDeleteAction} />
48
49
  </View>
49
50
  </RecordingsModal>
50
51
  );
@@ -32,7 +32,7 @@ function RTableHeader() {
32
32
  );
33
33
  }
34
34
 
35
- function RTableBody({status, recordings}) {
35
+ function RTableBody({status, recordings, onDeleteAction}) {
36
36
  const renderTableBodyContent = () => {
37
37
  if (status === 'idle' || status === 'pending') {
38
38
  return <Loading background="transparent" text="Fetching recordings.." />;
@@ -104,97 +104,121 @@ function RTableBody({status, recordings}) {
104
104
  </Text>
105
105
  </View>
106
106
  ) : item?.download_url?.length > 0 ? (
107
- item?.download_url?.map((link: string, i: number) => (
108
- <View style={style.tactions} key={i}>
109
- <View>
110
- <IconButtonWithToolTip
111
- hoverEffect={true}
112
- hoverEffectStyle={style.iconButtonHoverEffect}
113
- containerStyle={style.iconButton}
114
- iconProps={{
115
- name: 'download',
116
- iconType: 'plain',
117
- iconSize: 20,
118
- tintColor: `${$config.SECONDARY_ACTION_COLOR}`,
119
- }}
120
- onPress={() => {
121
- downloadRecording(link);
122
- }}
123
- />
124
- </View>
125
- <View style={style.pl15}>
126
- <IconButtonWithToolTip
127
- // placement="bottom"
128
- // toolTipMessage="Share"
129
- hoverEffect={true}
130
- hoverEffectStyle={style.iconButtonHoverEffect}
131
- containerStyle={style.iconButton}
132
- iconProps={{
133
- name: 'link-share',
134
- iconType: 'plain',
135
- iconSize: 20,
136
- tintColor: `${$config.SECONDARY_ACTION_COLOR}`,
137
- }}
138
- onPress={async () => {
139
- if (await Linking.canOpenURL(link)) {
140
- await Linking.openURL(link);
141
- }
142
- }}
143
- />
144
- </View>
145
- <View style={[style.pl15]}>
146
- <View>
147
- <Tooltip
148
- isClickable
149
- placement="left"
150
- toolTipMessage="Link Copied"
151
- onPress={() => {
152
- Clipboard.setString(link);
153
- }}
154
- toolTipIcon={
155
- <>
156
- <ImageIcon
157
- iconType="plain"
158
- name="tick-fill"
159
- tintColor={$config.SEMANTIC_SUCCESS}
160
- iconSize={20}
161
- />
162
- <Spacer size={8} horizontal={true} />
163
- </>
164
- }
165
- fontSize={12}
166
- renderContent={() => {
167
- return (
168
- <PlatformWrapper>
169
- {(isHovered: boolean) => (
170
- <TouchableOpacity
171
- style={[
172
- isHovered
173
- ? style.iconButtonHoverEffect
174
- : {},
175
- style.iconShareLink,
176
- ]}
177
- onPress={() => {
178
- Clipboard.setString(link);
179
- }}>
180
- <ImageIcon
181
- iconType="plain"
182
- name="copy-link"
183
- iconSize={20}
184
- tintColor={
185
- $config.SECONDARY_ACTION_COLOR
186
- }
187
- />
188
- </TouchableOpacity>
189
- )}
190
- </PlatformWrapper>
191
- );
192
- }}
193
- />
107
+ <View style={style.tactions}>
108
+ <View>
109
+ {item?.download_url?.map((link: string, i: number) => (
110
+ <View
111
+ style={[
112
+ style.tactions,
113
+ //if recording contains multiple parts then we need to add some space each row
114
+ i >= 1 ? {marginTop: 8} : {},
115
+ ]}>
116
+ <View>
117
+ <IconButtonWithToolTip
118
+ hoverEffect={true}
119
+ hoverEffectStyle={style.iconButtonHoverEffect}
120
+ containerStyle={style.iconButton}
121
+ iconProps={{
122
+ name: 'download',
123
+ iconType: 'plain',
124
+ iconSize: 20,
125
+ tintColor: `${$config.SECONDARY_ACTION_COLOR}`,
126
+ }}
127
+ onPress={() => {
128
+ downloadRecording(link);
129
+ }}
130
+ />
131
+ </View>
132
+ <View style={style.pl10}>
133
+ <IconButtonWithToolTip
134
+ // placement="bottom"
135
+ // toolTipMessage="Share"
136
+ hoverEffect={true}
137
+ hoverEffectStyle={style.iconButtonHoverEffect}
138
+ containerStyle={style.iconButton}
139
+ iconProps={{
140
+ name: 'link-share',
141
+ iconType: 'plain',
142
+ iconSize: 20,
143
+ tintColor: `${$config.SECONDARY_ACTION_COLOR}`,
144
+ }}
145
+ onPress={async () => {
146
+ if (await Linking.canOpenURL(link)) {
147
+ await Linking.openURL(link);
148
+ }
149
+ }}
150
+ />
151
+ </View>
152
+ <View style={[style.pl10]}>
153
+ <Tooltip
154
+ isClickable
155
+ placement="left"
156
+ toolTipMessage="Link Copied"
157
+ onPress={() => {
158
+ Clipboard.setString(link);
159
+ }}
160
+ toolTipIcon={
161
+ <>
162
+ <ImageIcon
163
+ iconType="plain"
164
+ name="tick-fill"
165
+ tintColor={$config.SEMANTIC_SUCCESS}
166
+ iconSize={20}
167
+ />
168
+ <Spacer size={8} horizontal={true} />
169
+ </>
170
+ }
171
+ fontSize={12}
172
+ renderContent={() => {
173
+ return (
174
+ <PlatformWrapper>
175
+ {(isHovered: boolean) => (
176
+ <TouchableOpacity
177
+ style={[
178
+ isHovered
179
+ ? style.iconButtonHoverEffect
180
+ : {},
181
+ style.iconShareLink,
182
+ ]}
183
+ onPress={() => {
184
+ Clipboard.setString(link);
185
+ }}>
186
+ <ImageIcon
187
+ iconType="plain"
188
+ name="copy-link"
189
+ iconSize={20}
190
+ tintColor={
191
+ $config.SECONDARY_ACTION_COLOR
192
+ }
193
+ />
194
+ </TouchableOpacity>
195
+ )}
196
+ </PlatformWrapper>
197
+ );
198
+ }}
199
+ />
200
+ </View>
194
201
  </View>
195
- </View>
202
+ ))}
196
203
  </View>
197
- ))
204
+ <View style={[style.pl10]}>
205
+ <IconButtonWithToolTip
206
+ hoverEffect={true}
207
+ hoverEffectStyle={style.iconButtonHoverEffect}
208
+ containerStyle={style.iconButton}
209
+ iconProps={{
210
+ name: 'delete',
211
+ iconType: 'plain',
212
+ iconSize: 20,
213
+ tintColor: `${$config.SEMANTIC_ERROR}`,
214
+ }}
215
+ onPress={() => {
216
+ //show confirmation popup
217
+ onDeleteAction && onDeleteAction(item.id);
218
+ }}
219
+ />
220
+ </View>
221
+ </View>
198
222
  ) : (
199
223
  <View style={(style.tactions, {marginTop: 0})}>
200
224
  <Text style={style.placeHolder}>No recordings found</Text>
@@ -13,7 +13,7 @@ export const style = StyleSheet.create({
13
13
  flexShrink: 0,
14
14
  // width: 620,
15
15
  width: '100%',
16
- maxWidth: 620,
16
+ maxWidth: 680,
17
17
  minWidth: 340,
18
18
  height: 620,
19
19
  maxHeight: 620,
@@ -98,9 +98,8 @@ export const style = StyleSheet.create({
98
98
  },
99
99
  td: {
100
100
  flex: 1,
101
- alignSelf: 'flex-start',
101
+ alignSelf: 'center',
102
102
  justifyContent: 'center',
103
- paddingHorizontal: 12,
104
103
  // height: 100,
105
104
  gap: 10,
106
105
  },
@@ -127,7 +126,6 @@ export const style = StyleSheet.create({
127
126
  tactions: {
128
127
  display: 'flex',
129
128
  flexDirection: 'row',
130
- marginTop: -8,
131
129
  },
132
130
  tlink: {
133
131
  color: $config.PRIMARY_ACTION_BRAND_COLOR,
@@ -102,7 +102,8 @@ type LogType = {
102
102
  | 'whiteboard_screenshot'
103
103
  | 'recording_start'
104
104
  | 'recording_stop'
105
- | 'recordings_get';
105
+ | 'recordings_get'
106
+ | 'recording_delete';
106
107
  [LogSource.Events]: 'CUSTOM_EVENTS' | 'RTM_EVENTS';
107
108
  [LogSource.CustomizationAPI]: 'Log';
108
109
  [LogSource.SDK]: 'Log' | 'Event';
@@ -34,6 +34,7 @@ import {
34
34
  ToolbarItem,
35
35
  ToolbarItemHide,
36
36
  ToolbarItemLabel,
37
+ useSpeechToText,
37
38
  } from 'customization-api';
38
39
  import LayoutIconButton from '../../subComponents/LayoutIconButton';
39
40
  import CaptionIcon from '../../../src/subComponents/caption/CaptionIcon';
@@ -259,6 +260,7 @@ const ActionSheetContent = props => {
259
260
  const {defaultContent} = useContent();
260
261
  const {waitingRoomUids} = useWaitingRoomContext();
261
262
  const defaultContentRef = React.useRef(defaultContent);
263
+ const {addStreamMessageListener} = useSpeechToText();
262
264
 
263
265
  React.useEffect(() => {
264
266
  defaultContentRef.current = defaultContent;
@@ -335,6 +337,8 @@ const ActionSheetContent = props => {
335
337
  },
336
338
  ];
337
339
  });
340
+ // start listening to stream Message callback
341
+ addStreamMessageListener();
338
342
  }, [sttLanguage]);
339
343
 
340
344
  const isLiveStream = $config.EVENT_MODE && !$config.AUDIO_ROOM;
@@ -38,7 +38,7 @@ const WAITING_ROOM_STATUS_UPDATE = 'WAITING_ROOM_STATUS_UPDATE';
38
38
  const WHITEBOARD_ACTIVE = 'WHITEBOARD_ACTIVE';
39
39
  const BOARD_COLOR_CHANGED = 'BOARD_COLOR_CHANGED';
40
40
  const WHITEBOARD_LAST_IMAGE_UPLOAD_POSITION = 'WHITEBOARD_L_I_U_P';
41
-
41
+ const RECORDING_DELETED = 'RECORDING_DELETED';
42
42
  const EventNames = {
43
43
  RECORDING_STATE_ATTRIBUTE,
44
44
  RECORDING_STARTED_BY_ATTRIBUTE,
@@ -58,6 +58,7 @@ const EventNames = {
58
58
  WHITEBOARD_ACTIVE,
59
59
  BOARD_COLOR_CHANGED,
60
60
  WHITEBOARD_LAST_IMAGE_UPLOAD_POSITION,
61
+ RECORDING_DELETED,
61
62
  };
62
63
  /** ***** EVENT NAMES ENDS ***** */
63
64
 
@@ -10,9 +10,16 @@ import hexadecimalTransparency from '../../utils/hexadecimalTransparency';
10
10
  import {useString} from '../../utils/useString';
11
11
  import {sttSettingSpokenLanguageText} from '../../language/default-labels/videoCallScreenLabels';
12
12
 
13
- type WebStreamMessageArgs = [number, Uint8Array];
14
- type NativeStreamMessageArgs = [{}, number, number, Uint8Array, number, number];
15
- type StreamMessageArgs = WebStreamMessageArgs | NativeStreamMessageArgs;
13
+ export type WebStreamMessageArgs = [number, Uint8Array];
14
+ export type NativeStreamMessageArgs = [
15
+ {},
16
+ number,
17
+ number,
18
+ Uint8Array,
19
+ number,
20
+ number,
21
+ ];
22
+ export type StreamMessageArgs = WebStreamMessageArgs | NativeStreamMessageArgs;
16
23
 
17
24
  interface CaptionProps {
18
25
  captionTextStyle?: TextStyle;
@@ -76,6 +76,7 @@ export interface RecordingContextInterface {
76
76
  isRecordingActive: boolean;
77
77
  inProgress: boolean;
78
78
  fetchRecordings?: (page: number) => Promise<RecordingsData>;
79
+ deleteRecording?: (id: number) => Promise<boolean>;
79
80
  }
80
81
 
81
82
  const RecordingContext = createContext<RecordingContextInterface>({
@@ -625,6 +626,85 @@ const RecordingProvider = (props: RecordingProviderProps) => {
625
626
  [roomId?.host, store.token],
626
627
  );
627
628
 
629
+ const deleteRecording = useCallback(
630
+ (recordingId: number) => {
631
+ const requestId = getUniqueID();
632
+ const startReqTs = Date.now();
633
+ logger.debug(
634
+ LogSource.NetworkRest,
635
+ 'recording_delete',
636
+ 'Trying to delete recording recording id:' + recordingId,
637
+ {
638
+ recordingId,
639
+ },
640
+ );
641
+ return fetch(
642
+ `${$config.BACKEND_ENDPOINT}/v1/recording/${recordingId}?passphrase=${roomId?.host}`,
643
+ {
644
+ method: 'DELETE',
645
+ headers: {
646
+ 'Content-Type': 'application/json',
647
+ authorization: store.token ? `Bearer ${store.token}` : '',
648
+ 'X-Request-Id': requestId,
649
+ 'X-Session-Id': logger.getSessionId(),
650
+ },
651
+ },
652
+ )
653
+ .then(async response => {
654
+ const endReqTs = Date.now();
655
+ if (response.ok) {
656
+ logger.debug(
657
+ LogSource.NetworkRest,
658
+ 'recording_delete',
659
+ 'delete recording successfull',
660
+ response,
661
+ {
662
+ recordingId,
663
+ startReqTs,
664
+ endReqTs,
665
+ latency: endReqTs - startReqTs,
666
+ requestId,
667
+ },
668
+ );
669
+ return Promise.resolve(true);
670
+ } else {
671
+ logger.error(
672
+ LogSource.NetworkRest,
673
+ 'recording_delete',
674
+ 'Error while deleting recording',
675
+ response,
676
+ {
677
+ recordingId,
678
+ startReqTs,
679
+ endReqTs,
680
+ latency: endReqTs - startReqTs,
681
+ requestId,
682
+ },
683
+ );
684
+ return Promise.reject(false);
685
+ }
686
+ })
687
+ .catch(error => {
688
+ const endReqTs = Date.now();
689
+ logger.error(
690
+ LogSource.NetworkRest,
691
+ 'recording_delete',
692
+ 'Error while deleting recording',
693
+ error,
694
+ {
695
+ recordingId,
696
+ startReqTs,
697
+ endReqTs,
698
+ latency: endReqTs - startReqTs,
699
+ requestId,
700
+ },
701
+ );
702
+ return Promise.reject(false);
703
+ });
704
+ },
705
+ [roomId?.host, store.token],
706
+ );
707
+
628
708
  // Events
629
709
  useEffect(() => {
630
710
  events.on(EventNames.RECORDING_STATE_ATTRIBUTE, data => {
@@ -924,6 +1004,7 @@ const RecordingProvider = (props: RecordingProviderProps) => {
924
1004
  stopRecording,
925
1005
  isRecordingActive,
926
1006
  fetchRecordings,
1007
+ deleteRecording,
927
1008
  }}>
928
1009
  {props.children}
929
1010
  </RecordingContext.Provider>
@@ -3,12 +3,20 @@ import {
3
3
  SidePanelType,
4
4
  customEvents,
5
5
  useContent,
6
+ useRtc,
6
7
  useSTTAPI,
7
8
  useSidePanel,
8
9
  } from 'customization-api';
9
10
  import useTranscriptDownload from '../subComponents/caption/useTranscriptDownload';
10
11
  import {useCaption} from '../subComponents/caption/useCaption';
11
12
  import {LanguageType} from '../subComponents/caption/utils';
13
+ import useStreamMessageUtils from '../subComponents/caption/useStreamMessageUtils';
14
+ import {
15
+ NativeStreamMessageArgs,
16
+ StreamMessageArgs,
17
+ WebStreamMessageArgs,
18
+ } from '../subComponents/caption/Caption';
19
+ import {isWebInternal} from './common';
12
20
 
13
21
  const useSpeechToText = () => {
14
22
  const {
@@ -18,6 +26,8 @@ const useSpeechToText = () => {
18
26
  captionObj: captionData,
19
27
  prevSpeakerRef,
20
28
  activeSpeakerRef,
29
+ isSTTListenerAdded,
30
+ setIsSTTListenerAdded,
21
31
  } = useCaption();
22
32
  const {setSidePanel} = useSidePanel();
23
33
 
@@ -26,6 +36,8 @@ const useSpeechToText = () => {
26
36
 
27
37
  const isAuthorizedSTTUserRef = useRef(isAuthorizedSTTUser);
28
38
  const defaultContentRef = useRef(defaultContent);
39
+ const {RtcEngineUnsafe} = useRtc();
40
+ const {streamMessageCallback} = useStreamMessageUtils();
29
41
 
30
42
  const showTranscriptPanel = (show: boolean) => {
31
43
  show
@@ -40,7 +52,7 @@ const useSpeechToText = () => {
40
52
  useEffect(() => {
41
53
  if (!$config.ENABLE_STT) {
42
54
  //throw new Error('Speech To Text is not enabled');
43
- console.error('Speech To Text is not enabled')
55
+ console.error('Speech To Text is not enabled');
44
56
  }
45
57
  }, []);
46
58
 
@@ -76,6 +88,24 @@ const useSpeechToText = () => {
76
88
  return await stop();
77
89
  };
78
90
 
91
+ const addStreamMessageListener = () => {
92
+ !isSTTListenerAdded &&
93
+ RtcEngineUnsafe.addListener(
94
+ 'onStreamMessage',
95
+ handleStreamMessageCallback,
96
+ );
97
+ };
98
+ const handleStreamMessageCallback = (...args: StreamMessageArgs) => {
99
+ setIsSTTListenerAdded(true);
100
+ if (isWebInternal()) {
101
+ const [uid, data] = args as WebStreamMessageArgs;
102
+ streamMessageCallback([uid, data]);
103
+ } else {
104
+ const [, uid, , data] = args as NativeStreamMessageArgs;
105
+ streamMessageCallback([uid, data]);
106
+ }
107
+ };
108
+
79
109
  const changeSpeakingLanguage = async (language: LanguageType[]) => {
80
110
  if (!isAuthorizedSTTUserRef.current) {
81
111
  throw new Error('Invalid user');
@@ -96,6 +126,7 @@ const useSpeechToText = () => {
96
126
  showCaptionPanel,
97
127
  transcriptData,
98
128
  captionData,
129
+ addStreamMessageListener,
99
130
  }
100
131
  : {};
101
132
  };