From 9784ef8c678d20a0095d246a1ca2c81520f75a33 Mon Sep 17 00:00:00 2001 From: Chris-1010 <122332721@umail.ucc.ie> Date: Sat, 1 Feb 2025 03:14:24 +0000 Subject: [PATCH] PATCH: Username was not being accessed in `chat.py`: The WebSockets used for chat do not maintain the same session context as regular HTTP routes and so, the username in Flask's session could not be accessed. --- frontend/src/App.tsx | 3 - frontend/src/components/Video/ChatPanel.tsx | 119 +++++++++++--------- frontend/src/context/SocketContext.tsx | 4 +- frontend/src/pages/VideoPage.tsx | 96 ++++++++-------- web_server/blueprints/chat.py | 16 +-- 5 files changed, 119 insertions(+), 119 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 16674e7..2cabd53 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -2,7 +2,6 @@ import { useState, useEffect } from "react"; import { AuthContext } from "./context/AuthContext"; import { StreamsProvider } from "./context/StreamsContext"; import { BrowserRouter, Routes, Route } from "react-router-dom"; -import { SocketProvider } from "./context/SocketContext"; import HomePage from "./pages/HomePage"; import StreamerRoute from "./components/Stream/StreamerRoute"; import NotFoundPage from "./pages/NotFoundPage"; @@ -26,7 +25,6 @@ function App() { return ( - @@ -39,7 +37,6 @@ function App() { - ); } diff --git a/frontend/src/components/Video/ChatPanel.tsx b/frontend/src/components/Video/ChatPanel.tsx index e2e0836..91a7c21 100644 --- a/frontend/src/components/Video/ChatPanel.tsx +++ b/frontend/src/components/Video/ChatPanel.tsx @@ -16,17 +16,20 @@ interface ChatPanelProps { } const ChatPanel: React.FC = ({ streamId }) => { + const { socket, isConnected } = useSocket(); const [messages, setMessages] = useState([]); const [inputMessage, setInputMessage] = useState(""); const chatContainerRef = useRef(null); const { isLoggedIn, username } = useAuth(); - const { socket, isConnected } = useSocket(); // Join chat room when component mounts useEffect(() => { if (socket && isConnected) { - // Join chat room - socket.emit("join", { stream_id: streamId }); + // Add username check + socket.emit("join", { + username: username ? username : "Guest", + stream_id: streamId, + }); // Handle beforeunload event const handleBeforeUnload = () => { @@ -53,6 +56,7 @@ const ChatPanel: React.FC = ({ streamId }) => { // Handle incoming messages socket.on("new_message", (data: ChatMessage) => { + console.log("New message:", data); setMessages((prev) => [...prev, data]); }); @@ -61,10 +65,9 @@ const ChatPanel: React.FC = ({ streamId }) => { window.removeEventListener("beforeunload", handleBeforeUnload); socket.emit("leave", { stream_id: streamId }); socket.disconnect(); - socket.off("new_message"); }; } - }, [socket, isConnected, streamId]); + }, [socket, isConnected, username, streamId]); // Auto-scroll to bottom when new messages arrive useEffect(() => { @@ -81,6 +84,7 @@ const ChatPanel: React.FC = ({ streamId }) => { } socket.emit("send_message", { + username: username, stream_id: streamId, message: inputMessage.trim(), }); @@ -111,68 +115,71 @@ const ChatPanel: React.FC = ({ streamId }) => { return ( <> -
-

Stream Chat

+
+

Stream Chat

-
- {messages.map((msg, index) => ( -
- - {new Date(msg.time_sent).toLocaleTimeString()} - - + {messages.map((msg, index) => ( +
- {" "} - {msg.chatter_username}:{" "} - - {msg.message} -
- ))} -
+ + {new Date(msg.time_sent).toLocaleTimeString()} + + + {" "} + {msg.chatter_username}:{" "} + + {msg.message} +
+ ))} +
-
- {isLoggedIn && - <> - setInputMessage(e.target.value)} - onKeyDown={handleKeyPress} - placeholder={isLoggedIn ? "Type a message..." : "Login to chat"} - disabled={!isLoggedIn} - extraClasses="flex-grow" - onClick={() => (!isLoggedIn && setShowAuthModal(true))} /> +
+ {isLoggedIn && ( + <> + setInputMessage(e.target.value)} + onKeyDown={handleKeyPress} + placeholder={isLoggedIn ? "Type a message..." : "Login to chat"} + disabled={!isLoggedIn} + extraClasses="flex-grow" + onClick={() => !isLoggedIn && setShowAuthModal(true)} + /> - - } - - {!isLoggedIn && - - } + + )} -
- {showAuthModal && ( -
- setShowAuthModal(false)} /> + {!isLoggedIn && ( + + )}
- )} + {showAuthModal && ( +
+ setShowAuthModal(false)} /> +
+ )}
); diff --git a/frontend/src/context/SocketContext.tsx b/frontend/src/context/SocketContext.tsx index 203e401..33b57e0 100644 --- a/frontend/src/context/SocketContext.tsx +++ b/frontend/src/context/SocketContext.tsx @@ -12,9 +12,9 @@ export const SocketProvider: React.FC<{ children: React.ReactNode }> = ({ children, }) => { const [socket, setSocket] = useState(null); + const socketRef = useRef(null); const [isConnected, setIsConnected] = useState(false); const [isLoading, setIsLoading] = useState(true); - const socketRef = useRef(null); useEffect(() => { console.log("Start of useEffect"); @@ -28,7 +28,7 @@ export const SocketProvider: React.FC<{ children: React.ReactNode }> = ({ console.log("Creating new socket connection"); const newSocket = io('http://localhost:8080', { path: '/socket.io/', - transports: ['websocket', 'polling'], + transports: ['websocket'], withCredentials: true, reconnectionDelay: 1000, reconnectionDelayMax: 5000, diff --git a/frontend/src/pages/VideoPage.tsx b/frontend/src/pages/VideoPage.tsx index af7ceb1..8accfbf 100644 --- a/frontend/src/pages/VideoPage.tsx +++ b/frontend/src/pages/VideoPage.tsx @@ -2,10 +2,11 @@ import React, { useState, useEffect } from "react"; import Navbar from "../components/Layout/Navbar"; import Button, { ToggleButton } from "../components/Layout/Button"; import ChatPanel from "../components/Video/ChatPanel"; -import CheckoutForm, { Return } from "../components/Checkout/CheckoutForm"; +// import CheckoutForm, { Return } from "../components/Checkout/CheckoutForm"; import { useNavigate, useParams } from "react-router-dom"; import { useAuth } from "../context/AuthContext"; import VideoPlayer from "../components/Video/VideoPlayer"; +import { SocketProvider } from "../context/SocketContext"; interface VideoPageProps { streamId: number; @@ -23,18 +24,16 @@ interface StreamDataProps { const VideoPage: React.FC = ({ streamId }) => { const { isLoggedIn } = useAuth(); - // const [showCheckout, setShowCheckout] = useState(false); - const showReturn = window.location.search.includes("session_id"); const { streamerName } = useParams<{ streamerName: string }>(); const [streamData, setStreamData] = useState(); - const navigate = useNavigate(); - + // const [showCheckout, setShowCheckout] = useState(false); + // const showReturn = window.location.search.includes("session_id"); + // const navigate = useNavigate(); const [isChatVisible, setIsChatVisible] = useState(false); - const [showAuthModal, setShowAuthModal] = useState(false); const toggleChat = () => { setIsChatVisible((prev) => !prev); - } + }; // useEffect(() => { // // Prevent scrolling when checkout is open @@ -51,7 +50,8 @@ const VideoPage: React.FC = ({ 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) { @@ -73,54 +73,48 @@ const VideoPage: React.FC = ({ streamId }) => { }); }, [streamId, streamerName]); - useEffect(() => { - document.body.style.overflow = showAuthModal ? "hidden" : "unset"; - - return () => { - document.body.style.overflow = "unset"; // Cleanup - }; - }, [showAuthModal]); - return ( -
- - -
- - - - - {isChatVisible ? "Hide Chat" : "Show Chat"} - - - {isChatVisible && -
- -
} - -
- {isLoggedIn && ( - + +
)} +
+

{streamData?.streamTitle}

+

{streamData?.streamerName}

+ {isLoggedIn && ( + + )} +
+ {/* {showCheckout && setShowCheckout(false)} />} */} + {/* {showReturn && } */}
- - {/* {showCheckout && setShowCheckout(false)} />} */} - {/* {showReturn && } */} -
+ ); }; diff --git a/web_server/blueprints/chat.py b/web_server/blueprints/chat.py index 764377a..53a3302 100644 --- a/web_server/blueprints/chat.py +++ b/web_server/blueprints/chat.py @@ -3,6 +3,7 @@ from database.database import Database from .socket import socketio from flask_socketio import emit, join_room, leave_room from datetime import datetime +from utils.user_utils import get_user_id chat_bp = Blueprint("chat", __name__) @@ -12,8 +13,8 @@ chat_bp = Blueprint("chat", __name__) def handle_connection() -> None: """ Accept the connection from the frontend. - """ - print("Client Connected") # Confirmation connect has been made + """ + print("\nClient Connected to Chat\n") # Confirmation connect has been made @socketio.on("join") @@ -77,28 +78,29 @@ def send_chat(data) -> None: """ # Take the message information from frontend - chatter_id = session.get("username") + chatter_name = data.get("username") stream_id = data.get("stream_id") message = data.get("message") # Input validation - chatter is logged in, message is not empty, stream exists - if not all([chatter_id, message, stream_id]): - emit("error", {"error": f"Unable to send a chat. The following info was given: chatter_id={chatter_id}, message={message}, stream_id={stream_id}"}, broadcast=False) + if not all([chatter_name, message, stream_id]): + emit("error", {"error": f"Unable to send a chat. The following info was given: chatter_name={chatter_name}, message={message}, stream_id={stream_id}"}, broadcast=False) return # Send the chat message to the client so it can be displayed emit("new_message", { - "chatter_id": chatter_id, + "chatter_username": chatter_name, "message": message, "time_sent": datetime.now().strftime("%Y-%m-%d %H:%M:%S") }, room=stream_id) # Asynchronously save the chat - save_chat(chatter_id, stream_id, message) + save_chat(get_user_id(chatter_name), stream_id, message) def save_chat(chatter_id, stream_id, message): """Save the chat to the database""" + print(f"Saving to database: {chatter_id}, {stream_id}, {message}") db = Database() db.execute(""" INSERT INTO chat (chatter_id, stream_id, message)