@bcc-code/vue-bcc-chat-ui 3.40.2 → 4.0.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.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@bcc-code/vue-bcc-chat-ui",
3
3
  "author": "bcc-code",
4
4
  "license": "Apache-2.0",
5
- "version": "3.40.2",
5
+ "version": "4.0.0",
6
6
  "type": "module",
7
7
  "private": false,
8
8
  "files": [
@@ -8,16 +8,18 @@ import {
8
8
  provide,
9
9
  ref,
10
10
  watch,
11
+ watchEffect,
11
12
  } from "vue";
12
13
  import chat from "../chat";
13
14
  import { connect, disconnect } from "../chat/connection";
14
15
  import { loggedIn } from "../chat/login";
15
- import { Group } from "@cometchat/chat-sdk-javascript";
16
+ import { CometChat, Group, GroupMember } from "@cometchat/chat-sdk-javascript";
16
17
  import { watchAndApplyStyle } from "../chat/styleFix";
17
- import { ChatInstance } from "../chat/types";
18
+ import { ChatInstance, ParticipantInfo } from "../chat/types";
18
19
  import { CometChatMessageEvents, MessageStatus } from "@cometchat/uikit-resources";
19
20
  import BccChatMessages from "./BccChatMessages.vue";
20
21
  import BccScheduledMessages from "./BccScheduledMessages.vue";
22
+ import { getGroupMembersInfo } from "../chat/data";
21
23
  const props = defineProps({
22
24
  chatUid: { type: String, required: true },
23
25
  senderDisplayName: { type: String, required: false },
@@ -32,6 +34,10 @@ const componentId = ref(
32
34
  `component-${Math.random().toString(36).substring(2, 11)}`
33
35
  );
34
36
  const chatGroup = ref<Group>();
37
+ const chatGroupNotFound = ref(false);
38
+
39
+ const emit = defineEmits(['groupMembersFetched']);
40
+ const groupMembers = ref<ParticipantInfo[]>();
35
41
 
36
42
  const chatInstance: Ref<ChatInstance> = ref({
37
43
  componentId: componentId,
@@ -48,30 +54,39 @@ const chatInstance: Ref<ChatInstance> = ref({
48
54
  provide("chatInstance", chatInstance);
49
55
 
50
56
  const onMessageSend = CometChatMessageEvents.ccMessageSent.subscribe(({ message, status }) => {
51
- if (status === MessageStatus.inprogress) {
52
- let metadata: any = (message as any)?.getMetadata() || {};
53
- let scope: string | undefined = chatGroup.value?.getScope();
57
+ if (status !== MessageStatus.inprogress) {
58
+ return
59
+ }
54
60
 
55
- if (chatInstance.value.replyToMessage) {
56
- metadata.replyToMessageId = chatInstance.value.replyToMessage?.getId();
57
- chatInstance.value.replyToMessage = null;
58
- }
61
+ let metadata: any = (message as any)?.getMetadata() || {};
62
+ let scope: string | undefined = chatGroup.value?.getScope();
59
63
 
60
- if (props.senderDisplayName && scope == "moderator") {
61
- metadata.senderDisplayName = props.senderDisplayName
62
- }
64
+ if (chatInstance.value.replyToMessage) {
65
+ metadata.replyToMessageId = chatInstance.value.replyToMessage?.getId();
66
+ chatInstance.value.replyToMessage = null;
67
+ }
63
68
 
64
- (message as any)?.setMetadata(metadata);
69
+ if (props.senderDisplayName && scope == "moderator") {
70
+ metadata.senderDisplayName = props.senderDisplayName
65
71
  }
72
+
73
+ const muid = window.crypto.randomUUID();
74
+ message.setMuid(muid);
75
+ const tags = (message as any).getTags() ?? [];
76
+ tags.push(muid);
77
+ (message as any).setTags(tags);
78
+ (message as any).setMetadata(metadata);
66
79
  });
67
80
 
68
- watch([loggedIn, () => props.chatUid, chat.initialized, () => props.groupMessageGetter], async ([_loggedIn, _chatUid, _initialized, _groupMessageGetter]) => {
81
+ watch([loggedIn, chat.onlineStatus, () => props.chatUid, chat.initialized, () => props.groupMessageGetter], async ([_loggedIn, _online, _chatUid, _initialized, _groupMessageGetter]) => {
69
82
  if (_loggedIn && _chatUid && _initialized) {
83
+ chatGroupNotFound.value = false;
70
84
  const [_, group] = await Promise.all([
71
85
  connect(componentId.value),
72
86
  chat.getGroup(props.chatUid)
73
87
  ])
74
88
  chatGroup.value = group ?? undefined;
89
+ chatGroupNotFound.value = group === undefined;
75
90
 
76
91
  if (_groupMessageGetter && !chatGroup.value?.getHasJoined()) {
77
92
  chat.updateGetGroupMessages(_groupMessageGetter)
@@ -86,6 +101,8 @@ watch([loggedIn, () => props.chatUid, chat.initialized, () => props.groupMessage
86
101
  }
87
102
  }
88
103
  ])
104
+
105
+ getGroupMembersInfo(_chatUid).then((participants) => groupMembers.value = participants);
89
106
  }
90
107
 
91
108
  await chat.clearGroupChatCount(props.chatUid);
@@ -94,7 +111,13 @@ watch([loggedIn, () => props.chatUid, chat.initialized, () => props.groupMessage
94
111
  }
95
112
  }, { immediate: true });
96
113
 
97
-
114
+ watchEffect(() => {
115
+ if (groupMembers.value && groupMembers.value.length) {
116
+ emit('groupMembersFetched', groupMembers.value);
117
+ } else {
118
+ emit('groupMembersFetched', []);
119
+ }
120
+ })
98
121
 
99
122
  onMounted(() => {
100
123
  watchAndApplyStyle(".bcc-chat-message-list-wrapper", [
@@ -115,6 +138,30 @@ onMounted(() => {
115
138
  shadowDomSelector: "cometchat-preview",
116
139
  }
117
140
  ]);
141
+
142
+ CometChat.addGroupListener("groupListener", new CometChat.GroupListener({
143
+ onGroupMemberScopeChanged: (_action: any, changedUser: GroupMember, newScope: any, _oldScope: any, _changedGroup: GroupMember) => {
144
+ handleGroupMemberChanges(changedUser, "update", newScope);
145
+ },
146
+ onGroupMemberKicked: (_action: any, changedUser: GroupMember, _Ye: any, _group: any) => {
147
+ handleGroupMemberChanges(changedUser, "remove");
148
+ },
149
+ onGroupMemberBanned: (_action: any, user: GroupMember, _Ye: any, _group: any) => {
150
+ handleGroupMemberChanges(user, "remove");
151
+ },
152
+ onGroupMemberUnbanned: (_action: any, user: GroupMember, _Ye: any, _group: any) => {
153
+ handleGroupMemberChanges(user, "add");
154
+ },
155
+ onMemberAddedToGroup: (_action: any, user: GroupMember, _Ye: any, _group: any) => {
156
+ handleGroupMemberChanges(user, "add");
157
+ },
158
+ onGroupMemberLeft: (_action: any, user: GroupMember, _group: any) => {
159
+ handleGroupMemberChanges(user, "remove");
160
+ },
161
+ onGroupMemberJoined: (_action: any, user: GroupMember, _group: any) => {
162
+ handleGroupMemberChanges(user, "add");
163
+ }
164
+ }))
118
165
  });
119
166
 
120
167
  onBeforeUnmount(() => {
@@ -125,6 +172,31 @@ onUnmounted(async () => {
125
172
  await disconnect(componentId.value);
126
173
  });
127
174
 
175
+ function handleGroupMemberChanges(changedUser: GroupMember, action: string, newScope?: string) {
176
+ let participant: ParticipantInfo = {
177
+ uid: changedUser.getUid(),
178
+ displayName: changedUser.getName(),
179
+ avatar: changedUser.getAvatar(),
180
+ role: newScope ? newScope : CometChat.GROUP_MEMBER_SCOPE.PARTICIPANT
181
+ }
182
+
183
+ if (action === "add" && !existsInGroup(changedUser.getUid())) {
184
+ groupMembers.value?.push(participant)
185
+ }
186
+
187
+ if (action === "remove" && existsInGroup(changedUser.getUid())) {
188
+ groupMembers.value = groupMembers.value?.filter(participant => participant.uid !== changedUser.getUid());
189
+ }
190
+
191
+ if (action === "update" && existsInGroup(changedUser.getUid())) {
192
+ groupMembers.value = groupMembers.value?.map(p => p.uid === changedUser.getUid() ? { ...p, ...participant } : p);
193
+ }
194
+ }
195
+
196
+ function existsInGroup(uid: string): boolean {
197
+ return groupMembers.value?.some(p => p.uid === uid) ?? false;
198
+ }
199
+
128
200
  function closeScheduledMessage() {
129
201
  chatInstance.value.captionedAttachment = null
130
202
  chatInstance.value.replyToMessage = null
@@ -144,14 +216,20 @@ function closeScheduledMessage() {
144
216
  <div>Connecting: {{chat.connecting.value}}</div>
145
217
  <div>CometChat: {{cometChatStatus}} <button @click="getCometChatStatus()">[refresh]</button></div>
146
218
  </div> -->
147
- <BccChatMessages v-if="chatGroup && !chatInstance.showScheduledMessagesChat" :chatGroup="chatGroup" :hide-deleted-messages="props.hideDeletedMessages"
148
- :chatUid="props.chatUid"></BccChatMessages>
219
+ <BccChatMessages v-if="chatGroup && !chatInstance.showScheduledMessagesChat" :chatGroup="chatGroup"
220
+ :hide-deleted-messages="props.hideDeletedMessages" :chatUid="props.chatUid"></BccChatMessages>
149
221
  <BccScheduledMessages v-else-if="chatGroup && chatInstance.showScheduledMessagesChat"
150
222
  class="bcc-scheduled-message-list" :chatGroup="chatInstance.scheduledMessageChat!"
151
223
  :originalChatUid="props.chatUid" @close="closeScheduledMessage()"></BccScheduledMessages>
224
+ <div v-else-if="chat.onlineStatus.value === 'offline'" class="bcc-chat-message-list-offline">
225
+ <span>Waiting for network...</span>
226
+ </div>
152
227
  <div v-else-if="!chat.connected.value" class="bcc-chat-message-list-offline">
153
228
  <span>Connecting...</span>
154
229
  </div>
230
+ <div v-else-if="chatGroupNotFound" class="bcc-chat-message-list-offline">
231
+ <span>Chat not found...</span>
232
+ </div>
155
233
  <div v-else-if="!chatGroup" class="bcc-chat-message-list-offline">
156
234
  <span>Loading chat...</span>
157
235
  </div>
@@ -165,8 +243,6 @@ function closeScheduledMessage() {
165
243
  body {
166
244
  background: #fff;
167
245
  color: #000;
168
-
169
-
170
246
  }
171
247
 
172
248
  :host.dark body {
@@ -230,8 +306,11 @@ accent900: text in avatar
230
306
  --cc__link-color: #cfeac8;
231
307
  }
232
308
 
309
+
310
+
233
311
  .bcc-chat-message-list {
234
312
  height: 100%;
313
+
235
314
  /* 1. Wrapper for Messages component */
236
315
  .cc-messages-wrapper {
237
316
 
@@ -284,26 +363,35 @@ accent900: text in avatar
284
363
  }
285
364
  }
286
365
 
366
+
287
367
  /* 2. Message for Messages Composer when offline */
288
368
  .bcc-chat-message-composer-offline {
289
369
  position: relative;
290
- top: -96px;
291
- height: 96px;
370
+ bottom: 38px;
371
+ text-align: center;
372
+ //height: 96px;
292
373
  width: 100%;
293
- backdrop-filter: blur(4px);
374
+ //backdrop-filter: blur(4px);
294
375
  color: var(--cc__text-color);
295
- font: 700 1rem sans-serif;
376
+ font: 500 11px sans-serif;
296
377
 
297
378
  /* 2.1 Text of Messages Composer offline view */
298
379
  span {
299
- width: 100%;
300
- height: 100%;
301
- display: flex;
302
- justify-content: center;
303
- align-items: center;
304
- border-radius: 8px 8px 0 0;
380
+ width: 150px;
381
+ display: inline;
382
+ padding: 5px;
383
+ background-color: #eee;
384
+ border-radius: 8px;
305
385
  }
306
386
  }
387
+
388
+ /* Hide send button when offline */
389
+ &.offline .messageinput__primaryactions,
390
+ &.offline .messageinput__auxiliaryactions,
391
+ &.offline .messageinput__secondaryactions {
392
+ opacity: 0.2;
393
+ }
394
+
307
395
  }
308
396
 
309
397
  .bcc-chat-message-list-offline {
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import chat from "../chat";
3
3
  import { Group } from "@cometchat/chat-sdk-javascript";
4
- import { inject, nextTick, onMounted, Ref, ref, watch, watchEffect } from 'vue';
4
+ import { computed, inject, nextTick, onMounted, Ref, ref, watch, watchEffect } from 'vue';
5
5
  import { ChatInstance } from '../chat/types';
6
6
  import BccAttachmentBox from "./BccAttachmentBox.vue";
7
7
  import BccChatSendButton from "./BccChatSendButton.vue";
@@ -15,6 +15,8 @@ const props = defineProps({
15
15
  hideDeletedMessages: { type: Boolean, required: false, default: false }
16
16
  })
17
17
 
18
+ const isOffline = computed(() => chat.onlineStatus.value === 'offline');
19
+
18
20
  const chatGroup = ref<Group>();
19
21
 
20
22
  const chatInstance = inject<Ref<ChatInstance>>("chatInstance");
@@ -185,16 +187,16 @@ function isParticipant(): boolean {
185
187
  </script>
186
188
 
187
189
  <template>
188
- <div class="bcc-chat-message-list">
190
+ <div :class="'bcc-chat-message-list' + (isOffline ? ' offline' : '')">
189
191
  <CometChatMessages :hideMessageHeader="true" :disableSoundForMessages="true"
190
192
  :messageComposerConfiguration="messageComposerConfiguration"
191
193
  :messageListConfiguration="messageListConfiguration"
192
194
  :threadedMessagesConfiguration="threadedMessagesConfiguration"
193
- :hideMessageComposer="isParticipant() && isChannel()" :hideDetails="true" :group="chatGroup" />
195
+ :hideMessageComposer="(isParticipant() && isChannel())" :hideDetails="true" :group="chatGroup" />
194
196
  <div v-if="chat.onlineStatus.value === 'offline' || !chat.connected.value"
195
197
  class="bcc-chat-message-composer-offline">
196
- <span v-if="chat.onlineStatus.value === 'offline'">Waiting for network...</span>
197
- <span v-else>Connecting...</span>
198
+ <span v-if="isOffline">Waiting for network...</span>
199
+ <span v-else>Connecting... </span>
198
200
  </div>
199
201
  <!-- <BccScheduledMessageModal v-if="showModal" @close="closeModal()" @send="scheduleMessage">
200
202
  </BccScheduledMessageModal>
@@ -4,6 +4,7 @@ import { SendIcon } from "@cometchat/chat-uikit-vue";
4
4
  import { sendMediaMessage } from "../chat/captionedAttachment";
5
5
  import { ChatInstance } from "../chat/types";
6
6
  import { getCurrentInstance, inject, onBeforeUnmount, onMounted, Ref } from "vue";
7
+ import chat from "../chat";
7
8
 
8
9
  const sendButtonIconURL = SendIcon;
9
10
  const sendButtonStyle = {
@@ -21,6 +22,8 @@ const msgComposerData = vueInstance?.parent?.type?.name === "CometChatMessageCom
21
22
  const msgComposerProps = vueInstance?.parent?.type?.name === "CometChatMessageComposer" && vueInstance?.parent?.props;
22
23
 
23
24
  function sendMessage() {
25
+ debugger;
26
+ if (chat.onlineStatus.value === "offline") return;
24
27
  if (!msgComposerData) return false;
25
28
  if (!chatInstance?.value.captionedAttachment?.fileObject) return false;
26
29
  if (!msgComposerProps?.group && !msgComposerProps?.user) return false;
@@ -44,6 +47,8 @@ function sendMessage() {
44
47
  }
45
48
 
46
49
  function sendMessageOnEnter(textEnteredEvent: CustomEventInit) {
50
+ debugger;
51
+ if (chat.onlineStatus.value === "offline") return;
47
52
  if (!msgComposerData) return;
48
53
  msgComposerData.messageText = textEnteredEvent.detail.value;
49
54
  if (sendMessage()) {
@@ -66,7 +71,7 @@ if (chatInstance) {
66
71
  // Here we add an additional onKeyDown event on the input field that sends our image when pressing Enter if the input field **is empty**.
67
72
  // We need this in addition to hooking into the "cc-text-input-entered" event because that event isn't triggered if the input field is empty.
68
73
  const ccTextInput = msgComposerData.inputRef.querySelector(".messageinput-input");
69
- ccTextInput.addEventListener("keydown", function(event: KeyboardEvent) {
74
+ ccTextInput.addEventListener("keydown", function (event: KeyboardEvent) {
70
75
  if (msgComposerData.messageText?.trim() == "" && event.keyCode === 13 && !event.shiftKey) sendMessage();
71
76
  });
72
77
  }
@@ -74,7 +79,8 @@ if (chatInstance) {
74
79
  </script>
75
80
 
76
81
  <template>
77
- <div class="cc-messagecomposer-wrapper__sendbutton" ref="sendButton" :style="{ 'background': !msgComposerData || !msgComposerProps ? '#f00' : 'unset'}">
82
+ <div class="cc-messagecomposer-wrapper__sendbutton" ref="sendButton"
83
+ :style="{ 'background': !msgComposerData || !msgComposerProps ? '#f00' : 'unset' }">
78
84
  <cometchat-button :iconURL="sendButtonIconURL" :buttonStyle="sendButtonStyle"
79
85
  :hoverText="localize('SEND_MESSAGE')" @click=sendMessage() />
80
86
  </div>
@@ -85,7 +85,7 @@ onUnmounted(() => {
85
85
  <div v-if="chat.onlineStatus.value === 'offline' || !chat.connected.value"
86
86
  class="bcc-chat-message-composer-offline">
87
87
  <span v-if="chat.onlineStatus.value === 'offline'">Waiting for network...</span>
88
- <span v-else>Connecting...</span>
88
+ <span v-else>Connecting... </span>
89
89
  </div>
90
90
  <BccAlert class="absolute top-0 w-[97%] mx-2 mt-11" icon closeButton context="danger"
91
91
  :open="chatInstance!.showAlert" @close="chatInstance!.showAlert = false">
@@ -1,10 +0,0 @@
1
- interface ChatToken {
2
- sub: string;
3
- token: string;
4
- time: number;
5
- authToken: string;
6
- }
7
- export declare const retrieveCcToken: () => ChatToken | null;
8
- export declare const clearToken: () => Promise<void>;
9
- export declare const getToken: (authToken: string, chatApiBaseUrl: string) => Promise<string | null>;
10
- export {};