cozy-ui 57.9.2 → 58.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/package.json +2 -2
- package/react/FileImageLoader/Readme.md +48 -0
- package/react/{Viewer → FileImageLoader}/checkImageSource.js +7 -3
- package/react/{Viewer/ImageLoader.jsx → FileImageLoader/index.jsx} +28 -8
- package/react/{Viewer/ImageLoader.spec.jsx → FileImageLoader/index.spec.jsx} +42 -3
- package/react/SquareAppIcon/Readme.md +4 -3
- package/react/SquareAppIcon/index.jsx +11 -7
- package/react/Viewer/ImageViewer.jsx +3 -2
- package/react/Viewer/ImageViewer.spec.jsx +3 -3
- package/react/Viewer/PdfMobileViewer.jsx +2 -2
- package/react/Viewer/PdfMobileViewer.spec.jsx +5 -0
- package/react/index.js +1 -0
- package/transpiled/react/FileImageLoader/checkImageSource.js +49 -0
- package/transpiled/react/{Viewer/ImageLoader.js → FileImageLoader/index.js} +46 -26
- package/transpiled/react/SquareAppIcon/index.js +9 -13
- package/transpiled/react/Viewer/ImageViewer.js +2 -2
- package/transpiled/react/Viewer/PdfMobileViewer.js +2 -2
- package/transpiled/react/index.js +2 -1
- package/transpiled/react/Viewer/checkImageSource.js +0 -24
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,45 @@
|
|
|
1
|
+
## [58.1.1](https://github.com/cozy/cozy-ui/compare/v58.1.0...v58.1.1) (2021-12-07)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* Replace jest function in documentation ([cdbc01f](https://github.com/cozy/cozy-ui/commit/cdbc01f))
|
|
7
|
+
|
|
8
|
+
# [58.1.0](https://github.com/cozy/cozy-ui/compare/v58.0.0...v58.1.0) (2021-12-06)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* Add realtime to FileImageLoader component ([621fe47](https://github.com/cozy/cozy-ui/commit/621fe47))
|
|
14
|
+
|
|
15
|
+
# [58.0.0](https://github.com/cozy/cozy-ui/compare/v57.10.0...v58.0.0) (2021-12-03)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* Renamed ImageLoader & Exposed it directly ([2ca789b](https://github.com/cozy/cozy-ui/commit/2ca789b))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### BREAKING CHANGES
|
|
24
|
+
|
|
25
|
+
* `ImageLoader` is rename `FileImageLoader`.
|
|
26
|
+
If you were previously importing `ImageLoader`
|
|
27
|
+
via the `Viewer`like this
|
|
28
|
+
`import ImageLoader from 'cozy-ui/transpiled/react/Viewer/ImageLoader'`
|
|
29
|
+
this no longer works, there is a more direct path:
|
|
30
|
+
`import ImageLoader from 'cozy-ui/transpiled/react/ImageLoader`.
|
|
31
|
+
The method `checkImageSource` has been moved also
|
|
32
|
+
from `Viewer` folder to `ImageLoader` folder.
|
|
33
|
+
|
|
34
|
+
Co-authored-by: JF-Cozy
|
|
35
|
+
|
|
36
|
+
# [57.10.0](https://github.com/cozy/cozy-ui/compare/v57.9.2...v57.10.0) (2021-11-29)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
### Features
|
|
40
|
+
|
|
41
|
+
* Forward remaining props to AppIcon ([ed86adf](https://github.com/cozy/cozy-ui/commit/ed86adf))
|
|
42
|
+
|
|
1
43
|
## [57.9.2](https://github.com/cozy/cozy-ui/compare/v57.9.1...v57.9.2) (2021-11-23)
|
|
2
44
|
|
|
3
45
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "58.1.1",
|
|
4
4
|
"description": "Cozy apps UI SDK",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"bin": {
|
|
@@ -140,7 +140,7 @@
|
|
|
140
140
|
"stylus": "0.54.7",
|
|
141
141
|
"stylus-loader": "3.0.2",
|
|
142
142
|
"svg-sprite-loader": "4.1.6",
|
|
143
|
-
"svgstore-cli": "1.3.
|
|
143
|
+
"svgstore-cli": "1.3.2",
|
|
144
144
|
"url-loader": "1.1.2",
|
|
145
145
|
"webpack": "4.39.3"
|
|
146
146
|
},
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
### FileImageLoader
|
|
2
|
+
|
|
3
|
+
A component to get the image in `links` prop of a file, according to its class (could be `image` or `pdf`).
|
|
4
|
+
|
|
5
|
+
```jsx
|
|
6
|
+
import DemoProvider from '../Viewer/docs/DemoProvider'
|
|
7
|
+
|
|
8
|
+
import FileImageLoader from 'cozy-ui/transpiled/react/FileImageLoader'
|
|
9
|
+
import Icon from 'cozy-ui/transpiled/react/Icon'
|
|
10
|
+
import FileDuotoneIcon from "cozy-ui/transpiled/react/Icons/FileDuotone"
|
|
11
|
+
import BankIcon from "cozy-ui/transpiled/react/Icons/Bank"
|
|
12
|
+
|
|
13
|
+
const file = {
|
|
14
|
+
_id: 'image',
|
|
15
|
+
class: 'image',
|
|
16
|
+
name: 'Demo.img',
|
|
17
|
+
mime: 'application/jpeg',
|
|
18
|
+
links: {
|
|
19
|
+
large: 'https://viewerdemo.cozycloud.cc/IMG_0062.PNG'
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const onImageError = () => console.info('image errored')
|
|
24
|
+
const onImageLoad = () => console.info('image loaded!')
|
|
25
|
+
const FallbackComp = () => {
|
|
26
|
+
return (
|
|
27
|
+
<div>fallback component</div>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
;
|
|
32
|
+
|
|
33
|
+
<DemoProvider>
|
|
34
|
+
<FileImageLoader
|
|
35
|
+
file={file}
|
|
36
|
+
linkType="large"
|
|
37
|
+
onError={onImageError}
|
|
38
|
+
render={src => (
|
|
39
|
+
<img
|
|
40
|
+
src={src}
|
|
41
|
+
height={300}
|
|
42
|
+
onLoad={onImageLoad}
|
|
43
|
+
/>
|
|
44
|
+
)}
|
|
45
|
+
renderFallback={() => <FallbackComp />}
|
|
46
|
+
/>
|
|
47
|
+
</DemoProvider>
|
|
48
|
+
```
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @param {string} src - Image source
|
|
3
|
+
* @returns {Promise<void>}
|
|
4
|
+
*/
|
|
5
|
+
export const checkImageSource = async src => {
|
|
6
|
+
const TTL = 10000
|
|
4
7
|
let timeout = null
|
|
5
8
|
let img = null
|
|
9
|
+
|
|
6
10
|
const cleanImageLoader = () => {
|
|
7
11
|
clearTimeout(timeout)
|
|
8
12
|
img.onload = img.onerror = null
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Component } from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
3
|
import { withClient } from 'cozy-client'
|
|
4
4
|
import logger from 'cozy-logger'
|
|
@@ -11,7 +11,8 @@ const FAILED = 'FAILED'
|
|
|
11
11
|
const GET_LINK = 'GET_LINK'
|
|
12
12
|
|
|
13
13
|
import { checkImageSource } from './checkImageSource'
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
export class FileImageLoader extends Component {
|
|
15
16
|
state = {
|
|
16
17
|
src: null
|
|
17
18
|
}
|
|
@@ -19,9 +20,13 @@ export class ImageLoader extends React.Component {
|
|
|
19
20
|
_mounted = false
|
|
20
21
|
|
|
21
22
|
componentDidMount() {
|
|
23
|
+
const { client } = this.props
|
|
22
24
|
this._mounted = true
|
|
23
25
|
this.status = PENDING
|
|
24
26
|
this.loadNextSrc()
|
|
27
|
+
this.realtime = client.plugins.realtime
|
|
28
|
+
this.type = 'io.cozy.files.thumbnails'
|
|
29
|
+
this.realtime.subscribe('created', this.type, this.handleCreate)
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
componentWillUnmount() {
|
|
@@ -30,6 +35,19 @@ export class ImageLoader extends React.Component {
|
|
|
30
35
|
this.img.onload = this.img.onerror = null
|
|
31
36
|
this.img.src = ''
|
|
32
37
|
}
|
|
38
|
+
this.realtime &&
|
|
39
|
+
this.realtime.unsubscribe('created', this.type, this.handleCreate)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Reload the link when realtime tell us that the
|
|
44
|
+
* thumbnail is created. By default linkType === small
|
|
45
|
+
*/
|
|
46
|
+
handleCreate = doc => {
|
|
47
|
+
const { file, linkType } = this.props
|
|
48
|
+
if (file._id === doc._id && doc.format === linkType) {
|
|
49
|
+
this.loadLink()
|
|
50
|
+
}
|
|
33
51
|
}
|
|
34
52
|
|
|
35
53
|
getFileId(file) {
|
|
@@ -51,6 +69,7 @@ export class ImageLoader extends React.Component {
|
|
|
51
69
|
const response = await this.props.client
|
|
52
70
|
.collection('io.cozy.files')
|
|
53
71
|
.get(this.getFileId(file))
|
|
72
|
+
|
|
54
73
|
if (!response.data.links) throw new Error('Could not fetch file links')
|
|
55
74
|
return response.data.links
|
|
56
75
|
}
|
|
@@ -63,6 +82,7 @@ export class ImageLoader extends React.Component {
|
|
|
63
82
|
const link = file.links ? file.links[linkType] : false
|
|
64
83
|
|
|
65
84
|
if (!link) throw new Error(`${linkType} link is not available`)
|
|
85
|
+
|
|
66
86
|
const src = client.getStackClient().uri + link
|
|
67
87
|
await checkImageSource(src)
|
|
68
88
|
if (this._mounted) {
|
|
@@ -72,10 +92,10 @@ export class ImageLoader extends React.Component {
|
|
|
72
92
|
})
|
|
73
93
|
}
|
|
74
94
|
} catch (e) {
|
|
75
|
-
logger.error(e)
|
|
76
95
|
this.loadNextSrc(e)
|
|
77
96
|
}
|
|
78
97
|
}
|
|
98
|
+
|
|
79
99
|
async loadLink() {
|
|
80
100
|
this.status = LOADING_LINK
|
|
81
101
|
const { file, linkType, client } = this.props
|
|
@@ -105,7 +125,7 @@ export class ImageLoader extends React.Component {
|
|
|
105
125
|
const { file, client } = this.props
|
|
106
126
|
|
|
107
127
|
try {
|
|
108
|
-
//
|
|
128
|
+
// FileImageLoader can also be used for pdf files, because on mobile a preview image is
|
|
109
129
|
// generated and the pdf is therefore treated as an image.
|
|
110
130
|
// But the fallback allows to display the original file as an image if there is an error
|
|
111
131
|
// during the preview recovery. This principle is not possible with a pdf file.
|
|
@@ -137,17 +157,17 @@ export class ImageLoader extends React.Component {
|
|
|
137
157
|
}
|
|
138
158
|
}
|
|
139
159
|
|
|
140
|
-
|
|
160
|
+
FileImageLoader.propTypes = {
|
|
141
161
|
file: PropTypes.object.isRequired,
|
|
142
162
|
render: PropTypes.func.isRequired,
|
|
143
|
-
linkType: PropTypes.oneOf(['small', 'medium', 'large', 'preview']),
|
|
163
|
+
linkType: PropTypes.oneOf(['small', 'medium', 'large', 'preview', 'icon']),
|
|
144
164
|
onError: PropTypes.func,
|
|
145
165
|
renderFallback: PropTypes.func
|
|
146
166
|
}
|
|
147
167
|
|
|
148
|
-
|
|
168
|
+
FileImageLoader.defaultProps = {
|
|
149
169
|
linkType: 'small',
|
|
150
170
|
onError: () => {}
|
|
151
171
|
}
|
|
152
172
|
|
|
153
|
-
export default withClient(
|
|
173
|
+
export default withClient(FileImageLoader)
|
|
@@ -4,7 +4,7 @@ import { render, waitFor } from '@testing-library/react'
|
|
|
4
4
|
import { createMockClient } from 'cozy-client'
|
|
5
5
|
import logger from 'cozy-logger'
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { FileImageLoader } from '.'
|
|
8
8
|
import { checkImageSource } from './checkImageSource'
|
|
9
9
|
|
|
10
10
|
jest.mock('./checkImageSource', () => ({
|
|
@@ -21,6 +21,11 @@ client.collection = jest.fn(() => ({
|
|
|
21
21
|
getDownloadLinkById: getDownloadLinkByIdMock,
|
|
22
22
|
get: getMock
|
|
23
23
|
}))
|
|
24
|
+
client.plugins.realtime = {
|
|
25
|
+
subscribe: jest.fn(),
|
|
26
|
+
unsubscribe: jest.fn(),
|
|
27
|
+
unsubscribeAll: jest.fn()
|
|
28
|
+
}
|
|
24
29
|
|
|
25
30
|
const file = {
|
|
26
31
|
_id: 'image',
|
|
@@ -34,11 +39,18 @@ const file = {
|
|
|
34
39
|
|
|
35
40
|
const setup = ({ file }) => {
|
|
36
41
|
const root = render(
|
|
37
|
-
<
|
|
42
|
+
<FileImageLoader
|
|
38
43
|
file={file}
|
|
39
44
|
linkType="large"
|
|
40
45
|
key={file.id}
|
|
41
46
|
render={src => <img alt={file.name} src={src} data-testid="img_image" />}
|
|
47
|
+
renderFallback={() => (
|
|
48
|
+
<img
|
|
49
|
+
alt="render fallback"
|
|
50
|
+
src="http://url.img_image_render_fallback/"
|
|
51
|
+
data-testid="img_image_renderFallback"
|
|
52
|
+
/>
|
|
53
|
+
)}
|
|
42
54
|
client={client}
|
|
43
55
|
/>
|
|
44
56
|
)
|
|
@@ -46,10 +58,11 @@ const setup = ({ file }) => {
|
|
|
46
58
|
return { root }
|
|
47
59
|
}
|
|
48
60
|
|
|
49
|
-
describe('
|
|
61
|
+
describe('FileImageLoader', () => {
|
|
50
62
|
afterEach(() => {
|
|
51
63
|
jest.restoreAllMocks()
|
|
52
64
|
})
|
|
65
|
+
|
|
53
66
|
it('should set the source of image to links.large if no error', async () => {
|
|
54
67
|
const { root } = setup({ file })
|
|
55
68
|
const { getByTestId } = root
|
|
@@ -115,4 +128,30 @@ describe('ImageLoader', () => {
|
|
|
115
128
|
const img = getByTestId('img_image')
|
|
116
129
|
expect(img.src).toEqual('http://valid/')
|
|
117
130
|
})
|
|
131
|
+
|
|
132
|
+
it('should render fallback component if other request failed & file is a PDF', async () => {
|
|
133
|
+
const file = {
|
|
134
|
+
_id: 'pdf',
|
|
135
|
+
class: 'pdf',
|
|
136
|
+
name: 'Demo.pdf',
|
|
137
|
+
mime: 'application/pdf',
|
|
138
|
+
links: {
|
|
139
|
+
icon: 'https://url.not.valid.anymore'
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
checkImageSource.mockRejectedValueOnce('KO')
|
|
143
|
+
const { root } = setup({ file })
|
|
144
|
+
const { getByTestId } = root
|
|
145
|
+
getMock.mockResolvedValue({
|
|
146
|
+
data: {
|
|
147
|
+
links: {
|
|
148
|
+
icon: 'http://urlnotvalidneither'
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
})
|
|
152
|
+
await waitFor(() => getByTestId('img_image_renderFallback'))
|
|
153
|
+
|
|
154
|
+
const img = getByTestId('img_image_renderFallback')
|
|
155
|
+
expect(img.src).toEqual('http://url.img_image_render_fallback/')
|
|
156
|
+
})
|
|
118
157
|
})
|
|
@@ -10,6 +10,7 @@ import { useCozyTheme } from 'cozy-ui/transpiled/react/CozyTheme'
|
|
|
10
10
|
import cloudWallpaper from '../../docs/cloud-wallpaper.jpg'
|
|
11
11
|
|
|
12
12
|
const theme = useCozyTheme()
|
|
13
|
+
const app = { name: "Test App", slug: "testapp", type: "app" }
|
|
13
14
|
|
|
14
15
|
;
|
|
15
16
|
|
|
@@ -17,13 +18,13 @@ const theme = useCozyTheme()
|
|
|
17
18
|
<Grid container spacing={1} style={{ background: `center / cover no-repeat url(${cloudWallpaper})` }}
|
|
18
19
|
>
|
|
19
20
|
<Grid item>
|
|
20
|
-
<SquareAppIcon app=
|
|
21
|
+
<SquareAppIcon app={app} name="Normal" />
|
|
21
22
|
</Grid>
|
|
22
23
|
<Grid item>
|
|
23
|
-
<SquareAppIcon app=
|
|
24
|
+
<SquareAppIcon app={app} name="Maintenance" variant="maintenance" />
|
|
24
25
|
</Grid>
|
|
25
26
|
<Grid item>
|
|
26
|
-
<SquareAppIcon app=
|
|
27
|
+
<SquareAppIcon app={app} name="Error" variant="error" />
|
|
27
28
|
</Grid>
|
|
28
29
|
<Grid item>
|
|
29
30
|
<SquareAppIcon name="Add" variant="add" />
|
|
@@ -2,13 +2,13 @@ import React from 'react'
|
|
|
2
2
|
import cx from 'classnames'
|
|
3
3
|
import PropTypes from 'prop-types'
|
|
4
4
|
import { makeStyles } from '@material-ui/styles'
|
|
5
|
+
import get from 'lodash/get'
|
|
5
6
|
|
|
6
7
|
import AppIcon from '../AppIcon'
|
|
7
8
|
import Badge from '../Badge'
|
|
8
9
|
import InfosBadge from '../InfosBadge'
|
|
9
10
|
import { nameToColor } from '../Avatar'
|
|
10
11
|
import Typography from '../Typography'
|
|
11
|
-
import { AppDoctype } from '../proptypes'
|
|
12
12
|
import Icon from '../Icon'
|
|
13
13
|
import iconPlus from '../Icons/Plus'
|
|
14
14
|
import iconWarning from '../Icons/WarningCircle'
|
|
@@ -61,9 +61,15 @@ const useStyles = makeStyles(theme => ({
|
|
|
61
61
|
}
|
|
62
62
|
}))
|
|
63
63
|
|
|
64
|
-
export const SquareAppIcon = ({
|
|
64
|
+
export const SquareAppIcon = ({
|
|
65
|
+
name,
|
|
66
|
+
variant,
|
|
67
|
+
IconContent,
|
|
68
|
+
...appIconProps
|
|
69
|
+
}) => {
|
|
65
70
|
const classes = useStyles()
|
|
66
|
-
const appName =
|
|
71
|
+
const appName =
|
|
72
|
+
name || get(appIconProps, 'app.name') || get(appIconProps, 'app') || ''
|
|
67
73
|
const letter = appName[0] || ''
|
|
68
74
|
|
|
69
75
|
return (
|
|
@@ -119,7 +125,7 @@ export const SquareAppIcon = ({ app, type, name, variant, IconContent }) => {
|
|
|
119
125
|
) : IconContent ? (
|
|
120
126
|
IconContent
|
|
121
127
|
) : (
|
|
122
|
-
<AppIcon
|
|
128
|
+
<AppIcon {...appIconProps} />
|
|
123
129
|
)}
|
|
124
130
|
</div>
|
|
125
131
|
)}
|
|
@@ -137,7 +143,6 @@ export const SquareAppIcon = ({ app, type, name, variant, IconContent }) => {
|
|
|
137
143
|
}
|
|
138
144
|
|
|
139
145
|
SquareAppIcon.propTypes = {
|
|
140
|
-
app: PropTypes.oneOfType([AppDoctype, PropTypes.string]),
|
|
141
146
|
name: PropTypes.string,
|
|
142
147
|
variant: PropTypes.oneOf([
|
|
143
148
|
'ghost',
|
|
@@ -146,8 +151,7 @@ SquareAppIcon.propTypes = {
|
|
|
146
151
|
'add',
|
|
147
152
|
'shortcut'
|
|
148
153
|
]),
|
|
149
|
-
IconContent: PropTypes.node
|
|
150
|
-
type: PropTypes.oneOf(['app', 'konnector'])
|
|
154
|
+
IconContent: PropTypes.node
|
|
151
155
|
}
|
|
152
156
|
|
|
153
157
|
export default SquareAppIcon
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import React, { Component } from 'react'
|
|
2
2
|
import Hammer from 'hammerjs'
|
|
3
3
|
|
|
4
|
+
import FileImageLoader from '../FileImageLoader'
|
|
5
|
+
|
|
4
6
|
import ViewerSpinner from './ViewerSpinner'
|
|
5
|
-
import ImageLoader from './ImageLoader'
|
|
6
7
|
import NoNetworkViewer from './NoNetworkViewer'
|
|
7
8
|
|
|
8
9
|
import styles from './styles.styl'
|
|
@@ -222,7 +223,7 @@ class ImageViewer extends Component {
|
|
|
222
223
|
<div className={styles['viewer-imageviewer']}>
|
|
223
224
|
{loading && <ViewerSpinner />}
|
|
224
225
|
{file && (
|
|
225
|
-
<
|
|
226
|
+
<FileImageLoader
|
|
226
227
|
file={file}
|
|
227
228
|
linkType="large"
|
|
228
229
|
onError={this.onImageError}
|
|
@@ -4,11 +4,11 @@ import { render, waitFor } from '@testing-library/react'
|
|
|
4
4
|
import { BreakpointsProvider } from '../hooks/useBreakpoints'
|
|
5
5
|
|
|
6
6
|
import DemoProvider from './docs/DemoProvider'
|
|
7
|
-
import { checkImageSource } from '
|
|
7
|
+
import { checkImageSource } from '../FileImageLoader/checkImageSource'
|
|
8
8
|
import ImageViewer from './ImageViewer'
|
|
9
9
|
|
|
10
|
-
jest.mock('
|
|
11
|
-
...jest.requireActual('
|
|
10
|
+
jest.mock('../FileImageLoader/checkImageSource', () => ({
|
|
11
|
+
...jest.requireActual('../FileImageLoader/checkImageSource'),
|
|
12
12
|
checkImageSource: jest.fn()
|
|
13
13
|
}))
|
|
14
14
|
|
|
@@ -7,10 +7,10 @@ import { isMobileApp } from 'cozy-device-helper'
|
|
|
7
7
|
import Alerter from '../Alerter'
|
|
8
8
|
import Spinner from '../Spinner'
|
|
9
9
|
import Button from '../Button'
|
|
10
|
+
import FileImageLoader from '../FileImageLoader'
|
|
10
11
|
|
|
11
12
|
import { withViewerLocales } from './withViewerLocales'
|
|
12
13
|
import DownloadButton from './NoViewer/DownloadButton'
|
|
13
|
-
import ImageLoader from './ImageLoader'
|
|
14
14
|
import NoViewer from './NoViewer'
|
|
15
15
|
|
|
16
16
|
import styles from './styles.styl'
|
|
@@ -80,7 +80,7 @@ export const PdfMobileViewer = ({ file, t, gestures }) => {
|
|
|
80
80
|
<div className={styles['viewer-pdfMobile']}>
|
|
81
81
|
{loading && <Spinner size="xxlarge" middle noMargin />}
|
|
82
82
|
{file && (
|
|
83
|
-
<
|
|
83
|
+
<FileImageLoader
|
|
84
84
|
file={file}
|
|
85
85
|
linkType="preview"
|
|
86
86
|
onError={onImageError}
|
|
@@ -20,6 +20,11 @@ const client = createMockClient({})
|
|
|
20
20
|
client.collection = jest.fn(() => ({
|
|
21
21
|
getDownloadLinkById: jest.fn()
|
|
22
22
|
}))
|
|
23
|
+
client.plugins.realtime = {
|
|
24
|
+
subscribe: jest.fn(),
|
|
25
|
+
unsubscribe: jest.fn(),
|
|
26
|
+
unsubscribeAll: jest.fn()
|
|
27
|
+
}
|
|
23
28
|
|
|
24
29
|
const file = {
|
|
25
30
|
_id: 'pdf',
|
package/react/index.js
CHANGED
|
@@ -97,3 +97,4 @@ export { default as Paper } from './Paper'
|
|
|
97
97
|
export { default as ProgressionBanner } from './ProgressionBanner'
|
|
98
98
|
export { default as Fab } from './Fab'
|
|
99
99
|
export { default as SquareAppIcon } from './SquareAppIcon'
|
|
100
|
+
export { default as FileImageLoader } from './FileImageLoader'
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
2
|
+
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {string} src - Image source
|
|
6
|
+
* @returns {Promise<void>}
|
|
7
|
+
*/
|
|
8
|
+
export var checkImageSource = /*#__PURE__*/function () {
|
|
9
|
+
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(src) {
|
|
10
|
+
var TTL, timeout, img, cleanImageLoader;
|
|
11
|
+
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
12
|
+
while (1) {
|
|
13
|
+
switch (_context.prev = _context.next) {
|
|
14
|
+
case 0:
|
|
15
|
+
TTL = 10000;
|
|
16
|
+
timeout = null;
|
|
17
|
+
img = null;
|
|
18
|
+
|
|
19
|
+
cleanImageLoader = function cleanImageLoader() {
|
|
20
|
+
clearTimeout(timeout);
|
|
21
|
+
img.onload = img.onerror = null;
|
|
22
|
+
img.src = '';
|
|
23
|
+
img = null;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
return _context.abrupt("return", new Promise(function (resolve, reject) {
|
|
27
|
+
img = new Image();
|
|
28
|
+
img.onload = resolve;
|
|
29
|
+
img.onerror = reject;
|
|
30
|
+
img.src = src;
|
|
31
|
+
timeout = setTimeout(function () {
|
|
32
|
+
return reject(new Error('Loading image took too long'));
|
|
33
|
+
}, TTL);
|
|
34
|
+
}).then(function () {
|
|
35
|
+
return cleanImageLoader();
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
case 5:
|
|
39
|
+
case "end":
|
|
40
|
+
return _context.stop();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}, _callee);
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
return function checkImageSource(_x) {
|
|
47
|
+
return _ref.apply(this, arguments);
|
|
48
|
+
};
|
|
49
|
+
}();
|
|
@@ -7,7 +7,7 @@ import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
|
|
|
7
7
|
import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized";
|
|
8
8
|
import _inherits from "@babel/runtime/helpers/inherits";
|
|
9
9
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
10
|
-
import
|
|
10
|
+
import { Component } from 'react';
|
|
11
11
|
import PropTypes from 'prop-types';
|
|
12
12
|
import { withClient } from 'cozy-client';
|
|
13
13
|
import logger from 'cozy-logger';
|
|
@@ -17,22 +17,22 @@ var LOADING_FALLBACK = 'LOADING_FALLBACK';
|
|
|
17
17
|
var LOADED = 'LOADED';
|
|
18
18
|
var FAILED = 'FAILED';
|
|
19
19
|
var GET_LINK = 'GET_LINK';
|
|
20
|
-
import { checkImageSource } from "cozy-ui/transpiled/react/
|
|
21
|
-
export var
|
|
22
|
-
_inherits(
|
|
20
|
+
import { checkImageSource } from "cozy-ui/transpiled/react/FileImageLoader/checkImageSource";
|
|
21
|
+
export var FileImageLoader = /*#__PURE__*/function (_Component) {
|
|
22
|
+
_inherits(FileImageLoader, _Component);
|
|
23
23
|
|
|
24
|
-
function
|
|
24
|
+
function FileImageLoader() {
|
|
25
25
|
var _getPrototypeOf2;
|
|
26
26
|
|
|
27
27
|
var _this;
|
|
28
28
|
|
|
29
|
-
_classCallCheck(this,
|
|
29
|
+
_classCallCheck(this, FileImageLoader);
|
|
30
30
|
|
|
31
31
|
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
32
32
|
args[_key] = arguments[_key];
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
_this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(
|
|
35
|
+
_this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(FileImageLoader)).call.apply(_getPrototypeOf2, [this].concat(args)));
|
|
36
36
|
|
|
37
37
|
_defineProperty(_assertThisInitialized(_this), "state", {
|
|
38
38
|
src: null
|
|
@@ -40,15 +40,29 @@ export var ImageLoader = /*#__PURE__*/function (_React$Component) {
|
|
|
40
40
|
|
|
41
41
|
_defineProperty(_assertThisInitialized(_this), "_mounted", false);
|
|
42
42
|
|
|
43
|
+
_defineProperty(_assertThisInitialized(_this), "handleCreate", function (doc) {
|
|
44
|
+
var _this$props = _this.props,
|
|
45
|
+
file = _this$props.file,
|
|
46
|
+
linkType = _this$props.linkType;
|
|
47
|
+
|
|
48
|
+
if (file._id === doc._id && doc.format === linkType) {
|
|
49
|
+
_this.loadLink();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
43
53
|
return _this;
|
|
44
54
|
}
|
|
45
55
|
|
|
46
|
-
_createClass(
|
|
56
|
+
_createClass(FileImageLoader, [{
|
|
47
57
|
key: "componentDidMount",
|
|
48
58
|
value: function componentDidMount() {
|
|
59
|
+
var client = this.props.client;
|
|
49
60
|
this._mounted = true;
|
|
50
61
|
this.status = PENDING;
|
|
51
62
|
this.loadNextSrc();
|
|
63
|
+
this.realtime = client.plugins.realtime;
|
|
64
|
+
this.type = 'io.cozy.files.thumbnails';
|
|
65
|
+
this.realtime.subscribe('created', this.type, this.handleCreate);
|
|
52
66
|
}
|
|
53
67
|
}, {
|
|
54
68
|
key: "componentWillUnmount",
|
|
@@ -59,7 +73,14 @@ export var ImageLoader = /*#__PURE__*/function (_React$Component) {
|
|
|
59
73
|
this.img.onload = this.img.onerror = null;
|
|
60
74
|
this.img.src = '';
|
|
61
75
|
}
|
|
76
|
+
|
|
77
|
+
this.realtime && this.realtime.unsubscribe('created', this.type, this.handleCreate);
|
|
62
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Reload the link when realtime tell us that the
|
|
81
|
+
* thumbnail is created. By default linkType === small
|
|
82
|
+
*/
|
|
83
|
+
|
|
63
84
|
}, {
|
|
64
85
|
key: "getFileId",
|
|
65
86
|
value: function getFileId(file) {
|
|
@@ -120,14 +141,14 @@ export var ImageLoader = /*#__PURE__*/function (_React$Component) {
|
|
|
120
141
|
key: "getLink",
|
|
121
142
|
value: function () {
|
|
122
143
|
var _getLink = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
|
|
123
|
-
var _this$
|
|
144
|
+
var _this$props2, file, linkType, client, link, src;
|
|
124
145
|
|
|
125
146
|
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
|
|
126
147
|
while (1) {
|
|
127
148
|
switch (_context2.prev = _context2.next) {
|
|
128
149
|
case 0:
|
|
129
150
|
this.status = GET_LINK;
|
|
130
|
-
_this$
|
|
151
|
+
_this$props2 = this.props, file = _this$props2.file, linkType = _this$props2.linkType, client = _this$props2.client;
|
|
131
152
|
_context2.prev = 2;
|
|
132
153
|
link = file.links ? file.links[linkType] : false;
|
|
133
154
|
|
|
@@ -151,16 +172,15 @@ export var ImageLoader = /*#__PURE__*/function (_React$Component) {
|
|
|
151
172
|
});
|
|
152
173
|
}
|
|
153
174
|
|
|
154
|
-
_context2.next =
|
|
175
|
+
_context2.next = 15;
|
|
155
176
|
break;
|
|
156
177
|
|
|
157
178
|
case 12:
|
|
158
179
|
_context2.prev = 12;
|
|
159
180
|
_context2.t0 = _context2["catch"](2);
|
|
160
|
-
logger.error(_context2.t0);
|
|
161
181
|
this.loadNextSrc(_context2.t0);
|
|
162
182
|
|
|
163
|
-
case
|
|
183
|
+
case 15:
|
|
164
184
|
case "end":
|
|
165
185
|
return _context2.stop();
|
|
166
186
|
}
|
|
@@ -178,14 +198,14 @@ export var ImageLoader = /*#__PURE__*/function (_React$Component) {
|
|
|
178
198
|
key: "loadLink",
|
|
179
199
|
value: function () {
|
|
180
200
|
var _loadLink = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
|
|
181
|
-
var _this$
|
|
201
|
+
var _this$props3, file, linkType, client, links, link, src;
|
|
182
202
|
|
|
183
203
|
return _regeneratorRuntime.wrap(function _callee3$(_context3) {
|
|
184
204
|
while (1) {
|
|
185
205
|
switch (_context3.prev = _context3.next) {
|
|
186
206
|
case 0:
|
|
187
207
|
this.status = LOADING_LINK;
|
|
188
|
-
_this$
|
|
208
|
+
_this$props3 = this.props, file = _this$props3.file, linkType = _this$props3.linkType, client = _this$props3.client;
|
|
189
209
|
_context3.prev = 2;
|
|
190
210
|
_context3.next = 5;
|
|
191
211
|
return this.fetchFileLinks(file);
|
|
@@ -241,14 +261,14 @@ export var ImageLoader = /*#__PURE__*/function (_React$Component) {
|
|
|
241
261
|
key: "loadFallback",
|
|
242
262
|
value: function () {
|
|
243
263
|
var _loadFallback = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4() {
|
|
244
|
-
var _this$
|
|
264
|
+
var _this$props4, file, client, src;
|
|
245
265
|
|
|
246
266
|
return _regeneratorRuntime.wrap(function _callee4$(_context4) {
|
|
247
267
|
while (1) {
|
|
248
268
|
switch (_context4.prev = _context4.next) {
|
|
249
269
|
case 0:
|
|
250
270
|
this.status = LOADING_FALLBACK;
|
|
251
|
-
_this$
|
|
271
|
+
_this$props4 = this.props, file = _this$props4.file, client = _this$props4.client;
|
|
252
272
|
_context4.prev = 2;
|
|
253
273
|
|
|
254
274
|
if (!(file.class === 'pdf')) {
|
|
@@ -301,24 +321,24 @@ export var ImageLoader = /*#__PURE__*/function (_React$Component) {
|
|
|
301
321
|
key: "render",
|
|
302
322
|
value: function render() {
|
|
303
323
|
var src = this.state.src;
|
|
304
|
-
var _this$
|
|
305
|
-
render = _this$
|
|
306
|
-
renderFallback = _this$
|
|
324
|
+
var _this$props5 = this.props,
|
|
325
|
+
render = _this$props5.render,
|
|
326
|
+
renderFallback = _this$props5.renderFallback;
|
|
307
327
|
if (src) return render(src);else if (renderFallback) return renderFallback();else return null;
|
|
308
328
|
}
|
|
309
329
|
}]);
|
|
310
330
|
|
|
311
|
-
return
|
|
312
|
-
}(
|
|
313
|
-
|
|
331
|
+
return FileImageLoader;
|
|
332
|
+
}(Component);
|
|
333
|
+
FileImageLoader.propTypes = {
|
|
314
334
|
file: PropTypes.object.isRequired,
|
|
315
335
|
render: PropTypes.func.isRequired,
|
|
316
|
-
linkType: PropTypes.oneOf(['small', 'medium', 'large', 'preview']),
|
|
336
|
+
linkType: PropTypes.oneOf(['small', 'medium', 'large', 'preview', 'icon']),
|
|
317
337
|
onError: PropTypes.func,
|
|
318
338
|
renderFallback: PropTypes.func
|
|
319
339
|
};
|
|
320
|
-
|
|
340
|
+
FileImageLoader.defaultProps = {
|
|
321
341
|
linkType: 'small',
|
|
322
342
|
onError: function onError() {}
|
|
323
343
|
};
|
|
324
|
-
export default withClient(
|
|
344
|
+
export default withClient(FileImageLoader);
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
|
1
2
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
3
|
import React from 'react';
|
|
3
4
|
import cx from 'classnames';
|
|
4
5
|
import PropTypes from 'prop-types';
|
|
5
6
|
import { makeStyles } from '@material-ui/styles';
|
|
7
|
+
import get from 'lodash/get';
|
|
6
8
|
import AppIcon from "cozy-ui/transpiled/react/AppIcon";
|
|
7
9
|
import Badge from "cozy-ui/transpiled/react/Badge";
|
|
8
10
|
import InfosBadge from "cozy-ui/transpiled/react/InfosBadge";
|
|
9
11
|
import { nameToColor } from "cozy-ui/transpiled/react/Avatar";
|
|
10
12
|
import Typography from "cozy-ui/transpiled/react/Typography";
|
|
11
|
-
import { AppDoctype } from "cozy-ui/transpiled/react/proptypes";
|
|
12
13
|
import Icon from "cozy-ui/transpiled/react/Icon";
|
|
13
14
|
import iconPlus from "cozy-ui/transpiled/react/Icons/Plus";
|
|
14
15
|
import iconWarning from "cozy-ui/transpiled/react/Icons/WarningCircle";
|
|
@@ -74,13 +75,13 @@ var useStyles = makeStyles(function (theme) {
|
|
|
74
75
|
export var SquareAppIcon = function SquareAppIcon(_ref) {
|
|
75
76
|
var _cx;
|
|
76
77
|
|
|
77
|
-
var
|
|
78
|
-
type = _ref.type,
|
|
79
|
-
name = _ref.name,
|
|
78
|
+
var name = _ref.name,
|
|
80
79
|
variant = _ref.variant,
|
|
81
|
-
IconContent = _ref.IconContent
|
|
80
|
+
IconContent = _ref.IconContent,
|
|
81
|
+
appIconProps = _objectWithoutProperties(_ref, ["name", "variant", "IconContent"]);
|
|
82
|
+
|
|
82
83
|
var classes = useStyles();
|
|
83
|
-
var appName = name ||
|
|
84
|
+
var appName = name || get(appIconProps, 'app.name') || get(appIconProps, 'app') || '';
|
|
84
85
|
var letter = appName[0] || '';
|
|
85
86
|
return React.createElement("div", {
|
|
86
87
|
"data-testid": "square-app-icon",
|
|
@@ -115,20 +116,15 @@ export var SquareAppIcon = function SquareAppIcon(_ref) {
|
|
|
115
116
|
}, variant === 'add' ? React.createElement(Icon, {
|
|
116
117
|
icon: iconPlus,
|
|
117
118
|
color: color
|
|
118
|
-
}) : IconContent ? IconContent : React.createElement(AppIcon, {
|
|
119
|
-
app: app,
|
|
120
|
-
type: type
|
|
121
|
-
})))), React.createElement(Typography, {
|
|
119
|
+
}) : IconContent ? IconContent : React.createElement(AppIcon, appIconProps)))), React.createElement(Typography, {
|
|
122
120
|
className: cx(classes.name, 'u-spacellipsis'),
|
|
123
121
|
variant: "h6",
|
|
124
122
|
align: "center"
|
|
125
123
|
}, appName));
|
|
126
124
|
};
|
|
127
125
|
SquareAppIcon.propTypes = {
|
|
128
|
-
app: PropTypes.oneOfType([AppDoctype, PropTypes.string]),
|
|
129
126
|
name: PropTypes.string,
|
|
130
127
|
variant: PropTypes.oneOf(['ghost', 'maintenance', 'error', 'add', 'shortcut']),
|
|
131
|
-
IconContent: PropTypes.node
|
|
132
|
-
type: PropTypes.oneOf(['app', 'konnector'])
|
|
128
|
+
IconContent: PropTypes.node
|
|
133
129
|
};
|
|
134
130
|
export default SquareAppIcon;
|
|
@@ -8,8 +8,8 @@ import _inherits from "@babel/runtime/helpers/inherits";
|
|
|
8
8
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
9
9
|
import React, { Component } from 'react';
|
|
10
10
|
import Hammer from 'hammerjs';
|
|
11
|
+
import FileImageLoader from "cozy-ui/transpiled/react/FileImageLoader";
|
|
11
12
|
import ViewerSpinner from "cozy-ui/transpiled/react/Viewer/ViewerSpinner";
|
|
12
|
-
import ImageLoader from "cozy-ui/transpiled/react/Viewer/ImageLoader";
|
|
13
13
|
import NoNetworkViewer from "cozy-ui/transpiled/react/Viewer/NoNetworkViewer";
|
|
14
14
|
var styles = {
|
|
15
15
|
"CozyTheme--inverted": "styles__CozyTheme--inverted___1II8o",
|
|
@@ -269,7 +269,7 @@ var ImageViewer = /*#__PURE__*/function (_Component) {
|
|
|
269
269
|
};
|
|
270
270
|
return React.createElement("div", {
|
|
271
271
|
className: styles['viewer-imageviewer']
|
|
272
|
-
}, loading && React.createElement(ViewerSpinner, null), file && React.createElement(
|
|
272
|
+
}, loading && React.createElement(ViewerSpinner, null), file && React.createElement(FileImageLoader, {
|
|
273
273
|
file: file,
|
|
274
274
|
linkType: "large",
|
|
275
275
|
onError: this.onImageError,
|
|
@@ -8,9 +8,9 @@ import { isMobileApp } from 'cozy-device-helper';
|
|
|
8
8
|
import Alerter from "cozy-ui/transpiled/react/Alerter";
|
|
9
9
|
import Spinner from "cozy-ui/transpiled/react/Spinner";
|
|
10
10
|
import Button from "cozy-ui/transpiled/react/Button";
|
|
11
|
+
import FileImageLoader from "cozy-ui/transpiled/react/FileImageLoader";
|
|
11
12
|
import { withViewerLocales } from "cozy-ui/transpiled/react/Viewer/withViewerLocales";
|
|
12
13
|
import DownloadButton from "cozy-ui/transpiled/react/Viewer/NoViewer/DownloadButton";
|
|
13
|
-
import ImageLoader from "cozy-ui/transpiled/react/Viewer/ImageLoader";
|
|
14
14
|
import NoViewer from "cozy-ui/transpiled/react/Viewer/NoViewer";
|
|
15
15
|
var styles = {
|
|
16
16
|
"CozyTheme--inverted": "styles__CozyTheme--inverted___1II8o",
|
|
@@ -146,7 +146,7 @@ export var PdfMobileViewer = function PdfMobileViewer(_ref) {
|
|
|
146
146
|
size: "xxlarge",
|
|
147
147
|
middle: true,
|
|
148
148
|
noMargin: true
|
|
149
|
-
}), file && React.createElement(
|
|
149
|
+
}), file && React.createElement(FileImageLoader, {
|
|
150
150
|
file: file,
|
|
151
151
|
linkType: "preview",
|
|
152
152
|
onError: onImageError,
|
|
@@ -74,4 +74,5 @@ export { default as CozyTheme } from './CozyTheme';
|
|
|
74
74
|
export { default as Paper } from './Paper';
|
|
75
75
|
export { default as ProgressionBanner } from './ProgressionBanner';
|
|
76
76
|
export { default as Fab } from './Fab';
|
|
77
|
-
export { default as SquareAppIcon } from './SquareAppIcon';
|
|
77
|
+
export { default as SquareAppIcon } from './SquareAppIcon';
|
|
78
|
+
export { default as FileImageLoader } from './FileImageLoader';
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
var TTL = 10000;
|
|
2
|
-
export var checkImageSource = function checkImageSource(src) {
|
|
3
|
-
var timeout = null;
|
|
4
|
-
var img = null;
|
|
5
|
-
|
|
6
|
-
var cleanImageLoader = function cleanImageLoader() {
|
|
7
|
-
clearTimeout(timeout);
|
|
8
|
-
img.onload = img.onerror = null;
|
|
9
|
-
img.src = '';
|
|
10
|
-
img = null;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
return new Promise(function (resolve, reject) {
|
|
14
|
-
img = new Image();
|
|
15
|
-
img.onload = resolve;
|
|
16
|
-
img.onerror = reject;
|
|
17
|
-
img.src = src;
|
|
18
|
-
timeout = setTimeout(function () {
|
|
19
|
-
return reject(new Error('Loading image took too long'));
|
|
20
|
-
}, TTL);
|
|
21
|
-
}).then(function () {
|
|
22
|
-
return cleanImageLoader();
|
|
23
|
-
});
|
|
24
|
-
};
|