diff --git a/src/assets/images/biometric-image.svg b/src/assets/images/biometric-image.svg
new file mode 100644
index 000000000..f608622c6
--- /dev/null
+++ b/src/assets/images/biometric-image.svg
@@ -0,0 +1,14 @@
+
diff --git a/src/assets/images/password-ico.svg b/src/assets/images/password-ico.svg
new file mode 100644
index 000000000..035444214
--- /dev/null
+++ b/src/assets/images/password-ico.svg
@@ -0,0 +1,13 @@
+
diff --git a/src/assets/images/pin-icon.svg b/src/assets/images/pin-icon.svg
new file mode 100644
index 000000000..fca778f41
--- /dev/null
+++ b/src/assets/images/pin-icon.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/components/ConfirmCredentialModal.tsx b/src/components/ConfirmCredentialModal.tsx
new file mode 100644
index 000000000..19b751418
--- /dev/null
+++ b/src/components/ConfirmCredentialModal.tsx
@@ -0,0 +1,55 @@
+import { Box } from 'native-base';
+import React from 'react';
+import { useSelector } from 'react-redux';
+import LoginMethod from 'src/models/enums/LoginMethod';
+import { useAppSelector } from 'src/store/hooks';
+import PasscodeVerifyModal from './Modal/PasscodeVerify';
+import PasswordModalContent from 'src/screens/AppSettings/PasswordModalContent';
+
+type Props = {
+ close?: () => void;
+ success?: any;
+ useBiometrics?: boolean;
+ forcedMode?: any;
+ onForceSuccess?: any;
+ primaryText?: string;
+};
+
+const ConfirmCredentialModal = ({
+ success,
+ close,
+ useBiometrics,
+ forcedMode,
+ onForceSuccess,
+ primaryText,
+}: Props) => {
+ const { loginMethod }: { loginMethod: LoginMethod } = useAppSelector((state) => state.settings);
+ const fallbackLoginMethod = useSelector((state) => state.settings.fallbackLoginMethod);
+
+ return (
+
+ {(loginMethod === LoginMethod.BIOMETRIC && fallbackLoginMethod === 'PIN') ||
+ (loginMethod !== LoginMethod.BIOMETRIC && loginMethod === LoginMethod.PIN) ? (
+ {
+ close();
+ }}
+ forcedMode={forcedMode}
+ onForceSuccess={onForceSuccess}
+ onSuccess={success}
+ primaryText={primaryText}
+ />
+ ) : (
+ {
+ close();
+ }}
+ onSuccess={success}
+ />
+ )}
+
+ );
+};
+
+export default ConfirmCredentialModal;
diff --git a/src/components/Modal/PasscodeVerify.tsx b/src/components/Modal/PasscodeVerify.tsx
index 93d523648..e4cfaf6cf 100644
--- a/src/components/Modal/PasscodeVerify.tsx
+++ b/src/components/Modal/PasscodeVerify.tsx
@@ -46,7 +46,7 @@ function PasscodeVerifyModal({
const [errMessage, setErrMessage] = useState(login.Incorrect);
const { isAuthenticated, authenticationFailed } = useAppSelector((state) => state.login);
const { loginMethod } = useAppSelector((state) => state.settings);
- const { appId, failedAttempts, lastLoginFailedAt } = useAppSelector((state) => state.storage);
+ const { appId } = useAppSelector((state) => state.storage);
useEffect(() => {
if (useBiometrics) {
diff --git a/src/components/RKSignersModal.tsx b/src/components/RKSignersModal.tsx
index 10000a987..ee4dcd8f3 100644
--- a/src/components/RKSignersModal.tsx
+++ b/src/components/RKSignersModal.tsx
@@ -14,7 +14,6 @@ import { CKTapCard } from 'cktap-protocol-react-native';
import useNfcModal from 'src/hooks/useNfcModal';
import NfcPrompt from 'src/components/NfcPromptAndroid';
import KeeperModal from 'src/components/KeeperModal';
-import PasscodeVerifyModal from 'src/components/Modal/PasscodeVerify';
import { Box, useColorMode } from 'native-base';
import { SIGNTRANSACTION } from 'src/navigation/contants';
import { useDispatch } from 'react-redux';
@@ -30,6 +29,7 @@ import { KeeperApp } from 'src/models/interfaces/KeeperApp';
import { LocalizationContext } from 'src/context/Localization/LocContext';
import { useAppSelector } from 'src/store/hooks';
import ShareKeyModalContent from 'src/screens/Vault/components/ShareKeyModalContent';
+import ConfirmCredentialModal from './ConfirmCredentialModal';
import Text from './KeeperText';
import WalletOperations from 'src/services/wallets/operations';
import ActivityIndicatorView from './AppActivityIndicator/ActivityIndicatorView';
@@ -388,12 +388,10 @@ const RKSignersModal = ({ signer, psbt, isMiniscript, vaultId }, ref) => {
textColor={`${colorMode}.textGreen`}
subTitleColor={`${colorMode}.modalSubtitleBlack`}
Content={() => (
- setConfirmPassVisible(false)}
+ success={signTransaction}
useBiometrics={false}
- close={() => {
- setConfirmPassVisible(false);
- }}
- onSuccess={signTransaction}
/>
)}
/>
diff --git a/src/models/enums/LoginMethod.ts b/src/models/enums/LoginMethod.ts
index cafce6405..2c89d61b4 100644
--- a/src/models/enums/LoginMethod.ts
+++ b/src/models/enums/LoginMethod.ts
@@ -1,5 +1,6 @@
enum LoginMethod {
PIN = 'PIN',
BIOMETRIC = 'BIOMETRIC',
+ PASSWORD = 'PASSWORD',
}
export default LoginMethod;
diff --git a/src/screens/AppSettings/AppBackupSettings.tsx b/src/screens/AppSettings/AppBackupSettings.tsx
index 7b72701ea..1a903a3c7 100644
--- a/src/screens/AppSettings/AppBackupSettings.tsx
+++ b/src/screens/AppSettings/AppBackupSettings.tsx
@@ -6,7 +6,6 @@ import { CommonActions, useNavigation } from '@react-navigation/native';
import OptionCard from 'src/components/OptionCard';
import KeeperModal from 'src/components/KeeperModal';
import ScreenWrapper from 'src/components/ScreenWrapper';
-import PasscodeVerifyModal from 'src/components/Modal/PasscodeVerify';
import { wp } from 'src/constants/responsive';
import { LocalizationContext } from 'src/context/Localization/LocContext';
import { RealmSchema } from 'src/storage/realm/enum';
@@ -31,6 +30,7 @@ import { NewVaultInfo } from 'src/store/sagas/wallets';
import BackupModalContent from './BackupModal';
import { credsAuthenticated } from 'src/store/reducers/login';
import WalletHeader from 'src/components/WalletHeader';
+import ConfirmCredentialModal from 'src/components/ConfirmCredentialModal';
function AppBackupSettings() {
const { colorMode } = useColorMode();
@@ -160,15 +160,15 @@ function AppBackupSettings() {
textColor={`${colorMode}.textGreen`}
subTitleColor={`${colorMode}.modalSubtitleBlack`}
Content={() => (
- {
setConfirmPassVisible(false);
}}
- onSuccess={() => {
+ success={() => {
setConfirmPassVisible(false);
setBackupModalVisible(true);
}}
+ useBiometrics={true}
/>
)}
/>
diff --git a/src/screens/AppSettings/CreatePasswordContent.tsx b/src/screens/AppSettings/CreatePasswordContent.tsx
new file mode 100644
index 000000000..560de2d0a
--- /dev/null
+++ b/src/screens/AppSettings/CreatePasswordContent.tsx
@@ -0,0 +1,104 @@
+import { Box, useColorMode } from 'native-base';
+import React, { useContext, useState } from 'react';
+import { StyleSheet } from 'react-native';
+import { useDispatch } from 'react-redux';
+import Buttons from 'src/components/Buttons';
+import Text from 'src/components/KeeperText';
+import KeeperTextInput from 'src/components/KeeperTextInput';
+import { hp } from 'src/constants/responsive';
+import { LocalizationContext } from 'src/context/Localization/LocContext';
+import LoginMethod from 'src/models/enums/LoginMethod';
+import { useAppSelector } from 'src/store/hooks';
+import { setFallbackLoginMethod } from 'src/store/reducers/settings';
+import { changeAuthCred } from 'src/store/sagaActions/login';
+
+interface Props {
+ close?: () => void;
+ onSuccess?: (method: LoginMethod) => void;
+ oldPassword?: string;
+}
+
+const CreatePasswordContent = ({ close, onSuccess, oldPassword }: Props) => {
+ const { colorMode } = useColorMode();
+ const { translations } = useContext(LocalizationContext);
+ const { common } = translations;
+ const dispatch = useDispatch();
+
+ const [password, setPassword] = useState('');
+ const [confirmPassword, setConfirmPassword] = useState('');
+ const [error, setError] = useState('');
+ const { loginMethod }: { loginMethod: LoginMethod } = useAppSelector((state) => state.settings);
+
+ const handleContinue = () => {
+ if (!password || !confirmPassword) {
+ setError('Please fill out both fields');
+ } else if (password !== confirmPassword) {
+ setError('Passwords do not match');
+ } else {
+ setError('');
+ dispatch(changeAuthCred(oldPassword, password));
+ if (loginMethod === LoginMethod.BIOMETRIC) {
+ dispatch(setFallbackLoginMethod(LoginMethod.PASSWORD));
+ }
+ onSuccess(LoginMethod.PASSWORD);
+ close();
+ }
+ };
+
+ return (
+
+
+
+ {!!error && (
+
+ {error}
+
+ )}
+
+
+
+
+ );
+};
+
+export default CreatePasswordContent;
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ marginTop: hp(-10),
+ },
+ btnsContainer: {
+ marginTop: hp(20),
+ },
+ errorText: {
+ textAlign: 'right',
+ fontStyle: 'italic',
+ marginRight: 10,
+ marginTop: 8,
+ },
+});
diff --git a/src/screens/AppSettings/ManageWallets.tsx b/src/screens/AppSettings/ManageWallets.tsx
index ef9330265..c4920d47a 100644
--- a/src/screens/AppSettings/ManageWallets.tsx
+++ b/src/screens/AppSettings/ManageWallets.tsx
@@ -21,7 +21,6 @@ import KeeperModal from 'src/components/KeeperModal';
import { captureError } from 'src/services/sentry';
import useWallets from 'src/hooks/useWallets';
import { useDispatch, useSelector } from 'react-redux';
-import PasscodeVerifyModal from 'src/components/Modal/PasscodeVerify';
import useVault from 'src/hooks/useVault';
import { Vault } from 'src/services/wallets/interfaces/vault';
import HexagonIcon from 'src/components/HexagonIcon';
@@ -45,6 +44,7 @@ import MiniscriptPathSelector, {
} from 'src/components/MiniscriptPathSelector';
import WalletHeader from 'src/components/WalletHeader';
import ThemedColor from 'src/components/ThemedColor/ThemedColor';
+import ConfirmCredentialModal from 'src/components/ConfirmCredentialModal';
enum PasswordMode {
DEFAULT = 'DEFAULT',
@@ -413,13 +413,13 @@ function ManageWallets() {
textColor={`${colorMode}.textGreen`}
subTitleColor={`${colorMode}.modalSubtitleBlack`}
Content={() => (
- {
setConfirmPassVisible(false);
}}
- onSuccess={onProceed}
+ success={onProceed}
+ useBiometrics={false}
onForceSuccess={onForceProceed}
/>
)}
@@ -435,12 +435,12 @@ function ManageWallets() {
textColor={`${colorMode}.textGreen`}
subTitleColor={`${colorMode}.modalSubtitleBlack`}
Content={() => (
- {
setConfirmPasscodeVisible(false);
}}
- onSuccess={deleteSelectedEntity}
+ success={deleteSelectedEntity}
/>
)}
/>
diff --git a/src/screens/AppSettings/PasswordModalContent.tsx b/src/screens/AppSettings/PasswordModalContent.tsx
new file mode 100644
index 000000000..1388ff59b
--- /dev/null
+++ b/src/screens/AppSettings/PasswordModalContent.tsx
@@ -0,0 +1,98 @@
+import { Box, useColorMode } from 'native-base';
+import React, { useContext, useEffect, useState } from 'react';
+import { StyleSheet } from 'react-native';
+import Buttons from 'src/components/Buttons';
+import Text from 'src/components/KeeperText';
+import KeeperTextInput from 'src/components/KeeperTextInput';
+import { hp } from 'src/constants/responsive';
+import { LocalizationContext } from 'src/context/Localization/LocContext';
+import LoginMethod from 'src/models/enums/LoginMethod';
+import { useAppDispatch, useAppSelector } from 'src/store/hooks';
+import { credsAuth } from 'src/store/sagaActions/login';
+import { credsAuthenticated } from 'src/store/reducers/login';
+
+interface Props {
+ close?: Function;
+ onSuccess?: Function;
+}
+const PasswordModalContent = ({ close, onSuccess }: Props) => {
+ const { colorMode } = useColorMode();
+ const dispatch = useAppDispatch();
+ const { translations } = useContext(LocalizationContext);
+ const { common } = translations;
+ const [password, setPassword] = useState('');
+ const [loginError, setLoginError] = useState(false);
+ const [errMessage, setErrMessage] = useState('');
+
+ const { isAuthenticated, authenticationFailed } = useAppSelector((state) => state.login);
+
+ const attemptLogin = (passcode: string) => {
+ dispatch(credsAuth(passcode, LoginMethod.PASSWORD, true));
+ };
+
+ useEffect(() => {
+ if (isAuthenticated) {
+ onSuccess(password);
+ close();
+ dispatch(credsAuthenticated(false));
+ }
+ }, [isAuthenticated]);
+ useEffect(() => {
+ if (authenticationFailed && password !== '') {
+ setLoginError(true);
+ setErrMessage('Incorrect password');
+ dispatch(credsAuthenticated(false));
+ } else {
+ setLoginError(false);
+ setErrMessage('');
+ }
+ }, [authenticationFailed]);
+ return (
+
+ setPassword(text)}
+ inpuBorderColor={`${colorMode}.separator`}
+ inpuBackgroundColor={`${colorMode}.boxSecondaryBackground`}
+ />
+ {loginError && (
+
+ {errMessage}
+
+ )}
+
+ {
+ attemptLogin(password);
+ }}
+ primaryText={common.continue}
+ secondaryText={common.cancel}
+ secondaryCallback={() => {
+ close();
+ }}
+ />
+
+
+ );
+};
+
+export default PasswordModalContent;
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ marginTop: hp(-10),
+ },
+ btnsContainer: {
+ marginTop: hp(20),
+ },
+ errorText: {
+ textAlign: 'right',
+ fontStyle: 'italic',
+ marginRight: 10,
+ },
+});
diff --git a/src/screens/AppSettings/PrivacyAndDisplay.tsx b/src/screens/AppSettings/PrivacyAndDisplay.tsx
index 25d847d2a..6beaa1cdf 100644
--- a/src/screens/AppSettings/PrivacyAndDisplay.tsx
+++ b/src/screens/AppSettings/PrivacyAndDisplay.tsx
@@ -1,8 +1,7 @@
import React, { useContext, useEffect, useState } from 'react';
-import { Box, ScrollView, useColorMode } from 'native-base';
+import { Box, useColorMode } from 'native-base';
import ReactNativeBiometrics from 'react-native-biometrics';
import ScreenWrapper from 'src/components/ScreenWrapper';
-import OptionCard from 'src/components/OptionCard';
import { LocalizationContext } from 'src/context/Localization/LocContext';
import Switch from 'src/components/Switch/Switch';
import { useAppDispatch, useAppSelector } from 'src/store/hooks';
@@ -10,7 +9,7 @@ import LoginMethod from 'src/models/enums/LoginMethod';
import { changeAuthCred, changeLoginMethod } from 'src/store/sagaActions/login';
import ToastErrorIcon from 'src/assets/images/toast_error.svg';
import useToastMessage from 'src/hooks/useToastMessage';
-import { setThemeMode } from 'src/store/reducers/settings';
+import { setFallbackLoginMethod, setThemeMode } from 'src/store/reducers/settings';
import ThemeMode from 'src/models/enums/ThemeMode';
import { Linking, StyleSheet, TouchableOpacity } from 'react-native';
import { hp, wp } from 'src/constants/responsive';
@@ -19,7 +18,6 @@ import { KeeperApp } from 'src/models/interfaces/KeeperApp';
import { useQuery } from '@realm/react';
import { RealmSchema } from 'src/storage/realm/enum';
import { getJSONFromRealmObject } from 'src/storage/realm/utils';
-import PasscodeVerifyModal from 'src/components/Modal/PasscodeVerify';
import KeeperModal from 'src/components/KeeperModal';
import ModalWrapper from 'src/components/Modal/ModalWrapper';
import HealthCheckComponent from 'src/components/Backup/HealthCheckComponent';
@@ -38,10 +36,23 @@ import { resetCredsChanged } from 'src/store/reducers/login';
import Buttons from 'src/components/Buttons';
import WalletHeader from 'src/components/WalletHeader';
import usePlan from 'src/hooks/usePlan';
+import SettingCard from '../Home/components/Settings/Component/SettingCard';
+import BiometricIcon from 'src/assets/images/biometric-image.svg';
+import PasswordIcon from 'src/assets/images/password-ico.svg';
+import PinIcon from 'src/assets/images/pin-icon.svg';
+import PasswordModalContent from './PasswordModalContent';
+import CreatePasswordContent from './CreatePasswordContent';
+import { useSelector } from 'react-redux';
+import PasscodeVerifyModal from 'src/components/Modal/PasscodeVerify';
const RNBiometrics = new ReactNativeBiometrics();
-function ConfirmPasscode({ oldPassword, setConfirmPasscodeModal, onCredsChange }) {
+function ConfirmPasscode({
+ oldPassword,
+ setConfirmPasscodeModal,
+ onCredsChange,
+ setShowSetPasscodeModal,
+}) {
const { colorMode } = useColorMode();
const { translations } = useContext(LocalizationContext);
@@ -52,7 +63,7 @@ function ConfirmPasscode({ oldPassword, setConfirmPasscodeModal, onCredsChange }
const [passcodeFlag, setPasscodeFlag] = useState(true);
const [confirmPasscodeFlag, setConfirmPasscodeFlag] = useState(0);
const { credsChanged } = useAppSelector((state) => state.login);
-
+ const { loginMethod }: { loginMethod: LoginMethod } = useAppSelector((state) => state.settings);
useEffect(() => {
if (credsChanged === 'changed') {
onCredsChange();
@@ -156,6 +167,10 @@ function ConfirmPasscode({ oldPassword, setConfirmPasscodeModal, onCredsChange }
primaryText={common.confirm}
primaryCallback={() => {
dispatch(changeAuthCred(oldPassword, passcode));
+ if (loginMethod === LoginMethod.BIOMETRIC) {
+ dispatch(setFallbackLoginMethod(LoginMethod.PIN));
+ }
+ setShowSetPasscodeModal(false);
}}
fullWidth
/>
@@ -190,12 +205,15 @@ function PrivacyAndDisplay({ route }) {
const [sensorType, setSensorType] = useState(null);
const [sensorAvailable, setSensorAvailable] = useState(false);
const [visiblePasscode, setVisiblePassCode] = useState(false);
+ const [visiblePassword, setVisiblePassword] = useState(false);
const [showConfirmSeedModal, setShowConfirmSeedModal] = useState(false);
const [confirmPasscode, setConfirmPasscode] = useState(false);
const [oldPassword, setOldPassword] = useState('');
const [backupModalVisible, setBackupModalVisible] = useState(false);
const [RKHealthCheckModal, setRKHealthCheckModal] = useState(false);
const [passcodeHCModal, setPasscodeHCModal] = useState(false);
+ const [showSetPasscodeModal, setShowSetPasscodeModal] = useState(false);
+ const [createPasswordModal, setCreatePasswordModal] = useState(false);
const { translations, formatString } = useContext(LocalizationContext);
const { settings, common, error: errorText } = translations;
@@ -208,6 +226,7 @@ function PrivacyAndDisplay({ route }) {
const app: KeeperApp = useQuery(RealmSchema.KeeperApp).map(getJSONFromRealmObject)[0];
const [credsChanged, setCredsChanged] = useState('');
const { isOnL4 } = usePlan();
+ const fallbackLoginMethod = useSelector((state) => state.settings.fallbackLoginMethod);
useEffect(() => {
if (credsChanged === 'changed') {
@@ -223,7 +242,11 @@ function PrivacyAndDisplay({ route }) {
useEffect(() => {
if (RKBackedUp) {
- setConfirmPasscode(true);
+ if (showSetPasscodeModal) {
+ setConfirmPasscode(true);
+ } else {
+ setCreatePasswordModal(true);
+ }
setOldPassword(oldPasscode);
}
}, [route?.params]);
@@ -270,7 +293,7 @@ function PrivacyAndDisplay({ route }) {
try {
const { available } = await RNBiometrics.isSensorAvailable();
if (available) {
- if (loginMethod === LoginMethod.PIN) {
+ if (loginMethod === LoginMethod.PIN || loginMethod === LoginMethod.PASSWORD) {
const { keysExist } = await RNBiometrics.biometricKeysExist();
if (keysExist) {
await RNBiometrics.deleteKeys();
@@ -280,10 +303,10 @@ function PrivacyAndDisplay({ route }) {
});
if (success) {
const { publicKey } = await RNBiometrics.createKeys();
- dispatch(changeLoginMethod(LoginMethod.BIOMETRIC, publicKey));
+ dispatch(changeLoginMethod(LoginMethod.BIOMETRIC, publicKey, loginMethod));
}
- } else {
- dispatch(changeLoginMethod(LoginMethod.PIN));
+ } else if (loginMethod === LoginMethod.BIOMETRIC) {
+ dispatch(changeLoginMethod(fallbackLoginMethod || LoginMethod.PIN));
}
} else {
setSensorAvailable(false);
@@ -295,54 +318,117 @@ function PrivacyAndDisplay({ route }) {
}
};
- return (
-
-
-
-
-
- onChangeLoginMethod()}
- disabled={!sensorType}
- Icon={
- sensorAvailable || !sensorType ? (
-
- ) : (
-
-
-
- {common.Enable} {sensorType}
-
-
-
- )
- }
- />
-
+ const updateBiometricAfterPasscodeChange = async (newFallbackMethod) => {
+ try {
+ const { available } = await RNBiometrics.isSensorAvailable();
- {
- setVisiblePassCode(true);
- }}
+ if (!available) {
+ setSensorAvailable(false);
+ showToast(errorText.biometricNotEnabled, );
+ return;
+ }
+ const { keysExist } = await RNBiometrics.biometricKeysExist();
+ const { success } = await RNBiometrics.simplePrompt({
+ promptMessage: errorText.confirmIdentity,
+ });
+
+ if (!success) {
+ showToast('Failed to update biometric authentication.', );
+ if (fallbackLoginMethod === 'PIN') {
+ dispatch(changeLoginMethod(LoginMethod.PIN));
+ } else if (fallbackLoginMethod === 'PASSWORD') {
+ dispatch(changeLoginMethod(LoginMethod.PASSWORD));
+ }
+ return;
+ }
+ if (keysExist) {
+ await RNBiometrics.deleteKeys();
+ }
+ const { publicKey } = await RNBiometrics.createKeys();
+
+ dispatch(changeLoginMethod(LoginMethod.BIOMETRIC, publicKey, newFallbackMethod));
+ showToast('Biometric updated successfully');
+ } catch (error) {
+ showToast('Failed to update biometric authentication.', );
+ setSensorAvailable(false);
+ }
+ };
+
+ const PrivacyAndDisplay = [
+ {
+ title: 'PIN',
+ description: 'Choose a 4 digits PIN code',
+ onPress: () => {
+ if (
+ loginMethod === LoginMethod.PIN ||
+ (loginMethod === LoginMethod.BIOMETRIC && fallbackLoginMethod === 'PIN')
+ ) {
+ setVisiblePassCode(true);
+ } else {
+ setVisiblePassword(true);
+ }
+ setShowSetPasscodeModal(true);
+ },
+ icon: ,
+ },
+ {
+ title: 'Password',
+ description: 'Choose a strong password',
+ onPress: () => {
+ if (
+ loginMethod === LoginMethod.PASSWORD ||
+ (loginMethod === LoginMethod.BIOMETRIC && fallbackLoginMethod === 'PASSWORD')
+ ) {
+ setVisiblePassword(true);
+ setShowSetPasscodeModal(false);
+ } else {
+ setVisiblePassCode(true);
+ setShowSetPasscodeModal(false);
+ }
+ },
+ icon: ,
+ },
+ {
+ title: sensorType || settings.Biometrics,
+ description: sensorType
+ ? formatString(settings.UseBiometricSubTitle, sensorType)
+ : settings.NoBiometricSubTitle,
+ onPress: onChangeLoginMethod,
+ isDisabled: !sensorType,
+ icon: ,
+ onRightPress: sensorAvailable || !sensorType ? onChangeLoginMethod : requestPermission,
+
+ rightIcon:
+ sensorAvailable || !sensorType ? (
+
-
-
+ ) : (
+
+
+
+ {common.Enable} {sensorType}
+
+
+
+ ),
+ },
+ ];
+
+ return (
+
+
+
+
+
+ )}
+ />
+ setVisiblePassword(false)}
+ title="Set password"
+ subTitle="Enter your existing password"
+ modalBackground={`${colorMode}.modalWhiteBackground`}
+ textColor={`${colorMode}.textGreen`}
+ subTitleColor={`${colorMode}.modalSubtitleBlack`}
+ Content={() => (
+ setVisiblePassword(false)}
+ onSuccess={(password) => {
+ if (data.length === 0) {
+ setRKHealthCheckModal(true);
+ setOldPassword(password);
+ } else {
+ setOldPassword(password);
+ setPasscodeHCModal(true);
+ }
+ }}
/>
)}
/>
@@ -420,7 +530,11 @@ function PrivacyAndDisplay({ route }) {
if (backupMethod === BackupType.SEED) {
setShowConfirmSeedModal(false);
dispatch(seedBackedConfirmed(true));
- setConfirmPasscode(true);
+ if (showSetPasscodeModal) {
+ setConfirmPasscode(true);
+ } else {
+ setCreatePasswordModal(true);
+ }
}
}}
/>
@@ -439,7 +553,15 @@ function PrivacyAndDisplay({ route }) {
setCredsChanged('changed')}
+ onCredsChange={() => {
+ setCredsChanged('changed');
+ if (loginMethod === LoginMethod.BIOMETRIC) {
+ updateBiometricAfterPasscodeChange(LoginMethod.PIN);
+ } else {
+ dispatch(changeLoginMethod(LoginMethod.PIN));
+ }
+ }}
+ setShowSetPasscodeModal={setShowSetPasscodeModal}
/>
)}
/>
@@ -488,11 +610,35 @@ function PrivacyAndDisplay({ route }) {
next: true,
parentScreen: PRIVACYANDDISPLAY,
oldPasscode: oldPassword,
+ showSetPasscodeModal: showSetPasscodeModal,
})
);
}}
Content={BackupModalContent}
/>
+ setCreatePasswordModal(false)}
+ title="Set Password"
+ subTitle="Enter Your new Password"
+ modalBackground={`${colorMode}.primaryBackground`}
+ subTitleColor={`${colorMode}.secondaryText`}
+ textColor={`${colorMode}.modalGreenTitle`}
+ Content={() => (
+ setCreatePasswordModal(false)}
+ onSuccess={(newFallbackMethod) => {
+ setCredsChanged('changed');
+ if (loginMethod === LoginMethod.BIOMETRIC) {
+ updateBiometricAfterPasscodeChange(newFallbackMethod);
+ } else {
+ dispatch(changeLoginMethod(LoginMethod.PASSWORD));
+ }
+ }}
+ oldPassword={oldPassword}
+ />
+ )}
+ />
);
}
@@ -500,8 +646,11 @@ const styles = StyleSheet.create({
wrapper: {
marginTop: hp(35),
gap: 50,
+ },
+ container: {
width: '95%',
- alignSelf: 'center',
+ justifyContent: 'center',
+ alignItems: 'center',
},
note: {
position: 'absolute',
diff --git a/src/screens/Home/components/Settings/Component/SettingCard.tsx b/src/screens/Home/components/Settings/Component/SettingCard.tsx
index 96ce3802c..05b43edb4 100644
--- a/src/screens/Home/components/Settings/Component/SettingCard.tsx
+++ b/src/screens/Home/components/Settings/Component/SettingCard.tsx
@@ -20,6 +20,7 @@ interface SettingCardItemProps {
showDot?: boolean;
onPress?: () => void;
onRightPress?: () => void;
+ isDisabled?: boolean;
}
interface SettingCardProps {
@@ -64,11 +65,14 @@ const SettingCard: React.FC = ({
borderColor={borderColor}
>
{items.map((item, index) => {
- const applyDiamondCheck = item?.isHodler
- ? isOnL2Above
- : item?.isDiamond
- ? isOnL3Above
- : true;
+ const applyDiamondCheck =
+ item?.isDisabled !== undefined
+ ? !item.isDisabled
+ : item?.isHodler
+ ? isOnL2Above
+ : item?.isDiamond
+ ? isOnL3Above
+ : true;
return (
@@ -121,6 +125,7 @@ const SettingCard: React.FC = ({
{item.rightIcon}
diff --git a/src/screens/Home/components/Settings/Component/SettingModal.tsx b/src/screens/Home/components/Settings/Component/SettingModal.tsx
index 91aa9e554..e60f1a1a8 100644
--- a/src/screens/Home/components/Settings/Component/SettingModal.tsx
+++ b/src/screens/Home/components/Settings/Component/SettingModal.tsx
@@ -2,8 +2,8 @@ import { CommonActions, useNavigation } from '@react-navigation/native';
import { useQuery } from '@realm/react';
import { Box, useColorMode } from 'native-base';
import React, { useContext, useEffect, useState } from 'react';
+import ConfirmCredentialModal from 'src/components/ConfirmCredentialModal';
import KeeperModal from 'src/components/KeeperModal';
-import PasscodeVerifyModal from 'src/components/Modal/PasscodeVerify';
import { wp } from 'src/constants/responsive';
import { LocalizationContext } from 'src/context/Localization/LocContext';
import { KeeperApp } from 'src/models/interfaces/KeeperApp';
@@ -46,13 +46,13 @@ const SettingModal = ({ isUaiFlow, confirmPass, setConfirmPass }) => {
textColor={`${colorMode}.textGreen`}
subTitleColor={`${colorMode}.modalSubtitleBlack`}
Content={() => (
- {
setConfirmPassVisible(false);
setConfirmPass(false);
}}
- onSuccess={() => {
+ success={() => {
setConfirmPassVisible(false);
setBackupModalVisible(true);
}}
diff --git a/src/screens/InheritanceToolsAndTips/components/LetterOfAttorney.tsx b/src/screens/InheritanceToolsAndTips/components/LetterOfAttorney.tsx
index b3c0cf681..cde14dc22 100644
--- a/src/screens/InheritanceToolsAndTips/components/LetterOfAttorney.tsx
+++ b/src/screens/InheritanceToolsAndTips/components/LetterOfAttorney.tsx
@@ -11,12 +11,12 @@ import DashedButton from 'src/components/DashedButton';
import GenerateLetterToAtternyPDFInheritanceTool from 'src/utils/GenerateLetterToAtternyPDFInheritanceTool';
import { LocalizationContext } from 'src/context/Localization/LocContext';
import useSigners from 'src/hooks/useSigners';
-import PasscodeVerifyModal from 'src/components/Modal/PasscodeVerify';
import KeeperModal from 'src/components/KeeperModal';
import { credsAuthenticated } from 'src/store/reducers/login';
import { useDispatch } from 'react-redux';
import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg';
import ThemedColor from 'src/components/ThemedColor/ThemedColor';
+import ConfirmCredentialModal from 'src/components/ConfirmCredentialModal';
function LetterOfAttorney() {
const { signers } = useSigners();
@@ -83,12 +83,12 @@ function LetterOfAttorney() {
textColor={`${colorMode}.textGreen`}
subTitleColor={`${colorMode}.modalSubtitleBlack`}
Content={() => (
- {
setConfirmPassVisible(false);
}}
- onSuccess={() => {
+ success={() => {
setConfirmPassVisible(false);
if (fingerPrints) {
GenerateLetterToAtternyPDFInheritanceTool(fingerPrints).then((res) => {
diff --git a/src/screens/InheritanceToolsAndTips/components/MasterRecoveryKey.tsx b/src/screens/InheritanceToolsAndTips/components/MasterRecoveryKey.tsx
index 6e2d328a1..6273b0b34 100644
--- a/src/screens/InheritanceToolsAndTips/components/MasterRecoveryKey.tsx
+++ b/src/screens/InheritanceToolsAndTips/components/MasterRecoveryKey.tsx
@@ -14,10 +14,10 @@ import { getJSONFromRealmObject } from 'src/storage/realm/utils';
import { CommonActions } from '@react-navigation/native';
import { LocalizationContext } from 'src/context/Localization/LocContext';
import MasterKey from 'src/assets/images/master_key.svg';
-import PasscodeVerifyModal from 'src/components/Modal/PasscodeVerify';
import KeeperModal from 'src/components/KeeperModal';
import { credsAuthenticated } from 'src/store/reducers/login';
import { useDispatch } from 'react-redux';
+import ConfirmCredentialModal from 'src/components/ConfirmCredentialModal';
function MasterRecoveryKey({ navigation }) {
const { colorMode } = useColorMode();
@@ -80,12 +80,12 @@ function MasterRecoveryKey({ navigation }) {
textColor={`${colorMode}.textGreen`}
subTitleColor={`${colorMode}.modalSubtitleBlack`}
Content={() => (
- {
setConfirmPassVisible(false);
}}
- onSuccess={() => {
+ success={() => {
setConfirmPassVisible(false);
navigation.dispatch(
CommonActions.navigate('ExportSeed', {
diff --git a/src/screens/LoginScreen/CreatePin.tsx b/src/screens/LoginScreen/CreatePin.tsx
index 04d21dec2..5f99eab33 100644
--- a/src/screens/LoginScreen/CreatePin.tsx
+++ b/src/screens/LoginScreen/CreatePin.tsx
@@ -167,7 +167,7 @@ export default function CreatePin(props) {
if (success) {
const { publicKey } = await RNBiometrics.createKeys();
- dispatch(changeLoginMethod(LoginMethod.BIOMETRIC, publicKey));
+ dispatch(changeLoginMethod(LoginMethod.BIOMETRIC, publicKey, LoginMethod.PIN));
props.navigation.replace('OnBoardingSlides');
} else {
showToast(errorText.biometicAuthFailed, );
diff --git a/src/screens/LoginScreen/Login.tsx b/src/screens/LoginScreen/Login.tsx
index e40cfdb03..db7cd4988 100644
--- a/src/screens/LoginScreen/Login.tsx
+++ b/src/screens/LoginScreen/Login.tsx
@@ -52,6 +52,8 @@ import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg';
import CampaignModalIllustration from 'src/assets/images/CampaignModalIllustration.svg';
import { uaiType } from 'src/models/interfaces/Uai';
import { addToUaiStack, uaiChecks } from 'src/store/sagaActions/uai';
+import KeeperTextInput from 'src/components/KeeperTextInput';
+import { useSelector } from 'react-redux';
const RNBiometrics = new ReactNativeBiometrics();
@@ -98,6 +100,7 @@ function LoginScreen({ navigation, route }) {
const { login } = translations;
const { common } = translations;
const { allAccounts, biometricEnabledAppId } = useAppSelector((state) => state.account);
+ const fallbackLoginMethod = useSelector((state) => state.settings.fallbackLoginMethod);
const [showCampaignModal, setShowCampaignModal] = useState(false);
const [campaignDetails, setCampaignDetails] = useState(null);
@@ -303,8 +306,17 @@ function LoginScreen({ navigation, route }) {
const attemptLogin = (passcode: string) => {
setLoginModal(true);
-
- dispatch(credsAuth(passcode, LoginMethod.PIN, relogin));
+ if (
+ loginMethod === LoginMethod.PIN ||
+ (loginMethod === LoginMethod.BIOMETRIC && fallbackLoginMethod === 'PIN')
+ ) {
+ dispatch(credsAuth(passcode, LoginMethod.PIN, relogin));
+ } else if (
+ loginMethod === LoginMethod.PASSWORD ||
+ (loginMethod === LoginMethod.BIOMETRIC && fallbackLoginMethod === 'PASSWORD')
+ ) {
+ dispatch(credsAuth(passcode, LoginMethod.PASSWORD, relogin));
+ }
};
const modelAsset = useMemo(() => {
@@ -469,55 +481,102 @@ function LoginScreen({ navigation, route }) {
-
-
- {isTestnet() && }
-
- {relogin ? title : login.welcomeback}
-
+ {loginMethod === LoginMethod.PIN || fallbackLoginMethod === 'PIN' ? (
+
-
-
- {login.enter_your}
- {login.passcode}
-
-
+
+ {isTestnet() && }
+
+
+ {relogin ? title : login.welcomeback}
+
+
+
+
+ {login.enter_your}
+ {login.passcode}
+
+
+
+
+
+ {loginError && (
+
+ {errMessage}
+
+ )}
-
- {loginError && (
-
- {errMessage}
-
- )}
+ }
+ bubbleEffect
+ keyColor={login_text_color}
+ />
+
+ {
+ setLoginError(false);
+ setLogging(true);
+ }}
+ primaryText={common.proceed}
+ primaryDisable={passcode.length !== 4}
+ primaryBackgroundColor={login_button_backGround}
+ primaryTextColor={login_button_text_color}
+ fullWidth
+ />
- }
- bubbleEffect
- keyColor={login_text_color}
- />
-
- {
- setLoginError(false);
- setLogging(true);
- }}
- primaryText={common.proceed}
- primaryDisable={passcode.length !== 4}
- primaryBackgroundColor={login_button_backGround}
- primaryTextColor={login_button_text_color}
- fullWidth
- />
+ ) : (
+
+
+
+ {isTestnet() && }
+
+
+ {relogin ? title : login.welcomeback}
+
+
+
+ Unlock the app with your password
+
+
+
+
+ {loginError && (
+
+ {errMessage}
+
+ )}
+
+ {
+ setLoginError(false);
+ setLogging(true);
+ }}
+ primaryText={common.proceed}
+ primaryBackgroundColor={login_button_backGround}
+ primaryTextColor={login_button_text_color}
+ fullWidth
+ />
+
-
+ )}
{
@@ -654,6 +717,12 @@ const styles = StyleSheet.create({
textAlign: 'center',
marginTop: 18,
},
+ passwordError: {
+ fontStyle: 'italic',
+ fontSize: 12,
+ textAlign: 'right',
+ marginBottom: 10,
+ },
forgotPassWrapper: {
flex: 0.8,
margin: 20,
@@ -713,6 +782,14 @@ const styles = StyleSheet.create({
marginTop: hp(45),
gap: hp(15),
},
+ passwordWrapper: {
+ justifyContent: 'center',
+ marginTop: hp(120),
+ paddingHorizontal: wp(15),
+ },
+ passwordInput: {
+ marginTop: hp(10),
+ },
});
export default LoginScreen;
diff --git a/src/screens/SeedScreens/ExportSeedScreen.tsx b/src/screens/SeedScreens/ExportSeedScreen.tsx
index 1c4ca9ac7..65cd34fe4 100644
--- a/src/screens/SeedScreens/ExportSeedScreen.tsx
+++ b/src/screens/SeedScreens/ExportSeedScreen.tsx
@@ -54,6 +54,7 @@ function ExportSeedScreen({ route, navigation }) {
parentScreen,
oldPasscode,
isFromMobileKey = false,
+ showSetPasscodeModal,
}: {
vaultKey: string;
vaultId: string;
@@ -68,6 +69,7 @@ function ExportSeedScreen({ route, navigation }) {
parentScreen?: string;
oldPasscode?: string;
isFromMobileKey: boolean;
+ showSetPasscodeModal: boolean;
} = route.params;
const { showToast } = useToastMessage();
const [words, setWords] = useState(seed.split(' '));
@@ -301,7 +303,12 @@ function ExportSeedScreen({ route, navigation }) {
dismissible={false}
close={
isChangePassword
- ? () => navigation.navigate('PrivacyAndDisplay', { RKBackedUp: true, oldPasscode })
+ ? () =>
+ navigation.navigate('PrivacyAndDisplay', {
+ RKBackedUp: true,
+ oldPasscode,
+ showSetPasscodeModal,
+ })
: () => {}
}
title={BackupWallet.backupSuccessTitle}
diff --git a/src/screens/Send/SendConfirmation.tsx b/src/screens/Send/SendConfirmation.tsx
index 4af391469..466fa9f3b 100644
--- a/src/screens/Send/SendConfirmation.tsx
+++ b/src/screens/Send/SendConfirmation.tsx
@@ -28,7 +28,6 @@ import useToastMessage from 'src/hooks/useToastMessage';
import useBalance from 'src/hooks/useBalance';
import useWallets from 'src/hooks/useWallets';
import useVault from 'src/hooks/useVault';
-import PasscodeVerifyModal from 'src/components/Modal/PasscodeVerify';
import { InputUTXOs, UTXO } from 'src/services/wallets/interfaces';
import CurrencyTypeSwitch from 'src/components/Switch/CurrencyTypeSwitch';
import FeeInsights from 'src/screens/FeeInsights/FeeInsightsContent';
@@ -67,6 +66,7 @@ import SendingCardIcon from 'src/assets/images/vault_icon.svg';
import WalletIcon from 'src/assets/images/daily_wallet.svg';
import MultiSendSvg from 'src/assets/images/@.svg';
import useExchangeRates from 'src/hooks/useExchangeRates';
+import ConfirmCredentialModal from 'src/components/ConfirmCredentialModal';
export interface SendConfirmationRouteParams {
sender: Wallet | Vault;
@@ -797,12 +797,12 @@ function SendConfirmation({ route }) {
textColor={`${colorMode}.textGreen`}
subTitleColor={`${colorMode}.modalSubtitleBlack`}
Content={() => (
- {
setConfirmPassVisible(false);
}}
- onSuccess={onProceed}
+ success={onProceed}
/>
)}
/>
diff --git a/src/screens/SignTransaction/SignTransactionScreen.tsx b/src/screens/SignTransaction/SignTransactionScreen.tsx
index bb03ba06b..31f281d7d 100644
--- a/src/screens/SignTransaction/SignTransactionScreen.tsx
+++ b/src/screens/SignTransaction/SignTransactionScreen.tsx
@@ -30,7 +30,6 @@ import { LocalizationContext } from 'src/context/Localization/LocContext';
import useSignerMap from 'src/hooks/useSignerMap';
import ActivityIndicatorView from 'src/components/AppActivityIndicator/ActivityIndicatorView';
import { getTxHexFromKeystonePSBT } from 'src/hardware/keystone';
-import PasscodeVerifyModal from 'src/components/Modal/PasscodeVerify';
import { DelayedTransaction } from 'src/models/interfaces/AssistedKeys';
import { hash256 } from 'src/utils/service-utilities/encryption';
import { hcStatusType } from 'src/models/interfaces/HeathCheckTypes';
@@ -60,6 +59,7 @@ import {
} from './signWithSD';
import SendSuccessfulContent from '../Send/SendSuccessfulContent';
import WalletHeader from 'src/components/WalletHeader';
+import ConfirmCredentialModal from 'src/components/ConfirmCredentialModal';
function SignTransactionScreen() {
const route = useRoute();
@@ -806,12 +806,12 @@ function SignTransactionScreen() {
textColor={`${colorMode}.textGreen`}
subTitleColor={`${colorMode}.modalSubtitleBlack`}
Content={() => (
- {
setConfirmPassVisible(false);
}}
- onSuccess={onSuccess}
+ success={onSuccess}
/>
)}
/>
diff --git a/src/screens/SigningDevices/DeleteKeys.tsx b/src/screens/SigningDevices/DeleteKeys.tsx
index 611a9398a..eef2e6f07 100644
--- a/src/screens/SigningDevices/DeleteKeys.tsx
+++ b/src/screens/SigningDevices/DeleteKeys.tsx
@@ -2,7 +2,6 @@ import React, { useContext, useEffect, useState } from 'react';
import { Box, Pressable, useColorMode } from 'native-base';
import { hp, windowWidth, wp } from 'src/constants/responsive';
import ScreenWrapper from 'src/components/ScreenWrapper';
-import PasscodeVerifyModal from 'src/components/Modal/PasscodeVerify';
import KeeperModal from 'src/components/KeeperModal';
import useSigners from 'src/hooks/useSigners';
import { StyleSheet, ScrollView } from 'react-native';
@@ -34,6 +33,7 @@ import TorAsset from 'src/components/Loader';
import moment from 'moment';
import { getKeyUID } from 'src/utils/utilities';
import WalletHeader from 'src/components/WalletHeader';
+import ConfirmCredentialModal from 'src/components/ConfirmCredentialModal';
import ShowAllIcon from 'src/assets/images/show_wallet.svg';
import HideAllIcon from 'src/assets/images/hide_wallet.svg';
import HideWalletIcon from 'src/assets/images/hide_wallet.svg';
@@ -370,10 +370,11 @@ function DeleteKeys({ route }) {
)}
- setConfirmPassVisible(false)}
- onSuccess={onSuccess}
+ success={onSuccess}
/>
)}
@@ -390,11 +391,11 @@ function DeleteKeys({ route }) {
subTitleColor={`${colorMode}.modalSubtitleBlack`}
Content={() => (
- setConfirmPassForSigner(false)}
- onSuccess={onSignerSuccess}
+ success={onSignerSuccess}
onForceSuccess={onForceProceed}
/>
diff --git a/src/screens/SigningDevices/ManageSigners.tsx b/src/screens/SigningDevices/ManageSigners.tsx
index ab7487b27..39c21c825 100644
--- a/src/screens/SigningDevices/ManageSigners.tsx
+++ b/src/screens/SigningDevices/ManageSigners.tsx
@@ -39,7 +39,6 @@ import ToastErrorIcon from 'src/assets/images/toast_error.svg';
import { setupKeeperSigner } from 'src/hardware/signerSetup';
import { getKeyUID } from 'src/utils/utilities';
import { SentryErrorBoundary } from 'src/services/sentry';
-import PasscodeVerifyModal from 'src/components/Modal/PasscodeVerify';
import EnhancedKeysSection from './components/EnhancedKeysSection';
import ConciergeNeedHelp from 'src/assets/images/conciergeNeedHelp.svg';
import {
@@ -52,6 +51,7 @@ import HWError from 'src/hardware/HWErrorState';
import { HWErrorType } from 'src/models/enums/Hardware';
import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg';
import ThemedColor from 'src/components/ThemedColor/ThemedColor';
+import ConfirmCredentialModal from 'src/components/ConfirmCredentialModal';
type ScreenProps = NativeStackScreenProps;
@@ -307,12 +307,12 @@ function ManageSigners({ route }: ScreenProps) {
textColor={`${colorMode}.textGreen`}
subTitleColor={`${colorMode}.modalSubtitleBlack`}
Content={() => (
- {
setConfirmPassVisible(false);
}}
- onSuccess={onSuccess}
+ success={onSuccess}
/>
)}
/>
diff --git a/src/screens/Vault/HardwareModalMap.tsx b/src/screens/Vault/HardwareModalMap.tsx
index 74c772267..ddb32a1ea 100644
--- a/src/screens/Vault/HardwareModalMap.tsx
+++ b/src/screens/Vault/HardwareModalMap.tsx
@@ -83,7 +83,6 @@ import {
setupSpecter,
} from 'src/hardware/signerSetup';
import { extractColdCardExport } from 'src/hardware/coldcard';
-import PasscodeVerifyModal from 'src/components/Modal/PasscodeVerify';
import useCanaryWalletSetup from 'src/hooks/UseCanaryWalletSetup';
import { hcStatusType } from 'src/models/interfaces/HeathCheckTypes';
import NFC from 'src/services/nfc';
@@ -101,6 +100,7 @@ import BackupModalContent from '../AppSettings/BackupModal';
import SignerOptionCard from './components/signerOptionCard';
import ColdCardUSBInstruction from './components/ColdCardUSBInstruction';
import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg';
+import ConfirmCredentialModal from 'src/components/ConfirmCredentialModal';
import { KRUX_LOAD_SEED, manipulateKruxData } from 'src/hardware/krux';
const RNBiometrics = new ReactNativeBiometrics();
@@ -2314,11 +2314,11 @@ function HardwareModalMap({
textColor={`${colorMode}.textGreen`}
subTitleColor={`${colorMode}.modalSubtitleBlack`}
Content={() => (
- {
setConfirmPassVisible(false);
}}
- onSuccess={() => {
+ success={() => {
if (type === SignerType.MY_KEEPER && mode === InteracationMode.HEALTH_CHECK) {
setConfirmPassVisible(false);
setBackupModalVisible(true);
diff --git a/src/screens/Vault/SignerAdvanceSettings.tsx b/src/screens/Vault/SignerAdvanceSettings.tsx
index 557bf9d74..2d9ef8c51 100644
--- a/src/screens/Vault/SignerAdvanceSettings.tsx
+++ b/src/screens/Vault/SignerAdvanceSettings.tsx
@@ -31,7 +31,6 @@ import { getAccountFromSigner, getKeyUID } from 'src/utils/utilities';
import useSignerMap from 'src/hooks/useSignerMap';
import { getSignerNameFromType } from 'src/hardware';
import { KEEPER_KNOWLEDGEBASE } from 'src/utils/service-utilities/config';
-import PasscodeVerifyModal from 'src/components/Modal/PasscodeVerify';
import { NewVaultInfo } from 'src/store/sagas/wallets';
import { addNewVault, refillMobileKey } from 'src/store/sagaActions/vaults';
import { generateVaultId } from 'src/services/wallets/factories/VaultFactory';
@@ -64,6 +63,7 @@ import HardwareModalMap, { InteracationMode } from './HardwareModalMap';
import RegisterSignerContent from './components/RegisterSignerContent';
import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg';
import ThemedColor from 'src/components/ThemedColor/ThemedColor';
+import ConfirmCredentialModal from 'src/components/ConfirmCredentialModal';
const { width } = Dimensions.get('screen');
@@ -1084,12 +1084,12 @@ function SignerAdvanceSettings({ route }: any) {
textColor={`${colorMode}.textGreen`}
subTitleColor={`${colorMode}.modalSubtitleBlack`}
Content={() => (
- {
setConfirmPassVisible(false);
}}
- onSuccess={onSuccess}
+ success={onSuccess}
/>
)}
/>
diff --git a/src/screens/Vault/SigningDeviceDetails.tsx b/src/screens/Vault/SigningDeviceDetails.tsx
index f34130599..980a2e3ff 100644
--- a/src/screens/Vault/SigningDeviceDetails.tsx
+++ b/src/screens/Vault/SigningDeviceDetails.tsx
@@ -48,7 +48,6 @@ import { uaiType } from 'src/models/interfaces/Uai';
import { ConciergeTag } from 'src/models/enums/ConciergeTag';
import { hcStatusType } from 'src/models/interfaces/HeathCheckTypes';
import { Signer, Vault } from 'src/services/wallets/interfaces/vault';
-import PasscodeVerifyModal from 'src/components/Modal/PasscodeVerify';
import BackupModalContent from 'src/screens/AppSettings/BackupModal';
import { getPersistedDocument } from 'src/services/documents';
import { generateDataFromPSBT, getAccountFromSigner, getKeyUID } from 'src/utils/utilities';
@@ -78,6 +77,7 @@ import nfcManager, { NfcTech } from 'react-native-nfc-manager';
import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg';
import ThemedColor from 'src/components/ThemedColor/ThemedColor';
import HexagonIcon from 'src/components/HexagonIcon';
+import ConfirmCredentialModal from 'src/components/ConfirmCredentialModal';
export const SignersReqVault = [
SignerType.LEDGER,
@@ -1005,12 +1005,12 @@ function SigningDeviceDetails({ route }) {
textColor={`${colorMode}.textGreen`}
subTitleColor={`${colorMode}.modalSubtitleBlack`}
Content={() => (
- {
setConfirmPassVisible(false);
}}
- onSuccess={() => {
+ success={() => {
setShowMobileKeyModal(false);
setBackupModalVisible(true);
}}
diff --git a/src/screens/WalletDetails/WalletSettings.tsx b/src/screens/WalletDetails/WalletSettings.tsx
index ed4baf1dc..f9fb27962 100644
--- a/src/screens/WalletDetails/WalletSettings.tsx
+++ b/src/screens/WalletDetails/WalletSettings.tsx
@@ -10,7 +10,6 @@ import TickIcon from 'src/assets/images/icon_tick.svg';
import useWallets from 'src/hooks/useWallets';
import { Pressable, StyleSheet } from 'react-native';
import ScreenWrapper from 'src/components/ScreenWrapper';
-import PasscodeVerifyModal from 'src/components/Modal/PasscodeVerify';
import useTestSats from 'src/hooks/useTestSats';
import idx from 'idx';
import dbManager from 'src/storage/realm/dbManager';
@@ -31,6 +30,7 @@ import ToastErrorIcon from 'src/assets/images/toast_error.svg';
import Instruction from 'src/components/Instruction';
import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg';
import ThemedColor from 'src/components/ThemedColor/ThemedColor';
+import ConfirmCredentialModal from 'src/components/ConfirmCredentialModal';
import { refreshWallets } from 'src/store/sagaActions/wallets';
function WalletSettings({ route }) {
@@ -193,12 +193,12 @@ function WalletSettings({ route }) {
textColor={`${colorMode}.textGreen`}
subTitleColor={`${colorMode}.modalSubtitleBlack`}
Content={() => (
- {
setConfirmPassVisible(false);
}}
- onSuccess={() => {
+ success={() => {
setConfirmPassVisible(false);
setBackupModalVisible(true);
}}
diff --git a/src/store/reducers/settings.ts b/src/store/reducers/settings.ts
index 17c2e82a7..f71252b92 100644
--- a/src/store/reducers/settings.ts
+++ b/src/store/reducers/settings.ts
@@ -8,6 +8,7 @@ import * as bitcoinJS from 'bitcoinjs-lib';
const initialState: {
loginMethod: LoginMethod;
+ fallbackLoginMethod: LoginMethod | null;
themeMode: ThemeMode;
currencyKind: CurrencyKind;
currencyCode: string;
@@ -24,6 +25,7 @@ const initialState: {
appWideLoading: boolean;
} = {
loginMethod: LoginMethod.PIN,
+ fallbackLoginMethod: null,
themeMode: ThemeMode.LIGHT,
currencyKind: CurrencyKind.BITCOIN,
currencyCode: 'USD',
@@ -47,6 +49,9 @@ const settingsSlice = createSlice({
setLoginMethod: (state, action: PayloadAction) => {
state.loginMethod = action.payload;
},
+ setFallbackLoginMethod: (state, action: PayloadAction) => {
+ state.fallbackLoginMethod = action.payload;
+ },
setThemeMode: (state, action: PayloadAction) => {
state.themeMode = action.payload;
},
@@ -99,6 +104,7 @@ export const {
setBackupModal,
setSubscription,
setBitcoinNetwork,
+ setFallbackLoginMethod,
setAppWideLoading,
} = settingsSlice.actions;
diff --git a/src/store/sagaActions/login.ts b/src/store/sagaActions/login.ts
index 151c9c825..83d9da8ea 100644
--- a/src/store/sagaActions/login.ts
+++ b/src/store/sagaActions/login.ts
@@ -19,11 +19,16 @@ export const storeCreds = (passcode, callback = null) => ({
},
});
-export const changeLoginMethod = (method: LoginMethod, pubKey: string = '') => ({
+export const changeLoginMethod = (
+ method: LoginMethod,
+ pubKey: string = '',
+ fallbackMethod = null
+) => ({
type: CHANGE_LOGIN_METHOD,
payload: {
method,
pubKey,
+ fallbackMethod,
},
});
diff --git a/src/store/sagas/login.ts b/src/store/sagas/login.ts
index dfd5a901e..54a9d2671 100644
--- a/src/store/sagas/login.ts
+++ b/src/store/sagas/login.ts
@@ -49,7 +49,7 @@ import {
import { RootState, store } from '../store';
import { createWatcher } from '../utilities';
import { fetchExchangeRates } from '../sagaActions/send_and_receive';
-import { setLoginMethod } from '../reducers/settings';
+import { setLoginMethod, setFallbackLoginMethod } from '../reducers/settings';
import { setSubscription } from 'src/store/sagaActions/settings';
import { backupAllSignersAndVaults } from '../sagaActions/bhr';
import { uaiChecks } from '../sagaActions/uai';
@@ -149,7 +149,7 @@ function* credentialsAuthWorker({ payload }) {
yield put(setupLoading('authenticating'));
let hash;
let encryptedKey;
- if (method === LoginMethod.PIN) {
+ if (method === LoginMethod.PIN || method === LoginMethod.PASSWORD) {
hash = yield call(hash512, payload.passcode);
if (payload.reLogin) encryptedKey = yield call(SecureStore.fetchSpecific, hash, appId);
else encryptedKey = yield call(SecureStore.fetch, hash);
@@ -273,7 +273,6 @@ function* credentialsAuthWorker({ payload }) {
RealmSchema.KeeperApp
);
if (updatedSubs.level > 2) yield put(setAllCampaigns(true));
-
const { pendingAllBackup, automaticCloudBackup } = yield select(
(state: RootState) => state.bhr
);
@@ -297,8 +296,17 @@ function* credentialsAuthWorker({ payload }) {
}
yield put(loadConciergeUserOnLogin({ appId: keeperApp.id }));
yield put(
+ // setLoginMethod(
+ // keeperApp.id === biometricEnabledAppId
+ // ? LoginMethod.BIOMETRIC
+ // : LoginMethod.PIN || LoginMethod.PASSWORD
+ // )
setLoginMethod(
- keeperApp.id === biometricEnabledAppId ? LoginMethod.BIOMETRIC : LoginMethod.PIN
+ keeperApp.id === biometricEnabledAppId
+ ? LoginMethod.BIOMETRIC
+ : method === LoginMethod.PIN
+ ? LoginMethod.PIN
+ : LoginMethod.PASSWORD
)
);
if (backupMethodByAppId[keeperApp.id])
@@ -400,20 +408,26 @@ export const changeAuthCredWatcher = createWatcher(changeAuthCredWorker, CHANGE_
function* changeLoginMethodWorker({
payload,
}: {
- payload: { method: LoginMethod; pubKey: string };
+ payload: {
+ method: LoginMethod;
+ pubKey: string;
+ fallbackMethod: any;
+ };
}) {
try {
- const { method, pubKey } = payload;
+ const { method, pubKey, fallbackMethod } = payload;
const keeperApp = yield call(dbManager.getObjectByIndex, RealmSchema.KeeperApp);
if (method === LoginMethod.BIOMETRIC) {
const savePubKey = yield call(SecureStore.storeBiometricPubKey, pubKey, keeperApp?.id);
if (savePubKey) {
yield put(setLoginMethod(method));
+ yield put(setFallbackLoginMethod(fallbackMethod));
if (keeperApp?.id) yield put(setBiometricEnabledAppId(keeperApp?.id));
}
} else {
yield put(setLoginMethod(method));
yield put(setBiometricEnabledAppId(null));
+ yield put(setFallbackLoginMethod(null));
}
} catch (err) {
console.log('🚀 ~ changeLoginMethodWorker:', err);