Skip to content
This repository has been archived by the owner on Nov 5, 2023. It is now read-only.

Reopened: Make password creation form functional #112

Merged
merged 6 commits into from
Jan 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 107 additions & 21 deletions extension/source/QuillPage/components/PasswordCreationForm.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,113 @@
import * as React from 'react';

import measurePasswordStrength, {
PasswordStrength,
} from '../../helpers/measurePasswordStrength';
import QuickColumn from './QuickColumn';
import QuickRow from './QuickRow';

const PasswordCreationForm: React.FunctionComponent = () => (
<div className="password-creation-form quick-column">
<QuickColumn>
<input
type="password"
placeholder="Password"
style={{ width: '100%', flexGrow: 0 }}
/>
<input
type="password"
placeholder="Confirm password"
style={{ width: '100%', flexGrow: 0 }}
/>
<QuickRow>
<div>Password strength</div>
<div>Average</div>
</QuickRow>
<div>This password is ok, but could be more secure.</div>
</QuickColumn>
</div>
);
const passwordCommentaryMap: Record<PasswordStrength['descriptor'], string> = {
'Very weak': 'Please consider using a stronger password.',
Weak: 'Please consider using a stronger password.',
Average:
"This password is about average, which isn't great for protecting assets.",
Good: 'This is a relatively good password, but could be more secure.',
Strong: '',
};

const PasswordCreationForm: React.FunctionComponent<{
onPasswordUpdate: (password: string | undefined) => void;
}> = ({ onPasswordUpdate }) => {
const [password, setPassword] = React.useState<string>();

const [passwordFieldValue, setPasswordFieldValue] = React.useState('');
const [confirmPasswordFieldValue, setConfirmPasswordFieldValue] =
React.useState('');

function handleFieldsChange(newPassword: string, newConfirmPassword: string) {
if (newPassword !== passwordFieldValue) {
setPasswordFieldValue(newPassword);
}

if (newConfirmPassword !== confirmPasswordFieldValue) {
setConfirmPasswordFieldValue(newConfirmPassword);
}

const newResultPassword =
newPassword === newConfirmPassword ? newPassword : undefined;

if (newResultPassword !== password) {
setPassword(newResultPassword);
onPasswordUpdate(newResultPassword);
}
}

const passwordStrength = measurePasswordStrength(passwordFieldValue);

const confirmFieldClasses = ['password-confirm-field'];

if (confirmPasswordFieldValue === '') {
confirmFieldClasses.push('empty');
} else if (confirmPasswordFieldValue === passwordFieldValue) {
confirmFieldClasses.push('correct');
} else if (passwordFieldValue.startsWith(confirmPasswordFieldValue)) {
confirmFieldClasses.push('incomplete');
} else {
confirmFieldClasses.push('incorrect');
}

return (
<div
className={[
'password-creation-form',
'quick-column',
passwordStrength.descriptor.toLowerCase().replace(' ', '-'),
].join(' ')}
>
<QuickColumn>
<input
type="password"
placeholder="Password"
style={{ width: '100%', flexGrow: 0 }}
onInput={(e) => {
const newPassword = (e.target as HTMLInputElement).value;
handleFieldsChange(newPassword, confirmPasswordFieldValue);
}}
/>
<input
type="password"
placeholder="Confirm password"
className={confirmFieldClasses.join(' ')}
style={{ width: '100%', flexGrow: 0 }}
onInput={(e) => {
const newConfirmPassword = (e.target as HTMLInputElement).value;
handleFieldsChange(passwordFieldValue, newConfirmPassword);
}}
/>
<QuickRow>
<div>Password strength</div>
<div>{passwordStrength.descriptor}</div>
</QuickRow>
<div>
<div
className="password-meter"
style={{
width: `${passwordStrength.fillRatio * 100}%`,
}}
>
&nbsp;
</div>
<div>
Fill width: {(passwordStrength.fillRatio * 100).toFixed(1)}%
</div>
</div>
<div>
{passwordFieldValue &&
passwordCommentaryMap[passwordStrength.descriptor]}
</div>
</QuickColumn>
</div>
);
};

export default PasswordCreationForm;
48 changes: 26 additions & 22 deletions extension/source/QuillPage/components/PasswordCreationPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,32 @@ import PasswordCreationForm from './PasswordCreationForm';

const PasswordCreationPanel: React.FunctionComponent<{
onComplete: () => void;
}> = ({ onComplete }) => (
<>
<div className="instructions-text">
<h3>Let&apos;s start by setting a password.</h3>
<p>
Occasionally we will ask you for this to prevent unwanted access of your
wallets.
</p>
</div>
<PasswordCreationForm />
<div>
<div style={{ display: 'inline-block' }}>
<Button
onPress={onComplete}
className="btn-primary"
icon={<ArrowRight className="icon-md" />}
>
Continue
</Button>
}> = ({ onComplete }) => {
const [password, setPassword] = React.useState<string>();

return (
<>
<div className="instructions-text">
<h3>Let&apos;s start by setting a password.</h3>
<p>
Occasionally we will ask you for this to prevent unwanted access of
your wallets.
</p>
</div>
<PasswordCreationForm onPasswordUpdate={setPassword} />
<div>
<div style={{ display: 'inline-block' }}>
<Button
className={password === undefined ? 'btn-disabled' : 'btn-primary'}
onPress={() => password && onComplete()}
icon={<ArrowRight className="icon-md" />}
>
Continue
</Button>
</div>
</div>
</div>
</>
);
</>
);
};

export default PasswordCreationPanel;
23 changes: 23 additions & 0 deletions extension/source/QuillPage/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,27 @@
background-color: $primary-base;
}
}

.password-creation-form {
.password-meter {
display: inline-block;
background-color: grey;
color: white;
transition: width 200ms ease;
}

.password-confirm-field {
&.correct {
background-color: green;
}

&.incorrect {
background-color: red;
}

&.incomplete {
background-color: yellow;
}
}
}
}
2 changes: 1 addition & 1 deletion extension/source/helpers/measurePasswordStrength.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import zxcvbn from 'zxcvbn';

type PasswordStrength = {
export type PasswordStrength = {
guessesLog10: number;
descriptor: 'Very weak' | 'Weak' | 'Average' | 'Good' | 'Strong';
fillRatio: number;
Expand Down