diff --git a/.gitignore b/.gitignore index 77178f2..5e86ae1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ .next .dockerignore +.env.local diff --git a/app/[slug]/page.tsx b/app/[slug]/page.tsx index e7ac325..22b2c9f 100644 --- a/app/[slug]/page.tsx +++ b/app/[slug]/page.tsx @@ -1,11 +1,15 @@ +/* eslint-disable unicorn/prevent-abbreviations */ import { ens_normalize } from '@adraffy/ens-normalize'; import { formatRecord } from '@ens-tools/format'; +import { Metadata } from 'next'; import { FC, PropsWithChildren, ReactNode } from 'react'; import { FaTelegram } from 'react-icons/fa'; import { FiGithub, FiLink, FiTwitter } from 'react-icons/fi'; import shortNumber from 'short-number'; +import { SPOAPModal } from '../../components/SPOAPModal'; import { useEnstate } from '../../hooks/useEnstate'; +import { useIYKRef } from '../../hooks/useIYKRef'; import { useWarpcast } from '../../hooks/useWarpcast'; const Button: FC> = ({ @@ -13,11 +17,7 @@ const Button: FC> = ({ href, }) => { return ( - + {children} ); @@ -76,8 +76,10 @@ const buttonControls = (key: string, value: string): ReactNode | undefined => { export default async function ({ params: { slug }, + searchParams: { event, iykRef }, }: { params: { slug: string }; + searchParams: { event?: string; iykRef?: string }; }) { const raw_name = slug; const name = ens_normalize(raw_name.toLowerCase()); @@ -86,82 +88,86 @@ export default async function ({ throw new Error('Invalid ENS name'); } - const [enstate, farcaster] = await Promise.all([ + const [enstate, farcaster, iykData] = await Promise.all([ useEnstate(name), useWarpcast(name), + useIYKRef(iykRef), ]); return ( -
-
- frensday -
November 13 2023, Istanbul Türkiye
-
-
-
-
- profile +
+
+
+ frensday +
November 13 2023, Istanbul Türkiye
+
+
+
+
+ profile +
+
+ frensday +
-
- frensday +
+
+ {enstate.name} +
+ {enstate.records.description && ( +
{enstate.records.description}
+ )}
-
-
- {enstate.name} -
- {enstate.records.description && ( -
{enstate.records.description}
+
+ {Object.keys(enstate.records) + .map((key) => buttonControls(key, enstate.records[key])) + .filter(Boolean)} + {farcaster && farcaster.result && ( + )} -
-
-
- {Object.keys(enstate.records) - .map((key) => buttonControls(key, enstate.records[key])) - .filter(Boolean)} - {farcaster && farcaster.result && ( - - )} - + {iykData && } +
); } // Metadata -import { Metadata } from 'next'; -// or Dynamic metadata export async function generateMetadata({ params: { slug }, }: { diff --git a/app/global.css b/app/global.css index b5c61c9..c751ca3 100644 --- a/app/global.css +++ b/app/global.css @@ -1,3 +1,7 @@ @tailwind base; @tailwind components; @tailwind utilities; + +.btn { + @apply bg-[#7116EB] relative rounded-lg flex items-center justify-center gap-2; +} diff --git a/components/POAPModal.tsx b/components/POAPModal.tsx new file mode 100644 index 0000000..0cce5a3 --- /dev/null +++ b/components/POAPModal.tsx @@ -0,0 +1,35 @@ +'use client'; + +import { FC } from 'react'; + +import { IYKRefResponse as IYKReferenceResponse } from '../hooks/useIYKRef'; +import { POAPMetadata } from '../hooks/usePOAPData'; + +export const POAPModal: FC<{ + data: IYKReferenceResponse; + name: string; + metadata: POAPMetadata; +}> = ({ data, name, metadata }) => { + return ( +
+
+
+
+
+ +
+
+
+ Mint POAP +
+
+ Claim your POAP to show you met {name} at frENSday! +
+
+
+ ); +}; diff --git a/components/SPOAPModal.tsx b/components/SPOAPModal.tsx new file mode 100644 index 0000000..4c2c4b7 --- /dev/null +++ b/components/SPOAPModal.tsx @@ -0,0 +1,20 @@ +import { FC } from 'react'; + +import { IYKRefResponse as IYKReferenceResponse } from '../hooks/useIYKRef'; +import { usePOAPData } from '../hooks/usePOAPData'; +import { POAPModal } from './POAPModal'; + +export const SPOAPModal: FC<{ + data: IYKReferenceResponse; + name: string; +}> = async ({ data, name }) => { + if (data.poapEvents.length === 0) return; + + const [iyk_poap_event_data] = data.poapEvents; + + const metadata = await usePOAPData(iyk_poap_event_data.poapEventId); + + if (!metadata) return; + + return ; +}; diff --git a/hooks/useIYKRef.ts b/hooks/useIYKRef.ts new file mode 100644 index 0000000..a4aaead --- /dev/null +++ b/hooks/useIYKRef.ts @@ -0,0 +1,33 @@ +/* eslint-disable unicorn/prevent-abbreviations */ + +type IYKPOAPEvent = { + id: number; + poapEventId: number; + otp: string; + status: 'expired'; +}; + +type IYKLinkedToken = { + contractAddress: string; + chainId: number; + tokenId: string; +}; + +export type IYKRefResponse = { + uid: string; + isValidRef: boolean; + poapEvents: IYKPOAPEvent[]; + linkedToken: IYKLinkedToken; +}; + +export const useIYKRef = async (reference?: string) => { + if (!reference) return; + + const response = await fetch('https://api.iyk.app/refs/' + reference, { + headers: { 'x-api-key': process.env.IYK_API_KEY }, + }); + + if (!response.ok) return; + + return (await response.json()) as IYKRefResponse; +}; diff --git a/hooks/usePOAPData.ts b/hooks/usePOAPData.ts new file mode 100644 index 0000000..1b85740 --- /dev/null +++ b/hooks/usePOAPData.ts @@ -0,0 +1,25 @@ +const POAP_API_HOST = 'https://api.poap.tech'; + +export type POAPMetadata = { + description: string; + external_url: string; + home_url: string; + image_url: string; + name: string; + year: number; + tags: string[]; + attributes: { + trait_type: string; + value: string; + }[]; +}; + +export const usePOAPData = async ( + poapEventId: number +): Promise => { + const request = await fetch(`${POAP_API_HOST}/metadata/${poapEventId}/1`); + + if (!request.ok) return; + + return await request.json(); +};