Add fancy error effect when user enters wrong password
This commit is contained in:
parent
6dfbe2abda
commit
43bfb6715c
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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]
|
||||
)
|
||||
|
48
packages/web/src/utils/useWrongPasswordFeedback.ts
Normal file
48
packages/web/src/utils/useWrongPasswordFeedback.ts
Normal 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
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user