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.
- package/CHANGELOG.md +69 -0
- package/dist/SharedRecipients.js +1 -1
- package/dist/SharedStatus.js +1 -1
- package/dist/SharingBanner/hooks/useSharingInfos.js +1 -1
- package/dist/SharingDetailsModal.js +1 -1
- package/dist/SharingOwnerAvatar.js +1 -1
- package/dist/SharingProvider.js +21 -8
- package/dist/components/ContactSuggestion.js +15 -27
- package/dist/components/EditLinkPermissionDialog.js +72 -0
- package/dist/components/EditLinkPermissionDialog.spec.js +48 -0
- package/dist/components/EditableSharingModal.js +15 -62
- package/dist/components/{AvatarPlusX.js → Recipient/AvatarPlusX.js} +1 -1
- package/dist/components/{Identity.js → Recipient/Identity.js} +17 -16
- package/dist/components/Recipient/LinkRecipient.js +72 -0
- package/dist/components/Recipient/LinkRecipientPermissions.js +159 -0
- package/dist/components/Recipient/OwnerIdentity.js +44 -0
- package/dist/components/Recipient/Recipient.js +72 -0
- package/dist/components/{Recipient.spec.js → Recipient/Recipient.spec.js} +4 -45
- package/dist/components/Recipient/RecipientAvatar.js +33 -0
- package/dist/components/Recipient/RecipientConfirm.js +63 -0
- package/dist/components/Recipient/RecipientPermissions.js +126 -0
- package/dist/components/Recipient/RecipientPlusX.js +45 -0
- package/dist/components/Recipient/RecipientStatus.js +64 -0
- package/dist/components/Recipient/RecipientWithoutStatus.js +45 -0
- package/dist/components/Recipient/RecipientsAvatars.js +100 -0
- package/dist/components/{RecipientsAvatars.spec.js → Recipient/RecipientsAvatars.spec.js} +43 -2
- package/dist/components/{recipient.styl → Recipient/recipient.styl} +6 -2
- package/dist/components/ShareAutosuggest.js +176 -252
- package/dist/components/ShareAutosuggest.spec.js +3 -2
- package/dist/components/ShareByEmail.js +142 -193
- package/dist/components/ShareByEmail.spec.js +42 -23
- package/dist/components/ShareByLink.js +251 -357
- package/dist/components/ShareDialogCozyToCozy.js +16 -4
- package/dist/components/ShareDialogCozyToCozy.spec.js +12 -1
- package/dist/components/ShareDialogOnlyByLink.js +23 -10
- package/dist/components/ShareDialogTwoStepsConfirmationContainer.js +7 -7
- package/dist/components/ShareModal.js +5 -1
- package/dist/components/ShareRecipientsInput.js +44 -128
- package/dist/components/WhoHasAccess.js +43 -5
- package/dist/components/WhoHasAccessLight.js +3 -19
- package/dist/components/autosuggest.styl +21 -36
- package/dist/helpers/documentType.js +6 -0
- package/dist/helpers/hooks.js +8 -1
- package/dist/helpers/link.js +5 -0
- package/dist/helpers/permissions.js +8 -0
- package/dist/helpers/recipients.js +24 -0
- package/dist/helpers/sharings.js +72 -53
- package/dist/helpers/synchronousJobQueue.js +67 -0
- package/dist/helpers/synchronousJobQueue.spec.js +97 -0
- package/dist/index.js +2 -1
- package/dist/queries/queries.js +72 -0
- package/dist/share.styl +0 -15
- package/dist/stylesheet.css +67 -92
- package/locales/en.json +29 -41
- package/locales/fr.json +26 -39
- package/package.json +10 -10
- package/src/SharedRecipients.jsx +1 -1
- package/src/SharedStatus.jsx +1 -1
- package/src/SharingBanner/hooks/useSharingInfos.jsx +1 -1
- package/src/SharingDetailsModal.jsx +1 -1
- package/src/SharingOwnerAvatar.jsx +1 -1
- package/src/SharingProvider.jsx +17 -4
- package/src/components/ContactSuggestion.jsx +25 -10
- package/src/components/EditLinkPermissionDialog.jsx +89 -0
- package/src/components/EditLinkPermissionDialog.spec.jsx +52 -0
- package/src/components/EditableSharingModal.jsx +58 -100
- package/src/components/{AvatarPlusX.jsx → Recipient/AvatarPlusX.jsx} +1 -1
- package/src/components/{Identity.jsx → Recipient/Identity.jsx} +0 -0
- package/src/components/Recipient/LinkRecipient.jsx +59 -0
- package/src/components/Recipient/LinkRecipientPermissions.jsx +157 -0
- package/src/components/Recipient/OwnerIdentity.jsx +20 -0
- package/src/components/Recipient/Recipient.jsx +79 -0
- package/src/components/{Recipient.spec.jsx → Recipient/Recipient.spec.jsx} +4 -42
- package/src/components/Recipient/RecipientAvatar.jsx +38 -0
- package/src/components/Recipient/RecipientConfirm.jsx +37 -0
- package/src/components/Recipient/RecipientPermissions.jsx +114 -0
- package/src/components/Recipient/RecipientPlusX.jsx +28 -0
- package/src/components/Recipient/RecipientStatus.jsx +48 -0
- package/src/components/Recipient/RecipientWithoutStatus.jsx +21 -0
- package/src/components/Recipient/RecipientsAvatars.jsx +108 -0
- package/src/components/{RecipientsAvatars.spec.jsx → Recipient/RecipientsAvatars.spec.jsx} +43 -2
- package/src/components/{recipient.styl → Recipient/recipient.styl} +6 -2
- package/src/components/ShareAutosuggest.jsx +136 -127
- package/src/components/ShareAutosuggest.spec.jsx +12 -8
- package/src/components/ShareByEmail.jsx +65 -90
- package/src/components/ShareByEmail.spec.jsx +39 -11
- package/src/components/ShareByLink.jsx +146 -238
- package/src/components/ShareDialogCozyToCozy.jsx +14 -2
- package/src/components/ShareDialogCozyToCozy.spec.jsx +8 -1
- package/src/components/ShareDialogOnlyByLink.jsx +30 -13
- package/src/components/ShareDialogTwoStepsConfirmationContainer.jsx +5 -5
- package/src/components/ShareModal.jsx +5 -1
- package/src/components/ShareRecipientsInput.jsx +30 -44
- package/src/components/WhoHasAccess.jsx +85 -27
- package/src/components/WhoHasAccessLight.jsx +2 -1
- package/src/components/__snapshots__/ContactSuggestion.spec.jsx.snap +915 -132
- package/src/components/__snapshots__/ShareRecipientsInput.spec.jsx.snap +86 -102
- package/src/components/autosuggest.styl +21 -36
- package/src/helpers/documentType.js +6 -0
- package/src/helpers/hooks.js +11 -1
- package/src/helpers/link.js +4 -0
- package/src/helpers/permissions.js +15 -0
- package/src/helpers/recipients.js +21 -0
- package/src/helpers/sharings.js +15 -12
- package/src/helpers/synchronousJobQueue.js +22 -0
- package/src/helpers/synchronousJobQueue.spec.js +75 -0
- package/src/index.jsx +1 -0
- package/src/queries/queries.js +75 -0
- package/src/share.styl +0 -15
- package/dist/SharingBanner/helpers/queries.js +0 -12
- package/dist/components/ContactsAndGroupsDataLoader.js +0 -50
- package/dist/components/Recipient.js +0 -450
- package/src/SharingBanner/helpers/queries.js +0 -14
- package/src/components/ContactsAndGroupsDataLoader.jsx +0 -30
- 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
|
|
3
|
-
import AppLike from '
|
|
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('
|
|
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('
|
|
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
|