@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.
- package/package.json +1 -1
- package/src/javascripts/api/errors/seamly-base-error.js +10 -0
- package/src/javascripts/api/errors/seamly-configuration-error.js +4 -6
- package/src/javascripts/api/errors/seamly-general-error.js +4 -6
- package/src/javascripts/api/errors/seamly-offline-error.js +4 -6
- package/src/javascripts/api/errors/seamly-session-expired-error.js +4 -6
- package/src/javascripts/api/errors/seamly-unauthorized-error.js +4 -6
- package/src/javascripts/api/errors/seamly-unavailable-error.js +17 -0
- package/src/javascripts/api/index.js +10 -11
- package/src/javascripts/domains/app/actions.js +33 -24
- package/src/javascripts/domains/app/index.js +2 -1
- package/src/javascripts/domains/config/reducer.js +1 -0
- package/src/javascripts/domains/config/selectors.js +1 -1
- package/src/javascripts/domains/errors/index.js +32 -0
- package/src/javascripts/domains/i18n/actions.js +9 -24
- package/src/javascripts/domains/i18n/reducer.js +15 -3
- package/src/javascripts/domains/i18n/utils.js +2 -7
- package/src/javascripts/domains/interrupt/middleware.js +12 -9
- package/src/javascripts/domains/interrupt/reducer.js +10 -9
- package/src/javascripts/domains/store/index.js +7 -2
- package/src/javascripts/domains/store/state-reducer.js +10 -6
- package/src/javascripts/domains/visibility/actions.js +73 -0
- package/src/javascripts/domains/visibility/constants.js +8 -0
- package/src/javascripts/domains/visibility/hooks.js +24 -0
- package/src/javascripts/domains/visibility/index.js +8 -0
- package/src/javascripts/domains/visibility/reducer.js +19 -0
- package/src/javascripts/domains/visibility/selectors.js +9 -0
- package/src/javascripts/domains/visibility/utils.js +42 -0
- package/src/javascripts/index.js +3 -12
- package/src/javascripts/lib/engine/index.js +1 -0
- package/src/javascripts/lib/redux-helpers/index.js +45 -13
- package/src/javascripts/lib/store/providers/session-storage.js +6 -1
- package/src/javascripts/style-guide/components/app.js +1 -1
- package/src/javascripts/style-guide/components/static-core.js +6 -1
- package/src/javascripts/style-guide/states.js +48 -21
- package/src/javascripts/style-guide/style-guide-engine.js +4 -2
- package/src/javascripts/ui/components/conversation/conversation.js +2 -2
- package/src/javascripts/ui/components/conversation/event/hooks/use-text-rendering.js +1 -8
- package/src/javascripts/ui/components/conversation/event/text.js +19 -13
- package/src/javascripts/ui/components/core/seamly-core.js +3 -0
- package/src/javascripts/ui/components/core/seamly-event-subscriber.js +3 -3
- package/src/javascripts/ui/components/core/seamly-initializer.js +2 -6
- package/src/javascripts/ui/components/core/seamly-instance-functions-loader.js +2 -3
- package/src/javascripts/ui/components/core/seamly-new-notifications.js +2 -2
- package/src/javascripts/ui/components/core/seamly-read-state.js +2 -2
- package/src/javascripts/ui/components/entry/toggle-button.js +2 -2
- package/src/javascripts/ui/components/layout/agent-info.js +2 -2
- package/src/javascripts/ui/components/layout/app-frame.js +2 -3
- package/src/javascripts/ui/components/layout/chat-frame.js +2 -2
- package/src/javascripts/ui/components/layout/modal-wrapper.js +3 -6
- package/src/javascripts/ui/components/layout/view.js +3 -6
- package/src/javascripts/ui/hooks/seamly-hooks.js +0 -2
- package/src/javascripts/ui/hooks/use-seamly-chat.js +3 -5
- package/src/javascripts/ui/hooks/use-seamly-commands.js +7 -29
- package/src/javascripts/ui/hooks/use-seamly-idle-detach-countdown.js +2 -2
- package/src/javascripts/ui/utils/general-utils.js +0 -9
- package/src/javascripts/ui/utils/seamly-utils.js +0 -66
- package/build/dist/lib/components.js +0 -62
- package/build/dist/lib/components.min.js +0 -1
- package/build/dist/lib/config.js +0 -51
- package/build/dist/lib/config.min.js +0 -1
- package/build/dist/lib/contexts.js +0 -53
- package/build/dist/lib/contexts.min.js +0 -1
- package/build/dist/lib/hooks.js +0 -64
- package/build/dist/lib/hooks.min.js +0 -1
- package/build/dist/lib/index.debug.js +0 -3113
- package/build/dist/lib/index.debug.min.js +0 -2
- package/build/dist/lib/index.debug.min.js.LICENSE.txt +0 -1099
- package/build/dist/lib/index.js +0 -25840
- package/build/dist/lib/index.min.js +0 -2
- package/build/dist/lib/index.min.js.LICENSE.txt +0 -14
- package/build/dist/lib/standalone.js +0 -34715
- package/build/dist/lib/standalone.min.js +0 -2
- package/build/dist/lib/standalone.min.js.LICENSE.txt +0 -9
- package/build/dist/lib/storage.js +0 -268
- package/build/dist/lib/storage.min.js +0 -2
- package/build/dist/lib/storage.min.js.LICENSE.txt +0 -1
- package/build/dist/lib/style-guide.js +0 -8039
- package/build/dist/lib/style-guide.min.js +0 -1
- package/build/dist/lib/styles.css +0 -1
- package/build/dist/lib/styles.js +0 -1
- package/build/dist/lib/utils.js +0 -59
- package/build/dist/lib/utils.min.js +0 -1
- package/src/javascripts/ui/hooks/use-seamly-stored-visibility.js +0 -31
- package/src/javascripts/ui/hooks/use-seamly-visibility.js +0 -98
package/package.json
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
constructor(params) {
|
|
3
|
-
super(params)
|
|
1
|
+
import SeamlyBaseError from './seamly-base-error'
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
export default class SeamlyConfigurationError extends SeamlyBaseError {
|
|
4
|
+
constructor(originalError, ...params) {
|
|
5
|
+
super(originalError, ...params)
|
|
8
6
|
|
|
9
7
|
this.name = 'SeamlyConfigurationError'
|
|
10
8
|
this.langKey = 'errors.configError'
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
constructor(params) {
|
|
3
|
-
super(params)
|
|
1
|
+
import SeamlyBaseError from './seamly-base-error'
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
export default class SeamlyGeneralError extends SeamlyBaseError {
|
|
4
|
+
constructor(originalError, ...params) {
|
|
5
|
+
super(originalError, ...params)
|
|
8
6
|
|
|
9
7
|
this.name = 'SeamlyGeneralError'
|
|
10
8
|
this.langKey = 'errors.general'
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
constructor(params) {
|
|
3
|
-
super(params)
|
|
1
|
+
import SeamlyBaseError from './seamly-base-error'
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
export default class SeamlyOfflineError extends SeamlyBaseError {
|
|
4
|
+
constructor(originalError, ...params) {
|
|
5
|
+
super(originalError, ...params)
|
|
8
6
|
|
|
9
7
|
this.name = 'SeamlyOfflineError'
|
|
10
8
|
this.langKey = 'errors.seamlyOffline'
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
constructor(params) {
|
|
3
|
-
super(params)
|
|
1
|
+
import SeamlyBaseError from './seamly-base-error'
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
export default class SeamlySessionExpiredError extends SeamlyBaseError {
|
|
4
|
+
constructor(originalError, ...params) {
|
|
5
|
+
super(originalError, ...params)
|
|
8
6
|
|
|
9
7
|
this.name = 'SeamlySessionExpiredError'
|
|
10
8
|
this.action = 'reset'
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
constructor(params) {
|
|
3
|
-
super(params)
|
|
1
|
+
import SeamlyBaseError from './seamly-base-error'
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
export default class SeamlyUnauthorizedError extends SeamlyBaseError {
|
|
4
|
+
constructor(originalError, ...params) {
|
|
5
|
+
super(originalError, ...params)
|
|
8
6
|
|
|
9
7
|
this.name = 'SeamlyUnauthorizedError'
|
|
10
8
|
this.langKey = 'errors.general'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This error is used to alert the user that there's a problem with the connection
|
|
3
|
+
* when initialising the application because of a connection issue on either the server
|
|
4
|
+
* or the client side.
|
|
5
|
+
*/
|
|
6
|
+
export default class SeamlyUnavailableError extends Error {
|
|
7
|
+
constructor(params) {
|
|
8
|
+
super(params)
|
|
9
|
+
|
|
10
|
+
if (Error.captureStackTrace) {
|
|
11
|
+
Error.captureStackTrace(this, SeamlyUnavailableError)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
this.name = 'SeamlyUnavailableError'
|
|
15
|
+
this.langKey = 'errors.seamlyUnavailable'
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -195,7 +195,6 @@ export class API {
|
|
|
195
195
|
// cookies in CORS requests.
|
|
196
196
|
.withCredentials()
|
|
197
197
|
.send({
|
|
198
|
-
authorizationRequired: true,
|
|
199
198
|
externalId: this.externalId || undefined,
|
|
200
199
|
})
|
|
201
200
|
|
|
@@ -211,7 +210,7 @@ export class API {
|
|
|
211
210
|
return initialState
|
|
212
211
|
} catch (error) {
|
|
213
212
|
if (error.status >= 500) {
|
|
214
|
-
throw new SeamlyGeneralError()
|
|
213
|
+
throw new SeamlyGeneralError(error)
|
|
215
214
|
}
|
|
216
215
|
throw error
|
|
217
216
|
}
|
|
@@ -260,13 +259,13 @@ export class API {
|
|
|
260
259
|
}
|
|
261
260
|
} catch (error) {
|
|
262
261
|
if (error.status === 401) {
|
|
263
|
-
throw new SeamlyUnauthorizedError()
|
|
262
|
+
throw new SeamlyUnauthorizedError(error)
|
|
264
263
|
}
|
|
265
264
|
if (error.status === 404) {
|
|
266
|
-
throw new SeamlySessionExpiredError()
|
|
265
|
+
throw new SeamlySessionExpiredError(error)
|
|
267
266
|
}
|
|
268
267
|
if (error.status >= 500) {
|
|
269
|
-
throw new SeamlyGeneralError()
|
|
268
|
+
throw new SeamlyGeneralError(error)
|
|
270
269
|
}
|
|
271
270
|
throw error
|
|
272
271
|
}
|
|
@@ -353,10 +352,10 @@ export class API {
|
|
|
353
352
|
})
|
|
354
353
|
.catch((error) => {
|
|
355
354
|
if (error.status === 404) {
|
|
356
|
-
throw new SeamlyConfigurationError()
|
|
355
|
+
throw new SeamlyConfigurationError(error)
|
|
357
356
|
}
|
|
358
357
|
if (error.status >= 500) {
|
|
359
|
-
throw new SeamlyGeneralError()
|
|
358
|
+
throw new SeamlyGeneralError(error)
|
|
360
359
|
}
|
|
361
360
|
throw error
|
|
362
361
|
})
|
|
@@ -373,13 +372,13 @@ export class API {
|
|
|
373
372
|
})
|
|
374
373
|
.catch((error) => {
|
|
375
374
|
if (error.status === 401) {
|
|
376
|
-
throw new SeamlyUnauthorizedError()
|
|
375
|
+
throw new SeamlyUnauthorizedError(error)
|
|
377
376
|
}
|
|
378
377
|
if (error.status === 404) {
|
|
379
|
-
throw new SeamlySessionExpiredError()
|
|
378
|
+
throw new SeamlySessionExpiredError(error)
|
|
380
379
|
}
|
|
381
380
|
if (error.status >= 500) {
|
|
382
|
-
throw new SeamlyGeneralError()
|
|
381
|
+
throw new SeamlyGeneralError(error)
|
|
383
382
|
}
|
|
384
383
|
throw error
|
|
385
384
|
})
|
|
@@ -402,7 +401,7 @@ export class API {
|
|
|
402
401
|
return body.translations
|
|
403
402
|
} catch (error) {
|
|
404
403
|
if (error.status >= 500) {
|
|
405
|
-
throw new SeamlyGeneralError()
|
|
404
|
+
throw new SeamlyGeneralError(error)
|
|
406
405
|
}
|
|
407
406
|
throw error
|
|
408
407
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { seamlyActions } from '../../ui/utils/seamly-utils'
|
|
2
2
|
import { Actions as ConfigActions } from '../config'
|
|
3
|
-
import { Actions as InterruptActions } from '../interrupt'
|
|
4
3
|
import { Actions as I18nActions } from '../i18n'
|
|
4
|
+
import * as VisibilityActions from '../visibility/actions'
|
|
5
5
|
import { createAction, createThunk } from './utils'
|
|
6
|
+
import SeamlyUnavailableError from '../../api/errors/seamly-unavailable-error'
|
|
7
|
+
import SeamlySessionExpiredError from '../../api/errors/seamly-session-expired-error'
|
|
6
8
|
|
|
7
9
|
export const setHasResponded = createAction(
|
|
8
10
|
'setHasResponded',
|
|
@@ -11,33 +13,40 @@ export const setHasResponded = createAction(
|
|
|
11
13
|
|
|
12
14
|
export const initialize = createThunk(
|
|
13
15
|
'initialize',
|
|
14
|
-
(config) =>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
dispatch(ConfigActions.initialize(config))
|
|
16
|
+
async (config, { dispatch, extra: { api } }) => {
|
|
17
|
+
dispatch(ConfigActions.initialize(config))
|
|
18
|
+
let locale = config?.context?.locale
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
try {
|
|
21
|
+
const { features, defaultLocale } = await api.getConfig()
|
|
22
|
+
dispatch({ type: seamlyActions.SET_FEATURES, features })
|
|
23
|
+
locale = locale || defaultLocale
|
|
24
|
+
dispatch(I18nActions.setInitialLocale(locale))
|
|
25
|
+
} catch (e) {
|
|
26
|
+
throw new SeamlyUnavailableError()
|
|
27
|
+
}
|
|
21
28
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const initialState = await api.getConversationIntitialState()
|
|
27
|
-
dispatch({ type: seamlyActions.SET_INITIAL_STATE, initialState })
|
|
29
|
+
try {
|
|
30
|
+
if (api.hasConversation()) {
|
|
31
|
+
const initialState = await api.getConversationIntitialState()
|
|
32
|
+
dispatch({ type: seamlyActions.SET_INITIAL_STATE, initialState })
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
locale = initialState.translation?.locale || locale
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
} catch (error) {
|
|
36
|
-
dispatch(InterruptActions.set(error))
|
|
36
|
+
if ('userResponded' in initialState) {
|
|
37
|
+
dispatch(setHasResponded(initialState.userResponded))
|
|
37
38
|
}
|
|
38
|
-
await dispatch(I18nActions.setLocale(locale))
|
|
39
|
-
} catch (error) {
|
|
40
|
-
dispatch(InterruptActions.set(error))
|
|
41
39
|
}
|
|
42
|
-
}
|
|
40
|
+
} catch (e) {
|
|
41
|
+
if (e instanceof SeamlySessionExpiredError) {
|
|
42
|
+
throw e
|
|
43
|
+
}
|
|
44
|
+
throw new SeamlyUnavailableError()
|
|
45
|
+
} finally {
|
|
46
|
+
await dispatch(I18nActions.setLocale(locale))
|
|
47
|
+
dispatch(VisibilityActions.initialize())
|
|
48
|
+
}
|
|
49
|
+
},
|
|
43
50
|
)
|
|
51
|
+
|
|
52
|
+
export const reset = createAction('reset', () => {})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createSelector } from 'reselect'
|
|
2
|
-
import { visibilityStates } from '
|
|
2
|
+
import { visibilityStates } from '../visibility/constants'
|
|
3
3
|
import { selectState } from './utils'
|
|
4
4
|
|
|
5
5
|
export const selectConfig = createSelector(selectState, (config) => {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createDomain } from '../../lib/redux-helpers'
|
|
2
|
+
import { Selectors as ConfigSelectors } from '../config'
|
|
3
|
+
|
|
4
|
+
const { createAction } = createDomain('errors')
|
|
5
|
+
|
|
6
|
+
export const catchError = createAction('catch-error', (error) => ({ error }))
|
|
7
|
+
|
|
8
|
+
export function createMiddleware({ api: seamlyApi }) {
|
|
9
|
+
return ({ getState }) => {
|
|
10
|
+
const handleError = (error) => {
|
|
11
|
+
const { errorCallback, namespace, api, layoutMode } =
|
|
12
|
+
ConfigSelectors.selectConfig(getState())
|
|
13
|
+
errorCallback?.(error, {
|
|
14
|
+
namespace,
|
|
15
|
+
api,
|
|
16
|
+
layoutMode,
|
|
17
|
+
conversationUrl: seamlyApi.getConversationUrl(),
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
return (next) => (action) => {
|
|
21
|
+
try {
|
|
22
|
+
if (action.error) {
|
|
23
|
+
handleError(action.error)
|
|
24
|
+
}
|
|
25
|
+
return next(action)
|
|
26
|
+
} catch (error) {
|
|
27
|
+
handleError(error)
|
|
28
|
+
throw error
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -1,35 +1,20 @@
|
|
|
1
1
|
import createMutex from '../../lib/mutex'
|
|
2
2
|
import { selectLocale } from './selectors'
|
|
3
|
-
import { createAction,
|
|
3
|
+
import { createAction, createThunk } from './utils'
|
|
4
4
|
|
|
5
5
|
export const setInitialLocale = createAction('setInitialLocale', (locale) => ({
|
|
6
6
|
locale,
|
|
7
7
|
}))
|
|
8
8
|
|
|
9
|
-
export const [setLocaleStart, setLocaleResolve, setLocaleReject] =
|
|
10
|
-
createActions('setLocale', {
|
|
11
|
-
start: (locale) => ({ locale }),
|
|
12
|
-
resolve: (locale, translations) => ({ locale, translations }),
|
|
13
|
-
reject: (locale, error) => ({ locale, error }),
|
|
14
|
-
})
|
|
15
|
-
|
|
16
9
|
const mutex = createMutex()
|
|
17
10
|
export const setLocale = createThunk(
|
|
18
11
|
'setLocale',
|
|
19
|
-
(locale) =>
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
try {
|
|
28
|
-
const translations = await api.getTranslations(locale)
|
|
29
|
-
dispatch(setLocaleResolve(locale, translations))
|
|
30
|
-
} catch (error) {
|
|
31
|
-
dispatch(setLocaleReject(locale, error))
|
|
32
|
-
}
|
|
33
|
-
})
|
|
34
|
-
},
|
|
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
|
+
},
|
|
35
20
|
)
|
|
@@ -18,6 +18,12 @@ const defaultState = {
|
|
|
18
18
|
'errors.seamlyOffline.srText':
|
|
19
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
20
|
'errors.seamlyOffline.title': 'Connection issues',
|
|
21
|
+
'errors.seamlyUnavailable.buttonText': 'Try again',
|
|
22
|
+
'errors.seamlyUnavailable.message':
|
|
23
|
+
'The server could not be reached. Try again in a little while.',
|
|
24
|
+
'errors.seamlyUnavailable.srText':
|
|
25
|
+
'The chat server could not be reached. Try again in a little while.',
|
|
26
|
+
'errors.seamlyUnavailable.title': 'Server unavailable',
|
|
21
27
|
},
|
|
22
28
|
isLoading: false,
|
|
23
29
|
initialLocale: undefined,
|
|
@@ -29,11 +35,17 @@ export default createReducer(
|
|
|
29
35
|
...state,
|
|
30
36
|
initialLocale: locale,
|
|
31
37
|
}),
|
|
32
|
-
[Actions.
|
|
38
|
+
[Actions.setLocale.pending]: (state) => ({
|
|
33
39
|
...state,
|
|
34
40
|
isLoading: true,
|
|
35
41
|
}),
|
|
36
|
-
[Actions.
|
|
42
|
+
[Actions.setLocale.fulfilled]: (
|
|
43
|
+
state,
|
|
44
|
+
{ payload: translations, meta: { arg: locale } },
|
|
45
|
+
) => {
|
|
46
|
+
if (!translations) {
|
|
47
|
+
return { ...state, isLoading: false }
|
|
48
|
+
}
|
|
37
49
|
return {
|
|
38
50
|
...state,
|
|
39
51
|
isLoading: false,
|
|
@@ -49,7 +61,7 @@ export default createReducer(
|
|
|
49
61
|
),
|
|
50
62
|
}
|
|
51
63
|
},
|
|
52
|
-
[Actions.
|
|
64
|
+
[Actions.setLocale.rejected]: (state) => ({
|
|
53
65
|
...state,
|
|
54
66
|
isLoading: false,
|
|
55
67
|
}),
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
import { createDomain } from '../../lib/redux-helpers'
|
|
2
2
|
|
|
3
|
-
export const {
|
|
4
|
-
|
|
5
|
-
createActions,
|
|
6
|
-
createThunk,
|
|
7
|
-
createReducer,
|
|
8
|
-
selectState,
|
|
9
|
-
} = createDomain('i18n')
|
|
3
|
+
export const { createAction, createThunk, createReducer, selectState } =
|
|
4
|
+
createDomain('i18n')
|
|
@@ -3,7 +3,7 @@ import SeamlyConfigurationError from '../../api/errors/seamly-configuration-erro
|
|
|
3
3
|
import SeamlySessionExpiredError from '../../api/errors/seamly-session-expired-error'
|
|
4
4
|
import SeamlyOfflineError from '../../api/errors/seamly-offline-error'
|
|
5
5
|
import SeamlyUnauthorizedError from '../../api/errors/seamly-unauthorized-error'
|
|
6
|
-
import
|
|
6
|
+
import SeamlyUnavailableError from '../../api/errors/seamly-unavailable-error'
|
|
7
7
|
|
|
8
8
|
const handledErrorTypes = [
|
|
9
9
|
SeamlyGeneralError,
|
|
@@ -11,17 +11,20 @@ const handledErrorTypes = [
|
|
|
11
11
|
SeamlySessionExpiredError,
|
|
12
12
|
SeamlyOfflineError,
|
|
13
13
|
SeamlyUnauthorizedError,
|
|
14
|
+
SeamlyUnavailableError,
|
|
14
15
|
]
|
|
15
16
|
|
|
16
|
-
export default function createMiddleware() {
|
|
17
|
+
export default function createMiddleware({ api }) {
|
|
17
18
|
return () => (next) => (action) => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
const { error } = action
|
|
20
|
+
if (error) {
|
|
21
|
+
if (!handledErrorTypes.some((ErrorType) => error instanceof ErrorType)) {
|
|
22
|
+
throw error
|
|
23
|
+
} else if (error.action === 'reset') {
|
|
24
|
+
// [SMLY-942] We clear the store before a reset to force a new conversation if the page is refreshed before the conversation is reset
|
|
25
|
+
api.disconnect().then(() => {
|
|
26
|
+
api.clearStore()
|
|
27
|
+
})
|
|
25
28
|
}
|
|
26
29
|
}
|
|
27
30
|
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import { createReducer } from './utils'
|
|
2
2
|
import * as Actions from './actions'
|
|
3
|
+
import { Actions as AppActions } from '../app'
|
|
3
4
|
|
|
4
5
|
const initialState = {
|
|
5
6
|
error: undefined,
|
|
6
7
|
}
|
|
7
8
|
|
|
9
|
+
const handleError = (state, { error }) => ({
|
|
10
|
+
...state,
|
|
11
|
+
error,
|
|
12
|
+
})
|
|
13
|
+
|
|
8
14
|
export default createReducer(
|
|
9
15
|
{
|
|
10
|
-
[Actions.set]:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
[Actions.clear]: () => {
|
|
17
|
-
return initialState
|
|
18
|
-
},
|
|
16
|
+
[Actions.set]: handleError,
|
|
17
|
+
[AppActions.initialize.rejected]: handleError,
|
|
18
|
+
[Actions.clear]: () => initialState,
|
|
19
|
+
[AppActions.reset]: () => initialState,
|
|
19
20
|
},
|
|
20
21
|
initialState,
|
|
21
22
|
)
|
|
@@ -11,14 +11,16 @@ import {
|
|
|
11
11
|
createMiddleware as createI18nMiddleware,
|
|
12
12
|
} from '../translations'
|
|
13
13
|
import { Reducer as i18nReducer } from '../i18n'
|
|
14
|
+
import { Reducer as visibilityReducer } from '../visibility'
|
|
14
15
|
import {
|
|
15
16
|
Reducer as interruptReducer,
|
|
16
17
|
createMiddleware as createInterruptMiddleware,
|
|
17
18
|
} from '../interrupt'
|
|
18
19
|
import { createMiddleware as createOptionsMiddleware } from '../options'
|
|
20
|
+
import { createMiddleware as createErrorsMiddleware } from '../errors'
|
|
19
21
|
import stateReducer from './state-reducer'
|
|
20
22
|
|
|
21
|
-
export function createStore({ initialState, api } = {}) {
|
|
23
|
+
export function createStore({ initialState, api, eventBus } = {}) {
|
|
22
24
|
const store = createReduxStore({
|
|
23
25
|
reducers: {
|
|
24
26
|
state: stateReducer,
|
|
@@ -28,14 +30,17 @@ export function createStore({ initialState, api } = {}) {
|
|
|
28
30
|
[String(translationsReducer)]: translationsReducer,
|
|
29
31
|
[String(i18nReducer)]: i18nReducer,
|
|
30
32
|
[String(interruptReducer)]: interruptReducer,
|
|
33
|
+
[String(visibilityReducer)]: visibilityReducer,
|
|
31
34
|
},
|
|
32
35
|
initialState,
|
|
33
36
|
middlewares: [
|
|
37
|
+
createErrorsMiddleware({ api }),
|
|
34
38
|
thunkMiddleware.withExtraArgument({
|
|
35
39
|
api,
|
|
40
|
+
eventBus,
|
|
36
41
|
}),
|
|
37
42
|
createConfigMiddleware(),
|
|
38
|
-
createInterruptMiddleware(),
|
|
43
|
+
createInterruptMiddleware({ api }),
|
|
39
44
|
createOptionsMiddleware({ api }),
|
|
40
45
|
createI18nMiddleware(),
|
|
41
46
|
],
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
// Legacy state reducer. Do not add new features here but extract/create new reducers as needed
|
|
2
2
|
|
|
3
3
|
import { randomId } from '../../lib/id'
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
seamlyStateReducer,
|
|
7
|
-
visibilityStates,
|
|
8
|
-
} from '../../ui/utils/seamly-utils'
|
|
4
|
+
import { entryTypes, seamlyStateReducer } from '../../ui/utils/seamly-utils'
|
|
5
|
+
import { Actions as AppActions } from '../app'
|
|
9
6
|
|
|
10
7
|
const initialState = {
|
|
11
8
|
events: [],
|
|
@@ -14,7 +11,6 @@ const initialState = {
|
|
|
14
11
|
isLoading: false,
|
|
15
12
|
idleDetachCountdown: { hasCountdown: false, isActive: false },
|
|
16
13
|
resumeConversationPrompt: false,
|
|
17
|
-
visible: visibilityStates.hidden,
|
|
18
14
|
serviceInfo: {
|
|
19
15
|
activeServiceSessionId: '',
|
|
20
16
|
},
|
|
@@ -52,5 +48,13 @@ const initialState = {
|
|
|
52
48
|
}
|
|
53
49
|
|
|
54
50
|
export default function stateReducer(state = initialState, action) {
|
|
51
|
+
if (action.type === String(AppActions.reset)) {
|
|
52
|
+
const { visible } = state
|
|
53
|
+
return {
|
|
54
|
+
...initialState,
|
|
55
|
+
visible,
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
55
59
|
return seamlyStateReducer(state, action)
|
|
56
60
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Selectors as ConfigSelectors } from '../config'
|
|
2
|
+
import * as AppSelectors from '../app/selectors'
|
|
3
|
+
|
|
4
|
+
import { visibilityStates, StoreKey } from './constants'
|
|
5
|
+
import { createAction, createThunk, calculateVisibility } from './utils'
|
|
6
|
+
import * as Selectors from './selectors'
|
|
7
|
+
import { selectState } from '../../ui/hooks/seamly-state-hooks'
|
|
8
|
+
|
|
9
|
+
export const setFromStorage = createAction('setFromStorage', (visibility) => ({
|
|
10
|
+
visibility,
|
|
11
|
+
}))
|
|
12
|
+
|
|
13
|
+
const validVisibilityStates = [
|
|
14
|
+
visibilityStates.open,
|
|
15
|
+
visibilityStates.minimized,
|
|
16
|
+
visibilityStates.hidden,
|
|
17
|
+
]
|
|
18
|
+
export const setVisibility = createThunk(
|
|
19
|
+
'set',
|
|
20
|
+
(requestedVisibility, { getState, extra: { api, eventBus } }) => {
|
|
21
|
+
const state = getState()
|
|
22
|
+
const previousVisibility = Selectors.selectVisibility(state)
|
|
23
|
+
const hasResponded = AppSelectors.selectUserHasResponded(state)
|
|
24
|
+
const hasConversation = api.hasConversation()
|
|
25
|
+
const config = ConfigSelectors.selectConfig(state)
|
|
26
|
+
const { visibilityCallback = calculateVisibility, layoutMode } = config
|
|
27
|
+
const { unreadEvents: unreadMessageCount } = selectState(state)
|
|
28
|
+
|
|
29
|
+
const calculatedVisibility = visibilityCallback({
|
|
30
|
+
hasConversation,
|
|
31
|
+
hasResponded,
|
|
32
|
+
previousVisibility,
|
|
33
|
+
requestedVisibility,
|
|
34
|
+
config,
|
|
35
|
+
})
|
|
36
|
+
if (!validVisibilityStates.includes(calculatedVisibility)) {
|
|
37
|
+
console.error(
|
|
38
|
+
'The visibilityCallback function should return "open", "minimized" or "hidden".',
|
|
39
|
+
)
|
|
40
|
+
return undefined
|
|
41
|
+
}
|
|
42
|
+
if (previousVisibility === calculatedVisibility) {
|
|
43
|
+
return undefined
|
|
44
|
+
}
|
|
45
|
+
// Store the user-requested visibility in order to reinitialize after refresh
|
|
46
|
+
api.store.set(StoreKey, {
|
|
47
|
+
...(api.store.get(StoreKey) || {}),
|
|
48
|
+
[layoutMode]: requestedVisibility,
|
|
49
|
+
})
|
|
50
|
+
if (requestedVisibility) {
|
|
51
|
+
eventBus.emit('ui.visible', requestedVisibility, {
|
|
52
|
+
visibility: requestedVisibility,
|
|
53
|
+
hasConversation,
|
|
54
|
+
hasResponded,
|
|
55
|
+
unreadMessageCount,
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
return calculatedVisibility
|
|
59
|
+
},
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
export const initialize = createThunk(
|
|
63
|
+
'initialize',
|
|
64
|
+
async (locale, { dispatch, getState, extra: { api } }) => {
|
|
65
|
+
// initialize stored visibility
|
|
66
|
+
const { layoutMode } = ConfigSelectors.selectConfig(getState())
|
|
67
|
+
const storedVisibility = api.store.get(StoreKey)?.[layoutMode]
|
|
68
|
+
if (storedVisibility) {
|
|
69
|
+
dispatch(setFromStorage(storedVisibility))
|
|
70
|
+
}
|
|
71
|
+
dispatch(setVisibility(visibilityStates.initialize))
|
|
72
|
+
},
|
|
73
|
+
)
|