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)}
-
-
+
`;
- 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)}
+
+
+ ${state.translate('accountBenefitTitle')}
+
+ - ${state.translate('accountBenefitMultiFile')}
+ - ${state.translate('accountBenefitLargeFiles')}
+ - ${state.translate('accountBenefitExpiry')}
+ - ${state.translate('accountBenefitSync')}
+ - ${state.translate('accountBenefitNotify')}
+ - ${state.translate('accountBenefitMore')}
+
+
+
+
+
+
+
+
+ `;
+
+ 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)}
-
-
- ${state.translate('uploadingPageCancel')}
-
-
-
- `;
-
- 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')}
-
-
+ ${title(state)}
+
+