agora-appbuilder-core 4.1.13-beta.1 → 4.1.13-beta.3
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 +1 -1
- package/template/customization-api/temp.ts +1 -9
- package/template/defaultConfig.js +2 -2
- package/template/package.json +1 -1
- package/template/src/assets/font-styles.css +4 -0
- package/template/src/assets/fonts/icomoon.ttf +0 -0
- package/template/src/assets/selection.json +1 -1
- package/template/src/atoms/ActionMenu.tsx +1 -1
- package/template/src/atoms/CustomIcon.tsx +1 -0
- package/template/src/atoms/Popup.tsx +1 -1
- package/template/src/components/Controls.tsx +22 -41
- package/template/src/components/whiteboard/StrokeWidthTool.tsx +1 -5
- package/template/src/components/whiteboard/WhiteboardButton.tsx +1 -9
- package/template/src/components/whiteboard/WhiteboardCanvas.tsx +1 -9
- package/template/src/components/whiteboard/WhiteboardConfigure.tsx +215 -95
- package/template/src/components/whiteboard/WhiteboardCursor.tsx +9 -17
- package/template/src/components/whiteboard/WhiteboardToolBox.tsx +1 -15
- package/template/src/components/whiteboard/WhiteboardView.tsx +1 -9
- package/template/src/components/whiteboard/WhiteboardWidget.tsx +1 -4
- package/template/src/components/whiteboard/WhiteboardWrapper.tsx +2 -3
- package/template/src/language/default-labels/videoCallScreenLabels.ts +9 -9
- package/template/src/pages/VideoCall.tsx +2 -1
- package/template/src/pages/video-call/ActionSheetContent.tsx +0 -7
- package/template/src/pages/video-call/SidePanelHeader.tsx +80 -63
- package/template/src/rtm-events/constants.ts +9 -0
- package/template/src/subComponents/caption/Caption.tsx +4 -20
- package/template/src/subComponents/caption/CaptionContainer.tsx +262 -250
- package/template/src/subComponents/caption/CaptionIcon.tsx +6 -4
- package/template/src/subComponents/caption/CaptionText.tsx +26 -20
- package/template/src/subComponents/caption/LanguageSelectorPopup.tsx +30 -142
- package/template/src/subComponents/caption/Transcript.tsx +77 -32
- package/template/src/subComponents/caption/TranscriptIcon.tsx +7 -6
- package/template/src/subComponents/caption/TranslateActionMenu.tsx +128 -0
- package/template/src/subComponents/caption/useCaption.tsx +645 -482
- package/template/src/subComponents/caption/useSTTAPI.tsx +25 -4
- package/template/src/subComponents/caption/useStreamMessageUtils.native.ts +1 -1
- package/template/src/subComponents/caption/useStreamMessageUtils.ts +1 -1
- package/template/src/subComponents/caption/utils.ts +48 -40
- package/template/src/components/whiteboard/FastBoardView.tsx +0 -227
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {createHook} from 'customization-implementation';
|
|
2
2
|
import React, {useContext} from 'react';
|
|
3
|
-
import {LanguageType, getLanguageLabel
|
|
3
|
+
import {LanguageType, getLanguageLabel} from './utils';
|
|
4
4
|
import useSTTAPI, {STTAPIResponse} from './useSTTAPI';
|
|
5
5
|
import {useLocalUid} from '../../../agora-rn-uikit';
|
|
6
6
|
import {logger, LogSource} from '../../logger/AppBuilderLogger';
|
|
@@ -12,31 +12,56 @@ import {useString} from '../../utils/useString';
|
|
|
12
12
|
import {
|
|
13
13
|
sttStartError,
|
|
14
14
|
sttUpdateError,
|
|
15
|
+
sttSpokenLanguageToastHeading,
|
|
16
|
+
sttSpokenLanguageToastSubHeading,
|
|
17
|
+
sttSpokenLanguageToastSubHeadingDataInterface,
|
|
15
18
|
} from '../../language/default-labels/videoCallScreenLabels';
|
|
16
19
|
import chatContext from '../../components/ChatContext';
|
|
20
|
+
import {useRoomInfo} from '../../components/room-info/useRoomInfo';
|
|
21
|
+
import {useContent} from 'customization-api';
|
|
22
|
+
|
|
23
|
+
// Types
|
|
24
|
+
type GlobalSttState = {
|
|
25
|
+
globalSttEnabled: boolean;
|
|
26
|
+
globalSpokenLanguage: LanguageType;
|
|
27
|
+
globalTranslationTargets: LanguageType[];
|
|
28
|
+
initiatorName?: string;
|
|
29
|
+
};
|
|
17
30
|
|
|
18
|
-
type
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
31
|
+
type TargetChange = {
|
|
32
|
+
prev: LanguageType | null;
|
|
33
|
+
next: LanguageType | null;
|
|
34
|
+
reason?: 'user' | 'spoken-language-changed' | 'auto-start';
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
type SttQueueItem = {
|
|
38
|
+
state: GlobalSttState;
|
|
39
|
+
isLocal: boolean;
|
|
40
|
+
targetChange?: TargetChange;
|
|
22
41
|
};
|
|
23
42
|
|
|
24
43
|
export type LanguageTranslationConfig = {
|
|
25
|
-
source: LanguageType[]; // 'en-US'
|
|
44
|
+
source: LanguageType[]; // ['en-US']
|
|
26
45
|
targets: LanguageType[]; // ['zh-CN', 'ja-JP']
|
|
27
|
-
autoPopulate?: boolean; // e.g. if auto-populated from others
|
|
28
46
|
};
|
|
29
47
|
|
|
30
|
-
export type
|
|
48
|
+
export type STTViewMode = 'original-and-translated' | 'translated';
|
|
49
|
+
|
|
50
|
+
type TranslationItem = {
|
|
51
|
+
lang: string;
|
|
52
|
+
text: string;
|
|
53
|
+
isFinal: boolean;
|
|
54
|
+
};
|
|
31
55
|
|
|
32
56
|
export type TranscriptItem = {
|
|
57
|
+
name: string;
|
|
33
58
|
uid: string;
|
|
34
59
|
time: number;
|
|
35
60
|
text: string;
|
|
36
61
|
translations?: TranslationItem[];
|
|
37
62
|
// Stores which translation language was active when this transcript was created
|
|
38
63
|
// This preserves historical context when users switch translation languages mid-meeting
|
|
39
|
-
selectedTranslationLanguage?:
|
|
64
|
+
selectedTranslationLanguage?: LanguageType;
|
|
40
65
|
};
|
|
41
66
|
|
|
42
67
|
type CaptionObj = {
|
|
@@ -47,6 +72,28 @@ type CaptionObj = {
|
|
|
47
72
|
};
|
|
48
73
|
};
|
|
49
74
|
|
|
75
|
+
// helper
|
|
76
|
+
// (sorted) version of the target list.
|
|
77
|
+
const normalizeTargets = (arr: LanguageType[]) => [...(arr || [])].sort();
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* This helpher compares the STT_GLOBAL_STATE
|
|
81
|
+
* When a new user joins a call, they replay all previously persisted
|
|
82
|
+
* STT_GLOBAL_STATE events sent by other participants, as STT_GLOBAL_STATE
|
|
83
|
+
* is a session persistance event
|
|
84
|
+
* Without this check, the joining user would:
|
|
85
|
+
* - run start/update api multiple times as it will read event
|
|
86
|
+
* from all users attributes
|
|
87
|
+
*/
|
|
88
|
+
const isSameState = (prev: GlobalSttState, next: GlobalSttState) => {
|
|
89
|
+
return (
|
|
90
|
+
prev.globalSttEnabled === next.globalSttEnabled &&
|
|
91
|
+
prev.globalSpokenLanguage === next.globalSpokenLanguage &&
|
|
92
|
+
JSON.stringify(normalizeTargets(prev.globalTranslationTargets)) ===
|
|
93
|
+
JSON.stringify(normalizeTargets(next.globalTranslationTargets))
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
50
97
|
export const CaptionContext = React.createContext<{
|
|
51
98
|
// for caption btn state
|
|
52
99
|
isCaptionON: boolean;
|
|
@@ -56,24 +103,26 @@ export const CaptionContext = React.createContext<{
|
|
|
56
103
|
isSTTError: boolean;
|
|
57
104
|
setIsSTTError: React.Dispatch<React.SetStateAction<boolean>>;
|
|
58
105
|
|
|
59
|
-
// to check if stt is active in the call
|
|
106
|
+
// to check if stt is active in the call :derived from globalSttState
|
|
60
107
|
isSTTActive: boolean;
|
|
61
|
-
|
|
108
|
+
|
|
109
|
+
// flag to check if STT dependencies are ready (all required data loaded)
|
|
110
|
+
// Used to disable caption/transcript buttons until system is fully initialized
|
|
111
|
+
sttDepsReady: boolean;
|
|
62
112
|
|
|
63
113
|
// holds the language selection for stt (deprecated - use sttForm instead)
|
|
64
114
|
// language: LanguageType[];
|
|
65
115
|
// setLanguage: React.Dispatch<React.SetStateAction<LanguageType[]>>;
|
|
66
116
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
>;
|
|
117
|
+
globalSttState: GlobalSttState;
|
|
118
|
+
confirmSpokenLanguageChange: (newLang: LanguageType) => Promise<void>;
|
|
119
|
+
confirmTargetLanguageChange: (newTargetLang: LanguageType) => Promise<void>;
|
|
71
120
|
|
|
72
|
-
captionViewMode:
|
|
73
|
-
setCaptionViewMode: React.Dispatch<React.SetStateAction<
|
|
121
|
+
captionViewMode: STTViewMode;
|
|
122
|
+
setCaptionViewMode: React.Dispatch<React.SetStateAction<STTViewMode>>;
|
|
74
123
|
|
|
75
|
-
transcriptViewMode:
|
|
76
|
-
setTranscriptViewMode: React.Dispatch<React.SetStateAction<
|
|
124
|
+
transcriptViewMode: STTViewMode;
|
|
125
|
+
setTranscriptViewMode: React.Dispatch<React.SetStateAction<STTViewMode>>;
|
|
77
126
|
|
|
78
127
|
// holds meeting transcript
|
|
79
128
|
meetingTranscript: TranscriptItem[];
|
|
@@ -83,12 +132,6 @@ export const CaptionContext = React.createContext<{
|
|
|
83
132
|
isLangChangeInProgress: boolean;
|
|
84
133
|
setIsLangChangeInProgress: React.Dispatch<React.SetStateAction<boolean>>;
|
|
85
134
|
|
|
86
|
-
// holds status of translation language change process
|
|
87
|
-
isTranslationChangeInProgress: boolean;
|
|
88
|
-
setIsTranslationChangeInProgress: React.Dispatch<
|
|
89
|
-
React.SetStateAction<boolean>
|
|
90
|
-
>;
|
|
91
|
-
|
|
92
135
|
// holds live captions
|
|
93
136
|
captionObj: CaptionObj;
|
|
94
137
|
setCaptionObj: React.Dispatch<React.SetStateAction<CaptionObj>>;
|
|
@@ -100,13 +143,9 @@ export const CaptionContext = React.createContext<{
|
|
|
100
143
|
activeSpeakerRef: React.MutableRefObject<string>;
|
|
101
144
|
prevSpeakerRef: React.MutableRefObject<string>;
|
|
102
145
|
|
|
103
|
-
selectedTranslationLanguage:
|
|
104
|
-
setSelectedTranslationLanguage: React.Dispatch<React.SetStateAction<string>>;
|
|
146
|
+
selectedTranslationLanguage: LanguageType;
|
|
105
147
|
// Ref for translation language - prevents stale closures in callbacks
|
|
106
|
-
selectedTranslationLanguageRef: React.MutableRefObject<
|
|
107
|
-
// Ref for translation config - prevents stale closures in callbacks
|
|
108
|
-
translationConfigRef: React.MutableRefObject<LanguageTranslationConfig>;
|
|
109
|
-
|
|
148
|
+
selectedTranslationLanguageRef: React.MutableRefObject<LanguageType | null>;
|
|
110
149
|
// Stores spoken languages of all remote users (userUid -> spoken language)
|
|
111
150
|
// Used to auto-populate target languages for new users
|
|
112
151
|
remoteSpokenLanguages: Record<string, LanguageType>;
|
|
@@ -114,14 +153,13 @@ export const CaptionContext = React.createContext<{
|
|
|
114
153
|
React.SetStateAction<Record<string, LanguageType>>
|
|
115
154
|
>;
|
|
116
155
|
|
|
117
|
-
handleTranslateConfigChange: (
|
|
118
|
-
inputTranslationConfig: LanguageTranslationConfig,
|
|
119
|
-
) => Promise<void>;
|
|
120
156
|
startSTTBotSession: (
|
|
121
157
|
newConfig: LanguageTranslationConfig,
|
|
122
158
|
) => Promise<STTAPIResponse>;
|
|
123
159
|
updateSTTBotSession: (
|
|
124
160
|
newConfig: LanguageTranslationConfig,
|
|
161
|
+
isLocal: boolean,
|
|
162
|
+
targetChange?: TargetChange,
|
|
125
163
|
) => Promise<STTAPIResponse>;
|
|
126
164
|
stopSTTBotSession: () => Promise<void>;
|
|
127
165
|
|
|
@@ -133,14 +171,9 @@ export const CaptionContext = React.createContext<{
|
|
|
133
171
|
isSTTError: false,
|
|
134
172
|
setIsSTTError: () => {},
|
|
135
173
|
isSTTActive: false,
|
|
136
|
-
|
|
174
|
+
sttDepsReady: false,
|
|
137
175
|
// language: ['en-US'],
|
|
138
176
|
// setLanguage: () => {},
|
|
139
|
-
translationConfig: {
|
|
140
|
-
source: [],
|
|
141
|
-
targets: [],
|
|
142
|
-
},
|
|
143
|
-
setTranslationConfig: () => {},
|
|
144
177
|
captionViewMode: 'translated',
|
|
145
178
|
setCaptionViewMode: () => {},
|
|
146
179
|
transcriptViewMode: 'translated',
|
|
@@ -149,8 +182,6 @@ export const CaptionContext = React.createContext<{
|
|
|
149
182
|
setMeetingTranscript: () => {},
|
|
150
183
|
isLangChangeInProgress: false,
|
|
151
184
|
setIsLangChangeInProgress: () => {},
|
|
152
|
-
isTranslationChangeInProgress: false,
|
|
153
|
-
setIsTranslationChangeInProgress: () => {},
|
|
154
185
|
captionObj: {},
|
|
155
186
|
setCaptionObj: () => {},
|
|
156
187
|
isSTTListenerAdded: false,
|
|
@@ -158,16 +189,21 @@ export const CaptionContext = React.createContext<{
|
|
|
158
189
|
activeSpeakerRef: {current: ''},
|
|
159
190
|
prevSpeakerRef: {current: ''},
|
|
160
191
|
selectedTranslationLanguage: '',
|
|
161
|
-
|
|
162
|
-
selectedTranslationLanguageRef: {current: ''},
|
|
163
|
-
translationConfigRef: {current: {source: [], targets: []}},
|
|
192
|
+
selectedTranslationLanguageRef: {current: null},
|
|
164
193
|
remoteSpokenLanguages: {},
|
|
165
194
|
setRemoteSpokenLanguages: () => {},
|
|
166
|
-
handleTranslateConfigChange: async () => {},
|
|
167
195
|
startSTTBotSession: async () => ({success: false}),
|
|
168
196
|
updateSTTBotSession: async () => ({success: false}),
|
|
169
197
|
stopSTTBotSession: async () => {},
|
|
170
198
|
getBotOwnerUid: (botUid: string | number) => botUid,
|
|
199
|
+
globalSttState: {
|
|
200
|
+
globalSttEnabled: false,
|
|
201
|
+
globalSpokenLanguage: '',
|
|
202
|
+
globalTranslationTargets: [],
|
|
203
|
+
initiatorName: '',
|
|
204
|
+
},
|
|
205
|
+
confirmSpokenLanguageChange: async () => {},
|
|
206
|
+
confirmTargetLanguageChange: async () => {},
|
|
171
207
|
});
|
|
172
208
|
|
|
173
209
|
interface CaptionProviderProps {
|
|
@@ -179,317 +215,224 @@ const CaptionProvider: React.FC<CaptionProviderProps> = ({
|
|
|
179
215
|
callActive,
|
|
180
216
|
children,
|
|
181
217
|
}) => {
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
//
|
|
218
|
+
const {
|
|
219
|
+
data: {isHost, roomId},
|
|
220
|
+
} = useRoomInfo();
|
|
221
|
+
// Toast message
|
|
222
|
+
const heading = useString<'Set' | 'Changed'>(sttSpokenLanguageToastHeading);
|
|
223
|
+
const subheading = useString<sttSpokenLanguageToastSubHeadingDataInterface>(
|
|
224
|
+
sttSpokenLanguageToastSubHeading,
|
|
225
|
+
);
|
|
186
226
|
|
|
187
|
-
|
|
188
|
-
const [translationConfig, setTranslationConfig] =
|
|
189
|
-
React.useState<LanguageTranslationConfig>({
|
|
190
|
-
source: [],
|
|
191
|
-
targets: [],
|
|
192
|
-
});
|
|
227
|
+
const [isCaptionON, setIsCaptionON] = React.useState<boolean>(false);
|
|
193
228
|
|
|
194
229
|
const [captionViewMode, setCaptionViewMode] =
|
|
195
|
-
React.useState<
|
|
196
|
-
|
|
230
|
+
React.useState<STTViewMode>('translated');
|
|
197
231
|
const [transcriptViewMode, setTranscriptViewMode] =
|
|
198
|
-
React.useState<
|
|
232
|
+
React.useState<STTViewMode>('original-and-translated');
|
|
199
233
|
|
|
234
|
+
const [isSTTError, setIsSTTError] = React.useState<boolean>(false);
|
|
200
235
|
const [isLangChangeInProgress, setIsLangChangeInProgress] =
|
|
201
236
|
React.useState<boolean>(false);
|
|
202
|
-
|
|
203
|
-
|
|
237
|
+
|
|
238
|
+
const [captionObj, setCaptionObj] = React.useState<CaptionObj>({});
|
|
204
239
|
const [meetingTranscript, setMeetingTranscript] = React.useState<
|
|
205
240
|
TranscriptItem[]
|
|
206
241
|
>([]);
|
|
207
|
-
|
|
208
|
-
console.log('[STT_PER_USER_BOT] captionObj: ', captionObj);
|
|
242
|
+
|
|
209
243
|
const [isSTTListenerAdded, setIsSTTListenerAdded] =
|
|
210
244
|
React.useState<boolean>(false);
|
|
211
245
|
const [activeSpeakerUID, setActiveSpeakerUID] = React.useState<string>('');
|
|
212
246
|
const [prevActiveSpeakerUID, setPrevActiveSpeakerUID] =
|
|
213
247
|
React.useState<string>('');
|
|
214
|
-
const [selectedTranslationLanguage, setSelectedTranslationLanguage] =
|
|
215
|
-
React.useState<string>('');
|
|
216
248
|
const [remoteSpokenLanguages, setRemoteSpokenLanguages] = React.useState<
|
|
217
249
|
Record<string, LanguageType>
|
|
218
250
|
>({});
|
|
219
251
|
|
|
252
|
+
// Default content
|
|
253
|
+
const {defaultContent} = useContent();
|
|
254
|
+
const defaultContentRef = React.useRef(defaultContent);
|
|
255
|
+
React.useEffect(() => {
|
|
256
|
+
defaultContentRef.current = defaultContent;
|
|
257
|
+
}, [defaultContent]);
|
|
258
|
+
|
|
259
|
+
// Active/prev speaker tracking (exposed as refs)
|
|
220
260
|
const activeSpeakerRef = React.useRef('');
|
|
221
261
|
const prevSpeakerRef = React.useRef('');
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
262
|
+
|
|
263
|
+
// Global STT shared state
|
|
264
|
+
const [globalSttState, setGlobalSttState] = React.useState<GlobalSttState>({
|
|
265
|
+
globalSttEnabled: false,
|
|
266
|
+
globalSpokenLanguage: '',
|
|
267
|
+
globalTranslationTargets: [],
|
|
268
|
+
initiatorName: '',
|
|
226
269
|
});
|
|
270
|
+
const globalSttStateRef = React.useRef(globalSttState);
|
|
271
|
+
React.useEffect(() => {
|
|
272
|
+
globalSttStateRef.current = globalSttState;
|
|
273
|
+
}, [globalSttState]);
|
|
227
274
|
|
|
228
|
-
//
|
|
275
|
+
// Queue for all stt operations
|
|
276
|
+
const sttEventQueueRef = React.useRef<SttQueueItem[]>([]);
|
|
277
|
+
const isProcessingSttEventRef = React.useRef(false);
|
|
278
|
+
const hasFlushedSttQueueRef = React.useRef(false);
|
|
279
|
+
|
|
280
|
+
// Selected Translated language
|
|
281
|
+
const [selectedTranslationLanguage, setSelectedTranslationLanguage] =
|
|
282
|
+
React.useState<LanguageType | null>(null);
|
|
283
|
+
const selectedTranslationLanguageRef = React.useRef<LanguageType | null>(
|
|
284
|
+
null,
|
|
285
|
+
);
|
|
229
286
|
React.useEffect(() => {
|
|
230
287
|
selectedTranslationLanguageRef.current = selectedTranslationLanguage;
|
|
231
288
|
}, [selectedTranslationLanguage]);
|
|
232
289
|
|
|
233
|
-
|
|
234
|
-
React.useEffect(() => {
|
|
235
|
-
translationConfigRef.current = translationConfig;
|
|
236
|
-
}, [translationConfig]);
|
|
290
|
+
const isSTTActive = globalSttState.globalSttEnabled;
|
|
237
291
|
|
|
238
|
-
//
|
|
292
|
+
// STT API methods
|
|
239
293
|
const {start, stop, update} = useSTTAPI();
|
|
240
294
|
|
|
241
295
|
const localUid = useLocalUid();
|
|
242
296
|
const username = useGetName();
|
|
243
297
|
const {hasUserJoinedRTM} = useContext(chatContext);
|
|
244
298
|
|
|
299
|
+
// Bot UID for this user
|
|
245
300
|
const [localBotUid, setLocalBotUid] = React.useState<number | null>(null);
|
|
301
|
+
const localBotUidRef = React.useRef<number | null>(null);
|
|
302
|
+
React.useEffect(() => {
|
|
303
|
+
localBotUidRef.current = localBotUid;
|
|
304
|
+
}, [localBotUid]);
|
|
305
|
+
|
|
306
|
+
// Host flag
|
|
307
|
+
const isHostRef = React.useRef(isHost);
|
|
308
|
+
React.useEffect(() => {
|
|
309
|
+
isHostRef.current = isHost;
|
|
310
|
+
}, [isHost]);
|
|
246
311
|
|
|
247
312
|
// i18n labels for error toasts
|
|
248
313
|
const startErrorLabel = useString(sttStartError)();
|
|
249
314
|
const updateErrorLabel = useString(sttUpdateError)();
|
|
250
315
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
// Silent update function to update local user's STT config without showing progress bar
|
|
260
|
-
const silentUpdateSTT = React.useCallback(
|
|
261
|
-
async (newtargetLanguages: LanguageType[]) => {
|
|
262
|
-
try {
|
|
263
|
-
// Merge new target languages with existing ones and keep unique
|
|
264
|
-
const currentTargets = translationConfigRef.current?.targets || [];
|
|
265
|
-
const mergedTargets = Array.from(
|
|
266
|
-
new Set([...newtargetLanguages, ...currentTargets]),
|
|
267
|
-
);
|
|
268
|
-
|
|
269
|
-
const newConfig: LanguageTranslationConfig = {
|
|
270
|
-
source: translationConfigRef.current?.source || [],
|
|
271
|
-
targets: mergedTargets,
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
console.log(
|
|
275
|
-
'[STT_PER_USER_BOT] Silent updating with merged targets:',
|
|
276
|
-
'newRemoteLanguages:',
|
|
277
|
-
newtargetLanguages,
|
|
278
|
-
'existingTargets:',
|
|
279
|
-
currentTargets,
|
|
280
|
-
'mergedTargets:',
|
|
281
|
-
mergedTargets,
|
|
282
|
-
);
|
|
283
|
-
|
|
284
|
-
// Call update API without showing progress bar (don't set isLangChangeInProgress)
|
|
285
|
-
const result = await update(localBotUid, newConfig);
|
|
316
|
+
// --- Derived readiness flag for STT ---
|
|
317
|
+
const sttDepsReady =
|
|
318
|
+
!!localUid &&
|
|
319
|
+
!!localBotUid &&
|
|
320
|
+
!!hasUserJoinedRTM &&
|
|
321
|
+
!!callActive &&
|
|
322
|
+
!!(roomId?.host || roomId?.attendee);
|
|
286
323
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
setIsSTTError(false);
|
|
290
|
-
|
|
291
|
-
logger.log(
|
|
292
|
-
LogSource.NetworkRest,
|
|
293
|
-
'stt',
|
|
294
|
-
'Local user STT updated silently',
|
|
295
|
-
{newtargetLanguages, mergedTargets, botUid: localBotUid},
|
|
296
|
-
);
|
|
297
|
-
} else {
|
|
298
|
-
setIsSTTError(true);
|
|
299
|
-
logger.error(
|
|
300
|
-
LogSource.NetworkRest,
|
|
301
|
-
'stt',
|
|
302
|
-
'Failed to silently update local user STT',
|
|
303
|
-
result.error,
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
} catch (error) {
|
|
307
|
-
setIsSTTError(true);
|
|
308
|
-
logger.error(
|
|
309
|
-
LogSource.NetworkRest,
|
|
310
|
-
'stt',
|
|
311
|
-
'Error in silentUpdateSTT',
|
|
312
|
-
error,
|
|
313
|
-
);
|
|
314
|
-
}
|
|
315
|
-
},
|
|
316
|
-
[localBotUid],
|
|
317
|
-
);
|
|
324
|
+
const sttStartGuardRef = React.useRef(false);
|
|
325
|
+
const sttAutoStartGuardRef = React.useRef(false);
|
|
318
326
|
|
|
327
|
+
// STT dependencues
|
|
328
|
+
const sttDepsReadyRef = React.useRef(false);
|
|
319
329
|
React.useEffect(() => {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
Object.entries(remoteSpokenLanguages)
|
|
323
|
-
.filter(([uid, lang]) => uid !== String(localUid) && lang)
|
|
324
|
-
.map(([, lang]) => lang),
|
|
325
|
-
),
|
|
326
|
-
);
|
|
327
|
-
|
|
328
|
-
// If STT is active, check if received language differs from current target languages
|
|
329
|
-
if (isSTTActive && remoteLangs && remoteLangs.length > 0) {
|
|
330
|
-
const currentTargetLanguages =
|
|
331
|
-
translationConfigRef.current?.targets || [];
|
|
332
|
-
// Only update if any received language is not in current target languages
|
|
333
|
-
const hasTargetsChanged = remoteLangs.some(
|
|
334
|
-
lang => !currentTargetLanguages.includes(lang),
|
|
335
|
-
);
|
|
336
|
-
if (hasTargetsChanged) {
|
|
337
|
-
console.log(
|
|
338
|
-
'[STT_PER_USER_BOT] Spoken language change detected',
|
|
339
|
-
'currentTargets:',
|
|
340
|
-
currentTargetLanguages,
|
|
341
|
-
'receivedSpokenLanguages (unique):',
|
|
342
|
-
remoteLangs,
|
|
343
|
-
);
|
|
344
|
-
// Call silentUpdateSTT directly
|
|
345
|
-
silentUpdateSTT(remoteLangs);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
}, [remoteSpokenLanguages]);
|
|
330
|
+
sttDepsReadyRef.current = sttDepsReady;
|
|
331
|
+
}, [sttDepsReady]);
|
|
349
332
|
|
|
350
|
-
// Listen for spoken language updates from other users
|
|
351
333
|
React.useEffect(() => {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
'[STT_PER_USER_BOT] Received spoken language from user:',
|
|
359
|
-
username,
|
|
360
|
-
'userUid:',
|
|
361
|
-
userUid,
|
|
362
|
-
'spokenLanguage:',
|
|
363
|
-
spokenLanguage,
|
|
364
|
-
);
|
|
365
|
-
|
|
366
|
-
// Update remoteSpokenLanguages with the user's spoken language
|
|
367
|
-
setRemoteSpokenLanguages(prev => ({
|
|
368
|
-
...prev,
|
|
369
|
-
[userUid]: spokenLanguage,
|
|
370
|
-
}));
|
|
371
|
-
} catch (error) {
|
|
372
|
-
logger.error(
|
|
373
|
-
LogSource.Internals,
|
|
374
|
-
'STT',
|
|
375
|
-
'Failed to parse STT_SPOKEN_LANGUAGE event',
|
|
376
|
-
error,
|
|
377
|
-
);
|
|
378
|
-
}
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
events.on(EventNames.STT_SPOKEN_LANGUAGE, handleSpokenLanguage);
|
|
334
|
+
if (sttDepsReadyRef.current && !hasFlushedSttQueueRef.current) {
|
|
335
|
+
hasFlushedSttQueueRef.current = true;
|
|
336
|
+
processSttEventQueue();
|
|
337
|
+
}
|
|
338
|
+
// When deps become ready → flush queue once
|
|
339
|
+
}, [sttDepsReady]);
|
|
382
340
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
};
|
|
341
|
+
// Helper: convert user UID -> bot UID
|
|
342
|
+
const generateBotUidForUser = React.useCallback((userLocalUid: number) => {
|
|
343
|
+
return 900000000 + (userLocalUid % 100000000);
|
|
387
344
|
}, []);
|
|
388
345
|
|
|
389
|
-
//
|
|
346
|
+
// Generate bot UID once deps are ready enough
|
|
390
347
|
React.useEffect(() => {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
'[STT_PER_USER_BOT] User stopped translation:',
|
|
398
|
-
username,
|
|
399
|
-
'userUid:',
|
|
400
|
-
userUid,
|
|
401
|
-
);
|
|
402
|
-
|
|
403
|
-
// Clear translations for this user by setting captionObj translations to empty
|
|
404
|
-
setCaptionObj(prevState => {
|
|
405
|
-
if (prevState[userUid]) {
|
|
406
|
-
return {
|
|
407
|
-
...prevState,
|
|
408
|
-
[userUid]: {
|
|
409
|
-
...prevState[userUid],
|
|
410
|
-
translations: [], // Clear translations
|
|
411
|
-
},
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
|
-
return prevState;
|
|
415
|
-
});
|
|
416
|
-
} catch (error) {
|
|
417
|
-
logger.error(
|
|
418
|
-
LogSource.Internals,
|
|
419
|
-
'STT',
|
|
420
|
-
'Failed to parse USER_STOPPED_TRANSLATION event',
|
|
421
|
-
error,
|
|
422
|
-
);
|
|
423
|
-
}
|
|
424
|
-
};
|
|
348
|
+
if (!localUid || !username || !hasUserJoinedRTM) {
|
|
349
|
+
return;
|
|
350
|
+
} // wait for room info to be ready
|
|
351
|
+
const uid = generateBotUidForUser(localUid);
|
|
352
|
+
setLocalBotUid(uid);
|
|
353
|
+
}, [localUid, username, generateBotUidForUser, hasUserJoinedRTM]);
|
|
425
354
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
355
|
+
const buildSttTranscriptForSourceChanged = (
|
|
356
|
+
prevSpokenLang: LanguageType,
|
|
357
|
+
newSpokenLang: LanguageType,
|
|
358
|
+
) => {
|
|
359
|
+
const spokenLanguageChanged = prevSpokenLang !== newSpokenLang;
|
|
360
|
+
if (!spokenLanguageChanged) {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
let message = '';
|
|
364
|
+
// Spoken lang changed
|
|
365
|
+
if (!prevSpokenLang) {
|
|
366
|
+
// First time STT is enabled
|
|
367
|
+
message = `Spoken language set to "${getLanguageLabel([newSpokenLang])}"`;
|
|
368
|
+
} else {
|
|
369
|
+
message = `Spoken language changed from "${getLanguageLabel([
|
|
370
|
+
prevSpokenLang,
|
|
371
|
+
])}" to "${getLanguageLabel([newSpokenLang])}"`;
|
|
372
|
+
}
|
|
373
|
+
setMeetingTranscript(prev => [
|
|
374
|
+
...prev,
|
|
375
|
+
{
|
|
376
|
+
name: 'langUpdate',
|
|
377
|
+
time: new Date().getTime(),
|
|
378
|
+
uid: `langUpdate-${localUid}`,
|
|
379
|
+
text: message,
|
|
380
|
+
},
|
|
381
|
+
]);
|
|
382
|
+
};
|
|
430
383
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
384
|
+
const buildSttTranscriptForTargetChanged = (
|
|
385
|
+
prevSelectedTargetLang: LanguageType | null,
|
|
386
|
+
newSelectedTargetLang: LanguageType | null,
|
|
387
|
+
reason?: TargetChange['reason'],
|
|
388
|
+
) => {
|
|
389
|
+
const targetLanguageChanged =
|
|
390
|
+
prevSelectedTargetLang !== newSelectedTargetLang;
|
|
391
|
+
if (!targetLanguageChanged) {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
let message = '';
|
|
395
|
+
|
|
396
|
+
if (reason === 'spoken-language-changed') {
|
|
397
|
+
message = `Translation for "${getLanguageLabel([
|
|
398
|
+
prevSelectedTargetLang,
|
|
399
|
+
])}" was turned off because the spoken language changed to ${getLanguageLabel(
|
|
400
|
+
[prevSelectedTargetLang],
|
|
401
|
+
)}`;
|
|
402
|
+
}
|
|
403
|
+
// Target lang changed
|
|
404
|
+
// Case 1: User turned translation OFF
|
|
405
|
+
else if (prevSelectedTargetLang && !newSelectedTargetLang) {
|
|
406
|
+
message = 'Translation turned off';
|
|
407
|
+
}
|
|
408
|
+
// Case 2: User selected ANY new translation
|
|
409
|
+
else {
|
|
410
|
+
message = `Translation set to "${getLanguageLabel([
|
|
411
|
+
newSelectedTargetLang,
|
|
412
|
+
])}"`;
|
|
413
|
+
}
|
|
414
|
+
setMeetingTranscript(prev => [
|
|
415
|
+
...prev,
|
|
416
|
+
{
|
|
417
|
+
name: 'translationUpdate',
|
|
418
|
+
uid: `translationUpdate-${localUid}`,
|
|
419
|
+
time: new Date().getTime(),
|
|
420
|
+
text: message,
|
|
421
|
+
selectedTranslationLanguage: newSelectedTargetLang,
|
|
422
|
+
},
|
|
423
|
+
]);
|
|
424
|
+
};
|
|
439
425
|
|
|
440
426
|
const startSTTBotSession = async (
|
|
441
427
|
newConfig: LanguageTranslationConfig,
|
|
442
428
|
): Promise<STTAPIResponse> => {
|
|
443
|
-
if (!localBotUid || !localUid) {
|
|
444
|
-
console.warn('[STT] Missing localUid or botUid');
|
|
445
|
-
return {
|
|
446
|
-
success: false,
|
|
447
|
-
error: {message: 'Missing localUid or botUid'},
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
|
-
|
|
451
429
|
try {
|
|
452
430
|
setIsLangChangeInProgress(true);
|
|
453
|
-
const result = await start(
|
|
454
|
-
console.log('STT start result: ', result);
|
|
455
|
-
|
|
431
|
+
const result = await start(localBotUidRef.current, newConfig);
|
|
432
|
+
console.log('[STT] start result: ', result);
|
|
456
433
|
if (result.success || result.error?.code === 610) {
|
|
457
434
|
// Success or already started
|
|
458
|
-
setIsSTTActive(true);
|
|
459
435
|
setIsSTTError(false);
|
|
460
|
-
|
|
461
|
-
// Add transcript entry for language change
|
|
462
|
-
// If STT was not active before, this is the first time setting the language
|
|
463
|
-
const actionText = !isSTTActive
|
|
464
|
-
? `has set the spoken language to "${getLanguageLabel(
|
|
465
|
-
newConfig.source,
|
|
466
|
-
)}"`
|
|
467
|
-
: `changed the spoken language from "${getLanguageLabel(
|
|
468
|
-
translationConfig?.source,
|
|
469
|
-
)}" to "${getLanguageLabel(newConfig.source)}"`;
|
|
470
|
-
|
|
471
|
-
setTranslationConfig(newConfig);
|
|
472
|
-
setMeetingTranscript(prev => [
|
|
473
|
-
...prev,
|
|
474
|
-
{
|
|
475
|
-
name: 'langUpdate',
|
|
476
|
-
time: new Date().getTime(),
|
|
477
|
-
uid: `langUpdate-${localUid}`,
|
|
478
|
-
text: actionText,
|
|
479
|
-
},
|
|
480
|
-
]);
|
|
481
|
-
|
|
482
|
-
// Broadcast spoken language to all users
|
|
483
|
-
events.send(
|
|
484
|
-
EventNames.STT_SPOKEN_LANGUAGE,
|
|
485
|
-
JSON.stringify({
|
|
486
|
-
userUid: localUid,
|
|
487
|
-
spokenLanguage: newConfig.source[0],
|
|
488
|
-
username: username,
|
|
489
|
-
}),
|
|
490
|
-
PersistanceLevel.Sender,
|
|
491
|
-
);
|
|
492
|
-
|
|
493
436
|
logger.log(
|
|
494
437
|
LogSource.NetworkRest,
|
|
495
438
|
'stt',
|
|
@@ -497,7 +440,6 @@ const CaptionProvider: React.FC<CaptionProviderProps> = ({
|
|
|
497
440
|
result.data,
|
|
498
441
|
);
|
|
499
442
|
} else {
|
|
500
|
-
// setIsCaptionON(false);
|
|
501
443
|
setIsSTTError(true);
|
|
502
444
|
logger.error(
|
|
503
445
|
LogSource.NetworkRest,
|
|
@@ -505,18 +447,19 @@ const CaptionProvider: React.FC<CaptionProviderProps> = ({
|
|
|
505
447
|
'Failed to start STT',
|
|
506
448
|
result.error,
|
|
507
449
|
);
|
|
508
|
-
// Show error toast: text1 = translated label, text2 = API error
|
|
509
450
|
Toast.show({
|
|
510
451
|
leadingIconName: 'alert',
|
|
511
452
|
type: 'error',
|
|
512
453
|
text1: startErrorLabel,
|
|
513
454
|
text2: result.error?.message || 'Unknown error occurred',
|
|
514
455
|
visibilityTime: 4000,
|
|
456
|
+
primaryBtn: null,
|
|
457
|
+
secondaryBtn: null,
|
|
515
458
|
});
|
|
516
459
|
}
|
|
517
460
|
setIsLangChangeInProgress(false);
|
|
518
461
|
return result;
|
|
519
|
-
} catch (error) {
|
|
462
|
+
} catch (error: any) {
|
|
520
463
|
setIsLangChangeInProgress(false);
|
|
521
464
|
setIsSTTError(true);
|
|
522
465
|
logger.error(LogSource.NetworkRest, 'stt', 'STT start error', error);
|
|
@@ -527,6 +470,8 @@ const CaptionProvider: React.FC<CaptionProviderProps> = ({
|
|
|
527
470
|
text1: startErrorLabel,
|
|
528
471
|
text2: error?.message || 'Unknown error occurred',
|
|
529
472
|
visibilityTime: 4000,
|
|
473
|
+
primaryBtn: null,
|
|
474
|
+
secondaryBtn: null,
|
|
530
475
|
});
|
|
531
476
|
return {
|
|
532
477
|
success: false,
|
|
@@ -537,106 +482,25 @@ const CaptionProvider: React.FC<CaptionProviderProps> = ({
|
|
|
537
482
|
|
|
538
483
|
const updateSTTBotSession = async (
|
|
539
484
|
newConfig: LanguageTranslationConfig,
|
|
485
|
+
isLocal = false,
|
|
486
|
+
targetChange?: TargetChange,
|
|
540
487
|
): Promise<STTAPIResponse> => {
|
|
541
|
-
if (!localBotUid || !localUid) {
|
|
542
|
-
console.warn('[STT] Missing localUid or botUid');
|
|
543
|
-
return {
|
|
544
|
-
success: false,
|
|
545
|
-
error: {message: 'Missing localUid or botUid'},
|
|
546
|
-
};
|
|
547
|
-
}
|
|
548
|
-
|
|
549
488
|
try {
|
|
550
|
-
setIsLangChangeInProgress(true);
|
|
551
|
-
const result = await update(
|
|
552
|
-
|
|
489
|
+
isLocal && setIsLangChangeInProgress(true);
|
|
490
|
+
const result = await update(localBotUidRef.current, newConfig);
|
|
553
491
|
if (result.success) {
|
|
554
|
-
setTranslationConfig(newConfig);
|
|
555
492
|
setIsSTTError(false);
|
|
556
493
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
const spokenLanguageChanged =
|
|
560
|
-
translationConfig?.source[0] !== newConfig.source[0];
|
|
561
|
-
const oldTargetsSorted = (translationConfig?.targets || [])
|
|
562
|
-
.sort()
|
|
563
|
-
.map(lang => getLanguageLabel([lang]))
|
|
564
|
-
.join(', ');
|
|
565
|
-
const newTargetsSorted = (newConfig.targets || [])
|
|
566
|
-
.sort()
|
|
567
|
-
.map(lang => getLanguageLabel([lang]))
|
|
568
|
-
.join(', ');
|
|
569
|
-
const targetsChanged = oldTargetsSorted !== newTargetsSorted;
|
|
570
|
-
|
|
571
|
-
const oldTargetsLength = translationConfig?.targets?.length || 0;
|
|
572
|
-
const newTargetsLength = newConfig.targets?.length || 0;
|
|
573
|
-
const targetsWereDisabled =
|
|
574
|
-
oldTargetsLength > 0 && newTargetsLength === 0;
|
|
575
|
-
const targetsWereEnabled =
|
|
576
|
-
oldTargetsLength === 0 && newTargetsLength > 0;
|
|
577
|
-
|
|
578
|
-
// Build target message once
|
|
579
|
-
let targetMessage = '';
|
|
580
|
-
if (targetsChanged) {
|
|
581
|
-
if (targetsWereDisabled) {
|
|
582
|
-
targetMessage = 'stopped translations';
|
|
583
|
-
} else if (targetsWereEnabled) {
|
|
584
|
-
targetMessage = `enabled translations to "${newTargetsSorted}"`;
|
|
585
|
-
} else {
|
|
586
|
-
targetMessage = `changed target translation languages to "${newTargetsSorted}"`;
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
// Build action text
|
|
591
|
-
let actionText = '';
|
|
592
|
-
if (spokenLanguageChanged && targetsChanged) {
|
|
593
|
-
// Both spoken language and targets changed
|
|
594
|
-
actionText = `changed spoken language from "${getLanguageLabel(
|
|
595
|
-
translationConfig?.source,
|
|
596
|
-
)}" to "${getLanguageLabel(newConfig.source)}" and ${targetMessage}`;
|
|
597
|
-
} else if (spokenLanguageChanged) {
|
|
598
|
-
// Only spoken language changed
|
|
599
|
-
actionText = `changed the spoken language from "${getLanguageLabel(
|
|
600
|
-
translationConfig?.source,
|
|
601
|
-
)}" to "${getLanguageLabel(newConfig.source)}"`;
|
|
602
|
-
} else if (targetsChanged) {
|
|
603
|
-
// Only target languages changed
|
|
604
|
-
actionText = targetMessage;
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
if (actionText) {
|
|
608
|
-
setMeetingTranscript(prev => [
|
|
609
|
-
...prev,
|
|
610
|
-
{
|
|
611
|
-
name: 'langUpdate',
|
|
612
|
-
uid: `langUpdate-${localUid}`,
|
|
613
|
-
time: new Date().getTime(),
|
|
614
|
-
text: actionText,
|
|
615
|
-
},
|
|
616
|
-
]);
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
// Broadcast updated spoken language to all users (only if it changed)
|
|
620
|
-
if (spokenLanguageChanged) {
|
|
621
|
-
events.send(
|
|
622
|
-
EventNames.STT_SPOKEN_LANGUAGE,
|
|
623
|
-
JSON.stringify({
|
|
624
|
-
userUid: localUid,
|
|
625
|
-
spokenLanguage: newConfig.source[0],
|
|
626
|
-
username: username,
|
|
627
|
-
}),
|
|
628
|
-
PersistanceLevel.Sender,
|
|
629
|
-
);
|
|
630
|
-
}
|
|
494
|
+
const oldSource = globalSttStateRef.current.globalSpokenLanguage;
|
|
495
|
+
const newSource = newConfig.source[0];
|
|
631
496
|
|
|
632
|
-
//
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
}),
|
|
497
|
+
// Build transcript messages if source changed
|
|
498
|
+
buildSttTranscriptForSourceChanged(oldSource, newSource);
|
|
499
|
+
if (isLocal && targetChange) {
|
|
500
|
+
buildSttTranscriptForTargetChanged(
|
|
501
|
+
targetChange?.prev,
|
|
502
|
+
targetChange?.next,
|
|
503
|
+
targetChange?.reason,
|
|
640
504
|
);
|
|
641
505
|
}
|
|
642
506
|
|
|
@@ -661,11 +525,13 @@ const CaptionProvider: React.FC<CaptionProviderProps> = ({
|
|
|
661
525
|
text1: updateErrorLabel,
|
|
662
526
|
text2: result.error?.message || 'Unknown error occurred',
|
|
663
527
|
visibilityTime: 4000,
|
|
528
|
+
primaryBtn: null,
|
|
529
|
+
secondaryBtn: null,
|
|
664
530
|
});
|
|
665
531
|
}
|
|
666
532
|
setIsLangChangeInProgress(false);
|
|
667
533
|
return result;
|
|
668
|
-
} catch (error) {
|
|
534
|
+
} catch (error: any) {
|
|
669
535
|
setIsLangChangeInProgress(false);
|
|
670
536
|
setIsSTTError(true);
|
|
671
537
|
logger.error(LogSource.NetworkRest, 'stt', 'STT update error', error);
|
|
@@ -676,6 +542,8 @@ const CaptionProvider: React.FC<CaptionProviderProps> = ({
|
|
|
676
542
|
text1: updateErrorLabel,
|
|
677
543
|
text2: error?.message || 'Unknown error occurred',
|
|
678
544
|
visibilityTime: 4000,
|
|
545
|
+
primaryBtn: null,
|
|
546
|
+
secondaryBtn: null,
|
|
679
547
|
});
|
|
680
548
|
return {
|
|
681
549
|
success: false,
|
|
@@ -684,88 +552,20 @@ const CaptionProvider: React.FC<CaptionProviderProps> = ({
|
|
|
684
552
|
}
|
|
685
553
|
};
|
|
686
554
|
|
|
687
|
-
const handleTranslateConfigChange = async (
|
|
688
|
-
inputTranslateConfig: LanguageTranslationConfig,
|
|
689
|
-
) => {
|
|
690
|
-
if (!localBotUid || !localUid) {
|
|
691
|
-
console.warn('[STT] Missing localUid or botUid');
|
|
692
|
-
return;
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
const newConfig: LanguageTranslationConfig = {
|
|
696
|
-
source: inputTranslateConfig?.source,
|
|
697
|
-
targets: inputTranslateConfig?.targets,
|
|
698
|
-
};
|
|
699
|
-
|
|
700
|
-
let action: 'start' | 'update' = 'start';
|
|
701
|
-
if (!isSTTActive) {
|
|
702
|
-
action = 'start';
|
|
703
|
-
} else if (hasConfigChanged(translationConfig, newConfig)) {
|
|
704
|
-
action = 'update';
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
console.log('[STT_HANDLE_CONFIRM]', {
|
|
708
|
-
action,
|
|
709
|
-
localBotUid,
|
|
710
|
-
inputConfig: inputTranslateConfig,
|
|
711
|
-
sanitizedConfig: newConfig,
|
|
712
|
-
});
|
|
713
|
-
|
|
714
|
-
try {
|
|
715
|
-
switch (action) {
|
|
716
|
-
case 'start':
|
|
717
|
-
const startResult = await startSTTBotSession(newConfig);
|
|
718
|
-
if (!startResult.success) {
|
|
719
|
-
throw new Error(
|
|
720
|
-
startResult.error?.message || 'Failed to start STT',
|
|
721
|
-
);
|
|
722
|
-
}
|
|
723
|
-
break;
|
|
724
|
-
|
|
725
|
-
case 'update':
|
|
726
|
-
const updateResult = await updateSTTBotSession(newConfig);
|
|
727
|
-
if (!updateResult.success) {
|
|
728
|
-
throw new Error(
|
|
729
|
-
updateResult.error?.message || 'Failed to update STT config',
|
|
730
|
-
);
|
|
731
|
-
}
|
|
732
|
-
break;
|
|
733
|
-
|
|
734
|
-
default:
|
|
735
|
-
console.warn('Unknown STT action');
|
|
736
|
-
}
|
|
737
|
-
} catch (error) {
|
|
738
|
-
setIsSTTError(true);
|
|
739
|
-
setIsLangChangeInProgress(false);
|
|
740
|
-
logger.error(
|
|
741
|
-
LogSource.NetworkRest,
|
|
742
|
-
'stt',
|
|
743
|
-
'Error in handleTranslateConfigChange',
|
|
744
|
-
error,
|
|
745
|
-
);
|
|
746
|
-
throw error;
|
|
747
|
-
}
|
|
748
|
-
};
|
|
749
|
-
|
|
750
555
|
const stopSTTBotSession = React.useCallback(async () => {
|
|
751
|
-
console.log('[
|
|
556
|
+
console.log('[STT] stopSTTBotSession called');
|
|
752
557
|
|
|
753
|
-
if (!
|
|
754
|
-
console.warn('[STT] Missing botUid');
|
|
558
|
+
if (!localBotUidRef.current) {
|
|
559
|
+
console.warn('[STT] Missing botUid for stop');
|
|
755
560
|
return;
|
|
756
561
|
}
|
|
757
562
|
|
|
758
563
|
try {
|
|
759
|
-
const result = await stop(
|
|
564
|
+
const result = await stop(localBotUidRef.current);
|
|
760
565
|
|
|
761
566
|
if (result.success) {
|
|
762
567
|
// Set STT inactive locally
|
|
763
|
-
setIsSTTActive(false);
|
|
764
568
|
// Clear user's source language when stopping
|
|
765
|
-
setTranslationConfig({
|
|
766
|
-
source: [],
|
|
767
|
-
targets: [],
|
|
768
|
-
});
|
|
769
569
|
// setIsCaptionON(false);
|
|
770
570
|
setIsSTTError(false);
|
|
771
571
|
|
|
@@ -792,14 +592,8 @@ const CaptionProvider: React.FC<CaptionProviderProps> = ({
|
|
|
792
592
|
'Error in stopSTTBotSession',
|
|
793
593
|
error,
|
|
794
594
|
);
|
|
795
|
-
throw error;
|
|
796
595
|
}
|
|
797
|
-
}, [stop
|
|
798
|
-
|
|
799
|
-
// Helper function to convert user UID to stt bot UID
|
|
800
|
-
const generateBotUidForUser = (userLocalUid: number): number => {
|
|
801
|
-
return 900000000 + (userLocalUid % 100000000);
|
|
802
|
-
};
|
|
596
|
+
}, [stop]);
|
|
803
597
|
|
|
804
598
|
// Helper function to convert bot UID to user UID
|
|
805
599
|
// Bot UIDs are in format: 900000000 + userUid
|
|
@@ -825,6 +619,379 @@ const CaptionProvider: React.FC<CaptionProviderProps> = ({
|
|
|
825
619
|
[],
|
|
826
620
|
);
|
|
827
621
|
|
|
622
|
+
// Spoken language handler
|
|
623
|
+
const confirmSpokenLanguageChange = React.useCallback(
|
|
624
|
+
async (newSpokenLang: LanguageType) => {
|
|
625
|
+
try {
|
|
626
|
+
const prevState = globalSttStateRef.current;
|
|
627
|
+
const prevTargets = prevState.globalTranslationTargets || [];
|
|
628
|
+
const prevSelectedTarget = selectedTranslationLanguageRef.current;
|
|
629
|
+
|
|
630
|
+
// Remove spoken from targets
|
|
631
|
+
const cleanedTargets = prevTargets.filter(t => t !== newSpokenLang);
|
|
632
|
+
|
|
633
|
+
// Build update state
|
|
634
|
+
const updatedState: GlobalSttState = {
|
|
635
|
+
...prevState,
|
|
636
|
+
globalSttEnabled: true,
|
|
637
|
+
globalSpokenLanguage: newSpokenLang,
|
|
638
|
+
globalTranslationTargets: cleanedTargets,
|
|
639
|
+
initiatorName: defaultContentRef?.current[localUid]?.name || username,
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
// Check if selected target still exists in target
|
|
643
|
+
const isSelectedStillValid =
|
|
644
|
+
prevSelectedTarget && cleanedTargets.includes(prevSelectedTarget);
|
|
645
|
+
|
|
646
|
+
// Prepare the target change
|
|
647
|
+
let targetChange: TargetChange;
|
|
648
|
+
if (prevSelectedTarget) {
|
|
649
|
+
if (isSelectedStillValid) {
|
|
650
|
+
// target remains valid
|
|
651
|
+
targetChange = {prev: prevSelectedTarget, next: prevSelectedTarget};
|
|
652
|
+
} else {
|
|
653
|
+
// target becomes invalid → must be turned OFF
|
|
654
|
+
targetChange = {
|
|
655
|
+
prev: prevSelectedTarget,
|
|
656
|
+
next: null,
|
|
657
|
+
reason: 'spoken-language-changed',
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
// Queue
|
|
662
|
+
enqueueSttEvent(updatedState, true, targetChange);
|
|
663
|
+
|
|
664
|
+
console.log(
|
|
665
|
+
'[STT_GLOBAL] confirmSpokenLanguageChange sent STT_GLOBAL_STATE: ',
|
|
666
|
+
updatedState,
|
|
667
|
+
);
|
|
668
|
+
} catch (error) {
|
|
669
|
+
console.log('[STT_GLOBAL] confirmSpokenLanguageChange error: ', error);
|
|
670
|
+
}
|
|
671
|
+
},
|
|
672
|
+
[localUid], // only real dependency
|
|
673
|
+
);
|
|
674
|
+
|
|
675
|
+
// Target language handler
|
|
676
|
+
const confirmTargetLanguageChange = React.useCallback(
|
|
677
|
+
async (newTargetLang: LanguageType) => {
|
|
678
|
+
// 1. User selected "Off"
|
|
679
|
+
const prevSelectedTargetLang = selectedTranslationLanguage;
|
|
680
|
+
if (!newTargetLang) {
|
|
681
|
+
buildSttTranscriptForTargetChanged(
|
|
682
|
+
prevSelectedTargetLang,
|
|
683
|
+
newTargetLang,
|
|
684
|
+
);
|
|
685
|
+
setSelectedTranslationLanguage(null);
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
// 2. User selected a translation language.
|
|
689
|
+
// 3. Check if target is already included in global targets
|
|
690
|
+
const alreadyInGlobal =
|
|
691
|
+
globalSttStateRef.current.globalTranslationTargets.includes(
|
|
692
|
+
newTargetLang,
|
|
693
|
+
);
|
|
694
|
+
if (alreadyInGlobal) {
|
|
695
|
+
buildSttTranscriptForTargetChanged(
|
|
696
|
+
prevSelectedTargetLang,
|
|
697
|
+
newTargetLang,
|
|
698
|
+
);
|
|
699
|
+
setSelectedTranslationLanguage(newTargetLang);
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
// 4. Not in global targets, need to create updated state to pass to api
|
|
703
|
+
const prevTargets = globalSttStateRef.current.globalTranslationTargets;
|
|
704
|
+
const newTargets = Array.from(new Set([...prevTargets, newTargetLang]));
|
|
705
|
+
|
|
706
|
+
const updatedState: GlobalSttState = {
|
|
707
|
+
globalSttEnabled: globalSttStateRef.current.globalSttEnabled,
|
|
708
|
+
globalSpokenLanguage: globalSttStateRef.current.globalSpokenLanguage,
|
|
709
|
+
globalTranslationTargets: newTargets,
|
|
710
|
+
};
|
|
711
|
+
// 5. Queue event
|
|
712
|
+
const prevTargetLang = selectedTranslationLanguageRef.current;
|
|
713
|
+
enqueueSttEvent(updatedState, true, {
|
|
714
|
+
prev: prevTargetLang,
|
|
715
|
+
next: newTargetLang,
|
|
716
|
+
});
|
|
717
|
+
},
|
|
718
|
+
[selectedTranslationLanguage], // only real state dependency
|
|
719
|
+
);
|
|
720
|
+
|
|
721
|
+
// Queues all local + remote stt events
|
|
722
|
+
const enqueueSttEvent = React.useCallback(
|
|
723
|
+
(state: GlobalSttState, isLocal: boolean, targetChange?: TargetChange) => {
|
|
724
|
+
console.log(
|
|
725
|
+
'[STT_GLOBAL] inside enqueueSttEvent - sttDepsReadyRef flag',
|
|
726
|
+
sttDepsReadyRef.current,
|
|
727
|
+
);
|
|
728
|
+
sttEventQueueRef.current.push({
|
|
729
|
+
state,
|
|
730
|
+
isLocal,
|
|
731
|
+
targetChange,
|
|
732
|
+
});
|
|
733
|
+
if (sttDepsReadyRef.current) {
|
|
734
|
+
processSttEventQueue();
|
|
735
|
+
}
|
|
736
|
+
},
|
|
737
|
+
[],
|
|
738
|
+
);
|
|
739
|
+
|
|
740
|
+
// Process stt events
|
|
741
|
+
const processSttEventQueue = React.useCallback(async () => {
|
|
742
|
+
// 1. Concurrent queue processing
|
|
743
|
+
if (isProcessingSttEventRef.current) {
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
isProcessingSttEventRef.current = true;
|
|
747
|
+
// 2. stt auto start check
|
|
748
|
+
if (
|
|
749
|
+
$config.STT_AUTO_START &&
|
|
750
|
+
sttDepsReadyRef.current &&
|
|
751
|
+
!sttAutoStartGuardRef.current
|
|
752
|
+
) {
|
|
753
|
+
if (isHostRef.current && !globalSttStateRef.current.globalSttEnabled) {
|
|
754
|
+
console.log('[STT] AUTO_START →supriya injecting start state');
|
|
755
|
+
|
|
756
|
+
sttAutoStartGuardRef.current = true;
|
|
757
|
+
|
|
758
|
+
const autoStartState: GlobalSttState = {
|
|
759
|
+
globalSttEnabled: true,
|
|
760
|
+
globalSpokenLanguage: 'en-US',
|
|
761
|
+
globalTranslationTargets: [],
|
|
762
|
+
initiatorName: defaultContentRef.current[localUid]?.name || username,
|
|
763
|
+
};
|
|
764
|
+
|
|
765
|
+
// adding auto start in the beginning of queue
|
|
766
|
+
sttEventQueueRef.current.unshift({
|
|
767
|
+
state: autoStartState,
|
|
768
|
+
isLocal: true,
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
// 3. queue processing of all stt events
|
|
773
|
+
while (sttEventQueueRef.current.length > 0) {
|
|
774
|
+
const item = sttEventQueueRef.current.shift();
|
|
775
|
+
if (!item) {
|
|
776
|
+
break;
|
|
777
|
+
}
|
|
778
|
+
const {state: newState, isLocal, targetChange} = item;
|
|
779
|
+
const prevState = globalSttStateRef.current;
|
|
780
|
+
if (isSameState(prevState, newState)) {
|
|
781
|
+
console.log('[STT] Skipped duplicate STT_GLOBAL_STATE');
|
|
782
|
+
continue; // no call to processGlobalSttSingleEvent
|
|
783
|
+
}
|
|
784
|
+
const ok = await processGlobalSttSingleEvent(
|
|
785
|
+
newState,
|
|
786
|
+
isLocal,
|
|
787
|
+
targetChange,
|
|
788
|
+
);
|
|
789
|
+
if (!ok) {
|
|
790
|
+
console.warn('[STT] Skipping global state update because API failed.');
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
// update global state AFTER processing
|
|
794
|
+
setGlobalSttState(newState);
|
|
795
|
+
globalSttStateRef.current = newState;
|
|
796
|
+
}
|
|
797
|
+
isProcessingSttEventRef.current = false;
|
|
798
|
+
}, []);
|
|
799
|
+
|
|
800
|
+
const processGlobalSttSingleEvent = React.useCallback(
|
|
801
|
+
async (
|
|
802
|
+
newState: GlobalSttState,
|
|
803
|
+
isLocal: boolean,
|
|
804
|
+
targetChange?: TargetChange,
|
|
805
|
+
) => {
|
|
806
|
+
const prevState = globalSttStateRef.current;
|
|
807
|
+
const wasEnabledBefore = prevState.globalSttEnabled;
|
|
808
|
+
const isEnabledNow = newState.globalSttEnabled;
|
|
809
|
+
|
|
810
|
+
const isStartOperation = !wasEnabledBefore && isEnabledNow;
|
|
811
|
+
const isUpdateOperation = wasEnabledBefore && isEnabledNow;
|
|
812
|
+
const isStopOperation = wasEnabledBefore && !isEnabledNow;
|
|
813
|
+
try {
|
|
814
|
+
if (isStartOperation) {
|
|
815
|
+
console.log('[STT] Remote global STT -> starting session', newState);
|
|
816
|
+
// Start guard starts
|
|
817
|
+
if (sttStartGuardRef.current) {
|
|
818
|
+
console.log('[STT] Start skipped (already started)');
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
sttStartGuardRef.current = true;
|
|
822
|
+
// Start guard ends
|
|
823
|
+
const result = await startSTTBotSession({
|
|
824
|
+
source: [newState.globalSpokenLanguage],
|
|
825
|
+
targets: newState.globalTranslationTargets,
|
|
826
|
+
});
|
|
827
|
+
if (!result.success) {
|
|
828
|
+
return false;
|
|
829
|
+
}
|
|
830
|
+
buildSttTranscriptForSourceChanged(
|
|
831
|
+
prevState.globalSpokenLanguage,
|
|
832
|
+
newState.globalSpokenLanguage,
|
|
833
|
+
);
|
|
834
|
+
// Toast
|
|
835
|
+
let spokenLangLabel =
|
|
836
|
+
getLanguageLabel([newState?.globalSpokenLanguage]) || '';
|
|
837
|
+
if (isLocal) {
|
|
838
|
+
// I see this
|
|
839
|
+
if (sttAutoStartGuardRef.current) {
|
|
840
|
+
Toast.show({
|
|
841
|
+
type: 'info',
|
|
842
|
+
text1: heading('Set'),
|
|
843
|
+
text2: `Live transcription are automatically enabled for this meeting in "${spokenLangLabel}"`,
|
|
844
|
+
visibilityTime: 3000,
|
|
845
|
+
primaryBtn: null,
|
|
846
|
+
secondaryBtn: null,
|
|
847
|
+
});
|
|
848
|
+
} else {
|
|
849
|
+
Toast.show({
|
|
850
|
+
type: 'info',
|
|
851
|
+
text1: heading('Set'),
|
|
852
|
+
text2: subheading({
|
|
853
|
+
username: 'You',
|
|
854
|
+
action: 'Set',
|
|
855
|
+
newLanguage: spokenLangLabel,
|
|
856
|
+
}),
|
|
857
|
+
visibilityTime: 3000,
|
|
858
|
+
primaryBtn: null,
|
|
859
|
+
secondaryBtn: null,
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
} else {
|
|
863
|
+
// Remote users see this
|
|
864
|
+
let subheadingObj: sttSpokenLanguageToastSubHeadingDataInterface = {
|
|
865
|
+
username: newState?.initiatorName || 'Host',
|
|
866
|
+
action: 'Set',
|
|
867
|
+
newLanguage: getLanguageLabel([newState?.globalSpokenLanguage]),
|
|
868
|
+
};
|
|
869
|
+
Toast.show({
|
|
870
|
+
type: 'info',
|
|
871
|
+
text1: heading('Set'),
|
|
872
|
+
text2: subheading(subheadingObj),
|
|
873
|
+
visibilityTime: 3000,
|
|
874
|
+
primaryBtn: null,
|
|
875
|
+
secondaryBtn: null,
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
if (isLocal) {
|
|
880
|
+
events.send(
|
|
881
|
+
EventNames.STT_GLOBAL_STATE,
|
|
882
|
+
JSON.stringify(newState),
|
|
883
|
+
PersistanceLevel.Session,
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
return true;
|
|
887
|
+
} else if (isUpdateOperation) {
|
|
888
|
+
console.log('[STT] Global STT -> updating session', newState);
|
|
889
|
+
const result = await updateSTTBotSession(
|
|
890
|
+
{
|
|
891
|
+
source: [newState.globalSpokenLanguage],
|
|
892
|
+
targets: newState.globalTranslationTargets,
|
|
893
|
+
},
|
|
894
|
+
isLocal,
|
|
895
|
+
targetChange ?? undefined,
|
|
896
|
+
);
|
|
897
|
+
if (!result.success) {
|
|
898
|
+
return false;
|
|
899
|
+
}
|
|
900
|
+
if (
|
|
901
|
+
prevState.globalSpokenLanguage !== newState.globalSpokenLanguage
|
|
902
|
+
) {
|
|
903
|
+
let subheadingObj: sttSpokenLanguageToastSubHeadingDataInterface = {
|
|
904
|
+
username: isLocal ? 'You' : newState.initiatorName || 'Host',
|
|
905
|
+
action: 'Changed',
|
|
906
|
+
newLanguage:
|
|
907
|
+
getLanguageLabel([newState.globalSpokenLanguage]) || '',
|
|
908
|
+
oldLanguage:
|
|
909
|
+
getLanguageLabel([prevState.globalSpokenLanguage]) || '',
|
|
910
|
+
};
|
|
911
|
+
// text1: 'Spoken language updated',
|
|
912
|
+
// text2: `Captions will now transcribe in ${getLanguageLabel(
|
|
913
|
+
// newConfig.source,
|
|
914
|
+
// )}`,
|
|
915
|
+
Toast.show({
|
|
916
|
+
type: 'info',
|
|
917
|
+
text1: heading('Changed'),
|
|
918
|
+
text2: subheading(subheadingObj),
|
|
919
|
+
// text2: `${subheading(
|
|
920
|
+
// subheadingObj,
|
|
921
|
+
// )} \n Captions will now transcribe in ${
|
|
922
|
+
// getLanguageLabel(newConfig.source) || ''
|
|
923
|
+
// }`,
|
|
924
|
+
// text1: 'Spoken language updated',
|
|
925
|
+
// text2: `Captions will now transcribe in ${getLanguageLabel(
|
|
926
|
+
// newConfig.source,
|
|
927
|
+
// )}`,
|
|
928
|
+
visibilityTime: 3000,
|
|
929
|
+
primaryBtn: null,
|
|
930
|
+
secondaryBtn: null,
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
if (isLocal) {
|
|
934
|
+
events.send(
|
|
935
|
+
EventNames.STT_GLOBAL_STATE,
|
|
936
|
+
JSON.stringify(newState),
|
|
937
|
+
PersistanceLevel.Session,
|
|
938
|
+
);
|
|
939
|
+
setSelectedTranslationLanguage(targetChange?.next);
|
|
940
|
+
} else {
|
|
941
|
+
const currentSelectedTarget =
|
|
942
|
+
selectedTranslationLanguageRef.current;
|
|
943
|
+
if (
|
|
944
|
+
currentSelectedTarget &&
|
|
945
|
+
!newState.globalTranslationTargets.includes(currentSelectedTarget)
|
|
946
|
+
) {
|
|
947
|
+
setSelectedTranslationLanguage(null);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
return true;
|
|
951
|
+
} else if (isStopOperation) {
|
|
952
|
+
console.log('[STT] Global STT -> stopping session', newState);
|
|
953
|
+
await stopSTTBotSession();
|
|
954
|
+
sttStartGuardRef.current = false;
|
|
955
|
+
return true;
|
|
956
|
+
}
|
|
957
|
+
return true;
|
|
958
|
+
} catch (error) {
|
|
959
|
+
logger.error(
|
|
960
|
+
LogSource.Internals,
|
|
961
|
+
'STT',
|
|
962
|
+
'Error handling STT_GLOBAL_STATE event',
|
|
963
|
+
error,
|
|
964
|
+
);
|
|
965
|
+
return false;
|
|
966
|
+
}
|
|
967
|
+
},
|
|
968
|
+
[],
|
|
969
|
+
);
|
|
970
|
+
|
|
971
|
+
// Handle GLOBAL STT events from others (simple "machine" + queue)
|
|
972
|
+
React.useEffect(() => {
|
|
973
|
+
const handleGlobalSTTChange = async (evt: any) => {
|
|
974
|
+
const {payload} = evt || {};
|
|
975
|
+
console.log('[STT] STT_GLOBAL_STATE event received: ', evt);
|
|
976
|
+
let newState: GlobalSttState;
|
|
977
|
+
try {
|
|
978
|
+
newState = JSON.parse(payload);
|
|
979
|
+
} catch (error) {
|
|
980
|
+
logger.error(
|
|
981
|
+
LogSource.Internals,
|
|
982
|
+
'STT',
|
|
983
|
+
'Failed to parse STT_GLOBAL_STATE event payload',
|
|
984
|
+
error,
|
|
985
|
+
);
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
enqueueSttEvent(newState, false);
|
|
989
|
+
};
|
|
990
|
+
|
|
991
|
+
events.on(EventNames.STT_GLOBAL_STATE, handleGlobalSTTChange);
|
|
992
|
+
return () => events.off(EventNames.STT_GLOBAL_STATE, handleGlobalSTTChange);
|
|
993
|
+
}, []);
|
|
994
|
+
|
|
828
995
|
return (
|
|
829
996
|
<CaptionContext.Provider
|
|
830
997
|
value={{
|
|
@@ -833,9 +1000,10 @@ const CaptionProvider: React.FC<CaptionProviderProps> = ({
|
|
|
833
1000
|
isSTTError,
|
|
834
1001
|
setIsSTTError,
|
|
835
1002
|
isSTTActive,
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
1003
|
+
sttDepsReady,
|
|
1004
|
+
globalSttState,
|
|
1005
|
+
confirmSpokenLanguageChange,
|
|
1006
|
+
confirmTargetLanguageChange,
|
|
839
1007
|
captionViewMode,
|
|
840
1008
|
setCaptionViewMode,
|
|
841
1009
|
transcriptViewMode,
|
|
@@ -844,8 +1012,6 @@ const CaptionProvider: React.FC<CaptionProviderProps> = ({
|
|
|
844
1012
|
setMeetingTranscript,
|
|
845
1013
|
isLangChangeInProgress,
|
|
846
1014
|
setIsLangChangeInProgress,
|
|
847
|
-
isTranslationChangeInProgress,
|
|
848
|
-
setIsTranslationChangeInProgress,
|
|
849
1015
|
captionObj,
|
|
850
1016
|
setCaptionObj,
|
|
851
1017
|
isSTTListenerAdded,
|
|
@@ -853,12 +1019,9 @@ const CaptionProvider: React.FC<CaptionProviderProps> = ({
|
|
|
853
1019
|
activeSpeakerRef,
|
|
854
1020
|
prevSpeakerRef,
|
|
855
1021
|
selectedTranslationLanguage,
|
|
856
|
-
setSelectedTranslationLanguage,
|
|
857
1022
|
selectedTranslationLanguageRef,
|
|
858
|
-
translationConfigRef,
|
|
859
1023
|
remoteSpokenLanguages,
|
|
860
1024
|
setRemoteSpokenLanguages,
|
|
861
|
-
handleTranslateConfigChange,
|
|
862
1025
|
startSTTBotSession,
|
|
863
1026
|
updateSTTBotSession,
|
|
864
1027
|
stopSTTBotSession,
|