From 653fbb73527bbf7576df169bdc8e081f07769198 Mon Sep 17 00:00:00 2001 From: EvanLin3141 Date: Mon, 3 Mar 2025 13:24:37 +0000 Subject: [PATCH 1/2] UPDATE: Following Page now Works --- frontend/src/App.tsx | 1 - frontend/src/pages/FollowUsers.tsx | 101 ++++++++++++++++++++++ frontend/src/pages/Following.tsx | 129 ++++++++++++++++------------- frontend/src/pages/UserPage.tsx | 17 ++-- 4 files changed, 180 insertions(+), 68 deletions(-) create mode 100644 frontend/src/pages/FollowUsers.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index ab3d870..29f1bf4 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -76,7 +76,6 @@ function App() { }> } /> } /> - } /> } /> diff --git a/frontend/src/pages/FollowUsers.tsx b/frontend/src/pages/FollowUsers.tsx new file mode 100644 index 0000000..9ed7009 --- /dev/null +++ b/frontend/src/pages/FollowUsers.tsx @@ -0,0 +1,101 @@ +import React, { useEffect, useState } from "react"; +import { useAuth } from "../context/AuthContext"; +import { useNavigate } from "react-router-dom"; +import DynamicPageContent from "../components/Layout/DynamicPageContent"; +import { useFollow } from "../hooks/useFollow"; +import { useAuthModal } from "../hooks/useAuthModal"; +import FollowUserButton from "../components/Input/FollowUserButton"; + +interface Streamer { + user_id: number; + username: string; +} + +interface FollowingStreamerProps { + extraClasses?: string; +} + +const FollowUsers: React.FC = ({ extraClasses = "" }) => { + const navigate = useNavigate(); + const { isLoggedIn } = useAuth(); + const [followedStreamers, setFollowedStreamers] = useState([]); + const [followingStatus, setFollowingStatus] = useState<{ [key: number]: boolean }>({}); // Store follow status for each streamer + + const { isFollowing, checkFollowStatus, followUser, unfollowUser } = + useFollow(); + + useEffect(() => { + const fetchFollowedStreamers = async () => { + try { + const response = await fetch("/api/user/following"); + if (!response.ok) throw new Error("Failed to fetch followed streamers"); + const data = await response.json(); + setFollowedStreamers(data); + + const updatedStatus: { [key: number]: boolean } = {}; + for (const streamer of data || []) { + const status = await checkFollowStatus(streamer.username); + updatedStatus[streamer.user_id] = Boolean(status); + } + setFollowingStatus(updatedStatus); + + console.log("Fetched Follow Status:", updatedStatus); // Log the status + } catch (error) { + console.error("Error fetching followed streamers:", error); + } + }; + + if (isLoggedIn) { + fetchFollowedStreamers(); + } + }, [isLoggedIn]); + + + const handleFollowToggle = async (userId: number) => { + const isCurrentlyFollowing = followingStatus[userId]; + + if (isCurrentlyFollowing) { + await unfollowUser(userId); + } else { + await followUser(userId); + } + + // Update local state for this specific streamer + setFollowingStatus((prev) => ({ + ...prev, + [userId]: !isCurrentlyFollowing, // Toggle based on previous state + })); + }; + + return ( + + + + ); +}; + +export default FollowUsers; \ No newline at end of file diff --git a/frontend/src/pages/Following.tsx b/frontend/src/pages/Following.tsx index 6566110..8b779b0 100644 --- a/frontend/src/pages/Following.tsx +++ b/frontend/src/pages/Following.tsx @@ -1,71 +1,79 @@ import React, { useEffect, useState } from "react"; import { useAuth } from "../context/AuthContext"; -import { useNavigate } from "react-router-dom"; +import { useLocation, useNavigate } from "react-router-dom"; import DynamicPageContent from "../components/Layout/DynamicPageContent"; import { useFollow } from "../hooks/useFollow"; -import { useAuthModal } from "../hooks/useAuthModal"; import FollowUserButton from "../components/Input/FollowUserButton"; interface Streamer { - user_id: number; - username: string; + user_id: number; + username: string; } -interface FollowingStreamerProps { +interface Category { + category_id: number; + category_name: string; +} + +interface FollowingProps { extraClasses?: string; } -const Following: React.FC = ({ extraClasses = "" }) => { +const Following: React.FC = ({ extraClasses = "" }) => { const navigate = useNavigate(); const { isLoggedIn } = useAuth(); const [followedStreamers, setFollowedStreamers] = useState([]); - const [followingStatus, setFollowingStatus] = useState<{ [key: number]: boolean }>({}); // Store follow status for each streamer + const [followedCategories, setFollowedCategories] = useState([]); + const [followingStatus, setFollowingStatus] = useState<{ [key: number]: boolean }>({}); - const { isFollowing, checkFollowStatus, followUser, unfollowUser } = - useFollow(); + const { checkFollowStatus, followUser, unfollowUser } = useFollow(); + + const location = useLocation(); + const queryParams = new URLSearchParams(location.search); + const initialTab = queryParams.get("tab") === "streamers" ? "streamers" : "categories"; + + const [activeTab, setActiveTab] = useState<"categories" | "streamers">(initialTab); + //const [followingStatus, setFollowingStatus] = useState>({}); useEffect(() => { - const fetchFollowedStreamers = async () => { + const newTab = queryParams.get("tab") as "categories" | "streamers"; + if (newTab) { + setActiveTab(newTab); + } + }, [location.search]); + + useEffect(() => { + const fetchFollowedContent = async () => { try { const response = await fetch("/api/user/following"); - if (!response.ok) throw new Error("Failed to fetch followed streamers"); - const data = await response.json(); - setFollowedStreamers(data); + if (!response.ok) throw new Error("Failed to fetch followed content"); + const data = await response.json(); + if (!Array.isArray(data.streams) || !Array.isArray(data.categories)) { + throw new Error("API response structure is incorrect"); + } + + setFollowedStreamers(data.streams); + setFollowedCategories(data.categories); + + // Fetch follow status for streamers const updatedStatus: { [key: number]: boolean } = {}; - for (const streamer of data || []) { + for (const streamer of data.streams) { const status = await checkFollowStatus(streamer.username); updatedStatus[streamer.user_id] = Boolean(status); } setFollowingStatus(updatedStatus); - - console.log("Fetched Follow Status:", updatedStatus); // Log the status } catch (error) { - console.error("Error fetching followed streamers:", error); + console.error("Error fetching followed content:", error); + setFollowedStreamers([]); + setFollowedCategories([]); } }; - if (isLoggedIn) { - fetchFollowedStreamers(); - } - }, [isLoggedIn]); - - - const handleFollowToggle = async (userId: number) => { - const isCurrentlyFollowing = followingStatus[userId]; - - if (isCurrentlyFollowing) { - await unfollowUser(userId); - } else { - await followUser(userId); + if (isLoggedIn) { + fetchFollowedContent(); } - - // Update local state for this specific streamer - setFollowingStatus((prev) => ({ - ...prev, - [userId]: !isCurrentlyFollowing, // Toggle based on previous state - })); - }; + }, [isLoggedIn]); return ( @@ -73,28 +81,33 @@ const Following: React.FC = ({ extraClasses = "" }) => { id="sidebar" className={`top-0 left-0 w-screen h-screen overflow-x-hidden flex flex-col bg-[var(--sideBar-bg)] text-[var(--sideBar-text)] text-center overflow-y-auto scrollbar-hide transition-all duration-500 ease-in-out ${extraClasses}`} > -
- {followedStreamers.map((streamer: any) => ( -
navigate(`/user/${streamer.username}`)}*/ - > - {streamer.username} - -
- ))} -
+ {activeTab === "streamers" && ( +
+ {followedStreamers.map((streamer: any) => ( +
navigate(`/user/${streamer.username}`)}*/ + > + {streamer.username} + +
+ ))} +
+ )} -
+ + + + ); }; diff --git a/frontend/src/pages/UserPage.tsx b/frontend/src/pages/UserPage.tsx index 4bd80dc..f97a394 100644 --- a/frontend/src/pages/UserPage.tsx +++ b/frontend/src/pages/UserPage.tsx @@ -82,13 +82,13 @@ const UserPage: React.FC = () => { const handleNavigation = (path: string) => { if (profilePicture === initialProfilePicture.current) { - // Variable hasn't changed - use React Router navigation - navigate(path); + // Variable hasn't changed - use React Router navigation + navigate(path); } else { - // Variable has changed - use full page reload - window.location.href = path; + // Variable has changed - use full page reload + window.location.href = path; } - }; + }; // Store initial profile picture to know if it changes later useEffect(() => { @@ -138,8 +138,7 @@ const UserPage: React.FC = () => { {/* Profile Picture */}
@@ -269,7 +268,7 @@ const UserPage: React.FC = () => { onMouseEnter={(e) => (e.currentTarget.style.boxShadow = "var(--follow-shadow)")} onMouseLeave={(e) => (e.currentTarget.style.boxShadow = "none")} > -
@@ -289,7 +288,7 @@ const UserPage: React.FC = () => { onMouseEnter={(e) => (e.currentTarget.style.boxShadow = "var(--follow-shadow)")} onMouseLeave={(e) => (e.currentTarget.style.boxShadow = "none")} > - + From 2494e40ffd09ee704397c812f63d03bb7ec156f1 Mon Sep 17 00:00:00 2001 From: EvanLin3141 Date: Mon, 3 Mar 2025 13:31:37 +0000 Subject: [PATCH 2/2] Refactor: Follow and Follow Categories in the same page --- frontend/src/App.tsx | 1 - frontend/src/pages/FollowUsers.tsx | 101 ---------------------- frontend/src/pages/FollowedCategories.tsx | 84 ------------------ frontend/src/pages/Following.tsx | 32 ++++++- 4 files changed, 30 insertions(+), 188 deletions(-) delete mode 100644 frontend/src/pages/FollowUsers.tsx delete mode 100644 frontend/src/pages/FollowedCategories.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 29f1bf4..0009939 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -15,7 +15,6 @@ import DashboardPage from "./pages/DashboardPage"; import { Brightness } from "./context/BrightnessContext"; import LoadingScreen from "./components/Layout/LoadingScreen"; import Following from "./pages/Following"; -import FollowedCategories from "./pages/FollowedCategories"; function App() { const [isLoggedIn, setIsLoggedIn] = useState(false); diff --git a/frontend/src/pages/FollowUsers.tsx b/frontend/src/pages/FollowUsers.tsx deleted file mode 100644 index 9ed7009..0000000 --- a/frontend/src/pages/FollowUsers.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { useAuth } from "../context/AuthContext"; -import { useNavigate } from "react-router-dom"; -import DynamicPageContent from "../components/Layout/DynamicPageContent"; -import { useFollow } from "../hooks/useFollow"; -import { useAuthModal } from "../hooks/useAuthModal"; -import FollowUserButton from "../components/Input/FollowUserButton"; - -interface Streamer { - user_id: number; - username: string; -} - -interface FollowingStreamerProps { - extraClasses?: string; -} - -const FollowUsers: React.FC = ({ extraClasses = "" }) => { - const navigate = useNavigate(); - const { isLoggedIn } = useAuth(); - const [followedStreamers, setFollowedStreamers] = useState([]); - const [followingStatus, setFollowingStatus] = useState<{ [key: number]: boolean }>({}); // Store follow status for each streamer - - const { isFollowing, checkFollowStatus, followUser, unfollowUser } = - useFollow(); - - useEffect(() => { - const fetchFollowedStreamers = async () => { - try { - const response = await fetch("/api/user/following"); - if (!response.ok) throw new Error("Failed to fetch followed streamers"); - const data = await response.json(); - setFollowedStreamers(data); - - const updatedStatus: { [key: number]: boolean } = {}; - for (const streamer of data || []) { - const status = await checkFollowStatus(streamer.username); - updatedStatus[streamer.user_id] = Boolean(status); - } - setFollowingStatus(updatedStatus); - - console.log("Fetched Follow Status:", updatedStatus); // Log the status - } catch (error) { - console.error("Error fetching followed streamers:", error); - } - }; - - if (isLoggedIn) { - fetchFollowedStreamers(); - } - }, [isLoggedIn]); - - - const handleFollowToggle = async (userId: number) => { - const isCurrentlyFollowing = followingStatus[userId]; - - if (isCurrentlyFollowing) { - await unfollowUser(userId); - } else { - await followUser(userId); - } - - // Update local state for this specific streamer - setFollowingStatus((prev) => ({ - ...prev, - [userId]: !isCurrentlyFollowing, // Toggle based on previous state - })); - }; - - return ( - - - - ); -}; - -export default FollowUsers; \ No newline at end of file diff --git a/frontend/src/pages/FollowedCategories.tsx b/frontend/src/pages/FollowedCategories.tsx deleted file mode 100644 index c71633d..0000000 --- a/frontend/src/pages/FollowedCategories.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import React, { useState, useEffect, useRef } from "react"; -import { useAuth } from "../context/AuthContext"; -import { useNavigate, useParams } from "react-router-dom"; -import DynamicPageContent from "../components/Layout/DynamicPageContent"; -import { useCategoryFollow } from "../hooks/useCategoryFollow"; -import FollowButton from "../components/Input/FollowButton"; -import { useAuthModal } from "../hooks/useAuthModal"; - - -interface Category { - isFollowing: boolean; - category_id: number; - category_name: string; -} - -interface FollowedCategoryProps { - extraClasses?: string; -} - -const FollowedCategories: React.FC = ({ extraClasses = "" }) => { - const navigate = useNavigate(); - const { username, isLoggedIn } = useAuth(); - const [followedCategories, setFollowedCategories] = useState([]); - const { categoryName } = useParams<{ categoryName: string }>(); - const { checkCategoryFollowStatus, followCategory, unfollowCategory } = useCategoryFollow(); - - - useEffect(() => { - if (categoryName) checkCategoryFollowStatus(categoryName); - }, [categoryName]); - - useEffect(() => { - if (!isLoggedIn) return; - - const fetchFollowedCategories = async () => { - try { - const response = await fetch("/api/categories/your_categories"); - if (!response.ok) throw new Error("Failed to fetch followed categories"); - const data = await response.json(); - setFollowedCategories(data); - } catch (error) { - console.error("Error fetching followed categories:", error); - } - }; - - fetchFollowedCategories(); - }, [isLoggedIn]); - - - return ( - - - - ); -}; - -export default FollowedCategories; diff --git a/frontend/src/pages/Following.tsx b/frontend/src/pages/Following.tsx index 8b779b0..b4a43c4 100644 --- a/frontend/src/pages/Following.tsx +++ b/frontend/src/pages/Following.tsx @@ -4,6 +4,7 @@ import { useLocation, useNavigate } from "react-router-dom"; import DynamicPageContent from "../components/Layout/DynamicPageContent"; import { useFollow } from "../hooks/useFollow"; import FollowUserButton from "../components/Input/FollowUserButton"; +import FollowButton from "../components/Input/FollowButton"; interface Streamer { user_id: number; @@ -11,6 +12,7 @@ interface Streamer { } interface Category { + isFollowing: boolean; category_id: number; category_name: string; } @@ -30,7 +32,7 @@ const Following: React.FC = ({ extraClasses = "" }) => { const location = useLocation(); const queryParams = new URLSearchParams(location.search); - const initialTab = queryParams.get("tab") === "streamers" ? "streamers" : "categories"; + const initialTab = queryParams.get("tab") === "streamers" ? "categories" : "categories"; const [activeTab, setActiveTab] = useState<"categories" | "streamers">(initialTab); //const [followingStatus, setFollowingStatus] = useState>({}); @@ -103,8 +105,34 @@ const Following: React.FC = ({ extraClasses = "" }) => { ))} )} + + {activeTab === "categories" && ( +
+ {followedCategories.map((category) => { + return ( +
navigate(`/category/${category.category_name}`)} + > + + {category.category_name} +
+ {category.category_name} +
+
+ ); + })} + +
+ )}; + - +