From bed57af6c5858b08b16f705620ad959c21d0059c Mon Sep 17 00:00:00 2001 From: Danny Coates Date: Thu, 10 Aug 2017 19:01:39 -0700 Subject: [PATCH] extracted filelist into its own file --- frontend/src/fileList.js | 221 ++++++++++++++++++++++++++++++++++++++ frontend/src/storage.js | 6 +- frontend/src/upload.js | 226 +-------------------------------------- frontend/src/utils.js | 6 ++ 4 files changed, 235 insertions(+), 224 deletions(-) create mode 100644 frontend/src/fileList.js diff --git a/frontend/src/fileList.js b/frontend/src/fileList.js new file mode 100644 index 00000000..d03d4ddf --- /dev/null +++ b/frontend/src/fileList.js @@ -0,0 +1,221 @@ +import FileSender from './fileSender'; +import Storage from './storage'; +import * as metrics from './metrics'; +import { allowedCopy, copyToClipboard, ONE_DAY_IN_MS } from './utils'; +import $ from 'jquery'; + +const storage = new Storage(); +let fileList = null; +let $link = null; + +document.addEventListener('DOMContentLoaded', function() { + $link = $('#link'); + fileList = document.getElementById('file-list'); + toggleHeader(); + // eslint-disable-next-line prefer-const + for (let file of storage.files) { + const id = file.fileId; + checkExistence(id).then(exists => { + if (exists) { + addFile(storage.getFileById(id)); + } else { + storage.remove(id); + } + }); + } +}); + +function toggleHeader() { + fileList.hidden = storage.files.length === 0; +} + +function addFile(file) { + if (!file) { + return; + } + const row = document.createElement('tr'); + const name = document.createElement('td'); + const link = document.createElement('td'); + const $copyIcon = $('', { + src: '/resources/copy-16.svg', + class: 'icon-copy', + 'data-l10n-id': 'copyUrlHover', + disabled: !allowedCopy() + }); + const expiry = document.createElement('td'); + const del = document.createElement('td'); + const $delIcon = $('', { + src: '/resources/close-16.svg', + class: 'icon-delete', + 'data-l10n-id': 'deleteButtonHover' + }); + const popupDiv = document.createElement('div'); + const $popupText = $('
', { class: 'popuptext' }); + const cellText = document.createTextNode(file.name); + + const url = file.url.trim() + `#${file.secretKey}`.trim(); + + $link.attr('value', url); + $('#copy-text') + .attr('data-l10n-args', `{"filename": "${file.name}"}`) + .attr('data-l10n-id', 'copyUrlFormLabelWithName'); + + $popupText.attr('tabindex', '-1'); + + name.appendChild(cellText); + + // create delete button + + const delSpan = document.createElement('span'); + $(delSpan) + .addClass('icon-cancel-1') + .attr('data-l10n-id', 'deleteButtonHover'); + del.appendChild(delSpan); + + const linkSpan = document.createElement('span'); + $(linkSpan).addClass('icon-docs').attr('data-l10n-id', 'copyUrlHover'); + + link.appendChild(linkSpan); + link.style.color = '#0A8DFF'; + + //copy link to clipboard when icon clicked + $copyIcon.on('click', () => { + // record copied event from upload list + metrics.copiedLink({ location: 'upload-list' }); + copyToClipboard(url); + document.l10n.formatValue('copiedUrl').then(translated => { + link.innerHTML = translated; + }); + setTimeout(() => { + const linkImg = document.createElement('img'); + $(linkImg) + .addClass('icon-copy') + .attr('data-l10n-id', 'copyUrlHover') + .attr('src', '/resources/copy-16.svg'); + + $(link).html(linkImg); + }, 500); + }); + + file.creationDate = new Date(file.creationDate); + + const future = new Date(); + future.setTime(file.creationDate.getTime() + file.expiry); + + let countdown = 0; + countdown = future.getTime() - Date.now(); + let minutes = Math.floor(countdown / 1000 / 60); + let hours = Math.floor(minutes / 60); + let seconds = Math.floor(countdown / 1000 % 60); + + const poll = () => { + countdown = future.getTime() - Date.now(); + minutes = Math.floor(countdown / 1000 / 60); + hours = Math.floor(minutes / 60); + seconds = Math.floor(countdown / 1000 % 60); + let t; + + if (hours >= 1) { + expiry.innerHTML = hours + 'h ' + minutes % 60 + 'm'; + t = setTimeout(() => { + poll(); + }, 60000); + } else if (hours === 0) { + expiry.innerHTML = minutes + 'm ' + seconds + 's'; + t = window.setTimeout(() => { + poll(); + }, 1000); + } + //remove from list when expired + if (countdown <= 0) { + storage.remove(file.fileId); + $(expiry).parents('tr').remove(); + window.clearTimeout(t); + toggleHeader(); + } + }; + + poll(); + + // create popup + popupDiv.classList.add('popup'); + const $popupMessage = $('
', { class: 'popup-message' }); + $popupMessage.attr('data-l10n-id', 'deletePopupText'); + const $popupAction = $('
', { class: 'popup-action' }); + const $popupNvmSpan = $('', { class: 'popup-no' }); + $popupNvmSpan.attr('data-l10n-id', 'deletePopupCancel'); + const $popupDelSpan = $('', { class: 'popup-yes' }); + $popupDelSpan.attr('data-l10n-id', 'deletePopupYes'); + + $popupText.html([$popupMessage, $popupAction]); + $popupAction.html([$popupNvmSpan, $popupDelSpan]); + + // add data cells to table row + row.appendChild(name); + $(link).append($copyIcon); + row.appendChild(link); + row.appendChild(expiry); + $(popupDiv).append($popupText); + $(del).append($delIcon); + del.appendChild(popupDiv); + row.appendChild(del); + $('tbody').append(row); //add row to table + + // delete file + $popupText.find('.popup-yes').on('click', e => { + FileSender.delete(file.fileId, file.deleteToken).then(() => { + $(e.target).parents('tr').remove(); + const ttl = ONE_DAY_IN_MS - (Date.now() - file.creationDate.getTime()); + metrics + .deletedUpload({ + size: file.size, + time: file.totalTime, + speed: file.uploadSpeed, + type: file.typeOfUpload, + location: 'upload-list', + ttl + }) + .then(() => { + storage.remove(file.fileId); + }); + toggleHeader(); + }); + }); + + // show popup + $delIcon.on('click', () => { + $popupText.addClass('show').focus(); + }); + + // hide popup + $popupText.find('.popup-no').on('click', e => { + e.stopPropagation(); + $popupText.removeClass('show'); + }); + + $popupText.on('click', e => { + e.stopPropagation(); + }); + + //close when popup loses focus + $popupText.on('blur', () => { + $popupText.removeClass('show'); + }); + + toggleHeader(); +} + +async function checkExistence(id) { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.onreadystatechange = () => { + if (xhr.readyState === XMLHttpRequest.DONE) { + resolve(xhr.status === 200); + } + }; + xhr.open('get', '/exists/' + id); + xhr.send(); + }); +} + +export { addFile }; diff --git a/frontend/src/storage.js b/frontend/src/storage.js index c8580a03..28f14b89 100644 --- a/frontend/src/storage.js +++ b/frontend/src/storage.js @@ -86,7 +86,11 @@ export default class Storage { } getFileById(id) { - return this.engine.getItem(id); + try { + return JSON.parse(this.engine.getItem(id)); + } catch (e) { + return null; + } } remove(property) { diff --git a/frontend/src/upload.js b/frontend/src/upload.js index 46d9c78e..f972a2aa 100644 --- a/frontend/src/upload.js +++ b/frontend/src/upload.js @@ -2,6 +2,7 @@ import { Raven } from './common'; import FileSender from './fileSender'; import { + allowedCopy, bytes, copyToClipboard, notify, @@ -11,15 +12,11 @@ import { import Storage from './storage'; import * as metrics from './metrics'; import * as progress from './progress'; +import * as fileList from './fileList'; import $ from 'jquery'; const storage = new Storage(); -const allowedCopy = () => { - const support = !!document.queryCommandSupported; - return support ? document.queryCommandSupported('copy') : false; -}; - $(() => { gcmCompliant() .then(function() { @@ -29,7 +26,6 @@ $(() => { const $uploadWindow = $('.upload-window'); const $uploadError = $('#upload-error'); const $uploadProgress = $('#upload-progress'); - const $fileList = $('#file-list'); $pageOne.removeAttr('hidden'); $('#file-upload').on('change', onUpload); @@ -44,27 +40,6 @@ $(() => { $link.attr('disabled', false); - const toggleHeader = () => { - //hide table header if empty list - if (document.querySelector('tbody').childNodes.length === 1) { - $fileList.attr('hidden', true); - } else { - $fileList.removeAttr('hidden'); - } - }; - - const files = storage.files; - if (files.length === 0) { - toggleHeader(); - } else { - // eslint-disable-next-line prefer-const - for (let index in files) { - const id = files[index].fileId; - //check if file still exists before adding to list - checkExistence(id, files[index], true); - } - } - // copy link to clipboard $copyBtn.on('click', () => { if (allowedCopy() && copyToClipboard($link.attr('value'))) { @@ -243,7 +218,7 @@ $(() => { $uploadError.attr('hidden', true); $('#share-link').removeAttr('hidden'); - populateFileList(fileData); + fileList.addFile(fileData); document.l10n.formatValue('notifyUploadDone').then(str => { notify(str); }); @@ -272,201 +247,6 @@ $(() => { function allowDrop(ev) { ev.preventDefault(); } - - function checkExistence(id, file, populate) { - const xhr = new XMLHttpRequest(); - xhr.onreadystatechange = () => { - if (xhr.readyState === XMLHttpRequest.DONE) { - if (xhr.status === 200) { - if (populate) { - populateFileList(file); - } - } else if (xhr.status === 404) { - storage.remove(id); - if (storage.numFiles === 0) { - toggleHeader(); - } - } - } - }; - xhr.open('get', '/exists/' + id, true); - xhr.send(); - } - - //update file table with current files in storage - const populateFileList = file => { - const row = document.createElement('tr'); - const name = document.createElement('td'); - const link = document.createElement('td'); - const $copyIcon = $('', { - src: '/resources/copy-16.svg', - class: 'icon-copy', - 'data-l10n-id': 'copyUrlHover', - disabled: !allowedCopy() - }); - const expiry = document.createElement('td'); - const del = document.createElement('td'); - const $delIcon = $('', { - src: '/resources/close-16.svg', - class: 'icon-delete', - 'data-l10n-id': 'deleteButtonHover' - }); - const popupDiv = document.createElement('div'); - const $popupText = $('
', { class: 'popuptext' }); - const cellText = document.createTextNode(file.name); - - const url = file.url.trim() + `#${file.secretKey}`.trim(); - - $link.attr('value', url); - $('#copy-text') - .attr('data-l10n-args', JSON.stringify({ filename: file.name })) - .attr('data-l10n-id', 'copyUrlFormLabelWithName'); - - $popupText.attr('tabindex', '-1'); - - name.appendChild(cellText); - - // create delete button - - const delSpan = document.createElement('span'); - $(delSpan) - .addClass('icon-cancel-1') - .attr('data-l10n-id', 'deleteButtonHover'); - del.appendChild(delSpan); - - const linkSpan = document.createElement('span'); - $(linkSpan).addClass('icon-docs').attr('data-l10n-id', 'copyUrlHover'); - - link.appendChild(linkSpan); - link.style.color = '#0A8DFF'; - - //copy link to clipboard when icon clicked - $copyIcon.on('click', () => { - // record copied event from upload list - metrics.copiedLink({ location: 'upload-list' }); - copyToClipboard(url); - document.l10n.formatValue('copiedUrl').then(translated => { - link.innerHTML = translated; - }); - setTimeout(() => { - const linkImg = document.createElement('img'); - $(linkImg) - .addClass('icon-copy') - .attr('data-l10n-id', 'copyUrlHover') - .attr('src', '/resources/copy-16.svg'); - - $(link).html(linkImg); - }, 500); - }); - - file.creationDate = new Date(file.creationDate); - - const future = new Date(); - future.setTime(file.creationDate.getTime() + file.expiry); - - let countdown = 0; - countdown = future.getTime() - Date.now(); - let minutes = Math.floor(countdown / 1000 / 60); - let hours = Math.floor(minutes / 60); - let seconds = Math.floor(countdown / 1000 % 60); - - const poll = () => { - countdown = future.getTime() - Date.now(); - minutes = Math.floor(countdown / 1000 / 60); - hours = Math.floor(minutes / 60); - seconds = Math.floor(countdown / 1000 % 60); - let t; - - if (hours >= 1) { - expiry.innerHTML = hours + 'h ' + minutes % 60 + 'm'; - t = setTimeout(() => { - poll(); - }, 60000); - } else if (hours === 0) { - expiry.innerHTML = minutes + 'm ' + seconds + 's'; - t = window.setTimeout(() => { - poll(); - }, 1000); - } - //remove from list when expired - if (countdown <= 0) { - storage.remove(file.fileId); - $(expiry).parents('tr').remove(); - window.clearTimeout(t); - toggleHeader(); - } - }; - - poll(); - - // create popup - popupDiv.classList.add('popup'); - const $popupMessage = $('
', { class: 'popup-message' }); - $popupMessage.attr('data-l10n-id', 'deletePopupText'); - const $popupAction = $('
', { class: 'popup-action' }); - const $popupNvmSpan = $('', { class: 'popup-no' }); - $popupNvmSpan.attr('data-l10n-id', 'deletePopupCancel'); - const $popupDelSpan = $('', { class: 'popup-yes' }); - $popupDelSpan.attr('data-l10n-id', 'deletePopupYes'); - - $popupText.html([$popupMessage, $popupAction]); - $popupAction.html([$popupNvmSpan, $popupDelSpan]); - - // add data cells to table row - row.appendChild(name); - $(link).append($copyIcon); - row.appendChild(link); - row.appendChild(expiry); - $(popupDiv).append($popupText); - $(del).append($delIcon); - del.appendChild(popupDiv); - row.appendChild(del); - $('tbody').append(row); //add row to table - - // delete file - $popupText.find('.popup-yes').on('click', e => { - FileSender.delete(file.fileId, file.deleteToken).then(() => { - $(e.target).parents('tr').remove(); - const ttl = - ONE_DAY_IN_MS - (Date.now() - file.creationDate.getTime()); - metrics - .deletedUpload({ - size: file.size, - time: file.totalTime, - speed: file.uploadSpeed, - type: file.typeOfUpload, - location: 'upload-list', - ttl - }) - .then(() => { - storage.remove(file.fileId); - }); - toggleHeader(); - }); - }); - - // show popup - $delIcon.on('click', () => { - $popupText.addClass('show').focus(); - }); - - // hide popup - $popupText.find('.popup-no').on('click', e => { - e.stopPropagation(); - $popupText.removeClass('show'); - }); - - $popupText.on('click', e => { - e.stopPropagation(); - }); - - //close when popup loses focus - $popupText.on('blur', () => { - $popupText.removeClass('show'); - }); - - toggleHeader(); - }; }) .catch(err => { metrics.unsupported({ err }).then(() => { diff --git a/frontend/src/utils.js b/frontend/src/utils.js index 41da2ff0..8a7ae84e 100644 --- a/frontend/src/utils.js +++ b/frontend/src/utils.js @@ -129,9 +129,15 @@ function percent(ratio) { : `${Math.floor(ratio * 100)}%`; } +function allowedCopy() { + const support = !!document.queryCommandSupported; + return support ? document.queryCommandSupported('copy') : false; +} + const ONE_DAY_IN_MS = 86400000; export { + allowedCopy, bytes, percent, copyToClipboard,