Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import { SingleNetworkSelector } from "@/components/blocks/NetworkSelectors";
import { TokenSelector } from "@/components/blocks/TokenSelector";
import { CopyTextButton } from "@/components/ui/CopyTextButton";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
import { cn } from "@/lib/utils";
import { ChevronDownIcon, CreditCardIcon } from "lucide-react";
import { useCallback, useMemo, useState } from "react";
Expand All @@ -19,10 +21,22 @@ import {
} from "thirdweb";
import { getCurrencyMetadata } from "thirdweb/extensions/erc20";
import { resolveScheme, upload } from "thirdweb/storage";
import { setThirdwebDomains } from "thirdweb/utils";
import { FileInput } from "../../../../components/shared/FileInput";
import {
THIRDWEB_ANALYTICS_DOMAIN,
THIRDWEB_BUNDLER_DOMAIN,
THIRDWEB_INAPP_WALLET_DOMAIN,
THIRDWEB_INSIGHT_API_DOMAIN,
THIRDWEB_PAY_DOMAIN,
THIRDWEB_RPC_DOMAIN,
THIRDWEB_SOCIAL_API_DOMAIN,
THIRDWEB_STORAGE_DOMAIN,
} from "../../../../constants/urls";
import { resolveEns } from "../../../../lib/ens";
import { getVercelEnv } from "../../../../lib/vercel-utils";

export function PaymentLinkForm({ client }: { client: ThirdwebClient }) {
export function PaymentLinkForm() {
const [chainId, setChainId] = useState<number>();
const [recipientAddress, setRecipientAddress] = useState("");
const [tokenAddressWithChain, setTokenAddressWithChain] = useState("");
Expand All @@ -32,8 +46,24 @@ export function PaymentLinkForm({ client }: { client: ThirdwebClient }) {
const [imageUri, setImageUri] = useState<string>("");
const [uploadingImage, setUploadingImage] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string>();
const [showAdvanced, setShowAdvanced] = useState(false);
const [paymentUrl, setPaymentUrl] = useState<string>("");

const client = useMemo(() => {
if (getVercelEnv() !== "production") {
setThirdwebDomains({
rpc: THIRDWEB_RPC_DOMAIN,
pay: THIRDWEB_PAY_DOMAIN,
storage: THIRDWEB_STORAGE_DOMAIN,
insight: THIRDWEB_INSIGHT_API_DOMAIN,
analytics: THIRDWEB_ANALYTICS_DOMAIN,
inAppWallet: THIRDWEB_INAPP_WALLET_DOMAIN,
bundler: THIRDWEB_BUNDLER_DOMAIN,
social: THIRDWEB_SOCIAL_API_DOMAIN,
});
}
return getClientThirdwebClient();
}, []);

const isFormComplete = useMemo(() => {
return chainId && recipientAddress && tokenAddressWithChain && amount;
Expand Down Expand Up @@ -72,7 +102,6 @@ export function PaymentLinkForm({ client }: { client: ThirdwebClient }) {
const handleSubmit = useCallback(
async (e: React.FormEvent) => {
e.preventDefault();
setError(undefined);
setIsLoading(true);

try {
Expand Down Expand Up @@ -112,15 +141,11 @@ export function PaymentLinkForm({ client }: { client: ThirdwebClient }) {
params.set("image", imageUri);
}

const paymentUrl = `${window.location.origin}/pay?${params.toString()}`;

// Copy to clipboard
await navigator.clipboard.writeText(paymentUrl);

// Show success toast
toast.success("Payment link copied to clipboard.");
const url = `${window.location.origin}/pay?${params.toString()}`;
setPaymentUrl(url);
toast.success("Payment link created!");
} catch (err) {
setError(err instanceof Error ? err.message : "An error occurred");
toast.error(err instanceof Error ? err.message : "An error occurred");
} finally {
setIsLoading(false);
}
Expand Down Expand Up @@ -290,7 +315,7 @@ export function PaymentLinkForm({ client }: { client: ThirdwebClient }) {
id="title"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="A title for your payment"
placeholder="Payment for..."
className="w-full"
/>
</div>
Expand Down Expand Up @@ -318,25 +343,35 @@ export function PaymentLinkForm({ client }: { client: ThirdwebClient }) {
</div>
</div>

{error && <div className="text-red-500 text-sm">{error}</div>}

<div className="flex gap-2">
<Button
type="button"
variant="outline"
className="flex-1"
disabled={isLoading || !isFormComplete}
onClick={handlePreview}
>
Preview
</Button>
<Button
type="submit"
className="flex-1"
disabled={isLoading || !isFormComplete}
>
{isLoading ? "Creating..." : "Create"}
</Button>
<div className="flex flex-col space-y-4">
{paymentUrl && (
<CopyTextButton
textToCopy={paymentUrl}
textToShow={paymentUrl}
tooltip="Copy Payment Link"
className="w-full justify-between truncate bg-background px-3 py-2"
copyIconPosition="right"
/>
)}

<div className="flex gap-2">
<Button
type="button"
variant="outline"
className="flex-1"
disabled={isLoading || !isFormComplete}
onClick={handlePreview}
>
Preview
</Button>
<Button
type="submit"
className="flex-1"
disabled={isLoading || !isFormComplete}
>
{isLoading ? "Creating..." : "Create"}
</Button>
</div>
</div>
</form>
</CardContent>
Expand Down
34 changes: 17 additions & 17 deletions apps/dashboard/src/app/pay/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,27 @@ const fontSans = Inter({
display: "swap",
});

export default async function CheckoutLayout({
export default async function PayLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" suppressHydrationWarning>
<Providers>
<ThemeProvider
attribute="class"
disableTransitionOnChange
enableSystem={false}
defaultTheme="dark"
>
<body
className={cn(
"h-screen w-screen bg-background font-sans antialiased",
fontSans.variable,
)}
<body
className={cn(
"h-screen w-screen bg-background font-sans antialiased",
fontSans.variable,
)}
>
<Providers>
<ThemeProvider
attribute="class"
disableTransitionOnChange
enableSystem={false}
defaultTheme="dark"
>
<div className="relative mx-auto flex h-full w-full flex-col items-center justify-center overflow-hidden border py-10">
<div className="relative mx-auto flex h-full w-full flex-col items-center justify-center overflow-x-hidden overflow-y-scroll py-10">
<main className="container z-10 flex justify-center">
{children}
</main>
Expand All @@ -42,9 +42,9 @@ export default async function CheckoutLayout({
className="-bottom-12 -right-12 pointer-events-none absolute lg:right-0 lg:bottom-0"
/>
</div>
</body>
</ThemeProvider>
</Providers>
</ThemeProvider>
</Providers>
</body>
</html>
);
}
10 changes: 1 addition & 9 deletions apps/dashboard/src/app/pay/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { getAuthToken } from "app/(app)/api/lib/getAuthToken";
import type { Metadata } from "next";
import { createThirdwebClient, defineChain, getContract } from "thirdweb";
import { getCurrencyMetadata } from "thirdweb/extensions/erc20";
Expand Down Expand Up @@ -32,14 +31,7 @@ export default async function RoutesPage({
!params.tokenAddress &&
!params.amount
) {
const authToken = await getAuthToken();

const client = getClientThirdwebClient({
jwt: authToken,
teamId: undefined,
});

return <PaymentLinkForm client={client} />;
return <PaymentLinkForm />;
}

// Validate query parameters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export async function getCurrencyMetadata(
symbol: symbol_,
decimals: decimals_,
};
} catch {
throw new Error("Invalid currency token");
} catch (e) {
throw new Error(`Invalid currency token: ${e}`);
}
}
Loading