cozy-sharing 37.5.5 → 37.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +3 -2
- package/dist/SharingProvider.js +270 -196
- package/dist/SharingProvider.spec.js +34 -4
- package/dist/components/FederatedFolder/FederatedFolderModal.js +61 -82
- package/dist/components/FederatedFolder/FederatedFolderModal.spec.js +19 -8
- package/dist/components/ShareModal/EditableSharingModal.js +44 -8
- package/dist/components/ShareModal/ShareModal.js +2 -2
- package/dist/helpers/effectiveRecipients.js +40 -0
- package/dist/helpers/effectiveRecipients.spec.js +134 -0
- package/dist/state.js +68 -1
- package/dist/state.spec.js +2 -1
- package/package.json +3 -3
- package/src/SharingProvider.jsx +61 -1
- package/src/SharingProvider.spec.jsx +28 -2
- package/src/components/FederatedFolder/FederatedFolderModal.jsx +50 -65
- package/src/components/FederatedFolder/FederatedFolderModal.spec.jsx +16 -8
- package/src/components/ShareModal/EditableSharingModal.jsx +61 -8
- package/src/components/ShareModal/ShareModal.jsx +2 -2
- package/src/helpers/effectiveRecipients.js +34 -0
- package/src/helpers/effectiveRecipients.spec.js +125 -0
- package/src/state.js +52 -1
- package/src/state.spec.js +2 -1
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
|
|
3
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
4
|
+
|
|
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
|
+
|
|
7
|
+
import { mapEffectiveRecipients } from './effectiveRecipients';
|
|
8
|
+
|
|
9
|
+
var makeAncestorSource = function makeAncestorSource() {
|
|
10
|
+
var rootName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'Parent Folder';
|
|
11
|
+
return {
|
|
12
|
+
sharing_id: 'sharing-1',
|
|
13
|
+
root_id: 'root-1',
|
|
14
|
+
root_name: rootName,
|
|
15
|
+
kind: 'ancestor',
|
|
16
|
+
member_index: 1,
|
|
17
|
+
read_only: false,
|
|
18
|
+
manageable: false
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
var makeSelfSource = function makeSelfSource() {
|
|
23
|
+
var _opts$memberIndex, _opts$readOnly, _opts$manageable;
|
|
24
|
+
|
|
25
|
+
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
26
|
+
return {
|
|
27
|
+
sharing_id: opts.sharing_id || 'sharing-2',
|
|
28
|
+
root_id: 'root-2',
|
|
29
|
+
root_name: 'Child Folder',
|
|
30
|
+
kind: 'self',
|
|
31
|
+
member_index: (_opts$memberIndex = opts.memberIndex) !== null && _opts$memberIndex !== void 0 ? _opts$memberIndex : 1,
|
|
32
|
+
read_only: (_opts$readOnly = opts.readOnly) !== null && _opts$readOnly !== void 0 ? _opts$readOnly : false,
|
|
33
|
+
manageable: (_opts$manageable = opts.manageable) !== null && _opts$manageable !== void 0 ? _opts$manageable : true
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
var makeRecipient = function makeRecipient() {
|
|
38
|
+
var overrides = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
39
|
+
return _objectSpread({
|
|
40
|
+
id: 'recip-1',
|
|
41
|
+
name: 'Alice',
|
|
42
|
+
email: 'alice@example.com',
|
|
43
|
+
instance: 'https://alice.mycozy.cloud',
|
|
44
|
+
status: 'ready',
|
|
45
|
+
read_only: false,
|
|
46
|
+
can_edit_here: false,
|
|
47
|
+
sources: overrides.sources || []
|
|
48
|
+
}, overrides);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
describe('mapEffectiveRecipients', function () {
|
|
52
|
+
it('returns empty array for empty input', function () {
|
|
53
|
+
expect(mapEffectiveRecipients([])).toEqual([]);
|
|
54
|
+
});
|
|
55
|
+
it('returns empty array for null or undefined', function () {
|
|
56
|
+
expect(mapEffectiveRecipients(null)).toEqual([]);
|
|
57
|
+
expect(mapEffectiveRecipients(undefined)).toEqual([]);
|
|
58
|
+
});
|
|
59
|
+
it('maps a recipient with a self source using the self source', function () {
|
|
60
|
+
var recipient = makeRecipient({
|
|
61
|
+
sources: [makeSelfSource()]
|
|
62
|
+
});
|
|
63
|
+
var result = mapEffectiveRecipients([recipient]);
|
|
64
|
+
expect(result).toHaveLength(1);
|
|
65
|
+
expect(result[0].sharingId).toBe('sharing-2');
|
|
66
|
+
expect(result[0].memberIndex).toBe(1);
|
|
67
|
+
expect(result[0].avatarPath).toBe('/sharings/sharing-2/recipients/1/avatar');
|
|
68
|
+
expect(result[0].type).toBe('two-way');
|
|
69
|
+
});
|
|
70
|
+
it('maps a recipient with only ancestor sources using the ancestor source', function () {
|
|
71
|
+
var recipient = makeRecipient({
|
|
72
|
+
sources: [makeAncestorSource('Shared Parent')]
|
|
73
|
+
});
|
|
74
|
+
var result = mapEffectiveRecipients([recipient]);
|
|
75
|
+
expect(result).toHaveLength(1);
|
|
76
|
+
expect(result[0].sharingId).toBe('sharing-1');
|
|
77
|
+
expect(result[0].memberIndex).toBe(1);
|
|
78
|
+
expect(result[0].name).toBe('Alice');
|
|
79
|
+
expect(result[0].email).toBe('alice@example.com');
|
|
80
|
+
});
|
|
81
|
+
it('maps an owner (self source, index 0) correctly', function () {
|
|
82
|
+
var recipient = makeRecipient({
|
|
83
|
+
status: 'owner',
|
|
84
|
+
read_only: true,
|
|
85
|
+
sources: [makeSelfSource({
|
|
86
|
+
memberIndex: 0,
|
|
87
|
+
manageable: false
|
|
88
|
+
})]
|
|
89
|
+
});
|
|
90
|
+
var result = mapEffectiveRecipients([recipient]);
|
|
91
|
+
expect(result).toHaveLength(1);
|
|
92
|
+
expect(result[0].status).toBe('owner');
|
|
93
|
+
expect(result[0].type).toBe('one-way');
|
|
94
|
+
});
|
|
95
|
+
it('maps an owner with only ancestor sources correctly', function () {
|
|
96
|
+
var recipient = makeRecipient({
|
|
97
|
+
status: 'owner',
|
|
98
|
+
sources: [makeAncestorSource('Parent Folder')]
|
|
99
|
+
});
|
|
100
|
+
var result = mapEffectiveRecipients([recipient]);
|
|
101
|
+
expect(result).toHaveLength(1);
|
|
102
|
+
expect(result[0].status).toBe('owner');
|
|
103
|
+
expect(result[0].sharingId).toBe('sharing-1');
|
|
104
|
+
expect(result[0].memberIndex).toBe(1);
|
|
105
|
+
});
|
|
106
|
+
it('sets type to one-way for read_only recipients', function () {
|
|
107
|
+
var recipient = makeRecipient({
|
|
108
|
+
read_only: true,
|
|
109
|
+
sources: [makeSelfSource({
|
|
110
|
+
readOnly: true
|
|
111
|
+
})]
|
|
112
|
+
});
|
|
113
|
+
var result = mapEffectiveRecipients([recipient]);
|
|
114
|
+
expect(result[0].type).toBe('one-way');
|
|
115
|
+
});
|
|
116
|
+
it('handles all recipients in the same array regardless of source kind', function () {
|
|
117
|
+
var selfRecipient = makeRecipient({
|
|
118
|
+
id: 'self-1',
|
|
119
|
+
name: 'Bob',
|
|
120
|
+
email: 'bob@example.com',
|
|
121
|
+
sources: [makeSelfSource()]
|
|
122
|
+
});
|
|
123
|
+
var ancestorRecipient = makeRecipient({
|
|
124
|
+
id: 'ancestor-1',
|
|
125
|
+
name: 'Carol',
|
|
126
|
+
email: 'carol@example.com',
|
|
127
|
+
sources: [makeAncestorSource('Root Folder')]
|
|
128
|
+
});
|
|
129
|
+
var result = mapEffectiveRecipients([selfRecipient, ancestorRecipient]);
|
|
130
|
+
expect(result).toHaveLength(2);
|
|
131
|
+
expect(result[0].sharingId).toBe('sharing-2');
|
|
132
|
+
expect(result[1].sharingId).toBe('sharing-1');
|
|
133
|
+
});
|
|
134
|
+
});
|
package/dist/state.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
1
2
|
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
2
3
|
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
|
3
4
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
@@ -23,6 +24,9 @@ var ADD_SHARING_LINK = 'ADD_SHARING_LINK';
|
|
|
23
24
|
var UPDATE_SHARING_LINK = 'UPDATE_SHARING_LINK';
|
|
24
25
|
var REVOKE_SHARING_LINK = 'REVOKE_SHARING_LINK';
|
|
25
26
|
var RECEIVE_PATHS = 'RECEIVE_PATHS';
|
|
27
|
+
var RECEIVE_EFFECTIVE_RECIPIENTS = 'RECEIVE_EFFECTIVE_RECIPIENTS';
|
|
28
|
+
var UPDATE_EFFECTIVE_RECIPIENT = 'UPDATE_EFFECTIVE_RECIPIENT';
|
|
29
|
+
var INVALIDATE_EFFECTIVE_RECIPIENTS = 'INVALIDATE_EFFECTIVE_RECIPIENTS';
|
|
26
30
|
export var SHARING_TYPE = {
|
|
27
31
|
TWO_WAY: 'two-way',
|
|
28
32
|
ONE_WAY: 'one-way'
|
|
@@ -136,6 +140,27 @@ export var receivePaths = function receivePaths(paths) {
|
|
|
136
140
|
paths: paths
|
|
137
141
|
};
|
|
138
142
|
};
|
|
143
|
+
export var receiveEffectiveRecipients = function receiveEffectiveRecipients(docId, recipients) {
|
|
144
|
+
return {
|
|
145
|
+
type: RECEIVE_EFFECTIVE_RECIPIENTS,
|
|
146
|
+
docId: docId,
|
|
147
|
+
recipients: recipients
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
export var updateEffectiveRecipient = function updateEffectiveRecipient(sharingId, memberIndex, readOnly) {
|
|
151
|
+
return {
|
|
152
|
+
type: UPDATE_EFFECTIVE_RECIPIENT,
|
|
153
|
+
sharingId: sharingId,
|
|
154
|
+
memberIndex: memberIndex,
|
|
155
|
+
readOnly: readOnly
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
export var invalidateEffectiveRecipients = function invalidateEffectiveRecipients(docId) {
|
|
159
|
+
return {
|
|
160
|
+
type: INVALIDATE_EFFECTIVE_RECIPIENTS,
|
|
161
|
+
docId: docId
|
|
162
|
+
};
|
|
163
|
+
};
|
|
139
164
|
export var matchingInstanceName = function matchingInstanceName() {
|
|
140
165
|
var instanceUri = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
|
141
166
|
return function (shareMember) {
|
|
@@ -373,6 +398,42 @@ var sharedPaths = function sharedPaths() {
|
|
|
373
398
|
}
|
|
374
399
|
};
|
|
375
400
|
|
|
401
|
+
var effectiveRecipients = function effectiveRecipients() {
|
|
402
|
+
var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
403
|
+
var action = arguments.length > 1 ? arguments[1] : undefined;
|
|
404
|
+
|
|
405
|
+
switch (action.type) {
|
|
406
|
+
case RECEIVE_EFFECTIVE_RECIPIENTS:
|
|
407
|
+
return _objectSpread(_objectSpread({}, state), {}, _defineProperty({}, action.docId, action.recipients));
|
|
408
|
+
|
|
409
|
+
case UPDATE_EFFECTIVE_RECIPIENT:
|
|
410
|
+
return Object.fromEntries(Object.entries(state).map(function (_ref2) {
|
|
411
|
+
var _ref3 = _slicedToArray(_ref2, 2),
|
|
412
|
+
docId = _ref3[0],
|
|
413
|
+
recipients = _ref3[1];
|
|
414
|
+
|
|
415
|
+
return [docId, recipients.map(function (r) {
|
|
416
|
+
return r.sharingId === action.sharingId && r.memberIndex === action.memberIndex ? _objectSpread(_objectSpread({}, r), {}, {
|
|
417
|
+
read_only: action.readOnly,
|
|
418
|
+
type: action.readOnly ? 'one-way' : 'two-way'
|
|
419
|
+
}) : r;
|
|
420
|
+
})];
|
|
421
|
+
}));
|
|
422
|
+
|
|
423
|
+
case INVALIDATE_EFFECTIVE_RECIPIENTS:
|
|
424
|
+
{
|
|
425
|
+
var _action$docId2 = action.docId,
|
|
426
|
+
_ = state[_action$docId2],
|
|
427
|
+
rest = _objectWithoutProperties(state, [_action$docId2].map(_toPropertyKey));
|
|
428
|
+
|
|
429
|
+
return rest;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
default:
|
|
433
|
+
return state;
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
|
|
376
437
|
var reducer = function reducer() {
|
|
377
438
|
var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
378
439
|
var action = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
@@ -380,7 +441,8 @@ var reducer = function reducer() {
|
|
|
380
441
|
byDocId: byDocId(state.byDocId, action),
|
|
381
442
|
sharings: sharings(state.sharings, action),
|
|
382
443
|
permissions: permissions(state.permissions, action),
|
|
383
|
-
sharedPaths: sharedPaths(state.sharedPaths, action)
|
|
444
|
+
sharedPaths: sharedPaths(state.sharedPaths, action),
|
|
445
|
+
effectiveRecipients: effectiveRecipients(state.effectiveRecipients, action)
|
|
384
446
|
};
|
|
385
447
|
};
|
|
386
448
|
|
|
@@ -643,6 +705,11 @@ export var getSharedParentPath = function getSharedParentPath(state, documentPat
|
|
|
643
705
|
}
|
|
644
706
|
|
|
645
707
|
return null;
|
|
708
|
+
};
|
|
709
|
+
export var getEffectiveRecipients = function getEffectiveRecipients(state, docId) {
|
|
710
|
+
var _state$effectiveRecip;
|
|
711
|
+
|
|
712
|
+
return ((_state$effectiveRecip = state.effectiveRecipients) === null || _state$effectiveRecip === void 0 ? void 0 : _state$effectiveRecip[docId]) || [];
|
|
646
713
|
}; // helpers
|
|
647
714
|
|
|
648
715
|
export var getSharedDocIds = function getSharedDocIds(doc) {
|
package/dist/state.spec.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-sharing",
|
|
3
|
-
"version": "37.
|
|
3
|
+
"version": "37.6.0",
|
|
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.3.
|
|
62
|
+
"cozy-ui-plus": "^12.3.15",
|
|
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": "
|
|
88
|
+
"gitHead": "aa6c91c03a8b734177fddb3075aa26110516c218"
|
|
89
89
|
}
|
package/src/SharingProvider.jsx
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from './components/ShareRestrictionModal/helpers'
|
|
10
10
|
import SharingContext from './context'
|
|
11
11
|
import { fetchNextPermissions } from './fetchNextPermissions'
|
|
12
|
+
import { mapEffectiveRecipients } from './helpers/effectiveRecipients'
|
|
12
13
|
import { fetchFilesPaths } from './helpers/files'
|
|
13
14
|
import {
|
|
14
15
|
getSharingObject,
|
|
@@ -28,6 +29,10 @@ import reducer, {
|
|
|
28
29
|
revokeGroup as revokeGroupFromState,
|
|
29
30
|
revokeSelf,
|
|
30
31
|
receivePaths,
|
|
32
|
+
receiveEffectiveRecipients,
|
|
33
|
+
updateEffectiveRecipient,
|
|
34
|
+
invalidateEffectiveRecipients,
|
|
35
|
+
getEffectiveRecipients,
|
|
31
36
|
isOwner,
|
|
32
37
|
isSharedDrive,
|
|
33
38
|
isOrgSharedDrive,
|
|
@@ -109,7 +114,11 @@ export class SharingProvider extends Component {
|
|
|
109
114
|
refresh: this.fetchAllSharings,
|
|
110
115
|
hasWriteAccess: this.hasWriteAccess,
|
|
111
116
|
renameSharedDrive: this.renameSharedDrive,
|
|
112
|
-
updateSharingMemberType: this.updateSharingMemberType
|
|
117
|
+
updateSharingMemberType: this.updateSharingMemberType,
|
|
118
|
+
getEffectiveRecipients: docId =>
|
|
119
|
+
getEffectiveRecipients(this.state, docId),
|
|
120
|
+
fetchEffectiveRecipients: this.fetchEffectiveRecipients,
|
|
121
|
+
invalidateEffectiveRecipients: this.invalidateEffectiveRecipients
|
|
113
122
|
}
|
|
114
123
|
this.isPublic = props.isPublic ?? false
|
|
115
124
|
this.realtime = null
|
|
@@ -261,6 +270,11 @@ export class SharingProvider extends Component {
|
|
|
261
270
|
|
|
262
271
|
await this.state.onShared({ document, recipients, readOnlyRecipients })
|
|
263
272
|
|
|
273
|
+
await this.fetchEffectiveRecipients(
|
|
274
|
+
getDocumentId(document),
|
|
275
|
+
document.driveId
|
|
276
|
+
)
|
|
277
|
+
|
|
264
278
|
return sharingResult
|
|
265
279
|
}
|
|
266
280
|
|
|
@@ -284,6 +298,10 @@ export class SharingProvider extends Component {
|
|
|
284
298
|
)
|
|
285
299
|
|
|
286
300
|
await this.state.onShared({ document, recipients, readOnlyRecipients })
|
|
301
|
+
await this.fetchEffectiveRecipients(
|
|
302
|
+
getDocumentId(document),
|
|
303
|
+
document.driveId
|
|
304
|
+
)
|
|
287
305
|
return data
|
|
288
306
|
}
|
|
289
307
|
|
|
@@ -318,6 +336,10 @@ export class SharingProvider extends Component {
|
|
|
318
336
|
)
|
|
319
337
|
)
|
|
320
338
|
})
|
|
339
|
+
await this.fetchEffectiveRecipients(
|
|
340
|
+
getDocumentId(document),
|
|
341
|
+
document.driveId
|
|
342
|
+
)
|
|
321
343
|
}
|
|
322
344
|
|
|
323
345
|
revoke = async (document, sharingId, recipientIndex) => {
|
|
@@ -331,6 +353,10 @@ export class SharingProvider extends Component {
|
|
|
331
353
|
document.path || (await fetchFilesPaths(client, doctype, [document]))
|
|
332
354
|
)
|
|
333
355
|
)
|
|
356
|
+
await this.fetchEffectiveRecipients(
|
|
357
|
+
getDocumentId(document),
|
|
358
|
+
document.driveId
|
|
359
|
+
)
|
|
334
360
|
}
|
|
335
361
|
|
|
336
362
|
revokeGroup = async (document, sharingId, recipientIndex) => {
|
|
@@ -350,6 +376,10 @@ export class SharingProvider extends Component {
|
|
|
350
376
|
const sharing = getSharingForSelf(this.state, document.id)
|
|
351
377
|
await this.sharingCol.revokeSelf(sharing)
|
|
352
378
|
this.dispatch(revokeSelf(sharing))
|
|
379
|
+
await this.fetchEffectiveRecipients(
|
|
380
|
+
getDocumentId(document),
|
|
381
|
+
document.driveId
|
|
382
|
+
)
|
|
353
383
|
}
|
|
354
384
|
|
|
355
385
|
/**
|
|
@@ -386,6 +416,9 @@ export class SharingProvider extends Component {
|
|
|
386
416
|
}
|
|
387
417
|
|
|
388
418
|
this.dispatch(updateSharing(optimisticSharing))
|
|
419
|
+
this.dispatch(
|
|
420
|
+
updateEffectiveRecipient(sharingId, memberIndex, makeReadOnly)
|
|
421
|
+
)
|
|
389
422
|
|
|
390
423
|
try {
|
|
391
424
|
if (makeReadOnly) {
|
|
@@ -411,12 +444,39 @@ export class SharingProvider extends Component {
|
|
|
411
444
|
}
|
|
412
445
|
|
|
413
446
|
this.dispatch(updateSharing(rollbackSharing))
|
|
447
|
+
this.dispatch(
|
|
448
|
+
updateEffectiveRecipient(sharingId, memberIndex, !makeReadOnly)
|
|
449
|
+
)
|
|
414
450
|
log.error(
|
|
415
451
|
`Failed to change member ${member.email} permission type to ${newType}`,
|
|
416
452
|
error
|
|
417
453
|
)
|
|
418
454
|
throw error
|
|
419
455
|
}
|
|
456
|
+
|
|
457
|
+
const docId = Object.keys(this.state.byDocId).find(id =>
|
|
458
|
+
this.state.byDocId[id].sharings?.includes(sharingId)
|
|
459
|
+
)
|
|
460
|
+
if (docId) {
|
|
461
|
+
const driveId = this.state.byDocId[docId].driveId
|
|
462
|
+
await this.fetchEffectiveRecipients(docId, driveId)
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
fetchEffectiveRecipients = async (fileId, driveId) => {
|
|
467
|
+
const resp = await this.sharingCol.fetchEffectiveRecipients(fileId, {
|
|
468
|
+
driveId
|
|
469
|
+
})
|
|
470
|
+
this.dispatch(
|
|
471
|
+
receiveEffectiveRecipients(
|
|
472
|
+
fileId,
|
|
473
|
+
mapEffectiveRecipients(resp?.data || [])
|
|
474
|
+
)
|
|
475
|
+
)
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
invalidateEffectiveRecipients = docId => {
|
|
479
|
+
this.dispatch(invalidateEffectiveRecipients(docId))
|
|
420
480
|
}
|
|
421
481
|
|
|
422
482
|
shareByLink = async (document, options) => {
|
|
@@ -533,7 +533,8 @@ describe('shared drive recipient revocation', () => {
|
|
|
533
533
|
sharingCol = {
|
|
534
534
|
addRecipients: jest.fn(),
|
|
535
535
|
create: jest.fn().mockResolvedValue({ data: NEW_SHARING }),
|
|
536
|
-
revokeRecipient: jest.fn().mockResolvedValue({})
|
|
536
|
+
revokeRecipient: jest.fn().mockResolvedValue({}),
|
|
537
|
+
fetchEffectiveRecipients: jest.fn().mockResolvedValue({ data: [] })
|
|
537
538
|
}
|
|
538
539
|
provider.sharingCol = sharingCol
|
|
539
540
|
})
|
|
@@ -792,7 +793,8 @@ describe('updateSharingMemberType', () => {
|
|
|
792
793
|
)
|
|
793
794
|
instance.sharingCol = {
|
|
794
795
|
setReadOnly: jest.fn().mockResolvedValue({}),
|
|
795
|
-
setReadWrite: jest.fn().mockResolvedValue({})
|
|
796
|
+
setReadWrite: jest.fn().mockResolvedValue({}),
|
|
797
|
+
fetchEffectiveRecipients: jest.fn().mockResolvedValue({ data: [] })
|
|
796
798
|
}
|
|
797
799
|
})
|
|
798
800
|
|
|
@@ -913,6 +915,12 @@ describe('updateSharingMemberType', () => {
|
|
|
913
915
|
}
|
|
914
916
|
})
|
|
915
917
|
expect(instance.dispatch).toHaveBeenNthCalledWith(2, {
|
|
918
|
+
type: 'UPDATE_EFFECTIVE_RECIPIENT',
|
|
919
|
+
sharingId: 'sharing-123',
|
|
920
|
+
memberIndex: 1,
|
|
921
|
+
readOnly: true
|
|
922
|
+
})
|
|
923
|
+
expect(instance.dispatch).toHaveBeenNthCalledWith(3, {
|
|
916
924
|
type: 'UPDATE_SHARING',
|
|
917
925
|
sharing: {
|
|
918
926
|
...mockSharing,
|
|
@@ -928,6 +936,12 @@ describe('updateSharingMemberType', () => {
|
|
|
928
936
|
}
|
|
929
937
|
}
|
|
930
938
|
})
|
|
939
|
+
expect(instance.dispatch).toHaveBeenNthCalledWith(4, {
|
|
940
|
+
type: 'UPDATE_EFFECTIVE_RECIPIENT',
|
|
941
|
+
sharingId: 'sharing-123',
|
|
942
|
+
memberIndex: 1,
|
|
943
|
+
readOnly: false
|
|
944
|
+
})
|
|
931
945
|
})
|
|
932
946
|
|
|
933
947
|
it('should preserve realtime changes when rolling back an API failure', async () => {
|
|
@@ -964,6 +978,12 @@ describe('updateSharingMemberType', () => {
|
|
|
964
978
|
).rejects.toThrow('Network error')
|
|
965
979
|
|
|
966
980
|
expect(instance.dispatch).toHaveBeenNthCalledWith(2, {
|
|
981
|
+
type: 'UPDATE_EFFECTIVE_RECIPIENT',
|
|
982
|
+
sharingId: 'sharing-123',
|
|
983
|
+
memberIndex: 1,
|
|
984
|
+
readOnly: true
|
|
985
|
+
})
|
|
986
|
+
expect(instance.dispatch).toHaveBeenNthCalledWith(3, {
|
|
967
987
|
type: 'UPDATE_SHARING',
|
|
968
988
|
sharing: {
|
|
969
989
|
...realtimeSharing,
|
|
@@ -979,6 +999,12 @@ describe('updateSharingMemberType', () => {
|
|
|
979
999
|
}
|
|
980
1000
|
}
|
|
981
1001
|
})
|
|
1002
|
+
expect(instance.dispatch).toHaveBeenNthCalledWith(4, {
|
|
1003
|
+
type: 'UPDATE_EFFECTIVE_RECIPIENT',
|
|
1004
|
+
sharingId: 'sharing-123',
|
|
1005
|
+
memberIndex: 1,
|
|
1006
|
+
readOnly: false
|
|
1007
|
+
})
|
|
982
1008
|
})
|
|
983
1009
|
})
|
|
984
1010
|
|
|
@@ -16,7 +16,6 @@ import { getOrCreateFromArray } from '../../helpers/contacts'
|
|
|
16
16
|
import withLocales from '../../hoc/withLocales'
|
|
17
17
|
import { usePendingRecipients } from '../../hooks/usePendingRecipients'
|
|
18
18
|
import { useSharingContext } from '../../hooks/useSharingContext'
|
|
19
|
-
import { getRecipientsFromSharing } from '../../state'
|
|
20
19
|
import styles from '../../styles/share.styl'
|
|
21
20
|
import AntivirusAlert from '../AntivirusAlert'
|
|
22
21
|
import { default as DumbShareByEmail } from '../ShareByEmail'
|
|
@@ -29,6 +28,7 @@ const FederatedFolderModalContent = ({
|
|
|
29
28
|
onClose,
|
|
30
29
|
onRevokeSuccess,
|
|
31
30
|
document: existingDocument,
|
|
31
|
+
recipients,
|
|
32
32
|
autoOpenShareRestriction,
|
|
33
33
|
showGenerateLinkButton
|
|
34
34
|
}) => {
|
|
@@ -48,10 +48,6 @@ const FederatedFolderModalContent = ({
|
|
|
48
48
|
getFederatedShareLink,
|
|
49
49
|
getDocumentPermissions,
|
|
50
50
|
fetchSharedDriveSharingLinks,
|
|
51
|
-
getRecipients,
|
|
52
|
-
getSharedParentPath,
|
|
53
|
-
hasSharedChild,
|
|
54
|
-
hasSharedParent,
|
|
55
51
|
isOwner,
|
|
56
52
|
revoke,
|
|
57
53
|
revokeSelf
|
|
@@ -70,11 +66,9 @@ const FederatedFolderModalContent = ({
|
|
|
70
66
|
useState(null)
|
|
71
67
|
const [frozenDoc, setFrozenDoc] = useState(null)
|
|
72
68
|
const [frozenRecipients, setFrozenRecipients] = useState(null)
|
|
73
|
-
const [sharedParentFolder, setSharedParentFolder] = useState(null)
|
|
74
69
|
|
|
75
70
|
const documentId = existingDocument?._id || existingDocument?.id
|
|
76
71
|
const folderName = existingDocument?.name || ''
|
|
77
|
-
const documentPath = existingDocument?.path
|
|
78
72
|
const sharedDriveSharing = existingDocument?.driveId
|
|
79
73
|
? getSharingById(existingDocument.driveId)
|
|
80
74
|
: null
|
|
@@ -83,25 +77,7 @@ const FederatedFolderModalContent = ({
|
|
|
83
77
|
(ids, rule) => ids.concat(rule.values || []),
|
|
84
78
|
[]
|
|
85
79
|
) || []
|
|
86
|
-
|
|
87
|
-
// find the parent shared folder to use its _id for recipient/permission lookups.
|
|
88
|
-
const hasSharedParentByPath = Boolean(
|
|
89
|
-
documentPath && hasSharedParent(documentPath)
|
|
90
|
-
)
|
|
91
|
-
const sharedParentPath =
|
|
92
|
-
!sharedDriveSharing && hasSharedParentByPath && documentPath
|
|
93
|
-
? getSharedParentPath(documentPath)
|
|
94
|
-
: null
|
|
95
|
-
const sharingReferenceId =
|
|
96
|
-
!sharedDriveSharing && sharedParentFolder?._id
|
|
97
|
-
? sharedParentFolder._id
|
|
98
|
-
: documentId
|
|
99
|
-
const existingRecipients =
|
|
100
|
-
sharedDriveSharing?.attributes?.members && documentId
|
|
101
|
-
? getRecipientsFromSharing(sharedDriveSharing, documentId)
|
|
102
|
-
: sharingReferenceId
|
|
103
|
-
? getRecipients(sharingReferenceId)
|
|
104
|
-
: []
|
|
80
|
+
|
|
105
81
|
const documentPermissions = documentId
|
|
106
82
|
? getDocumentPermissions(documentId)
|
|
107
83
|
: []
|
|
@@ -124,11 +100,6 @@ const FederatedFolderModalContent = ({
|
|
|
124
100
|
? isOwner(documentId)
|
|
125
101
|
: false
|
|
126
102
|
const isMemberReadOnly = isInsideSharedDrive && !isCurrentUserOwner
|
|
127
|
-
const hasParentRestriction = isInsideSharedDrive || hasSharedParentByPath
|
|
128
|
-
const hasChildRestriction = Boolean(
|
|
129
|
-
documentPath && hasSharedChild(documentPath)
|
|
130
|
-
)
|
|
131
|
-
const canShareByEmail = !hasParentRestriction && !hasChildRestriction
|
|
132
103
|
const canManageSharing =
|
|
133
104
|
isCurrentUserOwner || (documentId ? canReshare(documentId) : false)
|
|
134
105
|
|
|
@@ -181,7 +152,7 @@ const FederatedFolderModalContent = ({
|
|
|
181
152
|
|
|
182
153
|
setIsSending(true)
|
|
183
154
|
setFrozenDoc(existingDocument)
|
|
184
|
-
setFrozenRecipients(
|
|
155
|
+
setFrozenRecipients(recipients)
|
|
185
156
|
|
|
186
157
|
try {
|
|
187
158
|
const contacts = await getOrCreateFromArray(
|
|
@@ -219,21 +190,6 @@ const FederatedFolderModalContent = ({
|
|
|
219
190
|
}
|
|
220
191
|
}
|
|
221
192
|
|
|
222
|
-
useEffect(() => {
|
|
223
|
-
if (!sharedParentPath) return
|
|
224
|
-
const fetchParentFolder = async () => {
|
|
225
|
-
try {
|
|
226
|
-
const res = await client
|
|
227
|
-
.collection('io.cozy.files')
|
|
228
|
-
.statByPath(sharedParentPath)
|
|
229
|
-
if (res.data) setSharedParentFolder(res.data)
|
|
230
|
-
} catch (err) {
|
|
231
|
-
log.error('Failed to fetch shared parent folder', err)
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
fetchParentFolder()
|
|
235
|
-
}, [sharedParentPath, client])
|
|
236
|
-
|
|
237
193
|
useEffect(() => {
|
|
238
194
|
if (!existingDocument?.driveId) return
|
|
239
195
|
if (!documentId) return
|
|
@@ -283,7 +239,7 @@ const FederatedFolderModalContent = ({
|
|
|
283
239
|
<AntivirusAlert
|
|
284
240
|
document={isSending ? frozenDoc : existingDocument}
|
|
285
241
|
/>
|
|
286
|
-
{
|
|
242
|
+
{isInsideSharedDrive ? (
|
|
287
243
|
<div className={styles['share-byemail-onlybylink']}>
|
|
288
244
|
{t('Files.share.shareByEmail.onlyByLink', {
|
|
289
245
|
type: t(
|
|
@@ -292,26 +248,15 @@ const FederatedFolderModalContent = ({
|
|
|
292
248
|
}`
|
|
293
249
|
)
|
|
294
250
|
})}{' '}
|
|
295
|
-
<strong>
|
|
296
|
-
{t(
|
|
297
|
-
`Files.share.shareByEmail.${
|
|
298
|
-
hasParentRestriction
|
|
299
|
-
? 'hasSharedParent'
|
|
300
|
-
: 'hasSharedChild'
|
|
301
|
-
}`
|
|
302
|
-
)}
|
|
303
|
-
</strong>
|
|
251
|
+
<strong>{t('Files.share.shareByEmail.hasSharedParent')}</strong>
|
|
304
252
|
</div>
|
|
305
|
-
)
|
|
306
|
-
{canShareByEmail && (
|
|
253
|
+
) : (
|
|
307
254
|
<>
|
|
308
255
|
<Typography variant="h6" className="u-mt-1-half u-mb-half">
|
|
309
256
|
{t('Share.contacts.addUsers')}
|
|
310
257
|
</Typography>
|
|
311
258
|
<DumbShareByEmail
|
|
312
|
-
currentRecipients={
|
|
313
|
-
isSending ? frozenRecipients : existingRecipients
|
|
314
|
-
}
|
|
259
|
+
currentRecipients={isSending ? frozenRecipients : recipients}
|
|
315
260
|
document={isSending ? frozenDoc : existingDocument}
|
|
316
261
|
documentType="Files"
|
|
317
262
|
pendingRecipients={pendingRecipients}
|
|
@@ -329,7 +274,7 @@ const FederatedFolderModalContent = ({
|
|
|
329
274
|
isSharedDrive
|
|
330
275
|
canManageMembers={!isMemberReadOnly}
|
|
331
276
|
canManageLink={true}
|
|
332
|
-
recipients={isSending ? frozenRecipients :
|
|
277
|
+
recipients={isSending ? frozenRecipients : recipients}
|
|
333
278
|
document={isSending ? frozenDoc : existingDocument}
|
|
334
279
|
documentType="Files"
|
|
335
280
|
className={styles['share-dialog-members']}
|
|
@@ -348,7 +293,7 @@ const FederatedFolderModalContent = ({
|
|
|
348
293
|
showGenerateLinkButton={showGenerateLinkButton}
|
|
349
294
|
autoOpenShareRestriction={autoOpenShareRestriction}
|
|
350
295
|
/>
|
|
351
|
-
{
|
|
296
|
+
{!isInsideSharedDrive && (
|
|
352
297
|
<Button
|
|
353
298
|
variant="primary"
|
|
354
299
|
label={t('FederatedFolder.share')}
|
|
@@ -362,9 +307,49 @@ const FederatedFolderModalContent = ({
|
|
|
362
307
|
)
|
|
363
308
|
}
|
|
364
309
|
|
|
310
|
+
FederatedFolderModalContent.propTypes = {
|
|
311
|
+
onClose: PropTypes.func.isRequired,
|
|
312
|
+
onRevokeSuccess: PropTypes.func,
|
|
313
|
+
document: PropTypes.object.isRequired,
|
|
314
|
+
recipients: PropTypes.array,
|
|
315
|
+
autoOpenShareRestriction: PropTypes.bool,
|
|
316
|
+
showGenerateLinkButton: PropTypes.bool
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const FederatedFolderModalWrapper = ({ document, ...props }) => {
|
|
320
|
+
const fileId = document._id || document.id
|
|
321
|
+
const {
|
|
322
|
+
getEffectiveRecipients,
|
|
323
|
+
fetchEffectiveRecipients,
|
|
324
|
+
invalidateEffectiveRecipients
|
|
325
|
+
} = useSharingContext()
|
|
326
|
+
|
|
327
|
+
useEffect(() => {
|
|
328
|
+
fetchEffectiveRecipients(fileId, document.driveId).catch(log.error)
|
|
329
|
+
return () => {
|
|
330
|
+
invalidateEffectiveRecipients(fileId)
|
|
331
|
+
}
|
|
332
|
+
}, [
|
|
333
|
+
fileId,
|
|
334
|
+
document.driveId,
|
|
335
|
+
fetchEffectiveRecipients,
|
|
336
|
+
invalidateEffectiveRecipients
|
|
337
|
+
])
|
|
338
|
+
|
|
339
|
+
const recipients = getEffectiveRecipients(fileId)
|
|
340
|
+
|
|
341
|
+
return (
|
|
342
|
+
<FederatedFolderModalContent
|
|
343
|
+
document={document}
|
|
344
|
+
recipients={recipients}
|
|
345
|
+
{...props}
|
|
346
|
+
/>
|
|
347
|
+
)
|
|
348
|
+
}
|
|
349
|
+
|
|
365
350
|
export const FederatedFolderModal = withLocales(props => (
|
|
366
351
|
<ConfirmDialogProvider>
|
|
367
|
-
<
|
|
352
|
+
<FederatedFolderModalWrapper {...props} />
|
|
368
353
|
</ConfirmDialogProvider>
|
|
369
354
|
))
|
|
370
355
|
|