cozy-ui 111.21.0 → 112.1.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 (168) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/package.json +4 -3
  3. package/react/FileImageLoader/Readme.md +66 -3
  4. package/react/FileImageLoader/index.jsx +3 -3
  5. package/react/FileImageLoader/index.spec.jsx +1 -1
  6. package/react/hooks/useClientErrors.jsx +140 -0
  7. package/react/hooks/useClientErrors.spec.jsx +102 -0
  8. package/react/index.js +0 -1
  9. package/transpiled/react/FileImageLoader/index.js +3 -3
  10. package/transpiled/react/hooks/useClientErrors.js +167 -0
  11. package/transpiled/react/index.js +0 -1
  12. package/transpiled/react/stylesheet.css +1 -1
  13. package/react/Viewer/Footer/BottomSheetContent.jsx +0 -29
  14. package/react/Viewer/Footer/DownloadButton.jsx +0 -67
  15. package/react/Viewer/Footer/FooterActionButtons.jsx +0 -22
  16. package/react/Viewer/Footer/FooterActionButtons.spec.jsx +0 -30
  17. package/react/Viewer/Footer/FooterContent.jsx +0 -99
  18. package/react/Viewer/Footer/ForwardButton.jsx +0 -95
  19. package/react/Viewer/Footer/ForwardButton.spec.jsx +0 -87
  20. package/react/Viewer/Footer/ForwardOrDownloadButton.jsx +0 -24
  21. package/react/Viewer/Footer/Sharing.jsx +0 -60
  22. package/react/Viewer/Footer/helpers.js +0 -107
  23. package/react/Viewer/Footer/helpers.spec.js +0 -77
  24. package/react/Viewer/NoViewer/DownloadButton.jsx +0 -28
  25. package/react/Viewer/NoViewer/FileIcon.jsx +0 -46
  26. package/react/Viewer/NoViewer/NoViewer.jsx +0 -29
  27. package/react/Viewer/NoViewer/NoViewer.spec.jsx +0 -44
  28. package/react/Viewer/NoViewer/__snapshots__/NoViewer.spec.jsx.snap +0 -82
  29. package/react/Viewer/NoViewer/index.jsx +0 -1
  30. package/react/Viewer/Panel/ActionMenuDesktop.jsx +0 -66
  31. package/react/Viewer/Panel/ActionMenuMobile.jsx +0 -74
  32. package/react/Viewer/Panel/ActionMenuWrapper.jsx +0 -104
  33. package/react/Viewer/Panel/Certifications.jsx +0 -62
  34. package/react/Viewer/Panel/PanelContent.jsx +0 -49
  35. package/react/Viewer/Panel/Qualification.jsx +0 -114
  36. package/react/Viewer/Panel/QualificationListItemContact.jsx +0 -85
  37. package/react/Viewer/Panel/QualificationListItemDate.jsx +0 -77
  38. package/react/Viewer/Panel/QualificationListItemInformation.jsx +0 -68
  39. package/react/Viewer/Panel/QualificationListItemInformation.spec.jsx +0 -73
  40. package/react/Viewer/Panel/QualificationListItemOther.jsx +0 -61
  41. package/react/Viewer/Panel/QualificationListItemText.jsx +0 -30
  42. package/react/Viewer/Panel/getPanelBlocks.jsx +0 -56
  43. package/react/Viewer/Panel/getPanelBlocks.spec.jsx +0 -79
  44. package/react/Viewer/Panel/styles.styl +0 -13
  45. package/react/Viewer/Readme.md +0 -352
  46. package/react/Viewer/Viewer.jsx +0 -134
  47. package/react/Viewer/ViewerContainer.jsx +0 -169
  48. package/react/Viewer/ViewerExposer.js +0 -3
  49. package/react/Viewer/ViewerInformationsWrapper.jsx +0 -69
  50. package/react/Viewer/ViewerInformationsWrapper.spec.jsx +0 -63
  51. package/react/Viewer/ViewerWithCustomPanelAndFooter.jsx +0 -55
  52. package/react/Viewer/ViewersByFile/AudioViewer.jsx +0 -21
  53. package/react/Viewer/ViewersByFile/AudioViewer.spec.jsx +0 -39
  54. package/react/Viewer/ViewersByFile/BlankPaperViewer.jsx +0 -46
  55. package/react/Viewer/ViewersByFile/ImageViewer.jsx +0 -330
  56. package/react/Viewer/ViewersByFile/ImageViewer.spec.jsx +0 -70
  57. package/react/Viewer/ViewersByFile/NoNetworkViewer.jsx +0 -17
  58. package/react/Viewer/ViewersByFile/OnlyOfficeViewer.jsx +0 -28
  59. package/react/Viewer/ViewersByFile/PdfJsViewer.jsx +0 -210
  60. package/react/Viewer/ViewersByFile/PdfJsViewer.spec.jsx +0 -160
  61. package/react/Viewer/ViewersByFile/PdfMobileViewer.jsx +0 -106
  62. package/react/Viewer/ViewersByFile/PdfMobileViewer.spec.jsx +0 -76
  63. package/react/Viewer/ViewersByFile/ShortcutViewer.jsx +0 -38
  64. package/react/Viewer/ViewersByFile/ShortcutViewer.spec.jsx +0 -32
  65. package/react/Viewer/ViewersByFile/TextViewer.jsx +0 -126
  66. package/react/Viewer/ViewersByFile/TextViewer.spec.jsx +0 -118
  67. package/react/Viewer/ViewersByFile/VideoViewer.jsx +0 -13
  68. package/react/Viewer/ViewersByFile/VideoViewer.spec.jsx +0 -39
  69. package/react/Viewer/ViewersByFile/__snapshots__/AudioViewer.spec.jsx.snap +0 -43
  70. package/react/Viewer/ViewersByFile/__snapshots__/ShortcutViewer.spec.jsx.snap +0 -57
  71. package/react/Viewer/ViewersByFile/__snapshots__/TextViewer.spec.jsx.snap +0 -100
  72. package/react/Viewer/ViewersByFile/__snapshots__/VideoViewer.spec.jsx.snap +0 -19
  73. package/react/Viewer/ViewersByFile/styles.styl +0 -87
  74. package/react/Viewer/assets/IlluGenericNewPage.svg +0 -10
  75. package/react/Viewer/components/ExpirationAlert.jsx +0 -86
  76. package/react/Viewer/components/ExpirationAnnotation.jsx +0 -40
  77. package/react/Viewer/components/Footer.jsx +0 -13
  78. package/react/Viewer/components/InformationPanel.jsx +0 -26
  79. package/react/Viewer/components/Navigation.jsx +0 -39
  80. package/react/Viewer/components/PdfToolbarButton.jsx +0 -26
  81. package/react/Viewer/components/PrintButton.jsx +0 -90
  82. package/react/Viewer/components/Toolbar.jsx +0 -111
  83. package/react/Viewer/components/ToolbarButtons.jsx +0 -11
  84. package/react/Viewer/components/ToolbarFilePath.jsx +0 -61
  85. package/react/Viewer/components/ViewerByFile.jsx +0 -112
  86. package/react/Viewer/components/ViewerByFile.spec.jsx +0 -100
  87. package/react/Viewer/components/ViewerControls.jsx +0 -190
  88. package/react/Viewer/components/ViewerControls.spec.jsx +0 -54
  89. package/react/Viewer/components/ViewerSpinner.jsx +0 -17
  90. package/react/Viewer/components/styles.styl +0 -93
  91. package/react/Viewer/helpers.js +0 -131
  92. package/react/Viewer/helpers.spec.js +0 -136
  93. package/react/Viewer/hoc/withFileUrl.jsx +0 -93
  94. package/react/Viewer/hoc/withViewerLocales.jsx +0 -4
  95. package/react/Viewer/hooks/useReferencedContactName.jsx +0 -26
  96. package/react/Viewer/index.jsx +0 -12
  97. package/react/Viewer/locales/en.json +0 -66
  98. package/react/Viewer/locales/fr.json +0 -66
  99. package/react/Viewer/locales/index.js +0 -4
  100. package/react/Viewer/proptypes.js +0 -12
  101. package/react/Viewer/providers/ActionMenuProvider.jsx +0 -35
  102. package/react/Viewer/queries.js +0 -20
  103. package/react/Viewer/styles.styl +0 -22
  104. package/react/Viewer/vars.styl +0 -6
  105. package/transpiled/react/Viewer/Footer/BottomSheetContent.js +0 -28
  106. package/transpiled/react/Viewer/Footer/DownloadButton.js +0 -91
  107. package/transpiled/react/Viewer/Footer/FooterActionButtons.js +0 -21
  108. package/transpiled/react/Viewer/Footer/FooterContent.js +0 -98
  109. package/transpiled/react/Viewer/Footer/ForwardButton.js +0 -143
  110. package/transpiled/react/Viewer/Footer/ForwardOrDownloadButton.js +0 -25
  111. package/transpiled/react/Viewer/Footer/Sharing.js +0 -57
  112. package/transpiled/react/Viewer/Footer/helpers.js +0 -151
  113. package/transpiled/react/Viewer/NoViewer/DownloadButton.js +0 -34
  114. package/transpiled/react/Viewer/NoViewer/FileIcon.js +0 -57
  115. package/transpiled/react/Viewer/NoViewer/NoViewer.js +0 -49
  116. package/transpiled/react/Viewer/NoViewer/index.js +0 -1
  117. package/transpiled/react/Viewer/Panel/ActionMenuDesktop.js +0 -68
  118. package/transpiled/react/Viewer/Panel/ActionMenuMobile.js +0 -70
  119. package/transpiled/react/Viewer/Panel/ActionMenuWrapper.js +0 -129
  120. package/transpiled/react/Viewer/Panel/Certifications.js +0 -56
  121. package/transpiled/react/Viewer/Panel/PanelContent.js +0 -48
  122. package/transpiled/react/Viewer/Panel/Qualification.js +0 -119
  123. package/transpiled/react/Viewer/Panel/QualificationListItemContact.js +0 -96
  124. package/transpiled/react/Viewer/Panel/QualificationListItemDate.js +0 -64
  125. package/transpiled/react/Viewer/Panel/QualificationListItemInformation.js +0 -59
  126. package/transpiled/react/Viewer/Panel/QualificationListItemOther.js +0 -53
  127. package/transpiled/react/Viewer/Panel/QualificationListItemText.js +0 -29
  128. package/transpiled/react/Viewer/Panel/getPanelBlocks.js +0 -62
  129. package/transpiled/react/Viewer/Viewer.js +0 -172
  130. package/transpiled/react/Viewer/ViewerContainer.js +0 -189
  131. package/transpiled/react/Viewer/ViewerExposer.js +0 -2
  132. package/transpiled/react/Viewer/ViewerInformationsWrapper.js +0 -49
  133. package/transpiled/react/Viewer/ViewerWithCustomPanelAndFooter.js +0 -56
  134. package/transpiled/react/Viewer/ViewersByFile/AudioViewer.js +0 -41
  135. package/transpiled/react/Viewer/ViewersByFile/BlankPaperViewer.js +0 -74
  136. package/transpiled/react/Viewer/ViewersByFile/ImageViewer.js +0 -367
  137. package/transpiled/react/Viewer/ViewersByFile/NoNetworkViewer.js +0 -38
  138. package/transpiled/react/Viewer/ViewersByFile/OnlyOfficeViewer.js +0 -29
  139. package/transpiled/react/Viewer/ViewersByFile/PdfJsViewer.js +0 -254
  140. package/transpiled/react/Viewer/ViewersByFile/PdfMobileViewer.js +0 -153
  141. package/transpiled/react/Viewer/ViewersByFile/ShortcutViewer.js +0 -42
  142. package/transpiled/react/Viewer/ViewersByFile/TextViewer.js +0 -219
  143. package/transpiled/react/Viewer/ViewersByFile/VideoViewer.js +0 -33
  144. package/transpiled/react/Viewer/assets/IlluGenericNewPage.svg +0 -10
  145. package/transpiled/react/Viewer/components/ExpirationAlert.js +0 -100
  146. package/transpiled/react/Viewer/components/ExpirationAnnotation.js +0 -41
  147. package/transpiled/react/Viewer/components/Footer.js +0 -29
  148. package/transpiled/react/Viewer/components/InformationPanel.js +0 -23
  149. package/transpiled/react/Viewer/components/Navigation.js +0 -47
  150. package/transpiled/react/Viewer/components/PdfToolbarButton.js +0 -28
  151. package/transpiled/react/Viewer/components/PrintButton.js +0 -137
  152. package/transpiled/react/Viewer/components/Toolbar.js +0 -115
  153. package/transpiled/react/Viewer/components/ToolbarButtons.js +0 -9
  154. package/transpiled/react/Viewer/components/ToolbarFilePath.js +0 -71
  155. package/transpiled/react/Viewer/components/ViewerByFile.js +0 -105
  156. package/transpiled/react/Viewer/components/ViewerControls.js +0 -226
  157. package/transpiled/react/Viewer/components/ViewerSpinner.js +0 -17
  158. package/transpiled/react/Viewer/helpers.js +0 -147
  159. package/transpiled/react/Viewer/hoc/withFileUrl.js +0 -207
  160. package/transpiled/react/Viewer/hoc/withViewerLocales.js +0 -3
  161. package/transpiled/react/Viewer/hooks/useReferencedContactName.js +0 -32
  162. package/transpiled/react/Viewer/index.js +0 -11
  163. package/transpiled/react/Viewer/locales/index.js +0 -136
  164. package/transpiled/react/Viewer/proptypes.js +0 -14
  165. package/transpiled/react/Viewer/providers/ActionMenuProvider.js +0 -34
  166. package/transpiled/react/Viewer/queries.js +0 -26
  167. /package/react/{Viewer/providers/EncryptedProvider.jsx → providers/Encrypted/index.jsx} +0 -0
  168. /package/transpiled/react/{Viewer/providers/EncryptedProvider.js → providers/Encrypted/index.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ # [112.1.0](https://github.com/cozy/cozy-ui/compare/v112.0.0...v112.1.0) (2024-10-30)
2
+
3
+
4
+ ### Features
5
+
6
+ * Upgrade Bundlemon to 3.1.0 ([e8da4a0](https://github.com/cozy/cozy-ui/commit/e8da4a0))
7
+
8
+ # [112.0.0](https://github.com/cozy/cozy-ui/compare/v111.21.0...v112.0.0) (2024-10-24)
9
+
10
+
11
+ ### Features
12
+
13
+ * **hooks:** Add useClientErrors from cozy-client ([deb505e](https://github.com/cozy/cozy-ui/commit/deb505e))
14
+ * **providers:** Add Encrypted from cozy-viewer ([612cff6](https://github.com/cozy/cozy-ui/commit/612cff6))
15
+ * Remove Viewer ([7d2b7ef](https://github.com/cozy/cozy-ui/commit/7d2b7ef))
16
+
17
+
18
+ ### BREAKING CHANGES
19
+
20
+ * if you want to use the Viewer, you must import components from `cozy-viewer`. So replace `import Something from 'cozy-ui/transpiled/react/Viewer/...'` by `import Something from 'cozy-viewer/...'`
21
+
1
22
  # [111.21.0](https://github.com/cozy/cozy-ui/compare/v111.20.0...v111.21.0) (2024-10-21)
2
23
 
3
24
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-ui",
3
- "version": "111.21.0",
3
+ "version": "112.1.0",
4
4
  "description": "Cozy apps UI SDK",
5
5
  "main": "./index.js",
6
6
  "bin": {
@@ -92,6 +92,7 @@
92
92
  "babel-plugin-inline-json-import": "0.3.2",
93
93
  "babel-preset-cozy-app": "2.0.2",
94
94
  "browserslist-config-cozy": "0.4.0",
95
+ "bundlemon": "3.1.0",
95
96
  "copyfiles": "2.4.1",
96
97
  "cozy-client": "^48.8.0",
97
98
  "cozy-device-helper": "2.0.0",
@@ -158,14 +159,14 @@
158
159
  "svgo": "2.8.0",
159
160
  "svgstore-cli": "1.3.2",
160
161
  "url-loader": "1.1.2",
161
- "webpack": "4.39.3"
162
+ "webpack": "4.39.3",
163
+ "whatwg-fetch": "3.5.0"
162
164
  },
163
165
  "dependencies": {
164
166
  "@babel/runtime": "^7.3.4",
165
167
  "@material-ui/core": "4.12.3",
166
168
  "@material-ui/lab": "^4.0.0-alpha.61",
167
169
  "@popperjs/core": "^2.4.4",
168
- "bundlemon": "^1.3.2",
169
170
  "chart.js": "3.7.1",
170
171
  "classnames": "^2.2.5",
171
172
  "cozy-interapp": "^0.5.4",
@@ -3,14 +3,77 @@
3
3
  A component to get the image in `links` prop of a file, according to its class (could be `image` or `pdf`).
4
4
 
5
5
  ```jsx
6
- import DemoProvider from 'cozy-ui/transpiled/react/Viewer/docs/DemoProvider'
7
-
6
+ import DemoProvider from 'cozy-ui/transpiled/react/providers/DemoProvider'
8
7
  import FileImageLoader from 'cozy-ui/transpiled/react/FileImageLoader'
9
8
  import Icon from 'cozy-ui/transpiled/react/Icon'
10
9
  import FileDuotoneIcon from "cozy-ui/transpiled/react/Icons/FileDuotone"
11
10
  import BankIcon from "cozy-ui/transpiled/react/Icons/Bank"
12
11
  import CloudWallpaper from 'cozy-ui/docs/cloud-wallpaper.jpg'
13
12
 
13
+ const demoTextFileResponse = {
14
+ text: () => new Promise(resolve => resolve('Hello World !'))
15
+ }
16
+
17
+ const demoFilesByClass = {
18
+ pdf: 'https://raw.githubusercontent.com/rospdf/pdf-php/2ccf7591fc2f18e63342ebfedad7997b08c34ed2/readme.pdf',
19
+ audio: 'https://viewerdemo.cozycloud.cc/Z.mp3',
20
+ video: 'https://viewerdemo.cozycloud.cc/Nextcloud.mp4',
21
+ text: 'https://viewerdemo.cozycloud.cc/notes.md'
22
+ }
23
+
24
+ const mockClient = {
25
+ plugins: {
26
+ realtime: {
27
+ subscribe: () => {},
28
+ unsubscribe: () => {},
29
+ unsubscribeAll: () => {}
30
+ }
31
+ },
32
+ on: () => {},
33
+ collection: () => ({
34
+ getDownloadLinkById: id =>
35
+ new Promise(resolve => resolve(demoFilesByClass[id])),
36
+ download: () =>
37
+ alert(
38
+ "This is a demo, there's no actual Cozy to download the file from ¯\\_(ツ)_/¯"
39
+ ),
40
+ get: () =>
41
+ new Promise(resolve =>
42
+ resolve({
43
+ data: {
44
+ links: {
45
+ large: CloudWallpaper
46
+ }
47
+ }
48
+ })
49
+ )
50
+ }),
51
+ getStackClient: () => ({
52
+ uri: '',
53
+ fetch: () => new Promise(resolve => resolve(demoTextFileResponse))
54
+ }),
55
+ getClient: () => mockClient,
56
+ store: {
57
+ getState: () => {},
58
+ subscribe: () => {},
59
+ unsubscribe: () => {}
60
+ },
61
+ getQueryFromState: queryName => {
62
+ if (queryName === 'io.cozy.files/parent_folder') {
63
+ return {
64
+ data: {
65
+ _id: 'parent_id',
66
+ path: '/Parent'
67
+ }
68
+ }
69
+ }
70
+ },
71
+ query: () => ({
72
+ data: [{ attributes: { slug: 'mespapiers' }, links: { related: '' } }]
73
+ }),
74
+ getInstanceOptions: () => ({ app: { slug: 'mespapiers' }, subdomain: 'flat' })
75
+ }
76
+
14
77
  const file = {
15
78
  _id: 'image',
16
79
  class: 'image',
@@ -31,7 +94,7 @@ const FallbackComp = () => {
31
94
 
32
95
  ;
33
96
 
34
- <DemoProvider>
97
+ <DemoProvider client={mockClient}>
35
98
  <FileImageLoader
36
99
  file={file}
37
100
  linkType="large"
@@ -2,6 +2,7 @@ import PropTypes from 'prop-types'
2
2
  import { Component } from 'react'
3
3
 
4
4
  import { withClient } from 'cozy-client'
5
+ import { isEncrypted } from 'cozy-client/dist/models/file'
5
6
  import logger from 'cozy-logger'
6
7
 
7
8
  const PENDING = 'PENDING'
@@ -12,8 +13,7 @@ const FAILED = 'FAILED'
12
13
  const GET_LINK = 'GET_LINK'
13
14
 
14
15
  import { checkImageSource } from './checkImageSource'
15
- import { isFileEncrypted } from '../Viewer/helpers'
16
- import { EncryptedContext } from '../Viewer/providers/EncryptedProvider'
16
+ import { EncryptedContext } from '../providers/Encrypted'
17
17
 
18
18
  export class FileImageLoader extends Component {
19
19
  state = {
@@ -60,7 +60,7 @@ export class FileImageLoader extends Component {
60
60
 
61
61
  loadNextSrc(lastError = null) {
62
62
  const { file } = this.props
63
- if (isFileEncrypted(file)) {
63
+ if (isEncrypted(file)) {
64
64
  // No link available for encrypted files
65
65
  return
66
66
  }
@@ -6,7 +6,7 @@ import logger from 'cozy-logger'
6
6
 
7
7
  import { FileImageLoader } from '.'
8
8
  import { checkImageSource } from './checkImageSource'
9
- import EncryptedProvider from '../Viewer/providers/EncryptedProvider'
9
+ import EncryptedProvider from '../providers/Encrypted'
10
10
 
11
11
  jest.mock('./checkImageSource', () => ({
12
12
  ...jest.requireActual('./checkImageSource'),
@@ -0,0 +1,140 @@
1
+ import filter from 'lodash/filter'
2
+ import React, { useCallback, useState, useEffect } from 'react'
3
+
4
+ import { useClient } from 'cozy-client'
5
+ import { FetchError } from 'cozy-stack-client'
6
+
7
+ import QuotaAlert from '../deprecated/QuotaAlert'
8
+
9
+ /**
10
+ * Rendering functions for client fetch errors by status code
11
+ *
12
+ * @private
13
+ * @type {object}
14
+ */
15
+ const byHttpStatus = {
16
+ 413: QuotaError // <QuotaError />
17
+ }
18
+
19
+ /**
20
+ * Display for a quota error from the client
21
+ *
22
+ * @see QuotaAlert
23
+ * @private
24
+ * @param {object} props - Props
25
+ * @param {Function} props.dismiss - remove the error from the stack to display
26
+ * @returns {React.ReactElement}
27
+ */
28
+ function QuotaError({ dismiss }) {
29
+ return <QuotaAlert onClose={dismiss} />
30
+ }
31
+
32
+ /**
33
+ * Returns the handler for an error
34
+ *
35
+ * @param {import("../types").ClientError} error - The error
36
+ * @returns {Function|null} React Component
37
+ */
38
+ function getErrorComponent(error) {
39
+ if (error instanceof Response || error instanceof FetchError) {
40
+ const status = error.status || ''
41
+ return byHttpStatus[status] || null
42
+ }
43
+ return null
44
+ }
45
+
46
+ /**
47
+ * Renders a stack of errors
48
+ *
49
+ * @private
50
+ * @see ClientErrors
51
+ * @param {import("../types").ClientError[]} errorStack - array of errors/exceptions
52
+ * @param {Function} setErrorStack - mutates the array of errors
53
+ * @returns {Array<React.ReactElement>} React rendering
54
+ */
55
+ function renderErrors(errorStack, setErrorStack) {
56
+ const errors = errorStack.map((error, key) => {
57
+ const Component = getErrorComponent(error)
58
+ if (Component) {
59
+ const dismiss = () =>
60
+ setErrorStack(stack => filter(stack, e => e !== error))
61
+ return <Component key={key} error={error} dismiss={dismiss} />
62
+ } else {
63
+ return null
64
+ }
65
+ })
66
+ return errors
67
+ }
68
+
69
+ /**
70
+ * Manages the client errors and allows to display them
71
+ *
72
+ * Returns a `ClientErrors` React component that takes care
73
+ * of how to display cozy-client errors (probably displaying a modal)
74
+ *
75
+ * Only Quota Errors are managed for now.
76
+ *
77
+ * @example
78
+ * ```
79
+ * const App = () => {
80
+ * const { ClientErrors } = useClientErrors()
81
+ *
82
+ * return <Layout>
83
+ * <h1>My app</h1>
84
+ * <ClientErrors />
85
+ * </Layout>
86
+ * }
87
+ * ```
88
+ *
89
+ * @param {object} [props] - Props
90
+ * @param {boolean} [props.handleExceptions] - should cozy-client directly handle errors before forwarding them to the caller?
91
+ * @returns {{ClientErrors: Function}} React component
92
+ */
93
+ export default function useClientErrors({ handleExceptions = true } = {}) {
94
+ const client = useClient()
95
+ const [errorStack, setErrorStack] = useState([])
96
+
97
+ /**
98
+ * Handle client errors, add them to the error stack
99
+ *
100
+ * @param {import("../types").ClientError} error -
101
+ * @returns {boolean} true if the error was manager, false otherwise
102
+ */
103
+ const handleError = useCallback(
104
+ error => {
105
+ // `isErrorManaged` is there to avoid the same error to be added twice
106
+ // once the error has been added once, the `isErrorManaged`is set to true
107
+ // and any future push is ignored
108
+ if (error.isErrorManaged) return false
109
+
110
+ const isManageable = !!getErrorComponent(error)
111
+ if (isManageable) {
112
+ error.isErrorManaged = true
113
+ setErrorStack(stack => stack.concat(error))
114
+ return true
115
+ } else {
116
+ error.isErrorManaged = false
117
+ return false
118
+ }
119
+ },
120
+ [setErrorStack]
121
+ )
122
+
123
+ useEffect(() => {
124
+ if (handleExceptions) {
125
+ client.on('error', handleError)
126
+ return () => client.removeListener('error', handleError)
127
+ } else {
128
+ return undefined
129
+ }
130
+ }, [client, handleError, handleExceptions])
131
+
132
+ // @ts-ignore
133
+ const ClientErrors = useCallback(
134
+ () => renderErrors(errorStack, setErrorStack),
135
+ [errorStack, setErrorStack]
136
+ )
137
+ // @ts-ignore
138
+ ClientErrors.displayName = 'ClientErrors'
139
+ return { ClientErrors }
140
+ }
@@ -0,0 +1,102 @@
1
+ import { renderHook, act } from '@testing-library/react-hooks'
2
+ import { shallow } from 'enzyme'
3
+ import React from 'react'
4
+
5
+ import { CozyProvider } from 'cozy-client'
6
+ import { FetchError } from 'cozy-stack-client'
7
+
8
+ import useClientErrors from './useClientErrors'
9
+
10
+ function createCozyClient() {
11
+ return {
12
+ on: jest.fn(),
13
+ removeListener: jest.fn()
14
+ }
15
+ }
16
+
17
+ function createWrapper(client = createCozyClient()) {
18
+ function Wrapper({ children }) {
19
+ return <CozyProvider client={client}>{children}</CozyProvider>
20
+ }
21
+ return Wrapper
22
+ }
23
+
24
+ function renderWrappedHook(client) {
25
+ const wrapper = createWrapper(client)
26
+ return renderHook(() => useClientErrors(), { wrapper })
27
+ }
28
+
29
+ function wrappedShallow(tree, client) {
30
+ return shallow(tree, { wrappingComponent: createWrapper(client) })
31
+ }
32
+
33
+ describe('useClientErrors', () => {
34
+ it('registers an `error` handler in client', done => {
35
+ const client = createCozyClient()
36
+ client.on.mockImplementation(name => name === 'error' && done())
37
+ renderWrappedHook(client)
38
+ })
39
+
40
+ describe('renderErrors', () => {
41
+ it('returns a function', () => {
42
+ const { result } = renderWrappedHook()
43
+ const { ClientErrors } = result.current
44
+ expect(ClientErrors).toBeInstanceOf(Function)
45
+ })
46
+
47
+ it('displays nothing by default', () => {
48
+ const { result } = renderWrappedHook()
49
+ const { ClientErrors } = result.current
50
+ const node = wrappedShallow(<ClientErrors />)
51
+ expect(node).toHaveLength(0)
52
+ })
53
+
54
+ describe('for quota errors', () => {
55
+ const findQuotaAlert = node => {
56
+ return node.at(0).dive()
57
+ }
58
+ const setup = async () => {
59
+ const client = createCozyClient()
60
+ const response = new Response(null, {
61
+ status: 413,
62
+ statusText: 'Quota exceeded'
63
+ })
64
+ const error = new FetchError(
65
+ response,
66
+ `${response.status} ${response.statusText}`
67
+ )
68
+
69
+ const { result, waitForNextUpdate } = renderWrappedHook(client)
70
+ const nextUpdate = waitForNextUpdate()
71
+
72
+ act(() => {
73
+ const handler = client.on.mock.calls[0][1]
74
+ handler(error)
75
+ })
76
+
77
+ await nextUpdate
78
+ const { ClientErrors } = result.current
79
+ const node = wrappedShallow(<ClientErrors />, client)
80
+ return { node, result, client }
81
+ }
82
+
83
+ it('displays a a QuotaAlert', async () => {
84
+ const { node } = await setup()
85
+ expect(node).toHaveLength(1)
86
+ expect(findQuotaAlert(node).type().name).toEqual('QuotaAlert')
87
+ })
88
+
89
+ it('can be dismissed', async () => {
90
+ const { node, result, client } = await setup()
91
+ const quotaAlert = findQuotaAlert(node)
92
+ const onClose = quotaAlert.props().onClose
93
+ act(() => onClose())
94
+
95
+ // re-render ClientErrors returned by the hook
96
+ const { ClientErrors } = result.current
97
+ const updatedNode = wrappedShallow(<ClientErrors />, client)
98
+ expect(updatedNode.at(0).length).toBe(0)
99
+ })
100
+ })
101
+ })
102
+ })
package/react/index.js CHANGED
@@ -65,7 +65,6 @@ export { default as AppTitle } from './AppTitle'
65
65
  export { default as Filename } from './Filename'
66
66
  export { default as FilePath } from './FilePath'
67
67
  export { default as FilePathLink } from './FilePathLink'
68
- export { default as Viewer } from './Viewer/ViewerExposer'
69
68
  export { default as FileInput } from './FileInput'
70
69
  export { default as Card } from './Card'
71
70
  export { default as InlineCard } from './deprecated/InlineCard'
@@ -15,6 +15,7 @@ function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Re
15
15
  import PropTypes from 'prop-types';
16
16
  import { Component } from 'react';
17
17
  import { withClient } from 'cozy-client';
18
+ import { isEncrypted } from 'cozy-client/dist/models/file';
18
19
  import logger from 'cozy-logger';
19
20
  var PENDING = 'PENDING';
20
21
  var LOADING_LINK = 'LOADING_LINK';
@@ -23,8 +24,7 @@ var LOADED = 'LOADED';
23
24
  var FAILED = 'FAILED';
24
25
  var GET_LINK = 'GET_LINK';
25
26
  import { checkImageSource } from "cozy-ui/transpiled/react/FileImageLoader/checkImageSource";
26
- import { isFileEncrypted } from "cozy-ui/transpiled/react/Viewer/helpers";
27
- import { EncryptedContext } from "cozy-ui/transpiled/react/Viewer/providers/EncryptedProvider";
27
+ import { EncryptedContext } from "cozy-ui/transpiled/react/providers/Encrypted";
28
28
  export var FileImageLoader = /*#__PURE__*/function (_Component) {
29
29
  _inherits(FileImageLoader, _Component);
30
30
 
@@ -99,7 +99,7 @@ export var FileImageLoader = /*#__PURE__*/function (_Component) {
99
99
  var lastError = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
100
100
  var file = this.props.file;
101
101
 
102
- if (isFileEncrypted(file)) {
102
+ if (isEncrypted(file)) {
103
103
  // No link available for encrypted files
104
104
  return;
105
105
  }
@@ -0,0 +1,167 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import filter from 'lodash/filter';
3
+ import React, { useCallback, useState, useEffect } from 'react';
4
+ import { useClient } from 'cozy-client';
5
+ import { FetchError } from 'cozy-stack-client';
6
+ import QuotaAlert from "cozy-ui/transpiled/react/deprecated/QuotaAlert";
7
+ /**
8
+ * Rendering functions for client fetch errors by status code
9
+ *
10
+ * @private
11
+ * @type {object}
12
+ */
13
+
14
+ var byHttpStatus = {
15
+ 413: QuotaError // <QuotaError />
16
+
17
+ };
18
+ /**
19
+ * Display for a quota error from the client
20
+ *
21
+ * @see QuotaAlert
22
+ * @private
23
+ * @param {object} props - Props
24
+ * @param {Function} props.dismiss - remove the error from the stack to display
25
+ * @returns {React.ReactElement}
26
+ */
27
+
28
+ function QuotaError(_ref) {
29
+ var dismiss = _ref.dismiss;
30
+ return /*#__PURE__*/React.createElement(QuotaAlert, {
31
+ onClose: dismiss
32
+ });
33
+ }
34
+ /**
35
+ * Returns the handler for an error
36
+ *
37
+ * @param {import("../types").ClientError} error - The error
38
+ * @returns {Function|null} React Component
39
+ */
40
+
41
+
42
+ function getErrorComponent(error) {
43
+ if (error instanceof Response || error instanceof FetchError) {
44
+ var status = error.status || '';
45
+ return byHttpStatus[status] || null;
46
+ }
47
+
48
+ return null;
49
+ }
50
+ /**
51
+ * Renders a stack of errors
52
+ *
53
+ * @private
54
+ * @see ClientErrors
55
+ * @param {import("../types").ClientError[]} errorStack - array of errors/exceptions
56
+ * @param {Function} setErrorStack - mutates the array of errors
57
+ * @returns {Array<React.ReactElement>} React rendering
58
+ */
59
+
60
+
61
+ function renderErrors(errorStack, setErrorStack) {
62
+ var errors = errorStack.map(function (error, key) {
63
+ var Component = getErrorComponent(error);
64
+
65
+ if (Component) {
66
+ var dismiss = function dismiss() {
67
+ return setErrorStack(function (stack) {
68
+ return filter(stack, function (e) {
69
+ return e !== error;
70
+ });
71
+ });
72
+ };
73
+
74
+ return /*#__PURE__*/React.createElement(Component, {
75
+ key: key,
76
+ error: error,
77
+ dismiss: dismiss
78
+ });
79
+ } else {
80
+ return null;
81
+ }
82
+ });
83
+ return errors;
84
+ }
85
+ /**
86
+ * Manages the client errors and allows to display them
87
+ *
88
+ * Returns a `ClientErrors` React component that takes care
89
+ * of how to display cozy-client errors (probably displaying a modal)
90
+ *
91
+ * Only Quota Errors are managed for now.
92
+ *
93
+ * @example
94
+ * ```
95
+ * const App = () => {
96
+ * const { ClientErrors } = useClientErrors()
97
+ *
98
+ * return <Layout>
99
+ * <h1>My app</h1>
100
+ * <ClientErrors />
101
+ * </Layout>
102
+ * }
103
+ * ```
104
+ *
105
+ * @param {object} [props] - Props
106
+ * @param {boolean} [props.handleExceptions] - should cozy-client directly handle errors before forwarding them to the caller?
107
+ * @returns {{ClientErrors: Function}} React component
108
+ */
109
+
110
+
111
+ export default function useClientErrors() {
112
+ var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
113
+ _ref2$handleException = _ref2.handleExceptions,
114
+ handleExceptions = _ref2$handleException === void 0 ? true : _ref2$handleException;
115
+
116
+ var client = useClient();
117
+
118
+ var _useState = useState([]),
119
+ _useState2 = _slicedToArray(_useState, 2),
120
+ errorStack = _useState2[0],
121
+ setErrorStack = _useState2[1];
122
+ /**
123
+ * Handle client errors, add them to the error stack
124
+ *
125
+ * @param {import("../types").ClientError} error -
126
+ * @returns {boolean} true if the error was manager, false otherwise
127
+ */
128
+
129
+
130
+ var handleError = useCallback(function (error) {
131
+ // `isErrorManaged` is there to avoid the same error to be added twice
132
+ // once the error has been added once, the `isErrorManaged`is set to true
133
+ // and any future push is ignored
134
+ if (error.isErrorManaged) return false;
135
+ var isManageable = !!getErrorComponent(error);
136
+
137
+ if (isManageable) {
138
+ error.isErrorManaged = true;
139
+ setErrorStack(function (stack) {
140
+ return stack.concat(error);
141
+ });
142
+ return true;
143
+ } else {
144
+ error.isErrorManaged = false;
145
+ return false;
146
+ }
147
+ }, [setErrorStack]);
148
+ useEffect(function () {
149
+ if (handleExceptions) {
150
+ client.on('error', handleError);
151
+ return function () {
152
+ return client.removeListener('error', handleError);
153
+ };
154
+ } else {
155
+ return undefined;
156
+ }
157
+ }, [client, handleError, handleExceptions]); // @ts-ignore
158
+
159
+ var ClientErrors = useCallback(function () {
160
+ return renderErrors(errorStack, setErrorStack);
161
+ }, [errorStack, setErrorStack]); // @ts-ignore
162
+
163
+ ClientErrors.displayName = 'ClientErrors';
164
+ return {
165
+ ClientErrors: ClientErrors
166
+ };
167
+ }
@@ -52,7 +52,6 @@ export { default as AppTitle } from './AppTitle';
52
52
  export { default as Filename } from './Filename';
53
53
  export { default as FilePath } from './FilePath';
54
54
  export { default as FilePathLink } from './FilePathLink';
55
- export { default as Viewer } from './Viewer/ViewerExposer';
56
55
  export { default as FileInput } from './FileInput';
57
56
  export { default as Card } from './Card';
58
57
  export { default as InlineCard } from './deprecated/InlineCard';