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..6070799f 100644 --- a/app/archive.js +++ b/app/archive.js @@ -1,3 +1,4 @@ +/* global MAXFILESIZE */ import { blobStream, concatStream } from './streams'; export default class Archive { @@ -17,6 +18,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 => ({ @@ -30,4 +35,21 @@ export default class Archive { get stream() { return concatStream(this.files.map(file => blobStream(file))); } + + addFiles(files) { + const newSize = files.reduce((total, file) => total + file.size, 0); + if (this.size + newSize > MAXFILESIZE) { + return false; + } + this.files = this.files.concat(files); + return true; + } + + checkSize() { + return this.size <= MAXFILESIZE; + } + + remove(index) { + this.files.splice(index, 1); + } } diff --git a/app/base.css b/app/base.css index 2360a33a..f30d5c63 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,22 @@ a { .main { display: flex; - flex-direction: row; flex: auto; - padding: 0 20px; + padding: 0 25px; box-sizing: border-box; + min-height: 500px; + max-height: 800px; + height: 100px; } .stripedBox { - width: 480px; + flex: none; + position: relative; + width: 400px; + margin-top: 32px; 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 +75,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 +87,8 @@ a { } .uploads { - flex: auto; + flex: 0 0 262px; + position: relative; } .noscript { @@ -87,13 +98,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,35 +118,42 @@ a { background-color: var(--primaryControlHoverColor); } +.btn--stripes { + background: repeating-linear-gradient( + -65deg, + #7c7c7c 0, + #7c7c7c 17px, + #737373 17px, + #737373 30px + ); + background-size: 300% 300%; + animation: barberpole 12s linear infinite; +} + +@keyframes barberpole { + 0% { + background-position: 100% 0%; + } + + 100% { + background-position: 0% 0%; + } +} + .btn--cancel { + font-size: 13px; + font-weight: 700; + background: none; color: var(--errorColor); - background: var(--pageBGColor); - font-size: 15px; - border: 0; - cursor: pointer; - text-decoration: underline; -} - -.btn--cancel:disabled { - text-decoration: none; - cursor: auto; -} - -.btn--cancel:hover { - background-color: var(--pageBGColor); + border: none; } .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; } @@ -138,44 +162,6 @@ a { 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; -} - -.inputBtn:disabled { - cursor: auto; -} - -.inputBtn:hover { - background-color: var(--primaryControlHoverColor); -} - -.inputBtn--hidden { - display: none; -} - -.cursor--pointer { - cursor: pointer; -} - .link { color: var(--linkColor); text-decoration: none; @@ -188,36 +174,19 @@ 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; } -.progressSection { - margin: 0 auto; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - text-align: center; - font-size: 15px; -} - -.progressSection__text { - color: var(--lightTextColor); - letter-spacing: -0.4px; - margin-top: 24px; - margin-bottom: 74px; -} - .effect--fadeOut { opacity: 0; animation: fadeout 200ms linear; @@ -248,50 +217,75 @@ a { } } +.goBackButton { + position: absolute; + top: 0; + left: 0; + margin: 18px; +} + .error { color: var(--errorColor); + font-weight: 600; } .title { - color: var(--textColor); - font-size: 33px; + color: var(--lightTextColor); + font-size: 18px; line-height: 40px; margin: 20px auto; - text-align: center; max-width: 520px; font-family: 'SF Pro Text', sans-serif; + font-weight: 700; word-wrap: break-word; } .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 !important; +} + +.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; + height: 100%; } - .inputBtn { - border-radius: 0 0 6px 6px; - flex: 0 1 65px; + .spacer { + flex: none; + height: 0; } - .input--noBtn { - border-radius: 6px; + .stripedBox { + margin-top: 72; + min-height: 400px; + flex: 1; + } + + .uploads { + flex: none; + } + + .footer { + margin: 15px; } } diff --git a/app/dragManager.js b/app/dragManager.js index 7713e6ee..3002e9c1 100644 --- a/app/dragManager.js +++ b/app/dragManager.js @@ -1,12 +1,10 @@ -/* global MAXFILESIZE */ -import Archive from './archive'; -import { bytes } 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 +13,10 @@ 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 files = Array.from(event.dataTransfer.files); + + emitter.emit('addFiles', { files }); } }); }); diff --git a/app/fileManager.js b/app/fileManager.js index 087cddc2..f294f199 100644 --- a/app/fileManager.js +++ b/app/fileManager.js @@ -1,13 +1,11 @@ +/* global MAXFILESIZE */ 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'; +import { bytes } from './utils'; export default function(state, emitter) { let lastRender = 0; @@ -64,6 +62,11 @@ export default function(state, emitter) { metrics.changedDownloadLimit(file); }); + emitter.on('removeUpload', async ({ index }) => { + state.archive.remove(index); + render(); + }); + emitter.on('delete', async ({ file, location }) => { try { metrics.deletedUpload({ @@ -85,11 +88,32 @@ export default function(state, emitter) { state.transfer.cancel(); }); - emitter.on('upload', async ({ file, type }) => { - const size = file.size; - const sender = new FileSender(file); + emitter.on('addFiles', async ({ files }) => { + if (state.archive) { + if (!state.archive.addFiles(files)) { + // eslint-disable-next-line no-alert + alert(state.translate('fileTooBig', { size: bytes(MAXFILESIZE) })); + return; + } + } else { + const archive = new Archive(files); + if (!archive.checkSize()) { + // eslint-disable-next-line no-alert + alert(state.translate('fileTooBig', { size: bytes(MAXFILESIZE) })); + return; + } + state.archive = archive; + } + render(); + }); + + emitter.on('upload', async ({ type, dlCount, password }) => { + if (!state.archive) return; + const size = state.archive.size; + const sender = new FileSender(state.archive); sender.on('progress', updateProgress); sender.on('encrypting', render); + sender.on('complete', render); state.transfer = sender; state.uploading = true; render(); @@ -98,19 +122,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 +157,8 @@ export default function(state, emitter) { } } finally { openLinksInNewTab(links, false); + state.files = []; + state.password = ''; state.uploading = false; state.transfer = null; } @@ -150,6 +182,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 +215,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 +230,10 @@ 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'); } 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/legal.js b/app/pages/legal.js index e045ab76..f18a926a 100644 --- a/app/pages/legal.js +++ b/app/pages/legal.js @@ -1,9 +1,15 @@ const html = require('choo/html'); const raw = require('choo/html/raw'); +const assets = require('../../common/assets'); +const title = require('../templates/title'); module.exports = function(state) { return html`
+ + + + ${title(state)}
${state.translate('legalHeader')}
${raw( replaceLinks(state.translate('legalNoticeTestPilot'), [ 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..aa86d862 100644 --- a/app/pages/preview/index.js +++ b/app/pages/preview/index.js @@ -1,40 +1,42 @@ 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 ownedFile = state.storage.getFileById(state.params.id); - const size = fileInfo.size - ? state.translate('downloadFileSize', { size: bytes(fileInfo.size) }) - : ''; + const trySendLink = html` + + ${state.translate('sendYourFilesLink')} + `; + const cancelButton = html` + + `; - const title = fileInfo.name - ? state.translate('downloadFileName', { filename: fileInfo.name }) - : state.translate('downloadFileTitle'); + const bottomLink = + state.transfer.state === 'downloading' ? cancelButton : trySendLink; - const info = html` -
`; - if (!pageAction) { - return info; - } return html`
-
- ${title} - ${' ' + size} -
-
${state.translate('downloadMessage')}
- - ${pageAction} - - ${state.translate('sendYourFilesLink')} - - ${info} + ${titleSection(state)} + + ${downloadedFiles(ownedFile, state, emit)} +
${state.translate('downloadMessage2')}
+ ${downloadButton(state, emit)} + + ${bottomLink} +
`; + + function cancel() { + if (state.transfer.state === 'downloading') { + emit('cancel'); + } + } }; diff --git a/app/pages/share/index.js b/app/pages/share/index.js index 5710c8ef..8d72729b 100644 --- a/app/pages/share/index.js +++ b/app/pages/share/index.js @@ -3,42 +3,48 @@ 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 uploadedFileList = 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'; + return html` -
- ${expireInfo(file, state.translate, emit)} -
+ +
+ + + + ${expireInfo(file, state.translate)} + + ${uploadedFileList(file, state, emit)} +
- ${state.translate('copyUrlFormLabelWithName', { filename: file.name })} + ${state.translate('copyUrlLabel')} +
(don't forget the password too)
-
- - -
- ${setPasswordSection(state, emit)} - +
${deletePopup( state.translate('deletePopupText'), @@ -47,43 +53,39 @@ module.exports = function(state, emit) { deleteFile )}
- ${state.translate('sendAnotherFileLink')} + + +
-
+ `; - function showPopup() { + function showDeletePopup() { const popup = document.querySelector('.popup'); popup.classList.add('popup--show'); 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 +97,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..2ad3f468 100644 --- a/app/pages/share/share.css +++ b/app/pages/share/share.css @@ -1,112 +1,81 @@ -.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--copied, +.copyBtn--copied:hover { background: var(--successControlBGColor); - border: 1px solid var(--successControlBGColor); color: var(--successControlFGColor); } .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..d69ae1b4 --- /dev/null +++ b/app/pages/signin/index.js @@ -0,0 +1,66 @@ +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)} + + + + + + + +
+ `; + + function submitEmail(event) { + event.preventDefault(); + //TODO: hook up fxA onboarding + } +}; diff --git a/app/pages/signin/signin.css b/app/pages/signin/signin.css new file mode 100644 index 00000000..85745267 --- /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: 12px 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: 8px 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..c20a9965 100644 --- a/app/pages/welcome/index.js +++ b/app/pages/welcome/index.js @@ -1,50 +1,92 @@ -/* global MAXFILESIZE */ const html = require('choo/html'); const assets = require('../../../common/assets'); -const fileList = require('../../templates/fileList'); -const { bytes, fadeOut } = 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 hasAnUpload = state.archive && state.archive.numFiles > 0; + + const optionClass = state.uploading ? 'uploadOptions--faded' : ''; + const btnUploading = state.uploading ? 'btn--stripes' : ''; + const cancelVisible = state.uploading ? '' : 'noDisplay'; + const faded = hasAnUpload ? 'uploadArea--faded' : ''; + const selectFileClass = hasAnUpload > 0 ? 'btn--hidden' : ''; + const sendFileClass = hasAnUpload > 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)} + +
`; function hide(e) { e.stopPropagation(); diff --git a/app/templates/popup/popup.css b/app/templates/popup/popup.css index 3ced13ef..548aedb6 100644 --- a/app/templates/popup/popup.css +++ b/app/templates/popup/popup.css @@ -1,122 +1,79 @@ .popup { - visibility: hidden; - min-width: 204px; - min-height: 105px; - background-color: var(--pageBGColor); + display: block; + width: 100%; + height: 70px; + background-color: var(--errorColor); color: var(--textColor); - border: 1px solid #d7d7db; - padding: 15px 24px; - box-sizing: content-box; + padding: 0; + box-sizing: border-box; text-align: center; - border-radius: 5px; - position: absolute; - z-index: 1; - bottom: 20px; - left: -40px; + border-radius: 4px; transition: opacity 0.5s; - opacity: 0; outline: 0; - box-shadow: 3px 3px 7px rgba(136, 136, 136, 0.3); + opacity: 0; + visibility: hidden; } .popup::after { content: ''; position: absolute; - bottom: -11px; - left: 20px; - background-color: #fff; - display: block; - width: 20px; - height: 20px; - transform: rotate(45deg); - border-radius: 0 0 5px; - border-right: 1px solid #d7d7db; - border-bottom: 1px solid #d7d7db; - border-left: 1px solid #fff; - border-top: 1px solid #fff; -} - -.popup__wrapper { - position: absolute; - display: inline-block; + top: 100%; + left: 50%; + width: 0; + height: 0; + border: 8px solid; + border-color: var(--errorColor) transparent transparent; + margin-left: -8px; + pointer-events: none; } .popup__message { height: 40px; - display: flex; - justify-content: center; - align-items: center; - border-bottom: 1px #ebebeb solid; - color: var(--textColor); + padding: 10px; + box-sizing: border-box; + text-align: center; + color: var(--primaryControlFGColor); font-size: 15px; - font-weight: normal; - padding-bottom: 15px; + font-style: italic; white-space: nowrap; - width: calc(100% + 48px); - margin-left: -24px; } .popup__action { - margin-top: 15px; display: flex; flex-direction: row; - align-items: center; - justify-content: space-between; + text-transform: uppercase; +} + +.popup__action > div { + flex: auto; } .popup__no { - color: #4a4a4a; - background-color: #fbfbfb; - border: 1px #c1c1c1 solid; - border-radius: 5px; - padding: 5px 25px; - font-weight: normal; - min-width: 94px; - box-sizing: border-box; + color: var(--primaryControlFGColor); + padding: 5px; + font-weight: bold; cursor: pointer; white-space: nowrap; } .popup__no:hover { - background-color: #efeff1; + text-decoration: underline; } .popup__yes { color: var(--primaryControlFGColor); - background-color: var(--primaryControlBGColor); - border-radius: 5px; - padding: 5px 25px; + padding: 5px; font-weight: normal; cursor: pointer; - min-width: 94px; - box-sizing: border-box; white-space: nowrap; - margin-left: 12px; } .popup__yes:hover { - background-color: var(--primaryControlHoverColor); + text-decoration: underline; } .popup--show { visibility: visible; opacity: 1; -} - -@media (max-device-width: 992px), (max-width: 992px) { - .popup { - left: auto; - right: -40px; - } - - .popup::after { - left: auto; - right: 36px; - } -} - -@media (max-device-width: 520px), (max-width: 520px) { - .popup::after { - left: 125px; - } + pointer-events: auto; } diff --git a/app/templates/progress/index.js b/app/templates/progress/index.js deleted file mode 100644 index 4ab9a1d3..00000000 --- a/app/templates/progress/index.js +++ /dev/null @@ -1,56 +0,0 @@ -const html = require('choo/html'); -const percent = require('../../utils').percent; - -const radius = 73; -const oRadius = radius + 10; -const oDiameter = oRadius * 2; -const circumference = 2 * Math.PI * radius; - -module.exports = function(progressRatio, indefinite = false) { - // HACK - never indefinite for MS Edge - if (/edge/i.test(navigator.userAgent)) { - indefinite = false; - } - const p = indefinite ? 0.2 : progressRatio; - const dashOffset = (1 - p) * circumference; - const progressPercent = html` - - ${percent(progressRatio)} - `; - - return html` -
- - - - - ${indefinite ? '' : progressPercent} - -
- `; -}; diff --git a/app/templates/progress/progress.css b/app/templates/progress/progress.css deleted file mode 100644 index 410b0be0..00000000 --- a/app/templates/progress/progress.css +++ /dev/null @@ -1,43 +0,0 @@ -.progress { - margin-top: 3px; -} - -.progress__bg { - stroke: #eee; - stroke-width: 0.75em; -} - -.progress__bar { - stroke: #3b9dff; - stroke-width: 0.75em; - transition: stroke-dashoffset 300ms linear; -} - -.progress__indefinite { - stroke: #3b9dff; - stroke-width: 0.75em; - animation: 1s linear infinite spin; - transform-origin: center; -} - -@keyframes spin { - from { - transform: rotate(0deg); - } - - to { - transform: rotate(360deg); - } -} - -.progress__percent { - font-family: 'Segoe UI', 'SF Pro Text', sans-serif; - font-size: 43.2px; - letter-spacing: -0.78px; - line-height: 58px; - user-select: none; -} - -.progress--invisible { - display: none; -} diff --git a/app/templates/selectbox/index.js b/app/templates/selectbox/index.js index 52ed93b9..43d2d666 100644 --- a/app/templates/selectbox/index.js +++ b/app/templates/selectbox/index.js @@ -5,16 +5,14 @@ module.exports = function(selected, options, translate, changed) { let x = selected; return html` -
- ${options.map( i => html`` )} - -
`; + `; function choose(event) { const target = event.target; diff --git a/app/templates/selectbox/selectbox.css b/app/templates/selectbox/selectbox.css index 49664978..fcbb196b 100644 --- a/app/templates/selectbox/selectbox.css +++ b/app/templates/selectbox/selectbox.css @@ -1,46 +1,22 @@ -.select { - background-color: var(--pageBGColor); - overflow: hidden; - padding: 4px 2px 4px 2px; - border: 1px dotted #0094fb88; - border-radius: 4px; - display: inline; - position: relative; -} - -.select::after { - color: #0094fb; - content: '\25BC'; - pointer-events: none; - font-size: 20px; - margin-left: -30px; - padding-right: 10px; -} - option { padding: 0; } -select { +.selectBox { appearance: none; outline: 0; box-shadow: none; - border: 0; - background: #fff; - background-image: none; + border: none; + border-radius: 0; + background-color: #e6e6e6; font-size: 1em; font-weight: 200; margin: 0; - color: #0094fb; + padding: 4px 2px 4px 2px; cursor: pointer; - padding-right: 40px; } select:active { background-color: var(--pageBGColor); border: 0; } - -#arrow { - position: relative; -} diff --git a/app/templates/setPasswordSection/index.js b/app/templates/setPasswordSection/index.js index 16130712..8fad4337 100644 --- a/app/templates/setPasswordSection/index.js +++ b/app/templates/setPasswordSection/index.js @@ -1,25 +1,26 @@ const html = require('choo/html'); const passwordInput = require('../passwordInput'); -module.exports = function(state, emit) { - const file = state.storage.getFileById(state.params.id); +module.exports = function(state) { + const checked = state.password ? 'checked' : ''; + const label = state.password ? 'addPasswordLabel' : 'addPasswordMessage'; return html`
- ${passwordInput(file, state, emit)} + + ${passwordInput(state)} +
`; function togglePasswordInput(e) { @@ -28,9 +29,13 @@ module.exports = function(state, emit) { document .querySelector('.passwordInput') .classList.toggle('passwordInput--hidden', !boxChecked); + + const label = document.querySelector('.checkbox__label'); if (boxChecked) { + label.innerHTML = state.translate('addPasswordLabel'); unlockInput.focus(); } else { + label.innerHTML = state.translate('addPasswordMessage'); unlockInput.value = ''; } } diff --git a/app/templates/setPasswordSection/setPasswordSection.css b/app/templates/setPasswordSection/setPasswordSection.css index bf3adb75..28233cfe 100644 --- a/app/templates/setPasswordSection/setPasswordSection.css +++ b/app/templates/setPasswordSection/setPasswordSection.css @@ -1,11 +1,12 @@ .setPasswordSection { + display: flex; padding: 10px 0; max-width: 100%; - overflow-wrap: break-word; } .checkbox { - min-height: 24px; + flex: auto; + height: 24px; } .checkbox__input { @@ -14,7 +15,8 @@ } .checkbox__label { - line-height: 23px; + font-size: 13px; + line-height: 20px; cursor: pointer; color: var(--lightTextColor); user-select: none; @@ -22,27 +24,21 @@ .checkbox__label::before { content: ''; - height: 20px; - width: 20px; + height: 24px; + width: 24px; margin-right: 10px; - margin-left: 5px; float: left; - border: 1px solid rgba(12, 12, 13, 0.3); - border-radius: 2px; + background-color: #e6e6e6; } -.checkbox__input:focus + .checkbox__label::before, -.checkbox:hover .checkbox__label::before { - border: 1px solid var(--primaryControlBGColor); -} - -.checkbox__input:checked + .checkbox__label { - color: var(--textColor); +.checkbox__label:hover::before { + background-color: #d6d6d6; } .checkbox__input:checked + .checkbox__label::before { - background-image: url('../assets/check-16-blue.svg'); - background-position: 2px 1px; + background-image: url('../assets/lock.svg'); + background-position: 2px 2px; + background-repeat: no-repeat; } .checkbox__input:disabled + .checkbox__label { @@ -50,20 +46,13 @@ } .checkbox__input:disabled + .checkbox__label::before { - background-image: url('../assets/check-16-blue.svg'); + background-image: url('../assets/lock.svg'); background-repeat: no-repeat; background-size: 26px 26px; border: none; cursor: auto; } -@media (max-device-width: 520px), (max-width: 520px) { - .setPasswordSection { - align-self: center; - min-width: 95%; - } - - .checkbox__label::before { - margin-left: 0; - } +.setPasswordSection > .passwordInput--hidden { + display: none; } diff --git a/app/templates/signupPromo/index.js b/app/templates/signupPromo/index.js new file mode 100644 index 00000000..376b5ba0 --- /dev/null +++ b/app/templates/signupPromo/index.js @@ -0,0 +1,15 @@ +const html = require('choo/html'); + +module.exports = function(state) { + return html` +
+
${state.translate('signInPromoText')}
+
${state.translate('signInExplanation')}
+ + ${state.translate('signInLearnMore')} + +
+ `; +}; 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..40c8345d --- /dev/null +++ b/app/templates/uploadedFile/index.js @@ -0,0 +1,51 @@ +const html = require('choo/html'); +const assets = require('../../../common/assets'); +const bytes = require('../../utils').bytes; +const fileIcon = require('../fileIcon'); + +module.exports = function(file, index, state, emit, hasPassword) { + const transfer = state.transfer; + const transferState = transfer ? transfer.state : null; + const share = state.route.includes('share/'); + const complete = share ? 'uploadedFile--completed' : ''; + + const cancelVisible = + state.route === '/' && !state.uploading + ? 'uploadedFile__cancel--visible' + : ''; + + const stampClass = + share || transferState === 'complete' ? 'uploadedFile__stamp--visible' : ''; + + function cancel(event) { + event.preventDefault(); + if (state.route === '/') { + emit('removeUpload', { index }); + } + } + + return html` +
  • + + ${fileIcon(file.name, 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..1175af02 --- /dev/null +++ b/app/templates/uploadedFileList/index.js @@ -0,0 +1,15 @@ +const html = require('choo/html'); +const file = require('../uploadedFile'); + +module.exports = function(archive, state, emit) { + let files = []; + if (archive) { + files = Array.from(archive.manifest.files); + } + + return html` +
      + ${files.map((f, i) => file(f, i, state, emit, archive._hasPassword))} +
    + `; +}; 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..9a63fd5e --- /dev/null +++ b/app/templates/userAccount/index.js @@ -0,0 +1,48 @@ +const html = require('choo/html'); +const assets = require('../../../common/assets'); + +// eslint-disable-next-line no-unused-vars +module.exports = function(state) { + const notLoggedInMenu = html` + + `; + + return html` + `; + + function avatarClick(event) { + event.preventDefault(); + const dropdown = document.querySelector('.account_dropdown'); + dropdown.classList.toggle('visible'); + dropdown.focus(); + } + + //the onblur trick makes links unclickable wtf + /* + function hideMenu(event) { + event.stopPropagation(); + const dropdown = document.querySelector('.account_dropdown'); + dropdown.classList.remove('visible'); + } + */ +}; diff --git a/app/templates/userAccount/userAccount.css b/app/templates/userAccount/userAccount.css new file mode 100644 index 00000000..fa532684 --- /dev/null +++ b/app/templates/userAccount/userAccount.css @@ -0,0 +1,64 @@ +.account { + position: absolute; + right: 0; + margin: 0 21px; + padding: 0; +} + +.account_dropdown { + z-index: 2; + position: absolute; + top: 30px; + left: -15px; + width: 150px; + list-style-type: none; + border: 1px solid #ccc; + border-radius: 4px; + background-color: var(--pageBGColor); + box-shadow: 0 5px 12px 0 rgba(0, 0, 0, 0.2); + padding: 11px 0; + visibility: hidden; + outline: 0; +} + +.account_dropdown::after, +.account_dropdown::before { + position: absolute; + bottom: 100%; + left: 18px; + height: 0; + width: 0; + border: 1px solid transparent; + content: ''; + pointer-events: none; +} + +.account_dropdown::after { + border-bottom-color: var(--pageBGColor); + border-width: 12px; +} + +.account_dropdown::before { + border-bottom-color: #ccc; + border-width: 13px; + margin-left: -1px; +} + +.account_dropdown__link { + display: block; + padding: 0 14px; + font-size: 13px; + line-height: 24px; + color: var(--lightTextColor); + position: relative; + z-index: 999; +} + +.account_dropdown__link:visited { + color: var(--lightTextColor); +} + +.account_dropdown__link:hover { + background-color: var(--primaryControlBGColor); + color: var(--primaryControlFGColor); +} diff --git a/app/utils.js b/app/utils.js index 8baa6617..8e5e5302 100644 --- a/app/utils.js +++ b/app/utils.js @@ -128,6 +128,29 @@ function openLinksInNewTab(links, should = true) { return links; } +function browserName() { + try { + if (/firefox/i.test(navigator.userAgent)) { + return 'firefox'; + } + if (/edge/i.test(navigator.userAgent)) { + return 'edge'; + } + if (/trident/i.test(navigator.userAgent)) { + return 'ie'; + } + if (/chrome/i.test(navigator.userAgent)) { + return 'chrome'; + } + if (/safari/i.test(navigator.userAgent)) { + return 'safari'; + } + return 'other'; + } catch (e) { + return 'unknown'; + } +} + module.exports = { fadeOut, delay, @@ -140,5 +163,6 @@ module.exports = { b64ToArray, loadShim, isFile, - openLinksInNewTab + openLinksInNewTab, + browserName }; 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/background_1.jpg b/assets/background_1.jpg index c92d3fb1..1742afeb 100644 Binary files a/assets/background_1.jpg and b/assets/background_1.jpg differ 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/dropdown-arrow.svg b/assets/dropdown-arrow.svg new file mode 100644 index 00000000..b09cfc97 --- /dev/null +++ b/assets/dropdown-arrow.svg @@ -0,0 +1,4 @@ + + \ 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/package-lock.json b/package-lock.json index 9f15f32d..b9a1e794 100644 --- a/package-lock.json +++ b/package-lock.json @@ -921,9 +921,15 @@ } }, "aws-sdk": { +<<<<<<< HEAD "version": "2.285.1", "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.285.1.tgz", "integrity": "sha512-lkroCYcnb7UWR/jbaW6wyjAeGROrsBFWyqUukQjICuCV4a0Mapnjsxefl2A/z+0SX3gnBN7owUb/60UjQSHpzA==", +======= + "version": "2.283.1", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.283.1.tgz", + "integrity": "sha512-UZmiWboO0WgZUKcTDSa5v6xuHqfNr9PQrOoilWUBnWpWO4s6h9LlSvIm06qyA3XQjEpXVmetHAMl9GmiHk51qw==", +>>>>>>> add fxA ui elements "requires": { "buffer": "4.9.1", "events": "1.1.1", @@ -2321,6 +2327,7 @@ "resolved": "https://registry.npmjs.org/choo/-/choo-6.13.0.tgz", "integrity": "sha512-OsXC4v8zKcGAJ+C3fTVxU30daZIWFcQwTZlHKWzHzZvEaRaaBGF95jqTd3XYV5+Eitx/SaklbqtaoWUAKCG/Nw==", "requires": { +<<<<<<< HEAD "document-ready": "^2.0.1", "nanoassert": "^1.1.0", "nanobus": "^4.2.0", @@ -2335,6 +2342,22 @@ "nanotiming": "^7.0.0", "scroll-to-anchor": "^1.0.0", "xtend": "^4.0.1" +======= + "document-ready": "2.0.1", + "nanoassert": "1.1.0", + "nanobus": "4.3.3", + "nanocomponent": "6.5.2", + "nanohref": "3.0.3", + "nanohtml": "1.2.4", + "nanolru": "1.0.0", + "nanomorph": "5.1.3", + "nanoquery": "1.2.0", + "nanoraf": "3.1.0", + "nanorouter": "3.1.1", + "nanotiming": "7.3.1", + "scroll-to-anchor": "1.1.0", + "xtend": "4.0.1" +>>>>>>> add fxA ui elements } }, "chownr": { @@ -4336,7 +4359,7 @@ }, "event-stream": { "version": "3.3.4", - "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", "dev": true, "requires": { @@ -7379,7 +7402,7 @@ }, "onetime": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", "dev": true }, @@ -7584,7 +7607,7 @@ }, "onetime": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", "dev": true }, @@ -7781,8 +7804,13 @@ "integrity": "sha1-zbX4TitqLTEU3zO9BdnLMuPECDo=", "dev": true, "requires": { +<<<<<<< HEAD "unist-util-modify-children": "^1.0.0", "unist-util-visit": "^1.1.0" +======= + "unist-util-modify-children": "1.1.2", + "unist-util-visit": "1.4.0" +>>>>>>> add fxA ui elements } }, "mdn-data": { @@ -8254,9 +8282,12 @@ } }, "nanoraf": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/nanoraf/-/nanoraf-3.0.1.tgz", - "integrity": "sha1-q5+5wle5rcxx2CmCy1jY+jUDdko=" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nanoraf/-/nanoraf-3.1.0.tgz", + "integrity": "sha512-7Emv5Pv/fvgVK6yrud93WsdO4d3AUqLoP38Cpn0chYe+tT/wu25Yl2guxBjE3ngRrI5Yd9DxaTCgCFi1uq7hgQ==", + "requires": { + "nanoassert": "1.1.0" + } }, "nanorouter": { "version": "3.1.1", @@ -8415,6 +8446,24 @@ "version": "1.0.0-alpha.10", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.0.0-alpha.10.tgz", "integrity": "sha512-BSQrRgOfN6L/MoKIa7pRUc7dHvflCXMcqyTBvphixcSsgJTuUd24vAFONuNfVsuwTyz28S1HEc9XN6ZKylk4Hg==", +<<<<<<< HEAD +======= + "dev": true, + "requires": { + "semver": "5.5.0" + } + }, + "nodesecurity-npm-utils": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nodesecurity-npm-utils/-/nodesecurity-npm-utils-6.0.0.tgz", + "integrity": "sha512-NLRle1woNaT2orR6fue2jNqkhxDTktgJj3sZxvR/8kp21pvOY7Gwlx5wvo0H8ZVPqdgd2nE2ADB9wDu5Cl8zNg==", + "dev": true + }, + "nomnom": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.2.tgz", + "integrity": "sha1-hKZqJgF0QI/Ft3oY+IjszET7aXE=", +>>>>>>> add fxA ui elements "dev": true, "requires": { "semver": "^5.3.0" @@ -10902,7 +10951,7 @@ }, "onetime": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", "dev": true }, @@ -11723,10 +11772,17 @@ "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "dev": true, "requires": { +<<<<<<< HEAD "chalk": "^1.1.3", "js-base64": "^2.1.9", "source-map": "^0.5.6", "supports-color": "^3.2.3" +======= + "chalk": "1.1.3", + "js-base64": "2.4.8", + "source-map": "0.5.7", + "supports-color": "3.2.3" +>>>>>>> add fxA ui elements } }, "supports-color": { @@ -11948,7 +12004,11 @@ "integrity": "sha512-xZsFA3uX8MO3yAda03QrG3/Eg1LN3EPfjjf07vke/46HERLZyHrTsQ9E1r1w1W//fWEhtYNndo2hQplN2cVpCQ==", "dev": true, "requires": { +<<<<<<< HEAD "postcss": "^7.0.0" +======= + "postcss": "7.0.2" +>>>>>>> add fxA ui elements }, "dependencies": { "ansi-styles": { @@ -11957,7 +12017,11 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { +<<<<<<< HEAD "color-convert": "^1.9.0" +======= + "color-convert": "1.9.2" +>>>>>>> add fxA ui elements } }, "chalk": { @@ -11966,9 +12030,15 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { +<<<<<<< HEAD "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" +======= + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" +>>>>>>> add fxA ui elements } }, "postcss": { @@ -11977,9 +12047,15 @@ "integrity": "sha512-fmaUY5370keLUTx+CnwRxtGiuFTcNBLQBqr1oE3WZ/euIYmGAo0OAgOhVJ3ByDnVmOR3PK+0V9VebzfjRIUcqw==", "dev": true, "requires": { +<<<<<<< HEAD "chalk": "^2.4.1", "source-map": "^0.6.1", "supports-color": "^5.4.0" +======= + "chalk": "2.4.1", + "source-map": "0.6.1", + "supports-color": "5.4.0" +>>>>>>> add fxA ui elements } }, "source-map": { @@ -11994,7 +12070,11 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { +<<<<<<< HEAD "has-flag": "^3.0.0" +======= + "has-flag": "3.0.0" +>>>>>>> add fxA ui elements } } } @@ -12063,7 +12143,11 @@ "integrity": "sha512-um9zdGKaDZirMm+kZFKKVsnKPF7zF7qBAtIfTSnZXD1jZ0JNZIxdB6TxQOjCnlSzLRInVl2v3YdBh/M881C4ug==", "dev": true, "requires": { +<<<<<<< HEAD "postcss": "^7.0.0" +======= + "postcss": "7.0.2" +>>>>>>> add fxA ui elements }, "dependencies": { "ansi-styles": { @@ -12072,7 +12156,11 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { +<<<<<<< HEAD "color-convert": "^1.9.0" +======= + "color-convert": "1.9.2" +>>>>>>> add fxA ui elements } }, "chalk": { @@ -12081,9 +12169,15 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { +<<<<<<< HEAD "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" +======= + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" +>>>>>>> add fxA ui elements } }, "postcss": { @@ -12092,9 +12186,15 @@ "integrity": "sha512-fmaUY5370keLUTx+CnwRxtGiuFTcNBLQBqr1oE3WZ/euIYmGAo0OAgOhVJ3ByDnVmOR3PK+0V9VebzfjRIUcqw==", "dev": true, "requires": { +<<<<<<< HEAD "chalk": "^2.4.1", "source-map": "^0.6.1", "supports-color": "^5.4.0" +======= + "chalk": "2.4.1", + "source-map": "0.6.1", + "supports-color": "5.4.0" +>>>>>>> add fxA ui elements } }, "source-map": { @@ -12109,7 +12209,11 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { +<<<<<<< HEAD "has-flag": "^3.0.0" +======= + "has-flag": "3.0.0" +>>>>>>> add fxA ui elements } } } @@ -13965,6 +14069,7 @@ "integrity": "sha512-pcw0Dpb4Ib/OfgONhaeF+myA+5iZdsI8dYgWs1++IYN/dgvo90O0FhgMDKb1bMgZVy/A2Q1CCN/PFZ0FLnnRnQ==", "dev": true, "requires": { +<<<<<<< HEAD "autoprefixer": "^9.0.0", "balanced-match": "^1.0.0", "chalk": "^2.4.1", @@ -14009,6 +14114,52 @@ "sugarss": "^1.0.0", "svg-tags": "^1.0.0", "table": "^4.0.1" +======= + "autoprefixer": "9.0.2", + "balanced-match": "1.0.0", + "chalk": "2.4.1", + "cosmiconfig": "5.0.5", + "debug": "3.1.0", + "execall": "1.0.0", + "file-entry-cache": "2.0.0", + "get-stdin": "6.0.0", + "globby": "8.0.1", + "globjoin": "0.1.4", + "html-tags": "2.0.0", + "ignore": "4.0.2", + "import-lazy": "3.1.0", + "imurmurhash": "0.1.4", + "known-css-properties": "0.6.1", + "lodash": "4.17.10", + "log-symbols": "2.2.0", + "mathml-tag-names": "2.1.0", + "meow": "5.0.0", + "micromatch": "2.3.11", + "normalize-selector": "0.2.0", + "pify": "3.0.0", + "postcss": "7.0.2", + "postcss-html": "0.31.0", + "postcss-less": "2.0.0", + "postcss-markdown": "0.31.0", + "postcss-media-query-parser": "0.2.3", + "postcss-reporter": "5.0.0", + "postcss-resolve-nested-selector": "0.1.1", + "postcss-safe-parser": "4.0.1", + "postcss-sass": "0.3.2", + "postcss-scss": "2.0.0", + "postcss-selector-parser": "3.1.1", + "postcss-styled": "0.31.0", + "postcss-syntax": "0.31.0", + "postcss-value-parser": "3.3.0", + "resolve-from": "4.0.0", + "signal-exit": "3.0.2", + "specificity": "0.4.0", + "string-width": "2.1.1", + "style-search": "0.1.0", + "sugarss": "1.0.1", + "svg-tags": "1.0.0", + "table": "4.0.2" +>>>>>>> add fxA ui elements }, "dependencies": { "ansi-styles": { @@ -14041,12 +14192,21 @@ "integrity": "sha512-t5PpCq5nCNzgPEzhty83UHYLmteY9FTL3COBfRjL0y4BTDB0OADbHVzG/S7gzqvITSsAZiaJPduoDEv2n68JNQ==", "dev": true, "requires": { +<<<<<<< HEAD "browserslist": "^4.0.1", "caniuse-lite": "^1.0.30000865", "normalize-range": "^0.1.2", "num2fraction": "^1.2.2", "postcss": "^7.0.2", "postcss-value-parser": "^3.2.3" +======= + "browserslist": "4.0.1", + "caniuse-lite": "1.0.30000865", + "normalize-range": "0.1.2", + "num2fraction": "1.2.2", + "postcss": "7.0.2", + "postcss-value-parser": "3.3.0" +>>>>>>> add fxA ui elements } }, "braces": { @@ -14071,6 +14231,17 @@ "node-releases": "^1.0.0-alpha.10" } }, + "browserslist": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.0.1.tgz", + "integrity": "sha512-QqiiIWchEIkney3wY53/huI7ZErouNAdvOkjorUALAwRcu3tEwOV3Sh6He0DnP38mz1JjBpCBb50jQBmaYuHPw==", + "dev": true, + "requires": { + "caniuse-lite": "1.0.30000865", + "electron-to-chromium": "1.3.52", + "node-releases": "1.0.0-alpha.10" + } + }, "camelcase-keys": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", @@ -14132,6 +14303,7 @@ "integrity": "sha512-oMrYrJERnKBLXNLVTqhm3vPEdJ/b2ZE28xN4YARiix1NOIOBPEpOUnm844K1iu/BkphCaf2WNFwMszv8Soi1pw==", "dev": true, "requires": { +<<<<<<< HEAD "array-union": "^1.0.1", "dir-glob": "^2.0.0", "fast-glob": "^2.0.2", @@ -14139,6 +14311,15 @@ "ignore": "^3.3.5", "pify": "^3.0.0", "slash": "^1.0.0" +======= + "array-union": "1.0.2", + "dir-glob": "2.0.0", + "fast-glob": "2.2.2", + "glob": "7.1.2", + "ignore": "3.3.10", + "pify": "3.0.0", + "slash": "1.0.0" +>>>>>>> add fxA ui elements }, "dependencies": { "ignore": { @@ -14150,9 +14331,15 @@ } }, "ignore": { +<<<<<<< HEAD "version": "4.0.3", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.3.tgz", "integrity": "sha512-Z/vAH2GGIEATQnBVXMclE2IGV6i0GyVngKThcGZ5kHgHMxLo9Ow2+XHRq1aEKEej5vOF1TPJNbvX6J/anT0M7A==", +======= + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.2.tgz", + "integrity": "sha512-uoxnT7PYpyEnsja+yX+7v49B7LXxmzDJ2JALqHH3oEGzpM2U1IGcbfnOr8Dt57z3B/UWs7/iAgPFbmye8m4I0g==", +>>>>>>> add fxA ui elements "dev": true }, "indent-string": { @@ -14215,6 +14402,7 @@ "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", "dev": true, "requires": { +<<<<<<< HEAD "camelcase-keys": "^4.0.0", "decamelize-keys": "^1.0.0", "loud-rejection": "^1.0.0", @@ -14224,6 +14412,17 @@ "redent": "^2.0.0", "trim-newlines": "^2.0.0", "yargs-parser": "^10.0.0" +======= + "camelcase-keys": "4.2.0", + "decamelize-keys": "1.1.0", + "loud-rejection": "1.6.0", + "minimist-options": "3.0.2", + "normalize-package-data": "2.4.0", + "read-pkg-up": "3.0.0", + "redent": "2.0.0", + "trim-newlines": "2.0.0", + "yargs-parser": "10.1.0" +>>>>>>> add fxA ui elements } }, "micromatch": { @@ -14277,6 +14476,17 @@ "supports-color": "^5.4.0" } }, + "postcss": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.2.tgz", + "integrity": "sha512-fmaUY5370keLUTx+CnwRxtGiuFTcNBLQBqr1oE3WZ/euIYmGAo0OAgOhVJ3ByDnVmOR3PK+0V9VebzfjRIUcqw==", + "dev": true, + "requires": { + "chalk": "2.4.1", + "source-map": "0.6.1", + "supports-color": "5.4.0" + } + }, "postcss-selector-parser": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", @@ -14894,12 +15104,21 @@ "integrity": "sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA==", "dev": true, "requires": { +<<<<<<< HEAD "bail": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^1.1.0", "trough": "^1.0.0", "vfile": "^2.0.0", "x-is-string": "^0.1.0" +======= + "bail": "1.0.3", + "extend": "3.0.2", + "is-plain-obj": "1.1.0", + "trough": "1.0.2", + "vfile": "2.3.0", + "x-is-string": "0.1.0" +>>>>>>> add fxA ui elements } }, "union-value": { @@ -14991,7 +15210,11 @@ "integrity": "sha512-XxoNOBvq1WXRKXxgnSYbtCF76TJrRoe5++pD4cCBsssSiWSnPEktyFrFLE8LTk3JW5mt9hB0Sk5zn4x/JeWY7Q==", "dev": true, "requires": { +<<<<<<< HEAD "unist-util-visit": "^1.1.0" +======= + "unist-util-visit": "1.4.0" +>>>>>>> add fxA ui elements } }, "unist-util-stringify-position": { @@ -15006,7 +15229,11 @@ "integrity": "sha512-FiGu34ziNsZA3ZUteZxSFaczIjGmksfSgdKqBfOejrrfzyUy5b7YrlzT1Bcvi+djkYDituJDy2XB7tGTeBieKw==", "dev": true, "requires": { +<<<<<<< HEAD "unist-util-visit-parents": "^2.0.0" +======= + "unist-util-visit-parents": "2.0.1" +>>>>>>> add fxA ui elements } }, "unist-util-visit-parents": { diff --git a/package.json b/package.json index e95ab131..69c5ac40 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "@mattiasbuelens/web-streams-polyfill": "0.1.0-alpha.5", "asmcrypto.js": "^2.3.2", "babel-core": "^6.26.3", - "babel-loader": "^7.1.4", + "babel-loader": "^7.1.5", "babel-plugin-istanbul": "^4.1.6", "babel-plugin-yo-yoify": "^2.0.0", "babel-preset-env": "^1.7.0", diff --git a/public/locales/en-US/send.ftl b/public/locales/en-US/send.ftl index 60bd9e8c..731a2fac 100644 --- a/public/locales/en-US/send.ftl +++ b/public/locales/en-US/send.ftl @@ -20,12 +20,14 @@ notifyUploadDone = Your upload has finished. uploadingPageMessage = Once your file uploads you will be able to set expiry options. uploadingPageCancel = Cancel upload uploadCancelNotification = Your upload was cancelled. +downloadCancel = Cancel download uploadingPageLargeFileMessage = This file is large and may take a while to upload. Sit tight! uploadingFileNotification = Notify me when the upload is complete. 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 +36,18 @@ timespanHours = { $num -> [one] 1 hour *[other] { $num } hours } +timespanMinutes = { $num -> + [one] 1 minute + *[other] { $num } minutes + } +timespanWeeks = { $num -> + [one] 1 week + *[other] { $num } weeks + } +fileCount = { $num -> + [one] 1 file + *[other] { $num } files +} copyUrlFormLabelWithName = Copy and share the link to send your file: { $filename } copyUrlFormButton = Copy to clipboard copiedUrl = Copied! @@ -98,6 +112,7 @@ footerLinkLegal = Legal footerLinkAbout = About Test Pilot footerLinkPrivacy = Privacy footerLinkTerms = Terms +footerLinkPrivacyAndTerms = Privacy & Terms footerLinkCookies = Cookies requirePasswordCheckbox = Require a password to download this file addPasswordButton = Add password @@ -117,3 +132,32 @@ 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 +signInMenuOption = Sign in/up +accountMenuOption = Firefox Account +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 } }) ) )