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
9 changes: 7 additions & 2 deletions src/apps/wallet/nfts/nft/nft-tile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,21 @@ import styles from './nft-tile.module.scss';

interface NFTTileProps {
nft: NFT;
onClick?: (nft: NFT) => void;
}

export const NFTTile = ({ nft }: NFTTileProps) => {
export const NFTTile = ({ nft, onClick }: NFTTileProps) => {
const history = useHistory();
const [isCopied, setIsCopied] = useState(false);
const nftId = nft.id;
const nftName = nft.metadata?.name ?? 'Unnamed Token';

const handleTileClick = () => {
history.push(`/wallet/nfts/${encodeURIComponent(nft.collectionAddress)}/${encodeURIComponent(nftId)}`);
if (onClick) {
onClick(nft);
} else {
history.push(`/wallet/nfts/${encodeURIComponent(nft.collectionAddress)}/${encodeURIComponent(nftId)}`);
}
};

const handleCopyId = (e: React.MouseEvent) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,64 @@
text-align: center;
color: var(--text-secondary);
}

.nftTransferDetails {
display: flex;
flex-direction: column;
align-items: center;
padding: 24px var(--l-spacing);
border: 1px solid var(--border-primary);
border-radius: 12px;
gap: 16px;
}

.nftPreview {
width: 120px;
height: 120px;
}

.nftImage {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 8px;
}

.nftImagePlaceholder {
width: 100%;
height: 100%;
background: var(--bg-secondary);
border-radius: 8px;
display: grid;
place-items: center;
color: var(--text-secondary);
}

.nftInfo {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
text-align: center;
}

.nftName {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
}

.nftCollection {
font-size: 14px;
color: var(--text-secondary);
}

.nftTokenId {
font-size: 12px;
color: var(--text-tertiary);
font-family: monospace;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
69 changes: 49 additions & 20 deletions src/apps/wallet/send/review-transfer/wallet-review-transfer.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
import { useDispatch, useSelector } from 'react-redux';
import { previousStage, transferToken } from '../../../../store/wallet';
import { previousStage, transferToken, transferNft } from '../../../../store/wallet';
import styles from './wallet-review-transfer.module.scss';
import { SendHeader } from '../components/send-header';
import { amountSelector, recipientSelector, tokenSelector } from '../../../../store/wallet/selectors';
import { amountSelector, recipientSelector, tokenSelector, nftSelector } from '../../../../store/wallet/selectors';
import { MatrixAvatar } from '../../../../components/matrix-avatar';
import { TokenIcon } from '../../components/token-icon/token-icon';
import { FormattedNumber } from '../../components/formatted-number/formatted-number';
import { Button } from '../../components/button/button';
import { IconChevronRightDouble } from '@zero-tech/zui/icons';
import { IconChevronRightDouble, IconPackageMinus } from '@zero-tech/zui/icons';
import { truncateAddress } from '../../utils/address';

export const WalletReviewTransfer = () => {
const dispatch = useDispatch();
const recipient = useSelector(recipientSelector);
const token = useSelector(tokenSelector);
const amount = useSelector(amountSelector);
const nft = useSelector(nftSelector);

const isNftTransfer = nft !== null;

const handleBack = () => {
dispatch(previousStage());
};

const handleConfirm = () => {
dispatch(transferToken());
if (isNftTransfer) {
dispatch(transferNft());
} else {
dispatch(transferToken());
}
};

return (
Expand All @@ -32,30 +40,51 @@ export const WalletReviewTransfer = () => {
<div className={styles.confirmRecipientTitle}>Confirm transaction with</div>
<MatrixAvatar className={styles.recipientAvatar} imageURL={recipient?.profileImage} size='regular' />
<div className={styles.recipientName}>{recipient?.primaryZid || recipient?.name}</div>
<div className={styles.recipientAddress}>{recipient?.publicAddress}</div>
<div className={styles.recipientAddress}>
{recipient?.publicAddress ? truncateAddress(recipient.publicAddress) : ''}
</div>
</div>

<div className={styles.transferDetails}>
<div className={styles.tokenInfo}>
<TokenIcon url={token.logo} name={token.name} chainId={token.chainId} />
<div className={styles.tokenName}>{token.name}</div>
<div className={styles.tokenAmount}>
<FormattedNumber value={amount} />
{isNftTransfer ? (
<div className={styles.nftTransferDetails}>
<div className={styles.nftPreview}>
{nft.imageUrl ? (
<img src={nft.imageUrl} alt={nft.metadata?.name || 'NFT'} className={styles.nftImage} />
) : (
<div className={styles.nftImagePlaceholder}>
<IconPackageMinus size={48} />
</div>
)}
</div>
<div className={styles.nftInfo}>
<div className={styles.nftName}>{nft.metadata?.name || 'Unnamed NFT'}</div>
<div className={styles.nftCollection}>{nft.collectionName}</div>
<div className={styles.nftTokenId}>Token ID: {nft.id}</div>
</div>
</div>
) : (
<div className={styles.transferDetails}>
<div className={styles.tokenInfo}>
<TokenIcon url={token?.logo} name={token?.name} chainId={token?.chainId} />
<div className={styles.tokenName}>{token?.name}</div>
<div className={styles.tokenAmount}>
<FormattedNumber value={amount} />
</div>
</div>

<div className={styles.tokenInfoSeparator}>
<IconChevronRightDouble />
</div>
<div className={styles.tokenInfoSeparator}>
<IconChevronRightDouble />
</div>

<div className={styles.tokenInfo}>
<TokenIcon url={token.logo} name={token.name} chainId={token.chainId} />
<div className={styles.tokenName}>{token.name}</div>
<div className={styles.tokenAmount}>
<FormattedNumber value={amount} />
<div className={styles.tokenInfo}>
<TokenIcon url={token?.logo} name={token?.name} chainId={token?.chainId} />
<div className={styles.tokenName}>{token?.name}</div>
<div className={styles.tokenAmount}>
<FormattedNumber value={amount} />
</div>
</div>
</div>
</div>
)}
</div>

<div className={styles.confirmButton}>
Expand Down
61 changes: 61 additions & 0 deletions src/apps/wallet/send/success/wallet-transfer-success.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,64 @@
font-weight: 400;
color: var(--text-secondary);
}

.nftHero {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 30vh;
}

.nftSuccessImageContainer {
display: grid;
place-items: center;
margin-bottom: 16px;

& > * {
grid-column: 1;
grid-row: 1;
}
}

.nftSuccessGlow {
background-color: rgb(172, 253, 90, 0.15);
filter: blur(20px);
border-radius: 12px;
width: 140px;
height: 140px;
}

.nftSuccessImage {
width: 120px;
height: 120px;
object-fit: cover;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}

.nftSuccessImagePlaceholder {
width: 120px;
height: 120px;
background: var(--bg-secondary);
border-radius: 8px;
display: grid;
place-items: center;
color: var(--text-secondary);
}

.nftSuccessName {
color: #fff;
filter: drop-shadow(0px 0px 4px rgba(255, 255, 255, 0.4));
font-family: IBM Plex Sans;
font-weight: 600;
font-size: 24px;
text-align: center;
}

.nftSuccessCollection {
font-size: 16px;
color: var(--text-secondary);
font-weight: 400;
margin-top: 4px;
}
45 changes: 33 additions & 12 deletions src/apps/wallet/send/success/wallet-transfer-success.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IconButton } from '@zero-tech/zui/components';
import { SendHeader } from '../components/send-header';
import styles from './wallet-transfer-success.module.scss';
import { IconCheck, IconChevronRightDouble, IconXClose } from '@zero-tech/zui/icons';
import { IconCheck, IconChevronRightDouble, IconXClose, IconPackageMinus } from '@zero-tech/zui/icons';
import { useDispatch, useSelector } from 'react-redux';
import { reset } from '../../../../store/wallet';
import { getHistory } from '../../../../lib/browser';
Expand All @@ -11,6 +11,7 @@ import {
recipientSelector,
selectedWalletSelector,
tokenSelector,
nftSelector,
txReceiptSelector,
} from '../../../../store/wallet/selectors';
import { FormattedNumber } from '../../components/formatted-number/formatted-number';
Expand All @@ -31,6 +32,9 @@ export const WalletTransferSuccess = () => {
const currentUser = useSelector(currentUserSelector);
const selectedWallet = useSelector(selectedWalletSelector);
const txReceipt = useSelector(txReceiptSelector);
const nft = useSelector(nftSelector);

const isNftTransfer = nft !== null;

const handleClose = () => {
dispatch(reset());
Expand All @@ -42,25 +46,42 @@ export const WalletTransferSuccess = () => {
};

const dollarAmount = useMemo(() => {
if (!token.price) return '--';
if (!token?.price) return '--';
return formatDollars(Number(amount) * Number(token.price));
}, [token.price, amount]);
}, [token?.price, amount]);

return (
<div className={styles.container}>
<SendHeader title='Sent' action={<IconButton Icon={IconXClose} onClick={handleClose} />} />
<div className={styles.content}>
<div className={styles.tokenHero}>
<div className={styles.tokenIconContainer}>
<div className={styles.tokenIconBackground} />
<div className={styles.tokenIconHighlight} />
<TokenIcon className={styles.tokenIcon} url={token.logo} name={token.name} chainId={token.chainId} />
{isNftTransfer ? (
<div className={styles.nftHero}>
<div className={styles.nftSuccessImageContainer}>
<div className={styles.nftSuccessGlow} />
{nft.imageUrl ? (
<img src={nft.imageUrl} alt={nft.metadata?.name || 'NFT'} className={styles.nftSuccessImage} />
) : (
<div className={styles.nftSuccessImagePlaceholder}>
<IconPackageMinus size={48} />
</div>
)}
</div>
<div className={styles.nftSuccessName}>{nft.metadata?.name || 'NFT'}</div>
<div className={styles.nftSuccessCollection}>{nft.collectionName}</div>
</div>
<div className={styles.dollarAmount}>{dollarAmount}</div>
<div className={styles.amount}>
<FormattedNumber value={amount} /> <span className={styles.tokenSymbol}>{token.symbol}</span>
) : (
<div className={styles.tokenHero}>
<div className={styles.tokenIconContainer}>
<div className={styles.tokenIconBackground} />
<div className={styles.tokenIconHighlight} />
<TokenIcon className={styles.tokenIcon} url={token?.logo} name={token?.name} chainId={token?.chainId} />
</div>
<div className={styles.dollarAmount}>{dollarAmount}</div>
<div className={styles.amount}>
<FormattedNumber value={amount} /> <span className={styles.tokenSymbol}>{token?.symbol}</span>
</div>
</div>
</div>
)}

<div className={styles.transferDetails}>
<div className={styles.recipientInfo}>
Expand Down
Loading