Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
283e282
Collectibles page first working version
damianmarti Jul 11, 2025
b8f451a
Dark mode
damianmarti Jul 11, 2025
70d4bea
Responsiveness tweaks
damianmarti Jul 14, 2025
307ffed
Connect wallet and get badges from the connected address
damianmarti Jul 17, 2025
35b3be3
Fix hydration issues
damianmarti Jul 17, 2025
2204216
Merge branch 'ethereum:dev' into collectibles
damianmarti Jul 17, 2025
caa9f53
Use Image element instead of img on CollectiblesCurrentYear component
damianmarti Jul 17, 2025
8b3a80d
BASE_URL to COLLECTIBLES_BASE_URL
damianmarti Jul 17, 2025
3ace588
Merge branch 'collectibles' of github.com:damianmarti/ethereum-org-we…
damianmarti Jul 17, 2025
1adc26f
Merge branch 'dev' into pr/15860
wackerow Jul 28, 2025
eddc6ec
refactor: use DS base with ui/card, lucide
wackerow Jul 28, 2025
87074a2
refactor: page to ssr; use DS styling
wackerow Jul 29, 2025
acd84eb
chore: autoformat json
wackerow Jul 29, 2025
da6a31f
refactor: component organization
wackerow Jul 29, 2025
723ed35
feat: add color variants to ui/progress
wackerow Jul 29, 2025
87c0423
refactor: align styling with repo DS
wackerow Jul 29, 2025
8e9f871
fix: adjustments above-the-fold
wackerow Jul 29, 2025
7ccd2e0
fix: simple loading states
wackerow Jul 29, 2025
5ec42f6
fix: wallet connect learn more url
wackerow Jul 29, 2025
dec14a7
refactor: lazy loading
wackerow Jul 29, 2025
b385e48
fix: ui/accordion chevron rotation
wackerow Jul 30, 2025
9cbca5c
feat: add instructions to sections
wackerow Jul 30, 2025
c8bbb3b
feat: add contributing since
wackerow Jul 30, 2025
1a838ad
fix: memoize data, Link component, styling
wackerow Jul 30, 2025
06d34e7
feat: use tags
wackerow Jul 30, 2025
29d034f
refactor: DRY styling, fix ast
wackerow Jul 30, 2025
05cff54
patch: add sr-only h2 to structure
wackerow Jul 30, 2025
d468afa
patch: badge grid and labels
wackerow Jul 30, 2025
16066f7
fix: sanitize badge labels
wackerow Jul 30, 2025
38d8c15
fix: swag link
wackerow Jul 30, 2025
bd9e5f8
fix: responsive padding, "improve" section styling
wackerow Jul 30, 2025
78428f0
chore: clean up patches
wackerow Jul 30, 2025
28ed979
fix: wrap badge + label in link
wackerow Jul 30, 2025
aa5b3ba
chore: clean up labels and ast
wackerow Jul 30, 2025
3d72f2a
feat: add update frequency note
wackerow Jul 31, 2025
82f54c9
feat: add anchor links to year headers
wackerow Jul 31, 2025
cab4e83
chore: polish padding and hover scaling
wackerow Jul 31, 2025
24b4ce4
chore: sort intl json
wackerow Jul 31, 2025
bfa4b42
patch: use group-hover
wackerow Jul 31, 2025
32bc198
Merge branch 'dev' into pr/15860
wackerow Aug 1, 2025
385da56
feat: add year indicator to claimed section
wackerow Aug 1, 2025
cdadf70
Use useQuery and useMemo for badgesWithOwned
damianmarti Aug 6, 2025
8ec9c48
Remove staleTime comment
damianmarti Aug 7, 2025
49edb64
Merge branch 'dev' into pr/damianmarti/15860
corwintines Aug 11, 2025
65ebd0c
Merge branch 'dev' into pr/damianmarti/15860
corwintines Aug 11, 2025
f8853aa
add nav for collectibles page
corwintines Aug 11, 2025
a664a03
i18n: extract missing string
wackerow Aug 11, 2025
2eed932
i18n: update spanish version
wackerow Aug 11, 2025
ec37b97
refactor: WalletProviders; remove locale prop drilling
wackerow Aug 11, 2025
f8585fc
fix: WalletConnect modal issues on load
wackerow Aug 11, 2025
8f49cec
refactor: ExternalLinkIcon
wackerow Aug 11, 2025
36939c6
fix: link styling
wackerow Aug 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions app/[locale]/collectibles/_components/Collectibles/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"use client"

import React from "react"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"

import WalletProviders from "@/components/WalletProviders"

import type { Badge } from "../../types"
import CollectiblesContent from "../CollectiblesContent/lazy"

export type CollectiblesPageProps = {
badges: Badge[]
}

const queryClient = new QueryClient()

const CollectiblesPage = ({ badges }: CollectiblesPageProps) => (
<QueryClientProvider client={queryClient}>
<WalletProviders>
<CollectiblesContent badges={badges} />
</WalletProviders>
</QueryClientProvider>
)

export default CollectiblesPage
5 changes: 5 additions & 0 deletions app/[locale]/collectibles/_components/Collectibles/lazy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import dynamic from "next/dynamic"

import Loading from "./loading"

export default dynamic(() => import("."), { ssr: false, loading: Loading })
10 changes: 10 additions & 0 deletions app/[locale]/collectibles/_components/Collectibles/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Skeleton } from "@/components/ui/skeleton"

const Loading = () => (
<div className="flex w-full flex-col gap-x-8 gap-y-6 xl:flex-row">
<Skeleton className="h-80 w-full rounded-2xl xl:sticky xl:top-28 xl:max-w-xs" />
<Skeleton className="h-[75vh] w-full rounded-2xl" />
</div>
)

export default Loading
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"use client"

import { ConnectButton } from "@rainbow-me/rainbowkit"

import { Button } from "@/components/ui/buttons/Button"

import { useTranslation } from "@/hooks/useTranslation"

const CollectiblesConnectButton = () => {
const { t } = useTranslation("page-collectibles")
return (
<ConnectButton.Custom>
{({ account, chain, openConnectModal, mounted }) => {
const connected = mounted && account && chain

return (
<>
{(() => {
if (!connected) {
return (
<Button onClick={openConnectModal} variant="outline">
{t("page-collectibles-connect-wallet")}
</Button>
)
}

return <ConnectButton showBalance={false} />
})()}
</>
)
}}
</ConnectButton.Custom>
)
}

export default CollectiblesConnectButton
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import dynamic from "next/dynamic"

import Loading from "./loading"

export default dynamic(() => import("."), { ssr: false, loading: Loading })
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Skeleton } from "@/components/ui/skeleton"

const Loading = () => <Skeleton className="h-10 w-full" />

export default Loading
160 changes: 160 additions & 0 deletions app/[locale]/collectibles/_components/CollectiblesContent/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
"use client"

import { useMemo } from "react"
import { useIsMounted } from "usehooks-ts"
import { useAccount } from "wagmi"
import { useQuery } from "@tanstack/react-query"

import { Image } from "@/components/Image"
import { Skeleton } from "@/components/ui/skeleton"

import { cn } from "@/lib/utils/cn"

import { COLLECTIBLES_BASE_URL } from "../../constants"
import type { Badge } from "../../types"
import { type CollectiblesPageProps } from "../Collectibles"
import CollectiblesConnectButton from "../CollectiblesConnectButton/lazy"
import CollectiblesCurrentYear from "../CollectiblesCurrentYear"
import CollectiblesPreviousYears from "../CollectiblesPreviousYears"
import CollectiblesProgress from "../CollectiblesProgress/lazy"

import useTranslation from "@/hooks/useTranslation"
import alreadyContributorImg from "@/public/images/10-year-anniversary/adoption-1.png"

export type BadgeWithOwned = Badge & {
owned: boolean
}

const ADDRESS_STATS_API = `${COLLECTIBLES_BASE_URL}/api/stats/`

const CollectiblesContent = ({ badges }: CollectiblesPageProps) => {
const { t } = useTranslation("page-collectibles")

const currentYear = new Date().getFullYear().toString()

const steps = [
{
title: t("page-collectibles-how-step1-title"),
description: t("page-collectibles-how-step1-desc"),
color: "text-accent-a",
},
{
title: t("page-collectibles-how-step2-title"),
description: t("page-collectibles-how-step2-desc"),
color: "text-accent-b",
},
{
title: t("page-collectibles-how-step3-title"),
description: t("page-collectibles-how-step3-desc"),
color: "text-accent-c",
},
]

const isMounted = useIsMounted()
const { address, isConnected } = useAccount()

const {
data: addressBadges = [],
error,
isLoading,
} = useQuery({
queryKey: ["addressBadges", address],
queryFn: async (): Promise<Badge[]> => {
if (!address) return []
const response = await fetch(`${ADDRESS_STATS_API}${address}`)
if (!response.ok) {
throw new Error("Failed to fetch address badges")
}
return response.json()
},
enabled: !!address,
})

const badgesWithOwned = useMemo((): BadgeWithOwned[] => {
return badges.map((badge) => {
const addressBadge = addressBadges.find((b) => b.id === badge.id)
return {
...badge,
owned: addressBadge ? true : false,
}
})
}, [badges, addressBadges])

return (
<div className="flex flex-col gap-8 xl:flex-row">
{/* Already a contributor? section */}
<div className="flex h-fit w-full flex-col gap-y-4 rounded-2xl border border-accent-a/5 bg-gradient-to-b from-accent-a/5 to-accent-a/10 px-6 py-6 xl:sticky xl:top-28 xl:max-w-xs dark:from-accent-a/10 dark:to-accent-a/20">
<Image
src={alreadyContributorImg}
alt={t("page-collectibles-contributor-img-alt")}
className="h-32 w-32 object-cover"
sizes="128px"
/>
<div>
<h3 className="text-lg">{t("page-collectibles-already-title")}</h3>
<p className="text-body-medium">
{t("page-collectibles-already-desc")}
</p>
</div>

<CollectiblesConnectButton />

{isConnected && !isLoading && !error && (
<CollectiblesProgress badges={badgesWithOwned} />
)}
{isLoading && (
<div className="flex w-full flex-col gap-y-4">
<Skeleton className="h-10 w-full rounded-2xl" />
<Skeleton className="h-10 w-full rounded-2xl" />
</div>
)}
{error && (
<div className="text-body-medium text-red-500">
Error fetching address badges
</div>
)}
</div>

{/* How it works section */}
<div className="flex-1 space-y-8">
<section className="mx-auto space-y-6 border-b p-2 pb-6">
<h2 className="text-4xl">{t("page-collectibles-how-title")}</h2>
<div className="flex flex-col justify-center gap-8 py-4 md:flex-row md:items-center md:gap-12">
{steps.map((step, idx) => (
<div
key={step.title}
className="flex w-full flex-1 flex-row items-center gap-4 max-md:max-w-xs"
>
<div
className={cn(
"flex size-16 shrink-0 items-center justify-center rounded-full border text-2xl font-bold shadow-2xl xl:size-20",
step.color
)}
>
{idx + 1}
</div>
<div className="space-y-1 text-lg">
<div className="font-bold">{step.title}</div>
<div>{step.description}</div>
</div>
</div>
))}
</div>
</section>

<CollectiblesCurrentYear
badges={badgesWithOwned.filter(
(badge) => String(badge.year) === currentYear
)}
address={address}
/>

<CollectiblesPreviousYears
badges={isMounted() && isConnected ? addressBadges : badges}
/>
</div>
</div>
)
}

export default CollectiblesContent
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import dynamic from "next/dynamic"

import Loading from "../Collectibles/loading"

export default dynamic(() => import("."), { ssr: false, loading: Loading })
Loading