@@ -54,6 +54,7 @@ import {
5454 Delete ,
5555 Edit ,
5656 HourglassEmpty ,
57+ Refresh ,
5758} from "@mui/icons-material" ;
5859
5960import { getNetwork } from "store/config" ;
@@ -62,6 +63,8 @@ import InfoBox from "renderer/components/modal/swap/InfoBox";
6263import { isValidMultiAddressWithPeerId } from "utils/parseUtils" ;
6364
6465import { useAppSelector } from "store/hooks" ;
66+ import { getNodeStatus } from "renderer/rpc" ;
67+ import { setStatus } from "store/features/nodesSlice" ;
6568
6669const PLACEHOLDER_ELECTRUM_RPC_URL = "ssl://blockstream.info:700" ;
6770const PLACEHOLDER_MONERO_NODE_URL = "http://xmr-node.cakewallet.com:18081" ;
@@ -343,15 +346,49 @@ function MoneroRpcPoolSetting() {
343346}
344347
345348/**
346- * A setting that allows you to select the Monero Node URL to use .
347- * Only shown when RPC pool is disabled .
349+ * A setting that allows you to configure a single Monero Node URL.
350+ * Gets disabled when RPC pool is enabled .
348351 */
349352function MoneroNodeUrlSetting ( ) {
350- const [ tableVisible , setTableVisible ] = useState ( false ) ;
351353 const network = getNetwork ( ) ;
352354 const useMoneroRpcPool = useSettings ( ( s ) => s . useMoneroRpcPool ) ;
355+ const moneroNodeUrl = useSettings ( ( s ) => s . nodes [ network ] [ Blockchain . Monero ] [ 0 ] || "" ) ;
356+ const nodeStatuses = useNodes ( ( s ) => s . nodes ) ;
357+ const dispatch = useAppDispatch ( ) ;
358+ const [ isRefreshing , setIsRefreshing ] = useState ( false ) ;
359+
360+ const currentNodes = useSettings ( ( s ) => s . nodes [ network ] [ Blockchain . Monero ] ) ;
361+
362+ const handleNodeUrlChange = ( newUrl : string ) => {
363+ // Remove existing nodes and add the new one
364+ currentNodes . forEach ( node => {
365+ dispatch ( removeNode ( { network, type : Blockchain . Monero , node } ) ) ;
366+ } ) ;
367+
368+ if ( newUrl . trim ( ) ) {
369+ dispatch ( addNode ( { network, type : Blockchain . Monero , node : newUrl . trim ( ) } ) ) ;
370+ }
371+ } ;
372+
373+ const handleRefreshStatus = async ( ) => {
374+ // Don't refresh if pool is enabled or no node URL is configured
375+ if ( ! moneroNodeUrl || useMoneroRpcPool ) return ;
376+
377+ setIsRefreshing ( true ) ;
378+ try {
379+ const status = await getNodeStatus ( moneroNodeUrl , Blockchain . Monero , network ) ;
380+
381+ // Update the status in the store
382+ dispatch ( setStatus ( { node : moneroNodeUrl , status, blockchain : Blockchain . Monero } ) ) ;
383+ } catch ( error ) {
384+ console . error ( "Failed to refresh node status:" , error ) ;
385+ } finally {
386+ setIsRefreshing ( false ) ;
387+ }
388+ } ;
353389
354- const isValid = ( url : string ) => isValidUrl ( url , [ "http" ] ) ;
390+ const isValid = ( url : string ) => url === "" || isValidUrl ( url , [ "http" ] ) ;
391+ const nodeStatus = moneroNodeUrl ? nodeStatuses [ Blockchain . Monero ] [ moneroNodeUrl ] : null ;
355392
356393 return (
357394 < TableRow >
@@ -360,32 +397,55 @@ function MoneroNodeUrlSetting() {
360397 label = "Custom Monero Node URL"
361398 tooltip = {
362399 useMoneroRpcPool
363- ? "This setting is disabled because Monero RPC pool is enabled. Disable the RPC pool to configure custom nodes ."
400+ ? "This setting is disabled because Monero RPC pool is enabled. Disable the RPC pool to configure a custom node ."
364401 : "This is the URL of the Monero node that the GUI will connect to. It is used to sync Monero transactions. If you leave this field empty, the GUI will choose from a list of known servers at random."
365402 }
366403 disabled = { useMoneroRpcPool }
367404 />
368405 </ TableCell >
369406 < TableCell >
370- < IconButton
371- onClick = { ( ) => setTableVisible ( true ) }
372- size = "large"
373- disabled = { useMoneroRpcPool }
374- >
375- { < Edit /> }
376- </ IconButton >
377- { tableVisible ? (
378- < NodeTableModal
379- open = { tableVisible }
380- onClose = { ( ) => setTableVisible ( false ) }
381- network = { network }
382- blockchain = { Blockchain . Monero }
383- isValid = { isValid }
407+ < Box sx = { { display : "flex" , alignItems : "center" , gap : 1 } } >
408+ < ValidatedTextField
409+ value = { moneroNodeUrl }
410+ onValidatedChange = { handleNodeUrlChange }
384411 placeholder = { PLACEHOLDER_MONERO_NODE_URL }
412+ disabled = { useMoneroRpcPool }
413+ fullWidth
414+ isValid = { isValid }
415+ variant = "outlined"
416+ noErrorWhenEmpty
385417 />
386- ) : (
387- < > </ >
388- ) }
418+ < >
419+ < Tooltip title = {
420+ useMoneroRpcPool
421+ ? "Node status checking is disabled when using the pool"
422+ : ! moneroNodeUrl
423+ ? "Enter a node URL to check status"
424+ : "Node status"
425+ } >
426+ < Box sx = { { display : "flex" , alignItems : "center" } } >
427+ < Circle
428+ color = { useMoneroRpcPool || ! moneroNodeUrl ? "gray" : ( nodeStatus ? "green" : "red" ) }
429+ />
430+ </ Box >
431+ </ Tooltip >
432+ < Tooltip title = {
433+ useMoneroRpcPool
434+ ? "Node status refresh is disabled when using the pool"
435+ : ! moneroNodeUrl
436+ ? "Enter a node URL to refresh status"
437+ : "Refresh node status"
438+ } >
439+ < IconButton
440+ onClick = { handleRefreshStatus }
441+ disabled = { isRefreshing || useMoneroRpcPool || ! moneroNodeUrl }
442+ size = "small"
443+ >
444+ { isRefreshing ? < HourglassEmpty /> : < Refresh /> }
445+ </ IconButton >
446+ </ Tooltip >
447+ </ >
448+ </ Box >
389449 </ TableCell >
390450 </ TableRow >
391451 ) ;
0 commit comments