@seamly/web-ui 20.2.0 → 20.4.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 (107) hide show
  1. package/build/dist/lib/deprecated-view.css +1 -1
  2. package/build/dist/lib/index.debug.js +211 -200
  3. package/build/dist/lib/index.debug.min.js +1 -1
  4. package/build/dist/lib/index.debug.min.js.LICENSE.txt +40 -36
  5. package/build/dist/lib/index.js +1811 -1137
  6. package/build/dist/lib/index.min.js +1 -1
  7. package/build/dist/lib/index.min.js.LICENSE.txt +2 -2
  8. package/build/dist/lib/standalone.js +5167 -2856
  9. package/build/dist/lib/standalone.min.js +1 -1
  10. package/build/dist/lib/standalone.min.js.LICENSE.txt +1 -1
  11. package/build/dist/lib/style-guide.js +7427 -7243
  12. package/build/dist/lib/style-guide.min.js +1 -1
  13. package/build/dist/lib/styles.css +1 -1
  14. package/package.json +15 -14
  15. package/src/javascripts/api/index.js +5 -4
  16. package/src/javascripts/domains/app/actions.js +22 -10
  17. package/src/javascripts/domains/config/reducer.js +2 -2
  18. package/src/javascripts/domains/forms/reducer.js +1 -1
  19. package/src/javascripts/domains/i18n/reducer.js +1 -1
  20. package/src/javascripts/domains/interrupt/reducer.js +1 -1
  21. package/src/javascripts/domains/store/state-reducer.js +1 -0
  22. package/src/javascripts/domains/translations/components/chat-status.js +10 -9
  23. package/src/javascripts/domains/translations/components/options-button.js +8 -3
  24. package/src/javascripts/domains/translations/components/options-dialog/form.js +6 -5
  25. package/src/javascripts/domains/translations/components/options-dialog/index.js +5 -2
  26. package/src/javascripts/domains/translations/hooks.js +15 -1
  27. package/src/javascripts/domains/translations/middleware.js +5 -2
  28. package/src/javascripts/domains/translations/reducer.js +2 -2
  29. package/src/javascripts/domains/visibility/actions.js +1 -1
  30. package/src/javascripts/index.js +1 -0
  31. package/src/javascripts/lib/debug.js +1 -0
  32. package/src/javascripts/lib/external-api/index.js +29 -4
  33. package/src/javascripts/style-guide/components/app.js +1 -1
  34. package/src/javascripts/style-guide/components/static-core.js +1 -4
  35. package/src/javascripts/style-guide/states.js +25 -0
  36. package/src/javascripts/ui/components/conversation/conversation.js +11 -12
  37. package/src/javascripts/ui/components/conversation/event/card-component.js +4 -1
  38. package/src/javascripts/ui/components/conversation/event/carousel-component/index.js +1 -1
  39. package/src/javascripts/ui/components/conversation/event/conversation-suggestions.js +18 -10
  40. package/src/javascripts/ui/components/conversation/event/cta.js +1 -1
  41. package/src/javascripts/ui/components/conversation/event/divider/variants/new-translation.js +4 -9
  42. package/src/javascripts/ui/components/conversation/event/image.js +12 -4
  43. package/src/javascripts/ui/components/conversation/event/text.js +2 -2
  44. package/src/javascripts/ui/components/conversation/loader.js +1 -1
  45. package/src/javascripts/ui/components/conversation/message-container.js +1 -1
  46. package/src/javascripts/ui/components/core/seamly-activity-monitor.js +1 -1
  47. package/src/javascripts/ui/components/core/seamly-core.js +2 -2
  48. package/src/javascripts/ui/components/core/seamly-file-upload.js +1 -1
  49. package/src/javascripts/ui/components/core/seamly-instance-functions-loader.js +6 -2
  50. package/src/javascripts/ui/components/core/seamly-live-region.js +2 -2
  51. package/src/javascripts/ui/components/entry/entry-container.js +2 -2
  52. package/src/javascripts/ui/components/entry/text-entry/hooks.js +1 -4
  53. package/src/javascripts/ui/components/entry/text-entry/text-entry-form.js +7 -1
  54. package/src/javascripts/ui/components/form-controls/select.js +1 -1
  55. package/src/javascripts/ui/components/layout/agent-info.js +1 -1
  56. package/src/javascripts/ui/components/layout/header.js +1 -1
  57. package/src/javascripts/ui/components/options/options-button.js +1 -1
  58. package/src/javascripts/ui/components/options/options.js +1 -1
  59. package/src/javascripts/ui/components/options/transcript/index.js +1 -1
  60. package/src/javascripts/ui/components/suggestions/suggestions-item.js +1 -1
  61. package/src/javascripts/ui/components/view/index.js +2 -2
  62. package/src/javascripts/ui/components/warnings/resume-conversation-prompt.js +1 -1
  63. package/src/javascripts/ui/components/widgets/lightbox.js +1 -1
  64. package/src/javascripts/ui/hooks/file-upload-hooks.js +2 -3
  65. package/src/javascripts/ui/hooks/focus-helper-hooks.js +2 -2
  66. package/src/javascripts/ui/hooks/seamly-entry-hooks.js +1 -1
  67. package/src/javascripts/ui/hooks/seamly-option-hooks.js +1 -1
  68. package/src/javascripts/ui/hooks/seamly-state-hooks.js +3 -0
  69. package/src/javascripts/ui/hooks/use-seamly-chat.js +2 -2
  70. package/src/javascripts/ui/hooks/use-seamly-commands.js +7 -7
  71. package/src/javascripts/ui/hooks/use-seamly-idle-detach-countdown.js +4 -4
  72. package/src/javascripts/ui/hooks/use-seamly-resume-conversation-prompt.js +2 -2
  73. package/src/javascripts/ui/hooks/use-single-file-upload.js +1 -1
  74. package/src/javascripts/ui/utils/general-utils.js +1 -1
  75. package/src/javascripts/ui/utils/seamly-utils.js +9 -1
  76. package/src/stylesheets/1-settings/_animations.scss +1 -1
  77. package/src/stylesheets/1-settings/_config.scss +17 -2
  78. package/src/stylesheets/2-tools/_functions.scss +4 -4
  79. package/src/stylesheets/2-tools/_mixins.scss +16 -14
  80. package/src/stylesheets/3-chat/_chat.scss +1 -1
  81. package/src/stylesheets/5-components/_choice-prompt.scss +1 -1
  82. package/src/stylesheets/5-components/_collapse-button.scss +1 -1
  83. package/src/stylesheets/5-components/_input.scss +1 -1
  84. package/src/stylesheets/5-components/_loader.scss +2 -2
  85. package/src/stylesheets/5-components/_message-carousel.scss +1 -1
  86. package/src/stylesheets/5-components/_message-count.scss +6 -3
  87. package/src/stylesheets/5-components/_message.scss +1 -2
  88. package/src/stylesheets/5-components/_modal.scss +1 -1
  89. package/src/stylesheets/5-components/_options.scss +3 -3
  90. package/src/stylesheets/5-components/_suggestions.scss +4 -5
  91. package/src/stylesheets/5-components/_upload.scss +1 -1
  92. package/src/stylesheets/7-deprecated/1-settings/_animations.scss +1 -1
  93. package/src/stylesheets/7-deprecated/1-settings/_config.scss +10 -7
  94. package/src/stylesheets/7-deprecated/2-tools/_functions.scss +2 -2
  95. package/src/stylesheets/7-deprecated/2-tools/_mixins.scss +6 -4
  96. package/src/stylesheets/7-deprecated/3-app/_app.scss +2 -2
  97. package/src/stylesheets/7-deprecated/4-base/_formelements.scss +1 -0
  98. package/src/stylesheets/7-deprecated/5-components/_chat-status.scss +1 -1
  99. package/src/stylesheets/7-deprecated/5-components/_collapse-button.scss +1 -1
  100. package/src/stylesheets/7-deprecated/5-components/_disclaimer.scss +1 -1
  101. package/src/stylesheets/7-deprecated/5-components/_input.scss +2 -1
  102. package/src/stylesheets/7-deprecated/5-components/_loader.scss +2 -2
  103. package/src/stylesheets/7-deprecated/5-components/_message-count.scss +1 -1
  104. package/src/stylesheets/7-deprecated/5-components/_message.scss +3 -4
  105. package/src/stylesheets/7-deprecated/5-components/_modal.scss +1 -1
  106. package/src/stylesheets/7-deprecated/5-components/_options.scss +2 -2
  107. package/src/stylesheets/7-deprecated/5-components/_upload.scss +1 -1
@@ -53,12 +53,23 @@ class ExternalApi {
53
53
 
54
54
  setContext(action, args) {
55
55
  switch (action) {
56
+ case 'setTopic':
57
+ const { name } = args
58
+ if (name) {
59
+ this.context.topic = name
60
+ }
61
+ return true
56
62
  case 'setTranslation':
57
63
  const { enabled, locale } = args
58
64
  if (!!enabled && locale) {
59
65
  this.context.translationLocale = locale
60
66
  }
61
67
  return true
68
+ case 'setVariables':
69
+ if (Object.keys(args).length > 0) {
70
+ this.context.variables = { ...args }
71
+ }
72
+ return true
62
73
  default:
63
74
  return false
64
75
  }
@@ -145,19 +156,21 @@ class ExternalApi {
145
156
  }
146
157
 
147
158
  getCombinedConfig(userConfig) {
148
- if (typeof this.appConfig === 'function') {
149
- return this.appConfig(userConfig)
150
- }
151
159
  const context = {
152
160
  ...(userConfig.context || this.appConfig.context),
153
161
  ...this.context,
154
162
  }
163
+ // Key `variables` should only be set if there are any variables
164
+ const variables = this.getMergedVariables(userConfig)
165
+ if (Object.keys(variables).length > 0) {
166
+ context.variables = variables
167
+ }
155
168
  const defaults = {
156
169
  ...this.appConfig.defaults,
157
170
  ...userConfig.defaults,
158
171
  }
159
172
 
160
- return {
173
+ const combinedConfig = {
161
174
  ...this.appConfig,
162
175
  ...userConfig,
163
176
  api: {
@@ -167,6 +180,18 @@ class ExternalApi {
167
180
  context: Object.keys(context).length ? context : undefined,
168
181
  defaults: Object.keys(defaults).length ? defaults : undefined,
169
182
  }
183
+
184
+ return typeof this.appConfig === 'function'
185
+ ? this.appConfig(combinedConfig)
186
+ : combinedConfig
187
+ }
188
+
189
+ getMergedVariables(userConfig) {
190
+ return {
191
+ ...(this.appConfig.context?.variables || {}),
192
+ ...(userConfig.context?.variables || {}),
193
+ ...(this.context.variables || {}),
194
+ }
170
195
  }
171
196
  }
172
197
 
@@ -1,8 +1,8 @@
1
1
  import { useCallback, useEffect, useState } from 'preact/hooks'
2
2
  import { getUrlParams, getUrlSearchString, randomId } from '@seamly/web-ui'
3
+ import { getStateObj, getDeprecatedStateObj } from 'style-guide/states'
3
4
  import StyleGuideView from './view'
4
5
  import StyleGuideLinks from './links'
5
- import { getStateObj, getDeprecatedStateObj } from 'style-guide/states'
6
6
 
7
7
  const StyleGuideApp = ({
8
8
  config,
@@ -12,10 +12,7 @@ import {
12
12
  import stateReducer from 'domains/store/state-reducer'
13
13
  import { Reducer as formReducer } from 'domains/forms'
14
14
  import { Reducer as translationsReducer } from 'domains/translations'
15
- import {
16
- Reducer as i18nReducer,
17
- Actions as I18nActions,
18
- } from 'domains/i18n'
15
+ import { Reducer as i18nReducer, Actions as I18nActions } from 'domains/i18n'
19
16
  import { Reducer as interruptReducer } from 'domains/interrupt'
20
17
  import { Reducer as appReducer } from 'domains/app'
21
18
  import {
@@ -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,
@@ -1772,6 +1773,30 @@ const inlineInterface = {
1772
1773
  },
1773
1774
  },
1774
1775
  },
1776
+ minimizedInlineCharacterLimit: {
1777
+ category: categoryKeys.minimizedInline,
1778
+ headingText: 'Inline minimized with limited characters',
1779
+ description: '',
1780
+ inline: {
1781
+ ...baseState,
1782
+ config: {
1783
+ ...baseState.config,
1784
+ layoutMode: 'inline',
1785
+ },
1786
+ visibility: {
1787
+ ...baseState.visibility,
1788
+ visibility: visibilityStates.minimized,
1789
+ },
1790
+ entryMeta: {
1791
+ ...baseState.entryMeta,
1792
+ options: {
1793
+ text: {
1794
+ limit: 120,
1795
+ },
1796
+ },
1797
+ },
1798
+ },
1799
+ },
1775
1800
  minimizedInlinePrechat: {
1776
1801
  category: categoryKeys.minimizedInline,
1777
1802
  headingText: 'Inline minimized with pre-chat messages',
@@ -6,30 +6,29 @@ 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
+ import PrivacyDisclaimer from 'ui/components/layout/privacy-disclaimer'
11
+ import { useVisibility } from 'domains/visibility'
10
12
  import Event from './event/event'
11
13
  import Loader from './loader'
12
14
  import ComponentFilter from './component-filter'
13
- import PrivacyDisclaimer from 'ui/components/layout/privacy-disclaimer'
14
- import { useVisibility } from 'domains/visibility'
15
15
 
16
16
  const Conversation = () => {
17
17
  const { t } = useI18n()
18
- const appBodyContainer = useRef(null)
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
- window.requestAnimationFrame(() => {
27
- if (appBodyContainer.current) {
28
- appBodyContainer.current.scrollTop =
29
- appBodyContainer.current.scrollHeight
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={appBodyContainer}>
74
+ <div className={className('chat__body')} ref={chatBodyContainer}>
76
75
  <div className={className('conversation__container')}>
77
76
  <PrivacyDisclaimer />
78
77
  <ol className={className('conversation')}>
@@ -38,7 +38,10 @@ const CardComponent = ({
38
38
  sendMessage({ body: action.ask })
39
39
  } else if (action.type === cardTypes.topic) {
40
40
  const { topic: name, fallbackMessage } = action
41
- sendAction({ type: 'set_topic', body: { name, fallbackMessage } })
41
+ sendAction({
42
+ type: actionTypes.setTopic,
43
+ body: { name, fallbackMessage },
44
+ })
42
45
  }
43
46
  }, [sendMessage, action, sendAction, emitCardEvent])
44
47
 
@@ -2,9 +2,9 @@ import { createRef } from 'preact'
2
2
  import { useState, useEffect, useRef, useMemo } from 'preact/hooks'
3
3
  import { className } from 'lib/css'
4
4
  import { useGeneratedId } from 'ui/hooks/utility-hooks'
5
+ import CarouselMessageSlide from 'ui/components/conversation/event/carousel-message/components/slide'
5
6
  import CarouselPagination from './components/pagination'
6
7
  import CarouselControls from './components/controls'
7
- import CarouselMessageSlide from 'ui/components/conversation/event/carousel-message/components/slide'
8
8
 
9
9
  const defaultGetItemKey = (item, idx, prefix) => `${prefix}${idx}`
10
10
  const defaultGetItemLabel = (item) => item.label
@@ -1,12 +1,16 @@
1
1
  import { useCallback, useState } from 'preact/hooks'
2
- import { className } from '../../../../lib/css'
2
+ import { className } from 'lib/css'
3
+ import { useI18n } from 'domains/i18n'
4
+ import { useTranslatedEventData } from 'domains/translations'
5
+ import { useUserHasResponded } from 'domains/app'
6
+ import { setHasResponded } from 'domains/app/actions'
3
7
  import { actionTypes } from '../../../utils/seamly-utils'
4
- import { useI18n } from '../../../../domains/i18n'
5
- import { useTranslatedEventData } from '../../../../domains/translations'
6
8
  import MessageContainer from '../message-container'
7
- import { useSeamlyCommands } from '../../../hooks/seamly-hooks'
9
+ import {
10
+ useSeamlyCommands,
11
+ useSeamlyDispatchContext,
12
+ } from '../../../hooks/seamly-hooks'
8
13
  import SuggestionsList from '../../suggestions/suggestions-list'
9
- import { useConfig } from '../../../../domains/config'
10
14
 
11
15
  export const useSuggestions = (event) => {
12
16
  const { payload } = event
@@ -19,18 +23,22 @@ export const useSuggestions = (event) => {
19
23
  }
20
24
 
21
25
  const ConversationSuggestions = ({ event, ...props }) => {
22
- const { userResponded } = useConfig()
23
26
  const [isExpanded, setIsExpanded] = useState(true)
27
+ const dispatch = useSeamlyDispatchContext()
28
+ const userResponded = useUserHasResponded()
29
+ const { sendAction, addMessageBubble } = useSeamlyCommands()
30
+ const { suggestions, payload } = useSuggestions(event)
31
+
24
32
  const { t } = useI18n()
25
33
  const headingText = t('suggestions.headingText')
26
34
  const footerText = t('suggestions.footerText')
27
- const { sendAction, addMessageBubble } = useSeamlyCommands()
28
- const { suggestions, payload } = useSuggestions(event)
35
+
29
36
  const handleClick = useCallback(
30
37
  ({ id, question }) => {
31
- // @todo Refactor to 'suggestionclick'
32
38
  setIsExpanded(false)
39
+ dispatch(setHasResponded(true))
33
40
 
41
+ // @todo Refactor to 'suggestionclick'
34
42
  sendAction({
35
43
  type: actionTypes.custom,
36
44
  originMessage: payload.id,
@@ -45,7 +53,7 @@ const ConversationSuggestions = ({ event, ...props }) => {
45
53
 
46
54
  addMessageBubble(question)
47
55
  },
48
- [payload, sendAction, addMessageBubble, setIsExpanded],
56
+ [dispatch, sendAction, payload.id, addMessageBubble],
49
57
  )
50
58
 
51
59
  if (!isExpanded || userResponded) {
@@ -2,10 +2,10 @@ import { useCallback } from 'preact/hooks'
2
2
  import { className } from 'lib/css'
3
3
  import parseBody from 'lib/parse-body'
4
4
  import { useGeneratedId, useSeamlyCommands } from 'ui/hooks/seamly-hooks'
5
- import useEventLinkClickHandler from './hooks/use-event-link-click-handler'
6
5
  import { actionTypes } from 'ui/utils/seamly-utils'
7
6
  import MessageContainer from 'ui/components/conversation/message-container'
8
7
  import { useTranslatedEventData } from 'domains/translations'
8
+ import useEventLinkClickHandler from './hooks/use-event-link-click-handler'
9
9
 
10
10
  const Cta = ({ event }) => {
11
11
  const [body] = useTranslatedEventData(event)
@@ -1,18 +1,13 @@
1
- import { useMemo } from 'preact/hooks'
2
1
  import { useI18n } from 'domains/i18n'
3
2
  import EventDivider from 'ui/components/conversation/event-divider'
4
3
  import { className } from 'lib/css'
5
- import { useTranslations } from 'domains/translations'
4
+ import { useTranslations, useLocaleNativeName } from 'domains/translations'
6
5
 
7
6
  const NewTranslationDivider = ({ event }) => {
8
7
  const { t } = useI18n()
9
8
  const { translationEnabled, translationLocale, text } = event.payload.body
10
- const { enableTranslations, languages } = useTranslations()
11
-
12
- const languageName = useMemo(() => {
13
- return languages?.find((lang) => lang.locale === translationLocale)
14
- ?.nativeName
15
- }, [languages, translationLocale])
9
+ const { enableTranslations } = useTranslations()
10
+ const localeNativeName = useLocaleNativeName(translationLocale)
16
11
 
17
12
  const handleRestartButtonclick = () => {
18
13
  enableTranslations(translationLocale)
@@ -25,7 +20,7 @@ const NewTranslationDivider = ({ event }) => {
25
20
  translationEnabled
26
21
  ? 'translations.divider.startText'
27
22
  : 'translations.divider.stopText',
28
- { language: languageName },
23
+ { language: localeNativeName },
29
24
  )}
30
25
  </p>
31
26
  {translationEnabled ? (
@@ -1,12 +1,22 @@
1
1
  import { useState } from 'preact/hooks'
2
- import ImageLightbox from './image-lightbox'
3
2
  import MessageContainer from 'ui/components/conversation/message-container'
4
3
  import { useTranslatedEventData } from 'domains/translations'
4
+ import { useStoreDispatch } from 'domains/redux'
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,9 +1,9 @@
1
1
  import { useMemo } from 'preact/hooks'
2
2
  import parseBody from 'lib/parse-body'
3
- import useEventLinkClickHandler from './hooks/use-event-link-click-handler'
4
- import { parseRichText } from './hooks/use-text-rendering'
5
3
  import MessageContainer from 'ui/components/conversation/message-container'
6
4
  import { useTranslatedEventData } from 'domains/translations'
5
+ import useEventLinkClickHandler from './hooks/use-event-link-click-handler'
6
+ import { parseRichText } from './hooks/use-text-rendering'
7
7
 
8
8
  const Text = ({ event, ...props }) => {
9
9
  const [body] = useTranslatedEventData(event)
@@ -1,6 +1,6 @@
1
- import EventParticipant from './event/event-participant'
2
1
  import { useSeamlyCurrentAgent } from 'ui/hooks/seamly-hooks'
3
2
  import { className } from 'lib/css'
3
+ import EventParticipant from './event/event-participant'
4
4
 
5
5
  const Loader = () => {
6
6
  const { id } = useSeamlyCurrentAgent() || {}
@@ -1,8 +1,8 @@
1
1
  import { className } from 'lib/css'
2
- import EventParticipant from './event/event-participant'
3
2
  import { useSeamlyMessageContainerClassNames } from 'ui/hooks/component-helper-hooks'
4
3
  import { useTranslatedEventData } from 'domains/translations'
5
4
  import { useI18n } from 'domains/i18n'
5
+ import EventParticipant from './event/event-participant'
6
6
 
7
7
  function MessageContainer({
8
8
  showParticipant = true,
@@ -1,11 +1,11 @@
1
1
  import { useRef, useCallback } from 'preact/hooks'
2
- import SeamlyActivityEventContext from './seamly-activity-event-context'
3
2
  import {
4
3
  useSeamlyCommands,
5
4
  useSeamlyIdleDetachCountdown,
6
5
  } from 'ui/hooks/seamly-hooks'
7
6
  import { activitySendDelay } from 'config'
8
7
  import { className } from 'lib/css'
8
+ import SeamlyActivityEventContext from './seamly-activity-event-context'
9
9
 
10
10
  const SeamlyActivityMonitor = ({ children }) => {
11
11
  const prevSendTimestamp = useRef(0)
@@ -1,4 +1,6 @@
1
1
  import { useErrorBoundary } from 'preact/hooks'
2
+ import { StoreProvider } from 'domains/redux'
3
+ import { catchError } from 'domains/errors'
2
4
  import SeamlyInstanceFunctionsLoader from './seamly-instance-functions-loader'
3
5
  import SeamlyReadState from './seamly-read-state'
4
6
  import SeamlyNewNotifications from './seamly-new-notifications'
@@ -9,8 +11,6 @@ import SeamlyInitializer from './seamly-initializer'
9
11
  import SeamlyEventSubscriber from './seamly-event-subscriber'
10
12
  import SeamlyFileUpload from './seamly-file-upload'
11
13
  import { SeamlyEventBusContext, SeamlyApiContext } from './seamly-api-context'
12
- import { StoreProvider } from 'domains/redux'
13
- import { catchError } from 'domains/errors'
14
14
  import ComponentFilter from '../conversation/component-filter'
15
15
 
16
16
  const SeamlyCore = ({ store, children, config, eventBus, api }) => {
@@ -1,4 +1,3 @@
1
- import SeamlyFileUploadContext from './seamly-file-upload-context'
2
1
  import { useI18n } from 'domains/i18n'
3
2
  import { randomId } from 'lib/id'
4
3
  import { seamlyActions } from 'ui/utils/seamly-utils'
@@ -7,6 +6,7 @@ import {
7
6
  useSeamlyApiContext,
8
7
  useSeamlyDispatchContext,
9
8
  } from 'ui/hooks/seamly-hooks'
9
+ import SeamlyFileUploadContext from './seamly-file-upload-context'
10
10
 
11
11
  const {
12
12
  REGISTER_UPLOAD,
@@ -7,11 +7,12 @@ import {
7
7
  useSeamlyActivityEventHandler,
8
8
  useSeamlyApiContext,
9
9
  } from 'ui/hooks/seamly-hooks'
10
- import { SeamlyEventBusContext } from './seamly-api-context'
10
+ import { actionTypes } from 'ui/utils/seamly-utils'
11
11
  import { useTranslations } from 'domains/translations'
12
12
  import { useInterrupt } from 'domains/interrupt'
13
13
  import { useConfig } from 'domains/config'
14
14
  import { visibilityStates, useVisibility } from 'domains/visibility'
15
+ import { SeamlyEventBusContext } from './seamly-api-context'
15
16
 
16
17
  function useSeamlyInstanceFunction(functionName, fn, deps = []) {
17
18
  const eventBus = useContext(SeamlyEventBusContext)
@@ -101,7 +102,10 @@ const SeamlyInstanceFunctionsLoader = () => {
101
102
  'setTopic',
102
103
  ({ name, fallbackMessage }) => {
103
104
  if (name && fallbackMessage) {
104
- sendAction({ type: 'set_topic', body: { name, fallbackMessage } })
105
+ sendAction({
106
+ type: actionTypes.setTopic,
107
+ body: { name, fallbackMessage },
108
+ })
105
109
  } else {
106
110
  console.warn(
107
111
  'A name and a fallbackMessage are required for the setTopic action.',
@@ -5,10 +5,10 @@ import {
5
5
  useCallback,
6
6
  useContext,
7
7
  } from 'preact/hooks'
8
- import { SeamlyLiveRegionContext } from './seamly-live-region-context'
9
- import { SeamlyEventBusContext } from './seamly-api-context'
10
8
  import { ariaLiveLevels } from 'ui/utils/seamly-utils'
11
9
  import { className } from 'lib/css'
10
+ import { SeamlyLiveRegionContext } from './seamly-live-region-context'
11
+ import { SeamlyEventBusContext } from './seamly-api-context'
12
12
 
13
13
  const SeamlyLiveRegion = ({ children }) => {
14
14
  const eventBus = useContext(SeamlyEventBusContext)
@@ -11,11 +11,11 @@ import { entryTypes } from 'ui/utils/seamly-utils'
11
11
  import { runIfElementContainsOrHasFocus } from 'ui/utils/general-utils'
12
12
  import IdleDetachWarning from 'ui/components/warnings/idle-detach-warning'
13
13
  import ResumeConversationPrompt from 'ui/components/warnings/resume-conversation-prompt'
14
+ import { useConfig } from 'domains/config'
15
+ import { useVisibility } from 'domains/visibility'
14
16
  import TextEntry from './text-entry'
15
17
  import UploadToggle from './upload-toggle'
16
18
  import Upload from './upload'
17
- import { useConfig } from 'domains/config'
18
- import { useVisibility } from 'domains/visibility'
19
19
 
20
20
  const EntryContainer = () => {
21
21
  const { isOpen } = useVisibility()
@@ -3,10 +3,7 @@ import { useI18n } from 'domains/i18n'
3
3
  import { useLiveRegion } from 'ui/hooks/live-region-hooks'
4
4
  import { useEntryTextLimit } from 'ui/hooks/seamly-state-hooks'
5
5
  import { debounce } from 'ui/utils/general-utils'
6
- import {
7
- maxCharacterSrDebounceDelay,
8
- maxCharacterWarningLimit,
9
- } from 'config'
6
+ import { maxCharacterSrDebounceDelay, maxCharacterWarningLimit } from 'config'
10
7
  import { useFormControl } from 'domains/forms'
11
8
 
12
9
  export function useCharacterLimit(controlName) {
@@ -10,8 +10,8 @@ import Icon from 'ui/components/layout/icon'
10
10
  import { useFormControl } from 'domains/forms'
11
11
  import Form from 'ui/components/form-controls/form'
12
12
  import Input from 'ui/components/form-controls/input'
13
- import { useCharacterLimit } from './hooks'
14
13
  import { useI18n } from 'domains/i18n'
14
+ import { useCharacterLimit } from './hooks'
15
15
 
16
16
  export default function TextEntryForm({ controlName, skipLinkId }) {
17
17
  const { t } = useI18n()
@@ -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')} />
@@ -1,5 +1,5 @@
1
- import FormControlWrapper from './wrapper'
2
1
  import { useFormContext, useFormControl } from 'domains/forms'
2
+ import FormControlWrapper from './wrapper'
3
3
 
4
4
  const Select = ({
5
5
  id,
@@ -1,4 +1,3 @@
1
- import Icon from './icon'
2
1
  import {
3
2
  useSeamlyHeaderData,
4
3
  useSeamlyUnreadCount,
@@ -9,6 +8,7 @@ import { useI18n } from 'domains/i18n'
9
8
  import { useInterrupt } from 'domains/interrupt'
10
9
  import { useStartChatIcon } from 'domains/config'
11
10
  import { useVisibility } from 'domains/visibility'
11
+ import Icon from './icon'
12
12
 
13
13
  const AgentInfo = () => {
14
14
  const { t } = useI18n()
@@ -1,8 +1,8 @@
1
1
  import { useRef } from 'preact/hooks'
2
2
  import { className } from 'lib/css'
3
- import Icon from './icon'
4
3
  import { useI18n } from 'domains/i18n'
5
4
  import { useSeamlyStateContext } from 'ui/hooks/seamly-hooks'
5
+ import Icon from './icon'
6
6
 
7
7
  const Header = ({ children, onCloseChat }) => {
8
8
  const { headerCollapseButtonId } = useSeamlyStateContext()
@@ -4,11 +4,11 @@ import { className } from 'lib/css'
4
4
  import { useSeamlyOptions, useOptionButton } from 'ui/hooks/seamly-hooks'
5
5
  import { useI18n } from 'domains/i18n'
6
6
  import Icon from 'ui/components/layout/icon'
7
- import Options from './options'
8
7
  import InOutTransition, {
9
8
  transitionStartStates,
10
9
  } from 'ui/components/widgets/in-out-transition'
11
10
  import { getKey, keyNames, focusElement } from 'ui/utils/general-utils'
11
+ import Options from './options'
12
12
 
13
13
  const OptionsButton = () => {
14
14
  const { t } = useI18n()
@@ -1,6 +1,6 @@
1
1
  import { useRef } from 'preact/hooks'
2
- import Transcript from './transcript'
3
2
  import { useSeamlyOptions } from 'ui/hooks/seamly-hooks'
3
+ import Transcript from './transcript'
4
4
 
5
5
  const mapper = {
6
6
  sendTranscript: Transcript,
@@ -10,9 +10,9 @@ import {
10
10
  } from 'ui/hooks/seamly-hooks'
11
11
  import { actionTypes } from 'ui/utils/seamly-utils'
12
12
  import { FormProvider } from 'domains/forms'
13
- import TranscriptForm from './transcript-form'
14
13
  import { getValidator } from 'ui/utils/form-utils'
15
14
  import { isEmailString, isNotEmptyString } from 'ui/utils/validations'
15
+ import TranscriptForm from './transcript-form'
16
16
 
17
17
  const formId = 'sendTranscript'
18
18
  const controlName = 'email'
@@ -15,7 +15,7 @@ const SuggestionsItem = ({
15
15
  hasIcon,
16
16
  }) => {
17
17
  const mappedClassNames = useMemo(() => {
18
- return ['suggestions-item', ...categories.map(mapCategoryToClass)]
18
+ return ['suggestions__item', ...categories.map(mapCategoryToClass)]
19
19
  }, [categories])
20
20
 
21
21
  const handleClick = useCallback(() => {
@@ -16,7 +16,7 @@ const ViewComponentsMap = {
16
16
  window: WindowView,
17
17
  }
18
18
 
19
- const View = () => {
19
+ const View = ({ children }) => {
20
20
  const [, setSeamlyContainerElement] = useSeamlyContainerElement()
21
21
  const { namespace, layoutMode, zIndex } = useConfig()
22
22
  const { isOpen, isVisible } = useVisibility()
@@ -74,7 +74,7 @@ const View = () => {
74
74
  style={{ zIndex }}
75
75
  ref={containerElementRef}
76
76
  >
77
- <ViewComponent />
77
+ {children || <ViewComponent />}
78
78
  </div>
79
79
  )
80
80
  )
@@ -1,9 +1,9 @@
1
1
  import { className } from 'lib/css'
2
2
  import { useI18n } from 'domains/i18n'
3
- import Prompt from './prompt'
4
3
  import useSeamlyResumeConversationPrompt from 'ui/hooks/use-seamly-resume-conversation-prompt'
5
4
  import useSeamlyActivityEventHandler from 'ui/hooks/use-seamly-activity-event-handler'
6
5
  import { useSeamlyCurrentAgent } from 'ui/hooks/seamly-state-hooks'
6
+ import Prompt from './prompt'
7
7
 
8
8
  const ResumeConversationPrompt = () => {
9
9
  const { t } = useI18n()
@@ -1,5 +1,4 @@
1
1
  import { useRef } from 'preact/hooks'
2
- import Modal from './modal'
3
2
  import { useI18n } from 'domains/i18n'
4
3
  import {
5
4
  useSeamlyActivityEventHandler,
@@ -8,6 +7,7 @@ import {
8
7
  import { className } from 'lib/css'
9
8
  import Icon from 'ui/components/layout/icon'
10
9
  import { useConfig } from 'domains/config'
10
+ import Modal from './modal'
11
11
 
12
12
  const Lightbox = ({ url, description, onClose: onCloseHandler }) => {
13
13
  const { zIndex, layoutMode, namespace } = useConfig()