Mercurial
comparison hg-web/src/components/directory-browser.tsx @ 223:0e7b9464248d hg-web
[hg-web] Add browser route regression coverage
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Sun, 02 Aug 2026 10:07:40 -0700 |
| parents | 9f4429c49733 |
| children | 3007ef5fc0ed |
comparison
equal
deleted
inserted
replaced
| 222:a8d6435dc021 | 223:0e7b9464248d |
|---|---|
| 427 const [content, setContent] = useState<{ files: any[]; directories: any[] }>({ files: [], directories: [] }); | 427 const [content, setContent] = useState<{ files: any[]; directories: any[] }>({ files: [], directories: [] }); |
| 428 const [readme, setReadme] = useState<string | null>(null); | 428 const [readme, setReadme] = useState<string | null>(null); |
| 429 const [error, setError] = useState<string | null>(null); | 429 const [error, setError] = useState<string | null>(null); |
| 430 const [loading, setLoading] = useState(false); | 430 const [loading, setLoading] = useState(false); |
| 431 const [viewingFile, setViewingFile] = useState<string | null>(null); | 431 const [viewingFile, setViewingFile] = useState<string | null>(null); |
| 432 const requestGeneration = useRef(0); | |
| 432 | 433 |
| 433 // Sync with initialPath prop | 434 // Sync with initialPath prop |
| 434 useEffect(() => { | 435 useEffect(() => { |
| 435 setCurrentPath(initialPath); | 436 setCurrentPath(initialPath); |
| 436 }, [initialPath]); | 437 }, [initialPath]); |
| 437 | 438 |
| 438 useEffect(() => { | 439 useEffect(() => { |
| 439 fetchDirectory(currentPath); | 440 const generation = ++requestGeneration.current; |
| 440 fetchReadme(currentPath); | 441 fetchDirectory(currentPath, generation); |
| 442 fetchReadme(currentPath, generation); | |
| 441 }, [currentPath]); | 443 }, [currentPath]); |
| 442 | 444 |
| 443 const navigate = useCallback((path: string) => { | 445 const navigate = useCallback((path: string) => { |
| 444 setCurrentPath(path); | 446 setCurrentPath(path); |
| 445 onPathChange?.(path); | 447 onPathChange?.(path); |
| 446 }, [onPathChange]); | 448 }, [onPathChange]); |
| 447 | 449 |
| 448 const fetchDirectory = async (path: string) => { | 450 const fetchDirectory = async (path: string, generation: number) => { |
| 449 setLoading(true); | 451 setLoading(true); |
| 450 setError(null); | 452 setError(null); |
| 451 try { | 453 try { |
| 452 const cacheKey = `dir:${path}`; | 454 const cacheKey = `dir:${path}`; |
| 453 let data; | 455 let data; |
| 466 | 468 |
| 467 if (data?.error) { | 469 if (data?.error) { |
| 468 throw new Error(data.error); | 470 throw new Error(data.error); |
| 469 } | 471 } |
| 470 | 472 |
| 471 setContent({ | 473 if (generation === requestGeneration.current) { |
| 472 files: data?.files || [], | 474 setContent({ |
| 473 directories: data?.directories || [] | 475 files: data?.files || [], |
| 474 }); | 476 directories: data?.directories || [] |
| 477 }); | |
| 478 } | |
| 475 } catch (err: any) { | 479 } catch (err: any) { |
| 476 console.error('Error loading directory:', err); | 480 console.error('Error loading directory:', err); |
| 477 setError(err.message); | 481 if (generation === requestGeneration.current) setError(err.message); |
| 478 } finally { | 482 } finally { |
| 479 setLoading(false); | 483 if (generation === requestGeneration.current) setLoading(false); |
| 480 } | 484 } |
| 481 }; | 485 }; |
| 482 | 486 |
| 483 const fetchReadme = async (path: string) => { | 487 const fetchReadme = async (path: string, generation: number) => { |
| 484 setReadme(null); | 488 if (generation === requestGeneration.current) setReadme(null); |
| 485 const readmePath = path ? `${path}/README.md` : 'README.md'; | |
| 486 try { | 489 try { |
| 487 const response = await fetch(`${API_BASE}/file?path=${encodeURIComponent(readmePath)}`); | 490 const url = path |
| 491 ? `${API_BASE}/readme?path=${encodeURIComponent(path)}` | |
| 492 : `${API_BASE}/readme`; | |
| 493 const response = await fetch(url); | |
| 488 if (response.ok) { | 494 if (response.ok) { |
| 489 const text = await response.text(); | 495 const text = await response.text(); |
| 490 setReadme(text); | 496 if (generation === requestGeneration.current) setReadme(text || null); |
| 491 } | 497 } |
| 492 } catch (err) { | 498 } catch (err) { |
| 493 // Readme is optional | 499 // Readme is optional |
| 494 } | 500 } |
| 495 }; | 501 }; |