mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-21 18:13:55 +00:00
Compare commits
21 Commits
chore/bump
...
fix/custom
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad588b5600 | ||
|
|
c24ad34bc7 | ||
|
|
4f2c3105ce | ||
|
|
feedcb1dc7 | ||
|
|
4b8d64c72b | ||
|
|
dff68a3e26 | ||
|
|
58bdbdab71 | ||
|
|
574a012d8c | ||
|
|
8e3bf29a21 | ||
|
|
f6d21baff5 | ||
|
|
b2b7b36f70 | ||
|
|
dbf5d7afc7 | ||
|
|
bfc4bb1a83 | ||
|
|
120aad6c1c | ||
|
|
61072aa02a | ||
|
|
ddd6af0d4c | ||
|
|
6565ce5316 | ||
|
|
fee3a4522a | ||
|
|
cb3e52de34 | ||
|
|
7f2343413e | ||
|
|
ee35bc24b2 |
@@ -541,7 +541,9 @@
|
|||||||
"hidden": "Hidden",
|
"hidden": "Hidden",
|
||||||
"test_notification": "Test notification",
|
"test_notification": "Test notification",
|
||||||
"notification_preview": "Achievement Notification Preview",
|
"notification_preview": "Achievement Notification Preview",
|
||||||
"enable_friend_start_game_notifications": "When a friend starts playing a game"
|
"enable_friend_start_game_notifications": "When a friend starts playing a game",
|
||||||
|
"autoplay_trailers_on_game_page": "Automatically start playing trailers on game page",
|
||||||
|
"hide_to_tray_on_game_start": "Hide Hydra to tray on game startup"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "Download complete",
|
"download_complete": "Download complete",
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ import { gamesShopAssetsSublevel, levelKeys } from "@main/level";
|
|||||||
const LOCAL_CACHE_EXPIRATION = 1000 * 60 * 60 * 8; // 8 hours
|
const LOCAL_CACHE_EXPIRATION = 1000 * 60 * 60 * 8; // 8 hours
|
||||||
|
|
||||||
export const getGameAssets = async (objectId: string, shop: GameShop) => {
|
export const getGameAssets = async (objectId: string, shop: GameShop) => {
|
||||||
|
if (shop === "custom") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const cachedAssets = await gamesShopAssetsSublevel.get(
|
const cachedAssets = await gamesShopAssetsSublevel.get(
|
||||||
levelKeys.game(shop, objectId)
|
levelKeys.game(shop, objectId)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ const getGameShopDetails = async (
|
|||||||
shop: GameShop,
|
shop: GameShop,
|
||||||
language: string
|
language: string
|
||||||
): Promise<ShopDetailsWithAssets | null> => {
|
): Promise<ShopDetailsWithAssets | null> => {
|
||||||
|
if (shop === "custom") return null;
|
||||||
|
|
||||||
if (shop === "steam") {
|
if (shop === "steam") {
|
||||||
const [cachedData, cachedAssets] = await Promise.all([
|
const [cachedData, cachedAssets] = await Promise.all([
|
||||||
gamesShopCacheSublevel.get(
|
gamesShopCacheSublevel.get(
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ const getGameStats = async (
|
|||||||
objectId: string,
|
objectId: string,
|
||||||
shop: GameShop
|
shop: GameShop
|
||||||
) => {
|
) => {
|
||||||
|
if (shop === "custom") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const cachedStats = await gamesStatsCacheSublevel.get(
|
const cachedStats = await gamesStatsCacheSublevel.get(
|
||||||
levelKeys.game(shop, objectId)
|
levelKeys.game(shop, objectId)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ const addGameToFavorites = async (
|
|||||||
const game = await gamesSublevel.get(gameKey);
|
const game = await gamesSublevel.get(gameKey);
|
||||||
if (!game) return;
|
if (!game) return;
|
||||||
|
|
||||||
HydraApi.put(`/profile/games/${shop}/${objectId}/favorite`).catch(() => {});
|
if (shop !== "custom") {
|
||||||
|
HydraApi.put(`/profile/games/${shop}/${objectId}/favorite`).catch(() => {});
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await gamesSublevel.put(gameKey, {
|
await gamesSublevel.put(gameKey, {
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ const removeGameFromFavorites = async (
|
|||||||
const game = await gamesSublevel.get(gameKey);
|
const game = await gamesSublevel.get(gameKey);
|
||||||
if (!game) return;
|
if (!game) return;
|
||||||
|
|
||||||
HydraApi.put(`/profile/games/${shop}/${objectId}/unfavorite`).catch(() => {});
|
if (shop !== "custom") {
|
||||||
|
HydraApi.put(`/profile/games/${shop}/${objectId}/unfavorite`).catch(
|
||||||
|
() => {}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await gamesSublevel.put(gameKey, {
|
await gamesSublevel.put(gameKey, {
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ const removeGameFromLibrary = async (
|
|||||||
await resetShopAssets(gameKey);
|
await resetShopAssets(gameKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (game?.remoteId) {
|
if (game.remoteId) {
|
||||||
HydraApi.delete(`/profile/games/${game.remoteId}`).catch(() => {});
|
HydraApi.delete(`/profile/games/${game.remoteId}`).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ export const getGameAchievementData = async (
|
|||||||
shop: GameShop,
|
shop: GameShop,
|
||||||
useCachedData: boolean
|
useCachedData: boolean
|
||||||
) => {
|
) => {
|
||||||
|
if (shop === "custom") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const gameKey = levelKeys.game(shop, objectId);
|
const gameKey = levelKeys.game(shop, objectId);
|
||||||
|
|
||||||
const cachedAchievements = await gameAchievementsSublevel.get(gameKey);
|
const cachedAchievements = await gameAchievementsSublevel.get(gameKey);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export class HydraApi {
|
|||||||
private static instance: AxiosInstance;
|
private static instance: AxiosInstance;
|
||||||
|
|
||||||
private static readonly EXPIRATION_OFFSET_IN_MS = 1000 * 60 * 5; // 5 minutes
|
private static readonly EXPIRATION_OFFSET_IN_MS = 1000 * 60 * 5; // 5 minutes
|
||||||
private static readonly ADD_LOG_INTERCEPTOR = false;
|
private static readonly ADD_LOG_INTERCEPTOR = true;
|
||||||
|
|
||||||
private static secondsToMilliseconds(seconds: number) {
|
private static secondsToMilliseconds(seconds: number) {
|
||||||
return seconds * 1000;
|
return seconds * 1000;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { WindowManager } from "./window-manager";
|
import { WindowManager } from "./window-manager";
|
||||||
import { createGame, updateGamePlaytime } from "./library-sync";
|
import { createGame, updateGamePlaytime } from "./library-sync";
|
||||||
import type { Game, GameRunning } from "@types";
|
import type { Game, GameRunning, UserPreferences } from "@types";
|
||||||
import { PythonRPC } from "./python-rpc";
|
import { PythonRPC } from "./python-rpc";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { ProcessPayload } from "./download/types";
|
import { ProcessPayload } from "./download/types";
|
||||||
import { gamesSublevel, levelKeys } from "@main/level";
|
import { db, gamesSublevel, levelKeys } from "@main/level";
|
||||||
import { CloudSync } from "./cloud-sync";
|
import { CloudSync } from "./cloud-sync";
|
||||||
import { logger } from "./logger";
|
import { logger } from "./logger";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
@@ -209,6 +209,17 @@ function onOpenGame(game: Game) {
|
|||||||
lastSyncTick: now,
|
lastSyncTick: now,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Hide Hydra to tray on game startup if enabled
|
||||||
|
db.get<string, UserPreferences | null>(levelKeys.userPreferences, {
|
||||||
|
valueEncoding: "json",
|
||||||
|
})
|
||||||
|
.then((userPreferences) => {
|
||||||
|
if (userPreferences?.hideToTrayOnGameStart) {
|
||||||
|
WindowManager.mainWindow?.hide();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
|
||||||
if (game.remoteId) {
|
if (game.remoteId) {
|
||||||
updateGamePlaytime(
|
updateGamePlaytime(
|
||||||
game,
|
game,
|
||||||
|
|||||||
@@ -462,6 +462,7 @@ export class WindowManager {
|
|||||||
|
|
||||||
editorWindow.once("ready-to-show", () => {
|
editorWindow.once("ready-to-show", () => {
|
||||||
editorWindow.show();
|
editorWindow.show();
|
||||||
|
this.mainWindow?.webContents.openDevTools();
|
||||||
if (!app.isPackaged || isStaging) {
|
if (!app.isPackaged || isStaging) {
|
||||||
editorWindow.webContents.openDevTools();
|
editorWindow.webContents.openDevTools();
|
||||||
}
|
}
|
||||||
@@ -469,11 +470,12 @@ export class WindowManager {
|
|||||||
|
|
||||||
editorWindow.webContents.on("before-input-event", (_event, input) => {
|
editorWindow.webContents.on("before-input-event", (_event, input) => {
|
||||||
if (input.key === "F12") {
|
if (input.key === "F12") {
|
||||||
editorWindow.webContents.toggleDevTools();
|
this.mainWindow?.webContents.toggleDevTools();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
editorWindow.on("close", () => {
|
editorWindow.on("close", () => {
|
||||||
|
this.mainWindow?.webContents.closeDevTools();
|
||||||
this.editorWindows.delete(themeId);
|
this.editorWindows.delete(themeId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,11 @@ export function CloudSyncContextProvider({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const getGameArtifacts = useCallback(async () => {
|
const getGameArtifacts = useCallback(async () => {
|
||||||
|
if (shop === "custom") {
|
||||||
|
setArtifacts([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
objectId,
|
objectId,
|
||||||
shop,
|
shop,
|
||||||
|
|||||||
@@ -142,10 +142,12 @@ export function GameDetailsContextProvider({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
window.electron.getGameStats(objectId, shop).then((result) => {
|
if (shop !== "custom") {
|
||||||
if (abortController.signal.aborted) return;
|
window.electron.getGameStats(objectId, shop).then((result) => {
|
||||||
setStats(result);
|
if (abortController.signal.aborted) return;
|
||||||
});
|
setStats(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const assetsPromise = window.electron.getGameAssets(objectId, shop);
|
const assetsPromise = window.electron.getGameAssets(objectId, shop);
|
||||||
|
|
||||||
@@ -167,7 +169,7 @@ export function GameDetailsContextProvider({
|
|||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (userDetails) {
|
if (userDetails && shop !== "custom") {
|
||||||
window.electron
|
window.electron
|
||||||
.getUnlockedAchievements(objectId, shop)
|
.getUnlockedAchievements(objectId, shop)
|
||||||
.then((achievements) => {
|
.then((achievements) => {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@use "../../scss/globals.scss";
|
||||||
|
|
||||||
.pagination {
|
.pagination {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
@@ -18,4 +20,31 @@
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__page-input {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 40px;
|
||||||
|
min-width: 40px;
|
||||||
|
max-width: 40px;
|
||||||
|
min-height: 40px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: solid 1px globals.$border-color;
|
||||||
|
background-color: transparent;
|
||||||
|
color: globals.$muted-color;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 0 6px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__double-chevron {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 0; // remove whitespace node width between SVGs
|
||||||
|
}
|
||||||
|
|
||||||
|
&__double-chevron > svg + svg {
|
||||||
|
margin-left: -8px; // pull the second chevron closer
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,51 @@
|
|||||||
import { Button } from "@renderer/components/button/button";
|
import { Button } from "@renderer/components/button/button";
|
||||||
import { ChevronLeftIcon, ChevronRightIcon } from "@primer/octicons-react";
|
import { ChevronLeftIcon, ChevronRightIcon } from "@primer/octicons-react";
|
||||||
import { useFormat } from "@renderer/hooks/use-format";
|
import { useFormat } from "@renderer/hooks/use-format";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import type { ChangeEvent, KeyboardEvent, RefObject } from "react";
|
||||||
import "./pagination.scss";
|
import "./pagination.scss";
|
||||||
|
|
||||||
|
interface JumpControlProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
value: string;
|
||||||
|
totalPages: number;
|
||||||
|
inputRef: RefObject<HTMLInputElement>;
|
||||||
|
onOpen: () => void;
|
||||||
|
onClose: () => void;
|
||||||
|
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
onKeyDown: (e: KeyboardEvent<HTMLInputElement>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function JumpControl({
|
||||||
|
isOpen,
|
||||||
|
value,
|
||||||
|
totalPages,
|
||||||
|
inputRef,
|
||||||
|
onOpen,
|
||||||
|
onClose,
|
||||||
|
onChange,
|
||||||
|
onKeyDown,
|
||||||
|
}: JumpControlProps) {
|
||||||
|
return isOpen ? (
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
type="number"
|
||||||
|
min={1}
|
||||||
|
max={totalPages}
|
||||||
|
className="pagination__page-input"
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
onBlur={onClose}
|
||||||
|
aria-label="Go to page"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Button theme="outline" className="pagination__button" onClick={onOpen}>
|
||||||
|
...
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
interface PaginationProps {
|
interface PaginationProps {
|
||||||
page: number;
|
page: number;
|
||||||
totalPages: number;
|
totalPages: number;
|
||||||
@@ -16,20 +59,82 @@ export function Pagination({
|
|||||||
}: PaginationProps) {
|
}: PaginationProps) {
|
||||||
const { formatNumber } = useFormat();
|
const { formatNumber } = useFormat();
|
||||||
|
|
||||||
|
const [isJumpOpen, setIsJumpOpen] = useState(false);
|
||||||
|
const [jumpValue, setJumpValue] = useState<string>("");
|
||||||
|
const jumpInputRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isJumpOpen) {
|
||||||
|
setJumpValue("");
|
||||||
|
setTimeout(() => jumpInputRef.current?.focus(), 0);
|
||||||
|
}
|
||||||
|
}, [isJumpOpen, page]);
|
||||||
|
|
||||||
if (totalPages <= 1) return null;
|
if (totalPages <= 1) return null;
|
||||||
|
|
||||||
const visiblePages = 3;
|
const visiblePages = 3;
|
||||||
|
const isLastThree = totalPages > 3 && page >= totalPages - 2;
|
||||||
|
|
||||||
let startPage = Math.max(1, page - 1);
|
let startPage = Math.max(1, page - 1);
|
||||||
let endPage = startPage + visiblePages - 1;
|
let endPage = startPage + visiblePages - 1;
|
||||||
|
|
||||||
if (endPage > totalPages) {
|
if (isLastThree) {
|
||||||
|
startPage = Math.max(1, totalPages - 2);
|
||||||
|
endPage = totalPages;
|
||||||
|
} else if (endPage > totalPages) {
|
||||||
endPage = totalPages;
|
endPage = totalPages;
|
||||||
startPage = Math.max(1, endPage - visiblePages + 1);
|
startPage = Math.max(1, endPage - visiblePages + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onJumpChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const val = e.target.value;
|
||||||
|
if (val === "") {
|
||||||
|
setJumpValue("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const num = Number(val);
|
||||||
|
if (Number.isNaN(num)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (num < 1) {
|
||||||
|
setJumpValue("1");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (num > totalPages) {
|
||||||
|
setJumpValue(String(totalPages));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setJumpValue(val);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onJumpKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
if (jumpValue.trim() === "") return;
|
||||||
|
const parsed = Number(jumpValue);
|
||||||
|
if (Number.isNaN(parsed)) return;
|
||||||
|
const target = Math.max(1, Math.min(totalPages, parsed));
|
||||||
|
onPageChange(target);
|
||||||
|
setIsJumpOpen(false);
|
||||||
|
} else if (e.key === "Escape") {
|
||||||
|
setIsJumpOpen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pagination">
|
<div className="pagination">
|
||||||
|
{startPage > 1 && (
|
||||||
|
<Button
|
||||||
|
theme="outline"
|
||||||
|
onClick={() => onPageChange(1)}
|
||||||
|
className="pagination__button"
|
||||||
|
>
|
||||||
|
<span className="pagination__double-chevron">
|
||||||
|
<ChevronLeftIcon />
|
||||||
|
<ChevronLeftIcon />
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
theme="outline"
|
theme="outline"
|
||||||
onClick={() => onPageChange(page - 1)}
|
onClick={() => onPageChange(page - 1)}
|
||||||
@@ -39,20 +144,25 @@ export function Pagination({
|
|||||||
<ChevronLeftIcon />
|
<ChevronLeftIcon />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{page > 2 && (
|
{isLastThree && startPage > 1 && (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
theme="outline"
|
theme="outline"
|
||||||
onClick={() => onPageChange(1)}
|
|
||||||
className="pagination__button"
|
className="pagination__button"
|
||||||
disabled={page === 1}
|
onClick={() => onPageChange(1)}
|
||||||
>
|
>
|
||||||
{1}
|
{formatNumber(1)}
|
||||||
</Button>
|
</Button>
|
||||||
|
<JumpControl
|
||||||
<div className="pagination__ellipsis">
|
isOpen={isJumpOpen}
|
||||||
<span className="pagination__ellipsis-text">...</span>
|
value={jumpValue}
|
||||||
</div>
|
totalPages={totalPages}
|
||||||
|
inputRef={jumpInputRef}
|
||||||
|
onOpen={() => setIsJumpOpen(true)}
|
||||||
|
onClose={() => setIsJumpOpen(false)}
|
||||||
|
onChange={onJumpChange}
|
||||||
|
onKeyDown={onJumpKeyDown}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -70,11 +180,18 @@ export function Pagination({
|
|||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{page < totalPages - 1 && (
|
{!isLastThree && page < totalPages - 1 && (
|
||||||
<>
|
<>
|
||||||
<div className="pagination__ellipsis">
|
<JumpControl
|
||||||
<span className="pagination__ellipsis-text">...</span>
|
isOpen={isJumpOpen}
|
||||||
</div>
|
value={jumpValue}
|
||||||
|
totalPages={totalPages}
|
||||||
|
inputRef={jumpInputRef}
|
||||||
|
onOpen={() => setIsJumpOpen(true)}
|
||||||
|
onClose={() => setIsJumpOpen(false)}
|
||||||
|
onChange={onJumpChange}
|
||||||
|
onKeyDown={onJumpKeyDown}
|
||||||
|
/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
theme="outline"
|
theme="outline"
|
||||||
@@ -95,6 +212,19 @@ export function Pagination({
|
|||||||
>
|
>
|
||||||
<ChevronRightIcon />
|
<ChevronRightIcon />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
{endPage < totalPages && (
|
||||||
|
<Button
|
||||||
|
theme="outline"
|
||||||
|
onClick={() => onPageChange(totalPages)}
|
||||||
|
className="pagination__button"
|
||||||
|
>
|
||||||
|
<span className="pagination__double-chevron">
|
||||||
|
<ChevronRightIcon />
|
||||||
|
<ChevronRightIcon />
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,16 @@ import {
|
|||||||
} from "@primer/octicons-react";
|
} from "@primer/octicons-react";
|
||||||
import useEmblaCarousel from "embla-carousel-react";
|
import useEmblaCarousel from "embla-carousel-react";
|
||||||
import { gameDetailsContext } from "@renderer/context";
|
import { gameDetailsContext } from "@renderer/context";
|
||||||
|
import { useAppSelector } from "@renderer/hooks";
|
||||||
import "./gallery-slider.scss";
|
import "./gallery-slider.scss";
|
||||||
|
|
||||||
export function GallerySlider() {
|
export function GallerySlider() {
|
||||||
const { shopDetails } = useContext(gameDetailsContext);
|
const { shopDetails } = useContext(gameDetailsContext);
|
||||||
const { t } = useTranslation("game_details");
|
const { t } = useTranslation("game_details");
|
||||||
|
const userPreferences = useAppSelector(
|
||||||
|
(state) => state.userPreferences.value
|
||||||
|
);
|
||||||
|
const autoplayEnabled = userPreferences?.autoplayGameTrailers !== false;
|
||||||
|
|
||||||
const hasScreenshots = shopDetails && shopDetails.screenshots?.length;
|
const hasScreenshots = shopDetails && shopDetails.screenshots?.length;
|
||||||
|
|
||||||
@@ -164,7 +169,7 @@ export function GallerySlider() {
|
|||||||
poster={item.poster}
|
poster={item.poster}
|
||||||
loop
|
loop
|
||||||
muted
|
muted
|
||||||
autoPlay
|
autoPlay={autoplayEnabled}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
>
|
>
|
||||||
<source src={item.videoSrc} />
|
<source src={item.videoSrc} />
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ export function GameDetailsContent() {
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{game?.shop !== "custom" && shop && objectId && (
|
{shop !== "custom" && shop && objectId && (
|
||||||
<GameReviews
|
<GameReviews
|
||||||
shop={shop}
|
shop={shop}
|
||||||
objectId={objectId}
|
objectId={objectId}
|
||||||
@@ -241,7 +241,7 @@ export function GameDetailsContent() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{game?.shop !== "custom" && <Sidebar />}
|
{shop !== "custom" && <Sidebar />}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ export function GameReviews({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const checkUserReview = useCallback(async () => {
|
const checkUserReview = useCallback(async () => {
|
||||||
if (!objectId || !userDetailsId) return;
|
if (!objectId || !userDetailsId || shop === "custom") return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await window.electron.hydraApi.get<{
|
const response = await window.electron.hydraApi.get<{
|
||||||
@@ -147,7 +147,7 @@ export function GameReviews({
|
|||||||
|
|
||||||
const loadReviews = useCallback(
|
const loadReviews = useCallback(
|
||||||
async (reset = false) => {
|
async (reset = false) => {
|
||||||
if (!objectId) return;
|
if (!objectId || shop === "custom") return;
|
||||||
|
|
||||||
if (abortControllerRef.current) {
|
if (abortControllerRef.current) {
|
||||||
abortControllerRef.current.abort();
|
abortControllerRef.current.abort();
|
||||||
|
|||||||
@@ -146,6 +146,8 @@ $hero-height: 350px;
|
|||||||
&__game-logo {
|
&__game-logo {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
|
object-fit: contain;
|
||||||
|
object-position: left bottom;
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
width: 250px;
|
width: 250px;
|
||||||
@@ -153,6 +155,7 @@ $hero-height: 350px;
|
|||||||
|
|
||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
|
max-height: 150px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ export function SettingsBehavior() {
|
|||||||
showDownloadSpeedInMegabytes: false,
|
showDownloadSpeedInMegabytes: false,
|
||||||
extractFilesByDefault: true,
|
extractFilesByDefault: true,
|
||||||
enableSteamAchievements: false,
|
enableSteamAchievements: false,
|
||||||
|
autoplayGameTrailers: true,
|
||||||
|
hideToTrayOnGameStart: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { t } = useTranslation("settings");
|
const { t } = useTranslation("settings");
|
||||||
@@ -49,6 +51,8 @@ export function SettingsBehavior() {
|
|||||||
extractFilesByDefault: userPreferences.extractFilesByDefault ?? true,
|
extractFilesByDefault: userPreferences.extractFilesByDefault ?? true,
|
||||||
enableSteamAchievements:
|
enableSteamAchievements:
|
||||||
userPreferences.enableSteamAchievements ?? false,
|
userPreferences.enableSteamAchievements ?? false,
|
||||||
|
autoplayGameTrailers: userPreferences.autoplayGameTrailers ?? true,
|
||||||
|
hideToTrayOnGameStart: userPreferences.hideToTrayOnGameStart ?? false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [userPreferences]);
|
}, [userPreferences]);
|
||||||
@@ -76,6 +80,16 @@ export function SettingsBehavior() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<CheckboxField
|
||||||
|
label={t("hide_to_tray_on_game_start")}
|
||||||
|
checked={form.hideToTrayOnGameStart}
|
||||||
|
onChange={() =>
|
||||||
|
handleChange({
|
||||||
|
hideToTrayOnGameStart: !form.hideToTrayOnGameStart,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
{showRunAtStartup && (
|
{showRunAtStartup && (
|
||||||
<CheckboxField
|
<CheckboxField
|
||||||
label={t("launch_with_system")}
|
label={t("launch_with_system")}
|
||||||
@@ -120,6 +134,14 @@ export function SettingsBehavior() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<CheckboxField
|
||||||
|
label={t("autoplay_trailers_on_game_page")}
|
||||||
|
checked={form.autoplayGameTrailers}
|
||||||
|
onChange={() =>
|
||||||
|
handleChange({ autoplayGameTrailers: !form.autoplayGameTrailers })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<CheckboxField
|
<CheckboxField
|
||||||
label={t("disable_nsfw_alert")}
|
label={t("disable_nsfw_alert")}
|
||||||
checked={form.disableNsfwAlert}
|
checked={form.disableNsfwAlert}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export type GameShop = "steam" | "epic" | "custom";
|
export type GameShop = "steam" | "custom";
|
||||||
|
|
||||||
export type ShortcutLocation = "desktop" | "start_menu";
|
export type ShortcutLocation = "desktop" | "start_menu";
|
||||||
|
|
||||||
|
|||||||
@@ -118,6 +118,8 @@ export interface UserPreferences {
|
|||||||
showDownloadSpeedInMegabytes?: boolean;
|
showDownloadSpeedInMegabytes?: boolean;
|
||||||
extractFilesByDefault?: boolean;
|
extractFilesByDefault?: boolean;
|
||||||
enableSteamAchievements?: boolean;
|
enableSteamAchievements?: boolean;
|
||||||
|
autoplayGameTrailers?: boolean;
|
||||||
|
hideToTrayOnGameStart?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ScreenState {
|
export interface ScreenState {
|
||||||
|
|||||||
Reference in New Issue
Block a user