cozy-sharing 26.1.1 → 26.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 CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [26.2.0](https://github.com/cozy/cozy-libs/compare/cozy-sharing@26.1.1...cozy-sharing@26.2.0) (2025-09-18)
7
+
8
+ ### Features
9
+
10
+ - **sharing:** Add isSharedDrive selector ([50212d4](https://github.com/cozy/cozy-libs/commit/50212d4f88976633bb6d26830e759887ed593cc1))
11
+ - **sharing:** Add shared drive edit modal ([112ba42](https://github.com/cozy/cozy-libs/commit/112ba429876edc9a57884b55352126a6e29786e4))
12
+ - **sharing:** Allow shared drive modal to hide some inputs ([7a312d1](https://github.com/cozy/cozy-libs/commit/7a312d14743d1c1bfd70d15618615e75ce875274))
13
+ - **sharing:** Display good wording when removing access in a ([d78eb48](https://github.com/cozy/cozy-libs/commit/d78eb481918bb97ee771b7e65b130854994317c4))
14
+ - **sharing:** Properly display readonly for shared drives recipients ([cb18bd5](https://github.com/cozy/cozy-libs/commit/cb18bd506e1352875781f2585ca3d33a6aaae098))
15
+ - **sharing:** Save shared drive title in description during creation ([52a82ad](https://github.com/cozy/cozy-libs/commit/52a82ad7e7eadfa4306fc5836f497a5ec91074f5))
16
+
6
17
  ## [26.1.1](https://github.com/cozy/cozy-libs/compare/cozy-sharing@26.1.0...cozy-sharing@26.1.1) (2025-09-15)
7
18
 
8
19
  ### Bug Fixes
@@ -39,7 +39,7 @@ import { fetchFilesPaths } from './helpers/files';
39
39
  import { getSharingObject, createSharingInStore, updateSharingInStore } from './helpers/sharings';
40
40
  import { SynchronousJobQueue } from './helpers/synchronousJobQueue';
41
41
  import { fetchApps } from './queries/queries';
42
- import reducer, { receiveSharings, addSharing, updateSharing, addSharingLink, updateSharingLink, revokeSharingLink, revokeRecipient, revokeGroup as revokeGroupFromState, revokeSelf, receivePaths, isOwner as _isOwner, canReshare as _canReshare, getOwner as _getOwner, getRecipients as _getRecipients, getSharingById, getExternalSharingIds, getSharingForSelf as _getSharingForSelf, getSharingType as _getSharingType, getSharingLink as _getSharingLink, getSharedDocIdsBySharings, getDocumentSharing, getDocumentPermissions as _getDocumentPermissions, hasSharedParent as _hasSharedParent, hasSharedChild as _hasSharedChild, getSharedParentPath as _getSharedParentPath } from './state';
42
+ import reducer, { receiveSharings, addSharing, updateSharing, addSharingLink, updateSharingLink, revokeSharingLink, revokeRecipient, revokeGroup as revokeGroupFromState, revokeSelf, receivePaths, isOwner as _isOwner, isSharedDrive as _isSharedDrive, canReshare as _canReshare, getOwner as _getOwner, getRecipients as _getRecipients, getSharingById, getExternalSharingIds, getSharingForSelf as _getSharingForSelf, getSharingType as _getSharingType, getSharingLink as _getSharingLink, getSharedDocIdsBySharings, getDocumentSharing, getDocumentPermissions as _getDocumentPermissions, hasSharedParent as _hasSharedParent, hasSharedChild as _hasSharedChild, getSharedParentPath as _getSharedParentPath } from './state';
43
43
  var log = minilog('SharingProvider');
44
44
  var SHARING_DOCTYPE = 'io.cozy.sharings';
45
45
  var PERMISSION_DOCTYPE = 'io.cozy.permissions';
@@ -582,6 +582,9 @@ export var SharingProvider = /*#__PURE__*/function (_Component) {
582
582
  isOwner: function isOwner(docId) {
583
583
  return _isOwner(_this2.state, docId);
584
584
  },
585
+ isSharedDrive: function isSharedDrive(docId) {
586
+ return _isSharedDrive(_this2.state, docId);
587
+ },
585
588
  canReshare: function canReshare(docId) {
586
589
  return _canReshare(_this2.state, docId, _instanceUri);
587
590
  },
@@ -14,6 +14,7 @@ import { revokeMember } from './actions/revokeMember';
14
14
 
15
15
  var MemberRecipientPermissions = function MemberRecipientPermissions(_ref) {
16
16
  var isOwner = _ref.isOwner,
17
+ isSharedDrive = _ref.isSharedDrive,
17
18
  status = _ref.status,
18
19
  instance = _ref.instance,
19
20
  type = _ref.type,
@@ -87,6 +88,7 @@ var MemberRecipientPermissions = function MemberRecipientPermissions(_ref) {
87
88
  t: t,
88
89
  type: type !== null && type !== void 0 ? type : 'one-way',
89
90
  isOwner: isOwner,
91
+ isSharedDrive: isSharedDrive,
90
92
  handleRevocation: handleRevocation
91
93
  });
92
94
  return /*#__PURE__*/React.createElement("div", {
@@ -10,6 +10,7 @@ var RecipientList = function RecipientList(_ref) {
10
10
  var recipients = _ref.recipients,
11
11
  recipientsToBeConfirmed = _ref.recipientsToBeConfirmed,
12
12
  isOwner = _ref.isOwner,
13
+ isSharedDrive = _ref.isSharedDrive,
13
14
  document = _ref.document,
14
15
  documentType = _ref.documentType,
15
16
  onRevoke = _ref.onRevoke,
@@ -52,6 +53,7 @@ var RecipientList = function RecipientList(_ref) {
52
53
  return /*#__PURE__*/React.createElement(MemberRecipient, _extends({}, recipient, {
53
54
  key: recipient.index,
54
55
  isOwner: isOwner,
56
+ isSharedDrive: isSharedDrive,
55
57
  document: document,
56
58
  documentType: documentType,
57
59
  onRevoke: onRevoke,
@@ -10,10 +10,11 @@ import Typography from 'cozy-ui/transpiled/react/Typography';
10
10
  var revokeGroup = function revokeGroup(_ref) {
11
11
  var t = _ref.t,
12
12
  isOwner = _ref.isOwner,
13
+ isSharedDrive = _ref.isSharedDrive,
13
14
  handleRevocation = _ref.handleRevocation;
14
15
  var revokeType = isOwner ? 'revoke' : 'revokeSelf';
15
16
  var title = t("RevokeGroupItem.".concat(revokeType, ".title"));
16
- var desc = t("RevokeGroupItem.".concat(revokeType, ".desc"));
17
+ var desc = isOwner && isSharedDrive ? t("RevokeGroupItem.revoke.sharedDriveDesc") : t("RevokeGroupItem.".concat(revokeType, ".desc"));
17
18
  var icon = ForbiddenIcon;
18
19
  return {
19
20
  name: 'revokeMember',
@@ -10,10 +10,11 @@ import Typography from 'cozy-ui/transpiled/react/Typography';
10
10
  var revokeMember = function revokeMember(_ref) {
11
11
  var t = _ref.t,
12
12
  isOwner = _ref.isOwner,
13
+ isSharedDrive = _ref.isSharedDrive,
13
14
  handleRevocation = _ref.handleRevocation;
14
15
  var revokeType = isOwner ? 'revoke' : 'revokeSelf';
15
16
  var title = t("RevokeMemberItem.".concat(revokeType, ".title"));
16
- var desc = t("RevokeMemberItem.".concat(revokeType, ".desc"));
17
+ var desc = isOwner && isSharedDrive ? t("RevokeMemberItem.revoke.sharedDriveDesc") : t("RevokeMemberItem.".concat(revokeType, ".desc"));
17
18
  var icon = TrashIcon;
18
19
  return {
19
20
  name: 'revokeMember',
@@ -24,6 +24,7 @@ export var EditableSharingModal = function EditableSharingModal(_ref) {
24
24
  hasSharedChild = _useSharingContext.hasSharedChild,
25
25
  hasSharedParent = _useSharingContext.hasSharedParent,
26
26
  isOwner = _useSharingContext.isOwner,
27
+ isSharedDrive = _useSharingContext.isSharedDrive,
27
28
  revoke = _useSharingContext.revoke,
28
29
  revokeSelf = _useSharingContext.revokeSelf,
29
30
  share = _useSharingContext.share;
@@ -37,6 +38,7 @@ export var EditableSharingModal = function EditableSharingModal(_ref) {
37
38
  hasSharedChild: documentPath && hasSharedChild(documentPath),
38
39
  hasSharedParent: documentPath && hasSharedParent(documentPath),
39
40
  isOwner: isOwner(document._id),
41
+ isSharedDrive: isSharedDrive(document._id),
40
42
  link: getSharingLink(document._id),
41
43
  onRevoke: revoke,
42
44
  onRevokeSelf: revokeSelf,
@@ -3,6 +3,7 @@ import React from 'react';
3
3
  import flag from 'cozy-flags';
4
4
  import ShareDialogCozyToCozy from './ShareDialogCozyToCozy';
5
5
  import ShareDialogOnlyByLink from './ShareDialogOnlyByLink';
6
+ import { SharedDriveEditModal } from './SharedDrive/SharedDriveEditModal';
6
7
  export var ShareModal = function ShareModal(_ref) {
7
8
  var createContact = _ref.createContact,
8
9
  document = _ref.document,
@@ -11,6 +12,7 @@ export var ShareModal = function ShareModal(_ref) {
11
12
  hasSharedChild = _ref.hasSharedChild,
12
13
  hasSharedParent = _ref.hasSharedParent,
13
14
  isOwner = _ref.isOwner,
15
+ isSharedDrive = _ref.isSharedDrive,
14
16
  link = _ref.link,
15
17
  onClose = _ref.onClose,
16
18
  onRevoke = _ref.onRevoke,
@@ -21,6 +23,18 @@ export var ShareModal = function ShareModal(_ref) {
21
23
  sharing = _ref.sharing,
22
24
  sharingDesc = _ref.sharingDesc,
23
25
  twoStepsConfirmationMethods = _ref.twoStepsConfirmationMethods;
26
+
27
+ if (isSharedDrive) {
28
+ return /*#__PURE__*/React.createElement(SharedDriveEditModal, {
29
+ document: document,
30
+ sharing: sharing,
31
+ recipients: recipients,
32
+ onShare: onShare,
33
+ onRevoke: onRevoke,
34
+ onClose: onClose
35
+ });
36
+ }
37
+
24
38
  var shareDialogOnlyByLink = flag('cozy.hide-sharing-cozy-to-cozy') || documentType === 'Albums';
25
39
  var showShareByEmail = !hasSharedParent && !hasSharedChild;
26
40
  var showShareByLink = documentType !== 'Organizations';
@@ -0,0 +1,86 @@
1
+ import PropTypes from 'prop-types';
2
+ import React from 'react';
3
+ import Button from 'cozy-ui/transpiled/react/Buttons';
4
+ import { FixedDialog } from 'cozy-ui/transpiled/react/CozyDialogs';
5
+ import TextField from 'cozy-ui/transpiled/react/TextField';
6
+ import Typography from 'cozy-ui/transpiled/react/Typography';
7
+ import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n';
8
+ import withLocales from '../../hoc/withLocales';
9
+ import { default as DumbShareByEmail } from '../ShareByEmail';
10
+ import WhoHasAccess from '../WhoHasAccess';
11
+ export var DumbSharedDriveModal = withLocales(function (_ref) {
12
+ var title = _ref.title,
13
+ document = _ref.document,
14
+ sharedDriveName = _ref.sharedDriveName,
15
+ handleSharedDriveNameChange = _ref.handleSharedDriveNameChange,
16
+ createContact = _ref.createContact,
17
+ recipients = _ref.recipients,
18
+ onRevoke = _ref.onRevoke,
19
+ onSetType = _ref.onSetType,
20
+ onCreate = _ref.onCreate,
21
+ onClose = _ref.onClose,
22
+ onShare = _ref.onShare;
23
+
24
+ var _useI18n = useI18n(),
25
+ t = _useI18n.t;
26
+
27
+ return /*#__PURE__*/React.createElement(FixedDialog, {
28
+ open: true,
29
+ disableGutters: true,
30
+ onClose: onClose,
31
+ title: title,
32
+ content: /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
33
+ className: "u-ph-2"
34
+ }, handleSharedDriveNameChange && /*#__PURE__*/React.createElement(TextField, {
35
+ required: true,
36
+ label: t('SharedDrive.sharedDriveModal.nameLabel'),
37
+ variant: "outlined",
38
+ size: "small",
39
+ className: "u-w-100 u-mt-1-half",
40
+ value: sharedDriveName,
41
+ onChange: handleSharedDriveNameChange
42
+ }), /*#__PURE__*/React.createElement(Typography, {
43
+ variant: "h6",
44
+ className: "u-mt-1-half u-mb-half"
45
+ }, t('SharedDrive.sharedDriveModal.addPeople')), /*#__PURE__*/React.createElement(DumbShareByEmail, {
46
+ createContact: createContact,
47
+ currentRecipients: [],
48
+ document: document,
49
+ documentType: "Files",
50
+ onShare: onShare,
51
+ submitLabel: t('SharedDrive.sharedDriveModal.add'),
52
+ showNotifications: false
53
+ })), /*#__PURE__*/React.createElement(WhoHasAccess, {
54
+ isOwner: true,
55
+ isSharedDrive: true,
56
+ recipients: recipients,
57
+ document: document,
58
+ documentType: "Files",
59
+ className: "u-w-100",
60
+ onRevoke: onRevoke,
61
+ onSetType: onSetType
62
+ })),
63
+ actions: /*#__PURE__*/React.createElement(React.Fragment, null, onCreate && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Button, {
64
+ variant: "secondary",
65
+ label: t('SharedDrive.sharedDriveModal.cancel'),
66
+ onClick: onClose
67
+ }), /*#__PURE__*/React.createElement(Button, {
68
+ variant: "primary",
69
+ label: t('SharedDrive.sharedDriveModal.create'),
70
+ onClick: onCreate
71
+ })))
72
+ });
73
+ });
74
+ DumbSharedDriveModal.propTypes = {
75
+ title: PropTypes.string,
76
+ document: PropTypes.object,
77
+ sharedDriveName: PropTypes.string,
78
+ handleSharedDriveNameChange: PropTypes.func.isRequired,
79
+ createContact: PropTypes.func.isRequired,
80
+ recipients: PropTypes.array,
81
+ onRevoke: PropTypes.func.isRequired,
82
+ onSetType: PropTypes.func.isRequired,
83
+ onCreate: PropTypes.func.isRequired,
84
+ onClose: PropTypes.func.isRequired,
85
+ onShare: PropTypes.func.isRequired
86
+ };
@@ -0,0 +1,38 @@
1
+ import PropTypes from 'prop-types';
2
+ import React from 'react';
3
+ import { useClient } from 'cozy-client';
4
+ import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n';
5
+ import { DumbSharedDriveModal } from './DumbSharedDriveModal';
6
+ import withLocales from '../../hoc/withLocales';
7
+ import { Contact } from '../../models';
8
+ export var SharedDriveEditModal = withLocales(function (_ref) {
9
+ var document = _ref.document,
10
+ sharing = _ref.sharing,
11
+ recipients = _ref.recipients,
12
+ onShare = _ref.onShare,
13
+ onRevoke = _ref.onRevoke,
14
+ onClose = _ref.onClose;
15
+ var client = useClient();
16
+
17
+ var _useI18n = useI18n(),
18
+ t = _useI18n.t;
19
+
20
+ var createContact = function createContact(contact) {
21
+ return client.create(Contact.doctype, contact);
22
+ };
23
+
24
+ return /*#__PURE__*/React.createElement(DumbSharedDriveModal, {
25
+ title: t('Files.share.title', {
26
+ name: sharing === null || sharing === void 0 ? void 0 : sharing.description
27
+ }),
28
+ document: document,
29
+ createContact: createContact,
30
+ recipients: recipients,
31
+ onRevoke: onRevoke,
32
+ onClose: onClose,
33
+ onShare: onShare
34
+ });
35
+ });
36
+ SharedDriveEditModal.propTypes = {
37
+ onClose: PropTypes.func.isRequired
38
+ };
@@ -5,18 +5,13 @@ import _regeneratorRuntime from "@babel/runtime/regenerator";
5
5
  import PropTypes from 'prop-types';
6
6
  import React, { useState } from 'react';
7
7
  import { useClient } from 'cozy-client';
8
- import Button from 'cozy-ui/transpiled/react/Buttons';
9
- import { FixedDialog } from 'cozy-ui/transpiled/react/CozyDialogs';
10
- import TextField from 'cozy-ui/transpiled/react/TextField';
11
- import Typography from 'cozy-ui/transpiled/react/Typography';
12
8
  import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert';
13
9
  import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n';
10
+ import { DumbSharedDriveModal } from './DumbSharedDriveModal';
14
11
  import { mergeAndDeduplicateRecipients, formatRecipients } from './helpers';
15
12
  import withLocales from '../../hoc/withLocales';
16
13
  import { useSharingContext } from '../../hooks/useSharingContext';
17
14
  import { Contact } from '../../models';
18
- import { default as DumbShareByEmail } from '../ShareByEmail';
19
- import WhoHasAccess from '../WhoHasAccess';
20
15
  export var SharedDriveModal = withLocales(function (_ref) {
21
16
  var onClose = _ref.onClose;
22
17
  var client = useClient();
@@ -78,6 +73,7 @@ export var SharedDriveModal = withLocales(function (_ref) {
78
73
  sharedDriveFolder = _yield$client$create2.data;
79
74
  _context.next = 9;
80
75
  return share({
76
+ description: sharedDriveName,
81
77
  document: sharedDriveFolder,
82
78
  recipients: sharedDriveRecipients.recipients,
83
79
  readOnlyRecipients: sharedDriveRecipients.readOnlyRecipients,
@@ -159,53 +155,22 @@ export var SharedDriveModal = withLocales(function (_ref) {
159
155
  });
160
156
  };
161
157
 
158
+ var createContact = function createContact(contact) {
159
+ return client.create(Contact.doctype, contact);
160
+ };
161
+
162
162
  var recipients = formatRecipients(sharedDriveRecipients);
163
- return /*#__PURE__*/React.createElement(FixedDialog, {
164
- open: true,
165
- disableGutters: true,
166
- onClose: onClose,
163
+ return /*#__PURE__*/React.createElement(DumbSharedDriveModal, {
167
164
  title: t('SharedDrive.sharedDriveModal.title'),
168
- content: /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
169
- className: "u-ph-2"
170
- }, /*#__PURE__*/React.createElement(TextField, {
171
- required: true,
172
- label: t('SharedDrive.sharedDriveModal.nameLabel'),
173
- variant: "outlined",
174
- size: "small",
175
- className: "u-w-100 u-mt-1-half",
176
- value: sharedDriveName,
177
- onChange: handleSharedDriveNameChange
178
- }), /*#__PURE__*/React.createElement(Typography, {
179
- variant: "h6",
180
- className: "u-mt-1-half u-mb-half"
181
- }, t('SharedDrive.sharedDriveModal.addPeople')), /*#__PURE__*/React.createElement(DumbShareByEmail, {
182
- createContact: function createContact(contact) {
183
- return client.create(Contact.doctype, contact);
184
- },
185
- currentRecipients: [],
186
- document: document,
187
- documentType: "Files",
188
- onShare: onShare,
189
- submitLabel: t('SharedDrive.sharedDriveModal.add'),
190
- showNotifications: false
191
- })), /*#__PURE__*/React.createElement(WhoHasAccess, {
192
- isOwner: true,
193
- recipients: recipients,
194
- document: document,
195
- documentType: "Files",
196
- className: "u-w-100",
197
- onRevoke: onRevoke,
198
- onSetType: onSetType
199
- })),
200
- actions: /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Button, {
201
- variant: "secondary",
202
- label: t('SharedDrive.sharedDriveModal.cancel'),
203
- onClick: onClose
204
- }), /*#__PURE__*/React.createElement(Button, {
205
- variant: "primary",
206
- label: t('SharedDrive.sharedDriveModal.create'),
207
- onClick: onCreate
208
- }))
165
+ sharedDriveName: sharedDriveName,
166
+ handleSharedDriveNameChange: handleSharedDriveNameChange,
167
+ createContact: createContact,
168
+ recipients: recipients,
169
+ onRevoke: onRevoke,
170
+ onSetType: onSetType,
171
+ onCreate: onCreate,
172
+ onClose: onClose,
173
+ onShare: onShare
209
174
  });
210
175
  });
211
176
  SharedDriveModal.propTypes = {
@@ -30,6 +30,8 @@ var RecipientWaitingForConfirmationAlert = function RecipientWaitingForConfirmat
30
30
  var WhoHasAccess = function WhoHasAccess(_ref2) {
31
31
  var _ref2$isOwner = _ref2.isOwner,
32
32
  isOwner = _ref2$isOwner === void 0 ? false : _ref2$isOwner,
33
+ _ref2$isSharedDrive = _ref2.isSharedDrive,
34
+ isSharedDrive = _ref2$isSharedDrive === void 0 ? false : _ref2$isSharedDrive,
33
35
  recipients = _ref2.recipients,
34
36
  _ref2$recipientsToBeC = _ref2.recipientsToBeConfirmed,
35
37
  recipientsToBeConfirmed = _ref2$recipientsToBeC === void 0 ? [] : _ref2$recipientsToBeC,
@@ -62,6 +64,7 @@ var WhoHasAccess = function WhoHasAccess(_ref2) {
62
64
  recipients: recipients,
63
65
  recipientsToBeConfirmed: recipientsToBeConfirmed,
64
66
  isOwner: isOwner,
67
+ isSharedDrive: isSharedDrive,
65
68
  document: document,
66
69
  documentType: documentType,
67
70
  onRevoke: onRevoke,
package/dist/state.js CHANGED
@@ -397,6 +397,13 @@ export var isOwner = function isOwner(state, docId) {
397
397
 
398
398
  return true;
399
399
  };
400
+ export var isSharedDrive = function isSharedDrive(state, docId) {
401
+ if (state.byDocId[docId] && state.byDocId[docId].sharings.length !== 0) {
402
+ return getSharingById(state, state.byDocId[docId].sharings[0]).attributes.drive === true;
403
+ }
404
+
405
+ return false;
406
+ };
400
407
  export var canReshare = function canReshare(state, docId, instanceUri) {
401
408
  var sharing = getDocumentSharing(state, docId);
402
409
  var me = sharing.attributes.members.find(matchingInstanceName(instanceUri));
@@ -417,13 +424,30 @@ export var getRecipients = function getRecipients(state, docId) {
417
424
  return getRecipientsWithoutGroups(sharings, docId);
418
425
  };
419
426
 
427
+ var getRecipientType = function getRecipientType(sharing, member, documentType) {
428
+ // If member is read_only, for sure it's one-way
429
+ if (member.read_only) {
430
+ return 'one-way';
431
+ } // Else if it is a shared drive, for sure it's two-way
432
+
433
+
434
+ if (sharing.drive) {
435
+ return 'two-way';
436
+ } // Else, we keep the legacy way of determining the type
437
+ // because we need to take some time to understand if we can
438
+ // just return 'two-way' or not
439
+
440
+
441
+ return documentType;
442
+ };
443
+
420
444
  var getRecipientsWithoutGroups = function getRecipientsWithoutGroups(sharings, docId) {
421
445
  var recipients = sharings.map(function (sharing) {
422
446
  var type = getDocumentSharingType(sharing, docId);
423
447
  return sharing.attributes.members.map(function (m, idx) {
424
448
  return _objectSpread(_objectSpread({}, m), {}, {
425
449
  index: "sharing-".concat(sharing.id, "-member-").concat(idx),
426
- type: m.read_only ? 'one-way' : type,
450
+ type: getRecipientType(sharing, m, type),
427
451
  sharingId: sharing.id,
428
452
  memberIndex: idx,
429
453
  avatarPath: "/sharings/".concat(sharing.id, "/recipients/").concat(idx, "/avatar")
@@ -452,7 +476,7 @@ export var getRecipientsWithGroups = function getRecipientsWithGroups(sharings,
452
476
  var sharingId = sharing.id;
453
477
  var members = sharing.attributes.members.map(function (m, idx) {
454
478
  return _objectSpread(_objectSpread({}, m), {}, {
455
- type: m.read_only ? 'one-way' : type,
479
+ type: getRecipientType(sharing, m, type),
456
480
  sharingId: sharingId,
457
481
  index: "sharing-".concat(sharing.id, "-member-").concat(idx),
458
482
  memberIndex: idx,
package/locales/en.json CHANGED
@@ -380,7 +380,8 @@
380
380
  "RevokeMemberItem": {
381
381
  "revoke": {
382
382
  "title": "Remove from sharing",
383
- "desc": "This contact will keep a copy but the changes won't be synchrnoized anymore."
383
+ "desc": "This contact will keep a copy but the changes won't be synchrnoized anymore.",
384
+ "sharedDriveDesc": "Your contact will no longer be able to access the data."
384
385
  },
385
386
  "revokeSelf": {
386
387
  "title": "Remove me from sharing",
@@ -399,7 +400,8 @@
399
400
  "RevokeGroupItem": {
400
401
  "revoke": {
401
402
  "title": "Remove group from sharing",
402
- "desc": "Group members will keep a copy but the changes won't be synchrnoized anymore."
403
+ "desc": "Group members will keep a copy but the changes won't be synchrnoized anymore.",
404
+ "sharedDriveDesc": "Group members will no longer be able to access the data."
403
405
  },
404
406
  "revokeSelf": {
405
407
  "title": "Remove me from sharing",
package/locales/fr.json CHANGED
@@ -378,7 +378,8 @@
378
378
  "RevokeMemberItem": {
379
379
  "revoke": {
380
380
  "title": "Arrêter le partage",
381
- "desc": "Votre contact conservera une copie mais vos changements ne seront plus synchronisés."
381
+ "desc": "Votre contact conservera une copie mais vos changements ne seront plus synchronisés.",
382
+ "sharedDriveDesc": "Votre contact ne pourra plus accéder aux données."
382
383
  },
383
384
  "revokeSelf": {
384
385
  "title": "Arrêter le partage",
@@ -397,7 +398,8 @@
397
398
  "RevokeGroupItem": {
398
399
  "revoke": {
399
400
  "title": "Arrêter le partage pour le groupe",
400
- "desc": "Les membres du groupe conserveront une copie mais vos changements ne seront plus synchronisés"
401
+ "desc": "Les membres du groupe conserveront une copie mais vos changements ne seront plus synchronisés",
402
+ "sharedDriveDesc": "Les membres du groupe ne pourront plus accéder aux données."
401
403
  },
402
404
  "revokeSelf": {
403
405
  "title": "Quitter le partage",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-sharing",
3
- "version": "26.1.1",
3
+ "version": "26.2.0",
4
4
  "description": "Provides sharing login for React applications.",
5
5
  "main": "dist/index.js",
6
6
  "author": "Cozy",
@@ -79,5 +79,5 @@
79
79
  "sideEffects": [
80
80
  "*.css"
81
81
  ],
82
- "gitHead": "4fb4b75570ed5a36823d085ce27151a98d8aec1e"
82
+ "gitHead": "61c2e410b247be9903d2d68076fd8be5f90cba0e"
83
83
  }
@@ -25,6 +25,7 @@ import reducer, {
25
25
  revokeSelf,
26
26
  receivePaths,
27
27
  isOwner,
28
+ isSharedDrive,
28
29
  canReshare,
29
30
  getOwner,
30
31
  getRecipients,
@@ -58,6 +59,7 @@ export class SharingProvider extends Component {
58
59
  sharedFolderPaths: [],
59
60
  documentType,
60
61
  isOwner: docId => isOwner(this.state, docId),
62
+ isSharedDrive: docId => isSharedDrive(this.state, docId),
61
63
  canReshare: docId => canReshare(this.state, docId, instanceUri),
62
64
  getOwner: docId => getOwner(this.state, docId),
63
65
  getSharingType: docId => getSharingType(this.state, docId, instanceUri),
@@ -16,6 +16,7 @@ import { revokeMember } from './actions/revokeMember'
16
16
 
17
17
  const MemberRecipientPermissions = ({
18
18
  isOwner,
19
+ isSharedDrive,
19
20
  status,
20
21
  instance,
21
22
  type,
@@ -58,6 +59,7 @@ const MemberRecipientPermissions = ({
58
59
  t,
59
60
  type: type ?? 'one-way',
60
61
  isOwner,
62
+ isSharedDrive,
61
63
  handleRevocation
62
64
  })
63
65
 
@@ -10,6 +10,7 @@ const RecipientList = ({
10
10
  recipients,
11
11
  recipientsToBeConfirmed,
12
12
  isOwner,
13
+ isSharedDrive,
13
14
  document,
14
15
  documentType,
15
16
  onRevoke,
@@ -65,6 +66,7 @@ const RecipientList = ({
65
66
  {...recipient}
66
67
  key={recipient.index}
67
68
  isOwner={isOwner}
69
+ isSharedDrive={isSharedDrive}
68
70
  document={document}
69
71
  documentType={documentType}
70
72
  onRevoke={onRevoke}
@@ -7,10 +7,13 @@ import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
7
7
  import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
8
8
  import Typography from 'cozy-ui/transpiled/react/Typography'
9
9
 
10
- const revokeGroup = ({ t, isOwner, handleRevocation }) => {
10
+ const revokeGroup = ({ t, isOwner, isSharedDrive, handleRevocation }) => {
11
11
  const revokeType = isOwner ? 'revoke' : 'revokeSelf'
12
12
  const title = t(`RevokeGroupItem.${revokeType}.title`)
13
- const desc = t(`RevokeGroupItem.${revokeType}.desc`)
13
+ const desc =
14
+ isOwner && isSharedDrive
15
+ ? t(`RevokeGroupItem.revoke.sharedDriveDesc`)
16
+ : t(`RevokeGroupItem.${revokeType}.desc`)
14
17
 
15
18
  const icon = ForbiddenIcon
16
19
 
@@ -7,10 +7,13 @@ import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
7
7
  import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
8
8
  import Typography from 'cozy-ui/transpiled/react/Typography'
9
9
 
10
- const revokeMember = ({ t, isOwner, handleRevocation }) => {
10
+ const revokeMember = ({ t, isOwner, isSharedDrive, handleRevocation }) => {
11
11
  const revokeType = isOwner ? 'revoke' : 'revokeSelf'
12
12
  const title = t(`RevokeMemberItem.${revokeType}.title`)
13
- const desc = t(`RevokeMemberItem.${revokeType}.desc`)
13
+ const desc =
14
+ isOwner && isSharedDrive
15
+ ? t(`RevokeMemberItem.revoke.sharedDriveDesc`)
16
+ : t(`RevokeMemberItem.${revokeType}.desc`)
14
17
 
15
18
  const icon = TrashIcon
16
19
 
@@ -21,6 +21,7 @@ export const EditableSharingModal = ({ document, ...rest }) => {
21
21
  hasSharedChild,
22
22
  hasSharedParent,
23
23
  isOwner,
24
+ isSharedDrive,
24
25
  revoke,
25
26
  revokeSelf,
26
27
  share
@@ -34,6 +35,7 @@ export const EditableSharingModal = ({ document, ...rest }) => {
34
35
  hasSharedChild={documentPath && hasSharedChild(documentPath)}
35
36
  hasSharedParent={documentPath && hasSharedParent(documentPath)}
36
37
  isOwner={isOwner(document._id)}
38
+ isSharedDrive={isSharedDrive(document._id)}
37
39
  link={getSharingLink(document._id)}
38
40
  onRevoke={revoke}
39
41
  onRevokeSelf={revokeSelf}
@@ -5,6 +5,7 @@ import flag from 'cozy-flags'
5
5
 
6
6
  import ShareDialogCozyToCozy from './ShareDialogCozyToCozy'
7
7
  import ShareDialogOnlyByLink from './ShareDialogOnlyByLink'
8
+ import { SharedDriveEditModal } from './SharedDrive/SharedDriveEditModal'
8
9
 
9
10
  export const ShareModal = ({
10
11
  createContact,
@@ -13,6 +14,7 @@ export const ShareModal = ({
13
14
  hasSharedChild,
14
15
  hasSharedParent,
15
16
  isOwner,
17
+ isSharedDrive,
16
18
  link,
17
19
  onClose,
18
20
  onRevoke,
@@ -24,6 +26,19 @@ export const ShareModal = ({
24
26
  sharingDesc,
25
27
  twoStepsConfirmationMethods
26
28
  }) => {
29
+ if (isSharedDrive) {
30
+ return (
31
+ <SharedDriveEditModal
32
+ document={document}
33
+ sharing={sharing}
34
+ recipients={recipients}
35
+ onShare={onShare}
36
+ onRevoke={onRevoke}
37
+ onClose={onClose}
38
+ />
39
+ )
40
+ }
41
+
27
42
  const shareDialogOnlyByLink =
28
43
  flag('cozy.hide-sharing-cozy-to-cozy') || documentType === 'Albums'
29
44
 
@@ -0,0 +1,110 @@
1
+ import PropTypes from 'prop-types'
2
+ import React from 'react'
3
+
4
+ import Button from 'cozy-ui/transpiled/react/Buttons'
5
+ import { FixedDialog } from 'cozy-ui/transpiled/react/CozyDialogs'
6
+ import TextField from 'cozy-ui/transpiled/react/TextField'
7
+ import Typography from 'cozy-ui/transpiled/react/Typography'
8
+ import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
9
+
10
+ import withLocales from '../../hoc/withLocales'
11
+ import { default as DumbShareByEmail } from '../ShareByEmail'
12
+ import WhoHasAccess from '../WhoHasAccess'
13
+
14
+ export const DumbSharedDriveModal = withLocales(
15
+ ({
16
+ title,
17
+ document,
18
+ sharedDriveName,
19
+ handleSharedDriveNameChange,
20
+ createContact,
21
+ recipients,
22
+ onRevoke,
23
+ onSetType,
24
+ onCreate,
25
+ onClose,
26
+ onShare
27
+ }) => {
28
+ const { t } = useI18n()
29
+
30
+ return (
31
+ <FixedDialog
32
+ open
33
+ disableGutters
34
+ onClose={onClose}
35
+ title={title}
36
+ content={
37
+ <div>
38
+ <div className="u-ph-2">
39
+ {handleSharedDriveNameChange && (
40
+ <TextField
41
+ required
42
+ label={t('SharedDrive.sharedDriveModal.nameLabel')}
43
+ variant="outlined"
44
+ size="small"
45
+ className="u-w-100 u-mt-1-half"
46
+ value={sharedDriveName}
47
+ onChange={handleSharedDriveNameChange}
48
+ />
49
+ )}
50
+ <Typography variant="h6" className="u-mt-1-half u-mb-half">
51
+ {t('SharedDrive.sharedDriveModal.addPeople')}
52
+ </Typography>
53
+ <DumbShareByEmail
54
+ createContact={createContact}
55
+ currentRecipients={[]}
56
+ document={document}
57
+ documentType="Files"
58
+ onShare={onShare}
59
+ submitLabel={t('SharedDrive.sharedDriveModal.add')}
60
+ showNotifications={false}
61
+ />
62
+ </div>
63
+ <WhoHasAccess
64
+ isOwner
65
+ isSharedDrive
66
+ recipients={recipients}
67
+ document={document}
68
+ documentType="Files"
69
+ className="u-w-100"
70
+ onRevoke={onRevoke}
71
+ onSetType={onSetType}
72
+ />
73
+ </div>
74
+ }
75
+ actions={
76
+ <>
77
+ {onCreate && (
78
+ <>
79
+ <Button
80
+ variant="secondary"
81
+ label={t('SharedDrive.sharedDriveModal.cancel')}
82
+ onClick={onClose}
83
+ />
84
+ <Button
85
+ variant="primary"
86
+ label={t('SharedDrive.sharedDriveModal.create')}
87
+ onClick={onCreate}
88
+ />
89
+ </>
90
+ )}
91
+ </>
92
+ }
93
+ />
94
+ )
95
+ }
96
+ )
97
+
98
+ DumbSharedDriveModal.propTypes = {
99
+ title: PropTypes.string,
100
+ document: PropTypes.object,
101
+ sharedDriveName: PropTypes.string,
102
+ handleSharedDriveNameChange: PropTypes.func.isRequired,
103
+ createContact: PropTypes.func.isRequired,
104
+ recipients: PropTypes.array,
105
+ onRevoke: PropTypes.func.isRequired,
106
+ onSetType: PropTypes.func.isRequired,
107
+ onCreate: PropTypes.func.isRequired,
108
+ onClose: PropTypes.func.isRequired,
109
+ onShare: PropTypes.func.isRequired
110
+ }
@@ -0,0 +1,34 @@
1
+ import PropTypes from 'prop-types'
2
+ import React from 'react'
3
+
4
+ import { useClient } from 'cozy-client'
5
+ import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
6
+
7
+ import { DumbSharedDriveModal } from './DumbSharedDriveModal'
8
+ import withLocales from '../../hoc/withLocales'
9
+ import { Contact } from '../../models'
10
+
11
+ export const SharedDriveEditModal = withLocales(
12
+ ({ document, sharing, recipients, onShare, onRevoke, onClose }) => {
13
+ const client = useClient()
14
+ const { t } = useI18n()
15
+
16
+ const createContact = contact => client.create(Contact.doctype, contact)
17
+
18
+ return (
19
+ <DumbSharedDriveModal
20
+ title={t('Files.share.title', { name: sharing?.description })}
21
+ document={document}
22
+ createContact={createContact}
23
+ recipients={recipients}
24
+ onRevoke={onRevoke}
25
+ onClose={onClose}
26
+ onShare={onShare}
27
+ />
28
+ )
29
+ }
30
+ )
31
+
32
+ SharedDriveEditModal.propTypes = {
33
+ onClose: PropTypes.func.isRequired
34
+ }
@@ -2,19 +2,14 @@ import PropTypes from 'prop-types'
2
2
  import React, { useState } from 'react'
3
3
 
4
4
  import { useClient } from 'cozy-client'
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
5
  import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
10
6
  import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
11
7
 
8
+ import { DumbSharedDriveModal } from './DumbSharedDriveModal'
12
9
  import { mergeAndDeduplicateRecipients, formatRecipients } from './helpers'
13
10
  import withLocales from '../../hoc/withLocales'
14
11
  import { useSharingContext } from '../../hooks/useSharingContext'
15
12
  import { Contact } from '../../models'
16
- import { default as DumbShareByEmail } from '../ShareByEmail'
17
- import WhoHasAccess from '../WhoHasAccess'
18
13
 
19
14
  export const SharedDriveModal = withLocales(({ onClose }) => {
20
15
  const client = useClient()
@@ -57,6 +52,7 @@ export const SharedDriveModal = withLocales(({ onClose }) => {
57
52
  })
58
53
 
59
54
  await share({
55
+ description: sharedDriveName,
60
56
  document: sharedDriveFolder,
61
57
  recipients: sharedDriveRecipients.recipients,
62
58
  readOnlyRecipients: sharedDriveRecipients.readOnlyRecipients,
@@ -114,64 +110,22 @@ export const SharedDriveModal = withLocales(({ onClose }) => {
114
110
  })
115
111
  }
116
112
 
113
+ const createContact = contact => client.create(Contact.doctype, contact)
114
+
117
115
  const recipients = formatRecipients(sharedDriveRecipients)
118
116
 
119
117
  return (
120
- <FixedDialog
121
- open
122
- disableGutters
123
- onClose={onClose}
118
+ <DumbSharedDriveModal
124
119
  title={t('SharedDrive.sharedDriveModal.title')}
125
- content={
126
- <div>
127
- <div className="u-ph-2">
128
- <TextField
129
- required
130
- label={t('SharedDrive.sharedDriveModal.nameLabel')}
131
- variant="outlined"
132
- size="small"
133
- className="u-w-100 u-mt-1-half"
134
- value={sharedDriveName}
135
- onChange={handleSharedDriveNameChange}
136
- />
137
- <Typography variant="h6" className="u-mt-1-half u-mb-half">
138
- {t('SharedDrive.sharedDriveModal.addPeople')}
139
- </Typography>
140
- <DumbShareByEmail
141
- createContact={contact => client.create(Contact.doctype, contact)}
142
- currentRecipients={[]}
143
- document={document}
144
- documentType="Files"
145
- onShare={onShare}
146
- submitLabel={t('SharedDrive.sharedDriveModal.add')}
147
- showNotifications={false}
148
- />
149
- </div>
150
- <WhoHasAccess
151
- isOwner
152
- recipients={recipients}
153
- document={document}
154
- documentType="Files"
155
- className="u-w-100"
156
- onRevoke={onRevoke}
157
- onSetType={onSetType}
158
- />
159
- </div>
160
- }
161
- actions={
162
- <>
163
- <Button
164
- variant="secondary"
165
- label={t('SharedDrive.sharedDriveModal.cancel')}
166
- onClick={onClose}
167
- />
168
- <Button
169
- variant="primary"
170
- label={t('SharedDrive.sharedDriveModal.create')}
171
- onClick={onCreate}
172
- />
173
- </>
174
- }
120
+ sharedDriveName={sharedDriveName}
121
+ handleSharedDriveNameChange={handleSharedDriveNameChange}
122
+ createContact={createContact}
123
+ recipients={recipients}
124
+ onRevoke={onRevoke}
125
+ onSetType={onSetType}
126
+ onCreate={onCreate}
127
+ onClose={onClose}
128
+ onShare={onShare}
175
129
  />
176
130
  )
177
131
  })
@@ -28,6 +28,7 @@ const RecipientWaitingForConfirmationAlert = ({ recipientsToBeConfirmed }) => {
28
28
 
29
29
  const WhoHasAccess = ({
30
30
  isOwner = false,
31
+ isSharedDrive = false,
31
32
  recipients,
32
33
  recipientsToBeConfirmed = [],
33
34
  document,
@@ -67,6 +68,7 @@ const WhoHasAccess = ({
67
68
  recipients={recipients}
68
69
  recipientsToBeConfirmed={recipientsToBeConfirmed}
69
70
  isOwner={isOwner}
71
+ isSharedDrive={isSharedDrive}
70
72
  document={document}
71
73
  documentType={documentType}
72
74
  onRevoke={onRevoke}
package/src/state.js CHANGED
@@ -300,6 +300,16 @@ export const isOwner = (state, docId) => {
300
300
  return true
301
301
  }
302
302
 
303
+ export const isSharedDrive = (state, docId) => {
304
+ if (state.byDocId[docId] && state.byDocId[docId].sharings.length !== 0) {
305
+ return (
306
+ getSharingById(state, state.byDocId[docId].sharings[0]).attributes
307
+ .drive === true
308
+ )
309
+ }
310
+ return false
311
+ }
312
+
303
313
  export const canReshare = (state, docId, instanceUri) => {
304
314
  const sharing = getDocumentSharing(state, docId)
305
315
  const me = sharing.attributes.members.find(matchingInstanceName(instanceUri))
@@ -318,6 +328,23 @@ export const getRecipients = (state, docId) => {
318
328
  return getRecipientsWithoutGroups(sharings, docId)
319
329
  }
320
330
 
331
+ const getRecipientType = (sharing, member, documentType) => {
332
+ // If member is read_only, for sure it's one-way
333
+ if (member.read_only) {
334
+ return 'one-way'
335
+ }
336
+
337
+ // Else if it is a shared drive, for sure it's two-way
338
+ if (sharing.drive) {
339
+ return 'two-way'
340
+ }
341
+
342
+ // Else, we keep the legacy way of determining the type
343
+ // because we need to take some time to understand if we can
344
+ // just return 'two-way' or not
345
+ return documentType
346
+ }
347
+
321
348
  const getRecipientsWithoutGroups = (sharings, docId) => {
322
349
  const recipients = sharings
323
350
  .map(sharing => {
@@ -325,7 +352,7 @@ const getRecipientsWithoutGroups = (sharings, docId) => {
325
352
  return sharing.attributes.members.map((m, idx) => ({
326
353
  ...m,
327
354
  index: `sharing-${sharing.id}-member-${idx}`,
328
- type: m.read_only ? 'one-way' : type,
355
+ type: getRecipientType(sharing, m, type),
329
356
  sharingId: sharing.id,
330
357
  memberIndex: idx,
331
358
  avatarPath: `/sharings/${sharing.id}/recipients/${idx}/avatar`
@@ -346,7 +373,7 @@ export const getRecipientsWithGroups = (sharings, docId) => {
346
373
 
347
374
  const members = sharing.attributes.members.map((m, idx) => ({
348
375
  ...m,
349
- type: m.read_only ? 'one-way' : type,
376
+ type: getRecipientType(sharing, m, type),
350
377
  sharingId,
351
378
  index: `sharing-${sharing.id}-member-${idx}`,
352
379
  memberIndex: idx,