From 2d78b545a587c4334624d869997b2326dad32573 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Fri, 15 Feb 2019 18:52:28 -0800 Subject: Refactor file upload interface to use jQuery, and to have separate upload progress bars. The progess bars make clear when the data has been sent to the first Tor node, and when its waiting on the rest of the Tor network. It also allows the sender to cancel uploads --- share/static/css/style.css | 72 +++++++++--- share/static/img/ajax.gif | Bin 0 -> 847 bytes share/static/js/receive-noscript.js | 2 + share/static/js/receive.js | 218 ++++++++++++++++++++++-------------- share/templates/receive.html | 69 ++++++------ 5 files changed, 226 insertions(+), 135 deletions(-) create mode 100644 share/static/img/ajax.gif create mode 100644 share/static/js/receive-noscript.js diff --git a/share/static/css/style.css b/share/static/css/style.css index 73d3e6f9..65bafba5 100644 --- a/share/static/css/style.css +++ b/share/static/css/style.css @@ -9,7 +9,7 @@ body { margin: 0; - font-family: Helvetica; + font-family: Helvetica, sans-serif; } header { @@ -103,58 +103,96 @@ table.file-list td:last-child { } .upload-wrapper { - display: flex; align-items: center; justify-content: center; min-height: 400px; -} - -.upload { text-align: center; } -.upload img { +.upload-wrapper img.logo { width: 120px; height: 120px; } -.upload .upload-header { +.upload-wrapper .upload-header { font-size: 30px; font-weight: normal; color: #666666; margin: 0 0 10px 0; } -.upload .upload-description { +.upload-wrapper .upload-description { color: #666666; margin: 0 0 20px 0; } +div#uploads { + width: 800px; + max-width: 90%; + margin: 0 auto; +} + +div#uploads .upload { + border: 1px solid #DDDDDD; + margin: 20px 0; + padding: 10px; + text-align: left; +} + +div#uploads .upload .upload-filename { + font-weight: bold; + font-family: monospace; + font-size: 1.1em; + margin-bottom: 5px; +} + +div#uploads .upload .upload-status { + color: #999999; + font-size: 0.9em; + margin-bottom: 5px; +} + +div#uploads .upload input.cancel { + color: #f24537; + border: 0; + background: none; + box-shadow: none; + border-radius: 0px; + cursor: pointer; + font-family: sans-serif; + font-size: 12px; + text-decoration: none; + display: inline-block; + float:right; +} + +div#uploads .upload progress { + width: 100%; + height: 20px; +} + ul.flashes { list-style: none; margin: 0; padding: 0; - color: #cc0000; - text-align: left; + width: 800px; + max-width: 90%; + margin: 0 auto; } ul.flashes li { margin: 0 0 5px 0; - padding: 10px; + padding: 5px; list-style: none; - border: 0; - border-radius: 3px; text-align: left; } li.error { - color: #ffffff; - background-color: #c90c0c; + color: #dd4040; } li.info { - color: #000000; - background-color: #a9e26c; + color: #3e933f; } .closed-wrapper { diff --git a/share/static/img/ajax.gif b/share/static/img/ajax.gif new file mode 100644 index 00000000..01d955aa Binary files /dev/null and b/share/static/img/ajax.gif differ diff --git a/share/static/js/receive-noscript.js b/share/static/js/receive-noscript.js new file mode 100644 index 00000000..0f4ac1bc --- /dev/null +++ b/share/static/js/receive-noscript.js @@ -0,0 +1,2 @@ +// Hide the noscript div, because our javascript is executing +document.getElementById('noscript').style.display = 'none'; diff --git a/share/static/js/receive.js b/share/static/js/receive.js index 45cbc9dc..0de80952 100644 --- a/share/static/js/receive.js +++ b/share/static/js/receive.js @@ -1,94 +1,140 @@ -// Hide the noscript div, because our javascript is executing -document.getElementById('noscript').style.display = 'none'; - -var form = document.getElementById('send'); -var fileSelect = document.getElementById('file-select'); -var uploadButton = document.getElementById('send-button'); -var flashes = document.getElementById('flashes'); - -// Add a flash message -function flash(category, message) { - var el = document.createElement('li'); - el.innerText = message; - el.className = category; - flashes.appendChild(el); -} - -form.onsubmit = function(event) { - event.preventDefault(); - - // Disable button, and update text - uploadButton.innerHTML = 'Uploading ...'; - uploadButton.disabled = true; - fileSelect.disabled = true; - - // Create form data - var files = fileSelect.files; - var formData = new FormData(); - for (var i = 0; i < files.length; i++) { - var file = files[i]; - formData.append('file[]', file, file.name); - } - - // Set up the request - var ajax = new XMLHttpRequest(); - - ajax.upload.addEventListener('progress', function(event){ - console.log('upload progress', 'uploaded '+event.loaded+' bytes / '+event.total+' bytes'); - var percent = parseInt((event.loaded / event.total) * 100, 10); - uploadButton.innerHTML = 'Uploading '+percent+'%'; - }, false); - - ajax.addEventListener('load', function(event){ - console.log('upload finished', ajax.response); - if(ajax.status == 200) { - // Parse response - try { - var response = JSON.parse(ajax.response); - - // The 'new_body' response replaces the whole HTML document and ends - if('new_body' in response) { - document.body.innerHTML = response['new_body']; - return; +$(function(){ + // Add a flash message + var flash = function(category, message) { + $('#flashes').append($('
  • ').addClass(category).text(message)); + }; + + // Add an upload + var new_upload_div = function(xhr, filenames) { + /* + The DOM for an upload looks something like this: + +
    +
    + +
    educational-video.mp4, secret-plans.pdf
    +
    Sending to first Tor node ...
    +
    + +
    + */ + var $progress = $('').attr({ value: '0', max: 100 }); + var $cancel_button = $('').addClass('cancel').attr({ type: 'button', value: 'Cancel' }); + var $upload_filename = $('
    ').addClass('upload-filename').text(filenames.join(', ')); + var $upload_status = $('
    ').addClass('upload-status').text('Sending data to initial Tor node ...'); + + var $upload_div = $('
    ').addClass('upload') + .append( + $('
    ').addClass('upload-meta') + .append($cancel_button) + .append($upload_filename) + .append($upload_status) + ) + .append($progress); + + $cancel_button.click(function(){ + // Abort the upload, and remove the upload div + xhr.abort(); + $upload_div.remove() + }); + + return $upload_div; + }; + + // Intercept submitting the form + $('#send').submit(function(event){ + event.preventDefault(); + + // Create form data, and list of filenames + var files = $('#file-select').get(0).files; + var filenames = []; + var formData = new FormData(); + for(var i = 0; i < files.length; i++) { + var file = files[i]; + filenames.push(file.name); + formData.append('file[]', file, file.name); + } + + // Reset the upload form + $('#send').get(0).reset(); + + // Start upload + xhr = $.ajax({ + method: 'POST', + url: window.location.pathname + '/upload-ajax', + data: formData, + // Tell jQuery not to process data or worry about content-type + cache: false, + contentType: false, + processData: false, + // Custom XMLHttpRequest + xhr: function() { + var xhr = $.ajaxSettings.xhr(); + if(xhr.upload) { + xhr.upload.addEventListener('progress', function(event) { + // Update progress bar for this specific upload + if(event.lengthComputable) { + console.log('upload progress', ''+event.loaded+' bytes / '+event.total+' bytes'); + $('progress', this.$upload_div).attr({ + value: event.loaded, + max: event.total, + }); + } + + // If it's finished sending all data to the first Tor node, remove cancel button + // and update the status + if(event.loaded == event.total) { + console.log('upload progress', 'complete'); + $('.cancel', this.$upload_div).remove(); + $('.upload-status', this.$upload_div).html(' Waiting for data to finish traversing Tor network ...'); + } + }, false); } + return xhr; + }, + success: function(data, textStatus, xhr){ + console.log('upload finished', data); + + // Remove the upload div + xhr.$upload_div.remove(); + + // Parse response + try { + var response = JSON.parse(data); - // Show error flashes - if('error_flashes' in response) { - for(var i=0; i
    -
    -

    -

    Send Files

    -

    Select the files you want to send, then click "Send Files"...

    -
    -

    -

    -
    -
      - {% with messages = get_flashed_messages(with_categories=true) %} - {% if messages %} - {% for category, message in messages %} -
    • {{ message }}
    • - {% endfor %} - {% endif %} - {% endwith %} -
    -
    -
    - - -
    -

    - Warning: Due to a bug in Tor Browser and Firefox, uploads - sometimes never finish. To upload reliably, either set your Tor Browser - security slider - to Standard or - turn off your Tor Browser's NoScript XSS setting.

    -
    +

    +

    Send Files

    +

    Select the files you want to send, then click "Send Files"...

    + +
    +

    +

    +
    + +
    + +
    +
      + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} +
    • {{ message }}
    • + {% endfor %} + {% endif %} + {% endwith %} +
    + + +
    +

    + Warning: Due to a bug in Tor Browser and Firefox, uploads + sometimes never finish. To upload reliably, either set your Tor Browser + security slider + to Standard or + turn off your Tor Browser's NoScript XSS setting.

    +
    + +
    + -- cgit v1.2.3-54-g00ecf