@seamly/web-ui 20.3.0 → 20.5.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/build/dist/lib/index.debug.js +61 -94
- package/build/dist/lib/index.debug.min.js +1 -1
- package/build/dist/lib/index.debug.min.js.LICENSE.txt +4 -16
- package/build/dist/lib/index.js +467 -3965
- package/build/dist/lib/index.min.js +1 -1
- package/build/dist/lib/index.min.js.LICENSE.txt +0 -5
- package/build/dist/lib/standalone.js +432 -3865
- package/build/dist/lib/standalone.min.js +1 -1
- package/build/dist/lib/style-guide.js +267 -191
- package/build/dist/lib/style-guide.min.js +1 -1
- package/build/dist/lib/styles.css +1 -1
- package/package.json +2 -4
- package/src/.DS_Store +0 -0
- package/src/javascripts/api/index.js +17 -1
- package/src/javascripts/domains/config/reducer.js +2 -0
- package/src/javascripts/domains/forms/provider.js +14 -6
- package/src/javascripts/domains/store/state-reducer.js +1 -0
- package/src/javascripts/domains/visibility/actions.js +2 -0
- package/src/javascripts/domains/visibility/hooks.js +60 -1
- package/src/javascripts/domains/visibility/reducer.js +5 -0
- package/src/javascripts/domains/visibility/selectors.js +5 -0
- package/src/javascripts/domains/visibility/utils.js +5 -1
- package/src/javascripts/index.js +1 -0
- package/src/javascripts/style-guide/components/app.js +2 -0
- package/src/javascripts/style-guide/states.js +55 -50
- package/src/javascripts/ui/components/conversation/conversation.js +9 -10
- package/src/javascripts/ui/components/conversation/event/card-component.js +1 -2
- package/src/javascripts/ui/components/conversation/event/conversation-suggestions.js +19 -9
- package/src/javascripts/ui/components/conversation/event/cta.js +1 -2
- package/src/javascripts/ui/components/conversation/event/image.js +11 -3
- package/src/javascripts/ui/components/conversation/event/participant.js +2 -11
- package/src/javascripts/ui/components/conversation/event/splash.js +1 -3
- package/src/javascripts/ui/components/conversation/event/text.js +9 -9
- package/src/javascripts/ui/components/entry/text-entry/text-entry-form.js +6 -0
- package/src/javascripts/ui/components/layout/chat.js +52 -48
- package/src/javascripts/ui/components/suggestions/suggestions-list.js +12 -14
- package/src/javascripts/ui/components/view/deprecated-view.js +16 -11
- package/src/javascripts/ui/components/view/index.js +2 -2
- package/src/javascripts/ui/components/view/inline-view.js +13 -8
- package/src/javascripts/ui/hooks/seamly-entry-hooks.js +3 -2
- package/src/javascripts/ui/hooks/seamly-state-hooks.js +7 -3
- package/src/javascripts/ui/hooks/use-seamly-chat.js +41 -29
- package/src/javascripts/ui/hooks/use-seamly-commands.js +16 -4
- package/src/javascripts/ui/utils/seamly-utils.js +24 -7
- package/src/stylesheets/5-components/_message-count.scss +5 -2
- package/CHANGELOG.md +0 -729
- package/src/javascripts/lib/parse-body.js +0 -10
- package/src/javascripts/ui/components/conversation/event/hooks/use-text-rendering.js +0 -35
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { forwardRef } from 'preact/compat'
|
|
1
2
|
import { className } from '../../../lib/css'
|
|
2
3
|
import {
|
|
3
4
|
useSeamlyAppContainerClassNames,
|
|
@@ -9,54 +10,57 @@ import { useI18n } from '../../../domains/i18n'
|
|
|
9
10
|
import { useVisibility, visibilityStates } from '../../../domains/visibility'
|
|
10
11
|
import Suggestions from '../suggestions'
|
|
11
12
|
|
|
12
|
-
const Chat = (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
13
|
+
const Chat = forwardRef(
|
|
14
|
+
({ children, className: givenClassName = '' }, forwardedRef) => {
|
|
15
|
+
const { isOpen, isVisible, setVisibility } = useVisibility()
|
|
16
|
+
const { namespace, layoutMode } = useConfig()
|
|
17
|
+
const { isInline } = useSeamlyLayoutMode()
|
|
18
|
+
const appContainerClassNames = useSeamlyAppContainerClassNames()
|
|
19
|
+
const userResponded = useUserHasResponded()
|
|
20
|
+
const { t } = useI18n()
|
|
21
|
+
|
|
22
|
+
const defaultClassNames = [
|
|
23
|
+
'chat',
|
|
24
|
+
`chat--layout-${layoutMode}`,
|
|
25
|
+
`namespace--${namespace}`,
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
const classNames = [
|
|
29
|
+
...defaultClassNames,
|
|
30
|
+
...appContainerClassNames,
|
|
31
|
+
givenClassName,
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
if (!isOpen && layoutMode !== 'app') {
|
|
35
|
+
classNames.push('chat--collapsed')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (userResponded) {
|
|
39
|
+
classNames.push('chat--user-responded')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const onKeyDownHandler = (e) => {
|
|
43
|
+
if ((e.code && e.code === 'Escape') || e.keyCode === 27)
|
|
44
|
+
if (!isInline && isOpen) {
|
|
45
|
+
setVisibility(visibilityStates.minimized)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
isVisible && (
|
|
51
|
+
<section
|
|
52
|
+
className={className(classNames)}
|
|
53
|
+
onKeyDown={onKeyDownHandler}
|
|
54
|
+
tabIndex="-1"
|
|
55
|
+
ref={forwardedRef}
|
|
56
|
+
aria-label={t('chat.srLabel')}
|
|
57
|
+
>
|
|
58
|
+
<div className={className('chat-wrapper')}>{children}</div>
|
|
59
|
+
{layoutMode === 'inline' && isOpen && <Suggestions isAside={true} />}
|
|
60
|
+
</section>
|
|
61
|
+
)
|
|
58
62
|
)
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
},
|
|
64
|
+
)
|
|
61
65
|
|
|
62
66
|
export default Chat
|
|
@@ -6,19 +6,17 @@ const SuggestionsList = ({
|
|
|
6
6
|
suggestions = [],
|
|
7
7
|
onClickSuggestion,
|
|
8
8
|
hasIcon = true,
|
|
9
|
-
}) =>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
)
|
|
22
|
-
}
|
|
9
|
+
}) => (
|
|
10
|
+
<ul className={className('suggestions__list', givenClassName)}>
|
|
11
|
+
{suggestions.map((suggestion) => (
|
|
12
|
+
<SuggestionsItem
|
|
13
|
+
hasIcon={hasIcon}
|
|
14
|
+
key={suggestion.id}
|
|
15
|
+
onClick={onClickSuggestion}
|
|
16
|
+
{...suggestion}
|
|
17
|
+
/>
|
|
18
|
+
))}
|
|
19
|
+
</ul>
|
|
20
|
+
)
|
|
23
21
|
|
|
24
22
|
export default SuggestionsList
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useVisibility, useShowInlineView } from 'domains/visibility'
|
|
1
2
|
import DeprecatedAppFrame from '../layout/deprecated-app-frame'
|
|
2
3
|
import ChatFrame from '../layout/chat-frame'
|
|
3
4
|
import AgentInfo from '../layout/agent-info'
|
|
@@ -6,25 +7,29 @@ import Conversation from '../conversation/conversation'
|
|
|
6
7
|
import EntryContainer from '../entry/entry-container'
|
|
7
8
|
import Interrupt from '../layout/interrupt'
|
|
8
9
|
import { useSeamlyChat } from '../../hooks/seamly-hooks'
|
|
9
|
-
import { useVisibility } from '../../../domains/visibility'
|
|
10
10
|
import DeprecatedToggleButton from '../entry/deprecated-toggle-button'
|
|
11
11
|
|
|
12
12
|
const DeprecatedView = () => {
|
|
13
13
|
const { isVisible } = useVisibility()
|
|
14
14
|
const { openChat, closeChat } = useSeamlyChat()
|
|
15
|
+
const { showInlineView, containerRef } = useShowInlineView()
|
|
15
16
|
|
|
16
17
|
return (
|
|
17
18
|
isVisible && (
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
<div ref={containerRef}>
|
|
20
|
+
{showInlineView && (
|
|
21
|
+
<DeprecatedAppFrame>
|
|
22
|
+
<DeprecatedToggleButton onOpenChat={openChat} />
|
|
23
|
+
<Header onCloseChat={closeChat}>
|
|
24
|
+
<AgentInfo />
|
|
25
|
+
</Header>
|
|
26
|
+
<ChatFrame interruptComponent={Interrupt}>
|
|
27
|
+
<Conversation />
|
|
28
|
+
<EntryContainer />
|
|
29
|
+
</ChatFrame>
|
|
30
|
+
</DeprecatedAppFrame>
|
|
31
|
+
)}
|
|
32
|
+
</div>
|
|
28
33
|
)
|
|
29
34
|
)
|
|
30
35
|
}
|
|
@@ -16,7 +16,7 @@ const ViewComponentsMap = {
|
|
|
16
16
|
window: WindowView,
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
const View = () => {
|
|
19
|
+
const View = ({ children }) => {
|
|
20
20
|
const [, setSeamlyContainerElement] = useSeamlyContainerElement()
|
|
21
21
|
const { namespace, layoutMode, zIndex } = useConfig()
|
|
22
22
|
const { isOpen, isVisible } = useVisibility()
|
|
@@ -74,7 +74,7 @@ const View = () => {
|
|
|
74
74
|
style={{ zIndex }}
|
|
75
75
|
ref={containerElementRef}
|
|
76
76
|
>
|
|
77
|
-
<ViewComponent />
|
|
77
|
+
{children || <ViewComponent />}
|
|
78
78
|
</div>
|
|
79
79
|
)
|
|
80
80
|
)
|
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
import { className } from '
|
|
1
|
+
import { className } from 'lib/css'
|
|
2
|
+
import { useVisibility, useShowInlineView } from 'domains/visibility'
|
|
3
|
+
import { useInterrupt } from 'domains/interrupt'
|
|
2
4
|
import Chat from '../layout/chat'
|
|
3
5
|
import Interrupt from '../layout/interrupt'
|
|
4
6
|
import Conversation from '../conversation/conversation'
|
|
5
7
|
import EntryContainer from '../entry/entry-container'
|
|
6
8
|
import ChatFrame from '../layout/chat-frame'
|
|
7
9
|
import useSeamlyChat from '../../hooks/use-seamly-chat'
|
|
8
|
-
import { useVisibility } from '../../../domains/visibility'
|
|
9
10
|
import PreChatMessages from '../layout/pre-chat-messages'
|
|
10
11
|
import Suggestions from '../suggestions'
|
|
11
12
|
import InOutTransition, {
|
|
12
13
|
transitionStartStates,
|
|
13
14
|
} from '../widgets/in-out-transition'
|
|
14
|
-
import { useInterrupt } from '../../../domains/interrupt'
|
|
15
15
|
|
|
16
16
|
const InlineView = () => {
|
|
17
17
|
useSeamlyChat()
|
|
18
|
+
const { showInlineView, containerRef } = useShowInlineView()
|
|
19
|
+
|
|
18
20
|
const { isOpen } = useVisibility()
|
|
19
21
|
const { hasInterrupt, meta } = useInterrupt()
|
|
20
22
|
|
|
@@ -29,6 +31,7 @@ const InlineView = () => {
|
|
|
29
31
|
transitionStartState={transitionStartStates.rendered}
|
|
30
32
|
>
|
|
31
33
|
<div
|
|
34
|
+
ref={containerRef}
|
|
32
35
|
className={className(
|
|
33
36
|
'unstarted-wrapper',
|
|
34
37
|
'unstarted-wrapper--inline',
|
|
@@ -42,11 +45,13 @@ const InlineView = () => {
|
|
|
42
45
|
isActive={isOpen}
|
|
43
46
|
transitionStartState={transitionStartStates.rendered}
|
|
44
47
|
>
|
|
45
|
-
<Chat>
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
<Chat ref={containerRef}>
|
|
49
|
+
{showInlineView && (
|
|
50
|
+
<ChatFrame interruptComponent={Interrupt}>
|
|
51
|
+
{isOpen && <Conversation />}
|
|
52
|
+
<EntryContainer />
|
|
53
|
+
</ChatFrame>
|
|
54
|
+
)}
|
|
50
55
|
</Chat>
|
|
51
56
|
</InOutTransition>
|
|
52
57
|
</>
|
|
@@ -80,12 +80,13 @@ export const useSeamlyEntry = () => {
|
|
|
80
80
|
active,
|
|
81
81
|
userSelected,
|
|
82
82
|
options: entryOptions,
|
|
83
|
+
optionsOverride: entryOptionsOverride,
|
|
83
84
|
} = useSeamlyStateContext().entryMeta
|
|
84
85
|
const dispatch = useSeamlyDispatchContext()
|
|
85
86
|
|
|
86
87
|
const activeEntry = userSelected || active || defaultEntry
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
const activeEntryOptions =
|
|
89
|
+
entryOptionsOverride[activeEntry] || entryOptions[activeEntry] || {}
|
|
89
90
|
|
|
90
91
|
const setBlockAutoEntrySwitch = useCallback(
|
|
91
92
|
(value) => {
|
|
@@ -71,6 +71,9 @@ export const useSeamlyHeaderData = () => useSeamlyStateContext().headerTitles
|
|
|
71
71
|
|
|
72
72
|
export const useSeamlyUnreadCount = () => useSeamlyStateContext().unreadEvents
|
|
73
73
|
|
|
74
|
+
export const useLoadedImageEventIds = () =>
|
|
75
|
+
useSeamlyStateContext().loadedImageEventIds
|
|
76
|
+
|
|
74
77
|
export const useSkiplink = () => useSeamlyStateContext().skiplinkTargetId
|
|
75
78
|
|
|
76
79
|
export const useSeamlyParticipant = (participantId) =>
|
|
@@ -100,14 +103,15 @@ export const useEntryTextLimit = () => {
|
|
|
100
103
|
const {
|
|
101
104
|
entryMeta: {
|
|
102
105
|
options: { text },
|
|
106
|
+
optionsOverride: { text: overrideText },
|
|
103
107
|
},
|
|
104
108
|
} = useSeamlyStateContext()
|
|
105
109
|
|
|
106
|
-
const { limit } = text || {}
|
|
110
|
+
const { limit } = overrideText || text || { limit: null }
|
|
107
111
|
|
|
108
112
|
return {
|
|
109
|
-
hasLimit: limit
|
|
110
|
-
limit: limit
|
|
113
|
+
hasLimit: limit !== null,
|
|
114
|
+
limit: limit !== null ? limit : null,
|
|
111
115
|
}
|
|
112
116
|
}
|
|
113
117
|
|
|
@@ -1,29 +1,31 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'preact/hooks'
|
|
1
|
+
import { useCallback, useEffect, useRef } from 'preact/hooks'
|
|
2
2
|
import { useI18n } from 'domains/i18n'
|
|
3
3
|
import { seamlyActions } from 'ui/utils/seamly-utils'
|
|
4
4
|
import { useVisibility, visibilityStates } from 'domains/visibility'
|
|
5
5
|
import useSeamlyDispatchContext from './use-seamly-dispatch'
|
|
6
|
-
import { useEvents } from './seamly-state-hooks'
|
|
6
|
+
import { useEvents, useSeamlyLayoutMode } from './seamly-state-hooks'
|
|
7
7
|
import useSeamlyCommands from './use-seamly-commands'
|
|
8
8
|
import { useSeamlyHasConversation } from './seamly-api-hooks'
|
|
9
9
|
import { useLiveRegion } from './live-region-hooks'
|
|
10
|
-
import {
|
|
10
|
+
import { useSelector } from '../../domains/redux/hooks'
|
|
11
|
+
import { selectShowInlineView } from '../../domains/visibility/selectors'
|
|
11
12
|
|
|
12
13
|
const { SET_IS_LOADING } = seamlyActions
|
|
13
14
|
|
|
14
15
|
const useSeamlyChat = () => {
|
|
15
16
|
const { t } = useI18n()
|
|
16
|
-
const {
|
|
17
|
+
const { isInline, isWindow } = useSeamlyLayoutMode()
|
|
17
18
|
const { isOpen, isVisible, setVisibility } = useVisibility()
|
|
19
|
+
const showInlineView = useSelector(selectShowInlineView)
|
|
18
20
|
const dispatch = useSeamlyDispatchContext()
|
|
19
21
|
const events = useEvents()
|
|
20
22
|
const spinnerTimeout = useRef(null)
|
|
21
|
-
const { start, connect, apiConfigReady } = useSeamlyCommands()
|
|
23
|
+
const { start, connect, apiConfigReady, apiConnected } = useSeamlyCommands()
|
|
22
24
|
const hasConversation = useSeamlyHasConversation()
|
|
23
25
|
const prevIsOpen = useRef(null)
|
|
24
26
|
const prevIsVisible = useRef(null)
|
|
27
|
+
const startCalled = useRef(false)
|
|
25
28
|
const { sendAssertive } = useLiveRegion()
|
|
26
|
-
const connectCalled = useRef(false)
|
|
27
29
|
|
|
28
30
|
const hasEvents = events.length > 0
|
|
29
31
|
|
|
@@ -76,28 +78,41 @@ const useSeamlyChat = () => {
|
|
|
76
78
|
}, [hasEvents, dispatch])
|
|
77
79
|
|
|
78
80
|
useEffect(() => {
|
|
79
|
-
// This is needed to reset the ref to allow connect to happen again.
|
|
81
|
+
// This is needed to reset the ref to allow connect and start to happen again.
|
|
80
82
|
// Mostly due to Interrupt situations and a reset being called.
|
|
81
|
-
if (!
|
|
82
|
-
|
|
83
|
+
if (!apiConfigReady || !apiConnected) {
|
|
84
|
+
startCalled.current = false
|
|
83
85
|
}
|
|
84
|
-
}, [
|
|
86
|
+
}, [apiConfigReady, apiConnected])
|
|
85
87
|
|
|
86
|
-
|
|
87
|
-
// We don't connect
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
const connectAndStart = useCallback(async () => {
|
|
89
|
+
// We don't connect if we are already connected to the api to avoid multiple in-flight connection processes.
|
|
90
|
+
if (!apiConnected) {
|
|
91
|
+
await connect()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// We only start a conversation when the chat interface is either 'open' or if using the inline view if it's 'open' or 'minimized'.
|
|
95
|
+
if (isOpen || (isVisible && isInline)) {
|
|
96
|
+
start()
|
|
97
|
+
startCalled.current = true
|
|
98
|
+
}
|
|
99
|
+
}, [apiConnected, connect, isInline, isOpen, isVisible, start])
|
|
91
100
|
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
// We dont't connect or start when the apiConfig is not ready yet.
|
|
103
|
+
// We also keep track of whether start has been called to avoid multiple in-flight connection processes.
|
|
104
|
+
// We check if the window view is not open and no conversation is started yet.
|
|
105
|
+
// Lastly we check if the inline view is not scrolled in to view.
|
|
92
106
|
if (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
!
|
|
107
|
+
!apiConfigReady ||
|
|
108
|
+
startCalled.current ||
|
|
109
|
+
(isWindow && !isOpen && !hasConversation) ||
|
|
110
|
+
(isInline && !showInlineView)
|
|
96
111
|
) {
|
|
97
112
|
return
|
|
98
113
|
}
|
|
99
114
|
|
|
100
|
-
if (hasConversation) {
|
|
115
|
+
if (hasConversation && isOpen) {
|
|
101
116
|
// We deactivate the extra startup loading spinner when a conversation is available
|
|
102
117
|
// We also stop setting the loading indicator in the first place to avoid a flash.
|
|
103
118
|
clearTimeout(spinnerTimeout.current)
|
|
@@ -106,19 +121,16 @@ const useSeamlyChat = () => {
|
|
|
106
121
|
isLoading: false,
|
|
107
122
|
})
|
|
108
123
|
}
|
|
109
|
-
|
|
110
|
-
start()
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
connectCalled.current = true
|
|
124
|
+
connectAndStart()
|
|
114
125
|
}, [
|
|
115
|
-
isOpen,
|
|
116
|
-
hasConversation,
|
|
117
126
|
apiConfigReady,
|
|
118
|
-
|
|
119
|
-
connect,
|
|
127
|
+
connectAndStart,
|
|
120
128
|
dispatch,
|
|
121
|
-
|
|
129
|
+
hasConversation,
|
|
130
|
+
isInline,
|
|
131
|
+
isOpen,
|
|
132
|
+
isWindow,
|
|
133
|
+
showInlineView,
|
|
122
134
|
])
|
|
123
135
|
|
|
124
136
|
const openChat = () => {
|
|
@@ -7,7 +7,7 @@ import { Actions as InterruptActions } from 'domains/interrupt'
|
|
|
7
7
|
import { useConfig } from 'domains/config'
|
|
8
8
|
import * as AppActions from 'domains/app/actions'
|
|
9
9
|
import { useUserHasResponded } from 'domains/app/hooks'
|
|
10
|
-
import { useVisibility } from 'domains/visibility'
|
|
10
|
+
import { useVisibility, visibilityStates } from 'domains/visibility'
|
|
11
11
|
import { useStableCallback } from './utility-hooks'
|
|
12
12
|
import useSeamlyDispatchContext from './use-seamly-dispatch'
|
|
13
13
|
import { useSeamlyUnreadCount } from './seamly-state-hooks'
|
|
@@ -26,7 +26,7 @@ const useSeamlyCommands = () => {
|
|
|
26
26
|
|
|
27
27
|
const hasResponded = useUserHasResponded()
|
|
28
28
|
const hasConversation = useSeamlyHasConversation()
|
|
29
|
-
const { visible: visibility } = useVisibility()
|
|
29
|
+
const { visible: visibility, setVisibility } = useVisibility()
|
|
30
30
|
const unreadMessageCount = useSeamlyUnreadCount()
|
|
31
31
|
|
|
32
32
|
const emitEvent = useCallback(
|
|
@@ -44,6 +44,7 @@ const useSeamlyCommands = () => {
|
|
|
44
44
|
hasResponded,
|
|
45
45
|
unreadMessageCount,
|
|
46
46
|
})
|
|
47
|
+
|
|
47
48
|
api.send('start')
|
|
48
49
|
emitEvent('ui.start', {
|
|
49
50
|
visibility,
|
|
@@ -107,7 +108,13 @@ const useSeamlyCommands = () => {
|
|
|
107
108
|
emitEvent('message', message)
|
|
108
109
|
dispatch({
|
|
109
110
|
type: ADD_EVENT,
|
|
110
|
-
event: {
|
|
111
|
+
event: {
|
|
112
|
+
type: 'message',
|
|
113
|
+
payload: {
|
|
114
|
+
...message,
|
|
115
|
+
optimisticallyInjected: true,
|
|
116
|
+
},
|
|
117
|
+
},
|
|
111
118
|
})
|
|
112
119
|
},
|
|
113
120
|
[api, dispatch, emitEvent, getTextMessageBase],
|
|
@@ -213,12 +220,16 @@ const useSeamlyCommands = () => {
|
|
|
213
220
|
.then((initialState) => {
|
|
214
221
|
if (initialState) {
|
|
215
222
|
dispatch({ type: SET_INITIAL_STATE, initialState })
|
|
223
|
+
if (initialState.userResponded) {
|
|
224
|
+
dispatch(AppActions.setHasResponded(initialState.userResponded))
|
|
225
|
+
setVisibility(visibilityStates.open)
|
|
226
|
+
}
|
|
216
227
|
}
|
|
217
228
|
})
|
|
218
229
|
.catch((error) => {
|
|
219
230
|
dispatch(InterruptActions.set(error))
|
|
220
231
|
})
|
|
221
|
-
}, [api, dispatch])
|
|
232
|
+
}, [api, dispatch, setVisibility])
|
|
222
233
|
|
|
223
234
|
return {
|
|
224
235
|
connect,
|
|
@@ -232,6 +243,7 @@ const useSeamlyCommands = () => {
|
|
|
232
243
|
addMessageBubble,
|
|
233
244
|
addUploadBubble,
|
|
234
245
|
addDivider,
|
|
246
|
+
apiConnected: api.connected,
|
|
235
247
|
apiConfigReady: api.configReady,
|
|
236
248
|
}
|
|
237
249
|
}
|
|
@@ -73,6 +73,7 @@ export const seamlyActions = {
|
|
|
73
73
|
CLEAR_EVENTS: 'CLEAR_EVENTS',
|
|
74
74
|
SET_HISTORY: 'SET_HISTORY',
|
|
75
75
|
SET_EVENTS_READ: 'SET_EVENTS_READ',
|
|
76
|
+
SET_LOADED_IMAGE_EVENT_IDS: 'SET_LOADED_IMAGE_EVENT_IDS',
|
|
76
77
|
ACK_EVENT: 'ACK_EVENT',
|
|
77
78
|
SET_IS_LOADING: 'SET_IS_LOADING',
|
|
78
79
|
CLEAR_PARTICIPANTS: 'CLEAR_PARTICIPANTS',
|
|
@@ -121,6 +122,7 @@ const {
|
|
|
121
122
|
CLEAR_EVENTS,
|
|
122
123
|
SET_HISTORY,
|
|
123
124
|
SET_EVENTS_READ,
|
|
125
|
+
SET_LOADED_IMAGE_EVENT_IDS,
|
|
124
126
|
ACK_EVENT,
|
|
125
127
|
SET_IS_LOADING,
|
|
126
128
|
CLEAR_PARTICIPANTS,
|
|
@@ -171,13 +173,18 @@ const orderHistory = (events) => {
|
|
|
171
173
|
}
|
|
172
174
|
|
|
173
175
|
export const mergeHistory = (stateEvents, historyEvents) => {
|
|
176
|
+
const newStateEvents = stateEvents.filter(
|
|
177
|
+
(stateEvent) =>
|
|
178
|
+
// Deduplicate the event streams, giving events in historyEvents
|
|
179
|
+
// precedence so the server is able to push changes to events.
|
|
180
|
+
!historyEvents.some(
|
|
181
|
+
(historyEvent) => historyEvent.payload.id === stateEvent.payload.id,
|
|
182
|
+
),
|
|
183
|
+
)
|
|
184
|
+
|
|
174
185
|
const newHistoryEvents = historyEvents
|
|
175
186
|
.filter(
|
|
176
187
|
(historyEvent) =>
|
|
177
|
-
// Deduplicate the event streams
|
|
178
|
-
!stateEvents.find(
|
|
179
|
-
(stateEvent) => stateEvent.payload.id === historyEvent.payload.id,
|
|
180
|
-
) &&
|
|
181
188
|
// Remove all non displayable participant messages
|
|
182
189
|
!(
|
|
183
190
|
historyEvent.type === 'participant' &&
|
|
@@ -190,7 +197,7 @@ export const mergeHistory = (stateEvents, historyEvents) => {
|
|
|
190
197
|
// the normal merging logic there is no added effect.
|
|
191
198
|
.reverse()
|
|
192
199
|
|
|
193
|
-
return orderHistory([...newHistoryEvents, ...
|
|
200
|
+
return orderHistory([...newHistoryEvents, ...newStateEvents])
|
|
194
201
|
}
|
|
195
202
|
|
|
196
203
|
const participantReducer = (state, action) => {
|
|
@@ -384,7 +391,7 @@ export const seamlyStateReducer = (state, action) => {
|
|
|
384
391
|
: state
|
|
385
392
|
|
|
386
393
|
case CLEAR_EVENTS:
|
|
387
|
-
return { ...state, unreadEvents: 0, events: [] }
|
|
394
|
+
return { ...state, unreadEvents: 0, loadedImageEventIds: [], events: [] }
|
|
388
395
|
case SET_EVENTS_READ:
|
|
389
396
|
return {
|
|
390
397
|
...state,
|
|
@@ -404,6 +411,11 @@ export const seamlyStateReducer = (state, action) => {
|
|
|
404
411
|
return event
|
|
405
412
|
}),
|
|
406
413
|
}
|
|
414
|
+
case SET_LOADED_IMAGE_EVENT_IDS:
|
|
415
|
+
return {
|
|
416
|
+
...state,
|
|
417
|
+
loadedImageEventIds: [...state.loadedImageEventIds, action.eventId],
|
|
418
|
+
}
|
|
407
419
|
case SET_HISTORY:
|
|
408
420
|
const {
|
|
409
421
|
events: history,
|
|
@@ -606,7 +618,12 @@ export const seamlyStateReducer = (state, action) => {
|
|
|
606
618
|
headerTitles: headerTitlesReducer(state.headerTitles, action),
|
|
607
619
|
}
|
|
608
620
|
case SET_INITIAL_STATE:
|
|
609
|
-
|
|
621
|
+
const { initialState } = action
|
|
622
|
+
return {
|
|
623
|
+
...state,
|
|
624
|
+
initialState,
|
|
625
|
+
unreadEvents: initialState.unreadMessageCount,
|
|
626
|
+
}
|
|
610
627
|
case SET_SERVICE_DATA_ITEM:
|
|
611
628
|
return {
|
|
612
629
|
...state,
|
|
@@ -10,8 +10,10 @@
|
|
|
10
10
|
height: $messagecountsize;
|
|
11
11
|
transform: scale(1);
|
|
12
12
|
transform-origin: 50% 50%;
|
|
13
|
-
transition: transform 0.3s 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275)
|
|
13
|
+
transition: transform 0.3s 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275),
|
|
14
|
+
opacity $transition;
|
|
14
15
|
border-radius: 50%;
|
|
16
|
+
opacity: 1;
|
|
15
17
|
background-color: $negative;
|
|
16
18
|
color: $white;
|
|
17
19
|
font-size: $fontsize-small;
|
|
@@ -20,6 +22,7 @@
|
|
|
20
22
|
|
|
21
23
|
&:empty {
|
|
22
24
|
transform: scale(0);
|
|
23
|
-
transition: transform $transition;
|
|
25
|
+
transition: transform $transition, opacity $transition;
|
|
26
|
+
opacity: 0;
|
|
24
27
|
}
|
|
25
28
|
}
|