@defra/flood-webchat 0.0.1-alpha.9 → 0.0.1-beta.2
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/README.md +1 -83
- package/dist/client.js +445 -0
- package/dist/client.js.map +1 -0
- package/dist/server.js +362 -0
- package/dist/server.js.map +1 -0
- package/docs/development-guide.md +1 -2
- package/jest.config.mjs +29 -0
- package/main.scss +5 -741
- package/package.json +18 -13
- package/src/client/components/availability/availability.jsx +101 -0
- package/src/client/components/availability/availability.scss +89 -0
- package/src/client/index.jsx +16 -0
- package/src/client/lib/check-availability.js +7 -0
- package/src/client/lib/classnames.js +1 -0
- package/src/client/lib/external-stores.js +4 -0
- package/src/client/lib/external-sync-store.js +42 -0
- package/src/server/index.js +11 -6
- package/src/server/{client.js → lib/client.js} +32 -12
- package/src/server/{utils.js → lib/utils.js} +8 -13
- package/webpack.config.mjs +32 -0
- package/assets/audio/notification.mp3 +0 -0
- package/babel.config.cjs +0 -3
- package/dist/templates.js +0 -554
- package/src/client/index.js +0 -14
- package/src/client/lib/availability.js +0 -75
- package/src/client/lib/config.js +0 -17
- package/src/client/lib/keyboard.js +0 -149
- package/src/client/lib/notification.js +0 -59
- package/src/client/lib/nunjucks.js +0 -3
- package/src/client/lib/panel.js +0 -293
- package/src/client/lib/provider.js +0 -11
- package/src/client/lib/skiplink.js +0 -27
- package/src/client/lib/state.js +0 -82
- package/src/client/lib/transcript.js +0 -34
- package/src/client/lib/utils.js +0 -190
- package/src/client/lib/webchat.js +0 -1075
- package/src/style/_mixins.scss +0 -13
package/src/client/lib/utils.js
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import Config from './config.js'
|
|
2
|
-
import nunjucks from './nunjucks.js'
|
|
3
|
-
|
|
4
|
-
const env = nunjucks
|
|
5
|
-
|
|
6
|
-
class Utils {
|
|
7
|
-
static addOrUpdateParameter (uri, key, value) {
|
|
8
|
-
// Temporariliy remove fragment
|
|
9
|
-
const i = uri.indexOf('#')
|
|
10
|
-
const hash = i === -1 ? '' : uri.substr(i)
|
|
11
|
-
uri = i === -1 ? uri : uri.substr(0, i)
|
|
12
|
-
const re = new RegExp('([?&])' + key + '=[^&#]*', 'i')
|
|
13
|
-
// Delete parameter and value
|
|
14
|
-
if (!value || value === '') {
|
|
15
|
-
uri = uri.replace(re, '')
|
|
16
|
-
} else if (re.test(uri)) {
|
|
17
|
-
// Replace parameter value
|
|
18
|
-
uri = uri.replace(re, '$1' + key + '=' + value)
|
|
19
|
-
// Add parameter and value
|
|
20
|
-
} else {
|
|
21
|
-
const separator = /\?/.test(uri) ? '&' : '?'
|
|
22
|
-
uri = uri + separator + key + '=' + value
|
|
23
|
-
}
|
|
24
|
-
return uri + hash
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
static getParameterByName (name) {
|
|
28
|
-
const v = window.location.search.match(new RegExp('(?:[?&]' + name + '=)([^&]+)'))
|
|
29
|
-
return v ? v[1] : null
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
static listenForDevice (device, callback) {
|
|
33
|
-
const mQ = window.matchMedia(`(max-width: ${Config.getBreakpoint(device)})`)
|
|
34
|
-
if (window.matchMedia.addEventListener) {
|
|
35
|
-
mQ.addEventListener('change', e => { callback(e.matches) })
|
|
36
|
-
} else {
|
|
37
|
-
mQ.addListener(e => { callback(e.matches) })
|
|
38
|
-
}
|
|
39
|
-
callback(mQ.matches)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
static formatDate (value) {
|
|
43
|
-
const now = new Date().getTime()
|
|
44
|
-
const startOfDay = now - (now % 86400000)
|
|
45
|
-
const isToday = value.getTime() >= startOfDay
|
|
46
|
-
let hours = value.getHours()
|
|
47
|
-
let minutes = value.getMinutes()
|
|
48
|
-
minutes = `${minutes < 10 ? '0' : ''}${minutes}`
|
|
49
|
-
const ampm = hours >= 12 ? 'pm' : 'am'
|
|
50
|
-
hours %= 12
|
|
51
|
-
hours = hours || 12
|
|
52
|
-
const time = `${hours}:${minutes}${ampm}`
|
|
53
|
-
const date = value.toLocaleString('en-GB', { day: 'numeric', month: 'short' })
|
|
54
|
-
return isToday ? time : `${time}, ${date}`
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
static parseMessage (input) {
|
|
58
|
-
let text = input
|
|
59
|
-
|
|
60
|
-
// Convert line breaks
|
|
61
|
-
text = text.trim().replace(/(\r\n|\r|\n){2,}/g, '$1\n').replace(/\r\n|\r|\n/g, '<br>')
|
|
62
|
-
|
|
63
|
-
// Convert links
|
|
64
|
-
const linksFound = text.match(/(?:www|https?)[^\s]+/g)
|
|
65
|
-
const aLink = []
|
|
66
|
-
if (linksFound != null) {
|
|
67
|
-
for (let i = 0; i < linksFound.length; i++) {
|
|
68
|
-
const href = linksFound[i]
|
|
69
|
-
const anchor = linksFound[i].replace(/#webchat|https?:\/\//gi, '')
|
|
70
|
-
aLink.push(`<a href="${href}">${anchor}</a>`)
|
|
71
|
-
text = text.split(linksFound[i]).join(aLink[i])
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return text
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
static sortMessages (messages) {
|
|
79
|
-
return messages.sort((a, b) => {
|
|
80
|
-
return a.createdAt - b.createdAt
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
static addMessagesHtml (messages) {
|
|
85
|
-
const m = messages
|
|
86
|
-
for (let i = 0; i < m.length; i++) {
|
|
87
|
-
const isGroupStart = i === 0 || (i > 0 && m[i].direction !== m[i - 1].direction)
|
|
88
|
-
const isGroupEnd = (i === m.length - 1) || (i < (m.length - 1) && m[i].direction !== m[i + 1].direction)
|
|
89
|
-
m[i].isGroupStart = isGroupStart
|
|
90
|
-
m[i].isGroupEnd = isGroupEnd
|
|
91
|
-
const html = env.render('message.html', { model: m[i] })
|
|
92
|
-
m[i].html = html
|
|
93
|
-
}
|
|
94
|
-
return m
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
static setCountdown (element, callback) {
|
|
98
|
-
let second = Config.countdown
|
|
99
|
-
const interval = setInterval(() => {
|
|
100
|
-
second--
|
|
101
|
-
if (element) {
|
|
102
|
-
element.innerHTML = `${second} seconds`
|
|
103
|
-
}
|
|
104
|
-
if (second <= 0) {
|
|
105
|
-
clearInterval(interval)
|
|
106
|
-
callback()
|
|
107
|
-
}
|
|
108
|
-
}, 1000)
|
|
109
|
-
return interval
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
static toggleLabel (label, key, textbox) {
|
|
113
|
-
const chars = /^[a-zA-Z0-9- !'^+%&/()=?_\-~`;#$½{[\]}\\|<>@,]+$/i // /^[a-z\d -]+$/i
|
|
114
|
-
const hasValue = textbox.value.length > 0
|
|
115
|
-
const isValidChar = key && key.length === 1 && chars.test(key)
|
|
116
|
-
const isHidden = hasValue || (!hasValue && isValidChar)
|
|
117
|
-
label.classList.toggle('wc-message__label--hidden', isHidden)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
static submit (e, textbox) {
|
|
121
|
-
const form = textbox.closest('form')
|
|
122
|
-
const isEnterSubmit = textbox.hasAttribute('data-wc-enter-submit')
|
|
123
|
-
if (e.key !== 'Enter' || !isEnterSubmit || e.altKey || e.shiftKey) {
|
|
124
|
-
return
|
|
125
|
-
}
|
|
126
|
-
form.dispatchEvent(new Event('submit'))
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
static suppressEnter (e, textbox) {
|
|
130
|
-
const isEnterSubmit = textbox.hasAttribute('data-wc-enter-submit')
|
|
131
|
-
const hasValue = textbox.value.length > 0
|
|
132
|
-
if (e.key === 'Enter' && (!hasValue || (isEnterSubmit && !e.altKey && !e.shiftKey))) {
|
|
133
|
-
e.preventDefault()
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
static autosize (textbox) {
|
|
138
|
-
const offset = textbox.offsetHeight - textbox.clientHeight
|
|
139
|
-
textbox.addEventListener('input', e => {
|
|
140
|
-
e.target.style.height = 'auto'
|
|
141
|
-
e.target.style.height = e.target.scrollHeight + offset + 'px'
|
|
142
|
-
})
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
static async poll ({ fn, validate, interval, maxAttempts }) {
|
|
146
|
-
let attempts = 0
|
|
147
|
-
|
|
148
|
-
const executePoll = async (resolve, reject) => {
|
|
149
|
-
const result = await fn()
|
|
150
|
-
attempts++
|
|
151
|
-
|
|
152
|
-
if (interval <= 0) {
|
|
153
|
-
return
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (validate && validate(result)) {
|
|
157
|
-
return resolve(result)
|
|
158
|
-
} else if (maxAttempts && attempts === maxAttempts) {
|
|
159
|
-
return reject(new Error('Exceeded max attempts'))
|
|
160
|
-
} else {
|
|
161
|
-
setTimeout(executePoll, interval, resolve, reject)
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return new Promise(executePoll)
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
static async getAvailability (endpoint) {
|
|
169
|
-
try {
|
|
170
|
-
const response = await fetch(endpoint)
|
|
171
|
-
const data = await response.json()
|
|
172
|
-
return data.availability
|
|
173
|
-
} catch (err) {
|
|
174
|
-
console.log('getAvailability', err)
|
|
175
|
-
return 'UNAVAILABLE'
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
static getDuration (seconds) {
|
|
180
|
-
let duration = seconds
|
|
181
|
-
if (seconds <= 60) {
|
|
182
|
-
duration = `${seconds} seconds`
|
|
183
|
-
} else {
|
|
184
|
-
duration = `${Math.floor(seconds / 60)} minutes`
|
|
185
|
-
}
|
|
186
|
-
return duration
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export default Utils
|