@seamly/web-ui 21.0.9 → 22.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/build/dist/lib/components.js +9228 -7777
  2. package/build/dist/lib/components.js.map +1 -0
  3. package/build/dist/lib/components.min.js +2 -1
  4. package/build/dist/lib/components.min.js.LICENSE.txt +2 -2
  5. package/build/dist/lib/components.min.js.map +1 -0
  6. package/build/dist/lib/config.js +2 -1
  7. package/build/dist/lib/config.js.map +1 -0
  8. package/build/dist/lib/config.min.js +2 -1
  9. package/build/dist/lib/config.min.js.map +1 -0
  10. package/build/dist/lib/contexts.js +2 -1
  11. package/build/dist/lib/contexts.js.map +1 -0
  12. package/build/dist/lib/contexts.min.js +2 -1
  13. package/build/dist/lib/contexts.min.js.map +1 -0
  14. package/build/dist/lib/deprecated-view.css +1 -1
  15. package/build/dist/lib/deprecated-view.js +1 -1
  16. package/build/dist/lib/hooks.js +6999 -5996
  17. package/build/dist/lib/hooks.js.map +1 -0
  18. package/build/dist/lib/hooks.min.js +2 -1
  19. package/build/dist/lib/hooks.min.js.map +1 -0
  20. package/build/dist/lib/index.debug.js +940 -370
  21. package/build/dist/lib/index.debug.js.map +1 -0
  22. package/build/dist/lib/index.debug.min.js +2 -1
  23. package/build/dist/lib/index.debug.min.js.LICENSE.txt +334 -110
  24. package/build/dist/lib/index.debug.min.js.map +1 -0
  25. package/build/dist/lib/index.js +2810 -5472
  26. package/build/dist/lib/index.js.map +1 -0
  27. package/build/dist/lib/index.min.js +2 -1
  28. package/build/dist/lib/index.min.js.LICENSE.txt +2 -2
  29. package/build/dist/lib/index.min.js.map +1 -0
  30. package/build/dist/lib/sounds/beep.mp3 +0 -0
  31. package/build/dist/lib/standalone.js +10575 -13540
  32. package/build/dist/lib/standalone.js.map +1 -0
  33. package/build/dist/lib/standalone.min.js +2 -1
  34. package/build/dist/lib/standalone.min.js.LICENSE.txt +1 -1
  35. package/build/dist/lib/standalone.min.js.map +1 -0
  36. package/build/dist/lib/storage.js +2 -1
  37. package/build/dist/lib/storage.js.map +1 -0
  38. package/build/dist/lib/storage.min.js +2 -1
  39. package/build/dist/lib/storage.min.js.map +1 -0
  40. package/build/dist/lib/style-guide.js +1701 -5859
  41. package/build/dist/lib/style-guide.js.map +1 -0
  42. package/build/dist/lib/style-guide.min.js +2 -1
  43. package/build/dist/lib/style-guide.min.js.LICENSE.txt +2 -2
  44. package/build/dist/lib/style-guide.min.js.map +1 -0
  45. package/build/dist/lib/styles-default-implementation.css +1 -1
  46. package/build/dist/lib/styles-default-implementation.js +1 -1
  47. package/build/dist/lib/styles.css +1 -1
  48. package/build/dist/lib/styles.js +1 -1
  49. package/build/dist/lib/utils.js +11536 -14530
  50. package/build/dist/lib/utils.js.map +1 -0
  51. package/build/dist/lib/utils.min.js +2 -1
  52. package/build/dist/lib/utils.min.js.LICENSE.txt +1 -6
  53. package/build/dist/lib/utils.min.js.map +1 -0
  54. package/package.json +58 -48
  55. package/src/javascripts/api/conversation-connector.ts +2 -0
  56. package/src/javascripts/api/errors/seamly-api-error.ts +15 -0
  57. package/src/javascripts/api/index.ts +168 -94
  58. package/src/javascripts/config.ts +1 -1
  59. package/src/javascripts/config.types.ts +18 -11
  60. package/src/javascripts/domains/config/selectors.ts +1 -1
  61. package/src/javascripts/domains/config/slice.ts +12 -0
  62. package/src/javascripts/domains/forms/forms.types.ts +1 -0
  63. package/src/javascripts/domains/forms/hooks.ts +10 -2
  64. package/src/javascripts/domains/store/selectors.ts +23 -10
  65. package/src/javascripts/domains/store/slice.ts +63 -11
  66. package/src/javascripts/domains/store/store.types.ts +41 -1
  67. package/src/javascripts/domains/translations/components/options-button.tsx +1 -4
  68. package/src/javascripts/domains/translations/hooks.ts +11 -4
  69. package/src/javascripts/index.ts +2 -0
  70. package/src/javascripts/lib/url-helpers.ts +24 -0
  71. package/src/javascripts/schema.ts +10 -0
  72. package/src/javascripts/style-guide/states.js +109 -0
  73. package/src/javascripts/ui/components/conversation/conversation.tsx +2 -0
  74. package/src/javascripts/ui/components/conversation/event/chat-scroll/chat-scroll-provider.tsx +2 -0
  75. package/src/javascripts/ui/components/conversation/event/choice-prompt.js +1 -1
  76. package/src/javascripts/ui/components/conversation/event/text.js +1 -1
  77. package/src/javascripts/ui/components/conversation/event/upload.js +50 -9
  78. package/src/javascripts/ui/components/conversation/use-chat-scroll.ts +3 -2
  79. package/src/javascripts/ui/components/core/seamly-event-subscriber.ts +7 -1
  80. package/src/javascripts/ui/components/core/seamly-file-upload.tsx +156 -0
  81. package/src/javascripts/ui/components/entry/abort-transaction-button/abort-transaction-button.tsx +45 -0
  82. package/src/javascripts/ui/components/entry/text-entry/hooks.ts +108 -0
  83. package/src/javascripts/ui/components/entry/text-entry/index.js +7 -4
  84. package/src/javascripts/ui/components/entry/text-entry/{text-entry-form.js → text-entry-form.tsx} +8 -22
  85. package/src/javascripts/ui/components/form-controls/{input.js → input.tsx} +13 -2
  86. package/src/javascripts/ui/components/form-controls/{wrapper.js → wrapper.tsx} +8 -4
  87. package/src/javascripts/ui/components/view/{index.js → index.tsx} +53 -4
  88. package/src/javascripts/ui/components/view/window-view/{index.js → index.tsx} +14 -2
  89. package/src/javascripts/ui/components/widgets/{in-out-transition.js → in-out-transition.tsx} +67 -28
  90. package/src/javascripts/ui/hooks/{seamly-api-hooks.js → seamly-api-hooks.ts} +1 -1
  91. package/src/javascripts/ui/hooks/sounds/beep.mp3 +0 -0
  92. package/src/javascripts/ui/hooks/use-click-outside.ts +5 -3
  93. package/src/javascripts/ui/hooks/use-notifications.ts +114 -0
  94. package/src/javascripts/ui/hooks/use-timeout.ts +20 -0
  95. package/src/stylesheets/3-chat/_chat.scss +3 -5
  96. package/src/stylesheets/4-base/_formelements.scss +0 -36
  97. package/src/stylesheets/5-components/_abort-transaction.scss +10 -0
  98. package/src/stylesheets/5-components/_buttons.scss +18 -3
  99. package/src/stylesheets/5-components/_character-limit.scss +2 -2
  100. package/src/stylesheets/5-components/_chat-status.scss +26 -37
  101. package/src/stylesheets/5-components/_choice-prompt.scss +9 -10
  102. package/src/stylesheets/5-components/_conversation.scss +9 -62
  103. package/src/stylesheets/5-components/_disclaimer.scss +11 -3
  104. package/src/stylesheets/5-components/_error.scss +3 -2
  105. package/src/stylesheets/5-components/_idle.scss +3 -8
  106. package/src/stylesheets/5-components/_input.scss +34 -13
  107. package/src/stylesheets/5-components/_interrupt.scss +3 -10
  108. package/src/stylesheets/5-components/_loader.scss +1 -2
  109. package/src/stylesheets/5-components/_message-author.scss +2 -4
  110. package/src/stylesheets/5-components/_message-body.scss +33 -10
  111. package/src/stylesheets/5-components/_message-card.scss +2 -10
  112. package/src/stylesheets/5-components/_message-carousel.scss +4 -4
  113. package/src/stylesheets/5-components/_message-cta.scss +0 -6
  114. package/src/stylesheets/5-components/_message.scss +1 -0
  115. package/src/stylesheets/5-components/_modal.scss +2 -5
  116. package/src/stylesheets/5-components/_options.scss +17 -22
  117. package/src/stylesheets/5-components/_pre-chat-messages.scss +3 -1
  118. package/src/stylesheets/5-components/_prompt.scss +3 -7
  119. package/src/stylesheets/5-components/_skip-link.scss +2 -1
  120. package/src/stylesheets/5-components/_suggestions.scss +2 -2
  121. package/src/stylesheets/5-components/_translation-options.scss +5 -2
  122. package/src/stylesheets/5-components/_unread-messages.scss +33 -0
  123. package/src/stylesheets/5-components/_upload.scss +20 -27
  124. package/src/stylesheets/6-default-implementation/_hover.scss +14 -17
  125. package/src/stylesheets/7-deprecated/1-settings/_config.scss +17 -0
  126. package/src/stylesheets/7-deprecated/3-app/_app.scss +2 -1
  127. package/src/stylesheets/7-deprecated/5-components/_card.scss +1 -0
  128. package/src/stylesheets/7-deprecated/5-components/_chat-status.scss +66 -20
  129. package/src/stylesheets/7-deprecated/5-components/_conversation.scss +1 -4
  130. package/src/stylesheets/7-deprecated/5-components/_input.scss +6 -1
  131. package/src/stylesheets/7-deprecated/5-components/_interrupt.scss +1 -4
  132. package/src/stylesheets/7-deprecated/5-components/_message.scss +49 -12
  133. package/src/stylesheets/7-deprecated/5-components/_translation-options.scss +30 -37
  134. package/src/stylesheets/7-deprecated/5-components/_unread-messages.scss +38 -0
  135. package/src/stylesheets/deprecated-view.scss +1 -0
  136. package/src/stylesheets/styles.scss +2 -0
  137. package/webpack/config.common.js +6 -1
  138. package/webpack/config.package.js +18 -0
  139. package/webpack/defaults.js +1 -1
  140. package/src/javascripts/ui/components/core/seamly-file-upload.js +0 -86
  141. package/src/javascripts/ui/components/entry/text-entry/hooks.js +0 -46
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seamly/web-ui",
3
- "version": "21.0.9",
3
+ "version": "22.0.0",
4
4
  "main": "build/dist/lib/index.js",
5
5
  "types": "build/src/javascripts/index.d.ts",
6
6
  "module": "",
@@ -22,79 +22,89 @@
22
22
  "webpack/*"
23
23
  ],
24
24
  "dependencies": {
25
- "@reduxjs/toolkit": "^1.8.6",
25
+ "@reduxjs/toolkit": "^1.9.3",
26
26
  "@ultraq/icu-message-formatter": "^0.12.0",
27
+ "core-js": "^3.30.0",
27
28
  "deep-keys": "^0.5.0",
28
- "focus-trap": "^7.0.0",
29
- "include-media": "^1.4.10",
29
+ "focus-trap": "^7.4.0",
30
+ "include-media": "^2.0.0",
30
31
  "js-cookie": "^3.0.1",
31
32
  "minivents": "^2.2.1",
32
- "phoenix": "1.6.15",
33
- "react-redux": "^8.0.4",
34
- "superagent": "^8.0.3"
33
+ "phoenix": "^1.7.2",
34
+ "react-redux": "^8.0.5"
35
35
  },
36
36
  "devDependencies": {
37
- "@babel/core": "^7.19.6",
38
- "@babel/plugin-proposal-object-rest-spread": "^7.19.4",
37
+ "@babel/core": "^7.21.4",
38
+ "@babel/plugin-proposal-object-rest-spread": "^7.20.7",
39
39
  "@babel/plugin-syntax-dynamic-import": "^7.8.3",
40
- "@babel/plugin-transform-classes": "^7.19.0",
41
- "@babel/plugin-transform-react-jsx": "^7.19.0",
42
- "@babel/plugin-transform-runtime": "^7.19.6",
43
- "@babel/preset-env": "^7.19.4",
40
+ "@babel/plugin-transform-classes": "^7.21.0",
41
+ "@babel/plugin-transform-react-jsx": "^7.21.0",
42
+ "@babel/plugin-transform-runtime": "^7.21.4",
43
+ "@babel/preset-env": "^7.21.4",
44
44
  "@babel/preset-react": "^7.18.6",
45
- "@babel/preset-typescript": "^7.18.6",
46
- "@babel/runtime-corejs3": "^7.18.9",
47
- "@playwright/test": "^1.29.2",
45
+ "@babel/preset-typescript": "^7.21.4",
46
+ "@babel/runtime-corejs3": "^7.21.0",
47
+ "@playwright/test": "^1.32.2",
48
48
  "@seamly/doc-site": "^2.0.0",
49
49
  "@seamly/eslint-config": "^2.3.0",
50
50
  "@seamly/prettier-config": "^2.2.0",
51
51
  "@seamly/stylelint-config": "^2.0.0",
52
52
  "@testing-library/jest-dom": "^5.16.5",
53
- "@testing-library/preact": "^3.2.2",
54
- "@trivago/prettier-plugin-sort-imports": "^3.4.0",
55
- "@types/jest": "^27.5.2",
56
- "@types/phoenix": "^1.5.4",
57
- "@typescript-eslint/eslint-plugin": "^5.42.0",
58
- "@typescript-eslint/parser": "^5.42.0",
59
- "babel-jest": "^27.3.1",
60
- "babel-loader": "^9.0.1",
53
+ "@testing-library/preact": "^3.2.3",
54
+ "@trivago/prettier-plugin-sort-imports": "^4.1.1",
55
+ "@types/core-js": "^2.5.5",
56
+ "@types/jest": "^29.5.0",
57
+ "@types/phoenix": "^1.5.5",
58
+ "@typescript-eslint/eslint-plugin": "^5.57.1",
59
+ "@typescript-eslint/parser": "^5.57.1",
60
+ "babel-jest": "^29.5.0",
61
+ "babel-loader": "^9.1.2",
61
62
  "babel-plugin-istanbul": "^6.1.1",
62
63
  "babel-plugin-react-remove-properties": "^0.3.0",
63
64
  "copy-webpack-plugin": "^11.0.0",
64
65
  "debug": "^4.3.4",
65
- "eslint": "^8.27.0",
66
+ "eslint": "^8.37.0",
67
+ "eslint-config-prettier": "^8.8.0",
66
68
  "eslint-import-resolver-alias": "^1.1.2",
67
- "eslint-import-resolver-typescript": "^3.5.2",
68
- "eslint-plugin-import": "^2.26.0",
69
- "eslint-plugin-jest": "^27.1.3",
69
+ "eslint-import-resolver-typescript": "^3.5.5",
70
+ "eslint-plugin-filenames": "^1.3.2",
71
+ "eslint-plugin-import": "^2.27.5",
72
+ "eslint-plugin-jest": "^27.2.1",
73
+ "eslint-plugin-prettier": "^4.2.1",
74
+ "eslint-plugin-react": "^7.32.2",
75
+ "eslint-plugin-react-hooks": "^4.6.0",
70
76
  "file-loader": "^6.2.0",
71
- "glob": "^8.0.3",
72
- "husky": "^8.0.1",
77
+ "fork-ts-checker-webpack-plugin": "^8.0.0",
78
+ "glob": "^9.3.4",
79
+ "husky": "^8.0.3",
73
80
  "ignore-loader": "^0.1.2",
74
- "jest": "^27.5.1",
75
- "jest-environment-jsdom": "^27.5.1",
76
- "mini-css-extract-plugin": "^2.6.1",
81
+ "isomorphic-unfetch": "^4.0.2",
82
+ "jest": "^29.5.0",
83
+ "jest-environment-jsdom": "^29.5.0",
84
+ "jest-watch-typeahead": "^2.2.2",
85
+ "mini-css-extract-plugin": "^2.7.5",
77
86
  "nyc": "^15.1.0",
78
- "playwright-test-coverage": "^1.2.8",
79
- "postcss": "8",
80
- "preact": "^10.11.2",
81
- "prettier": "^2.7.1",
87
+ "playwright-test-coverage": "^1.2.12",
88
+ "postcss": "^8.4.21",
89
+ "preact": "^10.13.2",
90
+ "preact-render-to-string": "^6.0.2",
91
+ "prettier": "^2.8.7",
82
92
  "raw-loader": "^4.0.2",
83
- "rimraf": "^3.0.2",
84
- "start-server-and-test": "^1.14.0",
85
- "style-loader": "^3.3.1",
86
- "stylelint": "^14.14.0",
87
- "ts-loader": "^9.4.1",
88
- "typescript": "^4.8.4",
93
+ "rimraf": "^4.4.1",
94
+ "start-server-and-test": "^2.0.0",
95
+ "style-loader": "^3.3.2",
96
+ "stylelint": "^15.4.0",
97
+ "ts-loader": "^9.4.2",
98
+ "typescript": "^5.0.3",
89
99
  "url-loader": "^4.1.1",
90
- "webpack": "^5.74.0",
91
- "webpack-bundle-analyzer": "^4.7.0",
92
- "webpack-cli": "^4.10.0",
93
- "webpack-dev-server": "^4.11.1",
100
+ "webpack": "^5.78.0",
101
+ "webpack-bundle-analyzer": "^4.8.0",
102
+ "webpack-cli": "^5.0.1",
103
+ "webpack-dev-server": "^4.13.2",
94
104
  "webpack-merge": "^5.8.0"
95
105
  },
96
106
  "peerDependencies": {
97
- "preact": "^10.11.2"
107
+ "preact": "^10.13.2"
98
108
  },
99
109
  "scripts": {
100
110
  "build:clean": "rimraf build; mkdir -p build",
@@ -11,6 +11,7 @@ type CurrentConnectionState =
11
11
  | 'join_channel_succeeded' // Joined channel succesfully.
12
12
  | 'join_channel_erred' // Error on joining channel. This is usefull when you want to restart a chat sessions.
13
13
  | 'attach_channel_succeeded' // Attached channel to system.
14
+ | 'attach_channel_erred' // Error attaching channel.
14
15
 
15
16
  type ConnectionState = {
16
17
  connected: boolean
@@ -98,6 +99,7 @@ export default class ConversationConnector {
98
99
 
99
100
  this.channel.onError((msg) => {
100
101
  log('[CHANNEL][ERROR]', msg)
102
+
101
103
  this.#emitConnectionState({
102
104
  connected: false,
103
105
  ready: false,
@@ -0,0 +1,15 @@
1
+ // @ts-ignore
2
+ // eslint-disable-next-line no-undef
3
+ interface ApiErrorOptions extends ErrorOptions {
4
+ status?: number
5
+ }
6
+
7
+ export default class ApiError extends Error {
8
+ status: number
9
+
10
+ constructor(message: string, options?: ApiErrorOptions) {
11
+ // @ts-ignore
12
+ super(message, { cause: options?.cause })
13
+ this.status = options?.status
14
+ }
15
+ }
@@ -1,7 +1,7 @@
1
- import type { ApiConfig, Config, Context, LayoutMode } from 'config.types'
1
+ import type { ApiConfig, Context, LayoutMode } from 'config.types'
2
2
  import { components, operations } from 'schema'
3
- import superagent from 'superagent'
4
3
  import { apiVersion } from 'config'
4
+ import ApiError from 'api/errors/seamly-api-error'
5
5
  import SeamlyConfigurationError from 'api/errors/seamly-configuration-error'
6
6
  import SeamlyGeneralError from 'api/errors/seamly-general-error'
7
7
  import SeamlySessionExpiredError from 'api/errors/seamly-session-expired-error'
@@ -20,7 +20,7 @@ declare let PACKAGE_VERSION: string
20
20
  const log = debug('seamly')
21
21
 
22
22
  const DOMAIN = 'api.seamly-app.com'
23
- const TRANSLATIONS_VERSION = 2
23
+ const TRANSLATIONS_VERSION = 4
24
24
 
25
25
  function buildPayload(command, payload) {
26
26
  if (command !== 'message') {
@@ -37,6 +37,26 @@ function buildPayload(command, payload) {
37
37
  return { type, body, transactionId }
38
38
  }
39
39
 
40
+ const fetchApi = async (
41
+ input: string,
42
+ // eslint-disable-next-line no-undef
43
+ init: RequestInit,
44
+ ): Promise<Response> => {
45
+ const url = new URL(input)
46
+ url.searchParams.set('v', apiVersion)
47
+
48
+ const response = await fetch(url.href, { mode: 'cors', ...init })
49
+
50
+ if (!response.ok) {
51
+ throw new ApiError(
52
+ response.statusText || `Request failed with status: ${response.status}`,
53
+ { status: response.status },
54
+ )
55
+ }
56
+
57
+ return response
58
+ }
59
+
40
60
  /**
41
61
  * Tries to get the time zone key directly from the operating system for those
42
62
  * environments that support the ECMAScript Internationalization API.
@@ -80,11 +100,16 @@ type InitialConversation = Omit<
80
100
  >
81
101
  export type ConversationHistoryResponse = Omit<
82
102
  components['schemas']['ConversationHistoryResponse']['history'],
83
- 'messages' | 'ui.resumeConversationPrompt'
103
+ 'messages' | 'ui.resumeConversationPrompt' | 'activeServiceSettings'
84
104
  > & {
85
105
  resumeConversationPrompt: components['schemas']['ConversationHistoryResponse']['history']['ui']['resumeConversationPrompt']
86
106
  translationProposal: components['schemas']['ConversationHistoryResponse']['history']['ui']['translationProposal']
87
107
  events: ChannelEvent[]
108
+ activeServiceSettings: components['schemas']['ConversationHistoryResponse']['history']['activeServiceSettings'] & {
109
+ proactiveMessages?: {
110
+ enabled: boolean
111
+ }
112
+ }
88
113
  }
89
114
 
90
115
  export class API {
@@ -94,6 +119,8 @@ export class API {
94
119
 
95
120
  #externalId: string
96
121
 
122
+ #conversationAuthToken?: string
123
+
97
124
  #layoutMode: LayoutMode
98
125
 
99
126
  #config: ApiConfig & {
@@ -116,7 +143,7 @@ export class API {
116
143
 
117
144
  locale: string
118
145
 
119
- conversation?: ConversationConnector = new ConversationConnector()
146
+ conversation: ConversationConnector = new ConversationConnector()
120
147
 
121
148
  constructor({
122
149
  layoutMode,
@@ -238,18 +265,29 @@ export class API {
238
265
 
239
266
  async #createConversation(): Promise<InitialConversation> {
240
267
  try {
241
- const request = superagent
242
- .post(`${this.#getUrlPrefix('http')}${this.URLS.conversations}`)
243
- .set('Content-Type', 'application/json')
244
- .query({ v: apiVersion })
245
- // withCredentials() is necessary to allow browsers to save received
246
- // cookies in CORS requests.
247
- .withCredentials()
248
- .send({
249
- externalId: this.#externalId || undefined,
250
- })
251
-
252
- const { body } = await request
268
+ if (typeof this.#config?.getConversationAuthToken === 'function') {
269
+ this.#conversationAuthToken =
270
+ await this.#config.getConversationAuthToken()
271
+ }
272
+
273
+ const response = await fetchApi(
274
+ `${this.#getUrlPrefix('http')}${this.URLS.conversations}`,
275
+ {
276
+ method: 'POST',
277
+ credentials: 'include',
278
+ headers: {
279
+ 'Content-Type': 'application/json',
280
+ },
281
+ body: JSON.stringify({
282
+ externalId: this.#conversationAuthToken
283
+ ? undefined
284
+ : this.#externalId,
285
+ token: this.#conversationAuthToken,
286
+ }),
287
+ },
288
+ )
289
+
290
+ const body = await response.json()
253
291
 
254
292
  const { conversation } = body
255
293
  const initialState = { ...conversation }
@@ -268,57 +306,73 @@ export class API {
268
306
  throw new SeamlyGeneralError(error)
269
307
  }
270
308
 
309
+ if (error.status === 400) {
310
+ throw new SeamlyUnauthorizedError(error)
311
+ }
312
+
313
+ if (error.status === 404) {
314
+ throw new SeamlyConfigurationError(error)
315
+ }
316
+
271
317
  throw error
272
318
  }
273
319
  }
274
320
 
275
- getConfig(): Promise<Config> {
276
- return superagent
277
- .post(
321
+ async getConfig() {
322
+ try {
323
+ const response = await fetchApi(
278
324
  `${this.#getUrlPrefix('http')}/client/${
279
325
  this.connectionInfo.apiKey
280
326
  }/configs`,
281
- )
282
- .set('Content-Type', 'application/json')
283
- .query({ v: apiVersion })
284
- .send({
285
- context: {
286
- ...this.#config.context,
287
- environment:
288
- this.#config.sendEnvironment === true
289
- ? this.#getEnvironment()
290
- : this.#config.sendEnvironment,
327
+ {
328
+ method: 'POST',
329
+ headers: {
330
+ 'Content-Type': 'application/json',
331
+ },
332
+ body: JSON.stringify({
333
+ context: {
334
+ ...this.#config.context,
335
+ environment:
336
+ this.#config.sendEnvironment === true
337
+ ? this.#getEnvironment()
338
+ : this.#config.sendEnvironment,
339
+ },
340
+ }),
291
341
  },
292
- })
293
- .then(({ body }) => {
294
- this.#updateUrls(body)
295
- this.configReady = true
296
- return body.config
297
- })
298
- .catch((error) => {
299
- if (error.status === 404) {
300
- throw new SeamlyConfigurationError(error)
301
- }
342
+ )
343
+ const body = await response.json()
344
+ this.#updateUrls(body)
345
+ this.configReady = true
346
+ return body.config
347
+ } catch (error) {
348
+ if (error.status === 404) {
349
+ throw new SeamlyConfigurationError(error)
350
+ }
302
351
 
303
- if (error.status >= 500) {
304
- throw new SeamlyGeneralError(error)
305
- }
352
+ if (error.status >= 500) {
353
+ throw new SeamlyGeneralError(error)
354
+ }
306
355
 
307
- throw error
308
- })
356
+ throw error
357
+ }
309
358
  }
310
359
 
311
- async getConversation(): Promise<ConversationHistoryResponse> {
360
+ async getConversation(): Promise<ConversationHistoryResponse | null> {
312
361
  if (!this.hasConversation()) {
313
362
  return null
314
363
  }
315
364
 
316
365
  try {
317
- const { body } = await superagent
318
- .get(`${this.#getUrlPrefix('http')}${this.URLS.history}`)
319
- .set('Authorization', `Bearer ${this.#getAccessToken()}`)
320
- .query({ v: apiVersion })
321
-
366
+ const response = await fetchApi(
367
+ `${this.#getUrlPrefix('http')}${this.URLS.history}`,
368
+ {
369
+ method: 'GET',
370
+ headers: {
371
+ Authorization: `Bearer ${this.#getAccessToken()}`,
372
+ },
373
+ },
374
+ )
375
+ const body = await response.json()
322
376
  this.#updateUrls(body)
323
377
 
324
378
  const {
@@ -408,58 +462,76 @@ export class API {
408
462
  const formData = new FormData()
409
463
  formData.append('upload', file)
410
464
 
411
- const req = superagent
412
- .post(`${this.#getUrlPrefix('http')}${this.URLS.uploads}`)
413
- .set('Authorization', `Bearer ${this.#getAccessToken()}`)
414
- .send(formData)
465
+ const xhr = new XMLHttpRequest()
466
+ xhr.open('POST', `${this.#getUrlPrefix('http')}${this.URLS.uploads}`)
467
+ xhr.setRequestHeader('Authorization', `Bearer ${this.#getAccessToken()}`)
415
468
 
416
- req.on('progress', function (e) {
417
- const { direction, percent } = e
418
- if (direction === 'upload' && typeof progressCallback === 'function') {
469
+ xhr.upload.onprogress = (event) => {
470
+ if (typeof progressCallback === 'function') {
471
+ const percent = Math.ceil((event.loaded / event.total) * 100)
419
472
  progressCallback(percent)
420
473
  }
421
- })
474
+ }
422
475
 
423
- req
424
- .then((uploadResponse) => {
476
+ xhr.onloadend = () => {
477
+ // status is set to 0 when upload is aborted.
478
+ if (xhr.status === 0) return
479
+
480
+ if (xhr.status === 200 || xhr.status === 201) {
425
481
  if (successCallback) {
426
- successCallback(uploadResponse)
482
+ try {
483
+ successCallback(JSON.parse(xhr.response))
484
+ } catch (_) {
485
+ successCallback(xhr.response)
486
+ }
487
+ return
427
488
  }
428
- })
429
- .catch((err) => {
430
- if (errorCallback) {
431
- errorCallback(err.response)
432
- } else {
433
- throw err
489
+ }
490
+
491
+ if (errorCallback) {
492
+ try {
493
+ errorCallback(JSON.parse(xhr.response))
494
+ } catch (_) {
495
+ errorCallback(xhr.response)
434
496
  }
435
- })
497
+ } else {
498
+ throw new Error(xhr.response)
499
+ }
500
+ }
436
501
 
437
- return req
502
+ xhr.send(formData)
503
+ return xhr
438
504
  }
439
505
 
440
- getConversationIntitialState() {
441
- return superagent
442
- .get(`${this.#getUrlPrefix('http')}${this.getConversationUrl()}`)
443
- .set('Authorization', `Bearer ${this.#getAccessToken()}`)
444
- .query({ v: apiVersion })
445
- .then(({ body }) => {
446
- this.#updateUrls(body)
447
- this.userResponded = body.conversation.userResponded
448
- return omit(body.conversation, ['accessToken', 'channelTopic'])
449
- })
450
- .catch((error) => {
451
- if (error.status === 401) {
452
- throw new SeamlyUnauthorizedError(error)
453
- }
454
- if (error.status === 404) {
455
- throw new SeamlySessionExpiredError(error)
456
- }
457
- if (error.status >= 500) {
458
- throw new SeamlyGeneralError(error)
459
- }
506
+ async getConversationIntitialState() {
507
+ try {
508
+ const response = await fetchApi(
509
+ `${this.#getUrlPrefix('http')}${this.getConversationUrl()}`,
510
+ {
511
+ method: 'GET',
512
+ headers: {
513
+ Authorization: `Bearer ${this.#getAccessToken()}`,
514
+ },
515
+ },
516
+ )
460
517
 
461
- throw error
462
- })
518
+ const body = await response.json()
519
+
520
+ this.#updateUrls(body)
521
+ this.userResponded = body.conversation.userResponded
522
+ return omit(body.conversation, ['accessToken', 'channelTopic'])
523
+ } catch (error) {
524
+ if (error.status === 401) {
525
+ throw new SeamlyUnauthorizedError(error)
526
+ }
527
+ if (error.status === 404) {
528
+ throw new SeamlySessionExpiredError(error)
529
+ }
530
+ if (error.status >= 500) {
531
+ throw new SeamlyGeneralError(error)
532
+ }
533
+ throw error
534
+ }
463
535
  }
464
536
 
465
537
  async getTranslations(locale: string): Promise<Record<string, string>> {
@@ -471,9 +543,11 @@ export class API {
471
543
  const url = `${this.#getUrlPrefix('http')}${this.URLS.translations}`
472
544
  .replace('{version}', String(TRANSLATIONS_VERSION))
473
545
  .replace('{locale}', this.#getLocale(locale))
474
- const request = superagent.get(url)
475
546
 
476
- const { body } = await request
547
+ const response = await fetchApi(url, {
548
+ method: 'GET',
549
+ })
550
+ const body = await response.json()
477
551
  return body.translations
478
552
  } catch (error) {
479
553
  if (error.status >= 500) {
@@ -2,7 +2,7 @@ import type { Config } from 'config.types'
2
2
 
3
3
  export const CSS_NAME = 'cvco'
4
4
 
5
- export const apiVersion = '2'
5
+ export const apiVersion = '3'
6
6
 
7
7
  export const userParticipantId = 'seamly-client-participant'
8
8
 
@@ -1,3 +1,4 @@
1
+ import { operations } from 'schema'
1
2
  import { FunctionComponent } from 'preact'
2
3
  import type { ChannelEvent } from 'domains/store/store.types'
3
4
 
@@ -8,6 +9,7 @@ export type ApiConfig = {
8
9
  sendEnvironment?: boolean
9
10
  storageProvider?: unknown
10
11
  externalId?: string
12
+ getConversationAuthToken?: Function
11
13
  }
12
14
 
13
15
  export type LayoutMode = 'inline' | 'window' | 'app' | null
@@ -47,13 +49,11 @@ export type Context = {
47
49
  variables?: Record<string, unknown>
48
50
  }
49
51
 
50
- export type AgentParticipant = {
51
- name: string
52
- avatar: string
53
- }
54
- export type UserParticipant = {
55
- avatar: string
56
- }
52
+ export type ConfigResponse =
53
+ operations['getAccountConfig']['responses']['201']['content']['application/json']['config']
54
+
55
+ export type AgentParticipant = ConfigResponse['agentParticipant']
56
+ export type UserParticipant = ConfigResponse['userParticipant']
57
57
 
58
58
  export type VisibilityCallbackOptions = {
59
59
  hasConversation: boolean
@@ -67,7 +67,14 @@ export type VisibilityCallback = (
67
67
  _options: VisibilityCallbackOptions,
68
68
  ) => VisibilityOptions
69
69
 
70
- export type Config = {
70
+ type PreChatTransitionOptions =
71
+ | {
72
+ enterDelay: number
73
+ exitAfter: number
74
+ }
75
+ | boolean
76
+
77
+ export type Config = Omit<ConfigResponse, 'preChat'> & {
71
78
  parentElement?: HTMLElement
72
79
  namespace?: string
73
80
  customComponents?: {
@@ -85,14 +92,14 @@ export type Config = {
85
92
  zIndex?: number
86
93
  showFaq?: boolean
87
94
  showSuggestions?: boolean
95
+ continueChat?: PreChatTransitionOptions
96
+ preChat?: PreChatTransitionOptions
88
97
  context?: Context
89
98
  messages?: Messages
90
99
  defaults?: {
91
100
  visible: VisibilityOptions
92
101
  }
93
102
  preChatEvents?: ChannelEvent[]
94
- agentParticipant?: AgentParticipant
95
- userParticipant?: UserParticipant
96
- startChatIcon?: string
97
103
  visible?: VisibilityOptions
104
+ notificationAudioURL?: string | undefined | boolean
98
105
  }
@@ -10,7 +10,7 @@ export const selectConfig = createSelector(
10
10
  visible: (config?.layoutMode === 'inline'
11
11
  ? visibilityStates.open
12
12
  : visibilityStates.minimized) as VisibilityOptions,
13
- appContainerClassNames: config.appContainerClassNames || [],
13
+ appContainerClassNames: config?.appContainerClassNames || [],
14
14
  ...config,
15
15
  }
16
16
  if (typeof newConfig.appContainerClassNames === 'function') {
@@ -12,11 +12,20 @@ export const initialConfigState: Config = {
12
12
  key: '',
13
13
  secure: true,
14
14
  },
15
+ notificationAudioURL: undefined,
15
16
  hideOnNoUserResponse: false,
16
17
  connectWhenInView: true,
17
18
  showDisclaimer: false,
18
19
  showFaq: false,
19
20
  showSuggestions: true,
21
+ preChat: {
22
+ enterDelay: 1000,
23
+ exitAfter: 4000,
24
+ },
25
+ continueChat: {
26
+ enterDelay: 0,
27
+ exitAfter: 2000,
28
+ },
20
29
  customComponents: {},
21
30
  defaults: {
22
31
  visible: null,
@@ -30,6 +39,8 @@ const configKeys: (keyof Config)[] = [
30
39
  'showDisclaimer',
31
40
  'showFaq',
32
41
  'showSuggestions',
42
+ 'continueChat',
43
+ 'preChat',
33
44
  'namespace',
34
45
  'customComponents',
35
46
  'defaults',
@@ -45,6 +56,7 @@ const configKeys: (keyof Config)[] = [
45
56
  'agentParticipant',
46
57
  'userParticipant',
47
58
  'startChatIcon',
59
+ 'notificationAudioURL',
48
60
  ]
49
61
 
50
62
  const updateState = (state, config: Config): Config => {
@@ -14,6 +14,7 @@ export type ControlState = {
14
14
  value?: string
15
15
  touched?: boolean
16
16
  name?: string
17
+ error?: unknown
17
18
  }
18
19
 
19
20
  export interface FormState {