UPDATE: Fix to stream/userpage routing, Added UserPage and Tidy to code;
Added ability to visit a user's profile page from their stream; Cleaned up code formatting, primarily changing from single quotes to double quotes; Removed unused SignupForm component;
This commit is contained in:
@@ -13,7 +13,7 @@ const HomePage: React.FC<HomePageProps> = ({ variant = "default" }) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleStreamClick = (streamId: number, streamerName: string) => {
|
||||
console.log(`Navigating to ${streamId}`);
|
||||
console.log(`Navigating to stream ${streamId}`);
|
||||
navigate(`/${streamerName}`);
|
||||
};
|
||||
|
||||
@@ -28,20 +28,34 @@ const HomePage: React.FC<HomePageProps> = ({ variant = "default" }) => {
|
||||
{/* If Personalised_HomePage, display Streams recommended for the logged-in user. Else, live streams with the most viewers. */}
|
||||
<ListRow
|
||||
type="stream"
|
||||
title={"Live Now" + (variant === "personalised" ? " - Recommended" : "")}
|
||||
description={variant === "personalised" ? "We think you might like these streams - Streamers recommended for you" : "Streamers that are currently live"}
|
||||
title={
|
||||
"Live Now" + (variant === "personalised" ? " - Recommended" : "")
|
||||
}
|
||||
description={
|
||||
variant === "personalised"
|
||||
? "We think you might like these streams - Streamers recommended for you"
|
||||
: "Streamers that are currently live"
|
||||
}
|
||||
items={featuredStreams}
|
||||
onClick={handleStreamClick}
|
||||
/>
|
||||
|
||||
{/* If Personalised_HomePage, display Categories the logged-in user follows. Else, trending categories. */}
|
||||
<ListRow
|
||||
type="category"
|
||||
title={variant === "personalised" ? "Followed Categories" : "Trending Categories"}
|
||||
description={variant === "personalised" ? "Current streams from your followed categories" : "Categories that have been 'popping off' lately"}
|
||||
title={
|
||||
variant === "personalised"
|
||||
? "Followed Categories"
|
||||
: "Trending Categories"
|
||||
}
|
||||
description={
|
||||
variant === "personalised"
|
||||
? "Current streams from your followed categories"
|
||||
: "Categories that have been 'popping off' lately"
|
||||
}
|
||||
items={featuredCategories}
|
||||
onClick={() => {}} //TODO
|
||||
/>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
|
||||
const NotFoundPage: React.FC = () => {
|
||||
return (
|
||||
<div>
|
||||
</div>
|
||||
);
|
||||
return <div></div>;
|
||||
};
|
||||
|
||||
export default NotFoundPage;
|
||||
export default NotFoundPage;
|
||||
|
||||
@@ -1,14 +1,124 @@
|
||||
import React from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Navbar from "../components/Layout/Navbar";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
interface UserPageProps {
|
||||
interface UserProfileData {
|
||||
username: string;
|
||||
bio: string;
|
||||
followerCount: number;
|
||||
isPartnered: boolean;
|
||||
}
|
||||
|
||||
const UserPage: React.FC<UserPageProps> = ({ username }) => {
|
||||
const UserPage: React.FC = () => {
|
||||
const [profileData, setProfileData] = useState<UserProfileData | null>(null);
|
||||
const { username } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
// Fetch user profile data
|
||||
fetch(`/api/get_streamer_data/${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,
|
||||
isPartnered: data.isPartnered || false,
|
||||
});
|
||||
})
|
||||
.catch((err) => console.error("Error fetching profile data:", err));
|
||||
}, [username]);
|
||||
|
||||
if (!profileData) {
|
||||
return (
|
||||
<div className="h-screen w-screen flex items-center justify-center text-white">
|
||||
Loading...
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-[#808080] h-screen w-screen flex flex-col items-center justify-center">
|
||||
<h1>{username}</h1>
|
||||
<p>Profile page for {username}</p>
|
||||
<div className="min-h-screen bg-gray-900 text-white">
|
||||
<Navbar />
|
||||
<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 className="flex flex-col items-center">
|
||||
<div className="relative w-48 h-48 rounded-full overflow-hidden mb-6">
|
||||
<img
|
||||
src="/images/monkey.png"
|
||||
alt={`${profileData.username}'s profile`}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
|
||||
{profileData.isPartnered && (
|
||||
<div className="absolute bottom-2 right-2 bg-purple-600 rounded-full p-2">
|
||||
<svg
|
||||
className="w-6 h-6 text-white"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<h1 className="text-3xl font-bold mb-2">
|
||||
{profileData.username}
|
||||
</h1>
|
||||
|
||||
<div className="flex items-center space-x-2 mb-6">
|
||||
<span className="text-gray-400">
|
||||
{profileData.followerCount.toLocaleString()} followers
|
||||
</span>
|
||||
{profileData.isPartnered && (
|
||||
<span className="bg-purple-600 text-white text-sm px-2 py-1 rounded">
|
||||
Partner
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<button className="w-full bg-purple-600 hover:bg-purple-700 text-white font-bold py-2 px-4 rounded-lg mb-4 transition-colors">
|
||||
Follow
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Bio Section */}
|
||||
<div className="mt-6">
|
||||
<h2 className="text-xl font-semibold mb-3">
|
||||
About {profileData.username}
|
||||
</h2>
|
||||
<p className="text-gray-300 whitespace-pre-wrap">
|
||||
{profileData.bio}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Additional Stats */}
|
||||
<div className="mt-6 pt-6 border-t border-gray-700">
|
||||
<div className="grid grid-cols-2 gap-4 text-center">
|
||||
<div>
|
||||
<div className="text-2xl font-bold text-purple-400">0</div>
|
||||
<div className="text-sm text-gray-400">Total Views</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-2xl font-bold text-purple-400">0</div>
|
||||
<div className="text-sm text-gray-400">Following</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content Section */}
|
||||
<div 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
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Navbar from "../components/Layout/Navbar";
|
||||
import Button, { ToggleButton } from "../components/Layout/Button";
|
||||
import Button from "../components/Layout/Button";
|
||||
import ChatPanel from "../components/Video/ChatPanel";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useAuth } from "../context/AuthContext";
|
||||
import VideoPlayer from "../components/Video/VideoPlayer";
|
||||
import { SocketProvider } from "../context/SocketContext";
|
||||
@@ -28,7 +28,7 @@ const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
|
||||
const [isChatOpen, setIsChatOpen] = useState(true);
|
||||
// const [showCheckout, setShowCheckout] = useState(false);
|
||||
// const showReturn = window.location.search.includes("session_id");
|
||||
// const navigate = useNavigate();
|
||||
const navigate = useNavigate();
|
||||
|
||||
// useEffect(() => {
|
||||
// // Prevent scrolling when checkout is open
|
||||
@@ -45,8 +45,7 @@ const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
|
||||
useEffect(() => {
|
||||
// Fetch stream data for this streamer
|
||||
fetch(
|
||||
`/api/get_stream_data/${streamerName}${streamId == 0 ? "" : `/${streamId}`
|
||||
}`
|
||||
`/api/get_stream_data/${streamerName}${streamId == 0 ? "" : `/${streamId}`}`,
|
||||
).then((res) => {
|
||||
if (!res.ok) {
|
||||
console.error("Failed to load stream data:", res.statusText);
|
||||
@@ -80,7 +79,10 @@ const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
|
||||
<SocketProvider>
|
||||
<div id="videoPage" className="w-full">
|
||||
<Navbar isChatOpen={isChatOpen} toggleChat={toggleChat} />
|
||||
<div id="container" className={`grid grid-rows-[auto_1fr] bg-gray-900 h-full ${isChatOpen ? "grid-cols-[3fr_1fr]" : "grid-cols-1"}`}
|
||||
|
||||
<div
|
||||
id="container"
|
||||
className={`grid grid-rows-[auto_1fr] bg-gray-900 h-full ${isChatOpen ? "grid-cols-[3fr_1fr]" : "grid-cols-1"}`}
|
||||
>
|
||||
<div className="relative">
|
||||
<VideoPlayer streamId={streamId} />
|
||||
@@ -103,8 +105,8 @@ const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
|
||||
{streamData ? streamData.streamTitle : "Loading..."}
|
||||
</h1>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-semibold">Streamer:</span>
|
||||
<div id="streamer" className="flex items-center gap-2 cursor-pointer" onClick={() => navigate(`/user/${streamerName}`)} >
|
||||
<img src="/images/monkey.png" alt="streamer" className="w-10 h-10 bg-indigo-500 rounded-full" />
|
||||
<span>
|
||||
{streamData ? streamData.streamerName : "Loading..."}
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user