Massive changes. I dont know what happens. Rewrote the action wizard again. Fixed several bugs related to the utxo selection. QR codes were added for address. Add support for data results. Experiment with other methods of role extraction

This commit is contained in:
2026-03-22 13:20:46 +00:00
parent be52f73e64
commit a28d43a68b
35 changed files with 2226 additions and 1169 deletions

View File

@@ -10,12 +10,14 @@
import React, { useState, useEffect, useCallback, useMemo } from 'react';
import { Box, Text, useInput } from 'ink';
import { ScrollableList, type ListItemData } from '../components/List.js';
import { QRCode } from '../components/QRCode.js';
import { useNavigation } from '../hooks/useNavigation.js';
import { useAppContext, useStatus } from '../hooks/useAppContext.js';
import { useAppContext, useStatus, useDialog } from '../hooks/useAppContext.js';
import { colors, formatSatoshis, formatHex, logoSmall } from '../theme.js';
import type { HistoryItem } from '../../services/history.js';
import { generateTemplateIdentifier } from '@xo-cash/engine';
import { hexToBin, lockingBytecodeToCashAddress } from '@bitauth/libauth';
// Import utility functions
import {
@@ -71,6 +73,7 @@ export function WalletStateScreen(): React.ReactElement {
const { navigate } = useNavigation();
const { appService, showError, showInfo } = useAppContext();
const { setStatus } = useStatus();
const { setDialog } = useDialog();
// State
const [balance, setBalance] = useState<{ totalSatoshis: bigint; utxoCount: number } | null>(null);
@@ -80,6 +83,9 @@ export function WalletStateScreen(): React.ReactElement {
const [selectedHistoryIndex, setSelectedHistoryIndex] = useState(0);
const [isLoading, setIsLoading] = useState(true);
/** Cash address to display in the QR code dialog (null when dialog is hidden). */
const [qrAddress, setQrAddress] = useState<string | null>(null);
/**
* Refreshes wallet state.
*/
@@ -122,7 +128,7 @@ export function WalletStateScreen(): React.ReactElement {
}, [refresh]);
/**
* Generates a new receiving address.
* Generates a new receiving address and displays it as a QR code.
*/
const generateNewAddress = useCallback(async () => {
if (!appService) {
@@ -145,21 +151,33 @@ export function WalletStateScreen(): React.ReactElement {
// Generate the template identifier
const templateId = generateTemplateIdentifier(p2pkhTemplate);
// Generate the locking bytecode
const lockingBytecode = await appService.engine.generateLockingBytecode(
// Generate the locking bytecode (returned as a hex string)
const lockingBytecodeHex = await appService.engine.generateLockingBytecode(
templateId,
'receiveOutput',
'receiver',
);
showInfo(`New address generated!\n\nLocking bytecode:\n${formatHex(lockingBytecode, 40)}`);
// Convert the locking bytecode to a BCH cash address for display and QR encoding.
const result = lockingBytecodeToCashAddress({ bytecode: hexToBin(lockingBytecodeHex), prefix: 'bitcoincash' });
if (typeof result === 'string') {
showError(`Failed to encode address: ${result}`);
return;
}
console.log(result);
setQrAddress(result.address);
setDialog({ visible: true, type: 'custom', message: '' });
setStatus('Address generated');
// Refresh to show updated state
await refresh();
} catch (error) {
showError(`Failed to generate address: ${error instanceof Error ? error.message : String(error)}`);
}
}, [appService, setStatus, showInfo, showError, refresh]);
}, [appService, setStatus, showError, refresh]);
/**
* Handles menu action.
@@ -209,8 +227,16 @@ export function WalletStateScreen(): React.ReactElement {
});
}, [history]);
// Handle keyboard navigation between panels
// Handle keyboard navigation between panels and QR dialog dismissal
useInput((input, key) => {
if (qrAddress) {
if (key.escape || key.return) {
setQrAddress(null);
setDialog(null);
}
return;
}
if (key.tab) {
setFocusedPanel(prev => prev === 'menu' ? 'history' : 'menu');
}
@@ -400,6 +426,26 @@ export function WalletStateScreen(): React.ReactElement {
Tab: Switch focus Enter: Select : Navigate Esc: Back
</Text>
</Box>
{/* QR Code dialog overlay for generated addresses */}
{qrAddress && (
<Box
position="absolute"
flexDirection="column"
alignItems="center"
width="100%"
>
<QRCode
value={qrAddress}
dialog
dialogTitle="Receive Address"
showValue
/>
<Box justifyContent="center" marginTop={1}>
<Text color={colors.textMuted}>Press Enter or Esc to close</Text>
</Box>
</Box>
)}
</Box>
);
}