app-tutor-ai-consumer 1.18.2 → 1.20.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 (43) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +1 -1
  3. package/public/assets/svg/error-dark.svg +27 -0
  4. package/public/assets/svg/error-light.svg +27 -0
  5. package/src/config/tests/handlers.ts +12 -0
  6. package/src/development-bootstrap.tsx +5 -2
  7. package/src/index.tsx +3 -0
  8. package/src/lib/components/button/button.tsx +105 -14
  9. package/src/lib/components/button/styles.module.css +9 -0
  10. package/src/lib/components/errors/generic/generic-error.tsx +58 -3
  11. package/src/lib/components/icons/arrow-up.svg +5 -0
  12. package/src/lib/components/icons/copy.svg +5 -0
  13. package/src/lib/components/icons/icon-names.d.ts +3 -0
  14. package/src/lib/components/icons/like.svg +5 -0
  15. package/src/modules/messages/components/message-actions/index.ts +2 -0
  16. package/src/modules/messages/components/message-actions/message-actions.tsx +49 -0
  17. package/src/modules/messages/components/message-item/message-item.tsx +21 -5
  18. package/src/modules/messages/components/message-item-error/message-item-error.tsx +16 -9
  19. package/src/modules/messages/components/message-skeleton/message-skeleton.tsx +1 -4
  20. package/src/modules/messages/components/messages-container/index.ts +2 -0
  21. package/src/modules/messages/components/messages-container/messages-container.tsx +91 -0
  22. package/src/modules/messages/components/messages-list/messages-list.tsx +9 -82
  23. package/src/modules/messages/constants.ts +5 -0
  24. package/src/modules/messages/events.ts +12 -4
  25. package/src/modules/messages/hooks/index.ts +1 -0
  26. package/src/modules/messages/hooks/use-all-messages/use-all-messages.tsx +1 -2
  27. package/src/modules/messages/hooks/use-infinite-get-messages/use-infinite-get-messages.spec.tsx +18 -19
  28. package/src/modules/messages/hooks/use-infinite-get-messages/use-infinite-get-messages.tsx +41 -35
  29. package/src/modules/messages/hooks/use-scroller/index.ts +2 -0
  30. package/src/modules/messages/hooks/use-scroller/use-scroller.tsx +50 -0
  31. package/src/modules/messages/hooks/use-send-text-message/use-send-text-message.tsx +31 -2
  32. package/src/modules/messages/hooks/use-subscribe-message-received-event/use-subscribe-message-received-event.tsx +47 -64
  33. package/src/modules/messages/store/index.ts +1 -0
  34. package/src/modules/messages/store/messages-max-count.atom.ts +13 -0
  35. package/src/modules/messages/utils/index.ts +2 -0
  36. package/src/modules/messages/utils/set-messages-cache/index.ts +1 -0
  37. package/src/modules/messages/utils/set-messages-cache/utils.ts +53 -0
  38. package/src/modules/widget/components/chat-page/chat-page.spec.tsx +23 -7
  39. package/src/modules/widget/components/chat-page/chat-page.tsx +70 -14
  40. package/src/modules/widget/components/greetings-card/greetings-card.tsx +1 -1
  41. package/src/modules/widget/components/header/header.tsx +6 -4
  42. package/src/modules/widget/components/starter-page/starter-page.spec.tsx +4 -1
  43. package/src/modules/widget/components/starter-page/starter-page.tsx +31 -5
@@ -0,0 +1,91 @@
1
+ import { forwardRef, useEffect } from 'react'
2
+ import clsx from 'clsx'
3
+ import type { MouseEventHandler, PropsWithChildren } from 'react'
4
+ import { createPortal } from 'react-dom'
5
+ import { useTranslation } from 'react-i18next'
6
+
7
+ import { Button, Icon } from '@/src/lib/components'
8
+ import { MessageSkeleton } from '@/src/modules/messages/components'
9
+ import { useSkeletonRef } from '@/src/modules/messages/hooks/use-skeleton-ref'
10
+ import {
11
+ ScrollToBottomButton,
12
+ usePageLayoutMainRefContext,
13
+ useWidgetLoadingAtom
14
+ } from '@/src/modules/widget'
15
+ import { useScroller } from '../../hooks'
16
+
17
+ const MessagesContainer = forwardRef<
18
+ HTMLDivElement,
19
+ PropsWithChildren<{
20
+ showButton?: boolean
21
+ loading?: boolean
22
+ handleShowMore?: () => Promise<void>
23
+ }>
24
+ >(({ children, handleShowMore, showButton = false, loading = false }, forwardedRef) => {
25
+ const { t } = useTranslation()
26
+ const skeletonRef = useSkeletonRef()
27
+ const [isLoadingNewMsg] = useWidgetLoadingAtom()
28
+ const mainLayoutRef = usePageLayoutMainRefContext()
29
+ const { scrollerRef, scrollToButtonRef, scrollToBottom, showScrollButton } =
30
+ useScroller(forwardedRef)
31
+
32
+ useEffect(() => {
33
+ scrollToBottom()
34
+ }, [scrollToBottom])
35
+
36
+ const handleClickShowMore: MouseEventHandler<HTMLButtonElement> = (e) => {
37
+ const scroller = scrollerRef?.current
38
+ const heightBeforeRender = Number(e?.currentTarget?.scrollHeight)
39
+
40
+ void handleShowMore?.().then(() => {
41
+ if (scroller && !isNaN(heightBeforeRender)) {
42
+ setTimeout(
43
+ () =>
44
+ scroller.scrollTo({
45
+ top: heightBeforeRender + 10,
46
+ behavior: 'smooth'
47
+ }),
48
+ 180
49
+ )
50
+ }
51
+ })
52
+ }
53
+
54
+ return (
55
+ <div ref={scrollerRef} className='mx-2 my-4 flex h-full flex-col gap-2 overflow-auto px-4'>
56
+ <div className='mb-auto flex-1 self-center'>
57
+ <Button
58
+ className='max-w-max rounded-full border border-neutral-300 bg-neutral-200 px-2 py-1 text-xs/normal tracking-wide text-neutral-900'
59
+ onClick={handleClickShowMore}
60
+ loading={loading}
61
+ show={showButton}>
62
+ <Icon name='arrow-up' className='h-4 w-3' aria-hidden />
63
+ <span className='text-nowrap'>{t('general.buttons.show_more')}</span>
64
+ </Button>
65
+ </div>
66
+ {children}
67
+
68
+ {mainLayoutRef.current &&
69
+ createPortal(
70
+ <ScrollToBottomButton
71
+ ref={scrollToButtonRef}
72
+ show={showScrollButton}
73
+ onClick={scrollToBottom}
74
+ />,
75
+ mainLayoutRef.current
76
+ )}
77
+ <div
78
+ className={clsx({
79
+ 'pointer-events-none h-0 overflow-hidden opacity-0': !isLoadingNewMsg,
80
+ 'mt-2 pb-4': isLoadingNewMsg
81
+ })}
82
+ ref={skeletonRef}>
83
+ <MessageSkeleton />
84
+ </div>
85
+ </div>
86
+ )
87
+ })
88
+
89
+ MessagesContainer.displayName = 'MessagesContainer'
90
+
91
+ export default MessagesContainer
@@ -1,89 +1,16 @@
1
- import { lazy, useCallback, useRef } from 'react'
2
- import clsx from 'clsx'
3
- import { createPortal } from 'react-dom'
1
+ import type { ParsedMessage } from '@/src/modules/messages'
2
+ import { MessageItem } from '@/src/modules/messages/components'
4
3
 
5
- import { usePageLayoutMainRefContext, useWidgetLoadingAtomValue } from '@/src/modules/widget'
6
- import { useAllMessages, useManageScroll } from '../../hooks'
7
- import { useSkeletonRef } from '../../hooks/use-skeleton-ref'
8
- import { MessageItem } from '../message-item'
9
- import { MessageSkeleton } from '../message-skeleton'
4
+ function MessagesList({ messagesMap }: { messagesMap: Map<string, ParsedMessage[]> }) {
5
+ if (!(messagesMap.size > 0)) return null
10
6
 
11
- const MessageItemError = lazy(() => import('../message-item-error/message-item-error'))
12
-
13
- const MessageItemLoading = lazy(() => import('../message-item-loading/message-item-loading'))
14
-
15
- const MessageItemEndOfScroll = lazy(
16
- () => import('../message-item-end-of-scroll/message-item-end-of-scroll')
17
- )
18
-
19
- const ScrollToBottomButton = lazy(
20
- () => import('@/src/modules/widget/components/scroll-to-bottom-button/scroll-to-bottom-button')
21
- )
22
-
23
- function MessagesList() {
24
- const scrollerRef = useRef<HTMLDivElement>(null)
25
- const scrollToButtonRef = useRef<HTMLButtonElement>(null)
26
- const { allMessages, messagesQuery } = useAllMessages()
27
- const widgetIsLoading = useWidgetLoadingAtomValue()
28
- const skeletonRef = useSkeletonRef()
29
- const { showScrollButton } = useManageScroll(scrollerRef)
30
- const mainLayoutRef = usePageLayoutMainRefContext()
31
-
32
- const scrollToBottom = useCallback(() => {
33
- const { current: scroller } = scrollerRef
34
-
35
- if (!scroller) return
36
-
37
- scroller.scrollTo({
38
- top: scroller.scrollHeight,
39
- behavior: 'smooth'
40
- })
41
- }, [])
42
-
43
- return (
44
- <div ref={scrollerRef} className='mx-2 my-4 flex flex-col gap-2 overflow-auto px-4'>
45
- <MessageItemLoading show={messagesQuery.isFetching} />
46
-
47
- <MessageItemEndOfScroll
48
- show={!messagesQuery.isFetching && !messagesQuery.hasNextPage && allMessages.length > 0}
49
- />
50
- {mainLayoutRef.current &&
51
- createPortal(
52
- <ScrollToBottomButton
53
- ref={scrollToButtonRef}
54
- show={showScrollButton}
55
- onClick={scrollToBottom}
56
- />,
57
- mainLayoutRef.current
58
- )}
59
-
60
- {allMessages?.map(([publishingDate, messages], i) => (
61
- <div key={i} className='flex flex-1 flex-col justify-center gap-6'>
62
- <span className='self-center rounded-full border border-neutral-300 bg-neutral-200 px-4 py-2 text-xs capitalize text-neutral-900'>
63
- {publishingDate}
64
- </span>
65
- {messages.map((msg, k) => (
66
- <MessageItem key={`${msg.id}-${k}`} message={msg} />
67
- ))}
68
- </div>
7
+ return Array.from(messagesMap).map(([, messages], i) => (
8
+ <div key={i} className='flex flex-1 flex-col justify-center gap-6'>
9
+ {messages.map((msg, k) => (
10
+ <MessageItem key={`${msg.id}-${k}`} message={msg} />
69
11
  ))}
70
-
71
- <MessageItemError
72
- show={Boolean(messagesQuery.error)}
73
- message={`❌ Error loading messages: ${messagesQuery.error?.message ?? ''}`}
74
- retry={() => void messagesQuery.refetch()}
75
- />
76
-
77
- <div
78
- className={clsx({
79
- 'pointer-events-none h-0 overflow-hidden opacity-0': !widgetIsLoading,
80
- 'pb-4': widgetIsLoading
81
- })}
82
- ref={skeletonRef}>
83
- <MessageSkeleton />
84
- </div>
85
12
  </div>
86
- )
13
+ ))
87
14
  }
88
15
 
89
16
  export default MessagesList
@@ -1,2 +1,7 @@
1
1
  export const MSG_MAX_COUNT = 20
2
2
  export const MSG_MAX_PAGES = 20
3
+
4
+ export const MessagesEndpoints = {
5
+ getAll: (conversationId: string) =>
6
+ `${process.env.API_CONVERSATION_URL}/v1/conversations/${conversationId}/messages`
7
+ }
@@ -1,15 +1,23 @@
1
- import type { ICustomEvent } from '@/src/types'
2
-
3
1
  import type { SubmitQuestionEventDetail } from './types'
4
2
 
5
3
  export const MessagesEventTypes = {
6
4
  SUBMIT_QUESTION: 'c3po-chat:questionSubmitted'
7
5
  } as const
8
6
 
9
- const MessagesEventsList: Array<ICustomEvent<typeof MessagesEventTypes>> = [
7
+ const MessagesEventsList = [
10
8
  {
11
9
  name: MessagesEventTypes.SUBMIT_QUESTION,
12
- handler: () => () => undefined,
10
+ handler: (callback: () => void) => {
11
+ const listener = () => {
12
+ callback()
13
+ }
14
+
15
+ window.addEventListener(MessagesEventTypes.SUBMIT_QUESTION, listener)
16
+
17
+ return () => {
18
+ window.removeEventListener(MessagesEventTypes.SUBMIT_QUESTION, listener)
19
+ }
20
+ },
13
21
  dispatch: () => {
14
22
  const event: CustomEventInit<SubmitQuestionEventDetail> = {
15
23
  detail: {
@@ -2,5 +2,6 @@ export * from './use-all-messages'
2
2
  export * from './use-fetch-messages'
3
3
  export * from './use-infinite-get-messages'
4
4
  export * from './use-manage-scroll'
5
+ export * from './use-scroller'
5
6
  export * from './use-send-text-message'
6
7
  export * from './use-subscribe-message-received-event'
@@ -10,8 +10,7 @@ function useAllMessages() {
10
10
 
11
11
  const messagesQuery = useInfiniteGetMessages({
12
12
  conversationId: settings?.conversationId ?? '',
13
- profileId: profileQuery.data?.id ?? '',
14
- enabled: Boolean(settings?.conversationId && profileQuery.data?.id)
13
+ profileId: profileQuery.data?.id ?? ''
15
14
  })
16
15
 
17
16
  const allMessages = useMemo(
@@ -1,9 +1,6 @@
1
- import type MessageService from '@hotmart/sparkie/dist/MessageService'
2
-
3
- import { act, renderHook, waitFor } from '@/src/config/tests'
4
- import { SparkieService } from '@/src/modules/sparkie'
5
- import { SparkieMessageServiceMock } from '@/src/modules/sparkie/__tests__/sparkie.mock'
1
+ import { act, MockRequest, renderHook, waitFor } from '@/src/config/tests'
6
2
  import IMessageWithSenderDataMock from '../../__tests__/imessage-with-sender-data.mock'
3
+ import { MessagesEndpoints, MSG_MAX_COUNT } from '../../constants'
7
4
  import type { IMessageWithSenderData } from '../../types'
8
5
 
9
6
  import useInfiniteGetMessages from './use-infinite-get-messages'
@@ -11,21 +8,16 @@ import useInfiniteGetMessages from './use-infinite-get-messages'
11
8
  describe('useInfiniteGetMessages', () => {
12
9
  const mockConversationId = 'conversation-123'
13
10
  const mockProfileId = 'profile-456'
14
- const mockEnabled = true
11
+ const limitMock = 2
15
12
 
16
13
  beforeEach(() => {
17
14
  vi.clearAllMocks()
18
- vi.spyOn(SparkieService, 'getMessageService').mockResolvedValue(
19
- SparkieMessageServiceMock as unknown as MessageService
20
- )
21
- SparkieMessageServiceMock.getAll.mockClear()
22
15
  })
23
16
 
24
17
  const createHook = (
25
18
  props = {
26
19
  conversationId: mockConversationId,
27
- profileId: mockProfileId,
28
- enabled: mockEnabled
20
+ profileId: mockProfileId
29
21
  }
30
22
  ) => renderHook(() => useInfiniteGetMessages(props))
31
23
 
@@ -34,22 +26,28 @@ describe('useInfiniteGetMessages', () => {
34
26
 
35
27
  await waitFor(() => expect(result.current.data).toBeDefined())
36
28
 
37
- expect(result.current.data?.size).toBe(10)
29
+ expect(result.current.data?.size).toBe(MSG_MAX_COUNT)
38
30
  })
39
31
 
40
32
  it('should be able to fetch next pages', async () => {
41
- SparkieMessageServiceMock.getAll.mockRestore()
42
- SparkieMessageServiceMock.getAll.mockReturnValueOnce(
43
- new IMessageWithSenderDataMock().getMany(2) as IMessageWithSenderData[]
33
+ const allMessages = limitMock + 1
34
+
35
+ MockRequest.mock(
36
+ MessagesEndpoints.getAll(mockConversationId),
37
+ new IMessageWithSenderDataMock().getMany(allMessages) as IMessageWithSenderData[]
44
38
  )
45
39
 
46
- const { result } = createHook()
40
+ const { result } = createHook({
41
+ conversationId: mockConversationId,
42
+ profileId: mockProfileId,
43
+ limit: limitMock
44
+ } as never)
47
45
 
48
46
  await waitFor(() => {
49
47
  expect(result.current.isSuccess).toBe(true)
50
48
  })
51
49
 
52
- expect(result.current.data?.size).toBe(10)
50
+ expect(result.current.data?.size).toBe(allMessages)
53
51
 
54
52
  act(() => {
55
53
  void result.current.fetchNextPage()
@@ -59,7 +57,8 @@ describe('useInfiniteGetMessages', () => {
59
57
  expect(result.current.fetchStatus).toBe('idle')
60
58
  })
61
59
 
62
- expect(result.current.data?.size).toBe(2)
60
+ expect(result.current.data?.size).toBe(allMessages)
61
+
63
62
  expect(result.current.hasNextPage).toBe(false)
64
63
  })
65
64
  })
@@ -2,31 +2,38 @@ import type { UndefinedInitialDataInfiniteOptions } from '@tanstack/react-query'
2
2
  import { useInfiniteQuery } from '@tanstack/react-query'
3
3
 
4
4
  import { formatTime } from '@/src/config/dayjs'
5
- import { MessagesService } from '../..'
6
- import { MSG_MAX_COUNT } from '../../constants'
7
- import type { FetchMessagesResponse, ParsedMessage } from '../../types'
5
+ import { api } from '@/src/config/request'
6
+ import { MessagesEndpoints, MSG_MAX_COUNT } from '../../constants'
7
+ import type { FetchMessagesResponse, IMessageWithSenderData, ParsedMessage } from '../../types'
8
8
  import { messagesParser } from '../../utils'
9
- import type { UseFetchMessagesProps } from '../use-fetch-messages'
10
9
 
11
- export type UseGetMessagesQueryProps = {
12
- conversationId: string
13
- profileId: string
14
- enabled?: boolean
10
+ export type GetAllMessagesQueryProps = {
11
+ conversationId?: string
12
+ profileId?: string
13
+ limit?: number
15
14
  }
16
15
 
17
- export const getMessagesInfiniteQuery = ({
16
+ export const getAllMessagesQuery = ({
18
17
  conversationId,
19
18
  profileId,
20
- enabled
21
- }: UseGetMessagesQueryProps) =>
19
+ limit = MSG_MAX_COUNT
20
+ }: GetAllMessagesQueryProps) =>
22
21
  ({
23
- queryKey: ['sparkie:messageService:getAll', conversationId, profileId],
22
+ queryKey: ['getAllMessagesWithoutSparkie', limit, conversationId],
24
23
  queryFn: async ({ pageParam }: { pageParam?: number }) => {
25
- const messages = await MessagesService.getMessages({ conversationId, before: pageParam })
24
+ const { data: messages } = await api.get<IMessageWithSenderData[]>(
25
+ MessagesEndpoints.getAll(conversationId as string),
26
+ {
27
+ params: {
28
+ before: pageParam,
29
+ limit
30
+ }
31
+ }
32
+ )
26
33
 
27
34
  return {
28
35
  messages,
29
- hasMore: messages.length === MSG_MAX_COUNT
36
+ hasMore: messages.length === limit
30
37
  } as FetchMessagesResponse
31
38
  },
32
39
  initialPageParam: undefined,
@@ -37,26 +44,29 @@ export const getMessagesInfiniteQuery = ({
37
44
 
38
45
  return minSentAt
39
46
  },
40
- enabled,
47
+ enabled: Boolean(conversationId && profileId) && limit > 0,
41
48
  select(data) {
42
49
  const messages = data.pages?.flatMap((page) => page.messages) ?? []
43
- return messagesParser({ messages, profileId }).reduce((messagesMap, currentMessage) => {
44
- const timestamp = formatTime(currentMessage.timestamp, true)
50
+ return messagesParser({ messages, profileId: profileId as string }).reduce(
51
+ (messagesMap, currentMessage) => {
52
+ const timestamp = formatTime(currentMessage.timestamp, true)
45
53
 
46
- if (!messagesMap.has(timestamp)) {
47
- messagesMap.set(timestamp, [currentMessage])
48
- return messagesMap
49
- }
54
+ if (!messagesMap.has(timestamp)) {
55
+ messagesMap.set(timestamp, [currentMessage])
56
+ return messagesMap
57
+ }
50
58
 
51
- const existingTimestampValues = Array.from(messagesMap.get(timestamp) ?? [])
59
+ const existingTimestampValues = Array.from(messagesMap.get(timestamp) ?? [])
52
60
 
53
- messagesMap.set(
54
- timestamp,
55
- [...existingTimestampValues, currentMessage].sort((a, b) => a.timestamp - b.timestamp)
56
- )
61
+ messagesMap.set(
62
+ timestamp,
63
+ [...existingTimestampValues, currentMessage].sort((a, b) => a.timestamp - b.timestamp)
64
+ )
57
65
 
58
- return messagesMap
59
- }, new Map<string, ParsedMessage[]>())
66
+ return messagesMap
67
+ },
68
+ new Map<string, ParsedMessage[]>()
69
+ )
60
70
  }
61
71
  }) as UndefinedInitialDataInfiniteOptions<
62
72
  FetchMessagesResponse,
@@ -64,15 +74,11 @@ export const getMessagesInfiniteQuery = ({
64
74
  Map<string, ParsedMessage[]>
65
75
  >
66
76
 
67
- function useInfiniteGetMessages({
68
- conversationId,
69
- profileId,
70
- enabled = false
71
- }: Omit<UseFetchMessagesProps, 'currentMessages' | 'loadFirstPage'>) {
72
- const query = getMessagesInfiniteQuery({
77
+ function useInfiniteGetMessages({ conversationId, profileId, limit }: GetAllMessagesQueryProps) {
78
+ const query = getAllMessagesQuery({
73
79
  conversationId,
74
80
  profileId,
75
- enabled
81
+ limit
76
82
  })
77
83
 
78
84
  return useInfiniteQuery(query)
@@ -0,0 +1,2 @@
1
+ export * from './use-scroller'
2
+ export { default as useScroller } from './use-scroller'
@@ -0,0 +1,50 @@
1
+ import { useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react'
2
+ import type { ForwardedRef } from 'react'
3
+
4
+ const threshold = 50
5
+
6
+ function useScroller(forwardedRef: ForwardedRef<HTMLDivElement>) {
7
+ const scrollerRef = useRef<HTMLDivElement>(null)
8
+ const scrollToButtonRef = useRef<HTMLButtonElement>(null)
9
+ const [showScrollButton, setShowScrollButton] = useState(false)
10
+
11
+ useImperativeHandle(forwardedRef, () => scrollerRef?.current as HTMLDivElement)
12
+
13
+ useEffect(() => {
14
+ const { current: scroller } = scrollerRef
15
+
16
+ if (!scroller) return
17
+
18
+ const handleShowBtn = () => {
19
+ const scrollPosition =
20
+ Number(scroller.scrollHeight) - Number(scroller.scrollTop) - Number(scroller.clientHeight)
21
+
22
+ setShowScrollButton(scrollPosition > threshold)
23
+ }
24
+
25
+ scroller.addEventListener('scroll', handleShowBtn)
26
+
27
+ return () => {
28
+ scroller.removeEventListener('scroll', handleShowBtn)
29
+ }
30
+ }, [])
31
+
32
+ const scrollToBottom = useCallback(() => {
33
+ const { current: scroller } = scrollerRef
34
+ if (!scroller) return
35
+
36
+ scroller.scrollTo({
37
+ top: scroller.scrollHeight,
38
+ behavior: 'smooth'
39
+ })
40
+ }, [])
41
+
42
+ return {
43
+ scrollerRef,
44
+ scrollToButtonRef,
45
+ scrollToBottom,
46
+ showScrollButton
47
+ }
48
+ }
49
+
50
+ export default useScroller
@@ -1,5 +1,5 @@
1
1
  import { useMemo } from 'react'
2
- import { useMutation } from '@tanstack/react-query'
2
+ import { useMutation, useQueryClient } from '@tanstack/react-query'
3
3
  import { UAParser } from 'ua-parser-js'
4
4
  import { v4 } from 'uuid'
5
5
 
@@ -7,13 +7,28 @@ import { MessagesService } from '@/src/modules/messages'
7
7
  import { useGetProfile } from '@/src/modules/profile'
8
8
  import { useWidgetLoadingAtom, useWidgetSettingsAtomValue } from '@/src/modules/widget'
9
9
  import { MessagesEvents } from '../../events'
10
+ import { useMessagesMaxCount } from '../../store'
11
+ import { setMessagesCache } from '../../utils'
12
+ import { getAllMessagesQuery } from '../use-infinite-get-messages'
10
13
 
11
14
  function useSendTextMessage() {
12
15
  const settings = useWidgetSettingsAtomValue()
13
16
  const profileQuery = useGetProfile()
14
17
  const [, setWidgetLoading] = useWidgetLoadingAtom()
18
+ const queryClient = useQueryClient()
19
+ const limit = useMessagesMaxCount()
15
20
 
16
21
  const userId = useMemo(() => profileQuery.data?.userId?.toString(), [profileQuery.data?.userId])
22
+ const profileId = useMemo(() => profileQuery.data?.id?.toString(), [profileQuery.data?.id])
23
+ const messagesQueryConfig = useMemo(
24
+ () =>
25
+ getAllMessagesQuery({
26
+ conversationId: settings?.conversationId,
27
+ profileId,
28
+ limit
29
+ }),
30
+ [limit, profileId, settings?.conversationId]
31
+ )
17
32
 
18
33
  return useMutation({
19
34
  mutationFn(message: string) {
@@ -64,9 +79,23 @@ function useSendTextMessage() {
64
79
  }
65
80
  })
66
81
  },
67
- onMutate: () => {
82
+ onMutate: (variables) => {
83
+ setMessagesCache({
84
+ queryKey: messagesQueryConfig.queryKey,
85
+ queryClient,
86
+ sending: true,
87
+ data: {
88
+ content: {
89
+ type: 'text/plain',
90
+ text: variables
91
+ }
92
+ }
93
+ })()
68
94
  setWidgetLoading(true)
69
95
  MessagesEvents.get('c3po-chat:questionSubmitted')?.dispatch()
96
+ },
97
+ onSuccess(data) {
98
+ setMessagesCache({ queryKey: messagesQueryConfig.queryKey, queryClient, data })()
70
99
  }
71
100
  })
72
101
  }