Skip to content

fix: backgroundlock path state #337

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion packages/app/src/components/TextBackButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,16 @@ export function TextBackButton() {
const router = useRouter()
const { withHaptics } = useHaptics()

const handleBack = () => {
if (router.canGoBack()) {
router.back()
} else {
router.replace('/')
}
}

return (
<Button.Text color="$primary-500" fontWeight="$semiBold" onPress={withHaptics(() => router.back())} scaleOnPress>
<Button.Text color="$primary-500" fontWeight="$semiBold" onPress={withHaptics(handleBack)} scaleOnPress>
<HeroIcons.ArrowLeft mr={-4} color="$primary-500" strokeWidth={2} size={20} /> Back
</Button.Text>
)
Expand Down
23 changes: 17 additions & 6 deletions packages/app/src/provider/BackgroundLockProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,40 @@
import type { PropsWithChildren } from 'react'

import { TypedArrayEncoder } from '@credo-ts/core'
import { useSecureUnlock } from '@package/secure-store/secure-wallet-key/SecureUnlockProvider'
import { usePathname } from 'expo-router'
import { useRouter } from 'expo-router'
import { useEffect, useRef } from 'react'
import { type PropsWithChildren, useEffect, useRef } from 'react'
import { AppState, type AppStateStatus } from 'react-native'

const BACKGROUND_TIME_THRESHOLD = 60000 // 60 seconds
const BACKGROUND_TIME_THRESHOLD = 60000 // 1 minute

export function BackgroundLockProvider({ children }: PropsWithChildren) {
const router = useRouter()
const pathname = usePathname()
const secureUnlock = useSecureUnlock()
const backgroundTimeRef = useRef<Date | null>(null)
const lastPathRef = useRef<string | null>(null)

useEffect(() => {
const handleAppStateChange = (nextAppState: AppStateStatus) => {
if (nextAppState === 'background' || nextAppState === 'inactive') {
backgroundTimeRef.current = new Date()
lastPathRef.current = pathname
} else if (nextAppState === 'active') {
if (backgroundTimeRef.current) {
const timeInBackground = new Date().getTime() - backgroundTimeRef.current.getTime()

if (timeInBackground > BACKGROUND_TIME_THRESHOLD && secureUnlock.state === 'unlocked') {
console.log('App was in background for more than 30 seconds, locking')
const lastPath = lastPathRef.current
secureUnlock.lock()
router.replace('/authenticate')

if (lastPath && !['/', '/authenticate'].includes(lastPath)) {
// Encode the path for redirection after unlock
const encodedRedirect = TypedArrayEncoder.toBase64URL(TypedArrayEncoder.fromString(lastPath))
router.replace(`/authenticate?redirectAfterUnlock=${encodedRedirect}`)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the thing we want to fix is to handle a deeplinkg, but the wallet is closing the wallet on becoming active, so the deeplink isn't handled.

This may (not sure) fix the issue of a deeplink, but in case you were in a flow and went to the background, this won't work and require us to refactor a lot of of the logic, and will lead to bugs 🤔

So i think we need to know here whether we are handling a deeplink or not. if not, we should just go to authenticate, if we do, we need to extract the deeplink url and redirect to that aftewards

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed it to always require authentication when using a deeplink (easiest solution). The +native_intent runs first thing the app launches/comes alive, so there isn't a nice way to combine this with the current BackgroundLockProvider (e.g. not requiring authentication when authentication has been done < 60 seconds).

It think it is possible, but not sure if worth the hassle. Probably have to make a placeholder entry page for the deeplink routes where you check the last active time or something. Would also result in an extra spinner showing.

What do you think?

Copy link
Contributor Author

@janrtvld janrtvld Apr 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the flow bug with the background lock; you could handle inactive differently than background. E.g. when the modal opens to do the authorization code flow we have a state of inactive, and we could allow for a longer time (e.g. 2-3 minutes) then when you manually close the app into the background (1 minute).

I think if you want to keep the whole state, you'd have to make something of an authentication overlay where you enter your pin. With our current implementation I don't think it's possible as we unmount the actual route you're in.

} else {
router.replace('/authenticate')
}
}
backgroundTimeRef.current = null
}
Expand All @@ -35,7 +46,7 @@ export function BackgroundLockProvider({ children }: PropsWithChildren) {
return () => {
subscription.remove()
}
}, [secureUnlock, router])
}, [secureUnlock, router, pathname])

return <>{children}</>
}