cozy-sharing 32.1.2 → 32.3.1

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 (59) hide show
  1. package/.claude/settings.local.json +27 -0
  2. package/AGENTS.md +7 -0
  3. package/CHANGELOG.md +18 -0
  4. package/dist/RefreshableSharings.spec.js +40 -0
  5. package/dist/assets/illustrations/illustration-shared-drives.svg +1 -0
  6. package/dist/components/EditLinkPermissionDialog.js +72 -0
  7. package/dist/components/EditLinkPermissionDialog.spec.js +48 -0
  8. package/dist/components/FederatedFolder/DumbFederatedFolderModal.js +65 -0
  9. package/dist/components/FederatedFolder/DumbFederatedFolderModal.spec.js +107 -0
  10. package/dist/components/FederatedFolder/FederatedFolderModal.js +41 -4
  11. package/dist/components/FederatedFolder/FederatedFolderModal.jsx.tmp.179197.1773939270471 +172 -0
  12. package/dist/components/FederatedFolder/FederatedFolderModal.spec.js +79 -8
  13. package/dist/components/Recipient/actions/revokeGroup.js +42 -0
  14. package/dist/components/Recipient/actions/revokeMember.js +42 -0
  15. package/dist/components/Recipient/actions/revokeSharedDriveMember.js +41 -0
  16. package/dist/components/ShareAutosuggest.js +1 -0
  17. package/dist/components/ShareAutosuggest.spec.js +12 -4
  18. package/dist/components/ShareDialogCozyToCozy.spec.js +55 -8
  19. package/dist/components/ShareDiscardChangesModal.js +35 -0
  20. package/dist/components/ShareModal/ShareModal copy.js +43 -0
  21. package/dist/components/ShareModal/ShareModal.spec.js +197 -0
  22. package/dist/components/ShareModal/ShareModalBatchContainer.js +276 -0
  23. package/dist/components/ShareModal/ShareModalBatchContainer.spec.js +131 -0
  24. package/dist/components/ShareModal/SharedDriveEditModal.js +59 -0
  25. package/dist/components/ShareRecipientsInput.js +13 -3
  26. package/dist/components/ShareRecipientsInput.spec.js +63 -4
  27. package/dist/components/SharedDrive/DumbSharedDriveModal.js +78 -0
  28. package/dist/components/SharedDrive/DumbSharedDriveModal.spec.js +126 -0
  29. package/dist/components/SharedDrive/SharedDriveEditModal.js +110 -0
  30. package/dist/components/SharedDrive/SharedDriveModal copy.js +356 -0
  31. package/dist/components/SharedDrive/helpers.js +102 -0
  32. package/dist/components/SharedDrive/helpers.spec.js +208 -0
  33. package/dist/components/SharedDriveEditModal.js +43 -0
  34. package/dist/components/SharedFolder/DumbBatchSharedFolderModal.spec.js +321 -0
  35. package/dist/components/SharedStatus.js +61 -0
  36. package/dist/components/SharedStatus.spec.js +44 -0
  37. package/dist/components/Sharesubmit.js +28 -0
  38. package/dist/components/__snapshots__/SharedStatus.spec.js.snap +94 -0
  39. package/dist/helpers/owner.js +14 -0
  40. package/dist/helpers/owner.spec.js +34 -0
  41. package/dist/hooks/useConfirmDiscardChanges.js +33 -0
  42. package/dist/hooks/useContactsAndGroups.js +65 -0
  43. package/dist/hooks/useSharedDrive.js +130 -0
  44. package/dist/hooks/useSharedDriveContactsAndGroups.js +65 -0
  45. package/dist/propTypes.js +26 -0
  46. package/dist/styles/shareddrive.styl +8 -0
  47. package/locales/en.json +10 -5
  48. package/locales/fr.json +10 -5
  49. package/locales/ru.json +10 -5
  50. package/locales/vi.json +10 -5
  51. package/package.json +2 -2
  52. package/src/components/FederatedFolder/FederatedFolderModal.jsx +184 -138
  53. package/src/components/FederatedFolder/FederatedFolderModal.spec.jsx +47 -0
  54. package/src/components/ShareAutosuggest.jsx +1 -0
  55. package/src/components/ShareAutosuggest.spec.jsx +10 -5
  56. package/src/components/ShareDialogCozyToCozy.spec.jsx +50 -4
  57. package/src/components/ShareDiscardChangesModal.jsx +40 -0
  58. package/src/components/ShareRecipientsInput.jsx +11 -6
  59. package/src/components/ShareRecipientsInput.spec.jsx +59 -0
@@ -1,8 +1,12 @@
1
1
  import PropTypes from 'prop-types'
2
- import React, { useEffect, useState } from 'react'
2
+ import React, { useCallback, useEffect, useMemo, useState } from 'react'
3
3
 
4
4
  import { useClient } from 'cozy-client'
5
+ import Button from 'cozy-ui/transpiled/react/Buttons'
5
6
  import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
7
+ import ConfirmDialogProvider, {
8
+ useConfirmDialog
9
+ } from 'cozy-ui/transpiled/react/providers/ConfirmDialog'
6
10
  import { useI18n } from 'twake-i18n'
7
11
 
8
12
  import { getOrCreateFromArray } from '../../helpers/contacts'
@@ -11,149 +15,191 @@ import { usePendingRecipients } from '../../hooks/usePendingRecipients'
11
15
  import { useSharingContext } from '../../hooks/useSharingContext'
12
16
  import { DumbBatchSharedFolderModal } from '../SharedFolder/DumbBatchSharedFolderModal'
13
17
 
14
- export const FederatedFolderModal = withLocales(
15
- ({
16
- onClose,
17
- document: existingDocument,
18
- autoOpenShareRestriction,
19
- showGenerateLinkButton
20
- }) => {
21
- const client = useClient()
22
- const { t } = useI18n()
23
- const {
24
- share,
25
- getSharingLink,
26
- getFederatedShareLink,
27
- getDocumentPermissions,
28
- getOwner,
29
- getSharingById,
30
- getRecipients,
31
- revoke
32
- } = useSharingContext()
33
- const { showAlert } = useAlert()
34
-
35
- const [sharingLink, setSharingLink] = useState(null)
36
- const {
37
- pendingRecipients,
38
- setPendingRecipients,
39
- selectedOption,
40
- setSelectedOption
41
- } = usePendingRecipients()
42
-
43
- // share from CozyProvider is wired to callbacks and realtime that
44
- // makes existingDocument and existingRecipients reactive to changes.
45
- // But we want it to be reactive only when we revoke a member. When we add member
46
- // we close the popup and we do not want to see briefly the new contacts added
47
- // in members before the modal closes. That's why when clicking on "Share"
48
- // we do not use the reactive existingDocument and existingRecipients.
49
- const [isSending, setIsSending] = useState(false)
50
- const [frozenDoc, setFrozenDoc] = useState(null)
51
- const [frozenRecipients, setFrozenRecipients] = useState(null)
52
-
53
- const documentPermissions = existingDocument
54
- ? getDocumentPermissions(existingDocument._id)
55
- : []
56
-
57
- useEffect(() => {
58
- const fetchSharingLink = async () => {
59
- if (!existingDocument) return
60
-
61
- if (existingDocument.driveId) {
62
- const link = await getFederatedShareLink(existingDocument)
63
- setSharingLink(link)
64
- } else {
65
- setSharingLink(getSharingLink(existingDocument._id))
66
- }
67
- }
18
+ const FederatedFolderModalContent = ({
19
+ onClose,
20
+ document: existingDocument,
21
+ autoOpenShareRestriction,
22
+ showGenerateLinkButton
23
+ }) => {
24
+ const client = useClient()
25
+ const { t } = useI18n()
26
+ const {
27
+ share,
28
+ getSharingLink,
29
+ getFederatedShareLink,
30
+ getDocumentPermissions,
31
+ getOwner,
32
+ getSharingById,
33
+ getRecipients,
34
+ revoke
35
+ } = useSharingContext()
36
+ const { showAlert } = useAlert()
37
+ const { showConfirmDialog, closeConfirmDialog } = useConfirmDialog()
38
+
39
+ const [sharingLink, setSharingLink] = useState(null)
40
+ const {
41
+ pendingRecipients,
42
+ setPendingRecipients,
43
+ selectedOption,
44
+ setSelectedOption
45
+ } = usePendingRecipients()
46
+
47
+ // share from CozyProvider is wired to callbacks and realtime that
48
+ // makes existingDocument and existingRecipients reactive to changes.
49
+ // But we want it to be reactive only when we revoke a member. When we add member
50
+ // we close the popup and we do not want to see briefly the new contacts added
51
+ // in members before the modal closes. That's why when clicking on "Share"
52
+ // we do not use the reactive existingDocument and existingRecipients.
53
+ const [isSending, setIsSending] = useState(false)
54
+ const [frozenDoc, setFrozenDoc] = useState(null)
55
+ const [frozenRecipients, setFrozenRecipients] = useState(null)
56
+
57
+ const handleCloseRequest = useCallback(() => {
58
+ if (pendingRecipients.length === 0) {
59
+ onClose()
60
+ return
61
+ }
68
62
 
69
- fetchSharingLink()
70
- }, [
71
- existingDocument,
72
- documentPermissions,
73
- getFederatedShareLink,
74
- getSharingLink,
75
- getOwner,
76
- getSharingById
77
- ])
78
-
79
- const folderName = existingDocument?.name || ''
80
-
81
- const onSend = async () => {
82
- if (pendingRecipients.length === 0) {
83
- onClose()
84
- return
63
+ showConfirmDialog({
64
+ title: t('ShareDiscardChangesModal.title'),
65
+ actions: (
66
+ <>
67
+ <Button
68
+ variant="secondary"
69
+ label={t('ShareDiscardChangesModal.cancel')}
70
+ className="u-fz-small"
71
+ onClick={closeConfirmDialog}
72
+ />
73
+ <Button
74
+ variant="primary"
75
+ label={t('ShareDiscardChangesModal.discard')}
76
+ className="u-fz-small"
77
+ onClick={() => {
78
+ closeConfirmDialog()
79
+ onClose()
80
+ }}
81
+ />
82
+ </>
83
+ )
84
+ })
85
+ }, [
86
+ closeConfirmDialog,
87
+ onClose,
88
+ pendingRecipients.length,
89
+ showConfirmDialog,
90
+ t
91
+ ])
92
+
93
+ const documentPermissions = useMemo(
94
+ () =>
95
+ existingDocument ? getDocumentPermissions(existingDocument._id) : [],
96
+ [existingDocument, getDocumentPermissions]
97
+ )
98
+
99
+ useEffect(() => {
100
+ const fetchSharingLink = async () => {
101
+ if (!existingDocument) return
102
+
103
+ if (existingDocument.driveId) {
104
+ const link = await getFederatedShareLink(existingDocument)
105
+ setSharingLink(link)
106
+ } else {
107
+ setSharingLink(getSharingLink(existingDocument._id))
85
108
  }
109
+ }
86
110
 
87
- setIsSending(true)
88
- setFrozenDoc(existingDocument)
89
- setFrozenRecipients(existingRecipients)
90
-
91
- try {
92
- const contacts = await getOrCreateFromArray(
93
- client,
94
- pendingRecipients,
95
- contact => client.create('io.cozy.contacts', contact)
96
- )
97
- const readWriteRecipients =
98
- selectedOption === 'readOnly' ? [] : contacts
99
- const readOnlyRecipients = selectedOption === 'readOnly' ? contacts : []
100
-
101
- await share({
102
- description: folderName,
103
- document: existingDocument,
104
- recipients: readWriteRecipients,
105
- readOnlyRecipients,
106
- sharedDrive: true,
107
- openSharing: false
108
- })
109
-
110
- showAlert({
111
- message: t('FederatedFolder.successNotification'),
112
- severity: 'success',
113
- variant: 'filled'
114
- })
115
-
116
- onClose()
117
- } catch (_err) {
118
- showAlert({
119
- message: t('FederatedFolder.errorNotification'),
120
- severity: 'error',
121
- variant: 'filled'
122
- })
123
- } finally {
124
- setIsSending(false)
125
- }
111
+ fetchSharingLink()
112
+ }, [
113
+ existingDocument,
114
+ documentPermissions,
115
+ getFederatedShareLink,
116
+ getSharingLink,
117
+ getOwner,
118
+ getSharingById
119
+ ])
120
+
121
+ const folderName = existingDocument?.name || ''
122
+
123
+ const onSend = async () => {
124
+ if (pendingRecipients.length === 0) {
125
+ onClose()
126
+ return
126
127
  }
127
128
 
128
- const existingRecipients = existingDocument
129
- ? getRecipients(existingDocument._id)
130
- : []
131
-
132
- const modalTitle = t('FederatedFolder.shareTitle', { name: folderName })
133
- const isSharedDrive = Boolean(existingDocument?.driveId)
134
-
135
- return (
136
- <DumbBatchSharedFolderModal
137
- title={modalTitle}
138
- document={isSending ? frozenDoc : existingDocument}
139
- recipients={isSending ? frozenRecipients : existingRecipients}
140
- currentRecipients={isSending ? frozenRecipients : existingRecipients}
141
- onRevoke={revoke}
142
- onSend={onSend}
143
- onClose={onClose}
144
- sharingLink={sharingLink}
145
- shareLabel={t('FederatedFolder.share')}
146
- showShareByEmail={!isSharedDrive}
147
- autoOpenShareRestriction={autoOpenShareRestriction}
148
- showGenerateLinkButton={showGenerateLinkButton}
149
- pendingRecipients={pendingRecipients}
150
- onPendingRecipientsChange={setPendingRecipients}
151
- selectedOption={selectedOption}
152
- onSelectedOptionChange={setSelectedOption}
153
- />
154
- )
129
+ setIsSending(true)
130
+ setFrozenDoc(existingDocument)
131
+ setFrozenRecipients(existingRecipients)
132
+
133
+ try {
134
+ const contacts = await getOrCreateFromArray(
135
+ client,
136
+ pendingRecipients,
137
+ contact => client.create('io.cozy.contacts', contact)
138
+ )
139
+ const readWriteRecipients = selectedOption === 'readOnly' ? [] : contacts
140
+ const readOnlyRecipients = selectedOption === 'readOnly' ? contacts : []
141
+
142
+ await share({
143
+ description: folderName,
144
+ document: existingDocument,
145
+ recipients: readWriteRecipients,
146
+ readOnlyRecipients,
147
+ sharedDrive: true,
148
+ openSharing: false
149
+ })
150
+
151
+ showAlert({
152
+ message: t('FederatedFolder.successNotification'),
153
+ severity: 'success',
154
+ variant: 'filled'
155
+ })
156
+
157
+ onClose()
158
+ } catch (_err) {
159
+ showAlert({
160
+ message: t('FederatedFolder.errorNotification'),
161
+ severity: 'error',
162
+ variant: 'filled'
163
+ })
164
+ } finally {
165
+ setIsSending(false)
166
+ }
155
167
  }
156
- )
168
+
169
+ const existingRecipients = existingDocument
170
+ ? getRecipients(existingDocument._id)
171
+ : []
172
+
173
+ const modalTitle = t('FederatedFolder.shareTitle', { name: folderName })
174
+ const isSharedDrive = Boolean(existingDocument?.driveId)
175
+
176
+ return (
177
+ <DumbBatchSharedFolderModal
178
+ title={modalTitle}
179
+ document={isSending ? frozenDoc : existingDocument}
180
+ recipients={isSending ? frozenRecipients : existingRecipients}
181
+ currentRecipients={isSending ? frozenRecipients : existingRecipients}
182
+ onRevoke={revoke}
183
+ onSend={onSend}
184
+ onClose={handleCloseRequest}
185
+ sharingLink={sharingLink}
186
+ shareLabel={t('FederatedFolder.share')}
187
+ showShareByEmail={!isSharedDrive}
188
+ autoOpenShareRestriction={autoOpenShareRestriction}
189
+ showGenerateLinkButton={showGenerateLinkButton}
190
+ pendingRecipients={pendingRecipients}
191
+ onPendingRecipientsChange={setPendingRecipients}
192
+ selectedOption={selectedOption}
193
+ onSelectedOptionChange={setSelectedOption}
194
+ />
195
+ )
196
+ }
197
+
198
+ export const FederatedFolderModal = withLocales(props => (
199
+ <ConfirmDialogProvider>
200
+ <FederatedFolderModalContent {...props} />
201
+ </ConfirmDialogProvider>
202
+ ))
157
203
 
158
204
  FederatedFolderModal.propTypes = {
159
205
  onClose: PropTypes.func.isRequired,
@@ -320,6 +320,53 @@ describe('FederatedFolderModal', () => {
320
320
 
321
321
  expect(mockOnClose).toHaveBeenCalled()
322
322
  })
323
+
324
+ it('should ask confirmation when close button is clicked with pending recipients', async () => {
325
+ usePendingRecipients.mockReturnValue({
326
+ pendingRecipients: [{ name: 'Alice', email: 'alice@example.com' }],
327
+ setPendingRecipients: jest.fn(),
328
+ selectedOption: 'readWrite',
329
+ setSelectedOption: jest.fn()
330
+ })
331
+
332
+ const { getByRole, getByTestId, getByText } = setup()
333
+
334
+ await waitFor(() => {
335
+ expect(getByTestId('btn-close')).toBeTruthy()
336
+ })
337
+
338
+ fireEvent.click(getByTestId('btn-close'))
339
+
340
+ expect(getByText("Discard the changes you haven't saved?")).toBeTruthy()
341
+ expect(mockOnClose).not.toHaveBeenCalled()
342
+
343
+ fireEvent.click(getByRole('button', { name: 'Cancel' }))
344
+
345
+ expect(mockOnClose).not.toHaveBeenCalled()
346
+ })
347
+
348
+ it('should close after discard confirmation', async () => {
349
+ usePendingRecipients.mockReturnValue({
350
+ pendingRecipients: [{ name: 'Alice', email: 'alice@example.com' }],
351
+ setPendingRecipients: jest.fn(),
352
+ selectedOption: 'readWrite',
353
+ setSelectedOption: jest.fn()
354
+ })
355
+
356
+ const { getByRole, getByTestId, getByText } = setup()
357
+
358
+ await waitFor(() => {
359
+ expect(getByTestId('btn-close')).toBeTruthy()
360
+ })
361
+
362
+ fireEvent.click(getByTestId('btn-close'))
363
+
364
+ expect(getByText("Discard the changes you haven't saved?")).toBeTruthy()
365
+
366
+ fireEvent.click(getByRole('button', { name: 'Discard' }))
367
+
368
+ expect(mockOnClose).toHaveBeenCalledTimes(1)
369
+ })
323
370
  })
324
371
 
325
372
  describe('existing recipients', () => {
@@ -205,6 +205,7 @@ const ShareAutocomplete = ({
205
205
  renderInputComponent={renderInput}
206
206
  highlightFirstSuggestion
207
207
  inputProps={{
208
+ autoFocus: true,
208
209
  onFocus: onAutosuggestFocus,
209
210
  onChange: onAutosuggestChange,
210
211
  onPaste: onAutosuggestPaste,
@@ -51,17 +51,22 @@ describe('ShareAutosuggest', () => {
51
51
  })
52
52
  })
53
53
 
54
- it('should show loading only after user focus input', () => {
54
+ it('should show loading indicator when loading prop is true (autoFocus triggers onFocus on mount)', () => {
55
55
  const onPick = jest.fn()
56
56
  const onRemove = jest.fn()
57
57
 
58
58
  setup({ onPick, onRemove, loading: true })
59
59
 
60
- const inputNode = screen.getByPlaceholderText('myPlaceHolder')
60
+ // autoFocus triggers onFocus on mount, which sets isLoadingDisplayed
61
+ expect(screen.getByText('loading')).toBeInTheDocument()
62
+ })
61
63
 
62
- expect(screen.queryByText('loading')).toBeNull()
63
- fireEvent.focus(inputNode)
64
+ it('should not show loading indicator when loading prop is false', () => {
65
+ const onPick = jest.fn()
66
+ const onRemove = jest.fn()
64
67
 
65
- expect(screen.getByText('loading')).toBeInTheDocument()
68
+ setup({ onPick, onRemove, loading: false })
69
+
70
+ expect(screen.queryByText('loading')).toBeNull()
66
71
  })
67
72
  })
@@ -169,20 +169,66 @@ describe('ShareDialogCozyToCozy', () => {
169
169
  ).not.toBeInTheDocument()
170
170
  })
171
171
 
172
- it('main Share button calls onClose when there are no pending chips', () => {
172
+ it('main Done button calls onClose when there are no pending chips', () => {
173
173
  const onClose = jest.fn()
174
174
  const onShare = jest.fn()
175
175
  const props = { ...getMockProps(), onClose, onShare }
176
176
 
177
177
  const { getByRole } = setup(props)
178
178
 
179
- fireEvent.click(getByRole('button', { name: 'Share' }))
179
+ fireEvent.click(getByRole('button', { name: 'Done' }))
180
180
 
181
181
  expect(onShare).not.toHaveBeenCalled()
182
182
  expect(onClose).toHaveBeenCalledTimes(1)
183
183
  })
184
184
 
185
- it('main Share button calls onShare with chips then closes', async () => {
185
+ it('close action closes dialog when there are no pending chips', () => {
186
+ const onClose = jest.fn()
187
+ const props = { ...getMockProps(), onClose }
188
+
189
+ const { getByRole } = setup(props)
190
+
191
+ fireEvent.click(getByRole('button', { name: 'Close' }))
192
+
193
+ expect(onClose).toHaveBeenCalledTimes(1)
194
+ })
195
+
196
+ it('close action closes dialog when there are pending chips', () => {
197
+ const onClose = jest.fn()
198
+ const props = { ...getMockProps(), onClose }
199
+
200
+ const { getByPlaceholderText, getByRole, queryByText } = setup(props)
201
+
202
+ act(() => {
203
+ fireEvent.change(
204
+ getByPlaceholderText(
205
+ 'Enter the email address or name of the recipient'
206
+ ),
207
+ {
208
+ target: { value: 'new@cozycloud.cc' }
209
+ }
210
+ )
211
+ })
212
+ act(() => {
213
+ fireEvent.keyPress(
214
+ getByPlaceholderText(
215
+ 'Enter the email address or name of the recipient'
216
+ ),
217
+ {
218
+ key: 'Enter',
219
+ code: 'Enter',
220
+ charCode: 13
221
+ }
222
+ )
223
+ })
224
+
225
+ fireEvent.click(getByRole('button', { name: 'Close' }))
226
+
227
+ expect(queryByText("Discard the changes you haven't saved?")).toBe(null)
228
+ expect(onClose).toHaveBeenCalledTimes(1)
229
+ })
230
+
231
+ it('main Done button calls onShare with chips then closes', async () => {
186
232
  const onClose = jest.fn()
187
233
  const onShare = jest.fn().mockResolvedValue(undefined)
188
234
  const props = { ...getMockProps(), onClose, onShare }
@@ -213,7 +259,7 @@ describe('ShareDialogCozyToCozy', () => {
213
259
  })
214
260
 
215
261
  await act(async () => {
216
- fireEvent.click(getByRole('button', { name: 'Share' }))
262
+ fireEvent.click(getByRole('button', { name: 'Done' }))
217
263
  })
218
264
 
219
265
  expect(onShare).toHaveBeenCalledWith(
@@ -0,0 +1,40 @@
1
+ import PropTypes from 'prop-types'
2
+ import React from 'react'
3
+
4
+ import Button from 'cozy-ui/transpiled/react/Buttons'
5
+ import { ConfirmDialog } from 'cozy-ui/transpiled/react/CozyDialogs'
6
+ import { useI18n } from 'twake-i18n'
7
+
8
+ const ShareDiscardChangesModal = ({ onCancel, onConfirm }) => {
9
+ const { t } = useI18n()
10
+
11
+ return (
12
+ <ConfirmDialog
13
+ open
14
+ title={t('ShareDiscardChangesModal.title')}
15
+ actions={
16
+ <>
17
+ <Button
18
+ variant="secondary"
19
+ label={t('ShareDiscardChangesModal.cancel')}
20
+ className="u-fz-small"
21
+ onClick={onCancel}
22
+ />
23
+ <Button
24
+ variant="primary"
25
+ label={t('ShareDiscardChangesModal.discard')}
26
+ className="u-fz-small"
27
+ onClick={onConfirm}
28
+ />
29
+ </>
30
+ }
31
+ />
32
+ )
33
+ }
34
+
35
+ ShareDiscardChangesModal.propTypes = {
36
+ onCancel: PropTypes.func.isRequired,
37
+ onConfirm: PropTypes.func.isRequired
38
+ }
39
+
40
+ export { ShareDiscardChangesModal }
@@ -17,6 +17,8 @@ import {
17
17
  * We retrieve all the contacts that are reachable and the groups they belong to.
18
18
  * In order to display the total number of members in each group, we also retrieve the contacts that are not reachable.
19
19
  */
20
+ const isCurrentUser = contact => contact.me === true
21
+
20
22
  const ShareRecipientsInput = ({
21
23
  currentRecipients,
22
24
  recipients,
@@ -57,6 +59,12 @@ const ShareRecipientsInput = ({
57
59
  (isQueryLoading(unreachableContactsWithGroupsResult) ||
58
60
  unreachableContactsWithGroupsResult.hasMore))
59
61
 
62
+ const reachableContacts = (reachableContactsResult.data || []).filter(
63
+ contact => !isCurrentUser(contact)
64
+ )
65
+ const unreachableContactsWithGroups = (
66
+ unreachableContactsWithGroupsResult.data || []
67
+ ).filter(contact => !isCurrentUser(contact))
60
68
  const currentRecipientGroups = currentRecipients
61
69
  .map(({ id }) => id)
62
70
  .filter(Boolean)
@@ -68,7 +76,7 @@ const ShareRecipientsInput = ({
68
76
  )
69
77
  .map(contactGroup => {
70
78
  const reachableMembers = getContactsFromGroupId(
71
- reachableContactsResult.data,
79
+ reachableContacts,
72
80
  contactGroup.id
73
81
  ).map(contact => ({
74
82
  ...contact,
@@ -83,7 +91,7 @@ const ShareRecipientsInput = ({
83
91
  }
84
92
 
85
93
  const unreachableMembers = getContactsFromGroupId(
86
- unreachableContactsWithGroupsResult.data,
94
+ unreachableContactsWithGroups,
87
95
  contactGroup.id
88
96
  ).map(contact => ({
89
97
  ...contact,
@@ -96,10 +104,7 @@ const ShareRecipientsInput = ({
96
104
  }
97
105
  })
98
106
 
99
- const contactsAndGroups = [
100
- ...(reachableContactsResult.data || []),
101
- ...contactGroups
102
- ]
107
+ const contactsAndGroups = [...reachableContacts, ...contactGroups]
103
108
 
104
109
  return (
105
110
  <ShareAutosuggest
@@ -116,6 +116,65 @@ describe('ShareRecipientsInput component', () => {
116
116
  ).toBeInTheDocument()
117
117
  })
118
118
 
119
+ it('should exclude current user from suggestions when me is true', async () => {
120
+ const contacts = [
121
+ {
122
+ id: 'df563cc4-6440',
123
+ _id: 'df563cc4-6440',
124
+ _type: 'io.cozy.contacts',
125
+ me: true,
126
+ name: {
127
+ givenName: 'My',
128
+ familyName: 'Self'
129
+ },
130
+ relationships: {
131
+ groups: {
132
+ data: [
133
+ {
134
+ _id: 'group-id',
135
+ _type: 'io.cozy.contacts.groups'
136
+ }
137
+ ]
138
+ }
139
+ }
140
+ },
141
+ {
142
+ id: '5a3b4ccf-c257',
143
+ _id: '5a3b4ccf-c257',
144
+ _type: 'io.cozy.contacts',
145
+ name: {
146
+ givenName: 'Teagan',
147
+ familyName: 'Wolf'
148
+ },
149
+ relationships: {
150
+ groups: {
151
+ data: [
152
+ {
153
+ _id: 'group-id',
154
+ _type: 'io.cozy.contacts.groups'
155
+ }
156
+ ]
157
+ }
158
+ }
159
+ }
160
+ ]
161
+
162
+ const contactGroups = [
163
+ {
164
+ id: 'group-id',
165
+ name: 'Friends',
166
+ _id: 'group-id',
167
+ _type: 'io.cozy.contacts.groups'
168
+ }
169
+ ]
170
+
171
+ setup({ contacts, contactGroups })
172
+
173
+ expect(screen.queryByText('My Self')).not.toBeInTheDocument()
174
+ expect(screen.getByText('Teagan Wolf')).toBeInTheDocument()
175
+ expect(screen.getByText('Friends - 1 members')).toBeInTheDocument()
176
+ })
177
+
119
178
  it('should include unreachable members when flag sharing.show-recipient-groups is activated', async () => {
120
179
  flag.mockReturnValue(true)
121
180