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.
- package/CHANGELOG.md +16 -0
- package/dist/components/FederatedFolder/DumbFederatedFolderModal.js +15 -16
- package/dist/components/FederatedFolder/FederatedFolderModal.js +78 -103
- package/dist/components/FederatedFolder/FederatedFolderModal.spec.js +102 -292
- package/dist/components/Recipient/RecipientConfirm.js +0 -5
- package/dist/components/ShareAutosuggest.js +9 -4
- package/dist/components/ShareByEmail.js +29 -274
- package/dist/components/ShareByEmail.spec.js +52 -230
- package/dist/components/ShareByLink.js +1 -3
- package/dist/components/ShareDialogCozyToCozy.js +169 -35
- package/dist/components/ShareDialogCozyToCozy.spec.js +122 -35
- package/dist/components/ShareDialogTwoStepsConfirmationContainer.js +13 -3
- package/dist/components/ShareModal/SharingDetailsModal.js +0 -5
- package/dist/components/ShareRecipientsInput.js +6 -3
- package/dist/components/SharedDrive/SharedDriveForm.js +11 -23
- package/dist/components/SharedDrive/SharedDriveModal.js +1 -1
- package/dist/components/SharedDrive/useSharedDrive.js +33 -57
- package/dist/components/SharedDrive/useSharedDrive.spec.js +29 -93
- package/dist/components/SharedFolder/DumbBatchSharedFolderModal.js +15 -26
- package/dist/components/Sharetypeselect.js +85 -90
- package/dist/hooks/usePendingRecipients.js +20 -0
- package/dist/hooks/usePendingRecipients.spec.js +30 -0
- package/dist/styles/autosuggest.styl +8 -2
- package/dist/styles/share.styl +0 -39
- package/dist/stylesheet.css +8 -36
- package/locales/en.json +5 -9
- package/locales/fr.json +5 -9
- package/locales/ru.json +5 -9
- package/locales/vi.json +5 -9
- package/package.json +2 -2
- package/src/components/FederatedFolder/DumbFederatedFolderModal.jsx +14 -15
- package/src/components/FederatedFolder/FederatedFolderModal.jsx +50 -67
- package/src/components/FederatedFolder/FederatedFolderModal.spec.jsx +63 -186
- package/src/components/ShareAutosuggest.jsx +7 -2
- package/src/components/ShareByEmail.jsx +35 -186
- package/src/components/ShareByEmail.spec.jsx +22 -173
- package/src/components/ShareByLink.jsx +1 -7
- package/src/components/ShareDialogCozyToCozy.jsx +171 -38
- package/src/components/ShareDialogCozyToCozy.spec.jsx +66 -0
- package/src/components/ShareDialogTwoStepsConfirmationContainer.jsx +11 -1
- package/src/components/ShareRecipientsInput.jsx +5 -2
- package/src/components/SharedDrive/SharedDriveForm.jsx +10 -23
- package/src/components/SharedDrive/SharedDriveModal.jsx +1 -1
- package/src/components/SharedDrive/useSharedDrive.js +24 -50
- package/src/components/SharedDrive/useSharedDrive.spec.js +19 -67
- package/src/components/SharedFolder/DumbBatchSharedFolderModal.jsx +15 -29
- package/src/components/Sharetypeselect.jsx +75 -58
- package/src/hooks/usePendingRecipients.js +13 -0
- package/src/hooks/usePendingRecipients.spec.js +21 -0
- package/src/styles/autosuggest.styl +8 -2
- package/src/styles/share.styl +0 -39
- package/dist/components/SharedDrive/helpers.js +0 -102
- package/dist/components/SharedDrive/helpers.spec.js +0 -227
- package/dist/components/Sharesubmit.js +0 -28
- package/src/components/SharedDrive/helpers.js +0 -108
- package/src/components/SharedDrive/helpers.spec.js +0 -177
- 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
|
-
|
|
45
|
-
|
|
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="
|
|
52
|
-
{
|
|
59
|
+
<span data-testid="pending-recipients-count">
|
|
60
|
+
{pendingRecipients.length}
|
|
53
61
|
</span>
|
|
54
|
-
<
|
|
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
|
|
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
|
-
|
|
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('
|
|
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
|
-
|
|
232
|
-
|
|
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
|
-
|
|
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('
|
|
199
|
+
expect(getByTestId('btn-send')).toBeTruthy()
|
|
242
200
|
})
|
|
243
201
|
|
|
244
|
-
fireEvent.click(getByTestId('btn-
|
|
202
|
+
fireEvent.click(getByTestId('btn-send'))
|
|
245
203
|
|
|
246
204
|
await waitFor(() => {
|
|
247
|
-
expect(
|
|
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: [
|
|
269
|
-
readOnlyRecipients: [
|
|
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
|
|
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('
|
|
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(
|
|
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
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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 =
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
63
|
+
pendingRecipients.length === 0
|
|
194
64
|
? t(`${documentType}.share.shareByEmail.emailPlaceholder`)
|
|
195
65
|
: ''
|
|
196
66
|
}
|
|
197
|
-
onPick={
|
|
198
|
-
onRemove={
|
|
67
|
+
onPick={onRecipientPick}
|
|
68
|
+
onRemove={onRecipientRemove}
|
|
199
69
|
currentRecipients={currentRecipients}
|
|
200
|
-
recipients={
|
|
201
|
-
|
|
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
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|