app-tutor-ai-consumer 1.32.2 → 1.32.3
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 +2 -0
- package/config/vitest/__mocks__/sparkie.tsx +1 -1
- package/package.json +2 -2
- package/src/@types/index.d.ts +3 -2
- package/src/config/tanstack/query-provider.tsx +7 -3
- package/src/index.backup.tsx +61 -0
- package/src/index.tsx +63 -10
- package/src/lib/hooks/use-response-timeout/index.ts +1 -0
- package/src/lib/hooks/use-response-timeout/use-response-timeout.tsx +42 -0
- package/src/modules/global-providers/global-providers.tsx +1 -6
- package/src/modules/messages/__tests__/imessage-with-sender-data.builder.ts +1 -1
- package/src/modules/messages/hooks/use-subscribe-message-received-event/use-subscribe-message-received-event.tsx +119 -54
- package/src/modules/messages/service.direct.ts +1 -1
- package/src/modules/messages/service.ts +3 -2
- package/src/modules/messages/types.ts +1 -1
- package/src/modules/messages/utils/set-messages-cache/utils.ts +1 -1
- package/src/modules/sparkie/service.ts +2 -3
- package/src/modules/widget/components/container/container.tsx +0 -14
- package/src/types.ts +9 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
SparkieMessageServiceMock,
|
|
5
5
|
SparkieCursorServiceMock
|
|
6
6
|
} from '@/src/modules/sparkie/__tests__/sparkie.mock'
|
|
7
|
-
import MessageService from '@hotmart/sparkie/dist/MessageService'
|
|
7
|
+
import MessageService from '@hotmart-org-ca/sparkie/dist/MessageService'
|
|
8
8
|
|
|
9
9
|
vi.mock('@hotmart/sparkie', () => ({ default: SparkieMock }))
|
|
10
10
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "app-tutor-ai-consumer",
|
|
3
|
-
"version": "1.32.
|
|
3
|
+
"version": "1.32.3",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "rspack serve --env=development --config config/rspack/rspack.config.js",
|
|
@@ -108,8 +108,8 @@
|
|
|
108
108
|
"dependencies": {
|
|
109
109
|
"@hot-observability-js/react": "~1.1.0",
|
|
110
110
|
"@hotmart-org-ca/hot-observability-js": "~1.1.0",
|
|
111
|
+
"@hotmart-org-ca/sparkie": "~5.1.4",
|
|
111
112
|
"@hotmart/event-agent-js": "~1.1.2",
|
|
112
|
-
"@hotmart/sparkie": "~5.1.0",
|
|
113
113
|
"@optimizely/react-sdk": "~3.2.4",
|
|
114
114
|
"@tanstack/query-sync-storage-persister": "~5.80.7",
|
|
115
115
|
"@tanstack/react-query": "~5.80.6",
|
package/src/@types/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { StartTutorWidgetProps } from '@/src/types'
|
|
1
|
+
import type { StartTutorWidgetProps, WidgetInstance } from '@/src/types'
|
|
2
2
|
|
|
3
3
|
export {}
|
|
4
4
|
|
|
@@ -13,7 +13,8 @@ declare global {
|
|
|
13
13
|
elementId: StartTutorWidgetProps['elementId'],
|
|
14
14
|
settings: StartTutorWidgetProps['settings']
|
|
15
15
|
) => Promise<void>
|
|
16
|
-
closeChatWidget: () => void
|
|
16
|
+
closeChatWidget: () => Promise<void>
|
|
17
|
+
__CHAT_WIDGET_INSTANCE__?: WidgetInstance
|
|
17
18
|
TOKEN: string
|
|
18
19
|
}
|
|
19
20
|
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
import type { QueryClient } from '@tanstack/react-query'
|
|
1
2
|
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
|
|
2
3
|
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'
|
|
3
4
|
import type { PropsWithChildren } from 'react'
|
|
4
5
|
|
|
5
|
-
import { persister
|
|
6
|
+
import { persister } from './query-client'
|
|
6
7
|
|
|
7
|
-
export type QueryProviderProps = PropsWithChildren<{
|
|
8
|
+
export type QueryProviderProps = PropsWithChildren<{
|
|
9
|
+
showDevTools?: boolean
|
|
10
|
+
queryClient: QueryClient
|
|
11
|
+
}>
|
|
8
12
|
|
|
9
|
-
function QueryProvider({ children, showDevTools = true }: QueryProviderProps) {
|
|
13
|
+
function QueryProvider({ children, queryClient, showDevTools = true }: QueryProviderProps) {
|
|
10
14
|
return (
|
|
11
15
|
<PersistQueryClientProvider client={queryClient} persistOptions={{ persister }}>
|
|
12
16
|
{children}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import './config/styles/global.css'
|
|
2
|
+
import './config/styles/index.css'
|
|
3
|
+
|
|
4
|
+
import { StrictMode } from 'react'
|
|
5
|
+
import { createRoot } from 'react-dom/client'
|
|
6
|
+
|
|
7
|
+
import { initTheme } from '@/src/config/theme'
|
|
8
|
+
import { version } from '../package.json'
|
|
9
|
+
|
|
10
|
+
import { initLanguage } from './config/i18n'
|
|
11
|
+
import { devMode, productionMode } from './lib/utils'
|
|
12
|
+
import { Main } from './main'
|
|
13
|
+
import { TutorWidgetEvents } from './modules/widget'
|
|
14
|
+
import type { Theme, WidgetSettingProps } from './types'
|
|
15
|
+
|
|
16
|
+
const loadMainStyles = () => {
|
|
17
|
+
const isProduction = productionMode
|
|
18
|
+
const bundlePath = !isProduction
|
|
19
|
+
? `${process.env.BUNDLE_PATH}/`
|
|
20
|
+
: `${process.env.BUNDLE_PATH}/${process.env.APP_NAME}/_current/`
|
|
21
|
+
|
|
22
|
+
const cssPath = `${bundlePath}app-tutor-ai-consumer.css?v=${version}`
|
|
23
|
+
|
|
24
|
+
if (!document.querySelector(`link[href="${cssPath}"]`)) {
|
|
25
|
+
const linkElement = document.createElement('link')
|
|
26
|
+
linkElement.rel = 'stylesheet'
|
|
27
|
+
linkElement.href = cssPath
|
|
28
|
+
document.head.appendChild(linkElement)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
window.startChatWidget = async (
|
|
33
|
+
elementId = 'tutor-chat-app-widget',
|
|
34
|
+
settings: WidgetSettingProps
|
|
35
|
+
) => {
|
|
36
|
+
if (!devMode) {
|
|
37
|
+
loadMainStyles()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const rootElement = document.getElementById(elementId) as HTMLElement
|
|
41
|
+
|
|
42
|
+
if (!rootElement) return
|
|
43
|
+
|
|
44
|
+
rootElement.setAttribute('id', 'hotmart-app-tutor-ai-consumer-root')
|
|
45
|
+
const theme = (rootElement.getAttribute('data-theme') ?? 'dark') as Theme
|
|
46
|
+
const root = createRoot(rootElement)
|
|
47
|
+
|
|
48
|
+
await initLanguage(settings.locale)
|
|
49
|
+
initTheme(theme)
|
|
50
|
+
|
|
51
|
+
if (root) {
|
|
52
|
+
root.render(
|
|
53
|
+
<StrictMode>
|
|
54
|
+
<Main settings={{ ...settings, config: { ...settings.config, theme } }} />
|
|
55
|
+
</StrictMode>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
window.closeChatWidget = () =>
|
|
61
|
+
Promise.resolve(TutorWidgetEvents['c3po-app-widget-close'].dispatch())
|
package/src/index.tsx
CHANGED
|
@@ -4,13 +4,14 @@ import './config/styles/index.css'
|
|
|
4
4
|
import { StrictMode } from 'react'
|
|
5
5
|
import { createRoot } from 'react-dom/client'
|
|
6
6
|
|
|
7
|
+
import { queryClient, QueryProvider } from '@/src/config/tanstack'
|
|
7
8
|
import { initTheme } from '@/src/config/theme'
|
|
8
9
|
import { version } from '../package.json'
|
|
9
10
|
|
|
10
11
|
import { initLanguage } from './config/i18n'
|
|
11
12
|
import { devMode, productionMode } from './lib/utils'
|
|
12
13
|
import { Main } from './main'
|
|
13
|
-
import {
|
|
14
|
+
import { SparkieService } from './modules/sparkie'
|
|
14
15
|
import type { Theme, WidgetSettingProps } from './types'
|
|
15
16
|
|
|
16
17
|
const loadMainStyles = () => {
|
|
@@ -33,28 +34,80 @@ window.startChatWidget = async (
|
|
|
33
34
|
elementId = 'tutor-chat-app-widget',
|
|
34
35
|
settings: WidgetSettingProps
|
|
35
36
|
) => {
|
|
37
|
+
if (window.__CHAT_WIDGET_INSTANCE__) {
|
|
38
|
+
await window.closeChatWidget()
|
|
39
|
+
}
|
|
40
|
+
|
|
36
41
|
if (!devMode) {
|
|
37
42
|
loadMainStyles()
|
|
38
43
|
}
|
|
39
44
|
|
|
40
|
-
const
|
|
45
|
+
const container = document.getElementById(elementId) as HTMLElement
|
|
41
46
|
|
|
42
|
-
if (!
|
|
47
|
+
if (!container) return
|
|
43
48
|
|
|
44
|
-
|
|
45
|
-
const theme = (
|
|
46
|
-
const root = createRoot(rootElement)
|
|
49
|
+
container.setAttribute('id', 'hotmart-app-tutor-ai-consumer-root')
|
|
50
|
+
const theme = (container.getAttribute('data-theme') ?? 'dark') as Theme
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
initTheme(theme)
|
|
52
|
+
const root = createRoot(container)
|
|
50
53
|
|
|
51
54
|
if (root) {
|
|
55
|
+
initTheme(theme)
|
|
56
|
+
await initLanguage(settings.locale)
|
|
57
|
+
|
|
52
58
|
root.render(
|
|
53
59
|
<StrictMode>
|
|
54
|
-
<
|
|
60
|
+
<QueryProvider queryClient={queryClient} showDevTools={false}>
|
|
61
|
+
<Main settings={{ ...settings, config: { ...settings.config, theme } }} />
|
|
62
|
+
</QueryProvider>
|
|
55
63
|
</StrictMode>
|
|
56
64
|
)
|
|
65
|
+
|
|
66
|
+
window.__CHAT_WIDGET_INSTANCE__ = { root, container, queryClient }
|
|
57
67
|
}
|
|
58
68
|
}
|
|
59
69
|
|
|
60
|
-
window.closeChatWidget = () =>
|
|
70
|
+
window.closeChatWidget = async () => {
|
|
71
|
+
const chatWidgetInstance = window.__CHAT_WIDGET_INSTANCE__
|
|
72
|
+
|
|
73
|
+
if (!chatWidgetInstance) return
|
|
74
|
+
|
|
75
|
+
const { root, container, queryClient } = chatWidgetInstance
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
await queryClient.cancelQueries()
|
|
79
|
+
} catch (err) {
|
|
80
|
+
console.error('Error cancelling queries on widget close', err)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
root.unmount()
|
|
85
|
+
} catch (err) {
|
|
86
|
+
console.warn('Error unmounting widget root', err)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (typeof queryClient.clear === 'function') {
|
|
90
|
+
try {
|
|
91
|
+
queryClient.clear()
|
|
92
|
+
} catch {
|
|
93
|
+
queryClient.getQueryCache().clear()
|
|
94
|
+
queryClient.getMutationCache().clear()
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
queryClient.getQueryCache().clear()
|
|
98
|
+
queryClient.getMutationCache().clear()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
container.remove()
|
|
103
|
+
} catch {
|
|
104
|
+
// ignore
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
await SparkieService.destroySparkie()
|
|
109
|
+
window.__CHAT_WIDGET_INSTANCE__ = undefined
|
|
110
|
+
} catch (err) {
|
|
111
|
+
console.error('Error destroying Sparkie instance', err)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as useResponseTimeout } from './use-response-timeout'
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useCallback, useRef } from 'react'
|
|
2
|
+
|
|
3
|
+
const FIVE_MINUTES = 5 * 60 * 1000
|
|
4
|
+
|
|
5
|
+
function useResponseTimeout() {
|
|
6
|
+
const timeoutRef = useRef<NodeJS.Timeout>(null)
|
|
7
|
+
const isWaitingRef = useRef(false)
|
|
8
|
+
|
|
9
|
+
const startTimeout = useCallback((callback: () => void) => {
|
|
10
|
+
if (timeoutRef.current) clearTimeout(timeoutRef.current)
|
|
11
|
+
|
|
12
|
+
isWaitingRef.current = true
|
|
13
|
+
|
|
14
|
+
timeoutRef.current = setTimeout(() => {
|
|
15
|
+
callback()
|
|
16
|
+
isWaitingRef.current = false
|
|
17
|
+
}, FIVE_MINUTES)
|
|
18
|
+
}, [])
|
|
19
|
+
|
|
20
|
+
const reset = useCallback(() => {
|
|
21
|
+
if (timeoutRef.current) {
|
|
22
|
+
clearTimeout(timeoutRef.current)
|
|
23
|
+
timeoutRef.current = null
|
|
24
|
+
}
|
|
25
|
+
isWaitingRef.current = false
|
|
26
|
+
}, [])
|
|
27
|
+
|
|
28
|
+
const cleanup = useCallback(() => {
|
|
29
|
+
if (timeoutRef.current) {
|
|
30
|
+
clearTimeout(timeoutRef.current)
|
|
31
|
+
}
|
|
32
|
+
}, [])
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
startTimeout,
|
|
36
|
+
cleanup,
|
|
37
|
+
reset,
|
|
38
|
+
isWaiting: isWaitingRef.current
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default useResponseTimeout
|
|
@@ -2,7 +2,6 @@ import { type PropsWithChildren, useEffect } from 'react'
|
|
|
2
2
|
import { v4 } from 'uuid'
|
|
3
3
|
|
|
4
4
|
import { OptimizelyProvider } from '@/src/config/optimizely'
|
|
5
|
-
import { QueryProvider } from '@/src/config/tanstack'
|
|
6
5
|
import { useWidgetSettingsAtom } from '@/src/modules/widget'
|
|
7
6
|
import type { WidgetSettingProps } from '@/src/types'
|
|
8
7
|
|
|
@@ -21,11 +20,7 @@ function GlobalProviders({ children, settings }: GlobalProvidersProps) {
|
|
|
21
20
|
setWidgetSettings(settings)
|
|
22
21
|
}, [setWidgetSettings, settings])
|
|
23
22
|
|
|
24
|
-
return
|
|
25
|
-
<OptimizelyProvider settings={settings}>
|
|
26
|
-
<QueryProvider>{children}</QueryProvider>
|
|
27
|
-
</OptimizelyProvider>
|
|
28
|
-
)
|
|
23
|
+
return <OptimizelyProvider settings={settings}>{children}</OptimizelyProvider>
|
|
29
24
|
}
|
|
30
25
|
|
|
31
26
|
export default GlobalProviders
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo } from 'react'
|
|
2
|
-
import type { InfiniteData } from '@tanstack/react-query'
|
|
2
|
+
import type { InfiniteData, QueryClient } from '@tanstack/react-query'
|
|
3
3
|
import { useQueryClient } from '@tanstack/react-query'
|
|
4
4
|
import { produce } from 'immer'
|
|
5
5
|
|
|
6
6
|
import { ComponentSource, DataHubService } from '@/src/config/datahub'
|
|
7
7
|
import { ViewTutorAnswerMessageSchema } from '@/src/config/datahub/schemas/tutor'
|
|
8
|
+
import { useResponseTimeout } from '@/src/lib/hooks/use-response-timeout'
|
|
8
9
|
import { useUpdateCursor } from '@/src/modules/cursor/hooks'
|
|
9
10
|
import { useGetProfile } from '@/src/modules/profile'
|
|
10
11
|
import { SparkieService } from '@/src/modules/sparkie'
|
|
@@ -17,6 +18,59 @@ import { useMessagesMaxCount, useUnreadMessagesSetAtom } from '../../store'
|
|
|
17
18
|
import type { FetchMessagesResponse, IMessageWithSenderData } from '../../types'
|
|
18
19
|
import { getAllMessagesQuery } from '../use-infinite-get-messages'
|
|
19
20
|
|
|
21
|
+
export type MessageReceivedProps = {
|
|
22
|
+
data: IMessageWithSenderData
|
|
23
|
+
queryClient: QueryClient
|
|
24
|
+
conversationId: string
|
|
25
|
+
profileId: string
|
|
26
|
+
limit: number
|
|
27
|
+
messageIsMineCallback?: (data: IMessageWithSenderData) => void
|
|
28
|
+
messageIsNotMineCallback?: (data: IMessageWithSenderData) => void
|
|
29
|
+
errorCallback?: (error: unknown) => void
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const messageReceivedUtil = ({
|
|
33
|
+
queryClient,
|
|
34
|
+
data,
|
|
35
|
+
conversationId,
|
|
36
|
+
profileId,
|
|
37
|
+
limit,
|
|
38
|
+
messageIsMineCallback,
|
|
39
|
+
messageIsNotMineCallback,
|
|
40
|
+
errorCallback
|
|
41
|
+
}: MessageReceivedProps) => {
|
|
42
|
+
try {
|
|
43
|
+
const messagesQueryConfig = getAllMessagesQuery({ conversationId, profileId, limit })
|
|
44
|
+
const queryData = queryClient.getQueryData<InfiniteData<FetchMessagesResponse>>(
|
|
45
|
+
messagesQueryConfig.queryKey
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
const idsList = new Set(
|
|
49
|
+
queryData?.pages.flatMap((items) => items.messages.map((msg) => msg.id))
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if (idsList.has(data.id)) return
|
|
53
|
+
|
|
54
|
+
queryClient.setQueryData<InfiniteData<FetchMessagesResponse>>(
|
|
55
|
+
messagesQueryConfig.queryKey,
|
|
56
|
+
(oldData) =>
|
|
57
|
+
produce(oldData, (draft) => {
|
|
58
|
+
draft?.pages.at(-1)?.messages?.push(data)
|
|
59
|
+
return draft
|
|
60
|
+
})
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
const isMine = data.contactId === profileId
|
|
64
|
+
|
|
65
|
+
if (isMine) {
|
|
66
|
+
return messageIsMineCallback?.(data)
|
|
67
|
+
}
|
|
68
|
+
messageIsNotMineCallback?.(data)
|
|
69
|
+
} catch (error) {
|
|
70
|
+
errorCallback?.(error)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
20
74
|
const useSubscribeMessageReceivedEvent = () => {
|
|
21
75
|
const [settings] = useWidgetSettingsAtom()
|
|
22
76
|
const [, setWidgetLoading] = useWidgetLoadingAtom()
|
|
@@ -26,75 +80,78 @@ const useSubscribeMessageReceivedEvent = () => {
|
|
|
26
80
|
const useUpdateCursorMutation = useUpdateCursor()
|
|
27
81
|
const limit = useMessagesMaxCount()
|
|
28
82
|
const isAgentMode = useIsAgentParentAtomValue()
|
|
83
|
+
const { reset: resetLoadingTimeout, startTimeout } = useResponseTimeout()
|
|
29
84
|
|
|
30
85
|
const conversationId = useMemo(() => String(settings?.conversationId), [settings?.conversationId])
|
|
31
86
|
const profileId = useMemo(() => String(profileQuery?.data?.id), [profileQuery?.data?.id])
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
[conversationId, limit, profileId]
|
|
87
|
+
|
|
88
|
+
const messageIsMineCallback = useCallback(
|
|
89
|
+
(data: IMessageWithSenderData) => {
|
|
90
|
+
// The cursor should update only with my messages
|
|
91
|
+
useUpdateCursorMutation.mutate(data.conversationId)
|
|
92
|
+
},
|
|
93
|
+
[useUpdateCursorMutation]
|
|
40
94
|
)
|
|
41
95
|
|
|
42
|
-
const
|
|
96
|
+
const messageIsNotMineCallback = useCallback(
|
|
43
97
|
(data: IMessageWithSenderData) => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (idsList.has(data.id)) return
|
|
55
|
-
|
|
56
|
-
queryClient.setQueryData<InfiniteData<FetchMessagesResponse>>(
|
|
57
|
-
messagesQueryConfig.queryKey,
|
|
58
|
-
(oldData) => {
|
|
59
|
-
return produce(oldData, (draft) => {
|
|
60
|
-
draft?.pages.at(-1)?.messages?.push(data)
|
|
61
|
-
return draft
|
|
62
|
-
})
|
|
63
|
-
}
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
const isMine = data.contactId === profileId
|
|
67
|
-
|
|
68
|
-
if (!isMine) {
|
|
69
|
-
DataHubService.sendEvent({
|
|
70
|
-
schema: new ViewTutorAnswerMessageSchema({
|
|
71
|
-
correlationId: data.metadata.correlationId,
|
|
72
|
-
messageId: data.id,
|
|
73
|
-
componentSource: isAgentMode
|
|
74
|
-
? ComponentSource.PRODUCT_AGENT
|
|
75
|
-
: ComponentSource.HOTMART_TUTOR
|
|
76
|
-
})
|
|
98
|
+
setWidgetLoading(false)
|
|
99
|
+
|
|
100
|
+
DataHubService.sendEvent({
|
|
101
|
+
schema: new ViewTutorAnswerMessageSchema({
|
|
102
|
+
correlationId: data.metadata.correlationId,
|
|
103
|
+
messageId: data.id,
|
|
104
|
+
componentSource: isAgentMode
|
|
105
|
+
? ComponentSource.PRODUCT_AGENT
|
|
106
|
+
: ComponentSource.HOTMART_TUTOR
|
|
77
107
|
})
|
|
108
|
+
})
|
|
78
109
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
110
|
+
addUnreadMessagesToSet({ itemId: data.id })
|
|
111
|
+
},
|
|
112
|
+
[addUnreadMessagesToSet, isAgentMode, setWidgetLoading]
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
const errorCallback = useCallback(
|
|
116
|
+
(error: unknown) => {
|
|
117
|
+
if (error) {
|
|
118
|
+
setWidgetLoading(false)
|
|
119
|
+
console.error(error)
|
|
120
|
+
throw new Error('Error getting realtime messages')
|
|
84
121
|
}
|
|
85
122
|
},
|
|
123
|
+
[setWidgetLoading]
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
const messageReceived = useCallback(
|
|
127
|
+
(data: IMessageWithSenderData) => {
|
|
128
|
+
if (data.conversationId === conversationId)
|
|
129
|
+
messageReceivedUtil({
|
|
130
|
+
queryClient,
|
|
131
|
+
data,
|
|
132
|
+
conversationId,
|
|
133
|
+
profileId,
|
|
134
|
+
limit,
|
|
135
|
+
messageIsMineCallback,
|
|
136
|
+
messageIsNotMineCallback,
|
|
137
|
+
errorCallback
|
|
138
|
+
})
|
|
139
|
+
},
|
|
86
140
|
[
|
|
87
|
-
addUnreadMessagesToSet,
|
|
88
141
|
conversationId,
|
|
89
|
-
|
|
90
|
-
|
|
142
|
+
errorCallback,
|
|
143
|
+
limit,
|
|
144
|
+
messageIsMineCallback,
|
|
145
|
+
messageIsNotMineCallback,
|
|
91
146
|
profileId,
|
|
92
|
-
queryClient
|
|
93
|
-
setWidgetLoading,
|
|
94
|
-
useUpdateCursorMutation
|
|
147
|
+
queryClient
|
|
95
148
|
]
|
|
96
149
|
)
|
|
97
150
|
|
|
151
|
+
const startLoadingTimeout = useCallback(() => {
|
|
152
|
+
startTimeout(() => setWidgetLoading(false))
|
|
153
|
+
}, [setWidgetLoading, startTimeout])
|
|
154
|
+
|
|
98
155
|
useEffect(() => {
|
|
99
156
|
SparkieService.subscribeEvents({ messageReceived })
|
|
100
157
|
|
|
@@ -102,6 +159,14 @@ const useSubscribeMessageReceivedEvent = () => {
|
|
|
102
159
|
SparkieService.removeEventSubscription({ messageReceived })
|
|
103
160
|
}
|
|
104
161
|
}, [messageReceived])
|
|
162
|
+
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
startLoadingTimeout()
|
|
165
|
+
|
|
166
|
+
return () => {
|
|
167
|
+
resetLoadingTimeout()
|
|
168
|
+
}
|
|
169
|
+
}, [resetLoadingTimeout, startLoadingTimeout])
|
|
105
170
|
}
|
|
106
171
|
|
|
107
172
|
export default useSubscribeMessageReceivedEvent
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { Message } from '@hotmart/sparkie/dist/MessageService'
|
|
1
|
+
import type { Message } from '@hotmart-org-ca/sparkie/dist/MessageService'
|
|
2
|
+
import type MessageService from '@hotmart-org-ca/sparkie/dist/MessageService'
|
|
2
3
|
|
|
3
4
|
import { ApiError } from '@/src/config/request'
|
|
4
5
|
import { HttpCodes } from '@/src/lib/utils'
|
|
@@ -15,7 +16,7 @@ import type {
|
|
|
15
16
|
} from './types'
|
|
16
17
|
|
|
17
18
|
class MessagesService {
|
|
18
|
-
async getSparkieMessageService() {
|
|
19
|
+
async getSparkieMessageService(): Promise<MessageService> {
|
|
19
20
|
try {
|
|
20
21
|
const messageService = await SparkieService.getMessageService()
|
|
21
22
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Sparkie from '@hotmart/sparkie'
|
|
1
|
+
import Sparkie from '@hotmart-org-ca/sparkie'
|
|
2
2
|
|
|
3
3
|
import { MSG_MAX_COUNT } from '../messages'
|
|
4
4
|
|
|
@@ -110,8 +110,7 @@ class SparkieService {
|
|
|
110
110
|
|
|
111
111
|
while (attempt <= maxRetries) {
|
|
112
112
|
try {
|
|
113
|
-
if (!token
|
|
114
|
-
throw new Error('Invalid or missing token for Sparkie initialization')
|
|
113
|
+
if (!token?.trim()) throw new Error('Invalid or missing token for Sparkie initialization')
|
|
115
114
|
|
|
116
115
|
const sparkie = this.sparkieInstance
|
|
117
116
|
const service = await sparkie.init(token, { skipPresenceSetup })
|
|
@@ -1,21 +1,9 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react'
|
|
2
|
-
|
|
3
1
|
import { useSubscribeMessageReceivedEvent } from '@/src/modules/messages/hooks'
|
|
4
2
|
import { useSubscribeThreadClosedEvent } from '@/src/modules/thread/hooks'
|
|
5
3
|
import { useListenToVisibilityEvents } from '../../hooks'
|
|
6
4
|
import { useWidgetTabsAtom } from '../../store'
|
|
7
5
|
import { WIDGET_TABS } from '../constants'
|
|
8
6
|
|
|
9
|
-
// TODO: REMOVE
|
|
10
|
-
const hotmartRumKey = 'app-tutor-ai-consumer::hotmart-rum::activate'
|
|
11
|
-
const useSentryDebugger = () => {
|
|
12
|
-
const [logError] = useState(() => Boolean(window?.localStorage?.getItem?.(hotmartRumKey)))
|
|
13
|
-
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
if (logError) throw new Error(hotmartRumKey)
|
|
16
|
-
}, [logError])
|
|
17
|
-
}
|
|
18
|
-
|
|
19
7
|
function WidgetContainer() {
|
|
20
8
|
const [widgetTabs] = useWidgetTabsAtom()
|
|
21
9
|
|
|
@@ -23,8 +11,6 @@ function WidgetContainer() {
|
|
|
23
11
|
useSubscribeThreadClosedEvent()
|
|
24
12
|
useListenToVisibilityEvents()
|
|
25
13
|
|
|
26
|
-
useSentryDebugger()
|
|
27
|
-
|
|
28
14
|
return (
|
|
29
15
|
<div className='flex h-full flex-col items-center justify-stretch overflow-hidden'>
|
|
30
16
|
{WIDGET_TABS[widgetTabs.currentTab]}
|
package/src/types.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { QueryClient } from '@tanstack/react-query'
|
|
2
|
+
import type { Root } from 'react-dom/client'
|
|
3
|
+
|
|
1
4
|
import type { ILanguages } from './config/i18n'
|
|
2
5
|
|
|
3
6
|
export type StartTutorWidgetProps = {
|
|
@@ -68,3 +71,9 @@ export interface ICustomEvent<T = object> {
|
|
|
68
71
|
handler: (listener: EventListenerOrEventListenerObject) => () => void | Promise<void>
|
|
69
72
|
dispatch: <D = unknown>(detail?: D) => void
|
|
70
73
|
}
|
|
74
|
+
|
|
75
|
+
export type WidgetInstance = {
|
|
76
|
+
root: Root
|
|
77
|
+
container: HTMLElement
|
|
78
|
+
queryClient: QueryClient
|
|
79
|
+
}
|