app-tutor-ai-consumer 1.5.0 → 1.6.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 +6 -0
- package/config/vitest/__mocks__/sparkie.tsx +9 -0
- package/eslint.config.mjs +27 -0
- package/package.json +6 -2
- package/src/@types/index.d.ts +5 -2
- package/src/config/tanstack/query-client.ts +1 -1
- package/src/development-bootstrap.tsx +15 -15
- package/src/index.tsx +15 -5
- package/src/lib/components/icons/ai-color.svg +17 -0
- package/src/lib/components/icons/icon-names.d.ts +1 -1
- package/src/lib/utils/is-text-empty.ts +3 -0
- package/src/main/main.spec.tsx +0 -8
- package/src/modules/messages/components/chat-input/chat-input.spec.tsx +72 -0
- package/src/modules/messages/components/chat-input/chat-input.tsx +52 -6
- package/src/modules/messages/components/index.ts +1 -0
- package/src/modules/messages/components/message-skeleton/index.ts +1 -0
- package/src/modules/messages/components/message-skeleton/message-skeleton.tsx +23 -0
- package/src/modules/messages/components/messages-list/messages-list.tsx +14 -1
- package/src/modules/messages/hooks/index.ts +2 -0
- package/src/modules/messages/hooks/use-infinite-get-messages/use-infinite-get-messages.spec.tsx +7 -0
- package/src/modules/messages/hooks/use-infinite-get-messages/use-infinite-get-messages.tsx +7 -23
- package/src/modules/messages/hooks/use-manage-scroll/use-manage-scroll.tsx +5 -1
- package/src/modules/messages/hooks/use-send-text-message/index.ts +2 -0
- package/src/modules/messages/hooks/use-send-text-message/use-send-text-message.spec.tsx +86 -0
- package/src/modules/messages/hooks/use-send-text-message/use-send-text-message.tsx +60 -0
- package/src/modules/messages/hooks/use-skeleton-ref/index.ts +2 -0
- package/src/modules/messages/hooks/use-skeleton-ref/use-skeleton-ref.tsx +34 -0
- package/src/modules/messages/hooks/use-subscribe-message-received-event/index.ts +2 -0
- package/src/modules/messages/hooks/use-subscribe-message-received-event/use-subscribe-message-received-event.tsx +80 -0
- package/src/modules/messages/service.ts +8 -7
- package/src/modules/sparkie/service.ts +182 -35
- package/src/modules/sparkie/types.ts +10 -2
- package/src/modules/widget/__tests__/widget-settings-props.builder.ts +23 -1
- package/src/modules/widget/components/ai-avatar/ai-avatar.tsx +11 -53
- package/src/modules/widget/components/chat-page/chat-page.tsx +22 -1
- package/src/modules/widget/components/container/container.tsx +4 -24
- package/src/modules/widget/components/scroll-to-bottom-button/scroll-to-bottom-button.tsx +1 -1
- package/src/modules/widget/store/index.ts +2 -0
- package/src/modules/widget/store/widget-loading.atom.ts +11 -0
- package/src/modules/widget/store/widget-scrolling.atom.ts +11 -0
- package/src/modules/widget/store/widget-tabs.atom.ts +2 -1
- package/src/types.ts +4 -1
|
@@ -1,57 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
import clsx from 'clsx'
|
|
2
|
+
|
|
3
|
+
import { Icon } from '@/src/lib/components'
|
|
4
|
+
|
|
5
|
+
function AIAvatarIcon({
|
|
6
|
+
className = 'rounded-full border-4 border-neutral-900 bg-neutral-800'
|
|
7
|
+
}: {
|
|
8
|
+
className?: string
|
|
9
|
+
}) {
|
|
2
10
|
return (
|
|
3
|
-
<div className='flex h-11 w-11 items-center justify-center
|
|
4
|
-
<
|
|
5
|
-
width='25'
|
|
6
|
-
height='24'
|
|
7
|
-
viewBox='0 0 25 24'
|
|
8
|
-
fill='none'
|
|
9
|
-
xmlns='http://www.w3.org/2000/svg'>
|
|
10
|
-
<path
|
|
11
|
-
d='M8.21062 3.77518L11.05 9.08404L16.3739 11.9154L11.05 14.7469L8.21062 20.0557L5.37122 14.7469L0.0473633 11.9154L5.37122 9.08404L8.21062 3.77518Z'
|
|
12
|
-
fill='url(#paint0_linear_18316_181)'
|
|
13
|
-
/>
|
|
14
|
-
<path
|
|
15
|
-
d='M17.8806 0.590271L19.2848 3.21586L21.9178 4.61617L19.2848 6.01648L17.8806 8.64206L16.4762 6.01648L13.8433 4.61617L16.4762 3.21586L17.8806 0.590271Z'
|
|
16
|
-
fill='url(#paint1_linear_18316_181)'
|
|
17
|
-
/>
|
|
18
|
-
<path
|
|
19
|
-
d='M20.7199 16.7845L18.9453 13.4665L17.1707 16.7845L13.8433 18.5541L17.1707 20.3237L18.9453 23.6418L20.7199 20.3237L24.0473 18.5541L20.7199 16.7845Z'
|
|
20
|
-
fill='url(#paint2_linear_18316_181)'
|
|
21
|
-
/>
|
|
22
|
-
<defs>
|
|
23
|
-
<linearGradient
|
|
24
|
-
id='paint0_linear_18316_181'
|
|
25
|
-
x1='0.0473633'
|
|
26
|
-
y1='11.9154'
|
|
27
|
-
x2='16.3739'
|
|
28
|
-
y2='11.9154'
|
|
29
|
-
gradientUnits='userSpaceOnUse'>
|
|
30
|
-
<stop stopColor='#44D0FF' />
|
|
31
|
-
<stop offset='1' stopColor='#B48EFF' />
|
|
32
|
-
</linearGradient>
|
|
33
|
-
<linearGradient
|
|
34
|
-
id='paint1_linear_18316_181'
|
|
35
|
-
x1='13.8433'
|
|
36
|
-
y1='4.61617'
|
|
37
|
-
x2='21.9178'
|
|
38
|
-
y2='4.61617'
|
|
39
|
-
gradientUnits='userSpaceOnUse'>
|
|
40
|
-
<stop stopColor='#44D0FF' />
|
|
41
|
-
<stop offset='1' stopColor='#B48EFF' />
|
|
42
|
-
</linearGradient>
|
|
43
|
-
<linearGradient
|
|
44
|
-
id='paint2_linear_18316_181'
|
|
45
|
-
x1='13.8433'
|
|
46
|
-
y1='18.5541'
|
|
47
|
-
x2='24.0473'
|
|
48
|
-
y2='18.5541'
|
|
49
|
-
gradientUnits='userSpaceOnUse'>
|
|
50
|
-
<stop stopColor='#44D0FF' />
|
|
51
|
-
<stop offset='1' stopColor='#B48EFF' />
|
|
52
|
-
</linearGradient>
|
|
53
|
-
</defs>
|
|
54
|
-
</svg>
|
|
11
|
+
<div className={clsx('flex h-11 w-11 items-center justify-center', className)}>
|
|
12
|
+
<Icon name='ai-color' className='h-6 w-6' aria-label='AI avatar Icon' />
|
|
55
13
|
</div>
|
|
56
14
|
)
|
|
57
15
|
}
|
|
@@ -1,15 +1,36 @@
|
|
|
1
1
|
import { useRef } from 'react'
|
|
2
2
|
|
|
3
|
+
import { isTextEmpty } from '@/src/lib/utils/is-text-empty'
|
|
3
4
|
import { ChatInput, MessagesList } from '@/src/modules/messages/components'
|
|
5
|
+
import { useSendTextMessage } from '@/src/modules/messages/hooks'
|
|
6
|
+
import { useWidgetTabsValueAtom } from '../../store'
|
|
4
7
|
|
|
5
8
|
function ChatPage() {
|
|
9
|
+
const widgetTabs = useWidgetTabsValueAtom()
|
|
6
10
|
const chatInputRef = useRef<HTMLInputElement>(null)
|
|
11
|
+
const sendTextMessageMutation = useSendTextMessage()
|
|
12
|
+
|
|
13
|
+
const handleSendMessage = () => {
|
|
14
|
+
const text = chatInputRef.current?.value ?? ''
|
|
15
|
+
|
|
16
|
+
if (!isTextEmpty(text)) return
|
|
17
|
+
|
|
18
|
+
sendTextMessageMutation.mutate(text, {
|
|
19
|
+
onSuccess() {
|
|
20
|
+
if (chatInputRef.current?.value) chatInputRef.current.value = ''
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
}
|
|
7
24
|
|
|
8
25
|
return (
|
|
9
26
|
<>
|
|
10
27
|
<MessagesList />
|
|
11
28
|
<div className='border-t border-t-neutral-700 px-5 py-4'>
|
|
12
|
-
<ChatInput
|
|
29
|
+
<ChatInput
|
|
30
|
+
name='new-chat-msg-input'
|
|
31
|
+
ref={chatInputRef}
|
|
32
|
+
onSend={widgetTabs.currentTab === 'chat' ? handleSendMessage : undefined}
|
|
33
|
+
/>
|
|
13
34
|
</div>
|
|
14
35
|
</>
|
|
15
36
|
)
|
|
@@ -1,30 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { useWidgetSettingsAtom, useWidgetTabsAtom } from '../../store'
|
|
1
|
+
import { useSubscribeMessageReceivedEvent } from '@/src/modules/messages/hooks'
|
|
2
|
+
import { useWidgetTabsValueAtom } from '../../store'
|
|
4
3
|
import { WIDGET_TABS } from '../constants'
|
|
5
4
|
|
|
6
5
|
function WidgetContainer() {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
const [widgetTabs] = useWidgetTabsAtom()
|
|
10
|
-
|
|
11
|
-
if (!settings?.tutorName) return null
|
|
12
|
-
|
|
13
|
-
// TODO: change it for the general API error design from FIGMA as soon as it is available
|
|
14
|
-
if (sparkieQuery.isError)
|
|
15
|
-
return (
|
|
16
|
-
<div className='text-neutral-50'>
|
|
17
|
-
<span>Error initializing sparkie</span>
|
|
18
|
-
<button onClick={() => void sparkieQuery.refetch()}>Try again</button>
|
|
19
|
-
</div>
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
if (sparkieQuery.isLoading)
|
|
23
|
-
return (
|
|
24
|
-
<div className='flex flex-col items-center justify-center p-8'>
|
|
25
|
-
<Spinner className='inline-flex h-6 w-6 animate-spin text-neutral-200' />
|
|
26
|
-
</div>
|
|
27
|
-
)
|
|
6
|
+
const widgetTabs = useWidgetTabsValueAtom()
|
|
7
|
+
useSubscribeMessageReceivedEvent()
|
|
28
8
|
|
|
29
9
|
return (
|
|
30
10
|
<div className='flex min-h-svh flex-col items-center justify-center'>
|
|
@@ -13,7 +13,7 @@ const ScrollToBottomButton = forwardRef<HTMLButtonElement, IScrollToBottomButton
|
|
|
13
13
|
({ show = false, top = 0, onClick, className, ...props }, ref) => (
|
|
14
14
|
<button
|
|
15
15
|
{...props}
|
|
16
|
-
style={{ top }}
|
|
16
|
+
style={isNaN(Number(top)) ? undefined : { top }}
|
|
17
17
|
ref={ref}
|
|
18
18
|
className={clsx(
|
|
19
19
|
'fixed inset-x-1/2 flex size-7 cursor-pointer flex-col items-center justify-center rounded-full bg-neutral-600 text-sm text-neutral-50 outline-none transition-colors duration-300 ease-in hover:scale-110 hover:bg-neutral-700 focus:outline-none focus:ring-2 focus:ring-neutral-500 focus:ring-offset-2',
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { atom, useAtom, useAtomValue } from 'jotai'
|
|
2
|
+
|
|
3
|
+
const widgetLoadingAtom = atom<boolean>(false)
|
|
4
|
+
|
|
5
|
+
const setWidgetLoadingAtom = atom(
|
|
6
|
+
(get) => get(widgetLoadingAtom),
|
|
7
|
+
(_, set, isLoading: boolean) => set(widgetLoadingAtom, isLoading)
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
export const useWidgetLoadingAtom = () => useAtom(setWidgetLoadingAtom)
|
|
11
|
+
export const useWidgetLoadingAtomValue = () => useAtomValue(setWidgetLoadingAtom)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { atom, useAtom, useAtomValue } from 'jotai'
|
|
2
|
+
|
|
3
|
+
const widgetScrollingAtom = atom<boolean>(false)
|
|
4
|
+
|
|
5
|
+
const setWidgetScrollingAtom = atom(
|
|
6
|
+
(get) => get(widgetScrollingAtom),
|
|
7
|
+
(_, set, isScrolling: boolean) => set(widgetScrollingAtom, isScrolling)
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
export const useWidgetScrollingAtom = () => useAtom(setWidgetScrollingAtom)
|
|
11
|
+
export const useWidgetScrollingAtomValue = () => useAtomValue(setWidgetScrollingAtom)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { atom, useAtom } from 'jotai'
|
|
1
|
+
import { atom, useAtom, useAtomValue } from 'jotai'
|
|
2
2
|
|
|
3
3
|
import type { CurrentTabKey } from '../components'
|
|
4
4
|
|
|
@@ -50,4 +50,5 @@ export const goBackTabAtom = atom(null, (get, set) => {
|
|
|
50
50
|
|
|
51
51
|
export const useGetWidgetTabsAtom = () => useAtom(widgetTabsAtom)
|
|
52
52
|
export const useWidgetTabsAtom = () => useAtom(setWidgetTabsAtom)
|
|
53
|
+
export const useWidgetTabsValueAtom = () => useAtomValue(setWidgetTabsAtom)
|
|
53
54
|
export const useWidgetGoBackTabAton = () => useAtom(goBackTabAtom)
|
package/src/types.ts
CHANGED
|
@@ -26,10 +26,13 @@ export type WidgetSettingProps = {
|
|
|
26
26
|
namespace?: string
|
|
27
27
|
productId: number
|
|
28
28
|
productName: string
|
|
29
|
-
sessionId
|
|
29
|
+
sessionId: string
|
|
30
30
|
membershipId?: string
|
|
31
31
|
membershipSlug?: string
|
|
32
32
|
userId?: string
|
|
33
33
|
tutorName?: string
|
|
34
34
|
user?: User
|
|
35
|
+
classHashId?: string
|
|
36
|
+
owner_id?: string
|
|
37
|
+
current_media_codes?: string
|
|
35
38
|
}
|