From 6fa6bf54b4be1937e99c0915e77be6cc0aa6fef3 Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Fri, 27 Oct 2023 15:35:43 +0300 Subject: [PATCH 1/9] TW-737 Use TZKT for getting a delegate if possible --- src/lib/apis/tzkt/api.ts | 12 +- src/lib/apis/tzkt/index.ts | 3 + src/lib/apis/tzkt/types.ts | 268 ++++++++++++++++++++++++++ src/lib/temple/front/baking.ts | 29 ++- src/lib/temple/front/blockexplorer.ts | 20 +- src/lib/temple/networks.ts | 5 +- src/lib/temple/types.ts | 5 +- 7 files changed, 315 insertions(+), 27 deletions(-) diff --git a/src/lib/apis/tzkt/api.ts b/src/lib/apis/tzkt/api.ts index 40a632c44..aba23fefe 100644 --- a/src/lib/apis/tzkt/api.ts +++ b/src/lib/apis/tzkt/api.ts @@ -11,16 +11,17 @@ import { allInt32ParameterKeys, TzktGetRewardsParams, TzktGetRewardsResponse, - TzktRelatedContract + TzktRelatedContract, + TzktAccount } from './types'; const TZKT_API_BASE_URLS = { [TempleChainId.Mainnet]: 'https://api.tzkt.io/v1', - [TempleChainId.Jakartanet]: 'https://api.jakartanet.tzkt.io/v1', - [TempleChainId.Limanet]: 'https://api.limanet.tzkt.io/v1', + [TempleChainId.Mumbai]: 'https://api.mumbainet.tzkt.io/v1', + [TempleChainId.Nairobi]: 'https://api.nairobinet.tzkt.io/v1', [TempleChainId.Ghostnet]: 'https://api.ghostnet.tzkt.io/v1', [TempleChainId.Dcp]: 'https://explorer-api.tlnt.net/v1', - [TempleChainId.DcpTest]: 'https://explorer.tlnt.net:8009/v1' + [TempleChainId.DcpTest]: 'https://explorer-api.test.tlnt.net/v1' }; export type TzktApiChainId = keyof typeof TZKT_API_BASE_URLS; @@ -189,3 +190,6 @@ export const fetchAllTokensBalancesFromTzkt = async (selectedRpcUrl: string, acc return balances; }; + +export const getAccountStatsFromTzkt = async (account: string, chainId: string) => + isKnownChainId(chainId) ? await fetchGet(chainId, `/accounts/${account}`) : null; diff --git a/src/lib/apis/tzkt/index.ts b/src/lib/apis/tzkt/index.ts index 52ec9fe1e..8dee3bd0a 100644 --- a/src/lib/apis/tzkt/index.ts +++ b/src/lib/apis/tzkt/index.ts @@ -9,9 +9,12 @@ export type { TzktAccountToken } from './types'; +export { TzktAccountType } from './types'; + export type { TzktApiChainId } from './api'; export { isKnownChainId, + getAccountStatsFromTzkt, getDelegatorRewards, getOneUserContracts, fetchTzktTokens, diff --git a/src/lib/apis/tzkt/types.ts b/src/lib/apis/tzkt/types.ts index d20e282dd..cb4ed268a 100644 --- a/src/lib/apis/tzkt/types.ts +++ b/src/lib/apis/tzkt/types.ts @@ -221,3 +221,271 @@ export interface TzktTokenTransfer { }; transactionId: number; } + +export enum TzktAccountType { + User = 'user', + Delegate = 'delegate', + Contract = 'contract', + Ghost = 'ghost', + Rollup = 'rollup', + SmartRollup = 'smart_rollup', + Empty = 'empty' +} + +interface TzktAccountBase { + type: TzktAccountType; + address: string; + alias: string | nullish; +} + +export interface TzktUserAccount extends TzktAccountBase { + type: TzktAccountType.User; + id: number; + publicKey: string; + revealed: boolean; + balance: number; + rollupBonds: number; + smartRollupBonds: number; + counter: number; + delegate: TzktAlias | nullish; + delegationLevel: number; + delegationTime: string | nullish; + numContracts: number; + rollupsCount: number; + smartRollupsCount: number; + activeTokensCount: number; + tokenBalancesCount: number; + tokenTransfersCount: number; + numActivations: number; + numDelegations: number; + numOriginations: number; + numTransactions: number; + numReveals: number; + numRegisterConstants: number; + numSetDepositsLimits: number; + numMigrations: number; + txRollupOriginationCount: number; + txRollupSubmitBatchCount: number; + txRollupCommitCount: number; + txRollupReturnBondCount: number; + txRollupFinalizeCommitmentCount: number; + txRollupRemoveCommitmentCount: number; + txRollupRejectionCount: number; + txRollupDispatchTicketsCount: number; + transferTicketCount: number; + increasePaidStorageCount: number; + drainDelegateCount: number; + smartRollupAddMessagesCount: number; + smartRollupCementCount: number; + smartRollupExecuteCount: number; + smartRollupOriginateCount: number; + smartRollupPublishCount: number; + smartRollupRecoverBondCount: number; + smartRollupRefuteCount: number; + refutationGamesCount: number; + activeRefutationGamesCount: number; + firstActivity: number | nullish; + firstActivityTime: string | nullish; + lastActivity: number | nullish; + lastActivityTime: string | nullish; +} + +export interface TzktDelegateAccount extends TzktAccountBase { + type: TzktAccountType.Delegate; + id: number; + active: boolean; + publicKey: string | nullish; + revealed: boolean; + balance: number; + rollupBonds: number; + smartRollupBonds: number; + frozenDeposit: number; + frozenDepositLimit: number | nullish; + counter: number; + activationLevel: number; + activationTime: string; + deactivationLevel: number | nullish; + deactivationTime: string | nullish; + stakingBalance: number; + delegatedBalance: number; + numContracts: number; + rollupsCount: number; + smartRollupsCount: number; + activeTokensCount: number; + tokenBalancesCount: number; + tokenTransfersCount: number; + numDelegators: number; + numBlocks: number; + numEndorsements: number; + numPreendorsements: number; + numBallots: number; + numProposals: number; + numActivations: number; + numDoubleBaking: number; + numDoubleEndorsing: number; + numDoublePreendorsing: number; + numNonceRevelations: number; + vdfRevelationsCount: number; + numRevelationPenalties: number; + numEndorsingRewards: number; + numDelegations: number; + numOriginations: number; + numTransactions: number; + numReveals: number; + numRegisterConstants: number; + numSetDepositsLimits: number; + numMigrations: number; + txRollupOriginationCount: number; + txRollupSubmitBatchCount: number; + txRollupCommitCount: number; + txRollupReturnBondCount: number; + txRollupFinalizeCommitmentCount: number; + txRollupRemoveCommitmentCount: number; + txRollupRejectionCount: number; + txRollupDispatchTicketsCount: number; + transferTicketCount: number; + increasePaidStorageCount: number; + updateConsensusKeyCount: number; + drainDelegateCount: number; + smartRollupAddMessagesCount: number; + smartRollupCementCount: number; + smartRollupExecuteCount: number; + smartRollupOriginateCount: number; + smartRollupPublishCount: number; + smartRollupRecoverBondCount: number; + smartRollupRefuteCount: number; + refutationGamesCount: number; + activeRefutationGamesCount: number; + firstActivity: number; + firstActivityTime: string | nullish; + lastActivity: number; + lastActivityTime: string | nullish; + extras: unknown; + software: { date: string; version: string | nullish }; +} + +export interface TzktContractAccount extends TzktAccountBase { + type: TzktAccountType.Contract; + id: number; + kind: 'delegator_contract' | 'smart_contract' | nullish; + tzips: string[] | nullish; + balance: number; + creator: TzktAlias | nullish; + manager: TzktAlias | nullish; + delegate: TzktAlias | nullish; + delegationLevel: number | nullish; + delegationTime: string | nullish; + numContracts: number; + activeTokensCount: number; + tokensCount: number; + tokenBalancesCount: number; + tokenTransfersCount: number; + numDelegations: number; + numOriginations: number; + numTransactions: number; + numReveals: number; + numMigrations: number; + transferTicketCount: number; + increasePaidStorageCount: number; + eventsCount: number; + firstActivity: number; + firstActivityTime: string; + lastActivity: number; + lastActivityTime: string; + typeHash: number; + codeHash: number; + /** TZIP-16 metadata (with ?legacy=true this field will contain tzkt profile info). */ + metadata: unknown; + extras: unknown; + /** Contract storage value. Omitted by default. Use ?includeStorage=true to include it into response. */ + storage: unknown; +} + +export interface TzktGhostAccount extends TzktAccountBase { + type: TzktAccountType.Ghost; + id: number; + activeTokensCount: number; + tokenBalancesCount: number; + tokenTransfersCount: number; + firstActivity: number; + firstActivityTime: string; + lastActivity: number; + lastActivityTime: string; + extras: unknown; +} + +export interface TzktRollupAccount extends TzktAccountBase { + type: TzktAccountType.Rollup; + id: number; + creator: TzktAlias | nullish; + rollupBonds: number; + activeTokensCount: number; + tokenBalancesCount: number; + tokenTransfersCount: number; + numTransactions: number; + txRollupOriginationCount: number; + txRollupSubmitBatchCount: number; + txRollupCommitCount: number; + txRollupReturnBondCount: number; + txRollupFinalizeCommitmentCount: number; + txRollupRemoveCommitmentCount: number; + txRollupRejectionCount: number; + txRollupDispatchTicketsCount: number; + transferTicketCount: number; + firstActivity: number; + firstActivityTime: string; + lastActivity: number; + lastActivityTime: string; + extras: unknown; +} + +export interface TzktSmartRollupAccount extends TzktAccountBase { + type: TzktAccountType.SmartRollup; + id: number; + creator: TzktAlias | nullish; + pvmKind: 'arith' | 'wasm' | nullish; + genesisCommitment: string | nullish; + lastCommitment: string | nullish; + inboxLevel: number; + totalStakers: number; + activeStakers: number; + executedCommitments: number; + cementedCommitments: number; + pendingCommitments: number; + refutedCommitments: number; + orphanCommitments: number; + smartRollupBonds: number; + activeTokensCount: number; + tokenBalancesCount: number; + tokenTransfersCount: number; + numTransactions: number; + transferTicketCount: number; + smartRollupCementCount: number; + smartRollupExecuteCount: number; + smartRollupOriginateCount: number; + smartRollupPublishCount: number; + smartRollupRecoverBondCount: number; + smartRollupRefuteCount: number; + refutationGamesCount: number; + activeRefutationGamesCount: number; + firstActivity: number; + firstActivityTime: string; + lastActivity: number; + lastActivityTime: string; + extras: unknown; +} + +export interface TzktEmptyAccount extends TzktAccountBase { + type: TzktAccountType.Empty; + alias: undefined; + counter: number; +} + +export type TzktAccount = + | TzktUserAccount + | TzktDelegateAccount + | TzktContractAccount + | TzktGhostAccount + | TzktRollupAccount + | TzktSmartRollupAccount + | TzktEmptyAccount; diff --git a/src/lib/temple/front/baking.ts b/src/lib/temple/front/baking.ts index dc1d37fc4..84965ea0b 100644 --- a/src/lib/temple/front/baking.ts +++ b/src/lib/temple/front/baking.ts @@ -8,23 +8,36 @@ import { bakingBadGetBaker, getAllBakersBakingBad } from 'lib/apis/baking-bad'; -import type { TzktRewardsEntry } from 'lib/apis/tzkt'; +import { getAccountStatsFromTzkt, isKnownChainId, TzktRewardsEntry, TzktAccountType } from 'lib/apis/tzkt'; import { useRetryableSWR } from 'lib/swr'; -import { useNetwork, useTezos } from './ready'; +import { useChainId, useNetwork, useTezos } from './ready'; export function useDelegate(address: string, suspense = true) { const tezos = useTezos(); + const chainId = useChainId(suspense); const getDelegate = useCallback(async () => { - try { - return await tezos.rpc.getDelegate(address); - } catch { - return null; + if (chainId && isKnownChainId(chainId)) { + try { + const accountStats = await getAccountStatsFromTzkt(address, chainId); + + switch (accountStats?.type) { + case TzktAccountType.Empty: + return null; + case TzktAccountType.User: + case TzktAccountType.Contract: + return accountStats.delegate?.address ?? null; + } + } catch (e) { + console.error(e); + } } - }, [address, tezos]); - return useRetryableSWR(['delegate', tezos.checksum, address], getDelegate, { + return await tezos.rpc.getDelegate(address); + }, [address, tezos, chainId]); + + return useRetryableSWR(['delegate', tezos.checksum, address, chainId], getDelegate, { dedupingInterval: 20_000, suspense }); diff --git a/src/lib/temple/front/blockexplorer.ts b/src/lib/temple/front/blockexplorer.ts index 81de4fc0f..6a526a152 100644 --- a/src/lib/temple/front/blockexplorer.ts +++ b/src/lib/temple/front/blockexplorer.ts @@ -39,18 +39,19 @@ export const BLOCK_EXPLORERS: BlockExplorer[] = [ } ], [ - TempleChainId.Jakartanet, + TempleChainId.Mumbai, { - account: 'https://jakartanet.tzkt.io', - transaction: 'https://jakartanet.tzkt.io' + account: 'https://mumbainet.tzkt.io', + transaction: 'https://mumbainet.tzkt.io', + api: 'https://api.mumbainet.tzkt.io' } ], [ - TempleChainId.Limanet, + TempleChainId.Nairobi, { - account: 'https://limanet.tzkt.io', - transaction: 'https://limanet.tzkt.io', - api: 'https://api.limanet.tzkt.io' + account: 'https://nairobinet.tzkt.io', + transaction: 'https://nairobinet.tzkt.io', + api: 'https://api.nairobinet.tzkt.io' } ] ]) @@ -70,8 +71,9 @@ export const BLOCK_EXPLORERS: BlockExplorer[] = [ [ TempleChainId.DcpTest, { - account: 'https://explorer.tlnt.net:444', - transaction: 'https://explorer.tlnt.net:444' + account: 'https://explorer.test.tlnt.net', + transaction: 'https://explorer.test.tlnt.net', + api: 'https://explorer-api.test.tlnt.net' } ] ]) diff --git a/src/lib/temple/networks.ts b/src/lib/temple/networks.ts index 6b7f055cc..451c9cf35 100644 --- a/src/lib/temple/networks.ts +++ b/src/lib/temple/networks.ts @@ -13,9 +13,8 @@ const getLastMonday = (date = new Date()) => { export const NETWORK_IDS = new Map([ [TempleChainId.Mainnet, 'mainnet'], [TempleChainId.Ghostnet, 'ghostnet'], - [TempleChainId.Jakartanet, 'jakartanet'], - [TempleChainId.Limanet, 'limanet'], - [TempleChainId.Kathmandunet, 'kathmandunet'] + [TempleChainId.Mumbai, 'mumbainet'], + [TempleChainId.Nairobi, 'nairobinet'] ]); const DCP_NETWORKS: TempleNetwork[] = [ diff --git a/src/lib/temple/types.ts b/src/lib/temple/types.ts index 09b5c1746..7b93a83a3 100644 --- a/src/lib/temple/types.ts +++ b/src/lib/temple/types.ts @@ -39,10 +39,9 @@ export interface TempleState { export enum TempleChainId { Mainnet = 'NetXdQprcVkpaWU', Ghostnet = 'NetXnHfVqm9iesp', - Jakartanet = 'NetXLH1uAxK7CCh', - Limanet = 'NetXizpkH94bocH', - Kathmandunet = 'NetXdnUSgMs7Xc3', Monday = 'NetXaqtQ8b5nihx', + Mumbai = 'NetXgbcrNtXD2yA', + Nairobi = 'NetXyuzvDo2Ugzb', Daily = 'NetXxkAx4woPLyu', Dcp = 'NetXooyhiru73tk', DcpTest = 'NetXX7Tz1sK8JTa' From 337dcaea00e47887b1ba54e26d0b22813de04cd3 Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Fri, 27 Oct 2023 15:41:40 +0300 Subject: [PATCH 2/9] TW-737 Remove unused exports --- src/lib/apis/tzkt/types.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/apis/tzkt/types.ts b/src/lib/apis/tzkt/types.ts index cb4ed268a..6adaab43b 100644 --- a/src/lib/apis/tzkt/types.ts +++ b/src/lib/apis/tzkt/types.ts @@ -238,7 +238,7 @@ interface TzktAccountBase { alias: string | nullish; } -export interface TzktUserAccount extends TzktAccountBase { +interface TzktUserAccount extends TzktAccountBase { type: TzktAccountType.User; id: number; publicKey: string; @@ -290,7 +290,7 @@ export interface TzktUserAccount extends TzktAccountBase { lastActivityTime: string | nullish; } -export interface TzktDelegateAccount extends TzktAccountBase { +interface TzktDelegateAccount extends TzktAccountBase { type: TzktAccountType.Delegate; id: number; active: boolean; @@ -364,7 +364,7 @@ export interface TzktDelegateAccount extends TzktAccountBase { software: { date: string; version: string | nullish }; } -export interface TzktContractAccount extends TzktAccountBase { +interface TzktContractAccount extends TzktAccountBase { type: TzktAccountType.Contract; id: number; kind: 'delegator_contract' | 'smart_contract' | nullish; @@ -401,7 +401,7 @@ export interface TzktContractAccount extends TzktAccountBase { storage: unknown; } -export interface TzktGhostAccount extends TzktAccountBase { +interface TzktGhostAccount extends TzktAccountBase { type: TzktAccountType.Ghost; id: number; activeTokensCount: number; @@ -414,7 +414,7 @@ export interface TzktGhostAccount extends TzktAccountBase { extras: unknown; } -export interface TzktRollupAccount extends TzktAccountBase { +interface TzktRollupAccount extends TzktAccountBase { type: TzktAccountType.Rollup; id: number; creator: TzktAlias | nullish; @@ -439,7 +439,7 @@ export interface TzktRollupAccount extends TzktAccountBase { extras: unknown; } -export interface TzktSmartRollupAccount extends TzktAccountBase { +interface TzktSmartRollupAccount extends TzktAccountBase { type: TzktAccountType.SmartRollup; id: number; creator: TzktAlias | nullish; @@ -475,7 +475,7 @@ export interface TzktSmartRollupAccount extends TzktAccountBase { extras: unknown; } -export interface TzktEmptyAccount extends TzktAccountBase { +interface TzktEmptyAccount extends TzktAccountBase { type: TzktAccountType.Empty; alias: undefined; counter: number; From c945d47924e0e059d9f64832031e905469e89359 Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Fri, 27 Oct 2023 15:44:57 +0300 Subject: [PATCH 3/9] TW-737 Refactoring according to comments --- src/lib/apis/tzkt/api.ts | 4 ++-- src/lib/temple/front/baking.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/apis/tzkt/api.ts b/src/lib/apis/tzkt/api.ts index aba23fefe..dd61a847d 100644 --- a/src/lib/apis/tzkt/api.ts +++ b/src/lib/apis/tzkt/api.ts @@ -191,5 +191,5 @@ export const fetchAllTokensBalancesFromTzkt = async (selectedRpcUrl: string, acc return balances; }; -export const getAccountStatsFromTzkt = async (account: string, chainId: string) => - isKnownChainId(chainId) ? await fetchGet(chainId, `/accounts/${account}`) : null; +export const getAccountStatsFromTzkt = async (account: string, chainId: TzktApiChainId) => + fetchGet(chainId, `/accounts/${account}`); diff --git a/src/lib/temple/front/baking.ts b/src/lib/temple/front/baking.ts index 84965ea0b..90967d61d 100644 --- a/src/lib/temple/front/baking.ts +++ b/src/lib/temple/front/baking.ts @@ -22,7 +22,7 @@ export function useDelegate(address: string, suspense = true) { try { const accountStats = await getAccountStatsFromTzkt(address, chainId); - switch (accountStats?.type) { + switch (accountStats.type) { case TzktAccountType.Empty: return null; case TzktAccountType.User: From a57b59817108fec5749875a6730ffc832de5061d Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Mon, 6 Nov 2023 17:37:27 +0200 Subject: [PATCH 4/9] TW-737 Change UI for getting baker's address error --- public/_locales/en/messages.json | 6 ++ src/app/ErrorBoundary.tsx | 37 +++++----- src/app/pages/AddAsset/AddAsset.tsx | 4 +- src/app/pages/Home/ContentSection.tsx | 30 ++++++-- .../Home/OtherComponents/BakingSection.tsx | 2 +- .../components/TokenTag/DelegateTag.tsx | 2 +- src/lib/temple/front/baking.ts | 70 ++++++++++++++----- src/lib/temple/front/index.ts | 10 ++- 8 files changed, 116 insertions(+), 45 deletions(-) diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json index 79328a587..f9761fa39 100644 --- a/public/_locales/en/messages.json +++ b/public/_locales/en/messages.json @@ -624,6 +624,12 @@ "tryAgain": { "message": "Try again" }, + "errorGettingBakerAddressMessageOnline": { + "message": "Failed to get baker's address. Please, reload the page and try again." + }, + "errorGettingBakerAddressMessage": { + "message": "Failed to get baker's address. Please, check your internet connection and try again." + }, "tezosMainnet": { "message": "Tezos Mainnet", "description": "Mainnet = main network" diff --git a/src/app/ErrorBoundary.tsx b/src/app/ErrorBoundary.tsx index 8093e9efd..a9c12ebe9 100644 --- a/src/app/ErrorBoundary.tsx +++ b/src/app/ErrorBoundary.tsx @@ -3,11 +3,13 @@ import React, { Component, ErrorInfo } from 'react'; import classNames from 'clsx'; import { ReactComponent as DangerIcon } from 'app/icons/danger.svg'; -import { T } from 'lib/i18n'; +import { t, T } from 'lib/i18n'; -interface ErrorBoundaryProps extends React.PropsWithChildren { +export interface ErrorBoundaryProps extends React.PropsWithChildren { className?: string; whileMessage?: string; + wholeErrorMessageFn?: (error: Error, online: boolean, defaultMessage: string) => string; + beforeTryAgain?: (error: Error) => void | Promise; } type ErrorBoundaryState = { @@ -33,13 +35,26 @@ export default class ErrorBoundary extends Component { }); } - tryAgain() { + async tryAgain() { + if (this.props.beforeTryAgain) { + await this.props.beforeTryAgain(this.state.error!); + } this.setState({ error: null }); } + getDefaultErrorMessage() { + const { whileMessage } = this.props; + const online = getOnlineStatus(); + const firstPart = whileMessage ? t('smthWentWrongWhile', [whileMessage]) : t('smthWentWrong'); + + return online ? firstPart : [firstPart, t('mayHappenBecauseYouAreOffline')].join('. '); + } + render() { if (this.state.error) { const online = getOnlineStatus(); + const { wholeErrorMessageFn: wholeMessageFn } = this.props; + const defaultMessage = this.getDefaultErrorMessage(); return (
@@ -49,21 +64,7 @@ export default class ErrorBoundary extends Component { {message =>

{message}

}

- {this.props.whileMessage ? ( - - ) : ( - - )} - {!online && ( - - {message => ( - <> - {'. '} - {message} - - )} - - )} + {wholeMessageFn ? wholeMessageFn(this.state.error, online, defaultMessage) : defaultMessage}

diff --git a/src/app/pages/AddAsset/AddAsset.tsx b/src/app/pages/AddAsset/AddAsset.tsx index 8d5fb100c..e14996615 100644 --- a/src/app/pages/AddAsset/AddAsset.tsx +++ b/src/app/pages/AddAsset/AddAsset.tsx @@ -3,7 +3,7 @@ import React, { FC, ReactNode, useCallback, useEffect, useRef, useMemo } from 'r import classNames from 'clsx'; import { FormContextValues, useForm } from 'react-hook-form'; import { useDispatch } from 'react-redux'; -import { useSWRConfig } from 'swr'; +import { useSWRConfig, unstable_serialize } from 'swr'; import { useDebouncedCallback } from 'use-debounce'; import { Alert, FormField, FormSubmitButton, NoSpaceField } from 'app/atoms'; @@ -228,7 +228,7 @@ const Form: FC = () => { Repo.toAccountTokenKey(chainId, accountPkh, tokenSlug) ); - swrCache.delete(getBalanceSWRKey(tezos, tokenSlug, accountPkh)); + swrCache.delete(unstable_serialize(getBalanceSWRKey(tezos, tokenSlug, accountPkh))); formAnalytics.trackSubmitSuccess(); diff --git a/src/app/pages/Home/ContentSection.tsx b/src/app/pages/Home/ContentSection.tsx index 9717ead2d..9d13b98d1 100644 --- a/src/app/pages/Home/ContentSection.tsx +++ b/src/app/pages/Home/ContentSection.tsx @@ -5,13 +5,14 @@ import clsx from 'clsx'; import Spinner from 'app/atoms/Spinner/Spinner'; import { useTabSlug } from 'app/atoms/useTabSlug'; import { useAppEnv } from 'app/env'; -import ErrorBoundary from 'app/ErrorBoundary'; +import ErrorBoundary, { ErrorBoundaryProps } from 'app/ErrorBoundary'; import { ToolbarElement } from 'app/layouts/PageLayout'; import { ActivityComponent } from 'app/templates/activity/Activity'; import AssetInfo from 'app/templates/AssetInfo'; import { TabsBar } from 'app/templates/TabBar'; import { isTezAsset } from 'lib/assets'; import { t, TID } from 'lib/i18n'; +import { GetDelegateAddressError, useAccountPkh, useResetDelegateCache } from 'lib/temple/front'; import { CollectiblesTab } from '../Collectibles/CollectiblesTab'; import { HomeSelectors } from './Home.selectors'; @@ -33,12 +34,27 @@ interface TabData { whileMessageI18nKey?: TID; } +const wholeErrorMessageFn = (error: Error, online: boolean, defaultMessage: string) => { + if (error instanceof GetDelegateAddressError && online) { + return t('errorGettingBakerAddressMessageOnline'); + } + + if (error instanceof GetDelegateAddressError) { + return t('errorGettingBakerAddressMessage'); + } + + return defaultMessage; +}; + export const ContentSection: FC = ({ assetSlug, className }) => { const { fullPage } = useAppEnv(); const tabSlug = useTabSlug(); const tabBarElemRef = useRef(null); + const accountPkh = useAccountPkh(); + const resetDelegateCache = useResetDelegateCache(accountPkh); + const scrollToTheTabsBar = useCallback(() => { if (!tabBarElemRef.current) return; @@ -115,20 +131,24 @@ export const ContentSection: FC = ({ assetSlug, className }) => {
- + {Component && }
); }; -interface SuspenseContainerProps extends PropsWithChildren { +interface SuspenseContainerProps extends PropsWithChildren, Omit { whileMessage: string; fallback?: ReactNode; } -const SuspenseContainer: FC = ({ whileMessage, fallback = , children }) => ( - +const SuspenseContainer: FC = ({ fallback = , children, ...restProps }) => ( + {children} ); diff --git a/src/app/pages/Home/OtherComponents/BakingSection.tsx b/src/app/pages/Home/OtherComponents/BakingSection.tsx index d1c900f94..3b5d3cc75 100644 --- a/src/app/pages/Home/OtherComponents/BakingSection.tsx +++ b/src/app/pages/Home/OtherComponents/BakingSection.tsx @@ -70,7 +70,7 @@ const links = [ const BakingSection = memo(() => { const acc = useAccount(); - const { data: myBakerPkh } = useDelegate(acc.publicKeyHash); + const { data: myBakerPkh } = useDelegate(acc.publicKeyHash, true, false); const canDelegate = acc.type !== TempleAccountType.WatchOnly; const chainId = useChainId(true); const { isDcpNetwork } = useGasToken(); diff --git a/src/app/pages/Home/OtherComponents/Tokens/components/TokenTag/DelegateTag.tsx b/src/app/pages/Home/OtherComponents/Tokens/components/TokenTag/DelegateTag.tsx index 551e15fc6..45d89f2c1 100644 --- a/src/app/pages/Home/OtherComponents/Tokens/components/TokenTag/DelegateTag.tsx +++ b/src/app/pages/Home/OtherComponents/Tokens/components/TokenTag/DelegateTag.tsx @@ -6,7 +6,7 @@ import { Button } from 'app/atoms/Button'; import { HomeSelectors } from 'app/pages/Home/Home.selectors'; import { AnalyticsEventCategory, useAnalytics } from 'lib/analytics'; import { T } from 'lib/i18n'; -import { useAccount, useDelegate } from 'lib/temple/front'; +import { FAILED_TO_GET_DELEGATE_RESULT, useAccount, useDelegate } from 'lib/temple/front'; import { navigate } from 'lib/woozie'; import { AssetsSelectors } from '../../../Assets.selectors'; diff --git a/src/lib/temple/front/baking.ts b/src/lib/temple/front/baking.ts index 90967d61d..7032d75f6 100644 --- a/src/lib/temple/front/baking.ts +++ b/src/lib/temple/front/baking.ts @@ -1,6 +1,8 @@ import { useCallback, useMemo } from 'react'; +import retry from 'async-retry'; import BigNumber from 'bignumber.js'; +import useSWR, { unstable_serialize, useSWRConfig } from 'swr'; import { BakingBadBaker, @@ -13,31 +15,65 @@ import { useRetryableSWR } from 'lib/swr'; import { useChainId, useNetwork, useTezos } from './ready'; -export function useDelegate(address: string, suspense = true) { +export const FAILED_TO_GET_DELEGATE_RESULT = ''; + +export class GetDelegateAddressError extends Error { + constructor(public internalError: unknown) { + super('Failed to get delegate address'); + } +} + +export function useResetDelegateCache(address: string) { + const tezos = useTezos(); + const chainId = useChainId(false); + const { cache: swrCache } = useSWRConfig(); + + return useCallback(() => { + const cacheKeyBase = unstable_serialize(['delegate', tezos.checksum, address, chainId]); + + swrCache.delete(`$swr$${cacheKeyBase}`); + }, [address, tezos, chainId, swrCache]); +} + +export function useDelegate(address: string, suspense = true, shouldPreventErrorPropagation = true) { const tezos = useTezos(); const chainId = useChainId(suspense); const getDelegate = useCallback(async () => { - if (chainId && isKnownChainId(chainId)) { - try { - const accountStats = await getAccountStatsFromTzkt(address, chainId); + try { + return await retry( + async (): Promise => { + const freshChainId = chainId ?? (await tezos.rpc.getChainId()); + if (freshChainId && isKnownChainId(freshChainId)) { + try { + const accountStats = await getAccountStatsFromTzkt(address, freshChainId); - switch (accountStats.type) { - case TzktAccountType.Empty: - return null; - case TzktAccountType.User: - case TzktAccountType.Contract: - return accountStats.delegate?.address ?? null; - } - } catch (e) { - console.error(e); + switch (accountStats.type) { + case TzktAccountType.Empty: + return null; + case TzktAccountType.User: + case TzktAccountType.Contract: + return accountStats.delegate?.address ?? null; + } + } catch (e) { + console.error(e); + } + } + + return await tezos.rpc.getDelegate(address); + }, + { retries: 3, minTimeout: 3000, maxTimeout: 5000 } + ); + } catch (e) { + if (shouldPreventErrorPropagation) { + return FAILED_TO_GET_DELEGATE_RESULT; } - } - return await tezos.rpc.getDelegate(address); - }, [address, tezos, chainId]); + throw new GetDelegateAddressError(e); + } + }, [address, tezos, chainId, shouldPreventErrorPropagation]); - return useRetryableSWR(['delegate', tezos.checksum, address, chainId], getDelegate, { + return useSWR(['delegate', tezos.checksum, address, chainId], getDelegate, { dedupingInterval: 20_000, suspense }); diff --git a/src/lib/temple/front/index.ts b/src/lib/temple/front/index.ts index dccc583ad..b6740f5fb 100644 --- a/src/lib/temple/front/index.ts +++ b/src/lib/temple/front/index.ts @@ -38,7 +38,15 @@ export { useContactsActions, searchContacts } from './address-book'; export { useTezosDomainsClient, isDomainNameValid } from './tzdns'; export type { Baker } from './baking'; -export { getRewardsStats, useKnownBaker, useKnownBakers, useDelegate } from './baking'; +export { + FAILED_TO_GET_DELEGATE_RESULT, + GetDelegateAddressError, + getRewardsStats, + useKnownBaker, + useKnownBakers, + useDelegate, + useResetDelegateCache +} from './baking'; export { activateAccount } from './activate-account'; From 7ce2bfd7cc1bdc743d4a789e18cb7856d3b318c1 Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Mon, 6 Nov 2023 17:39:48 +0200 Subject: [PATCH 5/9] TW-737 Remove an unused import --- .../OtherComponents/Tokens/components/TokenTag/DelegateTag.tsx | 2 +- src/lib/temple/front/baking.ts | 2 +- src/lib/temple/front/index.ts | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/app/pages/Home/OtherComponents/Tokens/components/TokenTag/DelegateTag.tsx b/src/app/pages/Home/OtherComponents/Tokens/components/TokenTag/DelegateTag.tsx index 45d89f2c1..551e15fc6 100644 --- a/src/app/pages/Home/OtherComponents/Tokens/components/TokenTag/DelegateTag.tsx +++ b/src/app/pages/Home/OtherComponents/Tokens/components/TokenTag/DelegateTag.tsx @@ -6,7 +6,7 @@ import { Button } from 'app/atoms/Button'; import { HomeSelectors } from 'app/pages/Home/Home.selectors'; import { AnalyticsEventCategory, useAnalytics } from 'lib/analytics'; import { T } from 'lib/i18n'; -import { FAILED_TO_GET_DELEGATE_RESULT, useAccount, useDelegate } from 'lib/temple/front'; +import { useAccount, useDelegate } from 'lib/temple/front'; import { navigate } from 'lib/woozie'; import { AssetsSelectors } from '../../../Assets.selectors'; diff --git a/src/lib/temple/front/baking.ts b/src/lib/temple/front/baking.ts index 7032d75f6..25c717343 100644 --- a/src/lib/temple/front/baking.ts +++ b/src/lib/temple/front/baking.ts @@ -15,7 +15,7 @@ import { useRetryableSWR } from 'lib/swr'; import { useChainId, useNetwork, useTezos } from './ready'; -export const FAILED_TO_GET_DELEGATE_RESULT = ''; +const FAILED_TO_GET_DELEGATE_RESULT = ''; export class GetDelegateAddressError extends Error { constructor(public internalError: unknown) { diff --git a/src/lib/temple/front/index.ts b/src/lib/temple/front/index.ts index b6740f5fb..21870cd1b 100644 --- a/src/lib/temple/front/index.ts +++ b/src/lib/temple/front/index.ts @@ -39,7 +39,6 @@ export { useTezosDomainsClient, isDomainNameValid } from './tzdns'; export type { Baker } from './baking'; export { - FAILED_TO_GET_DELEGATE_RESULT, GetDelegateAddressError, getRewardsStats, useKnownBaker, From e077a56ad276d20bd2ca0750bf8e2070f86fab1b Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Mon, 6 Nov 2023 17:52:15 +0200 Subject: [PATCH 6/9] TW-737 Fix cleaning token balance cache --- src/app/pages/AddAsset/AddAsset.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/pages/AddAsset/AddAsset.tsx b/src/app/pages/AddAsset/AddAsset.tsx index e14996615..e704b882e 100644 --- a/src/app/pages/AddAsset/AddAsset.tsx +++ b/src/app/pages/AddAsset/AddAsset.tsx @@ -228,7 +228,7 @@ const Form: FC = () => { Repo.toAccountTokenKey(chainId, accountPkh, tokenSlug) ); - swrCache.delete(unstable_serialize(getBalanceSWRKey(tezos, tokenSlug, accountPkh))); + swrCache.delete(`$swr$${unstable_serialize(getBalanceSWRKey(tezos, tokenSlug, accountPkh))}`); formAnalytics.trackSubmitSuccess(); From 9ca9d73aa1661356c66f91aaec10a5781fb142d2 Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Tue, 7 Nov 2023 13:35:49 +0200 Subject: [PATCH 7/9] TW-737 Refactoring according to comments --- src/app/ErrorBoundary.tsx | 37 +++++++++++------- src/app/pages/Home/ContentSection.tsx | 22 +---------- .../Home/OtherComponents/BakingSection.tsx | 11 +++++- src/lib/temple/front/baking.ts | 38 +++++++++++-------- src/lib/temple/front/get-online-status.ts | 3 ++ src/lib/temple/front/index.ts | 11 ++---- 6 files changed, 62 insertions(+), 60 deletions(-) create mode 100644 src/lib/temple/front/get-online-status.ts diff --git a/src/app/ErrorBoundary.tsx b/src/app/ErrorBoundary.tsx index a9c12ebe9..509c9b9a4 100644 --- a/src/app/ErrorBoundary.tsx +++ b/src/app/ErrorBoundary.tsx @@ -4,12 +4,17 @@ import classNames from 'clsx'; import { ReactComponent as DangerIcon } from 'app/icons/danger.svg'; import { t, T } from 'lib/i18n'; +import { getOnlineStatus } from 'lib/temple/front'; export interface ErrorBoundaryProps extends React.PropsWithChildren { className?: string; whileMessage?: string; - wholeErrorMessageFn?: (error: Error, online: boolean, defaultMessage: string) => string; - beforeTryAgain?: (error: Error) => void | Promise; +} + +export class BoundaryError extends Error { + constructor(public readonly message: string, public readonly beforeTryAgain: EmptyFn) { + super(message); + } } type ErrorBoundaryState = { @@ -36,8 +41,9 @@ export default class ErrorBoundary extends Component { } async tryAgain() { - if (this.props.beforeTryAgain) { - await this.props.beforeTryAgain(this.state.error!); + const { error } = this.state; + if (error instanceof BoundaryError) { + error.beforeTryAgain(); } this.setState({ error: null }); } @@ -50,21 +56,28 @@ export default class ErrorBoundary extends Component { return online ? firstPart : [firstPart, t('mayHappenBecauseYouAreOffline')].join('. '); } + componentDidUpdate(prevProps: ErrorBoundaryProps) { + if (prevProps.children !== this.props.children) { + this.setState({ error: null }); + } + } + render() { - if (this.state.error) { - const online = getOnlineStatus(); - const { wholeErrorMessageFn: wholeMessageFn } = this.props; + const { children, className } = this.props; + const { error } = this.state; + + if (error) { const defaultMessage = this.getDefaultErrorMessage(); return ( -
+
{message =>

{message}

}

- {wholeMessageFn ? wholeMessageFn(this.state.error, online, defaultMessage) : defaultMessage} + {error instanceof BoundaryError ? error.message : defaultMessage}

@@ -93,10 +106,6 @@ export default class ErrorBoundary extends Component { ); } - return this.props.children; + return children; } } - -function getOnlineStatus() { - return typeof navigator !== 'undefined' && typeof navigator.onLine === 'boolean' ? navigator.onLine : true; -} diff --git a/src/app/pages/Home/ContentSection.tsx b/src/app/pages/Home/ContentSection.tsx index 9d13b98d1..d6dd17f23 100644 --- a/src/app/pages/Home/ContentSection.tsx +++ b/src/app/pages/Home/ContentSection.tsx @@ -12,7 +12,6 @@ import AssetInfo from 'app/templates/AssetInfo'; import { TabsBar } from 'app/templates/TabBar'; import { isTezAsset } from 'lib/assets'; import { t, TID } from 'lib/i18n'; -import { GetDelegateAddressError, useAccountPkh, useResetDelegateCache } from 'lib/temple/front'; import { CollectiblesTab } from '../Collectibles/CollectiblesTab'; import { HomeSelectors } from './Home.selectors'; @@ -34,27 +33,12 @@ interface TabData { whileMessageI18nKey?: TID; } -const wholeErrorMessageFn = (error: Error, online: boolean, defaultMessage: string) => { - if (error instanceof GetDelegateAddressError && online) { - return t('errorGettingBakerAddressMessageOnline'); - } - - if (error instanceof GetDelegateAddressError) { - return t('errorGettingBakerAddressMessage'); - } - - return defaultMessage; -}; - export const ContentSection: FC = ({ assetSlug, className }) => { const { fullPage } = useAppEnv(); const tabSlug = useTabSlug(); const tabBarElemRef = useRef(null); - const accountPkh = useAccountPkh(); - const resetDelegateCache = useResetDelegateCache(accountPkh); - const scrollToTheTabsBar = useCallback(() => { if (!tabBarElemRef.current) return; @@ -131,11 +115,7 @@ export const ContentSection: FC = ({ assetSlug, className }) => {
- + {Component && }
diff --git a/src/app/pages/Home/OtherComponents/BakingSection.tsx b/src/app/pages/Home/OtherComponents/BakingSection.tsx index 3b5d3cc75..64baaf321 100644 --- a/src/app/pages/Home/OtherComponents/BakingSection.tsx +++ b/src/app/pages/Home/OtherComponents/BakingSection.tsx @@ -1,4 +1,4 @@ -import React, { memo, useCallback, useMemo } from 'react'; +import React, { memo, useCallback, /* useEffect, */ useMemo } from 'react'; import BigNumber from 'bignumber.js'; import classNames from 'clsx'; @@ -23,7 +23,7 @@ import BakerBanner from 'app/templates/BakerBanner'; import { getDelegatorRewards, isKnownChainId } from 'lib/apis/tzkt'; import { T, t } from 'lib/i18n'; import { useRetryableSWR } from 'lib/swr'; -import { useAccount, useChainId, useDelegate, useGasToken } from 'lib/temple/front'; +import { useAccount, useChainId, useDelegate, useGasToken /*, useResetDelegateCache */ } from 'lib/temple/front'; import { TempleAccountType } from 'lib/temple/types'; import useTippy from 'lib/ui/useTippy'; import { Link } from 'lib/woozie'; @@ -85,6 +85,13 @@ const BakingSection = memo(() => { animation: 'shift-away-subtle' }; + /* const resetDelegateCache = useResetDelegateCache(acc.publicKeyHash); + useEffect(() => { + resetDelegateCache(); + + return () => resetDelegateCache(); + }, [resetDelegateCache]); */ + const getBakingHistory = useCallback( async (_k: string, accountPkh: string) => { if (!isKnownChainId(chainId!)) { diff --git a/src/lib/temple/front/baking.ts b/src/lib/temple/front/baking.ts index 25c717343..843fb6e3c 100644 --- a/src/lib/temple/front/baking.ts +++ b/src/lib/temple/front/baking.ts @@ -4,6 +4,7 @@ import retry from 'async-retry'; import BigNumber from 'bignumber.js'; import useSWR, { unstable_serialize, useSWRConfig } from 'swr'; +import { BoundaryError } from 'app/ErrorBoundary'; import { BakingBadBaker, BakingBadBakerValueHistoryItem, @@ -11,34 +12,38 @@ import { getAllBakersBakingBad } from 'lib/apis/baking-bad'; import { getAccountStatsFromTzkt, isKnownChainId, TzktRewardsEntry, TzktAccountType } from 'lib/apis/tzkt'; +import { t } from 'lib/i18n'; import { useRetryableSWR } from 'lib/swr'; +import type { ReactiveTezosToolkit } from 'lib/temple/front'; +import { getOnlineStatus } from './get-online-status'; import { useChainId, useNetwork, useTezos } from './ready'; -const FAILED_TO_GET_DELEGATE_RESULT = ''; - -export class GetDelegateAddressError extends Error { - constructor(public internalError: unknown) { - super('Failed to get delegate address'); - } +function getDelegateCacheKey( + tezos: ReactiveTezosToolkit, + address: string, + chainId: string | nullish, + shouldPreventErrorPropagation: boolean +) { + return `$swr$${unstable_serialize(['delegate', tezos.checksum, address, chainId, shouldPreventErrorPropagation])}`; } -export function useResetDelegateCache(address: string) { +function useResetDelegateCache(address: string, shouldPreventErrorPropagation: boolean) { const tezos = useTezos(); const chainId = useChainId(false); const { cache: swrCache } = useSWRConfig(); return useCallback(() => { - const cacheKeyBase = unstable_serialize(['delegate', tezos.checksum, address, chainId]); - - swrCache.delete(`$swr$${cacheKeyBase}`); - }, [address, tezos, chainId, swrCache]); + swrCache.delete(getDelegateCacheKey(tezos, address, chainId, shouldPreventErrorPropagation)); + }, [address, tezos, chainId, swrCache, shouldPreventErrorPropagation]); } export function useDelegate(address: string, suspense = true, shouldPreventErrorPropagation = true) { const tezos = useTezos(); const chainId = useChainId(suspense); + const resetDelegateCache = useResetDelegateCache(address, shouldPreventErrorPropagation); + const getDelegate = useCallback(async () => { try { return await retry( @@ -66,14 +71,17 @@ export function useDelegate(address: string, suspense = true, shouldPreventError ); } catch (e) { if (shouldPreventErrorPropagation) { - return FAILED_TO_GET_DELEGATE_RESULT; + return null; } - throw new GetDelegateAddressError(e); + throw new BoundaryError( + getOnlineStatus() ? t('errorGettingBakerAddressMessageOnline') : t('errorGettingBakerAddressMessage'), + resetDelegateCache + ); } - }, [address, tezos, chainId, shouldPreventErrorPropagation]); + }, [chainId, tezos, address, shouldPreventErrorPropagation, resetDelegateCache]); - return useSWR(['delegate', tezos.checksum, address, chainId], getDelegate, { + return useSWR(['delegate', tezos.checksum, address, chainId, shouldPreventErrorPropagation], getDelegate, { dedupingInterval: 20_000, suspense }); diff --git a/src/lib/temple/front/get-online-status.ts b/src/lib/temple/front/get-online-status.ts new file mode 100644 index 000000000..f4c90705b --- /dev/null +++ b/src/lib/temple/front/get-online-status.ts @@ -0,0 +1,3 @@ +export function getOnlineStatus() { + return typeof navigator !== 'undefined' && typeof navigator.onLine === 'boolean' ? navigator.onLine : true; +} diff --git a/src/lib/temple/front/index.ts b/src/lib/temple/front/index.ts index 21870cd1b..4bde1f873 100644 --- a/src/lib/temple/front/index.ts +++ b/src/lib/temple/front/index.ts @@ -38,14 +38,7 @@ export { useContactsActions, searchContacts } from './address-book'; export { useTezosDomainsClient, isDomainNameValid } from './tzdns'; export type { Baker } from './baking'; -export { - GetDelegateAddressError, - getRewardsStats, - useKnownBaker, - useKnownBakers, - useDelegate, - useResetDelegateCache -} from './baking'; +export { getRewardsStats, useKnownBaker, useKnownBakers, useDelegate } from './baking'; export { activateAccount } from './activate-account'; @@ -71,3 +64,5 @@ export { buildCollectibleImageURLs, buildObjktCollectibleArtifactUri } from './image-uri'; + +export { getOnlineStatus } from './get-online-status'; From 40d0356adc9f351d394178624531bcc94e69d2ca Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Tue, 7 Nov 2023 13:49:11 +0200 Subject: [PATCH 8/9] TW-737 Additional refactoring --- src/app/pages/AddAsset/AddAsset.tsx | 5 +++-- .../Home/OtherComponents/BakingSection.tsx | 11 ++-------- src/lib/swr/index.ts | 4 +++- src/lib/temple/front/baking.ts | 21 +++++++------------ 4 files changed, 15 insertions(+), 26 deletions(-) diff --git a/src/app/pages/AddAsset/AddAsset.tsx b/src/app/pages/AddAsset/AddAsset.tsx index e704b882e..75cecffca 100644 --- a/src/app/pages/AddAsset/AddAsset.tsx +++ b/src/app/pages/AddAsset/AddAsset.tsx @@ -3,7 +3,7 @@ import React, { FC, ReactNode, useCallback, useEffect, useRef, useMemo } from 'r import classNames from 'clsx'; import { FormContextValues, useForm } from 'react-hook-form'; import { useDispatch } from 'react-redux'; -import { useSWRConfig, unstable_serialize } from 'swr'; +import { useSWRConfig } from 'swr'; import { useDebouncedCallback } from 'use-debounce'; import { Alert, FormField, FormSubmitButton, NoSpaceField } from 'app/atoms'; @@ -24,6 +24,7 @@ import { T, t } from 'lib/i18n'; import type { TokenMetadata } from 'lib/metadata'; import { fetchOneTokenMetadata } from 'lib/metadata/fetch'; import { TokenMetadataNotFoundError } from 'lib/metadata/on-chain'; +import { getCacheKey } from 'lib/swr'; import { loadContract } from 'lib/temple/contract'; import { useTezos, @@ -228,7 +229,7 @@ const Form: FC = () => { Repo.toAccountTokenKey(chainId, accountPkh, tokenSlug) ); - swrCache.delete(`$swr$${unstable_serialize(getBalanceSWRKey(tezos, tokenSlug, accountPkh))}`); + swrCache.delete(getCacheKey(getBalanceSWRKey(tezos, tokenSlug, accountPkh))); formAnalytics.trackSubmitSuccess(); diff --git a/src/app/pages/Home/OtherComponents/BakingSection.tsx b/src/app/pages/Home/OtherComponents/BakingSection.tsx index 64baaf321..3b5d3cc75 100644 --- a/src/app/pages/Home/OtherComponents/BakingSection.tsx +++ b/src/app/pages/Home/OtherComponents/BakingSection.tsx @@ -1,4 +1,4 @@ -import React, { memo, useCallback, /* useEffect, */ useMemo } from 'react'; +import React, { memo, useCallback, useMemo } from 'react'; import BigNumber from 'bignumber.js'; import classNames from 'clsx'; @@ -23,7 +23,7 @@ import BakerBanner from 'app/templates/BakerBanner'; import { getDelegatorRewards, isKnownChainId } from 'lib/apis/tzkt'; import { T, t } from 'lib/i18n'; import { useRetryableSWR } from 'lib/swr'; -import { useAccount, useChainId, useDelegate, useGasToken /*, useResetDelegateCache */ } from 'lib/temple/front'; +import { useAccount, useChainId, useDelegate, useGasToken } from 'lib/temple/front'; import { TempleAccountType } from 'lib/temple/types'; import useTippy from 'lib/ui/useTippy'; import { Link } from 'lib/woozie'; @@ -85,13 +85,6 @@ const BakingSection = memo(() => { animation: 'shift-away-subtle' }; - /* const resetDelegateCache = useResetDelegateCache(acc.publicKeyHash); - useEffect(() => { - resetDelegateCache(); - - return () => resetDelegateCache(); - }, [resetDelegateCache]); */ - const getBakingHistory = useCallback( async (_k: string, accountPkh: string) => { if (!isKnownChainId(chainId!)) { diff --git a/src/lib/swr/index.ts b/src/lib/swr/index.ts index c7b117fce..d637a03a0 100644 --- a/src/lib/swr/index.ts +++ b/src/lib/swr/index.ts @@ -1,7 +1,9 @@ -import useSWR, { Key, Fetcher, SWRConfiguration, SWRResponse } from 'swr'; +import useSWR, { Key, Fetcher, SWRConfiguration, SWRResponse, unstable_serialize } from 'swr'; export const useRetryableSWR = ( key: SWRKey, fetcher: Fetcher | null, config: SWRConfiguration> | undefined ): SWRResponse => useSWR(key, fetcher, { errorRetryCount: 2, ...config }); + +export const getCacheKey = (key: Key) => `$swr$${unstable_serialize(key)}`; diff --git a/src/lib/temple/front/baking.ts b/src/lib/temple/front/baking.ts index 843fb6e3c..15f8609ee 100644 --- a/src/lib/temple/front/baking.ts +++ b/src/lib/temple/front/baking.ts @@ -2,7 +2,7 @@ import { useCallback, useMemo } from 'react'; import retry from 'async-retry'; import BigNumber from 'bignumber.js'; -import useSWR, { unstable_serialize, useSWRConfig } from 'swr'; +import useSWR, { useSWRConfig } from 'swr'; import { BoundaryError } from 'app/ErrorBoundary'; import { @@ -13,7 +13,7 @@ import { } from 'lib/apis/baking-bad'; import { getAccountStatsFromTzkt, isKnownChainId, TzktRewardsEntry, TzktAccountType } from 'lib/apis/tzkt'; import { t } from 'lib/i18n'; -import { useRetryableSWR } from 'lib/swr'; +import { getCacheKey, useRetryableSWR } from 'lib/swr'; import type { ReactiveTezosToolkit } from 'lib/temple/front'; import { getOnlineStatus } from './get-online-status'; @@ -25,29 +25,22 @@ function getDelegateCacheKey( chainId: string | nullish, shouldPreventErrorPropagation: boolean ) { - return `$swr$${unstable_serialize(['delegate', tezos.checksum, address, chainId, shouldPreventErrorPropagation])}`; + return getCacheKey(['delegate', tezos.checksum, address, chainId, shouldPreventErrorPropagation]); } -function useResetDelegateCache(address: string, shouldPreventErrorPropagation: boolean) { +export function useDelegate(address: string, suspense = true, shouldPreventErrorPropagation = true) { const tezos = useTezos(); - const chainId = useChainId(false); + const chainId = useChainId(suspense); const { cache: swrCache } = useSWRConfig(); - return useCallback(() => { + const resetDelegateCache = useCallback(() => { swrCache.delete(getDelegateCacheKey(tezos, address, chainId, shouldPreventErrorPropagation)); }, [address, tezos, chainId, swrCache, shouldPreventErrorPropagation]); -} - -export function useDelegate(address: string, suspense = true, shouldPreventErrorPropagation = true) { - const tezos = useTezos(); - const chainId = useChainId(suspense); - - const resetDelegateCache = useResetDelegateCache(address, shouldPreventErrorPropagation); const getDelegate = useCallback(async () => { try { return await retry( - async (): Promise => { + async () => { const freshChainId = chainId ?? (await tezos.rpc.getChainId()); if (freshChainId && isKnownChainId(freshChainId)) { try { From d15cfa2a31bc6b80425151e55a058d53cb0cac08 Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Fri, 10 Nov 2023 16:44:18 +0200 Subject: [PATCH 9/9] TW-737 Refactoring according to comments --- src/app/ErrorBoundary.tsx | 12 ++++++------ src/app/pages/Home/ContentSection.tsx | 9 +++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/app/ErrorBoundary.tsx b/src/app/ErrorBoundary.tsx index 509c9b9a4..b1edd733f 100644 --- a/src/app/ErrorBoundary.tsx +++ b/src/app/ErrorBoundary.tsx @@ -1,4 +1,4 @@ -import React, { Component, ErrorInfo } from 'react'; +import React, { Component, ErrorInfo, FC } from 'react'; import classNames from 'clsx'; @@ -9,6 +9,7 @@ import { getOnlineStatus } from 'lib/temple/front'; export interface ErrorBoundaryProps extends React.PropsWithChildren { className?: string; whileMessage?: string; + Content?: FC<{}>; } export class BoundaryError extends Error { @@ -57,18 +58,17 @@ export default class ErrorBoundary extends Component { } componentDidUpdate(prevProps: ErrorBoundaryProps) { - if (prevProps.children !== this.props.children) { + if (prevProps.Content !== this.props.Content) { this.setState({ error: null }); } } render() { - const { children, className } = this.props; + const { className, Content, children: childrenFromProps } = this.props; const { error } = this.state; + const children = Content ? : childrenFromProps; if (error) { - const defaultMessage = this.getDefaultErrorMessage(); - return (
@@ -77,7 +77,7 @@ export default class ErrorBoundary extends Component { {message =>

{message}

}

- {error instanceof BoundaryError ? error.message : defaultMessage} + {error instanceof BoundaryError ? error.message : this.getDefaultErrorMessage()}

diff --git a/src/app/pages/Home/ContentSection.tsx b/src/app/pages/Home/ContentSection.tsx index d6dd17f23..ced561a44 100644 --- a/src/app/pages/Home/ContentSection.tsx +++ b/src/app/pages/Home/ContentSection.tsx @@ -115,14 +115,15 @@ export const ContentSection: FC = ({ assetSlug, className }) => {
- - {Component && } - +
); }; -interface SuspenseContainerProps extends PropsWithChildren, Omit { +interface SuspenseContainerProps extends Omit { whileMessage: string; fallback?: ReactNode; }