@seamly/web-ui 20.8.0-beta.2 → 20.8.0-beta.5
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/build/dist/lib/hooks.js +0 -1
- package/build/dist/lib/hooks.min.js +1 -1
- package/build/dist/lib/index.debug.js +22 -11
- package/build/dist/lib/index.debug.min.js +1 -1
- package/build/dist/lib/index.debug.min.js.LICENSE.txt +4 -0
- package/build/dist/lib/index.js +150 -30
- package/build/dist/lib/index.min.js +1 -1
- package/build/dist/lib/standalone.js +154 -30
- package/build/dist/lib/standalone.min.js +1 -1
- package/build/dist/lib/style-guide.js +56 -58
- package/build/dist/lib/style-guide.min.js +1 -1
- package/package.json +1 -1
- package/src/javascripts/domains/app/actions.ts +9 -3
- package/src/javascripts/domains/config/actions.ts +2 -0
- package/src/javascripts/domains/i18n/actions.ts +24 -14
- package/src/javascripts/domains/i18n/hooks.ts +6 -1
- package/src/javascripts/domains/i18n/i18n.types.ts +3 -1
- package/src/javascripts/domains/i18n/slice.ts +7 -1
- package/src/javascripts/domains/translations/middleware.js +1 -0
- package/src/javascripts/domains/visibility/slice.ts +15 -7
- package/src/javascripts/index.ts +1 -1
- package/src/javascripts/lib/engine/index.js +7 -1
- package/src/javascripts/package/hooks.js +0 -1
- package/src/javascripts/ui/hooks/seamly-hooks.js +0 -3
- package/src/javascripts/ui/hooks/use-seamly-actions.ts +102 -0
- package/src/javascripts/ui/hooks/use-seamly-chat.js +14 -1
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createAsyncThunk } from '@reduxjs/toolkit'
|
|
1
|
+
import { createAsyncThunk, unwrapResult } from '@reduxjs/toolkit'
|
|
2
2
|
import SeamlySessionExpiredError from 'api/errors/seamly-session-expired-error'
|
|
3
3
|
import SeamlyUnavailableError from 'api/errors/seamly-unavailable-error'
|
|
4
4
|
import { Config } from 'config.types'
|
|
@@ -47,7 +47,6 @@ export const initializeApp = createAsyncThunk<
|
|
|
47
47
|
body: { enabled: true, locale },
|
|
48
48
|
})
|
|
49
49
|
}
|
|
50
|
-
dispatch(setLocale(locale))
|
|
51
50
|
return { initialState: undefined, locale, config }
|
|
52
51
|
}
|
|
53
52
|
} catch (e) {
|
|
@@ -81,7 +80,14 @@ export const resetApp = createAsyncThunk<unknown, void, ThunkAPI>(
|
|
|
81
80
|
|
|
82
81
|
dispatch(resetConfig())
|
|
83
82
|
await dispatch(initializeConfig())
|
|
84
|
-
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const { locale } = await dispatch(initializeApp()).unwrap()
|
|
86
|
+
await dispatch(setLocale(locale))
|
|
87
|
+
} catch (rejectedValueOrSerializedError) {
|
|
88
|
+
// nothing to do
|
|
89
|
+
}
|
|
90
|
+
|
|
85
91
|
dispatch(initializeVisibility())
|
|
86
92
|
},
|
|
87
93
|
)
|
|
@@ -16,6 +16,7 @@ export const initializeConfig = createAsyncThunk<any, void, ThunkAPI>(
|
|
|
16
16
|
} = await api.getConfig()
|
|
17
17
|
|
|
18
18
|
const locale = config?.context?.locale || defaultLocale
|
|
19
|
+
const { connectWhenInView } = config
|
|
19
20
|
|
|
20
21
|
return {
|
|
21
22
|
features,
|
|
@@ -25,6 +26,7 @@ export const initializeConfig = createAsyncThunk<any, void, ThunkAPI>(
|
|
|
25
26
|
userParticipant,
|
|
26
27
|
startChatIcon,
|
|
27
28
|
locale,
|
|
29
|
+
connectWhenInView,
|
|
28
30
|
}
|
|
29
31
|
} catch (e) {
|
|
30
32
|
const err = new SeamlyUnavailableError()
|
|
@@ -1,24 +1,34 @@
|
|
|
1
1
|
import { createAsyncThunk } from '@reduxjs/toolkit'
|
|
2
|
-
import {
|
|
2
|
+
import type { I18nState } from 'domains/i18n/i18n.types'
|
|
3
3
|
import { ThunkAPI } from 'domains/redux/redux.types'
|
|
4
4
|
|
|
5
5
|
export const setLocale = createAsyncThunk<
|
|
6
6
|
{
|
|
7
|
-
translations:
|
|
8
|
-
locale:
|
|
7
|
+
translations: I18nState['translations']
|
|
8
|
+
locale: I18nState['locale']
|
|
9
9
|
},
|
|
10
10
|
string,
|
|
11
11
|
ThunkAPI
|
|
12
|
-
>(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
12
|
+
>(
|
|
13
|
+
'setLocale',
|
|
14
|
+
async (locale, { extra: { api } }) => {
|
|
15
|
+
const response = await api.getTranslations(locale)
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
return {
|
|
18
|
+
translations: response,
|
|
19
|
+
locale,
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
condition: (locale, { getState }) => {
|
|
24
|
+
const {
|
|
25
|
+
i18n: { isLoading, locale: stateLocale },
|
|
26
|
+
} = getState()
|
|
19
27
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
28
|
+
if (locale === stateLocale || isLoading) {
|
|
29
|
+
// Already fetched or in progress, don't need to re-fetch
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
)
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
pluralTypeHandler,
|
|
4
4
|
selectTypeHandler,
|
|
5
5
|
} from '@ultraq/icu-message-formatter'
|
|
6
|
+
import { RootState } from 'domains/store'
|
|
6
7
|
import { useCallback } from 'preact/hooks'
|
|
7
8
|
import { useSelector } from 'react-redux'
|
|
8
9
|
import * as Selectors from './selectors'
|
|
@@ -19,16 +20,20 @@ export function useI18n() {
|
|
|
19
20
|
const translations = useSelector(Selectors.selectTranslations)
|
|
20
21
|
const locale = useSelector(Selectors.selectLocale)
|
|
21
22
|
const initialLocale = useSelector(Selectors.selectInitialLocale)
|
|
23
|
+
const isLoading = useSelector((state: RootState) => state.i18n.isLoading)
|
|
24
|
+
|
|
22
25
|
const t = useCallback(
|
|
23
26
|
(key, values = {}) => {
|
|
24
27
|
const translation = translations[key]
|
|
25
28
|
if (!translation) {
|
|
29
|
+
if (isLoading) return null
|
|
30
|
+
|
|
26
31
|
console.warn(`Translation key: ${key} is missing in locale: ${locale}`)
|
|
27
32
|
return null
|
|
28
33
|
}
|
|
29
34
|
return formatter.format(translation, values)
|
|
30
35
|
},
|
|
31
|
-
[translations, locale],
|
|
36
|
+
[translations, locale, isLoading],
|
|
32
37
|
)
|
|
33
38
|
|
|
34
39
|
return {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { operations } from 'schema'
|
|
2
|
+
|
|
1
3
|
export interface I18nState {
|
|
2
|
-
translations:
|
|
4
|
+
translations: operations['getAccountTranslations']['responses']['200']['content']['application/json']['translations']
|
|
3
5
|
isLoading: boolean
|
|
4
6
|
initialLocale?: string
|
|
5
7
|
locale?: string
|
|
@@ -50,9 +50,15 @@ export const i18nSlice = createSlice({
|
|
|
50
50
|
.addCase(initializeConfig.fulfilled, (state, { payload }) => {
|
|
51
51
|
state.initialLocale = payload.locale
|
|
52
52
|
})
|
|
53
|
+
.addCase(setLocale.pending, (state) => {
|
|
54
|
+
state.isLoading = true
|
|
55
|
+
})
|
|
56
|
+
.addCase(setLocale.rejected, (state) => {
|
|
57
|
+
state.isLoading = false
|
|
58
|
+
})
|
|
53
59
|
.addCase(setLocale.fulfilled, (state, { payload }) => {
|
|
60
|
+
state.isLoading = false
|
|
54
61
|
if (!payload?.translations) {
|
|
55
|
-
state.isLoading = false
|
|
56
62
|
return
|
|
57
63
|
}
|
|
58
64
|
|
|
@@ -27,6 +27,7 @@ export default function createI18nMiddleware({ dispatch, getState }) {
|
|
|
27
27
|
case setInitialState.type:
|
|
28
28
|
if (payload?.translation?.enabled) {
|
|
29
29
|
dispatch(enableTranslation(payload.translation.locale))
|
|
30
|
+
dispatch(setLocale(payload.locale))
|
|
30
31
|
}
|
|
31
32
|
break
|
|
32
33
|
case addEvent.type:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
|
2
2
|
import { VisibilityOptions } from 'config.types'
|
|
3
|
+
import { initializeConfig } from 'domains/config/actions'
|
|
3
4
|
import { setVisibility } from 'domains/visibility/actions'
|
|
4
5
|
import { visibilityStates } from 'domains/visibility/constants'
|
|
5
6
|
import type { VisibilityState } from 'domains/visibility/visibility.types'
|
|
@@ -21,14 +22,21 @@ export const visibilitySlice = createSlice({
|
|
|
21
22
|
},
|
|
22
23
|
},
|
|
23
24
|
extraReducers: (builder) => {
|
|
24
|
-
builder
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
builder
|
|
26
|
+
.addCase(
|
|
27
|
+
setVisibility.fulfilled,
|
|
28
|
+
(state, { payload: visibility }: PayloadAction<VisibilityOptions>) => {
|
|
29
|
+
if (visibility) {
|
|
30
|
+
state.visibility = visibility
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
)
|
|
34
|
+
.addCase(initializeConfig.fulfilled, (state, { payload }) => {
|
|
35
|
+
// We only want to (always) show the inlineView when connectWhenInView is disabled
|
|
36
|
+
if (!payload.connectWhenInView) {
|
|
37
|
+
state.showInlineView = !payload.connectWhenInView
|
|
29
38
|
}
|
|
30
|
-
}
|
|
31
|
-
)
|
|
39
|
+
})
|
|
32
40
|
},
|
|
33
41
|
})
|
|
34
42
|
|
package/src/javascripts/index.ts
CHANGED
|
@@ -80,7 +80,6 @@ export {
|
|
|
80
80
|
useGeneratedId,
|
|
81
81
|
useSeamlyChat,
|
|
82
82
|
useSeamlyCommands,
|
|
83
|
-
useSeamlyDispatchContext,
|
|
84
83
|
useSeamlyEventStream,
|
|
85
84
|
useSeamlyIdleDetachCountdown,
|
|
86
85
|
useSeamlyMessageContainerClassNames,
|
|
@@ -90,3 +89,4 @@ export {
|
|
|
90
89
|
export { getUrlParams, getUrlSearchString } from './ui/utils/general-utils'
|
|
91
90
|
// Used by: Client
|
|
92
91
|
export { eventTypes } from './ui/utils/seamly-utils'
|
|
92
|
+
export { useSeamlyActions } from 'ui/hooks/use-seamly-actions'
|
|
@@ -3,6 +3,7 @@ import { initializeApp } from 'domains/app/actions'
|
|
|
3
3
|
|
|
4
4
|
import { initializeConfig } from 'domains/config/actions'
|
|
5
5
|
import { setConfig } from 'domains/config/slice'
|
|
6
|
+
import { setLocale } from 'domains/i18n/actions'
|
|
6
7
|
|
|
7
8
|
import { createStore } from 'domains/store'
|
|
8
9
|
import { initializeVisibility } from 'domains/visibility/actions'
|
|
@@ -65,7 +66,12 @@ export default class Engine {
|
|
|
65
66
|
|
|
66
67
|
store.dispatch(setConfig(renderConfig))
|
|
67
68
|
await store.dispatch(initializeConfig())
|
|
68
|
-
|
|
69
|
+
try {
|
|
70
|
+
const { locale } = await store.dispatch(initializeApp()).unwrap()
|
|
71
|
+
await store.dispatch(setLocale(locale))
|
|
72
|
+
} catch (rejectedValueOrSerializedError) {
|
|
73
|
+
// nothing to do
|
|
74
|
+
}
|
|
69
75
|
store.dispatch(initializeVisibility())
|
|
70
76
|
|
|
71
77
|
if (View) {
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { useEffect } from 'preact/hooks'
|
|
2
|
-
import { useDispatch } from 'react-redux'
|
|
3
|
-
|
|
4
2
|
// Import extracted hooks here for use inside this file
|
|
5
3
|
import { useSeamlyApiContext } from './seamly-api-hooks'
|
|
6
4
|
// Export extracted hooks here,
|
|
@@ -49,7 +47,6 @@ export {
|
|
|
49
47
|
useGeneratedId,
|
|
50
48
|
useStableCallback,
|
|
51
49
|
} from './utility-hooks'
|
|
52
|
-
export { useDispatch as useSeamlyDispatchContext }
|
|
53
50
|
|
|
54
51
|
// This hook isn't used within the core
|
|
55
52
|
// But it is used in implementations
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
addEvent,
|
|
3
|
+
clearEvents,
|
|
4
|
+
setHistory,
|
|
5
|
+
setEventsRead,
|
|
6
|
+
setLoadedImageEventIds,
|
|
7
|
+
ackEvent,
|
|
8
|
+
setIsLoading,
|
|
9
|
+
setParticipant,
|
|
10
|
+
setHeaderTitle,
|
|
11
|
+
setHeaderSubTitle,
|
|
12
|
+
resetHistoryLoadedFlag,
|
|
13
|
+
setActiveService,
|
|
14
|
+
initIdleDetachCountdown,
|
|
15
|
+
decrementIdleDetachCountdownCounter,
|
|
16
|
+
stopIdleDetachCountdownCounter,
|
|
17
|
+
clearIdleDetachCountdown,
|
|
18
|
+
initResumeConversationPrompt,
|
|
19
|
+
clearResumeConversationPrompt,
|
|
20
|
+
setServiceDataItem,
|
|
21
|
+
setFeatures,
|
|
22
|
+
setFeatureEnabledState,
|
|
23
|
+
clearFeatures,
|
|
24
|
+
setInitialState,
|
|
25
|
+
setUserSelectedOptions,
|
|
26
|
+
setUserSelectedOption,
|
|
27
|
+
showOption,
|
|
28
|
+
hideOption,
|
|
29
|
+
setServiceEntryMetadata,
|
|
30
|
+
setBlockAutoEntrySwitch,
|
|
31
|
+
setActiveEntryType,
|
|
32
|
+
setUserEntryType,
|
|
33
|
+
registerUpload,
|
|
34
|
+
setUploadProgress,
|
|
35
|
+
setUploadComplete,
|
|
36
|
+
setUploadError,
|
|
37
|
+
clearAllUploads,
|
|
38
|
+
setSeamlyContainerElement,
|
|
39
|
+
} from 'domains/store/slice'
|
|
40
|
+
import {
|
|
41
|
+
ChannelEvent,
|
|
42
|
+
HistoryResponse,
|
|
43
|
+
ParticipantEvent,
|
|
44
|
+
} from 'domains/store/store.types'
|
|
45
|
+
import { useDispatch } from 'react-redux'
|
|
46
|
+
|
|
47
|
+
export const useSeamlyActions = () => {
|
|
48
|
+
const dispatch = useDispatch()
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
addEvent: (event: ChannelEvent) => dispatch(addEvent(event)),
|
|
52
|
+
clearEvents: () => dispatch(clearEvents()),
|
|
53
|
+
setHistory: (history?: HistoryResponse) => dispatch(setHistory(history)),
|
|
54
|
+
setEventsRead: (payload) => dispatch(setEventsRead(payload)),
|
|
55
|
+
setLoadedImageEventIds: (ids: string[]) =>
|
|
56
|
+
dispatch(setLoadedImageEventIds(ids)),
|
|
57
|
+
ackEvent: (event) => dispatch(ackEvent(event)),
|
|
58
|
+
setIsLoading: (isLoading: boolean) => dispatch(setIsLoading(isLoading)),
|
|
59
|
+
setParticipant: (participant: ParticipantEvent) =>
|
|
60
|
+
dispatch(setParticipant(participant)),
|
|
61
|
+
setHeaderTitle: (title: string) => dispatch(setHeaderTitle(title)),
|
|
62
|
+
setHeaderSubTitle: (title: string) => dispatch(setHeaderSubTitle(title)),
|
|
63
|
+
resetHistoryLoadedFlag: () => dispatch(resetHistoryLoadedFlag()),
|
|
64
|
+
setActiveService: () => dispatch(setActiveService()),
|
|
65
|
+
initIdleDetachCountdown: (payload) =>
|
|
66
|
+
dispatch(initIdleDetachCountdown(payload)),
|
|
67
|
+
decrementIdleDetachCountdownCounter: () =>
|
|
68
|
+
dispatch(decrementIdleDetachCountdownCounter()),
|
|
69
|
+
stopIdleDetachCountdownCounter: () =>
|
|
70
|
+
dispatch(stopIdleDetachCountdownCounter()),
|
|
71
|
+
clearIdleDetachCountdown: () => dispatch(clearIdleDetachCountdown()),
|
|
72
|
+
initResumeConversationPrompt: () =>
|
|
73
|
+
dispatch(initResumeConversationPrompt()),
|
|
74
|
+
clearResumeConversationPrompt: () =>
|
|
75
|
+
dispatch(clearResumeConversationPrompt()),
|
|
76
|
+
setServiceDataItem: (payload) => dispatch(setServiceDataItem(payload)),
|
|
77
|
+
setFeatures: (payload) => dispatch(setFeatures(payload)),
|
|
78
|
+
setFeatureEnabledState: (payload) =>
|
|
79
|
+
dispatch(setFeatureEnabledState(payload)),
|
|
80
|
+
clearFeatures: () => dispatch(clearFeatures()),
|
|
81
|
+
setInitialState: (initialState) => dispatch(setInitialState(initialState)),
|
|
82
|
+
setUserSelectedOptions: (payload) =>
|
|
83
|
+
dispatch(setUserSelectedOptions(payload)),
|
|
84
|
+
setUserSelectedOption: (payload) =>
|
|
85
|
+
dispatch(setUserSelectedOption(payload)),
|
|
86
|
+
showOption: (payload) => dispatch(showOption(payload)),
|
|
87
|
+
hideOption: () => dispatch(hideOption()),
|
|
88
|
+
setServiceEntryMetadata: (payload) =>
|
|
89
|
+
dispatch(setServiceEntryMetadata(payload)),
|
|
90
|
+
setBlockAutoEntrySwitch: (payload) =>
|
|
91
|
+
dispatch(setBlockAutoEntrySwitch(payload)),
|
|
92
|
+
setActiveEntryType: (payload) => dispatch(setActiveEntryType(payload)),
|
|
93
|
+
setUserEntryType: (payload) => dispatch(setUserEntryType(payload)),
|
|
94
|
+
registerUpload: (payload) => dispatch(registerUpload(payload)),
|
|
95
|
+
setUploadProgress: (payload) => dispatch(setUploadProgress(payload)),
|
|
96
|
+
setUploadComplete: (payload) => dispatch(setUploadComplete(payload)),
|
|
97
|
+
setUploadError: (payload) => dispatch(setUploadError(payload)),
|
|
98
|
+
clearAllUploads: () => dispatch(clearAllUploads()),
|
|
99
|
+
setSeamlyContainerElement: (payload) =>
|
|
100
|
+
dispatch(setSeamlyContainerElement(payload)),
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -7,10 +7,11 @@ import { useDispatch, useSelector } from 'react-redux'
|
|
|
7
7
|
import { selectShowInlineView } from '../../domains/visibility/selectors'
|
|
8
8
|
import { useLiveRegion } from './live-region-hooks'
|
|
9
9
|
import { useSeamlyHasConversation } from './seamly-api-hooks'
|
|
10
|
-
import { useSeamlyLayoutMode } from './seamly-state-hooks'
|
|
10
|
+
import { useEvents, useSeamlyLayoutMode } from './seamly-state-hooks'
|
|
11
11
|
import useSeamlyCommands from './use-seamly-commands'
|
|
12
12
|
|
|
13
13
|
const useSeamlyChat = () => {
|
|
14
|
+
const events = useEvents()
|
|
14
15
|
const { t } = useI18n()
|
|
15
16
|
const { isInline, isWindow } = useSeamlyLayoutMode()
|
|
16
17
|
const { isOpen, isVisible, setVisibility } = useVisibility()
|
|
@@ -63,6 +64,18 @@ const useSeamlyChat = () => {
|
|
|
63
64
|
}
|
|
64
65
|
}, [dispatch])
|
|
65
66
|
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (events.length) {
|
|
69
|
+
spinnerTimeout.current = setTimeout(() => {
|
|
70
|
+
dispatch(setIsLoading(false))
|
|
71
|
+
}, 5000)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return () => {
|
|
75
|
+
clearTimeout(spinnerTimeout.current)
|
|
76
|
+
}
|
|
77
|
+
}, [events, dispatch])
|
|
78
|
+
|
|
66
79
|
useEffect(() => {
|
|
67
80
|
// This is needed to reset the ref to allow connect and start to happen again.
|
|
68
81
|
// Mostly due to Interrupt situations and a reset being called.
|