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:
Chris-1010
2025-02-04 14:59:18 +00:00
parent f31834bc1d
commit 60c19b3052
24 changed files with 325 additions and 150 deletions

View File

@@ -5,14 +5,13 @@ import LoginForm from "./LoginForm";
import RegisterForm from "./RegisterForm";
import "../../assets/styles/auth.css";
interface AuthModalProps {
onClose: () => void;
}
const AuthModal: React.FC<AuthModalProps> = ({ onClose }) => {
const [selectedTab, setSelectedTab] = useState<string>("Login");
const [spinDuration, setSpinDuration] = useState("7s")
const [spinDuration, setSpinDuration] = useState("7s");
const handleSubmit = () => {
setSpinDuration("1s");
@@ -25,21 +24,26 @@ const AuthModal: React.FC<AuthModalProps> = ({ onClose }) => {
return (
<>
{/*Background Blur*/}
<div id="blurring-layer" className="fixed z-50 inset-0 w-screen h-screen backdrop-blur-sm group-has-[input:focus]:backdrop-blur-[5px]"></div>
<div
id="blurring-layer"
className="fixed z-50 inset-0 w-screen h-screen backdrop-blur-sm group-has-[input:focus]:backdrop-blur-[5px]"
></div>
{/*Container*/}
<div
className="container fixed inset-0 flex flex-col items-center justify-around z-[9999]
h-[75vh] m-auto min-w-[45vw] w-fit py-[50px] rounded-[5rem] transition-all animate-floating"
style={{ "--spin-duration": spinDuration } as React.CSSProperties}
>
{/*Border Container*/}
<div
{/*Border Container*/}
<div
id="border-container"
className="front-content fixed inset-0 bg-gradient-to-br from-blue-950 via-purple-500 to-violet-800 flex flex-col justify-center
z-50 h-[70vh] mr-0.5 mb-0.5 m-auto min-w-[40vw] w-fit py-[50px] rounded-[2rem] transition-all"
>
<div
id="login-methods"
className=" w-full flex flex-row items-center justify-evenly"
>
<div id="login-methods" className=" w-full flex flex-row items-center justify-evenly">
<button
onClick={onClose}
className="absolute top-[1rem] right-[2rem] text-[2rem] text-white hover:text-red-500 font-black hover:text-[2.5rem] transition-all"
@@ -63,7 +67,11 @@ const AuthModal: React.FC<AuthModalProps> = ({ onClose }) => {
Register
</ToggleButton>
</div>
{selectedTab === "Login" ? <LoginForm onSubmit={handleSubmit} /> : <RegisterForm onSubmit={handleSubmit}/>}
{selectedTab === "Login" ? (
<LoginForm onSubmit={handleSubmit} />
) : (
<RegisterForm onSubmit={handleSubmit} />
)}
</div>
</div>
</>

View File

@@ -16,7 +16,7 @@ interface FormErrors {
//Speed up border animation
interface SubmitProps {
onSubmit: () => void;
onSubmit: () => void;
}
const LoginForm: React.FC<SubmitProps> = ({ onSubmit }) => {

View File

@@ -19,7 +19,7 @@ interface FormErrors {
}
interface SubmitProps {
onSubmit: () => void;
onSubmit: () => void;
}
const RegisterForm: React.FC<SubmitProps> = ({ onSubmit }) => {
@@ -115,7 +115,7 @@ const RegisterForm: React.FC<SubmitProps> = ({ onSubmit }) => {
{errors.general && (
<p className="text-red-500 text-sm text-center">{errors.general}</p>
)}
{errors.username && (
<p className="text-red-500 mt-3 text-sm">{errors.username}</p>
)}
@@ -126,6 +126,7 @@ const RegisterForm: React.FC<SubmitProps> = ({ onSubmit }) => {
onChange={handleInputChange}
extraClasses={`${errors.username ? "border-red-500" : ""}`}
/>
{errors.email && (
<p className="text-red-500 mt-3 text-sm">{errors.email}</p>
)}
@@ -137,6 +138,7 @@ const RegisterForm: React.FC<SubmitProps> = ({ onSubmit }) => {
onChange={handleInputChange}
extraClasses={`${errors.email ? "border-red-500" : ""}`}
/>
{errors.password && (
<p className="text-red-500 mt-3 text-sm">{errors.password}</p>
)}
@@ -148,6 +150,7 @@ const RegisterForm: React.FC<SubmitProps> = ({ onSubmit }) => {
onChange={handleInputChange}
extraClasses={`${errors.password ? "border-red-500" : ""}`}
/>
{errors.confirmPassword && (
<p className="text-red-500 mt-3 text-sm">{errors.confirmPassword}</p>
)}
@@ -159,6 +162,7 @@ const RegisterForm: React.FC<SubmitProps> = ({ onSubmit }) => {
onChange={handleInputChange}
extraClasses={`${errors.confirmPassword ? "border-red-500" : ""}`}
/>
<Button type="submit">Register</Button>
</form>
);

View File

@@ -1 +0,0 @@
// signup.html

View File

@@ -67,8 +67,14 @@ const CheckoutForm: React.FC<CheckoutFormProps> = ({ onClose }) => {
return (
<>
<div id="blurring-layer" className="fixed z-10 inset-0 w-screen h-screen backdrop-blur-sm"></div>
<div id="modal-container" className="fixed inset-0 bg-black/30 flex items-center justify-center z-50 h-[70vh] m-auto w-fit py-[50px] px-[100px] rounded-[2rem]">
<div
id="blurring-layer"
className="fixed z-10 inset-0 w-screen h-screen backdrop-blur-sm"
></div>
<div
id="modal-container"
className="fixed inset-0 bg-black/30 flex items-center justify-center z-50 h-[70vh] m-auto w-fit py-[50px] px-[100px] rounded-[2rem]"
>
<button
onClick={onClose}
className="absolute top-[1rem] right-[3rem] text-[2rem] text-white hover:text-red-500 font-black hover:text-[2.5rem] transition-all"

View File

@@ -72,12 +72,10 @@ const ListRow: React.FC<ListRowProps> = ({
id={item.id}
type={item.type}
title={item.title}
streamer={item.type === "stream" ? (item.streamer) : undefined}
streamer={item.type === "stream" ? item.streamer : undefined}
viewers={item.viewers}
thumbnail={item.thumbnail}
onItemClick={() =>
onClick?.(item.id, item.streamer || item.title)
}
onItemClick={() => onClick?.(item.id, item.streamer || item.title)}
/>
))}
</div>

View File

@@ -6,11 +6,13 @@ interface LogoProps {
}
const Logo: React.FC<LogoProps> = ({ variant = "default" }) => {
const gradient =
"text-transparent group-hover:mx-1 transition-all";
const gradient = "text-transparent group-hover:mx-1 transition-all";
return (
<Link to="/" className="cursor-pointer">
<div id="logo" className={`group py-3 text-center font-bold hover:scale-110 transition-all ${variant === "home" ? "text-[12vh]" : "text-[4vh]"}`}>
<div
id="logo"
className={`group py-3 text-center font-bold hover:scale-110 transition-all ${variant === "home" ? "text-[12vh]" : "text-[4vh]"}`}
>
<h6 className="text-sm bg-gradient-to-br from-blue-400 via-green-500 to-indigo-500 font-black text-transparent bg-clip-text">
Go on, have a...
</h6>

View File

@@ -1,6 +1,6 @@
import React, { useState, useEffect } from "react";
import Logo from "./Logo";
import Button, {ToggleButton} from "./Button";
import Button, { ToggleButton } from "./Button";
import Sidebar from "./Sidebar";
import { Sidebar as SidebarIcon } from "lucide-react";
import {
@@ -13,14 +13,17 @@ import Input from "./Input";
import AuthModal from "../Auth/AuthModal";
import { useAuth } from "../../context/AuthContext";
interface NavbarProps {
variant?: "home" | "default";
isChatOpen: boolean;
toggleChat: () => void;
isChatOpen?: boolean;
toggleChat?: () => void;
}
const Navbar: React.FC<NavbarProps> = ({ variant = "default", isChatOpen, toggleChat }) => {
const Navbar: React.FC<NavbarProps> = ({
variant = "default",
isChatOpen,
toggleChat,
}) => {
const [showAuthModal, setShowAuthModal] = useState(false);
const { isLoggedIn } = useAuth();
const isVideoPage = location.pathname.includes("/EduGuru");
@@ -47,7 +50,10 @@ const Navbar: React.FC<NavbarProps> = ({ variant = "default", isChatOpen, toggle
};
return (
<div id="navbar" className={`flex justify-center items-center ${variant === "home" ? "h-[45vh] flex-col" : "h-[15vh] col-span-2 flex-row"}`}>
<div
id="navbar"
className={`flex justify-center items-center ${variant === "home" ? "h-[45vh] flex-col" : "h-[15vh] col-span-2 flex-row"}`}
>
<Logo variant={variant} />
<Button
extraClasses="absolute top-[20px] left-[20px] text-[1rem] flex items-center flex-nowrap"
@@ -83,11 +89,13 @@ const Navbar: React.FC<NavbarProps> = ({ variant = "default", isChatOpen, toggle
Quick Settings
</Button>
{isVideoPage && (
<ToggleButton onClick={toggleChat} toggled={isChatOpen}
extraClasses="absolute top-[80px] right-[20px] text-[1rem] flex items-center flex-nowrap"
>
{isChatOpen ? "Hide Chat" : "Show Chat"}
</ToggleButton>
<ToggleButton
onClick={toggleChat}
toggled={isChatOpen}
extraClasses="absolute top-[80px] right-[20px] text-[1rem] flex items-center flex-nowrap"
>
{isChatOpen ? "Hide Chat" : "Show Chat"}
</ToggleButton>
)}
<div id="search-bar" className="flex items-center">
@@ -96,10 +104,10 @@ const Navbar: React.FC<NavbarProps> = ({ variant = "default", isChatOpen, toggle
placeholder="Search..."
extraClasses="pr-[30px] focus:outline-none focus:border-purple-500 focus:w-[30vw]"
/>
<SearchIcon className="-translate-x-[28px] top-1/2 h-6 w-6 text-white" />
</div>
{showAuthModal && <AuthModal onClose={() => setShowAuthModal(false)} />}
</div>
);

View File

@@ -7,13 +7,14 @@ const StreamerRoute: React.FC = () => {
const { streamerName } = useParams<{ streamerName: string }>();
const [isLive, setIsLive] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(true);
let streamId: number = 0;
useEffect(() => {
const checkStreamStatus = async () => {
try {
const response = await fetch(`/api/streamer/${streamerName}/status`);
const data = await response.json();
setIsLive(data.is_live);
setIsLive(Boolean(data.is_live));
} catch (error) {
console.error("Error checking stream status:", error);
setIsLive(false);
@@ -31,11 +32,23 @@ const StreamerRoute: React.FC = () => {
}, [streamerName]);
if (isLoading) {
return <div className="h-screen w-screen flex text-6xl items-center justify-center" >Loading...</div>; // Or your loading component
return (
<div className="h-screen w-screen flex text-6xl items-center justify-center">
Loading...
</div>
);
// Or your loading component
}
// streamId=0 is a special case for the streamer's latest stream
return isLive ? <VideoPage streamId={1} /> : (streamerName ? <UserPage username={streamerName} /> : <div>Error: Streamer not found</div>);
return isLive ? (
<VideoPage streamId={streamId} />
) : streamerName ? (
<UserPage />
) : (
<div>Error: Streamer not found</div>
);
};
export default StreamerRoute;

View File

@@ -1,21 +1,16 @@
import React from 'react'
import React from "react";
interface ThumbnailProps {
path: string;
alt?: string;
path: string;
alt?: string;
}
const Thumbnail = ({ path, alt }: ThumbnailProps) => {
return (
<div id='stream-thumbnail'>
<img
width={300}
src={path}
alt={alt}
className="rounded-md"
/>
</div>
)
}
<div id="stream-thumbnail">
<img width={300} src={path} alt={alt} className="rounded-md" />
</div>
);
};
export default Thumbnail
export default Thumbnail;

View File

@@ -121,7 +121,7 @@ const ChatPanel: React.FC<ChatPanelProps> = ({ streamId }) => {
<div
ref={chatContainerRef}
id="chat-message-list"
className="flex-grow w-full max-h-[50vh] overflow-y-auto mb-4 space-y-2"
className="flex-grow w-full max-h-[50vh] overflow-y-auto mb-4 space-y-2 rounded-[67px]"
>
{messages.map((msg, index) => (
<div
@@ -129,15 +129,16 @@ const ChatPanel: React.FC<ChatPanelProps> = ({ streamId }) => {
className="grid grid-cols-[minmax(15%,_100px)_1fr] group h-fit items-center bg-gray-700 rounded p-2 text-white"
>
<span
className={`font-bold ${msg.chatter_username === username
className={`font-bold ${
msg.chatter_username === username
? "text-blue-400"
: "text-green-400"
}`}
}`}
>
{" "}
{msg.chatter_username}:{" "}
</span>
<span className="text-center" >{msg.message}</span>
<span className="text-center">{msg.message}</span>
<span className="text-gray-400 text-sm scale-0 group-hover:scale-100 h-[0px] group-hover:h-[10px] transition-all delay-1000 group-hover:delay-200">
{new Date(msg.time_sent).toLocaleTimeString()}
</span>
@@ -153,13 +154,12 @@ const ChatPanel: React.FC<ChatPanelProps> = ({ streamId }) => {
value={inputMessage}
onChange={(e) => setInputMessage(e.target.value)}
onKeyDown={handleKeyPress}
placeholder={
isLoggedIn ? "Type a message..." : "Login to chat"
}
placeholder={isLoggedIn ? "Type a message..." : "Login to chat"}
disabled={!isLoggedIn}
extraClasses="flex-grow"
onClick={() => !isLoggedIn && setShowAuthModal(true)}
/>
<button
onClick={sendChat}
className="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700"

View File

@@ -19,7 +19,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ streamId }) => {
"video-js",
"vjs-big-play-centered",
"w-full",
"h-full"
"h-full",
);
videoRef.current.appendChild(videoElement);
@@ -34,7 +34,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ streamId }) => {
{
src: `/images/sample_game_video.mp4`,
// type: "application/x-mpegURL",
type: 'video/mp4'
type: "video/mp4",
},
],
});