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.
Files changed (73) hide show
  1. package/.storybook/preview.jsx +1 -0
  2. package/CHANGELOG.md +40 -0
  3. package/dist/components/Avatar/AvatarList.js +13 -2
  4. package/dist/components/Avatar/AvatarList.spec.js +11 -6
  5. package/dist/components/Avatar/AvatarList.stories.js +13 -1
  6. package/dist/components/Avatar/GroupAvatar.js +4 -2
  7. package/dist/components/ContactSuggestion.js +7 -3
  8. package/dist/components/ContactSuggestion.spec.js +7 -1
  9. package/dist/components/Recipient/GroupRecipient.js +14 -7
  10. package/dist/components/Recipient/GroupRecipientDetail.js +4 -2
  11. package/dist/components/Recipient/GroupRecipientDetailWithAccess.js +9 -11
  12. package/dist/components/Recipient/GroupRecipientDetailWithoutAccess.js +8 -4
  13. package/dist/components/Recipient/GroupRecipientPermissions.js +3 -6
  14. package/dist/components/Recipient/MemberRecipient.js +1 -2
  15. package/dist/components/Recipient/MemberRecipientPermissions.js +1 -1
  16. package/dist/components/ShareAutosuggest.js +16 -9
  17. package/dist/components/ShareAutosuggest.spec.js +38 -13
  18. package/dist/components/ShareAutosuggest.stories.js +61 -0
  19. package/dist/components/ShareByEmail.js +15 -15
  20. package/dist/components/ShareByEmail.spec.js +0 -6
  21. package/dist/components/ShareDialogCozyToCozy.js +1 -5
  22. package/dist/components/ShareDialogCozyToCozy.spec.js +0 -61
  23. package/dist/components/ShareDialogTwoStepsConfirmationContainer.js +1 -5
  24. package/dist/components/ShareDialogTwoStepsConfirmationContainer.spec.js +0 -61
  25. package/dist/components/ShareModal/EditableSharingModal.js +3 -10
  26. package/dist/components/ShareModal/EditableSharingModal.spec.js +0 -6
  27. package/dist/components/ShareModal.js +1 -8
  28. package/dist/components/ShareRecipientsInput.js +46 -49
  29. package/dist/components/ShareRecipientsInput.spec.js +120 -144
  30. package/dist/helpers/contacts.js +12 -1
  31. package/dist/helpers/recipients.js +16 -9
  32. package/dist/helpers/successMessage.js +58 -16
  33. package/dist/helpers/successMessage.spec.js +122 -57
  34. package/dist/queries/queries.js +37 -5
  35. package/dist/styles/autosuggest.styl +2 -1
  36. package/dist/stylesheet.css +2 -1
  37. package/locales/en.json +23 -10
  38. package/locales/fr.json +23 -8
  39. package/package.json +4 -4
  40. package/src/components/Avatar/AvatarList.jsx +33 -14
  41. package/src/components/Avatar/AvatarList.spec.jsx +11 -6
  42. package/src/components/Avatar/AvatarList.stories.jsx +19 -1
  43. package/src/components/Avatar/GroupAvatar.jsx +2 -2
  44. package/src/components/ContactSuggestion.jsx +9 -3
  45. package/src/components/ContactSuggestion.spec.jsx +10 -1
  46. package/src/components/Recipient/GroupRecipient.jsx +17 -10
  47. package/src/components/Recipient/GroupRecipientDetail.jsx +5 -2
  48. package/src/components/Recipient/GroupRecipientDetailWithAccess.jsx +9 -14
  49. package/src/components/Recipient/GroupRecipientDetailWithoutAccess.jsx +8 -3
  50. package/src/components/Recipient/GroupRecipientPermissions.jsx +3 -8
  51. package/src/components/Recipient/MemberRecipient.jsx +4 -3
  52. package/src/components/Recipient/MemberRecipientPermissions.jsx +1 -1
  53. package/src/components/ShareAutosuggest.jsx +16 -11
  54. package/src/components/ShareAutosuggest.spec.jsx +31 -10
  55. package/src/components/ShareAutosuggest.stories.jsx +63 -0
  56. package/src/components/ShareByEmail.jsx +11 -11
  57. package/src/components/ShareByEmail.spec.jsx +0 -4
  58. package/src/components/ShareDialogCozyToCozy.jsx +0 -4
  59. package/src/components/ShareDialogCozyToCozy.spec.jsx +0 -67
  60. package/src/components/ShareDialogTwoStepsConfirmationContainer.jsx +0 -4
  61. package/src/components/ShareDialogTwoStepsConfirmationContainer.spec.jsx +0 -67
  62. package/src/components/ShareModal/EditableSharingModal.jsx +3 -21
  63. package/src/components/ShareModal/EditableSharingModal.spec.jsx +0 -2
  64. package/src/components/ShareModal.jsx +1 -7
  65. package/src/components/ShareRecipientsInput.jsx +84 -52
  66. package/src/components/ShareRecipientsInput.spec.jsx +140 -145
  67. package/src/helpers/contacts.js +8 -0
  68. package/src/helpers/recipients.js +24 -15
  69. package/src/helpers/successMessage.js +71 -21
  70. package/src/helpers/successMessage.spec.js +129 -69
  71. package/src/queries/queries.js +36 -5
  72. package/src/styles/autosuggest.styl +2 -1
  73. package/test/AppLike.jsx +6 -3
@@ -1,7 +1,16 @@
1
1
  import { render, screen } from '@testing-library/react'
2
2
  import React from 'react'
3
3
 
4
+ import { createMockClient } from 'cozy-client'
5
+ import flag from 'cozy-flags'
6
+
4
7
  import ShareRecipientsInput from './ShareRecipientsInput'
8
+ import AppLike from './SharingBanner/test/AppLike'
9
+
10
+ jest.mock('cozy-flags', () => ({
11
+ __esModule: true,
12
+ default: jest.fn()
13
+ }))
5
14
 
6
15
  jest.mock('./ShareAutosuggest', () => ({
7
16
  __esModule: true,
@@ -16,7 +25,11 @@ jest.mock('./ShareAutosuggest', () => ({
16
25
  </li>
17
26
  )
18
27
  } else {
19
- return <li key={contactOrGroup.id}>{contactOrGroup.name}</li>
28
+ return (
29
+ <li key={contactOrGroup.id}>
30
+ {contactOrGroup.name} - {contactOrGroup.members.length} members
31
+ </li>
32
+ )
20
33
  }
21
34
  })}
22
35
  </ul>
@@ -25,176 +38,158 @@ jest.mock('./ShareAutosuggest', () => ({
25
38
  }))
26
39
 
27
40
  describe('ShareRecipientsInput component', () => {
28
- const setup = ({ contacts, contactGroups }) => {
41
+ const setup = ({ contacts, contactGroups, unreachableContact }) => {
42
+ const mockClient = createMockClient({
43
+ queries: {
44
+ 'io.cozy.contacts/reachable': {
45
+ doctype: 'io.cozy.contacts',
46
+ data: contacts
47
+ },
48
+ 'io.cozy.contacts.groups/by-ids/fe86af20-c6c5': {
49
+ doctype: 'io.cozy.contacts.groups',
50
+ data: contactGroups
51
+ },
52
+ 'io.cozy.contacts/unreachable-with-groups': {
53
+ doctype: 'io.cozy.contacts',
54
+ data: unreachableContact
55
+ }
56
+ }
57
+ })
29
58
  render(
30
- <ShareRecipientsInput
31
- contacts={contacts}
32
- contactGroups={contactGroups}
33
- onPick={() => {}}
34
- onRemove={() => {}}
35
- placeholder="Enter recipients here"
36
- />
59
+ <AppLike client={mockClient}>
60
+ <ShareRecipientsInput
61
+ onPick={() => {}}
62
+ onRemove={() => {}}
63
+ placeholder="Enter recipients here"
64
+ />
65
+ </AppLike>
37
66
  )
38
67
  }
39
68
 
40
69
  it('should include groups and contacts when are loaded', async () => {
41
- const contacts = {
42
- id: 'contacts',
43
- fetchStatus: 'loaded',
44
- hasMore: false,
45
- data: [
46
- {
47
- id: 'df563cc4-6440',
48
- _id: 'df563cc4-6440',
49
- _type: 'io.cozy.contacts',
50
- name: {
51
- givenName: 'Michale',
52
- familyName: 'Russel'
53
- }
70
+ const contacts = [
71
+ {
72
+ id: 'df563cc4-6440',
73
+ _id: 'df563cc4-6440',
74
+ _type: 'io.cozy.contacts',
75
+ name: {
76
+ givenName: 'Michale',
77
+ familyName: 'Russel'
78
+ }
79
+ },
80
+ {
81
+ id: '5a3b4ccf-c257',
82
+ _id: '5a3b4ccf-c257',
83
+ _type: 'io.cozy.contacts',
84
+ name: {
85
+ givenName: 'Teagan',
86
+ familyName: 'Wolf'
54
87
  },
55
- {
56
- id: '5a3b4ccf-c257',
57
- _id: '5a3b4ccf-c257',
58
- _type: 'io.cozy.contacts',
59
- name: {
60
- givenName: 'Teagan',
61
- familyName: 'Wolf'
62
- },
63
- relationships: {
64
- groups: {
65
- data: [
66
- {
67
- _id: 'fe86af20-c6c5',
68
- _type: 'io.cozy.contacts.groups'
69
- }
70
- ]
71
- }
88
+ relationships: {
89
+ groups: {
90
+ data: [
91
+ {
92
+ _id: 'fe86af20-c6c5',
93
+ _type: 'io.cozy.contacts.groups'
94
+ }
95
+ ]
72
96
  }
73
97
  }
74
- ]
75
- }
76
- const contactGroups = {
77
- id: 'groups',
78
- fetchStatus: 'loaded',
79
- hasMore: false,
80
- data: [
81
- {
82
- id: 'fe86af20-c6c5',
83
- name: "The Night's Watch",
84
- _id: 'fe86af20-c6c5',
85
- _type: 'io.cozy.contacts.groups'
86
- }
87
- ]
88
- }
98
+ }
99
+ ]
100
+
101
+ const contactGroups = [
102
+ {
103
+ id: 'fe86af20-c6c5',
104
+ name: "The Night's Watch",
105
+ _id: 'fe86af20-c6c5',
106
+ _type: 'io.cozy.contacts.groups'
107
+ }
108
+ ]
89
109
 
90
110
  setup({ contacts, contactGroups })
91
111
 
92
112
  expect(screen.getByText('Michale Russel')).toBeInTheDocument()
93
113
  expect(screen.getByText('Teagan Wolf')).toBeInTheDocument()
94
- expect(screen.getByText("The Night's Watch")).toBeInTheDocument()
114
+ expect(
115
+ screen.getByText("The Night's Watch - 1 members")
116
+ ).toBeInTheDocument()
95
117
  })
96
118
 
97
- it('should include groups only if all contacts are loaded', async () => {
98
- const contacts = {
99
- id: 'contacts',
100
- fetchStatus: 'loading',
101
- hasMore: false,
102
- data: [
103
- {
104
- id: 'df563cc4-6440',
105
- _id: 'df563cc4-6440',
106
- _type: 'io.cozy.contacts',
107
- name: {
108
- givenName: 'Michale',
109
- familyName: 'Russel'
110
- }
119
+ it('should include unreachable members when flag sharing.show-recipient-groups is activated', async () => {
120
+ flag.mockReturnValue(true)
121
+
122
+ const contacts = [
123
+ {
124
+ id: 'df563cc4-6440',
125
+ _id: 'df563cc4-6440',
126
+ _type: 'io.cozy.contacts',
127
+ name: {
128
+ givenName: 'Michale',
129
+ familyName: 'Russel'
130
+ }
131
+ },
132
+ {
133
+ id: '5a3b4ccf-c257',
134
+ _id: '5a3b4ccf-c257',
135
+ _type: 'io.cozy.contacts',
136
+ name: {
137
+ givenName: 'Teagan',
138
+ familyName: 'Wolf'
111
139
  },
112
- {
113
- id: '5a3b4ccf-c257',
114
- _id: '5a3b4ccf-c257',
115
- _type: 'io.cozy.contacts',
116
- name: {
117
- givenName: 'Teagan',
118
- familyName: 'Wolf'
119
- },
120
- relationships: {
121
- groups: {
122
- data: [
123
- {
124
- _id: 'fe86af20-c6c5',
125
- _type: 'io.cozy.contacts.groups'
126
- }
127
- ]
128
- }
140
+ relationships: {
141
+ groups: {
142
+ data: [
143
+ {
144
+ _id: 'fe86af20-c6c5',
145
+ _type: 'io.cozy.contacts.groups'
146
+ }
147
+ ]
129
148
  }
130
149
  }
131
- ]
132
- }
133
- const contactGroups = {
134
- id: 'groups',
135
- fetchStatus: 'loaded',
136
- hasMore: false,
137
- data: [
138
- {
139
- id: 'fe86af20-c6c5',
140
- name: "The Night's Watch",
141
- _id: 'fe86af20-c6c5',
142
- _type: 'io.cozy.contacts.groups'
143
- }
144
- ]
145
- }
150
+ }
151
+ ]
146
152
 
147
- setup({ contacts, contactGroups })
153
+ const contactGroups = [
154
+ {
155
+ id: 'fe86af20-c6c5',
156
+ name: "The Night's Watch",
157
+ _id: 'fe86af20-c6c5',
158
+ _type: 'io.cozy.contacts.groups'
159
+ }
160
+ ]
148
161
 
149
- expect(screen.getByText('Michale Russel')).toBeInTheDocument()
150
- expect(screen.getByText('Teagan Wolf')).toBeInTheDocument()
151
- expect(screen.queryByText("The Night's Watch")).toBeNull()
152
- })
153
-
154
- it('should include groups only if they have more than one member', async () => {
155
- const contacts = {
156
- id: 'contacts',
157
- fetchStatus: 'loaded',
158
- hasMore: false,
159
- data: [
160
- {
161
- id: 'df563cc4-6440',
162
- _id: 'df563cc4-6440',
163
- _type: 'io.cozy.contacts',
164
- name: {
165
- givenName: 'Michale',
166
- familyName: 'Russel'
167
- }
162
+ const unreachableContact = [
163
+ {
164
+ id: 'df532cc4-6560',
165
+ _id: 'df532cc4-6560',
166
+ _type: 'io.cozy.contacts',
167
+ name: {
168
+ givenName: 'Tony',
169
+ familyName: 'Stark'
168
170
  },
169
- {
170
- id: '5a3b4ccf-c257',
171
- _id: '5a3b4ccf-c257',
172
- _type: 'io.cozy.contacts',
173
- name: {
174
- givenName: 'Teagan',
175
- familyName: 'Wolf'
171
+ relationships: {
172
+ groups: {
173
+ data: [
174
+ {
175
+ _id: 'fe86af20-c6c5',
176
+ _type: 'io.cozy.contacts.groups'
177
+ }
178
+ ]
176
179
  }
177
180
  }
178
- ]
179
- }
180
- const contactGroups = {
181
- id: 'groups',
182
- fetchStatus: 'loaded',
183
- hasMore: false,
184
- data: [
185
- {
186
- id: 'fe86af20-c6c5',
187
- name: "The Night's Watch",
188
- _id: 'fe86af20-c6c5',
189
- _type: 'io.cozy.contacts.groups'
190
- }
191
- ]
192
- }
181
+ }
182
+ ]
193
183
 
194
- setup({ contacts, contactGroups })
184
+ setup({ contacts, contactGroups, unreachableContact })
195
185
 
196
186
  expect(screen.getByText('Michale Russel')).toBeInTheDocument()
197
187
  expect(screen.getByText('Teagan Wolf')).toBeInTheDocument()
198
- expect(screen.queryByText("The Night's Watch")).toBeNull()
188
+ // 3 members because cozy-client replay the query with the new data
189
+ // so the unreachableContact is added to the reachable contact query
190
+ // TODO: improve createMockClient to handle this case
191
+ expect(
192
+ screen.getByText("The Night's Watch - 3 members")
193
+ ).toBeInTheDocument()
199
194
  })
200
195
  })
@@ -39,3 +39,11 @@ export const getOrCreateFromArray = async (client, contacts, createContact) => {
39
39
  })
40
40
  )
41
41
  }
42
+
43
+ export const getContactsFromGroupId = (contacts = [], groupId) => {
44
+ return contacts.filter(contact => {
45
+ return contact.relationships?.groups?.data?.some(
46
+ group => group._id === groupId
47
+ )
48
+ })
49
+ }
@@ -1,9 +1,9 @@
1
- import get from 'lodash/get'
2
-
3
1
  import flag from 'cozy-flags'
4
2
 
5
3
  import { Group } from '../models'
6
4
 
5
+ export const DEFAULT_DISPLAY_NAME = 'Share.contacts.defaultDisplayName'
6
+
7
7
  export const filterAndReworkRecipients = (recipients, previousRecipients) => {
8
8
  return recipients
9
9
  .filter(recipient => recipient.status !== 'owner')
@@ -52,26 +52,16 @@ export const hasReachRecipientsLimit = (current, next) => {
52
52
 
53
53
  /**
54
54
  * Merges the recipients list with a new recipient and their associated contacts.
55
+ * Only used when flag sharing.show-recipient-groups is not enabled.
55
56
  * @param {object[]} recipients - The list of recipients.
56
57
  * @param {object} recipient - The new recipient.
57
58
  * @param {object[]} contacts - The list of contacts.
58
59
  * @returns {object[]} - The merged recipients list.
59
60
  */
60
- export const spreadGroupAndMergeRecipients = (
61
- recipients,
62
- newRecipient,
63
- contacts
64
- ) => {
61
+ export const spreadGroupAndMergeRecipients = (recipients, newRecipient) => {
65
62
  let contactsToAdd
66
63
  if (newRecipient._type === Group.doctype) {
67
- const groupId = newRecipient.id
68
- contactsToAdd = contacts.data.filter(contact => {
69
- const contactGroupIds = get(contact, 'relationships.groups.data', []).map(
70
- group => group._id
71
- )
72
-
73
- return contactGroupIds.includes(groupId)
74
- })
64
+ contactsToAdd = newRecipient.members
75
65
  } else {
76
66
  contactsToAdd = [newRecipient]
77
67
  }
@@ -90,3 +80,22 @@ export const spreadGroupAndMergeRecipients = (
90
80
  export const mergeRecipients = (recipients, newRecipient) => {
91
81
  return [...recipients, newRecipient]
92
82
  }
83
+
84
+ export const getGroupRecipientSecondaryText = ({
85
+ nbMember,
86
+ nbMemberReady,
87
+ t,
88
+ isUserInsideMembers
89
+ }) => {
90
+ const memberCount =
91
+ nbMember === nbMemberReady ? nbMember : [nbMemberReady, nbMember].join('/')
92
+ const secondary = t('GroupRecipient.secondary', {
93
+ memberCount,
94
+ smart_count: nbMember
95
+ })
96
+
97
+ return (
98
+ secondary +
99
+ (isUserInsideMembers ? ` (${t('GroupRecipient.secondary_you')})` : '')
100
+ )
101
+ }
@@ -1,16 +1,40 @@
1
1
  import { models } from 'cozy-client'
2
2
 
3
- import { Contact } from '../models'
3
+ import { Contact, Group } from '../models'
4
+
4
5
  export const countNewRecipients = (currentRecipients, newRecipients) => {
5
- return newRecipients.filter(contact => {
6
- const email = models.contact.getPrimaryEmail(contact)
7
- const cozyUrl = models.contact.getPrimaryCozy(contact)
8
- return !currentRecipients.find(
9
- r =>
10
- (email && r.email && r.email === email) ||
11
- (cozyUrl && r.instance && r.instance === cozyUrl)
12
- )
13
- }).length
6
+ const newRecipientsNotAlreadyIncluded = newRecipients.filter(recipient => {
7
+ if (Contact.isContact(recipient)) {
8
+ const email = models.contact.getPrimaryEmail(recipient)
9
+ const cozyUrl = models.contact.getPrimaryCozy(recipient)
10
+ return !currentRecipients.find(
11
+ r =>
12
+ (email && r.email && r.email === email) ||
13
+ (cozyUrl && r.instance && r.instance === cozyUrl)
14
+ )
15
+ }
16
+ return true
17
+ })
18
+
19
+ return newRecipientsNotAlreadyIncluded.reduce(
20
+ (acc, recipient) => {
21
+ if (recipient._type === Group.doctype) {
22
+ return {
23
+ ...acc,
24
+ group: acc.group + 1
25
+ }
26
+ } else {
27
+ return {
28
+ ...acc,
29
+ contact: acc.contact + 1
30
+ }
31
+ }
32
+ },
33
+ {
34
+ group: 0,
35
+ contact: 0
36
+ }
37
+ )
14
38
  }
15
39
 
16
40
  export const getSuccessMessage = (
@@ -24,35 +48,61 @@ export const getSuccessMessage = (
24
48
  ? models.contact.getPrimaryEmail(recipient)
25
49
  : recipient.email
26
50
  const cozyUrl = models.contact.getPrimaryCozy(recipient)
51
+ const isContactGroup = recipient._type === Group.doctype
27
52
 
28
- if (email) {
53
+ if (isContactGroup) {
29
54
  return [
30
- `${documentType}.share.shareByEmail.success`,
55
+ `${documentType}.share.shareByEmail.singleGroupSuccess`,
56
+ {
57
+ groupName: recipient.name
58
+ }
59
+ ]
60
+ } else if (email) {
61
+ return [
62
+ `${documentType}.share.shareByEmail.singleContactSuccess`,
31
63
  {
32
64
  email
33
65
  }
34
66
  ]
35
67
  } else if (cozyUrl) {
36
68
  return [
37
- `${documentType}.share.shareByEmail.success`,
69
+ `${documentType}.share.shareByEmail.singleContactSuccess`,
38
70
  {
39
71
  email: cozyUrl
40
72
  }
41
73
  ]
42
74
  } else {
43
75
  return [
44
- `${documentType}.share.shareByEmail.genericSuccess`,
76
+ `${documentType}.share.shareByEmail.contactSuccess`,
45
77
  {
46
- count: 1
78
+ smart_count: 1
47
79
  }
48
80
  ]
49
81
  }
50
82
  } else {
51
- return [
52
- `${documentType}.share.shareByEmail.genericSuccess`,
53
- {
54
- count: countNewRecipients(recipientsBefore, recipientsAfter)
55
- }
56
- ]
83
+ const count = countNewRecipients(recipientsBefore, recipientsAfter)
84
+ if (count.contact > 0 && count.group > 0) {
85
+ return [
86
+ `${documentType}.share.shareByEmail.contactAndGroupSuccess`,
87
+ {
88
+ nbContact: count.contact,
89
+ nbGroup: count.group
90
+ }
91
+ ]
92
+ } else if (count.group > 0) {
93
+ return [
94
+ `${documentType}.share.shareByEmail.groupSuccess`,
95
+ {
96
+ smart_count: count.group
97
+ }
98
+ ]
99
+ } else {
100
+ return [
101
+ `${documentType}.share.shareByEmail.contactSuccess`,
102
+ {
103
+ smart_count: count.contact
104
+ }
105
+ ]
106
+ }
57
107
  }
58
108
  }