()
| 33 | }; |
| 34 | |
| 35 | const LNDViewInvoice = () => { |
| 36 | const { invoice, walletID } = useRoute<RouteProp<{ params: LNDViewInvoiceRouteParams }, 'params'>>().params; |
| 37 | const { wallets, fetchAndSaveWalletTransactions } = useStorage(); |
| 38 | const { colors, closeImage } = useTheme(); |
| 39 | const { direction } = useLocale(); |
| 40 | const { goBack, navigate, setParams, setOptions } = useExtendedNavigation(); |
| 41 | const navigation = useNavigation(); |
| 42 | |
| 43 | const wallet = wallets.find(w => w.getID() === walletID) as LightningCustodianWallet | undefined; |
| 44 | const arkWallet = |
| 45 | wallet && (wallet as { type?: string }).type === LightningArkWallet.type ? (wallet as unknown as LightningArkWallet) : undefined; |
| 46 | const [isFetchingInvoices, setIsFetchingInvoices] = useState<boolean>(true); |
| 47 | const [invoiceStatusChanged, setInvoiceStatusChanged] = useState<boolean>(false); |
| 48 | const [qrCodeSize, setQRCodeSize] = useState<number>(90); |
| 49 | const fetchInvoiceInterval = useRef<any>(null); |
| 50 | const isModal = useNavigationState(state => state.routeNames[0] === LNDCreateInvoice.routeName); |
| 51 | |
| 52 | // Per-swap claim/refund lookup, by the `swap-${id}` prefix mapped onto |
| 53 | // the row's `txid` field by lightning-ark-wallet getTransactions(). The |
| 54 | // route param is typed as LightningTransaction (which doesn't declare |
| 55 | // txid) but at runtime carries the merged `Transaction & LightningTransaction` |
| 56 | // shape, so we read txid through a narrow local cast. For non-Ark wallets |
| 57 | // and non-swap rows this resolves to undefined and the UI falls through |
| 58 | // to the existing branches. |
| 59 | const invoiceTxid = typeof invoice === 'object' ? (invoice as { txid?: unknown }).txid : undefined; |
| 60 | const swapId = typeof invoiceTxid === 'string' && invoiceTxid.startsWith('swap-') ? invoiceTxid.slice('swap-'.length) : undefined; |
| 61 | // Force-render token: bumped by the swap-event subscription below so live |
| 62 | // `swap.status` lookups (via getSwapById → _swapHistory) re-evaluate the |
| 63 | // moment the SDK observes a status transition, without waiting for the |
| 64 | // 3s polling tick to update the route-param snapshot. |
| 65 | const [, forceRender] = useReducer((x: number) => x + 1, 0); |
| 66 | const swap = swapId && arkWallet ? arkWallet.getSwapById(swapId) : undefined; |
| 67 | const [isActioning, setIsActioning] = useState<boolean>(false); |
| 68 | const claimable = arkWallet && swap ? arkWallet.isSwapClaimable(swap) : false; |
| 69 | const refundable = arkWallet && swap ? arkWallet.isSwapRefundable(swap) : false; |
| 70 | |
| 71 | // Subscribe to SwapManager status transitions for our swap so the spinner |
| 72 | // → success transition is driven by SDK events, not the 3s polling lag. |
| 73 | // The SDK mutates `swap.status` in place before invoking listeners, so by |
| 74 | // the time we force a render `getSwapById(swapId).status` reflects the |
| 75 | // new state and the success/refund branches re-evaluate correctly. |
| 76 | useEffect(() => { |
| 77 | if (!arkWallet || !swapId) return; |
| 78 | return arkWallet.subscribeToSwapEvents(updatedSwap => { |
| 79 | if (updatedSwap.id === swapId) forceRender(); |
| 80 | }); |
| 81 | }, [arkWallet, swapId]); |
| 82 | |
| 83 | const refreshAfterAction = async () => { |
| 84 | if (!arkWallet || !swapId) return; |
| 85 | const updatedRow = arkWallet.getTransactions().find(tx => tx.txid === `swap-${swapId}`); |
| 86 | if (updatedRow) setParams({ invoice: updatedRow }); |
| 87 | setInvoiceStatusChanged(true); |
| 88 | fetchAndSaveWalletTransactions(walletID); |
| 89 | }; |
| 90 | |
| 91 | const onRefundPressed = async () => { |
| 92 | if (!arkWallet || !swap || isActioning) return; |
nothing calls this directly
no test coverage detected