first commit
This commit is contained in:
100
src/App.jsx
Normal file
100
src/App.jsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Timeline from './components/Timeline';
|
||||
import PhotoDetail from './components/PhotoDetail';
|
||||
import Admin from './components/Admin';
|
||||
import { fetchPhotos } from './data/photos';
|
||||
import { Settings, Sun, Moon } from 'lucide-react';
|
||||
|
||||
function App() {
|
||||
const [view, setView] = useState(() => {
|
||||
return window.location.pathname === '/admin' ? 'admin' : 'timeline';
|
||||
});
|
||||
|
||||
const [activePhoto, setActivePhoto] = useState(null);
|
||||
const [photos, setPhotos] = useState([]);
|
||||
|
||||
// Theme State
|
||||
const [theme, setTheme] = useState(() => {
|
||||
return localStorage.getItem('theme_preference') || 'dark';
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
localStorage.setItem('theme_preference', theme);
|
||||
}, [theme]);
|
||||
|
||||
const toggleTheme = () => {
|
||||
setTheme(prev => prev === 'dark' ? 'light' : 'dark');
|
||||
};
|
||||
|
||||
const loadPhotos = async () => {
|
||||
const data = await fetchPhotos();
|
||||
setPhotos(data);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadPhotos();
|
||||
}, [view]);
|
||||
|
||||
// Handle browser back/forward
|
||||
useEffect(() => {
|
||||
const handlePopState = () => {
|
||||
setView(window.location.pathname === '/admin' ? 'admin' : 'timeline');
|
||||
};
|
||||
window.addEventListener('popstate', handlePopState);
|
||||
return () => window.removeEventListener('popstate', handlePopState);
|
||||
}, []);
|
||||
|
||||
// Update URL when view changes
|
||||
useEffect(() => {
|
||||
const path = view === 'admin' ? '/admin' : '/';
|
||||
if (window.location.pathname !== path) {
|
||||
window.history.pushState({}, '', path);
|
||||
}
|
||||
}, [view]);
|
||||
|
||||
if (view === 'admin') {
|
||||
return <Admin onBack={() => setView('timeline')} onUpdate={loadPhotos} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="app-container">
|
||||
{/* Theme Toggle */}
|
||||
<div style={{ position: 'fixed', top: '20px', right: '20px', zIndex: 50 }}>
|
||||
<button
|
||||
onClick={toggleTheme}
|
||||
style={{
|
||||
background: 'transparent',
|
||||
color: 'var(--text-primary)',
|
||||
padding: '8px',
|
||||
borderRadius: '50%',
|
||||
border: 'none',
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
opacity: 0.7,
|
||||
transition: 'opacity 0.2s'
|
||||
}}
|
||||
onMouseEnter={(e) => e.currentTarget.style.opacity = '1'}
|
||||
onMouseLeave={(e) => e.currentTarget.style.opacity = '0.7'}
|
||||
>
|
||||
{theme === 'dark' ? <Sun size={20} color="white" /> : <Moon size={20} color="black" />}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Main Timeline View */}
|
||||
<Timeline photos={photos} onSelectPhoto={setActivePhoto} />
|
||||
|
||||
{/* Detail Overlay */}
|
||||
{activePhoto && (
|
||||
<PhotoDetail
|
||||
photo={activePhoto}
|
||||
onClose={() => setActivePhoto(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
Reference in New Issue
Block a user