1- import { ArrowBack , Delete , Description , Update } from "@mui/icons-material" ;
1+ import { ArrowBack , Update } from "@mui/icons-material" ;
22import {
33 Box ,
44 Button ,
5- Card ,
6- CardContent ,
75 Container ,
86 Dialog ,
97 DialogContent ,
@@ -15,18 +13,19 @@ import { invoke } from "@tauri-apps/api/core";
1513import { listen } from "@tauri-apps/api/event" ;
1614import { useCallback , useEffect , useRef , useState } from "react" ;
1715import { useTranslation } from "react-i18next" ;
18- import { CachedImage } from "../../components/CachedImage" ;
1916import { ReleaseNotesModal } from "../../components/ReleaseNotesModal" ;
2017import { Terminal } from "../../components/Terminal" ;
2118import { UpdateAllModal } from "../../components/UpdateAllModal" ;
2219import type { InstalledAppInfo } from "../../store/installedAppsStore" ;
2320import { useInstalledAppsStore } from "../../store/installedAppsStore" ;
2421import { checkAvailableUpdates } from "../../utils/updateChecker" ;
22+ import { InstalledAppCard } from "./components/InstalledAppCard" ;
2523
2624interface InstalledAppRust {
2725 app_id : string ;
2826 name : string ;
2927 version : string ;
28+ summary ?: string ;
3029}
3130
3231interface MyAppsProps {
@@ -71,6 +70,11 @@ export const MyApps = ({ onBack }: MyAppsProps) => {
7170 const parentRef = useRef < HTMLDivElement > ( null ) ;
7271 const [ itemsPerRow , setItemsPerRow ] = useState ( 5 ) ;
7372
73+ // Fixed card height for consistent rendering
74+ const CARD_HEIGHT = 300 ;
75+ const ROW_GAP = 16 ; // 2 * 8px (gap: 2 in MUI)
76+ const ROW_HEIGHT = CARD_HEIGHT + ROW_GAP ;
77+
7478 useEffect ( ( ) => {
7579 const updateItemsPerRow = ( ) => {
7680 const width = window . innerWidth ;
@@ -95,13 +99,8 @@ export const MyApps = ({ onBack }: MyAppsProps) => {
9599 const rowVirtualizer = useVirtualizer ( {
96100 count : rowCount ,
97101 getScrollElement : ( ) => parentRef . current ,
98- estimateSize : ( ) => 340 , // Estimated height of each row (increased for more spacing)
99- overscan : 2 ,
100- measureElement :
101- typeof window !== "undefined" &&
102- navigator . userAgent . indexOf ( "Firefox" ) === - 1
103- ? undefined
104- : ( ) => 340 , // Use fixed size to avoid scroll jumps
102+ estimateSize : ( ) => ROW_HEIGHT ,
103+ overscan : 5 , // Increased for smoother scrolling
105104 } ) ;
106105
107106 const reloadInstalledApps = useCallback ( async ( ) => {
@@ -113,6 +112,7 @@ export const MyApps = ({ onBack }: MyAppsProps) => {
113112 appId : app . app_id ,
114113 name : app . name ,
115114 version : app . version ,
115+ summary : app . summary ,
116116 } ) ) ;
117117
118118 setInstalledAppsInfo ( installedAppsInfo ) ;
@@ -474,7 +474,7 @@ export const MyApps = ({ onBack }: MyAppsProps) => {
474474 top : 0 ,
475475 left : 0 ,
476476 width : "100%" ,
477- height : 340 ,
477+ height : CARD_HEIGHT ,
478478 transform : `translateY(${ virtualRow . start } px)` ,
479479 display : "grid" ,
480480 gridTemplateColumns : {
@@ -485,188 +485,25 @@ export const MyApps = ({ onBack }: MyAppsProps) => {
485485 xl : "repeat(5, 1fr)" ,
486486 } ,
487487 gap : 2 ,
488- pb : 2 , // Padding bottom to separate rows
488+ mb : 2 , // Margin bottom to separate rows
489489 } }
490490 >
491491 { rowApps . map ( ( app ) => (
492- < Card
492+ < InstalledAppCard
493493 key = { app . appId }
494- sx = { {
495- display : "flex" ,
496- flexDirection : "column" ,
497- boxSizing : "border-box" ,
498- minWidth : 0 ,
499- overflow : "hidden" ,
500- transition : "box-shadow 0.3s" ,
501- "&:hover" : { boxShadow : 6 } ,
502- } }
503- >
504- < Box
505- sx = { {
506- p : 2 ,
507- display : "flex" ,
508- flexDirection : "column" ,
509- alignItems : "center" ,
510- gap : 2 ,
511- minHeight : 150 ,
512- bgcolor : "background.paper" ,
513- } }
514- >
515- { /* App Icon */ }
516- < Box
517- sx = { {
518- width : 80 ,
519- height : 80 ,
520- flexShrink : 0 ,
521- borderRadius : 2 ,
522- overflow : "hidden" ,
523- bgcolor : "grey.800" ,
524- display : "flex" ,
525- alignItems : "center" ,
526- justifyContent : "center" ,
527- } }
528- >
529- < CachedImage
530- appId = { app . appId }
531- imageUrl = { `https://dl.flathub.org/repo/appstream/x86_64/icons/128x128/${ app . appId } .png` }
532- alt = { app . name }
533- variant = "rounded"
534- style = { {
535- width : "100%" ,
536- height : "100%" ,
537- objectFit : "cover" ,
538- } }
539- />
540- </ Box >
541-
542- { /* App Name */ }
543- < Typography
544- variant = "body1"
545- fontWeight = "bold"
546- textAlign = "center"
547- sx = { {
548- overflow : "hidden" ,
549- textOverflow : "ellipsis" ,
550- display : "-webkit-box" ,
551- WebkitLineClamp : 2 ,
552- WebkitBoxOrient : "vertical" ,
553- minHeight : "2.5em" ,
554- } }
555- >
556- { app . name }
557- </ Typography >
558- </ Box >
559-
560- < CardContent sx = { { flexGrow : 1 , pt : 1 } } >
561- { /* App ID */ }
562- < Typography
563- variant = "caption"
564- color = "text.secondary"
565- sx = { {
566- display : "block" ,
567- mb : 1 ,
568- overflow : "hidden" ,
569- textOverflow : "ellipsis" ,
570- whiteSpace : "nowrap" ,
571- } }
572- >
573- { app . appId }
574- </ Typography >
575-
576- { /* Version and Action Buttons */ }
577- < Box
578- sx = { {
579- display : "flex" ,
580- justifyContent : "space-between" ,
581- alignItems : "center" ,
582- gap : 1 ,
583- } }
584- >
585- < Typography
586- variant = "caption"
587- color = "primary"
588- sx = { {
589- fontWeight : "bold" ,
590- } }
591- >
592- v{ app . version }
593- </ Typography >
594-
595- < Box
596- sx = { {
597- display : "flex" ,
598- alignItems : "center" ,
599- gap : 0.5 ,
600- } }
601- >
602- { /* Release Notes Icon - only show if update available */ }
603- { hasUpdate ( app . appId ) && (
604- < IconButton
605- size = "small"
606- onClick = { ( ) =>
607- setSelectedAppForNotes ( app . appId )
608- }
609- sx = { {
610- p : 0.5 ,
611- "&:hover" : {
612- color : "primary.main" ,
613- } ,
614- } }
615- >
616- < Description fontSize = "small" />
617- </ IconButton >
618- ) }
619-
620- { /* Uninstall Icon */ }
621- < IconButton
622- size = "small"
623- onClick = { ( ) => handleUninstall ( app . appId ) }
624- disabled = {
625- isUninstalling &&
626- uninstallingApp === app . appId
627- }
628- sx = { {
629- p : 0.5 ,
630- bgcolor : "error.main" ,
631- color : "white" ,
632- "&:hover" : {
633- bgcolor : "error.dark" ,
634- } ,
635- "&.Mui-disabled" : {
636- bgcolor : "grey.500" ,
637- color : "grey.300" ,
638- } ,
639- } }
640- >
641- < Delete fontSize = "small" />
642- </ IconButton >
643-
644- { /* Update Button - only show if update available */ }
645- { hasUpdate ( app . appId ) && (
646- < Button
647- variant = "contained"
648- size = "small"
649- onClick = { ( ) => handleUpdate ( app . appId ) }
650- disabled = {
651- isUpdating && updatingApp === app . appId
652- }
653- sx = { {
654- minWidth : "auto" ,
655- px : 1.5 ,
656- py : 0.5 ,
657- fontSize : "0.7rem" ,
658- textTransform : "none" ,
659- } }
660- >
661- { isUpdating && updatingApp === app . appId
662- ? t ( "appDetails.updating" )
663- : t ( "appDetails.update" ) }
664- </ Button >
665- ) }
666- </ Box >
667- </ Box >
668- </ CardContent >
669- </ Card >
494+ app = { app }
495+ hasUpdate = { hasUpdate ( app . appId ) }
496+ isUpdating = { isUpdating && updatingApp === app . appId }
497+ isUninstalling = {
498+ isUninstalling && uninstallingApp === app . appId
499+ }
500+ cardHeight = { CARD_HEIGHT }
501+ onUpdate = { ( ) => handleUpdate ( app . appId ) }
502+ onUninstall = { ( ) => handleUninstall ( app . appId ) }
503+ onShowReleaseNotes = { ( ) =>
504+ setSelectedAppForNotes ( app . appId )
505+ }
506+ />
670507 ) ) }
671508 </ Box >
672509 ) ;
0 commit comments