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.
This commit is contained in:
Chris-1010
2025-02-01 03:14:24 +00:00
parent b5d9756766
commit 9784ef8c67
5 changed files with 119 additions and 119 deletions

View File

@@ -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 (
<AuthContext.Provider value={{ isLoggedIn, username, setIsLoggedIn, setUsername }}>
<SocketProvider>
<StreamsProvider>
<BrowserRouter>
<Routes>
@@ -39,7 +37,6 @@ function App() {
</Routes>
</BrowserRouter>
</StreamsProvider>
</SocketProvider>
</AuthContext.Provider>
);
}

View File

@@ -16,17 +16,20 @@ interface ChatPanelProps {
}
const ChatPanel: React.FC<ChatPanelProps> = ({ streamId }) => {
const { socket, isConnected } = useSocket();
const [messages, setMessages] = useState<ChatMessage[]>([]);
const [inputMessage, setInputMessage] = useState("");
const chatContainerRef = useRef<HTMLDivElement>(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<ChatPanelProps> = ({ 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<ChatPanelProps> = ({ 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<ChatPanelProps> = ({ streamId }) => {
}
socket.emit("send_message", {
username: username,
stream_id: streamId,
message: inputMessage.trim(),
});
@@ -111,68 +115,71 @@ const ChatPanel: React.FC<ChatPanelProps> = ({ streamId }) => {
return (
<>
<div id="chat-panel" className="h-full flex flex-col rounded-lg p-4">
<h2 className="text-xl font-bold mb-4 text-white">Stream Chat</h2>
<div id="chat-panel" className="h-full flex flex-col rounded-lg p-4">
<h2 className="text-xl font-bold mb-4 text-white">Stream Chat</h2>
<div
ref={chatContainerRef}
id="chat-message-list"
className="flex-grow w-full max-h-[50vh] overflow-y-auto mb-4 space-y-2"
>
{messages.map((msg, index) => (
<div
key={index}
className="grid grid-cols-[8%_minmax(15%,_100px)_1fr] items-center bg-gray-700 rounded p-2 text-white"
>
<span className="text-gray-400 text-sm">
{new Date(msg.time_sent).toLocaleTimeString()}
</span>
<span
className={`font-bold ${
msg.chatter_username === username ? "text-blue-400" : "text-green-400"
}`}
<div
ref={chatContainerRef}
id="chat-message-list"
className="flex-grow w-full max-h-[50vh] overflow-y-auto mb-4 space-y-2"
>
{messages.map((msg, index) => (
<div
key={index}
className="grid grid-cols-[8%_minmax(15%,_100px)_1fr] items-center bg-gray-700 rounded p-2 text-white"
>
{" "}
{msg.chatter_username}:{" "}
</span>
<span>{msg.message}</span>
</div>
))}
</div>
<span className="text-gray-400 text-sm">
{new Date(msg.time_sent).toLocaleTimeString()}
</span>
<span
className={`font-bold ${
msg.chatter_username === username
? "text-blue-400"
: "text-green-400"
}`}
>
{" "}
{msg.chatter_username}:{" "}
</span>
<span>{msg.message}</span>
</div>
))}
</div>
<div className="flex justify-center gap-2">
{isLoggedIn &&
<>
<Input
type="text"
value={inputMessage}
onChange={(e) => setInputMessage(e.target.value)}
onKeyDown={handleKeyPress}
placeholder={isLoggedIn ? "Type a message..." : "Login to chat"}
disabled={!isLoggedIn}
extraClasses="flex-grow"
onClick={() => (!isLoggedIn && setShowAuthModal(true))} />
<div className="flex justify-center gap-2">
{isLoggedIn && (
<>
<Input
type="text"
value={inputMessage}
onChange={(e) => setInputMessage(e.target.value)}
onKeyDown={handleKeyPress}
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"
>
Send
</button>
</>
}
</>
)}
{!isLoggedIn &&
<Button
{!isLoggedIn && (
<Button
extraClasses="absolute top-[20px] left-[20px] text-[1rem] flex items-center flex-nowrap z-[999]"
onClick={() => (setShowAuthModal(true))}></Button>
}
</div>
{showAuthModal && (
<div className="fixed inset-0 z-[9999] flex items-center justify-center">
<AuthModal onClose={() => setShowAuthModal(false)} />
onClick={() => setShowAuthModal(true)}
></Button>
)}
</div>
)}
{showAuthModal && (
<div className="fixed inset-0 z-[9999] flex items-center justify-center">
<AuthModal onClose={() => setShowAuthModal(false)} />
</div>
)}
</div>
</>
);

View File

@@ -12,9 +12,9 @@ export const SocketProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const [socket, setSocket] = useState<Socket | null>(null);
const socketRef = useRef<Socket | null>(null);
const [isConnected, setIsConnected] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const socketRef = useRef<Socket | null>(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,

View File

@@ -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<VideoPageProps> = ({ 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<StreamDataProps>();
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<VideoPageProps> = ({ 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<VideoPageProps> = ({ streamId }) => {
});
}, [streamId, streamerName]);
useEffect(() => {
document.body.style.overflow = showAuthModal ? "hidden" : "unset";
return () => {
document.body.style.overflow = "unset"; // Cleanup
};
}, [showAuthModal]);
return (
<div id="videoPage" className="w-full">
<Navbar />
<div id="container" className="bg-gray-900">
<VideoPlayer streamId={streamId} />
<ToggleButton
onClick={toggleChat}
toggled={isChatVisible}
extraClasses="absolute top-10 left-4 z-5"
>
{isChatVisible ? "Hide Chat" : "Show Chat"}
</ToggleButton>
{isChatVisible &&
<div id="chat" className="relative top-0 right-0 bg-gray-800 bg-opacity-75 text-white p-4 w-1/3 h-full z-10 overflow-y-auto">
<ChatPanel streamId={streamId} />
</div> }
<div
id="stream-info"
className="flex"
style={{ gridArea: "3 / 1 / 4 / 2" }}
>
{isLoggedIn && (
<Button
// onClick={() => setShowCheckout(true)}
extraClasses="mx-auto mb-4"
<SocketProvider>
<div id="videoPage" className="w-full">
<Navbar />
<div id="container" className="bg-gray-900">
<VideoPlayer streamId={streamId} />
<ToggleButton
onClick={toggleChat}
toggled={isChatVisible}
extraClasses="z-5"
>
{isChatVisible ? "Hide Chat" : "Show Chat"}
</ToggleButton>
{isChatVisible && (
<div
id="chat"
className="relative top-0 right-0 bg-gray-800 bg-opacity-75 text-white p-4 w-1/3 h-full z-10 overflow-y-auto"
>
Payment Screen Test
</Button>
<ChatPanel streamId={streamId} />
</div>
)}
<div
id="stream-info"
className="flex"
style={{ gridArea: "3 / 1 / 4 / 2" }}
>
<h1>{streamData?.streamTitle}</h1>
<h2>{streamData?.streamerName}</h2>
{isLoggedIn && (
<Button
// onClick={() => setShowCheckout(true)}
extraClasses="mx-auto mb-4"
>
Payment Screen Test
</Button>
)}
</div>
</div>
{/* {showCheckout && <CheckoutForm onClose={() => setShowCheckout(false)} />} */}
{/* {showReturn && <Return />} */}
</div>
{/* {showCheckout && <CheckoutForm onClose={() => setShowCheckout(false)} />} */}
{/* {showReturn && <Return />} */}
</div>
</SocketProvider>
);
};

View File

@@ -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__)
@@ -13,7 +14,7 @@ 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)