diff --git a/packages/nextjs/app/builders/_components/BuilderPicture.tsx b/packages/nextjs/app/builders/_components/BuilderPicture.tsx new file mode 100644 index 0000000..1bc63f7 --- /dev/null +++ b/packages/nextjs/app/builders/_components/BuilderPicture.tsx @@ -0,0 +1,57 @@ +"use client"; + +import { useEffect, useState } from "react"; +import Link from "next/link"; +import { getAddress, isAddress } from "viem"; +import { normalize } from "viem/ens"; +import { useEnsAvatar, useEnsName } from "wagmi"; +import { Address, BlockieAvatar } from "~~/components/scaffold-eth"; +import { Builder } from "~~/types/builders"; + +type ProfilePictureProps = { + person: Builder; +}; + +export const BuilderPicture = ({ person }: ProfilePictureProps) => { + const [ensAvatar, setEnsAvatar] = useState(); + const checkSumAddress = person.address ? getAddress(person.address) : undefined; + + const { data: fetchedEns } = useEnsName({ + address: checkSumAddress, + chainId: 1, + query: { + enabled: isAddress(checkSumAddress ?? ""), + }, + }); + + const { data: fetchedEnsAvatar } = useEnsAvatar({ + name: fetchedEns ? normalize(fetchedEns) : undefined, + chainId: 1, + query: { + enabled: Boolean(fetchedEns), + gcTime: 30_000, + }, + }); + + useEffect(() => { + setEnsAvatar(fetchedEnsAvatar); + }, [fetchedEnsAvatar]); + + return ( +
+
+ + +
+
+ + See Profile + +
+
+
+ ); +}; diff --git a/packages/nextjs/app/builders/_components/MentorPicture.tsx b/packages/nextjs/app/builders/_components/MentorPicture.tsx new file mode 100644 index 0000000..48ac47a --- /dev/null +++ b/packages/nextjs/app/builders/_components/MentorPicture.tsx @@ -0,0 +1,24 @@ +import Image from "next/image"; +import Link from "next/link"; +import { Mentor } from "~~/types/builders"; + +type ProfilePictureProps = { + person: Mentor; +}; + +export const MentorPicture = ({ person }: ProfilePictureProps) => { + return ( +
+ + {person.profileLink} + {person.name} + +
+ ); +}; diff --git a/packages/nextjs/app/builders/_components/index.tsx b/packages/nextjs/app/builders/_components/index.tsx new file mode 100644 index 0000000..d1d0239 --- /dev/null +++ b/packages/nextjs/app/builders/_components/index.tsx @@ -0,0 +1,2 @@ +export * from "./MentorPicture"; +export * from "./BuilderPicture"; diff --git a/packages/nextjs/app/builders/page.tsx b/packages/nextjs/app/builders/page.tsx new file mode 100644 index 0000000..e18afe6 --- /dev/null +++ b/packages/nextjs/app/builders/page.tsx @@ -0,0 +1,102 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { BuilderPicture, MentorPicture } from "./_components/index"; +import type { NextPage } from "next"; +import { useScaffoldEventHistory } from "~~/hooks/scaffold-eth"; +import { Builder, Mentor } from "~~/types/builders"; + +const getRandomGender = () => { + return Math.random() < 0.5 ? "men" : "women"; +}; + +const getRandomUserImage = () => { + const randomInt = Math.floor(Math.random() * 50); + const randomGender = getRandomGender(); + + return `https://randomuser.me/api/portraits/${randomGender}/${randomInt}.jpg`; +}; + +const getBuilderFromEvent = (event: any): Builder => ({ + image: getRandomUserImage(), + profileLink: "builders/" + event.args.builder, + address: event.args.builder, + checkedIn: true, +}); + +const mentors: Mentor[] = [ + { + name: "Eda", + image: "https://avatars.githubusercontent.com/u/22100698?v=4", + profileLink: "https://github.com/edakturk14", + checkedIn: false, + }, + { + name: "Derrek", + image: "https://avatars.githubusercontent.com/u/80121818?v=4", + profileLink: "https://github.com/derrekcoleman", + + checkedIn: false, + }, + { + name: "Philip", + image: "https://avatars.githubusercontent.com/u/90064316?v=4", + profileLink: "https://github.com/phipsae", + checkedIn: false, + }, +]; + +const Builders: NextPage = () => { + const [buildersCheckedIn, setBuildersCheckedIn] = useState(0); + const [builders, setBuilders] = useState([]); + + const { data: events, isLoading: isLoadingEvents } = useScaffoldEventHistory({ + contractName: "BatchRegistry", + eventName: "CheckedIn", + fromBlock: 31231n, + watch: true, + filters: { first: true } as any, + blockData: true, + transactionData: true, + receiptData: true, + }); + + useEffect(() => { + if (!isLoadingEvents && events != undefined) { + setBuildersCheckedIn(events.length); + setBuilders(events.map(e => getBuilderFromEvent(e))); + } + }, [isLoadingEvents, events]); + + return ( + <> +
+

Batch #9 people 💻

+
+
+
+
+

Mentors

+
+ {mentors.map((mentor, i) => ( + + ))} +
+
+ +
+

Builders 🏗

+

Builders that checkedIn: {buildersCheckedIn}

+
+ {[...new Map(builders.map(builder => [builder.address, builder])).values()].map((builder, i) => ( + + ))} +
+
+
+
+ + ); +}; + +export default Builders; diff --git a/packages/nextjs/components/Header.tsx b/packages/nextjs/components/Header.tsx index 58e9572..0e02123 100644 --- a/packages/nextjs/components/Header.tsx +++ b/packages/nextjs/components/Header.tsx @@ -19,6 +19,10 @@ export const menuLinks: HeaderMenuLink[] = [ label: "Home", href: "/", }, + { + label: "Builders", + href: "/builders", + }, { label: "Debug Contracts", href: "/debug", diff --git a/packages/nextjs/next.config.js b/packages/nextjs/next.config.js index d765869..c58f41d 100644 --- a/packages/nextjs/next.config.js +++ b/packages/nextjs/next.config.js @@ -14,6 +14,22 @@ const nextConfig = { config.externals.push("pino-pretty", "lokijs", "encoding"); return config; }, + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: 'avatars.githubusercontent.com', + port: '', + pathname: '/u/**', + }, + { + protocol: 'https', + hostname: 'randomuser.me', + port: '', + pathname: '/api/portraits/**', + } + ], + }, }; module.exports = nextConfig; diff --git a/packages/nextjs/types/builders.ts b/packages/nextjs/types/builders.ts new file mode 100644 index 0000000..bf52477 --- /dev/null +++ b/packages/nextjs/types/builders.ts @@ -0,0 +1,10 @@ +export type Builder = { + image: string; + profileLink: string; + address?: `0x${string}`; + checkedIn: boolean; +}; + +export type Mentor = Builder & { + name: string; +};