mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-11 22:06:17 +00:00
feat: Changing settings ui. Added new section achievements
This commit is contained in:
@@ -413,6 +413,7 @@
|
||||
"launch_with_system": "Launch Hydra on system start-up",
|
||||
"general": "General",
|
||||
"behavior": "Behavior",
|
||||
"achievements": "Achievements",
|
||||
"download_sources": "Download sources",
|
||||
"language": "Language",
|
||||
"api_token": "API Token",
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { appVersion, defaultDownloadsPath, isStaging, screenshotsPath } from "@main/constants";
|
||||
import {
|
||||
appVersion,
|
||||
defaultDownloadsPath,
|
||||
isStaging,
|
||||
screenshotsPath,
|
||||
} from "@main/constants";
|
||||
import { ipcMain } from "electron";
|
||||
|
||||
import "./catalogue/get-game-shop-details";
|
||||
|
||||
@@ -8,4 +8,4 @@ const openFolder = async (
|
||||
return shell.openPath(folderPath);
|
||||
};
|
||||
|
||||
registerEvent("openFolder", openFolder);
|
||||
registerEvent("openFolder", openFolder);
|
||||
|
||||
60
src/renderer/src/pages/settings/settings-achievements.scss
Normal file
60
src/renderer/src/pages/settings/settings-achievements.scss
Normal file
@@ -0,0 +1,60 @@
|
||||
@use "../../scss/globals.scss";
|
||||
|
||||
.settings-achievements {
|
||||
&__checkbox-container {
|
||||
opacity: globals.$disabled-opacity;
|
||||
cursor: not-allowed;
|
||||
|
||||
&--enabled {
|
||||
opacity: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&--with-tooltip {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&--tooltip {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&__button-container {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
&__section {
|
||||
margin-top: 32px;
|
||||
padding-top: 24px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
|
||||
&-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
> * + * {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
&--achievements {
|
||||
// First section sits flush with container top
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&__achievement-custom-notification-position__select-variation {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
&__test-achievement-notification-button {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
181
src/renderer/src/pages/settings/settings-achievements.tsx
Normal file
181
src/renderer/src/pages/settings/settings-achievements.tsx
Normal file
@@ -0,0 +1,181 @@
|
||||
import { useContext, useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { CheckboxField, Button, SelectField } from "@renderer/components";
|
||||
import { useAppSelector } from "@renderer/hooks";
|
||||
import { settingsContext } from "@renderer/context";
|
||||
import "./settings-achievements.scss";
|
||||
import { QuestionIcon } from "@primer/octicons-react";
|
||||
import { AchievementCustomNotificationPosition } from "@types";
|
||||
|
||||
export function SettingsAchievements() {
|
||||
const { t } = useTranslation("settings");
|
||||
|
||||
const userPreferences = useAppSelector((state) => state.userPreferences.value);
|
||||
const { updateUserPreferences } = useContext(settingsContext);
|
||||
|
||||
const [form, setForm] = useState({
|
||||
showHiddenAchievementsDescription: false,
|
||||
enableSteamAchievements: false,
|
||||
enableAchievementScreenshots: false,
|
||||
achievementNotificationsEnabled: true,
|
||||
achievementCustomNotificationsEnabled: true,
|
||||
achievementCustomNotificationPosition: "top-left" as AchievementCustomNotificationPosition,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (userPreferences) {
|
||||
setForm((prev) => ({
|
||||
...prev,
|
||||
showHiddenAchievementsDescription:
|
||||
userPreferences.showHiddenAchievementsDescription ?? false,
|
||||
enableSteamAchievements: userPreferences.enableSteamAchievements ?? false,
|
||||
enableAchievementScreenshots:
|
||||
userPreferences.enableAchievementScreenshots ?? false,
|
||||
achievementNotificationsEnabled:
|
||||
userPreferences.achievementNotificationsEnabled ?? true,
|
||||
achievementCustomNotificationsEnabled:
|
||||
userPreferences.achievementCustomNotificationsEnabled ?? true,
|
||||
achievementCustomNotificationPosition:
|
||||
userPreferences.achievementCustomNotificationPosition ?? "top-left",
|
||||
}));
|
||||
}
|
||||
}, [userPreferences]);
|
||||
|
||||
const achievementCustomNotificationPositionOptions = useMemo(() => {
|
||||
return [
|
||||
"top-left",
|
||||
"top-center",
|
||||
"top-right",
|
||||
"bottom-left",
|
||||
"bottom-center",
|
||||
"bottom-right",
|
||||
].map((position) => ({ key: position, value: position, label: t(position) }));
|
||||
}, [t]);
|
||||
|
||||
const handleChange = async (values: Partial<typeof form>) => {
|
||||
setForm((prev) => ({ ...prev, ...values }));
|
||||
await updateUserPreferences(values);
|
||||
};
|
||||
|
||||
const handleChangeAchievementCustomNotificationPosition = async (
|
||||
event: React.ChangeEvent<HTMLSelectElement>
|
||||
) => {
|
||||
const value = event.target.value as AchievementCustomNotificationPosition;
|
||||
|
||||
await handleChange({ achievementCustomNotificationPosition: value });
|
||||
window.electron.updateAchievementCustomNotificationWindow();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="settings-achievements">
|
||||
<div className="settings-achievements__section settings-achievements__section--achievements">
|
||||
|
||||
<CheckboxField
|
||||
label={t("show_hidden_achievement_description")}
|
||||
checked={form.showHiddenAchievementsDescription}
|
||||
onChange={() =>
|
||||
handleChange({
|
||||
showHiddenAchievementsDescription: !form.showHiddenAchievementsDescription,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="settings-achievements__checkbox-container--with-tooltip">
|
||||
<CheckboxField
|
||||
label={t("enable_steam_achievements")}
|
||||
checked={form.enableSteamAchievements}
|
||||
onChange={() =>
|
||||
handleChange({ enableSteamAchievements: !form.enableSteamAchievements })
|
||||
}
|
||||
/>
|
||||
|
||||
<small
|
||||
className="settings-achievements__checkbox-container--tooltip"
|
||||
data-open-article="steam-achievements"
|
||||
>
|
||||
<QuestionIcon size={12} />
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div className="settings-achievements__checkbox-container--with-tooltip">
|
||||
<CheckboxField
|
||||
label={t("enable_achievement_screenshots")}
|
||||
checked={form.enableAchievementScreenshots}
|
||||
disabled={window.electron.platform === "linux"}
|
||||
onChange={() =>
|
||||
handleChange({
|
||||
enableAchievementScreenshots: !form.enableAchievementScreenshots,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
<small
|
||||
className="settings-achievements__checkbox-container--tooltip"
|
||||
data-open-article="achievement-souvenirs"
|
||||
>
|
||||
<QuestionIcon size={12} />
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div className="settings-achievements__button-container">
|
||||
<Button
|
||||
theme="outline"
|
||||
onClick={async () => {
|
||||
const screenshotsPath = await window.electron.getScreenshotsPath();
|
||||
window.electron.openFolder(screenshotsPath);
|
||||
}}
|
||||
>
|
||||
{t("open_screenshots_directory")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="settings-achievements__section settings-achievements__section--notifications">
|
||||
<h3 className="settings-achievements__section-title">{t("notifications")}</h3>
|
||||
|
||||
<CheckboxField
|
||||
label={t("enable_achievement_notifications")}
|
||||
checked={form.achievementNotificationsEnabled}
|
||||
onChange={async () => {
|
||||
await handleChange({
|
||||
achievementNotificationsEnabled: !form.achievementNotificationsEnabled,
|
||||
});
|
||||
window.electron.updateAchievementCustomNotificationWindow();
|
||||
}}
|
||||
/>
|
||||
|
||||
<CheckboxField
|
||||
label={t("enable_achievement_custom_notifications")}
|
||||
checked={form.achievementCustomNotificationsEnabled}
|
||||
disabled={!form.achievementNotificationsEnabled}
|
||||
onChange={async () => {
|
||||
await handleChange({
|
||||
achievementCustomNotificationsEnabled:
|
||||
!form.achievementCustomNotificationsEnabled,
|
||||
});
|
||||
window.electron.updateAchievementCustomNotificationWindow();
|
||||
}}
|
||||
/>
|
||||
|
||||
{form.achievementNotificationsEnabled && form.achievementCustomNotificationsEnabled && (
|
||||
<>
|
||||
<SelectField
|
||||
className="settings-achievements__achievement-custom-notification-position__select-variation"
|
||||
label={t("achievement_custom_notification_position")}
|
||||
value={form.achievementCustomNotificationPosition}
|
||||
onChange={handleChangeAchievementCustomNotificationPosition}
|
||||
options={achievementCustomNotificationPositionOptions}
|
||||
/>
|
||||
|
||||
<Button
|
||||
className="settings-achievements__test-achievement-notification-button"
|
||||
onClick={() => window.electron.showAchievementTestNotification()}
|
||||
>
|
||||
{t("test_notification")}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -25,4 +25,21 @@
|
||||
&__button-container {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
&__section {
|
||||
margin-top: 32px;
|
||||
padding-top: 24px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
|
||||
&-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
// Add spacing between elements in the section
|
||||
> * + * {
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { CheckboxField, Button } from "@renderer/components";
|
||||
import { CheckboxField } from "@renderer/components";
|
||||
import { useAppSelector } from "@renderer/hooks";
|
||||
import { settingsContext } from "@renderer/context";
|
||||
import "./settings-behavior.scss";
|
||||
import { QuestionIcon } from "@primer/octicons-react";
|
||||
|
||||
export function SettingsBehavior() {
|
||||
const userPreferences = useAppSelector(
|
||||
@@ -141,17 +140,6 @@ export function SettingsBehavior() {
|
||||
}
|
||||
/>
|
||||
|
||||
<CheckboxField
|
||||
label={t("show_hidden_achievement_description")}
|
||||
checked={form.showHiddenAchievementsDescription}
|
||||
onChange={() =>
|
||||
handleChange({
|
||||
showHiddenAchievementsDescription:
|
||||
!form.showHiddenAchievementsDescription,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
<CheckboxField
|
||||
label={t("show_download_speed_in_megabytes")}
|
||||
checked={form.showDownloadSpeedInMegabytes}
|
||||
@@ -172,56 +160,7 @@ export function SettingsBehavior() {
|
||||
}
|
||||
/>
|
||||
|
||||
<div className={`settings-behavior__checkbox-container--with-tooltip`}>
|
||||
<CheckboxField
|
||||
label={t("enable_steam_achievements")}
|
||||
checked={form.enableSteamAchievements}
|
||||
onChange={() =>
|
||||
handleChange({
|
||||
enableSteamAchievements: !form.enableSteamAchievements,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
<small
|
||||
className="settings-behavior__checkbox-container--tooltip"
|
||||
data-open-article="steam-achievements"
|
||||
>
|
||||
<QuestionIcon size={12} />
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div className={`settings-behavior__checkbox-container--with-tooltip`}>
|
||||
<CheckboxField
|
||||
label={t("enable_achievement_screenshots")}
|
||||
checked={form.enableAchievementScreenshots}
|
||||
disabled={window.electron.platform === "linux"}
|
||||
onChange={() =>
|
||||
handleChange({
|
||||
enableAchievementScreenshots: !form.enableAchievementScreenshots,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
<small
|
||||
className="settings-behavior__checkbox-container--tooltip"
|
||||
data-open-article="achievement-souvenirs"
|
||||
>
|
||||
<QuestionIcon size={12} />
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div className="settings-behavior__button-container">
|
||||
<Button
|
||||
theme="outline"
|
||||
onClick={async () => {
|
||||
const screenshotsPath = await window.electron.getScreenshotsPath();
|
||||
window.electron.openFolder(screenshotsPath);
|
||||
}}
|
||||
>
|
||||
{t("open_screenshots_directory")}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useContext, useEffect, useMemo, useState } from "react";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import {
|
||||
TextField,
|
||||
Button,
|
||||
@@ -119,20 +119,6 @@ export function SettingsGeneral() {
|
||||
}
|
||||
}, [userPreferences, defaultDownloadsPath]);
|
||||
|
||||
const achievementCustomNotificationPositionOptions = useMemo(() => {
|
||||
return [
|
||||
"top-left",
|
||||
"top-center",
|
||||
"top-right",
|
||||
"bottom-left",
|
||||
"bottom-center",
|
||||
"bottom-right",
|
||||
].map((position) => ({
|
||||
key: position,
|
||||
value: position,
|
||||
label: t(position),
|
||||
}));
|
||||
}, [t]);
|
||||
|
||||
const handleLanguageChange = (
|
||||
event: React.ChangeEvent<HTMLSelectElement>
|
||||
@@ -148,15 +134,6 @@ export function SettingsGeneral() {
|
||||
await updateUserPreferences(values);
|
||||
};
|
||||
|
||||
const handleChangeAchievementCustomNotificationPosition = async (
|
||||
event: React.ChangeEvent<HTMLSelectElement>
|
||||
) => {
|
||||
const value = event.target.value as AchievementCustomNotificationPosition;
|
||||
|
||||
await handleChange({ achievementCustomNotificationPosition: value });
|
||||
|
||||
window.electron.updateAchievementCustomNotificationWindow();
|
||||
};
|
||||
|
||||
const handleChooseDownloadsPath = async () => {
|
||||
const { filePaths } = await window.electron.showOpenDialog({
|
||||
@@ -262,52 +239,6 @@ export function SettingsGeneral() {
|
||||
}
|
||||
/>
|
||||
|
||||
<CheckboxField
|
||||
label={t("enable_achievement_notifications")}
|
||||
checked={form.achievementNotificationsEnabled}
|
||||
onChange={async () => {
|
||||
await handleChange({
|
||||
achievementNotificationsEnabled:
|
||||
!form.achievementNotificationsEnabled,
|
||||
});
|
||||
|
||||
window.electron.updateAchievementCustomNotificationWindow();
|
||||
}}
|
||||
/>
|
||||
|
||||
<CheckboxField
|
||||
label={t("enable_achievement_custom_notifications")}
|
||||
checked={form.achievementCustomNotificationsEnabled}
|
||||
disabled={!form.achievementNotificationsEnabled}
|
||||
onChange={async () => {
|
||||
await handleChange({
|
||||
achievementCustomNotificationsEnabled:
|
||||
!form.achievementCustomNotificationsEnabled,
|
||||
});
|
||||
|
||||
window.electron.updateAchievementCustomNotificationWindow();
|
||||
}}
|
||||
/>
|
||||
|
||||
{form.achievementNotificationsEnabled &&
|
||||
form.achievementCustomNotificationsEnabled && (
|
||||
<>
|
||||
<SelectField
|
||||
className="settings-general__achievement-custom-notification-position__select-variation"
|
||||
label={t("achievement_custom_notification_position")}
|
||||
value={form.achievementCustomNotificationPosition}
|
||||
onChange={handleChangeAchievementCustomNotificationPosition}
|
||||
options={achievementCustomNotificationPositionOptions}
|
||||
/>
|
||||
|
||||
<Button
|
||||
className="settings-general__test-achievement-notification-button"
|
||||
onClick={() => window.electron.showAchievementTestNotification()}
|
||||
>
|
||||
{t("test_notification")}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
<h2 className="settings-general__section-title">{t("common_redist")}</h2>
|
||||
|
||||
|
||||
@@ -2,26 +2,36 @@
|
||||
|
||||
.settings {
|
||||
&__container {
|
||||
padding: 24px;
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
&__sidebar {
|
||||
width: 240px;
|
||||
min-width: 240px;
|
||||
margin-right: 12px;
|
||||
background-color: globals.$background-color;
|
||||
border: solid 1px globals.$border-color;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
padding: calc(globals.$spacing-unit * 2);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
&__content {
|
||||
background-color: globals.$background-color;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
padding: calc(globals.$spacing-unit * 3);
|
||||
border: solid 1px globals.$border-color;
|
||||
box-shadow: 0px 0px 15px 0px #000000;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
gap: calc(globals.$spacing-unit * 2);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&__categories {
|
||||
display: flex;
|
||||
gap: globals.$spacing-unit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Button } from "@renderer/components";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { SettingsGeneral } from "./settings-general";
|
||||
import { SettingsBehavior } from "./settings-behavior";
|
||||
import { SettingsDownloadSources } from "./settings-download-sources";
|
||||
import { SettingsAchievements } from "./settings-achievements";
|
||||
import {
|
||||
SettingsContextConsumer,
|
||||
SettingsContextProvider,
|
||||
@@ -13,6 +13,16 @@ import { useMemo } from "react";
|
||||
import "./settings.scss";
|
||||
import { SettingsAppearance } from "./aparence/settings-appearance";
|
||||
import { SettingsDebrid } from "./settings-debrid";
|
||||
import cn from "classnames";
|
||||
import {
|
||||
GearIcon,
|
||||
ToolsIcon,
|
||||
TrophyIcon,
|
||||
DownloadIcon,
|
||||
PaintbrushIcon,
|
||||
CloudIcon,
|
||||
PersonIcon,
|
||||
} from "@primer/octicons-react";
|
||||
|
||||
export default function Settings() {
|
||||
const { t } = useTranslation("settings");
|
||||
@@ -21,20 +31,26 @@ export default function Settings() {
|
||||
|
||||
const categories = useMemo(() => {
|
||||
const categories = [
|
||||
{ tabLabel: t("general"), contentTitle: t("general") },
|
||||
{ tabLabel: t("behavior"), contentTitle: t("behavior") },
|
||||
{ tabLabel: t("download_sources"), contentTitle: t("download_sources") },
|
||||
{ tabLabel: t("general"), contentTitle: t("general"), Icon: GearIcon },
|
||||
{ tabLabel: t("behavior"), contentTitle: t("behavior"), Icon: ToolsIcon },
|
||||
{ tabLabel: t("achievements"), contentTitle: t("achievements"), Icon: TrophyIcon },
|
||||
{
|
||||
tabLabel: t("download_sources"),
|
||||
contentTitle: t("download_sources"),
|
||||
Icon: DownloadIcon,
|
||||
},
|
||||
{
|
||||
tabLabel: t("appearance"),
|
||||
contentTitle: t("appearance"),
|
||||
Icon: PaintbrushIcon,
|
||||
},
|
||||
{ tabLabel: t("debrid"), contentTitle: t("debrid") },
|
||||
{ tabLabel: t("debrid"), contentTitle: t("debrid"), Icon: CloudIcon },
|
||||
];
|
||||
|
||||
if (userDetails)
|
||||
return [
|
||||
...categories,
|
||||
{ tabLabel: t("account"), contentTitle: t("account") },
|
||||
{ tabLabel: t("account"), contentTitle: t("account"), Icon: PersonIcon },
|
||||
];
|
||||
return categories;
|
||||
}, [userDetails, t]);
|
||||
@@ -53,14 +69,18 @@ export default function Settings() {
|
||||
}
|
||||
|
||||
if (currentCategoryIndex === 2) {
|
||||
return <SettingsDownloadSources />;
|
||||
return <SettingsAchievements />;
|
||||
}
|
||||
|
||||
if (currentCategoryIndex === 3) {
|
||||
return <SettingsAppearance appearance={appearance} />;
|
||||
return <SettingsDownloadSources />;
|
||||
}
|
||||
|
||||
if (currentCategoryIndex === 4) {
|
||||
return <SettingsAppearance appearance={appearance} />;
|
||||
}
|
||||
|
||||
if (currentCategoryIndex === 5) {
|
||||
return <SettingsDebrid />;
|
||||
}
|
||||
|
||||
@@ -69,21 +89,32 @@ export default function Settings() {
|
||||
|
||||
return (
|
||||
<section className="settings__container">
|
||||
<div className="settings__content">
|
||||
<section className="settings__categories">
|
||||
<aside className="settings__sidebar">
|
||||
<ul className="settings__categories sidebar__menu">
|
||||
{categories.map((category, index) => (
|
||||
<Button
|
||||
<li
|
||||
key={category.contentTitle}
|
||||
theme={
|
||||
currentCategoryIndex === index ? "primary" : "outline"
|
||||
}
|
||||
onClick={() => setCurrentCategoryIndex(index)}
|
||||
className={cn("sidebar__menu-item", {
|
||||
"sidebar__menu-item--active":
|
||||
currentCategoryIndex === index,
|
||||
})}
|
||||
>
|
||||
{category.tabLabel}
|
||||
</Button>
|
||||
<button
|
||||
type="button"
|
||||
className="sidebar__menu-item-button"
|
||||
onClick={() => setCurrentCategoryIndex(index)}
|
||||
>
|
||||
<category.Icon size={16} />
|
||||
<span className="sidebar__menu-item-button-label">
|
||||
{category.tabLabel}
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</section>
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
<div className="settings__content">
|
||||
<h2>{categories[currentCategoryIndex].contentTitle}</h2>
|
||||
{renderCategory()}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user