General-purpose "Confirm" modal
Added a re-usable "yes/no" confirmation modal where the client just sets the question string and gets a callback "OK" or "Cancel" is clicked. It doesn't make sense to create one modal for each confirmation, especially when the modal is only used in one place. Replaced one of the existing modal as an example.
This commit is contained in:
parent
4bfb4fb55d
commit
11bbd58e33
8 changed files with 84 additions and 67 deletions
|
@ -2,7 +2,7 @@ import { connect } from 'react-redux';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import WalletSwap from './view';
|
import WalletSwap from './view';
|
||||||
import { doOpenModal } from 'redux/actions/app';
|
import { doOpenModal } from 'redux/actions/app';
|
||||||
import { doAddCoinSwap, doQueryCoinSwapStatus } from 'redux/actions/coinSwap';
|
import { doAddCoinSwap, doRemoveCoinSwap, doQueryCoinSwapStatus } from 'redux/actions/coinSwap';
|
||||||
import { doToast } from 'redux/actions/notifications';
|
import { doToast } from 'redux/actions/notifications';
|
||||||
import { selectCoinSwaps } from 'redux/selectors/coinSwap';
|
import { selectCoinSwaps } from 'redux/selectors/coinSwap';
|
||||||
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
||||||
|
@ -19,6 +19,7 @@ const perform = (dispatch) => ({
|
||||||
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
|
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
|
||||||
doToast: (options) => dispatch(doToast(options)),
|
doToast: (options) => dispatch(doToast(options)),
|
||||||
addCoinSwap: (coinSwap) => dispatch(doAddCoinSwap(coinSwap)),
|
addCoinSwap: (coinSwap) => dispatch(doAddCoinSwap(coinSwap)),
|
||||||
|
removeCoinSwap: (chargeCode) => dispatch(doRemoveCoinSwap(chargeCode)),
|
||||||
getNewAddress: () => dispatch(doGetNewAddress()),
|
getNewAddress: () => dispatch(doGetNewAddress()),
|
||||||
checkAddressIsMine: (address) => dispatch(doCheckAddressIsMine(address)),
|
checkAddressIsMine: (address) => dispatch(doCheckAddressIsMine(address)),
|
||||||
queryCoinSwapStatus: (sendAddress) => dispatch(doQueryCoinSwapStatus(sendAddress)),
|
queryCoinSwapStatus: (sendAddress) => dispatch(doQueryCoinSwapStatus(sendAddress)),
|
||||||
|
|
|
@ -59,6 +59,7 @@ type Props = {
|
||||||
isAuthenticated: boolean,
|
isAuthenticated: boolean,
|
||||||
doToast: ({ message: string }) => void,
|
doToast: ({ message: string }) => void,
|
||||||
addCoinSwap: (CoinSwapInfo) => void,
|
addCoinSwap: (CoinSwapInfo) => void,
|
||||||
|
removeCoinSwap: (string) => void,
|
||||||
getNewAddress: () => void,
|
getNewAddress: () => void,
|
||||||
checkAddressIsMine: (string) => void,
|
checkAddressIsMine: (string) => void,
|
||||||
openModal: (string, {}) => void,
|
openModal: (string, {}) => void,
|
||||||
|
@ -72,6 +73,7 @@ function WalletSwap(props: Props) {
|
||||||
coinSwaps,
|
coinSwaps,
|
||||||
isAuthenticated,
|
isAuthenticated,
|
||||||
addCoinSwap,
|
addCoinSwap,
|
||||||
|
removeCoinSwap,
|
||||||
getNewAddress,
|
getNewAddress,
|
||||||
checkAddressIsMine,
|
checkAddressIsMine,
|
||||||
openModal,
|
openModal,
|
||||||
|
@ -103,9 +105,15 @@ function WalletSwap(props: Props) {
|
||||||
setSwap(null);
|
setSwap(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeCoinSwap(chargeCode) {
|
function handleRemoveSwap(chargeCode) {
|
||||||
openModal(MODALS.CONFIRM_REMOVE_BTC_SWAP_ADDRESS, {
|
openModal(MODALS.CONFIRM, {
|
||||||
chargeCode: chargeCode,
|
title: __('Remove Swap'),
|
||||||
|
subtitle: <I18nMessage tokens={{ address: <em>{`${chargeCode}`}</em> }}>Remove %address%?</I18nMessage>,
|
||||||
|
body: <p className="help--warning">{__('This process cannot be reversed.')}</p>,
|
||||||
|
onConfirm: (closeModal) => {
|
||||||
|
removeCoinSwap(chargeCode);
|
||||||
|
closeModal();
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -657,7 +665,7 @@ function WalletSwap(props: Props) {
|
||||||
button="link"
|
button="link"
|
||||||
icon={ICONS.REMOVE}
|
icon={ICONS.REMOVE}
|
||||||
title={__('Remove swap')}
|
title={__('Remove swap')}
|
||||||
onClick={() => removeCoinSwap(x.chargeCode)}
|
onClick={() => handleRemoveSwap(x.chargeCode)}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
export const CONFIRM = 'confirm';
|
||||||
export const CONFIRM_FILE_REMOVE = 'confirm_file_remove';
|
export const CONFIRM_FILE_REMOVE = 'confirm_file_remove';
|
||||||
export const CONFIRM_EXTERNAL_RESOURCE = 'confirm_external_resource';
|
export const CONFIRM_EXTERNAL_RESOURCE = 'confirm_external_resource';
|
||||||
export const COMMENT_ACKNOWEDGEMENT = 'comment_acknowlegement';
|
export const COMMENT_ACKNOWEDGEMENT = 'comment_acknowlegement';
|
||||||
|
@ -42,7 +43,6 @@ export const SYNC_ENABLE = 'SYNC_ENABLE';
|
||||||
export const IMAGE_UPLOAD = 'image_upload';
|
export const IMAGE_UPLOAD = 'image_upload';
|
||||||
export const MOBILE_SEARCH = 'mobile_search';
|
export const MOBILE_SEARCH = 'mobile_search';
|
||||||
export const VIEW_IMAGE = 'view_image';
|
export const VIEW_IMAGE = 'view_image';
|
||||||
export const CONFIRM_REMOVE_BTC_SWAP_ADDRESS = 'confirm_remove_btc_swap_address';
|
|
||||||
export const BLOCK_CHANNEL = 'block_channel';
|
export const BLOCK_CHANNEL = 'block_channel';
|
||||||
export const COLLECTION_ADD = 'collection_add';
|
export const COLLECTION_ADD = 'collection_add';
|
||||||
export const COLLECTION_DELETE = 'collection_delete';
|
export const COLLECTION_DELETE = 'collection_delete';
|
||||||
|
|
10
ui/modal/modalConfirm/index.js
Normal file
10
ui/modal/modalConfirm/index.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { doHideModal } from 'redux/actions/app';
|
||||||
|
|
||||||
|
import ModalConfirm from './view';
|
||||||
|
|
||||||
|
const perform = (dispatch) => ({
|
||||||
|
doHideModal: () => dispatch(doHideModal()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(null, perform)(ModalConfirm);
|
56
ui/modal/modalConfirm/view.jsx
Normal file
56
ui/modal/modalConfirm/view.jsx
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import type { Node } from 'react';
|
||||||
|
import Button from 'component/button';
|
||||||
|
import Card from 'component/common/card';
|
||||||
|
import Spinner from 'component/spinner';
|
||||||
|
import { Modal } from 'modal/modal';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
title: string,
|
||||||
|
subtitle?: string | Node,
|
||||||
|
body?: string | Node,
|
||||||
|
labelOk?: string,
|
||||||
|
labelCancel?: string,
|
||||||
|
onConfirm: (closeModal: () => void, setIsBusy: (boolean) => void) => void,
|
||||||
|
hideCancel?: boolean,
|
||||||
|
// --- perform ---
|
||||||
|
doHideModal: () => void,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ModalConfirm(props: Props) {
|
||||||
|
const { title, subtitle, body, labelOk, labelCancel, onConfirm, hideCancel, doHideModal } = props;
|
||||||
|
const [isBusy, setIsBusy] = React.useState(false);
|
||||||
|
|
||||||
|
function handleOnClick() {
|
||||||
|
if (onConfirm) {
|
||||||
|
onConfirm(doHideModal, setIsBusy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOkLabel() {
|
||||||
|
return isBusy ? <Spinner type="small" /> : labelOk || __('OK');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCancelLabel() {
|
||||||
|
return labelCancel || __('Cancel');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen type="card" onAborted={doHideModal}>
|
||||||
|
<Card
|
||||||
|
title={title}
|
||||||
|
subtitle={subtitle}
|
||||||
|
body={body}
|
||||||
|
actions={
|
||||||
|
<>
|
||||||
|
<div className="section__actions">
|
||||||
|
<Button button="primary" label={getOkLabel()} disabled={isBusy} onClick={handleOnClick} />
|
||||||
|
{!hideCancel && <Button button="link" label={getCancelLabel()} disabled={isBusy} onClick={doHideModal} />}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,13 +0,0 @@
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { doHideModal } from 'redux/actions/app';
|
|
||||||
import ModalRemoveBtcSwapAddress from './view';
|
|
||||||
import { doRemoveCoinSwap } from 'redux/actions/coinSwap';
|
|
||||||
|
|
||||||
const select = (state, props) => ({});
|
|
||||||
|
|
||||||
const perform = (dispatch) => ({
|
|
||||||
removeCoinSwap: (chargeCode) => dispatch(doRemoveCoinSwap(chargeCode)),
|
|
||||||
closeModal: () => dispatch(doHideModal()),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(select, perform)(ModalRemoveBtcSwapAddress);
|
|
|
@ -1,43 +0,0 @@
|
||||||
// @flow
|
|
||||||
import React from 'react';
|
|
||||||
import { Modal } from 'modal/modal';
|
|
||||||
import Button from 'component/button';
|
|
||||||
import Card from 'component/common/card';
|
|
||||||
import I18nMessage from 'component/i18nMessage';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
chargeCode: string,
|
|
||||||
removeCoinSwap: (string) => void,
|
|
||||||
closeModal: () => void,
|
|
||||||
};
|
|
||||||
|
|
||||||
function ModalRemoveBtcSwapAddress(props: Props) {
|
|
||||||
const { chargeCode, removeCoinSwap, closeModal } = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal isOpen contentLabel={__('Confirm Swap Removal')} type="card" onAborted={closeModal}>
|
|
||||||
<Card
|
|
||||||
title={__('Remove Swap')}
|
|
||||||
subtitle={<I18nMessage tokens={{ address: <em>{`${chargeCode}`}</em> }}>Remove %address%?</I18nMessage>}
|
|
||||||
body={<p className="help--warning">{__('This process cannot be reversed.')}</p>}
|
|
||||||
actions={
|
|
||||||
<>
|
|
||||||
<div className="section__actions">
|
|
||||||
<Button
|
|
||||||
button="primary"
|
|
||||||
label={__('OK')}
|
|
||||||
onClick={() => {
|
|
||||||
removeCoinSwap(chargeCode);
|
|
||||||
closeModal();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Button button="link" label={__('Cancel')} onClick={closeModal} />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ModalRemoveBtcSwapAddress;
|
|
|
@ -23,6 +23,7 @@ const ModalClaimCollectionAdd = lazyImport(() =>
|
||||||
const ModalCommentAcknowledgement = lazyImport(() =>
|
const ModalCommentAcknowledgement = lazyImport(() =>
|
||||||
import('modal/modalCommentAcknowledgement' /* webpackChunkName: "modalCommentAcknowledgement" */)
|
import('modal/modalCommentAcknowledgement' /* webpackChunkName: "modalCommentAcknowledgement" */)
|
||||||
);
|
);
|
||||||
|
const ModalConfirm = lazyImport(() => import('modal/modalConfirm' /* webpackChunkName: "modalConfirm" */));
|
||||||
const ModalConfirmAge = lazyImport(() => import('modal/modalConfirmAge' /* webpackChunkName: "modalConfirmAge" */));
|
const ModalConfirmAge = lazyImport(() => import('modal/modalConfirmAge' /* webpackChunkName: "modalConfirmAge" */));
|
||||||
const ModalConfirmThumbnailUpload = lazyImport(() =>
|
const ModalConfirmThumbnailUpload = lazyImport(() =>
|
||||||
import('modal/modalConfirmThumbnailUpload' /* webpackChunkName: "modalConfirmThumbnailUpload" */)
|
import('modal/modalConfirmThumbnailUpload' /* webpackChunkName: "modalConfirmThumbnailUpload" */)
|
||||||
|
@ -63,9 +64,6 @@ const ModalPublish = lazyImport(() => import('modal/modalPublish' /* webpackChun
|
||||||
const ModalPublishPreview = lazyImport(() =>
|
const ModalPublishPreview = lazyImport(() =>
|
||||||
import('modal/modalPublishPreview' /* webpackChunkName: "modalPublishPreview" */)
|
import('modal/modalPublishPreview' /* webpackChunkName: "modalPublishPreview" */)
|
||||||
);
|
);
|
||||||
const ModalRemoveBtcSwapAddress = lazyImport(() =>
|
|
||||||
import('modal/modalRemoveBtcSwapAddress' /* webpackChunkName: "modalRemoveBtcSwapAddress" */)
|
|
||||||
);
|
|
||||||
const ModalRemoveCard = lazyImport(() => import('modal/modalRemoveCard' /* webpackChunkName: "modalRemoveCard" */));
|
const ModalRemoveCard = lazyImport(() => import('modal/modalRemoveCard' /* webpackChunkName: "modalRemoveCard" */));
|
||||||
const ModalRemoveComment = lazyImport(() =>
|
const ModalRemoveComment = lazyImport(() =>
|
||||||
import('modal/modalRemoveComment' /* webpackChunkName: "modalRemoveComment" */)
|
import('modal/modalRemoveComment' /* webpackChunkName: "modalRemoveComment" */)
|
||||||
|
@ -124,6 +122,8 @@ function ModalRouter(props: Props) {
|
||||||
|
|
||||||
function getModal(id) {
|
function getModal(id) {
|
||||||
switch (id) {
|
switch (id) {
|
||||||
|
case MODALS.CONFIRM:
|
||||||
|
return ModalConfirm;
|
||||||
case MODALS.UPGRADE:
|
case MODALS.UPGRADE:
|
||||||
return ModalUpgrade;
|
return ModalUpgrade;
|
||||||
case MODALS.DOWNLOADING:
|
case MODALS.DOWNLOADING:
|
||||||
|
@ -198,8 +198,6 @@ function ModalRouter(props: Props) {
|
||||||
return ModalViewImage;
|
return ModalViewImage;
|
||||||
case MODALS.MASS_TIP_UNLOCK:
|
case MODALS.MASS_TIP_UNLOCK:
|
||||||
return ModalMassTipsUnlock;
|
return ModalMassTipsUnlock;
|
||||||
case MODALS.CONFIRM_REMOVE_BTC_SWAP_ADDRESS:
|
|
||||||
return ModalRemoveBtcSwapAddress;
|
|
||||||
case MODALS.BLOCK_CHANNEL:
|
case MODALS.BLOCK_CHANNEL:
|
||||||
return ModalBlockChannel;
|
return ModalBlockChannel;
|
||||||
case MODALS.COLLECTION_ADD:
|
case MODALS.COLLECTION_ADD:
|
||||||
|
|
Loading…
Reference in a new issue