@@ -6,7 +6,9 @@ const state = {
66 folderCache : { } ,
77 currentView : 'loading' ,
88 currentPath : [ ] ,
9- currentItem : null
9+ currentItem : null ,
10+ isLoading : true ,
11+ isInitialized : false
1012} ;
1113
1214// Utility
@@ -15,18 +17,27 @@ function getRepoPath() {
1517 return parts [ 1 ] && parts [ 2 ] ? `${ parts [ 1 ] } /${ parts [ 2 ] } ` : 'Vic-Nas/PythonSolutions' ;
1618}
1719
18- // Fetch folder contents
20+ // Fetch folder contents with cache busting and validation
1921async function fetchFolderContents ( path ) {
20- if ( state . folderCache [ path ] ) {
21- return state . folderCache [ path ] ;
22+ const cacheKey = path ;
23+ const cached = state . folderCache [ cacheKey ] ;
24+
25+ // Cache for 5 minutes
26+ if ( cached && Date . now ( ) - cached . timestamp < 300000 ) {
27+ return cached . data ;
2228 }
2329
2430 try {
25- const res = await fetch ( `https://api.github.com/repos/${ getRepoPath ( ) } /contents/${ path } ` ) ;
31+ const res = await fetch ( `https://api.github.com/repos/${ getRepoPath ( ) } /contents/${ path } ` , {
32+ cache : 'no-cache' // Force fresh data from GitHub
33+ } ) ;
2634 if ( ! res . ok ) return null ;
2735
2836 const items = await res . json ( ) ;
29- state . folderCache [ path ] = items ;
37+ state . folderCache [ cacheKey ] = {
38+ data : items ,
39+ timestamp : Date . now ( )
40+ } ;
3041 return items ;
3142 } catch ( err ) {
3243 console . error ( `Error fetching ${ path } :` , err ) ;
@@ -44,61 +55,79 @@ function hasFiles(items) {
4455 ) ;
4556}
4657
47- // Load all platforms
58+ // Load all platforms with proper error handling
4859async function loadPlatforms ( ) {
4960 console . log ( 'Loading platforms...' ) ;
50- const rootItems = await fetchFolderContents ( '' ) ;
51- if ( ! rootItems ) return ;
52-
53- const platformFolders = rootItems . filter ( item =>
54- item . type === 'dir' &&
55- ! [ 'utils' , '.git' ] . includes ( item . name ) &&
56- ! item . name . startsWith ( '.' )
57- ) ;
5861
59- for ( const folder of platformFolders ) {
60- const platformData = {
61- name : folder . name ,
62- path : folder . name ,
63- image : null ,
64- count : 0
65- } ;
62+ try {
63+ const rootItems = await fetchFolderContents ( '' ) ;
64+ if ( ! rootItems ) {
65+ console . error ( 'Failed to load root items' ) ;
66+ return ;
67+ }
68+
69+ const platformFolders = rootItems . filter ( item =>
70+ item . type === 'dir' &&
71+ ! [ 'utils' , '.git' ] . includes ( item . name ) &&
72+ ! item . name . startsWith ( '.' )
73+ ) ;
74+
75+ // Load platforms sequentially to avoid race conditions
76+ const platforms = [ ] ;
6677
67- // Check for platform.png
68- const contents = await fetchFolderContents ( folder . name ) ;
69- if ( contents ) {
70- const platformImg = contents . find ( f => f . name === 'platform.png' ) ;
71- if ( platformImg ) {
72- // Use the download_url from GitHub API
73- platformData . image = platformImg . download_url || `${ folder . name } /platform.png` ;
78+ for ( const folder of platformFolders ) {
79+ const platformData = {
80+ name : folder . name ,
81+ path : folder . name ,
82+ image : null ,
83+ count : 0
84+ } ;
85+
86+ // Check for platform.png
87+ const contents = await fetchFolderContents ( folder . name ) ;
88+ if ( contents ) {
89+ const platformImg = contents . find ( f => f . name === 'platform.png' ) ;
90+ if ( platformImg ) {
91+ platformData . image = platformImg . download_url || `${ folder . name } /platform.png` ;
92+ }
93+
94+ // Count items recursively
95+ platformData . count = await countItems ( folder . name ) ;
7496 }
7597
76- // Count items recursively
77- platformData . count = await countItems ( folder . name ) ;
98+ platforms . push ( platformData ) ;
7899 }
79100
80- state . platforms . push ( platformData ) ;
101+ // Sort and assign only after all data is loaded
102+ platforms . sort ( ( a , b ) => a . name . localeCompare ( b . name ) ) ;
103+ state . platforms = platforms ;
104+
105+ console . log ( 'Loaded platforms:' , state . platforms ) ;
106+ } catch ( err ) {
107+ console . error ( 'Error loading platforms:' , err ) ;
81108 }
82-
83- state . platforms . sort ( ( a , b ) => a . name . localeCompare ( b . name ) ) ;
84- console . log ( 'Loaded platforms:' , state . platforms ) ;
85109}
86110
87111// Recursively count problem items
88112async function countItems ( path ) {
89- const items = await fetchFolderContents ( path ) ;
90- if ( ! items ) return 0 ;
91-
92- if ( hasFiles ( items ) ) return 1 ;
93-
94- const subdirs = items . filter ( i => i . type === 'dir' ) ;
95- let count = 0 ;
96-
97- for ( const dir of subdirs ) {
98- count += await countItems ( `${ path } /${ dir . name } ` ) ;
113+ try {
114+ const items = await fetchFolderContents ( path ) ;
115+ if ( ! items ) return 0 ;
116+
117+ if ( hasFiles ( items ) ) return 1 ;
118+
119+ const subdirs = items . filter ( i => i . type === 'dir' ) ;
120+ let count = 0 ;
121+
122+ for ( const dir of subdirs ) {
123+ count += await countItems ( `${ path } /${ dir . name } ` ) ;
124+ }
125+
126+ return count ;
127+ } catch ( err ) {
128+ console . error ( `Error counting items in ${ path } :` , err ) ;
129+ return 0 ;
99130 }
100-
101- return count ;
102131}
103132
104133// Parse hash to navigate
@@ -129,10 +158,14 @@ function render() {
129158 console . log ( 'Rendering view:' , state . currentView , 'path:' , state . currentPath ) ;
130159
131160 const views = [ 'loading-screen' , 'platform-selector' , 'folder-view' , 'problem-view' ] ;
132- views . forEach ( v => document . getElementById ( v ) . style . display = 'none' ) ;
161+ views . forEach ( v => {
162+ const el = document . getElementById ( v ) ;
163+ if ( el ) el . style . display = 'none' ;
164+ } ) ;
133165
134166 if ( state . currentView === 'loading' ) {
135- document . getElementById ( 'loading-screen' ) . style . display = 'flex' ;
167+ const el = document . getElementById ( 'loading-screen' ) ;
168+ if ( el ) el . style . display = 'flex' ;
136169 } else if ( state . currentView === 'platforms' ) {
137170 renderPlatforms ( ) ;
138171 } else if ( state . currentView === 'folder' ) {
@@ -143,10 +176,19 @@ function render() {
143176}
144177
145178function renderPlatforms ( ) {
146- document . getElementById ( 'platform-selector' ) . style . display = 'block' ;
179+ const selector = document . getElementById ( 'platform-selector' ) ;
147180 const grid = document . getElementById ( 'platform-grid' ) ;
181+
182+ if ( ! selector || ! grid ) return ;
183+
184+ selector . style . display = 'block' ;
148185 grid . innerHTML = '' ;
149186
187+ if ( state . platforms . length === 0 ) {
188+ grid . innerHTML = '<p style="grid-column: 1/-1; text-align: center;">No platforms found</p>' ;
189+ return ;
190+ }
191+
150192 state . platforms . forEach ( platform => {
151193 const card = document . createElement ( 'button' ) ;
152194 card . className = 'platform-box' ;
@@ -171,6 +213,8 @@ function renderPlatforms() {
171213
172214async function renderFolder ( ) {
173215 const view = document . getElementById ( 'folder-view' ) ;
216+ if ( ! view ) return ;
217+
174218 view . style . display = 'block' ;
175219
176220 const pathStr = state . currentPath . join ( '/' ) ;
@@ -186,14 +230,17 @@ async function renderFolder() {
186230
187231 // Set title
188232 const titleParts = state . currentPath . map ( capitalize ) ;
189- document . getElementById ( 'folder-title' ) . textContent = titleParts . join ( ' / ' ) ;
233+ const titleEl = document . getElementById ( 'folder-title' ) ;
234+ if ( titleEl ) titleEl . textContent = titleParts . join ( ' / ' ) ;
190235
191236 // Setup back button
192237 const backBtn = document . getElementById ( 'back-button' ) ;
193- backBtn . onclick = goBack ;
238+ if ( backBtn ) backBtn . onclick = goBack ;
194239
195240 // Render cards
196241 const container = document . getElementById ( 'folder-cards' ) ;
242+ if ( ! container ) return ;
243+
197244 container . innerHTML = '' ;
198245
199246 for ( const dir of subdirs ) {
@@ -216,6 +263,8 @@ async function renderFolder() {
216263
217264async function renderProblem ( ) {
218265 const view = document . getElementById ( 'problem-view' ) ;
266+ if ( ! view ) return ;
267+
219268 view . style . display = 'block' ;
220269 view . innerHTML = '<div style="text-align: center; padding: 3rem;">Loading...</div>' ;
221270
@@ -255,7 +304,9 @@ async function renderProblem() {
255304
256305 if ( pyFiles . length > 0 ) {
257306 try {
258- const res = await fetch ( `https://api.github.com/repos/${ getRepoPath ( ) } /contents/${ pathStr } /${ pyFiles [ 0 ] . name } ` ) ;
307+ const res = await fetch ( `https://api.github.com/repos/${ getRepoPath ( ) } /contents/${ pathStr } /${ pyFiles [ 0 ] . name } ` , {
308+ cache : 'no-cache'
309+ } ) ;
259310 if ( res . ok ) {
260311 const data = await res . json ( ) ;
261312 pythonCode = atob ( data . content ) ;
@@ -352,7 +403,11 @@ async function renderProblem() {
352403 }
353404
354405 view . innerHTML = html ;
355- hljs . highlightAll ( ) ;
406+
407+ // Highlight code if hljs is available
408+ if ( typeof hljs !== 'undefined' ) {
409+ hljs . highlightAll ( ) ;
410+ }
356411}
357412
358413// Navigation
@@ -367,7 +422,7 @@ async function navigateTo(path) {
367422 return ;
368423 }
369424
370- // Check if this folder has files (is a problem)
425+ // Check if this folder has files (is a " problem" )
371426 if ( hasFiles ( items ) ) {
372427 window . location . hash = `view/${ path . map ( encodeURIComponent ) . join ( '/' ) } ` ;
373428 } else {
@@ -381,22 +436,18 @@ function goBack() {
381436 // If we're viewing a problem, go back to its parent folder
382437 if ( state . currentView === 'problem' ) {
383438 if ( state . currentPath . length > 1 ) {
384- // Go to parent folder
385439 const parentPath = state . currentPath . slice ( 0 , - 1 ) ;
386440 window . location . hash = parentPath . map ( encodeURIComponent ) . join ( '/' ) ;
387441 } else {
388- // Go to home
389442 window . location . hash = '' ;
390443 }
391444 }
392445 // If we're in a folder view, go back one level
393446 else if ( state . currentView === 'folder' ) {
394447 if ( state . currentPath . length > 1 ) {
395- // Go to parent folder
396448 const parentPath = state . currentPath . slice ( 0 , - 1 ) ;
397449 window . location . hash = parentPath . map ( encodeURIComponent ) . join ( '/' ) ;
398450 } else {
399- // Go to home
400451 window . location . hash = '' ;
401452 }
402453 }
@@ -429,6 +480,12 @@ function getDefaultEmoji(platformName) {
429480
430481// Event listeners
431482window . addEventListener ( 'hashchange' , async ( ) => {
483+ // Ignore hash changes during initial load
484+ if ( state . isLoading ) {
485+ console . log ( 'Ignoring hash change during initial load' ) ;
486+ return ;
487+ }
488+
432489 console . log ( 'Hash changed' ) ;
433490 const parsed = parseHash ( ) ;
434491 state . currentView = parsed . view ;
@@ -449,8 +506,19 @@ window.addEventListener('hashchange', async () => {
449506 return ;
450507 }
451508
509+ // Set loading state
510+ state . isLoading = true ;
511+ state . currentView = 'loading' ;
512+ render ( ) ;
513+
514+ // Load platforms completely before proceeding
452515 await loadPlatforms ( ) ;
453516
517+ // Mark as initialized
518+ state . isLoading = false ;
519+ state . isInitialized = true ;
520+
521+ // Now parse hash and render
454522 const parsed = parseHash ( ) ;
455523 state . currentView = parsed . view ;
456524 state . currentPath = parsed . path ;
0 commit comments