infinity scrolling init

This commit is contained in:
2025-11-07 21:27:29 +05:30
parent 13d52ec590
commit ce7b5dab6b
2 changed files with 62 additions and 19 deletions

View File

@@ -61,7 +61,14 @@ export default function Blog(props: { disableCustomTheme?: boolean }) {
{selectedArticle === null ? (
<>
<MainContent articles={articles} onSelectArticle={handleSelectArticle} />
<Latest articles={articles.slice(0, 3)} /> {/* show 3 most recent */}
<Latest
articles={articles}
onSelectArticle={handleSelectArticle}
onLoadMore={async (offset, limit) => {
// Optional: fetch more from API (if you want true pagination)
// await fetchMoreArticles(offset, limit);
}}
/>
</>
) : (
<Article article={articles[selectedArticle]} onBack={handleBack} />

View File

@@ -3,10 +3,10 @@ import Avatar from '@mui/material/Avatar';
import AvatarGroup from '@mui/material/AvatarGroup';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import Pagination from '@mui/material/Pagination';
import Typography from '@mui/material/Typography';
import { styled } from '@mui/material/styles';
import NavigateNextRoundedIcon from '@mui/icons-material/NavigateNextRounded';
import CircularProgress from '@mui/material/CircularProgress';
import type { Article } from '../providers/Article'; // ✅ import type for correctness
@@ -92,19 +92,58 @@ function Author({ authors }: { authors: { name: string; avatar: string }[] }) {
interface LatestProps {
articles: Article[];
onSelectArticle?: (index: number) => void;
onLoadMore?: (offset: number, limit: number) => Promise<void>; // optional async callback
}
export default function Latest({ articles, onSelectArticle }: LatestProps) {
const [focusedCardIndex, setFocusedCardIndex] = React.useState<number | null>(null);
export default function Latest({ articles, onSelectArticle, onLoadMore }: LatestProps) {
const [visibleCount, setVisibleCount] = React.useState(2);
const [loadingMore, setLoadingMore] = React.useState(false);
const handleFocus = (index: number) => setFocusedCardIndex(index);
const handleBlur = () => setFocusedCardIndex(null);
const displayedArticles = articles.slice(0, visibleCount);
// limit to 4-6 items for visual balance
const displayedArticles = articles.slice(0, 6);
// Intersection Observer ref
const loaderRef = React.useRef<HTMLDivElement | null>(null);
React.useEffect(() => {
if (!loaderRef.current) return;
const observer = new IntersectionObserver(
async (entries) => {
const first = entries[0];
if (first.isIntersecting && !loadingMore && visibleCount < articles.length) {
console.log('🟡 Intersection triggered — loading more blogs...');
setLoadingMore(true);
if (onLoadMore) {
console.log(`📡 Calling onLoadMore(offset=${visibleCount}, limit=2)`);
await onLoadMore(visibleCount, 2);
}
setVisibleCount((prev) => {
const newCount = prev + 2;
console.log(`✅ Increasing visibleCount from ${prev}${newCount}`);
return newCount;
});
setLoadingMore(false);
}
},
{ threshold: 0.5 }
);
const current = loaderRef.current;
observer.observe(current);
console.log('👀 IntersectionObserver attached to loaderRef:', loaderRef.current);
return () => {
if (current) observer.unobserve(current);
console.log('🧹 IntersectionObserver detached');
};
}, [loadingMore, visibleCount, articles.length, onLoadMore]);
return (
<div>
<Box>
<Typography variant="h2" gutterBottom>
Latest
</Typography>
@@ -128,17 +167,12 @@ export default function Latest({ articles, onSelectArticle }: LatestProps) {
gutterBottom
variant="h6"
tabIndex={0}
onFocus={() => handleFocus(index)}
onBlur={handleBlur}
onClick={() => onSelectArticle?.(index)}
className={focusedCardIndex === index ? 'Mui-focused' : ''}
>
{article.title}
<NavigateNextRoundedIcon
className="arrow"
sx={{ fontSize: '1rem' }}
/>
<NavigateNextRoundedIcon className="arrow" sx={{ fontSize: '1rem' }} />
</TitleTypography>
<StyledTypography variant="body2" color="text.secondary" gutterBottom>
{article.description}
</StyledTypography>
@@ -148,9 +182,11 @@ export default function Latest({ articles, onSelectArticle }: LatestProps) {
</Grid>
))}
</Grid>
<Box sx={{ display: 'flex', flexDirection: 'row', pt: 4 }}>
<Pagination hidePrevButton hideNextButton count={10} boundaryCount={10} />
{/* Infinite scroll loader */}
<Box ref={loaderRef} sx={{ display: 'flex', justifyContent: 'center', py: 3 }}>
{loadingMore && <CircularProgress size={28} />}
</Box>
</div>
</Box>
);
}