mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-02-03 00:33:49 +03:00
feat: adding modal animations
This commit is contained in:
commit
80a25bf409
@ -101,15 +101,23 @@
|
|||||||
"open_screenshot": "Open screenshot {{number}}",
|
"open_screenshot": "Open screenshot {{number}}",
|
||||||
"download_settings": "Download settings",
|
"download_settings": "Download settings",
|
||||||
"downloader": "Downloader",
|
"downloader": "Downloader",
|
||||||
"select_executable": "Select executable",
|
"select_executable": "Select",
|
||||||
"no_executable_selected": "No executable selected",
|
"no_executable_selected": "No executable selected",
|
||||||
"open_folder": "Open folder",
|
"open_folder": "Open folder",
|
||||||
"open_download_location": "Abrir local de download",
|
"open_download_location": "See downloaded files",
|
||||||
"create_shortcut": "Create shortcut",
|
"create_shortcut": "Create desktop shortcut",
|
||||||
"remove_files": "Remove files",
|
"remove_files": "Remove files",
|
||||||
"remove_from_library_title": "Are you sure?",
|
"remove_from_library_title": "Are you sure?",
|
||||||
"remove_from_library_description": "This will remove {{game}} from your library",
|
"remove_from_library_description": "This will remove {{game}} from your library",
|
||||||
"options": "Options"
|
"options": "Options",
|
||||||
|
"executable_section_title": "Executable",
|
||||||
|
"executable_section_description": "Path of the file that will be executed when \"Play\" is clicked",
|
||||||
|
"downloads_secion_title": "Downloads",
|
||||||
|
"downloads_section_description": "Check out updates or other versions of this game",
|
||||||
|
"danger_zone_section_title": "Danger zone",
|
||||||
|
"danger_zone_section_description": "Remove this game from your library or the files downloaded by Hydra",
|
||||||
|
"download_in_progress": "Download in progress",
|
||||||
|
"download_paused": "Download paused"
|
||||||
},
|
},
|
||||||
"activation": {
|
"activation": {
|
||||||
"title": "Activate Hydra",
|
"title": "Activate Hydra",
|
||||||
|
@ -98,15 +98,23 @@
|
|||||||
"open_screenshot": "Ver captura de tela {{number}}",
|
"open_screenshot": "Ver captura de tela {{number}}",
|
||||||
"download_settings": "Ajustes do download",
|
"download_settings": "Ajustes do download",
|
||||||
"downloader": "Downloader",
|
"downloader": "Downloader",
|
||||||
"select_executable": "Selecionar executável",
|
"select_executable": "Selecionar",
|
||||||
"no_executable_selected": "Nenhum executável selecionado",
|
"no_executable_selected": "Nenhum executável selecionado",
|
||||||
"open_folder": "Abrir pasta",
|
"open_folder": "Abrir pasta",
|
||||||
"open_download_location": "Abrir local de download",
|
"open_download_location": "Ver arquivos baixados",
|
||||||
"create_shortcut": "Criar atalho",
|
"create_shortcut": "Criar atalho na área de trabalho",
|
||||||
"remove_files": "Remover arquivos",
|
"remove_files": "Remover arquivos",
|
||||||
"remove_from_library_description": "Tem certeza que deseja remover {{game}} da sua biblioteca?",
|
"options": "Opções",
|
||||||
|
"remove_from_library_description": "Isso irá remover {{game}} da sua biblioteca",
|
||||||
"remove_from_library_title": "Tem certeza?",
|
"remove_from_library_title": "Tem certeza?",
|
||||||
"options": "Opções"
|
"executable_section_title": "Executável",
|
||||||
|
"executable_section_description": "O caminho do arquivo que será executado ao clicar em \"Jogar\"",
|
||||||
|
"downloads_secion_title": "Downloads",
|
||||||
|
"downloads_section_description": "Confira atualizações ou versões diferentes para este mesmo título",
|
||||||
|
"danger_zone_section_title": "Zona de perigo",
|
||||||
|
"danger_zone_section_description": "Remova o jogo da sua biblioteca ou os arquivos que foram baixados pelo Hydra",
|
||||||
|
"download_in_progress": "Download em andamento",
|
||||||
|
"download_paused": "Download pausado"
|
||||||
},
|
},
|
||||||
"activation": {
|
"activation": {
|
||||||
"title": "Ativação",
|
"title": "Ativação",
|
||||||
|
@ -13,10 +13,13 @@ const createGameShortcut = async (
|
|||||||
|
|
||||||
if (game) {
|
if (game) {
|
||||||
const filePath = game.executablePath;
|
const filePath = game.executablePath;
|
||||||
|
|
||||||
|
const options = { filePath, name: game.title };
|
||||||
|
|
||||||
return createDesktopShortcut({
|
return createDesktopShortcut({
|
||||||
windows: { filePath },
|
windows: options,
|
||||||
linux: { filePath },
|
linux: options,
|
||||||
osx: { filePath },
|
osx: options,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,10 @@ const removeGameFromLibrary = async (
|
|||||||
_event: Electron.IpcMainInvokeEvent,
|
_event: Electron.IpcMainInvokeEvent,
|
||||||
gameId: number
|
gameId: number
|
||||||
) => {
|
) => {
|
||||||
gameRepository.update({ id: gameId }, { isDeleted: true });
|
gameRepository.update(
|
||||||
|
{ id: gameId },
|
||||||
|
{ isDeleted: true, executablePath: null }
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
registerEvent("removeGameFromLibrary", removeGameFromLibrary);
|
registerEvent("removeGameFromLibrary", removeGameFromLibrary);
|
||||||
|
@ -55,4 +55,15 @@ export const button = styleVariants({
|
|||||||
color: "#c0c1c7",
|
color: "#c0c1c7",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
danger: [
|
||||||
|
base,
|
||||||
|
{
|
||||||
|
border: `solid 1px #a31533`,
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
color: "white",
|
||||||
|
":hover": {
|
||||||
|
backgroundColor: "#a31533",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
@ -54,7 +54,7 @@ export function Hero() {
|
|||||||
>
|
>
|
||||||
<div className={styles.backdrop}>
|
<div className={styles.backdrop}>
|
||||||
<img
|
<img
|
||||||
src="https://cdn2.steamgriddb.com/hero/4ef10445b952a8b3c93a9379d581146a.jpg"
|
src={steamUrlBuilder.libraryHero(FEATURED_GAME_ID)}
|
||||||
alt={FEATURED_GAME_TITLE}
|
alt={FEATURED_GAME_TITLE}
|
||||||
className={styles.heroMedia}
|
className={styles.heroMedia}
|
||||||
/>
|
/>
|
||||||
|
@ -2,24 +2,25 @@ import { keyframes, style } from "@vanilla-extract/css";
|
|||||||
import { recipe } from "@vanilla-extract/recipes";
|
import { recipe } from "@vanilla-extract/recipes";
|
||||||
import { SPACING_UNIT, vars } from "../../theme.css";
|
import { SPACING_UNIT, vars } from "../../theme.css";
|
||||||
|
|
||||||
export const fadeIn = keyframes({
|
export const scaleFadeIn = keyframes({
|
||||||
"0%": { opacity: 0 },
|
"0%": { opacity: "0", scale: "0.5" },
|
||||||
"100%": {
|
"100%": {
|
||||||
opacity: 1,
|
opacity: "1",
|
||||||
|
scale: "1",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const fadeOut = keyframes({
|
export const scaleFadeOut = keyframes({
|
||||||
"0%": { opacity: 1 },
|
"0%": { opacity: "1", scale: "1" },
|
||||||
"100%": {
|
"100%": {
|
||||||
opacity: 0,
|
opacity: "0",
|
||||||
|
scale: "0.5",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const modal = recipe({
|
export const modal = recipe({
|
||||||
base: {
|
base: {
|
||||||
animationName: fadeIn,
|
animation: `${scaleFadeIn} 0.2s cubic-bezier(0.33, 1, 0.68, 1) 0s 1 normal none running`,
|
||||||
animationDuration: "0.3s",
|
|
||||||
backgroundColor: vars.color.background,
|
backgroundColor: vars.color.background,
|
||||||
borderRadius: "4px",
|
borderRadius: "4px",
|
||||||
maxWidth: "600px",
|
maxWidth: "600px",
|
||||||
@ -33,8 +34,8 @@ export const modal = recipe({
|
|||||||
variants: {
|
variants: {
|
||||||
closing: {
|
closing: {
|
||||||
true: {
|
true: {
|
||||||
animationName: fadeOut,
|
animationName: scaleFadeOut,
|
||||||
opacity: 0,
|
opacity: "0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
large: {
|
large: {
|
||||||
|
@ -3,7 +3,7 @@ import { useParams, useSearchParams } from "react-router-dom";
|
|||||||
|
|
||||||
import { setHeaderTitle } from "@renderer/features";
|
import { setHeaderTitle } from "@renderer/features";
|
||||||
import { getSteamLanguage } from "@renderer/helpers";
|
import { getSteamLanguage } from "@renderer/helpers";
|
||||||
import { useAppDispatch, useDownload } from "@renderer/hooks";
|
import { useAppDispatch, useAppSelector, useDownload } from "@renderer/hooks";
|
||||||
|
|
||||||
import type { Game, GameRepack, GameShop, ShopDetails } from "@types";
|
import type { Game, GameRepack, GameShop, ShopDetails } from "@types";
|
||||||
|
|
||||||
@ -16,6 +16,7 @@ import {
|
|||||||
RepacksModal,
|
RepacksModal,
|
||||||
} from "./modals";
|
} from "./modals";
|
||||||
import { Downloader } from "@shared";
|
import { Downloader } from "@shared";
|
||||||
|
import { GameOptionsModal } from "./modals/game-options-modal";
|
||||||
|
|
||||||
export interface GameDetailsContext {
|
export interface GameDetailsContext {
|
||||||
game: Game | null;
|
game: Game | null;
|
||||||
@ -29,6 +30,8 @@ export interface GameDetailsContext {
|
|||||||
gameColor: string;
|
gameColor: string;
|
||||||
setGameColor: React.Dispatch<React.SetStateAction<string>>;
|
setGameColor: React.Dispatch<React.SetStateAction<string>>;
|
||||||
openRepacksModal: () => void;
|
openRepacksModal: () => void;
|
||||||
|
openGameOptionsModal: () => void;
|
||||||
|
selectGameExecutable: () => Promise<string | null>;
|
||||||
updateGame: () => Promise<void>;
|
updateGame: () => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,6 +47,8 @@ export const gameDetailsContext = createContext<GameDetailsContext>({
|
|||||||
gameColor: "",
|
gameColor: "",
|
||||||
setGameColor: () => {},
|
setGameColor: () => {},
|
||||||
openRepacksModal: () => {},
|
openRepacksModal: () => {},
|
||||||
|
openGameOptionsModal: () => {},
|
||||||
|
selectGameExecutable: async () => null,
|
||||||
updateGame: async () => {},
|
updateGame: async () => {},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -70,6 +75,7 @@ export function GameDetailsContextProvider({
|
|||||||
>(null);
|
>(null);
|
||||||
const [isGameRunning, setisGameRunning] = useState(false);
|
const [isGameRunning, setisGameRunning] = useState(false);
|
||||||
const [showRepacksModal, setShowRepacksModal] = useState(false);
|
const [showRepacksModal, setShowRepacksModal] = useState(false);
|
||||||
|
const [showGameOptionsModal, setShowGameOptionsModal] = useState(false);
|
||||||
|
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
|
|
||||||
@ -81,6 +87,10 @@ export function GameDetailsContextProvider({
|
|||||||
|
|
||||||
const { startDownload, lastPacket } = useDownload();
|
const { startDownload, lastPacket } = useDownload();
|
||||||
|
|
||||||
|
const userPreferences = useAppSelector(
|
||||||
|
(state) => state.userPreferences.value
|
||||||
|
);
|
||||||
|
|
||||||
const updateGame = useCallback(async () => {
|
const updateGame = useCallback(async () => {
|
||||||
return window.electron
|
return window.electron
|
||||||
.getGameByObjectID(objectID!)
|
.getGameByObjectID(objectID!)
|
||||||
@ -158,6 +168,7 @@ export function GameDetailsContextProvider({
|
|||||||
|
|
||||||
await updateGame();
|
await updateGame();
|
||||||
setShowRepacksModal(false);
|
setShowRepacksModal(false);
|
||||||
|
setShowGameOptionsModal(false);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
repack.repacker === "onlinefix" &&
|
repack.repacker === "onlinefix" &&
|
||||||
@ -172,7 +183,36 @@ export function GameDetailsContextProvider({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getDownloadsPath = async () => {
|
||||||
|
if (userPreferences?.downloadsPath) return userPreferences.downloadsPath;
|
||||||
|
return window.electron.getDefaultDownloadsPath();
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectGameExecutable = async () => {
|
||||||
|
const downloadsPath = await getDownloadsPath();
|
||||||
|
|
||||||
|
return window.electron
|
||||||
|
.showOpenDialog({
|
||||||
|
properties: ["openFile"],
|
||||||
|
defaultPath: downloadsPath,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
name: "Game executable",
|
||||||
|
extensions: ["exe"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.then(({ filePaths }) => {
|
||||||
|
if (filePaths && filePaths.length > 0) {
|
||||||
|
return filePaths[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const openRepacksModal = () => setShowRepacksModal(true);
|
const openRepacksModal = () => setShowRepacksModal(true);
|
||||||
|
const openGameOptionsModal = () => setShowGameOptionsModal(true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Provider
|
<Provider
|
||||||
@ -188,6 +228,8 @@ export function GameDetailsContextProvider({
|
|||||||
gameColor,
|
gameColor,
|
||||||
setGameColor,
|
setGameColor,
|
||||||
openRepacksModal,
|
openRepacksModal,
|
||||||
|
openGameOptionsModal,
|
||||||
|
selectGameExecutable,
|
||||||
updateGame,
|
updateGame,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -208,6 +250,16 @@ export function GameDetailsContextProvider({
|
|||||||
onClose={() => setShowInstructionsModal(null)}
|
onClose={() => setShowInstructionsModal(null)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{game && (
|
||||||
|
<GameOptionsModal
|
||||||
|
visible={showGameOptionsModal}
|
||||||
|
game={game}
|
||||||
|
onClose={() => {
|
||||||
|
setShowGameOptionsModal(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
</>
|
</>
|
||||||
</Provider>
|
</Provider>
|
||||||
|
@ -13,5 +13,6 @@ export const actions = style({
|
|||||||
|
|
||||||
export const separator = style({
|
export const separator = style({
|
||||||
width: "1px",
|
width: "1px",
|
||||||
backgroundColor: vars.color.border,
|
backgroundColor: vars.color.muted,
|
||||||
|
opacity: "0.2",
|
||||||
});
|
});
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
import { GearIcon, PlayIcon, PlusCircleIcon } from "@primer/octicons-react";
|
import { GearIcon, PlayIcon, PlusCircleIcon } from "@primer/octicons-react";
|
||||||
import { Button } from "@renderer/components";
|
import { Button } from "@renderer/components";
|
||||||
import { useAppSelector, useDownload, useLibrary } from "@renderer/hooks";
|
import { useDownload, useLibrary } from "@renderer/hooks";
|
||||||
import { useContext, useState } from "react";
|
import { useContext, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import * as styles from "./hero-panel-actions.css";
|
import * as styles from "./hero-panel-actions.css";
|
||||||
import { gameDetailsContext } from "../game-details.context";
|
import { gameDetailsContext } from "../game-details.context";
|
||||||
import { GameOptionsModal } from "../modals/game-options-modal";
|
|
||||||
|
|
||||||
export function HeroPanelActions() {
|
export function HeroPanelActions() {
|
||||||
const [toggleLibraryGameDisabled, setToggleLibraryGameDisabled] =
|
const [toggleLibraryGameDisabled, setToggleLibraryGameDisabled] =
|
||||||
useState(false);
|
useState(false);
|
||||||
const [showGameOptionsModal, setShowGameOptionsModal] = useState(false);
|
|
||||||
|
|
||||||
const { isGameDeleting } = useDownload();
|
const { isGameDeleting } = useDownload();
|
||||||
|
|
||||||
@ -21,45 +19,15 @@ export function HeroPanelActions() {
|
|||||||
objectID,
|
objectID,
|
||||||
gameTitle,
|
gameTitle,
|
||||||
openRepacksModal,
|
openRepacksModal,
|
||||||
|
openGameOptionsModal,
|
||||||
updateGame,
|
updateGame,
|
||||||
|
selectGameExecutable,
|
||||||
} = useContext(gameDetailsContext);
|
} = useContext(gameDetailsContext);
|
||||||
|
|
||||||
const userPreferences = useAppSelector(
|
|
||||||
(state) => state.userPreferences.value
|
|
||||||
);
|
|
||||||
|
|
||||||
const { updateLibrary } = useLibrary();
|
const { updateLibrary } = useLibrary();
|
||||||
|
|
||||||
const { t } = useTranslation("game_details");
|
const { t } = useTranslation("game_details");
|
||||||
|
|
||||||
const getDownloadsPath = async () => {
|
|
||||||
if (userPreferences?.downloadsPath) return userPreferences.downloadsPath;
|
|
||||||
return window.electron.getDefaultDownloadsPath();
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectGameExecutable = async () => {
|
|
||||||
const downloadsPath = await getDownloadsPath();
|
|
||||||
|
|
||||||
return window.electron
|
|
||||||
.showOpenDialog({
|
|
||||||
properties: ["openFile"],
|
|
||||||
defaultPath: downloadsPath,
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
name: "Game executable",
|
|
||||||
extensions: ["exe"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
.then(({ filePaths }) => {
|
|
||||||
if (filePaths && filePaths.length > 0) {
|
|
||||||
return filePaths[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const addGameToLibrary = async () => {
|
const addGameToLibrary = async () => {
|
||||||
setToggleLibraryGameDisabled(true);
|
setToggleLibraryGameDisabled(true);
|
||||||
|
|
||||||
@ -126,53 +94,40 @@ export function HeroPanelActions() {
|
|||||||
|
|
||||||
if (game) {
|
if (game) {
|
||||||
return (
|
return (
|
||||||
<>
|
<div className={styles.actions}>
|
||||||
<GameOptionsModal
|
{isGameRunning ? (
|
||||||
visible={showGameOptionsModal}
|
|
||||||
game={game}
|
|
||||||
onClose={() => {
|
|
||||||
setShowGameOptionsModal(false);
|
|
||||||
}}
|
|
||||||
selectGameExecutable={selectGameExecutable}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className={styles.actions}>
|
|
||||||
{isGameRunning ? (
|
|
||||||
<Button
|
|
||||||
onClick={closeGame}
|
|
||||||
theme="outline"
|
|
||||||
disabled={deleting}
|
|
||||||
className={styles.heroPanelAction}
|
|
||||||
>
|
|
||||||
{t("close")}
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<Button
|
|
||||||
onClick={openGame}
|
|
||||||
theme="outline"
|
|
||||||
disabled={deleting || isGameRunning}
|
|
||||||
className={styles.heroPanelAction}
|
|
||||||
>
|
|
||||||
<PlayIcon />
|
|
||||||
{t("play")}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className={styles.separator} />
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={closeGame}
|
||||||
setShowGameOptionsModal(true);
|
theme="outline"
|
||||||
}}
|
disabled={deleting}
|
||||||
|
className={styles.heroPanelAction}
|
||||||
|
>
|
||||||
|
{t("close")}
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
onClick={openGame}
|
||||||
theme="outline"
|
theme="outline"
|
||||||
disabled={deleting || isGameRunning}
|
disabled={deleting || isGameRunning}
|
||||||
className={styles.heroPanelAction}
|
className={styles.heroPanelAction}
|
||||||
>
|
>
|
||||||
<GearIcon />
|
<PlayIcon />
|
||||||
{t("options")}
|
{t("play")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
)}
|
||||||
</>
|
|
||||||
|
<div className={styles.separator} />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onClick={openGameOptionsModal}
|
||||||
|
theme="outline"
|
||||||
|
disabled={deleting || isGameRunning}
|
||||||
|
className={styles.heroPanelAction}
|
||||||
|
>
|
||||||
|
<GearIcon />
|
||||||
|
{t("options")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,8 @@ export function HeroPanelPlaytime() {
|
|||||||
|
|
||||||
const { i18n, t } = useTranslation("game_details");
|
const { i18n, t } = useTranslation("game_details");
|
||||||
|
|
||||||
|
const { progress, lastPacket } = useDownload();
|
||||||
|
|
||||||
const { formatDistance } = useDate();
|
const { formatDistance } = useDate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -48,40 +50,33 @@ export function HeroPanelPlaytime() {
|
|||||||
return t("amount_hours", { amount: numberFormatter.format(hours) });
|
return t("amount_hours", { amount: numberFormatter.format(hours) });
|
||||||
};
|
};
|
||||||
|
|
||||||
const { progress, lastPacket } = useDownload();
|
if (!game) return null;
|
||||||
|
|
||||||
|
const hasDownload =
|
||||||
|
["active", "paused"].includes(game.status) && game.progress !== 1;
|
||||||
|
|
||||||
const isGameDownloading =
|
const isGameDownloading =
|
||||||
game?.status === "active" && lastPacket?.game.id === game?.id;
|
game.status === "active" && lastPacket?.game.id === game.id;
|
||||||
|
|
||||||
if (!game) return;
|
const downloadInProgressInfo = (
|
||||||
|
<div className={styles.downloadDetailsRow}>
|
||||||
|
<Link to="/downloads" className={styles.downloadsLink}>
|
||||||
|
{game.status === "active"
|
||||||
|
? t("download_in_progress")
|
||||||
|
: t("download_paused")}
|
||||||
|
</Link>
|
||||||
|
|
||||||
let downloadContent: JSX.Element | null = null;
|
<small>
|
||||||
|
{isGameDownloading ? progress : formatDownloadProgress(game.progress)}
|
||||||
if (game.status === "active") {
|
</small>
|
||||||
if (lastPacket?.isDownloadingMetadata && isGameDownloading) {
|
</div>
|
||||||
downloadContent = <p>{t("downloading_metadata")}</p>;
|
);
|
||||||
} else if (game.progress !== 1) {
|
|
||||||
downloadContent = (
|
|
||||||
<div className={styles.downloadDetailsRow}>
|
|
||||||
<Link to="/downloads" className={styles.downloadsLink}>
|
|
||||||
Download em andamento
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<small>
|
|
||||||
{isGameDownloading
|
|
||||||
? progress
|
|
||||||
: formatDownloadProgress(game.progress)}
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!game.lastTimePlayed) {
|
if (!game.lastTimePlayed) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p>{t("not_played_yet", { title: game?.title })}</p>
|
<p>{t("not_played_yet", { title: game?.title })}</p>
|
||||||
{downloadContent}
|
{hasDownload && downloadInProgressInfo}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -89,15 +84,9 @@ export function HeroPanelPlaytime() {
|
|||||||
if (isGameRunning) {
|
if (isGameRunning) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{downloadContent || (
|
|
||||||
<p>
|
|
||||||
{t("play_time", {
|
|
||||||
amount: formatPlayTime(),
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<p>{t("playing_now")}</p>
|
<p>{t("playing_now")}</p>
|
||||||
|
|
||||||
|
{hasDownload && downloadInProgressInfo}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -110,7 +99,9 @@ export function HeroPanelPlaytime() {
|
|||||||
})}
|
})}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{downloadContent || (
|
{hasDownload ? (
|
||||||
|
downloadInProgressInfo
|
||||||
|
) : (
|
||||||
<p>
|
<p>
|
||||||
{t("last_time_played", {
|
{t("last_time_played", {
|
||||||
period: lastTimePlayed,
|
period: lastTimePlayed,
|
||||||
|
@ -36,6 +36,7 @@ export const downloadDetailsRow = style({
|
|||||||
|
|
||||||
export const downloadsLink = style({
|
export const downloadsLink = style({
|
||||||
color: vars.color.body,
|
color: vars.color.body,
|
||||||
|
textDecoration: "underline",
|
||||||
});
|
});
|
||||||
|
|
||||||
export const progressBar = recipe({
|
export const progressBar = recipe({
|
||||||
|
@ -7,6 +7,17 @@ export const optionsContainer = style({
|
|||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const gameOptionHeader = style({
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: `${SPACING_UNIT}px`,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const gameOptionHeaderDescription = style({
|
||||||
|
fontFamily: "'Fira Sans', sans-serif",
|
||||||
|
fontWeight: "400",
|
||||||
|
});
|
||||||
|
|
||||||
export const gameOptionRow = style({
|
export const gameOptionRow = style({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
gap: `${SPACING_UNIT}px`,
|
gap: `${SPACING_UNIT}px`,
|
||||||
|
@ -4,7 +4,6 @@ import { Button, Modal, TextField } from "@renderer/components";
|
|||||||
import type { Game } from "@types";
|
import type { Game } from "@types";
|
||||||
import * as styles from "./game-options-modal.css";
|
import * as styles from "./game-options-modal.css";
|
||||||
import { gameDetailsContext } from "../game-details.context";
|
import { gameDetailsContext } from "../game-details.context";
|
||||||
import { NoEntryIcon, TrashIcon } from "@primer/octicons-react";
|
|
||||||
import { DeleteGameModal } from "@renderer/pages/downloads/delete-game-modal";
|
import { DeleteGameModal } from "@renderer/pages/downloads/delete-game-modal";
|
||||||
import { useDownload } from "@renderer/hooks";
|
import { useDownload } from "@renderer/hooks";
|
||||||
import { RemoveGameFromLibraryModal } from "./remove-from-library-modal";
|
import { RemoveGameFromLibraryModal } from "./remove-from-library-modal";
|
||||||
@ -13,43 +12,50 @@ export interface GameOptionsModalProps {
|
|||||||
visible: boolean;
|
visible: boolean;
|
||||||
game: Game;
|
game: Game;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
selectGameExecutable: () => Promise<string | null>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GameOptionsModal({
|
export function GameOptionsModal({
|
||||||
visible,
|
visible,
|
||||||
game,
|
game,
|
||||||
onClose,
|
onClose,
|
||||||
selectGameExecutable,
|
|
||||||
}: GameOptionsModalProps) {
|
}: GameOptionsModalProps) {
|
||||||
const { t } = useTranslation("game_details");
|
const { t } = useTranslation("game_details");
|
||||||
|
|
||||||
const { updateGame, openRepacksModal } = useContext(gameDetailsContext);
|
const { updateGame, openRepacksModal, selectGameExecutable } =
|
||||||
|
useContext(gameDetailsContext);
|
||||||
|
|
||||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||||
const [showRemoveGameModal, setShowRemoveGameModal] = useState(false);
|
const [showRemoveGameModal, setShowRemoveGameModal] = useState(false);
|
||||||
|
|
||||||
const { removeGameInstaller, removeGameFromLibrary, isGameDeleting } =
|
const {
|
||||||
useDownload();
|
removeGameInstaller,
|
||||||
|
removeGameFromLibrary,
|
||||||
|
isGameDeleting,
|
||||||
|
cancelDownload,
|
||||||
|
} = useDownload();
|
||||||
|
|
||||||
const deleting = game ? isGameDeleting(game?.id) : false;
|
const deleting = isGameDeleting(game.id);
|
||||||
|
|
||||||
const { lastPacket } = useDownload();
|
const { lastPacket } = useDownload();
|
||||||
|
|
||||||
const isGameDownloading =
|
const isGameDownloading =
|
||||||
game?.status === "active" && lastPacket?.game.id === game?.id;
|
game.status === "active" && lastPacket?.game.id === game.id;
|
||||||
|
|
||||||
const handleRemoveGameFromLibrary = async () => {
|
const handleRemoveGameFromLibrary = async () => {
|
||||||
|
if (isGameDownloading) {
|
||||||
|
await cancelDownload(game.id);
|
||||||
|
}
|
||||||
|
|
||||||
await removeGameFromLibrary(game.id);
|
await removeGameFromLibrary(game.id);
|
||||||
updateGame();
|
updateGame();
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChangeExecutableLocation = async () => {
|
const handleChangeExecutableLocation = async () => {
|
||||||
const location = await selectGameExecutable();
|
const path = await selectGameExecutable();
|
||||||
|
|
||||||
if (location) {
|
if (path) {
|
||||||
await window.electron.updateExecutablePath(game.id, location);
|
await window.electron.updateExecutablePath(game.id, path);
|
||||||
updateGame();
|
updateGame();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -73,54 +79,34 @@ export function GameOptionsModal({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<DeleteGameModal
|
||||||
|
visible={showDeleteModal}
|
||||||
|
onClose={() => setShowDeleteModal(false)}
|
||||||
|
deleteGame={handleDeleteGame}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<RemoveGameFromLibraryModal
|
||||||
|
visible={showRemoveGameModal}
|
||||||
|
onClose={() => setShowRemoveGameModal(false)}
|
||||||
|
removeGameFromLibrary={handleRemoveGameFromLibrary}
|
||||||
|
game={game}
|
||||||
|
/>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
visible={visible}
|
visible={visible}
|
||||||
title={game.title}
|
title={game.title}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
large={true}
|
large={true}
|
||||||
>
|
>
|
||||||
<DeleteGameModal
|
|
||||||
visible={showDeleteModal}
|
|
||||||
onClose={() => setShowDeleteModal(false)}
|
|
||||||
deleteGame={handleDeleteGame}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<RemoveGameFromLibraryModal
|
|
||||||
visible={showRemoveGameModal}
|
|
||||||
onClose={() => setShowRemoveGameModal(false)}
|
|
||||||
removeGameFromLibrary={handleRemoveGameFromLibrary}
|
|
||||||
game={game}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className={styles.optionsContainer}>
|
<div className={styles.optionsContainer}>
|
||||||
<div className={styles.gameOptionRow}>
|
<div className={styles.gameOptionHeader}>
|
||||||
<Button
|
<h2>{t("executable_section_title")}</h2>
|
||||||
onClick={openRepacksModal}
|
<h4 className={styles.gameOptionHeaderDescription}>
|
||||||
theme="outline"
|
{t("executable_section_description")}
|
||||||
disabled={deleting}
|
</h4>
|
||||||
>
|
|
||||||
{t("open_download_options")}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={handleOpenDownloadFolder}
|
|
||||||
style={{ alignSelf: "flex-end" }}
|
|
||||||
theme="outline"
|
|
||||||
disabled={deleting || !game.downloadPath}
|
|
||||||
>
|
|
||||||
{t("open_download_location")}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={handleCreateShortcut}
|
|
||||||
style={{ alignSelf: "flex-end" }}
|
|
||||||
theme="outline"
|
|
||||||
disabled={deleting || !game.executablePath}
|
|
||||||
>
|
|
||||||
{t("create_shortcut")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.gameOptionRow}>
|
<div className={styles.gameOptionRow}>
|
||||||
<TextField
|
<TextField
|
||||||
label="Caminho do executável"
|
|
||||||
value={game.executablePath || ""}
|
value={game.executablePath || ""}
|
||||||
readOnly
|
readOnly
|
||||||
theme="dark"
|
theme="dark"
|
||||||
@ -130,43 +116,74 @@ export function GameOptionsModal({
|
|||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
theme="outline"
|
theme="outline"
|
||||||
style={{ alignSelf: "flex-end" }}
|
|
||||||
onClick={handleChangeExecutableLocation}
|
onClick={handleChangeExecutableLocation}
|
||||||
>
|
>
|
||||||
{t("select_executable")}
|
{t("select_executable")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
</div>
|
||||||
type="button"
|
|
||||||
theme="outline"
|
{game.executablePath && (
|
||||||
style={{ alignSelf: "flex-end" }}
|
<div className={styles.gameOptionRow}>
|
||||||
onClick={handleOpenGameExecutablePath}
|
<Button
|
||||||
disabled={!game.executablePath}
|
type="button"
|
||||||
>
|
theme="outline"
|
||||||
{t("open_folder")}
|
onClick={handleOpenGameExecutablePath}
|
||||||
</Button>
|
>
|
||||||
|
{t("open_folder")}
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleCreateShortcut} theme="outline">
|
||||||
|
{t("create_shortcut")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className={styles.gameOptionHeader}>
|
||||||
|
<h2>{t("downloads_secion_title")}</h2>
|
||||||
|
<h4 className={styles.gameOptionHeaderDescription}>
|
||||||
|
{t("downloads_section_description")}
|
||||||
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.gameOptionRow}>
|
<div className={styles.gameOptionRow}>
|
||||||
|
<Button
|
||||||
|
onClick={openRepacksModal}
|
||||||
|
theme="outline"
|
||||||
|
disabled={deleting || isGameDownloading}
|
||||||
|
>
|
||||||
|
{t("open_download_options")}
|
||||||
|
</Button>
|
||||||
|
{game.downloadPath && (
|
||||||
|
<Button
|
||||||
|
onClick={handleOpenDownloadFolder}
|
||||||
|
theme="outline"
|
||||||
|
disabled={deleting}
|
||||||
|
>
|
||||||
|
{t("open_download_location")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={styles.gameOptionHeader}>
|
||||||
|
<h2>{t("danger_zone_section_title")}</h2>
|
||||||
|
<h4 className={styles.gameOptionHeaderDescription}>
|
||||||
|
{t("danger_zone_section_description")}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div className={styles.gameOptionRow}>
|
||||||
|
<Button
|
||||||
|
onClick={() => setShowRemoveGameModal(true)}
|
||||||
|
theme="danger"
|
||||||
|
disabled={deleting}
|
||||||
|
>
|
||||||
|
{t("remove_from_library")}
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowDeleteModal(true);
|
setShowDeleteModal(true);
|
||||||
}}
|
}}
|
||||||
style={{ alignSelf: "flex-end" }}
|
theme="danger"
|
||||||
theme="outline"
|
|
||||||
disabled={isGameDownloading || deleting || !game.downloadPath}
|
disabled={isGameDownloading || deleting || !game.downloadPath}
|
||||||
>
|
>
|
||||||
<TrashIcon />
|
|
||||||
{t("remove_files")}
|
{t("remove_files")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
|
||||||
onClick={() => setShowRemoveGameModal(true)}
|
|
||||||
style={{ alignSelf: "flex-end" }}
|
|
||||||
theme="outline"
|
|
||||||
disabled={deleting}
|
|
||||||
>
|
|
||||||
<NoEntryIcon />
|
|
||||||
{t("remove_from_library")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
Loading…
Reference in New Issue
Block a user