Skip to content

Commit

Permalink
feat: Display upload errors in UIWindowProgress dialog
Browse files Browse the repository at this point in the history
The object returned from UIWindowProgress() now has a `show_error()`
method, taking a title and message. Calling it will replace the content
of the window with those messages.

I've made use of this for file uploads. The only other place we
currently have errors we could show is for zipping and downloading
files, but we do not always have a progress dialog in that case, so I'll
leave that for now.

I think ideally, we would always create a progress dialog, and it would
then support being invisible initially, but appearing after a delay.
Then we'd always have an object to call `show_error()` on, and it could
then immediately show the dialog. But I'll get to that another day. :^)
  • Loading branch information
AtkinsSJ committed May 9, 2024
1 parent f326969 commit edebbee
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 22 deletions.
68 changes: 49 additions & 19 deletions src/UI/UIWindowProgress.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,8 @@ import Button from './Components/Button.js';
* @param operation_id If provided, is saved in the data-operation-id attribute, for later lookup.
* @param show_progress Enable a progress bar, and display `(foo%)` after the status message
* @param on_cancel A callback run when the Cancel button is clicked. Without it, no Cancel button will appear.
* @returns {Promise<{set_progress: *, set_status: *, close: *, element: Element}>} Object for managing the progress dialog
* @returns {Promise<{set_progress: *, set_status: *, close: *, show_error: *, element: Element}>} Object for managing the progress dialog
* @constructor
* TODO: Error display
* TODO: Debouncing logic (show only after a delay, then hide only after a delay)
*/
async function UIWindowProgress({
Expand All @@ -37,30 +36,47 @@ async function UIWindowProgress({
on_cancel = null,
} = {}){
const placeholder_cancel_btn = Placeholder();
const placeholder_ok_btn = Placeholder();

let h = '';
h += `<div ${operation_id ? `data-operation-id="${operation_id}"` : ''}>`;
h += `<div style="display: flex; align-items: center;">`;
// spinner
h += `<svg style="margin-right: 7px; overflow: visible;" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24"><title>circle anim</title><g fill="#212121" class="nc-icon-wrapper"><g class="nc-loop-circle-24-icon-f"><path d="M12 24a12 12 0 1 1 12-12 12.013 12.013 0 0 1-12 12zm0-22a10 10 0 1 0 10 10A10.011 10.011 0 0 0 12 2z" fill="#212121" opacity=".4"></path><path d="M24 12h-2A10.011 10.011 0 0 0 12 2V0a12.013 12.013 0 0 1 12 12z" data-color="color-2"></path></g><style>.nc-loop-circle-24-icon-f{--animation-duration:0.5s;transform-origin:12px 12px;animation:nc-loop-circle-anim var(--animation-duration) infinite linear}@keyframes nc-loop-circle-anim{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}</style></g></svg>`;
// Progress report
h += `<div style="font-size:15px; overflow: hidden; flex-grow: 1; text-overflow: ellipsis; white-space: nowrap;">
<span class="progress-msg">${i18n('preparing')}</span>`;
h += `<div class="progress-running">`;
h += `<div style="display: flex; align-items: center; gap: 7px;">`;
// spinner
h += `<svg style="overflow: visible;" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24"><title>circle anim</title><g fill="#212121" class="nc-icon-wrapper"><g class="nc-loop-circle-24-icon-f"><path d="M12 24a12 12 0 1 1 12-12 12.013 12.013 0 0 1-12 12zm0-22a10 10 0 1 0 10 10A10.011 10.011 0 0 0 12 2z" fill="#212121" opacity=".4"></path><path d="M24 12h-2A10.011 10.011 0 0 0 12 2V0a12.013 12.013 0 0 1 12 12z" data-color="color-2"></path></g><style>.nc-loop-circle-24-icon-f{--animation-duration:0.5s;transform-origin:12px 12px;animation:nc-loop-circle-anim var(--animation-duration) infinite linear}@keyframes nc-loop-circle-anim{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}</style></g></svg>`;
// Progress report
h += `<div style="font-size:15px; overflow: hidden; flex-grow: 1; text-overflow: ellipsis; white-space: nowrap;">
<span class="progress-msg">${i18n('preparing')}</span>`;
if (show_progress) {
h += ` (<span class="progress-percent">0%</span>)`;
}
h += `</div>`;
h +=`</div>`;
if (show_progress) {
h += ` (<span class="progress-percent">0%</span>)`;
h += `<div class="progress-bar-container" style="margin-top:20px;">`;
h += `<div class="progress-bar"></div>`;
h += `</div>`;
}
if (on_cancel) {
h += `<div style="display: flex; justify-content: flex-end;">`;
h += placeholder_cancel_btn.html;
h += `</div>`;
}
h += `</div>`;
h += `<div class="progress-error" style="display: none">`;
h += `<div style="display: flex; align-items: center; gap: 7px;">`;
// Alert icon
h += `<img style="width:24px; height:24px;" src="${html_encode(window.icons['warning-sign.svg'])}" />`;
// Progress report
h += `<div style="font-size:15px; overflow: hidden; flex-grow: 1; text-overflow: ellipsis; white-space: nowrap;">
<span class="progress-error-title"></span>`;
h += `</div>`;
h += `</div>`;
h +=`</div>`;
if (show_progress) {
h += `<div class="progress-bar-container" style="margin-top:20px;">`;
h += `<div class="progress-bar"></div>`;
h += `</div>`;
}
if (on_cancel) {
h += `<p class="progress-error-message"></p>`;
h += `<div style="display: flex; justify-content: flex-end;">`;
h += placeholder_cancel_btn.html;
h += placeholder_ok_btn.html;
h += `</div>`;
}
h += `</div>`;
h += `</div>`;

const el_window = await UIWindow({
Expand Down Expand Up @@ -106,6 +122,15 @@ async function UIWindowProgress({
cancel_btn.attach(placeholder_cancel_btn);
}

const ok_btn = new Button({
label: i18n('ok'),
style: 'small',
on_click: () => {
$(el_window).close();
},
});
ok_btn.attach(placeholder_ok_btn);

return {
element: el_window,
set_status: (text) => {
Expand All @@ -118,7 +143,12 @@ async function UIWindowProgress({
close: () => {
$(el_window).close();
},
// TODO: show_error(), which replaces the window content with a title, error message, and OK button
show_error: (title, message) => {
el_window.querySelector('.progress-running').style.display = 'none';
el_window.querySelector('.progress-error').style.display = 'block';
el_window.querySelector('.progress-error-title').innerText = title;
el_window.querySelector('.progress-error-message').innerText = message;
},
};
}

Expand Down
4 changes: 1 addition & 3 deletions src/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -2771,9 +2771,7 @@ window.upload_items = async function(items, dest_path){
},
// error
error: async function(err){
// TODO: Display error in progress dialog
upload_progress_window.close();
// UIAlert(err?.message ?? 'An error occurred while uploading.');
upload_progress_window.show_error(i18n('error_uploading_files'), err.message);
// remove from active_uploads
delete window.active_uploads[opid];
},
Expand Down
1 change: 1 addition & 0 deletions src/i18n/translations/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ const en = {
enlarged_qr_code: "Enlarged QR Code",
enter_password_to_confirm_delete_user: "Enter your password to confirm account deletion",
error_unknown_cause: "An unknown error occurred.",
error_uploading_files: "Failed to upload files",
feedback: "Feedback",
feedback_c2a: "Please use the form below to send us your feedback, comments, and bug reports.",
feedback_sent_confirmation: "Thank you for contacting us. If you have an email associated with your account, you will hear back from us as soon as possible.",
Expand Down

0 comments on commit edebbee

Please sign in to comment.