@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.
@@ -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 ({ onForward }) {
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'>Do not use webchat to <a className='govuk-link' href='https://www.gov.uk/report-flood-cause'>report a flood</a>.</p>
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={onForward}>Continue</button>
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 ({ onBack }) {
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={onBack}>
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
+ }
@@ -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/reducer.js'
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 availability={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: sanitizeHtml(message.messageContent?.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 { initialState, reducer, CUSTOMER_ID_STORAGE_KEY, THREAD_ID_STORAGE_KEY } from './reducer.js'
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 './reducer'
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,
@@ -0,0 +1,3 @@
1
+ export const SETTINGS_STORAGE_KEY = 'webchat_settings'
2
+ export const CUSTOMER_ID_STORAGE_KEY = 'webchat_customer_id'
3
+ export const THREAD_ID_STORAGE_KEY = 'webchat_thread_id'
@@ -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) => {