@seamly/web-ui 20.4.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 +35 -68
- 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 +374 -3850
- 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 +394 -3862
- package/build/dist/lib/standalone.min.js +1 -1
- package/build/dist/lib/style-guide.js +200 -113
- package/build/dist/lib/style-guide.min.js +1 -1
- package/package.json +2 -4
- 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/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/style-guide/components/app.js +2 -0
- package/src/javascripts/style-guide/states.js +30 -50
- 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/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/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/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 +4 -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 +16 -6
- package/src/javascripts/lib/parse-body.js +0 -10
- package/src/javascripts/ui/components/conversation/event/hooks/use-text-rendering.js +0 -35
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { useCallback } from 'preact/hooks'
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'preact/hooks'
|
|
2
2
|
import { useSelector, useStoreDispatch } from 'domains/redux'
|
|
3
|
+
import { useConfig } from 'domains/config'
|
|
3
4
|
import * as Actions from './actions'
|
|
4
5
|
import * as Selectors from './selectors'
|
|
5
6
|
import { visibilityStates } from './constants'
|
|
@@ -24,3 +25,61 @@ export const useVisibility = () => {
|
|
|
24
25
|
setVisibility,
|
|
25
26
|
}
|
|
26
27
|
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Custom hook which enables initializing of IntersectionObserver on any node ref.
|
|
31
|
+
* @param {object} options Hook options.
|
|
32
|
+
* @param {boolean=} options.freezeOnceVisible Stops observing when the root element is visible.
|
|
33
|
+
* @param {boolean=} options.enabled Determines if useIntersect is enabled.
|
|
34
|
+
*/
|
|
35
|
+
const useIntersect = ({ freezeOnceVisible = false, enabled = true }) => {
|
|
36
|
+
const [entry, setEntry] = useState(null)
|
|
37
|
+
const containerRef = useRef(null)
|
|
38
|
+
|
|
39
|
+
const isVisible = !!entry?.isIntersecting || !enabled
|
|
40
|
+
const frozen = isVisible && freezeOnceVisible
|
|
41
|
+
|
|
42
|
+
const observerCallback = ([updatedEntry]) => setEntry(updatedEntry)
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
const node = containerRef?.current
|
|
46
|
+
const hasIOSupport = !!window.IntersectionObserver
|
|
47
|
+
|
|
48
|
+
if (!node && process.env.NODE_ENV === 'development') {
|
|
49
|
+
console.error(
|
|
50
|
+
'containerRef must be set on a DOM element. check the component where useIntersect is being used.',
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Return an arrow function to have a consistent return value
|
|
55
|
+
if (!hasIOSupport || frozen || !node || !enabled) return () => undefined
|
|
56
|
+
|
|
57
|
+
const observerOptions = { threshold: 0, root: null, rootMargin: '0%' }
|
|
58
|
+
const observer = new IntersectionObserver(observerCallback, observerOptions)
|
|
59
|
+
|
|
60
|
+
observer.observe(node)
|
|
61
|
+
|
|
62
|
+
return () => observer.disconnect()
|
|
63
|
+
}, [enabled, frozen])
|
|
64
|
+
|
|
65
|
+
return { isVisible, containerRef }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const useShowInlineView = () => {
|
|
69
|
+
const dispatch = useStoreDispatch()
|
|
70
|
+
const { connectWhenInView } = useConfig()
|
|
71
|
+
const showInlineView = useSelector(Selectors.selectShowInlineView)
|
|
72
|
+
|
|
73
|
+
const { containerRef, isVisible } = useIntersect({
|
|
74
|
+
enabled: connectWhenInView,
|
|
75
|
+
freezeOnceVisible: true,
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (!isVisible) return
|
|
80
|
+
|
|
81
|
+
dispatch(Actions.setShowInlineView())
|
|
82
|
+
}, [dispatch, isVisible])
|
|
83
|
+
|
|
84
|
+
return { containerRef, showInlineView }
|
|
85
|
+
}
|
|
@@ -4,6 +4,7 @@ import { visibilityStates } from './constants'
|
|
|
4
4
|
|
|
5
5
|
const initialState = {
|
|
6
6
|
visibility: visibilityStates.initialize,
|
|
7
|
+
showInlineView: false,
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export default createReducer(
|
|
@@ -14,6 +15,10 @@ export default createReducer(
|
|
|
14
15
|
}),
|
|
15
16
|
[Actions.setVisibility.fulfilled]: (state, { payload: visibility }) =>
|
|
16
17
|
visibility ? { ...state, visibility } : state,
|
|
18
|
+
[Actions.setShowInlineView]: (state) => ({
|
|
19
|
+
...state,
|
|
20
|
+
showInlineView: true,
|
|
21
|
+
}),
|
|
17
22
|
},
|
|
18
23
|
initialState,
|
|
19
24
|
)
|
|
@@ -16,9 +16,13 @@ export const calculateVisibility = ({
|
|
|
16
16
|
config,
|
|
17
17
|
}) => {
|
|
18
18
|
const { defaults, layoutMode, hideOnNoUserResponse } = config
|
|
19
|
-
|
|
20
19
|
const { visible: defaultVisibility } = defaults || {}
|
|
21
20
|
|
|
21
|
+
// The app layout should always be open by default.
|
|
22
|
+
if (layoutMode === 'app' && !previousVisibility) {
|
|
23
|
+
return visibilityStates.open
|
|
24
|
+
}
|
|
25
|
+
|
|
22
26
|
// Requesting open should override the responded check.
|
|
23
27
|
if (
|
|
24
28
|
layoutMode === 'window' &&
|
|
@@ -61,6 +61,8 @@ const StyleGuideApp = ({
|
|
|
61
61
|
config: {
|
|
62
62
|
...bareState.config,
|
|
63
63
|
...config,
|
|
64
|
+
showDisclaimer:
|
|
65
|
+
bareState.config.showDisclaimer || config.showDisclaimer,
|
|
64
66
|
layoutMode: bareState.config.layoutMode,
|
|
65
67
|
...(overrideMessages ? { messages: overrideMessages } : {}),
|
|
66
68
|
showFaq,
|
|
@@ -156,7 +156,6 @@ const infoMessage = {
|
|
|
156
156
|
body: {
|
|
157
157
|
text: 'This is a system generated info message',
|
|
158
158
|
type: 'text',
|
|
159
|
-
variables: {},
|
|
160
159
|
},
|
|
161
160
|
fromClient: false,
|
|
162
161
|
id: randomId(),
|
|
@@ -199,7 +198,7 @@ const participantMessage = {
|
|
|
199
198
|
participant: {
|
|
200
199
|
avatar,
|
|
201
200
|
id: 'agent',
|
|
202
|
-
introduction: "You're now talking to
|
|
201
|
+
introduction: "You're now talking to Mrs. Bot gimme a minit",
|
|
203
202
|
name: 'Mrs. Bot',
|
|
204
203
|
service: {
|
|
205
204
|
expose: { map: {}, version: 2 },
|
|
@@ -223,7 +222,7 @@ const participantMessageDefaultIcon = {
|
|
|
223
222
|
messageStatus: 'received',
|
|
224
223
|
participant: {
|
|
225
224
|
id: 'user',
|
|
226
|
-
introduction: "You're now talking to
|
|
225
|
+
introduction: "You're now talking to Mrs. Bot gimme a minit",
|
|
227
226
|
name: 'Mrs. Bot',
|
|
228
227
|
service: {
|
|
229
228
|
expose: { map: {}, version: 2 },
|
|
@@ -245,7 +244,6 @@ const getCustomMessage = ({ type, data, text }) => ({
|
|
|
245
244
|
type,
|
|
246
245
|
text,
|
|
247
246
|
data,
|
|
248
|
-
variables: {},
|
|
249
247
|
},
|
|
250
248
|
participant: 'agent',
|
|
251
249
|
service: {
|
|
@@ -269,7 +267,6 @@ const shortTextMessage = {
|
|
|
269
267
|
body: {
|
|
270
268
|
text: 'What do you want to do?',
|
|
271
269
|
type: 'text',
|
|
272
|
-
variables: {},
|
|
273
270
|
},
|
|
274
271
|
fromClient: false,
|
|
275
272
|
fromHistory: true,
|
|
@@ -304,7 +301,8 @@ const ctaMessage = {
|
|
|
304
301
|
type: 'message',
|
|
305
302
|
payload: {
|
|
306
303
|
body: {
|
|
307
|
-
description:
|
|
304
|
+
description:
|
|
305
|
+
'Thanks for chatting!\n\nMore info about our <strong>products</strong>?',
|
|
308
306
|
buttonLink: 'https://seamly.ai',
|
|
309
307
|
buttonText: 'View website',
|
|
310
308
|
buttonNewTab: true,
|
|
@@ -331,7 +329,6 @@ const longTextMessage = {
|
|
|
331
329
|
body: {
|
|
332
330
|
text: 'What do you want to do? This is a really long message from a bot that has a lot to say about a lot of things. Currently I am contemplating my own bot existence and constantly asking myself who I am. What do you want to do? This is a really long message from a bot that has a lot to say about a lot of things. Currently I am contemplating my own bot existence and constantly asking myself who I am. What do you want to do? This is a really long message from a bot that has a lot to say about a lot of things. Currently I am contemplating my own bot existence and constantly asking myself who I am.',
|
|
333
331
|
type: 'text',
|
|
334
|
-
variables: {},
|
|
335
332
|
},
|
|
336
333
|
fromClient: false,
|
|
337
334
|
fromHistory: true,
|
|
@@ -353,9 +350,8 @@ const textMessageBoldItalicUnderline = {
|
|
|
353
350
|
type: 'message',
|
|
354
351
|
payload: {
|
|
355
352
|
body: {
|
|
356
|
-
text: 'Bubble with
|
|
353
|
+
text: 'Bubble with <strong>bold</strong> <em>italic</em> <u>underline</u>',
|
|
357
354
|
type: 'text',
|
|
358
|
-
variables: {},
|
|
359
355
|
},
|
|
360
356
|
fromClient: false,
|
|
361
357
|
fromHistory: true,
|
|
@@ -377,24 +373,8 @@ const textMessageWithLinks = {
|
|
|
377
373
|
type: 'message',
|
|
378
374
|
payload: {
|
|
379
375
|
body: {
|
|
380
|
-
text: '
|
|
376
|
+
text: '<a href="https://google.com" data-link-id="1">Link in same window</a> and <a href="https://google.com" data-link-id="2" target="_blank">link in new window</a> embedded in text',
|
|
381
377
|
type: 'text',
|
|
382
|
-
variables: {
|
|
383
|
-
link_1: {
|
|
384
|
-
id: '1',
|
|
385
|
-
name: 'Link in same window',
|
|
386
|
-
newTab: false,
|
|
387
|
-
type: 'link',
|
|
388
|
-
url: 'https://google.com',
|
|
389
|
-
},
|
|
390
|
-
link_2: {
|
|
391
|
-
id: '2',
|
|
392
|
-
name: 'link in new window',
|
|
393
|
-
newTab: true,
|
|
394
|
-
type: 'link',
|
|
395
|
-
url: 'https://google.com',
|
|
396
|
-
},
|
|
397
|
-
},
|
|
398
378
|
},
|
|
399
379
|
fromClient: false,
|
|
400
380
|
fromHistory: true,
|
|
@@ -416,17 +396,8 @@ const textMessageWithLongLink = {
|
|
|
416
396
|
type: 'message',
|
|
417
397
|
payload: {
|
|
418
398
|
body: {
|
|
419
|
-
text: 'Here is a long link
|
|
399
|
+
text: 'Here is a long link <a href="https://google.com" data-link-id="1">click me click me please click me yoohoooo please please click me here I am click me now what are you waiting for click me now now now now now click meeeeeeeeeeeeee</a> embedded in text',
|
|
420
400
|
type: 'text',
|
|
421
|
-
variables: {
|
|
422
|
-
link_1: {
|
|
423
|
-
id: '1',
|
|
424
|
-
name: 'click me click me please click me yoohoooo please please click me here I am click me now what are you waiting for click me now now now now now click meeeeeeeeeeeeee',
|
|
425
|
-
newTab: false,
|
|
426
|
-
type: 'link',
|
|
427
|
-
url: 'https://google.com',
|
|
428
|
-
},
|
|
429
|
-
},
|
|
430
401
|
},
|
|
431
402
|
fromClient: false,
|
|
432
403
|
fromHistory: true,
|
|
@@ -450,7 +421,6 @@ const textMesageWithBullets = {
|
|
|
450
421
|
body: {
|
|
451
422
|
text: '<ul>\n<li>Bullets</li>\n<li>bullets</li>\n<li>bullets</li>\n</ul>\n',
|
|
452
423
|
type: 'text',
|
|
453
|
-
variables: {},
|
|
454
424
|
},
|
|
455
425
|
fromClient: false,
|
|
456
426
|
fromHistory: true,
|
|
@@ -605,7 +575,6 @@ const userMessage = {
|
|
|
605
575
|
body: {
|
|
606
576
|
text: 'This is what the user typed',
|
|
607
577
|
type: 'text',
|
|
608
|
-
variables: {},
|
|
609
578
|
},
|
|
610
579
|
fromClient: true,
|
|
611
580
|
fromHistory: true,
|
|
@@ -624,7 +593,23 @@ const userMessageLong = {
|
|
|
624
593
|
body: {
|
|
625
594
|
text: 'This is what the user typed. And sometimes the user has quite a lot to say and then we get longer lines that need to wrap well and not break the styling so here goes with just such a line right here!!',
|
|
626
595
|
type: 'text',
|
|
627
|
-
|
|
596
|
+
},
|
|
597
|
+
fromClient: true,
|
|
598
|
+
fromHistory: true,
|
|
599
|
+
id: randomId(),
|
|
600
|
+
messageStatus: 'read',
|
|
601
|
+
participant: 'user',
|
|
602
|
+
transactionId: '1cdefea9-7437-4672-bcf8-2c75dc99244c',
|
|
603
|
+
transactionLast: null,
|
|
604
|
+
type: 'text',
|
|
605
|
+
},
|
|
606
|
+
}
|
|
607
|
+
const userMessageWithLinks = {
|
|
608
|
+
type: 'message',
|
|
609
|
+
payload: {
|
|
610
|
+
body: {
|
|
611
|
+
text: '<a href="https://google.com" data-link-id="1">Link in same window</a> and <a href="https://google.com" data-link-id="2" target="_blank">link in new window</a> embedded in text',
|
|
612
|
+
type: 'text',
|
|
628
613
|
},
|
|
629
614
|
fromClient: true,
|
|
630
615
|
fromHistory: true,
|
|
@@ -836,7 +821,7 @@ const cardAskText = {
|
|
|
836
821
|
},
|
|
837
822
|
buttonText: 'Ask about pizzas!',
|
|
838
823
|
description:
|
|
839
|
-
'Pizza Margherita is a
|
|
824
|
+
'Pizza Margherita is a <strong>typical Neapolitan pizza</strong>.\n\nIt is made with San Marzano tomatoes, mozzarella cheese, fresh basil, salt, and extra-virgin olive oil.',
|
|
840
825
|
image:
|
|
841
826
|
'https://developers.seamly.ai/clients/web-ui/static/photos/card-square.jpg',
|
|
842
827
|
title: 'Pizza Margherita',
|
|
@@ -855,7 +840,8 @@ const cardNavigate = {
|
|
|
855
840
|
type: 'navigate',
|
|
856
841
|
},
|
|
857
842
|
buttonText: 'Order now!',
|
|
858
|
-
description:
|
|
843
|
+
description:
|
|
844
|
+
'Pizza Margherita is a <strong>typical Neapolitan pizza</strong>.',
|
|
859
845
|
image:
|
|
860
846
|
'https://developers.seamly.ai/clients/web-ui/static/photos/card-landscape.jpg',
|
|
861
847
|
title: 'Pizza Margherita',
|
|
@@ -1093,6 +1079,7 @@ const standardState = {
|
|
|
1093
1079
|
longTextMessage,
|
|
1094
1080
|
userMessage,
|
|
1095
1081
|
textMessageBoldItalicUnderline,
|
|
1082
|
+
userMessageWithLinks,
|
|
1096
1083
|
newTopicDivider,
|
|
1097
1084
|
imageMessage,
|
|
1098
1085
|
fileDownloadAgentMessage,
|
|
@@ -1120,6 +1107,7 @@ const standardState = {
|
|
|
1120
1107
|
userMessageLong,
|
|
1121
1108
|
fileDownloadUserMessage,
|
|
1122
1109
|
emptyUrlFileDownloadUserMessage,
|
|
1110
|
+
userMessageWithLinks,
|
|
1123
1111
|
],
|
|
1124
1112
|
},
|
|
1125
1113
|
withParticipants: {
|
|
@@ -1197,7 +1185,6 @@ const standardState = {
|
|
|
1197
1185
|
body: {
|
|
1198
1186
|
text: 'Long ago when a dialog started',
|
|
1199
1187
|
type: 'text',
|
|
1200
|
-
variables: {},
|
|
1201
1188
|
},
|
|
1202
1189
|
},
|
|
1203
1190
|
},
|
|
@@ -1259,7 +1246,6 @@ const standardState = {
|
|
|
1259
1246
|
body: {
|
|
1260
1247
|
text: 'Long ago when a dialog started',
|
|
1261
1248
|
type: 'text',
|
|
1262
|
-
variables: {},
|
|
1263
1249
|
},
|
|
1264
1250
|
},
|
|
1265
1251
|
},
|
|
@@ -1272,7 +1258,6 @@ const standardState = {
|
|
|
1272
1258
|
body: {
|
|
1273
1259
|
text: 'Above me should be a time indicator showing the full date',
|
|
1274
1260
|
type: 'text',
|
|
1275
|
-
variables: {},
|
|
1276
1261
|
},
|
|
1277
1262
|
},
|
|
1278
1263
|
},
|
|
@@ -1285,7 +1270,6 @@ const standardState = {
|
|
|
1285
1270
|
body: {
|
|
1286
1271
|
text: 'Another message',
|
|
1287
1272
|
type: 'text',
|
|
1288
|
-
variables: {},
|
|
1289
1273
|
},
|
|
1290
1274
|
},
|
|
1291
1275
|
},
|
|
@@ -1298,7 +1282,6 @@ const standardState = {
|
|
|
1298
1282
|
body: {
|
|
1299
1283
|
text: 'And another message',
|
|
1300
1284
|
type: 'text',
|
|
1301
|
-
variables: {},
|
|
1302
1285
|
},
|
|
1303
1286
|
},
|
|
1304
1287
|
},
|
|
@@ -1311,7 +1294,6 @@ const standardState = {
|
|
|
1311
1294
|
body: {
|
|
1312
1295
|
text: 'Above me should be a time indicator showing "yesterday"',
|
|
1313
1296
|
type: 'text',
|
|
1314
|
-
variables: {},
|
|
1315
1297
|
},
|
|
1316
1298
|
},
|
|
1317
1299
|
},
|
|
@@ -1324,7 +1306,6 @@ const standardState = {
|
|
|
1324
1306
|
body: {
|
|
1325
1307
|
text: 'Another message',
|
|
1326
1308
|
type: 'text',
|
|
1327
|
-
variables: {},
|
|
1328
1309
|
},
|
|
1329
1310
|
},
|
|
1330
1311
|
},
|
|
@@ -1337,7 +1318,6 @@ const standardState = {
|
|
|
1337
1318
|
body: {
|
|
1338
1319
|
text: 'And another message',
|
|
1339
1320
|
type: 'text',
|
|
1340
|
-
variables: {},
|
|
1341
1321
|
},
|
|
1342
1322
|
},
|
|
1343
1323
|
},
|
|
@@ -1350,7 +1330,6 @@ const standardState = {
|
|
|
1350
1330
|
body: {
|
|
1351
1331
|
text: 'Above me should be a time indicator showing me the dialog continues today',
|
|
1352
1332
|
type: 'text',
|
|
1353
|
-
variables: {},
|
|
1354
1333
|
},
|
|
1355
1334
|
},
|
|
1356
1335
|
},
|
|
@@ -1933,6 +1912,7 @@ const newInterface = {
|
|
|
1933
1912
|
description: '',
|
|
1934
1913
|
...baseState,
|
|
1935
1914
|
events: [
|
|
1915
|
+
getCustomMessage(shortTextMessage.payload.body),
|
|
1936
1916
|
{
|
|
1937
1917
|
type: 'service_data',
|
|
1938
1918
|
payload: {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef } from 'preact/hooks'
|
|
2
2
|
import { className } from 'lib/css'
|
|
3
|
-
import parseBody from 'lib/parse-body'
|
|
4
3
|
import { useGeneratedId, useSeamlyCommands } from 'ui/hooks/seamly-hooks'
|
|
5
4
|
import { cardTypes, actionTypes } from 'ui/utils/seamly-utils'
|
|
6
5
|
|
|
@@ -84,7 +83,7 @@ const CardComponent = ({
|
|
|
84
83
|
{description && (
|
|
85
84
|
<div
|
|
86
85
|
className={className('card__description')}
|
|
87
|
-
dangerouslySetInnerHTML={{ __html:
|
|
86
|
+
dangerouslySetInnerHTML={{ __html: description }}
|
|
88
87
|
/>
|
|
89
88
|
)}
|
|
90
89
|
<CardActionComponent
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { useCallback, useState } from 'preact/hooks'
|
|
1
|
+
import { useCallback, useMemo, useState } from 'preact/hooks'
|
|
2
2
|
import { className } from 'lib/css'
|
|
3
|
+
import {
|
|
4
|
+
useEvents,
|
|
5
|
+
useSeamlyCommands,
|
|
6
|
+
useSeamlyDispatchContext,
|
|
7
|
+
} from 'ui/hooks/seamly-hooks'
|
|
3
8
|
import { useI18n } from 'domains/i18n'
|
|
4
9
|
import { useTranslatedEventData } from 'domains/translations'
|
|
5
10
|
import { useUserHasResponded } from 'domains/app'
|
|
6
11
|
import { setHasResponded } from 'domains/app/actions'
|
|
7
|
-
import { actionTypes } from '
|
|
8
|
-
import MessageContainer from '
|
|
9
|
-
import
|
|
10
|
-
useSeamlyCommands,
|
|
11
|
-
useSeamlyDispatchContext,
|
|
12
|
-
} from '../../../hooks/seamly-hooks'
|
|
13
|
-
import SuggestionsList from '../../suggestions/suggestions-list'
|
|
12
|
+
import { actionTypes } from 'ui/utils/seamly-utils'
|
|
13
|
+
import MessageContainer from 'ui/components/conversation/message-container'
|
|
14
|
+
import SuggestionsList from 'ui/components/suggestions/suggestions-list'
|
|
14
15
|
|
|
15
16
|
export const useSuggestions = (event) => {
|
|
16
17
|
const { payload } = event
|
|
@@ -28,11 +29,20 @@ const ConversationSuggestions = ({ event, ...props }) => {
|
|
|
28
29
|
const userResponded = useUserHasResponded()
|
|
29
30
|
const { sendAction, addMessageBubble } = useSeamlyCommands()
|
|
30
31
|
const { suggestions, payload } = useSuggestions(event)
|
|
32
|
+
const events = useEvents()
|
|
31
33
|
|
|
32
34
|
const { t } = useI18n()
|
|
33
35
|
const headingText = t('suggestions.headingText')
|
|
34
36
|
const footerText = t('suggestions.footerText')
|
|
35
37
|
|
|
38
|
+
// We check if there is at least one last transaction
|
|
39
|
+
// to avoid rendering the suggestions before prior events are rendered.
|
|
40
|
+
const hasLastTransactionEvent = useMemo(
|
|
41
|
+
() =>
|
|
42
|
+
events.some(({ payload: eventPayload }) => eventPayload?.transactionLast),
|
|
43
|
+
[events],
|
|
44
|
+
)
|
|
45
|
+
|
|
36
46
|
const handleClick = useCallback(
|
|
37
47
|
({ id, question }) => {
|
|
38
48
|
setIsExpanded(false)
|
|
@@ -56,7 +66,7 @@ const ConversationSuggestions = ({ event, ...props }) => {
|
|
|
56
66
|
[dispatch, sendAction, payload.id, addMessageBubble],
|
|
57
67
|
)
|
|
58
68
|
|
|
59
|
-
if (!isExpanded || userResponded) {
|
|
69
|
+
if (!isExpanded || userResponded || !hasLastTransactionEvent) {
|
|
60
70
|
return null
|
|
61
71
|
}
|
|
62
72
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useCallback } from 'preact/hooks'
|
|
2
2
|
import { className } from 'lib/css'
|
|
3
|
-
import parseBody from 'lib/parse-body'
|
|
4
3
|
import { useGeneratedId, useSeamlyCommands } from 'ui/hooks/seamly-hooks'
|
|
5
4
|
import { actionTypes } from 'ui/utils/seamly-utils'
|
|
6
5
|
import MessageContainer from 'ui/components/conversation/message-container'
|
|
@@ -31,7 +30,7 @@ const Cta = ({ event }) => {
|
|
|
31
30
|
<div
|
|
32
31
|
className={className('cta__content')}
|
|
33
32
|
id={descriptionId}
|
|
34
|
-
dangerouslySetInnerHTML={{ __html:
|
|
33
|
+
dangerouslySetInnerHTML={{ __html: body.description }}
|
|
35
34
|
onClick={eventClick}
|
|
36
35
|
/>
|
|
37
36
|
<a
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { useMemo } from 'preact/hooks'
|
|
2
|
-
import Mustache from 'mustache'
|
|
3
|
-
import parseBody from 'lib/parse-body'
|
|
4
1
|
import EventDivider from 'ui/components/conversation/event-divider'
|
|
5
2
|
import { useTranslatedEventData } from 'domains/translations'
|
|
6
3
|
|
|
@@ -8,13 +5,7 @@ const Participant = ({ event }) => {
|
|
|
8
5
|
const { participant } = event.payload
|
|
9
6
|
const [introduction] = useTranslatedEventData(event)
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
return introduction
|
|
13
|
-
? Mustache.render(parseBody(introduction), participant)
|
|
14
|
-
: undefined
|
|
15
|
-
}, [introduction, participant])
|
|
16
|
-
|
|
17
|
-
if (!intro) {
|
|
8
|
+
if (!introduction) {
|
|
18
9
|
return null
|
|
19
10
|
}
|
|
20
11
|
|
|
@@ -23,7 +14,7 @@ const Participant = ({ event }) => {
|
|
|
23
14
|
graphicSrc={participant.avatar}
|
|
24
15
|
graphicType={participant.avatar ? 'avatar' : undefined}
|
|
25
16
|
iconName={!participant.avatar ? 'balloon' : undefined}
|
|
26
|
-
childrenHTML={
|
|
17
|
+
childrenHTML={introduction}
|
|
27
18
|
dividerType="participant"
|
|
28
19
|
/>
|
|
29
20
|
)
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import parseBody from '../../../../lib/parse-body'
|
|
2
1
|
import useEventLinkClickHandler from './hooks/use-event-link-click-handler'
|
|
3
|
-
import { parseRichText } from './hooks/use-text-rendering'
|
|
4
2
|
import MessageContainer from '../message-container'
|
|
5
3
|
import { useTranslatedEventData } from '../../../../domains/translations'
|
|
6
4
|
|
|
@@ -17,7 +15,7 @@ const Splash = ({ event, ...props }) => {
|
|
|
17
15
|
{...props}
|
|
18
16
|
bodyProps={{
|
|
19
17
|
dangerouslySetInnerHTML: {
|
|
20
|
-
__html:
|
|
18
|
+
__html: body.text,
|
|
21
19
|
},
|
|
22
20
|
}}
|
|
23
21
|
/>
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
import { useMemo } from 'preact/hooks'
|
|
2
|
-
import parseBody from 'lib/parse-body'
|
|
3
2
|
import MessageContainer from 'ui/components/conversation/message-container'
|
|
4
3
|
import { useTranslatedEventData } from 'domains/translations'
|
|
5
4
|
import useEventLinkClickHandler from './hooks/use-event-link-click-handler'
|
|
6
|
-
import { parseRichText } from './hooks/use-text-rendering'
|
|
7
5
|
|
|
8
6
|
const Text = ({ event, ...props }) => {
|
|
9
7
|
const [body] = useTranslatedEventData(event)
|
|
10
8
|
const eventClick = useEventLinkClickHandler(event.payload.id)
|
|
11
9
|
|
|
12
10
|
const containerProps = useMemo(() => {
|
|
13
|
-
if (
|
|
11
|
+
if (event.payload.optimisticallyInjected) {
|
|
14
12
|
return {
|
|
15
|
-
|
|
16
|
-
dangerouslySetInnerHTML: {
|
|
17
|
-
__html: parseRichText(parseBody(body.text), body.variables),
|
|
18
|
-
},
|
|
19
|
-
},
|
|
13
|
+
children: <p>{body.text}</p>,
|
|
20
14
|
}
|
|
21
15
|
}
|
|
16
|
+
|
|
22
17
|
return {
|
|
23
|
-
|
|
18
|
+
bodyProps: {
|
|
19
|
+
dangerouslySetInnerHTML: {
|
|
20
|
+
__html: body.text,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
24
23
|
}
|
|
25
24
|
}, [body, event])
|
|
25
|
+
|
|
26
26
|
return (
|
|
27
27
|
<MessageContainer
|
|
28
28
|
type="text"
|
|
@@ -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
|