@seamly/web-ui 21.0.9 → 22.0.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.
Files changed (141) hide show
  1. package/build/dist/lib/components.js +9228 -7777
  2. package/build/dist/lib/components.js.map +1 -0
  3. package/build/dist/lib/components.min.js +2 -1
  4. package/build/dist/lib/components.min.js.LICENSE.txt +2 -2
  5. package/build/dist/lib/components.min.js.map +1 -0
  6. package/build/dist/lib/config.js +2 -1
  7. package/build/dist/lib/config.js.map +1 -0
  8. package/build/dist/lib/config.min.js +2 -1
  9. package/build/dist/lib/config.min.js.map +1 -0
  10. package/build/dist/lib/contexts.js +2 -1
  11. package/build/dist/lib/contexts.js.map +1 -0
  12. package/build/dist/lib/contexts.min.js +2 -1
  13. package/build/dist/lib/contexts.min.js.map +1 -0
  14. package/build/dist/lib/deprecated-view.css +1 -1
  15. package/build/dist/lib/deprecated-view.js +1 -1
  16. package/build/dist/lib/hooks.js +6999 -5996
  17. package/build/dist/lib/hooks.js.map +1 -0
  18. package/build/dist/lib/hooks.min.js +2 -1
  19. package/build/dist/lib/hooks.min.js.map +1 -0
  20. package/build/dist/lib/index.debug.js +940 -370
  21. package/build/dist/lib/index.debug.js.map +1 -0
  22. package/build/dist/lib/index.debug.min.js +2 -1
  23. package/build/dist/lib/index.debug.min.js.LICENSE.txt +334 -110
  24. package/build/dist/lib/index.debug.min.js.map +1 -0
  25. package/build/dist/lib/index.js +2810 -5472
  26. package/build/dist/lib/index.js.map +1 -0
  27. package/build/dist/lib/index.min.js +2 -1
  28. package/build/dist/lib/index.min.js.LICENSE.txt +2 -2
  29. package/build/dist/lib/index.min.js.map +1 -0
  30. package/build/dist/lib/sounds/beep.mp3 +0 -0
  31. package/build/dist/lib/standalone.js +10575 -13540
  32. package/build/dist/lib/standalone.js.map +1 -0
  33. package/build/dist/lib/standalone.min.js +2 -1
  34. package/build/dist/lib/standalone.min.js.LICENSE.txt +1 -1
  35. package/build/dist/lib/standalone.min.js.map +1 -0
  36. package/build/dist/lib/storage.js +2 -1
  37. package/build/dist/lib/storage.js.map +1 -0
  38. package/build/dist/lib/storage.min.js +2 -1
  39. package/build/dist/lib/storage.min.js.map +1 -0
  40. package/build/dist/lib/style-guide.js +1701 -5859
  41. package/build/dist/lib/style-guide.js.map +1 -0
  42. package/build/dist/lib/style-guide.min.js +2 -1
  43. package/build/dist/lib/style-guide.min.js.LICENSE.txt +2 -2
  44. package/build/dist/lib/style-guide.min.js.map +1 -0
  45. package/build/dist/lib/styles-default-implementation.css +1 -1
  46. package/build/dist/lib/styles-default-implementation.js +1 -1
  47. package/build/dist/lib/styles.css +1 -1
  48. package/build/dist/lib/styles.js +1 -1
  49. package/build/dist/lib/utils.js +11536 -14530
  50. package/build/dist/lib/utils.js.map +1 -0
  51. package/build/dist/lib/utils.min.js +2 -1
  52. package/build/dist/lib/utils.min.js.LICENSE.txt +1 -6
  53. package/build/dist/lib/utils.min.js.map +1 -0
  54. package/package.json +58 -48
  55. package/src/javascripts/api/conversation-connector.ts +2 -0
  56. package/src/javascripts/api/errors/seamly-api-error.ts +15 -0
  57. package/src/javascripts/api/index.ts +168 -94
  58. package/src/javascripts/config.ts +1 -1
  59. package/src/javascripts/config.types.ts +18 -11
  60. package/src/javascripts/domains/config/selectors.ts +1 -1
  61. package/src/javascripts/domains/config/slice.ts +12 -0
  62. package/src/javascripts/domains/forms/forms.types.ts +1 -0
  63. package/src/javascripts/domains/forms/hooks.ts +10 -2
  64. package/src/javascripts/domains/store/selectors.ts +23 -10
  65. package/src/javascripts/domains/store/slice.ts +63 -11
  66. package/src/javascripts/domains/store/store.types.ts +41 -1
  67. package/src/javascripts/domains/translations/components/options-button.tsx +1 -4
  68. package/src/javascripts/domains/translations/hooks.ts +11 -4
  69. package/src/javascripts/index.ts +2 -0
  70. package/src/javascripts/lib/url-helpers.ts +24 -0
  71. package/src/javascripts/schema.ts +10 -0
  72. package/src/javascripts/style-guide/states.js +109 -0
  73. package/src/javascripts/ui/components/conversation/conversation.tsx +2 -0
  74. package/src/javascripts/ui/components/conversation/event/chat-scroll/chat-scroll-provider.tsx +2 -0
  75. package/src/javascripts/ui/components/conversation/event/choice-prompt.js +1 -1
  76. package/src/javascripts/ui/components/conversation/event/text.js +1 -1
  77. package/src/javascripts/ui/components/conversation/event/upload.js +50 -9
  78. package/src/javascripts/ui/components/conversation/use-chat-scroll.ts +3 -2
  79. package/src/javascripts/ui/components/core/seamly-event-subscriber.ts +7 -1
  80. package/src/javascripts/ui/components/core/seamly-file-upload.tsx +156 -0
  81. package/src/javascripts/ui/components/entry/abort-transaction-button/abort-transaction-button.tsx +45 -0
  82. package/src/javascripts/ui/components/entry/text-entry/hooks.ts +108 -0
  83. package/src/javascripts/ui/components/entry/text-entry/index.js +7 -4
  84. package/src/javascripts/ui/components/entry/text-entry/{text-entry-form.js → text-entry-form.tsx} +8 -22
  85. package/src/javascripts/ui/components/form-controls/{input.js → input.tsx} +13 -2
  86. package/src/javascripts/ui/components/form-controls/{wrapper.js → wrapper.tsx} +8 -4
  87. package/src/javascripts/ui/components/view/{index.js → index.tsx} +53 -4
  88. package/src/javascripts/ui/components/view/window-view/{index.js → index.tsx} +14 -2
  89. package/src/javascripts/ui/components/widgets/{in-out-transition.js → in-out-transition.tsx} +67 -28
  90. package/src/javascripts/ui/hooks/{seamly-api-hooks.js → seamly-api-hooks.ts} +1 -1
  91. package/src/javascripts/ui/hooks/sounds/beep.mp3 +0 -0
  92. package/src/javascripts/ui/hooks/use-click-outside.ts +5 -3
  93. package/src/javascripts/ui/hooks/use-notifications.ts +114 -0
  94. package/src/javascripts/ui/hooks/use-timeout.ts +20 -0
  95. package/src/stylesheets/3-chat/_chat.scss +3 -5
  96. package/src/stylesheets/4-base/_formelements.scss +0 -36
  97. package/src/stylesheets/5-components/_abort-transaction.scss +10 -0
  98. package/src/stylesheets/5-components/_buttons.scss +18 -3
  99. package/src/stylesheets/5-components/_character-limit.scss +2 -2
  100. package/src/stylesheets/5-components/_chat-status.scss +26 -37
  101. package/src/stylesheets/5-components/_choice-prompt.scss +9 -10
  102. package/src/stylesheets/5-components/_conversation.scss +9 -62
  103. package/src/stylesheets/5-components/_disclaimer.scss +11 -3
  104. package/src/stylesheets/5-components/_error.scss +3 -2
  105. package/src/stylesheets/5-components/_idle.scss +3 -8
  106. package/src/stylesheets/5-components/_input.scss +34 -13
  107. package/src/stylesheets/5-components/_interrupt.scss +3 -10
  108. package/src/stylesheets/5-components/_loader.scss +1 -2
  109. package/src/stylesheets/5-components/_message-author.scss +2 -4
  110. package/src/stylesheets/5-components/_message-body.scss +33 -10
  111. package/src/stylesheets/5-components/_message-card.scss +2 -10
  112. package/src/stylesheets/5-components/_message-carousel.scss +4 -4
  113. package/src/stylesheets/5-components/_message-cta.scss +0 -6
  114. package/src/stylesheets/5-components/_message.scss +1 -0
  115. package/src/stylesheets/5-components/_modal.scss +2 -5
  116. package/src/stylesheets/5-components/_options.scss +17 -22
  117. package/src/stylesheets/5-components/_pre-chat-messages.scss +3 -1
  118. package/src/stylesheets/5-components/_prompt.scss +3 -7
  119. package/src/stylesheets/5-components/_skip-link.scss +2 -1
  120. package/src/stylesheets/5-components/_suggestions.scss +2 -2
  121. package/src/stylesheets/5-components/_translation-options.scss +5 -2
  122. package/src/stylesheets/5-components/_unread-messages.scss +33 -0
  123. package/src/stylesheets/5-components/_upload.scss +20 -27
  124. package/src/stylesheets/6-default-implementation/_hover.scss +14 -17
  125. package/src/stylesheets/7-deprecated/1-settings/_config.scss +17 -0
  126. package/src/stylesheets/7-deprecated/3-app/_app.scss +2 -1
  127. package/src/stylesheets/7-deprecated/5-components/_card.scss +1 -0
  128. package/src/stylesheets/7-deprecated/5-components/_chat-status.scss +66 -20
  129. package/src/stylesheets/7-deprecated/5-components/_conversation.scss +1 -4
  130. package/src/stylesheets/7-deprecated/5-components/_input.scss +6 -1
  131. package/src/stylesheets/7-deprecated/5-components/_interrupt.scss +1 -4
  132. package/src/stylesheets/7-deprecated/5-components/_message.scss +49 -12
  133. package/src/stylesheets/7-deprecated/5-components/_translation-options.scss +30 -37
  134. package/src/stylesheets/7-deprecated/5-components/_unread-messages.scss +38 -0
  135. package/src/stylesheets/deprecated-view.scss +1 -0
  136. package/src/stylesheets/styles.scss +2 -0
  137. package/webpack/config.common.js +6 -1
  138. package/webpack/config.package.js +18 -0
  139. package/webpack/defaults.js +1 -1
  140. package/src/javascripts/ui/components/core/seamly-file-upload.js +0 -86
  141. package/src/javascripts/ui/components/entry/text-entry/hooks.js +0 -46
@@ -6,7 +6,7 @@ import {
6
6
  useMemo,
7
7
  } from 'preact/hooks'
8
8
  import { useDispatch, useSelector } from 'react-redux'
9
- import type { FormContextType } from 'domains/forms/forms.types'
9
+ import type { ControlState, FormContextType } from 'domains/forms/forms.types'
10
10
  import {
11
11
  getControlTouchedByName,
12
12
  getControlValueByName,
@@ -42,7 +42,15 @@ export function useValidations(values, validationSchema) {
42
42
  }
43
43
  }
44
44
 
45
- export function useFormControl(name) {
45
+ export function useFormControl(name): [
46
+ {
47
+ name: string
48
+ onInput: (_e: any) => void
49
+ onBlur: () => void
50
+ value: string
51
+ },
52
+ ControlState,
53
+ ] {
46
54
  const dispatch = useDispatch()
47
55
  const { formId, updateControlValue, updateControlTouched, errors } =
48
56
  useFormContext()
@@ -2,15 +2,28 @@ import { createSelector } from '@reduxjs/toolkit'
2
2
  import { selectEvents } from 'ui/hooks/seamly-state-hooks'
3
3
  import { readStates } from 'ui/utils/seamly-utils'
4
4
  import { isUnreadMessage } from 'domains/store/slice'
5
+ import { RootState } from '.'
5
6
 
6
- export const selectUnreadEventIds = createSelector(selectEvents, (events) => {
7
- return events
8
- .filter((event) => {
9
- return (
10
- isUnreadMessage(event) &&
11
- event.type !== 'service_data' &&
12
- event.payload?.messageStatus === readStates.received
13
- )
14
- })
15
- .map((event) => event.payload.id)
7
+ export const selectUnreadEvents = createSelector(selectEvents, (events) => {
8
+ return events.filter((event) => {
9
+ return (
10
+ isUnreadMessage(event) &&
11
+ event.type !== 'service_data' &&
12
+ event.payload?.messageStatus === readStates.received
13
+ )
14
+ })
16
15
  })
16
+ export const selectLastUnreadEvent = createSelector(
17
+ selectUnreadEvents,
18
+ (events) => events.at(-1),
19
+ )
20
+
21
+ export const selectUnreadEventIds = createSelector(
22
+ selectUnreadEvents,
23
+ (events) => events.map((event) => event.payload.id),
24
+ )
25
+
26
+ export const selectShowNotifications = createSelector(
27
+ ({ state }: RootState) => state.options.features?.webNotifications,
28
+ (webNotifications) => webNotifications?.enabled,
29
+ )
@@ -1,5 +1,5 @@
1
1
  import { PayloadAction, createSlice, isAnyOf } from '@reduxjs/toolkit'
2
- import { ConversationHistoryResponse } from 'api'
2
+ import type { ConversationHistoryResponse } from 'api'
3
3
  import { getTimeFromSeconds } from 'ui/utils/general-utils'
4
4
  import {
5
5
  entryTypes,
@@ -13,6 +13,7 @@ import { initializeConfig } from 'domains/config/actions'
13
13
  import type {
14
14
  AckEvent,
15
15
  ChannelEvent,
16
+ CurrentUploadPayload,
16
17
  EntryMeta,
17
18
  MessageParticipant,
18
19
  ServiceInfo,
@@ -21,6 +22,7 @@ import type {
21
22
  import { randomId } from 'lib/id'
22
23
 
23
24
  export const isUnreadMessage = ({ type, payload }: ChannelEvent) =>
25
+ document.visibilityState === 'hidden' ||
24
26
  (type === eventTypes.message && !payload.fromClient) ||
25
27
  (type === eventTypes.info && payload.type === payloadTypes.text)
26
28
 
@@ -104,12 +106,19 @@ const calculateNewEntryMeta = (
104
106
  channelEvent?: ChannelEvent,
105
107
  ) => {
106
108
  const entry =
107
- channelEvent?.type === 'message' ? channelEvent?.payload.entry : {}
109
+ channelEvent?.type === 'message'
110
+ ? channelEvent?.payload.translatedEntry || channelEvent?.payload.entry
111
+ : {}
108
112
 
109
113
  const { blockAutoEntrySwitch } = entryMeta
114
+ const actions = channelEvent?.payload?.actions || {}
110
115
 
111
116
  if (!entry) {
112
- return { ...entryMeta, optionsOverride: {} }
117
+ return {
118
+ ...entryMeta,
119
+ optionsOverride: {},
120
+ actions,
121
+ }
113
122
  }
114
123
 
115
124
  const { type, options } = entry
@@ -127,6 +136,7 @@ const calculateNewEntryMeta = (
127
136
  ...entryMeta.optionsOverride,
128
137
  [type]: options || {},
129
138
  },
139
+ actions,
130
140
  }
131
141
  }
132
142
 
@@ -151,6 +161,7 @@ export const initialStoreState: StoreState = {
151
161
  resumeConversationPrompt: false,
152
162
  serviceInfo: {
153
163
  activeServiceSessionId: '',
164
+ proactiveMessages: false,
154
165
  },
155
166
  participantInfo: {
156
167
  participants: {},
@@ -166,13 +177,18 @@ export const initialStoreState: StoreState = {
166
177
  headerCollapseButtonId: randomId(),
167
178
  serviceData: {},
168
179
  options: {
169
- features: {},
180
+ features: {
181
+ webNotifications: {
182
+ enabled: false,
183
+ },
184
+ },
170
185
  panelActive: false,
171
186
  optionActive: '',
172
187
  userSelectedOptions: {},
173
188
  },
174
189
  showFileUpload: false,
175
190
  currentUploads: [],
191
+ processingFileUploads: [],
176
192
  entryMeta: {
177
193
  default: entryTypes.text,
178
194
  active: entryTypes.text,
@@ -180,6 +196,7 @@ export const initialStoreState: StoreState = {
180
196
  blockAutoEntrySwitch: false,
181
197
  options: {},
182
198
  optionsOverride: {},
199
+ actions: {},
183
200
  },
184
201
  seamlyContainerElement: null,
185
202
  }
@@ -393,6 +410,8 @@ export const storeSlice = createSlice({
393
410
  state.serviceInfo = {
394
411
  ...state.serviceInfo,
395
412
  activeServiceSessionId,
413
+ proactiveMessages:
414
+ activeServiceSettings?.proactiveMessages?.enabled || false,
396
415
  }
397
416
 
398
417
  state.serviceData = {
@@ -402,7 +421,7 @@ export const storeSlice = createSlice({
402
421
 
403
422
  state.options = {
404
423
  ...state.options,
405
- features: newFeatures || {},
424
+ features: newFeatures,
406
425
  }
407
426
  state.entryMeta =
408
427
  !newFeaturesHasUpload &&
@@ -497,15 +516,15 @@ export const storeSlice = createSlice({
497
516
  state.options.features[payload.key].enabled = payload.enabled
498
517
  },
499
518
  clearFeatures: (state) => {
500
- // case CLEAR_FEATURES:
501
- state.options.features = {}
519
+ state.options.features = {
520
+ webNotifications: state.options.features.webNotifications,
521
+ }
502
522
  },
503
523
  showOption: (state, { payload }) => {
504
524
  state.options.panelActive = true
505
525
  state.options.optionActive = payload
506
526
  },
507
527
  hideOption: (state) => {
508
- // case HIDE_OPTION:
509
528
  state.options.panelActive = false
510
529
  state.options.optionActive = ''
511
530
  },
@@ -523,6 +542,7 @@ export const storeSlice = createSlice({
523
542
  state.entryMeta.active = payload.default
524
543
  state.entryMeta.options = payload.options || {}
525
544
  state.entryMeta.optionsOverride = {}
545
+ state.entryMeta.actions = {}
526
546
  },
527
547
  setActiveEntryType: (state, { payload }) => {
528
548
  state.entryMeta.active = payload
@@ -530,7 +550,13 @@ export const storeSlice = createSlice({
530
550
  setUserEntryType: (state, { payload }) => {
531
551
  state.entryMeta.userSelected = payload
532
552
  },
533
- registerUpload: (state, { payload }) => {
553
+ clearAbortTransaction: (state) => {
554
+ state.entryMeta.actions = {}
555
+ },
556
+ registerUpload: (
557
+ state,
558
+ { payload }: PayloadAction<Omit<CurrentUploadPayload, 'progress'>>,
559
+ ) => {
534
560
  state.currentUploads.push({
535
561
  id: payload.fileId,
536
562
  name: payload.fileName,
@@ -541,7 +567,12 @@ export const storeSlice = createSlice({
541
567
  uploadHandle: payload.uploadHandle,
542
568
  })
543
569
  },
544
- setUploadProgress: (state, { payload }) => {
570
+ setUploadProgress: (
571
+ state,
572
+ {
573
+ payload,
574
+ }: PayloadAction<Omit<CurrentUploadPayload, 'uploadHandle' | 'fileName'>>,
575
+ ) => {
545
576
  state.currentUploads = state.currentUploads.map((fileUpload) => {
546
577
  if (fileUpload.id === payload.fileId) {
547
578
  return {
@@ -555,6 +586,14 @@ export const storeSlice = createSlice({
555
586
  return fileUpload
556
587
  })
557
588
  },
589
+ startProcessingImage: (state, { payload }: PayloadAction<string>) => {
590
+ state.processingFileUploads.push(payload)
591
+ },
592
+ doneProcessingImage: (state, { payload }: PayloadAction<string>) => {
593
+ state.processingFileUploads = state.processingFileUploads.filter(
594
+ (fileId) => fileId !== payload,
595
+ )
596
+ },
558
597
  setUploadError: (state, { payload }) => {
559
598
  state.currentUploads = state.currentUploads.map((fileUpload) => {
560
599
  if (fileUpload.id === payload.fileId) {
@@ -569,7 +608,10 @@ export const storeSlice = createSlice({
569
608
  return fileUpload
570
609
  })
571
610
  },
572
- setUploadComplete: (state, { payload }) => {
611
+ setUploadComplete: (
612
+ state,
613
+ { payload }: PayloadAction<CurrentUploadPayload['fileId']>,
614
+ ) => {
573
615
  state.currentUploads = state.currentUploads.map((fileUpload) => {
574
616
  if (fileUpload.id === payload) {
575
617
  return {
@@ -586,6 +628,12 @@ export const storeSlice = createSlice({
586
628
  setSeamlyContainerElement: (state, { payload }) => {
587
629
  state.seamlyContainerElement = payload
588
630
  },
631
+ setProactiveMessages: (
632
+ state,
633
+ { payload }: PayloadAction<ServiceInfo['proactiveMessages']>,
634
+ ) => {
635
+ state.serviceInfo.proactiveMessages = payload
636
+ },
589
637
  },
590
638
  extraReducers: (builder) => {
591
639
  builder
@@ -640,14 +688,18 @@ export const {
640
688
  setSeamlyContainerElement,
641
689
  setServiceDataItem,
642
690
  setServiceEntryMetadata,
691
+ clearAbortTransaction,
643
692
  setUploadComplete,
644
693
  setUploadError,
645
694
  setUploadProgress,
695
+ startProcessingImage,
696
+ doneProcessingImage,
646
697
  setUserEntryType,
647
698
  setUserSelectedOption,
648
699
  setUserSelectedOptions,
649
700
  showOption,
650
701
  stopIdleDetachCountdownCounter,
702
+ setProactiveMessages,
651
703
  } = storeSlice.actions
652
704
 
653
705
  export default storeSlice.reducer
@@ -20,6 +20,14 @@ export type AckEvent = {
20
20
  type DefaultEventProps = {
21
21
  timeIndicator?: number
22
22
  key?: string
23
+ translatedEntry?: components['schemas']['MessageMessage']['entry']
24
+ actions?: {
25
+ abortTransaction?: {
26
+ label: string
27
+ topicFallbackMessage: string
28
+ topicName: string
29
+ }
30
+ }
23
31
  }
24
32
 
25
33
  export type MessageInfo = components['schemas']['MessageInfo'] &
@@ -32,6 +40,12 @@ export interface InfoEvent {
32
40
  export type MessageMessage = components['schemas']['MessageMessage'] & {
33
41
  key?: string
34
42
  }
43
+
44
+ export type MessageUpload = components['schemas']['MessageMessage'] & {
45
+ body: components['schemas']['MessageBodyUpload'] &
46
+ components['schemas']['MessageBodyVideo']
47
+ }
48
+
35
49
  export interface MessageEvent {
36
50
  type: 'message'
37
51
  payload: MessageMessage & DefaultEventProps
@@ -71,7 +85,12 @@ export type HistoryResponse = {
71
85
  export type HistoryEvents = HistoryResponse['events']
72
86
 
73
87
  export type EntryMetaOptions =
74
- History['activeServiceSettings']['entry']['options']
88
+ History['activeServiceSettings']['entry']['options'] & {
89
+ text?: History['activeServiceSettings']['entry']['options']['text'] & {
90
+ placeholder?: string
91
+ label?: string
92
+ }
93
+ }
75
94
 
76
95
  export type EntryMeta = {
77
96
  default: typeof entryTypes.text
@@ -80,6 +99,13 @@ export type EntryMeta = {
80
99
  blockAutoEntrySwitch: boolean
81
100
  options: EntryMetaOptions
82
101
  optionsOverride: EntryMetaOptions
102
+ actions?: {
103
+ abortTransaction?: {
104
+ label: string
105
+ topicFallbackMessage: string
106
+ topicName: string
107
+ }
108
+ }
83
109
  }
84
110
 
85
111
  export type ParticipantInfo = {
@@ -96,8 +122,16 @@ export interface CurrentUpload {
96
122
  complete: boolean
97
123
  }
98
124
 
125
+ export interface CurrentUploadPayload {
126
+ fileId: CurrentUpload['id']
127
+ progress: CurrentUpload['progress']
128
+ fileName: CurrentUpload['name']
129
+ uploadHandle: CurrentUpload['uploadHandle']
130
+ }
131
+
99
132
  export type ServiceInfo = {
100
133
  activeServiceSessionId: string
134
+ proactiveMessages: boolean
101
135
  }
102
136
 
103
137
  export interface StoreState {
@@ -144,6 +178,11 @@ export interface StoreState {
144
178
  enabledFromEntry: boolean
145
179
  enabled: boolean
146
180
  }
181
+ webNotifications: {
182
+ enabled: boolean
183
+ }
184
+ cobrowsing?: { enabled: boolean }
185
+ sendTranscript?: { enabled: boolean }
147
186
  }
148
187
  panelActive: boolean
149
188
  optionActive: string
@@ -151,6 +190,7 @@ export interface StoreState {
151
190
  }
152
191
  showFileUpload: boolean
153
192
  currentUploads: CurrentUpload[]
193
+ processingFileUploads: string[]
154
194
  entryMeta: EntryMeta
155
195
  seamlyContainerElement: null | HTMLElement
156
196
  }
@@ -58,9 +58,6 @@ export default function TranslationsOptionsButton({
58
58
  onKeyDown={onMainKeyDownHandler}
59
59
  >
60
60
  <InOutTransition
61
- onOutTransitionComplete={() => undefined}
62
- onInTransitionComplete={() => undefined}
63
- timeout={0}
64
61
  transitionStartState={transitionStartStates.notRendered}
65
62
  isActive={menuIsOpen}
66
63
  >
@@ -83,7 +80,7 @@ export default function TranslationsOptionsButton({
83
80
  onKeyDown={handleToggleKeyDown}
84
81
  ref={toggleButton}
85
82
  aria-haspopup="dialog"
86
- aria-expanded={menuIsOpen.toString()}
83
+ aria-expanded={menuIsOpen}
87
84
  >
88
85
  {children}
89
86
  </button>
@@ -85,32 +85,39 @@ export function useTranslatedEventData(channelEvent: ChannelEvent): {
85
85
  const getTranslations = (): {
86
86
  body: EventDataBody
87
87
  translatedBody: TranslatedEventDataBody
88
+ translation?: any
88
89
  } => {
89
- if (!channelEvent.payload)
90
+ if (!channelEvent?.payload)
90
91
  return { body: undefined, translatedBody: undefined }
91
92
  if (channelEvent.type === 'participant') {
92
93
  return {
93
94
  body: channelEvent.payload?.participant.introduction,
94
95
  translatedBody:
95
96
  channelEvent.payload?.participant?.translatedIntroduction,
97
+ translation:
98
+ // @ts-ignore
99
+ channelEvent.payload?.participant?.translation,
96
100
  }
97
101
  }
98
102
 
99
103
  return {
100
104
  body: channelEvent.payload.body,
101
105
  translatedBody: channelEvent.payload.translatedBody,
106
+ // @ts-ignore
107
+ translation: channelEvent?.payload?.translation,
102
108
  }
103
109
  }
104
110
 
105
- const { translatedBody, body } = getTranslations()
111
+ const { translatedBody, translation, body } = getTranslations()
106
112
  const hasTranslation = !!translatedBody
107
113
  const isTranslated = useSelector(selectIsTranslated(channelEvent))
108
114
 
109
115
  return {
110
- body: hasTranslation && isTranslated ? translatedBody?.data : body,
116
+ // @ts-ignore
117
+ body: hasTranslation && isTranslated ? translatedBody : body,
111
118
  hasTranslation,
112
119
  isTranslated: isTranslated && hasTranslation,
113
- locale: translatedBody?.locale,
120
+ locale: translation?.locale,
114
121
  }
115
122
  }
116
123
 
@@ -1,3 +1,5 @@
1
+ // Polyfills
2
+ import 'core-js/es/array/at'
1
3
  // Used by: Client
2
4
  import initializeExternalApi from './lib/external-api/initialize-api'
3
5
 
@@ -40,6 +40,30 @@ export const setSearchParam = (key: string, value: string): void => {
40
40
  window.location.href = url.toString()
41
41
  }
42
42
 
43
+ // Adds search parameters to the url
44
+ export const setSearchParams = (params: Record<string, string>): void => {
45
+ const url = new URL(window.location.href)
46
+ const { searchParams } = url
47
+
48
+ Object.entries(params).forEach(([key, value]) => {
49
+ searchParams.set(key, value)
50
+ })
51
+
52
+ url.search = searchParams.toString()
53
+ window.location.href = url.toString()
54
+ }
55
+
56
+ // Removes a search parameter from the url
57
+ export const removeSearchParam = (...keys: string[]): void => {
58
+ const url = new URL(window.location.href)
59
+ const { searchParams } = url
60
+
61
+ keys.forEach((key) => searchParams.delete(key))
62
+
63
+ url.search = searchParams.toString()
64
+ window.history.replaceState({}, null, url)
65
+ }
66
+
43
67
  // Replace search parameters with those found in URL search parameters or sessionStorage
44
68
  export const replaceSearchParams = <T extends string>(
45
69
  ...keys: T[]
@@ -251,6 +251,11 @@ export interface components {
251
251
  EntryText: {
252
252
  /** @description Settings of the text entry */
253
253
  options?: {
254
+ /**
255
+ * @description A custom label to show above the input field
256
+ * @example Your name:
257
+ */
258
+ label?: string | null
254
259
  /**
255
260
  * @description Whether or not the expected input is language neutral. A postal code or a last name for example are independent of language.
256
261
  * @example false
@@ -261,6 +266,11 @@ export interface components {
261
266
  * @example 100
262
267
  */
263
268
  limit?: number | null
269
+ /**
270
+ * @description A custom placeholder to show in the input field
271
+ * @example Your message (max. 100 characters)
272
+ */
273
+ placeholder?: string | null
264
274
  }
265
275
  /**
266
276
  * @description Entry type
@@ -13,6 +13,7 @@ const baseState = {
13
13
  showFaq: false,
14
14
  showDisclaimer: false,
15
15
  showSuggestions: true,
16
+ preChat: true,
16
17
  },
17
18
  initialState: {},
18
19
  unreadEvents: 0,
@@ -55,6 +56,7 @@ const baseState = {
55
56
  optionsOverride: {},
56
57
  },
57
58
  currentUploads: [],
59
+ processingFileUploads: [],
58
60
  }
59
61
 
60
62
  const avatar =
@@ -701,12 +703,28 @@ const fileDownloadAgentMessage = {
701
703
  },
702
704
  }
703
705
 
706
+ const fileUploadAgentMessage = {
707
+ type: 'message',
708
+ payload: {
709
+ ...fileDownloadPayload,
710
+ fromClient: false,
711
+ participant: 'agent',
712
+ id: randomId(),
713
+ },
714
+ }
715
+
716
+ sessionStorage.setItem(
717
+ `image-${fileDownloadAgentMessage.payload.id}`,
718
+ fileDownloadAgentMessage.payload.body.url,
719
+ )
720
+
704
721
  const deletedFileDownloadAgentMessage = {
705
722
  ...fileDownloadAgentMessage,
706
723
  payload: {
707
724
  ...fileDownloadAgentMessage.payload,
708
725
  body: {
709
726
  ...fileDownloadAgentMessage.payload.body,
727
+ url: undefined,
710
728
  },
711
729
  id: randomId(),
712
730
  },
@@ -722,6 +740,11 @@ const fileDownloadUserMessage = {
722
740
  },
723
741
  }
724
742
 
743
+ sessionStorage.setItem(
744
+ `image-${fileDownloadUserMessage.payload.id}`,
745
+ fileDownloadUserMessage.payload.body.url,
746
+ )
747
+
725
748
  const emptyUrlFileDownloadUserMessage = {
726
749
  ...fileDownloadUserMessage,
727
750
  payload: {
@@ -1143,6 +1166,7 @@ const standardState = {
1143
1166
  userMessageWithLinks,
1144
1167
  newTopicDivider,
1145
1168
  imageMessage,
1169
+ fileUploadAgentMessage,
1146
1170
  fileDownloadAgentMessage,
1147
1171
  deletedFileDownloadAgentMessage,
1148
1172
  userMessageLong,
@@ -1219,7 +1243,9 @@ const standardState = {
1219
1243
  imageMessage,
1220
1244
  videoMessage,
1221
1245
  imageMessageWithLightbox,
1246
+ fileUploadAgentMessage,
1222
1247
  fileDownloadAgentMessage,
1248
+ fileDownloadUserMessage,
1223
1249
  ],
1224
1250
  },
1225
1251
  systemMessages: {
@@ -1495,6 +1521,46 @@ const standardState = {
1495
1521
  optionsOverride: {},
1496
1522
  },
1497
1523
  },
1524
+ inputLabel: {
1525
+ category: categoryKeys.features,
1526
+ headingText: 'Input label',
1527
+ ...baseState,
1528
+ entryMeta: {
1529
+ default: 'text',
1530
+ active: 'text',
1531
+ options: {
1532
+ text: { limit: null },
1533
+ },
1534
+ optionsOverride: {
1535
+ text: {
1536
+ label: 'What is your name?',
1537
+ limit: null,
1538
+ placeholder: 'Please enter your name',
1539
+ },
1540
+ },
1541
+ },
1542
+ },
1543
+ abortTransactional: {
1544
+ category: categoryKeys.features,
1545
+ headingText: 'Abort transaction',
1546
+ ...baseState,
1547
+ events: [shortTextMessage, userMessage],
1548
+ entryMeta: {
1549
+ default: 'text',
1550
+ active: 'text',
1551
+ options: {
1552
+ text: { limit: null },
1553
+ },
1554
+ optionsOverride: {},
1555
+ actions: {
1556
+ abortTransaction: {
1557
+ label: 'Ask for another question',
1558
+ topicFallbackMessage: 'Oops...',
1559
+ topicName: 'abort_transactional',
1560
+ },
1561
+ },
1562
+ },
1563
+ },
1498
1564
  fileUploadToggle: {
1499
1565
  category: categoryKeys.uploads,
1500
1566
  headingText: 'File upload toggle button',
@@ -2193,6 +2259,49 @@ export const getStateObj = (layoutModes, customComponentEventBodies) => {
2193
2259
  headerTitles,
2194
2260
  },
2195
2261
  },
2262
+ minimizedWindowPrechatDelayed: {
2263
+ category: categoryKeys.minimizedWindow,
2264
+ headingText: 'Minimized with delayed pre-chat messages',
2265
+ description: '',
2266
+ window: {
2267
+ ...baseState,
2268
+ config: {
2269
+ ...baseState.config,
2270
+ layoutMode: 'window',
2271
+ preChatEvents: [splashMessage],
2272
+ preChat: {
2273
+ enterDelay: 1000,
2274
+ exitAfter: 3000,
2275
+ },
2276
+ },
2277
+ visibility: {
2278
+ ...baseState.visibility,
2279
+ visibility: visibilityStates.minimized,
2280
+ },
2281
+ participantInfo,
2282
+ headerTitles,
2283
+ },
2284
+ },
2285
+ minimizedWindowPrechatDisabled: {
2286
+ category: categoryKeys.minimizedWindow,
2287
+ headingText: 'Minimized with disabled pre-chat messages',
2288
+ description: '',
2289
+ window: {
2290
+ ...baseState,
2291
+ config: {
2292
+ ...baseState.config,
2293
+ layoutMode: 'window',
2294
+ preChatEvents: [splashMessage],
2295
+ preChat: false,
2296
+ },
2297
+ visibility: {
2298
+ ...baseState.visibility,
2299
+ visibility: visibilityStates.minimized,
2300
+ },
2301
+ participantInfo,
2302
+ headerTitles,
2303
+ },
2304
+ },
2196
2305
  }
2197
2306
  : {}),
2198
2307
  ...inlineInterface,
@@ -8,6 +8,7 @@ import { useEvents } from 'ui/hooks/seamly-state-hooks'
8
8
  import { useI18n } from 'domains/i18n/hooks'
9
9
  import { useVisibility } from 'domains/visibility/hooks'
10
10
  import { className } from 'lib/css'
11
+ import AbortTransactionButton from '../entry/abort-transaction-button/abort-transaction-button'
11
12
  import ComponentFilter from './component-filter'
12
13
  import Event from './event/event'
13
14
  import Loader from './loader'
@@ -77,6 +78,7 @@ const Conversation = () => {
77
78
  <Events />
78
79
  </ComponentFilter>
79
80
  {isLoading && <Loader />}
81
+ <AbortTransactionButton />
80
82
  </ol>
81
83
  </div>
82
84
  </div>
@@ -11,6 +11,8 @@ const ChatScrollProvider = ({ children }) => {
11
11
  const eventRefs = useMemo(
12
12
  () =>
13
13
  events.reduce<Record<string, RefObject<HTMLElement>>>((acc, value) => {
14
+ if (!value.payload.id) return acc
15
+
14
16
  acc[value.payload.id] = createRef()
15
17
  return acc
16
18
  }, {}),