@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.
Files changed (75) hide show
  1. package/package.json +4 -3
  2. package/src/Components/assets/aiAssistant/desktop/subtitleSettings.svg +4 -0
  3. package/src/Components/assets/aiAssistant/mobile/close-aiAssistant.svg +7 -0
  4. package/src/Components/assets/aiAssistant/mobile/open-aiAssistant.svg +7 -0
  5. package/src/Components/assets/aiAssistant/mobile/subtitleSettings.svg +3 -0
  6. package/src/Components/components/base/CustomSelect/CustomSelect.scss +116 -0
  7. package/src/Components/components/base/CustomSelect/CustomSelect.tsx +96 -0
  8. package/src/Components/components/common/AIAssistant/AISubtitle.scss +109 -0
  9. package/src/Components/components/common/AIAssistant/AISubtitle.tsx +52 -66
  10. package/src/Components/components/common/AIAssistant/components/AITranscriberSwitchH5.tsx +39 -0
  11. package/src/Components/components/common/AIAssistant/components/AITranscriberSwitchPC.scss +46 -0
  12. package/src/Components/components/common/AIAssistant/components/AITranscriberSwitchPC.tsx +42 -0
  13. package/src/Components/components/common/AIAssistant/components/SubtitleContent.scss +67 -0
  14. package/src/Components/components/common/AIAssistant/components/SubtitleContent.tsx +134 -0
  15. package/src/Components/components/common/AIAssistant/components/SubtitleSettingsH5.scss +275 -0
  16. package/src/Components/components/common/AIAssistant/components/SubtitleSettingsH5.tsx +265 -0
  17. package/src/Components/components/common/AIAssistant/components/SubtitleSettingsPC.scss +98 -0
  18. package/src/Components/components/common/AIAssistant/components/SubtitleSettingsPC.tsx +198 -0
  19. package/src/Components/components/common/AIAssistant/components/index.ts +5 -0
  20. package/src/Components/components/common/AIAssistant/index.ts +3 -1
  21. package/src/Components/components/common/TopBar/h5/TopBarH5.tsx +11 -1
  22. package/src/Components/components/common/TopBar/pc/TopBarPC.module.scss +20 -0
  23. package/src/Components/components/common/TopBar/pc/TopBarPC.tsx +14 -9
  24. package/src/Components/components/common/UIKitModal/UIKitModal.tsx +250 -0
  25. package/src/Components/components/common/UIKitModal/UIKitModalState.ts +177 -0
  26. package/src/Components/components/common/UIKitModal/index.module.scss +176 -0
  27. package/src/Components/components/common/UIKitModal/index.ts +3 -0
  28. package/src/Components/hooks/index.ts +2 -0
  29. package/src/Components/hooks/useAIAssistant.ts +174 -0
  30. package/src/TUICallService/CallService/AIAssistant.ts +285 -39
  31. package/src/TUICallService/CallService/UIKitModal.ts +72 -6
  32. package/src/TUICallService/CallService/bellContext.ts +25 -2
  33. package/src/TUICallService/CallService/engineEventHandler.ts +6 -1
  34. package/src/TUICallService/CallService/index.ts +72 -39
  35. package/src/TUICallService/CallService/miniProgram.ts +0 -12
  36. package/src/TUICallService/TUIStore/callStore.ts +6 -1
  37. package/src/TUICallService/UIKitModal/UIKitModal.ts +117 -0
  38. package/src/TUICallService/UIKitModal/index.ts +2 -0
  39. package/src/TUICallService/UIKitModal/type.ts +15 -0
  40. package/src/TUICallService/const/index.ts +4 -0
  41. package/src/TUICallService/interface/ICallStore.ts +5 -0
  42. package/src/TUICallService/locales/en.ts +17 -1
  43. package/src/TUICallService/locales/ja_JP.ts +17 -1
  44. package/src/TUICallService/locales/zh-cn.ts +17 -1
  45. package/src/TUICallService/utils/common-utils.ts +1 -1
  46. package/src/index.ts +1 -1
  47. package/tuicall-uikit-react.es.js +4009 -3940
  48. package/tuicall-uikit-react.umd.js +8 -33
  49. package/types/Components/components/base/CustomSelect/CustomSelect.d.ts +15 -0
  50. package/types/Components/components/common/AIAssistant/AISubtitle.d.ts +1 -0
  51. package/types/Components/components/common/AIAssistant/components/AITranscriberSwitchH5.d.ts +3 -0
  52. package/types/Components/components/common/AIAssistant/components/AITranscriberSwitchPC.d.ts +4 -0
  53. package/types/Components/components/common/AIAssistant/components/SubtitleContent.d.ts +8 -0
  54. package/types/Components/components/common/AIAssistant/components/SubtitleSettingsH5.d.ts +13 -0
  55. package/types/Components/components/common/AIAssistant/components/SubtitleSettingsPC.d.ts +13 -0
  56. package/types/Components/components/common/AIAssistant/components/index.d.ts +5 -0
  57. package/types/Components/components/common/AIAssistant/index.d.ts +1 -0
  58. package/types/Components/components/common/UIKitModal/UIKitModal.d.ts +21 -0
  59. package/types/Components/components/common/UIKitModal/UIKitModalState.d.ts +27 -0
  60. package/types/Components/components/common/UIKitModal/index.d.ts +3 -0
  61. package/types/Components/hooks/index.d.ts +2 -1
  62. package/types/Components/hooks/useAIAssistant.d.ts +22 -0
  63. package/types/TUICallService/CallService/AIAssistant.d.ts +72 -15
  64. package/types/TUICallService/CallService/bellContext.d.ts +3 -0
  65. package/types/TUICallService/CallService/index.d.ts +4 -3
  66. package/types/TUICallService/CallService/miniProgram.d.ts +0 -1
  67. package/types/TUICallService/UIKitModal/UIKitModal.d.ts +4 -0
  68. package/types/TUICallService/UIKitModal/index.d.ts +2 -0
  69. package/types/TUICallService/UIKitModal/type.d.ts +13 -0
  70. package/types/TUICallService/interface/ICallStore.d.ts +4 -0
  71. package/types/TUICallService/locales/en.d.ts +15 -0
  72. package/types/TUICallService/locales/ja_JP.d.ts +15 -0
  73. package/types/TUICallService/locales/zh-cn.d.ts +15 -0
  74. package/src/TUICallService/utils/validate/validateStatus.ts +0 -26
  75. 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
- // @ts-ignore
2
- import { aiAssistant } from '../../Components/components/common/AIAssistant/index';
3
- import { getRemoteUserProfile } from './utils';
4
-
5
- interface IAIAssistant {
6
- enableAISubtitle: (enable: boolean) => void;
7
- setEngineInstance: (engineInstance: any) => void;
8
- setImInstance: (imInstance: any) => void;
9
- };
10
-
11
- export class AIAssistant implements IAIAssistant {
12
- static instance: IAIAssistant;
13
- private _tuiCallEngine: any = null;
14
- private _imInstance: any = null;
15
-
16
- static getInstance() {
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
- public enableAISubtitle(enable: boolean): void {
24
- this._tuiCallEngine?.reportLog?.({
25
- name: 'TUICallKit.enableAISubtitle.start',
26
- data: { enable },
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 trtcCloudInstance = this._tuiCallEngine.getTRTCCloudInstance();
30
- const getNickName = async(userId: string) => {
31
- const res = await getRemoteUserProfile([userId], this._imInstance);
32
- return res.length ? res[0] : [];
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
- if (enable) {
35
- aiAssistant.initASR({
36
- trtcCloudInstance,
37
- // @ts-ignore
38
- getNickName,
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
- aiAssistant.destroyASR();
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
- public setEngineInstance(engineInstance: any): void {
46
- this._tuiCallEngine = engineInstance;
47
- }
48
- public setImInstance(imInstance: any): void {
49
- this._imInstance = imInstance;
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
- const MODAL_ERROR_CODES = [
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 MODAL_ERROR_MAP = {
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 (!MODAL_ERROR_CODES.includes(error.code)) {
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 = MODAL_ERROR_MAP[error.code.toString()];
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
- await this._bellContext.pause();
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
  }