mirror of
https://github.com/HeyPuter/puter.git
synced 2025-12-30 17:50:00 -06:00
Re-add direct download via URL parameter (#1914)
* Re-add direct download via URL parameter * Add comments * Confirm before downloading remote file * Allow aborting remote downloads * Preemptively add AbortController to fetch call puter.net.fetch currently doesn't support it (yet!) * Add filename to download dialog/progress dialogs
This commit is contained in:
@@ -30,6 +30,7 @@ import UIWindowFeedback from "./UIWindowFeedback.js"
|
||||
import UIWindowLogin from "./UIWindowLogin.js"
|
||||
import UIWindowQR from "./UIWindowQR.js"
|
||||
import UIWindowRefer from "./UIWindowRefer.js"
|
||||
import UIWindowProgress from "./UIWindowProgress.js"
|
||||
import UITaskbar from "./UITaskbar.js"
|
||||
import new_context_menu_item from "../helpers/new_context_menu_item.js"
|
||||
import refresh_item_container from "../helpers/refresh_item_container.js"
|
||||
@@ -1304,8 +1305,7 @@ async function UIDesktop(options) {
|
||||
// load from direct app URLs: /app/app-name
|
||||
// ---------------------------------------------
|
||||
else if (window.app_launched_from_url) {
|
||||
let qparams = new URLSearchParams(window.location.search);
|
||||
if (!qparams.has('c')) {
|
||||
if (!window.url_query_params.has('c')) {
|
||||
let posargs = undefined;
|
||||
if (window.app_query_params && window.app_query_params.posargs) {
|
||||
posargs = JSON.parse(window.app_query_params.posargs);
|
||||
@@ -1313,8 +1313,8 @@ async function UIDesktop(options) {
|
||||
launch_app({
|
||||
app: window.app_launched_from_url.name,
|
||||
app_obj: window.app_launched_from_url,
|
||||
readURL: qparams.get('readURL'),
|
||||
maximized: qparams.get('maximized'),
|
||||
readURL: window.url_query_params.get('readURL'),
|
||||
maximized: window.url_query_params.get('maximized'),
|
||||
params: window.app_query_params ?? [],
|
||||
...(posargs ? {
|
||||
args: {
|
||||
@@ -1678,11 +1678,11 @@ async function UIDesktop(options) {
|
||||
// i.e. https://puter.com/@<username>
|
||||
//--------------------------------------------------------------------------------------
|
||||
const url_paths = window.location.pathname.split('/').filter(element => element);
|
||||
if (url_paths[0]?.startsWith('@')) {
|
||||
const username = url_paths[0].substring(1);
|
||||
if (window.url_paths[0]?.startsWith('@')) {
|
||||
const username = window.url_paths[0].substring(1);
|
||||
let item_path = '/' + username + '/Public';
|
||||
if ( url_paths.length > 1 ) {
|
||||
item_path += '/' + url_paths.slice(1).join('/');
|
||||
if ( window.url_paths.length > 1 ) {
|
||||
item_path += '/' + window.url_paths.slice(1).join('/');
|
||||
}
|
||||
|
||||
// GUARD: avoid invalid user directories
|
||||
@@ -1781,6 +1781,117 @@ async function UIDesktop(options) {
|
||||
app: 'explorer',
|
||||
});
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Direct download link
|
||||
// i.e. https://puter.com/?download=<file_url>
|
||||
//--------------------------------------------------------------------------------------
|
||||
if (window.url_paths.length === 0 && window.url_query_params.has('download')) {
|
||||
const url = window.url_query_params.get('download');
|
||||
let file_name = url.split('/').pop().split('?')[0];
|
||||
|
||||
let response = await UIAlert({
|
||||
message: i18n('confirm_download_file_to_desktop', file_name),
|
||||
type: 'confirm',
|
||||
buttons: [
|
||||
{ label: i18n('alert_yes'), value: true, type: "primary" },
|
||||
{ label: i18n('alert_no'), value: false, type: "secondary" }
|
||||
],
|
||||
});
|
||||
|
||||
if (!response)
|
||||
return;
|
||||
|
||||
|
||||
|
||||
let cancelled = false;
|
||||
let upload_xhr = null;
|
||||
const abort_controller = new AbortController();
|
||||
|
||||
// create progressbar dialog
|
||||
let progwin = await UIWindowProgress({
|
||||
title: i18n('downloading'),
|
||||
icon: window.icons[`app-icon-uploader.svg`],
|
||||
operation_id: window.uuidv4(),
|
||||
show_progress: true,
|
||||
on_cancel: () => {
|
||||
cancelled = true;
|
||||
abort_controller.abort();
|
||||
if (upload_xhr) {
|
||||
upload_xhr.abort();
|
||||
}
|
||||
}
|
||||
});
|
||||
progwin?.set_status(i18n('downloading_file', file_name));
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
// download the file
|
||||
const response = await puter.net.fetch(url, {
|
||||
signal: abort_controller.signal,
|
||||
});
|
||||
|
||||
const total = Number(response.headers.get('content-length'));
|
||||
const reader = response.body.getReader();
|
||||
|
||||
const chunks = [];
|
||||
let received = 0;
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done || cancelled) break;
|
||||
|
||||
if (value) {
|
||||
// store the chunk
|
||||
chunks.push(value);
|
||||
received += value.length;
|
||||
// calculate progress
|
||||
const progress = Number.isFinite(total) && total > 0
|
||||
? received / total
|
||||
: 0;
|
||||
// update progressbar
|
||||
progwin?.set_progress(Math.floor(progress * 100));
|
||||
}
|
||||
}
|
||||
|
||||
if (cancelled) {
|
||||
progwin?.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// combine chunks into a blob
|
||||
let blob = new Blob(chunks, {
|
||||
type: response.headers.get('content-type') ?? 'application/octet-stream',
|
||||
});
|
||||
|
||||
// reset progressbar
|
||||
progwin?.set_progress(0);
|
||||
progwin?.set_status(i18n('uploading_file', file_name));
|
||||
|
||||
// upload to user's desktop
|
||||
await puter.fs.write(`~/Desktop/${file_name}`, blob, {
|
||||
dedupeName: true,
|
||||
progress: (_, percent) => {
|
||||
// update progressbar
|
||||
progwin?.set_progress(percent);
|
||||
},
|
||||
init: (_, xhr) => {
|
||||
upload_xhr = xhr;
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
// alert the user if there's a genuine error
|
||||
if (!cancelled && e.name !== 'AbortError') {
|
||||
await UIAlert({
|
||||
message: i18n('error_download_failed') + ': ' + e.message,
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
}
|
||||
// close progress window
|
||||
progwin?.close();
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
$(document).on('contextmenu taphold', '.taskbar', function (event) {
|
||||
|
||||
@@ -116,8 +116,11 @@ const en = {
|
||||
documents: 'Documents',
|
||||
dont_allow: 'Don\'t Allow',
|
||||
download: 'Download',
|
||||
confirm_download_file_to_desktop: 'Are you sure you want to download %% to your Desktop?',
|
||||
download_file: 'Download File',
|
||||
downloading: "Downloading",
|
||||
downloading_file: "Downloading %%",
|
||||
error_download_failed: "Failed to download file",
|
||||
email: "Email",
|
||||
email_change_confirmation_sent: "A confirmation email has been sent to your new email address. Please check your inbox and follow the instructions to complete the process.",
|
||||
email_invalid: 'Email is invalid.',
|
||||
@@ -324,6 +327,8 @@ const en = {
|
||||
untar: "Untar",
|
||||
untarring: "Untarring %strong%",
|
||||
upload: 'Upload',
|
||||
uploading: "Uploading",
|
||||
uploading_file: "Uploading %%",
|
||||
upload_here: 'Upload here',
|
||||
used_of: '{{used}} used of {{available}}',
|
||||
usage: 'Usage',
|
||||
|
||||
Reference in New Issue
Block a user