Add slide number to presentation mode route
- Update route to /presentations/:presentationId/present/:slideNumber - Add URL-synced navigation with goToSlide function - Update keyboard navigation to maintain URL state - Enable deep linking and browser navigation for slides - Start presentation from slide 1 in presentations list - Present button in viewer uses current slide number 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
3864de28e7
commit
7c7c8235f3
@ -26,7 +26,7 @@ function App() {
|
||||
<Route path="/presentations/new" element={<NewPresentationPage />} />
|
||||
<Route path="/presentations/:presentationId/edit/slides/:slideNumber" element={<PresentationEditor />} />
|
||||
<Route path="/presentations/:presentationId/view/slides/:slideNumber" element={<PresentationViewer />} />
|
||||
<Route path="/presentations/:presentationId/present" element={<PresentationMode />} />
|
||||
<Route path="/presentations/:presentationId/present/:slideNumber" element={<PresentationMode />} />
|
||||
<Route path="/presentations/:presentationId/slide/:slideId/edit" element={<SlideEditor />} />
|
||||
<Route path="/themes" element={<ThemeBrowser />} />
|
||||
<Route path="/themes/:themeId" element={<ThemeDetailPage />} />
|
||||
|
@ -10,15 +10,29 @@ import { loggers } from '../../utils/logger.ts';
|
||||
import './PresentationMode.css';
|
||||
|
||||
export const PresentationMode: React.FC = () => {
|
||||
const { presentationId } = useParams<{ presentationId: string }>();
|
||||
const { presentationId, slideNumber } = useParams<{
|
||||
presentationId: string;
|
||||
slideNumber: string;
|
||||
}>();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [presentation, setPresentation] = useState<Presentation | null>(null);
|
||||
const [theme, setTheme] = useState<Theme | null>(null);
|
||||
const [currentSlideIndex, setCurrentSlideIndex] = useState(0);
|
||||
const [currentSlideIndex, setCurrentSlideIndex] = useState(
|
||||
slideNumber ? parseInt(slideNumber, 10) - 1 : 0
|
||||
);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Navigate to specific slide and update URL
|
||||
const goToSlide = (slideIndex: number) => {
|
||||
if (!presentation) return;
|
||||
|
||||
const clampedIndex = Math.max(0, Math.min(slideIndex, presentation.slides.length - 1));
|
||||
setCurrentSlideIndex(clampedIndex);
|
||||
navigate(`/presentations/${presentationId}/present/${clampedIndex + 1}`, { replace: true });
|
||||
};
|
||||
|
||||
// Keyboard navigation handler
|
||||
const handleKeyPress = useCallback((event: KeyboardEvent) => {
|
||||
if (!presentation || presentation.slides.length === 0) return;
|
||||
@ -28,24 +42,22 @@ export const PresentationMode: React.FC = () => {
|
||||
case 'Space':
|
||||
case 'Enter':
|
||||
event.preventDefault();
|
||||
setCurrentSlideIndex(prev =>
|
||||
Math.min(prev + 1, presentation.slides.length - 1)
|
||||
);
|
||||
goToSlide(currentSlideIndex + 1);
|
||||
break;
|
||||
|
||||
case 'ArrowLeft':
|
||||
event.preventDefault();
|
||||
setCurrentSlideIndex(prev => Math.max(prev - 1, 0));
|
||||
goToSlide(currentSlideIndex - 1);
|
||||
break;
|
||||
|
||||
case 'Home':
|
||||
event.preventDefault();
|
||||
setCurrentSlideIndex(0);
|
||||
goToSlide(0);
|
||||
break;
|
||||
|
||||
case 'End':
|
||||
event.preventDefault();
|
||||
setCurrentSlideIndex(presentation.slides.length - 1);
|
||||
goToSlide(presentation.slides.length - 1);
|
||||
break;
|
||||
|
||||
case 'Escape':
|
||||
@ -55,14 +67,24 @@ export const PresentationMode: React.FC = () => {
|
||||
|
||||
default:
|
||||
// Handle number keys for direct slide navigation
|
||||
const slideNumber = parseInt(event.key);
|
||||
if (slideNumber >= 1 && slideNumber <= presentation.slides.length) {
|
||||
const slideNum = parseInt(event.key);
|
||||
if (slideNum >= 1 && slideNum <= presentation.slides.length) {
|
||||
event.preventDefault();
|
||||
setCurrentSlideIndex(slideNumber - 1);
|
||||
goToSlide(slideNum - 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}, [presentation, navigate, presentationId]);
|
||||
}, [presentation, currentSlideIndex, goToSlide]);
|
||||
|
||||
// Sync current slide index with URL parameter
|
||||
useEffect(() => {
|
||||
if (slideNumber) {
|
||||
const newIndex = parseInt(slideNumber, 10) - 1;
|
||||
if (newIndex >= 0 && newIndex !== currentSlideIndex) {
|
||||
setCurrentSlideIndex(newIndex);
|
||||
}
|
||||
}
|
||||
}, [slideNumber]);
|
||||
|
||||
// Set up keyboard listeners
|
||||
useEffect(() => {
|
||||
|
@ -86,7 +86,7 @@ export const PresentationViewer: React.FC = () => {
|
||||
|
||||
const enterPresentationMode = () => {
|
||||
if (!presentation) return;
|
||||
navigate(`/presentations/${presentationId}/present`);
|
||||
navigate(`/presentations/${presentationId}/present/${currentSlideIndex + 1}`);
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
|
@ -213,7 +213,7 @@ export const PresentationsList: React.FC = () => {
|
||||
<button
|
||||
type="button"
|
||||
className="card-action secondary"
|
||||
onClick={() => navigate(`/presentations/${presentation.metadata.id}/present`)}
|
||||
onClick={() => navigate(`/presentations/${presentation.metadata.id}/present/1`)}
|
||||
>
|
||||
Present
|
||||
</button>
|
||||
|
Loading…
Reference in New Issue
Block a user