Add fancy error effect when user enters wrong password

This commit is contained in:
aet 2022-04-02 23:01:41 -04:00
parent 6dfbe2abda
commit 43bfb6715c
4 changed files with 87 additions and 27 deletions

View File

@ -13,18 +13,19 @@ body {
font-family: var(--sans-serif); font-family: var(--sans-serif);
} }
:root { :root {
--page-background: #fff;
--color: #000;
--titlebar-height: 46px;
--titlebar-height: 0px;
--label-background: #ddd;
--selected-background: #d5d5d5;
--hover-background: #ddd;
--border-color: #e3e3e3; --border-color: #e3e3e3;
--sans-serif: -apple-system, BlinkMacSystemFont, system-ui, "Roboto", "Oxygen", --color: #000;
"Cantarell", "Droid Sans", "Helvetica Neue", "Noto Sans CJK JP", sans-serif; --hover-background: #ddd;
--label-background: #ddd;
--monospace: D2Coding, "source-code-pro", Menlo, Monaco, Consolas, "Courier New", --monospace: D2Coding, "source-code-pro", Menlo, Monaco, Consolas, "Courier New",
monospace; monospace;
--page-background: #fff;
--sans-serif: -apple-system, BlinkMacSystemFont, system-ui, "Roboto", "Oxygen",
"Cantarell", "Droid Sans", "Helvetica Neue", "Noto Sans CJK JP", sans-serif;
--selected-background: #d5d5d5;
--red-border: #fa144d;
--titlebar-height: 0px;
--titlebar-height: 46px;
} }
@media (prefers-color-scheme: light) { @media (prefers-color-scheme: light) {
@ -33,6 +34,21 @@ body {
} }
} }
@media (prefers-color-scheme: dark) {
:root {
--color: #fff;
--label-background: #353535;
--selected-background: #353535;
--border-color: #333;
--hover-background: #222;
--page-background: #292929;
--red-border: #9e1641;
}
body {
color: #fff;
}
}
html, html,
body, body,
#app { #app {
@ -44,20 +60,6 @@ body,
position: relative; position: relative;
} }
@media (prefers-color-scheme: dark) {
:root {
--color: #fff;
--label-background: #353535;
--selected-background: #353535;
--border-color: #333;
--hover-background: #222;
--page-background: #292929;
}
body {
color: #fff;
}
}
pre, pre,
code { code {
font-family: var(--monospace); font-family: var(--monospace);

View File

@ -4,6 +4,7 @@ import React, { useCallback, useEffect, useState } from "react"
import { IoMdArrowRoundBack } from "react-icons/io" import { IoMdArrowRoundBack } from "react-icons/io"
import { FaUnlock } from "react-icons/fa" import { FaUnlock } from "react-icons/fa"
import { useTranslate } from "../../i18n" import { useTranslate } from "../../i18n"
import { useWrongPasswordFeedback } from "../../utils/useWrongPasswordFeedback"
const Container = styled.div` const Container = styled.div`
padding: 20px; padding: 20px;
@ -72,7 +73,7 @@ const VaultPath = styled.div`
export const Unlock: React.FC<{ export const Unlock: React.FC<{
instance: OnePassword instance: OnePassword
vaultPath: string vaultPath: string
onUnlock(profile: string, password: string): void onUnlock(profile: string, password: string): Promise<boolean>
onReturn(): void onReturn(): void
}> = ({ onUnlock, onReturn, instance, vaultPath }) => { }> = ({ onUnlock, onReturn, instance, vaultPath }) => {
const t = useTranslate() const t = useTranslate()
@ -80,12 +81,14 @@ export const Unlock: React.FC<{
const [profile, setProfile] = useState<string>() const [profile, setProfile] = useState<string>()
const [password, setPassword] = useState("") const [password, setPassword] = useState("")
const [{ animation, error }, setFeedback] = useWrongPasswordFeedback()
const unlock = useCallback( const unlock = useCallback(
(e?: React.FormEvent) => { (e?: React.FormEvent) => {
e?.preventDefault() e?.preventDefault()
if (!profile) return if (!profile) return
onUnlock(profile, password) onUnlock(profile, password).then(setFeedback)
setPassword("") setPassword("")
}, },
[onUnlock, profile, password] [onUnlock, profile, password]
@ -132,12 +135,14 @@ export const Unlock: React.FC<{
onChange={e => setPassword(e.currentTarget.value)} onChange={e => setPassword(e.currentTarget.value)}
placeholder={t.label.password_placeholder} placeholder={t.label.password_placeholder}
onKeyUp={onKeyUp} onKeyUp={onKeyUp}
style={{ animation, borderColor: error ? "var(--red-border)" : undefined }}
/> />
<Submit <Submit
type="submit" type="submit"
disabled={!profile || !password} disabled={!profile || !password}
onClick={unlock} onClick={unlock}
title={t.action.unlock} title={t.action.unlock}
style={{ animation }}
> >
<FaUnlock /> <FaUnlock />
</Submit> </Submit>

View File

@ -24,8 +24,13 @@ export const VaultPicker: React.FC<VaultPickerProps> = ({
const unlock = useCallback( const unlock = useCallback(
async (profile: string, password: string) => { async (profile: string, password: string) => {
const vault = await instance!.getProfile(profile!) const vault = await instance!.getProfile(profile!)
await vault.unlock(password) try {
setVault(vault) await vault.unlock(password)
setVault(vault)
return true
} catch {
return false
}
}, },
[instance, setVault] [instance, setVault]
) )

View File

@ -0,0 +1,48 @@
import styled from "@emotion/styled"
import { keyframes } from "@emotion/css"
import React, { useCallback, useRef, useState } from "react"
const shake = keyframes`
8%, 41% {
transform: translateX(-10px);
}
25%, 58% {
transform: translateX(10px);
}
75% {
transform: translateX(-5px);
}
92% {
transform: translateX(5px);
}
0%, 100% {
transform: translateX(0);
}
`
const animation = `${shake} .5s linear`
export function useWrongPasswordFeedback() {
const timeout = useRef(0)
const [show, setShow] = useState(false)
const callback = useCallback((success: boolean) => {
setShow(!success)
if (timeout.current) {
window.clearTimeout(timeout.current)
}
timeout.current = window.setTimeout(() => {
setShow(false)
}, 500)
}, [])
return [
{
animation: show ? animation : undefined,
error: show,
},
callback,
] as const
}