UPDATE/REFACTOR: Change how VODs are accessed & loaded;
FIX: `isLive` -> `streamerIsLive`; FIX: `Sidebar` profile pictures;
This commit is contained in:
@@ -151,7 +151,7 @@ const VodListItem: React.FC<VodListItemProps> = ({
|
|||||||
<div className="flex justify-evenly items-stretch rounded-b-lg">
|
<div className="flex justify-evenly items-stretch rounded-b-lg">
|
||||||
<a
|
<a
|
||||||
className="flex justify-around w-full h-full bg-black/50 hover:bg-black/80 p-2 mx-1 font-semibold rounded-full border border-transparent hover:border-white"
|
className="flex justify-around w-full h-full bg-black/50 hover:bg-black/80 p-2 mx-1 font-semibold rounded-full border border-transparent hover:border-white"
|
||||||
href={`/vods/${username}/${vod_id}.mp4`}
|
href={`/vod/${username}/${vod_id}.mp4`}
|
||||||
download={`${username}_vod_${vod_id}.mp4`}
|
download={`${username}_vod_${vod_id}.mp4`}
|
||||||
>
|
>
|
||||||
<DownloadIcon />
|
<DownloadIcon />
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ interface ListRowProps {
|
|||||||
description?: string;
|
description?: string;
|
||||||
items: ItemType[];
|
items: ItemType[];
|
||||||
wrap?: boolean;
|
wrap?: boolean;
|
||||||
onItemClick: (itemName: string) => void;
|
onItemClick: (itemName: string, username?: string) => void;
|
||||||
titleClickable?: boolean;
|
titleClickable?: boolean;
|
||||||
extraClasses?: string;
|
extraClasses?: string;
|
||||||
itemExtraClasses?: string;
|
itemExtraClasses?: string;
|
||||||
@@ -185,8 +185,8 @@ const ListRow = forwardRef<ListRowRef, ListRowProps>((props, ref) => {
|
|||||||
category_name={item.category_name}
|
category_name={item.category_name}
|
||||||
length={item.length}
|
length={item.length}
|
||||||
views={item.views}
|
views={item.views}
|
||||||
thumbnail={item.thumbnail}
|
thumbnail={`/vods/${item.username}/${item.vod_id}.png`}
|
||||||
onItemClick={() => handleVodClick(item)}
|
onItemClick={() => onItemClick(item.username, item.vod_id.toString())}
|
||||||
extraClasses={itemExtraClasses}
|
extraClasses={itemExtraClasses}
|
||||||
variant={variant}
|
variant={variant}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ const Sidebar: React.FC<SideBarProps> = ({ extraClasses = "" }) => {
|
|||||||
</div>
|
</div>
|
||||||
<div id="streamers-followed" className="flex flex-col flex-grow items-center">
|
<div id="streamers-followed" className="flex flex-col flex-grow items-center">
|
||||||
<h2 className="border-b-2 border-t-2 w-[125%] text-2xl cursor-default mb-5">Streamers</h2>
|
<h2 className="border-b-2 border-t-2 w-[125%] text-2xl cursor-default mb-5">Streamers</h2>
|
||||||
<div className="flex flex-col flex-grow justify-evenly w-full">
|
<div className="flex flex-col flex-grow justify-start w-full">
|
||||||
{followedStreamers.map((streamer) => (
|
{followedStreamers.map((streamer) => (
|
||||||
<div
|
<div
|
||||||
key={`${sidebarId.current}-streamer-${streamer.username}`}
|
key={`${sidebarId.current}-streamer-${streamer.username}`}
|
||||||
@@ -131,6 +131,10 @@ const Sidebar: React.FC<SideBarProps> = ({ extraClasses = "" }) => {
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={`/user/${streamer.username}/profile_picture`}
|
src={`/user/${streamer.username}/profile_picture`}
|
||||||
|
onError={(e) => {
|
||||||
|
e.currentTarget.src = "/images/pfps/default.png";
|
||||||
|
e.currentTarget.onerror = null;
|
||||||
|
}}
|
||||||
alt={`${streamer.username}'s Profile`}
|
alt={`${streamer.username}'s Profile`}
|
||||||
className="w-10 h-10 rounded-full object-cover"
|
className="w-10 h-10 rounded-full object-cover"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { ChatProvider } from "../../context/ChatContext";
|
|||||||
const StreamerRoute: React.FC = () => {
|
const StreamerRoute: React.FC = () => {
|
||||||
const { streamerName } = useParams();
|
const { streamerName } = useParams();
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||||
const [isLive, setIsLive] = useState<boolean>(false);
|
const [streamerIsLive, setStreamerIsLive] = useState<boolean>(false);
|
||||||
const [streamId, setStreamId] = useState<number>(0);
|
const [streamId, setStreamId] = useState<number>(0);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
@@ -16,11 +16,11 @@ const StreamerRoute: React.FC = () => {
|
|||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/user/${streamerName}/status`);
|
const response = await fetch(`/api/user/${streamerName}/status`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
setIsLive(Boolean(data.is_live));
|
setStreamerIsLive(Boolean(data.is_live));
|
||||||
setStreamId(data.user_id);
|
setStreamId(data.user_id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error checking stream status:", error);
|
console.error("Error checking stream status:", error);
|
||||||
setIsLive(false);
|
setStreamerIsLive(false);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
@@ -36,7 +36,7 @@ const StreamerRoute: React.FC = () => {
|
|||||||
|
|
||||||
if (isLoading) return <LoadingScreen />;
|
if (isLoading) return <LoadingScreen />;
|
||||||
|
|
||||||
if (isLive) {
|
if (streamerIsLive) {
|
||||||
return (
|
return (
|
||||||
<ChatProvider>
|
<ChatProvider>
|
||||||
<VideoPage streamerId={streamId} />
|
<VideoPage streamerId={streamId} />
|
||||||
|
|||||||
@@ -10,18 +10,6 @@ interface VodsDashboardProps {
|
|||||||
const VodsDashboard: React.FC<VodsDashboardProps> = ({ vods }) => {
|
const VodsDashboard: React.FC<VodsDashboardProps> = ({ vods }) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const handleVodClick = (vodId: string) => {
|
|
||||||
if (vods.length > 0) {
|
|
||||||
navigate(`/stream/${vods[0].username}/vods/${vodId}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Ensure each VOD has a hardcoded thumbnail path
|
|
||||||
const thumbnails = vods.map((vod) => ({
|
|
||||||
...vod,
|
|
||||||
thumbnail: `/vods/${vod.username}/${vod.vod_id}.png`,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<h2 className="text-3xl font-bold text-white mb-6">Past Broadcasts</h2>
|
<h2 className="text-3xl font-bold text-white mb-6">Past Broadcasts</h2>
|
||||||
@@ -34,9 +22,9 @@ const VodsDashboard: React.FC<VodsDashboardProps> = ({ vods }) => {
|
|||||||
<ListRow
|
<ListRow
|
||||||
type="vod"
|
type="vod"
|
||||||
variant="vodDashboard"
|
variant="vodDashboard"
|
||||||
items={thumbnails} // Use modified VODs with hardcoded thumbnail
|
items={vods}
|
||||||
wrap={false}
|
wrap={false}
|
||||||
onItemClick={handleVodClick}
|
onItemClick={(username, vodId) => navigate(`/vods/${username}/${vodId}`) }
|
||||||
extraClasses="bg-black/50"
|
extraClasses="bg-black/50"
|
||||||
itemExtraClasses="w-[20vw]"
|
itemExtraClasses="w-[20vw]"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import Button from "../components/Input/Button";
|
|||||||
import DynamicPageContent from "../components/Layout/DynamicPageContent";
|
import DynamicPageContent from "../components/Layout/DynamicPageContent";
|
||||||
import LoadingScreen from "../components/Layout/LoadingScreen";
|
import LoadingScreen from "../components/Layout/LoadingScreen";
|
||||||
import Footer from "../components/Layout/Footer";
|
import Footer from "../components/Layout/Footer";
|
||||||
import { useAuth } from "../context/AuthContext";
|
|
||||||
|
|
||||||
const HomePage: React.FC = () => {
|
const HomePage: React.FC = () => {
|
||||||
const { streams, isLoading: isLoadingStreams } = useStreams();
|
const { streams, isLoading: isLoadingStreams } = useStreams();
|
||||||
@@ -14,16 +13,8 @@ const HomePage: React.FC = () => {
|
|||||||
const { vods, isLoading: isLoadingVods } = useVods();
|
const { vods, isLoading: isLoadingVods } = useVods();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (isLoadingStreams || isLoadingCategories || isLoadingVods) return <LoadingScreen>Loading Content...</LoadingScreen>;
|
if (isLoadingStreams || isLoadingCategories || isLoadingVods) return <LoadingScreen>Loading Content...</LoadingScreen>;
|
||||||
|
|
||||||
const thumbnails = vods.map((vod) => ({
|
|
||||||
...vod,
|
|
||||||
thumbnail: `/vods/${vod.username}/${vod.vod_id}.png`,
|
|
||||||
video: `/vods/${vod.username}/${vod.vod_id}.mp4`,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DynamicPageContent navbarVariant="home" className="relative min-h-screen animate-moving_bg" contentClassName="pb-[12vh]">
|
<DynamicPageContent navbarVariant="home" className="relative min-h-screen animate-moving_bg" contentClassName="pb-[12vh]">
|
||||||
{/* Streams Section */}
|
{/* Streams Section */}
|
||||||
@@ -60,9 +51,9 @@ const HomePage: React.FC = () => {
|
|||||||
type="vod"
|
type="vod"
|
||||||
title="Recent VODs"
|
title="Recent VODs"
|
||||||
description="Watch the latest recorded streams!"
|
description="Watch the latest recorded streams!"
|
||||||
items={thumbnails}
|
items={vods}
|
||||||
wrap={false}
|
wrap={false}
|
||||||
onItemClick={() => null}
|
onItemClick={(username, vodId) => navigate(`/vods/${username}/${vodId}`) }
|
||||||
extraClasses="bg-black/50"
|
extraClasses="bg-black/50"
|
||||||
itemExtraClasses="w-[20vw]"
|
itemExtraClasses="w-[20vw]"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -36,13 +36,6 @@ const UserPage: React.FC = () => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { streams } = useStreams(`/api/streams/${username}/data`);
|
const { streams } = useStreams(`/api/streams/${username}/data`);
|
||||||
const currentStream = streams[0];
|
const currentStream = streams[0];
|
||||||
console.log(vods)
|
|
||||||
|
|
||||||
const thumbnails = vods.map((vod) => ({
|
|
||||||
...vod,
|
|
||||||
thumbnail: `/vods/${vod.username}/${vod.vod_id}.png`,
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
const fetchProfileData = useCallback(async () => {
|
const fetchProfileData = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -80,7 +73,7 @@ const UserPage: React.FC = () => {
|
|||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
console.log("Success");
|
console.log("Success");
|
||||||
console.log(URL.createObjectURL(img))
|
console.log(URL.createObjectURL(img));
|
||||||
setProfilePicture(URL.createObjectURL(img));
|
setProfilePicture(URL.createObjectURL(img));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -130,7 +123,7 @@ const UserPage: React.FC = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
setProfileData(prev => prev ? { ...prev, bio: editedBio } : undefined);
|
setProfileData((prev) => (prev ? { ...prev, bio: editedBio } : undefined));
|
||||||
setIsEditingBio(false);
|
setIsEditingBio(false);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -166,7 +159,8 @@ const UserPage: React.FC = () => {
|
|||||||
{/* Profile Picture */}
|
{/* Profile Picture */}
|
||||||
<div
|
<div
|
||||||
className={`relative -top-[40px] sm:-top-[90px] w-[16vw] h-[16vw] sm:w-[20vw] sm:h-[20vw] max-w-[10em] max-h-[10em]
|
className={`relative -top-[40px] sm:-top-[90px] w-[16vw] h-[16vw] sm:w-[20vw] sm:h-[20vw] max-w-[10em] max-h-[10em]
|
||||||
rounded-full flex-shrink-0 border-4 ${profileData.isLive ? "border-[#ff0000]" : "border-[var(--user-pfp-border)]"
|
rounded-full flex-shrink-0 border-4 ${
|
||||||
|
profileData.isLive ? "border-[#ff0000]" : "border-[var(--user-pfp-border)]"
|
||||||
} inset-0 z-20`}
|
} inset-0 z-20`}
|
||||||
style={{ boxShadow: "var(--user-pfp-border-shadow)" }}
|
style={{ boxShadow: "var(--user-pfp-border-shadow)" }}
|
||||||
>
|
>
|
||||||
@@ -189,7 +183,7 @@ const UserPage: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
alt={`${profileData.username}'s profile`}
|
alt={`${profileData.username}'s profile`}
|
||||||
className="sm:w-full h-full object-cover rounded-full group-hover:brightness-50 relative z-0 transition-all"
|
className="sm:w-full h-full object-cover rounded-full group-hover:brightness-50 relative z-0 transition-all"
|
||||||
style={{ backgroundColor: 'white' }}
|
style={{ backgroundColor: "white" }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* If current user is the profile user then allow profile picture swap */}
|
{/* If current user is the profile user then allow profile picture swap */}
|
||||||
@@ -283,10 +277,7 @@ const UserPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content Section */}
|
{/* Content Section */}
|
||||||
<div
|
<div id="content" className="col-span-2 bg-[var(--user-contentBox)] rounded-lg p-6 flex flex-col items-center w-full">
|
||||||
id="content"
|
|
||||||
className="col-span-2 bg-[var(--user-contentBox)] rounded-lg p-6 flex flex-col items-center w-full"
|
|
||||||
>
|
|
||||||
{/* Stream */}
|
{/* Stream */}
|
||||||
{currentStream && (
|
{currentStream && (
|
||||||
<div className="mb-8 max-w-[500px] w-full">
|
<div className="mb-8 max-w-[500px] w-full">
|
||||||
@@ -295,7 +286,7 @@ const UserPage: React.FC = () => {
|
|||||||
font-black mb-4 rounded-full text-center"
|
font-black mb-4 rounded-full text-center"
|
||||||
>
|
>
|
||||||
Currently Live!
|
Currently Live!
|
||||||
</h2>
|
</h2>
|
||||||
<StreamListItem
|
<StreamListItem
|
||||||
id={profileData.id}
|
id={profileData.id}
|
||||||
title={currentStream.title || ""}
|
title={currentStream.title || ""}
|
||||||
@@ -312,14 +303,11 @@ const UserPage: React.FC = () => {
|
|||||||
{/* VODs */}
|
{/* VODs */}
|
||||||
{vods.length > 0 && (
|
{vods.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-bold mb-4"></h2>
|
|
||||||
<ListRow
|
<ListRow
|
||||||
type="vod"
|
type="vod"
|
||||||
title={`Past Broadcasts (${vods.length})`}
|
title={`Past Broadcasts (${vods.length})`}
|
||||||
items={thumbnails}
|
items={vods}
|
||||||
onItemClick={(vod) => {
|
onItemClick={(user, vodId) => handleNavigation(`/vods/${user}/${vodId}`)}
|
||||||
console.log("VOD Clicked:", vod);
|
|
||||||
}}
|
|
||||||
extraClasses="w-fit max-w-[40vw] py-0 mt-0"
|
extraClasses="w-fit max-w-[40vw] py-0 mt-0"
|
||||||
amountForScroll={2}
|
amountForScroll={2}
|
||||||
itemExtraClasses="w-[15vw]"
|
itemExtraClasses="w-[15vw]"
|
||||||
@@ -342,14 +330,12 @@ const UserPage: React.FC = () => {
|
|||||||
onMouseEnter={(e) => (e.currentTarget.style.boxShadow = "var(--follow-shadow)")}
|
onMouseEnter={(e) => (e.currentTarget.style.boxShadow = "var(--follow-shadow)")}
|
||||||
onMouseLeave={(e) => (e.currentTarget.style.boxShadow = "none")}
|
onMouseLeave={(e) => (e.currentTarget.style.boxShadow = "none")}
|
||||||
>
|
>
|
||||||
<button className="text-[var(--follow-text)] whitespace-pre-wrap pointer-events-none">
|
<button className="text-[var(--follow-text)] whitespace-pre-wrap pointer-events-none">Following</button>
|
||||||
Following
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="bg-[var(--user-follow-bg)] rounded-[1em] hover:scale-105 transition-all ease-in-out duration-300
|
className="bg-[var(--user-follow-bg)] rounded-[1em] hover:scale-105 transition-all ease-in-out duration-300
|
||||||
flex items-center justify-center w-full p-4 content-start cursor-pointer"
|
flex items-center justify-center w-full p-4 content-start cursor-pointer"
|
||||||
onClick={() => handleNavigation(`/user/${username}/vods`)}
|
onClick={() => handleNavigation(`/vods/${username}`)}
|
||||||
onMouseEnter={(e) => (e.currentTarget.style.boxShadow = "var(--follow-shadow)")}
|
onMouseEnter={(e) => (e.currentTarget.style.boxShadow = "var(--follow-shadow)")}
|
||||||
onMouseLeave={(e) => (e.currentTarget.style.boxShadow = "none")}
|
onMouseLeave={(e) => (e.currentTarget.style.boxShadow = "none")}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user