From 3a9730f8834c54e50b5f76bd6e9fecb809f170d4 Mon Sep 17 00:00:00 2001 From: Haocen Xu Date: Thu, 15 Aug 2019 19:28:42 -0400 Subject: [PATCH] Improve file upload UX Fix incorrect highlight logic Fix transition on fileupload highlight Handle drag leave Fix draghover Minor style improvements --- css/bootstrap/privatebin.css | 31 ++++++++-- css/privatebin.css | 34 +++++++++- js/privatebin.js | 117 ++++++++++++++++++++++++++++------- tpl/bootstrap.php | 7 +++ tpl/page.php | 7 +++ 5 files changed, 168 insertions(+), 28 deletions(-) diff --git a/css/bootstrap/privatebin.css b/css/bootstrap/privatebin.css index c424070..13897ed 100644 --- a/css/bootstrap/privatebin.css +++ b/css/bootstrap/privatebin.css @@ -80,10 +80,29 @@ body.loading { margin-bottom: 20px; } +#dropzone { + text-align: center; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 1000; + opacity: 0.6; + background-color: #99ccff; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3Cpath d='M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: center; + background-size: 25vh; + outline: 2px dashed #228bff; + outline-offset: -50px; +} + .dragAndDropFile{ - color:#777; - font-size:1em; - display:inline; + color: #777; + font-size: 1em; + display: inline; + white-space: normal; } #deletelink { @@ -124,6 +143,10 @@ body.loading { margin-bottom: 10px; } +#filewrap { + transition: background-color 0.75s ease-out; +} + .comment { border-left: 1px solid #ccc; padding: 5px 0 5px 10px; @@ -131,7 +154,7 @@ body.loading { transition: background-color 0.75s ease-out; } -.comment.highlight { +.highlight { background-color: #ffdd86; transition: background-color 0.2s ease-in; } diff --git a/css/privatebin.css b/css/privatebin.css index ae83de1..e3e2340 100644 --- a/css/privatebin.css +++ b/css/privatebin.css @@ -115,10 +115,29 @@ h3.title { margin-bottom: 20px; } +#dropzone { + text-align: center; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 1000; + opacity: 0.6; + background-color: #99ccff; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3Cpath d='M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: center; + background-size: 25vh; + outline: 2px dashed #228bff; + outline-offset: -50px; +} + .dragAndDropFile{ - color:#777; - font-size:1em; - display:inline; + color: #777; + font-size: 1em; + display: inline; + white-space: normal; } #status { @@ -405,6 +424,15 @@ h4.title { .commentdate { color: #bfcede; } +#filewrap { + transition: background-color 0.75s ease-out; +} + +.highlight { + background-color: #ffdd86; + transition: background-color 0.2s ease-in; +} + img.vizhash { width: 16px; height: 16px; diff --git a/js/privatebin.js b/js/privatebin.js index 72b7340..bf354e4 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -22,6 +22,27 @@ /** global: showdown */ /** global: kjua */ +jQuery.fn.draghover = function() { + return this.each(function() { + let collection = $(), + self = $(this); + + self.on('dragenter', function(e) { + if (collection.length === 0) { + self.trigger('draghoverstart'); + } + collection = collection.add(e.target); + }); + + self.on('dragleave drop', function(e) { + collection = collection.not(e.target); + if (collection.length === 0) { + self.trigger('draghoverend'); + } + }); + }); +}; + // main application start, called when DOM is fully loaded jQuery(document).ready(function() { 'use strict'; @@ -2492,7 +2513,8 @@ jQuery.PrivateBin = (function($, RawDeflate) { file, $fileInput, $dragAndDropFileName, - attachmentHasPreview = false; + attachmentHasPreview = false, + $dropzone; /** * sets the attachment but does not yet show it @@ -2714,7 +2736,9 @@ jQuery.PrivateBin = (function($, RawDeflate) { // revert loading status… me.hideAttachment(); me.hideAttachmentPreview(); - Alert.showError('Your browser does not support uploading encrypted files. Please use a newer browser.'); + Alert.showError( + I18n._('Your browser does not support uploading encrypted files. Please use a newer browser.') + ); return; } @@ -2728,16 +2752,20 @@ jQuery.PrivateBin = (function($, RawDeflate) { file = loadedFile; - fileReader.onload = function (event) { - const dataURL = event.target.result; - attachmentData = dataURL; + if (typeof loadedFile !== 'undefined') { + fileReader.onload = function (event) { + const dataURL = event.target.result; + attachmentData = dataURL; - if (Editor.isPreview()) { - me.setAttachment(dataURL, loadedFile.name || ''); - $attachmentPreview.removeClass('hidden'); - } - }; - fileReader.readAsDataURL(loadedFile); + if (Editor.isPreview()) { + me.handleAttachmentPreview($attachmentPreview, dataURL); + $attachmentPreview.removeClass('hidden'); + } + + TopNav.highlightFileupload(); + }; + fileReader.readAsDataURL(loadedFile); + } } /** @@ -2780,8 +2808,21 @@ jQuery.PrivateBin = (function($, RawDeflate) { .attr('src', blobUrl)) ); } else if (mimeType.match(/\/pdf/i)) { + // PDFs are only displayed if the filesize is smaller than about 1MB (after base64 encoding). + // Bigger filesizes currently cause crashes in various browsers. + // See also: https://code.google.com/p/chromium/issues/detail?id=69227 + + // Firefox crashes with files that are about 1.5MB + // The performance with 1MB files is bearable + if (data.length > 1398488) { + Alert.showError( + I18n._('File too large to display a preview. Please download the attachment.') + ); //TODO: is this error really neccessary? + return; + } + // Fallback for browsers, that don't support the vh unit - var clientHeight = $(window).height(); + const clientHeight = $(window).height(); $targetElement.html( $(document.createElement('embed')) @@ -2808,12 +2849,12 @@ jQuery.PrivateBin = (function($, RawDeflate) { return; } - const ignoreDragDrop = function(event) { + const handleDragEnterOrOver = function(event) { event.stopPropagation(); event.preventDefault(); }; - const drop = function(event) { + const handleDrop = function(event) { const evt = event.originalEvent; evt.stopPropagation(); evt.preventDefault(); @@ -2830,9 +2871,19 @@ jQuery.PrivateBin = (function($, RawDeflate) { } }; - $(document).on('drop', drop); - $(document).on('dragenter', ignoreDragDrop); - $(document).on('dragover', ignoreDragDrop); + $(document).draghover().on({ + 'draghoverstart': function() { + // show dropzone to indicate drop support + $dropzone.removeClass('hidden'); + }, + 'draghoverend': function() { + $dropzone.addClass('hidden'); + } + }); + + $(document).on('drop', handleDrop); + $(document).on('dragenter dragover', handleDragEnterOrOver); + $fileInput.on('change', function () { readFileData(); }); @@ -2920,6 +2971,7 @@ jQuery.PrivateBin = (function($, RawDeflate) { $attachmentLink = $('#attachment a'); $attachmentPreview = $('#attachmentPreview'); $dragAndDropFileName = $('#dragAndDropFileName'); + $dropzone = $('#dropzone'); $fileInput = $('#file'); addDragDropHandler(); @@ -3741,6 +3793,25 @@ jQuery.PrivateBin = (function($, RawDeflate) { retryButtonCallback = callback; } + /** + * Highlight file upload + * + * @name TopNav.highlightFileupload + * @function + */ + me.highlightFileupload = function() + { + // visually indicate file uploaded + const $attachDropdownToggle = $attach.children('.dropdown-toggle'); + if ($attachDropdownToggle.attr('aria-expanded') === 'false') { + $attachDropdownToggle.click(); + } + $fileWrap.addClass('highlight'); + setTimeout(function () { + $fileWrap.removeClass('highlight'); + }, 300); + } + /** * init navigation manager * @@ -4479,7 +4550,9 @@ jQuery.PrivateBin = (function($, RawDeflate) { // log detailed error, but display generic translation console.error(message); - Alert.showError('Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.'); + Alert.showError( + I18n._('Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.') + ); // reset password, so it can be re-entered Prompt.reset(); @@ -4548,8 +4621,8 @@ jQuery.PrivateBin = (function($, RawDeflate) { * @param {object} document * @class */ - var InitialCheck = (function () { - var me = {}; + const InitialCheck = (function () { + const me = {}; /** * blacklist of UserAgents (parts) known to belong to a bot @@ -4762,7 +4835,9 @@ jQuery.PrivateBin = (function($, RawDeflate) { // missing decryption key (or paste ID) in URL? if (window.location.hash.length === 0) { - Alert.showError('Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)'); + Alert.showError( + I18n._('Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)') + ); return; } } diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 8416d71..deca579 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -552,6 +552,13 @@ if ($DISCUSSION): + + + diff --git a/tpl/page.php b/tpl/page.php index 8c82827..0c55399 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -254,6 +254,13 @@ if ($DISCUSSION): + + +