🔧 Genericized AirDC++ settings

1. You can now enter your AirDC++ client settings in the settings menu and the UI will read from them
2. Hubs can also be selected for search
This commit is contained in:
2021-11-23 17:26:48 -08:00
parent 610038247f
commit 84f242184e
10 changed files with 180 additions and 113 deletions

View File

@@ -0,0 +1,98 @@
import React, { ReactElement, useEffect, useState } from "react";
import { Form, Field } from "react-final-form";
import axios from "axios";
import { useDispatch } from "react-redux";
import { isEmpty, isUndefined } from "lodash";
import Select from "react-select";
import { saveSettings } from "../../actions/settings.actions";
export const AirDCPPHubsForm = (airDCPPClientUserSettings): ReactElement => {
const { settings } = airDCPPClientUserSettings;
const dispatch = useDispatch();
const [hubList, setHubList] = useState([]);
useEffect(() => {
if (!isEmpty(settings)) {
axios({
url: `${settings.directConnect.client.host.protocol}://${settings.directConnect.client.host.hostname}/api/v1/hubs`,
method: "GET",
headers: {
Authorization: `${settings.directConnect.client.airDCPPUserSettings.auth_token}`,
},
}).then((hubs) => {
const hubSelectionOptions = hubs.data.map(({ hub_url, identity }) => ({
value: hub_url,
label: identity.name,
}));
setHubList(hubSelectionOptions);
});
}
}, []);
const onSubmit = (values) => {
if (!isUndefined(values.hubs)) {
dispatch(saveSettings({ hubs: values.hubs }, settings._id));
}
};
const validate = async () => {};
const SelectAdapter = ({ input, ...rest }) => {
return <Select {...input} {...rest} isClearable isMulti />;
};
return (
<>
<Form
onSubmit={onSubmit}
validate={validate}
render={({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<div>
<h3 className="title">Hubs</h3>
<h6 className="subtitle has-text-grey-light">
Select the hubs you want to perform searches against.
</h6>
</div>
<div className="field">
<label className="label">AirDC++ Host</label>
<div className="control">
<Field
name="hubs"
component={SelectAdapter}
className="basic-multi-select"
placeholder="Select Hubs to Search Against"
options={hubList}
/>
</div>
</div>
<button type="submit" className="button is-primary">
Submit
</button>
</form>
)}
/>
<div className="mt-4">
<article className="message is-warning">
<div className="message-body is-size-6 is-family-secondary">
Your selection in the dropdown <strong>will replace</strong> the
existing selection.
</div>
</article>
</div>
<div className="box mt-3">
<h6>Selected hubs</h6>
{settings.directConnect.client.hubs.map(({ value, label }) => (
<div key={value}>
<div>{label}</div>
<span className="is-size-7">{value}</span>
</div>
))}
</div>
</>
);
};
export default AirDCPPHubsForm;

View File

@@ -2,6 +2,7 @@ import React, { ReactElement } from "react";
export const AirDCPPSettingsConfirmation = (settingsObject): ReactElement => {
const { settings } = settingsObject;
return (
<div className="mt-4 is-clearfix">
<div className="card">
@@ -15,21 +16,21 @@ export const AirDCPPSettingsConfirmation = (settingsObject): ReactElement => {
<dt>
Client version:{" "}
{
settings.directConnect.client.airdcppUserSettings.system_info
settings.directConnect.client.airDCPPUserSettings.system_info
.client_version
}
</dt>
<dt>
Hostname:{" "}
{
settings.directConnect.client.airdcppUserSettings.system_info
settings.directConnect.client.airDCPPUserSettings.system_info
.hostname
}
</dt>
<dt>
Platform:{" "}
{
settings.directConnect.client.airdcppUserSettings.system_info
settings.directConnect.client.airDCPPUserSettings.system_info
.platform
}
</dt>
@@ -37,7 +38,7 @@ export const AirDCPPSettingsConfirmation = (settingsObject): ReactElement => {
<dt>
Username:{" "}
{
settings.directConnect.client.airdcppUserSettings.user
settings.directConnect.client.airDCPPUserSettings.user
.username
}
</dt>
@@ -45,7 +46,7 @@ export const AirDCPPSettingsConfirmation = (settingsObject): ReactElement => {
<dt>
Active Sessions:{" "}
{
settings.directConnect.client.airdcppUserSettings.user
settings.directConnect.client.airDCPPUserSettings.user
.active_sessions
}
</dt>
@@ -53,7 +54,7 @@ export const AirDCPPSettingsConfirmation = (settingsObject): ReactElement => {
Permissions:{" "}
<pre>
{JSON.stringify(
settings.directConnect.client.airdcppUserSettings.user
settings.directConnect.client.airDCPPUserSettings.user
.permissions,
undefined,
2,

View File

@@ -0,0 +1,145 @@
import React, { ReactElement, useCallback, useContext, useEffect } from "react";
import { Form, Field } from "react-final-form";
import { useDispatch } from "react-redux";
import { saveSettings, deleteSettings } from "../../actions/settings.actions";
import { AirDCPPSettingsConfirmation } from "./AirDCPPSettingsConfirmation";
import axios from "axios";
import { AirDCPPSocketContext } from "../../context/AirDCPPSocket";
import AirDCPPSocket from "../../services/DcppSearchService";
import { isUndefined, isEmpty } from "lodash";
export const AirDCPPSettingsForm = (airDCPPClientSettings): ReactElement => {
const { settings } = airDCPPClientSettings;
const dispatch = useDispatch();
const { setADCPPSocket } = useContext(AirDCPPSocketContext);
const onSubmit = async (values) => {
try {
const airDCPPResponse = await axios({
url: `${values.protocol}://${values.hostname}/api/v1/sessions/authorize`,
method: "POST",
data: {
username: values.username,
password: values.password,
},
});
if (airDCPPResponse.status === 200) {
dispatch(
saveSettings({
host: values,
airDCPPUserSettings: airDCPPResponse.data,
}),
);
setADCPPSocket(
new AirDCPPSocket({
hostname: `${values.hostname}`,
}),
);
}
} catch (error) {
console.log(error);
}
};
const removeSettings = useCallback(async () => {
dispatch(deleteSettings());
setADCPPSocket({});
}, []);
const validate = async () => {};
const initFormData =
!isEmpty(settings.directConnect) || !isUndefined(settings.directConnect)
? settings.directConnect.client.host
: {};
return (
<>
<Form
onSubmit={onSubmit}
validate={validate}
initialValues={initFormData}
render={({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<h2>AirDC++ Connection Information</h2>
<label className="label">AirDC++ Hostname</label>
<div className="field has-addons">
<p className="control">
<span className="select">
<Field name="protocol" component="select">
<option>Protocol</option>
<option value="http">http://</option>
<option value="https">https://</option>
</Field>
</span>
</p>
<p className="control is-expanded">
<Field
name="hostname"
component="input"
className="input"
placeholder="AirDC++ host IP / hostname"
/>
</p>
</div>
<div className="field">
<div className="is-clearfix">
<label className="label">Credentials</label>
</div>
<div className="field-body">
<div className="field">
<p className="control is-expanded has-icons-left">
<Field
name="username"
component="input"
className="input"
placeholder="Username"
/>
<span className="icon is-small is-left">
<i className="fa-solid fa-user-ninja"></i>
</span>
</p>
</div>
<div className="field">
<p className="control is-expanded has-icons-left has-icons-right">
<Field
name="password"
component="input"
type="password"
className="input"
placeholder="Password"
/>
<span className="icon is-small is-left">
<i className="fa-solid fa-lock"></i>
</span>
<span className="icon is-small is-right">
<i className="fas fa-check"></i>
</span>
</p>
</div>
</div>
</div>
<div className="field is-grouped">
<p className="control">
<button type="submit" className="button is-primary">
{!isEmpty(initFormData) ? "Update" : "Save"}
</button>
</p>
</div>
</form>
)}
/>
{!isEmpty(settings) ? (
<AirDCPPSettingsConfirmation settings={settings} />
) : null}
{!isEmpty(settings) ? (
<p className="control mt-4">
<button className="button is-danger" onClick={removeSettings}>
Delete
</button>
</p>
) : null}
</>
);
};
export default AirDCPPSettingsForm;