cozy-sharing 37.0.0 → 37.0.2

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,16 @@
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
+ ## [37.0.2](https://github.com/cozy/cozy-libs/compare/cozy-sharing@37.0.1...cozy-sharing@37.0.2) (2026-07-21)
7
+
8
+ **Note:** Version bump only for package cozy-sharing
9
+
10
+ ## [37.0.1](https://github.com/cozy/cozy-libs/compare/cozy-sharing@37.0.0...cozy-sharing@37.0.1) (2026-07-17)
11
+
12
+ ### Bug Fixes
13
+
14
+ - **cozy-sharing:** Restore federated recipient management ([7442f26](https://github.com/cozy/cozy-libs/commit/7442f26f6cdb757d62d656f19d237d7694335c0c))
15
+
6
16
  # [37.0.0](https://github.com/cozy/cozy-libs/compare/cozy-sharing@36.5.0...cozy-sharing@37.0.0) (2026-07-16)
7
17
 
8
18
  ### Features
@@ -54,6 +54,7 @@ var FederatedFolderModalContent = function FederatedFolderModalContent(_ref) {
54
54
  t = _useI18n.t;
55
55
 
56
56
  var _useSharingContext = useSharingContext(),
57
+ canReshare = _useSharingContext.canReshare,
57
58
  share = _useSharingContext.share,
58
59
  getSharingById = _useSharingContext.getSharingById,
59
60
  getSharingLink = _useSharingContext.getSharingLink,
@@ -278,6 +279,7 @@ var FederatedFolderModalContent = function FederatedFolderModalContent(_ref) {
278
279
  var documentPermissions = documentId ? getDocumentPermissions(documentId) : [];
279
280
  var displayedLink = existingDocument !== null && existingDocument !== void 0 && existingDocument.driveId ? getFederatedShareLink(existingDocument) : getSharingLink(existingDocument === null || existingDocument === void 0 ? void 0 : existingDocument._id);
280
281
  var isCurrentUserOwner = documentId ? isOwner(documentId) : false;
282
+ var canManageSharing = isCurrentUserOwner || (documentId ? canReshare(documentId) : false);
281
283
 
282
284
  var handleRevokeSelf = /*#__PURE__*/function () {
283
285
  var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(document) {
@@ -389,6 +391,7 @@ var FederatedFolderModalContent = function FederatedFolderModalContent(_ref) {
389
391
  enableCreateContact: true
390
392
  }))), /*#__PURE__*/React.createElement(WhoHasAccess, {
391
393
  isOwner: isCurrentUserOwner,
394
+ canManageSharing: canManageSharing,
392
395
  isSharedDrive: true,
393
396
  recipients: isSending ? frozenRecipients : existingRecipients,
394
397
  document: isSending ? frozenDoc : existingDocument,
@@ -19,6 +19,7 @@ var mockShareByLink = jest.fn();
19
19
  var mockRevoke = jest.fn();
20
20
  var mockRevokeSelf = jest.fn();
21
21
  var mockIsOwner = jest.fn();
22
+ var mockCanReshare = jest.fn();
22
23
  var mockGetSharingLink = jest.fn();
23
24
  var mockGetFederatedShareLink = jest.fn();
24
25
  var mockGetRecipients = jest.fn().mockReturnValue([]);
@@ -38,6 +39,7 @@ jest.mock('../../hooks/useSharingContext', function () {
38
39
  revoke: mockRevoke,
39
40
  revokeSelf: mockRevokeSelf,
40
41
  isOwner: mockIsOwner,
42
+ canReshare: mockCanReshare,
41
43
  getSharingLink: mockGetSharingLink,
42
44
  getFederatedShareLink: mockGetFederatedShareLink,
43
45
  getDocumentPermissions: mockGetDocumentPermissions,
@@ -133,6 +135,7 @@ describe('FederatedFolderModal', function () {
133
135
  mockRevoke.mockResolvedValue();
134
136
  mockRevokeSelf.mockResolvedValue();
135
137
  mockIsOwner.mockReturnValue(false);
138
+ mockCanReshare.mockReturnValue(false);
136
139
  mockGetDocumentPermissions.mockReturnValue([]);
137
140
  mockFetchSharedDriveSharingLinks.mockResolvedValue([]);
138
141
  mockGetSharingLink.mockReturnValue('https://example.com/share/abc123');
@@ -726,36 +729,73 @@ describe('FederatedFolderModal', function () {
726
729
  }
727
730
  }, _callee19);
728
731
  })));
729
- });
730
- describe('onClose callback', function () {
731
- it('should call onClose when close button is clicked', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee20() {
732
- var _setup17, findByRole, closeButton;
732
+ it('should allow a write recipient to revoke another recipient', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee20() {
733
+ var otherRecipient, _setup17, findByLabelText;
733
734
 
734
735
  return _regeneratorRuntime.wrap(function _callee20$(_context20) {
735
736
  while (1) switch (_context20.prev = _context20.next) {
736
737
  case 0:
737
- _setup17 = setup(), findByRole = _setup17.findByRole;
738
- _context20.next = 3;
738
+ otherRecipient = {
739
+ instance: 'https://other.mycozy.cloud',
740
+ memberIndex: 2,
741
+ sharingId: 'sharing-id',
742
+ type: 'two-way'
743
+ };
744
+ mockCanReshare.mockReturnValue(true);
745
+ mockGetRecipients.mockReturnValue([otherRecipient]);
746
+ _setup17 = setup(), findByLabelText = _setup17.findByLabelText;
747
+ _context20.t0 = fireEvent;
748
+ _context20.next = 7;
749
+ return findByLabelText('Remove from sharing');
750
+
751
+ case 7:
752
+ _context20.t1 = _context20.sent;
753
+
754
+ _context20.t0.click.call(_context20.t0, _context20.t1);
755
+
756
+ _context20.next = 11;
757
+ return waitFor(function () {
758
+ expect(mockCanReshare).toHaveBeenCalledWith(mockDocument._id);
759
+ expect(mockRevoke).toHaveBeenCalledWith(mockDocument, otherRecipient.sharingId, otherRecipient.memberIndex);
760
+ expect(mockRevokeSelf).not.toHaveBeenCalled();
761
+ });
762
+
763
+ case 11:
764
+ case "end":
765
+ return _context20.stop();
766
+ }
767
+ }, _callee20);
768
+ })));
769
+ });
770
+ describe('onClose callback', function () {
771
+ it('should call onClose when close button is clicked', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee21() {
772
+ var _setup18, findByRole, closeButton;
773
+
774
+ return _regeneratorRuntime.wrap(function _callee21$(_context21) {
775
+ while (1) switch (_context21.prev = _context21.next) {
776
+ case 0:
777
+ _setup18 = setup(), findByRole = _setup18.findByRole;
778
+ _context21.next = 3;
739
779
  return findByRole('button', {
740
780
  name: /close/i
741
781
  });
742
782
 
743
783
  case 3:
744
- closeButton = _context20.sent;
784
+ closeButton = _context21.sent;
745
785
  fireEvent.click(closeButton);
746
786
  expect(mockOnClose).toHaveBeenCalled();
747
787
 
748
788
  case 6:
749
789
  case "end":
750
- return _context20.stop();
790
+ return _context21.stop();
751
791
  }
752
- }, _callee20);
792
+ }, _callee21);
753
793
  })));
754
- it('should ask confirmation when close button is clicked with pending recipients', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee21() {
755
- var _setup18, getByRole, findByRole, getByText, closeButton;
794
+ it('should ask confirmation when close button is clicked with pending recipients', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee22() {
795
+ var _setup19, getByRole, findByRole, getByText, closeButton;
756
796
 
757
- return _regeneratorRuntime.wrap(function _callee21$(_context21) {
758
- while (1) switch (_context21.prev = _context21.next) {
797
+ return _regeneratorRuntime.wrap(function _callee22$(_context22) {
798
+ while (1) switch (_context22.prev = _context22.next) {
759
799
  case 0:
760
800
  usePendingRecipients.mockReturnValue({
761
801
  pendingRecipients: [{
@@ -766,14 +806,14 @@ describe('FederatedFolderModal', function () {
766
806
  selectedOption: 'readWrite',
767
807
  setSelectedOption: jest.fn()
768
808
  });
769
- _setup18 = setup(), getByRole = _setup18.getByRole, findByRole = _setup18.findByRole, getByText = _setup18.getByText;
770
- _context21.next = 4;
809
+ _setup19 = setup(), getByRole = _setup19.getByRole, findByRole = _setup19.findByRole, getByText = _setup19.getByText;
810
+ _context22.next = 4;
771
811
  return findByRole('button', {
772
812
  name: /close/i
773
813
  });
774
814
 
775
815
  case 4:
776
- closeButton = _context21.sent;
816
+ closeButton = _context22.sent;
777
817
  fireEvent.click(closeButton);
778
818
  expect(getByText("Discard the changes you haven't saved?")).toBeTruthy();
779
819
  expect(mockOnClose).not.toHaveBeenCalled();
@@ -784,15 +824,15 @@ describe('FederatedFolderModal', function () {
784
824
 
785
825
  case 10:
786
826
  case "end":
787
- return _context21.stop();
827
+ return _context22.stop();
788
828
  }
789
- }, _callee21);
829
+ }, _callee22);
790
830
  })));
791
- it('should close after discard confirmation', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee22() {
792
- var _setup19, getByRole, findByRole, getByText, closeButton;
831
+ it('should close after discard confirmation', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee23() {
832
+ var _setup20, getByRole, findByRole, getByText, closeButton;
793
833
 
794
- return _regeneratorRuntime.wrap(function _callee22$(_context22) {
795
- while (1) switch (_context22.prev = _context22.next) {
834
+ return _regeneratorRuntime.wrap(function _callee23$(_context23) {
835
+ while (1) switch (_context23.prev = _context23.next) {
796
836
  case 0:
797
837
  usePendingRecipients.mockReturnValue({
798
838
  pendingRecipients: [{
@@ -803,14 +843,14 @@ describe('FederatedFolderModal', function () {
803
843
  selectedOption: 'readWrite',
804
844
  setSelectedOption: jest.fn()
805
845
  });
806
- _setup19 = setup(), getByRole = _setup19.getByRole, findByRole = _setup19.findByRole, getByText = _setup19.getByText;
807
- _context22.next = 4;
846
+ _setup20 = setup(), getByRole = _setup20.getByRole, findByRole = _setup20.findByRole, getByText = _setup20.getByText;
847
+ _context23.next = 4;
808
848
  return findByRole('button', {
809
849
  name: /close/i
810
850
  });
811
851
 
812
852
  case 4:
813
- closeButton = _context22.sent;
853
+ closeButton = _context23.sent;
814
854
  fireEvent.click(closeButton);
815
855
  expect(getByText("Discard the changes you haven't saved?")).toBeTruthy();
816
856
  fireEvent.click(getByRole('button', {
@@ -820,9 +860,9 @@ describe('FederatedFolderModal', function () {
820
860
 
821
861
  case 9:
822
862
  case "end":
823
- return _context22.stop();
863
+ return _context23.stop();
824
864
  }
825
- }, _callee22);
865
+ }, _callee23);
826
866
  })));
827
867
  });
828
868
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-sharing",
3
- "version": "37.0.0",
3
+ "version": "37.0.2",
4
4
  "description": "Provides sharing login for React applications.",
5
5
  "main": "dist/index.js",
6
6
  "author": "Cozy",
@@ -59,7 +59,7 @@
59
59
  "cozy-intent": "^2.31.1",
60
60
  "cozy-minilog": "^3.10.1",
61
61
  "cozy-ui": "^140.5.0",
62
- "cozy-ui-plus": "^12.0.2",
62
+ "cozy-ui-plus": "^12.1.0",
63
63
  "jest": "30.3.0",
64
64
  "jest-environment-jsdom": "30.3.0",
65
65
  "react": "16.12.0",
@@ -85,5 +85,5 @@
85
85
  "sideEffects": [
86
86
  "*.css"
87
87
  ],
88
- "gitHead": "58606cf6a38bd7dbfcf6708135c2a9df4a31f284"
88
+ "gitHead": "22faa5b53511d7720c575564ec52f446743209e6"
89
89
  }
@@ -35,6 +35,7 @@ const FederatedFolderModalContent = ({
35
35
  const client = useClient()
36
36
  const { t } = useI18n()
37
37
  const {
38
+ canReshare,
38
39
  share,
39
40
  getSharingById,
40
41
  getSharingLink,
@@ -224,6 +225,8 @@ const FederatedFolderModalContent = ({
224
225
  ? getFederatedShareLink(existingDocument)
225
226
  : getSharingLink(existingDocument?._id)
226
227
  const isCurrentUserOwner = documentId ? isOwner(documentId) : false
228
+ const canManageSharing =
229
+ isCurrentUserOwner || (documentId ? canReshare(documentId) : false)
227
230
 
228
231
  const handleRevokeSelf = async document => {
229
232
  await revokeSelf(document)
@@ -323,6 +326,7 @@ const FederatedFolderModalContent = ({
323
326
  </div>
324
327
  <WhoHasAccess
325
328
  isOwner={isCurrentUserOwner}
329
+ canManageSharing={canManageSharing}
326
330
  isSharedDrive
327
331
  recipients={isSending ? frozenRecipients : existingRecipients}
328
332
  document={isSending ? frozenDoc : existingDocument}
@@ -13,6 +13,7 @@ const mockShareByLink = jest.fn()
13
13
  const mockRevoke = jest.fn()
14
14
  const mockRevokeSelf = jest.fn()
15
15
  const mockIsOwner = jest.fn()
16
+ const mockCanReshare = jest.fn()
16
17
  const mockGetSharingLink = jest.fn()
17
18
  const mockGetFederatedShareLink = jest.fn()
18
19
  const mockGetRecipients = jest.fn().mockReturnValue([])
@@ -31,6 +32,7 @@ jest.mock('../../hooks/useSharingContext', () => ({
31
32
  revoke: mockRevoke,
32
33
  revokeSelf: mockRevokeSelf,
33
34
  isOwner: mockIsOwner,
35
+ canReshare: mockCanReshare,
34
36
  getSharingLink: mockGetSharingLink,
35
37
  getFederatedShareLink: mockGetFederatedShareLink,
36
38
  getDocumentPermissions: mockGetDocumentPermissions,
@@ -109,6 +111,7 @@ describe('FederatedFolderModal', () => {
109
111
  mockRevoke.mockResolvedValue()
110
112
  mockRevokeSelf.mockResolvedValue()
111
113
  mockIsOwner.mockReturnValue(false)
114
+ mockCanReshare.mockReturnValue(false)
112
115
  mockGetDocumentPermissions.mockReturnValue([])
113
116
  mockFetchSharedDriveSharingLinks.mockResolvedValue([])
114
117
  mockGetSharingLink.mockReturnValue('https://example.com/share/abc123')
@@ -472,6 +475,31 @@ describe('FederatedFolderModal', () => {
472
475
  expect(onRevokeSuccess).toHaveBeenCalledWith(mockDocument)
473
476
  })
474
477
  })
478
+
479
+ it('should allow a write recipient to revoke another recipient', async () => {
480
+ const otherRecipient = {
481
+ instance: 'https://other.mycozy.cloud',
482
+ memberIndex: 2,
483
+ sharingId: 'sharing-id',
484
+ type: 'two-way'
485
+ }
486
+ mockCanReshare.mockReturnValue(true)
487
+ mockGetRecipients.mockReturnValue([otherRecipient])
488
+
489
+ const { findByLabelText } = setup()
490
+
491
+ fireEvent.click(await findByLabelText('Remove from sharing'))
492
+
493
+ await waitFor(() => {
494
+ expect(mockCanReshare).toHaveBeenCalledWith(mockDocument._id)
495
+ expect(mockRevoke).toHaveBeenCalledWith(
496
+ mockDocument,
497
+ otherRecipient.sharingId,
498
+ otherRecipient.memberIndex
499
+ )
500
+ expect(mockRevokeSelf).not.toHaveBeenCalled()
501
+ })
502
+ })
475
503
  })
476
504
 
477
505
  describe('onClose callback', () => {