Skip to content

Commit 352f15c

Browse files
authored
Merge pull request #15181 from ethereum/staging
Deploy v10.0.0
2 parents 3d4aa87 + 70a1ea4 commit 352f15c

File tree

781 files changed

+20821
-4738
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

781 files changed

+20821
-4738
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
* @wackerow @corwintines @pettinarip @minimalsm
99

1010
# Owners of specific files
11-
/src/data/consensus-bounty-hunters.json @djrtwo @asanso @fredriksvantes
11+
/src/data/consensus-bounty-hunters.json @asanso @fredriksvantes
1212
/src/data/wallets/new-to-crypto.ts @konopkja @minimalsm
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: Suggest a resource
2+
description: Suggest a new resource to list on ethereum.org/resources
3+
title: Suggest a resource
4+
labels: ["resource 📚"]
5+
body:
6+
- type: markdown
7+
attributes:
8+
value: |
9+
Before suggesting a resource, make sure you've read [our listing policy](https://www.ethereum.org/en/contributing/adding-resources/).
10+
- type: markdown
11+
attributes:
12+
value: Only continue with the issue if your resource meets the criteria listed there.
13+
- type: markdown
14+
attributes:
15+
value: If it does, complete the following information which we need to accurately list the resource.
16+
- type: markdown
17+
id: resource_info
18+
attributes:
19+
value: "## Resource info"
20+
- type: input
21+
id: resource_name
22+
attributes:
23+
label: Name
24+
description: Please provide the official name of the resource
25+
validations:
26+
required: true
27+
- type: input
28+
id: resource_url
29+
attributes:
30+
label: Resource URL
31+
description: Please provide a URL to the resource
32+
validations:
33+
required: true
34+
- type: textarea
35+
id: resource_description
36+
attributes:
37+
label: Description
38+
description: Please provide a short 1-2 sentence description of the resource
39+
validations:
40+
required: true
41+
- type: textarea
42+
id: resource_logo
43+
attributes:
44+
label: Logo
45+
description: |
46+
Please provide an SVG or transparent PNG
47+
Tip: You can attach images by clicking this area to highlight it and then dragging files in.
48+
- type: input
49+
id: resource_category
50+
attributes:
51+
label: Category
52+
description: Please specify a best fit to categorize the resource (e.g., DeFi, NFT, Scaling, etc.)
53+
- type: checkboxes
54+
id: resource_work_on
55+
attributes:
56+
label: Would you like to work on this issue?
57+
options:
58+
- label: "Yes"
59+
required: false
60+
- label: "No"
61+
required: false
62+
validations:
63+
required: true

app/[locale]/[...slug]/page.tsx

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import pick from "lodash.pick"
2+
import { notFound } from "next/navigation"
3+
import { getMessages, setRequestLocale } from "next-intl/server"
4+
5+
import I18nProvider from "@/components/I18nProvider"
6+
import mdComponents from "@/components/MdComponents"
7+
8+
import { dataLoader } from "@/lib/utils/data/dataLoader"
9+
import { dateToString } from "@/lib/utils/date"
10+
import { getPostSlugs } from "@/lib/utils/md"
11+
import { getRequiredNamespacesForPage } from "@/lib/utils/translations"
12+
13+
import { LOCALES_CODES } from "@/lib/constants"
14+
15+
import { componentsMapping, layoutMapping } from "@/layouts"
16+
import { fetchGFIs } from "@/lib/api/fetchGFIs"
17+
import { getPageData } from "@/lib/md/data"
18+
import { getMdMetadata } from "@/lib/md/metadata"
19+
20+
const loadData = dataLoader([["gfissues", fetchGFIs]])
21+
22+
function getLayoutFromSlug(slug: string) {
23+
if (slug.includes("developers/docs")) {
24+
return "docs"
25+
}
26+
27+
if (slug.includes("developers/tutorials")) {
28+
return "tutorial"
29+
}
30+
31+
return "static"
32+
}
33+
34+
export default async function Page({
35+
params,
36+
}: {
37+
params: Promise<{ locale: string; slug: string[] }>
38+
}) {
39+
const { locale, slug: slugArray } = await params
40+
41+
// Check if this specific path is in our valid paths
42+
const validPaths = await generateStaticParams()
43+
const isValidPath = validPaths.some(
44+
(path) =>
45+
path.locale === locale && path.slug.join("/") === slugArray.join("/")
46+
)
47+
48+
if (!isValidPath) {
49+
notFound()
50+
}
51+
52+
// Enable static rendering
53+
setRequestLocale(locale)
54+
55+
const [gfissues] = await loadData()
56+
57+
const slug = slugArray.join("/")
58+
59+
const {
60+
content,
61+
frontmatter,
62+
tocItems,
63+
lastEditLocaleTimestamp,
64+
isTranslated,
65+
contributors,
66+
} = await getPageData({
67+
locale,
68+
slug,
69+
// TODO: Address component typing error here (flip `FC` types to prop object types)
70+
// @ts-expect-error Incompatible component function signatures
71+
components: { ...mdComponents, ...componentsMapping },
72+
scope: {
73+
gfissues,
74+
},
75+
})
76+
77+
// Determine the actual layout after we have the frontmatter
78+
const layout = frontmatter.template || getLayoutFromSlug(slug)
79+
const Layout = layoutMapping[layout]
80+
81+
// If the page has a published date, format it
82+
if ("published" in frontmatter) {
83+
frontmatter.published = dateToString(frontmatter.published)
84+
}
85+
86+
// Get i18n messages
87+
const allMessages = await getMessages({ locale })
88+
const requiredNamespaces = getRequiredNamespacesForPage(slug, layout)
89+
const messages = pick(allMessages, requiredNamespaces)
90+
91+
return (
92+
<I18nProvider locale={locale} messages={messages}>
93+
<Layout
94+
slug={slug}
95+
frontmatter={frontmatter}
96+
tocItems={tocItems}
97+
lastEditLocaleTimestamp={lastEditLocaleTimestamp}
98+
contentNotTranslated={!isTranslated}
99+
contributors={contributors}
100+
// TODO: Remove this once we have a real timeToRead value
101+
timeToRead={2}
102+
>
103+
{content}
104+
</Layout>
105+
</I18nProvider>
106+
)
107+
}
108+
109+
export async function generateStaticParams() {
110+
const slugs = await getPostSlugs("/")
111+
112+
return LOCALES_CODES.flatMap((locale) =>
113+
slugs.map((slug) => ({
114+
slug: slug.split("/").slice(1),
115+
locale,
116+
}))
117+
)
118+
}
119+
120+
export const dynamicParams = false
121+
122+
export async function generateMetadata({
123+
params,
124+
}: {
125+
params: Promise<{ locale: string; slug: string[] }>
126+
}) {
127+
const { locale, slug } = await params
128+
129+
return await getMdMetadata({
130+
locale,
131+
slug,
132+
})
133+
}

app/[locale]/error.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"use client"
2+
3+
import { useEffect } from "react"
4+
5+
import MainArticle from "@/components/MainArticle"
6+
import Translation from "@/components/Translation"
7+
import { Button } from "@/components/ui/buttons/Button"
8+
9+
export default function Error({
10+
error,
11+
reset,
12+
}: {
13+
error: Error
14+
reset: () => void
15+
}) {
16+
useEffect(() => {
17+
// TODO: log the error to an error reporting service
18+
console.error(error)
19+
}, [error])
20+
21+
return (
22+
<div className="mx-auto mb-0 mt-16 flex w-full flex-col items-center">
23+
<MainArticle className="my-8 w-full space-y-8 px-8 py-4">
24+
<h1>
25+
<Translation id="we-couldnt-find-that-page" />
26+
</h1>
27+
<p>Something went wrong.</p>
28+
<Button onClick={() => reset()}>Try again</Button>
29+
</MainArticle>
30+
</div>
31+
)
32+
}

app/[locale]/layout.tsx

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import pick from "lodash.pick"
2+
import { notFound } from "next/navigation"
3+
import { getMessages, setRequestLocale } from "next-intl/server"
4+
5+
import { Lang } from "@/lib/types"
6+
7+
import { getLastDeployDate } from "@/lib/utils/getLastDeployDate"
8+
import { getLocaleTimestamp } from "@/lib/utils/time"
9+
10+
import Providers from "./providers"
11+
12+
import "@/styles/global.css"
13+
14+
import { routing } from "@/i18n/routing"
15+
import { BaseLayout } from "@/layouts/BaseLayout"
16+
17+
export default async function LocaleLayout({
18+
children,
19+
params: { locale },
20+
}: {
21+
children: React.ReactNode
22+
params: { locale: string }
23+
}) {
24+
if (!routing.locales.includes(locale)) {
25+
notFound()
26+
}
27+
28+
// Enable static rendering
29+
setRequestLocale(locale)
30+
31+
const allMessages = await getMessages({ locale })
32+
const messages = pick(allMessages, "common")
33+
34+
const lastDeployDate = getLastDeployDate()
35+
const lastDeployLocaleTimestamp = getLocaleTimestamp(
36+
locale as Lang,
37+
lastDeployDate
38+
)
39+
40+
return (
41+
<html lang={locale} suppressHydrationWarning>
42+
<body>
43+
<Providers locale={locale} messages={messages}>
44+
<BaseLayout lastDeployLocaleTimestamp={lastDeployLocaleTimestamp}>
45+
{children}
46+
</BaseLayout>
47+
</Providers>
48+
</body>
49+
</html>
50+
)
51+
}

app/[locale]/not-found.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import NotFoundPage from "@/components/NotFoundPage"
2+
3+
export default function NotFound() {
4+
return <NotFoundPage />
5+
}

app/[locale]/providers.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"use client"
2+
3+
import { type AbstractIntlMessages } from "next-intl"
4+
5+
import I18nProvider from "@/components/I18nProvider"
6+
import ThemeProvider from "@/components/ThemeProvider"
7+
import { TooltipProvider } from "@/components/ui/tooltip"
8+
9+
import { FeedbackWidgetProvider } from "@/contexts/FeedbackWidgetContext"
10+
11+
export default function Providers({
12+
children,
13+
locale,
14+
messages,
15+
}: {
16+
children: React.ReactNode
17+
locale: string
18+
messages: AbstractIntlMessages
19+
}) {
20+
return (
21+
<I18nProvider locale={locale} messages={messages}>
22+
<ThemeProvider>
23+
<FeedbackWidgetProvider>
24+
<TooltipProvider>{children}</TooltipProvider>
25+
</FeedbackWidgetProvider>
26+
</ThemeProvider>
27+
</I18nProvider>
28+
)
29+
}

app/layout.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { ReactNode } from "react"
2+
3+
import "@/styles/global.css"
4+
5+
type Props = {
6+
children: ReactNode
7+
}
8+
9+
// Since we have a root `not-found.tsx` page, a layout file
10+
// is required, even if it's just passing children through.
11+
export default function RootLayout({ children }: Props) {
12+
return children
13+
}

app/not-found.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"use client"
2+
3+
import Error from "next/error"
4+
5+
import { DEFAULT_LOCALE } from "@/lib/constants"
6+
7+
export default function GlobalNotFound() {
8+
return (
9+
<html lang={DEFAULT_LOCALE}>
10+
<body>
11+
<Error statusCode={404} />
12+
</body>
13+
</html>
14+
)
15+
}

app/page.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { redirect } from "next/navigation"
2+
3+
import { routing } from "@/i18n/routing"
4+
5+
export default function Page() {
6+
redirect(routing.defaultLocale)
7+
}

0 commit comments

Comments
 (0)