diff --git a/frontend/config.tsx b/frontend/config.tsx deleted file mode 100644 index 59de80b..0000000 --- a/frontend/config.tsx +++ /dev/null @@ -1,5 +0,0 @@ -export const paths = { - pfps: "", - category_thumbnails: "", - icons: "", -}; diff --git a/frontend/package.json b/frontend/package.json index 2f9b28a..5952062 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "0.5.0", + "version": "0.15.0", "type": "module", "scripts": { "dev": "vite --config vite.config.dev.ts", diff --git a/frontend/src/components/Layout/ListItem.tsx b/frontend/src/components/Layout/ListItem.tsx index 9967149..a39f3da 100644 --- a/frontend/src/components/Layout/ListItem.tsx +++ b/frontend/src/components/Layout/ListItem.tsx @@ -110,6 +110,7 @@ interface VodListItemProps extends BaseListItemProps, Omit { } const VodListItem: React.FC = ({ + vod_id, title, username, category_name, @@ -137,7 +138,7 @@ const VodListItem: React.FC = ({

{title}

-

{username}

+ {variant != "vodDashboard" &&

{username}

}

{category_name}

{datetime}

@@ -147,20 +148,21 @@ const VodListItem: React.FC = ({
{variant === "vodDashboard" && (
- - +
)}
diff --git a/frontend/src/components/Layout/ListRow.tsx b/frontend/src/components/Layout/ListRow.tsx index b44c54f..e52e087 100644 --- a/frontend/src/components/Layout/ListRow.tsx +++ b/frontend/src/components/Layout/ListRow.tsx @@ -164,7 +164,6 @@ const ListRow = forwardRef((props, ref) => { username={item.username} isLive={item.isLive} viewers={item.viewers} - thumbnail={item.thumbnail} onItemClick={() => onItemClick(item.username)} extraClasses={itemExtraClasses} /> diff --git a/frontend/src/components/Navigation/Sidebar.tsx b/frontend/src/components/Navigation/Sidebar.tsx index 1451a3d..6502847 100644 --- a/frontend/src/components/Navigation/Sidebar.tsx +++ b/frontend/src/components/Navigation/Sidebar.tsx @@ -141,7 +141,7 @@ const Sidebar: React.FC = ({ extraClasses = "" }) => { return (
(window.location.href = `/category/${category.category_name}`)} > diff --git a/frontend/src/components/Stream/StreamDashboard.tsx b/frontend/src/components/Stream/StreamDashboard.tsx index 0c9aaa0..c33ebac 100644 --- a/frontend/src/components/Stream/StreamDashboard.tsx +++ b/frontend/src/components/Stream/StreamDashboard.tsx @@ -225,13 +225,13 @@ const StreamDashboard: React.FC = ({ username, userId, isL const handleEndStream = async () => { console.log("Ending stream..."); - const formData = new FormData(); - formData.append("key", streamData.stream_key); - try { const response = await fetch("/api/end_stream", { method: "POST", - body: formData, + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ key: streamData.stream_key }), }); if (response.ok) { @@ -375,7 +375,9 @@ const StreamDashboard: React.FC = ({ username, userId, isL streamCategory={streamData.category_name || "Category"} viewers={streamData.viewer_count} thumbnail={thumbnailPreview.url || ""} - onItemClick={() => {}} + onItemClick={() => { + window.open(`/${username}`, "_blank"); + }} extraClasses="max-w-[20vw]" />
diff --git a/frontend/src/hooks/useContent.ts b/frontend/src/hooks/useContent.ts index 64084d3..14c6357 100644 --- a/frontend/src/hooks/useContent.ts +++ b/frontend/src/hooks/useContent.ts @@ -23,6 +23,7 @@ const processVodData = (data: any[]): VodType[] => { // Helper function to process API data into our consistent types const processStreamData = (data: any[]): StreamType[] => { + if (!data || data.length === 0 || !data[0] || !data[0].user_id) return []; return data.map((stream) => ({ type: "stream", id: stream.user_id, @@ -76,8 +77,9 @@ export function useFetchContent( throw new Error(`Error fetching data: ${response.status}`); } - const rawData = await response.json(); - const processedData = processor(rawData); + const rawData = await response.json(); + let processedData = processor(Array.isArray(rawData) ? rawData : (rawData ? [rawData] : [])); + console.log("processedData", processedData); setData(processedData); setError(null); } catch (err) { @@ -126,7 +128,7 @@ export function useVods(customUrl?: string): { isLoading: boolean; error: string | null; } { - const url = customUrl || "api/vods/all"; //TODO: Change this to the correct URL or implement it + const url = customUrl || "api/vods/all"; const { data, isLoading, error } = useFetchContent(url, processVodData, [customUrl]); return { vods: data, isLoading, error }; diff --git a/frontend/src/hooks/useSameUser.ts b/frontend/src/hooks/useSameUser.ts deleted file mode 100644 index 6bf874a..0000000 --- a/frontend/src/hooks/useSameUser.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useEffect, useState } from "react" - -export function useSameUser({ username }: { username: string | undefined }) { - const [isSame, setIsSame] = useState(false); - - useEffect(() => { - const fetchStatus = async () => { - try { - const response = await fetch(`/api/user/same/${username}`); - if (!response.ok) { - throw new Error("Failed to validate user"); - } - const data = await response.json(); - setIsSame(data.same); - } catch (error) { - console.error("Error:", error); - } - }; - - fetchStatus(); - }, []); - - return isSame; -} \ No newline at end of file diff --git a/frontend/src/pages/UserPage.tsx b/frontend/src/pages/UserPage.tsx index ee3381c..fcffa20 100644 --- a/frontend/src/pages/UserPage.tsx +++ b/frontend/src/pages/UserPage.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useCallback } from "react"; import AuthModal from "../components/Auth/AuthModal"; import { useAuthModal } from "../hooks/useAuthModal"; import { useAuth } from "../context/AuthContext"; @@ -10,8 +10,8 @@ import DynamicPageContent from "../components/Layout/DynamicPageContent"; import LoadingScreen from "../components/Layout/LoadingScreen"; import { StreamListItem } from "../components/Layout/ListItem"; import { EditIcon } from "lucide-react"; -import { getCategoryThumbnail } from "../utils/thumbnailUtils"; -import { useSameUser } from "../hooks/useSameUser"; +import ListRow from "../components/Layout/ListRow"; +import { useStreams, useVods } from "../hooks/useContent"; interface UserProfileData { id: number; @@ -20,22 +20,38 @@ interface UserProfileData { followerCount: number; isPartnered: boolean; isLive: boolean; - currentStreamTitle?: string; - currentStreamCategory?: string; - currentStreamViewers?: number; - currentStreamStartTime?: string; - currentStreamThumbnail?: string; } const UserPage: React.FC = () => { - const [userPageVariant, setUserPageVariant] = useState<"personal" | "streamer" | "user" | "admin">("user"); + const [userPageVariant, setUserPageVariant] = useState<"personal" | "user" | "admin">("user"); const [profileData, setProfileData] = useState(); const { isFollowing, checkFollowStatus, followUser, unfollowUser } = useFollow(); const { showAuthModal, setShowAuthModal } = useAuthModal(); const { username: loggedInUsername } = useAuth(); const { username } = useParams(); - const isUser = useSameUser({ username }); + const { vods } = useVods(`/api/vods/${username}`); const navigate = useNavigate(); + const { streams } = useStreams(`/api/streams/${username}/data`); + const currentStream = streams[0]; + + const fetchProfileData = useCallback(async () => { + try { + // Profile data + const profileResponse = await fetch(`/api/user/${username}`); + const profileData = await profileResponse.json(); + setProfileData({ + id: profileData.user_id, + username: profileData.username, + bio: profileData.bio || "This user hasn't written a bio yet.", + followerCount: profileData.num_followers || 0, + isPartnered: profileData.isPartnered || false, + isLive: profileData.is_live, + }); + } catch (err) { + console.error("Error fetching profile data:", err); + window.location.href = "/404"; + } + }, [username]); // Saves uploaded image as profile picture for the user const saveUploadedImage = async (event: React.ChangeEvent) => { @@ -62,58 +78,20 @@ const UserPage: React.FC = () => { } }; + // Check if the current user is the currently logged-in user useEffect(() => { - // Fetch user profile data - fetch(`/api/user/${username}`) - .then((res) => res.json()) - .then((data) => { - setProfileData({ - id: data.user_id, - username: data.username, - bio: data.bio || "This user hasn't written a bio yet.", - followerCount: data.num_followers || 0, - isPartnered: data.isPartnered || false, - isLive: data.is_live, - currentStreamTitle: "", - currentStreamCategory: "", - currentStreamViewers: 0, - currentStreamThumbnail: "", - }); + if (username === loggedInUsername) setUserPageVariant("personal"); + // else if (data.isAdmin) setUserPageVariant("admin"); + else setUserPageVariant("user"); - if (data.is_live) { - // Fetch stream data for this streamer - fetch(`/api/streams/${data.user_id}/data`) - .then((res) => res.json()) - .then((streamData) => { - setProfileData((prevData) => { - if (!prevData) return prevData; - return { - ...prevData, - currentStreamTitle: streamData.title, - currentStreamCategory: streamData.category_id, - currentStreamViewers: streamData.num_viewers, - currentStreamStartTime: streamData.start_time, - currentStreamThumbnail: getCategoryThumbnail(streamData.category_name, streamData.thumbnail), - }; - }); - let variant: "user" | "streamer" | "personal" | "admin"; - if (username === loggedInUsername) variant = "personal"; - else if (streamData.title) variant = "streamer"; - // else if (data.isAdmin) variant = "admin"; - else variant = "user"; - setUserPageVariant(variant); - }) - .catch((err) => console.error("Error fetching stream data:", err)); - } - }) - .catch((err) => { - console.error("Error fetching profile data:", err); - navigate("/404"); - }); - - // Check if the *logged-in* user is following this user if (loggedInUsername && username) checkFollowStatus(username); - }, [username]); + }, [username, loggedInUsername, checkFollowStatus]); + + // Fetch user profile data + useEffect(() => { + if (!username) return; + fetchProfileData(); + }, [fetchProfileData]); if (!profileData) return ; @@ -148,16 +126,21 @@ const UserPage: React.FC = () => { } inset-0 z-20`} style={{ boxShadow: "var(--user-pfp-border-shadow)" }} > -