@defra/flood-webchat 1.0.10 → 1.0.12
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/dist/client.js +1 -1
- package/dist/server.js +1 -1
- package/package.json +12 -12
- package/src/client/components/availability/availability.jsx +13 -23
- package/src/client/components/chat/chat.jsx +26 -36
- package/src/client/components/live-region.jsx +2 -4
- package/src/client/components/panel/panel-header.jsx +4 -4
- package/src/client/components/panel/panel.jsx +39 -34
- package/src/client/components/panel/panel.scss +1 -1
- package/src/client/components/screens/end-chat.jsx +3 -6
- package/src/client/components/screens/request-chat.jsx +9 -13
- package/src/client/components/screens/settings.jsx +1 -1
- package/src/client/components/screens/unavailable.jsx +10 -12
- package/src/client/components/skip-link.jsx +1 -1
- package/src/client/hooks/useFocusedElements.js +6 -8
- package/src/client/index.jsx +9 -22
- package/src/client/lib/history.js +0 -4
- package/src/client/store/{app/AppProvider.jsx → AppProvider.jsx} +86 -35
- package/src/client/store/{app/actions-map.js → actions-map.js} +48 -15
- package/src/client/store/constants.js +0 -1
- package/src/client/store/{app/reducer.js → reducer.js} +4 -1
- package/src/client/store/useChatSdk.js +37 -0
- package/src/server/lib/client.js +2 -2
- package/src/server/lib/utils.js +1 -7
- package/src/client/store/sdk/SdkProvider.jsx +0 -141
- package/src/client/store/sdk/actions-map.js +0 -45
- package/src/client/store/sdk/reducer.js +0 -21
- package/src/client/store/sdk/useSdk.js +0 -5
- /package/src/client/store/{app/useApp.js → useApp.js} +0 -0
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { actionsMap } from './actions-map'
|
|
2
2
|
|
|
3
3
|
export const initialState = {
|
|
4
|
-
sdk: null,
|
|
5
4
|
availability: null,
|
|
6
5
|
customerId: null,
|
|
7
6
|
threadId: null,
|
|
8
7
|
thread: null,
|
|
8
|
+
messages: [],
|
|
9
9
|
unseenCount: 0,
|
|
10
10
|
instigatorId: null,
|
|
11
|
+
agent: null,
|
|
12
|
+
agentStatus: null,
|
|
13
|
+
isAgentTyping: false,
|
|
11
14
|
isChatOpen: false,
|
|
12
15
|
isMobile: window.matchMedia('(max-width: 640px)').matches,
|
|
13
16
|
isKeyboard: false,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export const useChatSdk = sdk => {
|
|
2
|
+
const connect = async () => sdk.authorize()
|
|
3
|
+
|
|
4
|
+
const fetchCustomerId = async () => {
|
|
5
|
+
const response = await connect()
|
|
6
|
+
return response?.consumerIdentity.idOnExternalPlatform
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const fetchThread = async threadId => {
|
|
10
|
+
await connect()
|
|
11
|
+
return sdk.getThread(threadId)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const fetchMessages = async (thread, threadId) => {
|
|
15
|
+
await connect()
|
|
16
|
+
|
|
17
|
+
const recovered = await thread.recover(threadId)
|
|
18
|
+
|
|
19
|
+
const allMessages = []
|
|
20
|
+
let fetchedMessages = recovered.messages
|
|
21
|
+
|
|
22
|
+
while (fetchedMessages.length) {
|
|
23
|
+
fetchedMessages.map(msg => allMessages.push(msg))
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const response = await thread.loadMoreMessages()
|
|
27
|
+
fetchedMessages = response.data.messages
|
|
28
|
+
} catch (err) {
|
|
29
|
+
fetchedMessages = []
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return allMessages
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return { connect, fetchCustomerId, fetchThread, fetchMessages }
|
|
37
|
+
}
|
package/src/server/lib/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const querystring = require('querystring')
|
|
2
2
|
const axios = require('axios')
|
|
3
|
-
const
|
|
3
|
+
const { jwtDecode } = require('jwt-decode')
|
|
4
4
|
const { isWithinHours } = require('./utils.js')
|
|
5
5
|
|
|
6
6
|
const contentType = 'application/x-www-form-urlencoded'
|
|
@@ -25,7 +25,7 @@ const authenticate = async ({ authenticationUri, authorisation, accessKey, acces
|
|
|
25
25
|
return {
|
|
26
26
|
token: auth.data.access_token,
|
|
27
27
|
tokenType: auth.data.token_type,
|
|
28
|
-
tenantId:
|
|
28
|
+
tenantId: jwtDecode(auth.data.id_token)?.tenantId
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
package/src/server/lib/utils.js
CHANGED
|
@@ -18,12 +18,6 @@ const isWithinHours = (days, date) => {
|
|
|
18
18
|
return now.diff(todaysDateTimeOpen).milliseconds >= 0 && now.diff(todaysDateTimeClose).milliseconds <= 0
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const stripPageTitle = title => {
|
|
22
|
-
const prefixPattern = /^\(\d+ new messages?\) - /
|
|
23
|
-
return title.replace(prefixPattern, '')
|
|
24
|
-
}
|
|
25
|
-
|
|
26
21
|
module.exports = {
|
|
27
|
-
isWithinHours
|
|
28
|
-
stripPageTitle
|
|
22
|
+
isWithinHours
|
|
29
23
|
}
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import React, { createContext, useEffect, useReducer, useMemo } from 'react'
|
|
2
|
-
import { ChatEvent } from '@nice-devone/nice-cxone-chat-web-sdk'
|
|
3
|
-
|
|
4
|
-
import { initialState, reducer } from './reducer.js'
|
|
5
|
-
import { SETTINGS_STORAGE_KEY } from '../constants.js'
|
|
6
|
-
import { useApp } from '../app/useApp.js'
|
|
7
|
-
|
|
8
|
-
export const SdkContext = createContext(initialState)
|
|
9
|
-
|
|
10
|
-
export const SdkProvider = ({ sdk, playSound, onRecoverError, children }) => {
|
|
11
|
-
const [state, dispatch] = useReducer(reducer, initialState)
|
|
12
|
-
const { thread, threadId, setThread, setThreadId, setUnseenCount } = useApp()
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* SDK event handlers
|
|
16
|
-
*/
|
|
17
|
-
const onLiveChatRecovered = e => {
|
|
18
|
-
dispatch({ type: 'SET_AGENT', payload: e.detail.data.inboxAssignee })
|
|
19
|
-
dispatch({ type: 'SET_AGENT_STATUS', payload: e.detail.data.contact.status })
|
|
20
|
-
setUnseenCount(e.detail.data.contact.customerStatistics.unseenMessagesCount)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const onAssignedAgentChanged = e => {
|
|
24
|
-
dispatch({ type: 'SET_AGENT', payload: e.detail.data.inboxAssignee })
|
|
25
|
-
dispatch({ type: 'SET_AGENT_STATUS', payload: e.detail.data.case.status })
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const onAgentTypingStarted = () => {
|
|
29
|
-
dispatch({ type: 'SET_AGENT_TYPING', payload: true })
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const onAgentTypingEnded = () => {
|
|
33
|
-
dispatch({ type: 'SET_AGENT_TYPING', payload: false })
|
|
34
|
-
dispatch({ type: 'SET_LIVE_REGION_TEXT' })
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const onMessageCreated = e => {
|
|
38
|
-
dispatch({ type: 'SET_MESSAGE', payload: e.detail.data.message })
|
|
39
|
-
dispatch({ type: 'SET_AGENT_STATUS', payload: e.detail.data.case.status })
|
|
40
|
-
|
|
41
|
-
setUnseenCount(e.detail.data.case.customerStatistics.unseenMessagesCount)
|
|
42
|
-
|
|
43
|
-
const isAudioOn = JSON.parse(window.localStorage.getItem(SETTINGS_STORAGE_KEY)).audio
|
|
44
|
-
|
|
45
|
-
if (isAudioOn && e.detail.data.message.direction === 'outbound' && playSound) {
|
|
46
|
-
playSound()
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const onContactStatusChanged = e => {
|
|
51
|
-
dispatch({ type: 'SET_AGENT_STATUS', payload: e.detail.data.case.status })
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
if (sdk) {
|
|
56
|
-
sdk.onChatEvent(ChatEvent.LIVECHAT_RECOVERED, onLiveChatRecovered)
|
|
57
|
-
sdk.onChatEvent(ChatEvent.MESSAGE_CREATED, onMessageCreated)
|
|
58
|
-
sdk.onChatEvent(ChatEvent.AGENT_TYPING_STARTED, onAgentTypingStarted)
|
|
59
|
-
sdk.onChatEvent(ChatEvent.AGENT_TYPING_ENDED, onAgentTypingEnded)
|
|
60
|
-
sdk.onChatEvent(ChatEvent.ASSIGNED_AGENT_CHANGED, onAssignedAgentChanged)
|
|
61
|
-
sdk.onChatEvent(ChatEvent.CONTACT_STATUS_CHANGED, onContactStatusChanged)
|
|
62
|
-
}
|
|
63
|
-
}, [sdk])
|
|
64
|
-
|
|
65
|
-
const fetchMessages = async threadItem => {
|
|
66
|
-
const recovered = await threadItem.recover()
|
|
67
|
-
|
|
68
|
-
const allMessages = []
|
|
69
|
-
|
|
70
|
-
let fetchedMessages = recovered.messages
|
|
71
|
-
|
|
72
|
-
while (fetchedMessages.length) {
|
|
73
|
-
fetchedMessages.map(msg => allMessages.push(msg))
|
|
74
|
-
|
|
75
|
-
try {
|
|
76
|
-
const response = await threadItem.loadMoreMessages()
|
|
77
|
-
fetchedMessages = response.data.messages
|
|
78
|
-
} catch (err) {
|
|
79
|
-
fetchedMessages = []
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return allMessages
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Recovers the thread if there is a threadId but no thread loaded in to state
|
|
88
|
-
*/
|
|
89
|
-
useEffect(() => {
|
|
90
|
-
const recover = async () => {
|
|
91
|
-
try {
|
|
92
|
-
await sdk.authorize()
|
|
93
|
-
|
|
94
|
-
const fetchedThread = await sdk.getThread(threadId)
|
|
95
|
-
const fetchedMessages = await fetchMessages(fetchedThread)
|
|
96
|
-
|
|
97
|
-
setThread(fetchedThread)
|
|
98
|
-
setMessages(fetchedMessages)
|
|
99
|
-
} catch (err) {
|
|
100
|
-
console.log('[Chat Error] fetchThread', err)
|
|
101
|
-
setThreadId()
|
|
102
|
-
setThread()
|
|
103
|
-
onRecoverError()
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (sdk && threadId && !thread) {
|
|
108
|
-
console.log('recovering chat...')
|
|
109
|
-
recover()
|
|
110
|
-
}
|
|
111
|
-
}, [sdk, thread, threadId])
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* State update functions
|
|
115
|
-
*/
|
|
116
|
-
const setMessages = messages => {
|
|
117
|
-
dispatch({ type: 'SET_MESSAGES', payload: messages })
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Application-wide state and state functions
|
|
122
|
-
*/
|
|
123
|
-
const store = useMemo(() => ({
|
|
124
|
-
...state,
|
|
125
|
-
sdk,
|
|
126
|
-
fetchMessages,
|
|
127
|
-
setMessages,
|
|
128
|
-
onLiveChatRecovered,
|
|
129
|
-
onAssignedAgentChanged,
|
|
130
|
-
onAgentTypingStarted,
|
|
131
|
-
onAgentTypingEnded,
|
|
132
|
-
onMessageCreated,
|
|
133
|
-
onContactStatusChanged
|
|
134
|
-
}))
|
|
135
|
-
|
|
136
|
-
return (
|
|
137
|
-
<SdkContext.Provider value={store}>
|
|
138
|
-
{children}
|
|
139
|
-
</SdkContext.Provider>
|
|
140
|
-
)
|
|
141
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { transformMessages, transformMessage } from '../../lib/transform-messages'
|
|
2
|
-
|
|
3
|
-
const setMessages = (state, payload) => {
|
|
4
|
-
return {
|
|
5
|
-
...state,
|
|
6
|
-
messages: transformMessages(payload).reverse()
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const setMessage = (state, payload) => {
|
|
11
|
-
return {
|
|
12
|
-
...state,
|
|
13
|
-
message: transformMessage(payload),
|
|
14
|
-
messages: [...state.messages, transformMessage(payload)]
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const setAgent = (state, payload) => {
|
|
19
|
-
return {
|
|
20
|
-
...state,
|
|
21
|
-
agent: payload
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const setAgentIsTyping = (state, payload) => {
|
|
26
|
-
return {
|
|
27
|
-
...state,
|
|
28
|
-
isAgentTyping: payload
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const setAgentStatus = (state, payload) => {
|
|
33
|
-
return {
|
|
34
|
-
...state,
|
|
35
|
-
agentStatus: payload
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export const actionsMap = {
|
|
40
|
-
SET_MESSAGE: setMessage,
|
|
41
|
-
SET_MESSAGES: setMessages,
|
|
42
|
-
SET_AGENT: setAgent,
|
|
43
|
-
SET_AGENT_TYPING: setAgentIsTyping,
|
|
44
|
-
SET_AGENT_STATUS: setAgentStatus
|
|
45
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { actionsMap } from './actions-map'
|
|
2
|
-
|
|
3
|
-
export const initialState = {
|
|
4
|
-
messages: [],
|
|
5
|
-
agent: null,
|
|
6
|
-
agentStatus: null,
|
|
7
|
-
isAgentTyping: false
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const reducer = (state, action) => {
|
|
11
|
-
const { type, payload } = action
|
|
12
|
-
|
|
13
|
-
const fn = actionsMap[type]
|
|
14
|
-
|
|
15
|
-
if (fn) {
|
|
16
|
-
const actionFunction = fn.bind(this, state, payload)
|
|
17
|
-
return actionFunction()
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return state
|
|
21
|
-
}
|
|
File without changes
|