cozy-sharing 28.6.0 → 28.8.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 +13 -0
- package/dist/SharingProvider.js +4 -1
- package/dist/components/FederatedFolder/FederatedFolderModal.js +48 -16
- package/dist/components/FederatedFolder/FederatedFolderModal.spec.js +106 -7
- package/dist/components/Recipient/LinkRecipient.js +21 -5
- package/dist/components/Recipient/LinkRecipientPermissions.js +62 -15
- package/dist/components/Recipient/actions/revokeLink.js +34 -0
- package/dist/components/ShareModal/ShareModal.js +2 -6
- package/package.json +2 -2
- package/src/SharingProvider.jsx +1 -0
- package/src/components/FederatedFolder/FederatedFolderModal.jsx +23 -10
- package/src/components/FederatedFolder/FederatedFolderModal.spec.jsx +70 -1
- package/src/components/Recipient/LinkRecipient.jsx +20 -3
- package/src/components/Recipient/LinkRecipientPermissions.jsx +54 -16
- package/src/components/Recipient/actions/revokeLink.js +33 -0
- package/src/components/ShareModal/ShareModal.jsx +1 -7
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,19 @@
|
|
|
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
|
+
# [28.8.0](https://github.com/cozy/cozy-libs/compare/cozy-sharing@28.7.0...cozy-sharing@28.8.0) (2026-03-12)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- **cozy-sharing:** Use FederatedFolderModal on existing sharings ([541de13](https://github.com/cozy/cozy-libs/commit/541de1310631170b5567694ff1366bc997fb3ea3))
|
|
11
|
+
|
|
12
|
+
# [28.7.0](https://github.com/cozy/cozy-libs/compare/cozy-sharing@28.6.0...cozy-sharing@28.7.0) (2026-03-12)
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
- Allow to change link editing rights directly from the share modal ([57c3e61](https://github.com/cozy/cozy-libs/commit/57c3e6144649d3102c37961b4953219bdfe553a3))
|
|
17
|
+
- Display the Share restriction modal on click on the sharing link ([a875c36](https://github.com/cozy/cozy-libs/commit/a875c36e7b6ea592b25cbc8386a305b1c1dac516))
|
|
18
|
+
|
|
6
19
|
# [28.6.0](https://github.com/cozy/cozy-libs/compare/cozy-sharing@28.5.0...cozy-sharing@28.6.0) (2026-03-11)
|
|
7
20
|
|
|
8
21
|
### Features
|
package/dist/SharingProvider.js
CHANGED
|
@@ -755,9 +755,12 @@ export var SharingProvider = /*#__PURE__*/function (_Component) {
|
|
|
755
755
|
|
|
756
756
|
case 11:
|
|
757
757
|
resp = _context14.t0;
|
|
758
|
+
|
|
759
|
+
_this2.dispatch(updateSharingLink(resp.data));
|
|
760
|
+
|
|
758
761
|
return _context14.abrupt("return", resp);
|
|
759
762
|
|
|
760
|
-
case
|
|
763
|
+
case 14:
|
|
761
764
|
case "end":
|
|
762
765
|
return _context14.stop();
|
|
763
766
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
1
2
|
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
2
3
|
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
3
4
|
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
@@ -24,7 +25,9 @@ export var FederatedFolderModal = withLocales(function (_ref) {
|
|
|
24
25
|
getFederatedShareLink = _useSharingContext.getFederatedShareLink,
|
|
25
26
|
getDocumentPermissions = _useSharingContext.getDocumentPermissions,
|
|
26
27
|
getOwner = _useSharingContext.getOwner,
|
|
27
|
-
getSharingById = _useSharingContext.getSharingById
|
|
28
|
+
getSharingById = _useSharingContext.getSharingById,
|
|
29
|
+
getRecipients = _useSharingContext.getRecipients,
|
|
30
|
+
revoke = _useSharingContext.revoke;
|
|
28
31
|
|
|
29
32
|
var _useAlert = useAlert(),
|
|
30
33
|
showAlert = _useAlert.showAlert;
|
|
@@ -162,21 +165,50 @@ export var FederatedFolderModal = withLocales(function (_ref) {
|
|
|
162
165
|
}
|
|
163
166
|
};
|
|
164
167
|
|
|
165
|
-
var onRevoke = function
|
|
166
|
-
var
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
168
|
+
var onRevoke = /*#__PURE__*/function () {
|
|
169
|
+
var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(documentOrIndex, sharingId, memberIndex) {
|
|
170
|
+
var _id;
|
|
171
|
+
|
|
172
|
+
return _regeneratorRuntime.wrap(function _callee3$(_context3) {
|
|
173
|
+
while (1) switch (_context3.prev = _context3.next) {
|
|
174
|
+
case 0:
|
|
175
|
+
if (!(typeof documentOrIndex === 'string' && documentOrIndex.startsWith(RECIPIENT_INDEX_PREFIX))) {
|
|
176
|
+
_context3.next = 5;
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
_id = documentOrIndex.split(RECIPIENT_INDEX_PREFIX)[1];
|
|
181
|
+
setFederatedRecipients(function (prev) {
|
|
182
|
+
return {
|
|
183
|
+
recipients: prev.recipients.filter(function (r) {
|
|
184
|
+
return r._id !== _id;
|
|
185
|
+
}),
|
|
186
|
+
readOnlyRecipients: prev.readOnlyRecipients.filter(function (r) {
|
|
187
|
+
return r._id !== _id;
|
|
188
|
+
})
|
|
189
|
+
};
|
|
190
|
+
});
|
|
191
|
+
_context3.next = 7;
|
|
192
|
+
break;
|
|
193
|
+
|
|
194
|
+
case 5:
|
|
195
|
+
_context3.next = 7;
|
|
196
|
+
return revoke(documentOrIndex, sharingId, memberIndex);
|
|
197
|
+
|
|
198
|
+
case 7:
|
|
199
|
+
case "end":
|
|
200
|
+
return _context3.stop();
|
|
201
|
+
}
|
|
202
|
+
}, _callee3);
|
|
203
|
+
}));
|
|
204
|
+
|
|
205
|
+
return function onRevoke(_x, _x2, _x3) {
|
|
206
|
+
return _ref4.apply(this, arguments);
|
|
207
|
+
};
|
|
208
|
+
}();
|
|
178
209
|
|
|
179
|
-
var
|
|
210
|
+
var existingRecipients = existingDocument ? getRecipients(existingDocument._id) : [];
|
|
211
|
+
var recipients = [].concat(_toConsumableArray(existingRecipients), _toConsumableArray(formatRecipients(federatedRecipients)));
|
|
180
212
|
var modalTitle = t('FederatedFolder.shareTitle', {
|
|
181
213
|
name: folderName
|
|
182
214
|
});
|
|
@@ -189,7 +221,7 @@ export var FederatedFolderModal = withLocales(function (_ref) {
|
|
|
189
221
|
},
|
|
190
222
|
recipients: recipients,
|
|
191
223
|
readOnlyRecipients: federatedRecipients.readOnlyRecipients,
|
|
192
|
-
currentRecipients:
|
|
224
|
+
currentRecipients: existingRecipients,
|
|
193
225
|
onRevoke: onRevoke,
|
|
194
226
|
onSetType: onSetType,
|
|
195
227
|
onSend: onSend,
|
|
@@ -7,7 +7,9 @@ import { createMockClient } from 'cozy-client';
|
|
|
7
7
|
import { FederatedFolderModal } from './FederatedFolderModal';
|
|
8
8
|
import AppLike from '../SharingBanner/test/AppLike';
|
|
9
9
|
var mockShare = jest.fn();
|
|
10
|
+
var mockRevoke = jest.fn();
|
|
10
11
|
var mockGetSharingLink = jest.fn();
|
|
12
|
+
var mockGetRecipients = jest.fn().mockReturnValue([]);
|
|
11
13
|
var mockShowAlert = jest.fn();
|
|
12
14
|
var mockOnClose = jest.fn();
|
|
13
15
|
jest.mock('../../hooks/useSharingContext', function () {
|
|
@@ -15,8 +17,10 @@ jest.mock('../../hooks/useSharingContext', function () {
|
|
|
15
17
|
useSharingContext: function useSharingContext() {
|
|
16
18
|
return {
|
|
17
19
|
share: mockShare,
|
|
20
|
+
revoke: mockRevoke,
|
|
18
21
|
getSharingLink: mockGetSharingLink,
|
|
19
|
-
getDocumentPermissions: jest.fn().mockReturnValue([])
|
|
22
|
+
getDocumentPermissions: jest.fn().mockReturnValue([]),
|
|
23
|
+
getRecipients: mockGetRecipients
|
|
20
24
|
};
|
|
21
25
|
}
|
|
22
26
|
};
|
|
@@ -91,6 +95,13 @@ jest.mock('./DumbFederatedFolderModal', function () {
|
|
|
91
95
|
return onRevoke('virtual-shared-drive-sharing-r1');
|
|
92
96
|
}
|
|
93
97
|
}, "Revoke"), /*#__PURE__*/React.createElement("button", {
|
|
98
|
+
"data-testid": "btn-revoke-existing",
|
|
99
|
+
onClick: function onClick() {
|
|
100
|
+
return onRevoke({
|
|
101
|
+
_id: 'folder-123'
|
|
102
|
+
}, 'abc', 1);
|
|
103
|
+
}
|
|
104
|
+
}, "Revoke Existing"), /*#__PURE__*/React.createElement("button", {
|
|
94
105
|
"data-testid": "btn-close",
|
|
95
106
|
onClick: onClose
|
|
96
107
|
}, "Close"), /*#__PURE__*/React.createElement("button", {
|
|
@@ -150,6 +161,7 @@ describe('FederatedFolderModal', function () {
|
|
|
150
161
|
beforeEach(function () {
|
|
151
162
|
jest.clearAllMocks();
|
|
152
163
|
mockGetSharingLink.mockReturnValue('https://example.com/share/abc123');
|
|
164
|
+
mockGetRecipients.mockReturnValue([]);
|
|
153
165
|
client = createTestClient();
|
|
154
166
|
sharingContextValue = createSharingContextValue();
|
|
155
167
|
});
|
|
@@ -511,22 +523,109 @@ describe('FederatedFolderModal', function () {
|
|
|
511
523
|
}, _callee12);
|
|
512
524
|
})));
|
|
513
525
|
});
|
|
514
|
-
describe('
|
|
515
|
-
|
|
526
|
+
describe('existing recipients', function () {
|
|
527
|
+
var existingRecipients = [{
|
|
528
|
+
index: 'sharing-abc-member-0',
|
|
529
|
+
name: 'Charlie',
|
|
530
|
+
email: 'charlie@example.com',
|
|
531
|
+
status: 'owner',
|
|
532
|
+
type: 'two-way',
|
|
533
|
+
sharingId: 'abc',
|
|
534
|
+
memberIndex: 0
|
|
535
|
+
}, {
|
|
536
|
+
index: 'sharing-abc-member-1',
|
|
537
|
+
name: 'Diana',
|
|
538
|
+
email: 'diana@example.com',
|
|
539
|
+
status: 'ready',
|
|
540
|
+
type: 'one-way',
|
|
541
|
+
sharingId: 'abc',
|
|
542
|
+
memberIndex: 1
|
|
543
|
+
}];
|
|
544
|
+
it('should display existing recipients from getRecipients', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee13() {
|
|
516
545
|
var _setup13, getByTestId;
|
|
517
546
|
|
|
518
547
|
return _regeneratorRuntime.wrap(function _callee13$(_context13) {
|
|
519
548
|
while (1) switch (_context13.prev = _context13.next) {
|
|
520
549
|
case 0:
|
|
550
|
+
mockGetRecipients.mockReturnValue(existingRecipients);
|
|
521
551
|
_setup13 = setup(), getByTestId = _setup13.getByTestId;
|
|
522
|
-
_context13.next =
|
|
552
|
+
_context13.next = 4;
|
|
553
|
+
return waitFor(function () {
|
|
554
|
+
expect(getByTestId('recipients-count').textContent).toBe('2');
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
case 4:
|
|
558
|
+
case "end":
|
|
559
|
+
return _context13.stop();
|
|
560
|
+
}
|
|
561
|
+
}, _callee13);
|
|
562
|
+
})));
|
|
563
|
+
it('should pass existing recipients as currentRecipients', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee14() {
|
|
564
|
+
var _setup14, getByTestId;
|
|
565
|
+
|
|
566
|
+
return _regeneratorRuntime.wrap(function _callee14$(_context14) {
|
|
567
|
+
while (1) switch (_context14.prev = _context14.next) {
|
|
568
|
+
case 0:
|
|
569
|
+
mockGetRecipients.mockReturnValue(existingRecipients);
|
|
570
|
+
_setup14 = setup(), getByTestId = _setup14.getByTestId;
|
|
571
|
+
_context14.next = 4;
|
|
572
|
+
return waitFor(function () {
|
|
573
|
+
expect(getByTestId('recipients-count').textContent).toBe('2');
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
case 4:
|
|
577
|
+
case "end":
|
|
578
|
+
return _context14.stop();
|
|
579
|
+
}
|
|
580
|
+
}, _callee14);
|
|
581
|
+
})));
|
|
582
|
+
it('should call revoke for existing member', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee15() {
|
|
583
|
+
var _setup15, getByTestId;
|
|
584
|
+
|
|
585
|
+
return _regeneratorRuntime.wrap(function _callee15$(_context15) {
|
|
586
|
+
while (1) switch (_context15.prev = _context15.next) {
|
|
587
|
+
case 0:
|
|
588
|
+
mockGetRecipients.mockReturnValue(existingRecipients);
|
|
589
|
+
mockRevoke.mockResolvedValueOnce({});
|
|
590
|
+
_setup15 = setup(), getByTestId = _setup15.getByTestId;
|
|
591
|
+
_context15.next = 5;
|
|
592
|
+
return waitFor(function () {
|
|
593
|
+
expect(getByTestId('dumb-modal')).toBeTruthy();
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
case 5:
|
|
597
|
+
// Simulate revoke of an existing member (non-virtual-shared-drive index)
|
|
598
|
+
fireEvent.click(getByTestId('btn-revoke-existing'));
|
|
599
|
+
_context15.next = 8;
|
|
600
|
+
return waitFor(function () {
|
|
601
|
+
expect(mockRevoke).toHaveBeenCalledWith({
|
|
602
|
+
_id: 'folder-123'
|
|
603
|
+
}, 'abc', 1);
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
case 8:
|
|
607
|
+
case "end":
|
|
608
|
+
return _context15.stop();
|
|
609
|
+
}
|
|
610
|
+
}, _callee15);
|
|
611
|
+
})));
|
|
612
|
+
});
|
|
613
|
+
describe('createContact callback', function () {
|
|
614
|
+
it('should call client.create when creating a contact', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee16() {
|
|
615
|
+
var _setup16, getByTestId;
|
|
616
|
+
|
|
617
|
+
return _regeneratorRuntime.wrap(function _callee16$(_context16) {
|
|
618
|
+
while (1) switch (_context16.prev = _context16.next) {
|
|
619
|
+
case 0:
|
|
620
|
+
_setup16 = setup(), getByTestId = _setup16.getByTestId;
|
|
621
|
+
_context16.next = 3;
|
|
523
622
|
return waitFor(function () {
|
|
524
623
|
expect(getByTestId('btn-create-contact')).toBeTruthy();
|
|
525
624
|
});
|
|
526
625
|
|
|
527
626
|
case 3:
|
|
528
627
|
fireEvent.click(getByTestId('btn-create-contact'));
|
|
529
|
-
|
|
628
|
+
_context16.next = 6;
|
|
530
629
|
return waitFor(function () {
|
|
531
630
|
expect(client.create).toHaveBeenCalledWith('io.cozy.contacts', {
|
|
532
631
|
email: 'test@example.com'
|
|
@@ -535,9 +634,9 @@ describe('FederatedFolderModal', function () {
|
|
|
535
634
|
|
|
536
635
|
case 6:
|
|
537
636
|
case "end":
|
|
538
|
-
return
|
|
637
|
+
return _context16.stop();
|
|
539
638
|
}
|
|
540
|
-
},
|
|
639
|
+
}, _callee16);
|
|
541
640
|
})));
|
|
542
641
|
});
|
|
543
642
|
});
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
3
|
import PropTypes from 'prop-types';
|
|
3
|
-
import React from 'react';
|
|
4
|
+
import React, { useState } from 'react';
|
|
4
5
|
import { useI18n } from 'twake-i18n';
|
|
5
6
|
import Avatar from 'cozy-ui/transpiled/react/Avatar';
|
|
7
|
+
import DropdownButton from 'cozy-ui/transpiled/react/DropdownButton';
|
|
6
8
|
import Fade from 'cozy-ui/transpiled/react/Fade';
|
|
7
9
|
import Icon from 'cozy-ui/transpiled/react/Icon';
|
|
8
10
|
import LinkIcon from 'cozy-ui/transpiled/react/Icons/Link';
|
|
9
11
|
import ListItem from 'cozy-ui/transpiled/react/ListItem';
|
|
10
12
|
import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon';
|
|
11
13
|
import ListItemText from 'cozy-ui/transpiled/react/ListItemText';
|
|
12
|
-
import Typography from 'cozy-ui/transpiled/react/Typography';
|
|
13
14
|
import useBreakpoints from 'cozy-ui/transpiled/react/providers/Breakpoints';
|
|
14
15
|
import LinkRecipientPermissions from './LinkRecipientPermissions';
|
|
15
16
|
import RecipientConfirm from './RecipientConfirm';
|
|
16
17
|
import { checkIsPermissionHasPassword, getPermissionExpiresDate } from '../../helpers/permissions';
|
|
17
18
|
import { FADE_IN_DURATION } from '../../helpers/recipients';
|
|
18
19
|
import { useSharingContext } from '../../hooks/useSharingContext';
|
|
20
|
+
import { ShareRestrictionModal } from '../ShareRestrictionModal/ShareRestrictionModal';
|
|
19
21
|
|
|
20
22
|
var LinkRecipient = function LinkRecipient(props) {
|
|
21
23
|
var _useI18n = useI18n(),
|
|
@@ -33,6 +35,12 @@ var LinkRecipient = function LinkRecipient(props) {
|
|
|
33
35
|
verifyRecipient = props.verifyRecipient,
|
|
34
36
|
fadeIn = props.fadeIn,
|
|
35
37
|
document = props.document;
|
|
38
|
+
|
|
39
|
+
var _useState = useState(false),
|
|
40
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
41
|
+
openShareRestrictionModal = _useState2[0],
|
|
42
|
+
setOpenShareRestrictionModal = _useState2[1];
|
|
43
|
+
|
|
36
44
|
var permissions = getDocumentPermissions(document === null || document === void 0 ? void 0 : document._id);
|
|
37
45
|
var hasPassword = checkIsPermissionHasPassword(permissions);
|
|
38
46
|
var expiresDate = getPermissionExpiresDate(permissions);
|
|
@@ -61,11 +69,19 @@ var LinkRecipient = function LinkRecipient(props) {
|
|
|
61
69
|
}, /*#__PURE__*/React.createElement(Icon, {
|
|
62
70
|
icon: LinkIcon
|
|
63
71
|
}))), /*#__PURE__*/React.createElement(ListItemText, {
|
|
64
|
-
primary: /*#__PURE__*/React.createElement(
|
|
65
|
-
|
|
72
|
+
primary: /*#__PURE__*/React.createElement(DropdownButton, {
|
|
73
|
+
textVariant: "body1",
|
|
74
|
+
onClick: function onClick() {
|
|
75
|
+
return setOpenShareRestrictionModal(true);
|
|
76
|
+
}
|
|
66
77
|
}, textPrimary),
|
|
67
78
|
secondary: textSecondary
|
|
68
|
-
}), RightPart
|
|
79
|
+
}), RightPart, openShareRestrictionModal && /*#__PURE__*/React.createElement(ShareRestrictionModal, {
|
|
80
|
+
file: document,
|
|
81
|
+
onClose: function onClose() {
|
|
82
|
+
return setOpenShareRestrictionModal(false);
|
|
83
|
+
}
|
|
84
|
+
})));
|
|
69
85
|
};
|
|
70
86
|
|
|
71
87
|
LinkRecipient.propTypes = {
|
|
@@ -2,14 +2,20 @@ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import React, { useState, useRef } from 'react';
|
|
4
4
|
import { useI18n } from 'twake-i18n';
|
|
5
|
+
import ActionsMenu from 'cozy-ui/transpiled/react/ActionsMenu';
|
|
6
|
+
import { makeActions, divider } from 'cozy-ui/transpiled/react/ActionsMenu/Actions';
|
|
5
7
|
import DropdownButton from 'cozy-ui/transpiled/react/DropdownButton';
|
|
8
|
+
import { revokeLink } from './actions/revokeLink';
|
|
9
|
+
import { setReadOnlySharedPermission } from './actions/setReadOnlySharedPermission';
|
|
10
|
+
import { setReadWriteSharedPermission } from './actions/setReadWriteSharedPermission';
|
|
11
|
+
import { isOnlyReadOnlyLinkAllowed } from '../../helpers/link';
|
|
6
12
|
import { checkIsReadOnlyPermissions } from '../../helpers/permissions';
|
|
7
|
-
import {
|
|
13
|
+
import { useSharingContext } from '../../hooks/useSharingContext';
|
|
14
|
+
import { WRITE_PERMS, READ_ONLY_PERMS } from '../ShareRestrictionModal/helpers';
|
|
8
15
|
|
|
9
16
|
var LinkRecipientPermissions = function LinkRecipientPermissions(_ref) {
|
|
10
17
|
var className = _ref.className,
|
|
11
|
-
document = _ref.document
|
|
12
|
-
permissions = _ref.permissions;
|
|
18
|
+
document = _ref.document;
|
|
13
19
|
|
|
14
20
|
var _useI18n = useI18n(),
|
|
15
21
|
t = _useI18n.t;
|
|
@@ -18,29 +24,70 @@ var LinkRecipientPermissions = function LinkRecipientPermissions(_ref) {
|
|
|
18
24
|
|
|
19
25
|
var _useState = useState(false),
|
|
20
26
|
_useState2 = _slicedToArray(_useState, 2),
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
isMenuDisplayed = _useState2[0],
|
|
28
|
+
setMenuDisplayed = _useState2[1];
|
|
23
29
|
|
|
30
|
+
var _useSharingContext = useSharingContext(),
|
|
31
|
+
updateDocumentPermissions = _useSharingContext.updateDocumentPermissions,
|
|
32
|
+
revokeSharingLink = _useSharingContext.revokeSharingLink,
|
|
33
|
+
documentType = _useSharingContext.documentType,
|
|
34
|
+
getDocumentPermissions = _useSharingContext.getDocumentPermissions;
|
|
35
|
+
|
|
36
|
+
var permissions = getDocumentPermissions(document._id);
|
|
24
37
|
var isReadOnlyPermissions = checkIsReadOnlyPermissions(permissions);
|
|
38
|
+
var type = isReadOnlyPermissions ? 'one-way' : 'two-way';
|
|
39
|
+
|
|
40
|
+
var toggleMenu = function toggleMenu() {
|
|
41
|
+
return setMenuDisplayed(!isMenuDisplayed);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
var hideMenu = function hideMenu() {
|
|
45
|
+
return setMenuDisplayed(false);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
var handleSetType = function handleSetType(newType) {
|
|
49
|
+
var verbs = newType === 'one-way' ? READ_ONLY_PERMS : WRITE_PERMS;
|
|
50
|
+
updateDocumentPermissions(document, {
|
|
51
|
+
verbs: verbs
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
var handleRevocation = function handleRevocation() {
|
|
56
|
+
return revokeSharingLink(document);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
var actionsToShow = [setReadOnlySharedPermission];
|
|
60
|
+
|
|
61
|
+
if (!isOnlyReadOnlyLinkAllowed({
|
|
62
|
+
documentType: documentType
|
|
63
|
+
})) {
|
|
64
|
+
actionsToShow.push(setReadWriteSharedPermission);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
actionsToShow.push(divider, revokeLink);
|
|
68
|
+
var actions = makeActions(actionsToShow, {
|
|
69
|
+
t: t,
|
|
70
|
+
type: type,
|
|
71
|
+
setType: handleSetType,
|
|
72
|
+
handleRevocation: handleRevocation
|
|
73
|
+
});
|
|
25
74
|
return /*#__PURE__*/React.createElement("div", {
|
|
26
75
|
className: className
|
|
27
76
|
}, /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(DropdownButton, {
|
|
28
77
|
ref: buttonRef,
|
|
29
78
|
textVariant: "body2",
|
|
30
|
-
onClick:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
79
|
+
onClick: toggleMenu
|
|
80
|
+
}, t("Share.type.".concat(type)).toLowerCase()), /*#__PURE__*/React.createElement(ActionsMenu, {
|
|
81
|
+
ref: buttonRef,
|
|
82
|
+
open: isMenuDisplayed,
|
|
83
|
+
actions: actions,
|
|
84
|
+
autoClose: true,
|
|
85
|
+
onClose: hideMenu
|
|
38
86
|
})));
|
|
39
87
|
};
|
|
40
88
|
|
|
41
89
|
LinkRecipientPermissions.propTypes = {
|
|
42
90
|
className: PropTypes.string,
|
|
43
|
-
document: PropTypes.object.isRequired
|
|
44
|
-
permissions: PropTypes.arrayOf(PropTypes.object).isRequired
|
|
91
|
+
document: PropTypes.object.isRequired
|
|
45
92
|
};
|
|
46
93
|
export default LinkRecipientPermissions;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
import React, { forwardRef } from 'react';
|
|
3
|
+
import ActionsMenuItem from 'cozy-ui/transpiled/react/ActionsMenu/ActionsMenuItem';
|
|
4
|
+
import Icon from 'cozy-ui/transpiled/react/Icon';
|
|
5
|
+
import TrashIcon from 'cozy-ui/transpiled/react/Icons/Trash';
|
|
6
|
+
import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon';
|
|
7
|
+
import ListItemText from 'cozy-ui/transpiled/react/ListItemText';
|
|
8
|
+
import Typography from 'cozy-ui/transpiled/react/Typography';
|
|
9
|
+
|
|
10
|
+
var revokeLink = function revokeLink(_ref) {
|
|
11
|
+
var t = _ref.t,
|
|
12
|
+
handleRevocation = _ref.handleRevocation;
|
|
13
|
+
var title = t('Share.permissionLink.deactivate');
|
|
14
|
+
return {
|
|
15
|
+
name: 'revokeLink',
|
|
16
|
+
label: title,
|
|
17
|
+
icon: TrashIcon,
|
|
18
|
+
action: handleRevocation,
|
|
19
|
+
Component: /*#__PURE__*/forwardRef(function RevokeLinkItem(props, ref) {
|
|
20
|
+
return /*#__PURE__*/React.createElement(ActionsMenuItem, _extends({}, props, {
|
|
21
|
+
ref: ref
|
|
22
|
+
}), /*#__PURE__*/React.createElement(ListItemIcon, null, /*#__PURE__*/React.createElement(Icon, {
|
|
23
|
+
icon: TrashIcon,
|
|
24
|
+
color: "var(--errorColor)"
|
|
25
|
+
})), /*#__PURE__*/React.createElement(ListItemText, {
|
|
26
|
+
primary: /*#__PURE__*/React.createElement(Typography, {
|
|
27
|
+
color: "error"
|
|
28
|
+
}, title)
|
|
29
|
+
}));
|
|
30
|
+
})
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export { revokeLink };
|
|
@@ -14,7 +14,7 @@ import withLocales from '../../hoc/withLocales';
|
|
|
14
14
|
import { useSharingContext } from '../../hooks/useSharingContext';
|
|
15
15
|
import { FederatedFolderModal } from '../FederatedFolder/FederatedFolderModal';
|
|
16
16
|
export var ShareModal = withLocales(function (props) {
|
|
17
|
-
var _location$state
|
|
17
|
+
var _location$state;
|
|
18
18
|
|
|
19
19
|
var location = useLocation === null || useLocation === void 0 ? void 0 : useLocation();
|
|
20
20
|
var locationProps = location === null || location === void 0 || (_location$state = location.state) === null || _location$state === void 0 ? void 0 : _location$state.modalProps;
|
|
@@ -59,12 +59,8 @@ export var ShareModal = withLocales(function (props) {
|
|
|
59
59
|
}();
|
|
60
60
|
|
|
61
61
|
var isFederatedMode = flag('drive.federated-shared-folder.enabled');
|
|
62
|
-
var hasSharings = ((_byDocId$document$id = byDocId[document.id]) === null || _byDocId$document$id === void 0 || (_byDocId$document$id = _byDocId$document$id.sharings) === null || _byDocId$document$id === void 0 ? void 0 : _byDocId$document$id.length) > 0; // In federated mode, show FederatedFolderModal for unshared documents (sharing an existing folder).
|
|
63
|
-
// When isFederatedMode && hasSharings is true, we intentionally fall through to the legacy
|
|
64
|
-
// modals (EditableSharingModal / SharingDetailsModal) because federated-shared folders are
|
|
65
|
-
// handled by those components.
|
|
66
62
|
|
|
67
|
-
if (isFederatedMode && allLoaded
|
|
63
|
+
if (isFederatedMode && allLoaded) {
|
|
68
64
|
return /*#__PURE__*/React.createElement(FederatedFolderModal, {
|
|
69
65
|
document: document,
|
|
70
66
|
onClose: rest.onClose
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-sharing",
|
|
3
|
-
"version": "28.
|
|
3
|
+
"version": "28.8.0",
|
|
4
4
|
"description": "Provides sharing login for React applications.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"author": "Cozy",
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
"sideEffects": [
|
|
84
84
|
"*.css"
|
|
85
85
|
],
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "a7ccd04fd67452a62b7459d3814fcff565063289"
|
|
87
87
|
}
|
package/src/SharingProvider.jsx
CHANGED
|
@@ -25,7 +25,9 @@ export const FederatedFolderModal = withLocales(
|
|
|
25
25
|
getFederatedShareLink,
|
|
26
26
|
getDocumentPermissions,
|
|
27
27
|
getOwner,
|
|
28
|
-
getSharingById
|
|
28
|
+
getSharingById,
|
|
29
|
+
getRecipients,
|
|
30
|
+
revoke
|
|
29
31
|
} = useSharingContext()
|
|
30
32
|
const { showAlert } = useAlert()
|
|
31
33
|
|
|
@@ -107,18 +109,29 @@ export const FederatedFolderModal = withLocales(
|
|
|
107
109
|
}
|
|
108
110
|
}
|
|
109
111
|
|
|
110
|
-
const onRevoke =
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
const onRevoke = async (documentOrIndex, sharingId, memberIndex) => {
|
|
113
|
+
if (
|
|
114
|
+
typeof documentOrIndex === 'string' &&
|
|
115
|
+
documentOrIndex.startsWith(RECIPIENT_INDEX_PREFIX)
|
|
116
|
+
) {
|
|
117
|
+
const _id = documentOrIndex.split(RECIPIENT_INDEX_PREFIX)[1]
|
|
118
|
+
setFederatedRecipients(prev => ({
|
|
115
119
|
recipients: prev.recipients.filter(r => r._id !== _id),
|
|
116
120
|
readOnlyRecipients: prev.readOnlyRecipients.filter(r => r._id !== _id)
|
|
117
|
-
}
|
|
118
|
-
}
|
|
121
|
+
}))
|
|
122
|
+
} else {
|
|
123
|
+
await revoke(documentOrIndex, sharingId, memberIndex)
|
|
124
|
+
}
|
|
119
125
|
}
|
|
120
126
|
|
|
121
|
-
const
|
|
127
|
+
const existingRecipients = existingDocument
|
|
128
|
+
? getRecipients(existingDocument._id)
|
|
129
|
+
: []
|
|
130
|
+
|
|
131
|
+
const recipients = [
|
|
132
|
+
...existingRecipients,
|
|
133
|
+
...formatRecipients(federatedRecipients)
|
|
134
|
+
]
|
|
122
135
|
|
|
123
136
|
const modalTitle = t('FederatedFolder.shareTitle', { name: folderName })
|
|
124
137
|
|
|
@@ -131,7 +144,7 @@ export const FederatedFolderModal = withLocales(
|
|
|
131
144
|
createContact={contact => client.create('io.cozy.contacts', contact)}
|
|
132
145
|
recipients={recipients}
|
|
133
146
|
readOnlyRecipients={federatedRecipients.readOnlyRecipients}
|
|
134
|
-
currentRecipients={
|
|
147
|
+
currentRecipients={existingRecipients}
|
|
135
148
|
onRevoke={onRevoke}
|
|
136
149
|
onSetType={onSetType}
|
|
137
150
|
onSend={onSend}
|
|
@@ -7,15 +7,19 @@ import { FederatedFolderModal } from './FederatedFolderModal'
|
|
|
7
7
|
import AppLike from '../SharingBanner/test/AppLike'
|
|
8
8
|
|
|
9
9
|
const mockShare = jest.fn()
|
|
10
|
+
const mockRevoke = jest.fn()
|
|
10
11
|
const mockGetSharingLink = jest.fn()
|
|
12
|
+
const mockGetRecipients = jest.fn().mockReturnValue([])
|
|
11
13
|
const mockShowAlert = jest.fn()
|
|
12
14
|
const mockOnClose = jest.fn()
|
|
13
15
|
|
|
14
16
|
jest.mock('../../hooks/useSharingContext', () => ({
|
|
15
17
|
useSharingContext: () => ({
|
|
16
18
|
share: mockShare,
|
|
19
|
+
revoke: mockRevoke,
|
|
17
20
|
getSharingLink: mockGetSharingLink,
|
|
18
|
-
getDocumentPermissions: jest.fn().mockReturnValue([])
|
|
21
|
+
getDocumentPermissions: jest.fn().mockReturnValue([]),
|
|
22
|
+
getRecipients: mockGetRecipients
|
|
19
23
|
})
|
|
20
24
|
}))
|
|
21
25
|
|
|
@@ -79,6 +83,12 @@ jest.mock('./DumbFederatedFolderModal', () => ({
|
|
|
79
83
|
>
|
|
80
84
|
Revoke
|
|
81
85
|
</button>
|
|
86
|
+
<button
|
|
87
|
+
data-testid="btn-revoke-existing"
|
|
88
|
+
onClick={() => onRevoke({ _id: 'folder-123' }, 'abc', 1)}
|
|
89
|
+
>
|
|
90
|
+
Revoke Existing
|
|
91
|
+
</button>
|
|
82
92
|
<button data-testid="btn-close" onClick={onClose}>
|
|
83
93
|
Close
|
|
84
94
|
</button>
|
|
@@ -135,6 +145,7 @@ describe('FederatedFolderModal', () => {
|
|
|
135
145
|
beforeEach(() => {
|
|
136
146
|
jest.clearAllMocks()
|
|
137
147
|
mockGetSharingLink.mockReturnValue('https://example.com/share/abc123')
|
|
148
|
+
mockGetRecipients.mockReturnValue([])
|
|
138
149
|
client = createTestClient()
|
|
139
150
|
sharingContextValue = createSharingContextValue()
|
|
140
151
|
})
|
|
@@ -356,6 +367,64 @@ describe('FederatedFolderModal', () => {
|
|
|
356
367
|
})
|
|
357
368
|
})
|
|
358
369
|
|
|
370
|
+
describe('existing recipients', () => {
|
|
371
|
+
const existingRecipients = [
|
|
372
|
+
{
|
|
373
|
+
index: 'sharing-abc-member-0',
|
|
374
|
+
name: 'Charlie',
|
|
375
|
+
email: 'charlie@example.com',
|
|
376
|
+
status: 'owner',
|
|
377
|
+
type: 'two-way',
|
|
378
|
+
sharingId: 'abc',
|
|
379
|
+
memberIndex: 0
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
index: 'sharing-abc-member-1',
|
|
383
|
+
name: 'Diana',
|
|
384
|
+
email: 'diana@example.com',
|
|
385
|
+
status: 'ready',
|
|
386
|
+
type: 'one-way',
|
|
387
|
+
sharingId: 'abc',
|
|
388
|
+
memberIndex: 1
|
|
389
|
+
}
|
|
390
|
+
]
|
|
391
|
+
|
|
392
|
+
it('should display existing recipients from getRecipients', async () => {
|
|
393
|
+
mockGetRecipients.mockReturnValue(existingRecipients)
|
|
394
|
+
const { getByTestId } = setup()
|
|
395
|
+
|
|
396
|
+
await waitFor(() => {
|
|
397
|
+
expect(getByTestId('recipients-count').textContent).toBe('2')
|
|
398
|
+
})
|
|
399
|
+
})
|
|
400
|
+
|
|
401
|
+
it('should pass existing recipients as currentRecipients', async () => {
|
|
402
|
+
mockGetRecipients.mockReturnValue(existingRecipients)
|
|
403
|
+
const { getByTestId } = setup()
|
|
404
|
+
|
|
405
|
+
await waitFor(() => {
|
|
406
|
+
expect(getByTestId('recipients-count').textContent).toBe('2')
|
|
407
|
+
})
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
it('should call revoke for existing member', async () => {
|
|
411
|
+
mockGetRecipients.mockReturnValue(existingRecipients)
|
|
412
|
+
mockRevoke.mockResolvedValueOnce({})
|
|
413
|
+
const { getByTestId } = setup()
|
|
414
|
+
|
|
415
|
+
await waitFor(() => {
|
|
416
|
+
expect(getByTestId('dumb-modal')).toBeTruthy()
|
|
417
|
+
})
|
|
418
|
+
|
|
419
|
+
// Simulate revoke of an existing member (non-virtual-shared-drive index)
|
|
420
|
+
fireEvent.click(getByTestId('btn-revoke-existing'))
|
|
421
|
+
|
|
422
|
+
await waitFor(() => {
|
|
423
|
+
expect(mockRevoke).toHaveBeenCalledWith({ _id: 'folder-123' }, 'abc', 1)
|
|
424
|
+
})
|
|
425
|
+
})
|
|
426
|
+
})
|
|
427
|
+
|
|
359
428
|
describe('createContact callback', () => {
|
|
360
429
|
it('should call client.create when creating a contact', async () => {
|
|
361
430
|
const { getByTestId } = setup()
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import PropTypes from 'prop-types'
|
|
2
|
-
import React from 'react'
|
|
2
|
+
import React, { useState } from 'react'
|
|
3
3
|
import { useI18n } from 'twake-i18n'
|
|
4
4
|
|
|
5
5
|
import Avatar from 'cozy-ui/transpiled/react/Avatar'
|
|
6
|
+
import DropdownButton from 'cozy-ui/transpiled/react/DropdownButton'
|
|
6
7
|
import Fade from 'cozy-ui/transpiled/react/Fade'
|
|
7
8
|
import Icon from 'cozy-ui/transpiled/react/Icon'
|
|
8
9
|
import LinkIcon from 'cozy-ui/transpiled/react/Icons/Link'
|
|
9
10
|
import ListItem from 'cozy-ui/transpiled/react/ListItem'
|
|
10
11
|
import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
|
|
11
12
|
import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
|
|
12
|
-
import Typography from 'cozy-ui/transpiled/react/Typography'
|
|
13
13
|
import useBreakpoints from 'cozy-ui/transpiled/react/providers/Breakpoints'
|
|
14
14
|
|
|
15
15
|
import LinkRecipientPermissions from './LinkRecipientPermissions'
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
} from '../../helpers/permissions'
|
|
21
21
|
import { FADE_IN_DURATION } from '../../helpers/recipients'
|
|
22
22
|
import { useSharingContext } from '../../hooks/useSharingContext'
|
|
23
|
+
import { ShareRestrictionModal } from '../ShareRestrictionModal/ShareRestrictionModal'
|
|
23
24
|
|
|
24
25
|
const LinkRecipient = props => {
|
|
25
26
|
const { t, f, lang } = useI18n()
|
|
@@ -28,6 +29,9 @@ const LinkRecipient = props => {
|
|
|
28
29
|
|
|
29
30
|
const { recipientConfirmationData, verifyRecipient, fadeIn, document } = props
|
|
30
31
|
|
|
32
|
+
const [openShareRestrictionModal, setOpenShareRestrictionModal] =
|
|
33
|
+
useState(false)
|
|
34
|
+
|
|
31
35
|
const permissions = getDocumentPermissions(document?._id)
|
|
32
36
|
const hasPassword = checkIsPermissionHasPassword(permissions)
|
|
33
37
|
const expiresDate = getPermissionExpiresDate(permissions)
|
|
@@ -63,10 +67,23 @@ const LinkRecipient = props => {
|
|
|
63
67
|
</Avatar>
|
|
64
68
|
</ListItemIcon>
|
|
65
69
|
<ListItemText
|
|
66
|
-
primary={
|
|
70
|
+
primary={
|
|
71
|
+
<DropdownButton
|
|
72
|
+
textVariant="body1"
|
|
73
|
+
onClick={() => setOpenShareRestrictionModal(true)}
|
|
74
|
+
>
|
|
75
|
+
{textPrimary}
|
|
76
|
+
</DropdownButton>
|
|
77
|
+
}
|
|
67
78
|
secondary={textSecondary}
|
|
68
79
|
/>
|
|
69
80
|
{RightPart}
|
|
81
|
+
{openShareRestrictionModal && (
|
|
82
|
+
<ShareRestrictionModal
|
|
83
|
+
file={document}
|
|
84
|
+
onClose={() => setOpenShareRestrictionModal(false)}
|
|
85
|
+
/>
|
|
86
|
+
)}
|
|
70
87
|
</ListItem>
|
|
71
88
|
</Fade>
|
|
72
89
|
)
|
|
@@ -2,18 +2,58 @@ import PropTypes from 'prop-types'
|
|
|
2
2
|
import React, { useState, useRef } from 'react'
|
|
3
3
|
import { useI18n } from 'twake-i18n'
|
|
4
4
|
|
|
5
|
+
import ActionsMenu from 'cozy-ui/transpiled/react/ActionsMenu'
|
|
6
|
+
import {
|
|
7
|
+
makeActions,
|
|
8
|
+
divider
|
|
9
|
+
} from 'cozy-ui/transpiled/react/ActionsMenu/Actions'
|
|
5
10
|
import DropdownButton from 'cozy-ui/transpiled/react/DropdownButton'
|
|
6
11
|
|
|
12
|
+
import { revokeLink } from './actions/revokeLink'
|
|
13
|
+
import { setReadOnlySharedPermission } from './actions/setReadOnlySharedPermission'
|
|
14
|
+
import { setReadWriteSharedPermission } from './actions/setReadWriteSharedPermission'
|
|
15
|
+
import { isOnlyReadOnlyLinkAllowed } from '../../helpers/link'
|
|
7
16
|
import { checkIsReadOnlyPermissions } from '../../helpers/permissions'
|
|
8
|
-
import {
|
|
17
|
+
import { useSharingContext } from '../../hooks/useSharingContext'
|
|
18
|
+
import { WRITE_PERMS, READ_ONLY_PERMS } from '../ShareRestrictionModal/helpers'
|
|
9
19
|
|
|
10
|
-
const LinkRecipientPermissions = ({ className, document
|
|
20
|
+
const LinkRecipientPermissions = ({ className, document }) => {
|
|
11
21
|
const { t } = useI18n()
|
|
12
22
|
const buttonRef = useRef()
|
|
13
|
-
const [
|
|
14
|
-
|
|
23
|
+
const [isMenuDisplayed, setMenuDisplayed] = useState(false)
|
|
24
|
+
const {
|
|
25
|
+
updateDocumentPermissions,
|
|
26
|
+
revokeSharingLink,
|
|
27
|
+
documentType,
|
|
28
|
+
getDocumentPermissions
|
|
29
|
+
} = useSharingContext()
|
|
15
30
|
|
|
31
|
+
const permissions = getDocumentPermissions(document._id)
|
|
16
32
|
const isReadOnlyPermissions = checkIsReadOnlyPermissions(permissions)
|
|
33
|
+
const type = isReadOnlyPermissions ? 'one-way' : 'two-way'
|
|
34
|
+
|
|
35
|
+
const toggleMenu = () => setMenuDisplayed(!isMenuDisplayed)
|
|
36
|
+
const hideMenu = () => setMenuDisplayed(false)
|
|
37
|
+
|
|
38
|
+
const handleSetType = newType => {
|
|
39
|
+
const verbs = newType === 'one-way' ? READ_ONLY_PERMS : WRITE_PERMS
|
|
40
|
+
updateDocumentPermissions(document, { verbs })
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const handleRevocation = () => revokeSharingLink(document)
|
|
44
|
+
|
|
45
|
+
const actionsToShow = [setReadOnlySharedPermission]
|
|
46
|
+
if (!isOnlyReadOnlyLinkAllowed({ documentType })) {
|
|
47
|
+
actionsToShow.push(setReadWriteSharedPermission)
|
|
48
|
+
}
|
|
49
|
+
actionsToShow.push(divider, revokeLink)
|
|
50
|
+
|
|
51
|
+
const actions = makeActions(actionsToShow, {
|
|
52
|
+
t,
|
|
53
|
+
type,
|
|
54
|
+
setType: handleSetType,
|
|
55
|
+
handleRevocation
|
|
56
|
+
})
|
|
17
57
|
|
|
18
58
|
return (
|
|
19
59
|
<div className={className}>
|
|
@@ -21,18 +61,17 @@ const LinkRecipientPermissions = ({ className, document, permissions }) => {
|
|
|
21
61
|
<DropdownButton
|
|
22
62
|
ref={buttonRef}
|
|
23
63
|
textVariant="body2"
|
|
24
|
-
onClick={
|
|
64
|
+
onClick={toggleMenu}
|
|
25
65
|
>
|
|
26
|
-
{t(
|
|
27
|
-
`Share.type.${isReadOnlyPermissions ? 'one-way' : 'two-way'}`
|
|
28
|
-
).toLowerCase()}
|
|
66
|
+
{t(`Share.type.${type}`).toLowerCase()}
|
|
29
67
|
</DropdownButton>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
68
|
+
<ActionsMenu
|
|
69
|
+
ref={buttonRef}
|
|
70
|
+
open={isMenuDisplayed}
|
|
71
|
+
actions={actions}
|
|
72
|
+
autoClose
|
|
73
|
+
onClose={hideMenu}
|
|
74
|
+
/>
|
|
36
75
|
</>
|
|
37
76
|
</div>
|
|
38
77
|
)
|
|
@@ -40,8 +79,7 @@ const LinkRecipientPermissions = ({ className, document, permissions }) => {
|
|
|
40
79
|
|
|
41
80
|
LinkRecipientPermissions.propTypes = {
|
|
42
81
|
className: PropTypes.string,
|
|
43
|
-
document: PropTypes.object.isRequired
|
|
44
|
-
permissions: PropTypes.arrayOf(PropTypes.object).isRequired
|
|
82
|
+
document: PropTypes.object.isRequired
|
|
45
83
|
}
|
|
46
84
|
|
|
47
85
|
export default LinkRecipientPermissions
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react'
|
|
2
|
+
|
|
3
|
+
import ActionsMenuItem from 'cozy-ui/transpiled/react/ActionsMenu/ActionsMenuItem'
|
|
4
|
+
import Icon from 'cozy-ui/transpiled/react/Icon'
|
|
5
|
+
import TrashIcon from 'cozy-ui/transpiled/react/Icons/Trash'
|
|
6
|
+
import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
|
|
7
|
+
import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
|
|
8
|
+
import Typography from 'cozy-ui/transpiled/react/Typography'
|
|
9
|
+
|
|
10
|
+
const revokeLink = ({ t, handleRevocation }) => {
|
|
11
|
+
const title = t('Share.permissionLink.deactivate')
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
name: 'revokeLink',
|
|
15
|
+
label: title,
|
|
16
|
+
icon: TrashIcon,
|
|
17
|
+
action: handleRevocation,
|
|
18
|
+
Component: forwardRef(function RevokeLinkItem(props, ref) {
|
|
19
|
+
return (
|
|
20
|
+
<ActionsMenuItem {...props} ref={ref}>
|
|
21
|
+
<ListItemIcon>
|
|
22
|
+
<Icon icon={TrashIcon} color="var(--errorColor)" />
|
|
23
|
+
</ListItemIcon>
|
|
24
|
+
<ListItemText
|
|
25
|
+
primary={<Typography color="error">{title}</Typography>}
|
|
26
|
+
/>
|
|
27
|
+
</ActionsMenuItem>
|
|
28
|
+
)
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { revokeLink }
|
|
@@ -35,13 +35,7 @@ export const ShareModal = withLocales(props => {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
const isFederatedMode = flag('drive.federated-shared-folder.enabled')
|
|
38
|
-
|
|
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) {
|
|
38
|
+
if (isFederatedMode && allLoaded) {
|
|
45
39
|
return <FederatedFolderModal document={document} onClose={rest.onClose} />
|
|
46
40
|
}
|
|
47
41
|
|