cozy-sharing 38.1.4 → 38.1.6

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
+ ## [38.1.6](https://github.com/cozy/cozy-libs/compare/cozy-sharing@38.1.5...cozy-sharing@38.1.6) (2026-10-01)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **cozy-sharing:** Keep an unshared folder shareable by its owner ([d0bdac7](https://github.com/cozy/cozy-libs/commit/d0bdac725d3b854be789def6c67ae31ca8db59bd)), closes [linagora/twake-drive#4264](https://github.com/linagora/twake-drive/issues/4264)
11
+
12
+ ## [38.1.5](https://github.com/cozy/cozy-libs/compare/cozy-sharing@38.1.4...cozy-sharing@38.1.5) (2026-09-30)
13
+
14
+ **Note:** Version bump only for package cozy-sharing
15
+
6
16
  ## [38.1.4](https://github.com/cozy/cozy-libs/compare/cozy-sharing@38.1.3...cozy-sharing@38.1.4) (2026-09-30)
7
17
 
8
18
  **Note:** Version bump only for package cozy-sharing
@@ -1,10 +1,7 @@
1
1
  import { getInitials as clientGetInitials, getDisplayName as clientGetDisplayName } from 'cozy-client/dist/models/contact';
2
- import { Contact as DoctypeContact } from 'cozy-doctypes';
3
-
4
- var isContact = function isContact(candidate) {
2
+ export var isContact = function isContact(candidate) {
5
3
  return candidate._type === 'io.cozy.contacts';
6
4
  };
7
-
8
5
  export var getInitials = function getInitials(contactOrRecipient) {
9
6
  var defaultValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
10
7
 
@@ -30,5 +27,4 @@ export var getDisplayName = function getDisplayName(contact) {
30
27
  // @todo Extract to RecipientModel ?
31
28
  return contact.public_name || contact.displayName || contact.name || contact.email || defaultValue;
32
29
  }
33
- };
34
- export default DoctypeContact;
30
+ };
@@ -5,7 +5,7 @@ import ListItem from 'cozy-ui/transpiled/react/ListItem';
5
5
  import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon';
6
6
  import ListItemText from 'cozy-ui/transpiled/react/ListItemText';
7
7
  import { useI18n } from 'twake-i18n';
8
- import { Contact, Group, getDisplayName } from '../models';
8
+ import { DOCTYPE_GROUPS, contactPropType, groupPropType, getDisplayName } from '../models';
9
9
  import { GroupAvatar } from './Avatar/GroupAvatar';
10
10
  import { MemberAvatar } from './Avatar/MemberAvatar';
11
11
  export var ContactSuggestion = function ContactSuggestion(_ref) {
@@ -14,7 +14,7 @@ export var ContactSuggestion = function ContactSuggestion(_ref) {
14
14
  var _useI18n = useI18n(),
15
15
  t = _useI18n.t;
16
16
 
17
- if (contactOrGroup._type === Group.doctype) {
17
+ if (contactOrGroup._type === DOCTYPE_GROUPS) {
18
18
  return /*#__PURE__*/React.createElement(ListItem, {
19
19
  button: true
20
20
  }, /*#__PURE__*/React.createElement(ListItemIcon, null, /*#__PURE__*/React.createElement(GroupAvatar, {
@@ -45,7 +45,7 @@ var newUnknownContactProptypes = PropTypes.shape({
45
45
  email: PropTypes.string.isRequired
46
46
  });
47
47
  ContactSuggestion.propTypes = {
48
- contactOrGroup: PropTypes.oneOfType([Contact.propType, Group.propType, newUnknownContactProptypes]).isRequired,
49
- contacts: PropTypes.arrayOf(Contact.propType)
48
+ contactOrGroup: PropTypes.oneOfType([contactPropType, groupPropType, newUnknownContactProptypes]).isRequired,
49
+ contacts: PropTypes.arrayOf(contactPropType)
50
50
  };
51
51
  export default ContactSuggestion;
@@ -123,8 +123,9 @@ var FederatedFolderModalContent = function FederatedFolderModalContent(_ref) {
123
123
  var displayedLink = existingDocument !== null && existingDocument !== void 0 && existingDocument.driveId ? getFederatedShareLink(existingDocument) : getSharingLink(existingDocument === null || existingDocument === void 0 ? void 0 : existingDocument._id);
124
124
  var isSharedDriveRoot = Boolean((existingDocument === null || existingDocument === void 0 ? void 0 : existingDocument.driveId) && sharedDriveRootIds.some(function (id) {
125
125
  return id === (existingDocument === null || existingDocument === void 0 ? void 0 : existingDocument._id) || id === (existingDocument === null || existingDocument === void 0 ? void 0 : existingDocument.id);
126
- }));
127
- var isInsideSharedDrive = Boolean((existingDocument === null || existingDocument === void 0 ? void 0 : existingDocument.driveId) && !isSharedDriveRoot);
126
+ })); // Ignore a stale driveId left after an owner revokes the last recipient, so their own folder isn't wrongly restricted to link-only (linagora/twake-drive#4264).
127
+
128
+ var isInsideSharedDrive = Boolean((existingDocument === null || existingDocument === void 0 ? void 0 : existingDocument.driveId) && !isSharedDriveRoot && (sharedDriveSharing || !isOwner(documentId)));
128
129
  var isCurrentUserOwner = existingDocument !== null && existingDocument !== void 0 && existingDocument.driveId ? Boolean(sharedDriveSharing === null || sharedDriveSharing === void 0 || (_sharedDriveSharing$a2 = sharedDriveSharing.attributes) === null || _sharedDriveSharing$a2 === void 0 ? void 0 : _sharedDriveSharing$a2.owner) : documentId ? isOwner(documentId) : false;
129
130
  var isMemberReadOnly = isInsideSharedDrive && !isCurrentUserOwner && !hasWriteAccess(documentId, existingDocument === null || existingDocument === void 0 ? void 0 : existingDocument.driveId);
130
131
  var canManageSharing = isCurrentUserOwner || (documentId ? canReshare(documentId) : false);
@@ -385,11 +385,39 @@ describe('FederatedFolderModal', function () {
385
385
  }
386
386
  }, _callee8);
387
387
  })));
388
- });
389
- describe('shared drive link fetch on mount', function () {
390
- it('should fetch shared drive sharing links when document has a driveId and no permissions yet', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee9() {
388
+ it('should allow share by email when the owner revoked the sharing of a folder that still carries a stale driveId', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee9() {
389
+ var _setup9, findByText, queryByText;
390
+
391
391
  return _regeneratorRuntime.wrap(function _callee9$(_context9) {
392
392
  while (1) switch (_context9.prev = _context9.next) {
393
+ case 0:
394
+ // Owner revoked the last recipient: stale driveId, sharing gone, but still owner → stays shareable (linagora/twake-drive#4264).
395
+ mockGetSharingById.mockReturnValue(null);
396
+ mockIsOwner.mockReturnValue(true);
397
+ _setup9 = setup({
398
+ document: _objectSpread(_objectSpread({}, mockDocument), {}, {
399
+ driveId: 'federated-folder-id',
400
+ type: 'directory'
401
+ })
402
+ }), findByText = _setup9.findByText, queryByText = _setup9.queryByText;
403
+ _context9.next = 5;
404
+ return findByText('Add users');
405
+
406
+ case 5:
407
+ expect(queryByText('This folder can only be shared by link, because')).toBe(null);
408
+ expect(queryByText('it has a shared parent')).toBe(null);
409
+
410
+ case 7:
411
+ case "end":
412
+ return _context9.stop();
413
+ }
414
+ }, _callee9);
415
+ })));
416
+ });
417
+ describe('shared drive link fetch on mount', function () {
418
+ it('should fetch shared drive sharing links when document has a driveId and no permissions yet', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee10() {
419
+ return _regeneratorRuntime.wrap(function _callee10$(_context10) {
420
+ while (1) switch (_context10.prev = _context10.next) {
393
421
  case 0:
394
422
  mockGetDocumentPermissions.mockReturnValue([]);
395
423
  setup({
@@ -397,7 +425,7 @@ describe('FederatedFolderModal', function () {
397
425
  driveId: 'federated-folder-id'
398
426
  })
399
427
  });
400
- _context9.next = 4;
428
+ _context10.next = 4;
401
429
  return waitFor(function () {
402
430
  expect(mockFetchSharedDriveSharingLinks).toHaveBeenCalledTimes(1);
403
431
  });
@@ -410,14 +438,14 @@ describe('FederatedFolderModal', function () {
410
438
 
411
439
  case 5:
412
440
  case "end":
413
- return _context9.stop();
441
+ return _context10.stop();
414
442
  }
415
- }, _callee9);
443
+ }, _callee10);
416
444
  })));
417
- it('should use document.id when _id is missing', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee10() {
445
+ it('should use document.id when _id is missing', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee11() {
418
446
  var document;
419
- return _regeneratorRuntime.wrap(function _callee10$(_context10) {
420
- while (1) switch (_context10.prev = _context10.next) {
447
+ return _regeneratorRuntime.wrap(function _callee11$(_context11) {
448
+ while (1) switch (_context11.prev = _context11.next) {
421
449
  case 0:
422
450
  document = {
423
451
  id: 'folder-id-only',
@@ -428,7 +456,7 @@ describe('FederatedFolderModal', function () {
428
456
  setup({
429
457
  document: document
430
458
  });
431
- _context10.next = 4;
459
+ _context11.next = 4;
432
460
  return waitFor(function () {
433
461
  expect(mockFetchSharedDriveSharingLinks).toHaveBeenCalledWith(document);
434
462
  });
@@ -438,20 +466,20 @@ describe('FederatedFolderModal', function () {
438
466
 
439
467
  case 5:
440
468
  case "end":
441
- return _context10.stop();
469
+ return _context11.stop();
442
470
  }
443
- }, _callee10);
471
+ }, _callee11);
444
472
  })));
445
- it('should not fetch shared drive sharing links when document has no driveId', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee11() {
446
- var _setup9, findByText;
473
+ it('should not fetch shared drive sharing links when document has no driveId', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee12() {
474
+ var _setup10, findByText;
447
475
 
448
- return _regeneratorRuntime.wrap(function _callee11$(_context11) {
449
- while (1) switch (_context11.prev = _context11.next) {
476
+ return _regeneratorRuntime.wrap(function _callee12$(_context12) {
477
+ while (1) switch (_context12.prev = _context12.next) {
450
478
  case 0:
451
- _setup9 = setup({
479
+ _setup10 = setup({
452
480
  document: mockDocument
453
- }), findByText = _setup9.findByText;
454
- _context11.next = 3;
481
+ }), findByText = _setup10.findByText;
482
+ _context12.next = 3;
455
483
  return findByText('Copy link');
456
484
 
457
485
  case 3:
@@ -459,23 +487,23 @@ describe('FederatedFolderModal', function () {
459
487
 
460
488
  case 4:
461
489
  case "end":
462
- return _context11.stop();
490
+ return _context12.stop();
463
491
  }
464
- }, _callee11);
492
+ }, _callee12);
465
493
  })));
466
- it('should not refetch after one attempt when permissions stay empty', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee12() {
467
- var document, _setup10, rerender;
494
+ it('should not refetch after one attempt when permissions stay empty', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee13() {
495
+ var document, _setup11, rerender;
468
496
 
469
- return _regeneratorRuntime.wrap(function _callee12$(_context12) {
470
- while (1) switch (_context12.prev = _context12.next) {
497
+ return _regeneratorRuntime.wrap(function _callee13$(_context13) {
498
+ while (1) switch (_context13.prev = _context13.next) {
471
499
  case 0:
472
500
  document = _objectSpread(_objectSpread({}, mockDocument), {}, {
473
501
  driveId: 'federated-folder-id'
474
502
  });
475
- _setup10 = setup({
503
+ _setup11 = setup({
476
504
  document: document
477
- }), rerender = _setup10.rerender;
478
- _context12.next = 4;
505
+ }), rerender = _setup11.rerender;
506
+ _context13.next = 4;
479
507
  return waitFor(function () {
480
508
  expect(mockFetchSharedDriveSharingLinks).toHaveBeenCalledTimes(1);
481
509
  });
@@ -492,15 +520,15 @@ describe('FederatedFolderModal', function () {
492
520
 
493
521
  case 6:
494
522
  case "end":
495
- return _context12.stop();
523
+ return _context13.stop();
496
524
  }
497
- }, _callee12);
525
+ }, _callee13);
498
526
  })));
499
- it('should not fetch shared drive sharing links when permissions are already loaded', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee13() {
500
- var _setup11, findByText;
527
+ it('should not fetch shared drive sharing links when permissions are already loaded', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee14() {
528
+ var _setup12, findByText;
501
529
 
502
- return _regeneratorRuntime.wrap(function _callee13$(_context13) {
503
- while (1) switch (_context13.prev = _context13.next) {
530
+ return _regeneratorRuntime.wrap(function _callee14$(_context14) {
531
+ while (1) switch (_context14.prev = _context14.next) {
504
532
  case 0:
505
533
  mockGetDocumentPermissions.mockReturnValue([{
506
534
  id: 'perm-1',
@@ -511,12 +539,12 @@ describe('FederatedFolderModal', function () {
511
539
  }
512
540
  }
513
541
  }]);
514
- _setup11 = setup({
542
+ _setup12 = setup({
515
543
  document: _objectSpread(_objectSpread({}, mockDocument), {}, {
516
544
  driveId: 'federated-folder-id'
517
545
  })
518
- }), findByText = _setup11.findByText;
519
- _context13.next = 4;
546
+ }), findByText = _setup12.findByText;
547
+ _context14.next = 4;
520
548
  return findByText('Copy link');
521
549
 
522
550
  case 4:
@@ -524,9 +552,9 @@ describe('FederatedFolderModal', function () {
524
552
 
525
553
  case 5:
526
554
  case "end":
527
- return _context13.stop();
555
+ return _context14.stop();
528
556
  }
529
- }, _callee13);
557
+ }, _callee14);
530
558
  })));
531
559
  });
532
560
  describe('onSend callback', function () {
@@ -534,21 +562,21 @@ describe('FederatedFolderModal', function () {
534
562
  name: 'Alice',
535
563
  email: 'alice@example.com'
536
564
  };
537
- it('should call onClose and skip share when there are no pending recipients', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee14() {
538
- var _setup12, findByText, sendButton;
565
+ it('should call onClose and skip share when there are no pending recipients', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee15() {
566
+ var _setup13, findByText, sendButton;
539
567
 
540
- return _regeneratorRuntime.wrap(function _callee14$(_context14) {
541
- while (1) switch (_context14.prev = _context14.next) {
568
+ return _regeneratorRuntime.wrap(function _callee15$(_context15) {
569
+ while (1) switch (_context15.prev = _context15.next) {
542
570
  case 0:
543
571
  // pendingRecipients defaults to [] via beforeEach
544
- _setup12 = setup(), findByText = _setup12.findByText;
545
- _context14.next = 3;
572
+ _setup13 = setup(), findByText = _setup13.findByText;
573
+ _context15.next = 3;
546
574
  return findByText('Done');
547
575
 
548
576
  case 3:
549
- sendButton = _context14.sent;
577
+ sendButton = _context15.sent;
550
578
  fireEvent.click(sendButton);
551
- _context14.next = 7;
579
+ _context15.next = 7;
552
580
  return waitFor(function () {
553
581
  expect(mockOnClose).toHaveBeenCalled();
554
582
  expect(mockShare).not.toHaveBeenCalled();
@@ -556,15 +584,15 @@ describe('FederatedFolderModal', function () {
556
584
 
557
585
  case 7:
558
586
  case "end":
559
- return _context14.stop();
587
+ return _context15.stop();
560
588
  }
561
- }, _callee14);
589
+ }, _callee15);
562
590
  })));
563
- it('should call share with correct parameters', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee15() {
564
- var _setup13, findByText, sendButton;
591
+ it('should call share with correct parameters', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee16() {
592
+ var _setup14, findByText, sendButton;
565
593
 
566
- return _regeneratorRuntime.wrap(function _callee15$(_context15) {
567
- while (1) switch (_context15.prev = _context15.next) {
594
+ return _regeneratorRuntime.wrap(function _callee16$(_context16) {
595
+ while (1) switch (_context16.prev = _context16.next) {
568
596
  case 0:
569
597
  usePendingRecipients.mockReturnValue({
570
598
  pendingRecipients: [pendingRecipient],
@@ -574,14 +602,14 @@ describe('FederatedFolderModal', function () {
574
602
  });
575
603
  getOrCreateFromArray.mockResolvedValue([pendingRecipient]);
576
604
  mockShare.mockResolvedValueOnce({});
577
- _setup13 = setup(), findByText = _setup13.findByText;
578
- _context15.next = 6;
605
+ _setup14 = setup(), findByText = _setup14.findByText;
606
+ _context16.next = 6;
579
607
  return findByText('Done');
580
608
 
581
609
  case 6:
582
- sendButton = _context15.sent;
610
+ sendButton = _context16.sent;
583
611
  fireEvent.click(sendButton);
584
- _context15.next = 10;
612
+ _context16.next = 10;
585
613
  return waitFor(function () {
586
614
  expect(mockShare).toHaveBeenCalledWith({
587
615
  description: 'My Test Folder',
@@ -595,15 +623,15 @@ describe('FederatedFolderModal', function () {
595
623
 
596
624
  case 10:
597
625
  case "end":
598
- return _context15.stop();
626
+ return _context16.stop();
599
627
  }
600
- }, _callee15);
628
+ }, _callee16);
601
629
  })));
602
- it('should show success alert and close modal on successful share', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee16() {
603
- var _setup14, findByText, sendButton;
630
+ it('should show success alert and close modal on successful share', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee17() {
631
+ var _setup15, findByText, sendButton;
604
632
 
605
- return _regeneratorRuntime.wrap(function _callee16$(_context16) {
606
- while (1) switch (_context16.prev = _context16.next) {
633
+ return _regeneratorRuntime.wrap(function _callee17$(_context17) {
634
+ while (1) switch (_context17.prev = _context17.next) {
607
635
  case 0:
608
636
  usePendingRecipients.mockReturnValue({
609
637
  pendingRecipients: [pendingRecipient],
@@ -613,14 +641,14 @@ describe('FederatedFolderModal', function () {
613
641
  });
614
642
  getOrCreateFromArray.mockResolvedValue([pendingRecipient]);
615
643
  mockShare.mockResolvedValueOnce({});
616
- _setup14 = setup(), findByText = _setup14.findByText;
617
- _context16.next = 6;
644
+ _setup15 = setup(), findByText = _setup15.findByText;
645
+ _context17.next = 6;
618
646
  return findByText('Done');
619
647
 
620
648
  case 6:
621
- sendButton = _context16.sent;
649
+ sendButton = _context17.sent;
622
650
  fireEvent.click(sendButton);
623
- _context16.next = 10;
651
+ _context17.next = 10;
624
652
  return waitFor(function () {
625
653
  expect(mockShowAlert).toHaveBeenCalledWith({
626
654
  message: 'Folder has been shared',
@@ -632,15 +660,15 @@ describe('FederatedFolderModal', function () {
632
660
 
633
661
  case 10:
634
662
  case "end":
635
- return _context16.stop();
663
+ return _context17.stop();
636
664
  }
637
- }, _callee16);
665
+ }, _callee17);
638
666
  })));
639
- it('should show error alert when share fails', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee17() {
640
- var _setup15, findByText, sendButton;
667
+ it('should show error alert when share fails', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee18() {
668
+ var _setup16, findByText, sendButton;
641
669
 
642
- return _regeneratorRuntime.wrap(function _callee17$(_context17) {
643
- while (1) switch (_context17.prev = _context17.next) {
670
+ return _regeneratorRuntime.wrap(function _callee18$(_context18) {
671
+ while (1) switch (_context18.prev = _context18.next) {
644
672
  case 0:
645
673
  usePendingRecipients.mockReturnValue({
646
674
  pendingRecipients: [pendingRecipient],
@@ -650,14 +678,14 @@ describe('FederatedFolderModal', function () {
650
678
  });
651
679
  getOrCreateFromArray.mockResolvedValue([pendingRecipient]);
652
680
  mockShare.mockRejectedValueOnce(new Error('Share failed'));
653
- _setup15 = setup(), findByText = _setup15.findByText;
654
- _context17.next = 6;
681
+ _setup16 = setup(), findByText = _setup16.findByText;
682
+ _context18.next = 6;
655
683
  return findByText('Done');
656
684
 
657
685
  case 6:
658
- sendButton = _context17.sent;
686
+ sendButton = _context18.sent;
659
687
  fireEvent.click(sendButton);
660
- _context17.next = 10;
688
+ _context18.next = 10;
661
689
  return waitFor(function () {
662
690
  expect(mockShowAlert).toHaveBeenCalledWith({
663
691
  message: 'Error while sharing folder',
@@ -669,15 +697,15 @@ describe('FederatedFolderModal', function () {
669
697
 
670
698
  case 10:
671
699
  case "end":
672
- return _context17.stop();
700
+ return _context18.stop();
673
701
  }
674
- }, _callee17);
702
+ }, _callee18);
675
703
  })));
676
- it('should show progress icon on Done button while share is in progress', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee19() {
677
- var resolveShare, _setup16, findByText, getByRole, queryByRole, sendButton;
704
+ it('should show progress icon on Done button while share is in progress', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee20() {
705
+ var resolveShare, _setup17, findByText, getByRole, queryByRole, sendButton;
678
706
 
679
- return _regeneratorRuntime.wrap(function _callee19$(_context19) {
680
- while (1) switch (_context19.prev = _context19.next) {
707
+ return _regeneratorRuntime.wrap(function _callee20$(_context20) {
708
+ while (1) switch (_context20.prev = _context20.next) {
681
709
  case 0:
682
710
  usePendingRecipients.mockReturnValue({
683
711
  pendingRecipients: [pendingRecipient],
@@ -689,14 +717,14 @@ describe('FederatedFolderModal', function () {
689
717
  mockShare.mockReturnValueOnce(new Promise(function (resolve) {
690
718
  resolveShare = resolve;
691
719
  }));
692
- _setup16 = setup(), findByText = _setup16.findByText, getByRole = _setup16.getByRole, queryByRole = _setup16.queryByRole;
693
- _context19.next = 6;
720
+ _setup17 = setup(), findByText = _setup17.findByText, getByRole = _setup17.getByRole, queryByRole = _setup17.queryByRole;
721
+ _context20.next = 6;
694
722
  return findByText('Done');
695
723
 
696
724
  case 6:
697
- sendButton = _context19.sent;
725
+ sendButton = _context20.sent;
698
726
  fireEvent.click(sendButton);
699
- _context19.next = 10;
727
+ _context20.next = 10;
700
728
  return waitFor(function () {
701
729
  expect(getByRole('button', {
702
730
  name: 'Done'
@@ -705,25 +733,25 @@ describe('FederatedFolderModal', function () {
705
733
  });
706
734
 
707
735
  case 10:
708
- _context19.next = 12;
709
- return act( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee18() {
710
- return _regeneratorRuntime.wrap(function _callee18$(_context18) {
711
- while (1) switch (_context18.prev = _context18.next) {
736
+ _context20.next = 12;
737
+ return act( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee19() {
738
+ return _regeneratorRuntime.wrap(function _callee19$(_context19) {
739
+ while (1) switch (_context19.prev = _context19.next) {
712
740
  case 0:
713
741
  resolveShare({});
714
742
 
715
743
  case 1:
716
744
  case "end":
717
- return _context18.stop();
745
+ return _context19.stop();
718
746
  }
719
- }, _callee18);
747
+ }, _callee19);
720
748
  })));
721
749
 
722
750
  case 12:
723
751
  case "end":
724
- return _context19.stop();
752
+ return _context20.stop();
725
753
  }
726
- }, _callee19);
754
+ }, _callee20);
727
755
  })));
728
756
  });
729
757
  describe('onRevoke callback', function () {
@@ -736,27 +764,27 @@ describe('FederatedFolderModal', function () {
736
764
  name: 'Me',
737
765
  email: 'me@test.com'
738
766
  };
739
- it('should call onRevokeSelf and onRevokeSuccess when the current user is revoked', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee20() {
740
- var onRevokeSuccess, _setup17, findByLabelText;
767
+ it('should call onRevokeSelf and onRevokeSuccess when the current user is revoked', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee21() {
768
+ var onRevokeSuccess, _setup18, findByLabelText;
741
769
 
742
- return _regeneratorRuntime.wrap(function _callee20$(_context20) {
743
- while (1) switch (_context20.prev = _context20.next) {
770
+ return _regeneratorRuntime.wrap(function _callee21$(_context21) {
771
+ while (1) switch (_context21.prev = _context21.next) {
744
772
  case 0:
745
773
  onRevokeSuccess = jest.fn();
746
774
  mockGetEffectiveRecipients.mockReturnValue([currentUserRecipient]);
747
- _setup17 = setup({
775
+ _setup18 = setup({
748
776
  onRevokeSuccess: onRevokeSuccess
749
- }), findByLabelText = _setup17.findByLabelText;
750
- _context20.t0 = fireEvent;
751
- _context20.next = 6;
777
+ }), findByLabelText = _setup18.findByLabelText;
778
+ _context21.t0 = fireEvent;
779
+ _context21.next = 6;
752
780
  return findByLabelText('Remove from sharing');
753
781
 
754
782
  case 6:
755
- _context20.t1 = _context20.sent;
783
+ _context21.t1 = _context21.sent;
756
784
 
757
- _context20.t0.click.call(_context20.t0, _context20.t1);
785
+ _context21.t0.click.call(_context21.t0, _context21.t1);
758
786
 
759
- _context20.next = 10;
787
+ _context21.next = 10;
760
788
  return waitFor(function () {
761
789
  expect(mockRevokeSelf).toHaveBeenCalledWith(mockDocument);
762
790
  expect(mockRevoke).not.toHaveBeenCalled();
@@ -765,15 +793,15 @@ describe('FederatedFolderModal', function () {
765
793
 
766
794
  case 10:
767
795
  case "end":
768
- return _context20.stop();
796
+ return _context21.stop();
769
797
  }
770
- }, _callee20);
798
+ }, _callee21);
771
799
  })));
772
- it('should allow a write recipient to revoke another recipient', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee21() {
773
- var otherRecipient, _setup18, findByLabelText;
800
+ it('should allow a write recipient to revoke another recipient', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee22() {
801
+ var otherRecipient, _setup19, findByLabelText;
774
802
 
775
- return _regeneratorRuntime.wrap(function _callee21$(_context21) {
776
- while (1) switch (_context21.prev = _context21.next) {
803
+ return _regeneratorRuntime.wrap(function _callee22$(_context22) {
804
+ while (1) switch (_context22.prev = _context22.next) {
777
805
  case 0:
778
806
  otherRecipient = {
779
807
  instance: 'https://other.mycozy.cloud',
@@ -786,17 +814,17 @@ describe('FederatedFolderModal', function () {
786
814
  };
787
815
  mockCanReshare.mockReturnValue(true);
788
816
  mockGetEffectiveRecipients.mockReturnValue([otherRecipient]);
789
- _setup18 = setup(), findByLabelText = _setup18.findByLabelText;
790
- _context21.t0 = fireEvent;
791
- _context21.next = 7;
817
+ _setup19 = setup(), findByLabelText = _setup19.findByLabelText;
818
+ _context22.t0 = fireEvent;
819
+ _context22.next = 7;
792
820
  return findByLabelText('Remove from sharing');
793
821
 
794
822
  case 7:
795
- _context21.t1 = _context21.sent;
823
+ _context22.t1 = _context22.sent;
796
824
 
797
- _context21.t0.click.call(_context21.t0, _context21.t1);
825
+ _context22.t0.click.call(_context22.t0, _context22.t1);
798
826
 
799
- _context21.next = 11;
827
+ _context22.next = 11;
800
828
  return waitFor(function () {
801
829
  expect(mockCanReshare).toHaveBeenCalledWith(mockDocument._id);
802
830
  expect(mockRevoke).toHaveBeenCalledWith(mockDocument, otherRecipient.sharingId, otherRecipient.memberIndex);
@@ -805,40 +833,40 @@ describe('FederatedFolderModal', function () {
805
833
 
806
834
  case 11:
807
835
  case "end":
808
- return _context21.stop();
836
+ return _context22.stop();
809
837
  }
810
- }, _callee21);
838
+ }, _callee22);
811
839
  })));
812
840
  });
813
841
  describe('onClose callback', function () {
814
- it('should call onClose when close button is clicked', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee22() {
815
- var _setup19, findByRole, closeButton;
842
+ it('should call onClose when close button is clicked', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee23() {
843
+ var _setup20, findByRole, closeButton;
816
844
 
817
- return _regeneratorRuntime.wrap(function _callee22$(_context22) {
818
- while (1) switch (_context22.prev = _context22.next) {
845
+ return _regeneratorRuntime.wrap(function _callee23$(_context23) {
846
+ while (1) switch (_context23.prev = _context23.next) {
819
847
  case 0:
820
- _setup19 = setup(), findByRole = _setup19.findByRole;
821
- _context22.next = 3;
848
+ _setup20 = setup(), findByRole = _setup20.findByRole;
849
+ _context23.next = 3;
822
850
  return findByRole('button', {
823
851
  name: /close/i
824
852
  });
825
853
 
826
854
  case 3:
827
- closeButton = _context22.sent;
855
+ closeButton = _context23.sent;
828
856
  fireEvent.click(closeButton);
829
857
  expect(mockOnClose).toHaveBeenCalled();
830
858
 
831
859
  case 6:
832
860
  case "end":
833
- return _context22.stop();
861
+ return _context23.stop();
834
862
  }
835
- }, _callee22);
863
+ }, _callee23);
836
864
  })));
837
- it('should ask confirmation when close button is clicked with pending recipients', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee23() {
838
- var _setup20, getByRole, findByRole, getByText, closeButton;
865
+ it('should ask confirmation when close button is clicked with pending recipients', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee24() {
866
+ var _setup21, getByRole, findByRole, getByText, closeButton;
839
867
 
840
- return _regeneratorRuntime.wrap(function _callee23$(_context23) {
841
- while (1) switch (_context23.prev = _context23.next) {
868
+ return _regeneratorRuntime.wrap(function _callee24$(_context24) {
869
+ while (1) switch (_context24.prev = _context24.next) {
842
870
  case 0:
843
871
  usePendingRecipients.mockReturnValue({
844
872
  pendingRecipients: [{
@@ -849,14 +877,14 @@ describe('FederatedFolderModal', function () {
849
877
  selectedOption: 'readWrite',
850
878
  setSelectedOption: jest.fn()
851
879
  });
852
- _setup20 = setup(), getByRole = _setup20.getByRole, findByRole = _setup20.findByRole, getByText = _setup20.getByText;
853
- _context23.next = 4;
880
+ _setup21 = setup(), getByRole = _setup21.getByRole, findByRole = _setup21.findByRole, getByText = _setup21.getByText;
881
+ _context24.next = 4;
854
882
  return findByRole('button', {
855
883
  name: /close/i
856
884
  });
857
885
 
858
886
  case 4:
859
- closeButton = _context23.sent;
887
+ closeButton = _context24.sent;
860
888
  fireEvent.click(closeButton);
861
889
  expect(getByText("Discard the changes you haven't saved?")).toBeTruthy();
862
890
  expect(mockOnClose).not.toHaveBeenCalled();
@@ -867,15 +895,15 @@ describe('FederatedFolderModal', function () {
867
895
 
868
896
  case 10:
869
897
  case "end":
870
- return _context23.stop();
898
+ return _context24.stop();
871
899
  }
872
- }, _callee23);
900
+ }, _callee24);
873
901
  })));
874
- it('should close after discard confirmation', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee24() {
875
- var _setup21, getByRole, findByRole, getByText, closeButton;
902
+ it('should close after discard confirmation', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee25() {
903
+ var _setup22, getByRole, findByRole, getByText, closeButton;
876
904
 
877
- return _regeneratorRuntime.wrap(function _callee24$(_context24) {
878
- while (1) switch (_context24.prev = _context24.next) {
905
+ return _regeneratorRuntime.wrap(function _callee25$(_context25) {
906
+ while (1) switch (_context25.prev = _context25.next) {
879
907
  case 0:
880
908
  usePendingRecipients.mockReturnValue({
881
909
  pendingRecipients: [{
@@ -886,14 +914,14 @@ describe('FederatedFolderModal', function () {
886
914
  selectedOption: 'readWrite',
887
915
  setSelectedOption: jest.fn()
888
916
  });
889
- _setup21 = setup(), getByRole = _setup21.getByRole, findByRole = _setup21.findByRole, getByText = _setup21.getByText;
890
- _context24.next = 4;
917
+ _setup22 = setup(), getByRole = _setup22.getByRole, findByRole = _setup22.findByRole, getByText = _setup22.getByText;
918
+ _context25.next = 4;
891
919
  return findByRole('button', {
892
920
  name: /close/i
893
921
  });
894
922
 
895
923
  case 4:
896
- closeButton = _context24.sent;
924
+ closeButton = _context25.sent;
897
925
  fireEvent.click(closeButton);
898
926
  expect(getByText("Discard the changes you haven't saved?")).toBeTruthy();
899
927
  fireEvent.click(getByRole('button', {
@@ -903,9 +931,9 @@ describe('FederatedFolderModal', function () {
903
931
 
904
932
  case 9:
905
933
  case "end":
906
- return _context24.stop();
934
+ return _context25.stop();
907
935
  }
908
- }, _callee24);
936
+ }, _callee25);
909
937
  })));
910
938
  });
911
939
  });
@@ -10,7 +10,7 @@ import { MemberAvatar } from './Avatar/MemberAvatar';
10
10
  import ContactSuggestion from './ContactSuggestion';
11
11
  import { isContactToBeCreated } from '../helpers/contacts';
12
12
  import { extractEmails, validateEmail } from '../helpers/email';
13
- import { getDisplayName, Contact, Group } from '../models';
13
+ import { getDisplayName, DOCTYPE_CONTACTS, DOCTYPE_GROUPS } from '../models';
14
14
  var styles = {
15
15
  "container": "autosuggest__container___1Yro7",
16
16
  "suggestionsContainer": "autosuggest__suggestionsContainer___3EDuG",
@@ -76,7 +76,7 @@ var ShareAutosuggest = function ShareAutosuggest(_ref) {
76
76
  } else if (enableCreateContact && validateEmail(value)) {
77
77
  setSuggestions([{
78
78
  email: value,
79
- _type: Contact.doctype
79
+ _type: DOCTYPE_CONTACTS
80
80
  }]);
81
81
  } else {
82
82
  setSuggestions([]);
@@ -181,7 +181,7 @@ var ShareAutosuggest = function ShareAutosuggest(_ref) {
181
181
  return /*#__PURE__*/React.createElement("div", {
182
182
  className: styles['recipientsContainer']
183
183
  }, recipients.map(function (recipient, idx) {
184
- var isContactGroup = recipient._type === Group.doctype;
184
+ var isContactGroup = recipient._type === DOCTYPE_GROUPS;
185
185
  var name = getDisplayName(recipient);
186
186
  return /*#__PURE__*/React.createElement(Chip, {
187
187
  id: "recipient_".concat(idx),
@@ -9,7 +9,7 @@ import flag from 'cozy-flags';
9
9
  import minilog from 'cozy-minilog';
10
10
  import { useFetchDocumentPath } from '../../hooks/useFetchDocumentPath';
11
11
  import { useSharingContext } from '../../hooks/useSharingContext';
12
- import { Contact } from '../../models';
12
+ import { DOCTYPE_CONTACTS } from '../../models';
13
13
  import { ShareModal } from '../ShareModal';
14
14
  export var EditableSharingModal = function EditableSharingModal(_ref) {
15
15
  var document = _ref.document,
@@ -34,7 +34,7 @@ export var EditableSharingModal = function EditableSharingModal(_ref) {
34
34
 
35
35
  return /*#__PURE__*/React.createElement(ShareModal, _extends({
36
36
  createContact: function createContact(contact) {
37
- return client.create(Contact.doctype, contact);
37
+ return client.create(DOCTYPE_CONTACTS, contact);
38
38
  },
39
39
  document: document,
40
40
  documentType: documentType,
@@ -5,7 +5,7 @@ import { useState } from 'react';
5
5
  import { useClient } from 'cozy-client';
6
6
  import { getOrCreateFromArray } from '../../helpers/contacts';
7
7
  import { usePendingRecipients } from '../../hooks/usePendingRecipients';
8
- import { Contact } from '../../models';
8
+ import { DOCTYPE_CONTACTS } from '../../models';
9
9
  export var useSharedDrive = function useSharedDrive() {
10
10
  var client = useClient();
11
11
 
@@ -25,7 +25,7 @@ export var useSharedDrive = function useSharedDrive() {
25
25
  };
26
26
 
27
27
  var createContact = function createContact(contact) {
28
- return client.create(Contact.doctype, contact);
28
+ return client.create(DOCTYPE_CONTACTS, contact);
29
29
  };
30
30
 
31
31
  var onCreate = /*#__PURE__*/function () {
@@ -6,7 +6,7 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
6
6
  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; }
7
7
 
8
8
  import flag from 'cozy-flags';
9
- import { Group } from '../models';
9
+ import { DOCTYPE_GROUPS } from '../models';
10
10
  export var DEFAULT_DISPLAY_NAME = 'Share.contacts.defaultDisplayName';
11
11
  export var filterAndReworkRecipients = function filterAndReworkRecipients(recipients, previousRecipients) {
12
12
  return recipients.filter(function (recipient) {
@@ -66,7 +66,7 @@ export var hasReachRecipientsLimit = function hasReachRecipientsLimit(current, n
66
66
  export var spreadGroupAndMergeRecipients = function spreadGroupAndMergeRecipients(recipients, newRecipient) {
67
67
  var contactsToAdd;
68
68
 
69
- if (newRecipient._type === Group.doctype) {
69
+ if (newRecipient._type === DOCTYPE_GROUPS) {
70
70
  contactsToAdd = newRecipient.members;
71
71
  } else {
72
72
  contactsToAdd = [newRecipient];
@@ -5,10 +5,10 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
5
5
  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; }
6
6
 
7
7
  import { models } from 'cozy-client';
8
- import { Contact, Group, getDisplayName } from '../models';
8
+ import { DOCTYPE_CONTACTS, DOCTYPE_GROUPS, isContact, getDisplayName } from '../models';
9
9
  export var countNewRecipients = function countNewRecipients(currentRecipients, newRecipients) {
10
10
  var newRecipientsNotAlreadyIncluded = newRecipients.filter(function (recipient) {
11
- if (Contact.isContact(recipient)) {
11
+ if (isContact(recipient)) {
12
12
  var email = models.contact.getPrimaryEmail(recipient);
13
13
  var cozyUrl = models.contact.getPrimaryCozy(recipient);
14
14
  return !currentRecipients.find(function (r) {
@@ -19,7 +19,7 @@ export var countNewRecipients = function countNewRecipients(currentRecipients, n
19
19
  return true;
20
20
  });
21
21
  return newRecipientsNotAlreadyIncluded.reduce(function (acc, recipient) {
22
- if (recipient._type === Group.doctype) {
22
+ if (recipient._type === DOCTYPE_GROUPS) {
23
23
  return _objectSpread(_objectSpread({}, acc), {}, {
24
24
  group: acc.group + 1
25
25
  });
@@ -36,9 +36,9 @@ export var countNewRecipients = function countNewRecipients(currentRecipients, n
36
36
  export var getSuccessMessage = function getSuccessMessage(recipientsBefore, recipientsAfter, documentType) {
37
37
  if (recipientsAfter.length === 1) {
38
38
  var recipient = recipientsAfter[0];
39
- var email = Contact.isContact(recipient) ? models.contact.getPrimaryEmail(recipient) : recipient.email;
39
+ var email = isContact(recipient) ? models.contact.getPrimaryEmail(recipient) : recipient.email;
40
40
  var cozyUrl = models.contact.getPrimaryCozy(recipient);
41
- var isContactGroup = recipient._type === Group.doctype;
41
+ var isContactGroup = recipient._type === DOCTYPE_GROUPS;
42
42
 
43
43
  if (isContactGroup) {
44
44
  return ["".concat(documentType, ".share.shareByEmail.singleGroupSuccess"), {
@@ -89,7 +89,7 @@ export var getErrorMessage = function getErrorMessage(_ref) {
89
89
  if (detail.startsWith('A group member cannot be added as they are already in')) {
90
90
  var sharingRights = t("Share.errors.sharingRights.".concat(selectedOption === 'readOnly' ? 'readWrite' : 'readOnly'));
91
91
 
92
- if (recipients.length === 1 && recipients[0]._type === Contact.doctype) {
92
+ if (recipients.length === 1 && recipients[0]._type === DOCTYPE_CONTACTS) {
93
93
  return ['Share.errors.contactAlreadyInSharing', {
94
94
  smart_count: recipients.length,
95
95
  contactName: getDisplayName(recipients[0]),
package/dist/models.js CHANGED
@@ -1,3 +1,14 @@
1
- export { CozyFile } from 'cozy-doctypes';
2
- export { Group } from 'cozy-doctypes';
3
- export { default as Contact, getInitials, getDisplayName } from './DoctypeContact';
1
+ import PropTypes from 'prop-types';
2
+ export var DOCTYPE_CONTACTS = 'io.cozy.contacts';
3
+ export var DOCTYPE_GROUPS = 'io.cozy.contacts.groups';
4
+ export var contactPropType = PropTypes.shape({
5
+ _id: PropTypes.string.isRequired,
6
+ _type: PropTypes.string.isRequired
7
+ });
8
+ export var groupPropType = PropTypes.shape({
9
+ _id: PropTypes.string.isRequired,
10
+ _type: PropTypes.string.isRequired,
11
+ name: PropTypes.string.isRequired,
12
+ trashed: PropTypes.bool
13
+ });
14
+ export { isContact, getInitials, getDisplayName } from './DoctypeContact';
@@ -1,5 +1,5 @@
1
1
  import { Q, fetchPolicies } from 'cozy-client';
2
- import { Contact, Group } from '../models';
2
+ import { DOCTYPE_CONTACTS, DOCTYPE_GROUPS } from '../models';
3
3
  var DEFAULT_CACHE_TIMEOUT_QUERIES = 9 * 60 * 1000;
4
4
  var defaultFetchPolicy = fetchPolicies.olderThan(DEFAULT_CACHE_TIMEOUT_QUERIES);
5
5
  export var buildSharingsByIdQuery = function buildSharingsByIdQuery(sharingId) {
@@ -13,7 +13,7 @@ export var buildSharingsByIdQuery = function buildSharingsByIdQuery(sharingId) {
13
13
  };
14
14
  export var buildReachableContactsQuery = function buildReachableContactsQuery() {
15
15
  return {
16
- definition: Q(Contact.doctype).where({
16
+ definition: Q(DOCTYPE_CONTACTS).where({
17
17
  _id: {
18
18
  $gt: null
19
19
  }
@@ -46,7 +46,7 @@ export var buildReachableContactsQuery = function buildReachableContactsQuery()
46
46
  };
47
47
  export var buildContactGroupsQuery = function buildContactGroupsQuery() {
48
48
  return {
49
- definition: Q(Group.doctype),
49
+ definition: Q(DOCTYPE_GROUPS),
50
50
  options: {
51
51
  as: "io.cozy.contacts.groups",
52
52
  fetchPolicy: defaultFetchPolicy
@@ -55,7 +55,7 @@ export var buildContactGroupsQuery = function buildContactGroupsQuery() {
55
55
  };
56
56
  export var buildUnreachableContactsWithGroupsQuery = function buildUnreachableContactsWithGroupsQuery() {
57
57
  return {
58
- definition: Q(Contact.doctype).where({
58
+ definition: Q(DOCTYPE_CONTACTS).where({
59
59
  _id: {
60
60
  $gt: null
61
61
  }
@@ -1,4 +1,4 @@
1
- import { Group } from 'cozy-doctypes';
1
+ import { DOCTYPE_GROUPS } from './models';
2
2
  var removeDiacritics;
3
3
 
4
4
  try {
@@ -50,7 +50,7 @@ var cozyUrlMatch = function cozyUrlMatch(input, contact) {
50
50
  };
51
51
 
52
52
  var groupNameMatch = function groupNameMatch(input, contactOrGroup) {
53
- if (contactOrGroup._type !== Group.doctype) return false;
53
+ if (contactOrGroup._type !== DOCTYPE_GROUPS) return false;
54
54
  var normalizedInput = normalizeLowercase(input);
55
55
  return normalizeLowercase(contactOrGroup.name).includes(normalizedInput);
56
56
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-sharing",
3
- "version": "38.1.4",
3
+ "version": "38.1.6",
4
4
  "description": "Provides sharing login for React applications.",
5
5
  "main": "dist/index.js",
6
6
  "author": "Cozy",
@@ -30,7 +30,6 @@
30
30
  "dependencies": {
31
31
  "@cozy/minilog": "^1.0.0",
32
32
  "classnames": "^2.5.1",
33
- "cozy-doctypes": "^1.99.1",
34
33
  "date-fns": "2.30.0",
35
34
  "lodash": "^4.17.21",
36
35
  "react-autosuggest": "^10.1.0",
@@ -59,7 +58,7 @@
59
58
  "cozy-intent": "^2.31.1",
60
59
  "cozy-minilog": "^3.10.1",
61
60
  "cozy-ui": "^140.5.0",
62
- "cozy-ui-plus": "^13.0.8",
61
+ "cozy-ui-plus": "^13.0.10",
63
62
  "jest": "30.3.0",
64
63
  "jest-environment-jsdom": "30.3.0",
65
64
  "react": "16.12.0",
@@ -85,5 +84,5 @@
85
84
  "sideEffects": [
86
85
  "*.css"
87
86
  ],
88
- "gitHead": "2928778c60c287a513ae3e131177ef20b3d35ef5"
87
+ "gitHead": "59aff51cc02c4315da7889122d476df36d4c90e2"
89
88
  }
@@ -2,9 +2,8 @@ import {
2
2
  getInitials as clientGetInitials,
3
3
  getDisplayName as clientGetDisplayName
4
4
  } from 'cozy-client/dist/models/contact'
5
- import { Contact as DoctypeContact } from 'cozy-doctypes'
6
5
 
7
- const isContact = candidate => {
6
+ export const isContact = candidate => {
8
7
  return candidate._type === 'io.cozy.contacts'
9
8
  }
10
9
  export const getInitials = (contactOrRecipient, defaultValue = '') => {
@@ -43,5 +42,3 @@ export const getDisplayName = (contact, defaultValue = '') => {
43
42
  )
44
43
  }
45
44
  }
46
-
47
- export default DoctypeContact
@@ -7,14 +7,19 @@ import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
7
7
  import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
8
8
  import { useI18n } from 'twake-i18n'
9
9
 
10
- import { Contact, Group, getDisplayName } from '../models'
10
+ import {
11
+ DOCTYPE_GROUPS,
12
+ contactPropType,
13
+ groupPropType,
14
+ getDisplayName
15
+ } from '../models'
11
16
  import { GroupAvatar } from './Avatar/GroupAvatar'
12
17
  import { MemberAvatar } from './Avatar/MemberAvatar'
13
18
 
14
19
  export const ContactSuggestion = ({ contactOrGroup }) => {
15
20
  const { t } = useI18n()
16
21
 
17
- if (contactOrGroup._type === Group.doctype) {
22
+ if (contactOrGroup._type === DOCTYPE_GROUPS) {
18
23
  return (
19
24
  <ListItem button>
20
25
  <ListItemIcon>
@@ -50,11 +55,11 @@ const newUnknownContactProptypes = PropTypes.shape({
50
55
 
51
56
  ContactSuggestion.propTypes = {
52
57
  contactOrGroup: PropTypes.oneOfType([
53
- Contact.propType,
54
- Group.propType,
58
+ contactPropType,
59
+ groupPropType,
55
60
  newUnknownContactProptypes
56
61
  ]).isRequired,
57
- contacts: PropTypes.arrayOf(Contact.propType)
62
+ contacts: PropTypes.arrayOf(contactPropType)
58
63
  }
59
64
 
60
65
  export default ContactSuggestion
@@ -92,8 +92,11 @@ const FederatedFolderModalContent = ({
92
92
  id => id === existingDocument?._id || id === existingDocument?.id
93
93
  )
94
94
  )
95
+ // Ignore a stale driveId left after an owner revokes the last recipient, so their own folder isn't wrongly restricted to link-only (linagora/twake-drive#4264).
95
96
  const isInsideSharedDrive = Boolean(
96
- existingDocument?.driveId && !isSharedDriveRoot
97
+ existingDocument?.driveId &&
98
+ !isSharedDriveRoot &&
99
+ (sharedDriveSharing || !isOwner(documentId))
97
100
  )
98
101
  const isCurrentUserOwner = existingDocument?.driveId
99
102
  ? Boolean(sharedDriveSharing?.attributes?.owner)
@@ -271,6 +271,27 @@ describe('FederatedFolderModal', () => {
271
271
  ).toBe(null)
272
272
  expect(queryByText('it has a shared parent')).toBe(null)
273
273
  })
274
+
275
+ it('should allow share by email when the owner revoked the sharing of a folder that still carries a stale driveId', async () => {
276
+ // Owner revoked the last recipient: stale driveId, sharing gone, but still owner → stays shareable (linagora/twake-drive#4264).
277
+ mockGetSharingById.mockReturnValue(null)
278
+ mockIsOwner.mockReturnValue(true)
279
+
280
+ const { findByText, queryByText } = setup({
281
+ document: {
282
+ ...mockDocument,
283
+ driveId: 'federated-folder-id',
284
+ type: 'directory'
285
+ }
286
+ })
287
+
288
+ await findByText('Add users')
289
+
290
+ expect(
291
+ queryByText('This folder can only be shared by link, because')
292
+ ).toBe(null)
293
+ expect(queryByText('it has a shared parent')).toBe(null)
294
+ })
274
295
  })
275
296
 
276
297
  describe('shared drive link fetch on mount', () => {
@@ -10,7 +10,7 @@ import { MemberAvatar } from './Avatar/MemberAvatar'
10
10
  import ContactSuggestion from './ContactSuggestion'
11
11
  import { isContactToBeCreated } from '../helpers/contacts'
12
12
  import { extractEmails, validateEmail } from '../helpers/email'
13
- import { getDisplayName, Contact, Group } from '../models'
13
+ import { getDisplayName, DOCTYPE_CONTACTS, DOCTYPE_GROUPS } from '../models'
14
14
  import styles from '../styles/autosuggest.styl'
15
15
  import { contactOrGroupMatch } from '../suggestionMatchers'
16
16
 
@@ -53,7 +53,7 @@ const ShareAutosuggest = ({
53
53
  setSuggestions([
54
54
  {
55
55
  email: value,
56
- _type: Contact.doctype
56
+ _type: DOCTYPE_CONTACTS
57
57
  }
58
58
  ])
59
59
  } else {
@@ -152,7 +152,7 @@ const ShareAutosuggest = ({
152
152
  const renderInput = inputProps => (
153
153
  <div className={styles['recipientsContainer']}>
154
154
  {recipients.map((recipient, idx) => {
155
- const isContactGroup = recipient._type === Group.doctype
155
+ const isContactGroup = recipient._type === DOCTYPE_GROUPS
156
156
  const name = getDisplayName(recipient)
157
157
  return (
158
158
  <Chip
@@ -7,7 +7,7 @@ import minilog from 'cozy-minilog'
7
7
 
8
8
  import { useFetchDocumentPath } from '../../hooks/useFetchDocumentPath'
9
9
  import { useSharingContext } from '../../hooks/useSharingContext'
10
- import { Contact } from '../../models'
10
+ import { DOCTYPE_CONTACTS } from '../../models'
11
11
  import { ShareModal } from '../ShareModal'
12
12
 
13
13
  export const EditableSharingModal = ({ document, recipients, ...rest }) => {
@@ -30,7 +30,7 @@ export const EditableSharingModal = ({ document, recipients, ...rest }) => {
30
30
 
31
31
  return (
32
32
  <ShareModal
33
- createContact={contact => client.create(Contact.doctype, contact)}
33
+ createContact={contact => client.create(DOCTYPE_CONTACTS, contact)}
34
34
  document={document}
35
35
  documentType={documentType}
36
36
  hasSharedChild={
@@ -4,7 +4,7 @@ import { useClient } from 'cozy-client'
4
4
 
5
5
  import { getOrCreateFromArray } from '../../helpers/contacts'
6
6
  import { usePendingRecipients } from '../../hooks/usePendingRecipients'
7
- import { Contact } from '../../models'
7
+ import { DOCTYPE_CONTACTS } from '../../models'
8
8
 
9
9
  export const useSharedDrive = () => {
10
10
  const client = useClient()
@@ -21,7 +21,7 @@ export const useSharedDrive = () => {
21
21
  setSharedDriveName(event.target.value)
22
22
  }
23
23
 
24
- const createContact = contact => client.create(Contact.doctype, contact)
24
+ const createContact = contact => client.create(DOCTYPE_CONTACTS, contact)
25
25
 
26
26
  const onCreate = async () => {
27
27
  const contacts = await getOrCreateFromArray(
@@ -1,6 +1,6 @@
1
1
  import flag from 'cozy-flags'
2
2
 
3
- import { Group } from '../models'
3
+ import { DOCTYPE_GROUPS } from '../models'
4
4
 
5
5
  export const DEFAULT_DISPLAY_NAME = 'Share.contacts.defaultDisplayName'
6
6
 
@@ -60,7 +60,7 @@ export const hasReachRecipientsLimit = (current, next) => {
60
60
  */
61
61
  export const spreadGroupAndMergeRecipients = (recipients, newRecipient) => {
62
62
  let contactsToAdd
63
- if (newRecipient._type === Group.doctype) {
63
+ if (newRecipient._type === DOCTYPE_GROUPS) {
64
64
  contactsToAdd = newRecipient.members
65
65
  } else {
66
66
  contactsToAdd = [newRecipient]
@@ -1,10 +1,15 @@
1
1
  import { models } from 'cozy-client'
2
2
 
3
- import { Contact, Group, getDisplayName } from '../models'
3
+ import {
4
+ DOCTYPE_CONTACTS,
5
+ DOCTYPE_GROUPS,
6
+ isContact,
7
+ getDisplayName
8
+ } from '../models'
4
9
 
5
10
  export const countNewRecipients = (currentRecipients, newRecipients) => {
6
11
  const newRecipientsNotAlreadyIncluded = newRecipients.filter(recipient => {
7
- if (Contact.isContact(recipient)) {
12
+ if (isContact(recipient)) {
8
13
  const email = models.contact.getPrimaryEmail(recipient)
9
14
  const cozyUrl = models.contact.getPrimaryCozy(recipient)
10
15
  return !currentRecipients.find(
@@ -18,7 +23,7 @@ export const countNewRecipients = (currentRecipients, newRecipients) => {
18
23
 
19
24
  return newRecipientsNotAlreadyIncluded.reduce(
20
25
  (acc, recipient) => {
21
- if (recipient._type === Group.doctype) {
26
+ if (recipient._type === DOCTYPE_GROUPS) {
22
27
  return {
23
28
  ...acc,
24
29
  group: acc.group + 1
@@ -44,11 +49,11 @@ export const getSuccessMessage = (
44
49
  ) => {
45
50
  if (recipientsAfter.length === 1) {
46
51
  const recipient = recipientsAfter[0]
47
- const email = Contact.isContact(recipient)
52
+ const email = isContact(recipient)
48
53
  ? models.contact.getPrimaryEmail(recipient)
49
54
  : recipient.email
50
55
  const cozyUrl = models.contact.getPrimaryCozy(recipient)
51
- const isContactGroup = recipient._type === Group.doctype
56
+ const isContactGroup = recipient._type === DOCTYPE_GROUPS
52
57
 
53
58
  if (isContactGroup) {
54
59
  return [
@@ -125,7 +130,7 @@ export const getErrorMessage = ({
125
130
  }`
126
131
  )
127
132
 
128
- if (recipients.length === 1 && recipients[0]._type === Contact.doctype) {
133
+ if (recipients.length === 1 && recipients[0]._type === DOCTYPE_CONTACTS) {
129
134
  return [
130
135
  'Share.errors.contactAlreadyInSharing',
131
136
  {
package/src/models.js CHANGED
@@ -1,8 +1,18 @@
1
- export { CozyFile } from 'cozy-doctypes'
2
- export { Group } from 'cozy-doctypes'
3
-
4
- export {
5
- default as Contact,
6
- getInitials,
7
- getDisplayName
8
- } from './DoctypeContact'
1
+ import PropTypes from 'prop-types'
2
+
3
+ export const DOCTYPE_CONTACTS = 'io.cozy.contacts'
4
+ export const DOCTYPE_GROUPS = 'io.cozy.contacts.groups'
5
+
6
+ export const contactPropType = PropTypes.shape({
7
+ _id: PropTypes.string.isRequired,
8
+ _type: PropTypes.string.isRequired
9
+ })
10
+
11
+ export const groupPropType = PropTypes.shape({
12
+ _id: PropTypes.string.isRequired,
13
+ _type: PropTypes.string.isRequired,
14
+ name: PropTypes.string.isRequired,
15
+ trashed: PropTypes.bool
16
+ })
17
+
18
+ export { isContact, getInitials, getDisplayName } from './DoctypeContact'
@@ -1,6 +1,6 @@
1
1
  import { Q, fetchPolicies } from 'cozy-client'
2
2
 
3
- import { Contact, Group } from '../models'
3
+ import { DOCTYPE_CONTACTS, DOCTYPE_GROUPS } from '../models'
4
4
 
5
5
  const DEFAULT_CACHE_TIMEOUT_QUERIES = 9 * 60 * 1000
6
6
 
@@ -17,7 +17,7 @@ export const buildSharingsByIdQuery = sharingId => ({
17
17
  })
18
18
 
19
19
  export const buildReachableContactsQuery = () => ({
20
- definition: Q(Contact.doctype)
20
+ definition: Q(DOCTYPE_CONTACTS)
21
21
  .where({
22
22
  _id: {
23
23
  $gt: null
@@ -52,7 +52,7 @@ export const buildReachableContactsQuery = () => ({
52
52
  })
53
53
 
54
54
  export const buildContactGroupsQuery = () => ({
55
- definition: Q(Group.doctype),
55
+ definition: Q(DOCTYPE_GROUPS),
56
56
  options: {
57
57
  as: `io.cozy.contacts.groups`,
58
58
  fetchPolicy: defaultFetchPolicy
@@ -60,7 +60,7 @@ export const buildContactGroupsQuery = () => ({
60
60
  })
61
61
 
62
62
  export const buildUnreachableContactsWithGroupsQuery = () => ({
63
- definition: Q(Contact.doctype)
63
+ definition: Q(DOCTYPE_CONTACTS)
64
64
  .where({
65
65
  _id: {
66
66
  $gt: null
@@ -1,4 +1,4 @@
1
- import { Group } from 'cozy-doctypes'
1
+ import { DOCTYPE_GROUPS } from './models'
2
2
 
3
3
  let removeDiacritics
4
4
  try {
@@ -36,7 +36,7 @@ const cozyUrlMatch = (input, contact) => {
36
36
  }
37
37
 
38
38
  const groupNameMatch = (input, contactOrGroup) => {
39
- if (contactOrGroup._type !== Group.doctype) return false
39
+ if (contactOrGroup._type !== DOCTYPE_GROUPS) return false
40
40
  const normalizedInput = normalizeLowercase(input)
41
41
  return normalizeLowercase(contactOrGroup.name).includes(normalizedInput)
42
42
  }