@tencentcloud/roomkit-electron-vue3 2.7.0 → 2.7.1
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/es/components/AITools/AISubtitles.vue.mjs +1 -1
- package/es/components/AITools/AISubtitles.vue2.mjs +18 -19
- package/es/components/AITools/AITranscription.vue.mjs +1 -1
- package/es/components/AITools/AITranscription.vue2.mjs +65 -76
- package/es/components/ManageMember/MemberControl/index.vue.mjs +1 -1
- package/es/components/ManageMember/MemberControl/index.vue2.mjs +6 -4
- package/es/components/ManageMember/MemberItemCommon/MemberInfo.vue.mjs +1 -1
- package/es/components/RoomFooter/BasicBeauty.vue.mjs +1 -1
- package/es/components/RoomFooter/BasicBeauty.vue2.mjs +3 -1
- package/es/components/RoomFooter/ChatControl.vue.mjs +2 -0
- package/es/components/ScheduleConference/ScheduleConferencePanel/index.vue2.mjs +1 -1
- package/es/components/ScheduleConference/ScheduleRoomControl.vue2.mjs +1 -1
- package/es/components/ScheduleConference/ScheduleRoomList.vue2.mjs +1 -1
- package/es/index.mjs +118 -110
- package/es/services/function/aiTask.d.ts +13 -17
- package/es/services/function/aiTask.mjs +63 -100
- package/es/services/manager/dataReportManager.d.ts +4 -1
- package/es/services/manager/dataReportManager.mjs +3 -0
- package/es/utils/utils.d.ts +2 -0
- package/es/utils/utils.mjs +33 -0
- package/lib/components/AITools/AISubtitles.vue.js +1 -1
- package/lib/components/AITools/AISubtitles.vue2.js +17 -18
- package/lib/components/AITools/AITranscription.vue.js +1 -1
- package/lib/components/AITools/AITranscription.vue2.js +64 -75
- package/lib/components/ManageMember/MemberControl/index.vue.js +1 -1
- package/lib/components/ManageMember/MemberControl/index.vue2.js +5 -3
- package/lib/components/ManageMember/MemberItemCommon/MemberInfo.vue.js +1 -1
- package/lib/components/RoomFooter/BasicBeauty.vue.js +1 -1
- package/lib/components/RoomFooter/BasicBeauty.vue2.js +3 -1
- package/lib/components/RoomFooter/ChatControl.vue.js +2 -0
- package/lib/components/ScheduleConference/ScheduleConferencePanel/index.vue2.js +1 -1
- package/lib/components/ScheduleConference/ScheduleRoomControl.vue2.js +1 -1
- package/lib/components/ScheduleConference/ScheduleRoomList.vue2.js +1 -1
- package/lib/index.js +118 -110
- package/lib/services/function/aiTask.d.ts +13 -17
- package/lib/services/function/aiTask.js +63 -100
- package/lib/services/manager/dataReportManager.d.ts +4 -1
- package/lib/services/manager/dataReportManager.js +3 -0
- package/lib/utils/utils.d.ts +2 -0
- package/lib/utils/utils.js +33 -0
- package/package.json +2 -2
- package/src/TUIRoom/components/AITools/AISubtitles.vue +26 -23
- package/src/TUIRoom/components/AITools/AITranscription.vue +106 -101
- package/src/TUIRoom/components/ManageMember/MemberControl/index.vue +8 -3
- package/src/TUIRoom/components/ManageMember/MemberItemCommon/MemberInfo.vue +1 -1
- package/src/TUIRoom/components/RoomFooter/BasicBeauty.vue +2 -1
- package/src/TUIRoom/components/RoomFooter/ChatControl.vue +2 -1
- package/src/TUIRoom/services/function/aiTask.ts +79 -113
- package/src/TUIRoom/services/manager/dataReportManager.ts +3 -0
- package/src/TUIRoom/utils/utils.ts +47 -0
|
@@ -5,18 +5,20 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
5
5
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
6
6
|
const mitt = require("mitt");
|
|
7
7
|
const environment = require("../../utils/environment.js");
|
|
8
|
+
const utils = require("../../utils/utils.js");
|
|
8
9
|
var AI_TASK = /* @__PURE__ */ ((AI_TASK2) => {
|
|
9
10
|
AI_TASK2["TRANSCRIPTION_TASK"] = "transcription";
|
|
10
11
|
return AI_TASK2;
|
|
11
12
|
})(AI_TASK || {});
|
|
13
|
+
const ASR_EVENT_CODE = 1e4;
|
|
12
14
|
class AITask {
|
|
13
15
|
constructor(service) {
|
|
14
16
|
__publicField(this, "emitter", mitt());
|
|
15
17
|
__publicField(this, "trtc");
|
|
16
18
|
__publicField(this, "service");
|
|
17
|
-
__publicField(this, "
|
|
18
|
-
__publicField(this, "
|
|
19
|
-
__publicField(this, "
|
|
19
|
+
__publicField(this, "subtitleMessages", {});
|
|
20
|
+
__publicField(this, "transcribedMessageList", []);
|
|
21
|
+
__publicField(this, "subtitleTimeout", {});
|
|
20
22
|
this.service = service;
|
|
21
23
|
if (environment.isElectron || environment.isMobile) return;
|
|
22
24
|
this.bindCtx();
|
|
@@ -45,126 +47,87 @@ class AITask {
|
|
|
45
47
|
if (typeof ((_a = this.service.roomEngine.instance) == null ? void 0 : _a.getTRTCCloud) === "undefined" || typeof ((_c = (_b = this.service.roomEngine.instance) == null ? void 0 : _b.getTRTCCloud()) == null ? void 0 : _c._trtc) === "undefined") {
|
|
46
48
|
return;
|
|
47
49
|
}
|
|
48
|
-
|
|
49
|
-
this.trtc
|
|
50
|
-
trtc.on("custom-message", this.handleAIMessage);
|
|
50
|
+
this.trtc = (_d = this.service.roomEngine.instance) == null ? void 0 : _d.getTRTCCloud()._trtc;
|
|
51
|
+
this.trtc.on("custom-message", this.handleAIMessage);
|
|
51
52
|
}
|
|
52
53
|
handleUnmount() {
|
|
53
54
|
var _a;
|
|
54
|
-
this.
|
|
55
|
-
this.
|
|
56
|
-
this.transcriptionText.value = "";
|
|
55
|
+
this.subtitleMessages = {};
|
|
56
|
+
this.transcribedMessageList = [];
|
|
57
57
|
(_a = this.trtc) == null ? void 0 : _a.off("custom-message", this.handleAIMessage);
|
|
58
58
|
}
|
|
59
59
|
bindEvent() {
|
|
60
60
|
this.service.lifeCycleManager.on("mount", this.handleMount);
|
|
61
61
|
this.service.lifeCycleManager.on("unmount", this.handleUnmount);
|
|
62
62
|
}
|
|
63
|
-
|
|
63
|
+
resetSubtitleTimeout(id, fn) {
|
|
64
|
+
if (this.subtitleTimeout[id]) {
|
|
65
|
+
clearTimeout(this.subtitleTimeout[id]);
|
|
66
|
+
}
|
|
67
|
+
this.subtitleTimeout[id] = setTimeout(fn, 3e3);
|
|
68
|
+
}
|
|
64
69
|
handleAIMessage(event) {
|
|
65
70
|
if (event.cmdId !== 1) return;
|
|
66
71
|
const data = new TextDecoder().decode(event.data);
|
|
67
72
|
const jsonData = JSON.parse(data);
|
|
68
73
|
this.handleMessage(jsonData);
|
|
69
74
|
this.emit("transcription", {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
subtitleMsg: this.subtitleMsg
|
|
75
|
+
subtitleMessages: this.subtitleMessages,
|
|
76
|
+
transcribedMessageList: this.transcribedMessageList
|
|
73
77
|
});
|
|
74
78
|
}
|
|
75
79
|
handleMessage(data) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
if (data.type !== ASR_EVENT_CODE) return;
|
|
81
|
+
const { sender, payload } = data;
|
|
82
|
+
const { end } = payload;
|
|
83
|
+
const createSubtitleMsg = () => {
|
|
84
|
+
return {
|
|
85
|
+
sender,
|
|
86
|
+
text: payload.text,
|
|
87
|
+
translationText: payload.translation_text,
|
|
88
|
+
startMsTs: data.start_ms_ts,
|
|
89
|
+
end
|
|
90
|
+
};
|
|
87
91
|
};
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
text: data.payload.text,
|
|
102
|
-
translation_text: data.payload.translation_text
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
refreshSubtitle();
|
|
106
|
-
} else if (data.type === 1e4 && data.payload.end === true) {
|
|
107
|
-
for (let i = 0; i < this.subtitleMsg.length; i++) {
|
|
108
|
-
if (data.sender === this.subtitleMsg[i].userid) {
|
|
109
|
-
this.subtitleMsg[i].text = data.payload.text;
|
|
110
|
-
this.subtitleMsg[i].translation_text = data.payload.translation_text;
|
|
111
|
-
break;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
refreshSubtitle();
|
|
115
|
-
let content = `${data.payload.start_time}->${data.payload.end_time} ${data.sender}: ${data.payload.text}
|
|
116
|
-
`;
|
|
117
|
-
if (data.payload.translation_text !== "") {
|
|
118
|
-
content += `${data.payload.start_time}->${data.payload.end_time} ${data.sender}: ${data.payload.translation_text}
|
|
119
|
-
`;
|
|
92
|
+
const updateMsg = (msg) => {
|
|
93
|
+
msg.text = payload.text;
|
|
94
|
+
msg.translationText = payload.translation_text;
|
|
95
|
+
msg.end = end;
|
|
96
|
+
};
|
|
97
|
+
const appendMsg = (msg, target) => {
|
|
98
|
+
if (Array.isArray(target)) {
|
|
99
|
+
target.push(msg);
|
|
100
|
+
} else if (typeof target === "object") {
|
|
101
|
+
const recordTarget = target;
|
|
102
|
+
recordTarget[msg.sender] = msg;
|
|
103
|
+
} else {
|
|
104
|
+
throw new Error("Invalid target type");
|
|
120
105
|
}
|
|
121
|
-
|
|
106
|
+
};
|
|
107
|
+
const existingSubtitle = this.subtitleMessages[sender];
|
|
108
|
+
if (existingSubtitle) {
|
|
109
|
+
updateMsg(existingSubtitle);
|
|
110
|
+
} else {
|
|
111
|
+
appendMsg(createSubtitleMsg(), this.subtitleMessages);
|
|
122
112
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
if (!exist) {
|
|
134
|
-
this.subtitleMsg.push({
|
|
135
|
-
userid: data.userid,
|
|
136
|
-
text: data.text,
|
|
137
|
-
translation_text: data.translation_text
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
refreshSubtitle();
|
|
141
|
-
} else if (data.type === "transcription") {
|
|
142
|
-
for (let i = 0; i < this.subtitleMsg.length; i++) {
|
|
143
|
-
if (data.userid === this.subtitleMsg[i].userid) {
|
|
144
|
-
this.subtitleMsg[i].text = data.text;
|
|
145
|
-
this.subtitleMsg[i].translation_text = data.translation_text;
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
refreshSubtitle();
|
|
150
|
-
let content = `${formatTimestampToTime(data.start_ms_ts)}->${formatTimestampToTime(data.end_ms_ts)} ${this.service.roomStore.getDisplayName(data.userid)}: ${data.text}
|
|
151
|
-
`;
|
|
152
|
-
if (data.translation_text !== "") {
|
|
153
|
-
content += `${formatTimestampToTime(data.start_ms_ts)}->${formatTimestampToTime(data.end_ms_ts)} ${this.service.roomStore.getDisplayName(data.userid)}: ${data.translation_text}
|
|
154
|
-
`;
|
|
155
|
-
}
|
|
156
|
-
this.transcriptionText.value += content;
|
|
113
|
+
const transcriptionIndex = utils.findLastIndex(
|
|
114
|
+
this.transcribedMessageList,
|
|
115
|
+
(msg) => msg.sender === sender && !msg.end
|
|
116
|
+
);
|
|
117
|
+
if (transcriptionIndex !== -1) {
|
|
118
|
+
updateMsg(this.transcribedMessageList[transcriptionIndex]);
|
|
119
|
+
} else {
|
|
120
|
+
appendMsg(createSubtitleMsg(), this.transcribedMessageList);
|
|
157
121
|
}
|
|
122
|
+
this.resetSubtitleTimeout(sender, () => {
|
|
123
|
+
if (!end) return;
|
|
124
|
+
delete this.subtitleMessages[sender];
|
|
125
|
+
this.emit("transcription", {
|
|
126
|
+
subtitleMessages: this.subtitleMessages,
|
|
127
|
+
transcribedMessageList: this.transcribedMessageList
|
|
128
|
+
});
|
|
129
|
+
});
|
|
158
130
|
}
|
|
159
|
-
StartAITranscription() {
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
function formatTimestampToTime(timestamp) {
|
|
163
|
-
const date = new Date(timestamp);
|
|
164
|
-
const hours = date.getHours().toString().padStart(2, "0");
|
|
165
|
-
const minutes = date.getMinutes().toString().padStart(2, "0");
|
|
166
|
-
const seconds = date.getSeconds().toString().padStart(2, "0");
|
|
167
|
-
return `${hours}:${minutes}:${seconds}`;
|
|
168
131
|
}
|
|
169
132
|
exports.AITask = AITask;
|
|
170
133
|
exports.AI_TASK = AI_TASK;
|
|
@@ -10,7 +10,10 @@ export declare enum MetricsKey {
|
|
|
10
10
|
disableScreenSharing = 106053,
|
|
11
11
|
enableWatermark = 106054,
|
|
12
12
|
enableVirtualBackground = 106055,
|
|
13
|
-
hideFeatureButton = 106056
|
|
13
|
+
hideFeatureButton = 106056,
|
|
14
|
+
openChat = 106057,
|
|
15
|
+
setBasicBeauty = 106058,
|
|
16
|
+
aiTask = 106059
|
|
14
17
|
}
|
|
15
18
|
export declare class DataReportManager {
|
|
16
19
|
private taskQueue;
|
|
@@ -18,6 +18,9 @@ var MetricsKey = /* @__PURE__ */ ((MetricsKey2) => {
|
|
|
18
18
|
MetricsKey2[MetricsKey2["enableWatermark"] = 106054] = "enableWatermark";
|
|
19
19
|
MetricsKey2[MetricsKey2["enableVirtualBackground"] = 106055] = "enableVirtualBackground";
|
|
20
20
|
MetricsKey2[MetricsKey2["hideFeatureButton"] = 106056] = "hideFeatureButton";
|
|
21
|
+
MetricsKey2[MetricsKey2["openChat"] = 106057] = "openChat";
|
|
22
|
+
MetricsKey2[MetricsKey2["setBasicBeauty"] = 106058] = "setBasicBeauty";
|
|
23
|
+
MetricsKey2[MetricsKey2["aiTask"] = 106059] = "aiTask";
|
|
21
24
|
return MetricsKey2;
|
|
22
25
|
})(MetricsKey || {});
|
|
23
26
|
class DataReportManager {
|
package/lib/utils/utils.d.ts
CHANGED
|
@@ -60,3 +60,5 @@ export declare function convertSecondsToHMS(seconds: number): {
|
|
|
60
60
|
seconds: number;
|
|
61
61
|
};
|
|
62
62
|
export declare function getNanoId(size?: number): string;
|
|
63
|
+
export declare function findLastIndex<T>(array: T[], predicate: (value: T, index: number, obj: T[]) => boolean, thisArg?: any): number;
|
|
64
|
+
export declare function formatTimestampToTime(timestamp: number, format?: string): string;
|
package/lib/utils/utils.js
CHANGED
|
@@ -147,12 +147,45 @@ function getNanoId(size = 21) {
|
|
|
147
147
|
}
|
|
148
148
|
return id;
|
|
149
149
|
}
|
|
150
|
+
function findLastIndex(array, predicate, thisArg) {
|
|
151
|
+
const len = array.length >>> 0;
|
|
152
|
+
let k = len - 1;
|
|
153
|
+
while (k >= 0) {
|
|
154
|
+
const kValue = array[k];
|
|
155
|
+
if (predicate.call(thisArg, kValue, k, array)) {
|
|
156
|
+
return k;
|
|
157
|
+
}
|
|
158
|
+
k = k - 1;
|
|
159
|
+
}
|
|
160
|
+
return -1;
|
|
161
|
+
}
|
|
162
|
+
function formatTimestampToTime(timestamp, format = "MM-DD HH:mm") {
|
|
163
|
+
const date = new Date(timestamp);
|
|
164
|
+
const padStart = (value, length = 2) => value.toString().padStart(length, "0");
|
|
165
|
+
const replacements = {
|
|
166
|
+
YYYY: date.getFullYear().toString(),
|
|
167
|
+
YY: (date.getFullYear() % 100).toString().padStart(2, "0"),
|
|
168
|
+
MM: padStart(date.getMonth() + 1),
|
|
169
|
+
DD: padStart(date.getDate()),
|
|
170
|
+
HH: padStart(date.getHours()),
|
|
171
|
+
hh: padStart(date.getHours() % 12),
|
|
172
|
+
mm: padStart(date.getMinutes()),
|
|
173
|
+
ss: padStart(date.getSeconds()),
|
|
174
|
+
A: date.getHours() >= 12 ? "PM" : "AM"
|
|
175
|
+
};
|
|
176
|
+
return format.replace(
|
|
177
|
+
/YYYY|YY|MM|DD|HH|hh|mm|ss|A/g,
|
|
178
|
+
(match) => replacements[match]
|
|
179
|
+
);
|
|
180
|
+
}
|
|
150
181
|
exports.addSuffix = addSuffix;
|
|
151
182
|
exports.calculateByteLength = calculateByteLength;
|
|
152
183
|
exports.convertSecondsToHMS = convertSecondsToHMS;
|
|
153
184
|
exports.debounce = debounce;
|
|
154
185
|
exports.deepClone = deepClone;
|
|
155
186
|
exports.exitFullScreen = exitFullScreen;
|
|
187
|
+
exports.findLastIndex = findLastIndex;
|
|
188
|
+
exports.formatTimestampToTime = formatTimestampToTime;
|
|
156
189
|
exports.getNanoId = getNanoId;
|
|
157
190
|
exports.getUrlParam = getUrlParam;
|
|
158
191
|
exports.getUrlWithRoomId = getUrlWithRoomId;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tencentcloud/roomkit-electron-vue3",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.1",
|
|
4
4
|
"main": "./lib/index.js",
|
|
5
5
|
"module": "./es/index.mjs",
|
|
6
6
|
"types": "./es/index.d.ts",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@tencentcloud/chat": "latest",
|
|
22
22
|
"@tencentcloud/tui-core": "latest",
|
|
23
|
-
"@tencentcloud/tuiroom-engine-electron": "^2.7.
|
|
23
|
+
"@tencentcloud/tuiroom-engine-electron": "^2.7.1",
|
|
24
24
|
"@tencentcloud/chat-uikit-engine": "2.2.7",
|
|
25
25
|
"@tencentcloud/chat-uikit-vue": "2.2.7",
|
|
26
26
|
"@tencentcloud/universal-api": "^2.0.9",
|
|
@@ -1,37 +1,40 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div v-if="subtitleLines.length" class="ai-subtitles">
|
|
3
|
-
<div v-for="
|
|
3
|
+
<div v-for="line in subtitleLines" :key="line.sender">
|
|
4
|
+
{{
|
|
5
|
+
`${roomService.roomStore.getDisplayName(line.sender)}: ${line?.text}`
|
|
6
|
+
}}
|
|
7
|
+
</div>
|
|
4
8
|
</div>
|
|
5
9
|
</template>
|
|
6
10
|
|
|
7
11
|
<script setup lang="ts">
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
rawSubtitleText.value = '';
|
|
25
|
-
}, 3000);
|
|
26
|
-
};
|
|
12
|
+
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
|
13
|
+
import {
|
|
14
|
+
AI_TASK,
|
|
15
|
+
AITaskEvent,
|
|
16
|
+
MetricsKey,
|
|
17
|
+
roomService,
|
|
18
|
+
SubtitleMessage,
|
|
19
|
+
} from '../../services';
|
|
20
|
+
|
|
21
|
+
const subtitleMessages = ref<Record<string, SubtitleMessage>>({});
|
|
22
|
+
const subtitleLines = computed(() => {
|
|
23
|
+
const arr = Object.keys(subtitleMessages.value).map(userId => {
|
|
24
|
+
return subtitleMessages.value[userId];
|
|
25
|
+
});
|
|
26
|
+
return arr.sort((a, b) => a.startMsTs - b.startMsTs);
|
|
27
|
+
});
|
|
27
28
|
|
|
28
29
|
const handleAISubtitles = (data?: AITaskEvent[AI_TASK.TRANSCRIPTION_TASK]) => {
|
|
29
30
|
if (!data) return;
|
|
30
|
-
|
|
31
|
-
resetSubtitleTimeout();
|
|
31
|
+
subtitleMessages.value = Object.assign({}, data.subtitleMessages);
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
onMounted(() => {
|
|
35
|
+
roomService.dataReportManager.reportCount(MetricsKey.AITask);
|
|
36
|
+
roomService.aiTask.on(AI_TASK.TRANSCRIPTION_TASK, handleAISubtitles);
|
|
37
|
+
});
|
|
35
38
|
|
|
36
39
|
onUnmounted(() => {
|
|
37
40
|
roomService.aiTask.off(AI_TASK.TRANSCRIPTION_TASK, handleAISubtitles);
|
|
@@ -1,141 +1,138 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
|
+
class="conversation"
|
|
4
|
+
ref="conversationContainerRef"
|
|
5
|
+
@scroll="handleScroll"
|
|
6
|
+
>
|
|
3
7
|
<div
|
|
4
|
-
v-for="
|
|
5
|
-
:key="
|
|
6
|
-
class="conversation-
|
|
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">{{
|
|
10
|
-
|
|
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
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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,
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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 (
|
|
53
|
+
if (conversationContainerRef.value && !isUserScrolling.value) {
|
|
39
54
|
await nextTick();
|
|
40
|
-
|
|
55
|
+
conversationContainerRef.value.scrollTop =
|
|
56
|
+
conversationContainerRef.value.scrollHeight;
|
|
41
57
|
}
|
|
42
58
|
};
|
|
43
59
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
56
|
-
|
|
89
|
+
!currentAggregatedMessage ||
|
|
90
|
+
message.sender !== currentAggregatedMessage.sender ||
|
|
91
|
+
message.startMsTs - currentAggregatedMessage.startMsTs > timeInterval
|
|
57
92
|
) {
|
|
58
|
-
|
|
59
|
-
|
|
93
|
+
currentAggregatedMessage = {
|
|
94
|
+
messages: [message],
|
|
95
|
+
sender: message.sender,
|
|
96
|
+
startMsTs: message.startMsTs,
|
|
97
|
+
};
|
|
98
|
+
aggregatedMessageList.push(currentAggregatedMessage);
|
|
60
99
|
} else {
|
|
61
|
-
|
|
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
|
|
104
|
+
return aggregatedMessageList;
|
|
86
105
|
});
|
|
87
106
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
processedConversations.value = processConversation(
|
|
91
|
-
data.transcriptionText.value
|
|
92
|
-
);
|
|
107
|
+
watch(rawTranscribedMessageList, () => {
|
|
108
|
+
scrollToBottom();
|
|
93
109
|
});
|
|
94
110
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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-
|
|
138
|
-
margin-bottom:
|
|
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(() =>
|
|
108
|
+
const singleControl = computed(() => {
|
|
109
|
+
return isCanOperateMySelf.value ? null : controlList.value[0];
|
|
110
|
+
});
|
|
111
|
+
|
|
109
112
|
const moreControlList = computed(() => {
|
|
110
|
-
return isCanOperateMySelf
|
|
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();
|