@botonic/react 0.30.6-alpha.3 → 0.30.7-alpha.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/lib/cjs/contexts.js +3 -0
- package/lib/cjs/contexts.js.map +1 -1
- package/lib/cjs/index-types.d.ts +1 -0
- package/lib/cjs/webchat/actions.d.ts +2 -1
- package/lib/cjs/webchat/actions.js +1 -0
- package/lib/cjs/webchat/actions.js.map +1 -1
- package/lib/cjs/webchat/hooks/use-webchat.d.ts +1 -0
- package/lib/cjs/webchat/hooks/use-webchat.js +8 -0
- package/lib/cjs/webchat/hooks/use-webchat.js.map +1 -1
- package/lib/cjs/webchat/index-types.d.ts +1 -0
- package/lib/cjs/webchat/input-panel/textarea.js +8 -2
- package/lib/cjs/webchat/input-panel/textarea.js.map +1 -1
- package/lib/cjs/webchat/message-list/index.js +63 -35
- package/lib/cjs/webchat/message-list/index.js.map +1 -1
- package/lib/cjs/webchat/typing-indicator/index.d.ts +3 -1
- package/lib/cjs/webchat/typing-indicator/index.js +4 -3
- package/lib/cjs/webchat/typing-indicator/index.js.map +1 -1
- package/lib/cjs/webchat/typing-indicator/styles.d.ts +3 -2
- package/lib/cjs/webchat/typing-indicator/styles.js +6 -3
- package/lib/cjs/webchat/typing-indicator/styles.js.map +1 -1
- package/lib/cjs/webchat/webchat-reducer.js +2 -0
- package/lib/cjs/webchat/webchat-reducer.js.map +1 -1
- package/lib/cjs/webchat/webchat.js +2 -1
- package/lib/cjs/webchat/webchat.js.map +1 -1
- package/lib/esm/contexts.js +3 -0
- package/lib/esm/contexts.js.map +1 -1
- package/lib/esm/index-types.d.ts +1 -0
- package/lib/esm/webchat/actions.d.ts +2 -1
- package/lib/esm/webchat/actions.js +1 -0
- package/lib/esm/webchat/actions.js.map +1 -1
- package/lib/esm/webchat/hooks/use-webchat.d.ts +1 -0
- package/lib/esm/webchat/hooks/use-webchat.js +8 -0
- package/lib/esm/webchat/hooks/use-webchat.js.map +1 -1
- package/lib/esm/webchat/index-types.d.ts +1 -0
- package/lib/esm/webchat/input-panel/textarea.js +8 -2
- package/lib/esm/webchat/input-panel/textarea.js.map +1 -1
- package/lib/esm/webchat/message-list/index.js +62 -35
- package/lib/esm/webchat/message-list/index.js.map +1 -1
- package/lib/esm/webchat/typing-indicator/index.d.ts +3 -1
- package/lib/esm/webchat/typing-indicator/index.js +5 -2
- package/lib/esm/webchat/typing-indicator/index.js.map +1 -1
- package/lib/esm/webchat/typing-indicator/styles.d.ts +3 -2
- package/lib/esm/webchat/typing-indicator/styles.js +5 -2
- package/lib/esm/webchat/typing-indicator/styles.js.map +1 -1
- package/lib/esm/webchat/webchat-reducer.js +2 -0
- package/lib/esm/webchat/webchat-reducer.js.map +1 -1
- package/lib/esm/webchat/webchat.js +2 -1
- package/lib/esm/webchat/webchat.js.map +1 -1
- package/package.json +1 -1
- package/src/contexts.tsx +3 -0
- package/src/index-types.ts +1 -0
- package/src/webchat/actions.ts +1 -0
- package/src/webchat/hooks/use-webchat.ts +9 -0
- package/src/webchat/index-types.ts +1 -0
- package/src/webchat/input-panel/textarea.tsx +12 -1
- package/src/webchat/message-list/index.tsx +79 -48
- package/src/webchat/typing-indicator/index.tsx +20 -12
- package/src/webchat/typing-indicator/styles.ts +7 -3
- package/src/webchat/webchat-reducer.ts +2 -0
- package/src/webchat/webchat.jsx +2 -0
|
@@ -3,13 +3,23 @@ import React, { useContext, useEffect, useRef, useState } from 'react'
|
|
|
3
3
|
import { ROLES } from '../../constants'
|
|
4
4
|
import { WebchatContext } from '../../contexts'
|
|
5
5
|
import { BotonicContainerId } from '../constants'
|
|
6
|
-
import
|
|
6
|
+
import TypingIndicator from '../typing-indicator'
|
|
7
7
|
import { IntroMessage } from './intro-message'
|
|
8
8
|
import { ScrollButton } from './scroll-button'
|
|
9
9
|
import { ContainerMessage, ScrollableMessageList } from './styles'
|
|
10
10
|
import { UnreadMessagesBanner } from './unread-messages-banner'
|
|
11
11
|
import { useNotifications } from './use-notifications'
|
|
12
12
|
|
|
13
|
+
const SCROLL_TIMEOUT = 100
|
|
14
|
+
const scrollOptionsEnd: ScrollIntoViewOptions = {
|
|
15
|
+
behavior: 'smooth',
|
|
16
|
+
block: 'end',
|
|
17
|
+
}
|
|
18
|
+
const scrollOptionsCenter: ScrollIntoViewOptions = {
|
|
19
|
+
behavior: 'smooth',
|
|
20
|
+
block: 'center',
|
|
21
|
+
}
|
|
22
|
+
|
|
13
23
|
export const WebchatMessageList = () => {
|
|
14
24
|
const {
|
|
15
25
|
webchatState,
|
|
@@ -18,39 +28,50 @@ export const WebchatMessageList = () => {
|
|
|
18
28
|
scrollableMessagesListRef,
|
|
19
29
|
} = useContext(WebchatContext)
|
|
20
30
|
|
|
21
|
-
const
|
|
31
|
+
const { notificationsEnabled } = useNotifications()
|
|
22
32
|
|
|
23
|
-
const
|
|
33
|
+
const [firstUnreadMessageId, setFirstUnreadMessageId] = useState<string>()
|
|
24
34
|
|
|
25
|
-
const
|
|
35
|
+
const lastMessageRef = useRef<HTMLDivElement>(null)
|
|
36
|
+
const typingRef = useRef<HTMLDivElement>(null)
|
|
37
|
+
const unreadMessagesBannerRef = useRef<HTMLDivElement>(null)
|
|
38
|
+
|
|
39
|
+
const scrollToTyping = () => {
|
|
26
40
|
setTimeout(() => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
block: 'end',
|
|
30
|
-
})
|
|
31
|
-
}, 100)
|
|
41
|
+
typingRef.current?.scrollIntoView(scrollOptionsEnd)
|
|
42
|
+
}, SCROLL_TIMEOUT)
|
|
32
43
|
}
|
|
33
44
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
const scrollToLastMessage = () => {
|
|
46
|
+
setTimeout(() => {
|
|
47
|
+
lastMessageRef.current?.scrollIntoView(scrollOptionsEnd)
|
|
48
|
+
}, SCROLL_TIMEOUT)
|
|
37
49
|
}
|
|
38
50
|
|
|
39
|
-
const unreadMessagesBannerRef = useRef<HTMLDivElement>(null)
|
|
40
|
-
|
|
41
51
|
const scrollToBanner = () => {
|
|
42
52
|
setTimeout(() => {
|
|
43
|
-
unreadMessagesBannerRef.current?.scrollIntoView(
|
|
44
|
-
|
|
45
|
-
block: 'center',
|
|
46
|
-
})
|
|
47
|
-
}, 100)
|
|
53
|
+
unreadMessagesBannerRef.current?.scrollIntoView(scrollOptionsCenter)
|
|
54
|
+
}, SCROLL_TIMEOUT)
|
|
48
55
|
}
|
|
49
56
|
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
const handleScrollToBottom = () => {
|
|
58
|
+
resetUnreadMessages()
|
|
59
|
+
if (webchatState.typing) {
|
|
60
|
+
scrollToTyping()
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
scrollToLastMessage()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const showUnreadMessagesBanner = (messageComponentId: string) => {
|
|
68
|
+
return (
|
|
69
|
+
!webchatState.isInputFocused &&
|
|
70
|
+
firstUnreadMessageId &&
|
|
71
|
+
messageComponentId === firstUnreadMessageId &&
|
|
72
|
+
webchatState.numUnreadMessages > 0
|
|
73
|
+
)
|
|
74
|
+
}
|
|
54
75
|
|
|
55
76
|
useEffect(() => {
|
|
56
77
|
const firstUnreadMessage = webchatState.messagesComponents.find(
|
|
@@ -60,39 +81,47 @@ export const WebchatMessageList = () => {
|
|
|
60
81
|
}, [webchatState.messagesComponents])
|
|
61
82
|
|
|
62
83
|
useEffect(() => {
|
|
63
|
-
if (
|
|
64
|
-
webchatState.messagesComponents.length > 0 &&
|
|
65
|
-
lastMessageBottomRef.current
|
|
66
|
-
) {
|
|
84
|
+
if (webchatState.messagesComponents.length > 0 && lastMessageRef.current) {
|
|
67
85
|
const observer = new IntersectionObserver(entries => {
|
|
68
86
|
entries.forEach(entry => {
|
|
69
87
|
setLastMessageVisible(entry.isIntersecting)
|
|
70
88
|
})
|
|
71
89
|
})
|
|
72
|
-
observer.observe(
|
|
90
|
+
observer.observe(lastMessageRef.current)
|
|
73
91
|
}
|
|
74
92
|
}, [webchatState.messagesComponents])
|
|
75
93
|
|
|
76
|
-
const { notificationsEnabled } = useNotifications()
|
|
77
|
-
|
|
78
94
|
useEffect(() => {
|
|
79
95
|
if (!notificationsEnabled) {
|
|
80
|
-
|
|
81
|
-
|
|
96
|
+
if (webchatState.typing) {
|
|
97
|
+
scrollToTyping()
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
scrollToLastMessage()
|
|
82
102
|
}
|
|
83
|
-
}, [webchatState.typing])
|
|
103
|
+
}, [webchatState.typing, webchatState.messagesComponents])
|
|
84
104
|
|
|
85
105
|
useEffect(() => {
|
|
86
|
-
if (notificationsEnabled) {
|
|
87
|
-
if (
|
|
106
|
+
if (webchatState.isWebchatOpen && notificationsEnabled) {
|
|
107
|
+
if (unreadMessagesBannerRef.current) {
|
|
88
108
|
scrollToBanner()
|
|
89
109
|
return
|
|
90
110
|
}
|
|
91
111
|
|
|
92
|
-
|
|
93
|
-
|
|
112
|
+
if (webchatState.typing) {
|
|
113
|
+
scrollToTyping()
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
scrollToLastMessage()
|
|
94
118
|
}
|
|
95
|
-
}, [
|
|
119
|
+
}, [
|
|
120
|
+
firstUnreadMessageId,
|
|
121
|
+
webchatState.isWebchatOpen,
|
|
122
|
+
webchatState.typing,
|
|
123
|
+
webchatState.messagesComponents,
|
|
124
|
+
])
|
|
96
125
|
|
|
97
126
|
const showScrollButton =
|
|
98
127
|
webchatState.numUnreadMessages > 0 && !webchatState.isLastMessageVisible
|
|
@@ -107,25 +136,27 @@ export const WebchatMessageList = () => {
|
|
|
107
136
|
<IntroMessage />
|
|
108
137
|
{webchatState.messagesComponents.map((messageComponent, index) => {
|
|
109
138
|
return (
|
|
110
|
-
|
|
111
|
-
{
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
139
|
+
<>
|
|
140
|
+
<ContainerMessage role={ROLES.MESSAGE} key={index}>
|
|
141
|
+
{showUnreadMessagesBanner(messageComponent.props.id) && (
|
|
142
|
+
<UnreadMessagesBanner
|
|
143
|
+
unreadMessagesBannerRef={unreadMessagesBannerRef}
|
|
144
|
+
/>
|
|
145
|
+
)}
|
|
146
|
+
{messageComponent}
|
|
147
|
+
</ContainerMessage>
|
|
117
148
|
{index === webchatState.messagesComponents.length - 1 && (
|
|
118
149
|
<div
|
|
119
|
-
ref={
|
|
150
|
+
ref={lastMessageRef}
|
|
120
151
|
style={{
|
|
121
152
|
content: '',
|
|
122
153
|
}}
|
|
123
154
|
></div>
|
|
124
155
|
)}
|
|
125
|
-
|
|
156
|
+
</>
|
|
126
157
|
)
|
|
127
158
|
})}
|
|
128
|
-
{webchatState.typing && <TypingIndicator />}
|
|
159
|
+
{webchatState.typing && <TypingIndicator ref={typingRef} />}
|
|
129
160
|
</ScrollableMessageList>
|
|
130
161
|
{showScrollButton && <ScrollButton handleClick={handleScrollToBottom} />}
|
|
131
162
|
</>
|
|
@@ -1,16 +1,24 @@
|
|
|
1
|
-
import React from 'react'
|
|
1
|
+
import React, { ForwardedRef, forwardRef } from 'react'
|
|
2
2
|
|
|
3
3
|
import { COLORS, ROLES } from '../../constants'
|
|
4
|
-
import { Dot,
|
|
4
|
+
import { Dot, TypingContainer, TypingMsgWrapper } from './styles'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
6
|
+
const TypingIndicator = forwardRef<HTMLDivElement, any>(
|
|
7
|
+
(_props, ref: ForwardedRef<HTMLDivElement>) => (
|
|
8
|
+
<TypingContainer ref={ref}>
|
|
9
|
+
<TypingMsgWrapper
|
|
10
|
+
role={ROLES.TYPING_INDICATOR}
|
|
11
|
+
className='typing-indicator'
|
|
12
|
+
backgroundColor={COLORS.SEASHELL_WHITE}
|
|
13
|
+
>
|
|
14
|
+
<Dot />
|
|
15
|
+
<Dot />
|
|
16
|
+
<Dot />
|
|
17
|
+
</TypingMsgWrapper>
|
|
18
|
+
</TypingContainer>
|
|
19
|
+
)
|
|
16
20
|
)
|
|
21
|
+
|
|
22
|
+
TypingIndicator.displayName = 'TypingIndicator'
|
|
23
|
+
|
|
24
|
+
export default TypingIndicator
|
|
@@ -12,16 +12,20 @@ const bulge = keyframes`
|
|
|
12
12
|
}
|
|
13
13
|
`
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
export const TypingContainer = styled.div`
|
|
16
|
+
padding: 0px 8px 8px 8px;
|
|
17
|
+
`
|
|
18
|
+
|
|
19
|
+
interface TypingMsgWrapperProps {
|
|
16
20
|
backgroundColor: string
|
|
17
21
|
}
|
|
18
22
|
|
|
19
|
-
export const
|
|
23
|
+
export const TypingMsgWrapper = styled.div<TypingMsgWrapperProps>`
|
|
20
24
|
will-change: transform;
|
|
21
25
|
width: 44px;
|
|
22
26
|
line-height: 0px;
|
|
23
27
|
border-radius: 20px;
|
|
24
|
-
padding: 8px 2px
|
|
28
|
+
padding: 8px 2px;
|
|
25
29
|
text-align: center;
|
|
26
30
|
display: block;
|
|
27
31
|
margin: 8px;
|
|
@@ -48,6 +48,8 @@ export function webchatReducer(
|
|
|
48
48
|
return { ...state, lastRoutePath: action.payload }
|
|
49
49
|
case WebchatAction.SET_CURRENT_ATTACHMENT:
|
|
50
50
|
return { ...state, currentAttachment: action.payload }
|
|
51
|
+
case WebchatAction.SET_IS_INPUT_FOCUSED:
|
|
52
|
+
return { ...state, isInputFocused: action.payload }
|
|
51
53
|
default:
|
|
52
54
|
return messagesReducer(state, action)
|
|
53
55
|
}
|
package/src/webchat/webchat.jsx
CHANGED
|
@@ -121,6 +121,7 @@ export const Webchat = forwardRef((props, ref) => {
|
|
|
121
121
|
resetUnreadMessages,
|
|
122
122
|
setCurrentAttachment,
|
|
123
123
|
setError,
|
|
124
|
+
setIsInputFocused,
|
|
124
125
|
setLastMessageVisible,
|
|
125
126
|
setOnline,
|
|
126
127
|
toggleCoverComponent,
|
|
@@ -682,6 +683,7 @@ export const Webchat = forwardRef((props, ref) => {
|
|
|
682
683
|
openWebview,
|
|
683
684
|
resolveCase,
|
|
684
685
|
resetUnreadMessages,
|
|
686
|
+
setIsInputFocused,
|
|
685
687
|
setLastMessageVisible,
|
|
686
688
|
sendAttachment,
|
|
687
689
|
sendInput,
|