From 594d56db5c209f1f5ef2f82feefe12d5d33e1bee Mon Sep 17 00:00:00 2001 From: Moyasee Date: Thu, 23 Oct 2025 09:55:04 +0300 Subject: [PATCH] feat: Changing settings ui. Added new section achievements --- src/locales/en/translation.json | 1 + src/main/events/index.ts | 7 +- src/main/events/misc/open-folder.ts | 2 +- .../pages/settings/settings-achievements.scss | 60 ++++++ .../pages/settings/settings-achievements.tsx | 181 ++++++++++++++++++ .../src/pages/settings/settings-behavior.scss | 17 ++ .../src/pages/settings/settings-behavior.tsx | 63 +----- .../src/pages/settings/settings-general.tsx | 71 +------ src/renderer/src/pages/settings/settings.scss | 26 ++- src/renderer/src/pages/settings/settings.tsx | 67 +++++-- 10 files changed, 335 insertions(+), 160 deletions(-) create mode 100644 src/renderer/src/pages/settings/settings-achievements.scss create mode 100644 src/renderer/src/pages/settings/settings-achievements.tsx diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index c0514378..d65e86e9 100755 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -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", diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 1750cb30..c2c51418 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -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"; diff --git a/src/main/events/misc/open-folder.ts b/src/main/events/misc/open-folder.ts index 1a6c4447..3823f373 100644 --- a/src/main/events/misc/open-folder.ts +++ b/src/main/events/misc/open-folder.ts @@ -8,4 +8,4 @@ const openFolder = async ( return shell.openPath(folderPath); }; -registerEvent("openFolder", openFolder); \ No newline at end of file +registerEvent("openFolder", openFolder); diff --git a/src/renderer/src/pages/settings/settings-achievements.scss b/src/renderer/src/pages/settings/settings-achievements.scss new file mode 100644 index 00000000..674e6ad0 --- /dev/null +++ b/src/renderer/src/pages/settings/settings-achievements.scss @@ -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; + } +} \ No newline at end of file diff --git a/src/renderer/src/pages/settings/settings-achievements.tsx b/src/renderer/src/pages/settings/settings-achievements.tsx new file mode 100644 index 00000000..b7e98d35 --- /dev/null +++ b/src/renderer/src/pages/settings/settings-achievements.tsx @@ -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) => { + setForm((prev) => ({ ...prev, ...values })); + await updateUserPreferences(values); + }; + + const handleChangeAchievementCustomNotificationPosition = async ( + event: React.ChangeEvent + ) => { + const value = event.target.value as AchievementCustomNotificationPosition; + + await handleChange({ achievementCustomNotificationPosition: value }); + window.electron.updateAchievementCustomNotificationWindow(); + }; + + return ( +
+
+ + + handleChange({ + showHiddenAchievementsDescription: !form.showHiddenAchievementsDescription, + }) + } + /> + +
+ + handleChange({ enableSteamAchievements: !form.enableSteamAchievements }) + } + /> + + + + +
+ +
+ + handleChange({ + enableAchievementScreenshots: !form.enableAchievementScreenshots, + }) + } + /> + + + + +
+ +
+ +
+
+ +
+

{t("notifications")}

+ + { + await handleChange({ + achievementNotificationsEnabled: !form.achievementNotificationsEnabled, + }); + window.electron.updateAchievementCustomNotificationWindow(); + }} + /> + + { + await handleChange({ + achievementCustomNotificationsEnabled: + !form.achievementCustomNotificationsEnabled, + }); + window.electron.updateAchievementCustomNotificationWindow(); + }} + /> + + {form.achievementNotificationsEnabled && form.achievementCustomNotificationsEnabled && ( + <> + + + + + )} +
+
+ ); +} \ No newline at end of file diff --git a/src/renderer/src/pages/settings/settings-behavior.scss b/src/renderer/src/pages/settings/settings-behavior.scss index 88453996..d1189d49 100644 --- a/src/renderer/src/pages/settings/settings-behavior.scss +++ b/src/renderer/src/pages/settings/settings-behavior.scss @@ -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; + } + } } diff --git a/src/renderer/src/pages/settings/settings-behavior.tsx b/src/renderer/src/pages/settings/settings-behavior.tsx index 9a2fc2fc..81b15854 100644 --- a/src/renderer/src/pages/settings/settings-behavior.tsx +++ b/src/renderer/src/pages/settings/settings-behavior.tsx @@ -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() { } /> - - handleChange({ - showHiddenAchievementsDescription: - !form.showHiddenAchievementsDescription, - }) - } - /> - -
- - handleChange({ - enableSteamAchievements: !form.enableSteamAchievements, - }) - } - /> - - - -
- -
- - handleChange({ - enableAchievementScreenshots: !form.enableAchievementScreenshots, - }) - } - /> - - - - -
- -
- -
); } diff --git a/src/renderer/src/pages/settings/settings-general.tsx b/src/renderer/src/pages/settings/settings-general.tsx index c698440d..d3b49b4d 100644 --- a/src/renderer/src/pages/settings/settings-general.tsx +++ b/src/renderer/src/pages/settings/settings-general.tsx @@ -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 @@ -148,15 +134,6 @@ export function SettingsGeneral() { await updateUserPreferences(values); }; - const handleChangeAchievementCustomNotificationPosition = async ( - event: React.ChangeEvent - ) => { - 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() { } /> - { - await handleChange({ - achievementNotificationsEnabled: - !form.achievementNotificationsEnabled, - }); - - window.electron.updateAchievementCustomNotificationWindow(); - }} - /> - - { - await handleChange({ - achievementCustomNotificationsEnabled: - !form.achievementCustomNotificationsEnabled, - }); - - window.electron.updateAchievementCustomNotificationWindow(); - }} - /> - - {form.achievementNotificationsEnabled && - form.achievementCustomNotificationsEnabled && ( - <> - - - - - )}

{t("common_redist")}

diff --git a/src/renderer/src/pages/settings/settings.scss b/src/renderer/src/pages/settings/settings.scss index 49b228bc..cbb71979 100644 --- a/src/renderer/src/pages/settings/settings.scss +++ b/src/renderer/src/pages/settings/settings.scss @@ -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; - } } diff --git a/src/renderer/src/pages/settings/settings.tsx b/src/renderer/src/pages/settings/settings.tsx index eb19af31..bf12e3fc 100644 --- a/src/renderer/src/pages/settings/settings.tsx +++ b/src/renderer/src/pages/settings/settings.tsx @@ -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 ; + return ; } if (currentCategoryIndex === 3) { - return ; + return ; } if (currentCategoryIndex === 4) { + return ; + } + + if (currentCategoryIndex === 5) { return ; } @@ -69,21 +89,32 @@ export default function Settings() { return (
-
-
+
+ + +

{categories[currentCategoryIndex].contentTitle}

{renderCategory()}