@defra/flood-webchat 0.0.1-beta.17 → 0.0.1-beta.19
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 +270 -96
- package/dist/client.js.map +1 -1
- package/package.json +1 -2
- package/src/client/components/chat/chat.jsx +7 -6
- package/src/client/components/panel/panel-header.jsx +1 -0
- package/src/client/components/panel/panel.jsx +15 -15
- package/src/client/components/screens/end-chat.jsx +3 -3
- package/src/client/components/screens/pre-chat.jsx +16 -6
- package/src/client/components/screens/request-chat.jsx +2 -2
- package/src/client/components/screens/settings.jsx +67 -0
- package/src/client/index.jsx +3 -3
- package/src/client/lib/message-notification.js +36 -0
- package/src/client/lib/transform-messages.js +1 -2
- package/src/client/store/AppProvider.jsx +19 -2
- package/src/client/store/actions-map.js +15 -1
- package/src/client/store/constants.js +3 -0
- package/src/client/store/reducer.js +2 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { PanelHeader } from '../panel/panel-header.jsx'
|
|
3
3
|
|
|
4
|
-
export function PreChat ({
|
|
4
|
+
export function PreChat ({ onContinue }) {
|
|
5
5
|
return (
|
|
6
6
|
<>
|
|
7
7
|
<PanelHeader />
|
|
@@ -21,19 +21,29 @@ export function PreChat ({ onForward }) {
|
|
|
21
21
|
{
|
|
22
22
|
'There are other ways to '
|
|
23
23
|
}
|
|
24
|
-
<a className='govuk-link' href='https://www.gov.uk/sign-up-for-flood-warnings'>
|
|
24
|
+
<a className='govuk-link' href='https://www.gov.uk/sign-up-for-flood-warnings' target='_blank' rel='noreferrer'>
|
|
25
25
|
sign up for flood warnings
|
|
26
26
|
</a>
|
|
27
27
|
{
|
|
28
28
|
' and '
|
|
29
29
|
}
|
|
30
|
-
<a className='govuk-link' href='https://www.fws.environment-agency.gov.uk/app/olr/login'>
|
|
30
|
+
<a className='govuk-link' href='https://www.fws.environment-agency.gov.uk/app/olr/login' target='_blank' rel='noreferrer'>
|
|
31
31
|
manage your flood warnings account
|
|
32
32
|
</a>.
|
|
33
33
|
</p>
|
|
34
|
-
<p className='govuk-body-s'>
|
|
34
|
+
<p className='govuk-body-s'>
|
|
35
|
+
{'Do not use webchat to '}
|
|
36
|
+
<a
|
|
37
|
+
className='govuk-link'
|
|
38
|
+
href='https://www.gov.uk/report-flood-cause'
|
|
39
|
+
target='_blank'
|
|
40
|
+
rel='noreferrer'
|
|
41
|
+
>
|
|
42
|
+
report a flood
|
|
43
|
+
</a>.
|
|
44
|
+
</p>
|
|
35
45
|
|
|
36
|
-
<button className='govuk-button govuk-!-font-size-16' data-module='govuk-button' onClick={
|
|
46
|
+
<button className='govuk-button govuk-!-font-size-16' data-module='govuk-button' onClick={onContinue}>Continue</button>
|
|
37
47
|
|
|
38
48
|
<h3 className='govuk-heading-s'>Other flood information</h3>
|
|
39
49
|
|
|
@@ -47,7 +57,7 @@ export function PreChat ({ onForward }) {
|
|
|
47
57
|
<br />
|
|
48
58
|
Open 24 hours a day, 7 days a week
|
|
49
59
|
<br />
|
|
50
|
-
<a className='govuk-link' href='https://gov.uk/call-charges'>Find out more about call charges</a>
|
|
60
|
+
<a className='govuk-link' href='https://gov.uk/call-charges' target='_blank' rel='noreferrer'>Find out more about call charges</a>
|
|
51
61
|
</p>
|
|
52
62
|
</div>
|
|
53
63
|
</>
|
|
@@ -9,7 +9,7 @@ import { ErrorSummary } from '../errorSummary/error-summary.jsx'
|
|
|
9
9
|
|
|
10
10
|
const QUESTION_MAX_LENGTH = 500
|
|
11
11
|
|
|
12
|
-
export function RequestChat ({
|
|
12
|
+
export function RequestChat ({ onPreChatScreen }) {
|
|
13
13
|
const { sdk, setCustomerId, setThreadId, setThread } = useApp()
|
|
14
14
|
const { fetchCustomerId, fetchThread } = useChatSdk(sdk)
|
|
15
15
|
|
|
@@ -82,7 +82,7 @@ export function RequestChat ({ onBack }) {
|
|
|
82
82
|
<PanelHeader />
|
|
83
83
|
|
|
84
84
|
<div className='wc-body'>
|
|
85
|
-
<a href='#' className='wc-back-link govuk-back-link' onClick={
|
|
85
|
+
<a href='#' className='wc-back-link govuk-back-link' onClick={onPreChatScreen}>
|
|
86
86
|
What you can use webchat for
|
|
87
87
|
</a>
|
|
88
88
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import { PanelHeader } from '../panel/panel-header.jsx'
|
|
3
|
+
|
|
4
|
+
import { useApp } from '../../store/useApp.js'
|
|
5
|
+
|
|
6
|
+
export function Settings ({ onCancel }) {
|
|
7
|
+
const { settings, setSettings } = useApp()
|
|
8
|
+
|
|
9
|
+
const [optionAudio, setOptionAudio] = useState(settings.audio)
|
|
10
|
+
const [optionScroll, setOptionScroll] = useState(settings.scroll)
|
|
11
|
+
|
|
12
|
+
const onSave = e => {
|
|
13
|
+
e.preventDefault()
|
|
14
|
+
|
|
15
|
+
setSettings({ audio: optionAudio, scroll: optionScroll })
|
|
16
|
+
onCancel(e)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<>
|
|
21
|
+
<PanelHeader />
|
|
22
|
+
|
|
23
|
+
<div className='wc-body'>
|
|
24
|
+
<fieldset className='govuk-fieldset govuk-!-margin-bottom-4'>
|
|
25
|
+
<legend className='govuk-fieldset__legend govuk-fieldset__legend'>
|
|
26
|
+
<h3 id='wc-subtitle' className='govuk-heading-s'>Change settings</h3>
|
|
27
|
+
</legend>
|
|
28
|
+
<div className='govuk-checkboxes govuk-checkboxes--small' data-module='govuk-checkboxes'>
|
|
29
|
+
<div className='govuk-checkboxes__item'>
|
|
30
|
+
<input
|
|
31
|
+
id='audio'
|
|
32
|
+
name='audio'
|
|
33
|
+
type='checkbox'
|
|
34
|
+
value='audio'
|
|
35
|
+
className='govuk-checkboxes__input'
|
|
36
|
+
defaultChecked={optionAudio}
|
|
37
|
+
onChange={() => setOptionAudio(!optionAudio)}
|
|
38
|
+
/>
|
|
39
|
+
<label className='govuk-label govuk-checkboxes__label govuk-!-font-size-16' htmlFor='audio'>
|
|
40
|
+
Play a sound when receiving a new message
|
|
41
|
+
</label>
|
|
42
|
+
</div>
|
|
43
|
+
<div className='govuk-checkboxes__item'>
|
|
44
|
+
<input
|
|
45
|
+
id='scroll'
|
|
46
|
+
name='scroll'
|
|
47
|
+
type='checkbox'
|
|
48
|
+
value='scroll'
|
|
49
|
+
className='govuk-checkboxes__input'
|
|
50
|
+
defaultChecked={optionScroll}
|
|
51
|
+
onChange={() => setOptionScroll(!optionScroll)}
|
|
52
|
+
/>
|
|
53
|
+
<label className='govuk-label govuk-checkboxes__label govuk-!-font-size-16' htmlFor='scroll'>
|
|
54
|
+
Scroll automatically to a new message
|
|
55
|
+
</label>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</fieldset>
|
|
59
|
+
|
|
60
|
+
<div className='govuk-button-group'>
|
|
61
|
+
<a id='settings-save' href='#' className='govuk-button govuk-!-font-size-16' data-module='govuk-button' onClick={onSave}>Save</a>
|
|
62
|
+
<a id='settings-cancel' href='#' className='govuk-link govuk-!-font-size-16' data-module='govuk-button' onClick={onCancel}>Cancel</a>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</>
|
|
66
|
+
)
|
|
67
|
+
}
|
package/src/client/index.jsx
CHANGED
|
@@ -4,7 +4,7 @@ import { ChatSdk } from '@nice-devone/nice-cxone-chat-web-sdk'
|
|
|
4
4
|
import { Availability } from './components/availability/availability.jsx'
|
|
5
5
|
import { checkAvailability } from './lib/check-availability'
|
|
6
6
|
import { AppProvider } from './store/AppProvider.jsx'
|
|
7
|
-
import { CUSTOMER_ID_STORAGE_KEY } from './store/
|
|
7
|
+
import { CUSTOMER_ID_STORAGE_KEY } from './store/constants.js'
|
|
8
8
|
|
|
9
9
|
export async function init (container, options) {
|
|
10
10
|
const sdk = new ChatSdk({
|
|
@@ -23,8 +23,8 @@ export async function init (container, options) {
|
|
|
23
23
|
availability = 'UNAVAILABLE'
|
|
24
24
|
}
|
|
25
25
|
root.render(
|
|
26
|
-
<AppProvider sdk={sdk} availability={availability}>
|
|
27
|
-
<Availability
|
|
26
|
+
<AppProvider sdk={sdk} availability={availability} options={options}>
|
|
27
|
+
<Availability />
|
|
28
28
|
</AppProvider>
|
|
29
29
|
)
|
|
30
30
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const messageNotification = audioUrl => {
|
|
2
|
+
let buffer
|
|
3
|
+
|
|
4
|
+
const context = new (window.AudioContext || window.webkitAudioContext)()
|
|
5
|
+
|
|
6
|
+
if (context.state === 'suspended') {
|
|
7
|
+
const events = ['touchstart', 'touchend', 'mousedown', 'wheel', 'keydown', 'click']
|
|
8
|
+
|
|
9
|
+
const unlock = _e => {
|
|
10
|
+
events.forEach(event => {
|
|
11
|
+
document.body.removeEventListener(event, unlock)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
context.resume()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
events.forEach(event => {
|
|
18
|
+
document.body.addEventListener(event, unlock, false)
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
fetch(audioUrl)
|
|
23
|
+
.then(response => response.arrayBuffer())
|
|
24
|
+
.then(data => context.decodeAudioData(data))
|
|
25
|
+
.then(decodedData => {
|
|
26
|
+
buffer = decodedData
|
|
27
|
+
})
|
|
28
|
+
.catch(console.error)
|
|
29
|
+
|
|
30
|
+
return () => {
|
|
31
|
+
const source = context.createBufferSource()
|
|
32
|
+
source.buffer = buffer
|
|
33
|
+
source.connect(context.destination)
|
|
34
|
+
source.start()
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import * as uuid from 'uuid'
|
|
2
|
-
import sanitizeHtml from 'sanitize-html'
|
|
3
2
|
import { DateTime } from 'luxon'
|
|
4
3
|
|
|
5
4
|
export const transformMessage = message => {
|
|
6
5
|
return {
|
|
7
6
|
id: message.id,
|
|
8
|
-
text:
|
|
7
|
+
text: message.messageContent?.text,
|
|
9
8
|
createdAt: new Date(message.createdAt),
|
|
10
9
|
user: message.authorEndUserIdentity?.fullName?.trim() || null,
|
|
11
10
|
assignee: message.authorUser?.firstName || null,
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import React, { createContext, useEffect, useReducer, useMemo } from 'react'
|
|
2
2
|
import { ChatEvent } from '@nice-devone/nice-cxone-chat-web-sdk'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { messageNotification } from '../lib/message-notification.js'
|
|
5
|
+
|
|
6
|
+
import { initialState, reducer } from './reducer.js'
|
|
7
|
+
import { CUSTOMER_ID_STORAGE_KEY, THREAD_ID_STORAGE_KEY, SETTINGS_STORAGE_KEY } from './constants.js'
|
|
5
8
|
|
|
6
9
|
export const AppContext = createContext(initialState)
|
|
7
10
|
|
|
8
|
-
export const AppProvider = ({ sdk, availability, children }) => {
|
|
11
|
+
export const AppProvider = ({ sdk, availability, options, children }) => {
|
|
9
12
|
const [state, dispatch] = useReducer(reducer, initialState)
|
|
10
13
|
|
|
14
|
+
const playSound = messageNotification(options.audioUrl)
|
|
15
|
+
|
|
11
16
|
/**
|
|
12
17
|
* SDK event handlers
|
|
13
18
|
*/
|
|
@@ -34,6 +39,12 @@ export const AppProvider = ({ sdk, availability, children }) => {
|
|
|
34
39
|
dispatch({ type: 'SET_MESSAGE', payload: e.detail.data.message })
|
|
35
40
|
dispatch({ type: 'SET_AGENT_STATUS', payload: e.detail.data.case.status })
|
|
36
41
|
dispatch({ type: 'SET_UNSEEN_COUNT', payload: 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') {
|
|
46
|
+
playSound()
|
|
47
|
+
}
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
const onContactStatusChanged = e => {
|
|
@@ -57,6 +68,7 @@ export const AppProvider = ({ sdk, availability, children }) => {
|
|
|
57
68
|
|
|
58
69
|
setCustomerId(window.localStorage.getItem(CUSTOMER_ID_STORAGE_KEY))
|
|
59
70
|
setThreadId(window.localStorage.getItem(THREAD_ID_STORAGE_KEY))
|
|
71
|
+
setSettings(JSON.parse(window.localStorage.getItem(SETTINGS_STORAGE_KEY)) || state.settings)
|
|
60
72
|
|
|
61
73
|
if (window.location.hash === '#webchat') {
|
|
62
74
|
setChatVisibility(true)
|
|
@@ -90,6 +102,10 @@ export const AppProvider = ({ sdk, availability, children }) => {
|
|
|
90
102
|
dispatch({ type: 'SET_MESSAGES', payload: messages })
|
|
91
103
|
}
|
|
92
104
|
|
|
105
|
+
const setSettings = data => {
|
|
106
|
+
dispatch({ type: 'SET_SETTINGS', payload: data })
|
|
107
|
+
}
|
|
108
|
+
|
|
93
109
|
const setUnseenCount = unseenCount => {
|
|
94
110
|
dispatch({ type: 'SET_UNSEEN_COUNT', payload: unseenCount })
|
|
95
111
|
}
|
|
@@ -100,6 +116,7 @@ export const AppProvider = ({ sdk, availability, children }) => {
|
|
|
100
116
|
const store = useMemo(() => ({
|
|
101
117
|
...state,
|
|
102
118
|
sdk,
|
|
119
|
+
setSettings,
|
|
103
120
|
setCustomerId,
|
|
104
121
|
setThreadId,
|
|
105
122
|
setThread,
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { transformMessages, transformMessage } from '../lib/transform-messages'
|
|
2
|
-
import { CUSTOMER_ID_STORAGE_KEY, THREAD_ID_STORAGE_KEY } from './
|
|
2
|
+
import { CUSTOMER_ID_STORAGE_KEY, THREAD_ID_STORAGE_KEY, SETTINGS_STORAGE_KEY } from './constants'
|
|
3
|
+
|
|
4
|
+
const setSettings = (state, payload) => {
|
|
5
|
+
if (payload) {
|
|
6
|
+
window.localStorage.setItem(SETTINGS_STORAGE_KEY, JSON.stringify(payload))
|
|
7
|
+
} else {
|
|
8
|
+
window.localStorage.removeItem(SETTINGS_STORAGE_KEY)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
...state,
|
|
13
|
+
settings: payload
|
|
14
|
+
}
|
|
15
|
+
}
|
|
3
16
|
|
|
4
17
|
const setCustomerId = (state, payload) => {
|
|
5
18
|
if (payload) {
|
|
@@ -93,6 +106,7 @@ const setUnseenCount = (state, payload) => {
|
|
|
93
106
|
export const actionsMap = {
|
|
94
107
|
SET_CHAT_VISIBILITY: setChatVisibility,
|
|
95
108
|
SET_AVAILABILITY: setAvailability,
|
|
109
|
+
SET_SETTINGS: setSettings,
|
|
96
110
|
SET_CUSTOMER_ID: setCustomerId,
|
|
97
111
|
SET_THREAD_ID: setThreadId,
|
|
98
112
|
SET_THREAD: setThread,
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { actionsMap } from './actions-map'
|
|
2
2
|
|
|
3
|
-
export const CUSTOMER_ID_STORAGE_KEY = 'webchat_customer_id'
|
|
4
|
-
export const THREAD_ID_STORAGE_KEY = 'webchat_thread_id'
|
|
5
|
-
|
|
6
3
|
export const initialState = {
|
|
7
4
|
availability: null,
|
|
8
5
|
customerId: null,
|
|
@@ -13,7 +10,8 @@ export const initialState = {
|
|
|
13
10
|
agent: null,
|
|
14
11
|
agentStatus: null,
|
|
15
12
|
isAgentTyping: false,
|
|
16
|
-
isChatOpen: false
|
|
13
|
+
isChatOpen: false,
|
|
14
|
+
settings: { audio: true, scroll: true }
|
|
17
15
|
}
|
|
18
16
|
|
|
19
17
|
export const reducer = (state, action) => {
|