mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-03-09 15:40:26 +00:00
feat: adding toast component
This commit is contained in:
parent
da607fe741
commit
0162ebd133
13 changed files with 136 additions and 82 deletions
|
@ -79,6 +79,10 @@ globalStyle("img", {
|
|||
WebkitUserDrag: "none",
|
||||
} as Record<string, string>);
|
||||
|
||||
globalStyle("progress[value]", {
|
||||
WebkitAppearance: "none",
|
||||
});
|
||||
|
||||
export const container = style({
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
|
|
|
@ -2,14 +2,14 @@ import { keyframes, style } from "@vanilla-extract/css";
|
|||
import { recipe } from "@vanilla-extract/recipes";
|
||||
import { SPACING_UNIT, vars } from "../../theme.css";
|
||||
|
||||
export const modalSlideIn = keyframes({
|
||||
export const fadeIn = keyframes({
|
||||
"0%": { opacity: 0 },
|
||||
"100%": {
|
||||
opacity: 1,
|
||||
},
|
||||
});
|
||||
|
||||
export const modalSlideOut = keyframes({
|
||||
export const fadeOut = keyframes({
|
||||
"0%": { opacity: 1 },
|
||||
"100%": {
|
||||
opacity: 0,
|
||||
|
@ -18,7 +18,7 @@ export const modalSlideOut = keyframes({
|
|||
|
||||
export const modal = recipe({
|
||||
base: {
|
||||
animationName: modalSlideIn,
|
||||
animationName: fadeIn,
|
||||
animationDuration: "0.3s",
|
||||
backgroundColor: vars.color.background,
|
||||
borderRadius: "5px",
|
||||
|
@ -33,7 +33,7 @@ export const modal = recipe({
|
|||
variants: {
|
||||
closing: {
|
||||
true: {
|
||||
animationName: modalSlideOut,
|
||||
animationName: fadeOut,
|
||||
opacity: 0,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -27,6 +27,7 @@ export const content = recipe({
|
|||
display: "flex",
|
||||
flexDirection: "column",
|
||||
padding: `${SPACING_UNIT * 2}px`,
|
||||
gap: `${SPACING_UNIT * 2}px`,
|
||||
paddingBottom: "0",
|
||||
width: "100%",
|
||||
overflow: "auto",
|
||||
|
@ -118,7 +119,6 @@ export const sectionTitle = style({
|
|||
});
|
||||
|
||||
export const section = style({
|
||||
padding: `${SPACING_UNIT * 2}px 0`,
|
||||
gap: `${SPACING_UNIT * 2}px`,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
|
|
|
@ -29,7 +29,6 @@ export const downloaderName = style({
|
|||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
alignSelf: "flex-start",
|
||||
});
|
||||
|
||||
export const downloads = style({
|
||||
|
@ -46,9 +45,34 @@ export const downloadCover = style({
|
|||
width: "280px",
|
||||
minWidth: "280px",
|
||||
height: "auto",
|
||||
objectFit: "cover",
|
||||
objectPosition: "center",
|
||||
borderRight: `solid 1px ${vars.color.border}`,
|
||||
position: "relative",
|
||||
zIndex: "1",
|
||||
});
|
||||
|
||||
export const downloadCoverContent = style({
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
padding: `${SPACING_UNIT}px`,
|
||||
display: "flex",
|
||||
alignItems: "flex-end",
|
||||
justifyContent: "flex-end",
|
||||
});
|
||||
|
||||
export const downloadCoverBackdrop = style({
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
background: "linear-gradient(0deg, rgba(0, 0, 0, 0.8) 5%, transparent 100%)",
|
||||
display: "flex",
|
||||
overflow: "hidden",
|
||||
zIndex: "1",
|
||||
});
|
||||
|
||||
export const downloadCoverImage = style({
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
position: "absolute",
|
||||
zIndex: "-1",
|
||||
});
|
||||
|
||||
export const download = recipe({
|
||||
|
|
|
@ -2,7 +2,11 @@ import { useTranslation } from "react-i18next";
|
|||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { Button, TextField } from "@renderer/components";
|
||||
import { formatDownloadProgress, steamUrlBuilder } from "@renderer/helpers";
|
||||
import {
|
||||
buildGameDetailsPath,
|
||||
formatDownloadProgress,
|
||||
steamUrlBuilder,
|
||||
} from "@renderer/helpers";
|
||||
import { useDownload, useLibrary } from "@renderer/hooks";
|
||||
import type { Game } from "@types";
|
||||
|
||||
|
@ -86,9 +90,9 @@ export function Downloads() {
|
|||
</p>
|
||||
|
||||
{game.downloader === Downloader.Torrent && (
|
||||
<p>
|
||||
<small>
|
||||
{lastPacket?.numPeers} peers / {lastPacket?.numSeeds} seeds
|
||||
</p>
|
||||
</small>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
@ -102,7 +106,6 @@ export function Downloads() {
|
|||
</>
|
||||
);
|
||||
}
|
||||
if (game?.status === "removed") return <p>{t("cancelled")}</p>;
|
||||
|
||||
if (game?.status === "paused") {
|
||||
return (
|
||||
|
@ -113,7 +116,7 @@ export function Downloads() {
|
|||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
return <p>{t(game?.status)}</p>;
|
||||
};
|
||||
|
||||
const openDeleteModal = (gameId: number) => {
|
||||
|
@ -173,7 +176,7 @@ export function Downloads() {
|
|||
return (
|
||||
<>
|
||||
<Button
|
||||
onClick={() => navigate(`/game/${game.shop}/${game.objectID}`)}
|
||||
onClick={() => navigate(buildGameDetailsPath(game))}
|
||||
theme="outline"
|
||||
disabled={deleting}
|
||||
>
|
||||
|
@ -230,11 +233,21 @@ export function Downloads() {
|
|||
cancelled: game.status === "removed",
|
||||
})}
|
||||
>
|
||||
<img
|
||||
src={steamUrlBuilder.library(game.objectID)}
|
||||
className={styles.downloadCover}
|
||||
alt={game.title}
|
||||
/>
|
||||
<div className={styles.downloadCover}>
|
||||
<div className={styles.downloadCoverBackdrop}>
|
||||
<img
|
||||
src={steamUrlBuilder.library(game.objectID)}
|
||||
className={styles.downloadCoverImage}
|
||||
alt={game.title}
|
||||
/>
|
||||
|
||||
<div className={styles.downloadCoverContent}>
|
||||
<small className={styles.downloaderName}>
|
||||
{downloaderName[game?.downloader]}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.downloadRightContent}>
|
||||
<div className={styles.downloadDetails}>
|
||||
<div className={styles.downloadTitleWrapper}>
|
||||
|
@ -249,9 +262,6 @@ export function Downloads() {
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<small className={styles.downloaderName}>
|
||||
{downloaderName[game?.downloader]}
|
||||
</small>
|
||||
{getGameInfo(game)}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import { globalStyle, keyframes, style } from "@vanilla-extract/css";
|
|||
import { SPACING_UNIT, vars } from "../../theme.css";
|
||||
|
||||
export const slideIn = keyframes({
|
||||
"0%": { transform: `translateY(${40 + 16}px)` },
|
||||
"0%": { transform: `translateY(${40 + SPACING_UNIT * 2}px)` },
|
||||
"100%": { transform: "translateY(0)" },
|
||||
});
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { Button } from "@renderer/components";
|
||||
|
||||
import * as styles from "./settings.css";
|
||||
|
@ -7,6 +7,7 @@ import { UserPreferences } from "@types";
|
|||
import { SettingsRealDebrid } from "./settings-real-debrid";
|
||||
import { SettingsGeneral } from "./settings-general";
|
||||
import { SettingsBehavior } from "./settings-behavior";
|
||||
import { Toast } from "@renderer/components/toast/toast";
|
||||
|
||||
const categories = ["general", "behavior", "real_debrid"];
|
||||
|
||||
|
@ -14,6 +15,7 @@ export function Settings() {
|
|||
const [currentCategory, setCurrentCategory] = useState(categories.at(0)!);
|
||||
const [userPreferences, setUserPreferences] =
|
||||
useState<UserPreferences | null>(null);
|
||||
const [isToastVisible, setIsToastVisible] = useState(false);
|
||||
|
||||
const { t } = useTranslation("settings");
|
||||
|
||||
|
@ -23,8 +25,14 @@ export function Settings() {
|
|||
});
|
||||
}, []);
|
||||
|
||||
const handleUpdateUserPreferences = (values: Partial<UserPreferences>) => {
|
||||
window.electron.updateUserPreferences(values);
|
||||
const handleUpdateUserPreferences = async (
|
||||
values: Partial<UserPreferences>
|
||||
) => {
|
||||
await window.electron.updateUserPreferences(values);
|
||||
window.electron.getUserPreferences().then((userPreferences) => {
|
||||
setUserPreferences(userPreferences);
|
||||
setIsToastVisible(true);
|
||||
});
|
||||
};
|
||||
|
||||
const renderCategory = () => {
|
||||
|
@ -54,24 +62,37 @@ export function Settings() {
|
|||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<section className={styles.container}>
|
||||
<div className={styles.content}>
|
||||
<section className={styles.settingsCategories}>
|
||||
{categories.map((category) => (
|
||||
<Button
|
||||
key={category}
|
||||
theme={currentCategory === category ? "primary" : "outline"}
|
||||
onClick={() => setCurrentCategory(category)}
|
||||
>
|
||||
{t(category)}
|
||||
</Button>
|
||||
))}
|
||||
</section>
|
||||
const handleToastClose = useCallback(() => {
|
||||
setIsToastVisible(false);
|
||||
}, []);
|
||||
|
||||
<h2>{t(currentCategory)}</h2>
|
||||
{renderCategory()}
|
||||
</div>
|
||||
</section>
|
||||
return (
|
||||
<>
|
||||
<section className={styles.container}>
|
||||
<div className={styles.content}>
|
||||
<section className={styles.settingsCategories}>
|
||||
{categories.map((category) => (
|
||||
<Button
|
||||
key={category}
|
||||
theme={currentCategory === category ? "primary" : "outline"}
|
||||
onClick={() => setCurrentCategory(category)}
|
||||
>
|
||||
{t(category)}
|
||||
</Button>
|
||||
))}
|
||||
</section>
|
||||
|
||||
<h2>{t(currentCategory)}</h2>
|
||||
{renderCategory()}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Toast
|
||||
message="Settings have been saved"
|
||||
visible={isToastVisible}
|
||||
onClose={handleToastClose}
|
||||
type="success"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue