Skip to content

Commit

Permalink
Merge pull request #1402 from hydralauncher/feat/manage-account-buttons
Browse files Browse the repository at this point in the history
feat: manage account buttons
  • Loading branch information
zamitto authored Jan 16, 2025
2 parents d4be5b8 + d1fa489 commit 69787ee
Show file tree
Hide file tree
Showing 15 changed files with 421 additions and 205 deletions.
18 changes: 17 additions & 1 deletion src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,23 @@
"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_active_until": "Your Hydra Cloud is active until {{date}}",
"manage_subscription": "Manage subscription",
"update_email": "Update email",
"update_password": "Update password",
"current_email": "Current email:",
"no_email_account": "You have not set an email yet",
"account_data_updated_successfully": "Account data updated successfully",
"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",
"subscription_renew_cancelled": "Automatic renewal is disabled",
"subscription_renews_on": "Your subscription renews on {{date}}",
"bill_sent_until": "Your next bill will be sent until this day"
},
"notifications": {
"download_complete": "Download complete",
Expand Down
20 changes: 18 additions & 2 deletions src/locales/pt-BR/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,23 @@
"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_active_until": "Sua assinatura Hydra Cloud ficará ativa até {{date}}",
"manage_subscription": "Gerenciar assinatura",
"update_email": "Atualizar email",
"update_password": "Atualizar senha",
"current_email": "Email atual:",
"no_email_account": "Você ainda não adicionou um email a sua conta",
"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 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",
Expand Down Expand Up @@ -397,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:",
Expand Down
23 changes: 20 additions & 3 deletions src/main/events/auth/open-auth-window.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
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().catch(() => {
return { accessToken: "" };
});
searchParams.set("token", accessToken);
}

WindowManager.openAuthWindow(page, searchParams);
};

registerEvent("openAuthWindow", openAuthWindow);
3 changes: 2 additions & 1 deletion src/main/services/hosters/datanodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
);

Expand Down
66 changes: 35 additions & 31 deletions src/main/services/hydra-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,38 +215,42 @@ export class HydraApi {
}
}

private static async revalidateAccessTokenIfExpired() {
const now = new Date();
public static async refreshToken() {
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;

if (this.userAuth.expirationTimestamp < now.getTime()) {
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() {
if (this.userAuth.expirationTimestamp < Date.now()) {
try {
const response = await this.instance.post(`/auth/refresh`, {
refreshToken: this.userAuth.refreshToken,
});

const { accessToken, expiresIn } = response.data;

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);
}
Expand All @@ -261,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:",
Expand Down
18 changes: 11 additions & 7 deletions src/main/services/window-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ 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";
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;
Expand Down Expand Up @@ -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,
Expand All @@ -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", () => {
Expand All @@ -181,6 +178,13 @@ export class WindowManager {
authWindow.close();

HydraApi.handleExternalAuth(url);
return;
}

if (url.startsWith("hydralauncher://update-account")) {
authWindow.close();

WindowManager.mainWindow?.webContents.send("on-account-updated");
}
});
}
Expand Down
10 changes: 8 additions & 2 deletions src/preload/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import type {
SeedingStatus,
GameAchievement,
} from "@types";
import type { CatalogueCategory } from "@shared";
import type { AuthPage, CatalogueCategory } from "@shared";
import type { AxiosProgressEvent } from "axios";

contextBridge.exposeInMainWorld("electron", {
Expand Down Expand Up @@ -291,13 +291,19 @@ 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();
ipcRenderer.on("on-signin", listener);
return () => ipcRenderer.removeListener("on-signin", listener);
},
onAccountUpdated: (cb: () => void) => {
const listener = (_event: Electron.IpcRendererEvent) => cb();
ipcRenderer.on("on-account-updated", listener);
return () => ipcRenderer.removeListener("on-account-updated", listener);
},
onSignOut: (cb: () => void) => {
const listener = (_event: Electron.IpcRendererEvent) => cb();
ipcRenderer.on("on-signout", listener);
Expand Down
5 changes: 3 additions & 2 deletions src/renderer/src/components/sidebar/sidebar-profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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(() => {
Expand Down
5 changes: 3 additions & 2 deletions src/renderer/src/declaration.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { CatalogueCategory } from "@shared";
import type { AuthPage, CatalogueCategory } from "@shared";
import type {
AppUpdaterEvent,
Game,
Expand Down Expand Up @@ -208,9 +208,10 @@ declare global {

/* Auth */
signOut: () => Promise<void>;
openAuthWindow: () => Promise<void>;
openAuthWindow: (page: AuthPage) => Promise<void>;
getSessionHash: () => Promise<string | null>;
onSignIn: (cb: () => void) => () => Electron.IpcRenderer;
onAccountUpdated: (cb: () => void) => () => Electron.IpcRenderer;
onSignOut: (cb: () => void) => () => Electron.IpcRenderer;

/* User */
Expand Down
6 changes: 3 additions & 3 deletions src/renderer/src/pages/game-details/game-details-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -101,7 +101,7 @@ export function GameDetailsContent() {

const handleCloudSaveButtonClick = () => {
if (!userDetails) {
window.electron.openAuthWindow();
window.electron.openAuthWindow(AuthPage.SignIn);
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,7 @@ import { SPACING_UNIT, vars } from "../../theme.css";
export const form = style({
display: "flex",
flexDirection: "column",
gap: `${SPACING_UNIT}px`,
});

export const blockedUserAvatar = style({
width: "32px",
height: "32px",
borderRadius: "4px",
filter: "grayscale(100%)",
gap: `${SPACING_UNIT * 3}px`,
});

export const blockedUser = style({
Expand Down Expand Up @@ -43,5 +36,4 @@ export const blockedUsersList = style({
flexDirection: "column",
alignItems: "flex-start",
gap: `${SPACING_UNIT}px`,
marginTop: `${SPACING_UNIT}px`,
});
Loading

0 comments on commit 69787ee

Please sign in to comment.