cozy-sharing 5.0.0 → 6.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.
Files changed (115) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/dist/SharedRecipients.js +1 -1
  3. package/dist/SharedStatus.js +1 -1
  4. package/dist/SharingBanner/hooks/useSharingInfos.js +1 -1
  5. package/dist/SharingDetailsModal.js +1 -1
  6. package/dist/SharingOwnerAvatar.js +1 -1
  7. package/dist/SharingProvider.js +21 -8
  8. package/dist/components/ContactSuggestion.js +15 -27
  9. package/dist/components/EditLinkPermissionDialog.js +72 -0
  10. package/dist/components/EditLinkPermissionDialog.spec.js +48 -0
  11. package/dist/components/EditableSharingModal.js +15 -62
  12. package/dist/components/{AvatarPlusX.js → Recipient/AvatarPlusX.js} +1 -1
  13. package/dist/components/{Identity.js → Recipient/Identity.js} +17 -16
  14. package/dist/components/Recipient/LinkRecipient.js +72 -0
  15. package/dist/components/Recipient/LinkRecipientPermissions.js +159 -0
  16. package/dist/components/Recipient/OwnerIdentity.js +44 -0
  17. package/dist/components/Recipient/Recipient.js +72 -0
  18. package/dist/components/{Recipient.spec.js → Recipient/Recipient.spec.js} +4 -45
  19. package/dist/components/Recipient/RecipientAvatar.js +33 -0
  20. package/dist/components/Recipient/RecipientConfirm.js +63 -0
  21. package/dist/components/Recipient/RecipientPermissions.js +126 -0
  22. package/dist/components/Recipient/RecipientPlusX.js +45 -0
  23. package/dist/components/Recipient/RecipientStatus.js +64 -0
  24. package/dist/components/Recipient/RecipientWithoutStatus.js +45 -0
  25. package/dist/components/Recipient/RecipientsAvatars.js +100 -0
  26. package/dist/components/{RecipientsAvatars.spec.js → Recipient/RecipientsAvatars.spec.js} +43 -2
  27. package/dist/components/{recipient.styl → Recipient/recipient.styl} +6 -2
  28. package/dist/components/ShareAutosuggest.js +176 -252
  29. package/dist/components/ShareAutosuggest.spec.js +3 -2
  30. package/dist/components/ShareByEmail.js +142 -193
  31. package/dist/components/ShareByEmail.spec.js +42 -23
  32. package/dist/components/ShareByLink.js +251 -357
  33. package/dist/components/ShareDialogCozyToCozy.js +16 -4
  34. package/dist/components/ShareDialogCozyToCozy.spec.js +12 -1
  35. package/dist/components/ShareDialogOnlyByLink.js +23 -10
  36. package/dist/components/ShareDialogTwoStepsConfirmationContainer.js +7 -7
  37. package/dist/components/ShareModal.js +5 -1
  38. package/dist/components/ShareRecipientsInput.js +44 -128
  39. package/dist/components/WhoHasAccess.js +43 -5
  40. package/dist/components/WhoHasAccessLight.js +3 -19
  41. package/dist/components/autosuggest.styl +21 -36
  42. package/dist/helpers/documentType.js +6 -0
  43. package/dist/helpers/hooks.js +8 -1
  44. package/dist/helpers/link.js +5 -0
  45. package/dist/helpers/permissions.js +8 -0
  46. package/dist/helpers/recipients.js +24 -0
  47. package/dist/helpers/sharings.js +72 -53
  48. package/dist/helpers/synchronousJobQueue.js +67 -0
  49. package/dist/helpers/synchronousJobQueue.spec.js +97 -0
  50. package/dist/index.js +2 -1
  51. package/dist/queries/queries.js +72 -0
  52. package/dist/share.styl +0 -15
  53. package/dist/stylesheet.css +67 -92
  54. package/locales/en.json +29 -41
  55. package/locales/fr.json +26 -39
  56. package/package.json +10 -10
  57. package/src/SharedRecipients.jsx +1 -1
  58. package/src/SharedStatus.jsx +1 -1
  59. package/src/SharingBanner/hooks/useSharingInfos.jsx +1 -1
  60. package/src/SharingDetailsModal.jsx +1 -1
  61. package/src/SharingOwnerAvatar.jsx +1 -1
  62. package/src/SharingProvider.jsx +17 -4
  63. package/src/components/ContactSuggestion.jsx +25 -10
  64. package/src/components/EditLinkPermissionDialog.jsx +89 -0
  65. package/src/components/EditLinkPermissionDialog.spec.jsx +52 -0
  66. package/src/components/EditableSharingModal.jsx +58 -100
  67. package/src/components/{AvatarPlusX.jsx → Recipient/AvatarPlusX.jsx} +1 -1
  68. package/src/components/{Identity.jsx → Recipient/Identity.jsx} +0 -0
  69. package/src/components/Recipient/LinkRecipient.jsx +59 -0
  70. package/src/components/Recipient/LinkRecipientPermissions.jsx +157 -0
  71. package/src/components/Recipient/OwnerIdentity.jsx +20 -0
  72. package/src/components/Recipient/Recipient.jsx +79 -0
  73. package/src/components/{Recipient.spec.jsx → Recipient/Recipient.spec.jsx} +4 -42
  74. package/src/components/Recipient/RecipientAvatar.jsx +38 -0
  75. package/src/components/Recipient/RecipientConfirm.jsx +37 -0
  76. package/src/components/Recipient/RecipientPermissions.jsx +114 -0
  77. package/src/components/Recipient/RecipientPlusX.jsx +28 -0
  78. package/src/components/Recipient/RecipientStatus.jsx +48 -0
  79. package/src/components/Recipient/RecipientWithoutStatus.jsx +21 -0
  80. package/src/components/Recipient/RecipientsAvatars.jsx +108 -0
  81. package/src/components/{RecipientsAvatars.spec.jsx → Recipient/RecipientsAvatars.spec.jsx} +43 -2
  82. package/src/components/{recipient.styl → Recipient/recipient.styl} +6 -2
  83. package/src/components/ShareAutosuggest.jsx +136 -127
  84. package/src/components/ShareAutosuggest.spec.jsx +12 -8
  85. package/src/components/ShareByEmail.jsx +65 -90
  86. package/src/components/ShareByEmail.spec.jsx +39 -11
  87. package/src/components/ShareByLink.jsx +146 -238
  88. package/src/components/ShareDialogCozyToCozy.jsx +14 -2
  89. package/src/components/ShareDialogCozyToCozy.spec.jsx +8 -1
  90. package/src/components/ShareDialogOnlyByLink.jsx +30 -13
  91. package/src/components/ShareDialogTwoStepsConfirmationContainer.jsx +5 -5
  92. package/src/components/ShareModal.jsx +5 -1
  93. package/src/components/ShareRecipientsInput.jsx +30 -44
  94. package/src/components/WhoHasAccess.jsx +85 -27
  95. package/src/components/WhoHasAccessLight.jsx +2 -1
  96. package/src/components/__snapshots__/ContactSuggestion.spec.jsx.snap +915 -132
  97. package/src/components/__snapshots__/ShareRecipientsInput.spec.jsx.snap +86 -102
  98. package/src/components/autosuggest.styl +21 -36
  99. package/src/helpers/documentType.js +6 -0
  100. package/src/helpers/hooks.js +11 -1
  101. package/src/helpers/link.js +4 -0
  102. package/src/helpers/permissions.js +15 -0
  103. package/src/helpers/recipients.js +21 -0
  104. package/src/helpers/sharings.js +15 -12
  105. package/src/helpers/synchronousJobQueue.js +22 -0
  106. package/src/helpers/synchronousJobQueue.spec.js +75 -0
  107. package/src/index.jsx +1 -0
  108. package/src/queries/queries.js +75 -0
  109. package/src/share.styl +0 -15
  110. package/dist/SharingBanner/helpers/queries.js +0 -12
  111. package/dist/components/ContactsAndGroupsDataLoader.js +0 -50
  112. package/dist/components/Recipient.js +0 -450
  113. package/src/SharingBanner/helpers/queries.js +0 -14
  114. package/src/components/ContactsAndGroupsDataLoader.jsx +0 -30
  115. package/src/components/Recipient.jsx +0 -415
@@ -0,0 +1,157 @@
1
+ import React, { useState, useRef } from 'react'
2
+
3
+ import { useI18n } from 'cozy-ui/transpiled/react/I18n'
4
+ import DropdownButton from 'cozy-ui/transpiled/react/DropdownButton'
5
+ import ActionMenu, {
6
+ ActionMenuItem,
7
+ ActionMenuRadio
8
+ } from 'cozy-ui/transpiled/react/ActionMenu'
9
+ import Typography from 'cozy-ui/transpiled/react/Typography'
10
+ import Icon from 'cozy-ui/transpiled/react/Icon'
11
+ import TrashIcon from 'cozy-ui/transpiled/react/Icons/Trash'
12
+ import Alerter from 'cozy-ui/transpiled/react/Alerter'
13
+ import Spinner from 'cozy-ui/transpiled/react/Spinner'
14
+
15
+ import { checkIsReadOnlyPermissions } from '../../helpers/permissions'
16
+ import { isOnlyReadOnlyLinkAllowed } from '../../helpers/link'
17
+
18
+ import logger from '../../logger'
19
+
20
+ const LinkRecipientPermissions = ({
21
+ className,
22
+ document,
23
+ documentType,
24
+ permissions,
25
+ onChangePermissions,
26
+ onDisable
27
+ }) => {
28
+ const { t } = useI18n()
29
+ const buttonRef = useRef()
30
+ const [menuIsOpen, setMenuIsOpen] = useState(false)
31
+ const [loading, setLoading] = useState(false)
32
+
33
+ const isReadOnlyPermissions = checkIsReadOnlyPermissions(permissions)
34
+
35
+ const toggleMenu = () => {
36
+ setMenuIsOpen(!menuIsOpen)
37
+ }
38
+
39
+ const deleteShareLink = async () => {
40
+ try {
41
+ setLoading(true)
42
+ await onDisable(document)
43
+ } catch (e) {
44
+ Alerter.error(t(`${documentType}.share.error.revoke`))
45
+ logger.log(e)
46
+ } finally {
47
+ setLoading(false)
48
+ }
49
+ }
50
+
51
+ const updateLinkPermissions = ({ isReadOnly }) => {
52
+ const verbs = isReadOnly ? ['GET'] : ['GET', 'POST', 'PUT', 'PATCH']
53
+ try {
54
+ onChangePermissions(document, verbs)
55
+ } catch (err) {
56
+ Alerter.error(t(`${documentType}.share.shareByLink.permserror`))
57
+ logger.log(err)
58
+ }
59
+ }
60
+ return (
61
+ <div className={className}>
62
+ {loading && <Spinner />}
63
+ {!loading && (
64
+ <>
65
+ <DropdownButton
66
+ onClick={toggleMenu}
67
+ ref={buttonRef}
68
+ textVariant="body2"
69
+ >
70
+ {t(
71
+ `Share.type.${isReadOnlyPermissions ? 'one-way' : 'two-way'}`
72
+ ).toLowerCase()}
73
+ </DropdownButton>
74
+ {menuIsOpen && (
75
+ <ActionMenu
76
+ onClose={toggleMenu}
77
+ popperOptions={{
78
+ placement: 'bottom-end',
79
+ strategy: 'fixed'
80
+ }}
81
+ anchorElRef={buttonRef}
82
+ >
83
+ <ActionMenuItem
84
+ left={
85
+ <ActionMenuRadio
86
+ name="permissionLinkMenu"
87
+ value="true"
88
+ checked={isReadOnlyPermissions}
89
+ />
90
+ }
91
+ onClick={() => {
92
+ toggleMenu()
93
+ updateLinkPermissions({ isReadOnly: true })
94
+ }}
95
+ >
96
+ <>
97
+ {document?.type === 'directory'
98
+ ? t(`Share.permissionLink.seeFolder`)
99
+ : t(`Share.permissionLink.seeFile`)}
100
+ <Typography
101
+ className="u-mt-half"
102
+ variant="caption"
103
+ color="textSecondary"
104
+ >
105
+ {t('Share.permissionLink.readDescription')}
106
+ </Typography>
107
+ </>
108
+ </ActionMenuItem>
109
+ {!isOnlyReadOnlyLinkAllowed({ documentType }) && (
110
+ <ActionMenuItem
111
+ left={
112
+ <ActionMenuRadio
113
+ name="permissionLinkMenu"
114
+ value="false"
115
+ checked={!isReadOnlyPermissions}
116
+ />
117
+ }
118
+ onClick={() => {
119
+ toggleMenu()
120
+ updateLinkPermissions({ isReadOnly: false })
121
+ }}
122
+ >
123
+ <>
124
+ {document?.type === 'directory'
125
+ ? t(`Share.permissionLink.modifyFolder`)
126
+ : t(`Share.permissionLink.modifyFile`)}
127
+ <Typography
128
+ className="u-mt-half"
129
+ variant="caption"
130
+ color="textSecondary"
131
+ >
132
+ {t('Share.permissionLink.writeDescription')}
133
+ </Typography>
134
+ </>
135
+ </ActionMenuItem>
136
+ )}
137
+ <hr />
138
+ <ActionMenuItem
139
+ left={<Icon icon={TrashIcon} color="var(--errorColor)" />}
140
+ onClick={() => {
141
+ toggleMenu()
142
+ deleteShareLink()
143
+ }}
144
+ >
145
+ <Typography className="u-error" variant="body1">
146
+ {t('Share.permissionLink.deactivate')}
147
+ </Typography>
148
+ </ActionMenuItem>
149
+ </ActionMenu>
150
+ )}
151
+ </>
152
+ )}
153
+ </div>
154
+ )
155
+ }
156
+
157
+ export default LinkRecipientPermissions
@@ -0,0 +1,20 @@
1
+ import React from 'react'
2
+
3
+ import Avatar from 'cozy-ui/transpiled/react/Avatar'
4
+
5
+ import styles from './recipient.styl'
6
+ import { getDisplayName, getInitials } from '../../models'
7
+ import Identity from './Identity'
8
+
9
+ const OwnerIdentity = ({ url, size, ...rest }) => (
10
+ <div className={styles['avatar']}>
11
+ <Avatar
12
+ text={getInitials(rest)}
13
+ size={size}
14
+ textId={getDisplayName(rest)}
15
+ />
16
+ <Identity name={getDisplayName(rest)} details={url} />
17
+ </div>
18
+ )
19
+
20
+ export default OwnerIdentity
@@ -0,0 +1,79 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+
4
+ import { useClient } from 'cozy-client'
5
+ import { useI18n } from 'cozy-ui/transpiled/react/I18n'
6
+ import ListItem from 'cozy-ui/transpiled/react/MuiCozyTheme/ListItem'
7
+ import ListItemIcon from 'cozy-ui/transpiled/react/MuiCozyTheme/ListItemIcon'
8
+ import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
9
+ import Typography from 'cozy-ui/transpiled/react/Typography'
10
+ import Fade from 'cozy-ui/transpiled/react/Fade'
11
+
12
+ import { FADE_IN_DURATION } from '../../helpers/recipients'
13
+ import { getDisplayName } from '../../models'
14
+ import RecipientConfirm from './RecipientConfirm'
15
+ import RecipientPermissions from './RecipientPermissions'
16
+ import RecipientAvatar from './RecipientAvatar'
17
+ import RecipientStatus from './RecipientStatus'
18
+
19
+ const DEFAULT_DISPLAY_NAME = 'Share.contacts.defaultDisplayName'
20
+
21
+ const Recipient = props => {
22
+ const { t } = useI18n()
23
+ const client = useClient()
24
+
25
+ const {
26
+ instance,
27
+ isOwner,
28
+ status,
29
+ recipientConfirmationData,
30
+ verifyRecipient,
31
+ fadeIn,
32
+ ...rest
33
+ } = props
34
+
35
+ const isMe =
36
+ (isOwner && status === 'owner') || instance === client.options.uri
37
+ const defaultDisplayName = t(DEFAULT_DISPLAY_NAME)
38
+ const name = getDisplayName(rest, defaultDisplayName)
39
+
40
+ const RightPart = recipientConfirmationData ? (
41
+ <RecipientConfirm
42
+ recipientConfirmationData={recipientConfirmationData}
43
+ verifyRecipient={verifyRecipient}
44
+ ></RecipientConfirm>
45
+ ) : (
46
+ <RecipientPermissions {...props} className="u-flex-shrink-0" />
47
+ )
48
+
49
+ return (
50
+ <Fade in timeout={fadeIn ? FADE_IN_DURATION : 0}>
51
+ <ListItem disableGutters>
52
+ <ListItemIcon>
53
+ <RecipientAvatar size="small" recipient={props} />
54
+ </ListItemIcon>
55
+ <ListItemText
56
+ primary={
57
+ <Typography className="u-ellipsis" variant="body1">
58
+ {isMe ? t('Share.recipients.you') : name}
59
+ </Typography>
60
+ }
61
+ secondary={
62
+ <RecipientStatus status={status} isMe={isMe} instance={instance} />
63
+ }
64
+ />
65
+ {RightPart}
66
+ </ListItem>
67
+ </Fade>
68
+ )
69
+ }
70
+
71
+ Recipient.propTypes = {
72
+ instance: PropTypes.string,
73
+ isOwner: PropTypes.bool,
74
+ status: PropTypes.string,
75
+ recipientConfirmationData: PropTypes.object,
76
+ verifyRecipient: PropTypes.func
77
+ }
78
+
79
+ export default Recipient
@@ -1,6 +1,6 @@
1
1
  import React from 'react'
2
- import Recipient, { excludeMeAsOwnerFromRecipients } from './Recipient'
3
- import AppLike from '../../test/AppLike'
2
+ import Recipient from './Recipient'
3
+ import AppLike from '../../../test/AppLike'
4
4
  import { createMockClient } from 'cozy-client'
5
5
  import { render, fireEvent } from '@testing-library/react'
6
6
 
@@ -70,7 +70,7 @@ describe('Recipient component', () => {
70
70
  onRevokeSelf
71
71
  })
72
72
 
73
- fireEvent.click(getByText('Can Change'))
73
+ fireEvent.click(getByText('can change'))
74
74
  fireEvent.click(getByText('Remove me from sharing'))
75
75
  expect(onRevoke).not.toBeCalled()
76
76
  expect(onRevokeSelf).toBeCalled()
@@ -90,7 +90,7 @@ describe('Recipient component', () => {
90
90
  onRevokeSelf
91
91
  })
92
92
 
93
- fireEvent.click(getByText('Can Change'))
93
+ fireEvent.click(getByText('can change'))
94
94
  fireEvent.click(getByText('Remove from sharing'))
95
95
  expect(onRevoke).toBeCalled()
96
96
  expect(onRevokeSelf).not.toBeCalled()
@@ -156,41 +156,3 @@ describe('Recipient component', () => {
156
156
  })
157
157
  })
158
158
  })
159
-
160
- describe('excludeMeAsOwnerFromRecipients', () => {
161
- test('excludeMeAsOwnerFromRecipients behavior', () => {
162
- const recipients = [
163
- {
164
- status: 'owner',
165
- instance: 'http://foo1.cozy.bar'
166
- },
167
- { status: 'pending', instance: 'http://foo2.cozy.bar' },
168
- {
169
- status: 'pending',
170
- instance: 'http://foo3.cozy.bar'
171
- }
172
- ]
173
- const client = { options: { uri: 'http://foo2.cozy.bar' } }
174
- expect(
175
- excludeMeAsOwnerFromRecipients({ recipients, isOwner: true, client })
176
- ).toEqual([
177
- { status: 'pending', instance: 'http://foo2.cozy.bar' },
178
- {
179
- status: 'pending',
180
- instance: 'http://foo3.cozy.bar'
181
- }
182
- ])
183
- expect(
184
- excludeMeAsOwnerFromRecipients({ recipients, isOwner: false, client })
185
- ).toEqual([
186
- {
187
- status: 'owner',
188
- instance: 'http://foo1.cozy.bar'
189
- },
190
- {
191
- status: 'pending',
192
- instance: 'http://foo3.cozy.bar'
193
- }
194
- ])
195
- })
196
- })
@@ -0,0 +1,38 @@
1
+ import React from 'react'
2
+ import { useClient } from 'cozy-client'
3
+
4
+ import Avatar from 'cozy-ui/transpiled/react/Avatar'
5
+
6
+ import { getDisplayName, getInitials } from '../../models'
7
+
8
+ const RecipientAvatar = ({ recipient, ...rest }) => {
9
+ const client = useClient()
10
+
11
+ /**
12
+ * avatarPath is always the same for a recipient, but image
13
+ * can be different since the stack generate it on the fly.
14
+ * It can be "gray" during the first load depending of the
15
+ * status' sharing, but can become active (from the realtime)
16
+ * so we need a way to "refresh" the image. Passing the
17
+ * status in the url force the refresh of the image when the
18
+ * status changes
19
+ */
20
+ const image =
21
+ recipient.avatarPath && recipient.status
22
+ ? `${client.options.uri}${recipient.avatarPath}?v=${recipient.status}`
23
+ : null
24
+
25
+ return (
26
+ <Avatar
27
+ image={image}
28
+ text={getInitials(recipient)}
29
+ textId={getDisplayName(recipient)}
30
+ disabled={
31
+ recipient.status === 'pending' || recipient.status === 'mail-not-send'
32
+ }
33
+ {...rest}
34
+ />
35
+ )
36
+ }
37
+
38
+ export default RecipientAvatar
@@ -0,0 +1,37 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+
4
+ import { useI18n } from 'cozy-ui/transpiled/react/I18n'
5
+ import Button from 'cozy-ui/transpiled/react/Button'
6
+
7
+ import modalStyles from '../../share.styl'
8
+
9
+ /**
10
+ * Displays the confirmation interface that allows to confirm or reject a recipient
11
+ */
12
+ const RecipientConfirm = ({ recipientConfirmationData, verifyRecipient }) => {
13
+ const { t } = useI18n()
14
+
15
+ const verify = () => {
16
+ verifyRecipient(recipientConfirmationData)
17
+ }
18
+
19
+ return (
20
+ <>
21
+ <Button
22
+ style={{ position: 'initial' }} // fix z-index bug on iOS when under a BottomDrawer due to relative position
23
+ theme="text"
24
+ className={modalStyles['aligned-dropdown-button']}
25
+ onClick={verify}
26
+ label={t(`Share.twoStepsConfirmation.verify`)}
27
+ />
28
+ </>
29
+ )
30
+ }
31
+
32
+ RecipientConfirm.propTypes = {
33
+ recipientConfirmationData: PropTypes.object.isRequired,
34
+ verifyRecipient: PropTypes.func.isRequired
35
+ }
36
+
37
+ export default RecipientConfirm
@@ -0,0 +1,114 @@
1
+ import React, { useState, useRef, useCallback } from 'react'
2
+ import { useClient } from 'cozy-client'
3
+
4
+ import { useI18n } from 'cozy-ui/transpiled/react/I18n'
5
+ import Spinner from 'cozy-ui/transpiled/react/Spinner'
6
+ import Typography from 'cozy-ui/transpiled/react/Typography'
7
+ import DropdownButton from 'cozy-ui/transpiled/react/DropdownButton'
8
+ import ActionMenu, { ActionMenuItem } from 'cozy-ui/transpiled/react/ActionMenu'
9
+ import Icon from 'cozy-ui/transpiled/react/Icon'
10
+ import RenameIcon from 'cozy-ui/transpiled/react/Icons/Rename'
11
+ import TrashIcon from 'cozy-ui/transpiled/react/Icons/Trash'
12
+ import EyeIcon from 'cozy-ui/transpiled/react/Icons/Eye'
13
+
14
+ const RecipientPermissions = ({
15
+ isOwner,
16
+ status,
17
+ instance,
18
+ type,
19
+ document,
20
+ documentType,
21
+ className,
22
+ onRevoke,
23
+ onRevokeSelf,
24
+ sharingId,
25
+ index
26
+ }) => {
27
+ const { t } = useI18n()
28
+ const buttonRef = useRef()
29
+ const client = useClient()
30
+ const [revoking, setRevoking] = useState(false)
31
+ const [isMenuDisplayed, setIsMenuDisplayed] = useState(false)
32
+ const instanceMatchesClient =
33
+ instance !== undefined && instance === client.options.uri
34
+ const contactIsOwner = status === 'owner'
35
+ const shouldShowMenu =
36
+ !revoking &&
37
+ !contactIsOwner &&
38
+ ((instanceMatchesClient && !isOwner) || isOwner)
39
+
40
+ const showMenu = () => setIsMenuDisplayed(true)
41
+ const hideMenu = () => setIsMenuDisplayed(false)
42
+
43
+ const onRevokeClick = useCallback(async () => {
44
+ setRevoking(true)
45
+ if (isOwner) {
46
+ await onRevoke(document, sharingId, index)
47
+ } else {
48
+ await onRevokeSelf(document)
49
+ }
50
+
51
+ setRevoking(false)
52
+ }, [isOwner, onRevoke, onRevokeSelf, document, sharingId, index])
53
+
54
+ const PermissionIcon = type === 'two-way' ? RenameIcon : EyeIcon
55
+
56
+ return (
57
+ <div className={className}>
58
+ {revoking && <Spinner />}
59
+ {!shouldShowMenu && !revoking && (
60
+ <Typography variant="body2">
61
+ {t(`Share.status.${status}`).toLowerCase()}
62
+ </Typography>
63
+ )}
64
+ {shouldShowMenu && (
65
+ <>
66
+ <DropdownButton
67
+ onClick={showMenu}
68
+ ref={buttonRef}
69
+ textVariant="body2"
70
+ >
71
+ {t(`Share.type.${type}`).toLowerCase()}
72
+ </DropdownButton>
73
+ {isMenuDisplayed && (
74
+ <ActionMenu
75
+ onClose={hideMenu}
76
+ popperOptions={{
77
+ placement: 'bottom-end',
78
+ strategy: 'fixed'
79
+ }}
80
+ anchorElRef={buttonRef}
81
+ >
82
+ <ActionMenuItem
83
+ left={
84
+ <Icon icon={PermissionIcon} color="var(--primaryTextColor)" />
85
+ }
86
+ >
87
+ {t(`Share.type.${type}`)}
88
+ </ActionMenuItem>
89
+
90
+ <hr />
91
+ <ActionMenuItem
92
+ onClick={onRevokeClick}
93
+ left={<Icon icon={TrashIcon} color="var(--errorColor)" />}
94
+ >
95
+ <Typography className="u-error" variant="body1">
96
+ {isOwner
97
+ ? t(`${documentType}.share.revoke.title`)
98
+ : t(`${documentType}.share.revokeSelf.title`)}
99
+ </Typography>
100
+ <Typography variant="caption" color="textSecondary">
101
+ {isOwner
102
+ ? t(`${documentType}.share.revoke.desc`)
103
+ : t(`${documentType}.share.revokeSelf.desc`)}
104
+ </Typography>
105
+ </ActionMenuItem>
106
+ </ActionMenu>
107
+ )}
108
+ </>
109
+ )}
110
+ </div>
111
+ )
112
+ }
113
+
114
+ export default RecipientPermissions
@@ -0,0 +1,28 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+
4
+ import AvatarPlusX from './AvatarPlusX'
5
+ import styles from './recipient.styl'
6
+ import { getDisplayName } from '../../models'
7
+ import Identity from './Identity'
8
+
9
+ const RecipientPlusX = ({ extraRecipients }, { t }) => (
10
+ <div className={styles['recipient']}>
11
+ <AvatarPlusX
12
+ extraRecipients={extraRecipients.map(recipient =>
13
+ getDisplayName(recipient)
14
+ )}
15
+ />
16
+ <div className={styles['recipient-ident-status']}>
17
+ <Identity
18
+ name={t('Share.members.otherContacts', extraRecipients.length)}
19
+ />
20
+ </div>
21
+ </div>
22
+ )
23
+
24
+ RecipientPlusX.contextTypes = {
25
+ t: PropTypes.func.isRequired
26
+ }
27
+
28
+ export default RecipientPlusX
@@ -0,0 +1,48 @@
1
+ import React from 'react'
2
+
3
+ import { useI18n } from 'cozy-ui/transpiled/react/I18n'
4
+ import { Media, Img, Bd } from 'cozy-ui/transpiled/react/Media'
5
+ import Typography from 'cozy-ui/transpiled/react/Typography'
6
+ import Icon from 'cozy-ui/transpiled/react/Icon'
7
+ import PaperplaneIcon from 'cozy-ui/transpiled/react/Icons/Paperplane'
8
+ import ToTheCloudIcon from 'cozy-ui/transpiled/react/Icons/ToTheCloud'
9
+ import EyeIcon from 'cozy-ui/transpiled/react/Icons/Eye'
10
+
11
+ import styles from './recipient.styl'
12
+
13
+ const RecipientStatus = ({ status, isMe, instance }) => {
14
+ const { t } = useI18n()
15
+
16
+ const isSendingEmail = !isMe && status === 'mail-not-sent'
17
+ const isReady = isMe || status === 'ready'
18
+ let text, icon
19
+ if (isReady) {
20
+ text = instance
21
+ icon = ToTheCloudIcon
22
+ } else if (isSendingEmail) {
23
+ text = t('Share.status.mail-not-sent')
24
+ icon = PaperplaneIcon
25
+ } else {
26
+ const supportedStatus = ['pending', 'seen']
27
+ text = supportedStatus.includes(status)
28
+ ? t(`Share.status.${status}`)
29
+ : t('Share.status.pending')
30
+
31
+ icon = status === 'seen' ? EyeIcon : PaperplaneIcon
32
+ }
33
+
34
+ return (
35
+ <Media>
36
+ <Img className={styles['recipient-status-icon']}>
37
+ <Icon icon={icon} size={10} />
38
+ </Img>
39
+ <Bd>
40
+ <Typography variant="caption" color="textSecondary">
41
+ {text}
42
+ </Typography>
43
+ </Bd>
44
+ </Media>
45
+ )
46
+ }
47
+
48
+ export default RecipientStatus
@@ -0,0 +1,21 @@
1
+ import React from 'react'
2
+
3
+ import Avatar from 'cozy-ui/transpiled/react/Avatar'
4
+
5
+ import styles from './recipient.styl'
6
+ import { getDisplayName, getInitials } from '../../models'
7
+ import Identity from './Identity'
8
+
9
+ const RecipientWithoutStatus = ({ instance, ...rest }) => {
10
+ const name = getDisplayName(rest)
11
+ return (
12
+ <div className={styles['recipient']}>
13
+ <Avatar text={getInitials(rest)} textId={name} />
14
+ <div className={styles['recipient-ident-status']}>
15
+ <Identity name={name} details={instance} />
16
+ </div>
17
+ </div>
18
+ )
19
+ }
20
+
21
+ export default RecipientWithoutStatus