agora-appbuilder-core 4.1.8-beta.1 → 4.1.8-beta.10

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 (24) hide show
  1. package/package.json +2 -2
  2. package/template/agora-rn-uikit/src/Contexts/PropsContext.tsx +1 -1
  3. package/template/agora-rn-uikit/src/Rtc/Join.tsx +18 -9
  4. package/template/bridge/rtc/webNg/RtcEngine.ts +2 -2
  5. package/template/defaultConfig.js +4 -3
  6. package/template/global.d.ts +1 -0
  7. package/template/package.json +1 -1
  8. package/template/src/components/Controls.tsx +17 -15
  9. package/template/src/components/common/GenericPopup.tsx +0 -1
  10. package/template/src/components/common/data-table.tsx +42 -15
  11. package/template/src/components/controls/useControlPermissionMatrix.tsx +7 -4
  12. package/template/src/components/recordings/RecordingItemRow.tsx +289 -0
  13. package/template/src/components/recordings/RecordingsDateTable.tsx +99 -25
  14. package/template/src/components/recordings/TextTrackItemRow.tsx +120 -0
  15. package/template/src/components/room-info/useRoomInfo.tsx +1 -0
  16. package/template/src/components/{stt-transcript/STTTranscriptTable.tsx → text-tracks/TextTracksTable.tsx} +61 -50
  17. package/template/src/components/{stt-transcript/ViewSTTTranscriptModal.tsx → text-tracks/ViewTextTracksModal.tsx} +7 -7
  18. package/template/src/components/text-tracks/useFetchSTTTranscript.tsx +262 -0
  19. package/template/src/language/default-labels/videoCallScreenLabels.ts +7 -7
  20. package/template/src/pages/VideoCall.tsx +1 -1
  21. package/template/src/subComponents/recording/useRecording.tsx +19 -4
  22. package/template/src/utils/useCreateRoom.ts +1 -1
  23. package/template/src/utils/useJoinRoom.ts +5 -0
  24. package/template/src/components/stt-transcript/useFetchSTTTranscript.tsx +0 -193
@@ -0,0 +1,262 @@
1
+ import {useState, useCallback, useEffect, useContext} from 'react';
2
+ import StorageContext from '../StorageContext';
3
+ import {useRoomInfo} from 'customization-api';
4
+ import getUniqueID from '../../utils/getUniqueID';
5
+ import {logger, LogSource} from '../../logger/AppBuilderLogger';
6
+
7
+ export interface FetchSTTTranscriptResponse {
8
+ pagination: {limit: number; total: number; page: number};
9
+ stts: {
10
+ id: string;
11
+ download_url: string[];
12
+ title: string;
13
+ product_name: string;
14
+ status: 'COMPLETED' | 'STARTED' | 'INPROGRESS' | 'STOPPING';
15
+ created_at: string;
16
+ ended_at: string;
17
+ }[];
18
+ }
19
+
20
+ export type APIStatus = 'idle' | 'pending' | 'resolved' | 'rejected';
21
+
22
+ export function useFetchSTTTranscript() {
23
+ const {
24
+ data: {roomId},
25
+ } = useRoomInfo();
26
+ const {store} = useContext(StorageContext);
27
+
28
+ const [currentPage, setCurrentPage] = useState(1);
29
+
30
+ const [sttState, setSttState] = useState<{
31
+ status: APIStatus;
32
+ data: {
33
+ stts: FetchSTTTranscriptResponse['stts'];
34
+ pagination: FetchSTTTranscriptResponse['pagination'];
35
+ };
36
+ error: Error | null;
37
+ }>({
38
+ status: 'idle',
39
+ data: {stts: [], pagination: {total: 0, limit: 10, page: 1}},
40
+ error: null,
41
+ });
42
+
43
+ //–– by‐recording state ––
44
+ const [sttRecState, setSttRecState] = useState<{
45
+ status: APIStatus;
46
+ data: {stts: FetchSTTTranscriptResponse['stts']};
47
+ error: Error | null;
48
+ }>({
49
+ status: 'idle',
50
+ data: {
51
+ stts: [],
52
+ },
53
+ error: null,
54
+ });
55
+
56
+ const getSTTs = useCallback(
57
+ (page: number) => {
58
+ setSttState(s => ({...s, status: 'pending', error: null}));
59
+ const reqId = getUniqueID();
60
+ const start = Date.now();
61
+
62
+ fetch(`${$config.BACKEND_ENDPOINT}/v1/stt-transcript`, {
63
+ method: 'POST',
64
+ headers: {
65
+ 'Content-Type': 'application/json',
66
+ authorization: store.token ? `Bearer ${store.token}` : '',
67
+ 'X-Request-Id': reqId,
68
+ 'X-Session-Id': logger.getSessionId(),
69
+ },
70
+ body: JSON.stringify({
71
+ passphrase: roomId.host,
72
+ limit: 10,
73
+ page,
74
+ }),
75
+ })
76
+ .then(async res => {
77
+ const json = await res.json();
78
+ const end = Date.now();
79
+ if (!res.ok) {
80
+ logger.error(
81
+ LogSource.NetworkRest,
82
+ 'stt-transcript',
83
+ 'Fetch STT transcripts failed',
84
+ {
85
+ json,
86
+ start,
87
+ end,
88
+ latency: end - start,
89
+ requestId: reqId,
90
+ },
91
+ );
92
+ throw new Error(json?.error?.message || res.statusText);
93
+ }
94
+ logger.debug(
95
+ LogSource.NetworkRest,
96
+ 'stt-transcript',
97
+ 'Fetch STT transcripts succeeded',
98
+ {
99
+ json,
100
+ start,
101
+ end,
102
+ latency: end - start,
103
+ requestId: reqId,
104
+ },
105
+ );
106
+ return json as FetchSTTTranscriptResponse;
107
+ })
108
+ .then(({stts = [], pagination = {total: 0, limit: 10, page}}) => {
109
+ setSttState({
110
+ status: 'resolved',
111
+ data: {stts, pagination},
112
+ error: null,
113
+ });
114
+ })
115
+ .catch(err => {
116
+ setSttState(s => ({...s, status: 'rejected', error: err}));
117
+ });
118
+ },
119
+ [roomId.host, store.token],
120
+ );
121
+
122
+ // Delete stts
123
+ const deleteTranscript = useCallback(
124
+ async (id: string) => {
125
+ const reqId = getUniqueID();
126
+ const start = Date.now();
127
+
128
+ const res = await fetch(
129
+ `${
130
+ $config.BACKEND_ENDPOINT
131
+ }/v1/stt-transcript/${id}?passphrase=${encodeURIComponent(
132
+ roomId.host,
133
+ )}`,
134
+ {
135
+ method: 'DELETE',
136
+ headers: {
137
+ 'Content-Type': 'application/json',
138
+ authorization: store.token ? `Bearer ${store.token}` : '',
139
+ 'X-Request-Id': reqId,
140
+ 'X-Session-Id': logger.getSessionId(),
141
+ },
142
+ },
143
+ );
144
+ const end = Date.now();
145
+
146
+ if (!res.ok) {
147
+ logger.error(
148
+ LogSource.NetworkRest,
149
+ 'stt-transcript',
150
+ 'Delete transcript failed',
151
+ {start, end, latency: end - start, requestId: reqId},
152
+ );
153
+ throw new Error(`Delete failed (${res.status})`);
154
+ }
155
+ logger.debug(
156
+ LogSource.NetworkRest,
157
+ 'stt-transcript',
158
+ 'Delete transcript succeeded',
159
+ {start, end, latency: end - start, requestId: reqId},
160
+ );
161
+
162
+ // optimistic remove from paginated list
163
+ setSttState(prev => {
164
+ // remove the deleted item
165
+ const newStts = prev.data.stts.filter(item => item.id !== id);
166
+ // decrement total count
167
+ const newTotal = Math.max(prev.data.pagination.total - 1, 0);
168
+ let newPage = prev.data.pagination.page;
169
+ if (prev.data.stts.length === 1 && newPage > 1) {
170
+ newPage--;
171
+ }
172
+ return {
173
+ ...prev,
174
+ data: {
175
+ stts: newStts,
176
+
177
+ pagination: {
178
+ ...prev.data.pagination,
179
+ total: newTotal,
180
+ page: newPage,
181
+ },
182
+ },
183
+ };
184
+ });
185
+ },
186
+ [roomId.host, store.token],
187
+ );
188
+
189
+ //–– fetch for a given recording ––
190
+ const getSTTsForRecording = useCallback(
191
+ (recordingId: string) => {
192
+ setSttRecState(r => ({...r, status: 'pending', error: null}));
193
+ const reqId = getUniqueID();
194
+ const start = Date.now();
195
+
196
+ fetch(`${$config.BACKEND_ENDPOINT}/v1/recording/stt-transcript`, {
197
+ method: 'POST',
198
+ headers: {
199
+ 'Content-Type': 'application/json',
200
+ authorization: store.token ? `Bearer ${store.token}` : '',
201
+ 'X-Request-Id': reqId,
202
+ 'X-Session-Id': logger.getSessionId(),
203
+ },
204
+ body: JSON.stringify({
205
+ project_id: $config.PROJECT_ID,
206
+ recording_id: recordingId,
207
+ }),
208
+ })
209
+ .then(async res => {
210
+ const json = await res.json();
211
+ const end = Date.now();
212
+ console.log('supriua json', json);
213
+ if (!res.ok) {
214
+ logger.error(
215
+ LogSource.NetworkRest,
216
+ 'stt-transcript',
217
+ 'Fetch stt-by-recording failed',
218
+ {json, start, end, latency: end - start, requestId: reqId},
219
+ );
220
+ throw new Error(json?.error?.message || res.statusText);
221
+ }
222
+ logger.debug(
223
+ LogSource.NetworkRest,
224
+ 'stt-transcript',
225
+ 'Fetch stt-by-recording succeeded',
226
+ {json, start, end, latency: end - start, requestId: reqId},
227
+ );
228
+ if (json?.error) {
229
+ logger.debug(
230
+ LogSource.NetworkRest,
231
+ 'stt-transcript',
232
+ `No STT records found (code ${json.error.code}): ${json.error.message}`,
233
+ {start, end, latency: end - start, reqId},
234
+ );
235
+ return [];
236
+ } else {
237
+ return json as FetchSTTTranscriptResponse['stts'];
238
+ }
239
+ })
240
+ .then(stts =>
241
+ setSttRecState({status: 'resolved', data: {stts}, error: null}),
242
+ )
243
+ .catch(err =>
244
+ setSttRecState(r => ({...r, status: 'rejected', error: err})),
245
+ );
246
+ },
247
+ [store.token],
248
+ );
249
+
250
+ return {
251
+ // stt list
252
+ sttState,
253
+ getSTTs,
254
+ currentPage,
255
+ setCurrentPage,
256
+ // STT per recording
257
+ sttRecState,
258
+ getSTTsForRecording,
259
+ // delete
260
+ deleteTranscript,
261
+ };
262
+ }
@@ -109,8 +109,8 @@ export const toolbarItemNoiseCancellationText =
109
109
  export const toolbarItemWhiteboardText = 'toolbarItemWhiteboardText';
110
110
  export const toolbarItemCaptionText = 'toolbarItemCaptionText';
111
111
  export const toolbarItemTranscriptText = 'toolbarItemTranscriptText';
112
- export const toolbarItemManageTranscriptText =
113
- 'toolbarItemManageTranscriptText';
112
+ export const toolbarItemManageTextTracksText =
113
+ 'toolbarItemManageTextTracksText';
114
114
  export const toolbarItemVirtualBackgroundText =
115
115
  'toolbarItemVirtualBackgroundText';
116
116
  export const toolbarItemViewRecordingText = 'toolbarItemViewRecordingText';
@@ -151,7 +151,7 @@ export const nativeStopScreensharePopupPrimaryBtnText =
151
151
  'nativeStopScreensharePopupPrimaryBtnText';
152
152
 
153
153
  export const recordingModalTitleIntn = 'recordingModalTitleIntn';
154
- export const transcriptModalTitleIntn = 'transcriptModalTitleIntn';
154
+ export const textTrackModalTitleIntn = 'textTrackModalTitleIntn';
155
155
  export const stopRecordingPopupHeading = 'stopRecordingPopupHeading';
156
156
  export const stopRecordingPopupSubHeading = 'stopRecordingPopupSubHeading';
157
157
  export const stopRecordingPopupPrimaryBtnText =
@@ -573,7 +573,7 @@ export interface I18nVideoCallScreenLabelsInterface {
573
573
  [toolbarItemWhiteboardText]?: I18nConditionalType;
574
574
  [toolbarItemCaptionText]?: I18nConditionalType;
575
575
  [toolbarItemTranscriptText]?: I18nConditionalType;
576
- [toolbarItemManageTranscriptText]?: I18nConditionalType;
576
+ [toolbarItemManageTextTracksText]?: I18nConditionalType;
577
577
  [toolbarItemVirtualBackgroundText]?: I18nBaseType;
578
578
  [toolbarItemViewRecordingText]?: I18nConditionalType;
579
579
 
@@ -609,7 +609,7 @@ export interface I18nVideoCallScreenLabelsInterface {
609
609
  [nativeStopScreensharePopupPrimaryBtnText]?: I18nBaseType;
610
610
 
611
611
  [recordingModalTitleIntn]?: I18nBaseType;
612
- [transcriptModalTitleIntn]?: I18nBaseType;
612
+ [textTrackModalTitleIntn]?: I18nBaseType;
613
613
  [stopRecordingPopupHeading]?: I18nBaseType;
614
614
  [stopRecordingPopupSubHeading]?: I18nBaseType;
615
615
  [stopRecordingPopupPrimaryBtnText]?: I18nBaseType;
@@ -942,7 +942,7 @@ export const VideoCallScreenLabels: I18nVideoCallScreenLabelsInterface = {
942
942
  [toolbarItemTranscriptText]: active =>
943
943
  active ? 'Hide Transcript' : 'Show Transcript',
944
944
  [toolbarItemViewRecordingText]: 'View Recordings',
945
- [toolbarItemManageTranscriptText]: 'View Transcripts',
945
+ [toolbarItemManageTextTracksText]: 'View Text-tracks',
946
946
 
947
947
  [toolbarItemRaiseHandText]: active => (active ? 'Lower Hand' : 'Raise Hand'),
948
948
  [toolbarItemSwitchCameraText]: 'Switch Camera',
@@ -1025,7 +1025,7 @@ export const VideoCallScreenLabels: I18nVideoCallScreenLabelsInterface = {
1025
1025
  `Once removed, ${name} will still be able to screen share later.`,
1026
1026
  [removeScreenshareFromRoomPopupPrimaryBtnText]: 'REMOVE',
1027
1027
 
1028
- [transcriptModalTitleIntn]: 'View Transcripts',
1028
+ [textTrackModalTitleIntn]: 'Text Tracks',
1029
1029
  [sttChangeLanguagePopupHeading]: isFirstTimeOpened =>
1030
1030
  isFirstTimeOpened ? 'Set Spoken Language' : 'Change Spoken Language',
1031
1031
  [sttChangeLanguagePopupSubHeading]:
@@ -369,7 +369,7 @@ const VideoCall: React.FC = () => {
369
369
  encryption: $config.ENCRYPTION_ENABLED
370
370
  ? {
371
371
  key: data.encryptionSecret,
372
- mode: RnEncryptionEnum.AES256GCM2,
372
+ mode: data.encryptionMode,
373
373
  screenKey: data.encryptionSecret,
374
374
  salt: data.encryptionSecretSalt,
375
375
  }
@@ -67,16 +67,31 @@ const getFrontendUrl = (url: string) => {
67
67
  return url;
68
68
  };
69
69
 
70
- interface RecordingsData {
71
- recordings: [];
72
- pagination: {};
70
+ export type APIStatus = 'idle' | 'pending' | 'resolved' | 'rejected';
71
+
72
+ export interface FetchRecordingData {
73
+ pagination: {
74
+ limit: number;
75
+ total: number;
76
+ page: number;
77
+ };
78
+ recordings: {
79
+ id: string;
80
+ download_url: string[];
81
+ title: string;
82
+ product_name: string;
83
+ status: 'COMPLETED' | 'STARTED' | 'INPROGRESS' | 'STOPPING';
84
+ created_at: string;
85
+ ended_at: string;
86
+ }[];
73
87
  }
88
+
74
89
  export interface RecordingContextInterface {
75
90
  startRecording: () => void;
76
91
  stopRecording: () => void;
77
92
  isRecordingActive: boolean;
78
93
  inProgress: boolean;
79
- fetchRecordings?: (page: number) => Promise<RecordingsData>;
94
+ fetchRecordings?: (page: number) => Promise<FetchRecordingData>;
80
95
  deleteRecording?: (id: number) => Promise<boolean>;
81
96
  }
82
97
 
@@ -41,7 +41,7 @@ export default function useCreateRoom(): createRoomFun {
41
41
  try {
42
42
  const payload = JSON.stringify({
43
43
  title: roomTitle,
44
- enablePSTN: enablePSTN,
44
+ enable_pstn: enablePSTN,
45
45
  });
46
46
  const response = await fetch(`${CREATE_ROOM_URL}`, {
47
47
  method: 'POST',
@@ -157,6 +157,11 @@ export default function useJoinRoom() {
157
157
  isWaitingRoomEnabled ? data.secretSalt : data.secret_salt,
158
158
  ) as Uint8Array;
159
159
  }
160
+
161
+ if (data?.encryption_mode) {
162
+ roomInfo.encryptionMode = data.encryption_mode;
163
+ }
164
+
160
165
  if (data?.screen_share_user?.uid || data?.screenShare?.uid) {
161
166
  roomInfo.screenShareUid = isWaitingRoomEnabled
162
167
  ? data.screenShare.uid
@@ -1,193 +0,0 @@
1
- import {useState, useCallback, useEffect, useContext} from 'react';
2
- import StorageContext from '../StorageContext';
3
- import {useRoomInfo} from 'customization-api';
4
- import getUniqueID from '../../utils/getUniqueID';
5
- import {logger, LogSource} from '../../logger/AppBuilderLogger';
6
-
7
- export interface FetchSTTTranscriptResponse {
8
- pagination: {
9
- limit: number;
10
- total: number;
11
- page: number;
12
- };
13
- stts: {
14
- id: string;
15
- download_url: string[];
16
- title: string;
17
- product_name: string;
18
- status: 'COMPLETED' | 'STARTED' | 'INPROGRESS' | 'STOPPING';
19
- created_at: string;
20
- ended_at: string;
21
- }[];
22
- }
23
-
24
- export type APIStatus = 'idle' | 'pending' | 'resolved' | 'rejected';
25
-
26
- export function useFetchSTTTranscript(defaultLimit = 10) {
27
- const {
28
- data: {roomId},
29
- } = useRoomInfo();
30
- const {store} = useContext(StorageContext);
31
- const [currentPage, setCurrentPage] = useState(1);
32
-
33
- const [state, setState] = useState<{
34
- status: APIStatus;
35
- data: {
36
- stts: FetchSTTTranscriptResponse['stts'];
37
- pagination: FetchSTTTranscriptResponse['pagination'];
38
- };
39
- error: any;
40
- }>({
41
- status: 'idle',
42
- data: {stts: [], pagination: {total: 0, limit: defaultLimit, page: 1}},
43
- error: null,
44
- });
45
-
46
- const fetchStts = useCallback(
47
- async (page: number) => {
48
- const requestId = getUniqueID();
49
- const start = Date.now();
50
-
51
- try {
52
- if (!roomId?.host) {
53
- return Promise.reject('room id is empty');
54
- }
55
- const res = await fetch(
56
- `${$config.BACKEND_ENDPOINT}/v1/stt-transcript`,
57
- {
58
- method: 'POST',
59
- headers: {
60
- 'Content-Type': 'application/json',
61
- authorization: store.token ? `Bearer ${store.token}` : '',
62
- 'X-Request-Id': requestId,
63
- 'X-Session-Id': logger.getSessionId(),
64
- },
65
- body: JSON.stringify({
66
- passphrase: roomId.host,
67
- limit: defaultLimit,
68
- page,
69
- }),
70
- },
71
- );
72
- const json = await res.json();
73
- const end = Date.now();
74
-
75
- if (!res.ok) {
76
- logger.error(
77
- LogSource.NetworkRest,
78
- 'stt-transcript',
79
- 'Fetching STT transcripts failed',
80
- {
81
- json,
82
- start,
83
- end,
84
- latency: end - start,
85
- requestId,
86
- },
87
- );
88
- throw new Error(json?.error?.message || 'Unknown fetch error');
89
- }
90
-
91
- logger.debug(
92
- LogSource.NetworkRest,
93
- 'stt-transcript',
94
- 'Fetched STT transcripts',
95
- {
96
- json,
97
- start,
98
- end,
99
- latency: end - start,
100
- requestId,
101
- },
102
- );
103
- return json;
104
- } catch (err) {
105
- return Promise.reject(err);
106
- }
107
- },
108
- [roomId.host, store.token, defaultLimit],
109
- );
110
-
111
- const getSTTs = useCallback(
112
- (page: number) => {
113
- setState(s => ({...s, status: 'pending'}));
114
- fetchStts(page).then(
115
- data =>
116
- setState({
117
- status: 'resolved',
118
- data: {
119
- stts: data.stts || [],
120
- pagination: data.pagination || {
121
- total: 0,
122
- limit: defaultLimit,
123
- page: 1,
124
- },
125
- },
126
- error: null,
127
- }),
128
- err => setState(s => ({...s, status: 'rejected', error: err})),
129
- );
130
- },
131
- [fetchStts, defaultLimit],
132
- );
133
-
134
- const deleteTranscript = useCallback(
135
- async (id: string) => {
136
- const res = await fetch(
137
- `${
138
- $config.BACKEND_ENDPOINT
139
- }/v1/stt-transcript/${id}?passphrase=${encodeURIComponent(
140
- roomId.host,
141
- )}`,
142
- {
143
- method: 'DELETE',
144
- headers: {'Content-Type': 'application/json'},
145
- },
146
- );
147
- if (!res.ok) {
148
- const body = await res.json();
149
- throw new Error(
150
- body?.error?.message ?? `Delete failed (${res.status})`,
151
- );
152
- }
153
- // optimistic update local state:
154
- setState(prev => {
155
- // remove the deleted item
156
- const newStts = prev.data.stts.filter(item => item.id !== id);
157
- // decrement total count
158
- const newTotal = Math.max(prev.data.pagination.total - 1, 0);
159
- // if we just removed the *last* item on this page, go back a page
160
- let newPage = prev.data.pagination.page;
161
- if (prev.data.stts.length === 1 && newPage > 1) {
162
- newPage = newPage - 1;
163
- }
164
- return {
165
- ...prev,
166
- data: {
167
- stts: newStts,
168
- pagination: {
169
- ...prev.data.pagination,
170
- total: newTotal,
171
- page: newPage,
172
- },
173
- },
174
- };
175
- });
176
- },
177
- [roomId.host],
178
- );
179
-
180
- useEffect(() => {
181
- getSTTs(currentPage);
182
- }, [currentPage, getSTTs]);
183
-
184
- return {
185
- status: state.status as APIStatus,
186
- stts: state.data.stts,
187
- pagination: state.data.pagination,
188
- error: state.error,
189
- currentPage,
190
- setCurrentPage,
191
- deleteTranscript,
192
- };
193
- }