agora-appbuilder-core 4.1.10-beta.1 → 4.1.11

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 (132) hide show
  1. package/package.json +2 -2
  2. package/template/agora-rn-uikit/src/Utils/isBotUser.ts +1 -1
  3. package/template/android/app/build.gradle +0 -7
  4. package/template/bridge/rtc/webNg/RtcEngine.ts +2 -2
  5. package/template/bridge/rtm/web/Types.ts +0 -183
  6. package/template/bridge/rtm/web/index.ts +488 -450
  7. package/template/customization-api/typeDefinition.ts +0 -1
  8. package/template/defaultConfig.js +3 -4
  9. package/template/global.d.ts +0 -1
  10. package/template/ios/Podfile +0 -41
  11. package/template/package.json +5 -5
  12. package/template/src/AppRoutes.tsx +3 -3
  13. package/template/src/ai-agent/components/ControlButtons.tsx +1 -1
  14. package/template/src/assets/font-styles.css +1 -33
  15. package/template/src/assets/fonts/icomoon.ttf +0 -0
  16. package/template/src/assets/selection.json +1 -1
  17. package/template/src/atoms/ActionMenu.tsx +93 -13
  18. package/template/src/atoms/CustomIcon.tsx +1 -8
  19. package/template/src/atoms/DropDownMulti.tsx +80 -29
  20. package/template/src/atoms/Dropdown.tsx +0 -5
  21. package/template/src/atoms/Input.tsx +2 -1
  22. package/template/src/atoms/TertiaryButton.tsx +1 -1
  23. package/template/src/atoms/UserAvatar.tsx +1 -1
  24. package/template/src/components/ChatContext.ts +3 -5
  25. package/template/src/components/Controls.tsx +167 -208
  26. package/template/src/components/DeviceConfigure.tsx +1 -1
  27. package/template/src/components/EventsConfigure.tsx +168 -118
  28. package/template/src/components/Navbar.tsx +11 -14
  29. package/template/src/components/RTMConfigure.tsx +819 -32
  30. package/template/src/components/beauty-effect/useBeautyEffects.tsx +13 -50
  31. package/template/src/components/chat/chatConfigure.tsx +1 -7
  32. package/template/src/components/chat-messages/useChatMessages.tsx +11 -43
  33. package/template/src/components/controls/useControlPermissionMatrix.tsx +4 -32
  34. package/template/src/components/participants/AllHostParticipants.tsx +2 -10
  35. package/template/src/components/participants/Participant.tsx +1 -7
  36. package/template/src/components/participants/UserActionMenuOptions.tsx +2 -12
  37. package/template/src/components/precall/joinCallBtn.native.tsx +7 -2
  38. package/template/src/components/precall/joinCallBtn.tsx +7 -2
  39. package/template/src/components/precall/joinWaitingRoomBtn.native.tsx +16 -15
  40. package/template/src/components/precall/joinWaitingRoomBtn.tsx +31 -17
  41. package/template/src/components/precall/textInput.tsx +45 -22
  42. package/template/src/components/precall/usePreCall.tsx +7 -0
  43. package/template/src/components/recordings/RecordingsDateTable.tsx +2 -3
  44. package/template/src/components/room-info/useRoomInfo.tsx +5 -0
  45. package/template/src/components/useUserPreference.tsx +12 -39
  46. package/template/src/components/virtual-background/useVB.tsx +0 -18
  47. package/template/src/components/whiteboard/WhiteboardConfigure.tsx +0 -27
  48. package/template/src/language/default-labels/videoCallScreenLabels.ts +27 -11
  49. package/template/src/logger/AppBuilderLogger.tsx +3 -11
  50. package/template/src/pages/VideoCall.tsx +518 -171
  51. package/template/src/pages/video-call/ActionSheetContent.tsx +77 -77
  52. package/template/src/pages/video-call/SidePanelHeader.tsx +81 -53
  53. package/template/src/pages/video-call/VideoCallScreen.tsx +0 -18
  54. package/template/src/pages/video-call/VideoCallScreenWrapper.tsx +1 -0
  55. package/template/src/rtm/RTMEngine.ts +37 -262
  56. package/template/src/rtm/utils.ts +1 -68
  57. package/template/src/rtm-events/constants.ts +7 -40
  58. package/template/src/rtm-events-api/Events.ts +39 -158
  59. package/template/src/subComponents/ChatBubble.tsx +3 -3
  60. package/template/src/subComponents/ChatContainer.tsx +9 -19
  61. package/template/src/subComponents/LocalAudioMute.tsx +2 -2
  62. package/template/src/subComponents/LocalVideoMute.tsx +2 -2
  63. package/template/src/subComponents/SidePanelEnum.tsx +0 -1
  64. package/template/src/subComponents/caption/Caption.tsx +48 -7
  65. package/template/src/subComponents/caption/CaptionContainer.tsx +324 -51
  66. package/template/src/subComponents/caption/CaptionIcon.tsx +35 -34
  67. package/template/src/subComponents/caption/CaptionText.tsx +103 -2
  68. package/template/src/subComponents/caption/LanguageSelectorPopup.tsx +179 -69
  69. package/template/src/subComponents/caption/Transcript.tsx +46 -11
  70. package/template/src/subComponents/caption/TranscriptIcon.tsx +27 -35
  71. package/template/src/subComponents/caption/TranscriptText.tsx +78 -3
  72. package/template/src/subComponents/caption/proto/ptoto.js +38 -4
  73. package/template/src/subComponents/caption/proto/test.proto +34 -19
  74. package/template/src/subComponents/caption/useCaption.tsx +754 -11
  75. package/template/src/subComponents/caption/useSTTAPI.tsx +118 -205
  76. package/template/src/subComponents/caption/useStreamMessageUtils.native.ts +152 -33
  77. package/template/src/subComponents/caption/useStreamMessageUtils.ts +165 -34
  78. package/template/src/subComponents/caption/utils.ts +171 -3
  79. package/template/src/subComponents/chat/ChatSendButton.tsx +0 -1
  80. package/template/src/subComponents/screenshare/ScreenshareButton.tsx +0 -16
  81. package/template/src/subComponents/screenshare/ScreenshareConfigure.native.tsx +1 -1
  82. package/template/src/subComponents/waiting-rooms/WaitingRoomControls.tsx +4 -7
  83. package/template/src/utils/SdkEvents.ts +3 -0
  84. package/template/src/utils/useEndCall.ts +4 -4
  85. package/template/src/utils/useMuteToggleLocal.ts +10 -14
  86. package/template/src/utils/useSpeechToText.ts +31 -20
  87. package/template/bridge/rtm/web/index-legacy.ts +0 -540
  88. package/template/src/components/RTMConfigure-legacy.tsx +0 -848
  89. package/template/src/components/UserGlobalPreferenceProvider.tsx +0 -227
  90. package/template/src/components/breakout-room/BreakoutRoomPanel.tsx +0 -58
  91. package/template/src/components/breakout-room/context/BreakoutRoomContext.tsx +0 -2508
  92. package/template/src/components/breakout-room/events/BreakoutRoomEventsConfigure.tsx +0 -272
  93. package/template/src/components/breakout-room/events/constants.ts +0 -17
  94. package/template/src/components/breakout-room/hoc/BreakoutRoomNameRenderer.tsx +0 -68
  95. package/template/src/components/breakout-room/hooks/useBreakoutRoomExit.ts +0 -49
  96. package/template/src/components/breakout-room/state/reducer.ts +0 -522
  97. package/template/src/components/breakout-room/state/types.ts +0 -54
  98. package/template/src/components/breakout-room/ui/BreakoutMeetingTitle.tsx +0 -60
  99. package/template/src/components/breakout-room/ui/BreakoutRoomActionMenu.tsx +0 -136
  100. package/template/src/components/breakout-room/ui/BreakoutRoomAnnouncementModal.tsx +0 -135
  101. package/template/src/components/breakout-room/ui/BreakoutRoomGroupSettings.tsx +0 -588
  102. package/template/src/components/breakout-room/ui/BreakoutRoomMainRoomUsers.tsx +0 -142
  103. package/template/src/components/breakout-room/ui/BreakoutRoomMemberActionMenu.tsx +0 -122
  104. package/template/src/components/breakout-room/ui/BreakoutRoomParticipants.tsx +0 -124
  105. package/template/src/components/breakout-room/ui/BreakoutRoomRaiseHand.tsx +0 -65
  106. package/template/src/components/breakout-room/ui/BreakoutRoomRenameModal.tsx +0 -227
  107. package/template/src/components/breakout-room/ui/BreakoutRoomSettings.tsx +0 -140
  108. package/template/src/components/breakout-room/ui/BreakoutRoomTransition.tsx +0 -52
  109. package/template/src/components/breakout-room/ui/BreakoutRoomView.tsx +0 -193
  110. package/template/src/components/breakout-room/ui/ExitBreakoutRoomIconButton.tsx +0 -79
  111. package/template/src/components/breakout-room/ui/ParticipantManualAssignmentModal.tsx +0 -638
  112. package/template/src/components/breakout-room/ui/SelectParticipantAssignmentStrategy.tsx +0 -57
  113. package/template/src/components/common/Dividers.tsx +0 -53
  114. package/template/src/components/controls/toolbar-items/ExitBreakoutRoomToolbarItem.tsx +0 -13
  115. package/template/src/components/raise-hand/RaiseHandButton.tsx +0 -50
  116. package/template/src/components/raise-hand/RaiseHandProvider.tsx +0 -308
  117. package/template/src/components/raise-hand/index.ts +0 -14
  118. package/template/src/components/room-info/useCurrentRoomInfo.tsx +0 -42
  119. package/template/src/components/room-info/useSetBreakoutRoomInfo.tsx +0 -64
  120. package/template/src/pages/video-call/BreakoutVideoCall.tsx +0 -213
  121. package/template/src/pages/video-call/VideoCallContent.tsx +0 -211
  122. package/template/src/pages/video-call/VideoCallStateWrapper.tsx +0 -495
  123. package/template/src/rtm/RTMConfigureBreakoutRoomProvider.tsx +0 -882
  124. package/template/src/rtm/RTMConfigureMainRoomProvider.tsx +0 -757
  125. package/template/src/rtm/RTMCoreProvider.tsx +0 -419
  126. package/template/src/rtm/RTMGlobalStateProvider.tsx +0 -706
  127. package/template/src/rtm/RTMStatusBanner.tsx +0 -99
  128. package/template/src/rtm/constants.ts +0 -12
  129. package/template/src/rtm/hooks/useMainRoomUserDisplayName.ts +0 -45
  130. package/template/src/rtm/rtm-presence-utils.ts +0 -344
  131. package/template/src/subComponents/chat/ChatAnnouncementView.tsx +0 -65
  132. package/template/src/utils/useDebouncedCallback.tsx +0 -20
@@ -8,12 +8,21 @@ import {
8
8
  import React from 'react';
9
9
 
10
10
  import ThemeConfig from '../../../src/theme';
11
+ import {useCaption} from './useCaption';
11
12
  import hexadecimalTransparency from '../../../src/utils/hexadecimalTransparency';
12
13
  import {isAndroid, isMobileUA} from '../../utils/common';
14
+ import {getUserTranslatedText, getLanguageLabel, LanguageType} from './utils';
15
+
16
+ type TranslationItem = {
17
+ lang: string;
18
+ text: string;
19
+ isFinal: boolean;
20
+ };
13
21
 
14
22
  interface CaptionTextProps {
15
23
  user: string;
16
24
  value: string;
25
+ translations?: TranslationItem[];
17
26
  activeSpeakersCount: number;
18
27
  isActiveSpeaker?: boolean;
19
28
  activelinesAvailable?: number;
@@ -22,6 +31,9 @@ interface CaptionTextProps {
22
31
  setInActiveLinesAvaialble?: React.Dispatch<React.SetStateAction<number>>;
23
32
  captionUserStyle?: TextStyle;
24
33
  captionTextStyle?: TextStyle;
34
+ speakerUid?: string | number;
35
+ userLocalUid?: string | number;
36
+ spokenLanguageCode?: LanguageType;
25
37
  }
26
38
 
27
39
  const DESKTOP_LINE_HEIGHT = 28;
@@ -31,6 +43,7 @@ const MAX_CAPTIONS_LINES_ALLOWED = 3;
31
43
  const CaptionText = ({
32
44
  user,
33
45
  value,
46
+ translations = [],
34
47
  activeSpeakersCount,
35
48
  isActiveSpeaker = false,
36
49
  activelinesAvailable,
@@ -39,8 +52,12 @@ const CaptionText = ({
39
52
  setInActiveLinesAvaialble,
40
53
  captionUserStyle = {},
41
54
  captionTextStyle = {},
55
+ speakerUid,
56
+ userLocalUid,
57
+ spokenLanguageCode,
42
58
  }: CaptionTextProps) => {
43
59
  const isMobile = isMobileUA();
60
+ const {translationConfig, captionViewMode} = useCaption();
44
61
 
45
62
  const LINE_HEIGHT = isMobile ? MOBILE_LINE_HEIGHT : DESKTOP_LINE_HEIGHT;
46
63
 
@@ -82,6 +99,63 @@ const CaptionText = ({
82
99
  *
83
100
  */
84
101
 
102
+ // Get the appropriate source text for display based on viewer's language preferences
103
+ const viewerSourceLanguage = translationConfig.source[0];
104
+ // const localUserText = value;
105
+ // const remoteUserTranslatedText: {
106
+ // text: string,
107
+ // langcode: LanguageType
108
+ // } = () => {
109
+ // const matchingTranslation = translations.find(
110
+ // t => t.lang === viewerSourceLanguage,
111
+ // );
112
+ // if (matchingTranslation?.text) {
113
+ // return matchingTranslation.text;
114
+ // }
115
+ // return value;
116
+ // };
117
+
118
+ const displayTranslatedViewText = getUserTranslatedText(
119
+ value,
120
+ translations,
121
+ viewerSourceLanguage,
122
+ speakerUid,
123
+ userLocalUid,
124
+ );
125
+
126
+ /**
127
+ * ROBUST TEXT EXTRACTION LOGIC
128
+ * Problem: value and translationText contain FULL accumulated text (can be 1000+ chars)
129
+ * Solution: Extract only the latest portion that fits in allocated lines
130
+ * - With translation: 2 lines source (~100-120 chars) + 1 line translation (~50-75 chars)
131
+ * - Without translation: 3 lines source (~150-180 chars)
132
+ */
133
+ const getLatestTextPortion = (text: string, maxChars: number) => {
134
+ if (!text || text.length <= maxChars) return text;
135
+
136
+ // Take last maxChars, try to find sentence boundary for cleaner cut
137
+ const portion = text.slice(-maxChars);
138
+ const sentenceMatch = portion.match(/[.!?]\s+/);
139
+
140
+ // If we find a sentence boundary after position 10, start from there
141
+ if (sentenceMatch && sentenceMatch.index > 10) {
142
+ return portion.slice(sentenceMatch.index + sentenceMatch[0].length);
143
+ }
144
+
145
+ // Otherwise just return the last maxChars
146
+ return portion;
147
+ };
148
+
149
+ // Adjust char limits based on mobile vs desktop (mobile has smaller font)
150
+ const sourceCharLimit =
151
+ captionViewMode === 'original-and-translated'
152
+ ? isMobile
153
+ ? 50
154
+ : 70 // 1 line for source when showing original + translation below
155
+ : isMobile
156
+ ? 150
157
+ : 180; // 3 lines for source in translated mode (uses full space)
158
+
85
159
  return (
86
160
  <View
87
161
  style={[
@@ -125,17 +199,36 @@ const CaptionText = ({
125
199
  )) * LINE_HEIGHT,
126
200
  },
127
201
  ]}>
202
+ {/* Combined source and translation text */}
128
203
  <Text
129
204
  onLayout={handleTextLayout}
130
205
  style={[
131
206
  styles.captionText,
207
+ styles.transcriptionText,
132
208
  isMobile
133
209
  ? styles.mobileCaptionFontSize
134
210
  : styles.desktopCaptionFontSize,
135
211
  isAndroid() && {lineHeight: MOBILE_LINE_HEIGHT - 2},
136
212
  captionTextStyle,
137
213
  ]}>
138
- {value}
214
+ {/* Default view when view mode is : translated */}
215
+ <Text style={styles.languageLabel}>
216
+ ({displayTranslatedViewText.langCode}
217
+ ):{' '}
218
+ </Text>
219
+ {getLatestTextPortion(
220
+ displayTranslatedViewText.value,
221
+ sourceCharLimit,
222
+ )}
223
+ {/* View mode when "Original and Translated" is selected - show original for remote users only */}
224
+ {speakerUid !== userLocalUid &&
225
+ captionViewMode === 'original-and-translated' && (
226
+ <>
227
+ {'\n'}
228
+ <Text style={styles.languageLabel}>(Original): </Text>
229
+ {getLatestTextPortion(value, sourceCharLimit)}
230
+ </>
231
+ )}
139
232
  </Text>
140
233
  </View>
141
234
  </View>
@@ -155,8 +248,8 @@ const styles = StyleSheet.create({
155
248
  },
156
249
 
157
250
  captionTextContainerStyle: {
158
- overflow: 'hidden',
159
251
  width: '100%',
252
+ overflow: 'hidden',
160
253
  position: 'relative',
161
254
  },
162
255
 
@@ -168,6 +261,14 @@ const styles = StyleSheet.create({
168
261
  bottom: 0,
169
262
  },
170
263
 
264
+ transcriptionText: {
265
+ marginBottom: 2,
266
+ },
267
+
268
+ languageLabel: {
269
+ color: $config.FONT_COLOR + ThemeConfig.EmphasisPlus.low,
270
+ },
271
+
171
272
  captionUserName: {
172
273
  fontWeight: '600',
173
274
  fontFamily: ThemeConfig.FontFamily.sansPro,
@@ -1,4 +1,4 @@
1
- import React, {SetStateAction, useContext} from 'react';
1
+ import React, {SetStateAction} from 'react';
2
2
  import {StyleSheet, Text, View} from 'react-native';
3
3
  import Spacer from '../../atoms/Spacer';
4
4
  import Popup from '../../atoms/Popup';
@@ -6,50 +6,124 @@ import TertiaryButton from '../../atoms/TertiaryButton';
6
6
  import PrimaryButton from '../../atoms/PrimaryButton';
7
7
  import ThemeConfig from '../../theme';
8
8
  import {useIsDesktop} from '../../utils/common';
9
- import {useCaption} from './useCaption';
9
+ import {LanguageTranslationConfig, useCaption} from './useCaption';
10
+ import Dropdown from '../../atoms/Dropdown';
10
11
  import DropdownMulti from '../../atoms/DropDownMulti';
11
12
  import hexadecimalTransparency from '../../utils/hexadecimalTransparency';
12
13
  import Loading from '../Loading';
13
- import {LanguageType} from './utils';
14
+ import {LanguageType, langData, hasConfigChanged} from './utils';
14
15
  import {useString} from '../../utils/useString';
16
+ import {useLocalUid} from '../../../agora-rn-uikit';
17
+ // import {useRoomInfo} from '../../components/room-info/useRoomInfo';
15
18
  import {
16
- sttChangeLanguagePopupDropdownError,
17
- sttChangeLanguagePopupDropdownInfo,
18
19
  sttChangeLanguagePopupHeading,
19
20
  sttChangeLanguagePopupPrimaryBtnText,
20
- sttChangeLanguagePopupSubHeading,
21
21
  sttLanguageChangeInProgress,
22
+ sttChangeLanguagePopupDropdownInfo,
22
23
  } from '../../language/default-labels/videoCallScreenLabels';
23
24
  import {cancelText} from '../../language/default-labels/commonLabels';
24
25
 
25
26
  interface LanguageSelectorPopup {
26
27
  modalVisible: boolean;
27
28
  setModalVisible: React.Dispatch<SetStateAction<boolean>>;
28
- onConfirm: (param: boolean, lang: LanguageType[]) => void;
29
- isFirstTimePopupOpen?: boolean;
29
+ onConfirm: (inputTranslationConfig: LanguageTranslationConfig) => void;
30
30
  }
31
31
 
32
32
  const LanguageSelectorPopup = (props: LanguageSelectorPopup) => {
33
- const {isFirstTimePopupOpen = false} = props;
34
- const [isOpen, setIsOpen] = React.useState(false);
35
33
  const isDesktop = useIsDesktop()('popup');
36
34
  const heading = useString<boolean>(sttChangeLanguagePopupHeading);
37
- const subHeading = useString(sttChangeLanguagePopupSubHeading)();
38
35
  const cancelBtnLabel = useString(cancelText)();
39
36
  const ConfirmBtnLabel = useString(sttChangeLanguagePopupPrimaryBtnText)();
40
- const ddError = useString(sttChangeLanguagePopupDropdownError)();
41
- const ddInfo = useString(sttChangeLanguagePopupDropdownInfo)();
42
37
  const languageChangeInProgress = useString(sttLanguageChangeInProgress)();
43
- const {language, setLanguage, isLangChangeInProgress, isSTTActive} =
44
- useCaption();
38
+ const maxLangValInfo = useString(sttChangeLanguagePopupDropdownInfo)();
45
39
 
40
+ const {
41
+ translationConfig,
42
+ isLangChangeInProgress,
43
+ isSTTActive,
44
+ remoteSpokenLanguages,
45
+ } = useCaption();
46
+ const localUid = useLocalUid();
47
+ console.log(
48
+ '[STT_PER_USER_BOT] remoteSpokenLanguages',
49
+ remoteSpokenLanguages,
50
+ );
51
+
52
+ // const {sttLanguage} = useRoomInfo();
46
53
  const [error, setError] = React.useState<boolean>(false);
47
- const [selectedValues, setSelectedValues] =
48
- React.useState<LanguageType[]>(language);
54
+ const [isTargetOpen, setIsTargetOpen] = React.useState(false);
55
+
56
+ const [inputTranslationConfig, setInputTranslationConfig] =
57
+ React.useState<LanguageTranslationConfig>({
58
+ source: [],
59
+ targets: [],
60
+ });
61
+
49
62
  const isNotValidated =
50
- isOpen && (selectedValues.length === 0 || selectedValues.length === 2);
63
+ inputTranslationConfig?.source.length === 0 ||
64
+ inputTranslationConfig?.targets.length === 0 ||
65
+ inputTranslationConfig?.targets.length > 10;
66
+
67
+ // Create source language options with "None" prepended
68
+ const sourceLanguageOptions = React.useMemo(() => {
69
+ // return [{label: 'None', value: 'none'}, ...langData];
70
+ return [...langData];
71
+ }, []);
72
+
73
+ // All remote languages except for own user
74
+ const suggestedRemoteTargetLangs = React.useMemo(() => {
75
+ const remoteLangs = Object.entries(remoteSpokenLanguages)
76
+ .filter(([uid, lang]) => uid !== String(localUid) && lang)
77
+ .map(([, lang]) => lang);
78
+
79
+ return Array.from(new Set(remoteLangs));
80
+ }, [remoteSpokenLanguages, localUid]);
81
+
82
+ // Initialize or update source/targets dynamically when modal opens
83
+ React.useEffect(() => {
84
+ if (!props.modalVisible) {
85
+ return;
86
+ }
87
+ console.log(
88
+ '[STT_PER_USER_BOT] language selecter popup opened',
89
+ translationConfig,
90
+ );
51
91
 
52
- // React.useEffect(() => setSelectedValues(() => language), []);
92
+ const mergedTargets = Array.from(
93
+ new Set([
94
+ ...(translationConfig.targets || []),
95
+ ...suggestedRemoteTargetLangs,
96
+ ]),
97
+ );
98
+
99
+ setInputTranslationConfig({
100
+ source: translationConfig.source,
101
+ targets: mergedTargets,
102
+ });
103
+
104
+ console.log('[STT_PER_USER_BOT] mergedTargets —', mergedTargets);
105
+ console.log(
106
+ '[STT_PER_USER_BOT] all target langs —',
107
+ suggestedRemoteTargetLangs,
108
+ );
109
+ }, [props.modalVisible, translationConfig, suggestedRemoteTargetLangs]);
110
+
111
+ const onConfirmPress = async () => {
112
+ if (isNotValidated) {
113
+ return;
114
+ }
115
+
116
+ console.log('[LANG_SELECTOR] Confirm pressed:', {
117
+ inputTranslationConfig,
118
+ });
119
+
120
+ try {
121
+ props?.onConfirm(inputTranslationConfig);
122
+ // props.setModalVisible(false);
123
+ } catch (err) {
124
+ console.error('[LANG_SELECTOR] Error confirming STT config:', err);
125
+ }
126
+ };
53
127
 
54
128
  return (
55
129
  <Popup
@@ -57,8 +131,7 @@ const LanguageSelectorPopup = (props: LanguageSelectorPopup) => {
57
131
  setModalVisible={props.setModalVisible}
58
132
  showCloseIcon={true}
59
133
  contentContainerStyle={styles.contentContainer}
60
- title={heading(isFirstTimePopupOpen)}
61
- subtitle={subHeading}>
134
+ title={heading(isSTTActive ? false : true)}>
62
135
  {isLangChangeInProgress ? (
63
136
  <View style={styles.changeInProgress}>
64
137
  <Loading
@@ -70,35 +143,74 @@ const LanguageSelectorPopup = (props: LanguageSelectorPopup) => {
70
143
  </View>
71
144
  ) : (
72
145
  <>
146
+ {/* Source Language */}
147
+ <View>
148
+ <Text style={styles.labelText}>
149
+ What language will you speak in this meeting?
150
+ </Text>
151
+ <Spacer size={8} />
152
+ <Dropdown
153
+ label="Select language"
154
+ data={sourceLanguageOptions}
155
+ selectedValue={inputTranslationConfig.source[0] || ''}
156
+ onSelect={item =>
157
+ setInputTranslationConfig(prev => ({
158
+ ...prev,
159
+ source: [item.value as LanguageType],
160
+ }))
161
+ }
162
+ enabled={true}
163
+ />
164
+ <Spacer size={2} />
165
+ <Text style={styles.infoText}>
166
+ Captions and transcript will appear in this language for you.
167
+ </Text>
168
+ </View>
169
+
170
+ <Spacer size={20} />
171
+
172
+ {/* Target Languages */}
73
173
  <View>
174
+ <Text style={styles.labelText}>
175
+ Spoken languages in the meeting
176
+ </Text>
177
+ <Spacer size={8} />
74
178
  <DropdownMulti
75
- selectedValues={selectedValues}
76
- setSelectedValues={setSelectedValues}
77
- defaultSelectedValues={
78
- language.indexOf('') === -1 ? language : ['en-US']
179
+ selectedValues={inputTranslationConfig.targets}
180
+ setSelectedValues={(val: LanguageType[]) =>
181
+ setInputTranslationConfig(prev => ({
182
+ ...prev,
183
+ targets: val,
184
+ }))
79
185
  }
186
+ defaultSelectedValues={inputTranslationConfig.targets}
80
187
  error={error}
81
188
  setError={setError}
82
- isOpen={isOpen}
83
- setIsOpen={setIsOpen}
189
+ isOpen={isTargetOpen}
190
+ setIsOpen={setIsTargetOpen}
191
+ maxAllowedSelection={10}
192
+ protectedLanguages={Array.from(
193
+ new Set([...suggestedRemoteTargetLangs]),
194
+ )}
84
195
  />
196
+ <Spacer size={2} />
197
+ <Text style={styles.infoText}>
198
+ Auto populated by spoken languages of other users once they join
199
+ the room.
200
+ </Text>
85
201
  </View>
86
202
  <Spacer size={8} />
87
- <Text style={[styles.subHeading, isNotValidated && styles.errorTxt]}>
88
- {selectedValues.length === 0 ? ddError : ddInfo}
89
- </Text>
203
+ {inputTranslationConfig?.targets.length === 10 && (
204
+ <Text style={[styles.subHeading, styles.errorTxt]}>
205
+ {maxLangValInfo}
206
+ </Text>
207
+ )}
90
208
  <Spacer size={32} />
91
209
  <View
92
210
  style={isDesktop ? styles.btnContainer : styles.btnContainerMobile}>
93
211
  <View style={isDesktop && {flex: 1}}>
94
212
  <TertiaryButton
95
- containerStyle={{
96
- width: '100%',
97
- height: 48,
98
- paddingVertical: 12,
99
- paddingHorizontal: 12,
100
- borderRadius: ThemeConfig.BorderRadius.medium,
101
- }}
213
+ containerStyle={styles.button}
102
214
  text={cancelBtnLabel}
103
215
  textStyle={styles.btnText}
104
216
  onPress={() => props.setModalVisible(false)}
@@ -110,30 +222,14 @@ const LanguageSelectorPopup = (props: LanguageSelectorPopup) => {
110
222
  />
111
223
  <View style={isDesktop && {flex: 1}}>
112
224
  <PrimaryButton
113
- containerStyle={{
114
- minWidth: 'auto',
115
- width: '100%',
116
- borderRadius: ThemeConfig.BorderRadius.medium,
117
- height: 48,
118
- paddingVertical: 12,
119
- paddingHorizontal: 12,
120
- }}
121
- disabled={selectedValues.length === 0}
225
+ containerStyle={styles.button}
226
+ disabled={
227
+ isNotValidated ||
228
+ !hasConfigChanged(translationConfig, inputTranslationConfig)
229
+ }
122
230
  text={ConfirmBtnLabel}
123
231
  textStyle={styles.btnText}
124
- onPress={() => {
125
- console.log(selectedValues);
126
- console.log(language);
127
-
128
- if (selectedValues.length === 0) {
129
- // setError(true);
130
- return;
131
- }
132
- const lang1 = language.slice().sort().toString();
133
- const lang2 = selectedValues.slice().sort().toString();
134
- const isLangChanged = lang1 !== lang2 || !isSTTActive;
135
- props.onConfirm(isLangChanged, selectedValues);
136
- }}
232
+ onPress={onConfirmPress}
137
233
  />
138
234
  </View>
139
235
  </View>
@@ -152,26 +248,41 @@ const styles = StyleSheet.create({
152
248
  justifyContent: 'center',
153
249
  alignItems: 'center',
154
250
  },
155
- btnText: {
156
- fontWeight: '600',
157
- fontSize: 16,
158
- lineHeight: 24,
159
- },
160
251
  btnContainerMobile: {
161
252
  flexDirection: 'column-reverse',
162
253
  },
163
254
  contentContainer: {
164
255
  padding: 24,
165
- maxWidth: 446,
256
+ maxWidth: 500,
166
257
  width: '100%',
167
258
  },
168
- heading: {
169
- fontFamily: ThemeConfig.FontFamily.sansPro,
259
+ button: {
260
+ width: '100%',
261
+ minWidth: 'auto',
262
+ height: 48,
263
+ paddingVertical: 12,
264
+ paddingHorizontal: 12,
265
+ borderRadius: ThemeConfig.BorderRadius.medium,
266
+ },
267
+ btnText: {
170
268
  fontWeight: '600',
171
- fontSize: 22,
269
+ fontSize: 16,
172
270
  lineHeight: 24,
271
+ },
272
+ labelText: {
273
+ fontFamily: ThemeConfig.FontFamily.sansPro,
274
+ fontWeight: '400',
275
+ fontSize: ThemeConfig.FontSize.small,
276
+ lineHeight: 20,
173
277
  color: $config.FONT_COLOR,
174
278
  },
279
+ infoText: {
280
+ fontFamily: ThemeConfig.FontFamily.sansPro,
281
+ fontWeight: '400',
282
+ fontSize: ThemeConfig.FontSize.tiny,
283
+ lineHeight: 12,
284
+ color: $config.SEMANTIC_NEUTRAL,
285
+ },
175
286
  subHeading: {
176
287
  fontFamily: ThemeConfig.FontFamily.sansPro,
177
288
  fontWeight: '400',
@@ -179,7 +290,6 @@ const styles = StyleSheet.create({
179
290
  lineHeight: 20,
180
291
  color: $config.FONT_COLOR + hexadecimalTransparency['70%'],
181
292
  },
182
-
183
293
  errorTxt: {
184
294
  color: $config.SEMANTIC_WARNING,
185
295
  fontWeight: '600',
@@ -36,6 +36,7 @@ import DownloadTranscriptBtn from './DownloadTranscriptBtn';
36
36
  import {useString} from '../../../src/utils/useString';
37
37
  import {
38
38
  sttSettingSpokenLanguageText,
39
+ sttSettingTranslationLanguageText,
39
40
  sttTranscriptPanelNoSearchResultsFoundText,
40
41
  sttTranscriptPanelSearchText,
41
42
  sttTranscriptPanelViewLatestText,
@@ -50,11 +51,6 @@ type NativeStreamMessageArgs = [{}, number, number, Uint8Array, number, number];
50
51
  type StreamMessageArgs = WebStreamMessageArgs | NativeStreamMessageArgs;
51
52
 
52
53
  const Transcript = (props: TranscriptProps) => {
53
- const settingSpokenLanguageLabel = useString(sttSettingSpokenLanguageText)();
54
- const searchText = useString(sttTranscriptPanelSearchText)();
55
- const noresults = useString(sttTranscriptPanelNoSearchResultsFoundText)();
56
- const viewlatest = useString(sttTranscriptPanelViewLatestText)();
57
-
58
54
  const isSmall = useIsSmall();
59
55
  const {currentLayout} = useLayout();
60
56
  const {showHeader = true} = props;
@@ -63,9 +59,20 @@ const Transcript = (props: TranscriptProps) => {
63
59
  isLangChangeInProgress,
64
60
  isSTTListenerAdded,
65
61
  setIsSTTListenerAdded,
62
+ getBotOwnerUid,
63
+ isSTTActive,
66
64
  } = useCaption();
67
65
 
66
+ const settingSpokenLanguageLabel = useString(sttSettingSpokenLanguageText)();
67
+ const settingTranslationLanguageLabel = useString<boolean>(
68
+ sttSettingTranslationLanguageText,
69
+ )(isSTTActive);
70
+ const searchText = useString(sttTranscriptPanelSearchText)();
71
+ const noresults = useString(sttTranscriptPanelNoSearchResultsFoundText)();
72
+ const viewlatest = useString(sttTranscriptPanelViewLatestText)();
73
+
68
74
  const data = meetingTranscript; // Object.entries(transcript);
75
+ console.log('[STT_PER_USER_BOT] meetingTranscript data: ', data);
69
76
 
70
77
  const [showButton, setShowButton] = React.useState(false);
71
78
 
@@ -103,6 +110,19 @@ const Transcript = (props: TranscriptProps) => {
103
110
 
104
111
  const renderItem = ({item}) => {
105
112
  return item.uid.toString().indexOf('langUpdate') !== -1 ? (
113
+ <View style={styles.langChangeContainer}>
114
+ <ImageIcon
115
+ iconType="plain"
116
+ iconSize={20}
117
+ tintColor={$config.PRIMARY_ACTION_BRAND_COLOR}
118
+ name={'globe'}
119
+ />
120
+
121
+ <Text style={styles.langChange}>
122
+ {defaultContent[item?.uid?.split('-')[1]]?.name + ' ' + item.text}
123
+ </Text>
124
+ </View>
125
+ ) : item.uid.toString().indexOf('translationUpdate') !== -1 ? (
106
126
  <View style={styles.langChangeContainer}>
107
127
  <ImageIcon
108
128
  iconType="plain"
@@ -112,15 +132,17 @@ const Transcript = (props: TranscriptProps) => {
112
132
  />
113
133
 
114
134
  <Text style={styles.langChange}>
115
- {defaultContent[item?.uid?.split('-')[1]].name + ' ' + item.text}
135
+ {defaultContent[item?.uid?.split('-')[1]]?.name + ' has ' + item.text}
116
136
  </Text>
117
137
  </View>
118
138
  ) : (
119
139
  <TranscriptText
120
- user={defaultContent[item.uid].name}
140
+ user={defaultContent[getBotOwnerUid(item.uid)]?.name || 'Speaker'}
121
141
  time={item?.time}
122
142
  value={item.text}
143
+ translations={item.translations}
123
144
  searchQuery={searchQuery}
145
+ selectedTranslationLanguage={item.selectedTranslationLanguage}
124
146
  />
125
147
  );
126
148
  };
@@ -164,9 +186,20 @@ const Transcript = (props: TranscriptProps) => {
164
186
  const handleSearch = (text: string) => {
165
187
  setSearchQuery(text);
166
188
  // Filter the data based on the search query
167
- const filteredResults = meetingTranscript.filter(item =>
168
- item.text.toLowerCase().includes(text.toLowerCase()),
169
- );
189
+ const filteredResults = meetingTranscript.filter(item => {
190
+ const searchText = text.toLowerCase();
191
+ // Search in original text
192
+ if (item.text.toLowerCase().includes(searchText)) {
193
+ return true;
194
+ }
195
+ // Search in translations if available
196
+ if (item.translations) {
197
+ return item.translations.some(translation =>
198
+ translation.text.toLowerCase().includes(searchText),
199
+ );
200
+ }
201
+ return false;
202
+ });
170
203
  setShowButton(false);
171
204
  setSearchResults(filteredResults);
172
205
  // Scroll to the top of the FlatList when searching
@@ -180,6 +213,7 @@ const Transcript = (props: TranscriptProps) => {
180
213
  };
181
214
 
182
215
  const handleStreamMessageCallback = (...args: StreamMessageArgs) => {
216
+ console.log('[STT_PER_USER_BOT] handleStreamMessageCallback', args);
183
217
  setIsSTTListenerAdded(true);
184
218
  if (isWebInternal()) {
185
219
  const [uid, data] = args as WebStreamMessageArgs;
@@ -260,7 +294,7 @@ const Transcript = (props: TranscriptProps) => {
260
294
  {isLangChangeInProgress ? (
261
295
  <View style={{flex: 1}}>
262
296
  <Loading
263
- text={settingSpokenLanguageLabel}
297
+ text={settingTranslationLanguageLabel}
264
298
  background="transparent"
265
299
  indicatorColor={$config.FONT_COLOR + hexadecimalTransparency['70%']}
266
300
  textColor={$config.FONT_COLOR + hexadecimalTransparency['70%']}
@@ -438,6 +472,7 @@ export const styles = StyleSheet.create({
438
472
  langChangeContainer: {
439
473
  marginBottom: 20,
440
474
  flexDirection: 'row',
475
+ alignItems: 'center',
441
476
  },
442
477
  footer: {
443
478
  borderWidth: 1,