From 43bfb6715c90d37b800013b4c363c1dac39336c9 Mon Sep 17 00:00:00 2001 From: aet Date: Sat, 2 Apr 2022 23:01:41 -0400 Subject: [PATCH] Add fancy error effect when user enters wrong password --- packages/web/src/index.scss | 48 ++++++++++--------- packages/web/src/pages/VaultPicker/Unlock.tsx | 9 +++- packages/web/src/pages/VaultPicker/index.tsx | 9 +++- .../web/src/utils/useWrongPasswordFeedback.ts | 48 +++++++++++++++++++ 4 files changed, 87 insertions(+), 27 deletions(-) create mode 100644 packages/web/src/utils/useWrongPasswordFeedback.ts diff --git a/packages/web/src/index.scss b/packages/web/src/index.scss index 63eac30..8df3fb0 100644 --- a/packages/web/src/index.scss +++ b/packages/web/src/index.scss @@ -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); diff --git a/packages/web/src/pages/VaultPicker/Unlock.tsx b/packages/web/src/pages/VaultPicker/Unlock.tsx index 787e9d5..914cf0b 100644 --- a/packages/web/src/pages/VaultPicker/Unlock.tsx +++ b/packages/web/src/pages/VaultPicker/Unlock.tsx @@ -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 onReturn(): void }> = ({ onUnlock, onReturn, instance, vaultPath }) => { const t = useTranslate() @@ -80,12 +81,14 @@ export const Unlock: React.FC<{ const [profile, setProfile] = useState() 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 }} /> diff --git a/packages/web/src/pages/VaultPicker/index.tsx b/packages/web/src/pages/VaultPicker/index.tsx index 69a9bc9..35d9da2 100644 --- a/packages/web/src/pages/VaultPicker/index.tsx +++ b/packages/web/src/pages/VaultPicker/index.tsx @@ -24,8 +24,13 @@ export const VaultPicker: React.FC = ({ 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] ) diff --git a/packages/web/src/utils/useWrongPasswordFeedback.ts b/packages/web/src/utils/useWrongPasswordFeedback.ts new file mode 100644 index 0000000..e44be88 --- /dev/null +++ b/packages/web/src/utils/useWrongPasswordFeedback.ts @@ -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 +}