FIX: General fixes and update to HomePage

This commit is contained in:
Chris-1010
2025-02-06 19:47:25 +00:00
parent a922036408
commit 1499e042cb
17 changed files with 116 additions and 115 deletions

View File

@@ -7,7 +7,7 @@
<title>Team Software Project</title>
</head>
<body>
<div id="root" class="min-h-screen h-full bg-gradient-to-tr from-[#07001F] via-[#1D0085] to-[#CC00AF]"></div>
<div id="root" class="min-h-screen h-full bg-gradient-to-tr from-[#2043ba] via-[#0026a6] to-[#63007a] overflow-x-hidden"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View File

@@ -21,7 +21,7 @@ export const Return: React.FC = () => {
const sessionId = urlParams.get("session_id");
if (sessionId) {
fetch(`${API_URL}/session-status?session_id=${sessionId}`)
fetch(`/api/session-status?session_id=${sessionId}`)
.then((res) => res.json())
.then((data) => {
setStatus(data.status);
@@ -56,7 +56,7 @@ interface CheckoutFormProps {
const CheckoutForm: React.FC<CheckoutFormProps> = ({ onClose }) => {
const fetchClientSecret = () => {
return fetch(`${API_URL}/create-checkout-session`, {
return fetch(`/api/create-checkout-session`, {
method: "POST",
})
.then((res) => res.json())

View File

@@ -19,40 +19,6 @@ interface ListRowProps {
onClick: (itemId: number, itemName: string) => void;
}
// Individual list entry component
const ListItem: React.FC<ListItemProps> = ({
type,
title,
streamer,
viewers,
thumbnail,
onItemClick,
}) => {
return (
<div
className="flex flex-col bg-gray-800 rounded-lg overflow-hidden cursor-pointer hover:bg-gray-700 transition-colors"
onClick={onItemClick}
>
<div className="relative w-full pt-[56.25%] ">
{thumbnail ? (
<img
src={thumbnail}
alt={title}
className="absolute top-0 left-0 w-full h-full object-cover"
/>
) : (
<div className="absolute top-0 left-0 w-full h-full bg-gray-600" />
)}
</div>
<div className="p-3 bg-white">
<h3 className="font-semibold text-lg">{title}</h3>
{type === "stream" && <p className="text-red-600">{streamer}</p>}
<p className="text-sm text-white">{viewers} viewers</p>
</div>
</div>
);
};
// Row of entries
const ListRow: React.FC<ListRowProps> = ({
title,
@@ -62,10 +28,10 @@ const ListRow: React.FC<ListRowProps> = ({
extraClasses="",
}) => {
return (
<div className={`flex flex-col space-y-4 py-6 px-5 mx-2 mt-5 rounded-md ${extraClasses}`}>
<div className={`flex flex-col space-y-4 py-6 bg-black/50 text-white px-5 mx-2 mt-5 rounded-[1.5rem] ${extraClasses}`}>
<div className="space-y-1">
<h2 className="text-2xl font-bold text-white">{title}</h2>
<p className="text-white">{description}</p>
<h2 className="text-2xl font-bold">{title}</h2>
<p>{description}</p>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
{items.map((item) => (
@@ -85,4 +51,38 @@ const ListRow: React.FC<ListRowProps> = ({
);
};
// Individual list entry component
const ListItem: React.FC<ListItemProps> = ({
type,
title,
streamer,
viewers,
thumbnail,
onItemClick,
}) => {
return (
<div
className="flex flex-col bg-purple-900 rounded-lg overflow-hidden cursor-pointer hover:bg-pink-700 hover:scale-105 transition-all"
onClick={onItemClick}
>
<div className="relative w-full pt-[56.25%] ">
{thumbnail ? (
<img
src={thumbnail}
alt={title}
className="absolute top-0 left-0 w-full h-full object-cover"
/>
) : (
<div className="absolute top-0 left-0 w-full h-full bg-gray-600" />
)}
</div>
<div className="p-3">
<h3 className="font-semibold text-lg text-center">{title}</h3>
{type === "stream" && <p className="font-bold">{streamer}</p>}
<p className="text-sm text-gray-300">{viewers} viewers</p>
</div>
</div>
);
};
export default ListRow;

View File

@@ -23,6 +23,7 @@ const Navbar: React.FC<NavbarProps> = ({
const [showAuthModal, setShowAuthModal] = useState(false);
const { isLoggedIn } = useAuth();
const [showSideBar, setShowSideBar] = useState(false);
useEffect(() => {
if (showAuthModal) {
document.body.style.overflow = "hidden";

View File

@@ -12,7 +12,7 @@ const StreamerRoute: React.FC = () => {
useEffect(() => {
const checkStreamStatus = async () => {
try {
const response = await fetch(`/api/streamer/${streamerName}/status`);
const response = await fetch(`/api/user/${streamerName}/status`);
const data = await response.json();
setIsLive(Boolean(data.is_live));
setStreamId(data.most_recent_stream);
@@ -44,7 +44,7 @@ const StreamerRoute: React.FC = () => {
// streamId=0 is a special case for the streamer's latest stream
return isLive ? (
<VideoPage streamId={streamId} />
<VideoPage streamerId={streamId} />
) : streamerName ? (
<UserPage />
) : (

View File

@@ -34,7 +34,7 @@ export function StreamsProvider({ children }: { children: React.ReactNode }) {
const { isLoggedIn } = useAuth();
const fetch_url = isLoggedIn
? ["/api/streams/recommended", "/api/categories/following"]
? ["/api/streams/recommended", "/api/categories/recommended"]
: ["/api/streams/popular/4", "/api/categories/popular/4"];
useEffect(() => {
@@ -44,7 +44,7 @@ export function StreamsProvider({ children }: { children: React.ReactNode }) {
.then((data: StreamItem[]) => {
const extractedData: StreamItem[] = data.map((stream: any) => ({
type: "stream",
id: stream.stream_id,
id: stream.user_id,
title: stream.title,
streamer: stream.username,
viewers: stream.num_viewers,

View File

@@ -12,8 +12,8 @@ const HomePage: React.FC<HomePageProps> = ({ variant = "default" }) => {
const { featuredStreams, featuredCategories } = useStreams();
const navigate = useNavigate();
const handleStreamClick = (streamId: number, streamerName: string) => {
console.log(`Navigating to stream ${streamId}`);
const handleStreamClick = (streamerId: number, streamerName: string) => {
console.log(`Navigating to stream by user ${streamerId}`);
navigate(`/${streamerName}`);
};
@@ -43,7 +43,7 @@ const HomePage: React.FC<HomePageProps> = ({ variant = "default" }) => {
}
items={featuredStreams}
onClick={handleStreamClick}
extraClasses="border border-gray-700 bg-[#FF7F50]/80"
extraClasses="bg-red-950/60"
/>
{/* If Personalised_HomePage, display Categories the logged-in user follows. Else, trending categories. */}
@@ -61,7 +61,7 @@ const HomePage: React.FC<HomePageProps> = ({ variant = "default" }) => {
}
items={featuredCategories}
onClick={handleCategoryClick}
extraClasses="border border-gray-700 bg-[#5AFF75]/80"
extraClasses="bg-green-950/60"
/>
</div>

View File

@@ -1,6 +1,7 @@
import React, { useState, useEffect } from "react";
import Navbar from "../components/Layout/Navbar";
import { useParams } from "react-router-dom";
import { useAuth } from "../context/AuthContext";
interface UserProfileData {
username: string;
@@ -11,19 +12,28 @@ interface UserProfileData {
const UserPage: React.FC = () => {
const [profileData, setProfileData] = useState<UserProfileData | null>(null);
const { username: loggedInUsername } = useAuth();
const { username } = useParams();
let userPageVariant = "user";
let setUserPageVariant = (currentStream: string) => {
if (username === loggedInUsername) userPageVariant = "personal";
else if (currentStream) userPageVariant = "streamer";
};
useEffect(() => {
// Fetch user profile data
fetch(`/api/get_streamer_data/${username}`)
fetch(`/api/user/${username}`)
.then((res) => res.json())
.then((data) => {
setProfileData({
username: data.username,
bio: data.bio || "This user hasn't written a bio yet.",
followerCount: data.num_followering || 0,
followerCount: data.num_followers || 0,
isPartnered: data.isPartnered || false,
});
setUserPageVariant(data.current_stream_title);
})
.catch((err) => console.error("Error fetching profile data:", err));
}, [username]);
@@ -42,9 +52,12 @@ const UserPage: React.FC = () => {
<div className="mx-auto px-4 py-8">
<div className="grid grid-cols-3 gap-8">
{/* Profile Section - Left Third */}
<div className="col-span-1 bg-gray-800 rounded-lg p-6 shadow-lg">
{/* Profile Picture */}
<div
id="profile"
className="col-span-1 bg-gray-800 rounded-lg p-6 shadow-lg"
>
<div className="flex flex-col items-center">
{/* Profile Picture */}
<div className="relative w-48 h-48 rounded-full overflow-hidden mb-6">
<img
src="/images/monkey.png"
@@ -65,9 +78,11 @@ const UserPage: React.FC = () => {
)}
</div>
{/* Username & Follower Count */}
<h1 className="text-3xl font-bold mb-2">
{profileData.username}
</h1>
<small className="text-green-400" >{userPageVariant.toUpperCase()}</small>
<div className="flex items-center space-x-2 mb-6">
<span className="text-gray-400">
@@ -111,7 +126,10 @@ const UserPage: React.FC = () => {
</div>
{/* Content Section */}
<div className="col-span-2 bg-gray-800 rounded-lg p-6 flex flex-col">
<div
id="content"
className="col-span-2 bg-gray-800 rounded-lg p-6 flex flex-col"
>
<h2 className="text-2xl font-bold mb-4">Past Broadcasts</h2>
<div className="text-gray-400 flex h-full rounded-none">
No past broadcasts found

View File

@@ -8,23 +8,21 @@ import VideoPlayer from "../components/Video/VideoPlayer";
import { SocketProvider } from "../context/SocketContext";
interface VideoPageProps {
streamId: number;
streamerId: number;
}
interface StreamDataProps {
streamId: number;
streamTitle: string;
streamerName: string;
streamerId: number;
startTime: string;
categoryName: string;
}
const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
const VideoPage: React.FC<VideoPageProps> = ({ streamerId }) => {
const { isLoggedIn } = useAuth();
const { streamerName } = useParams<{ streamerName: string }>();
const [streamData, setStreamData] = useState<StreamDataProps>();
const [viewerCount, setViewerCount] = useState(1000000);
const [viewerCount, setViewerCount] = useState(0);
const [isChatOpen, setIsChatOpen] = useState(true);
// const [showCheckout, setShowCheckout] = useState(false);
// const showReturn = window.location.search.includes("session_id");
@@ -44,11 +42,7 @@ const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
// }, [showCheckout]);
useEffect(() => {
// Fetch stream data for this streamer
fetch(
`/api/get_stream_data/${streamerName}${
streamId == 0 ? "" : `/${streamId}`
}`
).then((res) => {
fetch(`/api/streams/${streamerId}/data`).then((res) => {
if (!res.ok) {
console.error("Failed to load stream data:", res.statusText);
}
@@ -57,9 +51,7 @@ const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
.then((data) => {
// Transform snake_case to camelCase
const transformedData: StreamDataProps = {
streamId: streamId,
streamerName: data.username,
streamerId: data.user_id,
streamTitle: data.title,
startTime: data.start_time,
categoryName: data.category_name,
@@ -70,7 +62,7 @@ const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
console.error("Error fetching stream data:", error);
});
});
}, [streamId, streamerName]);
}, [streamerId]);
const toggleChat = () => {
setIsChatOpen((prev) => !prev);
@@ -88,7 +80,7 @@ const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
} grid-rows-[auto_1fr] bg-gray-900 h-full grid-cols-[auto_25vw] transition-all`}
>
<div className="relative">
<VideoPlayer streamId={streamId} />
<VideoPlayer streamId={streamerId} />
</div>
<ToggleButton
@@ -99,7 +91,10 @@ const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
{isChatOpen ? "Hide Chat" : "Show Chat"}
</ToggleButton>
<ChatPanel streamId={streamId} onViewerCountChange={(count: number) => setViewerCount(count)} />
<ChatPanel
streamId={streamerId}
onViewerCountChange={(count: number) => setViewerCount(count)}
/>
<div
id="stream-info"

View File

@@ -11,7 +11,7 @@ export default {
animation: {
moving_text_colour: "moving_text_colour 6s ease-in-out infinite alternate",
moving_bg: 'moving_bg 200s linear infinite',
moving_bg: 'moving_bg 50s linear infinite',
'border-spin': 'border-spin linear infinite',
floating: "floating 30s linear infinite",
burnIn: 'burnIn 1s ease-out',
@@ -26,13 +26,13 @@ export default {
moving_text_colour: {
"0%": { backgroundPosition: "0% 50%" },
"100%": { backgroundPosition: "100% 50%" },
moving_bg: {
'0%': { backgroundPosition: '0% 0%' },
'100%': { backgroundPosition: '100% 0%' },
}
},
moving_bg: {
'0%': { backgroundPosition: '0% 0%' },
'100%': { backgroundPosition: '-1280px 1280px' },
},
floating: {
'0%': { transform: 'translate(0px, -5px) rotateX(0deg) rotateY(0deg)' },
'5%': { transform: 'translate(-3px, -5.5px) rotateX(-0.35deg) rotateY(-0.55deg)' },