@@ -22,8 +22,10 @@ function AppPage() {
2222
2323 const [ groupedApps , setGroupedApps ] = useState ( { } ) ;
2424 const [ collapsedCategories , setCollapsedCategories ] = useState ( { } ) ;
25+ const [ isLoading , setIsLoading ] = useState ( true ) ; // New loading state
2526
2627 useEffect ( ( ) => {
28+ setIsLoading ( true ) ; // Set loading to true when fetch starts
2729 fetch ( '/apps/apps.json' )
2830 . then ( ( response ) => response . json ( ) )
2931 . then ( ( data ) => {
@@ -34,7 +36,10 @@ function AppPage() {
3436 } , { } ) ;
3537 setCollapsedCategories ( initialCollapsedState ) ;
3638 } )
37- . catch ( ( error ) => console . error ( 'Error fetching apps:' , error ) ) ;
39+ . catch ( ( error ) => console . error ( 'Error fetching apps:' , error ) )
40+ . finally ( ( ) => {
41+ setIsLoading ( false ) ; // Set loading to false when fetch completes
42+ } ) ;
3843 } , [ ] ) ;
3944
4045 const toggleCategoryCollapse = ( categoryKey ) => {
@@ -64,44 +69,65 @@ function AppPage() {
6469 < span className = "apps-color" > apps</ span >
6570 </ h1 >
6671 < hr className = "border-gray-700" />
67- { Object . keys ( groupedApps )
68- . sort ( ( a , b ) => groupedApps [ a ] . order - groupedApps [ b ] . order )
69- . map ( categoryKey => {
70- const category = groupedApps [ categoryKey ] ;
71- const CategoryIcon = appIcons [ category . icon ] ;
72- return (
73- < div key = { categoryKey } className = "mt-8" >
74- < h2
75- className = "text-3xl font-arvo font-normal tracking-tight text-gray-200 mb-2 flex items-center cursor-pointer"
76- onClick = { ( ) => toggleCategoryCollapse ( categoryKey ) }
77- >
78- { collapsedCategories [ categoryKey ] ? < CaretRight size = { 24 } className = "mr-2" /> : < CaretDown size = { 24 } className = "mr-2" /> }
79- { CategoryIcon && < CategoryIcon size = { 28 } className = "mr-2" /> }
80- { category . name } ({ category . apps . length } )
81- </ h2 >
82- < p className = "text-gray-400 mb-4 ml-10" > { category . description } </ p >
83- < AnimatePresence initial = { false } >
84- { ! collapsedCategories [ categoryKey ] && (
85- < motion . div
86- key = "content"
87- initial = "collapsed"
88- animate = "open"
89- exit = "collapsed"
90- variants = { variants }
91- transition = { { duration : 0.3 , ease : "easeInOut" } }
92- style = { { overflow : "hidden" } }
93- >
94- < div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8" >
95- { category . apps . map ( ( app , index ) => (
96- < AppCard key = { index } app = { app } />
97- ) ) }
98- </ div >
99- </ motion . div >
100- ) }
101- </ AnimatePresence >
72+ { isLoading ? (
73+ // Skeleton Loading State
74+ < div className = "mt-8" >
75+ { [ ...Array ( 3 ) ] . map ( ( _ , categoryIndex ) => ( // Simulate 3 categories
76+ < div key = { categoryIndex } className = "mb-8" >
77+ < div className = "h-8 bg-gray-700 rounded w-1/3 mb-4 animate-pulse" > </ div > { /* Category Title */ }
78+ < div className = "h-4 bg-gray-800 rounded w-1/2 mb-6 animate-pulse" > </ div > { /* Category Description */ }
79+ < div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8" >
80+ { [ ...Array ( 3 ) ] . map ( ( _ , appIndex ) => ( // Simulate 3 app cards per category
81+ < div key = { appIndex } className = "bg-gray-800 rounded-lg shadow-lg p-6 h-40 animate-pulse" >
82+ < div className = "h-6 bg-gray-700 rounded w-3/4 mb-2" > </ div >
83+ < div className = "h-4 bg-gray-700 rounded w-1/2" > </ div >
84+ </ div >
85+ ) ) }
86+ </ div >
10287 </ div >
103- ) ;
104- } ) }
88+ ) ) }
89+ </ div >
90+ ) : (
91+ // Actual Content
92+ Object . keys ( groupedApps )
93+ . sort ( ( a , b ) => groupedApps [ a ] . order - groupedApps [ b ] . order )
94+ . map ( categoryKey => {
95+ const category = groupedApps [ categoryKey ] ;
96+ const CategoryIcon = appIcons [ category . icon ] ;
97+ return (
98+ < div key = { categoryKey } className = "mt-8" >
99+ < h2
100+ className = "text-3xl font-arvo font-normal tracking-tight text-gray-200 mb-2 flex items-center cursor-pointer"
101+ onClick = { ( ) => toggleCategoryCollapse ( categoryKey ) }
102+ >
103+ { collapsedCategories [ categoryKey ] ? < CaretRight size = { 24 } className = "mr-2" /> : < CaretDown size = { 24 } className = "mr-2" /> }
104+ { CategoryIcon && < CategoryIcon size = { 28 } className = "mr-2" /> }
105+ { category . name } ({ category . apps . length } )
106+ </ h2 >
107+ < p className = "text-gray-400 mb-4 ml-10" > { category . description } </ p >
108+ < AnimatePresence initial = { false } >
109+ { ! collapsedCategories [ categoryKey ] && (
110+ < motion . div
111+ key = "content"
112+ initial = "collapsed"
113+ animate = "open"
114+ exit = "collapsed"
115+ variants = { variants }
116+ transition = { { duration : 0.3 , ease : "easeInOut" } }
117+ style = { { overflow : "hidden" } }
118+ >
119+ < div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8" >
120+ { category . apps . map ( ( app , index ) => (
121+ < AppCard key = { index } app = { app } />
122+ ) ) }
123+ </ div >
124+ </ motion . div >
125+ ) }
126+ </ AnimatePresence >
127+ </ div >
128+ ) ;
129+ } )
130+ ) }
105131 </ div >
106132 </ div >
107133 ) ;
0 commit comments