@webitel/ui-chats 0.1.26 → 0.1.28

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webitel/ui-chats",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "description": "Reusable Webitel Frontend Code for Chats UI",
5
5
  "workspaces": [
6
6
  "../../",
@@ -2,23 +2,24 @@
2
2
  <div class="chat-observer">
3
3
  <wt-intersection-observer
4
4
  :can-load-more="props.next"
5
- :loading="props.isLoading"
5
+ :loading="props.loading"
6
6
  @next="emit(ChatAction.LoadNextMessages)"
7
7
  />
8
8
  </div>
9
9
  </template>
10
10
 
11
11
  <script setup lang="ts">
12
+ import { WtIntersectionObserver } from '@webitel/ui-sdk/components';
12
13
  import { ChatAction } from '../../chat-footer/modules/user-input/enums/ChatAction.enum';
13
14
 
14
15
  const props = withDefaults(
15
16
  defineProps<{
16
17
  next?: boolean;
17
- isLoading?: boolean;
18
+ loading?: boolean;
18
19
  }>(),
19
20
  {
20
21
  next: false,
21
- isLoading: false,
22
+ loading: false,
22
23
  },
23
24
  );
24
25
 
@@ -34,6 +35,8 @@ const emit = defineEmits<(e: typeof ChatAction.LoadNextMessages) => void>();
34
35
  /* @author ye.pohranichna */
35
36
  /* to place observer at the bottom of observer wrapper (closer to messages) */
36
37
  display: flex;
37
- align-items: flex-end;
38
+ flex-direction: column;
39
+ align-items: center;
40
+ justify-content: flex-end;
38
41
  }
39
42
  </style>
@@ -13,7 +13,7 @@
13
13
  v-if="props.next"
14
14
  :next="props.next"
15
15
  :loading="props.isLoading"
16
- @[ChatAction.LoadNextMessages]="emit(ChatAction.LoadNextMessages)"
16
+ @[ChatAction.LoadNextMessages]="handleLoadNextMessages"
17
17
  />
18
18
  <chat-message
19
19
  v-for="(message, index) of props.messages"
@@ -94,13 +94,19 @@ const {
94
94
  showScrollToBottomBtn,
95
95
  newUnseenMessagesCount,
96
96
  scrollToBottom,
97
+ loadNextMessages,
97
98
  handleChatScroll,
98
99
  handleChatResize,
99
100
  } = useChatScroll(
100
101
  messagesContainer,
101
102
  computed(() => props.messages), // props values reactivity https://stackoverflow.com/questions/72408463/use-props-in-composables-vue3 @author ye.pohranichna
103
+ computed(() => props.isLoading),
102
104
  );
103
105
 
106
+ function handleLoadNextMessages() {
107
+ loadNextMessages(props.next, () => emit(ChatAction.LoadNextMessages));
108
+ }
109
+
104
110
  function focusOnInput() {
105
111
  uiChatsEmitter?.on('focusOnTextField', focus);
106
112
  }
@@ -1,11 +1,19 @@
1
1
  import { useScroll } from '@vueuse/core';
2
- import { type ComputedRef, computed, type Ref, ref, watch } from 'vue';
2
+ import {
3
+ type ComputedRef,
4
+ computed,
5
+ nextTick,
6
+ type Ref,
7
+ ref,
8
+ watch,
9
+ } from 'vue';
3
10
 
4
11
  import type { ChatMessageType } from '../types/ChatMessage.types';
5
12
 
6
13
  export const useChatScroll = (
7
14
  element: Ref<HTMLElement | null> = null,
8
15
  messages: Ref<ChatMessageType[]> | ComputedRef<ChatMessageType[]>,
16
+ isLoading?: Ref<boolean> | ComputedRef<boolean>,
9
17
  ) => {
10
18
  /* @author ye.pohranichna
11
19
  why 136px? because: https://webitel.atlassian.net/browse/WTEL-7136 */
@@ -17,6 +25,8 @@ export const useChatScroll = (
17
25
  /* @author ye.pohranichna
18
26
  the distance where the scrollToBottomBtn must be shown/hide. */
19
27
  const threshold = ref<number>(defaultThreshold);
28
+ const isLoadingNextMessages = ref<boolean>(false);
29
+ const lastVisibleMessageEl = ref<HTMLElement | null>(null);
20
30
 
21
31
  const isLastMessageIsMy = computed<boolean>(
22
32
  () => lastMessage.value?.member?.self,
@@ -80,6 +90,25 @@ export const useChatScroll = (
80
90
  showScrollToBottomBtn.value = false;
81
91
  };
82
92
 
93
+ const getTopMessageEl = () => {
94
+ // help to fix chat viewing position when new messages was loaded
95
+ if (!element.value?.children) return;
96
+ lastVisibleMessageEl.value =
97
+ element.value?.getElementsByClassName('chat-message')[0] ?? null;
98
+ };
99
+
100
+ const loadNextMessages = (
101
+ canLoadMore: boolean | undefined,
102
+ onLoadNextMessages: () => void,
103
+ ) => {
104
+ if (isLoadingNextMessages.value || isLoading?.value || !canLoadMore) return;
105
+
106
+ isLoadingNextMessages.value = true;
107
+ getTopMessageEl();
108
+
109
+ onLoadNextMessages();
110
+ };
111
+
83
112
  watch(
84
113
  () => messages.value?.length,
85
114
  (newValue, oldValue) => {
@@ -91,10 +120,27 @@ export const useChatScroll = (
91
120
  },
92
121
  );
93
122
 
123
+ watch(
124
+ () => isLoading?.value,
125
+ async (loading) => {
126
+ if (loading || !isLoadingNextMessages.value) return;
127
+
128
+ await nextTick();
129
+
130
+ element.value?.scrollTo({
131
+ top: lastVisibleMessageEl.value?.offsetTop,
132
+ behavior: 'auto',
133
+ });
134
+
135
+ isLoadingNextMessages.value = false;
136
+ },
137
+ );
138
+
94
139
  return {
95
140
  showScrollToBottomBtn,
96
141
  newUnseenMessagesCount,
97
142
  scrollToBottom,
143
+ loadNextMessages,
98
144
  handleChatScroll,
99
145
  handleChatResize,
100
146
  };
@@ -108,7 +108,9 @@ const isSelfSide = computed<boolean>(
108
108
  const isBot = computed<boolean>(() => props.message.member?.type === 'bot');
109
109
 
110
110
  const getClientUsername = computed<string>(() => {
111
- return isSelfSide.value ? props?.agentName : props.username;
111
+ if (isSelfSide.value) return isSelfSide?.value;
112
+
113
+ return props.username || props.message.member?.name;
112
114
  });
113
115
 
114
116
  function handlePlayerInitialize(player) {
@@ -1,6 +1,6 @@
1
1
  type __VLS_Props = {
2
2
  next?: boolean;
3
- isLoading?: boolean;
3
+ loading?: boolean;
4
4
  };
5
5
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
6
6
  loadNextMessages: () => any;
@@ -8,7 +8,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
8
8
  onLoadNextMessages?: () => any;
9
9
  }>, {
10
10
  next: boolean;
11
- isLoading: boolean;
11
+ loading: boolean;
12
12
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
13
13
  declare const _default: typeof __VLS_export;
14
14
  export default _default;
@@ -1,9 +1,10 @@
1
1
  import { type ComputedRef, type Ref } from 'vue';
2
2
  import type { ChatMessageType } from '../types/ChatMessage.types';
3
- export declare const useChatScroll: (element: Ref<HTMLElement | null>, messages: Ref<ChatMessageType[]> | ComputedRef<ChatMessageType[]>) => {
3
+ export declare const useChatScroll: (element: Ref<HTMLElement | null>, messages: Ref<ChatMessageType[]> | ComputedRef<ChatMessageType[]>, isLoading?: Ref<boolean> | ComputedRef<boolean>) => {
4
4
  showScrollToBottomBtn: Ref<boolean, boolean>;
5
5
  newUnseenMessagesCount: Ref<number, number>;
6
6
  scrollToBottom: (behavior?: ScrollBehavior) => void;
7
+ loadNextMessages: (canLoadMore: boolean | undefined, onLoadNextMessages: () => void) => void;
7
8
  handleChatScroll: () => void;
8
9
  handleChatResize: () => void;
9
10
  };