agora-appbuilder-core 4.1.12 → 4.1.13-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 (27) hide show
  1. package/package.json +1 -1
  2. package/template/defaultConfig.js +2 -2
  3. package/template/src/assets/font-styles.css +4 -0
  4. package/template/src/assets/fonts/icomoon.ttf +0 -0
  5. package/template/src/assets/selection.json +1 -1
  6. package/template/src/atoms/ActionMenu.tsx +1 -1
  7. package/template/src/atoms/CustomIcon.tsx +1 -0
  8. package/template/src/atoms/Popup.tsx +1 -1
  9. package/template/src/components/Controls.tsx +21 -32
  10. package/template/src/language/default-labels/videoCallScreenLabels.ts +9 -9
  11. package/template/src/pages/VideoCall.tsx +2 -1
  12. package/template/src/pages/video-call/ActionSheetContent.tsx +0 -7
  13. package/template/src/pages/video-call/SidePanelHeader.tsx +80 -63
  14. package/template/src/rtm-events/constants.ts +9 -0
  15. package/template/src/subComponents/caption/Caption.tsx +4 -20
  16. package/template/src/subComponents/caption/CaptionContainer.tsx +262 -250
  17. package/template/src/subComponents/caption/CaptionIcon.tsx +6 -4
  18. package/template/src/subComponents/caption/CaptionText.tsx +26 -20
  19. package/template/src/subComponents/caption/LanguageSelectorPopup.tsx +30 -142
  20. package/template/src/subComponents/caption/Transcript.tsx +77 -32
  21. package/template/src/subComponents/caption/TranscriptIcon.tsx +7 -6
  22. package/template/src/subComponents/caption/TranslateActionMenu.tsx +128 -0
  23. package/template/src/subComponents/caption/useCaption.tsx +629 -482
  24. package/template/src/subComponents/caption/useSTTAPI.tsx +25 -4
  25. package/template/src/subComponents/caption/useStreamMessageUtils.native.ts +1 -1
  26. package/template/src/subComponents/caption/useStreamMessageUtils.ts +1 -1
  27. package/template/src/subComponents/caption/utils.ts +48 -40
@@ -36,6 +36,26 @@ const useSTTAPI = (): IuseSTTAPI => {
36
36
  const STT_API_URL = `${$config.BACKEND_ENDPOINT}/v1/stt`;
37
37
  const localUid = useLocalUid();
38
38
 
39
+ const roomIdRef = React.useRef(roomId);
40
+ React.useEffect(() => {
41
+ roomIdRef.current = roomId;
42
+ }, [roomId]);
43
+
44
+ const localUidRef = React.useRef(localUid);
45
+ React.useEffect(() => {
46
+ localUidRef.current = localUid;
47
+ }, [localUid]);
48
+
49
+ const tokenRef = React.useRef(store.token);
50
+ React.useEffect(() => {
51
+ tokenRef.current = store.token;
52
+ }, [store.token]);
53
+
54
+ const rtcPropsRef = React.useRef(rtcProps);
55
+ React.useEffect(() => {
56
+ rtcPropsRef.current = rtcProps;
57
+ }, [rtcProps]);
58
+
39
59
  const apiCall = async (
40
60
  method: 'startv7' | 'update' | 'stopv7',
41
61
  botUid: number,
@@ -49,10 +69,11 @@ const useSTTAPI = (): IuseSTTAPI => {
49
69
  const ownerUid = botUid - 900000000;
50
70
 
51
71
  let requestBody: any = {
52
- passphrase: roomId?.host || roomId?.attendee || '',
72
+ passphrase:
73
+ roomIdRef?.current?.host || roomIdRef?.current?.attendee || '',
53
74
  dataStream_uid: botUid,
54
75
  encryption_mode: $config.ENCRYPTION_ENABLED
55
- ? rtcProps.encryption.mode
76
+ ? rtcPropsRef?.current.encryption.mode
56
77
  : null,
57
78
  };
58
79
 
@@ -89,14 +110,14 @@ const useSTTAPI = (): IuseSTTAPI => {
89
110
  // If method is update and no targets are passed
90
111
  requestBody.translate = false;
91
112
  }
92
- requestBody.subscribeAudioUids = [`${localUid}`];
113
+ requestBody.subscribeAudioUids = [`${localUidRef.current}`];
93
114
  }
94
115
 
95
116
  const response = await fetch(`${STT_API_URL}/${method}`, {
96
117
  method: 'POST',
97
118
  headers: {
98
119
  'Content-Type': 'application/json',
99
- authorization: store.token ? `Bearer ${store.token}` : '',
120
+ authorization: tokenRef?.current ? `Bearer ${tokenRef?.current}` : '',
100
121
  'X-Request-Id': requestId,
101
122
  'X-Session-Id': logger.getSessionId(),
102
123
  },
@@ -50,7 +50,7 @@ const useStreamMessageUtils = (): {
50
50
  .lookupType('agora.audio2text.Text')
51
51
  .decode(payload as Uint8Array) as any;
52
52
 
53
- console.log('[STT_PER_USER_BOT] stt v7 textstream', botUid, textstream);
53
+ console.log('[STT_GLOBAL] stt v7 textstream', botUid, textstream);
54
54
  // console.log('STT - Parsed Textstream : ', textstream);
55
55
 
56
56
  // Identifing Current & Prev Speakers for the Captions
@@ -52,7 +52,7 @@ const useStreamMessageUtils = (): {
52
52
  .lookupType('agora.audio2text.Text')
53
53
  .decode(payload as Uint8Array) as any;
54
54
 
55
- console.log('[STT_PER_USER_BOT] stt v7 textstream', botUid, textstream);
55
+ console.log('[STT_GLOBAL] stt v7 textstream', botUid, textstream);
56
56
  //console.log('STT - Parsed Textstream : ', textstream);
57
57
  // console.log(
58
58
  // `STT-callback(${++counter}): %c${textstream.uid} %c${textstream.words
@@ -110,7 +110,7 @@ export function getLanguageLabel(
110
110
  languageCode: LanguageType[],
111
111
  ): string | undefined {
112
112
  const langLabels = languageCode.map(langCode => {
113
- return langData.find(data => data.value === langCode).label;
113
+ return langData.find(data => data.value === langCode)?.label;
114
114
  });
115
115
  return langLabels ? langLabels.join(', ') : undefined;
116
116
  }
@@ -163,31 +163,32 @@ export const formatTranscriptContent = (
163
163
  const formattedContent = meetingTranscript
164
164
  .map(item => {
165
165
  if (
166
- item.uid.toString().indexOf('langUpdate') !== -1 ||
167
- item.uid.toString().indexOf('translationUpdate') !== -1
166
+ item.uid.toString().includes('langUpdate') ||
167
+ item.uid.toString().includes('translationUpdate')
168
168
  ) {
169
- return `${defaultContent[item?.uid?.split('-')[1]]?.name} ${item.text}`;
169
+ // return `${defaultContent[item?.uid?.split('-')[1]]?.name} ${item.text}`;
170
+ return item.text;
170
171
  }
171
172
 
172
- // Build transcript entry with original text and all translations
173
- let transcriptEntry = `${defaultContent[item.uid]?.name} ${formatTime(
174
- Number(item?.time),
175
- )}:\n${item.text}`;
176
-
177
- // Add all translations with language labels
178
- if (item.translations && item.translations.length > 0) {
179
- const translationLines = item.translations
180
- .map(trans => {
181
- const langLabel =
182
- langData.find(l => l.value === trans.lang)?.label || trans.lang;
183
- return `${langLabel}: ${trans.text}`;
184
- })
185
- .join('\n');
186
-
187
- transcriptEntry += `\n${translationLines}`;
173
+ const speakerName = defaultContent[item.uid]?.name || 'Speaker';
174
+
175
+ // Original
176
+ let entry = `${speakerName}:\n${item.text}`;
177
+
178
+ // Selected Translation
179
+ const storedLang = item.selectedTranslationLanguage;
180
+ const selectedTranslation = storedLang
181
+ ? item.translations?.find(t => t.lang === storedLang) || null
182
+ : null;
183
+ if (selectedTranslation) {
184
+ const langLabel =
185
+ langData.find(l => l.value === selectedTranslation.lang)?.label ||
186
+ selectedTranslation.lang;
187
+
188
+ entry += `\n→ (${langLabel}) ${selectedTranslation.text}`;
188
189
  }
189
190
 
190
- return transcriptEntry;
191
+ return entry;
191
192
  })
192
193
  .join('\n\n');
193
194
 
@@ -196,13 +197,20 @@ export const formatTranscriptContent = (
196
197
  );
197
198
 
198
199
  const attendees = Object.entries(defaultContent)
199
- .filter(
200
- arr =>
201
- arr[1].type === 'rtc' &&
202
- arr[0] !== '100000' && // exclude recording bot
203
- (arr[1]?.isInWaitingRoom === true ? false : true),
204
- )
205
- .map(arr => arr[1].name)
200
+ .filter(([uid, user]) => {
201
+ const uidNum = Number(uid);
202
+
203
+ const isBot =
204
+ uidNum === 111111 || // STT bot (web)
205
+ uidNum > 900000000 || // STT bots (native)
206
+ uidNum === 100000 || // Recording bot (web user)
207
+ uidNum === 100001; // Recording bot (web screen)
208
+
209
+ const isWaitingRoom = user?.isInWaitingRoom === true;
210
+
211
+ return user.type === 'rtc' && !isBot && !isWaitingRoom;
212
+ })
213
+ .map(([_, user]) => user.name)
206
214
  .join(',');
207
215
 
208
216
  const info =
@@ -232,19 +240,18 @@ export const formatTranscriptContent = (
232
240
  * @param captionText - The original caption text
233
241
  * @param translations - Array of available translations
234
242
  * @param viewerSourceLanguage - The user's source (spoken) language
235
- * @param speakerUid - The UID of the person speaking
236
- * @param currentUserUid - The UID of the current user
237
243
  * @returns The appropriate caption text to display
238
244
  */
239
245
  export const getUserTranslatedText = (
240
246
  captionText: string,
241
247
  translations: Array<{lang: string; text: string; isFinal: boolean}> = [],
242
- viewerSourceLanguage: LanguageType,
243
- speakerUid: string | number,
244
- currentUserUid: string | number,
248
+ sourceLanguage: LanguageType,
249
+ selectedTranslationLanguage: LanguageType,
250
+ // speakerUid: string | number,
251
+ // currentUserUid: string | number,
245
252
  ): {
246
253
  value: string;
247
- langCode: string;
254
+ langLabel: string;
248
255
  } => {
249
256
  // console.log(
250
257
  // 'getUserTranslatedText input params',
@@ -256,16 +263,17 @@ export const getUserTranslatedText = (
256
263
  // );
257
264
 
258
265
  // 1. If the speaker is the local user, always show their own source text
259
- if (speakerUid === currentUserUid) {
266
+ if (!selectedTranslationLanguage) {
260
267
  return {
261
268
  value: captionText,
262
- langCode: getLanguageLabel([viewerSourceLanguage]) || '',
269
+ langLabel: getLanguageLabel([sourceLanguage]) || '',
263
270
  };
264
271
  }
272
+
265
273
  // For other users' captions, try to find translation matching viewer's source language
266
- if (viewerSourceLanguage && translations && translations.length > 0) {
274
+ if (selectedTranslationLanguage && translations && translations.length > 0) {
267
275
  const matchingTranslation = translations.find(
268
- t => t.lang === viewerSourceLanguage,
276
+ t => t.lang === selectedTranslationLanguage,
269
277
  );
270
278
  if (matchingTranslation) {
271
279
  // Translation exists (even if empty)
@@ -274,7 +282,7 @@ export const getUserTranslatedText = (
274
282
  const translatedText = matchingTranslation.text?.trim() || '';
275
283
  return {
276
284
  value: translatedText,
277
- langCode: getLanguageLabel([matchingTranslation.lang]) || '',
285
+ langLabel: getLanguageLabel([selectedTranslationLanguage]) || '',
278
286
  };
279
287
  }
280
288
  }
@@ -282,7 +290,7 @@ export const getUserTranslatedText = (
282
290
  // Fallback to original text if no translation found
283
291
  return {
284
292
  value: captionText,
285
- langCode: 'Original',
293
+ langLabel: 'Original',
286
294
  };
287
295
  };
288
296