Commit 7fee6df6 authored by tom goriunov's avatar tom goriunov Committed by GitHub

update page titles (#1065)

* metadata helpers and values for txs pages

* update titles to the rest of the pages

* refactor getServerSideProps

* dynamically update metadata for token and dapp page

* test

* 404 error page
parent 5d98955a
...@@ -14,6 +14,10 @@ import 'lib/setLocale'; ...@@ -14,6 +14,10 @@ import 'lib/setLocale';
const PAGE_PROPS = { const PAGE_PROPS = {
cookies: '', cookies: '',
referrer: '', referrer: '',
id: '',
height_or_hash: '',
hash: '',
q: '',
}; };
const TestApp = ({ children }: {children: React.ReactNode}) => { const TestApp = ({ children }: {children: React.ReactNode}) => {
......
...@@ -7,7 +7,14 @@ type Props = { ...@@ -7,7 +7,14 @@ type Props = {
pageProps: PageProps; pageProps: PageProps;
} }
const AppContext = createContext<PageProps>({ cookies: '', referrer: '' }); const AppContext = createContext<PageProps>({
cookies: '',
referrer: '',
id: '',
height_or_hash: '',
hash: '',
q: '',
});
export function AppContextProvider({ children, pageProps }: Props) { export function AppContextProvider({ children, pageProps }: Props) {
return ( return (
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`generates correct metadata for: dynamic route 1`] = `
{
"description": "View transaction 0x12345 on Blockscout (Blockscout) Explorer",
"title": "Blockscout transaction 0x12345 | Blockscout",
}
`;
exports[`generates correct metadata for: dynamic route with API data 1`] = `
{
"description": "0x12345, balances and analytics on the Blockscout (Blockscout) Explorer",
"title": "Blockscout USDT token details | Blockscout",
}
`;
exports[`generates correct metadata for: static route 1`] = `
{
"description": "Blockscout is the #1 open-source blockchain explorer available today. 100+ chains and counting rely on Blockscout data availability, APIs, and ecosystem tools to support their networks.",
"title": "Blockscout blocks | Blockscout",
}
`;
export default function compileValue(template: string, params: Record<string, string | Array<string> | undefined>) {
const PLACEHOLDER_REGEX = /%(\w+)%/g;
return template.replaceAll(PLACEHOLDER_REGEX, (match, p1) => {
const value = params[p1];
if (Array.isArray(value)) {
return value.join(', ');
}
if (value === undefined) {
return '';
}
return value;
});
}
import type { Route } from 'nextjs-routes';
import type { ApiData } from './types';
import generate from './generate';
interface TestCase<R extends Route> {
title: string;
route: R;
apiData?: ApiData<R>;
}
const TEST_CASES: Array<TestCase<Route>> = [
{
title: 'static route',
route: {
pathname: '/blocks',
},
},
{
title: 'dynamic route',
route: {
pathname: '/tx/[hash]',
query: { hash: '0x12345' },
},
},
{
title: 'dynamic route with API data',
route: {
pathname: '/token/[hash]',
query: { hash: '0x12345' },
},
apiData: { symbol: 'USDT' },
} as TestCase<{ pathname: '/token/[hash]'; query: { hash: string }}>,
];
describe('generates correct metadata for:', () => {
TEST_CASES.forEach((testCase) => {
it(`${ testCase.title }`, () => {
const result = generate(testCase.route, testCase.apiData);
expect(result).toMatchSnapshot();
});
});
});
import type { Route } from 'nextjs-routes';
import type { ApiData, Metadata } from './types';
import appConfig from 'configs/app/config';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
import compileValue from './compileValue';
import * as templates from './templates';
export default function generate<R extends Route>(route: R, apiData?: ApiData<R>): Metadata {
const params = {
...route.query,
...apiData,
network_name: appConfig.network.name,
network_title: getNetworkTitle(),
};
const title = compileValue(templates.title.make(route.pathname), params);
const description = compileValue(templates.description.make(route.pathname), params);
return {
title,
description,
};
}
export { default as generate } from './generate';
export { default as update } from './update';
import type { Route } from 'nextjs-routes';
// equal og:description
// eslint-disable-next-line max-len
const DEFAULT_TEMPLATE = 'Blockscout is the #1 open-source blockchain explorer available today. 100+ chains and counting rely on Blockscout data availability, APIs, and ecosystem tools to support their networks.';
// FIXME all page descriptions will be updated later
const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/': DEFAULT_TEMPLATE,
'/txs': DEFAULT_TEMPLATE,
'/tx/[hash]': 'View transaction %hash% on %network_title%',
'/blocks': DEFAULT_TEMPLATE,
'/block/[height_or_hash]': 'View the transactions, token transfers, and uncles for block %height_or_hash%',
'/accounts': DEFAULT_TEMPLATE,
'/address/[hash]': 'View the account balance, transactions, and other data for %hash% on the %network_title%',
'/verified-contracts': DEFAULT_TEMPLATE,
'/address/[hash]/contract-verification': 'View the account balance, transactions, and other data for %hash% on the %network_title%',
'/tokens': DEFAULT_TEMPLATE,
'/token/[hash]': '%hash%, balances and analytics on the %network_title%',
'/token/[hash]/instance/[id]': '%hash%, balances and analytics on the %network_title%',
'/apps': DEFAULT_TEMPLATE,
'/apps/[id]': DEFAULT_TEMPLATE,
'/stats': DEFAULT_TEMPLATE,
'/api-docs': DEFAULT_TEMPLATE,
'/graphiql': DEFAULT_TEMPLATE,
'/search-results': DEFAULT_TEMPLATE,
'/auth/profile': DEFAULT_TEMPLATE,
'/account/watchlist': DEFAULT_TEMPLATE,
'/account/api-key': DEFAULT_TEMPLATE,
'/account/custom-abi': DEFAULT_TEMPLATE,
'/account/public-tags-request': DEFAULT_TEMPLATE,
'/account/tag-address': DEFAULT_TEMPLATE,
'/account/verified-addresses': DEFAULT_TEMPLATE,
'/withdrawals': DEFAULT_TEMPLATE,
'/visualize/sol2uml': DEFAULT_TEMPLATE,
'/csv-export': DEFAULT_TEMPLATE,
'/l2-deposits': DEFAULT_TEMPLATE,
'/l2-output-roots': DEFAULT_TEMPLATE,
'/l2-txn-batches': DEFAULT_TEMPLATE,
'/l2-withdrawals': DEFAULT_TEMPLATE,
'/404': DEFAULT_TEMPLATE,
// service routes, added only to make typescript happy
'/login': DEFAULT_TEMPLATE,
'/api/media-type': DEFAULT_TEMPLATE,
'/api/proxy': DEFAULT_TEMPLATE,
'/api/csrf': DEFAULT_TEMPLATE,
'/api/healthz': DEFAULT_TEMPLATE,
'/auth/auth0': DEFAULT_TEMPLATE,
'/auth/unverified-email': DEFAULT_TEMPLATE,
};
export function make(pathname: Route['pathname']) {
const template = TEMPLATE_MAP[pathname];
return template ?? '';
}
export * as title from './title';
export * as description from './description';
import type { Route } from 'nextjs-routes';
const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/': 'blockchain explorer',
'/txs': 'transactions',
'/tx/[hash]': 'transaction %hash%',
'/blocks': 'blocks',
'/block/[height_or_hash]': 'block %height_or_hash%',
'/accounts': 'top accounts',
'/address/[hash]': 'address details for %hash%',
'/verified-contracts': 'verified contracts',
'/address/[hash]/contract-verification': 'contract verification for %hash%',
'/tokens': 'tokens',
'/token/[hash]': '%symbol% token details',
'/token/[hash]/instance/[id]': 'token instance for %symbol%',
'/apps': 'apps marketplace',
'/apps/[id]': '- %app_name%',
'/stats': 'statistics',
'/api-docs': 'REST API',
'/graphiql': 'GraphQL',
'/search-results': 'search result for %q%',
'/auth/profile': '- my profile',
'/account/watchlist': '- watchlist',
'/account/api-key': '- API keys',
'/account/custom-abi': '- custom ABI',
'/account/public-tags-request': '- public tag requests',
'/account/tag-address': '- private tags',
'/account/verified-addresses': '- my verified addresses',
'/withdrawals': 'withdrawals',
'/visualize/sol2uml': 'Solidity UML diagram',
'/csv-export': 'export data to CSV',
'/l2-deposits': 'deposits (L1 > L2)',
'/l2-output-roots': 'output roots',
'/l2-txn-batches': 'Tx batches (L2 blocks)',
'/l2-withdrawals': 'withdrawals (L2 > L1)',
'/404': 'error - page not found',
// service routes, added only to make typescript happy
'/login': 'login',
'/api/media-type': 'node API media type',
'/api/proxy': 'node API proxy',
'/api/csrf': 'node API CSRF token',
'/api/healthz': 'node API health check',
'/auth/auth0': 'authentication',
'/auth/unverified-email': 'unverified email',
};
export function make(pathname: Route['pathname']) {
const template = TEMPLATE_MAP[pathname];
return `%network_name% ${ template } | Blockscout`;
}
import type { Route } from 'nextjs-routes';
/* eslint-disable @typescript-eslint/indent */
export type ApiData<R extends Route> =
R['pathname'] extends '/token/[hash]' ? { symbol: string } :
R['pathname'] extends '/token/[hash]/instance/[id]' ? { symbol: string } :
R['pathname'] extends '/apps/[id]' ? { app_name: string } :
never;
export interface Metadata {
title: string;
description: string;
}
import type { Route } from 'nextjs-routes';
import type { ApiData } from './types';
import generate from './generate';
export default function update<R extends Route>(route: R, apiData: ApiData<R>) {
const { title, description } = generate(route, apiData);
window.document.title = title;
window.document.querySelector('meta[name="description"]')?.setAttribute('content', description);
}
...@@ -33,6 +33,7 @@ const PAGE_TYPE_DICT: Record<Route['pathname'], string> = { ...@@ -33,6 +33,7 @@ const PAGE_TYPE_DICT: Record<Route['pathname'], string> = {
'/l2-output-roots': 'Output roots', '/l2-output-roots': 'Output roots',
'/l2-txn-batches': 'Tx batches (L2 blocks)', '/l2-txn-batches': 'Tx batches (L2 blocks)',
'/l2-withdrawals': 'Withdrawals (L2 > L1)', '/l2-withdrawals': 'Withdrawals (L2 > L1)',
'/404': '404',
// service routes, added only to make typescript happy // service routes, added only to make typescript happy
'/login': 'Login', '/login': 'Login',
...@@ -41,7 +42,7 @@ const PAGE_TYPE_DICT: Record<Route['pathname'], string> = { ...@@ -41,7 +42,7 @@ const PAGE_TYPE_DICT: Record<Route['pathname'], string> = {
'/api/csrf': 'Node API: CSRF token', '/api/csrf': 'Node API: CSRF token',
'/api/healthz': 'Node API: Health check', '/api/healthz': 'Node API: Health check',
'/auth/auth0': 'Auth', '/auth/auth0': 'Auth',
'/auth/unverified-email': 'Auth', '/auth/unverified-email': 'Unverified email',
}; };
export default function getPageType(pathname: Route['pathname']) { export default function getPageType(pathname: Route['pathname']) {
......
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
// TODO delete when page descriptions is refactored
export default function getNetworkTitle() { export default function getNetworkTitle() {
return appConfig.network.name + (appConfig.network.shortName ? ` (${ appConfig.network.shortName })` : '') + ' Explorer'; return appConfig.network.name + (appConfig.network.shortName ? ` (${ appConfig.network.shortName })` : '') + ' Explorer';
} }
import Head from 'next/head';
import type { Route } from 'nextjs-routes';
import React from 'react';
import * as metadata from 'lib/metadata';
type Props = Route & {
children: React.ReactNode;
}
const PageServer = (props: Props) => {
const { title, description } = metadata.generate(props);
return (
<>
<Head>
<title>{ title }</title>
<meta name="description" content={ description }/>
</Head>
{ props.children }
</>
);
};
export default React.memo(PageServer);
import appConfig from 'configs/app/config';
import { getServerSideProps as base } from '../getServerSideProps';
export const getServerSideProps: typeof base = async(...args) => {
if (!appConfig.account.isEnabled) {
return {
notFound: true,
};
}
return base(...args);
};
export const getServerSidePropsForVerifiedAddresses: typeof base = async(...args) => {
if (!appConfig.account.isEnabled || !appConfig.adminServiceApi.endpoint || !appConfig.contractInfoApi.endpoint) {
return {
notFound: true,
};
}
return base(...args);
};
import type { RoutedQuery } from 'nextjs-routes';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
export default function getSeo(params: RoutedQuery<'/address/[hash]'>) {
const networkTitle = getNetworkTitle();
return {
title: params ? `${ params.hash } - ${ networkTitle }` : '',
description: params ?
`View the account balance, transactions, and other data for ${ params.hash } on the ${ networkTitle }` :
'',
};
}
import type { RoutedQuery } from 'nextjs-routes';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
export default function getSeo(params?: RoutedQuery<'/block/[height_or_hash]'>) {
const networkTitle = getNetworkTitle();
const isHash = params ? params.height_or_hash.startsWith('0x') : false;
return {
title: params ? `Block ${ params.height_or_hash } - ${ networkTitle }` : '',
description: params ?
`View the transactions, token transfers, and uncles for block ${ isHash ? '' : 'number ' }${ params.height_or_hash }` : '',
};
}
import getNetworkTitle from 'lib/networks/getNetworkTitle';
export default function getSeo() {
return {
title: getNetworkTitle(),
};
}
import type { GetServerSideProps } from 'next'; import type { GetServerSideProps } from 'next';
import appConfig from 'configs/app/config';
export type Props = { export type Props = {
cookies: string; cookies: string;
referrer: string; referrer: string;
id?: string; id: string;
height?: string; height_or_hash: string;
hash?: string; hash: string;
q: string;
} }
export const getServerSideProps: GetServerSideProps<Props> = async({ req, query }) => { export const base: GetServerSideProps<Props> = async({ req, query }) => {
return { return {
props: { props: {
cookies: req.headers.cookie || '', cookies: req.headers.cookie || '',
...@@ -16,6 +19,87 @@ export const getServerSideProps: GetServerSideProps<Props> = async({ req, query ...@@ -16,6 +19,87 @@ export const getServerSideProps: GetServerSideProps<Props> = async({ req, query
id: query.id?.toString() || '', id: query.id?.toString() || '',
hash: query.hash?.toString() || '', hash: query.hash?.toString() || '',
height_or_hash: query.height_or_hash?.toString() || '', height_or_hash: query.height_or_hash?.toString() || '',
q: query.q?.toString() || '',
}, },
}; };
}; };
export const account: GetServerSideProps<Props> = async(context) => {
if (!appConfig.account.isEnabled) {
return {
notFound: true,
};
}
return base(context);
};
export const verifiedAddresses: GetServerSideProps<Props> = async(context) => {
if (!appConfig.adminServiceApi.endpoint || !appConfig.contractInfoApi.endpoint) {
return {
notFound: true,
};
}
return account(context);
};
export const beaconChain: GetServerSideProps<Props> = async(context) => {
if (!appConfig.beaconChain.hasBeaconChain) {
return {
notFound: true,
};
}
return base(context);
};
export const L2: GetServerSideProps<Props> = async(context) => {
if (!appConfig.L2.isL2Network) {
return {
notFound: true,
};
}
return base(context);
};
export const marketplace: GetServerSideProps<Props> = async(context) => {
if (!appConfig.marketplace.configUrl || !appConfig.network.rpcUrl) {
return {
notFound: true,
};
}
return base(context);
};
export const apiDocs: GetServerSideProps<Props> = async(context) => {
if (!appConfig.apiDoc.specUrl) {
return {
notFound: true,
};
}
return base(context);
};
export const csvExport: GetServerSideProps<Props> = async(context) => {
if (!appConfig.reCaptcha.siteKey) {
return {
notFound: true,
};
}
return base(context);
};
export const stats: GetServerSideProps<Props> = async(context) => {
if (!appConfig.statsApi.endpoint) {
return {
notFound: true,
};
}
return base(context);
};
import type { GetServerSideProps } from 'next';
import appConfig from 'configs/app/config';
import type { Props } from 'lib/next/getServerSideProps';
import { getServerSideProps as getServerSidePropsBase } from 'lib/next/getServerSideProps';
export const getServerSideProps: GetServerSideProps<Props> = async(args) => {
if (!appConfig.beaconChain.hasBeaconChain) {
return {
notFound: true,
};
}
return getServerSidePropsBase(args);
};
import type { GetServerSideProps } from 'next';
import appConfig from 'configs/app/config';
import type { Props } from 'lib/next/getServerSideProps';
import { getServerSideProps as getServerSidePropsBase } from 'lib/next/getServerSideProps';
export const getServerSideProps: GetServerSideProps<Props> = async(args) => {
if (!appConfig.L2.isL2Network) {
return {
notFound: true,
};
}
return getServerSidePropsBase(args);
};
import type { PageParams } from './types';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
export default function getSeo(params: PageParams) {
const networkTitle = getNetworkTitle();
return {
title: params ? `${ params.hash } - ${ networkTitle }` : '',
description: params ?
`${ params.hash }, balances and analytics on the ${ networkTitle }` :
'',
};
}
import type { GetServerSideProps, GetServerSidePropsResult } from 'next';
export type Props = {
cookies: string;
referrer: string;
hash?: string;
}
export const getServerSideProps: GetServerSideProps = async({ req, query }): Promise<GetServerSidePropsResult<Props>> => {
return {
props: {
cookies: req.headers.cookie || '',
referrer: req.headers.referer || '',
hash: query.hash?.toString() || '',
},
};
};
export type PageParams = {
hash: string;
}
import type { RoutedQuery } from 'nextjs-routes';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
export default function getSeo(params?: RoutedQuery<'/tx/[hash]'>) {
const networkTitle = getNetworkTitle();
return {
title: params ? `Transaction ${ params.hash } - ${ networkTitle }` : '',
description: params ? `View transaction ${ params.hash } on ${ networkTitle }` : '',
};
}
import React from 'react';
import PageServer from 'lib/next/PageServer';
import AppError from 'ui/shared/AppError/AppError';
import Page from 'ui/shared/Page/Page';
const Custom404 = () => {
return (
<PageServer pathname="/404">
<Page>
<AppError statusCode={ 404 } mt="50px"/>
</Page>
</PageServer>
);
};
export default Custom404;
...@@ -19,37 +19,18 @@ ...@@ -19,37 +19,18 @@
import * as Sentry from '@sentry/nextjs'; import * as Sentry from '@sentry/nextjs';
import type { GetServerSideProps } from 'next'; import type { GetServerSideProps } from 'next';
import NextErrorComponent from 'next/error'; import NextErrorComponent from 'next/error';
import Head from 'next/head';
import React from 'react'; import React from 'react';
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 getNetworkTitle from 'lib/networks/getNetworkTitle';
import type { Props as ServerSidePropsCommon } from 'lib/next/getServerSideProps'; import type { Props as ServerSidePropsCommon } from 'lib/next/getServerSideProps';
import { getServerSideProps as getServerSidePropsCommon } from 'lib/next/getServerSideProps'; import { base as getServerSidePropsCommon } from 'lib/next/getServerSideProps';
import AppError from 'ui/shared/AppError/AppError';
import Page from 'ui/shared/Page/Page';
type Props = ServerSidePropsCommon & { type Props = ServerSidePropsCommon & {
statusCode: number; statusCode: number;
} }
const CustomErrorComponent = (props: Props) => { const CustomErrorComponent = (props: Props) => {
if (props.statusCode === 404) {
const title = getNetworkTitle();
return (
<>
<Head>
<title>{ title }</title>
</Head>
<Page>
<AppError statusCode={ 404 } mt="50px"/>
</Page>
</>
);
}
const colorModeCookie = cookies.getFromCookieString(props.cookies || '', cookies.NAMES.COLOR_MODE); const colorModeCookie = cookies.getFromCookieString(props.cookies || '', cookies.NAMES.COLOR_MODE);
return <NextErrorComponent statusCode={ props.statusCode } withDarkMode={ colorModeCookie === 'dark' }/>; return <NextErrorComponent statusCode={ props.statusCode } withDarkMode={ colorModeCookie === 'dark' }/>;
}; };
......
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; 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 ApiKeysPage: NextPage = () => {
const title = getNetworkTitle();
return ( return (
<> <PageServer pathname="/account/api-key">
<Head><title>{ title }</title></Head>
<Page> <Page>
<ApiKeys/> <ApiKeys/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default ApiKeysPage; export default ApiKeysPage;
export { getServerSideProps } from 'lib/next/account/getServerSideProps'; export { account as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; 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 CustomAbiPage: NextPage = () => {
const title = getNetworkTitle();
return ( return (
<> <PageServer pathname="/account/custom-abi">
<Head><title>{ title }</title></Head>
<Page> <Page>
<CustomAbi/> <CustomAbi/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default CustomAbiPage; export default CustomAbiPage;
export { getServerSideProps } from 'lib/next/account/getServerSideProps'; export { account as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; 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 PublicTagsPage: NextPage = () => {
const title = getNetworkTitle();
return ( return (
<> <PageServer pathname="/account/public-tags-request">
<Head><title>{ title }</title></Head>
<Page> <Page>
<PublicTags/> <PublicTags/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default PublicTagsPage; export default PublicTagsPage;
export { getServerSideProps } from 'lib/next/account/getServerSideProps'; export { account as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; 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 PrivateTagsPage: NextPage = () => {
const title = getNetworkTitle();
return ( return (
<> <PageServer pathname="/account/tag-address">
<Head><title>{ title }</title></Head>
<Page> <Page>
<PrivateTags/> <PrivateTags/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default PrivateTagsPage; export default PrivateTagsPage;
export { getServerSideProps } from 'lib/next/account/getServerSideProps'; export { account as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; 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 VerifiedAddressesPage: NextPage = () => {
const title = getNetworkTitle();
return ( return (
<> <PageServer pathname="/account/verified-addresses">
<Head><title>{ title }</title></Head>
<Page> <Page>
<VerifiedAddresses/> <VerifiedAddresses/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default VerifiedAddressesPage; export default VerifiedAddressesPage;
export { getServerSidePropsForVerifiedAddresses as getServerSideProps } from 'lib/next/account/getServerSideProps'; export { verifiedAddresses as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; 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 WatchListPage: NextPage = () => {
const title = getNetworkTitle();
return ( return (
<> <PageServer pathname="/account/watchlist">
<Head>
<title>{ title }</title>
</Head>
<Page> <Page>
<WatchList/> <WatchList/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default WatchListPage; export default WatchListPage;
export { getServerSideProps } from 'lib/next/account/getServerSideProps'; export { account as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; 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 AccountsPage: NextPage = () => {
const title = `Top Accounts - ${ getNetworkTitle() }`;
return ( return (
<> <PageServer pathname="/accounts">
<Head><title>{ title }</title></Head>
<Page> <Page>
<Accounts/> <Accounts/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default AccountsPage; export default AccountsPage;
export { getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import Head from 'next/head';
import type { RoutedQuery } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import getSeo from 'lib/next/address/getSeo'; import type { Props } from 'lib/next/getServerSideProps';
import PageServer from 'lib/next/PageServer';
import ContractVerification from 'ui/pages/ContractVerification'; import ContractVerification from 'ui/pages/ContractVerification';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
const ContractVerificationPage: NextPage<RoutedQuery<'/address/[hash]/contract-verification'>> = const ContractVerificationPage: NextPage<Props> = (props: Props) => {
({ hash }: RoutedQuery<'/address/[hash]/contract-verification'>) => {
const { title, description } = getSeo({ hash });
return ( return (
<> <PageServer pathname="/address/[hash]/contract-verification" query={ props }>
<Head>
<title>{ title }</title>
<meta name="description" content={ description }/>
</Head>
<Page> <Page>
<ContractVerification/> <ContractVerification/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default ContractVerificationPage; export default ContractVerificationPage;
export { getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import type { RoutedQuery } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import getSeo from 'lib/next/address/getSeo'; import type { Props } from 'lib/next/getServerSideProps';
import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; 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<RoutedQuery<'/address/[hash]'>> = ({ hash }: RoutedQuery<'/address/[hash]'>) => { const AddressPage: NextPage<Props> = (props: Props) => {
const { title, description } = getSeo({ hash });
return ( return (
<> <PageServer pathname="/address/[hash]" query={ props }>
<Head>
<title>{ title }</title>
<meta name="description" content={ description }/>
</Head>
<Page> <Page>
<Address/> <Address/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default AddressPage; export default AddressPage;
export { getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage, GetServerSideProps } from 'next'; import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import PageServer from 'lib/next/PageServer';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
import { getServerSideProps as getServerSidePropsBase } from 'lib/next/getServerSideProps';
import type { Props } from 'lib/next/getServerSideProps';
import SwaggerUI from 'ui/apiDocs/SwaggerUI'; import SwaggerUI from 'ui/apiDocs/SwaggerUI';
import Page from 'ui/shared/Page/Page'; 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 APIDocsPage: NextPage = () => {
const networkTitle = getNetworkTitle();
return ( return (
<PageServer pathname="/api-docs">
<Page> <Page>
<PageTitle title="API Documentation"/> <PageTitle title="API Documentation"/>
<Head><title>{ `API for the ${ networkTitle }` }</title></Head>
<SwaggerUI/> <SwaggerUI/>
</Page> </Page>
</PageServer>
); );
}; };
export default APIDocsPage; export default APIDocsPage;
export const getServerSideProps: GetServerSideProps<Props> = async(args) => { export { apiDocs as getServerSideProps } from 'lib/next/getServerSideProps';
if (!appConfig.apiDoc.specUrl) {
return {
notFound: true,
};
}
return getServerSidePropsBase(args);
};
import type { NextPage, GetServerSideProps } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config';
import type { Props } from 'lib/next/getServerSideProps'; import type { Props } from 'lib/next/getServerSideProps';
import { getServerSideProps as getServerSidePropsBase } from 'lib/next/getServerSideProps'; import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
const MarketplaceApp = dynamic(() => import('ui/pages/MarketplaceApp'), { ssr: false }); const MarketplaceApp = dynamic(() => import('ui/pages/MarketplaceApp'), { ssr: false });
const MarketplaceAppPage: NextPage = () => { const MarketplaceAppPage: NextPage<Props> = (props: Props) => {
return ( return (
<> <PageServer pathname="/apps/[id]" query={ props }>
<Head><title>Blockscout | Marketplace</title></Head>
<Page wrapChildren={ false }> <Page wrapChildren={ false }>
<MarketplaceApp/> <MarketplaceApp/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default MarketplaceAppPage; export default MarketplaceAppPage;
export const getServerSideProps: GetServerSideProps<Props> = async(args) => { export { marketplace as getServerSideProps } from 'lib/next/getServerSideProps';
if (!appConfig.marketplace.configUrl || !appConfig.network.rpcUrl) {
return {
notFound: true,
};
}
return getServerSidePropsBase(args);
};
import type { NextPage, GetServerSideProps } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import PageServer from 'lib/next/PageServer';
import type { Props } from 'lib/next/getServerSideProps';
import { getServerSideProps as getServerSidePropsBase } from 'lib/next/getServerSideProps';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
...@@ -13,24 +10,15 @@ const Marketplace = dynamic(() => import('ui/pages/Marketplace'), { ssr: false } ...@@ -13,24 +10,15 @@ const Marketplace = dynamic(() => import('ui/pages/Marketplace'), { ssr: false }
const MarketplacePage: NextPage = () => { const MarketplacePage: NextPage = () => {
return ( return (
<> <PageServer pathname="/apps">
<Head><title>Blockscout | Marketplace</title></Head>
<Page> <Page>
<PageTitle title="Marketplace"/> <PageTitle title="Marketplace"/>
<Marketplace/> <Marketplace/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default MarketplacePage; export default MarketplacePage;
export const getServerSideProps: GetServerSideProps<Props> = async(args) => { export { marketplace as getServerSideProps } from 'lib/next/getServerSideProps';
if (!appConfig.marketplace.configUrl || !appConfig.network.rpcUrl) {
return {
notFound: true,
};
}
return getServerSidePropsBase(args);
};
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer';
import MyProfile from 'ui/pages/MyProfile'; import MyProfile from 'ui/pages/MyProfile';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
const MyProfilePage: NextPage = () => { const MyProfilePage: NextPage = () => {
return ( return (
<> <PageServer pathname="/auth/profile">
<Head><title>My profile</title></Head>
<Page> <Page>
<MyProfile/> <MyProfile/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default MyProfilePage; export default MyProfilePage;
export { getServerSideProps } from 'lib/next/account/getServerSideProps'; export { account as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import PageServer from 'lib/next/PageServer';
import UnverifiedEmail from 'ui/pages/UnverifiedEmail'; import UnverifiedEmail from 'ui/pages/UnverifiedEmail';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
const UnverifiedEmailPage: NextPage = () => { const UnverifiedEmailPage: NextPage = () => {
const title = getNetworkTitle();
return ( return (
<> <PageServer pathname="/auth/unverified-email">
<Head><title>{ title }</title></Head>
<Page> <Page>
<UnverifiedEmail/> <UnverifiedEmail/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default UnverifiedEmailPage; export default UnverifiedEmailPage;
export { getServerSideProps } from 'lib/next/account/getServerSideProps'; export { account as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import type { RoutedQuery } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import getSeo from 'lib/next/block/getSeo'; import type { Props } from 'lib/next/getServerSideProps';
import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; 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<RoutedQuery<'/block/[height_or_hash]'>> = (params: RoutedQuery<'/block/[height_or_hash]'>) => { const BlockPage: NextPage<Props> = (props: Props) => {
const { title, description } = getSeo({ height_or_hash: params.height_or_hash });
return ( return (
<> <PageServer pathname="/block/[height_or_hash]" query={ props }>
<Head>
<title>{ title }</title>
<meta name="description" content={ description }/>
</Head>
<Page> <Page>
<Block/> <Block/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default BlockPage; export default BlockPage;
export { getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import getSeo from 'lib/next/blocks/getSeo'; import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; 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 BlockPage: NextPage = () => {
const { title } = getSeo();
return ( return (
<> <PageServer pathname="/blocks">
<Head>
<title>{ title }</title>
</Head>
<Page> <Page>
<Blocks/> <Blocks/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default BlockPage; export default BlockPage;
export { getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'lib/next/getServerSideProps';
import type { GetServerSideProps, NextPage } from 'next'; import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import PageServer from 'lib/next/PageServer';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
import type { Props } from 'lib/next/getServerSideProps';
import { getServerSideProps as getServerSidePropsBase } from 'lib/next/getServerSideProps';
import CsvExport from 'ui/pages/CsvExport'; import CsvExport from 'ui/pages/CsvExport';
const CsvExportPage: NextPage = () => { const CsvExportPage: NextPage = () => {
const title = getNetworkTitle();
return ( return (
<> <PageServer pathname="/csv-export">
<Head>
<title>{ title }</title>
</Head>
<CsvExport/> <CsvExport/>
</> </PageServer>
); );
}; };
export default CsvExportPage; export default CsvExportPage;
export const getServerSideProps: GetServerSideProps<Props> = async(args) => { export { csvExport as getServerSideProps } from 'lib/next/getServerSideProps';
if (!appConfig.reCaptcha.siteKey) {
return {
notFound: true,
};
}
return getServerSidePropsBase(args);
};
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
const GraphQL = dynamic(() => import('ui/graphQL/GraphQL'), {
loading: () => <ContentLoader/>,
ssr: false,
});
import Head from 'next/head';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer';
import ContentLoader from 'ui/shared/ContentLoader'; import ContentLoader from 'ui/shared/ContentLoader';
import Page from 'ui/shared/Page/Page'; 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'), {
loading: () => <ContentLoader/>,
ssr: false,
});
const GraphiqlPage: NextPage = () => { const GraphiqlPage: NextPage = () => {
return ( return (
<PageServer pathname="/graphiql">
<Page> <Page>
<Head><title>Graph Page</title></Head>
<PageTitle title="GraphQL playground"/> <PageTitle title="GraphQL playground"/>
<GraphQL/> <GraphQL/>
</Page> </Page>
</PageServer>
); );
}; };
export default GraphiqlPage; export default GraphiqlPage;
export { getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer';
import Home from 'ui/pages/Home'; import Home from 'ui/pages/Home';
const HomePage: NextPage = () => { const HomePage: NextPage = () => {
return ( return (
<> <PageServer pathname="/">
<Head><title>Home Page</title></Head>
<Home/> <Home/>
</> </PageServer>
); );
}; };
export default HomePage; export default HomePage;
export { getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; 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 DepositsPage: NextPage = () => {
const title = getNetworkTitle();
return ( return (
<> <PageServer pathname="/l2-deposits">
<Head>
<title>{ title }</title>
</Head>
<Page> <Page>
<L2Deposits/> <L2Deposits/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default DepositsPage; export default DepositsPage;
export { getServerSideProps } from 'lib/next/getServerSidePropsL2'; export { L2 as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; 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 OutputRootsPage: NextPage = () => {
const title = getNetworkTitle();
return ( return (
<> <PageServer pathname="/l2-output-roots">
<Head>
<title>{ title }</title>
</Head>
<Page> <Page>
<L2OutputRoots/> <L2OutputRoots/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default OutputRootsPage; export default OutputRootsPage;
export { getServerSideProps } from 'lib/next/getServerSidePropsL2'; export { L2 as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; 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 TxnBatchesPage: NextPage = () => {
const title = getNetworkTitle();
return ( return (
<> <PageServer pathname="/l2-txn-batches">
<Head>
<title>{ title }</title>
</Head>
<Page> <Page>
<L2TxnBatches/> <L2TxnBatches/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default TxnBatchesPage; export default TxnBatchesPage;
export { getServerSideProps } from 'lib/next/getServerSidePropsL2'; export { L2 as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; 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 WithdrawalsPage: NextPage = () => {
const title = getNetworkTitle();
return ( return (
<> <PageServer pathname="/l2-withdrawals">
<Head>
<title>{ title }</title>
</Head>
<Page> <Page>
<L2Withdrawals/> <L2Withdrawals/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default WithdrawalsPage; export default WithdrawalsPage;
export { getServerSideProps } from 'lib/next/getServerSidePropsL2'; export { L2 as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import PageServer from 'lib/next/PageServer';
import Login from 'ui/pages/Login'; import Login from 'ui/pages/Login';
const LoginPage: NextPage = () => { const LoginPage: NextPage = () => {
return ( return (
<> <PageServer pathname="/login">
<Head><title>Login Page</title></Head>
<Login/> <Login/>
</> </PageServer>
); );
}; };
export default LoginPage; export default LoginPage;
export { getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import type { Props } from 'lib/next/getServerSideProps';
import PageServer from 'lib/next/PageServer';
const SearchResults = dynamic(() => import('ui/pages/SearchResults'), { ssr: false }); const SearchResults = dynamic(() => import('ui/pages/SearchResults'), { ssr: false });
const SearchResultsPage: NextPage = () => { const SearchResultsPage: NextPage<Props> = (props: Props) => {
const title = getNetworkTitle();
return ( return (
<> <PageServer pathname="/search-results" query={ props }>
<Head>
<title>{ title }</title>
</Head>
<SearchResults/> <SearchResults/>
</> </PageServer>
); );
}; };
export default SearchResultsPage; export default SearchResultsPage;
export { getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage, GetServerSideProps } from 'next'; import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import PageServer from 'lib/next/PageServer';
import type { Props } from 'lib/next/getServerSideProps';
import { getServerSideProps as getServerSidePropsBase } from 'lib/next/getServerSideProps';
import Stats from '../ui/pages/Stats'; import Stats from '../ui/pages/Stats';
const StatsPage: NextPage = () => { const StatsPage: NextPage = () => {
return ( return (
<> <PageServer pathname="/stats">
<Head><title>{ appConfig.network.name } stats</title></Head>
<Stats/> <Stats/>
</> </PageServer>
); );
}; };
export default StatsPage; export default StatsPage;
export const getServerSideProps: GetServerSideProps<Props> = async(args) => { export { stats as getServerSideProps } from 'lib/next/getServerSideProps';
if (!appConfig.statsApi.endpoint) {
return {
notFound: true,
};
}
return getServerSidePropsBase(args);
};
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import type { PageParams } from 'lib/next/token/types'; import type { Props } from 'lib/next/getServerSideProps';
import PageServer from 'lib/next/PageServer';
import getSeo from 'lib/next/token/getSeo';
import Page from 'ui/shared/Page/Page'; 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<PageParams> = ({ hash }: PageParams) => { const TokenPage: NextPage<Props> = (props: Props) => {
const { title, description } = getSeo({ hash });
return ( return (
<> <PageServer pathname="/token/[hash]" query={ props }>
<Head>
<title>{ title }</title>
<meta name="description" content={ description }/>
</Head>
<Page> <Page>
<Token/> <Token/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default TokenPage; export default TokenPage;
export { getServerSideProps } from 'lib/next/token/getServerSideProps'; export { base as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import type { PageParams } from 'lib/next/token/types'; import type { Props } from 'lib/next/getServerSideProps';
import PageServer from 'lib/next/PageServer';
import getNetworkTitle from 'lib/networks/getNetworkTitle';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
const TokenInstance = dynamic(() => import('ui/pages/TokenInstance'), { ssr: false });
const TokenInstancePage: NextPage<PageParams> = () => { const TokenInstance = dynamic(() => import('ui/pages/TokenInstance'), { ssr: false });
const title = getNetworkTitle();
const TokenInstancePage: NextPage<Props> = (props: Props) => {
return ( return (
<> <PageServer pathname="/token/[hash]/instance/[id]" query={ props }>
<Head>
<title>{ title }</title>
</Head>
<Page> <Page>
<TokenInstance/> <TokenInstance/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default TokenInstancePage; export default TokenInstancePage;
export { getServerSideProps } from 'lib/next/token/getServerSideProps'; export { base as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; 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 TokensPage: NextPage = () => {
const title = getNetworkTitle();
return ( return (
<> <PageServer pathname="/tokens">
<Head>
<title>{ title }</title>
</Head>
<Page> <Page>
<Tokens/> <Tokens/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default TokensPage; export default TokensPage;
export { getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import type { RoutedQuery } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import getSeo from 'lib/next/tx/getSeo'; import type { Props } from 'lib/next/getServerSideProps';
import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; 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<RoutedQuery<'/tx/[hash]'>> = ({ hash }: RoutedQuery<'/tx/[hash]'>) => { const TransactionPage: NextPage<Props> = (props: Props) => {
const { title, description } = getSeo({ hash });
return ( return (
<> <PageServer pathname="/tx/[hash]" query={ props }>
<Head>
<title>{ title }</title>
<meta name="description" content={ description }/>
</Head>
<Page> <Page>
<Transaction/> <Transaction/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default TransactionPage; export default TransactionPage;
export { getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; 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 TxsPage: NextPage = () => {
const title = getNetworkTitle();
return ( return (
<> <PageServer pathname="/txs">
<Head><title>{ title }</title></Head>
<Page> <Page>
<Transactions/> <Transactions/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default TxsPage; export default TxsPage;
export { getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; 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 VerifiedContractsPage: NextPage = () => {
const title = getNetworkTitle();
return ( return (
<> <PageServer pathname="/verified-contracts">
<Head>
<title>{ title }</title>
</Head>
<Page> <Page>
<VerifiedContracts/> <VerifiedContracts/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default VerifiedContractsPage; export default VerifiedContractsPage;
export { getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import PageServer from 'lib/next/PageServer';
import Sol2Uml from 'ui/pages/Sol2Uml'; import Sol2Uml from 'ui/pages/Sol2Uml';
const Sol2UmlPage: NextPage = () => { const Sol2UmlPage: NextPage = () => {
const title = getNetworkTitle();
return ( return (
<> <PageServer pathname="/visualize/sol2uml">
<Head>
<title>{ title }</title>
</Head>
<Sol2Uml/> <Sol2Uml/>
</> </PageServer>
); );
}; };
export default Sol2UmlPage; export default Sol2UmlPage;
export { getServerSideProps } from 'lib/next/getServerSideProps'; export { base as getServerSideProps } from 'lib/next/getServerSideProps';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react'; import React from 'react';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import PageServer from 'lib/next/PageServer';
import Page from 'ui/shared/Page/Page'; 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 WithdrawalsPage: NextPage = () => {
const title = getNetworkTitle();
return ( return (
<> <PageServer pathname="/withdrawals">
<Head>
<title>{ title }</title>
</Head>
<Page> <Page>
<Withdrawals/> <Withdrawals/>
</Page> </Page>
</> </PageServer>
); );
}; };
export default WithdrawalsPage; export default WithdrawalsPage;
export { getServerSideProps } from 'lib/next/getServerSidePropsBeacon'; export { beaconChain as getServerSideProps } from 'lib/next/getServerSideProps';
...@@ -23,6 +23,10 @@ const defaultAppContext = { ...@@ -23,6 +23,10 @@ const defaultAppContext = {
pageProps: { pageProps: {
cookies: '', cookies: '',
referrer: '', referrer: '',
id: '',
height_or_hash: '',
hash: '',
q: '',
}, },
}; };
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
// prettier-ignore // prettier-ignore
declare module "nextjs-routes" { declare module "nextjs-routes" {
export type Route = export type Route =
| StaticRoute<"/404">
| StaticRoute<"/account/api-key"> | StaticRoute<"/account/api-key">
| StaticRoute<"/account/custom-abi"> | StaticRoute<"/account/custom-abi">
| StaticRoute<"/account/public-tags-request"> | StaticRoute<"/account/public-tags-request">
......
...@@ -10,6 +10,7 @@ import appConfig from 'configs/app/config'; ...@@ -10,6 +10,7 @@ import appConfig from 'configs/app/config';
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';
import useApiFetch from 'lib/hooks/useFetch'; import useApiFetch from 'lib/hooks/useFetch';
import * as metadata from 'lib/metadata';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import ContentLoader from 'ui/shared/ContentLoader'; import ContentLoader from 'ui/shared/ContentLoader';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
...@@ -70,6 +71,15 @@ const MarketplaceApp = () => { ...@@ -70,6 +71,15 @@ const MarketplaceApp = () => {
} }
}, [ isFrameLoading, data, colorMode, ref ]); }, [ isFrameLoading, data, colorMode, ref ]);
useEffect(() => {
if (data) {
metadata.update(
{ pathname: '/apps/[id]', query: { id: data.id } },
{ app_name: data.title },
);
}
}, [ data ]);
if (isError) { if (isError) {
throw new Error('Unable to load app', { cause: error }); throw new Error('Unable to load app', { cause: error });
} }
......
...@@ -15,6 +15,7 @@ import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; ...@@ -15,6 +15,7 @@ import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/contexts/app'; import { useAppContext } from 'lib/contexts/app';
import useContractTabs from 'lib/hooks/useContractTabs'; import useContractTabs from 'lib/hooks/useContractTabs';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import * as metadata from 'lib/metadata';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
...@@ -108,16 +109,7 @@ const TokenPageContent = () => { ...@@ -108,16 +109,7 @@ const TokenPageContent = () => {
useEffect(() => { useEffect(() => {
if (tokenQuery.data && !tokenQuery.isPlaceholderData) { if (tokenQuery.data && !tokenQuery.isPlaceholderData) {
const tokenSymbol = tokenQuery.data.symbol ? ` (${ tokenQuery.data.symbol })` : ''; metadata.update({ pathname: '/token/[hash]', query: { hash: tokenQuery.data.address } }, { symbol: tokenQuery.data.symbol ?? '' });
const tokenName = `${ tokenQuery.data.name || 'Unnamed' }${ tokenSymbol }`;
const title = document.getElementsByTagName('title')[0];
if (title) {
title.textContent = title.textContent?.replace(tokenQuery.data.address, tokenName) || title.textContent;
}
const description = document.getElementsByName('description')[0] as HTMLMetaElement;
if (description) {
description.content = description.content.replace(tokenQuery.data.address, tokenName) || description.content;
}
} }
}, [ tokenQuery.data, tokenQuery.isPlaceholderData ]); }, [ tokenQuery.data, tokenQuery.isPlaceholderData ]);
......
...@@ -9,6 +9,7 @@ import nftIcon from 'icons/nft_shield.svg'; ...@@ -9,6 +9,7 @@ import nftIcon from 'icons/nft_shield.svg';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/contexts/app'; import { useAppContext } from 'lib/contexts/app';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import * as metadata from 'lib/metadata';
import * as regexp from 'lib/regexp'; import * as regexp from 'lib/regexp';
import { TOKEN_INSTANCE } from 'stubs/token'; import { TOKEN_INSTANCE } from 'stubs/token';
import * as tokenStubs from 'stubs/token'; import * as tokenStubs from 'stubs/token';
...@@ -74,6 +75,15 @@ const TokenInstanceContent = () => { ...@@ -74,6 +75,15 @@ const TokenInstanceContent = () => {
}, },
}); });
React.useEffect(() => {
if (tokenInstanceQuery.data && !tokenInstanceQuery.isPlaceholderData) {
metadata.update(
{ pathname: '/token/[hash]/instance/[id]', query: { hash: tokenInstanceQuery.data.token.address, id: tokenInstanceQuery.data.id } },
{ symbol: tokenInstanceQuery.data.token.symbol ?? '' },
);
}
}, [ tokenInstanceQuery.data, tokenInstanceQuery.isPlaceholderData ]);
const backLink = React.useMemo(() => { const backLink = React.useMemo(() => {
const hasGoBackLink = appProps.referrer && appProps.referrer.includes(`/token/${ hash }`) && !appProps.referrer.includes('instance'); const hasGoBackLink = appProps.referrer && appProps.referrer.includes(`/token/${ hash }`) && !appProps.referrer.includes('instance');
......
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