110 lines
3.3 KiB
JavaScript
110 lines
3.3 KiB
JavaScript
import { useEffect, useState } from 'react';
|
|
import {
|
|
Alert,
|
|
Box,
|
|
Card,
|
|
CardContent,
|
|
Grid,
|
|
IconButton,
|
|
Skeleton,
|
|
Tooltip,
|
|
Typography,
|
|
} from '@mui/material';
|
|
import FavoriteIcon from '@mui/icons-material/Favorite';
|
|
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
|
|
import { api } from '../api';
|
|
import { useAuth } from '../AuthContext';
|
|
|
|
export default function Browse() {
|
|
const { user } = useAuth();
|
|
const [quotes, setQuotes] = useState(null);
|
|
const [favoriteIds, setFavoriteIds] = useState(new Set());
|
|
const [error, setError] = useState('');
|
|
|
|
useEffect(() => {
|
|
api.list().then(setQuotes).catch((err) => setError(err.message));
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!user) {
|
|
setFavoriteIds(new Set());
|
|
return;
|
|
}
|
|
api
|
|
.favorites()
|
|
.then((rows) => setFavoriteIds(new Set(rows.map((r) => r.id))))
|
|
.catch(() => setFavoriteIds(new Set()));
|
|
}, [user]);
|
|
|
|
async function toggleFavorite(quoteId) {
|
|
if (!user) return;
|
|
const isFav = favoriteIds.has(quoteId);
|
|
try {
|
|
if (isFav) {
|
|
await api.removeFavorite(quoteId);
|
|
} else {
|
|
await api.addFavorite(quoteId);
|
|
}
|
|
setFavoriteIds((prev) => {
|
|
const next = new Set(prev);
|
|
isFav ? next.delete(quoteId) : next.add(quoteId);
|
|
return next;
|
|
});
|
|
} catch (err) {
|
|
setError(err.message);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Box>
|
|
<Typography variant="h4" component="h1" gutterBottom>
|
|
Browse quotes
|
|
</Typography>
|
|
{error && (
|
|
<Alert severity="error" sx={{ mb: 2 }} onClose={() => setError('')}>
|
|
{error}
|
|
</Alert>
|
|
)}
|
|
<Grid container spacing={2}>
|
|
{quotes === null &&
|
|
Array.from({ length: 6 }).map((_, i) => (
|
|
<Grid key={i} size={{ xs: 12, sm: 6, md: 4 }}>
|
|
<Skeleton variant="rounded" height={160} />
|
|
</Grid>
|
|
))}
|
|
{quotes?.map((q) => (
|
|
<Grid key={q.id} size={{ xs: 12, sm: 6, md: 4 }}>
|
|
<Card variant="outlined" sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
|
<CardContent sx={{ flexGrow: 1, display: 'flex', flexDirection: 'column', gap: 1 }}>
|
|
<Typography variant="body1" sx={{ flexGrow: 1 }}>
|
|
“{q.text}”
|
|
</Typography>
|
|
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
|
<Typography variant="body2" color="text.secondary">
|
|
— {q.author}
|
|
</Typography>
|
|
<Tooltip title={user ? 'Toggle favorite' : 'Sign in to favorite'}>
|
|
<span>
|
|
<IconButton
|
|
size="small"
|
|
disabled={!user}
|
|
onClick={() => toggleFavorite(q.id)}
|
|
>
|
|
{favoriteIds.has(q.id) ? (
|
|
<FavoriteIcon fontSize="small" sx={{ color: 'tertiary.main' }} />
|
|
) : (
|
|
<FavoriteBorderIcon fontSize="small" />
|
|
)}
|
|
</IconButton>
|
|
</span>
|
|
</Tooltip>
|
|
</Box>
|
|
</CardContent>
|
|
</Card>
|
|
</Grid>
|
|
))}
|
|
</Grid>
|
|
</Box>
|
|
);
|
|
}
|