Implement basic reactions

This commit is contained in:
2025-10-05 22:56:43 +03:00
Unverified
parent a6e272e5b4
commit d6b2849e46
14 changed files with 923 additions and 60 deletions
+106 -1
View File
@@ -10,7 +10,7 @@ from fastapi.security import HTTPAuthorizationCredentials
from sqlalchemy.orm import Session
from dependencies import get_current_user, get_db
from constants import OWNER_USERNAME
from models import Message, SendMessageRequest, EditMessageRequest, User, DMEnvelope, MessageFile, DMFile
from models import Message, SendMessageRequest, EditMessageRequest, User, DMEnvelope, MessageFile, DMFile, Reaction, ReactionRequest, ReactionResponse
from push_service import push_service
from PIL import Image
import io
@@ -31,6 +31,23 @@ os.makedirs(FILES_ENCRYPTED_DIR, exist_ok=True)
def convert_message(msg: Message) -> dict:
# Group reactions by emoji
reactions_dict = {}
if msg.reactions:
for reaction in msg.reactions:
emoji = reaction.emoji
if emoji not in reactions_dict:
reactions_dict[emoji] = {
"emoji": emoji,
"count": 0,
"users": []
}
reactions_dict[emoji]["count"] += 1
reactions_dict[emoji]["users"].append({
"id": reaction.user_id,
"username": reaction.user.username
})
return {
"id": msg.id,
"content": msg.content,
@@ -40,6 +57,7 @@ def convert_message(msg: Message) -> dict:
"username": msg.author.username,
"profile_picture": msg.author.profile_picture,
"reply_to": convert_message(msg.reply_to) if msg.reply_to else None,
"reactions": list(reactions_dict.values()),
"files": [
{
"path": f"/api/uploads/files/normal/{Path(f.path).name}",
@@ -436,6 +454,63 @@ async def delete_message(
return {"status": "success", "message_id": message_id}
@router.post("/add_reaction")
async def add_reaction(
request: ReactionRequest,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
# Check if message exists
message = db.query(Message).filter(Message.id == request.message_id).first()
if not message:
raise HTTPException(status_code=404, detail="Message not found")
# Check if reaction already exists
existing_reaction = db.query(Reaction).filter(
Reaction.message_id == request.message_id,
Reaction.user_id == current_user.id,
Reaction.emoji == request.emoji
).first()
if existing_reaction:
# Remove existing reaction (toggle off)
db.delete(existing_reaction)
action = "removed"
else:
# Add new reaction
new_reaction = Reaction(
message_id=request.message_id,
user_id=current_user.id,
emoji=request.emoji
)
db.add(new_reaction)
action = "added"
db.commit()
# Refresh message to get updated reactions
db.refresh(message)
# Broadcast reaction update
try:
from .messaging import messagingManager
await messagingManager.broadcast({
"type": "reactionUpdate",
"data": {
"message_id": request.message_id,
"emoji": request.emoji,
"action": action,
"user_id": current_user.id,
"username": current_user.username,
"reactions": convert_message(message)["reactions"]
}
})
except Exception:
pass
return {"status": "success", "action": action, "reactions": convert_message(message)["reactions"]}
class MessaggingSocketManager:
def __init__(self) -> None:
self.connections: list[WebSocket] = []
@@ -670,6 +745,36 @@ class MessaggingSocketManager:
"data": {"message_id": message_id}
})
await websocket.send_json({"type": type, "data": response})
except HTTPException as e:
await self.send_error(websocket, type, e)
elif type == "addReaction":
try:
current_user = get_current_user_inner()
if not current_user:
raise HTTPException(401)
request_data = data["data"]
reaction_request = ReactionRequest(
message_id=request_data["message_id"],
emoji=request_data["emoji"]
)
response = await add_reaction(reaction_request, current_user, db)
# Broadcast reaction update
await self.broadcast({
"type": "reactionUpdate",
"data": {
"message_id": request_data["message_id"],
"emoji": request_data["emoji"],
"action": response["action"],
"user_id": current_user.id,
"username": current_user.username,
"reactions": response["reactions"]
}
})
await websocket.send_json({"type": type, "data": response})
except HTTPException as e:
await self.send_error(websocket, type, e)