@seamly/web-ui 20.8.0-alpha.1 → 20.8.0-beta.2

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 (48) hide show
  1. package/build/dist/lib/index.debug.js +48 -48
  2. package/build/dist/lib/index.debug.min.js +1 -1
  3. package/build/dist/lib/index.debug.min.js.LICENSE.txt +1 -1
  4. package/build/dist/lib/index.js +364 -284
  5. package/build/dist/lib/index.min.js +1 -1
  6. package/build/dist/lib/standalone.js +551 -274
  7. package/build/dist/lib/standalone.min.js +1 -1
  8. package/build/dist/lib/style-guide.js +166 -85
  9. package/build/dist/lib/style-guide.min.js +1 -1
  10. package/build/dist/lib/styles.css +1 -1
  11. package/package.json +1 -1
  12. package/src/javascripts/config.types.ts +1 -0
  13. package/src/javascripts/domains/app/actions.ts +45 -41
  14. package/src/javascripts/domains/config/slice.ts +2 -0
  15. package/src/javascripts/domains/i18n/slice.ts +0 -3
  16. package/src/javascripts/domains/store/slice.ts +23 -15
  17. package/src/javascripts/domains/store/store.types.ts +12 -1
  18. package/src/javascripts/domains/translations/hooks.ts +54 -48
  19. package/src/javascripts/domains/translations/selectors.ts +12 -0
  20. package/src/javascripts/domains/translations/slice.ts +70 -23
  21. package/src/javascripts/domains/translations/translations.types.ts +8 -1
  22. package/src/javascripts/schema.ts +3 -10
  23. package/src/javascripts/style-guide/components/app.js +2 -2
  24. package/src/javascripts/style-guide/states.js +61 -3
  25. package/src/javascripts/ui/components/conversation/conversation.js +7 -3
  26. package/src/javascripts/ui/components/conversation/event/card-message.js +1 -1
  27. package/src/javascripts/ui/components/conversation/event/carousel-message/index.js +1 -1
  28. package/src/javascripts/ui/components/conversation/event/choice-prompt.js +1 -1
  29. package/src/javascripts/ui/components/conversation/event/conversation-suggestions.js +9 -2
  30. package/src/javascripts/ui/components/conversation/event/cta.js +1 -1
  31. package/src/javascripts/ui/components/conversation/event/divider/variants/new-translation.js +39 -3
  32. package/src/javascripts/ui/components/conversation/event/image.js +1 -1
  33. package/src/javascripts/ui/components/conversation/event/participant.js +1 -1
  34. package/src/javascripts/ui/components/conversation/event/splash.js +1 -1
  35. package/src/javascripts/ui/components/conversation/event/text.js +1 -1
  36. package/src/javascripts/ui/components/conversation/event/translation.js +1 -1
  37. package/src/javascripts/ui/components/conversation/event/upload.js +1 -1
  38. package/src/javascripts/ui/components/conversation/event/video.js +1 -1
  39. package/src/javascripts/ui/components/conversation/message-container.js +1 -23
  40. package/src/javascripts/ui/components/core/seamly-event-subscriber.js +3 -2
  41. package/src/javascripts/ui/components/faq/faq.js +3 -1
  42. package/src/javascripts/ui/components/options/options-button.js +3 -1
  43. package/src/javascripts/ui/components/suggestions/index.js +5 -2
  44. package/src/javascripts/ui/hooks/seamly-state-hooks.js +6 -0
  45. package/src/javascripts/ui/hooks/use-seamly-chat.js +3 -9
  46. package/src/stylesheets/5-components/_message-carousel.scss +10 -8
  47. package/CHANGELOG.md +0 -987
  48. package/src/javascripts/domains/translations/selectors.js +0 -11
@@ -1,15 +1,53 @@
1
- import { TranslationState } from 'domains/translations/translations.types'
2
- import { createSlice, nanoid } from '@reduxjs/toolkit'
1
+ import {
2
+ TranslationEvent,
3
+ TranslationState,
4
+ } from 'domains/translations/translations.types'
5
+ import { createSlice, nanoid, PayloadAction } from '@reduxjs/toolkit'
3
6
  import { initializeApp } from 'domains/app/actions'
4
7
  import { initializeConfig } from 'domains/config/actions'
8
+ import { addEvent, orderHistory, setHistory } from 'domains/store/slice'
9
+ import { ChannelEvent } from 'domains/store/store.types'
5
10
 
6
11
  export const translationsInitialState: TranslationState = {
7
12
  isActive: false,
8
13
  currentLocale: undefined,
9
14
  isAvailable: false,
10
15
  languages: [],
11
- originalPayloadIds: [],
12
16
  containerId: nanoid(),
17
+ translatedEventGroups: {},
18
+ }
19
+
20
+ const getLastGroupId = (events: ChannelEvent[], id: string) => {
21
+ const eventGroup = [...events].reduce<Record<string, string[]>>(
22
+ (acc, { payload }, _index, arr) => {
23
+ if (acc[id]) {
24
+ // Splice to break early (make the reducer think we are done)
25
+ // This is needed to avoid events of other groups from being added to the array.
26
+ // @ts-ignore
27
+ if (payload?.type === 'divider' && payload?.body?.translationEnabled) {
28
+ arr.splice(0)
29
+ return acc
30
+ }
31
+
32
+ acc[id].push(payload.id)
33
+ }
34
+
35
+ if (payload.id === id) acc[id] = []
36
+
37
+ return acc
38
+ },
39
+ {},
40
+ )
41
+
42
+ const [[groupId, eventIds]] = Object.entries(eventGroup)
43
+
44
+ const lastGroupId = events
45
+ //@ts-ignore
46
+ .filter((event) => event.payload?.type === 'divider')
47
+ .map((event) => event.payload.id)
48
+ .at(-1)
49
+
50
+ return { lastGroupId, groupId, eventIds }
13
51
  }
14
52
 
15
53
  export const translationSlice = createSlice({
@@ -24,29 +62,31 @@ export const translationSlice = createSlice({
24
62
  state.isActive = false
25
63
  state.currentLocale = undefined
26
64
  },
27
- enableEvent: (state, { payload }) => {
28
- if (!state.originalPayloadIds.includes(payload)) {
29
- return
30
- }
31
- state.originalPayloadIds = state.originalPayloadIds.filter(
32
- (id) => id !== payload,
33
- )
65
+ enableEventsTranslation: (
66
+ state,
67
+ {
68
+ payload: { events, id },
69
+ }: PayloadAction<{ events: ChannelEvent[]; id: string }>,
70
+ ) => {
71
+ delete state.translatedEventGroups[id]
72
+
73
+ const { lastGroupId } = getLastGroupId(events, id)
74
+ state.lastGroupId = lastGroupId
34
75
  },
35
- disableEvent: (state, { payload }) => {
36
- if (state.originalPayloadIds.includes(payload)) {
37
- return
38
- }
39
- state.originalPayloadIds.push(payload)
76
+ disableEventsTranslation: (
77
+ state,
78
+ {
79
+ payload: { events, id },
80
+ }: PayloadAction<{ events: ChannelEvent[]; id: string }>,
81
+ ) => {
82
+ const { lastGroupId, groupId, eventIds } = getLastGroupId(events, id)
83
+
84
+ state.lastGroupId = lastGroupId
85
+ state.translatedEventGroups[groupId] = eventIds
40
86
  },
41
87
  },
42
88
  extraReducers: (builder) => {
43
89
  builder
44
- .addCase(initializeApp.fulfilled, (state, { payload }) => {
45
- if (!payload.config?.context?.translationLocale) return
46
-
47
- state.isActive = true
48
- state.currentLocale = payload.locale
49
- })
50
90
  .addCase(initializeConfig.fulfilled, (state, { payload }) => {
51
91
  const feature = payload?.features?.translation
52
92
  if (!feature) return
@@ -54,14 +94,21 @@ export const translationSlice = createSlice({
54
94
  state.isAvailable = feature.enabled === true
55
95
  state.languages = feature.languages
56
96
  })
97
+ .addCase(addEvent, (state, { payload }) => {
98
+ if (state.translatedEventGroups[state.lastGroupId]) {
99
+ state.translatedEventGroups[state.lastGroupId].push(
100
+ payload.payload.id,
101
+ )
102
+ }
103
+ })
57
104
  },
58
105
  })
59
106
 
60
107
  export const {
61
108
  enableTranslation,
62
109
  disableTranslation,
63
- enableEvent,
64
- disableEvent,
110
+ enableEventsTranslation,
111
+ disableEventsTranslation,
65
112
  } = translationSlice.actions
66
113
 
67
114
  export default translationSlice.reducer
@@ -2,11 +2,18 @@ import { operations } from 'schema'
2
2
 
3
3
  export type Languages =
4
4
  operations['getAccountConfig']['responses']['201']['content']['application/json']['config']['features']['translation']['languages']
5
+
6
+ export type TranslationEvent = {
7
+ id: string
8
+ items: string[]
9
+ }
10
+
5
11
  export interface TranslationState {
6
12
  isActive: boolean
7
13
  currentLocale?: string
8
14
  isAvailable: boolean
9
15
  languages: Languages
10
- originalPayloadIds: string[]
11
16
  containerId: string
17
+ translatedEventGroups: Record<string, string[]>
18
+ lastGroupId?: string
12
19
  }
@@ -406,11 +406,7 @@ export interface components {
406
406
  * @example true
407
407
  */
408
408
  persist?: boolean
409
- /**
410
- * @description Name of the attached service
411
- * @example bot
412
- */
413
- service?: string
409
+ service?: components['schemas']['Service']
414
410
  transactionId?: components['schemas']['TransactionId']
415
411
  /** @description In case a translations was active when this message originated, this object will contain the translated message in the translationLocale locale. */
416
412
  translatedBody?: {
@@ -713,11 +709,6 @@ export interface components {
713
709
  * @example image/jpeg
714
710
  */
715
711
  contentType?: string
716
- /**
717
- * @description Timestamp until when the file is available for download (in microseconds).
718
- * @example 1661165549122131
719
- */
720
- deleteAt?: number
721
712
  /**
722
713
  * @description Filename of the upload
723
714
  * @example dog.jpg
@@ -1453,3 +1444,5 @@ export interface operations {
1453
1444
  }
1454
1445
  }
1455
1446
  }
1447
+
1448
+ export interface external {}
@@ -50,8 +50,7 @@ const StyleGuideApp = ({
50
50
  const { headingText } = mainState[feature]
51
51
  setSelectedStateDescription(headingText)
52
52
  const bareState = mainState[feature][layoutMode]
53
- const { overrideMessages, showFaq } = bareState.config
54
-
53
+ const { overrideMessages, showFaq, showSuggestions } = bareState.config
55
54
  const agent = participants.find(
56
55
  (participant) => participant.id === 'agent',
57
56
  )
@@ -66,6 +65,7 @@ const StyleGuideApp = ({
66
65
  layoutMode: bareState.config.layoutMode,
67
66
  ...(overrideMessages ? { messages: overrideMessages } : {}),
68
67
  showFaq,
68
+ showSuggestions,
69
69
  },
70
70
  headerTitles: {
71
71
  ...bareState.headerTitles,
@@ -14,6 +14,7 @@ const baseState = {
14
14
  hideOnNoUserResponse: false,
15
15
  showFaq: false,
16
16
  showDisclaimer: false,
17
+ showSuggestions: true,
17
18
  },
18
19
  initialState: {},
19
20
  unreadEvents: 0,
@@ -126,6 +127,32 @@ const newTranslationDividerStart = {
126
127
  type: 'divider',
127
128
  },
128
129
  }
130
+
131
+ const newTranslationDividerStartTwo = {
132
+ type: 'info',
133
+ payload: {
134
+ body: {
135
+ language: 'Nederlands',
136
+ subtype: 'new_translation',
137
+ text: '[NL] Automatic translation to Dutch started. Please note that automatic translations may contain errors.',
138
+ translationEnabled: true,
139
+ translationLocale: 'nl',
140
+ },
141
+ fromClient: false,
142
+ fromHistory: true,
143
+ id: randomId(),
144
+ occurredAt: 1625658300534259,
145
+ participant: null,
146
+ service: {
147
+ meta: {},
148
+ name: null,
149
+ serviceSessionId: null,
150
+ },
151
+ transactionId: null,
152
+ translatedBody: null,
153
+ type: 'divider',
154
+ },
155
+ }
129
156
  const newTranslationDividerStop = {
130
157
  type: 'info',
131
158
  payload: {
@@ -736,7 +763,7 @@ const translationsSlice = {
736
763
  currentLocale: undefined,
737
764
  isAvailable: false,
738
765
  languages: [],
739
- originalPayloadIds: [],
766
+ translatedEventGroups: {},
740
767
  containerId: randomId(),
741
768
  }
742
769
 
@@ -1736,7 +1763,7 @@ const standardState = {
1736
1763
  userMessage,
1737
1764
  textMessageBoldItalicUnderline,
1738
1765
  newTopicDivider,
1739
- newTranslationDividerStart,
1766
+ newTranslationDividerStartTwo,
1740
1767
  newTranslationDividerStop,
1741
1768
  imageMessage,
1742
1769
  fileDownloadAgentMessage,
@@ -1752,13 +1779,44 @@ const standardState = {
1752
1779
  choicePromptMessage,
1753
1780
  ctaMessage,
1754
1781
  newTranslationDividerStop,
1755
- newTranslationDividerStart,
1756
1782
  ].map(addTranslationData),
1757
1783
  translations: {
1758
1784
  ...translationsSlice,
1759
1785
  currentLocale: 'nl',
1760
1786
  isActive: true,
1761
1787
  isAvailable: true,
1788
+ translatedEventGroups: {
1789
+ [newTranslationDividerStart.payload.id]: [
1790
+ infoMessage,
1791
+ shortTextMessage,
1792
+ {
1793
+ ...choicePromptMessage,
1794
+ payload: {
1795
+ ...choicePromptMessage.payload,
1796
+ id: `${choicePromptMessage.payload.id}XXX`,
1797
+ },
1798
+ },
1799
+ longTextMessage,
1800
+ userMessage,
1801
+ textMessageBoldItalicUnderline,
1802
+ newTopicDivider,
1803
+ ].map((p) => p.payload.id),
1804
+ [newTranslationDividerStartTwo.payload.id]: [
1805
+ imageMessage,
1806
+ fileDownloadAgentMessage,
1807
+ deletedFileDownloadAgentMessage,
1808
+ userMessageLong,
1809
+ videoMessage,
1810
+ textMessageWithLinks,
1811
+ textMessageWithLongLink,
1812
+ imageMessageWithLightbox,
1813
+ fileDownloadUserMessage,
1814
+ emptyUrlFileDownloadUserMessage,
1815
+ textMesageWithBullets,
1816
+ choicePromptMessage,
1817
+ ctaMessage,
1818
+ ].map((p) => p.payload.id),
1819
+ },
1762
1820
  languages: [
1763
1821
  { locale: 'nl', nativeName: 'Dutch' },
1764
1822
  { locale: 'en', nativeName: 'English' },
@@ -1,8 +1,9 @@
1
1
  import { useI18n } from 'domains/i18n/hooks'
2
2
  import { useVisibility } from 'domains/visibility/hooks'
3
3
  import { className } from 'lib/css'
4
- import { useEffect, useRef } from 'preact/hooks'
4
+ import { useLayoutEffect, useRef } from 'preact/hooks'
5
5
  import PrivacyDisclaimer from 'ui/components/layout/privacy-disclaimer'
6
+ import { timeout } from 'ui/hooks/focus-helper-hooks'
6
7
  import {
7
8
  useSeamlyIsLoading,
8
9
  useSkiplink,
@@ -51,10 +52,13 @@ const Conversation = () => {
51
52
  const focusSkiplinkTarget = useSkiplinkTargetFocusing()
52
53
  const loadedImageEventIds = useLoadedImageEventIds()
53
54
 
54
- useEffect(() => {
55
+ useLayoutEffect(() => {
55
56
  const containerElement = chatBodyContainer.current
56
57
  if (containerElement) {
57
- containerElement.scrollTop = containerElement.scrollHeight
58
+ requestAnimationFrame(async () => {
59
+ await timeout(30) // Wait for next frame tick
60
+ containerElement.scrollTop = containerElement.scrollHeight
61
+ })
58
62
  }
59
63
  }, [events, isLoading, isOpen, loadedImageEventIds])
60
64
 
@@ -4,7 +4,7 @@ import { useTranslatedEventData } from 'domains/translations/hooks'
4
4
  import CardComponent from './card-component'
5
5
 
6
6
  const CardMessage = ({ event }) => {
7
- const [body] = useTranslatedEventData(event)
7
+ const { body } = useTranslatedEventData(event)
8
8
  const descriptionId = useGeneratedId()
9
9
 
10
10
  return (
@@ -6,7 +6,7 @@ import CarouselMessageSlide from './components/slide'
6
6
  const getItemKey = (item, idx, prefix = '') => `${prefix}${item.title}:${idx}`
7
7
  const getItemLabel = (item) => item.title
8
8
  const CarouselMessage = ({ event }) => {
9
- const [body] = useTranslatedEventData(event)
9
+ const { body } = useTranslatedEventData(event)
10
10
  const slides = body.cards
11
11
 
12
12
  return (
@@ -19,7 +19,7 @@ export const useChoicePrompt = (event) => {
19
19
  const { sendAction, addMessageBubble, addDivider } = useSeamlyCommands()
20
20
  const { activeServiceSessionId } = useSeamlyServiceInfo()
21
21
  const lastEventId = useLastMessageEventId()
22
- const [body] = useTranslatedEventData(event)
22
+ const { body } = useTranslatedEventData(event)
23
23
  const { service } = payload
24
24
 
25
25
  const subEvent = useMemo(() => {
@@ -1,5 +1,6 @@
1
1
  import { useUserHasResponded } from 'domains/app/hooks'
2
2
  import { setHasResponded } from 'domains/app/slice'
3
+ import { useConfig } from 'domains/config/hooks'
3
4
  import { useI18n } from 'domains/i18n/hooks'
4
5
  import { useTranslatedEventData } from 'domains/translations/hooks'
5
6
  import { className } from 'lib/css'
@@ -12,7 +13,7 @@ import { actionTypes } from 'ui/utils/seamly-utils'
12
13
 
13
14
  export const useSuggestions = (event) => {
14
15
  const { payload } = event
15
- const [suggestions] = useTranslatedEventData(event)
16
+ const { body: suggestions } = useTranslatedEventData(event)
16
17
 
17
18
  return {
18
19
  suggestions,
@@ -26,6 +27,7 @@ const ConversationSuggestions = ({ event, ...props }) => {
26
27
  const userHasResponded = useUserHasResponded()
27
28
  const { sendAction, addMessageBubble } = useSeamlyCommands()
28
29
  const { suggestions, payload } = useSuggestions(event)
30
+ const { showSuggestions } = useConfig()
29
31
  const events = useEvents()
30
32
 
31
33
  const { t } = useI18n()
@@ -63,7 +65,12 @@ const ConversationSuggestions = ({ event, ...props }) => {
63
65
  [dispatch, sendAction, payload.id, addMessageBubble],
64
66
  )
65
67
 
66
- if (!isExpanded || userHasResponded || !hasLastTransactionEvent) {
68
+ if (
69
+ !isExpanded ||
70
+ userHasResponded ||
71
+ !hasLastTransactionEvent ||
72
+ !showSuggestions
73
+ ) {
67
74
  return null
68
75
  }
69
76
 
@@ -7,7 +7,7 @@ import { useTranslatedEventData } from 'domains/translations/hooks'
7
7
  import useEventLinkClickHandler from './hooks/use-event-link-click-handler'
8
8
 
9
9
  const Cta = ({ event }) => {
10
- const [body] = useTranslatedEventData(event)
10
+ const { body } = useTranslatedEventData(event)
11
11
  const eventClick = useEventLinkClickHandler(event.payload.id)
12
12
  const { emitEvent } = useSeamlyCommands()
13
13
 
@@ -5,17 +5,40 @@ import {
5
5
  useTranslations,
6
6
  useLocaleNativeName,
7
7
  } from 'domains/translations/hooks'
8
+ import { useDispatch, useSelector } from 'react-redux'
9
+ import {
10
+ enableEventsTranslation,
11
+ disableEventsTranslation,
12
+ } from 'domains/translations/slice'
13
+ import { useCallback } from 'preact/hooks'
14
+ import { useEvents } from 'ui/hooks/seamly-hooks'
8
15
 
9
16
  const NewTranslationDivider = ({ event }) => {
10
17
  const { t } = useI18n()
11
- const { translationEnabled, translationLocale, text } = event.payload.body
18
+ const events = useEvents()
19
+ const {
20
+ body: { translationEnabled, translationLocale, text },
21
+ id,
22
+ } = event.payload
12
23
  const { enableTranslations } = useTranslations()
13
24
  const localeNativeName = useLocaleNativeName(translationLocale)
25
+ const translatedEventGroups = useSelector(
26
+ (state) => state.translations.translatedEventGroups,
27
+ )
28
+
29
+ const dispatch = useDispatch()
30
+ const toggleTranslations = useCallback(() => {
31
+ if (!translatedEventGroups[id]) {
32
+ dispatch(disableEventsTranslation({ events, id }))
33
+ return
34
+ }
35
+
36
+ dispatch(enableEventsTranslation({ events, id }))
37
+ }, [dispatch, events, id, translatedEventGroups])
14
38
 
15
39
  const handleRestartButtonclick = () => {
16
40
  enableTranslations(translationLocale)
17
41
  }
18
-
19
42
  return (
20
43
  <EventDivider iconName="newTranslation" dividerType="newtranslation">
21
44
  <p className={className('divider__title')}>
@@ -27,10 +50,23 @@ const NewTranslationDivider = ({ event }) => {
27
50
  )}
28
51
  </p>
29
52
  {translationEnabled ? (
30
- <p>{text}</p>
53
+ <>
54
+ <p>{text}</p>
55
+ <button
56
+ className={className('button', 'button--secondary')}
57
+ onClick={toggleTranslations}
58
+ >
59
+ {t(
60
+ !translatedEventGroups[id]
61
+ ? 'translations.toggle.hideTranslationsButtonText'
62
+ : 'translations.toggle.showTranslationsButtonText',
63
+ )}
64
+ </button>
65
+ </>
31
66
  ) : (
32
67
  <button
33
68
  className={className('button', 'button--secondary')}
69
+ data-testid="restartTranslationButton"
34
70
  onClick={handleRestartButtonclick}
35
71
  >
36
72
  {t('translations.divider.restartButtonText')}
@@ -6,7 +6,7 @@ import MessageContainer from 'ui/components/conversation/message-container'
6
6
  import ImageLightbox from './image-lightbox'
7
7
 
8
8
  const Image = ({ event, descriptorId, ...props }) => {
9
- const [body] = useTranslatedEventData(event)
9
+ const { body } = useTranslatedEventData(event)
10
10
  const { description, url, isZoomable } = body
11
11
  const [showLighbox, setShowLightbox] = useState(false)
12
12
  const dispatch = useDispatch()
@@ -3,7 +3,7 @@ import { useTranslatedEventData } from 'domains/translations/hooks'
3
3
 
4
4
  const Participant = ({ event }) => {
5
5
  const { participant } = event.payload
6
- const [introduction] = useTranslatedEventData(event)
6
+ const { body: introduction } = useTranslatedEventData(event)
7
7
 
8
8
  if (!introduction) {
9
9
  return null
@@ -4,7 +4,7 @@ import MessageContainer from 'ui/components/conversation/message-container'
4
4
 
5
5
  const Splash = ({ event, ...props }) => {
6
6
  const { payload } = event
7
- const [body] = useTranslatedEventData(event)
7
+ const { body } = useTranslatedEventData(event)
8
8
  const eventClick = useEventLinkClickHandler(payload.id)
9
9
 
10
10
  return (
@@ -4,7 +4,7 @@ import { useTranslatedEventData } from 'domains/translations/hooks'
4
4
  import useEventLinkClickHandler from './hooks/use-event-link-click-handler'
5
5
 
6
6
  const Text = ({ event, ...props }) => {
7
- const [body] = useTranslatedEventData(event)
7
+ const { body } = useTranslatedEventData(event)
8
8
  const eventClick = useEventLinkClickHandler(event.payload.id)
9
9
 
10
10
  const containerProps = useMemo(() => {
@@ -4,7 +4,7 @@ import { useTranslatedEventData } from 'domains/translations/hooks'
4
4
 
5
5
  const Translation = ({ event, ...props }) => {
6
6
  const { t } = useI18n()
7
- const [body] = useTranslatedEventData(event)
7
+ const { body } = useTranslatedEventData(event)
8
8
  return (
9
9
  <MessageContainer type="text" event={event} {...props}>
10
10
  {t(body.key.join('.'), body.variables)}
@@ -21,7 +21,7 @@ const UploadContent = ({ children, url, target }) =>
21
21
 
22
22
  const Upload = ({ event, ...props }) => {
23
23
  const { t } = useI18n()
24
- const [body] = useTranslatedEventData(event)
24
+ const { body } = useTranslatedEventData(event)
25
25
  const { fromClient } = event.payload
26
26
  const { filename, url } = body
27
27
 
@@ -4,7 +4,7 @@ import { useTranslatedEventData } from 'domains/translations/hooks'
4
4
  import MessageContainer from 'ui/components/conversation/message-container'
5
5
 
6
6
  const Video = ({ event, descriptorId, ...props }) => {
7
- const [body] = useTranslatedEventData(event)
7
+ const { body } = useTranslatedEventData(event)
8
8
  const { description, url } = body
9
9
  const classNames = useSeamlyMessageContainerClassNames(event)
10
10
 
@@ -1,12 +1,10 @@
1
1
  import { className } from 'lib/css'
2
2
  import { useSeamlyMessageContainerClassNames } from 'ui/hooks/component-helper-hooks'
3
3
  import { useTranslatedEventData } from 'domains/translations/hooks'
4
- import { useI18n } from 'domains/i18n/hooks'
5
4
  import EventParticipant from './event/event-participant'
6
5
 
7
6
  function MessageContainer({
8
7
  showParticipant = true,
9
- showTranslationToggle = true,
10
8
  event,
11
9
  type,
12
10
  modifiers,
@@ -16,10 +14,8 @@ function MessageContainer({
16
14
  ...props
17
15
  }) {
18
16
  const classNames = useSeamlyMessageContainerClassNames(event)
19
- const { t } = useI18n()
20
17
 
21
- const [, { hasTranslation, isTranslated, toggleTranslation, locale }] =
22
- useTranslatedEventData(event)
18
+ const { hasTranslation, isTranslated, locale } = useTranslatedEventData(event)
23
19
 
24
20
  if (type) {
25
21
  classNames.push(`message--type-${type}`)
@@ -60,24 +56,6 @@ function MessageContainer({
60
56
  {info}
61
57
  </div>
62
58
  )}
63
- {showTranslationToggle && hasTranslation && (
64
- <div className={className('message__translation-info')}>
65
- <button
66
- className={className(
67
- 'message__translation-toggle',
68
- 'button',
69
- 'button--secondary',
70
- )}
71
- onClick={toggleTranslation}
72
- >
73
- {t(
74
- isTranslated
75
- ? 'translations.toggle.hideButtonText'
76
- : 'translations.toggle.showButtonText',
77
- )}
78
- </button>
79
- </div>
80
- )}
81
59
  </div>
82
60
  </>
83
61
  )
@@ -123,12 +123,13 @@ const SeamlyEventSubscriber = ({ eventBus }) => {
123
123
  case 'system':
124
124
  if (payload.type === 'service_changed') {
125
125
  const { serviceSettings, ...eventPayload } = payload
126
- const { uploads, entry } = serviceSettings
126
+ const { entry } = serviceSettings
127
+ const { upload } = entry.options
127
128
 
128
129
  dispatch(
129
130
  setFeatureEnabledState({
130
131
  key: featureKeys.uploads,
131
- enabled: !!(uploads && uploads.enabled),
132
+ enabled: !!(upload && upload.enabled),
132
133
  }),
133
134
  )
134
135
 
@@ -32,7 +32,9 @@ const Faq = () => {
32
32
  const { hasPrompt, continueChat } = useSeamlyResumeConversationPrompt()
33
33
 
34
34
  const lastFaqEventPayload = useSeamlyServiceData('suggestion')
35
- const [eventBody] = useTranslatedEventData({ payload: lastFaqEventPayload })
35
+ const { body: eventBody } = useTranslatedEventData({
36
+ payload: lastFaqEventPayload,
37
+ })
36
38
  const faqs = useMemo(() => {
37
39
  const newFaqs = lastFaqEventPayload && !hasInterrupt ? eventBody : []
38
40
  const itemBaseClass = `faqs__item`
@@ -43,10 +43,11 @@ const OptionsButton = () => {
43
43
  useEffect(() => {
44
44
  if (menuIsOpen && !prevMenuIsOpen.current) {
45
45
  requestAnimationFrame(async () => {
46
- await timeout(180) // Wait for next 3 frame ticks
46
+ await timeout(60) // Wait for next frame tick
47
47
  const firstActiveOptionIndex = menuOptions.findIndex(
48
48
  (option) => option.available,
49
49
  )
50
+
50
51
  const focusIndex =
51
52
  firstActiveOptionIndex === -1 ? 0 : firstActiveOptionIndex
52
53
  focusElement(menuItemButtons.current[focusIndex])
@@ -155,6 +156,7 @@ const OptionsButton = () => {
155
156
  if (!optionsLength) {
156
157
  return null
157
158
  }
159
+
158
160
  return (
159
161
  <div
160
162
  className={className('options__container')}