From 534c40331d49bf6456e206535b01a2eee5ace43a Mon Sep 17 00:00:00 2001 From: Slava Date: Fri, 15 Nov 2024 17:25:36 +0300 Subject: [PATCH 01/12] chore: replace effects with react query callbacks --- .storybook/preview.tsx | 16 +++- apps/bob-pay/src/app/[lang]/send/Send.tsx | 84 ++++++++----------- .../TokenButtonGroup/TokenButtonGroup.tsx | 14 ++-- apps/bob-pay/src/hooks/useBalances.ts | 5 +- apps/bob-pay/src/lib/react-query/index.ts | 16 +++- .../src/lib/react-query/react-query.d.ts | 21 +++++ .../app/[lang]/(bridge)/hooks/useGateway.ts | 25 +++--- apps/evm/src/lib/react-query/index.ts | 16 +++- apps/evm/src/lib/react-query/react-query.d.ts | 21 +++++ apps/evm/src/test-utils/wrapper.tsx | 16 +++- packages/react-query/react-query.d.ts | 21 +++++ .../components/NumberInput/NumberInput.tsx | 12 +-- .../src/components/TokenInput/TokenInput.tsx | 11 ++- 13 files changed, 195 insertions(+), 83 deletions(-) create mode 100644 apps/bob-pay/src/lib/react-query/react-query.d.ts create mode 100644 apps/evm/src/lib/react-query/react-query.d.ts create mode 100644 packages/react-query/react-query.d.ts diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 3cf93e143..681f7f898 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -6,9 +6,21 @@ import { CSSReset, BOBUIProvider, bobTheme } from '../packages/ui/src'; import { WagmiProvider } from '../packages/wagmi/src'; import { SatsWagmiConfig } from '../packages/sats-wagmi/src'; import './style.css'; -import { QueryClient, QueryClientProvider } from '../packages/react-query/src'; +import { QueryCache, QueryClient, QueryClientProvider } from '../packages/react-query/src'; -const queryClient = new QueryClient(); +const queryClient = new QueryClient({ + queryCache: new QueryCache({ + onError: (error, query) => { + if (typeof query.meta?.onError === 'function') query.meta.onError(error, query); + }, + onSuccess(data, query) { + if (typeof query.meta?.onSuccess === 'function') query.meta.onSuccess(data, query); + }, + onSettled(data, error, query) { + if (typeof query.meta?.onSettled === 'function') query.meta.onSettled(data, error, query); + } + }) +}); const preview: Preview = { parameters: { diff --git a/apps/bob-pay/src/app/[lang]/send/Send.tsx b/apps/bob-pay/src/app/[lang]/send/Send.tsx index 88939cc60..551ccfc63 100644 --- a/apps/bob-pay/src/app/[lang]/send/Send.tsx +++ b/apps/bob-pay/src/app/[lang]/send/Send.tsx @@ -12,7 +12,7 @@ import { t, Trans } from '@lingui/macro'; import { useLingui } from '@lingui/react'; import { mergeProps } from '@react-aria/utils'; import Big from 'big.js'; -import { useEffect, useMemo, useState } from 'react'; +import { useMemo, useState } from 'react'; import { Address, encodeFunctionData, erc20Abi, isAddress } from 'viem'; import { ScannerModal, TokenButtonGroup } from './components'; @@ -66,7 +66,15 @@ const Send = ({ ticker: tickerProp = 'WBTC', recipient }: SendProps): JSX.Elemen const [isGroupAmount, setGroupAmount] = useState(false); const { getPrice } = usePrices(); - const { getBalance, isPending } = useBalances(CHAIN); + const { getBalance } = useBalances(CHAIN, { + meta: { + onSettled: () => { + if (form.values[TRANSFER_TOKEN_AMOUNT]) { + form.validateField(TRANSFER_TOKEN_AMOUNT); + } + } + } + }); const { data: tokens } = useTokens(CHAIN); @@ -137,8 +145,7 @@ const Send = ({ ticker: tickerProp = 'WBTC', recipient }: SendProps): JSX.Elemen const { data: eoaTransferTx, mutate: eoaTransfer, - isPending: isEOATransferPending, - error: eoaTransferError + isPending: isEOATransferPending } = useMutation({ mutationKey: ['eoa-transfer', amount, form.values[TRANSFER_TOKEN_RECIPIENT]], mutationFn: async ({ @@ -184,39 +191,33 @@ const Send = ({ ticker: tickerProp = 'WBTC', recipient }: SendProps): JSX.Elemen }); return txid; + }, + onError(error) { + toast.error(error.message); + // eslint-disable-next-line no-console + console.log(error); } }); - const { isLoading: isWaitingEoaTransferTxConfirmation, data: eoaTransferTransactionReceipt } = - useWaitForTransactionReceipt({ - hash: eoaTransferTx - }); - - useEffect(() => { - if (eoaTransferTransactionReceipt?.status === 'success') { - toast.success(t(i18n)`Successfully sent ${amount} ${token?.currency.symbol}`); - - form.resetForm(); - setAmount(''); - setGroupAmount(false); - setTicker(tickerProp); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [eoaTransferTransactionReceipt]); + const { isLoading: isWaitingEoaTransferTxConfirmation } = useWaitForTransactionReceipt({ + hash: eoaTransferTx, + query: { + meta: { + onSuccess: (data: { status: 'success' }) => { + if (data?.status === 'success') { + toast.success(t(i18n)`Successfully sent ${amount} ${token?.currency.symbol}`); - useEffect(() => { - if (eoaTransferError) { - toast.error(eoaTransferError.message); - // eslint-disable-next-line no-console - console.log(eoaTransferError); + form.resetForm(); + setAmount(''); + setGroupAmount(false); + setTicker(tickerProp); + } + } + } } - }, [eoaTransferError]); + }); - const { - mutate: smartAccountTransfer, - isPending: isSmartAccountTransferPending, - error: smartAccountTransferError - } = useMutation({ + const { mutate: smartAccountTransfer, isPending: isSmartAccountTransferPending } = useMutation({ mutationKey: ['smart-account-transfer', amount, form.values[TRANSFER_TOKEN_RECIPIENT]], onSuccess: (tx, variables) => { if (!tx) { @@ -234,6 +235,11 @@ const Send = ({ ticker: tickerProp = 'WBTC', recipient }: SendProps): JSX.Elemen setGroupAmount(false); setTicker(tickerProp); }, + onError(error) { + toast.error(t(i18n)`Failed to submit transaction`); + // eslint-disable-next-line no-console + console.log(error); + }, mutationFn: async ({ recipient, currencyAmount @@ -317,22 +323,6 @@ const Send = ({ ticker: tickerProp = 'WBTC', recipient }: SendProps): JSX.Elemen } }); - useEffect(() => { - if (smartAccountTransferError) { - toast.error(t(i18n)`Failed to submit transaction`); - // eslint-disable-next-line no-console - console.log(smartAccountTransferError); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [smartAccountTransferError]); - - useEffect(() => { - if (!isPending && form.values[TRANSFER_TOKEN_AMOUNT]) { - form.validateField(TRANSFER_TOKEN_AMOUNT); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isPending]); - const tokenInputItems = useMemo( () => tokens?.map((token) => { diff --git a/apps/bob-pay/src/app/[lang]/send/components/TokenButtonGroup/TokenButtonGroup.tsx b/apps/bob-pay/src/app/[lang]/send/components/TokenButtonGroup/TokenButtonGroup.tsx index 0f3c78de2..d9092cb6e 100644 --- a/apps/bob-pay/src/app/[lang]/send/components/TokenButtonGroup/TokenButtonGroup.tsx +++ b/apps/bob-pay/src/app/[lang]/send/components/TokenButtonGroup/TokenButtonGroup.tsx @@ -3,7 +3,7 @@ import { Flex, Span, useCurrencyFormatter, useLocale } from '@gobob/ui'; import { Item } from '@react-stately/collections'; import { Currency, CurrencyAmount } from '@gobob/currency'; import { usePrices } from '@gobob/react-query'; -import { useEffect, useMemo, useState } from 'react'; +import { useMemo, useState } from 'react'; import Big from 'big.js'; import { ButtonGroup } from '../ButtonGroup'; @@ -25,12 +25,14 @@ const TokenButtonGroup = ({ isSelected, currency, balance, onSelectionChange }: const { getPrice, data: pricesData } = usePrices(); const [key, setKey] = useState(); + const [prevIsSelected, setPrevIsSelected] = useState(false); - useEffect(() => { - if (!isSelected) { - setKey(undefined); - } - }, [isSelected]); + if (!prevIsSelected && isSelected) setPrevIsSelected(true); + + if (prevIsSelected && !isSelected) { + setKey(undefined); + setPrevIsSelected(false); + } const amounts = useMemo(() => { if (currency.symbol === 'WBTC') { diff --git a/apps/bob-pay/src/hooks/useBalances.ts b/apps/bob-pay/src/hooks/useBalances.ts index 6d8a4a2b6..1fe7b357e 100644 --- a/apps/bob-pay/src/hooks/useBalances.ts +++ b/apps/bob-pay/src/hooks/useBalances.ts @@ -1,6 +1,6 @@ import { ChainId } from '@gobob/chains'; import { CurrencyAmount, ERC20Token, Ether } from '@gobob/currency'; -import { INTERVAL, useQuery } from '@gobob/react-query'; +import { DefinedInitialDataOptions, INTERVAL, useQuery } from '@gobob/react-query'; import { useCallback, useMemo } from 'react'; import { Address, erc20Abi } from 'viem'; import { usePublicClient } from 'wagmi'; @@ -10,7 +10,7 @@ import { useTokens } from './useTokens'; type Balances = Record>; -const useBalances = (chainId: ChainId) => { +const useBalances = (chainId: ChainId, query?: Pick) => { const publicClient = usePublicClient({ chainId }); const address = useDynamicAddress(); @@ -20,6 +20,7 @@ const useBalances = (chainId: ChainId) => { const native = useMemo(() => tokens.find((token) => token.currency.isNative), [tokens]); const { data: balances, ...queryResult } = useQuery({ + ...query, queryKey: ['balances', chainId, address], enabled: Boolean(address && publicClient && tokens), queryFn: async () => { diff --git a/apps/bob-pay/src/lib/react-query/index.ts b/apps/bob-pay/src/lib/react-query/index.ts index 845e508bd..cf933cbfd 100644 --- a/apps/bob-pay/src/lib/react-query/index.ts +++ b/apps/bob-pay/src/lib/react-query/index.ts @@ -1,5 +1,17 @@ -import { QueryClient } from '@gobob/react-query'; +import { QueryCache, QueryClient } from '@gobob/react-query'; -const queryClient = new QueryClient(); +const queryClient = new QueryClient({ + queryCache: new QueryCache({ + onError: (error, query) => { + if (typeof query.meta?.onError === 'function') query.meta.onError(error, query); + }, + onSuccess(data, query) { + if (typeof query.meta?.onSuccess === 'function') query.meta.onSuccess(data, query); + }, + onSettled(data, error, query) { + if (typeof query.meta?.onSettled === 'function') query.meta.onSettled(data, error, query); + } + }) +}); export { queryClient }; diff --git a/apps/bob-pay/src/lib/react-query/react-query.d.ts b/apps/bob-pay/src/lib/react-query/react-query.d.ts new file mode 100644 index 000000000..7c7cdc83b --- /dev/null +++ b/apps/bob-pay/src/lib/react-query/react-query.d.ts @@ -0,0 +1,21 @@ +import '@gobob/react-query'; +import { DefaultError, Query } from '@gobob/react-query'; + +// https://tanstack.com/query/latest/docs/framework/react/typescript#typing-meta +interface CustomQueryMeta extends Record { + onSuccess?: ((data: TQueryFnData, query: Query) => void) | undefined; + onError?: ((error: DefaultError, query: Query) => void) | undefined; + onSettled?: + | (( + data: TQueryFnData | undefined, + error: DefaultError | null, + query: Query + ) => void) + | undefined; +} + +declare module '@gobob/react-query' { + interface Register extends Record { + queryMeta: CustomQueryMeta; + } +} diff --git a/apps/evm/src/app/[lang]/(bridge)/hooks/useGateway.ts b/apps/evm/src/app/[lang]/(bridge)/hooks/useGateway.ts index fee8238b1..6f53e28f2 100644 --- a/apps/evm/src/app/[lang]/(bridge)/hooks/useGateway.ts +++ b/apps/evm/src/app/[lang]/(bridge)/hooks/useGateway.ts @@ -24,8 +24,8 @@ import { toast } from '@gobob/ui'; import { Address, useAccount } from '@gobob/wagmi'; import { t } from '@lingui/macro'; import { useLingui } from '@lingui/react'; +import { Dispatch, SetStateAction, useCallback, useMemo, useState } from 'react'; import * as Sentry from '@sentry/nextjs'; -import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from 'react'; import { DebouncedState, useDebounceValue } from 'usehooks-ts'; import { isAddress } from 'viem'; @@ -213,9 +213,16 @@ const useGateway = ({ params, onError, onMutate, onSuccess }: UseGatewayLiquidit } }); + const estimateFeeErrorMessage = t(i18n)`Failed to get estimated fee`; + const feeRatesQueryResult = useSatsFeeRate({ query: { - select: feeRatesSelect + select: feeRatesSelect, + meta: { + onError() { + toast.error(estimateFeeErrorMessage); + } + } } }); @@ -227,17 +234,15 @@ const useGateway = ({ params, onError, onMutate, onSuccess }: UseGatewayLiquidit feeRate: feeRate, query: { enabled: Boolean(satsBalance && satsBalance.total > 0n && evmAddress), - select: (data) => CurrencyAmount.fromRawAmount(BITCOIN, data.amount) + select: (data) => CurrencyAmount.fromRawAmount(BITCOIN, data.amount), + meta: { + onError() { + toast.error(estimateFeeErrorMessage); + } + } } }); - useEffect(() => { - if (feeEstimateQueryResult.error || feeRatesQueryResult.error) { - toast.error(t(i18n)`Failed to get estimated fee`); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [feeRatesQueryResult.error, feeEstimateQueryResult.error]); - const balance = useMemo( () => getBalanceAmount(satsBalance?.total, feeEstimateQueryResult.data, liquidityQueryResult.data?.liquidityAmount), [liquidityQueryResult.data?.liquidityAmount, satsBalance?.total, feeEstimateQueryResult.data] diff --git a/apps/evm/src/lib/react-query/index.ts b/apps/evm/src/lib/react-query/index.ts index 3e4d856ac..402c55e81 100644 --- a/apps/evm/src/lib/react-query/index.ts +++ b/apps/evm/src/lib/react-query/index.ts @@ -1,6 +1,18 @@ -import { QueryClient } from '@gobob/react-query'; +import { QueryCache, QueryClient } from '@gobob/react-query'; -const queryClient = new QueryClient(); +const queryClient = new QueryClient({ + queryCache: new QueryCache({ + onError: (error, query) => { + if (typeof query.meta?.onError === 'function') query.meta.onError(error, query); + }, + onSuccess(data, query) { + if (typeof query.meta?.onSuccess === 'function') query.meta.onSuccess(data, query); + }, + onSettled(data, error, query) { + if (typeof query.meta?.onSettled === 'function') query.meta.onSettled(data, error, query); + } + }) +}); export { queryClient }; export * from './keys'; diff --git a/apps/evm/src/lib/react-query/react-query.d.ts b/apps/evm/src/lib/react-query/react-query.d.ts new file mode 100644 index 000000000..7c7cdc83b --- /dev/null +++ b/apps/evm/src/lib/react-query/react-query.d.ts @@ -0,0 +1,21 @@ +import '@gobob/react-query'; +import { DefaultError, Query } from '@gobob/react-query'; + +// https://tanstack.com/query/latest/docs/framework/react/typescript#typing-meta +interface CustomQueryMeta extends Record { + onSuccess?: ((data: TQueryFnData, query: Query) => void) | undefined; + onError?: ((error: DefaultError, query: Query) => void) | undefined; + onSettled?: + | (( + data: TQueryFnData | undefined, + error: DefaultError | null, + query: Query + ) => void) + | undefined; +} + +declare module '@gobob/react-query' { + interface Register extends Record { + queryMeta: CustomQueryMeta; + } +} diff --git a/apps/evm/src/test-utils/wrapper.tsx b/apps/evm/src/test-utils/wrapper.tsx index d733392fb..379e8dfbf 100644 --- a/apps/evm/src/test-utils/wrapper.tsx +++ b/apps/evm/src/test-utils/wrapper.tsx @@ -1,4 +1,4 @@ -import { QueryClient, QueryClientProvider } from '@gobob/react-query'; +import { QueryCache, QueryClient, QueryClientProvider } from '@gobob/react-query'; import { BOBUIProvider } from '@gobob/ui'; import { WagmiProvider } from '@gobob/wagmi'; import { PropsWithChildren } from 'react'; @@ -6,7 +6,19 @@ import { PropsWithChildren } from 'react'; import { LinguiClientProvider } from '@/i18n/provider'; export const wrapper = ({ children }: PropsWithChildren) => { - const queryClient = new QueryClient(); + const queryClient = new QueryClient({ + queryCache: new QueryCache({ + onError: (error, query) => { + if (typeof query.meta?.onError === 'function') query.meta.onError(error, query); + }, + onSuccess(data, query) { + if (typeof query.meta?.onSuccess === 'function') query.meta.onSuccess(data, query); + }, + onSettled(data, error, query) { + if (typeof query.meta?.onSettled === 'function') query.meta.onSettled(data, error, query); + } + }) + }); return ( diff --git a/packages/react-query/react-query.d.ts b/packages/react-query/react-query.d.ts new file mode 100644 index 000000000..5eca5fc4d --- /dev/null +++ b/packages/react-query/react-query.d.ts @@ -0,0 +1,21 @@ +import '@tanstack/react-query'; +import { DefaultError, Query } from '@tanstack/react-query'; + +// https://tanstack.com/query/latest/docs/framework/react/typescript#typing-meta +interface CustomQueryMeta extends Record { + onSuccess?: ((data: TQueryFnData, query: Query) => void) | undefined; + onError?: ((error: DefaultError, query: Query) => void) | undefined; + onSettled?: + | (( + data: TQueryFnData | undefined, + error: DefaultError | null, + query: Query + ) => void) + | undefined; +} + +declare module '@tanstack/react-query' { + interface Register extends Record { + queryMeta: CustomQueryMeta; + } +} diff --git a/packages/ui/src/components/NumberInput/NumberInput.tsx b/packages/ui/src/components/NumberInput/NumberInput.tsx index 84c155fc2..dbd0cf72a 100644 --- a/packages/ui/src/components/NumberInput/NumberInput.tsx +++ b/packages/ui/src/components/NumberInput/NumberInput.tsx @@ -2,7 +2,7 @@ import { AriaTextFieldOptions, useTextField } from '@react-aria/textfield'; import { mergeProps } from '@react-aria/utils'; -import { ChangeEventHandler, forwardRef, useEffect, useState } from 'react'; +import { ChangeEventHandler, forwardRef, useState } from 'react'; import { useDOMRef } from '../../hooks'; import { BaseInput, BaseInputProps } from '../Input'; @@ -44,6 +44,7 @@ const NumberInput = forwardRef( ref ): JSX.Element => { const [value, setValue] = useState(defaultValue?.toString()); + const [prevValue, setPrevValue] = useState(value); const inputRef = useDOMRef(ref); const handleChange: ChangeEventHandler = (e) => { @@ -70,11 +71,10 @@ const NumberInput = forwardRef( inputRef ); - useEffect(() => { - if (valueProp === undefined) return; - - setValue(valueProp.toString()); - }, [valueProp]); + if (prevValue !== valueProp) { + setPrevValue(value); + if (valueProp !== undefined) setValue(valueProp.toString()); + } return ( ((props, ref): J const inputRef = useDOMRef(ref); const [value, setValue] = useState(defaultValue); + const [prevValueProp, setPrevValueProp] = useState(valueProp); const defaultCurrency = useMemo(() => getDefaultCurrency(props), [props]); const [currency, setCurrency] = useState(defaultCurrency); @@ -61,11 +62,13 @@ const TokenInput = forwardRef((props, ref): J props.type === 'selectable' ? [props.items, props.selectProps?.value, props.selectProps?.defaultValue] : [] ); - useEffect(() => { - if (valueProp === undefined) return; + if (valueProp !== prevValueProp) { + setPrevValueProp(valueProp); - setValue(valueProp); - }, [valueProp]); + if (valueProp !== undefined) { + setValue(valueProp); + } + } useEffect(() => { if (value && currency) { From e7dc4543dfbfe55ebc27a4c525b902b54cc23614 Mon Sep 17 00:00:00 2001 From: Slava Date: Fri, 15 Nov 2024 21:04:10 +0300 Subject: [PATCH 02/12] chore: replace more effects --- .../components/BridgeForm/BobBridgeForm.tsx | 61 +++++++++++-------- .../[lang]/(bridge)/hooks/useGatewayForm.ts | 19 +++--- .../ReferralInput/ReferralInput.tsx | 12 ++-- .../SendTokenModal/SendTokenModal.tsx | 11 ++-- .../src/components/TokenInput/TokenInput.tsx | 8 ++- 5 files changed, 64 insertions(+), 47 deletions(-) diff --git a/apps/evm/src/app/[lang]/(bridge)/bridge/components/BridgeForm/BobBridgeForm.tsx b/apps/evm/src/app/[lang]/(bridge)/bridge/components/BridgeForm/BobBridgeForm.tsx index 1020ac159..13d5a7a48 100644 --- a/apps/evm/src/app/[lang]/(bridge)/bridge/components/BridgeForm/BobBridgeForm.tsx +++ b/apps/evm/src/app/[lang]/(bridge)/bridge/components/BridgeForm/BobBridgeForm.tsx @@ -11,7 +11,7 @@ import { t } from '@lingui/macro'; import { useLingui } from '@lingui/react'; import { mergeProps } from '@react-aria/utils'; import Big from 'big.js'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { useDebounceValue } from 'usehooks-ts'; import { Address } from 'viem'; @@ -390,36 +390,15 @@ const BobBridgeForm = ({ } }; - useEffect(() => { - if (!amount) return; + const [prevChain, setPrevChain] = useState(chain); - const formAmount = form.values[BRIDGE_AMOUNT]; + if (chain !== prevChain) { + setPrevChain(chain); - if (!formAmount || isNaN(+formAmount) || !selectedCurrency || !selectedToken) return; - - // TODO: change currency - const currencyAmount = CurrencyAmount.fromBaseAmount(selectedCurrency, formAmount); - - handleChangeCurrencyAmount(currencyAmount, selectedToken); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [amount]); - - useEffect(() => { - form.resetForm(); - gasEstimateMutation.reset(); - - setTicker(nativeToken.symbol); - setGasTicker(nativeToken.symbol); - setAmount(''); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [direction]); - - useEffect(() => { if (currencyAmount && selectedToken) { handleChangeCurrencyAmount(currencyAmount, selectedToken); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [chain]); + } const handleSubmit = async (data: BridgeFormValues) => { if (!currencyAmount || !selectedToken || !selectedGasToken || isBridgeDisabled) return; @@ -473,6 +452,36 @@ const BobBridgeForm = ({ hideErrors: 'untouched' }); + const [prevAmount, setPrevAmount] = useState(amount); + + if (amount !== prevAmount) { + setPrevAmount(amount); + + if (amount) { + const formAmount = form.values[BRIDGE_AMOUNT]; + + if (formAmount && !isNaN(+formAmount) && selectedCurrency && selectedToken) { + // TODO: change currency + const currencyAmount = CurrencyAmount.fromBaseAmount(selectedCurrency, formAmount); + + handleChangeCurrencyAmount(currencyAmount, selectedToken); + } + } + } + + const [prevDirection, setPrevDirection] = useState(); + + if (direction !== prevDirection) { + setPrevDirection(direction); + + form.resetForm(); + gasEstimateMutation.reset(); + + setTicker(nativeToken.symbol); + setGasTicker(nativeToken.symbol); + setAmount(''); + } + const handleChangeTicker = (currency: Currency) => { setTicker(currency.symbol as string); diff --git a/apps/evm/src/app/[lang]/(bridge)/hooks/useGatewayForm.ts b/apps/evm/src/app/[lang]/(bridge)/hooks/useGatewayForm.ts index 7daedd938..f8b5e2231 100644 --- a/apps/evm/src/app/[lang]/(bridge)/hooks/useGatewayForm.ts +++ b/apps/evm/src/app/[lang]/(bridge)/hooks/useGatewayForm.ts @@ -3,7 +3,7 @@ import { useForm } from '@gobob/ui'; import { useAccount, useIsContract } from '@gobob/wagmi'; import Big from 'big.js'; -import { useEffect } from 'react'; +import { useState } from 'react'; import { useAccount as useSatsAccount } from '@gobob/sats-wagmi'; import { UseGatewayQueryDataReturnType } from './useGateway'; @@ -32,13 +32,6 @@ const useGatewayForm = ({ query, defaultAsset, onSubmit }: UseGatewayFormProps) const { address: btcAddress } = useSatsAccount(); - useEffect(() => { - if (!query.fee.estimate.data || !form.values[BRIDGE_AMOUNT]) return; - - form.validateField(BRIDGE_AMOUNT); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [query.fee.rates.data]); - const params: BridgeFormValidationParams = { [BRIDGE_AMOUNT]: { minAmount: new Big(query.minAmount.toExact()), @@ -61,6 +54,16 @@ const useGatewayForm = ({ query, defaultAsset, onSubmit }: UseGatewayFormProps) hideErrors: 'untouched' }); + const [prevData, setPrevData] = useState(query.fee.rates.data); + + if (query.fee.rates.data !== prevData) { + setPrevData(query.fee.rates.data); + + if (query.fee.estimate.data && form.values[BRIDGE_AMOUNT]) { + form.validateField(BRIDGE_AMOUNT); + } + } + return { isDisabled: isFormDisabled(form), form, diff --git a/apps/evm/src/app/[lang]/sign-up/components/ReferralInput/ReferralInput.tsx b/apps/evm/src/app/[lang]/sign-up/components/ReferralInput/ReferralInput.tsx index 3a7aa61f9..159e1c5f6 100644 --- a/apps/evm/src/app/[lang]/sign-up/components/ReferralInput/ReferralInput.tsx +++ b/apps/evm/src/app/[lang]/sign-up/components/ReferralInput/ReferralInput.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useState } from 'react'; import OtpInput, { OTPInputProps } from 'react-otp-input'; import { useLabel } from '@react-aria/label'; import { Flex, Label, P } from '@gobob/ui'; @@ -25,6 +25,7 @@ const ReferralInput = ({ onChange, errorMessage, ...props }: ReferralInputProps) const { i18n } = useLingui(); const [otp, setOtp] = useState(refCode || ''); + const [prevOtp, setPrevOtp] = useState(otp); const labelText = t(i18n)`Enter your access code (optional):`; const { fieldProps, labelProps } = useLabel({ label: labelText }); @@ -37,12 +38,11 @@ const ReferralInput = ({ onChange, errorMessage, ...props }: ReferralInputProps) [onChange] ); - useEffect(() => { - if (!otp) return; + if (otp !== prevOtp) { + setPrevOtp(otp); - handleChange(otp); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [otp]); + if (otp) handleChange(otp); + } const hasError = !!errorMessage; diff --git a/apps/evm/src/app/[lang]/wallet/components/SendTokenModal/SendTokenModal.tsx b/apps/evm/src/app/[lang]/wallet/components/SendTokenModal/SendTokenModal.tsx index 8f4962c40..c42bfeced 100644 --- a/apps/evm/src/app/[lang]/wallet/components/SendTokenModal/SendTokenModal.tsx +++ b/apps/evm/src/app/[lang]/wallet/components/SendTokenModal/SendTokenModal.tsx @@ -22,7 +22,7 @@ import { Trans, t } from '@lingui/macro'; import { useLingui } from '@lingui/react'; import { mergeProps } from '@react-aria/utils'; import Big from 'big.js'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { Address, erc20Abi } from 'viem'; import { TransactionDetails } from '@/app/[lang]/(bridge)/components/TransactionDetails'; @@ -192,13 +192,16 @@ const SendTokenModal = ({ token, onClose, ...props }: SendTokenModalProps): JSX. hash: currency.isToken ? transferErc20Result : sendTransactionResult }); - useEffect(() => { + const [prevStatus, setPrevStatus] = useState(transactionReceipt?.status); + + if (transactionReceipt?.status !== prevStatus) { + setPrevStatus(transactionReceipt?.status); + if (transactionReceipt?.status === 'success') { handleClose(); toast.success(`Successfully sent ${amount} ${currency.symbol}`); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [transactionReceipt]); + } const balance = tokenBalance?.toExact() || '0'; diff --git a/packages/ui/src/components/TokenInput/TokenInput.tsx b/packages/ui/src/components/TokenInput/TokenInput.tsx index 684da6f32..e9d33782d 100644 --- a/packages/ui/src/components/TokenInput/TokenInput.tsx +++ b/packages/ui/src/components/TokenInput/TokenInput.tsx @@ -45,6 +45,7 @@ const TokenInput = forwardRef((props, ref): J const defaultCurrency = useMemo(() => getDefaultCurrency(props), [props]); const [currency, setCurrency] = useState(defaultCurrency); + const [prevCurrency, setPrevCurrency] = useState(); const inputId = useId(); @@ -70,7 +71,9 @@ const TokenInput = forwardRef((props, ref): J } } - useEffect(() => { + if (prevCurrency !== currency) { + setPrevCurrency(currency); + if (value && currency) { const trimmedValue = trimDecimals(value, currency.decimals); @@ -79,8 +82,7 @@ const TokenInput = forwardRef((props, ref): J onValueChange?.(trimmedValue); } } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [currency]); + } const handleChange = useCallback( (e: ChangeEvent) => { From 2b332d4b30af392d52caf3fffd09222672c42a15 Mon Sep 17 00:00:00 2001 From: Slava Date: Fri, 15 Nov 2024 21:35:16 +0300 Subject: [PATCH 03/12] chore: reset with no use effect --- .../[lang]/fusion/components/UserInfo/UserInfo.tsx | 2 +- apps/evm/src/components/SpiceAmount/SpiceAmount.tsx | 11 ++--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/apps/evm/src/app/[lang]/fusion/components/UserInfo/UserInfo.tsx b/apps/evm/src/app/[lang]/fusion/components/UserInfo/UserInfo.tsx index 4788d753c..dcc820649 100644 --- a/apps/evm/src/app/[lang]/fusion/components/UserInfo/UserInfo.tsx +++ b/apps/evm/src/app/[lang]/fusion/components/UserInfo/UserInfo.tsx @@ -128,7 +128,7 @@ const UserInfo = ({ apps, user, quests, isAuthenticated }: UserInfoProps) => { Season 3 Harvested Spice - + (+{}/ diff --git a/apps/evm/src/components/SpiceAmount/SpiceAmount.tsx b/apps/evm/src/components/SpiceAmount/SpiceAmount.tsx index a08acacf0..e9c7fce4e 100644 --- a/apps/evm/src/components/SpiceAmount/SpiceAmount.tsx +++ b/apps/evm/src/components/SpiceAmount/SpiceAmount.tsx @@ -3,7 +3,7 @@ import { Flex, FlexProps, Span, SpanProps, useLocale } from '@gobob/ui'; import { Spice } from '@gobob/icons'; import { useCountUp } from 'use-count-up'; -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useState } from 'react'; type Props = { amount: number; @@ -27,7 +27,7 @@ const SpiceAmount = ({ amount, compact, hideIcon, gap = 'xs', showAnimation, ... [compact, locale] ); - const { value, reset } = useCountUp({ + const { value } = useCountUp({ isCounting: showAnimation, start, end: amount, @@ -35,13 +35,6 @@ const SpiceAmount = ({ amount, compact, hideIcon, gap = 'xs', showAnimation, ... onComplete: () => setStart(amount) }); - useEffect(() => { - if (showAnimation) { - reset(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [amount, showAnimation]); - return ( {!hideIcon && } From 77cdebbbe9eac4dca924b0e559873c594cc424f4 Mon Sep 17 00:00:00 2001 From: Slava Date: Mon, 18 Nov 2024 00:16:38 +0300 Subject: [PATCH 04/12] chore: code improvement --- .../components/TokenButtonGroup/TokenButtonGroup.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/bob-pay/src/app/[lang]/send/components/TokenButtonGroup/TokenButtonGroup.tsx b/apps/bob-pay/src/app/[lang]/send/components/TokenButtonGroup/TokenButtonGroup.tsx index d9092cb6e..0a0157045 100644 --- a/apps/bob-pay/src/app/[lang]/send/components/TokenButtonGroup/TokenButtonGroup.tsx +++ b/apps/bob-pay/src/app/[lang]/send/components/TokenButtonGroup/TokenButtonGroup.tsx @@ -27,11 +27,12 @@ const TokenButtonGroup = ({ isSelected, currency, balance, onSelectionChange }: const [key, setKey] = useState(); const [prevIsSelected, setPrevIsSelected] = useState(false); - if (!prevIsSelected && isSelected) setPrevIsSelected(true); + if (isSelected !== prevIsSelected) { + setPrevIsSelected(isSelected); - if (prevIsSelected && !isSelected) { - setKey(undefined); - setPrevIsSelected(false); + if (!isSelected) { + setKey(undefined); + } } const amounts = useMemo(() => { From 93ce5e95a9f44db49e9a8c30b89d3d67ee5e6ed8 Mon Sep 17 00:00:00 2001 From: Slava Date: Tue, 19 Nov 2024 01:06:23 +0300 Subject: [PATCH 05/12] chore: readability update --- .../send/components/TokenButtonGroup/TokenButtonGroup.tsx | 2 +- .../(bridge)/bridge/components/BridgeForm/BobBridgeForm.tsx | 6 +++--- apps/evm/src/app/[lang]/(bridge)/hooks/useGatewayForm.ts | 2 +- .../sign-up/components/ReferralInput/ReferralInput.tsx | 2 +- .../wallet/components/SendTokenModal/SendTokenModal.tsx | 2 +- packages/ui/src/components/TokenInput/TokenInput.tsx | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/bob-pay/src/app/[lang]/send/components/TokenButtonGroup/TokenButtonGroup.tsx b/apps/bob-pay/src/app/[lang]/send/components/TokenButtonGroup/TokenButtonGroup.tsx index 0a0157045..8f314ba48 100644 --- a/apps/bob-pay/src/app/[lang]/send/components/TokenButtonGroup/TokenButtonGroup.tsx +++ b/apps/bob-pay/src/app/[lang]/send/components/TokenButtonGroup/TokenButtonGroup.tsx @@ -27,7 +27,7 @@ const TokenButtonGroup = ({ isSelected, currency, balance, onSelectionChange }: const [key, setKey] = useState(); const [prevIsSelected, setPrevIsSelected] = useState(false); - if (isSelected !== prevIsSelected) { + if (prevIsSelected !== isSelected) { setPrevIsSelected(isSelected); if (!isSelected) { diff --git a/apps/evm/src/app/[lang]/(bridge)/bridge/components/BridgeForm/BobBridgeForm.tsx b/apps/evm/src/app/[lang]/(bridge)/bridge/components/BridgeForm/BobBridgeForm.tsx index 13d5a7a48..6f55c5a82 100644 --- a/apps/evm/src/app/[lang]/(bridge)/bridge/components/BridgeForm/BobBridgeForm.tsx +++ b/apps/evm/src/app/[lang]/(bridge)/bridge/components/BridgeForm/BobBridgeForm.tsx @@ -392,7 +392,7 @@ const BobBridgeForm = ({ const [prevChain, setPrevChain] = useState(chain); - if (chain !== prevChain) { + if (prevChain !== chain) { setPrevChain(chain); if (currencyAmount && selectedToken) { @@ -454,7 +454,7 @@ const BobBridgeForm = ({ const [prevAmount, setPrevAmount] = useState(amount); - if (amount !== prevAmount) { + if (prevAmount !== amount) { setPrevAmount(amount); if (amount) { @@ -471,7 +471,7 @@ const BobBridgeForm = ({ const [prevDirection, setPrevDirection] = useState(); - if (direction !== prevDirection) { + if (prevDirection !== direction) { setPrevDirection(direction); form.resetForm(); diff --git a/apps/evm/src/app/[lang]/(bridge)/hooks/useGatewayForm.ts b/apps/evm/src/app/[lang]/(bridge)/hooks/useGatewayForm.ts index f8b5e2231..cb369a2fd 100644 --- a/apps/evm/src/app/[lang]/(bridge)/hooks/useGatewayForm.ts +++ b/apps/evm/src/app/[lang]/(bridge)/hooks/useGatewayForm.ts @@ -56,7 +56,7 @@ const useGatewayForm = ({ query, defaultAsset, onSubmit }: UseGatewayFormProps) const [prevData, setPrevData] = useState(query.fee.rates.data); - if (query.fee.rates.data !== prevData) { + if (prevData !== query.fee.rates.data) { setPrevData(query.fee.rates.data); if (query.fee.estimate.data && form.values[BRIDGE_AMOUNT]) { diff --git a/apps/evm/src/app/[lang]/sign-up/components/ReferralInput/ReferralInput.tsx b/apps/evm/src/app/[lang]/sign-up/components/ReferralInput/ReferralInput.tsx index 159e1c5f6..0f24f8c60 100644 --- a/apps/evm/src/app/[lang]/sign-up/components/ReferralInput/ReferralInput.tsx +++ b/apps/evm/src/app/[lang]/sign-up/components/ReferralInput/ReferralInput.tsx @@ -38,7 +38,7 @@ const ReferralInput = ({ onChange, errorMessage, ...props }: ReferralInputProps) [onChange] ); - if (otp !== prevOtp) { + if (prevOtp !== otp) { setPrevOtp(otp); if (otp) handleChange(otp); diff --git a/apps/evm/src/app/[lang]/wallet/components/SendTokenModal/SendTokenModal.tsx b/apps/evm/src/app/[lang]/wallet/components/SendTokenModal/SendTokenModal.tsx index c42bfeced..e34233deb 100644 --- a/apps/evm/src/app/[lang]/wallet/components/SendTokenModal/SendTokenModal.tsx +++ b/apps/evm/src/app/[lang]/wallet/components/SendTokenModal/SendTokenModal.tsx @@ -194,7 +194,7 @@ const SendTokenModal = ({ token, onClose, ...props }: SendTokenModalProps): JSX. const [prevStatus, setPrevStatus] = useState(transactionReceipt?.status); - if (transactionReceipt?.status !== prevStatus) { + if (prevStatus !== transactionReceipt?.status) { setPrevStatus(transactionReceipt?.status); if (transactionReceipt?.status === 'success') { diff --git a/packages/ui/src/components/TokenInput/TokenInput.tsx b/packages/ui/src/components/TokenInput/TokenInput.tsx index e9d33782d..4712881f8 100644 --- a/packages/ui/src/components/TokenInput/TokenInput.tsx +++ b/packages/ui/src/components/TokenInput/TokenInput.tsx @@ -63,7 +63,7 @@ const TokenInput = forwardRef((props, ref): J props.type === 'selectable' ? [props.items, props.selectProps?.value, props.selectProps?.defaultValue] : [] ); - if (valueProp !== prevValueProp) { + if (prevValueProp !== valueProp) { setPrevValueProp(valueProp); if (valueProp !== undefined) { From e14f8a49dec8aaaa42891577a72f2c0ebe1f396f Mon Sep 17 00:00:00 2001 From: Slava Date: Tue, 19 Nov 2024 01:57:20 +0300 Subject: [PATCH 06/12] chore: hero banner to nextjs image --- .../components/HeroBanner/HeroBanner.style.tsx | 16 +--------------- .../apps/components/HeroBanner/HeroBanner.tsx | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/apps/evm/src/app/[lang]/apps/components/HeroBanner/HeroBanner.style.tsx b/apps/evm/src/app/[lang]/apps/components/HeroBanner/HeroBanner.style.tsx index 12d8948f7..6780cdf2b 100644 --- a/apps/evm/src/app/[lang]/apps/components/HeroBanner/HeroBanner.style.tsx +++ b/apps/evm/src/app/[lang]/apps/components/HeroBanner/HeroBanner.style.tsx @@ -1,22 +1,8 @@ import { Card, Flex, P, UnstyledButton } from '@gobob/ui'; -import styled, { css } from 'styled-components'; +import styled from 'styled-components'; const StyledCard = styled(Card)` position: relative; - - background-image: url(/assets/apps-leaderboard-hero.png); - background-repeat: no-repeat; - background-size: cover; - - ${({ theme }) => { - return css` - background-position: 55% 50%; - - @media ${theme.breakpoints.up('md')} { - background-position: 50% 70%; - } - `; - }} `; const StyledOpacityOverlay = styled.div` diff --git a/apps/evm/src/app/[lang]/apps/components/HeroBanner/HeroBanner.tsx b/apps/evm/src/app/[lang]/apps/components/HeroBanner/HeroBanner.tsx index 69fc29955..56ec9dce6 100644 --- a/apps/evm/src/app/[lang]/apps/components/HeroBanner/HeroBanner.tsx +++ b/apps/evm/src/app/[lang]/apps/components/HeroBanner/HeroBanner.tsx @@ -2,7 +2,10 @@ import { H1 } from '@gobob/ui'; import { useState } from 'react'; -import { Trans } from '@lingui/macro'; +import { t, Trans } from '@lingui/macro'; +import Image from 'next/image'; +import { useLingui } from '@lingui/react'; +import heroBanner from '@public/assets/apps-leaderboard-hero.png'; import { VotingInfoModal } from './VotingInfoModal'; import { @@ -15,9 +18,22 @@ import { const HeroBanner = (): JSX.Element => { const [isOpen, setOpen] = useState(false); + const { i18n } = useLingui(); return ( + {t(i18n)`Hero Date: Tue, 19 Nov 2024 02:23:37 +0300 Subject: [PATCH 07/12] chore: improve image loading --- .../CommunityVoting/CommunityVoting.style.tsx | 4 ---- .../CommunityVoting/CommunityVoting.tsx | 17 ++++++++++++++++- .../fusion/components/Quest/Quest.style.tsx | 4 ---- .../[lang]/fusion/components/Quest/Quest.tsx | 18 +++++++++++++++++- .../components/Strategies/Strategies.style.tsx | 4 ---- .../components/Strategies/StrategyCard.tsx | 17 ++++++++++++++++- 6 files changed, 49 insertions(+), 15 deletions(-) diff --git a/apps/evm/src/app/[lang]/fusion/components/CommunityVoting/CommunityVoting.style.tsx b/apps/evm/src/app/[lang]/fusion/components/CommunityVoting/CommunityVoting.style.tsx index 54a147ba5..a0c222c01 100644 --- a/apps/evm/src/app/[lang]/fusion/components/CommunityVoting/CommunityVoting.style.tsx +++ b/apps/evm/src/app/[lang]/fusion/components/CommunityVoting/CommunityVoting.style.tsx @@ -4,10 +4,6 @@ import styled from 'styled-components'; const StyledCard = styled(Card)` position: relative; - background-image: url(/assets/apps-leaderboard-hero.png); - background-repeat: no-repeat; - background-size: cover; - background-position: 50% 50%; min-height: 260px; `; diff --git a/apps/evm/src/app/[lang]/fusion/components/CommunityVoting/CommunityVoting.tsx b/apps/evm/src/app/[lang]/fusion/components/CommunityVoting/CommunityVoting.tsx index ee9b58656..d21b8fec3 100644 --- a/apps/evm/src/app/[lang]/fusion/components/CommunityVoting/CommunityVoting.tsx +++ b/apps/evm/src/app/[lang]/fusion/components/CommunityVoting/CommunityVoting.tsx @@ -1,8 +1,11 @@ import { Button, Chip, Flex, H2, Link, P, Skeleton, SolidClock, useMediaQuery } from '@gobob/ui'; import { formatDistanceToNow } from 'date-fns'; import { useTheme } from 'styled-components'; -import { Trans } from '@lingui/macro'; +import { t, Trans } from '@lingui/macro'; import { useIsClient } from 'usehooks-ts'; +import Image from 'next/image'; +import appsLeaderboardHero from '@public/assets/apps-leaderboard-hero.png'; +import { useLingui } from '@lingui/react'; import { StyledCard, StyledOpacityOverlay, StyledSpice } from './CommunityVoting.style'; @@ -16,11 +19,23 @@ const CommunityVoting = ({}: CommunityVotingProps) => { const isClient = useIsClient(); const isMobile = useMediaQuery(theme.breakpoints.down('s')); const { data: votingAppsData } = useGetVotingApps(); + const { i18n } = useLingui(); return ( {isClient && !isMobile && ( + {t(i18n)`Apps diff --git a/apps/evm/src/app/[lang]/fusion/components/Quest/Quest.style.tsx b/apps/evm/src/app/[lang]/fusion/components/Quest/Quest.style.tsx index e06141fe4..b292fc969 100644 --- a/apps/evm/src/app/[lang]/fusion/components/Quest/Quest.style.tsx +++ b/apps/evm/src/app/[lang]/fusion/components/Quest/Quest.style.tsx @@ -4,10 +4,6 @@ import styled from 'styled-components'; const StyledCard = styled(Card)` position: relative; - background-image: url(/assets/welcome-season-3.jpg); - background-repeat: no-repeat; - background-size: cover; - background-position: 50% 50%; min-height: 260px; `; diff --git a/apps/evm/src/app/[lang]/fusion/components/Quest/Quest.tsx b/apps/evm/src/app/[lang]/fusion/components/Quest/Quest.tsx index d9c79fa07..3dc0346bb 100644 --- a/apps/evm/src/app/[lang]/fusion/components/Quest/Quest.tsx +++ b/apps/evm/src/app/[lang]/fusion/components/Quest/Quest.tsx @@ -1,8 +1,11 @@ import { Button, Chip, Flex, H2, Link, Skeleton, SolidClock, useMediaQuery } from '@gobob/ui'; import { formatDistanceToNow } from 'date-fns'; import { useTheme } from 'styled-components'; -import { Trans } from '@lingui/macro'; +import { t, Trans } from '@lingui/macro'; import { useIsClient } from 'usehooks-ts'; +import Image from 'next/image'; +import { useLingui } from '@lingui/react'; +import welcomeSeason3 from '@public/assets/welcome-season-3.jpg'; import { StyledCard, StyledDescription, StyledIntract, StyledOpacityOverlay } from './Quest.style'; @@ -13,7 +16,9 @@ type QuestProps = { quests: QuestS3Response | undefined; id: string }; const Quest = ({ id, quests }: QuestProps) => { const theme = useTheme(); const isClient = useIsClient(); + const isMobile = useMediaQuery(theme.breakpoints.down('s')); + const { i18n } = useLingui(); const [intractQuest] = quests?.questBreakdown || []; @@ -23,6 +28,17 @@ const Quest = ({ id, quests }: QuestProps) => { {isClient && !isMobile && ( + {t(i18n)`Welcome diff --git a/apps/evm/src/app/[lang]/fusion/components/Strategies/Strategies.style.tsx b/apps/evm/src/app/[lang]/fusion/components/Strategies/Strategies.style.tsx index 405e4edae..9eff4e923 100644 --- a/apps/evm/src/app/[lang]/fusion/components/Strategies/Strategies.style.tsx +++ b/apps/evm/src/app/[lang]/fusion/components/Strategies/Strategies.style.tsx @@ -15,10 +15,6 @@ const StyledBannerWrapper = styled(Flex)` const StyledBanner = styled(Flex)` position: absolute; inset: 0; - background-image: url(/assets/spice-shape-background.jpg); - background-repeat: no-repeat; - background-size: cover; - background-position: 50% 50%; `; const StyledCard = styled(Card)` diff --git a/apps/evm/src/app/[lang]/fusion/components/Strategies/StrategyCard.tsx b/apps/evm/src/app/[lang]/fusion/components/Strategies/StrategyCard.tsx index 369a27921..107011153 100644 --- a/apps/evm/src/app/[lang]/fusion/components/Strategies/StrategyCard.tsx +++ b/apps/evm/src/app/[lang]/fusion/components/Strategies/StrategyCard.tsx @@ -1,7 +1,10 @@ import { Flex, H3, Modal, ModalBody, ModalHeader, P, SolidGift, Span } from '@gobob/ui'; import { ReactNode, useId, useState } from 'react'; import { Spice } from '@gobob/icons'; -import { Trans } from '@lingui/macro'; +import { t, Trans } from '@lingui/macro'; +import Image from 'next/image'; +import { useLingui } from '@lingui/react'; +import spiceShapedBackground from '@public/assets/spice-shape-background.jpg'; import { StyledBanner, StyledBannerWrapper, StyledCard, StyledTitle } from './Strategies.style'; @@ -24,6 +27,7 @@ type StrategyCardProps = Props; const StrategyCard = ({ title, longDescription, rewards, shortDescription, steps, isDisabled }: StrategyCardProps) => { const [isOpen, setOpen] = useState(false); + const { i18n } = useLingui(); const stepId = useId(); const rewardsId = useId(); @@ -40,6 +44,17 @@ const StrategyCard = ({ title, longDescription, rewards, shortDescription, steps > + {t(i18n)`Spice From 3febd4c47f1b99da71abc66a92eebe6a20e98f9a Mon Sep 17 00:00:00 2001 From: Slava Date: Tue, 19 Nov 2024 13:25:37 +0300 Subject: [PATCH 08/12] chore: fix typescript type --- apps/bob-pay/src/app/[lang]/send/Send.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/bob-pay/src/app/[lang]/send/Send.tsx b/apps/bob-pay/src/app/[lang]/send/Send.tsx index 551ccfc63..c10d76ea1 100644 --- a/apps/bob-pay/src/app/[lang]/send/Send.tsx +++ b/apps/bob-pay/src/app/[lang]/send/Send.tsx @@ -203,8 +203,8 @@ const Send = ({ ticker: tickerProp = 'WBTC', recipient }: SendProps): JSX.Elemen hash: eoaTransferTx, query: { meta: { - onSuccess: (data: { status: 'success' }) => { - if (data?.status === 'success') { + onSuccess: (data) => { + if ((data as { status: 'success' } | undefined)?.status === 'success') { toast.success(t(i18n)`Successfully sent ${amount} ${token?.currency.symbol}`); form.resetForm(); From aa649783f5445755056921d75b636381c2932fb4 Mon Sep 17 00:00:00 2001 From: Slava Date: Tue, 19 Nov 2024 13:34:03 +0300 Subject: [PATCH 09/12] fix: update code --- packages/ui/src/components/NumberInput/NumberInput.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/ui/src/components/NumberInput/NumberInput.tsx b/packages/ui/src/components/NumberInput/NumberInput.tsx index dbd0cf72a..6fb9518fc 100644 --- a/packages/ui/src/components/NumberInput/NumberInput.tsx +++ b/packages/ui/src/components/NumberInput/NumberInput.tsx @@ -44,7 +44,6 @@ const NumberInput = forwardRef( ref ): JSX.Element => { const [value, setValue] = useState(defaultValue?.toString()); - const [prevValue, setPrevValue] = useState(value); const inputRef = useDOMRef(ref); const handleChange: ChangeEventHandler = (e) => { @@ -71,8 +70,7 @@ const NumberInput = forwardRef( inputRef ); - if (prevValue !== valueProp) { - setPrevValue(value); + if (value !== valueProp?.toString()) { if (valueProp !== undefined) setValue(valueProp.toString()); } From 561fd64905e63ff13330530473d4ff6f5b5a2dd2 Mon Sep 17 00:00:00 2001 From: Slava Date: Tue, 19 Nov 2024 15:03:20 +0300 Subject: [PATCH 10/12] chore: fix failing tests --- .../ui/src/components/TokenInput/TokenInput.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/ui/src/components/TokenInput/TokenInput.tsx b/packages/ui/src/components/TokenInput/TokenInput.tsx index 4712881f8..985fbee37 100644 --- a/packages/ui/src/components/TokenInput/TokenInput.tsx +++ b/packages/ui/src/components/TokenInput/TokenInput.tsx @@ -41,11 +41,10 @@ const TokenInput = forwardRef((props, ref): J const inputRef = useDOMRef(ref); const [value, setValue] = useState(defaultValue); - const [prevValueProp, setPrevValueProp] = useState(valueProp); const defaultCurrency = useMemo(() => getDefaultCurrency(props), [props]); const [currency, setCurrency] = useState(defaultCurrency); - const [prevCurrency, setPrevCurrency] = useState(); + const [prevCurrency, setPrevCurrency] = useState(currency); const inputId = useId(); @@ -63,13 +62,11 @@ const TokenInput = forwardRef((props, ref): J props.type === 'selectable' ? [props.items, props.selectProps?.value, props.selectProps?.defaultValue] : [] ); - if (prevValueProp !== valueProp) { - setPrevValueProp(valueProp); + useEffect(() => { + if (valueProp === undefined) return; - if (valueProp !== undefined) { - setValue(valueProp); - } - } + setValue(valueProp); + }, [valueProp]); if (prevCurrency !== currency) { setPrevCurrency(currency); From eff534b79e205aaaec12cde8d61ad97651182c05 Mon Sep 17 00:00:00 2001 From: Slava Date: Thu, 28 Nov 2024 22:42:19 +0300 Subject: [PATCH 11/12] chore: update type defs --- apps/bob-pay/src/lib/react-query/react-query.d.ts | 6 +++--- apps/evm/src/lib/react-query/react-query.d.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/bob-pay/src/lib/react-query/react-query.d.ts b/apps/bob-pay/src/lib/react-query/react-query.d.ts index 7c7cdc83b..5eca5fc4d 100644 --- a/apps/bob-pay/src/lib/react-query/react-query.d.ts +++ b/apps/bob-pay/src/lib/react-query/react-query.d.ts @@ -1,5 +1,5 @@ -import '@gobob/react-query'; -import { DefaultError, Query } from '@gobob/react-query'; +import '@tanstack/react-query'; +import { DefaultError, Query } from '@tanstack/react-query'; // https://tanstack.com/query/latest/docs/framework/react/typescript#typing-meta interface CustomQueryMeta extends Record { @@ -14,7 +14,7 @@ interface CustomQueryMeta extends Record { queryMeta: CustomQueryMeta; } diff --git a/apps/evm/src/lib/react-query/react-query.d.ts b/apps/evm/src/lib/react-query/react-query.d.ts index 7c7cdc83b..5eca5fc4d 100644 --- a/apps/evm/src/lib/react-query/react-query.d.ts +++ b/apps/evm/src/lib/react-query/react-query.d.ts @@ -1,5 +1,5 @@ -import '@gobob/react-query'; -import { DefaultError, Query } from '@gobob/react-query'; +import '@tanstack/react-query'; +import { DefaultError, Query } from '@tanstack/react-query'; // https://tanstack.com/query/latest/docs/framework/react/typescript#typing-meta interface CustomQueryMeta extends Record { @@ -14,7 +14,7 @@ interface CustomQueryMeta extends Record { queryMeta: CustomQueryMeta; } From 2e305394e2675be9fdbe6be790fb0c8edd2c8a3b Mon Sep 17 00:00:00 2001 From: Slava Date: Thu, 28 Nov 2024 22:50:07 +0300 Subject: [PATCH 12/12] chore: remove react-query project package --- packages/react-query/react-query.d.ts | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 packages/react-query/react-query.d.ts diff --git a/packages/react-query/react-query.d.ts b/packages/react-query/react-query.d.ts deleted file mode 100644 index 5eca5fc4d..000000000 --- a/packages/react-query/react-query.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -import '@tanstack/react-query'; -import { DefaultError, Query } from '@tanstack/react-query'; - -// https://tanstack.com/query/latest/docs/framework/react/typescript#typing-meta -interface CustomQueryMeta extends Record { - onSuccess?: ((data: TQueryFnData, query: Query) => void) | undefined; - onError?: ((error: DefaultError, query: Query) => void) | undefined; - onSettled?: - | (( - data: TQueryFnData | undefined, - error: DefaultError | null, - query: Query - ) => void) - | undefined; -} - -declare module '@tanstack/react-query' { - interface Register extends Record { - queryMeta: CustomQueryMeta; - } -}