cozy-sharing 12.2.0 → 13.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/.storybook/preview.jsx +1 -0
- package/CHANGELOG.md +40 -0
- package/dist/components/Avatar/AvatarList.js +13 -2
- package/dist/components/Avatar/AvatarList.spec.js +11 -6
- package/dist/components/Avatar/AvatarList.stories.js +13 -1
- package/dist/components/Avatar/GroupAvatar.js +4 -2
- package/dist/components/ContactSuggestion.js +7 -3
- package/dist/components/ContactSuggestion.spec.js +7 -1
- package/dist/components/Recipient/GroupRecipient.js +14 -7
- package/dist/components/Recipient/GroupRecipientDetail.js +4 -2
- package/dist/components/Recipient/GroupRecipientDetailWithAccess.js +9 -11
- package/dist/components/Recipient/GroupRecipientDetailWithoutAccess.js +8 -4
- package/dist/components/Recipient/GroupRecipientPermissions.js +3 -6
- package/dist/components/Recipient/MemberRecipient.js +1 -2
- package/dist/components/Recipient/MemberRecipientPermissions.js +1 -1
- package/dist/components/ShareAutosuggest.js +16 -9
- package/dist/components/ShareAutosuggest.spec.js +38 -13
- package/dist/components/ShareAutosuggest.stories.js +61 -0
- package/dist/components/ShareByEmail.js +15 -15
- package/dist/components/ShareByEmail.spec.js +0 -6
- package/dist/components/ShareDialogCozyToCozy.js +1 -5
- package/dist/components/ShareDialogCozyToCozy.spec.js +0 -61
- package/dist/components/ShareDialogTwoStepsConfirmationContainer.js +1 -5
- package/dist/components/ShareDialogTwoStepsConfirmationContainer.spec.js +0 -61
- package/dist/components/ShareModal/EditableSharingModal.js +3 -10
- package/dist/components/ShareModal/EditableSharingModal.spec.js +0 -6
- package/dist/components/ShareModal.js +1 -8
- package/dist/components/ShareRecipientsInput.js +46 -49
- package/dist/components/ShareRecipientsInput.spec.js +120 -144
- package/dist/helpers/contacts.js +12 -1
- package/dist/helpers/recipients.js +16 -9
- package/dist/helpers/successMessage.js +58 -16
- package/dist/helpers/successMessage.spec.js +122 -57
- package/dist/queries/queries.js +37 -5
- package/dist/styles/autosuggest.styl +2 -1
- package/dist/stylesheet.css +2 -1
- package/locales/en.json +23 -10
- package/locales/fr.json +23 -8
- package/package.json +4 -4
- package/src/components/Avatar/AvatarList.jsx +33 -14
- package/src/components/Avatar/AvatarList.spec.jsx +11 -6
- package/src/components/Avatar/AvatarList.stories.jsx +19 -1
- package/src/components/Avatar/GroupAvatar.jsx +2 -2
- package/src/components/ContactSuggestion.jsx +9 -3
- package/src/components/ContactSuggestion.spec.jsx +10 -1
- package/src/components/Recipient/GroupRecipient.jsx +17 -10
- package/src/components/Recipient/GroupRecipientDetail.jsx +5 -2
- package/src/components/Recipient/GroupRecipientDetailWithAccess.jsx +9 -14
- package/src/components/Recipient/GroupRecipientDetailWithoutAccess.jsx +8 -3
- package/src/components/Recipient/GroupRecipientPermissions.jsx +3 -8
- package/src/components/Recipient/MemberRecipient.jsx +4 -3
- package/src/components/Recipient/MemberRecipientPermissions.jsx +1 -1
- package/src/components/ShareAutosuggest.jsx +16 -11
- package/src/components/ShareAutosuggest.spec.jsx +31 -10
- package/src/components/ShareAutosuggest.stories.jsx +63 -0
- package/src/components/ShareByEmail.jsx +11 -11
- package/src/components/ShareByEmail.spec.jsx +0 -4
- package/src/components/ShareDialogCozyToCozy.jsx +0 -4
- package/src/components/ShareDialogCozyToCozy.spec.jsx +0 -67
- package/src/components/ShareDialogTwoStepsConfirmationContainer.jsx +0 -4
- package/src/components/ShareDialogTwoStepsConfirmationContainer.spec.jsx +0 -67
- package/src/components/ShareModal/EditableSharingModal.jsx +3 -21
- package/src/components/ShareModal/EditableSharingModal.spec.jsx +0 -2
- package/src/components/ShareModal.jsx +1 -7
- package/src/components/ShareRecipientsInput.jsx +84 -52
- package/src/components/ShareRecipientsInput.spec.jsx +140 -145
- package/src/helpers/contacts.js +8 -0
- package/src/helpers/recipients.js +24 -15
- package/src/helpers/successMessage.js +71 -21
- package/src/helpers/successMessage.spec.js +129 -69
- package/src/queries/queries.js +36 -5
- package/src/styles/autosuggest.styl +2 -1
- package/test/AppLike.jsx +6 -3
|
@@ -4,7 +4,7 @@ import { recipients } from '../../../.storybook/fixtures/recipients'
|
|
|
4
4
|
const meta = {
|
|
5
5
|
component: AvatarList,
|
|
6
6
|
args: {
|
|
7
|
-
recipients: [...recipients
|
|
7
|
+
recipients: [...recipients]
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -14,3 +14,21 @@ export const Default = {
|
|
|
14
14
|
name: 'Default',
|
|
15
15
|
args: {}
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
export const WithGroups = {
|
|
19
|
+
name: 'With groups',
|
|
20
|
+
args: {
|
|
21
|
+
recipients: [
|
|
22
|
+
...recipients,
|
|
23
|
+
{
|
|
24
|
+
name: 'Group 1',
|
|
25
|
+
type: 'group',
|
|
26
|
+
members: [
|
|
27
|
+
{
|
|
28
|
+
public_name: 'Alice'
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -3,8 +3,8 @@ import React from 'react'
|
|
|
3
3
|
import Avatar from 'cozy-ui/transpiled/react/Avatar'
|
|
4
4
|
import TeamIcon from 'cozy-ui/transpiled/react/Icons/Team'
|
|
5
5
|
|
|
6
|
-
const GroupAvatar = ({ size }) => {
|
|
7
|
-
return <Avatar icon={TeamIcon} size={size} />
|
|
6
|
+
const GroupAvatar = ({ size, className }) => {
|
|
7
|
+
return <Avatar icon={TeamIcon} size={size} className={className} />
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export { GroupAvatar }
|
|
@@ -8,6 +8,7 @@ import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
|
|
|
8
8
|
import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
|
|
9
9
|
import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
10
10
|
|
|
11
|
+
import { GroupAvatar } from './Avatar/GroupAvatar'
|
|
11
12
|
import { Contact, Group, getDisplayName, getInitials } from '../models'
|
|
12
13
|
|
|
13
14
|
const ContactModel = models.contact
|
|
@@ -15,11 +16,12 @@ const ContactModel = models.contact
|
|
|
15
16
|
export const ContactSuggestion = ({ contactOrGroup }) => {
|
|
16
17
|
const { t } = useI18n()
|
|
17
18
|
let avatarText, name, details
|
|
18
|
-
|
|
19
|
+
const isContactGroup = contactOrGroup._type === Group.doctype
|
|
20
|
+
if (isContactGroup) {
|
|
19
21
|
name = contactOrGroup.name
|
|
20
22
|
avatarText = 'G'
|
|
21
23
|
details = t('Share.members.count', {
|
|
22
|
-
smart_count: contactOrGroup.
|
|
24
|
+
smart_count: contactOrGroup.members.length.toString()
|
|
23
25
|
})
|
|
24
26
|
} else {
|
|
25
27
|
name = getDisplayName(contactOrGroup)
|
|
@@ -30,7 +32,11 @@ export const ContactSuggestion = ({ contactOrGroup }) => {
|
|
|
30
32
|
return (
|
|
31
33
|
<ListItem button>
|
|
32
34
|
<ListItemIcon>
|
|
33
|
-
|
|
35
|
+
{isContactGroup ? (
|
|
36
|
+
<GroupAvatar size="small" />
|
|
37
|
+
) : (
|
|
38
|
+
<Avatar text={avatarText} size="small" />
|
|
39
|
+
)}
|
|
34
40
|
</ListItemIcon>
|
|
35
41
|
<ListItemText
|
|
36
42
|
primary={name}
|
|
@@ -60,7 +60,16 @@ describe('ContactSuggestion component', () => {
|
|
|
60
60
|
_id: '610718e6-2d7a',
|
|
61
61
|
name: "The Night's Watch",
|
|
62
62
|
_type: 'io.cozy.contacts.groups',
|
|
63
|
-
|
|
63
|
+
members: [
|
|
64
|
+
{
|
|
65
|
+
_id: 'f3a4e501-abbd',
|
|
66
|
+
fullname: 'Jon Snow'
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
_id: 'ab23a451-tzas',
|
|
70
|
+
fullname: 'Samwell Tarly'
|
|
71
|
+
}
|
|
72
|
+
]
|
|
64
73
|
}
|
|
65
74
|
setup({ contactOrGroup: theNightsWatch })
|
|
66
75
|
expect(screen.getByText("The Night's Watch")).toBeInTheDocument()
|
|
@@ -12,11 +12,14 @@ import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
|
12
12
|
|
|
13
13
|
import { GroupRecipientDetail } from './GroupRecipientDetail'
|
|
14
14
|
import { GroupRecipientPermissions } from './GroupRecipientPermissions'
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
FADE_IN_DURATION,
|
|
17
|
+
getGroupRecipientSecondaryText
|
|
18
|
+
} from '../../helpers/recipients'
|
|
16
19
|
import { GroupAvatar } from '../Avatar/GroupAvatar'
|
|
17
20
|
|
|
18
21
|
const GroupRecipient = props => {
|
|
19
|
-
const { name, members, fadeIn, owner } = props
|
|
22
|
+
const { name, members, fadeIn, owner, isOwner } = props
|
|
20
23
|
const { t } = useI18n()
|
|
21
24
|
const client = useClient()
|
|
22
25
|
const { isMobile } = useBreakpoints()
|
|
@@ -27,7 +30,7 @@ const GroupRecipient = props => {
|
|
|
27
30
|
const nbMemberReady = members.filter(
|
|
28
31
|
member => !['revoked', 'mail-not-sent'].includes(member.status)
|
|
29
32
|
).length
|
|
30
|
-
const
|
|
33
|
+
const isUserInsideMembers = members.some(
|
|
31
34
|
member => member.instance === client.options.uri
|
|
32
35
|
)
|
|
33
36
|
|
|
@@ -56,20 +59,24 @@ const GroupRecipient = props => {
|
|
|
56
59
|
{name}
|
|
57
60
|
</Typography>
|
|
58
61
|
}
|
|
59
|
-
secondary={
|
|
60
|
-
t
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
62
|
+
secondary={getGroupRecipientSecondaryText({
|
|
63
|
+
t,
|
|
64
|
+
nbMember,
|
|
65
|
+
nbMemberReady,
|
|
66
|
+
isUserInsideMembers
|
|
67
|
+
})}
|
|
65
68
|
/>
|
|
66
69
|
<ListItemSecondaryAction className={isMobile ? 'u-mr-1' : 'u-mr-2'}>
|
|
67
|
-
<GroupRecipientPermissions
|
|
70
|
+
<GroupRecipientPermissions
|
|
71
|
+
isUserInsideMembers={isUserInsideMembers}
|
|
72
|
+
{...props}
|
|
73
|
+
/>
|
|
68
74
|
</ListItemSecondaryAction>
|
|
69
75
|
</ListItem>
|
|
70
76
|
</Fade>
|
|
71
77
|
{isDetailOpened ? (
|
|
72
78
|
<GroupRecipientDetail
|
|
79
|
+
isOwner={isOwner}
|
|
73
80
|
owner={owner}
|
|
74
81
|
name={name}
|
|
75
82
|
members={members}
|
|
@@ -7,7 +7,7 @@ import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
|
7
7
|
import { GroupRecipientDetailWithAccess } from './GroupRecipientDetailWithAccess'
|
|
8
8
|
import { GroupRecipientDetailWithoutAccess } from './GroupRecipientDetailWithoutAccess'
|
|
9
9
|
|
|
10
|
-
const GroupRecipientDetail = ({ name, owner, members, onClose }) => {
|
|
10
|
+
const GroupRecipientDetail = ({ name, owner, members, onClose, isOwner }) => {
|
|
11
11
|
const { t } = useI18n()
|
|
12
12
|
const withAccess = members.filter(
|
|
13
13
|
member => !['revoked', 'mail-not-sent'].includes(member.status)
|
|
@@ -39,7 +39,10 @@ const GroupRecipientDetail = ({ name, owner, members, onClose }) => {
|
|
|
39
39
|
<GroupRecipientDetailWithAccess withAccess={withAccess} />
|
|
40
40
|
) : null}
|
|
41
41
|
{withoutAccess.length > 0 ? (
|
|
42
|
-
<GroupRecipientDetailWithoutAccess
|
|
42
|
+
<GroupRecipientDetailWithoutAccess
|
|
43
|
+
withoutAccess={withoutAccess}
|
|
44
|
+
isOwner={isOwner}
|
|
45
|
+
/>
|
|
43
46
|
) : null}
|
|
44
47
|
</div>
|
|
45
48
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
|
+
import { useClient } from 'cozy-client'
|
|
3
4
|
import Avatar from 'cozy-ui/transpiled/react/Avatar'
|
|
4
|
-
import Icon from 'cozy-ui/transpiled/react/Icon'
|
|
5
|
-
import ToTheCloudIcon from 'cozy-ui/transpiled/react/Icons/ToTheCloud'
|
|
6
5
|
import List from 'cozy-ui/transpiled/react/List'
|
|
7
6
|
import ListItem from 'cozy-ui/transpiled/react/ListItem'
|
|
8
7
|
import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
|
|
@@ -10,10 +9,12 @@ import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
|
|
|
10
9
|
import Typography from 'cozy-ui/transpiled/react/Typography'
|
|
11
10
|
import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
12
11
|
|
|
12
|
+
import MemberRecipientStatus from './MemberRecipientStatus'
|
|
13
13
|
import { getInitials, getDisplayName } from '../../models'
|
|
14
14
|
|
|
15
15
|
const GroupRecipientDetailWithAccess = ({ withAccess }) => {
|
|
16
16
|
const { t } = useI18n()
|
|
17
|
+
const client = useClient()
|
|
17
18
|
|
|
18
19
|
return (
|
|
19
20
|
<div>
|
|
@@ -23,6 +24,7 @@ const GroupRecipientDetailWithAccess = ({ withAccess }) => {
|
|
|
23
24
|
<List>
|
|
24
25
|
{withAccess.map(recipient => {
|
|
25
26
|
const name = getDisplayName(recipient)
|
|
27
|
+
const isMe = recipient.instance === client.options.uri
|
|
26
28
|
|
|
27
29
|
return (
|
|
28
30
|
<ListItem disableGutters key={recipient.index}>
|
|
@@ -32,18 +34,11 @@ const GroupRecipientDetailWithAccess = ({ withAccess }) => {
|
|
|
32
34
|
<ListItemText
|
|
33
35
|
primary={name}
|
|
34
36
|
secondary={
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
style={{
|
|
41
|
-
marginRight: '0.25rem'
|
|
42
|
-
}}
|
|
43
|
-
/>
|
|
44
|
-
{recipient.instance}
|
|
45
|
-
</div>
|
|
46
|
-
) : null
|
|
37
|
+
<MemberRecipientStatus
|
|
38
|
+
status={recipient.status}
|
|
39
|
+
isMe={isMe}
|
|
40
|
+
instance={recipient.instance}
|
|
41
|
+
/>
|
|
47
42
|
}
|
|
48
43
|
/>
|
|
49
44
|
</ListItem>
|
|
@@ -10,9 +10,10 @@ import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
|
|
|
10
10
|
import Typography from 'cozy-ui/transpiled/react/Typography'
|
|
11
11
|
import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
12
12
|
|
|
13
|
+
import { DEFAULT_DISPLAY_NAME } from '../../helpers/recipients'
|
|
13
14
|
import { getInitials, getDisplayName } from '../../models'
|
|
14
15
|
|
|
15
|
-
const GroupRecipientDetailWithoutAccess = ({ withoutAccess }) => {
|
|
16
|
+
const GroupRecipientDetailWithoutAccess = ({ withoutAccess, isOwner }) => {
|
|
16
17
|
const { t } = useI18n()
|
|
17
18
|
|
|
18
19
|
return (
|
|
@@ -23,10 +24,11 @@ const GroupRecipientDetailWithoutAccess = ({ withoutAccess }) => {
|
|
|
23
24
|
<List>
|
|
24
25
|
{withoutAccess.map(recipient => {
|
|
25
26
|
const icon = recipient.status === 'revoked' ? ForbiddenIcon : null
|
|
26
|
-
const
|
|
27
|
+
const defaultDisplayName = t(DEFAULT_DISPLAY_NAME)
|
|
28
|
+
const name = getDisplayName(recipient, defaultDisplayName)
|
|
27
29
|
|
|
28
30
|
return (
|
|
29
|
-
<ListItem disableGutters key={recipient.index}>
|
|
31
|
+
<ListItem disableGutters key={recipient.index} ellipsis={false}>
|
|
30
32
|
<ListItemIcon>
|
|
31
33
|
<Avatar text={getInitials(recipient)} textId={name} />
|
|
32
34
|
</ListItemIcon>
|
|
@@ -46,6 +48,9 @@ const GroupRecipientDetailWithoutAccess = ({ withoutAccess }) => {
|
|
|
46
48
|
{t(
|
|
47
49
|
`GroupRecipientDetail.withoutAccess.status.${recipient.status}`
|
|
48
50
|
)}
|
|
51
|
+
{isOwner
|
|
52
|
+
? ` - ${t('GroupRecipientDetail.withoutAccess.addEmail')}`
|
|
53
|
+
: null}
|
|
49
54
|
</div>
|
|
50
55
|
}
|
|
51
56
|
/>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React, { useState, useRef } from 'react'
|
|
2
2
|
|
|
3
|
-
import { useClient } from 'cozy-client'
|
|
4
3
|
import ActionsMenu from 'cozy-ui/transpiled/react/ActionsMenu'
|
|
5
4
|
import {
|
|
6
5
|
makeActions,
|
|
@@ -21,24 +20,20 @@ import { GroupAvatar } from '../Avatar/GroupAvatar'
|
|
|
21
20
|
const GroupRecipientPermissions = ({
|
|
22
21
|
name,
|
|
23
22
|
isOwner,
|
|
24
|
-
instance,
|
|
25
23
|
sharingId,
|
|
26
24
|
groupIndex,
|
|
27
25
|
read_only: isReadOnly = false,
|
|
28
|
-
className
|
|
26
|
+
className,
|
|
27
|
+
isUserInsideMembers
|
|
29
28
|
}) => {
|
|
30
29
|
const { t } = useI18n()
|
|
31
30
|
const buttonRef = useRef()
|
|
32
|
-
const client = useClient()
|
|
33
31
|
const { revokeGroup, revokeSelf } = useSharingContext()
|
|
34
32
|
|
|
35
33
|
const [isMenuDisplayed, setMenuDisplayed] = useState(false)
|
|
36
34
|
const [revoking, setRevoking] = useState(false)
|
|
37
35
|
|
|
38
|
-
const
|
|
39
|
-
instance !== undefined && instance === client.options.uri
|
|
40
|
-
const shouldShowMenu =
|
|
41
|
-
!revoking && ((instanceMatchesClient && !isOwner) || isOwner)
|
|
36
|
+
const shouldShowMenu = !revoking && (isOwner || isUserInsideMembers)
|
|
42
37
|
|
|
43
38
|
const toggleMenu = () => setMenuDisplayed(!isMenuDisplayed)
|
|
44
39
|
const hideMenu = () => setMenuDisplayed(false)
|
|
@@ -13,12 +13,13 @@ import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
|
13
13
|
import MemberRecipientPermissions from './MemberRecipientPermissions'
|
|
14
14
|
import MemberRecipientStatus from './MemberRecipientStatus'
|
|
15
15
|
import RecipientConfirm from './RecipientConfirm'
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
FADE_IN_DURATION,
|
|
18
|
+
DEFAULT_DISPLAY_NAME
|
|
19
|
+
} from '../../helpers/recipients'
|
|
17
20
|
import { getDisplayName } from '../../models'
|
|
18
21
|
import { MemberAvatar } from '../Avatar/MemberAvatar'
|
|
19
22
|
|
|
20
|
-
const DEFAULT_DISPLAY_NAME = 'Share.contacts.defaultDisplayName'
|
|
21
|
-
|
|
22
23
|
const MemberRecipient = props => {
|
|
23
24
|
const { t } = useI18n()
|
|
24
25
|
const client = useClient()
|
|
@@ -7,9 +7,10 @@ import Chip from 'cozy-ui/transpiled/react/Chips'
|
|
|
7
7
|
import { Spinner } from 'cozy-ui/transpiled/react/Spinner'
|
|
8
8
|
import palette from 'cozy-ui/transpiled/react/palette'
|
|
9
9
|
|
|
10
|
+
import { GroupAvatar } from './Avatar/GroupAvatar'
|
|
10
11
|
import ContactSuggestion from './ContactSuggestion'
|
|
11
12
|
import { extractEmails, validateEmail } from '../helpers/email'
|
|
12
|
-
import { getDisplayName, getInitials, Contact } from '../models'
|
|
13
|
+
import { getDisplayName, getInitials, Contact, Group } from '../models'
|
|
13
14
|
import styles from '../styles/autosuggest.styl'
|
|
14
15
|
import {
|
|
15
16
|
cozyUrlMatch,
|
|
@@ -22,7 +23,6 @@ const ShareAutocomplete = ({
|
|
|
22
23
|
loading,
|
|
23
24
|
contactsAndGroups,
|
|
24
25
|
recipients,
|
|
25
|
-
onFocus,
|
|
26
26
|
onPick,
|
|
27
27
|
onRemove,
|
|
28
28
|
placeholder
|
|
@@ -30,6 +30,7 @@ const ShareAutocomplete = ({
|
|
|
30
30
|
const [inputValue, setInputValue] = useState('')
|
|
31
31
|
const [suggestions, setSuggestions] = useState([])
|
|
32
32
|
const [isPasted, setIsPasted] = useState(false)
|
|
33
|
+
const [isLoadingDisplayed, setLoadingDisplayed] = useState(false)
|
|
33
34
|
|
|
34
35
|
const autosuggestRef = useRef(null)
|
|
35
36
|
|
|
@@ -116,7 +117,8 @@ const ShareAutocomplete = ({
|
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
const onAutosuggestFocus = () => {
|
|
119
|
-
|
|
120
|
+
// We only show the loading state when the user has focus on the input
|
|
121
|
+
setLoadingDisplayed(true)
|
|
120
122
|
}
|
|
121
123
|
|
|
122
124
|
const onAutosuggestBlur = () => {
|
|
@@ -139,13 +141,20 @@ const ShareAutocomplete = ({
|
|
|
139
141
|
const renderInput = inputProps => (
|
|
140
142
|
<div className={styles['recipientsContainer']}>
|
|
141
143
|
{recipients.map((recipient, idx) => {
|
|
144
|
+
const isContactGroup = recipient._type === Group.doctype
|
|
142
145
|
const avatarText = getInitials(recipient)
|
|
143
146
|
const name = getDisplayName(recipient)
|
|
144
147
|
return (
|
|
145
148
|
<Chip
|
|
146
149
|
id={`recipient_${idx}`}
|
|
147
150
|
key={`key_recipient_${idx}`}
|
|
148
|
-
avatar={
|
|
151
|
+
avatar={
|
|
152
|
+
isContactGroup ? (
|
|
153
|
+
<GroupAvatar size="xsmall" />
|
|
154
|
+
) : (
|
|
155
|
+
<Avatar text={avatarText} size="xsmall" />
|
|
156
|
+
)
|
|
157
|
+
}
|
|
149
158
|
label={name}
|
|
150
159
|
onDelete={() => {
|
|
151
160
|
onAutosuggestRemove(recipient)
|
|
@@ -155,12 +164,9 @@ const ShareAutocomplete = ({
|
|
|
155
164
|
)
|
|
156
165
|
})}
|
|
157
166
|
<input {...inputProps} onKeyPress={onKeyPress} onKeyUp={onKeyUp} />
|
|
158
|
-
{loading && (
|
|
159
|
-
<Spinner
|
|
160
|
-
|
|
161
|
-
className="u-flex u-flex-items-center"
|
|
162
|
-
/>
|
|
163
|
-
)}
|
|
167
|
+
{loading && isLoadingDisplayed ? (
|
|
168
|
+
<Spinner color={palette.dodgerBlue} />
|
|
169
|
+
) : null}
|
|
164
170
|
</div>
|
|
165
171
|
)
|
|
166
172
|
|
|
@@ -196,7 +202,6 @@ ShareAutocomplete.propTypes = {
|
|
|
196
202
|
loading: PropTypes.bool,
|
|
197
203
|
contactsAndGroups: PropTypes.array,
|
|
198
204
|
recipients: PropTypes.array.isRequired,
|
|
199
|
-
onFocus: PropTypes.func.isRequired,
|
|
200
205
|
onPick: PropTypes.func.isRequired,
|
|
201
206
|
onRemove: PropTypes.func.isRequired,
|
|
202
207
|
placeholder: PropTypes.string
|
|
@@ -1,29 +1,36 @@
|
|
|
1
|
-
import { render, fireEvent } from '@testing-library/react'
|
|
1
|
+
import { render, fireEvent, screen } from '@testing-library/react'
|
|
2
2
|
import React from 'react'
|
|
3
3
|
|
|
4
4
|
import ShareAutosuggest from './ShareAutosuggest'
|
|
5
5
|
import AppLike from '../../test/AppLike'
|
|
6
6
|
|
|
7
|
+
jest.mock('cozy-ui/transpiled/react/Spinner', () => ({
|
|
8
|
+
Spinner: () => <div>loading</div>
|
|
9
|
+
}))
|
|
10
|
+
|
|
7
11
|
describe('ShareAutosuggest', () => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const onFocus = jest.fn()
|
|
11
|
-
const onRemove = jest.fn()
|
|
12
|
-
const { getByPlaceholderText } = render(
|
|
12
|
+
const setup = ({ onPick, onRemove, loading = false }) => {
|
|
13
|
+
render(
|
|
13
14
|
<AppLike>
|
|
14
15
|
<ShareAutosuggest
|
|
16
|
+
loading={loading}
|
|
15
17
|
contactsAndGroups={[]}
|
|
16
18
|
placeholder="myPlaceHolder"
|
|
17
19
|
onPick={onPick}
|
|
18
|
-
onFocus={onFocus}
|
|
19
20
|
recipients={[]}
|
|
20
21
|
onRemove={onRemove}
|
|
21
22
|
/>
|
|
22
23
|
</AppLike>
|
|
23
24
|
)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
it('tests if ShareAutosuggest calls onPick', () => {
|
|
28
|
+
const onPick = jest.fn()
|
|
29
|
+
const onRemove = jest.fn()
|
|
30
|
+
|
|
31
|
+
setup({ onPick, onRemove })
|
|
32
|
+
|
|
33
|
+
const inputNode = screen.getByPlaceholderText('myPlaceHolder')
|
|
27
34
|
// It should not call onPick if the value is not an email
|
|
28
35
|
fireEvent.change(inputNode, { target: { value: 'quentin@qq' } })
|
|
29
36
|
fireEvent.keyPress(inputNode, { key: 'Enter', keyCode: 13, charCode: 13 })
|
|
@@ -43,4 +50,18 @@ describe('ShareAutosuggest', () => {
|
|
|
43
50
|
email: 'quentin.valmori@cozycloud.cc'
|
|
44
51
|
})
|
|
45
52
|
})
|
|
53
|
+
|
|
54
|
+
it('should show loading only after user focus input', () => {
|
|
55
|
+
const onPick = jest.fn()
|
|
56
|
+
const onRemove = jest.fn()
|
|
57
|
+
|
|
58
|
+
setup({ onPick, onRemove, loading: true })
|
|
59
|
+
|
|
60
|
+
const inputNode = screen.getByPlaceholderText('myPlaceHolder')
|
|
61
|
+
|
|
62
|
+
expect(screen.queryByText('loading')).toBeNull()
|
|
63
|
+
fireEvent.focus(inputNode)
|
|
64
|
+
|
|
65
|
+
expect(screen.getByText('loading')).toBeInTheDocument()
|
|
66
|
+
})
|
|
46
67
|
})
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import ShareAutosuggest from './ShareAutosuggest'
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
component: ShareAutosuggest,
|
|
7
|
+
args: {}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default meta
|
|
11
|
+
|
|
12
|
+
const ShareAutosuggestRender = props => {
|
|
13
|
+
const [recipients, setRecipients] = useState([])
|
|
14
|
+
const onPick = contactOrGroup => {
|
|
15
|
+
setRecipients([...recipients, contactOrGroup])
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const onRemove = contactOrGroup => {
|
|
19
|
+
setRecipients(recipients.filter(r => r.id !== contactOrGroup.id))
|
|
20
|
+
}
|
|
21
|
+
return (
|
|
22
|
+
<ShareAutosuggest
|
|
23
|
+
{...props}
|
|
24
|
+
recipients={recipients}
|
|
25
|
+
onPick={onPick}
|
|
26
|
+
onRemove={onRemove}
|
|
27
|
+
/>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const Default = {
|
|
32
|
+
name: 'Default',
|
|
33
|
+
render: ShareAutosuggestRender,
|
|
34
|
+
args: {
|
|
35
|
+
loading: false,
|
|
36
|
+
contactsAndGroups: [
|
|
37
|
+
{
|
|
38
|
+
id: 'group1',
|
|
39
|
+
name: 'Family',
|
|
40
|
+
_type: 'io.cozy.contacts.groups',
|
|
41
|
+
members: [
|
|
42
|
+
{
|
|
43
|
+
id: 'contact1',
|
|
44
|
+
name: 'John Doe',
|
|
45
|
+
_type: 'io.cozy.contacts'
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: 'contact2',
|
|
49
|
+
name: 'Jane Doe',
|
|
50
|
+
_type: 'io.cozy.contacts'
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: 'contact3',
|
|
56
|
+
name: 'Alice Wonderland',
|
|
57
|
+
_type: 'io.cozy.contacts',
|
|
58
|
+
email: 'alice@wonder.land'
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
placeholder: 'Type a name or email address'
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -4,6 +4,7 @@ import React, { useState } from 'react'
|
|
|
4
4
|
import { useClient } from 'cozy-client'
|
|
5
5
|
import flag from 'cozy-flags'
|
|
6
6
|
import Alerter from 'cozy-ui/transpiled/react/deprecated/Alerter'
|
|
7
|
+
import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
|
|
7
8
|
import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
8
9
|
|
|
9
10
|
import ShareRecipientsInput from './ShareRecipientsInput'
|
|
@@ -17,13 +18,10 @@ import {
|
|
|
17
18
|
hasReachRecipientsLimit
|
|
18
19
|
} from '../helpers/recipients'
|
|
19
20
|
import { getSuccessMessage } from '../helpers/successMessage'
|
|
20
|
-
import { contactsResponseType, contactGroupsResponseType } from '../propTypes'
|
|
21
21
|
import { isReadOnlySharing } from '../state'
|
|
22
22
|
import styles from '../styles/share.styl'
|
|
23
23
|
|
|
24
24
|
export const ShareByEmail = ({
|
|
25
|
-
contacts,
|
|
26
|
-
contactGroups,
|
|
27
25
|
document,
|
|
28
26
|
sharingDesc,
|
|
29
27
|
onShare,
|
|
@@ -34,6 +32,8 @@ export const ShareByEmail = ({
|
|
|
34
32
|
}) => {
|
|
35
33
|
const client = useClient()
|
|
36
34
|
const { t } = useI18n()
|
|
35
|
+
const { showAlert } = useAlert()
|
|
36
|
+
|
|
37
37
|
const [recipients, setRecipients] = useState([])
|
|
38
38
|
const [loading, setLoading] = useState(false)
|
|
39
39
|
const [selectedOption, setSelectedOption] = useState()
|
|
@@ -51,7 +51,7 @@ export const ShareByEmail = ({
|
|
|
51
51
|
const onRecipientPick = recipient => {
|
|
52
52
|
const mergedRecipients = flag('sharing.show-recipient-groups')
|
|
53
53
|
? mergeRecipients(recipients, recipient)
|
|
54
|
-
: spreadGroupAndMergeRecipients(recipients, recipient
|
|
54
|
+
: spreadGroupAndMergeRecipients(recipients, recipient)
|
|
55
55
|
setRecipients(mergedRecipients)
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -96,9 +96,13 @@ export const ShareByEmail = ({
|
|
|
96
96
|
openSharing: readWriteRecipients.length > 0
|
|
97
97
|
})
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
t(
|
|
101
|
-
|
|
99
|
+
showAlert({
|
|
100
|
+
message: t(
|
|
101
|
+
...getSuccessMessage(recipientsBefore, contacts, documentType)
|
|
102
|
+
),
|
|
103
|
+
severity: 'success',
|
|
104
|
+
variant: 'filled'
|
|
105
|
+
})
|
|
102
106
|
reset()
|
|
103
107
|
} catch (err) {
|
|
104
108
|
Alerter.error('Error.generic')
|
|
@@ -139,8 +143,6 @@ export const ShareByEmail = ({
|
|
|
139
143
|
}
|
|
140
144
|
onPick={recipient => onRecipientPick(recipient)}
|
|
141
145
|
onRemove={recipient => onRecipientRemove(recipient)}
|
|
142
|
-
contacts={contacts}
|
|
143
|
-
contactGroups={contactGroups}
|
|
144
146
|
recipients={recipients}
|
|
145
147
|
/>
|
|
146
148
|
</div>
|
|
@@ -167,8 +169,6 @@ export const ShareByEmail = ({
|
|
|
167
169
|
|
|
168
170
|
ShareByEmail.propTypes = {
|
|
169
171
|
currentRecipients: PropTypes.arrayOf(PropTypes.object),
|
|
170
|
-
contacts: contactsResponseType.isRequired,
|
|
171
|
-
contactGroups: contactGroupsResponseType.isRequired,
|
|
172
172
|
document: PropTypes.object.isRequired,
|
|
173
173
|
documentType: PropTypes.string.isRequired,
|
|
174
174
|
sharingDesc: PropTypes.string.isRequired,
|
|
@@ -15,8 +15,6 @@ import styles from '../styles/share.styl'
|
|
|
15
15
|
* Displays the sharing interface that can be used when ShareDialogCozyToCozy is in sharing state
|
|
16
16
|
*/
|
|
17
17
|
const SharingContent = ({
|
|
18
|
-
contacts,
|
|
19
|
-
contactGroups,
|
|
20
18
|
createContact,
|
|
21
19
|
document,
|
|
22
20
|
documentType,
|
|
@@ -68,8 +66,6 @@ const SharingContent = ({
|
|
|
68
66
|
</Typography>
|
|
69
67
|
{showShareByEmail && (
|
|
70
68
|
<DumbShareByEmail
|
|
71
|
-
contacts={contacts}
|
|
72
|
-
contactGroups={contactGroups}
|
|
73
69
|
createContact={createContact}
|
|
74
70
|
currentRecipients={recipients}
|
|
75
71
|
document={document}
|