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.
- package/CHANGELOG.md +12 -0
- package/package.json +1 -1
- package/public/assets/svg/error-dark.svg +27 -0
- package/public/assets/svg/error-light.svg +27 -0
- package/src/config/tests/handlers.ts +12 -0
- package/src/development-bootstrap.tsx +5 -2
- package/src/index.tsx +3 -0
- package/src/lib/components/button/button.tsx +105 -14
- package/src/lib/components/button/styles.module.css +9 -0
- package/src/lib/components/errors/generic/generic-error.tsx +58 -3
- package/src/lib/components/icons/arrow-up.svg +5 -0
- package/src/lib/components/icons/copy.svg +5 -0
- package/src/lib/components/icons/icon-names.d.ts +3 -0
- package/src/lib/components/icons/like.svg +5 -0
- package/src/modules/messages/components/message-actions/index.ts +2 -0
- package/src/modules/messages/components/message-actions/message-actions.tsx +49 -0
- package/src/modules/messages/components/message-item/message-item.tsx +21 -5
- package/src/modules/messages/components/message-item-error/message-item-error.tsx +16 -9
- package/src/modules/messages/components/message-skeleton/message-skeleton.tsx +1 -4
- package/src/modules/messages/components/messages-container/index.ts +2 -0
- package/src/modules/messages/components/messages-container/messages-container.tsx +91 -0
- package/src/modules/messages/components/messages-list/messages-list.tsx +9 -82
- package/src/modules/messages/constants.ts +5 -0
- package/src/modules/messages/events.ts +12 -4
- package/src/modules/messages/hooks/index.ts +1 -0
- package/src/modules/messages/hooks/use-all-messages/use-all-messages.tsx +1 -2
- package/src/modules/messages/hooks/use-infinite-get-messages/use-infinite-get-messages.spec.tsx +18 -19
- package/src/modules/messages/hooks/use-infinite-get-messages/use-infinite-get-messages.tsx +41 -35
- package/src/modules/messages/hooks/use-scroller/index.ts +2 -0
- package/src/modules/messages/hooks/use-scroller/use-scroller.tsx +50 -0
- package/src/modules/messages/hooks/use-send-text-message/use-send-text-message.tsx +31 -2
- package/src/modules/messages/hooks/use-subscribe-message-received-event/use-subscribe-message-received-event.tsx +47 -64
- package/src/modules/messages/store/index.ts +1 -0
- package/src/modules/messages/store/messages-max-count.atom.ts +13 -0
- package/src/modules/messages/utils/index.ts +2 -0
- package/src/modules/messages/utils/set-messages-cache/index.ts +1 -0
- package/src/modules/messages/utils/set-messages-cache/utils.ts +53 -0
- package/src/modules/widget/components/chat-page/chat-page.spec.tsx +23 -7
- package/src/modules/widget/components/chat-page/chat-page.tsx +70 -14
- package/src/modules/widget/components/greetings-card/greetings-card.tsx +1 -1
- package/src/modules/widget/components/header/header.tsx +6 -4
- package/src/modules/widget/components/starter-page/starter-page.spec.tsx +4 -1
- package/src/modules/widget/components/starter-page/starter-page.tsx +31 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useEffect, useMemo
|
|
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 {
|
|
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
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
63
|
-
})
|
|
45
|
+
if (idsList.has(data.id)) return
|
|
64
46
|
|
|
65
|
-
|
|
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
|
-
|
|
78
|
-
|
|
66
|
+
},
|
|
67
|
+
[
|
|
68
|
+
addUnreadMessagesToSet,
|
|
69
|
+
messagesQueryConfig.queryKey,
|
|
70
|
+
profileId,
|
|
71
|
+
queryClient,
|
|
72
|
+
setWidgetLoading,
|
|
73
|
+
useUpdateCursorMutation
|
|
74
|
+
]
|
|
75
|
+
)
|
|
79
76
|
|
|
80
|
-
|
|
81
|
-
|
|
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
|
|
@@ -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)
|
|
@@ -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 {
|
|
2
|
-
|
|
3
|
-
import
|
|
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
|
|
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 {
|
|
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
|
-
|
|
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
|
|
19
|
-
const widgetLoading = useWidgetLoadingAtomValue()
|
|
36
|
+
const limit = useMessagesMaxCount()
|
|
20
37
|
const [value, setValue] = useChatInputValueAtom()
|
|
21
|
-
const
|
|
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={
|
|
80
|
+
loading={sendTextMessageMutation.isPending}
|
|
44
81
|
inputDisabled={messagesQuery?.isLoading}
|
|
45
82
|
buttonDisabled={messagesQuery?.isLoading || !value.trim()}
|
|
46
83
|
/>
|
|
47
84
|
}>
|
|
48
|
-
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
|
@@ -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={
|
|
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', () => ({
|
|
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={
|
|
98
|
+
tutorName={name}
|
|
99
|
+
showContent={false}
|
|
74
100
|
/>
|
|
75
101
|
|
|
76
102
|
<div className='my-auto'>
|
|
77
103
|
<GreetingsCard
|
|
78
|
-
author={settings?.
|
|
79
|
-
tutorName={
|
|
104
|
+
author={settings?.user?.name}
|
|
105
|
+
tutorName={name}
|
|
80
106
|
isDarkTheme={settings?.config?.theme === 'dark'}
|
|
81
107
|
/>
|
|
82
108
|
</div>
|