@seamly/web-ui 18.1.1 → 18.3.1

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 (165) hide show
  1. package/build/dist/lib/index.debug.js +286 -99
  2. package/build/dist/lib/index.debug.min.js +1 -1
  3. package/build/dist/lib/index.debug.min.js.LICENSE.txt +84 -16
  4. package/build/dist/lib/index.js +4104 -3887
  5. package/build/dist/lib/index.min.js +1 -1
  6. package/build/dist/lib/standalone.js +4351 -4084
  7. package/build/dist/lib/standalone.min.js +1 -1
  8. package/build/dist/lib/style-guide.js +746 -641
  9. package/build/dist/lib/style-guide.min.js +1 -1
  10. package/build/dist/lib/styles.css +1 -1
  11. package/build/dist/lib/utils.js +0 -1
  12. package/build/dist/lib/utils.min.js +1 -1
  13. package/build/dist/translations/de-informal.js +0 -1
  14. package/build/dist/translations/de-informal.min.js +1 -1
  15. package/build/dist/translations/en.js +0 -1
  16. package/build/dist/translations/en.min.js +1 -1
  17. package/build/dist/translations/es-informal.js +0 -1
  18. package/build/dist/translations/es-informal.min.js +1 -1
  19. package/build/dist/translations/nl-formal.js +0 -1
  20. package/build/dist/translations/nl-formal.min.js +1 -1
  21. package/build/dist/translations/nl-informal.js +0 -1
  22. package/build/dist/translations/nl-informal.min.js +1 -1
  23. package/package.json +4 -2
  24. package/src/javascripts/api/index.js +9 -9
  25. package/src/javascripts/api/producer.js +8 -8
  26. package/src/javascripts/config.js +9 -11
  27. package/src/javascripts/domains/app/actions.js +25 -0
  28. package/src/javascripts/domains/app/index.js +3 -0
  29. package/src/javascripts/domains/config/actions.js +6 -0
  30. package/src/javascripts/domains/config/hooks.js +6 -0
  31. package/src/javascripts/domains/config/index.js +8 -0
  32. package/src/javascripts/domains/config/middleware.js +26 -0
  33. package/src/javascripts/domains/config/reducer.js +74 -0
  34. package/src/javascripts/domains/config/selectors.js +23 -0
  35. package/src/javascripts/domains/forms/actions.js +1 -1
  36. package/src/javascripts/domains/forms/hooks.js +10 -14
  37. package/src/javascripts/domains/forms/provider.js +4 -6
  38. package/src/javascripts/domains/forms/selectors.js +3 -3
  39. package/src/javascripts/domains/i18n/index.js +9 -5
  40. package/src/javascripts/domains/interrupt/actions.js +6 -0
  41. package/src/javascripts/domains/interrupt/hooks.js +29 -0
  42. package/src/javascripts/domains/interrupt/index.js +9 -0
  43. package/src/javascripts/domains/interrupt/middleware.js +30 -0
  44. package/src/javascripts/domains/interrupt/reducer.js +22 -0
  45. package/src/javascripts/domains/interrupt/selectors.js +5 -0
  46. package/src/javascripts/domains/options/index.js +1 -0
  47. package/src/javascripts/domains/options/middleware.js +35 -0
  48. package/src/javascripts/domains/redux/create-redux-store.js +14 -6
  49. package/src/javascripts/domains/redux/hooks.js +2 -2
  50. package/src/javascripts/domains/redux/index.js +2 -1
  51. package/src/javascripts/domains/redux/provider.js +5 -0
  52. package/src/javascripts/domains/store/index.js +38 -0
  53. package/src/javascripts/{ui → domains}/store/state-reducer.js +4 -7
  54. package/src/javascripts/domains/translations/actions.js +3 -3
  55. package/src/javascripts/domains/translations/components/chat-status.js +6 -12
  56. package/src/javascripts/domains/translations/components/options-button.js +3 -3
  57. package/src/javascripts/domains/translations/components/options-dialog/form.js +2 -2
  58. package/src/javascripts/domains/translations/components/options-dialog/index.js +2 -5
  59. package/src/javascripts/domains/translations/hooks.js +1 -1
  60. package/src/javascripts/domains/translations/reducer.js +2 -2
  61. package/src/javascripts/domains/translations/selectors.js +2 -2
  62. package/src/javascripts/index.js +17 -5
  63. package/src/javascripts/lib/css.js +5 -5
  64. package/src/javascripts/lib/engine/index.js +38 -11
  65. package/src/javascripts/lib/external-api/index.js +6 -6
  66. package/src/javascripts/lib/i18n.js +2 -2
  67. package/src/javascripts/lib/parse-body.js +1 -1
  68. package/src/javascripts/lib/redux-helpers/index.js +18 -4
  69. package/src/javascripts/lib/split-url-params.js +2 -2
  70. package/src/javascripts/lib/store/providers/app-storage.js +1 -1
  71. package/src/javascripts/lib/store/providers/cookie-storage.js +1 -1
  72. package/src/javascripts/package/utils.js +0 -1
  73. package/src/javascripts/style-guide/components/app.js +5 -12
  74. package/src/javascripts/style-guide/components/links.js +6 -6
  75. package/src/javascripts/style-guide/components/static-core.js +23 -7
  76. package/src/javascripts/style-guide/state-helpers/index.js +1 -1
  77. package/src/javascripts/style-guide/states.js +27 -67
  78. package/src/javascripts/style-guide/style-guide-engine.js +1 -1
  79. package/src/javascripts/ui/components/chat-app.js +2 -2
  80. package/src/javascripts/ui/components/conversation/component-filter.js +2 -2
  81. package/src/javascripts/ui/components/conversation/conversation.js +2 -2
  82. package/src/javascripts/ui/components/conversation/event/card-component.js +29 -4
  83. package/src/javascripts/ui/components/conversation/event/carousel-component/components/pagination.js +2 -2
  84. package/src/javascripts/ui/components/conversation/event/carousel-component/index.js +4 -3
  85. package/src/javascripts/ui/components/conversation/event/carousel-message/components/slide.js +2 -1
  86. package/src/javascripts/ui/components/conversation/event/carousel-message/index.js +2 -2
  87. package/src/javascripts/ui/components/conversation/event/choice-prompt.js +11 -6
  88. package/src/javascripts/ui/components/conversation/event/cta.js +1 -6
  89. package/src/javascripts/ui/components/conversation/event/divider/variants/new-translation.js +1 -1
  90. package/src/javascripts/ui/components/conversation/event/event-participant.js +3 -5
  91. package/src/javascripts/ui/components/conversation/event/hooks/use-event-link-click-handler.js +2 -2
  92. package/src/javascripts/ui/components/conversation/event/hooks/use-formatted-date.js +3 -3
  93. package/src/javascripts/ui/components/conversation/event/hooks/use-text-rendering.js +3 -3
  94. package/src/javascripts/ui/components/conversation/event/participant.js +2 -2
  95. package/src/javascripts/ui/components/conversation/event/upload.js +12 -27
  96. package/src/javascripts/ui/components/conversation/message-container.js +4 -6
  97. package/src/javascripts/ui/components/core/seamly-activity-monitor.js +4 -5
  98. package/src/javascripts/ui/components/core/seamly-core.js +6 -7
  99. package/src/javascripts/ui/components/core/seamly-event-subscriber.js +16 -17
  100. package/src/javascripts/ui/components/core/seamly-file-upload.js +5 -6
  101. package/src/javascripts/ui/components/core/seamly-idle-detach-counter.js +2 -6
  102. package/src/javascripts/ui/components/core/seamly-initializer.js +7 -60
  103. package/src/javascripts/ui/components/core/seamly-instance-functions-loader.js +10 -10
  104. package/src/javascripts/ui/components/core/seamly-live-region.js +1 -1
  105. package/src/javascripts/ui/components/core/seamly-new-notifications.js +1 -1
  106. package/src/javascripts/ui/components/core/seamly-read-state.js +2 -2
  107. package/src/javascripts/ui/components/entry/entry-container.js +7 -10
  108. package/src/javascripts/ui/components/entry/toggle-button.js +24 -10
  109. package/src/javascripts/ui/components/entry/upload/index.js +4 -11
  110. package/src/javascripts/ui/components/faq/faq.js +4 -4
  111. package/src/javascripts/ui/components/form-controls/error.js +22 -0
  112. package/src/javascripts/ui/components/form-controls/file-input.js +3 -9
  113. package/src/javascripts/ui/components/form-controls/select.js +1 -1
  114. package/src/javascripts/ui/components/form-controls/wrapper.js +2 -9
  115. package/src/javascripts/ui/components/layout/agent-info.js +4 -4
  116. package/src/javascripts/ui/components/layout/app-frame.js +5 -5
  117. package/src/javascripts/ui/components/layout/chat-frame.js +3 -5
  118. package/src/javascripts/ui/components/layout/header.js +4 -18
  119. package/src/javascripts/ui/components/layout/privacy-disclaimer.js +2 -2
  120. package/src/javascripts/ui/components/options/cobrowsing.js +3 -7
  121. package/src/javascripts/ui/components/options/options-button.js +9 -13
  122. package/src/javascripts/ui/components/options/options-frame.js +1 -1
  123. package/src/javascripts/ui/components/options/transcript/index.js +2 -2
  124. package/src/javascripts/ui/components/options/transcript/transcript-form.js +1 -1
  125. package/src/javascripts/ui/components/warnings/cobrowsing-active-frame.js +3 -6
  126. package/src/javascripts/ui/components/warnings/idle-detach-warning.js +2 -6
  127. package/src/javascripts/ui/components/widgets/in-out-transition.js +2 -2
  128. package/src/javascripts/ui/components/widgets/lightbox.js +4 -4
  129. package/src/javascripts/ui/components/widgets/modal.js +3 -3
  130. package/src/javascripts/ui/components/widgets/upload-progress.js +2 -13
  131. package/src/javascripts/ui/hooks/component-helper-hooks.js +4 -15
  132. package/src/javascripts/ui/hooks/file-upload-hooks.js +3 -3
  133. package/src/javascripts/ui/hooks/focus-helper-hooks.js +4 -4
  134. package/src/javascripts/ui/hooks/live-region-hooks.js +2 -2
  135. package/src/javascripts/ui/hooks/seamly-entry-hooks.js +7 -6
  136. package/src/javascripts/ui/hooks/seamly-hooks.js +3 -9
  137. package/src/javascripts/ui/hooks/seamly-option-hooks.js +4 -4
  138. package/src/javascripts/ui/hooks/seamly-state-hooks.js +8 -16
  139. package/src/javascripts/ui/hooks/use-event-component-mapping.js +1 -1
  140. package/src/javascripts/ui/hooks/use-seamly-chat.js +1 -0
  141. package/src/javascripts/ui/hooks/use-seamly-commands.js +27 -49
  142. package/src/javascripts/ui/hooks/use-seamly-idle-detach-countdown.js +3 -3
  143. package/src/javascripts/ui/hooks/use-seamly-stored-visibility.js +3 -3
  144. package/src/javascripts/ui/hooks/use-seamly-visibility.js +3 -3
  145. package/src/javascripts/ui/hooks/utility-hooks.js +2 -2
  146. package/src/javascripts/ui/utils/form-utils.js +3 -3
  147. package/src/javascripts/ui/utils/general-utils.js +17 -11
  148. package/src/javascripts/ui/utils/seamly-utils.js +15 -83
  149. package/src/javascripts/ui/utils/validations.js +10 -7
  150. package/src/stylesheets/5-components/_card.scss +0 -1
  151. package/src/stylesheets/5-components/_choice-prompt.scss +5 -0
  152. package/src/stylesheets/5-components/_message.scss +10 -0
  153. package/src/stylesheets/5-components/_options.scss +3 -2
  154. package/translations/de-informal.js +0 -2
  155. package/translations/en.js +0 -2
  156. package/translations/es-informal.js +0 -2
  157. package/translations/nl-formal.js +0 -2
  158. package/translations/nl-informal.js +0 -2
  159. package/webpack/config.common.js +3 -3
  160. package/webpack/config.package.js +4 -4
  161. package/webpack/config.site.js +8 -6
  162. package/CHANGELOG.md +0 -551
  163. package/src/javascripts/ui/components/core/seamly-api.js +0 -44
  164. package/src/javascripts/ui/hooks/use-seamly-interrupt.js +0 -62
  165. package/src/javascripts/ui/store/index.js +0 -37
@@ -106,7 +106,7 @@ export class API {
106
106
  this.internalProducer = new EventProducer('API')
107
107
  this.internal$ = xs.create(this.internalProducer).flatten()
108
108
  this.connection$ = this.internal$.filter(
109
- event => event.type === 'connection',
109
+ (event) => event.type === 'connection',
110
110
  )
111
111
 
112
112
  this.connection$.subscribe({
@@ -316,7 +316,7 @@ export class API {
316
316
  .set('Authorization', `Bearer ${this.getAccessToken()}`)
317
317
  .send(formData)
318
318
 
319
- req.on('progress', function(e) {
319
+ req.on('progress', function (e) {
320
320
  const { direction, percent } = e
321
321
  if (direction === 'upload' && typeof progressCallback === 'function') {
322
322
  progressCallback(percent)
@@ -324,12 +324,12 @@ export class API {
324
324
  })
325
325
 
326
326
  req
327
- .then(uploadResponse => {
327
+ .then((uploadResponse) => {
328
328
  if (successCallback) {
329
329
  successCallback(uploadResponse)
330
330
  }
331
331
  })
332
- .catch(err => {
332
+ .catch((err) => {
333
333
  if (errorCallback) {
334
334
  errorCallback(err.response)
335
335
  } else {
@@ -353,7 +353,7 @@ export class API {
353
353
  this.updateUrls(body)
354
354
  return { accountConfig: body.config }
355
355
  })
356
- .catch(error => {
356
+ .catch((error) => {
357
357
  if (error.status === 404) {
358
358
  throw new SeamlyConfigurationError()
359
359
  }
@@ -377,7 +377,7 @@ export class API {
377
377
 
378
378
  return { initialState }
379
379
  })
380
- .catch(error => {
380
+ .catch((error) => {
381
381
  if (error.status === 401) {
382
382
  throw new SeamlyUnauthorizedError()
383
383
  }
@@ -398,7 +398,7 @@ export class API {
398
398
  promiseArray.push(this.getConversationIntitialState())
399
399
  }
400
400
 
401
- return Promise.all(promiseArray).then(responses => {
401
+ return Promise.all(promiseArray).then((responses) => {
402
402
  const config = responses.reduce(
403
403
  (configObj, partialConfig) => ({
404
404
  ...configObj,
@@ -415,7 +415,7 @@ export class API {
415
415
  if (!this.connected || (waitForReady && !this.ready)) {
416
416
  // Wait for connection
417
417
  this.connection$
418
- .filter(e => (waitForReady ? e.connected && e.ready : e.connected))
418
+ .filter((e) => (waitForReady ? e.connected && e.ready : e.connected))
419
419
  .take(1)
420
420
  .subscribe({
421
421
  next: () => this.send(command, payload, waitForReady),
@@ -471,6 +471,6 @@ export class API {
471
471
  }
472
472
 
473
473
  stream() {
474
- return this.internal$.filter(event => event.type !== 'connection')
474
+ return this.internal$.filter((event) => event.type !== 'connection')
475
475
  }
476
476
  }
@@ -17,7 +17,7 @@ export default class ConversationProducer {
17
17
 
18
18
  this.connect()
19
19
 
20
- this.socket.onError(err => {
20
+ this.socket.onError((err) => {
21
21
  log('[SOCKET][ERROR]', err)
22
22
  this.emit({
23
23
  type: 'error',
@@ -34,7 +34,7 @@ export default class ConversationProducer {
34
34
  })
35
35
  })
36
36
 
37
- this.channel.on('system', msg => {
37
+ this.channel.on('system', (msg) => {
38
38
  switch (msg.type) {
39
39
  case 'join_conversation_succeeded':
40
40
  this.emit({ type: 'connection', connected: true, ready: true })
@@ -42,17 +42,17 @@ export default class ConversationProducer {
42
42
  }
43
43
  })
44
44
 
45
- this.socket.onClose(msg => {
45
+ this.socket.onClose((msg) => {
46
46
  log('[SOCKET]CLOSE')
47
47
  this.emit({ type: 'connection', connected: false, ready: false })
48
48
  })
49
49
 
50
- this.channel.onClose(msg => {
50
+ this.channel.onClose((msg) => {
51
51
  log('[CHANNEL]CLOSE')
52
52
  this.emit({ type: 'connection', connected: false, ready: false })
53
53
  })
54
54
 
55
- this.channel.onError(msg => {
55
+ this.channel.onError((msg) => {
56
56
  log('[CHANNEL][ERROR]', msg)
57
57
  this.emit({ type: 'connection', connected: false, ready: false })
58
58
  })
@@ -77,7 +77,7 @@ export default class ConversationProducer {
77
77
  log('[CHANNEL][JOIN] OK')
78
78
  this.emit({ type: 'connection', connected: true, ready: false })
79
79
  })
80
- .receive('error', err => {
80
+ .receive('error', (err) => {
81
81
  log('[CHANNEL][JOIN] ERROR', err)
82
82
 
83
83
  this.emit({ type: 'error', payload: { type: 'join_channel_erred' } })
@@ -105,13 +105,13 @@ export default class ConversationProducer {
105
105
  }
106
106
 
107
107
  disconnect() {
108
- return new Promise(resolve => {
108
+ return new Promise((resolve) => {
109
109
  this.socket.disconnect(resolve)
110
110
  })
111
111
  }
112
112
 
113
113
  listenTo(type, transform = null) {
114
- this.channel.on(type, msg => {
114
+ this.channel.on(type, (msg) => {
115
115
  log('[RECEIVE]', type, msg)
116
116
  this.emit({
117
117
  type,
@@ -2,17 +2,6 @@ export const CSS_NAME = 'cvco'
2
2
 
3
3
  export const apiVersion = '2'
4
4
 
5
- export const layoutMode = 'window' // "window", "inline" ("sidebar"), "modal"
6
-
7
- export const appContainerClassNames = config => [
8
- `app--layout-${config.layoutMode}`,
9
- `namespace--${config.namespace}`,
10
- ]
11
-
12
- export const typing = {
13
- timeout: 2000, // How long to wait before we decide the user isn't typing
14
- }
15
-
16
5
  export const userParticipantId = 'seamly-client-participant'
17
6
 
18
7
  // How long to debounce distinct changes in unread messages for before
@@ -32,6 +21,8 @@ export const maxCharacterSrDebounceDelay = 300
32
21
  export const defaultTransitionTimeMs = 300
33
22
 
34
23
  export const defaultConfig = {
24
+ namespace: 'default',
25
+ layoutMode: 'window', // "window", "inline" ("sidebar"), "modal"
35
26
  messages: {
36
27
  agent: {
37
28
  showAvatar: false, // true, "inline"
@@ -46,4 +37,11 @@ export const defaultConfig = {
46
37
  threshold: 3600000, // Default threshold is an hour in milliseconds
47
38
  },
48
39
  },
40
+ typing: {
41
+ timeout: 2000, // How long to wait before we decide the user isn't typing
42
+ },
43
+ appContainerClassNames: (config) => [
44
+ `app--layout-${config.layoutMode}`,
45
+ `namespace--${config.namespace}`,
46
+ ],
49
47
  }
@@ -0,0 +1,25 @@
1
+ import { seamlyActions } from '../../ui/utils/seamly-utils'
2
+ import { createDomain } from '../../lib/redux-helpers'
3
+ import { Actions as ConfigActions } from '../config'
4
+ import { Actions as InterruptActions } from '../interrupt'
5
+
6
+ const { createThunk } = createDomain('app')
7
+
8
+ export const initialize = createThunk(
9
+ 'initialize',
10
+ (config) =>
11
+ async (dispatch, getState, { api }) => {
12
+ try {
13
+ dispatch(ConfigActions.initialize(config))
14
+
15
+ const { accountConfig = {}, initialState = {} } = await api.getConfig()
16
+ const { features } = accountConfig || {}
17
+
18
+ dispatch({ type: seamlyActions.SET_FEATURES, features })
19
+
20
+ dispatch({ type: seamlyActions.SET_INITIAL_STATE, initialState })
21
+ } catch (error) {
22
+ dispatch(InterruptActions.set(error))
23
+ }
24
+ },
25
+ )
@@ -0,0 +1,3 @@
1
+ import * as Actions from './actions'
2
+
3
+ export { Actions }
@@ -0,0 +1,6 @@
1
+ import { createDomain } from '../../lib/redux-helpers'
2
+
3
+ const { createAction } = createDomain('config')
4
+
5
+ export const initialize = createAction('initialize', (config) => ({ config }))
6
+ export const update = createAction('update', (config) => ({ config }))
@@ -0,0 +1,6 @@
1
+ import { useSelector } from '../redux'
2
+ import * as Selectors from './selectors'
3
+
4
+ export function useConfig() {
5
+ return useSelector(Selectors.selectConfig)
6
+ }
@@ -0,0 +1,8 @@
1
+ import * as Actions from './actions'
2
+ import * as Selectors from './selectors'
3
+
4
+ export * from './hooks'
5
+ export { default as createMiddleware } from './middleware'
6
+ export { default as Reducer } from './reducer'
7
+
8
+ export { Actions, Selectors }
@@ -0,0 +1,26 @@
1
+ import { initI18n } from '../i18n'
2
+ import * as Actions from './actions'
3
+ import { seamlyActions } from '../../ui/utils/seamly-utils'
4
+
5
+ export default function createMiddleware() {
6
+ return ({ dispatch }) =>
7
+ (next) =>
8
+ (action) => {
9
+ const result = next(action)
10
+
11
+ switch (action.type) {
12
+ case String(Actions.initialize):
13
+ case String(Actions.update):
14
+ if (action?.config?.translations) {
15
+ dispatch(initI18n(action.config.translations))
16
+ }
17
+ if (action?.config?.defaults?.agentName) {
18
+ dispatch({
19
+ type: seamlyActions.SET_HEADER_SUB_TITLE,
20
+ title: action?.config?.defaults?.agentName,
21
+ })
22
+ }
23
+ }
24
+ return result
25
+ }
26
+ }
@@ -0,0 +1,74 @@
1
+ import { defaultConfig } from '../../config'
2
+ import { createReducer } from '../../lib/redux-helpers'
3
+ import * as Actions from './actions'
4
+ import { pick } from '../../ui/utils/general-utils'
5
+
6
+ const initialState = {
7
+ ...defaultConfig,
8
+ hideOnNoUserResponse: false,
9
+ showDisclaimer: false,
10
+ showFaq: false,
11
+ customComponents: {},
12
+ defaults: {},
13
+ }
14
+
15
+ const configKeys = [
16
+ 'hideOnNoUserResponse',
17
+ 'showDisclaimer',
18
+ 'showFaq',
19
+ 'namespace',
20
+ 'customComponents',
21
+ 'defaults',
22
+ 'layoutMode',
23
+ 'api',
24
+ 'zIndex',
25
+ 'context',
26
+ 'appContainerClassNames',
27
+ 'messages',
28
+ 'typing',
29
+ 'visible',
30
+ 'visibilityCallback',
31
+ ]
32
+ const updateState = (state, { config }) => {
33
+ const { messages, typing, ...partialConfig } = pick(config, configKeys)
34
+ let newState = state
35
+ if (Object.keys(partialConfig).length > 0) {
36
+ newState = {
37
+ ...newState,
38
+ ...partialConfig,
39
+ }
40
+ }
41
+ if (messages) {
42
+ newState = {
43
+ ...newState,
44
+ messages: {
45
+ ...newState.messages,
46
+ ...messages,
47
+ },
48
+ }
49
+ }
50
+
51
+ if (typing) {
52
+ newState = {
53
+ ...newState,
54
+ typing: {
55
+ ...newState.typing,
56
+ ...typing,
57
+ },
58
+ }
59
+ }
60
+ return newState
61
+ }
62
+
63
+ export default createReducer(
64
+ 'config',
65
+ {
66
+ [Actions.initialize]: (state, action) => {
67
+ return updateState(state, action)
68
+ },
69
+ [Actions.update]: (state, action) => {
70
+ return updateState(state, action)
71
+ },
72
+ },
73
+ initialState,
74
+ )
@@ -0,0 +1,23 @@
1
+ import { createSelector } from 'reselect'
2
+ import { visibilityStates } from '../../ui/utils/seamly-utils'
3
+ import Reducer from './reducer'
4
+
5
+ export const selectState = (state) => state[String(Reducer)]
6
+
7
+ export const selectConfig = createSelector(selectState, (config) => {
8
+ let newConfig = {
9
+ visible:
10
+ config?.layoutMode === 'inline'
11
+ ? visibilityStates.open
12
+ : visibilityStates.minimized,
13
+ appContainerClassNames: config.appContainerClassNames || [],
14
+ ...config,
15
+ }
16
+ if (typeof newConfig.appContainerClassNames === 'function') {
17
+ newConfig = {
18
+ ...newConfig,
19
+ appContainerClassNames: newConfig.appContainerClassNames(newConfig),
20
+ }
21
+ }
22
+ return newConfig
23
+ })
@@ -4,7 +4,7 @@ const { createActions } = createDomain('forms')
4
4
 
5
5
  export const [registerForm, deregisterForm] = createActions('form', {
6
6
  register: (formId, persistData) => ({ formId, persistData }),
7
- deregister: formId => ({ formId }),
7
+ deregister: (formId) => ({ formId }),
8
8
  })
9
9
 
10
10
  export const [
@@ -29,10 +29,10 @@ export function useForm() {
29
29
  }
30
30
 
31
31
  export function useValidations(values, validationSchema) {
32
- const errors = useMemo(() => validate(values, validationSchema), [
33
- values,
34
- validationSchema,
35
- ])
32
+ const errors = useMemo(
33
+ () => validate(values, validationSchema),
34
+ [values, validationSchema],
35
+ )
36
36
  return {
37
37
  isValid: Object.keys(errors).length === 0,
38
38
  errors,
@@ -41,12 +41,8 @@ export function useValidations(values, validationSchema) {
41
41
 
42
42
  export function useFormControl(name) {
43
43
  const dispatch = useStoreDispatch()
44
- const {
45
- formId,
46
- updateControlValue,
47
- updateControlTouched,
48
- errors,
49
- } = useFormContext()
44
+ const { formId, updateControlValue, updateControlTouched, errors } =
45
+ useFormContext()
50
46
  const form = useSelectorWithProps(getFormById, { formId }, [formId])
51
47
  const isRegistered = !!form
52
48
  const isRegisteredRef = useRef()
@@ -77,10 +73,10 @@ export function useFormControl(name) {
77
73
  }, [isRegistered, formId, name, dispatch])
78
74
 
79
75
  // preact uses onInput instead of onChange
80
- const onInput = useCallback(e => updateControlValue(name, e.target.value), [
81
- name,
82
- updateControlValue,
83
- ])
76
+ const onInput = useCallback(
77
+ (e) => updateControlValue(name, e.target.value),
78
+ [name, updateControlValue],
79
+ )
84
80
 
85
81
  const onBlur = useCallback(() => {
86
82
  updateControlTouched(name, true)
@@ -25,10 +25,8 @@ export default function FormProvider({
25
25
  ])
26
26
  const [isSubmitted, setIsSubmitted] = useState(false)
27
27
  const [externalErrors, setExternalErrors] = useState({})
28
- const {
29
- isValid: validationIsValid,
30
- errors: validationErrors,
31
- } = useValidations(values, validationSchema)
28
+ const { isValid: validationIsValid, errors: validationErrors } =
29
+ useValidations(values, validationSchema)
32
30
  const errors = useMemo(
33
31
  () => ({
34
32
  ...validationErrors,
@@ -68,7 +66,7 @@ export default function FormProvider({
68
66
  // Function to manually set an error
69
67
  const setError = useCallback(
70
68
  (name, error) => {
71
- setExternalErrors(val => {
69
+ setExternalErrors((val) => {
72
70
  return {
73
71
  ...val,
74
72
  [name]: error,
@@ -79,7 +77,7 @@ export default function FormProvider({
79
77
  )
80
78
 
81
79
  const handleSubmit = useCallback(
82
- e => {
80
+ (e) => {
83
81
  e.preventDefault()
84
82
  setIsSubmitted(true)
85
83
  if (validationIsValid) {
@@ -2,7 +2,7 @@ import { createSelector } from 'reselect'
2
2
  import { getPropSelector } from '../redux/utils'
3
3
  import Reducer from './reducer'
4
4
 
5
- export const getState = state => state[String(Reducer)]
5
+ export const getState = (state) => state[String(Reducer)]
6
6
 
7
7
  export const getFormById = createSelector(
8
8
  getState,
@@ -12,12 +12,12 @@ export const getFormById = createSelector(
12
12
 
13
13
  export const getFormControlsByFormId = createSelector(
14
14
  getFormById,
15
- form => form?.controls || {},
15
+ (form) => form?.controls || {},
16
16
  )
17
17
 
18
18
  export const getFormValuesByFormId = createSelector(
19
19
  getFormControlsByFormId,
20
- controls => {
20
+ (controls) => {
21
21
  const valuesObj = {}
22
22
  Object.entries(controls).forEach(([key, { value }]) => {
23
23
  valuesObj[key] = value
@@ -8,10 +8,14 @@ import en from '../../../../translations/en'
8
8
  // Actions
9
9
  const { createAction } = createDomain('i18n')
10
10
 
11
- export const initI18n = createAction('init', overrides => ({
11
+ export const initI18n = createAction('init', (overrides) => ({
12
12
  overrides,
13
13
  }))
14
14
 
15
+ export const Actions = {
16
+ initI18n,
17
+ }
18
+
15
19
  // Reducer
16
20
  const defaultState = { translations: flattenObject(en), overrides: {} }
17
21
  export const Reducer = createReducer(
@@ -28,14 +32,14 @@ export const Reducer = createReducer(
28
32
  )
29
33
 
30
34
  // Selectors
31
- export const getState = state => state[String(Reducer)]
35
+ export const getState = (state) => state[String(Reducer)]
32
36
  export const getTranslations = createSelector(
33
37
  getState,
34
- state => state.translations,
38
+ (state) => state.translations,
35
39
  )
36
40
  export const getOverrides = createSelector(
37
41
  getState,
38
- state => state.overrides || {},
42
+ (state) => state.overrides || {},
39
43
  )
40
44
 
41
45
  export const getCombinedTranslations = createSelector(
@@ -48,7 +52,7 @@ export const getCombinedTranslations = createSelector(
48
52
  }
49
53
 
50
54
  const defaultKeys = Object.keys(translations)
51
- defaultKeys.forEach(key => {
55
+ defaultKeys.forEach((key) => {
52
56
  if (overrideKeys.indexOf(key) === -1) {
53
57
  console.error('Seamly: Missing translation key:', key)
54
58
  }
@@ -0,0 +1,6 @@
1
+ import { createDomain } from '../../lib/redux-helpers'
2
+
3
+ const { createAction } = createDomain('interrupt')
4
+
5
+ export const set = createAction('set', (error) => ({ error }))
6
+ export const clear = createAction('clear')
@@ -0,0 +1,29 @@
1
+ import { useMemo } from 'preact/hooks'
2
+ import { useI18n } from '../i18n'
3
+ import { useSelector } from '../redux'
4
+ import * as Selectors from './selectors'
5
+
6
+ export function useInterrupt() {
7
+ const { t } = useI18n()
8
+
9
+ const error = useSelector(Selectors.selectError)
10
+ const hasInterrupt = Boolean(error)
11
+
12
+ const meta = useMemo(() => {
13
+ if (!error) return {}
14
+ const { langKey, action } = error
15
+ const title = t(`${langKey}.title`)
16
+ const message = t(`${langKey}.message`)
17
+ const srText = t(`${langKey}.srText`)
18
+ const buttonText = t(`${langKey}.buttonText`)
19
+
20
+ return {
21
+ ...(langKey ? { title, message, srText } : {}),
22
+ ...(action ? { action } : {}),
23
+ ...(action && langKey ? { buttonText } : {}),
24
+ originalError: error,
25
+ }
26
+ }, [t, error])
27
+
28
+ return { hasInterrupt, meta, error }
29
+ }
@@ -0,0 +1,9 @@
1
+ import * as Actions from './actions'
2
+ import * as Selectors from './selectors'
3
+
4
+ export * from './hooks'
5
+
6
+ export { default as createMiddleware } from './middleware'
7
+ export { default as Reducer } from './reducer'
8
+
9
+ export { Actions, Selectors }
@@ -0,0 +1,30 @@
1
+ import SeamlyGeneralError from '../../api/errors/seamly-general-error'
2
+ import SeamlyConfigurationError from '../../api/errors/seamly-configuration-error'
3
+ import SeamlySessionExpiredError from '../../api/errors/seamly-session-expired-error'
4
+ import SeamlyOfflineError from '../../api/errors/seamly-offline-error'
5
+ import SeamlyUnauthorizedError from '../../api/errors/seamly-unauthorized-error'
6
+ import * as Actions from './actions'
7
+
8
+ const handledErrorTypes = [
9
+ SeamlyGeneralError,
10
+ SeamlyConfigurationError,
11
+ SeamlySessionExpiredError,
12
+ SeamlyOfflineError,
13
+ SeamlyUnauthorizedError,
14
+ ]
15
+
16
+ export default function createMiddleware() {
17
+ return () => (next) => (action) => {
18
+ if (action.type === String(Actions.set)) {
19
+ if (
20
+ !handledErrorTypes.some(
21
+ (ErrorType) => action.error instanceof ErrorType,
22
+ )
23
+ ) {
24
+ throw action.error
25
+ }
26
+ }
27
+
28
+ return next(action)
29
+ }
30
+ }
@@ -0,0 +1,22 @@
1
+ import { createReducer } from '../../lib/redux-helpers'
2
+ import * as Actions from './actions'
3
+
4
+ const initialState = {
5
+ error: undefined,
6
+ }
7
+
8
+ export default createReducer(
9
+ 'interrupt',
10
+ {
11
+ [Actions.set]: (state, { error }) => {
12
+ return {
13
+ ...state,
14
+ error,
15
+ }
16
+ },
17
+ [Actions.clear]: () => {
18
+ return initialState
19
+ },
20
+ },
21
+ initialState,
22
+ )
@@ -0,0 +1,5 @@
1
+ import { createSelector } from 'reselect'
2
+ import Reducer from './reducer'
3
+
4
+ export const selectState = (state) => state[String(Reducer)]
5
+ export const selectError = createSelector(selectState, ({ error }) => error)
@@ -0,0 +1 @@
1
+ export { default as createMiddleware } from './middleware'
@@ -0,0 +1,35 @@
1
+ import { featureKeys, seamlyActions } from '../../ui/utils/seamly-utils'
2
+ import { Actions as InterruptActions } from '../interrupt'
3
+ import SeamlyOfflineError from '../../api/errors/seamly-offline-error'
4
+
5
+ export default function createMiddleware({ api }) {
6
+ return ({ dispatch }) =>
7
+ (next) =>
8
+ (action) => {
9
+ const result = next(action)
10
+
11
+ switch (action.type) {
12
+ // This is needed for backwards compatibility. If there's an error,
13
+ // Cobrowsing has to be disabled in order to not "re-start" into co-browsing
14
+ case String(InterruptActions.set):
15
+ if (!(action.error instanceof SeamlyOfflineError)) {
16
+ dispatch({
17
+ type: seamlyActions.SET_USER_SELECTED_OPTION,
18
+ option: featureKeys.cobrowsing,
19
+ value: false,
20
+ })
21
+ }
22
+ break
23
+ case seamlyActions.SET_USER_SELECTED_OPTIONS:
24
+ api.store.set('options', action.options)
25
+ break
26
+ case seamlyActions.SET_USER_SELECTED_OPTION:
27
+ api.store.set('options', {
28
+ ...(api.store.get('options') || {}),
29
+ [action.option]: action.value,
30
+ })
31
+ break
32
+ }
33
+ return result
34
+ }
35
+ }