@uzum-tech/ui 1.7.2 → 1.8.0

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 (93) hide show
  1. package/dist/index.js +1548 -998
  2. package/dist/index.prod.js +3 -3
  3. package/es/chat/index.d.ts +4 -0
  4. package/es/chat/index.js +2 -0
  5. package/es/chat/src/Chat.d.ts +19 -6
  6. package/es/chat/src/Chat.js +53 -14
  7. package/es/chat/src/ChatListItems.d.ts +6027 -0
  8. package/es/chat/src/ChatListItems.js +187 -0
  9. package/es/chat/src/ChatMessages.d.ts +6049 -0
  10. package/es/chat/src/ChatMessages.js +308 -0
  11. package/es/chat/src/ChatParts/ChatAttachment.js +4 -3
  12. package/es/chat/src/ChatParts/MainArea.d.ts +0 -2
  13. package/es/chat/src/ChatParts/MainArea.js +108 -229
  14. package/es/chat/src/ChatParts/Sidebar.js +16 -80
  15. package/es/chat/src/interface.d.ts +6 -1
  16. package/es/chat/src/styles/index.cssr.js +3 -2
  17. package/es/locales/common/arDZ.js +2 -1
  18. package/es/locales/common/deDE.js +2 -1
  19. package/es/locales/common/enGB.js +2 -1
  20. package/es/locales/common/enUS.d.ts +1 -0
  21. package/es/locales/common/enUS.js +2 -1
  22. package/es/locales/common/eo.js +2 -1
  23. package/es/locales/common/esAR.js +2 -1
  24. package/es/locales/common/faIR.js +2 -1
  25. package/es/locales/common/frFR.js +2 -1
  26. package/es/locales/common/idID.js +2 -1
  27. package/es/locales/common/itIT.js +2 -1
  28. package/es/locales/common/jaJP.js +2 -1
  29. package/es/locales/common/koKR.js +2 -1
  30. package/es/locales/common/nbNO.js +2 -1
  31. package/es/locales/common/nlNL.js +2 -1
  32. package/es/locales/common/plPL.js +2 -1
  33. package/es/locales/common/ptBR.js +2 -1
  34. package/es/locales/common/ruRU.js +2 -1
  35. package/es/locales/common/skSK.js +2 -1
  36. package/es/locales/common/svSE.js +2 -1
  37. package/es/locales/common/thTH.js +2 -1
  38. package/es/locales/common/trTR.js +2 -1
  39. package/es/locales/common/ukUA.js +2 -1
  40. package/es/locales/common/viVN.js +2 -1
  41. package/es/locales/common/zhCN.js +2 -1
  42. package/es/locales/common/zhTW.js +2 -1
  43. package/es/version.d.ts +1 -1
  44. package/es/version.js +1 -1
  45. package/lib/chat/index.d.ts +4 -0
  46. package/lib/chat/index.js +5 -1
  47. package/lib/chat/src/Chat.d.ts +19 -6
  48. package/lib/chat/src/Chat.js +52 -13
  49. package/lib/chat/src/ChatListItems.d.ts +6027 -0
  50. package/lib/chat/src/ChatListItems.js +193 -0
  51. package/lib/chat/src/ChatMessages.d.ts +6049 -0
  52. package/lib/chat/src/ChatMessages.js +314 -0
  53. package/lib/chat/src/ChatParts/ChatAttachment.js +4 -3
  54. package/lib/chat/src/ChatParts/MainArea.d.ts +0 -2
  55. package/lib/chat/src/ChatParts/MainArea.js +107 -228
  56. package/lib/chat/src/ChatParts/Sidebar.js +17 -78
  57. package/lib/chat/src/interface.d.ts +6 -1
  58. package/lib/chat/src/styles/index.cssr.js +3 -2
  59. package/lib/locales/common/arDZ.js +2 -1
  60. package/lib/locales/common/deDE.js +2 -1
  61. package/lib/locales/common/enGB.js +2 -1
  62. package/lib/locales/common/enUS.d.ts +1 -0
  63. package/lib/locales/common/enUS.js +2 -1
  64. package/lib/locales/common/eo.js +2 -1
  65. package/lib/locales/common/esAR.js +2 -1
  66. package/lib/locales/common/faIR.js +2 -1
  67. package/lib/locales/common/frFR.js +2 -1
  68. package/lib/locales/common/idID.js +2 -1
  69. package/lib/locales/common/itIT.js +2 -1
  70. package/lib/locales/common/jaJP.js +2 -1
  71. package/lib/locales/common/koKR.js +2 -1
  72. package/lib/locales/common/nbNO.js +2 -1
  73. package/lib/locales/common/nlNL.js +2 -1
  74. package/lib/locales/common/plPL.js +2 -1
  75. package/lib/locales/common/ptBR.js +2 -1
  76. package/lib/locales/common/ruRU.js +2 -1
  77. package/lib/locales/common/skSK.js +2 -1
  78. package/lib/locales/common/svSE.js +2 -1
  79. package/lib/locales/common/thTH.js +2 -1
  80. package/lib/locales/common/trTR.js +2 -1
  81. package/lib/locales/common/ukUA.js +2 -1
  82. package/lib/locales/common/viVN.js +2 -1
  83. package/lib/locales/common/zhCN.js +2 -1
  84. package/lib/locales/common/zhTW.js +2 -1
  85. package/lib/version.d.ts +1 -1
  86. package/lib/version.js +1 -1
  87. package/package.json +1 -1
  88. package/volar.d.ts +2 -0
  89. package/web-types.json +149 -1
  90. package/es/chat/src/ChatGlobalState.d.ts +0 -13
  91. package/es/chat/src/ChatGlobalState.js +0 -32
  92. package/lib/chat/src/ChatGlobalState.d.ts +0 -13
  93. package/lib/chat/src/ChatGlobalState.js +0 -36
@@ -9,8 +9,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { h, defineComponent, inject, computed, ref, watch, nextTick, onMounted, Fragment } from 'vue';
11
11
  import { MessageStatus, ChatMessageType, chatInjectionKey } from '../interface';
12
- import { ChatGlobalState } from '../ChatGlobalState';
13
- import ChatAttachmentComponent from './ChatAttachment';
14
12
  import { UInput } from '../../../input';
15
13
  import { UButton } from '../../../button';
16
14
  import { UIcon } from '../../../icon';
@@ -19,21 +17,14 @@ import { UUpload, UUploadTrigger } from '../../../upload';
19
17
  import { UFlex } from '../../../flex';
20
18
  import { resolveSlot } from '../../../_utils';
21
19
  import { UText } from '../../../typography';
22
- import { USkeleton } from '../../../skeleton';
23
- import { CheckmarkDoneSharp, Refresh, MdTime, ArrowHookUpRight, PersonNote, AttachIcon } from '../../../_internal/icons';
20
+ import UChatMessages from '../ChatMessages';
21
+ import { ArrowHookUpRight, PersonNote, AttachIcon } from '../../../_internal/icons';
24
22
  const SCROLL_DELAY = 50;
25
23
  const SENDING_DELAY = 100;
26
- const getChatGlobalState = () => ChatGlobalState.getInstance();
27
- const statusIconMapper = {
28
- [MessageStatus.READ]: CheckmarkDoneSharp,
29
- [MessageStatus.PENDING]: MdTime,
30
- [MessageStatus.RETRY]: Refresh,
31
- [MessageStatus.UNREAD]: CheckmarkDoneSharp
32
- };
33
24
  export default defineComponent({
34
25
  name: 'ChatMainArea',
35
26
  setup(_, { slots }) {
36
- const { mergedClsPrefixRef, selectedChatRef, messagesRef, typingChatIdsRef, messagesLoadingRef, messagesLoadingCountRef, headerButtonPropsRef, headerIconPropsRef, messageUploadPropsRef, footerInputPropsRef, footerButtonPropsRef, footerUploadPropsRef, footerIconPropsRef, inputPlaceholderRef, retryTextRef, typingTextRef, closeButtonTextRef, handleMessageSend, handleMessageRetry, onChatClose, onChatShare, onUserProfile
27
+ const { mergedClsPrefixRef, selectedChatRef, messagesRef, typingChatIdsRef, messagesLoadingRef, messagesLoadingCountRef, headerButtonPropsRef, headerIconPropsRef, messageUploadPropsRef, footerInputPropsRef, footerButtonPropsRef, footerUploadPropsRef, footerIconPropsRef, inputPlaceholderRef, retryTextRef, typingTextRef, closeButtonTextRef, unreadNotificationTextRef, notificationsShownSetRef, unreadCountsBeforeReadRef, markNotificationShown, handleMessageSend, handleMessageRetry, onChatClose, onChatShare, onUserProfile
37
28
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
38
29
  } = inject(chatInjectionKey);
39
30
  const messagesBodyRef = ref();
@@ -49,17 +40,18 @@ export default defineComponent({
49
40
  const hasUnreadMessages = ref(false);
50
41
  const unreadCountOnOpen = ref(0);
51
42
  const chatInputs = ref({});
43
+ const lastMessageId = ref(null);
52
44
  watch(selectedChatRef, (newChat, oldChat) => {
53
45
  if (newChat && newChat.id !== (oldChat === null || oldChat === void 0 ? void 0 : oldChat.id)) {
54
46
  inputValue.value = chatInputs.value[newChat.id] || '';
55
- unreadCountOnOpen.value = unreadMessagesCount.value;
56
- const globalState = getChatGlobalState();
57
- globalState.markChatOpened(newChat.id);
58
- if (unreadCountOnOpen.value > 0 &&
59
- !globalState.hasNotificationShown(newChat.id)) {
47
+ const unreadBeforeRead = unreadCountsBeforeReadRef.value[newChat.id] || 0;
48
+ unreadCountOnOpen.value = unreadBeforeRead;
49
+ lastMessageId.value = null;
50
+ if (unreadBeforeRead > 0 &&
51
+ !notificationsShownSetRef.value.has(newChat.id)) {
60
52
  hasUnreadMessages.value = true;
61
53
  showNotificationManually.value = true;
62
- globalState.markNotificationShown(newChat.id);
54
+ markNotificationShown(newChat.id);
63
55
  }
64
56
  else {
65
57
  hasUnreadMessages.value = false;
@@ -68,29 +60,41 @@ export default defineComponent({
68
60
  void nextTick(() => {
69
61
  scrollToBottom();
70
62
  });
63
+ setTimeout(() => {
64
+ scrollToBottom();
65
+ }, SCROLL_DELAY);
71
66
  }
72
67
  }, { immediate: true });
73
- watch(messagesRef, (newMessages, oldMessages) => {
74
- if (newMessages &&
75
- oldMessages &&
76
- newMessages.length > oldMessages.length) {
77
- if (selectedChatRef.value) {
78
- const globalState = getChatGlobalState();
79
- if (globalState.hasNotificationShown(selectedChatRef.value.id)) {
80
- showNotificationManually.value = false;
81
- }
82
- }
68
+ watch(messagesRef, (newMessages) => {
69
+ if (!newMessages || newMessages.length === 0) {
70
+ lastMessageId.value = null;
71
+ return;
72
+ }
73
+ const currentLastMessage = newMessages[newMessages.length - 1];
74
+ const currentLastId = currentLastMessage === null || currentLastMessage === void 0 ? void 0 : currentLastMessage.id;
75
+ if (currentLastId !== lastMessageId.value &&
76
+ lastMessageId.value !== null) {
83
77
  void nextTick(() => {
84
78
  scrollToBottom();
85
79
  });
80
+ setTimeout(() => {
81
+ scrollToBottom();
82
+ }, SCROLL_DELAY);
83
+ if (currentLastMessage &&
84
+ !currentLastMessage.isOwn &&
85
+ currentLastMessage.status === MessageStatus.UNREAD) {
86
+ unreadCountOnOpen.value = unreadMessagesCount.value;
87
+ }
86
88
  }
87
- }, { deep: true });
89
+ lastMessageId.value = currentLastId !== null && currentLastId !== void 0 ? currentLastId : null;
90
+ }, { deep: true, flush: 'post' });
88
91
  watch(unreadMessagesCount, (newCount, oldCount) => {
89
- if (selectedChatRef.value) {
90
- if (newCount === 0 && oldCount > 0) {
91
- showNotificationManually.value = false;
92
- hasUnreadMessages.value = false;
93
- }
92
+ if (selectedChatRef.value && newCount === 0 && oldCount > 0) {
93
+ showNotificationManually.value = false;
94
+ hasUnreadMessages.value = false;
95
+ const newSet = new Set(notificationsShownSetRef.value);
96
+ newSet.delete(selectedChatRef.value.id);
97
+ notificationsShownSetRef.value = newSet;
94
98
  }
95
99
  });
96
100
  const showUnreadNotification = computed(() => {
@@ -100,189 +104,63 @@ export default defineComponent({
100
104
  void nextTick(() => {
101
105
  scrollToBottom();
102
106
  });
107
+ setTimeout(() => {
108
+ scrollToBottom();
109
+ }, SCROLL_DELAY);
103
110
  });
104
111
  const scrollToBottom = () => {
105
112
  const el = messagesBodyRef.value;
106
113
  if (!el)
107
114
  return;
108
- if ('scrollTo' in el && typeof el.scrollTo === 'function') {
109
- el.scrollTo({ top: 999999, behavior: 'smooth' });
110
- return;
111
- }
112
115
  if ('$el' in el && el.$el instanceof HTMLElement) {
113
- const scrollContainer = el.$el.querySelector('.u-scrollbar-content') || el.$el;
114
- scrollContainer.scrollTop = scrollContainer.scrollHeight;
116
+ const scrollContainer = el.$el.querySelector('.u-scrollbar-content');
117
+ if (scrollContainer) {
118
+ scrollContainer.scrollTop = scrollContainer.scrollHeight;
119
+ return;
120
+ }
121
+ }
122
+ if ('scrollTo' in el && typeof el.scrollTo === 'function') {
123
+ el.scrollTo({ top: 999999, behavior: 'auto' });
115
124
  return;
116
125
  }
117
126
  if ('scrollTop' in el && 'scrollHeight' in el) {
118
127
  el.scrollTop = el.scrollHeight;
119
128
  }
120
129
  };
121
- const renderUnreadNotification = () => {
122
- if (!showUnreadNotification.value)
123
- return null;
124
- return (h("div", { class: `${mergedClsPrefixRef.value}-chat-main__unread-notification` },
125
- h("span", null,
126
- unreadCountOnOpen.value,
127
- " \u043D\u043E\u0432\u044B\u0445 \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F")));
128
- };
129
130
  const renderHeader = () => {
130
- var _a;
131
131
  return (h("div", { class: `${mergedClsPrefixRef.value}-chat-main__header` },
132
- h(UFlex, { justify: "space-between", align: "flex-start" },
133
- h(UText, { variant: "heading-s-bold", class: `${mergedClsPrefixRef.value}-chat-main__header-title` }, ((_a = selectedChatRef.value) === null || _a === void 0 ? void 0 : _a.title) || 'Select a chat'),
134
- h(UFlex, { align: "center", size: "small", class: `${mergedClsPrefixRef.value}-chat-main__header-actions` }, resolveSlot(slots.headerActions, () => [
135
- h(UButton, Object.assign({ secondary: true, circle: true, size: "large" }, headerButtonPropsRef.value, { onClick: () => { var _a; return (_a = onChatShare === null || onChatShare === void 0 ? void 0 : onChatShare.value) === null || _a === void 0 ? void 0 : _a.call(onChatShare); } }),
136
- h(UIcon, Object.assign({ size: 20 }, headerIconPropsRef.value),
137
- h(ArrowHookUpRight, null))),
138
- h(UButton, Object.assign({ secondary: true, circle: true, size: "large" }, headerButtonPropsRef.value, { onClick: () => { var _a; return (_a = onUserProfile === null || onUserProfile === void 0 ? void 0 : onUserProfile.value) === null || _a === void 0 ? void 0 : _a.call(onUserProfile); } }),
139
- h(UIcon, Object.assign({ size: 20 }, headerIconPropsRef.value),
140
- h(PersonNote, null))),
141
- h(UButton, Object.assign({ type: "primary", size: "large", round: true }, headerButtonPropsRef.value, { onClick: () => { var _a; return (_a = onChatClose === null || onChatClose === void 0 ? void 0 : onChatClose.value) === null || _a === void 0 ? void 0 : _a.call(onChatClose); } }), closeButtonTextRef.value)
142
- ])))));
143
- };
144
- const renderDateSeparator = (date) => {
145
- return (h("div", { class: `${mergedClsPrefixRef.value}-chat-main__date-separator` },
146
- h("span", null, date)));
147
- };
148
- const renderMessage = (message) => {
149
- const isOwn = message.isOwn;
150
- const attachments = message.attachment
151
- ? Array.isArray(message.attachment)
152
- ? message.attachment
153
- : [message.attachment]
154
- : [];
155
- return (h("div", { key: message.id, class: [
156
- `${mergedClsPrefixRef.value}-chat-main__message`,
157
- isOwn
158
- ? `${mergedClsPrefixRef.value}-chat-main__message--own`
159
- : `${mergedClsPrefixRef.value}-chat-main__message--other`
160
- ] },
161
- h("div", { class: `${mergedClsPrefixRef.value}-chat-main__message-wrapper` },
162
- message.content || attachments.length > 1 ? (h("div", { class: [
163
- `${mergedClsPrefixRef.value}-chat-main__message-bubble`,
164
- isOwn
165
- ? `${mergedClsPrefixRef.value}-chat-main__message-bubble--own`
166
- : `${mergedClsPrefixRef.value}-chat-main__message-bubble--other`
167
- ] },
168
- attachments.length > 0 && (h(ChatAttachmentComponent, { message: message, attachments: attachments, uploadProps: messageUploadPropsRef.value, withPadding: true }, {
169
- default: slots.messageAttachment,
170
- 'upload-file-title': slots.messageAttachmentTitle,
171
- 'upload-file-subtitle': slots.messageAttachmentSubtitle
172
- })),
173
- message.content && (h("div", { class: `${mergedClsPrefixRef.value}-chat-main__message-text` }, message.content)))) : attachments.length === 1 ? (h(ChatAttachmentComponent, { message: message, attachments: attachments, uploadProps: messageUploadPropsRef.value }, {
174
- default: slots.messageAttachment,
175
- 'upload-file-title': slots.messageAttachmentTitle,
176
- 'upload-file-subtitle': slots.messageAttachmentSubtitle
177
- })) : null,
178
- h("div", { class: [
179
- `${mergedClsPrefixRef.value}-chat-main__message-meta`,
180
- isOwn
181
- ? `${mergedClsPrefixRef.value}-chat-main__message-meta--own`
182
- : `${mergedClsPrefixRef.value}-chat-main__message-meta--other`,
183
- message.status === MessageStatus.RETRY &&
184
- `${mergedClsPrefixRef.value}-chat-main__message-meta--retry`
185
- ] }, message.status === MessageStatus.RETRY ? (h("div", { class: `${mergedClsPrefixRef.value}-chat-main__message-retry`, onClick: () => {
186
- handleMessageRetry(message);
187
- }, style: { cursor: 'pointer' } },
188
- h(UIcon, { size: 16, component: statusIconMapper[MessageStatus.RETRY], class: `${mergedClsPrefixRef.value}-chat-main__message-retry-icon` }),
189
- h("span", { class: `${mergedClsPrefixRef.value}-chat-main__message-retry-text` }, retryTextRef.value))) : (h(Fragment, null,
190
- h("span", { class: `${mergedClsPrefixRef.value}-chat-main__message-time` }, message.timestamp),
191
- isOwn && message.status && (h("div", { class: `${mergedClsPrefixRef.value}-chat-main__message-status` }, slots.messageStatus
192
- ? slots.messageStatus(message)
193
- : statusIconMapper[message.status] && (h(UIcon, { size: 16, component: statusIconMapper[message.status], class: [
194
- `${mergedClsPrefixRef.value}-chat-main__message-status-icon`,
195
- `${mergedClsPrefixRef.value}-chat-main__message-status-icon--${String(message.status)}`
196
- ] }))))))))));
197
- };
198
- const renderTypingIndicator = () => {
199
- return (h("div", { class: [
200
- `${mergedClsPrefixRef.value}-chat-main__message`,
201
- `${mergedClsPrefixRef.value}-chat-main__message--other`
202
- ] },
203
- h("div", { class: `${mergedClsPrefixRef.value}-chat-main__message-wrapper` },
204
- h("div", { class: [
205
- `${mergedClsPrefixRef.value}-chat-main__message-bubble`,
206
- `${mergedClsPrefixRef.value}-chat-main__message-bubble--other`,
207
- `${mergedClsPrefixRef.value}-chat-main__message-bubble--typing`
208
- ] }),
209
- h("div", { class: [
210
- `${mergedClsPrefixRef.value}-chat-main__message-meta`,
211
- `${mergedClsPrefixRef.value}-chat-main__message-meta--other`
212
- ] },
213
- h("span", { class: `${mergedClsPrefixRef.value}-chat-main__message-time` }, typingTextRef.value)))));
214
- };
215
- const renderSkeletonMessage = (isOwn, index) => {
216
- return (h("div", { key: `skeleton-${index}`, class: [
217
- `${mergedClsPrefixRef.value}-chat-main__message`,
218
- isOwn
219
- ? `${mergedClsPrefixRef.value}-chat-main__message--own`
220
- : `${mergedClsPrefixRef.value}-chat-main__message--other`
221
- ] },
222
- h("div", { class: `${mergedClsPrefixRef.value}-chat-main__message-wrapper` },
223
- h("div", { class: [
224
- `${mergedClsPrefixRef.value}-chat-main__message-bubble`,
225
- isOwn
226
- ? `${mergedClsPrefixRef.value}-chat-main__message-bubble--own`
227
- : `${mergedClsPrefixRef.value}-chat-main__message-bubble--other`
228
- ] },
229
- h("div", { class: `${mergedClsPrefixRef.value}-chat-main__message-text` },
230
- h("div", { style: { width: '376px', height: '20px' } }))),
231
- h("div", { class: [
232
- `${mergedClsPrefixRef.value}-chat-main__message-meta`,
233
- isOwn
234
- ? `${mergedClsPrefixRef.value}-chat-main__message-meta--own`
235
- : `${mergedClsPrefixRef.value}-chat-main__message-meta--other`
236
- ] },
237
- h(USkeleton, { style: { width: '36px', height: '20px', borderRadius: '4px' } })))));
132
+ h(UFlex, { justify: "space-between", align: "flex-start" }, {
133
+ default: () => (h(Fragment, null,
134
+ h(UText, { variant: "heading-s-bold", class: `${mergedClsPrefixRef.value}-chat-main__header-title` }, {
135
+ default: () => { var _a; return ((_a = selectedChatRef.value) === null || _a === void 0 ? void 0 : _a.title) || 'Select a chat'; }
136
+ }),
137
+ h(UFlex, { align: "center", size: "small", class: `${mergedClsPrefixRef.value}-chat-main__header-actions` }, {
138
+ default: () => resolveSlot(slots.headerActions, () => [
139
+ h(UButton, Object.assign({ secondary: true, circle: true, size: "large" }, headerButtonPropsRef.value, { onClick: () => { var _a; return (_a = onChatShare === null || onChatShare === void 0 ? void 0 : onChatShare.value) === null || _a === void 0 ? void 0 : _a.call(onChatShare); } }), {
140
+ default: () => (h(UIcon, Object.assign({ size: 20 }, headerIconPropsRef.value), {
141
+ default: () => h(ArrowHookUpRight, null)
142
+ }))
143
+ }),
144
+ h(UButton, Object.assign({ secondary: true, circle: true, size: "large" }, headerButtonPropsRef.value, { onClick: () => { var _a; return (_a = onUserProfile === null || onUserProfile === void 0 ? void 0 : onUserProfile.value) === null || _a === void 0 ? void 0 : _a.call(onUserProfile); } }), {
145
+ default: () => (h(UIcon, Object.assign({ size: 20 }, headerIconPropsRef.value), {
146
+ default: () => h(PersonNote, null)
147
+ }))
148
+ }),
149
+ h(UButton, Object.assign({ type: "primary", size: "large", round: true }, headerButtonPropsRef.value, { onClick: () => { var _a; return (_a = onChatClose === null || onChatClose === void 0 ? void 0 : onChatClose.value) === null || _a === void 0 ? void 0 : _a.call(onChatClose); } }), {
150
+ default: () => closeButtonTextRef.value
151
+ })
152
+ ])
153
+ })))
154
+ })));
238
155
  };
239
156
  const renderMessages = () => {
240
- var _a, _b;
241
- if (messagesLoadingRef.value) {
242
- return (h("div", { class: `${mergedClsPrefixRef.value}-chat-main__messages` }, Array.from({ length: messagesLoadingCountRef.value || 5 }).map((_, index) => {
243
- const isOwn = index % 2 === 0;
244
- return renderSkeletonMessage(isOwn, index);
245
- })));
246
- }
247
- const messagesWithDates = [];
248
- let currentDate = '';
249
- let unreadNotificationInserted = false;
250
- (_a = messagesRef.value) === null || _a === void 0 ? void 0 : _a.forEach((message, index) => {
251
- const messageDate = message.date || '';
252
- if (messageDate !== currentDate) {
253
- messagesWithDates.push({
254
- type: 'date-separator',
255
- date: messageDate,
256
- id: `date-${String(index)}`
257
- });
258
- currentDate = messageDate;
259
- }
260
- if (!unreadNotificationInserted &&
261
- !message.isOwn &&
262
- message.status === MessageStatus.UNREAD &&
263
- showUnreadNotification.value) {
264
- messagesWithDates.push({
265
- type: 'unread-notification',
266
- id: 'unread-notification'
267
- });
268
- unreadNotificationInserted = true;
269
- }
270
- messagesWithDates.push(Object.assign({ type: 'message' }, message));
271
- });
272
- return (h("div", { class: `${mergedClsPrefixRef.value}-chat-main__messages-container` },
273
- messagesWithDates.map((item) => {
274
- var _a;
275
- if (item.type === 'date-separator') {
276
- return renderDateSeparator((_a = item.date) !== null && _a !== void 0 ? _a : '');
277
- }
278
- if (item.type === 'unread-notification') {
279
- return renderUnreadNotification();
280
- }
281
- return renderMessage(item);
282
- }),
283
- selectedChatRef.value &&
284
- ((_b = typingChatIdsRef.value) === null || _b === void 0 ? void 0 : _b.includes(selectedChatRef.value.id)) &&
285
- renderTypingIndicator()));
157
+ var _a;
158
+ return (h(UChatMessages, { messages: messagesRef.value, loading: messagesLoadingRef.value, loadingCount: messagesLoadingCountRef.value, typingChatIds: typingChatIdsRef.value, selectedChatId: (_a = selectedChatRef.value) === null || _a === void 0 ? void 0 : _a.id, typingText: typingTextRef.value, retryText: retryTextRef.value, uploadProps: messageUploadPropsRef.value, showUnreadNotification: showUnreadNotification.value, unreadNotificationText: unreadNotificationTextRef.value, unreadNotificationCount: unreadCountOnOpen.value, onMessageRetry: handleMessageRetry }, {
159
+ messageAttachment: slots.messageAttachment,
160
+ messageAttachmentTitle: slots.messageAttachmentTitle,
161
+ messageAttachmentSubtitle: slots.messageAttachmentSubtitle,
162
+ messageStatus: slots.messageStatus
163
+ }));
286
164
  };
287
165
  const isSending = ref(false);
288
166
  const handleSendMessage = () => __awaiter(this, void 0, void 0, function* () {
@@ -316,12 +194,8 @@ export default defineComponent({
316
194
  inputValue.value = '';
317
195
  attachmentFileList.value = [];
318
196
  chatInputs.value[selectedChatRef.value.id] = '';
319
- const globalState = getChatGlobalState();
320
- if (globalState.hasNotificationShown(selectedChatRef.value.id)) {
321
- globalState.markNotificationShown(selectedChatRef.value.id);
322
- showNotificationManually.value = false;
323
- hasUnreadMessages.value = false;
324
- }
197
+ hasUnreadMessages.value = false;
198
+ showNotificationManually.value = false;
325
199
  void nextTick(() => {
326
200
  scrollToBottom();
327
201
  });
@@ -379,32 +253,35 @@ export default defineComponent({
379
253
  };
380
254
  const renderFooter = () => {
381
255
  return (h("div", { class: `${mergedClsPrefixRef.value}-chat-main__footer` },
382
- h(UUpload, Object.assign({ abstract: true, multiple: true, defaultUpload: false, fileList: attachmentFileList.value, onUpdateFileList: handleFileListUpdate }, footerUploadPropsRef.value),
383
- h(UFlex, { align: "center", size: "small", class: `${mergedClsPrefixRef.value}-chat-main__input-container` },
384
- h(UUploadTrigger, { abstract: true }, {
385
- default: ({ handleClick }) => (h(UButton, Object.assign({ secondary: true, size: "large", class: `${mergedClsPrefixRef.value}-chat-main__attach-btn` }, footerButtonPropsRef.value, { onClick: handleClick }),
386
- h(UIcon, Object.assign({ size: 24 }, footerIconPropsRef.value),
387
- h(AttachIcon, null))))
388
- }),
389
- h(UInput, Object.assign({ ref: "inputRef", value: inputValue.value, placeholder: inputPlaceholderRef.value, class: `${mergedClsPrefixRef.value}-chat-main__input` }, footerInputPropsRef.value, { onUpdateValue: (value) => {
390
- inputValue.value = value;
391
- if (selectedChatRef.value) {
392
- chatInputs.value[selectedChatRef.value.id] = value;
393
- }
394
- }, onKeydown: (e) => {
395
- if (e.key === 'Enter' && !e.shiftKey) {
396
- e.preventDefault();
397
- e.stopPropagation();
398
- void handleSendMessage();
399
- }
400
- } }))))));
256
+ h(UUpload, Object.assign({ abstract: true, multiple: true, defaultUpload: false, fileList: attachmentFileList.value, onUpdateFileList: handleFileListUpdate }, footerUploadPropsRef.value), {
257
+ default: () => (h(UFlex, { align: "center", size: "small", class: `${mergedClsPrefixRef.value}-chat-main__input-container` }, {
258
+ default: () => (h(Fragment, null,
259
+ h(UUploadTrigger, { abstract: true }, {
260
+ default: ({ handleClick }) => (h(UButton, Object.assign({ secondary: true, size: "large", class: `${mergedClsPrefixRef.value}-chat-main__attach-btn` }, footerButtonPropsRef.value, { onClick: handleClick }), {
261
+ default: () => (h(UIcon, Object.assign({ size: 24 }, footerIconPropsRef.value), {
262
+ default: () => h(AttachIcon, null)
263
+ }))
264
+ }))
265
+ }),
266
+ h(UInput, Object.assign({ ref: "inputRef", value: inputValue.value, placeholder: inputPlaceholderRef.value, class: `${mergedClsPrefixRef.value}-chat-main__input` }, footerInputPropsRef.value, { onUpdateValue: (value) => {
267
+ inputValue.value = value;
268
+ if (selectedChatRef.value) {
269
+ chatInputs.value[selectedChatRef.value.id] = value;
270
+ }
271
+ }, onKeydown: (e) => {
272
+ if (e.key === 'Enter' && !e.shiftKey) {
273
+ e.preventDefault();
274
+ e.stopPropagation();
275
+ void handleSendMessage();
276
+ }
277
+ } }))))
278
+ }))
279
+ })));
401
280
  };
402
281
  return {
403
282
  renderHeader,
404
283
  renderMessages,
405
284
  renderFooter,
406
- renderUnreadNotification,
407
- showUnreadNotification,
408
285
  messagesBodyRef,
409
286
  inputRef,
410
287
  inputValue,
@@ -417,7 +294,9 @@ export default defineComponent({
417
294
  const { mergedClsPrefixRef } = inject(chatInjectionKey);
418
295
  return (h("div", { class: `${mergedClsPrefixRef.value}-chat-main` },
419
296
  this.renderHeader(),
420
- h(UScrollbar, { ref: "messagesBodyRef", class: `${mergedClsPrefixRef.value}-chat-main__body` }, this.renderMessages()),
297
+ h(UScrollbar, { ref: "messagesBodyRef", class: `${mergedClsPrefixRef.value}-chat-main__body` }, {
298
+ default: () => this.renderMessages()
299
+ }),
421
300
  this.renderFooter()));
422
301
  }
423
302
  });
@@ -1,98 +1,34 @@
1
- import { h, defineComponent, inject } from 'vue';
2
- import { chatInjectionKey, MessageStatus } from '../interface';
3
- import { ChatGlobalState } from '../ChatGlobalState';
1
+ import { h, defineComponent, inject, Fragment } from 'vue';
2
+ import { chatInjectionKey } from '../interface';
4
3
  import { UFlex } from '../../../flex';
5
- import { UEmpty } from '../../../empty';
6
- import { UAvatar } from '../../../avatar';
7
4
  import { USelect } from '../../../select';
8
- import { UListItem, UList } from '../../../list';
9
- import { UBadge } from '../../../badge';
10
- import { UIcon } from '../../../icon';
11
- import { CheckmarkDoneSharp, MdTime, PersonOutline, Refresh } from '../../../_internal/icons';
12
- const statusIconMapper = {
13
- [MessageStatus.READ]: CheckmarkDoneSharp,
14
- [MessageStatus.PENDING]: MdTime,
15
- [MessageStatus.RETRY]: Refresh,
16
- [MessageStatus.UNREAD]: CheckmarkDoneSharp
17
- };
5
+ import UChatListItems from '../ChatListItems';
18
6
  export default defineComponent({
19
7
  name: 'ChatSidebar',
20
8
  setup(props, { slots }) {
21
9
  return { slots };
22
10
  },
23
11
  render() {
24
- var _a, _b;
25
12
  const { slots } = this;
26
- const { mergedClsPrefixRef, chatItemsRef, chatItemsLoadingRef, chatItemsLoadingCountRef, listEmptyPropsRef, selectedChatIdRef, typingChatIdsRef, typingTextRef, listItemAvatarPropsRef, listItemBadgePropsRef, handleChatSelect
13
+ const { mergedClsPrefixRef, chatItemsRef, chatItemsLoadingRef, chatItemsLoadingCountRef, listEmptyPropsRef, selectedChatIdRef, typingChatIdsRef, typingTextRef, listItemAvatarPropsRef, listItemBadgePropsRef, notificationsShownSetRef, handleChatSelect
27
14
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
28
15
  } = inject(chatInjectionKey);
29
16
  const renderListHeader = () => {
30
- var _a, _b;
31
17
  return (h("div", { class: `${mergedClsPrefixRef.value}-chat-sidebar__header` },
32
- h(UFlex, { align: "center", justify: "space-between", style: { width: '100%' } },
33
- h("div", { class: `${mergedClsPrefixRef.value}-chat-sidebar__header-main` }, ((_a = slots.sidebarHeaderMain) === null || _a === void 0 ? void 0 : _a.call(slots)) || h(USelect, { round: true })),
34
- h("div", { class: `${mergedClsPrefixRef.value}-chat-sidebar__header-actions` }, (_b = slots.sidebarHeaderActions) === null || _b === void 0 ? void 0 : _b.call(slots)))));
35
- };
36
- const renderChatItem = (item) => {
37
- var _a, _b;
38
- const isSelected = selectedChatIdRef.value === item.id;
39
- const isTyping = (_b = (_a = typingChatIdsRef.value) === null || _a === void 0 ? void 0 : _a.includes(item.id)) !== null && _b !== void 0 ? _b : false;
40
- return (h(UListItem, { key: item.id, showIcon: false, onClick: () => {
41
- handleChatSelect(item.id);
42
- }, class: [
43
- `${mergedClsPrefixRef.value}-chat-sidebar__item`,
44
- isSelected &&
45
- `${mergedClsPrefixRef.value}-chat-sidebar__item--selected`,
46
- isTyping && `${mergedClsPrefixRef.value}-chat-sidebar__item--typing`
47
- ] }, {
48
- prefix: () => {
49
- var _a;
50
- return (h(UAvatar, Object.assign({ size: "medium", src: typeof item.avatar === 'string' ? item.avatar : undefined }, listItemAvatarPropsRef.value), typeof item.avatar === 'function'
51
- ? item.avatar()
52
- : typeof item.title === 'string'
53
- ? (_a = item.title.charAt(0)) === null || _a === void 0 ? void 0 : _a.toUpperCase()
54
- : '?'));
55
- },
56
- header: () => (h("span", { class: `${mergedClsPrefixRef.value}-chat-sidebar__item-title` }, typeof item.title === 'function' ? item.title() : item.title)),
57
- description: () => (h("span", { class: [
58
- `${mergedClsPrefixRef.value}-chat-sidebar__item-subtitle`,
59
- isTyping && 'typing'
60
- ], style: {
61
- display: 'block',
62
- overflow: 'hidden',
63
- textOverflow: 'ellipsis',
64
- whiteSpace: 'nowrap',
65
- minWidth: 0,
66
- maxWidth: '100%'
67
- } }, isTyping
68
- ? typingTextRef.value
69
- : typeof item.subtitle === 'string'
70
- ? item.subtitle
71
- : typeof item.subtitle === 'function'
72
- ? item.subtitle()
73
- : item.subtitle)),
74
- suffix: () => {
75
- const getChatGlobalState = () => ChatGlobalState.getInstance();
76
- const globalState = getChatGlobalState();
77
- const wasOpened = globalState.isChatOpened(item.id);
78
- const displayUnreadCount = wasOpened ? 0 : item.unreadCount || 0;
79
- const hasUnreadIncoming = displayUnreadCount > 0;
80
- const lastMessageIsOwn = item.lastMessageIsOwn;
81
- return (h("div", { class: `${mergedClsPrefixRef.value}-chat-sidebar__item-indicators` },
82
- h("div", { class: `${mergedClsPrefixRef.value}-chat-sidebar__item-time` }, item.datetime),
83
- h("div", { class: `${mergedClsPrefixRef.value}-chat-sidebar__item-status` },
84
- lastMessageIsOwn &&
85
- (slots.chatItemStatus ? (slots.chatItemStatus(item)) : item.messageStatus &&
86
- statusIconMapper[item.messageStatus] ? (h(UIcon, { size: 16, component: statusIconMapper[item.messageStatus], class: [
87
- `${mergedClsPrefixRef.value}-chat-sidebar__item-status-icon`,
88
- `${mergedClsPrefixRef.value}-chat-sidebar__item-status-icon--${item.messageStatus}`
89
- ] })) : null),
90
- !lastMessageIsOwn && hasUnreadIncoming && (h(UBadge, Object.assign({ value: displayUnreadCount }, listItemBadgePropsRef.value))))));
91
- }
92
- }));
18
+ h(UFlex, { align: "center", justify: "space-between", class: `${mergedClsPrefixRef.value}-chat-sidebar__header-container`, style: { width: '100%' } }, {
19
+ default: () => {
20
+ var _a, _b;
21
+ return (h(Fragment, null,
22
+ h("div", { class: `${mergedClsPrefixRef.value}-chat-sidebar__header-main` }, ((_a = slots.sidebarHeaderMain) === null || _a === void 0 ? void 0 : _a.call(slots)) || (h(USelect, { round: true, size: "small" }))),
23
+ h("div", { class: `${mergedClsPrefixRef.value}-chat-sidebar__header-actions` }, (_b = slots.sidebarHeaderActions) === null || _b === void 0 ? void 0 : _b.call(slots))));
24
+ }
25
+ })));
93
26
  };
94
27
  return (h("div", { class: `${mergedClsPrefixRef.value}-chat-sidebar` },
95
28
  renderListHeader(),
96
- h("div", { class: `${mergedClsPrefixRef.value}-chat-sidebar__content` }, chatItemsLoadingRef.value ? (h(UList, { loading: true }, Array.from({ length: chatItemsLoadingCountRef.value || 10 }).map((_, index) => (h(UListItem, { key: index, avatar: { icon: PersonOutline }, description: { text: 'Loading...' }, header: { text: 'Loading...' } }))))) : ((_a = chatItemsRef.value) === null || _a === void 0 ? void 0 : _a.length) ? (h(UList, { showIcon: false }, (_b = chatItemsRef.value) === null || _b === void 0 ? void 0 : _b.map((item) => renderChatItem(item)))) : (h(UEmpty, Object.assign({}, listEmptyPropsRef.value))))));
29
+ h("div", { class: `${mergedClsPrefixRef.value}-chat-sidebar__content` },
30
+ h(UChatListItems, { chatItems: chatItemsRef.value, selectedChatId: selectedChatIdRef.value, typingChatIds: typingChatIdsRef.value, typingText: typingTextRef.value, loading: chatItemsLoadingRef.value, loadingCount: chatItemsLoadingCountRef.value, emptyProps: listEmptyPropsRef.value, avatarProps: listItemAvatarPropsRef.value, badgeProps: listItemBadgePropsRef.value, notificationsShown: notificationsShownSetRef.value, onChatSelect: handleChatSelect }, {
31
+ chatItemStatus: slots.chatItemStatus
32
+ }))));
97
33
  }
98
34
  });
@@ -109,7 +109,7 @@ export interface ChatAttachment {
109
109
  type: ChatMessageType.FILE;
110
110
  name: string;
111
111
  url: string;
112
- size?: number;
112
+ size?: number | string;
113
113
  thumbnail?: string | (() => VNodeChild);
114
114
  preview?: string | (() => VNodeChild);
115
115
  status?: 'pending' | 'uploading' | 'finished' | 'error';
@@ -146,6 +146,7 @@ export interface ChatProps {
146
146
  retryText?: string;
147
147
  typingText?: string;
148
148
  closeButtonText?: string;
149
+ unreadNotificationText?: string;
149
150
  chatItemsLoading?: boolean;
150
151
  chatItemsLoadingCount?: number;
151
152
  messagesLoading?: boolean;
@@ -269,6 +270,10 @@ export declare const chatInjectionKey: InjectionKey<{
269
270
  retryTextRef: Ref<string>;
270
271
  typingTextRef: Ref<string>;
271
272
  closeButtonTextRef: Ref<string>;
273
+ unreadNotificationTextRef: Ref<string>;
274
+ notificationsShownSetRef: Ref<Set<ChatId>>;
275
+ unreadCountsBeforeReadRef: Ref<Record<ChatId, number>>;
276
+ markNotificationShown: (chatId: ChatId) => void;
272
277
  handleChatSelect: (chatId: ChatId) => void;
273
278
  handleMessageSend: (content: string, attachments?: ChatAttachment[]) => void;
274
279
  handleMessageRetry: (message: ChatMessageData) => void;
@@ -26,8 +26,9 @@ export default cB('chat', `
26
26
  border-radius: 20px;
27
27
  overflow: hidden;
28
28
  `, [cE('header', `
29
- padding: 16px 16px 0 16px;
30
29
  flex-shrink: 0;
30
+ `), cE('header-container', `
31
+ padding: 16px 16px 4px 16px;
31
32
  `), cE('header-main', `
32
33
  flex: 0 0 48%;
33
34
  min-width: 0;
@@ -43,7 +44,7 @@ export default cB('chat', `
43
44
  overflow-y: auto;
44
45
  min-height: 0;
45
46
  padding: 16px;
46
- padding-top: 12px;
47
+ padding-top: 8px;
47
48
  `), cE('item', `
48
49
  padding: 12px 16px;
49
50
  width: 100%;
@@ -128,7 +128,8 @@ const arDZ = {
128
128
  inputPlaceholder: 'Type a message...',
129
129
  typingText: 'Typing...',
130
130
  retryText: 'Resend',
131
- closeButtonText: 'Close chat'
131
+ closeButtonText: 'Close chat',
132
+ unreadNotificationText: 'new messages'
132
133
  }
133
134
  };
134
135
  export default arDZ;
@@ -128,7 +128,8 @@ const deDE = {
128
128
  inputPlaceholder: 'Type a message...',
129
129
  typingText: 'Typing...',
130
130
  retryText: 'Resend',
131
- closeButtonText: 'Close chat'
131
+ closeButtonText: 'Close chat',
132
+ unreadNotificationText: 'new messages'
132
133
  }
133
134
  };
134
135
  export default deDE;
@@ -129,7 +129,8 @@ const enGB = {
129
129
  inputPlaceholder: 'Type a message...',
130
130
  typingText: 'Typing...',
131
131
  retryText: 'Resend',
132
- closeButtonText: 'Close chat'
132
+ closeButtonText: 'Close chat',
133
+ unreadNotificationText: 'new messages'
133
134
  }
134
135
  };
135
136
  export default enGB;