Merge pull request #761 from mozilla/i741

added maxPasswordLength and passwordError messages
This commit is contained in:
Danny Coates 2018-02-22 09:10:26 -08:00 committed by GitHub
commit b39bbaf6fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 81 additions and 56 deletions

View File

@ -106,6 +106,10 @@ a {
padding-right: 10px; padding-right: 10px;
} }
.input--error {
border-color: var(--errorColor);
}
.input--noBtn { .input--noBtn {
border-radius: 6px; border-radius: 6px;
} }
@ -215,6 +219,10 @@ a {
} }
} }
.error {
color: var(--errorColor);
}
.title { .title {
font-size: 33px; font-size: 33px;
line-height: 40px; line-height: 40px;

View File

@ -135,9 +135,11 @@ export default function(state, emitter) {
state.storage.writeFile(file); state.storage.writeFile(file);
metrics.addedPassword({ size: file.size }); metrics.addedPassword({ size: file.size });
await delay(1000); await delay(1000);
state.settingPassword = false;
} catch (err) { } catch (err) {
console.error(err); console.error(err);
state.passwordSetError = err;
} finally {
state.settingPassword = false;
} }
render(); render();
}); });

View File

@ -21,11 +21,17 @@ export default class OwnedFile {
} }
async setPassword(password) { async setPassword(password) {
this.password = password; try {
this._hasPassword = true; this.password = password;
this.keychain.setPassword(password, this.url); this._hasPassword = true;
const result = await setPassword(this.id, this.ownerToken, this.keychain); this.keychain.setPassword(password, this.url);
return result; const result = await setPassword(this.id, this.ownerToken, this.keychain);
return result;
} catch (e) {
this.password = null;
this._hasPassword = false;
throw e;
}
} }
del() { del() {

View File

@ -21,11 +21,9 @@ module.exports = function(state, emit) {
<div class="uploadArea" <div class="uploadArea"
ondragover=${dragover} ondragover=${dragover}
ondragleave=${dragleave}> ondragleave=${dragleave}>
<div id="upload-img"> <img
<img src="${assets.get('upload.svg')}"
src="${assets.get('upload.svg')}" title="${state.translate('uploadSvgAlt')}"/>
title="${state.translate('uploadSvgAlt')}"/>
</div>
<div class="uploadArea__msg"> <div class="uploadArea__msg">
${state.translate('uploadPageDropMessage')} ${state.translate('uploadPageDropMessage')}
</div> </div>

View File

@ -11,14 +11,6 @@
padding: 10px 0; padding: 10px 0;
} }
.error {
color: var(--errorColor);
}
.input--error {
border-color: var(--errorColor);
}
@media (max-device-width: 520px), (max-width: 520px) { @media (max-device-width: 520px), (max-width: 520px) {
.passwordSection { .passwordSection {
width: 100%; width: 100%;

View File

@ -1,11 +1,13 @@
const html = require('choo/html'); const html = require('choo/html');
const MAX_LENGTH = 32;
module.exports = function(file, state, emit) { module.exports = function(file, state, emit) {
const loading = state.settingPassword; const loading = state.settingPassword;
const pwd = file.hasPassword; const pwd = file.hasPassword;
const formClass = pwd const sectionClass =
? 'passwordInput' pwd || state.passwordSetError
: 'passwordInput passwordInput--hidden'; ? 'passwordInput'
: 'passwordInput passwordInput--hidden';
const inputClass = loading || pwd ? 'input' : 'input input--noBtn'; const inputClass = loading || pwd ? 'input' : 'input input--noBtn';
let btnClass = 'inputBtn inputBtn--password inputBtn--hidden'; let btnClass = 'inputBtn inputBtn--password inputBtn--hidden';
if (loading) { if (loading) {
@ -13,26 +15,25 @@ module.exports = function(file, state, emit) {
} else if (pwd) { } else if (pwd) {
btnClass = 'inputBtn inputBtn--password'; btnClass = 'inputBtn inputBtn--password';
} }
const action = pwd const action = pwd
? state.translate('changePasswordButton') ? state.translate('changePasswordButton')
: state.translate('addPasswordButton'); : state.translate('addPasswordButton');
return html` return html`
<div> <div class="${sectionClass}">
<form <form
class="${formClass}" class="passwordInput__form"
onsubmit=${setPassword} onsubmit=${setPassword}
data-no-csrf> data-no-csrf>
<input id="password-input" <input id="password-input"
${loading ? 'disabled' : ''} ${loading ? 'disabled' : ''}
class="${inputClass}" class="${inputClass}"
maxlength="32" maxlength="${MAX_LENGTH}"
autocomplete="off" autocomplete="off"
type="password" type="password"
oninput=${inputChanged} oninput=${inputChanged}
onfocus=${focused} onfocus=${focused}
placeholder="${ placeholder="${
pwd pwd && !state.passwordSetError
? passwordPlaceholder(file.password) ? passwordPlaceholder(file.password)
: state.translate('unlockInputPlaceholder') : state.translate('unlockInputPlaceholder')
}"> }">
@ -42,22 +43,28 @@ module.exports = function(file, state, emit) {
class="${btnClass}" class="${btnClass}"
value="${loading ? '' : action}"> value="${loading ? '' : action}">
</form> </form>
<div class="passwordInput__msg">${message( <label
loading, class="passwordInput__msg ${
pwd, state.passwordSetError ? 'passwordInput__msg--error' : ''
state.translate('passwordIsSet') }"
)}</div> for="password-input">${message(state, pwd)}</label>
</div> </div>`;
`;
function inputChanged() { function inputChanged() {
const pwdmsg = document.querySelector('.passwordInput__msg'); state.passwordSetError = null;
if (pwdmsg) {
pwdmsg.textContent = '';
}
const resetInput = document.getElementById('password-input'); const resetInput = document.getElementById('password-input');
const resetBtn = document.getElementById('password-btn'); const resetBtn = document.getElementById('password-btn');
if (resetInput.value.length > 0) { const pwdmsg = document.querySelector('.passwordInput__msg');
const length = resetInput.value.length;
if (length === MAX_LENGTH) {
pwdmsg.textContent = state.translate('maxPasswordLength', {
length: MAX_LENGTH
});
} else {
pwdmsg.textContent = '';
}
if (length > 0) {
resetBtn.classList.remove('inputBtn--hidden'); resetBtn.classList.remove('inputBtn--hidden');
resetInput.classList.remove('input--noBtn'); resetInput.classList.remove('input--noBtn');
} else { } else {
@ -91,9 +98,12 @@ function passwordPlaceholder(password) {
return password ? password.replace(/./g, '●') : '●●●●●●●●●●●●'; return password ? password.replace(/./g, '●') : '●●●●●●●●●●●●';
} }
function message(loading, pwd, deflt) { function message(state, pwd) {
if (loading || !pwd) { if (state.passwordSetError) {
return state.translate('passwordSetError');
}
if (state.settingPassword || !pwd) {
return ''; return '';
} }
return deflt; return state.translate('passwordIsSet');
} }

View File

@ -1,21 +1,28 @@
.passwordInput { .passwordInput {
display: flex; width: 90%;
flex-wrap: nowrap;
width: 80%;
padding: 10px 5px 5px;
}
.passwordInput__msg {
height: 100px; height: 100px;
margin: 0 5px; padding: 10px 5px 5px;
font-size: 15px;
color: var(--lightTextColor);
} }
.passwordInput--hidden { .passwordInput--hidden {
visibility: hidden; visibility: hidden;
} }
.passwordInput__form {
display: flex;
flex-wrap: nowrap;
padding-bottom: 5px;
}
.passwordInput__msg {
font-size: 15px;
color: var(--lightTextColor);
}
.passwordInput__msg--error {
color: var(--errorColor);
}
.inputBtn--loading { .inputBtn--loading {
background-image: url('../assets/spinner.svg'); background-image: url('../assets/spinner.svg');
background-position: center; background-position: center;

View File

@ -9,7 +9,7 @@ module.exports = function(state, emit) {
<div class="checkbox"> <div class="checkbox">
<input <input
${file.hasPassword ? 'disabled' : ''} ${file.hasPassword ? 'disabled' : ''}
${file.hasPassword ? 'checked' : ''} ${file.hasPassword || state.passwordSetError ? 'checked' : ''}
class="checkbox__input" class="checkbox__input"
id="add-password" id="add-password"
type="checkbox" type="checkbox"
@ -26,7 +26,7 @@ module.exports = function(state, emit) {
const unlockInput = document.getElementById('password-input'); const unlockInput = document.getElementById('password-input');
const boxChecked = e.target.checked; const boxChecked = e.target.checked;
document document
.querySelector('form.passwordInput') .querySelector('.passwordInput')
.classList.toggle('passwordInput--hidden', !boxChecked); .classList.toggle('passwordInput--hidden', !boxChecked);
if (boxChecked) { if (boxChecked) {
unlockInput.focus(); unlockInput.focus();

View File

@ -103,8 +103,6 @@ requirePasswordCheckbox = Require a password to download this file
addPasswordButton = Add password addPasswordButton = Add password
changePasswordButton = Change changePasswordButton = Change
passwordTryAgain = Incorrect password. Try again. passwordTryAgain = Incorrect password. Try again.
# This label is followed by the password needed to download a file
passwordResult = Password: { $password }
reportIPInfringement = Report IP Infringement reportIPInfringement = Report IP Infringement
javascriptRequired = Firefox Send requires JavaScript javascriptRequired = Firefox Send requires JavaScript
whyJavascript = Why does Firefox Send require JavaScript? whyJavascript = Why does Firefox Send require JavaScript?
@ -115,3 +113,7 @@ expiresHoursMinutes = { $hours }h { $minutes }m
expiresMinutes = { $minutes }m expiresMinutes = { $minutes }m
# A short status message shown when a password is successfully set # A short status message shown when a password is successfully set
passwordIsSet = Password set passwordIsSet = Password set
# A short status message shown when the user enters a long password
maxPasswordLength = Maximum password length: { $length }
# A short status message shown when there was an error setting the password
passwordSetError = This password could not be set