Feed funktioniert, infinite scroll funktioniert nicht mehr

This commit is contained in:
MisbehavedNinjaRadiator 2025-06-28 16:44:48 +02:00
parent 2e239cff48
commit cd38b295b7
3 changed files with 116 additions and 66 deletions

View file

@ -14,57 +14,79 @@ import FavoriteIcon from '@mui/icons-material/Favorite';
import ShareIcon from '@mui/icons-material/Share';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import MoreVertIcon from '@mui/icons-material/MoreVert';
import api from "../api/axios";
interface ExpandMoreProps extends IconButtonProps {
expand: boolean;
}
interface PostProps {
postId: number;
postId: string;
}
interface PostResponse {
description: string;
status: string;
likes: number;
tags: string[];
user: {
id: string;
name: string;
};
createdAt: string;
updatedAt: string;
images: {
originalName: string;
mimetype: string;
url: string;
}[];
following: boolean;
}
const ExpandMore = styled((props: ExpandMoreProps) => {
const { expand, ...other } = props;
return <IconButton {...other} />;
})(({ theme }) => ({
})<ExpandMoreProps>(({ theme, expand }) => ({
marginLeft: 'auto',
transition: theme.transitions.create('transform', {
duration: theme.transitions.duration.shortest,
}),
variants: [
{
props: ({ expand }) => !expand,
style: {
transform: 'rotate(0deg)',
},
},
{
props: ({ expand }) => !!expand,
style: {
transform: 'rotate(180deg)',
},
},
],
transform: expand ? 'rotate(180deg)' : 'rotate(0deg)',
}));
export default function Post({postId}: PostProps) {
export default function Post({ postId }: PostProps) {
const [expanded, setExpanded] = React.useState(false);
const content = "Fetch content here";
const expandedContent = "Fetch expanded here"
const title = "Fetch heading here";
const createdAt = "Fetch created at here";
const user = "Fetch user here";
const media = "Fetch media here (path)";
const [post, setPost] = React.useState<PostResponse | null>(null);
const handleExpandClick = () => {
setExpanded(!expanded);
};
React.useEffect(() => {
getPostbyID();
// eslint-disable-next-line
}, [postId]);
async function getPostbyID(): Promise<void> {
try {
const response = await api.get<PostResponse>(`/posts/getPost/{postId}?postId=${postId}`);
//const response = await api.get<PostResponse>(`http://localhost:3001/api/posts/getPost/{postId}?postId=${postId}`);
setPost(response.data);
} catch (error) {
console.error("Failed to fetch post:", error);
}
}
if (!post) {
return (
<Card sx={{ maxWidth: 345, margin: 2 }}>
<CardContent>
<Typography>Loading...</Typography>
</CardContent>
</Card>
);
}
return (
<Card sx={{ maxWidth: 345 }}>
<Card sx={{ maxWidth: 345, margin: 2 }}>
<CardHeader
avatar={
<Avatar sx={{ bgcolor: red[500] }} aria-label="recipe">
{user ? user.charAt(0).toUpperCase() : 'U'} //Todo: when fetching change to user.name or sth
<Avatar sx={{ bgcolor: red[500] }} aria-label="user">
{post.user.name.charAt(0).toUpperCase()}
</Avatar>
}
action={
@ -72,17 +94,32 @@ export default function Post({postId}: PostProps) {
<MoreVertIcon />
</IconButton>
}
title={title}
subheader= {createdAt}
/>
<CardMedia
component="img"
height="194"
image= {media}
title={post.user.name}
subheader={new Date(post.createdAt).toLocaleString()}
/>
{post.images && post.images.length > 0 && (
<CardMedia
component="img"
height="194"
image={post.images[0].url}
alt={post.images[0].originalName}
/>
)}
<CardContent>
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
{content}
<Typography variant="body1" sx={{ fontWeight: 600 }}>
{post.description}
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
Status: {post.status}
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
Likes: {post.likes}
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
Tags: {post.tags.join(", ")}
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
Following: {post.following ? "Ja" : "Nein"}
</Typography>
</CardContent>
<CardActions disableSpacing>
@ -94,7 +131,7 @@ export default function Post({postId}: PostProps) {
</IconButton>
<ExpandMore
expand={expanded}
onClick={handleExpandClick}
onClick={() => setExpanded(!expanded)}
aria-expanded={expanded}
aria-label="show more"
>
@ -103,7 +140,12 @@ export default function Post({postId}: PostProps) {
</CardActions>
<Collapse in={expanded} timeout="auto" unmountOnExit>
<CardContent>
<Typography sx={{ marginBottom: 2 }}>{expandedContent}</Typography>
<Typography variant="body2" color="text.secondary">
Erstellt am: {new Date(post.createdAt).toLocaleString()}
</Typography>
<Typography variant="body2" color="text.secondary">
Zuletzt aktualisiert: {new Date(post.updatedAt).toLocaleString()}
</Typography>
</CardContent>
</Collapse>
</Card>

View file

@ -1,34 +1,45 @@
import React, { useState, useEffect, useRef } from "react";
import Post from "../Post";
import "./feed.css";
import api from "../../api/axios";
interface PostListItem {
id: string;
createdAt: string;
description: string;
}
function Feed() {
const [posts, setPosts] = useState<number[]>([]);
const [posts, setPosts] = useState<PostListItem[]>([]);
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const [nextCursor, setNextCursor] = useState<string | null>(null);
const feedRef = useRef<HTMLDivElement | null>(null);
const PAGE_SIZE = 10;
// Dummy fetch function that simulates loading posts by ID
const fetchPosts = () => {
if (loading) return;
const fetchPosts = async () => {
if (loading || !hasMore) return;
setLoading(true);
try {
const params: any = { limit: PAGE_SIZE };
if (nextCursor) params.createdAt = nextCursor;
interface FeedResponse {
posts: PostListItem[];
setTimeout(() => {
setPosts((prev) => {
const newPosts = [];
for (let i = 0; i < PAGE_SIZE; i++) {
newPosts.push(prev.length + i + 1);
}
return [...prev, ...newPosts];
});
setLoading(false);
// Stop after 50 posts, just as an example
if (posts.length + PAGE_SIZE >= 50) {
setHasMore(false);
nextCursor: string | null;
}
}, 800);
const response = await api.get<FeedResponse>("/feed?limit=10");
//const response = await api.get<FeedResponse>("http://localhost:3001/api/feed?limit=10");
console.log("Feed response:", response.data);
const { posts: newPosts, nextCursor: newCursor } = response.data;
setPosts((prev) => [...prev, ...newPosts]);
setNextCursor(newCursor);
setHasMore(!!newCursor && newPosts.length > 0);
} catch (error) {
console.error("Error fetching posts:", error);
} finally {
setLoading(false);
}
};
useEffect(() => {
@ -39,24 +50,23 @@ function Feed() {
const onScroll = () => {
const feed = feedRef.current;
if (!feed || loading || !hasMore) return;
if (feed.scrollTop + feed.clientHeight >= feed.scrollHeight - 100) {
fetchPosts();
}
};
const feed = feedRef.current;
feed?.addEventListener("scroll", onScroll);
return () => {
feed?.removeEventListener("scroll", onScroll);
};
}, [loading, hasMore]);
}, [loading, hasMore, nextCursor]);
return (
<div className="feedContainer">
<main className="feedContent" ref={feedRef}>
{posts.map((postId) => (
<Post key={postId} postId={postId} />
{posts.length === 0 && !loading && <div>Keine Posts gefunden.</div>}
{posts.map((post) => (
<Post key={post.id} postId={post.id} />
))}
{loading && <div className="loading">Loading more posts...</div>}
{!hasMore && <div>No more posts</div>}

View file

@ -1,9 +1,7 @@
.feedContainer {
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
min-height: calc(100vh - var(--Header-height));
background-color: #f9f9f9;
}