diff --git a/app.py b/app.py index 61d8425..a8cf7ff 100644 --- a/app.py +++ b/app.py @@ -1,8 +1,12 @@ +from PIL import Image # Import the Image module from Pillow from flask import Flask, render_template, request, redirect, url_for, session, jsonify, send_from_directory, make_response from flask_sqlalchemy import SQLAlchemy from cryptography.fernet import Fernet, InvalidToken import os +import random +import string from datetime import datetime, timedelta, timezone +from werkzeug.utils import secure_filename # Add this line from werkzeug.security import generate_password_hash, check_password_hash from flask_socketio import SocketIO, emit, join_room, leave_room import logging @@ -62,6 +66,7 @@ class User(db.Model): online = db.Column(db.Boolean, nullable=False, default=False) is_admin = db.Column(db.Boolean, nullable=False, default=False) disabled = db.Column(db.Boolean, nullable=False, default=False) + profile_picture = db.Column(db.String(150), nullable=True) class Message(db.Model): id = db.Column(db.Integer, primary_key=True) @@ -310,6 +315,7 @@ def chat(friend_id): friend_user = User.query.filter_by(id=friend_id).first() user = User.query.filter_by(id=current_user_id).first() friends = Friend.query.filter_by(user_id=user.id).all() + profile_picture = user.profile_picture # Assuming this is the path to the profile picture friend_ids = [f.friend_id for f in friends] friend_users = User.query.filter(User.id.in_(friend_ids)).all() @@ -321,7 +327,7 @@ def chat(friend_id): groups = Group.query.filter(Group.id.in_(group_ids)).all() logger.info(f"User {session['username']} opened chat with {friend_user.username}") - return render_template('chat.html', username=session['username'], friend_id=friend_id, friend_username=friend_user.username, friends=friend_users, friend_requests=friend_requests, groups=groups, is_admin=user.is_admin) + return render_template('chat.html', friend=friend, profile_picture=profile_picture, username=session['username'], friend_id=friend_id, friend_username=friend_user.username, friends=friend_users, friend_requests=friend_requests, groups=groups, is_admin=user.is_admin) else: logger.warning(f"User {session['username']} attempted to access chat with non-friend user_id: {friend_id}") return redirect(url_for('dashboard')) @@ -343,11 +349,9 @@ def send_message(receiver): content_type = 'file' if content: - # Encrypt text content only, do not encrypt file names encrypted_content = cipher.encrypt(content.encode()).decode() if content_type == 'text' else content timestamp_dt = datetime.strptime(timestamp, '%Y-%m-%d %H:%M:%S').replace(tzinfo=timezone.utc) - # Check if they are friends sender_user = User.query.filter_by(username=session['username']).first() receiver_user = User.query.filter_by(id=receiver).first() if not receiver_user: @@ -362,20 +366,22 @@ def send_message(receiver): decrypted_content = cipher.decrypt(encrypted_content.encode()).decode() if content_type == 'text' else encrypted_content socketio.emit('new_message', { 'sender': session['username'], + 'receiver': receiver_user.username, # Include the receiver information 'content': decrypted_content, + 'sender_profile_picture': sender_user.profile_picture, # Ensure this is correct 'content_type': content_type, 'timestamp': timestamp, 'id': new_message.id }, room=receiver_user.username) logger.info(f"Message sent from {session['username']} to {receiver_user.username}") - return jsonify({'status': 'Message sent', 'message_id': new_message.id}), 200 + return jsonify({'status': 'Message sent', 'message_id': new_message.id, 'sender_profile_picture': sender_user.profile_picture}), 200 else: pending_message = PendingMessage(sender=session['username'], receiver=receiver_user.username, content=encrypted_content, content_type=content_type, timestamp=timestamp_dt) db.session.add(pending_message) db.session.commit() logger.info(f"Pending message from {session['username']} to {receiver_user.username}") - return jsonify({'status': 'Pending message sent', 'message_id': pending_message.id}), 200 + return jsonify({'status': 'Pending message sent', 'message_id': pending_message.id, 'sender_profile_picture': sender_user.profile_picture}), 200 return jsonify({'error': 'No content or file provided'}), 400 except Exception as e: logger.error(f"Error sending message from {session['username']} to {receiver}: {e}") @@ -399,6 +405,7 @@ def get_messages(friend_id): { 'id': msg.id, 'sender': msg.sender.username, + 'sender_profile_picture': f'/cdn/{msg.sender.profile_picture}' if msg.sender.profile_picture else None, 'content': cipher.decrypt(msg.content.encode()).decode() if msg.content_type == 'text' else msg.content, 'content_type': msg.content_type, 'timestamp': msg.timestamp.strftime("%Y-%m-%d %H:%M:%S") @@ -410,6 +417,44 @@ def get_messages(friend_id): logger.warning(f"Unauthorized message retrieval attempt by user {session.get('username', 'unknown')}") return jsonify({'error': 'Unauthorized'}), 401 +def generate_random_string(length=10): + characters = string.ascii_letters + string.digits + return ''.join(random.choice(characters) for i in range(length)) + +def allowed_file(filename): + ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp'} + return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS + +def compress_image(image_path, quality=15): + with Image.open(image_path) as img: + if img.format == 'PNG': + img.save(image_path, "PNG", optimize=True) + else: + img = img.convert("RGB") # Ensure the image is in RGB mode for non-PNG files + img.save(image_path, "JPEG", quality=quality) # Save with JPEG format and compression + +@app.route('/upload_profile_picture', methods=['POST']) +def upload_profile_picture(): + if 'username' in session: + user = User.query.filter_by(username=session['username']).first() + if 'profile_picture' in request.files: + file = request.files['profile_picture'] + if file and allowed_file(file.filename): + filename = secure_filename(file.filename) + random_string = generate_random_string() + filename_parts = filename.rsplit('.', 1) + filename = f"{filename_parts[0]}-{random_string}.{filename_parts[1]}" + file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) + file.save(file_path) + + # Compress the image + compress_image(file_path, quality=85) + + user.profile_picture = filename + db.session.commit() + logger.info(f"User {session['username']} uploaded a new profile picture: {filename}") + return redirect(url_for('dashboard')) # Redirect to a valid endpoint + return redirect(url_for('dashboard')) # Redirect to a valid endpoint if not logged in @app.route('/cdn/') def uploaded_file(filename): @@ -514,6 +559,15 @@ def remove_friend(friend_id): reciprocal_friend = Friend.query.filter_by(user_id=friend.friend_id, friend_id=user.id).first() if reciprocal_friend: db.session.delete(reciprocal_friend) + + # Remove friend requests associated with the friend + sent_request = FriendRequest.query.filter_by(sender_id=user.id, receiver_id=friend_id).first() + if sent_request: + db.session.delete(sent_request) + received_request = FriendRequest.query.filter_by(sender_id=friend_id, receiver_id=user.id).first() + if received_request: + db.session.delete(received_request) + db.session.commit() socketio.emit('friend_removed', { @@ -522,10 +576,10 @@ def remove_friend(friend_id): }, room=friend.friend.username) logger.info(f"Friend {friend.friend.username} removed by {session['username']}") - return redirect(url_for('dashboard')) + return jsonify({'status': 'success'}), 200 logger.warning(f"Friend removal failed: Friend not found or unauthorized access by {session['username']}") - return 'Friend not found' - return redirect(url_for('login')) + return jsonify({'status': 'error', 'message': 'Friend not found or unauthorized access'}), 404 + return jsonify({'status': 'error', 'message': 'Unauthorized'}), 401 @app.route('/create_group', methods=['POST']) def create_group(): @@ -633,6 +687,7 @@ def send_group_message(group_id): 'group_id': group_id, 'sender': session['username'], 'content': decrypted_content, + 'sender_profile_picture': sender_user.profile_picture, # Add profile picture 'content_type': content_type, 'timestamp': timestamp, 'id': new_message.id @@ -656,6 +711,7 @@ def get_group_messages(group_id): 'id': msg.GroupMessage.id, 'group_id': msg.GroupMessage.group_id, 'sender': msg.User.username, # Use the username from the User table + 'sender_profile_picture': msg.User.profile_picture, # Add profile picture 'content': cipher.decrypt(msg.GroupMessage.content.encode()).decode() if msg.GroupMessage.content_type == 'text' else msg.GroupMessage.content, 'content_type': msg.GroupMessage.content_type, 'timestamp': msg.GroupMessage.timestamp.strftime("%Y-%m-%d %H:%M:%S") diff --git a/requirements.txt b/requirements.txt index 4531df7..7ff1ad8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ Werkzeug==3.0.3 python-socketio==5.11.2 python-engineio==4.9.1 PyJWT==2.8.0 +Pillow==10.3.0 \ No newline at end of file diff --git a/src/input.css b/src/input.css index 1149f21..cc6694b 100644 --- a/src/input.css +++ b/src/input.css @@ -110,4 +110,15 @@ .dropdown-content { @apply bg-[#fafafa] dark:!bg-[#141414] shadow text-black dark:!text-white +} + +[data-message-text="---"]{ + width:auto !important; + padding: 0 !important; +} + +hr{ + height: 1px; + border: none; + @apply !bg-[#f1f1f1] dark:!bg-[#363636] } \ No newline at end of file diff --git a/static/js/chat.js b/static/js/chat.js index f7999c4..41da652 100644 --- a/static/js/chat.js +++ b/static/js/chat.js @@ -49,14 +49,14 @@ document.addEventListener('DOMContentLoaded', (event) => { function formatLocalTime(utcTimestamp) { const date = new Date(utcTimestamp); const localDate = new Date(date.getTime() - date.getTimezoneOffset() * 60000); - + const now = new Date(); const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1); - + const timeFormatter = { hour: '2-digit', minute: '2-digit' }; const dateFormatter = { year: 'numeric', month: '2-digit', day: '2-digit' }; - + let formattedTime; if (localDate >= today) { formattedTime = `Today at ${localDate.toLocaleTimeString(undefined, timeFormatter)}`; @@ -67,39 +67,90 @@ document.addEventListener('DOMContentLoaded', (event) => { } return formattedTime; } - function isImageUrl(url) { return /\.(jpg|jpeg|png|gif|bmp|svg)$/i.test(url); } + function appendMessage(msg, showUsername, senderProfilePicture) { + const currentTimestamp = new Date(msg.timestamp).getTime(); + previousSender = msg.sender; + previousTimestamp = currentTimestamp; + + const newMessage = document.createElement('div'); + newMessage.classList.add('message'); + + if (showUsername) { + const usernameElement = document.createElement('div'); + usernameElement.classList.add('message-sender'); + if (msg.sender === username) { + usernameElement.classList.add('message-myself-sender'); + } + usernameElement.innerHTML = ` + /cdn/${msg.sender} + ${msg.sender} +
${formatLocalTime(msg.timestamp)}
`; + messagesList.appendChild(usernameElement); + } + + if (msg.content_type === 'text') { + if (isImageUrl(msg.content)) { + newMessage.innerHTML = `Image
${formatLocalTime(msg.timestamp)}
`; + } else { + newMessage.innerHTML = `${md.render(msg.content)}
${formatLocalTime(msg.timestamp)}
`; + } + } else if (msg.content_type === 'file') { + const isImage = isImageUrl(msg.content); + const isVideo = /\.(mp4|webm|ogg)$/i.test(msg.content); + if (isImage) { + newMessage.innerHTML = `Image
${formatLocalTime(msg.timestamp)}
`; + } else if (isVideo) { + newMessage.innerHTML = `
${formatLocalTime(msg.timestamp)}
`; + } else { + newMessage.innerHTML = `Download File
${formatLocalTime(msg.timestamp)}
`; + } + } + + messagesList.appendChild(newMessage); + scrollToBottom(); + } + let previousSender = null; let previousTimestamp = 0; socket.on('new_message', function(data) { + // Check if the message is for the current chat + if (friendUsername !== data.sender && friendId != data.sender_id) { + return; + } + showNotification('New Message', `You have received a new message from ${data.sender}`); const currentTimestamp = new Date(data.timestamp).getTime(); const showUsername = previousSender !== data.sender || shouldShowUsername(previousTimestamp, currentTimestamp); previousSender = data.sender; previousTimestamp = currentTimestamp; - + const messageElement = document.createElement('div'); messageElement.classList.add('message'); const newMessage = document.createElement('div'); newMessage.classList.add('message'); - - newMessage.dataset.messageId = data.id; // Ensure this is correct - newMessage.dataset.messageText = data.content; + newMessage.dataset.messageId = data.id; + newMessage.dataset.messageText = data.content; + if (showUsername) { const usernameElement = document.createElement('div'); usernameElement.classList.add('message-sender'); const timestampFormatted = formatLocalTime(data.timestamp); - usernameElement.innerHTML = `${data.sender}:
${timestampFormatted}
`; + + // Include the profile picture if available + const profilePicture = data.sender_profile_picture ? `${data.sender}` : ''; + + usernameElement.innerHTML = `${profilePicture} ${data.sender}:
${timestampFormatted}
`; messagesList.appendChild(usernameElement); } - + if (data.content_type === 'text') { if (isImageUrl(data.content)) { newMessage.innerHTML = `Image
${formatLocalTime(data.timestamp)}
`; @@ -117,65 +168,71 @@ document.addEventListener('DOMContentLoaded', (event) => { newMessage.innerHTML = `Download File
${formatLocalTime(data.timestamp)}
`; } } - + messagesList.appendChild(newMessage); scrollToBottom(); }); - + + if (friendId) { fetch(`/get_messages/${friendId}`) - .then(response => response.json()) - .then(data => { - messagesList.innerHTML = ''; - document.getElementById('chat-with').textContent = `Chatting with ${friendUsername}`; - document.getElementById('send-message-form').dataset.receiver = friendId; - - data.messages.forEach((msg, index, messages) => { - const currentTimestamp = new Date(msg.timestamp).getTime(); - const previousMessage = messages[index - 1]; - const previousTimestamp = previousMessage ? new Date(previousMessage.timestamp).getTime() : 0; - const showUsername = index === 0 || msg.sender !== previousMessage.sender || shouldShowUsername(previousTimestamp, currentTimestamp); - - if (showUsername) { - const usernameElement = document.createElement('div'); - usernameElement.classList.add('message-sender'); - - const timestampFormatted = new Date(msg.timestamp).toLocaleString(undefined, { - weekday: 'short', year: 'numeric', month: 'short', day: 'numeric', - hour: '2-digit', minute: '2-digit' - }); - usernameElement.innerHTML = `${msg.sender}
${timestampFormatted}
`; - messagesList.appendChild(usernameElement); + .then(response => response.json()) + .then(data => { + messagesList.innerHTML = ''; + document.getElementById('chat-with').textContent = `Chatting with ${friendUsername}`; + document.getElementById('send-message-form').dataset.receiver = friendId; + + data.messages.forEach((msg, index, messages) => { + const currentTimestamp = new Date(msg.timestamp).getTime(); + const previousMessage = messages[index - 1]; + const previousTimestamp = previousMessage ? new Date(previousMessage.timestamp).getTime() : 0; + const showUsername = index === 0 || msg.sender !== previousMessage.sender || shouldShowUsername(previousTimestamp, currentTimestamp); + + if (showUsername) { + const usernameElement = document.createElement('div'); + usernameElement.classList.add('message-sender'); + if (msg.sender === username) { + usernameElement.classList.add('message-myself-sender'); } - - - const messageElement = document.createElement('div'); - messageElement.classList.add('message'); - - messageElement.dataset.messageId = msg.id; - messageElement.dataset.messageText = msg.content; - - if (msg.content_type === 'text') { - messageElement.innerHTML = `${md.render(msg.content)}
${msg.timestamp}
`; - } else if (msg.content_type === 'file') { - const isImage = /\.(jpg|jpeg|png|gif|bmp|svg)$/i.test(msg.content); - const isVideo = /\.(mp4|webm|ogg)$/i.test(msg.content); - if (isImage) { - messageElement.innerHTML = `Image
${msg.timestamp}
`; - } else if (isVideo) { - messageElement.innerHTML = `
${msg.timestamp}
`; - } else { - messageElement.innerHTML = `Download File
${msg.timestamp}
`; - } + // Include the profile picture if available + const profilePicture = msg.sender_profile_picture ? `${msg.sender}` : ''; + usernameElement.innerHTML = `${profilePicture} ${msg.sender}
${formatLocalTime(msg.timestamp)}
`; + messagesList.appendChild(usernameElement); + } + + const messageElement = document.createElement('div'); + messageElement.classList.add('message'); + + messageElement.dataset.messageId = msg.id; + messageElement.dataset.messageText = msg.content; + + if (msg.content_type === 'text') { + if (isImageUrl(msg.content)) { + messageElement.innerHTML = `Image
${formatLocalTime(msg.timestamp)}
`; + } else { + messageElement.innerHTML = `${md.render(msg.content)}
${formatLocalTime(msg.timestamp)}
`; } - - messagesList.appendChild(messageElement); - }); - - scrollToBottom(); - }) - .catch(error => console.error('Error fetching messages:', error)); + } else if (msg.content_type === 'file') { + const isImage = isImageUrl(msg.content); + const isVideo = /\.(mp4|webm|ogg)$/i.test(msg.content); + if (isImage) { + messageElement.innerHTML = `Image
${formatLocalTime(msg.timestamp)}
`; + } else if (isVideo) { + messageElement.innerHTML = `
${formatLocalTime(msg.timestamp)}
`; + } else { + messageElement.innerHTML = `Download File
${formatLocalTime(msg.timestamp)}
`; + } + } + + messagesList.appendChild(messageElement); + }); + + scrollToBottom(); + }) + .catch(error => console.error('Error fetching messages:', error)); } + + const sendMessageForm = document.querySelector('.send-message-form'); if (sendMessageForm) { sendMessageForm.addEventListener('submit', function(event) { @@ -192,7 +249,7 @@ document.addEventListener('DOMContentLoaded', (event) => { if (content || fileInput.files.length > 0) { // Append the message locally const newMessage = document.createElement('div'); - newMessage.classList.add('message'); + newMessage.classList.add('message'); newMessage.dataset.messageId = 'temp-id'; // Temporary ID for the new message const currentTimestamp = new Date(timestamp).getTime(); const showUsername = previousSender !== username || shouldShowUsername(previousTimestamp, currentTimestamp); @@ -203,9 +260,12 @@ document.addEventListener('DOMContentLoaded', (event) => { const usernameElement = document.createElement('div'); usernameElement.classList.add('message-sender'); const timestampFormatted = formatLocalTime(timestamp); - usernameElement.innerHTML = `${username}:
${timestampFormatted}
`; + const profilePictureUrl = `/cdn/${profilePicture}`; // Correct reference + usernameElement.innerHTML = `${username} ${username}:
${timestampFormatted}
`; messagesList.appendChild(usernameElement); } + + if (content) { newMessage.dataset.messageText = content; diff --git a/static/js/group_chat.js b/static/js/group_chat.js index 47e9c61..931137b 100644 --- a/static/js/group_chat.js +++ b/static/js/group_chat.js @@ -65,20 +65,21 @@ document.addEventListener('DOMContentLoaded', (event) => { const showUsername = previousSender !== data.sender || shouldShowUsername(previousTimestamp, currentTimestamp); previousSender = data.sender; previousTimestamp = currentTimestamp; - + const newMessage = document.createElement('div'); newMessage.classList.add('message'); - + newMessage.dataset.messageId = data.id; newMessage.dataset.messageText = data.content; - + if (showUsername) { const usernameElement = document.createElement('div'); usernameElement.classList.add('message-sender'); - usernameElement.innerHTML = `${data.sender}:
${formatLocalTime(data.timestamp)}
`; + const profilePictureUrl = `/cdn/${data.sender_profile_picture}`; + usernameElement.innerHTML = `${data.sender} ${data.sender}:
${formatLocalTime(data.timestamp)}
`; messagesList.appendChild(usernameElement); } - + if (data.content_type === 'text') { if (isImageUrl(data.content)) { newMessage.innerHTML = `Image
${formatLocalTime(data.timestamp)}
`; @@ -96,10 +97,10 @@ document.addEventListener('DOMContentLoaded', (event) => { newMessage.innerHTML = `Download File
${formatLocalTime(data.timestamp)}
`; } } - + messagesList.appendChild(newMessage); scrollToBottom(); - }); + }); if (groupId) { fetch(`/get_group_messages/${groupId}`) @@ -126,10 +127,12 @@ document.addEventListener('DOMContentLoaded', (event) => { if (msg.sender === username) { usernameElement.classList.add('message-myself-sender'); } - usernameElement.innerHTML = `${msg.sender}
${formatLocalTime(msg.timestamp)}
`; + const profilePictureUrl = `/cdn/${msg.sender_profile_picture}`; + usernameElement.innerHTML = `${msg.sender} ${msg.sender}
${formatLocalTime(msg.timestamp)}
`; messagesList.appendChild(usernameElement); } + const messageElement = document.createElement('div'); messageElement.classList.add('message'); diff --git a/static/output.css b/static/output.css index 7826c54..03ade64 100644 --- a/static/output.css +++ b/static/output.css @@ -624,6 +624,10 @@ video { margin-top: 0.75rem; } +.block { + display: block; +} + .flex { display: flex; } @@ -636,6 +640,10 @@ video { height: 400px !important; } +.\!h-\[465px\] { + height: 465px !important; +} + .\!h-fit { height: -moz-fit-content !important; height: fit-content !important; @@ -1178,6 +1186,23 @@ video { color: rgb(255 255 255 / var(--tw-text-opacity)) !important; } +[data-message-text="---"]{ + width:auto !important; + padding: 0 !important; +} + +hr{ + height: 1px; + border: none; + --tw-bg-opacity: 1 !important; + background-color: rgb(241 241 241 / var(--tw-bg-opacity)) !important +} + +hr:is(.dark *) { + --tw-bg-opacity: 1 !important; + background-color: rgb(54 54 54 / var(--tw-bg-opacity)) !important; +} + .hover\:\!text-slate-400:hover { --tw-text-opacity: 1 !important; color: rgb(148 163 184 / var(--tw-text-opacity)) !important; @@ -1213,6 +1238,11 @@ video { background-color: rgb(69 71 146 / var(--tw-bg-opacity)) !important; } +.dark\:\!bg-black:is(.dark *) { + --tw-bg-opacity: 1 !important; + background-color: rgb(0 0 0 / var(--tw-bg-opacity)) !important; +} + .dark\:\!bg-neutral-800:is(.dark *) { --tw-bg-opacity: 1 !important; background-color: rgb(38 38 38 / var(--tw-bg-opacity)) !important; diff --git a/static/styles.css b/static/styles.css index f49e75d..59af186 100644 --- a/static/styles.css +++ b/static/styles.css @@ -430,4 +430,20 @@ ul#friends-list { .dropdown:hover .dropdown-content {display: block;} /* Change the background color of the dropdown button when the dropdown content is shown */ - .dropdown:hover .dropbtn {background-color: transparent;} \ No newline at end of file + .dropdown:hover .dropbtn {background-color: transparent;} + + .profile-picture { + width: 40px !important; + height: 40px !important; + min-width: 40px !important; + min-height: 40px !important; + border-radius: 50% !important; + margin-right: 10px; + position: relative; + right: 44px; + top: 14px; +} +.message-sender > strong, .message-sender > .message-sender-timestamp { + position: relative; + right: 48px; +} \ No newline at end of file diff --git a/templates/admin_panel.html b/templates/admin_panel.html index 15feeaf..09c0009 100644 --- a/templates/admin_panel.html +++ b/templates/admin_panel.html @@ -3,6 +3,7 @@ Admin Panel + {% include 'head.html' %} - +
-

Admin Panel

- +

Admin Panel

+
diff --git a/templates/chat.html b/templates/chat.html index f7d97d8..b7a7217 100644 --- a/templates/chat.html +++ b/templates/chat.html @@ -8,6 +8,7 @@ var friendId = "{{ friend_id }}"; var friendUsername = "{{ friend_username }}"; var username = "{{ username }}"; + var profilePicture = "{{ profile_picture }}"; // Add this line diff --git a/templates/settings.html b/templates/settings.html index 3f70bf0..7ca3aba 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -6,7 +6,7 @@ {% include 'head.html' %} - diff --git a/templates/sidebar.html b/templates/sidebar.html index 829483b..79a60eb 100644 --- a/templates/sidebar.html +++ b/templates/sidebar.html @@ -1,8 +1,5 @@ + - \ No newline at end of file + document.addEventListener('click', function() { + if (contextMenu) { + contextMenu.style.display = 'none'; + } + }); + + const copyUidButton = document.getElementById('copy-uid'); + const removeFriendButton = document.getElementById('rm-friend'); + + if (copyUidButton) { + copyUidButton.addEventListener('click', function() { + if (selectedFriend) { + const friendId = selectedFriend.getAttribute('data-id'); + navigator.clipboard.writeText(friendId).then(() => { + alert('User ID copied to clipboard'); + }); + } + }); + } + + if (removeFriendButton) { + removeFriendButton.addEventListener('click', function() { + if (selectedFriend) { + const friendId = selectedFriend.getAttribute('data-id'); + fetch(`/remove_friend/${friendId}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }).then(response => response.json()) + .then(data => { + const messageDiv = document.getElementById('message'); // Add a div with id 'message' to display messages + if (data.status === 'success') { + selectedFriend.remove(); + if (messageDiv) { + messageDiv.innerHTML = '
Friend removed successfully
'; + } + } else { + if (messageDiv) { + messageDiv.innerHTML = `
Error removing friend: ${data.message}
`; + } + } + }).catch(error => { + console.error('Error removing friend:', error); + const messageDiv = document.getElementById('message'); + if (messageDiv) { + messageDiv.innerHTML = '
Error removing friend
'; + } + }); + } + }); + } + }); +
Username