diff --git a/src/components/asset-list/RecyclerAssetList2/core/RawRecyclerList.tsx b/src/components/asset-list/RecyclerAssetList2/core/RawRecyclerList.tsx index c68667a4f87..ffdfcf3cc6a 100644 --- a/src/components/asset-list/RecyclerAssetList2/core/RawRecyclerList.tsx +++ b/src/components/asset-list/RecyclerAssetList2/core/RawRecyclerList.tsx @@ -1,4 +1,4 @@ -import React, { LegacyRef, useCallback, useContext, useEffect, useMemo, useRef } from 'react'; +import React, { LegacyRef, useCallback, useEffect, useMemo, useRef } from 'react'; import { LayoutChangeEvent } from 'react-native'; import { SetterOrUpdater } from 'recoil'; import { DataProvider, RecyclerListView } from 'recyclerlistview'; @@ -22,7 +22,7 @@ import { remoteCardsStore } from '@/state/remoteCards/remoteCards'; import { useRoute } from '@react-navigation/native'; import Routes from '@/navigation/routesNames'; import { useRemoteConfig } from '@/model/remoteConfig'; -import { RainbowContext } from '@/helpers/RainbowContext'; +import { useExperimentalConfig } from '@/config/experimentalHooks'; const dataProvider = new DataProvider((r1, r2) => { return r1.uid !== r2.uid; @@ -57,7 +57,7 @@ const RawMemoRecyclerAssetList = React.memo(function RawRecyclerAssetList({ type?: AssetListType; }) { const remoteConfig = useRemoteConfig(); - const experimentalConfig = useContext(RainbowContext).config; + const experimentalConfig = useExperimentalConfig(); const currentDataProvider = useMemoOne(() => dataProvider.cloneWithRows(briefSectionsData), [briefSectionsData]); const { isCoinListEdited, setIsCoinListEdited } = useCoinListEdited(); const y = useRecyclerAssetListPosition()!; diff --git a/src/config/experimental.ts b/src/config/experimental.ts index 6aa5824dbdb..2e563439ed1 100644 --- a/src/config/experimental.ts +++ b/src/config/experimental.ts @@ -72,6 +72,10 @@ export const defaultConfig: Record = { [NFTS_ENABLED]: { settings: true, value: !!IS_TEST }, }; +export const defaultConfigValues: Record = Object.fromEntries( + Object.entries(defaultConfig).map(([key, { value }]) => [key, value]) +); + const storageKey = 'config'; const storage = new MMKV({ diff --git a/src/config/experimentalHooks.ts b/src/config/experimentalHooks.ts index f99fa930c37..e8f9bec81d1 100644 --- a/src/config/experimentalHooks.ts +++ b/src/config/experimentalHooks.ts @@ -1,5 +1,5 @@ import { useContext } from 'react'; -import { defaultConfig } from './experimental'; +import { defaultConfig, defaultConfigValues } from './experimental'; import { RainbowContext } from '@/helpers/RainbowContext'; import { IS_DEV } from '@/env'; import isTestFlight from '@/helpers/isTestFlight'; @@ -12,6 +12,16 @@ const useExperimentalFlag = (name: any) => { return defaultConfig[name].value; } }; + +export const useExperimentalConfig = () => { + if (IS_DEV || isTestFlight) { + // eslint-disable-next-line react-hooks/rules-of-hooks + return useContext(RainbowContext).config; + } else { + return defaultConfigValues; + } +}; + export default useExperimentalFlag; export * from './experimental'; diff --git a/src/hooks/useWalletSectionsData.ts b/src/hooks/useWalletSectionsData.ts index 91e7298151e..abd0653cb88 100644 --- a/src/hooks/useWalletSectionsData.ts +++ b/src/hooks/useWalletSectionsData.ts @@ -1,4 +1,4 @@ -import { useContext, useMemo } from 'react'; +import { useMemo } from 'react'; import useAccountSettings from './useAccountSettings'; import useCoinListEditOptions from './useCoinListEditOptions'; import useCoinListEdited from './useCoinListEdited'; @@ -13,9 +13,9 @@ import { useLegacyNFTs } from '@/resources/nfts'; import useNftSort from './useNFTsSortBy'; import useWalletsWithBalancesAndNames from './useWalletsWithBalancesAndNames'; import { useRemoteConfig } from '@/model/remoteConfig'; -import { RainbowContext } from '@/helpers/RainbowContext'; import { usePositions } from '@/resources/defi/PositionsQuery'; import { useClaimables } from '@/resources/addys/claimables/query'; +import { useExperimentalConfig } from '@/config/experimentalHooks'; export default function useWalletSectionsData({ type, @@ -50,7 +50,7 @@ export default function useWalletSectionsData({ const { hiddenTokens } = useHiddenTokens(); const remoteConfig = useRemoteConfig(); - const experimentalConfig = useContext(RainbowContext).config; + const experimentalConfig = useExperimentalConfig(); const { hiddenCoinsObj: hiddenCoins, pinnedCoinsObj: pinnedCoins } = useCoinListEditOptions(); diff --git a/src/resources/addys/claimables/query.ts b/src/resources/addys/claimables/query.ts index c1db04818bd..8ff0a26544b 100644 --- a/src/resources/addys/claimables/query.ts +++ b/src/resources/addys/claimables/query.ts @@ -79,6 +79,7 @@ export function useClaimables( ...config, enabled: !!address && (remoteFlag || localFlag) && !IS_TEST, staleTime: 1000 * 60 * 2, + refetchInterval: 1000 * 60 * 2, cacheTime: 1000 * 60 * 60 * 24, }); } diff --git a/src/screens/claimables/ClaimingClaimableSharedUI.tsx b/src/screens/claimables/ClaimingClaimableSharedUI.tsx index a800405e838..a788f349939 100644 --- a/src/screens/claimables/ClaimingClaimableSharedUI.tsx +++ b/src/screens/claimables/ClaimingClaimableSharedUI.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useMemo } from 'react'; import { AccentColorProvider, Bleed, Box, Inline, Text, TextShadow, globalColors, useColorMode } from '@/design-system'; import * as i18n from '@/languages'; import { ListHeader, Panel, TapToDismiss, controlPanelStyles } from '@/components/SmoothPager/ListPanel'; -import { deviceUtils, haptics, safeAreaInsetValues, watchingAlert } from '@/utils'; +import { deviceUtils, safeAreaInsetValues, watchingAlert } from '@/utils'; import { View } from 'react-native'; import { IS_IOS } from '@/env'; import { ButtonPressAnimation, ShimmerAnimation } from '@/components/animations'; @@ -14,7 +14,7 @@ import { chainsLabel } from '@/chains'; import { useNavigation } from '@/navigation'; import { TextColor } from '@/design-system/color/palettes'; import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'; -import { convertAmountToNativeDisplayWorklet, handleSignificantDecimalsWithThreshold } from '@/__swaps__/utils/numbers'; +import { convertAmountToNativeDisplayWorklet } from '@/__swaps__/utils/numbers'; import { useAccountSettings, useWallets } from '@/hooks'; import { enableActionsOnReadOnlyWallet } from '@/config'; import { debounce } from 'lodash'; diff --git a/src/screens/claimables/ClaimingSponsoredClaimable.tsx b/src/screens/claimables/ClaimingSponsoredClaimable.tsx index 81aff49c5ce..7ac9e840191 100644 --- a/src/screens/claimables/ClaimingSponsoredClaimable.tsx +++ b/src/screens/claimables/ClaimingSponsoredClaimable.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { ClaimResponse, SponsoredClaimable } from '@/resources/addys/claimables/types'; +import { Claimable, ClaimResponse, SponsoredClaimable } from '@/resources/addys/claimables/types'; import { ClaimingClaimableSharedUI, ClaimStatus } from './ClaimingClaimableSharedUI'; import { logger, RainbowError } from '@/logger'; import { queryClient } from '@/react-query'; @@ -15,6 +15,8 @@ export const ClaimingSponsoredClaimable = ({ claimable }: { claimable: Sponsored const [claimStatus, setClaimStatus] = useState('idle'); + const queryKey = claimablesQueryKey({ address: accountAddress, currency: nativeCurrency }); + const { mutate: claimClaimable } = useMutation({ mutationFn: async () => { const provider = getProvider({ chainId: claimable.chainId }); @@ -59,15 +61,16 @@ export const ClaimingSponsoredClaimable = ({ claimable }: { claimable: Sponsored setClaimStatus('error'); logger.error(new RainbowError('[ClaimSponsoredClaimable]: sponsored claim api call returned unsuccessful response')); } else { + haptics.notificationSuccess(); + if (response.data.payload.claim_transaction_status?.transaction_hash) { - haptics.notificationSuccess(); setClaimStatus('success'); } else { - haptics.notificationSuccess(); setClaimStatus('pending'); } - // Clear and refresh claimables data - queryClient.invalidateQueries(claimablesQueryKey({ address: accountAddress, currency: nativeCurrency })); + + // Immediately remove the claimable from cached data + queryClient.setQueryData(queryKey, (oldData: Claimable[] | undefined) => oldData?.filter(c => c.uniqueId !== claimable.uniqueId)); } }, onError: e => { @@ -86,6 +89,10 @@ export const ClaimingSponsoredClaimable = ({ claimable }: { claimable: Sponsored ); } }, + onSettled: () => { + // Clear and refresh claimables data 20s after claim button is pressed, regardless of success or failure + setTimeout(() => queryClient.invalidateQueries(queryKey), 20_000); + }, }); return ( diff --git a/src/screens/claimables/ClaimingTransactionClaimable.tsx b/src/screens/claimables/ClaimingTransactionClaimable.tsx index 2a838619fd8..1a9c5a14a66 100644 --- a/src/screens/claimables/ClaimingTransactionClaimable.tsx +++ b/src/screens/claimables/ClaimingTransactionClaimable.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useAccountSettings, useGas } from '@/hooks'; import { ethereumUtils, haptics } from '@/utils'; -import { TransactionClaimable } from '@/resources/addys/claimables/types'; +import { Claimable, TransactionClaimable } from '@/resources/addys/claimables/types'; import { estimateGasWithPadding, getProvider } from '@/handlers/web3'; import { parseGasParamsForTransaction } from '@/parsers'; import { getNextNonce } from '@/state/nonces'; @@ -52,6 +52,8 @@ export const ClaimingTransactionClaimable = ({ claimable }: { claimable: Transac const [txPayload, setTxPayload] = useState(); const [claimStatus, setClaimStatus] = useState('idle'); + const queryKey = claimablesQueryKey({ address: accountAddress, currency: nativeCurrency }); + const provider = useMemo(() => getProvider({ chainId: claimable.chainId }), [claimable.chainId]); const buildTxPayload = useCallback(async () => { @@ -174,8 +176,9 @@ export const ClaimingTransactionClaimable = ({ claimable }: { claimable: Transac } else { haptics.notificationSuccess(); setClaimStatus('success'); - // Clear and refresh claimables data - queryClient.invalidateQueries(claimablesQueryKey({ address: accountAddress, currency: nativeCurrency })); + + // Immediately remove the claimable from cached data + queryClient.setQueryData(queryKey, (oldData: Claimable[] | undefined) => oldData?.filter(c => c.uniqueId !== claimable.uniqueId)); } }, onError: e => { @@ -194,6 +197,10 @@ export const ClaimingTransactionClaimable = ({ claimable }: { claimable: Transac ); } }, + onSettled: () => { + // Clear and refresh claimables data 20s after claim button is pressed, regardless of success or failure + setTimeout(() => queryClient.invalidateQueries(queryKey), 20_000); + }, }); return (