@@ -5,7 +5,10 @@ import { useAlertDialog } from "@/context/AlertDialogContext";
55import { useDownloads } from "@/context/DownloadContext" ;
66import { getGameCoverMediaId } from "@/hooks/useGames" ;
77import { CloudArrowDownIcon } from "@heroicons/react/16/solid" ;
8- import { StarIcon as StarSolid , Cog8ToothIcon } from "@heroicons/react/24/solid" ;
8+ import {
9+ StarIcon as StarSolid ,
10+ Cog8ToothIcon ,
11+ } from "@heroicons/react/24/solid" ;
912import { StarIcon as StarOutline } from "@heroicons/react/24/outline" ;
1013import { Button } from "@tw/button" ;
1114import { GameSettings } from "@/components/admin/GameSettings" ;
@@ -99,10 +102,10 @@ export function GameCard({ game }: { game: GamevaultGame }) {
99102 e . stopPropagation ( ) ;
100103 if ( ! serverUrl ) return ;
101104 startDownload ( game . id , filename ) ;
102-
105+
103106 // Show global alert notification
104107 showAlert ( {
105- title : `Added ${ localGame . metadata ?. title || localGame . title } to the download queue`
108+ title : `Added ${ localGame . metadata ?. title || localGame . title } to the download queue` ,
106109 } ) ;
107110 } ,
108111 [ serverUrl , startDownload , game . id , filename , showAlert , localGame ] ,
@@ -113,29 +116,29 @@ export function GameCard({ game }: { game: GamevaultGame }) {
113116 e . preventDefault ( ) ;
114117 e . stopPropagation ( ) ;
115118 if ( ! serverUrl ) return ;
116-
117- console . log ( ' === handleTauriDownload called ===' ) ;
118- console . log ( ' Game ID:' , game . id , ' Filename:' , filename ) ;
119-
119+
120+ console . log ( " === handleTauriDownload called ===" ) ;
121+ console . log ( " Game ID:" , game . id , " Filename:" , filename ) ;
122+
120123 try {
121124 // Get download path from localStorage
122- const downloadPath = localStorage . getItem ( ' tauri_download_path' ) ;
123- console . log ( ' Download path check:' , downloadPath ) ;
125+ const downloadPath = localStorage . getItem ( " tauri_download_path" ) ;
126+ console . log ( " Download path check:" , downloadPath ) ;
124127 if ( ! downloadPath ) {
125- alert ( ' Please select a download location in Settings first.' ) ;
128+ alert ( " Please select a download location in Settings first." ) ;
126129 return ;
127130 }
128131
129132 // Start download tracking
130- console . log ( ' Starting download...' ) ;
133+ console . log ( " Starting download..." ) ;
131134 startDownload ( game . id , filename ) ;
132-
135+
133136 // Show global alert notification
134137 showAlert ( {
135- title : `Added ${ localGame . metadata ?. title || localGame . title } to the download queue`
138+ title : `Added ${ localGame . metadata ?. title || localGame . title } to the download queue` ,
136139 } ) ;
137140 } catch ( error ) {
138- console . error ( ' Error starting Tauri download:' , error ) ;
141+ console . error ( " Error starting Tauri download:" , error ) ;
139142 }
140143 } ,
141144 [ serverUrl , startDownload , game . id , filename , showAlert , localGame ] ,
@@ -153,129 +156,130 @@ export function GameCard({ game }: { game: GamevaultGame }) {
153156
154157 const gameViewUrl = `/library/${ game . id } ` ;
155158
156- const handleOpenSettings = useCallback (
157- ( e : React . MouseEvent ) => {
158- e . preventDefault ( ) ;
159- e . stopPropagation ( ) ;
160- setSettingsOpen ( true ) ;
161- } ,
162- [ ] ,
163- ) ;
159+ const handleOpenSettings = useCallback ( ( e : React . MouseEvent ) => {
160+ e . preventDefault ( ) ;
161+ e . stopPropagation ( ) ;
162+ setSettingsOpen ( true ) ;
163+ } , [ ] ) ;
164164
165165 return (
166166 < >
167- < Link
168- to = { gameViewUrl }
169- className = { clsx (
170- "group flex flex-col rounded-xl bg-zinc-100 dark:bg-zinc-800 shadow-sm ring-1 ring-zinc-950/10 dark:ring-white/5 overflow-hidden focus:outline-none focus:ring-2 focus:ring-indigo-500" ,
171- "transition-colors hover:bg-zinc-200/60 dark:hover:bg-zinc-700/70 cursor-pointer" ,
172- ) }
173- >
174- < div className = "relative aspect-[3/4] w-full bg-bg-muted flex items-center justify-center overflow-hidden" >
175- { coverId ? (
176- < Media
177- media = { {
178- id : typeof coverId === 'number' ? coverId : Number ( coverId ) || 0 ,
179- created_at : new Date ( 0 ) ,
180- entity_version : 0 ,
181- } as any }
182- size = { 300 }
183- className = "h-full w-full object-contain rounded-none"
184- square
185- alt = { localGame . title }
186- />
187- ) : (
188- < div className = "text-xs text-fg-muted" >
189- No Cover
190- </ div >
167+ < Link
168+ to = { gameViewUrl }
169+ className = { clsx (
170+ "group flex flex-col rounded-xl bg-zinc-100 dark:bg-zinc-800 shadow-sm ring-1 ring-zinc-950/10 dark:ring-white/5 overflow-hidden focus:outline-none focus:ring-2 focus:ring-indigo-500" ,
171+ "transition-colors hover:bg-zinc-200/60 dark:hover:bg-zinc-700/70 cursor-pointer" ,
191172 ) }
192- { /* Top-right bookmark toggle */ }
193- < button
194- type = "button"
195- onClick = { toggleBookmark }
196- aria-label = { bookmarked ? "Remove bookmark" : "Add bookmark" }
197- aria-pressed = { bookmarked }
198- disabled = { ! currentUserId || bookmarkBusy }
199- className = { clsx (
200- "absolute top-1 right-1 h-8 w-8 flex items-center justify-center rounded-md border shadow-sm backdrop-blur-sm transition-colors" ,
201- "disabled:opacity-50 disabled:cursor-not-allowed" ,
202- bookmarked
203- ? "bg-yellow-400/20 border-yellow-400"
204- : "bg-zinc-900/40 dark:bg-zinc-700/50 border-white/20 hover:bg-zinc-800/60 dark:hover:bg-zinc-600/60" ,
205- ) }
206- >
207- { bookmarked ? (
208- < StarSolid className = "h-5 w-5 text-yellow-400" />
173+ >
174+ < div className = "relative aspect-[3/4] w-full bg-bg-muted flex items-center justify-center overflow-hidden" >
175+ { coverId ? (
176+ < Media
177+ media = {
178+ {
179+ id :
180+ typeof coverId === "number"
181+ ? coverId
182+ : Number ( coverId ) || 0 ,
183+ created_at : new Date ( 0 ) ,
184+ entity_version : 0 ,
185+ } as any
186+ }
187+ size = { 300 }
188+ className = "h-full w-full object-contain rounded-none"
189+ square
190+ alt = { localGame . title }
191+ />
209192 ) : (
210- < StarOutline className = "h-5 w-5 text-white" / >
193+ < div className = "text-xs text-fg-muted" > No Cover </ div >
211194 ) }
212- </ button >
213- { /* Top-left settings button */ }
214- < button
215- type = "button"
216- onClick = { handleOpenSettings }
217- aria-label = "Settings"
218- className = "absolute top-1 left-1 h-8 w-8 flex items-center justify-center rounded-md border shadow-sm backdrop-blur-sm transition-colors bg-zinc-900/40 dark:bg-zinc-700/50 border-white/20 hover:bg-zinc-800/60 dark:hover:bg-zinc-600/60"
219- title = "Settings"
220- >
221- < Cog8ToothIcon className = "h-5 w-5 text-white" />
222- </ button >
223- { /* Bottom-right download actions */ }
224- < div className = "absolute bottom-0 right-0 p-1 z-10 flex justify-end opacity-85" >
225- { isTauri ? (
226- < Button
227- color = "zinc"
228- aria-label = "Download"
229- className = "flex justify-center h-8 text-md font-medium items-center gap-1 shadow-md shadow-black/20 backdrop-blur-sm"
230- onClick = { handleTauriDownload }
231- >
232- < CloudArrowDownIcon className = "w-6 h-6 fill-white" />
233- </ Button >
234- ) : (
235- < Dropdown >
236- < DropdownButton
237- as = { Button }
195+ { /* Top-right bookmark toggle */ }
196+ < button
197+ type = "button"
198+ onClick = { toggleBookmark }
199+ aria-label = { bookmarked ? "Remove bookmark" : "Add bookmark" }
200+ aria-pressed = { bookmarked }
201+ disabled = { ! currentUserId || bookmarkBusy }
202+ className = { clsx (
203+ "absolute top-1 right-1 h-8 w-8 flex items-center justify-center rounded-md border shadow-sm backdrop-blur-sm transition-colors" ,
204+ "disabled:opacity-50 disabled:cursor-not-allowed" ,
205+ bookmarked
206+ ? "bg-yellow-400/20 border-yellow-400"
207+ : "bg-zinc-900/40 dark:bg-zinc-700/50 border-white/20 hover:bg-zinc-800/60 dark:hover:bg-zinc-600/60" ,
208+ ) }
209+ >
210+ { bookmarked ? (
211+ < StarSolid className = "h-5 w-5 text-yellow-400" />
212+ ) : (
213+ < StarOutline className = "h-5 w-5 text-white" />
214+ ) }
215+ </ button >
216+ { /* Top-left settings button */ }
217+ < button
218+ type = "button"
219+ onClick = { handleOpenSettings }
220+ aria-label = "Settings"
221+ className = "absolute top-1 left-1 h-8 w-8 flex items-center justify-center rounded-md border shadow-sm backdrop-blur-sm transition-colors bg-zinc-900/40 dark:bg-zinc-700/50 border-white/20 hover:bg-zinc-800/60 dark:hover:bg-zinc-600/60"
222+ title = "Settings"
223+ >
224+ < Cog8ToothIcon className = "h-5 w-5 text-white" />
225+ </ button >
226+ { /* Bottom-right download actions */ }
227+ < div className = "absolute bottom-0 right-0 p-1 z-10 flex justify-end opacity-85" >
228+ { isTauri ? (
229+ < Button
238230 color = "zinc"
239231 aria-label = "Download"
240232 className = "flex justify-center h-8 text-md font-medium items-center gap-1 shadow-md shadow-black/20 backdrop-blur-sm"
241- onClick = { ( e : React . MouseEvent ) => {
242- e . preventDefault ( ) ;
243- e . stopPropagation ( ) ;
244- } }
233+ onClick = { handleTauriDownload }
245234 >
246235 < CloudArrowDownIcon className = "w-6 h-6 fill-white" />
247- </ DropdownButton >
248- < DropdownMenu className = "min-w-48" anchor = "top end" >
249- < DropdownItem onClick = { handleDirectDownload } >
250- < DropdownLabel > Direct Download</ DropdownLabel >
251- </ DropdownItem >
252- < DropdownItem onClick = { handleClientDownload } >
253- < DropdownLabel > Download via GameVault Client</ DropdownLabel >
254- </ DropdownItem >
255- </ DropdownMenu >
256- </ Dropdown >
236+ </ Button >
237+ ) : (
238+ < Dropdown >
239+ < DropdownButton
240+ as = { Button }
241+ color = "zinc"
242+ aria-label = "Download"
243+ className = "flex justify-center h-8 text-md font-medium items-center gap-1 shadow-md shadow-black/20 backdrop-blur-sm"
244+ onClick = { ( e : React . MouseEvent ) => {
245+ e . preventDefault ( ) ;
246+ e . stopPropagation ( ) ;
247+ } }
248+ >
249+ < CloudArrowDownIcon className = "w-6 h-6 fill-white" />
250+ </ DropdownButton >
251+ < DropdownMenu className = "min-w-48" anchor = "top end" >
252+ < DropdownItem onClick = { handleDirectDownload } >
253+ < DropdownLabel > Direct Download</ DropdownLabel >
254+ </ DropdownItem >
255+ < DropdownItem onClick = { handleClientDownload } >
256+ < DropdownLabel > Download via GameVault Client</ DropdownLabel >
257+ </ DropdownItem >
258+ </ DropdownMenu >
259+ </ Dropdown >
260+ ) }
261+ </ div >
262+ </ div >
263+ < div className = "p-2 pt-2" >
264+ < h3 className = "text-sm font-medium truncate" title = { localGame . title } >
265+ { localGame . metadata ?. title || localGame . title }
266+ </ h3 >
267+ { ( localGame as any ) . sort_title &&
268+ ( localGame as any ) . sort_title !== localGame . title && (
269+ < p
270+ className = "mt-0.5 text-xs text-fg-muted truncate"
271+ title = { localGame . title }
272+ >
273+ { localGame . title }
274+ </ p >
275+ ) }
276+ { formattedSize && (
277+ < p className = "mt-0.5 text-xs text-fg-muted" title = { formattedSize } >
278+ { formattedSize }
279+ </ p >
257280 ) }
258281 </ div >
259- </ div >
260- < div className = "p-2 pt-2" >
261- < h3 className = "text-sm font-medium truncate" title = { localGame . title } >
262- { localGame . metadata ?. title || localGame . title }
263- </ h3 >
264- { ( localGame as any ) . sort_title && ( localGame as any ) . sort_title !== localGame . title && (
265- < p
266- className = "mt-0.5 text-xs text-fg-muted truncate"
267- title = { localGame . title }
268- >
269- { localGame . title }
270- </ p >
271- ) }
272- { formattedSize && (
273- < p className = "mt-0.5 text-xs text-fg-muted" title = { formattedSize } >
274- { formattedSize }
275- </ p >
276- ) }
277- </ div >
278- </ Link >
282+ </ Link >
279283 { settingsOpen && (
280284 < GameSettings
281285 game = { game }
0 commit comments