Skip to content

Commit

Permalink
TW-737 Change UI for getting baker's address error
Browse files Browse the repository at this point in the history
  • Loading branch information
keshan3262 committed Nov 6, 2023
1 parent 4484438 commit a57b598
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 45 deletions.
6 changes: 6 additions & 0 deletions public/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
37 changes: 19 additions & 18 deletions src/app/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<void>;
}

type ErrorBoundaryState = {
Expand All @@ -33,13 +35,26 @@ export default class ErrorBoundary extends Component<ErrorBoundaryProps> {
});
}

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 (
<div className={classNames('w-full', 'flex items-center justify-center', this.props.className)}>
Expand All @@ -49,21 +64,7 @@ export default class ErrorBoundary extends Component<ErrorBoundaryProps> {
<T id="oops">{message => <h2 className="mb-1 text-2xl">{message}</h2>}</T>

<p className="mb-4 text-sm opacity-90 text-center font-light">
{this.props.whileMessage ? (
<T id="smthWentWrongWhile" substitutions={this.props.whileMessage} />
) : (
<T id="smthWentWrong" />
)}
{!online && (
<T id="mayHappenBecauseYouAreOffline">
{message => (
<>
{'. '}
{message}
</>
)}
</T>
)}
{wholeMessageFn ? wholeMessageFn(this.state.error, online, defaultMessage) : defaultMessage}
</p>

<T id="tryAgain">
Expand Down
4 changes: 2 additions & 2 deletions src/app/pages/AddAsset/AddAsset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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();

Expand Down
30 changes: 25 additions & 5 deletions src/app/pages/Home/ContentSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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<Props> = ({ assetSlug, className }) => {
const { fullPage } = useAppEnv();
const tabSlug = useTabSlug();

const tabBarElemRef = useRef<HTMLDivElement>(null);

const accountPkh = useAccountPkh();
const resetDelegateCache = useResetDelegateCache(accountPkh);

const scrollToTheTabsBar = useCallback(() => {
if (!tabBarElemRef.current) return;

Expand Down Expand Up @@ -115,20 +131,24 @@ export const ContentSection: FC<Props> = ({ assetSlug, className }) => {
<div className={clsx('-mx-4 shadow-top-light', fullPage && 'rounded-t-md', className)}>
<TabsBar ref={tabBarElemRef} tabs={tabs} activeTabName={name} />

<SuspenseContainer whileMessage={whileMessageI18nKey ? t(whileMessageI18nKey) : 'displaying tab'}>
<SuspenseContainer
whileMessage={whileMessageI18nKey ? t(whileMessageI18nKey) : 'displaying tab'}
beforeTryAgain={resetDelegateCache}
wholeErrorMessageFn={wholeErrorMessageFn}
>
{Component && <Component />}
</SuspenseContainer>
</div>
);
};

interface SuspenseContainerProps extends PropsWithChildren {
interface SuspenseContainerProps extends PropsWithChildren, Omit<ErrorBoundaryProps, 'className' | 'children'> {
whileMessage: string;
fallback?: ReactNode;
}

const SuspenseContainer: FC<SuspenseContainerProps> = ({ whileMessage, fallback = <SpinnerSection />, children }) => (
<ErrorBoundary whileMessage={whileMessage}>
const SuspenseContainer: FC<SuspenseContainerProps> = ({ fallback = <SpinnerSection />, children, ...restProps }) => (
<ErrorBoundary {...restProps}>
<Suspense fallback={fallback}>{children}</Suspense>
</ErrorBoundary>
);
Expand Down
2 changes: 1 addition & 1 deletion src/app/pages/Home/OtherComponents/BakingSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
70 changes: 53 additions & 17 deletions src/lib/temple/front/baking.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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<string | null> => {
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
});
Expand Down
10 changes: 9 additions & 1 deletion src/lib/temple/front/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down

0 comments on commit a57b598

Please sign in to comment.