diff --git a/app/[locale]/10years/_components/AdoptionSwiper.tsx b/app/[locale]/10years/_components/AdoptionSwiper.tsx index 17f5b6623fa..9ab70a033a9 100644 --- a/app/[locale]/10years/_components/AdoptionSwiper.tsx +++ b/app/[locale]/10years/_components/AdoptionSwiper.tsx @@ -11,13 +11,20 @@ import { import { cn } from "@/lib/utils/cn" -import { adoptionCards, adoptionStyles } from "./data" +import { AdoptionCard } from "./types" -const AdoptionSwiper = () => { +type AdoptionCardProps = { + adoptionCards: AdoptionCard[] + adoptionStyles: string[] +} +const AdoptionSwiper = ({ + adoptionCards, + adoptionStyles, +}: AdoptionCardProps) => { return (
- + {adoptionCards.map((card, index) => (
- + {innovationCards.map((card, index) => (

{card.title}

diff --git a/app/[locale]/10years/_components/TenYearGlobe.tsx b/app/[locale]/10years/_components/TenYearGlobe.tsx index f2ab6a843ae..05085f67b99 100644 --- a/app/[locale]/10years/_components/TenYearGlobe.tsx +++ b/app/[locale]/10years/_components/TenYearGlobe.tsx @@ -1,6 +1,6 @@ "use client" -import { useEffect, useMemo, useRef, useState } from "react" +import { useCallback, useEffect, useMemo, useRef, useState } from "react" import { useTheme } from "next-themes" import Globe from "react-globe.gl" import { type GlobeMethods } from "react-globe.gl" @@ -12,6 +12,7 @@ import Link from "@/components/ui/Link" import countries from "./countries.json" import { useBreakpointValue } from "@/hooks/useBreakpointValue" +import { usePrefersReducedMotion } from "@/hooks/usePrefersReducedMotion" import EthLogo from "@/public/images/assets/eth-glyph-colored.png" // Define a type for event data @@ -42,11 +43,12 @@ const TenYearGlobe = ({ events }: { events: EventData[] }) => { const globeRef = useRef() const globeContainerRef = useRef(null) const { resolvedTheme } = useTheme() + const { prefersReducedMotion } = usePrefersReducedMotion() const atmosphereColor = resolvedTheme === "dark" ? "#B38DF0" : "#945AF4" const width = useBreakpointValue({ - base: 260, + base: window?.innerWidth - 64 || 260, // Full width on mobile with padding sm: 400, md: 500, lg: 600, @@ -61,7 +63,7 @@ const TenYearGlobe = ({ events }: { events: EventData[] }) => { const b = Math.min(255, Math.floor((basePurple & 0xff) * 1.3)) return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}` }) - }, [countries.features, resolvedTheme]) + }, [resolvedTheme]) const hexPolygonColor = (feature: object) => { const idx = countries.features.indexOf( @@ -78,10 +80,18 @@ const TenYearGlobe = ({ events }: { events: EventData[] }) => { return hexPolygonColors[idx] } + // Function to safely set auto-rotate based on motion preferences + const setAutoRotate = useCallback( + (controls: ExtendedOrbitControls, value: boolean) => { + controls.autoRotate = value && !prefersReducedMotion + }, + [prefersReducedMotion] + ) + useEffect(() => { if (globeRef.current) { const controls = globeRef.current.controls() as ExtendedOrbitControls - controls.autoRotate = true + setAutoRotate(controls, true) controls.enablePan = false controls.enableZoom = false controls.autoRotateSpeed = 2.0 @@ -90,7 +100,7 @@ const TenYearGlobe = ({ events }: { events: EventData[] }) => { const ambientLight = new THREE.AmbientLight(0xffffff, 1) globeRef.current.scene().add(ambientLight) } - }, []) + }, [setAutoRotate]) // Prepare htmlElementsData for EthLogo const htmlElementsData = events.map((event) => ({ @@ -176,7 +186,7 @@ const TenYearGlobe = ({ events }: { events: EventData[] }) => { } // Stop rotation immediately const controls = globeRef.current.controls() as ExtendedOrbitControls - controls.autoRotate = false + setAutoRotate(controls, false) if ("autoRotateSpeed" in controls) controls.autoRotateSpeed = 0 if (controls._sphericalDelta) { controls._sphericalDelta.theta = 0 @@ -193,7 +203,7 @@ const TenYearGlobe = ({ events }: { events: EventData[] }) => { if (globeRef.current) { const controls = globeRef.current.controls() as ExtendedOrbitControls - controls.autoRotate = true + setAutoRotate(controls, true) if ("autoRotateSpeed" in controls) controls.autoRotateSpeed = 2.0 } } @@ -201,7 +211,7 @@ const TenYearGlobe = ({ events }: { events: EventData[] }) => { onPointClick={() => { if (globeRef.current) { const controls = globeRef.current.controls() as ExtendedOrbitControls - controls.autoRotate = false + setAutoRotate(controls, false) if ("autoRotateSpeed" in controls) controls.autoRotateSpeed = 0 if (controls._sphericalDelta) { controls._sphericalDelta.theta = 0 @@ -227,8 +237,8 @@ const TenYearGlobe = ({ events }: { events: EventData[] }) => { return (
{MemoizedGlobe} {hoveredEvent && tooltipPos && ( diff --git a/app/[locale]/10years/_components/TenYearHero.tsx b/app/[locale]/10years/_components/TenYearHero.tsx index 1a9057a39dd..2d3b3fa3e89 100644 --- a/app/[locale]/10years/_components/TenYearHero.tsx +++ b/app/[locale]/10years/_components/TenYearHero.tsx @@ -8,30 +8,32 @@ import ParallaxImage from "@/components/Image/ParallaxImage" import TenYearBackgroundImage from "@/public/images/10-year-anniversary/10-year-background.png" import TenYearGraphicImage from "@/public/images/10-year-anniversary/10-year-graphic.png" +const [initialText, ...initialWords] = [ + "censorship resistance", + "100% uptime", + "decentralization", + "community building", + "developer growth", + "global collaboration", + "cypherpunk values", + "hackathons", + "censorship resistance", + "permissionless finance", + "credible neutrality", + "the infinite garden", + "client diversity", +] + const TenYearHero = () => { const [words, setWords] = useState<{ text: string; words: string[] }>({ - text: "censorship resistance", - words: [ - "censorship resistance", - "100% uptime", - "decentralization", - "community building", - "developer growth", - "global collaboration", - "cypherpunk values", - "hackathons", - "censorship resistance", - "permissionless finance", - "credible neutrality", - "the infinite garden", - "client diversity", - ], + text: initialText, + words: initialWords, }) // loops over chars to morph a text to another const morpher = (start: string, end: string): void => { // array of chars to randomly morph the text between start and end - const chars = "abcdefghijklmnopqrstuvwxyz".split("") + const chars = "abcdfgijklnopqsvwxyz".split("") // duration of the global morph const duration = 3 // speed of the morph for each letter @@ -136,7 +138,17 @@ const TenYearHero = () => {

Celebrating 10 years of{" "} - {words.text} + + + {initialText} + + + {words.text} + +

) diff --git a/app/[locale]/10years/_components/TenYearHomeBanner.tsx b/app/[locale]/10years/_components/TenYearHomeBanner.tsx index e72da0de85d..35abf1a0523 100644 --- a/app/[locale]/10years/_components/TenYearHomeBanner.tsx +++ b/app/[locale]/10years/_components/TenYearHomeBanner.tsx @@ -15,11 +15,11 @@ const TenYearHomeBanner = () => { -
+
- +

diff --git a/app/[locale]/10years/_components/data.tsx b/app/[locale]/10years/_components/data.tsx index a5d5bf854fe..d4c0b80554d 100644 --- a/app/[locale]/10years/_components/data.tsx +++ b/app/[locale]/10years/_components/data.tsx @@ -1,5 +1,7 @@ import Link from "@/components/ui/Link" +import { AdoptionCard } from "./types" + import Adoption1Image from "@/public/images/10-year-anniversary/adoption-1.png" import Adoption2Image from "@/public/images/10-year-anniversary/adoption-2.png" import Adoption3Image from "@/public/images/10-year-anniversary/adoption-3.png" @@ -7,13 +9,13 @@ import DefiSummerImage from "@/public/images/10-year-anniversary/defi-summer.png import EthETFImage from "@/public/images/10-year-anniversary/eth-etf.png" import EthereumLaunchImage from "@/public/images/10-year-anniversary/ethereum-launch.png" import NftImage from "@/public/images/10-year-anniversary/nft-frontier.png" +import TheMergeImage from "@/public/images/10-year-anniversary/robot-and-crowd-cheering.png" import Adoption5Image from "@/public/images/10-year-anniversary/robot-walking.png" -import TheMergeImage from "@/public/images/10-year-anniversary/the-merge.png" import StableCoinImage from "@/public/images/10-year-anniversary/the-pioneer-stablecoin.png" import Adoption4Image from "@/public/images/10-year-anniversary/walking-talking-1.png" import Adoption6Image from "@/public/images/10-year-anniversary/walking-talking-2.png" -const adoptionCards = [ +const adoptionCards: AdoptionCard[] = [ { image: Adoption1Image, title: "Decade of Decentralization", @@ -102,12 +104,12 @@ const adoptionCards = [ // duplicate 1 2 3, 1 2 3 to fix mobile slider bug where styles are not applied const adoptionStyles = [ - "bg-background bg-gradient-to-t from-20% to-60% from-accent-c/10 to-accent-c/5 dark:from-accent-c/20 dark:to-accent-c/10 border-accent-c/10", + "bg-background bg-gradient-to-b from-20% to-60% from-accent-c/10 to-accent-c/5 dark:from-accent-c/20 dark:to-accent-c/10 border-accent-c/10", "bg-background bg-gradient-to-b from-20% to-60% from-accent-b/10 to-accent-b/5 dark:from-accent-b/20 dark:to-accent-b/10 border-accent-b/10", - "bg-background bg-gradient-to-r from-20% to-60% from-accent-a/10 to-accent-a/5 dark:from-accent-a/20 dark:to-accent-a/10 border-accent-a/10", - "bg-background bg-gradient-to-t from-20% to-60% from-accent-c/10 to-accent-c/5 dark:from-accent-c/20 dark:to-accent-c/10 border-accent-c/10", + "bg-background bg-gradient-to-b from-20% to-60% from-accent-a/10 to-accent-a/5 dark:from-accent-a/20 dark:to-accent-a/10 border-accent-a/10", + "bg-background bg-gradient-to-b from-20% to-60% from-accent-c/10 to-accent-c/5 dark:from-accent-c/20 dark:to-accent-c/10 border-accent-c/10", "bg-background bg-gradient-to-b from-20% to-60% from-accent-b/10 to-accent-b/5 dark:from-accent-b/20 dark:to-accent-b/10 border-accent-b/10", - "bg-background bg-gradient-to-r from-20% to-60% from-accent-a/10 to-accent-a/5 dark:from-accent-a/20 dark:to-accent-a/10 border-accent-a/10", + "bg-background bg-gradient-to-b from-20% to-60% from-accent-a/10 to-accent-a/5 dark:from-accent-a/20 dark:to-accent-a/10 border-accent-a/10", ] const innovationCards = [ diff --git a/app/[locale]/10years/_components/types.ts b/app/[locale]/10years/_components/types.ts new file mode 100644 index 00000000000..48c2f690a12 --- /dev/null +++ b/app/[locale]/10years/_components/types.ts @@ -0,0 +1,20 @@ +import { StaticImageData } from "next/image" + +export type Story = { + storyEnglish: string + storyOriginal: string + category: string + name: string + date: string + country: string + twitter: string + region: string +} + +export type AdoptionCard = { + image: StaticImageData + title: string + description: React.ReactNode + href: string + linkText: string +} diff --git a/app/[locale]/10years/_components/utils.ts b/app/[locale]/10years/_components/utils.ts new file mode 100644 index 00000000000..ca6740971ce --- /dev/null +++ b/app/[locale]/10years/_components/utils.ts @@ -0,0 +1,31 @@ +import { formatDate, isValidDate } from "@/lib/utils/date" + +import { DEFAULT_LOCALE } from "@/lib/constants" + +import type { Story } from "./types" + +const parseDate = (date: string, locale = DEFAULT_LOCALE): string => { + // TODO: Remove this check when spreadsheet is fixed + // Currently dates are in the formatted as "DD.MM." which is not parsable by Date.parse + // If partially valid date, reformat it + const partiallyValidDate = /^(\d{1,2})\.(\d{1})\.$/ + if (partiallyValidDate.test(date)) { + const [, day, month] = date.match(partiallyValidDate) || [] + const newDate = `2025-${month.padStart(2, "0")}-${day.padStart(2, "0")}` + return formatDate(newDate, locale) + } + + // If the date is already in a valid format, return it + if (isValidDate(date)) return formatDate(date, locale) + // If the date is not recognized, return original value + return date +} + +export const parseStoryDates = ( + stories: Story[], + locale = DEFAULT_LOCALE +): Story[] => + stories.map(({ date, ...story }) => ({ + ...story, + date: parseDate(date, locale), + })) diff --git a/app/[locale]/10years/page.tsx b/app/[locale]/10years/page.tsx index e4c5bfe8946..231da40f07e 100644 --- a/app/[locale]/10years/page.tsx +++ b/app/[locale]/10years/page.tsx @@ -29,6 +29,7 @@ import InnovationSwiper from "./_components/InnovationSwiper" import Stories from "./_components/Stories" import TenYearGlobe from "./_components/TenYearGlobe" import TenYearHero from "./_components/TenYearHero" +import { parseStoryDates } from "./_components/utils" import { fetch10YearEvents } from "@/lib/api/fetch10YearEvents" import { fetch10YearStories } from "@/lib/api/fetch10YearStories" @@ -54,6 +55,8 @@ const Page = async ({ params }: { params: Promise<{ locale: Lang }> }) => { const [fetched10YearEvents, fetched10YearStories] = await loadData() + const stories = parseStoryDates(fetched10YearStories, locale) + // Get i18n messages const allMessages = await getMessages({ locale }) const requiredNamespaces = getRequiredNamespacesForPage("/10years") @@ -271,13 +274,16 @@ const Page = async ({ params }: { params: Promise<{ locale: Lang }> }) => {

- +
{adoptionCards.map((card, index) => (
}) => {
- +
@@ -327,7 +333,7 @@ const Page = async ({ params }: { params: Promise<{ locale: Lang }> }) => { 10 year anniversary logo

Have an idea for how the community can celebrate?

diff --git a/app/[locale]/_components/home.tsx b/app/[locale]/_components/home.tsx index 472dc7a3ef3..db09497a328 100644 --- a/app/[locale]/_components/home.tsx +++ b/app/[locale]/_components/home.tsx @@ -608,7 +608,10 @@ const HomePage = ({ -

+
diff --git a/public/images/10-year-anniversary/10yeartext-mobile.svg b/public/images/10-year-anniversary/10yeartext-mobile.svg index 9a8e2c1f7d5..dc42495b51a 100644 --- a/public/images/10-year-anniversary/10yeartext-mobile.svg +++ b/public/images/10-year-anniversary/10yeartext-mobile.svg @@ -1,4 +1,4 @@ - + diff --git a/public/images/10-year-anniversary/defi-summer.png b/public/images/10-year-anniversary/defi-summer.png index f27f88d813f..d2f30b51b4b 100644 Binary files a/public/images/10-year-anniversary/defi-summer.png and b/public/images/10-year-anniversary/defi-summer.png differ diff --git a/public/images/10-year-anniversary/eth-etf.png b/public/images/10-year-anniversary/eth-etf.png index da4e8c3b25e..d2f30b51b4b 100644 Binary files a/public/images/10-year-anniversary/eth-etf.png and b/public/images/10-year-anniversary/eth-etf.png differ diff --git a/public/images/10-year-anniversary/ethereum-launch.png b/public/images/10-year-anniversary/ethereum-launch.png index 5c319bcc2a2..492a7a3d501 100644 Binary files a/public/images/10-year-anniversary/ethereum-launch.png and b/public/images/10-year-anniversary/ethereum-launch.png differ diff --git a/public/images/10-year-anniversary/nft-frontier.png b/public/images/10-year-anniversary/nft-frontier.png index 8b729220f37..1b6ebbe2716 100644 Binary files a/public/images/10-year-anniversary/nft-frontier.png and b/public/images/10-year-anniversary/nft-frontier.png differ diff --git a/public/images/10-year-anniversary/robot-and-crowd-cheering.png b/public/images/10-year-anniversary/robot-and-crowd-cheering.png new file mode 100644 index 00000000000..1285ca24716 Binary files /dev/null and b/public/images/10-year-anniversary/robot-and-crowd-cheering.png differ diff --git a/public/images/10-year-anniversary/the-merge.png b/public/images/10-year-anniversary/the-merge.png deleted file mode 100644 index ba90d3f10c2..00000000000 Binary files a/public/images/10-year-anniversary/the-merge.png and /dev/null differ diff --git a/public/images/10-year-anniversary/the-pioneer-stablecoin.png b/public/images/10-year-anniversary/the-pioneer-stablecoin.png index 7669b8827f4..249a0b4332c 100644 Binary files a/public/images/10-year-anniversary/the-pioneer-stablecoin.png and b/public/images/10-year-anniversary/the-pioneer-stablecoin.png differ diff --git a/src/data/mocks/fetched10YearEvents.json b/src/data/mocks/fetched10YearEvents.json index 465201bd74b..6900acbb09a 100644 --- a/src/data/mocks/fetched10YearEvents.json +++ b/src/data/mocks/fetched10YearEvents.json @@ -189,8 +189,8 @@ } ] }, - "central & south ameirca": { - "label": "Central & South Ameirca", + "central & south america": { + "label": "Central & South America", "events": [ { "host": "Nodo Serrano", @@ -201,7 +201,7 @@ "city": "Tandil", "country": "Argentina", "region": "Central & South America", - "continent": "Central & South Ameirca", + "continent": "Central & South America", "lat": "-37.3288", "lng": "-59.1367", "readyToShow": "TRUE", @@ -216,7 +216,7 @@ "city": "Monterrey", "country": "Mexico", "region": "Central & South America", - "continent": "Central & South Ameirca", + "continent": "Central & South America", "lat": "25.6866", "lng": "-100.3161", "readyToShow": "TRUE", @@ -231,7 +231,7 @@ "city": "Cochabamba", "country": "Bolivia", "region": "Central & South America", - "continent": "Central & South Ameirca", + "continent": "Central & South America", "lat": "-17.382", "lng": "-66.1596", "readyToShow": "TRUE", @@ -246,7 +246,7 @@ "city": "Florianópolis", "country": "Brazil", "region": "Central & South America", - "continent": "Central & South Ameirca", + "continent": "Central & South America", "lat": "-27.5969", "lng": "-48.5468", "readyToShow": "TRUE", @@ -389,4 +389,4 @@ } ] } -} \ No newline at end of file +} diff --git a/src/hooks/useRefWidth.ts b/src/hooks/useRefWidth.ts new file mode 100644 index 00000000000..62720b5fce0 --- /dev/null +++ b/src/hooks/useRefWidth.ts @@ -0,0 +1,39 @@ +"use client" + +import { type RefObject, useEffect, useState } from "react" + +import { useEventListener } from "./useEventListener" + +export const useRefWidth = ( + ref: RefObject, + padding: number = 0 +): number => { + const [width, setWidth] = useState(0) + + const updateWidth = () => { + if (ref.current) { + const rect = ref.current.getBoundingClientRect() + setWidth(Math.max(0, rect.width - padding)) + } + } + + // Use the internal useEventListener for window resize + useEventListener("resize", updateWidth) + + useEffect(() => { + // Initial measurement + updateWidth() + + // Create ResizeObserver for more accurate monitoring + const resizeObserver = new ResizeObserver(updateWidth) + if (ref.current) { + resizeObserver.observe(ref.current) + } + + return () => { + resizeObserver.disconnect() + } + }, [ref, padding]) + + return width +} diff --git a/src/lib/utils/date.ts b/src/lib/utils/date.ts index 1bea2c4553b..9aff7d97bfc 100644 --- a/src/lib/utils/date.ts +++ b/src/lib/utils/date.ts @@ -7,11 +7,11 @@ export const isValidDate = (dateString?: string | number): boolean => { return !isNaN(date.getTime()) } -export const formatDate = (date: string) => { +export const formatDate = (date: string, locale: string = "en-US") => { if (/^\d{4}$/.test(date)) { return date } - return new Date(date).toLocaleDateString("en-US", { + return new Date(date).toLocaleDateString(locale, { month: "long", day: "numeric", year: "numeric", diff --git a/src/styles/global.css b/src/styles/global.css index 107a97d667b..57843e06d39 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -24,8 +24,9 @@ rgba(58, 142, 137, 0.2) 100% ); --feedback-gradient: var(--gradient-main); - --table-box-shadow: 0 14px 66px rgba(0, 0, 0, 0.07), - 0 10px 17px rgba(0, 0, 0, 0.03), 0 4px 7px rgba(0, 0, 0, 0.05); + --table-box-shadow: + 0 14px 66px rgba(0, 0, 0, 0.07), 0 10px 17px rgba(0, 0, 0, 0.03), + 0 4px 7px rgba(0, 0, 0, 0.05); --table-item-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); --gradient-banner: radial-gradient( @@ -49,7 +50,7 @@ rgba(231, 202, 200, 0.1) 82.88% ); - --gradient-step-1: rgba(127, 127, 213, 0.20); + --gradient-step-1: rgba(127, 127, 213, 0.2); } .dark { @@ -69,7 +70,8 @@ #44404d 52.42%, #303038 98.77% ); - --table-box-shadow: 0 14px 66px hsla(0, 0%, 96.1%, 0.07), + --table-box-shadow: + 0 14px 66px hsla(0, 0%, 96.1%, 0.07), 0 10px 17px hsla(0, 0%, 96.1%, 0.03), 0 4px 7px hsla(0, 0%, 96.1%, 0.05); --table-item-box-shadow: 0 1px 1px hsla(0, 0%, 100%, 0.1); --banner-grid-gradient: linear-gradient( @@ -78,8 +80,8 @@ rgba(134, 253, 232, 0.08) 100% ); --search-background: #4c4c4c; - - --gradient-step-1: rgba(127, 127, 213, 0.20); + + --gradient-step-1: rgba(127, 127, 213, 0.2); } } @@ -201,6 +203,10 @@ .gold { @apply font-bold text-staking-gold; } + + .dev-ring { + @apply ring ring-red-500 sm:ring-orange-500 md:ring-yellow-400 lg:ring-green-500 xl:ring-blue-500 2xl:ring-violet-500; + } } @layer utilities {