@tencentcloud/ai-desk-customer-vue 1.5.11 → 1.6.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 (52) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/assets/arrow_down_icon.svg +1 -0
  3. package/assets/arrow_down_icon_white.svg +1 -0
  4. package/assets/audio-blue.svg +4 -0
  5. package/assets/audio_icon_1.svg +3 -0
  6. package/assets/audio_icon_2.svg +3 -0
  7. package/assets/audio_icon_3.svg +3 -0
  8. package/assets/keyboard-icon.svg +9 -0
  9. package/components/CustomerServiceChat/chat-header/index-web.vue +1 -5
  10. package/components/CustomerServiceChat/emoji-config/default-emoji.ts +11 -0
  11. package/components/CustomerServiceChat/emoji-config/index.ts +14 -1
  12. package/components/CustomerServiceChat/index-web.vue +10 -2
  13. package/components/CustomerServiceChat/message-input/index-web.vue +95 -10
  14. package/components/CustomerServiceChat/message-input/message-input-button.vue +5 -0
  15. package/components/CustomerServiceChat/message-input/message-input-editor-web.vue +346 -11
  16. package/components/CustomerServiceChat/message-input-toolbar/emoji-dialog-mobile/emoji-dialog-mobile.vue +26 -46
  17. package/components/CustomerServiceChat/message-input-toolbar/emoji-picker/emoji-picker-dialog.vue +8 -38
  18. package/components/CustomerServiceChat/message-list/bottom-quick-order/index.vue +1 -0
  19. package/components/CustomerServiceChat/message-list/index-web.vue +13 -0
  20. package/components/CustomerServiceChat/message-list/message-elements/message-audio-web.vue +50 -78
  21. package/components/CustomerServiceChat/message-list/message-elements/message-bubble-web.vue +1 -1
  22. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/marked.ts +5 -3
  23. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-robot-welcome.vue +13 -6
  24. package/components/CustomerServiceChat/message-list/message-elements/message-text.vue +3 -2
  25. package/components/CustomerServiceChat/message-list/scroll-button/index.vue +17 -9
  26. package/components/common/Toast/index-web.ts +16 -3
  27. package/components/common/Toast/index-web.vue +16 -6
  28. package/constant.ts +1 -0
  29. package/locales/en/TUIChat.ts +7 -3
  30. package/locales/en/aidesk.ts +0 -1
  31. package/locales/fil/TUIChat.ts +7 -3
  32. package/locales/fil/aidesk.ts +0 -1
  33. package/locales/id/TUIChat.ts +76 -72
  34. package/locales/id/aidesk.ts +0 -1
  35. package/locales/ja/TUIChat.ts +76 -72
  36. package/locales/ja/aidesk.ts +0 -1
  37. package/locales/ms/TUIChat.ts +76 -72
  38. package/locales/ms/aidesk.ts +0 -1
  39. package/locales/ru/TUIChat.ts +7 -3
  40. package/locales/ru/aidesk.ts +0 -1
  41. package/locales/th/TUIChat.ts +76 -72
  42. package/locales/th/aidesk.ts +0 -1
  43. package/locales/vi/TUIChat.ts +76 -72
  44. package/locales/vi/aidesk.ts +0 -1
  45. package/locales/zh_cn/TUIChat.ts +6 -2
  46. package/locales/zh_cn/aidesk.ts +0 -1
  47. package/locales/zh_tw/TUIChat.ts +6 -2
  48. package/locales/zh_tw/aidesk.ts +0 -1
  49. package/package.json +2 -1
  50. package/server.ts +22 -3
  51. package/assets/double-arrow.svg +0 -1
  52. package/assets/keyboard_icon.png +0 -0
@@ -548,8 +548,21 @@ const onCurrentConversationIDUpdated = (conversationID: string) => {
548
548
  // Synchronize storage about whether the audio has been played when converstaion switched
549
549
  chatStorage.setChatStorage('audioPlayedMapping', audioPlayedMapping.value);
550
550
  }
551
+ preloadEmoji();
551
552
  };
552
553
 
554
+ const preloadEmoji = () => {
555
+ setTimeout(() => {
556
+ let url;
557
+ let img;
558
+ for (let i = 0; i <= 61; i++) {
559
+ url = `https://web.sdk.qcloud.com/im/assets/emoji-plugin/emoji_${i}@2x.png`;
560
+ img = new Image();
561
+ img.src = url;
562
+ }
563
+ }, 0);
564
+ }
565
+
553
566
  const getHistoryMessageList = () => {
554
567
  TUIChatService.getMessageList().then((res: any) => {
555
568
  const { nextReqMessageID: ID } = res.data;
@@ -9,24 +9,22 @@
9
9
  @click.stop="play"
10
10
  >
11
11
  <div class="audio-icon-container">
12
- <!-- <div :class="{ mask: true, play: isAudioPlaying }" /> -->
13
12
  <Icon
14
13
  :class="{icon:true,play: isAudioPlaying}"
15
14
  width="16px"
16
- height="20px"
17
- :file="audioIcon"
18
-
15
+ height="16px"
16
+ :file="currentAudioIcon"
19
17
  />
20
18
  </div>
21
19
  <span
22
20
  class="time"
23
- :style="{ width: `${data.second * 10 + 20}px` }"
21
+ :style="{ width: `${data.second * 2 + 20}px` }"
24
22
  >
25
- {{ data.second || 1 }} "
23
+ {{ data.second || 1 }}"
26
24
  </span>
27
25
  <audio
28
26
  ref="audioRef"
29
- :src="data.url"
27
+ :src="audioUrl"
30
28
  />
31
29
  </div>
32
30
  </template>
@@ -34,9 +32,16 @@
34
32
  <script lang="ts" setup>
35
33
  import vue from '../../../../adapter-vue';
36
34
  import Icon from '../../../common/Icon.vue';
37
- import audioIcon from '../../../../assets/msg-audio.svg';
35
+ import audioIcon1 from '../../../../assets/audio_icon_1.svg';
36
+ import audioIcon2 from '../../../../assets/audio_icon_2.svg';
37
+ import audioIcon3 from '../../../../assets/audio_icon_3.svg';
38
38
  import { isMobile } from '../../../../utils/env';
39
- const { watchEffect, ref, onMounted, onUnmounted } = vue;
39
+ import {
40
+ Toast,
41
+ TOAST_TYPE,
42
+ } from '../../../common/Toast/index-web';
43
+ import { TUITranslateService } from '@tencentcloud/chat-uikit-engine';
44
+ const { watchEffect, ref, onMounted, onUnmounted, computed } = vue;
40
45
 
41
46
  interface IEmits {
42
47
  (e: 'setAudioPlayed', messageID: string): void;
@@ -58,6 +63,11 @@ const data = ref();
58
63
  const message = ref();
59
64
  const isAudioPlaying = ref();
60
65
  const audioRef = ref<HTMLAudioElement>();
66
+ const audioIcons = [audioIcon1, audioIcon2, audioIcon3];
67
+ const currentAudioIconIndex = ref(2);
68
+ const audioUrl = ref('');
69
+ const currentAudioIcon = computed(() => audioIcons[currentAudioIconIndex.value]);
70
+ let audioIconTimer: any = null;
61
71
 
62
72
  onMounted(() => {
63
73
  if (audioRef.value) {
@@ -76,6 +86,7 @@ onUnmounted(() => {
76
86
  watchEffect(() => {
77
87
  message.value = props.messageItem;
78
88
  data.value = props.content;
89
+ audioUrl.value = data.value.url || message.value.payload.remoteAudioUrl;
79
90
  });
80
91
 
81
92
  function play() {
@@ -95,45 +106,57 @@ function play() {
95
106
  audio.currentTime = 0;
96
107
  }
97
108
  });
98
- audioRef.value.play();
109
+ try {
110
+ if (!audioUrl.value) {
111
+ const message = `${TUITranslateService.t("TUIChat.语音播放失败")}`;
112
+ Toast({
113
+ message,
114
+ type: TOAST_TYPE.ERROR,
115
+ duration: 2000,
116
+ });
117
+ return;
118
+ }
119
+ audioRef.value.play();
120
+ if (!audioIconTimer) {
121
+ audioIconTimer = setInterval(() => {
122
+ currentAudioIconIndex.value = (currentAudioIconIndex.value + 1) % audioIcons.length;
123
+ }, 500);
124
+ }
125
+
126
+ } catch (e) {
127
+ console.warn(e);
128
+ }
99
129
  isAudioPlaying.value = true;
100
130
  if (message.value.flow === 'in') {
101
131
  emits('setAudioPlayed', message.value.ID);
102
132
  }
103
133
  }
104
134
 
135
+ function initAudioIcon() {
136
+ clearInterval(audioIconTimer);
137
+ audioIconTimer = null;
138
+ currentAudioIconIndex.value = 2;
139
+ }
140
+
105
141
  function onAudioEnded() {
106
142
  isAudioPlaying.value = false;
143
+ initAudioIcon();
107
144
  }
108
145
 
109
146
  function onAudioPaused() {
110
147
  isAudioPlaying.value = false;
148
+ initAudioIcon();
111
149
  }
112
150
  </script>
113
151
  <style lang="scss" scoped>
114
152
  @import "../../style/common";
115
-
116
- $flow-in-bg-color: #fbfbfb;
117
- $flow-out-bg-color: #dceafd;
118
-
119
- @keyframes blink {
120
- 0% {
121
- opacity: 1;
122
- }
123
- 50% {
124
- opacity: 0;
125
- }
126
- 100% {
127
- opacity: 1;
128
- }
129
- }
130
-
131
153
  .message-audio {
132
154
  flex-direction: row;
133
155
  display: flex;
134
156
  flex: 0 0 auto;
135
157
  cursor: pointer;
136
158
  overflow: hidden;
159
+ align-items: center;
137
160
 
138
161
  .time {
139
162
  flex: 1 1 auto;
@@ -156,47 +179,6 @@ $flow-out-bg-color: #dceafd;
156
179
  position: relative;
157
180
  margin: 0 7px 0 0;
158
181
  overflow: hidden;
159
-
160
- .mask {
161
- position: absolute;
162
- z-index: 2;
163
- width: 100%;
164
- height: 100%;
165
- left: 0;
166
- top: 0;
167
- transform-origin: right;
168
- transform: scaleX(0);
169
- background-color: $flow-in-bg-color;
170
-
171
- &.play {
172
- animation: audio-play 2s steps(1, end) infinite;
173
- }
174
- }
175
- .icon{
176
- &.play{
177
- animation: blink 1s infinite;
178
- }
179
- }
180
- }
181
-
182
- @keyframes audio-play {
183
- 0% {
184
- transform: scaleX(0.7056);
185
- }
186
-
187
- 50% {
188
- transform: scaleX(0.3953);
189
- }
190
-
191
- 75% {
192
- transform: scaleX(0);
193
- visibility: hidden;
194
- }
195
-
196
- 100% {
197
- transform: scaleX(0);
198
- visibility: hidden;
199
- }
200
182
  }
201
183
 
202
184
  .message-audio.reserve {
@@ -206,18 +188,8 @@ $flow-out-bg-color: #dceafd;
206
188
  text-align: end;
207
189
  }
208
190
 
209
- .icon {
210
- transform: rotate(180deg);
211
- }
212
-
213
191
  .audio-icon-container {
214
- margin: 0 0 0 7px;
215
-
216
- .mask {
217
- transform-origin: left;
218
- background-color: $flow-out-bg-color;
219
- // mask: linear-gradient(0deg, transparent 50%);
220
- }
192
+ margin: 0 0 0 4px;
221
193
  }
222
194
  }
223
195
  </style>
@@ -364,7 +364,7 @@ function onDislike(messageInfo: Object) {
364
364
  }
365
365
 
366
366
  .message-bubble {
367
- padding: 10px 5px;
367
+ padding: 10px;
368
368
  display: flex;
369
369
  flex-direction: row;
370
370
  user-select: none;
@@ -2,7 +2,7 @@ import {Marked} from 'marked';
2
2
  import Log from '../../../../../../utils/logger';
3
3
 
4
4
  export const marked = new Marked(
5
- {mangle: false, headerIds: false},
5
+ {mangle: false, headerIds: false, breaks: true, gfm: false, smartLists: true,},
6
6
  {
7
7
  renderer: {
8
8
  image(this: any, href: string | null, title: string | null, text: string) {
@@ -24,9 +24,10 @@ export const marked = new Marked(
24
24
  <video class="message-img video-web" src="${videoSrc}" onloadeddata="onMarkdownMediaLoad()" preload="auto" controls/>
25
25
  </div>
26
26
  </div>`;
27
+ }
27
28
  }
28
- }
29
- return text
29
+ // 在段落中用 p 包裹,避免 \n\n 无法换行
30
+ return `<p>${text}</p>`;
30
31
  },
31
32
  link(this: any, href: string | null, title: string | null, text: string) {
32
33
  if (href) {
@@ -57,6 +58,7 @@ const extractVideoSrc = (text: string) => {
57
58
  }
58
59
 
59
60
  export const parseMarkdown = (text: string) => {
61
+ // 不要手动替换,如把 \n\n 替换成 p 或者 <br/>,否则可能会导致Markdown结构被破坏,进而导致 bad case
60
62
  let ret = marked.parse(text);
61
63
  return typeof ret === 'string' ? ret : '';
62
64
  };
@@ -24,7 +24,9 @@
24
24
  class="welcome-item"
25
25
  @click="handleContentListItemClick(item)"
26
26
  >
27
- <div class="item-content">{{ item.content }}</div>
27
+ <div class="item-container">
28
+ <div class="item-content">{{ item.content }}</div>
29
+ </div>
28
30
  </div>
29
31
  <div class="welcome-item welcome-item-hidden">
30
32
  {{ longestContent }}
@@ -195,13 +197,18 @@ export default {
195
197
  color: #1c66e5;
196
198
  gap: 10px;
197
199
  box-sizing: border-box;
198
- .item-content {
200
+ .item-container {
199
201
  text-align: center;
200
- overflow-wrap: break-word;
201
- word-break: normal;
202
- width: 100%;
203
- padding: 0 10px;
202
+ max-width: 100%;
203
+ .item-content {
204
+ display: inline-block;
205
+ text-align: left;
206
+ overflow-wrap: break-word;
207
+ word-break: normal;
208
+ width: 100%;
209
+ }
204
210
  }
211
+
205
212
  }
206
213
 
207
214
  .welcome-item:hover {
@@ -3,7 +3,7 @@
3
3
  <span
4
4
  v-for="(item, index) in textMessageData.text"
5
5
  :key="index"
6
- >
6
+ >
7
7
  <span v-if="item.name === 'text' && enableURLDetection === 1"
8
8
  class="text"
9
9
  v-html="item.text"
@@ -64,7 +64,8 @@ const textMessageData = computed(() => {
64
64
  + CUSTOM_BASIC_EMOJI_URL_MAPPING[item.emojiKey];
65
65
  }
66
66
  } else if (item.name === 'text' && enableURLDetection.value) {
67
- item.text = item.text.replace(/https?:\/\/[\w\-./?=&:#]+(?=[^\w\-./?=&:#]|$)/g, (url) => {
67
+ // 开启 url 识别时先移除 html 标签,避免 xss 漏洞
68
+ item.text = item.text.replace(/<[^>]+>/g, '').replace(/https?:\/\/[\w\-./?=&:#]+(?=[^\w\-./?=&:#]|$)/g, (url) => {
68
69
  return `<a href="${url}" target="_blank" rel="noopener noreferrer" class="message-text-link" style="color: ${linkColor.value}; text-decoration: underline;">${url}</a>`;
69
70
  }) || '';
70
71
  }
@@ -1,15 +1,15 @@
1
1
  <template>
2
2
  <div
3
3
  v-if="isScrollButtonVisible"
4
- class="scroll-button"
4
+ :class="['scroll-button', isPC ? 'scroll-button-pc' : '']"
5
5
  @click="scrollToMessageListBottom"
6
6
  >
7
7
  <Icon
8
- width="10px"
9
- height="10px"
10
- :file="doubleArrowIcon"
8
+ width="15px"
9
+ height="15px"
10
+ :file="isPC ? arrowDownIconWhite : arrowDownIcon"
11
11
  />
12
- <div class="scroll-button-text">
12
+ <div v-if="scrollButtonContent" :class="isPC ? 'scroll-button-text-pc' : 'scroll-button-text'">
13
13
  {{ scrollButtonContent }}
14
14
  </div>
15
15
  </div>
@@ -26,10 +26,11 @@ import {
26
26
  TUITranslateService,
27
27
  } from '@tencentcloud/chat-uikit-engine';
28
28
  import Icon from '../../../common/Icon.vue';
29
- import doubleArrowIcon from '../../../../assets/double-arrow.svg';
29
+ import arrowDownIcon from '../../../../assets/arrow_down_icon.svg';
30
+ import arrowDownIconWhite from '../../../../assets/arrow_down_icon_white.svg';
30
31
  import { getBoundingClientRect } from '@tencentcloud/universal-api';
31
32
  import { JSONToObject } from '../../../../utils';
32
-
33
+ import { isPC } from '../../../../utils/env';
33
34
 
34
35
  interface IEmits {
35
36
  (key: 'scrollToLatestMessage'): void;
@@ -197,7 +198,6 @@ defineExpose({
197
198
  bottom: 10px;
198
199
  right: 10px;
199
200
  width: auto;
200
- height: 28px;
201
201
  background: #fff;
202
202
  border: 1px solid #e0e0e0;
203
203
  box-shadow: 0 4px 12px -5px rgba(0, 0, 0, 0.1);
@@ -205,7 +205,8 @@ defineExpose({
205
205
  flex-direction: row;
206
206
  align-items: center;
207
207
  justify-content: center;
208
- border-radius: 3px;
208
+ border-radius: 20px;
209
+ padding: 8px;
209
210
  cursor: pointer;
210
211
  -webkit-tap-highlight-color: transparent;
211
212
 
@@ -216,4 +217,11 @@ defineExpose({
216
217
  margin-right: 3px;
217
218
  }
218
219
  }
220
+ .scroll-button-pc {
221
+ background: #1c66e5;
222
+ }
223
+ .scroll-button-text-pc {
224
+ color: #ffffff;
225
+ font-size: 10px;
226
+ }
219
227
  </style>
@@ -5,7 +5,7 @@ import TOAST_TYPE from './type';
5
5
  import MessageConstructor from './index-web.vue';
6
6
  //@ts-ignore
7
7
  import { vueVersion } from '../../../adapter-vue-web';
8
- import { VNode } from 'vue';
8
+ import { VNode, ref } from 'vue';
9
9
  //@ts-ignore
10
10
  const { createVNode, render } = vue;
11
11
 
@@ -39,6 +39,7 @@ const Toast = function (options: IToast): IToastReturnType {
39
39
  zIndex: 20 + seed,
40
40
  offset: verticalOffset,
41
41
  id,
42
+ visible: true,
42
43
  ...options,
43
44
  onClose: () => {
44
45
  Toast.close(id, userOnClose);
@@ -66,8 +67,20 @@ const Toast = function (options: IToast): IToastReturnType {
66
67
  },
67
68
  };
68
69
  case 3:
70
+ let visible = ref(true);
69
71
  // @ts-ignore
70
- vm = createVNode(MessageConstructor, props);
72
+ vm = createVNode(MessageConstructor, {
73
+ ...props,
74
+ 'onUpdate:visible': (val: boolean) => {
75
+ visible.value = val;
76
+ if (!val) {
77
+ render(null, container);
78
+ if (container.parentNode) {
79
+ container.parentNode.removeChild(container);
80
+ }
81
+ }
82
+ },
83
+ });
71
84
  container = document.createElement('div');
72
85
  vm?.props
73
86
  && (vm.props!.onDestroy = () => {
@@ -83,7 +96,7 @@ const Toast = function (options: IToast): IToastReturnType {
83
96
  appendTo.appendChild(container.firstElementChild!);
84
97
  return {
85
98
  close: () => {
86
- vm?.component?.proxy && (vm.component!.proxy.visible = false);
99
+ vm?.component?.emit('update:visible', false);
87
100
  },
88
101
  };
89
102
  }
@@ -4,7 +4,7 @@
4
4
  @after-leave="$emit('destroy')"
5
5
  >
6
6
  <div
7
- v-show="visible"
7
+ v-show="toastVisible"
8
8
  class="message"
9
9
  :class="[handleStyle(type), isH5 && 'message-h5']"
10
10
  :style="customStyle"
@@ -20,7 +20,7 @@
20
20
  import vue from '../../../adapter-vue';
21
21
  import { isH5 } from '../../../utils/env';
22
22
  import TOAST_TYPE from './type';
23
- const { computed, onMounted, ref, watch } = vue;
23
+ const { computed, onMounted, ref, watch, onUnmounted } = vue;
24
24
 
25
25
  const props = defineProps({
26
26
  message: {
@@ -55,14 +55,19 @@ const props = defineProps({
55
55
  type: String,
56
56
  default: '',
57
57
  },
58
+ visible: {
59
+ type: Boolean,
60
+ default: false,
61
+ },
58
62
  });
59
- const visible = ref(false);
63
+ const emit = defineEmits(['update:visible']);
64
+ const toastVisible = ref(props.visible);
60
65
  let timer: any;
61
66
 
62
67
  const startTimer = () => {
63
68
  if (props.duration > 0) {
64
69
  timer = setTimeout(() => {
65
- if (visible.value) {
70
+ if (toastVisible.value) {
66
71
  close();
67
72
  }
68
73
  }, props.duration);
@@ -74,7 +79,7 @@ const clearTimer = () => {
74
79
  };
75
80
 
76
81
  const close = () => {
77
- visible.value = false;
82
+ emit('update:visible', false);
78
83
  if (typeof props.onClose === 'function') {
79
84
  props.onClose();
80
85
  }
@@ -95,7 +100,12 @@ const customStyle = computed<CSSProperties>(() => ({
95
100
 
96
101
  onMounted(() => {
97
102
  startTimer();
98
- visible.value = true;
103
+ toastVisible.value = true;
104
+ });
105
+
106
+ onUnmounted(() => {
107
+ clearTimer();
108
+ toastVisible.value = false;
99
109
  });
100
110
 
101
111
  const handleStyle = (type?: string) => {
package/constant.ts CHANGED
@@ -162,6 +162,7 @@ export const INPUT_TOOLBAR_TYPE = {
162
162
  FILE: 'file',
163
163
  VIDEO: 'video',
164
164
  RATING: 'rating',
165
+ AUDIO: 'audio',
165
166
  };
166
167
 
167
168
  export const USER_DEFAULT_AVATAR = 'https://web.sdk.qcloud.com/im/desk/assets/user_default_avatar.png';
@@ -20,7 +20,7 @@ const TUIChat = {
20
20
  '关闭阅读状态': 'Read status closed',
21
21
  '管理员开启全员禁言': 'The admin enables Mute All',
22
22
  '欢迎使用TUICallKit': 'Welcome to TUICallKit',
23
- '回到最新位置': 'Back to the latest location',
23
+ '回到最新位置': '',
24
24
  '回复': 'Reply',
25
25
  '回复详情': 'Replies',
26
26
  '集成TUICallKit': ' Integrate TUICallKit ',
@@ -105,8 +105,12 @@ const TUIChat = {
105
105
  '空': 'Empty',
106
106
  '文本包含本地审核拦截词': 'Text contains local moderation blocking words',
107
107
  '按住说话': 'Hold to speak',
108
- '抬起发送': 'Release to send',
109
- '抬起取消': 'Release to cancel',
108
+ '松开发送': 'Release to send',
109
+ '松手取消': 'Release to cancel',
110
+ '录音结束提醒': 'Recording will stop in',
111
+ '语音播放失败': 'Voice playback failed',
112
+ '请检查麦克风访问权限': 'Please check microphone access permissions',
113
+ '按压时间过短,请按压超过1秒': 'Press time too short, please hold for more than 1 second',
110
114
  '正在录音': 'Recording',
111
115
  '继续上滑可取消': 'Continue to swipe to cancel',
112
116
  '松开手指 取消发送': 'Release to cancel',
@@ -7,7 +7,6 @@ const AIDesk = {
7
7
  "提交": "Submit",
8
8
  "查看内容": "View content",
9
9
  "请填写必填项": "Please fill in the required fields",
10
- "Hi,我是": "Hi, I'm ",
11
10
  "请输入内容":"Please enter the content",
12
11
  "如果满意请给好评哦~":"If you're satisfied, please give a good review~",
13
12
  "请对本次服务进行评价": "Please rate this service",
@@ -42,7 +42,7 @@ const TUIChat = {
42
42
  '该消息不存在': 'Ang mensaheng ito ay hindi umiiral',
43
43
  '无法定位到原消息': 'Hindi mahanap ang orihinal na mensahe',
44
44
  '对方正在输入': 'Ang kabilang panig ay nagta-type...',
45
- '回到最新位置': 'Bumalik sa Pinakabagong Posisyon',
45
+ '回到最新位置': '',
46
46
  '条新消息': 'bagong mensahe',
47
47
  '点此投诉': 'I-click dito para magreklamo',
48
48
  '语音通话': 'Voice Call',
@@ -106,8 +106,12 @@ const TUIChat = {
106
106
  '空': 'Walang laman',
107
107
  '文本包含本地审核拦截词': 'May mga salitang sinusuri sa lokal na pagbabawal sa teksto',
108
108
  '按住说话': 'Pindutin at magsalita',
109
- '抬起发送': 'Ibaba at ipadala',
110
- '抬起取消': 'Ibaba at kanselahin',
109
+ '松开发送': 'Ibaba at ipadala',
110
+ '松手取消': 'Ibaba at kanselahin',
111
+ '录音结束提醒': 'Magtatapos ang pag-record sa loob ng',
112
+ '语音播放失败': 'Nabigo ang pag-play ng boses',
113
+ '请检查麦克风访问权限': 'Mangyaring suriin ang mga pahintulot sa access ng mikropono',
114
+ '按压时间过短,请按压超过1秒': 'Masyadong maikli ang pagpindot, pindutin nang higit sa 1 segundo',
111
115
  '正在录音': 'Nagre-record',
112
116
  '继续上滑可取消': 'Magpatuloy sa pag-swipe pataas para kanselahin',
113
117
  '松开手指 取消发送': 'Bitawan ang daliri para kanselahin',
@@ -7,7 +7,6 @@ const AIDesk = {
7
7
  "提交": "Isumite",
8
8
  "查看内容": "Tingnan ang Nilalaman",
9
9
  "请填写必填项": "Mangyaring punan ang mga kinakailangang field",
10
- "Hi,我是": "Hi, ako ",
11
10
  "请输入内容":"Pakilagay ang nilalaman",
12
11
  "如果满意请给好评哦~":"Kung nasiyahan ka, mangyaring magbigay ng magandang pagsusuri~",
13
12
  "请对本次服务进行评价": "Mangyaring suriin ang serbisyong ito",