cozy-viewer 1.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 (250) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/LICENSE +21 -0
  3. package/Readme.md +44 -0
  4. package/babel.config.js +23 -0
  5. package/dist/Footer/BottomSheetContent.d.ts +12 -0
  6. package/dist/Footer/BottomSheetContent.js +45 -0
  7. package/dist/Footer/DownloadButton.d.ts +16 -0
  8. package/dist/Footer/DownloadButton.js +113 -0
  9. package/dist/Footer/FooterActionButtons.d.ts +13 -0
  10. package/dist/Footer/FooterActionButtons.js +33 -0
  11. package/dist/Footer/FooterActionButtons.spec.d.ts +1 -0
  12. package/dist/Footer/FooterContent.d.ts +16 -0
  13. package/dist/Footer/FooterContent.js +120 -0
  14. package/dist/Footer/ForwardButton.d.ts +20 -0
  15. package/dist/Footer/ForwardButton.js +177 -0
  16. package/dist/Footer/ForwardButton.spec.d.ts +1 -0
  17. package/dist/Footer/ForwardOrDownloadButton.d.ts +11 -0
  18. package/dist/Footer/ForwardOrDownloadButton.js +42 -0
  19. package/dist/Footer/Sharing.d.ts +16 -0
  20. package/dist/Footer/Sharing.js +79 -0
  21. package/dist/Footer/helpers.d.ts +8 -0
  22. package/dist/Footer/helpers.js +179 -0
  23. package/dist/Footer/helpers.spec.d.ts +1 -0
  24. package/dist/NoViewer/DownloadButton.d.ts +2 -0
  25. package/dist/NoViewer/DownloadButton.js +52 -0
  26. package/dist/NoViewer/FileIcon.d.ts +4 -0
  27. package/dist/NoViewer/FileIcon.js +76 -0
  28. package/dist/NoViewer/NoViewer.d.ts +18 -0
  29. package/dist/NoViewer/NoViewer.js +64 -0
  30. package/dist/NoViewer/NoViewer.spec.d.ts +1 -0
  31. package/dist/NoViewer/index.d.ts +1 -0
  32. package/dist/NoViewer/index.js +15 -0
  33. package/dist/Panel/ActionMenuDesktop.d.ts +3 -0
  34. package/dist/Panel/ActionMenuDesktop.js +91 -0
  35. package/dist/Panel/ActionMenuMobile.d.ts +20 -0
  36. package/dist/Panel/ActionMenuMobile.js +95 -0
  37. package/dist/Panel/ActionMenuWrapper.d.ts +3 -0
  38. package/dist/Panel/ActionMenuWrapper.js +155 -0
  39. package/dist/Panel/Certifications.d.ts +2 -0
  40. package/dist/Panel/Certifications.js +80 -0
  41. package/dist/Panel/PanelContent.d.ts +2 -0
  42. package/dist/Panel/PanelContent.js +71 -0
  43. package/dist/Panel/Qualification.d.ts +2 -0
  44. package/dist/Panel/Qualification.js +148 -0
  45. package/dist/Panel/QualificationListItemContact.d.ts +10 -0
  46. package/dist/Panel/QualificationListItemContact.js +123 -0
  47. package/dist/Panel/QualificationListItemDate.d.ts +3 -0
  48. package/dist/Panel/QualificationListItemDate.js +90 -0
  49. package/dist/Panel/QualificationListItemInformation.d.ts +3 -0
  50. package/dist/Panel/QualificationListItemInformation.js +84 -0
  51. package/dist/Panel/QualificationListItemInformation.spec.d.ts +1 -0
  52. package/dist/Panel/QualificationListItemOther.d.ts +3 -0
  53. package/dist/Panel/QualificationListItemOther.js +78 -0
  54. package/dist/Panel/QualificationListItemText.d.ts +13 -0
  55. package/dist/Panel/QualificationListItemText.js +42 -0
  56. package/dist/Panel/getPanelBlocks.d.ts +26 -0
  57. package/dist/Panel/getPanelBlocks.js +79 -0
  58. package/dist/Panel/getPanelBlocks.spec.d.ts +1 -0
  59. package/dist/Viewer.d.ts +39 -0
  60. package/dist/Viewer.js +191 -0
  61. package/dist/ViewerContainer.d.ts +22 -0
  62. package/dist/ViewerContainer.js +221 -0
  63. package/dist/ViewerExposer.d.ts +2 -0
  64. package/dist/ViewerExposer.js +13 -0
  65. package/dist/ViewerInformationsWrapper.d.ts +20 -0
  66. package/dist/ViewerInformationsWrapper.js +68 -0
  67. package/dist/ViewerInformationsWrapper.spec.d.ts +1 -0
  68. package/dist/ViewerWithCustomPanelAndFooter.d.ts +2 -0
  69. package/dist/ViewerWithCustomPanelAndFooter.js +78 -0
  70. package/dist/ViewersByFile/AudioViewer.d.ts +74 -0
  71. package/dist/ViewersByFile/AudioViewer.js +57 -0
  72. package/dist/ViewersByFile/AudioViewer.spec.d.ts +1 -0
  73. package/dist/ViewersByFile/BlankPaperViewer.d.ts +2 -0
  74. package/dist/ViewersByFile/BlankPaperViewer.js +97 -0
  75. package/dist/ViewersByFile/ImageViewer.d.ts +31 -0
  76. package/dist/ViewersByFile/ImageViewer.js +385 -0
  77. package/dist/ViewersByFile/ImageViewer.spec.d.ts +1 -0
  78. package/dist/ViewersByFile/NoNetworkViewer.d.ts +2 -0
  79. package/dist/ViewersByFile/NoNetworkViewer.js +54 -0
  80. package/dist/ViewersByFile/OnlyOfficeViewer.d.ts +2 -0
  81. package/dist/ViewersByFile/OnlyOfficeViewer.js +46 -0
  82. package/dist/ViewersByFile/PdfJsViewer.d.ts +31 -0
  83. package/dist/ViewersByFile/PdfJsViewer.js +283 -0
  84. package/dist/ViewersByFile/PdfJsViewer.spec.d.ts +1 -0
  85. package/dist/ViewersByFile/PdfMobileViewer.d.ts +4 -0
  86. package/dist/ViewersByFile/PdfMobileViewer.js +185 -0
  87. package/dist/ViewersByFile/PdfMobileViewer.spec.d.ts +1 -0
  88. package/dist/ViewersByFile/ShortcutViewer.d.ts +2 -0
  89. package/dist/ViewersByFile/ShortcutViewer.js +61 -0
  90. package/dist/ViewersByFile/ShortcutViewer.spec.d.ts +4 -0
  91. package/dist/ViewersByFile/TextViewer.d.ts +90 -0
  92. package/dist/ViewersByFile/TextViewer.js +250 -0
  93. package/dist/ViewersByFile/TextViewer.spec.d.ts +1 -0
  94. package/dist/ViewersByFile/VideoViewer.d.ts +74 -0
  95. package/dist/ViewersByFile/VideoViewer.js +46 -0
  96. package/dist/ViewersByFile/VideoViewer.spec.d.ts +1 -0
  97. package/dist/assets/IlluGenericNewPage.svg +10 -0
  98. package/dist/components/ExpirationAlert.d.ts +2 -0
  99. package/dist/components/ExpirationAlert.js +129 -0
  100. package/dist/components/ExpirationAnnotation.d.ts +10 -0
  101. package/dist/components/ExpirationAnnotation.js +56 -0
  102. package/dist/components/Footer.d.ts +4 -0
  103. package/dist/components/Footer.js +41 -0
  104. package/dist/components/InformationPanel.d.ts +12 -0
  105. package/dist/components/InformationPanel.js +36 -0
  106. package/dist/components/Navigation.d.ts +18 -0
  107. package/dist/components/Navigation.js +63 -0
  108. package/dist/components/PdfToolbarButton.d.ts +16 -0
  109. package/dist/components/PdfToolbarButton.js +40 -0
  110. package/dist/components/PrintButton.d.ts +16 -0
  111. package/dist/components/PrintButton.js +163 -0
  112. package/dist/components/Toolbar.d.ts +2 -0
  113. package/dist/components/Toolbar.js +146 -0
  114. package/dist/components/ToolbarButtons.d.ts +5 -0
  115. package/dist/components/ToolbarButtons.js +20 -0
  116. package/dist/components/ToolbarFilePath.d.ts +3 -0
  117. package/dist/components/ToolbarFilePath.js +92 -0
  118. package/dist/components/ViewerByFile.d.ts +7 -0
  119. package/dist/components/ViewerByFile.js +137 -0
  120. package/dist/components/ViewerByFile.spec.d.ts +1 -0
  121. package/dist/components/ViewerControls.d.ts +9 -0
  122. package/dist/components/ViewerControls.js +251 -0
  123. package/dist/components/ViewerControls.spec.d.ts +1 -0
  124. package/dist/components/ViewerSpinner.d.ts +2 -0
  125. package/dist/components/ViewerSpinner.js +31 -0
  126. package/dist/docs/DemoProvider.d.ts +6 -0
  127. package/dist/docs/DemoProvider.js +159 -0
  128. package/dist/helpers.d.ts +37 -0
  129. package/dist/helpers.js +190 -0
  130. package/dist/helpers.spec.d.ts +1 -0
  131. package/dist/hoc/withFileUrl.d.ts +75 -0
  132. package/dist/hoc/withFileUrl.js +231 -0
  133. package/dist/hoc/withViewerLocales.d.ts +1 -0
  134. package/dist/hoc/withViewerLocales.js +15 -0
  135. package/dist/hooks/useReferencedContactName.d.ts +5 -0
  136. package/dist/hooks/useReferencedContactName.js +45 -0
  137. package/dist/index.d.ts +10 -0
  138. package/dist/index.js +75 -0
  139. package/dist/locales/index.d.ts +6 -0
  140. package/dist/locales/index.js +18 -0
  141. package/dist/proptypes.d.ts +7 -0
  142. package/dist/proptypes.js +25 -0
  143. package/dist/providers/ActionMenuProvider.d.ts +13 -0
  144. package/dist/providers/ActionMenuProvider.js +47 -0
  145. package/dist/providers/EncryptedProvider.d.ts +8 -0
  146. package/dist/providers/EncryptedProvider.js +43 -0
  147. package/dist/queries.d.ts +15 -0
  148. package/dist/queries.js +40 -0
  149. package/dist/stylesheet.css +218 -0
  150. package/jest.config.js +23 -0
  151. package/package.json +62 -0
  152. package/preprocess.js +16 -0
  153. package/src/Footer/BottomSheetContent.jsx +30 -0
  154. package/src/Footer/DownloadButton.jsx +66 -0
  155. package/src/Footer/FooterActionButtons.jsx +22 -0
  156. package/src/Footer/FooterActionButtons.spec.jsx +30 -0
  157. package/src/Footer/FooterContent.jsx +102 -0
  158. package/src/Footer/ForwardButton.jsx +95 -0
  159. package/src/Footer/ForwardButton.spec.jsx +87 -0
  160. package/src/Footer/ForwardOrDownloadButton.jsx +24 -0
  161. package/src/Footer/Sharing.jsx +59 -0
  162. package/src/Footer/helpers.js +106 -0
  163. package/src/Footer/helpers.spec.js +77 -0
  164. package/src/NoViewer/DownloadButton.jsx +28 -0
  165. package/src/NoViewer/FileIcon.jsx +46 -0
  166. package/src/NoViewer/NoViewer.jsx +30 -0
  167. package/src/NoViewer/NoViewer.spec.jsx +44 -0
  168. package/src/NoViewer/__snapshots__/NoViewer.spec.jsx.snap +82 -0
  169. package/src/NoViewer/index.jsx +1 -0
  170. package/src/Panel/ActionMenuDesktop.jsx +69 -0
  171. package/src/Panel/ActionMenuMobile.jsx +76 -0
  172. package/src/Panel/ActionMenuWrapper.jsx +104 -0
  173. package/src/Panel/Certifications.jsx +63 -0
  174. package/src/Panel/PanelContent.jsx +50 -0
  175. package/src/Panel/Qualification.jsx +114 -0
  176. package/src/Panel/QualificationListItemContact.jsx +85 -0
  177. package/src/Panel/QualificationListItemDate.jsx +78 -0
  178. package/src/Panel/QualificationListItemInformation.jsx +68 -0
  179. package/src/Panel/QualificationListItemInformation.spec.jsx +73 -0
  180. package/src/Panel/QualificationListItemOther.jsx +61 -0
  181. package/src/Panel/QualificationListItemText.jsx +30 -0
  182. package/src/Panel/getPanelBlocks.jsx +56 -0
  183. package/src/Panel/getPanelBlocks.spec.jsx +79 -0
  184. package/src/Panel/styles.styl +13 -0
  185. package/src/Readme.md +352 -0
  186. package/src/Viewer.jsx +135 -0
  187. package/src/ViewerContainer.jsx +170 -0
  188. package/src/ViewerExposer.js +3 -0
  189. package/src/ViewerInformationsWrapper.jsx +70 -0
  190. package/src/ViewerInformationsWrapper.spec.jsx +63 -0
  191. package/src/ViewerWithCustomPanelAndFooter.jsx +57 -0
  192. package/src/ViewersByFile/AudioViewer.jsx +22 -0
  193. package/src/ViewersByFile/AudioViewer.spec.jsx +40 -0
  194. package/src/ViewersByFile/BlankPaperViewer.jsx +47 -0
  195. package/src/ViewersByFile/ImageViewer.jsx +331 -0
  196. package/src/ViewersByFile/ImageViewer.spec.jsx +73 -0
  197. package/src/ViewersByFile/NoNetworkViewer.jsx +18 -0
  198. package/src/ViewersByFile/OnlyOfficeViewer.jsx +29 -0
  199. package/src/ViewersByFile/PdfJsViewer.jsx +210 -0
  200. package/src/ViewersByFile/PdfJsViewer.spec.jsx +161 -0
  201. package/src/ViewersByFile/PdfMobileViewer.jsx +106 -0
  202. package/src/ViewersByFile/PdfMobileViewer.spec.jsx +76 -0
  203. package/src/ViewersByFile/ShortcutViewer.jsx +38 -0
  204. package/src/ViewersByFile/ShortcutViewer.spec.jsx +32 -0
  205. package/src/ViewersByFile/TextViewer.jsx +126 -0
  206. package/src/ViewersByFile/TextViewer.spec.jsx +118 -0
  207. package/src/ViewersByFile/VideoViewer.jsx +13 -0
  208. package/src/ViewersByFile/VideoViewer.spec.jsx +40 -0
  209. package/src/ViewersByFile/__snapshots__/AudioViewer.spec.jsx.snap +43 -0
  210. package/src/ViewersByFile/__snapshots__/ShortcutViewer.spec.jsx.snap +57 -0
  211. package/src/ViewersByFile/__snapshots__/TextViewer.spec.jsx.snap +100 -0
  212. package/src/ViewersByFile/__snapshots__/VideoViewer.spec.jsx.snap +19 -0
  213. package/src/ViewersByFile/styles.styl +87 -0
  214. package/src/assets/IlluGenericNewPage.svg +10 -0
  215. package/src/components/ExpirationAlert.jsx +86 -0
  216. package/src/components/ExpirationAnnotation.jsx +39 -0
  217. package/src/components/Footer.jsx +14 -0
  218. package/src/components/InformationPanel.jsx +26 -0
  219. package/src/components/Navigation.jsx +40 -0
  220. package/src/components/PdfToolbarButton.jsx +26 -0
  221. package/src/components/PrintButton.jsx +89 -0
  222. package/src/components/Toolbar.jsx +111 -0
  223. package/src/components/ToolbarButtons.jsx +11 -0
  224. package/src/components/ToolbarFilePath.jsx +61 -0
  225. package/src/components/ViewerByFile.jsx +112 -0
  226. package/src/components/ViewerByFile.spec.jsx +100 -0
  227. package/src/components/ViewerControls.jsx +191 -0
  228. package/src/components/ViewerControls.spec.jsx +54 -0
  229. package/src/components/ViewerSpinner.jsx +17 -0
  230. package/src/components/styles.styl +93 -0
  231. package/src/docs/DemoProvider.jsx +90 -0
  232. package/src/helpers.js +131 -0
  233. package/src/helpers.spec.js +136 -0
  234. package/src/hoc/withFileUrl.jsx +93 -0
  235. package/src/hoc/withViewerLocales.jsx +5 -0
  236. package/src/hooks/useReferencedContactName.jsx +26 -0
  237. package/src/index.jsx +12 -0
  238. package/src/locales/en.json +66 -0
  239. package/src/locales/fr.json +66 -0
  240. package/src/locales/index.js +4 -0
  241. package/src/proptypes.js +12 -0
  242. package/src/providers/ActionMenuProvider.jsx +35 -0
  243. package/src/providers/EncryptedProvider.jsx +25 -0
  244. package/src/queries.js +20 -0
  245. package/src/styles.styl +22 -0
  246. package/src/vars.styl +6 -0
  247. package/test/__mocks__/fileMock.js +3 -0
  248. package/test/jestLib/setup.js +5 -0
  249. package/tsconfig-build.json +13 -0
  250. package/tsconfig.json +34 -0
@@ -0,0 +1,102 @@
1
+ import cx from 'classnames'
2
+ import PropTypes from 'prop-types'
3
+ import React, { useMemo } from 'react'
4
+
5
+ import BottomSheet, {
6
+ BottomSheetHeader
7
+ } from 'cozy-ui/transpiled/react/BottomSheet'
8
+ import { makeStyles } from 'cozy-ui/transpiled/react/styles'
9
+
10
+ import BottomSheetContent from './BottomSheetContent'
11
+ import { extractChildrenCompByName } from './helpers'
12
+ import PrintButton from '../components/PrintButton'
13
+ import { isValidForPanel } from '../helpers'
14
+
15
+ const FooterButtons = ({
16
+ file,
17
+ FooterActionButtonsWithFile = { FooterActionButtonsWithFile }
18
+ }) => {
19
+ return (
20
+ <>
21
+ {FooterActionButtonsWithFile}
22
+ <PrintButton file={file} variant="button" />
23
+ </>
24
+ )
25
+ }
26
+
27
+ const useStyles = makeStyles(theme => ({
28
+ footer: {
29
+ display: 'flex',
30
+ alignItems: 'center',
31
+ width: 'calc(100% - 2rem)',
32
+ height: '100%',
33
+ paddingLeft: '1rem',
34
+ paddingRight: '1rem',
35
+ borderTop: `1px solid ${theme.palette.divider}`,
36
+ columnGap: '0.5rem'
37
+ },
38
+ bottomSheetHeader: {
39
+ columnGap: '0.5rem'
40
+ }
41
+ }))
42
+
43
+ const FooterContent = ({ file, toolbarRef, children, isPublic }) => {
44
+ const styles = useStyles()
45
+
46
+ const toolbarProps = useMemo(() => ({ ref: toolbarRef }), [toolbarRef])
47
+
48
+ const FooterActionButtonsWithFile = extractChildrenCompByName({
49
+ children,
50
+ file,
51
+ name: 'FooterActionButtons'
52
+ })
53
+
54
+ const bottomSheetSettings = {
55
+ isOpenMin: isPublic ? true : false,
56
+ mediumHeightRatio: isPublic ? undefined : 0.5
57
+ }
58
+
59
+ if (isValidForPanel({ file })) {
60
+ return (
61
+ <BottomSheet
62
+ toolbarProps={toolbarProps}
63
+ portalProps={{ disablePortal: true }}
64
+ settings={bottomSheetSettings}
65
+ >
66
+ <BottomSheetHeader
67
+ className={cx('u-ph-1 u-pb-1', styles.bottomSheetHeader)}
68
+ >
69
+ <FooterButtons
70
+ file={file}
71
+ FooterActionButtonsWithFile={FooterActionButtonsWithFile}
72
+ />
73
+ </BottomSheetHeader>
74
+ <BottomSheetContent file={file} isPublic={isPublic} />
75
+ </BottomSheet>
76
+ )
77
+ }
78
+
79
+ // If `FooterActionButtons` hasn't children
80
+ if (!FooterActionButtonsWithFile) return null
81
+
82
+ return (
83
+ <div className={styles.footer}>
84
+ <FooterButtons
85
+ file={file}
86
+ FooterActionButtonsWithFile={FooterActionButtonsWithFile}
87
+ />
88
+ </div>
89
+ )
90
+ }
91
+
92
+ FooterContent.propTypes = {
93
+ file: PropTypes.object.isRequired,
94
+ toolbarRef: PropTypes.object,
95
+ isPublic: PropTypes.bool,
96
+ children: PropTypes.oneOfType([
97
+ PropTypes.node,
98
+ PropTypes.arrayOf(PropTypes.node)
99
+ ])
100
+ }
101
+
102
+ export default FooterContent
@@ -0,0 +1,95 @@
1
+ import PropTypes from 'prop-types'
2
+ import React from 'react'
3
+
4
+ import { useClient } from 'cozy-client'
5
+ import { getSharingLink } from 'cozy-client/dist/models/sharing'
6
+ import { isIOS, isMobileApp } from 'cozy-device-helper'
7
+ import Button from 'cozy-ui/transpiled/react/Buttons'
8
+ import Icon from 'cozy-ui/transpiled/react/Icon'
9
+ import IconButton from 'cozy-ui/transpiled/react/IconButton'
10
+ import ReplyIcon from 'cozy-ui/transpiled/react/Icons/Reply'
11
+ import ShareIosIcon from 'cozy-ui/transpiled/react/Icons/ShareIos'
12
+ import Alerter from 'cozy-ui/transpiled/react/deprecated/Alerter'
13
+ import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
14
+
15
+ import { exportFilesNative } from './helpers'
16
+
17
+ const ForwardIcon = isIOS() ? ShareIosIcon : ReplyIcon
18
+
19
+ const ForwardButton = ({ file, variant, onClick }) => {
20
+ const { t } = useI18n()
21
+ const client = useClient()
22
+
23
+ const icon = <Icon icon={ForwardIcon} />
24
+ const label = t('Viewer.actions.forward')
25
+
26
+ const onFileOpen = async file => {
27
+ if (isMobileApp()) {
28
+ try {
29
+ await exportFilesNative(client, [file])
30
+ } catch (error) {
31
+ Alerter.info(`Viewer.error.${error}`, { fileMime: file.mime })
32
+ }
33
+ } else {
34
+ try {
35
+ const url = await getSharingLink(client, [file.id])
36
+ const shareData = {
37
+ title: t('Viewer.share.title', { name: file.name }),
38
+ text: t('Viewer.share.text', { name: file.name }),
39
+ url
40
+ }
41
+ navigator.share(shareData)
42
+ } catch (error) {
43
+ Alerter.error('Viewer.share.error', { error: error })
44
+ }
45
+ }
46
+ }
47
+
48
+ const handleClick = () => {
49
+ if (onClick) onClick()
50
+ else onFileOpen(file)
51
+ }
52
+
53
+ if (variant === 'iconButton') {
54
+ return (
55
+ <IconButton className="u-white" aria-label={label} onClick={handleClick}>
56
+ {icon}
57
+ </IconButton>
58
+ )
59
+ }
60
+
61
+ if (variant === 'buttonIcon') {
62
+ return (
63
+ <Button
64
+ variant="secondary"
65
+ label={icon}
66
+ aria-label={label}
67
+ onClick={handleClick}
68
+ />
69
+ )
70
+ }
71
+
72
+ return (
73
+ <Button
74
+ fullWidth
75
+ variant="secondary"
76
+ startIcon={icon}
77
+ data-testid="openFileButton"
78
+ label={label}
79
+ onClick={handleClick}
80
+ />
81
+ )
82
+ }
83
+
84
+ ForwardButton.propTypes = {
85
+ file: PropTypes.object.isRequired,
86
+ variant: PropTypes.oneOf(['default', 'iconButton', 'buttonIcon']),
87
+ onClick: PropTypes.func
88
+ }
89
+
90
+ ForwardButton.defaultProptypes = {
91
+ variant: 'default'
92
+ }
93
+
94
+ export { exportFilesNative }
95
+ export default ForwardButton
@@ -0,0 +1,87 @@
1
+ import { render, fireEvent } from '@testing-library/react'
2
+ import React from 'react'
3
+
4
+ import { getSharingLink } from 'cozy-client/dist/models/sharing'
5
+ import { isMobileApp } from 'cozy-device-helper'
6
+
7
+ import ForwardButton from './ForwardButton'
8
+ import { exportFilesNative } from './helpers'
9
+ import DemoProvider from '../docs/DemoProvider'
10
+
11
+ jest.mock('cozy-device-helper')
12
+ jest.mock('cozy-client/dist/models/sharing', () => ({
13
+ ...jest.requireActual('cozy-client/dist/models/sharing'),
14
+ getSharingLink: jest.fn()
15
+ }))
16
+ jest.mock('./helpers')
17
+
18
+ const file = {
19
+ _id: '123',
20
+ name: 'filename.pdf'
21
+ }
22
+
23
+ const setup = ({ isMobileApplication, onClick }) => {
24
+ isMobileApp.mockReturnValue(isMobileApplication)
25
+ return render(
26
+ <DemoProvider>
27
+ <ForwardButton file={file} onClick={onClick} />
28
+ </DemoProvider>
29
+ )
30
+ }
31
+
32
+ describe('ForwardButton', () => {
33
+ beforeEach(() => {
34
+ jest.resetAllMocks()
35
+ })
36
+
37
+ describe('exportFilesNative', () => {
38
+ it('should call it if is on native app', async () => {
39
+ const { findByTestId } = setup({ isMobileApplication: true })
40
+
41
+ const btn = await findByTestId('openFileButton')
42
+ fireEvent.click(btn)
43
+
44
+ expect(exportFilesNative).toHaveBeenCalledTimes(1)
45
+ })
46
+
47
+ it('should not call it if is on web app', async () => {
48
+ const { findByTestId } = setup({ isMobileApplication: false })
49
+
50
+ const btn = await findByTestId('openFileButton')
51
+ fireEvent.click(btn)
52
+
53
+ expect(exportFilesNative).toHaveBeenCalledTimes(0)
54
+ })
55
+ })
56
+
57
+ describe('getSharingLink', () => {
58
+ it('should not call it if is on native app', async () => {
59
+ const { findByTestId } = setup({ isMobileApplication: true })
60
+
61
+ const btn = await findByTestId('openFileButton')
62
+ fireEvent.click(btn)
63
+
64
+ expect(getSharingLink).toHaveBeenCalledTimes(0)
65
+ })
66
+
67
+ it('should not call it if the "onClick" prop is passed', async () => {
68
+ const onClick = jest.fn()
69
+ const { findByTestId } = setup({ isMobileApplication: true, onClick })
70
+
71
+ const btn = await findByTestId('openFileButton')
72
+ fireEvent.click(btn)
73
+
74
+ expect(getSharingLink).toHaveBeenCalledTimes(0)
75
+ expect(onClick).toHaveBeenCalledTimes(1)
76
+ })
77
+
78
+ it('should call it if is on web app', async () => {
79
+ const { findByTestId } = setup({ isMobileApplication: false })
80
+
81
+ const btn = await findByTestId('openFileButton')
82
+ fireEvent.click(btn)
83
+
84
+ expect(getSharingLink).toHaveBeenCalledTimes(1)
85
+ })
86
+ })
87
+ })
@@ -0,0 +1,24 @@
1
+ import PropTypes from 'prop-types'
2
+ import React from 'react'
3
+
4
+ import { useClient } from 'cozy-client'
5
+
6
+ import DownloadButton from './DownloadButton'
7
+ import ForwardButton from './ForwardButton'
8
+ import { shouldBeForwardButton } from './helpers'
9
+
10
+ const ForwardOrDownloadButton = ({ file, ...props }) => {
11
+ const client = useClient()
12
+
13
+ const FileActionButton = shouldBeForwardButton(client)
14
+ ? ForwardButton
15
+ : DownloadButton
16
+
17
+ return <FileActionButton file={file} {...props} />
18
+ }
19
+
20
+ ForwardOrDownloadButton.propTypes = {
21
+ file: PropTypes.object
22
+ }
23
+
24
+ export default ForwardOrDownloadButton
@@ -0,0 +1,59 @@
1
+ import PropTypes from 'prop-types'
2
+ import React, { useState } from 'react'
3
+
4
+ import { useClient } from 'cozy-client'
5
+ import { ShareModal, ShareButton } from 'cozy-sharing'
6
+ import { SharingProvider } from 'cozy-sharing/dist/SharingProvider'
7
+ import Icon from 'cozy-ui/transpiled/react/Icon'
8
+ import IconButton from 'cozy-ui/transpiled/react/IconButton'
9
+ import ShareIcon from 'cozy-ui/transpiled/react/Icons/Share'
10
+
11
+ const Sharing = ({ file, variant }) => {
12
+ const client = useClient()
13
+ const [showShareModal, setShowShareModal] = useState(false)
14
+
15
+ const SharingButton =
16
+ variant === 'iconButton' ? (
17
+ <IconButton className="u-white" onClick={() => setShowShareModal(true)}>
18
+ <Icon icon={ShareIcon} />
19
+ </IconButton>
20
+ ) : (
21
+ <ShareButton
22
+ fullWidth
23
+ useShortLabel
24
+ docId={file.id}
25
+ onClick={() => setShowShareModal(true)}
26
+ />
27
+ )
28
+
29
+ return (
30
+ <>
31
+ <SharingProvider
32
+ client={client}
33
+ doctype="io.cozy.files"
34
+ documentType="Files"
35
+ >
36
+ {showShareModal && (
37
+ <ShareModal
38
+ document={file}
39
+ documentType="Files"
40
+ sharingDesc={file.name}
41
+ onClose={() => setShowShareModal(false)}
42
+ />
43
+ )}
44
+ {SharingButton}
45
+ </SharingProvider>
46
+ </>
47
+ )
48
+ }
49
+
50
+ Sharing.propTypes = {
51
+ file: PropTypes.object,
52
+ variant: PropTypes.oneOf(['default', 'iconButton'])
53
+ }
54
+
55
+ Sharing.defaultProptypes = {
56
+ variant: 'default'
57
+ }
58
+
59
+ export default Sharing
@@ -0,0 +1,106 @@
1
+ import { isValidElement, Children, cloneElement } from 'react'
2
+
3
+ import { saveFileWithCordova } from 'cozy-client/dist/models/fsnative'
4
+ import { isIOS, isMobileApp } from 'cozy-device-helper'
5
+ import Alerter from 'cozy-ui/transpiled/react/deprecated/Alerter'
6
+
7
+ export const shouldBeForwardButton = client => {
8
+ const isDrive = client?.appMetadata?.slug === 'drive'
9
+ if (isMobileApp() || (navigator.share && !isDrive)) return true
10
+ return false
11
+ }
12
+
13
+ const isMissingFileError = error => error.status === 404
14
+ const downloadFileError = error => {
15
+ return isMissingFileError(error)
16
+ ? 'Viewer.error.downloadFile.missing'
17
+ : 'Viewer.error.missing'
18
+ }
19
+
20
+ /**
21
+ * exportFilesNative - Triggers a prompt to download a file on mobile devices
22
+ *
23
+ * @param {CozyClient} client
24
+ * @param {array} files One or more files to download
25
+ * @param {string} filename The name of the file that will be saved
26
+ */
27
+ export const exportFilesNative = async (client, files, filename) => {
28
+ const downloadAllFiles = files.map(async file => {
29
+ const response = await client
30
+ .collection('io.cozy.files')
31
+ .fetchFileContentById(file.id)
32
+
33
+ const blob = await response.blob()
34
+ const filenameToUse = filename ? filename : file.name
35
+ const localFile = await saveFileWithCordova(blob, filenameToUse)
36
+ return localFile.nativeURL
37
+ })
38
+
39
+ try {
40
+ Alerter.info('Viewer.alert.preparing', {
41
+ duration: Math.min(downloadAllFiles.length * 2000, 6000)
42
+ })
43
+
44
+ const urls = await Promise.all(downloadAllFiles)
45
+ if (urls.length === 1 && isIOS()) {
46
+ // TODO
47
+ // It seems that files: is not well supported on iOS. url seems to work well
48
+ // at with one file. Need to check when severals
49
+ window.plugins.socialsharing.shareWithOptions(
50
+ {
51
+ url: urls[0]
52
+ },
53
+ result => {
54
+ if (result.completed === true) {
55
+ Alerter.success('Viewer.share.success')
56
+ }
57
+ },
58
+ error => {
59
+ throw error
60
+ }
61
+ )
62
+ } else {
63
+ window.plugins.socialsharing.shareWithOptions(
64
+ {
65
+ files: urls
66
+ },
67
+ null,
68
+ error => {
69
+ throw error
70
+ }
71
+ )
72
+ }
73
+ } catch (error) {
74
+ Alerter.error(downloadFileError(error))
75
+ }
76
+ }
77
+
78
+ export const mapToAllChildren = (children, cb) => {
79
+ return Children.map(children, child => {
80
+ if (!isValidElement(child)) return child
81
+
82
+ const grandchildren = child.props.children
83
+ if (grandchildren) {
84
+ return cloneElement(child, {
85
+ children: mapToAllChildren(grandchildren, cb)
86
+ })
87
+ }
88
+
89
+ return cb(child)
90
+ })
91
+ }
92
+
93
+ export const extractChildrenCompByName = ({ children, file, name }) => {
94
+ const ChildrenComp =
95
+ Children.toArray(children).find(child => {
96
+ return child.type.name === name || child.type.displayName === name
97
+ }) || null
98
+
99
+ const ChildrenCompWithFile = isValidElement(ChildrenComp)
100
+ ? cloneElement(ChildrenComp, {
101
+ file
102
+ })
103
+ : null
104
+
105
+ return ChildrenCompWithFile
106
+ }
@@ -0,0 +1,77 @@
1
+ import { isMobileApp } from 'cozy-device-helper'
2
+
3
+ import { shouldBeForwardButton } from './helpers'
4
+
5
+ jest.mock('cozy-device-helper')
6
+
7
+ describe('shouldBeForwardButton', () => {
8
+ const setup = ({ isMobileApplication = false, appSlug = '' }) => {
9
+ const mockClient = { appMetadata: { slug: appSlug } }
10
+ isMobileApp.mockReturnValue(isMobileApplication)
11
+
12
+ return mockClient
13
+ }
14
+
15
+ describe('Mobile native', () => {
16
+ it('should be true if the mobile native application is "Drive"', () => {
17
+ global.navigator.share = null
18
+ const mockClient = setup({
19
+ appSlug: 'drive',
20
+ isMobileApplication: true
21
+ })
22
+
23
+ expect(shouldBeForwardButton(mockClient)).toBe(true)
24
+ })
25
+ it('should be true if the mobile native application is not "Drive"', () => {
26
+ global.navigator.share = null
27
+ const mockClient = setup({
28
+ appSlug: 'other',
29
+ isMobileApplication: true
30
+ })
31
+
32
+ expect(shouldBeForwardButton(mockClient)).toBe(true)
33
+ })
34
+ })
35
+
36
+ describe('Mobile web', () => {
37
+ it('should be false if the mobile web application is "Drive"', () => {
38
+ global.navigator.share = () => {}
39
+ const mockClient = setup({
40
+ appSlug: 'drive',
41
+ isMobileApplication: false
42
+ })
43
+
44
+ expect(shouldBeForwardButton(mockClient)).toBe(false)
45
+ })
46
+ it('should be true if the mobile web application is not "Drive"', () => {
47
+ global.navigator.share = () => {}
48
+ const mockClient = setup({
49
+ appSlug: 'other',
50
+ isMobileApplication: false
51
+ })
52
+
53
+ expect(shouldBeForwardButton(mockClient)).toBe(true)
54
+ })
55
+ })
56
+
57
+ describe('Desktop', () => {
58
+ it('should be false if the desktop application is "Drive"', () => {
59
+ global.navigator.share = null
60
+ const mockClient = setup({
61
+ appSlug: 'drive',
62
+ isMobileApplication: false
63
+ })
64
+
65
+ expect(shouldBeForwardButton(mockClient)).toBe(false)
66
+ })
67
+ it('should be false if the desktop application is not "Drive"', () => {
68
+ global.navigator.share = null
69
+ const mockClient = setup({
70
+ appSlug: 'other',
71
+ isMobileApplication: false
72
+ })
73
+
74
+ expect(shouldBeForwardButton(mockClient)).toBe(false)
75
+ })
76
+ })
77
+ })
@@ -0,0 +1,28 @@
1
+ import PropTypes from 'prop-types'
2
+ import React from 'react'
3
+
4
+ import { withClient } from 'cozy-client'
5
+ import Button from 'cozy-ui/transpiled/react/deprecated/Button'
6
+ import { FileDoctype } from 'cozy-ui/transpiled/react/proptypes'
7
+ import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
8
+
9
+ import { downloadFile } from '../helpers'
10
+
11
+ const DownloadButton = ({ client, file, url }) => {
12
+ const { t } = useI18n()
13
+
14
+ return (
15
+ <Button
16
+ onClick={() => downloadFile({ client, file, url })}
17
+ label={t('Viewer.download')}
18
+ />
19
+ )
20
+ }
21
+
22
+ DownloadButton.propTypes = {
23
+ client: PropTypes.object.isRequired,
24
+ file: FileDoctype,
25
+ url: PropTypes.string
26
+ }
27
+
28
+ export default withClient(DownloadButton)
@@ -0,0 +1,46 @@
1
+ import React from 'react'
2
+
3
+ import Icon from 'cozy-ui/transpiled/react/Icon'
4
+ import FileTypeBinIcon from 'cozy-ui/transpiled/react/Icons/FileTypeBin'
5
+ import FileTypeCodeIcon from 'cozy-ui/transpiled/react/Icons/FileTypeCode'
6
+ import FileTypeFilesIcon from 'cozy-ui/transpiled/react/Icons/FileTypeFiles'
7
+ import FileTypePdfIcon from 'cozy-ui/transpiled/react/Icons/FileTypePdf'
8
+ import FileTypeSheetIcon from 'cozy-ui/transpiled/react/Icons/FileTypeSheet'
9
+ import FileTypeSlideIcon from 'cozy-ui/transpiled/react/Icons/FileTypeSlide'
10
+ import FileTypeTextIcon from 'cozy-ui/transpiled/react/Icons/FileTypeText'
11
+ import FileTypeZipIcon from 'cozy-ui/transpiled/react/Icons/FileTypeZip'
12
+
13
+ const FileIcon = ({ type }) => {
14
+ let icon
15
+
16
+ switch (type) {
17
+ case 'bin':
18
+ icon = FileTypeBinIcon
19
+ break
20
+ case 'code':
21
+ icon = FileTypeCodeIcon
22
+ break
23
+ case 'spreadsheet':
24
+ icon = FileTypeSheetIcon
25
+ break
26
+ case 'slide':
27
+ icon = FileTypeSlideIcon
28
+ break
29
+ case 'text':
30
+ icon = FileTypeTextIcon
31
+ break
32
+ case 'zip':
33
+ icon = FileTypeZipIcon
34
+ break
35
+ case 'pdf':
36
+ icon = FileTypePdfIcon
37
+ break
38
+ default:
39
+ icon = FileTypeFilesIcon
40
+ break
41
+ }
42
+
43
+ return <Icon icon={icon} width={160} height={140} />
44
+ }
45
+
46
+ export default FileIcon
@@ -0,0 +1,30 @@
1
+ import PropTypes from 'prop-types'
2
+ import React from 'react'
3
+
4
+ import { FileDoctype } from 'cozy-ui/transpiled/react/proptypes'
5
+
6
+ import DownloadButton from './DownloadButton'
7
+ import FileIcon from './FileIcon'
8
+ import styles from '../ViewersByFile/styles.styl'
9
+
10
+ const NoViewer = ({ file, url, renderFallbackExtraContent }) => (
11
+ <div className={styles['viewer-noviewer']}>
12
+ <FileIcon type={file.class} />
13
+ <p className={styles['viewer-filename']}>{file.name}</p>
14
+ {renderFallbackExtraContent(file, url)}
15
+ </div>
16
+ )
17
+
18
+ NoViewer.propTypes = {
19
+ file: FileDoctype.isRequired,
20
+ renderFallbackExtraContent: PropTypes.func,
21
+ url: PropTypes.string
22
+ }
23
+
24
+ NoViewer.defaultProps = {
25
+ renderFallbackExtraContent: (file, url) => (
26
+ <DownloadButton file={file} url={url} />
27
+ )
28
+ }
29
+
30
+ export default NoViewer