Commit c58a3fdf authored by tom goriunov's avatar tom goriunov Committed by GitHub

refactor page layout components (#1087)

* update csv export text

* fix pw tests

* base layout and customization for home page and search results page

* refactor rest of the pages

* refactor AppError

* move header padding to layout

* move nextjs utils to separate folder

* combine PageServer and Page

* make useQueryClientConfig hook

* add tests

* remove unused var

* fix envs for pw

* fix pw tests
parent e36711f2
...@@ -2,4 +2,6 @@ node_modules ...@@ -2,4 +2,6 @@ node_modules
node_modules_linux node_modules_linux
playwright/envs.js playwright/envs.js
deploy/tools/envs-validator/index.js deploy/tools/envs-validator/index.js
\ No newline at end of file deploy/tools/feature-reporter/build/**
deploy/tools/feature-reporter/index.js
\ No newline at end of file
...@@ -199,7 +199,23 @@ module.exports = { ...@@ -199,7 +199,23 @@ module.exports = {
groups: [ groups: [
'module', 'module',
'/types/', '/types/',
[ '/^configs/', '/^data/', '/^deploy/', '/^icons/', '/^jest/', '/^lib/', '/^mocks/', '/^pages/', '/^playwright/', '/^stubs/', '/^theme/', '/^ui/' ], [
'/^nextjs/',
],
[
'/^configs/',
'/^data/',
'/^deploy/',
'/^icons/',
'/^jest/',
'/^lib/',
'/^mocks/',
'/^pages/',
'/^playwright/',
'/^stubs/',
'/^theme/',
'/^ui/',
],
[ 'parent', 'sibling', 'index' ], [ 'parent', 'sibling', 'index' ],
], ],
alphabetize: { order: 'asc', ignoreCase: true }, alphabetize: { order: 'asc', ignoreCase: true },
......
...@@ -46,6 +46,9 @@ NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://localhost:3000/marketplace-config.jso ...@@ -46,6 +46,9 @@ NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://localhost:3000/marketplace-config.jso
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://localhost:3000/marketplace-submit-form NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://localhost:3000/marketplace-submit-form
NEXT_PUBLIC_IS_L2_NETWORK=false NEXT_PUBLIC_IS_L2_NETWORK=false
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3100
NEXT_PUBLIC_LOGOUT_URL=https://blockscoutcom.us.auth0.com/v2/logout
NEXT_PUBLIC_AUTH0_CLIENT_ID=xxx
NEXT_PUBLIC_STATS_API_HOST=https://localhost:3004 NEXT_PUBLIC_STATS_API_HOST=https://localhost:3004
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://localhost:3005 NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://localhost:3005
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://localhost:3006 NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://localhost:3006
......
# Set of ENVs for Playwright components tests # Set of ENVs for Playwright components tests
#
# be aware that ALL ENVs should be present in order to make a correct variables list for the browser
# leave empty values for the ones that are not required
# app configuration # app configuration
NEXT_PUBLIC_APP_PROTOCOL=http NEXT_PUBLIC_APP_PROTOCOL=http
...@@ -45,11 +48,15 @@ NEXT_PUBLIC_APP_INSTANCE=pw ...@@ -45,11 +48,15 @@ NEXT_PUBLIC_APP_INSTANCE=pw
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://localhost:3000/marketplace-config.json NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://localhost:3000/marketplace-config.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://localhost:3000/marketplace-submit-form NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://localhost:3000/marketplace-submit-form
NEXT_PUBLIC_IS_L2_NETWORK=false NEXT_PUBLIC_IS_L2_NETWORK=false
NEXT_PUBLIC_L1_BASE_URL=
NEXT_PUBLIC_L2_WITHDRAWAL_URL=
NEXT_PUBLIC_AD_BANNER_PROVIDER=slise NEXT_PUBLIC_AD_BANNER_PROVIDER=slise
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3100 NEXT_PUBLIC_AUTH_URL=http://localhost:3100
NEXT_PUBLIC_LOGOUT_URL=https://blockscoutcom.us.auth0.com/v2/logout NEXT_PUBLIC_LOGOUT_URL=https://blockscoutcom.us.auth0.com/v2/logout
NEXT_PUBLIC_AUTH0_CLIENT_ID=xxx
NEXT_PUBLIC_STATS_API_HOST=https://localhost:3004 NEXT_PUBLIC_STATS_API_HOST=https://localhost:3004
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://localhost:3005 NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://localhost:3005
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://localhost:3006 NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://localhost:3006
NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx
NEXT_PUBLIC_HAS_BEACON_CHAIN=
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
"module": "CommonJS", "module": "CommonJS",
"outDir": "./build", "outDir": "./build",
"paths": { "paths": {
"nextjs-routes": ["./types/nextjs-routes.d.ts"], "nextjs-routes": ["./nextjs/nextjs-routes.d.ts"],
} }
}, },
"include": [ "../../../configs/app/index.ts" ], "include": [ "../../../configs/app/index.ts" ],
......
import { QueryClient } from '@tanstack/react-query';
import React from 'react';
import getErrorObjPayload from 'lib/errors/getErrorObjPayload';
import getErrorObjStatusCode from 'lib/errors/getErrorObjStatusCode';
export default function useQueryClientConfig() {
const [ queryClient ] = React.useState(() => new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
retry: (failureCount, error) => {
const errorPayload = getErrorObjPayload<{ status: number }>(error);
const status = errorPayload?.status || getErrorObjStatusCode(error);
if (status && status >= 400 && status < 500) {
// don't do retry for client error responses
return false;
}
return failureCount < 2;
},
useErrorBoundary: (error) => {
const status = getErrorObjStatusCode(error);
// don't catch error for "Too many requests" response
return status === 429;
},
},
},
}));
return queryClient;
}
import React, { createContext, useContext } from 'react'; import React, { createContext, useContext } from 'react';
import type { Props as PageProps } from 'lib/next/getServerSideProps'; import type { Props as PageProps } from 'nextjs/getServerSideProps';
type Props = { type Props = {
children: React.ReactNode; children: React.ReactNode;
......
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
......
import type { Route } from 'nextjs-routes';
import type { ApiData } from './types'; import type { ApiData } from './types';
import type { Route } from 'nextjs-routes';
import generate from './generate'; import generate from './generate';
interface TestCase<R extends Route> { interface TestCase<R extends Route> {
......
import type { Route } from 'nextjs-routes';
import type { ApiData, Metadata } from './types'; import type { ApiData, Metadata } from './types';
import type { Route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import getNetworkTitle from 'lib/networks/getNetworkTitle';
......
import type { Route } from 'nextjs-routes';
import type { ApiData } from './types'; import type { ApiData } from './types';
import type { Route } from 'nextjs-routes';
import generate from './generate'; import generate from './generate';
export default function update<R extends Route>(route: R, apiData: ApiData<R>) { export default function update<R extends Route>(route: R, apiData: ApiData<R>) {
......
import type { NextRequest } from 'next/server'; import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server'; import { NextResponse } from 'next/server';
import generateCspPolicy from 'lib/csp/generateCspPolicy'; import generateCspPolicy from 'nextjs/csp/generateCspPolicy';
import * as middlewares from 'lib/next/middlewares/index'; import * as middlewares from 'nextjs/middlewares/index';
const cspPolicy = generateCspPolicy(); const cspPolicy = generateCspPolicy();
......
...@@ -3,14 +3,15 @@ const withTM = require('next-transpile-modules')([ ...@@ -3,14 +3,15 @@ const withTM = require('next-transpile-modules')([
'swagger-client', 'swagger-client',
'swagger-ui-react', 'swagger-ui-react',
]); ]);
const path = require('path');
const withRoutes = require('nextjs-routes/config')({ const withRoutes = require('nextjs-routes/config')({
outDir: 'types', outDir: 'nextjs',
}); });
const path = require('path');
const headers = require('./configs/nextjs/headers'); const headers = require('./nextjs/headers');
const redirects = require('./configs/nextjs/redirects'); const redirects = require('./nextjs/redirects');
const rewrites = require('./configs/nextjs/rewrites'); const rewrites = require('./nextjs/rewrites');
const moduleExports = withTM({ const moduleExports = withTM({
include: path.resolve(__dirname, 'icons'), include: path.resolve(__dirname, 'icons'),
......
import Head from 'next/head'; import Head from 'next/head';
import type { Route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Route } from 'nextjs-routes';
import useAdblockDetect from 'lib/hooks/useAdblockDetect';
import useConfigSentry from 'lib/hooks/useConfigSentry';
import useGetCsrfToken from 'lib/hooks/useGetCsrfToken';
import * as metadata from 'lib/metadata'; import * as metadata from 'lib/metadata';
import * as mixpanel from 'lib/mixpanel';
type Props = Route & { type Props = Route & {
children: React.ReactNode; children: React.ReactNode;
} }
const PageServer = (props: Props) => { const PageNextJs = (props: Props) => {
const { title, description } = metadata.generate(props); const { title, description } = metadata.generate(props);
useGetCsrfToken();
useAdblockDetect();
useConfigSentry();
const isMixpanelInited = mixpanel.useInit();
mixpanel.useLogPageView(isMixpanelInited);
return ( return (
<> <>
<Head> <Head>
...@@ -22,4 +34,4 @@ const PageServer = (props: Props) => { ...@@ -22,4 +34,4 @@ const PageServer = (props: Props) => {
); );
}; };
export default React.memo(PageServer); export default React.memo(PageNextJs);
import type { NextRequest } from 'next/server'; import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server'; import { NextResponse } from 'next/server';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
......
import type { NextPage } from 'next';
// eslint-disable-next-line @typescript-eslint/ban-types
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
getLayout?: (page: React.ReactElement) => React.ReactNode;
}
import { compile } from 'path-to-regexp'; import { compile } from 'path-to-regexp';
import config from 'configs/app'; import config from 'configs/app';
import { RESOURCES } from 'lib/api/resources';
import type { ApiResource, ResourceName } from 'lib/api/resources';
import { RESOURCES } from './resources'; export default function buildUrl(
import type { ApiResource, ResourceName } from './resources';
export default function buildUrlNode(
_resource: ApiResource | ResourceName, _resource: ApiResource | ResourceName,
pathParams?: Record<string, string | undefined>, pathParams?: Record<string, string | undefined>,
queryParams?: Record<string, string | number | undefined>, queryParams?: Record<string, string | number | undefined>,
......
...@@ -4,7 +4,8 @@ import type { NextApiRequestCookies } from 'next/dist/server/api-utils'; ...@@ -4,7 +4,8 @@ import type { NextApiRequestCookies } from 'next/dist/server/api-utils';
import type { RequestInit, Response } from 'node-fetch'; import type { RequestInit, Response } from 'node-fetch';
import nodeFetch from 'node-fetch'; import nodeFetch from 'node-fetch';
import { httpLogger } from 'lib/api/logger'; import { httpLogger } from 'nextjs/utils/logger';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
export default function fetchFactory( export default function fetchFactory(
......
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import type { NextPageWithLayout } from 'nextjs/types';
import PageNextJs from 'nextjs/PageNextJs';
import AppError from 'ui/shared/AppError/AppError'; import AppError from 'ui/shared/AppError/AppError';
import Page from 'ui/shared/Page/Page'; import LayoutError from 'ui/shared/layout/LayoutError';
const error = new Error('Not found', { cause: { status: 404 } });
const Page: NextPageWithLayout = () => {
return (
<PageNextJs pathname="/404">
<AppError error={ error }/>
</PageNextJs>
);
};
const Custom404 = () => { Page.getLayout = function getLayout(page: React.ReactElement) {
return ( return (
<PageServer pathname="/404"> <LayoutError>
<Page> { page }
<AppError statusCode={ 404 } mt="50px"/> </LayoutError>
</Page>
</PageServer>
); );
}; };
export default Custom404; export default Page;
import type { ChakraProps } from '@chakra-ui/react'; import type { ChakraProps } from '@chakra-ui/react';
import * as Sentry from '@sentry/react'; import * as Sentry from '@sentry/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import type { AppProps } from 'next/app'; import type { AppProps } from 'next/app';
import React, { useState } from 'react'; import React from 'react';
import type { NextPageWithLayout } from 'nextjs/types';
import config from 'configs/app'; import config from 'configs/app';
import useQueryClientConfig from 'lib/api/useQueryClientConfig';
import { AppContextProvider } from 'lib/contexts/app'; import { AppContextProvider } from 'lib/contexts/app';
import { ChakraProvider } from 'lib/contexts/chakra'; import { ChakraProvider } from 'lib/contexts/chakra';
import { ScrollDirectionProvider } from 'lib/contexts/scrollDirection'; import { ScrollDirectionProvider } from 'lib/contexts/scrollDirection';
import getErrorCauseStatusCode from 'lib/errors/getErrorCauseStatusCode';
import getErrorObjPayload from 'lib/errors/getErrorObjPayload';
import getErrorObjStatusCode from 'lib/errors/getErrorObjStatusCode';
import useConfigSentry from 'lib/hooks/useConfigSentry';
import { SocketProvider } from 'lib/socket/context'; import { SocketProvider } from 'lib/socket/context';
import theme from 'theme'; import theme from 'theme';
import AppError from 'ui/shared/AppError/AppError'; import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary';
import AppErrorTooManyRequests from 'ui/shared/AppError/AppErrorTooManyRequests';
import ErrorBoundary from 'ui/shared/ErrorBoundary';
import GoogleAnalytics from 'ui/shared/GoogleAnalytics'; import GoogleAnalytics from 'ui/shared/GoogleAnalytics';
import Layout from 'ui/shared/layout/Layout';
import 'lib/setLocale'; import 'lib/setLocale';
function MyApp({ Component, pageProps }: AppProps) { type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout;
useConfigSentry(); }
const [ queryClient ] = useState(() => new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
retry: (failureCount, error) => {
const errorPayload = getErrorObjPayload<{ status: number }>(error);
const status = errorPayload?.status || getErrorObjStatusCode(error);
if (status && status >= 400 && status < 500) {
// don't do retry for client error responses
return false;
}
return failureCount < 2;
},
useErrorBoundary: (error) => {
const status = getErrorObjStatusCode(error);
// don't catch error for "Too many requests" response
return status === 429;
},
},
},
}));
const renderErrorScreen = React.useCallback((error?: Error) => {
const statusCode = getErrorCauseStatusCode(error) || getErrorObjStatusCode(error);
const styles: ChakraProps = { const ERROR_SCREEN_STYLES: ChakraProps = {
h: '100vh', h: '100vh',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
alignItems: 'flex-start', alignItems: 'flex-start',
justifyContent: 'center', justifyContent: 'center',
width: 'fit-content', width: 'fit-content',
maxW: '800px', maxW: '800px',
margin: '0 auto', margin: '0 auto',
p: { base: 4, lg: 0 }, p: { base: 4, lg: 0 },
}; };
if (statusCode === 429) { function MyApp({ Component, pageProps }: AppPropsWithLayout) {
return <AppErrorTooManyRequests { ...styles }/>;
}
return ( const queryClient = useQueryClientConfig();
<AppError
statusCode={ statusCode || 500 }
{ ...styles }
/>
);
}, []);
const handleError = React.useCallback((error: Error) => { const handleError = React.useCallback((error: Error) => {
Sentry.captureException(error); Sentry.captureException(error);
}, []); }, []);
const getLayout = Component.getLayout ?? ((page) => <Layout>{ page }</Layout>);
return ( return (
<ChakraProvider theme={ theme } cookies={ pageProps.cookies }> <ChakraProvider theme={ theme } cookies={ pageProps.cookies }>
<ErrorBoundary renderErrorScreen={ renderErrorScreen } onError={ handleError }> <AppErrorBoundary
{ ...ERROR_SCREEN_STYLES }
onError={ handleError }
>
<AppContextProvider pageProps={ pageProps }> <AppContextProvider pageProps={ pageProps }>
<QueryClientProvider client={ queryClient }> <QueryClientProvider client={ queryClient }>
<ScrollDirectionProvider> <ScrollDirectionProvider>
<SocketProvider url={ `${ config.api.socket }${ config.api.basePath }/socket/v2` }> <SocketProvider url={ `${ config.api.socket }${ config.api.basePath }/socket/v2` }>
<Component { ...pageProps }/> { getLayout(<Component { ...pageProps }/>) }
</SocketProvider> </SocketProvider>
</ScrollDirectionProvider> </ScrollDirectionProvider>
<ReactQueryDevtools/> <ReactQueryDevtools/>
<GoogleAnalytics/> <GoogleAnalytics/>
</QueryClientProvider> </QueryClientProvider>
</AppContextProvider> </AppContextProvider>
</ErrorBoundary> </AppErrorBoundary>
</ChakraProvider> </ChakraProvider>
); );
} }
......
...@@ -3,8 +3,9 @@ import type { DocumentContext } from 'next/document'; ...@@ -3,8 +3,9 @@ import type { DocumentContext } from 'next/document';
import Document, { Html, Head, Main, NextScript } from 'next/document'; import Document, { Html, Head, Main, NextScript } from 'next/document';
import React from 'react'; import React from 'react';
import * as serverTiming from 'nextjs/utils/serverTiming';
import config from 'configs/app'; import config from 'configs/app';
import * as serverTiming from 'lib/next/serverTiming';
import theme from 'theme'; import theme from 'theme';
class MyDocument extends Document { class MyDocument extends Document {
......
...@@ -21,10 +21,11 @@ import type { GetServerSideProps } from 'next'; ...@@ -21,10 +21,11 @@ import type { GetServerSideProps } from 'next';
import NextErrorComponent from 'next/error'; import NextErrorComponent from 'next/error';
import React from 'react'; import React from 'react';
import type { Props as ServerSidePropsCommon } from 'nextjs/getServerSideProps';
import { base as getServerSidePropsCommon } from 'nextjs/getServerSideProps';
import sentryConfig from 'configs/sentry/nextjs'; import sentryConfig from 'configs/sentry/nextjs';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import type { Props as ServerSidePropsCommon } from 'lib/next/getServerSideProps';
import { base as getServerSidePropsCommon } from 'lib/next/getServerSideProps';
type Props = ServerSidePropsCommon & { type Props = ServerSidePropsCommon & {
statusCode: number; statusCode: number;
......
...@@ -2,21 +2,18 @@ import type { NextPage } from 'next'; ...@@ -2,21 +2,18 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const ApiKeys = dynamic(() => import('ui/pages/ApiKeys'), { ssr: false }); const ApiKeys = dynamic(() => import('ui/pages/ApiKeys'), { ssr: false });
const ApiKeysPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/account/api-key"> <PageNextJs pathname="/account/api-key">
<Page> <ApiKeys/>
<ApiKeys/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default ApiKeysPage; export default Page;
export { account as getServerSideProps } from 'lib/next/getServerSideProps'; export { account as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,21 +2,18 @@ import type { NextPage } from 'next'; ...@@ -2,21 +2,18 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const CustomAbi = dynamic(() => import('ui/pages/CustomAbi'), { ssr: false }); const CustomAbi = dynamic(() => import('ui/pages/CustomAbi'), { ssr: false });
const CustomAbiPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/account/custom-abi"> <PageNextJs pathname="/account/custom-abi">
<Page> <CustomAbi/>
<CustomAbi/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default CustomAbiPage; export default Page;
export { account as getServerSideProps } from 'lib/next/getServerSideProps'; export { account as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,21 +2,18 @@ import type { NextPage } from 'next'; ...@@ -2,21 +2,18 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const PublicTags = dynamic(() => import('ui/pages/PublicTags'), { ssr: false }); const PublicTags = dynamic(() => import('ui/pages/PublicTags'), { ssr: false });
const PublicTagsPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/account/public-tags-request"> <PageNextJs pathname="/account/public-tags-request">
<Page> <PublicTags/>
<PublicTags/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default PublicTagsPage; export default Page;
export { account as getServerSideProps } from 'lib/next/getServerSideProps'; export { account as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,21 +2,18 @@ import type { NextPage } from 'next'; ...@@ -2,21 +2,18 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const PrivateTags = dynamic(() => import('ui/pages/PrivateTags'), { ssr: false }); const PrivateTags = dynamic(() => import('ui/pages/PrivateTags'), { ssr: false });
const PrivateTagsPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/account/tag-address"> <PageNextJs pathname="/account/tag-address">
<Page> <PrivateTags/>
<PrivateTags/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default PrivateTagsPage; export default Page;
export { account as getServerSideProps } from 'lib/next/getServerSideProps'; export { account as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,21 +2,18 @@ import type { NextPage } from 'next'; ...@@ -2,21 +2,18 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const VerifiedAddresses = dynamic(() => import('ui/pages/VerifiedAddresses'), { ssr: false }); const VerifiedAddresses = dynamic(() => import('ui/pages/VerifiedAddresses'), { ssr: false });
const VerifiedAddressesPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/account/verified-addresses"> <PageNextJs pathname="/account/verified-addresses">
<Page> <VerifiedAddresses/>
<VerifiedAddresses/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default VerifiedAddressesPage; export default Page;
export { verifiedAddresses as getServerSideProps } from 'lib/next/getServerSideProps'; export { verifiedAddresses as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,21 +2,18 @@ import type { NextPage } from 'next'; ...@@ -2,21 +2,18 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const WatchList = dynamic(() => import('ui/pages/Watchlist'), { ssr: false }); const WatchList = dynamic(() => import('ui/pages/Watchlist'), { ssr: false });
const WatchListPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/account/watchlist"> <PageNextJs pathname="/account/watchlist">
<Page> <WatchList/>
<WatchList/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default WatchListPage; export default Page;
export { account as getServerSideProps } from 'lib/next/getServerSideProps'; export { account as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,21 +2,18 @@ import type { NextPage } from 'next'; ...@@ -2,21 +2,18 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const Accounts = dynamic(() => import('ui/pages/Accounts'), { ssr: false }); const Accounts = dynamic(() => import('ui/pages/Accounts'), { ssr: false });
const AccountsPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/accounts"> <PageNextJs pathname="/accounts">
<Page> <Accounts/>
<Accounts/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default AccountsPage; export default Page;
export { base as getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'nextjs/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import React from 'react'; import React from 'react';
import type { Props } from 'lib/next/getServerSideProps'; import type { Props } from 'nextjs/getServerSideProps';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import ContractVerification from 'ui/pages/ContractVerification'; import ContractVerification from 'ui/pages/ContractVerification';
import Page from 'ui/shared/Page/Page';
const ContractVerificationPage: NextPage<Props> = (props: Props) => { const Page: NextPage<Props> = (props: Props) => {
return ( return (
<PageServer pathname="/address/[hash]/contract-verification" query={ props }> <PageNextJs pathname="/address/[hash]/contract-verification" query={ props }>
<Page> <ContractVerification/>
<ContractVerification/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default ContractVerificationPage; export default Page;
export { base as getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,22 +2,19 @@ import type { NextPage } from 'next'; ...@@ -2,22 +2,19 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import type { Props } from 'lib/next/getServerSideProps'; import type { Props } from 'nextjs/getServerSideProps';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const Address = dynamic(() => import('ui/pages/Address'), { ssr: false }); const Address = dynamic(() => import('ui/pages/Address'), { ssr: false });
const AddressPage: NextPage<Props> = (props: Props) => { const Page: NextPage<Props> = (props: Props) => {
return ( return (
<PageServer pathname="/address/[hash]" query={ props }> <PageNextJs pathname="/address/[hash]" query={ props }>
<Page> <Address/>
<Address/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default AddressPage; export default Page;
export { base as getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'nextjs/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import SwaggerUI from 'ui/apiDocs/SwaggerUI'; import SwaggerUI from 'ui/apiDocs/SwaggerUI';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
const APIDocsPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/api-docs"> <PageNextJs pathname="/api-docs">
<Page> <PageTitle title="API Documentation"/>
<PageTitle title="API Documentation"/> <SwaggerUI/>
<SwaggerUI/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default APIDocsPage; export default Page;
export { apiDocs as getServerSideProps } from 'lib/next/getServerSideProps'; export { apiDocs as getServerSideProps } from 'nextjs/getServerSideProps';
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import buildUrlNode from 'lib/api/buildUrlNode'; import buildUrl from 'nextjs/utils/buildUrl';
import { httpLogger } from 'lib/api/logger'; import fetchFactory from 'nextjs/utils/fetch';
import fetchFactory from 'lib/api/nodeFetch'; import { httpLogger } from 'nextjs/utils/logger';
export default async function csrfHandler(_req: NextApiRequest, res: NextApiResponse) { export default async function csrfHandler(_req: NextApiRequest, res: NextApiResponse) {
httpLogger(_req, res); httpLogger(_req, res);
const url = buildUrlNode('csrf'); const url = buildUrl('csrf');
const response = await fetchFactory(_req)(url); const response = await fetchFactory(_req)(url);
if (response.status === 200) { if (response.status === 200) {
......
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import nodeFetch from 'node-fetch'; import nodeFetch from 'node-fetch';
import { httpLogger } from 'lib/api/logger'; import { httpLogger } from 'nextjs/utils/logger';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
export default async function mediaTypeHandler(req: NextApiRequest, res: NextApiResponse) { export default async function mediaTypeHandler(req: NextApiRequest, res: NextApiResponse) {
......
...@@ -2,8 +2,9 @@ import _pick from 'lodash/pick'; ...@@ -2,8 +2,9 @@ import _pick from 'lodash/pick';
import _pickBy from 'lodash/pickBy'; import _pickBy from 'lodash/pickBy';
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import fetchFactory from 'nextjs/utils/fetch';
import config from 'configs/app'; import config from 'configs/app';
import fetchFactory from 'lib/api/nodeFetch';
const handler = async(nextReq: NextApiRequest, nextRes: NextApiResponse) => { const handler = async(nextReq: NextApiRequest, nextRes: NextApiResponse) => {
if (!nextReq.url) { if (!nextReq.url) {
......
import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import type { Props } from 'lib/next/getServerSideProps'; import type { NextPageWithLayout } from 'nextjs/types';
import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; import type { Props } from 'nextjs/getServerSideProps';
import PageNextJs from 'nextjs/PageNextJs';
const MarketplaceApp = dynamic(() => import('ui/pages/MarketplaceApp'), { ssr: false }); const MarketplaceApp = dynamic(() => import('ui/pages/MarketplaceApp'), { ssr: false });
const MarketplaceAppPage: NextPage<Props> = (props: Props) => { const Page: NextPageWithLayout<Props> = (props: Props) => {
return ( return (
<PageServer pathname="/apps/[id]" query={ props }> <PageNextJs pathname="/apps/[id]" query={ props }>
<Page wrapChildren={ false }> <MarketplaceApp/>
<MarketplaceApp/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default MarketplaceAppPage; export default Page;
export { marketplace as getServerSideProps } from 'lib/next/getServerSideProps'; export { marketplace as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,23 +2,23 @@ import type { NextPage } from 'next'; ...@@ -2,23 +2,23 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
const Marketplace = dynamic(() => import('ui/pages/Marketplace'), { ssr: false }); const Marketplace = dynamic(() => import('ui/pages/Marketplace'), { ssr: false });
const MarketplacePage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/apps"> <PageNextJs pathname="/apps">
<Page> <>
<PageTitle title="Marketplace"/> <PageTitle title="Marketplace"/>
<Marketplace/> <Marketplace/>
</Page> </>
</PageServer> </PageNextJs>
); );
}; };
export default MarketplacePage; export default Page;
export { marketplace as getServerSideProps } from 'lib/next/getServerSideProps'; export { marketplace as getServerSideProps } from 'nextjs/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
const Auth0Page: NextPage = () => { const Page: NextPage = () => {
return null; return null;
}; };
export default Auth0Page; export default Page;
export async function getServerSideProps() { export async function getServerSideProps() {
return { return {
......
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import MyProfile from 'ui/pages/MyProfile'; import MyProfile from 'ui/pages/MyProfile';
import Page from 'ui/shared/Page/Page';
const MyProfilePage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/auth/profile"> <PageNextJs pathname="/auth/profile">
<Page> <MyProfile/>
<MyProfile/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default MyProfilePage; export default Page;
export { account as getServerSideProps } from 'lib/next/getServerSideProps'; export { account as getServerSideProps } from 'nextjs/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import UnverifiedEmail from 'ui/pages/UnverifiedEmail'; import UnverifiedEmail from 'ui/pages/UnverifiedEmail';
import Page from 'ui/shared/Page/Page';
const UnverifiedEmailPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/auth/unverified-email"> <PageNextJs pathname="/auth/unverified-email">
<Page> <UnverifiedEmail/>
<UnverifiedEmail/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default UnverifiedEmailPage; export default Page;
export { account as getServerSideProps } from 'lib/next/getServerSideProps'; export { account as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,22 +2,19 @@ import type { NextPage } from 'next'; ...@@ -2,22 +2,19 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import type { Props } from 'lib/next/getServerSideProps'; import type { Props } from 'nextjs/getServerSideProps';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const Block = dynamic(() => import('ui/pages/Block'), { ssr: false }); const Block = dynamic(() => import('ui/pages/Block'), { ssr: false });
const BlockPage: NextPage<Props> = (props: Props) => { const Page: NextPage<Props> = (props: Props) => {
return ( return (
<PageServer pathname="/block/[height_or_hash]" query={ props }> <PageNextJs pathname="/block/[height_or_hash]" query={ props }>
<Page> <Block/>
<Block/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default BlockPage; export default Page;
export { base as getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,21 +2,18 @@ import type { NextPage } from 'next'; ...@@ -2,21 +2,18 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const Blocks = dynamic(() => import('ui/pages/Blocks'), { ssr: false }); const Blocks = dynamic(() => import('ui/pages/Blocks'), { ssr: false });
const BlockPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/blocks"> <PageNextJs pathname="/blocks">
<Page> <Blocks/>
<Blocks/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default BlockPage; export default Page;
export { base as getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'nextjs/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import CsvExport from 'ui/pages/CsvExport'; import CsvExport from 'ui/pages/CsvExport';
const CsvExportPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/csv-export"> <PageNextJs pathname="/csv-export">
<CsvExport/> <CsvExport/>
</PageServer> </PageNextJs>
); );
}; };
export default CsvExportPage; export default Page;
export { csvExport as getServerSideProps } from 'lib/next/getServerSideProps'; export { csvExport as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,9 +2,9 @@ import type { NextPage } from 'next'; ...@@ -2,9 +2,9 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import ContentLoader from 'ui/shared/ContentLoader'; import ContentLoader from 'ui/shared/ContentLoader';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
const GraphQL = dynamic(() => import('ui/graphQL/GraphQL'), { const GraphQL = dynamic(() => import('ui/graphQL/GraphQL'), {
...@@ -12,18 +12,16 @@ const GraphQL = dynamic(() => import('ui/graphQL/GraphQL'), { ...@@ -12,18 +12,16 @@ const GraphQL = dynamic(() => import('ui/graphQL/GraphQL'), {
ssr: false, ssr: false,
}); });
const GraphiqlPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/graphiql"> <PageNextJs pathname="/graphiql">
<Page> <PageTitle title="GraphQL playground"/>
<PageTitle title="GraphQL playground"/> <GraphQL/>
<GraphQL/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default GraphiqlPage; export default Page;
export { base as getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'nextjs/getServerSideProps';
import type { NextPage } from 'next';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import type { NextPageWithLayout } from 'nextjs/types';
import PageNextJs from 'nextjs/PageNextJs';
import Home from 'ui/pages/Home'; import Home from 'ui/pages/Home';
import LayoutHome from 'ui/shared/layout/LayoutHome';
const HomePage: NextPage = () => { const Page: NextPageWithLayout = () => {
return ( return (
<PageServer pathname="/"> <PageNextJs pathname="/">
<Home/> <Home/>
</PageServer> </PageNextJs>
);
};
Page.getLayout = function getLayout(page: React.ReactElement) {
return (
<LayoutHome>
{ page }
</LayoutHome>
); );
}; };
export default HomePage; export default Page;
export { base as getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,21 +2,18 @@ import type { NextPage } from 'next'; ...@@ -2,21 +2,18 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const L2Deposits = dynamic(() => import('ui/pages/L2Deposits'), { ssr: false }); const L2Deposits = dynamic(() => import('ui/pages/L2Deposits'), { ssr: false });
const DepositsPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/l2-deposits"> <PageNextJs pathname="/l2-deposits">
<Page> <L2Deposits/>
<L2Deposits/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default DepositsPage; export default Page;
export { L2 as getServerSideProps } from 'lib/next/getServerSideProps'; export { L2 as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,21 +2,18 @@ import type { NextPage } from 'next'; ...@@ -2,21 +2,18 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const L2OutputRoots = dynamic(() => import('ui/pages/L2OutputRoots'), { ssr: false }); const L2OutputRoots = dynamic(() => import('ui/pages/L2OutputRoots'), { ssr: false });
const OutputRootsPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/l2-output-roots"> <PageNextJs pathname="/l2-output-roots">
<Page> <L2OutputRoots/>
<L2OutputRoots/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default OutputRootsPage; export default Page;
export { L2 as getServerSideProps } from 'lib/next/getServerSideProps'; export { L2 as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,21 +2,18 @@ import type { NextPage } from 'next'; ...@@ -2,21 +2,18 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const L2TxnBatches = dynamic(() => import('ui/pages/L2TxnBatches'), { ssr: false }); const L2TxnBatches = dynamic(() => import('ui/pages/L2TxnBatches'), { ssr: false });
const TxnBatchesPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/l2-txn-batches"> <PageNextJs pathname="/l2-txn-batches">
<Page> <L2TxnBatches/>
<L2TxnBatches/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default TxnBatchesPage; export default Page;
export { L2 as getServerSideProps } from 'lib/next/getServerSideProps'; export { L2 as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,21 +2,18 @@ import type { NextPage } from 'next'; ...@@ -2,21 +2,18 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const L2Withdrawals = dynamic(() => import('ui/pages/L2Withdrawals'), { ssr: false }); const L2Withdrawals = dynamic(() => import('ui/pages/L2Withdrawals'), { ssr: false });
const WithdrawalsPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/l2-withdrawals"> <PageNextJs pathname="/l2-withdrawals">
<Page> <L2Withdrawals/>
<L2Withdrawals/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default WithdrawalsPage; export default Page;
export { L2 as getServerSideProps } from 'lib/next/getServerSideProps'; export { L2 as getServerSideProps } from 'nextjs/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Login from 'ui/pages/Login'; import Login from 'ui/pages/Login';
const LoginPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/login"> <PageNextJs pathname="/login">
<Login/> <Login/>
</PageServer> </PageNextJs>
); );
}; };
export default LoginPage; export default Page;
export { base as getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'nextjs/getServerSideProps';
import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import type { Props } from 'lib/next/getServerSideProps'; import type { NextPageWithLayout } from 'nextjs/types';
import PageServer from 'lib/next/PageServer';
import type { Props } from 'nextjs/getServerSideProps';
import PageNextJs from 'nextjs/PageNextJs';
import LayoutSearchResults from 'ui/shared/layout/LayoutSearchResults';
const SearchResults = dynamic(() => import('ui/pages/SearchResults'), { ssr: false }); const SearchResults = dynamic(() => import('ui/pages/SearchResults'), { ssr: false });
const SearchResultsPage: NextPage<Props> = (props: Props) => { const Page: NextPageWithLayout<Props> = (props: Props) => {
return ( return (
<PageServer pathname="/search-results" query={ props }> <PageNextJs pathname="/search-results" query={ props }>
<SearchResults/> <SearchResults/>
</PageServer> </PageNextJs>
);
};
Page.getLayout = function getLayout(page: React.ReactElement) {
return (
<LayoutSearchResults>
{ page }
</LayoutSearchResults>
); );
}; };
export default SearchResultsPage; export default Page;
export { base as getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'nextjs/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Stats from '../ui/pages/Stats'; import Stats from 'ui/pages/Stats';
const StatsPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/stats"> <PageNextJs pathname="/stats">
<Stats/> <Stats/>
</PageServer> </PageNextJs>
); );
}; };
export default StatsPage; export default Page;
export { stats as getServerSideProps } from 'lib/next/getServerSideProps'; export { stats as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,22 +2,19 @@ import type { NextPage } from 'next'; ...@@ -2,22 +2,19 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import type { Props } from 'lib/next/getServerSideProps'; import type { Props } from 'nextjs/getServerSideProps';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const Token = dynamic(() => import('ui/pages/Token'), { ssr: false }); const Token = dynamic(() => import('ui/pages/Token'), { ssr: false });
const TokenPage: NextPage<Props> = (props: Props) => { const Page: NextPage<Props> = (props: Props) => {
return ( return (
<PageServer pathname="/token/[hash]" query={ props }> <PageNextJs pathname="/token/[hash]" query={ props }>
<Page> <Token/>
<Token/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default TokenPage; export default Page;
export { base as getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,22 +2,19 @@ import type { NextPage } from 'next'; ...@@ -2,22 +2,19 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import type { Props } from 'lib/next/getServerSideProps'; import type { Props } from 'nextjs/getServerSideProps';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const TokenInstance = dynamic(() => import('ui/pages/TokenInstance'), { ssr: false }); const TokenInstance = dynamic(() => import('ui/pages/TokenInstance'), { ssr: false });
const TokenInstancePage: NextPage<Props> = (props: Props) => { const Page: NextPage<Props> = (props: Props) => {
return ( return (
<PageServer pathname="/token/[hash]/instance/[id]" query={ props }> <PageNextJs pathname="/token/[hash]/instance/[id]" query={ props }>
<Page> <TokenInstance/>
<TokenInstance/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default TokenInstancePage; export default Page;
export { base as getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,21 +2,18 @@ import type { NextPage } from 'next'; ...@@ -2,21 +2,18 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const Tokens = dynamic(() => import('ui/pages/Tokens'), { ssr: false }); const Tokens = dynamic(() => import('ui/pages/Tokens'), { ssr: false });
const TokensPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/tokens"> <PageNextJs pathname="/tokens">
<Page> <Tokens/>
<Tokens/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default TokensPage; export default Page;
export { base as getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,22 +2,19 @@ import type { NextPage } from 'next'; ...@@ -2,22 +2,19 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import type { Props } from 'lib/next/getServerSideProps'; import type { Props } from 'nextjs/getServerSideProps';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const Transaction = dynamic(() => import('ui/pages/Transaction'), { ssr: false }); const Transaction = dynamic(() => import('ui/pages/Transaction'), { ssr: false });
const TransactionPage: NextPage<Props> = (props: Props) => { const Page: NextPage<Props> = (props: Props) => {
return ( return (
<PageServer pathname="/tx/[hash]" query={ props }> <PageNextJs pathname="/tx/[hash]" query={ props }>
<Page> <Transaction/>
<Transaction/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default TransactionPage; export default Page;
export { base as getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,21 +2,18 @@ import type { NextPage } from 'next'; ...@@ -2,21 +2,18 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const Transactions = dynamic(() => import('ui/pages/Transactions'), { ssr: false }); const Transactions = dynamic(() => import('ui/pages/Transactions'), { ssr: false });
const TxsPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/txs"> <PageNextJs pathname="/txs">
<Page> <Transactions/>
<Transactions/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default TxsPage; export default Page;
export { base as getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,21 +2,18 @@ import type { NextPage } from 'next'; ...@@ -2,21 +2,18 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const VerifiedContracts = dynamic(() => import('ui/pages/VerifiedContracts'), { ssr: false }); const VerifiedContracts = dynamic(() => import('ui/pages/VerifiedContracts'), { ssr: false });
const VerifiedContractsPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/verified-contracts"> <PageNextJs pathname="/verified-contracts">
<Page> <VerifiedContracts/>
<VerifiedContracts/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default VerifiedContractsPage; export default Page;
export { base as getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'nextjs/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Sol2Uml from 'ui/pages/Sol2Uml'; import Sol2Uml from 'ui/pages/Sol2Uml';
const Sol2UmlPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/visualize/sol2uml"> <PageNextJs pathname="/visualize/sol2uml">
<Sol2Uml/> <Sol2Uml/>
</PageServer> </PageNextJs>
); );
}; };
export default Sol2UmlPage; export default Page;
export { base as getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -2,21 +2,18 @@ import type { NextPage } from 'next'; ...@@ -2,21 +2,18 @@ import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer'; import PageNextJs from 'nextjs/PageNextJs';
import Page from 'ui/shared/Page/Page';
const Withdrawals = dynamic(() => import('ui/pages/Withdrawals'), { ssr: false }); const Withdrawals = dynamic(() => import('ui/pages/Withdrawals'), { ssr: false });
const WithdrawalsPage: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageServer pathname="/withdrawals"> <PageNextJs pathname="/withdrawals">
<Page> <Withdrawals/>
<Withdrawals/> </PageNextJs>
</Page>
</PageServer>
); );
}; };
export default WithdrawalsPage; export default Page;
export { beaconChain as getServerSideProps } from 'lib/next/getServerSideProps'; export { beaconChain as getServerSideProps } from 'nextjs/getServerSideProps';
...@@ -5,8 +5,9 @@ import React from 'react'; ...@@ -5,8 +5,9 @@ import React from 'react';
import { configureChains, createConfig, WagmiConfig } from 'wagmi'; import { configureChains, createConfig, WagmiConfig } from 'wagmi';
import { mainnet } from 'wagmi/chains'; import { mainnet } from 'wagmi/chains';
import type { Props as PageProps } from 'nextjs/getServerSideProps';
import { AppContextProvider } from 'lib/contexts/app'; import { AppContextProvider } from 'lib/contexts/app';
import type { Props as PageProps } from 'lib/next/getServerSideProps';
import { SocketProvider } from 'lib/socket/context'; import { SocketProvider } from 'lib/socket/context';
import * as app from 'playwright/utils/app'; import * as app from 'playwright/utils/app';
import theme from 'theme'; import theme from 'theme';
......
...@@ -8,3 +8,14 @@ export const viewport = { ...@@ -8,3 +8,14 @@ export const viewport = {
export const maskColor = '#4299E1'; // blue.400 export const maskColor = '#4299E1'; // blue.400
export const adsBannerSelector = '.adsbyslise'; export const adsBannerSelector = '.adsbyslise';
export const featureEnvs = {
beaconChain: [
{ name: 'NEXT_PUBLIC_HAS_BEACON_CHAIN', value: 'true' },
],
rollup: [
{ name: 'NEXT_PUBLIC_IS_L2_NETWORK', value: 'true' },
{ name: 'NEXT_PUBLIC_L1_BASE_URL', value: 'https://localhost:3101' },
{ name: 'NEXT_PUBLIC_L2_WITHDRAWAL_URL', value: 'https://localhost:3102' },
],
};
import type { Route } from 'nextjs-routes';
import type React from 'react'; import type React from 'react';
import type { Route } from 'nextjs-routes';
type NavIconOrComponent = { type NavIconOrComponent = {
icon?: React.FunctionComponent<React.SVGAttributes<SVGElement>>; icon?: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
} | { } | {
......
import { chakra, Icon, Tooltip, Hide, Skeleton, Flex } from '@chakra-ui/react'; import { chakra, Icon, Tooltip, Hide, Skeleton, Flex } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { CsvExportParams } from 'types/client/address'; import type { CsvExportParams } from 'types/client/address';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import svgFileIcon from 'icons/files/csv.svg'; import svgFileIcon from 'icons/files/csv.svg';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading'; import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
......
import { Box, Text, Grid, Skeleton } from '@chakra-ui/react'; import { Box, Text, Grid, Skeleton } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query'; import type { UseQueryResult } from '@tanstack/react-query';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Address as TAddress } from 'types/api/address'; import type { Address as TAddress } from 'types/api/address';
import { route } from 'nextjs-routes';
import blockIcon from 'icons/block.svg'; import blockIcon from 'icons/block.svg';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
......
import { Flex, Skeleton } from '@chakra-ui/react'; import { Flex, Skeleton } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
......
import { Td, Tr, Flex, Skeleton } from '@chakra-ui/react'; import { Td, Tr, Flex, Skeleton } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
import { route } from 'nextjs-routes';
import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
......
import { Text, Stat, StatHelpText, StatArrow, Flex, Skeleton } from '@chakra-ui/react'; import { Text, Stat, StatHelpText, StatArrow, Flex, Skeleton } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { AddressCoinBalanceHistoryItem } from 'types/api/address'; import type { AddressCoinBalanceHistoryItem } from 'types/api/address';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import { WEI, ZERO } from 'lib/consts'; import { WEI, ZERO } from 'lib/consts';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
......
import { Td, Tr, Text, Stat, StatHelpText, StatArrow, Skeleton } from '@chakra-ui/react'; import { Td, Tr, Text, Stat, StatHelpText, StatArrow, Skeleton } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { AddressCoinBalanceHistoryItem } from 'types/api/address'; import type { AddressCoinBalanceHistoryItem } from 'types/api/address';
import { route } from 'nextjs-routes';
import { WEI, ZERO } from 'lib/consts'; import { WEI, ZERO } from 'lib/consts';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
......
import { Flex, Skeleton, Button, Grid, GridItem, Alert, Link, chakra, Box } from '@chakra-ui/react'; import { Flex, Skeleton, Button, Grid, GridItem, Alert, Link, chakra, Box } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { SocketMessage } from 'lib/socket/types'; import type { SocketMessage } from 'lib/socket/types';
import type { Address as AddressInfo } from 'types/api/address'; import type { Address as AddressInfo } from 'types/api/address';
import { route } from 'nextjs-routes';
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
......
import { AccordionButton, AccordionIcon, AccordionItem, AccordionPanel, Box, Icon, Tooltip, useClipboard, useDisclosure } from '@chakra-ui/react'; import { AccordionButton, AccordionIcon, AccordionItem, AccordionPanel, Box, Icon, Tooltip, useClipboard, useDisclosure } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import { Element } from 'react-scroll'; import { Element } from 'react-scroll';
import type { SmartContractMethod } from 'types/api/contract'; import type { SmartContractMethod } from 'types/api/contract';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import iconLink from 'icons/link.svg'; import iconLink from 'icons/link.svg';
import Hint from 'ui/shared/Hint'; import Hint from 'ui/shared/Hint';
......
import { Box, Flex, Select, Skeleton, Text, Tooltip } from '@chakra-ui/react'; import { Box, Flex, Select, Skeleton, Text, Tooltip } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { SmartContract } from 'types/api/contract'; import type { SmartContract } from 'types/api/contract';
import type { ArrayElement } from 'types/utils'; import type { ArrayElement } from 'types/utils';
import { route } from 'nextjs-routes';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import * as stubs from 'stubs/contract'; import * as stubs from 'stubs/contract';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
......
import { Box, chakra, Spinner } from '@chakra-ui/react'; import { Box, chakra, Spinner } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { ContractMethodWriteResult } from './types'; import type { ContractMethodWriteResult } from './types';
import { route } from 'nextjs-routes';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
interface Props { interface Props {
......
import { Skeleton } from '@chakra-ui/react'; import { Skeleton } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query'; import type { UseQueryResult } from '@tanstack/react-query';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { AddressCounters } from 'types/api/address'; import type { AddressCounters } from 'types/api/address';
import { route } from 'nextjs-routes';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
interface Props { interface Props {
......
import { Flex, Box, HStack, Skeleton } from '@chakra-ui/react'; import { Flex, Box, HStack, Skeleton } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction'; import type { InternalTransaction } from 'types/api/internalTransaction';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import eastArrowIcon from 'icons/arrows/east.svg'; import eastArrowIcon from 'icons/arrows/east.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
......
import { Tr, Td, Box, Flex, Skeleton } from '@chakra-ui/react'; import { Tr, Td, Box, Flex, Skeleton } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction'; import type { InternalTransaction } from 'types/api/internalTransaction';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
......
import { chakra, Flex, Text, useColorModeValue } from '@chakra-ui/react'; import { chakra, Flex, Text, useColorModeValue } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import { route } from 'nextjs-routes';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet'; import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
import TruncatedValue from 'ui/shared/TruncatedValue'; import TruncatedValue from 'ui/shared/TruncatedValue';
......
import { Flex, Link, Text, LinkBox, LinkOverlay, useColorModeValue, Skeleton } from '@chakra-ui/react'; import { Flex, Link, Text, LinkBox, LinkOverlay, useColorModeValue, Skeleton } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { AddressTokenBalance } from 'types/api/address'; import type { AddressTokenBalance } from 'types/api/address';
import { route } from 'nextjs-routes';
import NftMedia from 'ui/shared/nft/NftMedia'; import NftMedia from 'ui/shared/nft/NftMedia';
import TokenLogo from 'ui/shared/TokenLogo'; import TokenLogo from 'ui/shared/TokenLogo';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip'; import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
......
import { Alert, Box, Button, Flex } from '@chakra-ui/react'; import { Alert, Box, Button, Flex } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { SubmitHandler } from 'react-hook-form'; import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
...@@ -12,6 +11,8 @@ import type { ...@@ -12,6 +11,8 @@ import type {
RootFields, RootFields,
} from '../types'; } from '../types';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
......
...@@ -3,12 +3,13 @@ import type { UseQueryResult } from '@tanstack/react-query'; ...@@ -3,12 +3,13 @@ import type { UseQueryResult } from '@tanstack/react-query';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import capitalize from 'lodash/capitalize'; import capitalize from 'lodash/capitalize';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import { scroller, Element } from 'react-scroll'; import { scroller, Element } from 'react-scroll';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import clockIcon from 'icons/clock.svg'; import clockIcon from 'icons/clock.svg';
import flameIcon from 'icons/flame.svg'; import flameIcon from 'icons/flame.svg';
......
import { Flex, Skeleton, Text, Box, useColorModeValue } from '@chakra-ui/react'; import { Flex, Skeleton, Text, Box, useColorModeValue } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import capitalize from 'lodash/capitalize'; import capitalize from 'lodash/capitalize';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import flameIcon from 'icons/flame.svg'; import flameIcon from 'icons/flame.svg';
import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
......
import { Tr, Td, Flex, Box, Tooltip, Skeleton, useColorModeValue } from '@chakra-ui/react'; import { Tr, Td, Flex, Box, Tooltip, Skeleton, useColorModeValue } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import flameIcon from 'icons/flame.svg'; import flameIcon from 'icons/flame.svg';
import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
......
import { Button, chakra, useUpdateEffect } from '@chakra-ui/react'; import { Button, chakra, useUpdateEffect } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { SubmitHandler } from 'react-hook-form'; import type { SubmitHandler } from 'react-hook-form';
import { useForm, FormProvider } from 'react-hook-form'; import { useForm, FormProvider } from 'react-hook-form';
...@@ -8,6 +7,8 @@ import type { FormFields } from './types'; ...@@ -8,6 +7,8 @@ import type { FormFields } from './types';
import type { SocketMessage } from 'lib/socket/types'; import type { SocketMessage } from 'lib/socket/types';
import type { SmartContractVerificationMethod, SmartContractVerificationConfig } from 'types/api/contract'; import type { SmartContractVerificationMethod, SmartContractVerificationConfig } from 'types/api/contract';
import { route } from 'nextjs-routes';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
import delay from 'lib/delay'; import delay from 'lib/delay';
import useToast from 'lib/hooks/useToast'; import useToast from 'lib/hooks/useToast';
......
import { Box, Heading, Flex, Text, VStack, Skeleton } from '@chakra-ui/react'; import { Box, Heading, Flex, Text, VStack, Skeleton } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { AnimatePresence } from 'framer-motion'; import { AnimatePresence } from 'framer-motion';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { SocketMessage } from 'lib/socket/types'; import type { SocketMessage } from 'lib/socket/types';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
......
import { test, expect } from '@playwright/experimental-ct-react'; import { test as base, expect } from '@playwright/experimental-ct-react';
import React from 'react'; import React from 'react';
import * as depositMock from 'mocks/l2deposits/deposits'; import * as depositMock from 'mocks/l2deposits/deposits';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl'; import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';
import LatestDeposits from './LatestDeposits'; import LatestDeposits from './LatestDeposits';
const test = base.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
context: contextWithEnvs(configs.featureEnvs.rollup) as any,
});
test('default view +@mobile +@dark-mode', async({ mount, page }) => { test('default view +@mobile +@dark-mode', async({ mount, page }) => {
await page.route(buildApiUrl('homepage_deposits'), (route) => route.fulfill({ await page.route(buildApiUrl('homepage_deposits'), (route) => route.fulfill({
status: 200, status: 200,
......
import { Box, Flex, Text } from '@chakra-ui/react'; import { Box, Flex, Text } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { SocketMessage } from 'lib/socket/types'; import type { SocketMessage } from 'lib/socket/types';
import { route } from 'nextjs-routes';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import useGradualIncrement from 'lib/hooks/useGradualIncrement'; import useGradualIncrement from 'lib/hooks/useGradualIncrement';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
......
...@@ -4,11 +4,12 @@ import { ...@@ -4,11 +4,12 @@ import {
Grid, Grid,
Skeleton, Skeleton,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { L2DepositsItem } from 'types/api/l2Deposits'; import type { L2DepositsItem } from 'types/api/l2Deposits';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import blockIcon from 'icons/block.svg'; import blockIcon from 'icons/block.svg';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
......
import { Box, Flex, Text } from '@chakra-ui/react'; import { Box, Flex, Text } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import { route } from 'nextjs-routes';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useNewTxsSocket from 'lib/hooks/useNewTxsSocket'; import useNewTxsSocket from 'lib/hooks/useNewTxsSocket';
......
import { Box, Flex, Text } from '@chakra-ui/react'; import { Box, Flex, Text } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import { route } from 'nextjs-routes';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthToken'; import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthToken';
......
import { Grid } from '@chakra-ui/react'; import { Grid } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import blockIcon from 'icons/block.svg'; import blockIcon from 'icons/block.svg';
import clockIcon from 'icons/clock-light.svg'; import clockIcon from 'icons/clock-light.svg';
......
import { Skeleton } from '@chakra-ui/react'; import { Skeleton } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { L2DepositsItem } from 'types/api/l2Deposits'; import type { L2DepositsItem } from 'types/api/l2Deposits';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import blockIcon from 'icons/block.svg'; import blockIcon from 'icons/block.svg';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
......
import { Td, Tr, Skeleton } from '@chakra-ui/react'; import { Td, Tr, Skeleton } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { L2DepositsItem } from 'types/api/l2Deposits'; import type { L2DepositsItem } from 'types/api/l2Deposits';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import blockIcon from 'icons/block.svg'; import blockIcon from 'icons/block.svg';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
......
import { Flex, Skeleton } from '@chakra-ui/react'; import { Flex, Skeleton } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { L2OutputRootsItem } from 'types/api/l2OutputRoots'; import type { L2OutputRootsItem } from 'types/api/l2OutputRoots';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
......
import { Flex, Td, Tr, Skeleton } from '@chakra-ui/react'; import { Flex, Td, Tr, Skeleton } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { L2OutputRootsItem } from 'types/api/l2OutputRoots'; import type { L2OutputRootsItem } from 'types/api/l2OutputRoots';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import txBatchIcon from 'icons/txBatch.svg'; import txBatchIcon from 'icons/txBatch.svg';
......
import { Skeleton, VStack } from '@chakra-ui/react'; import { Skeleton, VStack } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { L2TxnBatchesItem } from 'types/api/l2TxnBatches'; import type { L2TxnBatchesItem } from 'types/api/l2TxnBatches';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import txBatchIcon from 'icons/txBatch.svg'; import txBatchIcon from 'icons/txBatch.svg';
......
import { Td, Tr, VStack, Skeleton } from '@chakra-ui/react'; import { Td, Tr, VStack, Skeleton } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { L2TxnBatchesItem } from 'types/api/l2TxnBatches'; import type { L2TxnBatchesItem } from 'types/api/l2TxnBatches';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import txBatchIcon from 'icons/txBatch.svg'; import txBatchIcon from 'icons/txBatch.svg';
......
import { Skeleton } from '@chakra-ui/react'; import { Skeleton } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { L2WithdrawalsItem } from 'types/api/l2Withdrawals'; import type { L2WithdrawalsItem } from 'types/api/l2Withdrawals';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
......
import { Td, Tr, Skeleton } from '@chakra-ui/react'; import { Td, Tr, Skeleton } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { L2WithdrawalsItem } from 'types/api/l2Withdrawals'; import type { L2WithdrawalsItem } from 'types/api/l2Withdrawals';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
......
...@@ -34,7 +34,7 @@ test('base view +@mobile +@dark-mode', async({ mount, page }) => { ...@@ -34,7 +34,7 @@ test('base view +@mobile +@dark-mode', async({ mount, page }) => {
await page.waitForResponse('https://www.google.com/recaptcha/api2/**'); await page.waitForResponse('https://www.google.com/recaptcha/api2/**');
await expect(component.locator('main')).toHaveScreenshot({ await expect(component).toHaveScreenshot({
mask: [ page.locator('.recaptcha') ], mask: [ page.locator('.recaptcha') ],
maskColor: configs.maskColor, maskColor: configs.maskColor,
}); });
......
...@@ -16,7 +16,6 @@ import AddressIcon from 'ui/shared/address/AddressIcon'; ...@@ -16,7 +16,6 @@ import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import ContentLoader from 'ui/shared/ContentLoader'; import ContentLoader from 'ui/shared/ContentLoader';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
interface ExportTypeEntity { interface ExportTypeEntity {
...@@ -127,7 +126,7 @@ const CsvExport = () => { ...@@ -127,7 +126,7 @@ const CsvExport = () => {
})(); })();
return ( return (
<Page> <>
<PageTitle <PageTitle
title="Export data to CSV file" title="Export data to CSV file"
backLink={ backLink } backLink={ backLink }
...@@ -144,7 +143,7 @@ const CsvExport = () => { ...@@ -144,7 +143,7 @@ const CsvExport = () => {
<span>Exports are limited to the last 10K { EXPORT_TYPES[exportType].text }.</span> <span>Exports are limited to the last 10K { EXPORT_TYPES[exportType].text }.</span>
</Flex> </Flex>
{ content } { content }
</Page> </>
); );
}; };
......
...@@ -49,7 +49,7 @@ test.describe('default view', () => { ...@@ -49,7 +49,7 @@ test.describe('default view', () => {
}); });
test('-@default +@dark-mode', async({ page }) => { test('-@default +@dark-mode', async({ page }) => {
await expect(component.locator('main')).toHaveScreenshot({ await expect(component).toHaveScreenshot({
mask: [ page.locator(configs.adsBannerSelector) ], mask: [ page.locator(configs.adsBannerSelector) ],
maskColor: configs.maskColor, maskColor: configs.maskColor,
}); });
...@@ -59,7 +59,7 @@ test.describe('default view', () => { ...@@ -59,7 +59,7 @@ test.describe('default view', () => {
test.use({ viewport: configs.viewport.xl }); test.use({ viewport: configs.viewport.xl });
test('', async({ page }) => { test('', async({ page }) => {
await expect(component.locator('main')).toHaveScreenshot({ await expect(component).toHaveScreenshot({
mask: [ page.locator(configs.adsBannerSelector) ], mask: [ page.locator(configs.adsBannerSelector) ],
maskColor: configs.maskColor, maskColor: configs.maskColor,
}); });
...@@ -134,7 +134,7 @@ test.describe('mobile', () => { ...@@ -134,7 +134,7 @@ test.describe('mobile', () => {
</TestApp>, </TestApp>,
); );
await expect(component.locator('main')).toHaveScreenshot({ await expect(component).toHaveScreenshot({
mask: [ page.locator(configs.adsBannerSelector) ], mask: [ page.locator(configs.adsBannerSelector) ],
maskColor: configs.maskColor, maskColor: configs.maskColor,
}); });
......
...@@ -7,13 +7,12 @@ import LatestBlocks from 'ui/home/LatestBlocks'; ...@@ -7,13 +7,12 @@ import LatestBlocks from 'ui/home/LatestBlocks';
import Stats from 'ui/home/Stats'; import Stats from 'ui/home/Stats';
import Transactions from 'ui/home/Transactions'; import Transactions from 'ui/home/Transactions';
import AdBanner from 'ui/shared/ad/AdBanner'; import AdBanner from 'ui/shared/ad/AdBanner';
import Page from 'ui/shared/Page/Page';
import ProfileMenuDesktop from 'ui/snippets/profileMenu/ProfileMenuDesktop'; import ProfileMenuDesktop from 'ui/snippets/profileMenu/ProfileMenuDesktop';
import SearchBar from 'ui/snippets/searchBar/SearchBar'; import SearchBar from 'ui/snippets/searchBar/SearchBar';
const Home = () => { const Home = () => {
return ( return (
<Page isHomePage> <>
<Box <Box
w="100%" w="100%"
background={ config.UI.homepage.plate.background } background={ config.UI.homepage.plate.background }
...@@ -49,7 +48,7 @@ const Home = () => { ...@@ -49,7 +48,7 @@ const Home = () => {
<Transactions/> <Transactions/>
</Box> </Box>
</Flex> </Flex>
</Page> </>
); );
}; };
......
import { test, expect } from '@playwright/experimental-ct-react'; import { test as base, expect } from '@playwright/experimental-ct-react';
import React from 'react'; import React from 'react';
import { data as depositsData } from 'mocks/l2deposits/deposits'; import { data as depositsData } from 'mocks/l2deposits/deposits';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl'; import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';
import L2Deposits from './L2Deposits'; import L2Deposits from './L2Deposits';
const DEPOSITS_API_URL = buildApiUrl('l2_deposits'); const DEPOSITS_API_URL = buildApiUrl('l2_deposits');
const DEPOSITS_COUNT_API_URL = buildApiUrl('l2_deposits_count'); const DEPOSITS_COUNT_API_URL = buildApiUrl('l2_deposits_count');
const test = base.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
context: contextWithEnvs(configs.featureEnvs.rollup) as any,
});
test('base view +@mobile', async({ mount, page }) => { test('base view +@mobile', async({ mount, page }) => {
await page.route('https://request-global.czilladx.com/serve/native.php?z=19260bf627546ab7242', (route) => route.fulfill({ await page.route('https://request-global.czilladx.com/serve/native.php?z=19260bf627546ab7242', (route) => route.fulfill({
status: 200, status: 200,
......
import { test, expect } from '@playwright/experimental-ct-react'; import { test as base, expect } from '@playwright/experimental-ct-react';
import React from 'react'; import React from 'react';
import { outputRootsData } from 'mocks/l2outputRoots/outputRoots'; import { outputRootsData } from 'mocks/l2outputRoots/outputRoots';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl'; import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';
import OutputRoots from './L2OutputRoots'; import OutputRoots from './L2OutputRoots';
const test = base.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
context: contextWithEnvs(configs.featureEnvs.rollup) as any,
});
const OUTPUT_ROOTS_API_URL = buildApiUrl('l2_output_roots'); const OUTPUT_ROOTS_API_URL = buildApiUrl('l2_output_roots');
const OUTPUT_ROOTS_COUNT_API_URL = buildApiUrl('l2_output_roots_count'); const OUTPUT_ROOTS_COUNT_API_URL = buildApiUrl('l2_output_roots_count');
......
import { test, expect } from '@playwright/experimental-ct-react'; import { test as base, expect } from '@playwright/experimental-ct-react';
import React from 'react'; import React from 'react';
import { txnBatchesData } from 'mocks/l2txnBatches/txnBatches'; import { txnBatchesData } from 'mocks/l2txnBatches/txnBatches';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl'; import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';
import L2TxnBatches from './L2TxnBatches'; import L2TxnBatches from './L2TxnBatches';
const test = base.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
context: contextWithEnvs(configs.featureEnvs.rollup) as any,
});
const TXN_BATCHES_API_URL = buildApiUrl('l2_txn_batches'); const TXN_BATCHES_API_URL = buildApiUrl('l2_txn_batches');
const TXN_BATCHES_COUNT_API_URL = buildApiUrl('l2_txn_batches_count'); const TXN_BATCHES_COUNT_API_URL = buildApiUrl('l2_txn_batches_count');
......
import { test, expect } from '@playwright/experimental-ct-react'; import { test as base, expect } from '@playwright/experimental-ct-react';
import React from 'react'; import React from 'react';
import { data as withdrawalsData } from 'mocks/l2withdrawals/withdrawals'; import { data as withdrawalsData } from 'mocks/l2withdrawals/withdrawals';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl'; import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';
import L2Withdrawals from './L2Withdrawals'; import L2Withdrawals from './L2Withdrawals';
const test = base.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
context: contextWithEnvs(configs.featureEnvs.rollup) as any,
});
const WITHDRAWALS_API_URL = buildApiUrl('l2_withdrawals'); const WITHDRAWALS_API_URL = buildApiUrl('l2_withdrawals');
const WITHDRAWALS_COUNT_API_URL = buildApiUrl('l2_withdrawals_count'); const WITHDRAWALS_COUNT_API_URL = buildApiUrl('l2_withdrawals_count');
......
...@@ -8,7 +8,6 @@ import config from 'configs/app'; ...@@ -8,7 +8,6 @@ import config from 'configs/app';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import useGradualIncrement from 'lib/hooks/useGradualIncrement'; import useGradualIncrement from 'lib/hooks/useGradualIncrement';
import useToast from 'lib/hooks/useToast'; import useToast from 'lib/hooks/useToast';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
{ /* will be deleted when we fix login in preview CI stands */ } { /* will be deleted when we fix login in preview CI stands */ }
...@@ -22,6 +21,7 @@ const Login = () => { ...@@ -22,6 +21,7 @@ const Login = () => {
React.useEffect(() => { React.useEffect(() => {
const token = cookies.get(cookies.NAMES.API_TOKEN); const token = cookies.get(cookies.NAMES.API_TOKEN);
setFormVisibility(Boolean(!token && config.features.account.isEnabled)); setFormVisibility(Boolean(!token && config.features.account.isEnabled));
// throw new Error('Test error');
}, []); }, []);
const checkSentry = React.useCallback(() => { const checkSentry = React.useCallback(() => {
...@@ -59,33 +59,31 @@ const Login = () => { ...@@ -59,33 +59,31 @@ const Login = () => {
}, [ setNum ]); }, [ setNum ]);
return ( return (
<Page> <VStack gap={ 4 } alignItems="flex-start" maxW="1000px">
<VStack gap={ 4 } alignItems="flex-start" maxW="1000px"> <PageTitle title="Login page 😂"/>
<PageTitle title="Login page 😂"/> { isFormVisible && (
{ isFormVisible && ( <>
<> <Alert status="error" flexDirection="column" alignItems="flex-start">
<Alert status="error" flexDirection="column" alignItems="flex-start"> <AlertTitle fontSize="md">
<AlertTitle fontSize="md">
!!! Temporary solution for authentication on localhost !!! !!! Temporary solution for authentication on localhost !!!
</AlertTitle> </AlertTitle>
<AlertDescription mt={ 3 }> <AlertDescription mt={ 3 }>
To Sign in go to production instance first, sign in there, copy obtained API token from cookie 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 <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 authenticated in current environment
</AlertDescription> </AlertDescription>
</Alert> </Alert>
<Textarea value={ token } onChange={ handleTokenChange } placeholder="API token"/> <Textarea value={ token } onChange={ handleTokenChange } placeholder="API token"/>
<Button onClick={ handleSetTokenClick }>Set cookie</Button> <Button onClick={ handleSetTokenClick }>Set cookie</Button>
</> </>
) } ) }
<Button colorScheme="red" onClick={ checkSentry }>Check Sentry</Button> <Button colorScheme="red" onClick={ checkSentry }>Check Sentry</Button>
<Button colorScheme="teal" onClick={ checkMixpanel }>Check Mixpanel</Button> <Button colorScheme="teal" onClick={ checkMixpanel }>Check Mixpanel</Button>
<Flex columnGap={ 2 } alignItems="center"> <Flex columnGap={ 2 } alignItems="center">
<Box w="50px" textAlign="center">{ num }</Box> <Box w="50px" textAlign="center">{ num }</Box>
<Button onClick={ handleNumIncrement } size="sm">add</Button> <Button onClick={ handleNumIncrement } size="sm">add</Button>
</Flex> </Flex>
</VStack> </VStack>
</Page>
); );
}; };
......
import { Box, Center, useColorMode } from '@chakra-ui/react'; import { Box, Center, useColorMode } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { route } from 'nextjs-routes';
import React, { useCallback, useEffect, useRef, useState } from 'react'; import React, { useCallback, useEffect, useRef, useState } from 'react';
import type { MarketplaceAppOverview } from 'types/client/marketplace'; import type { MarketplaceAppOverview } from 'types/client/marketplace';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import { useAppContext } from 'lib/contexts/app'; import { useAppContext } from 'lib/contexts/app';
...@@ -105,10 +106,10 @@ const MarketplaceApp = () => { ...@@ -105,10 +106,10 @@ const MarketplaceApp = () => {
return ( return (
<> <>
{ !isLoading && <PageTitle title={ data.title } px={{ base: 4, lg: 12 }} pt={{ base: '138px', lg: 0 }} backLink={ backLink }/> } { !isLoading && <PageTitle title={ data.title } backLink={ backLink }/> }
<Center <Center
as="main"
h="100vh" h="100vh"
mx={{ base: -4, lg: -12 }}
> >
{ (isFrameLoading) && ( { (isFrameLoading) && (
<ContentLoader/> <ContentLoader/>
......
...@@ -6,6 +6,8 @@ import * as searchMock from 'mocks/search/index'; ...@@ -6,6 +6,8 @@ import * as searchMock from 'mocks/search/index';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl'; import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';
import LayoutMainColumn from 'ui/shared/layout/components/MainColumn';
import SearchResults from './SearchResults'; import SearchResults from './SearchResults';
...@@ -35,12 +37,17 @@ test('search by name +@mobile +@dark-mode', async({ mount, page }) => { ...@@ -35,12 +37,17 @@ test('search by name +@mobile +@dark-mode', async({ mount, page }) => {
const component = await mount( const component = await mount(
<TestApp> <TestApp>
<SearchResults/> <LayoutMainColumn>
<SearchResults/>
</LayoutMainColumn>
</TestApp>, </TestApp>,
{ hooksConfig }, { hooksConfig },
); );
await expect(component.locator('main')).toHaveScreenshot(); await expect(component).toHaveScreenshot({
mask: [ page.locator('header'), page.locator('form') ],
maskColor: configs.maskColor,
});
}); });
test('search by address hash +@mobile', async({ mount, page }) => { test('search by address hash +@mobile', async({ mount, page }) => {
...@@ -60,12 +67,17 @@ test('search by address hash +@mobile', async({ mount, page }) => { ...@@ -60,12 +67,17 @@ test('search by address hash +@mobile', async({ mount, page }) => {
const component = await mount( const component = await mount(
<TestApp> <TestApp>
<SearchResults/> <LayoutMainColumn>
<SearchResults/>
</LayoutMainColumn>
</TestApp>, </TestApp>,
{ hooksConfig }, { hooksConfig },
); );
await expect(component.locator('main')).toHaveScreenshot(); await expect(component).toHaveScreenshot({
mask: [ page.locator('header'), page.locator('form') ],
maskColor: configs.maskColor,
});
}); });
test('search by block number +@mobile', async({ mount, page }) => { test('search by block number +@mobile', async({ mount, page }) => {
...@@ -85,12 +97,17 @@ test('search by block number +@mobile', async({ mount, page }) => { ...@@ -85,12 +97,17 @@ test('search by block number +@mobile', async({ mount, page }) => {
const component = await mount( const component = await mount(
<TestApp> <TestApp>
<SearchResults/> <LayoutMainColumn>
<SearchResults/>
</LayoutMainColumn>
</TestApp>, </TestApp>,
{ hooksConfig }, { hooksConfig },
); );
await expect(component.locator('main')).toHaveScreenshot(); await expect(component).toHaveScreenshot({
mask: [ page.locator('header'), page.locator('form') ],
maskColor: configs.maskColor,
});
}); });
test('search by block hash +@mobile', async({ mount, page }) => { test('search by block hash +@mobile', async({ mount, page }) => {
...@@ -110,12 +127,17 @@ test('search by block hash +@mobile', async({ mount, page }) => { ...@@ -110,12 +127,17 @@ test('search by block hash +@mobile', async({ mount, page }) => {
const component = await mount( const component = await mount(
<TestApp> <TestApp>
<SearchResults/> <LayoutMainColumn>
<SearchResults/>
</LayoutMainColumn>
</TestApp>, </TestApp>,
{ hooksConfig }, { hooksConfig },
); );
await expect(component.locator('main')).toHaveScreenshot(); await expect(component).toHaveScreenshot({
mask: [ page.locator('header'), page.locator('form') ],
maskColor: configs.maskColor,
});
}); });
test('search by tx hash +@mobile', async({ mount, page }) => { test('search by tx hash +@mobile', async({ mount, page }) => {
...@@ -135,12 +157,17 @@ test('search by tx hash +@mobile', async({ mount, page }) => { ...@@ -135,12 +157,17 @@ test('search by tx hash +@mobile', async({ mount, page }) => {
const component = await mount( const component = await mount(
<TestApp> <TestApp>
<SearchResults/> <LayoutMainColumn>
<SearchResults/>
</LayoutMainColumn>
</TestApp>, </TestApp>,
{ hooksConfig }, { hooksConfig },
); );
await expect(component.locator('main')).toHaveScreenshot(); await expect(component).toHaveScreenshot({
mask: [ page.locator('header'), page.locator('form') ],
maskColor: configs.maskColor,
});
}); });
test.describe('with apps', () => { test.describe('with apps', () => {
...@@ -189,11 +216,16 @@ test.describe('with apps', () => { ...@@ -189,11 +216,16 @@ test.describe('with apps', () => {
const component = await mount( const component = await mount(
<TestApp> <TestApp>
<SearchResults/> <LayoutMainColumn>
<SearchResults/>
</LayoutMainColumn>
</TestApp>, </TestApp>,
{ hooksConfig }, { hooksConfig },
); );
await expect(component.locator('main')).toHaveScreenshot(); await expect(component).toHaveScreenshot({
mask: [ page.locator('header'), page.locator('form') ],
maskColor: configs.maskColor,
});
}); });
}); });
...@@ -3,14 +3,16 @@ import { useRouter } from 'next/router'; ...@@ -3,14 +3,16 @@ import { useRouter } from 'next/router';
import type { FormEvent } from 'react'; import type { FormEvent } from 'react';
import React from 'react'; import React from 'react';
import IndexingAlertBlocks from 'ui/home/IndexingAlertBlocks';
import useMarketplaceApps from 'ui/marketplace/useMarketplaceApps'; import useMarketplaceApps from 'ui/marketplace/useMarketplaceApps';
import SearchResultListItem from 'ui/searchResults/SearchResultListItem'; import SearchResultListItem from 'ui/searchResults/SearchResultListItem';
import SearchResultsInput from 'ui/searchResults/SearchResultsInput'; import SearchResultsInput from 'ui/searchResults/SearchResultsInput';
import SearchResultTableItem from 'ui/searchResults/SearchResultTableItem'; import SearchResultTableItem from 'ui/searchResults/SearchResultTableItem';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary';
import ContentLoader from 'ui/shared/ContentLoader'; import ContentLoader from 'ui/shared/ContentLoader';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import Page from 'ui/shared/Page/Page'; import * as Layout from 'ui/shared/layout/components';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import Thead from 'ui/shared/TheadSticky'; import Thead from 'ui/shared/TheadSticky';
...@@ -169,10 +171,6 @@ const SearchResultsPageContent = () => { ...@@ -169,10 +171,6 @@ const SearchResultsPageContent = () => {
); );
}, [ handleSearchTermChange, handleSubmit, searchTerm ]); }, [ handleSearchTermChange, handleSubmit, searchTerm ]);
const renderHeader = React.useCallback(() => {
return <Header renderSearchBar={ renderSearchBar }/>;
}, [ renderSearchBar ]);
const pageContent = !showContent ? <ContentLoader/> : ( const pageContent = !showContent ? <ContentLoader/> : (
<> <>
<PageTitle title="Search results"/> <PageTitle title="Search results"/>
...@@ -182,9 +180,15 @@ const SearchResultsPageContent = () => { ...@@ -182,9 +180,15 @@ const SearchResultsPageContent = () => {
); );
return ( return (
<Page renderHeader={ renderHeader }> <>
{ pageContent } <IndexingAlertBlocks/>
</Page> <Header renderSearchBar={ renderSearchBar }/>
<AppErrorBoundary>
<Layout.Content>
{ pageContent }
</Layout.Content>
</AppErrorBoundary>
</>
); );
}; };
......
...@@ -7,7 +7,6 @@ import useIsMobile from 'lib/hooks/useIsMobile'; ...@@ -7,7 +7,6 @@ import useIsMobile from 'lib/hooks/useIsMobile';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import Sol2UmlDiagram from 'ui/sol2uml/Sol2UmlDiagram'; import Sol2UmlDiagram from 'ui/sol2uml/Sol2UmlDiagram';
...@@ -32,7 +31,7 @@ const Sol2Uml = () => { ...@@ -32,7 +31,7 @@ const Sol2Uml = () => {
}, [ appProps.referrer ]); }, [ appProps.referrer ]);
return ( return (
<Page> <>
<PageTitle <PageTitle
title="Solidity UML diagram" title="Solidity UML diagram"
backLink={ backLink } backLink={ backLink }
...@@ -45,7 +44,7 @@ const Sol2Uml = () => { ...@@ -45,7 +44,7 @@ const Sol2Uml = () => {
</Address> </Address>
</Flex> </Flex>
<Sol2UmlDiagram addressHash={ addressHash }/> <Sol2UmlDiagram addressHash={ addressHash }/>
</Page> </>
); );
}; };
......
...@@ -2,7 +2,6 @@ import { Box } from '@chakra-ui/react'; ...@@ -2,7 +2,6 @@ import { Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import config from 'configs/app'; import config from 'configs/app';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import ChartsWidgetsList from '../stats/ChartsWidgetsList'; import ChartsWidgetsList from '../stats/ChartsWidgetsList';
...@@ -25,7 +24,7 @@ const Stats = () => { ...@@ -25,7 +24,7 @@ const Stats = () => {
} = useStats(); } = useStats();
return ( return (
<Page> <>
<PageTitle title={ `${ config.chain.name } stats` }/> <PageTitle title={ `${ config.chain.name } stats` }/>
<Box mb={{ base: 6, sm: 8 }}> <Box mb={{ base: 6, sm: 8 }}>
...@@ -50,7 +49,7 @@ const Stats = () => { ...@@ -50,7 +49,7 @@ const Stats = () => {
charts={ displayedCharts } charts={ displayedCharts }
interval={ interval } interval={ interval }
/> />
</Page> </>
); );
}; };
......
import { test, expect } from '@playwright/experimental-ct-react'; import { test as base, expect } from '@playwright/experimental-ct-react';
import React from 'react'; import React from 'react';
import { data as withdrawalsData } from 'mocks/withdrawals/withdrawals'; import { data as withdrawalsData } from 'mocks/withdrawals/withdrawals';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl'; import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';
import Withdrawals from './Withdrawals'; import Withdrawals from './Withdrawals';
const test = base.extend({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
context: contextWithEnvs(configs.featureEnvs.beaconChain) as any,
});
const WITHDRAWALS_API_URL = buildApiUrl('withdrawals'); const WITHDRAWALS_API_URL = buildApiUrl('withdrawals');
const WITHDRAWALS_COUNTERS_API_URL = buildApiUrl('withdrawals_counters'); const WITHDRAWALS_COUNTERS_API_URL = buildApiUrl('withdrawals_counters');
......
import { Flex, Grid, Icon, Image, Box, Text, chakra, Skeleton, useColorMode } from '@chakra-ui/react'; import { Flex, Grid, Icon, Image, Box, Text, chakra, Skeleton, useColorMode } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { SearchResultItem } from 'types/api/search'; import type { SearchResultItem } from 'types/api/search';
import { route } from 'nextjs-routes';
import blockIcon from 'icons/block.svg'; import blockIcon from 'icons/block.svg';
import labelIcon from 'icons/publictags.svg'; import labelIcon from 'icons/publictags.svg';
import iconSuccess from 'icons/status/success.svg'; import iconSuccess from 'icons/status/success.svg';
......
import { chakra, Tr, Td, Text, Flex, Icon, Image, Box, Skeleton, useColorMode } from '@chakra-ui/react'; import { chakra, Tr, Td, Text, Flex, Icon, Image, Box, Skeleton, useColorMode } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { SearchResultItem } from 'types/api/search'; import type { SearchResultItem } from 'types/api/search';
import { route } from 'nextjs-routes';
import blockIcon from 'icons/block.svg'; import blockIcon from 'icons/block.svg';
import labelIcon from 'icons/publictags.svg'; import labelIcon from 'icons/publictags.svg';
import iconSuccess from 'icons/status/success.svg'; import iconSuccess from 'icons/status/success.svg';
......
import { MenuItem, Icon, chakra, useDisclosure } from '@chakra-ui/react'; import { MenuItem, Icon, chakra, useDisclosure } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import type { Route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import iconEdit from 'icons/edit.svg'; import iconEdit from 'icons/edit.svg';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
......
...@@ -2,34 +2,78 @@ import { test, expect } from '@playwright/experimental-ct-react'; ...@@ -2,34 +2,78 @@ import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react'; import React from 'react';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
import * as configs from 'playwright/utils/configs';
import AppError from './AppError'; import AppError from './AppError';
test.use({ viewport: { width: 900, height: 400 } });
test('status code 404', async({ mount }) => { test('status code 404', async({ mount }) => {
const error = { message: 'Not found', cause: { status: 404 } } as Error;
const component = await mount( const component = await mount(
<TestApp> <TestApp>
<AppError statusCode={ 404 }/> <AppError error={ error }/>
</TestApp>, </TestApp>,
); );
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
test('status code 422', async({ mount }) => { test('status code 422', async({ mount }) => {
const error = { message: 'Unprocessable entry', cause: { status: 422 } } as Error;
const component = await mount( const component = await mount(
<TestApp> <TestApp>
<AppError statusCode={ 422 }/> <AppError error={ error }/>
</TestApp>, </TestApp>,
); );
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
test('status code 500', async({ mount }) => { test('status code 500', async({ mount }) => {
const error = { message: 'Unknown error', cause: { status: 500 } } as Error;
const component = await mount(
<TestApp>
<AppError error={ error }/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test('invalid tx hash', async({ mount }) => {
const error = { message: 'Invalid tx hash', cause: { status: 404 } } as Error;
const component = await mount( const component = await mount(
<TestApp> <TestApp>
<AppError statusCode={ 500 }/> <AppError error={ error }/>
</TestApp>, </TestApp>,
); );
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
test('block lost consensus', async({ mount }) => {
const error = {
message: 'Not found',
cause: { payload: { message: 'Block lost consensus', hash: 'hash' } },
} as Error;
const component = await mount(
<TestApp>
<AppError error={ error }/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test('too many requests +@mobile', async({ mount, page }) => {
const error = {
message: 'Too many requests',
cause: { status: 429 },
} as Error;
const component = await mount(
<TestApp>
<AppError error={ error }/>
</TestApp>,
);
await page.waitForResponse('https://www.google.com/recaptcha/api2/**');
await expect(component).toHaveScreenshot({
mask: [ page.locator('.recaptcha') ],
maskColor: configs.maskColor,
});
});
import { Box, Button, Heading, Icon, Text, chakra } from '@chakra-ui/react'; import { Box, Button, Text } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import icon404 from 'icons/error-pages/404.svg'; import { route } from 'nextjs-routes';
import icon422 from 'icons/error-pages/422.svg';
import icon500 from 'icons/error-pages/500.svg'; import getErrorCauseStatusCode from 'lib/errors/getErrorCauseStatusCode';
import getErrorObjStatusCode from 'lib/errors/getErrorObjStatusCode';
import getResourceErrorPayload from 'lib/errors/getResourceErrorPayload';
import AppErrorIcon from './AppErrorIcon';
import AppErrorTitle from './AppErrorTitle';
import AppErrorBlockConsensus from './custom/AppErrorBlockConsensus';
import AppErrorInvalidTxHash from './custom/AppErrorInvalidTxHash';
import AppErrorTooManyRequests from './custom/AppErrorTooManyRequests';
interface Props { interface Props {
statusCode: number;
className?: string; className?: string;
error: Error | undefined;
} }
const ERRORS: Record<string, {icon: React.FunctionComponent<React.SVGAttributes<SVGElement>>; text: string; title: string }> = { const ERROR_TEXTS: Record<string, { title: string; text: string }> = {
'404': { '404': {
icon: icon404,
title: 'Page not found', title: 'Page not found',
text: 'This page is no longer explorable! If you are lost, use the search bar to find what you are looking for.', text: 'This page is no longer explorable! If you are lost, use the search bar to find what you are looking for.',
}, },
'422': { '422': {
icon: icon422,
title: 'Request cannot be processed', title: 'Request cannot be processed',
text: 'Your request contained an error, perhaps a mistyped tx/block/address hash. Try again, and check the developer tools console for more info.', text: 'Your request contained an error, perhaps a mistyped tx/block/address hash. Try again, and check the developer tools console for more info.',
}, },
'500': { '500': {
icon: icon500,
title: 'Oops! Something went wrong', title: 'Oops! Something went wrong',
text: 'An unexpected error has occurred. Try reloading the page, or come back soon and try again.', text: 'An unexpected error has occurred. Try reloading the page, or come back soon and try again.',
}, },
}; };
const AppError = ({ statusCode, className }: Props) => { const AppError = ({ error, className }: Props) => {
const error = ERRORS[String(statusCode)] || ERRORS['500']; const content = (() => {
const resourceErrorPayload = getResourceErrorPayload(error);
const messageInPayload =
resourceErrorPayload &&
typeof resourceErrorPayload === 'object' &&
'message' in resourceErrorPayload &&
typeof resourceErrorPayload.message === 'string' ?
resourceErrorPayload.message :
undefined;
const isInvalidTxHash = error?.message?.includes('Invalid tx hash');
const isBlockConsensus = messageInPayload?.includes('Block lost consensus');
if (isInvalidTxHash) {
return <AppErrorInvalidTxHash/>;
}
if (isBlockConsensus) {
const hash =
resourceErrorPayload &&
typeof resourceErrorPayload === 'object' &&
'hash' in resourceErrorPayload &&
typeof resourceErrorPayload.hash === 'string' ?
resourceErrorPayload.hash :
undefined;
return <AppErrorBlockConsensus hash={ hash }/>;
}
const statusCode = getErrorCauseStatusCode(error) || getErrorObjStatusCode(error);
switch (statusCode) {
case 429: {
return <AppErrorTooManyRequests/>;
}
default: {
const { title, text } = ERROR_TEXTS[String(statusCode)] ?? ERROR_TEXTS[500];
return (
<>
<AppErrorIcon statusCode={ statusCode }/>
<AppErrorTitle title={ title }/>
<Text variant="secondary" mt={ 3 }>{ text }</Text>
<Button
mt={ 8 }
size="lg"
variant="outline"
as="a"
href={ route({ pathname: '/' }) }
>
Back to home
</Button>
</>
);
}
}
})();
return ( return (
<Box className={ className }> <Box className={ className } mt={{ base: '52px', lg: '104px' }} maxW="800px">
<Icon as={ error.icon } width="200px" height="auto"/> { content }
<Heading mt={ 8 } size="2xl" fontFamily="body">{ error.title }</Heading>
<Text variant="secondary" mt={ 3 }> { error.text } </Text>
<Button
mt={ 8 }
size="lg"
variant="outline"
as="a"
href={ route({ pathname: '/' }) }
>
Back to home
</Button>
</Box> </Box>
); );
}; };
export default chakra(AppError); export default React.memo(AppError);
import { chakra } from '@chakra-ui/react';
import React from 'react';
import ErrorBoundary from 'ui/shared/ErrorBoundary';
import AppError from './AppError';
interface Props {
className?: string;
children: React.ReactNode;
onError?: (error: Error) => void;
}
const AppErrorBoundary = ({ className, children, onError }: Props) => {
const renderErrorScreen = React.useCallback((error?: Error) => {
return <AppError error={ error } className={ className }/>;
}, [ className ]);
return (
<ErrorBoundary renderErrorScreen={ renderErrorScreen } onError={ onError }>
{ children }
</ErrorBoundary>
);
};
export default React.memo(chakra(AppErrorBoundary));
import { Icon } from '@chakra-ui/react';
import React from 'react';
import icon404 from 'icons/error-pages/404.svg';
import icon422 from 'icons/error-pages/422.svg';
import icon500 from 'icons/error-pages/500.svg';
const ICONS: Record<string, React.FunctionComponent<React.SVGAttributes<SVGElement>> > = {
'404': icon404,
'422': icon422,
'500': icon500,
};
interface Props {
statusCode: number | undefined;
}
const AppErrorIcon = ({ statusCode }: Props) => {
return <Icon as={ ICONS[String(statusCode)] || ICONS['500'] } width="200px" height="auto"/>;
};
export default AppErrorIcon;
import { Heading } from '@chakra-ui/react';
import React from 'react';
interface Props {
title: string;
}
const AppErrorTitle = ({ title }: Props) => {
return <Heading mt={ 8 } size="2xl" fontFamily="body">{ title }</Heading>;
};
export default AppErrorTitle;
import { Box, Button, Heading, Icon, chakra } from '@chakra-ui/react'; import { Button } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import icon404 from 'icons/error-pages/404.svg'; import { route } from 'nextjs-routes';
import AppErrorIcon from '../AppErrorIcon';
import AppErrorTitle from '../AppErrorTitle';
interface Props { interface Props {
hash?: string; hash?: string;
className?: string;
} }
const AppErrorBlockConsensus = ({ hash, className }: Props) => { const AppErrorBlockConsensus = ({ hash }: Props) => {
return ( return (
<Box className={ className }> <>
<Icon as={ icon404 } width="200px" height="auto"/> <AppErrorIcon statusCode={ 404 }/>
<Heading mt={ 8 } size="2xl" fontFamily="body">Block removed due to chain reorganization</Heading> <AppErrorTitle title="Block removed due to chain reorganization"/>
<Button <Button
mt={ 8 } mt={ 8 }
size="lg" size="lg"
...@@ -23,8 +24,8 @@ const AppErrorBlockConsensus = ({ hash, className }: Props) => { ...@@ -23,8 +24,8 @@ const AppErrorBlockConsensus = ({ hash, className }: Props) => {
> >
{ hash ? 'View reorg' : 'Back to home' } { hash ? 'View reorg' : 'Back to home' }
</Button> </Button>
</Box> </>
); );
}; };
export default chakra(AppErrorBlockConsensus); export default AppErrorBlockConsensus;
/* eslint-disable max-len */ /* eslint-disable max-len */
import { Box, Heading, OrderedList, ListItem, Icon, useColorModeValue, Flex } from '@chakra-ui/react'; import { Box, OrderedList, ListItem, Icon, useColorModeValue, Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import AppErrorTitle from '../AppErrorTitle';
const AppErrorInvalidTxHash = () => { const AppErrorInvalidTxHash = () => {
const textColor = useColorModeValue('gray.500', 'gray.400'); const textColor = useColorModeValue('gray.500', 'gray.400');
const snippet = { const snippet = {
...@@ -13,7 +15,7 @@ const AppErrorInvalidTxHash = () => { ...@@ -13,7 +15,7 @@ const AppErrorInvalidTxHash = () => {
}; };
return ( return (
<Box mt="50px"> <>
<Box p={ 4 } borderColor={ snippet.borderColor } borderRadius="md" w="230px" borderWidth="1px"> <Box p={ 4 } borderColor={ snippet.borderColor } borderRadius="md" w="230px" borderWidth="1px">
<Flex alignItems="center" pb={ 4 } borderBottomWidth="1px" borderColor={ snippet.borderColor }> <Flex alignItems="center" pb={ 4 } borderBottomWidth="1px" borderColor={ snippet.borderColor }>
<Icon as={ txIcon } boxSize={ 8 } color={ snippet.iconColor } bgColor={ snippet.iconBg } p={ 1 } borderRadius="md"/> <Icon as={ txIcon } boxSize={ 8 } color={ snippet.iconColor } bgColor={ snippet.iconBg } p={ 1 } borderRadius="md"/>
...@@ -33,9 +35,7 @@ const AppErrorInvalidTxHash = () => { ...@@ -33,9 +35,7 @@ const AppErrorInvalidTxHash = () => {
</Flex> </Flex>
</Flex> </Flex>
</Box> </Box>
<Heading size="2xl" fontFamily="body" mt={ 6 }> <AppErrorTitle title="Sorry, we are unable to locate this transaction hash"/>
Sorry, we are unable to locate this transaction hash
</Heading>
<OrderedList color={ textColor } mt={ 3 } spacing={ 3 }> <OrderedList color={ textColor } mt={ 3 } spacing={ 3 }>
<ListItem> <ListItem>
If you have just submitted this transaction please wait for at least 30 seconds before refreshing this page. If you have just submitted this transaction please wait for at least 30 seconds before refreshing this page.
...@@ -50,7 +50,7 @@ const AppErrorInvalidTxHash = () => { ...@@ -50,7 +50,7 @@ const AppErrorInvalidTxHash = () => {
If it still does not show up after 1 hour, please check with your sender/exchange/wallet/transaction provider for additional information. If it still does not show up after 1 hour, please check with your sender/exchange/wallet/transaction provider for additional information.
</ListItem> </ListItem>
</OrderedList> </OrderedList>
</Box> </>
); );
}; };
......
import { Box, Heading, Icon, Text, chakra } from '@chakra-ui/react'; import { Box, Heading, Icon, Text } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import ReCaptcha from 'react-google-recaptcha'; import ReCaptcha from 'react-google-recaptcha';
...@@ -8,11 +8,7 @@ import buildUrl from 'lib/api/buildUrl'; ...@@ -8,11 +8,7 @@ import buildUrl from 'lib/api/buildUrl';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
import useToast from 'lib/hooks/useToast'; import useToast from 'lib/hooks/useToast';
interface Props { const AppErrorTooManyRequests = () => {
className?: string;
}
const AppErrorTooManyRequests = ({ className }: Props) => {
const toast = useToast(); const toast = useToast();
const fetch = useFetch(); const fetch = useFetch();
...@@ -47,7 +43,6 @@ const AppErrorTooManyRequests = ({ className }: Props) => { ...@@ -47,7 +43,6 @@ const AppErrorTooManyRequests = ({ className }: Props) => {
return ( return (
<Box <Box
className={ className }
sx={{ sx={{
'.recaptcha': { '.recaptcha': {
mt: 8, mt: 8,
...@@ -71,4 +66,4 @@ const AppErrorTooManyRequests = ({ className }: Props) => { ...@@ -71,4 +66,4 @@ const AppErrorTooManyRequests = ({ className }: Props) => {
); );
}; };
export default chakra(AppErrorTooManyRequests); export default AppErrorTooManyRequests;
import { Box, Flex } from '@chakra-ui/react';
import React from 'react';
import getErrorCauseStatusCode from 'lib/errors/getErrorCauseStatusCode';
import getResourceErrorPayload from 'lib/errors/getResourceErrorPayload';
import useAdblockDetect from 'lib/hooks/useAdblockDetect';
import useGetCsrfToken from 'lib/hooks/useGetCsrfToken';
import * as mixpanel from 'lib/mixpanel';
import AppError from 'ui/shared/AppError/AppError';
import AppErrorBlockConsensus from 'ui/shared/AppError/AppErrorBlockConsensus';
import AppErrorInvalidTxHash from 'ui/shared/AppError/AppErrorInvalidTxHash';
import ErrorBoundary from 'ui/shared/ErrorBoundary';
import PageContent from 'ui/shared/Page/PageContent';
import Footer from 'ui/snippets/footer/Footer';
import Header from 'ui/snippets/header/Header';
import NavigationDesktop from 'ui/snippets/navigation/NavigationDesktop';
interface Props {
children: React.ReactNode;
wrapChildren?: boolean;
isHomePage?: boolean;
renderHeader?: () => React.ReactNode;
}
const Page = ({
children,
wrapChildren = true,
isHomePage,
renderHeader,
}: Props) => {
useGetCsrfToken();
useAdblockDetect();
const isMixpanelInited = mixpanel.useInit();
mixpanel.useLogPageView(isMixpanelInited);
const renderErrorScreen = React.useCallback((error?: Error) => {
const statusCode = getErrorCauseStatusCode(error) || 500;
const resourceErrorPayload = getResourceErrorPayload(error);
const messageInPayload =
resourceErrorPayload &&
typeof resourceErrorPayload === 'object' &&
'message' in resourceErrorPayload &&
typeof resourceErrorPayload.message === 'string' ?
resourceErrorPayload.message :
undefined;
const isInvalidTxHash = error?.message?.includes('Invalid tx hash');
const isBlockConsensus = messageInPayload?.includes('Block lost consensus');
if (isInvalidTxHash) {
return <PageContent isHomePage={ isHomePage }><AppErrorInvalidTxHash/></PageContent>;
}
if (isBlockConsensus) {
const hash =
resourceErrorPayload &&
typeof resourceErrorPayload === 'object' &&
'hash' in resourceErrorPayload &&
typeof resourceErrorPayload.hash === 'string' ?
resourceErrorPayload.hash :
undefined;
return <PageContent isHomePage={ isHomePage }><AppErrorBlockConsensus hash={ hash } mt="50px"/></PageContent>;
}
return <PageContent isHomePage={ isHomePage }><AppError statusCode={ statusCode } mt="50px"/></PageContent>;
}, [ isHomePage ]);
const renderedChildren = wrapChildren ? (
<PageContent isHomePage={ isHomePage }>{ children }</PageContent>
) : children;
return (
<Box minWidth={{ base: '100vw', lg: 'fit-content' }}>
<Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/>
<Flex flexDir="column" flexGrow={ 1 } w={{ base: '100%', lg: 'auto' }}>
{ renderHeader ?
renderHeader() :
<Header isHomePage={ isHomePage }/>
}
<ErrorBoundary renderErrorScreen={ renderErrorScreen }>
{ renderedChildren }
</ErrorBoundary>
</Flex>
</Flex>
<Footer/>
</Box>
);
};
export default Page;
import { Box } from '@chakra-ui/react';
import React from 'react';
import config from 'configs/app';
import IndexingAlertBlocks from 'ui/home/IndexingAlertBlocks';
interface Props {
children: React.ReactNode;
isHomePage?: boolean;
}
const PageContent = ({ children, isHomePage }: Props) => {
return (
<Box
as="main"
w="100%"
paddingX={{ base: 4, lg: 12 }}
paddingBottom={ 10 }
paddingTop={{ base: isHomePage ? '88px' : '138px', lg: 0 }}
>
{ !config.UI.indexingAlert.isHidden && <IndexingAlertBlocks display={{ base: 'block', lg: 'none' }}/> }
{ children }
</Box>
);
};
export default PageContent;
import { Box, chakra, Skeleton } from '@chakra-ui/react'; import { Box, chakra, Skeleton } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import { route } from 'nextjs-routes';
import nftPlaceholder from 'icons/nft_shield.svg'; import nftPlaceholder from 'icons/nft_shield.svg';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import HashStringShorten from 'ui/shared/HashStringShorten'; import HashStringShorten from 'ui/shared/HashStringShorten';
......
import { chakra, shouldForwardProp, Tooltip, Box, Skeleton } from '@chakra-ui/react'; import { chakra, shouldForwardProp, Tooltip, Box, Skeleton } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import type { HTMLAttributeAnchorTarget } from 'react'; import type { HTMLAttributeAnchorTarget } from 'react';
import React from 'react'; import React from 'react';
import { route } from 'nextjs-routes';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import HashStringShorten from 'ui/shared/HashStringShorten'; import HashStringShorten from 'ui/shared/HashStringShorten';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
......
...@@ -4,21 +4,11 @@ import React from 'react'; ...@@ -4,21 +4,11 @@ import React from 'react';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl'; import buildApiUrl from 'playwright/utils/buildApiUrl';
import Page from './Page'; import Layout from './Layout';
const API_URL = buildApiUrl('homepage_indexing_status'); const API_URL = buildApiUrl('homepage_indexing_status');
test('without indexing alert +@mobile', async({ mount }) => { test('base view +@mobile', async({ mount, page }) => {
const component = await mount(
<TestApp>
<Page>Page Content</Page>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test('with indexing alert +@mobile', async({ mount, page }) => {
await page.route(API_URL, (route) => route.fulfill({ await page.route(API_URL, (route) => route.fulfill({
status: 200, status: 200,
body: JSON.stringify({ finished_indexing_blocks: false, indexed_blocks_ratio: 0.1 }), body: JSON.stringify({ finished_indexing_blocks: false, indexed_blocks_ratio: 0.1 }),
...@@ -26,7 +16,7 @@ test('with indexing alert +@mobile', async({ mount, page }) => { ...@@ -26,7 +16,7 @@ test('with indexing alert +@mobile', async({ mount, page }) => {
const component = await mount( const component = await mount(
<TestApp> <TestApp>
<Page>Page Content</Page> <Layout>Page Content</Layout>
</TestApp>, </TestApp>,
); );
......
import React from 'react';
import type { Props } from './types';
import IndexingAlertBlocks from 'ui/home/IndexingAlertBlocks';
import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary';
import Header from 'ui/snippets/header/Header';
import * as Layout from './components';
const LayoutDefault = ({ children }: Props) => {
return (
<Layout.Container>
<Layout.MainArea>
<Layout.SideBar/>
<Layout.MainColumn>
<IndexingAlertBlocks/>
<Header/>
<AppErrorBoundary>
<Layout.Content>
{ children }
</Layout.Content>
</AppErrorBoundary>
</Layout.MainColumn>
</Layout.MainArea>
<Layout.Footer/>
</Layout.Container>
);
};
export default LayoutDefault;
import { Box } from '@chakra-ui/react';
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import LayoutHome from './LayoutHome';
const API_URL = buildApiUrl('homepage_indexing_status');
test('base view +@mobile', async({ mount, page }) => {
await page.route(API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify({ finished_indexing_blocks: false, indexed_blocks_ratio: 0.1 }),
}));
const component = await mount(
<TestApp>
<LayoutHome>
<Box pt={ 10 }>Error</Box>
</LayoutHome>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
import React from 'react';
import type { Props } from './types';
import IndexingAlertBlocks from 'ui/home/IndexingAlertBlocks';
import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary';
import Header from 'ui/snippets/header/Header';
import * as Layout from './components';
const LayoutError = ({ children }: Props) => {
return (
<Layout.Container>
<Layout.MainArea>
<Layout.SideBar/>
<Layout.MainColumn>
<IndexingAlertBlocks/>
<Header/>
<AppErrorBoundary>
<main>
{ children }
</main>
</AppErrorBoundary>
</Layout.MainColumn>
</Layout.MainArea>
<Layout.Footer/>
</Layout.Container>
);
};
export default LayoutError;
...@@ -2,20 +2,23 @@ import { test, expect } from '@playwright/experimental-ct-react'; ...@@ -2,20 +2,23 @@ import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react'; import React from 'react';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
import * as configs from 'playwright/utils/configs'; import buildApiUrl from 'playwright/utils/buildApiUrl';
import AppErrorTooManyRequests from './AppErrorTooManyRequests'; import LayoutHome from './LayoutHome';
const API_URL = buildApiUrl('homepage_indexing_status');
test('base view +@mobile', async({ mount, page }) => {
await page.route(API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify({ finished_indexing_blocks: false, indexed_blocks_ratio: 0.1 }),
}));
test('default view +@mobile', async({ mount, page }) => {
const component = await mount( const component = await mount(
<TestApp> <TestApp>
<AppErrorTooManyRequests/> <LayoutHome>Page Content</LayoutHome>
</TestApp>, </TestApp>,
); );
await page.waitForResponse('https://www.google.com/recaptcha/api2/**');
await expect(component).toHaveScreenshot({ await expect(component).toHaveScreenshot();
mask: [ page.locator('.recaptcha') ],
maskColor: configs.maskColor,
});
}); });
import React from 'react';
import type { Props } from './types';
import IndexingAlertBlocks from 'ui/home/IndexingAlertBlocks';
import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary';
import Header from 'ui/snippets/header/Header';
import * as Layout from './components';
const LayoutHome = ({ children }: Props) => {
return (
<Layout.Container>
<Layout.MainArea>
<Layout.SideBar/>
<Layout.MainColumn
paddingTop={{ base: '88px', lg: 9 }}
>
<IndexingAlertBlocks/>
<Header isHomePage/>
<AppErrorBoundary>
{ children }
</AppErrorBoundary>
</Layout.MainColumn>
</Layout.MainArea>
<Layout.Footer/>
</Layout.Container>
);
};
export default LayoutHome;
import React from 'react';
import type { Props } from './types';
import * as Layout from './components';
const LayoutSearchResults = ({ children }: Props) => {
return (
<Layout.Container>
<Layout.MainArea>
<Layout.SideBar/>
<Layout.MainColumn>
{ children }
</Layout.MainColumn>
</Layout.MainArea>
<Layout.Footer/>
</Layout.Container>
);
};
export default LayoutSearchResults;
import { Box } from '@chakra-ui/react';
import React from 'react';
interface Props {
children: React.ReactNode;
}
const Container = ({ children }: Props) => {
return (
<Box minWidth={{ base: '100vw', lg: 'fit-content' }}>
{ children }
</Box>
);
};
export default React.memo(Container);
import { Box } from '@chakra-ui/react';
import React from 'react';
interface Props {
children: React.ReactNode;
}
const Content = ({ children }: Props) => {
return (
<Box pt={{ base: 0, lg: '52px' }} as="main">
{ children }
</Box>
);
};
export default React.memo(Content);
import { Flex } from '@chakra-ui/react';
import React from 'react';
interface Props {
children: React.ReactNode;
}
const MainArea = ({ children }: Props) => {
return (
<Flex w="100%" minH="100vh" alignItems="stretch">
{ children }
</Flex>
);
};
export default React.memo(MainArea);
import { Flex, chakra } from '@chakra-ui/react';
import React from 'react';
interface Props {
className?: string;
children: React.ReactNode;
}
const MainColumn = ({ children, className }: Props) => {
return (
<Flex
className={ className }
flexDir="column"
flexGrow={ 1 }
w={{ base: '100%', lg: 'auto' }}
paddingX={{ base: 4, lg: 12 }}
paddingTop={{ base: '138px', lg: 9 }}
paddingBottom={ 10 }
>
{ children }
</Flex>
);
};
export default React.memo(chakra(MainColumn));
import NavigationDesktop from 'ui/snippets/navigation/NavigationDesktop';
export default NavigationDesktop;
import Footer from 'ui/snippets/footer/Footer';
import Container from './Container';
import Content from './Content';
import MainArea from './MainArea';
import MainColumn from './MainColumn';
import SideBar from './SideBar';
export {
Container,
Content,
MainArea,
SideBar,
MainColumn,
Footer,
};
// Container
// MainArea
// SideBar
// MainColumn
// Content
// Footer
export interface Props {
children: React.ReactNode;
}
import { Grid, GridItem, Tooltip, Button, useColorModeValue, Alert, Link, Skeleton } from '@chakra-ui/react'; import { Grid, GridItem, Tooltip, Button, useColorModeValue, Alert, Link, Skeleton } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Log } from 'types/api/log'; import type { Log } from 'types/api/log';
import { route } from 'nextjs-routes';
// import searchIcon from 'icons/search.svg'; // import searchIcon from 'icons/search.svg';
import { space } from 'lib/html-entities'; import { space } from 'lib/html-entities';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
......
import { AspectRatio, chakra, Skeleton } from '@chakra-ui/react'; import { AspectRatio, chakra, Skeleton } from '@chakra-ui/react';
import React from 'react';
import type { StaticRoute } from 'nextjs-routes'; import type { StaticRoute } from 'nextjs-routes';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import React from 'react';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
......
...@@ -78,7 +78,7 @@ const Footer = () => { ...@@ -78,7 +78,7 @@ const Footer = () => {
}); });
return ( return (
<Flex direction={{ base: 'column', lg: 'row' }} p={{ base: 4, lg: 9 }} borderTop="1px solid" borderColor="divider"> <Flex direction={{ base: 'column', lg: 'row' }} p={{ base: 4, lg: 9 }} borderTop="1px solid" borderColor="divider" as="footer">
<Box flexGrow="1" mb={{ base: 8, lg: 0 }}> <Box flexGrow="1" mb={{ base: 8, lg: 0 }}>
<Flex> <Flex>
<ColorModeToggler/> <ColorModeToggler/>
......
...@@ -3,7 +3,6 @@ import React from 'react'; ...@@ -3,7 +3,6 @@ import React from 'react';
import config from 'configs/app'; import config from 'configs/app';
import { useScrollDirection } from 'lib/contexts/scrollDirection'; import { useScrollDirection } from 'lib/contexts/scrollDirection';
import IndexingAlertBlocks from 'ui/home/IndexingAlertBlocks';
import NetworkLogo from 'ui/snippets/networkMenu/NetworkLogo'; import NetworkLogo from 'ui/snippets/networkMenu/NetworkLogo';
import ProfileMenuDesktop from 'ui/snippets/profileMenu/ProfileMenuDesktop'; import ProfileMenuDesktop from 'ui/snippets/profileMenu/ProfileMenuDesktop';
import ProfileMenuMobile from 'ui/snippets/profileMenu/ProfileMenuMobile'; import ProfileMenuMobile from 'ui/snippets/profileMenu/ProfileMenuMobile';
...@@ -47,12 +46,7 @@ const Header = ({ isHomePage, renderSearchBar }: Props) => { ...@@ -47,12 +46,7 @@ const Header = ({ isHomePage, renderSearchBar }: Props) => {
</Flex> </Flex>
{ !isHomePage && searchBar } { !isHomePage && searchBar }
</Box> </Box>
<Box <Box display={{ base: 'none', lg: 'block' }}>
paddingX={ 12 }
paddingTop={ 9 }
display={{ base: 'none', lg: 'block' }}
>
{ !config.UI.indexingAlert.isHidden && <IndexingAlertBlocks/> }
{ !isHomePage && ( { !isHomePage && (
<HStack <HStack
as="header" as="header"
...@@ -60,7 +54,6 @@ const Header = ({ isHomePage, renderSearchBar }: Props) => { ...@@ -60,7 +54,6 @@ const Header = ({ isHomePage, renderSearchBar }: Props) => {
alignItems="center" alignItems="center"
justifyContent="center" justifyContent="center"
gap={ 12 } gap={ 12 }
paddingBottom="52px"
> >
<Box width="100%"> <Box width="100%">
{ searchBar } { searchBar }
......
import { Link, Text, HStack, Tooltip, Box, useBreakpointValue, chakra, shouldForwardProp } from '@chakra-ui/react'; import { Link, Text, HStack, Tooltip, Box, useBreakpointValue, chakra, shouldForwardProp } from '@chakra-ui/react';
import NextLink from 'next/link'; import NextLink from 'next/link';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { NavItem } from 'types/client/navigation-items'; import type { NavItem } from 'types/client/navigation-items';
import { route } from 'nextjs-routes';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import { isInternalItem } from 'lib/hooks/useNavItems'; import { isInternalItem } from 'lib/hooks/useNavItems';
......
import { Icon, Box, Image, useColorModeValue, Skeleton } from '@chakra-ui/react'; import { Icon, Box, Image, useColorModeValue, Skeleton } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import iconPlaceholder from 'icons/networks/icon-placeholder.svg'; import iconPlaceholder from 'icons/networks/icon-placeholder.svg';
import logoPlaceholder from 'icons/networks/logo-placeholder.svg'; import logoPlaceholder from 'icons/networks/logo-placeholder.svg';
......
...@@ -5,7 +5,6 @@ import React from 'react'; ...@@ -5,7 +5,6 @@ import React from 'react';
import * as textAdMock from 'mocks/ad/textAd'; import * as textAdMock from 'mocks/ad/textAd';
import { apps as appsMock } from 'mocks/apps/apps'; import { apps as appsMock } from 'mocks/apps/apps';
import * as searchMock from 'mocks/search/index'; import * as searchMock from 'mocks/search/index';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl'; import buildApiUrl from 'playwright/utils/buildApiUrl';
...@@ -285,15 +284,9 @@ test('recent keywords suggest +@mobile', async({ mount, page }) => { ...@@ -285,15 +284,9 @@ test('recent keywords suggest +@mobile', async({ mount, page }) => {
}); });
test.describe('with apps', () => { test.describe('with apps', () => {
const MARKETPLACE_CONFIG_URL = 'https://marketplace-config.url'; const MARKETPLACE_CONFIG_URL = 'https://localhost:3000/marketplace-config.json';
const extendedTest = test.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', value: MARKETPLACE_CONFIG_URL },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
]) as any,
});
extendedTest('default view +@mobile', async({ mount, page }) => { test('default view +@mobile', async({ mount, page }) => {
const API_URL = buildApiUrl('search') + '?q=o'; const API_URL = buildApiUrl('search') + '?q=o';
await page.route(API_URL, (route) => route.fulfill({ await page.route(API_URL, (route) => route.fulfill({
status: 200, status: 200,
......
import { Box, Popover, PopoverTrigger, PopoverContent, PopoverBody, useDisclosure, PopoverFooter } from '@chakra-ui/react'; import { Box, Popover, PopoverTrigger, PopoverContent, PopoverBody, useDisclosure, PopoverFooter } from '@chakra-ui/react';
import _debounce from 'lodash/debounce'; import _debounce from 'lodash/debounce';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { route } from 'nextjs-routes';
import type { FormEvent, FocusEvent } from 'react'; import type { FormEvent, FocusEvent } from 'react';
import React from 'react'; import React from 'react';
import { Element } from 'react-scroll'; import { Element } from 'react-scroll';
import { route } from 'nextjs-routes';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import { getRecentSearchKeywords, saveToRecentKeywords } from 'lib/recentSearchKeywords'; import { getRecentSearchKeywords, saveToRecentKeywords } from 'lib/recentSearchKeywords';
......
import type { LinkProps as NextLinkProps } from 'next/link'; import type { LinkProps as NextLinkProps } from 'next/link';
import NextLink from 'next/link'; import NextLink from 'next/link';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { SearchResultItem } from 'types/api/search'; import type { SearchResultItem } from 'types/api/search';
import { route } from 'nextjs-routes';
import SearchBarSuggestAddress from './SearchBarSuggestAddress'; import SearchBarSuggestAddress from './SearchBarSuggestAddress';
import SearchBarSuggestBlock from './SearchBarSuggestBlock'; import SearchBarSuggestBlock from './SearchBarSuggestBlock';
import SearchBarSuggestItemLink from './SearchBarSuggestItemLink'; import SearchBarSuggestItemLink from './SearchBarSuggestItemLink';
......
import { Skeleton } from '@chakra-ui/react'; import { Skeleton } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import { route } from 'nextjs-routes';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
......
import { Hide, HStack, Show } from '@chakra-ui/react'; import { Hide, HStack, Show } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import type { Query } from 'nextjs-routes';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import type { TokenType } from 'types/api/token'; import type { TokenType } from 'types/api/token';
import type { TokensSorting } from 'types/api/tokens'; import type { TokensSorting } from 'types/api/tokens';
import type { Query } from 'nextjs-routes';
import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery'; import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery';
import useDebounce from 'lib/hooks/useDebounce'; import useDebounce from 'lib/hooks/useDebounce';
import { apos } from 'lib/html-entities'; import { apos } from 'lib/html-entities';
......
import { Flex, Link, Text, Icon, Box } from '@chakra-ui/react'; import { Flex, Link, Text, Icon, Box } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { TokenInfo } from 'types/api/token'; import type { TokenInfo } from 'types/api/token';
import { route } from 'nextjs-routes';
import nftIcon from 'icons/nft_shield.svg'; import nftIcon from 'icons/nft_shield.svg';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import HashStringShorten from 'ui/shared/HashStringShorten'; import HashStringShorten from 'ui/shared/HashStringShorten';
......
...@@ -154,10 +154,8 @@ test('with actions uniswap +@mobile +@dark-mode', async({ mount, page }) => { ...@@ -154,10 +154,8 @@ test('with actions uniswap +@mobile +@dark-mode', async({ mount, page }) => {
}); });
const l2Test = test.extend({ const l2Test = test.extend({
context: contextWithEnvs([ // eslint-disable-next-line @typescript-eslint/no-explicit-any
{ name: 'NEXT_PUBLIC_IS_L2_NETWORK', value: 'true' }, context: contextWithEnvs(configs.featureEnvs.rollup) as any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
]) as any,
}); });
l2Test('l2', async({ mount, page }) => { l2Test('l2', async({ mount, page }) => {
......
...@@ -14,10 +14,11 @@ import { ...@@ -14,10 +14,11 @@ import {
Alert, Alert,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import { scroller, Element } from 'react-scroll'; import { scroller, Element } from 'react-scroll';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import clockIcon from 'icons/clock.svg'; import clockIcon from 'icons/clock.svg';
import flameIcon from 'icons/flame.svg'; import flameIcon from 'icons/flame.svg';
......
import { Flex, Link, Icon, chakra } from '@chakra-ui/react'; import { Flex, Link, Icon, chakra } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { TxAction, TxActionGeneral } from 'types/api/txAction'; import type { TxAction, TxActionGeneral } from 'types/api/txAction';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import uniswapIcon from 'icons/uniswap.svg'; import uniswapIcon from 'icons/uniswap.svg';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
......
import { Icon, GridItem, Show, Flex } from '@chakra-ui/react'; import { Icon, GridItem, Show, Flex } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { TokenTransfer } from 'types/api/tokenTransfer'; import type { TokenTransfer } from 'types/api/tokenTransfer';
import { route } from 'nextjs-routes';
import tokenIcon from 'icons/token.svg'; import tokenIcon from 'icons/token.svg';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
......
import { Box, Heading, Text, Flex } from '@chakra-ui/react'; import { Box, Heading, Text, Flex } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Transaction } from 'types/api/transaction'; import type { Transaction } from 'types/api/transaction';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import getValueWithUnit from 'lib/getValueWithUnit'; import getValueWithUnit from 'lib/getValueWithUnit';
import CurrencyValue from 'ui/shared/CurrencyValue'; import CurrencyValue from 'ui/shared/CurrencyValue';
......
...@@ -4,11 +4,12 @@ import { ...@@ -4,11 +4,12 @@ import {
Flex, Flex,
Skeleton, Skeleton,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Transaction } from 'types/api/transaction'; import type { Transaction } from 'types/api/transaction';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import transactionIcon from 'icons/transactions.svg'; import transactionIcon from 'icons/transactions.svg';
......
...@@ -9,11 +9,12 @@ import { ...@@ -9,11 +9,12 @@ import {
Box, Box,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { Transaction } from 'types/api/transaction'; import type { Transaction } from 'types/api/transaction';
import { route } from 'nextjs-routes';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
......
import { Flex, Icon, Skeleton } from '@chakra-ui/react'; import { Flex, Icon, Skeleton } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { AddressWithdrawalsItem } from 'types/api/address'; import type { AddressWithdrawalsItem } from 'types/api/address';
import type { BlockWithdrawalsItem } from 'types/api/block'; import type { BlockWithdrawalsItem } from 'types/api/block';
import type { WithdrawalsItem } from 'types/api/withdrawals'; import type { WithdrawalsItem } from 'types/api/withdrawals';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import blockIcon from 'icons/block.svg'; import blockIcon from 'icons/block.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
......
import { Td, Tr, Icon, Skeleton, Flex } from '@chakra-ui/react'; import { Td, Tr, Icon, Skeleton, Flex } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { AddressWithdrawalsItem } from 'types/api/address'; import type { AddressWithdrawalsItem } from 'types/api/address';
import type { BlockWithdrawalsItem } from 'types/api/block'; import type { BlockWithdrawalsItem } from 'types/api/block';
import type { WithdrawalsItem } from 'types/api/withdrawals'; import type { WithdrawalsItem } from 'types/api/withdrawals';
import { route } from 'nextjs-routes';
import blockIcon from 'icons/block.svg'; import blockIcon from 'icons/block.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
......
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