cozy-sharing 33.2.0 → 33.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 CHANGED
@@ -3,6 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [33.2.1](https://github.com/cozy/cozy-libs/compare/cozy-sharing@33.2.0...cozy-sharing@33.2.1) (2026-06-09)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **cozy-sharing:** Refresh link state in share modal ([dfb6dd3](https://github.com/cozy/cozy-libs/commit/dfb6dd39e81b13a7da37ab8f5b4480fb645df01e))
11
+
6
12
  # [33.2.0](https://github.com/cozy/cozy-libs/compare/cozy-sharing@33.1.2...cozy-sharing@33.2.0) (2026-06-09)
7
13
 
8
14
  ### Features
@@ -2,7 +2,7 @@ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
2
2
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
3
  import _regeneratorRuntime from "@babel/runtime/regenerator";
4
4
  import PropTypes from 'prop-types';
5
- import React, { useCallback, useEffect, useMemo, useState } from 'react';
5
+ import React, { useCallback, useState } from 'react';
6
6
  import { useClient } from 'cozy-client';
7
7
  import Button from 'cozy-ui/transpiled/react/Buttons';
8
8
  import { FixedDialog } from 'cozy-ui/transpiled/react/CozyDialogs';
@@ -51,9 +51,6 @@ var FederatedFolderModalContent = function FederatedFolderModalContent(_ref) {
51
51
  share = _useSharingContext.share,
52
52
  getSharingLink = _useSharingContext.getSharingLink,
53
53
  getFederatedShareLink = _useSharingContext.getFederatedShareLink,
54
- getDocumentPermissions = _useSharingContext.getDocumentPermissions,
55
- getOwner = _useSharingContext.getOwner,
56
- getSharingById = _useSharingContext.getSharingById,
57
54
  getRecipients = _useSharingContext.getRecipients,
58
55
  hasSharedChild = _useSharingContext.hasSharedChild,
59
56
  hasSharedParent = _useSharingContext.hasSharedParent,
@@ -66,11 +63,6 @@ var FederatedFolderModalContent = function FederatedFolderModalContent(_ref) {
66
63
  showConfirmDialog = _useConfirmDialog.showConfirmDialog,
67
64
  closeConfirmDialog = _useConfirmDialog.closeConfirmDialog;
68
65
 
69
- var _useState = useState(null),
70
- _useState2 = _slicedToArray(_useState, 2),
71
- sharingLink = _useState2[0],
72
- setSharingLink = _useState2[1];
73
-
74
66
  var _usePendingRecipients = usePendingRecipients(),
75
67
  pendingRecipients = _usePendingRecipients.pendingRecipients,
76
68
  setPendingRecipients = _usePendingRecipients.setPendingRecipients,
@@ -83,20 +75,20 @@ var FederatedFolderModalContent = function FederatedFolderModalContent(_ref) {
83
75
  // we do not use the reactive existingDocument and existingRecipients.
84
76
 
85
77
 
86
- var _useState3 = useState(false),
78
+ var _useState = useState(false),
79
+ _useState2 = _slicedToArray(_useState, 2),
80
+ isSending = _useState2[0],
81
+ setIsSending = _useState2[1];
82
+
83
+ var _useState3 = useState(null),
87
84
  _useState4 = _slicedToArray(_useState3, 2),
88
- isSending = _useState4[0],
89
- setIsSending = _useState4[1];
85
+ frozenDoc = _useState4[0],
86
+ setFrozenDoc = _useState4[1];
90
87
 
91
88
  var _useState5 = useState(null),
92
89
  _useState6 = _slicedToArray(_useState5, 2),
93
- frozenDoc = _useState6[0],
94
- setFrozenDoc = _useState6[1];
95
-
96
- var _useState7 = useState(null),
97
- _useState8 = _slicedToArray(_useState7, 2),
98
- frozenRecipients = _useState8[0],
99
- setFrozenRecipients = _useState8[1];
90
+ frozenRecipients = _useState6[0],
91
+ setFrozenRecipients = _useState6[1];
100
92
 
101
93
  var handleCloseRequest = useCallback(function () {
102
94
  if (pendingRecipients.length === 0) {
@@ -122,55 +114,6 @@ var FederatedFolderModalContent = function FederatedFolderModalContent(_ref) {
122
114
  }))
123
115
  });
124
116
  }, [closeConfirmDialog, onClose, pendingRecipients.length, showConfirmDialog, t]);
125
- var documentPermissions = useMemo(function () {
126
- return existingDocument ? getDocumentPermissions(existingDocument._id) : [];
127
- }, [existingDocument, getDocumentPermissions]);
128
- useEffect(function () {
129
- var fetchSharingLink = /*#__PURE__*/function () {
130
- var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
131
- var link;
132
- return _regeneratorRuntime.wrap(function _callee$(_context) {
133
- while (1) switch (_context.prev = _context.next) {
134
- case 0:
135
- if (existingDocument) {
136
- _context.next = 2;
137
- break;
138
- }
139
-
140
- return _context.abrupt("return");
141
-
142
- case 2:
143
- if (!existingDocument.driveId) {
144
- _context.next = 9;
145
- break;
146
- }
147
-
148
- _context.next = 5;
149
- return getFederatedShareLink(existingDocument);
150
-
151
- case 5:
152
- link = _context.sent;
153
- setSharingLink(link);
154
- _context.next = 10;
155
- break;
156
-
157
- case 9:
158
- setSharingLink(getSharingLink(existingDocument._id));
159
-
160
- case 10:
161
- case "end":
162
- return _context.stop();
163
- }
164
- }, _callee);
165
- }));
166
-
167
- return function fetchSharingLink() {
168
- return _ref2.apply(this, arguments);
169
- };
170
- }();
171
-
172
- fetchSharingLink();
173
- }, [existingDocument, documentPermissions, getFederatedShareLink, getSharingLink, getOwner, getSharingById]);
174
117
  var folderName = (existingDocument === null || existingDocument === void 0 ? void 0 : existingDocument.name) || '';
175
118
  var documentPath = existingDocument === null || existingDocument === void 0 ? void 0 : existingDocument.path;
176
119
  var hasParentRestriction = Boolean((existingDocument === null || existingDocument === void 0 ? void 0 : existingDocument.driveId) || documentPath && hasSharedParent(documentPath));
@@ -178,34 +121,34 @@ var FederatedFolderModalContent = function FederatedFolderModalContent(_ref) {
178
121
  var canShareByEmail = !hasParentRestriction && !hasChildRestriction;
179
122
 
180
123
  var onSend = /*#__PURE__*/function () {
181
- var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
124
+ var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
182
125
  var contacts, readWriteRecipients, readOnlyRecipients;
183
- return _regeneratorRuntime.wrap(function _callee2$(_context2) {
184
- while (1) switch (_context2.prev = _context2.next) {
126
+ return _regeneratorRuntime.wrap(function _callee$(_context) {
127
+ while (1) switch (_context.prev = _context.next) {
185
128
  case 0:
186
129
  if (!(isSending || pendingRecipients.length === 0)) {
187
- _context2.next = 3;
130
+ _context.next = 3;
188
131
  break;
189
132
  }
190
133
 
191
134
  onClose();
192
- return _context2.abrupt("return");
135
+ return _context.abrupt("return");
193
136
 
194
137
  case 3:
195
138
  setIsSending(true);
196
139
  setFrozenDoc(existingDocument);
197
140
  setFrozenRecipients(existingRecipients);
198
- _context2.prev = 6;
199
- _context2.next = 9;
141
+ _context.prev = 6;
142
+ _context.next = 9;
200
143
  return getOrCreateFromArray(client, pendingRecipients, function (contact) {
201
144
  return client.create('io.cozy.contacts', contact);
202
145
  });
203
146
 
204
147
  case 9:
205
- contacts = _context2.sent;
148
+ contacts = _context.sent;
206
149
  readWriteRecipients = selectedOption === 'readOnly' ? [] : contacts;
207
150
  readOnlyRecipients = selectedOption === 'readOnly' ? contacts : [];
208
- _context2.next = 14;
151
+ _context.next = 14;
209
152
  return share({
210
153
  description: folderName,
211
154
  document: existingDocument,
@@ -222,12 +165,12 @@ var FederatedFolderModalContent = function FederatedFolderModalContent(_ref) {
222
165
  variant: 'filled'
223
166
  });
224
167
  onClose();
225
- _context2.next = 21;
168
+ _context.next = 21;
226
169
  break;
227
170
 
228
171
  case 18:
229
- _context2.prev = 18;
230
- _context2.t0 = _context2["catch"](6);
172
+ _context.prev = 18;
173
+ _context.t0 = _context["catch"](6);
231
174
  showAlert({
232
175
  message: t('FederatedFolder.errorNotification'),
233
176
  severity: 'error',
@@ -235,23 +178,24 @@ var FederatedFolderModalContent = function FederatedFolderModalContent(_ref) {
235
178
  });
236
179
 
237
180
  case 21:
238
- _context2.prev = 21;
181
+ _context.prev = 21;
239
182
  setIsSending(false);
240
- return _context2.finish(21);
183
+ return _context.finish(21);
241
184
 
242
185
  case 24:
243
186
  case "end":
244
- return _context2.stop();
187
+ return _context.stop();
245
188
  }
246
- }, _callee2, null, [[6, 18, 21, 24]]);
189
+ }, _callee, null, [[6, 18, 21, 24]]);
247
190
  }));
248
191
 
249
192
  return function onSend() {
250
- return _ref3.apply(this, arguments);
193
+ return _ref2.apply(this, arguments);
251
194
  };
252
195
  }();
253
196
 
254
197
  var existingRecipients = existingDocument ? getRecipients(existingDocument._id) : [];
198
+ var displayedLink = existingDocument !== null && existingDocument !== void 0 && existingDocument.driveId ? getFederatedShareLink(existingDocument) : getSharingLink(existingDocument === null || existingDocument === void 0 ? void 0 : existingDocument._id);
255
199
  var modalTitle = t('FederatedFolder.shareTitle', {
256
200
  name: folderName
257
201
  });
@@ -296,10 +240,10 @@ var FederatedFolderModalContent = function FederatedFolderModalContent(_ref) {
296
240
  documentType: "Files",
297
241
  className: "u-w-100",
298
242
  onRevoke: revoke,
299
- link: sharingLink
243
+ link: displayedLink
300
244
  })),
301
245
  actions: /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ShareByLink, {
302
- link: sharingLink,
246
+ link: displayedLink,
303
247
  document: isSending ? frozenDoc : existingDocument,
304
248
  documentType: "Files",
305
249
  showGenerateLinkButton: showGenerateLinkButton,
@@ -15,6 +15,7 @@ import { getOrCreateFromArray } from '../../helpers/contacts';
15
15
  import { usePendingRecipients } from '../../hooks/usePendingRecipients';
16
16
  import AppLike from '../SharingBanner/test/AppLike';
17
17
  var mockShare = jest.fn();
18
+ var mockShareByLink = jest.fn();
18
19
  var mockRevoke = jest.fn();
19
20
  var mockGetSharingLink = jest.fn();
20
21
  var mockGetFederatedShareLink = jest.fn();
@@ -28,10 +29,13 @@ jest.mock('../../hooks/useSharingContext', function () {
28
29
  useSharingContext: function useSharingContext() {
29
30
  return {
30
31
  share: mockShare,
32
+ shareByLink: mockShareByLink,
31
33
  revoke: mockRevoke,
32
34
  getSharingLink: mockGetSharingLink,
33
35
  getFederatedShareLink: mockGetFederatedShareLink,
34
36
  getDocumentPermissions: jest.fn().mockReturnValue([]),
37
+ getOwner: jest.fn(),
38
+ getSharingById: jest.fn(),
35
39
  getRecipients: mockGetRecipients,
36
40
  hasSharedChild: mockHasSharedChild,
37
41
  hasSharedParent: mockHasSharedParent
@@ -63,6 +67,11 @@ jest.mock('../../hooks/usePendingRecipients', function () {
63
67
  usePendingRecipients: jest.fn()
64
68
  };
65
69
  });
70
+ jest.mock('../ShareByLink', function () {
71
+ return function () {
72
+ return /*#__PURE__*/React.createElement("button", null, "Copy link");
73
+ };
74
+ });
66
75
  var mockDocument = {
67
76
  _id: 'folder-123',
68
77
  name: 'My Test Folder',
@@ -109,10 +118,25 @@ describe('FederatedFolderModal', function () {
109
118
  beforeEach(function () {
110
119
  jest.clearAllMocks();
111
120
  mockGetSharingLink.mockReturnValue('https://example.com/share/abc123');
112
- mockGetFederatedShareLink.mockResolvedValue('https://example.com/share/federated-abc123');
121
+ mockGetFederatedShareLink.mockReturnValue('https://example.com/share/federated-abc123');
113
122
  mockGetRecipients.mockReturnValue([]);
114
123
  mockHasSharedChild.mockReturnValue(false);
115
124
  mockHasSharedParent.mockReturnValue(false);
125
+ mockShareByLink.mockResolvedValue({
126
+ data: {
127
+ _id: 'permission-id',
128
+ attributes: {
129
+ shortcodes: {
130
+ code: 'abc123'
131
+ }
132
+ }
133
+ }
134
+ });
135
+ Object.assign(navigator, {
136
+ clipboard: {
137
+ writeText: jest.fn()
138
+ }
139
+ });
116
140
  client = createTestClient();
117
141
  sharingContextValue = createSharingContextValue();
118
142
  usePendingRecipients.mockReturnValue({
@@ -190,18 +214,44 @@ describe('FederatedFolderModal', function () {
190
214
  }
191
215
  }, _callee3);
192
216
  })));
193
- it('should hide share by email when document is in federated shared folder received by current user', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4() {
194
- var _setup4, findByText, queryByText;
217
+ it('should show link access as soon as a link is generated', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4() {
218
+ var _setup4, findByText, queryByText, rerender;
195
219
 
196
220
  return _regeneratorRuntime.wrap(function _callee4$(_context4) {
197
221
  while (1) switch (_context4.prev = _context4.next) {
198
222
  case 0:
199
- _setup4 = setup({
223
+ mockGetSharingLink.mockReturnValue(null);
224
+ _setup4 = setup(), findByText = _setup4.findByText, queryByText = _setup4.queryByText, rerender = _setup4.rerender;
225
+ expect(queryByText('Anyone with the link')).toBe(null);
226
+ mockGetSharingLink.mockReturnValue('https://example.com/share/abc123');
227
+ rerender( /*#__PURE__*/React.createElement(AppLike, {
228
+ client: client,
229
+ sharingContextValue: sharingContextValue
230
+ }, /*#__PURE__*/React.createElement(FederatedFolderModal, {
231
+ document: mockDocument,
232
+ onClose: mockOnClose
233
+ })));
234
+ _context4.next = 7;
235
+ return findByText('Anyone with the link');
236
+
237
+ case 7:
238
+ case "end":
239
+ return _context4.stop();
240
+ }
241
+ }, _callee4);
242
+ })));
243
+ it('should hide share by email when document is in federated shared folder received by current user', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5() {
244
+ var _setup5, findByText, queryByText;
245
+
246
+ return _regeneratorRuntime.wrap(function _callee5$(_context5) {
247
+ while (1) switch (_context5.prev = _context5.next) {
248
+ case 0:
249
+ _setup5 = setup({
200
250
  document: _objectSpread(_objectSpread({}, mockDocument), {}, {
201
251
  driveId: 'federated-folder-id'
202
252
  })
203
- }), findByText = _setup4.findByText, queryByText = _setup4.queryByText;
204
- _context4.next = 3;
253
+ }), findByText = _setup5.findByText, queryByText = _setup5.queryByText;
254
+ _context5.next = 3;
205
255
  return findByText('Copy link');
206
256
 
207
257
  case 3:
@@ -210,34 +260,34 @@ describe('FederatedFolderModal', function () {
210
260
 
211
261
  case 5:
212
262
  case "end":
213
- return _context4.stop();
263
+ return _context5.stop();
214
264
  }
215
- }, _callee4);
265
+ }, _callee5);
216
266
  })));
217
- it('should show only by link message when document is in federated shared folder received by current user', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5() {
218
- var _setup5, findByText;
267
+ it('should show only by link message when document is in federated shared folder received by current user', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee6() {
268
+ var _setup6, findByText;
219
269
 
220
- return _regeneratorRuntime.wrap(function _callee5$(_context5) {
221
- while (1) switch (_context5.prev = _context5.next) {
270
+ return _regeneratorRuntime.wrap(function _callee6$(_context6) {
271
+ while (1) switch (_context6.prev = _context6.next) {
222
272
  case 0:
223
- _setup5 = setup({
273
+ _setup6 = setup({
224
274
  document: _objectSpread(_objectSpread({}, mockDocument), {}, {
225
275
  driveId: 'federated-folder-id',
226
276
  type: 'directory'
227
277
  })
228
- }), findByText = _setup5.findByText;
229
- _context5.next = 3;
278
+ }), findByText = _setup6.findByText;
279
+ _context6.next = 3;
230
280
  return findByText('This folder can only be shared by link, because');
231
281
 
232
282
  case 3:
233
- _context5.next = 5;
283
+ _context6.next = 5;
234
284
  return findByText('it has a shared parent');
235
285
 
236
286
  case 5:
237
287
  case "end":
238
- return _context5.stop();
288
+ return _context6.stop();
239
289
  }
240
- }, _callee5);
290
+ }, _callee6);
241
291
  })));
242
292
  });
243
293
  describe('onSend callback', function () {
@@ -245,21 +295,21 @@ describe('FederatedFolderModal', function () {
245
295
  name: 'Alice',
246
296
  email: 'alice@example.com'
247
297
  };
248
- it('should call onClose and skip share when there are no pending recipients', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee6() {
249
- var _setup6, findByText, sendButton;
298
+ it('should call onClose and skip share when there are no pending recipients', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee7() {
299
+ var _setup7, findByText, sendButton;
250
300
 
251
- return _regeneratorRuntime.wrap(function _callee6$(_context6) {
252
- while (1) switch (_context6.prev = _context6.next) {
301
+ return _regeneratorRuntime.wrap(function _callee7$(_context7) {
302
+ while (1) switch (_context7.prev = _context7.next) {
253
303
  case 0:
254
304
  // pendingRecipients defaults to [] via beforeEach
255
- _setup6 = setup(), findByText = _setup6.findByText;
256
- _context6.next = 3;
305
+ _setup7 = setup(), findByText = _setup7.findByText;
306
+ _context7.next = 3;
257
307
  return findByText('Done');
258
308
 
259
309
  case 3:
260
- sendButton = _context6.sent;
310
+ sendButton = _context7.sent;
261
311
  fireEvent.click(sendButton);
262
- _context6.next = 7;
312
+ _context7.next = 7;
263
313
  return waitFor(function () {
264
314
  expect(mockOnClose).toHaveBeenCalled();
265
315
  expect(mockShare).not.toHaveBeenCalled();
@@ -267,15 +317,15 @@ describe('FederatedFolderModal', function () {
267
317
 
268
318
  case 7:
269
319
  case "end":
270
- return _context6.stop();
320
+ return _context7.stop();
271
321
  }
272
- }, _callee6);
322
+ }, _callee7);
273
323
  })));
274
- it('should call share with correct parameters', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee7() {
275
- var _setup7, findByText, sendButton;
324
+ it('should call share with correct parameters', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee8() {
325
+ var _setup8, findByText, sendButton;
276
326
 
277
- return _regeneratorRuntime.wrap(function _callee7$(_context7) {
278
- while (1) switch (_context7.prev = _context7.next) {
327
+ return _regeneratorRuntime.wrap(function _callee8$(_context8) {
328
+ while (1) switch (_context8.prev = _context8.next) {
279
329
  case 0:
280
330
  usePendingRecipients.mockReturnValue({
281
331
  pendingRecipients: [pendingRecipient],
@@ -285,14 +335,14 @@ describe('FederatedFolderModal', function () {
285
335
  });
286
336
  getOrCreateFromArray.mockResolvedValue([pendingRecipient]);
287
337
  mockShare.mockResolvedValueOnce({});
288
- _setup7 = setup(), findByText = _setup7.findByText;
289
- _context7.next = 6;
338
+ _setup8 = setup(), findByText = _setup8.findByText;
339
+ _context8.next = 6;
290
340
  return findByText('Done');
291
341
 
292
342
  case 6:
293
- sendButton = _context7.sent;
343
+ sendButton = _context8.sent;
294
344
  fireEvent.click(sendButton);
295
- _context7.next = 10;
345
+ _context8.next = 10;
296
346
  return waitFor(function () {
297
347
  expect(mockShare).toHaveBeenCalledWith({
298
348
  description: 'My Test Folder',
@@ -306,15 +356,15 @@ describe('FederatedFolderModal', function () {
306
356
 
307
357
  case 10:
308
358
  case "end":
309
- return _context7.stop();
359
+ return _context8.stop();
310
360
  }
311
- }, _callee7);
361
+ }, _callee8);
312
362
  })));
313
- it('should show success alert and close modal on successful share', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee8() {
314
- var _setup8, findByText, sendButton;
363
+ it('should show success alert and close modal on successful share', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee9() {
364
+ var _setup9, findByText, sendButton;
315
365
 
316
- return _regeneratorRuntime.wrap(function _callee8$(_context8) {
317
- while (1) switch (_context8.prev = _context8.next) {
366
+ return _regeneratorRuntime.wrap(function _callee9$(_context9) {
367
+ while (1) switch (_context9.prev = _context9.next) {
318
368
  case 0:
319
369
  usePendingRecipients.mockReturnValue({
320
370
  pendingRecipients: [pendingRecipient],
@@ -324,14 +374,14 @@ describe('FederatedFolderModal', function () {
324
374
  });
325
375
  getOrCreateFromArray.mockResolvedValue([pendingRecipient]);
326
376
  mockShare.mockResolvedValueOnce({});
327
- _setup8 = setup(), findByText = _setup8.findByText;
328
- _context8.next = 6;
377
+ _setup9 = setup(), findByText = _setup9.findByText;
378
+ _context9.next = 6;
329
379
  return findByText('Done');
330
380
 
331
381
  case 6:
332
- sendButton = _context8.sent;
382
+ sendButton = _context9.sent;
333
383
  fireEvent.click(sendButton);
334
- _context8.next = 10;
384
+ _context9.next = 10;
335
385
  return waitFor(function () {
336
386
  expect(mockShowAlert).toHaveBeenCalledWith({
337
387
  message: 'Folder has been shared',
@@ -343,15 +393,15 @@ describe('FederatedFolderModal', function () {
343
393
 
344
394
  case 10:
345
395
  case "end":
346
- return _context8.stop();
396
+ return _context9.stop();
347
397
  }
348
- }, _callee8);
398
+ }, _callee9);
349
399
  })));
350
- it('should show error alert when share fails', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee9() {
351
- var _setup9, findByText, sendButton;
400
+ it('should show error alert when share fails', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee10() {
401
+ var _setup10, findByText, sendButton;
352
402
 
353
- return _regeneratorRuntime.wrap(function _callee9$(_context9) {
354
- while (1) switch (_context9.prev = _context9.next) {
403
+ return _regeneratorRuntime.wrap(function _callee10$(_context10) {
404
+ while (1) switch (_context10.prev = _context10.next) {
355
405
  case 0:
356
406
  usePendingRecipients.mockReturnValue({
357
407
  pendingRecipients: [pendingRecipient],
@@ -361,14 +411,14 @@ describe('FederatedFolderModal', function () {
361
411
  });
362
412
  getOrCreateFromArray.mockResolvedValue([pendingRecipient]);
363
413
  mockShare.mockRejectedValueOnce(new Error('Share failed'));
364
- _setup9 = setup(), findByText = _setup9.findByText;
365
- _context9.next = 6;
414
+ _setup10 = setup(), findByText = _setup10.findByText;
415
+ _context10.next = 6;
366
416
  return findByText('Done');
367
417
 
368
418
  case 6:
369
- sendButton = _context9.sent;
419
+ sendButton = _context10.sent;
370
420
  fireEvent.click(sendButton);
371
- _context9.next = 10;
421
+ _context10.next = 10;
372
422
  return waitFor(function () {
373
423
  expect(mockShowAlert).toHaveBeenCalledWith({
374
424
  message: 'Error while sharing folder',
@@ -380,15 +430,15 @@ describe('FederatedFolderModal', function () {
380
430
 
381
431
  case 10:
382
432
  case "end":
383
- return _context9.stop();
433
+ return _context10.stop();
384
434
  }
385
- }, _callee9);
435
+ }, _callee10);
386
436
  })));
387
- it('should show progress icon on Done button while share is in progress', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee11() {
388
- var resolveShare, _setup10, findByText, getByRole, queryByRole, sendButton;
437
+ it('should show progress icon on Done button while share is in progress', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee12() {
438
+ var resolveShare, _setup11, findByText, getByRole, queryByRole, sendButton;
389
439
 
390
- return _regeneratorRuntime.wrap(function _callee11$(_context11) {
391
- while (1) switch (_context11.prev = _context11.next) {
440
+ return _regeneratorRuntime.wrap(function _callee12$(_context12) {
441
+ while (1) switch (_context12.prev = _context12.next) {
392
442
  case 0:
393
443
  usePendingRecipients.mockReturnValue({
394
444
  pendingRecipients: [pendingRecipient],
@@ -400,14 +450,14 @@ describe('FederatedFolderModal', function () {
400
450
  mockShare.mockReturnValueOnce(new Promise(function (resolve) {
401
451
  resolveShare = resolve;
402
452
  }));
403
- _setup10 = setup(), findByText = _setup10.findByText, getByRole = _setup10.getByRole, queryByRole = _setup10.queryByRole;
404
- _context11.next = 6;
453
+ _setup11 = setup(), findByText = _setup11.findByText, getByRole = _setup11.getByRole, queryByRole = _setup11.queryByRole;
454
+ _context12.next = 6;
405
455
  return findByText('Done');
406
456
 
407
457
  case 6:
408
- sendButton = _context11.sent;
458
+ sendButton = _context12.sent;
409
459
  fireEvent.click(sendButton);
410
- _context11.next = 10;
460
+ _context12.next = 10;
411
461
  return waitFor(function () {
412
462
  expect(getByRole('button', {
413
463
  name: 'Done'
@@ -416,56 +466,56 @@ describe('FederatedFolderModal', function () {
416
466
  });
417
467
 
418
468
  case 10:
419
- _context11.next = 12;
420
- return act( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee10() {
421
- return _regeneratorRuntime.wrap(function _callee10$(_context10) {
422
- while (1) switch (_context10.prev = _context10.next) {
469
+ _context12.next = 12;
470
+ return act( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee11() {
471
+ return _regeneratorRuntime.wrap(function _callee11$(_context11) {
472
+ while (1) switch (_context11.prev = _context11.next) {
423
473
  case 0:
424
474
  resolveShare({});
425
475
 
426
476
  case 1:
427
477
  case "end":
428
- return _context10.stop();
478
+ return _context11.stop();
429
479
  }
430
- }, _callee10);
480
+ }, _callee11);
431
481
  })));
432
482
 
433
483
  case 12:
434
484
  case "end":
435
- return _context11.stop();
485
+ return _context12.stop();
436
486
  }
437
- }, _callee11);
487
+ }, _callee12);
438
488
  })));
439
489
  });
440
490
  describe('onClose callback', function () {
441
- it('should call onClose when close button is clicked', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee12() {
442
- var _setup11, findByRole, closeButton;
491
+ it('should call onClose when close button is clicked', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee13() {
492
+ var _setup12, findByRole, closeButton;
443
493
 
444
- return _regeneratorRuntime.wrap(function _callee12$(_context12) {
445
- while (1) switch (_context12.prev = _context12.next) {
494
+ return _regeneratorRuntime.wrap(function _callee13$(_context13) {
495
+ while (1) switch (_context13.prev = _context13.next) {
446
496
  case 0:
447
- _setup11 = setup(), findByRole = _setup11.findByRole;
448
- _context12.next = 3;
497
+ _setup12 = setup(), findByRole = _setup12.findByRole;
498
+ _context13.next = 3;
449
499
  return findByRole('button', {
450
500
  name: /close/i
451
501
  });
452
502
 
453
503
  case 3:
454
- closeButton = _context12.sent;
504
+ closeButton = _context13.sent;
455
505
  fireEvent.click(closeButton);
456
506
  expect(mockOnClose).toHaveBeenCalled();
457
507
 
458
508
  case 6:
459
509
  case "end":
460
- return _context12.stop();
510
+ return _context13.stop();
461
511
  }
462
- }, _callee12);
512
+ }, _callee13);
463
513
  })));
464
- it('should ask confirmation when close button is clicked with pending recipients', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee13() {
465
- var _setup12, getByRole, findByRole, getByText, closeButton;
514
+ it('should ask confirmation when close button is clicked with pending recipients', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee14() {
515
+ var _setup13, getByRole, findByRole, getByText, closeButton;
466
516
 
467
- return _regeneratorRuntime.wrap(function _callee13$(_context13) {
468
- while (1) switch (_context13.prev = _context13.next) {
517
+ return _regeneratorRuntime.wrap(function _callee14$(_context14) {
518
+ while (1) switch (_context14.prev = _context14.next) {
469
519
  case 0:
470
520
  usePendingRecipients.mockReturnValue({
471
521
  pendingRecipients: [{
@@ -476,14 +526,14 @@ describe('FederatedFolderModal', function () {
476
526
  selectedOption: 'readWrite',
477
527
  setSelectedOption: jest.fn()
478
528
  });
479
- _setup12 = setup(), getByRole = _setup12.getByRole, findByRole = _setup12.findByRole, getByText = _setup12.getByText;
480
- _context13.next = 4;
529
+ _setup13 = setup(), getByRole = _setup13.getByRole, findByRole = _setup13.findByRole, getByText = _setup13.getByText;
530
+ _context14.next = 4;
481
531
  return findByRole('button', {
482
532
  name: /close/i
483
533
  });
484
534
 
485
535
  case 4:
486
- closeButton = _context13.sent;
536
+ closeButton = _context14.sent;
487
537
  fireEvent.click(closeButton);
488
538
  expect(getByText("Discard the changes you haven't saved?")).toBeTruthy();
489
539
  expect(mockOnClose).not.toHaveBeenCalled();
@@ -494,15 +544,15 @@ describe('FederatedFolderModal', function () {
494
544
 
495
545
  case 10:
496
546
  case "end":
497
- return _context13.stop();
547
+ return _context14.stop();
498
548
  }
499
- }, _callee13);
549
+ }, _callee14);
500
550
  })));
501
- it('should close after discard confirmation', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee14() {
502
- var _setup13, getByRole, findByRole, getByText, closeButton;
551
+ it('should close after discard confirmation', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee15() {
552
+ var _setup14, getByRole, findByRole, getByText, closeButton;
503
553
 
504
- return _regeneratorRuntime.wrap(function _callee14$(_context14) {
505
- while (1) switch (_context14.prev = _context14.next) {
554
+ return _regeneratorRuntime.wrap(function _callee15$(_context15) {
555
+ while (1) switch (_context15.prev = _context15.next) {
506
556
  case 0:
507
557
  usePendingRecipients.mockReturnValue({
508
558
  pendingRecipients: [{
@@ -513,14 +563,14 @@ describe('FederatedFolderModal', function () {
513
563
  selectedOption: 'readWrite',
514
564
  setSelectedOption: jest.fn()
515
565
  });
516
- _setup13 = setup(), getByRole = _setup13.getByRole, findByRole = _setup13.findByRole, getByText = _setup13.getByText;
517
- _context14.next = 4;
566
+ _setup14 = setup(), getByRole = _setup14.getByRole, findByRole = _setup14.findByRole, getByText = _setup14.getByText;
567
+ _context15.next = 4;
518
568
  return findByRole('button', {
519
569
  name: /close/i
520
570
  });
521
571
 
522
572
  case 4:
523
- closeButton = _context14.sent;
573
+ closeButton = _context15.sent;
524
574
  fireEvent.click(closeButton);
525
575
  expect(getByText("Discard the changes you haven't saved?")).toBeTruthy();
526
576
  fireEvent.click(getByRole('button', {
@@ -530,9 +580,9 @@ describe('FederatedFolderModal', function () {
530
580
 
531
581
  case 9:
532
582
  case "end":
533
- return _context14.stop();
583
+ return _context15.stop();
534
584
  }
535
- }, _callee14);
585
+ }, _callee15);
536
586
  })));
537
587
  });
538
588
  });
@@ -24,6 +24,7 @@ import { getOrCreateFromArray } from '../helpers/contacts';
24
24
  import { hasReachRecipientsLimit } from '../helpers/recipients';
25
25
  import { getErrorMessage, getSuccessMessage } from '../helpers/share';
26
26
  import { usePendingRecipients } from '../hooks/usePendingRecipients';
27
+ import { useSharingContext } from '../hooks/useSharingContext';
27
28
  var styles = {
28
29
  "share-modal-content": "share__share-modal-content___1iqEq",
29
30
  "coz-form": "share__coz-form___1ICST",
@@ -144,6 +145,9 @@ var ShareDialogCozyToCozy = function ShareDialogCozyToCozy(_ref3) {
144
145
  var _useAlert = useAlert(),
145
146
  showAlert = _useAlert.showAlert;
146
147
 
148
+ var _useSharingContext = useSharingContext(),
149
+ getSharingLink = _useSharingContext.getSharingLink;
150
+
147
151
  var _usePendingRecipients = usePendingRecipients(),
148
152
  pendingRecipients = _usePendingRecipients.pendingRecipients,
149
153
  setPendingRecipients = _usePendingRecipients.setPendingRecipients,
@@ -160,6 +164,7 @@ var ShareDialogCozyToCozy = function ShareDialogCozyToCozy(_ref3) {
160
164
  showRecipientsLimit = _useState4[0],
161
165
  setShowRecipientsLimit = _useState4[1];
162
166
 
167
+ var displayedLink = getSharingLink(document._id || document.id);
163
168
  var handleShare = useCallback( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
164
169
  var contacts, readWriteRecipients, readOnlyRecipients;
165
170
  return _regeneratorRuntime.wrap(function _callee$(_context) {
@@ -254,6 +259,7 @@ var ShareDialogCozyToCozy = function ShareDialogCozyToCozy(_ref3) {
254
259
  return Actions;
255
260
  }, [handleShare, submitting, t, documentType, showShareByLink, showGenerateLinkButton, autoOpenShareRestriction]);
256
261
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ShareDialogTwoStepsConfirmationContainer, _extends({}, props, {
262
+ link: displayedLink,
257
263
  documentType: documentType,
258
264
  document: document,
259
265
  recipients: recipients,
@@ -33,6 +33,9 @@ jest.mock('../hooks/useSharingContext', function () {
33
33
  return {
34
34
  useSharingContext: function useSharingContext() {
35
35
  return {
36
+ getSharingLink: jest.fn(function () {
37
+ return null;
38
+ }),
36
39
  updateSharingMemberType: jest.fn()
37
40
  };
38
41
  }
@@ -7,6 +7,7 @@ import { useI18n } from 'twake-i18n';
7
7
  import AntivirusAlert from './AntivirusAlert';
8
8
  import { default as DumbShareByLink } from './ShareByLink';
9
9
  import WhoHasAccess from './WhoHasAccess';
10
+ import { useSharingContext } from '../hooks/useSharingContext';
10
11
 
11
12
  var ShareDialogOnlyByLink = function ShareDialogOnlyByLink(_ref) {
12
13
  var isOwner = _ref.isOwner,
@@ -15,7 +16,6 @@ var ShareDialogOnlyByLink = function ShareDialogOnlyByLink(_ref) {
15
16
  recipients = _ref.recipients,
16
17
  document = _ref.document,
17
18
  documentType = _ref.documentType,
18
- link = _ref.link,
19
19
  onClose = _ref.onClose,
20
20
  permissions = _ref.permissions,
21
21
  showGenerateLinkButton = _ref.showGenerateLinkButton,
@@ -27,6 +27,10 @@ var ShareDialogOnlyByLink = function ShareDialogOnlyByLink(_ref) {
27
27
  var _useBreakpoints = useBreakpoints(),
28
28
  isMobile = _useBreakpoints.isMobile;
29
29
 
30
+ var _useSharingContext = useSharingContext(),
31
+ getSharingLink = _useSharingContext.getSharingLink;
32
+
33
+ var displayedLink = getSharingLink(document._id || document.id);
30
34
  return /*#__PURE__*/React.createElement(Dialog, {
31
35
  disableGutters: true,
32
36
  disableEnforceFocus: true,
@@ -47,12 +51,12 @@ var ShareDialogOnlyByLink = function ShareDialogOnlyByLink(_ref) {
47
51
  onRevoke: onRevoke,
48
52
  onRevokeSelf: onRevokeSelf,
49
53
  recipients: recipients,
50
- link: link,
54
+ link: displayedLink,
51
55
  permissions: permissions
52
56
  }), /*#__PURE__*/React.createElement("div", {
53
57
  className: cx('u-mt-half', isMobile ? 'u-ph-1 u-mb-1' : 'u-ph-2 u-mb-1-half')
54
58
  }, /*#__PURE__*/React.createElement(DumbShareByLink, {
55
- link: link,
59
+ link: displayedLink,
56
60
  document: document,
57
61
  documentType: documentType,
58
62
  showGenerateLinkButton: showGenerateLinkButton,
@@ -0,0 +1,75 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import React from 'react';
3
+ import ShareDialogOnlyByLink from './ShareDialogOnlyByLink';
4
+ import AppLike from '../../test/AppLike';
5
+ var mockSharingLink = null;
6
+ jest.mock('./ShareByLink', function () {
7
+ return function () {
8
+ return /*#__PURE__*/React.createElement("button", null, "Copy link");
9
+ };
10
+ });
11
+ jest.mock('../hooks/useSharingContext', function () {
12
+ return {
13
+ useSharingContext: function useSharingContext() {
14
+ return {
15
+ documentType: 'Files',
16
+ getDocumentPermissions: function getDocumentPermissions() {
17
+ return [];
18
+ },
19
+ getSharingLink: function getSharingLink() {
20
+ return mockSharingLink;
21
+ },
22
+ revokeSharingLink: jest.fn(),
23
+ updateDocumentPermissions: jest.fn()
24
+ };
25
+ }
26
+ };
27
+ });
28
+ describe('ShareDialogOnlyByLink', function () {
29
+ beforeEach(function () {
30
+ mockSharingLink = null;
31
+ });
32
+
33
+ var renderModal = function renderModal() {
34
+ return /*#__PURE__*/React.createElement(AppLike, null, /*#__PURE__*/React.createElement(ShareDialogOnlyByLink, {
35
+ document: {
36
+ _id: 'file-id',
37
+ attributes: {
38
+ name: 'file.txt'
39
+ }
40
+ },
41
+ documentType: "Files",
42
+ isOwner: true,
43
+ onClose: jest.fn(),
44
+ onRevoke: jest.fn(),
45
+ onRevokeSelf: jest.fn(),
46
+ permissions: [],
47
+ recipients: []
48
+ }));
49
+ };
50
+
51
+ var setup = function setup() {
52
+ return render(renderModal());
53
+ };
54
+
55
+ it('shows link access as soon as a link is generated', function () {
56
+ var _setup = setup(),
57
+ rerender = _setup.rerender;
58
+
59
+ expect(screen.queryByText('Anyone with the link')).toBe(null);
60
+ mockSharingLink = 'https://example.com/public';
61
+ rerender(renderModal());
62
+ expect(screen.queryByText('Anyone with the link')).toBeInTheDocument();
63
+ });
64
+ it('hides link access as soon as a link is revoked', function () {
65
+ mockSharingLink = 'https://example.com/public';
66
+
67
+ var _setup2 = setup(),
68
+ rerender = _setup2.rerender;
69
+
70
+ expect(screen.queryByText('Anyone with the link')).toBeInTheDocument();
71
+ mockSharingLink = null;
72
+ rerender(renderModal());
73
+ expect(screen.queryByText('Anyone with the link')).toBe(null);
74
+ });
75
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-sharing",
3
- "version": "33.2.0",
3
+ "version": "33.2.1",
4
4
  "description": "Provides sharing login for React applications.",
5
5
  "main": "dist/index.js",
6
6
  "author": "Cozy",
@@ -83,5 +83,5 @@
83
83
  "sideEffects": [
84
84
  "*.css"
85
85
  ],
86
- "gitHead": "aeaf0cb5bb862ce8908d78862ca79e8e749982f2"
86
+ "gitHead": "107c14be0fb0677515062187e3fdadeab4e00350"
87
87
  }
@@ -1,5 +1,5 @@
1
1
  import PropTypes from 'prop-types'
2
- import React, { useCallback, useEffect, useMemo, useState } from 'react'
2
+ import React, { useCallback, useState } from 'react'
3
3
 
4
4
  import { useClient } from 'cozy-client'
5
5
  import Button from 'cozy-ui/transpiled/react/Buttons'
@@ -33,9 +33,6 @@ const FederatedFolderModalContent = ({
33
33
  share,
34
34
  getSharingLink,
35
35
  getFederatedShareLink,
36
- getDocumentPermissions,
37
- getOwner,
38
- getSharingById,
39
36
  getRecipients,
40
37
  hasSharedChild,
41
38
  hasSharedParent,
@@ -44,7 +41,6 @@ const FederatedFolderModalContent = ({
44
41
  const { showAlert } = useAlert()
45
42
  const { showConfirmDialog, closeConfirmDialog } = useConfirmDialog()
46
43
 
47
- const [sharingLink, setSharingLink] = useState(null)
48
44
  const {
49
45
  pendingRecipients,
50
46
  setPendingRecipients,
@@ -98,34 +94,6 @@ const FederatedFolderModalContent = ({
98
94
  t
99
95
  ])
100
96
 
101
- const documentPermissions = useMemo(
102
- () =>
103
- existingDocument ? getDocumentPermissions(existingDocument._id) : [],
104
- [existingDocument, getDocumentPermissions]
105
- )
106
-
107
- useEffect(() => {
108
- const fetchSharingLink = async () => {
109
- if (!existingDocument) return
110
-
111
- if (existingDocument.driveId) {
112
- const link = await getFederatedShareLink(existingDocument)
113
- setSharingLink(link)
114
- } else {
115
- setSharingLink(getSharingLink(existingDocument._id))
116
- }
117
- }
118
-
119
- fetchSharingLink()
120
- }, [
121
- existingDocument,
122
- documentPermissions,
123
- getFederatedShareLink,
124
- getSharingLink,
125
- getOwner,
126
- getSharingById
127
- ])
128
-
129
97
  const folderName = existingDocument?.name || ''
130
98
  const documentPath = existingDocument?.path
131
99
  const hasParentRestriction = Boolean(
@@ -185,6 +153,9 @@ const FederatedFolderModalContent = ({
185
153
  const existingRecipients = existingDocument
186
154
  ? getRecipients(existingDocument._id)
187
155
  : []
156
+ const displayedLink = existingDocument?.driveId
157
+ ? getFederatedShareLink(existingDocument)
158
+ : getSharingLink(existingDocument?._id)
188
159
 
189
160
  const modalTitle = t('FederatedFolder.shareTitle', { name: folderName })
190
161
 
@@ -252,14 +223,14 @@ const FederatedFolderModalContent = ({
252
223
  documentType="Files"
253
224
  className="u-w-100"
254
225
  onRevoke={revoke}
255
- link={sharingLink}
226
+ link={displayedLink}
256
227
  />
257
228
  </div>
258
229
  }
259
230
  actions={
260
231
  <>
261
232
  <ShareByLink
262
- link={sharingLink}
233
+ link={displayedLink}
263
234
  document={isSending ? frozenDoc : existingDocument}
264
235
  documentType="Files"
265
236
  showGenerateLinkButton={showGenerateLinkButton}
@@ -9,6 +9,7 @@ import { usePendingRecipients } from '../../hooks/usePendingRecipients'
9
9
  import AppLike from '../SharingBanner/test/AppLike'
10
10
 
11
11
  const mockShare = jest.fn()
12
+ const mockShareByLink = jest.fn()
12
13
  const mockRevoke = jest.fn()
13
14
  const mockGetSharingLink = jest.fn()
14
15
  const mockGetFederatedShareLink = jest.fn()
@@ -21,10 +22,13 @@ const mockOnClose = jest.fn()
21
22
  jest.mock('../../hooks/useSharingContext', () => ({
22
23
  useSharingContext: () => ({
23
24
  share: mockShare,
25
+ shareByLink: mockShareByLink,
24
26
  revoke: mockRevoke,
25
27
  getSharingLink: mockGetSharingLink,
26
28
  getFederatedShareLink: mockGetFederatedShareLink,
27
29
  getDocumentPermissions: jest.fn().mockReturnValue([]),
30
+ getOwner: jest.fn(),
31
+ getSharingById: jest.fn(),
28
32
  getRecipients: mockGetRecipients,
29
33
  hasSharedChild: mockHasSharedChild,
30
34
  hasSharedParent: mockHasSharedParent
@@ -47,6 +51,8 @@ jest.mock('../../hooks/usePendingRecipients', () => ({
47
51
  usePendingRecipients: jest.fn()
48
52
  }))
49
53
 
54
+ jest.mock('../ShareByLink', () => () => <button>Copy link</button>)
55
+
50
56
  const mockDocument = {
51
57
  _id: 'folder-123',
52
58
  name: 'My Test Folder',
@@ -90,12 +96,25 @@ describe('FederatedFolderModal', () => {
90
96
  beforeEach(() => {
91
97
  jest.clearAllMocks()
92
98
  mockGetSharingLink.mockReturnValue('https://example.com/share/abc123')
93
- mockGetFederatedShareLink.mockResolvedValue(
99
+ mockGetFederatedShareLink.mockReturnValue(
94
100
  'https://example.com/share/federated-abc123'
95
101
  )
96
102
  mockGetRecipients.mockReturnValue([])
97
103
  mockHasSharedChild.mockReturnValue(false)
98
104
  mockHasSharedParent.mockReturnValue(false)
105
+ mockShareByLink.mockResolvedValue({
106
+ data: {
107
+ _id: 'permission-id',
108
+ attributes: {
109
+ shortcodes: { code: 'abc123' }
110
+ }
111
+ }
112
+ })
113
+ Object.assign(navigator, {
114
+ clipboard: {
115
+ writeText: jest.fn()
116
+ }
117
+ })
99
118
  client = createTestClient()
100
119
  sharingContextValue = createSharingContextValue()
101
120
  usePendingRecipients.mockReturnValue({
@@ -139,6 +158,22 @@ describe('FederatedFolderModal', () => {
139
158
  await findByText('Copy link')
140
159
  })
141
160
 
161
+ it('should show link access as soon as a link is generated', async () => {
162
+ mockGetSharingLink.mockReturnValue(null)
163
+ const { findByText, queryByText, rerender } = setup()
164
+
165
+ expect(queryByText('Anyone with the link')).toBe(null)
166
+
167
+ mockGetSharingLink.mockReturnValue('https://example.com/share/abc123')
168
+ rerender(
169
+ <AppLike client={client} sharingContextValue={sharingContextValue}>
170
+ <FederatedFolderModal document={mockDocument} onClose={mockOnClose} />
171
+ </AppLike>
172
+ )
173
+
174
+ await findByText('Anyone with the link')
175
+ })
176
+
142
177
  it('should hide share by email when document is in federated shared folder received by current user', async () => {
143
178
  const { findByText, queryByText } = setup({
144
179
  document: { ...mockDocument, driveId: 'federated-folder-id' }
@@ -19,6 +19,7 @@ import { getOrCreateFromArray } from '../helpers/contacts'
19
19
  import { hasReachRecipientsLimit } from '../helpers/recipients'
20
20
  import { getErrorMessage, getSuccessMessage } from '../helpers/share'
21
21
  import { usePendingRecipients } from '../hooks/usePendingRecipients'
22
+ import { useSharingContext } from '../hooks/useSharingContext'
22
23
  import styles from '../styles/share.styl'
23
24
 
24
25
  const SharingContent = ({
@@ -126,6 +127,7 @@ const ShareDialogCozyToCozy = ({
126
127
  const { t } = useI18n()
127
128
  const client = useClient()
128
129
  const { showAlert } = useAlert()
130
+ const { getSharingLink } = useSharingContext()
129
131
 
130
132
  const {
131
133
  pendingRecipients,
@@ -136,6 +138,7 @@ const ShareDialogCozyToCozy = ({
136
138
 
137
139
  const [submitting, setSubmitting] = useState(false)
138
140
  const [showRecipientsLimit, setShowRecipientsLimit] = useState(false)
141
+ const displayedLink = getSharingLink(document._id || document.id)
139
142
 
140
143
  const handleShare = useCallback(async () => {
141
144
  if (pendingRecipients.length === 0) {
@@ -238,6 +241,7 @@ const ShareDialogCozyToCozy = ({
238
241
  <>
239
242
  <ShareDialogTwoStepsConfirmationContainer
240
243
  {...props}
244
+ link={displayedLink}
241
245
  documentType={documentType}
242
246
  document={document}
243
247
  recipients={recipients}
@@ -16,6 +16,7 @@ jest.mock('cozy-client', () => ({
16
16
 
17
17
  jest.mock('../hooks/useSharingContext', () => ({
18
18
  useSharingContext: () => ({
19
+ getSharingLink: jest.fn(() => null),
19
20
  updateSharingMemberType: jest.fn()
20
21
  })
21
22
  }))
@@ -9,6 +9,7 @@ import { useI18n } from 'twake-i18n'
9
9
  import AntivirusAlert from './AntivirusAlert'
10
10
  import { default as DumbShareByLink } from './ShareByLink'
11
11
  import WhoHasAccess from './WhoHasAccess'
12
+ import { useSharingContext } from '../hooks/useSharingContext'
12
13
 
13
14
  const ShareDialogOnlyByLink = ({
14
15
  isOwner,
@@ -17,7 +18,6 @@ const ShareDialogOnlyByLink = ({
17
18
  recipients,
18
19
  document,
19
20
  documentType,
20
- link,
21
21
  onClose,
22
22
  permissions,
23
23
  showGenerateLinkButton,
@@ -25,6 +25,8 @@ const ShareDialogOnlyByLink = ({
25
25
  }) => {
26
26
  const { t } = useI18n()
27
27
  const { isMobile } = useBreakpoints()
28
+ const { getSharingLink } = useSharingContext()
29
+ const displayedLink = getSharingLink(document._id || document.id)
28
30
 
29
31
  return (
30
32
  <Dialog
@@ -51,7 +53,7 @@ const ShareDialogOnlyByLink = ({
51
53
  onRevoke={onRevoke}
52
54
  onRevokeSelf={onRevokeSelf}
53
55
  recipients={recipients}
54
- link={link}
56
+ link={displayedLink}
55
57
  permissions={permissions}
56
58
  />
57
59
  <div
@@ -61,7 +63,7 @@ const ShareDialogOnlyByLink = ({
61
63
  )}
62
64
  >
63
65
  <DumbShareByLink
64
- link={link}
66
+ link={displayedLink}
65
67
  document={document}
66
68
  documentType={documentType}
67
69
  showGenerateLinkButton={showGenerateLinkButton}
@@ -0,0 +1,65 @@
1
+ import { render, screen } from '@testing-library/react'
2
+ import React from 'react'
3
+
4
+ import ShareDialogOnlyByLink from './ShareDialogOnlyByLink'
5
+ import AppLike from '../../test/AppLike'
6
+
7
+ let mockSharingLink = null
8
+
9
+ jest.mock('./ShareByLink', () => () => <button>Copy link</button>)
10
+
11
+ jest.mock('../hooks/useSharingContext', () => ({
12
+ useSharingContext: () => ({
13
+ documentType: 'Files',
14
+ getDocumentPermissions: () => [],
15
+ getSharingLink: () => mockSharingLink,
16
+ revokeSharingLink: jest.fn(),
17
+ updateDocumentPermissions: jest.fn()
18
+ })
19
+ }))
20
+
21
+ describe('ShareDialogOnlyByLink', () => {
22
+ beforeEach(() => {
23
+ mockSharingLink = null
24
+ })
25
+
26
+ const renderModal = () => (
27
+ <AppLike>
28
+ <ShareDialogOnlyByLink
29
+ document={{ _id: 'file-id', attributes: { name: 'file.txt' } }}
30
+ documentType="Files"
31
+ isOwner
32
+ onClose={jest.fn()}
33
+ onRevoke={jest.fn()}
34
+ onRevokeSelf={jest.fn()}
35
+ permissions={[]}
36
+ recipients={[]}
37
+ />
38
+ </AppLike>
39
+ )
40
+
41
+ const setup = () => render(renderModal())
42
+
43
+ it('shows link access as soon as a link is generated', () => {
44
+ const { rerender } = setup()
45
+
46
+ expect(screen.queryByText('Anyone with the link')).toBe(null)
47
+
48
+ mockSharingLink = 'https://example.com/public'
49
+ rerender(renderModal())
50
+
51
+ expect(screen.queryByText('Anyone with the link')).toBeInTheDocument()
52
+ })
53
+
54
+ it('hides link access as soon as a link is revoked', () => {
55
+ mockSharingLink = 'https://example.com/public'
56
+ const { rerender } = setup()
57
+
58
+ expect(screen.queryByText('Anyone with the link')).toBeInTheDocument()
59
+
60
+ mockSharingLink = null
61
+ rerender(renderModal())
62
+
63
+ expect(screen.queryByText('Anyone with the link')).toBe(null)
64
+ })
65
+ })