cozy-sharing 21.2.2 → 21.3.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 (66) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/SharingProvider.js +4 -3
  3. package/dist/assets/illustrations/illustration-shared-drives.svg +1 -0
  4. package/dist/components/Avatar/AvatarList.js +0 -1
  5. package/dist/components/Identity/ExtraIdentity.js +0 -1
  6. package/dist/components/Identity/Identity.js +0 -1
  7. package/dist/components/Identity/MemberIdentity.js +0 -1
  8. package/dist/components/Identity/OwnerIdentity.js +0 -1
  9. package/dist/components/Recipient/GroupRecipient.js +0 -1
  10. package/dist/components/Recipient/GroupRecipientPermissions.js +0 -4
  11. package/dist/components/Recipient/LinkRecipient.js +0 -2
  12. package/dist/components/Recipient/LinkRecipientLite.js +0 -1
  13. package/dist/components/Recipient/MemberRecipient.js +0 -1
  14. package/dist/components/Recipient/MemberRecipientPermissions.js +0 -4
  15. package/dist/components/Recipient/MemberRecipientStatus.js +4 -22
  16. package/dist/components/Recipient/RecipientList.js +15 -0
  17. package/dist/components/Recipient/actions/revokeSharedDriveMember.js +41 -0
  18. package/dist/components/Recipient/actions/setReadOnlySharedPermission.js +54 -0
  19. package/dist/components/Recipient/actions/setReadWriteSharedPermission.js +54 -0
  20. package/dist/components/ShareByEmail.js +32 -19
  21. package/dist/components/ShareDialogCozyToCozy.js +0 -2
  22. package/dist/components/ShareDialogTwoStepsConfirmationContainer.js +0 -2
  23. package/dist/components/ShareModal.js +0 -3
  24. package/dist/components/ShareRestrictionModal/BoxEditingRights.js +0 -4
  25. package/dist/components/SharedDrive/SharedDriveModal.js +350 -0
  26. package/dist/components/SharedDrive/SharedDriveRecipient.js +61 -0
  27. package/dist/components/SharedDrive/SharedDriveRecipientPermissions.js +101 -0
  28. package/dist/components/SharedDrive/SharedDriveRecipientStatus.js +22 -0
  29. package/dist/components/SharedDrive/helpers.js +53 -0
  30. package/dist/components/WhoHasAccess.js +3 -1
  31. package/dist/components/WhoHasAccessLight.js +0 -1
  32. package/dist/index.js +1 -0
  33. package/dist/styles/autosuggest.styl +6 -2
  34. package/dist/styles/recipient.styl +0 -3
  35. package/dist/styles/shareddrive.styl +8 -0
  36. package/dist/stylesheet.css +20 -4
  37. package/locales/en.json +16 -0
  38. package/locales/fr.json +16 -0
  39. package/package.json +2 -2
  40. package/src/SharingProvider.jsx +4 -2
  41. package/src/assets/illustrations/illustration-shared-drives.svg +1 -0
  42. package/src/components/Recipient/GroupRecipient.jsx +1 -5
  43. package/src/components/Recipient/GroupRecipientPermissions.jsx +0 -4
  44. package/src/components/Recipient/LinkRecipient.jsx +1 -5
  45. package/src/components/Recipient/MemberRecipient.jsx +1 -1
  46. package/src/components/Recipient/MemberRecipientPermissions.jsx +0 -4
  47. package/src/components/Recipient/MemberRecipientStatus.jsx +7 -17
  48. package/src/components/Recipient/RecipientList.jsx +18 -0
  49. package/src/components/Recipient/actions/revokeSharedDriveMember.js +41 -0
  50. package/src/components/Recipient/actions/setReadOnlySharedPermission.js +43 -0
  51. package/src/components/Recipient/actions/setReadWriteSharedPermission.js +43 -0
  52. package/src/components/ShareByEmail.jsx +33 -23
  53. package/src/components/ShareDialogCozyToCozy.jsx +0 -2
  54. package/src/components/ShareDialogTwoStepsConfirmationContainer.jsx +0 -2
  55. package/src/components/ShareModal.jsx +0 -3
  56. package/src/components/ShareRestrictionModal/BoxEditingRights.jsx +0 -4
  57. package/src/components/SharedDrive/SharedDriveModal.jsx +196 -0
  58. package/src/components/SharedDrive/SharedDriveRecipient.jsx +62 -0
  59. package/src/components/SharedDrive/SharedDriveRecipientPermissions.jsx +77 -0
  60. package/src/components/SharedDrive/SharedDriveRecipientStatus.jsx +23 -0
  61. package/src/components/SharedDrive/helpers.js +60 -0
  62. package/src/components/WhoHasAccess.jsx +3 -1
  63. package/src/index.jsx +1 -0
  64. package/src/styles/autosuggest.styl +6 -2
  65. package/src/styles/recipient.styl +0 -3
  66. package/src/styles/shareddrive.styl +8 -0
@@ -0,0 +1,196 @@
1
+ import cx from 'classnames'
2
+ import PropTypes from 'prop-types'
3
+ import React, { useState } from 'react'
4
+
5
+ import { useClient } from 'cozy-client'
6
+ import Button from 'cozy-ui/transpiled/react/Buttons'
7
+ import { FixedActionsDialog } from 'cozy-ui/transpiled/react/CozyDialogs'
8
+ import TextField from 'cozy-ui/transpiled/react/TextField'
9
+ import Typography from 'cozy-ui/transpiled/react/Typography'
10
+ import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
11
+ import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
12
+
13
+ import { mergeAndDeduplicateRecipients, formatRecipients } from './helpers'
14
+ import IllustrationSharedDrives from '../../assets/illustrations/illustration-shared-drives.svg'
15
+ import withLocales from '../../hoc/withLocales'
16
+ import { useSharingContext } from '../../hooks/useSharingContext'
17
+ import { Contact } from '../../models'
18
+ import styles from '../../styles/shareddrive.styl'
19
+ import { default as DumbShareByEmail } from '../ShareByEmail'
20
+ import WhoHasAccess from '../WhoHasAccess'
21
+
22
+ export const SharedDriveModal = withLocales(({ onClose }) => {
23
+ const client = useClient()
24
+ const { t } = useI18n()
25
+ const { share } = useSharingContext()
26
+ const { showAlert } = useAlert()
27
+
28
+ const [sharedDriveRecipients, setSharedDriveRecipients] = useState({
29
+ recipients: [],
30
+ readOnlyRecipients: []
31
+ })
32
+ const [sharedDriveName, setSharedDriveName] = useState('')
33
+ const [sharedDriveDescription, setSharedDriveDescription] = useState('')
34
+
35
+ const onShare = params => {
36
+ setSharedDriveRecipients({
37
+ recipients: mergeAndDeduplicateRecipients([
38
+ sharedDriveRecipients.recipients,
39
+ params.recipients
40
+ ]),
41
+ readOnlyRecipients: mergeAndDeduplicateRecipients([
42
+ sharedDriveRecipients.readOnlyRecipients,
43
+ params.readOnlyRecipients
44
+ ])
45
+ })
46
+ }
47
+
48
+ const handleSharedDriveNameChange = event => {
49
+ setSharedDriveName(event.target.value)
50
+ }
51
+
52
+ const handleSharedDriveDescriptionChange = event => {
53
+ setSharedDriveDescription(event.target.value)
54
+ }
55
+
56
+ const onCreate = async () => {
57
+ try {
58
+ const { data: sharedDriveFolder } = await client.create('io.cozy.files', {
59
+ name: sharedDriveName,
60
+ dirId: 'io.cozy.files.shared-drives-dir',
61
+ type: 'directory'
62
+ })
63
+
64
+ await share({
65
+ document: sharedDriveFolder,
66
+ recipients: sharedDriveRecipients.recipients,
67
+ readOnlyRecipients: sharedDriveRecipients.readOnlyRecipients,
68
+ description: sharedDriveDescription,
69
+ sharedDrive: true
70
+ })
71
+
72
+ showAlert({
73
+ message: t('SharedDrive.sharedDriveModal.successNotification'),
74
+ severity: 'success',
75
+ variant: 'filled'
76
+ })
77
+
78
+ onClose()
79
+ } catch (err) {
80
+ showAlert({
81
+ message: t('SharedDrive.sharedDriveModal.errorNotification'),
82
+ severity: 'error',
83
+ variant: 'filled'
84
+ })
85
+ }
86
+ }
87
+
88
+ const onSetType = (index, newType) => {
89
+ const _id = index.split('virtual-shared-drive-sharing-')[1]
90
+
91
+ if (newType === 'two-way') {
92
+ setSharedDriveRecipients(prev => {
93
+ const recipientToMove = prev.readOnlyRecipients.find(r => r._id === _id)
94
+
95
+ return {
96
+ recipients: [...prev.recipients, recipientToMove],
97
+ readOnlyRecipients: prev.readOnlyRecipients.filter(r => r._id !== _id)
98
+ }
99
+ })
100
+ } else {
101
+ setSharedDriveRecipients(prev => {
102
+ const recipientToMove = prev.recipients.find(r => r._id === _id)
103
+
104
+ return {
105
+ recipients: prev.recipients.filter(r => r._id !== _id),
106
+ readOnlyRecipients: [...prev.readOnlyRecipients, recipientToMove]
107
+ }
108
+ })
109
+ }
110
+ }
111
+
112
+ const onRevoke = index => {
113
+ const _id = index.split('virtual-shared-drive-sharing-')[1]
114
+
115
+ setSharedDriveRecipients(prev => {
116
+ return {
117
+ recipients: prev.recipients.filter(r => r._id !== _id),
118
+ readOnlyRecipients: prev.readOnlyRecipients.filter(r => r._id !== _id)
119
+ }
120
+ })
121
+ }
122
+
123
+ const recipients = formatRecipients(sharedDriveRecipients)
124
+
125
+ return (
126
+ <FixedActionsDialog
127
+ open
128
+ onClose={onClose}
129
+ title={t('SharedDrive.sharedDriveModal.title')}
130
+ content={
131
+ <div className="u-flex u-flex-column u-flex-items-center">
132
+ <IllustrationSharedDrives></IllustrationSharedDrives>
133
+ <TextField
134
+ required
135
+ label={t('SharedDrive.sharedDriveModal.nameLabel')}
136
+ variant="outlined"
137
+ size="small"
138
+ className="u-w-100 u-mt-1"
139
+ value={sharedDriveName}
140
+ onChange={handleSharedDriveNameChange}
141
+ />
142
+ <TextField
143
+ required
144
+ label={t('SharedDrive.sharedDriveModal.descriptionLabel')}
145
+ variant="outlined"
146
+ size="large"
147
+ className="u-w-100 u-mt-1"
148
+ value={sharedDriveDescription}
149
+ onChange={handleSharedDriveDescriptionChange}
150
+ />
151
+ <div
152
+ className={cx(
153
+ 'u-mt-1',
154
+ styles['shared-drive-who-has-access-wrapper']
155
+ )}
156
+ >
157
+ <Typography variant="h6" className="u-mb-1-half">
158
+ {t('Share.contacts.whoHasAccess')}
159
+ </Typography>
160
+ <DumbShareByEmail
161
+ createContact={contact => client.create(Contact.doctype, contact)}
162
+ currentRecipients={[]}
163
+ document={document}
164
+ documentType="Files"
165
+ onShare={onShare}
166
+ submitLabel={t('SharedDrive.sharedDriveModal.add')}
167
+ showNotifications={false}
168
+ />
169
+ <WhoHasAccess
170
+ isOwner
171
+ recipients={recipients}
172
+ document={document}
173
+ documentType="Files"
174
+ className="u-w-100"
175
+ onRevoke={onRevoke}
176
+ onSetType={onSetType}
177
+ />
178
+ </div>
179
+ </div>
180
+ }
181
+ actions={
182
+ <>
183
+ <Button
184
+ variant="primary"
185
+ label={t('SharedDrive.sharedDriveModal.create')}
186
+ onClick={onCreate}
187
+ />
188
+ </>
189
+ }
190
+ />
191
+ )
192
+ })
193
+
194
+ SharedDriveModal.propTypes = {
195
+ onClose: PropTypes.func.isRequired
196
+ }
@@ -0,0 +1,62 @@
1
+ import PropTypes from 'prop-types'
2
+ import React from 'react'
3
+
4
+ import Fade from 'cozy-ui/transpiled/react/Fade'
5
+ import ListItem from 'cozy-ui/transpiled/react/ListItem'
6
+ import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
7
+ import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
8
+ import Typography from 'cozy-ui/transpiled/react/Typography'
9
+ import useBreakpoints from 'cozy-ui/transpiled/react/providers/Breakpoints'
10
+ import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
11
+
12
+ import SharedDriveRecipientPermissions from './SharedDriveRecipientPermissions'
13
+ import SharedDriveRecipientStatus from './SharedDriveRecipientStatus'
14
+ import {
15
+ FADE_IN_DURATION,
16
+ DEFAULT_DISPLAY_NAME
17
+ } from '../../helpers/recipients'
18
+ import { getDisplayName } from '../../models'
19
+ import { GroupAvatar } from '../Avatar/GroupAvatar'
20
+ import { MemberAvatar } from '../Avatar/MemberAvatar'
21
+
22
+ const SharedDriveRecipient = props => {
23
+ const { t } = useI18n()
24
+ const { isMobile } = useBreakpoints()
25
+
26
+ const { fadeIn, members, ...rest } = props
27
+
28
+ const defaultDisplayName = t(DEFAULT_DISPLAY_NAME)
29
+ const name = getDisplayName(rest, defaultDisplayName)
30
+
31
+ return (
32
+ <Fade in timeout={fadeIn ? FADE_IN_DURATION : 0}>
33
+ <ListItem gutters={isMobile ? 'default' : 'double'} size="small">
34
+ <ListItemIcon>
35
+ {members ? (
36
+ <GroupAvatar size="small" />
37
+ ) : (
38
+ <MemberAvatar size="small" recipient={props} />
39
+ )}
40
+ </ListItemIcon>
41
+ <ListItemText
42
+ primary={<Typography variant="body1">{name}</Typography>}
43
+ secondary={<SharedDriveRecipientStatus {...props} />}
44
+ />
45
+ <SharedDriveRecipientPermissions
46
+ {...props}
47
+ className="u-flex-shrink-0"
48
+ />
49
+ </ListItem>
50
+ </Fade>
51
+ )
52
+ }
53
+
54
+ SharedDriveRecipient.propTypes = {
55
+ instance: PropTypes.string,
56
+ isOwner: PropTypes.bool,
57
+ status: PropTypes.string,
58
+ recipientConfirmationData: PropTypes.object,
59
+ verifyRecipient: PropTypes.func
60
+ }
61
+
62
+ export default SharedDriveRecipient
@@ -0,0 +1,77 @@
1
+ import React, { useState, useRef } from 'react'
2
+
3
+ import ActionsMenu from 'cozy-ui/transpiled/react/ActionsMenu'
4
+ import {
5
+ makeActions,
6
+ divider
7
+ } from 'cozy-ui/transpiled/react/ActionsMenu/Actions'
8
+ import DropdownButton from 'cozy-ui/transpiled/react/DropdownButton'
9
+ import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
10
+
11
+ import { revokeSharedDriveMember } from '../Recipient/actions/revokeSharedDriveMember'
12
+ import { setReadOnlySharedPermission } from '../Recipient/actions/setReadOnlySharedPermission'
13
+ import { setReadWriteSharedPermission } from '../Recipient/actions/setReadWriteSharedPermission'
14
+
15
+ const ShareDriveRecipientPermissions = ({
16
+ index,
17
+ onSetType,
18
+ type,
19
+ onRevoke
20
+ }) => {
21
+ const { t } = useI18n()
22
+ const buttonRef = useRef()
23
+ const [isMenuDisplayed, setMenuDisplayed] = useState(false)
24
+
25
+ const toggleMenu = () => setMenuDisplayed(!isMenuDisplayed)
26
+ const hideMenu = () => setMenuDisplayed(false)
27
+
28
+ const handleRevocation = async () => {
29
+ onRevoke(index)
30
+ hideMenu()
31
+ }
32
+
33
+ const setType = async newType => {
34
+ if (newType !== type) {
35
+ onSetType(index, newType)
36
+ }
37
+ hideMenu()
38
+ }
39
+
40
+ const actions = makeActions(
41
+ [
42
+ setReadOnlySharedPermission,
43
+ setReadWriteSharedPermission,
44
+ divider,
45
+ revokeSharedDriveMember
46
+ ],
47
+ {
48
+ t,
49
+ type,
50
+ setType,
51
+ handleRevocation
52
+ }
53
+ )
54
+
55
+ return (
56
+ <div>
57
+ <DropdownButton
58
+ ref={buttonRef}
59
+ aria-controls="simple-menu"
60
+ aria-haspopup="true"
61
+ onClick={toggleMenu}
62
+ textVariant="body2"
63
+ >
64
+ {t(`Share.type.${type}`).toLowerCase()}
65
+ </DropdownButton>
66
+ <ActionsMenu
67
+ ref={buttonRef}
68
+ open={isMenuDisplayed}
69
+ actions={actions}
70
+ autoClose
71
+ onClose={hideMenu}
72
+ />
73
+ </div>
74
+ )
75
+ }
76
+
77
+ export default ShareDriveRecipientPermissions
@@ -0,0 +1,23 @@
1
+ import React from 'react'
2
+
3
+ import Typography from 'cozy-ui/transpiled/react/Typography'
4
+ import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
5
+
6
+ const SharedDriveRecipientStatus = ({ members, email }) => {
7
+ const { t } = useI18n()
8
+
9
+ const text = members
10
+ ? t('GroupRecipient.secondary', {
11
+ memberCount: members.length,
12
+ smart_count: members.length
13
+ })
14
+ : email
15
+
16
+ return (
17
+ <Typography variant="caption" color="textSecondary">
18
+ {text}
19
+ </Typography>
20
+ )
21
+ }
22
+
23
+ export default SharedDriveRecipientStatus
@@ -0,0 +1,60 @@
1
+ import { models } from 'cozy-client'
2
+
3
+ const ContactModel = models.contact
4
+
5
+ export const mergeAndDeduplicateRecipients = arrays => {
6
+ const combinedArray = arrays.flat()
7
+
8
+ const seenIds = new Set()
9
+
10
+ const uniqueArray = combinedArray.filter(item => {
11
+ if (!seenIds.has(item.id)) {
12
+ seenIds.add(item.id)
13
+ return true
14
+ }
15
+ return false
16
+ })
17
+
18
+ return uniqueArray
19
+ }
20
+
21
+ export const formatRecipients = sharedDriveRecipients => {
22
+ const recipients = []
23
+
24
+ sharedDriveRecipients.recipients.forEach(r => {
25
+ recipients.push({
26
+ index: `virtual-shared-drive-sharing-${r._id}`,
27
+ email: ContactModel.getPrimaryEmail(r),
28
+ public_name: r.displayName || r.name,
29
+ memberIndex: r._id,
30
+ type: 'two-way',
31
+ members: r.members
32
+ })
33
+ })
34
+
35
+ sharedDriveRecipients.readOnlyRecipients.forEach(r => {
36
+ recipients.push({
37
+ index: `virtual-shared-drive-sharing-${r._id}`,
38
+ email: ContactModel.getPrimaryEmail(r),
39
+ public_name: r.displayName || r.name,
40
+ memberIndex: r._id,
41
+ type: 'one-way',
42
+ members: r.members
43
+ })
44
+ })
45
+
46
+ // We need to sort recipients by public_name
47
+ // to avoid reording them every time we change
48
+ // them from read only to read write.
49
+ recipients.sort((a, b) => {
50
+ if (a.public_name < b.public_name) {
51
+ return -1
52
+ }
53
+ if (a.public_name > b.public_name) {
54
+ return 1
55
+ }
56
+ return 0
57
+ })
58
+
59
+ return recipients
60
+ }
@@ -32,9 +32,10 @@ const WhoHasAccess = ({
32
32
  recipientsToBeConfirmed = [],
33
33
  document,
34
34
  documentType,
35
- onRevoke,
36
35
  className,
36
+ onRevoke,
37
37
  onRevokeSelf,
38
+ onSetType,
38
39
  verifyRecipient,
39
40
  link,
40
41
  permissions
@@ -70,6 +71,7 @@ const WhoHasAccess = ({
70
71
  documentType={documentType}
71
72
  onRevoke={onRevoke}
72
73
  onRevokeSelf={onRevokeSelf}
74
+ onSetType={onSetType}
73
75
  verifyRecipient={verifyRecipient}
74
76
  />
75
77
  </List>
package/src/index.jsx CHANGED
@@ -19,6 +19,7 @@ export { default as LinkRecipientLite } from './components/Recipient/LinkRecipie
19
19
  export { default as OwnerRecipientDefaultLite } from './components/Recipient/OwnerRecipientDefaultLite'
20
20
  export { SharedRecipientsList } from './SharedRecipientsList'
21
21
  export { ShareButton } from './ShareButton'
22
+ export { SharedDriveModal } from './components/SharedDrive/SharedDriveModal'
22
23
  export { ShareModal } from './components/ShareModal/ShareModal'
23
24
  export { RefreshableSharings } from './RefreshableSharings'
24
25
  export { CozyPassFingerprintDialogContent } from './components/CozyPassFingerprintDialogContent'
@@ -55,9 +55,13 @@
55
55
  border none
56
56
  &:hover
57
57
  &:focus
58
+ &:focus-visible
58
59
  border none
60
+ outline none
59
61
  &:first-child
60
- padding .25rem .5rem
61
-
62
+ padding .25rem .5rem
63
+ &::placeholder
64
+ color var(--secondaryTextColor)
65
+ font-size 1rem
62
66
  .recipientChip
63
67
  margin .25rem .5rem .25rem 0
@@ -25,9 +25,6 @@
25
25
  overflow hidden
26
26
  text-overflow ellipsis
27
27
 
28
- .recipient-status-icon
29
- margin-right: .25rem
30
-
31
28
  .recipient-user
32
29
  .recipient-details
33
30
  text-overflow ellipsis
@@ -0,0 +1,8 @@
1
+ @require 'components/forms.styl'
2
+
3
+ .shared-drive-who-has-access-wrapper
4
+ border: 1px solid var(--borderMainColor)
5
+ border-radius: 0.5rem
6
+ width: 100%
7
+ padding: 1rem
8
+ box-sizing: border-box