Skip to content

Add TransactionDialog component #2496

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 6, 2025
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
76 changes: 76 additions & 0 deletions packages/kit/src/components/TransactionDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React, {useEffect} from "react"
import {Dialog} from "./internal/Dialog"
import {Button} from "./internal/Button"
import {TransactionLink} from "./TransactionLink"
import {LoaderCircleIcon} from "../icons/LoaderCircleIcon"
import {CircleCheckIcon} from "../icons/CircleCheckIcon"
import {useFlowTransactionStatus} from "../hooks/useFlowTransactionStatus"

interface TransactionDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
txId?: string
onSuccess?: () => void
pendingTitle?: string
pendingDescription?: string
successTitle?: string
successDescription?: string
closeOnSuccess?: boolean
}

export const TransactionDialog: React.FC<TransactionDialogProps> = ({
open,
onOpenChange,
txId,
onSuccess,
pendingTitle,
pendingDescription,
successTitle,
successDescription,
closeOnSuccess,
}) => {
const {transactionStatus} = useFlowTransactionStatus({id: txId})
const isSuccess =
typeof transactionStatus?.status === "number" &&
transactionStatus.status >= 3

useEffect(() => {
if (isSuccess) {
if (onSuccess) onSuccess()
if (closeOnSuccess) onOpenChange(false)
}
}, [isSuccess, onSuccess, closeOnSuccess, onOpenChange])

return (
<Dialog isOpen={open} onClose={() => onOpenChange(false)}>
<div className="flex flex-col items-center gap-4 py-8 min-w-[320px] animate-fade-in">
<div className="flex flex-col items-center gap-2">
{!isSuccess ? (
<LoaderCircleIcon className="animate-spin text-blue-500 w-12 h-12" />
) : (
<CircleCheckIcon className="text-green-500 animate-pop w-12 h-12" />
)}
<div className="text-lg font-semibold text-center">
{!isSuccess
? pendingTitle || "Transaction Pending"
: successTitle || "Transaction Successful"}
</div>
</div>
<div className="text-center text-gray-500 mt-2">
{!isSuccess
? pendingDescription ||
"Your transaction is being processed. Please wait..."
: successDescription || "Your transaction was successful!"}
</div>
{isSuccess && txId && <TransactionLink txId={txId} variant="link" />}
<Button
variant="outline"
onClick={() => onOpenChange(false)}
className="mt-4"
>
Close
</Button>
</div>
</Dialog>
)
}
21 changes: 21 additions & 0 deletions packages/kit/src/icons/CircleCheckIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from "react"

export const CircleCheckIcon: React.FC<{className?: string}> = ({
className,
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className={className}
>
<circle cx="12" cy="12" r="10" />
<path d="m9 12 2 2 4-4" />
</svg>
)
20 changes: 20 additions & 0 deletions packages/kit/src/icons/LoaderCircleIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from "react"

export const LoaderCircleIcon: React.FC<{className?: string}> = ({
className,
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className={className}
>
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
</svg>
)