2023-12-06 15:29:11 +00:00
import React , { useState } from "react" ;
import { RqbitDesktopConfig } from "./configuration" ;
2023-12-06 21:35:00 +00:00
import { Button , Form , Modal , Row , Tab , Tabs } from "react-bootstrap" ;
2023-12-06 15:29:11 +00:00
import { ErrorComponent } from "./rqbit-webui-src/rqbit-web" ;
import { invokeAPI } from "./api" ;
import { ErrorDetails } from "./rqbit-webui-src/api-types" ;
2023-12-06 21:57:11 +00:00
const FormCheck : React.FC < {
label : string ,
name : string ,
checked : boolean ,
onChange : ( e : any ) = > void ,
disabled? : boolean ,
2023-12-06 22:09:31 +00:00
help? : string ,
} > = ( { label , name , checked , onChange , disabled , help } ) = > {
2023-12-06 21:57:11 +00:00
return < Form.Group as = { Row } controlId = { name } className = "mb-3" >
< Form.Label className = "col-4" > { label } < / Form.Label >
< div className = "col-8" >
< Form.Check
type = "switch"
name = { name }
checked = { checked }
onChange = { onChange }
disabled = { disabled }
/ >
< / div >
2023-12-06 22:09:31 +00:00
{ help && < div className = "form-text" > { help } < / div > }
2023-12-06 21:57:11 +00:00
< / Form.Group >
}
const FormInput : React.FC < {
label : string ,
name : string ,
value : string | number ,
inputType : string ,
onChange : ( e : any ) = > void ,
disabled? : boolean ,
2023-12-06 22:09:31 +00:00
help? : string
} > = ( { label , name , value , inputType , onChange , disabled , help } ) = > {
2023-12-06 21:57:11 +00:00
return < Form.Group as = { Row } controlId = { name } className = "mb-3" >
< Form.Label className = "col-4 col-form-label" > { label } < / Form.Label >
< div className = "col-8" >
< Form.Control
type = { inputType }
name = { name }
value = { value }
onChange = { onChange }
disabled = { disabled }
/ >
< / div >
2023-12-06 22:09:31 +00:00
{ help && < div className = "form-text" > { help } < / div > }
2023-12-06 21:57:11 +00:00
< / Form.Group >
}
2023-12-06 15:29:11 +00:00
export const ConfigModal : React.FC < {
handleOk : ( config : RqbitDesktopConfig ) = > void ,
initialConfig : RqbitDesktopConfig ,
} > = ( { handleOk , initialConfig } ) = > {
const [ config , setConfig ] = useState ( initialConfig ) ;
const [ error , setError ] = useState < any | null > ( null ) ;
const handleInputChange = ( e : any ) = > {
const name : string = e . target . name ;
const value : any = e . target . value ;
const [ mainField , subField ] = name . split ( '.' , 2 ) ;
if ( subField ) {
setConfig ( ( prevConfig : any ) = > ( {
. . . prevConfig ,
[ mainField ] : {
. . . prevConfig [ mainField ] ,
[ subField ] : value ,
} ,
} ) ) ;
} else {
setConfig ( ( prevConfig ) = > ( {
. . . prevConfig ,
[ name ] : value ,
} ) ) ;
}
} ;
const handleToggleChange = ( e : any ) = > {
const name : string = e . target . name ;
const [ mainField , subField ] = name . split ( '.' , 2 ) ;
if ( subField ) {
setConfig ( ( prevConfig : any ) = > ( {
. . . prevConfig ,
[ mainField ] : {
. . . prevConfig [ mainField ] ,
[ subField ] : ! prevConfig [ mainField ] [ subField ] ,
} ,
} ) ) ;
} else {
setConfig ( ( prevConfig : any ) = > ( {
. . . prevConfig ,
[ name ] : ! prevConfig [ name ] ,
} ) ) ;
}
} ;
const handleOkClick = ( ) = > {
setError ( null ) ;
invokeAPI < { } > ( "config_change" , { config } ) . then (
( ) = > handleOk ( config ) ,
( e : ErrorDetails ) = > {
setError ( {
text : "Error saving configuration" ,
details : e ,
} ) ;
}
)
} ;
return (
< Modal show size = 'xl' >
< Modal.Header closeButton >
< Modal.Title > Configure Rqbit desktop < / Modal.Title >
< / Modal.Header >
< Modal.Body >
< ErrorComponent error = { error } > < / ErrorComponent >
2023-12-06 15:39:20 +00:00
< Tabs
defaultActiveKey = "main"
id = "rqbit-config"
className = "mb-3" >
< Tab className = "mb-3" eventKey = "home" title = "Home" >
2023-12-06 21:57:11 +00:00
< FormInput
label = "Default download folder"
name = "default_download_location"
value = { config . default_download_location }
inputType = "text"
onChange = { handleInputChange }
2023-12-06 22:09:31 +00:00
help = "Where to download torrents by default. You can override this per torrent."
2023-12-06 21:57:11 +00:00
/ >
2023-12-06 15:39:20 +00:00
< / Tab >
2023-12-06 15:29:11 +00:00
2023-12-06 15:39:20 +00:00
< Tab className = "mb-3" eventKey = "dht" title = "DHT" >
2023-12-06 15:29:11 +00:00
< legend > DHT config < / legend >
2023-12-06 21:57:11 +00:00
< FormCheck
label = "Enable DHT"
name = "dht.disable"
checked = { ! config . dht . disable }
onChange = { handleToggleChange }
2023-12-06 22:09:31 +00:00
help = "DHT is required to read magnet links. There's no good reason to disable it, unless you know what you are doing."
2023-12-06 21:57:11 +00:00
/ >
< FormCheck
label = "Enable DHT persistence"
name = "dht.disable_persistence"
checked = { ! config . dht . disable_persistence }
onChange = { handleToggleChange }
2023-12-06 22:09:31 +00:00
help = "Enable to store DHT state in a file periodically. If disabled, DHT will bootstrap from scratch on restart."
2023-12-06 21:57:11 +00:00
/ >
< FormInput
label = "Persistence filename"
name = "dht.persistence_filename"
value = { config . dht . persistence_filename }
inputType = "text"
onChange = { handleInputChange }
2023-12-06 22:09:31 +00:00
help = "The filename to store DHT state into"
2023-12-06 21:57:11 +00:00
/ >
2023-12-06 15:39:20 +00:00
< / Tab >
2023-12-06 15:29:11 +00:00
2023-12-06 15:39:20 +00:00
< Tab className = "mb-3" eventKey = "tcp_listen" title = "TCP" >
2023-12-06 15:29:11 +00:00
< legend > TCP Listener config < / legend >
2023-12-06 21:57:11 +00:00
< FormCheck
label = "Listen on TCP"
name = "tcp_listen.disable"
checked = { ! config . tcp_listen . disable }
onChange = { handleToggleChange }
2023-12-06 22:09:31 +00:00
help = "Listen for torrent requests on TCP. Required for peers to be able to connect to you, mainly for uploading."
2023-12-06 21:57:11 +00:00
/ >
< FormCheck
label = "Advertise over UPnP"
name = "tcp_listen.disable"
checked = { ! config . tcp_listen . disable }
onChange = { handleToggleChange }
2023-12-06 22:09:31 +00:00
help = "Advertise your port over UPnP. This is required for peers to be able to connect to you from the internet. Will only work if your router has a static IP."
2023-12-06 21:57:11 +00:00
/ >
< FormInput
inputType = "number"
label = "Min port"
name = "tcp_listen.min_port"
value = { config . tcp_listen . min_port }
disabled = { config . tcp_listen . disable }
onChange = { handleInputChange }
2023-12-06 22:09:31 +00:00
help = "The min port to try to listen on. First successful is taken."
2023-12-06 21:57:11 +00:00
/ >
< FormInput
inputType = "number"
label = "Max port"
name = "tcp_listen.max_port"
value = { config . tcp_listen . max_port }
disabled = { config . tcp_listen . disable }
onChange = { handleInputChange }
2023-12-06 22:09:31 +00:00
help = "The max port to try to listen on."
2023-12-06 21:57:11 +00:00
/ >
2023-12-06 15:39:20 +00:00
< / Tab >
2023-12-06 15:29:11 +00:00
2023-12-06 15:39:20 +00:00
< Tab className = "mb-3" eventKey = "session_persistence" title = "Session" >
2023-12-06 15:29:11 +00:00
< legend > Session persistence < / legend >
2023-12-06 21:57:11 +00:00
< FormCheck
label = "Enable persistence"
name = "persistence.disable"
checked = { ! config . persistence . disable }
onChange = { handleToggleChange }
2023-12-06 22:09:31 +00:00
help = "If you disable session persistence, rqbit won't remember the torrents you had before restart."
2023-12-06 21:57:11 +00:00
/ >
< FormInput
label = "Persistence filename"
name = "persistence.filename"
inputType = "text"
value = { config . persistence . filename }
onChange = { handleInputChange }
/ >
2023-12-06 15:39:20 +00:00
< / Tab >
2023-12-06 15:29:11 +00:00
2023-12-06 15:39:20 +00:00
< Tab className = "mb-3" eventKey = "peer_opts" title = "Peer options" >
2023-12-06 15:29:11 +00:00
< legend > Peer connection options < / legend >
2023-12-06 21:57:11 +00:00
< FormInput
label = "Connect timeout (seconds)"
inputType = "number"
name = "peer_opts.connect_timeout"
value = { config . peer_opts . connect_timeout }
onChange = { handleInputChange }
2023-12-06 22:09:31 +00:00
help = "How much to wait for outgoing connections to connect. Default is low to prefer faster peers."
2023-12-06 21:57:11 +00:00
/ >
< FormInput
label = "Read/write timeout (seconds)"
inputType = "number"
name = "peer_opts.read_write_timeout"
value = { config . peer_opts . read_write_timeout }
onChange = { handleInputChange }
2023-12-06 22:09:31 +00:00
help = "Peer socket read/write timeout."
2023-12-06 21:57:11 +00:00
/ >
2023-12-06 15:39:20 +00:00
< / Tab >
2023-12-06 15:29:11 +00:00
2023-12-06 15:39:20 +00:00
< Tab className = "mb-3" eventKey = "http_api" title = "HTTP API" >
2023-12-06 15:29:11 +00:00
< legend > HTTP API config < / legend >
2023-12-06 21:57:11 +00:00
< FormCheck
label = "Enable HTTP API"
name = "http_api.disable"
checked = { ! config . http_api . disable }
onChange = { handleToggleChange }
2023-12-06 22:09:31 +00:00
help = "If enabled you can access the HTTP API at the address below"
2023-12-06 21:57:11 +00:00
/ >
< FormCheck
2023-12-06 22:09:31 +00:00
label = "Read only"
2023-12-06 21:57:11 +00:00
name = "http_api.read_only"
checked = { config . http_api . read_only }
disabled = { config . http_api . disable }
onChange = { handleToggleChange }
2023-12-06 22:09:31 +00:00
help = "If enabled, only GET requests will be allowed through the API"
2023-12-06 21:57:11 +00:00
/ >
< FormInput
label = "Listen address"
inputType = "text"
name = "http_api.listen_addr"
value = { config . http_api . listen_addr }
disabled = { config . http_api . disable }
onChange = { handleInputChange }
2023-12-06 22:09:31 +00:00
help = { ` You'll access the API at http:// ${ config . http_api . listen_addr } ` }
2023-12-06 21:57:11 +00:00
/ >
2023-12-06 15:39:20 +00:00
< / Tab >
2023-12-06 15:29:11 +00:00
2023-12-06 15:39:20 +00:00
< / Tabs >
2023-12-06 15:29:11 +00:00
< / Modal.Body >
< Modal.Footer >
< Button variant = "secondary" onClick = { ( ) = > setConfig ( initialConfig ) } >
Reset to defaults
< / Button >
< Button variant = "primary" onClick = { handleOkClick } >
OK
< / Button >
< / Modal.Footer >
< / Modal >
) ;
} ;