rqbit desktop configuration before startup works
This commit is contained in:
parent
a3475784e9
commit
ee996012af
5 changed files with 340 additions and 8 deletions
|
|
@ -2,7 +2,7 @@ import { MouseEventHandler, RefObject, createContext, useContext, useEffect, use
|
|||
import { ProgressBar, Button, Container, Row, Col, Alert, Modal, Form, Spinner } from 'react-bootstrap';
|
||||
import { AddTorrentResponse, TorrentDetails, TorrentId, TorrentStats, ErrorDetails as ApiErrorDetails, STATE_INITIALIZING, STATE_LIVE, STATE_PAUSED, STATE_ERROR, RqbitAPI, AddTorrentOptions } from './api-types';
|
||||
|
||||
interface Error {
|
||||
export interface Error {
|
||||
text: string,
|
||||
details?: ApiErrorDetails,
|
||||
}
|
||||
|
|
@ -409,7 +409,7 @@ const ErrorDetails = (props: { details: ApiErrorDetails | null | undefined }) =>
|
|||
</>
|
||||
}
|
||||
|
||||
const ErrorComponent = (props: { error: Error | null, remove?: () => void }) => {
|
||||
export const ErrorComponent = (props: { error: Error | null, remove?: () => void }) => {
|
||||
let { error, remove } = props;
|
||||
|
||||
if (error == null) {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ function errorToUIError(path: string): (e: InvokeErrorResponse) => Promise<never
|
|||
}
|
||||
}
|
||||
|
||||
async function invokeAPI<Response>(name: string, params?: InvokeArgs): Promise<Response> {
|
||||
export async function invokeAPI<Response>(name: string, params?: InvokeArgs): Promise<Response> {
|
||||
console.log("invoking", name, params);
|
||||
const result = await invoke<Response>(name, params).catch(errorToUIError(name));
|
||||
console.log(result);
|
||||
|
|
|
|||
45
desktop/src/configuration.tsx
Normal file
45
desktop/src/configuration.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
type PathLike = string;
|
||||
type Duration = string;
|
||||
type SocketAddr = string;
|
||||
|
||||
interface RqbitDesktopConfigDht {
|
||||
disable: boolean;
|
||||
disable_persistence: boolean;
|
||||
persistence_filename: PathLike;
|
||||
}
|
||||
|
||||
interface RqbitDesktopConfigTcpListen {
|
||||
disable: boolean;
|
||||
min_port: number;
|
||||
max_port: number;
|
||||
}
|
||||
|
||||
interface RqbitDesktopConfigPersistence {
|
||||
disable: boolean;
|
||||
filename: PathLike;
|
||||
}
|
||||
|
||||
interface RqbitDesktopConfigPeerOpts {
|
||||
connect_timeout: Duration;
|
||||
read_write_timeout: Duration;
|
||||
}
|
||||
|
||||
interface RqbitDesktopConfigHttpApi {
|
||||
disable: boolean;
|
||||
listen_addr: SocketAddr;
|
||||
read_only: boolean;
|
||||
}
|
||||
|
||||
interface RqbitDesktopConfigUpnp {
|
||||
disable: boolean;
|
||||
}
|
||||
|
||||
export interface RqbitDesktopConfig {
|
||||
default_download_location: PathLike;
|
||||
dht: RqbitDesktopConfigDht;
|
||||
tcp_listen: RqbitDesktopConfigTcpListen;
|
||||
upnp: RqbitDesktopConfigUpnp;
|
||||
persistence: RqbitDesktopConfigPersistence;
|
||||
peer_opts: RqbitDesktopConfigPeerOpts;
|
||||
http_api: RqbitDesktopConfigHttpApi;
|
||||
}
|
||||
266
desktop/src/configure.tsx
Normal file
266
desktop/src/configure.tsx
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
import React, { useState } from "react";
|
||||
import { RqbitDesktopConfig } from "./configuration";
|
||||
import { Button, Form, Modal } from "react-bootstrap";
|
||||
import { ErrorComponent } from "./rqbit-webui-src/rqbit-web";
|
||||
import { invokeAPI } from "./api";
|
||||
import { ErrorDetails } from "./rqbit-webui-src/api-types";
|
||||
|
||||
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>
|
||||
<Form>
|
||||
<fieldset className="mb-3">
|
||||
<Form.Group controlId="default_download_location">
|
||||
<Form.Label>Default Download Location</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
name="default_download_location"
|
||||
value={config.default_download_location}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
</fieldset>
|
||||
|
||||
<fieldset className="mb-3">
|
||||
<legend>DHT config</legend>
|
||||
|
||||
<Form.Group controlId="dht_disable">
|
||||
<Form.Check
|
||||
type="switch"
|
||||
label="Disable DHT"
|
||||
name="dht.disable"
|
||||
checked={config.dht.disable}
|
||||
onChange={handleToggleChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="dht_disable_persistence">
|
||||
<Form.Check
|
||||
type="switch"
|
||||
label="Disable DHT Persistence"
|
||||
name="dht.disable_persistence"
|
||||
checked={config.dht.disable_persistence}
|
||||
onChange={handleToggleChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="dht_persistence_filename">
|
||||
<Form.Label>Persistence Filename</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
name="dht.persistence_filename"
|
||||
value={config.dht.persistence_filename}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
</fieldset>
|
||||
|
||||
<fieldset className="mb-3">
|
||||
|
||||
<legend>TCP Listener config</legend>
|
||||
|
||||
<Form.Group controlId="tcp_listen_disable">
|
||||
<Form.Check
|
||||
type="switch"
|
||||
label="Disable TCP Listen"
|
||||
name="tcp_listen.disable"
|
||||
checked={config.tcp_listen.disable}
|
||||
onChange={handleToggleChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="tcp_listen_min_port">
|
||||
<Form.Label>TCP Listen Min Port</Form.Label>
|
||||
<Form.Control
|
||||
type="number"
|
||||
name="tcp_listen.min_port"
|
||||
value={config.tcp_listen.min_port}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="tcp_listen_max_port">
|
||||
<Form.Label>TCP Listen Max Port</Form.Label>
|
||||
<Form.Control
|
||||
type="number"
|
||||
name="tcp_listen.max_port"
|
||||
value={config.tcp_listen.max_port}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="upnp_disable">
|
||||
<Form.Check
|
||||
type="switch"
|
||||
label="Do not advertise TCP port over UPnP"
|
||||
name="upnp.disable"
|
||||
checked={config.upnp.disable}
|
||||
onChange={handleToggleChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
</fieldset>
|
||||
|
||||
|
||||
<fieldset className="mb-3">
|
||||
<legend>Session persistence</legend>
|
||||
|
||||
<Form.Group controlId="persistence_disable">
|
||||
<Form.Check
|
||||
type="switch"
|
||||
label="Disable Persistence"
|
||||
name="persistence.disable"
|
||||
checked={config.persistence.disable}
|
||||
onChange={handleToggleChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="persistence_filename">
|
||||
<Form.Label>Persistence Filename</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
name="persistence.filename"
|
||||
value={config.persistence.filename}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
</fieldset>
|
||||
|
||||
<fieldset className="mb-3">
|
||||
<legend>Peer connection options</legend>
|
||||
|
||||
<Form.Group controlId="peer_opts_connect_timeout">
|
||||
<Form.Label>Peer Options Connect Timeout</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
name="peer_opts.connect_timeout"
|
||||
value={config.peer_opts.connect_timeout}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="peer_opts_read_write_timeout">
|
||||
<Form.Label>Peer Options Read Write Timeout</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
name="peer_opts.read_write_timeout"
|
||||
value={config.peer_opts.read_write_timeout}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
</fieldset>
|
||||
|
||||
<fieldset className="mb-3">
|
||||
<legend>HTTP API config</legend>
|
||||
|
||||
<Form.Group controlId="http_api_disable">
|
||||
<Form.Check
|
||||
type="switch"
|
||||
label="Disable HTTP API"
|
||||
name="http_api.disable"
|
||||
checked={config.http_api.disable}
|
||||
onChange={handleToggleChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="http_api_listen_addr">
|
||||
<Form.Label>HTTP API Listen Address</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
name="http_api.listen_addr"
|
||||
value={config.http_api.listen_addr}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="http_api_read_only">
|
||||
<Form.Check
|
||||
type="switch"
|
||||
label="HTTP API Read Only"
|
||||
name="http_api.read_only"
|
||||
checked={config.http_api.read_only}
|
||||
onChange={handleToggleChange}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
</fieldset>
|
||||
|
||||
</Form>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="secondary" onClick={() => setConfig(initialConfig)}>
|
||||
Reset to defaults
|
||||
</Button>
|
||||
<Button variant="primary" onClick={handleOkClick}>
|
||||
OK
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,19 +1,40 @@
|
|||
import { StrictMode } from "react";
|
||||
import { StrictMode, useState } from "react";
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { APIContext, RqbitWebUI } from "./rqbit-webui-src/rqbit-web";
|
||||
import { API } from "./api";
|
||||
import { invoke } from "@tauri-apps/api";
|
||||
import { RqbitDesktopConfig } from "./configuration";
|
||||
import { ConfigModal } from "./configure";
|
||||
|
||||
let version = invoke<string>("get_version").then((version) => {
|
||||
async function get_version(): Promise<string> {
|
||||
return invoke<string>("get_version");
|
||||
}
|
||||
|
||||
async function get_default_config(): Promise<RqbitDesktopConfig> {
|
||||
return invoke<RqbitDesktopConfig>("config_default");
|
||||
}
|
||||
|
||||
const RqbitDesktop: React.FC<{
|
||||
version: string,
|
||||
defaultConfig: RqbitDesktopConfig,
|
||||
}> = ({ version, defaultConfig }) => {
|
||||
let [configured, setConfigured] = useState<boolean>(false);
|
||||
|
||||
if (configured) {
|
||||
return <RqbitWebUI title={`Rqbit Desktop v${version}`}></RqbitWebUI>
|
||||
}
|
||||
return <ConfigModal handleOk={() => setConfigured(true)} initialConfig={defaultConfig}></ConfigModal>;
|
||||
}
|
||||
|
||||
Promise.all([get_version(), get_default_config()]).then(([version, config]) => {
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<StrictMode>
|
||||
<APIContext.Provider value={API}>
|
||||
<RqbitWebUI title={`Rqbit Desktop v${version}`} />
|
||||
<RqbitDesktop version={version} defaultConfig={config} />
|
||||
</APIContext.Provider>
|
||||
</StrictMode>
|
||||
);
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue