Compare commits

...

10 Commits

Author SHA1 Message Date
Moyasee
92d87c5d33 refactor: remove unnecessary useEffect in LibraryGameCard 2025-12-31 01:59:25 +02:00
Moyasee
af884d3772 refactor: simplify cover image assignment in LibraryGameCard 2025-12-30 14:09:04 +02:00
Moyasee
dc31ac0831 fix: library cards not using placeholder and icon as a game cover 2025-12-30 00:25:45 +02:00
Chubby Granny Chaser
9769eecec6 Merge pull request #1907 from hydralauncher/fix/lint-buzz
Some checks failed
Build / build (ubuntu-latest) (push) Has been cancelled
Build / build (windows-2022) (push) Has been cancelled
refactor: improve code formatting and consistency in DownloadManager
2025-12-27 00:56:29 +00:00
Moyasee
91adb97013 refactor: improve code formatting and consistency in DownloadManager 2025-12-27 02:50:27 +02:00
Chubby Granny Chaser
f138b2efcb Merge pull request #1906 from Wkeynhk/main
Buzzheavier fix
2025-12-27 00:47:44 +00:00
Wkeynhk
991aa05760 Sonar fix2 2025-12-27 03:23:36 +03:00
Wkeynhk
aff9e13bca Sonar fix 2025-12-27 03:17:42 +03:00
Wkeynhk
240a75c1d0 Buzzheavier fix 2025-12-27 03:02:35 +03:00
Chubby Granny Chaser
edbe86a1fb Merge pull request #1904 from hydralauncher/feat/LBX-155
Some checks failed
Build / build (ubuntu-latest) (push) Has been cancelled
Build / build (windows-2022) (push) Has been cancelled
fix: notification item styling
2025-12-26 22:54:13 +00:00
3 changed files with 66 additions and 24 deletions

View File

@@ -35,18 +35,27 @@ export class DownloadManager {
): string | undefined {
if (originalUrl?.includes("#")) {
const hashPart = originalUrl.split("#")[1];
if (hashPart && !hashPart.startsWith("http")) return hashPart;
if (hashPart && !hashPart.startsWith("http") && hashPart.includes(".")) {
return hashPart;
}
}
if (url.includes("#")) {
const hashPart = url.split("#")[1];
if (hashPart && !hashPart.startsWith("http")) return hashPart;
if (hashPart && !hashPart.startsWith("http") && hashPart.includes(".")) {
return hashPart;
}
}
try {
const urlObj = new URL(url);
const filename = urlObj.pathname.split("/").pop();
if (filename?.length) return filename;
const pathname = urlObj.pathname;
const pathParts = pathname.split("/");
const filename = pathParts[pathParts.length - 1];
if (filename?.includes(".") && filename.length > 0) {
return decodeURIComponent(filename);
}
} catch {
// Invalid URL
}
@@ -55,7 +64,7 @@ export class DownloadManager {
}
private static sanitizeFilename(filename: string): string {
return filename.replace(/[<>:"/\\|?*]/g, "_");
return filename.replaceAll(/[<>:"/\\|?*]/g, "_");
}
private static createDownloadPayload(
@@ -64,13 +73,19 @@ export class DownloadManager {
downloadId: string,
savePath: string
) {
const filename = this.extractFilename(directUrl, originalUrl);
const filename =
this.extractFilename(originalUrl, directUrl) ||
this.extractFilename(directUrl);
const sanitizedFilename = filename
? this.sanitizeFilename(filename)
: undefined;
if (sanitizedFilename) {
logger.log(`[DownloadManager] Using filename: ${sanitizedFilename}`);
} else {
logger.log(
`[DownloadManager] No filename extracted, aria2 will use default`
);
}
return {
@@ -227,10 +242,10 @@ export class DownloadManager {
)
) {
gameFilesManager.extractDownloadedFile();
} else {
} else if (download.folderName) {
gameFilesManager
.extractFilesInDirectory(
path.join(download.downloadPath, download.folderName!)
path.join(download.downloadPath, download.folderName)
)
.then(() => gameFilesManager.setExtractionComplete());
}

View File

@@ -221,6 +221,26 @@
left: 0;
z-index: 0;
}
&__cover-placeholder {
position: relative;
width: 100%;
height: 100%;
min-width: 100%;
min-height: 100%;
background: linear-gradient(
90deg,
rgba(255, 255, 255, 0.08) 0%,
rgba(255, 255, 255, 0.04) 50%,
rgba(255, 255, 255, 0.08) 100%
);
border-radius: 4px;
color: rgba(255, 255, 255, 0.3);
display: flex;
align-items: center;
justify-content: center;
z-index: 0;
}
}
@keyframes pulse {

View File

@@ -1,7 +1,12 @@
import { LibraryGame } from "@types";
import { useGameCard } from "@renderer/hooks";
import { memo } from "react";
import { ClockIcon, AlertFillIcon, TrophyIcon } from "@primer/octicons-react";
import { memo, useState } from "react";
import {
ClockIcon,
AlertFillIcon,
TrophyIcon,
ImageIcon,
} from "@primer/octicons-react";
import "./library-game-card.scss";
interface LibraryGameCardProps {
@@ -25,14 +30,9 @@ export const LibraryGameCard = memo(function LibraryGameCard({
const { formatPlayTime, handleCardClick, handleContextMenuClick } =
useGameCard(game, onContextMenu);
const coverImage = (
game.customIconUrl ??
game.coverImageUrl ??
game.libraryImageUrl ??
game.libraryHeroImageUrl ??
game.iconUrl ??
""
).replaceAll("\\", "/");
const coverImage = game.coverImageUrl?.replaceAll("\\", "/") ?? "";
const [imageError, setImageError] = useState(false);
return (
<button
@@ -98,12 +98,19 @@ export const LibraryGameCard = memo(function LibraryGameCard({
)}
</div>
<img
src={coverImage ?? undefined}
alt={game.title}
className="library-game-card__game-image"
loading="lazy"
/>
{imageError || !coverImage ? (
<div className="library-game-card__cover-placeholder">
<ImageIcon size={48} />
</div>
) : (
<img
src={coverImage}
alt={game.title}
className="library-game-card__game-image"
loading="lazy"
onError={() => setImageError(true)}
/>
)}
</button>
);
});