cozy-sharing 30.1.1 → 30.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 (28) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/components/FederatedFolder/DumbFederatedFolderModal.js +9 -3
  3. package/dist/components/FederatedFolder/FederatedFolderModal.js +9 -3
  4. package/dist/components/ShareByLink.js +115 -54
  5. package/dist/components/ShareDialogCozyToCozy.js +18 -3
  6. package/dist/components/ShareDialogOnlyByLink.js +6 -2
  7. package/dist/components/ShareModal/ShareModal.js +6 -5
  8. package/dist/components/ShareModal.js +13 -3
  9. package/dist/components/ShareModal.spec.js +4 -4
  10. package/dist/components/ShareRestrictionModal/ShareRestrictionModal.js +51 -64
  11. package/dist/components/ShareRestrictionModal/helpers.js +61 -12
  12. package/dist/components/SharedDrive/DumbSharedDriveModal.js +9 -3
  13. package/dist/components/SharedDrive/SharedDriveEditModal.js +14 -3
  14. package/dist/components/SharedFolder/DumbBatchSharedFolderModal.js +12 -4
  15. package/package.json +2 -2
  16. package/src/components/FederatedFolder/DumbFederatedFolderModal.jsx +8 -2
  17. package/src/components/FederatedFolder/FederatedFolderModal.jsx +11 -2
  18. package/src/components/ShareByLink.jsx +66 -25
  19. package/src/components/ShareDialogCozyToCozy.jsx +17 -2
  20. package/src/components/ShareDialogOnlyByLink.jsx +5 -1
  21. package/src/components/ShareModal/ShareModal.jsx +4 -2
  22. package/src/components/ShareModal.jsx +10 -0
  23. package/src/components/ShareModal.spec.js +7 -3
  24. package/src/components/ShareRestrictionModal/ShareRestrictionModal.jsx +42 -38
  25. package/src/components/ShareRestrictionModal/helpers.js +23 -4
  26. package/src/components/SharedDrive/DumbSharedDriveModal.jsx +8 -2
  27. package/src/components/SharedDrive/SharedDriveEditModal.jsx +20 -2
  28. package/src/components/SharedFolder/DumbBatchSharedFolderModal.jsx +10 -2
@@ -91,96 +91,83 @@ export var ShareRestrictionModal = function ShareRestrictionModal(_ref) {
91
91
 
92
92
  var handleClick = /*#__PURE__*/function () {
93
93
  var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
94
- var ttl, _yield$createPermissi2, perms, url, _yield$updatePermissi3, _yield$updatePermissi4, _perms, _url;
95
-
94
+ var permsPromise, urlPromise;
96
95
  return _regeneratorRuntime.wrap(function _callee$(_context) {
97
96
  while (1) switch (_context.prev = _context.next) {
98
97
  case 0:
99
- setLoading(true); // If the file is not shared, we create a new sharing link
100
-
101
- if (hasSharingLink) {
102
- _context.next = 13;
103
- break;
104
- }
98
+ setLoading(true); // The clipboard write must be initiated synchronously within the click
99
+ // handler for Safari to honor the user gesture; we pass a pending blob
100
+ // whose content resolves after the permission round-trip.
105
101
 
106
- ttl = makeTTL(dateToggle && selectedDate);
107
- _context.next = 5;
108
- return createPermissions({
102
+ permsPromise = hasSharingLink ? updatePermissions({
109
103
  file: file,
110
104
  t: t,
111
- ttl: ttl,
105
+ dateToggle: dateToggle,
106
+ selectedDate: selectedDate,
107
+ passwordToggle: passwordToggle,
112
108
  password: password,
113
109
  editingRights: editingRights,
114
110
  documentType: documentType,
115
- shareByLink: shareByLink,
116
- showAlert: showAlert
117
- });
118
-
119
- case 5:
120
- _yield$createPermissi2 = _context.sent;
121
- perms = _yield$createPermissi2.data;
122
- url = generateWebLink({
123
- cozyUrl: client.getStackClient().uri,
124
- searchParams: [['sharecode', perms.attributes.shortcodes.code]],
125
- pathname: '/public',
126
- slug: getAppSlugFromDocumentType({
127
- documentType: documentType
128
- }),
129
- subDomainType: client.capabilities.flat_subdomains ? 'flat' : 'nested'
130
- });
131
- _context.next = 10;
132
- return copyToClipboard(url, {
133
- t: t,
111
+ updateDocumentPermissions: updateDocumentPermissions,
134
112
  showAlert: showAlert
135
- });
136
-
137
- case 10:
138
- onClose();
139
- _context.next = 22;
140
- break;
113
+ }).then(function (_ref3) {
114
+ var _ref4 = _slicedToArray(_ref3, 1),
115
+ perms = _ref4[0].data;
141
116
 
142
- case 13:
143
- _context.next = 15;
144
- return updatePermissions({
117
+ return perms;
118
+ }) : createPermissions({
145
119
  file: file,
146
120
  t: t,
147
- dateToggle: dateToggle,
148
- selectedDate: selectedDate,
149
- passwordToggle: passwordToggle,
121
+ ttl: makeTTL(dateToggle && selectedDate),
150
122
  password: password,
151
123
  editingRights: editingRights,
152
124
  documentType: documentType,
153
- updateDocumentPermissions: updateDocumentPermissions,
125
+ shareByLink: shareByLink,
154
126
  showAlert: showAlert
127
+ }).then(function (_ref5) {
128
+ var perms = _ref5.data;
129
+ return perms;
155
130
  });
156
-
157
- case 15:
158
- _yield$updatePermissi3 = _context.sent;
159
- _yield$updatePermissi4 = _slicedToArray(_yield$updatePermissi3, 1);
160
- _perms = _yield$updatePermissi4[0].data;
161
- _url = generateWebLink({
162
- cozyUrl: client.getStackClient().uri,
163
- searchParams: [['sharecode', _perms.attributes.shortcodes.code]],
164
- pathname: '/public',
165
- slug: getAppSlugFromDocumentType({
166
- documentType: documentType
167
- }),
168
- subDomainType: client.capabilities.flat_subdomains ? 'flat' : 'nested'
131
+ urlPromise = permsPromise.then(function (perms) {
132
+ return generateWebLink({
133
+ cozyUrl: client.getStackClient().uri,
134
+ searchParams: [['sharecode', perms.attributes.shortcodes.code]],
135
+ pathname: '/public',
136
+ slug: getAppSlugFromDocumentType({
137
+ documentType: documentType
138
+ }),
139
+ subDomainType: client.capabilities.flat_subdomains ? 'flat' : 'nested'
140
+ });
169
141
  });
170
- _context.next = 21;
171
- return copyToClipboard(_url, {
142
+ _context.next = 5;
143
+ return copyToClipboard(urlPromise, {
172
144
  t: t,
173
145
  showAlert: showAlert
174
146
  });
175
147
 
176
- case 21:
148
+ case 5:
149
+ _context.prev = 5;
150
+ _context.next = 8;
151
+ return permsPromise;
152
+
153
+ case 8:
154
+ _context.next = 14;
155
+ break;
156
+
157
+ case 10:
158
+ _context.prev = 10;
159
+ _context.t0 = _context["catch"](5);
160
+ setLoading(false);
161
+ return _context.abrupt("return");
162
+
163
+ case 14:
177
164
  onClose();
178
165
 
179
- case 22:
166
+ case 15:
180
167
  case "end":
181
168
  return _context.stop();
182
169
  }
183
- }, _callee);
170
+ }, _callee, null, [[5, 10]]);
184
171
  }));
185
172
 
186
173
  return function handleClick() {
@@ -189,7 +176,7 @@ export var ShareRestrictionModal = function ShareRestrictionModal(_ref) {
189
176
  }();
190
177
 
191
178
  var handleRevokeLink = /*#__PURE__*/function () {
192
- var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
179
+ var _ref6 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
193
180
  return _regeneratorRuntime.wrap(function _callee2$(_context2) {
194
181
  while (1) switch (_context2.prev = _context2.next) {
195
182
  case 0:
@@ -214,7 +201,7 @@ export var ShareRestrictionModal = function ShareRestrictionModal(_ref) {
214
201
  }));
215
202
 
216
203
  return function handleRevokeLink() {
217
- return _ref3.apply(this, arguments);
204
+ return _ref6.apply(this, arguments);
218
205
  };
219
206
  }();
220
207
 
@@ -115,11 +115,19 @@ export var generateShareLinkFromFile = function generateShareLinkFromFile(_ref3)
115
115
  });
116
116
  };
117
117
  /**
118
- * Copy a string to the clipboard
118
+ * Copy a value to the clipboard.
119
119
  *
120
- * @param {string} value - The string saved to the clipboard
120
+ * Accepts a resolved string or a `Promise<string>`. The promise form is
121
+ * required when the value depends on a prior async step (e.g. creating a
122
+ * sharing link) from within a click handler: Safari drops the user-gesture
123
+ * context across any `await`, so `navigator.clipboard.writeText` called after
124
+ * an awaited call throws `NotAllowedError`. Passing a pending blob to
125
+ * `ClipboardItem` lets the clipboard API be invoked synchronously while the
126
+ * content is resolved later.
127
+ *
128
+ * @param {string | Promise<string>} value - Value to copy
121
129
  * @param {object} [options]
122
- * @param {number} options.t - Translation function
130
+ * @param {Function} options.t - Translation function
123
131
  * @param {Function} options.showAlert - Function to display an alert
124
132
  */
125
133
 
@@ -128,6 +136,7 @@ export var copyToClipboard = /*#__PURE__*/function () {
128
136
  var _ref5,
129
137
  t,
130
138
  showAlert,
139
+ blobPromise,
131
140
  _args2 = arguments;
132
141
 
133
142
  return _regeneratorRuntime.wrap(function _callee2$(_context2) {
@@ -144,33 +153,73 @@ export var copyToClipboard = /*#__PURE__*/function () {
144
153
 
145
154
  case 3:
146
155
  _context2.prev = 3;
147
- _context2.next = 6;
156
+
157
+ if (!(typeof value === 'string')) {
158
+ _context2.next = 9;
159
+ break;
160
+ }
161
+
162
+ _context2.next = 7;
148
163
  return navigator.clipboard.writeText(value);
149
164
 
150
- case 6:
165
+ case 7:
166
+ _context2.next = 21;
167
+ break;
168
+
169
+ case 9:
170
+ if (!(typeof ClipboardItem !== 'undefined')) {
171
+ _context2.next = 15;
172
+ break;
173
+ }
174
+
175
+ blobPromise = Promise.resolve(value).then(function (text) {
176
+ return new Blob([text], {
177
+ type: 'text/plain'
178
+ });
179
+ });
180
+ _context2.next = 13;
181
+ return navigator.clipboard.write([new ClipboardItem({
182
+ 'text/plain': blobPromise
183
+ })]);
184
+
185
+ case 13:
186
+ _context2.next = 21;
187
+ break;
188
+
189
+ case 15:
190
+ _context2.t0 = navigator.clipboard;
191
+ _context2.next = 18;
192
+ return value;
193
+
194
+ case 18:
195
+ _context2.t1 = _context2.sent;
196
+ _context2.next = 21;
197
+ return _context2.t0.writeText.call(_context2.t0, _context2.t1);
198
+
199
+ case 21:
151
200
  showAlert({
152
201
  message: t('copyReminderContent.success'),
153
202
  severity: 'success',
154
203
  variant: 'filled'
155
204
  });
156
- _context2.next = 13;
205
+ _context2.next = 28;
157
206
  break;
158
207
 
159
- case 9:
160
- _context2.prev = 9;
161
- _context2.t0 = _context2["catch"](3);
208
+ case 24:
209
+ _context2.prev = 24;
210
+ _context2.t2 = _context2["catch"](3);
162
211
  showAlert({
163
212
  message: t('copyReminderContent.error'),
164
213
  severity: 'error',
165
214
  variant: 'filled'
166
215
  });
167
- log.error("Error in 'copyToClipboard' function when trying to copy to clipboard", _context2.t0);
216
+ log.error("Error in 'copyToClipboard' function when trying to copy to clipboard", _context2.t2);
168
217
 
169
- case 13:
218
+ case 28:
170
219
  case "end":
171
220
  return _context2.stop();
172
221
  }
173
- }, _callee2, null, [[3, 9]]);
222
+ }, _callee2, null, [[3, 24]]);
174
223
  }));
175
224
 
176
225
  return function copyToClipboard(_x2) {
@@ -20,7 +20,9 @@ export var DumbSharedDriveModal = withLocales(function (_ref) {
20
20
  onRename = _ref.onRename,
21
21
  _ref$showNameField = _ref.showNameField,
22
22
  showNameField = _ref$showNameField === void 0 ? true : _ref$showNameField,
23
- sharingLink = _ref.sharingLink;
23
+ sharingLink = _ref.sharingLink,
24
+ autoOpenShareRestriction = _ref.autoOpenShareRestriction,
25
+ showGenerateLinkButton = _ref.showGenerateLinkButton;
24
26
 
25
27
  var _useI18n = useI18n(),
26
28
  t = _useI18n.t;
@@ -48,7 +50,9 @@ export var DumbSharedDriveModal = withLocales(function (_ref) {
48
50
  cancelLabel: t('SharedDrive.sharedDriveModal.cancel'),
49
51
  createLabel: t('SharedDrive.sharedDriveModal.create'),
50
52
  shareLabel: t('FederatedFolder.share'),
51
- saveLabel: t('SharedDrive.sharedDriveModal.save')
53
+ saveLabel: t('SharedDrive.sharedDriveModal.save'),
54
+ autoOpenShareRestriction: autoOpenShareRestriction,
55
+ showGenerateLinkButton: showGenerateLinkButton
52
56
  });
53
57
  });
54
58
  DumbSharedDriveModal.propTypes = {
@@ -67,6 +71,8 @@ DumbSharedDriveModal.propTypes = {
67
71
  onShare: PropTypes.func.isRequired,
68
72
  onRename: PropTypes.func,
69
73
  showNameField: PropTypes.bool,
70
- sharingLink: PropTypes.string
74
+ sharingLink: PropTypes.string,
75
+ autoOpenShareRestriction: PropTypes.bool,
76
+ showGenerateLinkButton: PropTypes.bool
71
77
  };
72
78
  export default DumbSharedDriveModal;
@@ -16,7 +16,9 @@ export var SharedDriveEditModal = withLocales(function (_ref) {
16
16
  recipients = _ref.recipients,
17
17
  onShare = _ref.onShare,
18
18
  onRevoke = _ref.onRevoke,
19
- onClose = _ref.onClose;
19
+ onClose = _ref.onClose,
20
+ autoOpenShareRestriction = _ref.autoOpenShareRestriction,
21
+ showGenerateLinkButton = _ref.showGenerateLinkButton;
20
22
  var client = useClient();
21
23
 
22
24
  var _useI18n = useI18n(),
@@ -91,9 +93,18 @@ export var SharedDriveEditModal = withLocales(function (_ref) {
91
93
  onShare: onShare,
92
94
  sharedDriveName: name,
93
95
  handleSharedDriveNameChange: handleNameChange,
94
- onRename: onRename
96
+ onRename: onRename,
97
+ autoOpenShareRestriction: autoOpenShareRestriction,
98
+ showGenerateLinkButton: showGenerateLinkButton
95
99
  });
96
100
  });
97
101
  SharedDriveEditModal.propTypes = {
98
- onClose: PropTypes.func.isRequired
102
+ document: PropTypes.object,
103
+ sharing: PropTypes.object,
104
+ recipients: PropTypes.array,
105
+ onShare: PropTypes.func.isRequired,
106
+ onRevoke: PropTypes.func.isRequired,
107
+ onClose: PropTypes.func.isRequired,
108
+ autoOpenShareRestriction: PropTypes.bool,
109
+ showGenerateLinkButton: PropTypes.bool
99
110
  };
@@ -36,7 +36,9 @@ export var DumbBatchSharedFolderModal = withLocales(function (_ref) {
36
36
  saveLabel = _ref.saveLabel,
37
37
  sharingDesc = _ref.sharingDesc,
38
38
  _ref$showShareByEmail = _ref.showShareByEmail,
39
- showShareByEmail = _ref$showShareByEmail === void 0 ? true : _ref$showShareByEmail;
39
+ showShareByEmail = _ref$showShareByEmail === void 0 ? true : _ref$showShareByEmail,
40
+ autoOpenShareRestriction = _ref.autoOpenShareRestriction,
41
+ showGenerateLinkButton = _ref.showGenerateLinkButton;
40
42
  var hasRecipients = Boolean(recipients === null || recipients === void 0 ? void 0 : recipients.length); // Determine action buttons based on modal mode
41
43
 
42
44
  var actionButtons = function () {
@@ -56,7 +58,9 @@ export var DumbBatchSharedFolderModal = withLocales(function (_ref) {
56
58
  return /*#__PURE__*/React.createElement(ShareByLink, {
57
59
  link: sharingLink,
58
60
  document: document,
59
- documentType: "Files"
61
+ documentType: "Files",
62
+ showGenerateLinkButton: showGenerateLinkButton,
63
+ autoOpenShareRestriction: autoOpenShareRestriction
60
64
  });
61
65
  }
62
66
 
@@ -64,7 +68,9 @@ export var DumbBatchSharedFolderModal = withLocales(function (_ref) {
64
68
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ShareByLink, {
65
69
  link: sharingLink,
66
70
  document: document,
67
- documentType: "Files"
71
+ documentType: "Files",
72
+ showGenerateLinkButton: showGenerateLinkButton,
73
+ autoOpenShareRestriction: autoOpenShareRestriction
68
74
  }), /*#__PURE__*/React.createElement(Button, {
69
75
  variant: "primary",
70
76
  label: shareLabel,
@@ -165,7 +171,9 @@ DumbBatchSharedFolderModal.propTypes = {
165
171
  shareLabel: PropTypes.string,
166
172
  saveLabel: PropTypes.string,
167
173
  sharingDesc: PropTypes.string,
168
- showShareByEmail: PropTypes.bool
174
+ showShareByEmail: PropTypes.bool,
175
+ autoOpenShareRestriction: PropTypes.bool,
176
+ showGenerateLinkButton: PropTypes.bool
169
177
  };
170
178
  DumbBatchSharedFolderModal.defaultProps = {
171
179
  sharingDesc: ''
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-sharing",
3
- "version": "30.1.1",
3
+ "version": "30.2.1",
4
4
  "description": "Provides sharing login for React applications.",
5
5
  "main": "dist/index.js",
6
6
  "author": "Cozy",
@@ -84,5 +84,5 @@
84
84
  "sideEffects": [
85
85
  "*.css"
86
86
  ],
87
- "gitHead": "f19da18da889a4164b25cfa3479f597bfe7e6047"
87
+ "gitHead": "4658ed10da57999de9a4cbef4887582ac10019db"
88
88
  }
@@ -20,7 +20,9 @@ export const DumbFederatedFolderModal = withLocales(
20
20
  onClose,
21
21
  onShare,
22
22
  sharingLink,
23
- showShareByEmail
23
+ showShareByEmail,
24
+ autoOpenShareRestriction,
25
+ showGenerateLinkButton
24
26
  }) => {
25
27
  const { t } = useI18n()
26
28
  return (
@@ -42,6 +44,8 @@ export const DumbFederatedFolderModal = withLocales(
42
44
  addButtonLabel={t('FederatedFolder.add')}
43
45
  shareLabel={t('FederatedFolder.share')}
44
46
  showShareByEmail={showShareByEmail}
47
+ autoOpenShareRestriction={autoOpenShareRestriction}
48
+ showGenerateLinkButton={showGenerateLinkButton}
45
49
  />
46
50
  )
47
51
  }
@@ -60,7 +64,9 @@ DumbFederatedFolderModal.propTypes = {
60
64
  onClose: PropTypes.func.isRequired,
61
65
  onShare: PropTypes.func.isRequired,
62
66
  sharingLink: PropTypes.string,
63
- showShareByEmail: PropTypes.bool
67
+ showShareByEmail: PropTypes.bool,
68
+ autoOpenShareRestriction: PropTypes.bool,
69
+ showGenerateLinkButton: PropTypes.bool
64
70
  }
65
71
 
66
72
  export default DumbFederatedFolderModal
@@ -17,7 +17,12 @@ import {
17
17
  } from '../SharedDrive/helpers'
18
18
 
19
19
  export const FederatedFolderModal = withLocales(
20
- ({ onClose, document: existingDocument }) => {
20
+ ({
21
+ onClose,
22
+ document: existingDocument,
23
+ autoOpenShareRestriction,
24
+ showGenerateLinkButton
25
+ }) => {
21
26
  const client = useClient()
22
27
  const { t } = useI18n()
23
28
  const {
@@ -159,6 +164,8 @@ export const FederatedFolderModal = withLocales(
159
164
  onShare={onShare}
160
165
  sharingLink={sharingLink}
161
166
  showShareByEmail={!isSharedDrive}
167
+ autoOpenShareRestriction={autoOpenShareRestriction}
168
+ showGenerateLinkButton={showGenerateLinkButton}
162
169
  />
163
170
  )
164
171
  }
@@ -166,7 +173,9 @@ export const FederatedFolderModal = withLocales(
166
173
 
167
174
  FederatedFolderModal.propTypes = {
168
175
  onClose: PropTypes.func.isRequired,
169
- document: PropTypes.object.isRequired
176
+ document: PropTypes.object.isRequired,
177
+ autoOpenShareRestriction: PropTypes.bool,
178
+ showGenerateLinkButton: PropTypes.bool
170
179
  }
171
180
 
172
181
  export default FederatedFolderModal
@@ -18,7 +18,13 @@ import {
18
18
  } from './ShareRestrictionModal/helpers'
19
19
  import { useSharingContext } from '../hooks/useSharingContext'
20
20
 
21
- const ShareByLink = ({ link, document, documentType }) => {
21
+ const ShareByLink = ({
22
+ link,
23
+ document,
24
+ documentType,
25
+ showGenerateLinkButton = false,
26
+ autoOpenShareRestriction = false
27
+ }) => {
22
28
  const { t } = useI18n()
23
29
  const { isMobile } = useBreakpoints()
24
30
  const { showAlert } = useAlert()
@@ -27,8 +33,9 @@ const ShareByLink = ({ link, document, documentType }) => {
27
33
  const [isGenerating, setIsGenerating] = useState(false)
28
34
 
29
35
  const canShare = typeof navigator?.share === 'function'
30
- const showCopyAndSendButtons = isMobile && canShare
31
- const showOnlyCopyButton = !isMobile || !canShare
36
+ const showGenerateButton = showGenerateLinkButton && !link
37
+ const showCopyAndSendButtons = !showGenerateButton && isMobile && canShare
38
+ const showOnlyCopyButton = !showGenerateButton && (!isMobile || !canShare)
32
39
  const isFederated = flag('drive.federated-shared-folder.enabled')
33
40
 
34
41
  const [isEditDialogOpen, toggleEditDialogOpen] = useReducer(
@@ -36,6 +43,14 @@ const ShareByLink = ({ link, document, documentType }) => {
36
43
  false
37
44
  )
38
45
 
46
+ const showGenericError = () => {
47
+ showAlert({
48
+ message: t(`${documentType}.share.error.generic`),
49
+ severity: 'error',
50
+ variant: 'filled'
51
+ })
52
+ }
53
+
39
54
  const handleGenerateLink = async () => {
40
55
  setIsGenerating(true)
41
56
  try {
@@ -60,18 +75,40 @@ const ShareByLink = ({ link, document, documentType }) => {
60
75
  }
61
76
 
62
77
  const generateLinkAndCopyLinkToClipboard = async () => {
63
- let linkToCopy = link
64
- if (!linkToCopy) {
65
- linkToCopy = await handleGenerateLink()
78
+ if (link) {
79
+ await copyToClipboard(link, { t, showAlert })
80
+ return
66
81
  }
67
- if (linkToCopy) {
68
- await copyToClipboard(linkToCopy, { t, showAlert })
69
- } else {
70
- showAlert({
71
- message: t(`${documentType}.share.error.generic`),
72
- severity: 'error',
73
- variant: 'filled'
74
- })
82
+ // Safari: the clipboard API must be invoked synchronously within the
83
+ // click handler, so we pass the pending link to `copyToClipboard`.
84
+ // The resolved URL is captured here so we can branch on success after
85
+ // the copy has completed, without re-awaiting the promise.
86
+ let generatedLink = null
87
+ await copyToClipboard(
88
+ handleGenerateLink().then(generated => {
89
+ generatedLink = generated
90
+ if (!generated) throw new Error('link-generation-failed')
91
+ return generated
92
+ }),
93
+ { t, showAlert }
94
+ )
95
+ if (!generatedLink) {
96
+ showGenericError()
97
+ return
98
+ }
99
+ if (autoOpenShareRestriction) {
100
+ toggleEditDialogOpen()
101
+ }
102
+ }
103
+
104
+ const generateLinkSilently = async () => {
105
+ const generated = await handleGenerateLink()
106
+ if (!generated) {
107
+ showGenericError()
108
+ return
109
+ }
110
+ if (autoOpenShareRestriction) {
111
+ toggleEditDialogOpen()
75
112
  }
76
113
  }
77
114
 
@@ -81,11 +118,7 @@ const ShareByLink = ({ link, document, documentType }) => {
81
118
  linkToShare = await handleGenerateLink()
82
119
  }
83
120
  if (!linkToShare) {
84
- showAlert({
85
- message: t(`${documentType}.share.error.generic`),
86
- severity: 'error',
87
- variant: 'filled'
88
- })
121
+ showGenericError()
89
122
  return
90
123
  }
91
124
 
@@ -100,11 +133,7 @@ const ShareByLink = ({ link, document, documentType }) => {
100
133
  } catch (error) {
101
134
  // Don't show error when user cancels the share dialog
102
135
  if (error.name !== 'AbortError') {
103
- showAlert({
104
- message: t(`${documentType}.share.error.generic`),
105
- severity: 'error',
106
- variant: 'filled'
107
- })
136
+ showGenericError()
108
137
  }
109
138
  }
110
139
  }
@@ -144,6 +173,16 @@ const ShareByLink = ({ link, document, documentType }) => {
144
173
  busy={isGenerating}
145
174
  />
146
175
  )}
176
+ {showGenerateButton && (
177
+ <Button
178
+ label={t(`${documentType}.share.shareByLink.create`)}
179
+ variant="secondary"
180
+ size="medium"
181
+ startIcon={<Icon icon={LinkIcon} />}
182
+ onClick={generateLinkSilently}
183
+ busy={isGenerating}
184
+ />
185
+ )}
147
186
  {isEditDialogOpen && (
148
187
  <ShareRestrictionModal file={document} onClose={toggleEditDialogOpen} />
149
188
  )}
@@ -154,7 +193,9 @@ const ShareByLink = ({ link, document, documentType }) => {
154
193
  ShareByLink.propTypes = {
155
194
  link: PropTypes.string,
156
195
  document: PropTypes.object.isRequired,
157
- documentType: PropTypes.string.isRequired
196
+ documentType: PropTypes.string.isRequired,
197
+ showGenerateLinkButton: PropTypes.bool,
198
+ autoOpenShareRestriction: PropTypes.bool
158
199
  }
159
200
 
160
201
  export default ShareByLink
@@ -1,5 +1,5 @@
1
1
  import cx from 'classnames'
2
- import React from 'react'
2
+ import React, { useMemo } from 'react'
3
3
 
4
4
  import Typography from 'cozy-ui/transpiled/react/Typography'
5
5
  import useBreakpoints from 'cozy-ui/transpiled/react/providers/Breakpoints'
@@ -111,17 +111,32 @@ const SharingTitleFunction = ({ documentType, document }) =>
111
111
  */
112
112
  const ShareDialogCozyToCozy = ({
113
113
  showShareByLink,
114
+ showGenerateLinkButton,
115
+ autoOpenShareRestriction,
114
116
  documentType,
115
117
  document,
116
118
  ...props
117
119
  }) => {
120
+ const dialogActionsOnShare = useMemo(() => {
121
+ if (!showShareByLink) return null
122
+ const BoundShareByLink = dialogActionProps => (
123
+ <DumbShareByLink
124
+ {...dialogActionProps}
125
+ showGenerateLinkButton={showGenerateLinkButton}
126
+ autoOpenShareRestriction={autoOpenShareRestriction}
127
+ />
128
+ )
129
+ BoundShareByLink.displayName = 'BoundShareByLink'
130
+ return BoundShareByLink
131
+ }, [showShareByLink, showGenerateLinkButton, autoOpenShareRestriction])
132
+
118
133
  return (
119
134
  <ShareDialogTwoStepsConfirmationContainer
120
135
  {...props}
121
136
  documentType={documentType}
122
137
  document={document}
123
138
  dialogContentOnShare={SharingContent}
124
- dialogActionsOnShare={showShareByLink ? DumbShareByLink : null}
139
+ dialogActionsOnShare={dialogActionsOnShare}
125
140
  dialogTitleOnShare={SharingTitleFunction({ documentType, document })}
126
141
  disableGutters
127
142
  />
@@ -19,7 +19,9 @@ const ShareDialogOnlyByLink = ({
19
19
  documentType,
20
20
  link,
21
21
  onClose,
22
- permissions
22
+ permissions,
23
+ showGenerateLinkButton,
24
+ autoOpenShareRestriction
23
25
  }) => {
24
26
  const { t } = useI18n()
25
27
  const { isMobile } = useBreakpoints()
@@ -62,6 +64,8 @@ const ShareDialogOnlyByLink = ({
62
64
  link={link}
63
65
  document={document}
64
66
  documentType={documentType}
67
+ showGenerateLinkButton={showGenerateLinkButton}
68
+ autoOpenShareRestriction={autoOpenShareRestriction}
65
69
  />
66
70
  </div>
67
71
  </>