Skip to content

Commit e73d73a

Browse files
authored
Merge pull request #647 from caesar-team/feature/CAES-1526-fix-error-when-download-attach-to-existing-item
Feature/caes 1526 fix error when download attach to existing item
2 parents 421af61 + a078437 commit e73d73a

File tree

5 files changed

+193
-108
lines changed

5 files changed

+193
-108
lines changed

packages/common/validation/schema.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,22 @@ const WEBSITE_MAX_LENGTH = 2048;
1414
// eslint-disable-next-line no-useless-escape
1515
const rUrl = /(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i;
1616

17+
export const attachmentSchema = yup.object({
18+
size: yup
19+
.number()
20+
.test(
21+
'fileSize',
22+
size =>
23+
ERROR.FILE_SIZE(
24+
humanizeSize(
25+
size.value ? getRealFileSizeForBase64enc(size.value) : 0,
26+
true,
27+
),
28+
),
29+
checkFileSize,
30+
),
31+
});
32+
1733
const attachmentsSchema = yup
1834
.array(
1935
yup.object({
@@ -63,5 +79,7 @@ export const SCHEMA = {
6379
excludeEmptyString: true,
6480
})
6581
.max(WEBSITE_MAX_LENGTH, ERROR.MAX_LENGTH(WEBSITE_MAX_LENGTH)),
82+
ATTACHMENT: attachmentSchema,
6683
ATTACHMENTS: attachmentsSchema,
84+
RAWS: yup.string(),
6785
};

packages/common/validation/utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const convertSizeNameToNumber = sizeName =>
2020
);
2121

2222
export const checkFileSize = raw =>
23-
raw.length * BASE_64_LENGTH_BYTE_RATE <=
23+
(typeof raw === 'number' ? raw : raw.length) * BASE_64_LENGTH_BYTE_RATE <=
2424
convertSizeNameToNumber(MAX_UPLOADING_FILE_SIZE);
2525

2626
export const checkAllFileSizes = files =>

packages/components/CreateForm/utils/getValidationSchema.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const getValidationSchema = type => {
1313
website: SCHEMA.WEBSITE,
1414
note: yup.string(),
1515
attachments: SCHEMA.ATTACHMENTS,
16+
raws: SCHEMA.RAWS,
1617
});
1718
case ITEM_TYPE.DOCUMENT:
1819
return yup.object({

packages/components/ItemFields/view/Attachments.js

Lines changed: 168 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import React, { useState, useEffect } from 'react';
1+
import React, { useState, useEffect, memo } from 'react';
22
import { useUpdateEffect } from 'react-use';
33
import { useDispatch } from 'react-redux';
44
import equal from 'fast-deep-equal';
55
import { getUniqueAndDublicates } from '@caesar/common/utils/file';
66
import { PERMISSION } from '@caesar/common/constants';
7+
import { SCHEMA } from '@caesar/common/validation';
78
import { isIterable } from '@caesar/common/utils/utils';
89
import { processUploadedFiles } from '@caesar/common/utils/attachment';
910
import {
@@ -30,94 +31,21 @@ const MODAL = {
3031
DELETE_FILE: 'delete_file',
3132
};
3233

33-
export const Attachments = ({
34-
itemId,
35-
attachments = [],
36-
raws = {},
37-
itemSubject,
38-
onClickAcceptEdit,
39-
isVisibleDragZone,
40-
}) => {
41-
const dispatch = useDispatch();
42-
const [newFiles, setNewFiles] = useState([]);
43-
const [itemRaws, setItemRaws] = useState(raws);
44-
const [itemAttachments, setItemAttachments] = useState(attachments);
45-
const [openedModal, setOpenedModal] = useState(null);
46-
47-
const syncStateWithServer = newItemData => {
48-
onClickAcceptEdit(newItemData);
49-
};
50-
51-
useUpdateEffect(() => {
52-
if (!equal(attachments, itemAttachments)) {
53-
setItemAttachments(attachments);
54-
}
55-
}, [attachments]);
56-
57-
useEffect(() => {
58-
if (raws && Object.keys(raws)?.length > 0) {
59-
setItemRaws(raws);
60-
}
61-
}, [raws, setItemRaws]); // This will only run when one of those variables change
62-
63-
// TODO: Add loader if raws are not ready
64-
const handleClickDownloadFile = attachment => {
65-
dispatch(downloadItemAttachment({ itemId, attachment }));
66-
};
67-
68-
const handleClickDownloadAll = () => {
69-
dispatch(downloadItemAttachments({ itemId }));
70-
};
71-
72-
const onClickRemove = handleAttachment => {
73-
const attachmentIndex = itemAttachments.findIndex(
74-
attachment => attachment.id === handleAttachment.id,
75-
);
76-
77-
itemAttachments.splice(attachmentIndex, 1);
78-
delete itemRaws[handleAttachment.id];
79-
80-
setItemAttachments(itemAttachments);
81-
setItemRaws(itemRaws);
82-
syncStateWithServer({
83-
attachments: itemAttachments,
84-
raws: itemRaws,
85-
});
86-
};
87-
88-
const handleChange = (name, files) => {
89-
const splitedFiles = processUploadedFiles(files);
90-
91-
const { uniqNewFiles, duplicatedFiles } = getUniqueAndDublicates(
92-
[...splitedFiles.attachments],
93-
[...itemAttachments],
94-
);
95-
96-
const uploadedDuplicatedFiles = duplicatedFiles.map(file => ({
97-
...file,
98-
error: 'The file already exists',
99-
}));
100-
101-
const uniqNewRaws = Object.fromEntries(
102-
uniqNewFiles.map(file => [file.id, splitedFiles.raws[file.id]]),
103-
);
104-
105-
const allAttachments = [...itemAttachments, ...uniqNewFiles];
106-
const allRaws = { ...itemRaws, ...uniqNewRaws };
107-
108-
setNewFiles([...uploadedDuplicatedFiles, ...uniqNewFiles]);
109-
110-
setItemRaws(allRaws);
111-
setItemAttachments(allAttachments);
112-
113-
setOpenedModal(MODAL.NEW_FILES);
114-
syncStateWithServer({
115-
attachments: allAttachments,
116-
raws: allRaws,
117-
});
118-
};
119-
120-
const AttachmentsComponent = () => (
34+
const AttachmentsComponent = memo(
35+
({
36+
attachments,
37+
itemAttachments,
38+
newFiles,
39+
itemSubject,
40+
isVisibleDragZone,
41+
openedModal,
42+
setOpenedModal,
43+
handleChange,
44+
handleClickDownloadAll,
45+
handleClickDownloadFile,
46+
onClickAcceptEdit,
47+
onClickRemove,
48+
}) => (
12149
<Wrapper>
12250
<Title>
12351
Attachments ({attachments?.length ? attachments.length : 0})
@@ -202,13 +130,161 @@ export const Attachments = ({
202130
/>
203131
)}
204132
</Wrapper>
205-
);
133+
),
134+
);
135+
136+
export const Attachments = ({
137+
itemId,
138+
attachments = [],
139+
raws = {},
140+
itemSubject,
141+
onClickAcceptEdit,
142+
isVisibleDragZone,
143+
}) => {
144+
const dispatch = useDispatch();
145+
const [newFiles, setNewFiles] = useState([]);
146+
const [itemRaws, setItemRaws] = useState(raws);
147+
const [itemAttachments, setItemAttachments] = useState(attachments);
148+
const [openedModal, setOpenedModal] = useState(null);
149+
150+
const syncStateWithServer = newItemData => {
151+
onClickAcceptEdit(newItemData);
152+
};
153+
154+
useUpdateEffect(() => {
155+
if (!equal(attachments, itemAttachments)) {
156+
setItemAttachments(attachments);
157+
}
158+
}, [attachments]);
159+
160+
useEffect(() => {
161+
if (raws && Object.keys(raws)?.length > 0) {
162+
setItemRaws(raws);
163+
}
164+
}, [raws, setItemRaws]); // This will only run when one of those variables change
165+
166+
// TODO: Add loader if raws are not ready
167+
const handleClickDownloadFile = attachment => {
168+
dispatch(downloadItemAttachment({ itemId, attachment }));
169+
};
170+
171+
const handleClickDownloadAll = () => {
172+
dispatch(downloadItemAttachments({ itemId }));
173+
};
174+
175+
const onClickRemove = handleAttachment => {
176+
const attachmentIndex = itemAttachments.findIndex(
177+
attachment => attachment.id === handleAttachment.id,
178+
);
179+
180+
itemAttachments.splice(attachmentIndex, 1);
181+
delete itemRaws[handleAttachment.id];
182+
183+
setItemAttachments(itemAttachments);
184+
setItemRaws(itemRaws);
185+
syncStateWithServer({
186+
attachments: itemAttachments,
187+
raws: itemRaws,
188+
});
189+
};
190+
191+
const handleChange = (name, files) => {
192+
const splitedFiles = processUploadedFiles(files);
193+
194+
const {
195+
validated: validatedFiles,
196+
errored: erroredFiles,
197+
} = splitedFiles.attachments.reduce(
198+
(acc, file) => {
199+
try {
200+
SCHEMA.ATTACHMENT.validateSync(file);
201+
} catch (error) {
202+
// eslint-disable-next-line no-param-reassign
203+
file.error = error.message;
204+
}
205+
206+
if (file.error) {
207+
return {
208+
...acc,
209+
errored: [...acc.errored, file],
210+
};
211+
}
212+
213+
return {
214+
...acc,
215+
validated: [...acc.validated, file],
216+
};
217+
},
218+
{
219+
validated: [],
220+
errored: [],
221+
},
222+
);
223+
224+
const { uniqNewFiles, duplicatedFiles } = getUniqueAndDublicates(
225+
validatedFiles,
226+
itemAttachments,
227+
);
228+
229+
const processedDuplicatedFiles = duplicatedFiles.map(file => ({
230+
...file,
231+
error: 'The file already exists',
232+
}));
233+
234+
const uniqNewRaws = Object.fromEntries(
235+
uniqNewFiles.map(file => [file.id, splitedFiles.raws[file.id]]),
236+
);
237+
238+
const allAttachments = [...itemAttachments, ...uniqNewFiles];
239+
const allRaws = { ...itemRaws, ...uniqNewRaws };
240+
241+
setNewFiles([
242+
...erroredFiles,
243+
...processedDuplicatedFiles,
244+
...uniqNewFiles,
245+
]);
246+
247+
setItemRaws(allRaws);
248+
setItemAttachments(allAttachments);
249+
250+
setOpenedModal(MODAL.NEW_FILES);
251+
syncStateWithServer({
252+
attachments: allAttachments,
253+
raws: allRaws,
254+
});
255+
};
206256

207257
return Array.isArray(itemAttachments) && itemAttachments.length === 0 ? (
208258
<Can I={PERMISSION.EDIT} an={itemSubject}>
209-
<AttachmentsComponent />
259+
<AttachmentsComponent
260+
attachments={attachments}
261+
itemAttachments={itemAttachments}
262+
newFiles={newFiles}
263+
itemSubject={itemSubject}
264+
isVisibleDragZone={isVisibleDragZone}
265+
openedModal={openedModal}
266+
setOpenedModal={setOpenedModal}
267+
handleChange={handleChange}
268+
handleClickDownloadAll={handleClickDownloadAll}
269+
handleClickDownloadFile={handleClickDownloadFile}
270+
onClickAcceptEdit={onClickAcceptEdit}
271+
onClickRemove={onClickRemove}
272+
/>
210273
</Can>
211274
) : (
212-
<AttachmentsComponent />
275+
<AttachmentsComponent
276+
attachments={attachments}
277+
itemAttachments={itemAttachments}
278+
newFiles={newFiles}
279+
itemSubject={itemSubject}
280+
isVisibleDragZone={isVisibleDragZone}
281+
openedModal={openedModal}
282+
setOpenedModal={setOpenedModal}
283+
handleChange={handleChange}
284+
handleClickDownloadAll={handleClickDownloadAll}
285+
handleClickDownloadFile={handleClickDownloadFile}
286+
onClickAcceptEdit={onClickAcceptEdit}
287+
onClickRemove={onClickRemove}
288+
/>
213289
);
214290
};

packages/containers/Preferences/Preferences.js

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const ButtonStyled = styled(Button)`
1212
`;
1313

1414
const ResetCacheButton = styled(ButtonStyled)`
15+
align-self: flex-start;
1516
margin-right: 0;
1617
`;
1718

@@ -25,21 +26,10 @@ export const PreferencesContainer = () => {
2526
};
2627

2728
return (
28-
<SettingsWrapper
29-
title="Preferences"
30-
addonTopComponent={
31-
<>
32-
<ResetCacheButton
33-
onClick={handleClearCache}
34-
icon="update"
35-
color="black"
36-
>
37-
Reset Application Cache
38-
</ResetCacheButton>
39-
</>
40-
}
41-
>
42-
<>¯\_(ツ)_/¯</>
29+
<SettingsWrapper title="Preferences">
30+
<ResetCacheButton onClick={handleClearCache} icon="update" color="black">
31+
Reset Application Cache
32+
</ResetCacheButton>
4333
</SettingsWrapper>
4434
);
4535
};

0 commit comments

Comments
 (0)