@seamly/web-ui 19.0.0-beta.1 → 19.1.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 (85) hide show
  1. package/package.json +1 -1
  2. package/src/javascripts/api/errors/seamly-base-error.js +10 -0
  3. package/src/javascripts/api/errors/seamly-configuration-error.js +4 -6
  4. package/src/javascripts/api/errors/seamly-general-error.js +4 -6
  5. package/src/javascripts/api/errors/seamly-offline-error.js +4 -6
  6. package/src/javascripts/api/errors/seamly-session-expired-error.js +4 -6
  7. package/src/javascripts/api/errors/seamly-unauthorized-error.js +4 -6
  8. package/src/javascripts/api/errors/seamly-unavailable-error.js +17 -0
  9. package/src/javascripts/api/index.js +10 -11
  10. package/src/javascripts/domains/app/actions.js +33 -24
  11. package/src/javascripts/domains/app/index.js +2 -1
  12. package/src/javascripts/domains/config/reducer.js +1 -0
  13. package/src/javascripts/domains/config/selectors.js +1 -1
  14. package/src/javascripts/domains/errors/index.js +32 -0
  15. package/src/javascripts/domains/i18n/actions.js +9 -24
  16. package/src/javascripts/domains/i18n/reducer.js +15 -3
  17. package/src/javascripts/domains/i18n/utils.js +2 -7
  18. package/src/javascripts/domains/interrupt/middleware.js +12 -9
  19. package/src/javascripts/domains/interrupt/reducer.js +10 -9
  20. package/src/javascripts/domains/store/index.js +7 -2
  21. package/src/javascripts/domains/store/state-reducer.js +10 -6
  22. package/src/javascripts/domains/visibility/actions.js +73 -0
  23. package/src/javascripts/domains/visibility/constants.js +8 -0
  24. package/src/javascripts/domains/visibility/hooks.js +24 -0
  25. package/src/javascripts/domains/visibility/index.js +8 -0
  26. package/src/javascripts/domains/visibility/reducer.js +19 -0
  27. package/src/javascripts/domains/visibility/selectors.js +9 -0
  28. package/src/javascripts/domains/visibility/utils.js +42 -0
  29. package/src/javascripts/index.js +3 -12
  30. package/src/javascripts/lib/engine/index.js +1 -0
  31. package/src/javascripts/lib/redux-helpers/index.js +45 -13
  32. package/src/javascripts/lib/store/providers/session-storage.js +6 -1
  33. package/src/javascripts/style-guide/components/app.js +1 -1
  34. package/src/javascripts/style-guide/components/static-core.js +6 -1
  35. package/src/javascripts/style-guide/states.js +48 -21
  36. package/src/javascripts/style-guide/style-guide-engine.js +4 -2
  37. package/src/javascripts/ui/components/conversation/conversation.js +2 -2
  38. package/src/javascripts/ui/components/conversation/event/hooks/use-text-rendering.js +1 -8
  39. package/src/javascripts/ui/components/conversation/event/text.js +19 -13
  40. package/src/javascripts/ui/components/core/seamly-core.js +3 -0
  41. package/src/javascripts/ui/components/core/seamly-event-subscriber.js +3 -3
  42. package/src/javascripts/ui/components/core/seamly-initializer.js +2 -6
  43. package/src/javascripts/ui/components/core/seamly-instance-functions-loader.js +2 -3
  44. package/src/javascripts/ui/components/core/seamly-new-notifications.js +2 -2
  45. package/src/javascripts/ui/components/core/seamly-read-state.js +2 -2
  46. package/src/javascripts/ui/components/entry/toggle-button.js +2 -2
  47. package/src/javascripts/ui/components/layout/agent-info.js +2 -2
  48. package/src/javascripts/ui/components/layout/app-frame.js +2 -3
  49. package/src/javascripts/ui/components/layout/chat-frame.js +2 -2
  50. package/src/javascripts/ui/components/layout/modal-wrapper.js +3 -6
  51. package/src/javascripts/ui/components/layout/view.js +3 -6
  52. package/src/javascripts/ui/hooks/seamly-hooks.js +0 -2
  53. package/src/javascripts/ui/hooks/use-seamly-chat.js +3 -5
  54. package/src/javascripts/ui/hooks/use-seamly-commands.js +7 -29
  55. package/src/javascripts/ui/hooks/use-seamly-idle-detach-countdown.js +2 -2
  56. package/src/javascripts/ui/utils/general-utils.js +0 -9
  57. package/src/javascripts/ui/utils/seamly-utils.js +0 -66
  58. package/build/dist/lib/components.js +0 -62
  59. package/build/dist/lib/components.min.js +0 -1
  60. package/build/dist/lib/config.js +0 -51
  61. package/build/dist/lib/config.min.js +0 -1
  62. package/build/dist/lib/contexts.js +0 -53
  63. package/build/dist/lib/contexts.min.js +0 -1
  64. package/build/dist/lib/hooks.js +0 -64
  65. package/build/dist/lib/hooks.min.js +0 -1
  66. package/build/dist/lib/index.debug.js +0 -3113
  67. package/build/dist/lib/index.debug.min.js +0 -2
  68. package/build/dist/lib/index.debug.min.js.LICENSE.txt +0 -1099
  69. package/build/dist/lib/index.js +0 -25840
  70. package/build/dist/lib/index.min.js +0 -2
  71. package/build/dist/lib/index.min.js.LICENSE.txt +0 -14
  72. package/build/dist/lib/standalone.js +0 -34715
  73. package/build/dist/lib/standalone.min.js +0 -2
  74. package/build/dist/lib/standalone.min.js.LICENSE.txt +0 -9
  75. package/build/dist/lib/storage.js +0 -268
  76. package/build/dist/lib/storage.min.js +0 -2
  77. package/build/dist/lib/storage.min.js.LICENSE.txt +0 -1
  78. package/build/dist/lib/style-guide.js +0 -8039
  79. package/build/dist/lib/style-guide.min.js +0 -1
  80. package/build/dist/lib/styles.css +0 -1
  81. package/build/dist/lib/styles.js +0 -1
  82. package/build/dist/lib/utils.js +0 -59
  83. package/build/dist/lib/utils.min.js +0 -1
  84. package/src/javascripts/ui/hooks/use-seamly-stored-visibility.js +0 -31
  85. package/src/javascripts/ui/hooks/use-seamly-visibility.js +0 -98
@@ -0,0 +1,24 @@
1
+ import { useCallback } from 'preact/hooks'
2
+ import { useConfig } from '../config'
3
+ import { useSelector, useStoreDispatch } from '../redux'
4
+ import * as Actions from './actions'
5
+ import * as Selectors from './selectors'
6
+ import { visibilityStates } from './constants'
7
+
8
+ export const useVisibility = () => {
9
+ const dispatch = useStoreDispatch()
10
+ const visible = useSelector(Selectors.selectVisibility)
11
+ const isVisible = visible ? visible !== visibilityStates.hidden : false
12
+ const { layoutMode } = useConfig()
13
+ const isOpen =
14
+ visible && layoutMode
15
+ ? visible === visibilityStates.open ||
16
+ (layoutMode === 'inline' && visible !== visibilityStates.hidden)
17
+ : false
18
+ const setVisibility = useCallback(
19
+ (visibility) => dispatch(Actions.setVisibility(visibility)),
20
+ [dispatch],
21
+ )
22
+
23
+ return { isVisible, isOpen, visible, setVisibility }
24
+ }
@@ -0,0 +1,8 @@
1
+ import * as Actions from './actions'
2
+ import * as Selectors from './selectors'
3
+
4
+ export * from './hooks'
5
+ export { visibilityStates } from './constants'
6
+ export { default as Reducer } from './reducer'
7
+ export { calculateVisibility } from './utils'
8
+ export { Actions, Selectors }
@@ -0,0 +1,19 @@
1
+ import { createReducer } from './utils'
2
+ import * as Actions from './actions'
3
+ import { visibilityStates } from './constants'
4
+
5
+ const initialState = {
6
+ visibility: visibilityStates.initialize,
7
+ }
8
+
9
+ export default createReducer(
10
+ {
11
+ [Actions.setFromStorage]: (state, { visibility }) => ({
12
+ ...state,
13
+ visibility,
14
+ }),
15
+ [Actions.setVisibility.fulfilled]: (state, { payload: visibility }) =>
16
+ visibility ? { ...state, visibility } : state,
17
+ },
18
+ initialState,
19
+ )
@@ -0,0 +1,9 @@
1
+ import { createSelector } from 'reselect'
2
+ import { selectState } from './utils'
3
+
4
+ export const selectVisibility = createSelector(
5
+ selectState,
6
+ (state) => state.visibility,
7
+ )
8
+
9
+ export { selectState }
@@ -0,0 +1,42 @@
1
+ import { createDomain } from '../../lib/redux-helpers'
2
+ import { visibilityStates } from './constants'
3
+
4
+ export const {
5
+ createAction,
6
+ createActions,
7
+ createThunk,
8
+ createReducer,
9
+ selectState,
10
+ } = createDomain('visibility')
11
+
12
+ export const calculateVisibility = ({
13
+ hasResponded,
14
+ previousVisibility,
15
+ requestedVisibility,
16
+ config,
17
+ }) => {
18
+ const { defaults, layoutMode, hideOnNoUserResponse } = config
19
+
20
+ const { visible: defaultVisibility } = defaults || {}
21
+
22
+ // Requesting open should override the responded check.
23
+ if (
24
+ layoutMode === 'window' &&
25
+ hideOnNoUserResponse &&
26
+ requestedVisibility !== visibilityStates.open
27
+ ) {
28
+ return hasResponded
29
+ ? requestedVisibility || previousVisibility || visibilityStates.open
30
+ : visibilityStates.hidden
31
+ }
32
+
33
+ const baseVisibility =
34
+ layoutMode === 'inline' ? visibilityStates.open : visibilityStates.minimized
35
+
36
+ return (
37
+ requestedVisibility ||
38
+ previousVisibility ||
39
+ defaultVisibility ||
40
+ baseVisibility
41
+ )
42
+ }
@@ -12,7 +12,7 @@ export { default as AgentInfo } from './ui/components/layout/agent-info'
12
12
  export { default as AppFrame } from './ui/components/layout/app-frame'
13
13
 
14
14
  // Used by: Client
15
- export { calculateVisibility } from './ui/utils/seamly-utils'
15
+ export { calculateVisibility } from './domains/visibility'
16
16
 
17
17
  // Used by: Client
18
18
  export { default as ChatFrame } from './ui/components/layout/chat-frame'
@@ -114,7 +114,7 @@ export { useSeamlyMessageContainerClassNames } from './ui/hooks/seamly-hooks'
114
114
  export { useSeamlyOptions } from './ui/hooks/seamly-hooks'
115
115
 
116
116
  // Used by: Client
117
- export { useSeamlyVisibility } from './ui/hooks/seamly-hooks'
117
+ export { useVisibility as useSeamlyVisibility } from './domains/visibility'
118
118
 
119
119
  // Used by: Client
120
120
  export {
@@ -135,7 +135,7 @@ export { default as View } from './ui/components/layout/view'
135
135
 
136
136
  // Used by: Client
137
137
  // Used by: StyleGuide
138
- export { visibilityStates } from './ui/utils/seamly-utils'
138
+ export { visibilityStates } from './domains/visibility'
139
139
 
140
140
  // Used by: StyleGuide
141
141
  export { API } from './api'
@@ -143,14 +143,5 @@ export { API } from './api'
143
143
  // Used by: StyleGuide
144
144
  export { default as SeamlyGeneralError } from './api/errors/seamly-general-error'
145
145
 
146
- // Used by: StyleGuide
147
- export { default as SeamlyConfigurationError } from './api/errors/seamly-configuration-error'
148
-
149
- // Used by: StyleGuide
150
- export { default as SeamlySessionExpiredError } from './api/errors/seamly-session-expired-error'
151
-
152
146
  // Used by: StyleGuide
153
147
  export { default as SeamlyOfflineError } from './api/errors/seamly-offline-error'
154
-
155
- // Used by: StyleGuide
156
- export { default as SeamlyUnauthorizedError } from './api/errors/seamly-unauthorized-error'
@@ -51,6 +51,7 @@ export default class Engine {
51
51
 
52
52
  const store = createStore({
53
53
  api: this.api,
54
+ eventBus: this.eventBus,
54
55
  })
55
56
 
56
57
  await store.dispatch(AppActions.initialize(renderConfig))
@@ -1,3 +1,5 @@
1
+ import { randomId } from '../id'
2
+
1
3
  export const SLICE_DELIMITER = '/'
2
4
  export const DOMAIN_DELIMITER = '//'
3
5
 
@@ -10,7 +12,8 @@ export function createAction(
10
12
  identityReducer = (payload) => ({ payload }),
11
13
  ) {
12
14
  const action = (...params) => ({ type, ...identityReducer(...params) })
13
- action.toString = () => type.toString()
15
+ action.toString = () => String(type)
16
+ action.match = (obj) => obj?.type === String(type)
14
17
  return action
15
18
  }
16
19
 
@@ -32,22 +35,51 @@ export function createActions(baseType, ...args) {
32
35
  return handlers.map((handler) => create(...handler))
33
36
  }
34
37
 
35
- export function createThunk(type, thunkCreator) {
36
- const fn = (...args) => {
37
- const thunk = thunkCreator(...args)
38
- thunk.type = type
39
- return thunk
38
+ export function createThunk(type, payloadCreator) {
39
+ const [pending, fulfilled, rejected] = createActions(type, {
40
+ pending: (arg, requestId) => ({
41
+ meta: { arg, requestId, status: 'pending' },
42
+ }),
43
+ fulfilled: (arg, payload, requestId) => ({
44
+ payload,
45
+ meta: { arg, requestId, status: 'fulfilled' },
46
+ }),
47
+ rejected: (arg, error, requestId) => ({
48
+ error,
49
+ meta: { arg, requestId, status: 'rejected', error: String(error) },
50
+ }),
51
+ })
52
+ const thunkCreator = (arg) => (dispatch, getState, extra) => {
53
+ const requestId = randomId()
54
+ const promise = (async () => {
55
+ let finalAction
56
+ try {
57
+ dispatch(pending(arg, requestId))
58
+ const prms = payloadCreator(arg, { dispatch, getState, extra })
59
+ const result = await prms
60
+ finalAction = fulfilled(arg, result, requestId)
61
+ } catch (error) {
62
+ finalAction = rejected(arg, error, requestId)
63
+ }
64
+ dispatch(finalAction)
65
+ return finalAction
66
+ })()
67
+ return Object.assign(promise, {
68
+ type,
69
+ arg,
70
+ requestId,
71
+ })
40
72
  }
41
- fn.toString = () => type
42
- return fn
73
+ return Object.assign(thunkCreator, {
74
+ type,
75
+ pending,
76
+ fulfilled,
77
+ rejected,
78
+ })
43
79
  }
44
80
 
45
81
  export function createReducer(domain, handlers = {}, defaultState) {
46
- const reducer = (state, action) => {
47
- if (state === undefined) {
48
- // eslint-disable-next-line no-param-reassign
49
- state = defaultState
50
- }
82
+ const reducer = (state = defaultState, action) => {
51
83
  const typeReducer = handlers?.[action?.type]
52
84
  return typeReducer ? typeReducer(state, action) : state
53
85
  }
@@ -3,7 +3,12 @@ export default function store(key) {
3
3
 
4
4
  return {
5
5
  get() {
6
- return JSON.parse(sessionStorage.getItem(KEY))
6
+ const candidates = [KEY, KEY.split('.').slice(0, -1).join('.')]
7
+ let val
8
+ do {
9
+ val = sessionStorage.getItem(candidates[0])
10
+ } while (candidates.shift() && !val)
11
+ return JSON.parse(val)
7
12
  },
8
13
 
9
14
  set(value) {
@@ -82,7 +82,7 @@ const StyleGuideApp = ({
82
82
  overlay.addEventListener('click', () => {
83
83
  setStaticState((s) => ({
84
84
  ...s,
85
- visible: 'minimized',
85
+ visibility: { ...s.visibility, visible: 'minimized' },
86
86
  }))
87
87
  })
88
88
  }
@@ -6,6 +6,7 @@ import {
6
6
  StoreProvider,
7
7
  createReduxStore,
8
8
  } from '@seamly/web-ui'
9
+
9
10
  import stateReducer from '../../domains/store/state-reducer'
10
11
  import { Reducer as formReducer } from '../../domains/forms'
11
12
  import { Reducer as translationsReducer } from '../../domains/translations'
@@ -19,6 +20,7 @@ import {
19
20
  Reducer as configReducer,
20
21
  Actions as ConfigActions,
21
22
  } from '../../domains/config'
23
+ import { Reducer as visibilityReducer } from '../../domains/visibility'
22
24
 
23
25
  const bareApi = {
24
26
  send: () => {},
@@ -37,6 +39,7 @@ const SeamlyTestCore = ({ state, translations, children }) => {
37
39
  translations: translationsSlice,
38
40
  interrupt: interruptSlice,
39
41
  config: configSlice,
42
+ visibility: visibilitySlice,
40
43
  ...restState
41
44
  } = state || {}
42
45
  const newStore = createReduxStore({
@@ -48,15 +51,17 @@ const SeamlyTestCore = ({ state, translations, children }) => {
48
51
  [String(translationsReducer)]: translationsReducer,
49
52
  [String(i18nReducer)]: i18nReducer,
50
53
  [String(interruptReducer)]: interruptReducer,
54
+ [String(visibilityReducer)]: visibilityReducer,
51
55
  },
52
56
  initialState: {
53
57
  state: restState,
54
58
  [String(translationsReducer)]: translationsSlice,
55
59
  [String(interruptReducer)]: interruptSlice,
60
+ [String(visibilityReducer)]: visibilitySlice,
56
61
  },
57
62
  })
58
63
  newStore.dispatch(ConfigActions.initialize(configSlice || {}))
59
- newStore.dispatch(I18nActions.setLocaleResolve('en-GB', translations))
64
+ newStore.dispatch(I18nActions.setLocale.fulfilled('en-GB', translations))
60
65
  return newStore
61
66
  }, [state, translations])
62
67
 
@@ -3,7 +3,6 @@ import {
3
3
  randomId,
4
4
  SeamlyOfflineError,
5
5
  SeamlyGeneralError,
6
- SeamlyConfigurationError,
7
6
  } from '@seamly/web-ui'
8
7
  import { addTranslationData } from './state-helpers'
9
8
 
@@ -21,7 +20,9 @@ const baseState = {
21
20
  isLoading: false,
22
21
  idleDetachCountdown: { hasCountdown: false, isActive: false },
23
22
  resumeConversationPrompt: false,
24
- visible: visibilityStates.open,
23
+ visibility: {
24
+ visibility: visibilityStates.open,
25
+ },
25
26
  serviceInfo: {
26
27
  activeServiceSessionId: '',
27
28
  },
@@ -953,13 +954,37 @@ const standardState = {
953
954
  headingText: 'System messages',
954
955
  description: '',
955
956
  ...baseState,
957
+ config: {
958
+ overrideMessages: {
959
+ timeIndicator: {
960
+ enabled: true,
961
+ threshold: 3600000,
962
+ },
963
+ },
964
+ },
956
965
  participantInfo,
957
966
  events: [
967
+ {
968
+ type: 'message',
969
+ payload: {
970
+ ...shortTextMessage.payload,
971
+ occurredAt: (Date.now() - 86400000 * 5) * 1000,
972
+ id: randomId(),
973
+ body: {
974
+ text: 'Long ago when a dialog started',
975
+ type: 'text',
976
+ variables: {},
977
+ },
978
+ },
979
+ },
958
980
  participantMessage,
959
981
  participantMessageDefaultIcon,
960
982
  newTopicDivider,
961
- infoMessage,
962
983
  transcriptInfoMessage,
984
+ ...[newTranslationDividerStart, newTranslationDividerStop].map(
985
+ addTranslationData,
986
+ ),
987
+ infoMessage,
963
988
  ],
964
989
  },
965
990
  choicePromptMessages: {
@@ -1142,34 +1167,24 @@ const standardState = {
1142
1167
  },
1143
1168
  ],
1144
1169
  },
1145
- disconnectedInterrupt: {
1146
- // Important: This cannot pick up the language files so the text is hard set here.
1147
- category: categoryKeys.errors,
1148
- headingText: 'Disconnected interrupt',
1149
- description: '',
1150
- ...baseState,
1151
- interrupt: {
1152
- error: new SeamlyOfflineError(),
1153
- },
1154
- },
1155
- generalErrorInterrupt: {
1170
+ errorWithAction: {
1156
1171
  // Important: This cannot pick up the language files so the text is hard set here.
1157
1172
  category: categoryKeys.errors,
1158
- headingText: 'General error interrupt',
1173
+ headingText: 'Error with a user action',
1159
1174
  description: '',
1160
1175
  ...baseState,
1161
1176
  interrupt: {
1162
1177
  error: new SeamlyGeneralError(),
1163
1178
  },
1164
1179
  },
1165
- configErrorInterrupt: {
1180
+ errorWithoutAction: {
1166
1181
  // Important: This cannot pick up the language files so the text is hard set here.
1167
1182
  category: categoryKeys.errors,
1168
- headingText: 'Config error interrupt',
1183
+ headingText: 'Error without a user action',
1169
1184
  description: '',
1170
1185
  ...baseState,
1171
1186
  interrupt: {
1172
- error: new SeamlyConfigurationError(),
1187
+ error: new SeamlyOfflineError(),
1173
1188
  },
1174
1189
  },
1175
1190
  privacyDisclaimer: {
@@ -1694,6 +1709,7 @@ const standardState = {
1694
1709
  activeServiceSessionId: '3942159e-9878-469e-9120-f44fd6be0f35',
1695
1710
  },
1696
1711
  events: [
1712
+ newTranslationDividerStart,
1697
1713
  infoMessage,
1698
1714
  shortTextMessage,
1699
1715
  {
@@ -1722,6 +1738,8 @@ const standardState = {
1722
1738
  textMesageWithBullets,
1723
1739
  choicePromptMessage,
1724
1740
  ctaMessage,
1741
+ newTranslationDividerStop,
1742
+ newTranslationDividerStart,
1725
1743
  ].map(addTranslationData),
1726
1744
  translations: {
1727
1745
  ...translationsSlice,
@@ -1786,7 +1804,10 @@ export const getStateObj = (layoutModes, customComponentEventBodies) => ({
1786
1804
  window: {
1787
1805
  ...baseState,
1788
1806
  config: { ...baseState.config, layoutMode: 'window' },
1789
- visible: visibilityStates.minimized,
1807
+ visibility: {
1808
+ ...baseState.visibility,
1809
+ visibility: visibilityStates.minimized,
1810
+ },
1790
1811
  },
1791
1812
  },
1792
1813
  minimizedStarted: {
@@ -1796,7 +1817,10 @@ export const getStateObj = (layoutModes, customComponentEventBodies) => ({
1796
1817
  window: {
1797
1818
  ...baseState,
1798
1819
  config: { ...baseState.config, layoutMode: 'window' },
1799
- visible: visibilityStates.minimized,
1820
+ visibility: {
1821
+ ...baseState.visibility,
1822
+ visibility: visibilityStates.minimized,
1823
+ },
1800
1824
  participantInfo,
1801
1825
  headerTitles,
1802
1826
  },
@@ -1808,7 +1832,10 @@ export const getStateObj = (layoutModes, customComponentEventBodies) => ({
1808
1832
  window: {
1809
1833
  ...baseState,
1810
1834
  config: { ...baseState.config, layoutMode: 'window' },
1811
- visible: visibilityStates.minimized,
1835
+ visibility: {
1836
+ ...baseState.visibility,
1837
+ visibility: visibilityStates.minimized,
1838
+ },
1812
1839
  participantInfo,
1813
1840
  headerTitles,
1814
1841
  unreadEvents: 12,
@@ -19,9 +19,11 @@ class SeamlyStyleGuideInstance extends Engine {
19
19
  config: this.config.api,
20
20
  })
21
21
  api.URLS = {
22
- translations: '/client/wilson/translations/{version}/{locale}.json',
22
+ translations: `/client/${this.config.api.key}/translations/{version}/{locale}.json`,
23
23
  }
24
- const translations = await api.getTranslations('en-GB')
24
+ const translations = await api.getTranslations(
25
+ this.config.context.locale || 'en-GB',
26
+ )
25
27
 
26
28
  const renderConfig = {
27
29
  ...this.config,
@@ -3,7 +3,6 @@ import { className } from '../../../lib/css'
3
3
  import { useI18n } from '../../../domains/i18n'
4
4
  import {
5
5
  useSeamlyIsLoading,
6
- useSeamlyVisibility,
7
6
  useSkiplink,
8
7
  useSkiplinkTargetFocusing,
9
8
  } from '../../hooks/seamly-hooks'
@@ -12,13 +11,14 @@ import Event from './event/event'
12
11
  import Loader from './loader'
13
12
  import ComponentFilter from './component-filter'
14
13
  import PrivacyDisclaimer from '../layout/privacy-disclaimer'
14
+ import { useVisibility } from '../../../domains/visibility'
15
15
 
16
16
  const Conversation = () => {
17
17
  const { t } = useI18n()
18
18
  const appBodyContainer = useRef(null)
19
19
  const events = useEvents()
20
20
  const isLoading = useSeamlyIsLoading()
21
- const { isOpen } = useSeamlyVisibility()
21
+ const { isOpen } = useVisibility()
22
22
  const skiplinkTargetId = useSkiplink()
23
23
  const focusSkiplinkTarget = useSkiplinkTargetFocusing()
24
24
 
@@ -1,4 +1,3 @@
1
- import { useMemo } from 'preact/hooks'
2
1
  import Mustache from 'mustache'
3
2
 
4
3
  Mustache.escape = function (escapeText) {
@@ -11,7 +10,7 @@ const parseLinkVariable = (variable) => {
11
10
  }>${variable.name}</a>`
12
11
  }
13
12
 
14
- export function parseRichtText(text, variables = {}) {
13
+ export function parseRichText(text, variables = {}) {
15
14
  const view = {}
16
15
  Object.entries(variables).forEach(([key, variable]) => {
17
16
  switch (variable.type) {
@@ -34,9 +33,3 @@ export function parseRichtText(text, variables = {}) {
34
33
 
35
34
  return output
36
35
  }
37
-
38
- const useTextRendering = ({ text, variables = {} }) => {
39
- return useMemo(() => parseRichtText(text, variables), [text, variables])
40
- }
41
-
42
- export default useTextRendering
@@ -1,29 +1,35 @@
1
+ import { useMemo } from 'preact/hooks'
1
2
  import parseBody from '../../../../lib/parse-body'
2
3
  import useEventLinkClickHandler from './hooks/use-event-link-click-handler'
3
- import useTextRendering from './hooks/use-text-rendering'
4
+ import { parseRichText } from './hooks/use-text-rendering'
4
5
  import MessageContainer from '../message-container'
5
6
  import { useTranslatedEventData } from '../../../../domains/translations'
6
7
 
7
8
  const Text = ({ event, ...props }) => {
8
- const { payload } = event
9
-
10
9
  const [body] = useTranslatedEventData(event)
11
- const eventClick = useEventLinkClickHandler(payload.id)
12
- const eventBody = useTextRendering({
13
- text: parseBody(body.text),
14
- variables: body.variables,
15
- })
10
+ const eventClick = useEventLinkClickHandler(event.payload.id)
11
+
12
+ const containerProps = useMemo(() => {
13
+ if (!event.payload.fromClient) {
14
+ return {
15
+ bodyProps: {
16
+ dangerouslySetInnerHTML: {
17
+ __html: parseRichText(parseBody(body.text), body.variables),
18
+ },
19
+ },
20
+ }
21
+ }
22
+ return {
23
+ children: <p>{body.text}</p>,
24
+ }
25
+ }, [body, event])
16
26
  return (
17
27
  <MessageContainer
18
28
  type="text"
19
29
  event={event}
20
30
  onClick={eventClick}
21
31
  {...props}
22
- bodyProps={{
23
- dangerouslySetInnerHTML: {
24
- __html: eventBody,
25
- },
26
- }}
32
+ {...containerProps}
27
33
  />
28
34
  )
29
35
  }
@@ -1,3 +1,4 @@
1
+ import { useErrorBoundary } from 'preact/hooks'
1
2
  import SeamlyInstanceFunctionsLoader from './seamly-instance-functions-loader'
2
3
  import SeamlyReadState from './seamly-read-state'
3
4
  import SeamlyNewNotifications from './seamly-new-notifications'
@@ -9,8 +10,10 @@ import SeamlyEventSubscriber from './seamly-event-subscriber'
9
10
  import SeamlyFileUpload from './seamly-file-upload'
10
11
  import { SeamlyEventBusContext, SeamlyApiContext } from './seamly-api-context'
11
12
  import { StoreProvider } from '../../../domains/redux'
13
+ import { catchError } from '../../../domains/errors'
12
14
 
13
15
  const SeamlyCore = ({ store, children, config, eventBus, api }) => {
16
+ useErrorBoundary((error) => store.dispatch(catchError(error)))
14
17
  return (
15
18
  <StoreProvider store={store}>
16
19
  <SeamlyEventBusContext.Provider value={eventBus}>
@@ -179,15 +179,15 @@ const SeamlyEventSubscriber = ({ eventBus }) => {
179
179
  switch (payload.type) {
180
180
  case 'find_conversation_erred':
181
181
  dispatch(
182
- InterruptActions.set(new SeamlySessionExpiredError()),
182
+ InterruptActions.set(new SeamlySessionExpiredError(event)),
183
183
  )
184
184
  break
185
185
  case 'seamly_offline':
186
- dispatch(InterruptActions.set(new SeamlyOfflineError()))
186
+ dispatch(InterruptActions.set(new SeamlyOfflineError(event)))
187
187
  dispatch({ type: CLEAR_EVENTS })
188
188
  break
189
189
  default:
190
- dispatch(InterruptActions.set(new SeamlyGeneralError()))
190
+ dispatch(InterruptActions.set(new SeamlyGeneralError(event)))
191
191
  break
192
192
  }
193
193
  break
@@ -1,22 +1,18 @@
1
1
  import { useEffect, useRef } from 'preact/hooks'
2
- import { visibilityStates } from '../../utils/seamly-utils'
3
- import { useSeamlyOptions, useSeamlyVisibility } from '../../hooks/seamly-hooks'
2
+ import { useSeamlyOptions } from '../../hooks/seamly-hooks'
4
3
  import { useConfig } from '../../../domains/config'
5
4
 
6
5
  const SeamlyInitializer = () => {
7
6
  const { initUserSelectedOptions } = useSeamlyOptions()
8
7
  const seamlyInitialized = useRef(false)
9
- const { setVisibility } = useSeamlyVisibility()
10
8
  const config = useConfig()
11
9
 
12
10
  useEffect(() => {
13
11
  if (config.api && !seamlyInitialized.current) {
14
12
  seamlyInitialized.current = true
15
13
  initUserSelectedOptions()
16
-
17
- setVisibility(visibilityStates.initialize)
18
14
  }
19
- }, [initUserSelectedOptions, config, setVisibility])
15
+ }, [initUserSelectedOptions, config])
20
16
 
21
17
  return null
22
18
  }
@@ -1,18 +1,17 @@
1
1
  import { useContext, useEffect, useRef } from 'preact/hooks'
2
2
  import {
3
3
  useSeamlyCommands,
4
- useSeamlyVisibility,
5
4
  useSeamlyUnreadCount,
6
5
  useSeamlyLayoutMode,
7
6
  useSeamlyConversationUrl,
8
7
  useSeamlyActivityEventHandler,
9
8
  useSeamlyApiContext,
10
9
  } from '../../hooks/seamly-hooks'
11
- import { visibilityStates } from '../../utils/seamly-utils'
12
10
  import { SeamlyEventBusContext } from './seamly-api-context'
13
11
  import { useTranslations } from '../../../domains/translations'
14
12
  import { useInterrupt } from '../../../domains/interrupt'
15
13
  import { useConfig } from '../../../domains/config'
14
+ import { visibilityStates, useVisibility } from '../../../domains/visibility'
16
15
 
17
16
  function useSeamlyInstanceFunction(functionName, fn, deps = []) {
18
17
  const eventBus = useContext(SeamlyEventBusContext)
@@ -34,7 +33,7 @@ function useSeamlyInstanceFunction(functionName, fn, deps = []) {
34
33
  const SeamlyInstanceFunctionsLoader = () => {
35
34
  const config = useConfig()
36
35
  const { sendMessage, sendContext, sendAction } = useSeamlyCommands()
37
- const { setVisibility, visible } = useSeamlyVisibility()
36
+ const { setVisibility, visible } = useVisibility()
38
37
  const currentVisibility = useRef(visible)
39
38
  const eventBus = useContext(SeamlyEventBusContext)
40
39
  const api = useSeamlyApiContext()