mirror of
https://github.com/RSSNext/Folo.git
synced 2026-10-16 20:51:43 +08:00
fix(mobile): explain an invalid email once typing pauses
The Forgot Password and Edit Email hints waited for the field to lose focus. Android keeps it focused when Back closes the keyboard, as does tapping the Edit Email header, so Continue or Submit stayed disabled without a reason. The hint now also shows a second after typing stops. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import { useEffect } from "react"
|
||||
|
||||
// Long enough that a value still being typed is not flagged
|
||||
const TYPING_PAUSE_MS = 1000
|
||||
|
||||
/**
|
||||
* Marks a field as touched once typing pauses on an invalid value. A hint that waits for the field
|
||||
* to lose focus never shows on Android when Back closes the keyboard, as the field keeps focus, nor
|
||||
* while the field stays focused and a disabled header button is tapped.
|
||||
*/
|
||||
export const useTouchOnTypingPause = (
|
||||
value: string,
|
||||
isInvalid: boolean,
|
||||
setTouched: (touched: boolean) => void,
|
||||
) => {
|
||||
useEffect(() => {
|
||||
if (!value || !isInvalid) return
|
||||
const timer = setTimeout(() => setTouched(true), TYPING_PAUSE_MS)
|
||||
return () => clearTimeout(timer)
|
||||
}, [value, isInvalid, setTouched])
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
GroupedPlainButtonCell,
|
||||
} from "@/src/components/ui/grouped/GroupedList"
|
||||
import { Text } from "@/src/components/ui/typography/Text"
|
||||
import { useTouchOnTypingPause } from "@/src/hooks/useTouchOnTypingPause"
|
||||
import { isValidEmail } from "@/src/lib/email"
|
||||
import { useNavigation } from "@/src/lib/navigation/hooks"
|
||||
import type { NavigationControllerView } from "@/src/lib/navigation/types"
|
||||
@@ -31,11 +32,12 @@ export const EditEmailScreen: NavigationControllerView = () => {
|
||||
const [email, setEmail] = useState(whoami?.email ?? "")
|
||||
|
||||
const [isDirty, setIsDirty] = useState(false)
|
||||
// Set once the field is left, so the hint does not interrupt typing
|
||||
// Set once the field is left or typing pauses, so the hint does not interrupt typing
|
||||
const [isEmailTouched, setIsEmailTouched] = useState(false)
|
||||
const isValidate = whoami?.emailVerified
|
||||
|
||||
const newEmailIsValid = isValidEmail(email)
|
||||
useTouchOnTypingPause(isDirty ? email : "", !newEmailIsValid, setIsEmailTouched)
|
||||
|
||||
const navigation = useNavigation()
|
||||
const { mutate: updateEmail, isPending } = useMutation({
|
||||
|
||||
@@ -9,6 +9,7 @@ import { SubmitButton } from "@/src/components/common/SubmitButton"
|
||||
import { HeaderCloseOnly } from "@/src/components/layouts/header/HeaderElements"
|
||||
import { PlainTextField } from "@/src/components/ui/form/TextField"
|
||||
import { Text } from "@/src/components/ui/typography/Text"
|
||||
import { useTouchOnTypingPause } from "@/src/hooks/useTouchOnTypingPause"
|
||||
import { forgetPassword } from "@/src/lib/auth"
|
||||
import { isValidEmail } from "@/src/lib/email"
|
||||
import { useNavigation } from "@/src/lib/navigation/hooks"
|
||||
@@ -20,10 +21,11 @@ export const ForgetPasswordScreen: NavigationControllerView = () => {
|
||||
const insets = useSafeAreaInsets()
|
||||
const { t } = useTranslation()
|
||||
const [email, setEmail] = useState("")
|
||||
// Set once the field is left or submitted, so the hint does not interrupt typing
|
||||
// Set once the field is left or submitted or typing pauses, so the hint does not interrupt typing
|
||||
const [isEmailTouched, setIsEmailTouched] = useState(false)
|
||||
const trimmedEmail = email.trim()
|
||||
const isEmailValid = isValidEmail(trimmedEmail)
|
||||
useTouchOnTypingPause(trimmedEmail, !isEmailValid, setIsEmailTouched)
|
||||
const navigation = useNavigation()
|
||||
const contentContainerStyle = {
|
||||
flexGrow: 1,
|
||||
|
||||
Reference in New Issue
Block a user