cozy-sharing 33.4.1 → 33.4.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 +7 -0
- package/dist/SharingProvider.js +40 -15
- package/dist/SharingProvider.spec.js +100 -8
- package/dist/components/Recipient/MemberRecipientPermissions.js +9 -11
- package/package.json +2 -2
- package/src/SharingProvider.jsx +35 -0
- package/src/SharingProvider.spec.jsx +121 -7
- package/src/components/Recipient/MemberRecipientPermissions.jsx +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
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
|
+
## [33.4.2](https://github.com/cozy/cozy-libs/compare/cozy-sharing@33.4.1...cozy-sharing@33.4.2) (2026-06-16)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- **cozy-sharing:** Preserve sharing updates on rollback ([7c6db1a](https://github.com/cozy/cozy-libs/commit/7c6db1ad63f3997f01bdfee7140f9dad36dde2c2))
|
|
11
|
+
- **sharing:** Update member permissions optimistically ([5c1ac86](https://github.com/cozy/cozy-libs/commit/5c1ac8631e4598ac6d2bc08761d2064c9b396f66))
|
|
12
|
+
|
|
6
13
|
## [33.4.1](https://github.com/cozy/cozy-libs/compare/cozy-sharing@33.4.0...cozy-sharing@33.4.1) (2026-06-15)
|
|
7
14
|
|
|
8
15
|
### Bug Fixes
|
package/dist/SharingProvider.js
CHANGED
|
@@ -471,7 +471,7 @@ export var SharingProvider = /*#__PURE__*/function (_Component) {
|
|
|
471
471
|
*/
|
|
472
472
|
_defineProperty(_this2, "updateSharingMemberType", /*#__PURE__*/function () {
|
|
473
473
|
var _ref12 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee10(sharingId, memberIndex, newType) {
|
|
474
|
-
var sharing, member, makeReadOnly;
|
|
474
|
+
var sharing, member, makeReadOnly, optimisticSharing, currentSharing, rollbackSharing;
|
|
475
475
|
return _regeneratorRuntime.wrap(function _callee10$(_context10) {
|
|
476
476
|
while (1) switch (_context10.prev = _context10.next) {
|
|
477
477
|
case 0:
|
|
@@ -505,39 +505,64 @@ export var SharingProvider = /*#__PURE__*/function (_Component) {
|
|
|
505
505
|
throw new Error('Unsupported sharing type: ' + newType);
|
|
506
506
|
|
|
507
507
|
case 9:
|
|
508
|
-
|
|
508
|
+
optimisticSharing = _objectSpread(_objectSpread({}, sharing), {}, {
|
|
509
|
+
attributes: _objectSpread(_objectSpread({}, sharing.attributes), {}, {
|
|
510
|
+
members: sharing.attributes.members.map(function (member, index) {
|
|
511
|
+
return index === memberIndex ? _objectSpread(_objectSpread({}, member), {}, {
|
|
512
|
+
read_only: makeReadOnly
|
|
513
|
+
}) : member;
|
|
514
|
+
})
|
|
515
|
+
})
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
_this2.dispatch(updateSharing(optimisticSharing));
|
|
519
|
+
|
|
520
|
+
_context10.prev = 11;
|
|
509
521
|
|
|
510
522
|
if (!makeReadOnly) {
|
|
511
|
-
_context10.next =
|
|
523
|
+
_context10.next = 17;
|
|
512
524
|
break;
|
|
513
525
|
}
|
|
514
526
|
|
|
515
|
-
_context10.next =
|
|
527
|
+
_context10.next = 15;
|
|
516
528
|
return _this2.sharingCol.setReadOnly(sharing, memberIndex);
|
|
517
529
|
|
|
518
|
-
case
|
|
519
|
-
_context10.next =
|
|
530
|
+
case 15:
|
|
531
|
+
_context10.next = 19;
|
|
520
532
|
break;
|
|
521
533
|
|
|
522
|
-
case
|
|
523
|
-
_context10.next =
|
|
534
|
+
case 17:
|
|
535
|
+
_context10.next = 19;
|
|
524
536
|
return _this2.sharingCol.setReadWrite(sharing, memberIndex);
|
|
525
537
|
|
|
526
|
-
case
|
|
527
|
-
_context10.next =
|
|
538
|
+
case 19:
|
|
539
|
+
_context10.next = 28;
|
|
528
540
|
break;
|
|
529
541
|
|
|
530
|
-
case
|
|
531
|
-
_context10.prev =
|
|
532
|
-
_context10.t0 = _context10["catch"](
|
|
542
|
+
case 21:
|
|
543
|
+
_context10.prev = 21;
|
|
544
|
+
_context10.t0 = _context10["catch"](11);
|
|
545
|
+
currentSharing = _getSharingById(_this2.state, sharingId) || sharing;
|
|
546
|
+
rollbackSharing = _objectSpread(_objectSpread({}, currentSharing), {}, {
|
|
547
|
+
attributes: _objectSpread(_objectSpread({}, currentSharing.attributes), {}, {
|
|
548
|
+
members: currentSharing.attributes.members.map(function (member, index) {
|
|
549
|
+
return index === memberIndex ? _objectSpread(_objectSpread({}, member), {}, {
|
|
550
|
+
read_only: !makeReadOnly
|
|
551
|
+
}) : member;
|
|
552
|
+
})
|
|
553
|
+
})
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
_this2.dispatch(updateSharing(rollbackSharing));
|
|
557
|
+
|
|
533
558
|
log.error("Failed to change member ".concat(member.email, " permission type to ").concat(newType), _context10.t0);
|
|
534
559
|
throw _context10.t0;
|
|
535
560
|
|
|
536
|
-
case
|
|
561
|
+
case 28:
|
|
537
562
|
case "end":
|
|
538
563
|
return _context10.stop();
|
|
539
564
|
}
|
|
540
|
-
}, _callee10, null, [[
|
|
565
|
+
}, _callee10, null, [[11, 21]]);
|
|
541
566
|
}));
|
|
542
567
|
|
|
543
568
|
return function (_x16, _x17, _x18) {
|
|
@@ -744,7 +744,9 @@ describe('updateSharingMemberType', function () {
|
|
|
744
744
|
setReadOnly: jest.fn().mockResolvedValue({}),
|
|
745
745
|
setReadWrite: jest.fn().mockResolvedValue({})
|
|
746
746
|
};
|
|
747
|
-
jest.
|
|
747
|
+
instance.dispatch = jest.fn(function (action) {
|
|
748
|
+
instance.state = _objectSpread(_objectSpread({}, instance.state), reducer(instance.state, action));
|
|
749
|
+
});
|
|
748
750
|
});
|
|
749
751
|
it('should throw when sharing is not found', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee15() {
|
|
750
752
|
return _regeneratorRuntime.wrap(function _callee15$(_context15) {
|
|
@@ -806,7 +808,7 @@ describe('updateSharingMemberType', function () {
|
|
|
806
808
|
}
|
|
807
809
|
}, _callee18);
|
|
808
810
|
})));
|
|
809
|
-
it('should
|
|
811
|
+
it('should optimistically dispatch a state update when switching to one-way', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee19() {
|
|
810
812
|
return _regeneratorRuntime.wrap(function _callee19$(_context19) {
|
|
811
813
|
while (1) switch (_context19.prev = _context19.next) {
|
|
812
814
|
case 0:
|
|
@@ -814,7 +816,16 @@ describe('updateSharingMemberType', function () {
|
|
|
814
816
|
return instance.updateSharingMemberType('sharing-123', 1, 'one-way');
|
|
815
817
|
|
|
816
818
|
case 2:
|
|
817
|
-
expect(instance.
|
|
819
|
+
expect(instance.dispatch).toHaveBeenCalledWith({
|
|
820
|
+
type: 'UPDATE_SHARING',
|
|
821
|
+
sharing: _objectSpread(_objectSpread({}, mockSharing), {}, {
|
|
822
|
+
attributes: _objectSpread(_objectSpread({}, mockSharing.attributes), {}, {
|
|
823
|
+
members: [mockSharing.attributes.members[0], _objectSpread(_objectSpread({}, mockSharing.attributes.members[1]), {}, {
|
|
824
|
+
read_only: true
|
|
825
|
+
})]
|
|
826
|
+
})
|
|
827
|
+
})
|
|
828
|
+
});
|
|
818
829
|
|
|
819
830
|
case 3:
|
|
820
831
|
case "end":
|
|
@@ -822,7 +833,7 @@ describe('updateSharingMemberType', function () {
|
|
|
822
833
|
}
|
|
823
834
|
}, _callee19);
|
|
824
835
|
})));
|
|
825
|
-
it('should
|
|
836
|
+
it('should optimistically dispatch a state update when switching to two-way', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee20() {
|
|
826
837
|
return _regeneratorRuntime.wrap(function _callee20$(_context20) {
|
|
827
838
|
while (1) switch (_context20.prev = _context20.next) {
|
|
828
839
|
case 0:
|
|
@@ -830,7 +841,16 @@ describe('updateSharingMemberType', function () {
|
|
|
830
841
|
return instance.updateSharingMemberType('sharing-123', 1, 'two-way');
|
|
831
842
|
|
|
832
843
|
case 2:
|
|
833
|
-
expect(instance.
|
|
844
|
+
expect(instance.dispatch).toHaveBeenCalledWith({
|
|
845
|
+
type: 'UPDATE_SHARING',
|
|
846
|
+
sharing: _objectSpread(_objectSpread({}, mockSharing), {}, {
|
|
847
|
+
attributes: _objectSpread(_objectSpread({}, mockSharing.attributes), {}, {
|
|
848
|
+
members: [mockSharing.attributes.members[0], _objectSpread(_objectSpread({}, mockSharing.attributes.members[1]), {}, {
|
|
849
|
+
read_only: false
|
|
850
|
+
})]
|
|
851
|
+
})
|
|
852
|
+
})
|
|
853
|
+
});
|
|
834
854
|
|
|
835
855
|
case 3:
|
|
836
856
|
case "end":
|
|
@@ -866,7 +886,7 @@ describe('updateSharingMemberType', function () {
|
|
|
866
886
|
}
|
|
867
887
|
}, _callee22);
|
|
868
888
|
})));
|
|
869
|
-
it('should
|
|
889
|
+
it('should rollback optimistic state update if API call fails', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee23() {
|
|
870
890
|
return _regeneratorRuntime.wrap(function _callee23$(_context23) {
|
|
871
891
|
while (1) switch (_context23.prev = _context23.next) {
|
|
872
892
|
case 0:
|
|
@@ -875,14 +895,86 @@ describe('updateSharingMemberType', function () {
|
|
|
875
895
|
return expect(instance.updateSharingMemberType('sharing-123', 1, 'one-way')).rejects.toThrow('Network error');
|
|
876
896
|
|
|
877
897
|
case 3:
|
|
878
|
-
expect(instance.
|
|
898
|
+
expect(instance.dispatch).toHaveBeenNthCalledWith(1, {
|
|
899
|
+
type: 'UPDATE_SHARING',
|
|
900
|
+
sharing: _objectSpread(_objectSpread({}, mockSharing), {}, {
|
|
901
|
+
attributes: _objectSpread(_objectSpread({}, mockSharing.attributes), {}, {
|
|
902
|
+
members: [mockSharing.attributes.members[0], _objectSpread(_objectSpread({}, mockSharing.attributes.members[1]), {}, {
|
|
903
|
+
read_only: true
|
|
904
|
+
})]
|
|
905
|
+
})
|
|
906
|
+
})
|
|
907
|
+
});
|
|
908
|
+
expect(instance.dispatch).toHaveBeenNthCalledWith(2, {
|
|
909
|
+
type: 'UPDATE_SHARING',
|
|
910
|
+
sharing: _objectSpread(_objectSpread({}, mockSharing), {}, {
|
|
911
|
+
attributes: _objectSpread(_objectSpread({}, mockSharing.attributes), {}, {
|
|
912
|
+
members: [mockSharing.attributes.members[0], _objectSpread(_objectSpread({}, mockSharing.attributes.members[1]), {}, {
|
|
913
|
+
read_only: false
|
|
914
|
+
})]
|
|
915
|
+
})
|
|
916
|
+
})
|
|
917
|
+
});
|
|
879
918
|
|
|
880
|
-
case
|
|
919
|
+
case 5:
|
|
881
920
|
case "end":
|
|
882
921
|
return _context23.stop();
|
|
883
922
|
}
|
|
884
923
|
}, _callee23);
|
|
885
924
|
})));
|
|
925
|
+
it('should preserve realtime changes when rolling back an API failure', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee25() {
|
|
926
|
+
var realtimeSharing;
|
|
927
|
+
return _regeneratorRuntime.wrap(function _callee25$(_context25) {
|
|
928
|
+
while (1) switch (_context25.prev = _context25.next) {
|
|
929
|
+
case 0:
|
|
930
|
+
realtimeSharing = _objectSpread(_objectSpread({}, mockSharing), {}, {
|
|
931
|
+
attributes: _objectSpread(_objectSpread({}, mockSharing.attributes), {}, {
|
|
932
|
+
description: 'updated by realtime',
|
|
933
|
+
members: [_objectSpread(_objectSpread({}, mockSharing.attributes.members[0]), {}, {
|
|
934
|
+
status: 'seen'
|
|
935
|
+
}), _objectSpread(_objectSpread({}, mockSharing.attributes.members[1]), {}, {
|
|
936
|
+
read_only: true
|
|
937
|
+
})]
|
|
938
|
+
})
|
|
939
|
+
});
|
|
940
|
+
instance.sharingCol.setReadOnly.mockImplementation( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee24() {
|
|
941
|
+
return _regeneratorRuntime.wrap(function _callee24$(_context24) {
|
|
942
|
+
while (1) switch (_context24.prev = _context24.next) {
|
|
943
|
+
case 0:
|
|
944
|
+
instance.state = _objectSpread(_objectSpread({}, instance.state), {}, {
|
|
945
|
+
sharings: instance.state.sharings.map(function (sharing) {
|
|
946
|
+
return sharing.id === realtimeSharing.id ? realtimeSharing : sharing;
|
|
947
|
+
})
|
|
948
|
+
});
|
|
949
|
+
throw new Error('Network error');
|
|
950
|
+
|
|
951
|
+
case 2:
|
|
952
|
+
case "end":
|
|
953
|
+
return _context24.stop();
|
|
954
|
+
}
|
|
955
|
+
}, _callee24);
|
|
956
|
+
})));
|
|
957
|
+
_context25.next = 4;
|
|
958
|
+
return expect(instance.updateSharingMemberType('sharing-123', 1, 'one-way')).rejects.toThrow('Network error');
|
|
959
|
+
|
|
960
|
+
case 4:
|
|
961
|
+
expect(instance.dispatch).toHaveBeenNthCalledWith(2, {
|
|
962
|
+
type: 'UPDATE_SHARING',
|
|
963
|
+
sharing: _objectSpread(_objectSpread({}, realtimeSharing), {}, {
|
|
964
|
+
attributes: _objectSpread(_objectSpread({}, realtimeSharing.attributes), {}, {
|
|
965
|
+
members: [realtimeSharing.attributes.members[0], _objectSpread(_objectSpread({}, realtimeSharing.attributes.members[1]), {}, {
|
|
966
|
+
read_only: false
|
|
967
|
+
})]
|
|
968
|
+
})
|
|
969
|
+
})
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
case 5:
|
|
973
|
+
case "end":
|
|
974
|
+
return _context25.stop();
|
|
975
|
+
}
|
|
976
|
+
}, _callee25);
|
|
977
|
+
})));
|
|
886
978
|
}); // TODO Convert with react-testing-library
|
|
887
979
|
// describe('hasWriteAccess', () => {
|
|
888
980
|
// it('tells if a doc is writtable', () => {
|
|
@@ -112,17 +112,18 @@ var MemberRecipientPermissions = function MemberRecipientPermissions(_ref) {
|
|
|
112
112
|
return _context2.abrupt("return");
|
|
113
113
|
|
|
114
114
|
case 3:
|
|
115
|
-
|
|
116
|
-
_context2.
|
|
115
|
+
hideMenu();
|
|
116
|
+
_context2.prev = 4;
|
|
117
|
+
_context2.next = 7;
|
|
117
118
|
return updateSharingMemberType(sharingId, memberIndex, newType);
|
|
118
119
|
|
|
119
|
-
case
|
|
120
|
-
_context2.next =
|
|
120
|
+
case 7:
|
|
121
|
+
_context2.next = 13;
|
|
121
122
|
break;
|
|
122
123
|
|
|
123
|
-
case
|
|
124
|
-
_context2.prev =
|
|
125
|
-
_context2.t0 = _context2["catch"](
|
|
124
|
+
case 9:
|
|
125
|
+
_context2.prev = 9;
|
|
126
|
+
_context2.t0 = _context2["catch"](4);
|
|
126
127
|
log.error('Failed to change member permission type', _context2.t0);
|
|
127
128
|
showAlert({
|
|
128
129
|
message: t('Share.members.error.changePermission'),
|
|
@@ -130,14 +131,11 @@ var MemberRecipientPermissions = function MemberRecipientPermissions(_ref) {
|
|
|
130
131
|
variant: 'filled'
|
|
131
132
|
});
|
|
132
133
|
|
|
133
|
-
case 12:
|
|
134
|
-
hideMenu();
|
|
135
|
-
|
|
136
134
|
case 13:
|
|
137
135
|
case "end":
|
|
138
136
|
return _context2.stop();
|
|
139
137
|
}
|
|
140
|
-
}, _callee2, null, [[
|
|
138
|
+
}, _callee2, null, [[4, 9]]);
|
|
141
139
|
}));
|
|
142
140
|
|
|
143
141
|
return function (_x) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-sharing",
|
|
3
|
-
"version": "33.4.
|
|
3
|
+
"version": "33.4.2",
|
|
4
4
|
"description": "Provides sharing login for React applications.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"author": "Cozy",
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
"sideEffects": [
|
|
84
84
|
"*.css"
|
|
85
85
|
],
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "ce6730dfc4d047382e31c566f7e066ad9d7eb195"
|
|
87
87
|
}
|
package/src/SharingProvider.jsx
CHANGED
|
@@ -358,6 +358,24 @@ export class SharingProvider extends Component {
|
|
|
358
358
|
if (newType !== 'one-way' && newType !== 'two-way') {
|
|
359
359
|
throw new Error('Unsupported sharing type: ' + newType)
|
|
360
360
|
}
|
|
361
|
+
|
|
362
|
+
const optimisticSharing = {
|
|
363
|
+
...sharing,
|
|
364
|
+
attributes: {
|
|
365
|
+
...sharing.attributes,
|
|
366
|
+
members: sharing.attributes.members.map((member, index) =>
|
|
367
|
+
index === memberIndex
|
|
368
|
+
? {
|
|
369
|
+
...member,
|
|
370
|
+
read_only: makeReadOnly
|
|
371
|
+
}
|
|
372
|
+
: member
|
|
373
|
+
)
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
this.dispatch(updateSharing(optimisticSharing))
|
|
378
|
+
|
|
361
379
|
try {
|
|
362
380
|
if (makeReadOnly) {
|
|
363
381
|
await this.sharingCol.setReadOnly(sharing, memberIndex)
|
|
@@ -365,6 +383,23 @@ export class SharingProvider extends Component {
|
|
|
365
383
|
await this.sharingCol.setReadWrite(sharing, memberIndex)
|
|
366
384
|
}
|
|
367
385
|
} catch (error) {
|
|
386
|
+
const currentSharing = getSharingById(this.state, sharingId) || sharing
|
|
387
|
+
const rollbackSharing = {
|
|
388
|
+
...currentSharing,
|
|
389
|
+
attributes: {
|
|
390
|
+
...currentSharing.attributes,
|
|
391
|
+
members: currentSharing.attributes.members.map((member, index) =>
|
|
392
|
+
index === memberIndex
|
|
393
|
+
? {
|
|
394
|
+
...member,
|
|
395
|
+
read_only: !makeReadOnly
|
|
396
|
+
}
|
|
397
|
+
: member
|
|
398
|
+
)
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
this.dispatch(updateSharing(rollbackSharing))
|
|
368
403
|
log.error(
|
|
369
404
|
`Failed to change member ${member.email} permission type to ${newType}`,
|
|
370
405
|
error
|
|
@@ -524,7 +524,9 @@ describe('updateSharingMemberType', () => {
|
|
|
524
524
|
setReadOnly: jest.fn().mockResolvedValue({}),
|
|
525
525
|
setReadWrite: jest.fn().mockResolvedValue({})
|
|
526
526
|
}
|
|
527
|
-
jest.
|
|
527
|
+
instance.dispatch = jest.fn(action => {
|
|
528
|
+
instance.state = { ...instance.state, ...reducer(instance.state, action) }
|
|
529
|
+
})
|
|
528
530
|
})
|
|
529
531
|
|
|
530
532
|
it('should throw when sharing is not found', async () => {
|
|
@@ -556,16 +558,46 @@ describe('updateSharingMemberType', () => {
|
|
|
556
558
|
expect(instance.sharingCol.setReadOnly).not.toHaveBeenCalled()
|
|
557
559
|
})
|
|
558
560
|
|
|
559
|
-
it('should
|
|
561
|
+
it('should optimistically dispatch a state update when switching to one-way', async () => {
|
|
560
562
|
await instance.updateSharingMemberType('sharing-123', 1, 'one-way')
|
|
561
563
|
|
|
562
|
-
expect(instance.
|
|
564
|
+
expect(instance.dispatch).toHaveBeenCalledWith({
|
|
565
|
+
type: 'UPDATE_SHARING',
|
|
566
|
+
sharing: {
|
|
567
|
+
...mockSharing,
|
|
568
|
+
attributes: {
|
|
569
|
+
...mockSharing.attributes,
|
|
570
|
+
members: [
|
|
571
|
+
mockSharing.attributes.members[0],
|
|
572
|
+
{
|
|
573
|
+
...mockSharing.attributes.members[1],
|
|
574
|
+
read_only: true
|
|
575
|
+
}
|
|
576
|
+
]
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
})
|
|
563
580
|
})
|
|
564
581
|
|
|
565
|
-
it('should
|
|
582
|
+
it('should optimistically dispatch a state update when switching to two-way', async () => {
|
|
566
583
|
await instance.updateSharingMemberType('sharing-123', 1, 'two-way')
|
|
567
584
|
|
|
568
|
-
expect(instance.
|
|
585
|
+
expect(instance.dispatch).toHaveBeenCalledWith({
|
|
586
|
+
type: 'UPDATE_SHARING',
|
|
587
|
+
sharing: {
|
|
588
|
+
...mockSharing,
|
|
589
|
+
attributes: {
|
|
590
|
+
...mockSharing.attributes,
|
|
591
|
+
members: [
|
|
592
|
+
mockSharing.attributes.members[0],
|
|
593
|
+
{
|
|
594
|
+
...mockSharing.attributes.members[1],
|
|
595
|
+
read_only: false
|
|
596
|
+
}
|
|
597
|
+
]
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
})
|
|
569
601
|
})
|
|
570
602
|
|
|
571
603
|
it('should rethrow error if setReadOnly fails', async () => {
|
|
@@ -588,7 +620,7 @@ describe('updateSharingMemberType', () => {
|
|
|
588
620
|
).rejects.toThrow('Network error')
|
|
589
621
|
})
|
|
590
622
|
|
|
591
|
-
it('should
|
|
623
|
+
it('should rollback optimistic state update if API call fails', async () => {
|
|
592
624
|
instance.sharingCol.setReadOnly.mockRejectedValue(
|
|
593
625
|
new Error('Network error')
|
|
594
626
|
)
|
|
@@ -597,7 +629,89 @@ describe('updateSharingMemberType', () => {
|
|
|
597
629
|
instance.updateSharingMemberType('sharing-123', 1, 'one-way')
|
|
598
630
|
).rejects.toThrow('Network error')
|
|
599
631
|
|
|
600
|
-
expect(instance.
|
|
632
|
+
expect(instance.dispatch).toHaveBeenNthCalledWith(1, {
|
|
633
|
+
type: 'UPDATE_SHARING',
|
|
634
|
+
sharing: {
|
|
635
|
+
...mockSharing,
|
|
636
|
+
attributes: {
|
|
637
|
+
...mockSharing.attributes,
|
|
638
|
+
members: [
|
|
639
|
+
mockSharing.attributes.members[0],
|
|
640
|
+
{
|
|
641
|
+
...mockSharing.attributes.members[1],
|
|
642
|
+
read_only: true
|
|
643
|
+
}
|
|
644
|
+
]
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
})
|
|
648
|
+
expect(instance.dispatch).toHaveBeenNthCalledWith(2, {
|
|
649
|
+
type: 'UPDATE_SHARING',
|
|
650
|
+
sharing: {
|
|
651
|
+
...mockSharing,
|
|
652
|
+
attributes: {
|
|
653
|
+
...mockSharing.attributes,
|
|
654
|
+
members: [
|
|
655
|
+
mockSharing.attributes.members[0],
|
|
656
|
+
{
|
|
657
|
+
...mockSharing.attributes.members[1],
|
|
658
|
+
read_only: false
|
|
659
|
+
}
|
|
660
|
+
]
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
})
|
|
664
|
+
})
|
|
665
|
+
|
|
666
|
+
it('should preserve realtime changes when rolling back an API failure', async () => {
|
|
667
|
+
const realtimeSharing = {
|
|
668
|
+
...mockSharing,
|
|
669
|
+
attributes: {
|
|
670
|
+
...mockSharing.attributes,
|
|
671
|
+
description: 'updated by realtime',
|
|
672
|
+
members: [
|
|
673
|
+
{
|
|
674
|
+
...mockSharing.attributes.members[0],
|
|
675
|
+
status: 'seen'
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
...mockSharing.attributes.members[1],
|
|
679
|
+
read_only: true
|
|
680
|
+
}
|
|
681
|
+
]
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
instance.sharingCol.setReadOnly.mockImplementation(async () => {
|
|
686
|
+
instance.state = {
|
|
687
|
+
...instance.state,
|
|
688
|
+
sharings: instance.state.sharings.map(sharing =>
|
|
689
|
+
sharing.id === realtimeSharing.id ? realtimeSharing : sharing
|
|
690
|
+
)
|
|
691
|
+
}
|
|
692
|
+
throw new Error('Network error')
|
|
693
|
+
})
|
|
694
|
+
|
|
695
|
+
await expect(
|
|
696
|
+
instance.updateSharingMemberType('sharing-123', 1, 'one-way')
|
|
697
|
+
).rejects.toThrow('Network error')
|
|
698
|
+
|
|
699
|
+
expect(instance.dispatch).toHaveBeenNthCalledWith(2, {
|
|
700
|
+
type: 'UPDATE_SHARING',
|
|
701
|
+
sharing: {
|
|
702
|
+
...realtimeSharing,
|
|
703
|
+
attributes: {
|
|
704
|
+
...realtimeSharing.attributes,
|
|
705
|
+
members: [
|
|
706
|
+
realtimeSharing.attributes.members[0],
|
|
707
|
+
{
|
|
708
|
+
...realtimeSharing.attributes.members[1],
|
|
709
|
+
read_only: false
|
|
710
|
+
}
|
|
711
|
+
]
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
})
|
|
601
715
|
})
|
|
602
716
|
})
|
|
603
717
|
|
|
@@ -74,6 +74,7 @@ const MemberRecipientPermissions = ({
|
|
|
74
74
|
hideMenu()
|
|
75
75
|
return
|
|
76
76
|
}
|
|
77
|
+
hideMenu()
|
|
77
78
|
try {
|
|
78
79
|
await updateSharingMemberType(sharingId, memberIndex, newType)
|
|
79
80
|
} catch (error) {
|
|
@@ -84,7 +85,6 @@ const MemberRecipientPermissions = ({
|
|
|
84
85
|
variant: 'filled'
|
|
85
86
|
})
|
|
86
87
|
}
|
|
87
|
-
hideMenu()
|
|
88
88
|
},
|
|
89
89
|
[
|
|
90
90
|
hideMenu,
|