Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lock without reconnect #1470

Merged
merged 4 commits into from
May 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 13 additions & 0 deletions stores/ModalStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,17 @@ export default class ModalStore {
public toggleAndroidNfcModal = (status: boolean) => {
this.showAndroidNfcModal = status;
};

@action
public closeVisibleModalDialog = () => {
if (this.showExternalLinkModal) {
this.showExternalLinkModal = false;
return true;
}
if (this.showAndroidNfcModal) {
this.showAndroidNfcModal = false;
return true;
}
return false;
};
}
12 changes: 12 additions & 0 deletions stores/SettingsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,18 @@ export default class SettingsStore {
});
};

public loginRequired = () =>
this.settings &&
(this.settings.passphrase ||
this.settings.pin ||
this.isBiometryRequired()) &&
!this.loggedIn;

public isBiometryRequired = () =>
this.settings != null &&
this.settings.isBiometryEnabled &&
this.settings.supportedBiometryType !== undefined;

@action
public setLoginStatus = (status = false) => {
this.loggedIn = status;
Expand Down
6 changes: 0 additions & 6 deletions utils/BiometricUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import ReactNativeBiometrics, { BiometryType } from 'react-native-biometrics';
import { Settings } from '../stores/SettingsStore';

const rnBiometrics = new ReactNativeBiometrics({
allowDeviceCredentials: false
Expand Down Expand Up @@ -42,9 +41,4 @@ export const verifyBiometry = async (
return false;
};

export const getIsBiometryRequired = (settings?: Settings): boolean =>
settings != null &&
settings.isBiometryEnabled &&
settings.supportedBiometryType !== undefined;

export default rnBiometrics;
4 changes: 2 additions & 2 deletions views/Lockscreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import TextInput from '../components/TextInput';

import SettingsStore from '../stores/SettingsStore';

import { getIsBiometryRequired, verifyBiometry } from '../utils/BiometricUtils';
import { verifyBiometry } from '../utils/BiometricUtils';
import LinkingUtils from '../utils/LinkingUtils';
import { localeString } from '../utils/LocaleUtils';
import { themeColor } from '../utils/ThemeUtils';
Expand Down Expand Up @@ -76,7 +76,7 @@ export default class Lockscreen extends React.Component<
const posEnabled: boolean =
(settings && settings.pos && settings.pos.squareEnabled) || false;

const isBiometryRequired = getIsBiometryRequired(settings);
const isBiometryRequired = SettingsStore.isBiometryRequired();

if (
isBiometryRequired &&
Expand Down
94 changes: 74 additions & 20 deletions views/Wallet/Wallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react';
import {
Animated,
AppState,
BackHandler,
Linking,
PanResponder,
Text,
Expand All @@ -10,7 +11,11 @@ import {
} from 'react-native';

import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { DefaultTheme, NavigationContainer } from '@react-navigation/native';
import {
DefaultTheme,
NavigationContainer,
NavigationContainerRef
} from '@react-navigation/native';
import { inject, observer } from 'mobx-react';
import RNRestart from 'react-native-restart';

Expand All @@ -31,18 +36,15 @@ import { themeColor } from './../../utils/ThemeUtils';

import BalanceStore from './../../stores/BalanceStore';
import ChannelsStore from './../../stores/ChannelsStore';

import FiatStore from './../../stores/FiatStore';
import NodeInfoStore from './../../stores/NodeInfoStore';
import PosStore from './../../stores/PosStore';
import SettingsStore, { Settings } from './../../stores/SettingsStore';
import UnitsStore from './../../stores/UnitsStore';
import UTXOsStore from './../../stores/UTXOsStore';
import ModalStore from './../../stores/ModalStore';

import {
getIsBiometryRequired,
getSupportedBiometryType
} from '../../utils/BiometricUtils';
import { getSupportedBiometryType } from '../../utils/BiometricUtils';
import Bitcoin from './../../assets/images/SVG/Bitcoin.svg';
import CaretUp from './../../assets/images/SVG/Caret Up.svg';
import ChannelsIcon from './../../assets/images/SVG/Channels.svg';
Expand All @@ -62,6 +64,7 @@ interface WalletProps {
FiatStore: FiatStore;
PosStore: PosStore;
UTXOsStore: UTXOsStore;
ModalStore: ModalStore;
}

interface WalletState {
Expand All @@ -77,10 +80,13 @@ interface WalletState {
'UnitsStore',
'FiatStore',
'PosStore',
'UTXOsStore'
'UTXOsStore',
'ModalStore'
)
@observer
export default class Wallet extends React.Component<WalletProps, WalletState> {
private tabNavigationRef = React.createRef<NavigationContainerRef<any>>();

constructor(props) {
super(props);
this.state = {
Expand All @@ -103,6 +109,7 @@ export default class Wallet extends React.Component<WalletProps, WalletState> {
}
});
}

async UNSAFE_componentWillMount(): Promise<void> {
const {
SettingsStore: { updateSettings }
Expand All @@ -113,25 +120,71 @@ export default class Wallet extends React.Component<WalletProps, WalletState> {
await updateSettings({ supportedBiometryType });
}

private handleBackButton() {
const dialogHasBeenClosed =
this.props.ModalStore.closeVisibleModalDialog();
if (dialogHasBeenClosed) {
return true;
}

if (this.props.SettingsStore.loginRequired()) {
// pop to close lock screen and return false to close the app
this.props.navigation.pop();
return false;
}

if (this.props.navigation.pop()) {
return true;
}

const tabNavigator = this.tabNavigationRef.current;
if (!tabNavigator) {
return false;
}
const tabNavigatorState = tabNavigator.getState();
if (!tabNavigatorState) {
return false;
}
const currentTabName =
tabNavigatorState.routeNames[tabNavigatorState.index];
const defaultView =
this.props.SettingsStore.settings.display.defaultView;
if (defaultView === currentTabName) {
return false;
} else if (defaultView) {
tabNavigator.navigate(defaultView);
return true;
}
return false;
}

async componentDidMount() {
// triggers when loaded from navigation or back action
this.props.navigation.addListener('didFocus', () => {
this.getSettingsAndNavigate();
});

AppState.addEventListener('change', this.handleAppStateChange);
BackHandler.addEventListener(
'hardwareBackPress',
this.handleBackButton.bind(this)
);
}

componentWillUnmount() {
this.props.navigation.removeListener &&
this.props.navigation.removeListener('didFocus');
AppState.removeEventListener &&
AppState.removeEventListener('change', this.handleAppStateChange);
BackHandler.removeEventListener(
'hardwareBackPress',
this.handleBackButton
);
}

handleAppStateChange = (nextAppState: any) => {
const { SettingsStore } = this.props;
const { settings, implementation } = SettingsStore;
const { settings } = SettingsStore;
const { loginBackground } = settings;
const loginRequired =
settings &&
Expand All @@ -140,11 +193,13 @@ export default class Wallet extends React.Component<WalletProps, WalletState> {
settings.isBiometryEnabled);

if (nextAppState === 'background' && loginRequired && loginBackground) {
if (implementation === 'lightning-node-connect') {
BackendUtils.disconnect();
}

RNRestart.Restart();
// In case the lock screen is visible and a valid PIN is entered and home button is pressed,
// unauthorized access would be possible because the PIN is not cleared on next launch.
// By calling pop, the lock screen is closed to clear the PIN.
this.props.navigation.pop();
SettingsStore.setLoginStatus(false);
} else if (nextAppState === 'active' && loginRequired) {
this.props.navigation.navigate('Lockscreen');
}
};

Expand All @@ -158,12 +213,7 @@ export default class Wallet extends React.Component<WalletProps, WalletState> {

// This awaits on settings, so should await on Tor being bootstrapped before making requests
await SettingsStore.getSettings().then(async (settings: Settings) => {
const isBiometryRequired = getIsBiometryRequired(settings);

const loginRequired =
settings &&
(settings.passphrase || settings.pin || isBiometryRequired) &&
!SettingsStore.loggedIn;
const loginRequired = SettingsStore.loginRequired();
const posEnabled =
settings && settings.pos && settings.pos.squareEnabled;

Expand Down Expand Up @@ -418,7 +468,10 @@ export default class Wallet extends React.Component<WalletProps, WalletState> {
return (
<View style={{ flex: 1 }}>
{!connecting && (!loginRequired || squareEnabled) && (
<NavigationContainer theme={Theme}>
<NavigationContainer
theme={Theme}
ref={this.tabNavigationRef}
>
<Tab.Navigator
initialRouteName={
squareEnabled && posStatus === 'active'
Expand All @@ -427,6 +480,7 @@ export default class Wallet extends React.Component<WalletProps, WalletState> {
settings.display.defaultView) ||
'Keypad'
}
backBehavior="none"
screenOptions={({ route }) => ({
tabBarIcon: ({ color }) => {
if (route.name === 'Keypad') {
Expand Down