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
4 changes: 3 additions & 1 deletion src/components/common/CreatorCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ const CreatorCard: React.FC<CreatorCardProps> = ({ creator, className }) => {
setTransactionState('success');
showToast.transactionSuccess(
'Purchase Successful!',
`You successfully bought a key for ${creator.title}`
`You successfully bought a key for ${creator.title}`,
'0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
'https://stellar.expert/explorer/testnet/tx/0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
);

window.setTimeout(() => {
Expand Down
17 changes: 17 additions & 0 deletions src/components/common/PendingTxModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { Button } from '@/components/ui/button';
import CircularSpinner from '@/components/common/CircularSpinnerProps';
import { cn } from '@/lib/utils';
import TransactionHashRow from '@/components/common/TransactionHashRow';

export interface PendingTxModalProps {
open: boolean;
Expand All @@ -18,6 +19,10 @@ export interface PendingTxModalProps {
isLoading?: boolean;
title?: string;
description?: string;
/** Optional transaction hash to display */
txHash?: string;
/** Optional explorer link for the transaction */
explorerUrl?: string;
/** Prevent the user from dismissing the modal while loading */
blockDismissal?: boolean;
/** Optional footer action (e.g. "View on explorer") */
Expand All @@ -33,6 +38,8 @@ const PendingTxModal: React.FC<PendingTxModalProps> = ({
isLoading = false,
title = 'Transaction pending',
description = 'Your transaction has been submitted and is awaiting confirmation.',
txHash,
explorerUrl,
blockDismissal = false,
action,
}) => {
Expand Down Expand Up @@ -87,6 +94,16 @@ const PendingTxModal: React.FC<PendingTxModalProps> = ({
</DialogDescription>
</DialogHeader>

{txHash && (
<div className="mt-2 border-t border-white/5 pt-4">
<TransactionHashRow
hash={txHash}
explorerUrl={explorerUrl}
className="bg-white/5 rounded-lg px-3 py-2"
/>
</div>
)}

{(action || !blockDismissal) && (
<DialogFooter className="sm:justify-center">
{action && (
Expand Down
74 changes: 74 additions & 0 deletions src/components/common/TransactionHashRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React, { useState } from 'react';
import { Copy, Check, ExternalLink } from 'lucide-react';
import { cn } from '@/lib/utils';
import { shortenAddress } from '@/lib/web3/format';

interface TransactionHashRowProps {
hash: string;
label?: string;
explorerUrl?: string;
className?: string;
}

const TransactionHashRow: React.FC<TransactionHashRowProps> = ({
hash,
label = 'Transaction Hash',
explorerUrl,
className,
}) => {
const [copied, setCopied] = useState(false);

const handleCopy = async (e: React.MouseEvent) => {
e.stopPropagation();
await navigator.clipboard.writeText(hash);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};

return (
<div
className={cn(
'flex items-center justify-between gap-3 py-1.5 text-xs',
className
)}
>
<span className="shrink-0 uppercase tracking-wider text-white/40">
{label}
</span>
<div className="flex items-center gap-2 overflow-hidden">
<span
className="font-mono font-medium text-white/75 truncate"
title={hash}
>
{shortenAddress(hash, 8, 8)}
</span>
<div className="flex shrink-0 items-center gap-1">
<button
onClick={handleCopy}
className="inline-flex size-6 items-center justify-center rounded-md bg-white/5 text-white/40 transition-colors hover:bg-white/10 hover:text-white"
aria-label="Copy transaction hash"
>
{copied ? (
<Check className="size-3 text-emerald-400" />
) : (
<Copy className="size-3" />
)}
</button>
{explorerUrl && (
<a
href={explorerUrl}
target="_blank"
rel="noopener noreferrer"
className="inline-flex size-6 items-center justify-center rounded-md bg-white/5 text-white/40 transition-colors hover:bg-white/10 hover:text-white"
aria-label="View on block explorer"
>
<ExternalLink className="size-3" />
</a>
)}
</div>
</div>
</div>
);
};

export default TransactionHashRow;
19 changes: 16 additions & 3 deletions src/utils/toast.util.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import toast from 'react-hot-toast';
import TransactionHashRow from '@/components/common/TransactionHashRow';

const showToast = {
message: (message: string) => {
Expand All @@ -17,7 +18,12 @@ const showToast = {
toast.remove();
toast.loading(message);
},
transactionSuccess: (title: string, message?: string) => {
transactionSuccess: (
title: string,
message?: string,
txHash?: string,
explorerUrl?: string
) => {
toast.remove();
toast.custom(
t => (
Expand All @@ -26,9 +32,9 @@ const showToast = {
t.visible ? 'animate-enter' : 'animate-leave'
} pointer-events-auto flex w-full max-w-sm rounded-xl border border-amber-500/20 bg-slate-900 shadow-xl shadow-amber-500/10`}
>
<div className="flex w-full p-4">
<div className="flex w-full p-4 flex-col gap-3">
<div className="flex items-start">
<div className="ml-3 flex-1">
<div className="flex-1">
<p className="font-jakarta text-sm font-bold text-white">
{title}
</p>
Expand All @@ -39,6 +45,13 @@ const showToast = {
)}
</div>
</div>
{txHash && (
<TransactionHashRow
hash={txHash}
explorerUrl={explorerUrl}
className="mt-1 bg-white/5 rounded-lg px-2.5 py-1.5"
/>
)}
</div>
</div>
),
Expand Down
Loading