diff --git a/app/api.js b/app/api.js index 3d394c46..79a7785e 100644 --- a/app/api.js +++ b/app/api.js @@ -65,6 +65,14 @@ export async function fileInfo(id, owner_token) { throw new Error(response.status); } +export async function hasPassword(id) { + const response = await fetch(`/api/exists/${id}`); + if (response.ok) { + return response.json(); + } + throw new Error(response.status); +} + export async function metadata(id, keychain) { const result = await fetchWithAuthAndRetry( `/api/metadata/${id}`, diff --git a/app/archive.js b/app/archive.js index 5c90e142..6cea76c7 100644 --- a/app/archive.js +++ b/app/archive.js @@ -17,6 +17,10 @@ export default class Archive { return this.files.reduce((total, file) => total + file.size, 0); } + get numFiles() { + return this.files.length; + } + get manifest() { return { files: this.files.map(file => ({ diff --git a/app/base.css b/app/base.css index 2360a33a..8a41b893 100644 --- a/app/base.css +++ b/app/base.css @@ -1,14 +1,16 @@ :root { --pageBGColor: #fff; - --primaryControlBGColor: #0297f8; + --lightControlBGColor: #e6e6e6; + --primaryControlBGColor: #0a84ff; --primaryControlFGColor: #fff; - --primaryControlHoverColor: #0287e8; + --primaryControlHoverColor: #0473e2; --inputTextColor: #737373; --errorColor: #d70022; --linkColor: #0094fb; --textColor: #0c0c0d; + --lightBorderColor: rgba(12, 12, 12, 0.2); --lightTextColor: #737373; - --successControlBGColor: #05a700; + --successControlBGColor: #12bc00; --successControlFGColor: #fff; } @@ -41,17 +43,21 @@ a { .main { display: flex; - flex-direction: row; flex: auto; - padding: 0 20px; + padding: 0 25px; box-sizing: border-box; + min-height: 500px; + max-height: 630px; + height: 100px; } .stripedBox { - width: 480px; + flex: none; + position: relative; + width: 400px; background-color: white; border-radius: 6px; - box-shadow: 0 0 0 3px rgba(155, 155, 155, 0.4); + box-shadow: 0 0 0 3px rgba(12, 12, 13, 0.2); background-image: repeating-linear-gradient( 45deg, white, @@ -68,8 +74,11 @@ a { .mainContent { height: 100%; background-color: white; + box-sizing: border-box; margin: 0 10px; - padding: 1px 10px 0 10px; /* top wtf? */ + padding: 10px 10px 28px; + display: flex; + flex-direction: column; } .spacer { @@ -77,7 +86,8 @@ a { } .uploads { - flex: auto; + flex: 0 0 262px; + position: relative; } .noscript { @@ -87,13 +97,19 @@ a { } .btn { - font-size: 15px; + display: block; + width: 100%; + height: 70px; + line-height: 70px; + font-size: 21px; font-weight: 500; - color: var(--primaryControlFGColor); - cursor: pointer; + text-transform: uppercase; text-align: center; + letter-spacing: 0.56px; + color: var(--primaryControlFGColor); background: var(--primaryControlBGColor); - border: 1px solid var(--primaryControlBGColor); + cursor: pointer; + border: 0; border-radius: 5px; } @@ -101,75 +117,52 @@ a { background-color: var(--primaryControlHoverColor); } -.btn--cancel { - color: var(--errorColor); - background: var(--pageBGColor); - font-size: 15px; - border: 0; - cursor: pointer; - text-decoration: underline; +.btn--stripes { + background: repeating-linear-gradient( + -65deg, + #7c7c7c 0, + #7c7c7c 17px, + #737373 17px, + #737373 30px + ); + background-size: 300% 300%; + animation: barberpole 12s linear infinite; } -.btn--cancel:disabled { - text-decoration: none; - cursor: auto; -} +@keyframes barberpole { + 0% { + background-position: 100% 0%; + } -.btn--cancel:hover { - background-color: var(--pageBGColor); + 100% { + background-position: 0% 0%; + } } .input { - flex: 2 0 auto; - border: 1px solid var(--primaryControlBGColor); - border-radius: 6px 0 0 6px; + border: 1px solid var(--lightBorderColor); font-size: 20px; color: var(--inputTextColor); font-family: 'SF Pro Text', sans-serif; - letter-spacing: 0; - line-height: 23px; font-weight: 300; - height: 46px; padding-left: 10px; padding-right: 10px; } -.input--error { - border-color: var(--errorColor); -} - .input--noBtn { border-radius: 6px; } -.inputBtn { - flex: auto; - background: var(--primaryControlBGColor); - border-radius: 0 6px 6px 0; - border: 1px solid var(--primaryControlBGColor); - color: var(--primaryControlFGColor); - cursor: pointer; - - /* Force flat button look */ - /* stylelint-disable-next-line plugin/no-unsupported-browser-features */ - appearance: none; - font-size: 15px; - padding-bottom: 3px; - padding-left: 10px; - padding-right: 10px; - white-space: nowrap; +.input--error { + border-color: var(--errorColor); } -.inputBtn:disabled { - cursor: auto; +.inputBtn.inputError { + background-color: var(--errorColor); } -.inputBtn:hover { - background-color: var(--primaryControlHoverColor); -} - -.inputBtn--hidden { - display: none; +.inputBtn.inputError:hover { + background-color: var(--errorColor); } .cursor--pointer { @@ -188,15 +181,15 @@ a { } .link--action { - text-decoration: underline; + font-weight: 500; + font-size: 14px; text-align: center; } .page { - margin: 0 auto 30px; + height: 100%; + margin: 0; display: flex; - justify-content: center; - align-items: center; flex-direction: column; text-align: center; } @@ -223,6 +216,13 @@ a { animation: fadeout 200ms linear; } +.goBackButton { + position: absolute; + top: 0; + left: 0; + margin: 18px; +} + @keyframes fadeout { 0% { opacity: 1; @@ -250,6 +250,7 @@ a { .error { color: var(--errorColor); + font-weight: 600; } .title { @@ -264,34 +265,50 @@ a { } .description { - font-size: 15px; - line-height: 23px; - max-width: 630px; - text-align: center; - margin: 0 auto 60px; - color: var(--textColor); - width: 92%; + font-size: 13px; + text-align: left; + margin: 14px auto; + color: var(--lightTextColor); + width: 95%; } -@media (max-device-width: 768px), (max-width: 768px) { +.visible { + visibility: visible !important; +} + +.noDisplay { + display: none; +} + +.flexible { + flex: 1; +} + +@media (max-device-width: 750px), (max-width: 750px) { .description { margin: 0 auto 25px; } -} -@media (max-device-width: 520px), (max-width: 520px) { - .input { - font-size: 22px; - padding: 10px 10px; - border-radius: 6px 6px 0 0; + .main { + flex-direction: column; + min-height: 700px; } - .inputBtn { - border-radius: 0 0 6px 6px; - flex: 0 1 65px; + .spacer { + flex: none; + height: 0; } - .input--noBtn { - border-radius: 6px; + .stripedBox { + max-height: 550px; + flex: 1; + } + + .uploads { + flex: none; + } + + .footer { + margin: 15px; } } diff --git a/app/dragManager.js b/app/dragManager.js index 7713e6ee..c4b1783e 100644 --- a/app/dragManager.js +++ b/app/dragManager.js @@ -1,12 +1,12 @@ -/* global MAXFILESIZE */ -import Archive from './archive'; -import { bytes } from './utils'; +import { checkSize } from './utils'; export default function(state, emitter) { emitter.on('DOMContentLoaded', () => { document.body.addEventListener('dragover', event => { if (state.route === '/') { event.preventDefault(); + const files = document.querySelector('.uploadedFilesWrapper'); + files.classList.add('uploadArea--noEvents'); } }); document.body.addEventListener('drop', event => { @@ -15,21 +15,12 @@ export default function(state, emitter) { document .querySelector('.uploadArea') .classList.remove('uploadArea--dragging'); - const target = event.dataTransfer; - if (target.files.length === 0) { - return; - } - const file = new Archive(target.files); - if (file.size === 0) { - return; - } - if (file.size > MAXFILESIZE) { - // eslint-disable-next-line no-alert - alert(state.translate('fileTooBig', { size: bytes(MAXFILESIZE) })); - return; - } - emitter.emit('upload', { file, type: 'drop' }); + const target = event.dataTransfer; + + checkSize(target.files, state.files); + + emitter.emit('addFiles', { files: target.files }); } }); }); diff --git a/app/fileManager.js b/app/fileManager.js index 087cddc2..d8bfbfdb 100644 --- a/app/fileManager.js +++ b/app/fileManager.js @@ -1,17 +1,14 @@ import FileSender from './fileSender'; import FileReceiver from './fileReceiver'; -import { - copyToClipboard, - delay, - fadeOut, - openLinksInNewTab, - percent -} from './utils'; +import { copyToClipboard, delay, openLinksInNewTab, percent } from './utils'; import * as metrics from './metrics'; +import { hasPassword } from './api'; +import Archive from './archive'; export default function(state, emitter) { let lastRender = 0; let updateTitle = false; + state.files = []; function render() { emitter.emit('render'); @@ -64,6 +61,16 @@ export default function(state, emitter) { metrics.changedDownloadLimit(file); }); + emitter.on('removeUpload', async ({ file }) => { + for (let i = 0; i < state.files.length; i++) { + if (state.files[i] === file) { + state.files.splice(i, 1); + render(); + return; + } + } + }); + emitter.on('delete', async ({ file, location }) => { try { metrics.deletedUpload({ @@ -85,11 +92,22 @@ export default function(state, emitter) { state.transfer.cancel(); }); - emitter.on('upload', async ({ file, type }) => { + emitter.on('addFiles', async ({ files }) => { + for (let i = 0; i < files.length; i++) { + state.files.push(files[i]); + } + render(); + }); + + //TODO: hook up to multi-file upload functionality + emitter.on('upload', async ({ files, type, dlCount, password }) => { + const file = new Archive(files); + const size = file.size; const sender = new FileSender(file); sender.on('progress', updateProgress); sender.on('encrypting', render); + sender.on('complete', render); state.transfer = sender; state.uploading = true; render(); @@ -98,19 +116,25 @@ export default function(state, emitter) { await delay(200); try { metrics.startedUpload({ size, type }); + const ownedFile = await sender.upload(); ownedFile.type = type; state.storage.totalUploads += 1; metrics.completedUpload(ownedFile); state.storage.addFile(ownedFile); + + if (password) { + emitter.emit('password', { password, file: ownedFile }); + } + emitter.emit('changeLimit', { file: ownedFile, value: dlCount }); + const cancelBtn = document.getElementById('cancel-upload'); if (cancelBtn) { cancelBtn.hidden = 'hidden'; } if (document.querySelector('.page')) { await delay(1000); - await fadeOut('.page'); } emitter.emit('pushState', `/share/${ownedFile.id}`); } catch (err) { @@ -127,6 +151,8 @@ export default function(state, emitter) { } } finally { openLinksInNewTab(links, false); + state.files = []; + state.password = ''; state.uploading = false; state.transfer = null; } @@ -150,6 +176,17 @@ export default function(state, emitter) { render(); }); + emitter.on('getPasswordExist', async ({ id }) => { + try { + state.fileInfo = await hasPassword(id); + render(); + } catch (e) { + if (e.message === '404') { + return emitter.emit('pushState', '/404'); + } + } + }); + emitter.on('getMetadata', async () => { const file = state.fileInfo; @@ -172,6 +209,7 @@ export default function(state, emitter) { emitter.on('download', async file => { state.transfer.on('progress', updateProgress); state.transfer.on('decrypting', render); + state.transfer.on('complete', render); const links = openLinksInNewTab(); const size = file.size; try { @@ -186,12 +224,11 @@ export default function(state, emitter) { const speed = size / (time / 1000); if (document.querySelector('.page')) { await delay(1000); - await fadeOut('.page'); } state.storage.totalDownloads += 1; state.transfer.reset(); metrics.completedDownload({ size, time, speed }); - emitter.emit('pushState', '/completed'); + //emitter.emit('pushState', '/completed'); } catch (err) { if (err.message === '0') { // download cancelled diff --git a/app/fileReceiver.js b/app/fileReceiver.js index 8f50dd2b..4546ad0a 100644 --- a/app/fileReceiver.js +++ b/app/fileReceiver.js @@ -172,6 +172,7 @@ export default class FileReceiver extends Nanobus { this.downloadRequest = null; this.msg = 'downloadFinish'; + this.emit('complete'); this.state = 'complete'; } catch (e) { this.downloadRequest = null; diff --git a/app/fileSender.js b/app/fileSender.js index aa49d19f..d8ef3f8b 100644 --- a/app/fileSender.js +++ b/app/fileSender.js @@ -93,6 +93,7 @@ export default class FileSender extends Nanobus { url: `${result.url}#${secretKey}`, name: this.file.name, size: this.file.size, + manifest: this.file.manifest, time: time, speed: this.file.size / (time / 1000), createdAt: Date.now(), @@ -101,6 +102,7 @@ export default class FileSender extends Nanobus { nonce: this.keychain.nonce, ownerToken: result.ownerToken }); + return ownedFile; } catch (e) { this.msg = 'errorPageHeader'; diff --git a/app/main.css b/app/main.css index db53502d..afc2040b 100644 --- a/app/main.css +++ b/app/main.css @@ -2,7 +2,6 @@ @import './templates/activeBackground/activeBackground.css'; @import './templates/header/header.css'; @import './templates/downloadButton/downloadButton.css'; -@import './templates/progress/progress.css'; @import './templates/passwordInput/passwordInput.css'; @import './templates/downloadPassword/downloadPassword.css'; @import './templates/setPasswordSection/setPasswordSection.css'; @@ -11,7 +10,14 @@ @import './templates/selectbox/selectbox.css'; @import './templates/fileList/fileList.css'; @import './templates/file/file.css'; +@import './templates/uploadedFile/uploadedFile.css'; +@import './templates/uploadedFileList/uploadedFileList.css'; @import './templates/popup/popup.css'; +@import './templates/title/title.css'; +@import './templates/fileIcon/fileIcon.css'; +@import './templates/signupPromo/signupPromo.css'; +@import './templates/userAccount/userAccount.css'; @import './pages/welcome/welcome.css'; @import './pages/share/share.css'; +@import './pages/signin/signin.css'; @import './pages/unsupported/unsupported.css'; diff --git a/app/ownedFile.js b/app/ownedFile.js index 387bf95c..3cee3594 100644 --- a/app/ownedFile.js +++ b/app/ownedFile.js @@ -9,6 +9,7 @@ export default class OwnedFile { this.name = obj.name; this.size = obj.size; this.type = obj.type; + this.manifest = obj.manifest; this.time = obj.time; this.speed = obj.speed; this.createdAt = obj.createdAt; @@ -70,6 +71,7 @@ export default class OwnedFile { name: this.name, size: this.size, type: this.type, + manifest: this.manifest, time: this.time, speed: this.speed, createdAt: this.createdAt, diff --git a/app/pages/completed/index.js b/app/pages/completed/index.js deleted file mode 100644 index 8d60dd47..00000000 --- a/app/pages/completed/index.js +++ /dev/null @@ -1,26 +0,0 @@ -const html = require('choo/html'); -const progress = require('../../templates/progress'); -const { fadeOut } = require('../../utils'); - -module.exports = function(state, emit) { - return html` -
-
- ${state.translate('downloadFinish')} -
-
- ${progress(1)} -
-
-
- ${state.translate('sendYourFilesLink')} -
`; - - async function sendNew(e) { - e.preventDefault(); - await fadeOut('.page'); - emit('pushState', '/'); - } -}; diff --git a/app/pages/download/index.js b/app/pages/download/index.js deleted file mode 100644 index 73857dfb..00000000 --- a/app/pages/download/index.js +++ /dev/null @@ -1,42 +0,0 @@ -const html = require('choo/html'); -const progress = require('../../templates/progress'); -const { bytes } = require('../../utils'); - -module.exports = function(state, emit) { - const transfer = state.transfer; - const cancelBtn = html` - `; - - return html` -
-
- ${state.translate('downloadingPageProgress', { - filename: state.fileInfo.name, - size: bytes(state.fileInfo.size) - })} -
-
- ${state.translate('downloadingPageMessage')} -
- ${progress(transfer.progressRatio, transfer.progressIndefinite)} -
-
- ${state.translate(transfer.msg, transfer.sizes)} -
- ${transfer.state === 'downloading' ? cancelBtn : null} -
-
- `; - - function cancel() { - const btn = document.getElementById('cancel'); - btn.remove(); - emit('cancel'); - } -}; diff --git a/app/pages/error/index.js b/app/pages/error/index.js index 4072101f..5b6167f5 100644 --- a/app/pages/error/index.js +++ b/app/pages/error/index.js @@ -1,10 +1,22 @@ const html = require('choo/html'); const assets = require('../../../common/assets'); +const title = require('../../templates/title'); module.exports = function(state) { return html`
-
${state.translate('errorPageHeader')}
- + + ${title(state)} + +
${state.translate('errorPageHeader')}
+ + +
+ ${state.translate('uploadPageExplainer')} +
+ + ${state.translate('sendYourFilesLink')} + +
`; }; diff --git a/app/pages/notFound/index.js b/app/pages/notFound/index.js index 97f0e04a..46ab6753 100644 --- a/app/pages/notFound/index.js +++ b/app/pages/notFound/index.js @@ -1,11 +1,17 @@ const html = require('choo/html'); const assets = require('../../../common/assets'); +const title = require('../../templates/title'); module.exports = function(state) { return html`
-
${state.translate('expiredPageHeader')}
- + + ${title(state)} + +
${state.translate('expiredPageHeader')}
+
${state.translate('uploadPageExplainer')}
diff --git a/app/pages/password/index.js b/app/pages/password/index.js new file mode 100644 index 00000000..e4c06949 --- /dev/null +++ b/app/pages/password/index.js @@ -0,0 +1,19 @@ +const html = require('choo/html'); +const titleSection = require('../../templates/title'); +const downloadPassword = require('../../templates/downloadPassword'); + +module.exports = function(state, emit) { + return html` +
+ ${titleSection(state)} + +
${state.translate('downloadMessage2')}
+ ${downloadPassword(state, emit)} + + + ${state.translate('sendYourFilesLink')} + + +
+ `; +}; diff --git a/app/pages/preview/index.js b/app/pages/preview/index.js index 91b44846..d86fb1b1 100644 --- a/app/pages/preview/index.js +++ b/app/pages/preview/index.js @@ -1,40 +1,25 @@ const html = require('choo/html'); -const assets = require('../../../common/assets'); -const { bytes } = require('../../utils'); +const titleSection = require('../../templates/title'); +const downloadButton = require('../../templates/downloadButton'); +const downloadedFiles = require('../../templates/uploadedFileList'); -module.exports = function(state, pageAction) { - const fileInfo = state.fileInfo; +module.exports = function(state, emit) { + const storageFile = state.storage.getFileById(state.params.id); - const size = fileInfo.size - ? state.translate('downloadFileSize', { size: bytes(fileInfo.size) }) - : ''; + const multifiles = Array.from(storageFile.manifest.files); - const title = fileInfo.name - ? state.translate('downloadFileName', { filename: fileInfo.name }) - : state.translate('downloadFileTitle'); - - const info = html` -
`; - if (!pageAction) { - return info; - } return html`
-
- ${title} - ${' ' + size} -
-
${state.translate('downloadMessage')}
- - ${pageAction} + ${titleSection(state)} + + ${downloadedFiles(multifiles, state, emit)} +
${state.translate('downloadMessage2')}
+ ${downloadButton(state, emit)} + ${state.translate('sendYourFilesLink')} - ${info} +
`; }; diff --git a/app/pages/share/index.js b/app/pages/share/index.js index 5710c8ef..ba9b8b71 100644 --- a/app/pages/share/index.js +++ b/app/pages/share/index.js @@ -3,42 +3,51 @@ const html = require('choo/html'); const raw = require('choo/html/raw'); const assets = require('../../../common/assets'); const notFound = require('../notFound'); -const setPasswordSection = require('../../templates/setPasswordSection'); -const selectbox = require('../../templates/selectbox'); const deletePopup = require('../../templates/popup'); +const uploadedFiles = require('../../templates/uploadedFileList'); const { allowedCopy, delay, fadeOut } = require('../../utils'); module.exports = function(state, emit) { const file = state.storage.getFileById(state.params.id); if (!file) { - return notFound(state, emit); + return notFound(state); } + const passwordReminderClass = file._hasPassword + ? '' + : 'passwordReminder--hidden'; + + const multifiles = Array.from(file.manifest.files); + return html` -
- ${expireInfo(file, state.translate, emit)} -
+ +
+ + + + ${expireInfo(file, state.translate)} + + ${uploadedFiles(multifiles, state, emit)} + +
- ${state.translate('copyUrlFormLabelWithName', { filename: file.name })} + ${state.translate('copyUrlFormLabelWithName', { filename: '' })} +
(don't forget the password too)
-
- - -
- ${setPasswordSection(state, emit)} - +
${deletePopup( state.translate('deletePopupText'), @@ -47,11 +56,15 @@ module.exports = function(state, emit) { deleteFile )}
- ${state.translate('sendAnotherFileLink')} + + ${state.translate('deleteFileButton')} + +
-
+ `; function showPopup() { @@ -60,30 +73,22 @@ module.exports = function(state, emit) { popup.focus(); } - async function sendNew(e) { - e.preventDefault(); - await fadeOut('#shareWrapper'); - emit('pushState', '/'); - } - async function copyLink() { if (allowedCopy()) { emit('copy', { url: file.url, location: 'success-screen' }); const input = document.getElementById('fileUrl'); input.disabled = true; - input.classList.add('input--copied'); const copyBtn = document.getElementById('copyBtn'); copyBtn.disabled = true; - copyBtn.classList.add('inputBtn--copied'); + copyBtn.classList.add('copyBtn--copied'); copyBtn.replaceChild( - html``, + html``, copyBtn.firstChild ); await delay(2000); input.disabled = false; - input.classList.remove('input--copied'); copyBtn.disabled = false; - copyBtn.classList.remove('inputBtn--copied'); + copyBtn.classList.remove('copyBtn--copied'); copyBtn.textContent = state.translate('copyUrlFormButton'); } } @@ -95,18 +100,14 @@ module.exports = function(state, emit) { } }; -function expireInfo(file, translate, emit) { +function expireInfo(file, translate) { const hours = Math.floor(EXPIRE_SECONDS / 60 / 60); - const el = html`
${raw( + const el = html`
${raw( translate('expireInfo', { - downloadCount: '', + downloadCount: translate('downloadCount', { num: file.dlimit }), timespan: translate('timespanHours', { num: hours }) }) )}
`; - const select = el.querySelector('select'); - const options = [1, 2, 3, 4, 5, 20].filter(i => i > (file.dtotal || 0)); - const t = num => translate('downloadCount', { num }); - const changed = value => emit('changeLimit', { file, value }); - el.replaceChild(selectbox(file.dlimit || 1, options, t, changed), select); + return el; } diff --git a/app/pages/share/share.css b/app/pages/share/share.css index 392e3db7..7c03bcd5 100644 --- a/app/pages/share/share.css +++ b/app/pages/share/share.css @@ -1,112 +1,86 @@ -.sharePage { - margin: 0 auto; - display: flex; - justify-content: center; - flex-direction: column; - width: 100%; - max-width: 640px; +.sharePage__copyText { + margin: 8px 0 8px; + word-wrap: break-word; + font-size: 15px; + color: var(--lightTextColor); + text-align: center; } -.sharePage__copyText { - align-self: flex-start; - margin-top: 60px; - margin-bottom: 10px; - color: var(--textColor); - max-width: 614px; - word-wrap: break-word; +.sharePage__passwordReminder { + font-size: 11px; + font-style: italic; +} + +.passwordReminder--hidden { + display: none; } .sharePage__deletePopup { position: relative; - align-self: center; - bottom: 50px; + margin-top: -70px; + pointer-events: none; +} + +.shareTitle { + color: var(--textColor); + margin: 8px auto 15px; + text-align: center; + font-family: 'SF Pro Text', sans-serif; + font-size: 12px; + font-style: italic; + width: 280px; } .copySection { - display: flex; - flex-wrap: nowrap; width: 100%; } .copySection__url { - flex: 1; - height: 56px; - border: 1px solid var(--primaryControlBGColor); - border-radius: 6px 0 0 6px; - font-size: 20px; - color: var(--inputTextColor); + box-sizing: border-box; + height: 30px; + border: 1px solid var(--lightBorderColor); + border-radius: 4px; + font-size: 15px; + color: var(--primaryControlBGColor); + margin: 0 0 6px; + padding: 6px; font-family: 'SF Pro Text', sans-serif; letter-spacing: 0; - line-height: 23px; - font-weight: 300; - padding-left: 10px; + line-height: 18px; + font-weight: 500; } .copySection__url:disabled { - border: 1px solid var(--successControlBGColor); background: var(--successControlFGColor); } -.inputBtn--copy { - flex: 0 1 165px; - padding-bottom: 4px; -} - .input--copied { border-color: var(--successControlBGColor); } -.inputBtn--copied, -.inputBtn--copied:hover { +.copyBtn { + transition: background 0.5s; +} + +.copyBtn--copied, +.copyBtn--copied:hover { background: var(--successControlBGColor); - border: 1px solid var(--successControlBGColor); color: var(--successControlFGColor); + transition: background 0s; } .btn--delete { + border: none; align-self: center; width: 176px; - height: 44px; background: #fff; - border-color: rgba(12, 12, 13, 0.3); - margin-top: 50px; - margin-bottom: 12px; - color: #313131; + margin: 10px 0 0; + font-size: 14px; + line-height: 16px; + color: var(--errorColor); + cursor: pointer; } .btn--delete:hover { - background: #efeff1; -} - -@media (max-device-width: 768px), (max-width: 768px) { - .copySection { - width: 100%; - } - - .copySection__url { - font-size: 18px; - } -} - -@media (max-device-width: 520px), (max-width: 520px) { - .copySection { - width: 100%; - flex-direction: column; - padding-left: 0; - } - - .copySection__url { - font-size: 22px; - padding: 15px 10px; - border-radius: 6px 6px 0 0; - } - - .sharePage__copyText { - text-align: center; - } - - .inputBtn--copy { - border-radius: 0 0 6px 6px; - flex: 0 1 65px; - } + text-decoration: underline; } diff --git a/app/pages/signin/index.js b/app/pages/signin/index.js new file mode 100644 index 00000000..652430b9 --- /dev/null +++ b/app/pages/signin/index.js @@ -0,0 +1,60 @@ +const html = require('choo/html'); +const assets = require('../../../common/assets'); +const title = require('../../templates/title'); + +// eslint-disable-next-line no-unused-vars +module.exports = function(state, emit) { + return html` + +
+ + + + ${title(state)} + + + + + + + +
+ `; +}; diff --git a/app/pages/signin/signin.css b/app/pages/signin/signin.css new file mode 100644 index 00000000..6b94511a --- /dev/null +++ b/app/pages/signin/signin.css @@ -0,0 +1,33 @@ +.signInPage { + font-size: 13px; + line-height: 18px; + color: var(--lightTextColor); +} + +.signIn__info { + width: 308px; + margin: 16px auto 0; + text-align: left; +} + +.signIn__firefoxLogo { + display: block; + margin: 0 auto; +} + +.signIn__emailLabel { + font-size: 22px; + margin: 8px 0; +} + +.signIn__emailInput { + box-sizing: border-box; + width: 310px; + height: 40px; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 4px; + margin: 0; + padding: 0 8px; + font-size: 18px; + color: var(--lightTextColor); +} diff --git a/app/pages/unsupported/index.js b/app/pages/unsupported/index.js index 99937cfd..61c724f5 100644 --- a/app/pages/unsupported/index.js +++ b/app/pages/unsupported/index.js @@ -1,11 +1,13 @@ const html = require('choo/html'); const assets = require('../../../common/assets'); +const title = require('../../templates/title'); module.exports = function(state) { let strings = {}; let why = ''; let url = ''; let buttonAction = ''; + if (state.params.reason !== 'outdated') { strings = unsupportedStrings(state); why = html` @@ -28,20 +30,26 @@ module.exports = function(state) { ${strings.button}
`; } + return html` -
-
${strings.title}
-
+
+ ${title(state)} +
${strings.header}
+
${strings.description} + ${why}
- ${why} - - - ${buttonAction} - + + +
${strings.explainer}
@@ -50,7 +58,7 @@ module.exports = function(state) { function outdatedStrings(state) { return { - title: state.translate('notSupportedHeader'), + header: state.translate('notSupportedHeader'), description: state.translate('notSupportedOutdatedDetail'), button: state.translate('updateFirefox'), explainer: state.translate('uploadPageExplainer') @@ -59,7 +67,7 @@ function outdatedStrings(state) { function unsupportedStrings(state) { return { - title: state.translate('notSupportedHeader'), + header: state.translate('notSupportedHeader'), description: state.translate('notSupportedDetail'), button: state.translate('downloadFirefoxButtonSub'), explainer: state.translate('uploadPageExplainer') diff --git a/app/pages/unsupported/unsupported.css b/app/pages/unsupported/unsupported.css index 8f22443a..b34adcae 100644 --- a/app/pages/unsupported/unsupported.css +++ b/app/pages/unsupported/unsupported.css @@ -1,26 +1,30 @@ .unsupportedPage { - display: flex; justify-content: center; align-items: center; - flex-direction: column; +} + +.unsupportedPage__error { + margin: 10px 0 20px; } .unsupportedPage__info { font-size: 13px; - line-height: 23px; - text-align: center; color: var(--lightTextColor); - margin: 0 auto 23px; + margin: 0 auto 10px; } .firefoxDownload { - margin-bottom: 181px; - height: 80px; - background: #98e02b; + flex: 2; +} + +.firefoxDownload__button { + margin: 0 auto 20px; + height: 70px; + width: 250px; + background: #12bc00; border-radius: 3px; cursor: pointer; border: 0; - box-shadow: 0 5px 3px rgb(234, 234, 234); font-family: 'Fira Sans', 'segoe ui', sans-serif; font-weight: 500; color: var(--primaryControlFGColor); @@ -29,21 +33,22 @@ justify-content: center; align-items: center; line-height: 1; - padding: 0 25px; + padding: 8px; } .firefoxDownload__logo { - width: 70px; + width: 55px; } .firefoxDownload__action { text-align: left; - margin-left: 20.4px; + text-transform: uppercase; + margin-left: 20px; } .firefoxDownload__text { + text-transform: none; font-family: 'Fira Sans', 'segoe ui', sans-serif; font-weight: 300; - font-size: 18px; - letter-spacing: -0.69px; + font-size: 17px; } diff --git a/app/pages/upload/index.js b/app/pages/upload/index.js deleted file mode 100644 index 81353e0f..00000000 --- a/app/pages/upload/index.js +++ /dev/null @@ -1,39 +0,0 @@ -const html = require('choo/html'); -const progress = require('../../templates/progress'); -const { bytes } = require('../../utils'); - -module.exports = function(state, emit) { - const transfer = state.transfer; - - return html` -
-
- ${state.translate('uploadingPageProgress', { - filename: transfer.file.name, - size: bytes(transfer.file.size) - })} -
-
- ${progress(transfer.progressRatio, transfer.progressIndefinite)} -
-
- ${state.translate(transfer.msg, transfer.sizes)} -
- -
-
- `; - - function cancel() { - const btn = document.getElementById('cancel-upload'); - btn.disabled = true; - btn.textContent = state.translate('uploadCancelNotification'); - emit('cancel'); - } -}; diff --git a/app/pages/welcome/index.js b/app/pages/welcome/index.js index d7a63f12..3f143ba5 100644 --- a/app/pages/welcome/index.js +++ b/app/pages/welcome/index.js @@ -1,50 +1,86 @@ -/* global MAXFILESIZE */ const html = require('choo/html'); const assets = require('../../../common/assets'); -const fileList = require('../../templates/fileList'); -const { bytes, fadeOut } = require('../../utils'); +const { checkSize } = require('../../utils'); +const title = require('../../templates/title'); +const setPasswordSection = require('../../templates/setPasswordSection'); +const uploadBox = require('../../templates/uploadedFileList'); +const expireInfo = require('../../templates/expireInfo'); module.exports = function(state, emit) { // the page flickers if both the server and browser set 'effect--fadeIn' const fade = state.layout ? '' : 'effect--fadeIn'; + const files = state.files ? state.files : []; + + const optionClass = state.uploading ? 'uploadOptions--faded' : ''; + const btnUploading = state.uploading ? 'btn--stripes' : ''; + const faded = files.length > 0 ? 'uploadArea--faded' : ''; + const selectFileClass = files.length > 0 ? 'btn--hidden' : ''; + const sendFileClass = files.length > 0 ? '' : 'btn--hidden'; + + let btnText = ''; + + if (state.encrypting) { + btnText = state.translate('encryptingFile'); + } else if (state.uploading) { + btnText = `sending... ${Math.floor(state.transfer.progressRatio * 100)}%`; + } else { + //default pre-upload text + btnText = state.translate('uploadSuccessConfirmHeader'); + } + return html` -
-
${state.translate('uploadPageHeader')}
-
-
${state.translate('uploadPageExplainer')}
- - ${state.translate('uploadPageLearnMore')} - -
-
+ ${title(state)} + +
- `; diff --git a/app/templates/header/header.css b/app/templates/header/header.css index 91b6ad04..fa345dc8 100644 --- a/app/templates/header/header.css +++ b/app/templates/header/header.css @@ -3,7 +3,7 @@ box-sizing: border-box; display: flex; justify-content: space-between; - padding: 31px; + padding: 20px; width: 100%; } @@ -62,7 +62,7 @@ color: var(--primaryControlFGColor); cursor: pointer; display: block; - float: right; + float: left; font-size: 12px; line-height: 12px; opacity: 0.9; @@ -88,17 +88,10 @@ background-color: var(--primaryControlHoverColor); } -@media (max-device-width: 520px), (max-width: 520px) { +@media (max-device-width: 750px), (max-width: 750px) { .header { + padding-top: 60px; flex-direction: column; justify-content: flex-start; } - - .feedback { - margin-top: 10px; - min-width: 30px; - max-width: 300px; - text-indent: 2px; - padding: 5px 5px 5px 20px; - } } diff --git a/app/templates/header/index.js b/app/templates/header/index.js index b5fbf882..4f4b9151 100644 --- a/app/templates/header/index.js +++ b/app/templates/header/index.js @@ -6,8 +6,6 @@ module.exports = function(state) { const feedbackUrl = `https://qsurvey.mozilla.com/s3/txp-firefox-send?ver=${version}&browser=${browser}`; const header = html`
-
+ `; +}; diff --git a/app/templates/signupPromo/signupPromo.css b/app/templates/signupPromo/signupPromo.css new file mode 100644 index 00000000..ce372c38 --- /dev/null +++ b/app/templates/signupPromo/signupPromo.css @@ -0,0 +1,85 @@ +.signupPromo { + display: flex; + flex-direction: column; + position: absolute; + right: 0; + height: 140px; + width: 150px; + background: #ffe900; + font-size: 13px; + font-weight: 500; + text-align: center; + justify-content: center; +} + +.signupPromo::before { + content: ''; + width: 0; + height: 0; + border-style: solid; + border-width: 0 35px 140px 0; + border-color: transparent #ffe900 transparent; + position: absolute; + right: 100%; +} + +.signupPromo::after { + content: ''; + width: 0; + height: 0; + border-style: solid; + border-width: 0 150px 35px 0; + border-color: transparent #ffe900 transparent; + position: absolute; + top: 100%; + left: 0%; +} + +.signupPromo__title { + color: #0a84ff; + font-size: 22px; + font-style: italic; + font-weight: 600; + padding: 6px; +} + +.signupPromo__info { + color: var(--lightTextColor); + padding: 6px; +} + +.signupPromo__link { + z-index: 5; +} + +.signupPromo__link:hover { + text-decoration: underline; +} + +@media (max-device-width: 750px), (max-width: 750px) { + .signupPromo { + flex-direction: row; + align-items: center; + height: 40px; + width: 100%; + } + + .signupPromo::before { + visibility: hidden; + } + + .signupPromo::after { + border-width: 15px 50vw 0 50vw; + border-color: #ffe900 transparent transparent; + } +} + +@media (max-device-width: 500px), (max-width: 500px) { + .signupPromo__link { + display: none; + } + + .signupPromo { + overflow: hidden; + } +} diff --git a/app/templates/title/index.js b/app/templates/title/index.js new file mode 100644 index 00000000..6e0b0e01 --- /dev/null +++ b/app/templates/title/index.js @@ -0,0 +1,11 @@ +const html = require('choo/html'); + +module.exports = function(state) { + return html` +
+ ${state.translate('uploadPageHeader')} +
+ ${state.translate('pageHeaderCredits')} +
+
`; +}; diff --git a/app/templates/title/title.css b/app/templates/title/title.css new file mode 100644 index 00000000..1f45a798 --- /dev/null +++ b/app/templates/title/title.css @@ -0,0 +1,13 @@ +.boxTitle { + font-size: 15px; + text-align: center; + font-weight: 500; + margin: 9px 0 19px 0; + color: var(--lightTextColor); +} + +.boxSubtitle { + font-size: 12px; + font-weight: 300; + font-style: italic; +} diff --git a/app/templates/uploadedFile/index.js b/app/templates/uploadedFile/index.js new file mode 100644 index 00000000..7fc65ccf --- /dev/null +++ b/app/templates/uploadedFile/index.js @@ -0,0 +1,64 @@ +const html = require('choo/html'); +const assets = require('../../../common/assets'); +const bytes = require('../../utils').bytes; +const fileIcon = require('../fileIcon'); + +module.exports = function(file, state, emit) { + const transfer = state.transfer; + const transferState = transfer ? transfer.state : null; + const transferring = state.uploading || state.downloading; + const share = state.route.includes('share/'); + const complete = share ? 'uploadedFile--completed' : ''; + + const cancelVisible = + transferring || state.route === '/' ? 'uploadedFile__cancel--visible' : ''; + + const stampClass = + share || transferState === 'complete' ? 'uploadedFile__stamp--visible' : ''; + + function cancel(event) { + event.preventDefault(); + const btn = document.querySelector('.uploadedFile__cancel'); + btn.disabled = true; + if (transferring) { + emit('cancel'); + } else if (state.route === '/') { + emit('removeUpload', { file }); + } + } + + //const percent = share ? 100 : Math.floor(progressRatio * 100); + /* + style=" + background: linear-gradient(to right, + #e8f2fe 0%, + #e8f2fe ${percent}%, + #fff ${percent}%, + #fff 100%);" + */ + + return html` +
  • + + ${fileIcon(file.name, file._hasPassword)} + +
    + cancel +
    + +
    +

    ${file.name}

    +

    + ${bytes(file.size)} +

    +
    + + +
  • + `; +}; diff --git a/app/templates/uploadedFile/uploadedFile.css b/app/templates/uploadedFile/uploadedFile.css new file mode 100644 index 00000000..f3fe4dcb --- /dev/null +++ b/app/templates/uploadedFile/uploadedFile.css @@ -0,0 +1,70 @@ +.uploadedFile { + margin: 11px; + list-style-type: none; + font-size: 11px; + line-height: 18px; + text-align: initial; + color: var(--lightTextColor); + background-color: var(--pageBGColor); + border: 1px solid #cececf; + box-sizing: border-box; + height: 53px; + border-radius: 4px; + position: relative; +} + +.uploadedFile--completed { + background-color: #e8f2fe; +} + +.uploadedFile__fileData { + margin: 8px 16px 8px 44px; +} + +.uploadedFile__fileName { + margin: 0; + font-size: 13px; + font-weight: 500; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + +.uploadedFile__fileInfo { + margin: 0; +} + +.uploadedFile__cancel { + float: right; + margin: 6px; + visibility: hidden; +} + +.uploadedFile:hover .uploadedFile__cancel--visible { + visibility: visible; +} + +.uploadedFile__stamp { + position: absolute; + top: -4px; + right: -8px; + visibility: hidden; + opacity: 0; +} + +.uploadedFile__stamp--visible { + visibility: visible; + opacity: 1; + animation: stampDown 0.2s linear; +} + +@keyframes stampDown { + 0% { + opacity: 0; + transform: scale(1.5); + } + + 100% { + opacity: 1; + } +} diff --git a/app/templates/uploadedFileList/index.js b/app/templates/uploadedFileList/index.js new file mode 100644 index 00000000..c10c4ce7 --- /dev/null +++ b/app/templates/uploadedFileList/index.js @@ -0,0 +1,12 @@ +const html = require('choo/html'); +const file = require('../uploadedFile'); + +module.exports = function(files, state, emit) { + //const progressRatio = state.transfer ? state.transfer.progressRatio : 0; + + return html` +
      + ${files.map(f => file(f, state, emit))} +
    + `; +}; diff --git a/app/templates/uploadedFileList/uploadedFileList.css b/app/templates/uploadedFileList/uploadedFileList.css new file mode 100644 index 00000000..378faf5e --- /dev/null +++ b/app/templates/uploadedFileList/uploadedFileList.css @@ -0,0 +1,10 @@ +.uploadedFiles { + border: 1px solid rgba(12, 12, 13, 0.1); + border-radius: 4px; + box-sizing: border-box; + margin: 0; + padding: 0; + align-content: center; + flex: 1; + overflow-y: scroll; +} diff --git a/app/templates/userAccount/index.js b/app/templates/userAccount/index.js new file mode 100644 index 00000000..76b28a2a --- /dev/null +++ b/app/templates/userAccount/index.js @@ -0,0 +1,24 @@ +const html = require('choo/html'); +const assets = require('../../../common/assets'); + +// eslint-disable-next-line no-unused-vars +module.exports = function(state) { + return html` + `; + + function onclick(event) { + event.preventDefault(); + const dropdown = document.querySelector('.account_dropdown'); + dropdown.classList.toggle('visible'); + } +}; diff --git a/app/templates/userAccount/userAccount.css b/app/templates/userAccount/userAccount.css new file mode 100644 index 00000000..b92d54e4 --- /dev/null +++ b/app/templates/userAccount/userAccount.css @@ -0,0 +1,33 @@ +.account { + position: absolute; + right: 0; + margin: 0 21px; + padding: 0; +} + +.account_dropdown { + z-index: 1; + position: absolute; + top: 25px; + left: -10px; + width: 150px; + list-style-type: none; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 4px; + background-color: var(--pageBGColor); + box-shadow: 0 5px 12px 0 rgba(0, 0, 0, 0.2); + padding: 11px 0; + visibility: hidden; +} + +.account_dropdown__item { + padding: 0 14px; + color: var(--lightTextColor); + font-size: 13px; + line-height: 24px; +} + +.account_dropdown__item:hover { + background-color: var(--primaryControlBGColor); + color: var(--primaryControlFGColor); +} diff --git a/app/utils.js b/app/utils.js index 8baa6617..2ec84e23 100644 --- a/app/utils.js +++ b/app/utils.js @@ -1,3 +1,4 @@ +/* global MAXFILESIZE */ const b64 = require('base64-js'); function arrayToB64(array) { @@ -128,6 +129,27 @@ function openLinksInNewTab(links, should = true) { return links; } +function checkSize(files, oldfiles, translate) { + function size(arr) { + let total = 0; + for (let i = 0; i < arr.length; i++) { + total += arr[i].size; + } + return total; + } + + const addSize = size(files); + if (addSize === 0) { + return; + } + const totalSize = addSize + size(oldfiles); + if (totalSize > MAXFILESIZE) { + // eslint-disable-next-line no-alert + alert(translate('fileTooBig', { size: bytes(MAXFILESIZE) })); + return; + } +} + module.exports = { fadeOut, delay, @@ -140,5 +162,6 @@ module.exports = { b64ToArray, loadShim, isFile, - openLinksInNewTab + openLinksInNewTab, + checkSize }; diff --git a/assets/addfile.svg b/assets/addfile.svg new file mode 100644 index 00000000..c55f170e --- /dev/null +++ b/assets/addfile.svg @@ -0,0 +1,10 @@ + + + + Shape + Created with Sketch. + + + + + \ No newline at end of file diff --git a/assets/back-arrow.svg b/assets/back-arrow.svg new file mode 100644 index 00000000..ee746536 --- /dev/null +++ b/assets/back-arrow.svg @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/assets/blue_file.svg b/assets/blue_file.svg new file mode 100644 index 00000000..c06eefd8 --- /dev/null +++ b/assets/blue_file.svg @@ -0,0 +1,13 @@ + + + + Group 8 + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/assets/lock-white.svg b/assets/lock-white.svg new file mode 100644 index 00000000..429a8dec --- /dev/null +++ b/assets/lock-white.svg @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/assets/lock.svg b/assets/lock.svg new file mode 100644 index 00000000..bf4b6249 --- /dev/null +++ b/assets/lock.svg @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/assets/red_file.svg b/assets/red_file.svg new file mode 100644 index 00000000..d52e51b1 --- /dev/null +++ b/assets/red_file.svg @@ -0,0 +1,13 @@ + + + + Group 8 + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/assets/sent-done.svg b/assets/sent-done.svg new file mode 100644 index 00000000..8c1dfe4f --- /dev/null +++ b/assets/sent-done.svg @@ -0,0 +1,19 @@ + + + + Group + Created with Sketch. + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/upload.svg b/assets/upload.svg deleted file mode 100644 index f98e6b2e..00000000 --- a/assets/upload.svg +++ /dev/null @@ -1 +0,0 @@ -upload \ No newline at end of file diff --git a/assets/user.svg b/assets/user.svg new file mode 100644 index 00000000..3d052d07 --- /dev/null +++ b/assets/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/locales/en-US/send.ftl b/public/locales/en-US/send.ftl index 60bd9e8c..43d017cd 100644 --- a/public/locales/en-US/send.ftl +++ b/public/locales/en-US/send.ftl @@ -26,6 +26,7 @@ uploadSuccessConfirmHeader = Ready to Send uploadSvgAlt = Upload uploadSuccessTimingHeader = The link to your file will expire after 1 download or in 24 hours. expireInfo = The link to your file will expire after { $downloadCount } or { $timespan }. +frontPageExpireInfo = Expires after { $downloadCount } or { $timespan } downloadCount = { $num -> [one] 1 download *[other] { $num } downloads @@ -34,6 +35,14 @@ timespanHours = { $num -> [one] 1 hour *[other] { $num } hours } +timespanMinutes = { $num -> + [one] 1 minute + *[other] { $num } minutes + } +timespanWeeks = { $num -> + [one] 1 week + *[other] { $num } weeks + } copyUrlFormLabelWithName = Copy and share the link to send your file: { $filename } copyUrlFormButton = Copy to clipboard copiedUrl = Copied! @@ -117,3 +126,30 @@ passwordIsSet = Password set maxPasswordLength = Maximum password length: { $length } # A short status message shown when there was an error setting the password passwordSetError = This password could not be set +pageHeaderCredits = from the makers of Firefox +addFilesButton = Add file(s) +uploadFilesButton = Send +uploadFileProgress = Sending +uploadDropDragMessage = Drop files here +uploadDropClickMessage = or click to select a file +addPasswordMessage = Protect with password +addPasswordLabel = Password: +copyUrlLabel = Copy and share this link: +passwordReminder = don't forget the password too +signInPromoText = Sign In/Up! +signInExplanation = It's free and gives you many more Send options +signInLearnMore = Learn more! +downloadProgressButton = Downloading... { $progress } +downloadMessage2 = Firefox Send lets you share files with a safe, private, and encrypted link that automatically expires to ensure your stuff does not remain online forever. +signInEmailEnter = Enter your Email +emailEntryPlaceholder = Email +signInContinueMessage = to continue to Firefox Send +signInContinueButton = Continue +accountBenefitTitle = With a free Firefox Account with Send you can: +accountBenefitMultiFile = Send multiple files at once +accountBenefitLargeFiles = Upload larger files (up to { $size } GB) +accountBenefitExpiry = Have more expiry options +accountBenefitSync = Manage your uploads across devices +accountBenefitNotify = Be notified when your files are downloaded +accountBenefitMore = Do a lot more! + diff --git a/server/routes/exists.js b/server/routes/exists.js index badb32c3..da49c019 100644 --- a/server/routes/exists.js +++ b/server/routes/exists.js @@ -5,7 +5,7 @@ module.exports = async (req, res) => { const meta = await storage.metadata(req.params.id); res.set('WWW-Authenticate', `send-v1 ${meta.nonce}`); res.send({ - password: meta.pwd + requiresPassword: meta.pwd }); } catch (e) { res.sendStatus(404); diff --git a/server/routes/index.js b/server/routes/index.js index d258c662..33091402 100644 --- a/server/routes/index.js +++ b/server/routes/index.js @@ -49,7 +49,7 @@ module.exports = function(app) { next(); }); app.use(express.json()); - app.get('/', language, pages.index); + app.get('/', language, pages.blank); app.get('/legal', language, pages.legal); app.get('/jsconfig.js', require('./jsconfig')); app.get(`/share/:id${ID_REGEX}`, language, pages.blank); diff --git a/server/routes/pages.js b/server/routes/pages.js index 74f633f1..4b8128cf 100644 --- a/server/routes/pages.js +++ b/server/routes/pages.js @@ -19,16 +19,15 @@ module.exports = { download: async function(req, res, next) { const id = req.params.id; - try { const { nonce, pwd } = await storage.metadata(id); res.set('WWW-Authenticate', `send-v1 ${nonce}`); res.send( stripEvents( routes.toString( - `/download/${req.params.id}`, + `/download/${id}`, Object.assign(state(req), { - fileInfo: { nonce, requiresPassword: +pwd } + fileInfo: { nonce, requiresPassword: pwd } }) ) )