Commit 2a5e42a7 authored by Igor Stuev's avatar Igor Stuev Committed by GitHub

og improvements (#1175)

* og improvements

* update review configs

* review fix

* review fix 2
parent 944773f0
...@@ -2,6 +2,7 @@ import api from './api'; ...@@ -2,6 +2,7 @@ import api from './api';
import app from './app'; import app from './app';
import chain from './chain'; import chain from './chain';
import * as features from './features'; import * as features from './features';
import meta from './meta';
import services from './services'; import services from './services';
import UI from './ui'; import UI from './ui';
...@@ -12,6 +13,7 @@ const config = Object.freeze({ ...@@ -12,6 +13,7 @@ const config = Object.freeze({
UI, UI,
features, features,
services, services,
meta,
}); });
export default config; export default config;
import app from './app';
import { getEnvValue, getExternalAssetFilePath } from './utils';
const defaultImageUrl = app.baseUrl + '/static/og_placeholder.png';
const meta = Object.freeze({
promoteBlockscoutInTitle: getEnvValue(process.env.NEXT_PUBLIC_PROMOTE_BLOCKSCOUT_IN_TITLE) || 'true',
og: {
description: getEnvValue(process.env.NEXT_PUBLIC_OG_DESCRIPTION) || '',
imageUrl: getExternalAssetFilePath('NEXT_PUBLIC_OG_IMAGE_URL', process.env.NEXT_PUBLIC_OG_IMAGE_URL) || defaultImageUrl,
},
});
export default meta;
...@@ -41,3 +41,6 @@ NEXT_PUBLIC_STATS_API_HOST=https://stats-eth-main.k8s.blockscout.com ...@@ -41,3 +41,6 @@ NEXT_PUBLIC_STATS_API_HOST=https://stats-eth-main.k8s.blockscout.com
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
#meta
NEXT_PUBLIC_OG_IMAGE_URL=https://github.com/blockscout/frontend-configs/blob/main/configs/og-images/eth.jpg?raw=true
...@@ -46,3 +46,6 @@ NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com ...@@ -46,3 +46,6 @@ NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
NEXT_PUBLIC_WEB3_WALLETS=['token_pocket','metamask'] NEXT_PUBLIC_WEB3_WALLETS=['token_pocket','metamask']
#meta
NEXT_PUBLIC_OG_IMAGE_URL=https://github.com/blockscout/frontend-configs/blob/main/configs/og-images/eth-goerli.png?raw=true
...@@ -40,4 +40,7 @@ NEXT_PUBLIC_APP_ENV=development ...@@ -40,4 +40,7 @@ NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_HAS_BEACON_CHAIN=false NEXT_PUBLIC_HAS_BEACON_CHAIN=false
# NEXT_PUBLIC_STATS_API_HOST=https://stats-rsk-testnet.k8s.blockscout.com # NEXT_PUBLIC_STATS_API_HOST=https://stats-rsk-testnet.k8s.blockscout.com
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_WEB3_WALLETS=['token_pocket','metamask'] NEXT_PUBLIC_WEB3_WALLETS=['token_pocket','metamask']
\ No newline at end of file
#meta
NEXT_PUBLIC_OG_IMAGE_URL=https://github.com/blockscout/frontend-configs/blob/main/configs/og-images/polygon-mainnet.png?raw=true
...@@ -41,3 +41,6 @@ NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x97fa753626b8d44011d0b9f9a947c735f20b6e895efde ...@@ -41,3 +41,6 @@ NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x97fa753626b8d44011d0b9f9a947c735f20b6e895efde
NEXT_PUBLIC_HAS_BEACON_CHAIN=false NEXT_PUBLIC_HAS_BEACON_CHAIN=false
NEXT_PUBLIC_STATS_API_HOST=https://stats-rsk-testnet.k8s.blockscout.com NEXT_PUBLIC_STATS_API_HOST=https://stats-rsk-testnet.k8s.blockscout.com
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
#meta
NEXT_PUBLIC_OG_IMAGE_URL=https://github.com/blockscout/frontend-configs/blob/main/configs/og-images/rootstock-testnet.png?raw=true
...@@ -21,6 +21,7 @@ ASSETS_ENVS=( ...@@ -21,6 +21,7 @@ ASSETS_ENVS=(
"NEXT_PUBLIC_NETWORK_LOGO_DARK" "NEXT_PUBLIC_NETWORK_LOGO_DARK"
"NEXT_PUBLIC_NETWORK_ICON" "NEXT_PUBLIC_NETWORK_ICON"
"NEXT_PUBLIC_NETWORK_ICON_DARK" "NEXT_PUBLIC_NETWORK_ICON_DARK"
"NEXT_PUBLIC_OG_IMAGE_URL"
) )
# Create the assets directory if it doesn't exist # Create the assets directory if it doesn't exist
......
...@@ -325,6 +325,9 @@ const schema = yup ...@@ -325,6 +325,9 @@ const schema = yup
}), }),
NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET: yup.boolean(), NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET: yup.boolean(),
NEXT_PUBLIC_AD_TEXT_PROVIDER: yup.string<AdTextProviders>().oneOf(SUPPORTED_AD_TEXT_PROVIDERS), NEXT_PUBLIC_AD_TEXT_PROVIDER: yup.string<AdTextProviders>().oneOf(SUPPORTED_AD_TEXT_PROVIDERS),
NEXT_PUBLIC_PROMOTE_BLOCKSCOUT_IN_TITLE: yup.boolean(),
NEXT_PUBLIC_OG_DESCRIPTION: yup.string(),
NEXT_PUBLIC_OG_IMAGE_URL: yup.string().url(),
// 6. External services envs // 6. External services envs
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: yup.string(), NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: yup.string(),
......
...@@ -144,3 +144,5 @@ frontend: ...@@ -144,3 +144,5 @@ frontend:
_default: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID _default: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID
NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY: NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY:
_default: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY _default: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY
NEXT_PUBLIC_OG_IMAGE_URL:
_default: https://github.com/blockscout/frontend-configs/blob/main/configs/og-images/base-goerli.png?raw=true
...@@ -13,6 +13,7 @@ The app instance could be customized by passing following variables to NodeJS en ...@@ -13,6 +13,7 @@ The app instance could be customized by passing following variables to NodeJS en
- [Sidebar](ENVS.md#sidebar) - [Sidebar](ENVS.md#sidebar)
- [Footer](ENVS.md#footer) - [Footer](ENVS.md#footer)
- [Favicon](ENVS.md#favicon) - [Favicon](ENVS.md#favicon)
- [Meta](ENVS.md#meta)
- [Views](ENVS.md#views) - [Views](ENVS.md#views)
- [Block](ENVS.md#block-views) - [Block](ENVS.md#block-views)
- [Misc](ENVS.md#misc) - [Misc](ENVS.md#misc)
...@@ -145,6 +146,18 @@ By default, the app has generic favicon. You can override this behavior by provi ...@@ -145,6 +146,18 @@ By default, the app has generic favicon. You can override this behavior by provi
&nbsp; &nbsp;
### Meta
Settings for meta tags and OG tags
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_PROMOTE_BLOCKSCOUT_IN_TITLE | `boolean` | Set to `true` to promote Blockscout in meta and OG titles | - | `true` | `true` |
| NEXT_PUBLIC_OG_DESCRIPTION | `string` | Custom OG 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.` |
| NEXT_PUBLIC_OG_IMAGE_URL | `string` | OG image url. Minimum image size is 200 x 20 pixels (recommended: 1200 x 600); maximum supported file size is 8 MB; 2:1 aspect ratio; supported formats: image/jpeg, image/gif, image/png | - | `static/og_placeholder.png` | `https://placekitten.com/1200/600` |
&nbsp;
### Views ### Views
#### Block views #### Block views
......
...@@ -3,6 +3,11 @@ ...@@ -3,6 +3,11 @@
exports[`generates correct metadata for: dynamic route 1`] = ` exports[`generates correct metadata for: dynamic route 1`] = `
{ {
"description": "View transaction 0x12345 on Blockscout (Blockscout) Explorer", "description": "View transaction 0x12345 on Blockscout (Blockscout) Explorer",
"opengraph": {
"description": "",
"imageUrl": "",
"title": "Blockscout transaction 0x12345 | Blockscout",
},
"title": "Blockscout transaction 0x12345 | Blockscout", "title": "Blockscout transaction 0x12345 | Blockscout",
} }
`; `;
...@@ -10,6 +15,11 @@ exports[`generates correct metadata for: dynamic route 1`] = ` ...@@ -10,6 +15,11 @@ exports[`generates correct metadata for: dynamic route 1`] = `
exports[`generates correct metadata for: dynamic route with API data 1`] = ` exports[`generates correct metadata for: dynamic route with API data 1`] = `
{ {
"description": "0x12345, balances and analytics on the Blockscout (Blockscout) Explorer", "description": "0x12345, balances and analytics on the Blockscout (Blockscout) Explorer",
"opengraph": {
"description": "",
"imageUrl": "",
"title": "Blockscout USDT token details | Blockscout",
},
"title": "Blockscout USDT token details | Blockscout", "title": "Blockscout USDT token details | Blockscout",
} }
`; `;
...@@ -17,6 +27,11 @@ exports[`generates correct metadata for: dynamic route with API data 1`] = ` ...@@ -17,6 +27,11 @@ exports[`generates correct metadata for: dynamic route with API data 1`] = `
exports[`generates correct metadata for: static route 1`] = ` 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.", "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.",
"opengraph": {
"description": "",
"imageUrl": "http://localhost:3000/static/og_placeholder.png",
"title": "Blockscout blocks | Blockscout",
},
"title": "Blockscout blocks | Blockscout", "title": "Blockscout blocks | Blockscout",
} }
`; `;
...@@ -6,6 +6,7 @@ import config from 'configs/app'; ...@@ -6,6 +6,7 @@ import config from 'configs/app';
import getNetworkTitle from 'lib/networks/getNetworkTitle'; import getNetworkTitle from 'lib/networks/getNetworkTitle';
import compileValue from './compileValue'; import compileValue from './compileValue';
import getPageOgType from './getPageOgType';
import * as templates from './templates'; import * as templates from './templates';
export default function generate<R extends Route>(route: R, apiData?: ApiData<R>): Metadata { export default function generate<R extends Route>(route: R, apiData?: ApiData<R>): Metadata {
...@@ -16,11 +17,19 @@ export default function generate<R extends Route>(route: R, apiData?: ApiData<R> ...@@ -16,11 +17,19 @@ export default function generate<R extends Route>(route: R, apiData?: ApiData<R>
network_title: getNetworkTitle(), network_title: getNetworkTitle(),
}; };
const title = compileValue(templates.title.make(route.pathname), params); const compiledTitle = compileValue(templates.title.make(route.pathname), params);
const title = compiledTitle ? compiledTitle + (config.meta.promoteBlockscoutInTitle ? ' | Blockscout' : '') : '';
const description = compileValue(templates.description.make(route.pathname), params); const description = compileValue(templates.description.make(route.pathname), params);
const pageOgType = getPageOgType(route.pathname);
return { return {
title, title: title,
description, description,
opengraph: {
title: title,
description: pageOgType !== 'Regular page' ? config.meta.og.description : '',
imageUrl: pageOgType !== 'Regular page' ? config.meta.og.imageUrl : '',
},
}; };
} }
import type { Route } from 'nextjs-routes';
type OGPageType = 'Homepage' | 'Root page' | 'Regular page';
const OG_TYPE_DICT: Record<Route['pathname'], OGPageType> = {
'/': 'Homepage',
'/txs': 'Root page',
'/tx/[hash]': 'Regular page',
'/blocks': 'Root page',
'/block/[height_or_hash]': 'Regular page',
'/accounts': 'Root page',
'/address/[hash]': 'Regular page',
'/verified-contracts': 'Root page',
'/address/[hash]/contract-verification': 'Regular page',
'/tokens': 'Root page',
'/token/[hash]': 'Regular page',
'/token/[hash]/instance/[id]': 'Regular page',
'/apps': 'Root page',
'/apps/[id]': 'Regular page',
'/stats': 'Root page',
'/api-docs': 'Regular page',
'/graphiql': 'Regular page',
'/search-results': 'Regular page',
'/auth/profile': 'Root page',
'/account/watchlist': 'Regular page',
'/account/api-key': 'Regular page',
'/account/custom-abi': 'Regular page',
'/account/public-tags-request': 'Regular page',
'/account/tag-address': 'Regular page',
'/account/verified-addresses': 'Root page',
'/withdrawals': 'Root page',
'/visualize/sol2uml': 'Regular page',
'/csv-export': 'Regular page',
'/l2-deposits': 'Root page',
'/l2-output-roots': 'Root page',
'/l2-txn-batches': 'Root page',
'/l2-withdrawals': 'Root page',
'/404': 'Regular page',
// service routes, added only to make typescript happy
'/login': 'Regular page',
'/api/media-type': 'Regular page',
'/api/proxy': 'Regular page',
'/api/csrf': 'Regular page',
'/api/healthz': 'Regular page',
'/auth/auth0': 'Regular page',
'/auth/unverified-email': 'Regular page',
};
export default function getPageOgType(pathname: Route['pathname']) {
return OG_TYPE_DICT[pathname];
}
...@@ -48,5 +48,5 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = { ...@@ -48,5 +48,5 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
export function make(pathname: Route['pathname']) { export function make(pathname: Route['pathname']) {
const template = TEMPLATE_MAP[pathname]; const template = TEMPLATE_MAP[pathname];
return `%network_name% ${ template } | Blockscout`; return `%network_name% ${ template }`;
} }
...@@ -10,4 +10,9 @@ never; ...@@ -10,4 +10,9 @@ never;
export interface Metadata { export interface Metadata {
title: string; title: string;
description: string; description: string;
opengraph: {
title: string;
description?: string;
imageUrl?: string;
};
} }
...@@ -14,7 +14,7 @@ type Props = Route & { ...@@ -14,7 +14,7 @@ type Props = Route & {
} }
const PageNextJs = (props: Props) => { const PageNextJs = (props: Props) => {
const { title, description } = metadata.generate(props); const { title, description, opengraph } = metadata.generate(props);
useGetCsrfToken(); useGetCsrfToken();
useAdblockDetect(); useAdblockDetect();
...@@ -28,6 +28,12 @@ const PageNextJs = (props: Props) => { ...@@ -28,6 +28,12 @@ const PageNextJs = (props: Props) => {
<Head> <Head>
<title>{ title }</title> <title>{ title }</title>
<meta name="description" content={ description }/> <meta name="description" content={ description }/>
{ /* OG TAGS */ }
<meta property="og:title" content={ opengraph.title }/>
{ opengraph.description && <meta property="og:description" content={ opengraph.description }/> }
<meta property="og:image" content={ opengraph.imageUrl }/>
<meta property="og:type" content="website"/>
</Head> </Head>
{ props.children } { props.children }
</> </>
......
...@@ -5,7 +5,6 @@ import React from 'react'; ...@@ -5,7 +5,6 @@ import React from 'react';
import * as serverTiming from 'nextjs/utils/serverTiming'; import * as serverTiming from 'nextjs/utils/serverTiming';
import config from 'configs/app';
import theme from 'theme'; import theme from 'theme';
class MyDocument extends Document { class MyDocument extends Document {
...@@ -46,19 +45,6 @@ class MyDocument extends Document { ...@@ -46,19 +45,6 @@ class MyDocument extends Document {
<link rel="icon" sizes="16x16" type="image/png"href="/favicon/favicon-16x16.png"/> <link rel="icon" sizes="16x16" type="image/png"href="/favicon/favicon-16x16.png"/>
<link rel="apple-touch-icon" href="/favicon/apple-touch-icon-180x180.png"/> <link rel="apple-touch-icon" href="/favicon/apple-touch-icon-180x180.png"/>
<link rel="mask-icon" href="/favicon/safari-pinned-tab.svg"/> <link rel="mask-icon" href="/favicon/safari-pinned-tab.svg"/>
{ /* OG TAGS */ }
<meta property="og:title" content="Blockscout: A block explorer designed for a decentralized world."/>
<meta
property="og:description"
// eslint-disable-next-line max-len
content="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."
/>
<meta property="og:image" content={ config.app.baseUrl + '/static/og.png' }/>
<meta property="og:site_name" content="Blockscout"/>
<meta property="og:type" content="website"/>
<meta name="twitter:card" content="summary_large_image"/>
<meta property="twitter:image" content={ config.app.baseUrl + '/static/og_twitter.png' }/>
</Head> </Head>
<body> <body>
<ColorModeScript initialColorMode={ theme.config.initialColorMode }/> <ColorModeScript initialColorMode={ theme.config.initialColorMode }/>
......
This diff is collapsed.
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