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-11 19:58:53 +00:00
import { ErrorComponent } from "rqbit-webui/src/components/ErrorComponent" ;
2023-12-06 15:29:11 +00:00
import { invokeAPI } from "./api" ;
2023-12-11 19:58:53 +00:00
import { ErrorDetails } from "rqbit-webui/src/api-types" ;
2023-12-06 15:29:11 +00:00
2023-12-06 21:57:11 +00:00
const FormCheck : React.FC < {
2023-12-07 14:11:12 +00:00
label : string ;
name : string ;
checked : boolean ;
2023-12-08 12:14:17 +00:00
onChange : React.ChangeEventHandler < HTMLInputElement > ;
2023-12-07 14:11:12 +00:00
disabled? : boolean ;
help? : string ;
2023-12-06 22:09:31 +00:00
} > = ( { label , name , checked , onChange , disabled , help } ) = > {
2023-12-07 14:11:12 +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 >
{ help && < div className = "form-text" > { help } < / div > }
2023-12-06 21:57:11 +00:00
< / Form.Group >
2023-12-07 14:11:12 +00:00
) ;
} ;
2023-12-06 21:57:11 +00:00
const FormInput : React.FC < {
2023-12-07 14:11:12 +00:00
label : string ;
name : string ;
value : string | number ;
inputType : string ;
2023-12-08 12:14:17 +00:00
onChange : React.ChangeEventHandler < HTMLInputElement > ;
2023-12-07 14:11:12 +00:00
disabled? : boolean ;
help? : string ;
2023-12-06 22:09:31 +00:00
} > = ( { label , name , value , inputType , onChange , disabled , help } ) = > {
2023-12-07 14:11:12 +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 >
{ help && < div className = "form-text" > { help } < / div > }
2023-12-06 21:57:11 +00:00
< / Form.Group >
2023-12-07 14:11:12 +00:00
) ;
} ;
2023-12-06 21:57:11 +00:00
2023-12-06 15:29:11 +00:00
export const ConfigModal : React.FC < {
2023-12-07 14:11:12 +00:00
show : boolean ;
handleStartReconfigure : ( ) = > void ;
handleConfigured : ( config : RqbitDesktopConfig ) = > void ;
handleCancel ? : ( ) = > void ;
initialConfig : RqbitDesktopConfig ;
defaultConfig : RqbitDesktopConfig ;
} > = ( {
show ,
handleStartReconfigure ,
handleConfigured ,
handleCancel ,
initialConfig ,
defaultConfig ,
} ) = > {
let [ config , setConfig ] = useState < RqbitDesktopConfig > ( initialConfig ) ;
let [ loading , setLoading ] = useState < boolean > ( false ) ;
const [ error , setError ] = useState < any | null > ( null ) ;
2023-12-08 12:14:17 +00:00
const handleInputChange : React.ChangeEventHandler < HTMLInputElement > = ( e ) = > {
2023-12-07 14:11:12 +00:00
const name : string = e . target . name ;
2023-12-08 12:14:17 +00:00
let value : string | number = e . target . value ;
if ( e . target . type == "number" ) {
value = e . target . valueAsNumber ;
}
console . log ( value , typeof value ) ;
2023-12-07 14:11:12 +00:00
const [ mainField , subField ] = name . split ( "." , 2 ) ;
if ( subField ) {
setConfig ( ( prevConfig : any ) = > ( {
. . . prevConfig ,
[ mainField ] : {
. . . prevConfig [ mainField ] ,
[ subField ] : value ,
} ,
} ) ) ;
} else {
setConfig ( ( prevConfig ) = > ( {
. . . prevConfig ,
[ name ] : value ,
} ) ) ;
}
} ;
2023-12-08 12:14:17 +00:00
const handleToggleChange : React.ChangeEventHandler < HTMLInputElement > = (
e
) = > {
2023-12-07 14:11:12 +00:00
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 ) ;
handleStartReconfigure ( ) ;
setLoading ( true ) ;
invokeAPI < { } > ( "config_change" , { config } ) . then (
( ) = > {
setLoading ( false ) ;
handleConfigured ( config ) ;
} ,
( e : ErrorDetails ) = > {
setLoading ( false ) ;
setError ( {
text : "Error saving configuration" ,
details : e ,
} ) ;
}
) ;
} ;
return (
< Modal show = { show } size = "xl" onHide = { handleCancel } >
< Modal.Header closeButton >
< Modal.Title > Configure Rqbit desktop < / Modal.Title >
< / Modal.Header >
< Modal.Body >
< ErrorComponent error = { error } > < / ErrorComponent >
< Tabs defaultActiveKey = "home" id = "rqbit-config" className = "mb-3" >
< Tab className = "mb-3" eventKey = "home" title = "Home" >
< FormInput
label = "Default download folder"
name = "default_download_location"
value = { config . default_download_location }
inputType = "text"
onChange = { handleInputChange }
help = "Where to download torrents by default. You can override this per torrent."
/ >
< / Tab >
2023-12-06 15:29:11 +00:00
2023-12-07 14:11:12 +00:00
< Tab className = "mb-3" eventKey = "dht" title = "DHT" >
< legend > DHT config < / legend >
2023-12-06 21:57:11 +00:00
2023-12-07 14:11:12 +00:00
< FormCheck
label = "Enable DHT"
name = "dht.disable"
checked = { ! config . dht . disable }
onChange = { handleToggleChange }
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
2023-12-07 14:11:12 +00:00
< FormCheck
label = "Enable DHT persistence"
name = "dht.disable_persistence"
checked = { ! config . dht . disable_persistence }
onChange = { handleToggleChange }
disabled = { config . dht . disable }
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
2023-12-07 14:11:12 +00:00
< FormInput
label = "Persistence filename"
name = "dht.persistence_filename"
value = { config . dht . persistence_filename }
inputType = "text"
disabled = { config . dht . disable }
onChange = { handleInputChange }
help = "The filename to store DHT state into"
/ >
< / Tab >
2023-12-06 15:29:11 +00:00
2023-12-07 14:11:12 +00:00
< Tab className = "mb-3" eventKey = "tcp_listen" title = "TCP" >
< legend > TCP Listener config < / legend >
2023-12-06 15:29:11 +00:00
2023-12-07 14:11:12 +00:00
< FormCheck
label = "Listen on TCP"
name = "tcp_listen.disable"
checked = { ! config . tcp_listen . disable }
onChange = { handleToggleChange }
help = "Listen for torrent requests on TCP. Required for peers to be able to connect to you, mainly for uploading."
/ >
2023-12-06 15:29:11 +00:00
2023-12-07 14:11:12 +00:00
< FormCheck
label = "Advertise over UPnP"
name = "tcp_listen.disable"
checked = { ! config . tcp_listen . disable }
onChange = { handleToggleChange }
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
2023-12-07 14:11:12 +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 }
help = "The min port to try to listen on. First successful is taken."
/ >
2023-12-06 15:29:11 +00:00
2023-12-07 14:11:12 +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 }
help = "The max port to try to listen on."
/ >
< / Tab >
2023-12-06 15:29:11 +00:00
2023-12-07 14:11:12 +00:00
< Tab className = "mb-3" eventKey = "session_persistence" title = "Session" >
< legend > Session persistence < / legend >
2023-12-06 21:57:11 +00:00
2023-12-07 14:11:12 +00:00
< FormCheck
label = "Enable persistence"
name = "persistence.disable"
checked = { ! config . persistence . disable }
onChange = { handleToggleChange }
help = "If you disable session persistence, rqbit won't remember the torrents you had before restart."
/ >
2023-12-06 15:29:11 +00:00
2023-12-07 14:11:12 +00:00
< FormInput
label = "Persistence filename"
name = "persistence.filename"
inputType = "text"
value = { config . persistence . filename }
onChange = { handleInputChange }
disabled = { config . persistence . disable }
/ >
< / Tab >
< Tab className = "mb-3" eventKey = "peer_opts" title = "Peer options" >
< legend > Peer connection options < / legend >
< FormInput
label = "Connect timeout (seconds)"
inputType = "number"
name = "peer_opts.connect_timeout"
value = { config . peer_opts . connect_timeout }
onChange = { handleInputChange }
help = "How much to wait for outgoing connections to connect. Default is low to prefer faster peers."
/ >
2023-12-06 15:29:11 +00:00
2023-12-07 14:11:12 +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 }
help = "Peer socket read/write timeout."
/ >
< / Tab >
2023-12-06 21:57:11 +00:00
2023-12-07 14:11:12 +00:00
< Tab className = "mb-3" eventKey = "http_api" title = "HTTP API" >
< legend > HTTP API config < / legend >
2023-12-06 21:57:11 +00:00
2023-12-07 14:11:12 +00:00
< FormCheck
label = "Enable HTTP API"
name = "http_api.disable"
checked = { ! config . http_api . disable }
onChange = { handleToggleChange }
help = "If enabled you can access the HTTP API at the address below"
/ >
2023-12-06 15:29:11 +00:00
2023-12-07 14:11:12 +00:00
< FormCheck
label = "Read only"
name = "http_api.read_only"
checked = { config . http_api . read_only }
disabled = { config . http_api . disable }
onChange = { handleToggleChange }
help = "If enabled, only GET requests will be allowed through the API"
/ >
2023-12-06 22:38:55 +00:00
2023-12-08 19:47:48 +00:00
< FormCheck
label = "CORS any"
name = "http_api.cors_enable_all"
checked = { config . http_api . cors_enable_all }
disabled = { config . http_api . disable }
onChange = { handleToggleChange }
help = "If enabled, the API will allow Cross Origin requests (including this app)"
/ >
2023-12-07 14:11:12 +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 }
help = { ` You'll access the API at http:// ${ config . http_api . listen_addr } ` }
/ >
< / Tab >
< / Tabs >
< / Modal.Body >
< Modal.Footer >
{ ! ! handleCancel && (
< Button variant = "secondary" onClick = { handleCancel } >
Cancel
< / Button >
) }
< Button variant = "secondary" onClick = { ( ) = > setConfig ( defaultConfig ) } >
Reset to defaults
< / Button >
< Button variant = "primary" onClick = { handleOkClick } disabled = { loading } >
OK
< / Button >
< / Modal.Footer >
< / Modal >
) ;
2023-12-06 15:29:11 +00:00
} ;