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:
@@ -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>
|
||||
</>
|
||||
|
||||
@@ -16,7 +16,7 @@ interface FormErrors {
|
||||
|
||||
//Speed up border animation
|
||||
interface SubmitProps {
|
||||
onSubmit: () => void;
|
||||
onSubmit: () => void;
|
||||
}
|
||||
|
||||
const LoginForm: React.FC<SubmitProps> = ({ onSubmit }) => {
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
// signup.html
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user