tuikit-atomicx-vue3 4.5.0 → 4.5.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.
Files changed (109) hide show
  1. package/dist/{PopoverTrigger-L8abAry7.js → PopoverPortal-DghpKKm8.js} +91 -136
  2. package/dist/PopoverTrigger-BajjNkGO.js +54 -0
  3. package/dist/{PopperContent-XdhqL8Y2.js → PopperContent-SLoFuK7k.js} +6 -6
  4. package/dist/{Teleport-CSEuZbpM.js → Teleport-DFrneqLM.js} +4 -4
  5. package/dist/baseComp/Modal/Modal.js +3 -3
  6. package/dist/components/BarrageInput/EmojiPicker/EmojiPicker.js +10 -9
  7. package/dist/components/BarrageInput/TextEditor/CharacterCountExtension.js +1 -1
  8. package/dist/components/BarrageInput/TextEditor/EditorCore.js +16 -15
  9. package/dist/components/ConversationList/ConversationPreview/ConversationPreviewAbstract.js +62 -40
  10. package/dist/components/ConversationList/ConversationPreview/ConversationPreviewTimestamp.js +29 -27
  11. package/dist/components/ConversationList/ConversationPreview/ConversationPreviewTitle.js +27 -23
  12. package/dist/components/ConversationList/ConversationPreview/ConversationPreviewTitle.vue.d.ts +1 -1
  13. package/dist/components/ConversationList/ConversationPreview/ConversationPreviewUI.js +67 -65
  14. package/dist/components/ConversationList/ConversationPreview/ConversationPreviewUnread.js +32 -30
  15. package/dist/components/ConversationList/ConversationPreview/utils.d.ts +2 -2
  16. package/dist/components/ConversationList/ConversationPreview/utils.js +81 -35
  17. package/dist/components/ConversationList/i18n/en-US.d.ts +10 -1
  18. package/dist/components/ConversationList/i18n/en-US.js +11 -2
  19. package/dist/components/ConversationList/i18n/zh-CN.d.ts +10 -1
  20. package/dist/components/ConversationList/i18n/zh-CN.js +11 -2
  21. package/dist/components/LiveScenePanel/index.js +10 -9
  22. package/dist/components/MessageInput/AttachmentPicker/index.js +10 -9
  23. package/dist/components/MessageInput/EmojiPicker/EmojiPicker.js +17 -16
  24. package/dist/components/MessageInput/QuotedMessagePreview/index.js +35 -35
  25. package/dist/components/MessageInput/TextEditor/EditorCore.d.ts +13 -12
  26. package/dist/components/MessageInput/TextEditor/EditorCore.js +54 -89
  27. package/dist/components/MessageInput/TextEditor/extensions/MentionSuggestion.js +229 -0
  28. package/dist/components/MessageInput/TextEditor/extensions/MentionSuggestion.vue.d.ts +15 -0
  29. package/dist/components/MessageInput/TextEditor/extensions/characterCountExtension.js +1 -1
  30. package/dist/components/MessageInput/TextEditor/extensions/emojiExtension.d.ts +1 -0
  31. package/dist/components/MessageInput/TextEditor/extensions/emojiExtension.js +22 -0
  32. package/dist/components/MessageInput/TextEditor/extensions/enterKeyExtension.d.ts +3 -0
  33. package/dist/components/MessageInput/TextEditor/extensions/enterKeyExtension.js +15 -0
  34. package/dist/components/MessageInput/TextEditor/extensions/imageExtension.js +2 -2
  35. package/dist/components/MessageInput/TextEditor/extensions/index.d.ts +8 -0
  36. package/dist/components/MessageInput/TextEditor/extensions/index.js +12 -0
  37. package/dist/components/MessageInput/TextEditor/extensions/mentionExtension.d.ts +5 -0
  38. package/dist/components/MessageInput/TextEditor/extensions/mentionExtension.js +330 -0
  39. package/dist/components/MessageInput/TextEditor/index.js +62 -62
  40. package/dist/components/MessageInput/i18n/en-US.d.ts +3 -0
  41. package/dist/components/MessageInput/i18n/en-US.js +4 -1
  42. package/dist/components/MessageInput/i18n/index.d.ts +6 -0
  43. package/dist/components/MessageInput/i18n/zh-CN.d.ts +3 -0
  44. package/dist/components/MessageInput/i18n/zh-CN.js +4 -1
  45. package/dist/components/MessageList/Message/ImageMessage/ImageMessage.js +119 -89
  46. package/dist/components/MessageList/Message/ImageMessage/ImagePreview.js +142 -0
  47. package/dist/components/MessageList/Message/ImageMessage/ImagePreview.vue.d.ts +20 -0
  48. package/dist/components/MessageList/Message/Message.vue.d.ts +8 -0
  49. package/dist/components/MessageList/Message/MessageLayout/MessageActionDropdown/MessageActionDropdown.js +3 -3
  50. package/dist/components/MessageList/Message/MessageLayout/MessageLayout.js +69 -58
  51. package/dist/components/MessageList/Message/MessageLayout/MessageLayout.vue.d.ts +8 -0
  52. package/dist/components/MessageList/Message/index.js +8 -4
  53. package/dist/components/MessageList/MessageList.js +98 -90
  54. package/dist/components/MessageList/MessageList.vue.d.ts +1 -1
  55. package/dist/components/MessageList/index.d.ts +21 -3
  56. package/dist/components/Search/SearchResults/SearchResultsItem/Message/Message.js +6 -6
  57. package/dist/components/Search/SearchResults/SearchResultsItem/Message/Message.vue.d.ts +1 -1
  58. package/dist/index-BvFYOUyz.js +2936 -0
  59. package/dist/{index-Do-2CngU.js → index-C8Jw_xE4.js} +1621 -1731
  60. package/dist/{index-7vNB_Vx8.js → index-CiYL_XsE.js} +1 -1
  61. package/dist/index-CzCDLp99.js +2174 -0
  62. package/dist/index.d.ts +1 -1
  63. package/dist/index.js +103 -103
  64. package/dist/states/MessageActionState/MessageActionState.js +83 -223
  65. package/dist/states/MessageInputState/MessageInputState.js +115 -83
  66. package/dist/states/MessageInputState/type.d.ts +36 -10
  67. package/dist/states/MessageInputState/utils.d.ts +1 -5
  68. package/dist/states/MessageListState/MessageListState.d.ts +1 -1
  69. package/dist/styles/index.css +1 -1
  70. package/dist/{chat/index.d.ts → subEntry/chat/chat.d.ts} +2112 -2087
  71. package/dist/subEntry/chat/chat.js +89 -0
  72. package/dist/subEntry/chat/index.d.ts +11 -0
  73. package/dist/subEntry/chat/index.js +81 -0
  74. package/dist/{chat → subEntry/chat}/server.js +4 -4
  75. package/dist/{useId-CtirfF0W.js → useId-D5WE76CM.js} +1 -1
  76. package/dist/{utils-DaB7eSu5.js → utils-CttDpxqz.js} +1 -1
  77. package/package.json +8 -6
  78. package/src/components/ConversationList/ConversationPreview/ConversationPreview.scss +8 -0
  79. package/src/components/ConversationList/ConversationPreview/ConversationPreviewAbstract.vue +32 -1
  80. package/src/components/ConversationList/ConversationPreview/ConversationPreviewTitle.vue +3 -2
  81. package/src/components/ConversationList/ConversationPreview/utils.ts +98 -28
  82. package/src/components/ConversationList/i18n/en-US.ts +10 -1
  83. package/src/components/ConversationList/i18n/zh-CN.ts +10 -1
  84. package/src/components/LiveScenePanel/index.vue +1 -0
  85. package/src/components/MessageInput/QuotedMessagePreview/QuotedMessagePreview.vue +19 -22
  86. package/src/components/MessageInput/TextEditor/Editor.scss +25 -0
  87. package/src/components/MessageInput/TextEditor/EditorCore.ts +79 -99
  88. package/src/components/MessageInput/TextEditor/TextEditor.vue +64 -68
  89. package/src/components/MessageInput/TextEditor/extensions/MentionSuggestion.vue +449 -0
  90. package/src/components/MessageInput/TextEditor/extensions/emojiExtension.ts +22 -0
  91. package/src/components/MessageInput/TextEditor/extensions/enterKeyExtension.ts +22 -0
  92. package/src/components/MessageInput/TextEditor/extensions/index.ts +8 -0
  93. package/src/components/MessageInput/TextEditor/extensions/mentionExtension.ts +87 -0
  94. package/src/components/MessageInput/i18n/en-US.ts +3 -0
  95. package/src/components/MessageInput/i18n/zh-CN.ts +3 -0
  96. package/src/components/MessageList/Message/ImageMessage/ImageMessage.vue +49 -0
  97. package/src/components/MessageList/Message/ImageMessage/ImagePreview.vue +344 -0
  98. package/src/components/MessageList/Message/Message.vue +6 -0
  99. package/src/components/MessageList/Message/MessageLayout/MessageLayout.vue +8 -1
  100. package/src/components/MessageList/MessageList.vue +36 -14
  101. package/src/components/Search/SearchResults/SearchResultsItem/Message/Message.vue +30 -31
  102. package/src/index.ts +1 -1
  103. package/src/{chat/index.ts → subEntry/chat/chat.ts} +25 -18
  104. package/src/subEntry/chat/index.ts +13 -0
  105. package/src/{chat → subEntry/chat}/server.ts +3 -3
  106. package/dist/chat/index.js +0 -59
  107. package/dist/index-ZILx4LYk.js +0 -4826
  108. package/dist/states/SearchState.d.ts +0 -314
  109. /package/dist/{chat → subEntry/chat}/server.d.ts +0 -0
@@ -1,6 +1,7 @@
1
1
  <script lang="ts" setup>
2
2
  import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue';
3
3
  import cs from 'classnames';
4
+ import ImagePreview from './ImagePreview.vue';
4
5
  import type { IMessageModel as MessageModel } from '@tencentcloud/chat-uikit-engine';
5
6
 
6
7
  interface ImageMessageProps {
@@ -15,6 +16,8 @@ interface ImageMessageContent {
15
16
  showName?: string;
16
17
  }
17
18
 
19
+ defineOptions({ inheritAttrs: false });
20
+
18
21
  const MAX_HEIGHT = 320;
19
22
  const MIN_HEIGHT = 50;
20
23
  const DEFAULT_ASPECT_RATIO = 4 / 3;
@@ -58,6 +61,9 @@ const imageRef = ref<HTMLImageElement | null>(null);
58
61
  const containerRef = ref<HTMLDivElement | null>(null);
59
62
  let observerRef: IntersectionObserver | null = null;
60
63
 
64
+ const isPreviewOpen = ref(false);
65
+ let previousBodyOverflow: string | null = null;
66
+
61
67
  const isBlobImage = computed(() => messageContent.value.url.startsWith('blob'));
62
68
 
63
69
  // Utility function to get image dimensions
@@ -180,6 +186,30 @@ const handleImageError = () => {
180
186
  loadingState.value = 'error';
181
187
  };
182
188
 
189
+ function handlePreviewOpen() {
190
+ if (loadingState.value !== 'loaded') return;
191
+ if (!messageContent.value.url) return;
192
+ isPreviewOpen.value = true;
193
+ }
194
+
195
+ function handlePreviewClose() {
196
+ isPreviewOpen.value = false;
197
+ }
198
+
199
+ function lockBodyScroll() {
200
+ if (typeof document === 'undefined') return;
201
+ previousBodyOverflow = document.body.style.overflow;
202
+ document.body.style.overflow = 'hidden';
203
+ }
204
+
205
+ function unlockBodyScroll() {
206
+ if (typeof document === 'undefined') return;
207
+ if (previousBodyOverflow !== null) {
208
+ document.body.style.overflow = previousBodyOverflow;
209
+ previousBodyOverflow = null;
210
+ }
211
+ }
212
+
183
213
  // Watch for message content changes
184
214
  watch(() => messageContent.value.url, () => {
185
215
  loadingState.value = getInitialLoadingState(messageContent.value.url);
@@ -195,6 +225,14 @@ watch(() => messageContent.value.url, () => {
195
225
  fetchBlobDimensions();
196
226
  });
197
227
 
228
+ watch(isPreviewOpen, (open) => {
229
+ if (open) {
230
+ lockBodyScroll();
231
+ } else {
232
+ unlockBodyScroll();
233
+ }
234
+ });
235
+
198
236
  onMounted(() => {
199
237
  initializeLazyLoading();
200
238
  fetchBlobDimensions();
@@ -205,12 +243,14 @@ onUnmounted(() => {
205
243
  if (observerRef) {
206
244
  observerRef.disconnect();
207
245
  }
246
+ unlockBodyScroll();
208
247
  });
209
248
  </script>
210
249
 
211
250
  <template>
212
251
  <div
213
252
  ref="containerRef"
253
+ v-bind="$attrs"
214
254
  :class="cs('image-message', {
215
255
  'image-message--loading': loadingState === 'loading',
216
256
  'image-message--error': loadingState === 'error',
@@ -221,6 +261,9 @@ onUnmounted(() => {
221
261
  height: `${displaySize.height}px`,
222
262
  aspectRatio: `${displaySize.aspectRatio}`,
223
263
  }"
264
+ role="button"
265
+ tabindex="0"
266
+ @click="handlePreviewOpen"
224
267
  >
225
268
  <!-- skeleton -->
226
269
  <div
@@ -249,6 +292,12 @@ onUnmounted(() => {
249
292
  @error="handleImageError"
250
293
  >
251
294
  </div>
295
+ <ImagePreview
296
+ :open="isPreviewOpen"
297
+ :src="messageContent.url"
298
+ :alt="messageContent.showName || 'image message preview'"
299
+ @close="handlePreviewClose"
300
+ />
252
301
  </template>
253
302
 
254
303
  <style lang="scss" scoped>
@@ -0,0 +1,344 @@
1
+ <script lang="ts" setup>
2
+ import { ref, watch, onMounted, onUnmounted } from 'vue';
3
+ import cs from 'classnames';
4
+
5
+ interface ImagePreviewProps {
6
+ open: boolean;
7
+ src: string;
8
+ alt?: string;
9
+ }
10
+
11
+ defineOptions({ inheritAttrs: false });
12
+
13
+ const props = defineProps<ImagePreviewProps>();
14
+ const emit = defineEmits<{
15
+ (e: 'close'): void;
16
+ }>();
17
+
18
+ const scale = ref(1);
19
+ const translate = ref({ x: 0, y: 0 });
20
+ const isDragging = ref(false);
21
+ const lastPointer = ref({ x: 0, y: 0 });
22
+ const isError = ref(false);
23
+
24
+ let pinchStartDistance: number | null = null;
25
+ let pinchStartScale = 1;
26
+
27
+ const MIN_SCALE = 0.5;
28
+ const MAX_SCALE = 3;
29
+
30
+ function clampScale(value: number) {
31
+ return Math.min(MAX_SCALE, Math.max(MIN_SCALE, value));
32
+ }
33
+
34
+ function resetTransform() {
35
+ scale.value = 1;
36
+ translate.value = { x: 0, y: 0 };
37
+ }
38
+
39
+ function handleClose() {
40
+ emit('close');
41
+ resetTransform();
42
+ }
43
+
44
+ function handleImageError() {
45
+ isError.value = true;
46
+ }
47
+
48
+ function handleWheel(event: WheelEvent) {
49
+ event.preventDefault();
50
+ const delta = event.deltaY > 0 ? -0.2 : 0.2;
51
+ const next = clampScale(scale.value + delta);
52
+ scale.value = next;
53
+ }
54
+
55
+ function handleDoubleClick() {
56
+ scale.value = scale.value === 1 ? 2 : 1;
57
+ if (scale.value === 1) {
58
+ translate.value = { x: 0, y: 0 };
59
+ }
60
+ }
61
+
62
+ function handlePointerDown(event: PointerEvent) {
63
+ isDragging.value = true;
64
+ lastPointer.value = { x: event.clientX, y: event.clientY };
65
+ (event.target as HTMLElement)?.setPointerCapture(event.pointerId);
66
+ }
67
+
68
+ function handlePointerMove(event: PointerEvent) {
69
+ if (!isDragging.value) {
70
+ return;
71
+ }
72
+ const dx = event.clientX - lastPointer.value.x;
73
+ const dy = event.clientY - lastPointer.value.y;
74
+ translate.value = {
75
+ x: translate.value.x + dx,
76
+ y: translate.value.y + dy,
77
+ };
78
+ lastPointer.value = { x: event.clientX, y: event.clientY };
79
+ }
80
+
81
+ function handlePointerUp(event: PointerEvent) {
82
+ isDragging.value = false;
83
+ (event.target as HTMLElement)?.releasePointerCapture(event.pointerId);
84
+ }
85
+
86
+ function getTouchDistance(touches: TouchList) {
87
+ if (touches.length < 2) {
88
+ return null;
89
+ }
90
+ const [t1, t2] = [touches[0], touches[1]];
91
+ const dx = t1.clientX - t2.clientX;
92
+ const dy = t1.clientY - t2.clientY;
93
+ return Math.hypot(dx, dy);
94
+ }
95
+
96
+ function handleTouchStart(event: TouchEvent) {
97
+ if (event.touches.length === 2) {
98
+ pinchStartDistance = getTouchDistance(event.touches);
99
+ pinchStartScale = scale.value;
100
+ }
101
+ }
102
+
103
+ function handleTouchMove(event: TouchEvent) {
104
+ if (event.touches.length === 2 && pinchStartDistance) {
105
+ event.preventDefault();
106
+ const currentDistance = getTouchDistance(event.touches);
107
+ if (!currentDistance) {
108
+ return;
109
+ }
110
+ const factor = currentDistance / pinchStartDistance;
111
+ scale.value = clampScale(pinchStartScale * factor);
112
+ }
113
+ }
114
+
115
+ function handleTouchEnd() {
116
+ pinchStartDistance = null;
117
+ }
118
+
119
+ function handleKeydown(event: KeyboardEvent) {
120
+ if (event.key === 'Escape') {
121
+ handleClose();
122
+ }
123
+ }
124
+
125
+ watch(() => props.open, (isOpen) => {
126
+ if (!isOpen) {
127
+ resetTransform();
128
+ isError.value = false;
129
+ }
130
+ });
131
+
132
+ onMounted(() => {
133
+ window.addEventListener('keydown', handleKeydown);
134
+ });
135
+
136
+ onUnmounted(() => {
137
+ window.removeEventListener('keydown', handleKeydown);
138
+ });
139
+ </script>
140
+
141
+ <template>
142
+ <teleport to="body">
143
+ <div
144
+ v-if="open"
145
+ class="image-preview"
146
+ v-bind="$attrs"
147
+ >
148
+ <div class="image-preview__mask" @click="handleClose" />
149
+ <button
150
+ class="image-preview__close"
151
+ type="button"
152
+ aria-label="Close preview"
153
+ @click="handleClose"
154
+ />
155
+ <div class="image-preview__content">
156
+ <div class="image-preview__inner">
157
+ <div
158
+ class="image-preview__viewport"
159
+ >
160
+ <div :class="cs('image-preview__img-wrap', { 'image-preview__img-wrap--error': isError })">
161
+ <img
162
+ v-if="!isError"
163
+ :src="src"
164
+ :alt="alt || 'image preview'"
165
+ :style="{
166
+ transform: `translate3d(${translate.x}px, ${translate.y}px, 0) scale(${scale})`,
167
+ }"
168
+ class="image-preview__img"
169
+ draggable="false"
170
+ @error="handleImageError"
171
+ @wheel.prevent="handleWheel"
172
+ @dblclick.prevent="handleDoubleClick"
173
+ @pointerdown="handlePointerDown"
174
+ @pointermove="handlePointerMove"
175
+ @pointerup="handlePointerUp"
176
+ @pointercancel="handlePointerUp"
177
+ @touchstart.passive="handleTouchStart"
178
+ @touchmove.prevent="handleTouchMove"
179
+ @touchend="handleTouchEnd"
180
+ @touchcancel="handleTouchEnd"
181
+ >
182
+ <div v-else class="image-preview__error">
183
+ <div class="image-preview__error-icon" />
184
+ <span class="image-preview__error-text">Preview failed</span>
185
+ </div>
186
+ </div>
187
+ </div>
188
+ </div>
189
+ </div>
190
+ </div>
191
+ </teleport>
192
+ </template>
193
+
194
+ <style scoped lang="scss">
195
+ .image-preview {
196
+ position: fixed;
197
+ inset: 0;
198
+ z-index: 9999;
199
+ display: flex;
200
+ align-items: center;
201
+ justify-content: center;
202
+ color: #fff;
203
+ }
204
+
205
+ .image-preview__mask {
206
+ position: absolute;
207
+ inset: 0;
208
+ background: rgba(0, 0, 0, 0.6);
209
+ backdrop-filter: blur(2px);
210
+ z-index: 1;
211
+ }
212
+
213
+ .image-preview__close {
214
+ position: absolute;
215
+ top: 16px;
216
+ right: 16px;
217
+ width: 32px;
218
+ height: 32px;
219
+ border: none;
220
+ border-radius: 50%;
221
+ background: rgba(0, 0, 0, 0.55);
222
+ cursor: pointer;
223
+ z-index: 3;
224
+ }
225
+
226
+ .image-preview__close::before,
227
+ .image-preview__close::after {
228
+ content: "";
229
+ position: absolute;
230
+ top: 15px;
231
+ left: 8px;
232
+ width: 16px;
233
+ height: 2px;
234
+ background: #fff;
235
+ }
236
+
237
+ .image-preview__close::before {
238
+ transform: rotate(45deg);
239
+ }
240
+
241
+ .image-preview__close::after {
242
+ transform: rotate(-45deg);
243
+ }
244
+
245
+ .image-preview__content {
246
+ position: relative;
247
+ width: 100%;
248
+ height: 100%;
249
+ display: flex;
250
+ align-items: center;
251
+ justify-content: center;
252
+ padding: 0;
253
+ box-sizing: border-box;
254
+ z-index: 2;
255
+ pointer-events: none;
256
+ }
257
+
258
+ .image-preview__inner {
259
+ max-width: 100%;
260
+ max-height: 100%;
261
+ width: 100%;
262
+ height: 100%;
263
+ display: flex;
264
+ align-items: center;
265
+ justify-content: center;
266
+ pointer-events: none;
267
+ }
268
+
269
+ .image-preview__viewport {
270
+ position: relative;
271
+ max-width: 100vw;
272
+ max-height: 100vh;
273
+ width: auto;
274
+ height: auto;
275
+ display: flex;
276
+ align-items: center;
277
+ justify-content: center;
278
+ touch-action: none;
279
+ pointer-events: none;
280
+ overflow: visible;
281
+ }
282
+
283
+ .image-preview__img-wrap {
284
+ position: relative;
285
+ max-width: 100vw;
286
+ max-height: 100vh;
287
+ display: flex;
288
+ align-items: center;
289
+ justify-content: center;
290
+ overflow: visible;
291
+ border-radius: 0;
292
+ background: transparent;
293
+ pointer-events: auto;
294
+ }
295
+
296
+ .image-preview__img {
297
+ max-width: 100vw;
298
+ max-height: 100vh;
299
+ object-fit: contain;
300
+ user-select: none;
301
+ transition: transform 0.1s ease-out;
302
+ pointer-events: auto;
303
+ }
304
+
305
+ .image-preview__img-wrap--error {
306
+ background: rgba(255, 255, 255, 0.06);
307
+ }
308
+
309
+ .image-preview__error {
310
+ display: flex;
311
+ flex-direction: column;
312
+ align-items: center;
313
+ gap: 8px;
314
+ padding: 24px;
315
+ color: #fff;
316
+ font-size: 14px;
317
+ }
318
+
319
+ .image-preview__error-icon {
320
+ width: 48px;
321
+ height: 48px;
322
+ border-radius: 50%;
323
+ background: rgba(255, 255, 255, 0.2);
324
+ position: relative;
325
+ }
326
+
327
+ .image-preview__error-icon::before,
328
+ .image-preview__error-icon::after {
329
+ content: "";
330
+ position: absolute;
331
+ top: 22px;
332
+ left: 14px;
333
+ width: 20px;
334
+ height: 2px;
335
+ background: #fff;
336
+ }
337
+
338
+ .image-preview__error-icon::before { transform: rotate(45deg); }
339
+ .image-preview__error-icon::after { transform: rotate(-45deg); }
340
+
341
+ .image-preview__error-text {
342
+ color: #fff;
343
+ }
344
+ </style>
@@ -5,28 +5,33 @@ import type { MessageModel } from '../../../types/engine';
5
5
 
6
6
  interface MessageProps {
7
7
  message: MessageModel;
8
+ nick?: string;
8
9
  alignment?: 'left' | 'right' | 'two-sided';
9
10
  messageActionList?: MessageAction[] | undefined;
10
11
  isAggregated?: boolean;
11
12
  isFirstInChunk?: boolean;
12
13
  isLastInChunk?: boolean;
13
14
  isHiddenMessageAvatar?: boolean;
15
+ isHiddenMessageNick?: boolean;
14
16
  isHiddenMessageMeta?: boolean;
15
17
  }
16
18
 
17
19
  withDefaults(defineProps<MessageProps>(), {
18
20
  alignment: 'two-sided',
21
+ nick: undefined,
19
22
  messageActionList: undefined,
20
23
  isAggregated: false,
21
24
  isFirstInChunk: undefined,
22
25
  isLastInChunk: undefined,
23
26
  isHiddenMessageAvatar: false,
27
+ isHiddenMessageNick: false,
24
28
  isHiddenMessageMeta: false,
25
29
  });
26
30
  </script>
27
31
 
28
32
  <template>
29
33
  <MessageLayout
34
+ :nick="nick"
30
35
  :message="message"
31
36
  :messageActionList="messageActionList"
32
37
  :alignment="alignment"
@@ -34,6 +39,7 @@ withDefaults(defineProps<MessageProps>(), {
34
39
  :is-first-in-chunk="isFirstInChunk"
35
40
  :is-last-in-chunk="isLastInChunk"
36
41
  :is-hidden-message-avatar="isHiddenMessageAvatar"
42
+ :is-hidden-message-nick="isHiddenMessageNick"
37
43
  :is-hidden-message-meta="isHiddenMessageMeta"
38
44
  />
39
45
  </template>
@@ -25,9 +25,11 @@ import type { MessageModel } from '../../../../types/engine';
25
25
 
26
26
  interface MessageLayoutProps {
27
27
  message: MessageModel;
28
+ nick?: string;
28
29
  isAggregated?: boolean;
29
30
  isHiddenMessageAvatar?: boolean;
30
31
  isHiddenMessageMeta?: boolean;
32
+ isHiddenMessageNick?: boolean;
31
33
  isFirstInChunk?: boolean;
32
34
  isLastInChunk?: boolean;
33
35
  alignment?: 'left' | 'right' | 'two-sided';
@@ -39,9 +41,11 @@ interface MessageLayoutProps {
39
41
  const props = withDefaults(defineProps<MessageLayoutProps>(), {
40
42
  message: () => ({}) as MessageModel,
41
43
  alignment: 'two-sided',
44
+ nick: undefined,
42
45
  isAggregated: false,
43
46
  isHiddenMessageAvatar: false,
44
47
  isHiddenMessageMeta: false,
48
+ isHiddenMessageNick: false,
45
49
  isFirstInChunk: undefined,
46
50
  isLastInChunk: undefined,
47
51
  messageActionList: undefined,
@@ -125,6 +129,9 @@ function handleReadReceiptClose() {
125
129
  :src="message.avatar"
126
130
  />
127
131
  <View :class="cs(wrapperClasses)">
132
+ <View v-if="!isHiddenMessageNick" :class="cs('message-layout__nick')">
133
+ {{ props.nick || message.nameCard || message.nick || message.from }}
134
+ </View>
128
135
  <MessageBubble
129
136
  :class="bubbleClasses"
130
137
  :message="message"
@@ -186,7 +193,7 @@ $message-avatar-gap: 8px;
186
193
 
187
194
  &__nick {
188
195
  font-size: 12px;
189
- min-width: min(70%, 200px);
196
+ max-width: min(70%, 120px);
190
197
  color: var(--text-color-tertiary);
191
198
  @include mixin.text-ellipsis;
192
199
  }
@@ -14,7 +14,11 @@ import cs from 'classnames';
14
14
  import { ObserverView } from '../../baseComp/ObserverView';
15
15
  import { View } from '../../baseComp/View';
16
16
  import { useScroll } from '../../hooks/useScroll';
17
+ import { useConversationListState } from '../../states/ConversationListState';
18
+ import { useGroupSettingState } from '../../states/GroupSettingState';
17
19
  import { useMessageListState } from '../../states/MessageListState';
20
+ import { ConversationType, MessageType } from '../../types/engine';
21
+ import { isCallMessage } from '../../utils/call';
18
22
  import { throttle } from '../../utils/lodash';
19
23
  import { Message as DefaultMessage } from './Message';
20
24
  import { MessageForward } from './MessageForward';
@@ -22,7 +26,7 @@ import { MessageListContextSymbol } from './MessageListContext';
22
26
  import { MessageTimeDivider as DefaultMessageTimeDivider } from './MessageTimeDivider';
23
27
  import { ScrollToBottom } from './ScrollToBottom';
24
28
  import type { MessageAction } from '../../hooks/useMessageActions';
25
- import type { IMessageModel as MessageModel } from '@tencentcloud/chat-uikit-engine';
29
+ import type { MessageModel } from '../../types/engine';
26
30
 
27
31
  // Define message chunk interface
28
32
  interface MessageChunk {
@@ -80,8 +84,12 @@ const {
80
84
  setEnableReadReceipt,
81
85
  } = useMessageListState();
82
86
 
87
+ const { getGroupMemberList } = useGroupSettingState();
88
+
83
89
  const { scrollToBottom } = useScroll();
90
+ const { activeConversation } = useConversationListState();
84
91
 
92
+ const isGroup = computed(() => activeConversation.value?.type === ConversationType.GROUP);
85
93
  const enableMessageAggregation = computed(() => props.messageAggregationTime && props.messageAggregationTime > 0);
86
94
 
87
95
  // Message aggregation logic
@@ -110,18 +118,21 @@ const messageChunks = computed(() => {
110
118
 
111
119
  filteredMessageList.forEach((message, index, messages) => {
112
120
  const messageTime = message.time;
113
- const lastChunk = chunks.length > 0 ? chunks[chunks.length - 1] : undefined;
114
- const lastMessage = index > 0 ? messages[index - 1] : undefined;
121
+ const prevChunk = chunks.length > 0 ? chunks[chunks.length - 1] : undefined;
122
+ const prevMessage = index > 0 ? messages[index - 1] : undefined;
115
123
 
116
- const shouldCreateNewChunk = !lastChunk
117
- || messageTime - lastChunk.timestamp > MAX_TIME_BETWEEN_MESSAGE_GROUP
118
- || lastChunk.messages[0].from !== message.from
124
+ const shouldCreateNewChunk = !prevChunk
125
+ || messageTime - prevChunk.timestamp > MAX_TIME_BETWEEN_MESSAGE_GROUP
126
+ || prevChunk.messages[0].from !== message.from
119
127
  || message.isRevoked
120
- || (lastMessage && lastMessage.isRevoked)
128
+ || (prevMessage && prevMessage.isRevoked)
121
129
  || message.status === 'fail'
122
- || (lastMessage && lastMessage.status === 'fail')
130
+ || (prevMessage && prevMessage.status === 'fail')
123
131
  || message.hasRiskContent
124
- || (lastMessage && lastMessage.hasRiskContent);
132
+ || (prevMessage && prevMessage.hasRiskContent)
133
+ || isCallMessage(message)
134
+ || (prevMessage && isCallMessage(prevMessage))
135
+ || (prevMessage && prevMessage.type === MessageType.CUSTOM && prevMessage.getMessageContent().businessID === 'group_create');
125
136
 
126
137
  if (shouldCreateNewChunk) {
127
138
  chunks.push({
@@ -130,7 +141,7 @@ const messageChunks = computed(() => {
130
141
  key: `chunk-${message.ID}`,
131
142
  });
132
143
  } else {
133
- lastChunk.messages.push(message);
144
+ prevChunk.messages.push(message);
134
145
  }
135
146
  });
136
147
 
@@ -222,6 +233,9 @@ watch(messageList, (newMessages, oldMessages) => {
222
233
  scrollToBottom({ behavior: 'instant' });
223
234
  isFinishFirstRender.value = true;
224
235
  });
236
+ if (isGroup.value) {
237
+ getGroupMemberList();
238
+ }
225
239
  return;
226
240
  }
227
241
 
@@ -306,14 +320,22 @@ onUnmounted(() => {
306
320
  :message="message"
307
321
  :alignment="props.alignment"
308
322
  :messageActionList="props.messageActionList"
309
- :isAggregated="Boolean(enableMessageAggregation && messageIndex !== chunk.messages.length - 1)"
323
+ :isAggregated="Boolean(enableMessageAggregation && messageIndex !== 0)"
310
324
  :is-first-in-chunk="Boolean(messageIndex === 0)"
311
325
  :is-last-in-chunk="Boolean(messageIndex === chunk.messages.length - 1)"
312
326
  :isHiddenMessageAvatar="
313
327
  Boolean(
314
328
  alignment === 'two-sided'
315
- ? (enableMessageAggregation && messageIndex !== chunk.messages.length - 1 || message.flow === 'out')
316
- : (enableMessageAggregation && messageIndex !== chunk.messages.length - 1 )
329
+ ? (enableMessageAggregation && messageIndex !== 0 || message.flow === 'out')
330
+ : (enableMessageAggregation && messageIndex !== 0 )
331
+ )
332
+ "
333
+ :is-hidden-message-nick="
334
+ Boolean(
335
+ !isGroup
336
+ || (alignment === 'two-sided'
337
+ ? enableMessageAggregation && messageIndex !== 0 || message.flow === 'out'
338
+ : enableMessageAggregation && messageIndex !== 0)
317
339
  )
318
340
  "
319
341
  :isHiddenMessageMeta="
@@ -354,7 +376,7 @@ onUnmounted(() => {
354
376
  @include scrollbar.scrollbar-hidden();
355
377
  }
356
378
  .message-chunk--container {
357
- margin-top: 10px;
379
+ margin-top: 25px;
358
380
  }
359
381
  .message-chunk {
360
382
  display: flex;