From 8fbe23e61cfcba955d148fd0d9df810e6da0daf1 Mon Sep 17 00:00:00 2001 From: Kelvin Date: Thu, 9 Jan 2025 00:58:25 -0300 Subject: [PATCH 01/28] readme translation pt-BR improvement --- docs/README.pt-BR.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/README.pt-BR.md b/docs/README.pt-BR.md index f9ba9d66..6e65d3fb 100644 --- a/docs/README.pt-BR.md +++ b/docs/README.pt-BR.md @@ -125,6 +125,10 @@ cd hydra yarn ``` +### Install OpenSSL 1.1 + +[OpenSSL 1.1](https://slproweb.com/download/Win64OpenSSL-1_1_1w.exe) é exigido pelo libtorrent em ambientes Windows. + ### Instale Python 3.9 Certifique-se de ter o Python 3.9 instalado em sua máquina. Você pode baixá-lo e instalá-lo em [python.org](https://www.python.org/downloads/release/python-3913/). From 392279c4e1d47f190026b62b3075a46f26799e1e Mon Sep 17 00:00:00 2001 From: Kelvin <90298223+KelvinDiasMoreira@users.noreply.github.com> Date: Thu, 9 Jan 2025 11:58:08 -0300 Subject: [PATCH 02/28] fix translate error --- docs/README.pt-BR.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.pt-BR.md b/docs/README.pt-BR.md index 6e65d3fb..ca5933fb 100644 --- a/docs/README.pt-BR.md +++ b/docs/README.pt-BR.md @@ -125,7 +125,7 @@ cd hydra yarn ``` -### Install OpenSSL 1.1 +### Instale OpenSSL 1.1 [OpenSSL 1.1](https://slproweb.com/download/Win64OpenSSL-1_1_1w.exe) é exigido pelo libtorrent em ambientes Windows. From 09c1170407670385dc847648e8c49ed4b25322bb Mon Sep 17 00:00:00 2001 From: Kelvin <90298223+KelvinDiasMoreira@users.noreply.github.com> Date: Fri, 10 Jan 2025 14:45:19 -0300 Subject: [PATCH 03/28] added anchor tag --- docs/README.pt-BR.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.pt-BR.md b/docs/README.pt-BR.md index ca5933fb..ca942854 100644 --- a/docs/README.pt-BR.md +++ b/docs/README.pt-BR.md @@ -125,7 +125,7 @@ cd hydra yarn ``` -### Instale OpenSSL 1.1 +### Instale OpenSSL 1.1 [OpenSSL 1.1](https://slproweb.com/download/Win64OpenSSL-1_1_1w.exe) é exigido pelo libtorrent em ambientes Windows. From af4fcb8f064a5f8bb6db5180ebe23c506e80e87a Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 15 Jan 2025 15:49:11 -0300 Subject: [PATCH 04/28] feat: manage account buttons --- src/locales/en/translation.json | 13 +- src/locales/pt-BR/translation.json | 13 +- src/main/events/index.ts | 1 + src/main/events/misc/open-manage-account.ts | 25 ++ src/main/services/hydra-api.ts | 14 +- src/preload/index.ts | 3 + src/renderer/src/declaration.d.ts | 2 + ...privacy.css.ts => settings-account.css.ts} | 0 .../src/pages/settings/settings-account.tsx | 217 ++++++++++++++++++ .../src/pages/settings/settings-privacy.tsx | 139 ----------- src/renderer/src/pages/settings/settings.tsx | 6 +- src/types/index.ts | 2 + 12 files changed, 286 insertions(+), 149 deletions(-) create mode 100644 src/main/events/misc/open-manage-account.ts rename src/renderer/src/pages/settings/{settings-privacy.css.ts => settings-account.css.ts} (100%) create mode 100644 src/renderer/src/pages/settings/settings-account.tsx delete mode 100644 src/renderer/src/pages/settings/settings-privacy.tsx diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 4e3dcb37..e55954bf 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -280,7 +280,18 @@ "launch_minimized": "Launch Hydra minimized", "disable_nsfw_alert": "Disable NSFW alert", "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": { "download_complete": "Download complete", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 2a80084f..24ab4653 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -268,7 +268,18 @@ "launch_minimized": "Iniciar o Hydra minimizado", "disable_nsfw_alert": "Desativar alerta de conteúdo inapropriado", "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": { "download_complete": "Download concluído", diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 25882c3f..f0f882ca 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -30,6 +30,7 @@ import "./library/remove-game-from-library"; import "./library/select-game-wine-prefix"; import "./library/reset-game-achievements"; import "./misc/open-checkout"; +import "./misc/open-manage-account"; import "./misc/open-external"; import "./misc/show-open-dialog"; import "./misc/get-features"; diff --git a/src/main/events/misc/open-manage-account.ts b/src/main/events/misc/open-manage-account.ts new file mode 100644 index 00000000..2946b04b --- /dev/null +++ b/src/main/events/misc/open-manage-account.ts @@ -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); diff --git a/src/main/services/hydra-api.ts b/src/main/services/hydra-api.ts index 63dd9b16..4ab20e80 100644 --- a/src/main/services/hydra-api.ts +++ b/src/main/services/hydra-api.ts @@ -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() { const now = new Date(); if (this.userAuth.expirationTimestamp < now.getTime()) { try { - const response = await this.instance.post(`/auth/refresh`, { - refreshToken: this.userAuth.refreshToken, - }); - - const { accessToken, expiresIn } = response.data; + const { accessToken, expiresIn } = await this.refreshToken(); const tokenExpirationTimestamp = now.getTime() + diff --git a/src/preload/index.ts b/src/preload/index.ts index 316397d2..24e6cf39 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -14,6 +14,7 @@ import type { CatalogueSearchPayload, SeedingStatus, GameAchievement, + ManageAccountPage, } from "@types"; import type { CatalogueCategory } from "@shared"; import type { AxiosProgressEvent } from "axios"; @@ -226,6 +227,8 @@ contextBridge.exposeInMainWorld("electron", { isPortableVersion: () => ipcRenderer.invoke("isPortableVersion"), openExternal: (src: string) => ipcRenderer.invoke("openExternal", src), openCheckout: () => ipcRenderer.invoke("openCheckout"), + openManageAccount: (page: ManageAccountPage) => + ipcRenderer.invoke("openManageAccount", page), showOpenDialog: (options: Electron.OpenDialogOptions) => ipcRenderer.invoke("showOpenDialog", options), showItemInFolder: (path: string) => diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 88f3297f..33fcb7da 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -29,6 +29,7 @@ import type { UserAchievement, ComparedAchievements, CatalogueSearchPayload, + ManageAccountPage, } from "@types"; import type { AxiosProgressEvent } from "axios"; import type disk from "diskusage"; @@ -187,6 +188,7 @@ declare global { /* Misc */ openExternal: (src: string) => Promise; openCheckout: () => Promise; + openManageAccount: (page: ManageAccountPage) => Promise; getVersion: () => Promise; isStaging: () => Promise; ping: () => string; diff --git a/src/renderer/src/pages/settings/settings-privacy.css.ts b/src/renderer/src/pages/settings/settings-account.css.ts similarity index 100% rename from src/renderer/src/pages/settings/settings-privacy.css.ts rename to src/renderer/src/pages/settings/settings-account.css.ts diff --git a/src/renderer/src/pages/settings/settings-account.tsx b/src/renderer/src/pages/settings/settings-account.tsx new file mode 100644 index 00000000..16ed40e4 --- /dev/null +++ b/src/renderer/src/pages/settings/settings-account.tsx @@ -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(); + + 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 ( +
+ { + const handleChange = ( + event: React.ChangeEvent + ) => { + field.onChange(event); + handleSubmit(onSubmit)(); + }; + + return ( + <> + ({ + key: visiblity.value, + value: visiblity.value, + label: visiblity.label, + }))} + disabled={isSubmitting} + /> + + {t("profile_visibility_description")} + + ); + }} + /> + +

+ {t("manage_account")} +

+ + {userDetails?.email ? ( +
+

{t("current_email")}

+

{userDetails.email}

+
+ ) : ( +

{t("no_associated_email")}

+ )} + +
+ + + +
+ +

+ {t("subscription")} +

+ {userDetails?.subscription?.expiresAt ? ( +

+ {t("subscription_active_until", { + date: formatDate(userDetails?.subscription?.expiresAt), + })} +

+ ) : ( +

{t("subscription_not_active")}

+ )} + +
+ +
+ +

+ {t("blocked_users")} +

+ +
    + {blockedUsers.length > 0 ? ( + blockedUsers.map((user) => { + return ( +
  • +
    + {user.displayName} + {user.displayName} +
    + + +
  • + ); + }) + ) : ( + {t("no_users_blocked")} + )} +
+ + ); +} diff --git a/src/renderer/src/pages/settings/settings-privacy.tsx b/src/renderer/src/pages/settings/settings-privacy.tsx deleted file mode 100644 index b93d1d07..00000000 --- a/src/renderer/src/pages/settings/settings-privacy.tsx +++ /dev/null @@ -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(); - - 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 ( -
- { - const handleChange = ( - event: React.ChangeEvent - ) => { - field.onChange(event); - handleSubmit(onSubmit)(); - }; - - return ( - <> - ({ - key: visiblity.value, - value: visiblity.value, - label: visiblity.label, - }))} - disabled={isSubmitting} - /> - - {t("profile_visibility_description")} - - ); - }} - /> - -

- {t("blocked_users")} -

- -
    - {blockedUsers.map((user) => { - return ( -
  • -
    - {user.displayName} - {user.displayName} -
    - - -
  • - ); - })} -
- - ); -} diff --git a/src/renderer/src/pages/settings/settings.tsx b/src/renderer/src/pages/settings/settings.tsx index dffdfbae..5fba6c5d 100644 --- a/src/renderer/src/pages/settings/settings.tsx +++ b/src/renderer/src/pages/settings/settings.tsx @@ -11,7 +11,7 @@ import { SettingsContextConsumer, SettingsContextProvider, } from "@renderer/context"; -import { SettingsPrivacy } from "./settings-privacy"; +import { SettingsAccount } from "./settings-account"; import { useUserDetails } from "@renderer/hooks"; import { useMemo } from "react"; @@ -28,7 +28,7 @@ export default function Settings() { "Real-Debrid", ]; - if (userDetails) return [...categories, t("privacy")]; + if (userDetails) return [...categories, t("account")]; return categories; }, [userDetails, t]); @@ -53,7 +53,7 @@ export default function Settings() { return ; } - return ; + return ; }; return ( diff --git a/src/types/index.ts b/src/types/index.ts index 345893a5..66c458b5 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -416,6 +416,8 @@ export interface CatalogueSearchPayload { developers: string[]; } +export type ManageAccountPage = "update-email" | "update-password"; + export * from "./steam.types"; export * from "./real-debrid.types"; export * from "./ludusavi.types"; From c4378c0ffc44af29a78d123e56559c34b3c3dd8c Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:26:44 -0300 Subject: [PATCH 05/28] feat: update user details on settings account tab --- src/main/services/hosters/datanodes.ts | 3 ++- .../src/pages/settings/settings-account.tsx | 20 +++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/services/hosters/datanodes.ts b/src/main/services/hosters/datanodes.ts index d77e7d51..ae144418 100644 --- a/src/main/services/hosters/datanodes.ts +++ b/src/main/services/hosters/datanodes.ts @@ -33,7 +33,8 @@ export class DatanodesApi { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36", }, - maxRedirects: 0, validateStatus: (status: number) => status === 302 || status < 400, + maxRedirects: 0, + validateStatus: (status: number) => status === 302 || status < 400, } ); diff --git a/src/renderer/src/pages/settings/settings-account.tsx b/src/renderer/src/pages/settings/settings-account.tsx index 16ed40e4..14fc3564 100644 --- a/src/renderer/src/pages/settings/settings-account.tsx +++ b/src/renderer/src/pages/settings/settings-account.tsx @@ -1,5 +1,5 @@ import { Button, SelectField } from "@renderer/components"; -import { SPACING_UNIT, vars } from "@renderer/theme.css"; +import { SPACING_UNIT } from "@renderer/theme.css"; import { Controller, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; @@ -36,9 +36,13 @@ export function SettingsAccount() { handleSubmit, } = useForm(); - const { patchUser, userDetails } = useUserDetails(); - - const { unblockUser } = useUserDetails(); + const { + userDetails, + patchUser, + fetchUserDetails, + updateUserDetails, + unblockUser, + } = useUserDetails(); useEffect(() => { if (userDetails?.profileVisibility) { @@ -46,6 +50,14 @@ export function SettingsAccount() { } }, [userDetails, setValue]); + useEffect(() => { + fetchUserDetails().then((response) => { + if (response) { + updateUserDetails(response); + } + }); + }, []); + const visibilityOptions = [ { value: "PUBLIC", label: t("public") }, { value: "FRIENDS", label: t("friends_only") }, From ffd3e37b484994a80de531ce499cb927ba68a5ec Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:57:25 -0300 Subject: [PATCH 06/28] feat: refactor --- src/main/services/hydra-api.ts | 56 +++++++++++++++++----------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/main/services/hydra-api.ts b/src/main/services/hydra-api.ts index 4ab20e80..16bbc21f 100644 --- a/src/main/services/hydra-api.ts +++ b/src/main/services/hydra-api.ts @@ -216,41 +216,41 @@ export class HydraApi { } public static async refreshToken() { - return this.instance + const { accessToken, expiresIn } = await this.instance .post<{ accessToken: string; expiresIn: number }>(`/auth/refresh`, { refreshToken: this.userAuth.refreshToken, }) .then((response) => response.data); + + const tokenExpirationTimestamp = + Date.now() + + this.secondsToMilliseconds(expiresIn) - + this.EXPIRATION_OFFSET_IN_MS; + + this.userAuth.authToken = accessToken; + this.userAuth.expirationTimestamp = tokenExpirationTimestamp; + + logger.log( + "Token refreshed. New expiration:", + this.userAuth.expirationTimestamp + ); + + userAuthRepository.upsert( + { + id: 1, + accessToken, + tokenExpirationTimestamp, + }, + ["id"] + ); + + return { accessToken, expiresIn }; } private static async revalidateAccessTokenIfExpired() { - const now = new Date(); - - if (this.userAuth.expirationTimestamp < now.getTime()) { + if (this.userAuth.expirationTimestamp < Date.now()) { try { - const { accessToken, expiresIn } = await this.refreshToken(); - - const tokenExpirationTimestamp = - now.getTime() + - this.secondsToMilliseconds(expiresIn) - - this.EXPIRATION_OFFSET_IN_MS; - - this.userAuth.authToken = accessToken; - this.userAuth.expirationTimestamp = tokenExpirationTimestamp; - - logger.log( - "Token refreshed. New expiration:", - this.userAuth.expirationTimestamp - ); - - userAuthRepository.upsert( - { - id: 1, - accessToken, - tokenExpirationTimestamp, - }, - ["id"] - ); + await this.refreshToken(); } catch (err) { this.handleUnauthorizedError(err); } @@ -265,7 +265,7 @@ export class HydraApi { }; } - private static handleUnauthorizedError = (err) => { + private static readonly handleUnauthorizedError = (err) => { if (err instanceof AxiosError && err.response?.status === 401) { logger.error( "401 - Current credentials:", From 56fabb288110e7809c3c0be1f75451006ea7fd36 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 15 Jan 2025 17:08:07 -0300 Subject: [PATCH 07/28] fix: hook dependencies --- src/renderer/src/pages/settings/settings-account.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/pages/settings/settings-account.tsx b/src/renderer/src/pages/settings/settings-account.tsx index 14fc3564..89f5bde1 100644 --- a/src/renderer/src/pages/settings/settings-account.tsx +++ b/src/renderer/src/pages/settings/settings-account.tsx @@ -56,7 +56,7 @@ export function SettingsAccount() { updateUserDetails(response); } }); - }, []); + }, [fetchUserDetails, updateUserDetails]); const visibilityOptions = [ { value: "PUBLIC", label: t("public") }, From 15f721ac3935d1bfc0ae7c32b8f88e90fbafbcfa Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 15 Jan 2025 17:11:02 -0300 Subject: [PATCH 08/28] feat: use Avatar component and remove non null assertion --- src/renderer/src/pages/settings/settings-account.css.ts | 7 ------- src/renderer/src/pages/settings/settings-account.tsx | 9 +++++---- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/renderer/src/pages/settings/settings-account.css.ts b/src/renderer/src/pages/settings/settings-account.css.ts index 2aec8cd0..44ec0d14 100644 --- a/src/renderer/src/pages/settings/settings-account.css.ts +++ b/src/renderer/src/pages/settings/settings-account.css.ts @@ -8,13 +8,6 @@ export const form = style({ gap: `${SPACING_UNIT}px`, }); -export const blockedUserAvatar = style({ - width: "32px", - height: "32px", - borderRadius: "4px", - filter: "grayscale(100%)", -}); - export const blockedUser = style({ display: "flex", minWidth: "240px", diff --git a/src/renderer/src/pages/settings/settings-account.tsx b/src/renderer/src/pages/settings/settings-account.tsx index 89f5bde1..0aedb4b8 100644 --- a/src/renderer/src/pages/settings/settings-account.tsx +++ b/src/renderer/src/pages/settings/settings-account.tsx @@ -1,4 +1,4 @@ -import { Button, SelectField } from "@renderer/components"; +import { Avatar, Button, SelectField } from "@renderer/components"; import { SPACING_UNIT } from "@renderer/theme.css"; import { Controller, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; @@ -201,10 +201,11 @@ export function SettingsAccount() { alignItems: "center", }} > - {user.displayName} {user.displayName} From 9941460c60afab8a9063c2cfcf55ce87cadd006d Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 15 Jan 2025 17:13:36 -0300 Subject: [PATCH 09/28] feat: code review --- src/renderer/src/pages/settings/settings-account.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/pages/settings/settings-account.tsx b/src/renderer/src/pages/settings/settings-account.tsx index 0aedb4b8..6ba4cb7a 100644 --- a/src/renderer/src/pages/settings/settings-account.tsx +++ b/src/renderer/src/pages/settings/settings-account.tsx @@ -163,7 +163,7 @@ export function SettingsAccount() { {userDetails?.subscription?.expiresAt ? (

{t("subscription_active_until", { - date: formatDate(userDetails?.subscription?.expiresAt), + date: formatDate(userDetails.subscription.expiresAt), })}

) : ( From 44fd971c9554cdce4261098b66a98595f07a63e1 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 15 Jan 2025 23:56:37 -0300 Subject: [PATCH 10/28] feat: refactor open auth --- src/locales/en/translation.json | 2 +- src/locales/pt-BR/translation.json | 2 +- src/main/events/auth/open-auth-window.ts | 21 +++++++++++++--- src/main/events/index.ts | 1 - src/main/events/misc/open-manage-account.ts | 25 ------------------- src/main/services/window-manager.ts | 11 +++----- src/preload/index.ts | 8 +++--- .../components/sidebar/sidebar-profile.tsx | 5 ++-- src/renderer/src/declaration.d.ts | 6 ++--- .../game-details/game-details-content.tsx | 6 ++--- .../src/pages/settings/settings-account.tsx | 9 ++++--- src/shared/constants.ts | 6 +++++ src/types/index.ts | 2 -- 13 files changed, 47 insertions(+), 57 deletions(-) delete mode 100644 src/main/events/misc/open-manage-account.ts diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index e55954bf..a79b1107 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -291,7 +291,7 @@ "update_email": "Update email", "update_password": "Update password", "current_email": "Current email:", - "no_associated_email": "You don't have an associated email yet" + "no_email_account": "You have not set an email yet" }, "notifications": { "download_complete": "Download complete", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 24ab4653..62565165 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -279,7 +279,7 @@ "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" + "no_email_account": "Você ainda não adicionou um email a sua conta" }, "notifications": { "download_complete": "Download concluído", diff --git a/src/main/events/auth/open-auth-window.ts b/src/main/events/auth/open-auth-window.ts index e93a5a42..84b333d2 100644 --- a/src/main/events/auth/open-auth-window.ts +++ b/src/main/events/auth/open-auth-window.ts @@ -1,7 +1,22 @@ +import i18next from "i18next"; import { registerEvent } from "../register-event"; -import { WindowManager } from "@main/services"; +import { HydraApi, WindowManager } from "@main/services"; +import { AuthPage } from "@shared"; -const openAuthWindow = async (_event: Electron.IpcMainInvokeEvent) => - WindowManager.openAuthWindow(); +const openAuthWindow = async ( + _event: Electron.IpcMainInvokeEvent, + page: AuthPage +) => { + const searchParams = new URLSearchParams({ + lng: i18next.language, + }); + + if ([AuthPage.UpdateEmail, AuthPage.UpdatePassword].includes(page)) { + const { accessToken } = await HydraApi.refreshToken(); + searchParams.set("token", accessToken); + } + + return WindowManager.openAuthWindow(page, searchParams); +}; registerEvent("openAuthWindow", openAuthWindow); diff --git a/src/main/events/index.ts b/src/main/events/index.ts index f0f882ca..25882c3f 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -30,7 +30,6 @@ import "./library/remove-game-from-library"; import "./library/select-game-wine-prefix"; import "./library/reset-game-achievements"; import "./misc/open-checkout"; -import "./misc/open-manage-account"; import "./misc/open-external"; import "./misc/show-open-dialog"; import "./misc/get-features"; diff --git a/src/main/events/misc/open-manage-account.ts b/src/main/events/misc/open-manage-account.ts deleted file mode 100644 index 2946b04b..00000000 --- a/src/main/events/misc/open-manage-account.ts +++ /dev/null @@ -1,25 +0,0 @@ -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); diff --git a/src/main/services/window-manager.ts b/src/main/services/window-manager.ts index a7cfcee2..adf34a42 100644 --- a/src/main/services/window-manager.ts +++ b/src/main/services/window-manager.ts @@ -9,7 +9,7 @@ import { shell, } from "electron"; import { is } from "@electron-toolkit/utils"; -import i18next, { t } from "i18next"; +import { t } from "i18next"; import path from "node:path"; import icon from "@resources/icon.png?asset"; import trayIcon from "@resources/tray-icon.png?asset"; @@ -17,6 +17,7 @@ import { gameRepository, userPreferencesRepository } from "@main/repository"; import { IsNull, Not } from "typeorm"; import { HydraApi } from "./hydra-api"; import UserAgent from "user-agents"; +import { AuthPage } from "@shared"; export class WindowManager { public static mainWindow: Electron.BrowserWindow | null = null; @@ -142,7 +143,7 @@ export class WindowManager { }); } - public static openAuthWindow() { + public static openAuthWindow(page: AuthPage, searchParams: URLSearchParams) { if (this.mainWindow) { const authWindow = new BrowserWindow({ width: 600, @@ -164,12 +165,8 @@ export class WindowManager { if (!app.isPackaged) authWindow.webContents.openDevTools(); - const searchParams = new URLSearchParams({ - lng: i18next.language, - }); - authWindow.loadURL( - `${import.meta.env.MAIN_VITE_AUTH_URL}/?${searchParams.toString()}` + `${import.meta.env.MAIN_VITE_AUTH_URL}/${page}?${searchParams.toString()}` ); authWindow.once("ready-to-show", () => { diff --git a/src/preload/index.ts b/src/preload/index.ts index 24e6cf39..07b4ec99 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -14,9 +14,8 @@ import type { CatalogueSearchPayload, SeedingStatus, GameAchievement, - ManageAccountPage, } from "@types"; -import type { CatalogueCategory } from "@shared"; +import type { AuthPage, CatalogueCategory } from "@shared"; import type { AxiosProgressEvent } from "axios"; contextBridge.exposeInMainWorld("electron", { @@ -227,8 +226,6 @@ contextBridge.exposeInMainWorld("electron", { isPortableVersion: () => ipcRenderer.invoke("isPortableVersion"), openExternal: (src: string) => ipcRenderer.invoke("openExternal", src), openCheckout: () => ipcRenderer.invoke("openCheckout"), - openManageAccount: (page: ManageAccountPage) => - ipcRenderer.invoke("openManageAccount", page), showOpenDialog: (options: Electron.OpenDialogOptions) => ipcRenderer.invoke("showOpenDialog", options), showItemInFolder: (path: string) => @@ -294,7 +291,8 @@ contextBridge.exposeInMainWorld("electron", { /* Auth */ signOut: () => ipcRenderer.invoke("signOut"), - openAuthWindow: () => ipcRenderer.invoke("openAuthWindow"), + openAuthWindow: (page: AuthPage) => + ipcRenderer.invoke("openAuthWindow", page), getSessionHash: () => ipcRenderer.invoke("getSessionHash"), onSignIn: (cb: () => void) => { const listener = (_event: Electron.IpcRendererEvent) => cb(); diff --git a/src/renderer/src/components/sidebar/sidebar-profile.tsx b/src/renderer/src/components/sidebar/sidebar-profile.tsx index 49e56ab7..3897ac54 100644 --- a/src/renderer/src/components/sidebar/sidebar-profile.tsx +++ b/src/renderer/src/components/sidebar/sidebar-profile.tsx @@ -7,6 +7,7 @@ import { useTranslation } from "react-i18next"; import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal"; import SteamLogo from "@renderer/assets/steam-logo.svg?react"; import { Avatar } from "../avatar/avatar"; +import { AuthPage } from "@shared"; const LONG_POLLING_INTERVAL = 120_000; @@ -26,11 +27,11 @@ export function SidebarProfile() { const handleProfileClick = () => { if (userDetails === null) { - window.electron.openAuthWindow(); + window.electron.openAuthWindow(AuthPage.SignIn); return; } - navigate(`/profile/${userDetails!.id}`); + navigate(`/profile/${userDetails.id}`); }; useEffect(() => { diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 33fcb7da..d2e50fde 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -1,4 +1,4 @@ -import type { CatalogueCategory } from "@shared"; +import type { AuthPage, CatalogueCategory } from "@shared"; import type { AppUpdaterEvent, Game, @@ -29,7 +29,6 @@ import type { UserAchievement, ComparedAchievements, CatalogueSearchPayload, - ManageAccountPage, } from "@types"; import type { AxiosProgressEvent } from "axios"; import type disk from "diskusage"; @@ -188,7 +187,6 @@ declare global { /* Misc */ openExternal: (src: string) => Promise; openCheckout: () => Promise; - openManageAccount: (page: ManageAccountPage) => Promise; getVersion: () => Promise; isStaging: () => Promise; ping: () => string; @@ -210,7 +208,7 @@ declare global { /* Auth */ signOut: () => Promise; - openAuthWindow: () => Promise; + openAuthWindow: (page: AuthPage) => Promise; getSessionHash: () => Promise; onSignIn: (cb: () => void) => () => Electron.IpcRenderer; onSignOut: (cb: () => void) => () => Electron.IpcRenderer; diff --git a/src/renderer/src/pages/game-details/game-details-content.tsx b/src/renderer/src/pages/game-details/game-details-content.tsx index 12495231..bd138e81 100644 --- a/src/renderer/src/pages/game-details/game-details-content.tsx +++ b/src/renderer/src/pages/game-details/game-details-content.tsx @@ -10,7 +10,7 @@ import { Sidebar } from "./sidebar/sidebar"; import * as styles from "./game-details.css"; import { useTranslation } from "react-i18next"; import { cloudSyncContext, gameDetailsContext } from "@renderer/context"; -import { steamUrlBuilder } from "@shared"; +import { AuthPage, steamUrlBuilder } from "@shared"; import cloudIconAnimated from "@renderer/assets/icons/cloud-animated.gif"; import { useUserDetails } from "@renderer/hooks"; @@ -69,7 +69,7 @@ export function GameDetailsContent() { }); const backgroundColor = output - ? (new Color(output).darken(0.7).toString() as string) + ? new Color(output).darken(0.7).toString() : ""; setGameColor(backgroundColor); @@ -101,7 +101,7 @@ export function GameDetailsContent() { const handleCloudSaveButtonClick = () => { if (!userDetails) { - window.electron.openAuthWindow(); + window.electron.openAuthWindow(AuthPage.SignIn); return; } diff --git a/src/renderer/src/pages/settings/settings-account.tsx b/src/renderer/src/pages/settings/settings-account.tsx index 6ba4cb7a..4a19819d 100644 --- a/src/renderer/src/pages/settings/settings-account.tsx +++ b/src/renderer/src/pages/settings/settings-account.tsx @@ -13,6 +13,7 @@ import { XCircleFillIcon, } from "@primer/octicons-react"; import { settingsContext } from "@renderer/context"; +import { AuthPage } from "@shared"; interface FormValues { profileVisibility: "PUBLIC" | "FRIENDS" | "PRIVATE"; @@ -128,7 +129,7 @@ export function SettingsAccount() {

{userDetails.email}

) : ( -

{t("no_associated_email")}

+

{t("no_email_account")}

)}
+
+

Hydra Cloud

+ {getHydraCloudSectionContent().description}
+ +

{t("blocked_users")}

From ff0ef740664a7cd06363018a606a27e6ef7677ff Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 16 Jan 2025 09:38:28 -0300 Subject: [PATCH 13/28] feat: update icon order --- src/renderer/src/pages/settings/settings-account.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/renderer/src/pages/settings/settings-account.tsx b/src/renderer/src/pages/settings/settings-account.tsx index 79303703..76feb957 100644 --- a/src/renderer/src/pages/settings/settings-account.tsx +++ b/src/renderer/src/pages/settings/settings-account.tsx @@ -174,8 +174,8 @@ export function SettingsAccount() { theme="outline" onClick={() => window.electron.openAuthWindow(AuthPage.UpdateEmail)} > - {t("update_email")} + {t("update_email")}
@@ -199,8 +199,8 @@ export function SettingsAccount() { theme="outline" onClick={() => window.electron.openCheckout()} > - {getHydraCloudSectionContent().callToAction} + {getHydraCloudSectionContent().callToAction}

From 153ab051747367eb7743e8549a7a2b6a503262cb Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 16 Jan 2025 09:47:24 -0300 Subject: [PATCH 14/28] feat: remove unused string --- src/locales/en/translation.json | 1 - src/locales/pt-BR/translation.json | 1 - 2 files changed, 2 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index e894727c..30913b2b 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -284,7 +284,6 @@ "account": "Account", "no_users_blocked": "You have no blocked users", "subscription_active_until": "Enjoy your Hydra Cloud until {{date}}", - "subscription_not_active": "You don't have an active Hydra Cloud subscription", "manage_subscription": "Manage subscription", "update_email": "Update email", "update_password": "Update password", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 8f22f5c2..70052f28 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -272,7 +272,6 @@ "account": "Conta", "no_users_blocked": "Você não bloqueou nenhum usuário", "subscription_active_until": "Aproveite seu Hydra Cloud até {{date}}", - "subscription_not_active": "Você não possui uma assinatura Hydra Cloud ativa", "manage_subscription": "Gerenciar assinatura", "update_email": "Atualizar email", "update_password": "Atualizar senha", From 923f7d7e806730c721269df1b32899565fd2f8ee Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 16 Jan 2025 09:48:08 -0300 Subject: [PATCH 15/28] feat: review --- src/main/events/auth/open-auth-window.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/events/auth/open-auth-window.ts b/src/main/events/auth/open-auth-window.ts index 84b333d2..925dbdd3 100644 --- a/src/main/events/auth/open-auth-window.ts +++ b/src/main/events/auth/open-auth-window.ts @@ -16,7 +16,7 @@ const openAuthWindow = async ( searchParams.set("token", accessToken); } - return WindowManager.openAuthWindow(page, searchParams); + WindowManager.openAuthWindow(page, searchParams); }; registerEvent("openAuthWindow", openAuthWindow); From 81cb73c24333710277211895b54baa6e6314372d Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:22:10 -0300 Subject: [PATCH 16/28] feat: code suggestion --- src/main/services/window-manager.ts | 2 +- src/shared/constants.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/services/window-manager.ts b/src/main/services/window-manager.ts index 47fc92e0..f7e82f07 100644 --- a/src/main/services/window-manager.ts +++ b/src/main/services/window-manager.ts @@ -166,7 +166,7 @@ export class WindowManager { if (!app.isPackaged) authWindow.webContents.openDevTools(); authWindow.loadURL( - `${import.meta.env.MAIN_VITE_AUTH_URL}/${page}?${searchParams.toString()}` + `${import.meta.env.MAIN_VITE_AUTH_URL}${page}?${searchParams.toString()}` ); authWindow.once("ready-to-show", () => { diff --git a/src/shared/constants.ts b/src/shared/constants.ts index 62a97d83..f2bcc793 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -44,7 +44,7 @@ export enum Cracker { } export enum AuthPage { - SignIn = "", - UpdateEmail = "update-email", - UpdatePassword = "update-password", + SignIn = "/", + UpdateEmail = "/update-email", + UpdatePassword = "/update-password", } From 2346a5bf86d16d19477a2d4f788c45623f394d3b Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:32:59 -0300 Subject: [PATCH 17/28] feat: add renewal info --- src/locales/en/translation.json | 5 ++- src/locales/pt-BR/translation.json | 7 +++- .../src/pages/settings/settings-account.tsx | 39 ++++++++++++++----- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 30913b2b..55eac438 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -293,7 +293,10 @@ "renew_subscription": "Renew Hydra Cloud", "subscription_expired_at": "Your subscription expired at {{date}}", "no_subscription": "Enjoy Hydra in the best possible way", - "become_subscriber": "Be Hydra Cloud" + "become_subscriber": "Be Hydra Cloud", + "subscription_renew_cancelled": "Automatic renewal is not active", + "subscription_renews_on": "Your subscription renews on {{date}}", + "bill_sent_until": "You next bill will be sent until this day" }, "notifications": { "download_complete": "Download complete", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 70052f28..13c75e4d 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -280,8 +280,11 @@ "account_data_updated_successfully": "Dados da conta atualizados com sucesso", "renew_subscription": "Renovar Hydra Cloud", "subscription_expired_at": "Sua assinatura expirou em {{date}}", - "no_subscription": "Aproveite o Hydra ao seu máximo", - "become_subscriber": "Seja Hydra Cloud" + "no_subscription": "Aproveite o Hydra da melhor forma possível", + "become_subscriber": "Seja Hydra Cloud", + "subscription_renew_cancelled": "A renovação automática está desativada", + "subscription_renews_on": "Sua assinatura renova dia {{date}}", + "bill_sent_until": "Sua próxima cobrança será enviada até esse dia" }, "notifications": { "download_complete": "Download concluído", diff --git a/src/renderer/src/pages/settings/settings-account.tsx b/src/renderer/src/pages/settings/settings-account.tsx index 76feb957..6357a243 100644 --- a/src/renderer/src/pages/settings/settings-account.tsx +++ b/src/renderer/src/pages/settings/settings-account.tsx @@ -96,27 +96,48 @@ export function SettingsAccount() { const getHydraCloudSectionContent = () => { const hasSubscribedBefore = Boolean(userDetails?.subscription?.expiresAt); + const isRenewalActive = userDetails?.subscription?.status === "active"; if (!hasSubscribedBefore) { return { - description: t("no_subscription"), + description: {t("no_subscription")}, callToAction: t("become_subscriber"), }; } if (hasActiveSubscription) { return { - description: t("subscription_active_until", { - date: formatDate(userDetails!.subscription!.expiresAt!), - }), + description: isRenewalActive ? ( + <> + + {t("subscription_renews_on", { + date: formatDate(userDetails.subscription!.expiresAt!), + })} + + {t("bill_sent_until")} + + ) : ( + <> + {t("subscription_renew_cancelled")} + + {t("subscription_active_until", { + date: formatDate(userDetails!.subscription!.expiresAt!), + })} + + + ), callToAction: t("manage_subscription"), }; } return { - description: t("subscription_expired_at", { - date: formatDate(userDetails!.subscription!.expiresAt!), - }), + description: ( + + {t("subscription_expired_at", { + date: formatDate(userDetails!.subscription!.expiresAt!), + })} + + ), callToAction: t("renew_subscription"), }; }; @@ -189,9 +210,9 @@ export function SettingsAccount() { -
+

Hydra Cloud

- {getHydraCloudSectionContent().description} + {getHydraCloudSectionContent().description}
-
-

Hydra Cloud

+
+

Hydra Cloud

{getHydraCloudSectionContent().description}
From b06339d362815d6d04c712abd2cc7ffd4f1de867 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:55:13 -0300 Subject: [PATCH 19/28] feat: handle refreshToken failure --- src/main/events/auth/open-auth-window.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/events/auth/open-auth-window.ts b/src/main/events/auth/open-auth-window.ts index 925dbdd3..0f5ec371 100644 --- a/src/main/events/auth/open-auth-window.ts +++ b/src/main/events/auth/open-auth-window.ts @@ -12,7 +12,9 @@ const openAuthWindow = async ( }); if ([AuthPage.UpdateEmail, AuthPage.UpdatePassword].includes(page)) { - const { accessToken } = await HydraApi.refreshToken(); + const { accessToken } = await HydraApi.refreshToken().catch(() => { + return { accessToken: "" }; + }); searchParams.set("token", accessToken); } From 5d0e8258808b45f6c8bd49666bbab5c2aea70df3 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:57:28 -0300 Subject: [PATCH 20/28] fix: i18n --- src/locales/en/translation.json | 2 +- src/locales/pt-BR/translation.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 0288cdf7..1baf4614 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -296,7 +296,7 @@ "become_subscriber": "Be Hydra Cloud", "subscription_renew_cancelled": "Automatic renewal is disabled", "subscription_renews_on": "Your subscription renews on {{date}}", - "bill_sent_until": "You next bill will be sent until this day" + "bill_sent_until": "Your next bill will be sent until this day" }, "notifications": { "download_complete": "Download complete", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 23093f22..ed891f04 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -413,7 +413,7 @@ "new_achievements_unlocked": "{{achievementCount}} novas conquistas de {{gameCount}} jogos", "achievement_progress": "{{unlockedCount}}/{{totalCount}} conquistas", "achievements_unlocked_for_game": "Desbloqueadas {{achievementCount}} novas conquistas em {{gameTitle}}", - "hidden_achievement_tooltip": "Está é uma conquista oculta", + "hidden_achievement_tooltip": "Esta é uma conquista oculta", "achievement_earn_points": "Ganhe {{points}} pontos com essa conquista", "earned_points": "Pontos ganhos:", "available_points": "Pontos disponíveis:", From d1fa4895e46d3f3be21d73314918ab452a84bbc3 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 16 Jan 2025 12:58:09 -0300 Subject: [PATCH 21/28] feat: adjust spacing --- src/locales/pt-BR/translation.json | 2 +- .../pages/settings/settings-account.css.ts | 3 +- .../src/pages/settings/settings-account.tsx | 167 ++++++++++-------- 3 files changed, 93 insertions(+), 79 deletions(-) diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index ed891f04..9e1021fc 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -271,7 +271,7 @@ "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_active_until": "Sua assinatura Hydra Cloud estará ativa até {{date}}", + "subscription_active_until": "Sua assinatura Hydra Cloud ficará ativa até {{date}}", "manage_subscription": "Gerenciar assinatura", "update_email": "Atualizar email", "update_password": "Atualizar senha", diff --git a/src/renderer/src/pages/settings/settings-account.css.ts b/src/renderer/src/pages/settings/settings-account.css.ts index 44ec0d14..8fbb3845 100644 --- a/src/renderer/src/pages/settings/settings-account.css.ts +++ b/src/renderer/src/pages/settings/settings-account.css.ts @@ -5,7 +5,7 @@ import { SPACING_UNIT, vars } from "../../theme.css"; export const form = style({ display: "flex", flexDirection: "column", - gap: `${SPACING_UNIT}px`, + gap: `${SPACING_UNIT * 3}px`, }); export const blockedUser = style({ @@ -36,5 +36,4 @@ export const blockedUsersList = style({ flexDirection: "column", alignItems: "flex-start", gap: `${SPACING_UNIT}px`, - marginTop: `${SPACING_UNIT}px`, }); diff --git a/src/renderer/src/pages/settings/settings-account.tsx b/src/renderer/src/pages/settings/settings-account.tsx index aa8a7596..d43e5b2d 100644 --- a/src/renderer/src/pages/settings/settings-account.tsx +++ b/src/renderer/src/pages/settings/settings-account.tsx @@ -158,7 +158,7 @@ export function SettingsAccount() { }; return ( - <> +
{t("profile_visibility_description")} - +
); }} /> -
+

{t("current_email")}

{userDetails?.email ?? t("no_email_account")}

-
-
- + - -
+ +
+ -

Hydra Cloud

- {getHydraCloudSectionContent().description} -
+
+ {getHydraCloudSectionContent().description} +
- + + +
- - {getHydraCloudSectionContent().callToAction} - +

{t("blocked_users")}

-

- {t("blocked_users")} -

- -
    {blockedUsers.length > 0 ? ( - blockedUsers.map((user) => { - return ( -
  • -
    - - {user.displayName} -
    +
      + {blockedUsers.map((user) => { + return ( +
    • +
      + + {user.displayName} +
      - -
    • - ); - }) + + + ); + })} +
    ) : ( {t("no_users_blocked")} )} -
+
); } From 8ebb5edfbc61d172ddd39207c9961f84cef73d83 Mon Sep 17 00:00:00 2001 From: hydrasources Date: Thu, 16 Jan 2025 21:29:28 +0300 Subject: [PATCH 22/28] Update translation.json --- src/locales/ru/translation.json | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index 92008a5e..f96ef495 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -278,7 +278,23 @@ "source_already_exists": "Этот источник уже добавлен", "user_unblocked": "Пользователь разблокирован", "seed_after_download_complete": "Раздавать после завершения загрузки", - "show_hidden_achievement_description": "Показывать описание скрытых достижений перед их получением" + "show_hidden_achievement_description": "Показывать описание скрытых достижений перед их получением", + "account": "Аккаунт", + "no_users_blocked": "У вас нет заблокированных пользователей", + "subscription_active_until": "Ваша подписка на Hydra Cloud активна до {{date}}", + "manage_subscription": "Управлять подпиской", + "update_email": "Обновить электронную почту", + "update_password": "Обновить пароль", + "current_email": "Текущий email:", + "no_email_account": "Вы еще не установили электронную почту", + "account_data_updated_successfully": "Данные учетной записи успешно обновлены", + "renew_subscription": "Обновить подписку Hydra Cloud", + "subscription_expired_at": "Срок действия вашей подписки истек в {{date}}", + "no_subscription": "Наслаждайтесь Hydra по максимуму", + "become_subscriber": "Станьте обладателем Hydra Cloud", + "subscription_renew_cancelled": "Автоматическое продление отключено", + "subscription_renews_on": "Ваша подписка продлевается на {{date}}", + "bill_sent_until": "Ваш следующий счет будет отправлен до этого дня" }, "notifications": { "download_complete": "Загрузка завершена", From 2ba653429fadc7455538cf7fc0196c13701bfa0f Mon Sep 17 00:00:00 2001 From: vitorRibeiro7 Date: Thu, 16 Jan 2025 18:20:08 -0300 Subject: [PATCH 23/28] fix: fix css bug on requirements details style --- src/renderer/src/pages/game-details/sidebar/sidebar.css.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts b/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts index aa27cd42..8242e7c9 100644 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts @@ -34,6 +34,7 @@ export const requirementButton = style({ }); export const requirementsDetails = style({ + minHeight: "500px", padding: `${SPACING_UNIT * 2}px`, lineHeight: "22px", fontSize: "16px", From 049c27cdb7d8e7e104a722327a220e8e785633bf Mon Sep 17 00:00:00 2001 From: vitorRibeiro7 Date: Fri, 17 Jan 2025 11:32:28 -0300 Subject: [PATCH 24/28] fix; revert minHeight --- src/renderer/src/pages/game-details/sidebar/sidebar.css.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts b/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts index 8242e7c9..aa27cd42 100644 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts @@ -34,7 +34,6 @@ export const requirementButton = style({ }); export const requirementsDetails = style({ - minHeight: "500px", padding: `${SPACING_UNIT * 2}px`, lineHeight: "22px", fontSize: "16px", From 4e34f41ee0d60a1c2138475a8cc36028131dad13 Mon Sep 17 00:00:00 2001 From: vitorRibeiro7 Date: Fri, 17 Jan 2025 11:33:44 -0300 Subject: [PATCH 25/28] ench: remove maxHeight on sidebar section --- .../src/pages/game-details/sidebar-section/sidebar-section.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx index da9d078f..adbc5930 100644 --- a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx +++ b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx @@ -26,8 +26,6 @@ export function SidebarSection({ title, children }: SidebarSectionProps) {
Date: Fri, 17 Jan 2025 11:54:14 -0300 Subject: [PATCH 26/28] fix: reset overflow hidden --- .../src/pages/game-details/sidebar-section/sidebar-section.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx index adbc5930..4858cc0a 100644 --- a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx +++ b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx @@ -26,6 +26,7 @@ export function SidebarSection({ title, children }: SidebarSectionProps) {
Date: Fri, 17 Jan 2025 11:55:03 -0300 Subject: [PATCH 27/28] ench: add dynamic height --- .../game-details/sidebar-section/sidebar-section.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx index 4858cc0a..4b01adeb 100644 --- a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx +++ b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx @@ -1,5 +1,5 @@ import { ChevronDownIcon } from "@primer/octicons-react"; -import { useRef, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import * as styles from "./sidebar-section.css"; @@ -11,6 +11,13 @@ export interface SidebarSectionProps { export function SidebarSection({ title, children }: SidebarSectionProps) { const content = useRef(null); const [isOpen, setIsOpen] = useState(true); + const [height, setHeight] = useState(0); + + useEffect(() => { + if (content.current) { + setHeight(isOpen ? content.current.scrollHeight : 0); + } + }, [isOpen, children]); return (
@@ -26,6 +33,7 @@ export function SidebarSection({ title, children }: SidebarSectionProps) {
Date: Fri, 17 Jan 2025 12:25:35 -0300 Subject: [PATCH 28/28] refactor: add non re-render rules to useEffect --- .../pages/game-details/sidebar-section/sidebar-section.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx index 4b01adeb..e24f677b 100644 --- a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx +++ b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx @@ -14,10 +14,12 @@ export function SidebarSection({ title, children }: SidebarSectionProps) { const [height, setHeight] = useState(0); useEffect(() => { - if (content.current) { + if (content.current && content.current.scrollHeight !== height) { setHeight(isOpen ? content.current.scrollHeight : 0); + } else if (!isOpen) { + setHeight(0); } - }, [isOpen, children]); + }, [isOpen, children, height]); return (