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);
}
:root {
--page-background: #fff;
--color: #000;
--titlebar-height: 46px;
--titlebar-height: 0px;
--label-background: #ddd;
--selected-background: #d5d5d5;
--hover-background: #ddd;
--border-color: #e3e3e3;
--sans-serif: -apple-system, BlinkMacSystemFont, system-ui, "Roboto", "Oxygen",
"Cantarell", "Droid Sans", "Helvetica Neue", "Noto Sans CJK JP", sans-serif;
--color: #000;
--hover-background: #ddd;
--label-background: #ddd;
--monospace: D2Coding, "source-code-pro", Menlo, Monaco, Consolas, "Courier New",
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) {
@ -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,
body,
#app {
@ -44,20 +60,6 @@ body,
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,
code {
font-family: var(--monospace);

View File

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

View File

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