let uploader set a collection name for a multiple-file upload
This commit is contained in:
parent
3125e1f4e7
commit
97f58bbbe3
|
@ -20,6 +20,10 @@ const MAX_FILES: usize = 256;
|
||||||
const FILENAME_DATE_FORMAT: &[time::format_description::FormatItem] =
|
const FILENAME_DATE_FORMAT: &[time::format_description::FormatItem] =
|
||||||
time::macros::format_description!("[year]-[month]-[day]-[hour][minute][second]");
|
time::macros::format_description!("[year]-[month]-[day]-[hour][minute][second]");
|
||||||
|
|
||||||
|
fn sanitise(name: &str) -> String {
|
||||||
|
sanitise_file_name::sanitise(&name.nfd().collect::<String>())
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
enum Error {
|
enum Error {
|
||||||
#[error("Failed to parse file metadata")]
|
#[error("Failed to parse file metadata")]
|
||||||
|
@ -104,7 +108,7 @@ pub struct UploadedFile {
|
||||||
impl UploadedFile {
|
impl UploadedFile {
|
||||||
fn new(name: &str, size: u64, modtime: OffsetDateTime) -> Self {
|
fn new(name: &str, size: u64, modtime: OffsetDateTime) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: sanitise_file_name::sanitise(&name.nfd().collect::<String>()),
|
name: sanitise(name),
|
||||||
size,
|
size,
|
||||||
modtime,
|
modtime,
|
||||||
}
|
}
|
||||||
|
@ -134,6 +138,7 @@ struct UploadManifest {
|
||||||
files: Vec<RawUploadedFile>,
|
files: Vec<RawUploadedFile>,
|
||||||
lifetime: u16,
|
lifetime: u16,
|
||||||
password: String,
|
password: String,
|
||||||
|
collection_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
@ -229,6 +234,7 @@ impl Uploader {
|
||||||
files: raw_files,
|
files: raw_files,
|
||||||
lifetime,
|
lifetime,
|
||||||
password,
|
password,
|
||||||
|
collection_name,
|
||||||
} = serde_json::from_slice(text.as_bytes())?;
|
} = serde_json::from_slice(text.as_bytes())?;
|
||||||
if std::env::var("TRANSBEAM_UPLOAD_PASSWORD") != Ok(password) {
|
if std::env::var("TRANSBEAM_UPLOAD_PASSWORD") != Ok(password) {
|
||||||
return Err(Error::IncorrectPassword);
|
return Err(Error::IncorrectPassword);
|
||||||
|
@ -277,9 +283,13 @@ impl Uploader {
|
||||||
let now = OffsetDateTime::now_utc();
|
let now = OffsetDateTime::now_utc();
|
||||||
let zip_writer = super::zip::ZipGenerator::new(files.clone(), writer);
|
let zip_writer = super::zip::ZipGenerator::new(files.clone(), writer);
|
||||||
let size = zip_writer.total_size();
|
let size = zip_writer.total_size();
|
||||||
let download_filename = super::APP_NAME.to_owned()
|
let download_filename = collection_name
|
||||||
+ &now.format(FILENAME_DATE_FORMAT).unwrap()
|
.map(|f| sanitise(&(f + ".zip")))
|
||||||
+ ".zip";
|
.unwrap_or_else(|| {
|
||||||
|
super::APP_NAME.to_owned()
|
||||||
|
+ &now.format(FILENAME_DATE_FORMAT).unwrap()
|
||||||
|
+ ".zip"
|
||||||
|
});
|
||||||
(Box::new(zip_writer), download_filename, size, now)
|
(Box::new(zip_writer), download_filename, size, now)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* landing: haven't entered upload password yet
|
* landing: haven't entered upload password yet
|
||||||
* uploads_closed: uploading is currently unavailable
|
* uploads_closed: uploading is currently unavailable
|
||||||
* no_files: no files are selected
|
* no_files: no files are selected
|
||||||
|
* one_file: exactly one file is selected
|
||||||
* selecting: upload hasn't started yet
|
* selecting: upload hasn't started yet
|
||||||
* uploading: upload is in progress
|
* uploading: upload is in progress
|
||||||
* completed: upload is done
|
* completed: upload is done
|
||||||
|
@ -43,6 +44,8 @@ body.landing #upload_controls { display: none; }
|
||||||
body.selecting #upload_settings { display: revert; }
|
body.selecting #upload_settings { display: revert; }
|
||||||
body.no_files #upload_settings { display: none; }
|
body.no_files #upload_settings { display: none; }
|
||||||
|
|
||||||
|
body.one_file #collection_name { display: none; }
|
||||||
|
|
||||||
body.selecting #download_code_container { display: none; }
|
body.selecting #download_code_container { display: none; }
|
||||||
|
|
||||||
#progress_container { display: none; }
|
#progress_container { display: none; }
|
||||||
|
|
|
@ -273,6 +273,7 @@ button:disabled, input:disabled + .fake_button, input[type="submit"]:disabled {
|
||||||
|
|
||||||
#lifetime_container {
|
#lifetime_container {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="text"], input[type="password"] {
|
input[type="text"], input[type="password"] {
|
||||||
|
|
|
@ -21,6 +21,7 @@ let fileInput;
|
||||||
let fileList;
|
let fileList;
|
||||||
let uploadButton;
|
let uploadButton;
|
||||||
let lifetimeInput;
|
let lifetimeInput;
|
||||||
|
let collectionNameInput;
|
||||||
let downloadCode;
|
let downloadCode;
|
||||||
|
|
||||||
let progressPercentage;
|
let progressPercentage;
|
||||||
|
@ -43,6 +44,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
fileList = document.getElementById('file_list');
|
fileList = document.getElementById('file_list');
|
||||||
uploadButton = document.getElementById('upload_button');
|
uploadButton = document.getElementById('upload_button');
|
||||||
lifetimeInput = document.getElementById('lifetime');
|
lifetimeInput = document.getElementById('lifetime');
|
||||||
|
collectionNameInput = document.getElementById('collection_name');
|
||||||
downloadCode = document.getElementById('download_code');
|
downloadCode = document.getElementById('download_code');
|
||||||
progressPercentage = document.getElementById('progress_percentage');
|
progressPercentage = document.getElementById('progress_percentage');
|
||||||
progressSize = document.getElementById('progress_size');
|
progressSize = document.getElementById('progress_size');
|
||||||
|
@ -120,6 +122,9 @@ function updateFiles() {
|
||||||
fileInputMessage.textContent = 'Select files to upload...';
|
fileInputMessage.textContent = 'Select files to upload...';
|
||||||
document.body.className = 'no_files selecting' + extraClasses;
|
document.body.className = 'no_files selecting' + extraClasses;
|
||||||
} else {
|
} else {
|
||||||
|
if (files.length === 1) {
|
||||||
|
extraClasses += " one_file";
|
||||||
|
}
|
||||||
fileInputMessage.textContent = 'Select more files to upload...';
|
fileInputMessage.textContent = 'Select more files to upload...';
|
||||||
uploadButton.textContent = `Upload ${files.length} file${files.length > 1 ? 's' : ''} (${displaySize(totalBytes)})`;
|
uploadButton.textContent = `Upload ${files.length} file${files.length > 1 ? 's' : ''} (${displaySize(totalBytes)})`;
|
||||||
document.body.className = 'selecting' + extraClasses;
|
document.body.className = 'selecting' + extraClasses;
|
||||||
|
@ -184,6 +189,7 @@ function beginUpload() {
|
||||||
|
|
||||||
function sendManifest() {
|
function sendManifest() {
|
||||||
const lifetime = parseInt(lifetimeInput.value);
|
const lifetime = parseInt(lifetimeInput.value);
|
||||||
|
const collection_name = collectionNameInput.value || null;
|
||||||
const fileMetadata = files.map((file) => ({
|
const fileMetadata = files.map((file) => ({
|
||||||
name: file.name,
|
name: file.name,
|
||||||
size: file.size,
|
size: file.size,
|
||||||
|
@ -192,6 +198,7 @@ function sendManifest() {
|
||||||
socket.send(JSON.stringify({
|
socket.send(JSON.stringify({
|
||||||
files: fileMetadata,
|
files: fileMetadata,
|
||||||
lifetime,
|
lifetime,
|
||||||
|
collection_name,
|
||||||
password: uploadPassword,
|
password: uploadPassword,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,9 @@
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="text" id="collection_name" placeholder="Collection name"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="download_code_container">
|
<div id="download_code_container">
|
||||||
<div id="download_code_main">
|
<div id="download_code_main">
|
||||||
|
|
Loading…
Reference in a new issue