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
@@ -6,12 +6,24 @@ import flag from 'cozy-flags'
6
6
 
7
7
  import { ShareByEmail } from './ShareByEmail'
8
8
  import AppLike from '../../test/AppLike'
9
+ import { getOrCreateFromArray } from '../helpers/contacts'
9
10
 
10
11
  jest.mock('../helpers/contacts', () => ({
11
- getOrCreateFromArray: (client, recipients) => recipients
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
+ })
12
17
  }))
13
18
 
14
- jest.mock('cozy-flags')
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
+ )
15
27
 
16
28
  describe('ShareByEmailComponent', () => {
17
29
  const defaultDocument = {
@@ -24,7 +36,8 @@ describe('ShareByEmailComponent', () => {
24
36
  const setup = ({
25
37
  sharingDesc = 'test',
26
38
  document = defaultDocument,
27
- currentRecipients = []
39
+ currentRecipients = [],
40
+ sharedDrive = false
28
41
  } = {}) => {
29
42
  const props = {
30
43
  documentType: 'Files',
@@ -32,7 +45,8 @@ describe('ShareByEmailComponent', () => {
32
45
  document,
33
46
  sharingDesc,
34
47
  currentRecipients,
35
- createContact: jest.fn()
48
+ createContact: jest.fn(),
49
+ sharedDrive
36
50
  }
37
51
  return render(
38
52
  <AppLike>
@@ -43,6 +57,13 @@ describe('ShareByEmailComponent', () => {
43
57
 
44
58
  beforeEach(() => {
45
59
  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
+ })
46
67
  })
47
68
 
48
69
  it('shoud call share if submited', async () => {
@@ -77,13 +98,22 @@ describe('ShareByEmailComponent', () => {
77
98
  document: defaultDocument,
78
99
  openSharing: true,
79
100
  readOnlyRecipients: [],
80
- recipients: [{ email: 'quentin@cozycloud.cc' }]
101
+ recipients: [{ email: 'quentin@cozycloud.cc' }],
102
+ sharedDrive: false
81
103
  })
82
104
  })
83
105
  })
84
106
 
85
107
  it('should alert user when it has reached the recipients limit for a document', async () => {
86
- flag.mockReturnValue(2)
108
+ flag.mockImplementation(flagName => {
109
+ if (flagName === 'sharing.recipients-limit') {
110
+ return 2
111
+ }
112
+ if (flagName === 'drive.federated-shared-folder.enabled') {
113
+ return false
114
+ }
115
+ return null
116
+ })
87
117
 
88
118
  setup({
89
119
  currentRecipients: [
@@ -129,4 +159,64 @@ describe('ShareByEmailComponent', () => {
129
159
  expect(onShare).not.toBeCalled()
130
160
  })
131
161
  })
162
+
163
+ describe('federated mode', () => {
164
+ beforeEach(() => {
165
+ flag.mockImplementation(flagName => {
166
+ if (flagName === 'drive.federated-shared-folder.enabled') {
167
+ return true
168
+ }
169
+ return null
170
+ })
171
+ getOrCreateFromArray.mockResolvedValue([
172
+ { _id: 'contact1', email: 'quentin@cozycloud.cc' }
173
+ ])
174
+ })
175
+
176
+ it('should directly share when selecting a recipient in federated mode', async () => {
177
+ const sharingDesc = 'test'
178
+ const createContact = jest.fn()
179
+
180
+ setup({ sharingDesc, createContact })
181
+
182
+ act(() => {
183
+ fireEvent.change(
184
+ screen.getByPlaceholderText('Add contacts or groups'),
185
+ {
186
+ target: { value: 'quentin@cozycloud.cc' }
187
+ }
188
+ )
189
+ })
190
+
191
+ act(() => {
192
+ fireEvent.keyPress(
193
+ screen.getByPlaceholderText('Add contacts or groups'),
194
+ {
195
+ key: 'Enter',
196
+ code: 'Enter',
197
+ charCode: 13
198
+ }
199
+ )
200
+ })
201
+
202
+ await waitFor(() => {
203
+ expect(getOrCreateFromArray).toHaveBeenCalledWith(
204
+ expect.any(Object),
205
+ [{ email: 'quentin@cozycloud.cc' }],
206
+ expect.any(Function)
207
+ )
208
+ })
209
+
210
+ await waitFor(() => {
211
+ expect(onShare).toHaveBeenCalledWith({
212
+ document: defaultDocument,
213
+ recipients: [{ _id: 'contact1', email: 'quentin@cozycloud.cc' }],
214
+ readOnlyRecipients: [],
215
+ description: sharingDesc,
216
+ openSharing: false,
217
+ sharedDrive: true
218
+ })
219
+ })
220
+ })
221
+ })
132
222
  })
@@ -3,6 +3,7 @@ import React, { useReducer, useState } from 'react'
3
3
  import { useI18n } from 'twake-i18n'
4
4
 
5
5
  import { useClient } from 'cozy-client'
6
+ import flag from 'cozy-flags'
6
7
  import Button from 'cozy-ui/transpiled/react/Buttons'
7
8
  import Icon from 'cozy-ui/transpiled/react/Icon'
8
9
  import CopyIcon from 'cozy-ui/transpiled/react/Icons/Copy'
@@ -28,6 +29,7 @@ const ShareByLink = ({ link, document, documentType }) => {
28
29
  const canShare = typeof navigator?.share === 'function'
29
30
  const showCopyAndSendButtons = isMobile && canShare
30
31
  const showOnlyCopyButton = !isMobile || !canShare
32
+ const isFederated = flag('drive.federated-shared-folder.enabled')
31
33
 
32
34
  const [isEditDialogOpen, toggleEditDialogOpen] = useReducer(
33
35
  state => !state,
@@ -106,7 +108,11 @@ const ShareByLink = ({ link, document, documentType }) => {
106
108
  }
107
109
 
108
110
  return (
109
- <div className="u-w-100 u-flex u-flex-justify-center">
111
+ <div
112
+ className={
113
+ isFederated ? 'u-w-100' : 'u-w-100 u-flex u-flex-justify-center'
114
+ }
115
+ >
110
116
  {showCopyAndSendButtons && (
111
117
  <>
112
118
  <Button
@@ -114,7 +120,6 @@ const ShareByLink = ({ link, document, documentType }) => {
114
120
  variant="secondary"
115
121
  size="medium"
116
122
  startIcon={<Icon icon={LinkIcon} />}
117
- className="u-flex-auto u-mr-half"
118
123
  onClick={shareLink}
119
124
  busy={isGenerating}
120
125
  />
@@ -133,7 +138,6 @@ const ShareByLink = ({ link, document, documentType }) => {
133
138
  variant="secondary"
134
139
  size="medium"
135
140
  startIcon={<Icon icon={LinkIcon} />}
136
- className="u-flex-auto u-mr-half"
137
141
  onClick={generateLinkAndCopyLinkToClipboard}
138
142
  busy={isGenerating}
139
143
  />
@@ -3,10 +3,13 @@ import PropTypes from 'prop-types'
3
3
  import React from 'react'
4
4
  import { useLocation } from 'react-router'
5
5
 
6
+ import flag from 'cozy-flags'
7
+
6
8
  import EditableSharingModal from './EditableSharingModal'
7
9
  import { SharingDetailsModal } from './SharingDetailsModal'
8
10
  import withLocales from '../../hoc/withLocales'
9
11
  import { useSharingContext } from '../../hooks/useSharingContext'
12
+ import { FederatedFolderModal } from '../FederatedFolder/FederatedFolderModal'
10
13
 
11
14
  export const ShareModal = withLocales(props => {
12
15
  const location = useLocation?.()
@@ -22,7 +25,8 @@ export const ShareModal = withLocales(props => {
22
25
  getOwner,
23
26
  getSharingType,
24
27
  getRecipients,
25
- revokeSelf
28
+ revokeSelf,
29
+ allLoaded
26
30
  } = useSharingContext()
27
31
 
28
32
  const handleRevokeSelf = async document => {
@@ -30,6 +34,17 @@ export const ShareModal = withLocales(props => {
30
34
  onRevokeSuccess?.(document)
31
35
  }
32
36
 
37
+ const isFederatedMode = flag('drive.federated-shared-folder.enabled')
38
+ const hasSharings = byDocId[document.id]?.sharings?.length > 0
39
+
40
+ // In federated mode, show FederatedFolderModal for unshared documents (sharing an existing folder).
41
+ // When isFederatedMode && hasSharings is true, we intentionally fall through to the legacy
42
+ // modals (EditableSharingModal / SharingDetailsModal) because federated-shared folders are
43
+ // handled by those components.
44
+ if (isFederatedMode && allLoaded && !hasSharings) {
45
+ return <FederatedFolderModal document={document} onClose={rest.onClose} />
46
+ }
47
+
33
48
  const isEditable =
34
49
  !byDocId[document.id] || isOwner(document.id) || canReshare(document.id)
35
50
 
@@ -26,7 +26,7 @@ export const ShareModal = ({
26
26
  sharingDesc,
27
27
  twoStepsConfirmationMethods
28
28
  }) => {
29
- if (isSharedDrive) {
29
+ if (isSharedDrive && !flag('drive.federated-shared-folder.enabled')) {
30
30
  return (
31
31
  <SharedDriveEditModal
32
32
  document={document}
@@ -22,7 +22,8 @@ const ShareRecipientsInput = ({
22
22
  recipients,
23
23
  placeholder,
24
24
  onPick,
25
- onRemove
25
+ onRemove,
26
+ disabled
26
27
  }) => {
27
28
  const reachableContactsQuery = buildReachableContactsQuery()
28
29
  const reachableContactsResult = useQueryAll(
@@ -102,6 +103,7 @@ const ShareRecipientsInput = ({
102
103
  return (
103
104
  <ShareAutosuggest
104
105
  loading={isLoading}
106
+ disabled={disabled}
105
107
  contactsAndGroups={contactsAndGroups}
106
108
  recipients={recipients}
107
109
  onPick={onPick}
@@ -116,7 +118,8 @@ ShareRecipientsInput.propTypes = {
116
118
  recipients: PropTypes.array,
117
119
  placeholder: PropTypes.string,
118
120
  onPick: PropTypes.func.isRequired,
119
- onRemove: PropTypes.func.isRequired
121
+ onRemove: PropTypes.func.isRequired,
122
+ disabled: PropTypes.bool
120
123
  }
121
124
 
122
125
  ShareRecipientsInput.defaultProps = {
@@ -2,15 +2,8 @@ import PropTypes from 'prop-types'
2
2
  import React from 'react'
3
3
  import { useI18n } from 'twake-i18n'
4
4
 
5
- import Button from 'cozy-ui/transpiled/react/Buttons'
6
- import { FixedDialog } from 'cozy-ui/transpiled/react/CozyDialogs'
7
- import TextField from 'cozy-ui/transpiled/react/TextField'
8
- import Typography from 'cozy-ui/transpiled/react/Typography'
9
-
10
5
  import withLocales from '../../hoc/withLocales'
11
- import AntivirusAlert from '../AntivirusAlert'
12
- import { default as DumbShareByEmail } from '../ShareByEmail'
13
- import WhoHasAccess from '../WhoHasAccess'
6
+ import DumbBatchSharedFolderModal from '../SharedFolder/DumbBatchSharedFolderModal'
14
7
 
15
8
  export const DumbSharedDriveModal = withLocales(
16
9
  ({
@@ -20,92 +13,43 @@ export const DumbSharedDriveModal = withLocales(
20
13
  handleSharedDriveNameChange,
21
14
  createContact,
22
15
  recipients,
16
+ currentRecipients,
23
17
  onRevoke,
24
18
  onSetType,
25
19
  onCreate,
20
+ onSend,
26
21
  onClose,
27
22
  onShare,
28
- onRename
23
+ onRename,
24
+ showNameField = true,
25
+ sharingLink
29
26
  }) => {
30
27
  const { t } = useI18n()
31
-
32
28
  return (
33
- <FixedDialog
34
- open
35
- disableGutters
36
- onClose={onClose}
29
+ <DumbBatchSharedFolderModal
37
30
  title={title}
38
- classes={{ paper: 'u-ov-visible' }}
39
- componentsProps={{
40
- dialogContent: { className: 'u-ov-visible' }
41
- }}
42
- content={
43
- <div>
44
- <div className="u-ph-2">
45
- <AntivirusAlert document={document} />
46
- {handleSharedDriveNameChange && (
47
- <TextField
48
- required
49
- label={t('SharedDrive.sharedDriveModal.nameLabel')}
50
- variant="outlined"
51
- size="small"
52
- className="u-w-100 u-mt-1-half"
53
- value={sharedDriveName}
54
- onChange={handleSharedDriveNameChange}
55
- />
56
- )}
57
- <Typography variant="h6" className="u-mt-1-half u-mb-half">
58
- {t('SharedDrive.sharedDriveModal.addPeople')}
59
- </Typography>
60
- <DumbShareByEmail
61
- createContact={createContact}
62
- currentRecipients={[]}
63
- document={document}
64
- documentType="Files"
65
- onShare={onShare}
66
- submitLabel={t('SharedDrive.sharedDriveModal.add')}
67
- showNotifications={false}
68
- />
69
- </div>
70
- <WhoHasAccess
71
- isOwner
72
- isSharedDrive
73
- recipients={recipients}
74
- document={document}
75
- documentType="Files"
76
- className="u-w-100"
77
- onRevoke={onRevoke}
78
- onSetType={onSetType}
79
- />
80
- </div>
81
- }
82
- actions={
83
- <>
84
- {onCreate && (
85
- <>
86
- <Button
87
- variant="secondary"
88
- label={t('SharedDrive.sharedDriveModal.cancel')}
89
- onClick={onClose}
90
- />
91
- <Button
92
- variant="primary"
93
- label={t('SharedDrive.sharedDriveModal.create')}
94
- onClick={onCreate}
95
- />
96
- </>
97
- )}
98
- {onRename && (
99
- <>
100
- <Button
101
- variant="primary"
102
- label={t('SharedDrive.sharedDriveModal.save')}
103
- onClick={() => onRename(sharedDriveName)}
104
- />
105
- </>
106
- )}
107
- </>
108
- }
31
+ document={document}
32
+ folderName={sharedDriveName}
33
+ handleFolderNameChange={handleSharedDriveNameChange}
34
+ createContact={createContact}
35
+ recipients={recipients}
36
+ currentRecipients={currentRecipients}
37
+ onRevoke={onRevoke}
38
+ onSetType={onSetType}
39
+ onCreate={onCreate}
40
+ onSend={onSend}
41
+ onClose={onClose}
42
+ onShare={onShare}
43
+ onRename={onRename}
44
+ showNameField={showNameField}
45
+ sharingLink={sharingLink}
46
+ nameLabel={t('SharedDrive.sharedDriveModal.nameLabel')}
47
+ addPeopleLabel={t('SharedDrive.sharedDriveModal.addPeople')}
48
+ addButtonLabel={t('SharedDrive.sharedDriveModal.add')}
49
+ cancelLabel={t('SharedDrive.sharedDriveModal.cancel')}
50
+ createLabel={t('SharedDrive.sharedDriveModal.create')}
51
+ shareLabel={t('FederatedFolder.share')}
52
+ saveLabel={t('SharedDrive.sharedDriveModal.save')}
109
53
  />
110
54
  )
111
55
  }
@@ -115,13 +59,19 @@ DumbSharedDriveModal.propTypes = {
115
59
  title: PropTypes.string,
116
60
  document: PropTypes.object,
117
61
  sharedDriveName: PropTypes.string,
118
- handleSharedDriveNameChange: PropTypes.func.isRequired,
62
+ handleSharedDriveNameChange: PropTypes.func,
119
63
  createContact: PropTypes.func.isRequired,
120
64
  recipients: PropTypes.array,
65
+ currentRecipients: PropTypes.array,
121
66
  onRevoke: PropTypes.func.isRequired,
122
67
  onSetType: PropTypes.func.isRequired,
123
- onCreate: PropTypes.func.isRequired,
68
+ onCreate: PropTypes.func,
69
+ onSend: PropTypes.func,
124
70
  onClose: PropTypes.func.isRequired,
125
71
  onShare: PropTypes.func.isRequired,
126
- onRename: PropTypes.func.isRequired
72
+ onRename: PropTypes.func,
73
+ showNameField: PropTypes.bool,
74
+ sharingLink: PropTypes.string
127
75
  }
76
+
77
+ export default DumbSharedDriveModal
@@ -6,7 +6,11 @@ import { useClient } from 'cozy-client'
6
6
  import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
7
7
 
8
8
  import { DumbSharedDriveModal } from './DumbSharedDriveModal'
9
- import { mergeAndDeduplicateRecipients, formatRecipients } from './helpers'
9
+ import {
10
+ mergeAndDeduplicateRecipients,
11
+ formatRecipients,
12
+ RECIPIENT_INDEX_PREFIX
13
+ } from './helpers'
10
14
  import withLocales from '../../hoc/withLocales'
11
15
  import { useSharingContext } from '../../hooks/useSharingContext'
12
16
  import { Contact } from '../../models'
@@ -56,7 +60,8 @@ export const SharedDriveModal = withLocales(({ onClose }) => {
56
60
  document: sharedDriveFolder,
57
61
  recipients: sharedDriveRecipients.recipients,
58
62
  readOnlyRecipients: sharedDriveRecipients.readOnlyRecipients,
59
- sharedDrive: true
63
+ sharedDrive: true,
64
+ openSharing: false
60
65
  })
61
66
 
62
67
  showAlert({
@@ -76,7 +81,7 @@ export const SharedDriveModal = withLocales(({ onClose }) => {
76
81
  }
77
82
 
78
83
  const onSetType = (index, newType) => {
79
- const _id = index.split('virtual-shared-drive-sharing-')[1]
84
+ const _id = index.split(RECIPIENT_INDEX_PREFIX)[1]
80
85
 
81
86
  if (newType === 'two-way') {
82
87
  setSharedDriveRecipients(prev => {
@@ -100,7 +105,7 @@ export const SharedDriveModal = withLocales(({ onClose }) => {
100
105
  }
101
106
 
102
107
  const onRevoke = index => {
103
- const _id = index.split('virtual-shared-drive-sharing-')[1]
108
+ const _id = index.split(RECIPIENT_INDEX_PREFIX)[1]
104
109
 
105
110
  setSharedDriveRecipients(prev => {
106
111
  return {
@@ -121,11 +126,16 @@ export const SharedDriveModal = withLocales(({ onClose }) => {
121
126
  handleSharedDriveNameChange={handleSharedDriveNameChange}
122
127
  createContact={createContact}
123
128
  recipients={recipients}
129
+ currentRecipients={[]}
124
130
  onRevoke={onRevoke}
125
131
  onSetType={onSetType}
126
132
  onCreate={onCreate}
133
+ onSend={undefined}
127
134
  onClose={onClose}
128
135
  onShare={onShare}
136
+ showNameField={true}
137
+ sharingLink={undefined}
138
+ document={undefined}
129
139
  />
130
140
  )
131
141
  })
@@ -2,6 +2,8 @@ import { models } from 'cozy-client'
2
2
 
3
3
  const ContactModel = models.contact
4
4
 
5
+ export const RECIPIENT_INDEX_PREFIX = 'virtual-shared-drive-sharing-'
6
+
5
7
  export const mergeAndDeduplicateRecipients = arrays => {
6
8
  const combinedArray = arrays.flat()
7
9
 
@@ -18,12 +20,41 @@ export const mergeAndDeduplicateRecipients = arrays => {
18
20
  return uniqueArray
19
21
  }
20
22
 
23
+ export const moveRecipientToReadWrite = (federatedRecipients, _id) => {
24
+ const recipientToMove = federatedRecipients.readOnlyRecipients.find(
25
+ r => r._id === _id
26
+ )
27
+ if (!recipientToMove) return federatedRecipients
28
+
29
+ return {
30
+ recipients: [...federatedRecipients.recipients, recipientToMove],
31
+ readOnlyRecipients: federatedRecipients.readOnlyRecipients.filter(
32
+ r => r._id !== _id
33
+ )
34
+ }
35
+ }
36
+
37
+ export const moveRecipientToReadOnly = (federatedRecipients, _id) => {
38
+ const recipientToMove = federatedRecipients.recipients.find(
39
+ r => r._id === _id
40
+ )
41
+ if (!recipientToMove) return federatedRecipients
42
+
43
+ return {
44
+ recipients: federatedRecipients.recipients.filter(r => r._id !== _id),
45
+ readOnlyRecipients: [
46
+ ...federatedRecipients.readOnlyRecipients,
47
+ recipientToMove
48
+ ]
49
+ }
50
+ }
51
+
21
52
  export const formatRecipients = sharedDriveRecipients => {
22
53
  const recipients = []
23
54
 
24
55
  sharedDriveRecipients.recipients.forEach(r => {
25
56
  recipients.push({
26
- index: `virtual-shared-drive-sharing-${r._id}`,
57
+ index: `${RECIPIENT_INDEX_PREFIX}${r._id}`,
27
58
  email: ContactModel.getPrimaryEmail(r),
28
59
  public_name: r.displayName || r.name,
29
60
  memberIndex: r._id,
@@ -34,7 +65,7 @@ export const formatRecipients = sharedDriveRecipients => {
34
65
 
35
66
  sharedDriveRecipients.readOnlyRecipients.forEach(r => {
36
67
  recipients.push({
37
- index: `virtual-shared-drive-sharing-${r._id}`,
68
+ index: `${RECIPIENT_INDEX_PREFIX}${r._id}`,
38
69
  email: ContactModel.getPrimaryEmail(r),
39
70
  public_name: r.displayName || r.name,
40
71
  memberIndex: r._id,