cozy-sharing 40.2.0 → 40.2.1
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 +4 -0
- package/dist/SharingOwnerAvatar.js +2 -2
- package/dist/components/Avatar/AvatarList.js +2 -2
- package/dist/components/Avatar/MemberAvatar.js +8 -33
- package/dist/components/Avatar/MemberAvatar.spec.js +17 -77
- package/dist/components/Avatar/MemberAvatarWrapper.js +20 -0
- package/dist/components/ConfirmTrustedRecipientsDialog.js +5 -1
- package/dist/components/ContactSuggestion.js +5 -2
- package/dist/components/Recipient/MemberRecipient.js +6 -3
- package/dist/components/Recipient/MemberRecipient.spec.js +1 -9
- package/dist/components/Recipient/MemberRecipientLite.js +2 -2
- package/dist/components/ShareAutosuggest.js +8 -4
- package/dist/components/ShareAutosuggest.spec.js +4 -1
- package/dist/components/ShareAutosuggest.stories.js +3 -0
- package/dist/components/ShareAutosuggestWrapper.js +5 -1
- package/dist/components/WhoHasAccessWrapper.js +6 -2
- package/dist/helpers/recipients.js +38 -0
- package/dist/helpers/recipients.spec.js +53 -1
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/src/SharingOwnerAvatar.jsx +2 -2
- package/src/components/Avatar/AvatarList.jsx +2 -2
- package/src/components/Avatar/MemberAvatar.jsx +4 -34
- package/src/components/Avatar/MemberAvatar.spec.jsx +13 -73
- package/src/components/Avatar/MemberAvatarWrapper.jsx +22 -0
- package/src/components/ConfirmTrustedRecipientsDialog.jsx +7 -0
- package/src/components/ContactSuggestion.jsx +8 -3
- package/src/components/Recipient/MemberRecipient.jsx +4 -2
- package/src/components/Recipient/MemberRecipient.spec.jsx +1 -8
- package/src/components/Recipient/MemberRecipientLite.jsx +2 -2
- package/src/components/ShareAutosuggest.jsx +13 -4
- package/src/components/ShareAutosuggest.spec.jsx +1 -0
- package/src/components/ShareAutosuggest.stories.jsx +1 -0
- package/src/components/ShareAutosuggestWrapper.jsx +2 -0
- package/src/components/WhoHasAccessWrapper.jsx +14 -2
- package/src/helpers/recipients.js +40 -0
- package/src/helpers/recipients.spec.js +83 -1
- package/src/index.jsx +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
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
|
+
## [40.2.1](https://github.com/cozy/cozy-libs/compare/cozy-sharing@40.2.0...cozy-sharing@40.2.1) (2026-10-28)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package cozy-sharing
|
|
9
|
+
|
|
6
10
|
# [40.2.0](https://github.com/cozy/cozy-libs/compare/cozy-sharing@40.1.9...cozy-sharing@40.2.0) (2026-10-28)
|
|
7
11
|
|
|
8
12
|
### Bug Fixes
|
|
@@ -2,7 +2,7 @@ import _extends from "@babel/runtime/helpers/extends";
|
|
|
2
2
|
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
|
3
3
|
var _excluded = ["docId"];
|
|
4
4
|
import React from 'react';
|
|
5
|
-
import {
|
|
5
|
+
import { MemberAvatarWrapper } from './components/Avatar/MemberAvatarWrapper';
|
|
6
6
|
import SharingContext from './context';
|
|
7
7
|
import withLocales from './hoc/withLocales';
|
|
8
8
|
export var SharingOwnerAvatar = withLocales(function (_ref) {
|
|
@@ -14,7 +14,7 @@ export var SharingOwnerAvatar = withLocales(function (_ref) {
|
|
|
14
14
|
byDocId = _ref2.byDocId,
|
|
15
15
|
getOwner = _ref2.getOwner;
|
|
16
16
|
|
|
17
|
-
return !byDocId || !byDocId[docId] ? null : /*#__PURE__*/React.createElement(
|
|
17
|
+
return !byDocId || !byDocId[docId] ? null : /*#__PURE__*/React.createElement(MemberAvatarWrapper, _extends({
|
|
18
18
|
recipient: getOwner(docId)
|
|
19
19
|
}, rest));
|
|
20
20
|
});
|
|
@@ -6,7 +6,7 @@ import { useClient } from 'cozy-client';
|
|
|
6
6
|
import Avatar from 'cozy-ui/transpiled/react/Avatar';
|
|
7
7
|
import { ExtraAvatar } from './ExtraAvatar';
|
|
8
8
|
import { GroupAvatar } from './GroupAvatar';
|
|
9
|
-
import {
|
|
9
|
+
import { MemberAvatarWrapper } from './MemberAvatarWrapper';
|
|
10
10
|
import { getDisplayName } from '../../models';
|
|
11
11
|
var styles = {
|
|
12
12
|
"recipient": "recipient__recipient___2DmZI",
|
|
@@ -96,7 +96,7 @@ var AvatarList = function AvatarList(_ref2) {
|
|
|
96
96
|
return /*#__PURE__*/React.createElement("span", {
|
|
97
97
|
"data-testid": "AvatarList-avatar".concat(recipient.status === 'owner' ? '-owner' : ''),
|
|
98
98
|
key: recipient.index
|
|
99
|
-
}, /*#__PURE__*/React.createElement(
|
|
99
|
+
}, /*#__PURE__*/React.createElement(MemberAvatarWrapper, {
|
|
100
100
|
recipient: recipient,
|
|
101
101
|
size: size
|
|
102
102
|
}));
|
|
@@ -1,49 +1,25 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
2
|
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
3
3
|
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
|
4
|
-
var _excluded = ["recipient"];
|
|
4
|
+
var _excluded = ["recipient", "avatarUrl"];
|
|
5
5
|
|
|
6
6
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
7
7
|
|
|
8
8
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
9
9
|
|
|
10
10
|
import React, { useState } from 'react';
|
|
11
|
-
import { useClient } from 'cozy-client';
|
|
12
|
-
import { getPrimaryCozy } from 'cozy-client/dist/models/contact';
|
|
13
11
|
import Avatar from 'cozy-ui/transpiled/react/Avatar';
|
|
14
12
|
import logger from '../../logger';
|
|
15
13
|
import { getInitials } from '../../models';
|
|
16
|
-
/**
|
|
17
|
-
* makeAvatarUrl derives the avatar image URL for the recipient.
|
|
18
|
-
* - If the recipient has an avatarPath (sharing context), it is
|
|
19
|
-
* used directly with a base URI prefix (unless it is already
|
|
20
|
-
* a full URL for a remote Cozy).
|
|
21
|
-
* - If the recipient has no avatarPath but has a cozy URL
|
|
22
|
-
* (contact context), it points to that Cozy's public avatar.
|
|
23
|
-
* - The status is used as a cache-buster in the URL so the
|
|
24
|
-
* browser refreshes the image when the sharing status changes.
|
|
25
|
-
* For non-sharing recipients, 'contact' is the stable default.
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
var makeAvatarUrl = function makeAvatarUrl(recipient, instanceUri) {
|
|
29
|
-
var avatarPath = recipient.avatarPath;
|
|
30
|
-
var cozyUrl = !avatarPath ? getPrimaryCozy(recipient) : null;
|
|
31
|
-
if (!avatarPath && !cozyUrl) return null;
|
|
32
|
-
var finalPath = avatarPath || "".concat(cozyUrl.replace(/\/$/, ''), "/public/avatar?fallback=initials");
|
|
33
|
-
var status = recipient.status || 'contact';
|
|
34
|
-
var isFullPath = finalPath.startsWith('http');
|
|
35
|
-
var base = isFullPath ? '' : instanceUri;
|
|
36
|
-
return "".concat(base).concat(finalPath).concat(finalPath.includes('?') ? '&' : '?', "v=").concat(status);
|
|
37
|
-
};
|
|
38
14
|
|
|
39
15
|
var MemberAvatar = function MemberAvatar(_ref) {
|
|
40
16
|
var recipient = _ref.recipient,
|
|
17
|
+
avatarUrl = _ref.avatarUrl,
|
|
41
18
|
rest = _objectWithoutProperties(_ref, _excluded);
|
|
42
19
|
|
|
43
|
-
|
|
20
|
+
// Remember which image url failed to load so we can fall back to the
|
|
44
21
|
// initials. Storing the url (instead of a boolean) means a new url is
|
|
45
22
|
// retried automatically, without an effect to reset the error state.
|
|
46
|
-
|
|
47
23
|
var _useState = useState(null),
|
|
48
24
|
_useState2 = _slicedToArray(_useState, 2),
|
|
49
25
|
erroredSrc = _useState2[0],
|
|
@@ -59,19 +35,18 @@ var MemberAvatar = function MemberAvatar(_ref) {
|
|
|
59
35
|
recipient: recipient
|
|
60
36
|
}, rest));
|
|
61
37
|
return null;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
var image = makeAvatarUrl(recipient, client.options.uri); // The avatar can become unreachable (eg. the related sharing is revoked
|
|
38
|
+
} // The avatar can become unreachable (eg. the related sharing is revoked
|
|
65
39
|
// while its members are still rendered), in which case the image request
|
|
66
40
|
// fails. Without a fallback the browser shows a broken-image icon, so we
|
|
67
41
|
// render the initials instead.
|
|
68
42
|
|
|
69
|
-
|
|
43
|
+
|
|
44
|
+
return /*#__PURE__*/React.createElement(Avatar, rest, avatarUrl && erroredSrc !== avatarUrl ? /*#__PURE__*/React.createElement("img", {
|
|
70
45
|
width: "100%",
|
|
71
46
|
height: "100%",
|
|
72
|
-
src:
|
|
47
|
+
src: avatarUrl,
|
|
73
48
|
onError: function onError() {
|
|
74
|
-
return setErroredSrc(
|
|
49
|
+
return setErroredSrc(avatarUrl);
|
|
75
50
|
}
|
|
76
51
|
}) : getInitials(recipient));
|
|
77
52
|
};
|
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
import { render, fireEvent } from '@testing-library/react';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { createMockClient } from 'cozy-client';
|
|
4
3
|
import { MemberAvatar } from './MemberAvatar';
|
|
5
4
|
import AppLike from '../../../test/AppLike';
|
|
6
5
|
describe('MemberAvatar', function () {
|
|
7
|
-
var client = createMockClient({});
|
|
8
|
-
client.options = {
|
|
9
|
-
uri: 'http://cozy.example.com'
|
|
10
|
-
};
|
|
11
|
-
|
|
12
6
|
var setup = function setup(recipient) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
var avatarUrl = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
8
|
+
return render( /*#__PURE__*/React.createElement(AppLike, null, /*#__PURE__*/React.createElement(MemberAvatar, {
|
|
9
|
+
recipient: recipient,
|
|
10
|
+
avatarUrl: avatarUrl
|
|
17
11
|
})));
|
|
18
12
|
};
|
|
19
13
|
|
|
@@ -26,92 +20,38 @@ describe('MemberAvatar', function () {
|
|
|
26
20
|
expect(container.querySelector('[class*="MuiAvatar-root"]')).toBeNull();
|
|
27
21
|
expect(container.querySelector('img')).toBeNull();
|
|
28
22
|
});
|
|
29
|
-
it('renders the avatar image
|
|
23
|
+
it('renders the avatar image', function () {
|
|
30
24
|
var _setup2 = setup({
|
|
31
25
|
public_name: 'Bob',
|
|
32
|
-
status: 'owner'
|
|
33
|
-
|
|
34
|
-
}),
|
|
26
|
+
status: 'owner'
|
|
27
|
+
}, 'http://cozy.example.com/sharings/123/recipients/0/avatar?v=owner'),
|
|
35
28
|
container = _setup2.container;
|
|
36
29
|
|
|
37
|
-
|
|
38
|
-
expect(img).toBeTruthy();
|
|
39
|
-
expect(img.getAttribute('src')).toBe('http://cozy.example.com/sharings/123/recipients/0/avatar?v=owner');
|
|
30
|
+
expect(container.querySelector('img').getAttribute('src')).toBe('http://cozy.example.com/sharings/123/recipients/0/avatar?v=owner');
|
|
40
31
|
});
|
|
41
|
-
it('
|
|
32
|
+
it('renders the initials when there is no avatar URL', function () {
|
|
42
33
|
var _setup3 = setup({
|
|
43
|
-
public_name: 'Bob',
|
|
44
|
-
status: 'owner',
|
|
45
|
-
avatarPath: '/public/avatar?fallback=initials'
|
|
46
|
-
}),
|
|
47
|
-
container = _setup3.container;
|
|
48
|
-
|
|
49
|
-
expect(container.querySelector('img').getAttribute('src')).toBe('http://cozy.example.com/public/avatar?fallback=initials&v=owner');
|
|
50
|
-
});
|
|
51
|
-
it('renders a full URL avatarPath without prefixing the local instance URI', function () {
|
|
52
|
-
var _setup4 = setup({
|
|
53
|
-
public_name: 'Jon Snow',
|
|
54
|
-
status: 'contact',
|
|
55
|
-
avatarPath: 'https://jonsnow.mycozy.cloud/public/avatar?fallback=initials'
|
|
56
|
-
}),
|
|
57
|
-
container = _setup4.container;
|
|
58
|
-
|
|
59
|
-
expect(container.querySelector('img').getAttribute('src')).toBe('https://jonsnow.mycozy.cloud/public/avatar?fallback=initials&v=contact');
|
|
60
|
-
});
|
|
61
|
-
it('derives avatarPath from cozy URL when avatarPath is not set', function () {
|
|
62
|
-
var _setup5 = setup({
|
|
63
|
-
_type: 'io.cozy.contacts',
|
|
64
|
-
name: {
|
|
65
|
-
givenName: 'Jon',
|
|
66
|
-
familyName: 'Snow'
|
|
67
|
-
},
|
|
68
|
-
cozy: [{
|
|
69
|
-
url: 'https://jonsnow.mycozy.cloud'
|
|
70
|
-
}]
|
|
71
|
-
}),
|
|
72
|
-
container = _setup5.container;
|
|
73
|
-
|
|
74
|
-
var img = container.querySelector('img');
|
|
75
|
-
expect(img).toBeTruthy();
|
|
76
|
-
expect(img.getAttribute('src')).toBe('https://jonsnow.mycozy.cloud/public/avatar?fallback=initials&v=contact');
|
|
77
|
-
});
|
|
78
|
-
it('renders initials when there is no avatarPath and no cozy URL', function () {
|
|
79
|
-
var _setup6 = setup({
|
|
80
34
|
_type: 'io.cozy.contacts',
|
|
81
35
|
name: {
|
|
82
36
|
givenName: 'Jon',
|
|
83
37
|
familyName: 'Snow'
|
|
84
38
|
}
|
|
85
39
|
}),
|
|
86
|
-
container =
|
|
87
|
-
getByText =
|
|
40
|
+
container = _setup3.container,
|
|
41
|
+
getByText = _setup3.getByText;
|
|
88
42
|
|
|
89
43
|
expect(container.querySelector('img')).toBeNull();
|
|
90
44
|
expect(getByText('JS')).toBeTruthy();
|
|
91
45
|
});
|
|
92
|
-
it('renders the initials when there is no avatarPath', function () {
|
|
93
|
-
var _setup7 = setup({
|
|
94
|
-
public_name: 'Bob',
|
|
95
|
-
status: 'owner'
|
|
96
|
-
}),
|
|
97
|
-
container = _setup7.container,
|
|
98
|
-
getByText = _setup7.getByText;
|
|
99
|
-
|
|
100
|
-
expect(container.querySelector('img')).toBeNull();
|
|
101
|
-
expect(getByText('B')).toBeTruthy();
|
|
102
|
-
});
|
|
103
46
|
it('falls back to the initials when the avatar image fails to load', function () {
|
|
104
|
-
var
|
|
47
|
+
var _setup4 = setup({
|
|
105
48
|
public_name: 'Bob',
|
|
106
|
-
status: 'owner'
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
getByText = _setup8.getByText;
|
|
49
|
+
status: 'owner'
|
|
50
|
+
}, 'http://cozy.example.com/sharings/123/recipients/0/avatar?v=owner'),
|
|
51
|
+
container = _setup4.container,
|
|
52
|
+
getByText = _setup4.getByText;
|
|
111
53
|
|
|
112
|
-
|
|
113
|
-
expect(img).toBeTruthy();
|
|
114
|
-
fireEvent.error(img);
|
|
54
|
+
fireEvent.error(container.querySelector('img'));
|
|
115
55
|
expect(container.querySelector('img')).toBeNull();
|
|
116
56
|
expect(getByText('B')).toBeTruthy();
|
|
117
57
|
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
|
3
|
+
var _excluded = ["recipient"];
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { useClient } from 'cozy-client';
|
|
6
|
+
import { MemberAvatar } from './MemberAvatar';
|
|
7
|
+
import { makeAvatarUrl } from '../../helpers/recipients';
|
|
8
|
+
|
|
9
|
+
var MemberAvatarWrapper = function MemberAvatarWrapper(_ref) {
|
|
10
|
+
var recipient = _ref.recipient,
|
|
11
|
+
avatarProps = _objectWithoutProperties(_ref, _excluded);
|
|
12
|
+
|
|
13
|
+
var client = useClient();
|
|
14
|
+
return /*#__PURE__*/React.createElement(MemberAvatar, _extends({
|
|
15
|
+
recipient: recipient,
|
|
16
|
+
avatarUrl: recipient ? makeAvatarUrl(recipient, client.options.uri) : null
|
|
17
|
+
}, avatarProps));
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export { MemberAvatarWrapper };
|
|
@@ -2,12 +2,14 @@ import _objectDestructuringEmpty from "@babel/runtime/helpers/objectDestructurin
|
|
|
2
2
|
import _extends from "@babel/runtime/helpers/extends";
|
|
3
3
|
import cx from 'classnames';
|
|
4
4
|
import React from 'react';
|
|
5
|
+
import { useClient } from 'cozy-client';
|
|
5
6
|
import List from 'cozy-ui/transpiled/react/List';
|
|
6
7
|
import Typography from 'cozy-ui/transpiled/react/Typography';
|
|
7
8
|
import useBreakpoints from 'cozy-ui/transpiled/react/providers/Breakpoints';
|
|
8
9
|
import { useI18n } from 'twake-i18n';
|
|
9
10
|
import MemberRecipient from './Recipient/MemberRecipient';
|
|
10
11
|
import ShareDialogTwoStepsConfirmationContainer from './ShareDialogTwoStepsConfirmationContainer';
|
|
12
|
+
import { makeAvatarUrl } from '../helpers/recipients';
|
|
11
13
|
/**
|
|
12
14
|
* Displays the sharing interface that can be used when ConfirmTrustedRecipientsDialog is in sharing state
|
|
13
15
|
*/
|
|
@@ -22,6 +24,7 @@ var SharingContent = function SharingContent(_ref) {
|
|
|
22
24
|
var _useBreakpoints = useBreakpoints(),
|
|
23
25
|
isMobile = _useBreakpoints.isMobile;
|
|
24
26
|
|
|
27
|
+
var client = useClient();
|
|
25
28
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Typography, {
|
|
26
29
|
variant: "body1",
|
|
27
30
|
className: cx('u-mb-half', isMobile ? 'u-ph-1 u-mt-1' : 'u-ph-2 u-mt-1-half')
|
|
@@ -32,7 +35,8 @@ var SharingContent = function SharingContent(_ref) {
|
|
|
32
35
|
key: "key_r_".concat(recipientConfirmationData.id),
|
|
33
36
|
isOwner: false,
|
|
34
37
|
recipientConfirmationData: recipientConfirmationData,
|
|
35
|
-
verifyRecipient: verifyRecipient
|
|
38
|
+
verifyRecipient: verifyRecipient,
|
|
39
|
+
avatarUrl: makeAvatarUrl(recipientConfirmationData, client.options.uri)
|
|
36
40
|
}));
|
|
37
41
|
})));
|
|
38
42
|
};
|
|
@@ -9,7 +9,8 @@ import { DOCTYPE_GROUPS, contactPropType, groupPropType, getDisplayName } from '
|
|
|
9
9
|
import { GroupAvatar } from './Avatar/GroupAvatar';
|
|
10
10
|
import { MemberAvatar } from './Avatar/MemberAvatar';
|
|
11
11
|
export var ContactSuggestion = function ContactSuggestion(_ref) {
|
|
12
|
-
var contactOrGroup = _ref.contactOrGroup
|
|
12
|
+
var contactOrGroup = _ref.contactOrGroup,
|
|
13
|
+
avatarUrl = _ref.avatarUrl;
|
|
13
14
|
|
|
14
15
|
var _useI18n = useI18n(),
|
|
15
16
|
t = _useI18n.t;
|
|
@@ -34,6 +35,7 @@ export var ContactSuggestion = function ContactSuggestion(_ref) {
|
|
|
34
35
|
button: true
|
|
35
36
|
}, /*#__PURE__*/React.createElement(ListItemIcon, null, /*#__PURE__*/React.createElement(MemberAvatar, {
|
|
36
37
|
recipient: contactOrGroup,
|
|
38
|
+
avatarUrl: avatarUrl,
|
|
37
39
|
size: "m"
|
|
38
40
|
})), /*#__PURE__*/React.createElement(ListItemText, {
|
|
39
41
|
primary: name,
|
|
@@ -46,6 +48,7 @@ var newUnknownContactProptypes = PropTypes.shape({
|
|
|
46
48
|
});
|
|
47
49
|
ContactSuggestion.propTypes = {
|
|
48
50
|
contactOrGroup: PropTypes.oneOfType([contactPropType, groupPropType, newUnknownContactProptypes]).isRequired,
|
|
49
|
-
contacts: PropTypes.arrayOf(contactPropType)
|
|
51
|
+
contacts: PropTypes.arrayOf(contactPropType),
|
|
52
|
+
avatarUrl: PropTypes.string
|
|
50
53
|
};
|
|
51
54
|
export default ContactSuggestion;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/extends";
|
|
2
2
|
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
|
3
|
-
var _excluded = ["instance", "isOwner", "status", "isCurrentUser", "recipientConfirmationData", "verifyRecipient", "fadeIn", "isOrgSharedDrive"];
|
|
3
|
+
var _excluded = ["instance", "isOwner", "status", "isCurrentUser", "recipientConfirmationData", "verifyRecipient", "fadeIn", "isOrgSharedDrive", "avatarUrl"];
|
|
4
4
|
import PropTypes from 'prop-types';
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import Fade from 'cozy-ui/transpiled/react/Fade';
|
|
@@ -33,6 +33,7 @@ var MemberRecipient = function MemberRecipient(props) {
|
|
|
33
33
|
verifyRecipient = props.verifyRecipient,
|
|
34
34
|
fadeIn = props.fadeIn,
|
|
35
35
|
isOrgSharedDrive = props.isOrgSharedDrive,
|
|
36
|
+
avatarUrl = props.avatarUrl,
|
|
36
37
|
rest = _objectWithoutProperties(props, _excluded);
|
|
37
38
|
|
|
38
39
|
var isMe = isOwner && status === 'owner' || isCurrentUser;
|
|
@@ -55,7 +56,8 @@ var MemberRecipient = function MemberRecipient(props) {
|
|
|
55
56
|
size: "small"
|
|
56
57
|
}, /*#__PURE__*/React.createElement(ListItemIcon, null, /*#__PURE__*/React.createElement(MemberAvatar, {
|
|
57
58
|
size: "m",
|
|
58
|
-
recipient: props
|
|
59
|
+
recipient: props,
|
|
60
|
+
avatarUrl: avatarUrl
|
|
59
61
|
})), /*#__PURE__*/React.createElement(ListItemText, {
|
|
60
62
|
primary: /*#__PURE__*/React.createElement(Typography, {
|
|
61
63
|
variant: "body1"
|
|
@@ -78,6 +80,7 @@ MemberRecipient.propTypes = {
|
|
|
78
80
|
isCurrentUser: PropTypes.bool,
|
|
79
81
|
recipientConfirmationData: PropTypes.object,
|
|
80
82
|
verifyRecipient: PropTypes.func,
|
|
81
|
-
isOrgSharedDrive: PropTypes.bool
|
|
83
|
+
isOrgSharedDrive: PropTypes.bool,
|
|
84
|
+
avatarUrl: PropTypes.string
|
|
82
85
|
};
|
|
83
86
|
export default MemberRecipient;
|
|
@@ -2,19 +2,11 @@ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
|
2
2
|
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
3
3
|
import { render, fireEvent } from '@testing-library/react';
|
|
4
4
|
import React from 'react';
|
|
5
|
-
import { createMockClient } from 'cozy-client';
|
|
6
5
|
import MemberRecipient from './MemberRecipient';
|
|
7
6
|
import AppLike from '../../../test/AppLike';
|
|
8
7
|
describe('MemberRecipient component', function () {
|
|
9
|
-
var client = createMockClient({});
|
|
10
|
-
client.options = {
|
|
11
|
-
uri: 'foo.mycozy.cloud'
|
|
12
|
-
};
|
|
13
|
-
|
|
14
8
|
var setup = function setup(props) {
|
|
15
|
-
return render( /*#__PURE__*/React.createElement(AppLike,
|
|
16
|
-
client: client
|
|
17
|
-
}, /*#__PURE__*/React.createElement(MemberRecipient, props)));
|
|
9
|
+
return render( /*#__PURE__*/React.createElement(AppLike, null, /*#__PURE__*/React.createElement(MemberRecipient, props)));
|
|
18
10
|
};
|
|
19
11
|
|
|
20
12
|
var createRangeBackup;
|
|
@@ -13,7 +13,7 @@ import MemberRecipientStatus from './MemberRecipientStatus';
|
|
|
13
13
|
import { DEFAULT_DISPLAY_NAME } from '../../helpers/recipients';
|
|
14
14
|
import withLocales from '../../hoc/withLocales';
|
|
15
15
|
import { getDisplayName } from '../../models';
|
|
16
|
-
import {
|
|
16
|
+
import { MemberAvatarWrapper } from '../Avatar/MemberAvatarWrapper';
|
|
17
17
|
|
|
18
18
|
var MemberRecipientLite = function MemberRecipientLite(_ref) {
|
|
19
19
|
var _recipient$type;
|
|
@@ -31,7 +31,7 @@ var MemberRecipientLite = function MemberRecipientLite(_ref) {
|
|
|
31
31
|
return /*#__PURE__*/React.createElement(ListItem, {
|
|
32
32
|
size: "small",
|
|
33
33
|
ellipsis: true
|
|
34
|
-
}, /*#__PURE__*/React.createElement(ListItemIcon, null, /*#__PURE__*/React.createElement(
|
|
34
|
+
}, /*#__PURE__*/React.createElement(ListItemIcon, null, /*#__PURE__*/React.createElement(MemberAvatarWrapper, _extends({
|
|
35
35
|
size: "m",
|
|
36
36
|
recipient: recipient
|
|
37
37
|
}, props))), /*#__PURE__*/React.createElement(ListItemText, {
|
|
@@ -42,7 +42,8 @@ var ShareAutosuggest = function ShareAutosuggest(_ref) {
|
|
|
42
42
|
placeholder = _ref.placeholder,
|
|
43
43
|
autoFocus = _ref.autoFocus,
|
|
44
44
|
endAdornment = _ref.endAdornment,
|
|
45
|
-
fetchContacts = _ref.fetchContacts
|
|
45
|
+
fetchContacts = _ref.fetchContacts,
|
|
46
|
+
getAvatarUrl = _ref.getAvatarUrl;
|
|
46
47
|
|
|
47
48
|
var _useState = useState(''),
|
|
48
49
|
_useState2 = _slicedToArray(_useState, 2),
|
|
@@ -263,7 +264,8 @@ var ShareAutosuggest = function ShareAutosuggest(_ref) {
|
|
|
263
264
|
color: recipient.color
|
|
264
265
|
}) : /*#__PURE__*/React.createElement(MemberAvatar, {
|
|
265
266
|
size: "xs",
|
|
266
|
-
recipient: recipient
|
|
267
|
+
recipient: recipient,
|
|
268
|
+
avatarUrl: getAvatarUrl(recipient)
|
|
267
269
|
}),
|
|
268
270
|
label: name,
|
|
269
271
|
onDelete: function onDelete() {
|
|
@@ -283,7 +285,8 @@ var ShareAutosuggest = function ShareAutosuggest(_ref) {
|
|
|
283
285
|
|
|
284
286
|
var renderSuggestion = function renderSuggestion(contactOrGroup) {
|
|
285
287
|
return /*#__PURE__*/React.createElement(ContactSuggestion, {
|
|
286
|
-
contactOrGroup: contactOrGroup
|
|
288
|
+
contactOrGroup: contactOrGroup,
|
|
289
|
+
avatarUrl: getAvatarUrl(contactOrGroup)
|
|
287
290
|
});
|
|
288
291
|
};
|
|
289
292
|
|
|
@@ -331,6 +334,7 @@ ShareAutosuggest.propTypes = {
|
|
|
331
334
|
placeholder: PropTypes.string,
|
|
332
335
|
autoFocus: PropTypes.bool,
|
|
333
336
|
endAdornment: PropTypes.node,
|
|
334
|
-
fetchContacts: PropTypes.func
|
|
337
|
+
fetchContacts: PropTypes.func,
|
|
338
|
+
getAvatarUrl: PropTypes.func.isRequired
|
|
335
339
|
};
|
|
336
340
|
export default ShareAutosuggest;
|
|
@@ -28,7 +28,10 @@ describe('ShareAutosuggest', function () {
|
|
|
28
28
|
recipients: [],
|
|
29
29
|
onRemove: onRemove,
|
|
30
30
|
enableCreateContact: enableCreateContact,
|
|
31
|
-
fetchContacts: fetchContacts
|
|
31
|
+
fetchContacts: fetchContacts,
|
|
32
|
+
getAvatarUrl: function getAvatarUrl() {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
32
35
|
})));
|
|
33
36
|
};
|
|
34
37
|
|
|
@@ -13,6 +13,7 @@ import { models } from 'cozy-client';
|
|
|
13
13
|
import flag from 'cozy-flags';
|
|
14
14
|
import ShareAutosuggest from './ShareAutosuggest';
|
|
15
15
|
import { computeContactsAndGroups, suggestContacts } from '../helpers/contacts';
|
|
16
|
+
import { makeAvatarUrl } from '../helpers/recipients';
|
|
16
17
|
import { useSharingContext } from '../hooks/useSharingContext';
|
|
17
18
|
import { DOCTYPE_GROUPS } from '../models';
|
|
18
19
|
import { buildReachableContactsQuery, buildContactGroupsQuery, buildUnreachableContactsWithGroupsQuery } from '../queries/queries';
|
|
@@ -109,7 +110,10 @@ var ShareAutosuggestWrapper = function ShareAutosuggestWrapper(_ref) {
|
|
|
109
110
|
placeholder: placeholder,
|
|
110
111
|
autoFocus: autoFocus,
|
|
111
112
|
endAdornment: endAdornment,
|
|
112
|
-
fetchContacts: contactsSuggestEnabled ? fetchContacts : undefined
|
|
113
|
+
fetchContacts: contactsSuggestEnabled ? fetchContacts : undefined,
|
|
114
|
+
getAvatarUrl: function getAvatarUrl(recipient) {
|
|
115
|
+
return makeAvatarUrl(recipient, client.options.uri);
|
|
116
|
+
}
|
|
113
117
|
});
|
|
114
118
|
};
|
|
115
119
|
|
|
@@ -7,7 +7,7 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
|
|
|
7
7
|
import React from 'react';
|
|
8
8
|
import { useClient, useQuery, hasQueryBeenLoaded } from 'cozy-client';
|
|
9
9
|
import WhoHasAccess from './WhoHasAccess';
|
|
10
|
-
import { hasMemberActions, markCurrentUser } from '../helpers/recipients';
|
|
10
|
+
import { addAvatarUrls, hasMemberActions, makeAvatarUrl, markCurrentUser } from '../helpers/recipients';
|
|
11
11
|
import { useMemberPermissionType } from '../hooks/useMemberPermissionType';
|
|
12
12
|
import { useSharingContext } from '../hooks/useSharingContext';
|
|
13
13
|
import { buildInstanceSettingsQuery } from '../queries/queries'; // When the owner is not part of a sharing's members (eg. the folder only has
|
|
@@ -53,7 +53,7 @@ var WhoHasAccessWrapper = function WhoHasAccessWrapper(_ref) {
|
|
|
53
53
|
revokeSelf = _useSharingContext.revokeSelf;
|
|
54
54
|
|
|
55
55
|
var instanceUri = client.options.uri;
|
|
56
|
-
var markedRecipients = markCurrentUser(recipients, instanceUri);
|
|
56
|
+
var markedRecipients = addAvatarUrls(markCurrentUser(recipients, instanceUri), instanceUri);
|
|
57
57
|
var hasMemberMenus = markedRecipients.some(function (recipient) {
|
|
58
58
|
return !recipient.members && !recipientsToBeConfirmed.some(function (user) {
|
|
59
59
|
return user.email === recipient.email;
|
|
@@ -85,6 +85,10 @@ var WhoHasAccessWrapper = function WhoHasAccessWrapper(_ref) {
|
|
|
85
85
|
status: 'owner',
|
|
86
86
|
instance: instanceUri,
|
|
87
87
|
avatarPath: OWNER_PUBLIC_AVATAR_PATH,
|
|
88
|
+
avatarUrl: makeAvatarUrl({
|
|
89
|
+
status: 'owner',
|
|
90
|
+
avatarPath: OWNER_PUBLIC_AVATAR_PATH
|
|
91
|
+
}, instanceUri),
|
|
88
92
|
public_name: (_instanceSettingsResu = instanceSettingsResult.data) === null || _instanceSettingsResu === void 0 || (_instanceSettingsResu = _instanceSettingsResu.attributes) === null || _instanceSettingsResu === void 0 ? void 0 : _instanceSettingsResu.public_name
|
|
89
93
|
} : null;
|
|
90
94
|
var owner = ownerRecipient ? _objectSpread(_objectSpread({}, ownerRecipient), {}, {
|
|
@@ -5,6 +5,7 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
|
|
|
5
5
|
|
|
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
|
+
import { getPrimaryCozy } from 'cozy-client/dist/models/contact';
|
|
8
9
|
import flag from 'cozy-flags';
|
|
9
10
|
import { DOCTYPE_GROUPS } from '../models';
|
|
10
11
|
export var DEFAULT_DISPLAY_NAME = 'Share.contacts.defaultDisplayName';
|
|
@@ -141,4 +142,41 @@ export var hasMemberActions = function hasMemberActions(_ref2) {
|
|
|
141
142
|
canManageSharing = _ref2.canManageSharing,
|
|
142
143
|
canManageMembers = _ref2.canManageMembers;
|
|
143
144
|
return canManageMembers && status !== 'owner' && (isCurrentUser && !isOwner || canManageSharing);
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* makeAvatarUrl derives the avatar image URL for the recipient.
|
|
148
|
+
* - If the recipient has an avatarPath (sharing context), it is
|
|
149
|
+
* used directly with a base URI prefix (unless it is already
|
|
150
|
+
* a full URL for a remote Cozy).
|
|
151
|
+
* - If the recipient has no avatarPath but has a cozy URL
|
|
152
|
+
* (contact context), it points to that Cozy's public avatar.
|
|
153
|
+
* - The status is used as a cache-buster in the URL so the
|
|
154
|
+
* browser refreshes the image when the sharing status changes.
|
|
155
|
+
* For non-sharing recipients, 'contact' is the stable default.
|
|
156
|
+
*/
|
|
157
|
+
|
|
158
|
+
export var makeAvatarUrl = function makeAvatarUrl(recipient, instanceUri) {
|
|
159
|
+
var avatarPath = recipient.avatarPath;
|
|
160
|
+
var cozyUrl = !avatarPath ? getPrimaryCozy(recipient) : null;
|
|
161
|
+
if (!avatarPath && !cozyUrl) return null;
|
|
162
|
+
var finalPath = avatarPath || "".concat(cozyUrl.replace(/\/$/, ''), "/public/avatar?fallback=initials");
|
|
163
|
+
var status = recipient.status || 'contact';
|
|
164
|
+
var isFullPath = finalPath.startsWith('http');
|
|
165
|
+
var base = isFullPath ? '' : instanceUri;
|
|
166
|
+
return "".concat(base).concat(finalPath).concat(finalPath.includes('?') ? '&' : '?', "v=").concat(status);
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* Adds to the recipients, except groups, the URL of their avatar
|
|
170
|
+
*
|
|
171
|
+
* @param {object[]} recipients - Recipients of a sharing
|
|
172
|
+
* @param {string} instanceUri - URI of the current user's instance
|
|
173
|
+
* @returns {object[]} The recipients with their `avatarUrl`
|
|
174
|
+
*/
|
|
175
|
+
|
|
176
|
+
export var addAvatarUrls = function addAvatarUrls(recipients, instanceUri) {
|
|
177
|
+
return recipients.map(function (recipient) {
|
|
178
|
+
return recipient.members ? recipient : _objectSpread(_objectSpread({}, recipient), {}, {
|
|
179
|
+
avatarUrl: makeAvatarUrl(recipient, instanceUri)
|
|
180
|
+
});
|
|
181
|
+
});
|
|
144
182
|
};
|
|
@@ -5,7 +5,7 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
|
|
|
5
5
|
|
|
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
|
-
import { hasMemberActions, markCurrentUser } from './recipients';
|
|
8
|
+
import { addAvatarUrls, hasMemberActions, makeAvatarUrl, markCurrentUser } from './recipients';
|
|
9
9
|
var instanceUri = 'https://alice.cozy.cc';
|
|
10
10
|
describe('markCurrentUser', function () {
|
|
11
11
|
it('flags the recipient whose instance is the current one', function () {
|
|
@@ -86,4 +86,56 @@ describe('hasMemberActions', function () {
|
|
|
86
86
|
status: 'ready'
|
|
87
87
|
}))).toBe(false);
|
|
88
88
|
});
|
|
89
|
+
});
|
|
90
|
+
describe('makeAvatarUrl', function () {
|
|
91
|
+
var instanceUri = 'http://cozy.example.com';
|
|
92
|
+
it('prefixes the avatarPath with the instance URI and the status', function () {
|
|
93
|
+
expect(makeAvatarUrl({
|
|
94
|
+
status: 'owner',
|
|
95
|
+
avatarPath: '/sharings/123/recipients/0/avatar'
|
|
96
|
+
}, instanceUri)).toBe('http://cozy.example.com/sharings/123/recipients/0/avatar?v=owner');
|
|
97
|
+
});
|
|
98
|
+
it('appends the status with & when avatarPath already has a query string', function () {
|
|
99
|
+
expect(makeAvatarUrl({
|
|
100
|
+
status: 'owner',
|
|
101
|
+
avatarPath: '/public/avatar?fallback=initials'
|
|
102
|
+
}, instanceUri)).toBe('http://cozy.example.com/public/avatar?fallback=initials&v=owner');
|
|
103
|
+
});
|
|
104
|
+
it('does not prefix a full URL avatarPath with the instance URI', function () {
|
|
105
|
+
expect(makeAvatarUrl({
|
|
106
|
+
status: 'contact',
|
|
107
|
+
avatarPath: 'https://jonsnow.mycozy.cloud/public/avatar?fallback=initials'
|
|
108
|
+
}, instanceUri)).toBe('https://jonsnow.mycozy.cloud/public/avatar?fallback=initials&v=contact');
|
|
109
|
+
});
|
|
110
|
+
it('points to the public avatar of the contact cozy when there is no avatarPath', function () {
|
|
111
|
+
expect(makeAvatarUrl({
|
|
112
|
+
_type: 'io.cozy.contacts',
|
|
113
|
+
cozy: [{
|
|
114
|
+
url: 'https://jonsnow.mycozy.cloud'
|
|
115
|
+
}]
|
|
116
|
+
}, instanceUri)).toBe('https://jonsnow.mycozy.cloud/public/avatar?fallback=initials&v=contact');
|
|
117
|
+
});
|
|
118
|
+
it('returns null when there is no avatarPath and no cozy URL', function () {
|
|
119
|
+
expect(makeAvatarUrl({
|
|
120
|
+
_type: 'io.cozy.contacts',
|
|
121
|
+
public_name: 'Bob'
|
|
122
|
+
}, instanceUri)).toBe(null);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
describe('addAvatarUrls', function () {
|
|
126
|
+
it('adds the avatar URL to the members but not to the groups', function () {
|
|
127
|
+
var _addAvatarUrls = addAvatarUrls([{
|
|
128
|
+
status: 'ready',
|
|
129
|
+
avatarPath: '/sharings/123/recipients/1/avatar'
|
|
130
|
+
}, {
|
|
131
|
+
name: 'Team',
|
|
132
|
+
members: []
|
|
133
|
+
}], 'http://cozy.example.com'),
|
|
134
|
+
_addAvatarUrls2 = _slicedToArray(_addAvatarUrls, 2),
|
|
135
|
+
member = _addAvatarUrls2[0],
|
|
136
|
+
group = _addAvatarUrls2[1];
|
|
137
|
+
|
|
138
|
+
expect(member.avatarUrl).toBe('http://cozy.example.com/sharings/123/recipients/1/avatar?v=ready');
|
|
139
|
+
expect(group).not.toHaveProperty('avatarUrl');
|
|
140
|
+
});
|
|
89
141
|
});
|