Skip to content

Commit

Permalink
Refactor messaging functionality and update API routes for improved c…
Browse files Browse the repository at this point in the history
…hat handling
  • Loading branch information
deepraj21 committed Oct 30, 2024
1 parent 742ff96 commit b3ed6e0
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 114 deletions.
114 changes: 64 additions & 50 deletions client/src/components/Messages/Message.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { useEffect,useState } from "react"
import { useEffect, useState } from "react";
import {
Search,
} from "lucide-react"
import { Input } from "@/components/ui/input"
} from "lucide-react";
import { Input } from "@/components/ui/input";
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@/components/ui/resizable"
import { TooltipProvider } from "@/components/ui/tooltip"
import axios from "axios"
import { Separator } from "@/components/ui/separator"
import { Textarea } from "@/components/ui/textarea"
import { Button } from "@/components/ui/button"
} from "@/components/ui/resizable";
import { TooltipProvider } from "@/components/ui/tooltip";
import axios from "axios";
import { Separator } from "@/components/ui/separator";
import { Textarea } from "@/components/ui/textarea";
import { Button } from "@/components/ui/button";

const backendUrl = import.meta.env.VITE_BACKEND_URL || 'http://localhost:5000';

Expand All @@ -28,71 +28,83 @@ interface ChatMessage {
export function Message() {
const [currentUserId, setCurrentUserId] = useState<string>("");
const [searchTerm, setSearchTerm] = useState<string>("");
const [users, setUsers] = useState<User[]>([]);
const [selectedUser, setSelectedUser] = useState<User | null>(null);
const [users, setUsers] = useState<User[]>([]);
const [selectedUser, setSelectedUser] = useState<User | null>(null);
const [message, setMessage] = useState<string>("");
const [chattedUsers, setChattedUsers] = useState<User[]>([]);
const [chattedUsers, setChattedUsers] = useState<User[]>([]);
const [chatMessages, setChatMessages] = useState<ChatMessage[]>([]);

// Fetch current user from local storage on component load
useEffect(() => {
const username = localStorage.getItem('devhub_username') || "";
setCurrentUserId(username);
}, []);
if (username) {
fetchChattedUsers(); // Fetch chatted users on initial load
}
}, [currentUserId]);

// Function to fetch chatted users from backend
const fetchChattedUsers = async () => {
try {
const response = await axios.get(`${backendUrl}/chatted_users/${currentUserId}`);
setChattedUsers(response.data); // Update chatted users list
} catch (error) {
console.error("Error fetching chatted users:", error);
}
};

const handleSearch = async () => {
if (searchTerm) {
// Function to search users based on input
const handleSearch = async (term: string) => {
if (term) {
try {
const response = await axios.get(`${backendUrl}/search_users?username=${searchTerm}`);
const response = await axios.get(`${backendUrl}/search_users?username=${term}`);
setUsers(response.data);
} catch (error) {
console.error("Error searching users:", error);
}
} else {
setUsers([]); // Clear users if search term is empty
}
}
};

// Function to send a message and refresh chat & chatted users
const handleSendMessage = async () => {
if (selectedUser && message.trim() !== "") {
try {
await axios.post(`${backendUrl}/send_message`, {
await axios.post(`${backendUrl}/send`, {
sender_username: currentUserId,
receiver_username: selectedUser.username,
message: message
});
setMessage("");
fetchChatMessages();

if (!chattedUsers.some(user => user.username === selectedUser.username)) {
setChattedUsers([...chattedUsers, selectedUser]);
}
fetchChatMessages(selectedUser); // Refresh chat messages after sending
fetchChattedUsers(); // Refresh chatted users from the backend
} catch (error) {
console.error("Error sending message:", error);
}
}
}
};

const fetchChatMessages = async () => {
if (selectedUser) {
try {
const response = await axios.get(`${backendUrl}/get_messages/${selectedUser.username}`);
setChatMessages(response.data);
} catch (error) {
console.error("Error fetching messages:", error);
}
// Function to fetch chat messages between the current user and the selected user
const fetchChatMessages = async (user: User) => {
try {
const response = await axios.get(`${backendUrl}/chat_history/${currentUserId}/${user.username}`);
setChatMessages(response.data);
} catch (error) {
console.error("Error fetching messages:", error);
}
}
};

// Call handleSearch whenever searchTerm changes
useEffect(() => {
handleSearch();
handleSearch(searchTerm);
}, [searchTerm]);

const handleUserSelect = (user: User) => {
setSelectedUser(user);
setUsers([]);
setChatMessages([]);
fetchChatMessages();
}
setSelectedUser(user); // Update selected user state
setUsers([]); // Clear the search results
fetchChatMessages(user); // Pass the user directly to fetch chat messages
};

return (
<TooltipProvider delayDuration={0}>
Expand All @@ -105,14 +117,17 @@ export function Message() {
>
<ResizablePanel minSize={30} className="flex flex-col">
<div className="bg-background/95 p-4 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<form onSubmit={(e) => { e.preventDefault(); handleSearch(); }}>
<form onSubmit={(e) => { e.preventDefault(); handleSearch(searchTerm); }}>
<div className="relative">
<Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
placeholder="Search users"
className="pl-8"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
onChange={(e) => {
setSearchTerm(e.target.value);
handleSearch(e.target.value); // Search as user types
}}
/>
</div>
</form>
Expand All @@ -138,7 +153,6 @@ export function Message() {
<ResizableHandle withHandle />
<ResizablePanel minSize={30} className="flex flex-col">
<div className="flex-grow overflow-auto">

{selectedUser ? (
<div>
<h3>Chat with {selectedUser.username}</h3>
Expand All @@ -151,7 +165,7 @@ export function Message() {
</div>
<Separator className="mt-auto" />
<div className="p-4">
<form>
<form onSubmit={(e) => { e.preventDefault(); handleSendMessage(); }}>
<div className="grid gap-4">
<Textarea
className="p-4"
Expand All @@ -161,7 +175,7 @@ export function Message() {
/>
<div className="flex items-center">
<Button
onClick={handleSendMessage}
type="submit"
size="sm"
className="ml-auto"
>
Expand All @@ -171,15 +185,15 @@ export function Message() {
</div>
</form>
</div>
</div>
</div>
) : (
<div className="p-8 text-center text-muted-foreground">
No message selected
</div>
)}
<div className="p-8 text-center text-muted-foreground">
No message selected
</div>
)}
</div>
</ResizablePanel>
</ResizablePanelGroup>
</TooltipProvider>
)
);
}
3 changes: 0 additions & 3 deletions client/src/components/Posts/ShowPosts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,6 @@ export default function ShowPosts() {
}

fetchPosts()

const intervalId = setInterval(fetchPosts, 5000)
return () => clearInterval(intervalId)
}, [])

const handleCommentSubmit = (postId: string) => {
Expand Down
10 changes: 1 addition & 9 deletions client/src/components/Posts/ShowUserPosts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,8 @@ const ShowUserPosts = () => {
console.error('Error fetching posts:', error)
})
}

// Fetch posts when component mounts
fetchPosts()

// Set up polling every 5 seconds
const intervalId = setInterval(fetchPosts, 5000)

// Cleanup the interval when component unmounts
return () => clearInterval(intervalId)
})
}, [username])

return (
<div className="max-w-2xl mx-auto space-y-0 md:space-y-8">
Expand Down
75 changes: 39 additions & 36 deletions server/api/handlers/message/message.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,47 @@
from flask import request, jsonify
from models import Chat
from extensions import users_chat, chat_collection
from models import Chat
from extensions import users_chat

def save_message(sender_id, receiver_id, message):
chat = Chat(sender_id, receiver_id, message)
chat_collection.insert_one(chat.__dict__)
chat_instance = Chat()

def get_messages(user_id):
messages = chat_collection.find({"$or": [{"sender_id": user_id}, {"receiver_id": user_id}]})
return list(messages)
def send_message():
data = request.json
sender = data.get('sender_username')
receiver = data.get('receiver_username')
message = data.get('message')

if not sender or not receiver or not message:
return jsonify({'error': 'Sender, receiver, and message are required.'}), 400

chat_instance.send_message(sender, receiver, message)
return jsonify({'status': 'Message sent successfully.'}), 200

def get_messages():
user1 = request.args.get('user1')
user2 = request.args.get('user2')

if not user1 or not user2:
return jsonify({'error': 'Both users are required.'}), 400

messages = chat_instance.get_messages(user1, user2)
return jsonify(messages), 200

def get_chatted_users(username):
chatted_users = chat_instance.get_chatted_users(username)
return jsonify(chatted_users), 200

def get_chat_history(user1, user2):
try:
chat_history = chat_instance.get_messages(user1, user2)
return jsonify(chat_history), 200
except Exception as e:
return jsonify({"error": str(e)}), 500

def search_users():
username = request.args.get('username')

users = users_chat.find({"username": {"$regex": username, "$options": "i"}}) # Case-insensitive search

return jsonify([{"username": user['username']} for user in users])

def send_message():
data = request.get_json()
sender_username = data['sender_username']
receiver_username = data['receiver_username']
message = data['message']

# Fetch the sender and receiver users based on their usernames
sender = users_chat.find_one({"username": sender_username})
receiver = users_chat.find_one({"username": receiver_username})

if sender and receiver:
save_message(sender['_id'], receiver['_id'], message)
return jsonify({"status": "Message sent!"}), 200
else:
return jsonify({"error": "User not found!"}), 404

def get_messages(username):
user = users_chat.find_one({"username": username})

if user:
messages = get_messages(user['_id'])
return jsonify(messages), 200
else:
return jsonify({"error": "User not found!"}), 404
if not username:
return jsonify({'error': 'Username parameter is required.'}), 400

users = users_chat.find({"username": {"$regex": username, "$options": "i"}})
return jsonify([{"username": user['username']} for user in users]), 200
8 changes: 5 additions & 3 deletions server/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from api.handlers.analyze.leetcodedata import leetcode_data, leetcode_card
from api.handlers.query.querymodel import chat,chat_history
from api.handlers.user.friends import add_friend, remove_friend, get_friends
from api.handlers.message.message import search_users, send_message, get_messages
from api.handlers.message.message import search_users, send_message, get_messages, get_chatted_users, get_chat_history
from api.handlers.visualization.visualization import get_user_relations
from api.handlers.neo4jconnect.connect import connect_to_neo4j, execute_cypher, schema
from api.handlers.posts.posts import create_post, update_post, delete_post, vote_post, add_comment, delete_comment, get_posts, get_posts_by_author
Expand Down Expand Up @@ -56,8 +56,10 @@ def register_routes(app):

# Messaging routes
app.add_url_rule('/search_users', 'search_users', search_users, methods=['GET'])
app.add_url_rule('/send_message', 'send_message', send_message, methods=['POST'])
app.add_url_rule('/get_messages/<username>', 'get_messages', get_messages, methods=['GET'])
app.add_url_rule('/send', 'send_message', send_message, methods=['POST'])
app.add_url_rule('/messages', 'get_messages', get_messages, methods=['GET'])
app.add_url_rule('/chatted_users/<username>', 'get_chatted_users', get_chatted_users, methods=['GET'])
app.add_url_rule('/chat_history/<user1>/<user2>','get_chat_history',get_chat_history, methods=['GET'])

# Visualization route
app.add_url_rule('/profile/relations','get_user_relations',get_user_relations, methods=['GET'])
Expand Down
Loading

0 comments on commit b3ed6e0

Please sign in to comment.