From 97f58bbbe335eb72f527139e18191d49e54cc801 Mon Sep 17 00:00:00 2001 From: xenofem Date: Thu, 26 May 2022 15:42:11 -0400 Subject: [PATCH] let uploader set a collection name for a multiple-file upload --- src/upload.rs | 18 ++++++++++++++---- static/css/states.css | 3 +++ static/css/transbeam.css | 1 + static/js/upload.js | 7 +++++++ templates/index.html | 3 +++ 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/upload.rs b/src/upload.rs index c266a82..525fb43 100644 --- a/src/upload.rs +++ b/src/upload.rs @@ -20,6 +20,10 @@ const MAX_FILES: usize = 256; const FILENAME_DATE_FORMAT: &[time::format_description::FormatItem] = time::macros::format_description!("[year]-[month]-[day]-[hour][minute][second]"); +fn sanitise(name: &str) -> String { + sanitise_file_name::sanitise(&name.nfd().collect::()) +} + #[derive(thiserror::Error, Debug)] enum Error { #[error("Failed to parse file metadata")] @@ -104,7 +108,7 @@ pub struct UploadedFile { impl UploadedFile { fn new(name: &str, size: u64, modtime: OffsetDateTime) -> Self { Self { - name: sanitise_file_name::sanitise(&name.nfd().collect::()), + name: sanitise(name), size, modtime, } @@ -134,6 +138,7 @@ struct UploadManifest { files: Vec, lifetime: u16, password: String, + collection_name: Option, } #[derive(Serialize)] @@ -229,6 +234,7 @@ impl Uploader { files: raw_files, lifetime, password, + collection_name, } = serde_json::from_slice(text.as_bytes())?; if std::env::var("TRANSBEAM_UPLOAD_PASSWORD") != Ok(password) { return Err(Error::IncorrectPassword); @@ -277,9 +283,13 @@ impl Uploader { let now = OffsetDateTime::now_utc(); let zip_writer = super::zip::ZipGenerator::new(files.clone(), writer); let size = zip_writer.total_size(); - let download_filename = super::APP_NAME.to_owned() - + &now.format(FILENAME_DATE_FORMAT).unwrap() - + ".zip"; + let download_filename = collection_name + .map(|f| sanitise(&(f + ".zip"))) + .unwrap_or_else(|| { + super::APP_NAME.to_owned() + + &now.format(FILENAME_DATE_FORMAT).unwrap() + + ".zip" + }); (Box::new(zip_writer), download_filename, size, now) } else { ( diff --git a/static/css/states.css b/static/css/states.css index 7dd95d7..530e5ed 100644 --- a/static/css/states.css +++ b/static/css/states.css @@ -4,6 +4,7 @@ * landing: haven't entered upload password yet * uploads_closed: uploading is currently unavailable * no_files: no files are selected + * one_file: exactly one file is selected * selecting: upload hasn't started yet * uploading: upload is in progress * completed: upload is done @@ -43,6 +44,8 @@ body.landing #upload_controls { display: none; } body.selecting #upload_settings { display: revert; } body.no_files #upload_settings { display: none; } +body.one_file #collection_name { display: none; } + body.selecting #download_code_container { display: none; } #progress_container { display: none; } diff --git a/static/css/transbeam.css b/static/css/transbeam.css index c0d48c9..32bdd0c 100644 --- a/static/css/transbeam.css +++ b/static/css/transbeam.css @@ -273,6 +273,7 @@ button:disabled, input:disabled + .fake_button, input[type="submit"]:disabled { #lifetime_container { margin-top: 10px; + margin-bottom: 10px; } input[type="text"], input[type="password"] { diff --git a/static/js/upload.js b/static/js/upload.js index caa33bf..debf733 100644 --- a/static/js/upload.js +++ b/static/js/upload.js @@ -21,6 +21,7 @@ let fileInput; let fileList; let uploadButton; let lifetimeInput; +let collectionNameInput; let downloadCode; let progressPercentage; @@ -43,6 +44,7 @@ document.addEventListener('DOMContentLoaded', () => { fileList = document.getElementById('file_list'); uploadButton = document.getElementById('upload_button'); lifetimeInput = document.getElementById('lifetime'); + collectionNameInput = document.getElementById('collection_name'); downloadCode = document.getElementById('download_code'); progressPercentage = document.getElementById('progress_percentage'); progressSize = document.getElementById('progress_size'); @@ -120,6 +122,9 @@ function updateFiles() { fileInputMessage.textContent = 'Select files to upload...'; document.body.className = 'no_files selecting' + extraClasses; } else { + if (files.length === 1) { + extraClasses += " one_file"; + } fileInputMessage.textContent = 'Select more files to upload...'; uploadButton.textContent = `Upload ${files.length} file${files.length > 1 ? 's' : ''} (${displaySize(totalBytes)})`; document.body.className = 'selecting' + extraClasses; @@ -184,6 +189,7 @@ function beginUpload() { function sendManifest() { const lifetime = parseInt(lifetimeInput.value); + const collection_name = collectionNameInput.value || null; const fileMetadata = files.map((file) => ({ name: file.name, size: file.size, @@ -192,6 +198,7 @@ function sendManifest() { socket.send(JSON.stringify({ files: fileMetadata, lifetime, + collection_name, password: uploadPassword, })); } diff --git a/templates/index.html b/templates/index.html index 81b3612..ca3bf6c 100644 --- a/templates/index.html +++ b/templates/index.html @@ -60,6 +60,9 @@ +
+ +