Commit bccb1574 authored by tom's avatar tom

deprecate env and remove old code

parent e2f7ab6d
import type { Feature } from './types';
import stripTrailingSlash from 'lib/stripTrailingSlash';
import app from '../app';
import { getEnvValue } from '../utils';
const authUrl = stripTrailingSlash(getEnvValue('NEXT_PUBLIC_AUTH_URL') || app.baseUrl);
const logoutUrl = (() => {
try {
const envUrl = getEnvValue('NEXT_PUBLIC_LOGOUT_URL');
const auth0ClientId = getEnvValue('NEXT_PUBLIC_AUTH0_CLIENT_ID');
const returnUrl = authUrl + '/auth/logout';
if (!envUrl || !auth0ClientId) {
throw Error();
}
const url = new URL(envUrl);
url.searchParams.set('client_id', auth0ClientId);
url.searchParams.set('returnTo', returnUrl);
return url.toString();
} catch (error) {
return;
}
})();
const title = 'My account';
// TODO @tom2drum add condition for recaptcha
const config: Feature<{ authUrl: string; logoutUrl: string }> = (() => {
if (
getEnvValue('NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED') === 'true' &&
authUrl &&
logoutUrl
) {
return Object.freeze({
title,
isEnabled: true,
authUrl,
logoutUrl,
});
}
const config: Feature<{ isEnabled: true }> = (() => {
return Object.freeze({
title,
isEnabled: false,
isEnabled: getEnvValue('NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED') === 'true',
});
})();
......
......@@ -147,4 +147,15 @@ function printDeprecationWarning(envsMap: Record<string, string>) {
console.warn('The NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR and NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND variables are now deprecated and will be removed in the next release. Please migrate to the NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG variable.');
console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗\n');
}
if (
envsMap.NEXT_PUBLIC_AUTH0_CLIENT_ID ||
envsMap.NEXT_PUBLIC_AUTH_URL ||
envsMap.NEXT_PUBLIC_LOGOUT_URL
) {
console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗');
// eslint-disable-next-line max-len
console.warn('The NEXT_PUBLIC_AUTH0_CLIENT_ID, NEXT_PUBLIC_AUTH_URL and NEXT_PUBLIC_LOGOUT_URL variables are now deprecated and will be removed in the next release.');
console.log('❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗❗\n');
}
}
......@@ -10,4 +10,4 @@
| NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER | `boolean` | Set to false if network doesn't have gas tracker | - | `true` | `false` | - | v1.25.0 | Replaced by NEXT_PUBLIC_GAS_TRACKER_ENABLED |
| NEXT_PUBLIC_NETWORK_GOVERNANCE_TOKEN_SYMBOL | `string` | Network governance token symbol | - | - | `GNO` | v1.12.0 | v1.29.0 | Replaced by NEXT_PUBLIC_NETWORK_SECONDARY_COIN_SYMBOL |
| NEXT_PUBLIC_SWAP_BUTTON_URL | `string` | Application ID in the marketplace or website URL | - | - | `uniswap` | v1.24.0 | v1.31.0 | Replaced by NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS |
| NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME | `boolean` | Set to false if average block time is useless for the network | - | `true` | `false` | v1.0.x+ | v1.35.0 | Replaces by NEXT_PUBLIC_HOMEPAGE_STATS
\ No newline at end of file
| NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME | `boolean` | Set to false if average block time is useless for the network | - | `true` | `false` | v1.0.x+ | v1.35.0 | Replaces by NEXT_PUBLIC_HOMEPAGE_STATS |
\ No newline at end of file
......@@ -340,9 +340,9 @@ Settings for meta tags, OG tags and SEO
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED | `boolean` | Set to true if network has account feature | Required | - | `true` | v1.0.x+ |
| NEXT_PUBLIC_AUTH0_CLIENT_ID | `string` | Client id for [Auth0](https://auth0.com/) provider | Required | - | `<your-secret>` | v1.0.x+ |
| NEXT_PUBLIC_AUTH_URL | `string` | Account auth base url; it is used for building login URL (`${ NEXT_PUBLIC_AUTH_URL }/auth/auth0`) and logout return URL (`${ NEXT_PUBLIC_AUTH_URL }/auth/logout`); if not provided the base app URL will be used instead | Required | - | `https://blockscout.com` | v1.0.x+ |
| NEXT_PUBLIC_LOGOUT_URL | `string` | Account logout url. Required if account is supported for the app instance. | Required | - | `https://blockscoutcom.us.auth0.com/v2/logout` | v1.0.x+ |
| NEXT_PUBLIC_AUTH0_CLIENT_ID | `string` | **DEPRECATED** Client id for [Auth0](https://auth0.com/) provider | Required | - | `<your-secret>` | v1.0.x+ |
| NEXT_PUBLIC_AUTH_URL | `string` | **DEPRECATED** Account auth base url; it is used for building login URL (`${ NEXT_PUBLIC_AUTH_URL }/auth/auth0`) and logout return URL (`${ NEXT_PUBLIC_AUTH_URL }/auth/logout`); if not provided the base app URL will be used instead | Required | - | `https://blockscout.com` | v1.0.x+ |
| NEXT_PUBLIC_LOGOUT_URL | `string` | **DEPRECATED** Account logout url. Required if account is supported for the app instance. | Required | - | `https://blockscoutcom.us.auth0.com/v2/logout` | v1.0.x+ |
&nbsp;
......
......@@ -6,6 +6,7 @@ export enum NAMES {
NAV_BAR_COLLAPSED='nav_bar_collapsed',
API_TOKEN='_explorer_key',
INVALID_SESSION='invalid_session',
// TODO @tom2drum remove everything about unverified email
CONFIRM_EMAIL_PAGE_VIEWED='confirm_email_page_viewed',
TXS_SORT='txs_sort',
COLOR_MODE='chakra-ui-color-mode',
......
import { useQueryClient } from '@tanstack/react-query';
import React from 'react';
import type { UserInfo } from 'types/api/account';
import { resourceKey } from 'lib/api/resources';
import useLoginUrl from 'lib/hooks/useLoginUrl';
// TODO @tom2drum remove this hook
export default function useIsAccountActionAllowed() {
const queryClient = useQueryClient();
const profileData = queryClient.getQueryData<UserInfo>([ resourceKey('user_info') ]);
const isAuth = Boolean(profileData);
const loginUrl = useLoginUrl();
return React.useCallback(() => {
if (!loginUrl) {
return false;
}
if (!isAuth) {
window.location.assign(loginUrl);
return false;
}
return true;
}, [ isAuth, loginUrl ]);
}, []);
}
import { useRouter } from 'next/router';
import { route } from 'nextjs-routes';
import config from 'configs/app';
const feature = config.features.account;
// TODO @tom2drum remove this hook
export default function useLoginUrl() {
const router = useRouter();
return feature.isEnabled ?
feature.authUrl + route({ pathname: '/auth/auth0', query: { path: router.asPath } }) :
undefined;
}
......@@ -5,12 +5,10 @@ import type { NavItemInternal, NavItem, NavGroupItem } from 'types/client/naviga
import config from 'configs/app';
import { rightLineArrow } from 'lib/html-entities';
import UserAvatar from 'ui/shared/UserAvatar';
interface ReturnType {
mainNavItems: Array<NavItem | NavGroupItem>;
accountNavItems: Array<NavItem>;
profileItem: NavItem;
}
export function isGroupItem(item: NavItem | NavGroupItem): item is NavGroupItem {
......@@ -297,14 +295,6 @@ export default function useNavItems(): ReturnType {
},
].filter(Boolean);
// TODO @tom2drum remove this
const profileItem = {
text: 'My profile',
nextRoute: { pathname: '/auth/profile' as const },
iconComponent: UserAvatar,
isActive: pathname === '/auth/profile',
};
return { mainNavItems, accountNavItems, profileItem };
return { mainNavItems, accountNavItems };
}, [ pathname ]);
}
import * as Sentry from '@sentry/react';
import { useQueryClient } from '@tanstack/react-query';
import React from 'react';
import { resourceKey } from 'lib/api/resources';
import type { ResourceError } from 'lib/api/resources';
import * as cookies from 'lib/cookies';
import useLoginUrl from 'lib/hooks/useLoginUrl';
import useProfileQuery from 'ui/snippets/auth/useProfileQuery';
// TODO @tom2drum remove or revise this hook
export default function useRedirectForInvalidAuthToken() {
const queryClient = useQueryClient();
const state = queryClient.getQueryState<unknown, ResourceError>([ resourceKey('user_info') ]);
const errorStatus = state?.error?.status;
const loginUrl = useLoginUrl();
const profileQuery = useProfileQuery();
const errorStatus = profileQuery.error?.status;
React.useEffect(() => {
if (errorStatus === 401) {
const apiToken = cookies.get(cookies.NAMES.API_TOKEN);
if (apiToken && loginUrl) {
if (apiToken) {
Sentry.captureException(new Error('Invalid API token'), { tags: { source: 'invalid_api_token' } });
window.location.assign(loginUrl);
cookies.remove(cookies.NAMES.API_TOKEN);
window.location.assign('/');
}
}
}, [ errorStatus, loginUrl ]);
}, [ errorStatus ]);
}
......@@ -52,7 +52,6 @@ const OG_TYPE_DICT: Record<Route['pathname'], OGPageType> = {
'/mud-worlds': 'Root page',
// service routes, added only to make typescript happy
'/login': 'Regular page',
'/sprite': 'Regular page',
'/api/metrics': 'Regular page',
'/api/monitoring/invalid-api-schema': 'Regular page',
......
......@@ -56,7 +56,6 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/mud-worlds': DEFAULT_TEMPLATE,
// service routes, added only to make typescript happy
'/login': DEFAULT_TEMPLATE,
'/sprite': DEFAULT_TEMPLATE,
'/api/metrics': DEFAULT_TEMPLATE,
'/api/monitoring/invalid-api-schema': DEFAULT_TEMPLATE,
......
......@@ -52,7 +52,6 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/mud-worlds': '%network_name% MUD worlds list',
// service routes, added only to make typescript happy
'/login': '%network_name% login',
'/sprite': '%network_name% SVG sprite',
'/api/metrics': '%network_name% node API prometheus metrics',
'/api/monitoring/invalid-api-schema': '%network_name% node API prometheus metrics',
......
......@@ -50,7 +50,6 @@ export const PAGE_TYPE_DICT: Record<Route['pathname'], string> = {
'/mud-worlds': 'MUD worlds',
// service routes, added only to make typescript happy
'/login': 'Login',
'/sprite': 'Sprite',
'/api/metrics': 'Node API: Prometheus metrics',
'/api/monitoring/invalid-api-schema': 'Node API: Prometheus metrics',
......
......@@ -6,7 +6,6 @@ import type { RollupType } from 'types/client/rollup';
import type { Route } from 'nextjs-routes';
import config from 'configs/app';
import isNeedProxy from 'lib/api/isNeedProxy';
const rollupFeature = config.features.rollup;
const adBannerFeature = config.features.adsBanner;
import type * as metadata from 'lib/metadata';
......@@ -254,17 +253,6 @@ export const dataAvailability: GetServerSideProps<Props> = async(context) => {
return base(context);
};
export const login: GetServerSideProps<Props> = async(context) => {
if (!isNeedProxy()) {
return {
notFound: true,
};
}
return base(context);
};
export const dev: GetServerSideProps<Props> = async(context) => {
if (!config.app.isDev) {
return {
......
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
import { route } from 'nextjs-routes';
import config from 'configs/app';
import { DAY } from 'lib/consts';
import * as cookies from 'lib/cookies';
export function account(req: NextRequest) {
......@@ -25,37 +22,7 @@ export function account(req: NextRequest) {
const isProfileRoute = req.nextUrl.pathname.includes('/auth/profile');
if ((isAccountRoute || isProfileRoute)) {
const authUrl = feature.authUrl + route({ pathname: '/auth/auth0', query: { path: req.nextUrl.pathname } });
return NextResponse.redirect(authUrl);
}
}
// if user hasn't confirmed email yet
if (req.cookies.get(cookies.NAMES.INVALID_SESSION)) {
// if user has both cookies, make redirect to logout
if (apiTokenCookie) {
// yes, we could have checked that the current URL is not the logout URL, but we hadn't
// logout URL is always external URL in auth0.com sub-domain
// at least we hope so
const res = NextResponse.redirect(feature.logoutUrl);
res.cookies.delete(cookies.NAMES.CONFIRM_EMAIL_PAGE_VIEWED); // reset cookie to show email verification page again
return res;
}
// if user hasn't seen email verification page, make redirect to it
if (!req.cookies.get(cookies.NAMES.CONFIRM_EMAIL_PAGE_VIEWED)) {
if (!req.nextUrl.pathname.includes('/auth/unverified-email')) {
const url = config.app.baseUrl + route({ pathname: '/auth/unverified-email' });
const res = NextResponse.redirect(url);
res.cookies.set({
name: cookies.NAMES.CONFIRM_EMAIL_PAGE_VIEWED,
value: 'true',
expires: Date.now() + 7 * DAY,
});
return res;
}
return NextResponse.redirect(config.app.baseUrl);
}
}
}
......@@ -45,7 +45,6 @@ declare module "nextjs-routes" {
| StaticRoute<"/gas-tracker">
| StaticRoute<"/graphiql">
| StaticRoute<"/">
| StaticRoute<"/login">
| StaticRoute<"/mud-worlds">
| DynamicRoute<"/name-domains/[name]", { "name": string }>
| StaticRoute<"/name-domains">
......
......@@ -32,7 +32,8 @@ export default function fetchFactory(
httpLogger.logger.info({
message: 'API fetch via Next.js proxy',
url,
// headers,
headers,
init,
});
const body = (() => {
......
import type { NextPage } from 'next';
import React from 'react';
import PageNextJs from 'nextjs/PageNextJs';
import Login from 'ui/pages/Login';
const Page: NextPage = () => {
return (
<PageNextJs pathname="/login">
<Login/>
</PageNextJs>
);
};
export default Page;
export { login as getServerSideProps } from 'nextjs/getServerSideProps';
......@@ -2,11 +2,11 @@ import { Heading } from '@chakra-ui/react';
import React from 'react';
import config from 'configs/app';
import useHasAccount from 'lib/hooks/useHasAccount';
import LatestOptimisticDeposits from 'ui/home/latestDeposits/LatestOptimisticDeposits';
import LatestTxs from 'ui/home/LatestTxs';
import LatestWatchlistTxs from 'ui/home/LatestWatchlistTxs';
import TabsWithScroll from 'ui/shared/Tabs/TabsWithScroll';
import useAuth from 'ui/snippets/auth/useIsAuth';
import LatestArbitrumDeposits from './latestDeposits/LatestArbitrumDeposits';
......@@ -17,15 +17,15 @@ const TAB_LIST_PROPS = {
};
const TransactionsHome = () => {
const hasAccount = useHasAccount();
if ((rollupFeature.isEnabled && (rollupFeature.type === 'optimistic' || rollupFeature.type === 'arbitrum')) || hasAccount) {
const isAuth = useAuth();
if ((rollupFeature.isEnabled && (rollupFeature.type === 'optimistic' || rollupFeature.type === 'arbitrum')) || isAuth) {
const tabs = [
{ id: 'txn', title: 'Latest txn', component: <LatestTxs/> },
rollupFeature.isEnabled && rollupFeature.type === 'optimistic' &&
{ id: 'deposits', title: 'Deposits (L1→L2 txn)', component: <LatestOptimisticDeposits/> },
rollupFeature.isEnabled && rollupFeature.type === 'arbitrum' &&
{ id: 'deposits', title: 'Deposits (L1→L2 txn)', component: <LatestArbitrumDeposits/> },
hasAccount && { id: 'watchlist', title: 'Watch list', component: <LatestWatchlistTxs/> },
isAuth && { id: 'watchlist', title: 'Watch list', component: <LatestWatchlistTxs/> },
].filter(Boolean);
return (
<>
......
import React from 'react';
import { test, expect } from 'playwright/lib';
import Login from './Login';
test.fixme('has feature text', async({ render, mockFeatures }) => {
await mockFeatures([
[ 'test_value', 'kitty' ],
]);
const component = await render(<Login/>);
const featureText = component.getByText('kitty');
await expect(featureText).toBeVisible();
});
import { VStack, Textarea, Button, Alert, AlertTitle, AlertDescription, Code, Flex, Box } from '@chakra-ui/react';
import * as Sentry from '@sentry/react';
import mixpanel from 'mixpanel-browser';
import type { ChangeEvent } from 'react';
import React from 'react';
import config from 'configs/app';
import * as cookies from 'lib/cookies';
import useFeatureValue from 'lib/growthbook/useFeatureValue';
import useGradualIncrement from 'lib/hooks/useGradualIncrement';
import useToast from 'lib/hooks/useToast';
import PageTitle from 'ui/shared/Page/PageTitle';
// TODO @tom2drum delete this page
{ /* will be deleted when we fix login in preview CI stands */ }
const Login = () => {
const toast = useToast();
const [ num, setNum ] = useGradualIncrement(0);
const testFeature = useFeatureValue('test_value', 'fallback');
const [ isFormVisible, setFormVisibility ] = React.useState(false);
const [ token, setToken ] = React.useState('');
React.useEffect(() => {
const token = cookies.get(cookies.NAMES.API_TOKEN);
setFormVisibility(Boolean(!token && config.features.account.isEnabled));
// throw new Error('Test error');
}, []);
const checkSentry = React.useCallback(() => {
Sentry.captureException(new Error('Test error'), { tags: { source: 'test' } });
}, []);
const checkMixpanel = React.useCallback(() => {
mixpanel.track('Test event', { my_prop: 'foo bar' });
}, []);
const handleTokenChange = React.useCallback((event: ChangeEvent<HTMLTextAreaElement>) => {
setToken(event.target.value);
}, []);
const handleSetTokenClick = React.useCallback(() => {
cookies.set(cookies.NAMES.API_TOKEN, token);
setToken('');
toast({
position: 'top-right',
title: 'Success 🥳',
description: 'Successfully set cookie',
status: 'success',
variant: 'subtle',
isClosable: true,
onCloseComplete: () => {
setFormVisibility(false);
},
});
}, [ toast, token ]);
const handleNumIncrement = React.useCallback(() => {
for (let index = 0; index < 5; index++) {
setNum(5);
}
}, [ setNum ]);
return (
<VStack gap={ 4 } alignItems="flex-start" maxW="1000px">
<PageTitle title="Login page 😂"/>
{ isFormVisible && (
<>
<Alert status="error" flexDirection="column" alignItems="flex-start">
<AlertTitle fontSize="md">
!!! Temporary solution for authentication on localhost !!!
</AlertTitle>
<AlertDescription mt={ 3 }>
To Sign in go to production instance first, sign in there, copy obtained API token from cookie
<Code ml={ 1 }>{ cookies.NAMES.API_TOKEN }</Code> and paste it in the form below. After submitting the form you should be successfully
authenticated in current environment
</AlertDescription>
</Alert>
<Textarea value={ token } onChange={ handleTokenChange } placeholder="API token"/>
<Button onClick={ handleSetTokenClick }>Set cookie</Button>
</>
) }
<Flex columnGap={ 2 }>
<Button colorScheme="red" onClick={ checkSentry }>Check Sentry</Button>
<Button colorScheme="teal" onClick={ checkMixpanel }>Check Mixpanel</Button>
</Flex>
<Flex columnGap={ 2 } alignItems="center">
<Box w="50px" textAlign="center">{ num }</Box>
<Button onClick={ handleNumIncrement } size="sm">add</Button>
</Flex>
<Box>Test feature value: <b>{ testFeature.isLoading ? 'loading...' : JSON.stringify(testFeature.value) }</b></Box>
</VStack>
);
};
export default Login;
......@@ -4,7 +4,6 @@ import React from 'react';
import type { Screen } from 'ui/snippets/auth/types';
import config from 'configs/app';
import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo';
import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthToken';
import MyProfileEmail from 'ui/myProfile/MyProfileEmail';
import MyProfileWallet from 'ui/myProfile/MyProfileWallet';
......@@ -12,12 +11,13 @@ import ContentLoader from 'ui/shared/ContentLoader';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import PageTitle from 'ui/shared/Page/PageTitle';
import AuthModal from 'ui/snippets/auth/AuthModal';
import useProfileQuery from 'ui/snippets/auth/useProfileQuery';
const MyProfile = () => {
const [ authInitialScreen, setAuthInitialScreen ] = React.useState<Screen>();
const authModal = useDisclosure();
const profileQuery = useFetchProfileInfo();
const profileQuery = useProfileQuery();
useRedirectForInvalidAuthToken();
const handleAddWalletClick = React.useCallback(() => {
......
......@@ -3,12 +3,12 @@ import React from 'react';
import type { FormSubmitResult } from 'ui/publicTags/submit/types';
import useApiQuery from 'lib/api/useApiQuery';
import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo';
import PublicTagsSubmitForm from 'ui/publicTags/submit/PublicTagsSubmitForm';
import PublicTagsSubmitResult from 'ui/publicTags/submit/PublicTagsSubmitResult';
import ContentLoader from 'ui/shared/ContentLoader';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import PageTitle from 'ui/shared/Page/PageTitle';
import useProfileQuery from 'ui/snippets/auth/useProfileQuery';
type Screen = 'form' | 'result' | 'initializing' | 'error';
......@@ -17,8 +17,8 @@ const PublicTagsSubmit = () => {
const [ screen, setScreen ] = React.useState<Screen>('initializing');
const [ submitResult, setSubmitResult ] = React.useState<FormSubmitResult>();
const userQuery = useFetchProfileInfo();
const configQuery = useApiQuery('address_metadata_tag_types', { queryOptions: { enabled: !userQuery.isLoading } });
const profileQuery = useProfileQuery();
const configQuery = useApiQuery('address_metadata_tag_types', { queryOptions: { enabled: !profileQuery.isLoading } });
React.useEffect(() => {
if (!configQuery.isPending) {
......@@ -38,7 +38,7 @@ const PublicTagsSubmit = () => {
case 'error':
return <DataFetchAlert/>;
case 'form':
return <PublicTagsSubmitForm config={ configQuery.data } onSubmitResult={ handleFormSubmitResult } userInfo={ userQuery.data }/>;
return <PublicTagsSubmitForm config={ configQuery.data } onSubmitResult={ handleFormSubmitResult } userInfo={ profileQuery.data }/>;
case 'result':
return <PublicTagsSubmitResult data={ submitResult }/>;
default:
......
......@@ -5,7 +5,6 @@ import React from 'react';
import type { RoutedTab } from 'ui/shared/Tabs/types';
import config from 'configs/app';
import useHasAccount from 'lib/hooks/useHasAccount';
import useIsMobile from 'lib/hooks/useIsMobile';
import useNewTxsSocket from 'lib/hooks/useNewTxsSocket';
import getNetworkValidationActionText from 'lib/networks/getNetworkValidationActionText';
......@@ -16,6 +15,7 @@ import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import useIsAuth from 'ui/snippets/auth/useIsAuth';
import TxsStats from 'ui/txs/TxsStats';
import TxsWatchlist from 'ui/txs/TxsWatchlist';
import TxsWithFrontendSorting from 'ui/txs/TxsWithFrontendSorting';
......@@ -88,7 +88,7 @@ const Transactions = () => {
const { num, socketAlert } = useNewTxsSocket();
const hasAccount = useHasAccount();
const isAuth = useIsAuth();
const tabs: Array<RoutedTab> = [
{
......@@ -129,7 +129,7 @@ const Transactions = () => {
/>
),
},
hasAccount ? {
isAuth ? {
id: 'watchlist',
title: 'Watch list',
component: <TxsWatchlist query={ txsWatchlistQuery }/>,
......
......@@ -7,7 +7,6 @@ import type { VerifiedAddress, TokenInfoApplication, TokenInfoApplications, Veri
import config from 'configs/app';
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo';
import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthToken';
import { PAGE_TYPE_DICT } from 'lib/mixpanel/getPageType';
import getQueryParamString from 'lib/router/getQueryParamString';
......@@ -17,6 +16,7 @@ import AccountPageDescription from 'ui/shared/AccountPageDescription';
import DataListDisplay from 'ui/shared/DataListDisplay';
import PageTitle from 'ui/shared/Page/PageTitle';
import AdminSupportText from 'ui/shared/texts/AdminSupportText';
import useProfileQuery from 'ui/snippets/auth/useProfileQuery';
import TokenInfoForm from 'ui/tokenInfo/TokenInfoForm';
import VerifiedAddressesEmailAlert from 'ui/verifiedAddresses/VerifiedAddressesEmailAlert';
import VerifiedAddressesListItem from 'ui/verifiedAddresses/VerifiedAddressesListItem';
......@@ -39,20 +39,20 @@ const VerifiedAddresses = () => {
const modalProps = useDisclosure();
const queryClient = useQueryClient();
const userInfoQuery = useFetchProfileInfo();
const profileQuery = useProfileQuery();
const addressesQuery = useApiQuery('verified_addresses', {
pathParams: { chainId: config.chain.id },
queryOptions: {
placeholderData: { verifiedAddresses: Array(3).fill(VERIFIED_ADDRESS) },
enabled: Boolean(userInfoQuery.data?.email),
enabled: Boolean(profileQuery.data?.email),
},
});
const applicationsQuery = useApiQuery('token_info_applications', {
pathParams: { chainId: config.chain.id, id: undefined },
queryOptions: {
placeholderData: { submissions: Array(3).fill(TOKEN_INFO_APPLICATION) },
enabled: Boolean(userInfoQuery.data?.email),
enabled: Boolean(profileQuery.data?.email),
select: (data) => {
return {
...data,
......@@ -63,7 +63,7 @@ const VerifiedAddresses = () => {
});
const isLoading = addressesQuery.isPlaceholderData || applicationsQuery.isPlaceholderData;
const userWithoutEmail = userInfoQuery.data && !userInfoQuery.data.email;
const userWithoutEmail = profileQuery.data && !profileQuery.data.email;
const handleGoBack = React.useCallback(() => {
setSelectedAddress(undefined);
......@@ -219,7 +219,7 @@ const VerifiedAddresses = () => {
<AdminSupportText mt={ 5 }/>
</AccountPageDescription>
<DataListDisplay
isError={ userInfoQuery.isError || addressesQuery.isError || applicationsQuery.isError }
isError={ profileQuery.isError || addressesQuery.isError || applicationsQuery.isError }
items={ addressesQuery.data?.verifiedAddresses }
content={ content }
emptyText=""
......
......@@ -5,12 +5,12 @@ import React from 'react';
import type { ItemProps } from './types';
import config from 'configs/app';
import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo';
import useIsAccountActionAllowed from 'lib/hooks/useIsAccountActionAllowed';
import * as mixpanel from 'lib/mixpanel/index';
import getQueryParamString from 'lib/router/getQueryParamString';
import Menu from 'ui/shared/chakra/Menu';
import IconSvg from 'ui/shared/IconSvg';
import useProfileQuery from 'ui/snippets/auth/useProfileQuery';
import MetadataUpdateMenuItem from './items/MetadataUpdateMenuItem';
import PrivateTagMenuItem from './items/PrivateTagMenuItem';
......@@ -32,7 +32,7 @@ const AccountActionsMenu = ({ isLoading, className, showUpdateMetadataItem }: Pr
const isTxPage = router.pathname === '/tx/[hash]';
const isAccountActionAllowed = useIsAccountActionAllowed();
const userInfoQuery = useFetchProfileInfo();
const profileQuery = useProfileQuery();
const handleButtonClick = React.useCallback(() => {
mixpanel.logEvent(mixpanel.EventTypes.PAGE_WIDGET, { Type: 'Address actions (more button)' });
......@@ -42,7 +42,7 @@ const AccountActionsMenu = ({ isLoading, className, showUpdateMetadataItem }: Pr
return null;
}
const userWithoutEmail = userInfoQuery.data && !userInfoQuery.data.email;
const userWithoutEmail = profileQuery.data && !profileQuery.data.email;
const items = [
{
......
......@@ -6,10 +6,10 @@ import type { ItemProps } from '../types';
import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery';
import useHasAccount from 'lib/hooks/useHasAccount';
import { PAGE_TYPE_DICT } from 'lib/mixpanel/getPageType';
import AddressVerificationModal from 'ui/addressVerification/AddressVerificationModal';
import IconSvg from 'ui/shared/IconSvg';
import useIsAuth from 'ui/snippets/auth/useIsAuth';
import ButtonItem from '../parts/ButtonItem';
import MenuItem from '../parts/MenuItem';
......@@ -17,7 +17,7 @@ import MenuItem from '../parts/MenuItem';
const TokenInfoMenuItem = ({ className, hash, onBeforeClick, type }: ItemProps) => {
const router = useRouter();
const modal = useDisclosure();
const isAuth = useHasAccount();
const isAuth = useIsAuth();
const verifiedAddressesQuery = useApiQuery('verified_addresses', {
pathParams: { chainId: config.chain.id },
......
import { SkeletonCircle, Image } from '@chakra-ui/react';
import React from 'react';
import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies';
import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo';
import IconSvg from 'ui/shared/IconSvg';
interface Props {
size: number;
fallbackIconSize?: number;
}
// TODO @tom2drum remove this component
const UserAvatar = ({ size, fallbackIconSize = 20 }: Props) => {
const appProps = useAppContext();
const hasAuth = Boolean(cookies.get(cookies.NAMES.API_TOKEN, appProps.cookies));
const [ isImageLoadError, setImageLoadError ] = React.useState(false);
const { data, isFetched } = useFetchProfileInfo();
const sizeString = `${ size }px`;
const handleImageLoadError = React.useCallback(() => {
setImageLoadError(true);
}, []);
if (hasAuth && !isFetched) {
return <SkeletonCircle h={ sizeString } w={ sizeString }/>;
}
return (
<Image
flexShrink={ 0 }
src={ data?.avatar }
alt={ `Profile picture of ${ data?.name || data?.nickname || '' }` }
boxSize={ `${ size }px` }
borderRadius="full"
overflow="hidden"
fallback={ isImageLoadError || !data?.avatar ? <IconSvg name="profile" boxSize={ `${ fallbackIconSize }px` }/> : undefined }
onError={ handleImageLoadError }
/>
);
};
export default React.memo(UserAvatar);
......@@ -3,7 +3,6 @@ import React from 'react';
import type { Screen, ScreenSuccess } from './types';
import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo';
import IconSvg from 'ui/shared/IconSvg';
import AuthModalScreenConnectWallet from './screens/AuthModalScreenConnectWallet';
......@@ -12,6 +11,7 @@ import AuthModalScreenOtpCode from './screens/AuthModalScreenOtpCode';
import AuthModalScreenSelectMethod from './screens/AuthModalScreenSelectMethod';
import AuthModalScreenSuccessEmail from './screens/AuthModalScreenSuccessEmail';
import AuthModalScreenSuccessWallet from './screens/AuthModalScreenSuccessWallet';
import useProfileQuery from './useProfileQuery';
interface Props {
initialScreen: Screen;
......@@ -20,7 +20,7 @@ interface Props {
const AuthModal = ({ initialScreen, onClose }: Props) => {
const [ steps, setSteps ] = React.useState<Array<Screen>>([ initialScreen ]);
const profileQuery = useFetchProfileInfo();
const profileQuery = useProfileQuery();
const onNextStep = React.useCallback((screen: Screen) => {
setSteps((prev) => [ ...prev, screen ]);
......
......@@ -2,12 +2,11 @@ import config from 'configs/app';
import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies';
import useFetchProfileInfo from './useFetchProfileInfo';
import useProfileQuery from './useProfileQuery';
// TODO @tom2drum move to auth
export default function useHasAccount() {
export default function useAuth() {
const appProps = useAppContext();
const profileQuery = useFetchProfileInfo();
const profileQuery = useProfileQuery();
if (!config.features.account.isEnabled) {
return false;
......
import useApiQuery from 'lib/api/useApiQuery';
import * as cookies from 'lib/cookies';
// TODO @tom2drum move to auth
export default function useFetchProfileInfo() {
export default function useProfileQuery() {
return useApiQuery('user_info', {
queryOptions: {
refetchOnMount: false,
......
......@@ -2,9 +2,9 @@ import { Box, Flex, Text, VStack, useColorModeValue } from '@chakra-ui/react';
import { animate, motion, useMotionValue } from 'framer-motion';
import React, { useCallback } from 'react';
import useHasAccount from 'lib/hooks/useHasAccount';
import useNavItems, { isGroupItem } from 'lib/hooks/useNavItems';
import IconSvg from 'ui/shared/IconSvg';
import useIsAuth from 'ui/snippets/auth/useIsAuth';
import NavLink from '../vertical/NavLink';
import NavLinkGroup from './NavLinkGroup';
......@@ -35,7 +35,7 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => {
animate(subX, DRAWER_WIDTH, { ease: 'easeInOut', onComplete: () => setOpenedGroupIndex(-1) });
}, [ mainX, subX ]);
const hasAccount = useHasAccount();
const isAuth = useIsAuth();
const iconColor = useColorModeValue('blue.600', 'blue.300');
......@@ -73,7 +73,7 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => {
}) }
</VStack>
</Box>
{ hasAccount && (
{ isAuth && (
<Box
as="nav"
mt={ 3 }
......
......@@ -4,10 +4,10 @@ import React from 'react';
import config from 'configs/app';
import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies';
import useHasAccount from 'lib/hooks/useHasAccount';
import useNavItems, { isGroupItem } from 'lib/hooks/useNavItems';
import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps';
import IconSvg from 'ui/shared/IconSvg';
import useIsAuth from 'ui/snippets/auth/useIsAuth';
import NetworkLogo from 'ui/snippets/networkMenu/NetworkLogo';
import NetworkMenu from 'ui/snippets/networkMenu/NetworkMenu';
......@@ -30,7 +30,7 @@ const NavigationDesktop = () => {
const { mainNavItems, accountNavItems } = useNavItems();
const hasAccount = useHasAccount();
const isAuth = useIsAuth();
const [ isCollapsed, setCollapsedState ] = React.useState<boolean | undefined>(isNavBarCollapsed);
......@@ -97,7 +97,7 @@ const NavigationDesktop = () => {
}) }
</VStack>
</Box>
{ hasAccount && (
{ isAuth && (
<Box as="nav" borderTopWidth="1px" borderColor="divider" w="100%" mt={ 3 } pt={ 3 }>
<VStack as="ul" spacing="1" alignItems="flex-start">
{ accountNavItems.map((item) => <NavLink key={ item.text } item={ item } isCollapsed={ isCollapsed }/>) }
......
......@@ -5,9 +5,9 @@ import React from 'react';
import type { Screen } from 'ui/snippets/auth/types';
import config from 'configs/app';
import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo';
import Popover from 'ui/shared/chakra/Popover';
import AuthModal from 'ui/snippets/auth/AuthModal';
import useProfileQuery from 'ui/snippets/auth/useProfileQuery';
import useSignInWithWallet from 'ui/snippets/auth/useSignInWithWallet';
import UserProfileButton from './UserProfileButton';
......@@ -27,7 +27,7 @@ const UserProfileDesktop = ({ buttonSize, buttonVariant = 'header' }: Props) =>
const authModal = useDisclosure();
const profileMenu = useDisclosure();
const profileQuery = useFetchProfileInfo();
const profileQuery = useProfileQuery();
const signInWithWallet = useSignInWithWallet({});
const handleProfileButtonClick = React.useCallback(() => {
......
......@@ -5,8 +5,8 @@ import React from 'react';
import type { Screen } from 'ui/snippets/auth/types';
import config from 'configs/app';
import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo';
import AuthModal from 'ui/snippets/auth/AuthModal';
import useProfileQuery from 'ui/snippets/auth/useProfileQuery';
import useSignInWithWallet from 'ui/snippets/auth/useSignInWithWallet';
import UserProfileButton from './UserProfileButton';
......@@ -21,7 +21,7 @@ const UserProfileMobile = () => {
const authModal = useDisclosure();
const profileMenu = useDisclosure();
const profileQuery = useFetchProfileInfo();
const profileQuery = useProfileQuery();
const signInWithWallet = useSignInWithWallet({});
const handleProfileButtonClick = React.useCallback(() => {
......
......@@ -15,12 +15,12 @@ import type { WatchlistAddress, WatchlistErrors } from 'types/api/account';
import type { ResourceErrorAccount } from 'lib/api/resources';
import useApiFetch from 'lib/api/useApiFetch';
import getErrorMessage from 'lib/getErrorMessage';
import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo';
import { ADDRESS_REGEXP } from 'lib/validations/address';
import AddressInput from 'ui/shared/AddressInput';
import CheckboxInput from 'ui/shared/CheckboxInput';
import TagInput from 'ui/shared/TagInput';
import AuthModal from 'ui/snippets/auth/AuthModal';
import useProfileQuery from 'ui/snippets/auth/useProfileQuery';
import AddressFormNotifications from './AddressFormNotifications';
......@@ -73,7 +73,7 @@ type Checkboxes = 'notification' |
const AddressForm: React.FC<Props> = ({ data, onSuccess, setAlertVisible, isAdd }) => {
const [ pending, setPending ] = useState(false);
const profileQuery = useFetchProfileInfo();
const profileQuery = useProfileQuery();
const userWithoutEmail = profileQuery.data && !profileQuery.data.email;
const authModal = useDisclosure();
......
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