@trtc/calls-uikit-react 4.4.3-beta.2 → 4.4.4
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 +4 -3
- package/src/Components/assets/aiAssistant/desktop/subtitleSettings.svg +4 -0
- package/src/Components/assets/aiAssistant/mobile/close-aiAssistant.svg +7 -0
- package/src/Components/assets/aiAssistant/mobile/open-aiAssistant.svg +7 -0
- package/src/Components/assets/aiAssistant/mobile/subtitleSettings.svg +3 -0
- package/src/Components/components/base/CustomSelect/CustomSelect.scss +116 -0
- package/src/Components/components/base/CustomSelect/CustomSelect.tsx +96 -0
- package/src/Components/components/common/AIAssistant/AISubtitle.scss +109 -0
- package/src/Components/components/common/AIAssistant/AISubtitle.tsx +52 -66
- package/src/Components/components/common/AIAssistant/components/AITranscriberSwitchH5.tsx +39 -0
- package/src/Components/components/common/AIAssistant/components/AITranscriberSwitchPC.scss +46 -0
- package/src/Components/components/common/AIAssistant/components/AITranscriberSwitchPC.tsx +42 -0
- package/src/Components/components/common/AIAssistant/components/SubtitleContent.scss +67 -0
- package/src/Components/components/common/AIAssistant/components/SubtitleContent.tsx +134 -0
- package/src/Components/components/common/AIAssistant/components/SubtitleSettingsH5.scss +275 -0
- package/src/Components/components/common/AIAssistant/components/SubtitleSettingsH5.tsx +265 -0
- package/src/Components/components/common/AIAssistant/components/SubtitleSettingsPC.scss +98 -0
- package/src/Components/components/common/AIAssistant/components/SubtitleSettingsPC.tsx +198 -0
- package/src/Components/components/common/AIAssistant/components/index.ts +5 -0
- package/src/Components/components/common/AIAssistant/index.ts +3 -1
- package/src/Components/components/common/TopBar/h5/TopBarH5.tsx +11 -1
- package/src/Components/components/common/TopBar/pc/TopBarPC.module.scss +20 -0
- package/src/Components/components/common/TopBar/pc/TopBarPC.tsx +14 -9
- package/src/Components/components/common/UIKitModal/UIKitModal.tsx +250 -0
- package/src/Components/components/common/UIKitModal/UIKitModalState.ts +177 -0
- package/src/Components/components/common/UIKitModal/index.module.scss +176 -0
- package/src/Components/components/common/UIKitModal/index.ts +3 -0
- package/src/Components/hooks/index.ts +2 -0
- package/src/Components/hooks/useAIAssistant.ts +174 -0
- package/src/TUICallService/CallService/AIAssistant.ts +285 -39
- package/src/TUICallService/CallService/UIKitModal.ts +72 -6
- package/src/TUICallService/CallService/bellContext.ts +25 -2
- package/src/TUICallService/CallService/engineEventHandler.ts +6 -1
- package/src/TUICallService/CallService/index.ts +72 -39
- package/src/TUICallService/CallService/miniProgram.ts +0 -12
- package/src/TUICallService/TUIStore/callStore.ts +6 -1
- package/src/TUICallService/UIKitModal/UIKitModal.ts +117 -0
- package/src/TUICallService/UIKitModal/index.ts +2 -0
- package/src/TUICallService/UIKitModal/type.ts +15 -0
- package/src/TUICallService/const/index.ts +4 -0
- package/src/TUICallService/interface/ICallStore.ts +5 -0
- package/src/TUICallService/locales/en.ts +17 -1
- package/src/TUICallService/locales/ja_JP.ts +17 -1
- package/src/TUICallService/locales/zh-cn.ts +17 -1
- package/src/TUICallService/utils/common-utils.ts +1 -1
- package/src/index.ts +1 -1
- package/tuicall-uikit-react.es.js +4009 -3940
- package/tuicall-uikit-react.umd.js +8 -33
- package/types/Components/components/base/CustomSelect/CustomSelect.d.ts +15 -0
- package/types/Components/components/common/AIAssistant/AISubtitle.d.ts +1 -0
- package/types/Components/components/common/AIAssistant/components/AITranscriberSwitchH5.d.ts +3 -0
- package/types/Components/components/common/AIAssistant/components/AITranscriberSwitchPC.d.ts +4 -0
- package/types/Components/components/common/AIAssistant/components/SubtitleContent.d.ts +8 -0
- package/types/Components/components/common/AIAssistant/components/SubtitleSettingsH5.d.ts +13 -0
- package/types/Components/components/common/AIAssistant/components/SubtitleSettingsPC.d.ts +13 -0
- package/types/Components/components/common/AIAssistant/components/index.d.ts +5 -0
- package/types/Components/components/common/AIAssistant/index.d.ts +1 -0
- package/types/Components/components/common/UIKitModal/UIKitModal.d.ts +21 -0
- package/types/Components/components/common/UIKitModal/UIKitModalState.d.ts +27 -0
- package/types/Components/components/common/UIKitModal/index.d.ts +3 -0
- package/types/Components/hooks/index.d.ts +2 -1
- package/types/Components/hooks/useAIAssistant.d.ts +22 -0
- package/types/TUICallService/CallService/AIAssistant.d.ts +72 -15
- package/types/TUICallService/CallService/bellContext.d.ts +3 -0
- package/types/TUICallService/CallService/index.d.ts +4 -3
- package/types/TUICallService/CallService/miniProgram.d.ts +0 -1
- package/types/TUICallService/UIKitModal/UIKitModal.d.ts +4 -0
- package/types/TUICallService/UIKitModal/index.d.ts +2 -0
- package/types/TUICallService/UIKitModal/type.d.ts +13 -0
- package/types/TUICallService/interface/ICallStore.d.ts +4 -0
- package/types/TUICallService/locales/en.d.ts +15 -0
- package/types/TUICallService/locales/ja_JP.d.ts +15 -0
- package/types/TUICallService/locales/zh-cn.d.ts +15 -0
- package/src/TUICallService/utils/validate/validateStatus.ts +0 -26
- package/types/TUICallService/utils/validate/validateStatus.d.ts +0 -5
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Assistant Hook
|
|
3
|
+
* @description React hook for AI Transcriber functionality
|
|
4
|
+
*/
|
|
5
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
6
|
+
import { TUIStore, StoreName, NAME } from '../../TUICallService/index';
|
|
7
|
+
|
|
8
|
+
// Translation content interface
|
|
9
|
+
interface ITranslationContent {
|
|
10
|
+
language: string;
|
|
11
|
+
text: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Transcriber message interface (simplified for React version)
|
|
15
|
+
interface TranscriberMessage {
|
|
16
|
+
segmentId: string;
|
|
17
|
+
speakerUserId: string;
|
|
18
|
+
sourceText: string;
|
|
19
|
+
isCompleted: boolean;
|
|
20
|
+
translationTexts: any;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Converted message interface
|
|
24
|
+
interface ITranslationInfo {
|
|
25
|
+
roundId: string;
|
|
26
|
+
sender: string;
|
|
27
|
+
nick?: string;
|
|
28
|
+
text: string;
|
|
29
|
+
end: boolean;
|
|
30
|
+
translation: ITranslationContent[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* AI Assistant hook
|
|
35
|
+
* Provides state management for AI transcription functionality
|
|
36
|
+
*/
|
|
37
|
+
export function useAIAssistant() {
|
|
38
|
+
// AI Transcriber Status State
|
|
39
|
+
const [isAITranscriberEnabled, setIsAITranscriberEnabled] = useState<boolean>(false);
|
|
40
|
+
const [isAITranscriberRunning, setIsAITranscriberRunning] = useState<boolean>(false);
|
|
41
|
+
|
|
42
|
+
// Converted message list
|
|
43
|
+
const [subtitleInfoList, setSubtitleInfoList] = useState<ITranslationInfo[]>([]);
|
|
44
|
+
|
|
45
|
+
// Helper functions for message conversion
|
|
46
|
+
const parseTranslationTexts = useCallback((translationTexts: any): ITranslationContent[] => {
|
|
47
|
+
if (!translationTexts) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Handle Map type
|
|
52
|
+
if (translationTexts instanceof Map) {
|
|
53
|
+
return Array.from(translationTexts.entries()).map(([language, content]) => ({
|
|
54
|
+
language: String(language),
|
|
55
|
+
text: ensureStringContent(content)
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Handle plain object type
|
|
60
|
+
if (typeof translationTexts === 'object') {
|
|
61
|
+
return Object.entries(translationTexts).map(([language, content]) => ({
|
|
62
|
+
language: String(language),
|
|
63
|
+
text: ensureStringContent(content)
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return [];
|
|
68
|
+
}, []);
|
|
69
|
+
|
|
70
|
+
const ensureStringContent = useCallback((content: any): string => {
|
|
71
|
+
if (content === null || content === undefined) {
|
|
72
|
+
return '';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (typeof content === 'string') {
|
|
76
|
+
return content;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (typeof content === 'object') {
|
|
80
|
+
try {
|
|
81
|
+
return JSON.stringify(content);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.warn('Failed to stringify translation content:', error);
|
|
84
|
+
return '[Invalid Object]';
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return String(content);
|
|
89
|
+
}, []);
|
|
90
|
+
|
|
91
|
+
const convertToLegacyFormat = useCallback((messages: TranscriberMessage[]): ITranslationInfo[] => {
|
|
92
|
+
return messages.map(msg => ({
|
|
93
|
+
roundId: msg.segmentId,
|
|
94
|
+
sender: msg.speakerUserId,
|
|
95
|
+
nick: '',
|
|
96
|
+
text: msg.sourceText,
|
|
97
|
+
end: msg.isCompleted,
|
|
98
|
+
translation: parseTranslationTexts(msg.translationTexts)
|
|
99
|
+
}));
|
|
100
|
+
}, [parseTranslationTexts]);
|
|
101
|
+
|
|
102
|
+
// Store watcher callbacks
|
|
103
|
+
const handleAITranscriberEnabledChange = useCallback((value: boolean) => {
|
|
104
|
+
setIsAITranscriberEnabled(value || false);
|
|
105
|
+
}, []);
|
|
106
|
+
|
|
107
|
+
const handleAITranscriberRunningChange = useCallback((value: boolean) => {
|
|
108
|
+
setIsAITranscriberRunning(value || false);
|
|
109
|
+
}, []);
|
|
110
|
+
|
|
111
|
+
const handleRealtimeMessageListChange = useCallback((messageList: TranscriberMessage[]) => {
|
|
112
|
+
if (messageList && messageList.length > 0) {
|
|
113
|
+
const convertedMessages = convertToLegacyFormat(messageList);
|
|
114
|
+
setSubtitleInfoList(convertedMessages);
|
|
115
|
+
} else {
|
|
116
|
+
setSubtitleInfoList([]);
|
|
117
|
+
}
|
|
118
|
+
}, [convertToLegacyFormat]);
|
|
119
|
+
|
|
120
|
+
// Effect for lifecycle management
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
// Try to get initial values from TUIStore if available
|
|
123
|
+
try {
|
|
124
|
+
const initialEnabled = TUIStore.getData(StoreName.CALL, NAME.IS_AI_TRANSCRIBER_ENABLED);
|
|
125
|
+
const initialRunning = TUIStore.getData(StoreName.CALL, NAME.IS_AI_TRANSCRIBER_RUNNING);
|
|
126
|
+
const initialMessages = TUIStore.getData(StoreName.CALL, NAME.REALTIME_MESSAGE_LIST);
|
|
127
|
+
|
|
128
|
+
if (initialEnabled !== undefined) {
|
|
129
|
+
setIsAITranscriberEnabled(initialEnabled);
|
|
130
|
+
}
|
|
131
|
+
if (initialRunning !== undefined) {
|
|
132
|
+
setIsAITranscriberRunning(initialRunning);
|
|
133
|
+
}
|
|
134
|
+
if (initialMessages) {
|
|
135
|
+
handleRealtimeMessageListChange(initialMessages);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Subscribe to TUIStore changes if the store supports these fields
|
|
139
|
+
if (NAME.IS_AI_TRANSCRIBER_ENABLED && NAME.IS_AI_TRANSCRIBER_RUNNING && NAME.REALTIME_MESSAGE_LIST) {
|
|
140
|
+
TUIStore.watch(StoreName.CALL, {
|
|
141
|
+
[NAME.IS_AI_TRANSCRIBER_ENABLED]: handleAITranscriberEnabledChange,
|
|
142
|
+
[NAME.IS_AI_TRANSCRIBER_RUNNING]: handleAITranscriberRunningChange,
|
|
143
|
+
[NAME.REALTIME_MESSAGE_LIST]: handleRealtimeMessageListChange,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.warn('AI Assistant hook initialization failed:', error);
|
|
148
|
+
// Set default values for development/testing
|
|
149
|
+
setIsAITranscriberEnabled(true);
|
|
150
|
+
setIsAITranscriberRunning(false);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Cleanup function
|
|
154
|
+
return () => {
|
|
155
|
+
try {
|
|
156
|
+
if (NAME.IS_AI_TRANSCRIBER_ENABLED && NAME.IS_AI_TRANSCRIBER_RUNNING && NAME.REALTIME_MESSAGE_LIST) {
|
|
157
|
+
TUIStore.unwatch(StoreName.CALL, {
|
|
158
|
+
[NAME.IS_AI_TRANSCRIBER_ENABLED]: handleAITranscriberEnabledChange,
|
|
159
|
+
[NAME.IS_AI_TRANSCRIBER_RUNNING]: handleAITranscriberRunningChange,
|
|
160
|
+
[NAME.REALTIME_MESSAGE_LIST]: handleRealtimeMessageListChange,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.warn('AI Assistant hook cleanup failed:', error);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}, [handleAITranscriberEnabledChange, handleAITranscriberRunningChange, handleRealtimeMessageListChange]);
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
isAITranscriberEnabled,
|
|
171
|
+
isAITranscriberRunning,
|
|
172
|
+
subtitleInfoList,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
@@ -1,51 +1,297 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
/**
|
|
2
|
+
* AI Realtime Transcriber State Management
|
|
3
|
+
* @module AITranscriberState
|
|
4
|
+
* @description Manages realtime voice transcription state and operations, including message reception and history
|
|
5
|
+
*/
|
|
6
|
+
import { ITUIStore } from '../interface/ITUIStore';
|
|
7
|
+
import { StoreName, NAME } from '../const/index';
|
|
8
|
+
import TuiStore from '../TUIStore/tuiStore';
|
|
9
|
+
|
|
10
|
+
// AI Transcriber events
|
|
11
|
+
export enum RealtimeTranscriberEvent {
|
|
12
|
+
onReceiveTranscriberMessage = 'onReceiveTranscriberMessage',
|
|
13
|
+
onRealtimeTranscriberStarted = 'onRealtimeTranscriberStarted',
|
|
14
|
+
onRealtimeTranscriberStopped = 'onRealtimeTranscriberStopped',
|
|
15
|
+
onRealtimeTranscriberError = 'onRealtimeTranscriberError',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Transcriber language type
|
|
19
|
+
export type TranscriberLanguage = string;
|
|
20
|
+
|
|
21
|
+
// Transcriber message interface
|
|
22
|
+
export interface TranscriberMessage {
|
|
23
|
+
segmentId: string;
|
|
24
|
+
speakerUserId: string;
|
|
25
|
+
sourceText: string;
|
|
26
|
+
translationTexts?: Map<string, string>;
|
|
27
|
+
timestamp: number;
|
|
28
|
+
isCompleted: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Event callback types
|
|
32
|
+
export interface RealtimeTranscriberEventInfoMap {
|
|
33
|
+
[RealtimeTranscriberEvent.onReceiveTranscriberMessage]: {
|
|
34
|
+
roomId: string | number;
|
|
35
|
+
message: TranscriberMessage;
|
|
36
|
+
};
|
|
37
|
+
[RealtimeTranscriberEvent.onRealtimeTranscriberStarted]: {
|
|
38
|
+
roomId: string | number;
|
|
39
|
+
transcriberRobotId: string;
|
|
40
|
+
sourceLanguage: string;
|
|
41
|
+
};
|
|
42
|
+
[RealtimeTranscriberEvent.onRealtimeTranscriberStopped]: {
|
|
43
|
+
roomId: string | number;
|
|
44
|
+
transcriberRobotId: string;
|
|
45
|
+
};
|
|
46
|
+
[RealtimeTranscriberEvent.onRealtimeTranscriberError]: {
|
|
47
|
+
roomId: string | number;
|
|
48
|
+
transcriberRobotId: string;
|
|
49
|
+
error: number;
|
|
50
|
+
errorMessage: string;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type RealtimeTranscriberEventCallback = <T extends RealtimeTranscriberEvent>(
|
|
55
|
+
eventInfo: RealtimeTranscriberEventInfoMap[T]
|
|
56
|
+
) => void;
|
|
57
|
+
|
|
58
|
+
const TUIStore: ITUIStore = TuiStore.getInstance();
|
|
59
|
+
|
|
60
|
+
export default class AIAssistant {
|
|
61
|
+
static instance: AIAssistant;
|
|
62
|
+
private _callService: any;
|
|
63
|
+
private eventListeners = new Map<RealtimeTranscriberEvent, RealtimeTranscriberEventCallback[]>();
|
|
64
|
+
|
|
65
|
+
constructor(options: { callService: any }) {
|
|
66
|
+
this._callService = options.callService;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static getInstance(options: { callService: any }) {
|
|
17
70
|
if (!AIAssistant.instance) {
|
|
18
|
-
AIAssistant.instance = new AIAssistant();
|
|
71
|
+
AIAssistant.instance = new AIAssistant(options);
|
|
19
72
|
}
|
|
20
73
|
return AIAssistant.instance;
|
|
21
74
|
}
|
|
22
75
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Get AI Transcriber Manager instance
|
|
78
|
+
* @returns AITranscriberManager instance or null if not available
|
|
79
|
+
*/
|
|
80
|
+
private getTranscriberManager = () => {
|
|
81
|
+
const callEngine = this._callService?.getTUICallEngineInstance();
|
|
82
|
+
if (!callEngine) {
|
|
83
|
+
console.warn(`${NAME.PREFIX}Failed to get call engine instance.`);
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
28
86
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return
|
|
87
|
+
const trtcCloud = callEngine.getTRTCCloudInstance?.();
|
|
88
|
+
if (!trtcCloud || !trtcCloud.getAITranscriberManager) {
|
|
89
|
+
console.warn(`${NAME.PREFIX}AI Transcriber not supported.`);
|
|
90
|
+
return null;
|
|
33
91
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
92
|
+
|
|
93
|
+
return trtcCloud.getAITranscriberManager();
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
private triggerEvent = <T extends RealtimeTranscriberEvent>(
|
|
97
|
+
event: T,
|
|
98
|
+
eventInfo: RealtimeTranscriberEventInfoMap[T],
|
|
99
|
+
): void => {
|
|
100
|
+
const listeners = this.eventListeners.get(event);
|
|
101
|
+
if (listeners) {
|
|
102
|
+
listeners.forEach((callback) => {
|
|
103
|
+
try {
|
|
104
|
+
callback(eventInfo);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error(`${NAME.PREFIX}Error in event callback for ${event}:`, error);
|
|
107
|
+
}
|
|
39
108
|
});
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
public startRealtimeTranscriber = async (config: {
|
|
113
|
+
sourceLanguage: TranscriberLanguage;
|
|
114
|
+
translationLanguages?: TranscriberLanguage[];
|
|
115
|
+
}) => {
|
|
116
|
+
const transcriberManager = this.getTranscriberManager();
|
|
117
|
+
if (!transcriberManager) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const transcriberRobotId = await transcriberManager.startRealtimeTranscriber(config);
|
|
123
|
+
|
|
124
|
+
TUIStore.update(StoreName.CALL, NAME.TRANSCRIBER_ROBOT_ID, transcriberRobotId);
|
|
125
|
+
TUIStore.update(StoreName.CALL, NAME.IS_AI_TRANSCRIBER_RUNNING, true);
|
|
126
|
+
return transcriberRobotId;
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error(`${NAME.PREFIX}startRealtimeTranscriber error:`, error);
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
public stopRealtimeTranscriber = async () => {
|
|
134
|
+
const transcriberManager = this.getTranscriberManager();
|
|
135
|
+
if (!transcriberManager) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
const transcriberRobotId = TUIStore.getData(StoreName.CALL, NAME.TRANSCRIBER_ROBOT_ID);
|
|
141
|
+
|
|
142
|
+
if (transcriberRobotId) {
|
|
143
|
+
await transcriberManager.stopRealtimeTranscriber(transcriberRobotId);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
TUIStore.update(StoreName.CALL, NAME.TRANSCRIBER_ROBOT_ID, null);
|
|
147
|
+
TUIStore.update(StoreName.CALL, NAME.IS_AI_TRANSCRIBER_RUNNING, false);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.error(`${NAME.PREFIX}stopRealtimeTranscriber error:`, error);
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
public updateRealTimeTranscriber = async (config: {
|
|
155
|
+
sourceLanguage: TranscriberLanguage;
|
|
156
|
+
translationLanguages?: TranscriberLanguage[];
|
|
157
|
+
}) => {
|
|
158
|
+
const transcriberManager = this.getTranscriberManager();
|
|
159
|
+
if (!transcriberManager) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const currentTranscriberRobotId = TUIStore.getData(StoreName.CALL, NAME.TRANSCRIBER_ROBOT_ID);
|
|
165
|
+
|
|
166
|
+
if (currentTranscriberRobotId) {
|
|
167
|
+
await transcriberManager.stopRealtimeTranscriber(currentTranscriberRobotId);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const newTranscriberRobotId = await transcriberManager.startRealtimeTranscriber(config);
|
|
171
|
+
TUIStore.update(StoreName.CALL, NAME.TRANSCRIBER_ROBOT_ID, newTranscriberRobotId);
|
|
172
|
+
|
|
173
|
+
return newTranscriberRobotId;
|
|
174
|
+
} catch (error) {
|
|
175
|
+
console.error(`${NAME.PREFIX}updateRealTimeTranscriber error:`, error);
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
public subscribeEvent = (event: RealtimeTranscriberEvent, callback: RealtimeTranscriberEventCallback) => {
|
|
181
|
+
if (!this.eventListeners.has(event)) {
|
|
182
|
+
this.eventListeners.set(event, []);
|
|
183
|
+
}
|
|
184
|
+
this.eventListeners.get(event)?.push(callback);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
public unsubscribeEvent = (event: RealtimeTranscriberEvent, callback: RealtimeTranscriberEventCallback) => {
|
|
188
|
+
const listeners = this.eventListeners.get(event);
|
|
189
|
+
if (listeners) {
|
|
190
|
+
const index = listeners.indexOf(callback);
|
|
191
|
+
if (index > -1) {
|
|
192
|
+
listeners.splice(index, 1);
|
|
193
|
+
}
|
|
194
|
+
if (listeners.length === 0) {
|
|
195
|
+
this.eventListeners.delete(event);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
private onReceiveTranscriberMessage = (
|
|
201
|
+
_roomId: string | number,
|
|
202
|
+
message: {
|
|
203
|
+
segmentId: string;
|
|
204
|
+
speakerUserId: string;
|
|
205
|
+
sourceText: string;
|
|
206
|
+
translationTexts?: Map<string, string>;
|
|
207
|
+
timestamp: number;
|
|
208
|
+
isCompleted: boolean;
|
|
209
|
+
},
|
|
210
|
+
) => {
|
|
211
|
+
const { segmentId } = message;
|
|
212
|
+
let realtimeMessageList: TranscriberMessage[] = TUIStore.getData(StoreName.CALL, NAME.REALTIME_MESSAGE_LIST) || [];
|
|
213
|
+
|
|
214
|
+
const idx = realtimeMessageList.findIndex(msg => msg.segmentId === segmentId);
|
|
215
|
+
if (idx >= 0) {
|
|
216
|
+
realtimeMessageList[idx] = message;
|
|
40
217
|
} else {
|
|
41
|
-
|
|
218
|
+
realtimeMessageList.push(message);
|
|
42
219
|
}
|
|
43
|
-
|
|
220
|
+
|
|
221
|
+
TUIStore.update(StoreName.CALL, NAME.REALTIME_MESSAGE_LIST, [...realtimeMessageList]);
|
|
222
|
+
|
|
223
|
+
this.triggerEvent(RealtimeTranscriberEvent.onReceiveTranscriberMessage, {
|
|
224
|
+
roomId: _roomId,
|
|
225
|
+
message,
|
|
226
|
+
});
|
|
227
|
+
};
|
|
44
228
|
|
|
45
|
-
|
|
46
|
-
this.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
229
|
+
private onRealtimeTranscriberStarted = (_roomId: string | number, _transcriberRobotId: string, _sourceLanguage: string) => {
|
|
230
|
+
this.triggerEvent(RealtimeTranscriberEvent.onRealtimeTranscriberStarted, {
|
|
231
|
+
roomId: _roomId,
|
|
232
|
+
transcriberRobotId: _transcriberRobotId,
|
|
233
|
+
sourceLanguage: _sourceLanguage,
|
|
234
|
+
});
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
private onRealtimeTranscriberStopped = (_roomId: string | number, _transcriberRobotId: string) => {
|
|
238
|
+
this.triggerEvent(RealtimeTranscriberEvent.onRealtimeTranscriberStopped, {
|
|
239
|
+
roomId: _roomId,
|
|
240
|
+
transcriberRobotId: _transcriberRobotId,
|
|
241
|
+
});
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
private onRealtimeTranscriberError = (_roomId: string | number, _transcriberRobotId: string, error: number, errorMessage: string) => {
|
|
245
|
+
this.triggerEvent(RealtimeTranscriberEvent.onRealtimeTranscriberError, {
|
|
246
|
+
roomId: _roomId,
|
|
247
|
+
transcriberRobotId: _transcriberRobotId,
|
|
248
|
+
error,
|
|
249
|
+
errorMessage,
|
|
250
|
+
});
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
public bindEvent = () => {
|
|
254
|
+
const transcriberManager = this.getTranscriberManager();
|
|
255
|
+
if (!transcriberManager) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
transcriberManager.addListener({
|
|
261
|
+
onReceiveTranscriberMessage: this.onReceiveTranscriberMessage,
|
|
262
|
+
onRealtimeTranscriberStarted: this.onRealtimeTranscriberStarted,
|
|
263
|
+
onRealtimeTranscriberStopped: this.onRealtimeTranscriberStopped,
|
|
264
|
+
onRealtimeTranscriberError: this.onRealtimeTranscriberError,
|
|
265
|
+
});
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error(`${NAME.PREFIX}bindEvent error:`, error);
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
public unbindEvent = () => {
|
|
272
|
+
const transcriberManager = this.getTranscriberManager();
|
|
273
|
+
if (!transcriberManager) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
try {
|
|
278
|
+
transcriberManager.removeListener({
|
|
279
|
+
onReceiveTranscriberMessage: this.onReceiveTranscriberMessage,
|
|
280
|
+
onRealtimeTranscriberStarted: this.onRealtimeTranscriberStarted,
|
|
281
|
+
onRealtimeTranscriberStopped: this.onRealtimeTranscriberStopped,
|
|
282
|
+
onRealtimeTranscriberError: this.onRealtimeTranscriberError,
|
|
283
|
+
});
|
|
284
|
+
} catch (error) {
|
|
285
|
+
console.error(`${NAME.PREFIX}unbindEvent error:`, error);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
public reset = () => {
|
|
290
|
+
TUIStore.update(StoreName.CALL, NAME.TRANSCRIBER_ROBOT_ID, null);
|
|
291
|
+
TUIStore.update(StoreName.CALL, NAME.REALTIME_MESSAGE_LIST, []);
|
|
292
|
+
TUIStore.update(StoreName.CALL, NAME.IS_AI_TRANSCRIBER_ENABLED, false);
|
|
293
|
+
this.eventListeners.clear();
|
|
294
|
+
};
|
|
51
295
|
}
|
|
296
|
+
|
|
297
|
+
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
// @if process.env.BUILD_TARGET ='MINI'
|
|
3
|
+
import { UIKitModal } from '../UIKitModal';
|
|
4
|
+
// @endif
|
|
5
|
+
// @if process.env.BUILD_TARGET!='MINI'
|
|
1
6
|
import TuiStore from '../TUIStore/tuiStore';
|
|
2
7
|
import { StoreName } from '../const/call';
|
|
3
8
|
import { NAME } from '../const/index';
|
|
4
|
-
import { t } from '../locales'
|
|
9
|
+
import { t } from '../locales';
|
|
5
10
|
const TUIStore = TuiStore.getInstance();
|
|
11
|
+
// @endif
|
|
6
12
|
|
|
7
|
-
|
|
13
|
+
// @if process.env.BUILD_TARGET!='MINI'
|
|
14
|
+
const WEB_MODAL_ERROR_CODES = [
|
|
8
15
|
-1001,
|
|
9
16
|
-1002,
|
|
10
17
|
-1101,
|
|
@@ -14,9 +21,10 @@ const MODAL_ERROR_CODES = [
|
|
|
14
21
|
-1201,
|
|
15
22
|
30000,
|
|
16
23
|
20007,
|
|
24
|
+
101002,
|
|
17
25
|
];
|
|
18
26
|
|
|
19
|
-
const
|
|
27
|
+
const WEB_MODAL_ERROR_MAP = {
|
|
20
28
|
'-1001': {
|
|
21
29
|
id: 10001,
|
|
22
30
|
key: 'error.10001'
|
|
@@ -52,19 +60,58 @@ const MODAL_ERROR_MAP = {
|
|
|
52
60
|
'20007': {
|
|
53
61
|
id: 10013,
|
|
54
62
|
key: 'error.10013'
|
|
63
|
+
},
|
|
64
|
+
'101002': {
|
|
65
|
+
id: 10014,
|
|
66
|
+
key: 'error.10014'
|
|
55
67
|
}
|
|
56
68
|
};
|
|
69
|
+
// @endif
|
|
70
|
+
|
|
71
|
+
// @if process.env.BUILD_TARGET ='MINI'
|
|
72
|
+
const MINI_MODAL_ERROR_CODES = [
|
|
73
|
+
-1001,
|
|
74
|
+
-1002,
|
|
75
|
+
101002,
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
const MINI_MODAL_ERROR_MAP = {
|
|
79
|
+
'-1001': {
|
|
80
|
+
id: 10001,
|
|
81
|
+
content: '您的应用还未开通音视频通话能力'
|
|
82
|
+
},
|
|
83
|
+
'-1002': {
|
|
84
|
+
id: 10002,
|
|
85
|
+
content: '您暂不支持使用该能力,请前往购买页购买开通'
|
|
86
|
+
},
|
|
87
|
+
'101002': {
|
|
88
|
+
id: 10014,
|
|
89
|
+
content: '发起通话失败,用户 ID 无效,请确认该用户已注册'
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// @endif
|
|
57
93
|
|
|
58
94
|
export function handleModalError(error) {
|
|
59
95
|
if (!error || !error?.code) {
|
|
60
96
|
return;
|
|
61
97
|
}
|
|
62
98
|
|
|
63
|
-
if
|
|
99
|
+
// @if process.env.BUILD_TARGET!='MINI'
|
|
100
|
+
handleWebModalError(error);
|
|
101
|
+
// @endif
|
|
102
|
+
|
|
103
|
+
// @if process.env.BUILD_TARGET='MINI'
|
|
104
|
+
handleMiniModalError(error);
|
|
105
|
+
// @endif
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// @if process.env.BUILD_TARGET!='MINI'
|
|
109
|
+
function handleWebModalError(error) {
|
|
110
|
+
if (!WEB_MODAL_ERROR_CODES.includes(error.code)) {
|
|
64
111
|
return;
|
|
65
112
|
}
|
|
66
113
|
|
|
67
|
-
const errorInfo =
|
|
114
|
+
const errorInfo = WEB_MODAL_ERROR_MAP[error.code.toString()];
|
|
68
115
|
if (errorInfo) {
|
|
69
116
|
let content = t(errorInfo.key);
|
|
70
117
|
TUIStore.update(StoreName.CALL, NAME.MODAL_ERROR, {
|
|
@@ -73,4 +120,23 @@ export function handleModalError(error) {
|
|
|
73
120
|
title: t('error')
|
|
74
121
|
});
|
|
75
122
|
}
|
|
76
|
-
}
|
|
123
|
+
}
|
|
124
|
+
// @endif
|
|
125
|
+
|
|
126
|
+
// @if process.env.BUILD_TARGET='MINI'
|
|
127
|
+
function handleMiniModalError(error) {
|
|
128
|
+
if (!MINI_MODAL_ERROR_CODES.includes(error.code)) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const errorInfo = MINI_MODAL_ERROR_MAP[error.code.toString()];
|
|
133
|
+
if (errorInfo) {
|
|
134
|
+
UIKitModal.openModal({
|
|
135
|
+
id: errorInfo.id,
|
|
136
|
+
content: errorInfo.content,
|
|
137
|
+
title: '错误',
|
|
138
|
+
type: 'error'
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// @endif
|
|
@@ -10,6 +10,7 @@ export class BellContext {
|
|
|
10
10
|
private _calleeBellFilePath: string = DEFAULT_CALLEE_BELL_FILEPATH;
|
|
11
11
|
private _callRole: string = CallRole.UNKNOWN;
|
|
12
12
|
private _callStatus: string = CallStatus.IDLE;
|
|
13
|
+
private _isPlaying: boolean = false;
|
|
13
14
|
|
|
14
15
|
constructor() {
|
|
15
16
|
this._bellContext = new Audio();
|
|
@@ -39,14 +40,26 @@ export class BellContext {
|
|
|
39
40
|
async play() {
|
|
40
41
|
try {
|
|
41
42
|
if (this._callStatus !== CallStatus.CALLING) {
|
|
42
|
-
return
|
|
43
|
+
return;
|
|
43
44
|
}
|
|
45
|
+
// iOS 优化:增加更严格的状态检查
|
|
46
|
+
if (this._callStatus !== CallStatus.CALLING || this._isPlaying) {
|
|
47
|
+
console.warn(`${NAME.PREFIX}play skipped, callStatus: ${this._callStatus}, isPlaying: ${this._isPlaying}`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
this._isPlaying = true;
|
|
44
51
|
this.setBellSrc();
|
|
45
52
|
if (this._callRole === CallRole.CALLEE && !this._isMuteBell) {
|
|
53
|
+
// 再次检查状态,避免在设置过程中状态已变更
|
|
54
|
+
if (!this._isPlaying || this._callStatus !== CallStatus.CALLING) return;
|
|
46
55
|
await this._bellContext.play();
|
|
56
|
+
return;
|
|
47
57
|
}
|
|
48
58
|
if (this._callRole === CallRole.CALLER) {
|
|
59
|
+
// 再次检查状态,避免在设置过程中状态已变更
|
|
60
|
+
if (!this._isPlaying || this._callStatus !== CallStatus.CALLING) return;
|
|
49
61
|
await this._bellContext.play();
|
|
62
|
+
return;
|
|
50
63
|
}
|
|
51
64
|
} catch (error) {
|
|
52
65
|
console.warn(`${NAME.PREFIX}Failed to play audio file, ${error}`);
|
|
@@ -55,7 +68,8 @@ export class BellContext {
|
|
|
55
68
|
|
|
56
69
|
async stop() {
|
|
57
70
|
try {
|
|
58
|
-
|
|
71
|
+
this._isPlaying = false;
|
|
72
|
+
await this._bellContext?.pause();
|
|
59
73
|
} catch (error) {
|
|
60
74
|
console.warn(`${NAME.PREFIX}Failed to stop audio file, ${error}`);
|
|
61
75
|
}
|
|
@@ -78,11 +92,20 @@ export class BellContext {
|
|
|
78
92
|
this._calleeBellFilePath = '';
|
|
79
93
|
this._callRole = CallRole.UNKNOWN;
|
|
80
94
|
this._callStatus = CallStatus.IDLE;
|
|
95
|
+
this._isPlaying = false;
|
|
81
96
|
this._bellContext.pause();
|
|
82
97
|
this._bellContext = null;
|
|
83
98
|
} catch (error) {
|
|
84
99
|
console.warn(`${NAME.PREFIX}Failed to destroy, ${error}`);
|
|
85
100
|
}
|
|
86
101
|
}
|
|
102
|
+
|
|
103
|
+
private _delay(ms: number): any {
|
|
104
|
+
return new Promise((resolve: any) => setTimeout(resolve, ms));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private _delayCallback(callback: () => void, ms: number) {
|
|
108
|
+
setTimeout(callback, ms);
|
|
109
|
+
}
|
|
87
110
|
|
|
88
111
|
}
|