feat: own tab to torbox

This commit is contained in:
Zamitto
2025-02-01 17:42:49 -03:00
parent 7fbaea15e8
commit ebc70ce28f
33 changed files with 256 additions and 141 deletions

View File

@@ -28,6 +28,7 @@ import type {
CatalogueSearchPayload,
LibraryGame,
GameRunning,
TorBoxUser,
} from "@types";
import type { AxiosProgressEvent } from "axios";
import type disk from "diskusage";
@@ -142,6 +143,7 @@ declare global {
minimized: boolean;
}) => Promise<void>;
authenticateRealDebrid: (apiToken: string) => Promise<RealDebridUser>;
authenticateTorBox: (apiToken: string) => Promise<TorBoxUser>;
onAchievementUnlocked: (cb: () => void) => () => Electron.IpcRenderer;
/* Download sources */

View File

@@ -2,7 +2,7 @@ import { useContext, useEffect, useState } from "react";
import { Trans, useTranslation } from "react-i18next";
import { Button, CheckboxField, Link, TextField } from "@renderer/components";
import * as styles from "./settings-debrid.css";
import * as styles from "./settings-real-debrid.css";
import { useAppSelector, useToast } from "@renderer/hooks";
@@ -10,9 +10,8 @@ import { SPACING_UNIT } from "@renderer/theme.css";
import { settingsContext } from "@renderer/context";
const REAL_DEBRID_API_TOKEN_URL = "https://real-debrid.com/apitoken";
const TORBOX_API_TOKEN_URL = "https://torbox.app/settings";
export function SettingsDebrid() {
export function SettingsRealDebrid() {
const userPreferences = useAppSelector(
(state) => state.userPreferences.value
);
@@ -23,8 +22,6 @@ export function SettingsDebrid() {
const [form, setForm] = useState({
useRealDebrid: false,
realDebridApiToken: null as string | null,
useTorBox: false,
torBoxApiToken: null as string | null,
});
const { showSuccessToast, showErrorToast } = useToast();
@@ -36,8 +33,6 @@ export function SettingsDebrid() {
setForm({
useRealDebrid: Boolean(userPreferences.realDebridApiToken),
realDebridApiToken: userPreferences.realDebridApiToken ?? null,
useTorBox: Boolean(userPreferences.torBoxApiToken),
torBoxApiToken: userPreferences.torBoxApiToken ?? null,
});
}
}, [userPreferences]);
@@ -62,7 +57,7 @@ export function SettingsDebrid() {
return;
} else {
showSuccessToast(
t("real_debrid_linked_message", { username: user.username })
t("debrid_linked_message", { username: user.username })
);
}
} else {
@@ -71,10 +66,9 @@ export function SettingsDebrid() {
updateUserPreferences({
realDebridApiToken: form.useRealDebrid ? form.realDebridApiToken : null,
torBoxApiToken: form.useTorBox ? form.torBoxApiToken : null,
});
} catch (err) {
showErrorToast(t("real_debrid_invalid_token"));
showErrorToast(t("debrid_invalid_token"));
} finally {
setIsLoading(false);
}
@@ -100,14 +94,18 @@ export function SettingsDebrid() {
{form.useRealDebrid && (
<TextField
label={t("real_debrid_api_token")}
label={t("api_token")}
value={form.realDebridApiToken ?? ""}
type="password"
onChange={(event) =>
setForm({ ...form, realDebridApiToken: event.target.value })
}
placeholder="API Token"
containerProps={{ style: { marginTop: `${SPACING_UNIT}px` } }}
containerProps={{
style: {
marginTop: `${SPACING_UNIT}px`,
},
}}
rightContent={
<Button
type="submit"
@@ -120,52 +118,12 @@ export function SettingsDebrid() {
</Button>
}
hint={
<Trans i18nKey="real_debrid_api_token_hint" ns="settings">
<Trans i18nKey="debrid_api_token_hint" ns="settings">
<Link to={REAL_DEBRID_API_TOKEN_URL} />
</Trans>
}
/>
)}
<CheckboxField
label={t("enable_torbox")}
checked={form.useTorBox}
onChange={() =>
setForm((prev) => ({
...prev,
useTorBox: !form.useTorBox,
}))
}
/>
{form.useTorBox && (
<TextField
label={t("real_debrid_api_token")}
value={form.torBoxApiToken ?? ""}
type="password"
onChange={(event) =>
setForm({ ...form, torBoxApiToken: event.target.value })
}
placeholder="API Token"
containerProps={{ style: { marginTop: `${SPACING_UNIT}px` } }}
rightContent={
<Button
type="submit"
style={{
alignSelf: "flex-end",
}}
disabled={isButtonDisabled}
>
{t("save")}
</Button>
}
hint={
<Trans i18nKey="real_debrid_api_token_hint" ns="settings">
<Link to={TORBOX_API_TOKEN_URL} />
</Trans>
}
/>
)}
</form>
);
}

View File

@@ -0,0 +1,13 @@
import { style } from "@vanilla-extract/css";
import { SPACING_UNIT } from "../../theme.css";
export const form = style({
display: "flex",
flexDirection: "column",
gap: `${SPACING_UNIT}px`,
});
export const description = style({
marginBottom: `${SPACING_UNIT * 2}px`,
});

View File

@@ -0,0 +1,119 @@
import { useContext, useEffect, useState } from "react";
import { Trans, useTranslation } from "react-i18next";
import { Button, CheckboxField, Link, TextField } from "@renderer/components";
import * as styles from "./settings-torbox.css";
import { useAppSelector, useToast } from "@renderer/hooks";
import { SPACING_UNIT } from "@renderer/theme.css";
import { settingsContext } from "@renderer/context";
const TORBOX_API_TOKEN_URL = "https://torbox.app/settings";
export function SettingsTorbox() {
const userPreferences = useAppSelector(
(state) => state.userPreferences.value
);
const { updateUserPreferences } = useContext(settingsContext);
const [isLoading, setIsLoading] = useState(false);
const [form, setForm] = useState({
useTorBox: false,
torBoxApiToken: null as string | null,
});
const { showSuccessToast, showErrorToast } = useToast();
const { t } = useTranslation("settings");
useEffect(() => {
if (userPreferences) {
setForm({
useTorBox: Boolean(userPreferences.torBoxApiToken),
torBoxApiToken: userPreferences.torBoxApiToken ?? null,
});
}
}, [userPreferences]);
const handleFormSubmit: React.FormEventHandler<HTMLFormElement> = async (
event
) => {
setIsLoading(true);
event.preventDefault();
try {
if (form.useTorBox) {
const user = await window.electron.authenticateTorBox(
form.torBoxApiToken!
);
showSuccessToast(t("debrid_linked_message", { username: user.email }));
} else {
showSuccessToast(t("changes_saved"));
}
updateUserPreferences({
torBoxApiToken: form.useTorBox ? form.torBoxApiToken : null,
});
} catch (err) {
showErrorToast(t("debrid_invalid_token"));
} finally {
setIsLoading(false);
}
};
const isButtonDisabled =
(form.useTorBox && !form.torBoxApiToken) || isLoading;
return (
<form className={styles.form} onSubmit={handleFormSubmit}>
<p className={styles.description}>{t("torbox_description")}</p>
<CheckboxField
label={t("enable_torbox")}
checked={form.useTorBox}
onChange={() =>
setForm((prev) => ({
...prev,
useTorBox: !form.useTorBox,
}))
}
/>
{form.useTorBox && (
<TextField
label={t("api_token")}
value={form.torBoxApiToken ?? ""}
type="password"
onChange={(event) =>
setForm({ ...form, torBoxApiToken: event.target.value })
}
placeholder="API Token"
containerProps={{
style: {
marginTop: `${SPACING_UNIT}px`,
},
}}
rightContent={
<Button
type="submit"
style={{
alignSelf: "flex-end",
}}
disabled={isButtonDisabled}
>
{t("save")}
</Button>
}
hint={
<Trans i18nKey="debrid_api_token_hint" ns="settings">
<Link to={TORBOX_API_TOKEN_URL} />
</Trans>
}
/>
)}
</form>
);
}

View File

@@ -2,7 +2,7 @@ import { Button } from "@renderer/components";
import * as styles from "./settings.css";
import { useTranslation } from "react-i18next";
import { SettingsDebrid } from "./settings-debrid";
import { SettingsRealDebrid } from "./settings-real-debrid";
import { SettingsGeneral } from "./settings-general";
import { SettingsBehavior } from "./settings-behavior";
@@ -14,6 +14,7 @@ import {
import { SettingsAccount } from "./settings-account";
import { useUserDetails } from "@renderer/hooks";
import { useMemo } from "react";
import { SettingsTorbox } from "./settings-torbox";
export default function Settings() {
const { t } = useTranslation("settings");
@@ -25,7 +26,8 @@ export default function Settings() {
t("general"),
t("behavior"),
t("download_sources"),
t("debrid_services"),
"Torbox",
"Real-Debrid",
];
if (userDetails) return [...categories, t("account")];
@@ -50,7 +52,11 @@ export default function Settings() {
}
if (currentCategoryIndex === 3) {
return <SettingsDebrid />;
return <SettingsTorbox />;
}
if (currentCategoryIndex === 4) {
return <SettingsRealDebrid />;
}
return <SettingsAccount />;