cozy-sharing 31.1.0 → 31.2.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 (57) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/components/FederatedFolder/DumbFederatedFolderModal.js +15 -16
  3. package/dist/components/FederatedFolder/FederatedFolderModal.js +78 -103
  4. package/dist/components/FederatedFolder/FederatedFolderModal.spec.js +102 -292
  5. package/dist/components/Recipient/RecipientConfirm.js +0 -5
  6. package/dist/components/ShareAutosuggest.js +9 -4
  7. package/dist/components/ShareByEmail.js +29 -274
  8. package/dist/components/ShareByEmail.spec.js +52 -230
  9. package/dist/components/ShareByLink.js +1 -3
  10. package/dist/components/ShareDialogCozyToCozy.js +169 -35
  11. package/dist/components/ShareDialogCozyToCozy.spec.js +122 -35
  12. package/dist/components/ShareDialogTwoStepsConfirmationContainer.js +13 -3
  13. package/dist/components/ShareModal/SharingDetailsModal.js +0 -5
  14. package/dist/components/ShareRecipientsInput.js +6 -3
  15. package/dist/components/SharedDrive/SharedDriveForm.js +11 -23
  16. package/dist/components/SharedDrive/SharedDriveModal.js +1 -1
  17. package/dist/components/SharedDrive/useSharedDrive.js +33 -57
  18. package/dist/components/SharedDrive/useSharedDrive.spec.js +29 -93
  19. package/dist/components/SharedFolder/DumbBatchSharedFolderModal.js +15 -26
  20. package/dist/components/Sharetypeselect.js +85 -90
  21. package/dist/hooks/usePendingRecipients.js +20 -0
  22. package/dist/hooks/usePendingRecipients.spec.js +30 -0
  23. package/dist/styles/autosuggest.styl +8 -2
  24. package/dist/styles/share.styl +0 -39
  25. package/dist/stylesheet.css +8 -36
  26. package/locales/en.json +5 -9
  27. package/locales/fr.json +5 -9
  28. package/locales/ru.json +5 -9
  29. package/locales/vi.json +5 -9
  30. package/package.json +2 -2
  31. package/src/components/FederatedFolder/DumbFederatedFolderModal.jsx +14 -15
  32. package/src/components/FederatedFolder/FederatedFolderModal.jsx +50 -67
  33. package/src/components/FederatedFolder/FederatedFolderModal.spec.jsx +63 -186
  34. package/src/components/ShareAutosuggest.jsx +7 -2
  35. package/src/components/ShareByEmail.jsx +35 -186
  36. package/src/components/ShareByEmail.spec.jsx +22 -173
  37. package/src/components/ShareByLink.jsx +1 -7
  38. package/src/components/ShareDialogCozyToCozy.jsx +171 -38
  39. package/src/components/ShareDialogCozyToCozy.spec.jsx +66 -0
  40. package/src/components/ShareDialogTwoStepsConfirmationContainer.jsx +11 -1
  41. package/src/components/ShareRecipientsInput.jsx +5 -2
  42. package/src/components/SharedDrive/SharedDriveForm.jsx +10 -23
  43. package/src/components/SharedDrive/SharedDriveModal.jsx +1 -1
  44. package/src/components/SharedDrive/useSharedDrive.js +24 -50
  45. package/src/components/SharedDrive/useSharedDrive.spec.js +19 -67
  46. package/src/components/SharedFolder/DumbBatchSharedFolderModal.jsx +15 -29
  47. package/src/components/Sharetypeselect.jsx +75 -58
  48. package/src/hooks/usePendingRecipients.js +13 -0
  49. package/src/hooks/usePendingRecipients.spec.js +21 -0
  50. package/src/styles/autosuggest.styl +8 -2
  51. package/src/styles/share.styl +0 -39
  52. package/dist/components/SharedDrive/helpers.js +0 -102
  53. package/dist/components/SharedDrive/helpers.spec.js +0 -227
  54. package/dist/components/Sharesubmit.js +0 -28
  55. package/src/components/SharedDrive/helpers.js +0 -108
  56. package/src/components/SharedDrive/helpers.spec.js +0 -177
  57. package/src/components/Sharesubmit.jsx +0 -31
@@ -4,6 +4,8 @@ import React from 'react'
4
4
  import { createMockClient } from 'cozy-client'
5
5
 
6
6
  import { FederatedFolderModal } from './FederatedFolderModal'
7
+ import { getOrCreateFromArray } from '../../helpers/contacts'
8
+ import { usePendingRecipients } from '../../hooks/usePendingRecipients'
7
9
  import AppLike from '../SharingBanner/test/AppLike'
8
10
 
9
11
  const mockShare = jest.fn()
@@ -31,87 +33,45 @@ jest.mock('cozy-ui/transpiled/react/providers/Alert', () => ({
31
33
 
32
34
  jest.mock('../../hoc/withLocales', () => Component => Component)
33
35
 
36
+ jest.mock('../../helpers/contacts', () => ({
37
+ getOrCreateFromArray: jest.fn()
38
+ }))
39
+
40
+ jest.mock('../../hooks/usePendingRecipients', () => ({
41
+ usePendingRecipients: jest.fn()
42
+ }))
43
+
34
44
  jest.mock('./DumbFederatedFolderModal', () => ({
35
45
  DumbFederatedFolderModal: ({
36
46
  title,
37
- createContact,
38
47
  recipients,
39
- readOnlyRecipients,
40
48
  onRevoke,
41
- onSetType,
42
49
  onSend,
43
50
  onClose,
44
- onShare,
45
- sharingLink
51
+ sharingLink,
52
+ pendingRecipients,
53
+ selectedOption
46
54
  }) => (
47
55
  <div data-testid="dumb-modal">
48
56
  <span data-testid="title">{title}</span>
49
57
  <span data-testid="sharing-link">{sharingLink}</span>
50
58
  <span data-testid="recipients-count">{recipients.length}</span>
51
- <span data-testid="readOnly-recipients-count">
52
- {readOnlyRecipients ? readOnlyRecipients.length : 0}
59
+ <span data-testid="pending-recipients-count">
60
+ {pendingRecipients.length}
53
61
  </span>
54
- <button
55
- data-testid="btn-share"
56
- onClick={() =>
57
- onShare({
58
- recipients: [{ _id: 'r1', id: 'r1', displayName: 'Alice' }],
59
- readOnlyRecipients: [{ _id: 'r2', id: 'r2', displayName: 'Bob' }]
60
- })
61
- }
62
- >
63
- Share
64
- </button>
65
- <button
66
- data-testid="btn-share-other"
67
- onClick={() =>
68
- onShare({
69
- recipients: [
70
- { _id: 'r1', id: 'r1', displayName: 'Alice' },
71
- { _id: 'r3', id: 'r3', displayName: 'Charlie' }
72
- ],
73
- readOnlyRecipients: []
74
- })
75
- }
76
- >
77
- Share Other
78
- </button>
62
+ <span data-testid="selected-option">{selectedOption}</span>
79
63
  <button data-testid="btn-send" onClick={onSend}>
80
64
  Send
81
65
  </button>
82
- <button
83
- data-testid="btn-set-type-two-way"
84
- onClick={() => onSetType('virtual-shared-drive-sharing-r2', 'two-way')}
85
- >
86
- Set Two-Way
87
- </button>
88
- <button
89
- data-testid="btn-set-type-one-way"
90
- onClick={() => onSetType('virtual-shared-drive-sharing-r1', 'one-way')}
91
- >
92
- Set One-Way
93
- </button>
94
66
  <button
95
67
  data-testid="btn-revoke"
96
- onClick={() => onRevoke('virtual-shared-drive-sharing-r1')}
97
- >
98
- Revoke
99
- </button>
100
- <button
101
- data-testid="btn-revoke-existing"
102
68
  onClick={() => onRevoke({ _id: 'folder-123' }, 'abc', 1)}
103
69
  >
104
- Revoke Existing
70
+ Revoke
105
71
  </button>
106
72
  <button data-testid="btn-close" onClick={onClose}>
107
73
  Close
108
74
  </button>
109
- <button
110
- data-testid="btn-create-contact"
111
- onClick={() => createContact({ email: 'test@example.com' })}
112
- >
113
- Create Contact
114
- </button>
115
75
  </div>
116
76
  )
117
77
  }))
@@ -162,6 +122,13 @@ describe('FederatedFolderModal', () => {
162
122
  mockGetRecipients.mockReturnValue([])
163
123
  client = createTestClient()
164
124
  sharingContextValue = createSharingContextValue()
125
+ usePendingRecipients.mockReturnValue({
126
+ pendingRecipients: [],
127
+ setPendingRecipients: jest.fn(),
128
+ selectedOption: 'readWrite',
129
+ setSelectedOption: jest.fn()
130
+ })
131
+ getOrCreateFromArray.mockResolvedValue([])
165
132
  })
166
133
 
167
134
  const setup = (props = {}) => {
@@ -211,46 +178,43 @@ describe('FederatedFolderModal', () => {
211
178
  expect(getByTestId('sharing-link').textContent).toBe('')
212
179
  })
213
180
  })
214
- })
215
181
 
216
- describe('onShare callback', () => {
217
- it('should update recipients when onShare is called', async () => {
182
+ it('should pass selectedOption from usePendingRecipients', async () => {
218
183
  const { getByTestId } = setup()
219
184
 
220
185
  await waitFor(() => {
221
- expect(getByTestId('recipients-count').textContent).toBe('0')
222
- })
223
-
224
- fireEvent.click(getByTestId('btn-share'))
225
-
226
- await waitFor(() => {
227
- expect(getByTestId('recipients-count').textContent).toBe('2')
186
+ expect(getByTestId('selected-option').textContent).toBe('readWrite')
228
187
  })
229
188
  })
189
+ })
230
190
 
231
- it('should accumulate recipients when onShare is called multiple times', async () => {
232
- const { getByTestId } = setup()
233
-
234
- await waitFor(() => {
235
- expect(getByTestId('recipients-count').textContent).toBe('0')
236
- })
191
+ describe('onSend callback', () => {
192
+ const pendingRecipient = { name: 'Alice', email: 'alice@example.com' }
237
193
 
238
- fireEvent.click(getByTestId('btn-share'))
194
+ it('should call onClose and skip share when there are no pending recipients', async () => {
195
+ // pendingRecipients defaults to [] via beforeEach
196
+ const { getByTestId } = setup()
239
197
 
240
198
  await waitFor(() => {
241
- expect(getByTestId('recipients-count').textContent).toBe('2')
199
+ expect(getByTestId('btn-send')).toBeTruthy()
242
200
  })
243
201
 
244
- fireEvent.click(getByTestId('btn-share-other'))
202
+ fireEvent.click(getByTestId('btn-send'))
245
203
 
246
204
  await waitFor(() => {
247
- expect(getByTestId('recipients-count').textContent).toBe('3')
205
+ expect(mockOnClose).toHaveBeenCalled()
206
+ expect(mockShare).not.toHaveBeenCalled()
248
207
  })
249
208
  })
250
- })
251
209
 
252
- describe('onSend callback', () => {
253
210
  it('should call share with correct parameters', async () => {
211
+ usePendingRecipients.mockReturnValue({
212
+ pendingRecipients: [pendingRecipient],
213
+ setPendingRecipients: jest.fn(),
214
+ selectedOption: 'readWrite',
215
+ setSelectedOption: jest.fn()
216
+ })
217
+ getOrCreateFromArray.mockResolvedValue([pendingRecipient])
254
218
  mockShare.mockResolvedValueOnce({})
255
219
  const { getByTestId } = setup()
256
220
 
@@ -258,15 +222,14 @@ describe('FederatedFolderModal', () => {
258
222
  expect(getByTestId('btn-send')).toBeTruthy()
259
223
  })
260
224
 
261
- fireEvent.click(getByTestId('btn-share'))
262
225
  fireEvent.click(getByTestId('btn-send'))
263
226
 
264
227
  await waitFor(() => {
265
228
  expect(mockShare).toHaveBeenCalledWith({
266
229
  description: 'My Test Folder',
267
230
  document: mockDocument,
268
- recipients: [{ _id: 'r1', id: 'r1', displayName: 'Alice' }],
269
- readOnlyRecipients: [{ _id: 'r2', id: 'r2', displayName: 'Bob' }],
231
+ recipients: [pendingRecipient],
232
+ readOnlyRecipients: [],
270
233
  sharedDrive: true,
271
234
  openSharing: false
272
235
  })
@@ -274,6 +237,13 @@ describe('FederatedFolderModal', () => {
274
237
  })
275
238
 
276
239
  it('should show success alert and close modal on successful share', async () => {
240
+ usePendingRecipients.mockReturnValue({
241
+ pendingRecipients: [pendingRecipient],
242
+ setPendingRecipients: jest.fn(),
243
+ selectedOption: 'readWrite',
244
+ setSelectedOption: jest.fn()
245
+ })
246
+ getOrCreateFromArray.mockResolvedValue([pendingRecipient])
277
247
  mockShare.mockResolvedValueOnce({})
278
248
  const { getByTestId } = setup()
279
249
 
@@ -294,6 +264,13 @@ describe('FederatedFolderModal', () => {
294
264
  })
295
265
 
296
266
  it('should show error alert when share fails', async () => {
267
+ usePendingRecipients.mockReturnValue({
268
+ pendingRecipients: [pendingRecipient],
269
+ setPendingRecipients: jest.fn(),
270
+ selectedOption: 'readWrite',
271
+ setSelectedOption: jest.fn()
272
+ })
273
+ getOrCreateFromArray.mockResolvedValue([pendingRecipient])
297
274
  mockShare.mockRejectedValueOnce(new Error('Share failed'))
298
275
  const { getByTestId } = setup()
299
276
 
@@ -314,75 +291,19 @@ describe('FederatedFolderModal', () => {
314
291
  })
315
292
  })
316
293
 
317
- describe('onSetType callback', () => {
318
- it('should move recipient from readOnlyRecipients to recipients when setting two-way', async () => {
319
- const { getByTestId } = setup()
320
-
321
- await waitFor(() => {
322
- expect(getByTestId('btn-share')).toBeTruthy()
323
- })
324
-
325
- fireEvent.click(getByTestId('btn-share'))
326
-
327
- // After btn-share: combined recipients = 2 (1 readWrite + 1 readOnly)
328
- await waitFor(() => {
329
- expect(getByTestId('recipients-count').textContent).toBe('2')
330
- expect(getByTestId('readOnly-recipients-count').textContent).toBe('1')
331
- })
332
-
333
- fireEvent.click(getByTestId('btn-set-type-two-way'))
334
-
335
- // After setting two-way: Bob moves from readOnlyRecipients to recipients
336
- await waitFor(() => {
337
- expect(getByTestId('recipients-count').textContent).toBe('2')
338
- expect(getByTestId('readOnly-recipients-count').textContent).toBe('0')
339
- })
340
- })
341
-
342
- it('should move recipient from recipients to readOnlyRecipients when setting one-way', async () => {
343
- const { getByTestId } = setup()
344
-
345
- await waitFor(() => {
346
- expect(getByTestId('btn-share')).toBeTruthy()
347
- })
348
-
349
- fireEvent.click(getByTestId('btn-share'))
350
-
351
- // After btn-share: combined recipients = 2 (1 readWrite + 1 readOnly)
352
- await waitFor(() => {
353
- expect(getByTestId('recipients-count').textContent).toBe('2')
354
- expect(getByTestId('readOnly-recipients-count').textContent).toBe('1')
355
- })
356
-
357
- fireEvent.click(getByTestId('btn-set-type-one-way'))
358
-
359
- // After setting one-way: Alice moves from recipients to readOnlyRecipients
360
- // Combined count stays at 2, but readOnlyRecipients increases from 1 to 2
361
- await waitFor(() => {
362
- expect(getByTestId('recipients-count').textContent).toBe('2')
363
- expect(getByTestId('readOnly-recipients-count').textContent).toBe('2')
364
- })
365
- })
366
- })
367
-
368
294
  describe('onRevoke callback', () => {
369
- it('should remove recipient from both lists', async () => {
295
+ it('should call revoke from sharing context for existing members', async () => {
296
+ mockRevoke.mockResolvedValueOnce({})
370
297
  const { getByTestId } = setup()
371
298
 
372
299
  await waitFor(() => {
373
- expect(getByTestId('btn-share')).toBeTruthy()
374
- })
375
-
376
- fireEvent.click(getByTestId('btn-share'))
377
-
378
- await waitFor(() => {
379
- expect(getByTestId('recipients-count').textContent).toBe('2')
300
+ expect(getByTestId('dumb-modal')).toBeTruthy()
380
301
  })
381
302
 
382
303
  fireEvent.click(getByTestId('btn-revoke'))
383
304
 
384
305
  await waitFor(() => {
385
- expect(getByTestId('recipients-count').textContent).toBe('1')
306
+ expect(mockRevoke).toHaveBeenCalledWith({ _id: 'folder-123' }, 'abc', 1)
386
307
  })
387
308
  })
388
309
  })
@@ -431,49 +352,5 @@ describe('FederatedFolderModal', () => {
431
352
  expect(getByTestId('recipients-count').textContent).toBe('2')
432
353
  })
433
354
  })
434
-
435
- it('should pass existing recipients as currentRecipients', async () => {
436
- mockGetRecipients.mockReturnValue(existingRecipients)
437
- const { getByTestId } = setup()
438
-
439
- await waitFor(() => {
440
- expect(getByTestId('recipients-count').textContent).toBe('2')
441
- })
442
- })
443
-
444
- it('should call revoke for existing member', async () => {
445
- mockGetRecipients.mockReturnValue(existingRecipients)
446
- mockRevoke.mockResolvedValueOnce({})
447
- const { getByTestId } = setup()
448
-
449
- await waitFor(() => {
450
- expect(getByTestId('dumb-modal')).toBeTruthy()
451
- })
452
-
453
- // Simulate revoke of an existing member (non-virtual-shared-drive index)
454
- fireEvent.click(getByTestId('btn-revoke-existing'))
455
-
456
- await waitFor(() => {
457
- expect(mockRevoke).toHaveBeenCalledWith({ _id: 'folder-123' }, 'abc', 1)
458
- })
459
- })
460
- })
461
-
462
- describe('createContact callback', () => {
463
- it('should call client.create when creating a contact', async () => {
464
- const { getByTestId } = setup()
465
-
466
- await waitFor(() => {
467
- expect(getByTestId('btn-create-contact')).toBeTruthy()
468
- })
469
-
470
- fireEvent.click(getByTestId('btn-create-contact'))
471
-
472
- await waitFor(() => {
473
- expect(client.create).toHaveBeenCalledWith('io.cozy.contacts', {
474
- email: 'test@example.com'
475
- })
476
- })
477
- })
478
355
  })
479
356
  })
@@ -25,7 +25,8 @@ const ShareAutocomplete = ({
25
25
  recipients,
26
26
  onPick,
27
27
  onRemove,
28
- placeholder
28
+ placeholder,
29
+ endAdornment
29
30
  }) => {
30
31
  const [inputValue, setInputValue] = useState('')
31
32
  const [suggestions, setSuggestions] = useState([])
@@ -180,6 +181,9 @@ const ShareAutocomplete = ({
180
181
  {loading && isLoadingDisplayed ? (
181
182
  <Spinner color="var(--primaryColor)" />
182
183
  ) : null}
184
+ {endAdornment ? (
185
+ <div className={styles['endAdornment']}>{endAdornment}</div>
186
+ ) : null}
183
187
  </div>
184
188
  )
185
189
 
@@ -223,7 +227,8 @@ ShareAutocomplete.propTypes = {
223
227
  recipients: PropTypes.array.isRequired,
224
228
  onPick: PropTypes.func.isRequired,
225
229
  onRemove: PropTypes.func.isRequired,
226
- placeholder: PropTypes.string
230
+ placeholder: PropTypes.string,
231
+ endAdornment: PropTypes.node
227
232
  }
228
233
 
229
234
  export default ShareAutocomplete
@@ -1,167 +1,43 @@
1
1
  import PropTypes from 'prop-types'
2
- import React, { useState } from 'react'
2
+ import React from 'react'
3
3
 
4
- import { useClient } from 'cozy-client'
5
4
  import flag from 'cozy-flags'
6
- import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
7
5
  import { useI18n } from 'twake-i18n'
8
6
 
9
7
  import ShareRecipientsInput from './ShareRecipientsInput'
10
- import { ShareRecipientsLimitModal } from './ShareRecipientsLimitModal'
11
- import ShareSubmit from './Sharesubmit'
12
8
  import ShareTypeSelect from './Sharetypeselect'
13
- import { getOrCreateFromArray } from '../helpers/contacts'
14
9
  import {
15
10
  mergeRecipients,
16
- spreadGroupAndMergeRecipients,
17
- hasReachRecipientsLimit
11
+ spreadGroupAndMergeRecipients
18
12
  } from '../helpers/recipients'
19
- import { getSuccessMessage, getErrorMessage } from '../helpers/share'
20
13
  import { isReadOnlySharing } from '../state'
21
14
  import styles from '../styles/share.styl'
22
15
 
23
16
  export const ShareByEmail = ({
24
17
  document,
25
- sharingDesc,
26
- onShare,
27
- createContact,
28
18
  documentType,
29
19
  currentRecipients,
30
- sharing,
31
- submitLabel,
32
- showNotifications = true,
33
- sharedDrive = false
20
+ pendingRecipients,
21
+ onPendingRecipientsChange,
22
+ selectedOption,
23
+ onSelectedOptionChange,
24
+ sharing
34
25
  }) => {
35
- const client = useClient()
36
26
  const { t } = useI18n()
37
- const { showAlert } = useAlert()
38
27
 
39
- const isFederatedMode = flag('drive.federated-shared-folder.enabled')
40
-
41
- const [recipients, setRecipients] = useState([])
42
- const [loading, setLoading] = useState(false)
43
- const [selectedOption, setSelectedOption] = useState('readWrite')
44
- const [showRecipientsLimit, setRecipientsLimit] = useState(false)
45
-
46
- const reset = () => {
47
- setSelectedOption('readWrite')
48
- setRecipients([])
49
- setLoading(false)
50
- }
51
-
52
- const onChange = value => {
53
- setSelectedOption(value)
54
- }
55
-
56
- const onRecipientPick = async recipient => {
57
- // In federated mode, directly share with the recipient with readWrite access
58
- if (isFederatedMode) {
59
- setLoading(true)
60
- try {
61
- const contacts = await getOrCreateFromArray(
62
- client,
63
- [recipient],
64
- createContact
65
- )
66
- await onShare({
67
- document,
68
- recipients: contacts,
69
- readOnlyRecipients: [],
70
- description: sharingDesc,
71
- openSharing: false,
72
- sharedDrive: true
73
- })
74
- } catch (_err) {
75
- if (showNotifications) {
76
- showAlert({
77
- message: t('Share.shareByEmail.error.addingRecipient'),
78
- severity: 'error',
79
- variant: 'filled'
80
- })
81
- }
82
- } finally {
83
- reset()
84
- }
85
- return
86
- }
87
-
88
- // Normal mode: just add to the list
89
- const mergedRecipients = flag('sharing.show-recipient-groups')
90
- ? mergeRecipients(recipients, recipient)
91
- : spreadGroupAndMergeRecipients(recipients, recipient)
92
- setRecipients(mergedRecipients)
28
+ const onRecipientPick = recipient => {
29
+ const merged = flag('sharing.show-recipient-groups')
30
+ ? mergeRecipients(pendingRecipients, recipient)
31
+ : spreadGroupAndMergeRecipients(pendingRecipients, recipient)
32
+ onPendingRecipientsChange(merged)
93
33
  }
94
34
 
95
35
  const onRecipientRemove = recipient => {
96
- const idx = recipients.findIndex(r => r === recipient)
97
- setRecipients([...recipients.slice(0, idx), ...recipients.slice(idx + 1)])
98
- }
99
-
100
- const share = async () => {
101
- if (recipients.length === 0) {
102
- return
103
- }
104
-
105
- // we can't use currentRecipients prop in getSuccessMessage because it may use
106
- // the updated prop to count the new recipients
107
- const recipientsBefore = currentRecipients
108
-
109
- if (hasReachRecipientsLimit(recipientsBefore, recipients)) {
110
- setRecipientsLimit(true)
111
- return
112
- }
113
-
114
- setLoading(true)
115
- try {
116
- const contacts = await getOrCreateFromArray(
117
- client,
118
- recipients,
119
- createContact
120
- )
121
- let readWriteRecipients = []
122
- let readOnlyRecipients = []
123
- if (selectedOption === 'readOnly') {
124
- readOnlyRecipients = contacts
125
- } else {
126
- readWriteRecipients = contacts
127
- }
128
- await onShare({
129
- document,
130
- recipients: readWriteRecipients,
131
- readOnlyRecipients,
132
- description: sharingDesc,
133
- openSharing: readWriteRecipients.length > 0,
134
- sharedDrive
135
- })
136
-
137
- if (showNotifications) {
138
- showAlert({
139
- message: t(
140
- ...getSuccessMessage(recipientsBefore, contacts, documentType)
141
- ),
142
- severity: 'success',
143
- variant: 'filled'
144
- })
145
- }
146
- } catch (err) {
147
- if (showNotifications) {
148
- showAlert({
149
- message: t(
150
- ...getErrorMessage({
151
- t,
152
- err,
153
- documentType,
154
- recipients,
155
- selectedOption
156
- })
157
- ),
158
- severity: 'error',
159
- variant: 'filled'
160
- })
161
- }
162
- } finally {
163
- reset()
164
- }
36
+ const idx = pendingRecipients.findIndex(r => r === recipient)
37
+ onPendingRecipientsChange([
38
+ ...pendingRecipients.slice(0, idx),
39
+ ...pendingRecipients.slice(idx + 1)
40
+ ])
165
41
  }
166
42
 
167
43
  const getSharingOptions = () => {
@@ -170,58 +46,37 @@ export const ShareByEmail = ({
170
46
  : false
171
47
  const readWrite = {
172
48
  value: 'readWrite',
173
- label: t('Share.type.two-way'),
174
- desc: t('Share.type.desc.two-way'),
175
- disabled: false
49
+ label: t('Share.type.two-way')
176
50
  }
177
51
  const readOnly = {
178
52
  value: 'readOnly',
179
- label: t('Share.type.one-way'),
180
- desc: t('Share.type.desc.one-way'),
181
- disabled: false
53
+ label: t('Share.type.one-way')
182
54
  }
183
55
  return isSharingReadOnly ? [readOnly] : [readWrite, readOnly]
184
56
  }
185
57
 
186
- const showShareControl = recipients.length > 0
187
-
188
58
  return (
189
59
  <div className={styles['coz-form-group']}>
190
60
  <div className={styles['coz-form']}>
191
61
  <ShareRecipientsInput
192
62
  placeholder={
193
- recipients.length === 0
63
+ pendingRecipients.length === 0
194
64
  ? t(`${documentType}.share.shareByEmail.emailPlaceholder`)
195
65
  : ''
196
66
  }
197
- onPick={recipient => onRecipientPick(recipient)}
198
- onRemove={recipient => onRecipientRemove(recipient)}
67
+ onPick={onRecipientPick}
68
+ onRemove={onRecipientRemove}
199
69
  currentRecipients={currentRecipients}
200
- recipients={recipients}
201
- disabled={loading}
70
+ recipients={pendingRecipients}
71
+ endAdornment={
72
+ <ShareTypeSelect
73
+ value={selectedOption}
74
+ options={getSharingOptions()}
75
+ onChange={onSelectedOptionChange}
76
+ />
77
+ }
202
78
  />
203
79
  </div>
204
- {!isFederatedMode && showShareControl && (
205
- <div className={styles['share-type-control']}>
206
- <ShareTypeSelect
207
- value={selectedOption}
208
- options={getSharingOptions()}
209
- onChange={onChange}
210
- />
211
- <ShareSubmit
212
- label={submitLabel || t(`${documentType}.share.shareByEmail.send`)}
213
- onSubmit={share}
214
- loading={loading}
215
- disabled={recipients.length === 0}
216
- />
217
- </div>
218
- )}
219
- {showRecipientsLimit ? (
220
- <ShareRecipientsLimitModal
221
- documentName={document?.name}
222
- onConfirm={() => setRecipientsLimit(false)}
223
- />
224
- ) : null}
225
80
  </div>
226
81
  )
227
82
  }
@@ -230,17 +85,11 @@ ShareByEmail.propTypes = {
230
85
  currentRecipients: PropTypes.arrayOf(PropTypes.object),
231
86
  document: PropTypes.object,
232
87
  documentType: PropTypes.string.isRequired,
233
- sharingDesc: PropTypes.string.isRequired,
234
- onShare: PropTypes.func.isRequired,
235
- createContact: PropTypes.func.isRequired,
236
- // We can display this component without having created the sharing yet
237
- sharing: PropTypes.object,
238
- // Customize the label of the button that submit contacts
239
- submitLabel: PropTypes.string,
240
- // Display success or error notifications
241
- showNotifications: PropTypes.bool,
242
- // Set to true for shared drive context
243
- sharedDrive: PropTypes.bool
88
+ pendingRecipients: PropTypes.array.isRequired,
89
+ onPendingRecipientsChange: PropTypes.func.isRequired,
90
+ selectedOption: PropTypes.oneOf(['readWrite', 'readOnly']).isRequired,
91
+ onSelectedOptionChange: PropTypes.func.isRequired,
92
+ sharing: PropTypes.object
244
93
  }
245
94
 
246
95
  export default ShareByEmail