cozy-sharing 18.0.0 → 19.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/dist/components/Recipient/LinkRecipientPermissions.js +18 -129
- package/dist/components/Recipient/actions/editPermissionLink.js +71 -0
- package/dist/components/Recipient/actions/readOnlyPermissionLink.js +63 -0
- package/dist/components/ShareByLink.js +45 -185
- package/dist/components/ShareDialogCozyToCozy.js +2 -6
- package/dist/components/ShareDialogOnlyByLink.js +2 -8
- package/dist/components/ShareDialogTwoStepsConfirmationContainer.js +1 -3
- package/dist/components/ShareModal/EditableSharingModal.js +1 -7
- package/dist/components/ShareModal.js +0 -12
- package/dist/components/ShareRestrictionModal/BoxDate.js +77 -0
- package/dist/components/ShareRestrictionModal/BoxEditingRights.js +83 -0
- package/dist/components/ShareRestrictionModal/BoxPassword.js +138 -0
- package/dist/components/ShareRestrictionModal/ShareRestrictionContentModal.js +94 -0
- package/dist/components/ShareRestrictionModal/ShareRestrictionModal.js +260 -0
- package/dist/components/ShareRestrictionModal/helpers.js +275 -0
- package/dist/components/ShareRestrictionModal/helpers.spec.js +110 -0
- package/dist/components/WhoHasAccess.js +1 -5
- package/locales/en.json +28 -0
- package/locales/fr.json +29 -0
- package/package.json +11 -7
- package/src/components/Recipient/LinkRecipientPermissions.jsx +28 -137
- package/src/components/Recipient/actions/editPermissionLink.js +63 -0
- package/src/components/Recipient/actions/readOnlyPermissionLink.js +59 -0
- package/src/components/ShareByLink.jsx +20 -96
- package/src/components/ShareDialogCozyToCozy.jsx +1 -5
- package/src/components/ShareDialogOnlyByLink.jsx +0 -6
- package/src/components/ShareDialogTwoStepsConfirmationContainer.jsx +0 -2
- package/src/components/ShareModal/EditableSharingModal.jsx +1 -7
- package/src/components/ShareModal.jsx +0 -12
- package/src/components/ShareRestrictionModal/BoxDate.jsx +76 -0
- package/src/components/ShareRestrictionModal/BoxEditingRights.jsx +91 -0
- package/src/components/ShareRestrictionModal/BoxPassword.jsx +112 -0
- package/src/components/ShareRestrictionModal/ShareRestrictionContentModal.jsx +102 -0
- package/src/components/ShareRestrictionModal/ShareRestrictionModal.jsx +177 -0
- package/src/components/ShareRestrictionModal/helpers.js +193 -0
- package/src/components/ShareRestrictionModal/helpers.spec.js +78 -0
- package/src/components/WhoHasAccess.jsx +1 -5
- package/dist/components/EditLinkPermissionDialog.js +0 -72
- package/dist/components/EditLinkPermissionDialog.spec.js +0 -48
- package/src/components/EditLinkPermissionDialog.jsx +0 -89
- package/src/components/EditLinkPermissionDialog.spec.jsx +0 -53
|
@@ -15,11 +15,8 @@ export const ShareModal = ({
|
|
|
15
15
|
needsContactsPermission,
|
|
16
16
|
onClose,
|
|
17
17
|
onRevoke,
|
|
18
|
-
onRevokeLink,
|
|
19
18
|
onRevokeSelf,
|
|
20
19
|
onShare,
|
|
21
|
-
onShareByLink,
|
|
22
|
-
onUpdateShareLinkPermissions,
|
|
23
20
|
permissions,
|
|
24
21
|
recipients,
|
|
25
22
|
sharing,
|
|
@@ -43,9 +40,6 @@ export const ShareModal = ({
|
|
|
43
40
|
documentType={documentType}
|
|
44
41
|
link={link}
|
|
45
42
|
onClose={onClose}
|
|
46
|
-
onRevokeLink={onRevokeLink}
|
|
47
|
-
onShareByLink={onShareByLink}
|
|
48
|
-
onUpdateShareLinkPermissions={onUpdateShareLinkPermissions}
|
|
49
43
|
permissions={permissions}
|
|
50
44
|
/>
|
|
51
45
|
) : (
|
|
@@ -59,11 +53,8 @@ export const ShareModal = ({
|
|
|
59
53
|
needsContactsPermission={needsContactsPermission}
|
|
60
54
|
onClose={onClose}
|
|
61
55
|
onRevoke={onRevoke}
|
|
62
|
-
onRevokeLink={onRevokeLink}
|
|
63
56
|
onRevokeSelf={onRevokeSelf}
|
|
64
57
|
onShare={onShare}
|
|
65
|
-
onShareByLink={onShareByLink}
|
|
66
|
-
onUpdateShareLinkPermissions={onUpdateShareLinkPermissions}
|
|
67
58
|
permissions={permissions}
|
|
68
59
|
recipients={recipients}
|
|
69
60
|
sharing={sharing}
|
|
@@ -90,10 +81,7 @@ ShareModal.propTypes = {
|
|
|
90
81
|
needsContactsPermission: PropTypes.bool,
|
|
91
82
|
onClose: PropTypes.func.isRequired,
|
|
92
83
|
onRevoke: PropTypes.func.isRequired,
|
|
93
|
-
onRevokeLink: PropTypes.func.isRequired,
|
|
94
84
|
onShare: PropTypes.func.isRequired,
|
|
95
|
-
onShareByLink: PropTypes.func.isRequired,
|
|
96
|
-
onUpdateShareLinkPermissions: PropTypes.func.isRequired,
|
|
97
85
|
permissions: PropTypes.array.isRequired,
|
|
98
86
|
recipients: PropTypes.array.isRequired,
|
|
99
87
|
sharingDesc: PropTypes.string,
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { format } from 'date-fns'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
|
|
5
|
+
import Box from 'cozy-ui/transpiled/react/Box'
|
|
6
|
+
import DatePicker from 'cozy-ui/transpiled/react/DatePicker'
|
|
7
|
+
import Icon from 'cozy-ui/transpiled/react/Icon'
|
|
8
|
+
import List from 'cozy-ui/transpiled/react/List'
|
|
9
|
+
import ListItem from 'cozy-ui/transpiled/react/ListItem'
|
|
10
|
+
import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
|
|
11
|
+
import ListItemSecondaryAction from 'cozy-ui/transpiled/react/ListItemSecondaryAction'
|
|
12
|
+
import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
|
|
13
|
+
import Switch from 'cozy-ui/transpiled/react/Switch'
|
|
14
|
+
import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
15
|
+
|
|
16
|
+
export const BoxDate = ({ onChange, date, toggle, onToggle }) => {
|
|
17
|
+
const { t, lang } = useI18n()
|
|
18
|
+
const formatDate = lang === 'fr' ? 'dd/MM/yyyy' : 'MM/dd/yyyy'
|
|
19
|
+
|
|
20
|
+
const handleDateToggle = val => {
|
|
21
|
+
const value = val?.target?.checked ?? val
|
|
22
|
+
onToggle(value)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<Box borderRadius="0.5rem" border="1px solid var(--borderMainColor)">
|
|
27
|
+
<List className="u-p-0">
|
|
28
|
+
<ListItem
|
|
29
|
+
button
|
|
30
|
+
size="large"
|
|
31
|
+
ellipsis={false}
|
|
32
|
+
onClick={() => handleDateToggle(!toggle)}
|
|
33
|
+
>
|
|
34
|
+
<ListItemIcon>
|
|
35
|
+
<Icon icon="calendar" />
|
|
36
|
+
</ListItemIcon>
|
|
37
|
+
<ListItemText primary={t('BoxDate.text')} />
|
|
38
|
+
<ListItemSecondaryAction>
|
|
39
|
+
<Switch
|
|
40
|
+
edge="end"
|
|
41
|
+
color="primary"
|
|
42
|
+
checked={toggle}
|
|
43
|
+
onChange={handleDateToggle}
|
|
44
|
+
/>
|
|
45
|
+
</ListItemSecondaryAction>
|
|
46
|
+
</ListItem>
|
|
47
|
+
</List>
|
|
48
|
+
{toggle && (
|
|
49
|
+
<div className="u-pt-half u-ph-1 u-pb-1">
|
|
50
|
+
<DatePicker
|
|
51
|
+
className="u-w-100"
|
|
52
|
+
enableKeyboard
|
|
53
|
+
label={t('BoxDate.label')}
|
|
54
|
+
placeholder={format(new Date(), formatDate)}
|
|
55
|
+
value={date}
|
|
56
|
+
minDate={new Date()}
|
|
57
|
+
onChange={onChange}
|
|
58
|
+
inputProps={{
|
|
59
|
+
inputMode: 'numeric'
|
|
60
|
+
}}
|
|
61
|
+
KeyboardButtonProps={{
|
|
62
|
+
'aria-label': t('BoxDate.label')
|
|
63
|
+
}}
|
|
64
|
+
/>
|
|
65
|
+
</div>
|
|
66
|
+
)}
|
|
67
|
+
</Box>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
BoxDate.propTypes = {
|
|
72
|
+
onChange: PropTypes.func.isRequired,
|
|
73
|
+
date: PropTypes.instanceOf(Date),
|
|
74
|
+
toggle: PropTypes.bool.isRequired,
|
|
75
|
+
onToggle: PropTypes.func.isRequired
|
|
76
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import PropTypes from 'prop-types'
|
|
2
|
+
import React, { useReducer, useRef } from 'react'
|
|
3
|
+
|
|
4
|
+
import { isDirectory as isDir } from 'cozy-client/dist/models/file'
|
|
5
|
+
import ActionsMenu from 'cozy-ui/transpiled/react/ActionsMenu'
|
|
6
|
+
import { makeActions } from 'cozy-ui/transpiled/react/ActionsMenu/Actions'
|
|
7
|
+
import Box from 'cozy-ui/transpiled/react/Box'
|
|
8
|
+
import Icon from 'cozy-ui/transpiled/react/Icon'
|
|
9
|
+
import List from 'cozy-ui/transpiled/react/List'
|
|
10
|
+
import ListItem from 'cozy-ui/transpiled/react/ListItem'
|
|
11
|
+
import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
|
|
12
|
+
import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
|
|
13
|
+
import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
14
|
+
|
|
15
|
+
import { useSharingContext } from '../../hooks/useSharingContext'
|
|
16
|
+
import { editPermissionLink } from '../Recipient/actions/editPermissionLink'
|
|
17
|
+
import { readOnlyPermissionLink } from '../Recipient/actions/readOnlyPermissionLink'
|
|
18
|
+
|
|
19
|
+
export const BoxEditingRights = ({ file, editingRights, setEditingRights }) => {
|
|
20
|
+
const [isMenuDisplayed, toggleMenuDisplayed] = useReducer(
|
|
21
|
+
state => !state,
|
|
22
|
+
false
|
|
23
|
+
)
|
|
24
|
+
const buttonRef = useRef()
|
|
25
|
+
const { t } = useI18n()
|
|
26
|
+
const { documentType } = useSharingContext()
|
|
27
|
+
|
|
28
|
+
const isDirectory = isDir(file)
|
|
29
|
+
|
|
30
|
+
const actions = makeActions([readOnlyPermissionLink, editPermissionLink], {
|
|
31
|
+
t,
|
|
32
|
+
editingRights,
|
|
33
|
+
setEditingRights,
|
|
34
|
+
documentType,
|
|
35
|
+
isDirectory
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const textSecondary =
|
|
39
|
+
editingRights === 'readOnly'
|
|
40
|
+
? t('Share.permissionLink.readDescription')
|
|
41
|
+
: t('Share.permissionLink.writeDescription')
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<>
|
|
45
|
+
<Box
|
|
46
|
+
borderRadius="0.5rem"
|
|
47
|
+
border="1px solid var(--borderMainColor)"
|
|
48
|
+
ref={buttonRef}
|
|
49
|
+
>
|
|
50
|
+
<List className="u-p-0">
|
|
51
|
+
<ListItem
|
|
52
|
+
button
|
|
53
|
+
size="large"
|
|
54
|
+
ellipsis={false}
|
|
55
|
+
onClick={toggleMenuDisplayed}
|
|
56
|
+
>
|
|
57
|
+
<ListItemIcon>
|
|
58
|
+
<Icon icon="people" />
|
|
59
|
+
</ListItemIcon>
|
|
60
|
+
<ListItemText
|
|
61
|
+
primary={t('BoxEditingRights.text')}
|
|
62
|
+
secondary={textSecondary}
|
|
63
|
+
/>
|
|
64
|
+
<ListItemIcon className="u-mr-half">
|
|
65
|
+
<Icon icon="bottom" />
|
|
66
|
+
</ListItemIcon>
|
|
67
|
+
</ListItem>
|
|
68
|
+
</List>
|
|
69
|
+
</Box>
|
|
70
|
+
|
|
71
|
+
<ActionsMenu
|
|
72
|
+
ref={buttonRef}
|
|
73
|
+
open={isMenuDisplayed}
|
|
74
|
+
docs={[file]}
|
|
75
|
+
actions={actions}
|
|
76
|
+
anchorOrigin={{
|
|
77
|
+
vertical: 'bottom',
|
|
78
|
+
horizontal: 'right'
|
|
79
|
+
}}
|
|
80
|
+
autoClose
|
|
81
|
+
onClose={toggleMenuDisplayed}
|
|
82
|
+
/>
|
|
83
|
+
</>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
BoxEditingRights.propTypes = {
|
|
88
|
+
file: PropTypes.object.isRequired,
|
|
89
|
+
editingRights: PropTypes.oneOf(['readOnly', 'write']).isRequired,
|
|
90
|
+
setEditingRights: PropTypes.func.isRequired
|
|
91
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import PropTypes from 'prop-types'
|
|
2
|
+
import React, { useState } from 'react'
|
|
3
|
+
|
|
4
|
+
import Box from 'cozy-ui/transpiled/react/Box'
|
|
5
|
+
import Icon from 'cozy-ui/transpiled/react/Icon'
|
|
6
|
+
import IconButton from 'cozy-ui/transpiled/react/IconButton'
|
|
7
|
+
import List from 'cozy-ui/transpiled/react/List'
|
|
8
|
+
import ListItem from 'cozy-ui/transpiled/react/ListItem'
|
|
9
|
+
import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
|
|
10
|
+
import ListItemSecondaryAction from 'cozy-ui/transpiled/react/ListItemSecondaryAction'
|
|
11
|
+
import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
|
|
12
|
+
import Switch from 'cozy-ui/transpiled/react/Switch'
|
|
13
|
+
import TextField from 'cozy-ui/transpiled/react/TextField'
|
|
14
|
+
import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
|
|
15
|
+
import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
16
|
+
|
|
17
|
+
import { copyToClipboard } from './helpers'
|
|
18
|
+
|
|
19
|
+
export const BoxPassword = ({
|
|
20
|
+
onChange,
|
|
21
|
+
password,
|
|
22
|
+
onToggle,
|
|
23
|
+
toggle,
|
|
24
|
+
helperText,
|
|
25
|
+
inputProps
|
|
26
|
+
}) => {
|
|
27
|
+
const { t } = useI18n()
|
|
28
|
+
const { showAlert } = useAlert()
|
|
29
|
+
const [displayHelper, setDisplayHelper] = useState(false)
|
|
30
|
+
const inputRef = React.useRef()
|
|
31
|
+
|
|
32
|
+
const handlePasswordToggle = val => {
|
|
33
|
+
const value = val?.target?.checked ?? val
|
|
34
|
+
onToggle(value)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const handleChange = val => {
|
|
38
|
+
const value = val?.target?.value ?? val
|
|
39
|
+
onChange(value)
|
|
40
|
+
}
|
|
41
|
+
const handleOnFocus = () => {
|
|
42
|
+
setDisplayHelper(false)
|
|
43
|
+
}
|
|
44
|
+
const handleOnBlur = () => {
|
|
45
|
+
handleChange(inputRef.current.value.trim())
|
|
46
|
+
setDisplayHelper(true)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const handleCopy = async () => {
|
|
50
|
+
await copyToClipboard(password, { t, showAlert })
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Box borderRadius="0.5rem" border="1px solid var(--borderMainColor)">
|
|
55
|
+
<List className="u-p-0">
|
|
56
|
+
<ListItem
|
|
57
|
+
button
|
|
58
|
+
size="large"
|
|
59
|
+
ellipsis={false}
|
|
60
|
+
onClick={() => handlePasswordToggle(!toggle)}
|
|
61
|
+
>
|
|
62
|
+
<ListItemIcon>
|
|
63
|
+
<Icon icon="password" />
|
|
64
|
+
</ListItemIcon>
|
|
65
|
+
<ListItemText primary={t('BoxPassword.text')} />
|
|
66
|
+
<ListItemSecondaryAction>
|
|
67
|
+
<Switch
|
|
68
|
+
edge="end"
|
|
69
|
+
color="primary"
|
|
70
|
+
checked={toggle}
|
|
71
|
+
onChange={handlePasswordToggle}
|
|
72
|
+
/>
|
|
73
|
+
</ListItemSecondaryAction>
|
|
74
|
+
</ListItem>
|
|
75
|
+
</List>
|
|
76
|
+
{toggle && (
|
|
77
|
+
<div className="u-pt-half u-ph-1 u-pb-1">
|
|
78
|
+
<TextField
|
|
79
|
+
inputRef={inputRef}
|
|
80
|
+
label={t('BoxPassword.label')}
|
|
81
|
+
value={password}
|
|
82
|
+
error={displayHelper && !!helperText}
|
|
83
|
+
helperText={displayHelper && helperText}
|
|
84
|
+
onChange={handleChange}
|
|
85
|
+
onFocus={handleOnFocus}
|
|
86
|
+
onBlur={handleOnBlur}
|
|
87
|
+
fullWidth
|
|
88
|
+
type="text"
|
|
89
|
+
variant="outlined"
|
|
90
|
+
InputProps={{
|
|
91
|
+
endAdornment: (
|
|
92
|
+
<IconButton onClick={handleCopy}>
|
|
93
|
+
<Icon icon="copy" />
|
|
94
|
+
</IconButton>
|
|
95
|
+
)
|
|
96
|
+
}}
|
|
97
|
+
inputProps={inputProps}
|
|
98
|
+
/>
|
|
99
|
+
</div>
|
|
100
|
+
)}
|
|
101
|
+
</Box>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
BoxPassword.propTypes = {
|
|
106
|
+
onChange: PropTypes.func.isRequired,
|
|
107
|
+
password: PropTypes.string,
|
|
108
|
+
onToggle: PropTypes.func.isRequired,
|
|
109
|
+
toggle: PropTypes.bool.isRequired,
|
|
110
|
+
helperText: PropTypes.string,
|
|
111
|
+
inputProps: PropTypes.object
|
|
112
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { addDays } from 'date-fns'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
|
|
5
|
+
import Box from 'cozy-ui/transpiled/react/Box'
|
|
6
|
+
import Typography from 'cozy-ui/transpiled/react/Typography'
|
|
7
|
+
import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
8
|
+
|
|
9
|
+
import { BoxDate } from './BoxDate'
|
|
10
|
+
import { BoxEditingRights } from './BoxEditingRights'
|
|
11
|
+
import { BoxPassword } from './BoxPassword'
|
|
12
|
+
|
|
13
|
+
const PASSWORD_MIN_LENGTH = 4
|
|
14
|
+
|
|
15
|
+
export const ShareRestrictionContentModal = ({
|
|
16
|
+
file,
|
|
17
|
+
// Date
|
|
18
|
+
selectedDate,
|
|
19
|
+
setSelectedDate,
|
|
20
|
+
dateToggle,
|
|
21
|
+
setDateToggle,
|
|
22
|
+
setIsValidDate,
|
|
23
|
+
// Password
|
|
24
|
+
helperTextPassword,
|
|
25
|
+
password,
|
|
26
|
+
setPassword,
|
|
27
|
+
passwordToggle,
|
|
28
|
+
setPasswordToggle,
|
|
29
|
+
setIsValidPassword,
|
|
30
|
+
// Editing rights
|
|
31
|
+
editingRights,
|
|
32
|
+
setEditingRights
|
|
33
|
+
}) => {
|
|
34
|
+
const { t } = useI18n()
|
|
35
|
+
|
|
36
|
+
const handlePasswordToggle = val => {
|
|
37
|
+
setPasswordToggle(val)
|
|
38
|
+
}
|
|
39
|
+
const handlePassword = value => {
|
|
40
|
+
setPassword(value)
|
|
41
|
+
setIsValidPassword(
|
|
42
|
+
value.trim().length === 0 || value.trim().length >= PASSWORD_MIN_LENGTH
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const handleDateToggle = val => {
|
|
47
|
+
if (!val) {
|
|
48
|
+
setIsValidDate(true)
|
|
49
|
+
}
|
|
50
|
+
const defaultValue = val ? addDays(new Date(), 30) : null
|
|
51
|
+
setSelectedDate(defaultValue, true)
|
|
52
|
+
setDateToggle(val)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<Box display="flex" flexDirection="column" gridGap="1rem">
|
|
57
|
+
<Typography variant="h4" className="u-mb-half u-ml-half">
|
|
58
|
+
{t('ShareRestrictionModal.title')}
|
|
59
|
+
</Typography>
|
|
60
|
+
|
|
61
|
+
<BoxEditingRights
|
|
62
|
+
file={file}
|
|
63
|
+
editingRights={editingRights}
|
|
64
|
+
setEditingRights={setEditingRights}
|
|
65
|
+
/>
|
|
66
|
+
<BoxDate
|
|
67
|
+
onChange={setSelectedDate}
|
|
68
|
+
date={selectedDate}
|
|
69
|
+
onToggle={handleDateToggle}
|
|
70
|
+
toggle={dateToggle}
|
|
71
|
+
/>
|
|
72
|
+
<BoxPassword
|
|
73
|
+
onChange={handlePassword}
|
|
74
|
+
password={password}
|
|
75
|
+
onToggle={handlePasswordToggle}
|
|
76
|
+
toggle={passwordToggle}
|
|
77
|
+
helperText={helperTextPassword}
|
|
78
|
+
inputProps={{ minLength: PASSWORD_MIN_LENGTH }}
|
|
79
|
+
/>
|
|
80
|
+
</Box>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
ShareRestrictionContentModal.propTypes = {
|
|
85
|
+
file: PropTypes.object.isRequired,
|
|
86
|
+
// Date
|
|
87
|
+
selectedDate: PropTypes.object,
|
|
88
|
+
setSelectedDate: PropTypes.func.isRequired,
|
|
89
|
+
dateToggle: PropTypes.bool,
|
|
90
|
+
setDateToggle: PropTypes.func.isRequired,
|
|
91
|
+
setIsValidDate: PropTypes.func.isRequired,
|
|
92
|
+
// Password
|
|
93
|
+
helperTextPassword: PropTypes.string,
|
|
94
|
+
password: PropTypes.string,
|
|
95
|
+
setPassword: PropTypes.func.isRequired,
|
|
96
|
+
passwordToggle: PropTypes.bool.isRequired,
|
|
97
|
+
setPasswordToggle: PropTypes.func.isRequired,
|
|
98
|
+
setIsValidPassword: PropTypes.func.isRequired,
|
|
99
|
+
// Editing rights
|
|
100
|
+
editingRights: PropTypes.oneOf(['readOnly', 'write']),
|
|
101
|
+
setEditingRights: PropTypes.func.isRequired
|
|
102
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { addDays } from 'date-fns'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import React, { useState } from 'react'
|
|
4
|
+
|
|
5
|
+
import { useClient } from 'cozy-client'
|
|
6
|
+
import { getSharingLink as makeSharingLink } from 'cozy-client/dist/models/sharing'
|
|
7
|
+
import { isMobile } from 'cozy-device-helper'
|
|
8
|
+
import Button from 'cozy-ui/transpiled/react/Buttons'
|
|
9
|
+
import { ConfirmDialog } from 'cozy-ui/transpiled/react/CozyDialogs'
|
|
10
|
+
import Icon from 'cozy-ui/transpiled/react/Icon'
|
|
11
|
+
import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
|
|
12
|
+
import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
13
|
+
|
|
14
|
+
import { ShareRestrictionContentModal } from './ShareRestrictionContentModal'
|
|
15
|
+
import {
|
|
16
|
+
copyToClipboard,
|
|
17
|
+
forwardFile,
|
|
18
|
+
updatePermissions,
|
|
19
|
+
makeTTL,
|
|
20
|
+
READ_ONLY_PERMS,
|
|
21
|
+
WRITE_PERMS,
|
|
22
|
+
revokePermissions
|
|
23
|
+
} from './helpers'
|
|
24
|
+
import { checkIsReadOnlyPermissions } from '../../helpers/permissions'
|
|
25
|
+
import { useSharingContext } from '../../hooks/useSharingContext'
|
|
26
|
+
|
|
27
|
+
const PASSWORD_MIN_LENGTH = 4
|
|
28
|
+
|
|
29
|
+
export const ShareRestrictionModal = ({ file, onClose }) => {
|
|
30
|
+
const client = useClient()
|
|
31
|
+
const { t } = useI18n()
|
|
32
|
+
const { showAlert } = useAlert()
|
|
33
|
+
const [password, setPassword] = useState('')
|
|
34
|
+
const [selectedDate, setSelectedDate] = useState(addDays(new Date(), 30))
|
|
35
|
+
const [isValidDate, setIsValidDate] = useState(true)
|
|
36
|
+
const [isValidPassword, setIsValidPassword] = useState(true)
|
|
37
|
+
const [dateToggle, setDateToggle] = useState(true)
|
|
38
|
+
const [passwordToggle, setPasswordToggle] = useState(false)
|
|
39
|
+
const [loading, setLoading] = useState(false)
|
|
40
|
+
|
|
41
|
+
const {
|
|
42
|
+
documentType,
|
|
43
|
+
getDocumentPermissions,
|
|
44
|
+
updateDocumentPermissions,
|
|
45
|
+
shareByLink,
|
|
46
|
+
getSharingLink,
|
|
47
|
+
revokeSharingLink
|
|
48
|
+
} = useSharingContext()
|
|
49
|
+
|
|
50
|
+
const hasSharingLink = getSharingLink(file._id) !== null
|
|
51
|
+
const permissions = getDocumentPermissions(file._id)
|
|
52
|
+
const isReadOnlyPermissions = checkIsReadOnlyPermissions(permissions)
|
|
53
|
+
|
|
54
|
+
const [editingRights, setEditingRights] = useState(
|
|
55
|
+
isReadOnlyPermissions || permissions.length === 0 ? 'readOnly' : 'write'
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
const isDesktopOrMobileWithoutShareAPI =
|
|
59
|
+
(isMobile() && !navigator.share) || !isMobile()
|
|
60
|
+
|
|
61
|
+
const helperTextPassword = !isValidPassword
|
|
62
|
+
? t('ShareRestrictionModal.invalidPasswordMessage', {
|
|
63
|
+
smart_count: PASSWORD_MIN_LENGTH - password.length
|
|
64
|
+
})
|
|
65
|
+
: null
|
|
66
|
+
|
|
67
|
+
const handleDateChange = (date, isValid) => {
|
|
68
|
+
setSelectedDate(date)
|
|
69
|
+
setIsValidDate(isValid)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const handleClick = async () => {
|
|
73
|
+
setLoading(true)
|
|
74
|
+
// If the file is not shared, we create a new sharing link
|
|
75
|
+
if (!hasSharingLink) {
|
|
76
|
+
const verbs = editingRights === 'readOnly' ? READ_ONLY_PERMS : WRITE_PERMS
|
|
77
|
+
await shareByLink(file, { verbs })
|
|
78
|
+
const url = getSharingLink(file._id)
|
|
79
|
+
await copyToClipboard(url, { t, showAlert })
|
|
80
|
+
onClose()
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
await updatePermissions({
|
|
85
|
+
file,
|
|
86
|
+
t,
|
|
87
|
+
editingRights,
|
|
88
|
+
documentType,
|
|
89
|
+
updateDocumentPermissions,
|
|
90
|
+
showAlert
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
const ttl = makeTTL(dateToggle && selectedDate)
|
|
94
|
+
if (isDesktopOrMobileWithoutShareAPI) {
|
|
95
|
+
const url = await makeSharingLink(client, [file._id], {
|
|
96
|
+
ttl,
|
|
97
|
+
password
|
|
98
|
+
})
|
|
99
|
+
await copyToClipboard(url, { t, showAlert })
|
|
100
|
+
} else {
|
|
101
|
+
await forwardFile({
|
|
102
|
+
client,
|
|
103
|
+
file,
|
|
104
|
+
t,
|
|
105
|
+
ttl,
|
|
106
|
+
password,
|
|
107
|
+
showAlert
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
onClose()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const handleRevokeLink = async () => {
|
|
114
|
+
setLoading(true)
|
|
115
|
+
await revokePermissions({
|
|
116
|
+
file,
|
|
117
|
+
t,
|
|
118
|
+
documentType,
|
|
119
|
+
revokeSharingLink,
|
|
120
|
+
showAlert
|
|
121
|
+
})
|
|
122
|
+
onClose()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<ConfirmDialog
|
|
127
|
+
open
|
|
128
|
+
onClose={onClose}
|
|
129
|
+
content={
|
|
130
|
+
<ShareRestrictionContentModal
|
|
131
|
+
file={file}
|
|
132
|
+
// Date
|
|
133
|
+
dateToggle={dateToggle}
|
|
134
|
+
setDateToggle={setDateToggle}
|
|
135
|
+
setSelectedDate={handleDateChange}
|
|
136
|
+
selectedDate={selectedDate}
|
|
137
|
+
setIsValidDate={setIsValidDate}
|
|
138
|
+
// Password
|
|
139
|
+
helperTextPassword={helperTextPassword}
|
|
140
|
+
passwordToggle={passwordToggle}
|
|
141
|
+
setPasswordToggle={setPasswordToggle}
|
|
142
|
+
password={password}
|
|
143
|
+
setPassword={setPassword}
|
|
144
|
+
setIsValidPassword={setIsValidPassword}
|
|
145
|
+
// Editing rights
|
|
146
|
+
editingRights={editingRights}
|
|
147
|
+
setEditingRights={setEditingRights}
|
|
148
|
+
/>
|
|
149
|
+
}
|
|
150
|
+
actions={
|
|
151
|
+
<>
|
|
152
|
+
{hasSharingLink && (
|
|
153
|
+
<Button
|
|
154
|
+
label={t('Share.permissionLink.deactivate')}
|
|
155
|
+
variant="secondary"
|
|
156
|
+
color="error"
|
|
157
|
+
startIcon={<Icon icon="trash" />}
|
|
158
|
+
onClick={handleRevokeLink}
|
|
159
|
+
busy={loading}
|
|
160
|
+
/>
|
|
161
|
+
)}
|
|
162
|
+
<Button
|
|
163
|
+
label={t('ShareRestrictionModal.action.confirm')}
|
|
164
|
+
onClick={handleClick}
|
|
165
|
+
disabled={!isValidDate || !isValidPassword}
|
|
166
|
+
busy={loading}
|
|
167
|
+
/>
|
|
168
|
+
</>
|
|
169
|
+
}
|
|
170
|
+
/>
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
ShareRestrictionModal.propTypes = {
|
|
175
|
+
file: PropTypes.object.isRequired,
|
|
176
|
+
onClose: PropTypes.func
|
|
177
|
+
}
|