mirror of
https://github.com/bubblecup-12/VogelSocialMedia.git
synced 2025-07-13 16:20:49 +00:00
Feed and user Feed done
This commit is contained in:
parent
9cf6531f02
commit
04f4198ebf
29 changed files with 331 additions and 10621 deletions
|
@ -1,6 +1,6 @@
|
|||
import React, { useEffect, useRef, useState } from "react";
|
||||
import "./notFound.css";
|
||||
import ButtonPrimary from "../../components/ButtonRotkehlchen";
|
||||
import ButtonPrimary from "../../components/buttons/buttonRotkehlchen/ButtonRotkehlchen";
|
||||
|
||||
type Block = {
|
||||
x: number;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import "./loginAndSignUpPage.css";
|
||||
import { useEffect, useState } from "react";
|
||||
import api from "../api/axios";
|
||||
import ButtonRotkehlchen from "../components/ButtonRotkehlchen";
|
||||
import ButtonRotkehlchen from "../components/buttons/buttonRotkehlchen/ButtonRotkehlchen";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { useAuth } from "../api/Auth";
|
||||
import { createTheme, useMediaQuery } from "@mui/material";
|
||||
|
|
|
@ -5,7 +5,7 @@ import { useNavigate } from "react-router-dom";
|
|||
import Chip from '@mui/material/Chip';
|
||||
import Autocomplete from '@mui/material/Autocomplete';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import ButtonPrimary from "../components/ButtonRotkehlchen";
|
||||
import ButtonPrimary from "../components/buttons/buttonRotkehlchen/ButtonRotkehlchen";
|
||||
|
||||
import api from "../api/axios";
|
||||
import { useAuth } from "../api/Auth";
|
||||
|
|
|
@ -3,7 +3,7 @@ import QuiltedImageList from "../components/profile/QuiltedImageList";
|
|||
import { StyledEngineProvider, Divider } from "@mui/material";
|
||||
import ChangeAvatarDialog from "../components/profile/ChangeAvatarDialog";
|
||||
import Bio from "../components/profile/Bio";
|
||||
import RotkehlchenButton from "../components/ButtonRotkehlchen";
|
||||
import RotkehlchenButton from "../components/buttons/buttonRotkehlchen/ButtonRotkehlchen";
|
||||
import api, { redirectToLogin } from "../api/axios";
|
||||
import { useAuth } from "../api/Auth";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
|
@ -28,11 +28,11 @@ function Profile() {
|
|||
|
||||
const userProfile = async () => {
|
||||
try {
|
||||
const response = await api.get(`/profile/${username}`);
|
||||
const response = await api.get<{ data: UserProfile }>(`/profile/${username}`);
|
||||
setUserData(response.data.data);
|
||||
return;
|
||||
} catch (error) {
|
||||
navigate("/"); /* replace to 404 page */
|
||||
navigate("/notfound");
|
||||
console.error("Error fetching user profile:", error);
|
||||
return null;
|
||||
}
|
||||
|
|
136
code/frontend/src/pages/feed/Feed.tsx
Normal file
136
code/frontend/src/pages/feed/Feed.tsx
Normal file
|
@ -0,0 +1,136 @@
|
|||
import React, { useState, useEffect, useRef } from "react";
|
||||
import Post from "../../components/post/Post";
|
||||
import "./feed.css";
|
||||
import api from "../../api/axios";
|
||||
import WelcomeMessage from "../../components/welcomeMessage/welcomeMessage";
|
||||
import { useAuth } from "../../api/Auth";
|
||||
import LogInButton from "../../components/buttons/LogInButton";
|
||||
import SignUpButton from "../../components/buttons/SignUpButton";
|
||||
import NaggingFooter from "../../components/naggingFooter/NaggingFooter";
|
||||
import { useLocation, useParams } from "react-router-dom";
|
||||
|
||||
interface PostListItem {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface FeedProps {
|
||||
username?: string;
|
||||
}
|
||||
|
||||
function Feed({ username }: FeedProps) {
|
||||
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;
|
||||
const { user } = useAuth();
|
||||
const scrollTargetRef = useRef<string | null>(null);
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
// Remove the # character from the hash
|
||||
const hashValue = location.hash.replace("#", "");
|
||||
console.log(hashValue);
|
||||
console.log("Hash value:", hashValue);
|
||||
|
||||
if (hashValue) {
|
||||
scrollTargetRef.current = hashValue;
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
const fetchPosts = async () => {
|
||||
if (loading || !hasMore) return;
|
||||
setLoading(true);
|
||||
try {
|
||||
let url: string;
|
||||
if (username) {
|
||||
url = `/posts/getUserPosts/${encodeURIComponent(username)}`;
|
||||
const response = await api.get<{ posts: PostListItem[] }>(url);
|
||||
setPosts(response.data.posts);
|
||||
setHasMore(false);
|
||||
} else {
|
||||
url = `/feed?limit=${PAGE_SIZE}`;
|
||||
if (nextCursor) {
|
||||
url = `/feed?createdAt=${encodeURIComponent(
|
||||
nextCursor
|
||||
)}&limit=${PAGE_SIZE}`;
|
||||
}
|
||||
interface FeedResponse {
|
||||
posts: PostListItem[];
|
||||
nextCursor: string | null;
|
||||
}
|
||||
const response = await api.get<FeedResponse>(url);
|
||||
const { posts: newPosts, nextCursor: newCursor } = response.data;
|
||||
const tempPost: PostListItem[] = [...posts, ...newPosts];
|
||||
setPosts(tempPost);
|
||||
setNextCursor(newCursor);
|
||||
setHasMore(!!newCursor && newPosts.length > 0);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching posts:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
if (username) return;
|
||||
|
||||
const onScroll = () => {
|
||||
if (loading || !hasMore) return;
|
||||
if (
|
||||
window.innerHeight + window.scrollY >=
|
||||
document.body.offsetHeight - 100
|
||||
) {
|
||||
fetchPosts();
|
||||
}
|
||||
};
|
||||
window.addEventListener("scroll", onScroll);
|
||||
return () => {
|
||||
window.removeEventListener("scroll", onScroll);
|
||||
};
|
||||
}, [loading, hasMore, nextCursor, username]);
|
||||
|
||||
useEffect(() => {
|
||||
setPosts([]);
|
||||
setNextCursor(null);
|
||||
setHasMore(true);
|
||||
fetchPosts();
|
||||
}, [username]);
|
||||
|
||||
return (
|
||||
<div className={user ? "loggedInfeedContainer" : "feedContainer"}>
|
||||
{!user && (
|
||||
<div className="welcome-for-logged-out">
|
||||
<WelcomeMessage />
|
||||
<SignUpButton />
|
||||
<LogInButton />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<main className="feedContent" ref={feedRef}>
|
||||
{posts.length === 0 && !loading && <div>Keine Posts gefunden.</div>}
|
||||
{posts.map((post) => (
|
||||
<div id={post.id} key={post.id} className="feed-post-container">
|
||||
<Post
|
||||
postId={post.id}
|
||||
autoScroll={post.id === scrollTargetRef.current}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
{loading && <div className="loading">Loading more posts...</div>}
|
||||
{!hasMore && <div className="no-more-posts-message">No more posts</div>}
|
||||
</main>
|
||||
{!user && <NaggingFooter />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function UserFeedRoute() {
|
||||
const { user } = useParams<{ user: string }>();
|
||||
return <Feed username={user} />;
|
||||
}
|
||||
|
||||
export default Feed;
|
59
code/frontend/src/pages/feed/feed.css
Normal file
59
code/frontend/src/pages/feed/feed.css
Normal file
|
@ -0,0 +1,59 @@
|
|||
.feedContainer {
|
||||
min-height: 100vh;
|
||||
|
||||
}
|
||||
|
||||
.feedContent {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
.loading {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-top: 1rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
.feed-post-container {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
margin: 0.1rem auto;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.no-more-posts-message {
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Desktop view*/
|
||||
@media (min-width: 768px) {
|
||||
.feedContainer {
|
||||
display: grid;
|
||||
grid-template-columns: 40% 60%;
|
||||
}
|
||||
|
||||
.welcome-for-logged-out {
|
||||
position: sticky;
|
||||
top: 5rem;
|
||||
left: 5rem;
|
||||
right: 1rem;
|
||||
align-self: center;
|
||||
align-self: flex-start;
|
||||
}
|
||||
.feedContent {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 0 2rem 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -99,7 +99,7 @@
|
|||
justify-content: start;
|
||||
width: 50vw;
|
||||
height: 60vh;
|
||||
min-height: 400px;
|
||||
min-height: 60vh;
|
||||
min-width: 500px;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue