refactor: toast notification rework

This commit is contained in:
Joao Victor 2022-07-25 18:56:26 -03:00
parent 24e78a1864
commit 07434d238c
6 changed files with 165 additions and 55 deletions

View File

@ -17,5 +17,5 @@ export default function handlePresentationExport({ body }, meetingId) {
check(presentationId, String);
sendExportedPresentationChatMsg(meetingId, presentationId, fileURI);
setPresentationExporting(meetingId, presentationId, { isRunning: false, error: false });
setPresentationExporting(meetingId, presentationId, { status: 'EXPORTED' });
}

View File

@ -31,12 +31,13 @@ export default function exportPresentationToChat(presentationId) {
const selector = { meetingId, id: presentationId };
const cursor = Presentations.find(selector);
const numPages = cursor.fetch()[0]?.pages?.length ?? 1;
const threshold = EXPORTING_THRESHOLD_PER_SLIDE * numPages;
const observer = cursor.observe({
changed: (doc) => {
const { isRunning, error } = doc.exportation;
const { status } = doc.exportation;
if (!isRunning && !error) {
if (status === 'EXPORTED') {
Meteor.clearTimeout(timeoutRef);
}
},
@ -44,11 +45,11 @@ export default function exportPresentationToChat(presentationId) {
timeoutRef = Meteor.setTimeout(() => {
observer.stop();
setPresentationExporting(meetingId, presentationId, { isRunning: false, error: true });
}, EXPORTING_THRESHOLD_PER_SLIDE * numPages);
setPresentationExporting(meetingId, presentationId, { status: 'TIMEOUT' });
}, threshold);
};
setPresentationExporting(meetingId, presentationId, { isRunning: true, error: false });
setPresentationExporting(meetingId, presentationId, { status: 'RUNNING' });
setObserver();
RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);

View File

@ -65,8 +65,7 @@ export default function addPresentation(meetingId, podId, presentation) {
podId,
'conversion.done': true,
'conversion.error': false,
'exportation.isRunning': false,
'exportation.error': false,
'exportation.status': null,
}, flat(presentation, { safe: true })),
};

View File

@ -249,8 +249,26 @@ const intlMessages = defineMessages({
id: 'app.presentation.downloadLabel',
description: 'download label',
},
sending: {
id: 'app.presentationUploader.sending',
description: 'sending label',
},
sent: {
id: 'app.presentationUploader.sent',
description: 'sent label',
},
exportingTimeout: {
id: 'app.presentationUploader.exportingTimeout',
description: 'exporting timeout label',
},
});
const EXPORT_STATUSES = {
RUNNING: 'RUNNING',
TIMEOUT: 'TIMEOUT',
EXPORTED: 'EXPORTED',
};
class PresentationUploader extends Component {
constructor(props) {
super(props);
@ -259,7 +277,7 @@ class PresentationUploader extends Component {
presentations: [],
disableActions: false,
toUploadCount: 0,
exportingCount: 0,
presExporting: new Set(),
};
this.toastId = null;
@ -282,9 +300,13 @@ class PresentationUploader extends Component {
this.renderPresentationItemStatus = this.renderPresentationItemStatus.bind(this);
this.renderToastList = this.renderToastList.bind(this);
this.renderToastItem = this.renderToastItem.bind(this);
this.renderExportToast = this.renderExportToast.bind(this);
this.renderToastExportItem = this.renderToastExportItem.bind(this);
this.renderExportationStatus = this.renderExportationStatus.bind(this);
// utilities
this.deepMergeUpdateFileKey = this.deepMergeUpdateFileKey.bind(this);
this.updateFileKey = this.updateFileKey.bind(this);
this.getPresentationsToShow = this.getPresentationsToShow.bind(this);
}
componentDidUpdate(prevProps) {
@ -605,34 +627,57 @@ class PresentationUploader extends Component {
);
}
getPresentationsToShow() {
const { presentations, presExporting } = this.state;
return Array.from(presExporting)
.map((id) => presentations.find((p) => p.id === id))
.filter((p) => p);
}
handleSendToChat(item) {
const { exportPresentationToChat } = this.props;
const observer = (exportation, done) => {
if (done) {
this.setState((prevState) => ({
exportingCount: prevState.exportingCount - 1,
}));
}
const observer = (exportation) => {
this.deepMergeUpdateFileKey(item.id, 'exportation', exportation);
if (exportation.status === EXPORT_STATUSES.RUNNING) {
this.setState((prevState) => {
prevState.presExporting.add(item.id);
return {
presExporting: prevState.presExporting,
};
}, () => {
if (this.exportToastId) {
toast.update(this.exportToastId, {
render: this.renderExportToast(),
});
} else {
this.exportToastId = toast.info(this.renderExportToast(), {
hideProgressBar: true,
autoClose: false,
newestOnTop: true,
closeOnClick: true,
onClose: () => {
this.exportToastId = null;
const presToShow = this.getPresentationsToShow();
const isAnyRunning = presToShow.some(
(p) => p.exportation.status === EXPORT_STATUSES.RUNNING
);
if (!isAnyRunning) {
this.setState({ presExporting: new Set() });
}
},
});
}
});
}
};
this.setState((prevState) => ({
exportingCount: prevState.exportingCount + 1,
}));
exportPresentationToChat(item.id, observer);
this.exportToastId = toast.info(this.renderExportToast(), {
hideProgressBar: true,
autoClose: false,
newestOnTop: true,
closeOnClick: true,
onClose: () => {
this.exportToastId = null;
},
});
Session.set('showUploadPresentationView', false);
}
@ -835,28 +880,47 @@ class PresentationUploader extends Component {
renderExportToast() {
const { intl } = this.props;
const { presentations, exportingCount } = this.state;
const { presExporting } = this.state;
const presExporting = presentations.filter((pres) => pres.exportation.isRunning);
const presToShow = this.getPresentationsToShow();
if (exportingCount === 0 && this.exportToastId) {
const isAllExported = presToShow.every(
(p) => p.exportation.status === EXPORT_STATUSES.EXPORTED
);
const shouldDismiss = isAllExported && this.exportToastId;
if (shouldDismiss) {
this.handleDismissToast(this.exportToastId);
if (presExporting.size) {
this.setState({ presExporting: new Set() });
}
return;
}
const headerLabelId = exportingCount === 1 ? 'exportToastHeader' : 'exportToastHeaderPlural';
const presToShowSorted = [
...presToShow.filter((p) => p.exportation.status === EXPORT_STATUSES.RUNNING),
...presToShow.filter((p) => p.exportation.status === EXPORT_STATUSES.TIMEOUT),
...presToShow.filter((p) => p.exportation.status === EXPORT_STATUSES.EXPORTED),
];
const headerLabelId = presToShowSorted.length === 1
? 'exportToastHeader'
: 'exportToastHeaderPlural';
return (
<Styled.ToastWrapper>
<Styled.UploadToastHeader>
<Styled.UploadIcon iconName="download" />
<Styled.UploadToastTitle>
{intl.formatMessage(intlMessages[headerLabelId], { 0: exportingCount })}
{intl.formatMessage(intlMessages[headerLabelId], { 0: presToShowSorted.length })}
</Styled.UploadToastTitle>
</Styled.UploadToastHeader>
<Styled.InnerToast>
<div>
<div>
{presExporting.map((item) => this.renderToastExportItem(item))}
{presToShowSorted.map((item) => this.renderToastExportItem(item))}
</div>
</div>
</Styled.InnerToast>
@ -865,25 +929,67 @@ class PresentationUploader extends Component {
}
renderToastExportItem(item) {
const { status } = item.exportation;
const loading = status === EXPORT_STATUSES.RUNNING;
const done = status === EXPORT_STATUSES.EXPORTED;
let icon;
switch (status) {
case EXPORT_STATUSES.RUNNING:
icon = 'blank'
break;
case EXPORT_STATUSES.EXPORTED:
icon = 'check'
break;
case EXPORT_STATUSES.TIMEOUT:
icon = 'warning'
break;
default:
break;
}
return (
<Styled.FileLine>
<span>
<Icon iconName="file" />
</span>
<Styled.ToastFileName>
<span>{item.filename}</span>
</Styled.ToastFileName>
<Styled.StatusIcon>
<Styled.ToastItemIcon
loading
iconName="blank"
color="#0F70D7"
/>
</Styled.StatusIcon>
</Styled.FileLine>
<Styled.UploadRow>
<Styled.FileLine>
<span>
<Icon iconName="file" />
</span>
<Styled.ToastFileName>
<span>{item.filename}</span>
</Styled.ToastFileName>
<Styled.StatusIcon>
<Styled.ToastItemIcon
loading={loading}
done={done}
iconName={icon}
color="#0F70D7"
/>
</Styled.StatusIcon>
</Styled.FileLine>
<Styled.StatusInfo>
<Styled.StatusInfoSpan>
{this.renderExportationStatus(item)}
</Styled.StatusInfoSpan>
</Styled.StatusInfo>
</Styled.UploadRow>
);
}
renderExportationStatus(item) {
const { intl } = this.props;
switch (item.exportation.status) {
case EXPORT_STATUSES.RUNNING:
return intl.formatMessage(intlMessages.sending);
case EXPORT_STATUSES.TIMEOUT:
return intl.formatMessage(intlMessages.exportingTimeout);
case EXPORT_STATUSES.EXPORTED:
return intl.formatMessage(intlMessages.sent);
default:
return '';
}
}
renderPresentationItem(item) {
const { disableActions } = this.state;
const {
@ -904,7 +1010,9 @@ class PresentationUploader extends Component {
const { animations } = Settings.application;
const { isRemovable, exportation: { isRunning: isExporting } } = item;
const { isRemovable, exportation: { status } } = item;
const isExporting = status === 'RUNNING';
const shouldDisableExportButton = isExporting
|| !item.conversion.done

View File

@ -277,8 +277,7 @@ const exportPresentationToChat = (presentationId, observer) => {
const cursor = Presentations.find({ id: presentationId });
const checkStatus = (exportation) => {
const shouldStop = (lastStatus.isRunning === true && exportation.isRunning === false)
|| exportation.error;
const shouldStop = lastStatus.status === 'RUNNING' && exportation.status !== 'RUNNING';
if (shouldStop) {
observer(exportation, true);

View File

@ -232,9 +232,12 @@
"app.presentationUploder.title": "Presentation",
"app.presentationUploder.message": "As a presenter you have the ability to upload any office document or PDF file. We recommend PDF file for best results. Please ensure that a presentation is selected using the circle checkbox on the left hand side.",
"app.presentationUploader.exportHint": "Selecting \"Send to chat\" will provide users with a downloadable link with annotations in public chat.",
"app.presentationUploader.exportToastHeader": "Downloading to chat ({0} item)",
"app.presentationUploader.exportToastHeaderPlural": "Downloading to chat ({0} items)",
"app.presentationUploader.exportToastHeader": "Sending to chat ({0} item)",
"app.presentationUploader.exportToastHeaderPlural": "Sending to chat ({0} items)",
"app.presentationUploader.exporting": "Sending to chat",
"app.presentationUploader.sending": "Sending...",
"app.presentationUploader.sent": "Sent",
"app.presentationUploader.exportingTimeout": "The exporting is taking too long...",
"app.presentationUploader.export": "Send to chat",
"app.presentationUploader.currentPresentationLabel": "Current presentation",
"app.presentationUploder.extraHint": "IMPORTANT: each file may not exceed {0} MB and {1} pages.",