forked from deriv-com/deriv-app
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[WALL] Rostislav / WALL-2092 /
useTransferMessages
(deriv-com#11954)
* feat: hook outline * refactor: logic expansion * refactor: logic expansion * refactor: expansion * refactor: improve code quality * refactor: split split * fix: build * fix: logic
- Loading branch information
1 parent
44861a9
commit 4f70cae
Showing
11 changed files
with
339 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
...s/src/features/cashier/modules/Transfer/components/TransferMessages/TransferMessages.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.wallets-transfer-messages { | ||
min-height: 2rem; | ||
} |
22 changes: 22 additions & 0 deletions
22
...ts/src/features/cashier/modules/Transfer/components/TransferMessages/TransferMessages.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import React from 'react'; | ||
import { useFormikContext } from 'formik'; | ||
import { FadedAnimatedList, WalletAlertMessage } from '../../../../../../components'; | ||
import { useTransferMessages } from '../../hooks'; | ||
import { TInitialTransferFormValues } from '../../types'; | ||
import './TransferMessages.scss'; | ||
|
||
const TransferMessages = () => { | ||
const { values } = useFormikContext<TInitialTransferFormValues>(); | ||
|
||
const messages = useTransferMessages(values.fromAccount, values.toAccount, values); | ||
|
||
return ( | ||
<FadedAnimatedList className='wallets-transfer-messages'> | ||
{messages.map(message => ( | ||
<WalletAlertMessage key={message.text + message.type} message={message.text} type={message.type} /> | ||
))} | ||
</FadedAnimatedList> | ||
); | ||
}; | ||
|
||
export default TransferMessages; |
1 change: 1 addition & 0 deletions
1
packages/wallets/src/features/cashier/modules/Transfer/components/TransferMessages/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default as TransferMessages } from './TransferMessages'; |
1 change: 1 addition & 0 deletions
1
packages/wallets/src/features/cashier/modules/Transfer/hooks/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export { default as useExtendedTransferAccountProperties } from './useExtendedTransferAccountProperties'; | ||
export { default as useSortedTransferAccounts } from './useSortedTransferAccounts'; | ||
export { default as useTransferMessages } from './useTransferMessages'; |
3 changes: 3 additions & 0 deletions
3
packages/wallets/src/features/cashier/modules/Transfer/hooks/useTransferMessages/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import useTransferMessages from './useTransferMessages'; | ||
|
||
export default useTransferMessages; |
1 change: 1 addition & 0 deletions
1
...es/wallets/src/features/cashier/modules/Transfer/hooks/useTransferMessages/types/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './types'; |
17 changes: 17 additions & 0 deletions
17
...es/wallets/src/features/cashier/modules/Transfer/hooks/useTransferMessages/types/types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { THooks } from '../../../../../../../types'; | ||
import { TAccount } from '../../../types'; | ||
|
||
export type TMessage = { | ||
text: string; | ||
type: 'error' | 'success'; | ||
}; | ||
|
||
export type TMessageFnProps = { | ||
activeWallet: THooks.ActiveWalletAccount; | ||
displayMoney?: (amount: number, currency: string, fractionalDigits: number) => string; | ||
exchangeRates?: THooks.ExchangeRate; | ||
limits?: THooks.AccountLimits; | ||
sourceAccount: NonNullable<TAccount>; | ||
sourceAmount: number; | ||
targetAccount: NonNullable<TAccount>; | ||
}; |
110 changes: 110 additions & 0 deletions
110
...ts/src/features/cashier/modules/Transfer/hooks/useTransferMessages/useTransferMessages.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { useCallback, useEffect, useState } from 'react'; | ||
import { useAccountLimits, useActiveWalletAccount, useAuthorize, useExchangeRate, usePOI } from '@deriv/api'; | ||
import { displayMoney as displayMoney_ } from '@deriv/api/src/utils'; | ||
import { THooks } from '../../../../../../types'; | ||
import { TAccount, TInitialTransferFormValues } from '../../types'; | ||
import { | ||
cumulativeAccountLimitsMessageFn, | ||
lifetimeAccountLimitsBetweenWalletsMessageFn, | ||
} from './utils/messageFunctions'; | ||
import { TMessage, TMessageFnProps } from './types'; | ||
|
||
const useTransferMessages = ( | ||
fromAccount: NonNullable<TAccount> | undefined, | ||
toAccount: NonNullable<TAccount> | undefined, | ||
formData: TInitialTransferFormValues | ||
) => { | ||
const { data: authorizeData } = useAuthorize(); | ||
const { data: activeWallet } = useActiveWalletAccount(); | ||
const { preferred_language: preferredLanguage } = authorizeData; | ||
const { data: poi } = usePOI(); | ||
const { data: accountLimits } = useAccountLimits(); | ||
const { data: exchangeRatesRaw, subscribe, unsubscribe } = useExchangeRate(); | ||
|
||
const [exchangeRates, setExchangeRates] = useState<THooks.ExchangeRate>(); | ||
|
||
const isTransferBetweenWallets = | ||
fromAccount?.account_category === 'wallet' && toAccount?.account_category === 'wallet'; | ||
const isAccountVerified = poi?.is_verified; | ||
|
||
useEffect( | ||
() => setExchangeRates(prev => ({ ...prev, rates: { ...prev?.rates, ...exchangeRatesRaw?.rates } })), | ||
[exchangeRatesRaw?.rates] | ||
); | ||
|
||
useEffect(() => { | ||
if (!fromAccount?.currency || !toAccount?.currency || !activeWallet?.currency || !activeWallet?.loginid) return; | ||
unsubscribe(); | ||
if (!isAccountVerified && isTransferBetweenWallets) { | ||
subscribe({ | ||
base_currency: activeWallet.currency, | ||
loginid: activeWallet.loginid, | ||
target_currency: | ||
activeWallet.loginid === fromAccount.loginid ? toAccount.currency : fromAccount.currency, | ||
}); | ||
} else { | ||
subscribe({ | ||
base_currency: 'USD', | ||
loginid: activeWallet.loginid, | ||
target_currency: toAccount.currency, | ||
}); | ||
if (fromAccount.currency !== toAccount.currency) | ||
subscribe({ | ||
base_currency: 'USD', | ||
loginid: activeWallet.loginid, | ||
target_currency: fromAccount.currency, | ||
}); | ||
return unsubscribe; | ||
} | ||
}, [ | ||
activeWallet?.currency, | ||
activeWallet?.loginid, | ||
fromAccount?.currency, | ||
fromAccount?.loginid, | ||
isAccountVerified, | ||
isTransferBetweenWallets, | ||
subscribe, | ||
toAccount?.currency, | ||
unsubscribe, | ||
]); | ||
|
||
const displayMoney = useCallback( | ||
(amount: number, currency: string, fractionalDigits: number) => | ||
displayMoney_(amount, currency, { | ||
fractional_digits: fractionalDigits, | ||
preferred_language: preferredLanguage, | ||
}), | ||
[preferredLanguage] | ||
); | ||
|
||
if (!activeWallet || !fromAccount || !toAccount) return []; | ||
|
||
const sourceAmount = formData.fromAmount; | ||
|
||
const messageFns: ((props: TMessageFnProps) => TMessage | null)[] = []; | ||
const messages: TMessage[] = []; | ||
|
||
if (isAccountVerified || (!isAccountVerified && !isTransferBetweenWallets)) { | ||
messageFns.push(cumulativeAccountLimitsMessageFn); | ||
} | ||
if (!isAccountVerified && isTransferBetweenWallets) { | ||
messageFns.push(lifetimeAccountLimitsBetweenWalletsMessageFn); | ||
} | ||
|
||
messageFns.forEach(messageFn => { | ||
const message = messageFn({ | ||
activeWallet, | ||
displayMoney, | ||
exchangeRates, | ||
limits: accountLimits, | ||
sourceAccount: fromAccount, | ||
sourceAmount, | ||
targetAccount: toAccount, | ||
}); | ||
if (message) messages.push(message); | ||
}); | ||
|
||
return messages; | ||
}; | ||
|
||
export default useTransferMessages; |
177 changes: 177 additions & 0 deletions
177
...src/features/cashier/modules/Transfer/hooks/useTransferMessages/utils/messageFunctions.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
import { TMessageFnProps } from '../types'; | ||
|
||
// this function should work once BE WALL-1440 is delivered | ||
const lifetimeAccountLimitsBetweenWalletsMessageFn = ({ | ||
activeWallet, | ||
displayMoney, | ||
exchangeRates, | ||
limits, | ||
sourceAccount, | ||
sourceAmount, | ||
targetAccount, | ||
}: TMessageFnProps) => { | ||
if (sourceAccount?.account_category !== 'wallet' || targetAccount?.account_category !== 'wallet') return null; | ||
|
||
const sourceWalletType = sourceAccount.account_type === 'crypto' ? 'crypto' : 'fiat'; | ||
const targetWalletType = targetAccount.account_type === 'crypto' ? 'crypto' : 'fiat'; | ||
const limitsCaseKey = `${sourceWalletType}_to_${targetWalletType}` as const; | ||
|
||
//@ts-expect-error needs backend type | ||
const allowedSumActiveWalletCurrency = limits?.lifetime_transfers?.[limitsCaseKey].allowed as number; | ||
//@ts-expect-error needs backend type | ||
const availableSumActiveWalletCurrency = limits?.lifetime_transfers?.[limitsCaseKey].available as number; | ||
|
||
if ( | ||
!sourceAccount.currency || | ||
!exchangeRates?.rates?.[sourceAccount.currency] || | ||
!targetAccount.currency || | ||
!exchangeRates?.rates?.[targetAccount.currency] || | ||
!sourceAccount.currencyConfig || | ||
!targetAccount.currencyConfig | ||
) | ||
return null; | ||
|
||
const transferDirection = activeWallet.loginid === sourceAccount.loginid ? 'from' : 'to'; | ||
|
||
const allowedSumConverted = | ||
allowedSumActiveWalletCurrency * | ||
(exchangeRates?.rates[transferDirection === 'from' ? targetAccount.currency : sourceAccount.currency] ?? 1); | ||
const availableSumConverted = | ||
availableSumActiveWalletCurrency * | ||
(exchangeRates?.rates[transferDirection === 'from' ? targetAccount.currency : sourceAccount.currency] ?? 1); | ||
|
||
const sourceCurrencyLimit = transferDirection === 'from' ? allowedSumActiveWalletCurrency : allowedSumConverted; | ||
const targetCurrencyLimit = transferDirection === 'from' ? allowedSumConverted : allowedSumActiveWalletCurrency; | ||
|
||
const sourceCurrencyRemainder = | ||
transferDirection === 'from' ? availableSumActiveWalletCurrency : availableSumConverted; | ||
const targetCurrencyRemainder = | ||
transferDirection === 'from' ? availableSumConverted : availableSumActiveWalletCurrency; | ||
|
||
const formattedSourceCurrencyLimit = displayMoney?.( | ||
sourceCurrencyLimit, | ||
sourceAccount.currencyConfig.display_code, | ||
sourceAccount.currencyConfig.fractional_digits | ||
); | ||
const formattedTargetCurrencyLimit = displayMoney?.( | ||
targetCurrencyLimit, | ||
targetAccount.currencyConfig.display_code, | ||
targetAccount.currencyConfig?.fractional_digits | ||
); | ||
|
||
const formattedSourceCurrencyRemainder = displayMoney?.( | ||
sourceCurrencyRemainder, | ||
sourceAccount.currencyConfig.display_code, | ||
sourceAccount.currencyConfig.fractional_digits | ||
); | ||
const formattedTargetCurrencyRemainder = displayMoney?.( | ||
targetCurrencyRemainder, | ||
targetAccount.currencyConfig?.display_code, | ||
targetAccount.currencyConfig?.fractional_digits | ||
); | ||
|
||
if (availableSumActiveWalletCurrency === 0) | ||
return { | ||
text: `You've reached the lifetime transfer limit from your ${sourceAccount.accountName} to any ${targetWalletType} Wallet. Verify your account to upgrade the limit.`, | ||
type: 'error' as const, | ||
}; | ||
|
||
if (allowedSumActiveWalletCurrency === availableSumActiveWalletCurrency) | ||
return { | ||
text: `The lifetime transfer limit from ${sourceAccount.accountName} to any ${targetWalletType} Wallet is ${formattedSourceCurrencyLimit} (${formattedTargetCurrencyLimit}).`, | ||
type: sourceAmount > sourceCurrencyRemainder ? ('error' as const) : ('success' as const), | ||
}; | ||
|
||
return { | ||
text: `Remaining lifetime transfer limit is ${formattedSourceCurrencyRemainder} (${formattedTargetCurrencyRemainder}). Verify your account to upgrade the limit.`, | ||
type: sourceAmount > sourceCurrencyRemainder ? ('error' as const) : ('success' as const), | ||
}; | ||
}; | ||
|
||
const cumulativeAccountLimitsMessageFn = ({ | ||
displayMoney, | ||
exchangeRates, | ||
limits, | ||
sourceAccount, | ||
sourceAmount, | ||
targetAccount, | ||
}: TMessageFnProps) => { | ||
const isTransferBetweenWallets = | ||
sourceAccount.account_category === 'wallet' && targetAccount.account_category === 'wallet'; | ||
const isSameCurrency = sourceAccount.currency === targetAccount.currency; | ||
|
||
const keyAccountType = | ||
[sourceAccount, targetAccount].find(acc => acc.account_category !== 'wallet')?.account_type ?? 'wallets'; | ||
|
||
const platformKey = keyAccountType === 'standard' ? 'dtrade' : keyAccountType; | ||
|
||
//@ts-expect-error needs backend type | ||
const allowedSumUSD = limits?.daily_cumulative_amount_transfers?.[platformKey].allowed as number; | ||
//@ts-expect-error needs backend type | ||
const availableSumUSD = limits?.daily_cumulative_amount_transfers?.[platformKey].available as number; | ||
|
||
if ( | ||
!sourceAccount.currency || | ||
!exchangeRates?.rates?.[sourceAccount.currency] || | ||
!targetAccount.currency || | ||
!exchangeRates?.rates?.[targetAccount.currency] || | ||
!sourceAccount.currencyConfig || | ||
!targetAccount.currencyConfig | ||
) | ||
return null; | ||
|
||
const sourceCurrencyLimit = allowedSumUSD * (exchangeRates.rates[sourceAccount.currency] ?? 1); | ||
const targetCurrencyLimit = allowedSumUSD * (exchangeRates.rates[targetAccount.currency] ?? 1); | ||
|
||
const sourceCurrencyRemainder = availableSumUSD * (exchangeRates.rates[sourceAccount.currency] ?? 1); | ||
const targetCurrencyRemainder = availableSumUSD * (exchangeRates.rates[targetAccount.currency] ?? 1); | ||
|
||
const formattedSourceCurrencyLimit = displayMoney?.( | ||
sourceCurrencyLimit, | ||
sourceAccount.currencyConfig.display_code, | ||
sourceAccount.currencyConfig.fractional_digits | ||
); | ||
const formattedTargetCurrencyLimit = displayMoney?.( | ||
targetCurrencyLimit, | ||
targetAccount.currencyConfig.display_code, | ||
targetAccount.currencyConfig?.fractional_digits | ||
); | ||
|
||
const formattedSourceCurrencyRemainder = displayMoney?.( | ||
sourceCurrencyRemainder, | ||
sourceAccount.currencyConfig.display_code, | ||
sourceAccount.currencyConfig.fractional_digits | ||
); | ||
const formattedTargetCurrencyRemainder = displayMoney?.( | ||
targetCurrencyRemainder, | ||
targetAccount.currencyConfig?.display_code, | ||
targetAccount.currencyConfig?.fractional_digits | ||
); | ||
|
||
if (availableSumUSD === 0) | ||
return { | ||
text: `You have reached your daily transfer limit of ${formattedSourceCurrencyLimit} ${ | ||
!isSameCurrency ? ` (${formattedTargetCurrencyLimit})` : '' | ||
} between your ${ | ||
isTransferBetweenWallets ? 'Wallets' : `${sourceAccount.accountName} and ${targetAccount.accountName}` | ||
}. The limit will reset at 00:00 GMT.`, | ||
type: 'error' as const, | ||
}; | ||
|
||
if (allowedSumUSD === availableSumUSD) | ||
return { | ||
text: `The daily transfer limit between your ${ | ||
isTransferBetweenWallets ? 'Wallets' : `${sourceAccount.accountName} and ${targetAccount.accountName}` | ||
} is ${formattedSourceCurrencyLimit}${!isSameCurrency ? ` (${formattedTargetCurrencyLimit})` : ''}.`, | ||
type: sourceAmount > sourceCurrencyRemainder ? ('error' as const) : ('success' as const), | ||
}; | ||
|
||
return { | ||
text: `The remaining daily transfer limit between ${ | ||
isTransferBetweenWallets ? 'Wallets' : `your ${sourceAccount.accountName} and ${targetAccount.accountName}` | ||
} is ${formattedSourceCurrencyRemainder}${!isSameCurrency ? ` (${formattedTargetCurrencyRemainder})` : ''}.`, | ||
type: sourceAmount > sourceCurrencyRemainder ? ('error' as const) : ('success' as const), | ||
}; | ||
}; | ||
|
||
export { cumulativeAccountLimitsMessageFn, lifetimeAccountLimitsBetweenWalletsMessageFn }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters