fix(mobile): fix OTPWindow and integrate verification function

- Updated OTPWindow component to accept a generic verification function and handle success callbacks more flexibly.
- Refactored useTOTPModalWrapper to utilize the new OTPWindow for improved modal presentation and verification handling.
- Enhanced the animation settings for the modal to maintain consistency in user experience.

These changes streamline the two-factor authentication process and improve the overall usability of the OTP modal.

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei 2025-04-02 17:59:57 +08:00
parent 9be249dba7
commit e2cc31d4ba
No known key found for this signature in database
GPG Key ID: 0F62D33977F021F7
2 changed files with 34 additions and 34 deletions

View File

@ -1,5 +1,4 @@
import { useMutation } from "@tanstack/react-query"
import type { FC } from "react"
import { useEffect, useRef, useState } from "react"
import { Keyboard, Modal, StyleSheet, Text, useWindowDimensions, View } from "react-native"
import type { OtpInputRef } from "react-native-otp-entry"
@ -8,13 +7,16 @@ import Animated, { FadeIn, FadeOut, useSharedValue, withSpring } from "react-nat
import { useSafeAreaInsets } from "react-native-safe-area-context"
import { useColor } from "react-native-uikit-colors"
import { isAuthCodeValid, twoFactor } from "@/src/lib/auth"
import { isAuthCodeValid } from "@/src/lib/auth"
import { toast } from "@/src/lib/toast"
import { accentColor } from "@/src/theme/colors"
export const OTPWindow: FC<{
onSuccess: (token: string) => void
}> = ({ onSuccess }) => {
type OTPWindowProps<T> = {
onSuccess: (data: T) => void
verifyFn: (code: string) => Promise<T>
}
export const OTPWindow = <T,>({ onSuccess, verifyFn }: OTPWindowProps<T>) => {
const otpInputRef = useRef<OtpInputRef>(null)
const label = useColor("label")
const tertiaryLabel = useColor("tertiaryLabel")
@ -26,21 +28,12 @@ export const OTPWindow: FC<{
toast.error(`Failed to verify: ${error.message}`)
},
onSuccess(data) {
onSuccess(data.token)
onSuccess(data)
},
onSettled() {
setOpen(false)
},
mutationFn: async ({ code }: { code: string }) => {
const { data, error } = await twoFactor.verifyTotp({ code })
if (!data || error) {
const errorMessage = error?.message ?? "Invalid TOTP code"
toast.error(errorMessage)
throw new Error(errorMessage)
}
return data
},
mutationFn: ({ code }: { code: string }) => verifyFn(code),
})
const windowScaleValue = useSharedValue(0.9)
@ -53,12 +46,13 @@ export const OTPWindow: FC<{
}, [])
useEffect(() => {
const preset = { stiffness: 100, damping: 10 }
if (open) {
windowScaleValue.value = withSpring(1, { stiffness: 100, damping: 10 })
windowOpacityValue.value = withSpring(1, { stiffness: 100, damping: 10 })
windowScaleValue.value = withSpring(1, preset)
windowOpacityValue.value = withSpring(1, preset)
} else {
windowOpacityValue.value = withSpring(0, { stiffness: 100, damping: 10 })
windowScaleValue.value = withSpring(0.9, { stiffness: 100, damping: 10 })
windowOpacityValue.value = withSpring(0, preset)
windowScaleValue.value = withSpring(0.9, preset)
}
}, [open, windowOpacityValue, windowScaleValue])

View File

@ -1,4 +1,5 @@
import { useCallback } from "react"
import Siblings from "react-native-root-siblings"
import { getFetchErrorInfo } from "@/src/lib/error-parser"
import { useNavigation } from "@/src/lib/navigation/hooks"
@ -6,6 +7,8 @@ import { toast } from "@/src/lib/toast"
import { TwoFactorAuthScreen } from "@/src/screens/(modal)/2fa"
import { useWhoami } from "@/src/store/user/hooks"
import { OTPWindow } from "../compoents/OTPWindow"
export const useTOTPModalWrapper = <T extends { TOTPCode?: string }>(
callback: (input: T) => Promise<any>,
options?: { force?: boolean; dismiss?: () => any },
@ -24,20 +27,23 @@ export const useTOTPModalWrapper = <T extends { TOTPCode?: string }>(
return
}
// present({
// title: t("profile.totp_code.title"),
// content: ({ dismiss }) => {
// return createElement(TOTPForm, {
// async onSubmitMutationFn(values) {
// await callback({
// ...input,
// TOTPCode: values.code,
// })
// dismiss()
// },
// })
// },
// })
const root = new Siblings(
(
<OTPWindow
verifyFn={async (TOTPCode) => {
await callback({
...input,
TOTPCode,
})
root.destroy()
}}
onSuccess={async () => {
root.destroy()
}}
/>
),
)
}
if (options?.force) {