Added profile pictures
Added profile pictures and fixed some bugs
This commit is contained in:
parent
44c2b2cffa
commit
0ac1f2795b
11 changed files with 367 additions and 99 deletions
72
app.py
72
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 import Flask, render_template, request, redirect, url_for, session, jsonify, send_from_directory, make_response
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
from cryptography.fernet import Fernet, InvalidToken
|
from cryptography.fernet import Fernet, InvalidToken
|
||||||
import os
|
import os
|
||||||
|
import random
|
||||||
|
import string
|
||||||
from datetime import datetime, timedelta, timezone
|
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 werkzeug.security import generate_password_hash, check_password_hash
|
||||||
from flask_socketio import SocketIO, emit, join_room, leave_room
|
from flask_socketio import SocketIO, emit, join_room, leave_room
|
||||||
import logging
|
import logging
|
||||||
|
@ -62,6 +66,7 @@ class User(db.Model):
|
||||||
online = db.Column(db.Boolean, nullable=False, default=False)
|
online = db.Column(db.Boolean, nullable=False, default=False)
|
||||||
is_admin = 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)
|
disabled = db.Column(db.Boolean, nullable=False, default=False)
|
||||||
|
profile_picture = db.Column(db.String(150), nullable=True)
|
||||||
|
|
||||||
class Message(db.Model):
|
class Message(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
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()
|
friend_user = User.query.filter_by(id=friend_id).first()
|
||||||
user = User.query.filter_by(id=current_user_id).first()
|
user = User.query.filter_by(id=current_user_id).first()
|
||||||
friends = Friend.query.filter_by(user_id=user.id).all()
|
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_ids = [f.friend_id for f in friends]
|
||||||
friend_users = User.query.filter(User.id.in_(friend_ids)).all()
|
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()
|
groups = Group.query.filter(Group.id.in_(group_ids)).all()
|
||||||
|
|
||||||
logger.info(f"User {session['username']} opened chat with {friend_user.username}")
|
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:
|
else:
|
||||||
logger.warning(f"User {session['username']} attempted to access chat with non-friend user_id: {friend_id}")
|
logger.warning(f"User {session['username']} attempted to access chat with non-friend user_id: {friend_id}")
|
||||||
return redirect(url_for('dashboard'))
|
return redirect(url_for('dashboard'))
|
||||||
|
@ -343,11 +349,9 @@ def send_message(receiver):
|
||||||
content_type = 'file'
|
content_type = 'file'
|
||||||
|
|
||||||
if content:
|
if content:
|
||||||
# Encrypt text content only, do not encrypt file names
|
|
||||||
encrypted_content = cipher.encrypt(content.encode()).decode() if content_type == 'text' else content
|
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)
|
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()
|
sender_user = User.query.filter_by(username=session['username']).first()
|
||||||
receiver_user = User.query.filter_by(id=receiver).first()
|
receiver_user = User.query.filter_by(id=receiver).first()
|
||||||
if not receiver_user:
|
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
|
decrypted_content = cipher.decrypt(encrypted_content.encode()).decode() if content_type == 'text' else encrypted_content
|
||||||
socketio.emit('new_message', {
|
socketio.emit('new_message', {
|
||||||
'sender': session['username'],
|
'sender': session['username'],
|
||||||
|
'receiver': receiver_user.username, # Include the receiver information
|
||||||
'content': decrypted_content,
|
'content': decrypted_content,
|
||||||
|
'sender_profile_picture': sender_user.profile_picture, # Ensure this is correct
|
||||||
'content_type': content_type,
|
'content_type': content_type,
|
||||||
'timestamp': timestamp,
|
'timestamp': timestamp,
|
||||||
'id': new_message.id
|
'id': new_message.id
|
||||||
}, room=receiver_user.username)
|
}, room=receiver_user.username)
|
||||||
logger.info(f"Message sent from {session['username']} to {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:
|
else:
|
||||||
pending_message = PendingMessage(sender=session['username'], receiver=receiver_user.username, content=encrypted_content, content_type=content_type, timestamp=timestamp_dt)
|
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.add(pending_message)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
logger.info(f"Pending message from {session['username']} to {receiver_user.username}")
|
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
|
return jsonify({'error': 'No content or file provided'}), 400
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error sending message from {session['username']} to {receiver}: {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,
|
'id': msg.id,
|
||||||
'sender': msg.sender.username,
|
'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': cipher.decrypt(msg.content.encode()).decode() if msg.content_type == 'text' else msg.content,
|
||||||
'content_type': msg.content_type,
|
'content_type': msg.content_type,
|
||||||
'timestamp': msg.timestamp.strftime("%Y-%m-%d %H:%M:%S")
|
'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')}")
|
logger.warning(f"Unauthorized message retrieval attempt by user {session.get('username', 'unknown')}")
|
||||||
return jsonify({'error': 'Unauthorized'}), 401
|
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/<filename>')
|
@app.route('/cdn/<filename>')
|
||||||
def uploaded_file(filename):
|
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()
|
reciprocal_friend = Friend.query.filter_by(user_id=friend.friend_id, friend_id=user.id).first()
|
||||||
if reciprocal_friend:
|
if reciprocal_friend:
|
||||||
db.session.delete(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()
|
db.session.commit()
|
||||||
|
|
||||||
socketio.emit('friend_removed', {
|
socketio.emit('friend_removed', {
|
||||||
|
@ -522,10 +576,10 @@ def remove_friend(friend_id):
|
||||||
}, room=friend.friend.username)
|
}, room=friend.friend.username)
|
||||||
logger.info(f"Friend {friend.friend.username} removed by {session['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']}")
|
logger.warning(f"Friend removal failed: Friend not found or unauthorized access by {session['username']}")
|
||||||
return 'Friend not found'
|
return jsonify({'status': 'error', 'message': 'Friend not found or unauthorized access'}), 404
|
||||||
return redirect(url_for('login'))
|
return jsonify({'status': 'error', 'message': 'Unauthorized'}), 401
|
||||||
|
|
||||||
@app.route('/create_group', methods=['POST'])
|
@app.route('/create_group', methods=['POST'])
|
||||||
def create_group():
|
def create_group():
|
||||||
|
@ -633,6 +687,7 @@ def send_group_message(group_id):
|
||||||
'group_id': group_id,
|
'group_id': group_id,
|
||||||
'sender': session['username'],
|
'sender': session['username'],
|
||||||
'content': decrypted_content,
|
'content': decrypted_content,
|
||||||
|
'sender_profile_picture': sender_user.profile_picture, # Add profile picture
|
||||||
'content_type': content_type,
|
'content_type': content_type,
|
||||||
'timestamp': timestamp,
|
'timestamp': timestamp,
|
||||||
'id': new_message.id
|
'id': new_message.id
|
||||||
|
@ -656,6 +711,7 @@ def get_group_messages(group_id):
|
||||||
'id': msg.GroupMessage.id,
|
'id': msg.GroupMessage.id,
|
||||||
'group_id': msg.GroupMessage.group_id,
|
'group_id': msg.GroupMessage.group_id,
|
||||||
'sender': msg.User.username, # Use the username from the User table
|
'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': cipher.decrypt(msg.GroupMessage.content.encode()).decode() if msg.GroupMessage.content_type == 'text' else msg.GroupMessage.content,
|
||||||
'content_type': msg.GroupMessage.content_type,
|
'content_type': msg.GroupMessage.content_type,
|
||||||
'timestamp': msg.GroupMessage.timestamp.strftime("%Y-%m-%d %H:%M:%S")
|
'timestamp': msg.GroupMessage.timestamp.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
|
@ -7,3 +7,4 @@ Werkzeug==3.0.3
|
||||||
python-socketio==5.11.2
|
python-socketio==5.11.2
|
||||||
python-engineio==4.9.1
|
python-engineio==4.9.1
|
||||||
PyJWT==2.8.0
|
PyJWT==2.8.0
|
||||||
|
Pillow==10.3.0
|
|
@ -110,4 +110,15 @@
|
||||||
|
|
||||||
.dropdown-content {
|
.dropdown-content {
|
||||||
@apply bg-[#fafafa] dark:!bg-[#141414] shadow text-black dark:!text-white
|
@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]
|
||||||
}
|
}
|
|
@ -49,14 +49,14 @@ document.addEventListener('DOMContentLoaded', (event) => {
|
||||||
function formatLocalTime(utcTimestamp) {
|
function formatLocalTime(utcTimestamp) {
|
||||||
const date = new Date(utcTimestamp);
|
const date = new Date(utcTimestamp);
|
||||||
const localDate = new Date(date.getTime() - date.getTimezoneOffset() * 60000);
|
const localDate = new Date(date.getTime() - date.getTimezoneOffset() * 60000);
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||||
const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
|
const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
|
||||||
|
|
||||||
const timeFormatter = { hour: '2-digit', minute: '2-digit' };
|
const timeFormatter = { hour: '2-digit', minute: '2-digit' };
|
||||||
const dateFormatter = { year: 'numeric', month: '2-digit', day: '2-digit' };
|
const dateFormatter = { year: 'numeric', month: '2-digit', day: '2-digit' };
|
||||||
|
|
||||||
let formattedTime;
|
let formattedTime;
|
||||||
if (localDate >= today) {
|
if (localDate >= today) {
|
||||||
formattedTime = `Today at ${localDate.toLocaleTimeString(undefined, timeFormatter)}`;
|
formattedTime = `Today at ${localDate.toLocaleTimeString(undefined, timeFormatter)}`;
|
||||||
|
@ -67,39 +67,90 @@ document.addEventListener('DOMContentLoaded', (event) => {
|
||||||
}
|
}
|
||||||
return formattedTime;
|
return formattedTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function isImageUrl(url) {
|
function isImageUrl(url) {
|
||||||
return /\.(jpg|jpeg|png|gif|bmp|svg)$/i.test(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 = `
|
||||||
|
<img src="/cdn/${senderProfilePicture}" alt="/cdn/${msg.sender}" class="profile-picture">
|
||||||
|
<strong>${msg.sender}</strong>
|
||||||
|
<div class="message-sender-timestamp">${formatLocalTime(msg.timestamp)}</div>`;
|
||||||
|
messagesList.appendChild(usernameElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.content_type === 'text') {
|
||||||
|
if (isImageUrl(msg.content)) {
|
||||||
|
newMessage.innerHTML = `<img src="${msg.content}" alt="Image" class="enhanceable-image" style="max-width: 200px; max-height: 200px;" /><div class="timestamp">${formatLocalTime(msg.timestamp)}</div>`;
|
||||||
|
} else {
|
||||||
|
newMessage.innerHTML = `${md.render(msg.content)}<div class="timestamp">${formatLocalTime(msg.timestamp)}</div>`;
|
||||||
|
}
|
||||||
|
} else if (msg.content_type === 'file') {
|
||||||
|
const isImage = isImageUrl(msg.content);
|
||||||
|
const isVideo = /\.(mp4|webm|ogg)$/i.test(msg.content);
|
||||||
|
if (isImage) {
|
||||||
|
newMessage.innerHTML = `<img src="/cdn/${msg.content}" alt="Image" class="enhanceable-image" style="max-width: 200px; max-height: 200px;" /><div class="timestamp">${formatLocalTime(msg.timestamp)}</div>`;
|
||||||
|
} else if (isVideo) {
|
||||||
|
newMessage.innerHTML = `<video controls style="max-width: 200px; max-height: 200px;"><source src="/cdn/${msg.content}" type="video/mp4"></video><div class="timestamp">${formatLocalTime(msg.timestamp)}</div>`;
|
||||||
|
} else {
|
||||||
|
newMessage.innerHTML = `<a href="/cdn/${msg.content}" target="_blank">Download File</a><div class="timestamp">${formatLocalTime(msg.timestamp)}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
messagesList.appendChild(newMessage);
|
||||||
|
scrollToBottom();
|
||||||
|
}
|
||||||
|
|
||||||
let previousSender = null;
|
let previousSender = null;
|
||||||
let previousTimestamp = 0;
|
let previousTimestamp = 0;
|
||||||
|
|
||||||
socket.on('new_message', function(data) {
|
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}`);
|
showNotification('New Message', `You have received a new message from ${data.sender}`);
|
||||||
const currentTimestamp = new Date(data.timestamp).getTime();
|
const currentTimestamp = new Date(data.timestamp).getTime();
|
||||||
const showUsername = previousSender !== data.sender || shouldShowUsername(previousTimestamp, currentTimestamp);
|
const showUsername = previousSender !== data.sender || shouldShowUsername(previousTimestamp, currentTimestamp);
|
||||||
previousSender = data.sender;
|
previousSender = data.sender;
|
||||||
previousTimestamp = currentTimestamp;
|
previousTimestamp = currentTimestamp;
|
||||||
|
|
||||||
const messageElement = document.createElement('div');
|
const messageElement = document.createElement('div');
|
||||||
messageElement.classList.add('message');
|
messageElement.classList.add('message');
|
||||||
|
|
||||||
const newMessage = document.createElement('div');
|
const newMessage = document.createElement('div');
|
||||||
newMessage.classList.add('message');
|
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) {
|
if (showUsername) {
|
||||||
const usernameElement = document.createElement('div');
|
const usernameElement = document.createElement('div');
|
||||||
usernameElement.classList.add('message-sender');
|
usernameElement.classList.add('message-sender');
|
||||||
const timestampFormatted = formatLocalTime(data.timestamp);
|
const timestampFormatted = formatLocalTime(data.timestamp);
|
||||||
usernameElement.innerHTML = `<strong>${data.sender}</strong>: <div class="message-sender-timestamp">${timestampFormatted}</div>`;
|
|
||||||
|
// Include the profile picture if available
|
||||||
|
const profilePicture = data.sender_profile_picture ? `<img src="/cdn/${data.sender_profile_picture}" alt="${data.sender}" class="profile-picture enhanceable-image">` : '';
|
||||||
|
|
||||||
|
usernameElement.innerHTML = `${profilePicture} <strong>${data.sender}</strong>: <div class="message-sender-timestamp">${timestampFormatted}</div>`;
|
||||||
messagesList.appendChild(usernameElement);
|
messagesList.appendChild(usernameElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.content_type === 'text') {
|
if (data.content_type === 'text') {
|
||||||
if (isImageUrl(data.content)) {
|
if (isImageUrl(data.content)) {
|
||||||
newMessage.innerHTML = `<img src="${data.content}" alt="Image" class="enhanceable-image" style="max-width: 300px; max-height: 300px;" /><div class="timestamp">${formatLocalTime(data.timestamp)}</div>`;
|
newMessage.innerHTML = `<img src="${data.content}" alt="Image" class="enhanceable-image" style="max-width: 300px; max-height: 300px;" /><div class="timestamp">${formatLocalTime(data.timestamp)}</div>`;
|
||||||
|
@ -117,65 +168,71 @@ document.addEventListener('DOMContentLoaded', (event) => {
|
||||||
newMessage.innerHTML = `<a href="/cdn/${data.content}" target="_blank">Download File</a><div class="timestamp">${formatLocalTime(data.timestamp)}</div>`;
|
newMessage.innerHTML = `<a href="/cdn/${data.content}" target="_blank">Download File</a><div class="timestamp">${formatLocalTime(data.timestamp)}</div>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
messagesList.appendChild(newMessage);
|
messagesList.appendChild(newMessage);
|
||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
if (friendId) {
|
if (friendId) {
|
||||||
fetch(`/get_messages/${friendId}`)
|
fetch(`/get_messages/${friendId}`)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
messagesList.innerHTML = '';
|
messagesList.innerHTML = '';
|
||||||
document.getElementById('chat-with').textContent = `Chatting with ${friendUsername}`;
|
document.getElementById('chat-with').textContent = `Chatting with ${friendUsername}`;
|
||||||
document.getElementById('send-message-form').dataset.receiver = friendId;
|
document.getElementById('send-message-form').dataset.receiver = friendId;
|
||||||
|
|
||||||
data.messages.forEach((msg, index, messages) => {
|
data.messages.forEach((msg, index, messages) => {
|
||||||
const currentTimestamp = new Date(msg.timestamp).getTime();
|
const currentTimestamp = new Date(msg.timestamp).getTime();
|
||||||
const previousMessage = messages[index - 1];
|
const previousMessage = messages[index - 1];
|
||||||
const previousTimestamp = previousMessage ? new Date(previousMessage.timestamp).getTime() : 0;
|
const previousTimestamp = previousMessage ? new Date(previousMessage.timestamp).getTime() : 0;
|
||||||
const showUsername = index === 0 || msg.sender !== previousMessage.sender || shouldShowUsername(previousTimestamp, currentTimestamp);
|
const showUsername = index === 0 || msg.sender !== previousMessage.sender || shouldShowUsername(previousTimestamp, currentTimestamp);
|
||||||
|
|
||||||
if (showUsername) {
|
if (showUsername) {
|
||||||
const usernameElement = document.createElement('div');
|
const usernameElement = document.createElement('div');
|
||||||
usernameElement.classList.add('message-sender');
|
usernameElement.classList.add('message-sender');
|
||||||
|
if (msg.sender === username) {
|
||||||
const timestampFormatted = new Date(msg.timestamp).toLocaleString(undefined, {
|
usernameElement.classList.add('message-myself-sender');
|
||||||
weekday: 'short', year: 'numeric', month: 'short', day: 'numeric',
|
|
||||||
hour: '2-digit', minute: '2-digit'
|
|
||||||
});
|
|
||||||
usernameElement.innerHTML = `<strong>${msg.sender}</strong> <div class="message-sender-timestamp">${timestampFormatted}</div>`;
|
|
||||||
messagesList.appendChild(usernameElement);
|
|
||||||
}
|
}
|
||||||
|
// Include the profile picture if available
|
||||||
|
const profilePicture = msg.sender_profile_picture ? `<img src="${msg.sender_profile_picture}" alt="${msg.sender}" class="profile-picture enhanceable-image">` : '';
|
||||||
const messageElement = document.createElement('div');
|
usernameElement.innerHTML = `${profilePicture} <strong>${msg.sender}</strong><div class="message-sender-timestamp">${formatLocalTime(msg.timestamp)}</div>`;
|
||||||
messageElement.classList.add('message');
|
messagesList.appendChild(usernameElement);
|
||||||
|
}
|
||||||
messageElement.dataset.messageId = msg.id;
|
|
||||||
messageElement.dataset.messageText = msg.content;
|
const messageElement = document.createElement('div');
|
||||||
|
messageElement.classList.add('message');
|
||||||
if (msg.content_type === 'text') {
|
|
||||||
messageElement.innerHTML = `${md.render(msg.content)}<div class="timestamp">${msg.timestamp}</div>`;
|
messageElement.dataset.messageId = msg.id;
|
||||||
} else if (msg.content_type === 'file') {
|
messageElement.dataset.messageText = msg.content;
|
||||||
const isImage = /\.(jpg|jpeg|png|gif|bmp|svg)$/i.test(msg.content);
|
|
||||||
const isVideo = /\.(mp4|webm|ogg)$/i.test(msg.content);
|
if (msg.content_type === 'text') {
|
||||||
if (isImage) {
|
if (isImageUrl(msg.content)) {
|
||||||
messageElement.innerHTML = `<img src="/cdn/${msg.content}" alt="Image" class="enhanceable-image" style="max-width: 200px; max-height: 200px;" /><div class="timestamp">${msg.timestamp}</div>`;
|
messageElement.innerHTML = `<img src="${msg.content}" alt="Image" class="enhanceable-image" style="max-width: 200px; max-height: 200px;" /><div class="timestamp">${formatLocalTime(msg.timestamp)}</div>`;
|
||||||
} else if (isVideo) {
|
} else {
|
||||||
messageElement.innerHTML = `<video controls style="max-width: 200px; max-height: 200px;"><source src="/cdn/${msg.content}" type="video/mp4"></video><div class="timestamp">${msg.timestamp}</div>`;
|
messageElement.innerHTML = `${md.render(msg.content)}<div class="timestamp">${formatLocalTime(msg.timestamp)}</div>`;
|
||||||
} else {
|
|
||||||
messageElement.innerHTML = `<a href="/cdn/${msg.content}" target="_blank">Download File</a><div class="timestamp">${msg.timestamp}</div>`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else if (msg.content_type === 'file') {
|
||||||
messagesList.appendChild(messageElement);
|
const isImage = isImageUrl(msg.content);
|
||||||
});
|
const isVideo = /\.(mp4|webm|ogg)$/i.test(msg.content);
|
||||||
|
if (isImage) {
|
||||||
scrollToBottom();
|
messageElement.innerHTML = `<img src="/cdn/${msg.content}" alt="Image" class="enhanceable-image" style="max-width: 200px; max-height: 200px;" /><div class="timestamp">${formatLocalTime(msg.timestamp)}</div>`;
|
||||||
})
|
} else if (isVideo) {
|
||||||
.catch(error => console.error('Error fetching messages:', error));
|
messageElement.innerHTML = `<video controls style="max-width: 200px; max-height: 200px;"><source src="/cdn/${msg.content}" type="video/mp4"></video><div class="timestamp">${formatLocalTime(msg.timestamp)}</div>`;
|
||||||
|
} else {
|
||||||
|
messageElement.innerHTML = `<a href="/cdn/${msg.content}" target="_blank">Download File</a><div class="timestamp">${formatLocalTime(msg.timestamp)}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
messagesList.appendChild(messageElement);
|
||||||
|
});
|
||||||
|
|
||||||
|
scrollToBottom();
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error fetching messages:', error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const sendMessageForm = document.querySelector('.send-message-form');
|
const sendMessageForm = document.querySelector('.send-message-form');
|
||||||
if (sendMessageForm) {
|
if (sendMessageForm) {
|
||||||
sendMessageForm.addEventListener('submit', function(event) {
|
sendMessageForm.addEventListener('submit', function(event) {
|
||||||
|
@ -192,7 +249,7 @@ document.addEventListener('DOMContentLoaded', (event) => {
|
||||||
if (content || fileInput.files.length > 0) {
|
if (content || fileInput.files.length > 0) {
|
||||||
// Append the message locally
|
// Append the message locally
|
||||||
const newMessage = document.createElement('div');
|
const newMessage = document.createElement('div');
|
||||||
newMessage.classList.add('message');
|
newMessage.classList.add('message');
|
||||||
newMessage.dataset.messageId = 'temp-id'; // Temporary ID for the new message
|
newMessage.dataset.messageId = 'temp-id'; // Temporary ID for the new message
|
||||||
const currentTimestamp = new Date(timestamp).getTime();
|
const currentTimestamp = new Date(timestamp).getTime();
|
||||||
const showUsername = previousSender !== username || shouldShowUsername(previousTimestamp, currentTimestamp);
|
const showUsername = previousSender !== username || shouldShowUsername(previousTimestamp, currentTimestamp);
|
||||||
|
@ -203,9 +260,12 @@ document.addEventListener('DOMContentLoaded', (event) => {
|
||||||
const usernameElement = document.createElement('div');
|
const usernameElement = document.createElement('div');
|
||||||
usernameElement.classList.add('message-sender');
|
usernameElement.classList.add('message-sender');
|
||||||
const timestampFormatted = formatLocalTime(timestamp);
|
const timestampFormatted = formatLocalTime(timestamp);
|
||||||
usernameElement.innerHTML = `<strong>${username}</strong>: <div class="message-sender-timestamp">${timestampFormatted}</div>`;
|
const profilePictureUrl = `/cdn/${profilePicture}`; // Correct reference
|
||||||
|
usernameElement.innerHTML = `<img src="${profilePictureUrl}" alt="${username}" class="profile-picture enhanceable-image"> <strong>${username}</strong>: <div class="message-sender-timestamp">${timestampFormatted}</div>`;
|
||||||
messagesList.appendChild(usernameElement);
|
messagesList.appendChild(usernameElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (content) {
|
if (content) {
|
||||||
newMessage.dataset.messageText = content;
|
newMessage.dataset.messageText = content;
|
||||||
|
|
|
@ -65,20 +65,21 @@ document.addEventListener('DOMContentLoaded', (event) => {
|
||||||
const showUsername = previousSender !== data.sender || shouldShowUsername(previousTimestamp, currentTimestamp);
|
const showUsername = previousSender !== data.sender || shouldShowUsername(previousTimestamp, currentTimestamp);
|
||||||
previousSender = data.sender;
|
previousSender = data.sender;
|
||||||
previousTimestamp = currentTimestamp;
|
previousTimestamp = currentTimestamp;
|
||||||
|
|
||||||
const newMessage = document.createElement('div');
|
const newMessage = document.createElement('div');
|
||||||
newMessage.classList.add('message');
|
newMessage.classList.add('message');
|
||||||
|
|
||||||
newMessage.dataset.messageId = data.id;
|
newMessage.dataset.messageId = data.id;
|
||||||
newMessage.dataset.messageText = data.content;
|
newMessage.dataset.messageText = data.content;
|
||||||
|
|
||||||
if (showUsername) {
|
if (showUsername) {
|
||||||
const usernameElement = document.createElement('div');
|
const usernameElement = document.createElement('div');
|
||||||
usernameElement.classList.add('message-sender');
|
usernameElement.classList.add('message-sender');
|
||||||
usernameElement.innerHTML = `<strong>${data.sender}</strong>:<div class="message-sender-timestamp">${formatLocalTime(data.timestamp)}</div>`;
|
const profilePictureUrl = `/cdn/${data.sender_profile_picture}`;
|
||||||
|
usernameElement.innerHTML = `<img src="${profilePictureUrl}" alt="${data.sender}" class="profile-picture"> <strong>${data.sender}</strong>:<div class="message-sender-timestamp">${formatLocalTime(data.timestamp)}</div>`;
|
||||||
messagesList.appendChild(usernameElement);
|
messagesList.appendChild(usernameElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.content_type === 'text') {
|
if (data.content_type === 'text') {
|
||||||
if (isImageUrl(data.content)) {
|
if (isImageUrl(data.content)) {
|
||||||
newMessage.innerHTML = `<img src="${data.content}" alt="Image" class="enhanceable-image" style="max-width: 300px; max-height: 300px;" /><div class="timestamp">${formatLocalTime(data.timestamp)}</div>`;
|
newMessage.innerHTML = `<img src="${data.content}" alt="Image" class="enhanceable-image" style="max-width: 300px; max-height: 300px;" /><div class="timestamp">${formatLocalTime(data.timestamp)}</div>`;
|
||||||
|
@ -96,10 +97,10 @@ document.addEventListener('DOMContentLoaded', (event) => {
|
||||||
newMessage.innerHTML = `<a href="/cdn/${data.content}" target="_blank">Download File</a><div class="timestamp">${formatLocalTime(data.timestamp)}</div>`;
|
newMessage.innerHTML = `<a href="/cdn/${data.content}" target="_blank">Download File</a><div class="timestamp">${formatLocalTime(data.timestamp)}</div>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
messagesList.appendChild(newMessage);
|
messagesList.appendChild(newMessage);
|
||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (groupId) {
|
if (groupId) {
|
||||||
fetch(`/get_group_messages/${groupId}`)
|
fetch(`/get_group_messages/${groupId}`)
|
||||||
|
@ -126,10 +127,12 @@ document.addEventListener('DOMContentLoaded', (event) => {
|
||||||
if (msg.sender === username) {
|
if (msg.sender === username) {
|
||||||
usernameElement.classList.add('message-myself-sender');
|
usernameElement.classList.add('message-myself-sender');
|
||||||
}
|
}
|
||||||
usernameElement.innerHTML = `<strong>${msg.sender}</strong><div class="message-sender-timestamp">${formatLocalTime(msg.timestamp)}</div>`;
|
const profilePictureUrl = `/cdn/${msg.sender_profile_picture}`;
|
||||||
|
usernameElement.innerHTML = `<img src="${profilePictureUrl}" alt="${msg.sender}" class="profile-picture"> <strong>${msg.sender}</strong><div class="message-sender-timestamp">${formatLocalTime(msg.timestamp)}</div>`;
|
||||||
messagesList.appendChild(usernameElement);
|
messagesList.appendChild(usernameElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const messageElement = document.createElement('div');
|
const messageElement = document.createElement('div');
|
||||||
messageElement.classList.add('message');
|
messageElement.classList.add('message');
|
||||||
|
|
|
@ -624,6 +624,10 @@ video {
|
||||||
margin-top: 0.75rem;
|
margin-top: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.flex {
|
.flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
@ -636,6 +640,10 @@ video {
|
||||||
height: 400px !important;
|
height: 400px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.\!h-\[465px\] {
|
||||||
|
height: 465px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.\!h-fit {
|
.\!h-fit {
|
||||||
height: -moz-fit-content !important;
|
height: -moz-fit-content !important;
|
||||||
height: fit-content !important;
|
height: fit-content !important;
|
||||||
|
@ -1178,6 +1186,23 @@ video {
|
||||||
color: rgb(255 255 255 / var(--tw-text-opacity)) !important;
|
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 {
|
.hover\:\!text-slate-400:hover {
|
||||||
--tw-text-opacity: 1 !important;
|
--tw-text-opacity: 1 !important;
|
||||||
color: rgb(148 163 184 / var(--tw-text-opacity)) !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;
|
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 *) {
|
.dark\:\!bg-neutral-800:is(.dark *) {
|
||||||
--tw-bg-opacity: 1 !important;
|
--tw-bg-opacity: 1 !important;
|
||||||
background-color: rgb(38 38 38 / var(--tw-bg-opacity)) !important;
|
background-color: rgb(38 38 38 / var(--tw-bg-opacity)) !important;
|
||||||
|
|
|
@ -430,4 +430,20 @@ ul#friends-list {
|
||||||
.dropdown:hover .dropdown-content {display: block;}
|
.dropdown:hover .dropdown-content {display: block;}
|
||||||
|
|
||||||
/* Change the background color of the dropdown button when the dropdown content is shown */
|
/* Change the background color of the dropdown button when the dropdown content is shown */
|
||||||
.dropdown:hover .dropbtn {background-color: transparent;}
|
.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;
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Admin Panel</title>
|
<title>Admin Panel</title>
|
||||||
|
{% include 'head.html' %}
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
|
||||||
<script>
|
<script>
|
||||||
function changePassword(userId) {
|
function changePassword(userId) {
|
||||||
|
@ -62,10 +63,10 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="dark:!bg-black bg-white">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Admin Panel</h1>
|
<h1 class="dark:!text-white text-black">Admin Panel</h1>
|
||||||
<table class="table">
|
<table class="table dark:!text-white text-black">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Username</th>
|
<th>Username</th>
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
var friendId = "{{ friend_id }}";
|
var friendId = "{{ friend_id }}";
|
||||||
var friendUsername = "{{ friend_username }}";
|
var friendUsername = "{{ friend_username }}";
|
||||||
var username = "{{ username }}";
|
var username = "{{ username }}";
|
||||||
|
var profilePicture = "{{ profile_picture }}"; // Add this line
|
||||||
</script>
|
</script>
|
||||||
<script src="{{ url_for('static', filename='js/chat.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/chat.js') }}"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
{% include 'head.html' %}
|
{% include 'head.html' %}
|
||||||
</head>
|
</head>
|
||||||
<body class="login-body">
|
<body class="login-body">
|
||||||
<div class="login-div">
|
<div class="login-div !h-[465px]">
|
||||||
<form class="login-form" id="settings-form">
|
<form class="login-form" id="settings-form">
|
||||||
<img class="login-img" src="{{ url_for('static', filename='images/ms-style-logo.svg') }}" alt="Image">
|
<img class="login-img" src="{{ url_for('static', filename='images/ms-style-logo.svg') }}" alt="Image">
|
||||||
<h2 class="login-h2">Settings</h2>
|
<h2 class="login-h2">Settings</h2>
|
||||||
|
@ -18,13 +18,24 @@
|
||||||
<option value="dark" {% if current_theme == 'dark' %}selected{% endif %}>Dark</option>
|
<option value="dark" {% if current_theme == 'dark' %}selected{% endif %}>Dark</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="login-input-div-first !flex-row">
|
|
||||||
<a class="no-account no-account-button" href="{{ url_for('dashboard') }}"> Go Back</a>
|
|
||||||
</div>
|
|
||||||
<div class="login-input-div-first items-end">
|
<div class="login-input-div-first items-end">
|
||||||
<button class="ms-button-signin" type="submit" class="btn btn-primary">Save Settings</button>
|
<button class="ms-button-signin" type="submit" class="btn btn-primary">Save Settings</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<form class="login-form" id="upload-profile-picture-form" action="{{ url_for('upload_profile_picture') }}" method="POST" enctype="multipart/form-data">
|
||||||
|
<h2 class="login-h2">Upload Profile Picture</h2>
|
||||||
|
<div class="login-input-div-first">
|
||||||
|
<label for="profile_picture" class="login-label">Choose a profile picture:</label>
|
||||||
|
<input class="login-input" type="file" name="profile_picture" id="profile_picture" accept=".png, .jpg, .jpeg, .gif, .webp">
|
||||||
|
</div>
|
||||||
|
<div class="login-input-div-first items-end">
|
||||||
|
<button class="ms-button-signin" type="submit" class="btn btn-primary">Upload</button>
|
||||||
|
</div>
|
||||||
|
<div class="login-input-div-first !flex-row">
|
||||||
|
<a class="no-account no-account-button" href="{{ url_for('dashboard') }}"> Go Back</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
<div id="message" class="mt-3"></div>
|
<div id="message" class="mt-3"></div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
|
@ -62,6 +73,12 @@
|
||||||
const messageDiv = document.getElementById('message');
|
const messageDiv = document.getElementById('message');
|
||||||
messageDiv.innerHTML = '<div class="alert alert-success">Settings saved successfully!</div>';
|
messageDiv.innerHTML = '<div class="alert alert-success">Settings saved successfully!</div>';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Handle the profile picture upload
|
||||||
|
document.querySelector('#upload-profile-picture-form').addEventListener('submit', function(event) {
|
||||||
|
const messageDiv = document.getElementById('message');
|
||||||
|
messageDiv.innerHTML = ''; // Clear any previous messages
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
<div class="sidebar w-fit bg-transparent text-white flex flex-col items-center">
|
<div class="sidebar w-fit bg-transparent text-white flex flex-col items-center">
|
||||||
<div class="sidebar-content h-full w-60 p-5 rounded-t-lg">
|
<div class="sidebar-content h-full w-60 p-5 rounded-t-lg">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h4 class="flex flex-row items-center">
|
<h4 class="flex flex-row items-center">
|
||||||
<i class="fa-solid fa-table-columns text-slate-900 dark:!text-slate-200"></i>
|
<i class="fa-solid fa-table-columns text-slate-900 dark:!text-slate-200"></i>
|
||||||
<a class="text-slate-900 dark:!text-slate-100 pl-2" href="/dashboard">Dashboard</a>
|
<a class="text-slate-900 dark:!text-slate-100 pl-2" href="/dashboard">Dashboard</a>
|
||||||
|
@ -13,7 +10,7 @@
|
||||||
</h4>
|
</h4>
|
||||||
<ul id="friends-list">
|
<ul id="friends-list">
|
||||||
{% for friend in friends %}
|
{% for friend in friends %}
|
||||||
<li data-username="{{ friend.username }}">
|
<li data-id="{{ friend.id }}" data-username="{{ friend.username }}">
|
||||||
<span class="status-indicator {{ 'online' if friend.online else 'offline' }}"></span>
|
<span class="status-indicator {{ 'online' if friend.online else 'offline' }}"></span>
|
||||||
<a class="dark:!text-slate-400 text-slate-900 hover:!text-slate-400 hover:dark:!text-slate-200" href="/chat/{{ friend.id }}">{{ friend.username }}</a>
|
<a class="dark:!text-slate-400 text-slate-900 hover:!text-slate-400 hover:dark:!text-slate-200" href="/chat/{{ friend.id }}">{{ friend.username }}</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -30,15 +27,90 @@
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<div id="context-menu2" class="custom-context-menu">
|
||||||
// Function to refresh the page
|
<ul>
|
||||||
function refreshPage() {
|
<li id="copy-uid">Copy User ID</li>
|
||||||
location.reload();
|
<li id="rm-friend">Remove Friend</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="message" class="mt-3"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const contextMenu = document.getElementById('context-menu2');
|
||||||
|
const friendsList = document.getElementById('friends-list');
|
||||||
|
let selectedFriend = null;
|
||||||
|
|
||||||
|
if (friendsList) {
|
||||||
|
friendsList.addEventListener('contextmenu', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const friendElement = event.target.closest('li');
|
||||||
|
if (friendElement) {
|
||||||
|
selectedFriend = friendElement;
|
||||||
|
if (contextMenu) {
|
||||||
|
contextMenu.style.top = `${event.clientY}px`;
|
||||||
|
contextMenu.style.left = `${event.clientX}px`;
|
||||||
|
contextMenu.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the interval to refresh the page every 3 seconds (3000 milliseconds)
|
document.addEventListener('click', function() {
|
||||||
// setInterval(refreshPage, 2000);
|
if (contextMenu) {
|
||||||
</script>
|
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 = '<div class="alert alert-success">Friend removed successfully</div>';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (messageDiv) {
|
||||||
|
messageDiv.innerHTML = `<div class="alert alert-danger">Error removing friend: ${data.message}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('Error removing friend:', error);
|
||||||
|
const messageDiv = document.getElementById('message');
|
||||||
|
if (messageDiv) {
|
||||||
|
messageDiv.innerHTML = '<div class="alert alert-danger">Error removing friend</div>';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
Loading…
Reference in a new issue