cozy-sharing 31.0.2 → 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 +30 -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/GroupRecipientPermissions.js +15 -11
- package/dist/components/Recipient/LinkRecipient.js +15 -11
- package/dist/components/Recipient/LinkRecipientLite.js +1 -1
- package/dist/components/Recipient/LinkRecipientPermissions.js +2 -1
- package/dist/components/Recipient/MemberRecipient.spec.js +8 -10
- package/dist/components/Recipient/MemberRecipientLite.js +1 -1
- package/dist/components/Recipient/MemberRecipientPermissions.js +16 -12
- package/dist/components/Recipient/MemberRecipientPermissions.spec.js +62 -50
- 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 +123 -36
- 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 +17 -25
- package/dist/components/SharedDrive/SharedDriveModal.js +1 -1
- package/dist/components/SharedDrive/SharedDriveRecipientPermissions.js +15 -10
- 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 +14 -44
- package/locales/fr.json +14 -44
- package/locales/ru.json +14 -44
- package/locales/vi.json +14 -42
- 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/Recipient/GroupRecipientPermissions.jsx +15 -13
- package/src/components/Recipient/LinkRecipient.jsx +9 -9
- package/src/components/Recipient/LinkRecipientLite.jsx +1 -3
- package/src/components/Recipient/LinkRecipientPermissions.jsx +2 -1
- package/src/components/Recipient/MemberRecipient.spec.jsx +4 -6
- package/src/components/Recipient/MemberRecipientLite.jsx +2 -2
- package/src/components/Recipient/MemberRecipientPermissions.jsx +16 -21
- package/src/components/Recipient/MemberRecipientPermissions.spec.jsx +21 -23
- 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 +67 -1
- package/src/components/ShareDialogTwoStepsConfirmationContainer.jsx +11 -1
- package/src/components/ShareRecipientsInput.jsx +5 -2
- package/src/components/SharedDrive/SharedDriveForm.jsx +16 -25
- package/src/components/SharedDrive/SharedDriveModal.jsx +1 -1
- package/src/components/SharedDrive/SharedDriveRecipientPermissions.jsx +15 -17
- 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/Recipient/actions/revokeGroup.js +0 -42
- package/dist/components/Recipient/actions/revokeMember.js +0 -42
- package/dist/components/Recipient/actions/revokeSharedDriveMember.js +0 -41
- 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/Recipient/actions/revokeGroup.js +0 -42
- package/src/components/Recipient/actions/revokeMember.js +0 -42
- package/src/components/Recipient/actions/revokeSharedDriveMember.js +0 -41
- package/src/components/SharedDrive/helpers.js +0 -108
- package/src/components/SharedDrive/helpers.spec.js +0 -177
- package/src/components/Sharesubmit.jsx +0 -31
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
import PropTypes from 'prop-types'
|
|
2
|
-
import React, {
|
|
2
|
+
import React, { useEffect, useState } from 'react'
|
|
3
3
|
|
|
4
4
|
import { useClient } from 'cozy-client'
|
|
5
5
|
import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
|
|
6
6
|
import { useI18n } from 'twake-i18n'
|
|
7
7
|
|
|
8
8
|
import { DumbFederatedFolderModal } from './DumbFederatedFolderModal'
|
|
9
|
+
import { getOrCreateFromArray } from '../../helpers/contacts'
|
|
9
10
|
import withLocales from '../../hoc/withLocales'
|
|
11
|
+
import { usePendingRecipients } from '../../hooks/usePendingRecipients'
|
|
10
12
|
import { useSharingContext } from '../../hooks/useSharingContext'
|
|
11
|
-
import {
|
|
12
|
-
formatRecipients,
|
|
13
|
-
mergeAndDeduplicateRecipients,
|
|
14
|
-
moveRecipientToReadWrite,
|
|
15
|
-
moveRecipientToReadOnly,
|
|
16
|
-
RECIPIENT_INDEX_PREFIX
|
|
17
|
-
} from '../SharedDrive/helpers'
|
|
18
13
|
|
|
19
14
|
export const FederatedFolderModal = withLocales(
|
|
20
15
|
({
|
|
@@ -38,6 +33,22 @@ export const FederatedFolderModal = withLocales(
|
|
|
38
33
|
const { showAlert } = useAlert()
|
|
39
34
|
|
|
40
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)
|
|
41
52
|
|
|
42
53
|
const documentPermissions = existingDocument
|
|
43
54
|
? getDocumentPermissions(existingDocument._id)
|
|
@@ -65,32 +76,33 @@ export const FederatedFolderModal = withLocales(
|
|
|
65
76
|
getSharingById
|
|
66
77
|
])
|
|
67
78
|
|
|
68
|
-
const
|
|
69
|
-
recipients: [],
|
|
70
|
-
readOnlyRecipients: []
|
|
71
|
-
})
|
|
72
|
-
const [folderName] = useState(existingDocument?.name || '')
|
|
73
|
-
|
|
74
|
-
const onShare = params => {
|
|
75
|
-
setFederatedRecipients(prev => ({
|
|
76
|
-
recipients: mergeAndDeduplicateRecipients([
|
|
77
|
-
prev.recipients,
|
|
78
|
-
params.recipients || []
|
|
79
|
-
]),
|
|
80
|
-
readOnlyRecipients: mergeAndDeduplicateRecipients([
|
|
81
|
-
prev.readOnlyRecipients,
|
|
82
|
-
params.readOnlyRecipients || []
|
|
83
|
-
])
|
|
84
|
-
}))
|
|
85
|
-
}
|
|
79
|
+
const folderName = existingDocument?.name || ''
|
|
86
80
|
|
|
87
81
|
const onSend = async () => {
|
|
82
|
+
if (pendingRecipients.length === 0) {
|
|
83
|
+
onClose()
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
setIsSending(true)
|
|
88
|
+
setFrozenDoc(existingDocument)
|
|
89
|
+
setFrozenRecipients(existingRecipients)
|
|
90
|
+
|
|
88
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
|
+
|
|
89
101
|
await share({
|
|
90
102
|
description: folderName,
|
|
91
103
|
document: existingDocument,
|
|
92
|
-
recipients:
|
|
93
|
-
readOnlyRecipients
|
|
104
|
+
recipients: readWriteRecipients,
|
|
105
|
+
readOnlyRecipients,
|
|
94
106
|
sharedDrive: true,
|
|
95
107
|
openSharing: false
|
|
96
108
|
})
|
|
@@ -108,31 +120,8 @@ export const FederatedFolderModal = withLocales(
|
|
|
108
120
|
severity: 'error',
|
|
109
121
|
variant: 'filled'
|
|
110
122
|
})
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const onSetType = (index, newType) => {
|
|
115
|
-
const _id = index.split(RECIPIENT_INDEX_PREFIX)[1]
|
|
116
|
-
|
|
117
|
-
if (newType === 'two-way') {
|
|
118
|
-
setFederatedRecipients(prev => moveRecipientToReadWrite(prev, _id))
|
|
119
|
-
} else {
|
|
120
|
-
setFederatedRecipients(prev => moveRecipientToReadOnly(prev, _id))
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const onRevoke = async (documentOrIndex, sharingId, memberIndex) => {
|
|
125
|
-
if (
|
|
126
|
-
typeof documentOrIndex === 'string' &&
|
|
127
|
-
documentOrIndex.startsWith(RECIPIENT_INDEX_PREFIX)
|
|
128
|
-
) {
|
|
129
|
-
const _id = documentOrIndex.split(RECIPIENT_INDEX_PREFIX)[1]
|
|
130
|
-
setFederatedRecipients(prev => ({
|
|
131
|
-
recipients: prev.recipients.filter(r => r._id !== _id),
|
|
132
|
-
readOnlyRecipients: prev.readOnlyRecipients.filter(r => r._id !== _id)
|
|
133
|
-
}))
|
|
134
|
-
} else {
|
|
135
|
-
await revoke(documentOrIndex, sharingId, memberIndex)
|
|
123
|
+
} finally {
|
|
124
|
+
setIsSending(false)
|
|
136
125
|
}
|
|
137
126
|
}
|
|
138
127
|
|
|
@@ -140,32 +129,26 @@ export const FederatedFolderModal = withLocales(
|
|
|
140
129
|
? getRecipients(existingDocument._id)
|
|
141
130
|
: []
|
|
142
131
|
|
|
143
|
-
const recipients = [
|
|
144
|
-
...existingRecipients,
|
|
145
|
-
...formatRecipients(federatedRecipients)
|
|
146
|
-
]
|
|
147
|
-
|
|
148
132
|
const modalTitle = t('FederatedFolder.shareTitle', { name: folderName })
|
|
149
|
-
|
|
150
133
|
const isSharedDrive = Boolean(existingDocument?.driveId)
|
|
151
134
|
|
|
152
135
|
return (
|
|
153
136
|
<DumbFederatedFolderModal
|
|
154
137
|
title={modalTitle}
|
|
155
|
-
document={existingDocument}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
currentRecipients={existingRecipients}
|
|
160
|
-
onRevoke={onRevoke}
|
|
161
|
-
onSetType={onSetType}
|
|
138
|
+
document={isSending ? frozenDoc : existingDocument}
|
|
139
|
+
recipients={isSending ? frozenRecipients : existingRecipients}
|
|
140
|
+
currentRecipients={isSending ? frozenRecipients : existingRecipients}
|
|
141
|
+
onRevoke={revoke}
|
|
162
142
|
onSend={onSend}
|
|
163
143
|
onClose={onClose}
|
|
164
|
-
onShare={onShare}
|
|
165
144
|
sharingLink={sharingLink}
|
|
166
145
|
showShareByEmail={!isSharedDrive}
|
|
167
146
|
autoOpenShareRestriction={autoOpenShareRestriction}
|
|
168
147
|
showGenerateLinkButton={showGenerateLinkButton}
|
|
148
|
+
pendingRecipients={pendingRecipients}
|
|
149
|
+
onPendingRecipientsChange={setPendingRecipients}
|
|
150
|
+
selectedOption={selectedOption}
|
|
151
|
+
onSelectedOptionChange={setSelectedOption}
|
|
169
152
|
/>
|
|
170
153
|
)
|
|
171
154
|
}
|
|
@@ -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
|
})
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import React, { useState, useRef } from 'react'
|
|
2
2
|
|
|
3
|
-
import { useClient } from 'cozy-client'
|
|
4
3
|
import ActionsMenu from 'cozy-ui/transpiled/react/ActionsMenu'
|
|
5
|
-
import {
|
|
6
|
-
makeActions,
|
|
7
|
-
divider
|
|
8
|
-
} from 'cozy-ui/transpiled/react/ActionsMenu/Actions'
|
|
4
|
+
import { makeActions } from 'cozy-ui/transpiled/react/ActionsMenu/Actions'
|
|
9
5
|
import ActionsMenuMobileHeader from 'cozy-ui/transpiled/react/ActionsMenu/ActionsMenuMobileHeader'
|
|
10
6
|
import DropdownButton from 'cozy-ui/transpiled/react/DropdownButton'
|
|
7
|
+
import Icon from 'cozy-ui/transpiled/react/Icon'
|
|
8
|
+
import IconButton from 'cozy-ui/transpiled/react/IconButton'
|
|
9
|
+
import CrossCircleOutlineIcon from 'cozy-ui/transpiled/react/Icons/CrossCircleOutline'
|
|
11
10
|
import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
|
|
12
11
|
import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
|
|
13
12
|
import Spinner from 'cozy-ui/transpiled/react/Spinner'
|
|
14
13
|
import { useI18n } from 'twake-i18n'
|
|
15
14
|
|
|
16
15
|
import { permission } from './actions/permission'
|
|
17
|
-
import { revokeGroup as revokeGroupAction } from './actions/revokeGroup'
|
|
18
16
|
import { useSharingContext } from '../../hooks/useSharingContext'
|
|
19
17
|
import { GroupAvatar } from '../Avatar/GroupAvatar'
|
|
20
18
|
|
|
@@ -30,7 +28,6 @@ const GroupRecipientPermissions = ({
|
|
|
30
28
|
}) => {
|
|
31
29
|
const { t } = useI18n()
|
|
32
30
|
const buttonRef = useRef()
|
|
33
|
-
const client = useClient()
|
|
34
31
|
const { revokeGroup, revokeSelf } = useSharingContext()
|
|
35
32
|
|
|
36
33
|
const [isMenuDisplayed, setMenuDisplayed] = useState(false)
|
|
@@ -53,12 +50,9 @@ const GroupRecipientPermissions = ({
|
|
|
53
50
|
|
|
54
51
|
const type = isReadOnly ? 'one-way' : 'two-way'
|
|
55
52
|
|
|
56
|
-
const actions = makeActions([permission
|
|
57
|
-
client,
|
|
53
|
+
const actions = makeActions([permission], {
|
|
58
54
|
t,
|
|
59
|
-
type
|
|
60
|
-
isOwner,
|
|
61
|
-
handleRevocation
|
|
55
|
+
type
|
|
62
56
|
})
|
|
63
57
|
|
|
64
58
|
return (
|
|
@@ -73,7 +67,7 @@ const GroupRecipientPermissions = ({
|
|
|
73
67
|
onClick={toggleMenu}
|
|
74
68
|
textVariant="body2"
|
|
75
69
|
>
|
|
76
|
-
{t(`Share.type.${type}`)
|
|
70
|
+
{t(`Share.type.${type}`)}
|
|
77
71
|
</DropdownButton>
|
|
78
72
|
<ActionsMenu
|
|
79
73
|
ref={buttonRef}
|
|
@@ -92,6 +86,14 @@ const GroupRecipientPermissions = ({
|
|
|
92
86
|
/>
|
|
93
87
|
</ActionsMenuMobileHeader>
|
|
94
88
|
</ActionsMenu>
|
|
89
|
+
<IconButton
|
|
90
|
+
onClick={handleRevocation}
|
|
91
|
+
size="small"
|
|
92
|
+
className="u-ml-half"
|
|
93
|
+
aria-label={t('Share.members.revoke')}
|
|
94
|
+
>
|
|
95
|
+
<Icon icon={CrossCircleOutlineIcon} />
|
|
96
|
+
</IconButton>
|
|
95
97
|
</>
|
|
96
98
|
)}
|
|
97
99
|
</div>
|
|
@@ -2,14 +2,15 @@ import PropTypes from 'prop-types'
|
|
|
2
2
|
import React, { useState } from 'react'
|
|
3
3
|
|
|
4
4
|
import Avatar from 'cozy-ui/transpiled/react/Avatar'
|
|
5
|
-
import Button from 'cozy-ui/transpiled/react/Button'
|
|
6
5
|
import Fade from 'cozy-ui/transpiled/react/Fade'
|
|
7
6
|
import Icon from 'cozy-ui/transpiled/react/Icon'
|
|
7
|
+
import IconButton from 'cozy-ui/transpiled/react/IconButton'
|
|
8
8
|
import Gear from 'cozy-ui/transpiled/react/Icons/Gear'
|
|
9
9
|
import LinkIcon from 'cozy-ui/transpiled/react/Icons/Link'
|
|
10
10
|
import ListItem from 'cozy-ui/transpiled/react/ListItem'
|
|
11
11
|
import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
|
|
12
12
|
import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
|
|
13
|
+
import Typography from 'cozy-ui/transpiled/react/Typography'
|
|
13
14
|
import useBreakpoints from 'cozy-ui/transpiled/react/providers/Breakpoints'
|
|
14
15
|
import { useI18n } from 'twake-i18n'
|
|
15
16
|
|
|
@@ -68,15 +69,14 @@ const LinkRecipient = props => {
|
|
|
68
69
|
</Avatar>
|
|
69
70
|
</ListItemIcon>
|
|
70
71
|
<ListItemText
|
|
72
|
+
onClick={() => setOpenShareRestrictionModal(true)}
|
|
71
73
|
primary={
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
>
|
|
78
|
-
{textPrimary}
|
|
79
|
-
</Button>
|
|
74
|
+
<div className="u-flex u-flex-items-center">
|
|
75
|
+
<Typography className="u-c-pointer ">{textPrimary}</Typography>
|
|
76
|
+
<IconButton size="small" className="u-ml-half">
|
|
77
|
+
<Icon icon={Gear} />
|
|
78
|
+
</IconButton>
|
|
79
|
+
</div>
|
|
80
80
|
}
|
|
81
81
|
secondary={textSecondary}
|
|
82
82
|
/>
|
|
@@ -32,9 +32,7 @@ const LinkRecipientLite = ({ permissions, link }) => {
|
|
|
32
32
|
secondary={link}
|
|
33
33
|
/>
|
|
34
34
|
<Typography className="u-flex-shrink-0" variant="body2">
|
|
35
|
-
{t(
|
|
36
|
-
`Share.type.${isReadOnlyPermissions ? 'one-way' : 'two-way'}`
|
|
37
|
-
).toLowerCase()}
|
|
35
|
+
{t(`Share.type.${isReadOnlyPermissions ? 'one-way' : 'two-way'}`)}
|
|
38
36
|
</Typography>
|
|
39
37
|
</ListItem>
|
|
40
38
|
)
|
|
@@ -38,6 +38,7 @@ const LinkRecipientPermissions = ({ className, document }) => {
|
|
|
38
38
|
const handleSetType = newType => {
|
|
39
39
|
const verbs = newType === 'one-way' ? READ_ONLY_PERMS : WRITE_PERMS
|
|
40
40
|
updateDocumentPermissions(document, { verbs })
|
|
41
|
+
hideMenu()
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
const handleRevocation = () => revokeSharingLink(document)
|
|
@@ -63,7 +64,7 @@ const LinkRecipientPermissions = ({ className, document }) => {
|
|
|
63
64
|
textVariant="body2"
|
|
64
65
|
onClick={toggleMenu}
|
|
65
66
|
>
|
|
66
|
-
{t(`Share.type.${type}`)
|
|
67
|
+
{t(`Share.type.${type}`)}
|
|
67
68
|
</DropdownButton>
|
|
68
69
|
<ActionsMenu
|
|
69
70
|
ref={buttonRef}
|