@@ -2,7 +2,7 @@ import Clipboard from '@react-native-clipboard/clipboard';
22import lm from '@synonymdev/react-native-ldk' ;
33import React , { ReactElement , memo , useState } from 'react' ;
44import { useTranslation } from 'react-i18next' ;
5- import { ScrollView , StyleSheet } from 'react-native' ;
5+ import { ScrollView , StyleSheet , TouchableOpacity , View } from 'react-native' ;
66import RNFS from 'react-native-fs' ;
77import Share from 'react-native-share' ;
88
@@ -13,17 +13,19 @@ import { useLightningBalance } from '../../../hooks/lightning';
1313import { useAppDispatch , useAppSelector } from '../../../hooks/redux' ;
1414import { useSheetRef } from '../../../sheets/SheetRefsProvider' ;
1515import { openChannelsSelector } from '../../../store/reselect/lightning' ;
16+ import { settingsSelector } from '../../../store/reselect/settings' ;
1617import {
1718 selectedNetworkSelector ,
1819 selectedWalletSelector ,
1920} from '../../../store/reselect/wallet' ;
2021import { removeLightningPeer } from '../../../store/slices/lightning' ;
22+ import { updateSettings } from '../../../store/slices/settings' ;
2123import {
2224 createLightningInvoice ,
2325 savePeer ,
2426} from '../../../store/utils/lightning' ;
2527import { TextInput , View as ThemedView } from '../../../styles/components' ;
26- import { Caption13Up } from '../../../styles/text' ;
28+ import { BodyM , Caption13Up } from '../../../styles/text' ;
2729import {
2830 addPeer ,
2931 getNodeId ,
@@ -53,6 +55,15 @@ const LdkDebug = (): ReactElement => {
5355 const selectedWallet = useAppSelector ( selectedWalletSelector ) ;
5456 const selectedNetwork = useAppSelector ( selectedNetworkSelector ) ;
5557 const openChannels = useAppSelector ( openChannelsSelector ) ;
58+ const settings = useAppSelector ( settingsSelector ) ;
59+
60+ // Max Dust HTLC Exposure form state
61+ const [ selectedType , setSelectedType ] = useState <
62+ 'fixed_limit' | 'fee_rate_multiplier' | null
63+ > ( settings ?. max_dust_htlc_exposure_type ?? null ) ;
64+ const [ exposureValue , setExposureValue ] = useState < string > (
65+ settings ?. max_dust_htlc_exposure ?. toString ( ) ?? '1000' ,
66+ ) ;
5667
5768 const onNodeId = async ( ) : Promise < void > => {
5869 const nodeId = await getNodeId ( ) ;
@@ -435,6 +446,97 @@ const LdkDebug = (): ReactElement => {
435446 </ >
436447 ) }
437448
449+ < Caption13Up style = { styles . sectionTitle } color = "secondary" >
450+ Max Dust HTLC Exposure
451+ </ Caption13Up >
452+
453+ { /* Radio buttons for type selection */ }
454+ < TouchableOpacity
455+ style = { styles . radioOption }
456+ onPress = { ( ) =>
457+ setSelectedType (
458+ selectedType === 'fixed_limit' ? null : 'fixed_limit' ,
459+ )
460+ } >
461+ < View
462+ style = { [
463+ styles . radioButton ,
464+ selectedType === 'fixed_limit' && styles . radioButtonSelected ,
465+ ] }
466+ />
467+ < BodyM style = { styles . radioLabel } > Fixed Limit (sats)</ BodyM >
468+ </ TouchableOpacity >
469+
470+ < TouchableOpacity
471+ style = { styles . radioOption }
472+ onPress = { ( ) =>
473+ setSelectedType (
474+ selectedType === 'fee_rate_multiplier'
475+ ? null
476+ : 'fee_rate_multiplier' ,
477+ )
478+ } >
479+ < View
480+ style = { [
481+ styles . radioButton ,
482+ selectedType === 'fee_rate_multiplier' &&
483+ styles . radioButtonSelected ,
484+ ] }
485+ />
486+ < BodyM style = { styles . radioLabel } > Fee Rate Multiplier</ BodyM >
487+ </ TouchableOpacity >
488+
489+ { /* Number input - only show when a type is selected */ }
490+ { selectedType && (
491+ < TextInput
492+ style = { styles . textInput }
493+ value = { exposureValue }
494+ onChangeText = { setExposureValue }
495+ placeholder = { selectedType === 'fixed_limit' ? '1000' : '10' }
496+ keyboardType = "numeric"
497+ autoCapitalize = "none"
498+ autoComplete = "off"
499+ autoCorrect = { false }
500+ />
501+ ) }
502+
503+ { /* Action buttons */ }
504+ < View style = { styles . buttonRow } >
505+ < Button
506+ style = { [ styles . button , styles . buttonHalf ] }
507+ text = "Set max dust"
508+ disabled = { ! selectedType || ! exposureValue }
509+ onPress = { ( ) : void => {
510+ const numValue = Number . parseInt ( exposureValue , 10 ) ;
511+ if ( ! Number . isNaN ( numValue ) && selectedType ) {
512+ dispatch (
513+ updateSettings ( {
514+ max_dust_htlc_exposure_type : selectedType ,
515+ max_dust_htlc_exposure : numValue ,
516+ } ) ,
517+ ) ;
518+ }
519+ } }
520+ />
521+ { ( settings ?. max_dust_htlc_exposure_type ||
522+ settings ?. max_dust_htlc_exposure ) && (
523+ < Button
524+ style = { [ styles . button , styles . buttonHalf ] }
525+ text = "Reset max dust"
526+ onPress = { ( ) : void => {
527+ setSelectedType ( null ) ;
528+ setExposureValue ( '1000' ) ;
529+ dispatch (
530+ updateSettings ( {
531+ max_dust_htlc_exposure_type : undefined ,
532+ max_dust_htlc_exposure : undefined ,
533+ } ) ,
534+ ) ;
535+ } }
536+ />
537+ ) }
538+ </ View >
539+
438540 < SafeAreaInset type = "bottom" minPadding = { 16 } />
439541 </ ScrollView >
440542 </ ThemedView >
@@ -467,6 +569,35 @@ const styles = StyleSheet.create({
467569 button : {
468570 marginTop : 8 ,
469571 } ,
572+ radioOption : {
573+ flexDirection : 'row' ,
574+ alignItems : 'center' ,
575+ marginTop : 12 ,
576+ } ,
577+ radioButton : {
578+ width : 20 ,
579+ height : 20 ,
580+ borderRadius : 10 ,
581+ borderWidth : 2 ,
582+ borderColor : '#666' ,
583+ marginRight : 12 ,
584+ } ,
585+ radioButtonSelected : {
586+ borderColor : '#FF6600' ,
587+ backgroundColor : '#FF6600' ,
588+ } ,
589+ radioLabel : {
590+ flex : 1 ,
591+ } ,
592+ buttonRow : {
593+ flexDirection : 'row' ,
594+ marginTop : 16 ,
595+ gap : 8 ,
596+ } ,
597+ buttonHalf : {
598+ flex : 1 ,
599+ marginTop : 0 ,
600+ } ,
470601} ) ;
471602
472603export default memo ( LdkDebug ) ;
0 commit comments