cozy-sharing 18.0.0 → 20.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 +57 -0
- package/dist/SharingProvider.js +16 -9
- package/dist/components/Recipient/LinkRecipient.js +20 -4
- 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 +155 -0
- package/dist/components/ShareRestrictionModal/ShareRestrictionContentModal.js +118 -0
- package/dist/components/ShareRestrictionModal/ShareRestrictionModal.js +256 -0
- package/dist/components/ShareRestrictionModal/helpers.js +310 -0
- package/dist/components/ShareRestrictionModal/helpers.spec.js +232 -0
- package/dist/components/WhoHasAccess.js +1 -5
- package/dist/helpers/permissions.js +15 -0
- package/locales/en.json +30 -0
- package/locales/fr.json +31 -0
- package/package.json +11 -7
- package/src/SharingProvider.jsx +10 -5
- package/src/components/Recipient/LinkRecipient.jsx +28 -4
- 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 +126 -0
- package/src/components/ShareRestrictionModal/ShareRestrictionContentModal.jsx +124 -0
- package/src/components/ShareRestrictionModal/ShareRestrictionModal.jsx +186 -0
- package/src/components/ShareRestrictionModal/helpers.js +227 -0
- package/src/components/ShareRestrictionModal/helpers.spec.js +159 -0
- package/src/components/WhoHasAccess.jsx +1 -5
- package/src/helpers/permissions.js +12 -0
- 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
|
@@ -23,10 +23,7 @@ export const EditableSharingModal = ({ document, ...rest }) => {
|
|
|
23
23
|
isOwner,
|
|
24
24
|
revoke,
|
|
25
25
|
revokeSelf,
|
|
26
|
-
|
|
27
|
-
share,
|
|
28
|
-
shareByLink,
|
|
29
|
-
updateDocumentPermissions
|
|
26
|
+
share
|
|
30
27
|
} = useSharingContext()
|
|
31
28
|
|
|
32
29
|
return (
|
|
@@ -39,11 +36,8 @@ export const EditableSharingModal = ({ document, ...rest }) => {
|
|
|
39
36
|
isOwner={isOwner(document.id)}
|
|
40
37
|
link={getSharingLink(document.id)}
|
|
41
38
|
onRevoke={revoke}
|
|
42
|
-
onRevokeLink={revokeSharingLink}
|
|
43
39
|
onRevokeSelf={revokeSelf}
|
|
44
40
|
onShare={share}
|
|
45
|
-
onShareByLink={shareByLink}
|
|
46
|
-
onUpdateShareLinkPermissions={updateDocumentPermissions}
|
|
47
41
|
permissions={getDocumentPermissions(document.id)}
|
|
48
42
|
recipients={getRecipients(document.id)}
|
|
49
43
|
sharing={getSharingForSelf(document.id)}
|
|
@@ -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,126 @@
|
|
|
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
|
+
import { checkIsPermissionHasPassword } from '../../helpers/permissions'
|
|
19
|
+
import { useSharingContext } from '../../hooks/useSharingContext'
|
|
20
|
+
|
|
21
|
+
export const BoxPassword = ({
|
|
22
|
+
file,
|
|
23
|
+
onChange,
|
|
24
|
+
password,
|
|
25
|
+
onToggle,
|
|
26
|
+
toggle,
|
|
27
|
+
helperText,
|
|
28
|
+
inputProps
|
|
29
|
+
}) => {
|
|
30
|
+
const { t } = useI18n()
|
|
31
|
+
const { showAlert } = useAlert()
|
|
32
|
+
const [displayHelper, setDisplayHelper] = useState(false)
|
|
33
|
+
const inputRef = React.useRef()
|
|
34
|
+
const { getDocumentPermissions } = useSharingContext()
|
|
35
|
+
|
|
36
|
+
const permissions = getDocumentPermissions(file._id)
|
|
37
|
+
const hasPassword = checkIsPermissionHasPassword(permissions)
|
|
38
|
+
|
|
39
|
+
const handlePasswordToggle = val => {
|
|
40
|
+
const value = val?.target?.checked ?? val
|
|
41
|
+
onToggle(value)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const handleChange = val => {
|
|
45
|
+
const value = val?.target?.value ?? val
|
|
46
|
+
onChange(value)
|
|
47
|
+
}
|
|
48
|
+
const handleOnFocus = () => {
|
|
49
|
+
setDisplayHelper(false)
|
|
50
|
+
}
|
|
51
|
+
const handleOnBlur = () => {
|
|
52
|
+
handleChange(inputRef.current.value.trim())
|
|
53
|
+
setDisplayHelper(true)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const handleCopy = async () => {
|
|
57
|
+
await copyToClipboard(password, { t, showAlert })
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<Box borderRadius="0.5rem" border="1px solid var(--borderMainColor)">
|
|
62
|
+
<List className="u-p-0">
|
|
63
|
+
<ListItem
|
|
64
|
+
button
|
|
65
|
+
size="large"
|
|
66
|
+
ellipsis={false}
|
|
67
|
+
onClick={() => handlePasswordToggle(!toggle)}
|
|
68
|
+
>
|
|
69
|
+
<ListItemIcon>
|
|
70
|
+
<Icon icon="password" />
|
|
71
|
+
</ListItemIcon>
|
|
72
|
+
<ListItemText primary={t('BoxPassword.text')} />
|
|
73
|
+
<ListItemSecondaryAction>
|
|
74
|
+
<Switch
|
|
75
|
+
edge="end"
|
|
76
|
+
color="primary"
|
|
77
|
+
checked={toggle}
|
|
78
|
+
onChange={handlePasswordToggle}
|
|
79
|
+
/>
|
|
80
|
+
</ListItemSecondaryAction>
|
|
81
|
+
</ListItem>
|
|
82
|
+
</List>
|
|
83
|
+
{toggle && (
|
|
84
|
+
<div className="u-pt-half u-ph-1 u-pb-1">
|
|
85
|
+
<TextField
|
|
86
|
+
inputRef={inputRef}
|
|
87
|
+
autoFocus
|
|
88
|
+
label={t('BoxPassword.label')}
|
|
89
|
+
{...(hasPassword && {
|
|
90
|
+
placeholder: '****',
|
|
91
|
+
InputLabelProps: {
|
|
92
|
+
shrink: true
|
|
93
|
+
}
|
|
94
|
+
})}
|
|
95
|
+
value={password}
|
|
96
|
+
error={displayHelper && !!helperText}
|
|
97
|
+
helperText={displayHelper && helperText}
|
|
98
|
+
onChange={handleChange}
|
|
99
|
+
onFocus={handleOnFocus}
|
|
100
|
+
onBlur={handleOnBlur}
|
|
101
|
+
fullWidth
|
|
102
|
+
type="text"
|
|
103
|
+
variant="outlined"
|
|
104
|
+
InputProps={{
|
|
105
|
+
endAdornment: (
|
|
106
|
+
<IconButton onClick={handleCopy}>
|
|
107
|
+
<Icon icon="copy" />
|
|
108
|
+
</IconButton>
|
|
109
|
+
)
|
|
110
|
+
}}
|
|
111
|
+
inputProps={inputProps}
|
|
112
|
+
/>
|
|
113
|
+
</div>
|
|
114
|
+
)}
|
|
115
|
+
</Box>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
BoxPassword.propTypes = {
|
|
120
|
+
onChange: PropTypes.func.isRequired,
|
|
121
|
+
password: PropTypes.string,
|
|
122
|
+
onToggle: PropTypes.func.isRequired,
|
|
123
|
+
toggle: PropTypes.bool.isRequired,
|
|
124
|
+
helperText: PropTypes.string,
|
|
125
|
+
inputProps: PropTypes.object
|
|
126
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { addDays, isValid } 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
|
+
import { checkIsPermissionHasPassword } from '../../helpers/permissions'
|
|
13
|
+
import { useSharingContext } from '../../hooks/useSharingContext'
|
|
14
|
+
|
|
15
|
+
const PASSWORD_MIN_LENGTH = 4
|
|
16
|
+
|
|
17
|
+
export const ShareRestrictionContentModal = ({
|
|
18
|
+
file,
|
|
19
|
+
// Date
|
|
20
|
+
selectedDate,
|
|
21
|
+
setSelectedDate,
|
|
22
|
+
dateToggle,
|
|
23
|
+
setDateToggle,
|
|
24
|
+
setIsValidDate,
|
|
25
|
+
// Password
|
|
26
|
+
helperTextPassword,
|
|
27
|
+
password,
|
|
28
|
+
setPassword,
|
|
29
|
+
passwordToggle,
|
|
30
|
+
setPasswordToggle,
|
|
31
|
+
setIsValidPassword,
|
|
32
|
+
// Editing rights
|
|
33
|
+
editingRights,
|
|
34
|
+
setEditingRights
|
|
35
|
+
}) => {
|
|
36
|
+
const { t } = useI18n()
|
|
37
|
+
const { getDocumentPermissions } = useSharingContext()
|
|
38
|
+
const permissions = getDocumentPermissions(file._id)
|
|
39
|
+
const hasPassword = checkIsPermissionHasPassword(permissions)
|
|
40
|
+
|
|
41
|
+
const handlePassword = value => {
|
|
42
|
+
setPassword(value)
|
|
43
|
+
setIsValidPassword(
|
|
44
|
+
(hasPassword && value.trim().length === 0) ||
|
|
45
|
+
value.trim().length >= PASSWORD_MIN_LENGTH
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const handlePasswordToggle = val => {
|
|
50
|
+
setPasswordToggle(val)
|
|
51
|
+
setPassword('')
|
|
52
|
+
if (val && !password) {
|
|
53
|
+
setIsValidPassword(hasPassword)
|
|
54
|
+
} else {
|
|
55
|
+
setIsValidPassword(true)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const handleDate = date => {
|
|
60
|
+
setSelectedDate(date)
|
|
61
|
+
setIsValidDate(isValid(date))
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const handleDateToggle = val => {
|
|
65
|
+
setDateToggle(val)
|
|
66
|
+
if (!val) {
|
|
67
|
+
setSelectedDate(null)
|
|
68
|
+
setIsValidDate(true)
|
|
69
|
+
} else {
|
|
70
|
+
const defaultValue = val ? addDays(new Date(), 30) : null
|
|
71
|
+
setSelectedDate(defaultValue)
|
|
72
|
+
setIsValidDate(true)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<Box display="flex" flexDirection="column" gridGap="1rem">
|
|
78
|
+
<Typography variant="h4" className="u-mb-half u-ml-half">
|
|
79
|
+
{t('ShareRestrictionModal.title')}
|
|
80
|
+
</Typography>
|
|
81
|
+
|
|
82
|
+
<BoxEditingRights
|
|
83
|
+
file={file}
|
|
84
|
+
editingRights={editingRights}
|
|
85
|
+
setEditingRights={setEditingRights}
|
|
86
|
+
/>
|
|
87
|
+
<BoxDate
|
|
88
|
+
onChange={handleDate}
|
|
89
|
+
date={selectedDate}
|
|
90
|
+
onToggle={handleDateToggle}
|
|
91
|
+
toggle={dateToggle}
|
|
92
|
+
/>
|
|
93
|
+
<BoxPassword
|
|
94
|
+
file={file}
|
|
95
|
+
onChange={handlePassword}
|
|
96
|
+
password={password}
|
|
97
|
+
onToggle={handlePasswordToggle}
|
|
98
|
+
toggle={passwordToggle}
|
|
99
|
+
helperText={helperTextPassword}
|
|
100
|
+
inputProps={{ minLength: PASSWORD_MIN_LENGTH }}
|
|
101
|
+
/>
|
|
102
|
+
</Box>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
ShareRestrictionContentModal.propTypes = {
|
|
107
|
+
file: PropTypes.object.isRequired,
|
|
108
|
+
// Date
|
|
109
|
+
selectedDate: PropTypes.object,
|
|
110
|
+
setSelectedDate: PropTypes.func.isRequired,
|
|
111
|
+
dateToggle: PropTypes.bool,
|
|
112
|
+
setDateToggle: PropTypes.func.isRequired,
|
|
113
|
+
setIsValidDate: PropTypes.func.isRequired,
|
|
114
|
+
// Password
|
|
115
|
+
helperTextPassword: PropTypes.string,
|
|
116
|
+
password: PropTypes.string,
|
|
117
|
+
setPassword: PropTypes.func.isRequired,
|
|
118
|
+
passwordToggle: PropTypes.bool.isRequired,
|
|
119
|
+
setPasswordToggle: PropTypes.func.isRequired,
|
|
120
|
+
setIsValidPassword: PropTypes.func.isRequired,
|
|
121
|
+
// Editing rights
|
|
122
|
+
editingRights: PropTypes.oneOf(['readOnly', 'write']),
|
|
123
|
+
setEditingRights: PropTypes.func.isRequired
|
|
124
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { addDays } from 'date-fns'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import React, { useState } from 'react'
|
|
4
|
+
|
|
5
|
+
import { generateWebLink, useClient } from 'cozy-client'
|
|
6
|
+
import Button from 'cozy-ui/transpiled/react/Buttons'
|
|
7
|
+
import { ConfirmDialog } from 'cozy-ui/transpiled/react/CozyDialogs'
|
|
8
|
+
import Icon from 'cozy-ui/transpiled/react/Icon'
|
|
9
|
+
import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
|
|
10
|
+
import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
11
|
+
|
|
12
|
+
import { ShareRestrictionContentModal } from './ShareRestrictionContentModal'
|
|
13
|
+
import {
|
|
14
|
+
copyToClipboard,
|
|
15
|
+
updatePermissions,
|
|
16
|
+
makeTTL,
|
|
17
|
+
revokePermissions,
|
|
18
|
+
createPermissions
|
|
19
|
+
} from './helpers'
|
|
20
|
+
import {
|
|
21
|
+
checkIsPermissionHasExpiresDate,
|
|
22
|
+
checkIsPermissionHasPassword,
|
|
23
|
+
checkIsReadOnlyPermissions,
|
|
24
|
+
getPermissionExpiresDate
|
|
25
|
+
} from '../../helpers/permissions'
|
|
26
|
+
import { useSharingContext } from '../../hooks/useSharingContext'
|
|
27
|
+
|
|
28
|
+
const PASSWORD_MIN_LENGTH = 4
|
|
29
|
+
|
|
30
|
+
export const ShareRestrictionModal = ({ file, onClose }) => {
|
|
31
|
+
const client = useClient()
|
|
32
|
+
const { t } = useI18n()
|
|
33
|
+
const { showAlert } = useAlert()
|
|
34
|
+
const [password, setPassword] = useState('')
|
|
35
|
+
const [isValidDate, setIsValidDate] = useState(true)
|
|
36
|
+
const [isValidPassword, setIsValidPassword] = useState(true)
|
|
37
|
+
const [loading, setLoading] = useState(false)
|
|
38
|
+
|
|
39
|
+
const {
|
|
40
|
+
documentType,
|
|
41
|
+
getDocumentPermissions,
|
|
42
|
+
updateDocumentPermissions,
|
|
43
|
+
shareByLink,
|
|
44
|
+
getSharingLink,
|
|
45
|
+
revokeSharingLink
|
|
46
|
+
} = useSharingContext()
|
|
47
|
+
|
|
48
|
+
const hasSharingLink = getSharingLink(file._id) !== null
|
|
49
|
+
const permissions = getDocumentPermissions(file._id)
|
|
50
|
+
const isReadOnlyPermissions = checkIsReadOnlyPermissions(permissions)
|
|
51
|
+
const hasPassword = checkIsPermissionHasPassword(permissions)
|
|
52
|
+
const hasExpiresDate = checkIsPermissionHasExpiresDate(permissions)
|
|
53
|
+
const expiresDate = getPermissionExpiresDate(permissions)
|
|
54
|
+
const defaultDate = expiresDate
|
|
55
|
+
? new Date(expiresDate)
|
|
56
|
+
: addDays(new Date(), 30)
|
|
57
|
+
|
|
58
|
+
const [selectedDate, setSelectedDate] = useState(defaultDate)
|
|
59
|
+
const [dateToggle, setDateToggle] = useState(
|
|
60
|
+
permissions.length > 0 ? hasExpiresDate : true
|
|
61
|
+
)
|
|
62
|
+
const [passwordToggle, setPasswordToggle] = useState(hasPassword)
|
|
63
|
+
const [editingRights, setEditingRights] = useState(
|
|
64
|
+
isReadOnlyPermissions || permissions.length === 0 ? 'readOnly' : 'write'
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
const helperTextPassword = !isValidPassword
|
|
68
|
+
? t('ShareRestrictionModal.invalidPasswordMessage', {
|
|
69
|
+
smart_count: PASSWORD_MIN_LENGTH - password.length
|
|
70
|
+
})
|
|
71
|
+
: null
|
|
72
|
+
|
|
73
|
+
const handleClick = async () => {
|
|
74
|
+
setLoading(true)
|
|
75
|
+
// If the file is not shared, we create a new sharing link
|
|
76
|
+
if (!hasSharingLink) {
|
|
77
|
+
const ttl = makeTTL(dateToggle && selectedDate)
|
|
78
|
+
const { data: perms } = await createPermissions({
|
|
79
|
+
file,
|
|
80
|
+
t,
|
|
81
|
+
ttl,
|
|
82
|
+
password,
|
|
83
|
+
editingRights,
|
|
84
|
+
documentType,
|
|
85
|
+
shareByLink,
|
|
86
|
+
showAlert
|
|
87
|
+
})
|
|
88
|
+
const url = generateWebLink({
|
|
89
|
+
cozyUrl: client.getStackClient().uri,
|
|
90
|
+
searchParams: [['sharecode', perms.attributes.shortcodes.code]],
|
|
91
|
+
pathname: '/public',
|
|
92
|
+
slug: 'drive',
|
|
93
|
+
subDomainType: client.capabilities.flat_subdomains ? 'flat' : 'nested'
|
|
94
|
+
})
|
|
95
|
+
await copyToClipboard(url, { t, showAlert })
|
|
96
|
+
onClose()
|
|
97
|
+
} else {
|
|
98
|
+
const [{ data: perms }] = await updatePermissions({
|
|
99
|
+
file,
|
|
100
|
+
t,
|
|
101
|
+
dateToggle,
|
|
102
|
+
selectedDate,
|
|
103
|
+
passwordToggle,
|
|
104
|
+
password,
|
|
105
|
+
editingRights,
|
|
106
|
+
documentType,
|
|
107
|
+
updateDocumentPermissions,
|
|
108
|
+
showAlert
|
|
109
|
+
})
|
|
110
|
+
const url = generateWebLink({
|
|
111
|
+
cozyUrl: client.getStackClient().uri,
|
|
112
|
+
searchParams: [['sharecode', perms.attributes.shortcodes.code]],
|
|
113
|
+
pathname: '/public',
|
|
114
|
+
slug: 'drive',
|
|
115
|
+
subDomainType: client.capabilities.flat_subdomains ? 'flat' : 'nested'
|
|
116
|
+
})
|
|
117
|
+
await copyToClipboard(url, { t, showAlert })
|
|
118
|
+
onClose()
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const handleRevokeLink = async () => {
|
|
123
|
+
setLoading(true)
|
|
124
|
+
await revokePermissions({
|
|
125
|
+
file,
|
|
126
|
+
t,
|
|
127
|
+
documentType,
|
|
128
|
+
revokeSharingLink,
|
|
129
|
+
showAlert
|
|
130
|
+
})
|
|
131
|
+
onClose()
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<ConfirmDialog
|
|
136
|
+
open
|
|
137
|
+
onClose={onClose}
|
|
138
|
+
content={
|
|
139
|
+
<ShareRestrictionContentModal
|
|
140
|
+
file={file}
|
|
141
|
+
// Date
|
|
142
|
+
dateToggle={dateToggle}
|
|
143
|
+
setDateToggle={setDateToggle}
|
|
144
|
+
setSelectedDate={setSelectedDate}
|
|
145
|
+
selectedDate={selectedDate}
|
|
146
|
+
setIsValidDate={setIsValidDate}
|
|
147
|
+
// Password
|
|
148
|
+
helperTextPassword={helperTextPassword}
|
|
149
|
+
passwordToggle={passwordToggle}
|
|
150
|
+
setPasswordToggle={setPasswordToggle}
|
|
151
|
+
password={password}
|
|
152
|
+
setPassword={setPassword}
|
|
153
|
+
setIsValidPassword={setIsValidPassword}
|
|
154
|
+
// Editing rights
|
|
155
|
+
editingRights={editingRights}
|
|
156
|
+
setEditingRights={setEditingRights}
|
|
157
|
+
/>
|
|
158
|
+
}
|
|
159
|
+
actions={
|
|
160
|
+
<>
|
|
161
|
+
{hasSharingLink && (
|
|
162
|
+
<Button
|
|
163
|
+
label={t('Share.permissionLink.deactivate')}
|
|
164
|
+
variant="secondary"
|
|
165
|
+
color="error"
|
|
166
|
+
startIcon={<Icon icon="trash" />}
|
|
167
|
+
onClick={handleRevokeLink}
|
|
168
|
+
busy={loading}
|
|
169
|
+
/>
|
|
170
|
+
)}
|
|
171
|
+
<Button
|
|
172
|
+
label={t('ShareRestrictionModal.action.confirm')}
|
|
173
|
+
onClick={handleClick}
|
|
174
|
+
disabled={!isValidDate || !isValidPassword}
|
|
175
|
+
busy={loading}
|
|
176
|
+
/>
|
|
177
|
+
</>
|
|
178
|
+
}
|
|
179
|
+
/>
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
ShareRestrictionModal.propTypes = {
|
|
184
|
+
file: PropTypes.object.isRequired,
|
|
185
|
+
onClose: PropTypes.func
|
|
186
|
+
}
|