refactor: toast notification rework
This commit is contained in:
parent
24e78a1864
commit
07434d238c
@ -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' });
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 })),
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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.",
|
||||
|
Loading…
Reference in New Issue
Block a user