mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-03-09 15:40:26 +00:00
feat: manage account buttons
This commit is contained in:
parent
d4be5b8c66
commit
af4fcb8f06
12 changed files with 286 additions and 149 deletions
|
@ -280,7 +280,18 @@
|
||||||
"launch_minimized": "Launch Hydra minimized",
|
"launch_minimized": "Launch Hydra minimized",
|
||||||
"disable_nsfw_alert": "Disable NSFW alert",
|
"disable_nsfw_alert": "Disable NSFW alert",
|
||||||
"seed_after_download_complete": "Seed after download complete",
|
"seed_after_download_complete": "Seed after download complete",
|
||||||
"show_hidden_achievement_description": "Show hidden achievements description before unlocking them"
|
"show_hidden_achievement_description": "Show hidden achievements description before unlocking them",
|
||||||
|
"account": "Account",
|
||||||
|
"no_users_blocked": "You have no blocked users",
|
||||||
|
"subscription": "Hydra Cloud subscription",
|
||||||
|
"subscription_active_until": "Your Hydra Cloud is active until {{date}}",
|
||||||
|
"subscription_not_active": "You don't have an active Hydra Cloud subscription",
|
||||||
|
"manage_account": "Manage account",
|
||||||
|
"manage_subscription": "Manage subscription",
|
||||||
|
"update_email": "Update email",
|
||||||
|
"update_password": "Update password",
|
||||||
|
"current_email": "Current email:",
|
||||||
|
"no_associated_email": "You don't have an associated email yet"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "Download complete",
|
"download_complete": "Download complete",
|
||||||
|
|
|
@ -268,7 +268,18 @@
|
||||||
"launch_minimized": "Iniciar o Hydra minimizado",
|
"launch_minimized": "Iniciar o Hydra minimizado",
|
||||||
"disable_nsfw_alert": "Desativar alerta de conteúdo inapropriado",
|
"disable_nsfw_alert": "Desativar alerta de conteúdo inapropriado",
|
||||||
"seed_after_download_complete": "Semear após a conclusão do download",
|
"seed_after_download_complete": "Semear após a conclusão do download",
|
||||||
"show_hidden_achievement_description": "Mostrar descrição de conquistas ocultas antes de debloqueá-las"
|
"show_hidden_achievement_description": "Mostrar descrição de conquistas ocultas antes de debloqueá-las",
|
||||||
|
"account": "Conta",
|
||||||
|
"no_users_blocked": "Você não bloqueou nenhum usuário",
|
||||||
|
"subscription": "Assinatura Hydra Cloud",
|
||||||
|
"subscription_active_until": "Seu Hydra Cloud ficará ativo até {{date}}",
|
||||||
|
"subscription_not_active": "Você não possui uma assinatura Hydra Cloud ativa",
|
||||||
|
"manage_account": "Gerenciar conta",
|
||||||
|
"manage_subscription": "Gerenciar assinatura",
|
||||||
|
"update_email": "Atualizar email",
|
||||||
|
"update_password": "Atualizar senha",
|
||||||
|
"current_email": "Email atual:",
|
||||||
|
"no_associated_email": "Você ainda não associou nenhum email a sua conta"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "Download concluído",
|
"download_complete": "Download concluído",
|
||||||
|
|
|
@ -30,6 +30,7 @@ import "./library/remove-game-from-library";
|
||||||
import "./library/select-game-wine-prefix";
|
import "./library/select-game-wine-prefix";
|
||||||
import "./library/reset-game-achievements";
|
import "./library/reset-game-achievements";
|
||||||
import "./misc/open-checkout";
|
import "./misc/open-checkout";
|
||||||
|
import "./misc/open-manage-account";
|
||||||
import "./misc/open-external";
|
import "./misc/open-external";
|
||||||
import "./misc/show-open-dialog";
|
import "./misc/show-open-dialog";
|
||||||
import "./misc/get-features";
|
import "./misc/get-features";
|
||||||
|
|
25
src/main/events/misc/open-manage-account.ts
Normal file
25
src/main/events/misc/open-manage-account.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { shell } from "electron";
|
||||||
|
import { registerEvent } from "../register-event";
|
||||||
|
import { HydraApi, logger } from "@main/services";
|
||||||
|
import { ManageAccountPage } from "@types";
|
||||||
|
|
||||||
|
const openManageAccount = async (
|
||||||
|
_event: Electron.IpcMainInvokeEvent,
|
||||||
|
page: ManageAccountPage
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const { accessToken } = await HydraApi.refreshToken();
|
||||||
|
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
token: accessToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
shell.openExternal(
|
||||||
|
`${import.meta.env.MAIN_VITE_AUTH_URL}/${page}?${params.toString()}`
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
logger.error("Failed to open manage account", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
registerEvent("openManageAccount", openManageAccount);
|
|
@ -215,16 +215,20 @@ export class HydraApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async refreshToken() {
|
||||||
|
return this.instance
|
||||||
|
.post<{ accessToken: string; expiresIn: number }>(`/auth/refresh`, {
|
||||||
|
refreshToken: this.userAuth.refreshToken,
|
||||||
|
})
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
private static async revalidateAccessTokenIfExpired() {
|
private static async revalidateAccessTokenIfExpired() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
||||||
if (this.userAuth.expirationTimestamp < now.getTime()) {
|
if (this.userAuth.expirationTimestamp < now.getTime()) {
|
||||||
try {
|
try {
|
||||||
const response = await this.instance.post(`/auth/refresh`, {
|
const { accessToken, expiresIn } = await this.refreshToken();
|
||||||
refreshToken: this.userAuth.refreshToken,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { accessToken, expiresIn } = response.data;
|
|
||||||
|
|
||||||
const tokenExpirationTimestamp =
|
const tokenExpirationTimestamp =
|
||||||
now.getTime() +
|
now.getTime() +
|
||||||
|
|
|
@ -14,6 +14,7 @@ import type {
|
||||||
CatalogueSearchPayload,
|
CatalogueSearchPayload,
|
||||||
SeedingStatus,
|
SeedingStatus,
|
||||||
GameAchievement,
|
GameAchievement,
|
||||||
|
ManageAccountPage,
|
||||||
} from "@types";
|
} from "@types";
|
||||||
import type { CatalogueCategory } from "@shared";
|
import type { CatalogueCategory } from "@shared";
|
||||||
import type { AxiosProgressEvent } from "axios";
|
import type { AxiosProgressEvent } from "axios";
|
||||||
|
@ -226,6 +227,8 @@ contextBridge.exposeInMainWorld("electron", {
|
||||||
isPortableVersion: () => ipcRenderer.invoke("isPortableVersion"),
|
isPortableVersion: () => ipcRenderer.invoke("isPortableVersion"),
|
||||||
openExternal: (src: string) => ipcRenderer.invoke("openExternal", src),
|
openExternal: (src: string) => ipcRenderer.invoke("openExternal", src),
|
||||||
openCheckout: () => ipcRenderer.invoke("openCheckout"),
|
openCheckout: () => ipcRenderer.invoke("openCheckout"),
|
||||||
|
openManageAccount: (page: ManageAccountPage) =>
|
||||||
|
ipcRenderer.invoke("openManageAccount", page),
|
||||||
showOpenDialog: (options: Electron.OpenDialogOptions) =>
|
showOpenDialog: (options: Electron.OpenDialogOptions) =>
|
||||||
ipcRenderer.invoke("showOpenDialog", options),
|
ipcRenderer.invoke("showOpenDialog", options),
|
||||||
showItemInFolder: (path: string) =>
|
showItemInFolder: (path: string) =>
|
||||||
|
|
2
src/renderer/src/declaration.d.ts
vendored
2
src/renderer/src/declaration.d.ts
vendored
|
@ -29,6 +29,7 @@ import type {
|
||||||
UserAchievement,
|
UserAchievement,
|
||||||
ComparedAchievements,
|
ComparedAchievements,
|
||||||
CatalogueSearchPayload,
|
CatalogueSearchPayload,
|
||||||
|
ManageAccountPage,
|
||||||
} from "@types";
|
} from "@types";
|
||||||
import type { AxiosProgressEvent } from "axios";
|
import type { AxiosProgressEvent } from "axios";
|
||||||
import type disk from "diskusage";
|
import type disk from "diskusage";
|
||||||
|
@ -187,6 +188,7 @@ declare global {
|
||||||
/* Misc */
|
/* Misc */
|
||||||
openExternal: (src: string) => Promise<void>;
|
openExternal: (src: string) => Promise<void>;
|
||||||
openCheckout: () => Promise<void>;
|
openCheckout: () => Promise<void>;
|
||||||
|
openManageAccount: (page: ManageAccountPage) => Promise<void>;
|
||||||
getVersion: () => Promise<string>;
|
getVersion: () => Promise<string>;
|
||||||
isStaging: () => Promise<boolean>;
|
isStaging: () => Promise<boolean>;
|
||||||
ping: () => string;
|
ping: () => string;
|
||||||
|
|
217
src/renderer/src/pages/settings/settings-account.tsx
Normal file
217
src/renderer/src/pages/settings/settings-account.tsx
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
import { Button, SelectField } from "@renderer/components";
|
||||||
|
import { SPACING_UNIT, vars } from "@renderer/theme.css";
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import * as styles from "./settings-account.css";
|
||||||
|
import { useDate, useToast, useUserDetails } from "@renderer/hooks";
|
||||||
|
import { useCallback, useContext, useEffect, useState } from "react";
|
||||||
|
import {
|
||||||
|
CloudIcon,
|
||||||
|
KeyIcon,
|
||||||
|
MailIcon,
|
||||||
|
XCircleFillIcon,
|
||||||
|
} from "@primer/octicons-react";
|
||||||
|
import { settingsContext } from "@renderer/context";
|
||||||
|
|
||||||
|
interface FormValues {
|
||||||
|
profileVisibility: "PUBLIC" | "FRIENDS" | "PRIVATE";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SettingsAccount() {
|
||||||
|
const { t } = useTranslation("settings");
|
||||||
|
|
||||||
|
const [isUnblocking, setIsUnblocking] = useState(false);
|
||||||
|
|
||||||
|
const { showSuccessToast } = useToast();
|
||||||
|
|
||||||
|
const { blockedUsers, fetchBlockedUsers } = useContext(settingsContext);
|
||||||
|
|
||||||
|
const { formatDate } = useDate();
|
||||||
|
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
formState: { isSubmitting },
|
||||||
|
setValue,
|
||||||
|
handleSubmit,
|
||||||
|
} = useForm<FormValues>();
|
||||||
|
|
||||||
|
const { patchUser, userDetails } = useUserDetails();
|
||||||
|
|
||||||
|
const { unblockUser } = useUserDetails();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (userDetails?.profileVisibility) {
|
||||||
|
setValue("profileVisibility", userDetails.profileVisibility);
|
||||||
|
}
|
||||||
|
}, [userDetails, setValue]);
|
||||||
|
|
||||||
|
const visibilityOptions = [
|
||||||
|
{ value: "PUBLIC", label: t("public") },
|
||||||
|
{ value: "FRIENDS", label: t("friends_only") },
|
||||||
|
{ value: "PRIVATE", label: t("private") },
|
||||||
|
];
|
||||||
|
|
||||||
|
const onSubmit = async (values: FormValues) => {
|
||||||
|
await patchUser(values);
|
||||||
|
showSuccessToast(t("changes_saved"));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUnblockClick = useCallback(
|
||||||
|
(id: string) => {
|
||||||
|
setIsUnblocking(true);
|
||||||
|
|
||||||
|
unblockUser(id)
|
||||||
|
.then(() => {
|
||||||
|
fetchBlockedUsers();
|
||||||
|
showSuccessToast(t("user_unblocked"));
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setIsUnblocking(false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[unblockUser, fetchBlockedUsers, t, showSuccessToast]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form className={styles.form} onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="profileVisibility"
|
||||||
|
render={({ field }) => {
|
||||||
|
const handleChange = (
|
||||||
|
event: React.ChangeEvent<HTMLSelectElement>
|
||||||
|
) => {
|
||||||
|
field.onChange(event);
|
||||||
|
handleSubmit(onSubmit)();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SelectField
|
||||||
|
label={t("profile_visibility")}
|
||||||
|
value={field.value}
|
||||||
|
onChange={handleChange}
|
||||||
|
options={visibilityOptions.map((visiblity) => ({
|
||||||
|
key: visiblity.value,
|
||||||
|
value: visiblity.value,
|
||||||
|
label: visiblity.label,
|
||||||
|
}))}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<small>{t("profile_visibility_description")}</small>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h3 style={{ marginTop: `${SPACING_UNIT * 2}px` }}>
|
||||||
|
{t("manage_account")}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
{userDetails?.email ? (
|
||||||
|
<div>
|
||||||
|
<h4>{t("current_email")}</h4>
|
||||||
|
<p>{userDetails.email}</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<p>{t("no_associated_email")}</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "start",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: `${SPACING_UNIT}px`,
|
||||||
|
marginTop: 8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
theme="outline"
|
||||||
|
onClick={() => window.electron.openManageAccount("update-email")}
|
||||||
|
>
|
||||||
|
{t("update_email")}
|
||||||
|
<MailIcon />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
theme="outline"
|
||||||
|
onClick={() => window.electron.openManageAccount("update-password")}
|
||||||
|
>
|
||||||
|
{t("update_password")}
|
||||||
|
<KeyIcon />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 style={{ marginTop: `${SPACING_UNIT * 2}px` }}>
|
||||||
|
{t("subscription")}
|
||||||
|
</h3>
|
||||||
|
{userDetails?.subscription?.expiresAt ? (
|
||||||
|
<p>
|
||||||
|
{t("subscription_active_until", {
|
||||||
|
date: formatDate(userDetails?.subscription?.expiresAt),
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<p>{t("subscription_not_active")}</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "start",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: `${SPACING_UNIT}px`,
|
||||||
|
marginTop: 8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button theme="outline" onClick={() => window.electron.openCheckout()}>
|
||||||
|
{t("manage_subscription")}
|
||||||
|
<CloudIcon />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 style={{ marginTop: `${SPACING_UNIT * 2}px` }}>
|
||||||
|
{t("blocked_users")}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<ul className={styles.blockedUsersList}>
|
||||||
|
{blockedUsers.length > 0 ? (
|
||||||
|
blockedUsers.map((user) => {
|
||||||
|
return (
|
||||||
|
<li key={user.id} className={styles.blockedUser}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
gap: `${SPACING_UNIT}px`,
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={user.profileImageUrl!}
|
||||||
|
alt={user.displayName}
|
||||||
|
className={styles.blockedUserAvatar}
|
||||||
|
/>
|
||||||
|
<span>{user.displayName}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={styles.unblockButton}
|
||||||
|
onClick={() => handleUnblockClick(user.id)}
|
||||||
|
disabled={isUnblocking}
|
||||||
|
>
|
||||||
|
<XCircleFillIcon />
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<small>{t("no_users_blocked")}</small>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,139 +0,0 @@
|
||||||
import { SelectField } from "@renderer/components";
|
|
||||||
import { SPACING_UNIT } from "@renderer/theme.css";
|
|
||||||
import { Controller, useForm } from "react-hook-form";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
import * as styles from "./settings-privacy.css";
|
|
||||||
import { useToast, useUserDetails } from "@renderer/hooks";
|
|
||||||
import { useCallback, useContext, useEffect, useState } from "react";
|
|
||||||
import { XCircleFillIcon } from "@primer/octicons-react";
|
|
||||||
import { settingsContext } from "@renderer/context";
|
|
||||||
|
|
||||||
interface FormValues {
|
|
||||||
profileVisibility: "PUBLIC" | "FRIENDS" | "PRIVATE";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SettingsPrivacy() {
|
|
||||||
const { t } = useTranslation("settings");
|
|
||||||
|
|
||||||
const [isUnblocking, setIsUnblocking] = useState(false);
|
|
||||||
|
|
||||||
const { showSuccessToast } = useToast();
|
|
||||||
|
|
||||||
const { blockedUsers, fetchBlockedUsers } = useContext(settingsContext);
|
|
||||||
|
|
||||||
const {
|
|
||||||
control,
|
|
||||||
formState: { isSubmitting },
|
|
||||||
setValue,
|
|
||||||
handleSubmit,
|
|
||||||
} = useForm<FormValues>();
|
|
||||||
|
|
||||||
const { patchUser, userDetails } = useUserDetails();
|
|
||||||
|
|
||||||
const { unblockUser } = useUserDetails();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (userDetails?.profileVisibility) {
|
|
||||||
setValue("profileVisibility", userDetails.profileVisibility);
|
|
||||||
}
|
|
||||||
}, [userDetails, setValue]);
|
|
||||||
|
|
||||||
const visibilityOptions = [
|
|
||||||
{ value: "PUBLIC", label: t("public") },
|
|
||||||
{ value: "FRIENDS", label: t("friends_only") },
|
|
||||||
{ value: "PRIVATE", label: t("private") },
|
|
||||||
];
|
|
||||||
|
|
||||||
const onSubmit = async (values: FormValues) => {
|
|
||||||
await patchUser(values);
|
|
||||||
showSuccessToast(t("changes_saved"));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleUnblockClick = useCallback(
|
|
||||||
(id: string) => {
|
|
||||||
setIsUnblocking(true);
|
|
||||||
|
|
||||||
unblockUser(id)
|
|
||||||
.then(() => {
|
|
||||||
fetchBlockedUsers();
|
|
||||||
showSuccessToast(t("user_unblocked"));
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setIsUnblocking(false);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[unblockUser, fetchBlockedUsers, t, showSuccessToast]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<form className={styles.form} onSubmit={handleSubmit(onSubmit)}>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="profileVisibility"
|
|
||||||
render={({ field }) => {
|
|
||||||
const handleChange = (
|
|
||||||
event: React.ChangeEvent<HTMLSelectElement>
|
|
||||||
) => {
|
|
||||||
field.onChange(event);
|
|
||||||
handleSubmit(onSubmit)();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<SelectField
|
|
||||||
label={t("profile_visibility")}
|
|
||||||
value={field.value}
|
|
||||||
onChange={handleChange}
|
|
||||||
options={visibilityOptions.map((visiblity) => ({
|
|
||||||
key: visiblity.value,
|
|
||||||
value: visiblity.value,
|
|
||||||
label: visiblity.label,
|
|
||||||
}))}
|
|
||||||
disabled={isSubmitting}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<small>{t("profile_visibility_description")}</small>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<h3 style={{ marginTop: `${SPACING_UNIT * 2}px` }}>
|
|
||||||
{t("blocked_users")}
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<ul className={styles.blockedUsersList}>
|
|
||||||
{blockedUsers.map((user) => {
|
|
||||||
return (
|
|
||||||
<li key={user.id} className={styles.blockedUser}>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
gap: `${SPACING_UNIT}px`,
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={user.profileImageUrl!}
|
|
||||||
alt={user.displayName}
|
|
||||||
className={styles.blockedUserAvatar}
|
|
||||||
/>
|
|
||||||
<span>{user.displayName}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={styles.unblockButton}
|
|
||||||
onClick={() => handleUnblockClick(user.id)}
|
|
||||||
disabled={isUnblocking}
|
|
||||||
>
|
|
||||||
<XCircleFillIcon />
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</ul>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
SettingsContextConsumer,
|
SettingsContextConsumer,
|
||||||
SettingsContextProvider,
|
SettingsContextProvider,
|
||||||
} from "@renderer/context";
|
} from "@renderer/context";
|
||||||
import { SettingsPrivacy } from "./settings-privacy";
|
import { SettingsAccount } from "./settings-account";
|
||||||
import { useUserDetails } from "@renderer/hooks";
|
import { useUserDetails } from "@renderer/hooks";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ export default function Settings() {
|
||||||
"Real-Debrid",
|
"Real-Debrid",
|
||||||
];
|
];
|
||||||
|
|
||||||
if (userDetails) return [...categories, t("privacy")];
|
if (userDetails) return [...categories, t("account")];
|
||||||
return categories;
|
return categories;
|
||||||
}, [userDetails, t]);
|
}, [userDetails, t]);
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ export default function Settings() {
|
||||||
return <SettingsRealDebrid />;
|
return <SettingsRealDebrid />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <SettingsPrivacy />;
|
return <SettingsAccount />;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -416,6 +416,8 @@ export interface CatalogueSearchPayload {
|
||||||
developers: string[];
|
developers: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ManageAccountPage = "update-email" | "update-password";
|
||||||
|
|
||||||
export * from "./steam.types";
|
export * from "./steam.types";
|
||||||
export * from "./real-debrid.types";
|
export * from "./real-debrid.types";
|
||||||
export * from "./ludusavi.types";
|
export * from "./ludusavi.types";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue