mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-21 18:13:55 +00:00
feat: migration to scss
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
<title>Hydra</title>
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: local: *; media-src 'self' local: data: *;"
|
||||
content="default-src 'self'; script-src *; style-src 'self' 'unsafe-inline'; img-src 'self' data: local: *; media-src 'self' local: data: *; connect-src *; font-src *;"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
import {
|
||||
ComplexStyleRule,
|
||||
createContainer,
|
||||
globalStyle,
|
||||
style,
|
||||
} from "@vanilla-extract/css";
|
||||
import { SPACING_UNIT, vars } from "./theme.css";
|
||||
|
||||
export const appContainer = createContainer();
|
||||
|
||||
globalStyle("*", {
|
||||
boxSizing: "border-box",
|
||||
});
|
||||
|
||||
globalStyle("::-webkit-scrollbar", {
|
||||
width: "9px",
|
||||
backgroundColor: vars.color.darkBackground,
|
||||
});
|
||||
|
||||
globalStyle("::-webkit-scrollbar-track", {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.03)",
|
||||
});
|
||||
|
||||
globalStyle("::-webkit-scrollbar-thumb", {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.08)",
|
||||
borderRadius: "24px",
|
||||
});
|
||||
|
||||
globalStyle("::-webkit-scrollbar-thumb:hover", {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.16)",
|
||||
});
|
||||
|
||||
globalStyle("html, body, #root, main", {
|
||||
height: "100%",
|
||||
});
|
||||
|
||||
globalStyle("body", {
|
||||
overflow: "hidden",
|
||||
userSelect: "none",
|
||||
fontFamily: "Noto Sans, sans-serif",
|
||||
fontSize: vars.size.body,
|
||||
color: vars.color.body,
|
||||
margin: "0",
|
||||
});
|
||||
|
||||
globalStyle("button", {
|
||||
padding: "0",
|
||||
backgroundColor: "transparent",
|
||||
border: "none",
|
||||
fontFamily: "inherit",
|
||||
});
|
||||
|
||||
globalStyle("h1, h2, h3, h4, h5, h6, p", {
|
||||
margin: 0,
|
||||
});
|
||||
|
||||
globalStyle("p", {
|
||||
lineHeight: "20px",
|
||||
});
|
||||
|
||||
globalStyle("#root, main", {
|
||||
display: "flex",
|
||||
});
|
||||
|
||||
globalStyle("#root", {
|
||||
flexDirection: "column",
|
||||
});
|
||||
|
||||
globalStyle("main", {
|
||||
overflow: "hidden",
|
||||
});
|
||||
|
||||
globalStyle(
|
||||
"input::-webkit-outer-spin-button, input::-webkit-inner-spin-button",
|
||||
{
|
||||
WebkitAppearance: "none",
|
||||
margin: "0",
|
||||
}
|
||||
);
|
||||
|
||||
globalStyle("label", {
|
||||
fontSize: vars.size.body,
|
||||
});
|
||||
|
||||
globalStyle("input[type=number]", {
|
||||
MozAppearance: "textfield",
|
||||
});
|
||||
|
||||
globalStyle("img", {
|
||||
WebkitUserDrag: "none",
|
||||
} as Record<string, string>);
|
||||
|
||||
globalStyle("progress[value]", {
|
||||
WebkitAppearance: "none",
|
||||
});
|
||||
|
||||
export const container = style({
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
containerName: appContainer,
|
||||
containerType: "inline-size",
|
||||
});
|
||||
|
||||
export const content = style({
|
||||
overflowY: "auto",
|
||||
alignItems: "center",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
position: "relative",
|
||||
height: "100%",
|
||||
background: `linear-gradient(0deg, ${vars.color.darkBackground} 50%, ${vars.color.background} 100%)`,
|
||||
});
|
||||
|
||||
export const titleBar = style({
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
height: "35px",
|
||||
minHeight: "35px",
|
||||
backgroundColor: vars.color.darkBackground,
|
||||
alignItems: "center",
|
||||
padding: `0 ${SPACING_UNIT * 2}px`,
|
||||
WebkitAppRegion: "drag",
|
||||
zIndex: "4",
|
||||
borderBottom: `1px solid ${vars.color.border}`,
|
||||
} as ComplexStyleRule);
|
||||
@@ -127,4 +127,10 @@ progress[value] {
|
||||
-webkit-app-region: drag;
|
||||
z-index: 4;
|
||||
border-bottom: 1px solid globals.$border-color;
|
||||
|
||||
&__cloud-text {
|
||||
background: linear-gradient(270deg, #16b195 50%, #3e62c0 100%);
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useCallback, useContext, useEffect, useRef } from "react";
|
||||
import { Sidebar, BottomPanel, Header, Toast } from "@renderer/components";
|
||||
|
||||
import "./app.scss";
|
||||
import Intercom from "@intercom/messenger-js-sdk";
|
||||
|
||||
import {
|
||||
useAppDispatch,
|
||||
@@ -13,8 +14,6 @@ import {
|
||||
useUserDetails,
|
||||
} from "@renderer/hooks";
|
||||
|
||||
import * as styles from "./app.css";
|
||||
|
||||
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||
import {
|
||||
setSearch,
|
||||
@@ -36,6 +35,12 @@ export interface AppProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
console.log(import.meta.env);
|
||||
|
||||
Intercom({
|
||||
app_id: import.meta.env.RENDERER_VITE_INTERCOM_APP_ID,
|
||||
});
|
||||
|
||||
export function App() {
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
const { updateLibrary, library } = useLibrary();
|
||||
@@ -56,8 +61,13 @@ export function App() {
|
||||
hideFriendsModal,
|
||||
} = useUserDetails();
|
||||
|
||||
const { userDetails, fetchUserDetails, updateUserDetails, clearUserDetails } =
|
||||
useUserDetails();
|
||||
const {
|
||||
userDetails,
|
||||
hasActiveSubscription,
|
||||
fetchUserDetails,
|
||||
updateUserDetails,
|
||||
clearUserDetails,
|
||||
} = useUserDetails();
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
@@ -206,7 +216,9 @@ export function App() {
|
||||
|
||||
useEffect(() => {
|
||||
new MutationObserver(() => {
|
||||
const modal = document.body.querySelector("[role=dialog]");
|
||||
const modal = document.body.querySelector(
|
||||
"[role=dialog]:not([data-intercom-frame='true'])"
|
||||
);
|
||||
|
||||
dispatch(toggleDraggingDisabled(Boolean(modal)));
|
||||
}).observe(document.body, {
|
||||
@@ -271,8 +283,13 @@ export function App() {
|
||||
return (
|
||||
<>
|
||||
{window.electron.platform === "win32" && (
|
||||
<div className={styles.titleBar}>
|
||||
<h4>Hydra</h4>
|
||||
<div className="title-bar">
|
||||
<h4>
|
||||
Hydra
|
||||
{hasActiveSubscription && (
|
||||
<span className="title-bar__cloud-text"> Cloud</span>
|
||||
)}
|
||||
</h4>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -295,14 +312,14 @@ export function App() {
|
||||
<main>
|
||||
<Sidebar />
|
||||
|
||||
<article className={styles.container}>
|
||||
<article className="container">
|
||||
<Header
|
||||
onSearch={handleSearch}
|
||||
search={search}
|
||||
onClear={handleClear}
|
||||
/>
|
||||
|
||||
<section ref={contentRef} className={styles.content}>
|
||||
<section ref={contentRef} className="container__content">
|
||||
<Outlet />
|
||||
</section>
|
||||
</article>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
color: globals.$muted-color;
|
||||
font-size: 10px;
|
||||
padding: calc(globals.$spacing-unit / 2) globals.$spacing-unit;
|
||||
border: solid 1px globals.$border-color;
|
||||
border: solid 1px globals.$muted-color;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
padding: calc(globals.$spacing-unit * 2) calc(globals.$spacing-unit * 3);
|
||||
color: globals.$muted-color;
|
||||
border-bottom: solid 1px globals.$border-color;
|
||||
background-color: globals.$dark-background-color;
|
||||
|
||||
&--dragging-disabled {
|
||||
-webkit-app-region: no-drag;
|
||||
@@ -20,7 +21,7 @@
|
||||
}
|
||||
|
||||
&__search {
|
||||
background-color: globals.$dark-background-color;
|
||||
background-color: globals.$background-color;
|
||||
display: inline-flex;
|
||||
transition: all ease 0.2s;
|
||||
width: 200px;
|
||||
|
||||
@@ -107,4 +107,30 @@
|
||||
flex-direction: column;
|
||||
padding-bottom: globals.$spacing-unit;
|
||||
}
|
||||
|
||||
&__help-button {
|
||||
color: globals.$muted-color;
|
||||
padding: globals.$spacing-unit calc(globals.$spacing-unit * 2);
|
||||
gap: 9px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
border-top: solid 1px globals.$border-color;
|
||||
transition: background-color ease 0.1s;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
&__help-button-icon {
|
||||
background: linear-gradient(0deg, #16b195 50%, #3e62c0 100%);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,12 @@ import { useLocation, useNavigate } from "react-router-dom";
|
||||
import type { LibraryGame } from "@types";
|
||||
|
||||
import { TextField } from "@renderer/components";
|
||||
import { useDownload, useLibrary, useToast } from "@renderer/hooks";
|
||||
import {
|
||||
useDownload,
|
||||
useLibrary,
|
||||
useToast,
|
||||
useUserDetails,
|
||||
} from "@renderer/hooks";
|
||||
|
||||
import { routes } from "./routes";
|
||||
|
||||
@@ -17,6 +22,9 @@ import SteamLogo from "@renderer/assets/steam-logo.svg?react";
|
||||
import { SidebarProfile } from "./sidebar-profile";
|
||||
import { sortBy } from "lodash-es";
|
||||
import cn from "classnames";
|
||||
import { CommentDiscussionIcon } from "@primer/octicons-react";
|
||||
|
||||
import { show, update } from "@intercom/messenger-js-sdk";
|
||||
|
||||
const SIDEBAR_MIN_WIDTH = 200;
|
||||
const SIDEBAR_INITIAL_WIDTH = 250;
|
||||
@@ -44,6 +52,20 @@ export function Sidebar() {
|
||||
return sortBy(library, (game) => game.title);
|
||||
}, [library]);
|
||||
|
||||
const { userDetails, hasActiveSubscription } = useUserDetails();
|
||||
|
||||
useEffect(() => {
|
||||
if (userDetails) {
|
||||
update({
|
||||
name: userDetails.displayName,
|
||||
Username: userDetails.username,
|
||||
Email: userDetails.email,
|
||||
"Subscription expiration date": userDetails?.subscription?.expiresAt,
|
||||
"Payment status": userDetails?.subscription?.status,
|
||||
});
|
||||
}
|
||||
}, [userDetails, hasActiveSubscription]);
|
||||
|
||||
const { lastPacket, progress } = useDownload();
|
||||
|
||||
const { showWarningToast } = useToast();
|
||||
@@ -168,77 +190,91 @@ export function Sidebar() {
|
||||
maxWidth: sidebarWidth,
|
||||
}}
|
||||
>
|
||||
<SidebarProfile />
|
||||
<div
|
||||
style={{ display: "flex", flexDirection: "column", overflow: "hidden" }}
|
||||
>
|
||||
<SidebarProfile />
|
||||
|
||||
<div className="sidebar__content">
|
||||
<section className="sidebar__section">
|
||||
<ul className="sidebar__menu">
|
||||
{routes.map(({ nameKey, path, render }) => (
|
||||
<li
|
||||
key={nameKey}
|
||||
className={cn("sidebar__menu-item", {
|
||||
"sidebar__menu-item--active": location.pathname === path,
|
||||
})}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="sidebar__menu-item-button"
|
||||
onClick={() => handleSidebarItemClick(path)}
|
||||
<div className="sidebar__content">
|
||||
<section className="sidebar__section">
|
||||
<ul className="sidebar__menu">
|
||||
{routes.map(({ nameKey, path, render }) => (
|
||||
<li
|
||||
key={nameKey}
|
||||
className={cn("sidebar__menu-item", {
|
||||
"sidebar__menu-item--active": location.pathname === path,
|
||||
})}
|
||||
>
|
||||
{render()}
|
||||
<span>{t(nameKey)}</span>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
<button
|
||||
type="button"
|
||||
className="sidebar__menu-item-button"
|
||||
onClick={() => handleSidebarItemClick(path)}
|
||||
>
|
||||
{render()}
|
||||
<span>{t(nameKey)}</span>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className="sidebar__section">
|
||||
<small className="sidebar__section-title">{t("my_library")}</small>
|
||||
<section className="sidebar__section">
|
||||
<small className="sidebar__section-title">{t("my_library")}</small>
|
||||
|
||||
<TextField
|
||||
ref={filterRef}
|
||||
placeholder={t("filter")}
|
||||
onChange={handleFilter}
|
||||
theme="dark"
|
||||
/>
|
||||
<TextField
|
||||
ref={filterRef}
|
||||
placeholder={t("filter")}
|
||||
onChange={handleFilter}
|
||||
theme="dark"
|
||||
/>
|
||||
|
||||
<ul className="sidebar__menu">
|
||||
{filteredLibrary.map((game) => (
|
||||
<li
|
||||
key={game.id}
|
||||
className={cn("sidebar__menu-item", {
|
||||
"sidebar__menu-item--active":
|
||||
location.pathname === `/game/${game.shop}/${game.objectID}`,
|
||||
"sidebar__menu-item--muted": game.status === "removed",
|
||||
})}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="sidebar__menu-item-button"
|
||||
onClick={(event) => handleSidebarGameClick(event, game)}
|
||||
<ul className="sidebar__menu">
|
||||
{filteredLibrary.map((game) => (
|
||||
<li
|
||||
key={game.id}
|
||||
className={cn("sidebar__menu-item", {
|
||||
"sidebar__menu-item--active":
|
||||
location.pathname ===
|
||||
`/game/${game.shop}/${game.objectID}`,
|
||||
"sidebar__menu-item--muted": game.status === "removed",
|
||||
})}
|
||||
>
|
||||
{game.iconUrl ? (
|
||||
<img
|
||||
className="sidebar__game-icon"
|
||||
src={game.iconUrl}
|
||||
alt={game.title}
|
||||
loading="lazy"
|
||||
/>
|
||||
) : (
|
||||
<SteamLogo className="sidebar__game-icon" />
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className="sidebar__menu-item-button"
|
||||
onClick={(event) => handleSidebarGameClick(event, game)}
|
||||
>
|
||||
{game.iconUrl ? (
|
||||
<img
|
||||
className="sidebar__game-icon"
|
||||
src={game.iconUrl}
|
||||
alt={game.title}
|
||||
loading="lazy"
|
||||
/>
|
||||
) : (
|
||||
<SteamLogo className="sidebar__game-icon" />
|
||||
)}
|
||||
|
||||
<span className="sidebar__menu-item-button-label">
|
||||
{getGameTitle(game)}
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
<span className="sidebar__menu-item-button-label">
|
||||
{getGameTitle(game)}
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{hasActiveSubscription && (
|
||||
<button type="button" className="sidebar__help-button" onClick={show}>
|
||||
<div className="sidebar__help-button-icon">
|
||||
<CommentDiscussionIcon size={14} />
|
||||
</div>
|
||||
<span>{t("need_help")}</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="sidebar__handle"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@use "../../scss/globals.scss";
|
||||
|
||||
$toast-height: 80;
|
||||
$toast-height: 80px;
|
||||
|
||||
.toast {
|
||||
animation-duration: 0.2s;
|
||||
@@ -12,7 +12,7 @@ $toast-height: 80;
|
||||
border: solid 1px globals.$border-color;
|
||||
right: calc(globals.$spacing-unit * 2);
|
||||
//bottom panel height + 16px
|
||||
bottom: calc(26 + #{globals.$spacing-unit * 2});
|
||||
bottom: calc(26px + #{globals.$spacing-unit * 2});
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -24,7 +24,7 @@ $toast-height: 80;
|
||||
|
||||
&--closing {
|
||||
animation-name: slide-out;
|
||||
transform: translateY(calc($toast-height #{globals.$spacing-unit * 2}));
|
||||
transform: translateY(calc($toast-height + #{globals.$spacing-unit * 2}));
|
||||
}
|
||||
|
||||
&__content {
|
||||
@@ -38,10 +38,10 @@ $toast-height: 80;
|
||||
&__progress {
|
||||
width: 100%;
|
||||
height: 5px;
|
||||
::-webkit-progress-bar {
|
||||
&::-webkit-progress-bar {
|
||||
background-color: globals.$dark-background-color;
|
||||
}
|
||||
::-webkit-progress-value {
|
||||
&::-webkit-progress-value {
|
||||
background-color: globals.$muted-color;
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ $toast-height: 80;
|
||||
|
||||
@keyframes slide-in {
|
||||
0% {
|
||||
transform: translateY(calc($toast-height #{globals.$spacing-unit * 2}));
|
||||
transform: translateY(calc($toast-height + #{globals.$spacing-unit * 2}));
|
||||
}
|
||||
|
||||
100% {
|
||||
@@ -76,12 +76,12 @@ $toast-height: 80;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide-in {
|
||||
@keyframes slide-out {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(calc($toast-height #{globals.$spacing-unit * 2}));
|
||||
transform: translateY(calc($toast-height + #{globals.$spacing-unit * 2}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +147,8 @@ export function GameDetailsContextProvider({
|
||||
if (
|
||||
result?.content_descriptors.ids.includes(
|
||||
SteamContentDescriptor.AdultOnlySexualContent
|
||||
)
|
||||
) &&
|
||||
!userPreferences?.disableNsfwAlert
|
||||
) {
|
||||
setHasNSFWContentBlocked(true);
|
||||
}
|
||||
|
||||
@@ -10,12 +10,22 @@ export interface HowLongToBeatEntry {
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface CatalogueCache {
|
||||
id?: number;
|
||||
category: string;
|
||||
games: { objectId: string; shop: GameShop }[];
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
expiresAt: Date;
|
||||
}
|
||||
|
||||
export const db = new Dexie("Hydra");
|
||||
|
||||
db.version(4).stores({
|
||||
db.version(5).stores({
|
||||
repacks: `++id, title, uris, fileSize, uploadDate, downloadSourceId, repacker, createdAt, updatedAt`,
|
||||
downloadSources: `++id, url, name, etag, downloadCount, status, createdAt, updatedAt`,
|
||||
howLongToBeatEntries: `++id, categories, [shop+objectId], createdAt, updatedAt`,
|
||||
catalogueCache: `++id, category, games, createdAt, updatedAt, expiresAt`,
|
||||
});
|
||||
|
||||
export const downloadSourcesTable = db.table("downloadSources");
|
||||
@@ -24,4 +34,6 @@ export const howLongToBeatEntriesTable = db.table<HowLongToBeatEntry>(
|
||||
"howLongToBeatEntries"
|
||||
);
|
||||
|
||||
export const catalogueCacheTable = db.table<CatalogueCache>("catalogueCache");
|
||||
|
||||
db.open();
|
||||
|
||||
@@ -15,6 +15,14 @@ import * as styles from "./home.css";
|
||||
import { SPACING_UNIT, vars } from "@renderer/theme.css";
|
||||
import { buildGameDetailsPath } from "@renderer/helpers";
|
||||
import { CatalogueCategory } from "@shared";
|
||||
import { catalogueCacheTable, db } from "@renderer/dexie";
|
||||
import { add } from "date-fns";
|
||||
|
||||
const categoryCacheDurationInSeconds = {
|
||||
[CatalogueCategory.Hot]: 60 * 60 * 2,
|
||||
[CatalogueCategory.Weekly]: 60 * 60 * 24,
|
||||
[CatalogueCategory.Achievements]: 60 * 60 * 24,
|
||||
};
|
||||
|
||||
export default function Home() {
|
||||
const { t } = useTranslation("home");
|
||||
@@ -36,19 +44,43 @@ export default function Home() {
|
||||
[CatalogueCategory.Achievements]: [],
|
||||
});
|
||||
|
||||
const getCatalogue = useCallback((category: CatalogueCategory) => {
|
||||
setCurrentCatalogueCategory(category);
|
||||
setIsLoading(true);
|
||||
const getCatalogue = useCallback(async (category: CatalogueCategory) => {
|
||||
try {
|
||||
const catalogueCache = await catalogueCacheTable
|
||||
.where("expiresAt")
|
||||
.above(new Date())
|
||||
.and((cache) => cache.category === category)
|
||||
.first();
|
||||
|
||||
window.electron
|
||||
.getCatalogue(category)
|
||||
.then((catalogue) => {
|
||||
setCatalogue((prev) => ({ ...prev, [category]: catalogue }));
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
setCurrentCatalogueCategory(category);
|
||||
setIsLoading(true);
|
||||
|
||||
if (catalogueCache)
|
||||
return setCatalogue((prev) => ({
|
||||
...prev,
|
||||
[category]: catalogueCache.games,
|
||||
}));
|
||||
|
||||
const catalogue = await window.electron.getCatalogue(category);
|
||||
|
||||
db.transaction("rw", catalogueCacheTable, async () => {
|
||||
await catalogueCacheTable.where("category").equals(category).delete();
|
||||
|
||||
await catalogueCacheTable.add({
|
||||
category,
|
||||
games: catalogue,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
expiresAt: add(new Date(), {
|
||||
seconds: categoryCacheDurationInSeconds[category],
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
setCatalogue((prev) => ({ ...prev, [category]: catalogue }));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const getRandomGame = useCallback(() => {
|
||||
|
||||
@@ -18,6 +18,7 @@ export function SettingsBehavior() {
|
||||
preferQuitInsteadOfHiding: false,
|
||||
runAtStartup: false,
|
||||
startMinimized: false,
|
||||
disableNsfwAlert: false,
|
||||
});
|
||||
|
||||
const { t } = useTranslation("settings");
|
||||
@@ -28,6 +29,7 @@ export function SettingsBehavior() {
|
||||
preferQuitInsteadOfHiding: userPreferences.preferQuitInsteadOfHiding,
|
||||
runAtStartup: userPreferences.runAtStartup,
|
||||
startMinimized: userPreferences.startMinimized,
|
||||
disableNsfwAlert: userPreferences.disableNsfwAlert,
|
||||
});
|
||||
}
|
||||
}, [userPreferences]);
|
||||
@@ -86,6 +88,14 @@ export function SettingsBehavior() {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<CheckboxField
|
||||
label={t("disable_nsfw_alert")}
|
||||
checked={form.disableNsfwAlert}
|
||||
onChange={() =>
|
||||
handleChange({ disableNsfwAlert: !form.disableNsfwAlert })
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
8
src/renderer/src/vite-env.d.ts
vendored
8
src/renderer/src/vite-env.d.ts
vendored
@@ -1,2 +1,10 @@
|
||||
/// <reference types="vite/client" />
|
||||
/// <reference types="vite-plugin-svgr/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly RENDERER_VITE_INTERCOM_APP_ID: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user