app-tutor-ai-consumer 1.18.2 → 1.19.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 (40) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/package.json +1 -1
  3. package/src/config/tests/handlers.ts +12 -0
  4. package/src/development-bootstrap.tsx +5 -2
  5. package/src/index.tsx +3 -0
  6. package/src/lib/components/button/button.tsx +105 -14
  7. package/src/lib/components/button/styles.module.css +9 -0
  8. package/src/lib/components/icons/arrow-up.svg +5 -0
  9. package/src/lib/components/icons/copy.svg +5 -0
  10. package/src/lib/components/icons/icon-names.d.ts +3 -0
  11. package/src/lib/components/icons/like.svg +5 -0
  12. package/src/modules/messages/components/message-actions/index.ts +2 -0
  13. package/src/modules/messages/components/message-actions/message-actions.tsx +49 -0
  14. package/src/modules/messages/components/message-item/message-item.tsx +21 -5
  15. package/src/modules/messages/components/message-item-error/message-item-error.tsx +16 -9
  16. package/src/modules/messages/components/message-skeleton/message-skeleton.tsx +1 -4
  17. package/src/modules/messages/components/messages-container/index.ts +2 -0
  18. package/src/modules/messages/components/messages-container/messages-container.tsx +91 -0
  19. package/src/modules/messages/components/messages-list/messages-list.tsx +9 -82
  20. package/src/modules/messages/constants.ts +5 -0
  21. package/src/modules/messages/events.ts +12 -4
  22. package/src/modules/messages/hooks/index.ts +1 -0
  23. package/src/modules/messages/hooks/use-all-messages/use-all-messages.tsx +1 -2
  24. package/src/modules/messages/hooks/use-infinite-get-messages/use-infinite-get-messages.spec.tsx +18 -19
  25. package/src/modules/messages/hooks/use-infinite-get-messages/use-infinite-get-messages.tsx +41 -35
  26. package/src/modules/messages/hooks/use-scroller/index.ts +2 -0
  27. package/src/modules/messages/hooks/use-scroller/use-scroller.tsx +50 -0
  28. package/src/modules/messages/hooks/use-send-text-message/use-send-text-message.tsx +31 -2
  29. package/src/modules/messages/hooks/use-subscribe-message-received-event/use-subscribe-message-received-event.tsx +47 -64
  30. package/src/modules/messages/store/index.ts +1 -0
  31. package/src/modules/messages/store/messages-max-count.atom.ts +13 -0
  32. package/src/modules/messages/utils/index.ts +2 -0
  33. package/src/modules/messages/utils/set-messages-cache/index.ts +1 -0
  34. package/src/modules/messages/utils/set-messages-cache/utils.ts +53 -0
  35. package/src/modules/widget/components/chat-page/chat-page.spec.tsx +23 -7
  36. package/src/modules/widget/components/chat-page/chat-page.tsx +70 -14
  37. package/src/modules/widget/components/greetings-card/greetings-card.tsx +1 -1
  38. package/src/modules/widget/components/header/header.tsx +6 -4
  39. package/src/modules/widget/components/starter-page/starter-page.spec.tsx +4 -1
  40. package/src/modules/widget/components/starter-page/starter-page.tsx +31 -5
@@ -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
  }
@@ -1,4 +1,4 @@
1
- import { useCallback, useEffect, useMemo, useRef } from 'react'
1
+ import { useCallback, useEffect, useMemo } from 'react'
2
2
  import type { InfiniteData } from '@tanstack/react-query'
3
3
  import { useQueryClient } from '@tanstack/react-query'
4
4
  import { produce } from 'immer'
@@ -7,63 +7,52 @@ import { useUpdateCursor } from '@/src/modules/cursor/hooks'
7
7
  import { useGetProfile } from '@/src/modules/profile'
8
8
  import { SparkieService } from '@/src/modules/sparkie'
9
9
  import { useWidgetLoadingAtom, useWidgetSettingsAtom } from '@/src/modules/widget'
10
- import { useUnreadMessagesSetAtom } from '../../store'
10
+ import { useMessagesMaxCount, useUnreadMessagesSetAtom } from '../../store'
11
11
  import type { FetchMessagesResponse, IMessageWithSenderData } from '../../types'
12
- import { getMessagesInfiniteQuery } from '../use-infinite-get-messages'
12
+ import { getAllMessagesQuery } from '../use-infinite-get-messages'
13
13
 
14
14
  const useSubscribeMessageReceivedEvent = () => {
15
15
  const [settings] = useWidgetSettingsAtom()
16
- const profileQuery = useGetProfile()
17
- const queryClient = useQueryClient()
18
16
  const [, setWidgetLoading] = useWidgetLoadingAtom()
19
17
  const [, addUnreadMessagesToSet] = useUnreadMessagesSetAtom()
18
+ const profileQuery = useGetProfile()
19
+ const queryClient = useQueryClient()
20
20
  const useUpdateCursorMutation = useUpdateCursor()
21
- const idsList = useRef<Set<string>>(new Set())
21
+ const limit = useMessagesMaxCount()
22
22
 
23
23
  const conversationId = useMemo(() => String(settings?.conversationId), [settings?.conversationId])
24
24
  const profileId = useMemo(() => String(profileQuery?.data?.id), [profileQuery?.data?.id])
25
-
26
- const query = getMessagesInfiniteQuery({
27
- conversationId,
28
- profileId,
29
- enabled: true
30
- })
31
-
32
- const execute = useCallback(() => {
33
- const messageReceived = (data: IMessageWithSenderData) => {
34
- if (idsList.current.has(data.id)) return
35
-
36
- const queryKey = query.queryKey
37
-
38
- if (!queryKey || !queryClient) return
39
-
40
- const previousData = queryClient.getQueryData<InfiniteData<FetchMessagesResponse>>(
41
- query.queryKey
25
+ const messagesQueryConfig = useMemo(
26
+ () =>
27
+ getAllMessagesQuery({
28
+ conversationId,
29
+ profileId,
30
+ limit
31
+ }),
32
+ [conversationId, limit, profileId]
33
+ )
34
+
35
+ const messageReceived = useCallback(
36
+ (data: IMessageWithSenderData) => {
37
+ const queryData = queryClient.getQueryData<InfiniteData<FetchMessagesResponse>>(
38
+ messagesQueryConfig.queryKey
42
39
  )
43
40
 
44
- if (!previousData) return
45
-
46
- const pages = previousData?.pages
47
-
48
- if (!(Number(pages?.length) > 0)) return
49
-
50
- const currentMsgIds = new Set(pages.flatMap((page) => page.messages.map((msg) => msg.id)))
51
-
52
- if (currentMsgIds.has(data.id)) return
53
-
54
- queryClient.setQueryData<InfiniteData<FetchMessagesResponse>>(queryKey, (prev) => {
55
- if (!prev) return prev
56
-
57
- const result = produce(prev, (draft) => {
58
- if (!(Number(draft?.pages?.at?.(-1)?.messages?.length) > 0)) return draft
59
-
60
- draft.pages.at(-1)?.messages.push(data)
41
+ const idsList = new Set(
42
+ queryData?.pages.flatMap((items) => items.messages.map((msg) => msg.id))
43
+ )
61
44
 
62
- return draft
63
- })
45
+ if (idsList.has(data.id)) return
64
46
 
65
- return result
66
- })
47
+ queryClient.setQueryData<InfiniteData<FetchMessagesResponse>>(
48
+ messagesQueryConfig.queryKey,
49
+ (oldData) => {
50
+ return produce(oldData, (draft) => {
51
+ draft?.pages.at(-1)?.messages?.push(data)
52
+ return draft
53
+ })
54
+ }
55
+ )
67
56
 
68
57
  const isMine = data.contactId === profileId
69
58
 
@@ -74,30 +63,24 @@ const useSubscribeMessageReceivedEvent = () => {
74
63
  // The cursor should update only with my messages
75
64
  useUpdateCursorMutation.mutate(data.conversationId)
76
65
  }
77
- idsList.current.add(data.id)
78
- }
66
+ },
67
+ [
68
+ addUnreadMessagesToSet,
69
+ messagesQueryConfig.queryKey,
70
+ profileId,
71
+ queryClient,
72
+ setWidgetLoading,
73
+ useUpdateCursorMutation
74
+ ]
75
+ )
79
76
 
80
- SparkieService.subscribeEvents({
81
- messageReceived
82
- })
77
+ useEffect(() => {
78
+ SparkieService.subscribeEvents({ messageReceived })
83
79
 
84
80
  return () => {
85
- SparkieService.removeEventSubscription({
86
- messageReceived
87
- })
81
+ SparkieService.removeEventSubscription({ messageReceived })
88
82
  }
89
- }, [
90
- addUnreadMessagesToSet,
91
- profileId,
92
- query.queryKey,
93
- queryClient,
94
- setWidgetLoading,
95
- useUpdateCursorMutation
96
- ])
97
-
98
- useEffect(() => {
99
- execute()
100
- }, [execute])
83
+ }, [messageReceived])
101
84
  }
102
85
 
103
86
  export default useSubscribeMessageReceivedEvent
@@ -1 +1,2 @@
1
+ export * from './messages-max-count.atom'
1
2
  export * from './unread-messages-set.atom'
@@ -0,0 +1,13 @@
1
+ import { atom, useAtom, useAtomValue } from 'jotai'
2
+
3
+ const INITIAL_MAX_COUNT = 2
4
+
5
+ const messagesMaxCountAtom = atom(INITIAL_MAX_COUNT)
6
+
7
+ const setMessagesMaxCountAtom = atom(
8
+ (get) => get(messagesMaxCountAtom),
9
+ (_, set, count: number) => set(messagesMaxCountAtom, count)
10
+ )
11
+
12
+ export const useSetMessagesMaxCountAtom = () => useAtom(setMessagesMaxCountAtom)
13
+ export const useMessagesMaxCount = () => useAtomValue(setMessagesMaxCountAtom)
@@ -1 +1,3 @@
1
+ export * from './has-to-update-cursor'
1
2
  export * from './messages-parser'
3
+ export * from './set-messages-cache'
@@ -0,0 +1 @@
1
+ export * from './utils'