diff --git a/.stylelintrc b/.stylelintrc
index 3c593e83..c0c673c7 100644
--- a/.stylelintrc
+++ b/.stylelintrc
@@ -1,6 +1,6 @@
extends: stylelint-config-standard
rules:
- color-hex-case: upper
+ color-hex-case: lower
declaration-colon-newline-after: null
selector-list-comma-newline-after: null
diff --git a/frontend/src/fileReceiver.js b/frontend/src/fileReceiver.js
index 155a8dd9..ad1611df 100644
--- a/frontend/src/fileReceiver.js
+++ b/frontend/src/fileReceiver.js
@@ -58,41 +58,46 @@ class FileReceiver extends EventEmitter {
true,
['encrypt', 'decrypt']
)
- ]).then(([fdata, key]) => {
- this.emit('decrypting', true);
- return Promise.all([
- window.crypto.subtle.decrypt(
- {
- name: 'AES-GCM',
- iv: hexToArray(fdata.iv),
- additionalData: hexToArray(fdata.aad)
- },
- key,
- fdata.data
- ).then(decrypted => {
- this.emit('decrypting', false);
- return Promise.resolve(decrypted)
- }),
- fdata.filename,
- hexToArray(fdata.aad)
- ]);
- }).then(([decrypted, fname, proposedHash]) => {
- this.emit('hashing', true);
- return window.crypto.subtle.digest('SHA-256', decrypted).then(calculatedHash => {
- this.emit('hashing', false);
- const integrity = new Uint8Array(calculatedHash).toString() === proposedHash.toString();
- if (!integrity) {
- this.emit('unsafe', true)
- return Promise.reject();
- } else {
- this.emit('safe', true);
- return Promise.all([
- decrypted,
- decodeURIComponent(fname)
- ]);
- }
+ ])
+ .then(([fdata, key]) => {
+ this.emit('decrypting', true);
+ return Promise.all([
+ window.crypto.subtle
+ .decrypt(
+ {
+ name: 'AES-GCM',
+ iv: hexToArray(fdata.iv),
+ additionalData: hexToArray(fdata.aad)
+ },
+ key,
+ fdata.data
+ )
+ .then(decrypted => {
+ this.emit('decrypting', false);
+ return Promise.resolve(decrypted);
+ }),
+ fdata.filename,
+ hexToArray(fdata.aad)
+ ]);
})
- })
+ .then(([decrypted, fname, proposedHash]) => {
+ this.emit('hashing', true);
+ return window.crypto.subtle
+ .digest('SHA-256', decrypted)
+ .then(calculatedHash => {
+ this.emit('hashing', false);
+ const integrity =
+ new Uint8Array(calculatedHash).toString() ===
+ proposedHash.toString();
+ if (!integrity) {
+ this.emit('unsafe', true);
+ return Promise.reject();
+ } else {
+ this.emit('safe', true);
+ return Promise.all([decrypted, decodeURIComponent(fname)]);
+ }
+ });
+ });
}
}
diff --git a/frontend/src/upload.js b/frontend/src/upload.js
index 194d50af..3f6ba2cf 100644
--- a/frontend/src/upload.js
+++ b/frontend/src/upload.js
@@ -40,7 +40,9 @@ $(document).ready(function() {
//disable button for 3s
$copyBtn.attr('disabled', true);
$('#link').attr('disabled', true);
- $copyBtn.html('');
+ $copyBtn.html(
+ ''
+ );
window.setTimeout(() => {
$copyBtn.attr('disabled', false);
$('#link').attr('disabled', false);
@@ -71,12 +73,14 @@ $(document).ready(function() {
event.preventDefault();
let file = '';
if (event.type === 'drop') {
- if (event.originalEvent.dataTransfer.files.length > 1 || event.originalEvent.dataTransfer.files[0].size === 0){
+ if (
+ event.originalEvent.dataTransfer.files.length > 1 ||
+ event.originalEvent.dataTransfer.files[0].size === 0
+ ) {
$('.upload-window').removeClass('ondrag');
- document.l10n.formatValue('uploadPageMultipleFilesAlert')
- .then(str => {
- alert(str);
- });
+ document.l10n.formatValue('uploadPageMultipleFilesAlert').then(str => {
+ alert(str);
+ });
return;
}
file = event.originalEvent.dataTransfer.files[0];
@@ -95,10 +99,9 @@ $(document).ready(function() {
$('#cancel-upload').click(() => {
fileSender.cancel();
location.reload();
- document.l10n.formatValue('uploadCancelNotification')
- .then(str => {
- notify(str);
- });
+ document.l10n.formatValue('uploadCancelNotification').then(str => {
+ notify(str);
+ });
});
fileSender.on('progress', progress => {
@@ -110,15 +113,21 @@ $(document).ready(function() {
});
if (progress[1] < 1000000) {
$('.progress-text').text(
- `${file.name} (${(progress[0] / 1000).toFixed(1)}KB of ${(progress[1] / 1000).toFixed(1)}KB)`
+ `${file.name} (${(progress[0] / 1000).toFixed(
+ 1
+ )}KB of ${(progress[1] / 1000).toFixed(1)}KB)`
);
} else if (progress[1] < 1000000000) {
$('.progress-text').text(
- `${file.name} (${(progress[0] / 1000000).toFixed(1)}MB of ${(progress[1] / 1000000).toFixed(1)}MB)`
+ `${file.name} (${(progress[0] / 1000000).toFixed(
+ 1
+ )}MB of ${(progress[1] / 1000000).toFixed(1)}MB)`
);
} else {
$('.progress-text').text(
- `${file.name} (${(progress[0] / 1000000).toFixed(1)}MB of ${(progress[1] / 1000000000).toFixed(1)}GB)`
+ `${file.name} (${(progress[0] / 1000000).toFixed(
+ 1
+ )}MB of ${(progress[1] / 1000000000).toFixed(1)}GB)`
);
}
});
@@ -163,7 +172,10 @@ $(document).ready(function() {
expiry: expiration
};
localStorage.setItem(info.fileId, JSON.stringify(fileData));
- $('#upload-filename').attr('data-l10n-id', 'uploadSuccessConfirmHeader');
+ $('#upload-filename').attr(
+ 'data-l10n-id',
+ 'uploadSuccessConfirmHeader'
+ );
t = window.setTimeout(() => {
$('#page-one').attr('hidden', true);
$('#upload-progress').attr('hidden', true);
@@ -172,10 +184,9 @@ $(document).ready(function() {
}, 1000);
populateFileList(JSON.stringify(fileData));
- document.l10n.formatValue('notifyUploadDone')
- .then(str => {
- notify(str);
- });
+ document.l10n.formatValue('notifyUploadDone').then(str => {
+ notify(str);
+ });
})
.catch(err => {
Raven.captureException(err);
@@ -219,10 +230,18 @@ $(document).ready(function() {
const row = document.createElement('tr');
const name = document.createElement('td');
const link = document.createElement('td');
- const $copyIcon = $('', { src: '/resources/copy-16.svg', class: 'icon-copy', 'data-l10n-id': 'copyUrlHover'});
+ const $copyIcon = $('', {
+ src: '/resources/copy-16.svg',
+ class: 'icon-copy',
+ 'data-l10n-id': 'copyUrlHover'
+ });
const expiry = document.createElement('td');
const del = document.createElement('td');
- const $delIcon = $('', { src: '/resources/close-16.svg', class: 'icon-delete', 'data-l10n-id': 'deleteButtonHover' });
+ const $delIcon = $('', {
+ src: '/resources/close-16.svg',
+ class: 'icon-delete',
+ 'data-l10n-id': 'deleteButtonHover'
+ });
const popupDiv = document.createElement('div');
const $popupText = $('
', { class: 'popuptext' });
const cellText = document.createTextNode(file.name);
@@ -230,14 +249,8 @@ $(document).ready(function() {
const url = file.url.trim() + `#${file.secretKey}`.trim();
$('#link').attr('value', url);
- $('#copy-text').attr(
- 'data-l10n-args',
- '{"filename": "' + file.name + '"}'
- );
- $('#copy-text').attr(
- 'data-l10n-id',
- 'copyUrlFormLabelWithName'
- );
+ $('#copy-text').attr('data-l10n-args', '{"filename": "' + file.name + '"}');
+ $('#copy-text').attr('data-l10n-id', 'copyUrlFormLabelWithName');
$popupText.attr('tabindex', '-1');
name.appendChild(cellText);
@@ -264,10 +277,9 @@ $(document).ready(function() {
aux.select();
document.execCommand('copy');
document.body.removeChild(aux);
- document.l10n.formatValue('copiedUrl')
- .then(translated => {
- link.innerHTML = translated;
- });
+ document.l10n.formatValue('copiedUrl').then(translated => {
+ link.innerHTML = translated;
+ });
window.setTimeout(() => {
const linkImg = document.createElement('img');
$(linkImg).addClass('icon-copy');
@@ -327,13 +339,7 @@ $(document).ready(function() {
$(popupNvmSpan).addClass('nvm');
$(popupNvmSpan).attr('data-l10n-id', 'nevermindButton');
- $popupText.html([
- popupDelSpan,
- ' ',
- ' ',
- popupNvmSpan
- ]);
-
+ $popupText.html([popupDelSpan, ' ', ' ', popupNvmSpan]);
// add data cells to table row
row.appendChild(name);
@@ -378,7 +384,6 @@ $(document).ready(function() {
$popupText.removeClass('show');
});
-
toggleHeader();
}
function toggleHeader() {
diff --git a/public/locales/send.en-US.ftl b/public/locales/send.en-US.ftl
index a3873fbb..a68ab579 100644
--- a/public/locales/send.en-US.ftl
+++ b/public/locales/send.en-US.ftl
@@ -1,4 +1,6 @@
title = Firefox Send
+siteSubtitle = web experiment
+siteFeedback = Feedback
uploadPageHeader = Private, Encrypted File Sharing
uploadPageExplainer = Send files through a safe, private, and encrypted link that automatically expires to ensure your stuff does not remain online forever.
diff --git a/public/main.css b/public/main.css
index e1a1bf7b..18ea52a0 100644
--- a/public/main.css
+++ b/public/main.css
@@ -1,7 +1,12 @@
/*** index.html ***/
html {
background: url('resources/send_bg.svg');
- font-family: 'SF Pro Text', sans-serif;
+ font-family: -apple-system,
+ BlinkMacSystemFont,
+ 'SF Pro Text',
+ Helvetica,
+ Arial,
+ sans-serif;
font-weight: 200;
background-size: 100%;
background-repeat: no-repeat;
@@ -10,24 +15,94 @@ html {
}
body {
- min-height: 100%;
- position: relative;
+ display: flex;
+ flex-direction: column;
margin: 0;
+ min-height: 100vh;
+ position: relative;
+}
+
+.header {
+ align-items: flex-start;
+ box-sizing: border-box;
+ display: flex;
+ justify-content: space-between;
+ padding: 31px;
+ width: 100%;
}
.send-logo {
+ display: flex;
position: relative;
- top: 31px;
- left: 31px;
- display: inline-block;
+ align-items: center;
+}
+
+.site-title {
+ font-size: 34px;
+ font-weight: 500;
+ margin: 0;
+ position: relative;
+ top: -1px;
+}
+
+.site-subtitle {
+ font-size: 12px;
+ margin: 0 8px;
+ color: #0c0c0d;
+}
+
+.site-subtitle a {
+ font-weight: bold;
+ color: #0c0c0d;
+ transition: color 50ms;
+}
+
+.send-logo:hover a {
+ color: #0297f8;
+}
+
+.feedback {
+ background-color: #0297f8;
+ background-image: url('resources/feedback.svg');
+ background-position: 4px 6px;
+ background-repeat: no-repeat;
+ background-size: 14px;
+ border-radius: 3px;
+ border: 1px solid #0297f8;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
+ color: #fff;
+ cursor: pointer;
+ display: block;
+ float: right;
+ font-size: 12px;
+ line-height: 12px;
+ opacity: 0.9;
+ padding: 6px 6px 5px 20px;
+}
+
+.feedback:hover,
+.feedback:focus {
+ background-color: #0287e8;
+}
+
+.feedback:active {
+ background-color: #0277d8;
}
.all {
- padding-top: 10%;
- padding-bottom: 51px;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ max-width: 630px;
+ margin: 0 auto;
+ width: 96%;
}
-input, select, textarea, button {
+input,
+select,
+textarea,
+button {
font-family: inherit;
}
@@ -38,24 +113,26 @@ a {
/** page-one **/
.title {
font-size: 33px;
+ line-height: 40px;
margin: 20px auto;
text-align: center;
+ max-width: 520px;
font-family: 'SF Pro Display', sans-serif;
}
.description {
font-size: 15px;
line-height: 23px;
- width: 630px;
+ max-width: 630px;
text-align: center;
margin: 0 auto 60px;
- color: #0C0C0D;
+ color: #0c0c0d;
+ width: 92%;
}
.upload-window {
border: 1px dashed rgba(0, 148, 251, 0.5);
margin: 0 auto;
- width: 640px;
height: 255px;
border-radius: 4px;
display: flex;
@@ -63,14 +140,15 @@ a {
align-items: center;
flex-direction: column;
text-align: center;
+ transition: transform 150ms;
+ padding: 15px;
}
.upload-window.ondrag {
border: 3px dashed rgba(0, 148, 251, 0.5);
margin: 0 auto;
- width: 636px;
height: 251px;
- transform: scale(1.05);
+ transform: scale(1.04);
border-radius: 4.2px;
display: flex;
justify-content: center;
@@ -80,7 +158,7 @@ a {
}
.link {
- color: #0094FB;
+ color: #0094fb;
text-decoration: none;
}
@@ -92,10 +170,10 @@ a {
}
#browse {
- background: #0297F8;
+ background: #0297f8;
border-radius: 5px;
font-size: 15px;
- color: #FFF;
+ color: #fff;
width: 240px;
height: 44px;
display: flex;
@@ -110,6 +188,7 @@ input[type="file"] {
#file-size-msg {
font-size: 12px;
+ line-height: 16px;
color: #737373;
margin-bottom: 22px;
}
@@ -129,7 +208,7 @@ th {
td {
font-size: 15px;
vertical-align: top;
- color: #4A4A4A;
+ color: #4a4a4a;
padding: 17px 19px 0;
line-height: 23px;
}
@@ -144,12 +223,13 @@ tbody {
}
#uploaded-files {
- width: 640px;
margin: 45.3px auto;
table-layout: fixed;
}
-.icon-delete, .icon-copy, .icon-check {
+.icon-delete,
+.icon-copy,
+.icon-check {
cursor: pointer;
}
@@ -165,7 +245,7 @@ tbody {
visibility: hidden;
width: 160px;
background-color: #555;
- color: #FFF;
+ color: #fff;
text-align: center;
border-radius: 6px;
padding: 8px 0;
@@ -239,14 +319,13 @@ tbody {
}
#cancel-upload {
- color: #D70022;
+ color: #d70022;
cursor: pointer;
text-decoration: underline;
}
/** share-link **/
#share-window {
- width: 645px;
margin: 0 auto;
display: flex;
justify-content: center;
@@ -262,53 +341,59 @@ tbody {
#copy {
display: flex;
flex-wrap: nowrap;
+ width: 640px;
}
#copy-text {
align-self: flex-start;
margin-top: 60px;
margin-bottom: 10px;
- color: #0C0C0D;
+ color: #0c0c0d;
}
#link {
- width: 480px;
+ flex: 1;
height: 56px;
- border: 1px solid #0297F8;
+ border: 1px solid #0297f8;
border-radius: 6px 0 0 6px;
font-size: 24px;
color: #737373;
font-family: 'SF Pro Display', sans-serif;
letter-spacing: 0;
line-height: 23px;
+ padding-left: 5px;
+ padding-right: 5px;
}
#link:disabled {
- border: 1px solid #05A700;
- background: #FFF;
+ border: 1px solid #05a700;
+ background: #fff;
}
#copy-btn {
- width: 165px;
- height: 60px;
- background: #0297F8;
- border: 1px solid #0297F8;
+ flex: 0 1 165px;
+ background: #0297f8;
border-radius: 0 6px 6px 0;
+ border: 1px solid #0297f8;
color: white;
cursor: pointer;
font-size: 15px;
+ height: 60px;
+ padding-left: 10px;
+ padding-right: 10px;
+ white-space: nowrap;
}
#copy-btn:disabled {
- background: #05A700;
- border: 1px solid #05A700;
+ background: #05a700;
+ border: 1px solid #05a700;
cursor: auto;
}
#delete-file {
width: 176px;
height: 44px;
- background: #FFF;
+ background: #fff;
border: 1px solid rgba(12, 12, 13, 0.3);
border-radius: 5px;
font-size: 15px;
@@ -322,7 +407,7 @@ tbody {
font-size: 15px;
margin: auto;
text-align: center;
- color: #0094FB;
+ color: #0094fb;
cursor: pointer;
text-decoration: underline;
}
@@ -336,7 +421,8 @@ tbody {
text-align: center;
}
-#upload-error[hidden], #unsupported-browser[hidden] {
+#upload-error[hidden],
+#unsupported-browser[hidden] {
display: none;
}
@@ -356,9 +442,8 @@ tbody {
.unsupported-description {
font-size: 13px;
line-height: 23px;
- width: 630px;
text-align: center;
- color: #7D7D7D;
+ color: #7d7d7d;
margin: 0 auto 23px;
}
@@ -370,14 +455,14 @@ tbody {
margin-bottom: 181px;
width: 260px;
height: 80px;
- background: #12BC00;
+ background: #12bc00;
border-radius: 3px;
cursor: pointer;
border: 0;
box-shadow: 0 5px 3px rgb(234, 234, 234);
font-family: 'Fira Sans';
font-weight: 500;
- color: #FFF;
+ color: #fff;
font-size: 26px;
display: flex;
justify-content: center;
@@ -406,15 +491,15 @@ tbody {
margin-top: 20px;
margin-bottom: 30px;
text-align: center;
- background: #0297F8;
- border: 1px solid #0297F8;
+ background: #0297f8;
+ border: 1px solid #0297f8;
border-radius: 5px;
font-weight: 300;
cursor: pointer;
}
#download-btn:disabled {
- background: #47B04B;
+ background: #47b04b;
cursor: auto;
}
@@ -434,9 +519,8 @@ tbody {
.expired-description {
font-size: 15px;
line-height: 23px;
- width: 630px;
text-align: center;
- color: #7D7D7D;
+ color: #7d7d7d;
margin: 0 auto 23px;
}
@@ -460,14 +544,13 @@ tbody {
/* footer */
.footer {
- position: absolute;
right: 0;
bottom: 0;
left: 0;
font-size: 15px;
display: flex;
align-items: flex-end;
- padding: 10px;
+ padding: 50px 10px 10px;
}
.mozilla-logo {
@@ -495,8 +578,60 @@ tbody {
margin-left: 30px;
}
-.github, .twitter {
+.github,
+.twitter {
width: 32px;
height: 32px;
margin-bottom: -5px;
}
+
+@media (max-width: 768px) {
+ .description {
+ margin: 0 auto 25px;
+ }
+
+ #copy {
+ width: 100%;
+ }
+
+ #link {
+ font-size: 18px;
+ }
+
+ .mozilla-logo {
+ margin-left: -7px;
+ }
+
+ .legal-links > * {
+ display: block;
+ padding: 10px 0;
+ }
+}
+
+@media (max-width: 520px) {
+ #copy {
+ width: 100%;
+ flex-direction: column;
+ }
+
+ #link {
+ font-size: 22px;
+ padding: 15px 10px;
+ border-radius: 6px 6px 0 0;
+ }
+
+ #copy-btn {
+ border-radius: 0 0 6px 6px;
+ flex: 0 1 65px;
+ }
+
+ th {
+ font-size: 14px;
+ padding: 0 5px;
+ }
+
+ td {
+ font-size: 13px;
+ padding: 17px 5px 0;
+ }
+}
diff --git a/public/resources/feedback.svg b/public/resources/feedback.svg
new file mode 100644
index 00000000..1e3e3fe5
--- /dev/null
+++ b/public/resources/feedback.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/resources/send_logo_type.svg b/public/resources/send_logo_type.svg
deleted file mode 100644
index 07091885..00000000
--- a/public/resources/send_logo_type.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/server/server.js b/server/server.js
index 24e1d418..97787255 100644
--- a/server/server.js
+++ b/server/server.js
@@ -34,10 +34,12 @@ app.engine(
app.set('view engine', 'handlebars');
app.use(helmet());
-app.use(helmet.hsts({
- maxAge: 31536000,
- force: conf.env === 'production'
-}));
+app.use(
+ helmet.hsts({
+ maxAge: 31536000,
+ force: conf.env === 'production'
+ })
+);
app.use(
helmet.contentSecurityPolicy({
directives: {
@@ -241,7 +243,7 @@ app.post('/upload', (req, res, next) => {
.catch(err => {
log.info('DeleteError:', newId);
});
- })
+ });
});
app.get('/__lbheartbeat__', (req, res) => {
diff --git a/views/layouts/main.handlebars b/views/layouts/main.handlebars
index 285ad3c7..5eb891ff 100644
--- a/views/layouts/main.handlebars
+++ b/views/layouts/main.handlebars
@@ -13,10 +13,17 @@
-
-
-
-
+
{{{body}}}