Enhanced InfoModal and Security UX
This commit is contained in:
@@ -431,7 +431,7 @@ export default class FeeBreakdown extends React.Component<
|
||||
value={`${csv_delay} ${localeString(
|
||||
'general.blocks'
|
||||
)}`}
|
||||
infoText={[
|
||||
infoModalText={[
|
||||
localeString(
|
||||
'views.Channel.csvDelay.info1'
|
||||
),
|
||||
|
||||
+18
-9
@@ -24,9 +24,12 @@ interface KeyValueProps {
|
||||
color?: string;
|
||||
indicatorColor?: string;
|
||||
sensitive?: boolean;
|
||||
infoText?: string | Array<string>;
|
||||
infoLink?: string;
|
||||
infoNav?: string;
|
||||
infoModalText?: string | Array<string>;
|
||||
infoModalLink?: string;
|
||||
infoModalAdditionalButtons?: Array<{
|
||||
title: string;
|
||||
callback?: () => void;
|
||||
}>;
|
||||
mempoolLink?: () => void;
|
||||
disableCopy?: boolean;
|
||||
ModalStore?: ModalStore;
|
||||
@@ -43,9 +46,9 @@ export default class KeyValue extends React.Component<KeyValueProps, {}> {
|
||||
color,
|
||||
indicatorColor,
|
||||
sensitive,
|
||||
infoText,
|
||||
infoLink,
|
||||
infoNav,
|
||||
infoModalText,
|
||||
infoModalLink,
|
||||
infoModalAdditionalButtons,
|
||||
mempoolLink,
|
||||
disableCopy,
|
||||
ModalStore,
|
||||
@@ -86,7 +89,7 @@ export default class KeyValue extends React.Component<KeyValueProps, {}> {
|
||||
>
|
||||
{keyValue}
|
||||
</Text>
|
||||
{infoText && (
|
||||
{infoModalText && (
|
||||
<Text
|
||||
style={{
|
||||
color:
|
||||
@@ -103,10 +106,16 @@ export default class KeyValue extends React.Component<KeyValueProps, {}> {
|
||||
);
|
||||
|
||||
let Key: any;
|
||||
if (infoText) {
|
||||
if (infoModalText) {
|
||||
Key = (
|
||||
<TouchableOpacity
|
||||
onPress={() => toggleInfoModal(infoText, infoLink, infoNav)}
|
||||
onPress={() =>
|
||||
toggleInfoModal(
|
||||
infoModalText,
|
||||
infoModalLink,
|
||||
infoModalAdditionalButtons
|
||||
)
|
||||
}
|
||||
>
|
||||
{KeyBase}
|
||||
</TouchableOpacity>
|
||||
|
||||
@@ -110,12 +110,7 @@ export default class AlertModal extends React.Component<AlertModalProps, {}> {
|
||||
)}
|
||||
</Text>
|
||||
|
||||
<View
|
||||
style={{
|
||||
...styles.button,
|
||||
marginBottom: 25
|
||||
}}
|
||||
>
|
||||
<View style={styles.button}>
|
||||
<Button
|
||||
title={localeString(
|
||||
'components.AlertModal.resetEGS'
|
||||
@@ -129,7 +124,7 @@ export default class AlertModal extends React.Component<AlertModalProps, {}> {
|
||||
restartNeeded();
|
||||
}}
|
||||
tertiary
|
||||
></Button>
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
@@ -170,12 +165,7 @@ export default class AlertModal extends React.Component<AlertModalProps, {}> {
|
||||
{peers.join(', ')}
|
||||
</Text>
|
||||
|
||||
<View
|
||||
style={{
|
||||
...styles.button,
|
||||
marginBottom: 25
|
||||
}}
|
||||
>
|
||||
<View style={styles.button}>
|
||||
<Button
|
||||
title={localeString(
|
||||
'components.AlertModal.reviewPeers'
|
||||
@@ -187,7 +177,7 @@ export default class AlertModal extends React.Component<AlertModalProps, {}> {
|
||||
);
|
||||
}}
|
||||
tertiary
|
||||
></Button>
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
@@ -196,7 +186,7 @@ export default class AlertModal extends React.Component<AlertModalProps, {}> {
|
||||
title={localeString('general.close')}
|
||||
onPress={() => toggleAlertModal(false)}
|
||||
secondary
|
||||
></Button>
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</ModalBox>
|
||||
@@ -216,11 +206,9 @@ const styles = StyleSheet.create({
|
||||
fontSize: 20,
|
||||
marginBottom: 10
|
||||
},
|
||||
buttons: {
|
||||
width: '100%'
|
||||
},
|
||||
button: {
|
||||
marginTop: 20,
|
||||
width: 350
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
marginVertical: 20
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,8 +2,6 @@ import React from 'react';
|
||||
import { View, StyleSheet, Text } from 'react-native';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
|
||||
import NavigationService from '../../NavigationService';
|
||||
|
||||
import Button from '../Button';
|
||||
import ModalBox from '../ModalBox';
|
||||
|
||||
@@ -26,7 +24,7 @@ export default class InfoModal extends React.Component<InfoModalProps, {}> {
|
||||
showInfoModal,
|
||||
infoModalText,
|
||||
infoModalLink,
|
||||
infoModalNav,
|
||||
infoModalAdditionalButtons,
|
||||
toggleInfoModal
|
||||
} = ModalStore;
|
||||
|
||||
@@ -88,7 +86,27 @@ export default class InfoModal extends React.Component<InfoModalProps, {}> {
|
||||
))}
|
||||
|
||||
<View style={styles.buttons}>
|
||||
{(infoModalLink || infoModalNav) && (
|
||||
{infoModalAdditionalButtons?.map(
|
||||
({ title, callback }, index) => (
|
||||
<View
|
||||
key={index}
|
||||
style={{
|
||||
...styles.button,
|
||||
marginBottom: 25
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
title={title}
|
||||
onPress={() => {
|
||||
toggleInfoModal();
|
||||
if (callback) callback();
|
||||
}}
|
||||
tertiary
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
)}
|
||||
{infoModalLink && (
|
||||
<View
|
||||
style={{
|
||||
...styles.button,
|
||||
@@ -101,15 +119,10 @@ export default class InfoModal extends React.Component<InfoModalProps, {}> {
|
||||
)}
|
||||
onPress={() => {
|
||||
toggleInfoModal();
|
||||
if (infoModalLink)
|
||||
UrlUtils.goToUrl(infoModalLink);
|
||||
if (infoModalNav)
|
||||
NavigationService.navigate(
|
||||
infoModalNav
|
||||
);
|
||||
UrlUtils.goToUrl(infoModalLink);
|
||||
}}
|
||||
tertiary
|
||||
></Button>
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.button}>
|
||||
@@ -117,7 +130,7 @@ export default class InfoModal extends React.Component<InfoModalProps, {}> {
|
||||
title={localeString('general.close')}
|
||||
onPress={() => toggleInfoModal()}
|
||||
secondary
|
||||
></Button>
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
@@ -129,7 +142,8 @@ export default class InfoModal extends React.Component<InfoModalProps, {}> {
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
buttons: {
|
||||
width: '100%'
|
||||
width: '100%',
|
||||
alignItems: 'center'
|
||||
},
|
||||
button: {
|
||||
width: 350
|
||||
|
||||
+23
-8
@@ -11,17 +11,26 @@ interface TextProps {
|
||||
ModalStore?: ModalStore;
|
||||
style?: TextStyle;
|
||||
children?: string;
|
||||
infoText?: string | Array<string>;
|
||||
infoLink?: string;
|
||||
infoNav?: string;
|
||||
infoModalText?: string | Array<string>;
|
||||
infoModalLink?: string;
|
||||
infoModalAdditionalButtons?: Array<{
|
||||
title: string;
|
||||
callback?: () => void;
|
||||
}>;
|
||||
}
|
||||
|
||||
@inject('ModalStore')
|
||||
@observer
|
||||
export default class ZeusText extends React.Component<TextProps, {}> {
|
||||
render() {
|
||||
const { children, style, infoText, infoLink, infoNav, ModalStore } =
|
||||
this.props;
|
||||
const {
|
||||
children,
|
||||
style,
|
||||
infoModalText,
|
||||
infoModalLink,
|
||||
infoModalAdditionalButtons,
|
||||
ModalStore
|
||||
} = this.props;
|
||||
const { toggleInfoModal } = ModalStore!;
|
||||
|
||||
const CoreText = () => (
|
||||
@@ -35,7 +44,7 @@ export default class ZeusText extends React.Component<TextProps, {}> {
|
||||
>
|
||||
{children}
|
||||
</Text>
|
||||
{infoText && (
|
||||
{infoModalText && (
|
||||
<Text
|
||||
style={{
|
||||
color: themeColor('text'),
|
||||
@@ -49,10 +58,16 @@ export default class ZeusText extends React.Component<TextProps, {}> {
|
||||
</Row>
|
||||
);
|
||||
|
||||
if (infoText) {
|
||||
if (infoModalText) {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => toggleInfoModal(infoText, infoLink, infoNav)}
|
||||
onPress={() =>
|
||||
toggleInfoModal(
|
||||
infoModalText,
|
||||
infoModalLink,
|
||||
infoModalAdditionalButtons
|
||||
)
|
||||
}
|
||||
>
|
||||
<CoreText />
|
||||
</TouchableOpacity>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"general.error": "Error",
|
||||
"general.danger": "Danger!",
|
||||
"general.ok": "OK",
|
||||
"general.continueQuestion": "Continue?",
|
||||
"general.readOnlyWallet": "Read-only wallet",
|
||||
"general.custodialWallet": "Custodial wallet",
|
||||
"general.show": "Show",
|
||||
@@ -684,6 +685,7 @@
|
||||
"views.Settings.enabled": "Enabled",
|
||||
"views.Settings.disabled": "Disabled",
|
||||
"views.Settings.newPassword": "New Password",
|
||||
"views.Settings.createYourPassword": "Create your Password",
|
||||
"views.Settings.confirmPassword": "Confirm New Password",
|
||||
"views.Settings.newDuressPassword": "New Duress Password",
|
||||
"views.Settings.confirmDuressPassword": "Confirm Duress Password",
|
||||
@@ -777,10 +779,12 @@
|
||||
"views.Settings.SetDuressPassword.deletePassword": "Delete Duress Password",
|
||||
"views.Settings.SetDuressPassword.duressPasswordExplanation": "Once set, you can enter your duress password on the login screen to delete all of your wallet configurations.",
|
||||
"views.Settings.SetPin.title": "Set / Change PIN",
|
||||
"views.Settings.Security.BiometryRequiresPinOrPassword": "To enable biometric authentication, you need to set up a PIN or Password first as a backup method.",
|
||||
"views.Settings.Security.FaceID.title": "FaceID",
|
||||
"views.Settings.Security.TouchID.title": "TouchID",
|
||||
"views.Settings.Security.Biometrics.title": "Biometrics",
|
||||
"views.Settings.Security.Biometrics.prompt": "Unlock",
|
||||
"views.Settings.Security.biometricsWillBeDisabled": "Deleting your PIN or Password will also disable biometric authentication.",
|
||||
"views.Lockscreen.Biometrics.prompt": "Unlock Zeus",
|
||||
"views.Settings.SetPin.noMatch": "PINs do not match. Please resubmit.",
|
||||
"views.Settings.SetPin.invalid": "PIN and Duress PIN cannot be equal.",
|
||||
|
||||
@@ -9,7 +9,10 @@ export default class ModalStore {
|
||||
@observable public clipboardValue: string;
|
||||
@observable public infoModalText: string | Array<string> | undefined;
|
||||
@observable public infoModalLink: string | undefined;
|
||||
@observable public infoModalNav: string | undefined;
|
||||
@observable public infoModalAdditionalButtons?: Array<{
|
||||
title: string;
|
||||
callback?: () => void;
|
||||
}>;
|
||||
@observable public alertModalText: string | Array<string> | undefined;
|
||||
@observable public alertModalLink: string | undefined;
|
||||
@observable public alertModalNav: string | undefined;
|
||||
@@ -25,12 +28,12 @@ export default class ModalStore {
|
||||
public toggleInfoModal = (
|
||||
text?: string | Array<string>,
|
||||
link?: string,
|
||||
nav?: string
|
||||
buttons?: Array<{ title: string; callback?: () => void }>
|
||||
) => {
|
||||
this.showInfoModal = text ? true : false;
|
||||
this.infoModalText = text;
|
||||
this.infoModalLink = link;
|
||||
this.infoModalNav = nav;
|
||||
this.infoModalAdditionalButtons = buttons;
|
||||
};
|
||||
|
||||
@action
|
||||
@@ -73,7 +76,7 @@ export default class ModalStore {
|
||||
this.showInfoModal = false;
|
||||
this.infoModalText = '';
|
||||
this.infoModalLink = '';
|
||||
this.infoModalNav = '';
|
||||
this.infoModalAdditionalButtons = undefined;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -666,10 +666,10 @@ export default class ChannelView extends React.Component<
|
||||
}
|
||||
/>
|
||||
}
|
||||
infoText={localeString(
|
||||
infoModalText={localeString(
|
||||
'views.Channel.localReserve.info'
|
||||
)}
|
||||
infoLink="https://bitcoin.design/guide/how-it-works/liquidity/#what-is-a-channel-reserve"
|
||||
infoModalLink="https://bitcoin.design/guide/how-it-works/liquidity/#what-is-a-channel-reserve"
|
||||
indicatorColor={themeColor('outboundReserve')}
|
||||
/>
|
||||
)}
|
||||
@@ -685,10 +685,10 @@ export default class ChannelView extends React.Component<
|
||||
toggleable
|
||||
/>
|
||||
}
|
||||
infoText={localeString(
|
||||
infoModalText={localeString(
|
||||
'views.Channel.remoteReserve.info'
|
||||
)}
|
||||
infoLink="https://bitcoin.design/guide/how-it-works/liquidity/#what-is-a-channel-reserve"
|
||||
infoModalLink="https://bitcoin.design/guide/how-it-works/liquidity/#what-is-a-channel-reserve"
|
||||
indicatorColor={themeColor('inboundReserve')}
|
||||
/>
|
||||
)}
|
||||
@@ -824,7 +824,7 @@ export default class ChannelView extends React.Component<
|
||||
...styles.text,
|
||||
color: themeColor('text')
|
||||
}}
|
||||
infoText={localeString(
|
||||
infoModalText={localeString(
|
||||
'views.Channel.externalAddress.info'
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -313,12 +313,14 @@ export default class Lockscreen extends React.Component<
|
||||
const { updateSettings } = SettingsStore;
|
||||
|
||||
// duress pin is also deleted when pin is deleted
|
||||
// biometry is also disabled when pin is deleted
|
||||
updateSettings({
|
||||
pin: '',
|
||||
duressPin: '',
|
||||
authenticationAttempts: 0
|
||||
authenticationAttempts: 0,
|
||||
isBiometryEnabled: false
|
||||
}).then(() => {
|
||||
navigation.pop(2);
|
||||
navigation.popTo('Security');
|
||||
});
|
||||
};
|
||||
|
||||
@@ -330,7 +332,7 @@ export default class Lockscreen extends React.Component<
|
||||
duressPin: '',
|
||||
authenticationAttempts: 0
|
||||
}).then(() => {
|
||||
navigation.pop(2);
|
||||
navigation.popTo('Security');
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
+17
-7
@@ -1984,7 +1984,7 @@ export default class Receive extends React.Component<
|
||||
),
|
||||
top: 20
|
||||
}}
|
||||
infoText={[
|
||||
infoModalText={[
|
||||
localeString(
|
||||
'views.Receive.lspSwitchExplainer1'
|
||||
),
|
||||
@@ -1992,7 +1992,17 @@ export default class Receive extends React.Component<
|
||||
'views.Receive.lspSwitchExplainer2'
|
||||
)
|
||||
]}
|
||||
infoNav="LspExplanationOverview"
|
||||
infoModalAdditionalButtons={[
|
||||
{
|
||||
title: localeString(
|
||||
'general.learnMore'
|
||||
),
|
||||
callback: () =>
|
||||
navigation.navigate(
|
||||
'LspExplanationOverview'
|
||||
)
|
||||
}
|
||||
]}
|
||||
>
|
||||
{localeString(
|
||||
'views.Settings.LSP.enableLSP'
|
||||
@@ -2488,7 +2498,7 @@ export default class Receive extends React.Component<
|
||||
),
|
||||
top: 20
|
||||
}}
|
||||
infoText={[
|
||||
infoModalText={[
|
||||
localeString(
|
||||
'views.Receive.routeHintSwitchExplainer1'
|
||||
),
|
||||
@@ -2622,7 +2632,7 @@ export default class Receive extends React.Component<
|
||||
),
|
||||
top: 20
|
||||
}}
|
||||
infoText={[
|
||||
infoModalText={[
|
||||
localeString(
|
||||
'views.Receive.ampSwitchExplainer1'
|
||||
),
|
||||
@@ -2630,7 +2640,7 @@ export default class Receive extends React.Component<
|
||||
'views.Receive.ampSwitchExplainer2'
|
||||
)
|
||||
]}
|
||||
infoLink="https://docs.lightning.engineering/lightning-network-tools/lnd/amp"
|
||||
infoModalLink="https://docs.lightning.engineering/lightning-network-tools/lnd/amp"
|
||||
>
|
||||
{localeString(
|
||||
'views.Receive.ampInvoice'
|
||||
@@ -2660,7 +2670,7 @@ export default class Receive extends React.Component<
|
||||
),
|
||||
top: 20
|
||||
}}
|
||||
infoText={[
|
||||
infoModalText={[
|
||||
localeString(
|
||||
'views.Receive.blindedPathsExplainer1'
|
||||
),
|
||||
@@ -2668,7 +2678,7 @@ export default class Receive extends React.Component<
|
||||
'views.Receive.blindedPathsExplainer2'
|
||||
)
|
||||
]}
|
||||
infoLink="https://lightningprivacy.com/en/blinded-trampoline"
|
||||
infoModalLink="https://lightningprivacy.com/en/blinded-trampoline"
|
||||
>
|
||||
{localeString(
|
||||
'views.Receive.blindedPaths'
|
||||
|
||||
@@ -342,7 +342,7 @@ export default class InvoicesSettings extends React.Component<
|
||||
color: themeColor('secondaryText'),
|
||||
top: 20
|
||||
}}
|
||||
infoText={[
|
||||
infoModalText={[
|
||||
localeString(
|
||||
'views.Receive.routeHintSwitchExplainer1'
|
||||
),
|
||||
@@ -386,7 +386,7 @@ export default class InvoicesSettings extends React.Component<
|
||||
color: themeColor('secondaryText'),
|
||||
top: 20
|
||||
}}
|
||||
infoText={[
|
||||
infoModalText={[
|
||||
localeString(
|
||||
'views.Receive.ampSwitchExplainer1'
|
||||
),
|
||||
@@ -394,6 +394,7 @@ export default class InvoicesSettings extends React.Component<
|
||||
'views.Receive.ampSwitchExplainer2'
|
||||
)
|
||||
]}
|
||||
infoModalLink="https://docs.lightning.engineering/lightning-network-tools/lnd/amp"
|
||||
>
|
||||
{localeString('views.Receive.ampInvoice')}
|
||||
</Text>
|
||||
@@ -429,7 +430,7 @@ export default class InvoicesSettings extends React.Component<
|
||||
color: themeColor('secondaryText'),
|
||||
top: 20
|
||||
}}
|
||||
infoText={[
|
||||
infoModalText={[
|
||||
localeString(
|
||||
'views.Receive.blindedPathsExplainer1'
|
||||
),
|
||||
|
||||
@@ -206,7 +206,7 @@ export default class LightningAddressSettings extends React.Component<
|
||||
fontFamily: 'PPNeueMontreal-Book',
|
||||
fontSize: 17
|
||||
}}
|
||||
infoText={[
|
||||
infoModalText={[
|
||||
localeString(
|
||||
'views.Settings.LightningAddressSettings.routeHintsExplainer1'
|
||||
),
|
||||
|
||||
@@ -343,7 +343,7 @@ export default class LightningAddress extends React.Component<
|
||||
),
|
||||
left: 5
|
||||
}}
|
||||
infoText={[
|
||||
infoModalText={[
|
||||
localeString(
|
||||
'views.Settings.LightningAddress.statusExplainer1'
|
||||
),
|
||||
|
||||
@@ -173,7 +173,7 @@ export default class Privacy extends React.Component<
|
||||
fontFamily: 'PPNeueMontreal-Book',
|
||||
left: -10
|
||||
}}
|
||||
infoText={localeString(
|
||||
infoModalText={localeString(
|
||||
'views.Settings.Privacy.clipboard.explainer'
|
||||
).replace('Zeus', 'ZEUS')}
|
||||
>
|
||||
@@ -218,7 +218,7 @@ export default class Privacy extends React.Component<
|
||||
fontFamily: 'PPNeueMontreal-Book',
|
||||
left: -10
|
||||
}}
|
||||
infoText={[
|
||||
infoModalText={[
|
||||
localeString(
|
||||
'views.Settings.Privacy.lurkerMode.explainer1'
|
||||
),
|
||||
|
||||
+90
-21
@@ -10,6 +10,7 @@ import Screen from '../../components/Screen';
|
||||
import Switch from '../../components/Switch';
|
||||
|
||||
import SettingsStore from '../../stores/SettingsStore';
|
||||
import ModalStore from '../../stores/ModalStore';
|
||||
|
||||
import { verifyBiometry } from '../../utils/BiometricUtils';
|
||||
import { localeString } from '../../utils/LocaleUtils';
|
||||
@@ -18,6 +19,7 @@ import { themeColor } from '../../utils/ThemeUtils';
|
||||
interface SecurityProps {
|
||||
navigation: StackNavigationProp<any, any>;
|
||||
SettingsStore: SettingsStore;
|
||||
ModalStore: ModalStore;
|
||||
}
|
||||
|
||||
interface SecurityState {
|
||||
@@ -28,6 +30,7 @@ interface SecurityState {
|
||||
passphraseExists: boolean;
|
||||
supportedBiometryType: BiometryType | undefined;
|
||||
isBiometryEnabled: boolean | undefined;
|
||||
pendingBiometricsEnable: boolean;
|
||||
}
|
||||
|
||||
const possibleSecurityItems = [
|
||||
@@ -57,7 +60,7 @@ const possibleSecurityItems = [
|
||||
}
|
||||
];
|
||||
|
||||
@inject('SettingsStore')
|
||||
@inject('SettingsStore', 'ModalStore')
|
||||
@observer
|
||||
export default class Security extends React.Component<
|
||||
SecurityProps,
|
||||
@@ -65,9 +68,6 @@ export default class Security extends React.Component<
|
||||
> {
|
||||
constructor(props: SecurityProps) {
|
||||
super(props);
|
||||
|
||||
this.handleBiometricsSwitchChange =
|
||||
this.handleBiometricsSwitchChange.bind(this);
|
||||
}
|
||||
|
||||
state = {
|
||||
@@ -77,7 +77,8 @@ export default class Security extends React.Component<
|
||||
pinExists: false,
|
||||
passphraseExists: false,
|
||||
supportedBiometryType: undefined,
|
||||
isBiometryEnabled: undefined
|
||||
isBiometryEnabled: undefined,
|
||||
pendingBiometricsEnable: false
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
@@ -145,25 +146,73 @@ export default class Security extends React.Component<
|
||||
displaySecurityItems: minPinItems
|
||||
});
|
||||
}
|
||||
|
||||
// If user tried to enable biometrics, but was forced to first set up pin or password,
|
||||
// call handleBiometricsSwitchChange again
|
||||
if (
|
||||
this.state.pendingBiometricsEnable &&
|
||||
(settings.pin || settings.passphrase)
|
||||
) {
|
||||
this.handleBiometricsSwitchChange(true);
|
||||
}
|
||||
};
|
||||
|
||||
async handleBiometricsSwitchChange(value: boolean): Promise<void> {
|
||||
const isVerified = await verifyBiometry(
|
||||
localeString(`views.Settings.Security.Biometrics.prompt`)
|
||||
);
|
||||
const { SettingsStore, ModalStore, navigation } = this.props;
|
||||
|
||||
if (isVerified) {
|
||||
const {
|
||||
SettingsStore: { updateSettings }
|
||||
} = this.props;
|
||||
if (value) {
|
||||
const settings = SettingsStore.settings;
|
||||
if (!settings.pin && !settings.passphrase) {
|
||||
this.setState({ pendingBiometricsEnable: true });
|
||||
ModalStore.toggleInfoModal(
|
||||
localeString(
|
||||
'views.Settings.Security.BiometryRequiresPinOrPassword'
|
||||
),
|
||||
undefined,
|
||||
[
|
||||
{
|
||||
title: localeString(
|
||||
'views.Settings.createYourPassword'
|
||||
),
|
||||
callback: () => navigation.navigate('SetPassword')
|
||||
},
|
||||
{
|
||||
title: localeString('views.Settings.newPin'),
|
||||
callback: () => navigation.navigate('SetPin')
|
||||
}
|
||||
]
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isBiometryEnabled: value
|
||||
});
|
||||
const isVerified = await verifyBiometry(
|
||||
localeString('views.Settings.Security.Biometrics.prompt')
|
||||
);
|
||||
|
||||
updateSettings({
|
||||
isBiometryEnabled: value
|
||||
});
|
||||
if (isVerified) {
|
||||
this.setState({
|
||||
isBiometryEnabled: value,
|
||||
pendingBiometricsEnable: false
|
||||
});
|
||||
|
||||
SettingsStore.updateSettings({
|
||||
isBiometryEnabled: value
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const isVerified = await verifyBiometry(
|
||||
localeString(`views.Settings.Security.Biometrics.prompt`)
|
||||
);
|
||||
|
||||
if (isVerified) {
|
||||
this.setState({
|
||||
isBiometryEnabled: value
|
||||
});
|
||||
|
||||
SettingsStore.updateSettings({
|
||||
isBiometryEnabled: value
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,11 +226,31 @@ export default class Security extends React.Component<
|
||||
);
|
||||
|
||||
navigateSecurity = (item: any) => {
|
||||
const { navigation, SettingsStore } = this.props;
|
||||
const { navigation, SettingsStore, ModalStore } = this.props;
|
||||
const { settings }: any = SettingsStore;
|
||||
const { isBiometryEnabled } = this.state;
|
||||
|
||||
if (!(settings.passphrase || settings.pin)) {
|
||||
navigation.navigate(item.screen);
|
||||
} else if (item.action === 'DeletePin' && isBiometryEnabled) {
|
||||
ModalStore.toggleInfoModal(
|
||||
[
|
||||
localeString(
|
||||
'views.Settings.Security.biometricsWillBeDisabled'
|
||||
),
|
||||
localeString('general.continueQuestion')
|
||||
],
|
||||
undefined,
|
||||
[
|
||||
{
|
||||
title: localeString('general.ok'),
|
||||
callback: () =>
|
||||
navigation.navigate('Lockscreen', {
|
||||
deletePin: true
|
||||
})
|
||||
}
|
||||
]
|
||||
);
|
||||
} else if (item.action === 'DeletePin') {
|
||||
navigation.navigate('Lockscreen', {
|
||||
deletePin: true
|
||||
@@ -281,8 +350,8 @@ export default class Security extends React.Component<
|
||||
|
||||
<Switch
|
||||
value={isBiometryEnabled}
|
||||
onValueChange={
|
||||
this.handleBiometricsSwitchChange
|
||||
onValueChange={(value: boolean) =>
|
||||
this.handleBiometricsSwitchChange(value)
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
@@ -12,10 +12,12 @@ import TextInput from '../../components/TextInput';
|
||||
import { localeString } from '../../utils/LocaleUtils';
|
||||
import { themeColor } from '../../utils/ThemeUtils';
|
||||
import SettingsStore from '../../stores/SettingsStore';
|
||||
import ModalStore from '../../stores/ModalStore';
|
||||
|
||||
interface SetPassphraseProps {
|
||||
navigation: StackNavigationProp<any, any>;
|
||||
SettingsStore: SettingsStore;
|
||||
ModalStore: ModalStore;
|
||||
}
|
||||
|
||||
interface SetPassphraseState {
|
||||
@@ -26,9 +28,10 @@ interface SetPassphraseState {
|
||||
passphraseInvalidError: boolean;
|
||||
passphraseEmptyError: boolean;
|
||||
confirmDelete: boolean;
|
||||
isBiometryEnabled: boolean;
|
||||
}
|
||||
|
||||
@inject('SettingsStore')
|
||||
@inject('SettingsStore', 'ModalStore')
|
||||
@observer
|
||||
export default class SetPassphrase extends React.Component<
|
||||
SetPassphraseProps,
|
||||
@@ -41,14 +44,17 @@ export default class SetPassphrase extends React.Component<
|
||||
passphraseMismatchError: false,
|
||||
passphraseInvalidError: false,
|
||||
passphraseEmptyError: false,
|
||||
confirmDelete: false
|
||||
confirmDelete: false,
|
||||
isBiometryEnabled: false
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
const { SettingsStore } = this.props;
|
||||
const { getSettings } = SettingsStore;
|
||||
const settings = await getSettings();
|
||||
const settings = await SettingsStore.getSettings();
|
||||
|
||||
this.setState({
|
||||
isBiometryEnabled: settings.isBiometryEnabled
|
||||
});
|
||||
if (settings.passphrase) {
|
||||
this.setState({ savedPassphrase: settings.passphrase });
|
||||
}
|
||||
@@ -96,9 +102,7 @@ export default class SetPassphrase extends React.Component<
|
||||
await updateSettings({ passphrase }).then(() => {
|
||||
setLoginStatus(true);
|
||||
getSettings();
|
||||
navigation.popTo('Settings', {
|
||||
refresh: true
|
||||
});
|
||||
navigation.popTo('Security', { refresh: true });
|
||||
});
|
||||
};
|
||||
|
||||
@@ -110,12 +114,10 @@ export default class SetPassphrase extends React.Component<
|
||||
|
||||
await updateSettings({
|
||||
duressPassphrase: '',
|
||||
passphrase: ''
|
||||
}).then(() => {
|
||||
navigation.popTo('Settings', {
|
||||
refresh: true
|
||||
});
|
||||
passphrase: '',
|
||||
isBiometryEnabled: false
|
||||
});
|
||||
navigation.popTo('Security', { refresh: true });
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -251,6 +253,22 @@ export default class SetPassphrase extends React.Component<
|
||||
this.setState({
|
||||
confirmDelete: true
|
||||
});
|
||||
} else if (this.state.isBiometryEnabled) {
|
||||
this.props.ModalStore.toggleInfoModal(
|
||||
localeString(
|
||||
'views.Settings.Security.biometricsWillBeDisabled'
|
||||
),
|
||||
undefined,
|
||||
[
|
||||
{
|
||||
title: localeString(
|
||||
'general.ok'
|
||||
),
|
||||
callback: () =>
|
||||
this.deletePassword()
|
||||
}
|
||||
]
|
||||
);
|
||||
} else {
|
||||
this.deletePassword();
|
||||
}
|
||||
|
||||
@@ -95,9 +95,7 @@ export default class SetPin extends React.Component<SetPinProps, SetPinState> {
|
||||
await updateSettings({ pin }).then(() => {
|
||||
setLoginStatus(true);
|
||||
getSettings();
|
||||
navigation.popTo('Settings', {
|
||||
refresh: true
|
||||
});
|
||||
navigation.popTo('Security');
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user