cozy-sharing 34.1.1 → 35.0.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,22 @@
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
+ # [35.0.0](https://github.com/cozy/cozy-libs/compare/cozy-sharing@34.1.2...cozy-sharing@35.0.0) (2026-06-25)
7
+
8
+ ### Features
9
+
10
+ - Ugrade twake-icons in libs and remove test mock ([46178bc](https://github.com/cozy/cozy-libs/commit/46178bcfff73fb8486b6a5209a14e306b497371b))
11
+
12
+ ### BREAKING CHANGES
13
+
14
+ - You must have `@linagora/twake-icons >=2.6.6`
15
+
16
+ ## [34.1.2](https://github.com/cozy/cozy-libs/compare/cozy-sharing@34.1.1...cozy-sharing@34.1.2) (2026-06-25)
17
+
18
+ ### Bug Fixes
19
+
20
+ - **sharing:** Call self revoke for federated recipients ([25e62c2](https://github.com/cozy/cozy-libs/commit/25e62c2416e4b91bdab1e06fbbd77b00574a0b11))
21
+
6
22
  ## [34.1.1](https://github.com/cozy/cozy-libs/compare/cozy-sharing@34.1.0...cozy-sharing@34.1.1) (2026-06-25)
7
23
 
8
24
  ### Bug Fixes
@@ -44,6 +44,7 @@ var FederatedFolderModalContent = function FederatedFolderModalContent(_ref) {
44
44
  var _sharedDriveSharing$a, _sharedDriveSharing$a2;
45
45
 
46
46
  var onClose = _ref.onClose,
47
+ onRevokeSuccess = _ref.onRevokeSuccess,
47
48
  existingDocument = _ref.document,
48
49
  autoOpenShareRestriction = _ref.autoOpenShareRestriction,
49
50
  showGenerateLinkButton = _ref.showGenerateLinkButton;
@@ -63,7 +64,9 @@ var FederatedFolderModalContent = function FederatedFolderModalContent(_ref) {
63
64
  getSharedParentPath = _useSharingContext.getSharedParentPath,
64
65
  hasSharedChild = _useSharingContext.hasSharedChild,
65
66
  hasSharedParent = _useSharingContext.hasSharedParent,
66
- revoke = _useSharingContext.revoke;
67
+ isOwner = _useSharingContext.isOwner,
68
+ revoke = _useSharingContext.revoke,
69
+ revokeSelf = _useSharingContext.revokeSelf;
67
70
 
68
71
  var _useAlert = useAlert(),
69
72
  showAlert = _useAlert.showAlert;
@@ -274,6 +277,31 @@ var FederatedFolderModalContent = function FederatedFolderModalContent(_ref) {
274
277
  var existingRecipients = sharedDriveSharing !== null && sharedDriveSharing !== void 0 && (_sharedDriveSharing$a2 = sharedDriveSharing.attributes) !== null && _sharedDriveSharing$a2 !== void 0 && _sharedDriveSharing$a2.members && documentId ? getRecipientsFromSharing(sharedDriveSharing, documentId) : sharingReferenceId ? getRecipients(sharingReferenceId) : [];
275
278
  var documentPermissions = documentId ? getDocumentPermissions(documentId) : [];
276
279
  var displayedLink = existingDocument !== null && existingDocument !== void 0 && existingDocument.driveId ? getFederatedShareLink(existingDocument) : getSharingLink(existingDocument === null || existingDocument === void 0 ? void 0 : existingDocument._id);
280
+ var isCurrentUserOwner = documentId ? isOwner(documentId) : false;
281
+
282
+ var handleRevokeSelf = /*#__PURE__*/function () {
283
+ var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(document) {
284
+ return _regeneratorRuntime.wrap(function _callee3$(_context3) {
285
+ while (1) switch (_context3.prev = _context3.next) {
286
+ case 0:
287
+ _context3.next = 2;
288
+ return revokeSelf(document);
289
+
290
+ case 2:
291
+ onRevokeSuccess === null || onRevokeSuccess === void 0 || onRevokeSuccess(document);
292
+
293
+ case 3:
294
+ case "end":
295
+ return _context3.stop();
296
+ }
297
+ }, _callee3);
298
+ }));
299
+
300
+ return function handleRevokeSelf(_x) {
301
+ return _ref4.apply(this, arguments);
302
+ };
303
+ }();
304
+
277
305
  useEffect(function () {
278
306
  if (!(existingDocument !== null && existingDocument !== void 0 && existingDocument.driveId)) return;
279
307
  if (!documentId) return;
@@ -283,39 +311,39 @@ var FederatedFolderModalContent = function FederatedFolderModalContent(_ref) {
283
311
  if (fetchedSharingLinksDocumentId === documentId) return;
284
312
 
285
313
  var fetchSharingLinks = /*#__PURE__*/function () {
286
- var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
287
- return _regeneratorRuntime.wrap(function _callee3$(_context3) {
288
- while (1) switch (_context3.prev = _context3.next) {
314
+ var _ref5 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4() {
315
+ return _regeneratorRuntime.wrap(function _callee4$(_context4) {
316
+ while (1) switch (_context4.prev = _context4.next) {
289
317
  case 0:
290
318
  setIsFetchingSharingLinks(true);
291
- _context3.prev = 1;
292
- _context3.next = 4;
319
+ _context4.prev = 1;
320
+ _context4.next = 4;
293
321
  return fetchSharedDriveSharingLinks(existingDocument);
294
322
 
295
323
  case 4:
296
- _context3.next = 9;
324
+ _context4.next = 9;
297
325
  break;
298
326
 
299
327
  case 6:
300
- _context3.prev = 6;
301
- _context3.t0 = _context3["catch"](1);
302
- log.error('Failed to fetch shared drive sharing links', _context3.t0);
328
+ _context4.prev = 6;
329
+ _context4.t0 = _context4["catch"](1);
330
+ log.error('Failed to fetch shared drive sharing links', _context4.t0);
303
331
 
304
332
  case 9:
305
- _context3.prev = 9;
333
+ _context4.prev = 9;
306
334
  setFetchedSharingLinksDocumentId(documentId);
307
335
  setIsFetchingSharingLinks(false);
308
- return _context3.finish(9);
336
+ return _context4.finish(9);
309
337
 
310
338
  case 13:
311
339
  case "end":
312
- return _context3.stop();
340
+ return _context4.stop();
313
341
  }
314
- }, _callee3, null, [[1, 6, 9, 13]]);
342
+ }, _callee4, null, [[1, 6, 9, 13]]);
315
343
  }));
316
344
 
317
345
  return function fetchSharingLinks() {
318
- return _ref4.apply(this, arguments);
346
+ return _ref5.apply(this, arguments);
319
347
  };
320
348
  }();
321
349
 
@@ -360,13 +388,14 @@ var FederatedFolderModalContent = function FederatedFolderModalContent(_ref) {
360
388
  onSelectedOptionChange: setSelectedOption,
361
389
  enableCreateContact: true
362
390
  }))), /*#__PURE__*/React.createElement(WhoHasAccess, {
363
- isOwner: true,
391
+ isOwner: isCurrentUserOwner,
364
392
  isSharedDrive: true,
365
393
  recipients: isSending ? frozenRecipients : existingRecipients,
366
394
  document: isSending ? frozenDoc : existingDocument,
367
395
  documentType: "Files",
368
396
  className: styles['share-dialog-members'],
369
397
  onRevoke: revoke,
398
+ onRevokeSelf: handleRevokeSelf,
370
399
  link: displayedLink
371
400
  })),
372
401
  actions: /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ShareByLink, {
@@ -389,6 +418,7 @@ export var FederatedFolderModal = withLocales(function (props) {
389
418
  });
390
419
  FederatedFolderModal.propTypes = {
391
420
  onClose: PropTypes.func.isRequired,
421
+ onRevokeSuccess: PropTypes.func,
392
422
  document: PropTypes.object.isRequired,
393
423
  autoOpenShareRestriction: PropTypes.bool,
394
424
  showGenerateLinkButton: PropTypes.bool
@@ -1,12 +1,12 @@
1
- import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
1
  import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
3
2
  import _extends from "@babel/runtime/helpers/extends";
3
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
4
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
4
5
 
5
6
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
6
7
 
7
8
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
8
9
 
9
- import _regeneratorRuntime from "@babel/runtime/regenerator";
10
10
  import { act, fireEvent, render, waitFor } from '@testing-library/react';
11
11
  import React from 'react';
12
12
  import { createMockClient } from 'cozy-client';
@@ -17,6 +17,8 @@ import AppLike from '../SharingBanner/test/AppLike';
17
17
  var mockShare = jest.fn();
18
18
  var mockShareByLink = jest.fn();
19
19
  var mockRevoke = jest.fn();
20
+ var mockRevokeSelf = jest.fn();
21
+ var mockIsOwner = jest.fn();
20
22
  var mockGetSharingLink = jest.fn();
21
23
  var mockGetFederatedShareLink = jest.fn();
22
24
  var mockGetRecipients = jest.fn().mockReturnValue([]);
@@ -34,6 +36,8 @@ jest.mock('../../hooks/useSharingContext', function () {
34
36
  share: mockShare,
35
37
  shareByLink: mockShareByLink,
36
38
  revoke: mockRevoke,
39
+ revokeSelf: mockRevokeSelf,
40
+ isOwner: mockIsOwner,
37
41
  getSharingLink: mockGetSharingLink,
38
42
  getFederatedShareLink: mockGetFederatedShareLink,
39
43
  getDocumentPermissions: mockGetDocumentPermissions,
@@ -123,6 +127,9 @@ describe('FederatedFolderModal', function () {
123
127
  var sharingContextValue;
124
128
  beforeEach(function () {
125
129
  jest.clearAllMocks();
130
+ mockRevoke.mockResolvedValue();
131
+ mockRevokeSelf.mockResolvedValue();
132
+ mockIsOwner.mockReturnValue(false);
126
133
  mockGetDocumentPermissions.mockReturnValue([]);
127
134
  mockFetchSharedDriveSharingLinks.mockResolvedValue([]);
128
135
  mockGetSharingLink.mockReturnValue('https://example.com/share/abc123');
@@ -147,6 +154,9 @@ describe('FederatedFolderModal', function () {
147
154
  }
148
155
  });
149
156
  client = createTestClient();
157
+ client.options = _objectSpread(_objectSpread({}, client.options), {}, {
158
+ uri: 'https://me.mycozy.cloud'
159
+ });
150
160
  sharingContextValue = createSharingContextValue();
151
161
  usePendingRecipients.mockReturnValue({
152
162
  pendingRecipients: [],
@@ -673,35 +683,76 @@ describe('FederatedFolderModal', function () {
673
683
  }, _callee18);
674
684
  })));
675
685
  });
676
- describe('onClose callback', function () {
677
- it('should call onClose when close button is clicked', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee19() {
678
- var _setup16, findByRole, closeButton;
686
+ describe('onRevoke callback', function () {
687
+ var currentUserRecipient = {
688
+ instance: 'https://me.mycozy.cloud',
689
+ memberIndex: 1,
690
+ sharingId: 'sharing-id',
691
+ type: 'two-way'
692
+ };
693
+ it('should call onRevokeSelf and onRevokeSuccess when the current user is revoked', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee19() {
694
+ var onRevokeSuccess, _setup16, findByLabelText;
679
695
 
680
696
  return _regeneratorRuntime.wrap(function _callee19$(_context19) {
681
697
  while (1) switch (_context19.prev = _context19.next) {
682
698
  case 0:
683
- _setup16 = setup(), findByRole = _setup16.findByRole;
684
- _context19.next = 3;
699
+ onRevokeSuccess = jest.fn();
700
+ mockGetRecipients.mockReturnValue([currentUserRecipient]);
701
+ _setup16 = setup({
702
+ onRevokeSuccess: onRevokeSuccess
703
+ }), findByLabelText = _setup16.findByLabelText;
704
+ _context19.t0 = fireEvent;
705
+ _context19.next = 6;
706
+ return findByLabelText('Remove from sharing');
707
+
708
+ case 6:
709
+ _context19.t1 = _context19.sent;
710
+
711
+ _context19.t0.click.call(_context19.t0, _context19.t1);
712
+
713
+ _context19.next = 10;
714
+ return waitFor(function () {
715
+ expect(mockRevokeSelf).toHaveBeenCalledWith(mockDocument);
716
+ expect(mockRevoke).not.toHaveBeenCalled();
717
+ expect(onRevokeSuccess).toHaveBeenCalledWith(mockDocument);
718
+ });
719
+
720
+ case 10:
721
+ case "end":
722
+ return _context19.stop();
723
+ }
724
+ }, _callee19);
725
+ })));
726
+ });
727
+ describe('onClose callback', function () {
728
+ it('should call onClose when close button is clicked', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee20() {
729
+ var _setup17, findByRole, closeButton;
730
+
731
+ return _regeneratorRuntime.wrap(function _callee20$(_context20) {
732
+ while (1) switch (_context20.prev = _context20.next) {
733
+ case 0:
734
+ _setup17 = setup(), findByRole = _setup17.findByRole;
735
+ _context20.next = 3;
685
736
  return findByRole('button', {
686
737
  name: /close/i
687
738
  });
688
739
 
689
740
  case 3:
690
- closeButton = _context19.sent;
741
+ closeButton = _context20.sent;
691
742
  fireEvent.click(closeButton);
692
743
  expect(mockOnClose).toHaveBeenCalled();
693
744
 
694
745
  case 6:
695
746
  case "end":
696
- return _context19.stop();
747
+ return _context20.stop();
697
748
  }
698
- }, _callee19);
749
+ }, _callee20);
699
750
  })));
700
- it('should ask confirmation when close button is clicked with pending recipients', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee20() {
701
- var _setup17, getByRole, findByRole, getByText, closeButton;
751
+ it('should ask confirmation when close button is clicked with pending recipients', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee21() {
752
+ var _setup18, getByRole, findByRole, getByText, closeButton;
702
753
 
703
- return _regeneratorRuntime.wrap(function _callee20$(_context20) {
704
- while (1) switch (_context20.prev = _context20.next) {
754
+ return _regeneratorRuntime.wrap(function _callee21$(_context21) {
755
+ while (1) switch (_context21.prev = _context21.next) {
705
756
  case 0:
706
757
  usePendingRecipients.mockReturnValue({
707
758
  pendingRecipients: [{
@@ -712,14 +763,14 @@ describe('FederatedFolderModal', function () {
712
763
  selectedOption: 'readWrite',
713
764
  setSelectedOption: jest.fn()
714
765
  });
715
- _setup17 = setup(), getByRole = _setup17.getByRole, findByRole = _setup17.findByRole, getByText = _setup17.getByText;
716
- _context20.next = 4;
766
+ _setup18 = setup(), getByRole = _setup18.getByRole, findByRole = _setup18.findByRole, getByText = _setup18.getByText;
767
+ _context21.next = 4;
717
768
  return findByRole('button', {
718
769
  name: /close/i
719
770
  });
720
771
 
721
772
  case 4:
722
- closeButton = _context20.sent;
773
+ closeButton = _context21.sent;
723
774
  fireEvent.click(closeButton);
724
775
  expect(getByText("Discard the changes you haven't saved?")).toBeTruthy();
725
776
  expect(mockOnClose).not.toHaveBeenCalled();
@@ -730,15 +781,15 @@ describe('FederatedFolderModal', function () {
730
781
 
731
782
  case 10:
732
783
  case "end":
733
- return _context20.stop();
784
+ return _context21.stop();
734
785
  }
735
- }, _callee20);
786
+ }, _callee21);
736
787
  })));
737
- it('should close after discard confirmation', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee21() {
738
- var _setup18, getByRole, findByRole, getByText, closeButton;
788
+ it('should close after discard confirmation', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee22() {
789
+ var _setup19, getByRole, findByRole, getByText, closeButton;
739
790
 
740
- return _regeneratorRuntime.wrap(function _callee21$(_context21) {
741
- while (1) switch (_context21.prev = _context21.next) {
791
+ return _regeneratorRuntime.wrap(function _callee22$(_context22) {
792
+ while (1) switch (_context22.prev = _context22.next) {
742
793
  case 0:
743
794
  usePendingRecipients.mockReturnValue({
744
795
  pendingRecipients: [{
@@ -749,14 +800,14 @@ describe('FederatedFolderModal', function () {
749
800
  selectedOption: 'readWrite',
750
801
  setSelectedOption: jest.fn()
751
802
  });
752
- _setup18 = setup(), getByRole = _setup18.getByRole, findByRole = _setup18.findByRole, getByText = _setup18.getByText;
753
- _context21.next = 4;
803
+ _setup19 = setup(), getByRole = _setup19.getByRole, findByRole = _setup19.findByRole, getByText = _setup19.getByText;
804
+ _context22.next = 4;
754
805
  return findByRole('button', {
755
806
  name: /close/i
756
807
  });
757
808
 
758
809
  case 4:
759
- closeButton = _context21.sent;
810
+ closeButton = _context22.sent;
760
811
  fireEvent.click(closeButton);
761
812
  expect(getByText("Discard the changes you haven't saved?")).toBeTruthy();
762
813
  fireEvent.click(getByRole('button', {
@@ -766,9 +817,9 @@ describe('FederatedFolderModal', function () {
766
817
 
767
818
  case 9:
768
819
  case "end":
769
- return _context21.stop();
820
+ return _context22.stop();
770
821
  }
771
- }, _callee21);
822
+ }, _callee22);
772
823
  })));
773
824
  });
774
825
  });
@@ -20,6 +20,8 @@ var log = minilog('MemberRecipientPermissions');
20
20
 
21
21
  var MemberRecipientPermissions = function MemberRecipientPermissions(_ref) {
22
22
  var isOwner = _ref.isOwner,
23
+ _ref$canManageSharing = _ref.canManageSharing,
24
+ canManageSharing = _ref$canManageSharing === void 0 ? isOwner : _ref$canManageSharing,
23
25
  isReadOnly = _ref.isReadOnly,
24
26
  status = _ref.status,
25
27
  instance = _ref.instance,
@@ -54,7 +56,7 @@ var MemberRecipientPermissions = function MemberRecipientPermissions(_ref) {
54
56
 
55
57
  var instanceMatchesClient = instance !== undefined && instance === client.options.uri;
56
58
  var contactIsOwner = status === 'owner';
57
- var shouldShowMenu = !isReadOnly && !revoking && !contactIsOwner && (instanceMatchesClient && !isOwner || isOwner);
59
+ var shouldShowMenu = !isReadOnly && !revoking && !contactIsOwner && (instanceMatchesClient && !isOwner || canManageSharing);
58
60
  var toggleMenu = useCallback(function () {
59
61
  setMenuDisplayed(function (displayed) {
60
62
  return !displayed;
@@ -70,13 +72,13 @@ var MemberRecipientPermissions = function MemberRecipientPermissions(_ref) {
70
72
  setRevoking(true);
71
73
  _context.prev = 1;
72
74
 
73
- if (!isOwner) {
75
+ if (!(instanceMatchesClient && !isOwner)) {
74
76
  _context.next = 7;
75
77
  break;
76
78
  }
77
79
 
78
80
  _context.next = 5;
79
- return onRevoke(document, sharingId, memberIndex);
81
+ return onRevokeSelf(document);
80
82
 
81
83
  case 5:
82
84
  _context.next = 9;
@@ -84,7 +86,7 @@ var MemberRecipientPermissions = function MemberRecipientPermissions(_ref) {
84
86
 
85
87
  case 7:
86
88
  _context.next = 9;
87
- return onRevokeSelf(document);
89
+ return onRevoke(document, sharingId, memberIndex);
88
90
 
89
91
  case 9:
90
92
  _context.prev = 9;
@@ -96,7 +98,7 @@ var MemberRecipientPermissions = function MemberRecipientPermissions(_ref) {
96
98
  return _context.stop();
97
99
  }
98
100
  }, _callee, null, [[1,, 9, 12]]);
99
- })), [isOwner, onRevoke, onRevokeSelf, document, sharingId, memberIndex]);
101
+ })), [document, instanceMatchesClient, isOwner, memberIndex, onRevoke, onRevokeSelf, sharingId]);
100
102
  var setType = useCallback( /*#__PURE__*/function () {
101
103
  var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(newType) {
102
104
  return _regeneratorRuntime.wrap(function _callee2$(_context2) {
@@ -9,6 +9,7 @@ var RecipientList = function RecipientList(_ref) {
9
9
  var recipients = _ref.recipients,
10
10
  recipientsToBeConfirmed = _ref.recipientsToBeConfirmed,
11
11
  isOwner = _ref.isOwner,
12
+ canManageSharing = _ref.canManageSharing,
12
13
  isSharedDrive = _ref.isSharedDrive,
13
14
  isReadOnly = _ref.isReadOnly,
14
15
  document = _ref.document,
@@ -40,6 +41,7 @@ var RecipientList = function RecipientList(_ref) {
40
41
  return /*#__PURE__*/React.createElement(MemberRecipient, _extends({}, recipient, {
41
42
  key: recipient.index,
42
43
  isOwner: isOwner,
44
+ canManageSharing: canManageSharing,
43
45
  isSharedDrive: isSharedDrive,
44
46
  isReadOnly: isReadOnly,
45
47
  document: document,
@@ -63,7 +63,8 @@ export var ShareModal = withLocales(function (props) {
63
63
 
64
64
  if (isFederatedMode && allLoaded) {
65
65
  return /*#__PURE__*/React.createElement(FederatedFolderModal, _extends({
66
- document: document
66
+ document: document,
67
+ onRevokeSuccess: onRevokeSuccess
67
68
  }, rest));
68
69
  }
69
70
 
@@ -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$canManageSharin = _ref2.canManageSharing,
34
+ canManageSharing = _ref2$canManageSharin === void 0 ? isOwner : _ref2$canManageSharin,
33
35
  _ref2$isSharedDrive = _ref2.isSharedDrive,
34
36
  isSharedDrive = _ref2$isSharedDrive === void 0 ? false : _ref2$isSharedDrive,
35
37
  _ref2$isReadOnly = _ref2.isReadOnly,
@@ -68,6 +70,7 @@ var WhoHasAccess = function WhoHasAccess(_ref2) {
68
70
  recipients: recipients,
69
71
  recipientsToBeConfirmed: recipientsToBeConfirmed,
70
72
  isOwner: isOwner,
73
+ canManageSharing: canManageSharing,
71
74
  isSharedDrive: isSharedDrive,
72
75
  isReadOnly: isReadOnly,
73
76
  document: document,
@@ -81,6 +84,7 @@ var WhoHasAccess = function WhoHasAccess(_ref2) {
81
84
 
82
85
  WhoHasAccess.propTypes = {
83
86
  isOwner: PropTypes.bool,
87
+ canManageSharing: PropTypes.bool,
84
88
  showOwner: PropTypes.bool,
85
89
  recipients: PropTypes.array.isRequired,
86
90
  recipientsToBeConfirmed: PropTypes.arrayOf(PropTypes.shape({
package/jest.config.js CHANGED
@@ -26,8 +26,7 @@ module.exports = {
26
26
  '^cozy-client$': '<rootDir>/node_modules/cozy-client/dist/index.js',
27
27
  '^cozy-client/dist/types$':
28
28
  '<rootDir>/node_modules/cozy-client/dist/types.js',
29
- '^twake-i18n$': '<rootDir>/../twake-i18n/dist/index.js',
30
- '^@linagora/twake-icons$': '<rootDir>/jestHelpers/mocks/twake-icons.js'
29
+ '^twake-i18n$': '<rootDir>/../twake-i18n/dist/index.js'
31
30
  },
32
31
  transformIgnorePatterns: [
33
32
  '<rootDir>/node_modules/(?!(cozy-ui|cozy-harvest-lib|cozy-ui-plus))'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-sharing",
3
- "version": "34.1.1",
3
+ "version": "35.0.0",
4
4
  "description": "Provides sharing login for React applications.",
5
5
  "main": "dist/index.js",
6
6
  "author": "Cozy",
@@ -40,7 +40,7 @@
40
40
  "devDependencies": {
41
41
  "@babel/cli": "7.16.8",
42
42
  "@babel/core": "7.16.12",
43
- "@linagora/twake-icons": "^2.6.2",
43
+ "@linagora/twake-icons": "^2.6.6",
44
44
  "@storybook/addon-essentials": "7.6.0",
45
45
  "@storybook/addon-interactions": "7.6.0",
46
46
  "@storybook/addon-links": "7.6.0",
@@ -59,7 +59,7 @@
59
59
  "cozy-intent": "^2.31.1",
60
60
  "cozy-minilog": "^3.10.1",
61
61
  "cozy-ui": "^139.0.2",
62
- "cozy-ui-plus": "^9.0.0",
62
+ "cozy-ui-plus": "^10.0.0",
63
63
  "jest": "30.3.0",
64
64
  "jest-environment-jsdom": "30.3.0",
65
65
  "react": "16.12.0",
@@ -69,7 +69,7 @@
69
69
  "twake-i18n": "^0.4.1"
70
70
  },
71
71
  "peerDependencies": {
72
- "@linagora/twake-icons": ">=2.6.2",
72
+ "@linagora/twake-icons": ">=2.6.6",
73
73
  "cozy-client": ">=60.28.0",
74
74
  "cozy-device-helper": ">=4.0.0",
75
75
  "cozy-flags": ">=4.8.1",
@@ -85,5 +85,5 @@
85
85
  "sideEffects": [
86
86
  "*.css"
87
87
  ],
88
- "gitHead": "f5d5d581fa16bf65f8411057274fae219f7ee709"
88
+ "gitHead": "113feef92df29d2b6fe30bad984d3f5a70659b6f"
89
89
  }
@@ -27,6 +27,7 @@ const log = minilog('FederatedFolderModal')
27
27
 
28
28
  const FederatedFolderModalContent = ({
29
29
  onClose,
30
+ onRevokeSuccess,
30
31
  document: existingDocument,
31
32
  autoOpenShareRestriction,
32
33
  showGenerateLinkButton
@@ -44,7 +45,9 @@ const FederatedFolderModalContent = ({
44
45
  getSharedParentPath,
45
46
  hasSharedChild,
46
47
  hasSharedParent,
47
- revoke
48
+ isOwner,
49
+ revoke,
50
+ revokeSelf
48
51
  } = useSharingContext()
49
52
  const { showAlert } = useAlert()
50
53
  const { showConfirmDialog, closeConfirmDialog } = useConfirmDialog()
@@ -220,6 +223,12 @@ const FederatedFolderModalContent = ({
220
223
  const displayedLink = existingDocument?.driveId
221
224
  ? getFederatedShareLink(existingDocument)
222
225
  : getSharingLink(existingDocument?._id)
226
+ const isCurrentUserOwner = documentId ? isOwner(documentId) : false
227
+
228
+ const handleRevokeSelf = async document => {
229
+ await revokeSelf(document)
230
+ onRevokeSuccess?.(document)
231
+ }
223
232
 
224
233
  useEffect(() => {
225
234
  if (!existingDocument?.driveId) return
@@ -313,13 +322,14 @@ const FederatedFolderModalContent = ({
313
322
  )}
314
323
  </div>
315
324
  <WhoHasAccess
316
- isOwner
325
+ isOwner={isCurrentUserOwner}
317
326
  isSharedDrive
318
327
  recipients={isSending ? frozenRecipients : existingRecipients}
319
328
  document={isSending ? frozenDoc : existingDocument}
320
329
  documentType="Files"
321
330
  className={styles['share-dialog-members']}
322
331
  onRevoke={revoke}
332
+ onRevokeSelf={handleRevokeSelf}
323
333
  link={displayedLink}
324
334
  />
325
335
  </div>
@@ -355,6 +365,7 @@ export const FederatedFolderModal = withLocales(props => (
355
365
 
356
366
  FederatedFolderModal.propTypes = {
357
367
  onClose: PropTypes.func.isRequired,
368
+ onRevokeSuccess: PropTypes.func,
358
369
  document: PropTypes.object.isRequired,
359
370
  autoOpenShareRestriction: PropTypes.bool,
360
371
  showGenerateLinkButton: PropTypes.bool
@@ -11,6 +11,8 @@ import AppLike from '../SharingBanner/test/AppLike'
11
11
  const mockShare = jest.fn()
12
12
  const mockShareByLink = jest.fn()
13
13
  const mockRevoke = jest.fn()
14
+ const mockRevokeSelf = jest.fn()
15
+ const mockIsOwner = jest.fn()
14
16
  const mockGetSharingLink = jest.fn()
15
17
  const mockGetFederatedShareLink = jest.fn()
16
18
  const mockGetRecipients = jest.fn().mockReturnValue([])
@@ -27,6 +29,8 @@ jest.mock('../../hooks/useSharingContext', () => ({
27
29
  share: mockShare,
28
30
  shareByLink: mockShareByLink,
29
31
  revoke: mockRevoke,
32
+ revokeSelf: mockRevokeSelf,
33
+ isOwner: mockIsOwner,
30
34
  getSharingLink: mockGetSharingLink,
31
35
  getFederatedShareLink: mockGetFederatedShareLink,
32
36
  getDocumentPermissions: mockGetDocumentPermissions,
@@ -101,6 +105,9 @@ describe('FederatedFolderModal', () => {
101
105
 
102
106
  beforeEach(() => {
103
107
  jest.clearAllMocks()
108
+ mockRevoke.mockResolvedValue()
109
+ mockRevokeSelf.mockResolvedValue()
110
+ mockIsOwner.mockReturnValue(false)
104
111
  mockGetDocumentPermissions.mockReturnValue([])
105
112
  mockFetchSharedDriveSharingLinks.mockResolvedValue([])
106
113
  mockGetSharingLink.mockReturnValue('https://example.com/share/abc123')
@@ -125,6 +132,10 @@ describe('FederatedFolderModal', () => {
125
132
  }
126
133
  })
127
134
  client = createTestClient()
135
+ client.options = {
136
+ ...client.options,
137
+ uri: 'https://me.mycozy.cloud'
138
+ }
128
139
  sharingContextValue = createSharingContextValue()
129
140
  usePendingRecipients.mockReturnValue({
130
141
  pendingRecipients: [],
@@ -438,6 +449,30 @@ describe('FederatedFolderModal', () => {
438
449
  })
439
450
  })
440
451
 
452
+ describe('onRevoke callback', () => {
453
+ const currentUserRecipient = {
454
+ instance: 'https://me.mycozy.cloud',
455
+ memberIndex: 1,
456
+ sharingId: 'sharing-id',
457
+ type: 'two-way'
458
+ }
459
+
460
+ it('should call onRevokeSelf and onRevokeSuccess when the current user is revoked', async () => {
461
+ const onRevokeSuccess = jest.fn()
462
+ mockGetRecipients.mockReturnValue([currentUserRecipient])
463
+
464
+ const { findByLabelText } = setup({ onRevokeSuccess })
465
+
466
+ fireEvent.click(await findByLabelText('Remove from sharing'))
467
+
468
+ await waitFor(() => {
469
+ expect(mockRevokeSelf).toHaveBeenCalledWith(mockDocument)
470
+ expect(mockRevoke).not.toHaveBeenCalled()
471
+ expect(onRevokeSuccess).toHaveBeenCalledWith(mockDocument)
472
+ })
473
+ })
474
+ })
475
+
441
476
  describe('onClose callback', () => {
442
477
  it('should call onClose when close button is clicked', async () => {
443
478
  const { findByRole } = setup()
@@ -20,6 +20,7 @@ const log = minilog('MemberRecipientPermissions')
20
20
 
21
21
  const MemberRecipientPermissions = ({
22
22
  isOwner,
23
+ canManageSharing = isOwner,
23
24
  isReadOnly,
24
25
  status,
25
26
  instance,
@@ -46,7 +47,7 @@ const MemberRecipientPermissions = ({
46
47
  !isReadOnly &&
47
48
  !revoking &&
48
49
  !contactIsOwner &&
49
- ((instanceMatchesClient && !isOwner) || isOwner)
50
+ ((instanceMatchesClient && !isOwner) || canManageSharing)
50
51
 
51
52
  const toggleMenu = useCallback(() => {
52
53
  setMenuDisplayed(displayed => !displayed)
@@ -58,15 +59,23 @@ const MemberRecipientPermissions = ({
58
59
  const handleRevocation = useCallback(async () => {
59
60
  setRevoking(true)
60
61
  try {
61
- if (isOwner) {
62
- await onRevoke(document, sharingId, memberIndex)
63
- } else {
62
+ if (instanceMatchesClient && !isOwner) {
64
63
  await onRevokeSelf(document)
64
+ } else {
65
+ await onRevoke(document, sharingId, memberIndex)
65
66
  }
66
67
  } finally {
67
68
  setRevoking(false)
68
69
  }
69
- }, [isOwner, onRevoke, onRevokeSelf, document, sharingId, memberIndex])
70
+ }, [
71
+ document,
72
+ instanceMatchesClient,
73
+ isOwner,
74
+ memberIndex,
75
+ onRevoke,
76
+ onRevokeSelf,
77
+ sharingId
78
+ ])
70
79
 
71
80
  const setType = useCallback(
72
81
  async newType => {
@@ -9,6 +9,7 @@ const RecipientList = ({
9
9
  recipients,
10
10
  recipientsToBeConfirmed,
11
11
  isOwner,
12
+ canManageSharing,
12
13
  isSharedDrive,
13
14
  isReadOnly,
14
15
  document,
@@ -50,6 +51,7 @@ const RecipientList = ({
50
51
  {...recipient}
51
52
  key={recipient.index}
52
53
  isOwner={isOwner}
54
+ canManageSharing={canManageSharing}
53
55
  isSharedDrive={isSharedDrive}
54
56
  isReadOnly={isReadOnly}
55
57
  document={document}
@@ -38,7 +38,13 @@ export const ShareModal = withLocales(props => {
38
38
  if (isEditable) {
39
39
  const isFederatedMode = flag('drive.federated-shared-folder.enabled')
40
40
  if (isFederatedMode && allLoaded) {
41
- return <FederatedFolderModal document={document} {...rest} />
41
+ return (
42
+ <FederatedFolderModal
43
+ document={document}
44
+ onRevokeSuccess={onRevokeSuccess}
45
+ {...rest}
46
+ />
47
+ )
42
48
  }
43
49
 
44
50
  return <EditableSharingModal document={document} {...rest} />
@@ -28,6 +28,7 @@ const RecipientWaitingForConfirmationAlert = ({ recipientsToBeConfirmed }) => {
28
28
 
29
29
  const WhoHasAccess = ({
30
30
  isOwner = false,
31
+ canManageSharing = isOwner,
31
32
  isSharedDrive = false,
32
33
  isReadOnly = false,
33
34
  showOwner = true,
@@ -70,6 +71,7 @@ const WhoHasAccess = ({
70
71
  recipients={recipients}
71
72
  recipientsToBeConfirmed={recipientsToBeConfirmed}
72
73
  isOwner={isOwner}
74
+ canManageSharing={canManageSharing}
73
75
  isSharedDrive={isSharedDrive}
74
76
  isReadOnly={isReadOnly}
75
77
  document={document}
@@ -86,6 +88,7 @@ const WhoHasAccess = ({
86
88
 
87
89
  WhoHasAccess.propTypes = {
88
90
  isOwner: PropTypes.bool,
91
+ canManageSharing: PropTypes.bool,
89
92
  showOwner: PropTypes.bool,
90
93
  recipients: PropTypes.array.isRequired,
91
94
  recipientsToBeConfirmed: PropTypes.arrayOf(
@@ -1,23 +0,0 @@
1
- const React = require('react')
2
-
3
- const Icon = ({ icon, iconRef, ...props }) =>
4
- React.createElement('svg', { 'data-icon': true, ...props })
5
-
6
- const Sprite = () => null
7
-
8
- const getFileTypeIcon = () =>
9
- function FileTypeIcon() {
10
- return React.createElement('svg', { 'data-filetypeicon': true })
11
- }
12
-
13
- const proxyHandler = {
14
- get: function (target, prop) {
15
- if (prop in target) return target[prop]
16
- if (prop === 'default') return target.Icon
17
- return function IconMock({ iconRef, ...props }) {
18
- return React.createElement('svg', { 'data-mock-icon': prop, ...props })
19
- }
20
- }
21
- }
22
-
23
- module.exports = new Proxy({ Icon, Sprite, getFileTypeIcon }, proxyHandler)