From 05aaa5b6671f40d87c6751e0d665271f7260fb97 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 9 May 2024 12:06:54 +0100 Subject: [PATCH 01/13] chore: Remove dead code from 2FA development No `.qr-code-checkbox input`s now exist. Also, `.code-confirm-btn` is only used by CodeEntryView, which doesn't use this Button component. --- src/UI/Components/Button.js | 4 ++-- src/UI/UIWindowQR.js | 7 ------- src/css/style.css | 10 ---------- 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/src/UI/Components/Button.js b/src/UI/Components/Button.js index 375e1ad9..49490244 100644 --- a/src/UI/Components/Button.js +++ b/src/UI/Components/Button.js @@ -33,14 +33,14 @@ export default class Button extends Component { create_template ({ template }) { if ( this.get('style') === 'link' ) { $(template).html(/*html*/` - `); return; } $(template).html(/*html*/` - `); diff --git a/src/UI/UIWindowQR.js b/src/UI/UIWindowQR.js index 812b6a33..8976fc68 100644 --- a/src/UI/UIWindowQR.js +++ b/src/UI/UIWindowQR.js @@ -88,14 +88,7 @@ async function UIWindowQR(options){ ] }); - // component_qr.attach(placeholder_qr); component_flexer.attach(placeholder_qr); - // placeholder_qr.replaceWith($(`

test

`).get(0)); - - $(el_window).find('.qr-code-checkbox input').on('change', () => { - const all_checked = $(el_window).find('.qr-code-checkbox input').toArray().every(el => el.checked); - $(el_window).find('.code-confirm-btn').prop('disabled', !all_checked); - }); } export default UIWindowQR \ No newline at end of file diff --git a/src/css/style.css b/src/css/style.css index 0fe24790..faa21aba 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -2625,16 +2625,6 @@ fieldset[name=number-code] { margin: 20px 0; } -.qr-code-checkbox { - display: flex; - gap: 10px; - align-items: center; -} - -.qr-code-checkbox input[type=checkbox] { - margin: 0; -} - .perm-title { text-align: center; margin-top: 0; From df3f7e9a6e82a29b54e0a0a18dec7f6a2b689fab Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 9 May 2024 12:37:22 +0100 Subject: [PATCH 02/13] Allow Button component to not have button-block class This needs a better solution for configuring the button's CSS classes, but this works for now. --- src/UI/Components/Button.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/UI/Components/Button.js b/src/UI/Components/Button.js index 49490244..9950f51e 100644 --- a/src/UI/Components/Button.js +++ b/src/UI/Components/Button.js @@ -39,8 +39,9 @@ export default class Button extends Component { `); return; } + // TODO: Replace hack for 'small' with a better way to configure button classes. $(template).html(/*html*/` - `); From 52ddfc8801b806d15f79576da3dca19f4240f7d5 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 9 May 2024 12:31:10 +0100 Subject: [PATCH 03/13] Introduce a general-purpose progress window We have 7 of these, which all have very similar code. Let's deduplicate them! :^) --- src/UI/UIWindowProgress.js | 125 +++++++++++++++++++++++++++++++++++++ src/css/style.css | 12 ++-- 2 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 src/UI/UIWindowProgress.js diff --git a/src/UI/UIWindowProgress.js b/src/UI/UIWindowProgress.js new file mode 100644 index 00000000..85dc5da9 --- /dev/null +++ b/src/UI/UIWindowProgress.js @@ -0,0 +1,125 @@ +/** + * Copyright (C) 2024 Puter Technologies Inc. + * + * This file is part of Puter. + * + * Puter is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import UIWindow from './UIWindow.js' +import Placeholder from '../util/Placeholder.js'; +import Button from './Components/Button.js'; + +/** + * General purpose progress dialog. + * @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 + * @constructor + * TODO: Error display + * TODO: Debouncing logic (show only after a delay, then hide only after a delay) + */ +async function UIWindowProgress({ + operation_id = null, + show_progress = false, + on_cancel = null, +} = {}){ + const placeholder_cancel_btn = Placeholder(); + + let h = ''; + h += `
`; + h += `
`; + // spinner + h += `circle anim`; + // Progress report + h += `
+ ${i18n('preparing')}`; + if (show_progress) { + h += ` (0%)`; + } + h += `
`; + h +=`
`; + if (show_progress) { + h += `
`; + h += `
`; + h += `
`; + } + if (on_cancel) { + h += `
`; + h += placeholder_cancel_btn.html; + h += `
`; + } + h += `
`; + + const el_window = await UIWindow({ + uid: null, + is_dir: false, + body_content: h, + has_head: false, + selectable_body: false, + draggable_body: true, + allow_context_menu: false, + is_resizable: false, + is_droppable: false, + init_center: true, + allow_native_ctxmenu: false, + allow_user_select: false, + window_class: 'window-progress', + width: 450, + dominant: true, + window_css:{ + height: 'initial', + }, + body_css: { + padding: '22px', + width: 'initial', + 'background-color': `hsla( + var(--primary-hue), + var(--primary-saturation), + var(--primary-lightness), + var(--primary-alpha))`, + 'backdrop-filter': 'blur(3px)', + } + }); + + if (on_cancel) { + const cancel_btn = new Button({ + label: i18n('cancel'), + style: 'small', + on_click: () => { + $(el_window).close(); + on_cancel(); + }, + }); + cancel_btn.attach(placeholder_cancel_btn); + } + + return { + element: el_window, + set_status: (text) => { + el_window.querySelector('.progress-msg').innerHTML = text; + }, + set_progress: (percent) => { + el_window.querySelector('.progress-bar').style.width = `${percent}%`; + el_window.querySelector('.progress-percent').innerText = `${percent}%`; + }, + close: () => { + $(el_window).close(); + }, + // TODO: show_error(), which replaces the window content with a title, error message, and OK button + }; +} + +export default UIWindowProgress; \ No newline at end of file diff --git a/src/css/style.css b/src/css/style.css index faa21aba..69a19e30 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -2708,17 +2708,19 @@ fieldset[name=number-code] { } } -.upload-progress-bar-container, .download-progress-bar-container { +.progress-bar-container, .upload-progress-bar-container, .download-progress-bar-container { + box-sizing: border-box; width: 100%; - height: 15px; + height: 17px; border: 1px solid rgb(40 109 157); + border-radius: 3px; background-color: white; box-shadow: inset -1px 3px 4px #dfdfdf; } -.upload-progress-bar, .download-progress-bar { +.progress-bar, .upload-progress-bar, .download-progress-bar { width: 0; - height: 15px; + height: 100%; background-color: rgb(0 137 255); transition: 0.4s width; background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.05)); @@ -3937,4 +3939,4 @@ fieldset[name=number-code] { .taskmgr-task-title { flex-grow: 1; padding-left: calc(2.5 * var(--scale)); -} \ No newline at end of file +} From 5b565a69d7d2c6357b3feb20b555ecc87885eb8a Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 9 May 2024 12:34:14 +0100 Subject: [PATCH 04/13] refactor: Replace UIWindowUploadProgress with UIWindowProgress --- src/UI/UIWindowUploadProgress.js | 75 -------------------------------- src/css/style.css | 4 +- src/helpers.js | 30 +++++++------ 3 files changed, 19 insertions(+), 90 deletions(-) delete mode 100644 src/UI/UIWindowUploadProgress.js diff --git a/src/UI/UIWindowUploadProgress.js b/src/UI/UIWindowUploadProgress.js deleted file mode 100644 index 23c5d5ec..00000000 --- a/src/UI/UIWindowUploadProgress.js +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright (C) 2024 Puter Technologies Inc. - * - * This file is part of Puter. - * - * Puter is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import UIWindow from './UIWindow.js' - -// todo do this using uid rather than item_path, since item_path is way mroe expensive on the DB -async function UIWindowUploadProgress(options){ - let h = ''; - h += `
`; - h += `
`; - // spinner - h +=`circle anim`; - // Progress report - h +=`
`; - // msg - h += `${i18n('preparing_for_upload')}`; - h += `
`; - // progress - h += `
`; - h += `
`; - h += `
`; - // cancel - h += ``; - h +=`
`; - h += `
`; - - const el_window = await UIWindow({ - title: i18n('upload'), - icon: window.icons[`app-icon-uploader.svg`], - uid: null, - is_dir: false, - body_content: h, - has_head: false, - selectable_body: false, - draggable_body: true, - allow_context_menu: false, - is_resizable: false, - is_droppable: false, - init_center: true, - allow_native_ctxmenu: false, - allow_user_select: false, - window_class: 'window-upload-progress', - width: 450, - dominant: true, - window_css:{ - height: 'initial', - }, - body_css: { - padding: '22px', - width: 'initial', - 'background-color': 'rgba(231, 238, 245, .95)', - 'backdrop-filter': 'blur(3px)', - } - }); - - return el_window; -} - -export default UIWindowUploadProgress \ No newline at end of file diff --git a/src/css/style.css b/src/css/style.css index 69a19e30..dbecf56b 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -2708,7 +2708,7 @@ fieldset[name=number-code] { } } -.progress-bar-container, .upload-progress-bar-container, .download-progress-bar-container { +.progress-bar-container, .download-progress-bar-container { box-sizing: border-box; width: 100%; height: 17px; @@ -2718,7 +2718,7 @@ fieldset[name=number-code] { box-shadow: inset -1px 3px 4px #dfdfdf; } -.progress-bar, .upload-progress-bar, .download-progress-bar { +.progress-bar, .download-progress-bar { width: 0; height: 100%; background-color: rgb(0 137 255); diff --git a/src/helpers.js b/src/helpers.js index 4a9d2128..680f4e76 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -27,13 +27,13 @@ import UIWindowSaveAccount from './UI/UIWindowSaveAccount.js'; import UIWindowCopyProgress from './UI/UIWindowCopyProgress.js'; import UIWindowMoveProgress from './UI/UIWindowMoveProgress.js'; import UIWindowNewFolderProgress from './UI/UIWindowNewFolderProgress.js'; -import UIWindowUploadProgress from './UI/UIWindowUploadProgress.js'; import UIWindowProgressEmptyTrash from './UI/UIWindowProgressEmptyTrash.js'; import update_username_in_gui from './helpers/update_username_in_gui.js'; import update_title_based_on_uploads from './helpers/update_title_based_on_uploads.js'; import content_type_to_icon from './helpers/content_type_to_icon.js'; import UIWindowDownloadDirProg from './UI/UIWindowDownloadDirProg.js'; import { PROCESS_RUNNING, PortalProcess, PseudoProcess } from "./definitions.js"; +import UIWindowProgress from './UI/UIWindowProgress.js'; window.is_auth = ()=>{ if(localStorage.getItem("auth_token") === null || window.auth_token === null) @@ -2685,25 +2685,28 @@ window.upload_items = async function(items, dest_path){ init: async(operation_id, xhr)=>{ opid = operation_id; // create upload progress window - upload_progress_window = await UIWindowUploadProgress({operation_id: operation_id}); - // cancel btn - $(upload_progress_window).find('.upload-cancel-btn').on('click', function(e){ - $(upload_progress_window).close(); - window.show_save_account_notice_if_needed(); - xhr.abort(); - }) + upload_progress_window = await UIWindowProgress({ + title: i18n('upload'), + icon: window.icons[`app-icon-uploader.svg`], + operation_id: operation_id, + show_progress: true, + on_cancel: () => { + window.show_save_account_notice_if_needed(); + xhr.abort(); + }, + }); // add to active_uploads window.active_uploads[opid] = 0; }, // start start: async function(){ // change upload progress window message to uploading - $(upload_progress_window).find('.upload-progress-msg').html(`Uploading (0%)`); + upload_progress_window.set_status('Uploading'); + upload_progress_window.set_progress(0); }, // progress progress: async function(operation_id, op_progress){ - $(`[data-upload-operation-id="${operation_id}"]`).find('.upload-progress-bar').css( 'width', op_progress+'%'); - $(`[data-upload-operation-id="${operation_id}"]`).find('.upload-progress-percent').html(op_progress+'%'); + upload_progress_window.set_progress(op_progress); // update active_uploads window.active_uploads[opid] = op_progress; // update title if window is not visible @@ -2731,7 +2734,7 @@ window.upload_items = async function(items, dest_path){ // close progress window after a bit of delay for a better UX setTimeout(() => { setTimeout(() => { - $(upload_progress_window).close(); + upload_progress_window.close(); window.show_save_account_notice_if_needed(); }, Math.abs(window.upload_progress_hide_delay)); }) @@ -2740,7 +2743,8 @@ window.upload_items = async function(items, dest_path){ }, // error error: async function(err){ - $(upload_progress_window).close(); + // TODO: Display error in progress dialog + upload_progress_window.close(); // UIAlert(err?.message ?? 'An error occurred while uploading.'); // remove from active_uploads delete window.active_uploads[opid]; From 09ca82e1d8298231012a5fb6f643874b5e3185ca Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 9 May 2024 14:02:27 +0100 Subject: [PATCH 05/13] refactor: Replace UIWindowNewFolderProgress with UIWindowProgress This previously had code for the cancel button, but that cancel button didn't exist. I've left the previous on-cancel code commented out with a TODO. --- src/UI/UIWindowNewFolderProgress.js | 74 ----------------------------- src/helpers.js | 26 ++++++---- 2 files changed, 17 insertions(+), 83 deletions(-) delete mode 100644 src/UI/UIWindowNewFolderProgress.js diff --git a/src/UI/UIWindowNewFolderProgress.js b/src/UI/UIWindowNewFolderProgress.js deleted file mode 100644 index 08d694a4..00000000 --- a/src/UI/UIWindowNewFolderProgress.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (C) 2024 Puter Technologies Inc. - * - * This file is part of Puter. - * - * Puter is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import UIWindow from './UIWindow.js' - -// todo do this using uid rather than item_path, since item_path is way mroe expensive on the DB -async function UIWindowNewFolderProgress(options){ - let h = ''; - h += `
`; - h += `
`; - // spinner - h +=`circle anim`; - // message - h +=`
`; - // text - h += `${i18n('taking_longer_than_usual')}`; - h += `
`; - h +=`
`; - h += `
`; - - const el_window = await UIWindow({ - title: `Creating New Folder`, - icon: window.icons[`app-icon-newfolder.svg`], - uid: null, - is_dir: false, - body_content: h, - has_head: false, - selectable_body: false, - draggable_body: true, - allow_context_menu: false, - is_resizable: false, - is_droppable: false, - init_center: true, - allow_native_ctxmenu: false, - allow_user_select: false, - window_class: 'window-newfolder-progress', - width: 450, - dominant: true, - window_css:{ - height: 'initial', - }, - body_css: { - padding: '22px', - width: 'initial', - 'background-color': 'rgba(231, 238, 245, .95)', - 'backdrop-filter': 'blur(3px)', - } - }); - - $(el_window).find('.newfolder-cancel-btn').on('click', function(e){ - window.operation_cancelled[options.operation_id] = true; - $(el_window).close(); - }) - - return el_window; -} - -export default UIWindowNewFolderProgress \ No newline at end of file diff --git a/src/helpers.js b/src/helpers.js index 680f4e76..e0980a7a 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -26,7 +26,6 @@ import UIWindowLogin from './UI/UIWindowLogin.js'; import UIWindowSaveAccount from './UI/UIWindowSaveAccount.js'; import UIWindowCopyProgress from './UI/UIWindowCopyProgress.js'; import UIWindowMoveProgress from './UI/UIWindowMoveProgress.js'; -import UIWindowNewFolderProgress from './UI/UIWindowNewFolderProgress.js'; import UIWindowProgressEmptyTrash from './UI/UIWindowProgressEmptyTrash.js'; import update_username_in_gui from './helpers/update_username_in_gui.js'; import update_title_based_on_uploads from './helpers/update_title_based_on_uploads.js'; @@ -1166,7 +1165,14 @@ window.create_folder = async(basedir, appendto_element)=>{ // only show progress window if it takes longer than 500ms to create folder let progwin_timeout = setTimeout(async () => { - progwin = await UIWindowNewFolderProgress({operation_id: newfolder_op_id}); + progwin = await UIWindowProgress({ + operation_id: newfolder_op_id, + // TODO: Implement cancellation. + // on_cancel: () => { + // window.operation_cancelled[newfolder_op_id] = true; + // }, + }); + progwin.set_status(i18n('taking_longer_than_usual')); }, 500); // create folder @@ -1190,14 +1196,16 @@ window.create_folder = async(basedir, appendto_element)=>{ // done let newfolder_duration = (Date.now() - newfolder_progress_window_init_ts); - if( progwin && newfolder_duration >= window.copy_progress_hide_delay){ - $(progwin).close(); - }else if(progwin){ - setTimeout(() => { + if (progwin) { + if (newfolder_duration >= window.copy_progress_hide_delay) { + progwin.close(); + } else { setTimeout(() => { - $(progwin).close(); - }, Math.abs(window.copy_progress_hide_delay - newfolder_duration)); - }) + setTimeout(() => { + progwin.close(); + }, Math.abs(window.copy_progress_hide_delay - newfolder_duration)); + }); + } } } }); From cb6c098f95a393039166a37c5c5da29a6712a1d3 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 9 May 2024 18:17:15 +0100 Subject: [PATCH 06/13] refactor: Replace UIWindowProgressEmptyTrash with UIWindowProgress I noticed during this that emptying the trash from inside the Trash window uses a different code path without a progress dialog, so I've added a TODO to merge that in. I tried just using it directly but the behaviour is a bit different. (The Trash one makes all the items fade out.) --- src/UI/UIWindow.js | 1 + src/UI/UIWindowProgressEmptyTrash.js | 74 ---------------------------- src/helpers.js | 8 +-- 3 files changed, 5 insertions(+), 78 deletions(-) delete mode 100644 src/UI/UIWindowProgressEmptyTrash.js diff --git a/src/UI/UIWindow.js b/src/UI/UIWindow.js index dbcbbf38..06def551 100644 --- a/src/UI/UIWindow.js +++ b/src/UI/UIWindow.js @@ -2086,6 +2086,7 @@ async function UIWindow(options) { html: i18n('empty_trash'), disabled: false, onClick: async function(){ + // TODO: Merge this with window.empty_trash() const alert_resp = await UIAlert({ message: i18n('empty_trash_confirmation'), buttons:[ diff --git a/src/UI/UIWindowProgressEmptyTrash.js b/src/UI/UIWindowProgressEmptyTrash.js deleted file mode 100644 index 8f8a8aff..00000000 --- a/src/UI/UIWindowProgressEmptyTrash.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (C) 2024 Puter Technologies Inc. - * - * This file is part of Puter. - * - * Puter is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import UIWindow from './UIWindow.js' - -// todo do this using uid rather than item_path, since item_path is way mroe expensive on the DB -async function UIWindowProgressEmptyTrash(options){ - let h = ''; - h += `
`; - h += `
`; - // spinner - h +=`circle anim`; - // message - h +=`
`; - // text - h += `${i18n('emptying_trash')}`; - h += `
`; - h +=`
`; - h += `
`; - - const el_window = await UIWindow({ - title: i18n('emptying_trash'), - icon: window.icons[`app-icon-newfolder.svg`], - uid: null, - is_dir: false, - body_content: h, - has_head: false, - selectable_body: false, - draggable_body: true, - allow_context_menu: false, - is_resizable: false, - is_droppable: false, - init_center: true, - allow_native_ctxmenu: false, - allow_user_select: false, - window_class: 'window-newfolder-progress', - width: 450, - dominant: true, - window_css:{ - height: 'initial', - }, - body_css: { - padding: '22px', - width: 'initial', - 'background-color': 'rgba(231, 238, 245, .95)', - 'backdrop-filter': 'blur(3px)', - } - }); - - $(el_window).find('.newfolder-cancel-btn').on('click', function(e){ - window.operation_cancelled[options.operation_id] = true; - $(el_window).close(); - }) - - return el_window; -} - -export default UIWindowProgressEmptyTrash \ No newline at end of file diff --git a/src/helpers.js b/src/helpers.js index e0980a7a..2b27b285 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -26,7 +26,6 @@ import UIWindowLogin from './UI/UIWindowLogin.js'; import UIWindowSaveAccount from './UI/UIWindowSaveAccount.js'; import UIWindowCopyProgress from './UI/UIWindowCopyProgress.js'; import UIWindowMoveProgress from './UI/UIWindowMoveProgress.js'; -import UIWindowProgressEmptyTrash from './UI/UIWindowProgressEmptyTrash.js'; import update_username_in_gui from './helpers/update_username_in_gui.js'; import update_title_based_on_uploads from './helpers/update_title_based_on_uploads.js'; import content_type_to_icon from './helpers/content_type_to_icon.js'; @@ -2790,7 +2789,8 @@ window.empty_trash = async function(){ let progwin; let op_id = window.uuidv4(); let progwin_timeout = setTimeout(async () => { - progwin = await UIWindowProgressEmptyTrash({operation_id: op_id}); + progwin = await UIWindowProgress({operation_id: op_id}); + progwin.set_status(i18n('emptying_trash')); }, 500); await puter.fs.delete({ @@ -2814,13 +2814,13 @@ window.empty_trash = async function(){ // close progress window clearTimeout(progwin_timeout); setTimeout(() => { - $(progwin).close(); + progwin?.close(); }, Math.max(0, window.copy_progress_hide_delay - (Date.now() - init_ts))); }, error: async function (err){ clearTimeout(progwin_timeout); setTimeout(() => { - $(progwin).close(); + progwin?.close(); }, Math.max(0, window.copy_progress_hide_delay - (Date.now() - init_ts))); } }); From c12312cbd37343d6ba33784327d0d6661576763f Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 9 May 2024 18:18:27 +0100 Subject: [PATCH 07/13] refactor: Replace UIWindowMoveProgress with UIWindowProgress --- src/UI/UIWindowMoveProgress.js | 81 ---------------------------------- src/helpers.js | 14 +++--- 2 files changed, 9 insertions(+), 86 deletions(-) delete mode 100644 src/UI/UIWindowMoveProgress.js diff --git a/src/UI/UIWindowMoveProgress.js b/src/UI/UIWindowMoveProgress.js deleted file mode 100644 index 5ec74062..00000000 --- a/src/UI/UIWindowMoveProgress.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (C) 2024 Puter Technologies Inc. - * - * This file is part of Puter. - * - * Puter is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import UIWindow from './UIWindow.js' - -// todo do this using uid rather than item_path, since item_path is way mroe expensive on the DB -async function UIWindowMoveProgress(options){ - let h = ''; - h += `
`; - h += `
`; - // spinner - h +=`circle anim`; - // Progress report - h +=`
`; - // msg - h += `${i18n('moving')} `; - h += ``; - h += `
`; - // progress - h += `
`; - h += `
`; - h += `
`; - // cancel - // h += ``; - h +=`
`; - h += `
`; - - const el_window = await UIWindow({ - title: `moveing`, - icon: window.icons[`app-icon-moveing.svg`], - uid: null, - is_dir: false, - body_content: h, - has_head: false, - selectable_body: false, - draggable_body: true, - allow_context_menu: false, - is_resizable: false, - is_droppable: false, - init_center: true, - allow_native_ctxmenu: false, - allow_user_select: false, - window_class: 'window-move-progress', - width: 450, - dominant: true, - window_css:{ - height: 'initial', - }, - body_css: { - padding: '22px', - width: 'initial', - 'background-color': 'rgba(231, 238, 245, .95)', - 'backdrop-filter': 'blur(3px)', - } - }); - - $(el_window).find('.move-cancel-btn').on('click', function(e){ - window.operation_cancelled[options.operation_id] = true; - $(el_window).close(); - }) - - return el_window; -} - -export default UIWindowMoveProgress \ No newline at end of file diff --git a/src/helpers.js b/src/helpers.js index 2b27b285..ce2ffc48 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -25,7 +25,6 @@ import UIWindow from './UI/UIWindow.js' import UIWindowLogin from './UI/UIWindowLogin.js'; import UIWindowSaveAccount from './UI/UIWindowSaveAccount.js'; import UIWindowCopyProgress from './UI/UIWindowCopyProgress.js'; -import UIWindowMoveProgress from './UI/UIWindowMoveProgress.js'; import update_username_in_gui from './helpers/update_username_in_gui.js'; import update_title_based_on_uploads from './helpers/update_title_based_on_uploads.js'; import content_type_to_icon from './helpers/content_type_to_icon.js'; @@ -2132,7 +2131,12 @@ window.move_items = async function(el_items, dest_path, is_undo = false){ // only show progress window if it takes longer than 2s to move let progwin; let progwin_timeout = setTimeout(async () => { - progwin = await UIWindowMoveProgress({operation_id: move_op_id}); + progwin = await UIWindowProgress({ + operation_id: move_op_id, + on_cancel: () => { + window.operation_cancelled[move_op_id] = true; + }, + }); }, 2000); // storing moved items for undo ability @@ -2218,7 +2222,7 @@ window.move_items = async function(el_items, dest_path, is_undo = false){ // moving an item into a trashed directory? deny. else if(dest_path.startsWith(window.trash_path)){ - $(progwin).close(); + progwin?.close(); UIAlert('Cannot move items into a deleted folder.'); return; } @@ -2237,7 +2241,7 @@ window.move_items = async function(el_items, dest_path, is_undo = false){ // -------------------------------------------------------- // update progress window with current item being moved // -------------------------------------------------------- - $(progwin).find('.move-from').html(html_encode(path_to_show_on_progwin)); + progwin?.set_status(i18n('moving') + ' ' + html_encode(path_to_show_on_progwin)); // execute move let resp = await puter.fs.move({ @@ -2444,7 +2448,7 @@ window.move_items = async function(el_items, dest_path, is_undo = false){ if(progwin){ setTimeout(() => { - $(progwin).close(); + progwin.close(); }, window.copy_progress_hide_delay); } } From f07c13a50cee790eec44bce2f6e56fbcbf73f9b0 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 9 May 2024 15:11:54 +0100 Subject: [PATCH 08/13] feat: Show "Deleting /foo" in progress window when deleting files Previously we showed "Moving /foo" which was confusing. Also, make use of i18n replacements instead of gluing strings together, because other languages might put the words in a different order. For now, I've modified all the translations so that the "Moving foo" text will appear as it did before, but these will require someone who actually knows the different languages to check and correct them. --- src/helpers.js | 6 +++++- src/i18n/translations/ar.js | 2 +- src/i18n/translations/bn.js | 2 +- src/i18n/translations/br.js | 2 +- src/i18n/translations/da.js | 2 +- src/i18n/translations/de.js | 2 +- src/i18n/translations/emoji.js | 2 +- src/i18n/translations/en.js | 3 ++- src/i18n/translations/es.js | 2 +- src/i18n/translations/fa.js | 2 +- src/i18n/translations/fi.js | 2 +- src/i18n/translations/fr.js | 2 +- src/i18n/translations/hy.js | 2 +- src/i18n/translations/ig.js | 2 +- src/i18n/translations/it.js | 2 +- src/i18n/translations/ko.js | 2 +- src/i18n/translations/nb.js | 2 +- src/i18n/translations/nl.js | 2 +- src/i18n/translations/nn.js | 2 +- src/i18n/translations/pl.js | 2 +- src/i18n/translations/pt.js | 2 +- src/i18n/translations/ro.js | 2 +- src/i18n/translations/ru.js | 2 +- src/i18n/translations/sv.js | 2 +- src/i18n/translations/th.js | 2 +- src/i18n/translations/tr.js | 2 +- src/i18n/translations/ur.js | 2 +- src/i18n/translations/zh.js | 2 +- src/i18n/translations/zhtw.js | 2 +- 29 files changed, 34 insertions(+), 29 deletions(-) diff --git a/src/helpers.js b/src/helpers.js index ce2ffc48..2302e54f 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -2199,6 +2199,8 @@ window.move_items = async function(el_items, dest_path, is_undo = false){ // indicates whether this is a recycling operation let recycling = false; + let status_i18n_string = 'moving_file'; + // -------------------------------------------------------- // Trashing // -------------------------------------------------------- @@ -2210,6 +2212,8 @@ window.move_items = async function(el_items, dest_path, is_undo = false){ trashed_ts: Math.round(Date.now() / 1000), }; + status_i18n_string = 'deleting_file'; + // update other clients if(window.socket) window.socket.emit('trash.is_empty', {is_empty: false}); @@ -2241,7 +2245,7 @@ window.move_items = async function(el_items, dest_path, is_undo = false){ // -------------------------------------------------------- // update progress window with current item being moved // -------------------------------------------------------- - progwin?.set_status(i18n('moving') + ' ' + html_encode(path_to_show_on_progwin)); + progwin?.set_status(i18n(status_i18n_string, path_to_show_on_progwin)); // execute move let resp = await puter.fs.move({ diff --git a/src/i18n/translations/ar.js b/src/i18n/translations/ar.js index a7b3f4e0..c429fd48 100644 --- a/src/i18n/translations/ar.js +++ b/src/i18n/translations/ar.js @@ -83,7 +83,7 @@ const ar = { log_in: "تسجيل الدخول", log_out: 'تسجيل خروج', move: 'يتحرك', - moving: "متحرك ", + moving_file: "متحرك %%", my_websites: "مواقعي الإلكترونية ", name: 'اسم', name_cannot_be_empty: 'لا يمكن أن يكون الاسم فارغًا', diff --git a/src/i18n/translations/bn.js b/src/i18n/translations/bn.js index e5a89f13..c90ebb55 100644 --- a/src/i18n/translations/bn.js +++ b/src/i18n/translations/bn.js @@ -82,7 +82,7 @@ const bn = { log_in: "প্রবেশ করুন", log_out: 'প্রস্থান', move: 'সরান', - moving: "চলন্ত", + moving_file: "চলন্ত %%", my_websites: "আমার ওয়েবসাইট", name: 'নাম', name_cannot_be_empty: 'নাম খালি রাখা যাবে না।', diff --git a/src/i18n/translations/br.js b/src/i18n/translations/br.js index 40ccc905..be8fb93b 100644 --- a/src/i18n/translations/br.js +++ b/src/i18n/translations/br.js @@ -102,7 +102,7 @@ const br = { log_into_another_account_anyway: 'Entrar com outra conta de qualquer maneira', log_out: 'Sair', move: 'Mover', - moving: "Movendo", + moving_file: "Movendo %%", my_websites: "Meus Sites", name: 'Nome', name_cannot_be_empty: 'Nome não pode ser vazio.', diff --git a/src/i18n/translations/da.js b/src/i18n/translations/da.js index 891fc56a..a91d109f 100644 --- a/src/i18n/translations/da.js +++ b/src/i18n/translations/da.js @@ -83,7 +83,7 @@ const da = { log_in: "Log ind", log_out: "Log ud", move: "Flyt", - moving: "Flytter", + moving_file: "Flytter %%", my_websites: "Mine websteder", name: "Navn", name_cannot_be_empty: "Navn kan ikke være tomt.", diff --git a/src/i18n/translations/de.js b/src/i18n/translations/de.js index 639d4b76..7f6b0858 100644 --- a/src/i18n/translations/de.js +++ b/src/i18n/translations/de.js @@ -83,7 +83,7 @@ const de = { log_in: "Einloggen", log_out: 'Ausloggen', move: 'Verschieben', - moving: "Verschiebe", + moving_file: "Verschiebe %%", my_websites: "Meine Webseiten", name: 'Name', name_cannot_be_empty: 'Name kann nicht leer sein.', diff --git a/src/i18n/translations/emoji.js b/src/i18n/translations/emoji.js index dfa41ee4..109dc7d2 100644 --- a/src/i18n/translations/emoji.js +++ b/src/i18n/translations/emoji.js @@ -92,7 +92,7 @@ const emoji = { log_into_another_account_anyway: '👤🔄', log_out: '🔚', move: '➡️', - moving: "➡️...", + moving_file: "➡️ %%...", my_websites: "🌐👤", name: '📛', name_cannot_be_empty: '📛❌', diff --git a/src/i18n/translations/en.js b/src/i18n/translations/en.js index edee5410..c575de5f 100644 --- a/src/i18n/translations/en.js +++ b/src/i18n/translations/en.js @@ -86,6 +86,7 @@ const en = { delete: 'Delete', delete_account: "Delete Account", delete_permanently: "Delete Permanently", + deleting_file: "Deleting %%", deploy_as_app: 'Deploy as app', descending: 'Descending', desktop_background_fit: "Fit", @@ -142,7 +143,7 @@ const en = { looks_good: "Looks good!", manage_sessions: "Manage Sessions", move: 'Move', - moving: "Moving", + moving_file: "Moving %%", my_websites: "My Websites", name: 'Name', name_cannot_be_empty: 'Name cannot be empty.', diff --git a/src/i18n/translations/es.js b/src/i18n/translations/es.js index 917ef1c3..6d03e69a 100644 --- a/src/i18n/translations/es.js +++ b/src/i18n/translations/es.js @@ -83,7 +83,7 @@ const es = { log_in: "Iniciar sesión", log_out: 'Cerrar sesión', move: 'Mover', - moving: "Moviendo", + moving_file: "Moviendo %%", my_websites: "Mis páginas web", name: 'Nombre', name_cannot_be_empty: 'El nombre no puede estar vacío.', diff --git a/src/i18n/translations/fa.js b/src/i18n/translations/fa.js index 1aecc6d1..8f577361 100644 --- a/src/i18n/translations/fa.js +++ b/src/i18n/translations/fa.js @@ -84,7 +84,7 @@ const fa = { log_in: "ورود", log_out: 'خروج', move: 'انتقال', - moving: "انتقال", + moving_file: "انتقال %%", my_websites: "وبسایت های من", name: 'نام', name_cannot_be_empty: 'نام نمی تواند خالی باشد.', diff --git a/src/i18n/translations/fi.js b/src/i18n/translations/fi.js index 9dc4eb62..37a159df 100644 --- a/src/i18n/translations/fi.js +++ b/src/i18n/translations/fi.js @@ -116,7 +116,7 @@ const fi = { log_in: "Kirjaudu Sisään", log_out: 'Kirjaudu Ulos', move: 'Siirrä', - moving: "Siirretään", + moving_file: "Siirretään %%", my_websites: "Verkkosivustoni", name: 'Nimi', name_cannot_be_empty: 'Nimi ei voi olla tyhjä.', diff --git a/src/i18n/translations/fr.js b/src/i18n/translations/fr.js index 1ff642a6..5f6bbe0e 100644 --- a/src/i18n/translations/fr.js +++ b/src/i18n/translations/fr.js @@ -82,7 +82,7 @@ const fr = { log_in: "Se connecter", log_out: "Se déconnecter", move: "Déplacer", - moving: "Déplacement en cours", + moving_file: "Déplacement en cours %%", my_websites: "Mes sites web", name: "Nom", name_cannot_be_empty: "Le nom ne peut pas être vide.", diff --git a/src/i18n/translations/hy.js b/src/i18n/translations/hy.js index 86ee13c6..06cae2ca 100644 --- a/src/i18n/translations/hy.js +++ b/src/i18n/translations/hy.js @@ -83,7 +83,7 @@ const hy = { log_in: "Մուտք գործել", log_out: "Դուրս գալ", move: "Տեղափոխել", - moving: "Տեղափոխվում է", + moving_file: "Տեղափոխվում է %%", my_websites: "Իմ կայքերը", name: "Անուն", name_cannot_be_empty: "Անվան դաշտը չի կառող լինել դատարկ։", diff --git a/src/i18n/translations/ig.js b/src/i18n/translations/ig.js index 9276a8a8..f10d3094 100644 --- a/src/i18n/translations/ig.js +++ b/src/i18n/translations/ig.js @@ -126,7 +126,7 @@ const ig = { log_out: 'pụọ', manage_sessions: "Jikwaa Oge", move: 'Bugharịa', - moving: "Na Bugharịa", + moving_file: "Na Bugharịa %%", my_websites: "Weebụsaịtị m", name: 'Aha', name_cannot_be_empty: 'Aha enweghị ike ịbụ ihe efu.', diff --git a/src/i18n/translations/it.js b/src/i18n/translations/it.js index 6efe23db..4be3baad 100644 --- a/src/i18n/translations/it.js +++ b/src/i18n/translations/it.js @@ -83,7 +83,7 @@ const it = { log_in: "Accedi", log_out: 'Disconnettiti', move: 'Sposta', - moving: "Spostamento in corso", + moving_file: "Spostamento in corso %%", my_websites: "I miei siti web", name: 'Nome', name_cannot_be_empty: 'Il nome non può essere vuoto.', diff --git a/src/i18n/translations/ko.js b/src/i18n/translations/ko.js index f9f2c8f6..b7318a6e 100644 --- a/src/i18n/translations/ko.js +++ b/src/i18n/translations/ko.js @@ -91,7 +91,7 @@ const ko = { log_in: "로그인", log_out: '로그아웃', move: '이동', - moving: "이동 중", + moving_file: "이동 중 %%", my_websites: "내 웹사이트", name: '이름', name_cannot_be_empty: '이름은 비워둘 수 없습니다.', diff --git a/src/i18n/translations/nb.js b/src/i18n/translations/nb.js index 6fdc67e4..478ef91e 100644 --- a/src/i18n/translations/nb.js +++ b/src/i18n/translations/nb.js @@ -92,7 +92,7 @@ const nb = { log_into_another_account_anyway: 'Logg inn på en annen bruker uansett', log_out: "Logg ut", move: "Flytt", - moving: "Flytter", + moving_file: "Flytter %%", my_websites: "Mine nettsteder", name: "Navn", name_cannot_be_empty: "Navn kan ikke være tomt.", diff --git a/src/i18n/translations/nl.js b/src/i18n/translations/nl.js index 568af6fc..c2d8b75f 100644 --- a/src/i18n/translations/nl.js +++ b/src/i18n/translations/nl.js @@ -102,7 +102,7 @@ const nl = { log_into_another_account_anyway: 'Alsnog met ander account inloggen', log_out: 'Uitloggen', move: 'Verplaatsen', - moving: "Aan het verplaatsen", + moving_file: "Aan het verplaatsen %%", my_websites: "Mijn Websites", name: 'Naam', name_cannot_be_empty: 'Naam kan niet leeg zijn.', diff --git a/src/i18n/translations/nn.js b/src/i18n/translations/nn.js index 15d2e4d2..ccadf63b 100644 --- a/src/i18n/translations/nn.js +++ b/src/i18n/translations/nn.js @@ -83,7 +83,7 @@ const nn = { log_in: "Logg inn", log_out: "Logg ut", move: "Flytt", - moving: "Flyttar", + moving_file: "Flyttar %%", my_websites: "Mine nettstader", name: "Namn", name_cannot_be_empty: "Namn kan ikkje vere tomt.", diff --git a/src/i18n/translations/pl.js b/src/i18n/translations/pl.js index 74628cb2..7db04716 100644 --- a/src/i18n/translations/pl.js +++ b/src/i18n/translations/pl.js @@ -92,7 +92,7 @@ const pl = { log_into_another_account_anyway: 'Zaloguj się do innego konta mimo wszystko', log_out: 'Wyloguj się', move: 'Przenieś', - moving: "Przenoszenie", + moving_file: "Przenoszenie %%", my_websites: "Moje strony", name: 'Nazwa', name_cannot_be_empty: 'Nazwa nie może być pusta.', diff --git a/src/i18n/translations/pt.js b/src/i18n/translations/pt.js index 0d346828..349c906a 100644 --- a/src/i18n/translations/pt.js +++ b/src/i18n/translations/pt.js @@ -106,7 +106,7 @@ const pt = { log_into_another_account_anyway: 'Entrar com outra conta na mesma', log_out: 'Sair', move: 'Mover', - moving: "Movendo", + moving_file: "Movendo %%", my_websites: "Meus Sites", name: 'Nome', name_cannot_be_empty: 'Nome não pode ser vazio.', diff --git a/src/i18n/translations/ro.js b/src/i18n/translations/ro.js index 823b860c..9bca3a48 100644 --- a/src/i18n/translations/ro.js +++ b/src/i18n/translations/ro.js @@ -82,7 +82,7 @@ const ro = { log_in: "Loghează-te", log_out: 'Deconectează-te', move: 'Mută', - moving: "Se mută", + moving_file: "Se mută %%", my_websites: "Site-urile mele", name: 'Nume', name_cannot_be_empty: 'Numele nu poate fi necompletat.', diff --git a/src/i18n/translations/ru.js b/src/i18n/translations/ru.js index bd15f365..9d20d6c4 100644 --- a/src/i18n/translations/ru.js +++ b/src/i18n/translations/ru.js @@ -126,7 +126,7 @@ const ru = { log_out: 'Выйти', manage_sessions: "Управление Сеансами", move: 'Переместить', - moving: "Перемещается", + moving_file: "Перемещается %%", my_websites: "Мои Сайты", name: 'Имя', name_cannot_be_empty: 'Имя не может быть пустым.', diff --git a/src/i18n/translations/sv.js b/src/i18n/translations/sv.js index 6d7d1ece..a05a5e76 100644 --- a/src/i18n/translations/sv.js +++ b/src/i18n/translations/sv.js @@ -83,7 +83,7 @@ const sv = { log_in: "Logga in", log_out: "Logga ut", move: "Flytta", - moving: "Flyttar", + moving_file: "Flyttar %%", my_websites: "Mina webbplatser", name: "Namn", name_cannot_be_empty: "Namn kan inte vara tomt.", diff --git a/src/i18n/translations/th.js b/src/i18n/translations/th.js index 0be6cc90..2e7eeed6 100644 --- a/src/i18n/translations/th.js +++ b/src/i18n/translations/th.js @@ -91,7 +91,7 @@ const th = { log_into_another_account_anyway: "ต้องการเข้าสู่บัญชีอื่น", log_out: "ออกจากระบบ", move: "ย้าย", - moving: "กำลังย้าย", + moving_file: "กำลังย้าย %%", my_websites: "เว็บไซต์ของฉัน", name: "ชื่อ", name_cannot_be_empty: "ไม่สามารถปล่อยช่องชื่อให้ว่างได้", diff --git a/src/i18n/translations/tr.js b/src/i18n/translations/tr.js index 49e8ee23..b440cb0b 100644 --- a/src/i18n/translations/tr.js +++ b/src/i18n/translations/tr.js @@ -116,7 +116,7 @@ const tr = { log_out: "Çıkış Yap", manage_sessions: "Oturumları Yönet", move: "Taşı", - moving: "Taşınıyor", + moving_file: "Taşınıyor %%", my_websites: "Web Sitelerim", name: "Ad", name_cannot_be_empty: "Ad boş olamaz.", diff --git a/src/i18n/translations/ur.js b/src/i18n/translations/ur.js index f6492e40..af177e68 100644 --- a/src/i18n/translations/ur.js +++ b/src/i18n/translations/ur.js @@ -83,7 +83,7 @@ const ur = { log_in: "لاگ ان", log_out: 'لاگ آؤٹ', move: 'منتقل کریں', - moving: "منتقل ہو رہا ہے ", + moving_file: "منتقل ہو رہا ہے %%", my_websites: "میری ویب سائٹیں ", name: 'نام', name_cannot_be_empty: 'نام خالی نہیں ہو سکتا', diff --git a/src/i18n/translations/zh.js b/src/i18n/translations/zh.js index f8b976d0..ee730ad7 100644 --- a/src/i18n/translations/zh.js +++ b/src/i18n/translations/zh.js @@ -87,7 +87,7 @@ const zh = { log_in: "登录", log_out: '登出', move: '移动', - moving: "移动", + moving_file: "移动 %%", my_websites: "我的网站", name: '名称', name_cannot_be_empty: '名称不能为空。', diff --git a/src/i18n/translations/zhtw.js b/src/i18n/translations/zhtw.js index 755fab81..25b1a3b0 100644 --- a/src/i18n/translations/zhtw.js +++ b/src/i18n/translations/zhtw.js @@ -113,7 +113,7 @@ const zhtw = { log_into_another_account_anyway: '無論如何都要登入另一個帳號', log_out: '登出', move: '移動', - moving: "正在移動", + moving_file: "正在移動 %%", my_websites: "我的網站", name: '名稱', name_cannot_be_empty: '名稱不能為空。', From 6d8c709de8a5e31d8c57916740a6eb57d3f69073 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 9 May 2024 18:11:15 +0100 Subject: [PATCH 09/13] Delete unused UIWindowDownloadProgress.js --- src/UI/UIWindowDownloadProgress.js | 81 ------------------------------ src/css/style.css | 4 +- src/helpers/download.js | 3 -- 3 files changed, 2 insertions(+), 86 deletions(-) delete mode 100644 src/UI/UIWindowDownloadProgress.js diff --git a/src/UI/UIWindowDownloadProgress.js b/src/UI/UIWindowDownloadProgress.js deleted file mode 100644 index 337fdcda..00000000 --- a/src/UI/UIWindowDownloadProgress.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (C) 2024 Puter Technologies Inc. - * - * This file is part of Puter. - * - * Puter is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import UIWindow from './UIWindow.js' - -// todo do this using uid rather than item_path, since item_path is way mroe expensive on the DB -async function UIWindowDownloadProgress(options){ - let h = ''; - h += `
`; - h += `
`; - // Spinner - h +=`circle anim`; - // Progress report - h +=`
`; - // msg - h += `${i18n('downloading')}...${options.item_name ?? ''}`; - h += `
`; - // Progress - h += `
`; - h += `
`; - h += `
`; - // Cancel - h += ``; - h +=`
`; - h += `
`; - - const el_window = await UIWindow({ - title: `Upload`, - icon: window.icons[`app-icon-uploader.svg`], - uid: null, - is_dir: false, - body_content: h, - has_head: false, - selectable_body: false, - draggable_body: true, - allow_context_menu: false, - is_resizable: false, - is_droppable: false, - init_center: true, - allow_native_ctxmenu: false, - allow_user_select: false, - window_class: 'window-upload-progress', - width: 450, - dominant: true, - window_css:{ - height: 'initial', - }, - body_css: { - padding: '22px', - width: 'initial', - 'background-color': 'rgba(231, 238, 245, .95)', - 'backdrop-filter': 'blur(3px)', - } - }); - - // cancel download button clicked - $(el_window).find('.download-cancel-btn').on('click', function(){ - window.operation_cancelled[options.operation_id] = true; - $(el_window).close(); - }) - - return el_window; -} - -export default UIWindowDownloadProgress \ No newline at end of file diff --git a/src/css/style.css b/src/css/style.css index dbecf56b..a2657158 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -2708,7 +2708,7 @@ fieldset[name=number-code] { } } -.progress-bar-container, .download-progress-bar-container { +.progress-bar-container { box-sizing: border-box; width: 100%; height: 17px; @@ -2718,7 +2718,7 @@ fieldset[name=number-code] { box-shadow: inset -1px 3px 4px #dfdfdf; } -.progress-bar, .download-progress-bar { +.progress-bar { width: 0; height: 100%; background-color: rgb(0 137 255); diff --git a/src/helpers/download.js b/src/helpers/download.js index 5ccbb8a2..b00d9bca 100644 --- a/src/helpers/download.js +++ b/src/helpers/download.js @@ -109,9 +109,6 @@ const download = function(options){ let batch_progress = ((batch_download_progress[0].cloud_uploaded + batch_download_progress[0].downloaded)/batch_download_progress[0].total * 100).toFixed(0); batch_progress = batch_progress > 100 ? 100 : batch_progress; - // Update the progress bar - $(`[data-download-operation-id="${options.operation_id}"]`).find('.download-progress-bar').css( 'width', batch_progress+'%'); - // If download is finished resolve promise if((item_progress >= 1 || item_progress === 0) && item){ // For a better UX, resolve 0.5 second after operation is finished. From e525747002fe9b201a6ad88e3fc937cfbf358dcd Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 9 May 2024 18:20:34 +0100 Subject: [PATCH 10/13] refactor: Replace UIWindowCopyProgress with UIWindowProgress --- src/UI/UIWindowCopyProgress.js | 81 ---------------------------------- src/helpers.js | 54 ++++++++++++++--------- src/i18n/translations/en.js | 1 + 3 files changed, 35 insertions(+), 101 deletions(-) delete mode 100644 src/UI/UIWindowCopyProgress.js diff --git a/src/UI/UIWindowCopyProgress.js b/src/UI/UIWindowCopyProgress.js deleted file mode 100644 index 297de727..00000000 --- a/src/UI/UIWindowCopyProgress.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (C) 2024 Puter Technologies Inc. - * - * This file is part of Puter. - * - * Puter is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import UIWindow from './UIWindow.js' - -// todo do this using uid rather than item_path, since item_path is way mroe expensive on the DB -async function UIWindowCopyProgress(options){ - let h = ''; - h += `
`; - h += `
`; - // spinner - h +=`circle anim`; - // Progress report - h +=`
`; - // msg - h += `${i18n('copying')} `; - h += ``; - h += `
`; - // progress - h += `
`; - h += `
`; - h += `
`; - // cancel - // h += ``; - h +=`
`; - h += `
`; - - const el_window = await UIWindow({ - title: i18n('copying'), - icon: window.icons[`app-icon-copying.svg`], - uid: null, - is_dir: false, - body_content: h, - has_head: false, - selectable_body: false, - draggable_body: true, - allow_context_menu: false, - is_resizable: false, - is_droppable: false, - init_center: true, - allow_native_ctxmenu: false, - allow_user_select: false, - window_class: 'window-copy-progress', - width: 450, - dominant: true, - window_css:{ - height: 'initial', - }, - body_css: { - padding: '22px', - width: 'initial', - 'background-color': 'rgba(231, 238, 245, .95)', - 'backdrop-filter': 'blur(3px)', - } - }); - - $(el_window).find('.copy-cancel-btn').on('click', function(e){ - window.operation_cancelled[options.operation_id] = true; - $(el_window).close(); - }) - - return el_window; -} - -export default UIWindowCopyProgress \ No newline at end of file diff --git a/src/helpers.js b/src/helpers.js index 2302e54f..5c8ed123 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -24,7 +24,6 @@ import UIItem from './UI/UIItem.js' import UIWindow from './UI/UIWindow.js' import UIWindowLogin from './UI/UIWindowLogin.js'; import UIWindowSaveAccount from './UI/UIWindowSaveAccount.js'; -import UIWindowCopyProgress from './UI/UIWindowCopyProgress.js'; import update_username_in_gui from './helpers/update_username_in_gui.js'; import update_title_based_on_uploads from './helpers/update_title_based_on_uploads.js'; import content_type_to_icon from './helpers/content_type_to_icon.js'; @@ -1273,8 +1272,13 @@ window.copy_clipboard_items = async function(dest_path, dest_container_element){ // only show progress window if it takes longer than 2s to copy let progwin; let progwin_timeout = setTimeout(async () => { - progwin = await UIWindowCopyProgress({operation_id: copy_op_id}); - }, 2000); + progwin = await UIWindowProgress({ + operation_id: copy_op_id, + on_cancel: () => { + window.operation_cancelled[copy_op_id] = true; + }, + }); + }, 0); const copied_item_paths = [] @@ -1282,7 +1286,8 @@ window.copy_clipboard_items = async function(dest_path, dest_container_element){ let copy_path = window.clipboard[i].path; let item_with_same_name_already_exists = true; let overwrite = overwrite_all; - $(progwin).find('.copy-from').html(html_encode(copy_path)); + progwin?.set_status(i18n('copying_file', copy_path)); + do{ if(overwrite) item_with_same_name_already_exists = false; @@ -1350,14 +1355,16 @@ window.copy_clipboard_items = async function(dest_path, dest_container_element){ clearTimeout(progwin_timeout); let copy_duration = (Date.now() - copy_progress_window_init_ts); - if(progwin && copy_duration >= window.copy_progress_hide_delay){ - $(progwin).close(); - }else if(progwin){ - setTimeout(() => { + if (progwin) { + if (copy_duration >= window.copy_progress_hide_delay) { + progwin.close(); + } else { setTimeout(() => { - $(progwin).close(); - }, Math.abs(window.copy_progress_hide_delay - copy_duration)); - }) + setTimeout(() => { + progwin.close(); + }, Math.abs(window.copy_progress_hide_delay - copy_duration)); + }); + } } })(); } @@ -1377,7 +1384,12 @@ window.copy_items = function(el_items, dest_path){ // only show progress window if it takes longer than 2s to copy let progwin; let progwin_timeout = setTimeout(async () => { - progwin = await UIWindowCopyProgress({operation_id: copy_op_id}); + progwin = await UIWindowProgress({ + operation_id: copy_op_id, + on_cancel: () => { + window.operation_cancelled[copy_op_id] = true; + }, + }); }, 2000); const copied_item_paths = [] @@ -1386,7 +1398,7 @@ window.copy_items = function(el_items, dest_path){ let copy_path = $(el_items[i]).attr('data-path'); let item_with_same_name_already_exists = true; let overwrite = overwrite_all; - $(progwin).find('.copy-from').html(html_encode(copy_path)); + progwin?.set_status(i18n('copying_file', copy_path)); do{ if(overwrite) @@ -1455,14 +1467,16 @@ window.copy_items = function(el_items, dest_path){ clearTimeout(progwin_timeout); let copy_duration = (Date.now() - copy_progress_window_init_ts); - if(progwin && copy_duration >= window.copy_progress_hide_delay){ - $(progwin).close(); - }else if(progwin){ - setTimeout(() => { + if (progwin) { + if (copy_duration >= window.copy_progress_hide_delay) { + progwin.close(); + } else { setTimeout(() => { - $(progwin).close(); - }, Math.abs(window.copy_progress_hide_delay - copy_duration)); - }) + setTimeout(() => { + progwin.close(); + }, Math.abs(window.copy_progress_hide_delay - copy_duration)); + }); + } } })() } diff --git a/src/i18n/translations/en.js b/src/i18n/translations/en.js index c575de5f..e31875aa 100644 --- a/src/i18n/translations/en.js +++ b/src/i18n/translations/en.js @@ -70,6 +70,7 @@ const en = { copy: 'Copy', copy_link: "Copy Link", copying: "Copying", + copying_file: "Copying %%", cover: 'Cover', create_account: "Create Account", create_free_account: "Create Free Account", From c2c87bf0bafdc8df1628a14f5aa3ef77f96c754a Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 9 May 2024 15:52:38 +0100 Subject: [PATCH 11/13] refactor: Replace UIWindowDownloadDirProg with UIWindowProgress --- src/UI/UIWindowDownloadDirProg.js | 70 ------------------------------- src/helpers.js | 18 ++++---- src/i18n/translations/en.js | 2 + 3 files changed, 11 insertions(+), 79 deletions(-) delete mode 100644 src/UI/UIWindowDownloadDirProg.js diff --git a/src/UI/UIWindowDownloadDirProg.js b/src/UI/UIWindowDownloadDirProg.js deleted file mode 100644 index 9068fc88..00000000 --- a/src/UI/UIWindowDownloadDirProg.js +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (C) 2024 Puter Technologies Inc. - * - * This file is part of Puter. - * - * Puter is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import UIWindow from './UIWindow.js' - -async function UIWindowDownloadDirProg(options){ - options = options ?? {}; - - let h = ''; - // Loading spinner - h +=`circle anim`; - h += `

${options.defaultText ?? i18n('preparing')}

`; - - const el_window = await UIWindow({ - title: 'Download Directory Progress', - app: 'instant-login', - single_instance: true, - icon: null, - uid: null, - is_dir: false, - body_content: h, - has_head: false, - selectable_body: false, - allow_context_menu: false, - is_resizable: false, - is_droppable: false, - init_center: true, - allow_native_ctxmenu: false, - allow_user_select: false, - backdrop: false, - width: 460, - height: 'auto', - dominant: true, - show_in_taskbar: false, - draggable_body: true, - onAppend: function(this_window){ - }, - window_class: 'window-qr', - body_css: { - width: 'initial', - height: '100px', - 'background-color': 'rgb(245 247 249)', - 'backdrop-filter': 'blur(3px)', - 'display': 'flex', - "flex-direction": 'row', - 'justify-content': 'center', - 'align-items': 'center', - } - }) - - return el_window; -} - -export default UIWindowDownloadDirProg \ No newline at end of file diff --git a/src/helpers.js b/src/helpers.js index 5c8ed123..77c8abe3 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -27,7 +27,6 @@ import UIWindowSaveAccount from './UI/UIWindowSaveAccount.js'; import update_username_in_gui from './helpers/update_username_in_gui.js'; import update_title_based_on_uploads from './helpers/update_title_based_on_uploads.js'; import content_type_to_icon from './helpers/content_type_to_icon.js'; -import UIWindowDownloadDirProg from './UI/UIWindowDownloadDirProg.js'; import { PROCESS_RUNNING, PortalProcess, PseudoProcess } from "./definitions.js"; import UIWindowProgress from './UI/UIWindowProgress.js'; @@ -2965,14 +2964,14 @@ window.zipItems = async function(el_items, targetDirPath, download = true) { let progwin, progwin_timeout; // only show progress window if it takes longer than 500ms to download progwin_timeout = setTimeout(async () => { - progwin = await UIWindowDownloadDirProg(); + progwin = await UIWindowProgress(); }, 500); for (const el_item of el_items) { let targetPath = $(el_item).attr('data-path'); // if directory, zip the directory if($(el_item).attr('data-is_dir') === '1'){ - $(progwin).find('.dir-dl-status').html(`Reading ${html_encode(targetPath)}`); + progwin?.set_status(i18n('reading_file', targetPath)); // Recursively read the directory let children = await readDirectoryRecursive(targetPath); @@ -2985,7 +2984,7 @@ window.zipItems = async function(el_items, targetDirPath, download = true) { relativePath = path.basename(targetPath) + '/' + child.relativePath; // update progress window - $(progwin).find('.dir-dl-status').html(`Zipping ${html_encode(relativePath)}`); + progwin?.set_status(i18n('zipping_file', relativePath)); // read file content let content = await puter.fs.read(child.path); @@ -3034,17 +3033,18 @@ window.zipItems = async function(el_items, targetDirPath, download = true) { // close progress window clearTimeout(progwin_timeout); setTimeout(() => { - $(progwin).close(); + progwin?.close(); }, Math.max(0, window.copy_progress_hide_delay - (Date.now() - start_ts))); }) .catch(function (err) { // close progress window clearTimeout(progwin_timeout); setTimeout(() => { - $(progwin).close(); + progwin?.close(); }, Math.max(0, window.copy_progress_hide_delay - (Date.now() - start_ts))); // handle errors + // TODO: Display in progress dialog console.error("Error in zipping files: ", err); }); } @@ -3087,7 +3087,7 @@ window.unzipItem = async function(itemPath) { let progwin, progwin_timeout; // only show progress window if it takes longer than 500ms to download progwin_timeout = setTimeout(async () => { - progwin = await UIWindowDownloadDirProg(); + progwin = await UIWindowProgress(); }, 500); const zip = new JSZip(); @@ -3109,7 +3109,7 @@ window.unzipItem = async function(itemPath) { // close progress window clearTimeout(progwin_timeout); setTimeout(() => { - $(progwin).close(); + progwin?.close(); }, Math.max(0, window.copy_progress_hide_delay - (Date.now() - start_ts))); }).catch(function (e) { @@ -3117,7 +3117,7 @@ window.unzipItem = async function(itemPath) { // close progress window clearTimeout(progwin_timeout); setTimeout(() => { - $(progwin).close(); + progwin?.close(); }, Math.max(0, window.copy_progress_hide_delay - (Date.now() - start_ts))); }) } diff --git a/src/i18n/translations/en.js b/src/i18n/translations/en.js index e31875aa..14ca930f 100644 --- a/src/i18n/translations/en.js +++ b/src/i18n/translations/en.js @@ -198,6 +198,7 @@ const en = { publish_as_website: 'Publish as website', puter_description: `Puter is a privacy-first personal cloud to keep all your files, apps, and games in one secure place, accessible from anywhere at any time.`, + reading_file: "Reading %strong%", recent: "Recent", recommended: "Recommended", recover_password: "Recover Password", @@ -281,6 +282,7 @@ const en = { yes_release_it: 'Yes, Release It', you_have_been_referred_to_puter_by_a_friend: "You have been referred to Puter by a friend!", zip: "Zip", + zipping_file: "Zipping %strong%", // === 2FA Setup === setup2fa_1_step_heading: 'Open your authenticator app', From f3269693de7cce109da42834591d036e286d2828 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 9 May 2024 15:55:07 +0100 Subject: [PATCH 12/13] Stop dumping binary data to the console This was freaking out and freezing the Puter server, and my entire terminal app. XD --- packages/backend/src/services/file-cache/FileCacheService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/services/file-cache/FileCacheService.js b/packages/backend/src/services/file-cache/FileCacheService.js index b2e6121a..c46e437e 100644 --- a/packages/backend/src/services/file-cache/FileCacheService.js +++ b/packages/backend/src/services/file-cache/FileCacheService.js @@ -250,7 +250,7 @@ class FileCacheService extends AdvancedBase { const path = this._get_path(tracker.key); console.log(`precache fetch key I guess?`, tracker.key); const data = this.precache.get(tracker.key); - console.log(`path and data: ${path} ${data}`); + // console.log(`path and data: ${path} ${data}`); await fs.promises.writeFile(path, data); this.precache.delete(tracker.key); tracker.phase = FileTracker.PHASE_DISK; From edebbee9e7e9efbb33bf709b637c103be40d15a8 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 9 May 2024 19:00:55 +0100 Subject: [PATCH 13/13] feat: Display upload errors in UIWindowProgress dialog 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. :^) --- src/UI/UIWindowProgress.js | 68 ++++++++++++++++++++++++++----------- src/helpers.js | 4 +-- src/i18n/translations/en.js | 1 + 3 files changed, 51 insertions(+), 22 deletions(-) diff --git a/src/UI/UIWindowProgress.js b/src/UI/UIWindowProgress.js index 85dc5da9..3f111858 100644 --- a/src/UI/UIWindowProgress.js +++ b/src/UI/UIWindowProgress.js @@ -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({ @@ -37,30 +36,47 @@ async function UIWindowProgress({ on_cancel = null, } = {}){ const placeholder_cancel_btn = Placeholder(); + const placeholder_ok_btn = Placeholder(); let h = ''; h += `
`; - h += `
`; - // spinner - h += `circle anim`; - // Progress report - h += `
- ${i18n('preparing')}`; + h += `
`; + h += `
`; + // spinner + h += `circle anim`; + // Progress report + h += `
+ ${i18n('preparing')}`; + if (show_progress) { + h += ` (0%)`; + } + h += `
`; + h +=`
`; if (show_progress) { - h += ` (0%)`; + h += `
`; + h += `
`; + h += `
`; } + if (on_cancel) { + h += `
`; + h += placeholder_cancel_btn.html; + h += `
`; + } + h += `
`; + h += ``; - if (show_progress) { - h += `
`; - h += `
`; - h += `
`; - } - if (on_cancel) { + h += `

`; h += `
`; - h += placeholder_cancel_btn.html; + h += placeholder_ok_btn.html; h += `
`; - } + h += `
`; h += `
`; const el_window = await UIWindow({ @@ -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) => { @@ -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; + }, }; } diff --git a/src/helpers.js b/src/helpers.js index 77c8abe3..f4e3d134 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -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]; }, diff --git a/src/i18n/translations/en.js b/src/i18n/translations/en.js index 14ca930f..ad2abc47 100644 --- a/src/i18n/translations/en.js +++ b/src/i18n/translations/en.js @@ -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.",