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 };