From f56b31b6c2a0727a1990fa5fd3d653bd3dd7d611 Mon Sep 17 00:00:00 2001 From: deregs Date: Sat, 23 Apr 2022 19:26:39 -0400 Subject: [PATCH 1/2] add scramble pin toggle --- components/Pin.tsx | 6 +- stores/SettingsStore.ts | 4 +- views/Lockscreen.tsx | 11 +- views/Settings/AddEditNode.tsx | 30 ++-- views/Settings/Currency.tsx | 2 + views/Settings/Language.tsx | 2 + views/Settings/Nodes.tsx | 2 + views/Settings/Privacy.tsx | 10 ++ views/Settings/Security.tsx | 281 +++++++++++++++++++++----------- views/Settings/SetDuressPin.tsx | 8 +- views/Settings/SetPin.tsx | 38 +++-- views/Settings/Theme.tsx | 2 + 12 files changed, 255 insertions(+), 141 deletions(-) diff --git a/components/Pin.tsx b/components/Pin.tsx index 916e9225a..abc64198f 100644 --- a/components/Pin.tsx +++ b/components/Pin.tsx @@ -9,6 +9,7 @@ interface PinProps { hidePinLength: boolean; pinLength?: number; pinConfirm?: boolean; + shuffle?: boolean; } export default function Pin({ @@ -16,7 +17,8 @@ export default function Pin({ onPinChange = () => void 0, hidePinLength, pinLength = 4, - pinConfirm = false + pinConfirm = false, + shuffle = true }: PinProps) { const [pinValue, setPinValue] = useState(''); const maxLength = 8; @@ -84,7 +86,7 @@ export default function Pin({ clearValue={clearValue} deleteValue={deleteValue} submitValue={submitValue} - shuffle={true} + shuffle={shuffle} hidePinLength={hidePinLength} minLength={minLength} maxLength={maxLength} diff --git a/stores/SettingsStore.ts b/stores/SettingsStore.ts index 96c2cbb05..4e5c82a14 100644 --- a/stores/SettingsStore.ts +++ b/stores/SettingsStore.ts @@ -37,6 +37,7 @@ interface Settings { duressPassphrase?: string; pin?: string; duressPin?: string; + scramblePin?: boolean; authenticationAttempts?: number; fiat?: string; locale?: string; @@ -137,7 +138,8 @@ export default class SettingsStore { clipboard: true, lurkerMode: false, enableMempoolRates: true - } + }, + scramblePin: true }; @observable public loading = false; @observable btcPayError: string | null; diff --git a/views/Lockscreen.tsx b/views/Lockscreen.tsx index 0a5e29d3c..f9125280c 100644 --- a/views/Lockscreen.tsx +++ b/views/Lockscreen.tsx @@ -63,7 +63,6 @@ export default class Lockscreen extends React.Component< UNSAFE_componentWillMount() { const { SettingsStore, navigation } = this.props; const { settings } = SettingsStore; - const modifySecurityScreen: string = navigation.getParam( 'modifySecurityScreen' ); @@ -185,6 +184,7 @@ export default class Lockscreen extends React.Component< duressPassphrase: updatedSettings?.duressPassphrase, pin: updatedSettings?.pin, duressPin: updatedSettings?.duressPin, + scramblePin: updatedSettings?.scramblePin, authenticationAttempts, fiat: updatedSettings?.fiat, locale: updatedSettings?.locale, @@ -220,6 +220,7 @@ export default class Lockscreen extends React.Component< duressPassphrase: settings.duressPassphrase, pin: '', duressPin: '', + scramblePin: settings.scramblePin, authenticationAttempts: 0, fiat: settings.fiat, locale: settings.locale, @@ -243,6 +244,7 @@ export default class Lockscreen extends React.Component< duressPassphrase: settings.duressPassphrase, pin: settings.pin, duressPin: '', + scramblePin: settings.scramblePin, authenticationAttempts: 0, fiat: settings.fiat, locale: settings.locale, @@ -266,6 +268,7 @@ export default class Lockscreen extends React.Component< duressPassphrase: settings.duressPassphrase, pin: settings.pin, duressPin: settings.duressPin, + scramblePin: settings.scramblePin, authenticationAttempts: 0, fiat: settings.fiat, locale: settings.locale, @@ -289,6 +292,7 @@ export default class Lockscreen extends React.Component< duressPassphrase: '', pin: '', duressPin: '', + scramblePin: settings.scramblePin, authenticationAttempts: 0, fiat: settings.fiat, locale: settings.locale, @@ -312,6 +316,7 @@ export default class Lockscreen extends React.Component< duressPassphrase: settings.duressPassphrase, pin: settings.pin, duressPin: settings.duressPin, + scramblePin: settings.scramblePin, authenticationAttempts: 0, fiat: settings.fiat, locale: settings.locale, @@ -340,7 +345,8 @@ export default class Lockscreen extends React.Component< }; render() { - const { navigation } = this.props; + const { navigation, SettingsStore } = this.props; + const { settings } = SettingsStore; const { passphrase, passphraseAttempt, @@ -492,6 +498,7 @@ export default class Lockscreen extends React.Component< } hidePinLength={true} pinLength={pin.length} + shuffle={settings.scramblePin} /> diff --git a/views/Settings/AddEditNode.tsx b/views/Settings/AddEditNode.tsx index ba1e23a0b..102ec1e19 100644 --- a/views/Settings/AddEditNode.tsx +++ b/views/Settings/AddEditNode.tsx @@ -285,6 +285,7 @@ export default class AddEditNode extends React.Component< duressPassphrase: settings.duressPassphrase, pin: settings.pin, duressPin: settings.duressPin, + scramblePin: settings.scramblePin, authenticationAttempts: settings.authenticationAttempts, privacy: settings.privacy @@ -306,7 +307,7 @@ export default class AddEditNode extends React.Component< copyNodeConfig = () => { const { SettingsStore, navigation } = this.props; - const { setSettings, settings } = SettingsStore; + const { settings } = SettingsStore; const { nickname, host, @@ -322,7 +323,7 @@ export default class AddEditNode extends React.Component< implementation, certVerification } = this.state; - const { nodes, lurkerMode, passphrase, fiat, locale } = settings; + const { nodes } = settings; const node = { nickname: `${nickname} copy`, @@ -340,24 +341,11 @@ export default class AddEditNode extends React.Component< enableTor }; - setSettings( - JSON.stringify({ - nodes, - theme: settings.theme, - selectedNode: settings.selectedNode, - fiat, - locale, - lurkerMode, - passphrase, - privacy: settings.privacy - }) - ).then(() => { - navigation.navigate('AddEditNode', { - node, - newEntry: true, - saved: false, - index: Number(nodes.length) - }); + navigation.navigate('AddEditNode', { + node, + newEntry: true, + saved: false, + index: Number(nodes.length) }); }; @@ -386,6 +374,7 @@ export default class AddEditNode extends React.Component< duressPassphrase: settings.duressPassphrase, pin: settings.pin, duressPin: settings.duressPin, + scramblePin: settings.scramblePin, authenticationAttempts: settings.authenticationAttempts, privacy: settings.privacy }) @@ -411,6 +400,7 @@ export default class AddEditNode extends React.Component< duressPassphrase: settings.duressPassphrase, pin: settings.pin, duressPin: settings.duressPin, + scramblePin: settings.scramblePin, authenticationAttempts: settings.authenticationAttempts, privacy: settings.privacy }) diff --git a/views/Settings/Currency.tsx b/views/Settings/Currency.tsx index a97f545b0..04a5b5a7f 100644 --- a/views/Settings/Currency.tsx +++ b/views/Settings/Currency.tsx @@ -143,6 +143,8 @@ export default class Currency extends React.Component< pin: settings.pin, duressPin: settings.duressPin, + scramblePin: + settings.scramblePin, authenticationAttempts: settings.authenticationAttempts, locale: settings.locale, diff --git a/views/Settings/Language.tsx b/views/Settings/Language.tsx index f689520f4..2308a961e 100644 --- a/views/Settings/Language.tsx +++ b/views/Settings/Language.tsx @@ -135,6 +135,8 @@ export default class Language extends React.Component< pin: settings.pin, duressPin: settings.duressPin, + scramblePin: + settings.scramblePin, authenticationAttempts: settings.authenticationAttempts, locale: item.value, diff --git a/views/Settings/Nodes.tsx b/views/Settings/Nodes.tsx index 8397c1ad6..7b49909e3 100644 --- a/views/Settings/Nodes.tsx +++ b/views/Settings/Nodes.tsx @@ -140,6 +140,8 @@ export default class Nodes extends React.Component { settings.duressPassphrase, pin: settings.pin, duressPin: settings.duressPin, + scramblePin: + settings.scramblePin, authenticationAttempts: settings.authenticationAttempts, privacy: settings.privacy diff --git a/views/Settings/Privacy.tsx b/views/Settings/Privacy.tsx index b076b6081..3e7755118 100644 --- a/views/Settings/Privacy.tsx +++ b/views/Settings/Privacy.tsx @@ -147,6 +147,8 @@ export default class Privacy extends React.Component< settings.duressPassphrase, pin: settings.pin, duressPin: settings.duressPin, + scramblePin: + settings.scramblePin, authenticationAttempts: settings.authenticationAttempts, locale: settings.locale, @@ -211,6 +213,8 @@ export default class Privacy extends React.Component< pin: settings.pin, duressPin: settings.duressPin, + scramblePin: + settings.scramblePin, authenticationAttempts: settings.authenticationAttempts, locale: settings.locale, @@ -287,6 +291,8 @@ export default class Privacy extends React.Component< pin: settings.pin, duressPin: settings.duressPin, + scramblePin: + settings.scramblePin, authenticationAttempts: settings.authenticationAttempts, locale: settings.locale, @@ -366,6 +372,8 @@ export default class Privacy extends React.Component< pin: settings.pin, duressPin: settings.duressPin, + scramblePin: + settings.scramblePin, authenticationAttempts: settings.authenticationAttempts, locale: settings.locale, @@ -446,6 +454,8 @@ export default class Privacy extends React.Component< pin: settings.pin, duressPin: settings.duressPin, + scramblePin: + settings.scramblePin, authenticationAttempts: settings.authenticationAttempts, locale: settings.locale, diff --git a/views/Settings/Security.tsx b/views/Settings/Security.tsx index 3fa5394fa..8712f87ac 100644 --- a/views/Settings/Security.tsx +++ b/views/Settings/Security.tsx @@ -1,75 +1,102 @@ +import { inject, observer } from 'mobx-react'; import * as React from 'react'; -import { FlatList, View } from 'react-native'; +import { FlatList, View, ScrollView, Switch } from 'react-native'; import { Header, Icon, ListItem } from 'react-native-elements'; - -import stores from '../../stores/Stores'; - import { localeString } from './../../utils/LocaleUtils'; import { themeColor } from './../../utils/ThemeUtils'; +import SettingsStore from '../../stores/SettingsStore'; interface SecurityProps { navigation: any; + SettingsStore: SettingsStore; } -function Security(props: SecurityProps) { - const { navigation } = props; - const { settings } = stores.settingsStore; +interface SecurityState { + scramblePin: boolean; + displaySecurityItems: Array; +} - let displaySecurityItems = []; - const possibleSecurityItems = [ - { - label: localeString('views.Settings.SetPassword.title'), - screen: 'SetPassword' - }, - { - label: localeString('views.Settings.SetDuressPassword.title'), - screen: 'SetDuressPassword' - }, - { - label: localeString('views.Settings.SetPin.title'), - screen: 'SetPin' - }, - { - label: localeString('views.Settings.Security.deletePIN'), - action: 'DeletePin' - }, - { - label: localeString('views.Settings.SetDuressPin.title'), - screen: 'SetDuressPin' - }, - { - label: localeString('views.Settings.Security.deleteDuressPIN'), - action: 'DeleteDuressPin' - } - // { label: 'Verify TLS Certificate', url: 'https://twitter.com/ZeusLN' } - ]; +const possibleSecurityItems = [ + { + label: localeString('views.Settings.SetPassword.title'), + screen: 'SetPassword' + }, + { + label: localeString('views.Settings.SetDuressPassword.title'), + screen: 'SetDuressPassword' + }, + { + label: localeString('views.Settings.SetPin.title'), + screen: 'SetPin' + }, + { + label: localeString('views.Settings.Security.deletePIN'), + action: 'DeletePin' + }, + { + label: localeString('views.Settings.SetDuressPin.title'), + screen: 'SetDuressPin' + }, + { + label: localeString('views.Settings.Security.deleteDuressPIN'), + action: 'DeleteDuressPin' + } +]; - // Three cases: - // 1) If no passphrase or pin is set, allow user to set passphrase or pin - // 2) If passphrase is set, allow user to set passphrase or duress passphrase - // 3) If pin is set, allow user to set pin or duress pin - if (!settings.passphrase && !settings.pin) { - displaySecurityItems = [ - possibleSecurityItems[0], - possibleSecurityItems[2] - ]; - } else if (settings.passphrase) { - displaySecurityItems = [ - possibleSecurityItems[0], - possibleSecurityItems[1] - ]; - } else if (settings.pin) { - displaySecurityItems = [ - possibleSecurityItems[2], - possibleSecurityItems[3], - possibleSecurityItems[4] - ]; - if (settings.duressPin) { - displaySecurityItems.push(possibleSecurityItems[5]); +@inject('SettingsStore') +@observer +export default class Security extends React.Component< + SecurityProps, + SecurityState +> { + state = { + scramblePin: true, + displaySecurityItems: [] + }; + + async componentDidMount() { + const { SettingsStore } = this.props; + const { getSettings } = SettingsStore; + const settings = await getSettings(); + + this.setState({ + scramblePin: settings.scramblePin ?? true + }); + + // Three cases: + // 1) If no passphrase or pin is set, allow user to set passphrase or pin + // 2) If passphrase is set, allow user to set passphrase or duress passphrase + // 3) If pin is set, allow user to set pin or duress pin + if (!settings.passphrase && !settings.pin) { + this.setState({ + displaySecurityItems: [ + possibleSecurityItems[0], + possibleSecurityItems[2] + ] + }); + } else if (settings.passphrase) { + this.setState({ + displaySecurityItems: [ + possibleSecurityItems[0], + possibleSecurityItems[1] + ] + }); + } else if (settings.pin) { + const minPinItems = [ + possibleSecurityItems[2], + possibleSecurityItems[3], + possibleSecurityItems[4] + ]; + if (settings.duressPin) { + minPinItems.push(possibleSecurityItems[5]); + } + this.setState({ + displaySecurityItems: minPinItems + }); } } - const renderSeparator = () => ( + renderSeparator = () => ( ); - const BackButton = () => ( - navigation.goBack()} - color={themeColor('text')} - underlayColor="transparent" - /> - ); + navigateSecurity = (item: any) => { + const { navigation, SettingsStore } = this.props; + const { settings }: any = SettingsStore; - const navigateSecurity = (item: any) => { if (!(settings.passphrase || settings.pin)) { navigation.navigate(item.screen); } else if (item.action === 'DeletePin') { @@ -106,14 +127,14 @@ function Security(props: SecurityProps) { } }; - const renderItem = ({ item }) => { + renderItem = ({ item }) => { return ( navigateSecurity(item)} + onPress={() => this.navigateSecurity(item)} > -
} - centerComponent={{ - text: localeString('views.Settings.Security.title'), - style: { - color: themeColor('text'), - fontFamily: 'Lato-Regular' - } - }} - backgroundColor={themeColor('background')} - containerStyle={{ - borderBottomWidth: 0 - }} - /> - `${item.label}-${index}`} - ItemSeparatorComponent={renderSeparator} + render() { + const { navigation, SettingsStore } = this.props; + const { scramblePin, displaySecurityItems } = this.state; + const { setSettings, getSettings, settings }: any = SettingsStore; + + const BackButton = () => ( + navigation.goBack()} + color={themeColor('text')} + underlayColor="transparent" /> - - ); -} + ); -export default Security; + return ( + +
} + centerComponent={{ + text: localeString('views.Settings.Security.title'), + style: { + color: themeColor('text'), + fontFamily: 'Lato-Regular' + } + }} + backgroundColor={themeColor('background')} + containerStyle={{ + borderBottomWidth: 0 + }} + /> + `${item.label}-${index}`} + ItemSeparatorComponent={this.renderSeparator} + /> + + + + {'Scramble PIN numbers'} + + + { + this.setState({ + scramblePin: !scramblePin + }); + const settings = await getSettings(); + setSettings( + JSON.stringify({ + nodes: settings.nodes, + theme: settings.theme, + selectedNode: settings.selectedNode, + fiat: settings.fiat, + passphrase: settings.passphrase, + duressPassphrase: settings.duressPassphrase, + pin: settings.pin, + duressPin: settings.duressPin, + scramblePin: !scramblePin, + authenticationAttempts: + settings.authenticationAttempts, + locale: settings.locale, + privacy: settings.privacy + }) + ); + }} + trackColor={{ + false: '#767577', + true: themeColor('highlight') + }} + style={{ + alignSelf: 'flex-end' + }} + /> + + + ); + } +} diff --git a/views/Settings/SetDuressPin.tsx b/views/Settings/SetDuressPin.tsx index f6764843b..e333e586a 100644 --- a/views/Settings/SetDuressPin.tsx +++ b/views/Settings/SetDuressPin.tsx @@ -105,7 +105,8 @@ export default class SetDuressPin extends React.Component< passphrase: settings.passphrase, duressPassphrase: settings.duressPassphrase, pin: settings.pin, - duressPin + duressPin, + scramblePin: settings.scramblePin } : { duressPin } ) @@ -118,7 +119,8 @@ export default class SetDuressPin extends React.Component< }; render() { - const { navigation } = this.props; + const { navigation, SettingsStore } = this.props; + const { settings } = SettingsStore; const { duressPin, duressPinMismatchError, duressPinInvalidError } = this.state; const BackButton = () => ( @@ -203,6 +205,7 @@ export default class SetDuressPin extends React.Component< onPinChange={this.onPinChange} hidePinLength={true} pinConfirm={false} + shuffle={settings.scramblePin} /> @@ -245,6 +248,7 @@ export default class SetDuressPin extends React.Component< hidePinLength={false} pinConfirm={true} pinLength={duressPin.length} + shuffle={settings.scramblePin} /> diff --git a/views/Settings/SetPin.tsx b/views/Settings/SetPin.tsx index 7d8a5db00..885cc6617 100644 --- a/views/Settings/SetPin.tsx +++ b/views/Settings/SetPin.tsx @@ -102,6 +102,7 @@ export default class SetPin extends React.Component { passphrase: settings.passphrase, duressPassphrase: settings.duressPassphrase, duressPin: settings.duressPin, + scramblePin: settings.scramblePin, pin } : { pin } @@ -116,7 +117,8 @@ export default class SetPin extends React.Component { }; render() { - const { navigation } = this.props; + const { navigation, SettingsStore } = this.props; + const { settings } = SettingsStore; const { pin, pinMismatchError, pinInvalidError } = this.state; const BackButton = () => ( { 'views.Settings.SetPin.createPin' )} - - {localeString( - 'views.Settings.SetPin.scramblePin' - )} - + {(settings.scramblePin || + typeof typeof settings.scramblePin === + 'undefined') && ( + + {localeString( + 'views.Settings.SetPin.scramblePin' + )} + + )} { onPinChange={this.onPinChange} hidePinLength={true} pinConfirm={false} + shuffle={settings.scramblePin} /> @@ -244,6 +251,7 @@ export default class SetPin extends React.Component { hidePinLength={false} pinConfirm={true} pinLength={pin.length} + shuffle={settings.scramblePin} /> diff --git a/views/Settings/Theme.tsx b/views/Settings/Theme.tsx index 25d1496c9..32a23d0f4 100644 --- a/views/Settings/Theme.tsx +++ b/views/Settings/Theme.tsx @@ -102,6 +102,8 @@ export default class Theme extends React.Component { pin: settings.pin, duressPin: settings.duressPin, + scramblePin: + settings.scramblePin, authenticationAttempts: settings.authenticationAttempts, locale: settings.locale, From 0b1b0c789a625568cbc95a7db8231fcc1ca8d1e7 Mon Sep 17 00:00:00 2001 From: deregs Date: Mon, 25 Apr 2022 17:10:10 -0400 Subject: [PATCH 2/2] create scramble pin locale string --- locales/en.json | 1 + views/Settings/Security.tsx | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 37bdf9f40..d5bfebc4f 100644 --- a/locales/en.json +++ b/locales/en.json @@ -131,6 +131,7 @@ "views.Settings.Security.title": "Security settings", "views.Settings.Security.deletePIN": "Delete PIN", "views.Settings.Security.deleteDuressPIN": "Delete Duress PIN", + "views.Settings.Security.scramblePIN": "Scramble PIN numbers", "views.ImportAccount.title": "Import account", "views.ImportAccount.name": "Account Name", "views.ImportAccount.extendedPubKey": "Extended Public Key (Xpub)", diff --git a/views/Settings/Security.tsx b/views/Settings/Security.tsx index 8712f87ac..651b4455f 100644 --- a/views/Settings/Security.tsx +++ b/views/Settings/Security.tsx @@ -207,7 +207,9 @@ export default class Security extends React.Component< fontFamily: 'Lato-Regular' }} > - {'Scramble PIN numbers'} + {localeString( + 'views.Settings.Security.scramblePIN' + )}