diff --git a/app/base.css b/app/base.css new file mode 100644 index 000000000..b7230147d --- /dev/null +++ b/app/base.css @@ -0,0 +1,249 @@ +:root { + --pageBGColor: #fff; + --primaryControlBGColor: #0297f8; + --primaryControlFGColor: #fff; + --primaryControlHoverColor: #0287e8; + --inputTextColor: #737373; + --errorColor: #d70022; + --linkColor: #0094fb; + --textColor: #0c0c0d; + --lightTextColor: #737373; + --successControlBGColor: #05a700; + --successControlFGColor: #fff; +} + +html { + background: url('../assets/send_bg.svg'); + font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'segoe ui', + 'helvetica neue', helvetica, ubuntu, roboto, noto, arial, sans-serif; + font-weight: 200; + background-size: 110%; + background-repeat: no-repeat; + background-position: center top; + height: 100%; + margin: auto; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'segoe ui', + 'helvetica neue', helvetica, ubuntu, roboto, noto, arial, sans-serif; + display: flex; + flex-direction: column; + margin: 0; + min-height: 100vh; + position: relative; +} + +input, +select, +textarea, +button { + font-family: inherit; + margin: 0; +} + +a { + text-decoration: none; +} + +.all { + flex: 1; + display: flex; + flex-direction: column; + justify-content: flex-start; + max-width: 650px; + margin: 0 auto; + padding: 0 20px; + box-sizing: border-box; + width: 96%; +} + +.btn { + font-size: 15px; + font-weight: 500; + color: var(--primaryControlFGColor); + cursor: pointer; + text-align: center; + background: var(--primaryControlBGColor); + border: 1px solid var(--primaryControlBGColor); + border-radius: 5px; +} + +.btn--cancel { + color: var(--errorColor); + background: var(--pageBGColor); + font-size: 15px; + border: 0; + cursor: pointer; + text-decoration: underline; +} + +.btn--cancel:disabled { + text-decoration: none; + cursor: auto; +} + +.input { + border: 1px solid var(--primaryControlBGColor); + border-radius: 6px 0 0 6px; + 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--noBtn { + border-radius: 6px; +} + +.inputBtn { + background: var(--primaryControlBGColor); + border-radius: 0 6px 6px 0; + border: 1px solid var(--primaryControlBGColor); + color: var(--primaryControlFGColor); + cursor: pointer; + + /* Force flat button look */ + 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; +} + +.link:focus, +.link:active, +.link:hover { + color: var(--primaryControlHoverColor); +} + +.link--action { + text-decoration: underline; + text-align: center; +} + +.page { + margin: 0 auto 30px; + 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; +} + +@keyframes fadeout { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + } +} + +.effect--fadeIn { + opacity: 1; + animation: fadein 200ms linear; +} + +@keyframes fadein { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +.title { + font-size: 33px; + line-height: 40px; + margin: 20px auto; + text-align: center; + max-width: 520px; + font-family: 'SF Pro Text', sans-serif; + 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%; +} + +@media (max-device-width: 768px), (max-width: 768px) { + .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; + } + + .inputBtn { + border-radius: 0 0 6px 6px; + flex: 0 1 65px; + } + + .input--noBtn { + border-radius: 6px; + } +} diff --git a/app/fileManager.js b/app/fileManager.js index 26da0fc6b..b8892272a 100644 --- a/app/fileManager.js +++ b/app/fileManager.js @@ -108,7 +108,7 @@ export default function(state, emitter) { document.getElementById('cancel-upload').hidden = 'hidden'; await delay(1000); - await fadeOut('upload-progress'); + await fadeOut('.page'); openLinksInNewTab(links, false); emitter.emit('pushState', `/share/${ownedFile.id}`); } catch (err) { @@ -130,9 +130,13 @@ export default function(state, emitter) { emitter.on('password', async ({ password, file }) => { try { + state.settingPassword = true; + render(); await file.setPassword(password); state.storage.writeFile(file); metrics.addedPassword({ size: file.size }); + await delay(1000); + state.settingPassword = false; } catch (err) { console.error(err); } @@ -170,7 +174,7 @@ export default function(state, emitter) { const time = Date.now() - start; const speed = size / (time / 1000); await delay(1000); - await fadeOut('download-progress'); + await fadeOut('.page'); saveFile(f); state.storage.totalDownloads += 1; state.transfer.reset(); diff --git a/app/main.css b/app/main.css new file mode 100644 index 000000000..814595687 --- /dev/null +++ b/app/main.css @@ -0,0 +1,18 @@ +@import './base.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'; +@import './templates/footer/footer.css'; +@import './templates/fxPromo/fxPromo.css'; +@import './templates/selectbox/selectbox.css'; +@import './templates/fileList/fileList.css'; +@import './templates/file/file.css'; +@import './templates/popup/popup.css'; +@import './pages/welcome/welcome.css'; +@import './pages/preview/preview.css'; +@import './pages/share/share.css'; +@import './pages/notFound/notFound.css'; +@import './pages/unsupported/unsupported.css'; diff --git a/app/pages/blank.js b/app/pages/blank.js index ec104ac33..edaadcac2 100644 --- a/app/pages/blank.js +++ b/app/pages/blank.js @@ -1,6 +1,5 @@ const html = require('choo/html'); module.exports = function() { - const div = html`
`; - return div; + return html`
`; }; diff --git a/app/pages/completed.js b/app/pages/completed.js deleted file mode 100644 index fa1fb896d..000000000 --- a/app/pages/completed.js +++ /dev/null @@ -1,34 +0,0 @@ -const html = require('choo/html'); -const progress = require('../templates/progress'); -const { fadeOut } = require('../utils'); - -module.exports = function(state, emit) { - const div = html` -
-
-
-
- ${state.translate('downloadFinish')} -
-
- ${progress(1)} -
-
-
-
- ${state.translate('sendYourFilesLink')} -
-
- `; - - async function sendNew(e) { - e.preventDefault(); - await fadeOut('download'); - emit('pushState', '/'); - } - - return div; -}; diff --git a/app/pages/completed/index.js b/app/pages/completed/index.js new file mode 100644 index 000000000..8d60dd472 --- /dev/null +++ b/app/pages/completed/index.js @@ -0,0 +1,26 @@ +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.js b/app/pages/download.js deleted file mode 100644 index 4a415f17b..000000000 --- a/app/pages/download.js +++ /dev/null @@ -1,46 +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` - `; - - const div = html` -
-
-
-
- ${state.translate('downloadingPageProgress', { - filename: state.fileInfo.name, - size: bytes(state.fileInfo.size) - })} -
-
- ${state.translate('downloadingPageMessage')} -
- ${progress(transfer.progressRatio)} -
-
- ${state.translate(transfer.msg, transfer.sizes)} -
- ${transfer.state === 'downloading' ? cancelBtn : null} -
-
-
-
- `; - - function cancel() { - const btn = document.getElementById('cancel-upload'); - btn.remove(); - emit('cancel'); - } - return div; -}; diff --git a/app/pages/download/index.js b/app/pages/download/index.js new file mode 100644 index 000000000..149eca105 --- /dev/null +++ b/app/pages/download/index.js @@ -0,0 +1,42 @@ +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)} +
+
+ ${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/error.css b/app/pages/error/error.css new file mode 100644 index 000000000..2bf8739cd --- /dev/null +++ b/app/pages/error/error.css @@ -0,0 +1,11 @@ +.errorPage { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + text-align: center; +} + +.errorPage__img { + margin: 51px 0 71px; +} diff --git a/app/pages/error.js b/app/pages/error/index.js similarity index 51% rename from app/pages/error.js rename to app/pages/error/index.js index f7751faa7..214467894 100644 --- a/app/pages/error.js +++ b/app/pages/error/index.js @@ -1,10 +1,10 @@ const html = require('choo/html'); -const assets = require('../../common/assets'); +const assets = require('../../../common/assets'); module.exports = function(state) { return html` -
+
${state.translate('errorPageHeader')}
- +
`; }; diff --git a/app/pages/legal.js b/app/pages/legal.js index 3b92b4d1c..b140a78bd 100644 --- a/app/pages/legal.js +++ b/app/pages/legal.js @@ -1,35 +1,32 @@ const html = require('choo/html'); const raw = require('choo/html/raw'); -function replaceLinks(str, urls) { - let i = -1; - const s = str.replace(/([^<]+)<\/a>/g, (m, v) => { - i++; - return `${v}`; - }); - return `
${s}
`; -} - module.exports = function(state) { - const div = html` -
- + return html` + `; - return div; }; + +function replaceLinks(str, urls) { + let i = 0; + const s = str.replace( + /([^<]+)<\/a>/g, + (m, v) => `${v}` + ); + return `
${s}
`; +} diff --git a/app/pages/notFound.js b/app/pages/notFound/index.js similarity index 56% rename from app/pages/notFound.js rename to app/pages/notFound/index.js index 495b8b082..f12bbe374 100644 --- a/app/pages/notFound.js +++ b/app/pages/notFound/index.js @@ -1,21 +1,18 @@ const html = require('choo/html'); -const assets = require('../../common/assets'); +const assets = require('../../../common/assets'); module.exports = function(state) { - const div = html` -
-
+ return html` +
${state.translate('expiredPageHeader')}
- `; - return div; +
`; }; diff --git a/app/pages/notFound/notFound.css b/app/pages/notFound/notFound.css new file mode 100644 index 000000000..07bf0e151 --- /dev/null +++ b/app/pages/notFound/notFound.css @@ -0,0 +1,17 @@ +.notFoundPage { + margin: 0 auto 30px; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + text-align: center; +} + +.notFoundPage__img { + margin: 0 auto; + display: flex; + justify-content: center; + flex-direction: column; + width: 100%; + max-width: 640px; +} diff --git a/app/pages/preview.js b/app/pages/preview.js deleted file mode 100644 index 84e54f250..000000000 --- a/app/pages/preview.js +++ /dev/null @@ -1,44 +0,0 @@ -const html = require('choo/html'); -const assets = require('../../common/assets'); -const { bytes } = require('../utils'); - -module.exports = function(state, pageAction) { - const fileInfo = state.fileInfo; - - const size = fileInfo.size - ? state.translate('downloadFileSize', { size: bytes(fileInfo.size) }) - : ''; - - const title = fileInfo.name - ? state.translate('downloadFileName', { filename: fileInfo.name }) - : state.translate('downloadFileTitle'); - - const info = html` -
`; - if (!pageAction) { - return info; - } - const div = html` -
-
-
-
- ${title} - ${' ' + size} -
-
${state.translate('downloadMessage')}
- - ${pageAction} -
- ${state.translate('sendYourFilesLink')} -
- ${info} -
- `; - return div; -}; diff --git a/app/pages/preview/index.js b/app/pages/preview/index.js new file mode 100644 index 000000000..3cc306e68 --- /dev/null +++ b/app/pages/preview/index.js @@ -0,0 +1,41 @@ +const html = require('choo/html'); +const assets = require('../../../common/assets'); +const { bytes } = require('../../utils'); + +module.exports = function(state, pageAction) { + const fileInfo = state.fileInfo; + + const size = fileInfo.size + ? state.translate('downloadFileSize', { size: bytes(fileInfo.size) }) + : ''; + + 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} + + ${state.translate('sendYourFilesLink')} + + ${info} +
+ `; +}; diff --git a/app/pages/preview/preview.css b/app/pages/preview/preview.css new file mode 100644 index 000000000..83b1bc987 --- /dev/null +++ b/app/pages/preview/preview.css @@ -0,0 +1,4 @@ +.preview__img { + width: 283px; + height: 196px; +} diff --git a/app/pages/share.js b/app/pages/share.js deleted file mode 100644 index aa16a8e5f..000000000 --- a/app/pages/share.js +++ /dev/null @@ -1,127 +0,0 @@ -/* global EXPIRE_SECONDS */ -const html = require('choo/html'); -const raw = require('choo/html/raw'); -const assets = require('../../common/assets'); -const notFound = require('./notFound'); -const uploadPasswordSet = require('../templates/uploadPasswordSet'); -const uploadPasswordUnset = require('../templates/uploadPasswordUnset'); -const selectbox = require('../templates/selectbox'); -const { allowedCopy, delay, fadeOut } = require('../utils'); - -function expireInfo(file, translate, emit) { - const hours = Math.floor(EXPIRE_SECONDS / 60 / 60); - const el = html`
${raw( - translate('expireInfo', { - downloadCount: '', - 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 }); - select.parentNode.replaceChild( - selectbox(file.dlimit || 1, options, t, changed), - select - ); - return el; -} - -module.exports = function(state, emit) { - const file = state.storage.getFileById(state.params.id); - if (!file) { - return notFound(state, emit); - } - - const passwordSection = file.hasPassword - ? uploadPasswordSet(state, emit) - : uploadPasswordUnset(state, emit); - const div = html` - - `; - - function showPopup() { - const popupText = document.querySelector('.popuptext'); - popupText.classList.add('show'); - popupText.focus(); - } - - function cancel(e) { - e.stopPropagation(); - const popupText = document.querySelector('.popuptext'); - popupText.classList.remove('show'); - } - - async function sendNew(e) { - e.preventDefault(); - await fadeOut('share-link'); - emit('pushState', '/'); - } - - async function copyLink() { - if (allowedCopy()) { - emit('copy', { url: file.url, location: 'success-screen' }); - const input = document.getElementById('link'); - input.disabled = true; - const copyBtn = document.getElementById('copy-btn'); - copyBtn.disabled = true; - copyBtn.classList.add('success'); - copyBtn.replaceChild( - html``, - copyBtn.firstChild - ); - await delay(2000); - input.disabled = false; - if (!copyBtn.parentNode.classList.contains('wait-password')) { - copyBtn.disabled = false; - } - copyBtn.classList.remove('success'); - copyBtn.textContent = state.translate('copyUrlFormButton'); - } - } - - async function deleteFile() { - emit('delete', { file, location: 'success-screen' }); - await fadeOut('share-link'); - emit('pushState', '/'); - } - return div; -}; diff --git a/app/pages/share/index.js b/app/pages/share/index.js new file mode 100644 index 000000000..2454bf327 --- /dev/null +++ b/app/pages/share/index.js @@ -0,0 +1,115 @@ +/* global EXPIRE_SECONDS */ +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 { 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 html` +
+
${expireInfo(file, state.translate, emit)}
+
+
+ ${state.translate('copyUrlFormLabelWithName', { filename: file.name })} +
+
+ + +
+ ${setPasswordSection(state, emit)} + +
+ ${deletePopup( + state.translate('deletePopupText'), + state.translate('deletePopupYes'), + state.translate('deletePopupCancel'), + deleteFile + )} +
+ ${state.translate('sendAnotherFileLink')} +
+
+ `; + + function showPopup() { + 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.replaceChild( + html``, + copyBtn.firstChild + ); + await delay(2000); + input.disabled = false; + input.classList.remove('input--copied'); + copyBtn.disabled = false; + copyBtn.classList.remove('inputBtn--copied'); + copyBtn.textContent = state.translate('copyUrlFormButton'); + } + } + + async function deleteFile() { + emit('delete', { file, location: 'success-screen' }); + await fadeOut('#shareWrapper'); + emit('pushState', '/'); + } +}; + +function expireInfo(file, translate, emit) { + const hours = Math.floor(EXPIRE_SECONDS / 60 / 60); + const el = html`
${raw( + translate('expireInfo', { + downloadCount: '', + 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 }); + select.parentNode.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 new file mode 100644 index 000000000..392e3db73 --- /dev/null +++ b/app/pages/share/share.css @@ -0,0 +1,112 @@ +.sharePage { + margin: 0 auto; + display: flex; + justify-content: center; + flex-direction: column; + width: 100%; + max-width: 640px; +} + +.sharePage__copyText { + align-self: flex-start; + margin-top: 60px; + margin-bottom: 10px; + color: var(--textColor); + max-width: 614px; + word-wrap: break-word; +} + +.sharePage__deletePopup { + position: relative; + align-self: center; + bottom: 50px; +} + +.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); + font-family: 'SF Pro Text', sans-serif; + letter-spacing: 0; + line-height: 23px; + font-weight: 300; + padding-left: 10px; +} + +.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 { + background: var(--successControlBGColor); + border: 1px solid var(--successControlBGColor); + color: var(--successControlFGColor); +} + +.btn--delete { + 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; +} + +.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; + } +} diff --git a/app/pages/unsupported.js b/app/pages/unsupported.js deleted file mode 100644 index 03a558837..000000000 --- a/app/pages/unsupported.js +++ /dev/null @@ -1,52 +0,0 @@ -const html = require('choo/html'); -const assets = require('../../common/assets'); - -module.exports = function(state) { - const msg = - state.params.reason === 'outdated' - ? html` -
-
${state.translate('notSupportedHeader')}
-
- ${state.translate('notSupportedOutdatedDetail')} -
- - -
- ${state.translate('updateFirefox')} -
-
-
- ${state.translate('uploadPageExplainer')} -
-
` - : html` -
-
${state.translate('notSupportedHeader')}
-
${state.translate('notSupportedDetail')}
- - - -
Firefox
- ${state.translate('downloadFirefoxButtonSub')} -
-
-
- ${state.translate('uploadPageExplainer')} -
-
`; - const div = html`
${msg}
`; - return div; -}; diff --git a/app/pages/unsupported/index.js b/app/pages/unsupported/index.js new file mode 100644 index 000000000..99937cfd9 --- /dev/null +++ b/app/pages/unsupported/index.js @@ -0,0 +1,67 @@ +const html = require('choo/html'); +const assets = require('../../../common/assets'); + +module.exports = function(state) { + let strings = {}; + let why = ''; + let url = ''; + let buttonAction = ''; + if (state.params.reason !== 'outdated') { + strings = unsupportedStrings(state); + why = html` + `; + url = + 'https://www.mozilla.org/firefox/new/?utm_campaign=send-acquisition&utm_medium=referral&utm_source=send.firefox.com'; + buttonAction = html` +
+ Firefox
${strings.button} +
`; + } else { + strings = outdatedStrings(state); + url = 'https://support.mozilla.org/kb/update-firefox-latest-version'; + buttonAction = html` +
+ ${strings.button} +
`; + } + return html` +
+
${strings.title}
+
+ ${strings.description} +
+ ${why} + + + ${buttonAction} + +
+ ${strings.explainer} +
+
`; +}; + +function outdatedStrings(state) { + return { + title: state.translate('notSupportedHeader'), + description: state.translate('notSupportedOutdatedDetail'), + button: state.translate('updateFirefox'), + explainer: state.translate('uploadPageExplainer') + }; +} + +function unsupportedStrings(state) { + return { + title: 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 new file mode 100644 index 000000000..8f22443a7 --- /dev/null +++ b/app/pages/unsupported/unsupported.css @@ -0,0 +1,49 @@ +.unsupportedPage { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} + +.unsupportedPage__info { + font-size: 13px; + line-height: 23px; + text-align: center; + color: var(--lightTextColor); + margin: 0 auto 23px; +} + +.firefoxDownload { + margin-bottom: 181px; + height: 80px; + background: #98e02b; + 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); + font-size: 26px; + display: flex; + justify-content: center; + align-items: center; + line-height: 1; + padding: 0 25px; +} + +.firefoxDownload__logo { + width: 70px; +} + +.firefoxDownload__action { + text-align: left; + margin-left: 20.4px; +} + +.firefoxDownload__text { + font-family: 'Fira Sans', 'segoe ui', sans-serif; + font-weight: 300; + font-size: 18px; + letter-spacing: -0.69px; +} diff --git a/app/pages/upload.js b/app/pages/upload.js deleted file mode 100644 index 7a21bd3be..000000000 --- a/app/pages/upload.js +++ /dev/null @@ -1,41 +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 div = html` -
-
-
- ${state.translate('uploadingPageProgress', { - filename: transfer.file.name, - size: bytes(transfer.file.size) - })} -
-
- ${progress(transfer.progressRatio)} -
-
- ${state.translate(transfer.msg, transfer.sizes)} -
- -
-
-
- `; - - function cancel() { - const btn = document.getElementById('cancel-upload'); - btn.disabled = true; - btn.textContent = state.translate('uploadCancelNotification'); - emit('cancel'); - } - return div; -}; diff --git a/app/pages/upload/index.js b/app/pages/upload/index.js new file mode 100644 index 000000000..7d5f7d762 --- /dev/null +++ b/app/pages/upload/index.js @@ -0,0 +1,39 @@ +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)} +
+
+ ${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.js b/app/pages/welcome/index.js similarity index 62% rename from app/pages/welcome.js rename to app/pages/welcome/index.js index ab5df4080..a0581e418 100644 --- a/app/pages/welcome.js +++ b/app/pages/welcome/index.js @@ -1,13 +1,13 @@ /* global MAXFILESIZE */ const html = require('choo/html'); -const assets = require('../../common/assets'); -const fileList = require('../templates/fileList'); -const { bytes, fadeOut } = require('../utils'); +const assets = require('../../../common/assets'); +const fileList = require('../../templates/fileList'); +const { bytes, fadeOut } = require('../../utils'); module.exports = function(state, emit) { - // the page flickers if both the server and browser set 'fadeIn' - const fade = state.layout ? '' : 'fadeIn'; - const div = html` + // the page flickers if both the server and browser set 'effect--fadeIn' + const fade = state.layout ? '' : 'effect--fadeIn'; + return html`
${state.translate('uploadPageHeader')}
@@ -18,7 +18,7 @@ module.exports = function(state, emit) { ${state.translate('uploadPageLearnMore')}
-
@@ -26,19 +26,21 @@ module.exports = function(state, emit) { src="${assets.get('upload.svg')}" title="${state.translate('uploadSvgAlt')}"/>
-
${state.translate('uploadPageDropMessage')}
- - ${state.translate('uploadPageSizeMessage')} +
+ ${state.translate('uploadPageDropMessage')} +
+ + ${state.translate('uploadPageSizeMessage')} @@ -48,21 +50,21 @@ module.exports = function(state, emit) { `; function dragover(event) { - const div = document.querySelector('.upload-window'); - div.classList.add('ondrag'); + const div = document.querySelector('.uploadArea'); + div.classList.add('uploadArea--dragging'); } function dragleave(event) { - const div = document.querySelector('.upload-window'); - div.classList.remove('ondrag'); + const div = document.querySelector('.uploadArea'); + div.classList.remove('uploadArea--dragging'); } function onfocus(event) { - event.target.classList.add('has-focus'); + event.target.classList.add('inputFile--focused'); } function onblur(event) { - event.target.classList.remove('has-focus'); + event.target.classList.remove('inputFile--focused'); } async function upload(event) { @@ -77,8 +79,7 @@ module.exports = function(state, emit) { return; } - await fadeOut('page-one'); + await fadeOut('#page-one'); emit('upload', { file, type: 'click' }); } - return div; }; diff --git a/app/pages/welcome/welcome.css b/app/pages/welcome/welcome.css new file mode 100644 index 000000000..298c1a3a6 --- /dev/null +++ b/app/pages/welcome/welcome.css @@ -0,0 +1,71 @@ +.uploadArea { + border: 3px dashed rgba(0, 148, 251, 0.5); + margin: 0 auto 10px; + height: 255px; + border-radius: 4px; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + text-align: center; + transition: transform 150ms; + padding: 15px; +} + +.uploadArea__msg { + font-size: 22px; + color: var(--lightTextColor); + margin: 20px 0 10px; + font-family: 'SF Pro Text', sans-serif; +} + +.uploadArea__sizeMsg { + font-style: italic; + font-size: 12px; + line-height: 16px; + color: var(--lightTextColor); + margin-bottom: 22px; +} + +.uploadArea--dragging { + border: 5px dashed rgba(0, 148, 251, 0.5); + height: 251px; + transform: scale(1.04); + border-radius: 4.2px; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + text-align: center; +} + +.uploadArea--dragging * { + pointer-events: none; +} + +.btn--file { + font-size: 20px; + min-width: 240px; + height: 60px; + display: flex; + justify-content: center; + align-items: center; + padding: 0 10px; +} + +.btn--file:hover { + background-color: var(--primaryControlHoverColor); +} + +.inputFile { + opacity: 0; + overflow: hidden; + position: absolute; + z-index: -1; +} + +.btn--file .inputFile--focused { + background-color: var(--primaryControlHoverColor); + outline: 1px dotted #000; + outline: -webkit-focus-ring-color auto 5px; +} diff --git a/app/templates/downloadButton/downloadButton.css b/app/templates/downloadButton/downloadButton.css new file mode 100644 index 000000000..834dc76fa --- /dev/null +++ b/app/templates/downloadButton/downloadButton.css @@ -0,0 +1,10 @@ +.btn--download { + width: 180px; + height: 44px; + margin-top: 20px; + margin-bottom: 30px; +} + +.btn--download:hover { + background-color: var(--primaryControlHoverColor); +} diff --git a/app/templates/downloadButton.js b/app/templates/downloadButton/index.js similarity index 76% rename from app/templates/downloadButton.js rename to app/templates/downloadButton/index.js index be01333f2..e1cac5389 100644 --- a/app/templates/downloadButton.js +++ b/app/templates/downloadButton/index.js @@ -1,16 +1,13 @@ const html = require('choo/html'); module.exports = function(state, emit) { + return html` + `; + function download(event) { event.preventDefault(); emit('download', state.fileInfo); } - - return html` -
- -
`; }; diff --git a/app/templates/downloadPassword/downloadPassword.css b/app/templates/downloadPassword/downloadPassword.css new file mode 100644 index 000000000..f6272f4fd --- /dev/null +++ b/app/templates/downloadPassword/downloadPassword.css @@ -0,0 +1,30 @@ +.passwordSection { + text-align: left; + padding: 40px; +} + +.passwordForm { + display: flex; + flex-wrap: nowrap; + width: 100%; + padding: 10px 0; +} + +.input--password { + flex: 1; +} + +.inputBtn--password { + flex: 0 1 165px; +} + +.red { + color: red; +} + +@media (max-device-width: 520px), (max-width: 520px) { + .passwordForm { + flex-direction: column; + padding-left: 0; + } +} diff --git a/app/templates/downloadPassword.js b/app/templates/downloadPassword/index.js similarity index 54% rename from app/templates/downloadPassword.js rename to app/templates/downloadPassword/index.js index bca7ccf15..08e41d5c4 100644 --- a/app/templates/downloadPassword.js +++ b/app/templates/downloadPassword/index.js @@ -5,52 +5,52 @@ module.exports = function(state, emit) { const label = fileInfo.password === null ? html` -