Logout Prompt Modal on Navigation from Send (#275)

* Add disclaimer modal to footer

* Remove duplicate code & unnecessary styles

* Fix formatting noise

* remove un-used css style

* Fix tslint error & add media query for modals

* Nest Media Query

* Add NavigationPrompt component

* Fix types

* Fix types

* Extend History Types

* break out logic into setupUnblock method
This commit is contained in:
James Prado 2017-10-10 19:08:55 -04:00 committed by Daniel Ternyak
parent 2e472e5c5a
commit 4858f96520
6 changed files with 140 additions and 7 deletions

View File

@ -99,3 +99,10 @@ export function broadCastTxFailed(
} }
}; };
} }
export type TResetWallet = typeof resetWallet;
export function resetWallet() {
return {
type: constants.WALLET_RESET
};
}

View File

@ -23,6 +23,11 @@ export interface SetWalletAction {
payload: IWallet; payload: IWallet;
} }
/*** Reset Wallet ***/
export interface ResetWalletAction {
type: 'WALLET_RESET';
}
/*** Set Balance ***/ /*** Set Balance ***/
export interface SetBalanceAction { export interface SetBalanceAction {
type: 'WALLET_SET_BALANCE'; type: 'WALLET_SET_BALANCE';
@ -84,6 +89,7 @@ export interface BroadcastTxFailedAction {
export type WalletAction = export type WalletAction =
| UnlockPrivateKeyAction | UnlockPrivateKeyAction
| SetWalletAction | SetWalletAction
| ResetWalletAction
| SetBalanceAction | SetBalanceAction
| SetTokenBalancesAction | SetTokenBalancesAction
| BroadcastTxRequestedAction | BroadcastTxRequestedAction

View File

@ -7,3 +7,4 @@ export const WALLET_SET_TOKEN_BALANCES = 'WALLET_SET_TOKEN_BALANCES';
export const WALLET_BROADCAST_TX_REQUESTED = 'WALLET_BROADCAST_TX_REQUESTED'; export const WALLET_BROADCAST_TX_REQUESTED = 'WALLET_BROADCAST_TX_REQUESTED';
export const WALLET_BROADCAST_TX_FAILED = 'WALLET_BROADCAST_TX_FAILED'; export const WALLET_BROADCAST_TX_FAILED = 'WALLET_BROADCAST_TX_FAILED';
export const WALLET_BROADCAST_TX_SUCCEEDED = 'WALLET_BROADCAST_TX_SUCCEEDED'; export const WALLET_BROADCAST_TX_SUCCEEDED = 'WALLET_BROADCAST_TX_SUCCEEDED';
export const WALLET_RESET = 'WALLET_RESET';

View File

@ -0,0 +1,107 @@
import React from 'react';
import { withRouter } from 'react-router-dom';
import Modal, { IButton } from 'components/ui/Modal';
import { Location, History as H } from 'history';
type UnregisterCallback = () => void;
type BooleanCallback = (arg?: any) => boolean;
interface History extends H {
block(prompt?: boolean | BooleanCallback): UnregisterCallback;
}
interface Props {
when: boolean;
onConfirm?: any;
onCancel?: any;
}
interface InjectedProps extends Props {
location: Location;
history: History;
}
interface State {
nextLocation: Location | null;
openModal: boolean;
}
class NavigationPrompt extends React.Component<Props, State> {
public unblock;
get injected() {
return this.props as InjectedProps;
}
constructor(props) {
super(props);
this.state = {
nextLocation: null,
openModal: false
};
}
public setupUnblock() {
this.unblock = this.injected.history.block(nextLocation => {
if (
this.props.when &&
nextLocation.pathname !== this.injected.location.pathname
) {
this.setState({
openModal: true,
nextLocation
});
}
return !this.props.when;
});
}
public componentDidMount() {
this.setupUnblock();
}
public componentWillUnmount() {
this.unblock();
}
public onCancel = () => {
if (this.props.onCancel) {
this.props.onCancel();
}
this.setState({ nextLocation: null, openModal: false });
};
public onConfirm = () => {
if (this.props.onConfirm) {
this.props.onConfirm();
}
// Lock Wallet
this.navigateToNextLocation();
};
public navigateToNextLocation() {
this.unblock();
if (this.state.nextLocation) {
this.injected.history.push(this.state.nextLocation.pathname);
}
}
public render() {
const buttons: IButton[] = [
{ text: 'Log Out', type: 'primary', onClick: this.onConfirm },
{ text: 'Cancel', type: 'default', onClick: this.onCancel }
];
return (
<Modal
title="You are about to log out"
isOpen={this.state.openModal}
handleClose={this.onCancel}
buttons={buttons}
>
<p>
Leaving this page will log you out. Are you sure you want to continue?
</p>
</Modal>
);
}
}
export default withRouter(NavigationPrompt);

View File

@ -1,11 +1,17 @@
import { showNotification, TShowNotification } from 'actions/notifications'; import { showNotification, TShowNotification } from 'actions/notifications';
import { broadcastTx, TBroadcastTx } from 'actions/wallet'; import {
broadcastTx,
TBroadcastTx,
resetWallet,
TResetWallet
} from 'actions/wallet';
import Big from 'bignumber.js'; import Big from 'bignumber.js';
import { BalanceSidebar } from 'components'; import { BalanceSidebar } from 'components';
// COMPONENTS // COMPONENTS
import { UnlockHeader } from 'components/ui'; import { UnlockHeader } from 'components/ui';
import { donationAddressMap, NetworkConfig, NodeConfig } from 'config/data'; import { donationAddressMap, NetworkConfig, NodeConfig } from 'config/data';
import TabSection from 'containers/TabSection'; import TabSection from 'containers/TabSection';
import NavigationPrompt from './components/NavigationPrompt';
// CONFIG // CONFIG
import { TransactionWithoutGas } from 'libs/messages'; import { TransactionWithoutGas } from 'libs/messages';
import { RPCNode } from 'libs/nodes'; import { RPCNode } from 'libs/nodes';
@ -92,6 +98,7 @@ interface Props {
transactions: BroadcastTransactionStatus[]; transactions: BroadcastTransactionStatus[];
showNotification: TShowNotification; showNotification: TShowNotification;
broadcastTx: TBroadcastTx; broadcastTx: TBroadcastTx;
resetWallet: TResetWallet;
location: { search: string }; location: { search: string };
} }
@ -172,12 +179,14 @@ export class SendTransaction extends React.Component<Props, State> {
transaction transaction
} = this.state; } = this.state;
const customMessage = customMessages.find(m => m.to === to); const customMessage = customMessages.find(m => m.to === to);
return ( return (
<TabSection> <TabSection>
<section className="Tab-content"> <section className="Tab-content">
<UnlockHeader title={'NAV_SendEther'} /> <UnlockHeader title={'NAV_SendEther'} />
<NavigationPrompt
when={unlocked}
onConfirm={this.props.resetWallet}
/>
<div className="row"> <div className="row">
{/* Send Form */} {/* Send Form */}
{unlocked && ( {unlocked && (
@ -272,7 +281,6 @@ export class SendTransaction extends React.Component<Props, State> {
</section> </section>
)} )}
</div> </div>
{transaction && {transaction &&
showTxConfirm && ( showTxConfirm && (
<ConfirmationModal <ConfirmationModal
@ -489,6 +497,8 @@ function mapStateToProps(state: AppState) {
}; };
} }
export default connect(mapStateToProps, { showNotification, broadcastTx })( export default connect(mapStateToProps, {
SendTransaction showNotification,
); broadcastTx,
resetWallet
})(SendTransaction);

View File

@ -109,6 +109,8 @@ export function wallet(
switch (action.type) { switch (action.type) {
case 'WALLET_SET': case 'WALLET_SET':
return setWallet(state, action); return setWallet(state, action);
case 'WALLET_RESET':
return INITIAL_STATE;
case 'WALLET_SET_BALANCE': case 'WALLET_SET_BALANCE':
return setBalance(state, action); return setBalance(state, action);
case 'WALLET_SET_TOKEN_BALANCES': case 'WALLET_SET_TOKEN_BALANCES':