Skip to content
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

feat: add inline error messages for pin enter screen #1294

Closed
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const ToggleButton: React.FC<ToggleButtonProps> = ({

const backgroundColor = toggleAnim.interpolate({
inputRange: [0, 1],
outputRange: [ColorPallet.grayscale.lightGrey, ColorPallet.brand.primaryDisabled],
outputRange: [ColorPallet.grayscale.lightGrey, ColorPallet.brand.primary],
})

const translateX = toggleAnim.interpolate({
Expand Down
1 change: 1 addition & 0 deletions packages/legacy/core/App/components/inputs/PINInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const PINInputComponent = (
justifyContent: 'flex-start',
alignItems: 'center',
...PINInputTheme.cell,
borderColor: PINInputTheme.cell.backgroundColor
Comment on lines 53 to +54
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps this is a good opportunity to add a PINInputTheme.codeFieldRoot field since Bifold users have a need to style it for theming and it is a separate element from the cell.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@bryce-mcmath @MosCD3 I will close my PR as it conflicts with Mostafa's PR. However, this change in PINInput particularly, I will make changes to in separate PR @bryce-mcmath

},
codeFieldContainer: {
flex: 1,
Expand Down
2 changes: 2 additions & 0 deletions packages/legacy/core/App/localization/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ const translation = {
"Or": "Or",
"BiometricsUnlock": "Unlock with biometrics",
"IncorrectPIN": "Incorrect PIN",
"IncorrectPINTries": "Incorrect PIN: {{tries}} tries before timeout",
"LastTryBeforeTimeout": "Incorrect PIN: Last try before timeout",
"RepeatPIN": "Please try your PIN again.",
"EnableBiometrics": "You have to enable biometrics to be able to load the wallet.",
"BiometricsNotProvided": "Biometrics not provided, you may use PIN to load the wallet.",
Expand Down
2 changes: 2 additions & 0 deletions packages/legacy/core/App/localization/fr/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ const translation = {
"Or": "Ou",
"BiometricsUnlock": "Déverrouiller avec la biométrie",
"IncorrectPIN": "NIP erroné",
"IncorrectPINTries": "Code PIN incorrect : {{tries}} essaie avant l'expiration du délai",
"LastTryBeforeTimeout": "Code PIN incorrect : dernier essai avant expiration du délai",
"RepeatPIN": "Essayez votre NIP à nouveau",
"EnableBiometrics": "Vous devez activer la biométrie pour pouvoir charger le portefeuille.",
"BiometricsNotProvided": "Biométrie non fournie, vous pouvez utiliser le NIP pour vous connecter au portefeuille.",
Expand Down
61 changes: 39 additions & 22 deletions packages/legacy/core/App/screens/PINEnter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useNavigation, CommonActions } from '@react-navigation/native'
import React, { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Keyboard, StyleSheet, Text, View, DeviceEventEmitter, InteractionManager } from 'react-native'
import Icon from 'react-native-vector-icons/MaterialIcons'

import Button, { ButtonType } from '../components/buttons/Button'
import PINInput from '../components/inputs/PINInput'
Expand Down Expand Up @@ -43,6 +44,7 @@ const PINEnter: React.FC<PINEnterProps> = ({ setAuthenticated, usage = PINEntryU
const [PIN, setPIN] = useState<string>('')
const [continueEnabled, setContinueEnabled] = useState(true)
const [displayLockoutWarning, setDisplayLockoutWarning] = useState(false)
const [errorMessage, setErrorMessage] = useState<string | null>(null) // State for inline error message
const [biometricsErr, setBiometricsErr] = useState(false)
const navigation = useNavigation()
const [alertModalVisible, setAlertModalVisible] = useState<boolean>(false)
Expand Down Expand Up @@ -96,7 +98,21 @@ const PINEnter: React.FC<PINEnterProps> = ({ setAuthenticated, usage = PINEntryU
},
subText: {
...TextTheme.bold,
marginBottom: 20,
marginBottom: 8,
},
errorText: {
...TextTheme.normal,
color: ColorPallet.semantic.error,
marginBottom: 4,
},
inlineErrorContainer: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 12,
},
inlineErrorSvg: {
color: ColorPallet.semantic.error,
margin: 2,
},
})

Expand Down Expand Up @@ -242,14 +258,21 @@ const PINEnter: React.FC<PINEnterProps> = ({ setAuthenticated, usage = PINEntryU

if (!result) {
const newAttempt = store.loginAttempt.loginAttempts + 1
if (!getLockoutPenalty(newAttempt)) {
// skip displaying modals if we are going to lockout
setAlertModalVisible(true)
const attemptsLeft = 5 - newAttempt // 5 total attempts allowed

if (attemptsLeft > 1) {
setErrorMessage(t('PINEnter.IncorrectPINTries', { tries: attemptsLeft })) // Example: 'Incorrect PIN: 4 tries before timeout'
} else if (attemptsLeft === 1) {
setErrorMessage(t('PINEnter.LastTryBeforeTimeout')) // Show last try warning
} else {
const penalty = getLockoutPenalty(newAttempt)
if (penalty !== undefined) {
attemptLockout(penalty) // Only call attemptLockout if penalty is defined
}
return
}

setContinueEnabled(true)

// log incorrect login attempts
dispatch({
type: DispatchAction.ATTEMPT_UPDATED,
payload: [{ loginAttempts: newAttempt }],
Expand All @@ -258,13 +281,12 @@ const PINEnter: React.FC<PINEnterProps> = ({ setAuthenticated, usage = PINEntryU
return
}

// reset login attempts if login is successful
// If PIN is correct, reset attempts and clear error messages
dispatch({
type: DispatchAction.ATTEMPT_UPDATED,
payload: [{ loginAttempts: 0 }],
})

// remove lockout notification if login is successful
dispatch({
type: DispatchAction.LOCKOUT_UPDATED,
payload: [{ displayNotification: false }],
Expand All @@ -286,29 +308,18 @@ const PINEnter: React.FC<PINEnterProps> = ({ setAuthenticated, usage = PINEntryU
checkPIN,
store.loginAttempt,
unMarkServedPenalty,
getLockoutPenalty,
dispatch,
setAuthenticated,
gotoPostAuthScreens,
t,
attemptLockout,
getLockoutPenalty,
]
)

const clearAlertModal = useCallback(() => {
switch (usage) {
case PINEntryUsage.PINCheck:
setAlertModalVisible(false)
setAuthenticated(false)
break

default:
setAlertModalVisible(false)

break
}

setAlertModalVisible(false)
}, [usage, setAuthenticated])
}, [])

const verifyPIN = useCallback(
async (PIN: string) => {
Expand Down Expand Up @@ -410,6 +421,12 @@ const PINEnter: React.FC<PINEnterProps> = ({ setAuthenticated, usage = PINEntryU
{/* <Image source={Assets.img.logoSecondary.src} style={style.image} /> */}
{displayHelpText()}
<Text style={style.subText}>{t('PINEnter.EnterPIN')}</Text>
{errorMessage && (
<View style={style.inlineErrorContainer}>
<Icon name="error" style={style.inlineErrorSvg} size={24} />
<Text style={style.errorText}>{errorMessage}</Text>
</View>
)}
<PINInput
onPINChanged={(p: string) => {
setPIN(p)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ exports[`ToggleButton Component renders correctly when enabled 1`] = `
collapsable={false}
style={
Object {
"backgroundColor": "rgba(53, 130, 63, 0.349)",
"backgroundColor": "rgba(66, 128, 62, 1)",
"borderRadius": 25,
"height": 30,
"justifyContent": "center",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ exports[`PINEnter Screen PIN Enter renders correctly 1`] = `
"color": "#FFFFFF",
"fontSize": 18,
"fontWeight": "bold",
"marginBottom": 20,
"marginBottom": 8,
}
}
>
Expand All @@ -107,7 +107,7 @@ exports[`PINEnter Screen PIN Enter renders correctly 1`] = `
Object {
"alignItems": "center",
"backgroundColor": "#313132",
"borderColor": "#FFFFFFFF",
"borderColor": "#313132",
"borderRadius": 5,
"borderWidth": 1,
"flexDirection": "row",
Expand Down Expand Up @@ -139,7 +139,7 @@ exports[`PINEnter Screen PIN Enter renders correctly 1`] = `
Object {
"alignItems": "center",
"backgroundColor": "#313132",
"borderColor": "#FFFFFFFF",
"borderColor": "#313132",
"borderRadius": 5,
"borderWidth": 1,
"flexDirection": "row",
Expand Down Expand Up @@ -571,7 +571,7 @@ exports[`PINEnter Screen PIN Enter renders correctly when logged out message is
"color": "#FFFFFF",
"fontSize": 18,
"fontWeight": "bold",
"marginBottom": 20,
"marginBottom": 8,
}
}
>
Expand All @@ -598,7 +598,7 @@ exports[`PINEnter Screen PIN Enter renders correctly when logged out message is
Object {
"alignItems": "center",
"backgroundColor": "#313132",
"borderColor": "#FFFFFFFF",
"borderColor": "#313132",
"borderRadius": 5,
"borderWidth": 1,
"flexDirection": "row",
Expand Down Expand Up @@ -630,7 +630,7 @@ exports[`PINEnter Screen PIN Enter renders correctly when logged out message is
Object {
"alignItems": "center",
"backgroundColor": "#313132",
"borderColor": "#FFFFFFFF",
"borderColor": "#313132",
"borderRadius": 5,
"borderWidth": 1,
"flexDirection": "row",
Expand Down
Loading