Files
redux-scraper/app/javascript/bundles/Main/components/PostFiles.tsx
Dylan Knutson f2f8a9c34a Refactor PostFiles component to use URL parameters and simplify implementation
- Change from hash fragments (#file=2) to URL parameters (?idx=2) for server-side prerendering support
- Simplify React component by removing complex client-side hydration logic
- Remove unnecessary props: totalFiles, hasMultipleFiles (derive from files.length)
- Remove redundant useCallback and popstate handlers
- Update Rails helper to read URL parameter and pass correct initialSelectedIndex
- Maintain all functionality: carousel, keyboard navigation, URL state management
2025-08-09 00:59:26 +00:00

100 lines
2.6 KiB
TypeScript

import * as React from 'react';
import { useState, useEffect, useCallback } from 'react';
import { FileCarousel } from './FileCarousel';
import { DisplayedFile } from './DisplayedFile';
export interface FileData {
id: number;
thumbnailPath?: string;
hasContent: boolean;
index: number;
contentHtml?: string;
fileDetailsHtml?: string;
}
interface PostFilesProps {
files: FileData[];
initialSelectedIndex?: number;
}
export const PostFiles: React.FC<PostFilesProps> = ({
files,
initialSelectedIndex = 0,
}) => {
const [selectedIndex, setSelectedIndex] = useState(initialSelectedIndex);
// Update URL parameter when selected file changes
const updateUrlWithFileIndex = (index: number) => {
if (typeof window === 'undefined' || files.length <= 1) return;
const url = new URL(window.location.href);
url.searchParams.set('idx', index.toString());
window.history.replaceState({}, '', url.toString());
};
const handleFileSelect = (fileId: number, index: number) => {
setSelectedIndex(index);
updateUrlWithFileIndex(index);
};
const navigateToNextFile = () => {
if (files.length > 1) {
const nextIndex = (selectedIndex + 1) % files.length;
handleFileSelect(files[nextIndex].id, nextIndex);
}
};
const navigateToPreviousFile = () => {
if (files.length > 1) {
const prevIndex = (selectedIndex - 1 + files.length) % files.length;
handleFileSelect(files[prevIndex].id, prevIndex);
}
};
// Add keyboard navigation
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
// Only handle arrow keys if we have multiple files
if (files.length <= 1) return;
switch (event.key) {
case 'ArrowLeft':
event.preventDefault();
navigateToPreviousFile();
break;
case 'ArrowRight':
event.preventDefault();
navigateToNextFile();
break;
}
};
// Add event listener to document
document.addEventListener('keydown', handleKeyDown);
// Cleanup event listener on unmount
return () => {
document.removeEventListener('keydown', handleKeyDown);
};
}, [selectedIndex, files.length]);
const selectedFile = files[selectedIndex];
return (
<section id="file-display-section">
{files.length > 1 && (
<FileCarousel
files={files}
totalFiles={files.length}
selectedIndex={selectedIndex}
onFileSelect={handleFileSelect}
/>
)}
{selectedFile && <DisplayedFile file={selectedFile} />}
</section>
);
};
export default PostFiles;