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
@@ -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'
@@ -0,0 +1,53 @@
1
+ import type { Message } from '@hotmart/sparkie/dist/MessageService'
2
+ import type { InfiniteData, QueryClient } from '@tanstack/react-query'
3
+ import { produce } from 'immer'
4
+
5
+ import type { FetchMessagesResponse, IMessageWithSenderData } from '../../types'
6
+
7
+ const placeholderID = 'remove::placeholder::id'
8
+
9
+ export type SetMessageCacheParams = {
10
+ queryKey: readonly unknown[]
11
+ queryClient: QueryClient
12
+ data: Partial<Message>
13
+ sending?: boolean
14
+ }
15
+
16
+ export const setMessagesCache =
17
+ ({ queryClient, queryKey, data, sending }: SetMessageCacheParams) =>
18
+ () => {
19
+ queryClient.setQueryData<InfiniteData<FetchMessagesResponse>>(queryKey, (oldData) => {
20
+ return produce(oldData, (draft) => {
21
+ const lastPageMessages = draft?.pages?.at(-1)?.messages
22
+ const lastMessage = lastPageMessages?.at?.(-1)
23
+
24
+ const placeholderMsgIndex = Number(
25
+ lastPageMessages?.findIndex((msg) => msg.id === placeholderID)
26
+ )
27
+
28
+ const msgIndex = Number(
29
+ !isNaN(placeholderMsgIndex) && placeholderMsgIndex !== -1
30
+ ? placeholderMsgIndex
31
+ : lastPageMessages?.length
32
+ )
33
+
34
+ if (isNaN(msgIndex)) return draft
35
+
36
+ const newMessage = {
37
+ ...lastMessage,
38
+ sentAt: Date.now(),
39
+ updatedAt: Date.now(),
40
+ metadata: {
41
+ ...lastMessage?.metadata,
42
+ author: 'user'
43
+ },
44
+ ...data,
45
+ ...(sending ? { id: placeholderID } : {})
46
+ } as IMessageWithSenderData
47
+
48
+ lastPageMessages?.splice(msgIndex, 1, newMessage)
49
+
50
+ return draft
51
+ })
52
+ })
53
+ }
@@ -1,27 +1,43 @@
1
- import { render, screen, waitFor } from '@/src/config/tests'
2
- import type { IMessageWithSenderData } from '@/src/modules/messages'
3
- import IMessageWithSenderDataMock from '@/src/modules/messages/__tests__/imessage-with-sender-data.mock'
1
+ import { createRef } from 'react'
2
+
3
+ import { chance, render, screen, waitFor } from '@/src/config/tests'
4
+ import { useScroller } from '@/src/modules/messages/hooks/use-scroller'
5
+ import { useGetProfile } from '@/src/modules/profile'
4
6
  import WidgetSettingPropsBuilder from '../../__tests__/widget-settings-props.builder'
5
7
  import * as Store from '../../store'
6
8
 
7
9
  import ChatPage from './chat-page'
8
10
 
11
+ vi.mock('@/src/modules/profile', () => ({ useGetProfile: vi.fn() }))
12
+
13
+ vi.mock('@/src/modules/messages/hooks/use-scroller', () => ({
14
+ useScroller: vi.fn()
15
+ }))
16
+
9
17
  describe('ChatPage', () => {
10
18
  const defaultSettings = new WidgetSettingPropsBuilder()
11
- const getMessagesMock = new IMessageWithSenderDataMock().getMany(10) as IMessageWithSenderData[]
19
+ const scrollerRef = createRef<HTMLDivElement>()
20
+ const scrollToButtonRef = createRef<HTMLButtonElement>()
21
+
22
+ const useScrollerMock = {
23
+ scrollerRef,
24
+ scrollToButtonRef,
25
+ scrollToBottom: vi.fn(),
26
+ showScrollButton: false
27
+ }
12
28
 
13
29
  const renderComponent = () => render(<ChatPage />)
14
30
 
15
31
  beforeEach(() => {
16
32
  vi.spyOn(Store, 'useWidgetSettingsAtomValue').mockReturnValue(defaultSettings)
33
+ vi.mocked(useGetProfile).mockReturnValue({ data: { id: chance.guid() } } as never)
34
+ vi.mocked(useScroller).mockReturnValue(useScrollerMock)
17
35
  })
18
36
 
19
37
  it('should render each fetched message item from API', async () => {
20
38
  renderComponent()
21
39
 
22
- await waitFor(() =>
23
- expect(screen.getAllByTestId('messages-item')).toHaveLength(getMessagesMock.length)
24
- )
40
+ await waitFor(() => expect(screen.getAllByTestId('messages-item')).toHaveLength(2))
25
41
 
26
42
  expect(screen.getByPlaceholderText(/send_message.field.placeholder/)).toBeInTheDocument()
27
43
  })
@@ -1,24 +1,55 @@
1
- import { useRef } from 'react'
1
+ import { lazy, useEffect, useMemo, useRef } from 'react'
2
+ import { useInfiniteQuery } from '@tanstack/react-query'
2
3
 
3
4
  import { isTextEmpty } from '@/src/lib/utils/is-text-empty'
4
5
  import { ChatInput, MessagesList, useChatInputValueAtom } from '@/src/modules/messages/components'
5
- import { useAllMessages, useSendTextMessage } from '@/src/modules/messages/hooks'
6
+ import { MessagesContainer } from '@/src/modules/messages/components/messages-container'
7
+ import { getAllMessagesQuery, useSendTextMessage } from '@/src/modules/messages/hooks'
8
+ import { useMessagesMaxCount } from '@/src/modules/messages/store'
9
+ import { useGetProfile } from '@/src/modules/profile'
6
10
  import {
7
- useWidgetLoadingAtomValue,
11
+ useWidgetLoadingAtom,
8
12
  useWidgetSettingsAtomValue,
9
13
  useWidgetTabsValueAtom
10
14
  } from '../../store'
11
15
  import { WidgetHeader } from '../header'
12
16
  import { PageLayout } from '../page-layout'
13
17
 
18
+ const MessageItemError = lazy(
19
+ () => import('@/src/modules/messages/components/message-item-error/message-item-error')
20
+ )
21
+
22
+ const MessageItemEndOfScroll = lazy(
23
+ () =>
24
+ import(
25
+ '@/src/modules/messages/components/message-item-end-of-scroll/message-item-end-of-scroll'
26
+ )
27
+ )
28
+
14
29
  function ChatPage() {
15
- const widgetTabs = useWidgetTabsValueAtom()
16
30
  const chatInputRef = useRef<HTMLTextAreaElement>(null)
31
+ const scrollerRef = useRef<HTMLDivElement>(null)
32
+ const settings = useWidgetSettingsAtomValue()
33
+ const profileQuery = useGetProfile()
34
+ const widgetTabs = useWidgetTabsValueAtom()
17
35
  const sendTextMessageMutation = useSendTextMessage()
18
- const { messagesQuery } = useAllMessages()
19
- const widgetLoading = useWidgetLoadingAtomValue()
36
+ const limit = useMessagesMaxCount()
20
37
  const [value, setValue] = useChatInputValueAtom()
21
- const settings = useWidgetSettingsAtomValue()
38
+ const [, setWidgetLoading] = useWidgetLoadingAtom()
39
+
40
+ const conversationId = useMemo(() => settings?.conversationId, [settings?.conversationId])
41
+ const profileId = useMemo(() => profileQuery.data?.id, [profileQuery.data?.id])
42
+ const messagesQueryConfig = useMemo(
43
+ () =>
44
+ getAllMessagesQuery({
45
+ conversationId,
46
+ profileId,
47
+ limit
48
+ }),
49
+ [conversationId, limit, profileId]
50
+ )
51
+
52
+ const messagesQuery = useInfiniteQuery(messagesQueryConfig)
22
53
 
23
54
  const handleSendMessage = () => {
24
55
  const text = chatInputRef.current?.value ?? ''
@@ -33,6 +64,12 @@ function ChatPage() {
33
64
  })
34
65
  }
35
66
 
67
+ useEffect(() => {
68
+ if (messagesQuery.isError) {
69
+ setWidgetLoading(false)
70
+ }
71
+ }, [messagesQuery.isError, setWidgetLoading])
72
+
36
73
  return (
37
74
  <PageLayout
38
75
  asideChild={
@@ -40,18 +77,37 @@ function ChatPage() {
40
77
  name='new-chat-msg-input'
41
78
  ref={chatInputRef}
42
79
  onSend={widgetTabs.currentTab === 'chat' ? handleSendMessage : undefined}
43
- loading={widgetLoading || sendTextMessageMutation.isPending}
80
+ loading={sendTextMessageMutation.isPending}
44
81
  inputDisabled={messagesQuery?.isLoading}
45
82
  buttonDisabled={messagesQuery?.isLoading || !value.trim()}
46
83
  />
47
84
  }>
48
- <>
49
- <div className='mt-4 px-6 py-4'>
50
- <WidgetHeader enabledButtons={['info', 'close']} tutorName={settings?.tutorName} />
51
- </div>
52
- <MessagesList />
53
- </>
85
+ <div className='mt-4 px-6 py-4'>
86
+ <WidgetHeader enabledButtons={['info', 'close']} tutorName={settings?.tutorName} />
87
+ </div>
88
+ <MessagesContainer
89
+ ref={scrollerRef}
90
+ handleShowMore={async () => {
91
+ await messagesQuery.fetchNextPage()
92
+ }}
93
+ showButton={messagesQuery.hasNextPage}
94
+ loading={messagesQuery.isFetchingNextPage}>
95
+ <MessageItemEndOfScroll
96
+ show={
97
+ !messagesQuery.isFetching &&
98
+ !messagesQuery.hasNextPage &&
99
+ Number(messagesQuery.data?.size) > 0
100
+ }
101
+ />
102
+ {messagesQuery.data && <MessagesList messagesMap={messagesQuery.data} />}
103
+ <MessageItemError
104
+ show={messagesQuery.isError}
105
+ message={`❌ Error loading messages: ${messagesQuery.error?.message ?? ''}`}
106
+ retry={() => void messagesQuery.refetch()}
107
+ />
108
+ </MessagesContainer>
54
109
  </PageLayout>
55
110
  )
56
111
  }
112
+
57
113
  export default ChatPage
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'
3
3
 
4
4
  export type GreetingsCardProps = {
5
5
  tutorName: string
6
- author: string
6
+ author?: string
7
7
  isDarkTheme?: boolean
8
8
  }
9
9
 
@@ -1,3 +1,5 @@
1
+ import { useTranslation } from 'react-i18next'
2
+
1
3
  import { Button, Icon } from '@/src/lib/components'
2
4
  import { TutorWidgetEvents } from '../../events'
3
5
  import { useWidgetTabsAtom } from '../../store'
@@ -35,7 +37,9 @@ function WidgetHeader({
35
37
  showContentWithoutMeta,
36
38
  showContent = true
37
39
  }: WidgetHeaderProps) {
40
+ const { t } = useTranslation()
38
41
  const [, setTab] = useWidgetTabsAtom()
42
+ const name = tutorName ?? t('general.name')
39
43
 
40
44
  const handleClickArchive = () => {
41
45
  setTab('chat')
@@ -48,10 +52,8 @@ function WidgetHeader({
48
52
  return (
49
53
  <div className='grid-areas-[a_b] mt-0.5 grid grid-cols-[1fr_auto] items-center text-neutral-1000'>
50
54
  <div className='grid-area-[a]'>
51
- {showContent && !showContentWithoutMeta && <WidgetHeaderContent tutorName={tutorName} />}
52
- {showContentWithoutMeta && !showContent && (
53
- <WidgetHeaderContentWithoutMeta name={tutorName} />
54
- )}
55
+ {showContent && !showContentWithoutMeta && <WidgetHeaderContent tutorName={name} />}
56
+ {showContentWithoutMeta && !showContent && <WidgetHeaderContentWithoutMeta name={name} />}
55
57
  </div>
56
58
  <div className='shrink-0'>
57
59
  <div className='grid-area-[b] ml-auto flex max-w-max gap-3 text-neutral-700'>
@@ -3,7 +3,10 @@ import { useSendTextMessage } from '@/src/modules/messages/hooks'
3
3
 
4
4
  import WidgetStarterPage from './starter-page'
5
5
 
6
- vi.mock('@/src/modules/messages/hooks', () => ({ useSendTextMessage: vi.fn() }))
6
+ vi.mock('@/src/modules/messages/hooks', () => ({
7
+ getAllMessagesQuery: vi.fn(() => ({ pages: [], queryKey: ['any'] })),
8
+ useSendTextMessage: vi.fn()
9
+ }))
7
10
 
8
11
  describe('WidgetStarterPage', () => {
9
12
  const useSendTextMessageMock = { mutate: vi.fn() }
@@ -1,4 +1,5 @@
1
- import { useRef } from 'react'
1
+ import { useEffect, useMemo, useRef } from 'react'
2
+ import { useQueryClient } from '@tanstack/react-query'
2
3
  import clsx from 'clsx'
3
4
  import type { MouseEventHandler } from 'react'
4
5
  import { useTranslation } from 'react-i18next'
@@ -6,7 +7,9 @@ import { useTranslation } from 'react-i18next'
6
7
  import { Button } from '@/src/lib/components'
7
8
  import { useRefEventListener } from '@/src/lib/hooks'
8
9
  import { ChatInput, useChatInputValueAtom } from '@/src/modules/messages/components'
9
- import { useSendTextMessage } from '@/src/modules/messages/hooks'
10
+ import { getAllMessagesQuery, useSendTextMessage } from '@/src/modules/messages/hooks'
11
+ import { useMessagesMaxCount } from '@/src/modules/messages/store'
12
+ import { useGetProfile } from '@/src/modules/profile'
10
13
  import { useWidgetSettingsAtom, useWidgetTabsAtom } from '../../store'
11
14
  import { GreetingsCard } from '../greetings-card'
12
15
  import { WidgetHeader } from '../header'
@@ -21,6 +24,10 @@ function WidgetStarterPage() {
21
24
  const [chatInputValue, setChatInputValue] = useChatInputValueAtom()
22
25
  const [, setWidgetTabs] = useWidgetTabsAtom()
23
26
  const sendTextMessageMutation = useSendTextMessage()
27
+ const profileQuery = useGetProfile()
28
+ const limit = useMessagesMaxCount()
29
+ const queryClient = useQueryClient()
30
+ const name = settings?.tutorName ?? t('general.name')
24
31
 
25
32
  useRefEventListener<HTMLTextAreaElement>({
26
33
  config: {
@@ -53,6 +60,24 @@ function WidgetStarterPage() {
53
60
  sendText(chatInputRef.current?.value)
54
61
  }
55
62
 
63
+ const conversationId = useMemo(() => settings?.conversationId, [settings?.conversationId])
64
+
65
+ const profileId = useMemo(() => profileQuery.data?.id, [profileQuery.data?.id])
66
+
67
+ const messagesQueryConfig = useMemo(
68
+ () =>
69
+ getAllMessagesQuery({
70
+ conversationId,
71
+ profileId,
72
+ limit
73
+ }),
74
+ [conversationId, limit, profileId]
75
+ )
76
+
77
+ useEffect(() => {
78
+ void queryClient.prefetchInfiniteQuery(messagesQueryConfig)
79
+ }, [messagesQueryConfig, queryClient])
80
+
56
81
  return (
57
82
  <PageLayout
58
83
  asideChild={
@@ -70,13 +95,14 @@ function WidgetStarterPage() {
70
95
  })}>
71
96
  <WidgetHeader
72
97
  enabledButtons={['archive', 'info', 'close']}
73
- tutorName={settings?.tutorName}
98
+ tutorName={name}
99
+ showContent={false}
74
100
  />
75
101
 
76
102
  <div className='my-auto'>
77
103
  <GreetingsCard
78
- author={settings?.author ?? ''}
79
- tutorName={settings?.tutorName ?? ''}
104
+ author={settings?.user?.name}
105
+ tutorName={name}
80
106
  isDarkTheme={settings?.config?.theme === 'dark'}
81
107
  />
82
108
  </div>