updated password input UI
This commit is contained in:
parent
8d41111cd6
commit
346e604f34
14
app/base.css
14
app/base.css
@ -34,7 +34,6 @@ body {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
pre,
|
||||
input,
|
||||
select,
|
||||
textarea,
|
||||
@ -43,13 +42,6 @@ button {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: monospace;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
@ -69,7 +61,7 @@ a {
|
||||
.btn {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: white;
|
||||
color: var(--primaryControlFGColor);
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
background: var(--primaryControlBGColor);
|
||||
@ -113,7 +105,7 @@ a {
|
||||
background: var(--primaryControlBGColor);
|
||||
border-radius: 0 6px 6px 0;
|
||||
border: 1px solid var(--primaryControlBGColor);
|
||||
color: white;
|
||||
color: var(--primaryControlFGColor);
|
||||
cursor: pointer;
|
||||
|
||||
/* Force flat button look */
|
||||
@ -177,7 +169,7 @@ a {
|
||||
}
|
||||
|
||||
.progressSection__text {
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
color: var(--lightTextColor);
|
||||
letter-spacing: -0.4px;
|
||||
margin-top: 24px;
|
||||
margin-bottom: 74px;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
@import './templates/passwordInput/passwordInput.css';
|
||||
@import './templates/downloadPassword/downloadPassword.css';
|
||||
@import './templates/setPasswordSection/setPasswordSection.css';
|
||||
@import './templates/changePasswordSection/changePasswordSection.css';
|
||||
@import './templates/footer/footer.css';
|
||||
@import './templates/fxPromo/fxPromo.css';
|
||||
@import './templates/selectbox/selectbox.css';
|
||||
|
@ -1,6 +1,5 @@
|
||||
const html = require('choo/html');
|
||||
|
||||
module.exports = function() {
|
||||
const div = html`<div></div>`;
|
||||
return div;
|
||||
return html`<div></div>`;
|
||||
};
|
||||
|
@ -3,7 +3,7 @@ const progress = require('../../templates/progress');
|
||||
const { fadeOut } = require('../../utils');
|
||||
|
||||
module.exports = function(state, emit) {
|
||||
const div = html`
|
||||
return html`
|
||||
<div class="page effect--fadeIn">
|
||||
<div class="title">
|
||||
${state.translate('downloadFinish')}
|
||||
@ -23,6 +23,4 @@ module.exports = function(state, emit) {
|
||||
await fadeOut('.page');
|
||||
emit('pushState', '/');
|
||||
}
|
||||
|
||||
return div;
|
||||
};
|
||||
|
@ -13,7 +13,7 @@ module.exports = function(state, emit) {
|
||||
${state.translate('deletePopupCancel')}
|
||||
</button>`;
|
||||
|
||||
const div = html`
|
||||
return html`
|
||||
<div class="page effect--fadeIn">
|
||||
<div class="title">
|
||||
${state.translate('downloadingPageProgress', {
|
||||
@ -39,5 +39,4 @@ module.exports = function(state, emit) {
|
||||
btn.remove();
|
||||
emit('cancel');
|
||||
}
|
||||
return div;
|
||||
};
|
||||
|
@ -1,17 +1,8 @@
|
||||
const html = require('choo/html');
|
||||
const raw = require('choo/html/raw');
|
||||
|
||||
function replaceLinks(str, urls) {
|
||||
let i = -1;
|
||||
const s = str.replace(/<a>([^<]+)<\/a>/g, (m, v) => {
|
||||
i++;
|
||||
return `<a href="${urls[i]}">${v}</a>`;
|
||||
});
|
||||
return `<div class="description">${s}</div>`;
|
||||
}
|
||||
|
||||
module.exports = function(state) {
|
||||
const div = html`
|
||||
return html`
|
||||
<div id="legal">
|
||||
<div class="title">${state.translate('legalHeader')}</div>
|
||||
${raw(
|
||||
@ -29,5 +20,13 @@ module.exports = function(state) {
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
return div;
|
||||
};
|
||||
|
||||
function replaceLinks(str, urls) {
|
||||
let i = 0;
|
||||
const s = str.replace(
|
||||
/<a>([^<]+)<\/a>/g,
|
||||
(m, v) => `<a href="${urls[i++]}">${v}</a>`
|
||||
);
|
||||
return `<div class="description">${s}</div>`;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ const html = require('choo/html');
|
||||
const assets = require('../../../common/assets');
|
||||
|
||||
module.exports = function(state) {
|
||||
const div = html`
|
||||
return html`
|
||||
<div class="notFoundPage">
|
||||
<div class="title">${state.translate('expiredPageHeader')}</div>
|
||||
<div class="notFoundPage__img">
|
||||
@ -15,5 +15,4 @@ module.exports = function(state) {
|
||||
${state.translate('sendYourFilesLink')}
|
||||
</a>
|
||||
</div>`;
|
||||
return div;
|
||||
};
|
||||
|
@ -20,7 +20,7 @@ module.exports = function(state, pageAction) {
|
||||
if (!pageAction) {
|
||||
return info;
|
||||
}
|
||||
const div = html`
|
||||
return html`
|
||||
<div class="page">
|
||||
<div class="title">
|
||||
<span>${title}</span>
|
||||
@ -38,5 +38,4 @@ module.exports = function(state, pageAction) {
|
||||
${info}
|
||||
</div>
|
||||
`;
|
||||
return div;
|
||||
};
|
||||
|
@ -3,41 +3,18 @@ const html = require('choo/html');
|
||||
const raw = require('choo/html/raw');
|
||||
const assets = require('../../../common/assets');
|
||||
const notFound = require('../notFound');
|
||||
const changePasswordSection = require('../../templates/changePasswordSection');
|
||||
const setPasswordSection = require('../../templates/setPasswordSection');
|
||||
const selectbox = require('../../templates/selectbox');
|
||||
const deletePopup = require('../../templates/popup');
|
||||
const { allowedCopy, delay, fadeOut } = require('../../utils');
|
||||
|
||||
function expireInfo(file, translate, emit) {
|
||||
const hours = Math.floor(EXPIRE_SECONDS / 60 / 60);
|
||||
const el = html`<div>${raw(
|
||||
translate('expireInfo', {
|
||||
downloadCount: '<select></select>',
|
||||
timespan: translate('timespanHours', { num: hours })
|
||||
})
|
||||
)}</div>`;
|
||||
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
|
||||
? changePasswordSection(state, emit)
|
||||
: setPasswordSection(state, emit);
|
||||
const div = html`
|
||||
return html`
|
||||
<div id="shareWrapper" class="effect--fadeIn">
|
||||
<div class="title">${expireInfo(file, state.translate, emit)}</div>
|
||||
<div class="sharePage">
|
||||
@ -56,7 +33,7 @@ module.exports = function(state, emit) {
|
||||
title="${state.translate('copyUrlFormButton')}"
|
||||
onclick=${copyLink}>${state.translate('copyUrlFormButton')}</button>
|
||||
</div>
|
||||
${passwordSection}
|
||||
${setPasswordSection(state, emit)}
|
||||
<button
|
||||
class="btn btn--delete"
|
||||
title="${state.translate('deleteFileButton')}"
|
||||
@ -94,6 +71,7 @@ module.exports = function(state, emit) {
|
||||
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');
|
||||
@ -103,6 +81,7 @@ module.exports = function(state, emit) {
|
||||
);
|
||||
await delay(2000);
|
||||
input.disabled = false;
|
||||
input.classList.remove('input--copied');
|
||||
copyBtn.disabled = false;
|
||||
copyBtn.classList.remove('inputBtn--copied');
|
||||
copyBtn.textContent = state.translate('copyUrlFormButton');
|
||||
@ -114,5 +93,23 @@ module.exports = function(state, emit) {
|
||||
await fadeOut('#shareWrapper');
|
||||
emit('pushState', '/');
|
||||
}
|
||||
return div;
|
||||
};
|
||||
|
||||
function expireInfo(file, translate, emit) {
|
||||
const hours = Math.floor(EXPIRE_SECONDS / 60 / 60);
|
||||
const el = html`<div>${raw(
|
||||
translate('expireInfo', {
|
||||
downloadCount: '<select></select>',
|
||||
timespan: translate('timespanHours', { num: hours })
|
||||
})
|
||||
)}</div>`;
|
||||
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;
|
||||
}
|
||||
|
@ -40,7 +40,6 @@
|
||||
line-height: 23px;
|
||||
font-weight: 300;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.copySection__url:disabled {
|
||||
@ -53,6 +52,10 @@
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.input--copied {
|
||||
border-color: var(--successControlBGColor);
|
||||
}
|
||||
|
||||
.inputBtn--copied,
|
||||
.inputBtn--copied:hover {
|
||||
background: var(--successControlBGColor);
|
||||
|
@ -1,24 +1,6 @@
|
||||
const html = require('choo/html');
|
||||
const assets = require('../../../common/assets');
|
||||
|
||||
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')
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = function(state) {
|
||||
let strings = {};
|
||||
let why = '';
|
||||
@ -46,7 +28,7 @@ module.exports = function(state) {
|
||||
${strings.button}
|
||||
</div>`;
|
||||
}
|
||||
const div = html`
|
||||
return html`
|
||||
<div class="unsupportedPage">
|
||||
<div class="title">${strings.title}</div>
|
||||
<div class="description">
|
||||
@ -64,5 +46,22 @@ module.exports = function(state) {
|
||||
${strings.explainer}
|
||||
</div>
|
||||
</div>`;
|
||||
return div;
|
||||
};
|
||||
|
||||
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')
|
||||
};
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
font-size: 13px;
|
||||
line-height: 23px;
|
||||
text-align: center;
|
||||
color: #7d7d7d;
|
||||
color: var(--lightTextColor);
|
||||
margin: 0 auto 23px;
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
box-shadow: 0 5px 3px rgb(234, 234, 234);
|
||||
font-family: 'Fira Sans', 'segoe ui', sans-serif;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
color: var(--primaryControlFGColor);
|
||||
font-size: 26px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
@ -5,7 +5,7 @@ const { bytes } = require('../../utils');
|
||||
module.exports = function(state, emit) {
|
||||
const transfer = state.transfer;
|
||||
|
||||
const div = html`
|
||||
return html`
|
||||
<div class="page effect--fadeIn">
|
||||
<div class="title">
|
||||
${state.translate('uploadingPageProgress', {
|
||||
@ -36,5 +36,4 @@ module.exports = function(state, emit) {
|
||||
btn.textContent = state.translate('uploadCancelNotification');
|
||||
emit('cancel');
|
||||
}
|
||||
return div;
|
||||
};
|
||||
|
@ -7,7 +7,7 @@ const { bytes, fadeOut } = require('../../utils');
|
||||
module.exports = function(state, emit) {
|
||||
// the page flickers if both the server and browser set 'effect--fadeIn'
|
||||
const fade = state.layout ? '' : 'effect--fadeIn';
|
||||
const div = html`
|
||||
return html`
|
||||
<div id="page-one" class="${fade}">
|
||||
<div class="title">${state.translate('uploadPageHeader')}</div>
|
||||
<div class="description">
|
||||
@ -82,5 +82,4 @@ module.exports = function(state, emit) {
|
||||
await fadeOut('#page-one');
|
||||
emit('upload', { file, type: 'click' });
|
||||
}
|
||||
return div;
|
||||
};
|
||||
|
@ -1,29 +0,0 @@
|
||||
.changePasswordSection {
|
||||
padding: 10px 0;
|
||||
align-self: left;
|
||||
max-width: 100%;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.btn--reset {
|
||||
width: 80px;
|
||||
height: 30px;
|
||||
background: #fff;
|
||||
border-color: rgba(12, 12, 13, 0.3);
|
||||
margin-top: 5px;
|
||||
margin-left: 15px;
|
||||
margin-bottom: 12px;
|
||||
line-height: 24px;
|
||||
color: #313131;
|
||||
}
|
||||
|
||||
.btn--reset:hover {
|
||||
background: #efeff1;
|
||||
}
|
||||
|
||||
@media (max-device-width: 520px), (max-width: 520px) {
|
||||
.changePasswordSection {
|
||||
align-self: center;
|
||||
min-width: 95%;
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
const html = require('choo/html');
|
||||
const raw = require('choo/html/raw');
|
||||
const passwordInput = require('../passwordInput');
|
||||
|
||||
module.exports = function(state, emit) {
|
||||
const file = state.storage.getFileById(state.params.id);
|
||||
|
||||
return html`<div class="changePasswordSection">
|
||||
${passwordSpan(file.password)}
|
||||
<button
|
||||
class="btn btn--reset"
|
||||
onclick=${toggleResetInput}
|
||||
>${state.translate('changePasswordButton')}</button>
|
||||
${passwordInput(
|
||||
state.translate('unlockInputPlaceholder'),
|
||||
state.translate('changePasswordButton'),
|
||||
changePassword
|
||||
)}
|
||||
</div>`;
|
||||
|
||||
function passwordSpan(password) {
|
||||
password = password || '●●●●●';
|
||||
const span = html`<span>${raw(
|
||||
state.translate('passwordResult', {
|
||||
password: '<pre class="passwordMask"></pre>'
|
||||
})
|
||||
)}</span>`;
|
||||
const masked = span.querySelector('.passwordMask');
|
||||
masked.textContent = password.replace(/./g, '●');
|
||||
return span;
|
||||
}
|
||||
|
||||
function changePassword(event) {
|
||||
event.preventDefault();
|
||||
const password = document.getElementById('password-input').value;
|
||||
if (password.length > 0) {
|
||||
emit('password', { password, file });
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function toggleResetInput(event) {
|
||||
const form = event.target.parentElement.querySelector('form.passwordInput');
|
||||
const input = document.getElementById('password-input');
|
||||
if (form.style.visibility === 'hidden' || form.style.visibility === '') {
|
||||
form.style.visibility = 'visible';
|
||||
input.focus();
|
||||
} else {
|
||||
form.style.visibility = 'hidden';
|
||||
}
|
||||
}
|
||||
};
|
@ -1,15 +1,13 @@
|
||||
const html = require('choo/html');
|
||||
|
||||
module.exports = function(state, emit) {
|
||||
return html`
|
||||
<button class="btn btn--download"
|
||||
onclick=${download}>${state.translate('downloadButtonLabel')}
|
||||
</button>`;
|
||||
|
||||
function download(event) {
|
||||
event.preventDefault();
|
||||
emit('download', state.fileInfo);
|
||||
}
|
||||
|
||||
return html`
|
||||
<div>
|
||||
<button class="btn btn--download"
|
||||
onclick=${download}>${state.translate('downloadButtonLabel')}
|
||||
</button>
|
||||
</div>`;
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
.fileData {
|
||||
font-size: 15px;
|
||||
vertical-align: top;
|
||||
color: #4a4a4a;
|
||||
color: var(--lightTextColor);
|
||||
padding: 17px 19px 0;
|
||||
line-height: 23px;
|
||||
position: relative;
|
||||
|
@ -3,30 +3,13 @@ const assets = require('../../../common/assets');
|
||||
const number = require('../../utils').number;
|
||||
const deletePopup = require('../popup');
|
||||
|
||||
function timeLeft(milliseconds, state) {
|
||||
const minutes = Math.floor(milliseconds / 1000 / 60);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
if (hours >= 1) {
|
||||
return state.translate('expiresHoursMinutes', {
|
||||
hours,
|
||||
minutes: minutes % 60
|
||||
});
|
||||
} else if (hours === 0) {
|
||||
if (minutes === 0) {
|
||||
return state.translate('expiresMinutes', { minutes: '< 1' });
|
||||
}
|
||||
return state.translate('expiresMinutes', { minutes });
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = function(file, state, emit) {
|
||||
const ttl = file.expiresAt - Date.now();
|
||||
const remainingTime =
|
||||
timeLeft(ttl, state) || state.translate('linkExpiredAlt');
|
||||
const downloadLimit = file.dlimit || 1;
|
||||
const totalDownloads = file.dtotal || 0;
|
||||
const row = html`
|
||||
return html`
|
||||
<tr id="${file.id}">
|
||||
<td class="fileData fileData--overflow" title="${file.name}">
|
||||
<a class="link" href="/share/${file.id}">${file.name}</a>
|
||||
@ -84,6 +67,21 @@ module.exports = function(file, state, emit) {
|
||||
emit('delete', { file, location: 'upload-list' });
|
||||
emit('render');
|
||||
}
|
||||
|
||||
return row;
|
||||
};
|
||||
|
||||
function timeLeft(milliseconds, state) {
|
||||
const minutes = Math.floor(milliseconds / 1000 / 60);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
if (hours >= 1) {
|
||||
return state.translate('expiresHoursMinutes', {
|
||||
hours,
|
||||
minutes: minutes % 60
|
||||
});
|
||||
} else if (hours === 0) {
|
||||
if (minutes === 0) {
|
||||
return state.translate('expiresMinutes', { minutes: '< 1' });
|
||||
}
|
||||
return state.translate('expiresMinutes', { minutes });
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
.fileList__header {
|
||||
font-size: 16px;
|
||||
color: #858585;
|
||||
color: var(--lightTextColor);
|
||||
font-weight: lighter;
|
||||
text-align: left;
|
||||
background: rgba(0, 148, 251, 0.05);
|
||||
|
@ -2,9 +2,8 @@ const html = require('choo/html');
|
||||
const file = require('../file');
|
||||
|
||||
module.exports = function(state, emit) {
|
||||
let table = '';
|
||||
if (state.storage.files.length) {
|
||||
table = html`
|
||||
return html`
|
||||
<table class="fileList">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -31,5 +30,4 @@ module.exports = function(state, emit) {
|
||||
</table>
|
||||
`;
|
||||
}
|
||||
return table;
|
||||
};
|
||||
|
@ -20,7 +20,7 @@
|
||||
}
|
||||
|
||||
.legalSection__link {
|
||||
color: #858585;
|
||||
color: var(--lightTextColor);
|
||||
opacity: 0.9;
|
||||
white-space: nowrap;
|
||||
margin-right: 2vw;
|
||||
@ -31,7 +31,7 @@
|
||||
}
|
||||
|
||||
.legalSection__link:visited {
|
||||
color: #858585;
|
||||
color: var(--lightTextColor);
|
||||
}
|
||||
|
||||
.legalSection__mozLogo {
|
||||
|
@ -2,9 +2,6 @@ const html = require('choo/html');
|
||||
const assets = require('../../../common/assets');
|
||||
|
||||
module.exports = function(state, emit) {
|
||||
function clicked() {
|
||||
emit('experiment', { cd3: 'promo' });
|
||||
}
|
||||
let classes = 'fxPromo';
|
||||
switch (state.promo) {
|
||||
case 'blue':
|
||||
@ -30,4 +27,8 @@ module.exports = function(state, emit) {
|
||||
>Download Firefox now ≫</a></span>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
function clicked() {
|
||||
emit('experiment', { cd3: 'promo' });
|
||||
}
|
||||
};
|
||||
|
@ -59,7 +59,7 @@
|
||||
border-radius: 3px;
|
||||
border: 1px solid var(--primaryControlBGColor);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
color: #fff;
|
||||
color: var(--primaryControlFGColor);
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
float: right;
|
||||
@ -85,7 +85,7 @@
|
||||
}
|
||||
|
||||
.feedback:active {
|
||||
background-color: #0277d8;
|
||||
background-color: var(--primaryControlHoverColor);
|
||||
}
|
||||
|
||||
@media (max-device-width: 520px), (max-width: 520px) {
|
||||
|
@ -11,6 +11,28 @@ const assets = require('../../../common/assets');
|
||||
string with the value from package.json. 🤢
|
||||
*/
|
||||
const version = require('../../../package.json').version || 'VERSION';
|
||||
const browser = browserName();
|
||||
|
||||
module.exports = function(state) {
|
||||
return html`<header class="header">
|
||||
<div class="logo">
|
||||
<a class="logo__link" href="/">
|
||||
<img
|
||||
src="${assets.get('send_logo.svg')}"
|
||||
alt="Send"/>
|
||||
<h1 class="logo__title">Send</h1>
|
||||
</a>
|
||||
<div class="logo__subtitle">
|
||||
<a class="logo__subtitle-link" href="https://testpilot.firefox.com">Firefox Test Pilot</a>
|
||||
<div>${state.translate('siteSubtitle')}</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="https://qsurvey.mozilla.com/s3/txp-firefox-send?ver=${version}&browser=${browser}"
|
||||
rel="noreferrer noopener"
|
||||
class="feedback"
|
||||
target="_blank">${state.translate('siteFeedback')}</a>
|
||||
</header>`;
|
||||
};
|
||||
|
||||
function browserName() {
|
||||
try {
|
||||
@ -34,26 +56,3 @@ function browserName() {
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
const browser = browserName();
|
||||
|
||||
module.exports = function(state) {
|
||||
return html`<header class="header">
|
||||
<div class="logo">
|
||||
<a class="logo__link" href="/">
|
||||
<img
|
||||
src="${assets.get('send_logo.svg')}"
|
||||
alt="Send"/>
|
||||
<h1 class="logo__title">Send</h1>
|
||||
</a>
|
||||
<div class="logo__subtitle">
|
||||
<a class="logo__subtitle-link" href="https://testpilot.firefox.com">Firefox Test Pilot</a>
|
||||
<div>${state.translate('siteSubtitle')}</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="https://qsurvey.mozilla.com/s3/txp-firefox-send?ver=${version}&browser=${browser}"
|
||||
rel="noreferrer noopener"
|
||||
class="feedback"
|
||||
target="_blank">${state.translate('siteFeedback')}</a>
|
||||
</header>`;
|
||||
};
|
||||
|
@ -1,26 +1,54 @@
|
||||
const html = require('choo/html');
|
||||
|
||||
module.exports = function(placeholder, action, submit) {
|
||||
module.exports = function(file, state, emit) {
|
||||
const setting = state.settingPassword;
|
||||
const formClass = file.hasPassword
|
||||
? 'passwordInput'
|
||||
: 'passwordInput passwordInput--hidden';
|
||||
const inputClass = setting ? 'input input--copied' : 'input input--noBtn';
|
||||
const btnClass = setting
|
||||
? 'inputBtn inputBtn--loading'
|
||||
: 'inputBtn inputBtn--hidden';
|
||||
const action = file.hasPassword
|
||||
? state.translate('changePasswordButton')
|
||||
: state.translate('addPasswordButton');
|
||||
return html`
|
||||
<div>
|
||||
<form
|
||||
class="passwordInput passwordInput--hidden"
|
||||
onsubmit=${submit}
|
||||
class="${formClass}"
|
||||
onsubmit=${setPassword}
|
||||
data-no-csrf>
|
||||
<input id="password-input"
|
||||
class="input input--noBtn"
|
||||
${setting ? 'disabled' : ''}
|
||||
class="${inputClass}"
|
||||
maxlength="32"
|
||||
autocomplete="off"
|
||||
type="password"
|
||||
oninput=${inputChanged}
|
||||
placeholder="${placeholder}">
|
||||
placeholder="${
|
||||
file.hasPassword
|
||||
? passwordPlaceholder(file.password)
|
||||
: state.translate('unlockInputPlaceholder')
|
||||
}">
|
||||
<input type="submit"
|
||||
id="password-btn"
|
||||
class="inputBtn inputBtn--hidden"
|
||||
value="${action}"/>
|
||||
${setting ? 'disabled' : ''}
|
||||
class="${btnClass}"
|
||||
value="${setting ? '' : action}">
|
||||
</form>
|
||||
<div class="passwordInput__msg">${message(
|
||||
setting,
|
||||
file.hasPassword,
|
||||
state.translate('passwordIsSet')
|
||||
)}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
function inputChanged() {
|
||||
const pwdmsg = document.querySelector('.passwordInput__msg');
|
||||
if (pwdmsg) {
|
||||
pwdmsg.textContent = '';
|
||||
}
|
||||
const resetInput = document.getElementById('password-input');
|
||||
const resetBtn = document.getElementById('password-btn');
|
||||
if (resetInput.value.length > 0) {
|
||||
@ -31,4 +59,24 @@ module.exports = function(placeholder, action, submit) {
|
||||
resetInput.classList.add('input--noBtn');
|
||||
}
|
||||
}
|
||||
|
||||
function setPassword(event) {
|
||||
event.preventDefault();
|
||||
const password = document.getElementById('password-input').value;
|
||||
if (password.length > 0) {
|
||||
emit('password', { password, file });
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
function passwordPlaceholder(password) {
|
||||
return password ? password.replace(/./g, '●') : '●●●●●●●●●●●●';
|
||||
}
|
||||
|
||||
function message(setting, pwd, deflt) {
|
||||
if (setting || !pwd) {
|
||||
return '';
|
||||
}
|
||||
return deflt;
|
||||
}
|
||||
|
@ -3,16 +3,42 @@
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
width: 80%;
|
||||
padding: 10px 5px;
|
||||
padding: 10px 5px 5px;
|
||||
}
|
||||
|
||||
.passwordInput__msg {
|
||||
height: 100px;
|
||||
margin: 0 5px;
|
||||
font-size: 15px;
|
||||
color: var(--lightTextColor);
|
||||
}
|
||||
|
||||
.passwordInput--hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.inputBtn--loading {
|
||||
background-image: url('../assets/spinner.svg');
|
||||
background-position: center;
|
||||
background-size: 30px 30px;
|
||||
background-repeat: no-repeat;
|
||||
background-color: var(--successControlBGColor);
|
||||
border: 1px solid var(--successControlBGColor);
|
||||
color: var(--successControlFGColor);
|
||||
width: 10em;
|
||||
}
|
||||
|
||||
.inputBtn--loading:hover {
|
||||
background-color: var(--successControlBGColor);
|
||||
}
|
||||
|
||||
@media (max-device-width: 520px), (max-width: 520px) {
|
||||
.passwordInput {
|
||||
flex-direction: column;
|
||||
width: inherit;
|
||||
}
|
||||
|
||||
.inputBtn--loading {
|
||||
width: inherit;
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,6 @@
|
||||
const html = require('choo/html');
|
||||
|
||||
module.exports = function(msg, confirmText, cancelText, confirmCallback) {
|
||||
function hide(e) {
|
||||
e.stopPropagation();
|
||||
const popup = document.querySelector('.popup.popup--show');
|
||||
if (popup) {
|
||||
popup.classList.remove('popup--show');
|
||||
}
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="popup__wrapper">
|
||||
<div class="popup" onblur=${hide} tabindex="-1">
|
||||
@ -23,4 +15,12 @@ module.exports = function(msg, confirmText, cancelText, confirmCallback) {
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
function hide(e) {
|
||||
e.stopPropagation();
|
||||
const popup = document.querySelector('.popup.popup--show');
|
||||
if (popup) {
|
||||
popup.classList.remove('popup--show');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -2,8 +2,8 @@
|
||||
visibility: hidden;
|
||||
min-width: 204px;
|
||||
min-height: 105px;
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
background-color: var(--pageBGColor);
|
||||
color: var(--textColor);
|
||||
border: 1px solid #d7d7db;
|
||||
padding: 15px 24px;
|
||||
box-sizing: content-box;
|
||||
@ -82,7 +82,7 @@
|
||||
}
|
||||
|
||||
.popup__yes {
|
||||
color: #fff;
|
||||
color: var(--primaryControlFGColor);
|
||||
background-color: var(--primaryControlBGColor);
|
||||
border-radius: 5px;
|
||||
padding: 5px 25px;
|
||||
|
@ -9,7 +9,7 @@ const circumference = 2 * Math.PI * radius;
|
||||
module.exports = function(progressRatio) {
|
||||
const dashOffset = (1 - progressRatio) * circumference;
|
||||
const percentComplete = percent(progressRatio);
|
||||
const div = html`
|
||||
return html`
|
||||
<div class="progress">
|
||||
<svg
|
||||
width="${oDiameter}"
|
||||
@ -37,5 +37,4 @@ module.exports = function(progressRatio) {
|
||||
</svg>
|
||||
</div>
|
||||
`;
|
||||
return div;
|
||||
};
|
||||
|
@ -5,6 +5,25 @@ module.exports = function(selected, options, translate, changed) {
|
||||
const id = `select-${Math.random()}`;
|
||||
let x = selected;
|
||||
|
||||
return html`
|
||||
<div class="selectbox">
|
||||
<div onclick=${toggle}>
|
||||
<span class="link">${translate(selected)}</span>
|
||||
<svg width="32" height="32">
|
||||
<polygon points="8 18 17 28 26 18" fill="#0094fb"/>
|
||||
</svg>
|
||||
</div>
|
||||
<ul id="${id}" class="selectbox__options">
|
||||
${options.map(
|
||||
i => html`
|
||||
<li
|
||||
class="selectbox__option"
|
||||
onclick=${choose}
|
||||
data-value="${i}">${number(i)}</li>`
|
||||
)}
|
||||
</ul>
|
||||
</div>`;
|
||||
|
||||
function close() {
|
||||
const ul = document.getElementById(id);
|
||||
const body = document.querySelector('body');
|
||||
@ -37,21 +56,4 @@ module.exports = function(selected, options, translate, changed) {
|
||||
}
|
||||
close();
|
||||
}
|
||||
return html`
|
||||
<div class="selectbox">
|
||||
<div onclick=${toggle}>
|
||||
<span class="link">${translate(selected)}</span>
|
||||
<svg width="32" height="32">
|
||||
<polygon points="8 18 17 28 26 18" fill="#0094fb"/>
|
||||
</svg>
|
||||
</div>
|
||||
<ul id="${id}" class="selectbox__options">
|
||||
${options.map(
|
||||
i =>
|
||||
html`<li class="selectbox__option" onclick=${choose} data-value="${i}">${number(
|
||||
i
|
||||
)}</li>`
|
||||
)}
|
||||
</ul>
|
||||
</div>`;
|
||||
};
|
||||
|
@ -15,7 +15,7 @@
|
||||
left: 0;
|
||||
padding: 0;
|
||||
margin: 40px 0;
|
||||
background-color: white;
|
||||
background-color: var(--pageBGColor);
|
||||
border: 1px solid rgba(12, 12, 13, 0.3);
|
||||
border-radius: 4px;
|
||||
box-shadow: 1px 2px 4px rgba(12, 12, 13, 0.3);
|
||||
|
@ -3,10 +3,13 @@ const passwordInput = require('../passwordInput');
|
||||
|
||||
module.exports = function(state, emit) {
|
||||
const file = state.storage.getFileById(state.params.id);
|
||||
const div = html`
|
||||
|
||||
return html`
|
||||
<div class="setPasswordSection">
|
||||
<div class="checkbox">
|
||||
<input
|
||||
${file.hasPassword ? 'disabled' : ''}
|
||||
${file.hasPassword ? 'checked' : ''}
|
||||
class="checkbox__input"
|
||||
id="add-password"
|
||||
type="checkbox"
|
||||
@ -16,22 +19,9 @@ module.exports = function(state, emit) {
|
||||
${state.translate('requirePasswordCheckbox')}
|
||||
</label>
|
||||
</div>
|
||||
${passwordInput(
|
||||
state.translate('unlockInputPlaceholder'),
|
||||
state.translate('addPasswordButton'),
|
||||
addPassword
|
||||
)}
|
||||
${passwordInput(file, state, emit)}
|
||||
</div>`;
|
||||
|
||||
function addPassword(event) {
|
||||
event.preventDefault();
|
||||
const password = document.getElementById('password-input').value;
|
||||
if (password.length > 0) {
|
||||
emit('password', { password, file });
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function togglePasswordInput(e) {
|
||||
const unlockInput = document.getElementById('password-input');
|
||||
const boxChecked = e.target.checked;
|
||||
@ -44,6 +34,4 @@ module.exports = function(state, emit) {
|
||||
unlockInput.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
return div;
|
||||
};
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
.checkbox__input {
|
||||
position: absolute;
|
||||
visibility: collapse;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.checkbox__label {
|
||||
@ -31,12 +31,13 @@
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.checkbox__input:focus + .checkbox__label::before,
|
||||
.checkbox:hover .checkbox__label::before {
|
||||
border: 1px solid var(--primaryControlBGColor);
|
||||
}
|
||||
|
||||
.checkbox__input:checked + .checkbox__label {
|
||||
color: #000;
|
||||
color: var(--textColor);
|
||||
}
|
||||
|
||||
.checkbox__input:checked + .checkbox__label::before {
|
||||
@ -44,6 +45,19 @@
|
||||
background-position: 2px 1px;
|
||||
}
|
||||
|
||||
.checkbox__input:disabled + .checkbox__label {
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.checkbox__input:disabled + .checkbox__label::before {
|
||||
background-image: url('../assets/check-16.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 18px 18px;
|
||||
border-color: var(--successControlBGColor);
|
||||
background-color: var(--successControlBGColor);
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
@media (max-device-width: 520px), (max-width: 520px) {
|
||||
.setPasswordSection {
|
||||
align-self: center;
|
||||
|
17
assets/spinner.svg
Normal file
17
assets/spinner.svg
Normal file
@ -0,0 +1,17 @@
|
||||
<!-- By Sam Herbert (@sherb), for everyone. More @ http://goo.gl/7AJzbL -->
|
||||
<svg width="38" height="38" viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg" stroke="#fff">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<g transform="translate(1 1)" stroke-width="2">
|
||||
<circle stroke-opacity=".5" cx="18" cy="18" r="18"/>
|
||||
<path d="M36 18c0-9.94-8.06-18-18-18">
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
type="rotate"
|
||||
from="0 18 18"
|
||||
to="360 18 18"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"/>
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 694 B |
@ -113,3 +113,5 @@ enableJavascript = Please enable JavaScript and try again.
|
||||
expiresHoursMinutes = { $hours }h { $minutes }m
|
||||
# A short representation of a countdown timer containing the number of minutes remaining as digits, example "56m"
|
||||
expiresMinutes = { $minutes }m
|
||||
# A short status message shown when a password is successfully set
|
||||
passwordIsSet = Password set
|
||||
|
Loading…
Reference in New Issue
Block a user