-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathauthenticate.tsx
124 lines (108 loc) · 4.57 KB
/
authenticate.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import { Redirect, useLocalSearchParams } from 'expo-router'
import { TypedArrayEncoder, WalletInvalidKeyError } from '@credo-ts/core'
import { initializeAppAgent, useSecureUnlock } from '@easypid/agent'
import { useBiometricsType } from '@easypid/hooks/useBiometricsType'
import { secureWalletKey } from '@package/secure-store/secureUnlock'
import { FlexPage, Heading, HeroIcons, IconContainer, YStack, useDeviceMedia, useToastController } from '@package/ui'
import * as SplashScreen from 'expo-splash-screen'
import { PinDotsInput, type PinDotsInputRef } from 'packages/app/src'
import { useEffect, useRef, useState } from 'react'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { useResetWalletDevMenu } from '../utils/resetWallet'
/**
* Authenticate screen is redirect to from app layout when app is configured but locked
*/
export default function Authenticate() {
useResetWalletDevMenu()
const { redirectAfterUnlock } = useLocalSearchParams<{ redirectAfterUnlock?: string }>()
const toast = useToastController()
const secureUnlock = useSecureUnlock()
const biometricsType = useBiometricsType()
const pinInputRef = useRef<PinDotsInputRef>(null)
const { additionalPadding, noBottomSafeArea } = useDeviceMedia()
const [isInitializingAgent, setIsInitializingAgent] = useState(false)
const [isAllowedToUnlockWithFaceId, setIsAllowedToUnlockWithFaceId] = useState(false)
const isLoading =
secureUnlock.state === 'acquired-wallet-key' || (secureUnlock.state === 'locked' && secureUnlock.isUnlocking)
// After resetting the wallet, we want to avoid prompting for face id immediately
// So we add an artificial delay
useEffect(() => {
const timer = setTimeout(() => setIsAllowedToUnlockWithFaceId(true), 500)
return () => clearTimeout(timer)
}, [])
// biome-ignore lint/correctness/useExhaustiveDependencies: canTryUnlockingUsingBiometrics not needed
useEffect(() => {
if (secureUnlock.state === 'locked' && secureUnlock.canTryUnlockingUsingBiometrics && isAllowedToUnlockWithFaceId) {
secureUnlock.tryUnlockingUsingBiometrics()
}
}, [secureUnlock.state, isAllowedToUnlockWithFaceId])
useEffect(() => {
if (secureUnlock.state !== 'acquired-wallet-key') return
if (isInitializingAgent) return
setIsInitializingAgent(true)
initializeAppAgent({
walletKey: secureUnlock.walletKey,
walletKeyVersion: secureWalletKey.getWalletKeyVersion(),
})
.then((agent) => secureUnlock.setWalletKeyValid({ agent }, { enableBiometrics: true }))
.catch((error) => {
if (error instanceof WalletInvalidKeyError) {
secureUnlock.setWalletKeyInvalid()
pinInputRef.current?.clear()
pinInputRef.current?.shake()
}
// TODO: handle other
console.error(error)
})
.finally(() => {
setIsInitializingAgent(false)
})
}, [secureUnlock, isInitializingAgent])
if (secureUnlock.state === 'unlocked') {
// Expo and urls as query params don't go well together, so we encoded the url as base64
const redirect = redirectAfterUnlock
? TypedArrayEncoder.toUtf8String(TypedArrayEncoder.fromBase64(redirectAfterUnlock))
: '/'
return <Redirect href={redirect} />
}
if (secureUnlock.state === 'initializing' || secureUnlock.state === 'not-configured') {
return <Redirect href="/" />
}
void SplashScreen.hideAsync()
const unlockUsingBiometrics = async () => {
if (secureUnlock.state === 'locked') {
secureUnlock.tryUnlockingUsingBiometrics()
} else {
toast.show('You PIN is required to unlock the app', {
customData: {
preset: 'danger',
},
})
}
}
const unlockUsingPin = async (pin: string) => {
if (secureUnlock.state !== 'locked') return
await secureUnlock.unlockUsingPin(pin)
}
return (
<FlexPage flex-1 safeArea="y" alignItems="center">
<YStack fg={1} gap="$6" mb={noBottomSafeArea ? -additionalPadding : undefined}>
<YStack flex-1 alignItems="center" justifyContent="flex-end" gap="$4">
<IconContainer h="$4" w="$4" ai="center" jc="center" icon={<HeroIcons.LockClosedFilled />} />
<Heading variant="h2" fontWeight="$semiBold">
Enter your app PIN code
</Heading>
</YStack>
<PinDotsInput
isLoading={isLoading}
ref={pinInputRef}
pinLength={6}
onPinComplete={unlockUsingPin}
onBiometricsTap={unlockUsingBiometrics}
useNativeKeyboard={false}
biometricsType={biometricsType ?? 'fingerprint'}
/>
</YStack>
</FlexPage>
)
}