Commit 67272a9c authored by tom goriunov's avatar tom goriunov Committed by GitHub

Merge pull request #130 from blockscout/lighthouse-report-fixes

some improvements from lighthouse report
parents 4a32e7de 6bc9c5f8
import { withSentry } from '@sentry/nextjs';
import type { NextApiRequest, NextApiResponse } from 'next';
import fetch from 'lib/api/fetch';
......@@ -5,28 +6,39 @@ import getUrlWithNetwork from 'lib/api/getUrlWithNetwork';
type Methods = 'GET' | 'POST' | 'PUT' | 'DELETE';
export default function handler<TRes, TErrRes>(getUrl: (_req: NextApiRequest) => string, allowedMethods: Array<Methods>) {
return async(_req: NextApiRequest, res: NextApiResponse<TRes | TErrRes>) => {
if (_req.method && allowedMethods.includes(_req.method as Methods)) {
const isBodyDisallowed = _req.method === 'GET' || _req.method === 'HEAD';
export default function createHandler(getUrl: (_req: NextApiRequest) => string, allowedMethods: Array<Methods>) {
const handler = async(_req: NextApiRequest, res: NextApiResponse) => {
if (!_req.method || !allowedMethods.includes(_req.method as Methods)) {
res.setHeader('Allow', allowedMethods);
res.status(405).end(`Method ${ _req.method } Not Allowed`);
return;
}
const url = getUrlWithNetwork(_req, `/api${ getUrl(_req) }`);
const response = await fetch(url, {
method: _req.method,
body: isBodyDisallowed ? undefined : _req.body,
});
const isBodyDisallowed = _req.method === 'GET' || _req.method === 'HEAD';
if (response.status !== 200) {
const error = await response.json() as { errors: TErrRes };
res.status(500).json(error?.errors || {} as TErrRes);
return;
}
const url = getUrlWithNetwork(_req, `/api${ getUrl(_req) }`);
const response = await fetch(url, {
method: _req.method,
body: isBodyDisallowed ? undefined : _req.body,
});
const data = await response.json() as TRes;
if (response.status === 200) {
const data = await response.json();
res.status(200).json(data);
} else {
res.setHeader('Allow', allowedMethods);
res.status(405).end(`Method ${ _req.method } Not Allowed`);
return;
}
let responseError;
try {
const error = await response.json() as { errors: unknown };
responseError = error?.errors || {};
} catch (error) {
responseError = { statusText: response.statusText, status: response.status };
}
res.status(500).json(responseError);
};
return withSentry(handler);
}
......@@ -7,7 +7,14 @@ const headers = require('./configs/nextjs/headers');
const moduleExports = {
include: path.resolve(__dirname, 'icons'),
reactStrictMode: true,
webpack(config) {
webpack(config, { webpack }) {
config.plugins.push(
new webpack.DefinePlugin({
__SENTRY_DEBUG__: false,
__SENTRY_TRACING__: false,
}),
);
return config;
},
async redirects() {
......
......@@ -7,7 +7,7 @@ import theme from 'theme';
class MyDocument extends Document {
render() {
return (
<Html>
<Html lang="en">
<Head>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap"
......
import type { ApiKeys, ApiKeyErrors } from 'types/api/account';
import handler from 'lib/api/handler';
const apiKeysHandler = handler<ApiKeys, ApiKeyErrors>(() => '/account/v1/user/api_keys', [ 'GET', 'POST' ]);
const apiKeysHandler = handler(() => '/account/v1/user/api_keys', [ 'GET', 'POST' ]);
export default apiKeysHandler;
import type { CustomAbis, CustomAbiErrors } from 'types/api/account';
import handler from 'lib/api/handler';
const customAbiHandler = handler<CustomAbis, CustomAbiErrors>(() => '/account/v1/user/custom_abis', [ 'GET', 'POST' ]);
const customAbiHandler = handler(() => '/account/v1/user/custom_abis', [ 'GET', 'POST' ]);
export default customAbiHandler;
import type { AddressTags, AddressTagErrors } from 'types/api/account';
import handler from 'lib/api/handler';
const addressHandler = handler<AddressTags, AddressTagErrors>(() => '/account/v1/user/tags/address', [ 'GET', 'POST' ]);
const addressHandler = handler(() => '/account/v1/user/tags/address', [ 'GET', 'POST' ]);
export default addressHandler;
import type { TransactionTags, TransactionTagErrors } from 'types/api/account';
import handler from 'lib/api/handler';
const transactionHandler = handler<TransactionTags, TransactionTagErrors>(() => '/account/v1/user/tags/transaction', [ 'GET', 'POST' ]);
const transactionHandler = handler(() => '/account/v1/user/tags/transaction', [ 'GET', 'POST' ]);
export default transactionHandler;
import type { UserInfo } from 'types/api/account';
import handler from 'lib/api/handler';
const profileHandler = handler<UserInfo, unknown>(() => '/account/v1/user/info', [ 'GET' ]);
const profileHandler = handler(() => '/account/v1/user/info', [ 'GET' ]);
export default profileHandler;
import type { PublicTags, PublicTagErrors } from 'types/api/account';
import handler from 'lib/api/handler';
const publicKeysHandler = handler<PublicTags, PublicTagErrors>(() => '/account/v1/user/public_tags', [ 'GET', 'POST' ]);
const publicKeysHandler = handler(() => '/account/v1/user/public_tags', [ 'GET', 'POST' ]);
export default publicKeysHandler;
import type { NextApiRequest } from 'next';
import type { WatchlistAddresses, WatchlistErrors } from 'types/api/account';
import handler from 'lib/api/handler';
const getUrl = (req: NextApiRequest) => {
return `/account/v1/user/watchlist/${ req.query.id }`;
};
const addressEditHandler = handler<WatchlistAddresses, WatchlistErrors>(getUrl, [ 'DELETE', 'PUT' ]);
const addressEditHandler = handler(getUrl, [ 'DELETE', 'PUT' ]);
export default addressEditHandler;
import type { WatchlistAddresses, WatchlistErrors } from 'types/api/account';
import handler from 'lib/api/handler';
const watchlistHandler = handler<WatchlistAddresses, WatchlistErrors>(() => '/account/v1/user/watchlist', [ 'GET', 'POST' ]);
const watchlistHandler = handler(() => '/account/v1/user/watchlist', [ 'GET', 'POST' ]);
export default watchlistHandler;
......@@ -9,10 +9,10 @@ import useIsMobile from 'lib/hooks/useIsMobile';
import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps';
const SOCIAL_LINKS = [
{ link: process.env.NEXT_PUBLIC_FOOTER_GITHUB_LINK, icon: ghIcon },
{ link: process.env.NEXT_PUBLIC_FOOTER_TWITTER_LINK, icon: twIcon },
{ link: process.env.NEXT_PUBLIC_FOOTER_TELEGRAM_LINK, icon: tgIcon },
{ link: process.env.NEXT_PUBLIC_FOOTER_STAKING_LINK, icon: statsIcon },
{ link: process.env.NEXT_PUBLIC_FOOTER_GITHUB_LINK, icon: ghIcon, label: 'Github link' },
{ link: process.env.NEXT_PUBLIC_FOOTER_TWITTER_LINK, icon: twIcon, label: 'Twitter link' },
{ link: process.env.NEXT_PUBLIC_FOOTER_TELEGRAM_LINK, icon: tgIcon, label: 'Telegram link' },
{ link: process.env.NEXT_PUBLIC_FOOTER_STAKING_LINK, icon: statsIcon, label: 'Staking analytic link' },
].filter(({ link }) => link !== undefined);
const BLOCKSCOUT_VERSION = process.env.NEXT_PUBLIC_BLOCKSCOUT_VERSION;
......@@ -51,7 +51,7 @@ const NavFooter = ({ isCollapsed }: Props) => {
<Stack direction={ isCollapsed ? 'column' : 'row' }>
{ SOCIAL_LINKS.map(sl => {
return (
<Link href={ sl.link } key={ sl.link } variant="secondary" w={ 5 } h={ 5 }>
<Link href={ sl.link } key={ sl.link } variant="secondary" w={ 5 } h={ 5 } aria-label={ sl.label }>
<Icon as={ sl.icon } boxSize={ 5 }/>
</Link>
);
......
......@@ -24,6 +24,7 @@ const NetworkLogo = ({ isCollapsed, onClick }: Props) => {
overflow="hidden"
onClick={ onClick }
{ ...getDefaultTransitionProps({ transitionProperty: 'width' }) }
aria-label="Link to main page"
>
<Icon
as={ logoIcon }
......
......@@ -26,6 +26,8 @@ const NetworkMenuButton = ({ isMobile, isActive, onClick }: Props, ref: React.Fo
borderRadius="base"
backgroundColor={ isActive ? bgColorMobile : 'none' }
onClick={ onClick }
aria-label="Network menu"
aria-roledescription="menu"
>
<Icon
as={ networksIcon }
......
......@@ -56,7 +56,7 @@ const WatchListItem = ({ item, onEditClick, onDeleteClick }: Props) => {
<Flex alignItems="center" justifyContent="space-between" mt={ 6 } w="100%">
<HStack spacing={ 3 }>
<Text fontSize="sm" fontWeight={ 500 }>Email notification</Text>
<Switch colorScheme="blue" size="md" isChecked={ notificationEnabled } onChange={ onSwitch }/>
<Switch colorScheme="blue" size="md" isChecked={ notificationEnabled } onChange={ onSwitch } aria-label="Email notification"/>
</HStack>
<TableItemActionButtons onDeleteClick={ onItemDeleteClick } onEditClick={ onItemEditClick }/>
</Flex>
......
......@@ -84,6 +84,7 @@ const WatchlistTableItem = ({ item, onEditClick, onDeleteClick }: Props) => {
isChecked={ notificationEnabled }
onChange={ onSwitch }
isDisabled={ switchDisabled }
aria-label="Email notification"
/>
</Td>
<Td>
......
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