313 lines
11 KiB
JavaScript
313 lines
11 KiB
JavaScript
import React from 'react';
|
|
import Icon from '/imports/ui/components/common/icon/component';
|
|
import Styled from '/imports/ui/components/presentation/presentation-uploader/styles';
|
|
import { toast } from 'react-toastify';
|
|
import { defineMessages } from 'react-intl';
|
|
|
|
const TIMEOUT_CLOSE_TOAST = 1; // second
|
|
|
|
const intlMessages = defineMessages({
|
|
item: {
|
|
id: 'app.presentationUploder.item',
|
|
description: 'single item label',
|
|
},
|
|
itemPlural: {
|
|
id: 'app.presentationUploder.itemPlural',
|
|
description: 'plural item label',
|
|
},
|
|
uploading: {
|
|
id: 'app.presentationUploder.uploading',
|
|
description: 'uploading label for toast notification',
|
|
},
|
|
uploadStatus: {
|
|
id: 'app.presentationUploder.uploadStatus',
|
|
description: 'upload status for toast notification',
|
|
},
|
|
completed: {
|
|
id: 'app.presentationUploder.completed',
|
|
description: 'uploads complete label for toast notification',
|
|
},
|
|
GENERATING_THUMBNAIL: {
|
|
id: 'app.presentationUploder.conversion.generatingThumbnail',
|
|
description: 'indicatess that it is generating thumbnails',
|
|
},
|
|
GENERATING_SVGIMAGES: {
|
|
id: 'app.presentationUploder.conversion.generatingSvg',
|
|
description: 'warns that it is generating svg images',
|
|
},
|
|
GENERATED_SLIDE: {
|
|
id: 'app.presentationUploder.conversion.generatedSlides',
|
|
description: 'warns that were slides generated',
|
|
},
|
|
413: {
|
|
id: 'app.presentationUploder.upload.413',
|
|
description: 'error that file exceed the size limit',
|
|
},
|
|
CONVERSION_TIMEOUT: {
|
|
id: 'app.presentationUploder.conversion.conversionTimeout',
|
|
description: 'warns the user that the presentation timed out in the back-end in specific page of the document',
|
|
},
|
|
FILE_TOO_LARGE: {
|
|
id: 'app.presentationUploder.upload.413',
|
|
description: 'error that file exceed the size limit',
|
|
},
|
|
INVALID_MIME_TYPE: {
|
|
id: 'app.presentationUploder.conversion.invalidMimeType',
|
|
description: 'warns user that the file\'s mime type is not supported or it doesn\'t match the extension',
|
|
},
|
|
PAGE_COUNT_EXCEEDED: {
|
|
id: 'app.presentationUploder.conversion.pageCountExceeded',
|
|
description: 'warns the user that the conversion failed because of the page count',
|
|
},
|
|
PDF_HAS_BIG_PAGE: {
|
|
id: 'app.presentationUploder.conversion.pdfHasBigPage',
|
|
description: 'warns the user that the conversion failed because of the pdf page siz that exceeds the allowed limit',
|
|
},
|
|
OFFICE_DOC_CONVERSION_INVALID: {
|
|
id: 'app.presentationUploder.conversion.officeDocConversionInvalid',
|
|
description: '',
|
|
},
|
|
OFFICE_DOC_CONVERSION_FAILED: {
|
|
id: 'app.presentationUploder.conversion.officeDocConversionFailed',
|
|
description: 'warns the user that the conversion failed because of wrong office file',
|
|
},
|
|
UNSUPPORTED_DOCUMENT: {
|
|
id: 'app.presentationUploder.conversion.unsupportedDocument',
|
|
description: 'warns the user that the file extension is not supported',
|
|
},
|
|
204: {
|
|
id: 'app.presentationUploder.conversion.204',
|
|
description: 'error indicating that the file has no content to capture',
|
|
},
|
|
fileToUpload: {
|
|
id: 'app.presentationUploder.fileToUpload',
|
|
description: 'message used in the file selected for upload',
|
|
},
|
|
uploadProcess: {
|
|
id: 'app.presentationUploder.upload.progress',
|
|
description: 'message that indicates the percentage of the upload',
|
|
},
|
|
badConnectionError: {
|
|
id: 'app.presentationUploder.connectionClosedError',
|
|
description: 'message indicating that the connection was closed',
|
|
},
|
|
conversionProcessingSlides: {
|
|
id: 'app.presentationUploder.conversion.conversionProcessingSlides',
|
|
description: 'indicates how many slides were converted',
|
|
},
|
|
genericError: {
|
|
id: 'app.presentationUploder.genericError',
|
|
description: 'generic error while uploading/converting',
|
|
},
|
|
genericConversionStatus: {
|
|
id: 'app.presentationUploder.conversion.genericConversionStatus',
|
|
description: 'indicates that file is being converted',
|
|
},
|
|
});
|
|
|
|
function renderPresentationItemStatus(item, intl) {
|
|
if ((('progress' in item) && item.progress === 0) || (('upload' in item) && item.upload.progress === 0 && !item.upload.error)) {
|
|
return intl.formatMessage(intlMessages.fileToUpload);
|
|
}
|
|
|
|
if (('progress' in item) && item.progress < 100 && !('conversion' in item)) {
|
|
return intl.formatMessage(intlMessages.uploadProcess, {
|
|
0: Math.floor(item.progress).toString(),
|
|
});
|
|
}
|
|
|
|
const constraint = {};
|
|
|
|
if (('upload' in item) && (item.upload.done && item.upload.error)) {
|
|
if (item.conversion.status === 'FILE_TOO_LARGE' || item.upload.status !== 413) {
|
|
constraint['0'] = ((item.conversion.maxFileSize) / 1000 / 1000).toFixed(2);
|
|
} else if (item.progress < 100) {
|
|
const errorMessage = intlMessages.badConnectionError;
|
|
return intl.formatMessage(errorMessage);
|
|
}
|
|
|
|
const errorMessage = intlMessages[item.upload.status] || intlMessages.genericError;
|
|
return intl.formatMessage(errorMessage, constraint);
|
|
}
|
|
|
|
if (('uploadErrorMsgKey' in item) && (item.uploadInProgress && item.uploadErrorMsgKey)) {
|
|
const errorMessage = intlMessages[item.uploadErrorMsgKey]
|
|
|| intlMessages.genericConversionStatus;
|
|
|
|
switch (item.uploadErrorMsgKey) {
|
|
case 'CONVERSION_TIMEOUT':
|
|
constraint['0'] = item.uploadErrorDetailsJson.numberPageError;
|
|
constraint['1'] = item.uploadErrorDetailsJson.maxNumberOfAttempts;
|
|
break;
|
|
case 'FILE_TOO_LARGE':
|
|
constraint['0'] = ((item.uploadErrorDetailsJson.maxFileSize) / 1000 / 1000).toFixed(2);
|
|
break;
|
|
case 'PAGE_COUNT_EXCEEDED':
|
|
constraint['0'] = item.uploadErrorDetailsJson.maxNumberPages;
|
|
break;
|
|
case 'PDF_HAS_BIG_PAGE':
|
|
constraint['0'] = (item.uploadErrorDetailsJson.bigPageSize / 1000 / 1000).toFixed(2);
|
|
break;
|
|
case 'INVALID_MIME_TYPE':
|
|
constraint['0'] = item.uploadErrorDetailsJson.fileExtension;
|
|
constraint['1'] = item.uploadErrorDetailsJson.fileMime;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return intl.formatMessage(errorMessage, constraint);
|
|
}
|
|
|
|
if ((('uploadInProgress' in item) && (item.uploadInProgress && !item.uploadErrorMsgKey)) || (('progress' in item) && item.progress === 100)) {
|
|
let conversionStatusMessage;
|
|
if ('totalPagesUploaded' in item) {
|
|
if (item.totalPagesUploaded < item.totalPages) {
|
|
return intl.formatMessage(intlMessages.conversionProcessingSlides, {
|
|
0: item.totalPagesUploaded,
|
|
1: item.totalPages,
|
|
});
|
|
}
|
|
|
|
conversionStatusMessage = intlMessages[item.conversion?.status]
|
|
|| intlMessages.genericConversionStatus;
|
|
} else {
|
|
conversionStatusMessage = intlMessages.genericConversionStatus;
|
|
}
|
|
return intl.formatMessage(conversionStatusMessage);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function renderToastItem(item, intl) {
|
|
const isUploading = ('totalPages' in item) && item.totalPages > 0;
|
|
const uploadInProgress = ('uploadCompleted' in item) && !item.uploadCompleted;
|
|
const hasError = (('uploadErrorMsgKey' in item) && item.uploadErrorMsgKey !== '');
|
|
const isProcessing = (isUploading || uploadInProgress) && !hasError;
|
|
|
|
let icon = isProcessing ? 'blank' : 'check';
|
|
if (hasError) icon = 'circle_close';
|
|
|
|
return (
|
|
<Styled.UploadRow
|
|
key={item.presentationId || item.temporaryPresentationId}
|
|
onClick={() => {
|
|
if (hasError || isProcessing) Session.set('showUploadPresentationView', true);
|
|
}}
|
|
>
|
|
<Styled.FileLine>
|
|
<span>
|
|
<Icon iconName="file" />
|
|
</span>
|
|
<Styled.ToastFileName>
|
|
<span>{item.filename || item.name}</span>
|
|
</Styled.ToastFileName>
|
|
<Styled.StatusIcon>
|
|
<Styled.ToastItemIcon
|
|
done={!isProcessing && !hasError}
|
|
error={hasError}
|
|
loading={isProcessing}
|
|
iconName={icon}
|
|
/>
|
|
</Styled.StatusIcon>
|
|
</Styled.FileLine>
|
|
<Styled.StatusInfo>
|
|
<Styled.StatusInfoSpan data-test="presentationStatusInfo" styles={hasError ? 'error' : 'info'}>
|
|
{renderPresentationItemStatus(item, intl)}
|
|
</Styled.StatusInfoSpan>
|
|
</Styled.StatusInfo>
|
|
</Styled.UploadRow>
|
|
);
|
|
}
|
|
|
|
const renderToastList = (presentations, intl) => {
|
|
let converted = 0;
|
|
|
|
const presentationsSorted = presentations
|
|
.sort((a, b) => a.uploadCompleted - b.uploadCompleted);
|
|
|
|
presentationsSorted
|
|
.forEach((p) => {
|
|
const presDone = !p.uploadInProgress;
|
|
if (presDone) converted += 1;
|
|
return p;
|
|
});
|
|
|
|
let toastHeading = '';
|
|
const itemLabel = presentationsSorted.length > 1
|
|
? intl.formatMessage(intlMessages.itemPlural)
|
|
: intl.formatMessage(intlMessages.item);
|
|
|
|
if (converted === 0) {
|
|
toastHeading = intl.formatMessage(intlMessages.uploading, {
|
|
0: presentationsSorted.length,
|
|
1: itemLabel,
|
|
});
|
|
}
|
|
|
|
if (converted > 0 && converted !== presentationsSorted.length) {
|
|
toastHeading = intl.formatMessage(intlMessages.uploadStatus, {
|
|
0: converted,
|
|
1: presentationsSorted.length,
|
|
});
|
|
}
|
|
|
|
if (converted === presentationsSorted.length) {
|
|
toastHeading = intl.formatMessage(intlMessages.completed, {
|
|
0: converted,
|
|
});
|
|
}
|
|
|
|
return (
|
|
<Styled.ToastWrapper data-test="presentationUploadProgressToast">
|
|
<Styled.UploadToastHeader>
|
|
<Styled.UploadIcon iconName="upload" />
|
|
<Styled.UploadToastTitle>{toastHeading}</Styled.UploadToastTitle>
|
|
</Styled.UploadToastHeader>
|
|
<Styled.InnerToast>
|
|
<div>
|
|
<div>
|
|
{presentationsSorted.map((item) => renderToastItem(item, intl))}
|
|
</div>
|
|
</div>
|
|
</Styled.InnerToast>
|
|
</Styled.ToastWrapper>
|
|
);
|
|
};
|
|
|
|
function handleDismissToast(toastId) {
|
|
return toast.dismiss(toastId);
|
|
}
|
|
|
|
export const PresentationUploaderToast = ({intl, convertingPresentations, uploadingPresentations }) => {
|
|
const presentationsToConvert = convertingPresentations.concat(uploadingPresentations);
|
|
|
|
let activeToast = Session.get('presentationUploaderToastId');
|
|
const showToast = presentationsToConvert.length > 0;
|
|
|
|
if (showToast && !activeToast) {
|
|
activeToast = toast.info(() => renderToastList(presentationsToConvert, intl), {
|
|
hideProgressBar: true,
|
|
autoClose: false,
|
|
newestOnTop: true,
|
|
closeOnClick: true,
|
|
className: 'presentationUploaderToast toastClass',
|
|
});
|
|
Session.set('presentationUploaderToastId', activeToast);
|
|
} else if (!showToast && activeToast) {
|
|
handleDismissToast(activeToast);
|
|
Session.set('presentationUploaderToastId', null);
|
|
} else {
|
|
toast.update(activeToast, {
|
|
render: renderToastList(presentationsToConvert, intl),
|
|
});
|
|
}
|
|
return null;
|
|
};
|
|
|
|
export default {
|
|
handleDismissToast,
|
|
renderPresentationItemStatus,
|
|
};
|