Save entire swap info instead of just the address.

- Users should be able to see the entered and promised amount, otherwise they might forget how much to send over.
- This change also prepares for the future upgrade to support multiple coins.
This commit is contained in:
infinite-persistence 2021-04-04 15:10:55 +08:00 committed by Sean Yesmunt
parent 9c808e2b5e
commit b2630f6f21
10 changed files with 97 additions and 77 deletions

View file

@ -1,10 +1,27 @@
declare type CoinSwapInfo = {
coin: string,
sendAddress: string,
sendAmount: number,
lbcAmount: number,
}
declare type CoinSwapState = {
btcAddresses: Array<string>
coinSwaps: Array<CoinSwapInfo>
};
declare type CoinSwapAction = {
type: string,
data: {
btcAddress: string,
coin: string,
sendAddress: string,
sendAmount: number,
lbcAmount: number,
},
};
declare type CoinSwapRemoveAction = {
type: string,
data: {
sendAddress: string,
},
};

View file

@ -2,20 +2,20 @@ import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import WalletSwap from './view';
import { doOpenModal } from 'redux/actions/app';
import { doAddBtcAddress } from 'redux/actions/coinSwap';
import { doAddCoinSwap } from 'redux/actions/coinSwap';
import { doToast } from 'redux/actions/notifications';
import { selectBtcAddresses } from 'redux/selectors/coinSwap';
import { selectCoinSwaps } from 'redux/selectors/coinSwap';
import { selectReceiveAddress, doGetNewAddress, doCheckAddressIsMine } from 'lbry-redux';
const select = (state, props) => ({
receiveAddress: selectReceiveAddress(state),
btcAddresses: selectBtcAddresses(state),
coinSwaps: selectCoinSwaps(state),
});
const perform = (dispatch) => ({
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
doToast: (options) => dispatch(doToast(options)),
doAddBtcAddress: (address) => dispatch(doAddBtcAddress(address)),
addCoinSwap: (coinSwap) => dispatch(doAddCoinSwap(coinSwap)),
getNewAddress: () => dispatch(doGetNewAddress()),
checkAddressIsMine: (address) => dispatch(doCheckAddressIsMine(address)),
});

View file

@ -47,24 +47,16 @@ const NAG_RATE_CALL_FAILED = 'Unable to obtain exchange rate. Try again later.';
type Props = {
receiveAddress: string,
btcAddresses: Array<string>,
coinSwaps: Array<CoinSwapInfo>,
doToast: ({ message: string }) => void,
doAddBtcAddress: (string) => void,
addCoinSwap: (CoinSwapInfo) => void,
getNewAddress: () => void,
checkAddressIsMine: (string) => void,
openModal: (string, {}) => void,
};
function WalletSwap(props: Props) {
const {
receiveAddress,
doToast,
btcAddresses,
doAddBtcAddress,
getNewAddress,
checkAddressIsMine,
openModal,
} = props;
const { receiveAddress, doToast, coinSwaps, addCoinSwap, getNewAddress, checkAddressIsMine, openModal } = props;
const [btc, setBtc] = usePersistedState('swap-btc-amount', 0.001);
const [btcError, setBtcError] = React.useState();
@ -92,9 +84,9 @@ function WalletSwap(props: Props) {
setBtcAddress(null);
}
function removeBtcAddress(btcAddress) {
function removeCoinSwap(sendAddress) {
openModal(MODALS.CONFIRM_REMOVE_BTC_SWAP_ADDRESS, {
btcAddress: btcAddress,
sendAddress: sendAddress,
});
}
@ -236,7 +228,12 @@ function WalletSwap(props: Props) {
})
.then((result) => {
setBtcAddress(result);
doAddBtcAddress(result);
addCoinSwap({
coin: 'btc',
sendAddress: result,
sendAmount: btc,
lbcAmount: lbc,
});
})
.catch((err) => {
setNag({ msg: err === INTERNAL_APIS_DOWN ? NAG_SWAP_CALL_FAILED : err.message, type: 'error' });
@ -259,8 +256,8 @@ function WalletSwap(props: Props) {
setNag(null);
setIsRefreshingStatus(true);
btcAddresses.forEach((x) => {
queryStatus(x, null, null);
coinSwaps.forEach((x) => {
queryStatus(x.sendAddress, null, null);
});
}
@ -357,9 +354,7 @@ function WalletSwap(props: Props) {
disabled={isSwapping || isNaN(btc) || btc === 0 || lbc === 0 || btcError}
label={isSwapping ? __('Processing...') : __('Start Swap')}
/>
{btcAddresses.length !== 0 && (
<Button button="link" label={__('View Past Swaps')} onClick={handleViewPastSwaps} />
)}
{coinSwaps.length !== 0 && <Button button="link" label={__('View Past Swaps')} onClick={handleViewPastSwaps} />}
</div>
</>
);
@ -424,37 +419,36 @@ function WalletSwap(props: Props) {
</tr>
</thead>
<tbody>
{btcAddresses.length === 0 && (
{coinSwaps.length === 0 && (
<tr>
<td>{'---'}</td>
</tr>
)}
{btcAddresses.length !== 0 &&
btcAddresses.map((x) => {
const shortBtcAddress = x.substring(0, 7);
{coinSwaps.length !== 0 &&
coinSwaps.map((x) => {
return (
<tr key={x}>
<tr key={x.sendAddress}>
<td>
<Button
button="link"
className="button--hash-id"
title={x}
label={shortBtcAddress}
title={x.sendAddress}
label={x.sendAddress.substring(0, 7)}
onClick={() => {
clipboard.writeText(x);
clipboard.writeText(x.sendAddress);
doToast({
message: __('Address copied.'),
});
}}
/>
</td>
<td>{isRefreshingStatus ? '...' : getStatusStr(x)}</td>
<td>{isRefreshingStatus ? '...' : getStatusStr(x.sendAddress)}</td>
<td>
<Button
button="link"
icon={ICONS.REMOVE}
title={__('Remove address')}
onClick={() => removeBtcAddress(x)}
onClick={() => removeCoinSwap(x.sendAddress)}
/>
</td>
</tr>
@ -467,7 +461,7 @@ function WalletSwap(props: Props) {
</div>
<div className="section__actions">
<Button autoFocus onClick={handleCancelPending} button="primary" label={__('Go Back')} />
{btcAddresses.length !== 0 && !isRefreshingStatus && (
{coinSwaps.length !== 0 && !isRefreshingStatus && (
<Button button="link" label={__('Refresh')} onClick={handleViewPastSwaps} />
)}
{isRefreshingStatus && <Spinner type="small" />}

View file

@ -283,8 +283,8 @@ export const COMMENT_RECEIVED = 'COMMENT_RECEIVED';
export const TOGGLE_BLOCK_CHANNEL = 'TOGGLE_BLOCK_CHANNEL';
// Coin swap
export const ADD_BTC_ADDRESS = 'ADD_BTC_ADDRESS';
export const REMOVE_BTC_ADDRESS = 'REMOVE_BTC_ADDRESS';
export const ADD_COIN_SWAP = 'ADD_COIN_SWAP';
export const REMOVE_COIN_SWAP = 'REMOVE_COIN_SWAP';
// Tags
export const TOGGLE_TAG_FOLLOW = 'TOGGLE_TAG_FOLLOW';

View file

@ -1,12 +1,12 @@
import { connect } from 'react-redux';
import { doHideModal } from 'redux/actions/app';
import ModalRemoveBtcSwapAddress from './view';
import { doRemoveBtcAddress } from 'redux/actions/coinSwap';
import { doRemoveCoinSwap } from 'redux/actions/coinSwap';
const select = (state, props) => ({});
const perform = (dispatch) => ({
doRemoveBtcAddress: (btcAddress) => dispatch(doRemoveBtcAddress(btcAddress)),
removeCoinSwap: (sendAddress) => dispatch(doRemoveCoinSwap(sendAddress)),
closeModal: () => dispatch(doHideModal()),
});

View file

@ -6,19 +6,19 @@ import Card from 'component/common/card';
import I18nMessage from 'component/i18nMessage';
type Props = {
btcAddress: string,
doRemoveBtcAddress: (string) => void,
sendAddress: string,
removeCoinSwap: (string) => void,
closeModal: () => void,
};
function ModalRemoveBtcSwapAddress(props: Props) {
const { btcAddress, doRemoveBtcAddress, closeModal } = props;
const { sendAddress, removeCoinSwap, closeModal } = props;
return (
<Modal isOpen contentLabel={__('Confirm Address Removal')} type="card" onAborted={closeModal}>
<Card
title={__('Remove BTC Swap Address')}
subtitle={<I18nMessage tokens={{ btc_address: <em>{`${btcAddress}`}</em> }}>Remove %btc_address%?</I18nMessage>}
subtitle={<I18nMessage tokens={{ address: <em>{`${sendAddress}`}</em> }}>Remove %address%?</I18nMessage>}
body={<p className="help--warning">{__('This process cannot be reversed.')}</p>}
actions={
<>
@ -27,7 +27,7 @@ function ModalRemoveBtcSwapAddress(props: Props) {
button="primary"
label={__('OK')}
onClick={() => {
doRemoveBtcAddress(btcAddress);
removeCoinSwap(sendAddress);
closeModal();
}}
/>

View file

@ -3,7 +3,7 @@ import * as ACTIONS from 'constants/action_types';
import { selectPrefsReady } from 'redux/selectors/sync';
import { doAlertWaitingForSync } from 'redux/actions/app';
export const doAddBtcAddress = (btcAddress: string) => (dispatch: Dispatch, getState: GetState) => {
export const doAddCoinSwap = (coinSwap: CoinSwapInfo) => (dispatch: Dispatch, getState: GetState) => {
const state = getState();
const ready = selectPrefsReady(state);
@ -12,14 +12,17 @@ export const doAddBtcAddress = (btcAddress: string) => (dispatch: Dispatch, getS
}
dispatch({
type: ACTIONS.ADD_BTC_ADDRESS,
type: ACTIONS.ADD_COIN_SWAP,
data: {
btcAddress,
coin: coinSwap.coin,
sendAddress: coinSwap.sendAddress,
sendAmount: coinSwap.sendAmount,
lbcAmount: coinSwap.lbcAmount,
},
});
};
export const doRemoveBtcAddress = (btcAddress: string) => (dispatch: Dispatch, getState: GetState) => {
export const doRemoveCoinSwap = (sendAddress: string) => (dispatch: Dispatch, getState: GetState) => {
const state = getState();
const ready = selectPrefsReady(state);
@ -28,9 +31,9 @@ export const doRemoveBtcAddress = (btcAddress: string) => (dispatch: Dispatch, g
}
dispatch({
type: ACTIONS.REMOVE_BTC_ADDRESS,
type: ACTIONS.REMOVE_COIN_SWAP,
data: {
btcAddress,
sendAddress,
},
});
};

View file

@ -4,41 +4,47 @@ import { ACTIONS as LBRY_REDUX_ACTIONS } from 'lbry-redux';
import { handleActions } from 'util/redux-utils';
const defaultState: CoinSwapState = {
btcAddresses: [],
coinSwaps: [],
};
export default handleActions(
{
[ACTIONS.ADD_BTC_ADDRESS]: (state: CoinSwapState, action: CoinSwapAction): CoinSwapState => {
const { btcAddresses } = state;
const { btcAddress } = action.data;
let newBtcAddresses = btcAddresses.slice();
if (!newBtcAddresses.includes(btcAddress)) {
newBtcAddresses.push(btcAddress);
[ACTIONS.ADD_COIN_SWAP]: (state: CoinSwapState, action: CoinSwapAction): CoinSwapState => {
const { coinSwaps } = state;
const { coin, sendAddress, sendAmount, lbcAmount } = action.data;
let newCoinSwaps = coinSwaps.slice();
if (!newCoinSwaps.find((x) => x.sendAddress === sendAddress)) {
newCoinSwaps.push({
coin: coin,
sendAddress: sendAddress,
sendAmount: sendAmount,
lbcAmount: lbcAmount,
});
}
return {
btcAddresses: newBtcAddresses,
coinSwaps: newCoinSwaps,
};
},
[ACTIONS.REMOVE_BTC_ADDRESS]: (state: CoinSwapState, action: CoinSwapAction): CoinSwapState => {
const { btcAddresses } = state;
const { btcAddress } = action.data;
let newBtcAddresses = btcAddresses.slice();
newBtcAddresses = newBtcAddresses.filter((x) => x !== btcAddress);
[ACTIONS.REMOVE_COIN_SWAP]: (state: CoinSwapState, action: CoinSwapRemoveAction): CoinSwapState => {
const { coinSwaps } = state;
const { sendAddress } = action.data;
let newCoinSwaps = coinSwaps.slice();
newCoinSwaps = newCoinSwaps.filter((x) => x.sendAddress !== sendAddress);
return {
btcAddresses: newBtcAddresses,
coinSwaps: newCoinSwaps,
};
},
[LBRY_REDUX_ACTIONS.USER_STATE_POPULATE]: (
state: CoinSwapState,
action: { data: { btcAddresses: ?Array<string> } }
action: { data: { coinSwaps: ?Array<CoinSwapInfo> } }
) => {
const { btcAddresses } = action.data;
const sanitizedBtcAddresses = btcAddresses && btcAddresses.filter((e) => typeof e === 'string');
const { coinSwaps } = action.data;
const sanitizedCoinSwaps = coinSwaps && coinSwaps.filter((x) => typeof x.sendAddress === 'string');
return {
...state,
btcAddresses:
sanitizedBtcAddresses && sanitizedBtcAddresses.length ? sanitizedBtcAddresses : state.btcAddresses,
coinSwaps: sanitizedCoinSwaps && sanitizedCoinSwaps.length ? sanitizedCoinSwaps : state.coinSwaps,
};
},
},

View file

@ -3,6 +3,6 @@ import { createSelector } from 'reselect';
const selectState = (state: { coinSwap: CoinSwapState }) => state.coinSwap || {};
export const selectBtcAddresses = createSelector(selectState, (state: CoinSwapState) => {
return state.btcAddresses.filter((x) => typeof x === 'string');
export const selectCoinSwaps = createSelector(selectState, (state: CoinSwapState) => {
return state.coinSwaps.filter((x) => typeof x.sendAddress === 'string');
});

View file

@ -66,7 +66,7 @@ const searchFilter = createFilter('search', ['options']);
const tagsFilter = createFilter('tags', ['followedTags']);
const subscriptionsFilter = createFilter('subscriptions', ['subscriptions']);
const blockedFilter = createFilter('blocked', ['blockedChannels']);
const btcAddressesFilter = createFilter('coinSwap', ['btcAddresses']);
const coinSwapsFilter = createFilter('coinSwap', ['coinSwaps']);
const settingsFilter = createBlacklistFilter('settings', ['loadedLanguages', 'language']);
const whiteListedReducers = [
'fileInfo',
@ -86,7 +86,7 @@ const transforms = [
fileInfoFilter,
walletFilter,
blockedFilter,
btcAddressesFilter,
coinSwapsFilter,
tagsFilter,
appFilter,
searchFilter,
@ -122,8 +122,8 @@ const triggerSharedStateActions = [
ACTIONS.CHANNEL_SUBSCRIBE,
ACTIONS.CHANNEL_UNSUBSCRIBE,
ACTIONS.TOGGLE_BLOCK_CHANNEL,
ACTIONS.ADD_BTC_ADDRESS,
ACTIONS.REMOVE_BTC_ADDRESS,
ACTIONS.ADD_COIN_SWAP,
ACTIONS.REMOVE_COIN_SWAP,
ACTIONS.TOGGLE_TAG_FOLLOW,
LBRY_REDUX_ACTIONS.CREATE_CHANNEL_COMPLETED,
ACTIONS.SYNC_CLIENT_SETTINGS,
@ -156,7 +156,7 @@ const sharedStateFilters = {
property: 'following',
},
blocked: { source: 'blocked', property: 'blockedChannels' },
btc_addresses: { source: 'coinSwap', property: 'btcAddresses' },
coin_swaps: { source: 'coinSwap', property: 'coinSwaps' },
settings: { source: 'settings', property: 'sharedPreferences' },
app_welcome_version: { source: 'app', property: 'welcomeVersion' },
sharing_3P: { source: 'app', property: 'allowAnalytics' },