updated password input UI
This commit is contained in:
parent
8d41111cd6
commit
346e604f34
37 changed files with 282 additions and 288 deletions
app
base.cssfileManager.jsmain.css
pages
templates
changePasswordSection
downloadButton
file
fileList
footer
fxPromo
header
passwordInput
popup
progress
selectbox
setPasswordSection
assets
public/locales/en-US
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 (image error) 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 a new issue