1- import { ShieldCheckIcon } from "lucide-react " ;
1+ import { cn } from "@workspace/ui/lib/utils " ;
22import type { Metadata } from "next" ;
33import { ThemeProvider } from "next-themes" ;
4- import { Bridge , defineChain , toTokens } from "thirdweb" ;
5- import { getChainMetadata } from "thirdweb/chains" ;
6- import { shortenAddress } from "thirdweb/utils" ;
4+ import { Bridge } from "thirdweb" ;
75import { getPaymentLink } from "@/api/universal-bridge/links" ;
8- import { Badge } from "@/components/ui/badge" ;
96import { NEXT_PUBLIC_THIRDWEB_API_HOST } from "@/constants/public-envs" ;
107import {
118 API_SERVER_SECRET ,
129 DASHBOARD_THIRDWEB_SECRET_KEY ,
1310} from "@/constants/server-envs" ;
1411import { getConfiguredThirdwebClient } from "@/constants/thirdweb.server" ;
15- import { resolveEns } from "@/lib/ens" ;
16- import { resolveSchemeWithErrorHandler } from "@/utils/resolveSchemeWithErrorHandler" ;
1712import { PayPageWidget } from "../components/client/PayPageWidget.client" ;
18- import { payAppThirdwebClient } from "../constants " ;
13+ import { PayIdPageHeader } from "./header " ;
1914
2015const title = "thirdweb Pay" ;
2116const description = "Fast, secure, and simple payments." ;
@@ -54,171 +49,44 @@ export default async function PayPage({
5449 tokenAddress : paymentLink . destinationToken . address ,
5550 } ) ;
5651
57- const chainPromise = getChainMetadata (
58- // eslint-disable-next-line no-restricted-syntax
59- defineChain ( Number ( paymentLink . destinationToken . chainId ) ) ,
60- ) ;
61-
62- const recipientPromise = resolveEns (
63- paymentLink . receiver ,
64- getConfiguredThirdwebClient ( {
65- secretKey : DASHBOARD_THIRDWEB_SECRET_KEY ,
66- teamId : undefined ,
67- } ) ,
68- ) ;
69-
70- const [ tokens , projectMetadata , chain , recipientEnsOrAddress ] =
71- await Promise . all ( [
72- tokensPromise ,
73- projectMetadataPromise ,
74- chainPromise ,
75- recipientPromise ,
76- ] ) ;
52+ const [ tokens , projectMetadata ] = await Promise . all ( [
53+ tokensPromise ,
54+ projectMetadataPromise ,
55+ ] ) ;
7756
7857 const token = tokens [ 0 ] ;
7958 if ( ! token ) {
8059 throw new Error ( "Token not found" ) ;
8160 }
8261
8362 return (
84- < div className = "relative flex h-dvh w-full items-center justify-center " >
63+ < div className = "relative flex flex-col min- h-dvh w-full" >
8564 < ThemeProvider
86- forcedTheme = { theme === "light" ? "light" : "dark" }
65+ defaultTheme = { theme === "light" ? "light" : "dark" }
8766 attribute = "class"
8867 disableTransitionOnChange
8968 enableSystem = { false }
9069 >
91- < div className = "flex z-10 flex-col lg:flex-row h-full w-full" >
92- < header className = "min-w-full lg:min-w-[500px] border-b lg:border-r lg:h-full bg-card flex flex-col gap-4 items-start p-4 lg:p-8" >
93- < div >
94- < div className = "flex flex-row items-center justify-start gap-4" >
95- { projectMetadata . image && (
96- < img
97- src = {
98- resolveSchemeWithErrorHandler ( {
99- uri : projectMetadata . image ,
100- client : payAppThirdwebClient ,
101- } ) || ""
102- }
103- alt = { projectMetadata . name }
104- width = { 25 }
105- height = { 25 }
106- className = "rounded-full overflow-hidden"
107- />
108- ) }
109- < h2 className = "text-xl font-bold" > { projectMetadata . name } </ h2 >
110- </ div >
111- { projectMetadata . description && (
112- < p className = "mt-2 text-sm text-muted-foreground" >
113- { projectMetadata . description }
114- </ p >
115- ) }
116- </ div >
117-
118- < div className = "hidden lg:block my-4 w-full" >
119- { paymentLink . amount && (
120- < div className = "flex flex-col gap-1 w-full my-4" >
121- < span className = "text-muted-foreground text-xs" > Details</ span >
122- < div className = "font-medium flex-row flex justify-between items-center w-full" >
123- < div className = "flex flex-row items-center gap-2" >
124- { token . iconUri && (
125- < img
126- src = { resolveSchemeWithErrorHandler ( {
127- uri : token . iconUri ,
128- client : getConfiguredThirdwebClient ( {
129- secretKey : DASHBOARD_THIRDWEB_SECRET_KEY ,
130- teamId : undefined ,
131- } ) ,
132- } ) }
133- alt = { token . name }
134- width = { 25 }
135- height = { 25 }
136- className = "size-5 rounded-full overflow-hidden"
137- />
138- ) }
139- { toTokens ( BigInt ( paymentLink . amount ) , token . decimals ) } { " " }
140- { token . symbol }
141- </ div >
142- { token . prices . USD && (
143- < span >
144- $
145- { (
146- Number ( token . prices . USD ) *
147- Number (
148- toTokens (
149- BigInt ( paymentLink . amount ) ,
150- token . decimals ,
151- ) ,
152- )
153- ) . toFixed ( 2 ) }
154- </ span >
155- ) }
156- </ div >
157- </ div >
158- ) }
159- { chain && (
160- < div className = "flex flex-col gap-1 w-full my-4" >
161- < span className = "text-muted-foreground text-xs" > Network</ span >
162- < div className = "font-medium flex-row flex justify-between items-center w-full" >
163- < div className = "flex flex-row items-center gap-2" >
164- { chain . icon ?. url && (
165- < img
166- src = { resolveSchemeWithErrorHandler ( {
167- uri : chain . icon . url ,
168- client : getConfiguredThirdwebClient ( {
169- secretKey : DASHBOARD_THIRDWEB_SECRET_KEY ,
170- teamId : undefined ,
171- } ) ,
172- } ) }
173- alt = { chain . name }
174- width = { chain . icon . width }
175- height = { chain . icon . height }
176- className = "size-5 rounded-full overflow-hidden"
177- />
178- ) }
179- { chain . name }
180- </ div >
181- </ div >
182- </ div >
183- ) }
184- { recipientEnsOrAddress . ensName ||
185- ( recipientEnsOrAddress . address && (
186- < div className = "flex flex-col gap-1 w-full my-4" >
187- < span className = "text-muted-foreground text-xs" >
188- Seller
189- </ span >
190- < div className = "font-medium flex-row flex justify-between items-center w-full" >
191- { recipientEnsOrAddress . ensName ??
192- shortenAddress ( recipientEnsOrAddress . address ) }
193- </ div >
194- </ div >
195- ) ) }
196- </ div >
70+ < PayIdPageHeader
71+ projectIcon = { projectMetadata . image || undefined }
72+ projectName = { projectMetadata . name }
73+ />
19774
198- < div className = "mt-auto hidden lg:block" >
199- < Badge className = "flex items-center gap-1.5 bg-purple-100 text-purple-800 border-purple-200 dark:bg-purple-950 dark:text-purple-300 dark:border-purple-800" >
200- < ShieldCheckIcon className = "size-3" />
201- Secured by thirdweb
202- </ Badge >
203- </ div >
204- </ header >
205- < main className = "flex justify-center py-12 w-full items-center grow" >
206- < PayPageWidget
207- amount = {
208- paymentLink . amount ? BigInt ( paymentLink . amount ) : undefined
209- }
210- chainId = { Number ( paymentLink . destinationToken . chainId ) }
211- clientId = { undefined } // Payment links don't need to use the same client ID to be executed
212- image = { paymentLink . imageUrl }
213- name = { paymentLink . title }
214- paymentLinkId = { id }
215- purchaseData = { paymentLink . purchaseData }
216- recipientAddress = { paymentLink . receiver }
217- redirectUri = { redirectUri }
218- token = { token }
219- />
220- </ main >
221- </ div >
75+ < main className = "flex justify-center py-12 w-full items-center grow overflow-hidden relative" >
76+ < DotsBackgroundPattern />
77+ < PayPageWidget
78+ amount = { paymentLink . amount ? BigInt ( paymentLink . amount ) : undefined }
79+ chainId = { Number ( paymentLink . destinationToken . chainId ) }
80+ clientId = { undefined } // Payment links don't need to use the same client ID to be executed
81+ image = { paymentLink . imageUrl }
82+ name = { paymentLink . title }
83+ paymentLinkId = { id }
84+ purchaseData = { paymentLink . purchaseData }
85+ recipientAddress = { paymentLink . receiver }
86+ redirectUri = { redirectUri }
87+ token = { token }
88+ />
89+ </ main >
22290 </ ThemeProvider >
22391 </ div >
22492 ) ;
@@ -237,7 +105,24 @@ async function getProjectMetadata(clientId: string) {
237105 }
238106
239107 const { data } = ( await response . json ( ) ) as {
240- data : { name : string ; image : string | null ; description : string | null } ;
108+ data : { name : string ; image : string | null } ;
241109 } ;
242110 return data ;
243111}
112+
113+ function DotsBackgroundPattern ( props : { className ?: string } ) {
114+ return (
115+ < div
116+ className = { cn (
117+ "pointer-events-none absolute -inset-x-36 -inset-y-24 text-foreground/20 dark:text-muted-foreground/15 hidden lg:block" ,
118+ props . className ,
119+ ) }
120+ style = { {
121+ backgroundImage : "radial-gradient(currentColor 1px, transparent 1px)" ,
122+ backgroundSize : "24px 24px" ,
123+ maskImage :
124+ "radial-gradient(ellipse 100% 100% at 50% 50%, black 30%, transparent 50%)" ,
125+ } }
126+ />
127+ ) ;
128+ }
0 commit comments