mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-22 18:33:56 +00:00
Compare commits
3 Commits
v3.7.6
...
release/v3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e578047929 | ||
|
|
e49d885b30 | ||
|
|
cb01301a0d |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hydralauncher",
|
"name": "hydralauncher",
|
||||||
"version": "3.7.6",
|
"version": "3.7.5",
|
||||||
"description": "Hydra",
|
"description": "Hydra",
|
||||||
"main": "./out/main/index.js",
|
"main": "./out/main/index.js",
|
||||||
"author": "Los Broxas",
|
"author": "Los Broxas",
|
||||||
@@ -19,12 +19,12 @@
|
|||||||
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
|
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
|
||||||
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false",
|
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false",
|
||||||
"typecheck:web": "tsc --noEmit -p tsconfig.web.json --composite false",
|
"typecheck:web": "tsc --noEmit -p tsconfig.web.json --composite false",
|
||||||
"typecheck": "npm run typecheck:node && npm run typecheck:web",
|
"typecheck": "yarn run typecheck:node && yarn run typecheck:web",
|
||||||
"start": "electron-vite preview",
|
"start": "electron-vite preview",
|
||||||
"dev": "electron-vite dev",
|
"dev": "electron-vite dev",
|
||||||
"build": "npm run typecheck && electron-vite build",
|
"build": "yarn run typecheck && electron-vite build",
|
||||||
"postinstall": "electron-builder install-app-deps && node ./scripts/postinstall.cjs",
|
"postinstall": "electron-builder install-app-deps && node ./scripts/postinstall.cjs",
|
||||||
"build:unpack": "npm run build && electron-builder --dir",
|
"build:unpack": "yarn run build && electron-builder --dir",
|
||||||
"build:win": "electron-vite build && electron-builder --win",
|
"build:win": "electron-vite build && electron-builder --win",
|
||||||
"build:mac": "electron-vite build && electron-builder --mac",
|
"build:mac": "electron-vite build && electron-builder --mac",
|
||||||
"build:linux": "electron-vite build && electron-builder --linux",
|
"build:linux": "electron-vite build && electron-builder --linux",
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"library": "Library",
|
"library": "Library",
|
||||||
"downloads": "Downloads",
|
"downloads": "Downloads",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
|
"hydra_2025_wrapped": "Hydra Wrapped 2025 Available",
|
||||||
"my_library": "My library",
|
"my_library": "My library",
|
||||||
"downloading_metadata": "{{title}} (Downloading metadata…)",
|
"downloading_metadata": "{{title}} (Downloading metadata…)",
|
||||||
"paused": "{{title}} (Paused)",
|
"paused": "{{title}} (Paused)",
|
||||||
@@ -414,7 +415,11 @@
|
|||||||
"resume_seeding": "Resume seeding",
|
"resume_seeding": "Resume seeding",
|
||||||
"options": "Manage",
|
"options": "Manage",
|
||||||
"extract": "Extract files",
|
"extract": "Extract files",
|
||||||
"extracting": "Extracting files…"
|
"extracting": "Extracting files…",
|
||||||
|
"network": "Network",
|
||||||
|
"peak": "Peak",
|
||||||
|
"seeds": "Seeds",
|
||||||
|
"peers": "Peers"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"downloads_path": "Downloads path",
|
"downloads_path": "Downloads path",
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"library": "Librería",
|
"library": "Librería",
|
||||||
"downloads": "Descargas",
|
"downloads": "Descargas",
|
||||||
"settings": "Ajustes",
|
"settings": "Ajustes",
|
||||||
|
"hydra_2025_wrapped": "Hydra Wrapped 2025 Disponible",
|
||||||
"my_library": "Mi Librería",
|
"my_library": "Mi Librería",
|
||||||
"downloading_metadata": "{{title}} (Descargando metadatos…)",
|
"downloading_metadata": "{{title}} (Descargando metadatos…)",
|
||||||
"paused": "{{title}} (Pausado)",
|
"paused": "{{title}} (Pausado)",
|
||||||
@@ -414,7 +415,11 @@
|
|||||||
"resume_seeding": "Continuar sembrando",
|
"resume_seeding": "Continuar sembrando",
|
||||||
"options": "Administrar",
|
"options": "Administrar",
|
||||||
"extract": "Extraer archivos",
|
"extract": "Extraer archivos",
|
||||||
"extracting": "Extrayendo archivos…"
|
"extracting": "Extrayendo archivos…",
|
||||||
|
"network": "Red",
|
||||||
|
"peak": "Pico",
|
||||||
|
"seeds": "Seeds",
|
||||||
|
"peers": "Peers"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"downloads_path": "Ruta de descarga",
|
"downloads_path": "Ruta de descarga",
|
||||||
@@ -458,7 +463,6 @@
|
|||||||
"description_confirmation_delete_all_sources": "Vas a eliminar todas las fuentes de descargas",
|
"description_confirmation_delete_all_sources": "Vas a eliminar todas las fuentes de descargas",
|
||||||
"button_delete_all_sources": "Eliminar todo",
|
"button_delete_all_sources": "Eliminar todo",
|
||||||
"added_download_source": "Añadir fuente de descarga",
|
"added_download_source": "Añadir fuente de descarga",
|
||||||
"adding": "Añadiendo…",
|
|
||||||
"download_sources_synced": "Todas las fuentes de descarga están sincronizadas",
|
"download_sources_synced": "Todas las fuentes de descarga están sincronizadas",
|
||||||
"insert_valid_json_url": "Introducí una URL de json válida",
|
"insert_valid_json_url": "Introducí una URL de json válida",
|
||||||
"found_download_option_zero": "Sin opciones de descargas encontrada",
|
"found_download_option_zero": "Sin opciones de descargas encontrada",
|
||||||
@@ -564,19 +568,6 @@
|
|||||||
"debrid_description": "Los servicios Debrid son descargadores premium sin restricciones que te dejan descargar más rápido archivos alojados en servicios de alojamiento siendo que la única limitación es tu velocidad de internet.",
|
"debrid_description": "Los servicios Debrid son descargadores premium sin restricciones que te dejan descargar más rápido archivos alojados en servicios de alojamiento siendo que la única limitación es tu velocidad de internet.",
|
||||||
"enable_friend_start_game_notifications": "Cuando un amigo está jugando un juego",
|
"enable_friend_start_game_notifications": "Cuando un amigo está jugando un juego",
|
||||||
"autoplay_trailers_on_game_page": "Reproducir trailers automáticamente en la página del juego",
|
"autoplay_trailers_on_game_page": "Reproducir trailers automáticamente en la página del juego",
|
||||||
"change_achievement_sound": "Cambiar sonido de logro",
|
|
||||||
"download_source_already_exists": "Esta fuente de descarga URL ya existe.",
|
|
||||||
"download_source_failed": "Error",
|
|
||||||
"download_source_matched": "Actualizado",
|
|
||||||
"download_source_matching": "Actualizando",
|
|
||||||
"download_source_no_information": "Sin información disponible",
|
|
||||||
"download_source_pending_matching": "Actualizando pronto",
|
|
||||||
"download_sources_synced_successfully": "Todas las fuentes de descarga están sincronizadas",
|
|
||||||
"failed_add_download_source": "Error al añadir la fuente de descarga. Por favor intentá de nuevo.",
|
|
||||||
"hydra_cloud": "Hydra Cloud",
|
|
||||||
"preview_sound": "Vista previa de sonido",
|
|
||||||
"remove_achievement_sound": "Eliminar sonido de logros",
|
|
||||||
"removed_all_download_sources": "Todas las fuentes de descarga eliminadas",
|
|
||||||
"hide_to_tray_on_game_start": "Ocultar Hydra en la bandeja al iniciar un juego"
|
"hide_to_tray_on_game_start": "Ocultar Hydra en la bandeja al iniciar un juego"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"library": "Biblioteca",
|
"library": "Biblioteca",
|
||||||
"downloads": "Downloads",
|
"downloads": "Downloads",
|
||||||
"settings": "Ajustes",
|
"settings": "Ajustes",
|
||||||
|
"hydra_2025_wrapped": "Hydra Wrapped 2025 Já disponível",
|
||||||
"my_library": "Biblioteca",
|
"my_library": "Biblioteca",
|
||||||
"downloading_metadata": "{{title}} (Baixando metadados…)",
|
"downloading_metadata": "{{title}} (Baixando metadados…)",
|
||||||
"paused": "{{title}} (Pausado)",
|
"paused": "{{title}} (Pausado)",
|
||||||
@@ -402,7 +403,11 @@
|
|||||||
"resume_seeding": "Semear",
|
"resume_seeding": "Semear",
|
||||||
"options": "Gerenciar",
|
"options": "Gerenciar",
|
||||||
"extract": "Extrair arquivos",
|
"extract": "Extrair arquivos",
|
||||||
"extracting": "Extraindo arquivos…"
|
"extracting": "Extraindo arquivos…",
|
||||||
|
"network": "Rede",
|
||||||
|
"peak": "Pico",
|
||||||
|
"seeds": "Seeds",
|
||||||
|
"peers": "Peers"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"downloads_path": "Diretório dos downloads",
|
"downloads_path": "Diretório dos downloads",
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"catalogue": "Catálogo",
|
"catalogue": "Catálogo",
|
||||||
"downloads": "Transferências",
|
"downloads": "Transferências",
|
||||||
"settings": "Definições",
|
"settings": "Definições",
|
||||||
|
"hydra_2025_wrapped": "Hydra Wrapped 2025 Já disponível",
|
||||||
"my_library": "Biblioteca",
|
"my_library": "Biblioteca",
|
||||||
"downloading_metadata": "{{title}} (A transferir metadados…)",
|
"downloading_metadata": "{{title}} (A transferir metadados…)",
|
||||||
"paused": "{{title}} (Em pausa)",
|
"paused": "{{title}} (Em pausa)",
|
||||||
@@ -229,7 +230,13 @@
|
|||||||
"seeding": "A semear",
|
"seeding": "A semear",
|
||||||
"stop_seeding": "Parar de semear",
|
"stop_seeding": "Parar de semear",
|
||||||
"resume_seeding": "Semear",
|
"resume_seeding": "Semear",
|
||||||
"options": "Opções"
|
"options": "Opções",
|
||||||
|
"extract": "Extrair ficheiros",
|
||||||
|
"extracting": "A extrair ficheiros…",
|
||||||
|
"network": "Rede",
|
||||||
|
"peak": "Pico",
|
||||||
|
"seeds": "Seeds",
|
||||||
|
"peers": "Peers"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"downloads_path": "Local das transferências",
|
"downloads_path": "Local das transferências",
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"library": "Библиотека",
|
"library": "Библиотека",
|
||||||
"downloads": "Загрузки",
|
"downloads": "Загрузки",
|
||||||
"settings": "Настройки",
|
"settings": "Настройки",
|
||||||
|
"hydra_2025_wrapped": "Hydra Wrapped 2025 Доступно",
|
||||||
"my_library": "Библиотека",
|
"my_library": "Библиотека",
|
||||||
"downloading_metadata": "{{title}} (Загрузка метаданных…)",
|
"downloading_metadata": "{{title}} (Загрузка метаданных…)",
|
||||||
"paused": "{{title}} (Приостановлено)",
|
"paused": "{{title}} (Приостановлено)",
|
||||||
@@ -414,7 +415,11 @@
|
|||||||
"resume_seeding": "Продолжить раздачу",
|
"resume_seeding": "Продолжить раздачу",
|
||||||
"options": "Управлять",
|
"options": "Управлять",
|
||||||
"extract": "Распаковать файлы",
|
"extract": "Распаковать файлы",
|
||||||
"extracting": "Распаковка файлов…"
|
"extracting": "Распаковка файлов…",
|
||||||
|
"network": "Сеть",
|
||||||
|
"peak": "Пик",
|
||||||
|
"seeds": "Seeds",
|
||||||
|
"peers": "Peers"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"downloads_path": "Путь загрузок",
|
"downloads_path": "Путь загрузок",
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ export const loadState = async () => {
|
|||||||
|
|
||||||
await import("./events");
|
await import("./events");
|
||||||
|
|
||||||
Aria2.spawn();
|
if (process.platform !== "darwin") {
|
||||||
|
Aria2.spawn();
|
||||||
|
}
|
||||||
|
|
||||||
if (userPreferences?.realDebridApiToken) {
|
if (userPreferences?.realDebridApiToken) {
|
||||||
RealDebridClient.authorize(userPreferences.realDebridApiToken);
|
RealDebridClient.authorize(userPreferences.realDebridApiToken);
|
||||||
|
|||||||
@@ -7,12 +7,9 @@ export class Aria2 {
|
|||||||
private static process: cp.ChildProcess | null = null;
|
private static process: cp.ChildProcess | null = null;
|
||||||
|
|
||||||
public static spawn() {
|
public static spawn() {
|
||||||
const binaryPath =
|
const binaryPath = app.isPackaged
|
||||||
process.platform === "darwin"
|
? path.join(process.resourcesPath, "aria2c")
|
||||||
? "aria2c"
|
: path.join(__dirname, "..", "..", "binaries", "aria2c");
|
||||||
: app.isPackaged
|
|
||||||
? path.join(process.resourcesPath, "aria2c")
|
|
||||||
: path.join(__dirname, "..", "..", "binaries", "aria2c");
|
|
||||||
|
|
||||||
this.process = cp.spawn(
|
this.process = cp.spawn(
|
||||||
binaryPath,
|
binaryPath,
|
||||||
|
|||||||
@@ -36,13 +36,16 @@ export class GofileApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static async getDownloadLink(id: string) {
|
public static async getDownloadLink(id: string) {
|
||||||
|
const searchParams = new URLSearchParams({
|
||||||
|
wt: WT,
|
||||||
|
});
|
||||||
|
|
||||||
const response = await axios.get<{
|
const response = await axios.get<{
|
||||||
status: string;
|
status: string;
|
||||||
data: GofileContentsResponse;
|
data: GofileContentsResponse;
|
||||||
}>(`https://api.gofile.io/contents/${id}`, {
|
}>(`https://api.gofile.io/contents/${id}?${searchParams.toString()}`, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${this.token}`,
|
Authorization: `Bearer ${this.token}`,
|
||||||
"X-Website-Token": WT,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -58,13 +58,7 @@ export class HydraApi {
|
|||||||
const decodedBase64 = atob(payload as string);
|
const decodedBase64 = atob(payload as string);
|
||||||
const jsonData = JSON.parse(decodedBase64);
|
const jsonData = JSON.parse(decodedBase64);
|
||||||
|
|
||||||
const {
|
const { accessToken, expiresIn, refreshToken } = jsonData;
|
||||||
accessToken,
|
|
||||||
expiresIn,
|
|
||||||
refreshToken,
|
|
||||||
featurebaseJwt,
|
|
||||||
workwondersJwt,
|
|
||||||
} = jsonData;
|
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
||||||
@@ -91,8 +85,6 @@ export class HydraApi {
|
|||||||
accessToken,
|
accessToken,
|
||||||
refreshToken,
|
refreshToken,
|
||||||
tokenExpirationTimestamp,
|
tokenExpirationTimestamp,
|
||||||
featurebaseJwt,
|
|
||||||
workwondersJwt,
|
|
||||||
},
|
},
|
||||||
{ valueEncoding: "json" }
|
{ valueEncoding: "json" }
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -138,8 +138,7 @@ export class WindowManager {
|
|||||||
(details, callback) => {
|
(details, callback) => {
|
||||||
if (
|
if (
|
||||||
details.webContentsId !== this.mainWindow?.webContents.id ||
|
details.webContentsId !== this.mainWindow?.webContents.id ||
|
||||||
details.url.includes("chatwoot") ||
|
details.url.includes("chatwoot")
|
||||||
details.url.includes("workwonders")
|
|
||||||
) {
|
) {
|
||||||
return callback(details);
|
return callback(details);
|
||||||
}
|
}
|
||||||
@@ -160,8 +159,7 @@ export class WindowManager {
|
|||||||
if (
|
if (
|
||||||
details.webContentsId !== this.mainWindow?.webContents.id ||
|
details.webContentsId !== this.mainWindow?.webContents.id ||
|
||||||
details.url.includes("featurebase") ||
|
details.url.includes("featurebase") ||
|
||||||
details.url.includes("chatwoot") ||
|
details.url.includes("chatwoot")
|
||||||
details.url.includes("workwonders")
|
|
||||||
) {
|
) {
|
||||||
return callback(details);
|
return callback(details);
|
||||||
}
|
}
|
||||||
@@ -356,6 +354,8 @@ export class WindowManager {
|
|||||||
public static async createNotificationWindow() {
|
public static async createNotificationWindow() {
|
||||||
if (this.notificationWindow) return;
|
if (this.notificationWindow) return;
|
||||||
|
|
||||||
|
if (process.platform === "darwin") return;
|
||||||
|
|
||||||
const userPreferences = await db.get<string, UserPreferences | undefined>(
|
const userPreferences = await db.get<string, UserPreferences | undefined>(
|
||||||
levelKeys.userPreferences,
|
levelKeys.userPreferences,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -122,10 +122,10 @@ export function BottomPanel() {
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
data-open-workwonders-changelog-mini
|
data-featurebase-changelog
|
||||||
className="bottom-panel__version-button"
|
className="bottom-panel__version-button"
|
||||||
>
|
>
|
||||||
<small>
|
<small data-featurebase-changelog>
|
||||||
{sessionHash ? `${sessionHash} -` : ""} v{version} "
|
{sessionHash ? `${sessionHash} -` : ""} v{version} "
|
||||||
{VERSION_CODENAME}"
|
{VERSION_CODENAME}"
|
||||||
</small>
|
</small>
|
||||||
|
|||||||
@@ -32,4 +32,15 @@ export const routes = [
|
|||||||
nameKey: "settings",
|
nameKey: "settings",
|
||||||
render: () => <GearIcon />,
|
render: () => <GearIcon />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "https://hydrawrapped.com",
|
||||||
|
nameKey: "hydra_2025_wrapped",
|
||||||
|
render: () => (
|
||||||
|
<img
|
||||||
|
src="https://cdn.losbroxas.org/thumbnail_hydra_badge2_fb01af31e3.png"
|
||||||
|
alt="Hydra 2025 Wrapped"
|
||||||
|
style={{ width: 16, height: 16 }}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -88,6 +88,34 @@
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--wrapped {
|
||||||
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
rgba(74, 144, 226, 0.25) 0%,
|
||||||
|
rgba(123, 104, 238, 0.2) 25%,
|
||||||
|
rgba(59, 130, 246, 0.25) 50%,
|
||||||
|
rgba(96, 165, 250, 0.2) 75%,
|
||||||
|
rgba(74, 144, 226, 0.25) 100%
|
||||||
|
);
|
||||||
|
background-size: 200% 200%;
|
||||||
|
animation: wrapped-gradient-flow 8s ease infinite;
|
||||||
|
color: globals.$muted-color;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
rgba(74, 144, 226, 0.35) 0%,
|
||||||
|
rgba(123, 104, 238, 0.3) 25%,
|
||||||
|
rgba(59, 130, 246, 0.35) 50%,
|
||||||
|
rgba(96, 165, 250, 0.3) 75%,
|
||||||
|
rgba(74, 144, 226, 0.35) 100%
|
||||||
|
);
|
||||||
|
background-size: 200% 200%;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__menu-item-button {
|
&__menu-item-button {
|
||||||
@@ -106,6 +134,21 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__menu-item-marquee {
|
||||||
|
overflow: hidden;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__menu-item-marquee-content {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__menu-item-marquee-content span {
|
||||||
|
display: inline-block;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
&__game-icon {
|
&__game-icon {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
@@ -228,3 +271,24 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes wrapped-gradient-flow {
|
||||||
|
0% {
|
||||||
|
background-position: 0% 50%;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
background-position: 100% 50%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 0% 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes marquee-scroll {
|
||||||
|
0% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(calc(-50% - 1em));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import { buildGameDetailsPath } from "@renderer/helpers";
|
|||||||
import { SidebarProfile } from "./sidebar-profile";
|
import { SidebarProfile } from "./sidebar-profile";
|
||||||
import { sortBy } from "lodash-es";
|
import { sortBy } from "lodash-es";
|
||||||
import cn from "classnames";
|
import cn from "classnames";
|
||||||
|
import { logger } from "@renderer/logger";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
import {
|
import {
|
||||||
CommentDiscussionIcon,
|
CommentDiscussionIcon,
|
||||||
PlayIcon,
|
PlayIcon,
|
||||||
@@ -238,8 +240,32 @@ export function Sidebar() {
|
|||||||
return game.title;
|
return game.title;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSidebarItemClick = (path: string) => {
|
const handleSidebarItemClick = async (path: string) => {
|
||||||
if (path !== location.pathname) {
|
if (path.startsWith("http")) {
|
||||||
|
if (path === "https://hydrawrapped.com") {
|
||||||
|
try {
|
||||||
|
const auth = await window.electron.getAuth();
|
||||||
|
if (auth) {
|
||||||
|
const payload = {
|
||||||
|
accessToken: auth.accessToken,
|
||||||
|
refreshToken: auth.refreshToken,
|
||||||
|
expiresIn: 3600,
|
||||||
|
};
|
||||||
|
const base64Payload = btoa(JSON.stringify(payload));
|
||||||
|
window.electron.openExternal(
|
||||||
|
`${path}?payload=${encodeURIComponent(base64Payload)}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
window.electron.openExternal(path);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Failed to get auth for wrapped:", error);
|
||||||
|
window.electron.openExternal(path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
window.electron.openExternal(path);
|
||||||
|
}
|
||||||
|
} else if (path !== location.pathname) {
|
||||||
navigate(path);
|
navigate(path);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -297,7 +323,10 @@ export function Sidebar() {
|
|||||||
<li
|
<li
|
||||||
key={nameKey}
|
key={nameKey}
|
||||||
className={cn("sidebar__menu-item", {
|
className={cn("sidebar__menu-item", {
|
||||||
"sidebar__menu-item--active": location.pathname === path,
|
"sidebar__menu-item--active":
|
||||||
|
!path.startsWith("http") && location.pathname === path,
|
||||||
|
"sidebar__menu-item--wrapped":
|
||||||
|
nameKey === "hydra_2025_wrapped",
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
@@ -306,7 +335,33 @@ export function Sidebar() {
|
|||||||
onClick={() => handleSidebarItemClick(path)}
|
onClick={() => handleSidebarItemClick(path)}
|
||||||
>
|
>
|
||||||
{render()}
|
{render()}
|
||||||
<span>{t(nameKey)}</span>
|
{nameKey === "hydra_2025_wrapped" ? (
|
||||||
|
<div className="sidebar__menu-item-marquee">
|
||||||
|
<motion.div
|
||||||
|
className="sidebar__menu-item-marquee-content"
|
||||||
|
animate={{
|
||||||
|
x: ["0%", "-50%"],
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
x: {
|
||||||
|
repeat: Infinity,
|
||||||
|
repeatType: "loop",
|
||||||
|
duration: 8,
|
||||||
|
ease: "linear",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{t(nameKey)}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
{t(nameKey)}
|
||||||
|
</span>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<span>{t(nameKey)}</span>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -108,11 +108,16 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
transition: scale 0.2s ease;
|
transition: opacity 0.2s ease;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
scale: 1.05;
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&:focus-visible {
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,21 +395,6 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background-color: rgba(0, 0, 0, 0.3);
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
border: 1px solid globals.$border-color;
|
border: 1px solid globals.$border-color;
|
||||||
padding: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
transition:
|
|
||||||
opacity 0.2s ease,
|
|
||||||
transform 0.2s ease;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus,
|
|
||||||
&:focus-visible {
|
|
||||||
outline: 2px solid rgba(255, 255, 255, 0.5);
|
|
||||||
outline-offset: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -421,21 +411,6 @@
|
|||||||
gap: calc(globals.$spacing-unit / 1);
|
gap: calc(globals.$spacing-unit / 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__simple-title-button {
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
padding: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
text-align: left;
|
|
||||||
width: 100%;
|
|
||||||
transition: opacity 0.2s ease;
|
|
||||||
|
|
||||||
&:focus,
|
|
||||||
&:focus-visible {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__simple-title {
|
&__simple-title {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|||||||
@@ -305,9 +305,11 @@ function HeroDownloadView({
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<span className="download-group__progress-percentage">
|
{(!lastPacket?.isCheckingFiles || currentProgress > 0) && (
|
||||||
<AnimatedPercentage value={currentProgress} />
|
<span className="download-group__progress-percentage">
|
||||||
</span>
|
<AnimatedPercentage value={currentProgress} />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="download-group__progress-bar">
|
<div className="download-group__progress-bar">
|
||||||
<div
|
<div
|
||||||
@@ -358,7 +360,7 @@ function HeroDownloadView({
|
|||||||
</span>
|
</span>
|
||||||
<div className="download-group__stat-content">
|
<div className="download-group__stat-content">
|
||||||
<span className="download-group__stat-label">
|
<span className="download-group__stat-label">
|
||||||
{t("network")}:
|
{t("network")}
|
||||||
</span>
|
</span>
|
||||||
<span className="download-group__stat-value">
|
<span className="download-group__stat-value">
|
||||||
{isGameDownloading ? formatSpeed(downloadSpeed) : "0 B/s"}
|
{isGameDownloading ? formatSpeed(downloadSpeed) : "0 B/s"}
|
||||||
@@ -371,37 +373,38 @@ function HeroDownloadView({
|
|||||||
<GraphIcon size={16} />
|
<GraphIcon size={16} />
|
||||||
</span>
|
</span>
|
||||||
<div className="download-group__stat-content">
|
<div className="download-group__stat-content">
|
||||||
<span className="download-group__stat-label">{t("peak")}:</span>
|
<span className="download-group__stat-label">{t("peak")}</span>
|
||||||
<span className="download-group__stat-value">
|
<span className="download-group__stat-value">
|
||||||
{peakSpeed > 0 ? formatSpeed(peakSpeed) : "0 B/s"}
|
{peakSpeed > 0 ? formatSpeed(peakSpeed) : "0 B/s"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{game.download?.downloader === Downloader.Torrent &&
|
|
||||||
isGameDownloading &&
|
|
||||||
lastPacket &&
|
|
||||||
(lastPacket.numSeeds > 0 || lastPacket.numPeers > 0) && (
|
|
||||||
<div className="download-group__stat-item">
|
|
||||||
<div className="download-group__stat-content">
|
|
||||||
<span className="download-group__stat-label">
|
|
||||||
Seeds:{" "}
|
|
||||||
<span className="download-group__stat-value">
|
|
||||||
{lastPacket.numSeeds}
|
|
||||||
</span>
|
|
||||||
, Peers:{" "}
|
|
||||||
<span className="download-group__stat-value">
|
|
||||||
{lastPacket.numPeers}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{game.download?.downloader && (
|
{game.download?.downloader && (
|
||||||
<div className="download-group__stat-item">
|
<div className="download-group__stat-item">
|
||||||
<div className="download-group__stat-content">
|
<div
|
||||||
|
className="download-group__stat-content"
|
||||||
|
style={{
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Badge>{DOWNLOADER_NAME[game.download.downloader]}</Badge>
|
<Badge>{DOWNLOADER_NAME[game.download.downloader]}</Badge>
|
||||||
|
{game.download?.downloader === Downloader.Torrent &&
|
||||||
|
isGameDownloading &&
|
||||||
|
lastPacket &&
|
||||||
|
(lastPacket.numSeeds > 0 || lastPacket.numPeers > 0) && (
|
||||||
|
<span className="download-group__stat-label">
|
||||||
|
{t("seeds")}{" "}
|
||||||
|
<span className="download-group__stat-value">
|
||||||
|
{lastPacket.numSeeds}
|
||||||
|
</span>
|
||||||
|
, {t("peers")}{" "}
|
||||||
|
<span className="download-group__stat-value">
|
||||||
|
{lastPacket.numPeers}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -870,8 +873,14 @@ export function DownloadGroup({
|
|||||||
<li key={game.id} className="download-group__simple-card">
|
<li key={game.id} className="download-group__simple-card">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => navigate(buildGameDetailsPath(game))}
|
|
||||||
className="download-group__simple-thumbnail"
|
className="download-group__simple-thumbnail"
|
||||||
|
onClick={() => navigate(buildGameDetailsPath(game))}
|
||||||
|
style={{
|
||||||
|
background: "none",
|
||||||
|
border: "none",
|
||||||
|
padding: 0,
|
||||||
|
cursor: "pointer",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<img src={game.libraryImageUrl || ""} alt={game.title} />
|
<img src={game.libraryImageUrl || ""} alt={game.title} />
|
||||||
</button>
|
</button>
|
||||||
@@ -879,10 +888,18 @@ export function DownloadGroup({
|
|||||||
<div className="download-group__simple-info">
|
<div className="download-group__simple-info">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
className="download-group__simple-title"
|
||||||
onClick={() => navigate(buildGameDetailsPath(game))}
|
onClick={() => navigate(buildGameDetailsPath(game))}
|
||||||
className="download-group__simple-title-button"
|
style={{
|
||||||
|
background: "none",
|
||||||
|
border: "none",
|
||||||
|
padding: 0,
|
||||||
|
cursor: "pointer",
|
||||||
|
textAlign: "left",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<h3 className="download-group__simple-title">{game.title}</h3>
|
{game.title}
|
||||||
</button>
|
</button>
|
||||||
<div className="download-group__simple-meta">
|
<div className="download-group__simple-meta">
|
||||||
<div className="download-group__simple-meta-row">
|
<div className="download-group__simple-meta-row">
|
||||||
|
|||||||
@@ -87,12 +87,16 @@ export function LibraryTab({
|
|||||||
|
|
||||||
<ul className="profile-content__games-grid">
|
<ul className="profile-content__games-grid">
|
||||||
{pinnedGames?.map((game) => (
|
{pinnedGames?.map((game) => (
|
||||||
<li key={game.objectId} style={{ listStyle: "none" }}>
|
<li
|
||||||
|
key={game.objectId}
|
||||||
|
style={{ listStyle: "none" }}
|
||||||
|
className="user-library-game__wrapper"
|
||||||
|
onMouseEnter={onMouseEnter}
|
||||||
|
onMouseLeave={onMouseLeave}
|
||||||
|
>
|
||||||
<UserLibraryGameCard
|
<UserLibraryGameCard
|
||||||
game={game}
|
game={game}
|
||||||
statIndex={statsIndex}
|
statIndex={statsIndex}
|
||||||
onMouseEnter={onMouseEnter}
|
|
||||||
onMouseLeave={onMouseLeave}
|
|
||||||
sortBy={sortBy}
|
sortBy={sortBy}
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
@@ -134,6 +138,9 @@ export function LibraryTab({
|
|||||||
<motion.li
|
<motion.li
|
||||||
key={`${sortBy}-${game.objectId}`}
|
key={`${sortBy}-${game.objectId}`}
|
||||||
style={{ listStyle: "none" }}
|
style={{ listStyle: "none" }}
|
||||||
|
className="user-library-game__wrapper"
|
||||||
|
onMouseEnter={onMouseEnter}
|
||||||
|
onMouseLeave={onMouseLeave}
|
||||||
initial={
|
initial={
|
||||||
isNewGame
|
isNewGame
|
||||||
? { opacity: 0.5, y: 15, scale: 0.96 }
|
? { opacity: 0.5, y: 15, scale: 0.96 }
|
||||||
@@ -160,8 +167,6 @@ export function LibraryTab({
|
|||||||
<UserLibraryGameCard
|
<UserLibraryGameCard
|
||||||
game={game}
|
game={game}
|
||||||
statIndex={statsIndex}
|
statIndex={statsIndex}
|
||||||
onMouseEnter={onMouseEnter}
|
|
||||||
onMouseLeave={onMouseLeave}
|
|
||||||
sortBy={sortBy}
|
sortBy={sortBy}
|
||||||
/>
|
/>
|
||||||
</motion.li>
|
</motion.li>
|
||||||
|
|||||||
@@ -25,16 +25,12 @@ import "./user-library-game-card.scss";
|
|||||||
interface UserLibraryGameCardProps {
|
interface UserLibraryGameCardProps {
|
||||||
game: UserGame;
|
game: UserGame;
|
||||||
statIndex: number;
|
statIndex: number;
|
||||||
onMouseEnter: () => void;
|
|
||||||
onMouseLeave: () => void;
|
|
||||||
sortBy?: string;
|
sortBy?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UserLibraryGameCard({
|
export function UserLibraryGameCard({
|
||||||
game,
|
game,
|
||||||
statIndex,
|
statIndex,
|
||||||
onMouseEnter,
|
|
||||||
onMouseLeave,
|
|
||||||
sortBy,
|
sortBy,
|
||||||
}: UserLibraryGameCardProps) {
|
}: UserLibraryGameCardProps) {
|
||||||
const { userProfile, isMe, getUserLibraryGames } =
|
const { userProfile, isMe, getUserLibraryGames } =
|
||||||
@@ -130,129 +126,119 @@ export function UserLibraryGameCard({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<li
|
<button
|
||||||
onMouseEnter={onMouseEnter}
|
type="button"
|
||||||
onMouseLeave={onMouseLeave}
|
className="user-library-game__cover"
|
||||||
className="user-library-game__wrapper"
|
onClick={() => navigate(buildUserGameDetailsPath(game))}
|
||||||
title={isTooltipHovered ? undefined : game.title}
|
title={isTooltipHovered ? undefined : game.title}
|
||||||
>
|
>
|
||||||
<button
|
<div className="user-library-game__overlay">
|
||||||
type="button"
|
{isMe && (
|
||||||
className="user-library-game__cover"
|
<div className="user-library-game__actions-container">
|
||||||
onClick={() => navigate(buildUserGameDetailsPath(game))}
|
<button
|
||||||
>
|
type="button"
|
||||||
<div className="user-library-game__overlay">
|
className="user-library-game__pin-button"
|
||||||
{isMe && (
|
onClick={(e) => {
|
||||||
<div className="user-library-game__actions-container">
|
e.stopPropagation();
|
||||||
<button
|
toggleGamePinned();
|
||||||
type="button"
|
}}
|
||||||
className="user-library-game__pin-button"
|
disabled={isPinning}
|
||||||
onClick={(e) => {
|
>
|
||||||
e.stopPropagation();
|
{game.isPinned ? (
|
||||||
toggleGamePinned();
|
<PinSlashIcon size={12} />
|
||||||
}}
|
) : (
|
||||||
disabled={isPinning}
|
<PinIcon size={12} />
|
||||||
>
|
)}
|
||||||
{game.isPinned ? (
|
</button>
|
||||||
<PinSlashIcon size={12} />
|
|
||||||
) : (
|
|
||||||
<PinIcon size={12} />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div
|
|
||||||
className="user-library-game__playtime"
|
|
||||||
data-tooltip-place="top"
|
|
||||||
data-tooltip-content={
|
|
||||||
game.hasManuallyUpdatedPlaytime
|
|
||||||
? t("manual_playtime_tooltip")
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
data-tooltip-id={game.objectId}
|
|
||||||
>
|
|
||||||
{game.hasManuallyUpdatedPlaytime ? (
|
|
||||||
<AlertFillIcon
|
|
||||||
size={11}
|
|
||||||
className="user-library-game__manual-playtime"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<ClockIcon size={11} />
|
|
||||||
)}
|
|
||||||
<span className="user-library-game__playtime-long">
|
|
||||||
{formatPlayTime(game.playTimeInSeconds)}
|
|
||||||
</span>
|
|
||||||
<span className="user-library-game__playtime-short">
|
|
||||||
{formatPlayTime(game.playTimeInSeconds, true)}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className="user-library-game__playtime"
|
||||||
|
data-tooltip-place="top"
|
||||||
|
data-tooltip-content={
|
||||||
|
game.hasManuallyUpdatedPlaytime
|
||||||
|
? t("manual_playtime_tooltip")
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
data-tooltip-id={game.objectId}
|
||||||
|
>
|
||||||
|
{game.hasManuallyUpdatedPlaytime ? (
|
||||||
|
<AlertFillIcon
|
||||||
|
size={11}
|
||||||
|
className="user-library-game__manual-playtime"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ClockIcon size={11} />
|
||||||
|
)}
|
||||||
|
<span className="user-library-game__playtime-long">
|
||||||
|
{formatPlayTime(game.playTimeInSeconds)}
|
||||||
|
</span>
|
||||||
|
<span className="user-library-game__playtime-short">
|
||||||
|
{formatPlayTime(game.playTimeInSeconds, true)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
{userProfile?.hasActiveSubscription &&
|
{userProfile?.hasActiveSubscription && game.achievementCount > 0 && (
|
||||||
game.achievementCount > 0 && (
|
<div className="user-library-game__stats">
|
||||||
<div className="user-library-game__stats">
|
<div className="user-library-game__stats-header">
|
||||||
<div className="user-library-game__stats-header">
|
<div className="user-library-game__stats-content">
|
||||||
<div className="user-library-game__stats-content">
|
<div
|
||||||
<div
|
className="user-library-game__stats-item"
|
||||||
className="user-library-game__stats-item"
|
style={{
|
||||||
style={{
|
transform: `translateY(${-100 * (statIndex % getStatsItemCount())}%)`,
|
||||||
transform: `translateY(${-100 * (statIndex % getStatsItemCount())}%)`,
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<TrophyIcon size={13} />
|
||||||
<TrophyIcon size={13} />
|
|
||||||
<span>
|
|
||||||
{game.unlockedAchievementCount} /{" "}
|
|
||||||
{game.achievementCount}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{game.achievementsPointsEarnedSum > 0 && (
|
|
||||||
<div
|
|
||||||
className="user-library-game__stats-item"
|
|
||||||
style={{
|
|
||||||
transform: `translateY(${-100 * (statIndex % getStatsItemCount())}%)`,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<HydraIcon width={16} height={16} />
|
|
||||||
{formatAchievementPoints(
|
|
||||||
game.achievementsPointsEarnedSum
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
{formatDownloadProgress(
|
{game.unlockedAchievementCount} / {game.achievementCount}
|
||||||
game.unlockedAchievementCount / game.achievementCount,
|
|
||||||
1
|
|
||||||
)}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<progress
|
{game.achievementsPointsEarnedSum > 0 && (
|
||||||
max={1}
|
<div
|
||||||
value={
|
className="user-library-game__stats-item"
|
||||||
game.unlockedAchievementCount / game.achievementCount
|
style={{
|
||||||
}
|
transform: `translateY(${-100 * (statIndex % getStatsItemCount())}%)`,
|
||||||
className="user-library-game__achievements-progress"
|
}}
|
||||||
/>
|
>
|
||||||
|
<HydraIcon width={16} height={16} />
|
||||||
|
{formatAchievementPoints(
|
||||||
|
game.achievementsPointsEarnedSum
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{imageError || !game.coverImageUrl ? (
|
<span>
|
||||||
<div className="user-library-game__cover-placeholder">
|
{formatDownloadProgress(
|
||||||
<ImageIcon size={48} />
|
game.unlockedAchievementCount / game.achievementCount,
|
||||||
|
1
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<progress
|
||||||
|
max={1}
|
||||||
|
value={game.unlockedAchievementCount / game.achievementCount}
|
||||||
|
className="user-library-game__achievements-progress"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<img
|
|
||||||
src={game.coverImageUrl}
|
|
||||||
alt={game.title}
|
|
||||||
className="user-library-game__game-image"
|
|
||||||
onError={() => setImageError(true)}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</button>
|
</div>
|
||||||
</li>
|
|
||||||
|
{imageError || !game.coverImageUrl ? (
|
||||||
|
<div className="user-library-game__cover-placeholder">
|
||||||
|
<ImageIcon size={48} />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<img
|
||||||
|
src={game.coverImageUrl}
|
||||||
|
alt={game.title}
|
||||||
|
className="user-library-game__game-image"
|
||||||
|
onError={() => setImageError(true)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
id={game.objectId}
|
id={game.objectId}
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ export interface Auth {
|
|||||||
accessToken: string;
|
accessToken: string;
|
||||||
refreshToken: string;
|
refreshToken: string;
|
||||||
tokenExpirationTimestamp: number;
|
tokenExpirationTimestamp: number;
|
||||||
featurebaseJwt: string;
|
|
||||||
workwondersJwt: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
|
|||||||
10
yarn.lock
10
yarn.lock
@@ -6330,7 +6330,7 @@ jsonwebtoken@^9.0.2:
|
|||||||
object.assign "^4.1.4"
|
object.assign "^4.1.4"
|
||||||
object.values "^1.1.6"
|
object.values "^1.1.6"
|
||||||
|
|
||||||
jwa@^1.4.2:
|
jwa@^1.4.1:
|
||||||
version "1.4.2"
|
version "1.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.2.tgz#16011ac6db48de7b102777e57897901520eec7b9"
|
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.2.tgz#16011ac6db48de7b102777e57897901520eec7b9"
|
||||||
integrity sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==
|
integrity sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==
|
||||||
@@ -6340,11 +6340,11 @@ jwa@^1.4.2:
|
|||||||
safe-buffer "^5.0.1"
|
safe-buffer "^5.0.1"
|
||||||
|
|
||||||
jws@^3.2.2:
|
jws@^3.2.2:
|
||||||
version "3.2.3"
|
version "3.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.3.tgz#5ac0690b460900a27265de24520526853c0b8ca1"
|
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
|
||||||
integrity sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==
|
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
|
||||||
dependencies:
|
dependencies:
|
||||||
jwa "^1.4.2"
|
jwa "^1.4.1"
|
||||||
safe-buffer "^5.0.1"
|
safe-buffer "^5.0.1"
|
||||||
|
|
||||||
keyv@^4.0.0, keyv@^4.5.3:
|
keyv@^4.0.0, keyv@^4.5.3:
|
||||||
|
|||||||
Reference in New Issue
Block a user