MAJOR Fix: Resolved API Request Delays;

Feat: Added a dev test account to users for expedited login;
Refactor: Improve socket connection handling and add logging for API request duration & update to ListRow key generation for improved uniqueness;
Feat: Made it so streams with no set thumbnail use their category's thumbnail;
Minor Fix: Corrections to db recommendation methods;
This commit is contained in:
Chris-1010
2025-01-30 03:42:22 +00:00
parent 6cfac0d78f
commit 6586506c97
19 changed files with 197 additions and 118 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -68,7 +68,7 @@ const ListRow: React.FC<ListRowProps> = ({
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
{items.map((item) => (
<ListItem
key={item.id}
key={`${item.type}-${item.id}`}
id={item.id}
type={item.type}
title={item.title}

View File

@@ -23,8 +23,17 @@ const ChatPanel: React.FC<ChatPanelProps> = ({ streamId }) => {
// Join chat room when component mounts
useEffect(() => {
if (socket && isConnected) {
// Join chat room
socket.emit("join", { stream_id: streamId });
// Handle beforeunload event
const handleBeforeUnload = () => {
socket.emit("leave", { stream_id: streamId });
socket.disconnect();
};
window.addEventListener("beforeunload", handleBeforeUnload);
// Load initial chat history
fetch(`/api/chat/${streamId}`)
.then((response) => {
@@ -45,9 +54,11 @@ const ChatPanel: React.FC<ChatPanelProps> = ({ streamId }) => {
setMessages((prev) => [...prev, data]);
});
// Cleanup
// Cleanup function
return () => {
window.removeEventListener("beforeunload", handleBeforeUnload);
socket.emit("leave", { stream_id: streamId });
socket.disconnect();
socket.off("new_message");
};
}

View File

@@ -1,5 +1,5 @@
import React, { createContext, useContext, useEffect, useState } from 'react';
import { io, Socket } from 'socket.io-client';
import React, { createContext, useContext, useEffect, useRef, useState } from "react";
import { io, Socket } from "socket.io-client";
interface SocketContextType {
socket: Socket | null;
@@ -8,39 +8,92 @@ interface SocketContextType {
const SocketContext = createContext<SocketContextType | undefined>(undefined);
export const SocketProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
export const SocketProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const [socket, setSocket] = useState<Socket | null>(null);
const [isConnected, setIsConnected] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const socketRef = useRef<Socket | null>(null);
useEffect(() => {
const newSocket = io("http://localhost:8080", {
path: "/socket.io/",
console.log("Start of useEffect");
// Check if we already have a socket instance
if (socketRef.current) {
console.log("Socket already exists, closing existing socket");
socketRef.current.close();
}
console.log("Creating new socket connection");
const newSocket = io('http://localhost:8080', {
path: '/socket.io/',
transports: ['websocket', 'polling'],
withCredentials: true,
transports: ['websocket'],
upgrade: false
reconnectionDelay: 1000,
reconnectionDelayMax: 5000,
reconnectionAttempts: 5,
timeout: 5000
});
newSocket.on('connect', () => {
console.log('Socket connected!');
setIsConnected(true);
});
newSocket.on('connect_error', (error) => {
console.error('Socket connection error:', error);
});
newSocket.on('disconnect', () => {
console.log('Socket disconnected!');
setIsConnected(false);
});
socketRef.current = newSocket;
setSocket(newSocket);
newSocket.on("connect", () => {
console.log("Socket connected!");
setIsConnected(true);
setIsLoading(false);
});
newSocket.on("reconnect_attempt", (attemptNumber) => {
console.log(`Reconnecting... Attempt ${attemptNumber}`);
});
newSocket.on("reconnect_error", (error) => {
console.error("Reconnection error:", error);
});
newSocket.on("reconnect", (attemptNumber) => {
console.log(`Reconnected after ${attemptNumber} attempts!`);
});
newSocket.on("reconnect_failed", () => {
console.error("Reconnection failed. Please refresh the page.");
});
newSocket.on("connect_error", (error) => {
console.error("Socket connection error:", error);
setIsLoading(false);
if (newSocket) newSocket.disconnect();
newSocket.connect();
});
newSocket.on("disconnect", (reason) => {
console.log(
"Socket disconnected! Reason: " + reason + " - Attempting reconnect..."
);
setIsConnected(false);
newSocket.connect();
});
return () => {
newSocket.close();
if (socketRef.current) {
console.log("Cleaning up socket connection...");
socketRef.current.disconnect();
socketRef.current.close();
socketRef.current = null;
}
};
}, []);
if (isLoading) {
return (
<div className="h-screen w-screen flex items-center justify-center">
<div className="text-4xl text-white">Connecting to socket...</div>
</div>
);
}
return (
<SocketContext.Provider value={{ socket, isConnected }}>
{children}
@@ -51,7 +104,7 @@ export const SocketProvider: React.FC<{ children: React.ReactNode }> = ({ childr
export const useSocket = () => {
const context = useContext(SocketContext);
if (context === undefined) {
throw new Error('useSocket must be used within a SocketProvider');
throw new Error("useSocket must be used within a SocketProvider");
}
return context;
};
};

View File

@@ -48,8 +48,15 @@ export function StreamsProvider({ children }: { children: React.ReactNode }) {
title: stream.title,
streamer: stream.username,
viewers: stream.num_viewers,
thumbnail: stream.thumbnail,
thumbnail:
stream.thumbnail ||
`/images/thumbnails/categories/${stream.category_name
.toLowerCase()
.replace(/ /g, "_")}.webp`,
category: stream.category_name,
}));
console.log(extractedData);
setFeaturedStreams(extractedData);
});
@@ -57,15 +64,15 @@ export function StreamsProvider({ children }: { children: React.ReactNode }) {
fetch(fetch_url[1])
.then((response) => response.json())
.then((data: CategoryItem[]) => {
const extractedData: CategoryItem[] = data.map(
(category: any) => ({
type: "category",
id: category.category_id,
title: category.category_name,
viewers: category.num_viewers,
thumbnail: `/images/thumbnails/categories/${category.category_name.toLowerCase().replace(/ /g, "_")}.webp`
})
);
const extractedData: CategoryItem[] = data.map((category: any) => ({
type: "category",
id: category.category_id,
title: category.category_name,
viewers: category.num_viewers,
thumbnail: `/images/thumbnails/categories/${category.category_name
.toLowerCase()
.replace(/ /g, "_")}.webp`,
}));
console.log(extractedData);
setFeaturedCategories(extractedData);
});

View File

@@ -4,7 +4,7 @@ import './assets/styles/index.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
// <StrictMode>
<App />
</StrictMode>,
// </StrictMode>,
)

View File

@@ -23,24 +23,24 @@ interface StreamDataProps {
const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
const { isLoggedIn } = useAuth();
const [showCheckout, setShowCheckout] = useState(false);
// const [showCheckout, setShowCheckout] = useState(false);
const showReturn = window.location.search.includes("session_id");
const { streamerName } = useParams<{ streamerName: string }>();
const [streamData, setStreamData] = useState<StreamDataProps>();
const navigate = useNavigate();
useEffect(() => {
// Prevent scrolling when checkout is open
if (showCheckout) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "unset";
}
// Cleanup function to ensure overflow is restored when component unmounts
return () => {
document.body.style.overflow = "unset";
};
}, [showCheckout]);
// useEffect(() => {
// // Prevent scrolling when checkout is open
// if (showCheckout) {
// document.body.style.overflow = "hidden";
// } else {
// document.body.style.overflow = "unset";
// }
// // Cleanup function to ensure overflow is restored when component unmounts
// return () => {
// document.body.style.overflow = "unset";
// };
// }, [showCheckout]);
useEffect(() => {
// Fetch stream data for this streamer
fetch(
@@ -83,7 +83,7 @@ const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
>
{isLoggedIn && (
<Button
onClick={() => setShowCheckout(true)}
// onClick={() => setShowCheckout(true)}
extraClasses="mx-auto mb-4"
>
Payment Screen Test
@@ -92,8 +92,8 @@ const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
</div>
</div>
{showCheckout && <CheckoutForm onClose={() => setShowCheckout(false)} />}
{showReturn && <Return />}
{/* {showCheckout && <CheckoutForm onClose={() => setShowCheckout(false)} />} */}
{/* {showReturn && <Return />} */}
</div>
);
};