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:
@@ -2,7 +2,6 @@ import { useState, useEffect } from "react";
|
|||||||
import { AuthContext } from "./context/AuthContext";
|
import { AuthContext } from "./context/AuthContext";
|
||||||
import { StreamsProvider } from "./context/StreamsContext";
|
import { StreamsProvider } from "./context/StreamsContext";
|
||||||
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
||||||
import { SocketProvider } from "./context/SocketContext";
|
|
||||||
import HomePage from "./pages/HomePage";
|
import HomePage from "./pages/HomePage";
|
||||||
import StreamerRoute from "./components/Stream/StreamerRoute";
|
import StreamerRoute from "./components/Stream/StreamerRoute";
|
||||||
import NotFoundPage from "./pages/NotFoundPage";
|
import NotFoundPage from "./pages/NotFoundPage";
|
||||||
@@ -26,7 +25,6 @@ function App() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthContext.Provider value={{ isLoggedIn, username, setIsLoggedIn, setUsername }}>
|
<AuthContext.Provider value={{ isLoggedIn, username, setIsLoggedIn, setUsername }}>
|
||||||
<SocketProvider>
|
|
||||||
<StreamsProvider>
|
<StreamsProvider>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Routes>
|
<Routes>
|
||||||
@@ -39,7 +37,6 @@ function App() {
|
|||||||
</Routes>
|
</Routes>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</StreamsProvider>
|
</StreamsProvider>
|
||||||
</SocketProvider>
|
|
||||||
</AuthContext.Provider>
|
</AuthContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,17 +16,20 @@ interface ChatPanelProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ChatPanel: React.FC<ChatPanelProps> = ({ streamId }) => {
|
const ChatPanel: React.FC<ChatPanelProps> = ({ streamId }) => {
|
||||||
|
const { socket, isConnected } = useSocket();
|
||||||
const [messages, setMessages] = useState<ChatMessage[]>([]);
|
const [messages, setMessages] = useState<ChatMessage[]>([]);
|
||||||
const [inputMessage, setInputMessage] = useState("");
|
const [inputMessage, setInputMessage] = useState("");
|
||||||
const chatContainerRef = useRef<HTMLDivElement>(null);
|
const chatContainerRef = useRef<HTMLDivElement>(null);
|
||||||
const { isLoggedIn, username } = useAuth();
|
const { isLoggedIn, username } = useAuth();
|
||||||
const { socket, isConnected } = useSocket();
|
|
||||||
|
|
||||||
// Join chat room when component mounts
|
// Join chat room when component mounts
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (socket && isConnected) {
|
if (socket && isConnected) {
|
||||||
// Join chat room
|
// Add username check
|
||||||
socket.emit("join", { stream_id: streamId });
|
socket.emit("join", {
|
||||||
|
username: username ? username : "Guest",
|
||||||
|
stream_id: streamId,
|
||||||
|
});
|
||||||
|
|
||||||
// Handle beforeunload event
|
// Handle beforeunload event
|
||||||
const handleBeforeUnload = () => {
|
const handleBeforeUnload = () => {
|
||||||
@@ -53,6 +56,7 @@ const ChatPanel: React.FC<ChatPanelProps> = ({ streamId }) => {
|
|||||||
|
|
||||||
// Handle incoming messages
|
// Handle incoming messages
|
||||||
socket.on("new_message", (data: ChatMessage) => {
|
socket.on("new_message", (data: ChatMessage) => {
|
||||||
|
console.log("New message:", data);
|
||||||
setMessages((prev) => [...prev, data]);
|
setMessages((prev) => [...prev, data]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -61,10 +65,9 @@ const ChatPanel: React.FC<ChatPanelProps> = ({ streamId }) => {
|
|||||||
window.removeEventListener("beforeunload", handleBeforeUnload);
|
window.removeEventListener("beforeunload", handleBeforeUnload);
|
||||||
socket.emit("leave", { stream_id: streamId });
|
socket.emit("leave", { stream_id: streamId });
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
socket.off("new_message");
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [socket, isConnected, streamId]);
|
}, [socket, isConnected, username, streamId]);
|
||||||
|
|
||||||
// Auto-scroll to bottom when new messages arrive
|
// Auto-scroll to bottom when new messages arrive
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -81,6 +84,7 @@ const ChatPanel: React.FC<ChatPanelProps> = ({ streamId }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
socket.emit("send_message", {
|
socket.emit("send_message", {
|
||||||
|
username: username,
|
||||||
stream_id: streamId,
|
stream_id: streamId,
|
||||||
message: inputMessage.trim(),
|
message: inputMessage.trim(),
|
||||||
});
|
});
|
||||||
@@ -129,7 +133,9 @@ const ChatPanel: React.FC<ChatPanelProps> = ({ streamId }) => {
|
|||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={`font-bold ${
|
className={`font-bold ${
|
||||||
msg.chatter_username === username ? "text-blue-400" : "text-green-400"
|
msg.chatter_username === username
|
||||||
|
? "text-blue-400"
|
||||||
|
: "text-green-400"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{" "}
|
{" "}
|
||||||
@@ -141,7 +147,7 @@ const ChatPanel: React.FC<ChatPanelProps> = ({ streamId }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-center gap-2">
|
<div className="flex justify-center gap-2">
|
||||||
{isLoggedIn &&
|
{isLoggedIn && (
|
||||||
<>
|
<>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -151,7 +157,8 @@ const ChatPanel: React.FC<ChatPanelProps> = ({ streamId }) => {
|
|||||||
placeholder={isLoggedIn ? "Type a message..." : "Login to chat"}
|
placeholder={isLoggedIn ? "Type a message..." : "Login to chat"}
|
||||||
disabled={!isLoggedIn}
|
disabled={!isLoggedIn}
|
||||||
extraClasses="flex-grow"
|
extraClasses="flex-grow"
|
||||||
onClick={() => (!isLoggedIn && setShowAuthModal(true))} />
|
onClick={() => !isLoggedIn && setShowAuthModal(true)}
|
||||||
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={sendChat}
|
onClick={sendChat}
|
||||||
className="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700"
|
className="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700"
|
||||||
@@ -159,14 +166,14 @@ const ChatPanel: React.FC<ChatPanelProps> = ({ streamId }) => {
|
|||||||
Send
|
Send
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
}
|
)}
|
||||||
|
|
||||||
{!isLoggedIn &&
|
{!isLoggedIn && (
|
||||||
<Button
|
<Button
|
||||||
extraClasses="absolute top-[20px] left-[20px] text-[1rem] flex items-center flex-nowrap z-[999]"
|
extraClasses="absolute top-[20px] left-[20px] text-[1rem] flex items-center flex-nowrap z-[999]"
|
||||||
onClick={() => (setShowAuthModal(true))}></Button>
|
onClick={() => setShowAuthModal(true)}
|
||||||
}
|
></Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{showAuthModal && (
|
{showAuthModal && (
|
||||||
<div className="fixed inset-0 z-[9999] flex items-center justify-center">
|
<div className="fixed inset-0 z-[9999] flex items-center justify-center">
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ export const SocketProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
const [socket, setSocket] = useState<Socket | null>(null);
|
const [socket, setSocket] = useState<Socket | null>(null);
|
||||||
|
const socketRef = useRef<Socket | null>(null);
|
||||||
const [isConnected, setIsConnected] = useState(false);
|
const [isConnected, setIsConnected] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const socketRef = useRef<Socket | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("Start of useEffect");
|
console.log("Start of useEffect");
|
||||||
@@ -28,7 +28,7 @@ export const SocketProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||||||
console.log("Creating new socket connection");
|
console.log("Creating new socket connection");
|
||||||
const newSocket = io('http://localhost:8080', {
|
const newSocket = io('http://localhost:8080', {
|
||||||
path: '/socket.io/',
|
path: '/socket.io/',
|
||||||
transports: ['websocket', 'polling'],
|
transports: ['websocket'],
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
reconnectionDelay: 1000,
|
reconnectionDelay: 1000,
|
||||||
reconnectionDelayMax: 5000,
|
reconnectionDelayMax: 5000,
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ import React, { useState, useEffect } from "react";
|
|||||||
import Navbar from "../components/Layout/Navbar";
|
import Navbar from "../components/Layout/Navbar";
|
||||||
import Button, { ToggleButton } from "../components/Layout/Button";
|
import Button, { ToggleButton } from "../components/Layout/Button";
|
||||||
import ChatPanel from "../components/Video/ChatPanel";
|
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 { useNavigate, useParams } from "react-router-dom";
|
||||||
import { useAuth } from "../context/AuthContext";
|
import { useAuth } from "../context/AuthContext";
|
||||||
import VideoPlayer from "../components/Video/VideoPlayer";
|
import VideoPlayer from "../components/Video/VideoPlayer";
|
||||||
|
import { SocketProvider } from "../context/SocketContext";
|
||||||
|
|
||||||
interface VideoPageProps {
|
interface VideoPageProps {
|
||||||
streamId: number;
|
streamId: number;
|
||||||
@@ -23,18 +24,16 @@ interface StreamDataProps {
|
|||||||
|
|
||||||
const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
|
const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
|
||||||
const { isLoggedIn } = useAuth();
|
const { isLoggedIn } = useAuth();
|
||||||
// const [showCheckout, setShowCheckout] = useState(false);
|
|
||||||
const showReturn = window.location.search.includes("session_id");
|
|
||||||
const { streamerName } = useParams<{ streamerName: string }>();
|
const { streamerName } = useParams<{ streamerName: string }>();
|
||||||
const [streamData, setStreamData] = useState<StreamDataProps>();
|
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 [isChatVisible, setIsChatVisible] = useState(false);
|
||||||
const [showAuthModal, setShowAuthModal] = useState(false);
|
|
||||||
|
|
||||||
const toggleChat = () => {
|
const toggleChat = () => {
|
||||||
setIsChatVisible((prev) => !prev);
|
setIsChatVisible((prev) => !prev);
|
||||||
}
|
};
|
||||||
|
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// // Prevent scrolling when checkout is open
|
// // Prevent scrolling when checkout is open
|
||||||
@@ -51,7 +50,8 @@ const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Fetch stream data for this streamer
|
// Fetch stream data for this streamer
|
||||||
fetch(
|
fetch(
|
||||||
`/api/get_stream_data/${streamerName}${streamId == 0 ? "" : `/${streamId}`
|
`/api/get_stream_data/${streamerName}${
|
||||||
|
streamId == 0 ? "" : `/${streamId}`
|
||||||
}`
|
}`
|
||||||
).then((res) => {
|
).then((res) => {
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
@@ -73,40 +73,34 @@ const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
|
|||||||
});
|
});
|
||||||
}, [streamId, streamerName]);
|
}, [streamId, streamerName]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
document.body.style.overflow = showAuthModal ? "hidden" : "unset";
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.body.style.overflow = "unset"; // Cleanup
|
|
||||||
};
|
|
||||||
}, [showAuthModal]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<SocketProvider>
|
||||||
<div id="videoPage" className="w-full">
|
<div id="videoPage" className="w-full">
|
||||||
<Navbar />
|
<Navbar />
|
||||||
|
|
||||||
<div id="container" className="bg-gray-900">
|
<div id="container" className="bg-gray-900">
|
||||||
|
|
||||||
<VideoPlayer streamId={streamId} />
|
<VideoPlayer streamId={streamId} />
|
||||||
|
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
onClick={toggleChat}
|
onClick={toggleChat}
|
||||||
toggled={isChatVisible}
|
toggled={isChatVisible}
|
||||||
extraClasses="absolute top-10 left-4 z-5"
|
extraClasses="z-5"
|
||||||
>
|
>
|
||||||
{isChatVisible ? "Hide Chat" : "Show Chat"}
|
{isChatVisible ? "Hide Chat" : "Show Chat"}
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
|
{isChatVisible && (
|
||||||
{isChatVisible &&
|
<div
|
||||||
<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">
|
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} />
|
<ChatPanel streamId={streamId} />
|
||||||
</div> }
|
</div>
|
||||||
|
)}
|
||||||
<div
|
<div
|
||||||
id="stream-info"
|
id="stream-info"
|
||||||
className="flex"
|
className="flex"
|
||||||
style={{ gridArea: "3 / 1 / 4 / 2" }}
|
style={{ gridArea: "3 / 1 / 4 / 2" }}
|
||||||
>
|
>
|
||||||
|
<h1>{streamData?.streamTitle}</h1>
|
||||||
|
<h2>{streamData?.streamerName}</h2>
|
||||||
{isLoggedIn && (
|
{isLoggedIn && (
|
||||||
<Button
|
<Button
|
||||||
// onClick={() => setShowCheckout(true)}
|
// onClick={() => setShowCheckout(true)}
|
||||||
@@ -117,10 +111,10 @@ const VideoPage: React.FC<VideoPageProps> = ({ streamId }) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* {showCheckout && <CheckoutForm onClose={() => setShowCheckout(false)} />} */}
|
{/* {showCheckout && <CheckoutForm onClose={() => setShowCheckout(false)} />} */}
|
||||||
{/* {showReturn && <Return />} */}
|
{/* {showReturn && <Return />} */}
|
||||||
</div>
|
</div>
|
||||||
|
</SocketProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from database.database import Database
|
|||||||
from .socket import socketio
|
from .socket import socketio
|
||||||
from flask_socketio import emit, join_room, leave_room
|
from flask_socketio import emit, join_room, leave_room
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from utils.user_utils import get_user_id
|
||||||
|
|
||||||
chat_bp = Blueprint("chat", __name__)
|
chat_bp = Blueprint("chat", __name__)
|
||||||
|
|
||||||
@@ -13,7 +14,7 @@ def handle_connection() -> None:
|
|||||||
"""
|
"""
|
||||||
Accept the connection from the frontend.
|
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")
|
@socketio.on("join")
|
||||||
@@ -77,28 +78,29 @@ def send_chat(data) -> None:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Take the message information from frontend
|
# Take the message information from frontend
|
||||||
chatter_id = session.get("username")
|
chatter_name = data.get("username")
|
||||||
stream_id = data.get("stream_id")
|
stream_id = data.get("stream_id")
|
||||||
message = data.get("message")
|
message = data.get("message")
|
||||||
|
|
||||||
# Input validation - chatter is logged in, message is not empty, stream exists
|
# Input validation - chatter is logged in, message is not empty, stream exists
|
||||||
if not all([chatter_id, message, stream_id]):
|
if not all([chatter_name, 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)
|
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
|
return
|
||||||
|
|
||||||
# Send the chat message to the client so it can be displayed
|
# Send the chat message to the client so it can be displayed
|
||||||
emit("new_message", {
|
emit("new_message", {
|
||||||
"chatter_id": chatter_id,
|
"chatter_username": chatter_name,
|
||||||
"message": message,
|
"message": message,
|
||||||
"time_sent": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
"time_sent": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
}, room=stream_id)
|
}, room=stream_id)
|
||||||
|
|
||||||
# Asynchronously save the chat
|
# 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):
|
def save_chat(chatter_id, stream_id, message):
|
||||||
"""Save the chat to the database"""
|
"""Save the chat to the database"""
|
||||||
|
print(f"Saving to database: {chatter_id}, {stream_id}, {message}")
|
||||||
db = Database()
|
db = Database()
|
||||||
db.execute("""
|
db.execute("""
|
||||||
INSERT INTO chat (chatter_id, stream_id, message)
|
INSERT INTO chat (chatter_id, stream_id, message)
|
||||||
|
|||||||
Reference in New Issue
Block a user