UPDATE/REFACTOR: Change how followed content is obtained for Sidebar

This commit is contained in:
Chris-1010
2025-03-01 00:35:34 +00:00
parent 3dbcf3d5c0
commit 3857d8d767
4 changed files with 98 additions and 93 deletions

View File

@@ -28,22 +28,23 @@ const Sidebar: React.FC<SideBarProps> = ({ extraClasses = "" }) => {
const [justToggled, setJustToggled] = useState(false); const [justToggled, setJustToggled] = useState(false);
const sidebarId = useRef(Math.floor(Math.random() * 1000000)); const sidebarId = useRef(Math.floor(Math.random() * 1000000));
// Fetch followed streamers // Fetch followed streamers & categories
useEffect(() => { useEffect(() => {
if (!isLoggedIn) return; if (!isLoggedIn) return;
const fetchFollowedStreamers = async () => { const fetchFollowData = async () => {
try { try {
const response = await fetch("/api/user/following"); const response = await fetch("/api/user/following");
if (!response.ok) throw new Error("Failed to fetch followed streamers"); if (!response.ok) throw new Error("Failed to fetch followed content");
const data = await response.json(); const data = await response.json();
setFollowedStreamers(data); setFollowedStreamers(data.streams);
setFollowedCategories(data.categories);
} catch (error) { } catch (error) {
console.error("Error fetching followed streamers:", error); console.error("Error fetching followed content:", error);
} }
}; };
fetchFollowedStreamers(); fetchFollowData();
}, [isLoggedIn]); }, [isLoggedIn]);
const handleSideBar = () => { const handleSideBar = () => {
@@ -70,24 +71,6 @@ const Sidebar: React.FC<SideBarProps> = ({ extraClasses = "" }) => {
}; };
}, [showSideBar, setShowSideBar, isLoggedIn]); }, [showSideBar, setShowSideBar, isLoggedIn]);
useEffect(() => {
if (!isLoggedIn) return;
const fetchFollowedCategories = async () => {
try {
const response = await fetch("/api/categories/following");
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 ( return (
<> <>
<ToggleButton <ToggleButton
@@ -160,7 +143,7 @@ const Sidebar: React.FC<SideBarProps> = ({ extraClasses = "" }) => {
Streamers Streamers
</h2> </h2>
<div className="flex flex-col flex-grow justify-evenly w-full"> <div className="flex flex-col flex-grow justify-evenly w-full">
{followedStreamers.map((streamer: any) => ( {followedStreamers.map((streamer) => (
<button <button
key={`${sidebarId.current}-streamer-${streamer.username}`} key={`${sidebarId.current}-streamer-${streamer.username}`}
className="cursor-pointer bg-black w-full py-2 border border-[--text-color] rounded-lg text-white hover:text-purple-500 font-bold transition-colors" className="cursor-pointer bg-black w-full py-2 border border-[--text-color] rounded-lg text-white hover:text-purple-500 font-bold transition-colors"
@@ -192,7 +175,7 @@ const Sidebar: React.FC<SideBarProps> = ({ extraClasses = "" }) => {
className="group relative flex flex-col items-center justify-center h-full max-h-[50px] border border-[--text-color] className="group relative flex flex-col items-center justify-center h-full max-h-[50px] border border-[--text-color]
rounded-lg overflow-hidden hover:shadow-lg transition-all text-white hover:text-purple-500 cursor-pointer" rounded-lg overflow-hidden hover:shadow-lg transition-all text-white hover:text-purple-500 cursor-pointer"
onClick={() => onClick={() =>
window.location.href = `/category/${category.category_name}` (window.location.href = `/category/${category.category_name}`)
} }
> >
<img <img

View File

@@ -7,74 +7,63 @@ import DynamicPageContent from "../components/Layout/DynamicPageContent";
import LoadingScreen from "../components/Layout/LoadingScreen"; import LoadingScreen from "../components/Layout/LoadingScreen";
import Footer from "../components/Layout/Footer"; import Footer from "../components/Layout/Footer";
const HomePage: React.FC = () => {
const { streams, isLoading: isLoadingStreams } = useStreams();
const { categories, isLoading: isLoadingCategories } = useCategories();
const { vods, isLoading: isLoadingVods } = useVods();
const navigate = useNavigate();
interface HomePageProps { const handleVodClick = (vodUrl: string) => {
variant?: "default" | "personalised"; window.open(vodUrl, "_blank");
} };
if (isLoadingStreams || isLoadingCategories || isLoadingVods) return <LoadingScreen>Loading Content...</LoadingScreen>;
const HomePage: React.FC<HomePageProps> = ({ variant = "default" }) => { return (
const { streams, isLoading: isLoadingStreams } = useStreams(); <DynamicPageContent navbarVariant="home" className="relative min-h-screen animate-moving_bg" contentClassName="pb-[12vh]">
const { categories, isLoading: isLoadingCategories } = useCategories(); {/* Streams Section */}
const { vods, isLoading: isLoadingVods } = useVods(); // Fetch VODs <ListRow
const navigate = useNavigate(); type="stream"
title="Streams - Live Now"
description="Popular streamers that are currently live!"
items={streams}
wrap={false}
onItemClick={(streamerName) => navigate(`/${streamerName}`)}
extraClasses="bg-[var(--liveNow)]"
itemExtraClasses="w-[20vw]"
/>
const handleVodClick = (vodUrl: string) => { {/* Categories Section */}
window.open(vodUrl, "_blank"); // Open VOD in new tab <ListRow
}; type="category"
title="Trending Categories"
description="Recently popular categories lately!"
items={categories}
wrap={false}
onItemClick={(categoryName) => navigate(`/category/${categoryName}`)}
titleClickable={true}
extraClasses="bg-[var(--recommend)]"
itemExtraClasses="w-[20vw]"
>
<Button extraClasses="absolute right-10" onClick={() => navigate("/categories")}>
Show All
</Button>
</ListRow>
if (isLoadingStreams || isLoadingCategories || isLoadingVods) {/* VODs Section */}
return <LoadingScreen>Loading Content...</LoadingScreen>; <ListRow
type="vod"
return ( title="Recent VODs"
<DynamicPageContent description="Watch the latest recorded streams!"
navbarVariant="home" items={vods}
className="relative min-h-screen animate-moving_bg" wrap={false}
contentClassName="pb-[12vh]" onItemClick={handleVodClick}
> extraClasses="bg-black/50"
{/* Streams Section */} itemExtraClasses="w-[20vw]"
<ListRow />
type="stream" <Footer />
title="Streams - Live Now" </DynamicPageContent>
description="Popular streamers that are currently live!" );
items={streams}
wrap={false}
onItemClick={(streamerName) => navigate(`/${streamerName}`)}
extraClasses="bg-[var(--liveNow)]"
itemExtraClasses="w-[20vw]"
/>
{/* Categories Section */}
<ListRow
type="category"
title="Trending Categories"
description="Recently popular categories lately!"
items={categories}
wrap={false}
onItemClick={(categoryName) => navigate(`/category/${categoryName}`)}
titleClickable={true}
extraClasses="bg-[var(--recommend)]"
itemExtraClasses="w-[20vw]"
>
<Button extraClasses="absolute right-10" onClick={() => navigate("/categories")}>
Show All
</Button>
</ListRow>
{/* VODs Section */}
<ListRow
type="vod"
title="Recent VODs"
description="Watch the latest recorded streams!"
items={vods}
wrap={false}
onItemClick={handleVodClick}
extraClasses="bg-black/50"
itemExtraClasses="w-[20vw]"
/>
<Footer />
</DynamicPageContent>
);
}; };
export default HomePage; export default HomePage;

View File

@@ -132,14 +132,15 @@ def user_unfollow(target_user_id: int):
@login_required @login_required
@user_bp.route('/user/following') @user_bp.route('/user/following')
def user_followed_streamers(): def user_followed_content():
""" """
Queries DB to get a list of followed streamers Queries DB to get a dict of followed users and categories
""" """
user_id = session.get('user_id') user_id = session.get('user_id')
live_following_streams = get_followed_streamers(user_id) streams = get_followed_streamers(user_id)
return live_following_streams categories = get_followed_categories(user_id)
return jsonify({"streams": streams, "categories": categories})
@login_required @login_required
@user_bp.route('/user/category/follow/<string:category_name>') @user_bp.route('/user/category/follow/<string:category_name>')

View File

@@ -216,6 +216,16 @@ def get_email(user_id: int) -> Optional[str]:
def get_followed_streamers(user_id: int) -> Optional[List[dict]]: def get_followed_streamers(user_id: int) -> Optional[List[dict]]:
""" """
Returns a list of streamers who the user follows Returns a list of streamers who the user follows
Returns:
List of dictionaries with the following structure:
[
{
"user_id": int,
"username": str
},
...
]
""" """
with Database() as db: with Database() as db:
followed_streamers = db.fetchall(""" followed_streamers = db.fetchall("""
@@ -225,6 +235,28 @@ def get_followed_streamers(user_id: int) -> Optional[List[dict]]:
""", (user_id,)) """, (user_id,))
return followed_streamers return followed_streamers
def get_followed_categories(user_id: int) -> Optional[List[dict]]:
"""
Returns a list of categories that the user follows
Returns:
List of dictionaries with the following structure:
[
{
"category_id": int,
"category_name": str
},
...
]
"""
with Database() as db:
followed_categories = db.fetchall("""
SELECT category_id, category_name
FROM categories
WHERE category_id IN (SELECT category_id FROM followed_categories WHERE user_id = ?);
""", (user_id,))
return followed_categories
def get_user(user_id: int) -> Optional[dict]: def get_user(user_id: int) -> Optional[dict]:
""" """
Returns information about a user from user_id Returns information about a user from user_id