From 21e060b96743c6e58b7aea26bea10e7927529687 Mon Sep 17 00:00:00 2001 From: notTanveer Date: Sun, 19 Jul 2026 19:14:46 +0530 Subject: [PATCH 1/2] DEL: removed TransactionStatus screen --- loc/en.json | 13 - navigation/DetailViewScreensStack.tsx | 20 - navigation/DetailViewStackParamList.ts | 2 +- screen/transactions/TransactionStatus.tsx | 696 ---------------------- 4 files changed, 1 insertion(+), 730 deletions(-) delete mode 100644 screen/transactions/TransactionStatus.tsx diff --git a/loc/en.json b/loc/en.json index 73f400056..27c8d115f 100644 --- a/loc/en.json +++ b/loc/en.json @@ -231,9 +231,6 @@ "cancel_explain": "We will replace this transaction with one that pays you and has higher fees. This effectively cancels the current transaction. This is called RBF—Replace by Fee.", "cancel_no": "This transaction is not replaceable.", "cancel_title": "Cancel this transaction (RBF)", - "transaction_loading_error": "There was an issue loading the transaction. Please try again later.", - "transaction_not_available": "Transaction not available", - "confirmations_lowercase": "{confirmations} confirmations", "expand_note": "Expand Note", "cpfp_create": "Create", "cpfp_exp": "We will create another transaction that spends your unconfirmed transaction. The total fee will be higher than the original transaction fee, so it should be mined faster. This is called CPFP—Child Pays for Parent.", @@ -243,12 +240,7 @@ "details_copy": "Copy", "details_copy_note": "Copy Note", "details_copy_txid": "Copy Transaction ID", - "details_from": "Input", - "details_inputs": "Inputs", - "details_outputs": "Outputs", - "details_received": "Received", "details_title": "Transaction", - "details_to": "Output", "list_conf": "Conf: {number}", "pending": "Pending", "pending_with_amount": "Pending {amt1} ({amt2})", @@ -256,14 +248,9 @@ "eta_10m": "ETA: In ~10 minutes", "eta_3h": "ETA: In ~3 hours", "eta_1d": "ETA: In ~1 day", - "view_wallet": "View {walletLabel}", "list_title": "Transactions", - "transaction": "Transaction", "rbf_explain": "We will replace this transaction with one with a higher fee so that it will be mined faster. This is called RBF—Replace by Fee.", "rbf_title": "Bump Fee (RBF)", - "status_bump": "Bump Fee", - "status_cancel": "Cancel Transaction", - "txid": "Transaction ID", "updating": "Updating..." }, "wallets": { diff --git a/navigation/DetailViewScreensStack.tsx b/navigation/DetailViewScreensStack.tsx index ddbbbbeee..c0a64e6c2 100644 --- a/navigation/DetailViewScreensStack.tsx +++ b/navigation/DetailViewScreensStack.tsx @@ -1,6 +1,5 @@ import React, { useMemo } from 'react'; import { NativeStackNavigationOptions } from '@react-navigation/native-stack'; -import HeaderRightButton from '../components/HeaderRightButton'; import navigationStyle, { CloseButtonPosition } from '../components/navigationStyle'; import { useTheme } from '../components/themes'; import loc from '../loc'; @@ -10,7 +9,6 @@ import CPFP from '../screen/transactions/CPFP'; import TransactionDetails from '../screen/transactions/TransactionDetails'; import RBFBumpFee from '../screen/transactions/RBFBumpFee'; import RBFCancel from '../screen/transactions/RBFCancel'; -import TransactionStatus from '../screen/transactions/TransactionStatus'; import WalletsList from '../screen/wallets/WalletsList'; import { DetailViewStack } from './index'; import SettingsButton from '../components/icons/SettingsButton'; @@ -36,7 +34,6 @@ import OnboardingStack from './OnboardingStack'; const DetailViewStackScreensStack = () => { const theme = useTheme(); const { sizeClass } = useSizeClass(); - const DetailButton = useMemo(() => , []); const RightBarButtons = useMemo(() => , []); const walletListScreenOptions = useMemo(() => { @@ -80,23 +77,6 @@ const DetailViewStackScreensStack = () => { headerTitle: loc.transactions.details_title, })(theme)} /> - DetailButton, - headerBackButtonDisplayMode: 'default', - })(theme)} - /> ; -type NavigationProps = NativeStackNavigationProp; - -enum ActionType { - SetCPFPPossible, - SetRBFBumpFeePossible, - SetRBFCancelPossible, - SetTransaction, - SetLoading, - SetEta, - SetIntervalMs, - SetAllButtonStatus, - SetWallet, - SetLoadingError, -} - -interface State { - isCPFPPossible: ButtonStatus; - isRBFBumpFeePossible: ButtonStatus; - isRBFCancelPossible: ButtonStatus; - tx: any; - isLoading: boolean; - eta: string; - intervalMs: number; - wallet: TWallet | null; - loadingError: boolean; -} - -const initialState: State = { - isCPFPPossible: ButtonStatus.Unknown, - isRBFBumpFeePossible: ButtonStatus.Unknown, - isRBFCancelPossible: ButtonStatus.Unknown, - tx: undefined, - isLoading: true, - eta: '', - intervalMs: 1000, - wallet: null, - loadingError: false, -}; - -const reducer = (state: State, action: { type: ActionType; payload?: any }): State => { - switch (action.type) { - case ActionType.SetCPFPPossible: - return { ...state, isCPFPPossible: action.payload }; - case ActionType.SetRBFBumpFeePossible: - return { ...state, isRBFBumpFeePossible: action.payload }; - case ActionType.SetRBFCancelPossible: - return { ...state, isRBFCancelPossible: action.payload }; - case ActionType.SetTransaction: - return { ...state, tx: action.payload }; - case ActionType.SetLoading: - return { ...state, isLoading: action.payload }; - case ActionType.SetEta: - return { ...state, eta: action.payload }; - case ActionType.SetIntervalMs: - return { ...state, intervalMs: action.payload }; - case ActionType.SetAllButtonStatus: - return { ...state, isCPFPPossible: action.payload, isRBFBumpFeePossible: action.payload, isRBFCancelPossible: action.payload }; - case ActionType.SetWallet: - return { ...state, wallet: action.payload }; - case ActionType.SetLoadingError: - return { ...state, loadingError: action.payload }; - default: - return state; - } -}; - -type TransactionStatusProps = { - transaction?: { - amount?: number; - value?: number; - confirmations?: number; - }; - txid?: string; -}; - -const TransactionStatus: React.FC = ({ transaction, txid }) => { - const [state, dispatch] = useReducer(reducer, initialState); - const { isCPFPPossible, isRBFBumpFeePossible, isRBFCancelPossible, tx, isLoading, eta, intervalMs, wallet, loadingError } = state; - const { wallets, txMetadata, fetchAndSaveWalletTransactions } = useStorage(); - const { hash, walletID } = useRoute().params; - const { navigate, setOptions, goBack } = useExtendedNavigation(); - const { colors } = useTheme(); - const fetchTxInterval = useRef(); - const stylesHook = StyleSheet.create({ - value: { - color: colors.alternativeTextColor2, - }, - valueUnit: { - color: colors.alternativeTextColor2, - }, - iconRoot: { - backgroundColor: colors.success, - }, - }); - - const setTX = (value: any) => { - dispatch({ type: ActionType.SetTransaction, payload: value }); - }; - - const setIntervalMs = (ms: number) => { - dispatch({ type: ActionType.SetIntervalMs, payload: ms }); - }; - - const setEta = (value: string) => { - dispatch({ type: ActionType.SetEta, payload: value }); - }; - - const setAllButtonStatus = (status: ButtonStatus) => { - dispatch({ type: ActionType.SetAllButtonStatus, payload: status }); - }; - - const setIsLoading = (value: boolean) => { - dispatch({ type: ActionType.SetLoading, payload: value }); - }; - - const setIsCPFPPossible = (status: ButtonStatus) => { - dispatch({ type: ActionType.SetCPFPPossible, payload: status }); - }; - - const setIsRBFBumpFeePossible = (status: ButtonStatus) => { - dispatch({ type: ActionType.SetRBFBumpFeePossible, payload: status }); - }; - - const setIsRBFCancelPossible = (status: ButtonStatus) => { - dispatch({ type: ActionType.SetRBFCancelPossible, payload: status }); - }; - - const navigateToTransactionDetails = useCallback(() => { - if (walletID && tx && tx.hash) { - navigate('TransactionDetails', { tx, hash, walletID }); - } else { - console.error('Cannot navigate to TransactionDetails: Missing tx or hash.'); - } - }, [hash, navigate, tx, walletID]); - - const DetailsButton = useMemo( - () => ( - - ), - [navigateToTransactionDetails, loadingError, isLoading, tx, wallet], - ); - - useEffect(() => { - setOptions({ - headerRight: () => DetailsButton, - }); - }, [DetailsButton, colors, hash, setOptions]); - - useEffect(() => { - if (wallet) { - const transactions = wallet.getTransactions(); - const newTx = transactions.find((t: Transaction) => t.hash === hash); - if (newTx) { - setTX(newTx); - } - } - }, [hash, wallet]); - - useEffect(() => { - const foundWallet = wallets.find(w => w.getID() === walletID) || null; - dispatch({ type: ActionType.SetWallet, payload: foundWallet }); - }, [walletID, wallets]); - - // re-fetching tx status periodically - useEffect(() => { - console.debug('transactionStatus - useEffect'); - - if (!tx || tx?.confirmations) return; - if (!hash) return; - - if (fetchTxInterval.current) { - // interval already exists, lets cleanup it and recreate, so theres no duplicate intervals - clearInterval(fetchTxInterval.current); - fetchTxInterval.current = undefined; - } - - console.debug('setting up interval to check tx...'); - fetchTxInterval.current = setInterval(async () => { - try { - setIntervalMs(31000); // upon first execution we increase poll interval; - - console.debug('checking tx', hash, 'for confirmations...'); - const transactions = await Electrum.multiGetTransactionByTxid([hash], true, 10); - const txFromElectrum = transactions[hash]; - if (!txFromElectrum) { - console.error(`Transaction from Electrum with hash ${hash} not found.`); - return; - } - - console.debug('got txFromElectrum=', txFromElectrum); - - const address = txFromElectrum.vout?.[0]?.scriptPubKey?.addresses?.pop(); - if (!address) { - console.error('Address not found in txFromElectrum.'); - return; - } - - if (!txFromElectrum.confirmations && txFromElectrum.vsize) { - const txsM = await Electrum.getMempoolTransactionsByAddress(address); - let txFromMempool; - // searching for a correct tx in case this address has several pending txs: - for (const tempTxM of txsM) { - if (tempTxM?.tx_hash === hash) { - txFromMempool = tempTxM; - break; - } - } - if (!txFromMempool) { - console.error(`Transaction from mempool with hash ${hash} not found.`); - return; - } - - console.debug('txFromMempool=', txFromMempool); - - const satPerVbyte = txFromMempool.fee && txFromElectrum.vsize ? Math.round(txFromMempool.fee / txFromElectrum.vsize) : 0; - const fees = await Electrum.estimateFees(); - console.debug('fees=', fees, 'satPerVbyte=', satPerVbyte); - if (satPerVbyte >= fees.fast) { - setEta(loc.formatString(loc.transactions.eta_10m)); - } - if (satPerVbyte >= fees.medium && satPerVbyte < fees.fast) { - setEta(loc.formatString(loc.transactions.eta_3h)); - } - if (satPerVbyte < fees.medium) { - setEta(loc.formatString(loc.transactions.eta_1d)); - } - } else if (txFromElectrum.confirmations && txFromElectrum.confirmations > 0) { - // now, handling a case when tx became confirmed! - triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess); - setEta(''); - if (tx) { - setTX((prevState: any) => { - return Object.assign({}, prevState, { confirmations: txFromElectrum.confirmations }); - }); - } else { - console.error('Cannot set confirmations: tx is undefined.'); - } - clearInterval(fetchTxInterval.current); - fetchTxInterval.current = undefined; - if (wallet?.getID()) { - fetchAndSaveWalletTransactions(wallet.getID()); - } else { - console.error('Cannot fetch and save wallet transactions: wallet ID is undefined.'); - } - } - } catch (error) { - console.error('Error in fetchTxInterval:', error); - } - }, intervalMs); - - return () => { - clearInterval(fetchTxInterval.current); - fetchTxInterval.current = undefined; - }; - }, [hash, intervalMs, tx, fetchAndSaveWalletTransactions, wallet]); - - useEffect(() => { - const subscription = BackHandler.addEventListener('hardwareBackPress', () => { - goBack(); - return true; - }); - - return () => { - subscription.remove(); - clearInterval(fetchTxInterval.current); - fetchTxInterval.current = undefined; - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const initialButtonsState = async () => { - try { - await checkPossibilityOfCPFP(); - await checkPossibilityOfRBFBumpFee(); - await checkPossibilityOfRBFCancel(); - } catch (e) { - console.error('Error in initialButtonsState:', e); - setAllButtonStatus(ButtonStatus.NotPossible); - } - setIsLoading(false); - }; - - useEffect(() => { - initialButtonsState().catch(error => console.error('Unhandled error in initialButtonsState:', error)); - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [tx, wallets]); - - useEffect(() => {}, [tx, wallets]); - - useEffect(() => { - console.debug('transactionStatus - useEffect'); - }, []); - - const checkPossibilityOfCPFP = async () => { - if (!wallet?.allowRBF()) { - return setIsCPFPPossible(ButtonStatus.NotPossible); - } - - if (wallet) { - const cpfbTx = new HDSegwitBech32Transaction(null, tx.hash, wallet as HDSilentPaymentsWallet); - if ((await cpfbTx.isToUsTransaction()) && (await cpfbTx.getRemoteConfirmationsNum()) === 0) { - return setIsCPFPPossible(ButtonStatus.Possible); - } else { - return setIsCPFPPossible(ButtonStatus.NotPossible); - } - } - return setIsCPFPPossible(ButtonStatus.NotPossible); - }; - - const checkPossibilityOfRBFBumpFee = async () => { - if (!wallet?.allowRBF()) { - return setIsRBFBumpFeePossible(ButtonStatus.NotPossible); - } - - const rbfTx = new HDSegwitBech32Transaction(null, tx.hash, wallet as HDSilentPaymentsWallet); - if ( - (await rbfTx.isOurTransaction()) && - (await rbfTx.getRemoteConfirmationsNum()) === 0 && - (await rbfTx.isSequenceReplaceable()) && - (await rbfTx.canBumpTx()) - ) { - return setIsRBFBumpFeePossible(ButtonStatus.Possible); - } else { - return setIsRBFBumpFeePossible(ButtonStatus.NotPossible); - } - }; - - const checkPossibilityOfRBFCancel = async () => { - if (!wallet?.allowRBF()) { - return setIsRBFCancelPossible(ButtonStatus.NotPossible); - } - - const rbfTx = new HDSegwitBech32Transaction(null, tx.hash, wallet as HDSilentPaymentsWallet); - if ( - (await rbfTx.isOurTransaction()) && - (await rbfTx.getRemoteConfirmationsNum()) === 0 && - (await rbfTx.isSequenceReplaceable()) && - (await rbfTx.canCancelTx()) - ) { - return setIsRBFCancelPossible(ButtonStatus.Possible); - } else { - return setIsRBFCancelPossible(ButtonStatus.NotPossible); - } - }; - - const navigateToRBFBumpFee = () => { - navigate('RBFBumpFee', { - txid: tx.hash, - wallet, - }); - }; - - const navigateToRBFCancel = () => { - navigate('RBFCancel', { - txid: tx.hash, - wallet, - }); - }; - - const navigateToCPFP = () => { - navigate('CPFP', { - txid: tx.hash, - wallet, - }); - }; - - const renderCPFP = () => { - if (isCPFPPossible === ButtonStatus.Unknown) { - return ( - <> - - - - ); - } else if (isCPFPPossible === ButtonStatus.Possible) { - return ( - <> -