⚙️Settings-driven AirDC++ configuration first draft
This commit is contained in:
@@ -30,10 +30,16 @@ function sleep(ms: number): Promise<NodeJS.Timeout> {
|
||||
}
|
||||
|
||||
export const search =
|
||||
(data: SearchData, ADCPPSocket: any) => async (dispatch) => {
|
||||
(data: SearchData, ADCPPSocket: any, credentials: any) =>
|
||||
async (dispatch) => {
|
||||
try {
|
||||
console.log(credentials);
|
||||
if (!ADCPPSocket.isConnected()) {
|
||||
await ADCPPSocket.connect("admin", "password", true);
|
||||
await ADCPPSocket.connect(
|
||||
credentials.username,
|
||||
credentials.password,
|
||||
true,
|
||||
);
|
||||
}
|
||||
const instance: SearchInstance = await ADCPPSocket.post("search");
|
||||
dispatch({
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import axios from "axios";
|
||||
import { SETTINGS_OBJECT_FETCHED } from "../constants/action-types";
|
||||
import {
|
||||
SETTINGS_OBJECT_FETCHED,
|
||||
SETTINGS_OBJECT_DELETED,
|
||||
} from "../constants/action-types";
|
||||
import { SETTINGS_SERVICE_BASE_URI } from "../constants/endpoints";
|
||||
|
||||
export const saveSettings =
|
||||
@@ -29,3 +32,17 @@ export const getSettings = (settingsKey?) => async (dispatch) => {
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteSettings = () => async (dispatch) => {
|
||||
const result = await axios({
|
||||
url: `${SETTINGS_SERVICE_BASE_URI}/deleteSettings`,
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
if (result.data.ok === 1) {
|
||||
dispatch({
|
||||
type: SETTINGS_OBJECT_FETCHED,
|
||||
data: {},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import React, { useCallback, useContext, ReactElement } from "react";
|
||||
import React, { useCallback, useContext, ReactElement, useEffect } from "react";
|
||||
import {
|
||||
search,
|
||||
downloadAirDCPPItem,
|
||||
getBundlesForComic,
|
||||
} from "../actions/airdcpp.actions";
|
||||
import { SocketContext } from "../context/AirDCPPSocket";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { RootState, SearchInstance } from "threetwo-ui-typings";
|
||||
import ellipsize from "ellipsize";
|
||||
import { isEmpty, isNil, isUndefined, map } from "lodash";
|
||||
import { AirDCPPSocketContext } from "../context/AirDCPPSocket";
|
||||
import { getSettings } from "../actions/settings.actions";
|
||||
import AirDCPPSocket from "../services/DcppSearchService";
|
||||
interface IAcquisitionPanelProps {
|
||||
comicBookMetadata: any;
|
||||
}
|
||||
@@ -21,7 +23,6 @@ export const AcquisitionPanel = (
|
||||
const sanitizedVolumeName = volumeName.replace(/[^a-zA-Z0-9 ]/g, "");
|
||||
const issueName = props.comicBookMetadata.sourcedMetadata.comicvine.name;
|
||||
|
||||
// local state
|
||||
// Selectors for picking state
|
||||
const airDCPPSearchResults = useSelector((state: RootState) => {
|
||||
return state.airdcpp.searchResults;
|
||||
@@ -35,18 +36,31 @@ export const AcquisitionPanel = (
|
||||
const searchInstance: SearchInstance = useSelector(
|
||||
(state: RootState) => state.airdcpp.searchInstance,
|
||||
);
|
||||
const airDCPPClientSettings = useSelector(
|
||||
(state: RootState) => state.settings.data,
|
||||
);
|
||||
|
||||
const userSettings = useSelector((state: RootState) => state.settings.data);
|
||||
const { ADCPPSocket, setADCPPSocket } = useContext(AirDCPPSocketContext);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const ADCPPSocket = useContext(SocketContext);
|
||||
useEffect(() => {
|
||||
dispatch(getSettings());
|
||||
}, []);
|
||||
|
||||
if (isEmpty(ADCPPSocket) && !isEmpty(userSettings)) {
|
||||
setADCPPSocket(
|
||||
new AirDCPPSocket({
|
||||
hostname: `${userSettings.directConnect.client.hostname}`,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const getDCPPSearchResults = useCallback(
|
||||
(searchQuery) => {
|
||||
console.log(ADCPPSocket);
|
||||
dispatch(search(searchQuery, ADCPPSocket));
|
||||
dispatch(
|
||||
search(searchQuery, ADCPPSocket, {
|
||||
username: `${userSettings.directConnect.client.username}`,
|
||||
password: `${userSettings.directConnect.client.password}`,
|
||||
}),
|
||||
);
|
||||
},
|
||||
[dispatch, ADCPPSocket],
|
||||
);
|
||||
@@ -80,8 +94,9 @@ export const AcquisitionPanel = (
|
||||
|
||||
return (
|
||||
<>
|
||||
{JSON.stringify(ADCPPSocket)}
|
||||
<div className="comic-detail columns">
|
||||
{!isEmpty(ADCPPSocket) && !isUndefined(ADCPPSocket) ? (
|
||||
{!isEmpty(ADCPPSocket) ? (
|
||||
<div className="column is-one-fifth">
|
||||
<button
|
||||
className={
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
import React, { ReactElement, useEffect } from "react";
|
||||
import React, { ReactElement, useCallback, useContext, useEffect } from "react";
|
||||
import { Form, Field } from "react-final-form";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { saveSettings, getSettings } from "../actions/settings.actions";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import {
|
||||
getSettings,
|
||||
saveSettings,
|
||||
deleteSettings,
|
||||
} from "../actions/settings.actions";
|
||||
import { AirDCPPSettingsConfirmation } from "./AirDCPPSettings/AirDCPPSettingsConfirmation";
|
||||
import axios from "axios";
|
||||
import { AirDCPPSocketContext } from "../context/AirDCPPSocket";
|
||||
import AirDCPPSocket from "../services/DcppSearchService";
|
||||
import { isUndefined, isEmpty } from "lodash";
|
||||
|
||||
export const AirDCPPSettingsForm = (): ReactElement => {
|
||||
const airdcppClientSettings = useSelector(
|
||||
const airDCPPClientSettings = useSelector(
|
||||
(state: RootState) => state.settings.data,
|
||||
);
|
||||
|
||||
const { ADCPPSocket, setADCPPSocket } = useContext(AirDCPPSocketContext);
|
||||
const dispatch = useDispatch();
|
||||
useEffect(() => {
|
||||
dispatch(getSettings());
|
||||
}, [dispatch]);
|
||||
|
||||
}, []);
|
||||
const onSubmit = async (values) => {
|
||||
try {
|
||||
const fqdn = values.protocol + values.hostname;
|
||||
const airdcppResponse = await axios({
|
||||
const airDCPPResponse = await axios({
|
||||
url: `${fqdn}/api/v1/sessions/authorize`,
|
||||
method: "POST",
|
||||
data: {
|
||||
@@ -27,18 +33,37 @@ export const AirDCPPSettingsForm = (): ReactElement => {
|
||||
password: values.password,
|
||||
},
|
||||
});
|
||||
if (airdcppResponse.status === 200) {
|
||||
dispatch(saveSettings(values, airdcppResponse.data));
|
||||
if (airDCPPResponse.status === 200) {
|
||||
dispatch(saveSettings(values, airDCPPResponse.data));
|
||||
setADCPPSocket(
|
||||
new AirDCPPSocket({
|
||||
hostname: `${values.hostname}`,
|
||||
}),
|
||||
);
|
||||
const hubList = await axios({
|
||||
url: `${fqdn}/api/v1/hubs`,
|
||||
method: "GET",
|
||||
params: {
|
||||
username: values.username,
|
||||
password: values.password,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
const removeSettings = useCallback(async () => {
|
||||
dispatch(deleteSettings());
|
||||
setADCPPSocket({});
|
||||
}, []);
|
||||
|
||||
const validate = async () => {};
|
||||
const initFormData =
|
||||
!isEmpty(airdcppClientSettings.directConnect) ||
|
||||
!isUndefined(airdcppClientSettings.directConnect)
|
||||
? airdcppClientSettings.directConnect.client
|
||||
!isEmpty(airDCPPClientSettings.directConnect) ||
|
||||
!isUndefined(airDCPPClientSettings.directConnect)
|
||||
? airDCPPClientSettings.directConnect.client
|
||||
: {};
|
||||
return (
|
||||
<>
|
||||
@@ -112,17 +137,19 @@ export const AirDCPPSettingsForm = (): ReactElement => {
|
||||
{!isEmpty(initFormData) ? "Update" : "Save"}
|
||||
</button>
|
||||
</p>
|
||||
{!isUndefined(airdcppClientSettings) ? (
|
||||
<p className="control">
|
||||
<button className="button is-danger">Delete</button>
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
/>
|
||||
{!isEmpty(airdcppClientSettings) ? (
|
||||
<AirDCPPSettingsConfirmation settings={airdcppClientSettings} />
|
||||
{!isEmpty(airDCPPClientSettings) ? (
|
||||
<p className="control">
|
||||
<button className="button is-danger" onClick={removeSettings}>
|
||||
Delete
|
||||
</button>
|
||||
</p>
|
||||
) : null}
|
||||
{!isEmpty(airDCPPClientSettings) ? (
|
||||
<AirDCPPSettingsConfirmation settings={airDCPPClientSettings} />
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ReactElement } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import React, { ReactElement, useEffect, useState } from "react";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { hot } from "react-hot-loader";
|
||||
import Dashboard from "./Dashboard";
|
||||
|
||||
@@ -14,6 +14,10 @@ import { Switch, Route } from "react-router";
|
||||
import Navbar from "./Navbar";
|
||||
import "../assets/scss/App.scss";
|
||||
import Notifications from "react-notification-system-redux";
|
||||
import { getSettings } from "../actions/settings.actions";
|
||||
import { AirDCPPSocketContext } from "../context/AirDCPPSocket";
|
||||
import { isEmpty, isUndefined } from "lodash";
|
||||
import AirDCPPSocket from "../services/DcppSearchService";
|
||||
|
||||
//Optional styling
|
||||
const style = {
|
||||
@@ -58,39 +62,48 @@ const style = {
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const App = (): ReactElement => {
|
||||
const notifications = useSelector((state: RootState) => state.notifications);
|
||||
|
||||
const [ADCPPSocket, setADCPPSocket] = useState({});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Navbar />
|
||||
<Notifications
|
||||
notifications={notifications}
|
||||
style={style}
|
||||
newOnTop={true}
|
||||
allowHTML={true}
|
||||
/>
|
||||
<Switch>
|
||||
<Route exact path="/">
|
||||
<Dashboard />
|
||||
</Route>
|
||||
<Route path="/import">
|
||||
<Import path={"./comics"} />
|
||||
</Route>
|
||||
<Route path="/library">
|
||||
<Library />
|
||||
</Route>
|
||||
<Route path="/library-grid">
|
||||
<LibraryGrid />
|
||||
</Route>
|
||||
<Route path="/search">
|
||||
<Search />
|
||||
</Route>
|
||||
<Route path={"/comic/details/:comicObjectId"} component={ComicDetail} />
|
||||
<Route path="/settings">
|
||||
<Settings />
|
||||
</Route>
|
||||
</Switch>
|
||||
</div>
|
||||
<AirDCPPSocketContext.Provider value={{ ADCPPSocket, setADCPPSocket }}>
|
||||
<div>
|
||||
<Navbar />
|
||||
<Notifications
|
||||
notifications={notifications}
|
||||
style={style}
|
||||
newOnTop={true}
|
||||
allowHTML={true}
|
||||
/>
|
||||
<Switch>
|
||||
<Route exact path="/">
|
||||
<Dashboard />
|
||||
</Route>
|
||||
<Route path="/import">
|
||||
<Import path={"./comics"} />
|
||||
</Route>
|
||||
<Route path="/library">
|
||||
<Library />
|
||||
</Route>
|
||||
<Route path="/library-grid">
|
||||
<LibraryGrid />
|
||||
</Route>
|
||||
<Route path="/search">
|
||||
<Search />
|
||||
</Route>
|
||||
<Route
|
||||
path={"/comic/details/:comicObjectId"}
|
||||
component={ComicDetail}
|
||||
/>
|
||||
<Route path="/settings">
|
||||
<Settings />
|
||||
</Route>
|
||||
</Switch>
|
||||
</div>
|
||||
</AirDCPPSocketContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
} from "../actions/airdcpp.actions";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { RootState } from "threetwo-ui-typings";
|
||||
import { SocketContext } from "../context/AirDCPPSocket";
|
||||
import { isNil, map } from "lodash";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import dayjs from "dayjs";
|
||||
@@ -27,6 +26,7 @@ export const DownloadsPanel = (
|
||||
});
|
||||
console.log("BANDYA", bundles);
|
||||
const ADCPPSocket = useContext(SocketContext);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
useEffect(() => {
|
||||
dispatch(getBundlesForComic(props.comicObjectId, ADCPPSocket));
|
||||
|
||||
@@ -67,3 +67,4 @@ export const LS_COVER_EXTRACTED = "LS_COVER_EXTRACTED";
|
||||
export const SETTINGS_CALL_IN_PROGRESS = "SETTINGS_CALL_IN_PROGRESS";
|
||||
export const SETTINGS_OBJECT_FETCHED = "SETTINGS_OBJECT_FETCHED";
|
||||
export const SETTINGS_CALL_FAILED = "SETTINGS_CALL_FAILED";
|
||||
export const SETTINGS_OBJECT_DELETED = "SETTINGS_OBJECT_DELETED";
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
import React from "react";
|
||||
import AirDCPPSocket from "../services/DcppSearchService";
|
||||
import axios from "axios";
|
||||
import { SETTINGS_SERVICE_BASE_URI } from "../constants/endpoints";
|
||||
|
||||
const socketInitConfiguration = await axios({
|
||||
url: `${SETTINGS_SERVICE_BASE_URI}/getSettings`,
|
||||
method: "POST",
|
||||
});
|
||||
const AirDCPPSocketContext = React.createContext(null);
|
||||
|
||||
export const airDCPPSocket = new AirDCPPSocket({
|
||||
hostname: `${socketInitConfiguration.data.directConnect.client.hostname}`,
|
||||
});
|
||||
|
||||
export const SocketContext = React.createContext(airDCPPSocket);
|
||||
export { AirDCPPSocketContext };
|
||||
|
||||
@@ -3,18 +3,17 @@ import { render } from "react-dom";
|
||||
import { Provider } from "react-redux";
|
||||
import { ConnectedRouter } from "connected-react-router";
|
||||
import configureStore, { history } from "./store/index";
|
||||
import { SocketContext, airDCPPSocket } from "./context/AirDCPPSocket";
|
||||
|
||||
import App from "./components/App";
|
||||
const store = configureStore({});
|
||||
|
||||
const rootEl = document.getElementById("root");
|
||||
|
||||
render(
|
||||
<SocketContext.Provider value={airDCPPSocket}>
|
||||
<Provider store={store}>
|
||||
<ConnectedRouter history={history}>
|
||||
<App />
|
||||
</ConnectedRouter>
|
||||
</Provider>
|
||||
</SocketContext.Provider>,
|
||||
<Provider store={store}>
|
||||
<ConnectedRouter history={history}>
|
||||
<App />
|
||||
</ConnectedRouter>
|
||||
</Provider>,
|
||||
rootEl,
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
SETTINGS_CALL_FAILED,
|
||||
SETTINGS_OBJECT_FETCHED,
|
||||
SETTINGS_OBJECT_DELETED,
|
||||
SETTINGS_CALL_IN_PROGRESS,
|
||||
} from "../constants/action-types";
|
||||
const initialState = {
|
||||
@@ -23,6 +24,13 @@ function settingsReducer(state = initialState, action) {
|
||||
inProgress: false,
|
||||
};
|
||||
|
||||
case SETTINGS_OBJECT_DELETED:
|
||||
return {
|
||||
...state,
|
||||
data: action.data,
|
||||
inProgress: false,
|
||||
};
|
||||
|
||||
default:
|
||||
return { ...state };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user