From d055bb7a7a09fdaecd5911a6c2caa9b015bcd167 Mon Sep 17 00:00:00 2001 From: xenofem Date: Wed, 27 Apr 2022 23:52:45 -0400 Subject: [PATCH] actual nice UI --- static/index.html | 46 ++++++++++++++------ static/transbeam.css | 54 ++++++++++++++++++++++- static/upload.js | 100 +++++++++++++++++++++++++++++++------------ static/util.js | 14 ++++++ 4 files changed, 172 insertions(+), 42 deletions(-) create mode 100644 static/util.js diff --git a/static/index.html b/static/index.html index 9023dc8..d471163 100644 --- a/static/index.html +++ b/static/index.html @@ -4,21 +4,41 @@ + Upload Test -
- -
- -

Files selected:

- - -
- + + + + + + + + +
+ +
+ + diff --git a/static/transbeam.css b/static/transbeam.css index d442e02..e9968d3 100644 --- a/static/transbeam.css +++ b/static/transbeam.css @@ -1,10 +1,54 @@ +body { + text-align: center; + font-family: sans-serif; + max-width: 512px; + margin-left: auto; + margin-right: auto; +} + +#progress_container { + margin: 10px auto; +} + +#progress_bar, #progress_bar_filled { + height: 20px; + border-radius: 8px; +} +#progress_bar { + border: 1px solid #48f; +} +#progress_bar_filled { + width: 0; + background-color: #27f; +} + +table { + border-collapse: collapse; + margin: 20px auto; +} + +tr + tr td { + border-top: 1px solid #ddd; +} + +td { + padding: 10px; +} + +td.file_size { + text-align: right; +} + +td.file_name { + text-align: left; +} + input[type="file"] { display: none; } button, .fake_button { font-size: 18px; - font-family: sans-serif; color: #000; background-color: #ccc; border: 1px solid #bbb; @@ -23,3 +67,11 @@ button:disabled, input:disabled + .fake_button { border-color: #ddd; cursor: not-allowed; } + +#footer { + margin-top: 30px; +} + +#footer h5 { + margin: 5px auto; +} diff --git a/static/upload.js b/static/upload.js index d332674..d1b570e 100644 --- a/static/upload.js +++ b/static/upload.js @@ -1,5 +1,21 @@ const FILE_CHUNK_SIZE = 16384; +const fileInputContainer = document.getElementById('file_input_container'); +const fileInput = document.getElementById('file_input'); +const fileInputMessage = document.getElementById('file_input_message'); + +const fileListContainer = document.getElementById('file_list_container'); +const fileList = document.getElementById('file_list'); + +const uploadButton = document.getElementById('upload'); + +const downloadLinkContainer = document.getElementById('download_link_container'); +const downloadLink = document.getElementById('download_link'); + +const progressContainer = document.getElementById('progress_container'); +const progress = document.getElementById('progress'); +const progressBarFilled = document.getElementById('progress_bar_filled'); + let files = []; let socket = null; @@ -23,7 +39,7 @@ function finishSending() { return; } socket.close(); - alert("done"); + progressContainer.textContent = "Upload complete!"; } function sendData() { @@ -38,7 +54,7 @@ function sendData() { socket.send(data); byteIndex = endpoint; bytesSent += data.size; - progress.textContent = `${Math.floor(bytesSent * 100 / totalBytes)}%`; + updateProgress(); } else { fileIndex += 1; byteIndex = 0; @@ -46,42 +62,69 @@ function sendData() { } } -const fileInput = document.getElementById('file_input'); -const fileInputMessage = document.getElementById('file_input_message'); -const fileList = document.getElementById('file_list'); -const uploadButton = document.getElementById('upload'); -const downloadLink = document.getElementById('download_link'); -const progress = document.getElementById('progress'); - -function updateButtons() { - if (files.length === 0) { - uploadButton.disabled = true; - fileInputMessage.textContent = 'Select files to upload...'; +function updateProgress() { + let percentage; + if (totalBytes === 0) { + percentage = "0%"; + } else { + percentage = `${(bytesSent*100/totalBytes).toFixed(1)}%`; + } + progress.textContent = percentage; + progressBarFilled.style.width = percentage; +} + +function updateFiles() { + totalBytes = files.reduce((acc, file) => acc + file.size, 0); + + if (files.length === 0) { + fileInputMessage.textContent = 'Select files to upload...'; + fileListContainer.style.display = 'none'; + uploadButton.style.display = 'none'; } else { - uploadButton.disabled = false; fileInputMessage.textContent = 'Select more files to upload...'; + fileListContainer.style.display = ''; + uploadButton.textContent = `Upload ${files.length} file${files.length > 1 ? 's' : ''} (${displaySize(totalBytes)})`; + uploadButton.style.display = ''; } } -updateButtons(); +updateFiles(); +downloadLinkContainer.style.display = 'none'; +progressContainer.style.display = 'none'; function addFile(newFile) { if (files.some((oldFile) => newFile.name === oldFile.name)) { return; } files.push(newFile); - const listEntry = document.createElement('li'); + addListEntry(newFile); +} + +function addListEntry(file) { + const listEntry = document.createElement('tr'); + + const deleteButtonCell = document.createElement('td'); const deleteButton = document.createElement('button'); + deleteButtonCell.appendChild(deleteButton); + deleteButton.className = 'file_delete'; deleteButton.textContent = 'x'; deleteButton.addEventListener('click', () => { - removeFile(newFile.name); + removeFile(file.name); listEntry.remove(); - updateButtons(); + updateFiles(); }); - const entryName = document.createElement('span'); - entryName.textContent = newFile.name; - listEntry.appendChild(deleteButton); - listEntry.appendChild(entryName); + + const sizeCell = document.createElement('td'); + sizeCell.className = 'file_size'; + sizeCell.textContent = displaySize(file.size); + + const nameCell = document.createElement('td'); + nameCell.className = 'file_name'; + nameCell.textContent = file.name; + + listEntry.appendChild(deleteButtonCell); + listEntry.appendChild(sizeCell); + listEntry.appendChild(nameCell); fileList.appendChild(listEntry); } @@ -92,25 +135,26 @@ function removeFile(name) { fileInput.addEventListener('input', (e) => { for (const file of e.target.files) { addFile(file); } - updateButtons(); + updateFiles(); e.target.value = ''; }); uploadButton.addEventListener('click', (e) => { if (files.length === 0) { return; } - fileInput.disabled = true; - for (const button of document.getElementsByTagName('button')) { - button.disabled = true; + fileInputContainer.remove(); + for (const button of Array.from(document.getElementsByTagName('button'))) { + button.remove(); } - totalBytes = files.reduce((acc, file) => acc + file.size, 0); - socket = new WebSocket(`${window.location.protocol === 'http:' ? 'ws' : 'wss'}://${window.location.host}/upload`); socket.addEventListener('open', sendMetadata); socket.addEventListener('message', (msg) => { if (bytesSent === 0 && msg.data.match(/^[A-Za-z0-9]+$/)) { downloadLink.textContent = `${window.location.origin}/download/${msg.data}`; + downloadLinkContainer.style.display = ''; + updateProgress(); + progressContainer.style.display = ''; sendData(); } else if (msg.data === 'ack') { sendData(); diff --git a/static/util.js b/static/util.js new file mode 100644 index 0000000..195d803 --- /dev/null +++ b/static/util.js @@ -0,0 +1,14 @@ +const UNITS = [ + { name: 'GB', size: Math.pow(2, 30) }, + { name: 'MB', size: Math.pow(2, 20) }, + { name: 'KB', size: Math.pow(2, 10) }, +]; + +function displaySize(bytes) { + for (const unit of UNITS) { + if (bytes >= unit.size) { + return `${(bytes / unit.size).toFixed(1)}${unit.name}`; + } + } + return `${bytes}B`; +}