[website] Add head, links and meta tags (#411)

* add assets

* add head, links and meta tags

* add url origin

* rebase

* update head

* comment out placeholder og images

* add scrollRestoration

* fix related posts

* rm browserconfig.xml

* remove icons

* u

* u

* lint

* Update error-page.tsx

* rm twitter meta
This commit is contained in:
Felicio Mununga 2023-06-27 10:40:09 +01:00 committed by GitHub
parent ceb1f60605
commit 9d2b3e9cea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 181 additions and 65 deletions

View File

@ -30,6 +30,7 @@ let config = {
experimental: { experimental: {
legacyBrowsers: false, legacyBrowsers: false,
// esmExternals: 'loose', // esmExternals: 'loose',
scrollRestoration: true,
}, },
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -1,53 +0,0 @@
import Head from 'next/head'
type Props = {
index?: boolean
children?: React.ReactElement
imageUrl?: string
}
function _Head(props: Props) {
const { index = true, children, imageUrl } = props
return (
<Head>
<title>Status</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
{/* todo: app stores banners/redirects */}
{/* todo: eval following meta tags */}
<meta property="og:site_name" content="Join Status" />
<meta property="og:type" content="website" />
<meta
property="og:description"
content="Status — A secure messaging app, crypto wallet, and Web3 browser"
/>
<meta property="og:title" content="Join [@|#]<name> in Status" />
<meta property="og:url" content="<url>" />
{imageUrl && <meta property="og:image" content={imageUrl} />}
<meta name="twitter:card" content="summary" />
<meta name="twitter:site" content="@ethstatus" />
{/* <meta property="twitter:image" content="<logo>" /> */}
<meta property="twitter:image:alt" content="Status logo" />
<meta property="status-im:target" content="<displayName>" />
<meta property="al:ios:url" content="status-im:/<url>" />
<meta property="al:ios:app_store_id" content="1178893006" />
<meta property="al:ios:app_name" content="Status — Ethereum. Anywhere" />
<meta property="al:android:url" content="status-im:/<url>" />
<meta property="al:android:package" content="im.status.ethereum" />
<meta
property="al:android:app_name"
content="Status — Ethereum. Anywhere"
/>
{/* todo?: except communities; ask product */}
{!index && <meta name="robots" content="noindex" />}
{/* todo?: entity QR */}
{/* todo?: fallback OG */}
{children}
</Head>
)
}
export { _Head as Head }

View File

@ -2,6 +2,7 @@ import Image from 'next/image'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { match, P } from 'ts-pattern' import { match, P } from 'ts-pattern'
import logoBlogSrc from '../../public/images/logo/blog.svg'
import logoSrc from '../../public/images/logo/default.svg' import logoSrc from '../../public/images/logo/default.svg'
import logoDevSrc from '../../public/images/logo/dev.svg' import logoDevSrc from '../../public/images/logo/dev.svg'
import logoLearnSrc from '../../public/images/logo/learn.svg' import logoLearnSrc from '../../public/images/logo/learn.svg'
@ -29,7 +30,7 @@ export const Logo = (props: Props) => {
) )
.with( .with(
P.when(p => p.startsWith('/blog')), P.when(p => p.startsWith('/blog')),
() => <Image src={logoSrc} alt="Status logo" /> () => <Image src={logoBlogSrc} alt="Status logo" />
) )
.otherwise(() => ( .otherwise(() => (
<Image src={logoSrc} alt="Status logo" /> <Image src={logoSrc} alt="Status logo" />

View File

@ -21,9 +21,9 @@ import {
QrCodeIcon, QrCodeIcon,
} from '@status-im/icons' } from '@status-im/icons'
import { useQuery } from '@tanstack/react-query' import { useQuery } from '@tanstack/react-query'
import Head from 'next/head'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { Head } from '@/components/head'
import { ERROR_CODES } from '@/consts/error-codes' import { ERROR_CODES } from '@/consts/error-codes'
import { useURLData } from '@/hooks/use-url-data' import { useURLData } from '@/hooks/use-url-data'
import { getRequestClient } from '@/lib/request-client' import { getRequestClient } from '@/lib/request-client'
@ -80,6 +80,12 @@ export type Data =
info: UserInfo info: UserInfo
} }
const ACTION_VERB: Record<Type, string> = {
community: 'Join',
channel: 'View',
profile: 'Open',
}
const INSTRUCTIONS_HEADING: Record<Type, string> = { const INSTRUCTIONS_HEADING: Record<Type, string> = {
community: 'How to join this community:', community: 'How to join this community:',
channel: 'How to join this channel:', channel: 'How to join this channel:',
@ -93,7 +99,7 @@ const JOIN_BUTTON_LABEL: Record<Type, string> = {
} }
export function PreviewPage(props: PreviewPageProps) { export function PreviewPage(props: PreviewPageProps) {
const { type, decodedData, encodedData } = props const { type, decodedData, encodedData, index } = props
const { asPath } = useRouter() const { asPath } = useRouter()
@ -216,9 +222,34 @@ export function PreviewPage(props: PreviewPageProps) {
return <ErrorPage errorCode={ERROR_CODES.NOT_FOUND} /> return <ErrorPage errorCode={ERROR_CODES.NOT_FOUND} />
} }
// const urlOrigin = process.env.VERCEL_URL
// ? 'https://' + process.env.VERCEL_URL
// : ''
if ((loading && !data) || !data || !publicKey) { if ((loading && !data) || !data || !publicKey) {
return ( return (
<> <>
<Head>
<meta property="og:url" content={`https://status.app${asPath}`} />
{/* todo: test if server-rendered version with which a (social) card would be
generated would not effectively override actual shared link on clicking */}
{/* <meta
property="og:image"
content={`${urlOrigin}/assets/preview/entity.png`}
key="og:image"
/> */}
<meta
property="al:ios:url"
content={`https://status.app${asPath}`}
key="al:ios:url"
/>
<meta
property="al:android:url"
content={`https://status.app${asPath}`}
key="al:android:url"
/>
{!index && <meta name="robots" content="noindex" />}
</Head>
<div className="h-full xl:grid xl:grid-cols-[560px,auto]"> <div className="h-full xl:grid xl:grid-cols-[560px,auto]">
<div className="pb-10"> <div className="pb-10">
<div className="mx-auto px-5 pt-20 xl:px-20"> <div className="mx-auto px-5 pt-20 xl:px-20">
@ -326,10 +357,33 @@ export function PreviewPage(props: PreviewPageProps) {
return ( return (
<> <>
<Head index={props.index} /> <Head>
<title>
{`${ACTION_VERB[type]} ${data.info.displayName} in Status`}
</title>
<meta property="og:url" content={`https://status.app${asPath}`} />
<meta
property="og:title"
content={`${ACTION_VERB[type]} ${data.info.displayName} in Status`}
/>
{/* <meta
property="og:image"
content={`${urlOrigin}/assets/preview/entity.png`}
key="og:image"
/> */}
<meta property="og:description" content={data.info.description} />
<meta
name="apple-itunes-app"
content={`app-id=1178893006, app-argument=status-app://${asPath.replace(
/\//,
''
)}`}
/>
{!index && <meta name="robots" content="noindex" />}
</Head>
<> <>
{/* todo: theme; based on user system settings */} {/* todo: theme; based on user system settings */}
{/* todo: (system or both?) install banner */}
<div <div
style={!bannerURL ? getGradientStyles(data) : undefined} style={!bannerURL ? getGradientStyles(data) : undefined}
className="relative h-full bg-gradient-to-b from-[var(--gradient-color)] to-[#fff] to-20% xl:grid xl:grid-cols-[560px,auto]" className="relative h-full bg-gradient-to-b from-[var(--gradient-color)] to-[#fff] to-20% xl:grid xl:grid-cols-[560px,auto]"

View File

@ -4,6 +4,9 @@ import '@/styles/nav-nested-links.css'
import { ThemeProvider } from '@status-im/components' import { ThemeProvider } from '@status-im/components'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { Inter } from 'next/font/google' import { Inter } from 'next/font/google'
import Head from 'next/head'
import { useRouter } from 'next/router'
import { match, P } from 'ts-pattern'
import type { Page, PageLayout } from 'next' import type { Page, PageLayout } from 'next'
import type { AppProps } from 'next/app' import type { AppProps } from 'next/app'
@ -23,11 +26,122 @@ type Props = AppProps & {
export default function App({ Component, pageProps }: Props) { export default function App({ Component, pageProps }: Props) {
const getLayout: PageLayout = Component.getLayout || (page => page) const getLayout: PageLayout = Component.getLayout || (page => page)
// const urlOrigin = process.env.VERCEL_URL
// ? 'https://' + process.env.VERCEL_URL
// : ''
const { pathname, asPath } = useRouter()
return ( return (
<div id="app" className={inter.variable + ' font-sans'}> <>
<QueryClientProvider client={queryClient}> <Head>
<ThemeProvider>{getLayout(<Component {...pageProps} />)}</ThemeProvider> <title>Status - Private, Secure Communication</title>
</QueryClientProvider>
</div> <meta name="title" content="Status - Private, Secure Communication" />
<meta
name="description"
content="Status brings the power of Ethereum into your pocket by combining a messenger, crypto-wallet, and Web3 browser."
/>
<meta property="og:type" content="website" />
<meta property="og:url" content={`https://status.app${asPath}`} />
<meta
property="og:title"
content="Status - Private, Secure Communication"
/>
<meta
property="og:description"
content="Status brings the power of Ethereum into your pocket by combining a messenger, crypto-wallet, and Web3 browser."
/>
{/* <meta
property="og:image"
content={`${urlOrigin}/assets/preview/page.png`}
key="og:image"
/> */}
<meta property="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@ethstatus" />
<meta name="apple-itunes-app" content="app-id=1178893006" />
<meta
property="al:ios:url"
content={`https://status.app${asPath}`}
key="al:ios:url"
/>
<meta property="al:ios:app_store_id" content="1178893006" />
<meta
property="al:ios:app_name"
content="Status — Ethereum. Anywhere"
/>
<meta
property="al:android:url"
content={`https://status.app${asPath}`}
key="al:android:url"
/>
<meta property="al:android:package" content="im.status.ethereum" />
<meta
property="al:android:app_name"
content="Status — Ethereum. Anywhere"
/>
<meta
property="article:publisher"
content="https://www.facebook.com/ethstatus"
/>
{match(pathname)
.with(
P.when(p => p.startsWith('/insights')),
() => (
<>
<link rel="icon" href="/assets/favicon/dev.png" />
{/* <link rel="apple-touch-icon" href="/assets/favicon/dev.png" />
<link
rel="apple-touch-icon-precomposed"
href="/assets/favicon/dev.png"
/> */}
</>
)
)
.with(
P.when(p => p.startsWith('/learn')),
() => (
<>
<link rel="icon" href="/assets/favicon/learn.png" />
{/* <link rel="apple-touch-icon" href="/assets/favicon/learn.png" />
<link
rel="apple-touch-icon-precomposed"
href="/assets/favicon/learn.png"
/> */}
</>
)
)
.with(
P.when(p => p.startsWith('/blog')),
() => (
<>
<link rel="icon" href="/assets/favicon/blog.png" />
{/* <link rel="apple-touch-icon" href="/assets/favicon/blog.png" />
<link
rel="apple-touch-icon-precomposed"
href="/assets/favicon/blog.png"
/> */}
</>
)
)
.otherwise(() => (
<>
<link rel="icon" href="/assets/favicon/default.png" />
{/* <link rel="apple-touch-icon" href="/assets/favicon/default.png" />
<link
rel="apple-touch-icon-precomposed"
href="/assets/favicon/default.png"
/> */}
</>
))}
</Head>
<div id="app" className={inter.variable + ' font-sans'}>
<QueryClientProvider client={queryClient}>
<ThemeProvider>
{getLayout(<Component {...pageProps} />)}
</ThemeProvider>
</QueryClientProvider>
</div>
</>
) )
} }

View File

@ -29,7 +29,6 @@ export default class Document extends NextDocument {
id="tamagui" id="tamagui"
dangerouslySetInnerHTML={{ __html: Tamagui.getCSS() }} dangerouslySetInnerHTML={{ __html: Tamagui.getCSS() }}
/> />
<meta name="theme-color" content="#09101C" />
</Head> </Head>
<body> <body>
<Main /> <Main />

View File

@ -223,7 +223,7 @@ const BlogDetailPage: Page<Props> = ({ post, relatedPosts }) => {
</div> </div>
</div> </div>
{relatedPosts.length && ( {relatedPosts.length > 0 && (
<div className="border-t border-neutral-10 bg-neutral-5 px-5 pb-[64px] pt-12 lg:px-10"> <div className="border-t border-neutral-10 bg-neutral-5 px-5 pb-[64px] pt-12 lg:px-10">
<div className="mb-6"> <div className="mb-6">
<Text size={27} weight="semibold"> <Text size={27} weight="semibold">