@tencentcloud/roomkit-electron-vue3 2.7.0 → 2.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/es/components/AITools/AISubtitles.vue.mjs +1 -1
  2. package/es/components/AITools/AISubtitles.vue2.mjs +18 -19
  3. package/es/components/AITools/AITranscription.vue.mjs +1 -1
  4. package/es/components/AITools/AITranscription.vue2.mjs +65 -76
  5. package/es/components/Chat/ChatEditor/index.vue2.mjs +1 -1
  6. package/es/components/Chat/ChatKit/components/TUIChat/chat-header/index.vue2.mjs +1 -1
  7. package/es/components/Chat/ChatKit/components/TUIChat/emoji-config/default-emoji.mjs +4 -4
  8. package/es/components/Chat/ChatKit/components/TUIChat/emoji-config/index.mjs +1 -1
  9. package/es/components/Chat/ChatKit/components/TUIChat/index.vue2.mjs +1 -1
  10. package/es/components/Chat/ChatKit/components/TUIChat/message-input/message-input-quote/index.vue2.mjs +1 -1
  11. package/es/components/Chat/ChatKit/components/TUIChat/message-input-toolbar/emoji-picker/emoji-picker-dialog.vue2.mjs +1 -1
  12. package/es/components/Chat/ChatKit/components/TUIChat/message-input-toolbar/image-upload/index.vue2.mjs +1 -1
  13. package/es/components/Chat/ChatKit/components/TUIChat/message-input-toolbar/video-upload/index.vue2.mjs +1 -1
  14. package/es/components/Chat/ChatKit/components/TUIChat/message-list/index.vue2.mjs +2 -2
  15. package/es/components/Chat/ChatKit/components/TUIChat/message-list/message-elements/message-audio.vue2.mjs +1 -1
  16. package/es/components/Chat/ChatKit/components/TUIChat/message-list/message-elements/message-record/index.vue2.mjs +1 -1
  17. package/es/components/Chat/ChatKit/components/TUIChat/message-list/message-group-application/index.vue2.mjs +2 -2
  18. package/es/components/Chat/ChatKit/components/TUIChat/offlinePushInfoManager/offlinePushInfoManager.mjs +1 -1
  19. package/es/components/Chat/ChatKit/components/TUIChat/utils/sendMessage.mjs +1 -1
  20. package/es/components/Chat/ChatKit/components/common/BottomPopup/index.vue2.mjs +1 -1
  21. package/es/components/Chat/ChatKit/components/common/Dialog/index.vue2.mjs +1 -1
  22. package/es/components/Chat/ChatKit/components/common/Drawer/index.vue2.mjs +1 -1
  23. package/es/components/Chat/ChatKit/components/common/Icon.vue2.mjs +1 -1
  24. package/es/components/Chat/ChatKit/components/common/Overlay/index.vue2.mjs +1 -1
  25. package/es/components/Chat/ChatKit/components/common/Toast/index.mjs +1 -1
  26. package/es/components/Chat/ChatKit/components/common/Toast/index.vue2.mjs +1 -1
  27. package/es/components/Chat/ChatKit/components/common/Transfer/index.vue2.mjs +1 -1
  28. package/es/components/Chat/ChatKit/locales/index.mjs +4 -4
  29. package/es/components/Chat/ChatKit/plugins/plugin-components/message-plugin.vue2.mjs +1 -1
  30. package/es/components/ManageMember/MemberControl/index.vue.mjs +1 -1
  31. package/es/components/ManageMember/MemberControl/index.vue2.mjs +6 -4
  32. package/es/components/ManageMember/MemberControl/useMemberControlHooks.mjs +1 -1
  33. package/es/components/ManageMember/MemberItemCommon/MemberInfo.vue.mjs +1 -1
  34. package/es/components/ManageMember/index.vue2.mjs +1 -1
  35. package/es/components/PreRoom/PasswordDialog.vue2.mjs +1 -1
  36. package/es/components/RoomContent/StreamRegion/StreamPlay/index.vue2.mjs +1 -1
  37. package/es/components/RoomFooter/ApplyControl/MasterApplyControl/index.vue2.mjs +1 -1
  38. package/es/components/RoomFooter/ApplyControl/MemberApplyControl.vue2.mjs +1 -1
  39. package/es/components/RoomFooter/AudioControl.vue2.mjs +1 -1
  40. package/es/components/RoomFooter/BasicBeauty.vue.mjs +1 -1
  41. package/es/components/RoomFooter/BasicBeauty.vue2.mjs +5 -3
  42. package/es/components/RoomFooter/ChatControl.vue.mjs +2 -0
  43. package/es/components/RoomFooter/EndControl/index.vue2.mjs +1 -1
  44. package/es/components/RoomFooter/ScreenShareControl/Index.vue2.mjs +1 -1
  45. package/es/components/RoomFooter/ScreenShareControl/ScreenWindowSelectDialog.vue2.mjs +1 -1
  46. package/es/components/RoomFooter/VideoControl.vue2.mjs +2 -2
  47. package/es/components/RoomFooter/VirtualBackground.vue2.mjs +1 -1
  48. package/es/components/RoomFooter/WhiteboardControl.vue.mjs +1 -1
  49. package/es/components/RoomHeader/UserInfo/index.vue.mjs +1 -1
  50. package/es/components/RoomHeader/UserInfo/index.vue2.mjs +2 -1
  51. package/es/components/RoomHeader/index/LayoutControl.vue2.mjs +1 -1
  52. package/es/components/RoomHome/RoomControl/index.vue2.mjs +2 -2
  53. package/es/components/RoomInvite/InvitationNotification.vue2.mjs +1 -1
  54. package/es/components/RoomInvite/index.vue2.mjs +1 -1
  55. package/es/components/RoomSetting/index.vue2.mjs +1 -1
  56. package/es/components/ScheduleConference/Contacts.vue2.mjs +1 -1
  57. package/es/components/ScheduleConference/ScheduleConferencePanel/index.vue2.mjs +2 -2
  58. package/es/components/ScheduleConference/ScheduleRoomControl.vue2.mjs +2 -2
  59. package/es/components/ScheduleConference/ScheduleRoomList.vue2.mjs +1 -1
  60. package/es/components/ScheduleConference/ShareLink.vue2.mjs +1 -1
  61. package/es/components/common/VideoProfile.vue2.mjs +1 -1
  62. package/es/components/common/VideoSettingTab.vue2.mjs +2 -2
  63. package/es/components/common/base/Button.vue2.mjs +1 -1
  64. package/es/components/common/base/Dialog/index.vue2.mjs +1 -1
  65. package/es/components/common/base/Input/index.vue2.mjs +1 -1
  66. package/es/components/common/base/Message/Message.vue2.mjs +1 -1
  67. package/es/components/common/base/MessageBox/index.vue2.mjs +1 -1
  68. package/es/components/common/base/SvgIcon.vue2.mjs +1 -1
  69. package/es/constants/room.mjs +1 -3
  70. package/es/extension/RoomMessageCard/RoomMessageCard.vue2.mjs +1 -1
  71. package/es/extension/chatExtension.mjs +3 -6
  72. package/es/hooks/useDeviceManager.mjs +1 -1
  73. package/es/index.mjs +134 -126
  74. package/es/services/function/aiTask.d.ts +13 -17
  75. package/es/services/function/aiTask.mjs +63 -100
  76. package/es/services/manager/MediaManager.mjs +1 -1
  77. package/es/services/manager/dataReportManager.d.ts +4 -1
  78. package/es/services/manager/dataReportManager.mjs +3 -0
  79. package/es/services/manager/roomActionManager.mjs +1 -1
  80. package/es/services/roomService.mjs +1 -1
  81. package/es/stores/basic.mjs +1 -1
  82. package/es/stores/room.mjs +1 -4
  83. package/es/utils/common/logger/index.mjs +1 -1
  84. package/es/utils/utils.d.ts +2 -0
  85. package/es/utils/utils.mjs +33 -0
  86. package/lib/components/AITools/AISubtitles.vue.js +1 -1
  87. package/lib/components/AITools/AISubtitles.vue2.js +17 -18
  88. package/lib/components/AITools/AITranscription.vue.js +1 -1
  89. package/lib/components/AITools/AITranscription.vue2.js +64 -75
  90. package/lib/components/Chat/ChatKit/components/TUIChat/message-input/index.vue2.js +6 -6
  91. package/lib/components/Chat/ChatKit/components/TUIChat/message-list/index.vue2.js +8 -8
  92. package/lib/components/Chat/ChatKit/components/TUIChat/message-list/message-elements/message-quote/index.vue2.js +4 -4
  93. package/lib/components/Chat/ChatKit/components/TUIChat/message-list/message-tool/index.vue2.js +10 -10
  94. package/lib/components/Chat/ChatKit/locales/index.js +4 -4
  95. package/lib/components/ManageMember/MemberControl/index.vue.js +1 -1
  96. package/lib/components/ManageMember/MemberControl/index.vue2.js +5 -3
  97. package/lib/components/ManageMember/MemberItemCommon/MemberInfo.vue.js +1 -1
  98. package/lib/components/RoomContent/StreamContainer/index.vue2.js +6 -6
  99. package/lib/components/RoomFooter/AudioControl.vue2.js +4 -4
  100. package/lib/components/RoomFooter/BasicBeauty.vue.js +1 -1
  101. package/lib/components/RoomFooter/BasicBeauty.vue2.js +3 -1
  102. package/lib/components/RoomFooter/ChatControl.vue.js +2 -0
  103. package/lib/components/RoomFooter/ScreenShareControl/Index.vue2.js +5 -5
  104. package/lib/components/RoomFooter/VideoControl.vue2.js +4 -4
  105. package/lib/components/RoomHeader/UserInfo/index.vue.js +1 -1
  106. package/lib/components/RoomHeader/UserInfo/index.vue2.js +2 -1
  107. package/lib/components/ScheduleConference/ScheduleConferencePanel/index.vue2.js +1 -1
  108. package/lib/components/ScheduleConference/ScheduleRoomControl.vue2.js +1 -1
  109. package/lib/components/ScheduleConference/ScheduleRoomList.vue2.js +1 -1
  110. package/lib/conference.vue2.js +12 -12
  111. package/lib/constants/room.js +1 -3
  112. package/lib/extension/chatExtension.js +1 -4
  113. package/lib/hooks/useMasterApplyControl.js +9 -9
  114. package/lib/index.js +134 -126
  115. package/lib/preConference.vue2.js +7 -7
  116. package/lib/services/function/aiTask.d.ts +13 -17
  117. package/lib/services/function/aiTask.js +63 -100
  118. package/lib/services/manager/dataReportManager.d.ts +4 -1
  119. package/lib/services/manager/dataReportManager.js +3 -0
  120. package/lib/stores/room.js +0 -3
  121. package/lib/utils/utils.d.ts +2 -0
  122. package/lib/utils/utils.js +33 -0
  123. package/package.json +2 -2
  124. package/src/TUIRoom/components/AITools/AISubtitles.vue +26 -23
  125. package/src/TUIRoom/components/AITools/AITranscription.vue +106 -101
  126. package/src/TUIRoom/components/ManageMember/MemberControl/index.vue +8 -3
  127. package/src/TUIRoom/components/ManageMember/MemberItemCommon/MemberInfo.vue +1 -1
  128. package/src/TUIRoom/components/RoomFooter/BasicBeauty.vue +2 -1
  129. package/src/TUIRoom/components/RoomFooter/ChatControl.vue +2 -1
  130. package/src/TUIRoom/components/RoomHeader/UserInfo/index.vue +2 -5
  131. package/src/TUIRoom/services/function/aiTask.ts +79 -113
  132. package/src/TUIRoom/services/manager/dataReportManager.ts +3 -0
  133. package/src/TUIRoom/stores/room.ts +0 -3
  134. package/src/TUIRoom/utils/utils.ts +47 -0
@@ -1,141 +1,138 @@
1
1
  <template>
2
- <div class="conversation" ref="conversationContainer">
2
+ <div
3
+ class="conversation"
4
+ ref="conversationContainerRef"
5
+ @scroll="handleScroll"
6
+ >
3
7
  <div
4
- v-for="(item, index) in conversations"
5
- :key="index"
6
- class="conversation-item"
8
+ v-for="group in transcribedMessageList"
9
+ :key="group.startMsTs"
10
+ class="conversation-group"
7
11
  >
8
12
  <div class="title">
9
- <span class="speaker">{{ item.speaker }}</span>
10
- <span class="timestamp">{{ item.timestamp }}</span>
13
+ <span class="speaker">{{
14
+ roomService.roomStore.getDisplayName(group.sender)
15
+ }}</span>
16
+ <span class="timestamp">
17
+ {{ formatTimestampToTime(group.startMsTs) }}
18
+ </span>
11
19
  </div>
12
- <div class="content">
13
- <div v-for="(msg, msgIndex) in item.messages" :key="msgIndex">
14
- {{ msg }}
15
- </div>
20
+ <div
21
+ v-for="(message, messageIndex) in group.messages"
22
+ :key="messageIndex"
23
+ class="content"
24
+ >
25
+ {{ message.text }}
16
26
  </div>
17
27
  </div>
18
28
  </div>
19
29
  </template>
20
30
 
21
31
  <script setup lang="ts">
22
- import { ref, computed, onMounted, nextTick } from 'vue';
23
- import { roomService, AI_TASK } from '../../services';
24
-
25
- const conversationContainer = ref<HTMLElement | null>(null);
26
-
27
- onMounted(() => scrollToBottom());
28
-
29
- const processedConversations = ref<
30
- {
31
- timestamp: string;
32
- speaker: string;
33
- content: string;
34
- }[]
35
- >(processConversation(roomService.aiTask.transcriptionText.value));
32
+ import { ref, onMounted, computed, nextTick, watch, onUnmounted } from 'vue';
33
+ import {
34
+ roomService,
35
+ AI_TASK,
36
+ SubtitleMessage,
37
+ MetricsKey,
38
+ } from '../../services';
39
+ import { formatTimestampToTime } from '../../utils/utils.ts';
40
+
41
+ const conversationContainerRef = ref<HTMLElement>();
42
+ const isUserScrolling = ref(false);
43
+ const timeInterval = 60 * 1000;
44
+ const rawTranscribedMessageList = ref<SubtitleMessage[]>(
45
+ roomService.aiTask.transcribedMessageList
46
+ );
47
+
48
+ onMounted(() => {
49
+ scrollToBottom();
50
+ });
36
51
 
37
52
  const scrollToBottom = async () => {
38
- if (conversationContainer.value) {
53
+ if (conversationContainerRef.value && !isUserScrolling.value) {
39
54
  await nextTick();
40
- conversationContainer.value.scrollIntoView({ block: 'end' });
55
+ conversationContainerRef.value.scrollTop =
56
+ conversationContainerRef.value.scrollHeight;
41
57
  }
42
58
  };
43
59
 
44
- const conversations = computed(() => {
45
- const result = [];
46
- let currentSpeaker: string | null = null;
47
- let currentTimestamp: string | null = null;
48
- let currentMessages: string[] = [];
60
+ const handleScroll = () => {
61
+ if (conversationContainerRef.value) {
62
+ const isAtBottom =
63
+ conversationContainerRef.value.scrollTop +
64
+ conversationContainerRef.value.clientHeight >=
65
+ conversationContainerRef.value.scrollHeight - 5;
66
+ if (isAtBottom) {
67
+ isUserScrolling.value = false;
68
+ scrollToBottom();
69
+ } else {
70
+ isUserScrolling.value = true;
71
+ }
72
+ }
73
+ };
49
74
 
50
- processedConversations.value.forEach(item => {
51
- const [start, end] = item.timestamp.split('->');
52
- const timestampMinute = start.slice(0, 5); // Take the "HH:MM" part
75
+ const transcribedMessageList = computed(() => {
76
+ const aggregatedMessageList: {
77
+ sender: string;
78
+ startMsTs: number;
79
+ messages: SubtitleMessage[];
80
+ }[] = [];
81
+ let currentAggregatedMessage: {
82
+ sender: string;
83
+ startMsTs: number;
84
+ messages: SubtitleMessage[];
85
+ } | null = null;
53
86
 
87
+ for (const message of rawTranscribedMessageList.value) {
54
88
  if (
55
- item.speaker === currentSpeaker &&
56
- timestampMinute === currentTimestamp
89
+ !currentAggregatedMessage ||
90
+ message.sender !== currentAggregatedMessage.sender ||
91
+ message.startMsTs - currentAggregatedMessage.startMsTs > timeInterval
57
92
  ) {
58
- // Aggregate messages from the same speaker and within the same minute
59
- currentMessages.push(item.content);
93
+ currentAggregatedMessage = {
94
+ messages: [message],
95
+ sender: message.sender,
96
+ startMsTs: message.startMsTs,
97
+ };
98
+ aggregatedMessageList.push(currentAggregatedMessage);
60
99
  } else {
61
- // New speakers or different minutes, save previous aggregation results
62
- if (currentSpeaker !== null) {
63
- result.push({
64
- timestamp: currentTimestamp,
65
- speaker: currentSpeaker,
66
- messages: currentMessages,
67
- });
68
- }
69
- // Update active speaker, timestamp, and message
70
- currentSpeaker = item.speaker;
71
- currentTimestamp = timestampMinute;
72
- currentMessages = [item.content];
100
+ currentAggregatedMessage.messages.push(message);
73
101
  }
74
- });
75
-
76
- // Save the last set of aggregation results
77
- if (currentSpeaker !== null) {
78
- result.push({
79
- timestamp: currentTimestamp,
80
- speaker: currentSpeaker,
81
- messages: currentMessages,
82
- });
83
102
  }
84
103
 
85
- return result;
104
+ return aggregatedMessageList;
86
105
  });
87
106
 
88
- roomService.aiTask.on(AI_TASK.TRANSCRIPTION_TASK, data => {
89
- if (!data) return;
90
- processedConversations.value = processConversation(
91
- data.transcriptionText.value
92
- );
107
+ watch(rawTranscribedMessageList, () => {
108
+ scrollToBottom();
93
109
  });
94
110
 
95
- function processConversation(data: string) {
96
- const pattern =
97
- /(\d{2}:\d{2}:\d{2}->\d{2}:\d{2}:\d{2})\s+([\u4e00-\u9fa5\w]+):\s*(.*)/;
98
-
99
- const conversations: {
100
- timestamp: string;
101
- speaker: string;
102
- content: string;
103
- }[] = [];
104
-
105
- const lines = data.split('\n');
106
-
107
- lines.forEach(line => {
108
- const match = pattern.exec(line);
109
- if (match) {
110
- const timestamp = match[1];
111
- const speaker = match[2];
112
- const content = match[3];
113
- conversations.push({
114
- timestamp,
115
- speaker,
116
- content,
117
- });
118
- }
119
- });
120
-
121
- conversations.sort((a, b) => {
122
- const timeA = a.timestamp.split('->')[0];
123
- const timeB = b.timestamp.split('->')[0];
124
- return timeA.localeCompare(timeB);
125
- });
126
-
127
- return conversations;
128
- }
111
+ const handleAITranscriptionTask = async (data?: {
112
+ subtitleMessages: { [key: string]: SubtitleMessage };
113
+ transcribedMessageList: SubtitleMessage[];
114
+ }) => {
115
+ if (!data) return;
116
+ rawTranscribedMessageList.value = [...data.transcribedMessageList];
117
+ };
118
+ onMounted(() => {
119
+ roomService.dataReportManager.reportCount(MetricsKey.aiTask);
120
+ roomService.aiTask.on(AI_TASK.TRANSCRIPTION_TASK, handleAITranscriptionTask);
121
+ });
122
+ onUnmounted(() => {
123
+ roomService.aiTask.off(AI_TASK.TRANSCRIPTION_TASK, handleAITranscriptionTask);
124
+ });
129
125
  </script>
130
126
 
131
127
  <style scoped lang="scss">
132
128
  .conversation {
129
+ height: 100%;
133
130
  padding: 20px;
134
131
  overflow-y: auto;
135
132
  }
136
133
 
137
- .conversation-item {
138
- margin-bottom: 10px;
134
+ .conversation-group {
135
+ margin-bottom: 20px;
139
136
  }
140
137
 
141
138
  .title {
@@ -147,10 +144,18 @@ function processConversation(data: string) {
147
144
  line-height: 22px;
148
145
  color: var(--font-color-4);
149
146
  text-align: left;
147
+
148
+ .speaker {
149
+ max-width: 150px;
150
+ overflow: hidden;
151
+ text-overflow: ellipsis;
152
+ white-space: nowrap;
153
+ }
150
154
  }
151
155
 
152
156
  .content {
153
157
  padding: 8px;
158
+ margin-top: 5px;
154
159
  font-size: 14px;
155
160
  font-weight: 400;
156
161
  line-height: 22px;
@@ -1,10 +1,10 @@
1
1
  <template>
2
2
  <div class="member-control-container">
3
3
  <tui-button
4
+ v-if="singleControl"
4
5
  class="button"
5
6
  size="default"
6
7
  @click="singleControl.func(props.userInfo)"
7
- v-if="!isCanOperateMySelf"
8
8
  >
9
9
  {{ singleControl?.title }}
10
10
  </tui-button>
@@ -105,9 +105,14 @@ const {
105
105
 
106
106
  const { isCanOperateMySelf } = useMemberItemHooks(props.userInfo);
107
107
 
108
- const singleControl = computed(() => controlList.value[0]);
108
+ const singleControl = computed(() => {
109
+ return isCanOperateMySelf.value ? null : controlList.value[0];
110
+ });
111
+
109
112
  const moreControlList = computed(() => {
110
- return isCanOperateMySelf ? controlList.value : controlList.value.slice(1);
113
+ return isCanOperateMySelf.value
114
+ ? controlList.value
115
+ : controlList.value.slice(1);
111
116
  });
112
117
  const dropdownClass = ref('down');
113
118
  const moreBtnRef = ref();
@@ -221,7 +221,7 @@ const iconList = computed(() => {
221
221
  height: 100%;
222
222
  color: var(--icon-color);
223
223
 
224
- .state-icon .member-invite {
224
+ .state-icon {
225
225
  margin-left: 16px;
226
226
  }
227
227
 
@@ -108,7 +108,7 @@ import IconButton from '../common/base/IconButton.vue';
108
108
  import SvgIcon from '../common/base/SvgIcon.vue';
109
109
  import BasicBeautyIcon from '../common/icons/BasicBeautyIcon.vue';
110
110
  import { useI18n } from '../../locales';
111
- import { roomService } from '../../services';
111
+ import { roomService, MetricsKey } from '../../services';
112
112
  import Dialog from '../common/base/Dialog/index.vue';
113
113
  import TuiButton from '../common/base/Button.vue';
114
114
  import Slider from '../common/base/Slider.vue';
@@ -197,6 +197,7 @@ const openBeautySettingPanel = async () => {
197
197
  view: 'test-preview',
198
198
  });
199
199
  isLoading.value = false;
200
+ roomService.dataReportManager.reportCount(MetricsKey.setBasicBeauty);
200
201
  };
201
202
 
202
203
  const closeBeautySettingPanel = async () => {
@@ -23,7 +23,7 @@ import { useChatStore } from '../../stores/chat';
23
23
  import { storeToRefs } from 'pinia';
24
24
  import { useI18n } from '../../locales';
25
25
  import TuiBadge from '../common/base/Badge.vue';
26
- import { roomService } from '../../services';
26
+ import { roomService, MetricsKey } from '../../services';
27
27
  const { t } = useI18n();
28
28
  const chatControlConfig = roomService.getComponentConfig('ChatControl');
29
29
  const basicStore = useBasicStore();
@@ -39,5 +39,6 @@ async function toggleChatSidebar() {
39
39
  basicStore.setSidebarOpenStatus(true);
40
40
  basicStore.setSidebarName('chat');
41
41
  chatStore.updateUnReadCount(0);
42
+ roomService.dataReportManager.reportCount(MetricsKey.openChat);
42
43
  }
43
44
  </script>
@@ -97,11 +97,8 @@ const props = defineProps<{
97
97
  isShowEditName?: boolean;
98
98
  }>();
99
99
 
100
- /**
101
- * Save the new userName
102
- *
103
- **/
104
100
  async function saveUserName(userName: string) {
101
+ const { userId } = props;
105
102
  if (userName.length === 0) {
106
103
  TUIMessage({
107
104
  type: 'warning',
@@ -115,7 +112,7 @@ async function saveUserName(userName: string) {
115
112
  avatarUrl: roomStore.localUser.avatarUrl || '',
116
113
  });
117
114
  basicStore.setUserName(userName);
118
- roomStore.setLocalUser({ userName });
115
+ roomStore.updateUserInfo({ userId, userName });
119
116
  emits('update-user-name', userName);
120
117
  closeUserNameEditor();
121
118
  }
@@ -1,12 +1,16 @@
1
1
  import { IRoomService } from '../';
2
2
  import mitt from 'mitt';
3
3
  import { isElectron, isMobile } from '../../utils/environment';
4
+ import { findLastIndex } from '../../utils/utils';
4
5
 
5
- interface SubtitleMessage {
6
- userid: string;
6
+ export interface SubtitleMessage {
7
+ sender: string;
7
8
  text: string;
8
- translation_text: string;
9
+ translationText: string;
10
+ end?: boolean;
11
+ startMsTs: number;
9
12
  }
13
+
10
14
  interface DataPayload {
11
15
  end: boolean;
12
16
  text: string;
@@ -34,22 +38,23 @@ export enum AI_TASK {
34
38
 
35
39
  export interface AITaskEvent {
36
40
  [AI_TASK.TRANSCRIPTION_TASK]: {
37
- subtitleMsg: SubtitleMessage[];
38
- subtitleText: { value: string };
39
- transcriptionText: { value: string };
41
+ subtitleMessages: { [key: string]: SubtitleMessage };
42
+ transcribedMessageList: SubtitleMessage[];
40
43
  };
41
44
  [key: string]: unknown;
42
45
  [key: symbol]: unknown;
43
46
  }
44
47
 
48
+ const ASR_EVENT_CODE = 10000;
49
+
45
50
  export class AITask {
46
51
  private emitter = mitt<AITaskEvent>();
47
-
48
52
  private trtc: any;
49
53
  private service: IRoomService;
50
- public subtitleMsg: SubtitleMessage[] = [];
51
- public subtitleText: { value: string } = { value: '' };
52
- public transcriptionText: { value: string } = { value: '' };
54
+ public subtitleMessages: { [key: string]: SubtitleMessage } = {};
55
+ public transcribedMessageList: SubtitleMessage[] = [];
56
+ private subtitleTimeout: { [key: string]: ReturnType<typeof setTimeout> } =
57
+ {};
53
58
 
54
59
  constructor(service: IRoomService) {
55
60
  this.service = service;
@@ -95,14 +100,13 @@ export class AITask {
95
100
  ) {
96
101
  return;
97
102
  }
98
- const trtc = this.service.roomEngine.instance?.getTRTCCloud()._trtc;
99
- this.trtc = trtc;
100
- trtc.on('custom-message', this.handleAIMessage);
103
+ this.trtc = this.service.roomEngine.instance?.getTRTCCloud()._trtc;
104
+ this.trtc.on('custom-message', this.handleAIMessage);
101
105
  }
106
+
102
107
  private handleUnmount() {
103
- this.subtitleMsg = [];
104
- this.subtitleText.value = '';
105
- this.transcriptionText.value = '';
108
+ this.subtitleMessages = {};
109
+ this.transcribedMessageList = [];
106
110
  this.trtc?.off('custom-message', this.handleAIMessage);
107
111
  }
108
112
 
@@ -111,122 +115,84 @@ export class AITask {
111
115
  this.service.lifeCycleManager.on('unmount', this.handleUnmount);
112
116
  }
113
117
 
114
- // todo trtc defines this type as any
118
+ private resetSubtitleTimeout(id: string, fn: () => void) {
119
+ if (this.subtitleTimeout[id]) {
120
+ clearTimeout(this.subtitleTimeout[id]);
121
+ }
122
+ this.subtitleTimeout[id] = setTimeout(fn, 3000);
123
+ }
124
+
115
125
  private handleAIMessage(event: any) {
116
126
  if (event.cmdId !== 1) return;
117
127
  const data = new TextDecoder().decode(event.data);
118
128
  const jsonData = JSON.parse(data);
119
129
  this.handleMessage(jsonData);
120
130
  this.emit(AI_TASK.TRANSCRIPTION_TASK, {
121
- subtitleText: this.subtitleText,
122
- transcriptionText: this.transcriptionText,
123
- subtitleMsg: this.subtitleMsg,
131
+ subtitleMessages: this.subtitleMessages,
132
+ transcribedMessageList: this.transcribedMessageList,
124
133
  });
125
134
  }
126
135
 
127
136
  private handleMessage(data: MessageData): void {
128
- const refreshSubtitle = (): void => {
129
- let displayText = '';
130
- for (let i = 0; i < this.subtitleMsg.length; i++) {
131
- displayText += `${this.service.roomStore.getDisplayName(this.subtitleMsg[i].userid)}: ${this.subtitleMsg[i].text}\n`;
132
- if (this.subtitleMsg[i].translation_text !== '') {
133
- displayText += `${this.service.roomStore.getDisplayName(this.subtitleMsg[i].userid)}: ${this.subtitleMsg[i].translation_text}\n`;
134
- }
135
- }
136
- this.subtitleText.value = displayText;
137
+ if (data.type !== ASR_EVENT_CODE) return;
138
+ const { sender, payload } = data;
139
+ const { end } = payload;
140
+
141
+ const createSubtitleMsg = () => {
142
+ return {
143
+ sender,
144
+ text: payload.text,
145
+ translationText: payload.translation_text,
146
+ startMsTs: data.start_ms_ts,
147
+ end,
148
+ };
137
149
  };
138
150
 
139
- if (data.type === 10000 && data.payload.end === false) {
140
- // Real-time subtitles
141
- let exist = false;
142
- for (let i = 0; i < this.subtitleMsg.length; i++) {
143
- if (data.sender === this.subtitleMsg[i].userid) {
144
- this.subtitleMsg[i].text = data.payload.text;
145
- this.subtitleMsg[i].translation_text = data.payload.translation_text;
146
- exist = true;
147
- break;
148
- }
149
- }
150
- if (!exist) {
151
- this.subtitleMsg.push({
152
- userid: data.sender,
153
- text: data.payload.text,
154
- translation_text: data.payload.translation_text,
155
- });
156
- }
151
+ const updateMsg = (msg: SubtitleMessage) => {
152
+ msg.text = payload.text;
153
+ msg.translationText = payload.translation_text;
154
+ msg.end = end;
155
+ };
157
156
 
158
- refreshSubtitle();
159
- } else if (data.type === 10000 && data.payload.end === true) {
160
- // One sentence recognition completed
161
- let index = 0;
162
- for (let i = 0; i < this.subtitleMsg.length; i++) {
163
- if (data.sender === this.subtitleMsg[i].userid) {
164
- this.subtitleMsg[i].text = data.payload.text;
165
- this.subtitleMsg[i].translation_text = data.payload.translation_text;
166
- index = i;
167
- break;
168
- }
157
+ const appendMsg = <T extends SubtitleMessage>(
158
+ msg: T,
159
+ target: T[] | Record<string, T>
160
+ ) => {
161
+ if (Array.isArray(target)) {
162
+ target.push(msg);
163
+ } else if (typeof target === 'object') {
164
+ const recordTarget = target as Record<string, T>;
165
+ recordTarget[msg.sender] = msg;
166
+ } else {
167
+ throw new Error('Invalid target type');
169
168
  }
170
- refreshSubtitle();
171
- // todo start_ms_ts end_ms_ts
169
+ };
172
170
 
173
- let content = `${data.payload.start_time}->${data.payload.end_time} ${data.sender}: ${data.payload.text}\n`;
174
- if (data.payload.translation_text !== '') {
175
- content += `${data.payload.start_time}->${data.payload.end_time} ${data.sender}: ${data.payload.translation_text}\n`;
176
- }
177
- this.transcriptionText.value += content;
171
+ const existingSubtitle = this.subtitleMessages[sender];
172
+ if (existingSubtitle) {
173
+ updateMsg(existingSubtitle);
174
+ } else {
175
+ appendMsg(createSubtitleMsg(), this.subtitleMessages);
178
176
  }
179
177
 
180
- // subtitle and transcription is deprecated
181
- if (data.type === 'subtitle') {
182
- let exist = false;
183
- for (let i = 0; i < this.subtitleMsg.length; i++) {
184
- if (data.userid === this.subtitleMsg[i].userid) {
185
- this.subtitleMsg[i].text = data.text;
186
- this.subtitleMsg[i].translation_text = data.translation_text;
187
- exist = true;
188
- break;
189
- }
190
- }
191
- if (!exist) {
192
- this.subtitleMsg.push({
193
- userid: data.userid,
194
- text: data.text,
195
- translation_text: data.translation_text,
196
- });
197
- }
198
-
199
- refreshSubtitle();
200
- } else if (data.type === 'transcription') {
201
- let index = 0;
202
- for (let i = 0; i < this.subtitleMsg.length; i++) {
203
- if (data.userid === this.subtitleMsg[i].userid) {
204
- this.subtitleMsg[i].text = data.text;
205
- this.subtitleMsg[i].translation_text = data.translation_text;
206
- index = i;
207
- break;
208
- }
209
- }
210
- refreshSubtitle();
178
+ const transcriptionIndex = findLastIndex(
179
+ this.transcribedMessageList,
180
+ msg => msg.sender === sender && !msg.end
181
+ );
211
182
 
212
- let content = `${formatTimestampToTime(data.start_ms_ts)}->${formatTimestampToTime(data.end_ms_ts)} ${this.service.roomStore.getDisplayName(data.userid)}: ${data.text}\n`;
213
- if (data.translation_text !== '') {
214
- content += `${formatTimestampToTime(data.start_ms_ts)}->${formatTimestampToTime(data.end_ms_ts)} ${this.service.roomStore.getDisplayName(data.userid)}: ${data.translation_text}\n`;
215
- }
216
- this.transcriptionText.value += content;
183
+ if (transcriptionIndex !== -1) {
184
+ updateMsg(this.transcribedMessageList[transcriptionIndex]);
185
+ } else {
186
+ appendMsg(createSubtitleMsg(), this.transcribedMessageList);
217
187
  }
218
- }
219
188
 
220
- public StartAITranscription(): void {
221
- // Implementation for starting AI transcription
189
+ this.resetSubtitleTimeout(sender, () => {
190
+ if (!end) return;
191
+ delete this.subtitleMessages[sender];
192
+ this.emit(AI_TASK.TRANSCRIPTION_TASK, {
193
+ subtitleMessages: this.subtitleMessages,
194
+ transcribedMessageList: this.transcribedMessageList,
195
+ });
196
+ });
222
197
  }
223
198
  }
224
-
225
- // utils
226
- function formatTimestampToTime(timestamp: number): string {
227
- const date = new Date(timestamp);
228
- const hours = date.getHours().toString().padStart(2, '0');
229
- const minutes = date.getMinutes().toString().padStart(2, '0');
230
- const seconds = date.getSeconds().toString().padStart(2, '0');
231
- return `${hours}:${minutes}:${seconds}`;
232
- }
@@ -16,6 +16,9 @@ export enum MetricsKey {
16
16
  enableWatermark = 106054,
17
17
  enableVirtualBackground = 106055,
18
18
  hideFeatureButton = 106056,
19
+ openChat = 106057,
20
+ setBasicBeauty = 106058,
21
+ aiTask = 106059,
19
22
  }
20
23
 
21
24
  type Task = () => void;
@@ -530,9 +530,6 @@ export const useRoomStore = defineStore('room', {
530
530
  updateVideoQuality(quality: TUIVideoQuality) {
531
531
  this.localVideoQuality = quality;
532
532
  },
533
- setLocalUser(obj: Record<string, any>) {
534
- Object.assign(this.localUser, obj);
535
- },
536
533
  setDeviceList(
537
534
  type: TUIMediaDeviceType,
538
535
  deviceList: { deviceId: string; deviceName: string }[]