cozy-sharing 32.3.1 → 32.3.2
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 +6 -0
- package/dist/components/FederatedFolder/FederatedFolderModal.js +56 -19
- package/dist/components/FederatedFolder/FederatedFolderModal.spec.js +101 -254
- package/dist/components/Recipient/RecipientList.js +0 -15
- package/dist/components/ShareModal/ShareModal.js +8 -8
- package/dist/state.js +6 -1
- package/package.json +2 -2
- package/src/components/FederatedFolder/FederatedFolderModal.jsx +65 -19
- package/src/components/FederatedFolder/FederatedFolderModal.spec.jsx +28 -150
- package/src/components/Recipient/RecipientList.jsx +0 -18
- package/src/components/ShareModal/ShareModal.jsx +5 -5
- package/src/state.js +5 -1
- package/.claude/settings.local.json +0 -27
- package/AGENTS.md +0 -7
- package/dist/RefreshableSharings.spec.js +0 -40
- package/dist/assets/illustrations/illustration-shared-drives.svg +0 -1
- package/dist/components/EditLinkPermissionDialog.js +0 -72
- package/dist/components/EditLinkPermissionDialog.spec.js +0 -48
- package/dist/components/FederatedFolder/DumbFederatedFolderModal.js +0 -65
- package/dist/components/FederatedFolder/DumbFederatedFolderModal.spec.js +0 -107
- package/dist/components/FederatedFolder/FederatedFolderModal.jsx.tmp.179197.1773939270471 +0 -172
- package/dist/components/Recipient/actions/revokeGroup.js +0 -42
- package/dist/components/Recipient/actions/revokeMember.js +0 -42
- package/dist/components/Recipient/actions/revokeSharedDriveMember.js +0 -41
- package/dist/components/ShareModal/ShareModal copy.js +0 -43
- package/dist/components/ShareModal/ShareModal.spec.js +0 -197
- package/dist/components/ShareModal/ShareModalBatchContainer.js +0 -276
- package/dist/components/ShareModal/ShareModalBatchContainer.spec.js +0 -131
- package/dist/components/ShareModal/SharedDriveEditModal.js +0 -59
- package/dist/components/SharedDrive/DumbSharedDriveModal.js +0 -78
- package/dist/components/SharedDrive/DumbSharedDriveModal.spec.js +0 -126
- package/dist/components/SharedDrive/SharedDriveEditModal.js +0 -110
- package/dist/components/SharedDrive/SharedDriveModal copy.js +0 -356
- package/dist/components/SharedDrive/SharedDriveRecipient.js +0 -61
- package/dist/components/SharedDrive/SharedDriveRecipientPermissions.js +0 -109
- package/dist/components/SharedDrive/SharedDriveRecipientStatus.js +0 -22
- package/dist/components/SharedDrive/helpers.js +0 -102
- package/dist/components/SharedDrive/helpers.spec.js +0 -208
- package/dist/components/SharedDriveEditModal.js +0 -43
- package/dist/components/SharedFolder/DumbBatchSharedFolderModal.js +0 -145
- package/dist/components/SharedFolder/DumbBatchSharedFolderModal.spec.js +0 -321
- package/dist/components/SharedStatus.js +0 -61
- package/dist/components/SharedStatus.spec.js +0 -44
- package/dist/components/Sharesubmit.js +0 -28
- package/dist/components/__snapshots__/SharedStatus.spec.js.snap +0 -94
- package/dist/helpers/owner.js +0 -14
- package/dist/helpers/owner.spec.js +0 -34
- package/dist/hooks/useConfirmDiscardChanges.js +0 -33
- package/dist/hooks/useContactsAndGroups.js +0 -65
- package/dist/hooks/useSharedDrive.js +0 -130
- package/dist/hooks/useSharedDriveContactsAndGroups.js +0 -65
- package/dist/propTypes.js +0 -26
- package/dist/styles/shareddrive.styl +0 -8
- package/src/components/SharedDrive/SharedDriveRecipient.jsx +0 -62
- package/src/components/SharedDrive/SharedDriveRecipientPermissions.jsx +0 -78
- package/src/components/SharedDrive/SharedDriveRecipientStatus.jsx +0 -23
- package/src/components/SharedFolder/DumbBatchSharedFolderModal.jsx +0 -161
|
@@ -4,7 +4,6 @@ import { GroupRecipient } from './GroupRecipient';
|
|
|
4
4
|
import MemberRecipient from './MemberRecipient';
|
|
5
5
|
import { usePrevious } from '../../helpers/hooks';
|
|
6
6
|
import { filterAndReworkRecipients } from '../../helpers/recipients';
|
|
7
|
-
import SharedDriveRecipient from '../SharedDrive/SharedDriveRecipient';
|
|
8
7
|
|
|
9
8
|
var RecipientList = function RecipientList(_ref) {
|
|
10
9
|
var recipients = _ref.recipients,
|
|
@@ -15,27 +14,13 @@ var RecipientList = function RecipientList(_ref) {
|
|
|
15
14
|
documentType = _ref.documentType,
|
|
16
15
|
onRevoke = _ref.onRevoke,
|
|
17
16
|
onRevokeSelf = _ref.onRevokeSelf,
|
|
18
|
-
onSetType = _ref.onSetType,
|
|
19
17
|
verifyRecipient = _ref.verifyRecipient;
|
|
20
18
|
var previousRecipients = usePrevious(recipients);
|
|
21
19
|
var recipientsToDisplay = filterAndReworkRecipients(recipients, previousRecipients);
|
|
22
20
|
return recipientsToDisplay.map(function (recipient) {
|
|
23
|
-
var _recipient$index;
|
|
24
|
-
|
|
25
21
|
var recipientConfirmationData = recipientsToBeConfirmed.find(function (user) {
|
|
26
22
|
return user.email === recipient.email;
|
|
27
23
|
});
|
|
28
|
-
var isSharedDriveRecipient = recipient === null || recipient === void 0 || (_recipient$index = recipient.index) === null || _recipient$index === void 0 ? void 0 : _recipient$index.toString().startsWith('virtual-shared-drive');
|
|
29
|
-
|
|
30
|
-
if (isSharedDriveRecipient) {
|
|
31
|
-
return /*#__PURE__*/React.createElement(SharedDriveRecipient, _extends({}, recipient, {
|
|
32
|
-
key: recipient.index,
|
|
33
|
-
onRevoke: onRevoke,
|
|
34
|
-
onSetType: onSetType,
|
|
35
|
-
fadeIn: recipient.hasBeenJustAdded
|
|
36
|
-
}));
|
|
37
|
-
}
|
|
38
|
-
|
|
39
24
|
var isGroupRecipient = recipient.members;
|
|
40
25
|
|
|
41
26
|
if (isGroupRecipient) {
|
|
@@ -58,17 +58,17 @@ export var ShareModal = withLocales(function (props) {
|
|
|
58
58
|
};
|
|
59
59
|
}();
|
|
60
60
|
|
|
61
|
-
var isFederatedMode = flag('drive.federated-shared-folder.enabled');
|
|
62
|
-
|
|
63
|
-
if (isFederatedMode && allLoaded) {
|
|
64
|
-
return /*#__PURE__*/React.createElement(FederatedFolderModal, _extends({
|
|
65
|
-
document: document
|
|
66
|
-
}, rest));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
61
|
var isEditable = !byDocId[document.id] || isOwner(document.id) || canReshare(document.id);
|
|
70
62
|
|
|
71
63
|
if (isEditable) {
|
|
64
|
+
var isFederatedMode = flag('drive.federated-shared-folder.enabled');
|
|
65
|
+
|
|
66
|
+
if (isFederatedMode && allLoaded) {
|
|
67
|
+
return /*#__PURE__*/React.createElement(FederatedFolderModal, _extends({
|
|
68
|
+
document: document
|
|
69
|
+
}, rest));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
72
|
return /*#__PURE__*/React.createElement(EditableSharingModal, _extends({
|
|
73
73
|
document: document
|
|
74
74
|
}, rest));
|
package/dist/state.js
CHANGED
|
@@ -412,7 +412,12 @@ export var isSharedDrive = function isSharedDrive(state, docId) {
|
|
|
412
412
|
export var canReshare = function canReshare(state, docId, instanceUri) {
|
|
413
413
|
var sharing = getDocumentSharing(state, docId);
|
|
414
414
|
var me = sharing.attributes.members.find(matchingInstanceName(instanceUri));
|
|
415
|
-
|
|
415
|
+
|
|
416
|
+
if (sharing.drive) {
|
|
417
|
+
return me && !me.read_only;
|
|
418
|
+
} else {
|
|
419
|
+
return sharing.attributes.open_sharing === true && me && !me.read_only;
|
|
420
|
+
}
|
|
416
421
|
};
|
|
417
422
|
export var getOwner = function getOwner(state, docId) {
|
|
418
423
|
return getRecipients(state, docId).find(function (r) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-sharing",
|
|
3
|
-
"version": "32.3.
|
|
3
|
+
"version": "32.3.2",
|
|
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": "
|
|
86
|
+
"gitHead": "5fd20886ef0dfec91783b81bce0ae59d210a0cfa"
|
|
87
87
|
}
|
|
@@ -3,6 +3,8 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
|
|
3
3
|
|
|
4
4
|
import { useClient } from 'cozy-client'
|
|
5
5
|
import Button from 'cozy-ui/transpiled/react/Buttons'
|
|
6
|
+
import { FixedDialog } from 'cozy-ui/transpiled/react/CozyDialogs'
|
|
7
|
+
import Typography from 'cozy-ui/transpiled/react/Typography'
|
|
6
8
|
import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
|
|
7
9
|
import ConfirmDialogProvider, {
|
|
8
10
|
useConfirmDialog
|
|
@@ -13,7 +15,10 @@ import { getOrCreateFromArray } from '../../helpers/contacts'
|
|
|
13
15
|
import withLocales from '../../hoc/withLocales'
|
|
14
16
|
import { usePendingRecipients } from '../../hooks/usePendingRecipients'
|
|
15
17
|
import { useSharingContext } from '../../hooks/useSharingContext'
|
|
16
|
-
import
|
|
18
|
+
import AntivirusAlert from '../AntivirusAlert'
|
|
19
|
+
import { default as DumbShareByEmail } from '../ShareByEmail'
|
|
20
|
+
import ShareByLink from '../ShareByLink'
|
|
21
|
+
import WhoHasAccess from '../WhoHasAccess'
|
|
17
22
|
|
|
18
23
|
const FederatedFolderModalContent = ({
|
|
19
24
|
onClose,
|
|
@@ -121,7 +126,7 @@ const FederatedFolderModalContent = ({
|
|
|
121
126
|
const folderName = existingDocument?.name || ''
|
|
122
127
|
|
|
123
128
|
const onSend = async () => {
|
|
124
|
-
if (pendingRecipients.length === 0) {
|
|
129
|
+
if (isSending || pendingRecipients.length === 0) {
|
|
125
130
|
onClose()
|
|
126
131
|
return
|
|
127
132
|
}
|
|
@@ -171,26 +176,67 @@ const FederatedFolderModalContent = ({
|
|
|
171
176
|
: []
|
|
172
177
|
|
|
173
178
|
const modalTitle = t('FederatedFolder.shareTitle', { name: folderName })
|
|
174
|
-
const isSharedDrive = Boolean(existingDocument?.driveId)
|
|
175
179
|
|
|
176
180
|
return (
|
|
177
|
-
<
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
recipients={isSending ? frozenRecipients : existingRecipients}
|
|
181
|
-
currentRecipients={isSending ? frozenRecipients : existingRecipients}
|
|
182
|
-
onRevoke={revoke}
|
|
183
|
-
onSend={onSend}
|
|
181
|
+
<FixedDialog
|
|
182
|
+
open
|
|
183
|
+
disableGutters
|
|
184
184
|
onClose={handleCloseRequest}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
185
|
+
title={modalTitle}
|
|
186
|
+
classes={{ paper: 'u-ov-visible' }}
|
|
187
|
+
componentsProps={{
|
|
188
|
+
dialogContent: { className: 'u-ov-visible' }
|
|
189
|
+
}}
|
|
190
|
+
content={
|
|
191
|
+
<div>
|
|
192
|
+
<div className="u-ph-2">
|
|
193
|
+
<AntivirusAlert
|
|
194
|
+
document={isSending ? frozenDoc : existingDocument}
|
|
195
|
+
/>
|
|
196
|
+
<Typography variant="h6" className="u-mt-1-half u-mb-half">
|
|
197
|
+
{t('Share.contacts.addUsers')}
|
|
198
|
+
</Typography>
|
|
199
|
+
<DumbShareByEmail
|
|
200
|
+
currentRecipients={
|
|
201
|
+
isSending ? frozenRecipients : existingRecipients
|
|
202
|
+
}
|
|
203
|
+
document={isSending ? frozenDoc : existingDocument}
|
|
204
|
+
documentType="Files"
|
|
205
|
+
pendingRecipients={pendingRecipients}
|
|
206
|
+
onPendingRecipientsChange={setPendingRecipients}
|
|
207
|
+
selectedOption={selectedOption}
|
|
208
|
+
onSelectedOptionChange={setSelectedOption}
|
|
209
|
+
/>
|
|
210
|
+
</div>
|
|
211
|
+
<WhoHasAccess
|
|
212
|
+
isOwner
|
|
213
|
+
isSharedDrive
|
|
214
|
+
recipients={isSending ? frozenRecipients : existingRecipients}
|
|
215
|
+
document={isSending ? frozenDoc : existingDocument}
|
|
216
|
+
documentType="Files"
|
|
217
|
+
className="u-w-100"
|
|
218
|
+
onRevoke={revoke}
|
|
219
|
+
link={sharingLink}
|
|
220
|
+
/>
|
|
221
|
+
</div>
|
|
222
|
+
}
|
|
223
|
+
actions={
|
|
224
|
+
<>
|
|
225
|
+
<ShareByLink
|
|
226
|
+
link={sharingLink}
|
|
227
|
+
document={isSending ? frozenDoc : existingDocument}
|
|
228
|
+
documentType="Files"
|
|
229
|
+
showGenerateLinkButton={showGenerateLinkButton}
|
|
230
|
+
autoOpenShareRestriction={autoOpenShareRestriction}
|
|
231
|
+
/>
|
|
232
|
+
<Button
|
|
233
|
+
variant="primary"
|
|
234
|
+
label={t('FederatedFolder.share')}
|
|
235
|
+
disabled={isSending}
|
|
236
|
+
onClick={onSend}
|
|
237
|
+
/>
|
|
238
|
+
</>
|
|
239
|
+
}
|
|
194
240
|
/>
|
|
195
241
|
)
|
|
196
242
|
}
|
|
@@ -41,41 +41,6 @@ jest.mock('../../hooks/usePendingRecipients', () => ({
|
|
|
41
41
|
usePendingRecipients: jest.fn()
|
|
42
42
|
}))
|
|
43
43
|
|
|
44
|
-
jest.mock('../SharedFolder/DumbBatchSharedFolderModal', () => ({
|
|
45
|
-
DumbBatchSharedFolderModal: ({
|
|
46
|
-
title,
|
|
47
|
-
recipients,
|
|
48
|
-
onRevoke,
|
|
49
|
-
onSend,
|
|
50
|
-
onClose,
|
|
51
|
-
sharingLink,
|
|
52
|
-
pendingRecipients,
|
|
53
|
-
selectedOption
|
|
54
|
-
}) => (
|
|
55
|
-
<div data-testid="dumb-modal">
|
|
56
|
-
<span data-testid="title">{title}</span>
|
|
57
|
-
<span data-testid="sharing-link">{sharingLink}</span>
|
|
58
|
-
<span data-testid="recipients-count">{recipients.length}</span>
|
|
59
|
-
<span data-testid="pending-recipients-count">
|
|
60
|
-
{pendingRecipients.length}
|
|
61
|
-
</span>
|
|
62
|
-
<span data-testid="selected-option">{selectedOption}</span>
|
|
63
|
-
<button data-testid="btn-send" onClick={onSend}>
|
|
64
|
-
Send
|
|
65
|
-
</button>
|
|
66
|
-
<button
|
|
67
|
-
data-testid="btn-revoke"
|
|
68
|
-
onClick={() => onRevoke({ _id: 'folder-123' }, 'abc', 1)}
|
|
69
|
-
>
|
|
70
|
-
Revoke
|
|
71
|
-
</button>
|
|
72
|
-
<button data-testid="btn-close" onClick={onClose}>
|
|
73
|
-
Close
|
|
74
|
-
</button>
|
|
75
|
-
</div>
|
|
76
|
-
)
|
|
77
|
-
}))
|
|
78
|
-
|
|
79
44
|
const mockDocument = {
|
|
80
45
|
_id: 'folder-123',
|
|
81
46
|
name: 'My Test Folder',
|
|
@@ -145,46 +110,22 @@ describe('FederatedFolderModal', () => {
|
|
|
145
110
|
|
|
146
111
|
describe('initialization', () => {
|
|
147
112
|
it('should pass document name as title', async () => {
|
|
148
|
-
const {
|
|
113
|
+
const { findByText } = setup()
|
|
149
114
|
|
|
150
|
-
await
|
|
151
|
-
expect(getByTestId('title').textContent).toBe('Share "My Test Folder"')
|
|
152
|
-
})
|
|
115
|
+
await findByText('Share "My Test Folder"')
|
|
153
116
|
})
|
|
154
117
|
|
|
155
118
|
it('should pass default title when document has no name', async () => {
|
|
156
119
|
const documentWithoutName = { _id: 'folder-456', path: '/test' }
|
|
157
|
-
const {
|
|
158
|
-
|
|
159
|
-
await waitFor(() => {
|
|
160
|
-
expect(getByTestId('title').textContent).toBe('Share ""')
|
|
161
|
-
})
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
it('should pass sharing link to DumbBatchSharedFolderModal', async () => {
|
|
165
|
-
const { getByTestId } = setup()
|
|
166
|
-
|
|
167
|
-
await waitFor(() => {
|
|
168
|
-
expect(getByTestId('sharing-link').textContent).toBe(
|
|
169
|
-
'https://example.com/share/abc123'
|
|
170
|
-
)
|
|
171
|
-
})
|
|
172
|
-
})
|
|
120
|
+
const { findByText } = setup({ document: documentWithoutName })
|
|
173
121
|
|
|
174
|
-
|
|
175
|
-
const { getByTestId } = setup({ document: undefined })
|
|
176
|
-
|
|
177
|
-
await waitFor(() => {
|
|
178
|
-
expect(getByTestId('sharing-link').textContent).toBe('')
|
|
179
|
-
})
|
|
122
|
+
await findByText('Share ""')
|
|
180
123
|
})
|
|
181
124
|
|
|
182
|
-
it('should
|
|
183
|
-
const {
|
|
125
|
+
it('should show Copy link', async () => {
|
|
126
|
+
const { findByText } = setup()
|
|
184
127
|
|
|
185
|
-
await
|
|
186
|
-
expect(getByTestId('selected-option').textContent).toBe('readWrite')
|
|
187
|
-
})
|
|
128
|
+
await findByText('Copy link')
|
|
188
129
|
})
|
|
189
130
|
})
|
|
190
131
|
|
|
@@ -193,13 +134,11 @@ describe('FederatedFolderModal', () => {
|
|
|
193
134
|
|
|
194
135
|
it('should call onClose and skip share when there are no pending recipients', async () => {
|
|
195
136
|
// pendingRecipients defaults to [] via beforeEach
|
|
196
|
-
const {
|
|
137
|
+
const { findByText } = setup()
|
|
197
138
|
|
|
198
|
-
await
|
|
199
|
-
expect(getByTestId('btn-send')).toBeTruthy()
|
|
200
|
-
})
|
|
139
|
+
const sendButton = await findByText('Done')
|
|
201
140
|
|
|
202
|
-
fireEvent.click(
|
|
141
|
+
fireEvent.click(sendButton)
|
|
203
142
|
|
|
204
143
|
await waitFor(() => {
|
|
205
144
|
expect(mockOnClose).toHaveBeenCalled()
|
|
@@ -216,13 +155,11 @@ describe('FederatedFolderModal', () => {
|
|
|
216
155
|
})
|
|
217
156
|
getOrCreateFromArray.mockResolvedValue([pendingRecipient])
|
|
218
157
|
mockShare.mockResolvedValueOnce({})
|
|
219
|
-
const {
|
|
158
|
+
const { findByText } = setup()
|
|
220
159
|
|
|
221
|
-
await
|
|
222
|
-
expect(getByTestId('btn-send')).toBeTruthy()
|
|
223
|
-
})
|
|
160
|
+
const sendButton = await findByText('Done')
|
|
224
161
|
|
|
225
|
-
fireEvent.click(
|
|
162
|
+
fireEvent.click(sendButton)
|
|
226
163
|
|
|
227
164
|
await waitFor(() => {
|
|
228
165
|
expect(mockShare).toHaveBeenCalledWith({
|
|
@@ -245,13 +182,11 @@ describe('FederatedFolderModal', () => {
|
|
|
245
182
|
})
|
|
246
183
|
getOrCreateFromArray.mockResolvedValue([pendingRecipient])
|
|
247
184
|
mockShare.mockResolvedValueOnce({})
|
|
248
|
-
const {
|
|
185
|
+
const { findByText } = setup()
|
|
249
186
|
|
|
250
|
-
await
|
|
251
|
-
expect(getByTestId('btn-send')).toBeTruthy()
|
|
252
|
-
})
|
|
187
|
+
const sendButton = await findByText('Done')
|
|
253
188
|
|
|
254
|
-
fireEvent.click(
|
|
189
|
+
fireEvent.click(sendButton)
|
|
255
190
|
|
|
256
191
|
await waitFor(() => {
|
|
257
192
|
expect(mockShowAlert).toHaveBeenCalledWith({
|
|
@@ -272,13 +207,11 @@ describe('FederatedFolderModal', () => {
|
|
|
272
207
|
})
|
|
273
208
|
getOrCreateFromArray.mockResolvedValue([pendingRecipient])
|
|
274
209
|
mockShare.mockRejectedValueOnce(new Error('Share failed'))
|
|
275
|
-
const {
|
|
210
|
+
const { findByText } = setup()
|
|
276
211
|
|
|
277
|
-
await
|
|
278
|
-
expect(getByTestId('btn-send')).toBeTruthy()
|
|
279
|
-
})
|
|
212
|
+
const sendButton = await findByText('Done')
|
|
280
213
|
|
|
281
|
-
fireEvent.click(
|
|
214
|
+
fireEvent.click(sendButton)
|
|
282
215
|
|
|
283
216
|
await waitFor(() => {
|
|
284
217
|
expect(mockShowAlert).toHaveBeenCalledWith({
|
|
@@ -291,32 +224,13 @@ describe('FederatedFolderModal', () => {
|
|
|
291
224
|
})
|
|
292
225
|
})
|
|
293
226
|
|
|
294
|
-
describe('onRevoke callback', () => {
|
|
295
|
-
it('should call revoke from sharing context for existing members', async () => {
|
|
296
|
-
mockRevoke.mockResolvedValueOnce({})
|
|
297
|
-
const { getByTestId } = setup()
|
|
298
|
-
|
|
299
|
-
await waitFor(() => {
|
|
300
|
-
expect(getByTestId('dumb-modal')).toBeTruthy()
|
|
301
|
-
})
|
|
302
|
-
|
|
303
|
-
fireEvent.click(getByTestId('btn-revoke'))
|
|
304
|
-
|
|
305
|
-
await waitFor(() => {
|
|
306
|
-
expect(mockRevoke).toHaveBeenCalledWith({ _id: 'folder-123' }, 'abc', 1)
|
|
307
|
-
})
|
|
308
|
-
})
|
|
309
|
-
})
|
|
310
|
-
|
|
311
227
|
describe('onClose callback', () => {
|
|
312
228
|
it('should call onClose when close button is clicked', async () => {
|
|
313
|
-
const {
|
|
229
|
+
const { findByRole } = setup()
|
|
314
230
|
|
|
315
|
-
await
|
|
316
|
-
expect(getByTestId('btn-close')).toBeTruthy()
|
|
317
|
-
})
|
|
231
|
+
const closeButton = await findByRole('button', { name: /close/i })
|
|
318
232
|
|
|
319
|
-
fireEvent.click(
|
|
233
|
+
fireEvent.click(closeButton)
|
|
320
234
|
|
|
321
235
|
expect(mockOnClose).toHaveBeenCalled()
|
|
322
236
|
})
|
|
@@ -329,13 +243,11 @@ describe('FederatedFolderModal', () => {
|
|
|
329
243
|
setSelectedOption: jest.fn()
|
|
330
244
|
})
|
|
331
245
|
|
|
332
|
-
const { getByRole,
|
|
246
|
+
const { getByRole, findByRole, getByText } = setup()
|
|
333
247
|
|
|
334
|
-
await
|
|
335
|
-
expect(getByTestId('btn-close')).toBeTruthy()
|
|
336
|
-
})
|
|
248
|
+
const closeButton = await findByRole('button', { name: /close/i })
|
|
337
249
|
|
|
338
|
-
fireEvent.click(
|
|
250
|
+
fireEvent.click(closeButton)
|
|
339
251
|
|
|
340
252
|
expect(getByText("Discard the changes you haven't saved?")).toBeTruthy()
|
|
341
253
|
expect(mockOnClose).not.toHaveBeenCalled()
|
|
@@ -353,13 +265,11 @@ describe('FederatedFolderModal', () => {
|
|
|
353
265
|
setSelectedOption: jest.fn()
|
|
354
266
|
})
|
|
355
267
|
|
|
356
|
-
const { getByRole,
|
|
268
|
+
const { getByRole, findByRole, getByText } = setup()
|
|
357
269
|
|
|
358
|
-
await
|
|
359
|
-
expect(getByTestId('btn-close')).toBeTruthy()
|
|
360
|
-
})
|
|
270
|
+
const closeButton = await findByRole('button', { name: /close/i })
|
|
361
271
|
|
|
362
|
-
fireEvent.click(
|
|
272
|
+
fireEvent.click(closeButton)
|
|
363
273
|
|
|
364
274
|
expect(getByText("Discard the changes you haven't saved?")).toBeTruthy()
|
|
365
275
|
|
|
@@ -368,36 +278,4 @@ describe('FederatedFolderModal', () => {
|
|
|
368
278
|
expect(mockOnClose).toHaveBeenCalledTimes(1)
|
|
369
279
|
})
|
|
370
280
|
})
|
|
371
|
-
|
|
372
|
-
describe('existing recipients', () => {
|
|
373
|
-
const existingRecipients = [
|
|
374
|
-
{
|
|
375
|
-
index: 'sharing-abc-member-0',
|
|
376
|
-
name: 'Charlie',
|
|
377
|
-
email: 'charlie@example.com',
|
|
378
|
-
status: 'owner',
|
|
379
|
-
type: 'two-way',
|
|
380
|
-
sharingId: 'abc',
|
|
381
|
-
memberIndex: 0
|
|
382
|
-
},
|
|
383
|
-
{
|
|
384
|
-
index: 'sharing-abc-member-1',
|
|
385
|
-
name: 'Diana',
|
|
386
|
-
email: 'diana@example.com',
|
|
387
|
-
status: 'ready',
|
|
388
|
-
type: 'one-way',
|
|
389
|
-
sharingId: 'abc',
|
|
390
|
-
memberIndex: 1
|
|
391
|
-
}
|
|
392
|
-
]
|
|
393
|
-
|
|
394
|
-
it('should display existing recipients from getRecipients', async () => {
|
|
395
|
-
mockGetRecipients.mockReturnValue(existingRecipients)
|
|
396
|
-
const { getByTestId } = setup()
|
|
397
|
-
|
|
398
|
-
await waitFor(() => {
|
|
399
|
-
expect(getByTestId('recipients-count').textContent).toBe('2')
|
|
400
|
-
})
|
|
401
|
-
})
|
|
402
|
-
})
|
|
403
281
|
})
|
|
@@ -4,7 +4,6 @@ import { GroupRecipient } from './GroupRecipient'
|
|
|
4
4
|
import MemberRecipient from './MemberRecipient'
|
|
5
5
|
import { usePrevious } from '../../helpers/hooks'
|
|
6
6
|
import { filterAndReworkRecipients } from '../../helpers/recipients'
|
|
7
|
-
import SharedDriveRecipient from '../SharedDrive/SharedDriveRecipient'
|
|
8
7
|
|
|
9
8
|
const RecipientList = ({
|
|
10
9
|
recipients,
|
|
@@ -15,7 +14,6 @@ const RecipientList = ({
|
|
|
15
14
|
documentType,
|
|
16
15
|
onRevoke,
|
|
17
16
|
onRevokeSelf,
|
|
18
|
-
onSetType,
|
|
19
17
|
verifyRecipient
|
|
20
18
|
}) => {
|
|
21
19
|
const previousRecipients = usePrevious(recipients)
|
|
@@ -29,22 +27,6 @@ const RecipientList = ({
|
|
|
29
27
|
user => user.email === recipient.email
|
|
30
28
|
)
|
|
31
29
|
|
|
32
|
-
const isSharedDriveRecipient = recipient?.index
|
|
33
|
-
?.toString()
|
|
34
|
-
.startsWith('virtual-shared-drive')
|
|
35
|
-
|
|
36
|
-
if (isSharedDriveRecipient) {
|
|
37
|
-
return (
|
|
38
|
-
<SharedDriveRecipient
|
|
39
|
-
{...recipient}
|
|
40
|
-
key={recipient.index}
|
|
41
|
-
onRevoke={onRevoke}
|
|
42
|
-
onSetType={onSetType}
|
|
43
|
-
fadeIn={recipient.hasBeenJustAdded}
|
|
44
|
-
/>
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
30
|
const isGroupRecipient = recipient.members
|
|
49
31
|
if (isGroupRecipient) {
|
|
50
32
|
return (
|
|
@@ -34,15 +34,15 @@ export const ShareModal = withLocales(props => {
|
|
|
34
34
|
onRevokeSuccess?.(document)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
const isFederatedMode = flag('drive.federated-shared-folder.enabled')
|
|
38
|
-
if (isFederatedMode && allLoaded) {
|
|
39
|
-
return <FederatedFolderModal document={document} {...rest} />
|
|
40
|
-
}
|
|
41
|
-
|
|
42
37
|
const isEditable =
|
|
43
38
|
!byDocId[document.id] || isOwner(document.id) || canReshare(document.id)
|
|
44
39
|
|
|
45
40
|
if (isEditable) {
|
|
41
|
+
const isFederatedMode = flag('drive.federated-shared-folder.enabled')
|
|
42
|
+
if (isFederatedMode && allLoaded) {
|
|
43
|
+
return <FederatedFolderModal document={document} {...rest} />
|
|
44
|
+
}
|
|
45
|
+
|
|
46
46
|
return <EditableSharingModal document={document} {...rest} />
|
|
47
47
|
}
|
|
48
48
|
|
package/src/state.js
CHANGED
|
@@ -326,7 +326,11 @@ export const isSharedDrive = (state, docId) => {
|
|
|
326
326
|
export const canReshare = (state, docId, instanceUri) => {
|
|
327
327
|
const sharing = getDocumentSharing(state, docId)
|
|
328
328
|
const me = sharing.attributes.members.find(matchingInstanceName(instanceUri))
|
|
329
|
-
|
|
329
|
+
if (sharing.drive) {
|
|
330
|
+
return me && !me.read_only
|
|
331
|
+
} else {
|
|
332
|
+
return sharing.attributes.open_sharing === true && me && !me.read_only
|
|
333
|
+
}
|
|
330
334
|
}
|
|
331
335
|
|
|
332
336
|
export const getOwner = (state, docId) =>
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Bash(yarn test:*)",
|
|
5
|
-
"Bash(git stash)",
|
|
6
|
-
"Bash(git stash pop)",
|
|
7
|
-
"Bash(find /home/doubleface/Workspace/cozy-libs -path \"*/node_modules/cozy-client*\" -name \"*.js\" 2>/dev/null | head -3)",
|
|
8
|
-
"Bash(find /home/doubleface/Workspace/cozy-libs/packages -path \"*/cozy-client/src*\" -name \"*.js\" 2>/dev/null | xargs grep -l \"PermissionsCollection\\\\b\" 2>/dev/null | head -3)",
|
|
9
|
-
"Bash(find /home/doubleface/Workspace/cozy-libs/packages/cozy-client -name \"PermissionsCollection*\" 2>/dev/null | head -5)",
|
|
10
|
-
"Bash(find /home/doubleface/Workspace/cozy-libs/packages/cozy-sharing/node_modules -path \"*/cozy-client/dist*\" -name \"collections.js\" 2>/dev/null | head -3)",
|
|
11
|
-
"Bash(find /home/doubleface/Workspace/cozy-libs/packages/cozy-sharing/node_modules -name \"*.js\" 2>/dev/null | xargs grep -l \"PermissionsCollection\" 2>/dev/null | head -5)",
|
|
12
|
-
"Bash(find /home/doubleface/Workspace/cozy-libs/node_modules -name \"*.js\" 2>/dev/null | xargs grep -l \"PermissionsCollection\" 2>/dev/null | head -5)",
|
|
13
|
-
"Bash(find /home/doubleface/.cache/yarn -name \"*.js\" 2>/dev/null | xargs grep -l \"PermissionsCollection\" 2>/dev/null | head -3)",
|
|
14
|
-
"Bash(find /home/doubleface/.cache/yarn -name \"*.js\" 2>/dev/null | xargs grep -l \"class PermissionsCollection\" 2>/dev/null | head -3)",
|
|
15
|
-
"Bash(find /home/doubleface/.cache/yarn -path \"*cozy-client-60*\" -name \"*.js\" 2>/dev/null | head -5; find /home/doubleface/.cache/yarn -name \"package.json\" 2>/dev/null | xargs grep -l '\"name\": \"cozy-client\"' 2>/dev/null | head -3)",
|
|
16
|
-
"Bash(npx jest:*)",
|
|
17
|
-
"Bash(git:*)",
|
|
18
|
-
"Bash(grep -v \"^$\")",
|
|
19
|
-
"Bash(grep -v \"^\\\\-\\\\-$\")",
|
|
20
|
-
"Bash(grep -v \"console\\\\.\\\\|^--$\\\\|JSS\\\\|CozyThemeContext\")"
|
|
21
|
-
]
|
|
22
|
-
},
|
|
23
|
-
"sandbox": {
|
|
24
|
-
"enabled": true,
|
|
25
|
-
"autoAllowBashIfSandboxed": true
|
|
26
|
-
}
|
|
27
|
-
}
|
package/AGENTS.md
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
- Package manager: `yarn`.
|
|
2
|
-
- Run all tests: `corepack yarn test`
|
|
3
|
-
- Run a single test file: `corepack yarn test path/to/file.spec.js`
|
|
4
|
-
- Run eslint (with fix): `corepack yarn lint --fix`
|
|
5
|
-
- Run eslint (with fix) on one file: `corepack yarn lint --fix path/to/file.js`
|
|
6
|
-
|
|
7
|
-
No need to build the package. The user will handle this
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { mount } from 'enzyme';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
import { createMockClient } from 'cozy-client';
|
|
4
|
-
import { RefreshableSharings } from './RefreshableSharings';
|
|
5
|
-
import { SharingProvider } from './SharingProvider';
|
|
6
|
-
import AppLike from '../test/AppLike';
|
|
7
|
-
|
|
8
|
-
var DumbComponent = function DumbComponent() {
|
|
9
|
-
return /*#__PURE__*/React.createElement("div", null, "test ");
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
var AppWrapper = function AppWrapper(_ref) {
|
|
13
|
-
var children = _ref.children,
|
|
14
|
-
client = _ref.client;
|
|
15
|
-
return /*#__PURE__*/React.createElement(AppLike, {
|
|
16
|
-
client: client
|
|
17
|
-
}, /*#__PURE__*/React.createElement(SharingProvider, {
|
|
18
|
-
client: client
|
|
19
|
-
}, children));
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
describe('RefreshableSharings', function () {
|
|
23
|
-
it('should test', function () {
|
|
24
|
-
var client = createMockClient({});
|
|
25
|
-
var component = mount( /*#__PURE__*/React.createElement(AppWrapper, {
|
|
26
|
-
client: client
|
|
27
|
-
}, /*#__PURE__*/React.createElement(RefreshableSharings, null, function (_ref2) {
|
|
28
|
-
var refresh = _ref2.refresh;
|
|
29
|
-
return /*#__PURE__*/React.createElement(DumbComponent, {
|
|
30
|
-
client: client,
|
|
31
|
-
t: function t(x) {
|
|
32
|
-
return x;
|
|
33
|
-
},
|
|
34
|
-
refreshSharings: refresh
|
|
35
|
-
});
|
|
36
|
-
})));
|
|
37
|
-
var refreshMethod = component.find(DumbComponent).prop('refreshSharings');
|
|
38
|
-
expect(typeof refreshMethod).toBe('function');
|
|
39
|
-
});
|
|
40
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg width="200" height="136" viewBox="0 0 200 136" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M161 14.592c-1.888.222-7.568-.664-15.182-5.982-9.518-6.647-13.847-8.724-22.434-4.183C115 8.86 115.227 23.012 115 27" stroke="#CEBEF7" stroke-width="4" stroke-linecap="round"/><path d="M165.844 92c5.431-3.51 16.097-12.335 15.315-19.555-.977-9.025-12.708-11.281-22.81-10.78-10.101.502-17.596 4.513-28.349 1.504" stroke="#B9F6F7" stroke-width="4" stroke-linecap="round"/><path d="M23.495 72c-.329 4.729-2.425 21.765 5.479 27.308 9.88 6.928 23.668-1.244 30.831-5.203C66.97 90.146 79.072 83.217 85 85.527" stroke="#FFD799" stroke-width="4" stroke-linecap="round"/><circle cx="100" cy="64" fill="#297EF2" r="48"/><circle cx="148" cy="104" r="24" fill="#B9F6F7"/><path fill-rule="evenodd" clip-rule="evenodd" d="M148 106c-4.418 0-8-4.029-8-9 0-4.97 3.582-9 8-9s8 4.03 8 9c0 4.971-3.582 9-8 9Zm-16 10c0-2 4-8 8-8s2 2 8 2 4-2 8-2 8 6 8 8 0 4-2 4h-28c-2 0-2-2-2-4Z" fill="#38D1D3"/><circle cx="148" cy="104" r="20" stroke="#B9F6F7" stroke-width="8"/><circle cx="24" cy="65" r="24" fill="#FFD799"/><path fill-rule="evenodd" clip-rule="evenodd" d="M24 67c-4.418 0-8-4.03-8-9s3.582-9 8-9 8 4.03 8 9-3.582 9-8 9ZM8 77c0-2 4-8 8-8s2 2 8 2 4-2 8-2 8 6 8 8 0 4-2 4H10c-2 0-2-2-2-4Z" fill="#FF9300"/><circle cx="24" cy="65" r="20" stroke="#FFD799" stroke-width="8"/><circle cx="172" cy="28" r="24" fill="#CEBEF7"/><path fill-rule="evenodd" clip-rule="evenodd" d="M172 30c-4.418 0-8-4.03-8-9s3.582-9 8-9 8 4.03 8 9-3.582 9-8 9Zm-16 10c0-2 4-8 8-8s2 2 8 2 4-2 8-2 8 6 8 8 0 4-2 4h-28c-2 0-2-2-2-4Z" fill="#855CEA"/><circle cx="172" cy="28" r="20" stroke="#CEBEF7" stroke-width="8"/><path d="M66.418 133.29a81.818 81.818 0 0 0 16.142 2.244c16.166.948 38.758.424 48.325-1.346 9.567-1.771 10.083-4.465-14.448-5.812-20.925-1.147-45.26.474-50.362 2.894-1.52.648-1.447 1.371.343 2.02Z" fill="#BBD9FD"/><path d="M80.198 64.752a6 6 0 0 1 4.686-2.252h30.232c1.823 0 3.547.828 4.685 2.252l3.213 4.015c.638.798.986 1.79.986 2.812V76H76v-4.421a4.5 4.5 0 0 1 .986-2.812l3.212-4.015ZM80.198 43.752a6 6 0 0 1 4.686-2.252h30.232c1.823 0 3.547.828 4.685 2.252l3.213 4.015c.638.798.986 1.79.986 2.811V55H76v-4.422a4.5 4.5 0 0 1 .986-2.81l3.212-4.016Z" fill="#fff"/><path d="M76 71.5a3 3 0 0 1 3-3h42a3 3 0 0 1 3 3v12a3 3 0 0 1-3 3H79a3 3 0 0 1-3-3v-12ZM76 50.5a3 3 0 0 1 3-3h42a3 3 0 0 1 3 3v12a3 3 0 0 1-3 3H79a3 3 0 0 1-3-3v-12Z" fill="#B2D3FF"/><path d="M88 77.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0ZM88 56.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" fill="#fff"/><path d="M194.751 99.667c1.695-.06 1.635-2.667-.06-2.667-.303 0-.606 0-.909.182v-.849c-.06-.727-.544-1.333-1.331-1.333-.666 0-1.393.606-1.332 1.333v.728h-.303c-.181-.061-.303 0-.484 0-.908 0-1.332.909-1.332 1.575 0 .667.363.97.908 1.152.363.182.787.06 1.211.06 0 .182.06.364.06.607.061 1.757 2.785 1.757 2.664 0 0-.182 0-.364-.061-.546.303-.121.666-.121.969-.242ZM48.112 111.445c1.212-.045 1.17-1.968-.043-1.968-.216 0-.433 0-.65.134v-.627c-.043-.537-.389-.984-.952-.984-.476 0-.995.447-.952.984v.537H45.3c-.13-.044-.217 0-.347 0-.649 0-.952.671-.952 1.164 0 .492.26.715.65.85.259.134.562.044.865.044 0 .135.043.269.043.448.044 1.297 1.992 1.297 1.905 0 0-.134 0-.269-.043-.403.216-.089.476-.089.692-.179ZM176.04 97.024c.121.424.424.788.666.97.666.424 1.695.303 2.179-.425.484-.666.545-1.757-.484-2.363-.908-.546-1.998.06-2.301 1.03-.121.242-.121.485-.06.788Z" fill="#BBD9FD"/><path d="M185.045 86.024c.121.424.424.788.666.97.666.424 1.695.303 2.18-.425.484-.666.544-1.757-.545-2.363-.908-.546-1.998.06-2.301 1.03-.06.242-.06.545 0 .788ZM28.09 29.576c1.695-.06 1.634-2.667-.061-2.667-.303 0-.605 0-.908.182V26.243c-.06-.788-.545-1.394-1.332-1.394-.666 0-1.392.606-1.332 1.333v.727h-.303c-.181-.06-.302 0-.484 0-.908 0-1.332.91-1.332 1.576 0 .667.364.97.908 1.152.364.181.787.06 1.211.06 0 .182.06.364.06.606.061 1.758 2.785 1.758 2.664 0 0-.182 0-.364-.06-.545.303 0 .666-.06.968-.182ZM78.751 8.667C80.446 8.607 80.386 6 78.691 6c-.303 0-.606 0-.909.182v-.849C77.722 4.606 77.239 4 76.451 4c-.666 0-1.393.606-1.332 1.333v.728h-.303c-.181-.061-.303 0-.484 0-.908 0-1.332.909-1.332 1.575 0 .667.363.97.908 1.152.363.182.787.06 1.21.06 0 .182.061.364.061.607.06 1.757 2.785 1.757 2.664 0 0-.182 0-.364-.06-.546.302-.121.665-.121.968-.242Z" fill="#3281EE"/><path d="M107.751 10.667c1.695-.06 1.635-2.667-.06-2.667-.303 0-.606 0-.909.182v-.849c-.06-.727-.544-1.333-1.331-1.333-.666 0-1.393.606-1.332 1.333v.728h-.303c-.181-.061-.303 0-.484 0-.908 0-1.332.909-1.332 1.575 0 .667.363.97.908 1.152.363.182.787.06 1.211.06 0 .182.06.364.06.606.061 1.758 2.785 1.758 2.664 0 0-.181 0-.363-.061-.545.303-.06.666-.121.969-.242ZM21.612 7.879h-1.634v-1.03c0-2.304-3.633-2.304-3.572 0 0 .424 0 .848.06 1.272-.181 0-.847-.06-.968-.121-2.18-.727-3.148 2.727-.969 3.454.545.182 1.574.182 2.119.182v.788c0 2.303 3.632 2.303 3.572 0v-.97c.484 0 .968-.06 1.513-.06 2.18.06 2.18-3.515-.12-3.515ZM8.9 32.485c.12.424.423.788.665.97.666.424 1.695.303 2.18-.425.484-.666.544-1.757-.485-2.363-.908-.546-1.998.06-2.3 1.03-.121.242-.121.546-.06.788ZM44.04 41.024c.12.424.424.788.666.97.666.424 1.695.303 2.179-.425.484-.666.545-1.757-.484-2.363-.908-.546-1.998.06-2.3 1.03-.122.242-.122.546-.061.788ZM58.066 16.024c.121.424.424.788.666.97.666.424 1.695.303 2.18-.425.484-.666.544-1.757-.485-2.363-.908-.546-1.998.06-2.3 1.03a.87.87 0 0 0-.061.788Z" fill="#BBD9FD"/><path d="M43.89 7.576c.12.424.423.788.666.97.666.424 1.695.302 2.179-.425.484-.667.545-1.757-.545-2.363-.908-.546-1.998.06-2.3 1.03-.06.242-.06.545 0 .788ZM84.026 28.97c.12.424.424.787.666.969.666.424 1.695.303 2.18-.424.483-.667.544-1.758-.546-2.364-.908-.545-1.997.06-2.3 1.03a1.73 1.73 0 0 0 0 .788Z" fill="#3281EE"/><path d="M20.04 107.024c.12.424.424.788.666.97.666.424 1.695.303 2.179-.425.484-.666.545-1.757-.484-2.363-.908-.546-1.998.06-2.3 1.03-.122.242-.122.546-.061.788ZM5.04 88.024c.12.424.424.788.666.97.666.424 1.695.303 2.179-.425.484-.666.545-1.757-.484-2.363-.908-.546-1.998.06-2.3 1.03-.122.242-.122.545-.061.788Z" fill="#BBD9FD"/><path d="M191.311 54.758h-1.634v-1.03c0-2.304-3.632-2.304-3.572 0 0 .423 0 .848.061 1.272-.182 0-.848-.06-.969-.121-2.179-.727-3.148 2.727-.969 3.454.545.182 1.574.182 2.119.182v.788c0 2.303 3.633 2.303 3.572 0v-.97c.484 0 .969-.06 1.513-.06 2.24.06 2.24-3.515-.121-3.515Z" fill="#3281EE"/><path d="M136.045 21.024c.121.424.424.788.666.97.666.424 1.695.303 2.18-.425.484-.666.544-1.757-.545-2.363-.908-.546-1.998.06-2.301 1.03-.06.242-.06.485 0 .788Z" fill="#BBD9FD"/><path d="M129.045 10.024c.121.424.424.788.666.97.666.424 1.695.303 2.18-.425.484-.666.545-1.757-.545-2.363-.908-.546-1.998.06-2.301 1.03-.06.242-.06.546 0 .788ZM46.07 26.024c.121.424.424.788.666.97.666.424 1.695.303 2.18-.425.484-.666.544-1.757-.546-2.363-.908-.546-1.997.06-2.3 1.03-.06.242-.121.546 0 .788ZM11.751 99.667c1.695-.06 1.635-2.667-.06-2.667-.303 0-.606 0-.909.182v-.849c-.06-.727-.544-1.333-1.331-1.333-.666 0-1.393.606-1.332 1.333v.728h-.303c-.181-.061-.303 0-.484 0-.908 0-1.332.909-1.332 1.575 0 .667.363.97.908 1.152.363.182.787.06 1.21.06 0 .182.061.364.061.607.06 1.757 2.785 1.757 2.664 0 0-.182 0-.364-.06-.546.363-.06.665-.121.968-.242ZM164.751 74.667c1.695-.06 1.635-2.667-.06-2.667-.303 0-.606 0-.909.182v-.849c-.06-.727-.544-1.333-1.331-1.333-.666 0-1.393.606-1.332 1.333v.728h-.303c-.181-.061-.303 0-.484 0-.908 0-1.332.909-1.332 1.575 0 .667.363.97.908 1.152.363.182.787.06 1.211.06 0 .182.06.364.06.606.061 1.758 2.785 1.758 2.664 0 0-.181 0-.363-.061-.545.364-.06.666-.121.969-.242ZM153.045 56.024c.121.424.424.788.666.97.666.424 1.695.303 2.18-.425.484-.666.545-1.757-.545-2.363-.908-.546-1.998.06-2.301 1.03-.06.242-.06.546 0 .788Z" fill="#3281EE"/><path d="M193.07 73.024c.121.424.424.788.666.97.666.424 1.695.303 2.179-.425.484-.666.545-1.757-.545-2.363-.908-.546-1.997.06-2.3 1.03-.061.242-.121.545 0 .788ZM194.751 7.667c1.695-.06 1.635-2.667-.06-2.667-.303 0-.606 0-.909.182v-.849c-.06-.727-.544-1.333-1.331-1.333-.666 0-1.393.606-1.332 1.333v.728h-.303c-.181-.061-.303 0-.484 0-.908 0-1.332.909-1.332 1.575 0 .667.363.97.908 1.152.363.182.787.06 1.211.06 0 .182.06.364.06.607.061 1.757 2.785 1.757 2.664 0 0-.182 0-.364-.061-.546.303-.121.666-.182.969-.242Z" fill="#BBD9FD"/><path d="M49.751 89.667c1.695-.06 1.635-2.667-.06-2.667-.303 0-.606 0-.909.182v-.849c-.06-.727-.544-1.333-1.331-1.333-.666 0-1.393.606-1.332 1.333v.728h-.303c-.181-.061-.303 0-.484 0-.908 0-1.332.909-1.332 1.575 0 .667.363.97.908 1.152.363.182.787.06 1.21.06 0 .182.061.364.061.606.06 1.758 2.785 1.758 2.664 0 0-.181 0-.363-.06-.545.302-.121.665-.182.968-.242ZM59.045 105.024c.121.424.424.788.666.97.666.424 1.695.303 2.18-.425.484-.666.544-1.757-.545-2.363-.908-.546-1.998.06-2.3 1.03-.061.243-.061.546 0 .788Z" fill="#3281EE"/></svg>
|