@seamly/web-ui 20.3.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 +61 -94
- 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 +467 -3965
- 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 +432 -3865
- package/build/dist/lib/standalone.min.js +1 -1
- package/build/dist/lib/style-guide.js +267 -191
- package/build/dist/lib/style-guide.min.js +1 -1
- package/build/dist/lib/styles.css +1 -1
- package/package.json +2 -4
- package/src/.DS_Store +0 -0
- 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/store/state-reducer.js +1 -0
- 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/index.js +1 -0
- package/src/javascripts/style-guide/components/app.js +2 -0
- package/src/javascripts/style-guide/states.js +55 -50
- package/src/javascripts/ui/components/conversation/conversation.js +9 -10
- 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/image.js +11 -3
- 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/entry/text-entry/text-entry-form.js +6 -0
- 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/index.js +2 -2
- 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 +7 -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 +24 -7
- package/src/stylesheets/5-components/_message-count.scss +5 -2
- package/CHANGELOG.md +0 -729
- package/src/javascripts/lib/parse-body.js +0 -10
- package/src/javascripts/ui/components/conversation/event/hooks/use-text-rendering.js +0 -35
|
@@ -17,6 +17,7 @@ const baseState = {
|
|
|
17
17
|
},
|
|
18
18
|
initialState: {},
|
|
19
19
|
unreadEvents: 0,
|
|
20
|
+
loadedImageEventIds: [],
|
|
20
21
|
isLoading: false,
|
|
21
22
|
idleDetachCountdown: { hasCountdown: false, isActive: false },
|
|
22
23
|
resumeConversationPrompt: false,
|
|
@@ -155,7 +156,6 @@ const infoMessage = {
|
|
|
155
156
|
body: {
|
|
156
157
|
text: 'This is a system generated info message',
|
|
157
158
|
type: 'text',
|
|
158
|
-
variables: {},
|
|
159
159
|
},
|
|
160
160
|
fromClient: false,
|
|
161
161
|
id: randomId(),
|
|
@@ -198,7 +198,7 @@ const participantMessage = {
|
|
|
198
198
|
participant: {
|
|
199
199
|
avatar,
|
|
200
200
|
id: 'agent',
|
|
201
|
-
introduction: "You're now talking to
|
|
201
|
+
introduction: "You're now talking to Mrs. Bot gimme a minit",
|
|
202
202
|
name: 'Mrs. Bot',
|
|
203
203
|
service: {
|
|
204
204
|
expose: { map: {}, version: 2 },
|
|
@@ -222,7 +222,7 @@ const participantMessageDefaultIcon = {
|
|
|
222
222
|
messageStatus: 'received',
|
|
223
223
|
participant: {
|
|
224
224
|
id: 'user',
|
|
225
|
-
introduction: "You're now talking to
|
|
225
|
+
introduction: "You're now talking to Mrs. Bot gimme a minit",
|
|
226
226
|
name: 'Mrs. Bot',
|
|
227
227
|
service: {
|
|
228
228
|
expose: { map: {}, version: 2 },
|
|
@@ -244,7 +244,6 @@ const getCustomMessage = ({ type, data, text }) => ({
|
|
|
244
244
|
type,
|
|
245
245
|
text,
|
|
246
246
|
data,
|
|
247
|
-
variables: {},
|
|
248
247
|
},
|
|
249
248
|
participant: 'agent',
|
|
250
249
|
service: {
|
|
@@ -268,7 +267,6 @@ const shortTextMessage = {
|
|
|
268
267
|
body: {
|
|
269
268
|
text: 'What do you want to do?',
|
|
270
269
|
type: 'text',
|
|
271
|
-
variables: {},
|
|
272
270
|
},
|
|
273
271
|
fromClient: false,
|
|
274
272
|
fromHistory: true,
|
|
@@ -303,7 +301,8 @@ const ctaMessage = {
|
|
|
303
301
|
type: 'message',
|
|
304
302
|
payload: {
|
|
305
303
|
body: {
|
|
306
|
-
description:
|
|
304
|
+
description:
|
|
305
|
+
'Thanks for chatting!\n\nMore info about our <strong>products</strong>?',
|
|
307
306
|
buttonLink: 'https://seamly.ai',
|
|
308
307
|
buttonText: 'View website',
|
|
309
308
|
buttonNewTab: true,
|
|
@@ -330,7 +329,6 @@ const longTextMessage = {
|
|
|
330
329
|
body: {
|
|
331
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.',
|
|
332
331
|
type: 'text',
|
|
333
|
-
variables: {},
|
|
334
332
|
},
|
|
335
333
|
fromClient: false,
|
|
336
334
|
fromHistory: true,
|
|
@@ -352,9 +350,8 @@ const textMessageBoldItalicUnderline = {
|
|
|
352
350
|
type: 'message',
|
|
353
351
|
payload: {
|
|
354
352
|
body: {
|
|
355
|
-
text: 'Bubble with
|
|
353
|
+
text: 'Bubble with <strong>bold</strong> <em>italic</em> <u>underline</u>',
|
|
356
354
|
type: 'text',
|
|
357
|
-
variables: {},
|
|
358
355
|
},
|
|
359
356
|
fromClient: false,
|
|
360
357
|
fromHistory: true,
|
|
@@ -376,24 +373,8 @@ const textMessageWithLinks = {
|
|
|
376
373
|
type: 'message',
|
|
377
374
|
payload: {
|
|
378
375
|
body: {
|
|
379
|
-
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',
|
|
380
377
|
type: 'text',
|
|
381
|
-
variables: {
|
|
382
|
-
link_1: {
|
|
383
|
-
id: '1',
|
|
384
|
-
name: 'Link in same window',
|
|
385
|
-
newTab: false,
|
|
386
|
-
type: 'link',
|
|
387
|
-
url: 'https://google.com',
|
|
388
|
-
},
|
|
389
|
-
link_2: {
|
|
390
|
-
id: '2',
|
|
391
|
-
name: 'link in new window',
|
|
392
|
-
newTab: true,
|
|
393
|
-
type: 'link',
|
|
394
|
-
url: 'https://google.com',
|
|
395
|
-
},
|
|
396
|
-
},
|
|
397
378
|
},
|
|
398
379
|
fromClient: false,
|
|
399
380
|
fromHistory: true,
|
|
@@ -415,17 +396,8 @@ const textMessageWithLongLink = {
|
|
|
415
396
|
type: 'message',
|
|
416
397
|
payload: {
|
|
417
398
|
body: {
|
|
418
|
-
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',
|
|
419
400
|
type: 'text',
|
|
420
|
-
variables: {
|
|
421
|
-
link_1: {
|
|
422
|
-
id: '1',
|
|
423
|
-
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',
|
|
424
|
-
newTab: false,
|
|
425
|
-
type: 'link',
|
|
426
|
-
url: 'https://google.com',
|
|
427
|
-
},
|
|
428
|
-
},
|
|
429
401
|
},
|
|
430
402
|
fromClient: false,
|
|
431
403
|
fromHistory: true,
|
|
@@ -449,7 +421,6 @@ const textMesageWithBullets = {
|
|
|
449
421
|
body: {
|
|
450
422
|
text: '<ul>\n<li>Bullets</li>\n<li>bullets</li>\n<li>bullets</li>\n</ul>\n',
|
|
451
423
|
type: 'text',
|
|
452
|
-
variables: {},
|
|
453
424
|
},
|
|
454
425
|
fromClient: false,
|
|
455
426
|
fromHistory: true,
|
|
@@ -604,7 +575,6 @@ const userMessage = {
|
|
|
604
575
|
body: {
|
|
605
576
|
text: 'This is what the user typed',
|
|
606
577
|
type: 'text',
|
|
607
|
-
variables: {},
|
|
608
578
|
},
|
|
609
579
|
fromClient: true,
|
|
610
580
|
fromHistory: true,
|
|
@@ -623,7 +593,23 @@ const userMessageLong = {
|
|
|
623
593
|
body: {
|
|
624
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!!',
|
|
625
595
|
type: 'text',
|
|
626
|
-
|
|
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',
|
|
627
613
|
},
|
|
628
614
|
fromClient: true,
|
|
629
615
|
fromHistory: true,
|
|
@@ -835,7 +821,7 @@ const cardAskText = {
|
|
|
835
821
|
},
|
|
836
822
|
buttonText: 'Ask about pizzas!',
|
|
837
823
|
description:
|
|
838
|
-
'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.',
|
|
839
825
|
image:
|
|
840
826
|
'https://developers.seamly.ai/clients/web-ui/static/photos/card-square.jpg',
|
|
841
827
|
title: 'Pizza Margherita',
|
|
@@ -854,7 +840,8 @@ const cardNavigate = {
|
|
|
854
840
|
type: 'navigate',
|
|
855
841
|
},
|
|
856
842
|
buttonText: 'Order now!',
|
|
857
|
-
description:
|
|
843
|
+
description:
|
|
844
|
+
'Pizza Margherita is a <strong>typical Neapolitan pizza</strong>.',
|
|
858
845
|
image:
|
|
859
846
|
'https://developers.seamly.ai/clients/web-ui/static/photos/card-landscape.jpg',
|
|
860
847
|
title: 'Pizza Margherita',
|
|
@@ -1092,6 +1079,7 @@ const standardState = {
|
|
|
1092
1079
|
longTextMessage,
|
|
1093
1080
|
userMessage,
|
|
1094
1081
|
textMessageBoldItalicUnderline,
|
|
1082
|
+
userMessageWithLinks,
|
|
1095
1083
|
newTopicDivider,
|
|
1096
1084
|
imageMessage,
|
|
1097
1085
|
fileDownloadAgentMessage,
|
|
@@ -1119,6 +1107,7 @@ const standardState = {
|
|
|
1119
1107
|
userMessageLong,
|
|
1120
1108
|
fileDownloadUserMessage,
|
|
1121
1109
|
emptyUrlFileDownloadUserMessage,
|
|
1110
|
+
userMessageWithLinks,
|
|
1122
1111
|
],
|
|
1123
1112
|
},
|
|
1124
1113
|
withParticipants: {
|
|
@@ -1196,7 +1185,6 @@ const standardState = {
|
|
|
1196
1185
|
body: {
|
|
1197
1186
|
text: 'Long ago when a dialog started',
|
|
1198
1187
|
type: 'text',
|
|
1199
|
-
variables: {},
|
|
1200
1188
|
},
|
|
1201
1189
|
},
|
|
1202
1190
|
},
|
|
@@ -1258,7 +1246,6 @@ const standardState = {
|
|
|
1258
1246
|
body: {
|
|
1259
1247
|
text: 'Long ago when a dialog started',
|
|
1260
1248
|
type: 'text',
|
|
1261
|
-
variables: {},
|
|
1262
1249
|
},
|
|
1263
1250
|
},
|
|
1264
1251
|
},
|
|
@@ -1271,7 +1258,6 @@ const standardState = {
|
|
|
1271
1258
|
body: {
|
|
1272
1259
|
text: 'Above me should be a time indicator showing the full date',
|
|
1273
1260
|
type: 'text',
|
|
1274
|
-
variables: {},
|
|
1275
1261
|
},
|
|
1276
1262
|
},
|
|
1277
1263
|
},
|
|
@@ -1284,7 +1270,6 @@ const standardState = {
|
|
|
1284
1270
|
body: {
|
|
1285
1271
|
text: 'Another message',
|
|
1286
1272
|
type: 'text',
|
|
1287
|
-
variables: {},
|
|
1288
1273
|
},
|
|
1289
1274
|
},
|
|
1290
1275
|
},
|
|
@@ -1297,7 +1282,6 @@ const standardState = {
|
|
|
1297
1282
|
body: {
|
|
1298
1283
|
text: 'And another message',
|
|
1299
1284
|
type: 'text',
|
|
1300
|
-
variables: {},
|
|
1301
1285
|
},
|
|
1302
1286
|
},
|
|
1303
1287
|
},
|
|
@@ -1310,7 +1294,6 @@ const standardState = {
|
|
|
1310
1294
|
body: {
|
|
1311
1295
|
text: 'Above me should be a time indicator showing "yesterday"',
|
|
1312
1296
|
type: 'text',
|
|
1313
|
-
variables: {},
|
|
1314
1297
|
},
|
|
1315
1298
|
},
|
|
1316
1299
|
},
|
|
@@ -1323,7 +1306,6 @@ const standardState = {
|
|
|
1323
1306
|
body: {
|
|
1324
1307
|
text: 'Another message',
|
|
1325
1308
|
type: 'text',
|
|
1326
|
-
variables: {},
|
|
1327
1309
|
},
|
|
1328
1310
|
},
|
|
1329
1311
|
},
|
|
@@ -1336,7 +1318,6 @@ const standardState = {
|
|
|
1336
1318
|
body: {
|
|
1337
1319
|
text: 'And another message',
|
|
1338
1320
|
type: 'text',
|
|
1339
|
-
variables: {},
|
|
1340
1321
|
},
|
|
1341
1322
|
},
|
|
1342
1323
|
},
|
|
@@ -1349,7 +1330,6 @@ const standardState = {
|
|
|
1349
1330
|
body: {
|
|
1350
1331
|
text: 'Above me should be a time indicator showing me the dialog continues today',
|
|
1351
1332
|
type: 'text',
|
|
1352
|
-
variables: {},
|
|
1353
1333
|
},
|
|
1354
1334
|
},
|
|
1355
1335
|
},
|
|
@@ -1772,6 +1752,30 @@ const inlineInterface = {
|
|
|
1772
1752
|
},
|
|
1773
1753
|
},
|
|
1774
1754
|
},
|
|
1755
|
+
minimizedInlineCharacterLimit: {
|
|
1756
|
+
category: categoryKeys.minimizedInline,
|
|
1757
|
+
headingText: 'Inline minimized with limited characters',
|
|
1758
|
+
description: '',
|
|
1759
|
+
inline: {
|
|
1760
|
+
...baseState,
|
|
1761
|
+
config: {
|
|
1762
|
+
...baseState.config,
|
|
1763
|
+
layoutMode: 'inline',
|
|
1764
|
+
},
|
|
1765
|
+
visibility: {
|
|
1766
|
+
...baseState.visibility,
|
|
1767
|
+
visibility: visibilityStates.minimized,
|
|
1768
|
+
},
|
|
1769
|
+
entryMeta: {
|
|
1770
|
+
...baseState.entryMeta,
|
|
1771
|
+
options: {
|
|
1772
|
+
text: {
|
|
1773
|
+
limit: 120,
|
|
1774
|
+
},
|
|
1775
|
+
},
|
|
1776
|
+
},
|
|
1777
|
+
},
|
|
1778
|
+
},
|
|
1775
1779
|
minimizedInlinePrechat: {
|
|
1776
1780
|
category: categoryKeys.minimizedInline,
|
|
1777
1781
|
headingText: 'Inline minimized with pre-chat messages',
|
|
@@ -1908,6 +1912,7 @@ const newInterface = {
|
|
|
1908
1912
|
description: '',
|
|
1909
1913
|
...baseState,
|
|
1910
1914
|
events: [
|
|
1915
|
+
getCustomMessage(shortTextMessage.payload.body),
|
|
1911
1916
|
{
|
|
1912
1917
|
type: 'service_data',
|
|
1913
1918
|
payload: {
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
useSkiplink,
|
|
7
7
|
useSkiplinkTargetFocusing,
|
|
8
8
|
} from 'ui/hooks/seamly-hooks'
|
|
9
|
-
import { useEvents } from 'ui/hooks/seamly-state-hooks'
|
|
9
|
+
import { useEvents, useLoadedImageEventIds } from 'ui/hooks/seamly-state-hooks'
|
|
10
10
|
import PrivacyDisclaimer from 'ui/components/layout/privacy-disclaimer'
|
|
11
11
|
import { useVisibility } from 'domains/visibility'
|
|
12
12
|
import Event from './event/event'
|
|
@@ -15,21 +15,20 @@ import ComponentFilter from './component-filter'
|
|
|
15
15
|
|
|
16
16
|
const Conversation = () => {
|
|
17
17
|
const { t } = useI18n()
|
|
18
|
-
const
|
|
18
|
+
const chatBodyContainer = useRef(null)
|
|
19
19
|
const events = useEvents()
|
|
20
20
|
const isLoading = useSeamlyIsLoading()
|
|
21
21
|
const { isOpen } = useVisibility()
|
|
22
22
|
const skiplinkTargetId = useSkiplink()
|
|
23
23
|
const focusSkiplinkTarget = useSkiplinkTargetFocusing()
|
|
24
|
+
const loadedImageEventIds = useLoadedImageEventIds()
|
|
24
25
|
|
|
25
26
|
useEffect(() => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
})
|
|
32
|
-
}, [events, isLoading, isOpen])
|
|
27
|
+
const containerElement = chatBodyContainer.current
|
|
28
|
+
if (containerElement) {
|
|
29
|
+
containerElement.scrollTop = containerElement.scrollHeight
|
|
30
|
+
}
|
|
31
|
+
}, [events, isLoading, isOpen, loadedImageEventIds])
|
|
33
32
|
|
|
34
33
|
const renderEvents = () => {
|
|
35
34
|
let prevParticipant = null
|
|
@@ -72,7 +71,7 @@ const Conversation = () => {
|
|
|
72
71
|
{t('skiplinkText')}
|
|
73
72
|
</a>
|
|
74
73
|
)}
|
|
75
|
-
<div className={className('chat__body')} ref={
|
|
74
|
+
<div className={className('chat__body')} ref={chatBodyContainer}>
|
|
76
75
|
<div className={className('conversation__container')}>
|
|
77
76
|
<PrivacyDisclaimer />
|
|
78
77
|
<ol className={className('conversation')}>
|
|
@@ -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,12 +1,22 @@
|
|
|
1
1
|
import { useState } from 'preact/hooks'
|
|
2
2
|
import MessageContainer from 'ui/components/conversation/message-container'
|
|
3
3
|
import { useTranslatedEventData } from 'domains/translations'
|
|
4
|
+
import { useStoreDispatch } from 'domains/redux'
|
|
4
5
|
import ImageLightbox from './image-lightbox'
|
|
6
|
+
import { seamlyActions } from '../../../utils/seamly-utils'
|
|
7
|
+
|
|
8
|
+
const { SET_LOADED_IMAGE_EVENT_IDS } = seamlyActions
|
|
5
9
|
|
|
6
10
|
const Image = ({ event, descriptorId, ...props }) => {
|
|
7
11
|
const [body] = useTranslatedEventData(event)
|
|
8
12
|
const { description, url, isZoomable } = body
|
|
9
13
|
const [showLighbox, setShowLightbox] = useState(false)
|
|
14
|
+
const dispatch = useStoreDispatch()
|
|
15
|
+
|
|
16
|
+
const handleOnLoad = () => {
|
|
17
|
+
dispatch({ type: SET_LOADED_IMAGE_EVENT_IDS, eventId: event.payload.id })
|
|
18
|
+
setShowLightbox(true)
|
|
19
|
+
}
|
|
10
20
|
|
|
11
21
|
return (
|
|
12
22
|
<MessageContainer event={event} type="image" {...props}>
|
|
@@ -14,9 +24,7 @@ const Image = ({ event, descriptorId, ...props }) => {
|
|
|
14
24
|
src={url}
|
|
15
25
|
id={descriptorId}
|
|
16
26
|
alt={description}
|
|
17
|
-
onLoad={
|
|
18
|
-
setShowLightbox(true)
|
|
19
|
-
}}
|
|
27
|
+
onLoad={handleOnLoad}
|
|
20
28
|
/>
|
|
21
29
|
{isZoomable && showLighbox && (
|
|
22
30
|
<ImageLightbox description={description} url={url} />
|
|
@@ -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"
|
|
@@ -64,6 +64,11 @@ export default function TextEntryForm({ controlName, skipLinkId }) {
|
|
|
64
64
|
}
|
|
65
65
|
}, [setBlockAutoEntrySwitch, hasValue])
|
|
66
66
|
|
|
67
|
+
const handlePointerDown = (event) => {
|
|
68
|
+
// When a message is submitted, the keyboard should be prevented from closing on mobile devices
|
|
69
|
+
event.preventDefault()
|
|
70
|
+
}
|
|
71
|
+
|
|
67
72
|
return (
|
|
68
73
|
<Form
|
|
69
74
|
className={className('entry-form')}
|
|
@@ -99,6 +104,7 @@ export default function TextEntryForm({ controlName, skipLinkId }) {
|
|
|
99
104
|
<button
|
|
100
105
|
className={className('button', 'input__submit')}
|
|
101
106
|
type="submit"
|
|
107
|
+
onPointerDown={handlePointerDown}
|
|
102
108
|
aria-disabled={!hasValue || reachedCharacterLimit ? 'true' : null}
|
|
103
109
|
>
|
|
104
110
|
<Icon name="send" size="32" alt={t('input.sendMessage')} />
|