cozy-sharing 40.1.9 → 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.
Files changed (55) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +1 -0
  3. package/dist/SharingOwnerAvatar.js +2 -2
  4. package/dist/SharingProvider.js +3 -1
  5. package/dist/components/Avatar/AvatarList.js +2 -2
  6. package/dist/components/Avatar/MemberAvatar.js +8 -33
  7. package/dist/components/Avatar/MemberAvatar.spec.js +17 -77
  8. package/dist/components/Avatar/MemberAvatarWrapper.js +20 -0
  9. package/dist/components/ConfirmTrustedRecipientsDialog.js +5 -1
  10. package/dist/components/ContactSuggestion.js +5 -2
  11. package/dist/components/Recipient/MemberRecipient.js +6 -3
  12. package/dist/components/Recipient/MemberRecipient.spec.js +1 -9
  13. package/dist/components/Recipient/MemberRecipientLite.js +2 -2
  14. package/dist/components/Recipient/PermissionTypeMenuWrapper.spec.js +3 -1
  15. package/dist/components/ShareAutosuggest.js +93 -16
  16. package/dist/components/ShareAutosuggest.spec.js +126 -3
  17. package/dist/components/ShareAutosuggest.stories.js +3 -0
  18. package/dist/components/ShareAutosuggestWrapper.js +65 -5
  19. package/dist/components/ShareAutosuggestWrapper.spec.js +113 -11
  20. package/dist/components/WhoHasAccessWrapper.js +6 -2
  21. package/dist/helpers/contacts.js +75 -16
  22. package/dist/helpers/contacts.spec.js +150 -1
  23. package/dist/helpers/recipients.js +38 -0
  24. package/dist/helpers/recipients.spec.js +53 -1
  25. package/dist/hooks/useResolveRecipients.js +6 -2
  26. package/dist/hooks/useResolveRecipients.spec.js +11 -2
  27. package/dist/index.js +1 -1
  28. package/dist/queries/queries.js +16 -3
  29. package/package.json +3 -3
  30. package/src/SharingOwnerAvatar.jsx +2 -2
  31. package/src/SharingProvider.jsx +6 -1
  32. package/src/components/Avatar/AvatarList.jsx +2 -2
  33. package/src/components/Avatar/MemberAvatar.jsx +4 -34
  34. package/src/components/Avatar/MemberAvatar.spec.jsx +13 -73
  35. package/src/components/Avatar/MemberAvatarWrapper.jsx +22 -0
  36. package/src/components/ConfirmTrustedRecipientsDialog.jsx +7 -0
  37. package/src/components/ContactSuggestion.jsx +8 -3
  38. package/src/components/Recipient/MemberRecipient.jsx +4 -2
  39. package/src/components/Recipient/MemberRecipient.spec.jsx +1 -8
  40. package/src/components/Recipient/MemberRecipientLite.jsx +2 -2
  41. package/src/components/Recipient/PermissionTypeMenuWrapper.spec.jsx +3 -1
  42. package/src/components/ShareAutosuggest.jsx +56 -9
  43. package/src/components/ShareAutosuggest.spec.jsx +60 -2
  44. package/src/components/ShareAutosuggest.stories.jsx +1 -0
  45. package/src/components/ShareAutosuggestWrapper.jsx +36 -4
  46. package/src/components/ShareAutosuggestWrapper.spec.jsx +78 -5
  47. package/src/components/WhoHasAccessWrapper.jsx +14 -2
  48. package/src/helpers/contacts.js +31 -1
  49. package/src/helpers/contacts.spec.js +102 -1
  50. package/src/helpers/recipients.js +40 -0
  51. package/src/helpers/recipients.spec.js +83 -1
  52. package/src/hooks/useResolveRecipients.js +5 -2
  53. package/src/hooks/useResolveRecipients.spec.js +8 -2
  54. package/src/index.jsx +1 -1
  55. package/src/queries/queries.js +6 -3
@@ -1,4 +1,6 @@
1
- import { render, fireEvent, screen } from '@testing-library/react';
1
+ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
2
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
3
+ import { act, render, fireEvent, screen } from '@testing-library/react';
2
4
  import React from 'react';
3
5
  import ShareAutosuggest from './ShareAutosuggest';
4
6
  import AppLike from '../../test/AppLike';
@@ -16,7 +18,8 @@ describe('ShareAutosuggest', function () {
16
18
  _ref$loading = _ref.loading,
17
19
  loading = _ref$loading === void 0 ? false : _ref$loading,
18
20
  _ref$enableCreateCont = _ref.enableCreateContact,
19
- enableCreateContact = _ref$enableCreateCont === void 0 ? true : _ref$enableCreateCont;
21
+ enableCreateContact = _ref$enableCreateCont === void 0 ? true : _ref$enableCreateCont,
22
+ fetchContacts = _ref.fetchContacts;
20
23
  render( /*#__PURE__*/React.createElement(AppLike, null, /*#__PURE__*/React.createElement(ShareAutosuggest, {
21
24
  loading: loading,
22
25
  contactsAndGroups: [],
@@ -24,7 +27,11 @@ describe('ShareAutosuggest', function () {
24
27
  onPick: onPick,
25
28
  recipients: [],
26
29
  onRemove: onRemove,
27
- enableCreateContact: enableCreateContact
30
+ enableCreateContact: enableCreateContact,
31
+ fetchContacts: fetchContacts,
32
+ getAvatarUrl: function getAvatarUrl() {
33
+ return null;
34
+ }
28
35
  })));
29
36
  };
30
37
 
@@ -123,4 +130,120 @@ describe('ShareAutosuggest', function () {
123
130
  });
124
131
  expect(screen.queryByText('loading')).toBeNull();
125
132
  });
133
+ describe('with fetchContacts', function () {
134
+ beforeEach(function () {
135
+ jest.useFakeTimers(); // Not implemented by jsdom, called when a suggestion is highlighted
136
+
137
+ Element.prototype.scrollIntoView = jest.fn();
138
+ });
139
+ afterEach(function () {
140
+ jest.useRealTimers();
141
+ });
142
+ it('should suggest the fetched contacts, debounced and from 3 characters', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
143
+ var fetchContacts, inputNode;
144
+ return _regeneratorRuntime.wrap(function _callee$(_context) {
145
+ while (1) switch (_context.prev = _context.next) {
146
+ case 0:
147
+ fetchContacts = jest.fn().mockResolvedValue([{
148
+ _id: 'org-member-1',
149
+ _type: 'io.cozy.contacts',
150
+ displayName: 'Jean Dupont',
151
+ email: [{
152
+ address: 'jean@org.example',
153
+ primary: true
154
+ }]
155
+ }]);
156
+ setup({
157
+ onPick: jest.fn(),
158
+ onRemove: jest.fn(),
159
+ fetchContacts: fetchContacts
160
+ });
161
+ inputNode = screen.getByPlaceholderText('myPlaceHolder');
162
+ fireEvent.change(inputNode, {
163
+ target: {
164
+ value: 'du'
165
+ }
166
+ });
167
+ act(function () {
168
+ return jest.runAllTimers();
169
+ });
170
+ expect(fetchContacts).not.toHaveBeenCalled();
171
+ fireEvent.change(inputNode, {
172
+ target: {
173
+ value: 'dup'
174
+ }
175
+ });
176
+ fireEvent.change(inputNode, {
177
+ target: {
178
+ value: 'dupo'
179
+ }
180
+ });
181
+ act(function () {
182
+ return jest.runAllTimers();
183
+ });
184
+ expect(fetchContacts).toHaveBeenCalledTimes(1);
185
+ expect(fetchContacts).toHaveBeenCalledWith('dupo');
186
+ _context.t0 = expect;
187
+ _context.next = 14;
188
+ return screen.findByText('Jean Dupont');
189
+
190
+ case 14:
191
+ _context.t1 = _context.sent;
192
+ (0, _context.t0)(_context.t1).toBeInTheDocument();
193
+
194
+ case 16:
195
+ case "end":
196
+ return _context.stop();
197
+ }
198
+ }, _callee);
199
+ })));
200
+ it('should show the loading indicator until the contacts are fetched', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
201
+ var resolveFetch, fetchContacts, inputNode;
202
+ return _regeneratorRuntime.wrap(function _callee3$(_context3) {
203
+ while (1) switch (_context3.prev = _context3.next) {
204
+ case 0:
205
+ fetchContacts = jest.fn(function () {
206
+ return new Promise(function (resolve) {
207
+ return resolveFetch = resolve;
208
+ });
209
+ });
210
+ setup({
211
+ onPick: jest.fn(),
212
+ onRemove: jest.fn(),
213
+ fetchContacts: fetchContacts
214
+ });
215
+ inputNode = screen.getByPlaceholderText('myPlaceHolder');
216
+ fireEvent.change(inputNode, {
217
+ target: {
218
+ value: 'dup'
219
+ }
220
+ });
221
+ expect(screen.getByText('loading')).toBeInTheDocument();
222
+ act(function () {
223
+ return jest.runAllTimers();
224
+ });
225
+ _context3.next = 8;
226
+ return act( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
227
+ return _regeneratorRuntime.wrap(function _callee2$(_context2) {
228
+ while (1) switch (_context2.prev = _context2.next) {
229
+ case 0:
230
+ return _context2.abrupt("return", resolveFetch([]));
231
+
232
+ case 1:
233
+ case "end":
234
+ return _context2.stop();
235
+ }
236
+ }, _callee2);
237
+ })));
238
+
239
+ case 8:
240
+ expect(screen.queryByText('loading')).toBeNull();
241
+
242
+ case 9:
243
+ case "end":
244
+ return _context3.stop();
245
+ }
246
+ }, _callee3);
247
+ })));
248
+ });
126
249
  });
@@ -37,6 +37,9 @@ export var Default = {
37
37
  render: ShareAutosuggestRender,
38
38
  args: {
39
39
  loading: false,
40
+ getAvatarUrl: function getAvatarUrl() {
41
+ return null;
42
+ },
40
43
  contactsAndGroups: [{
41
44
  id: 'group1',
42
45
  name: 'Family',
@@ -1,4 +1,6 @@
1
+ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
1
2
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
2
4
 
3
5
  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
6
 
@@ -6,18 +8,26 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
6
8
 
7
9
  import PropTypes from 'prop-types';
8
10
  import React from 'react';
9
- import { useQueryAll, isQueryLoading } from 'cozy-client';
11
+ import { useClient, useQueryAll, isQueryLoading } from 'cozy-client';
12
+ import { models } from 'cozy-client';
10
13
  import flag from 'cozy-flags';
11
14
  import ShareAutosuggest from './ShareAutosuggest';
12
- import { computeContactsAndGroups } from '../helpers/contacts';
15
+ import { computeContactsAndGroups, suggestContacts } from '../helpers/contacts';
16
+ import { makeAvatarUrl } from '../helpers/recipients';
17
+ import { useSharingContext } from '../hooks/useSharingContext';
18
+ import { DOCTYPE_GROUPS } from '../models';
13
19
  import { buildReachableContactsQuery, buildContactGroupsQuery, buildUnreachableContactsWithGroupsQuery } from '../queries/queries';
20
+ var ContactModel = models.contact;
14
21
  /**
15
22
  * ShareAutosuggestWrapper is responsible for fetching contacts and groups.
16
23
  * We retrieve all the contacts that are reachable and the groups they belong to.
17
24
  * In order to display the total number of members in each group, we also retrieve the contacts that are not reachable.
25
+ * With contactsSuggestEnabled, the stack suggests the contacts and we only retrieve the group members.
18
26
  */
19
27
 
20
28
  var ShareAutosuggestWrapper = function ShareAutosuggestWrapper(_ref) {
29
+ var _useSharingContext;
30
+
21
31
  var currentRecipients = _ref.currentRecipients,
22
32
  recipients = _ref.recipients,
23
33
  placeholder = _ref.placeholder,
@@ -27,7 +37,11 @@ var ShareAutosuggestWrapper = function ShareAutosuggestWrapper(_ref) {
27
37
  disabled = _ref.disabled,
28
38
  autoFocus = _ref.autoFocus,
29
39
  endAdornment = _ref.endAdornment;
30
- var reachableContactsQuery = buildReachableContactsQuery();
40
+ var client = useClient();
41
+ var contactsSuggestEnabled = (_useSharingContext = useSharingContext()) === null || _useSharingContext === void 0 ? void 0 : _useSharingContext.contactsSuggestEnabled;
42
+ var reachableContactsQuery = buildReachableContactsQuery({
43
+ inGroups: contactsSuggestEnabled
44
+ });
31
45
  var reachableContactsResult = useQueryAll(reachableContactsQuery.definition, reachableContactsQuery.options);
32
46
  var unreachableContactsWithGroupsQuery = buildUnreachableContactsWithGroupsQuery();
33
47
  var unreachableContactsWithGroupsResult = useQueryAll(unreachableContactsWithGroupsQuery.definition, _objectSpread(_objectSpread({}, unreachableContactsWithGroupsQuery.options), {}, {
@@ -36,13 +50,55 @@ var ShareAutosuggestWrapper = function ShareAutosuggestWrapper(_ref) {
36
50
  var contactGroupsQuery = buildContactGroupsQuery();
37
51
  var contactGroupsResult = useQueryAll(contactGroupsQuery.definition, contactGroupsQuery.options);
38
52
  var isLoading = isQueryLoading(reachableContactsResult) || reachableContactsResult.hasMore || isQueryLoading(contactGroupsResult) || contactGroupsResult.hasMore || flag('sharing.show-recipient-groups') && (isQueryLoading(unreachableContactsWithGroupsResult) || unreachableContactsWithGroupsResult.hasMore);
39
- var contactsAndGroups = computeContactsAndGroups({
53
+ var suggestedContactsAndGroups = computeContactsAndGroups({
40
54
  reachableContacts: reachableContactsResult.data || [],
41
55
  unreachableContactsWithGroups: unreachableContactsWithGroupsResult.data || [],
42
56
  contactGroups: contactGroupsResult.data || [],
43
57
  currentRecipients: currentRecipients,
44
58
  pendingRecipients: recipients
45
59
  });
60
+ var contactsAndGroups = contactsSuggestEnabled ? suggestedContactsAndGroups.filter(function (_ref2) {
61
+ var _type = _ref2._type;
62
+ return _type === DOCTYPE_GROUPS;
63
+ }) : suggestedContactsAndGroups; // The stack matches the emails whatever their case
64
+
65
+ var normalizeEmail = function normalizeEmail(email) {
66
+ return email === null || email === void 0 ? void 0 : email.trim().toLowerCase();
67
+ };
68
+
69
+ var isRecipient = function isRecipient(email) {
70
+ return currentRecipients.some(function (r) {
71
+ return normalizeEmail(r.email) === email;
72
+ }) || recipients.some(function (r) {
73
+ return normalizeEmail(ContactModel.getPrimaryEmail(r)) === email;
74
+ });
75
+ };
76
+
77
+ var fetchContacts = /*#__PURE__*/function () {
78
+ var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(q) {
79
+ return _regeneratorRuntime.wrap(function _callee$(_context) {
80
+ while (1) switch (_context.prev = _context.next) {
81
+ case 0:
82
+ _context.next = 2;
83
+ return suggestContacts(client, q);
84
+
85
+ case 2:
86
+ return _context.abrupt("return", _context.sent.filter(function (contact) {
87
+ return !isRecipient(normalizeEmail(ContactModel.getPrimaryEmail(contact)));
88
+ }));
89
+
90
+ case 3:
91
+ case "end":
92
+ return _context.stop();
93
+ }
94
+ }, _callee);
95
+ }));
96
+
97
+ return function fetchContacts(_x) {
98
+ return _ref3.apply(this, arguments);
99
+ };
100
+ }();
101
+
46
102
  return /*#__PURE__*/React.createElement(ShareAutosuggest, {
47
103
  loading: isLoading,
48
104
  disabled: disabled,
@@ -53,7 +109,11 @@ var ShareAutosuggestWrapper = function ShareAutosuggestWrapper(_ref) {
53
109
  enableCreateContact: enableCreateContact,
54
110
  placeholder: placeholder,
55
111
  autoFocus: autoFocus,
56
- endAdornment: endAdornment
112
+ endAdornment: endAdornment,
113
+ fetchContacts: contactsSuggestEnabled ? fetchContacts : undefined,
114
+ getAvatarUrl: function getAvatarUrl(recipient) {
115
+ return makeAvatarUrl(recipient, client.options.uri);
116
+ }
57
117
  });
58
118
  };
59
119
 
@@ -14,11 +14,13 @@ jest.mock('cozy-flags', function () {
14
14
  })
15
15
  };
16
16
  });
17
+ var mockAutosuggestProps;
17
18
  jest.mock('./ShareAutosuggest', function () {
18
19
  return {
19
20
  __esModule: true,
20
- default: function _default(_ref) {
21
- var contactsAndGroups = _ref.contactsAndGroups;
21
+ default: function _default(props) {
22
+ mockAutosuggestProps = props;
23
+ var contactsAndGroups = props.contactsAndGroups;
22
24
  return /*#__PURE__*/React.createElement("ul", null, contactsAndGroups.map(function (contactOrGroup) {
23
25
  if (contactOrGroup._type === 'io.cozy.contacts') {
24
26
  return /*#__PURE__*/React.createElement("li", {
@@ -34,14 +36,16 @@ jest.mock('./ShareAutosuggest', function () {
34
36
  };
35
37
  });
36
38
  describe('ShareAutosuggestWrapper component', function () {
37
- var setup = function setup(_ref2) {
38
- var contacts = _ref2.contacts,
39
- contactGroups = _ref2.contactGroups,
40
- unreachableContact = _ref2.unreachableContact;
39
+ var setup = function setup(_ref) {
40
+ var contacts = _ref.contacts,
41
+ contactGroups = _ref.contactGroups,
42
+ unreachableContact = _ref.unreachableContact,
43
+ groupMembers = _ref.groupMembers;
41
44
 
42
- var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
43
- currentRecipients = _ref3.currentRecipients,
44
- recipients = _ref3.recipients;
45
+ var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
46
+ currentRecipients = _ref2.currentRecipients,
47
+ recipients = _ref2.recipients,
48
+ contactsSuggestEnabled = _ref2.contactsSuggestEnabled;
45
49
 
46
50
  var mockClient = createMockClient({
47
51
  queries: {
@@ -56,11 +60,18 @@ describe('ShareAutosuggestWrapper component', function () {
56
60
  'io.cozy.contacts/unreachable-with-groups': {
57
61
  doctype: 'io.cozy.contacts',
58
62
  data: unreachableContact
63
+ },
64
+ 'io.cozy.contacts/reachable-with-groups': {
65
+ doctype: 'io.cozy.contacts',
66
+ data: groupMembers
59
67
  }
60
68
  }
61
69
  });
62
70
  render( /*#__PURE__*/React.createElement(AppLike, {
63
- client: mockClient
71
+ client: mockClient,
72
+ sharingContextValue: {
73
+ contactsSuggestEnabled: contactsSuggestEnabled
74
+ }
64
75
  }, /*#__PURE__*/React.createElement(ShareAutosuggestWrapper, {
65
76
  onPick: function onPick() {},
66
77
  onRemove: function onRemove() {},
@@ -68,6 +79,7 @@ describe('ShareAutosuggestWrapper component', function () {
68
79
  currentRecipients: currentRecipients,
69
80
  recipients: recipients
70
81
  })));
82
+ return mockClient;
71
83
  };
72
84
 
73
85
  it('should include groups and contacts when are loaded', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
@@ -240,7 +252,9 @@ describe('ShareAutosuggestWrapper component', function () {
240
252
  return _regeneratorRuntime.wrap(function _callee4$(_context4) {
241
253
  while (1) switch (_context4.prev = _context4.next) {
242
254
  case 0:
243
- flag.mockReturnValue(true);
255
+ flag.mockImplementation(function (name) {
256
+ return name === 'sharing.show-recipient-groups';
257
+ });
244
258
  contacts = [{
245
259
  id: 'df563cc4-6440',
246
260
  _id: 'df563cc4-6440',
@@ -307,4 +321,92 @@ describe('ShareAutosuggestWrapper component', function () {
307
321
  }
308
322
  }, _callee4);
309
323
  })));
324
+ describe('with contactsSuggestEnabled', function () {
325
+ beforeEach(function () {
326
+ flag.mockReset();
327
+ });
328
+ it('should suggest only groups, with the count of their members', function () {
329
+ var groupMembers = [{
330
+ id: '5a3b4ccf-c257',
331
+ _id: '5a3b4ccf-c257',
332
+ _type: 'io.cozy.contacts',
333
+ name: {
334
+ givenName: 'Teagan',
335
+ familyName: 'Wolf'
336
+ },
337
+ relationships: {
338
+ groups: {
339
+ data: [{
340
+ _id: 'fe86af20-c6c5',
341
+ _type: 'io.cozy.contacts.groups'
342
+ }]
343
+ }
344
+ }
345
+ }];
346
+ var contactGroups = [{
347
+ id: 'fe86af20-c6c5',
348
+ _id: 'fe86af20-c6c5',
349
+ _type: 'io.cozy.contacts.groups',
350
+ name: 'Friends'
351
+ }];
352
+ setup({
353
+ contactGroups: contactGroups,
354
+ groupMembers: groupMembers
355
+ }, {
356
+ contactsSuggestEnabled: true
357
+ });
358
+ expect(screen.getByText('Friends - 1 members')).toBeInTheDocument();
359
+ expect(screen.queryByText('Teagan Wolf')).not.toBeInTheDocument();
360
+ });
361
+ it('should fetch suggestions without the current and pending recipients, whatever the case of their email', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5() {
362
+ var suggestion, mockClient, fetchJSON, contacts;
363
+ return _regeneratorRuntime.wrap(function _callee5$(_context5) {
364
+ while (1) switch (_context5.prev = _context5.next) {
365
+ case 0:
366
+ suggestion = function suggestion(email) {
367
+ return {
368
+ type: 'io.cozy.contacts',
369
+ attributes: {
370
+ fullname: email,
371
+ email: [{
372
+ address: email
373
+ }]
374
+ }
375
+ };
376
+ };
377
+
378
+ mockClient = setup({
379
+ contactGroups: [],
380
+ groupMembers: []
381
+ }, {
382
+ currentRecipients: [{
383
+ email: 'Current@Cozy.cc'
384
+ }],
385
+ recipients: [{
386
+ email: 'pending@cozy.cc'
387
+ }],
388
+ contactsSuggestEnabled: true
389
+ });
390
+ fetchJSON = jest.fn().mockResolvedValue({
391
+ data: [suggestion('current@cozy.cc'), suggestion('PENDING@cozy.cc'), suggestion('new@cozy.cc')]
392
+ });
393
+ jest.spyOn(mockClient, 'getStackClient').mockReturnValue({
394
+ fetchJSON: fetchJSON
395
+ });
396
+ _context5.next = 6;
397
+ return mockAutosuggestProps.fetchContacts('coz');
398
+
399
+ case 6:
400
+ contacts = _context5.sent;
401
+ expect(contacts.map(function (c) {
402
+ return c.fullname;
403
+ })).toEqual(['new@cozy.cc']);
404
+
405
+ case 8:
406
+ case "end":
407
+ return _context5.stop();
408
+ }
409
+ }, _callee5);
410
+ })));
411
+ });
310
412
  });
@@ -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), {}, {
@@ -10,16 +10,35 @@ import _regeneratorRuntime from "@babel/runtime/regenerator";
10
10
  import { models } from 'cozy-client';
11
11
  import flag from 'cozy-flags';
12
12
  import { validateEmail } from '../helpers/email';
13
+ import { DOCTYPE_CONTACTS, DOCTYPE_GROUPS } from '../models';
13
14
  var ContactModel = models.contact; // Return the contact object if present
14
15
  // Else check if a contact exist with this email address, if yes return it
15
16
  // if not create it
17
+ // With contactsSuggestEnabled, people are sent by email and the
18
+ // stack finds or creates the contact: an org member's id is not on this instance
16
19
 
17
20
  export var getOrCreateFromArray = /*#__PURE__*/function () {
18
- var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(client, contacts, createContact) {
21
+ var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(client, contacts, createContact, contactsSuggestEnabled) {
19
22
  return _regeneratorRuntime.wrap(function _callee2$(_context2) {
20
23
  while (1) switch (_context2.prev = _context2.next) {
21
24
  case 0:
22
- _context2.next = 2;
25
+ if (!contactsSuggestEnabled) {
26
+ _context2.next = 2;
27
+ break;
28
+ }
29
+
30
+ return _context2.abrupt("return", contacts.map(function (contact) {
31
+ if (contact._type === DOCTYPE_GROUPS) return contact;
32
+ var email = ContactModel.getPrimaryEmail(contact);
33
+ if (validateEmail(email)) return {
34
+ _type: DOCTYPE_CONTACTS,
35
+ email: email
36
+ };
37
+ return contact._id ? contact : undefined;
38
+ }));
39
+
40
+ case 2:
41
+ _context2.next = 4;
23
42
  return Promise.all(contacts.map( /*#__PURE__*/function () {
24
43
  var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(contact) {
25
44
  var matchedContact, resp;
@@ -83,24 +102,64 @@ export var getOrCreateFromArray = /*#__PURE__*/function () {
83
102
  }, _callee);
84
103
  }));
85
104
 
86
- return function (_x4) {
105
+ return function (_x5) {
87
106
  return _ref2.apply(this, arguments);
88
107
  };
89
108
  }()));
90
109
 
91
- case 2:
110
+ case 4:
92
111
  return _context2.abrupt("return", _context2.sent);
93
112
 
94
- case 3:
113
+ case 5:
95
114
  case "end":
96
115
  return _context2.stop();
97
116
  }
98
117
  }, _callee2);
99
118
  }));
100
119
 
101
- return function getOrCreateFromArray(_x, _x2, _x3) {
120
+ return function getOrCreateFromArray(_x, _x2, _x3, _x4) {
102
121
  return _ref.apply(this, arguments);
103
122
  };
123
+ }(); // A suggestion without id has no contact document, and only a fullname,
124
+ // which getDisplayName does not read
125
+
126
+ export var suggestContacts = /*#__PURE__*/function () {
127
+ var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(client, q) {
128
+ var _yield$client$getStac, data;
129
+
130
+ return _regeneratorRuntime.wrap(function _callee3$(_context3) {
131
+ while (1) switch (_context3.prev = _context3.next) {
132
+ case 0:
133
+ _context3.next = 2;
134
+ return client.getStackClient().fetchJSON('GET', "/contacts/suggest?q=".concat(encodeURIComponent(q)));
135
+
136
+ case 2:
137
+ _yield$client$getStac = _context3.sent;
138
+ data = _yield$client$getStac.data;
139
+ return _context3.abrupt("return", data.map(function (_ref4) {
140
+ var id = _ref4.id,
141
+ type = _ref4.type,
142
+ attributes = _ref4.attributes;
143
+ return _objectSpread(_objectSpread(_objectSpread({}, attributes), id ? {
144
+ _id: id,
145
+ id: id
146
+ } : {
147
+ displayName: attributes.fullname
148
+ }), {}, {
149
+ _type: type
150
+ });
151
+ }));
152
+
153
+ case 5:
154
+ case "end":
155
+ return _context3.stop();
156
+ }
157
+ }, _callee3);
158
+ }));
159
+
160
+ return function suggestContacts(_x6, _x7) {
161
+ return _ref3.apply(this, arguments);
162
+ };
104
163
  }();
105
164
  export var getContactsFromGroupId = function getContactsFromGroupId() {
106
165
  var contacts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
@@ -144,12 +203,12 @@ var isCurrentUser = function isCurrentUser(contact) {
144
203
  */
145
204
 
146
205
 
147
- export var computeContactsAndGroups = function computeContactsAndGroups(_ref3) {
148
- var reachableContacts = _ref3.reachableContacts,
149
- unreachableContactsWithGroups = _ref3.unreachableContactsWithGroups,
150
- contactGroups = _ref3.contactGroups,
151
- currentRecipients = _ref3.currentRecipients,
152
- pendingRecipients = _ref3.pendingRecipients;
206
+ export var computeContactsAndGroups = function computeContactsAndGroups(_ref5) {
207
+ var reachableContacts = _ref5.reachableContacts,
208
+ unreachableContactsWithGroups = _ref5.unreachableContactsWithGroups,
209
+ contactGroups = _ref5.contactGroups,
210
+ currentRecipients = _ref5.currentRecipients,
211
+ pendingRecipients = _ref5.pendingRecipients;
153
212
  var suggestedContacts = reachableContacts.filter(function (contact) {
154
213
  return !isCurrentUser(contact) && // We do not want contact already in the sharing
155
214
  !currentRecipients.find(function (r) {
@@ -162,12 +221,12 @@ export var computeContactsAndGroups = function computeContactsAndGroups(_ref3) {
162
221
  var unreachableContacts = unreachableContactsWithGroups.filter(function (contact) {
163
222
  return !isCurrentUser(contact);
164
223
  });
165
- var currentRecipientGroups = currentRecipients.map(function (_ref4) {
166
- var id = _ref4.id;
224
+ var currentRecipientGroups = currentRecipients.map(function (_ref6) {
225
+ var id = _ref6.id;
167
226
  return id;
168
227
  }).filter(Boolean);
169
- var suggestedGroups = contactGroups.filter(function (_ref5) {
170
- var _id = _ref5._id;
228
+ var suggestedGroups = contactGroups.filter(function (_ref7) {
229
+ var _id = _ref7._id;
171
230
  return !flag('sharing.show-recipient-groups') || !currentRecipientGroups.includes(_id);
172
231
  }).map(function (contactGroup) {
173
232
  var reachableMembers = getContactsFromGroupId(suggestedContacts, contactGroup.id).map(function (contact) {