Commit a3ba5696 authored by tom's avatar tom

Merge branch 'main' of github.com:blockscout/frontend

parents 8cca42ff ba453797
import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import type { Route } from 'nextjs-routes';
import React from 'react';
import type { UserInfo } from 'types/api/account';
import type { ResourceError } from 'lib/api/resources';
import { resourceKey } from 'lib/api/resources';
import useLoginUrl from 'lib/hooks/useLoginUrl';
export default function useRedirectIfNotAuth() {
export default function useIsAccountActionAllowed() {
const queryClient = useQueryClient();
const profileData = queryClient.getQueryData<UserInfo>([ resourceKey('user_info') ]);
const profileState = queryClient.getQueryState<unknown, ResourceError<{ message: string }>>([ resourceKey('user_info') ]);
const isAuth = Boolean(profileData);
const loginUrl = useLoginUrl();
const router = useRouter();
return React.useCallback((accountRoute: Route) => {
if (profileState?.error?.status === 403) {
router.push(accountRoute);
return false;
}
return React.useCallback(() => {
if (!isAuth) {
window.location.assign(loginUrl);
return true;
return false;
}
return false;
}, [ isAuth, loginUrl ]);
return true;
}, [ isAuth, loginUrl, profileState?.error?.status, router ]);
}
......@@ -5,12 +5,9 @@ import React from 'react';
import starFilledIcon from 'icons/star_filled.svg';
import starOutlineIcon from 'icons/star_outline.svg';
import type { ResourceError } from 'lib/api/resources';
import { resourceKey } from 'lib/api/resources';
import { getResourceKey } from 'lib/api/useApiQuery';
import useIsAccountActionAllowed from 'lib/hooks/useIsAccountActionAllowed';
import usePreventFocusAfterModalClosing from 'lib/hooks/usePreventFocusAfterModalClosing';
import useRedirectIfNotAuth from 'lib/hooks/useRedirectIfNotAuth';
import useToast from 'lib/hooks/useToast';
import WatchlistAddModal from 'ui/watchlist/AddressModal/AddressModal';
import DeleteAddressModal from 'ui/watchlist/DeleteAddressModal';
......@@ -25,33 +22,14 @@ const AddressFavoriteButton = ({ className, hash, watchListId }: Props) => {
const deleteModalProps = useDisclosure();
const queryClient = useQueryClient();
const router = useRouter();
const toast = useToast();
const redirectIfNotAuth = useRedirectIfNotAuth();
const profileState = queryClient.getQueryState<unknown, ResourceError<{ message: string }>>([ resourceKey('user_info') ]);
const isAccountActionAllowed = useIsAccountActionAllowed();
const handleClick = React.useCallback(() => {
if (profileState?.error?.status === 403) {
const isUnverifiedEmail = profileState.error.payload?.message.includes('Unverified email');
if (isUnverifiedEmail) {
toast({
position: 'top-right',
title: 'Error',
description: 'Unable to add address to watch list. Please go to the watch list page instead.',
status: 'error',
variant: 'subtle',
isClosable: true,
});
return;
}
}
if (redirectIfNotAuth()) {
if (!isAccountActionAllowed({ pathname: '/account/watchlist' })) {
return;
}
watchListId ? deleteModalProps.onOpen() : addModalProps.onOpen();
}, [ profileState, redirectIfNotAuth, watchListId, deleteModalProps, addModalProps, toast ]);
}, [ isAccountActionAllowed, watchListId, deleteModalProps, addModalProps ]);
const handleAddOrDeleteSuccess = React.useCallback(async() => {
const queryKey = getResourceKey('address', { pathParams: { hash: router.query.hash?.toString() } });
......
......@@ -7,6 +7,7 @@ import type { VerifiedAddress, TokenInfoApplication, TokenInfoApplications, Veri
import appConfig from 'configs/app/config';
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo';
import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthToken';
import getQueryParamString from 'lib/router/getQueryParamString';
import { TOKEN_INFO_APPLICATION, VERIFIED_ADDRESS } from 'stubs/account';
......@@ -54,6 +55,7 @@ const VerifiedAddresses = () => {
},
},
});
const profileQuery = useFetchProfileInfo();
const isLoading = addressesQuery.isPlaceholderData || applicationsQuery.isPlaceholderData;
......@@ -99,8 +101,8 @@ const VerifiedAddresses = () => {
});
}, [ queryClient ]);
if (addressesQuery.isError && addressesQuery.error.status === 403) {
throw new Error('Unverified email error', { cause: addressesQuery.error });
if (profileQuery.isError && profileQuery.error.status === 403) {
throw new Error('Unverified email error', { cause: profileQuery.error });
}
const addButton = (
......
......@@ -4,6 +4,7 @@ import React from 'react';
import appConfig from 'configs/app/config';
import iconArrow from 'icons/arrows/east-mini.svg';
import useIsAccountActionAllowed from 'lib/hooks/useIsAccountActionAllowed';
import getQueryParamString from 'lib/router/getQueryParamString';
import PrivateTagMenuItem from './PrivateTagMenuItem';
......@@ -19,6 +20,7 @@ const AddressActions = ({ isLoading }: Props) => {
const hash = getQueryParamString(router.query.hash);
const isTokenPage = router.pathname === '/token/[hash]';
const isAccountActionAllowed = useIsAccountActionAllowed();
return (
<Menu>
......@@ -36,9 +38,9 @@ const AddressActions = ({ isLoading }: Props) => {
</Skeleton>
<MenuList minWidth="180px" zIndex="popover">
{ isTokenPage && appConfig.contractInfoApi.endpoint && appConfig.adminServiceApi.endpoint && appConfig.isAccountSupported &&
<TokenInfoMenuItem py={ 2 } px={ 4 } hash={ hash }/> }
<PrivateTagMenuItem py={ 2 } px={ 4 } hash={ hash }/>
<PublicTagMenuItem py={ 2 } px={ 4 } hash={ hash }/>
<TokenInfoMenuItem py={ 2 } px={ 4 } hash={ hash } onBeforeClick={ isAccountActionAllowed }/> }
<PrivateTagMenuItem py={ 2 } px={ 4 } hash={ hash } onBeforeClick={ isAccountActionAllowed }/>
<PublicTagMenuItem py={ 2 } px={ 4 } hash={ hash } onBeforeClick={ isAccountActionAllowed }/>
</MenuList>
</Menu>
);
......
import { MenuItem, Icon, chakra, useDisclosure } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import type { Route } from 'nextjs-routes';
import React from 'react';
import type { Address } from 'types/api/address';
import iconPrivateTags from 'icons/privattags.svg';
import { getResourceKey } from 'lib/api/useApiQuery';
import useRedirectIfNotAuth from 'lib/hooks/useRedirectIfNotAuth';
import PrivateTagModal from 'ui/privateTags/AddressModal/AddressModal';
interface Props {
className?: string;
hash: string;
onBeforeClick: (route: Route) => boolean;
}
const PrivateTagMenuItem = ({ className, hash }: Props) => {
const PrivateTagMenuItem = ({ className, hash, onBeforeClick }: Props) => {
const modal = useDisclosure();
const queryClient = useQueryClient();
const redirectIfNotAuth = useRedirectIfNotAuth();
const queryKey = getResourceKey('address', { pathParams: { hash } });
const addressData = queryClient.getQueryData<Address>(queryKey);
const handleClick = React.useCallback(() => {
if (redirectIfNotAuth()) {
if (!onBeforeClick({ pathname: '/account/tag_address' })) {
return;
}
modal.onOpen();
}, [ modal, redirectIfNotAuth ]);
}, [ modal, onBeforeClick ]);
const handleAddPrivateTag = React.useCallback(async() => {
await queryClient.refetchQueries({ queryKey });
......
import { MenuItem, Icon, chakra } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import type { Route } from 'nextjs-routes';
import React from 'react';
import iconPublicTags from 'icons/publictags.svg';
import useRedirectIfNotAuth from 'lib/hooks/useRedirectIfNotAuth';
interface Props {
className?: string;
hash: string;
onBeforeClick: (route: Route) => boolean;
}
const PublicTagMenuItem = ({ className, hash }: Props) => {
const PublicTagMenuItem = ({ className, hash, onBeforeClick }: Props) => {
const router = useRouter();
const redirectIfNotAuth = useRedirectIfNotAuth();
const handleClick = React.useCallback(() => {
if (redirectIfNotAuth()) {
if (!onBeforeClick({ pathname: '/account/public_tags_request' })) {
return;
}
router.push({ pathname: '/account/public_tags_request', query: { address: hash } });
}, [ hash, redirectIfNotAuth, router ]);
}, [ hash, onBeforeClick, router ]);
return (
<MenuItem className={ className }onClick={ handleClick }>
......
import { MenuItem, Icon, chakra, useDisclosure } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import type { Route } from 'nextjs-routes';
import React from 'react';
import appConfig from 'configs/app/config';
import iconEdit from 'icons/edit.svg';
import useApiQuery from 'lib/api/useApiQuery';
import useHasAccount from 'lib/hooks/useHasAccount';
import useRedirectIfNotAuth from 'lib/hooks/useRedirectIfNotAuth';
import AddressVerificationModal from 'ui/addressVerification/AddressVerificationModal';
interface Props {
className?: string;
hash: string;
onBeforeClick: (route: Route) => boolean;
}
const TokenInfoMenuItem = ({ className, hash }: Props) => {
const TokenInfoMenuItem = ({ className, hash, onBeforeClick }: Props) => {
const router = useRouter();
const modal = useDisclosure();
const redirectIfNotAuth = useRedirectIfNotAuth();
const isAuth = useHasAccount();
const verifiedAddressesQuery = useApiQuery('verified_addresses', {
......@@ -40,12 +40,12 @@ const TokenInfoMenuItem = ({ className, hash }: Props) => {
});
const handleAddAddressClick = React.useCallback(() => {
if (redirectIfNotAuth()) {
if (!onBeforeClick({ pathname: '/account/verified_addresses' })) {
return;
}
modal.onOpen();
}, [ modal, redirectIfNotAuth ]);
}, [ modal, onBeforeClick ]);
const handleAddApplicationClick = React.useCallback(async() => {
router.push({ pathname: '/account/verified_addresses', query: { address: hash } });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment