cozy-sharing 28.3.4 → 28.4.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 (40) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/components/FederatedFolder/DumbFederatedFolderModal.js +56 -0
  3. package/dist/components/FederatedFolder/FederatedFolderModal.js +150 -0
  4. package/dist/components/FederatedFolder/FederatedFolderModal.spec.js +543 -0
  5. package/dist/components/Recipient/LinkRecipient.js +1 -1
  6. package/dist/components/ShareAutosuggest.js +4 -1
  7. package/dist/components/ShareByEmail.js +105 -33
  8. package/dist/components/ShareByEmail.spec.js +111 -9
  9. package/dist/components/ShareByLink.js +3 -3
  10. package/dist/components/ShareModal/ShareModal.js +18 -2
  11. package/dist/components/ShareModal.js +1 -1
  12. package/dist/components/ShareRecipientsInput.js +5 -2
  13. package/dist/components/SharedDrive/DumbSharedDriveModal.js +39 -72
  14. package/dist/components/SharedDrive/SharedDriveModal.js +11 -5
  15. package/dist/components/SharedDrive/helpers.js +28 -2
  16. package/dist/components/SharedFolder/DumbBatchSharedFolderModal.js +159 -0
  17. package/dist/components/WhoHasAccess.js +1 -1
  18. package/dist/index.js +1 -0
  19. package/locales/en.json +13 -1
  20. package/locales/fr.json +15 -3
  21. package/locales/ru.json +13 -1
  22. package/locales/vi.json +13 -1
  23. package/package.json +3 -3
  24. package/src/components/FederatedFolder/DumbFederatedFolderModal.jsx +62 -0
  25. package/src/components/FederatedFolder/FederatedFolderModal.jsx +119 -0
  26. package/src/components/FederatedFolder/FederatedFolderModal.spec.jsx +376 -0
  27. package/src/components/Recipient/LinkRecipient.jsx +1 -1
  28. package/src/components/ShareAutosuggest.jsx +4 -1
  29. package/src/components/ShareByEmail.jsx +47 -8
  30. package/src/components/ShareByEmail.spec.jsx +96 -6
  31. package/src/components/ShareByLink.jsx +7 -3
  32. package/src/components/ShareModal/ShareModal.jsx +16 -1
  33. package/src/components/ShareModal.jsx +1 -1
  34. package/src/components/ShareRecipientsInput.jsx +5 -2
  35. package/src/components/SharedDrive/DumbSharedDriveModal.jsx +38 -88
  36. package/src/components/SharedDrive/SharedDriveModal.jsx +14 -4
  37. package/src/components/SharedDrive/helpers.js +33 -2
  38. package/src/components/SharedFolder/DumbBatchSharedFolderModal.jsx +174 -0
  39. package/src/components/WhoHasAccess.jsx +1 -1
  40. package/src/index.jsx +1 -0
@@ -0,0 +1,119 @@
1
+ import PropTypes from 'prop-types'
2
+ import React, { useState } from 'react'
3
+ import { useI18n } from 'twake-i18n'
4
+
5
+ import { useClient } from 'cozy-client'
6
+ import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
7
+
8
+ import { DumbFederatedFolderModal } from './DumbFederatedFolderModal'
9
+ import withLocales from '../../hoc/withLocales'
10
+ import { useSharingContext } from '../../hooks/useSharingContext'
11
+ import {
12
+ formatRecipients,
13
+ moveRecipientToReadWrite,
14
+ moveRecipientToReadOnly,
15
+ RECIPIENT_INDEX_PREFIX
16
+ } from '../SharedDrive/helpers'
17
+
18
+ export const FederatedFolderModal = withLocales(
19
+ ({ onClose, document: existingDocument }) => {
20
+ const client = useClient()
21
+ const { t } = useI18n()
22
+ const { share, getSharingLink } = useSharingContext()
23
+ const { showAlert } = useAlert()
24
+
25
+ // Get sharing link
26
+ const sharingLink = existingDocument
27
+ ? getSharingLink(existingDocument._id)
28
+ : null
29
+
30
+ const [federatedRecipients, setFederatedRecipients] = useState({
31
+ recipients: [],
32
+ readOnlyRecipients: []
33
+ })
34
+ const [folderName] = useState(existingDocument?.name || '')
35
+
36
+ const onShare = params => {
37
+ setFederatedRecipients({
38
+ recipients: params.recipients || [],
39
+ readOnlyRecipients: params.readOnlyRecipients || []
40
+ })
41
+ }
42
+
43
+ const onSend = async () => {
44
+ try {
45
+ await share({
46
+ description: folderName,
47
+ document: existingDocument,
48
+ recipients: federatedRecipients.recipients,
49
+ readOnlyRecipients: federatedRecipients.readOnlyRecipients,
50
+ sharedDrive: true,
51
+ openSharing: false
52
+ })
53
+
54
+ showAlert({
55
+ message: t('FederatedFolder.successNotification'),
56
+ severity: 'success',
57
+ variant: 'filled'
58
+ })
59
+
60
+ onClose()
61
+ } catch (err) {
62
+ showAlert({
63
+ message: t('FederatedFolder.errorNotification'),
64
+ severity: 'error',
65
+ variant: 'filled'
66
+ })
67
+ }
68
+ }
69
+
70
+ const onSetType = (index, newType) => {
71
+ const _id = index.split(RECIPIENT_INDEX_PREFIX)[1]
72
+
73
+ if (newType === 'two-way') {
74
+ setFederatedRecipients(prev => moveRecipientToReadWrite(prev, _id))
75
+ } else {
76
+ setFederatedRecipients(prev => moveRecipientToReadOnly(prev, _id))
77
+ }
78
+ }
79
+
80
+ const onRevoke = index => {
81
+ const _id = index.split(RECIPIENT_INDEX_PREFIX)[1]
82
+
83
+ setFederatedRecipients(prev => {
84
+ return {
85
+ recipients: prev.recipients.filter(r => r._id !== _id),
86
+ readOnlyRecipients: prev.readOnlyRecipients.filter(r => r._id !== _id)
87
+ }
88
+ })
89
+ }
90
+
91
+ const recipients = formatRecipients(federatedRecipients)
92
+
93
+ const modalTitle = t('FederatedFolder.shareTitle', { name: folderName })
94
+
95
+ return (
96
+ <DumbFederatedFolderModal
97
+ title={modalTitle}
98
+ document={existingDocument}
99
+ createContact={contact => client.create('io.cozy.contacts', contact)}
100
+ recipients={recipients}
101
+ readOnlyRecipients={federatedRecipients.readOnlyRecipients}
102
+ currentRecipients={[]}
103
+ onRevoke={onRevoke}
104
+ onSetType={onSetType}
105
+ onSend={onSend}
106
+ onClose={onClose}
107
+ onShare={onShare}
108
+ sharingLink={sharingLink}
109
+ />
110
+ )
111
+ }
112
+ )
113
+
114
+ FederatedFolderModal.propTypes = {
115
+ onClose: PropTypes.func.isRequired,
116
+ document: PropTypes.object.isRequired
117
+ }
118
+
119
+ export default FederatedFolderModal
@@ -0,0 +1,376 @@
1
+ import { fireEvent, render, waitFor } from '@testing-library/react'
2
+ import React from 'react'
3
+
4
+ import { createMockClient } from 'cozy-client'
5
+
6
+ import { FederatedFolderModal } from './FederatedFolderModal'
7
+ import AppLike from '../SharingBanner/test/AppLike'
8
+
9
+ const mockShare = jest.fn()
10
+ const mockGetSharingLink = jest.fn()
11
+ const mockShowAlert = jest.fn()
12
+ const mockOnClose = jest.fn()
13
+
14
+ jest.mock('../../hooks/useSharingContext', () => ({
15
+ useSharingContext: () => ({
16
+ share: mockShare,
17
+ getSharingLink: mockGetSharingLink,
18
+ getDocumentPermissions: jest.fn().mockReturnValue([])
19
+ })
20
+ }))
21
+
22
+ jest.mock('cozy-ui/transpiled/react/providers/Alert', () => ({
23
+ useAlert: () => ({
24
+ showAlert: mockShowAlert
25
+ })
26
+ }))
27
+
28
+ jest.mock('../../hoc/withLocales', () => Component => Component)
29
+
30
+ jest.mock('./DumbFederatedFolderModal', () => ({
31
+ DumbFederatedFolderModal: ({
32
+ title,
33
+ createContact,
34
+ recipients,
35
+ readOnlyRecipients,
36
+ onRevoke,
37
+ onSetType,
38
+ onSend,
39
+ onClose,
40
+ onShare,
41
+ sharingLink
42
+ }) => (
43
+ <div data-testid="dumb-modal">
44
+ <span data-testid="title">{title}</span>
45
+ <span data-testid="sharing-link">{sharingLink}</span>
46
+ <span data-testid="recipients-count">{recipients.length}</span>
47
+ <span data-testid="readOnly-recipients-count">
48
+ {readOnlyRecipients ? readOnlyRecipients.length : 0}
49
+ </span>
50
+ <button
51
+ data-testid="btn-share"
52
+ onClick={() =>
53
+ onShare({
54
+ recipients: [{ _id: 'r1', displayName: 'Alice' }],
55
+ readOnlyRecipients: [{ _id: 'r2', displayName: 'Bob' }]
56
+ })
57
+ }
58
+ >
59
+ Share
60
+ </button>
61
+ <button data-testid="btn-send" onClick={onSend}>
62
+ Send
63
+ </button>
64
+ <button
65
+ data-testid="btn-set-type-two-way"
66
+ onClick={() => onSetType('virtual-shared-drive-sharing-r2', 'two-way')}
67
+ >
68
+ Set Two-Way
69
+ </button>
70
+ <button
71
+ data-testid="btn-set-type-one-way"
72
+ onClick={() => onSetType('virtual-shared-drive-sharing-r1', 'one-way')}
73
+ >
74
+ Set One-Way
75
+ </button>
76
+ <button
77
+ data-testid="btn-revoke"
78
+ onClick={() => onRevoke('virtual-shared-drive-sharing-r1')}
79
+ >
80
+ Revoke
81
+ </button>
82
+ <button data-testid="btn-close" onClick={onClose}>
83
+ Close
84
+ </button>
85
+ <button
86
+ data-testid="btn-create-contact"
87
+ onClick={() => createContact({ email: 'test@example.com' })}
88
+ >
89
+ Create Contact
90
+ </button>
91
+ </div>
92
+ )
93
+ }))
94
+
95
+ const mockDocument = {
96
+ _id: 'folder-123',
97
+ name: 'My Test Folder',
98
+ path: '/test/folder'
99
+ }
100
+
101
+ const createSharingContextValue = () => ({
102
+ refresh: jest.fn(),
103
+ hasWriteAccess: jest.fn(),
104
+ getRecipients: jest.fn(),
105
+ getSharingLink: mockGetSharingLink,
106
+ getDocumentPermissions: jest.fn().mockReturnValue([]),
107
+ share: mockShare
108
+ })
109
+
110
+ const createTestClient = () => {
111
+ const client = createMockClient({
112
+ queries: {
113
+ 'io.cozy.contacts/reachable': {
114
+ doctype: 'io.cozy.contacts',
115
+ data: []
116
+ },
117
+ 'io.cozy.contacts/groups': {
118
+ doctype: 'io.cozy.contacts.groups',
119
+ data: []
120
+ },
121
+ 'io.cozy.contacts/unreachable-with-groups': {
122
+ doctype: 'io.cozy.contacts',
123
+ data: []
124
+ }
125
+ }
126
+ })
127
+ client.create = jest.fn().mockResolvedValue({ data: {} })
128
+ return client
129
+ }
130
+
131
+ describe('FederatedFolderModal', () => {
132
+ let client
133
+ let sharingContextValue
134
+
135
+ beforeEach(() => {
136
+ jest.clearAllMocks()
137
+ mockGetSharingLink.mockReturnValue('https://example.com/share/abc123')
138
+ client = createTestClient()
139
+ sharingContextValue = createSharingContextValue()
140
+ })
141
+
142
+ const setup = (props = {}) => {
143
+ return render(
144
+ <AppLike client={client} sharingContextValue={sharingContextValue}>
145
+ <FederatedFolderModal
146
+ document={mockDocument}
147
+ onClose={mockOnClose}
148
+ {...props}
149
+ />
150
+ </AppLike>
151
+ )
152
+ }
153
+
154
+ describe('initialization', () => {
155
+ it('should pass document name as title', async () => {
156
+ const { getByTestId } = setup()
157
+
158
+ await waitFor(() => {
159
+ expect(getByTestId('title').textContent).toBe('Share "My Test Folder"')
160
+ })
161
+ })
162
+
163
+ it('should pass default title when document has no name', async () => {
164
+ const documentWithoutName = { _id: 'folder-456', path: '/test' }
165
+ const { getByTestId } = setup({ document: documentWithoutName })
166
+
167
+ await waitFor(() => {
168
+ expect(getByTestId('title').textContent).toBe('Share ""')
169
+ })
170
+ })
171
+
172
+ it('should pass sharing link to DumbFederatedFolderModal', async () => {
173
+ const { getByTestId } = setup()
174
+
175
+ await waitFor(() => {
176
+ expect(getByTestId('sharing-link').textContent).toBe(
177
+ 'https://example.com/share/abc123'
178
+ )
179
+ })
180
+ })
181
+
182
+ it('should pass null sharing link when document is undefined', async () => {
183
+ const { getByTestId } = setup({ document: undefined })
184
+
185
+ await waitFor(() => {
186
+ expect(getByTestId('sharing-link').textContent).toBe('')
187
+ })
188
+ })
189
+ })
190
+
191
+ describe('onShare callback', () => {
192
+ it('should update recipients when onShare is called', async () => {
193
+ const { getByTestId } = setup()
194
+
195
+ await waitFor(() => {
196
+ expect(getByTestId('recipients-count').textContent).toBe('0')
197
+ })
198
+
199
+ fireEvent.click(getByTestId('btn-share'))
200
+
201
+ await waitFor(() => {
202
+ expect(getByTestId('recipients-count').textContent).toBe('2')
203
+ })
204
+ })
205
+ })
206
+
207
+ describe('onSend callback', () => {
208
+ it('should call share with correct parameters', async () => {
209
+ mockShare.mockResolvedValueOnce({})
210
+ const { getByTestId } = setup()
211
+
212
+ await waitFor(() => {
213
+ expect(getByTestId('btn-send')).toBeTruthy()
214
+ })
215
+
216
+ fireEvent.click(getByTestId('btn-share'))
217
+ fireEvent.click(getByTestId('btn-send'))
218
+
219
+ await waitFor(() => {
220
+ expect(mockShare).toHaveBeenCalledWith({
221
+ description: 'My Test Folder',
222
+ document: mockDocument,
223
+ recipients: [{ _id: 'r1', displayName: 'Alice' }],
224
+ readOnlyRecipients: [{ _id: 'r2', displayName: 'Bob' }],
225
+ sharedDrive: true,
226
+ openSharing: false
227
+ })
228
+ })
229
+ })
230
+
231
+ it('should show success alert and close modal on successful share', async () => {
232
+ mockShare.mockResolvedValueOnce({})
233
+ const { getByTestId } = setup()
234
+
235
+ await waitFor(() => {
236
+ expect(getByTestId('btn-send')).toBeTruthy()
237
+ })
238
+
239
+ fireEvent.click(getByTestId('btn-send'))
240
+
241
+ await waitFor(() => {
242
+ expect(mockShowAlert).toHaveBeenCalledWith({
243
+ message: 'Folder has been shared',
244
+ severity: 'success',
245
+ variant: 'filled'
246
+ })
247
+ expect(mockOnClose).toHaveBeenCalled()
248
+ })
249
+ })
250
+
251
+ it('should show error alert when share fails', async () => {
252
+ mockShare.mockRejectedValueOnce(new Error('Share failed'))
253
+ const { getByTestId } = setup()
254
+
255
+ await waitFor(() => {
256
+ expect(getByTestId('btn-send')).toBeTruthy()
257
+ })
258
+
259
+ fireEvent.click(getByTestId('btn-send'))
260
+
261
+ await waitFor(() => {
262
+ expect(mockShowAlert).toHaveBeenCalledWith({
263
+ message: 'Error while sharing folder',
264
+ severity: 'error',
265
+ variant: 'filled'
266
+ })
267
+ expect(mockOnClose).not.toHaveBeenCalled()
268
+ })
269
+ })
270
+ })
271
+
272
+ describe('onSetType callback', () => {
273
+ it('should move recipient from readOnlyRecipients to recipients when setting two-way', async () => {
274
+ const { getByTestId } = setup()
275
+
276
+ await waitFor(() => {
277
+ expect(getByTestId('btn-share')).toBeTruthy()
278
+ })
279
+
280
+ fireEvent.click(getByTestId('btn-share'))
281
+
282
+ // After btn-share: combined recipients = 2 (1 readWrite + 1 readOnly)
283
+ await waitFor(() => {
284
+ expect(getByTestId('recipients-count').textContent).toBe('2')
285
+ expect(getByTestId('readOnly-recipients-count').textContent).toBe('1')
286
+ })
287
+
288
+ fireEvent.click(getByTestId('btn-set-type-two-way'))
289
+
290
+ // After setting two-way: Bob moves from readOnlyRecipients to recipients
291
+ await waitFor(() => {
292
+ expect(getByTestId('recipients-count').textContent).toBe('2')
293
+ expect(getByTestId('readOnly-recipients-count').textContent).toBe('0')
294
+ })
295
+ })
296
+
297
+ it('should move recipient from recipients to readOnlyRecipients when setting one-way', async () => {
298
+ const { getByTestId } = setup()
299
+
300
+ await waitFor(() => {
301
+ expect(getByTestId('btn-share')).toBeTruthy()
302
+ })
303
+
304
+ fireEvent.click(getByTestId('btn-share'))
305
+
306
+ // After btn-share: combined recipients = 2 (1 readWrite + 1 readOnly)
307
+ await waitFor(() => {
308
+ expect(getByTestId('recipients-count').textContent).toBe('2')
309
+ expect(getByTestId('readOnly-recipients-count').textContent).toBe('1')
310
+ })
311
+
312
+ fireEvent.click(getByTestId('btn-set-type-one-way'))
313
+
314
+ // After setting one-way: Alice moves from recipients to readOnlyRecipients
315
+ // Combined count stays at 2, but readOnlyRecipients increases from 1 to 2
316
+ await waitFor(() => {
317
+ expect(getByTestId('recipients-count').textContent).toBe('2')
318
+ expect(getByTestId('readOnly-recipients-count').textContent).toBe('2')
319
+ })
320
+ })
321
+ })
322
+
323
+ describe('onRevoke callback', () => {
324
+ it('should remove recipient from both lists', async () => {
325
+ const { getByTestId } = setup()
326
+
327
+ await waitFor(() => {
328
+ expect(getByTestId('btn-share')).toBeTruthy()
329
+ })
330
+
331
+ fireEvent.click(getByTestId('btn-share'))
332
+
333
+ await waitFor(() => {
334
+ expect(getByTestId('recipients-count').textContent).toBe('2')
335
+ })
336
+
337
+ fireEvent.click(getByTestId('btn-revoke'))
338
+
339
+ await waitFor(() => {
340
+ expect(getByTestId('recipients-count').textContent).toBe('1')
341
+ })
342
+ })
343
+ })
344
+
345
+ describe('onClose callback', () => {
346
+ it('should call onClose when close button is clicked', async () => {
347
+ const { getByTestId } = setup()
348
+
349
+ await waitFor(() => {
350
+ expect(getByTestId('btn-close')).toBeTruthy()
351
+ })
352
+
353
+ fireEvent.click(getByTestId('btn-close'))
354
+
355
+ expect(mockOnClose).toHaveBeenCalled()
356
+ })
357
+ })
358
+
359
+ describe('createContact callback', () => {
360
+ it('should call client.create when creating a contact', async () => {
361
+ const { getByTestId } = setup()
362
+
363
+ await waitFor(() => {
364
+ expect(getByTestId('btn-create-contact')).toBeTruthy()
365
+ })
366
+
367
+ fireEvent.click(getByTestId('btn-create-contact'))
368
+
369
+ await waitFor(() => {
370
+ expect(client.create).toHaveBeenCalledWith('io.cozy.contacts', {
371
+ email: 'test@example.com'
372
+ })
373
+ })
374
+ })
375
+ })
376
+ })
@@ -29,7 +29,7 @@ const LinkRecipient = props => {
29
29
  const { recipientConfirmationData, verifyRecipient, link, fadeIn, document } =
30
30
  props
31
31
 
32
- const permissions = getDocumentPermissions(document._id)
32
+ const permissions = getDocumentPermissions(document?._id)
33
33
  const hasPassword = checkIsPermissionHasPassword(permissions)
34
34
  const expiresDate = getPermissionExpiresDate(permissions)
35
35
  const dateFormatted = expiresDate
@@ -20,6 +20,7 @@ import {
20
20
 
21
21
  const ShareAutocomplete = ({
22
22
  loading,
23
+ disabled,
23
24
  contactsAndGroups,
24
25
  recipients,
25
26
  onPick,
@@ -192,7 +193,8 @@ const ShareAutocomplete = ({
192
193
  value: inputValue,
193
194
  type: 'email',
194
195
  placeholder,
195
- className: styles['suggestionInput']
196
+ className: styles['suggestionInput'],
197
+ disabled
196
198
  }}
197
199
  />
198
200
  )
@@ -200,6 +202,7 @@ const ShareAutocomplete = ({
200
202
 
201
203
  ShareAutocomplete.propTypes = {
202
204
  loading: PropTypes.bool,
205
+ disabled: PropTypes.bool,
203
206
  contactsAndGroups: PropTypes.array,
204
207
  recipients: PropTypes.array.isRequired,
205
208
  onPick: PropTypes.func.isRequired,
@@ -29,12 +29,15 @@ export const ShareByEmail = ({
29
29
  currentRecipients,
30
30
  sharing,
31
31
  submitLabel,
32
- showNotifications = true
32
+ showNotifications = true,
33
+ sharedDrive = false
33
34
  }) => {
34
35
  const client = useClient()
35
36
  const { t } = useI18n()
36
37
  const { showAlert } = useAlert()
37
38
 
39
+ const isFederatedMode = flag('drive.federated-shared-folder.enabled')
40
+
38
41
  const [recipients, setRecipients] = useState([])
39
42
  const [loading, setLoading] = useState(false)
40
43
  const [selectedOption, setSelectedOption] = useState('readWrite')
@@ -50,7 +53,39 @@ export const ShareByEmail = ({
50
53
  setSelectedOption(value)
51
54
  }
52
55
 
53
- const onRecipientPick = recipient => {
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
54
89
  const mergedRecipients = flag('sharing.show-recipient-groups')
55
90
  ? mergeRecipients(recipients, recipient)
56
91
  : spreadGroupAndMergeRecipients(recipients, recipient)
@@ -95,7 +130,8 @@ export const ShareByEmail = ({
95
130
  recipients: readWriteRecipients,
96
131
  readOnlyRecipients,
97
132
  description: sharingDesc,
98
- openSharing: readWriteRecipients.length > 0
133
+ openSharing: readWriteRecipients.length > 0,
134
+ sharedDrive
99
135
  })
100
136
 
101
137
  if (showNotifications) {
@@ -130,7 +166,7 @@ export const ShareByEmail = ({
130
166
 
131
167
  const getSharingOptions = () => {
132
168
  const isSharingReadOnly = sharing
133
- ? isReadOnlySharing(sharing, document._id)
169
+ ? isReadOnlySharing(sharing, document?._id)
134
170
  : false
135
171
  const readWrite = {
136
172
  value: 'readWrite',
@@ -162,9 +198,10 @@ export const ShareByEmail = ({
162
198
  onRemove={recipient => onRecipientRemove(recipient)}
163
199
  currentRecipients={currentRecipients}
164
200
  recipients={recipients}
201
+ disabled={loading}
165
202
  />
166
203
  </div>
167
- {showShareControl && (
204
+ {!isFederatedMode && showShareControl && (
168
205
  <div className={styles['share-type-control']}>
169
206
  <ShareTypeSelect
170
207
  value={selectedOption}
@@ -181,7 +218,7 @@ export const ShareByEmail = ({
181
218
  )}
182
219
  {showRecipientsLimit ? (
183
220
  <ShareRecipientsLimitModal
184
- documentName={document.name}
221
+ documentName={document?.name}
185
222
  onConfirm={() => setRecipientsLimit(false)}
186
223
  />
187
224
  ) : null}
@@ -191,7 +228,7 @@ export const ShareByEmail = ({
191
228
 
192
229
  ShareByEmail.propTypes = {
193
230
  currentRecipients: PropTypes.arrayOf(PropTypes.object),
194
- document: PropTypes.object.isRequired,
231
+ document: PropTypes.object,
195
232
  documentType: PropTypes.string.isRequired,
196
233
  sharingDesc: PropTypes.string.isRequired,
197
234
  onShare: PropTypes.func.isRequired,
@@ -201,7 +238,9 @@ ShareByEmail.propTypes = {
201
238
  // Customize the label of the button that submit contacts
202
239
  submitLabel: PropTypes.string,
203
240
  // Display success or error notifications
204
- showNotifications: PropTypes.bool
241
+ showNotifications: PropTypes.bool,
242
+ // Set to true for shared drive context
243
+ sharedDrive: PropTypes.bool
205
244
  }
206
245
 
207
246
  export default ShareByEmail