@seamly/web-ui 18.3.0 → 19.0.0-beta.3

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 (109) hide show
  1. package/build/dist/lib/index.debug.js +349 -74
  2. package/build/dist/lib/index.debug.min.js +1 -1
  3. package/build/dist/lib/index.debug.min.js.LICENSE.txt +108 -8
  4. package/build/dist/lib/index.js +6103 -5988
  5. package/build/dist/lib/index.min.js +1 -1
  6. package/build/dist/lib/index.min.js.LICENSE.txt +1 -1
  7. package/build/dist/lib/standalone.js +2414 -2226
  8. package/build/dist/lib/standalone.min.js +1 -1
  9. package/build/dist/lib/standalone.min.js.LICENSE.txt +1 -1
  10. package/build/dist/lib/storage.js +8 -1
  11. package/build/dist/lib/storage.min.js +1 -1
  12. package/build/dist/lib/style-guide.js +1517 -785
  13. package/build/dist/lib/style-guide.min.js +1 -1
  14. package/build/dist/lib/styles.css +1 -1
  15. package/package.json +27 -28
  16. package/src/javascripts/api/index.js +25 -40
  17. package/src/javascripts/api/producer.js +3 -6
  18. package/src/javascripts/config.js +3 -3
  19. package/src/javascripts/domains/app/actions.js +28 -11
  20. package/src/javascripts/domains/app/hooks.js +6 -0
  21. package/src/javascripts/domains/app/index.js +3 -0
  22. package/src/javascripts/domains/app/reducer.js +16 -0
  23. package/src/javascripts/domains/app/selectors.js +8 -0
  24. package/src/javascripts/domains/app/utils.js +4 -0
  25. package/src/javascripts/domains/config/actions.js +1 -3
  26. package/src/javascripts/domains/config/middleware.js +0 -4
  27. package/src/javascripts/domains/config/reducer.js +2 -13
  28. package/src/javascripts/domains/config/selectors.js +3 -3
  29. package/src/javascripts/domains/config/utils.js +4 -0
  30. package/src/javascripts/domains/forms/actions.js +1 -3
  31. package/src/javascripts/domains/forms/reducer.js +1 -2
  32. package/src/javascripts/domains/forms/selectors.js +2 -2
  33. package/src/javascripts/domains/forms/utils.js +5 -0
  34. package/src/javascripts/domains/i18n/actions.js +20 -0
  35. package/src/javascripts/domains/i18n/hooks.js +38 -0
  36. package/src/javascripts/domains/i18n/index.js +5 -84
  37. package/src/javascripts/domains/i18n/reducer.js +64 -0
  38. package/src/javascripts/domains/i18n/selectors.js +15 -0
  39. package/src/javascripts/domains/i18n/utils.js +4 -0
  40. package/src/javascripts/domains/interrupt/actions.js +1 -3
  41. package/src/javascripts/domains/interrupt/reducer.js +1 -2
  42. package/src/javascripts/domains/interrupt/selectors.js +3 -2
  43. package/src/javascripts/domains/interrupt/utils.js +4 -0
  44. package/src/javascripts/domains/redux/hooks.js +1 -0
  45. package/src/javascripts/domains/store/index.js +7 -1
  46. package/src/javascripts/domains/translations/actions.js +1 -3
  47. package/src/javascripts/domains/translations/components/chat-status.js +1 -1
  48. package/src/javascripts/domains/translations/components/options-dialog/form.js +11 -6
  49. package/src/javascripts/domains/translations/index.js +1 -0
  50. package/src/javascripts/domains/translations/middleware.js +43 -0
  51. package/src/javascripts/domains/translations/reducer.js +2 -9
  52. package/src/javascripts/domains/translations/selectors.js +2 -2
  53. package/src/javascripts/domains/translations/utils.js +4 -0
  54. package/src/javascripts/index.js +3 -0
  55. package/src/javascripts/lib/engine/index.js +1 -0
  56. package/src/javascripts/lib/mutex.js +30 -0
  57. package/src/javascripts/lib/redux-helpers/index.js +55 -16
  58. package/src/javascripts/lib/store/providers/session-storage.js +6 -1
  59. package/src/javascripts/style-guide/components/app.js +7 -2
  60. package/src/javascripts/style-guide/components/static-core.js +9 -3
  61. package/src/javascripts/style-guide/states.js +8 -8
  62. package/src/javascripts/style-guide/style-guide-engine.js +14 -11
  63. package/src/javascripts/ui/components/conversation/event/divider/variants/new-translation.js +1 -1
  64. package/src/javascripts/ui/components/conversation/event/upload.js +2 -2
  65. package/src/javascripts/ui/components/core/seamly-activity-monitor.js +2 -0
  66. package/src/javascripts/ui/components/core/seamly-event-subscriber.js +2 -0
  67. package/src/javascripts/ui/components/core/seamly-instance-functions-loader.js +1 -7
  68. package/src/javascripts/ui/components/core/seamly-new-notifications.js +5 -6
  69. package/src/javascripts/ui/components/core/seamly-read-state.js +6 -4
  70. package/src/javascripts/ui/components/entry/text-entry/hooks.js +6 -4
  71. package/src/javascripts/ui/components/entry/text-entry/text-entry-form.js +10 -3
  72. package/src/javascripts/ui/components/entry/upload/file-upload-form.js +6 -3
  73. package/src/javascripts/ui/components/entry/upload/index.js +8 -3
  74. package/src/javascripts/ui/components/faq/faq.js +2 -2
  75. package/src/javascripts/ui/components/layout/app-frame.js +11 -8
  76. package/src/javascripts/ui/components/layout/interrupt.js +6 -2
  77. package/src/javascripts/ui/components/warnings/resume-conversation-prompt.js +1 -1
  78. package/src/javascripts/ui/components/widgets/upload-progress.js +1 -1
  79. package/src/javascripts/ui/hooks/seamly-api-hooks.js +0 -6
  80. package/src/javascripts/ui/hooks/seamly-entry-hooks.js +17 -21
  81. package/src/javascripts/ui/hooks/seamly-hooks.js +0 -1
  82. package/src/javascripts/ui/hooks/use-seamly-commands.js +5 -6
  83. package/src/javascripts/ui/hooks/use-seamly-visibility.js +3 -5
  84. package/src/javascripts/ui/hooks/use-single-file-upload.js +4 -1
  85. package/src/javascripts/ui/utils/general-utils.js +6 -13
  86. package/src/stylesheets/1-settings/_config.scss +2 -1
  87. package/src/stylesheets/3-app/_app.scss +3 -4
  88. package/src/stylesheets/5-components/_faq.scss +3 -8
  89. package/src/stylesheets/5-components/_modal.scss +3 -3
  90. package/webpack/config.package.js +0 -18
  91. package/webpack/config.site.js +6 -0
  92. package/webpack/defaults.js +0 -3
  93. package/CHANGELOG.md +0 -573
  94. package/build/dist/translations/de-informal.js +0 -274
  95. package/build/dist/translations/de-informal.min.js +0 -1
  96. package/build/dist/translations/en.js +0 -274
  97. package/build/dist/translations/en.min.js +0 -1
  98. package/build/dist/translations/es-informal.js +0 -280
  99. package/build/dist/translations/es-informal.min.js +0 -1
  100. package/build/dist/translations/nl-formal.js +0 -274
  101. package/build/dist/translations/nl-formal.min.js +0 -1
  102. package/build/dist/translations/nl-informal.js +0 -274
  103. package/build/dist/translations/nl-informal.min.js +0 -1
  104. package/src/javascripts/lib/i18n.js +0 -46
  105. package/translations/de-informal.js +0 -235
  106. package/translations/en.js +0 -232
  107. package/translations/es-informal.js +0 -241
  108. package/translations/nl-formal.js +0 -228
  109. package/translations/nl-informal.js +0 -228
@@ -1,5 +1,5 @@
1
1
  import { defaultConfig } from '../../config'
2
- import { createReducer } from '../../lib/redux-helpers'
2
+ import { createReducer } from './utils'
3
3
  import * as Actions from './actions'
4
4
  import { pick } from '../../ui/utils/general-utils'
5
5
 
@@ -25,12 +25,11 @@ const configKeys = [
25
25
  'context',
26
26
  'appContainerClassNames',
27
27
  'messages',
28
- 'typing',
29
28
  'visible',
30
29
  'visibilityCallback',
31
30
  ]
32
31
  const updateState = (state, { config }) => {
33
- const { messages, typing, ...partialConfig } = pick(config, configKeys)
32
+ const { messages, ...partialConfig } = pick(config, configKeys)
34
33
  let newState = state
35
34
  if (Object.keys(partialConfig).length > 0) {
36
35
  newState = {
@@ -48,20 +47,10 @@ const updateState = (state, { config }) => {
48
47
  }
49
48
  }
50
49
 
51
- if (typing) {
52
- newState = {
53
- ...newState,
54
- typing: {
55
- ...newState.typing,
56
- ...typing,
57
- },
58
- }
59
- }
60
50
  return newState
61
51
  }
62
52
 
63
53
  export default createReducer(
64
- 'config',
65
54
  {
66
55
  [Actions.initialize]: (state, action) => {
67
56
  return updateState(state, action)
@@ -1,8 +1,6 @@
1
1
  import { createSelector } from 'reselect'
2
2
  import { visibilityStates } from '../../ui/utils/seamly-utils'
3
- import Reducer from './reducer'
4
-
5
- export const selectState = (state) => state[String(Reducer)]
3
+ import { selectState } from './utils'
6
4
 
7
5
  export const selectConfig = createSelector(selectState, (config) => {
8
6
  let newConfig = {
@@ -21,3 +19,5 @@ export const selectConfig = createSelector(selectState, (config) => {
21
19
  }
22
20
  return newConfig
23
21
  })
22
+
23
+ export { selectState }
@@ -0,0 +1,4 @@
1
+ import { createDomain } from '../../lib/redux-helpers'
2
+
3
+ export const { createAction, createThunk, createReducer, selectState } =
4
+ createDomain('config')
@@ -1,6 +1,4 @@
1
- import { createDomain } from '../../lib/redux-helpers'
2
-
3
- const { createActions } = createDomain('forms')
1
+ import { createActions } from './utils'
4
2
 
5
3
  export const [registerForm, deregisterForm] = createActions('form', {
6
4
  register: (formId, persistData) => ({ formId, persistData }),
@@ -1,4 +1,4 @@
1
- import { createReducer } from '../../lib/redux-helpers'
1
+ import { createReducer } from './utils'
2
2
  import * as Actions from './actions'
3
3
 
4
4
  const initialState = {}
@@ -31,7 +31,6 @@ function updateFormControl(state, formId, name, controlState) {
31
31
  }
32
32
 
33
33
  export default createReducer(
34
- 'form',
35
34
  {
36
35
  // Form handlers
37
36
  [Actions.registerForm]: (state, { formId, persistData }) => {
@@ -1,8 +1,8 @@
1
1
  import { createSelector } from 'reselect'
2
2
  import { getPropSelector } from '../redux/utils'
3
- import Reducer from './reducer'
3
+ import { selectState } from './utils'
4
4
 
5
- export const getState = (state) => state[String(Reducer)]
5
+ export const getState = selectState
6
6
 
7
7
  export const getFormById = createSelector(
8
8
  getState,
@@ -1,3 +1,8 @@
1
+ import { createDomain } from '../../lib/redux-helpers'
2
+
3
+ export const { createActions, createReducer, selectState } =
4
+ createDomain('forms')
5
+
1
6
  export function validate(values, schema = {}) {
2
7
  return Object.entries(schema).reduce((errors, [key, validations]) => {
3
8
  if (validations && !Array.isArray(validations)) {
@@ -0,0 +1,20 @@
1
+ import createMutex from '../../lib/mutex'
2
+ import { selectLocale } from './selectors'
3
+ import { createAction, createThunk } from './utils'
4
+
5
+ export const setInitialLocale = createAction('setInitialLocale', (locale) => ({
6
+ locale,
7
+ }))
8
+
9
+ const mutex = createMutex()
10
+ export const setLocale = createThunk(
11
+ 'setLocale',
12
+ async (locale, { getState, extra: { api } }) => {
13
+ return mutex.runExclusively(() => {
14
+ if (locale === selectLocale(getState())) {
15
+ return undefined
16
+ }
17
+ return api.getTranslations(locale)
18
+ })
19
+ },
20
+ )
@@ -0,0 +1,38 @@
1
+ import { useCallback } from 'preact/hooks'
2
+ import {
3
+ MessageFormatter,
4
+ pluralTypeHandler,
5
+ selectTypeHandler,
6
+ } from '@ultraq/icu-message-formatter'
7
+ import { useSelector } from '../redux'
8
+ import * as Selectors from './selectors'
9
+
10
+ // The passed in locale (en-GB) is only used to call Intl.PluralRules.select() in
11
+ // pluralTypeHandler. Since we only use exact plural matches (=0, =1 etc) we can
12
+ // safely use en-GB all the time.
13
+ const formatter = new MessageFormatter('en-GB', {
14
+ plural: pluralTypeHandler,
15
+ select: selectTypeHandler,
16
+ })
17
+
18
+ export function useI18n() {
19
+ const translations = useSelector(Selectors.selectTranslations)
20
+ const locale = useSelector(Selectors.selectLocale)
21
+ const initialLocale = useSelector(Selectors.selectInitialLocale)
22
+ const t = useCallback(
23
+ (key, values = {}) => {
24
+ const translation = translations[key]
25
+ if (!translation) {
26
+ return null
27
+ }
28
+ return formatter.format(translation, values)
29
+ },
30
+ [translations],
31
+ )
32
+
33
+ return {
34
+ t,
35
+ locale,
36
+ initialLocale,
37
+ }
38
+ }
@@ -1,86 +1,7 @@
1
- import { useCallback } from 'preact/hooks'
2
- import { createSelector } from 'reselect'
3
- import { createDomain, createReducer } from '../../lib/redux-helpers'
4
- import { useSelector } from '../redux'
5
- import { flattenObject } from '../../ui/utils/general-utils'
6
- import en from '../../../../translations/en'
1
+ import * as Actions from './actions'
2
+ import * as Selectors from './selectors'
7
3
 
8
- // Actions
9
- const { createAction } = createDomain('i18n')
4
+ export * from './hooks'
5
+ export { default as Reducer } from './reducer'
10
6
 
11
- export const initI18n = createAction('init', (overrides) => ({
12
- overrides,
13
- }))
14
-
15
- export const Actions = {
16
- initI18n,
17
- }
18
-
19
- // Reducer
20
- const defaultState = { translations: flattenObject(en), overrides: {} }
21
- export const Reducer = createReducer(
22
- 'i18n',
23
- {
24
- [initI18n]: (state, { overrides }) => {
25
- return {
26
- ...state,
27
- overrides: overrides ? flattenObject(overrides) : undefined,
28
- }
29
- },
30
- },
31
- defaultState,
32
- )
33
-
34
- // Selectors
35
- export const getState = (state) => state[String(Reducer)]
36
- export const getTranslations = createSelector(
37
- getState,
38
- (state) => state.translations,
39
- )
40
- export const getOverrides = createSelector(
41
- getState,
42
- (state) => state.overrides || {},
43
- )
44
-
45
- export const getCombinedTranslations = createSelector(
46
- getTranslations,
47
- getOverrides,
48
- (translations, overrides) => {
49
- const overrideKeys = Object.keys(overrides)
50
- if (overrideKeys.length === 0) {
51
- return translations
52
- }
53
-
54
- const defaultKeys = Object.keys(translations)
55
- defaultKeys.forEach((key) => {
56
- if (overrideKeys.indexOf(key) === -1) {
57
- console.error('Seamly: Missing translation key:', key)
58
- }
59
- })
60
-
61
- return {
62
- ...translations,
63
- ...overrides,
64
- }
65
- },
66
- )
67
-
68
- // Hooks
69
- export function useI18n() {
70
- const translations = useSelector(getCombinedTranslations)
71
- const t = useCallback(
72
- (key, values = {}) => {
73
- const translation = translations[key]
74
- if (typeof translation === 'function') {
75
- return translation(values)
76
- } else {
77
- return translation
78
- }
79
- },
80
- [translations],
81
- )
82
-
83
- return {
84
- t,
85
- }
86
- }
7
+ export { Actions, Selectors }
@@ -0,0 +1,64 @@
1
+ import * as Actions from './actions'
2
+ import { createReducer } from './utils'
3
+
4
+ const defaultState = {
5
+ translations: {
6
+ 'errors.configError.message':
7
+ 'We are sorry this happened, please retry at a later time.',
8
+ 'errors.configError.srText':
9
+ 'A chat configuration error occurred. Our apologies, please retry at a later time.',
10
+ 'errors.configError.title': 'Chat configuration error.',
11
+ 'errors.general.buttonText': 'Restart chat',
12
+ 'errors.general.message': 'Do you want to start a new chat session?',
13
+ 'errors.general.srText':
14
+ 'Something went wrong with the chat session. You can restart the chat.',
15
+ 'errors.general.title': 'Something went wrong',
16
+ 'errors.seamlyOffline.message':
17
+ 'There might be a problem with your or our network connection. The chat session should resume as soon the connection is available again.',
18
+ 'errors.seamlyOffline.srText':
19
+ 'The chat has connection issues. There might be a problem with your or our network connection. The chat session should resume as soon as the connection is available again.',
20
+ 'errors.seamlyOffline.title': 'Connection issues',
21
+ },
22
+ isLoading: false,
23
+ initialLocale: undefined,
24
+ }
25
+
26
+ export default createReducer(
27
+ {
28
+ [Actions.setInitialLocale]: (state, { locale }) => ({
29
+ ...state,
30
+ initialLocale: locale,
31
+ }),
32
+ [Actions.setLocale.pending]: (state) => ({
33
+ ...state,
34
+ isLoading: true,
35
+ }),
36
+ [Actions.setLocale.fulfilled]: (
37
+ state,
38
+ { payload: translations, meta: { arg: locale } },
39
+ ) => {
40
+ if (!translations) {
41
+ return { ...state, isLoading: false }
42
+ }
43
+ return {
44
+ ...state,
45
+ isLoading: false,
46
+ locale,
47
+ translations: Object.keys(translations)
48
+ .sort()
49
+ .reduce(
50
+ (accum, key) => ({
51
+ ...accum,
52
+ [key]: translations[key],
53
+ }),
54
+ {},
55
+ ),
56
+ }
57
+ },
58
+ [Actions.setLocale.rejected]: (state) => ({
59
+ ...state,
60
+ isLoading: false,
61
+ }),
62
+ },
63
+ defaultState,
64
+ )
@@ -0,0 +1,15 @@
1
+ import { createSelector } from 'reselect'
2
+ import { selectState } from './utils'
3
+
4
+ export const selectTranslations = createSelector(
5
+ selectState,
6
+ (state) => state.translations,
7
+ )
8
+
9
+ export const selectInitialLocale = createSelector(
10
+ selectState,
11
+ (state) => state.initialLocale,
12
+ )
13
+ export const selectLocale = createSelector(selectState, (state) => state.locale)
14
+
15
+ export { selectState }
@@ -0,0 +1,4 @@
1
+ import { createDomain } from '../../lib/redux-helpers'
2
+
3
+ export const { createAction, createThunk, createReducer, selectState } =
4
+ createDomain('i18n')
@@ -1,6 +1,4 @@
1
- import { createDomain } from '../../lib/redux-helpers'
2
-
3
- const { createAction } = createDomain('interrupt')
1
+ import { createAction } from './utils'
4
2
 
5
3
  export const set = createAction('set', (error) => ({ error }))
6
4
  export const clear = createAction('clear')
@@ -1,4 +1,4 @@
1
- import { createReducer } from '../../lib/redux-helpers'
1
+ import { createReducer } from './utils'
2
2
  import * as Actions from './actions'
3
3
 
4
4
  const initialState = {
@@ -6,7 +6,6 @@ const initialState = {
6
6
  }
7
7
 
8
8
  export default createReducer(
9
- 'interrupt',
10
9
  {
11
10
  [Actions.set]: (state, { error }) => {
12
11
  return {
@@ -1,5 +1,6 @@
1
1
  import { createSelector } from 'reselect'
2
- import Reducer from './reducer'
2
+ import { selectState } from './utils'
3
3
 
4
- export const selectState = (state) => state[String(Reducer)]
5
4
  export const selectError = createSelector(selectState, ({ error }) => error)
5
+
6
+ export { selectState }
@@ -0,0 +1,4 @@
1
+ import { createDomain } from '../../lib/redux-helpers'
2
+
3
+ export const { createAction, createReducer, selectState } =
4
+ createDomain('interrupt')
@@ -26,6 +26,7 @@ export function useSelector(selector, deps = []) {
26
26
  // instead of accepting a stabilized selector, we stick to the signature
27
27
  // of useCallback, as that makes the exposed api much more dev-friendly
28
28
  // otherwise you'd have to stabilize selectors externally
29
+ // eslint-disable-next-line react-hooks/exhaustive-deps
29
30
  const selectorCb = useCallback(selector, deps)
30
31
  // we're keeping the selector in a ref to compare against
31
32
  // we need this both in the store subscription and for
@@ -1,11 +1,15 @@
1
1
  import thunkMiddleware from 'redux-thunk'
2
2
  import { createReduxStore } from '../redux'
3
+ import { Reducer as appReducer } from '../app'
3
4
  import {
4
5
  createMiddleware as createConfigMiddleware,
5
6
  Reducer as configReducer,
6
7
  } from '../config'
7
8
  import { Reducer as formReducer } from '../forms'
8
- import { Reducer as translationsReducer } from '../translations'
9
+ import {
10
+ Reducer as translationsReducer,
11
+ createMiddleware as createI18nMiddleware,
12
+ } from '../translations'
9
13
  import { Reducer as i18nReducer } from '../i18n'
10
14
  import {
11
15
  Reducer as interruptReducer,
@@ -18,6 +22,7 @@ export function createStore({ initialState, api } = {}) {
18
22
  const store = createReduxStore({
19
23
  reducers: {
20
24
  state: stateReducer,
25
+ [String(appReducer)]: appReducer,
21
26
  [String(configReducer)]: configReducer,
22
27
  [String(formReducer)]: formReducer,
23
28
  [String(translationsReducer)]: translationsReducer,
@@ -32,6 +37,7 @@ export function createStore({ initialState, api } = {}) {
32
37
  createConfigMiddleware(),
33
38
  createInterruptMiddleware(),
34
39
  createOptionsMiddleware({ api }),
40
+ createI18nMiddleware(),
35
41
  ],
36
42
  })
37
43
  return store
@@ -1,6 +1,4 @@
1
- import { createDomain } from '../../lib/redux-helpers'
2
-
3
- const { createActions } = createDomain('translations')
1
+ import { createActions } from './utils'
4
2
 
5
3
  export const [enable, disable] = createActions('translate', {
6
4
  enable: (locale) => ({ locale }),
@@ -31,7 +31,7 @@ export default function TranslationsChatStatus() {
31
31
  <ChatStatus
32
32
  type="translations"
33
33
  id={id}
34
- label={t('translations.status.label', languageName)}
34
+ label={t('translations.status.label', { language: languageName })}
35
35
  onButtonClick={handleClickStop}
36
36
  buttonText={t('translations.status.stopText')}
37
37
  srButtonText={t('translations.status.srStopText')}
@@ -8,7 +8,7 @@ import Select from '../../../../ui/components/form-controls/select'
8
8
  function TranslationsOptionsDialogForm({ controlName, descriptionId }) {
9
9
  const { t } = useI18n()
10
10
  const { isActive, languages, currentLocale } = useTranslations()
11
-
11
+ const { locale: uiLocale } = useI18n()
12
12
  const languageName = useMemo(() => {
13
13
  return languages?.find((lang) => lang.locale === currentLocale)?.nativeName
14
14
  }, [languages, currentLocale])
@@ -16,12 +16,17 @@ function TranslationsOptionsDialogForm({ controlName, descriptionId }) {
16
16
  const options = useMemo(() => {
17
17
  return [
18
18
  { value: '', label: t('translations.settings.defaultOptionLabel') },
19
- ...languages.map((language) => ({
20
- value: language.locale,
21
- label: language.nativeName,
22
- })),
19
+ ...languages
20
+ .filter(
21
+ (language) =>
22
+ language.locale.toLowerCase() !== String(uiLocale).toLowerCase(),
23
+ )
24
+ .map((language) => ({
25
+ value: language.locale,
26
+ label: language.nativeName,
27
+ })),
23
28
  ]
24
- }, [t, languages])
29
+ }, [t, languages, uiLocale])
25
30
 
26
31
  return (
27
32
  <Form noValidate="true">
@@ -2,6 +2,7 @@ import * as Actions from './actions'
2
2
  import * as Selectors from './selectors'
3
3
 
4
4
  export * from './hooks'
5
+ export { default as createMiddleware } from './middleware'
5
6
  export { default as Reducer } from './reducer'
6
7
  export { default as OptionsButton } from './components/options-button'
7
8
  export { default as ChatStatus } from './components/chat-status'
@@ -0,0 +1,43 @@
1
+ import * as Actions from './actions'
2
+ import { seamlyActions } from '../../ui/utils/seamly-utils'
3
+ import { Actions as I18nActions, Selectors as I18nSelectors } from '../i18n'
4
+
5
+ export default function createMiddleware() {
6
+ return ({ dispatch, getState }) =>
7
+ (next) =>
8
+ (action) => {
9
+ const result = next(action)
10
+
11
+ switch (action.type) {
12
+ case String(seamlyActions.SET_HISTORY):
13
+ if (action.history?.translation?.enabled) {
14
+ dispatch(Actions.enable(action.history.translation.locale))
15
+ }
16
+ break
17
+ case String(seamlyActions.SET_INITIAL_STATE):
18
+ if (action.initialState?.translation?.enabled) {
19
+ dispatch(Actions.enable(action.initialState.translation.locale))
20
+ dispatch(I18nActions.setLocale(action.locale))
21
+ }
22
+ break
23
+ case String(seamlyActions.ADD_EVENT):
24
+ if (
25
+ action.event.type === 'info' &&
26
+ action.event?.payload?.body?.subtype === 'new_translation' &&
27
+ action.event.payload.body.translationEnabled
28
+ ) {
29
+ dispatch(
30
+ I18nActions.setLocale(
31
+ action.event.payload.body.translationLocale,
32
+ ),
33
+ )
34
+ }
35
+ break
36
+ case String(Actions.disable):
37
+ const initialLocale = I18nSelectors.selectInitialLocale(getState())
38
+ dispatch(I18nActions.setLocale(initialLocale))
39
+ break
40
+ }
41
+ return result
42
+ }
43
+ }
@@ -1,4 +1,4 @@
1
- import { createReducer } from '../../lib/redux-helpers'
1
+ import { createReducer } from './utils'
2
2
  import { seamlyActions } from '../../ui/utils/seamly-utils'
3
3
  import { randomId } from '../../lib/id'
4
4
  import * as Actions from './actions'
@@ -13,7 +13,6 @@ const initialState = {
13
13
  }
14
14
 
15
15
  export default createReducer(
16
- 'translations',
17
16
  {
18
17
  [seamlyActions.SET_FEATURES]: (state, action) => {
19
18
  const feature = action?.features?.translation
@@ -27,13 +26,7 @@ export default createReducer(
27
26
  languages: feature.languages || [],
28
27
  }
29
28
  },
30
- [seamlyActions.SET_HISTORY]: (state, { history }) => {
31
- return {
32
- ...state,
33
- isActive: history?.translation?.enabled,
34
- currentLocale: history?.translation?.locale,
35
- }
36
- },
29
+
37
30
  [seamlyActions.CLEAR_FEATURES]: () => initialState,
38
31
  [Actions.enable]: (state, { locale }) => {
39
32
  return {
@@ -1,8 +1,8 @@
1
1
  import { createSelector } from 'reselect'
2
2
  import { getPropSelector } from '../redux/utils'
3
- import Reducer from './reducer'
3
+ import { selectState } from './utils'
4
4
 
5
- export const getState = (state) => state[String(Reducer)]
5
+ export const getState = selectState
6
6
 
7
7
  export const getOriginalPayloadIds = createSelector(
8
8
  getState,
@@ -0,0 +1,4 @@
1
+ import { createDomain } from '../../lib/redux-helpers'
2
+
3
+ export const { createActions, createReducer, selectState } =
4
+ createDomain('translations')
@@ -137,6 +137,9 @@ export { default as View } from './ui/components/layout/view'
137
137
  // Used by: StyleGuide
138
138
  export { visibilityStates } from './ui/utils/seamly-utils'
139
139
 
140
+ // Used by: StyleGuide
141
+ export { API } from './api'
142
+
140
143
  // Used by: StyleGuide
141
144
  export { default as SeamlyGeneralError } from './api/errors/seamly-general-error'
142
145
 
@@ -21,6 +21,7 @@ export default class Engine {
21
21
  this.api = new API({
22
22
  namespace: config.namespace,
23
23
  config: config.api,
24
+ locale: config?.context?.locale,
24
25
  })
25
26
 
26
27
  this.eventBus = new Events()
@@ -0,0 +1,30 @@
1
+ export default function createMutex() {
2
+ let isRunning = false
3
+ const tasks = []
4
+
5
+ const next = async () => {
6
+ if (!isRunning) {
7
+ while (tasks.length) {
8
+ const task = tasks.shift()
9
+ isRunning = true
10
+ // eslint-disable-next-line no-await-in-loop
11
+ await task().catch(() => {})
12
+ isRunning = false
13
+ }
14
+ }
15
+ }
16
+ const runExclusively = async (task) => {
17
+ const prms = new Promise((resolve, reject) => {
18
+ tasks.push(async () => {
19
+ try {
20
+ resolve(await task())
21
+ } catch (e) {
22
+ reject(e)
23
+ }
24
+ })
25
+ })
26
+ next()
27
+ return prms
28
+ }
29
+ return { next, runExclusively }
30
+ }