init next 13 (#613)

* init next 13

* layout responsive by default again

* more next13 things

* push up

* remove beehiiv embed

* make lgtm happy

* halfway through push up

* push up
This commit is contained in:
Jonas Daniels
2022-11-02 16:05:50 -07:00
committed by GitHub
parent 3d0cb90946
commit a53b6e9e44
19 changed files with 1209 additions and 765 deletions
+7
View File
@@ -64,3 +64,10 @@ yalc.lock
public/sitemap*.xml
# Sentry
.sentryclirc
.vscode
# future_app dir
future_app/
+3 -2
View File
@@ -1,3 +1,4 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}
"typescript.tsdk": "./node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}
+5 -91
View File
@@ -1,94 +1,8 @@
import {
Box,
BoxProps,
ImageProps as ChakraImageProps,
chakra,
} from "@chakra-ui/react";
import NextImage, { ImageProps } from "next/image";
import { useMemo } from "react";
import useDimensions from "react-cool-dimensions";
import { isBrowser } from "utils/isBrowser";
import { chakra } from "@chakra-ui/react";
import NextImage from "next/image";
const ChakraNextUnwrappedImage = chakra(NextImage, {
shouldForwardProp: (prop) =>
[
"width",
"height",
"src",
"alt",
"quality",
"placeholder",
"blurDataURL",
"loader ",
"layout",
"sizes",
"onLoadingComplete",
"priority",
].includes(prop),
export const ChakraNextImage = chakra(NextImage, {
baseStyle: { objectFit: "contain!important", maxW: "100%", zIndex: 1 },
});
const previousLoadedImagesSet = new Set<string>();
export type ChakraNextImageProps = ImageProps &
Omit<BoxProps, "width" | "height"> & {
alt: string;
imgProps?: Omit<ChakraImageProps, "placeholder">;
};
export const ChakraNextImage = (props: ChakraNextImageProps) => {
const {
src,
alt,
width,
quality = 90,
height,
layout = "responsive",
objectFit,
objectPosition,
placeholder = typeof src === "string" ? "empty" : "blur",
imgProps,
priority,
sizes,
...rest
} = props;
const { observe, width: _width } = useDimensions<HTMLDivElement | null>();
const imageId = useMemo(() => {
const _src: string =
typeof src === "string"
? src
: (src as any)?.default?.src || (src as any)?.src || "";
return `${_src}_w=${_width}`;
}, [_width, src]);
const size =
sizes ||
(isBrowser() && _width !== undefined
? `${(_width / window.innerWidth) * 100}vw`
: "100vw");
return (
<Box maxW="100%" pos="relative" {...rest} ref={observe}>
<ChakraNextUnwrappedImage
onLoadingComplete={() => {
previousLoadedImagesSet.add(imageId);
}}
objectFit={objectFit}
objectPosition={objectPosition}
w="auto"
h="auto"
layout={layout}
sizes={size}
width={width}
quality={quality}
height={height}
placeholder={placeholder}
src={src}
alt={alt}
priority={priority}
transition={
previousLoadedImagesSet.has(imageId) ? undefined : "all 0.2s"
}
{...imgProps}
/>
</Box>
);
};
export type ChakraNextImageProps = React.ComponentProps<typeof ChakraNextImage>;
@@ -10,7 +10,6 @@ import {
Icon,
IconButton,
Image,
Link,
Menu,
MenuButton,
MenuItemOption,
@@ -37,7 +36,6 @@ import {
import { ChakraNextImage } from "components/Image";
import { useReleasesFromDeploy } from "components/contract-components/hooks";
import { CONTRACT_TYPE_NAME_MAP, FeatureIconMap } from "constants/mappings";
import OriginalNextLink from "next/link";
import { useRouter } from "next/router";
import { useEffect, useMemo, useState } from "react";
import * as React from "react";
@@ -48,6 +46,7 @@ import {
AddressCopyButton,
Badge,
Card,
ChakraNextLink,
Heading,
LinkButton,
Text,
@@ -479,18 +478,16 @@ const AsyncContractNameCell: React.FC<AsyncContractNameCellProps> = ({
return (
<Skeleton isLoaded={!metadataQuery.isLoading}>
<OriginalNextLink href={href} passHref>
<Link>
<Text
color="blue.700"
_dark={{ color: "blue.300" }}
size="label.md"
_groupHover={{ textDecor: "underline" }}
>
{metadataQuery.data?.name || shortenIfAddress(cell.address)}
</Text>
</Link>
</OriginalNextLink>
<ChakraNextLink href={href} passHref>
<Text
color="blue.700"
_dark={{ color: "blue.300" }}
size="label.md"
_groupHover={{ textDecor: "underline" }}
>
{metadataQuery.data?.name || shortenIfAddress(cell.address)}
</Text>
</ChakraNextLink>
</Skeleton>
);
};
+3 -3
View File
@@ -10,7 +10,7 @@ import {
import { ethers } from "ethers";
import { useTrack } from "hooks/analytics/useTrack";
import { FiExternalLink } from "react-icons/fi";
import { Heading, NextLink, Text } from "tw-components";
import { Heading, Link, Text } from "tw-components";
import { ComponentWithChildren } from "types/component-with-children";
interface PriceLineProps {
@@ -87,7 +87,7 @@ export const GasEstimatorBox: React.FC<GasEstimatorBoxProps> = ({
return (
<Box p={6} border="1px solid" borderColor="borderColor" {...props}>
<NextLink
<Link
href={`https://portal.thirdweb.com/pre-built-contracts/${contractType}`}
onClick={() =>
trackEvent({
@@ -103,7 +103,7 @@ export const GasEstimatorBox: React.FC<GasEstimatorBoxProps> = ({
{CONTRACT_TYPE_NAME_MAP[contractType]}
<Icon as={FiExternalLink} ml={1} boxSize={4} />
</Heading>
</NextLink>
</Link>
<PriceLine
title="Contract creation"
label="The price to deploy this Smart Contract"
+2 -8
View File
@@ -1,5 +1,5 @@
import { Flex, VStack } from "@chakra-ui/react";
import Image from "next/image";
import { ChakraNextImage } from "components/Image";
import { Heading, Link } from "tw-components";
export const Resources: React.FC = () => {
@@ -48,13 +48,7 @@ export const Resources: React.FC = () => {
m={4}
p={4}
>
<Image
width="175px"
objectFit="cover"
height="135px"
src={image}
alt={label}
/>
<ChakraNextImage width={175} height={135} src={image} alt={label} />
<Link href={link} fontSize="20px" mt={4}>
{label}
</Link>
-1
View File
@@ -294,7 +294,6 @@ export const WelcomeScreen: React.FC = () => {
w={{ base: "45%", md: "90%" }}
src={require("public/assets/product-pages/dashboard/hero.png")}
alt=""
layout="responsive"
/>
</Center>
</motion.div>
+1 -7
View File
@@ -122,13 +122,7 @@ export const Hero: ComponentWithChildren<IHero> = ({
>
<Flex justifyContent={{ base: "center", md: "flex-end" }} w="100%">
<AspectRatio ratio={1} maxW={96} w="100%">
<ChakraNextImage
alt=""
src={image}
layout="fill"
objectFit="contain"
priority
/>
<ChakraNextImage alt="" src={image} priority />
</AspectRatio>
</Flex>
</Center>
@@ -12,7 +12,6 @@ export const MultiChainSVG: React.FC = () => {
<ChakraNextImage
alt=""
placeholder="empty"
layout="fill"
src={require(`./multi-chain/${src}.svg`)}
/>
</AspectRatio>
+5 -3
View File
@@ -1,5 +1,3 @@
/** @type {import('next').NextConfig} */
const ContentSecurityPolicy = `
default-src 'self';
img-src * data: blob:;
@@ -36,8 +34,8 @@ const securityHeaders = [
},
];
/** @type {import('next').NextConfig} */
const moduleExports = {
reactStrictMode: true,
async headers() {
return [
{
@@ -109,6 +107,10 @@ const moduleExports = {
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
domains: ["thirdweb.com", "portal.thirdweb.com", "blog.thirdweb.com"],
},
reactStrictMode: true,
experimental: {
scrollRestoration: true,
},
};
// eslint-disable-next-line @typescript-eslint/no-var-requires
+11 -7
View File
@@ -21,6 +21,7 @@
"@emotion/styled": "^11",
"@hookform/resolvers": "^2.8.0",
"@n8tb1t/use-scroll-position": "^2.0.3",
"@next/font": "^13.0.0",
"@react-icons/all-files": "^4.1.0",
"@sentry/nextjs": "^7.16.0",
"@solana/wallet-adapter-base": "^0.9.16",
@@ -33,17 +34,19 @@
"@tanstack/react-query-persist-client": "^4.0.10",
"@tanstack/react-table": "^8.5.13",
"@thirdweb-dev/chain-icons": "^1.0.5",
"@thirdweb-dev/react": "^3.5.1-nightly-4e7b293",
"@thirdweb-dev/sdk": "^3.5.1-nightly-4e7b293",
"@thirdweb-dev/react": "^3.5.1",
"@thirdweb-dev/sdk": "^3.5.1",
"@thirdweb-dev/storage": "^1.0.6",
"bn.js": "^5.2.0",
"ad": "^0.3.2",
"bn.js": "^5.2.1",
"classnames": "^2.3.2",
"color": "^4.0.1",
"date-fns": "^2.28.0",
"emotion-theming": "^11.0.0",
"ethers": "^5.4.6",
"flat": "^5.0.2",
"framer-motion": "^7.0.0",
"next": "^12.0.9",
"next": "^13.0.0",
"next-seo": "^5.0.0",
"nprogress": "^0.2.0",
"papaparse": "^5.3.1",
@@ -64,12 +67,13 @@
"remark-gfm": "^3.0.1",
"spdx-correct": "^3.1.1",
"tiny-invariant": "^1.2.0",
"yarn": "^1.22.19",
"zod": "^3.8.2"
},
"devDependencies": {
"@babel/core": "^7.15.5",
"@chakra-ui/cli": "^2.0.0",
"@next/bundle-analyzer": "^12.1.5",
"@next/bundle-analyzer": "^13.0.0",
"@tanstack/react-location-devtools": "^3.4.4",
"@trivago/prettier-plugin-sort-imports": "^3.2.0",
"@types/color": "^3.0.2",
@@ -86,7 +90,7 @@
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.0",
"eslint-config-next": "^12.0.0",
"eslint-config-next": "^13.0.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-inclusive-language": "^2.1.1",
@@ -100,7 +104,7 @@
"resolutions": {
"@types/react": "^18",
"@types/react-dom": "^18",
"bn.js": "^5.2.0",
"bn.js": "^5.2.1",
"typescript": "^4.7.0"
},
"next-unused": {
+15
View File
@@ -93,6 +93,21 @@ function ConsoleApp({ Component, pageProps }: AppPropsWithLayout) {
const router = useRouter();
useEffect(() => {
// Taken from StackOverflow. Trying to detect both Safari desktop and mobile.
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
if (isSafari) {
// This is kind of a lie.
// We still rely on the manual Next.js scrollRestoration logic.
// However, we *also* don't want Safari grey screen during the back swipe gesture.
// Seems like it doesn't hurt to enable auto restore *and* Next.js logic at the same time.
history.scrollRestoration = "auto";
} else {
// For other browsers, let Next.js set scrollRestoration to 'manual'.
// It seems to work better for Chrome and Firefox which don't animate the back swipe.
}
}, []);
useEffect(() => {
// setup route cancellation
const handleStart = (url: string) => {
+38
View File
@@ -0,0 +1,38 @@
import { withSentry } from "@sentry/nextjs";
import { NextApiRequest, NextApiResponse } from "next";
import invariant from "tiny-invariant";
interface EmailSignupPayload {
email: string;
send_welcome_email?: boolean;
}
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method !== "POST") {
return res.status(400).json({ error: "invalid method" });
}
const { email, send_welcome_email = false } = JSON.parse(
req.body,
) as EmailSignupPayload;
invariant(process.env.BEEHIIV_API_KEY, "missing BEEHIIV_API_KEY");
const response = await fetch("https://api.beehiiv.com/v1/subscribers", {
headers: {
"Content-Type": "application/json",
"X-ApiKey": process.env.BEEHIIV_API_KEY,
},
method: "POST",
body: JSON.stringify({
email,
publication_id: "9f54090a-6d14-406b-adfd-dbb30574f664",
send_welcome_email,
utm_source: "thirdweb.com",
}),
});
return res.status(response.status).json({ status: response.statusText });
};
export default withSentry(handler);
+56 -10
View File
@@ -6,8 +6,10 @@ import {
DarkMode,
Divider,
Flex,
FormControl,
Icon,
IconButton,
Input,
LightMode,
List,
ListIcon,
@@ -40,10 +42,18 @@ import Hero from "public/assets/landingpage/hero.png";
import MobileHero from "public/assets/landingpage/mobile-hero.png";
import ThirdwebTeams from "public/assets/landingpage/thirdweb-teams.png";
import WhiteLogo from "public/assets/landingpage/white-logo.png";
import { useForm } from "react-hook-form";
// end images
import { BsLightningCharge, BsMenuButtonWide } from "react-icons/bs";
import { MdMarkEmailRead, MdOutlineAnalytics } from "react-icons/md";
import { Card, Heading, LinkButton, Text, TrackedLink } from "tw-components";
import {
Button,
Card,
Heading,
LinkButton,
Text,
TrackedLink,
} from "tw-components";
const HomePage: ThirdwebNextPage = () => {
const trackEvent = useTrack();
@@ -753,6 +763,12 @@ const ContractsDescriptorItem: React.FC<ContractsDescriptorItemProps> = ({
};
const NewsLetterSection: React.FC = () => {
const form = useForm({
defaultValues: {
email: "",
},
});
return (
<Box bgColor="rgba(0,0,0,.6)" zIndex="100">
<Container as="section" maxW="container.page" color="gray.500">
@@ -776,15 +792,45 @@ const NewsLetterSection: React.FC = () => {
</Stack>
</Stack>
<Box
as="iframe"
src="https://embeds.beehiiv.com/42f51ba6-da56-42f9-92d8-24a339b9acd0?slim=true"
data-test-id="beehiiv-embed"
frameBorder="0"
scrolling="no"
h="50px"
borderRadius="md"
/>
<form
onSubmit={form.handleSubmit(async (data) => {
try {
await fetch("/api/email-signup", {
method: "POST",
body: JSON.stringify({ email: data.email }),
});
form.setValue("email", "");
} catch (err) {
console.error(err);
}
})}
>
<Flex gap={0} minW={{ base: "100%", md: "350px" }}>
<FormControl isRequired>
<Input
borderRightRadius={0}
type="email"
borderColor="purple.500"
borderRight={0}
_hover={{ borderColor: "purple.500" }}
_focus={{ borderColor: "purple.500" }}
placeholder="Enter your email"
{...form.register("email")}
autoComplete="email"
/>
</FormControl>
<Button
borderLeftRadius={0}
flexShrink={0}
isLoading={form.formState.isSubmitting}
type="submit"
mr={2}
colorScheme="purple"
>
Sign up
</Button>
</Flex>
</form>
</Stack>
</Container>
</Box>
-1
View File
@@ -142,7 +142,6 @@ const Solana: ThirdwebNextPage = () => {
<ChakraNextImage
alt=""
src={require("public/assets/network-pages/solana/hero.png")}
layout="fill"
objectFit="contain"
priority
/>
+13 -2
View File
@@ -19,8 +19,19 @@
"paths": {
"tw-components": ["tw-components"],
"tw-components/*": ["tw-components/*"]
}
},
"plugins": [
{
"name": "next"
}
]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "additional.d.ts"],
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
"additional.d.ts",
".next/types/**/*.ts"
],
"exclude": ["node_modules", ".next"]
}
+10 -11
View File
@@ -1,4 +1,5 @@
import { Card } from "./card";
import { ChakraNextLink } from "./link";
import { Text } from "./text";
import { convertFontSizeToCSSVar } from "./utils/typography";
import {
@@ -17,7 +18,6 @@ import {
} from "@chakra-ui/react";
import { Link as LocationLink, useMatch } from "@tanstack/react-location";
import { useTrack } from "hooks/analytics/useTrack";
import NextLink from "next/link";
import React, { useEffect } from "react";
import { FiCopy, FiExternalLink } from "react-icons/fi";
import { fontWeights, letterSpacings, lineHeights } from "theme/typography";
@@ -126,16 +126,15 @@ export const LinkButton = React.forwardRef<HTMLButtonElement, LinkButtonProps>(
}
return (
<NextLink href={href} passHref>
<Button
as={Link}
ref={ref}
{...restButtonProps}
textDecoration="none!important"
>
{children}
</Button>
</NextLink>
<Button
as={ChakraNextLink}
href={href}
ref={ref}
{...restButtonProps}
textDecoration="none!important"
>
{children}
</Button>
);
},
);
+24 -12
View File
@@ -1,12 +1,24 @@
import {
Link as ChakraLink,
LinkProps as ChakraLinkProps,
forwardRef,
} from "@chakra-ui/react";
import { Link as LocationLink, useMatch } from "@tanstack/react-location";
import { useTrack } from "hooks/analytics/useTrack";
import _NextLink, { LinkProps as _NextLinkProps } from "next/link";
import React, { useCallback } from "react";
export type ChakraNextLinkProps = Omit<ChakraLinkProps, "as"> &
Omit<_NextLinkProps, "as">;
export const ChakraNextLink = forwardRef<ChakraNextLinkProps, "a">(
(props, ref) => <ChakraLink as={_NextLink} {...props} ref={ref} />,
);
export type ChakraLocationLinkProps = Omit<ChakraLinkProps, "as">;
export const ChakraLocationLink = forwardRef<ChakraLocationLinkProps, "a">(
(props, ref) => <ChakraLink as={LocationLink} {...props} ref={ref} />,
);
interface LinkProps
extends Omit<ChakraLinkProps, "href">,
Pick<_NextLinkProps, "href"> {
@@ -36,17 +48,22 @@ export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(
// we're in a react location context, so we can use that
if (match && !noMatch) {
return (
<ChakraLink as={LocationLink} to={href} {...restLinkProps}>
<ChakraLocationLink to={href} {...restLinkProps}>
{children}
</ChakraLink>
</ChakraLocationLink>
);
}
return (
<_NextLink href={href} passHref scroll={scroll || false}>
<ChakraLink ref={ref} _focus={{ boxShadow: "none" }} {...restLinkProps}>
{children}
</ChakraLink>
</_NextLink>
<ChakraNextLink
href={href}
scroll={scroll}
scrollBehavior="smooth"
ref={ref}
_focus={{ boxShadow: "none" }}
{...restLinkProps}
>
{children}
</ChakraNextLink>
);
},
);
@@ -75,8 +92,3 @@ export const TrackedLink = React.forwardRef<
});
TrackedLink.displayName = "TrackedLink";
/**
* @deprecated Use {@link Link} instead.
*/
export const NextLink = Link;
+1005 -592
View File
File diff suppressed because it is too large Load Diff