@seamly/web-ui 20.7.0 → 20.8.0-alpha.1

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 (222) hide show
  1. package/CHANGELOG.md +212 -16
  2. package/build/dist/lib/hooks.js +1 -1
  3. package/build/dist/lib/hooks.min.js +1 -1
  4. package/build/dist/lib/index.debug.js +939 -784
  5. package/build/dist/lib/index.debug.min.js +1 -1
  6. package/build/dist/lib/index.debug.min.js.LICENSE.txt +186 -130
  7. package/build/dist/lib/index.js +24806 -19703
  8. package/build/dist/lib/index.min.js +1 -1
  9. package/build/dist/lib/index.min.js.LICENSE.txt +38 -4
  10. package/build/dist/lib/standalone.js +32726 -26838
  11. package/build/dist/lib/standalone.min.js +1 -1
  12. package/build/dist/lib/standalone.min.js.LICENSE.txt +39 -0
  13. package/build/dist/lib/storage.js +2 -2
  14. package/build/dist/lib/storage.min.js +1 -1
  15. package/build/dist/lib/style-guide.js +8649 -7863
  16. package/build/dist/lib/style-guide.min.js +2 -1
  17. package/build/dist/lib/style-guide.min.js.LICENSE.txt +38 -0
  18. package/build/dist/lib/utils.js +1 -2
  19. package/build/dist/lib/utils.min.js +1 -1
  20. package/package.json +19 -9
  21. package/src/icons/avatar_agent-32.svg +7 -0
  22. package/src/icons/avatar_bot-32.svg +6 -1
  23. package/src/javascripts/api/index.js +1 -1
  24. package/src/javascripts/{config.js → config.ts} +3 -1
  25. package/src/javascripts/config.types.ts +95 -0
  26. package/src/javascripts/domains/app/actions.ts +83 -0
  27. package/src/javascripts/domains/app/app.types.ts +3 -0
  28. package/src/javascripts/domains/app/hooks.js +3 -5
  29. package/src/javascripts/domains/app/selectors.ts +6 -0
  30. package/src/javascripts/domains/app/slice.ts +30 -0
  31. package/src/javascripts/domains/config/actions.ts +45 -0
  32. package/src/javascripts/domains/config/hooks.ts +19 -0
  33. package/src/javascripts/domains/config/selectors.ts +24 -0
  34. package/src/javascripts/domains/config/slice.ts +111 -0
  35. package/src/javascripts/domains/errors/index.js +13 -9
  36. package/src/javascripts/domains/forms/context.ts +14 -0
  37. package/src/javascripts/domains/forms/forms.types.ts +24 -0
  38. package/src/javascripts/domains/forms/{hooks.js → hooks.ts} +23 -26
  39. package/src/javascripts/domains/forms/{provider.js → provider.tsx} +20 -14
  40. package/src/javascripts/domains/forms/{selectors.js → selectors.ts} +7 -8
  41. package/src/javascripts/domains/forms/slice.ts +84 -0
  42. package/src/javascripts/domains/forms/utils.ts +15 -0
  43. package/src/javascripts/domains/i18n/actions.ts +24 -0
  44. package/src/javascripts/domains/i18n/{hooks.js → hooks.ts} +2 -2
  45. package/src/javascripts/domains/i18n/i18n.types.ts +6 -0
  46. package/src/javascripts/domains/i18n/selectors.ts +16 -0
  47. package/src/javascripts/domains/i18n/{reducer.js → slice.ts} +43 -37
  48. package/src/javascripts/domains/interrupt/{hooks.js → hooks.ts} +2 -2
  49. package/src/javascripts/domains/interrupt/{middleware.js → middleware.ts} +11 -8
  50. package/src/javascripts/domains/interrupt/selectors.ts +6 -0
  51. package/src/javascripts/domains/interrupt/slice.ts +40 -0
  52. package/src/javascripts/domains/options/middleware.js +9 -6
  53. package/src/javascripts/domains/redux/redux.types.ts +11 -0
  54. package/src/javascripts/domains/store/index.ts +53 -0
  55. package/src/javascripts/domains/store/slice.ts +639 -0
  56. package/src/javascripts/domains/store/store.types.ts +135 -0
  57. package/src/javascripts/domains/translations/components/chat-status.js +2 -2
  58. package/src/javascripts/domains/translations/components/options-button.js +1 -1
  59. package/src/javascripts/domains/translations/components/options-dialog/form.js +5 -5
  60. package/src/javascripts/domains/translations/components/options-dialog/index.js +2 -2
  61. package/src/javascripts/domains/translations/{hooks.js → hooks.ts} +28 -23
  62. package/src/javascripts/domains/translations/middleware.js +29 -27
  63. package/src/javascripts/domains/translations/selectors.js +4 -9
  64. package/src/javascripts/domains/translations/slice.ts +67 -0
  65. package/src/javascripts/domains/translations/translations.types.ts +12 -0
  66. package/src/javascripts/domains/visibility/{actions.js → actions.ts} +25 -19
  67. package/src/javascripts/domains/visibility/{hooks.js → hooks.ts} +13 -10
  68. package/src/javascripts/domains/visibility/{selectors.js → selectors.ts} +3 -6
  69. package/src/javascripts/domains/visibility/slice.ts +38 -0
  70. package/src/javascripts/domains/visibility/utils.js +0 -9
  71. package/src/javascripts/domains/visibility/visibility.types.ts +6 -0
  72. package/src/javascripts/index.ts +92 -0
  73. package/src/javascripts/lib/engine/index.js +15 -11
  74. package/src/javascripts/lib/external-api/initialize-api.js +1 -1
  75. package/src/javascripts/lib/id.js +5 -8
  76. package/src/javascripts/lib/mutex.js +3 -1
  77. package/src/javascripts/lib/store/providers/cookie-storage.js +1 -1
  78. package/src/javascripts/lib/store/providers/session-storage.js +1 -1
  79. package/src/javascripts/package/hooks.js +2 -2
  80. package/src/javascripts/package/utils.js +0 -1
  81. package/src/javascripts/schema.ts +1455 -0
  82. package/src/javascripts/style-guide/components/app.js +4 -4
  83. package/src/javascripts/style-guide/components/static-core.js +87 -65
  84. package/src/javascripts/style-guide/components/view.js +4 -4
  85. package/src/javascripts/style-guide/state-helpers/index.js +5 -5
  86. package/src/javascripts/style-guide/states.js +6 -4
  87. package/src/javascripts/style-guide.ts +5 -0
  88. package/src/javascripts/ui/components/app-options/index.js +2 -4
  89. package/src/javascripts/ui/components/conversation/component-filter.js +1 -1
  90. package/src/javascripts/ui/components/conversation/conversation.js +5 -5
  91. package/src/javascripts/ui/components/conversation/event/card-message.js +1 -1
  92. package/src/javascripts/ui/components/conversation/event/carousel-component/components/controls.js +1 -1
  93. package/src/javascripts/ui/components/conversation/event/carousel-message/components/slide.js +1 -1
  94. package/src/javascripts/ui/components/conversation/event/carousel-message/index.js +1 -1
  95. package/src/javascripts/ui/components/conversation/event/choice-prompt.js +2 -2
  96. package/src/javascripts/ui/components/conversation/event/conversation-suggestions.js +11 -14
  97. package/src/javascripts/ui/components/conversation/event/cta.js +1 -1
  98. package/src/javascripts/ui/components/conversation/event/divider/variants/default.js +1 -1
  99. package/src/javascripts/ui/components/conversation/event/divider/variants/new-translation.js +5 -2
  100. package/src/javascripts/ui/components/conversation/event/event-participant.js +2 -2
  101. package/src/javascripts/ui/components/conversation/event/hooks/use-formatted-date.js +2 -2
  102. package/src/javascripts/ui/components/conversation/event/image-lightbox.js +1 -1
  103. package/src/javascripts/ui/components/conversation/event/image.js +5 -7
  104. package/src/javascripts/ui/components/conversation/event/participant.js +1 -1
  105. package/src/javascripts/ui/components/conversation/event/splash.js +3 -3
  106. package/src/javascripts/ui/components/conversation/event/text.js +1 -1
  107. package/src/javascripts/ui/components/conversation/event/translation.js +2 -2
  108. package/src/javascripts/ui/components/conversation/event/upload.js +2 -2
  109. package/src/javascripts/ui/components/conversation/event/video.js +1 -1
  110. package/src/javascripts/ui/components/conversation/message-container.js +4 -4
  111. package/src/javascripts/ui/components/core/seamly-api-context.js +1 -1
  112. package/src/javascripts/ui/components/core/seamly-core.js +15 -14
  113. package/src/javascripts/ui/components/core/seamly-event-subscriber.js +96 -91
  114. package/src/javascripts/ui/components/core/seamly-file-upload.js +20 -24
  115. package/src/javascripts/ui/components/core/seamly-initializer.js +1 -1
  116. package/src/javascripts/ui/components/core/seamly-instance-functions-loader.js +5 -4
  117. package/src/javascripts/ui/components/core/seamly-new-notifications.js +2 -2
  118. package/src/javascripts/ui/components/core/seamly-read-state.js +10 -17
  119. package/src/javascripts/ui/components/entry/deprecated-toggle-button.js +3 -3
  120. package/src/javascripts/ui/components/entry/entry-container.js +4 -6
  121. package/src/javascripts/ui/components/entry/text-entry/hooks.js +3 -3
  122. package/src/javascripts/ui/components/entry/text-entry/index.js +3 -2
  123. package/src/javascripts/ui/components/entry/text-entry/text-entry-form.js +6 -10
  124. package/src/javascripts/ui/components/entry/upload/file-upload-form.js +2 -2
  125. package/src/javascripts/ui/components/entry/upload/index.js +10 -9
  126. package/src/javascripts/ui/components/entry/upload-toggle.js +2 -2
  127. package/src/javascripts/ui/components/faq/faq.js +6 -6
  128. package/src/javascripts/ui/components/form-controls/file-input.js +1 -1
  129. package/src/javascripts/ui/components/form-controls/form.js +1 -1
  130. package/src/javascripts/ui/components/form-controls/input.js +1 -1
  131. package/src/javascripts/ui/components/form-controls/select.js +1 -1
  132. package/src/javascripts/ui/components/layout/agent-info.js +4 -4
  133. package/src/javascripts/ui/components/layout/chat-frame.js +3 -3
  134. package/src/javascripts/ui/components/layout/chat.js +11 -12
  135. package/src/javascripts/ui/components/layout/deprecated-app-frame.js +10 -9
  136. package/src/javascripts/ui/components/layout/header.js +1 -1
  137. package/src/javascripts/ui/components/layout/interrupt.js +23 -24
  138. package/src/javascripts/ui/components/layout/pre-chat-messages.js +11 -11
  139. package/src/javascripts/ui/components/layout/privacy-disclaimer.js +2 -2
  140. package/src/javascripts/ui/components/options/options-button.js +12 -10
  141. package/src/javascripts/ui/components/options/transcript/index.js +2 -2
  142. package/src/javascripts/ui/components/options/transcript/transcript-form.js +1 -1
  143. package/src/javascripts/ui/components/suggestions/index.js +9 -8
  144. package/src/javascripts/ui/components/view/deprecated-view.js +19 -16
  145. package/src/javascripts/ui/components/view/index.js +12 -12
  146. package/src/javascripts/ui/components/view/inline-view.js +2 -2
  147. package/src/javascripts/ui/components/view/window-view/collapse-button.js +3 -3
  148. package/src/javascripts/ui/components/view/window-view/index.js +13 -13
  149. package/src/javascripts/ui/components/view/window-view/window-open-button.js +13 -13
  150. package/src/javascripts/ui/components/warnings/idle-detach-warning.js +1 -1
  151. package/src/javascripts/ui/components/warnings/resume-conversation-prompt.js +1 -1
  152. package/src/javascripts/ui/components/widgets/lightbox.js +2 -2
  153. package/src/javascripts/ui/components/widgets/upload-progress.js +1 -1
  154. package/src/javascripts/ui/hooks/component-helper-hooks.js +1 -1
  155. package/src/javascripts/ui/hooks/file-upload-hooks.js +4 -6
  156. package/src/javascripts/ui/hooks/focus-helper-hooks.js +14 -12
  157. package/src/javascripts/ui/hooks/live-region-hooks.js +2 -0
  158. package/src/javascripts/ui/hooks/seamly-api-hooks.js +8 -3
  159. package/src/javascripts/ui/hooks/seamly-entry-hooks.js +28 -25
  160. package/src/javascripts/ui/hooks/seamly-hooks.js +25 -25
  161. package/src/javascripts/ui/hooks/seamly-option-hooks.js +17 -19
  162. package/src/javascripts/ui/hooks/seamly-state-hooks.js +14 -13
  163. package/src/javascripts/ui/hooks/use-seamly-chat.js +15 -25
  164. package/src/javascripts/ui/hooks/use-seamly-commands.js +46 -46
  165. package/src/javascripts/ui/hooks/use-seamly-idle-detach-countdown.js +22 -24
  166. package/src/javascripts/ui/hooks/use-seamly-resume-conversation-prompt.js +8 -9
  167. package/src/javascripts/ui/hooks/use-single-file-upload.js +4 -6
  168. package/src/javascripts/ui/hooks/utility-hooks.js +4 -4
  169. package/src/javascripts/ui/utils/form-utils.js +0 -145
  170. package/src/javascripts/ui/utils/general-utils.js +3 -4
  171. package/src/javascripts/ui/utils/seamly-utils.ts +73 -0
  172. package/webpack/config.common.js +16 -0
  173. package/webpack/config.dev.js +1 -0
  174. package/webpack/config.package.js +26 -5
  175. package/webpack/defaults.js +7 -2
  176. package/webpack/parts/babel-loader-browser-plugins.js +1 -0
  177. package/webpack/parts/dev-server.js +4 -3
  178. package/src/javascripts/domains/app/actions.js +0 -112
  179. package/src/javascripts/domains/app/index.js +0 -7
  180. package/src/javascripts/domains/app/reducer.js +0 -16
  181. package/src/javascripts/domains/app/selectors.js +0 -8
  182. package/src/javascripts/domains/app/utils.js +0 -4
  183. package/src/javascripts/domains/config/actions.js +0 -7
  184. package/src/javascripts/domains/config/hooks.js +0 -23
  185. package/src/javascripts/domains/config/index.js +0 -7
  186. package/src/javascripts/domains/config/reducer.js +0 -79
  187. package/src/javascripts/domains/config/selectors.js +0 -23
  188. package/src/javascripts/domains/config/utils.js +0 -4
  189. package/src/javascripts/domains/forms/actions.js +0 -21
  190. package/src/javascripts/domains/forms/context.js +0 -6
  191. package/src/javascripts/domains/forms/index.js +0 -8
  192. package/src/javascripts/domains/forms/reducer.js +0 -84
  193. package/src/javascripts/domains/forms/utils.js +0 -20
  194. package/src/javascripts/domains/i18n/actions.js +0 -20
  195. package/src/javascripts/domains/i18n/index.js +0 -7
  196. package/src/javascripts/domains/i18n/selectors.js +0 -15
  197. package/src/javascripts/domains/i18n/utils.js +0 -4
  198. package/src/javascripts/domains/interrupt/actions.js +0 -4
  199. package/src/javascripts/domains/interrupt/index.js +0 -9
  200. package/src/javascripts/domains/interrupt/reducer.js +0 -22
  201. package/src/javascripts/domains/interrupt/selectors.js +0 -6
  202. package/src/javascripts/domains/interrupt/utils.js +0 -4
  203. package/src/javascripts/domains/options/index.js +0 -1
  204. package/src/javascripts/domains/redux/context.js +0 -6
  205. package/src/javascripts/domains/redux/create-redux-store.js +0 -21
  206. package/src/javascripts/domains/redux/hooks.js +0 -80
  207. package/src/javascripts/domains/redux/index.js +0 -19
  208. package/src/javascripts/domains/redux/provider.js +0 -5
  209. package/src/javascripts/domains/redux/utils.js +0 -12
  210. package/src/javascripts/domains/store/index.js +0 -46
  211. package/src/javascripts/domains/store/state-reducer.js +0 -56
  212. package/src/javascripts/domains/translations/actions.js +0 -11
  213. package/src/javascripts/domains/translations/index.js +0 -10
  214. package/src/javascripts/domains/translations/reducer.js +0 -69
  215. package/src/javascripts/domains/translations/utils.js +0 -4
  216. package/src/javascripts/domains/visibility/index.js +0 -8
  217. package/src/javascripts/domains/visibility/reducer.js +0 -24
  218. package/src/javascripts/index.js +0 -153
  219. package/src/javascripts/lib/redux-helpers/index.js +0 -99
  220. package/src/javascripts/style-guide.js +0 -5
  221. package/src/javascripts/ui/hooks/use-seamly-dispatch.js +0 -3
  222. package/src/javascripts/ui/utils/seamly-utils.js +0 -832
@@ -0,0 +1,135 @@
1
+ import type { Config } from 'config.types'
2
+ import type { AppState } from 'domains/app/app.types'
3
+ import type { FormState } from 'domains/forms/forms.types'
4
+ import type { I18nState } from 'domains/i18n/i18n.types'
5
+ import type { TranslationState } from 'domains/translations/translations.types'
6
+ import type { VisibilityState } from 'domains/visibility/visibility.types'
7
+ import { components } from 'schema'
8
+ import { entryTypes } from 'ui/utils/seamly-utils'
9
+
10
+ export type MessageInfo = components['schemas']['MessageInfo'] & {
11
+ key?: string
12
+ }
13
+ export interface InfoEvent {
14
+ type: 'info'
15
+ payload: MessageInfo
16
+ }
17
+
18
+ export type MessageMessage = components['schemas']['MessageMessage'] & {
19
+ key?: string
20
+ }
21
+ export interface MessageEvent {
22
+ type: 'message'
23
+ payload: MessageMessage
24
+ }
25
+
26
+ export type MessageParticipant = components['schemas']['MessageParticipant'] & {
27
+ key?: string
28
+ }
29
+ export interface ParticipantEvent {
30
+ type: 'participant'
31
+ payload: MessageParticipant
32
+ }
33
+
34
+ export type ChannelEvent = MessageEvent | ParticipantEvent | InfoEvent
35
+
36
+ export type HistoryEvent = ChannelEvent
37
+
38
+ export type EntryMeta = {
39
+ default: typeof entryTypes.text
40
+ active: typeof entryTypes.text
41
+ userSelected: null
42
+ blockAutoEntrySwitch: boolean
43
+ options: Record<string, unknown>
44
+ optionsOverride: Record<string, unknown>
45
+ }
46
+
47
+ export type ParticipantInfo = {
48
+ participants: Record<string, MessageParticipant['participant']>
49
+ currentAgent: string
50
+ }
51
+ export interface CurrentUpload {
52
+ id: string
53
+ progress: number
54
+ name: string
55
+ uploading: boolean
56
+ uploadHandle: null | { abort: () => void }
57
+ error: string
58
+ complete: boolean
59
+ }
60
+
61
+ export type ServiceInfo = {
62
+ activeServiceSessionId: string
63
+ }
64
+
65
+ export interface StoreState {
66
+ events: ChannelEvent[]
67
+ initialState: Partial<
68
+ Omit<
69
+ StoreState,
70
+ 'initialState' | 'userHasResponded' | 'seamlyContainerElement'
71
+ >
72
+ > & {
73
+ userResponded: boolean
74
+ }
75
+ unreadEvents: number
76
+ userHasResponded: boolean
77
+ loadedImageEventIds: string[]
78
+ isLoading: boolean
79
+ idleDetachCountdown: {
80
+ hasCountdown: boolean
81
+ isActive: boolean
82
+ remaining?: number
83
+ wasStopped?: boolean
84
+ count?: number
85
+ timer?: {
86
+ minutes: number
87
+ seconds: number
88
+ }
89
+ }
90
+ resumeConversationPrompt: boolean
91
+ serviceInfo: ServiceInfo
92
+ participantInfo: ParticipantInfo
93
+ headerTitles: {
94
+ title: null
95
+ subTitle: string
96
+ }
97
+ historyLoaded: boolean
98
+ skiplinkTargetId: string
99
+ optionsButtonId: string
100
+ headerCollapseButtonId: string
101
+ serviceData: Record<string, unknown>
102
+ options: {
103
+ features: {
104
+ uploads?: {
105
+ enabledFromEntry: boolean
106
+ enabled: boolean
107
+ }
108
+ }
109
+ panelActive: boolean
110
+ optionActive: string
111
+ userSelectedOptions: Record<string, unknown>
112
+ }
113
+ showFileUpload: boolean
114
+ currentUploads: CurrentUpload[]
115
+ entryMeta: EntryMeta
116
+ seamlyContainerElement: null | HTMLElement
117
+ }
118
+
119
+ export type CreateStore = {
120
+ initialState: Partial<StoreState>
121
+ api: Record<string, unknown>
122
+ eventBus: Record<string, unknown>
123
+ config: Config
124
+ }
125
+
126
+ export interface ReduxStore {
127
+ state: StoreState
128
+ app: AppState
129
+ config: Config
130
+ forms: FormState
131
+ translations: TranslationState
132
+ i18n: I18nState
133
+ interrupt: {}
134
+ visibility: VisibilityState
135
+ }
@@ -1,13 +1,13 @@
1
1
  import { useCallback } from 'preact/hooks'
2
2
  import ChatStatus from 'ui/components/chat-status'
3
- import { useI18n } from 'domains/i18n'
3
+ import { useI18n } from 'domains/i18n/hooks'
4
4
  import { useSkiplinkTargetFocusing } from 'ui/hooks/seamly-hooks'
5
5
  import {
6
6
  useTranslationsContainer,
7
7
  useTranslations,
8
8
  useLocaleNativeName,
9
9
  } from 'domains/translations/hooks'
10
- import { useInterrupt } from 'domains/interrupt'
10
+ import { useInterrupt } from 'domains/interrupt/hooks'
11
11
 
12
12
  export default function TranslationsChatStatus() {
13
13
  const { t } = useI18n()
@@ -7,7 +7,7 @@ import InOutTransition, {
7
7
  transitionStartStates,
8
8
  } from 'ui/components/widgets/in-out-transition'
9
9
  import { useLocaleNativeName } from 'domains/translations/hooks'
10
- import { useI18n } from 'domains/i18n'
10
+ import { useI18n } from 'domains/i18n/hooks'
11
11
  import TranslationsOptionsDialog from './options-dialog'
12
12
 
13
13
  export default function TranslationsOptionButton() {
@@ -1,11 +1,11 @@
1
- import { useMemo } from 'preact/hooks'
2
- import Form from 'ui/components/form-controls/form'
3
- import { className } from 'lib/css'
4
- import { useI18n } from 'domains/i18n'
1
+ import { useI18n } from 'domains/i18n/hooks'
5
2
  import {
6
- useTranslations,
7
3
  useLocaleNativeName,
4
+ useTranslations,
8
5
  } from 'domains/translations/hooks'
6
+ import { className } from 'lib/css'
7
+ import { useMemo } from 'preact/hooks'
8
+ import Form from 'ui/components/form-controls/form'
9
9
  import Select from 'ui/components/form-controls/select'
10
10
 
11
11
  function TranslationsOptionsDialogForm({ controlName, descriptionId }) {
@@ -1,6 +1,6 @@
1
1
  import { useCallback, useMemo, useRef, useState } from 'preact/hooks'
2
2
  import OptionsFrame from 'ui/components/options/options-frame'
3
- import { FormProvider } from 'domains/forms'
3
+ import FormProvider from 'domains/forms/provider'
4
4
  import { useGeneratedId } from 'ui/hooks/seamly-hooks'
5
5
  import { getValidator } from 'ui/utils/form-utils'
6
6
  import { isNotEmptyString } from 'ui/utils/validations'
@@ -8,7 +8,7 @@ import {
8
8
  useTranslations,
9
9
  useTranslationsContainer,
10
10
  } from 'domains/translations/hooks'
11
- import { useI18n } from 'domains/i18n'
11
+ import { useI18n } from 'domains/i18n/hooks'
12
12
  import TranslationsOptionsDialogForm from './form'
13
13
 
14
14
  export const formName = 'translation-settings'
@@ -1,39 +1,43 @@
1
+ import { getIsPayloadTranslated } from 'domains/translations/selectors'
2
+ import {
3
+ disableEvent,
4
+ disableTranslation,
5
+ enableEvent,
6
+ enableTranslation,
7
+ } from 'domains/translations/slice'
1
8
  import { useMemo } from 'preact/hooks'
9
+ import { useDispatch, useSelector } from 'react-redux'
2
10
  import {
3
11
  useElementFocusingById,
4
12
  useSeamlyCommands,
5
13
  useStableCallback,
6
14
  } from 'ui/hooks/seamly-hooks'
7
15
  import { actionTypes } from 'ui/utils/seamly-utils'
8
- import {
9
- useSelector,
10
- useSelectorWithProps,
11
- useStoreDispatch,
12
- } from 'domains/redux'
13
- import * as Selectors from './selectors'
14
- import * as Actions from './actions'
15
16
 
16
17
  export function useTranslations() {
17
18
  const { sendAction } = useSeamlyCommands()
18
- const dispatch = useStoreDispatch()
19
+ const dispatch = useDispatch()
19
20
  const enableTranslations = useStableCallback(
20
21
  (locale) => {
21
22
  sendAction({
22
23
  type: actionTypes.setTranslation,
23
24
  body: { enabled: true, locale },
24
25
  })
25
- dispatch(Actions.enable(locale))
26
+ dispatch(enableTranslation(locale))
26
27
  },
28
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
29
+ // @ts-ignore
27
30
  [sendAction, dispatch],
28
31
  )
29
32
  const disableTranslations = useStableCallback(() => {
30
33
  sendAction({ type: actionTypes.setTranslation, body: { enabled: false } })
31
- dispatch(Actions.disable())
34
+ dispatch(disableTranslation())
35
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
36
+ // @ts-ignore
32
37
  }, [sendAction, dispatch])
33
38
 
34
39
  const { languages, isActive, isAvailable, currentLocale } = useSelector(
35
- Selectors.getState,
36
- [],
40
+ ({ translations }) => translations,
37
41
  )
38
42
 
39
43
  return {
@@ -46,8 +50,8 @@ export function useTranslations() {
46
50
  }
47
51
  }
48
52
 
49
- export function useTranslatedEventData({ payload } = {}) {
50
- const payloadId = payload?.id
53
+ export function useTranslatedEventData({ payload }) {
54
+ const payloadId: string = payload?.id
51
55
  let body
52
56
  let translatedBody
53
57
  switch (payload?.type) {
@@ -60,19 +64,20 @@ export function useTranslatedEventData({ payload } = {}) {
60
64
  translatedBody = payload?.translatedBody
61
65
  }
62
66
  const hasTranslation = !!translatedBody
63
- const isTranslated = useSelectorWithProps(
64
- Selectors.getIsPayloadTranslated,
65
- { payloadId },
66
- [payloadId],
67
+ const isTranslated = useSelector((store) =>
68
+ // @ts-ignore
69
+ getIsPayloadTranslated(store, payloadId),
67
70
  )
68
- const dispatch = useStoreDispatch()
71
+ const dispatch = useDispatch()
69
72
 
70
73
  const toggleTranslation = useStableCallback(() => {
71
74
  if (isTranslated) {
72
- dispatch(Actions.disableEvent(payloadId))
75
+ dispatch(disableEvent(payloadId))
73
76
  } else {
74
- dispatch(Actions.enableEvent(payloadId))
77
+ dispatch(enableEvent(payloadId))
75
78
  }
79
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
80
+ // @ts-ignore
76
81
  }, [isTranslated, payloadId, dispatch])
77
82
 
78
83
  return [
@@ -88,12 +93,12 @@ export function useTranslatedEventData({ payload } = {}) {
88
93
  }
89
94
 
90
95
  export function useTranslationsContainer() {
91
- const id = useSelector(Selectors.getState, []).containerId
96
+ const id = useSelector(({ translations }) => translations.containerId)
92
97
  const focusContainer = useElementFocusingById(id)
93
98
  return { id, focusContainer }
94
99
  }
95
100
 
96
- export function useLocaleNativeName(locale) {
101
+ export function useLocaleNativeName(locale: string) {
97
102
  const { languages } = useTranslations()
98
103
 
99
104
  return useMemo(
@@ -1,44 +1,46 @@
1
+ import { initializeConfig } from 'domains/config/actions'
2
+ import { setLocale } from 'domains/i18n/actions'
3
+ import { selectInitialLocale } from 'domains/i18n/selectors'
4
+ import { addEvent, setHistory, setInitialState } from 'domains/store/slice'
1
5
  import {
2
- Actions as I18nActions,
3
- Selectors as I18nSelectors,
4
- } from 'domains/i18n'
5
- import { seamlyActions } from 'ui/utils/seamly-utils'
6
- import * as Actions from './actions'
6
+ disableTranslation,
7
+ enableTranslation,
8
+ } from 'domains/translations/slice'
7
9
 
8
- export default function createMiddleware({ dispatch, getState }) {
10
+ export default function createI18nMiddleware({ dispatch, getState }) {
9
11
  return (next) => {
10
12
  return (action) => {
11
13
  const result = next(action)
12
-
14
+ const { payload } = action
13
15
  switch (action.type) {
14
- case String(seamlyActions.SET_HISTORY):
15
- if (action.history?.translation?.enabled) {
16
- dispatch(Actions.enable(action.history.translation.locale))
17
- dispatch(I18nActions.setLocale(action.history.translation.locale))
16
+ case setHistory.type:
17
+ if (payload?.translation?.enabled) {
18
+ dispatch(enableTranslation(payload.translation.locale))
19
+ dispatch(setLocale(payload.translation.locale))
20
+ }
21
+ break
22
+ case initializeConfig.fulfilled.type:
23
+ if (payload.locale) {
24
+ dispatch(setLocale(payload.locale))
18
25
  }
19
26
  break
20
- case String(seamlyActions.SET_INITIAL_STATE):
21
- if (action.initialState?.translation?.enabled) {
22
- dispatch(Actions.enable(action.initialState.translation.locale))
23
- dispatch(I18nActions.setLocale(action.locale))
27
+ case setInitialState.type:
28
+ if (payload?.translation?.enabled) {
29
+ dispatch(enableTranslation(payload.translation.locale))
24
30
  }
25
31
  break
26
- case String(seamlyActions.ADD_EVENT):
32
+ case addEvent.type:
27
33
  if (
28
- action.event.type === 'info' &&
29
- action.event?.payload?.body?.subtype === 'new_translation' &&
30
- action.event.payload.body.translationEnabled
34
+ payload.type === 'info' &&
35
+ payload?.payload?.body?.subtype === 'new_translation' &&
36
+ payload.payload.body.translationEnabled
31
37
  ) {
32
- dispatch(
33
- I18nActions.setLocale(
34
- action.event.payload.body.translationLocale,
35
- ),
36
- )
38
+ dispatch(setLocale(payload.payload.body.translationLocale))
37
39
  }
38
40
  break
39
- case String(Actions.disable):
40
- const initialLocale = I18nSelectors.selectInitialLocale(getState())
41
- dispatch(I18nActions.setLocale(initialLocale))
41
+ case disableTranslation.type:
42
+ const initialLocale = selectInitialLocale(getState())
43
+ dispatch(setLocale(initialLocale))
42
44
  break
43
45
  }
44
46
  return result
@@ -1,16 +1,11 @@
1
- import { createSelector } from 'reselect'
2
- import { getPropSelector } from 'domains/redux/utils'
3
- import { selectState } from './utils'
4
-
5
- export const getState = selectState
1
+ import { createSelector } from '@reduxjs/toolkit'
6
2
 
7
3
  export const getOriginalPayloadIds = createSelector(
8
- getState,
9
- (state) => state.originalPayloadIds,
4
+ ({ translations }) => translations,
5
+ ({ originalPayloadIds }) => originalPayloadIds,
10
6
  )
11
7
 
12
8
  export const getIsPayloadTranslated = createSelector(
13
- getOriginalPayloadIds,
14
- getPropSelector('payloadId'),
9
+ [getOriginalPayloadIds, (_, payloadId) => payloadId],
15
10
  (payloadIds, payloadId) => !payloadIds.includes(payloadId),
16
11
  )
@@ -0,0 +1,67 @@
1
+ import { TranslationState } from 'domains/translations/translations.types'
2
+ import { createSlice, nanoid } from '@reduxjs/toolkit'
3
+ import { initializeApp } from 'domains/app/actions'
4
+ import { initializeConfig } from 'domains/config/actions'
5
+
6
+ export const translationsInitialState: TranslationState = {
7
+ isActive: false,
8
+ currentLocale: undefined,
9
+ isAvailable: false,
10
+ languages: [],
11
+ originalPayloadIds: [],
12
+ containerId: nanoid(),
13
+ }
14
+
15
+ export const translationSlice = createSlice({
16
+ name: 'translation',
17
+ initialState: translationsInitialState,
18
+ reducers: {
19
+ enableTranslation: (state, { payload }) => {
20
+ state.isActive = true
21
+ state.currentLocale = payload
22
+ },
23
+ disableTranslation: (state) => {
24
+ state.isActive = false
25
+ state.currentLocale = undefined
26
+ },
27
+ enableEvent: (state, { payload }) => {
28
+ if (!state.originalPayloadIds.includes(payload)) {
29
+ return
30
+ }
31
+ state.originalPayloadIds = state.originalPayloadIds.filter(
32
+ (id) => id !== payload,
33
+ )
34
+ },
35
+ disableEvent: (state, { payload }) => {
36
+ if (state.originalPayloadIds.includes(payload)) {
37
+ return
38
+ }
39
+ state.originalPayloadIds.push(payload)
40
+ },
41
+ },
42
+ extraReducers: (builder) => {
43
+ 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
+ .addCase(initializeConfig.fulfilled, (state, { payload }) => {
51
+ const feature = payload?.features?.translation
52
+ if (!feature) return
53
+
54
+ state.isAvailable = feature.enabled === true
55
+ state.languages = feature.languages
56
+ })
57
+ },
58
+ })
59
+
60
+ export const {
61
+ enableTranslation,
62
+ disableTranslation,
63
+ enableEvent,
64
+ disableEvent,
65
+ } = translationSlice.actions
66
+
67
+ export default translationSlice.reducer
@@ -0,0 +1,12 @@
1
+ import { operations } from 'schema'
2
+
3
+ export type Languages =
4
+ operations['getAccountConfig']['responses']['201']['content']['application/json']['config']['features']['translation']['languages']
5
+ export interface TranslationState {
6
+ isActive: boolean
7
+ currentLocale?: string
8
+ isAvailable: boolean
9
+ languages: Languages
10
+ originalPayloadIds: string[]
11
+ containerId: string
12
+ }
@@ -1,36 +1,38 @@
1
+ import { createAsyncThunk } from '@reduxjs/toolkit'
2
+ import { VisibilityOptions } from 'config.types'
3
+ import { selectUserHasResponded } from 'domains/app/selectors'
1
4
  import * as ConfigSelectors from 'domains/config/selectors'
2
- import * as AppSelectors from 'domains/app/selectors'
3
-
5
+ import { ThunkAPI } from 'domains/redux/redux.types'
6
+ import { setFromStorage } from 'domains/visibility/slice'
7
+ import { calculateVisibility } from 'domains/visibility/utils'
8
+ import { VisibilityState } from 'domains/visibility/visibility.types'
4
9
  import { selectState } from 'ui/hooks/seamly-state-hooks'
5
- import { visibilityStates, StoreKey } from './constants'
6
- import { createAction, createThunk, calculateVisibility } from './utils'
10
+ import { StoreKey, visibilityStates } from './constants'
7
11
  import * as Selectors from './selectors'
8
12
 
9
- export const setFromStorage = createAction('setFromStorage', (visibility) => ({
10
- visibility,
11
- }))
12
-
13
- export const setShowInlineView = createAction('setShowInlineView')
14
-
15
13
  const validVisibilityStates = [
16
14
  visibilityStates.open,
17
15
  visibilityStates.minimized,
18
16
  visibilityStates.hidden,
19
17
  ]
20
- export const setVisibility = createThunk(
21
- 'set',
18
+ export const setVisibility = createAsyncThunk<
19
+ VisibilityOptions,
20
+ VisibilityState,
21
+ ThunkAPI
22
+ >(
23
+ 'setVisibility',
22
24
  (requestedVisibility, { getState, extra: { api, eventBus } }) => {
23
25
  const state = getState()
24
26
  const previousVisibility = Selectors.selectVisibility(state)
25
- const hasResponded = AppSelectors.selectUserHasResponded(state)
27
+ const userHasResponded = selectUserHasResponded(state)
26
28
  const hasConversation = api.hasConversation()
27
29
  const config = ConfigSelectors.selectConfig(state)
30
+
28
31
  const { visibilityCallback = calculateVisibility, layoutMode } = config
29
32
  const { unreadEvents: unreadMessageCount } = selectState(state)
30
33
 
31
34
  const calculatedVisibility = visibilityCallback({
32
- hasConversation,
33
- hasResponded,
35
+ hasResponded: userHasResponded,
34
36
  previousVisibility,
35
37
  requestedVisibility,
36
38
  config,
@@ -53,23 +55,27 @@ export const setVisibility = createThunk(
53
55
  eventBus.emit('ui.visible', requestedVisibility, {
54
56
  visibility: requestedVisibility,
55
57
  hasConversation,
56
- hasResponded,
58
+ hasResponded: userHasResponded,
57
59
  unreadMessageCount,
58
60
  })
59
61
  }
62
+
60
63
  return calculatedVisibility
61
64
  },
62
65
  )
63
66
 
64
- export const initialize = createThunk(
67
+ export const initializeVisibility = createAsyncThunk<unknown, void, ThunkAPI>(
65
68
  'initialize',
66
- async (locale, { dispatch, getState, extra: { api } }) => {
69
+ async (_, { dispatch, getState, extra: { api } }) => {
67
70
  // initialize stored visibility
68
71
  const { layoutMode } = ConfigSelectors.selectConfig(getState())
72
+
69
73
  const storedVisibility = api.store.get(StoreKey)?.[layoutMode]
70
74
  if (storedVisibility) {
71
75
  dispatch(setFromStorage(storedVisibility))
72
76
  }
73
- dispatch(setVisibility(visibilityStates.initialize))
77
+
78
+ dispatch(setVisibility(storedVisibility))
79
+ return storedVisibility
74
80
  },
75
81
  )
@@ -1,19 +1,22 @@
1
+ import { useConfig } from 'domains/config/hooks'
2
+ import { useAppDispatch } from 'domains/store'
3
+ import { setVisibility } from 'domains/visibility/actions'
4
+ import { setShowInlineView } from 'domains/visibility/slice'
5
+ import { VisibilityState } from 'domains/visibility/visibility.types'
1
6
  import { useCallback, useEffect, useRef, useState } from 'preact/hooks'
2
- import { useSelector, useStoreDispatch } from 'domains/redux'
3
- import { useConfig } from 'domains/config'
4
- import * as Actions from './actions'
5
- import * as Selectors from './selectors'
7
+ import { useDispatch, useSelector } from 'react-redux'
6
8
  import { visibilityStates } from './constants'
9
+ import * as Selectors from './selectors'
7
10
 
8
11
  export const useVisibility = () => {
9
- const dispatch = useStoreDispatch()
12
+ const dispatch = useAppDispatch()
10
13
  const visible = useSelector(Selectors.selectVisibility)
11
14
  const isVisible = visible ? visible !== visibilityStates.hidden : false
12
15
  const isOpen = visible === visibilityStates.open
13
16
  const isMinimized = visible === visibilityStates.minimized
14
17
 
15
- const setVisibility = useCallback(
16
- (visibility) => dispatch(Actions.setVisibility(visibility)),
18
+ const dispatchVisibility = useCallback(
19
+ (visibility: VisibilityState) => dispatch(setVisibility(visibility)),
17
20
  [dispatch],
18
21
  )
19
22
 
@@ -22,7 +25,7 @@ export const useVisibility = () => {
22
25
  isOpen,
23
26
  isMinimized,
24
27
  visible,
25
- setVisibility,
28
+ setVisibility: dispatchVisibility,
26
29
  }
27
30
  }
28
31
 
@@ -66,7 +69,7 @@ const useIntersect = ({ freezeOnceVisible = false, enabled = true }) => {
66
69
  }
67
70
 
68
71
  export const useShowInlineView = () => {
69
- const dispatch = useStoreDispatch()
72
+ const dispatch = useDispatch()
70
73
  const { connectWhenInView } = useConfig()
71
74
  const showInlineView = useSelector(Selectors.selectShowInlineView)
72
75
 
@@ -78,7 +81,7 @@ export const useShowInlineView = () => {
78
81
  useEffect(() => {
79
82
  if (!isVisible) return
80
83
 
81
- dispatch(Actions.setShowInlineView())
84
+ dispatch(setShowInlineView())
82
85
  }, [dispatch, isVisible])
83
86
 
84
87
  return { containerRef, showInlineView }
@@ -1,14 +1,11 @@
1
- import { createSelector } from 'reselect'
2
- import { selectState } from './utils'
1
+ import { createSelector } from '@reduxjs/toolkit'
3
2
 
4
3
  export const selectVisibility = createSelector(
5
- selectState,
4
+ ({ visibility }) => visibility,
6
5
  (state) => state.visibility,
7
6
  )
8
7
 
9
8
  export const selectShowInlineView = createSelector(
10
- selectState,
9
+ ({ visibility }) => visibility,
11
10
  (state) => state.showInlineView,
12
11
  )
13
-
14
- export { selectState }