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,138 +1,44 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { fireEvent, render, screen } from '@testing-library/react'
|
|
2
2
|
import React from 'react'
|
|
3
3
|
import { act } from 'react-dom/test-utils'
|
|
4
4
|
|
|
5
|
-
import flag from 'cozy-flags'
|
|
6
|
-
|
|
7
5
|
import { ShareByEmail } from './ShareByEmail'
|
|
8
6
|
import AppLike from '../../test/AppLike'
|
|
9
|
-
import { getOrCreateFromArray } from '../helpers/contacts'
|
|
10
|
-
|
|
11
|
-
jest.mock('../helpers/contacts', () => ({
|
|
12
|
-
// eslint-disable-next-line no-unused-vars
|
|
13
|
-
getOrCreateFromArray: jest.fn((client, recipients, _createContact) => {
|
|
14
|
-
// Return the recipients as-is by default (simulating contact creation/lookup)
|
|
15
|
-
return Promise.resolve(recipients)
|
|
16
|
-
})
|
|
17
|
-
}))
|
|
18
|
-
|
|
19
|
-
jest.mock('cozy-flags', () =>
|
|
20
|
-
jest.fn(flagName => {
|
|
21
|
-
if (flagName === 'drive.federated-shared-folder.enabled') {
|
|
22
|
-
return false
|
|
23
|
-
}
|
|
24
|
-
return null
|
|
25
|
-
})
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
describe('ShareByEmailComponent', () => {
|
|
29
|
-
const defaultDocument = {
|
|
30
|
-
id: 'doc_id',
|
|
31
|
-
name: 'documentName'
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const onShare = jest.fn()
|
|
35
7
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
sharedDrive = false
|
|
41
|
-
} = {}) => {
|
|
8
|
+
describe('ShareByEmail (controlled)', () => {
|
|
9
|
+
const setup = (overrides = {}) => {
|
|
10
|
+
const onPendingRecipientsChange = jest.fn()
|
|
11
|
+
const onSelectedOptionChange = jest.fn()
|
|
42
12
|
const props = {
|
|
13
|
+
document: { id: 'doc_id', name: 'documentName' },
|
|
43
14
|
documentType: 'Files',
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
15
|
+
currentRecipients: [],
|
|
16
|
+
pendingRecipients: [],
|
|
17
|
+
onPendingRecipientsChange,
|
|
18
|
+
selectedOption: 'readWrite',
|
|
19
|
+
onSelectedOptionChange,
|
|
20
|
+
...overrides
|
|
50
21
|
}
|
|
51
|
-
|
|
22
|
+
const utils = render(
|
|
52
23
|
<AppLike>
|
|
53
24
|
<ShareByEmail {...props} />
|
|
54
25
|
</AppLike>
|
|
55
26
|
)
|
|
27
|
+
return { ...utils, onPendingRecipientsChange, onSelectedOptionChange }
|
|
56
28
|
}
|
|
57
29
|
|
|
58
30
|
beforeEach(() => {
|
|
59
31
|
jest.clearAllMocks()
|
|
60
|
-
// Reset flag mock to default (non-federated mode)
|
|
61
|
-
flag.mockImplementation(flagName => {
|
|
62
|
-
if (flagName === 'drive.federated-shared-folder.enabled') {
|
|
63
|
-
return false
|
|
64
|
-
}
|
|
65
|
-
return null
|
|
66
|
-
})
|
|
67
32
|
})
|
|
68
33
|
|
|
69
|
-
it('
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
setup({ sharingDesc })
|
|
34
|
+
it('calls onPendingRecipientsChange when an email is picked', () => {
|
|
35
|
+
const { onPendingRecipientsChange } = setup()
|
|
73
36
|
|
|
74
37
|
act(() => {
|
|
75
38
|
fireEvent.change(screen.getByPlaceholderText('Add contacts'), {
|
|
76
39
|
target: { value: 'quentin@cozycloud.cc' }
|
|
77
40
|
})
|
|
78
41
|
})
|
|
79
|
-
|
|
80
|
-
act(() => {
|
|
81
|
-
fireEvent.keyPress(screen.getByPlaceholderText('Add contacts'), {
|
|
82
|
-
key: 'Enter',
|
|
83
|
-
code: 'Enter',
|
|
84
|
-
charCode: 13
|
|
85
|
-
})
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
act(() => {
|
|
89
|
-
fireEvent.click(screen.getByRole('button', { name: 'Send' }))
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
await waitFor(() => {
|
|
93
|
-
expect(onShare).toHaveBeenCalledWith({
|
|
94
|
-
description: sharingDesc,
|
|
95
|
-
document: defaultDocument,
|
|
96
|
-
openSharing: true,
|
|
97
|
-
readOnlyRecipients: [],
|
|
98
|
-
recipients: [{ email: 'quentin@cozycloud.cc' }],
|
|
99
|
-
sharedDrive: false
|
|
100
|
-
})
|
|
101
|
-
})
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('should alert user when it has reached the recipients limit for a document', async () => {
|
|
105
|
-
flag.mockImplementation(flagName => {
|
|
106
|
-
if (flagName === 'sharing.recipients-limit') {
|
|
107
|
-
return 2
|
|
108
|
-
}
|
|
109
|
-
if (flagName === 'drive.federated-shared-folder.enabled') {
|
|
110
|
-
return false
|
|
111
|
-
}
|
|
112
|
-
return null
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
setup({
|
|
116
|
-
currentRecipients: [
|
|
117
|
-
{
|
|
118
|
-
email: 'alice@gmail.com',
|
|
119
|
-
status: 'pending',
|
|
120
|
-
type: 'two-way'
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
email: 'bob@gmail.com',
|
|
124
|
-
status: 'pending',
|
|
125
|
-
type: 'two-way'
|
|
126
|
-
}
|
|
127
|
-
]
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
act(() => {
|
|
131
|
-
fireEvent.change(screen.getByPlaceholderText('Add contacts'), {
|
|
132
|
-
target: { value: 'john@gmail.com' }
|
|
133
|
-
})
|
|
134
|
-
})
|
|
135
|
-
|
|
136
42
|
act(() => {
|
|
137
43
|
fireEvent.keyPress(screen.getByPlaceholderText('Add contacts'), {
|
|
138
44
|
key: 'Enter',
|
|
@@ -141,70 +47,13 @@ describe('ShareByEmailComponent', () => {
|
|
|
141
47
|
})
|
|
142
48
|
})
|
|
143
49
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
act(() => {
|
|
149
|
-
fireEvent.click(screen.getByRole('button', { name: 'I understand' }))
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
await waitFor(() => {
|
|
153
|
-
expect(onShare).not.toHaveBeenCalled()
|
|
154
|
-
})
|
|
50
|
+
expect(onPendingRecipientsChange).toHaveBeenCalledWith([
|
|
51
|
+
{ email: 'quentin@cozycloud.cc' }
|
|
52
|
+
])
|
|
155
53
|
})
|
|
156
54
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (flagName === 'drive.federated-shared-folder.enabled') {
|
|
161
|
-
return true
|
|
162
|
-
}
|
|
163
|
-
return null
|
|
164
|
-
})
|
|
165
|
-
getOrCreateFromArray.mockResolvedValue([
|
|
166
|
-
{ _id: 'contact1', email: 'quentin@cozycloud.cc' }
|
|
167
|
-
])
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
it('should directly share when selecting a recipient in federated mode', async () => {
|
|
171
|
-
const sharingDesc = 'test'
|
|
172
|
-
const createContact = jest.fn()
|
|
173
|
-
|
|
174
|
-
setup({ sharingDesc, createContact })
|
|
175
|
-
|
|
176
|
-
act(() => {
|
|
177
|
-
fireEvent.change(screen.getByPlaceholderText('Add contacts'), {
|
|
178
|
-
target: { value: 'quentin@cozycloud.cc' }
|
|
179
|
-
})
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
act(() => {
|
|
183
|
-
fireEvent.keyPress(screen.getByPlaceholderText('Add contacts'), {
|
|
184
|
-
key: 'Enter',
|
|
185
|
-
code: 'Enter',
|
|
186
|
-
charCode: 13
|
|
187
|
-
})
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
await waitFor(() => {
|
|
191
|
-
expect(getOrCreateFromArray).toHaveBeenCalledWith(
|
|
192
|
-
expect.any(Object),
|
|
193
|
-
[{ email: 'quentin@cozycloud.cc' }],
|
|
194
|
-
expect.any(Function)
|
|
195
|
-
)
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
await waitFor(() => {
|
|
199
|
-
expect(onShare).toHaveBeenCalledWith({
|
|
200
|
-
document: defaultDocument,
|
|
201
|
-
recipients: [{ _id: 'contact1', email: 'quentin@cozycloud.cc' }],
|
|
202
|
-
readOnlyRecipients: [],
|
|
203
|
-
description: sharingDesc,
|
|
204
|
-
openSharing: false,
|
|
205
|
-
sharedDrive: true
|
|
206
|
-
})
|
|
207
|
-
})
|
|
208
|
-
})
|
|
55
|
+
it('always shows the read/write dropdown trigger inside the input', () => {
|
|
56
|
+
setup()
|
|
57
|
+
expect(screen.getByRole('button', { name: 'Editor' })).not.toBeNull()
|
|
209
58
|
})
|
|
210
59
|
})
|
|
@@ -2,7 +2,6 @@ import PropTypes from 'prop-types'
|
|
|
2
2
|
import React, { useReducer, useState } from 'react'
|
|
3
3
|
|
|
4
4
|
import { useClient } from 'cozy-client'
|
|
5
|
-
import flag from 'cozy-flags'
|
|
6
5
|
import Button from 'cozy-ui/transpiled/react/Buttons'
|
|
7
6
|
import Icon from 'cozy-ui/transpiled/react/Icon'
|
|
8
7
|
import CopyIcon from 'cozy-ui/transpiled/react/Icons/Copy'
|
|
@@ -36,7 +35,6 @@ const ShareByLink = ({
|
|
|
36
35
|
const showGenerateButton = showGenerateLinkButton && !link
|
|
37
36
|
const showCopyAndSendButtons = !showGenerateButton && isMobile && canShare
|
|
38
37
|
const showOnlyCopyButton = !showGenerateButton && (!isMobile || !canShare)
|
|
39
|
-
const isFederated = flag('drive.federated-shared-folder.enabled')
|
|
40
38
|
|
|
41
39
|
const [isEditDialogOpen, toggleEditDialogOpen] = useReducer(
|
|
42
40
|
state => !state,
|
|
@@ -139,11 +137,7 @@ const ShareByLink = ({
|
|
|
139
137
|
}
|
|
140
138
|
|
|
141
139
|
return (
|
|
142
|
-
<div
|
|
143
|
-
className={
|
|
144
|
-
isFederated ? 'u-w-100' : 'u-w-100 u-flex u-flex-justify-center'
|
|
145
|
-
}
|
|
146
|
-
>
|
|
140
|
+
<div className="u-w-100">
|
|
147
141
|
{showCopyAndSendButtons && (
|
|
148
142
|
<>
|
|
149
143
|
<Button
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import cx from 'classnames'
|
|
2
|
-
import
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import React, { useCallback, useMemo, useState } from 'react'
|
|
3
4
|
|
|
5
|
+
import { useClient } from 'cozy-client'
|
|
6
|
+
import Button from 'cozy-ui/transpiled/react/Buttons'
|
|
4
7
|
import Typography from 'cozy-ui/transpiled/react/Typography'
|
|
8
|
+
import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
|
|
5
9
|
import useBreakpoints from 'cozy-ui/transpiled/react/providers/Breakpoints'
|
|
6
10
|
import { useI18n } from 'twake-i18n'
|
|
7
11
|
|
|
@@ -9,31 +13,34 @@ import AntivirusAlert from './AntivirusAlert'
|
|
|
9
13
|
import { default as DumbShareByEmail } from './ShareByEmail'
|
|
10
14
|
import { default as DumbShareByLink } from './ShareByLink'
|
|
11
15
|
import ShareDialogTwoStepsConfirmationContainer from './ShareDialogTwoStepsConfirmationContainer'
|
|
16
|
+
import { ShareRecipientsLimitModal } from './ShareRecipientsLimitModal'
|
|
12
17
|
import WhoHasAccess from './WhoHasAccess'
|
|
18
|
+
import { getOrCreateFromArray } from '../helpers/contacts'
|
|
19
|
+
import { hasReachRecipientsLimit } from '../helpers/recipients'
|
|
20
|
+
import { getErrorMessage, getSuccessMessage } from '../helpers/share'
|
|
21
|
+
import { usePendingRecipients } from '../hooks/usePendingRecipients'
|
|
13
22
|
import styles from '../styles/share.styl'
|
|
14
23
|
|
|
15
|
-
/**
|
|
16
|
-
* Displays the sharing interface that can be used when ShareDialogCozyToCozy is in sharing state
|
|
17
|
-
*/
|
|
18
24
|
const SharingContent = ({
|
|
19
|
-
createContact,
|
|
20
25
|
document,
|
|
21
26
|
documentType,
|
|
22
27
|
hasSharedParent,
|
|
23
28
|
isOwner,
|
|
24
29
|
onRevoke,
|
|
25
30
|
onRevokeSelf,
|
|
26
|
-
onShare,
|
|
27
31
|
recipients,
|
|
28
32
|
sharing,
|
|
29
|
-
sharingDesc,
|
|
30
33
|
showShareByEmail,
|
|
31
34
|
showShareOnlyByLink,
|
|
32
35
|
showWhoHasAccess,
|
|
33
36
|
recipientsToBeConfirmed,
|
|
34
37
|
verifyRecipient,
|
|
35
38
|
link,
|
|
36
|
-
permissions
|
|
39
|
+
permissions,
|
|
40
|
+
pendingRecipients,
|
|
41
|
+
onPendingRecipientsChange,
|
|
42
|
+
selectedOption,
|
|
43
|
+
onSelectedOptionChange
|
|
37
44
|
}) => {
|
|
38
45
|
const { t } = useI18n()
|
|
39
46
|
const { isMobile } = useBreakpoints()
|
|
@@ -64,13 +71,14 @@ const SharingContent = ({
|
|
|
64
71
|
</Typography>
|
|
65
72
|
{showShareByEmail && (
|
|
66
73
|
<DumbShareByEmail
|
|
67
|
-
createContact={createContact}
|
|
68
74
|
currentRecipients={recipients}
|
|
69
75
|
document={document}
|
|
70
76
|
documentType={documentType}
|
|
71
|
-
onShare={onShare}
|
|
72
77
|
sharing={sharing}
|
|
73
|
-
|
|
78
|
+
pendingRecipients={pendingRecipients}
|
|
79
|
+
onPendingRecipientsChange={onPendingRecipientsChange}
|
|
80
|
+
selectedOption={selectedOption}
|
|
81
|
+
onSelectedOptionChange={onSelectedOptionChange}
|
|
74
82
|
/>
|
|
75
83
|
)}
|
|
76
84
|
</div>
|
|
@@ -92,55 +100,180 @@ const SharingContent = ({
|
|
|
92
100
|
)
|
|
93
101
|
}
|
|
94
102
|
|
|
95
|
-
/**
|
|
96
|
-
* Displays the dialog's title that can be used when ShareDialogCozyToCozy is in sharing state
|
|
97
|
-
*/
|
|
98
103
|
const SharingTitleFunction = ({ documentType, document }) =>
|
|
99
104
|
function SharingTitle() {
|
|
100
105
|
const { t } = useI18n()
|
|
101
|
-
|
|
102
106
|
const title = t(`${documentType}.share.title`, {
|
|
103
107
|
name: document.name || document.attributes?.name
|
|
104
108
|
})
|
|
105
|
-
|
|
106
109
|
return title
|
|
107
110
|
}
|
|
108
111
|
|
|
109
|
-
/**
|
|
110
|
-
* Displays a sharing dialog that allows to share a document between multiple Cozy users
|
|
111
|
-
*/
|
|
112
112
|
const ShareDialogCozyToCozy = ({
|
|
113
113
|
showShareByLink,
|
|
114
114
|
showGenerateLinkButton,
|
|
115
115
|
autoOpenShareRestriction,
|
|
116
116
|
documentType,
|
|
117
117
|
document,
|
|
118
|
+
onShare,
|
|
119
|
+
recipients,
|
|
120
|
+
sharingDesc,
|
|
121
|
+
createContact,
|
|
122
|
+
onClose,
|
|
118
123
|
...props
|
|
119
124
|
}) => {
|
|
125
|
+
const { t } = useI18n()
|
|
126
|
+
const client = useClient()
|
|
127
|
+
const { showAlert } = useAlert()
|
|
128
|
+
|
|
129
|
+
const {
|
|
130
|
+
pendingRecipients,
|
|
131
|
+
setPendingRecipients,
|
|
132
|
+
selectedOption,
|
|
133
|
+
setSelectedOption
|
|
134
|
+
} = usePendingRecipients()
|
|
135
|
+
|
|
136
|
+
const [submitting, setSubmitting] = useState(false)
|
|
137
|
+
const [showRecipientsLimit, setShowRecipientsLimit] = useState(false)
|
|
138
|
+
|
|
139
|
+
const handleShare = useCallback(async () => {
|
|
140
|
+
if (pendingRecipients.length === 0) {
|
|
141
|
+
onClose()
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (hasReachRecipientsLimit(recipients, pendingRecipients)) {
|
|
146
|
+
setShowRecipientsLimit(true)
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
setSubmitting(true)
|
|
151
|
+
try {
|
|
152
|
+
const contacts = await getOrCreateFromArray(
|
|
153
|
+
client,
|
|
154
|
+
pendingRecipients,
|
|
155
|
+
createContact
|
|
156
|
+
)
|
|
157
|
+
const readWriteRecipients = selectedOption === 'readOnly' ? [] : contacts
|
|
158
|
+
const readOnlyRecipients = selectedOption === 'readOnly' ? contacts : []
|
|
159
|
+
|
|
160
|
+
await onShare({
|
|
161
|
+
document,
|
|
162
|
+
recipients: readWriteRecipients,
|
|
163
|
+
readOnlyRecipients,
|
|
164
|
+
description: sharingDesc,
|
|
165
|
+
openSharing: readWriteRecipients.length > 0
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
showAlert({
|
|
169
|
+
message: t(...getSuccessMessage(recipients, contacts, documentType)),
|
|
170
|
+
severity: 'success',
|
|
171
|
+
variant: 'filled'
|
|
172
|
+
})
|
|
173
|
+
onClose()
|
|
174
|
+
} catch (err) {
|
|
175
|
+
showAlert({
|
|
176
|
+
message: t(
|
|
177
|
+
...getErrorMessage({
|
|
178
|
+
t,
|
|
179
|
+
err,
|
|
180
|
+
documentType,
|
|
181
|
+
recipients: pendingRecipients,
|
|
182
|
+
selectedOption
|
|
183
|
+
})
|
|
184
|
+
),
|
|
185
|
+
severity: 'error',
|
|
186
|
+
variant: 'filled'
|
|
187
|
+
})
|
|
188
|
+
} finally {
|
|
189
|
+
setSubmitting(false)
|
|
190
|
+
}
|
|
191
|
+
}, [
|
|
192
|
+
pendingRecipients,
|
|
193
|
+
selectedOption,
|
|
194
|
+
client,
|
|
195
|
+
showAlert,
|
|
196
|
+
t,
|
|
197
|
+
recipients,
|
|
198
|
+
onShare,
|
|
199
|
+
onClose,
|
|
200
|
+
createContact,
|
|
201
|
+
document,
|
|
202
|
+
documentType,
|
|
203
|
+
sharingDesc
|
|
204
|
+
])
|
|
205
|
+
|
|
120
206
|
const dialogActionsOnShare = useMemo(() => {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
207
|
+
const Actions = dialogActionProps => (
|
|
208
|
+
<>
|
|
209
|
+
{showShareByLink && (
|
|
210
|
+
<DumbShareByLink
|
|
211
|
+
{...dialogActionProps}
|
|
212
|
+
showGenerateLinkButton={showGenerateLinkButton}
|
|
213
|
+
autoOpenShareRestriction={autoOpenShareRestriction}
|
|
214
|
+
/>
|
|
215
|
+
)}
|
|
216
|
+
<Button
|
|
217
|
+
variant="primary"
|
|
218
|
+
label={t(`${documentType}.share.shareByEmail.send`)}
|
|
219
|
+
busy={submitting}
|
|
220
|
+
onClick={handleShare}
|
|
221
|
+
/>
|
|
222
|
+
</>
|
|
128
223
|
)
|
|
129
|
-
|
|
130
|
-
return
|
|
131
|
-
}, [
|
|
224
|
+
Actions.displayName = 'ShareDialogCozyToCozyActions'
|
|
225
|
+
return Actions
|
|
226
|
+
}, [
|
|
227
|
+
handleShare,
|
|
228
|
+
submitting,
|
|
229
|
+
t,
|
|
230
|
+
documentType,
|
|
231
|
+
showShareByLink,
|
|
232
|
+
showGenerateLinkButton,
|
|
233
|
+
autoOpenShareRestriction
|
|
234
|
+
])
|
|
132
235
|
|
|
133
236
|
return (
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
237
|
+
<>
|
|
238
|
+
<ShareDialogTwoStepsConfirmationContainer
|
|
239
|
+
{...props}
|
|
240
|
+
documentType={documentType}
|
|
241
|
+
document={document}
|
|
242
|
+
recipients={recipients}
|
|
243
|
+
sharingDesc={sharingDesc}
|
|
244
|
+
createContact={createContact}
|
|
245
|
+
onShare={onShare}
|
|
246
|
+
onClose={onClose}
|
|
247
|
+
dialogContentOnShare={SharingContent}
|
|
248
|
+
dialogActionsOnShare={dialogActionsOnShare}
|
|
249
|
+
dialogTitleOnShare={SharingTitleFunction({ documentType, document })}
|
|
250
|
+
disableGutters
|
|
251
|
+
pendingRecipients={pendingRecipients}
|
|
252
|
+
onPendingRecipientsChange={setPendingRecipients}
|
|
253
|
+
selectedOption={selectedOption}
|
|
254
|
+
onSelectedOptionChange={setSelectedOption}
|
|
255
|
+
/>
|
|
256
|
+
{showRecipientsLimit && (
|
|
257
|
+
<ShareRecipientsLimitModal
|
|
258
|
+
documentName={document?.name}
|
|
259
|
+
onConfirm={() => setShowRecipientsLimit(false)}
|
|
260
|
+
/>
|
|
261
|
+
)}
|
|
262
|
+
</>
|
|
143
263
|
)
|
|
144
264
|
}
|
|
145
265
|
|
|
266
|
+
ShareDialogCozyToCozy.propTypes = {
|
|
267
|
+
showShareByLink: PropTypes.bool,
|
|
268
|
+
showGenerateLinkButton: PropTypes.bool,
|
|
269
|
+
autoOpenShareRestriction: PropTypes.bool,
|
|
270
|
+
documentType: PropTypes.string.isRequired,
|
|
271
|
+
document: PropTypes.object.isRequired,
|
|
272
|
+
onShare: PropTypes.func.isRequired,
|
|
273
|
+
recipients: PropTypes.array.isRequired,
|
|
274
|
+
sharingDesc: PropTypes.string,
|
|
275
|
+
createContact: PropTypes.func.isRequired,
|
|
276
|
+
onClose: PropTypes.func.isRequired
|
|
277
|
+
}
|
|
278
|
+
|
|
146
279
|
export default ShareDialogCozyToCozy
|
|
@@ -20,6 +20,12 @@ jest.mock('../hooks/useSharingContext', () => ({
|
|
|
20
20
|
})
|
|
21
21
|
}))
|
|
22
22
|
|
|
23
|
+
jest.mock('../helpers/contacts', () => ({
|
|
24
|
+
getOrCreateFromArray: jest.fn((client, recipients) =>
|
|
25
|
+
Promise.resolve(recipients)
|
|
26
|
+
)
|
|
27
|
+
}))
|
|
28
|
+
|
|
23
29
|
describe('ShareDialogCozyToCozy', () => {
|
|
24
30
|
const client = createMockClient({})
|
|
25
31
|
client.options = {
|
|
@@ -42,7 +48,7 @@ describe('ShareDialogCozyToCozy', () => {
|
|
|
42
48
|
const { getByText } = setup(props)
|
|
43
49
|
|
|
44
50
|
expect(getByText('You')).toBeTruthy()
|
|
45
|
-
expect(getByText('
|
|
51
|
+
expect(getByText('Owner')).toBeTruthy()
|
|
46
52
|
})
|
|
47
53
|
|
|
48
54
|
it('should show loading while calling getRecipientsToBeConfirmed', async () => {
|
|
@@ -163,6 +169,65 @@ describe('ShareDialogCozyToCozy', () => {
|
|
|
163
169
|
).not.toBeInTheDocument()
|
|
164
170
|
})
|
|
165
171
|
|
|
172
|
+
it('main Share button calls onClose when there are no pending chips', () => {
|
|
173
|
+
const onClose = jest.fn()
|
|
174
|
+
const onShare = jest.fn()
|
|
175
|
+
const props = { ...getMockProps(), onClose, onShare }
|
|
176
|
+
|
|
177
|
+
const { getByRole } = setup(props)
|
|
178
|
+
|
|
179
|
+
fireEvent.click(getByRole('button', { name: 'Share' }))
|
|
180
|
+
|
|
181
|
+
expect(onShare).not.toHaveBeenCalled()
|
|
182
|
+
expect(onClose).toHaveBeenCalledTimes(1)
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('main Share button calls onShare with chips then closes', async () => {
|
|
186
|
+
const onClose = jest.fn()
|
|
187
|
+
const onShare = jest.fn().mockResolvedValue(undefined)
|
|
188
|
+
const props = { ...getMockProps(), onClose, onShare }
|
|
189
|
+
|
|
190
|
+
const { getByPlaceholderText, getByRole } = setup(props)
|
|
191
|
+
|
|
192
|
+
act(() => {
|
|
193
|
+
fireEvent.change(
|
|
194
|
+
getByPlaceholderText(
|
|
195
|
+
'Enter the email address or name of the recipient'
|
|
196
|
+
),
|
|
197
|
+
{
|
|
198
|
+
target: { value: 'new@cozycloud.cc' }
|
|
199
|
+
}
|
|
200
|
+
)
|
|
201
|
+
})
|
|
202
|
+
act(() => {
|
|
203
|
+
fireEvent.keyPress(
|
|
204
|
+
getByPlaceholderText(
|
|
205
|
+
'Enter the email address or name of the recipient'
|
|
206
|
+
),
|
|
207
|
+
{
|
|
208
|
+
key: 'Enter',
|
|
209
|
+
code: 'Enter',
|
|
210
|
+
charCode: 13
|
|
211
|
+
}
|
|
212
|
+
)
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
await act(async () => {
|
|
216
|
+
fireEvent.click(getByRole('button', { name: 'Share' }))
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
expect(onShare).toHaveBeenCalledWith(
|
|
220
|
+
expect.objectContaining({
|
|
221
|
+
document: props.document,
|
|
222
|
+
recipients: [{ email: 'new@cozycloud.cc' }],
|
|
223
|
+
readOnlyRecipients: [],
|
|
224
|
+
description: props.sharingDesc,
|
|
225
|
+
openSharing: true
|
|
226
|
+
})
|
|
227
|
+
)
|
|
228
|
+
expect(onClose).toHaveBeenCalledTimes(1)
|
|
229
|
+
})
|
|
230
|
+
|
|
166
231
|
it(`should display confirmation of 'contact rejection' when user click on the 'reject' button`, async () => {
|
|
167
232
|
const { promise, resolve } = deferablePromise()
|
|
168
233
|
|
|
@@ -372,6 +437,7 @@ const getMockProps = () => {
|
|
|
372
437
|
showWhoHasAccess: true,
|
|
373
438
|
onRevoke: jest.fn(),
|
|
374
439
|
onShare: jest.fn(),
|
|
440
|
+
onClose: jest.fn(),
|
|
375
441
|
createContact: jest.fn()
|
|
376
442
|
}
|
|
377
443
|
}
|
|
@@ -108,7 +108,11 @@ const ShareDialogTwoStepsConfirmationContainer = ({
|
|
|
108
108
|
twoStepsConfirmationMethods = {},
|
|
109
109
|
dialogContentOnShare: DialogContentOnShare,
|
|
110
110
|
dialogActionsOnShare: DialogActionsOnShare,
|
|
111
|
-
dialogTitleOnShare: DialogTitleOnShare
|
|
111
|
+
dialogTitleOnShare: DialogTitleOnShare,
|
|
112
|
+
pendingRecipients,
|
|
113
|
+
onPendingRecipientsChange,
|
|
114
|
+
selectedOption,
|
|
115
|
+
onSelectedOptionChange
|
|
112
116
|
}) => {
|
|
113
117
|
const { t } = useI18n()
|
|
114
118
|
|
|
@@ -222,6 +226,10 @@ const ShareDialogTwoStepsConfirmationContainer = ({
|
|
|
222
226
|
permissions={permissions}
|
|
223
227
|
onUpdateShareLinkPermissions={onUpdateShareLinkPermissions}
|
|
224
228
|
onRevokeLink={onRevokeLink}
|
|
229
|
+
pendingRecipients={pendingRecipients}
|
|
230
|
+
onPendingRecipientsChange={onPendingRecipientsChange}
|
|
231
|
+
selectedOption={selectedOption}
|
|
232
|
+
onSelectedOptionChange={onSelectedOptionChange}
|
|
225
233
|
/>
|
|
226
234
|
)
|
|
227
235
|
|
|
@@ -230,6 +238,8 @@ const ShareDialogTwoStepsConfirmationContainer = ({
|
|
|
230
238
|
link={link}
|
|
231
239
|
document={document}
|
|
232
240
|
documentType={documentType}
|
|
241
|
+
pendingRecipients={pendingRecipients}
|
|
242
|
+
selectedOption={selectedOption}
|
|
233
243
|
/>
|
|
234
244
|
) : null
|
|
235
245
|
} else if (status === 'confirmingRecipient') {
|
|
@@ -23,7 +23,8 @@ const ShareRecipientsInput = ({
|
|
|
23
23
|
placeholder,
|
|
24
24
|
onPick,
|
|
25
25
|
onRemove,
|
|
26
|
-
disabled
|
|
26
|
+
disabled,
|
|
27
|
+
endAdornment
|
|
27
28
|
}) => {
|
|
28
29
|
const reachableContactsQuery = buildReachableContactsQuery()
|
|
29
30
|
const reachableContactsResult = useQueryAll(
|
|
@@ -109,6 +110,7 @@ const ShareRecipientsInput = ({
|
|
|
109
110
|
onPick={onPick}
|
|
110
111
|
onRemove={onRemove}
|
|
111
112
|
placeholder={placeholder}
|
|
113
|
+
endAdornment={endAdornment}
|
|
112
114
|
/>
|
|
113
115
|
)
|
|
114
116
|
}
|
|
@@ -119,7 +121,8 @@ ShareRecipientsInput.propTypes = {
|
|
|
119
121
|
placeholder: PropTypes.string,
|
|
120
122
|
onPick: PropTypes.func.isRequired,
|
|
121
123
|
onRemove: PropTypes.func.isRequired,
|
|
122
|
-
disabled: PropTypes.bool
|
|
124
|
+
disabled: PropTypes.bool,
|
|
125
|
+
endAdornment: PropTypes.node
|
|
123
126
|
}
|
|
124
127
|
|
|
125
128
|
ShareRecipientsInput.defaultProps = {
|