feat: auto-activate biometrics prompt when mobile regains focus (#1891)

This commit is contained in:
Aman Harwara
2022-10-27 02:02:33 +05:30
committed by GitHub
parent d817a2115e
commit 3bd1ad38ad
5 changed files with 72 additions and 10 deletions

View File

@@ -13,7 +13,7 @@ import {
TransferPayload,
UuidString,
} from '@standardnotes/snjs'
import { Alert, Linking, PermissionsAndroid, Platform, StatusBar } from 'react-native'
import { Alert, AppState, AppStateStatus, Linking, PermissionsAndroid, Platform, StatusBar } from 'react-native'
import FileViewer from 'react-native-file-viewer'
import FingerprintScanner from 'react-native-fingerprint-scanner'
import FlagSecure from 'react-native-flag-secure-android'
@@ -610,4 +610,8 @@ export class MobileDevice implements MobileDeviceInterface {
isUrlComponentUrl(url: string): boolean {
return Array.from(this.componentUrls.values()).includes(url)
}
async getAppState(): Promise<AppStateStatus> {
return AppState.currentState
}
}

View File

@@ -20,4 +20,5 @@ export interface MobileDeviceInterface extends DeviceInterface {
addComponentUrl(componentUuid: string, componentUrl: string): void
removeComponentUrl(componentUuid: string): void
isUrlComponentUrl(url: string): boolean
getAppState(): Promise<'active' | 'background' | 'inactive' | 'unknown' | 'extension'>
}

View File

@@ -35,7 +35,7 @@ import {
ThemeManager,
WebAlertService,
} from '@standardnotes/ui-services'
import { MobileWebReceiver } from '../NativeMobileWeb/MobileWebReceiver'
import { MobileWebReceiver, NativeMobileEventListener } from '../NativeMobileWeb/MobileWebReceiver'
import { AndroidBackHandler } from '@/NativeMobileWeb/AndroidBackHandler'
import { PrefDefaults } from '@/Constants/PrefDefaults'
import { setViewportHeightWithFallback } from '@/setViewportHeightWithFallback'
@@ -330,4 +330,12 @@ export class WebApplication extends SNApplication implements WebApplicationInter
openPurchaseFlow(): void {
this.getViewControllerManager().purchaseFlowController.openPurchaseFlow()
}
addNativeMobileEventListener = (listener: NativeMobileEventListener) => {
if (!this.mobileWebReceiver) {
return
}
return this.mobileWebReceiver.addReactListener(listener)
}
}

View File

@@ -1,5 +1,10 @@
import { ChallengePrompt, ChallengeValidation, ProtectionSessionDurations } from '@standardnotes/snjs'
import { FunctionComponent, useEffect, useRef } from 'react'
import {
ChallengePrompt,
ChallengeValidation,
ProtectionSessionDurations,
ReactNativeToWebEvent,
} from '@standardnotes/snjs'
import { FunctionComponent, useCallback, useEffect, useRef } from 'react'
import DecoratedInput from '@/Components/Input/DecoratedInput'
import DecoratedPasswordInput from '@/Components/Input/DecoratedPasswordInput'
import { ChallengeModalValues } from './ChallengeModalValues'
@@ -27,6 +32,40 @@ const ChallengeModalPrompt: FunctionComponent<Props> = ({
const inputRef = useRef<HTMLInputElement>(null)
const biometricsButtonRef = useRef<HTMLButtonElement>(null)
const activatePrompt = useCallback(async () => {
if (prompt.validation === ChallengeValidation.Biometric) {
if (application.isNativeMobileWeb()) {
const appState = await application.mobileDevice().getAppState()
if (appState !== 'active') {
return
}
}
biometricsButtonRef.current?.click()
} else {
inputRef.current?.focus()
}
}, [application, prompt.validation])
useEffect(() => {
if (!application.isNativeMobileWeb()) {
return
}
const disposeListener = application.addNativeMobileEventListener((event: ReactNativeToWebEvent) => {
if (event === ReactNativeToWebEvent.GainingFocus) {
void activatePrompt()
}
})
return () => {
if (disposeListener) {
disposeListener()
}
}
}, [activatePrompt, application])
useEffect(() => {
const isNotFirstPrompt = index !== 0
@@ -34,12 +73,8 @@ const ChallengeModalPrompt: FunctionComponent<Props> = ({
return
}
if (prompt.validation === ChallengeValidation.Biometric) {
biometricsButtonRef.current?.click()
} else {
inputRef.current?.focus()
}
}, [index, prompt.validation])
void activatePrompt()
}, [activatePrompt, index])
useEffect(() => {
if (isInvalid) {

View File

@@ -1,6 +1,10 @@
import { ReactNativeToWebEvent, WebApplicationInterface } from '@standardnotes/snjs'
export type NativeMobileEventListener = (event: ReactNativeToWebEvent) => void
export class MobileWebReceiver {
private listeners: Set<NativeMobileEventListener> = new Set()
constructor(private application: WebApplicationInterface) {
this.listenForNativeMobileEvents()
}
@@ -39,6 +43,14 @@ export class MobileWebReceiver {
}
}
addReactListener = (listener: NativeMobileEventListener) => {
this.listeners.add(listener)
return () => {
this.listeners.delete(listener)
}
}
handleNativeEvent(event: ReactNativeToWebEvent) {
switch (event) {
case ReactNativeToWebEvent.EnteringBackground:
@@ -60,5 +72,7 @@ export class MobileWebReceiver {
default:
break
}
this.listeners.forEach((listener) => listener(event))
}
}