From 4858f96520b711bea66c2ea379de10c8c4ced682 Mon Sep 17 00:00:00 2001 From: James Prado Date: Tue, 10 Oct 2017 19:08:55 -0400 Subject: [PATCH] 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 --- common/actions/wallet/actionCreators.ts | 7 ++ common/actions/wallet/actionTypes.ts | 6 + common/actions/wallet/constants.ts | 1 + .../components/NavigationPrompt.tsx | 107 ++++++++++++++++++ .../containers/Tabs/SendTransaction/index.tsx | 24 ++-- common/reducers/wallet.ts | 2 + 6 files changed, 140 insertions(+), 7 deletions(-) create mode 100644 common/containers/Tabs/SendTransaction/components/NavigationPrompt.tsx diff --git a/common/actions/wallet/actionCreators.ts b/common/actions/wallet/actionCreators.ts index ed079345..21a423e1 100644 --- a/common/actions/wallet/actionCreators.ts +++ b/common/actions/wallet/actionCreators.ts @@ -99,3 +99,10 @@ export function broadCastTxFailed( } }; } + +export type TResetWallet = typeof resetWallet; +export function resetWallet() { + return { + type: constants.WALLET_RESET + }; +} diff --git a/common/actions/wallet/actionTypes.ts b/common/actions/wallet/actionTypes.ts index 8ad65b5e..aec7819b 100644 --- a/common/actions/wallet/actionTypes.ts +++ b/common/actions/wallet/actionTypes.ts @@ -23,6 +23,11 @@ export interface SetWalletAction { payload: IWallet; } +/*** Reset Wallet ***/ +export interface ResetWalletAction { + type: 'WALLET_RESET'; +} + /*** Set Balance ***/ export interface SetBalanceAction { type: 'WALLET_SET_BALANCE'; @@ -84,6 +89,7 @@ export interface BroadcastTxFailedAction { export type WalletAction = | UnlockPrivateKeyAction | SetWalletAction + | ResetWalletAction | SetBalanceAction | SetTokenBalancesAction | BroadcastTxRequestedAction diff --git a/common/actions/wallet/constants.ts b/common/actions/wallet/constants.ts index 1569aeb9..2178f68a 100644 --- a/common/actions/wallet/constants.ts +++ b/common/actions/wallet/constants.ts @@ -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_FAILED = 'WALLET_BROADCAST_TX_FAILED'; export const WALLET_BROADCAST_TX_SUCCEEDED = 'WALLET_BROADCAST_TX_SUCCEEDED'; +export const WALLET_RESET = 'WALLET_RESET'; diff --git a/common/containers/Tabs/SendTransaction/components/NavigationPrompt.tsx b/common/containers/Tabs/SendTransaction/components/NavigationPrompt.tsx new file mode 100644 index 00000000..9a965ee0 --- /dev/null +++ b/common/containers/Tabs/SendTransaction/components/NavigationPrompt.tsx @@ -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 { + 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 ( + +

+ Leaving this page will log you out. Are you sure you want to continue? +

+
+ ); + } +} + +export default withRouter(NavigationPrompt); diff --git a/common/containers/Tabs/SendTransaction/index.tsx b/common/containers/Tabs/SendTransaction/index.tsx index 281e76e6..d66ed24b 100644 --- a/common/containers/Tabs/SendTransaction/index.tsx +++ b/common/containers/Tabs/SendTransaction/index.tsx @@ -1,11 +1,17 @@ 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 { BalanceSidebar } from 'components'; // COMPONENTS import { UnlockHeader } from 'components/ui'; import { donationAddressMap, NetworkConfig, NodeConfig } from 'config/data'; import TabSection from 'containers/TabSection'; +import NavigationPrompt from './components/NavigationPrompt'; // CONFIG import { TransactionWithoutGas } from 'libs/messages'; import { RPCNode } from 'libs/nodes'; @@ -92,6 +98,7 @@ interface Props { transactions: BroadcastTransactionStatus[]; showNotification: TShowNotification; broadcastTx: TBroadcastTx; + resetWallet: TResetWallet; location: { search: string }; } @@ -172,12 +179,14 @@ export class SendTransaction extends React.Component { transaction } = this.state; const customMessage = customMessages.find(m => m.to === to); - return (
- +
{/* Send Form */} {unlocked && ( @@ -272,7 +281,6 @@ export class SendTransaction extends React.Component {
)} - {transaction && showTxConfirm && (