From 153b954e78f3a979cebc4243d7dc488960ad17fb Mon Sep 17 00:00:00 2001 From: Moyasee Date: Sun, 30 Nov 2025 07:05:19 +0200 Subject: [PATCH] fix: progress bar, context menu, repacks modal, responsiveness and styling fix --- .../dropdown-menu/dropdown-menu.tsx | 3 + .../src/pages/downloads/download-group.scss | 151 ++++++++++++----- .../src/pages/downloads/download-group.tsx | 156 ++++++++---------- .../game-details/modals/repacks-modal.tsx | 14 +- 4 files changed, 187 insertions(+), 137 deletions(-) diff --git a/src/renderer/src/components/dropdown-menu/dropdown-menu.tsx b/src/renderer/src/components/dropdown-menu/dropdown-menu.tsx index 9e3a1dec..a0a5d43e 100644 --- a/src/renderer/src/components/dropdown-menu/dropdown-menu.tsx +++ b/src/renderer/src/components/dropdown-menu/dropdown-menu.tsx @@ -18,6 +18,7 @@ interface DropdownMenuProps { side?: "top" | "bottom" | "left" | "right"; align?: "start" | "center" | "end"; alignOffset?: number; + collisionPadding?: number; } export function DropdownMenu({ @@ -29,6 +30,7 @@ export function DropdownMenu({ loop = true, align = "center", alignOffset = 0, + collisionPadding = 16, }: Readonly) { return ( @@ -43,6 +45,7 @@ export function DropdownMenu({ loop={loop} align={align} alignOffset={alignOffset} + collisionPadding={collisionPadding} className="dropdown-menu__content" > {title && ( diff --git a/src/renderer/src/pages/downloads/download-group.scss b/src/renderer/src/pages/downloads/download-group.scss index 3dfea93d..e5fa6009 100644 --- a/src/renderer/src/pages/downloads/download-group.scss +++ b/src/renderer/src/pages/downloads/download-group.scss @@ -28,7 +28,15 @@ } &-count { - font-weight: 400; + background-color: rgba(255, 255, 255, 0.1); + color: rgba(255, 255, 255, 0.7); + padding: 6px 10px; + border-radius: 6px; + font-size: 12px; + font-weight: 600; + min-width: 24px; + text-align: center; + flex-shrink: 0; } } &--hero { @@ -37,7 +45,7 @@ overflow: hidden; margin: 0; padding: 0; - padding-bottom: calc(globals.$spacing-unit * 3); + padding-bottom: globals.$spacing-unit; } &__hero-background { @@ -80,34 +88,59 @@ gap: calc(globals.$spacing-unit * 2); } - &__hero-header { - display: flex; - justify-content: flex-end; - margin-bottom: calc(globals.$spacing-unit * 2); - } - &__hero-logo { flex: 1; + min-width: 0; img { - max-width: 600px; - max-height: 200px; + max-width: 180px; + max-height: 60px; object-fit: contain; + + @container #{globals.$app-container} (min-width: 700px) { + max-width: 220px; + max-height: 75px; + } + + @container #{globals.$app-container} (min-width: 900px) { + max-width: 280px; + max-height: 95px; + } + + @container #{globals.$app-container} (min-width: 1200px) { + max-width: 340px; + max-height: 115px; + } + + @container #{globals.$app-container} (min-width: 1500px) { + max-width: 400px; + max-height: 130px; + } } h1 { - font-size: 64px; + font-size: 20px; font-weight: 700; color: #ffffff; text-shadow: 2px 2px 12px rgba(0, 0, 0, 0.9); margin: 0; - } - } - &__hero-actions { - display: flex; - gap: calc(globals.$spacing-unit); - align-items: center; + @container #{globals.$app-container} (min-width: 700px) { + font-size: 26px; + } + + @container #{globals.$app-container} (min-width: 900px) { + font-size: 32px; + } + + @container #{globals.$app-container} (min-width: 1200px) { + font-size: 38px; + } + + @container #{globals.$app-container} (min-width: 1500px) { + font-size: 44px; + } + } } &__hero-action-row { @@ -115,30 +148,60 @@ justify-content: space-between; align-items: flex-end; gap: calc(globals.$spacing-unit * 3); - margin-bottom: calc(globals.$spacing-unit * 3); + margin-top: calc(globals.$spacing-unit * 4); + margin-bottom: calc(globals.$spacing-unit * 2); } - &__hero-menu-btn { - background-color: rgba(0, 0, 0, 0.4); - padding: calc(globals.$spacing-unit * 1); - min-height: unset; + &__hero-buttons { + display: flex; + gap: calc(globals.$spacing-unit); + align-items: center; + flex-shrink: 0; } - &__hero-menu-btn:hover { - background-color: rgba(0, 0, 0, 0.8); + + &__glass-btn { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 16px; + border-radius: 8px; + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(12px); + border: 1px solid rgba(255, 255, 255, 0.2); + box-shadow: + 0 10px 15px -3px rgba(0, 0, 0, 0.1), + 0 4px 6px -4px rgba(0, 0, 0, 0.1); + color: #fff; + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: background-color 0.2s ease; + + &:hover { + background: rgba(255, 255, 255, 0.2); + } } &__hero-progress { display: flex; flex-direction: column; - gap: calc(globals.$spacing-unit); - margin-bottom: calc(globals.$spacing-unit * 3); } - &__progress-header { + &__progress-info-row { + flex: 1; display: flex; justify-content: space-between; align-items: center; - margin-bottom: calc(globals.$spacing-unit / 2); + } + + &__progress-row { + display: flex; + align-items: flex-end; + gap: calc(globals.$spacing-unit * 2); + + &--bar { + margin-top: calc(globals.$spacing-unit); + } } &__progress-status { @@ -155,20 +218,14 @@ color: #ffffff; } - &__progress-details { - display: flex; - justify-content: space-between; - align-items: center; - font-size: 13px; - color: rgba(255, 255, 255, 0.9); - margin-top: calc(globals.$spacing-unit / 2); - } - &__progress-size { + font-size: 13px; font-weight: 600; + color: rgba(255, 255, 255, 0.9); } &__progress-time { + font-size: 13px; color: globals.$muted-color; } @@ -190,6 +247,7 @@ min-width: 200px; padding-right: calc(globals.$spacing-unit * 2); border-right: 1px solid rgba(255, 255, 255, 0.1); + align-self: flex-start; } &__speed-chart { @@ -202,7 +260,7 @@ &__speed-chart-canvas { width: 100%; - height: 100px; + height: 80px; image-rendering: crisp-edges; } @@ -281,7 +339,7 @@ min-width: 0; display: flex; flex-direction: column; - gap: calc(globals.$spacing-unit / 2); + gap: calc(globals.$spacing-unit / 1); } &__simple-title { @@ -342,13 +400,20 @@ min-height: unset; } + &__progress-wrapper { + flex: 1; + display: flex; + flex-direction: column; + gap: calc(globals.$spacing-unit / 2); + } + &__progress-bar { width: 100%; height: 8px; background-color: rgba(255, 255, 255, 0.08); border-radius: 4px; overflow: hidden; - position: relative; + margin-top: calc(globals.$spacing-unit / 2); &--small { height: 6px; @@ -357,10 +422,8 @@ &__progress-fill { height: 100%; - background-color: globals.$muted-color; - transition: - width 0.3s ease, - background 0.35s ease; + background-color: #fff; + transition: width 0.3s ease; border-radius: 4px; } } diff --git a/src/renderer/src/pages/downloads/download-group.tsx b/src/renderer/src/pages/downloads/download-group.tsx index 6db52ac8..7d2ea0e7 100644 --- a/src/renderer/src/pages/downloads/download-group.tsx +++ b/src/renderer/src/pages/downloads/download-group.tsx @@ -29,23 +29,6 @@ import { } from "@primer/octicons-react"; import { average } from "color.js"; -const getProgressGradient = ( - colorHex: string, - isPaused = false -): string | undefined => { - const hex = isPaused ? "#ffffff" : colorHex || "#08ea79"; - if (!hex.startsWith("#")) return undefined; - - try { - const r = Number.parseInt(hex.slice(1, 3), 16); - const g = Number.parseInt(hex.slice(3, 5), 16); - const b = Number.parseInt(hex.slice(5, 7), 16); - return `linear-gradient(90deg, rgba(${r},${g},${b},0.95) 0%, rgba(${r},${g},${b},0.65) 100%)`; - } catch { - return undefined; - } -}; - interface SpeedChartProps { speeds: number[]; peakSpeed: number; @@ -158,12 +141,12 @@ interface HeroDownloadViewProps { dominantColor: string; lastPacket: ReturnType["lastPacket"]; speedHistory: number[]; - getGameActions: (game: LibraryGame) => DropdownMenuItem[]; getStatusText: (game: LibraryGame) => string; formatSpeed: (speed: number) => string; calculateETA: () => string; pauseDownload: (shop: GameShop, objectId: string) => void; resumeDownload: (shop: GameShop, objectId: string) => void; + cancelDownload: (shop: GameShop, objectId: string) => void; t: (key: string) => string; } @@ -177,12 +160,12 @@ function HeroDownloadView({ dominantColor, lastPacket, speedHistory, - getGameActions, getStatusText, formatSpeed, calculateETA, pauseDownload, resumeDownload, + cancelDownload, t, }: Readonly) { return ( @@ -196,16 +179,6 @@ function HeroDownloadView({
-
-
- - - -
-
-
{game.logoImageUrl ? ( @@ -214,70 +187,71 @@ function HeroDownloadView({

{game.title}

)}
- - {isGameDownloading ? ( - - ) : ( - - )}
-
- - {getStatusText(game)} - - - {formatDownloadProgress(currentProgress)} - -
-
-
-
-
- - {isGameDownloading && lastPacket - ? `${formatBytes(lastPacket.download.bytesDownloaded)} / ${finalDownloadSize}` - : `0 B / ${finalDownloadSize}`} - - - {isGameDownloading && - lastPacket?.timeRemaining && - lastPacket.timeRemaining > 0 - ? calculateETA() - : ""} - +
+
+
+ + {getStatusText(game)} + + + {formatDownloadProgress(currentProgress)} + +
+
+ + {isGameDownloading && lastPacket + ? `${formatBytes(lastPacket.download.bytesDownloaded)} / ${finalDownloadSize}` + : `0 B / ${finalDownloadSize}`} + + + {isGameDownloading && + lastPacket?.timeRemaining && + lastPacket.timeRemaining > 0 + ? calculateETA() + : ""} + +
+
+
+
+
+
+ {isGameDownloading ? ( + + ) : ( + + )} + +
@@ -717,12 +691,12 @@ export function DownloadGroup({ dominantColor={dominantColor} lastPacket={lastPacket} speedHistory={speedHistoryRef.current[game.id] || []} - getGameActions={getGameActions} getStatusText={getStatusText} formatSpeed={formatSpeed} calculateETA={calculateETA} pauseDownload={pauseDownload} resumeDownload={resumeDownload} + cancelDownload={cancelDownload} t={t} /> ); diff --git a/src/renderer/src/pages/game-details/modals/repacks-modal.tsx b/src/renderer/src/pages/game-details/modals/repacks-modal.tsx index 51744fc5..08efb014 100644 --- a/src/renderer/src/pages/game-details/modals/repacks-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/repacks-modal.tsx @@ -231,9 +231,19 @@ export function RepacksModal({ return false; } - const lastCheckUtc = new Date(lastCheckTimestamp).toISOString(); + try { + const lastCheckDate = new Date(lastCheckTimestamp); - return repack.createdAt > lastCheckUtc; + if (isNaN(lastCheckDate.getTime())) { + return false; + } + + const lastCheckUtc = lastCheckDate.toISOString(); + + return repack.createdAt > lastCheckUtc; + } catch { + return false; + } }; const [isFilterDrawerOpen, setIsFilterDrawerOpen] = useState(false);