@seamly/web-ui 20.8.1 → 21.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 (208) hide show
  1. package/build/dist/lib/deprecated-view.js +1 -1
  2. package/build/dist/lib/index.debug.js +585 -584
  3. package/build/dist/lib/index.debug.min.js +1 -1
  4. package/build/dist/lib/index.debug.min.js.LICENSE.txt +110 -110
  5. package/build/dist/lib/index.js +20269 -26441
  6. package/build/dist/lib/index.min.js +1 -1
  7. package/build/dist/lib/index.min.js.LICENSE.txt +6 -1
  8. package/build/dist/lib/standalone.js +27728 -34583
  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/storage.js +6 -15
  12. package/build/dist/lib/style-guide.js +9660 -8970
  13. package/build/dist/lib/style-guide.min.js +1 -1
  14. package/build/dist/lib/styles-default-implementation.js +1 -1
  15. package/build/dist/lib/styles.js +1 -1
  16. package/build/dist/lib/utils.js +85 -3
  17. package/build/dist/lib/utils.min.js +1 -1
  18. package/package.json +54 -52
  19. package/src/icons/icon_check-16.svg +14 -0
  20. package/src/icons/icon_check-32.svg +14 -0
  21. package/src/javascripts/api/conversation-connector.ts +149 -0
  22. package/src/javascripts/api/errors/seamly-base-error.js +19 -0
  23. package/src/javascripts/api/errors/seamly-unavailable-error.js +5 -7
  24. package/src/javascripts/api/{index.js → index.ts} +163 -116
  25. package/src/javascripts/config.types.ts +5 -4
  26. package/src/javascripts/domains/app/actions.ts +47 -46
  27. package/src/javascripts/domains/app/hooks.js +1 -1
  28. package/src/javascripts/domains/config/actions.ts +2 -8
  29. package/src/javascripts/domains/config/hooks.ts +1 -1
  30. package/src/javascripts/domains/config/selectors.ts +6 -6
  31. package/src/javascripts/domains/config/slice.ts +3 -3
  32. package/src/javascripts/domains/errors/index.ts +66 -0
  33. package/src/javascripts/domains/forms/context.ts +1 -1
  34. package/src/javascripts/domains/forms/forms.types.ts +3 -3
  35. package/src/javascripts/domains/forms/hooks.ts +10 -10
  36. package/src/javascripts/domains/forms/provider.tsx +9 -9
  37. package/src/javascripts/domains/i18n/actions.ts +11 -5
  38. package/src/javascripts/domains/i18n/hooks.ts +11 -8
  39. package/src/javascripts/domains/i18n/selectors.ts +10 -4
  40. package/src/javascripts/domains/i18n/slice.ts +0 -1
  41. package/src/javascripts/domains/interrupt/hooks.ts +1 -1
  42. package/src/javascripts/domains/interrupt/middleware.ts +1 -1
  43. package/src/javascripts/domains/store/index.ts +1 -1
  44. package/src/javascripts/domains/store/selectors.ts +16 -0
  45. package/src/javascripts/domains/store/slice.ts +47 -41
  46. package/src/javascripts/domains/store/store.types.ts +38 -10
  47. package/src/javascripts/domains/translations/components/{options-button.js → options-button.tsx} +30 -20
  48. package/src/javascripts/domains/translations/components/options-dialog/index.tsx +33 -0
  49. package/src/javascripts/domains/translations/components/options-dialog/translation-option.tsx +37 -0
  50. package/src/javascripts/domains/translations/components/options-dialog/translation-options.tsx +85 -0
  51. package/src/javascripts/domains/translations/components/translation-status.tsx +15 -0
  52. package/src/javascripts/domains/translations/hooks.ts +77 -11
  53. package/src/javascripts/domains/translations/slice.ts +20 -9
  54. package/src/javascripts/domains/translations/translations.types.ts +4 -2
  55. package/src/javascripts/domains/visibility/actions.ts +6 -10
  56. package/src/javascripts/domains/visibility/hooks.ts +33 -14
  57. package/src/javascripts/domains/visibility/selectors.ts +3 -2
  58. package/src/javascripts/domains/visibility/slice.ts +2 -6
  59. package/src/javascripts/index.ts +19 -21
  60. package/src/javascripts/lib/engine/{index.js → index.tsx} +25 -7
  61. package/src/javascripts/lib/url-helpers.ts +112 -0
  62. package/src/javascripts/package/utils.js +5 -2
  63. package/src/javascripts/schema.ts +28 -0
  64. package/src/javascripts/style-guide/components/app.js +16 -12
  65. package/src/javascripts/style-guide/components/links.js +6 -6
  66. package/src/javascripts/style-guide/components/static-core.js +6 -3
  67. package/src/javascripts/style-guide/components/view.js +1 -1
  68. package/src/javascripts/style-guide/states.js +129 -31
  69. package/src/javascripts/style-guide/style-guide-engine.js +1 -1
  70. package/src/javascripts/ui/components/app-options/index.js +25 -6
  71. package/src/javascripts/ui/components/chat-app.js +1 -1
  72. package/src/javascripts/ui/components/chat-status/chat-status-action.tsx +30 -0
  73. package/src/javascripts/ui/components/chat-status/index.tsx +61 -0
  74. package/src/javascripts/ui/components/conversation/component-filter.js +9 -9
  75. package/src/javascripts/ui/components/conversation/{conversation.js → conversation.tsx} +32 -41
  76. package/src/javascripts/ui/components/conversation/event/card-component.js +2 -2
  77. package/src/javascripts/ui/components/conversation/event/card-message.js +1 -1
  78. package/src/javascripts/ui/components/conversation/event/carousel-component/components/controls.js +2 -2
  79. package/src/javascripts/ui/components/conversation/event/carousel-component/index.js +4 -4
  80. package/src/javascripts/ui/components/conversation/event/carousel-message/components/slide.js +2 -2
  81. package/src/javascripts/ui/components/conversation/event/carousel-message/index.js +1 -1
  82. package/src/javascripts/ui/components/conversation/event/chat-scroll/chat-scroll-context.ts +12 -0
  83. package/src/javascripts/ui/components/conversation/event/chat-scroll/chat-scroll-provider.tsx +46 -0
  84. package/src/javascripts/ui/components/conversation/event/chat-scroll/unread-messages-button.tsx +27 -0
  85. package/src/javascripts/ui/components/conversation/event/choice-prompt.js +12 -8
  86. package/src/javascripts/ui/components/conversation/event/conversation-suggestions.js +6 -6
  87. package/src/javascripts/ui/components/conversation/event/cta.js +2 -2
  88. package/src/javascripts/ui/components/conversation/event/divider/index.js +0 -1
  89. package/src/javascripts/ui/components/conversation/event/divider/variants/default.js +1 -1
  90. package/src/javascripts/ui/components/conversation/event/divider/variants/new-translation.js +17 -22
  91. package/src/javascripts/ui/components/conversation/event/divider/variants/time-indicator.js +2 -2
  92. package/src/javascripts/ui/components/conversation/event/event-participant.js +1 -1
  93. package/src/javascripts/ui/components/conversation/event/event.tsx +66 -0
  94. package/src/javascripts/ui/components/conversation/event/hooks/use-event-link-click-handler.js +1 -1
  95. package/src/javascripts/ui/components/conversation/event/hooks/use-formatted-date.js +1 -1
  96. package/src/javascripts/ui/components/conversation/event/image-lightbox.js +1 -1
  97. package/src/javascripts/ui/components/conversation/event/image.js +2 -2
  98. package/src/javascripts/ui/components/conversation/event/splash.js +1 -1
  99. package/src/javascripts/ui/components/conversation/event/translation.js +1 -1
  100. package/src/javascripts/ui/components/conversation/event/upload.js +2 -2
  101. package/src/javascripts/ui/components/conversation/event/video.js +2 -2
  102. package/src/javascripts/ui/components/conversation/event-divider.js +1 -1
  103. package/src/javascripts/ui/components/conversation/message-container.js +1 -1
  104. package/src/javascripts/ui/components/conversation/use-chat-scroll.ts +108 -0
  105. package/src/javascripts/ui/components/core/{seamly-activity-monitor.js → seamly-activity-monitor.tsx} +12 -5
  106. package/src/javascripts/ui/components/core/seamly-api-context.ts +7 -0
  107. package/src/javascripts/ui/components/core/seamly-chat.tsx +8 -0
  108. package/src/javascripts/ui/components/core/{seamly-core.js → seamly-core.tsx} +27 -14
  109. package/src/javascripts/ui/components/core/seamly-event-subscriber.ts +340 -0
  110. package/src/javascripts/ui/components/core/seamly-file-upload.js +2 -2
  111. package/src/javascripts/ui/components/core/seamly-idle-detach-counter.js +1 -1
  112. package/src/javascripts/ui/components/core/seamly-instance-functions-loader.js +24 -11
  113. package/src/javascripts/ui/components/core/seamly-live-region.js +4 -4
  114. package/src/javascripts/ui/components/core/seamly-new-notifications.js +3 -3
  115. package/src/javascripts/ui/components/core/seamly-read-state.js +2 -33
  116. package/src/javascripts/ui/components/entry/deprecated-toggle-button.js +4 -4
  117. package/src/javascripts/ui/components/entry/entry-container.js +8 -8
  118. package/src/javascripts/ui/components/entry/text-entry/hooks.js +3 -3
  119. package/src/javascripts/ui/components/entry/text-entry/index.js +3 -3
  120. package/src/javascripts/ui/components/entry/text-entry/text-entry-form.js +4 -4
  121. package/src/javascripts/ui/components/entry/upload/file-upload-form.js +3 -3
  122. package/src/javascripts/ui/components/entry/upload/index.js +5 -5
  123. package/src/javascripts/ui/components/entry/upload-toggle.js +6 -6
  124. package/src/javascripts/ui/components/faq/faq.js +14 -14
  125. package/src/javascripts/ui/components/form-controls/error.js +2 -2
  126. package/src/javascripts/ui/components/form-controls/file-input.js +3 -3
  127. package/src/javascripts/ui/components/layout/agent-info.js +3 -3
  128. package/src/javascripts/ui/components/layout/chat-frame.js +20 -12
  129. package/src/javascripts/ui/components/layout/chat.js +5 -5
  130. package/src/javascripts/ui/components/layout/deprecated-app-frame.js +6 -6
  131. package/src/javascripts/ui/components/layout/deprecated-chat-frame.js +34 -0
  132. package/src/javascripts/ui/components/layout/header.js +2 -2
  133. package/src/javascripts/ui/components/layout/icon.js +11 -9
  134. package/src/javascripts/ui/components/layout/interrupt.js +7 -5
  135. package/src/javascripts/ui/components/layout/pre-chat-messages.js +1 -1
  136. package/src/javascripts/ui/components/layout/privacy-disclaimer.js +2 -2
  137. package/src/javascripts/ui/components/options/options-button.js +5 -5
  138. package/src/javascripts/ui/components/options/{options-frame.js → options-frame.tsx} +52 -18
  139. package/src/javascripts/ui/components/options/transcript/index.js +9 -10
  140. package/src/javascripts/ui/components/options/transcript/transcript-form.js +2 -2
  141. package/src/javascripts/ui/components/suggestions/index.js +8 -8
  142. package/src/javascripts/ui/components/suggestions/suggestions-item.js +1 -1
  143. package/src/javascripts/{domains/translations/components/chat-status.js → ui/components/translation-chat-status/index.tsx} +13 -14
  144. package/src/javascripts/ui/components/translation-proposal/index.tsx +36 -0
  145. package/src/javascripts/ui/components/view/app-view.js +2 -7
  146. package/src/javascripts/ui/components/view/deprecated-view.js +8 -10
  147. package/src/javascripts/ui/components/view/index.js +6 -6
  148. package/src/javascripts/ui/components/view/inline-view.js +4 -8
  149. package/src/javascripts/ui/components/view/window-view/collapse-button.js +2 -2
  150. package/src/javascripts/ui/components/view/window-view/index.js +11 -17
  151. package/src/javascripts/ui/components/view/window-view/window-open-button.js +6 -6
  152. package/src/javascripts/ui/components/warnings/idle-detach-warning.js +3 -3
  153. package/src/javascripts/ui/components/warnings/prompt.js +1 -1
  154. package/src/javascripts/ui/components/warnings/resume-conversation-prompt.js +4 -4
  155. package/src/javascripts/ui/components/widgets/in-out-transition.js +20 -18
  156. package/src/javascripts/ui/components/widgets/lightbox.js +3 -3
  157. package/src/javascripts/ui/components/widgets/modal.js +2 -2
  158. package/src/javascripts/ui/components/widgets/upload-progress.js +2 -2
  159. package/src/javascripts/ui/hooks/file-upload-hooks.js +1 -1
  160. package/src/javascripts/ui/hooks/focus-helper-hooks.js +1 -1
  161. package/src/javascripts/ui/hooks/seamly-entry-hooks.js +6 -6
  162. package/src/javascripts/ui/hooks/seamly-hooks.js +11 -10
  163. package/src/javascripts/ui/hooks/seamly-option-hooks.js +6 -6
  164. package/src/javascripts/ui/hooks/{seamly-state-hooks.js → seamly-state-hooks.ts} +9 -6
  165. package/src/javascripts/ui/hooks/use-click-outside.ts +29 -0
  166. package/src/javascripts/ui/hooks/use-event-component-mapping.js +11 -10
  167. package/src/javascripts/ui/hooks/use-interval.js +1 -1
  168. package/src/javascripts/ui/hooks/use-seamly-actions.ts +29 -29
  169. package/src/javascripts/ui/hooks/use-seamly-chat.js +13 -23
  170. package/src/javascripts/ui/hooks/use-seamly-commands.js +20 -15
  171. package/src/javascripts/ui/hooks/use-seamly-idle-detach-countdown.js +8 -8
  172. package/src/javascripts/ui/hooks/use-seamly-resume-conversation-prompt.js +2 -2
  173. package/src/javascripts/ui/hooks/use-single-file-upload.js +1 -1
  174. package/src/javascripts/ui/hooks/utility-hooks.js +1 -1
  175. package/src/javascripts/ui/utils/general-utils.js +0 -23
  176. package/src/javascripts/ui/utils/seamly-utils.ts +10 -1
  177. package/src/javascripts/ui/utils/seamly-utils.types.ts +9 -0
  178. package/src/stylesheets/1-settings/_config.scss +1 -1
  179. package/src/stylesheets/3-chat/_chat.scss +23 -5
  180. package/src/stylesheets/5-components/_chat-status.scss +72 -16
  181. package/src/stylesheets/5-components/_conversation.scss +35 -1
  182. package/src/stylesheets/5-components/_disclaimer.scss +0 -5
  183. package/src/stylesheets/5-components/_options.scss +16 -2
  184. package/src/stylesheets/5-components/_translation-options.scss +39 -0
  185. package/src/stylesheets/6-default-implementation/_scrollbar.scss +1 -1
  186. package/src/stylesheets/7-deprecated/3-app/_app.scss +19 -4
  187. package/src/stylesheets/7-deprecated/5-components/_chat-status.scss +5 -0
  188. package/src/stylesheets/7-deprecated/5-components/_options.scss +1 -0
  189. package/src/stylesheets/7-deprecated/5-components/_translation-options.scss +39 -0
  190. package/src/stylesheets/deprecated-view.scss +1 -0
  191. package/src/stylesheets/styles.scss +1 -0
  192. package/webpack/config.common.js +4 -4
  193. package/webpack/config.package.js +10 -16
  194. package/webpack/config.site.js +4 -1
  195. package/webpack/config.test.js +2 -1
  196. package/build/dist/lib/deprecated-view.css +0 -1
  197. package/build/dist/lib/styles-default-implementation.css +0 -1
  198. package/build/dist/lib/styles.css +0 -1
  199. package/src/.DS_Store +0 -0
  200. package/src/javascripts/api/event-producer.js +0 -20
  201. package/src/javascripts/api/producer.js +0 -136
  202. package/src/javascripts/domains/errors/index.js +0 -37
  203. package/src/javascripts/domains/translations/components/options-dialog/form.js +0 -70
  204. package/src/javascripts/domains/translations/components/options-dialog/index.js +0 -87
  205. package/src/javascripts/ui/components/chat-status/index.js +0 -38
  206. package/src/javascripts/ui/components/conversation/event/event.js +0 -36
  207. package/src/javascripts/ui/components/core/seamly-api-context.js +0 -5
  208. package/src/javascripts/ui/components/core/seamly-event-subscriber.js +0 -279
@@ -1,10 +1,10 @@
1
- import { setSeamlyContainerElement } from 'domains/store/slice'
2
1
  import { useCallback, useRef } from 'preact/hooks'
3
2
  import { useDispatch } from 'react-redux'
4
3
  import {
5
4
  focusElement,
6
5
  runIfElementContainsOrHasFocus,
7
6
  } from 'ui/utils/general-utils'
7
+ import { setSeamlyContainerElement } from 'domains/store/slice'
8
8
  import { useSeamlyStateContext, useSkiplink } from './seamly-state-hooks'
9
9
 
10
10
  export const timeout = (ms = 0) =>
@@ -1,14 +1,14 @@
1
+ import { useCallback, useEffect, useRef } from 'preact/hooks'
2
+ import { useDispatch } from 'react-redux'
1
3
  import { typingTimeout } from 'config'
4
+ import { useSeamlyCommands, useSeamlyOptions } from 'ui/hooks/seamly-hooks'
5
+ import { useSeamlyStateContext } from 'ui/hooks/seamly-state-hooks'
6
+ import { actionTypes } from 'ui/utils/seamly-utils'
2
7
  import {
3
- setBlockAutoEntrySwitch,
4
8
  setActiveEntryType,
9
+ setBlockAutoEntrySwitch,
5
10
  setUserEntryType,
6
11
  } from 'domains/store/slice'
7
- import { useSeamlyCommands, useSeamlyOptions } from 'ui/hooks/seamly-hooks'
8
- import { useRef, useEffect, useCallback } from 'preact/hooks'
9
- import { useSeamlyStateContext } from 'ui/hooks/seamly-state-hooks'
10
- import { useDispatch } from 'react-redux'
11
- import { actionTypes } from 'ui/utils/seamly-utils'
12
12
 
13
13
  export const useSeamlyTyping = () => {
14
14
  const { sendAction } = useSeamlyCommands()
@@ -1,6 +1,7 @@
1
1
  import { useEffect } from 'preact/hooks'
2
2
  // Import extracted hooks here for use inside this file
3
3
  import { useSeamlyApiContext } from './seamly-api-hooks'
4
+
4
5
  // Export extracted hooks here,
5
6
  // although this is a redundant, it prevents a bazillion code changes for now.
6
7
  // TODO: Remove exports and import them from the correct files
@@ -56,16 +57,16 @@ export const useSeamlyEventStream = (nextFn, filterFn) => {
56
57
  const api = useSeamlyApiContext()
57
58
 
58
59
  useEffect(() => {
59
- if (api.stream) {
60
- if (filterFn) {
61
- api.stream().filter(filterFn).subscribe({
62
- next: nextFn,
63
- })
64
- } else {
65
- api.stream().subscribe({
66
- next: nextFn,
67
- })
60
+ if (api.connectionInfo && api.conversation?.channel) {
61
+ const { channel } = api.conversation
62
+
63
+ channel.onMessage = (type, payload) => {
64
+ if (!filterFn || filterFn({ type, payload })) {
65
+ nextFn({ type, payload })
66
+ }
67
+
68
+ return payload
68
69
  }
69
70
  }
70
- }, [api, nextFn, filterFn])
71
+ }, [nextFn, filterFn, api.connectionInfo, api.conversation])
71
72
  }
@@ -1,15 +1,15 @@
1
+ import { useCallback } from 'preact/hooks'
2
+ import { useDispatch } from 'react-redux'
3
+ import { useElementFocusingById } from 'ui/hooks/focus-helper-hooks'
4
+ import { useSeamlyObjectStore } from 'ui/hooks/seamly-api-hooks'
5
+ import { useSeamlyStateContext } from 'ui/hooks/seamly-state-hooks'
1
6
  import { useI18n } from 'domains/i18n/hooks'
2
7
  import {
3
8
  hideOption as dispatchHideOption,
9
+ showOption as dispatchShowOption,
4
10
  setUserSelectedOption as dispatchUserSelectedOption,
5
11
  setUserSelectedOptions as dispatchUserSelectedOptions,
6
- showOption as dispatchShowOption,
7
12
  } from 'domains/store/slice'
8
- import { useCallback } from 'preact/hooks'
9
- import { useDispatch } from 'react-redux'
10
- import { useElementFocusingById } from 'ui/hooks/focus-helper-hooks'
11
- import { useSeamlyObjectStore } from 'ui/hooks/seamly-api-hooks'
12
- import { useSeamlyStateContext } from 'ui/hooks/seamly-state-hooks'
13
13
 
14
14
  export const useSeamlyOptions = () => {
15
15
  const { t } = useI18n()
@@ -1,11 +1,13 @@
1
- import * as ConfigSelectors from 'domains/config/selectors'
2
- import { microsecondsToMilliseconds } from 'ui/utils/general-utils'
3
1
  import { createSelector } from '@reduxjs/toolkit'
2
+ import { useSelector } from 'react-redux'
3
+ import { microsecondsToMilliseconds } from 'ui/utils/general-utils'
4
4
  import { selectUserHasResponded } from 'domains/app/selectors'
5
5
  import { useConfig } from 'domains/config/hooks'
6
- import { useSelector } from 'react-redux'
6
+ import * as ConfigSelectors from 'domains/config/selectors'
7
+ import type { RootState } from 'domains/store'
8
+ import type { ChannelEvent, ServiceDataEvent } from 'domains/store/store.types'
7
9
 
8
- export const selectState = ({ state }) => state
10
+ export const selectState = ({ state }: RootState) => state
9
11
  export const useSeamlyStateContext = () => useSelector(selectState)
10
12
 
11
13
  export const selectEventsWithSuggestion = createSelector(
@@ -22,7 +24,7 @@ export const selectEventsWithSuggestion = createSelector(
22
24
  return events
23
25
  }
24
26
 
25
- const suggestionsEvent = {
27
+ const suggestionsEvent: ServiceDataEvent = {
26
28
  type: 'service_data',
27
29
  payload: serviceData.suggestion,
28
30
  }
@@ -34,7 +36,7 @@ export const selectEventsWithSuggestion = createSelector(
34
36
  export const selectEvents = createSelector(
35
37
  selectEventsWithSuggestion,
36
38
  ConfigSelectors.selectConfig,
37
- (events, config) => {
39
+ (events, config): ChannelEvent[] => {
38
40
  const { enabled, threshold } = config?.messages?.timeIndicator ?? {}
39
41
  if (!enabled) {
40
42
  return events
@@ -128,6 +130,7 @@ export const useSeamlyLayoutMode = () => {
128
130
  return {
129
131
  isInline: layoutMode === 'inline',
130
132
  isWindow: layoutMode === 'window',
133
+ isApp: layoutMode === 'app',
131
134
  isResolving: !layoutMode,
132
135
  }
133
136
  }
@@ -0,0 +1,29 @@
1
+ import { useEffect, useRef } from 'preact/hooks'
2
+
3
+ const useClickOutside = (callback: Function) => {
4
+ const ref = useRef<HTMLElement>(null)
5
+
6
+ useEffect(() => {
7
+ const handler = ({ target }: TouchEvent | MouseEvent): void => {
8
+ if (
9
+ ref.current &&
10
+ target instanceof HTMLElement &&
11
+ !ref.current.contains(target)
12
+ ) {
13
+ callback()
14
+ }
15
+ }
16
+
17
+ document.addEventListener('click', handler, true)
18
+ document.addEventListener('touchstart', handler, true)
19
+
20
+ return () => {
21
+ document.removeEventListener('click', handler, true)
22
+ document.removeEventListener('touchstart', handler, true)
23
+ }
24
+ }, [callback, ref])
25
+
26
+ return ref
27
+ }
28
+
29
+ export default useClickOutside
@@ -4,22 +4,23 @@ import { payloadTypes } from 'ui/utils/seamly-utils'
4
4
 
5
5
  const useEventComponentMapping = (event) => {
6
6
  const components = useContext(ComponentContext)
7
-
7
+ const componentType = components[event.type]
8
+ let Component = componentType[event.payload.type]
8
9
  let SubComponent = null
9
10
 
10
11
  if (event.payload.type === payloadTypes.choicePrompt) {
11
- SubComponent = components[event.type][event.payload.body.prompt.type]
12
+ SubComponent = componentType[event.payload.body.prompt.type]
13
+ }
14
+
15
+ if (event.type === 'participant') {
16
+ Component = componentType[event.type]
12
17
  }
13
18
 
14
- const Component =
15
- components[event.type][event.payload.type] || components.fallback
19
+ if (Component && typeof Component !== 'function') {
20
+ Component = Component[event.payload.body.type]
21
+ }
16
22
 
17
- return [
18
- Component && typeof Component !== 'function'
19
- ? Component[event.payload.body.type] || components.fallback
20
- : Component,
21
- SubComponent,
22
- ]
23
+ return [Component || components.fallback, SubComponent]
23
24
  }
24
25
 
25
26
  export default useEventComponentMapping
@@ -1,4 +1,4 @@
1
- import { useRef, useEffect } from 'preact/hooks'
1
+ import { useEffect, useRef } from 'preact/hooks'
2
2
 
3
3
  const useInterval = (callback, delay) => {
4
4
  const savedCallback = useRef(callback)
@@ -1,48 +1,48 @@
1
+ import { useDispatch } from 'react-redux'
1
2
  import {
3
+ ackEvent,
2
4
  addEvent,
5
+ clearAllUploads,
3
6
  clearEvents,
4
- setHistory,
5
- setEventsRead,
6
- setLoadedImageEventIds,
7
- ackEvent,
8
- setIsLoading,
9
- setParticipant,
10
- setHeaderTitle,
11
- setHeaderSubTitle,
12
- resetHistoryLoadedFlag,
13
- setActiveService,
14
- initIdleDetachCountdown,
15
- decrementIdleDetachCountdownCounter,
16
- stopIdleDetachCountdownCounter,
7
+ clearFeatures,
17
8
  clearIdleDetachCountdown,
18
- initResumeConversationPrompt,
19
9
  clearResumeConversationPrompt,
20
- setServiceDataItem,
21
- setFeatures,
10
+ decrementIdleDetachCountdownCounter,
11
+ hideOption,
12
+ initIdleDetachCountdown,
13
+ initResumeConversationPrompt,
14
+ registerUpload,
15
+ resetHistoryLoadedFlag,
16
+ setActiveEntryType,
17
+ setActiveService,
18
+ setBlockAutoEntrySwitch,
19
+ setEventsRead,
22
20
  setFeatureEnabledState,
23
- clearFeatures,
21
+ setFeatures,
22
+ setHeaderSubTitle,
23
+ setHeaderTitle,
24
+ setHistory,
24
25
  setInitialState,
25
- setUserSelectedOptions,
26
- setUserSelectedOption,
27
- showOption,
28
- hideOption,
26
+ setIsLoading,
27
+ setLoadedImageEventIds,
28
+ setParticipant,
29
+ setSeamlyContainerElement,
30
+ setServiceDataItem,
29
31
  setServiceEntryMetadata,
30
- setBlockAutoEntrySwitch,
31
- setActiveEntryType,
32
- setUserEntryType,
33
- registerUpload,
34
- setUploadProgress,
35
32
  setUploadComplete,
36
33
  setUploadError,
37
- clearAllUploads,
38
- setSeamlyContainerElement,
34
+ setUploadProgress,
35
+ setUserEntryType,
36
+ setUserSelectedOption,
37
+ setUserSelectedOptions,
38
+ showOption,
39
+ stopIdleDetachCountdownCounter,
39
40
  } from 'domains/store/slice'
40
41
  import {
41
42
  ChannelEvent,
42
43
  HistoryResponse,
43
44
  ParticipantEvent,
44
45
  } from 'domains/store/store.types'
45
- import { useDispatch } from 'react-redux'
46
46
 
47
47
  export const useSeamlyActions = () => {
48
48
  const dispatch = useDispatch()
@@ -1,9 +1,8 @@
1
+ import { useCallback, useEffect, useRef } from 'preact/hooks'
2
+ import { useDispatch, useSelector } from 'react-redux'
1
3
  import { useI18n } from 'domains/i18n/hooks'
2
4
  import { setIsLoading } from 'domains/store/slice'
3
- import { visibilityStates } from 'domains/visibility/constants'
4
5
  import { useVisibility } from 'domains/visibility/hooks'
5
- import { useCallback, useEffect, useRef } from 'preact/hooks'
6
- import { useDispatch, useSelector } from 'react-redux'
7
6
  import { selectShowInlineView } from '../../domains/visibility/selectors'
8
7
  import { useLiveRegion } from './live-region-hooks'
9
8
  import { useSeamlyHasConversation } from './seamly-api-hooks'
@@ -13,8 +12,8 @@ import useSeamlyCommands from './use-seamly-commands'
13
12
  const useSeamlyChat = () => {
14
13
  const events = useEvents()
15
14
  const { t } = useI18n()
16
- const { isInline, isWindow } = useSeamlyLayoutMode()
17
- const { isOpen, isVisible, setVisibility } = useVisibility()
15
+ const { isInline, isWindow, isApp } = useSeamlyLayoutMode()
16
+ const { isOpen, isVisible } = useVisibility()
18
17
  const showInlineView = useSelector(selectShowInlineView)
19
18
  const dispatch = useDispatch()
20
19
  const spinnerTimeout = useRef(null)
@@ -22,7 +21,7 @@ const useSeamlyChat = () => {
22
21
  const hasConversation = useSeamlyHasConversation()
23
22
  const prevIsOpen = useRef(null)
24
23
  const prevIsVisible = useRef(null)
25
- const startCalled = useRef(false)
24
+ const connectCalled = useRef(false)
26
25
  const { sendAssertive } = useLiveRegion()
27
26
 
28
27
  useEffect(() => {
@@ -80,22 +79,22 @@ const useSeamlyChat = () => {
80
79
  // This is needed to reset the ref to allow connect and start to happen again.
81
80
  // Mostly due to Interrupt situations and a reset being called.
82
81
  if (!apiConfigReady || !apiConnected) {
83
- startCalled.current = false
82
+ connectCalled.current = false
84
83
  }
85
84
  }, [apiConfigReady, apiConnected])
86
85
 
87
86
  const connectAndStart = useCallback(async () => {
88
87
  // We don't connect if we are already connected to the api to avoid multiple in-flight connection processes.
89
88
  if (!apiConnected) {
89
+ connectCalled.current = true
90
90
  await connect()
91
91
  }
92
92
 
93
- // We only start a conversation when the chat interface is either 'open' or if using the inline view if it's 'open' or 'minimized'.
94
- if (isOpen || (isVisible && isInline)) {
93
+ // We only start a conversation when the chat interface is either app, 'open' or if using the inline view if it's 'open' or 'minimized'.
94
+ if (isApp || isOpen || (isVisible && isInline)) {
95
95
  start()
96
- startCalled.current = true
97
96
  }
98
- }, [apiConnected, connect, isInline, isOpen, isVisible, start])
97
+ }, [apiConnected, connect, isApp, isInline, isOpen, isVisible, start])
99
98
 
100
99
  useEffect(() => {
101
100
  // We dont't connect or start when the apiConfig is not ready yet.
@@ -104,9 +103,9 @@ const useSeamlyChat = () => {
104
103
  // Lastly we check if the inline view is not scrolled in to view.
105
104
  if (
106
105
  !apiConfigReady ||
107
- startCalled.current ||
106
+ connectCalled.current ||
108
107
  (isWindow && !isOpen && !hasConversation()) ||
109
- (isInline && !showInlineView)
108
+ (isInline && (!isVisible || !showInlineView))
110
109
  ) {
111
110
  return
112
111
  }
@@ -127,18 +126,9 @@ const useSeamlyChat = () => {
127
126
  isInline,
128
127
  isOpen,
129
128
  isWindow,
129
+ isVisible,
130
130
  showInlineView,
131
131
  ])
132
-
133
- const openChat = () => {
134
- setVisibility(visibilityStates.open)
135
- }
136
-
137
- const closeChat = () => {
138
- setVisibility(visibilityStates.minimized)
139
- }
140
-
141
- return { openChat, closeChat }
142
132
  }
143
133
 
144
134
  export default useSeamlyChat
@@ -1,18 +1,17 @@
1
+ import { useCallback, useContext } from 'preact/hooks'
2
+ import { useDispatch } from 'react-redux'
1
3
  import { userParticipantId } from 'config'
2
- import { useUserHasResponded } from 'domains/app/hooks'
4
+ import { SeamlyEventBusContext } from 'ui/components/core/seamly-api-context'
5
+ import { actionTypes } from 'ui/utils/seamly-utils'
3
6
  import * as AppActions from 'domains/app/actions'
7
+ import { useUserHasResponded } from 'domains/app/hooks'
4
8
  import { setHasResponded } from 'domains/app/slice'
5
9
  import { useConfig } from 'domains/config/hooks'
6
10
  import { setInterrupt } from 'domains/interrupt/slice'
7
11
  import { addEvent, setInitialState } from 'domains/store/slice'
8
12
  import { visibilityStates } from 'domains/visibility/constants'
9
13
  import { useVisibility } from 'domains/visibility/hooks'
10
-
11
14
  import { randomId } from 'lib/id'
12
- import { useCallback, useContext } from 'preact/hooks'
13
- import { useDispatch } from 'react-redux'
14
- import { SeamlyEventBusContext } from 'ui/components/core/seamly-api-context'
15
- import { actionTypes } from 'ui/utils/seamly-utils'
16
15
  import {
17
16
  useSeamlyApiContext,
18
17
  useSeamlyHasConversation,
@@ -120,8 +119,13 @@ const useSeamlyCommands = () => {
120
119
  )
121
120
 
122
121
  const addMessageBubble = useCallback(
123
- (text) => {
124
- dispatch(addEvent({ type: 'message', payload: getTextMessageBase(text) }))
122
+ (text, transactionId = randomId()) => {
123
+ dispatch(
124
+ addEvent({
125
+ type: 'message',
126
+ payload: { ...getTextMessageBase(text), transactionId },
127
+ }),
128
+ )
125
129
  },
126
130
  [dispatch, getTextMessageBase],
127
131
  )
@@ -148,13 +152,13 @@ const useSeamlyCommands = () => {
148
152
  )
149
153
 
150
154
  const addDivider = useCallback(
151
- (subtype) => {
155
+ (subtype, transactionId = randomId()) => {
152
156
  const payload = {
153
157
  body: { subtype, type: 'divider' },
154
158
  fromClient: false,
155
159
  fromHistory: true,
156
160
  id: randomId(),
157
- transactionId: randomId(),
161
+ transactionId,
158
162
  type: 'divider',
159
163
  }
160
164
 
@@ -205,11 +209,12 @@ const useSeamlyCommands = () => {
205
209
  .catch((error) => {
206
210
  dispatch(
207
211
  setInterrupt({
208
- name: error.name,
209
- message: error.message,
210
- langKey: error.langKey,
211
- originalEvent: error.originalEvent,
212
- originalError: error.originalError,
212
+ name: error?.name,
213
+ message: error?.message,
214
+ langKey: error?.langKey,
215
+ action: error?.action,
216
+ originalEvent: error?.originalEvent,
217
+ originalError: error?.originalError,
213
218
  }),
214
219
  )
215
220
  })
@@ -1,19 +1,19 @@
1
+ import { useCallback, useRef } from 'preact/hooks'
2
+ import { useDispatch } from 'react-redux'
1
3
  import { screenReaderDebounceDelaySeconds } from 'config'
4
+ import {
5
+ getTimeFromSeconds,
6
+ millisecondsToSeconds,
7
+ } from 'ui/utils/general-utils'
8
+ import { actionTypes } from 'ui/utils/seamly-utils'
2
9
  import { useI18n } from 'domains/i18n/hooks'
3
- import { useVisibility } from 'domains/visibility/hooks'
4
10
  import {
5
11
  clearIdleDetachCountdown,
6
12
  decrementIdleDetachCountdownCounter,
7
13
  initIdleDetachCountdown,
8
14
  stopIdleDetachCountdownCounter,
9
15
  } from 'domains/store/slice'
10
- import { useCallback, useRef } from 'preact/hooks'
11
- import { useDispatch } from 'react-redux'
12
- import {
13
- getTimeFromSeconds,
14
- millisecondsToSeconds,
15
- } from 'ui/utils/general-utils'
16
- import { actionTypes } from 'ui/utils/seamly-utils'
16
+ import { useVisibility } from 'domains/visibility/hooks'
17
17
  import { useLiveRegion } from './live-region-hooks'
18
18
  import { useSeamlyStateContext } from './seamly-state-hooks'
19
19
  import useSeamlyCommands from './use-seamly-commands'
@@ -1,7 +1,7 @@
1
- import { useI18n } from 'domains/i18n/hooks'
2
- import { clearResumeConversationPrompt } from 'domains/store/slice'
3
1
  import { useDispatch } from 'react-redux'
4
2
  import { actionTypes, dismissTypes } from 'ui/utils/seamly-utils'
3
+ import { useI18n } from 'domains/i18n/hooks'
4
+ import { clearResumeConversationPrompt } from 'domains/store/slice'
5
5
  import { useLiveRegion } from './live-region-hooks'
6
6
  import { useSeamlyStateContext } from './seamly-state-hooks'
7
7
  import useSeamlyCommands from './use-seamly-commands'
@@ -1,5 +1,5 @@
1
- import { getControlValueByName } from 'domains/forms/selectors'
2
1
  import { useSelector } from 'react-redux'
2
+ import { getControlValueByName } from 'domains/forms/selectors'
3
3
  import { useFileUploads } from './seamly-hooks'
4
4
 
5
5
  const useSingleFileUpload = (formId, name) => {
@@ -1,4 +1,3 @@
1
- import { randomId } from 'lib/id'
2
1
  import {
3
2
  useCallback,
4
3
  useMemo,
@@ -6,6 +5,7 @@ import {
6
5
  useRef,
7
6
  useState,
8
7
  } from 'preact/hooks'
8
+ import { randomId } from 'lib/id'
9
9
 
10
10
  export const useForceUpdate = () => {
11
11
  // This is an escape hatch mentioned in the React docs:
@@ -105,29 +105,6 @@ export const formatBytes = (bytes, decimals = 2) => {
105
105
  return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`
106
106
  }
107
107
 
108
- export const getUrlParams = () => {
109
- const { search } = window.location
110
-
111
- if (!search) {
112
- return {}
113
- }
114
-
115
- const hashes = search.slice(search.indexOf('?') + 1).split('&')
116
-
117
- return hashes.reduce((acc, hash) => {
118
- const [key, val] = hash.split('=')
119
- return { ...acc, [key]: decodeURIComponent(val) }
120
- }, {})
121
- }
122
-
123
- export const getUrlSearchString = (params) => {
124
- return Object.keys(params).reduce(
125
- (search, key) =>
126
- `${search}${search ? '&' : ''}${key}=${encodeURIComponent(params[key])}`,
127
- '',
128
- )
129
- }
130
-
131
108
  export const keyCodes = {
132
109
  27: 'Escape',
133
110
  35: 'End',
@@ -46,9 +46,18 @@ export const actionTypes = {
46
46
  clickCard: 'click_card',
47
47
  }
48
48
 
49
+ export const TRANSLATION_PROPOSAL = 'translation_proposal'
50
+
49
51
  export const dismissTypes = {
50
52
  resumeConversationPrompt: 'resume_conversation_prompt',
51
- }
53
+ translationProposal: TRANSLATION_PROPOSAL,
54
+ } as const
55
+
56
+ export const sourceTypes = {
57
+ translationProposal: TRANSLATION_PROPOSAL,
58
+ translationChoice: 'translation_choice',
59
+ windowApi: 'window_api',
60
+ } as const
52
61
 
53
62
  export const ariaLiveLevels = {
54
63
  assertive: 'assertive',
@@ -0,0 +1,9 @@
1
+ // Generic ValueOf type to force value of objects
2
+ // example:
3
+ // export const objectType = {
4
+ // infoKey: 'info_key',
5
+ // messageKey: 'message_key',
6
+ // } as const <- important for Typescript
7
+ // const forceValueOf = ValueOf<typeof objectType> = objectType.infoKey GOOD
8
+ // const forceValueOf = ValueOf<typeof objectType> = "anything else" ERR
9
+ export type ValueOf<T> = T[keyof T]
@@ -81,7 +81,7 @@ $chat-window-width: $spacer * 18 !default;
81
81
  $chat-window-height: $spacer * 36 !default;
82
82
 
83
83
  // CHAT INLINE
84
- $chat-inline-height: $spacer * 32 !default;
84
+ $chat-inline-height: $spacer * 36 !default;
85
85
  $chat-inline-maxheight: 90vh !default;
86
86
 
87
87
  // CHAT SUGGESTION
@@ -71,6 +71,8 @@
71
71
  width: 100%;
72
72
  height: 100%;
73
73
  overflow: hidden;
74
+ transform: translate3d(0, 0, 0);
75
+ backface-visibility: hidden;
74
76
  transition: flex $transition, width $transition, height $transition,
75
77
  border-radius $transition;
76
78
  border: $thin-border solid set-border-color($chat-bg);
@@ -106,17 +108,33 @@
106
108
  .#{$n}-chat__container {
107
109
  display: flex;
108
110
  position: relative;
109
- flex-direction: column;
110
- width: 100%;
111
- height: 100%;
111
+ flex-grow: 1;
112
+ justify-content: center;
113
+
114
+ &__scroll-area {
115
+ display: flex;
116
+ position: relative;
117
+ flex-direction: column;
118
+ justify-content: space-between;
119
+ width: 100%;
120
+ height: 100%;
121
+ overflow-y: auto;
122
+ will-change: transform;
123
+ }
124
+
125
+ &__header {
126
+ display: flex;
127
+ position: sticky;
128
+ z-index: 1;
129
+ top: 0;
130
+ }
112
131
  }
113
132
 
114
133
  .#{$n}-chat__body {
115
134
  display: flex;
116
135
  position: relative;
117
- flex-grow: 4;
136
+ flex-grow: 1;
118
137
  padding: 0;
119
- overflow-y: auto;
120
138
  }
121
139
 
122
140
  .#{$n}-chat--layout-window .#{$n}-chat__body {