@seamly/web-ui 21.0.7 → 21.0.9

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 (44) hide show
  1. package/build/dist/lib/components.js +224 -231
  2. package/build/dist/lib/components.min.js +1 -1
  3. package/build/dist/lib/hooks.js +116 -16
  4. package/build/dist/lib/hooks.min.js +1 -1
  5. package/build/dist/lib/index.debug.js +50 -39
  6. package/build/dist/lib/index.debug.min.js +1 -1
  7. package/build/dist/lib/index.debug.min.js.LICENSE.txt +5 -1
  8. package/build/dist/lib/index.js +208 -220
  9. package/build/dist/lib/index.min.js +1 -1
  10. package/build/dist/lib/standalone.js +215 -251
  11. package/build/dist/lib/standalone.min.js +1 -1
  12. package/build/dist/lib/style-guide.js +194 -191
  13. package/build/dist/lib/style-guide.min.js +1 -1
  14. package/build/dist/lib/utils.js +192 -189
  15. package/build/dist/lib/utils.min.js +1 -1
  16. package/package.json +1 -1
  17. package/src/javascripts/domains/forms/provider.tsx +1 -1
  18. package/src/javascripts/domains/i18n/slice.ts +2 -0
  19. package/src/javascripts/domains/interrupt/hooks.ts +15 -7
  20. package/src/javascripts/domains/interrupt/middleware.ts +7 -14
  21. package/src/javascripts/domains/interrupt/selectors.ts +4 -0
  22. package/src/javascripts/domains/interrupt/slice.ts +2 -2
  23. package/src/javascripts/domains/translations/components/translation-status.tsx +4 -3
  24. package/src/javascripts/domains/translations/slice.ts +2 -0
  25. package/src/javascripts/ui/components/app-options/index.js +4 -3
  26. package/src/javascripts/ui/components/core/seamly-event-subscriber.ts +9 -13
  27. package/src/javascripts/ui/components/core/seamly-instance-functions-loader.js +5 -5
  28. package/src/javascripts/ui/components/entry/deprecated-toggle-button.js +4 -3
  29. package/src/javascripts/ui/components/faq/faq.js +5 -4
  30. package/src/javascripts/ui/components/layout/agent-info.js +4 -3
  31. package/src/javascripts/ui/components/layout/chat-frame.js +7 -8
  32. package/src/javascripts/ui/components/layout/deprecated-chat-frame.js +7 -8
  33. package/src/javascripts/ui/components/layout/interrupt.js +6 -15
  34. package/src/javascripts/ui/components/layout/pre-chat-messages.js +4 -3
  35. package/src/javascripts/ui/components/suggestions/index.js +5 -4
  36. package/src/javascripts/ui/components/translation-chat-status/index.tsx +4 -3
  37. package/src/javascripts/ui/components/view/app-view.js +1 -2
  38. package/src/javascripts/ui/components/view/deprecated-view.js +1 -2
  39. package/src/javascripts/ui/components/view/inline-view.js +1 -11
  40. package/src/javascripts/ui/components/view/window-view/index.js +1 -9
  41. package/src/javascripts/ui/components/view/window-view/window-open-button.js +4 -3
  42. package/src/javascripts/ui/hooks/{use-seamly-chat.js → use-seamly-chat.ts} +5 -1
  43. package/src/javascripts/ui/hooks/use-session-expired-command.ts +17 -0
  44. package/src/.DS_Store +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seamly/web-ui",
3
- "version": "21.0.7",
3
+ "version": "21.0.9",
4
4
  "main": "build/dist/lib/index.js",
5
5
  "types": "build/src/javascripts/index.d.ts",
6
6
  "module": "",
@@ -88,7 +88,7 @@ export default function FormProvider({
88
88
  (e) => {
89
89
  e.preventDefault()
90
90
  // If the submitter is set to being aria-disabled, block the submit action
91
- const ariaDisabled = e.submitter?.ariaDisabled === 'true'
91
+ const ariaDisabled = e.submitter.getAttribute('aria-disabled') === 'true'
92
92
  setIsSubmitted(!ariaDisabled)
93
93
  if (!ariaDisabled && validationIsValid) {
94
94
  dispatch(setHasResponded(true))
@@ -1,4 +1,5 @@
1
1
  import { createSlice } from '@reduxjs/toolkit'
2
+ import { resetApp } from 'domains/app/actions'
2
3
  import { initializeConfig } from 'domains/config/actions'
3
4
  import { setLocale } from 'domains/i18n/actions'
4
5
  import { I18nState } from 'domains/i18n/i18n.types'
@@ -46,6 +47,7 @@ export const i18nSlice = createSlice({
46
47
  extraReducers: (builder) => {
47
48
  // Add reducers for additional action types here, and handle loading state as needed
48
49
  builder
50
+ .addCase(resetApp.pending, () => initialState)
49
51
  .addCase(initializeConfig.fulfilled, (state, { payload }) => {
50
52
  state.initialLocale = payload.locale
51
53
  })
@@ -1,16 +1,24 @@
1
1
  import { useMemo } from 'preact/hooks'
2
2
  import { useSelector } from 'react-redux'
3
3
  import { useI18n } from 'domains/i18n/hooks'
4
- import * as Selectors from './selectors'
4
+ import { selectError, selectHasError } from './selectors'
5
5
 
6
6
  export function useInterrupt() {
7
7
  const { t } = useI18n()
8
-
9
- const error = useSelector(Selectors.selectError)
10
- const hasInterrupt = Boolean(error)
8
+ const error = useSelector(selectError)
9
+ const hasError = useSelector(selectHasError)
11
10
 
12
11
  const meta = useMemo(() => {
13
- if (!error) return {}
12
+ if (!hasError) {
13
+ return {
14
+ title: undefined,
15
+ message: undefined,
16
+ srText: undefined,
17
+ buttonText: undefined,
18
+ originalError: undefined,
19
+ }
20
+ }
21
+
14
22
  const { langKey, action } = error
15
23
  const title = t(`${langKey}.title`)
16
24
  const message = t(`${langKey}.message`)
@@ -23,7 +31,7 @@ export function useInterrupt() {
23
31
  ...(action && langKey ? { buttonText } : {}),
24
32
  originalError: error,
25
33
  }
26
- }, [t, error])
34
+ }, [hasError, error, t])
27
35
 
28
- return { hasInterrupt, meta, error }
36
+ return { hasError, meta, error }
29
37
  }
@@ -1,27 +1,20 @@
1
- import SeamlyConfigurationError from 'api/errors/seamly-configuration-error'
2
1
  import SeamlyGeneralError from 'api/errors/seamly-general-error'
3
- import SeamlyOfflineError from 'api/errors/seamly-offline-error'
4
- import SeamlySessionExpiredError from 'api/errors/seamly-session-expired-error'
5
- import SeamlyUnauthorizedError from 'api/errors/seamly-unauthorized-error'
6
- import SeamlyUnavailableError from 'api/errors/seamly-unavailable-error'
7
2
  import { setInterrupt } from 'domains/interrupt/slice'
8
3
 
9
4
  const handledErrorTypes = [
10
- SeamlyGeneralError,
11
- SeamlyConfigurationError,
12
- SeamlySessionExpiredError,
13
- SeamlyOfflineError,
14
- SeamlyUnauthorizedError,
15
- SeamlyUnavailableError,
5
+ 'SeamlyGeneralError',
6
+ 'SeamlyConfigurationError',
7
+ 'SeamlySessionExpiredError',
8
+ 'SeamlyOfflineError',
9
+ 'SeamlyUnauthorizedError',
10
+ 'SeamlyUnavailableError',
16
11
  ]
17
12
 
18
13
  export default function createInterruptMiddleware({ api }) {
19
14
  return () => (next) => (action) => {
20
15
  const { payload, type } = action
21
16
  if (type === setInterrupt.type) {
22
- if (
23
- !handledErrorTypes.some((ErrorType) => payload.name === ErrorType.name)
24
- ) {
17
+ if (!handledErrorTypes.includes(payload.name)) {
25
18
  throw new SeamlyGeneralError(payload)
26
19
  } else if (payload.action === 'reset') {
27
20
  // [SMLY-942] We clear the store before a reset to force a new conversation if the page is refreshed before the conversation is reset
@@ -4,3 +4,7 @@ export const selectError = createSelector(
4
4
  ({ interrupt }) => interrupt,
5
5
  ({ error }) => error,
6
6
  )
7
+
8
+ export const selectHasError = createSelector(selectError, (error) =>
9
+ Boolean(error),
10
+ )
@@ -1,5 +1,5 @@
1
1
  import { createSlice, isAnyOf } from '@reduxjs/toolkit'
2
- import { initializeApp, resetApp } from 'domains/app/actions'
2
+ import { initializeApp } from 'domains/app/actions'
3
3
  import { initializeConfig } from 'domains/config/actions'
4
4
  import { setLocale } from 'domains/i18n/actions'
5
5
  import { initializeVisibility, setVisibility } from 'domains/visibility/actions'
@@ -19,7 +19,7 @@ export const interruptSlice = createSlice({
19
19
  },
20
20
  extraReducers: (builder) => {
21
21
  builder
22
- .addCase(resetApp.fulfilled, () => initialState)
22
+ .addCase(initializeConfig.pending, () => initialState)
23
23
  .addMatcher(
24
24
  isAnyOf(
25
25
  initializeApp.rejected,
@@ -1,13 +1,14 @@
1
+ import { useSelector } from 'react-redux'
1
2
  import TranslationChatStatus from 'ui/components/translation-chat-status'
2
3
  import TranslationProposal from 'ui/components/translation-proposal'
3
- import { useInterrupt } from 'domains/interrupt/hooks'
4
+ import { selectHasError } from 'domains/interrupt/selectors'
4
5
  import { useTranslations } from 'domains/translations/hooks'
5
6
 
6
7
  export default function TranslationStatus() {
7
- const { hasInterrupt } = useInterrupt()
8
+ const hasError = useSelector(selectHasError)
8
9
 
9
10
  const { isActive } = useTranslations()
10
- if (hasInterrupt) {
11
+ if (hasError) {
11
12
  return null
12
13
  }
13
14
 
@@ -1,4 +1,5 @@
1
1
  import { PayloadAction, createSlice, nanoid } from '@reduxjs/toolkit'
2
+ import { resetApp } from 'domains/app/actions'
2
3
  import { initializeConfig } from 'domains/config/actions'
3
4
  import { addEvent, setHistory } from 'domains/store/slice'
4
5
  import type { ChannelEvent, HistoryResponse } from 'domains/store/store.types'
@@ -93,6 +94,7 @@ export const translationSlice = createSlice({
93
94
  },
94
95
  extraReducers: (builder) => {
95
96
  builder
97
+ .addCase(resetApp.pending, () => translationsInitialState)
96
98
  .addCase(initializeConfig.fulfilled, (state, { payload }) => {
97
99
  const feature = payload?.features?.translation
98
100
  if (!feature) return
@@ -1,8 +1,9 @@
1
+ import { useSelector } from 'react-redux'
1
2
  import Icon from 'ui/components/layout/icon'
2
3
  import OptionsButton from 'ui/components/options/options-button'
3
4
  import { useSeamlyOptions } from 'ui/hooks/seamly-hooks'
4
5
  import { useI18n } from 'domains/i18n/hooks'
5
- import { useInterrupt } from 'domains/interrupt/hooks'
6
+ import { selectHasError } from 'domains/interrupt/selectors'
6
7
  import TranslationsOptionsButton from 'domains/translations/components/options-button'
7
8
  import {
8
9
  useLocaleNativeName,
@@ -13,14 +14,14 @@ import { className } from 'lib/css'
13
14
  export default function AppOptions() {
14
15
  const { menuOptions, allowOptionSelection } = useSeamlyOptions()
15
16
  const { isAvailable: isTranslationsAvailable } = useTranslations()
16
- const { hasInterrupt } = useInterrupt()
17
+ const hasError = useSelector(selectHasError)
17
18
  const { t, locale } = useI18n()
18
19
  const localeNativeName = useLocaleNativeName(locale)
19
20
 
20
21
  if (
21
22
  (!isTranslationsAvailable &&
22
23
  (!allowOptionSelection || !menuOptions.length)) ||
23
- hasInterrupt
24
+ hasError
24
25
  ) {
25
26
  return null
26
27
  }
@@ -73,7 +73,7 @@ const SeamlyEventSubscriber = () => {
73
73
  channel?.leave()
74
74
  }
75
75
  }
76
- return () => {}
76
+ return () => undefined
77
77
  }, [api, api.connectionInfo, api.conversation])
78
78
 
79
79
  useEffect(() => {
@@ -283,6 +283,10 @@ const SeamlyEventSubscriber = () => {
283
283
 
284
284
  const { channel } = api.conversation
285
285
 
286
+ if (messageChannelRef.current) {
287
+ channel?.off('message', messageChannelRef.current)
288
+ }
289
+
286
290
  messageChannelRef.current = channel.on('message', (payload) => {
287
291
  if (!EMITTABLE_MESSAGE_TYPES.includes(payload.type)) {
288
292
  return payload
@@ -301,13 +305,7 @@ const SeamlyEventSubscriber = () => {
301
305
 
302
306
  return true
303
307
  })
304
-
305
- return () => {
306
- api.conversation.channel?.off('message', messageChannelRef.current)
307
- }
308
308
  }
309
-
310
- return () => undefined
311
309
  }, [api, api.connectionInfo, api.conversation.channel, eventBus])
312
310
 
313
311
  useEffect(() => {
@@ -315,6 +313,10 @@ const SeamlyEventSubscriber = () => {
315
313
  api.conversation.onConnection(({ connected }) => {
316
314
  if (!connected) return false
317
315
 
316
+ if (syncChannelRef.current) {
317
+ api.conversation.channel?.off('sync', syncChannelRef.current)
318
+ }
319
+
318
320
  syncChannelRef.current = api.conversation.channel.on(
319
321
  'sync',
320
322
  (payload) => {
@@ -348,13 +350,7 @@ const SeamlyEventSubscriber = () => {
348
350
 
349
351
  return true
350
352
  })
351
-
352
- return () => {
353
- api.conversation.channel?.off('sync', syncChannelRef.current)
354
- }
355
353
  }
356
-
357
- return () => undefined
358
354
  }, [api, api.connectionInfo, api.conversation.channel, events, dispatch])
359
355
 
360
356
  return null
@@ -1,5 +1,5 @@
1
1
  import { useContext, useEffect, useRef } from 'preact/hooks'
2
- import { useDispatch } from 'react-redux'
2
+ import { useDispatch, useSelector } from 'react-redux'
3
3
  import { userParticipantId } from 'config'
4
4
  import {
5
5
  useSeamlyActivityEventHandler,
@@ -12,7 +12,7 @@ import {
12
12
  import { actionTypes, sourceTypes } from 'ui/utils/seamly-utils'
13
13
  import { useConfig } from 'domains/config/hooks'
14
14
  import { updateConfig } from 'domains/config/slice'
15
- import { useInterrupt } from 'domains/interrupt/hooks'
15
+ import { selectHasError } from 'domains/interrupt/selectors'
16
16
  import { useTranslations } from 'domains/translations/hooks'
17
17
  import { visibilityStates } from 'domains/visibility/constants'
18
18
  import { useVisibility } from 'domains/visibility/hooks'
@@ -47,7 +47,7 @@ const SeamlyInstanceFunctionsLoader = () => {
47
47
  const previousUnreadCount = useRef(null)
48
48
  const previousVisibilityState = useRef(null)
49
49
  const { isInline, isResolving } = useSeamlyLayoutMode()
50
- const { hasInterrupt } = useInterrupt()
50
+ const hasError = useSelector(selectHasError)
51
51
  const currentConversationUrl = useSeamlyConversationUrl()
52
52
  const prevConversationUrl = useRef(null)
53
53
  const onActivityHandler = useSeamlyActivityEventHandler()
@@ -142,7 +142,7 @@ const SeamlyInstanceFunctionsLoader = () => {
142
142
  )
143
143
 
144
144
  useEffect(() => {
145
- if (!isResolving && !hasInterrupt) {
145
+ if (!isResolving && !hasError) {
146
146
  // Check for app reset
147
147
  if (
148
148
  prevConversationUrl.current &&
@@ -178,7 +178,7 @@ const SeamlyInstanceFunctionsLoader = () => {
178
178
  eventBus,
179
179
  isInline,
180
180
  isResolving,
181
- hasInterrupt,
181
+ hasError,
182
182
  currentConversationUrl,
183
183
  ])
184
184
 
@@ -1,4 +1,5 @@
1
1
  import { useLayoutEffect, useRef } from 'preact/hooks'
2
+ import { useSelector } from 'react-redux'
2
3
  import {
3
4
  useFocusIfSeamlyContainedFocus,
4
5
  useGeneratedId,
@@ -8,7 +9,7 @@ import {
8
9
  useSkiplinkTargetFocusing,
9
10
  } from 'ui/hooks/seamly-hooks'
10
11
  import { useI18n } from 'domains/i18n/hooks'
11
- import { useInterrupt } from 'domains/interrupt/hooks'
12
+ import { selectHasError } from 'domains/interrupt/selectors'
12
13
  import { useVisibility } from 'domains/visibility/hooks'
13
14
  import { className } from 'lib/css'
14
15
 
@@ -24,10 +25,10 @@ const DeprecatedToggleButton = ({ onOpenChat }) => {
24
25
  const focusIfContained = useFocusIfSeamlyContainedFocus()
25
26
  const currentAgent = useSeamlyCurrentAgent()
26
27
  const agentSubtitle = useSeamlyHeaderData().subTitle
27
- const { hasInterrupt } = useInterrupt()
28
+ const hasError = useSelector(selectHasError)
28
29
  const { headerCollapseButtonId } = useSeamlyStateContext()
29
30
 
30
- const showAgentInfo = currentAgent && !hasInterrupt
31
+ const showAgentInfo = currentAgent && !hasError
31
32
 
32
33
  useLayoutEffect(() => {
33
34
  // Because we can close the app from the external API we
@@ -1,4 +1,5 @@
1
1
  import { useEffect, useMemo, useRef } from 'preact/hooks'
2
+ import { useSelector } from 'react-redux'
2
3
  import Icon from 'ui/components/layout/icon'
3
4
  import InOutTransition, {
4
5
  transitionStartStates,
@@ -17,7 +18,7 @@ import { runIfElementContainsOrHasFocus } from 'ui/utils/general-utils'
17
18
  import { actionTypes } from 'ui/utils/seamly-utils'
18
19
  import { useUserHasResponded } from 'domains/app/hooks'
19
20
  import { useI18n } from 'domains/i18n/hooks'
20
- import { useInterrupt } from 'domains/interrupt/hooks'
21
+ import { selectHasError } from 'domains/interrupt/selectors'
21
22
  import { useTranslatedEventData } from 'domains/translations/hooks'
22
23
  import { className } from 'lib/css'
23
24
 
@@ -27,7 +28,7 @@ const Faq = () => {
27
28
  const sectionId = useGeneratedId()
28
29
  const focusSkiplinkTarget = useSkiplinkTargetFocusing()
29
30
  const { sendPolite } = useLiveRegion()
30
- const { hasInterrupt } = useInterrupt()
31
+ const hasError = useSelector(selectHasError)
31
32
  const { hasCountdown, endCountdown } = useSeamlyIdleDetachCountdown()
32
33
  const { hasPrompt, continueChat } = useSeamlyResumeConversationPrompt()
33
34
 
@@ -36,7 +37,7 @@ const Faq = () => {
36
37
  payload: lastFaqEventPayload,
37
38
  })
38
39
  const faqs = useMemo(() => {
39
- const newFaqs = lastFaqEventPayload && !hasInterrupt ? eventBody : []
40
+ const newFaqs = lastFaqEventPayload && !hasError ? eventBody : []
40
41
  const itemBaseClass = `faqs__item`
41
42
  return newFaqs.map(({ categories = [], ...faqRest }) => ({
42
43
  ...faqRest,
@@ -51,7 +52,7 @@ const Faq = () => {
51
52
  ),
52
53
  ],
53
54
  }))
54
- }, [lastFaqEventPayload, hasInterrupt, eventBody])
55
+ }, [lastFaqEventPayload, hasError, eventBody])
55
56
 
56
57
  const prevFaqs = useRef(null)
57
58
  const prevHasFaqs = useRef(false)
@@ -1,3 +1,4 @@
1
+ import { useSelector } from 'react-redux'
1
2
  import {
2
3
  useSeamlyCurrentAgent,
3
4
  useSeamlyHeaderData,
@@ -5,7 +6,7 @@ import {
5
6
  } from 'ui/hooks/seamly-hooks'
6
7
  import { useStartChatIcon } from 'domains/config/hooks'
7
8
  import { useI18n } from 'domains/i18n/hooks'
8
- import { useInterrupt } from 'domains/interrupt/hooks'
9
+ import { selectHasError } from 'domains/interrupt/selectors'
9
10
  import { useVisibility } from 'domains/visibility/hooks'
10
11
  import { className } from 'lib/css'
11
12
  import Icon from './icon'
@@ -16,10 +17,10 @@ const AgentInfo = () => {
16
17
  const unreadMessageCount = useSeamlyUnreadCount()
17
18
  const { isOpen } = useVisibility()
18
19
  const currentAgent = useSeamlyCurrentAgent()
19
- const { hasInterrupt } = useInterrupt()
20
+ const hasError = useSelector(selectHasError)
20
21
  const startChatIcon = useStartChatIcon()
21
22
  const src = currentAgent?.avatar ?? startChatIcon
22
- const displaySubtitle = hasInterrupt ? '' : subTitle
23
+ const displaySubtitle = hasError ? '' : subTitle
23
24
 
24
25
  const classNames = ['message-count']
25
26
 
@@ -1,21 +1,20 @@
1
+ import { useSelector } from 'react-redux'
1
2
  import AppOptions from 'ui/components/app-options'
2
3
  import ChatScrollProvider from 'ui/components/conversation/event/chat-scroll/chat-scroll-provider'
3
4
  import EntryContainer from 'ui/components/entry/entry-container'
4
5
  import CollapseButton from 'ui/components/view/window-view/collapse-button'
5
- import { useInterrupt } from 'domains/interrupt/hooks'
6
+ import { selectHasError } from 'domains/interrupt/selectors'
6
7
  import TranslationStatus from 'domains/translations/components/translation-status'
7
8
  import { useVisibility } from 'domains/visibility/hooks'
8
9
  import { className } from 'lib/css'
10
+ import Interrupt from './interrupt'
9
11
 
10
- function ChatFrame({ children, interruptComponent: InterruptComponent }) {
11
- const { hasInterrupt, meta } = useInterrupt()
12
+ function ChatFrame({ children }) {
13
+ const hasError = useSelector(selectHasError)
12
14
  const { isOpen } = useVisibility()
13
15
 
14
- if (hasInterrupt) {
15
- if (isOpen) {
16
- return <InterruptComponent {...meta} />
17
- }
18
- return null
16
+ if (hasError) {
17
+ return <Interrupt />
19
18
  }
20
19
 
21
20
  return (
@@ -1,20 +1,19 @@
1
+ import { useSelector } from 'react-redux'
1
2
  import AppOptions from 'ui/components/app-options'
2
3
  import ChatScrollProvider from 'ui/components/conversation/event/chat-scroll/chat-scroll-provider'
3
4
  import EntryContainer from 'ui/components/entry/entry-container'
4
- import { useInterrupt } from 'domains/interrupt/hooks'
5
+ import { selectHasError } from 'domains/interrupt/selectors'
5
6
  import TranslationStatus from 'domains/translations/components/translation-status'
6
7
  import { useVisibility } from 'domains/visibility/hooks'
7
8
  import { className } from 'lib/css'
9
+ import Interrupt from './interrupt'
8
10
 
9
- function ChatFrame({ children, interruptComponent: InterruptComponent }) {
10
- const { hasInterrupt, meta } = useInterrupt()
11
+ function ChatFrame({ children }) {
11
12
  const { isOpen } = useVisibility()
13
+ const hasError = useSelector(selectHasError)
12
14
 
13
- if (hasInterrupt) {
14
- if (isOpen) {
15
- return <InterruptComponent {...meta} />
16
- }
17
- return null
15
+ if (hasError) {
16
+ return <Interrupt />
18
17
  }
19
18
 
20
19
  return (
@@ -6,27 +6,18 @@ import {
6
6
  useSeamlyCommands,
7
7
  useSkiplinkTargetFocusing,
8
8
  } from 'ui/hooks/seamly-hooks'
9
+ import { useInterrupt } from 'domains/interrupt/hooks'
9
10
  import { className } from 'lib/css'
10
11
 
11
- const Interrupt = ({
12
- originalError,
13
- title,
14
- message,
15
- buttonText,
16
- action,
17
- srText,
18
- }) => {
12
+ const Interrupt = () => {
13
+ const {
14
+ meta: { originalError, title, message, buttonText, action, srText },
15
+ } = useInterrupt()
19
16
  const seamlyCommands = useSeamlyCommands()
20
17
  const headingId = useGeneratedId()
21
18
  const { sendPolite } = useLiveRegion()
22
19
  const focusSkiplinkTarget = useSkiplinkTargetFocusing()
23
- const isExpiredError = originalError.name === 'SeamlySessionExpiredError'
24
-
25
- useEffect(() => {
26
- if (isExpiredError && seamlyCommands[action]) {
27
- seamlyCommands[action]()
28
- }
29
- }, [action, seamlyCommands, isExpiredError])
20
+ const isExpiredError = originalError?.name === 'SeamlySessionExpiredError'
30
21
 
31
22
  useEffect(() => {
32
23
  if (!isExpiredError && srText) {
@@ -1,6 +1,7 @@
1
+ import { useSelector } from 'react-redux'
1
2
  import useEventComponentMapping from 'ui/hooks/use-event-component-mapping'
2
3
  import { useConfig } from 'domains/config/hooks'
3
- import { useInterrupt } from 'domains/interrupt/hooks'
4
+ import { selectHasError } from 'domains/interrupt/selectors'
4
5
  import { useVisibility } from 'domains/visibility/hooks'
5
6
  import { className } from 'lib/css'
6
7
 
@@ -11,10 +12,10 @@ export function PreChatMessageEvent({ event }) {
11
12
 
12
13
  export default function PreChatMessages() {
13
14
  const { preChatEvents, layoutMode } = useConfig()
14
- const { hasInterrupt } = useInterrupt()
15
+ const hasError = useSelector(selectHasError)
15
16
  const { isOpen } = useVisibility()
16
17
 
17
- const isVisible = !(hasInterrupt || !preChatEvents?.length || isOpen)
18
+ const isVisible = !(hasError || !preChatEvents?.length || isOpen)
18
19
 
19
20
  return (
20
21
  isVisible && (
@@ -1,4 +1,5 @@
1
1
  import { useCallback, useEffect, useMemo, useRef } from 'preact/hooks'
2
+ import { useSelector } from 'react-redux'
2
3
  import SuggestionsList from 'ui/components/suggestions/suggestions-list'
3
4
  import InOutTransition, {
4
5
  transitionStartStates,
@@ -18,7 +19,7 @@ import { actionTypes } from 'ui/utils/seamly-utils'
18
19
  import { useUserHasResponded } from 'domains/app/hooks'
19
20
  import { useConfig } from 'domains/config/hooks'
20
21
  import { useI18n } from 'domains/i18n/hooks'
21
- import { useInterrupt } from 'domains/interrupt/hooks'
22
+ import { selectHasError } from 'domains/interrupt/selectors'
22
23
  import { useTranslatedEventData } from 'domains/translations/hooks'
23
24
  import { visibilityStates } from 'domains/visibility/constants'
24
25
  import { useVisibility } from 'domains/visibility/hooks'
@@ -37,7 +38,7 @@ const Suggestions = ({ isAside = false }) => {
37
38
  const containerRef = useRef(null)
38
39
  const { sendPolite } = useLiveRegion()
39
40
  // interrupt & countdown hooks
40
- const { hasInterrupt } = useInterrupt()
41
+ const hasError = useSelector(selectHasError)
41
42
  const { hasCountdown, endCountdown } = useSeamlyIdleDetachCountdown()
42
43
  const { hasPrompt, continueChat } = useSeamlyResumeConversationPrompt()
43
44
  // data hooks
@@ -45,8 +46,8 @@ const Suggestions = ({ isAside = false }) => {
45
46
  const payload = useSeamlyServiceData('suggestion')
46
47
  const { body: eventBody } = useTranslatedEventData({ payload })
47
48
  const suggestions = useMemo(
48
- () => (payload && !hasInterrupt ? eventBody : []),
49
- [payload, hasInterrupt, eventBody],
49
+ () => (payload && !hasError ? eventBody : []),
50
+ [payload, hasError, eventBody],
50
51
  )
51
52
 
52
53
  const prevSuggestions = useRef(null)
@@ -1,8 +1,9 @@
1
1
  import { useCallback } from 'preact/hooks'
2
+ import { useSelector } from 'react-redux'
2
3
  import ChatStatus from 'ui/components/chat-status'
3
4
  import { useSkiplinkTargetFocusing } from 'ui/hooks/focus-helper-hooks'
4
5
  import { useI18n } from 'domains/i18n/hooks'
5
- import { useInterrupt } from 'domains/interrupt/hooks'
6
+ import { selectHasError } from 'domains/interrupt/selectors'
6
7
  import {
7
8
  useLocaleNativeName,
8
9
  useTranslations,
@@ -12,7 +13,7 @@ import {
12
13
  export default function TranslationChatStatus() {
13
14
  const { t } = useI18n()
14
15
  const { id } = useTranslationsContainer()
15
- const { hasInterrupt } = useInterrupt()
16
+ const hasError = useSelector(selectHasError)
16
17
  const { disableTranslations, currentLocale } = useTranslations()
17
18
  const localeNativeName = useLocaleNativeName(currentLocale)
18
19
  const focusSkiplinkTarget = useSkiplinkTargetFocusing()
@@ -24,7 +25,7 @@ export default function TranslationChatStatus() {
24
25
  focusSkiplinkTarget()
25
26
  }, [disableTranslations, focusSkiplinkTarget])
26
27
 
27
- if (hasInterrupt) {
28
+ if (hasError) {
28
29
  return null
29
30
  }
30
31
 
@@ -1,12 +1,11 @@
1
1
  import Conversation from '../conversation/conversation'
2
2
  import Chat from '../layout/chat'
3
3
  import ChatFrame from '../layout/chat-frame'
4
- import Interrupt from '../layout/interrupt'
5
4
 
6
5
  const AppView = () => {
7
6
  return (
8
7
  <Chat>
9
- <ChatFrame interruptComponent={Interrupt}>
8
+ <ChatFrame>
10
9
  <Conversation />
11
10
  </ChatFrame>
12
11
  </Chat>
@@ -5,7 +5,6 @@ import DeprecatedToggleButton from '../entry/deprecated-toggle-button'
5
5
  import AgentInfo from '../layout/agent-info'
6
6
  import DeprecatedAppFrame from '../layout/deprecated-app-frame'
7
7
  import Header from '../layout/header'
8
- import Interrupt from '../layout/interrupt'
9
8
 
10
9
  const ShowInlineView = ({ children }) => {
11
10
  const { showInlineView, containerRef } = useShowInlineView()
@@ -24,7 +23,7 @@ const DeprecatedView = () => {
24
23
  <Header onCloseChat={closeChat}>
25
24
  <AgentInfo />
26
25
  </Header>
27
- <ChatFrame interruptComponent={Interrupt}>
26
+ <ChatFrame>
28
27
  <Conversation />
29
28
  </ChatFrame>
30
29
  </DeprecatedAppFrame>
@@ -1,10 +1,8 @@
1
- import { useInterrupt } from 'domains/interrupt/hooks'
2
1
  import { useShowInlineView, useVisibility } from 'domains/visibility/hooks'
3
2
  import { className } from 'lib/css'
4
3
  import Conversation from '../conversation/conversation'
5
4
  import Chat from '../layout/chat'
6
5
  import ChatFrame from '../layout/chat-frame'
7
- import Interrupt from '../layout/interrupt'
8
6
  import PreChatMessages from '../layout/pre-chat-messages'
9
7
  import Suggestions from '../suggestions'
10
8
  import InOutTransition, {
@@ -15,12 +13,6 @@ const InlineView = () => {
15
13
  const { showInlineView, containerRef } = useShowInlineView()
16
14
 
17
15
  const { isOpen } = useVisibility()
18
- const { hasInterrupt, meta } = useInterrupt()
19
-
20
- if (hasInterrupt && !isOpen) {
21
- return <Interrupt {...meta} />
22
- }
23
-
24
16
  return (
25
17
  <>
26
18
  <InOutTransition
@@ -44,9 +36,7 @@ const InlineView = () => {
44
36
  >
45
37
  <Chat ref={containerRef}>
46
38
  {showInlineView && (
47
- <ChatFrame interruptComponent={Interrupt}>
48
- {isOpen && <Conversation />}
49
- </ChatFrame>
39
+ <ChatFrame>{isOpen && <Conversation />}</ChatFrame>
50
40
  )}
51
41
  </Chat>
52
42
  </InOutTransition>