From 8592508ed7ccc168ad751189c9acd87670ea0699 Mon Sep 17 00:00:00 2001 From: Maphikza Date: Tue, 3 Sep 2024 17:02:32 +0200 Subject: [PATCH] Adding replacement transactions --- .../Balance/components/SendForm/SendForm.tsx | 2 + .../UnconfirmedTransactions.styles.ts | 7 + .../UnconfirmedTransactions.tsx | 56 +++++-- .../components/Modal/UnconfirmedTxModal.tsx | 8 +- .../ReplaceTransaction/ReplaceTransaction.tsx | 140 +++++++++++++----- src/hooks/usePendingTransactions.ts | 19 ++- 6 files changed, 167 insertions(+), 65 deletions(-) diff --git a/src/components/nft-dashboard/Balance/components/SendForm/SendForm.tsx b/src/components/nft-dashboard/Balance/components/SendForm/SendForm.tsx index 7c89f57..d7b54b3 100644 --- a/src/components/nft-dashboard/Balance/components/SendForm/SendForm.tsx +++ b/src/components/nft-dashboard/Balance/components/SendForm/SendForm.tsx @@ -19,6 +19,7 @@ interface PendingTransaction { txid: string; feeRate: number; timestamp: string; // ISO format string + amount: string; } export type tiers = 'low' | 'med' | 'high'; @@ -102,6 +103,7 @@ const SendForm: React.FC = ({ onSend }) => { txid: result.txid, feeRate: selectedFee, timestamp: new Date().toISOString(), // Capture the current time in ISO format + amount: formData.amount }; // Send the transaction details to the pending-transactions endpoint diff --git a/src/components/nft-dashboard/unconfirmed-transactions/UnconfirmedTransactions.styles.ts b/src/components/nft-dashboard/unconfirmed-transactions/UnconfirmedTransactions.styles.ts index d87d73a..0b215e5 100644 --- a/src/components/nft-dashboard/unconfirmed-transactions/UnconfirmedTransactions.styles.ts +++ b/src/components/nft-dashboard/unconfirmed-transactions/UnconfirmedTransactions.styles.ts @@ -22,6 +22,13 @@ export const TransactionWrapper = styled.div` `; export const ButtonWrapper = styled.div` disply: flex; + margin: .5rem; +`; + +export const NoTransactionsText = styled.p` + text-align: center; + color: #888; + font-size: 16px; `; export const RowWrapper = styled.div` diff --git a/src/components/nft-dashboard/unconfirmed-transactions/UnconfirmedTransactions.tsx b/src/components/nft-dashboard/unconfirmed-transactions/UnconfirmedTransactions.tsx index 338cd64..6fde9f3 100644 --- a/src/components/nft-dashboard/unconfirmed-transactions/UnconfirmedTransactions.tsx +++ b/src/components/nft-dashboard/unconfirmed-transactions/UnconfirmedTransactions.tsx @@ -3,40 +3,60 @@ import * as S from './UnconfirmedTransactions.styles'; import UnconfirmedTransaction from './components/UnconfirmedTransaction/UnconfirmedTransaction'; import ReplaceTransaction from './components/ReplaceTransaction/ReplaceTransaction'; import { BaseButton } from '@app/components/common/BaseButton/BaseButton'; -interface UnconfirmedTransactionsProps { - transactions: any[]; //TODO: update the type -} +import usePendingTransactions, { PendingTransaction } from '@app/hooks/usePendingTransactions'; -const UnconfirmedTransactions: React.FC = ({}) => { +const UnconfirmedTransactions: React.FC = () => { const [isReplacingTransaction, setIsReplacingTransaction] = useState(false); + const [selectedTransaction, setSelectedTransaction] = useState(null); + const { pendingTransactions } = usePendingTransactions(); - const handleOpenReplaceTransaction = () => { + const handleOpenReplaceTransaction = (transaction: PendingTransaction) => { + setSelectedTransaction(transaction); setIsReplacingTransaction(true); }; + const handleCancelReplaceTransaction = () => { + setSelectedTransaction(null); setIsReplacingTransaction(false); }; - const onReplaceTransaction = () => {}; //define any behavior after replacing transaction + const onReplaceTransaction = () => { + // Define any behavior after replacing a transaction + }; + return ( - {isReplacingTransaction ? ( + {isReplacingTransaction && selectedTransaction ? ( <> Replace Transaction - + ) : ( <> Unconfirmed Transactions - - - - - - Replace - - + {pendingTransactions.length === 0 ? ( + No unconfirmed transactions available. + ) : ( + pendingTransactions.map((transaction) => ( + + + + + + handleOpenReplaceTransaction(transaction)}>Replace + + + )) + )} )} @@ -45,3 +65,7 @@ const UnconfirmedTransactions: React.FC = ({}) => }; export default UnconfirmedTransactions; + + + + diff --git a/src/components/nft-dashboard/unconfirmed-transactions/components/Modal/UnconfirmedTxModal.tsx b/src/components/nft-dashboard/unconfirmed-transactions/components/Modal/UnconfirmedTxModal.tsx index 51cc485..29be838 100644 --- a/src/components/nft-dashboard/unconfirmed-transactions/components/Modal/UnconfirmedTxModal.tsx +++ b/src/components/nft-dashboard/unconfirmed-transactions/components/Modal/UnconfirmedTxModal.tsx @@ -1,19 +1,19 @@ import React from 'react'; import * as S from './UnconfirmedTxModal.styles'; import UnconfirmedTransactions from '../../UnconfirmedTransactions'; + interface UnconfirmedTxModalProps { isOpen: boolean; onOpenChange: () => void; } -const transactions:any = [ -] const UnconfirmedTxModal: React.FC = ({ isOpen, onOpenChange }) => { return ( - - + + ); }; export default UnconfirmedTxModal; + diff --git a/src/components/nft-dashboard/unconfirmed-transactions/components/ReplaceTransaction/ReplaceTransaction.tsx b/src/components/nft-dashboard/unconfirmed-transactions/components/ReplaceTransaction/ReplaceTransaction.tsx index 11245ab..bb24cba 100644 --- a/src/components/nft-dashboard/unconfirmed-transactions/components/ReplaceTransaction/ReplaceTransaction.tsx +++ b/src/components/nft-dashboard/unconfirmed-transactions/components/ReplaceTransaction/ReplaceTransaction.tsx @@ -2,54 +2,118 @@ import React, { useState } from 'react'; import * as S from './ReplaceTransaction.styles'; import { useResponsive } from '@app/hooks/useResponsive'; import TieredFees from '@app/components/nft-dashboard/Balance/components/SendForm/components/TieredFees/TieredFees'; +import { PendingTransaction } from '@app/hooks/usePendingTransactions'; +import { BaseSpin } from '@app/components/common/BaseSpin/BaseSpin'; +import ResultScreen from '@app/components/nft-dashboard/Balance/components/SendForm/components/ResultScreen/ResultScreen'; +import config from '@app/config/config'; + interface ReplaceTransactionProps { onCancel: () => void; - onReplace: () => void; + onReplace: () => void; + transaction: PendingTransaction; } -const ReplaceTransaction: React.FC = ({ onCancel, onReplace }) => { +const ReplaceTransaction: React.FC = ({ onCancel, onReplace, transaction }) => { const { isDesktop, isTablet } = useResponsive(); - const [inValidAmount, setInvalidAmount] = React.useState(false); + const [inValidAmount, setInvalidAmount] = useState(false); + const [newFee, setNewFee] = useState(transaction.FeeRate); + const [loading, setLoading] = useState(false); // Add loading state + const [isFinished, setIsFinished] = useState(false); // Add finished state + const [result, setResult] = useState<{ isSuccess: boolean; message: string; txid: string }>({ + isSuccess: false, + message: '', + txid: '', + }); + + const handleFeeChange = (fee: number) => { + setNewFee(fee); // Update the new fee when it changes + }; + + const handleReplace = async (e: React.MouseEvent) => { + e.stopPropagation(); + setLoading(true); // Start loading + console.log('Replace button clicked'); - const handleFeeChange = (fee: number) => {}; //use this for calculating new total + try { + const replaceRequest = { + choice: 2, // Replace transaction option + original_tx_id: transaction.TxID, // Send the original transaction ID + new_fee_rate: newFee, // Send the updated fee rate + }; - const handleReplace = () => { - //function to replace transaction - onReplace(); + const response = await fetch('http://localhost:9003/transaction', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(replaceRequest), + }); + + const result = await response.json(); + setLoading(false); // Stop loading + + if (result.status === 'success') { + setResult({ isSuccess: true, message: result.message, txid: result.txid }); + setIsFinished(true); + onReplace(); // Notify the parent that the replace was successful + } else { + setResult({ isSuccess: false, message: result.message, txid: '' }); + setIsFinished(true); + } + } catch (error) { + console.error('RBF transaction failed:', error); + setLoading(false); // Stop loading + setResult({ isSuccess: false, message: 'RBF transaction failed due to a network error.', txid: '' }); + setIsFinished(true); + } }; + if (isFinished) { + return ( + + ); + } + return ( - - - Transaction ID - - 123456 - - - - Amount - - 123456 - - - - - New Fee - - 123456 - - - - Total - - 123456 - - - - Cancel - Replace - - + + + + Transaction ID + + {transaction.TxID} + + + + Amount + + {transaction.Amount} + + + + + New Fee + + {newFee} + + + + Total + + {Number(transaction.Amount) + newFee} + + + + Cancel + Replace + + + ); }; diff --git a/src/hooks/usePendingTransactions.ts b/src/hooks/usePendingTransactions.ts index 99dac64..5000cc9 100644 --- a/src/hooks/usePendingTransactions.ts +++ b/src/hooks/usePendingTransactions.ts @@ -1,11 +1,12 @@ import { useState, useEffect } from 'react'; import config from '@app/config/config'; -interface PendingTransaction { - txid: string; - feeRate: number; - timestamp: string; -} +export interface PendingTransaction { + TxID: string; + FeeRate: number; + Timestamp: string; + Amount: number; + } const usePendingTransactions = () => { const [pendingTransactions, setPendingTransactions] = useState([]); @@ -24,10 +25,12 @@ const usePendingTransactions = () => { if (!response.ok) { throw new Error(`Network response was not ok (status: ${response.status})`); } - const data: PendingTransaction[] = await response.json(); - setPendingTransactions(data); + const data: PendingTransaction[] | null = await response.json(); + console.log('Fetched Pending Transactions:', data); + setPendingTransactions(data || []); // Ensuring it is always an array } catch (error) { console.error('Error fetching pending transactions:', error); + setPendingTransactions([]); // Setting an empty array on error } finally { setIsLoading(false); } @@ -40,3 +43,5 @@ const usePendingTransactions = () => { }; export default usePendingTransactions; + +