Skip to content

Commit

Permalink
fix(trade): persistent form state
Browse files Browse the repository at this point in the history
  • Loading branch information
adderpositive authored and tomasklim committed Jan 23, 2025
1 parent 7517a71 commit 4839c7b
Show file tree
Hide file tree
Showing 11 changed files with 81 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,23 @@ import {
interface CoinmarketUseAccountProps {
coinmarketAccount: Account | undefined;
selectedAccount: SelectedAccountLoaded;
isNotFormPage?: boolean;
shouldUseCoinmarketAccount?: boolean;
}

/**
* Hook used to get account for trade form (used in Sell/Swap)
* - coinmarketAccount is used whether user is in the trading flow (persistent)
* - selectedAccount is used as initial state if user entries from different page than trade
*/
export const useCoinmarketAccount = ({
coinmarketAccount,
selectedAccount,
isNotFormPage,
shouldUseCoinmarketAccount,
}: CoinmarketUseAccountProps): [Account, (state: Account) => void] => {
const accounts = useSelector(selectAccounts);
const device = useSelector(selectSelectedDevice);

// coinmarketAccount is used on offers page
// if is testnet, use
// selectedAccount is used as initial state if this is form page
const [account, setAccount] = useState<Account>(() => {
if (coinmarketAccount && isNotFormPage) {
if (coinmarketAccount && shouldUseCoinmarketAccount) {
return coinmarketAccount;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useSelector } from 'src/hooks/suite';
import { selectRouter } from 'src/reducers/suite/routerReducer';
import { CoinmarketTradeType } from 'src/types/coinmarket/coinmarket';
import { getTradeTypeByRoute } from 'src/utils/wallet/coinmarket/coinmarketUtils';

export const useCoinmarketPreviousRoute = (tradeType: CoinmarketTradeType) => {
const {
settingsBackRoute: { name: previousRouteName },
} = useSelector(selectRouter);
const tradeTypeFromRoute = getTradeTypeByRoute(previousRouteName);
const isPreviousRouteFromTradeSection = tradeTypeFromRoute === tradeType;

return isPreviousRouteFromTradeSection;
};
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import { useCoinmarketModalCrypto } from 'src/hooks/wallet/coinmarket/form/commo
import { useCoinmarketInfo } from 'src/hooks/wallet/coinmarket/useCoinmarketInfo';
import { useCoinmarketBuyFormDefaultValues } from 'src/hooks/wallet/coinmarket/form/useCoinmarketBuyFormDefaultValues';
import type { AmountLimitProps } from 'src/utils/suite/validation';
import { useCoinmarketPreviousRoute } from 'src/hooks/wallet/coinmarket/form/common/useCoinmarketPreviousRoute';

import { useCoinmarketInitializer } from './common/useCoinmarketInitializer';

Expand Down Expand Up @@ -78,6 +79,7 @@ export const useCoinmarketBuyForm = ({
const [isSubmittingHelper, setIsSubmittingHelper] = useState(false);
const abortControllerRef = useRef<AbortController | null>(null);
const { shouldSendInSats } = useBitcoinAmountUnit(account.symbol);
const isPreviousRouteFromTradeSection = useCoinmarketPreviousRoute(type);

const {
defaultValues,
Expand All @@ -98,11 +100,14 @@ export const useCoinmarketBuyForm = ({
? draft.fiatInput
: buyInfo?.buyInfo?.defaultAmountsOfFiatCurrencies.get(suggestedFiatCurrency),
// remember only for offers page
cryptoSelect: pageType === 'form' ? defaultValues.cryptoSelect : draft.cryptoSelect,
cryptoSelect: isPreviousRouteFromTradeSection
? draft.cryptoSelect
: defaultValues.cryptoSelect,
}
: null;

const isDraft = !!draftUpdated || !!isNotFormPage;

const methods = useForm<CoinmarketBuyFormProps>({
mode: 'onChange',
defaultValues: isDraft && draftUpdated ? draftUpdated : defaultValues,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import { useCoinmarketInfo } from 'src/hooks/wallet/coinmarket/useCoinmarketInfo
import { useCoinmarketFiatValues } from 'src/hooks/wallet/coinmarket/form/common/useCoinmarketFiatValues';
import type { CryptoAmountLimitProps } from 'src/utils/suite/validation';
import { useCoinmarketExchangeQuotesFilter } from 'src/hooks/wallet/coinmarket/form/common/useCoinmarketExchangeQuotesFilter';
import { useCoinmarketPreviousRoute } from 'src/hooks/wallet/coinmarket/form/common/useCoinmarketPreviousRoute';

import { useCoinmarketInitializer } from './common/useCoinmarketInitializer';

Expand All @@ -79,12 +80,11 @@ export const useCoinmarketExchangeForm = ({
addressVerified,
} = useSelector(state => state.wallet.coinmarket.exchange);
const { cryptoIdToCoinSymbol } = useCoinmarketInfo();
// selectedAccount is used as initial state if this is form page
// coinmarketAccount is used on offers page
const isPreviousRouteFromTradeSection = useCoinmarketPreviousRoute(type);
const [account, setAccount] = useCoinmarketAccount({
coinmarketAccount,
selectedAccount,
isNotFormPage,
shouldUseCoinmarketAccount: isPreviousRouteFromTradeSection,
});
const { callInProgress, timer, device, setCallInProgress, checkQuotesTimer } =
useCoinmarketInitializer({ selectedAccount, pageType });
Expand Down Expand Up @@ -133,7 +133,7 @@ export const useCoinmarketExchangeForm = ({
const isDraft = !!draft;
const getDraftUpdated = (): CoinmarketExchangeFormProps | null => {
if (!draft) return null;
if (isNotFormPage) return draft;
if (isPreviousRouteFromTradeSection) return draft;

const defaultReceiveCryptoSelect = coinmarketGetExchangeReceiveCryptoId(
defaultValues.sendCryptoSelect?.value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import { useCoinmarketCurrencySwitcher } from 'src/hooks/wallet/coinmarket/form/
import { useCoinmarketAccount } from 'src/hooks/wallet/coinmarket/form/common/useCoinmarketAccount';
import { useCoinmarketInfo } from 'src/hooks/wallet/coinmarket/useCoinmarketInfo';
import type { AmountLimitProps } from 'src/utils/suite/validation';
import { useCoinmarketPreviousRoute } from 'src/hooks/wallet/coinmarket/form/common/useCoinmarketPreviousRoute';

import { useCoinmarketInitializer } from './common/useCoinmarketInitializer';

Expand All @@ -75,11 +76,11 @@ export const useCoinmarketSellForm = ({
selectedQuote,
} = useSelector(state => state.wallet.coinmarket.sell);
const { cryptoIdToCoinSymbol } = useCoinmarketInfo();

const isPreviousRouteFromTradeSection = useCoinmarketPreviousRoute(type);
const [account, setAccount] = useCoinmarketAccount({
coinmarketAccount,
selectedAccount,
isNotFormPage,
shouldUseCoinmarketAccount: isPreviousRouteFromTradeSection,
});
const { callInProgress, timer, device, setCallInProgress, checkQuotesTimer } =
useCoinmarketInitializer({ selectedAccount, pageType });
Expand Down Expand Up @@ -119,7 +120,7 @@ export const useCoinmarketSellForm = ({
const draft = getDraft(sellDraftKey);
const getDraftUpdated = (): CoinmarketSellFormProps | null => {
if (!draft) return null;
if (isNotFormPage) {
if (isPreviousRouteFromTradeSection) {
const outputs = draft.outputs?.map(output => ({
...output,
fiat: output.fiat ?? '',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,33 +297,6 @@ describe('coinmarketMiddleware', () => {
expect(store.getState().wallet.coinmarket.modalAccount).toEqual(undefined);
});

it('Test of cleaning coinmarketAccount property', () => {
const store = initStore(
getInitialState({
coinmarket: {
...initialState,
exchange: {
...initialState.exchange,
coinmarketAccount: accounts[0],
},
sell: {
...initialState.sell,
coinmarketAccount: accounts[0],
},
},
}),
);

// go to coinmarket
store.dispatch({
type: ROUTER.LOCATION_CHANGE,
payload: COINMARKET_EXCHANGE_ROUTE,
});

expect(store.getState().wallet.coinmarket.sell.coinmarketAccount).toBe(accounts[0]);
expect(store.getState().wallet.coinmarket.exchange.coinmarketAccount).toEqual(undefined);
});

it('Test of setting activeSection after changing route', () => {
const store = initStore(
getInitialState({
Expand Down
33 changes: 3 additions & 30 deletions packages/suite/src/middlewares/wallet/coinmarketMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { MiddlewareAPI } from 'redux';

import { UI } from '@trezor/connect';
import { Route } from '@suite-common/suite-types';
import { accountsActions } from '@suite-common/wallet-core';

import { AppState, Action, Dispatch } from 'src/types/suite';
Expand All @@ -18,27 +17,13 @@ import * as coinmarketBuyActions from 'src/actions/wallet/coinmarketBuyActions';
import * as coinmarketExchangeActions from 'src/actions/wallet/coinmarketExchangeActions';
import * as coinmarketSellActions from 'src/actions/wallet/coinmarketSellActions';
import { ROUTER, MODAL } from 'src/actions/suite/constants';
import { CoinmarketTradeType } from 'src/types/coinmarket/coinmarket';

const getTradeTypeByRoute = (route: Route | undefined): CoinmarketTradeType | undefined => {
if (route?.name.startsWith('wallet-coinmarket-buy')) {
return 'buy';
}

if (route?.name.startsWith('wallet-coinmarket-sell')) {
return 'sell';
}

if (route?.name.startsWith('wallet-coinmarket-exchange')) {
return 'exchange';
}
};
import { getTradeTypeByRoute } from 'src/utils/wallet/coinmarket/coinmarketUtils';

/**
* In the Sell and Swap section an account can be changed by a user in the select
*/
export const getAccountAccordingToRoute = (state: AppState) => {
const tradeType = getTradeTypeByRoute(state.router.route);
const tradeType = getTradeTypeByRoute(state.router.route?.name);

const { account } = state.wallet.selectedAccount;
const sellSelectedAccount = state.wallet.coinmarket.sell.coinmarketAccount;
Expand Down Expand Up @@ -81,7 +66,7 @@ export const coinmarketMiddleware =
invityAPI.setInvityServersEnvironment(invityServerEnvironment);
}

const tradeType = getTradeTypeByRoute(state.router.route);
const tradeType = getTradeTypeByRoute(state.router.route?.name);
if (tradeType) {
api.dispatch(coinmarketCommonActions.setActiveSection(tradeType));
}
Expand Down Expand Up @@ -158,25 +143,13 @@ export const coinmarketMiddleware =

// get the new state after the action has been processed
const newState = api.getState();
const sellCoinmarketAccount = newState.wallet.coinmarket.sell.coinmarketAccount;
const exchangeCoinmarketAccount = newState.wallet.coinmarket.exchange.coinmarketAccount;

if (action.type === ROUTER.LOCATION_CHANGE) {
const routeName = newState.router.route?.name;
const isBuy = routeName === 'wallet-coinmarket-buy';
const isSell = routeName === 'wallet-coinmarket-sell';
const isExchange = routeName === 'wallet-coinmarket-exchange';

// clean coinmarketAccount in sell
if (isSell && sellCoinmarketAccount) {
api.dispatch(coinmarketSellActions.setCoinmarketSellAccount(undefined));
}

// clean coinmarketAccount in exchange
if (isExchange && exchangeCoinmarketAccount) {
api.dispatch(coinmarketExchangeActions.setCoinmarketExchangeAccount(undefined));
}

if (isBuy) {
api.dispatch(coinmarketCommonActions.setActiveSection('buy'));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
testnetToProdCryptoId,
getAddressAndTokenFromAccountOptionsGroupProps,
isCryptoIdForNativeToken,
getTradeTypeByRoute,
} from 'src/utils/wallet/coinmarket/coinmarketUtils';
import {
FIXTURE_ACCOUNTS,
Expand Down Expand Up @@ -339,4 +340,24 @@ describe('coinmarket utils', () => {
),
).toEqual(true);
});

it('getTradeTypeByRoute - testing correct returning trade section according to route', () => {
expect(getTradeTypeByRoute('wallet-coinmarket-buy')).toEqual('buy');
expect(getTradeTypeByRoute('wallet-coinmarket-buy-detail')).toEqual('buy');
expect(getTradeTypeByRoute('wallet-coinmarket-buy-offers')).toEqual('buy');
expect(getTradeTypeByRoute('wallet-coinmarket-buy-confirm')).toEqual('buy');

expect(getTradeTypeByRoute('wallet-coinmarket-sell')).toEqual('sell');
expect(getTradeTypeByRoute('wallet-coinmarket-sell-detail')).toEqual('sell');
expect(getTradeTypeByRoute('wallet-coinmarket-sell-offers')).toEqual('sell');
expect(getTradeTypeByRoute('wallet-coinmarket-sell-confirm')).toEqual('sell');

expect(getTradeTypeByRoute('wallet-coinmarket-exchange')).toEqual('exchange');
expect(getTradeTypeByRoute('wallet-coinmarket-exchange-detail')).toEqual('exchange');
expect(getTradeTypeByRoute('wallet-coinmarket-exchange-offers')).toEqual('exchange');
expect(getTradeTypeByRoute('wallet-coinmarket-exchange-confirm')).toEqual('exchange');

expect(getTradeTypeByRoute('wallet-coinmarket-dca')).toEqual(undefined);
expect(getTradeTypeByRoute('wallet-index')).toEqual(undefined);
});
});
18 changes: 17 additions & 1 deletion packages/suite/src/utils/wallet/coinmarket/coinmarketUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { BigNumber } from '@trezor/utils';

import { Account } from 'src/types/wallet';
import regional from 'src/constants/wallet/coinmarket/regional';
import { ExtendedMessageDescriptor, TrezorDevice } from 'src/types/suite';
import { ExtendedMessageDescriptor, Route, TrezorDevice } from 'src/types/suite';
import {
CoinmarketAccountOptionsGroupOptionProps,
CoinmarketAccountsOptionsGroupProps,
Expand Down Expand Up @@ -541,3 +541,19 @@ export const getAddressAndTokenFromAccountOptionsGroupProps = (

return { address: '', token: selected.contractAddress ?? null };
};

export const getTradeTypeByRoute = (
routeName: Route['name'] | undefined,
): CoinmarketTradeType | undefined => {
if (routeName?.startsWith('wallet-coinmarket-buy')) {
return 'buy';
}

if (routeName?.startsWith('wallet-coinmarket-sell')) {
return 'sell';
}

if (routeName?.startsWith('wallet-coinmarket-exchange')) {
return 'exchange';
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,11 @@ export const CoinmarketFormInputCryptoSelect = <
if (!findOption) return;

if (isCoinmarketExchangeContext(context)) {
context.setValue(FORM_RECEIVE_CRYPTO_CURRENCY_SELECT, findOption);
context.setValue(FORM_RECEIVE_CRYPTO_CURRENCY_SELECT, findOption, {
shouldDirty: true,
});
} else {
context.setValue(FORM_CRYPTO_CURRENCY_SELECT, findOption);
context.setValue(FORM_CRYPTO_CURRENCY_SELECT, findOption, { shouldDirty: true });
}

context.setAmountLimits(undefined);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ import {
} from 'src/reducers/suite/suiteReducer';
import { SUITE } from 'src/actions/suite/constants';
import { copyAddressToClipboard, showCopyAddressModal } from 'src/actions/suite/copyAddressActions';
import { setCoinmarketExchangeAccount } from 'src/actions/wallet/coinmarketExchangeActions';
import { setCoinmarketPrefilledFromCryptoId } from 'src/actions/wallet/coinmarket/coinmarketCommonActions';
import { setCoinmarketSellAccount } from 'src/actions/wallet/coinmarketSellActions';

import { BlurUrls } from '../BlurUrls';

Expand Down Expand Up @@ -230,7 +228,6 @@ export const TokenRow = ({
label: <Translation id="TR_COINMARKET_SELL" />,
icon: 'currencyCircleDollar',
onClick: () => {
dispatch(setCoinmarketSellAccount(account));
dispatch(
setCoinmarketPrefilledFromCryptoId(
tokenCryptoId,
Expand All @@ -250,7 +247,6 @@ export const TokenRow = ({
label: <Translation id="TR_COINMARKET_SWAP" />,
icon: 'arrowsLeftRight',
onClick: () => {
dispatch(setCoinmarketExchangeAccount(account));
dispatch(
setCoinmarketPrefilledFromCryptoId(
tokenCryptoId,
Expand Down Expand Up @@ -444,7 +440,6 @@ export const TokenRow = ({
icon="arrowsLeftRight"
size="small"
onClick={() => {
dispatch(setCoinmarketExchangeAccount(account));
dispatch(setCoinmarketPrefilledFromCryptoId(tokenCryptoId));
goToWithAnalytics('wallet-coinmarket-exchange', {
params: {
Expand Down

0 comments on commit 4839c7b

Please sign in to comment.