|
{% extends "base.html" %}
|
|
|
|
{% block title %}{{ user.username }} - الرسائل{% endblock %}
|
|
|
|
{% block content %}
|
|
|
|
<style>
|
|
|
|
.messages-container {
|
|
max-width: 600px;
|
|
margin: 20px auto;
|
|
padding: 10px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 10px;
|
|
background-color: #f9f9f9;
|
|
font-family: Arial, sans-serif;
|
|
}
|
|
|
|
|
|
.user-info {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 15px;
|
|
padding: 10px;
|
|
background-color: #ffffff;
|
|
border-bottom: 1px solid #ddd;
|
|
}
|
|
|
|
.profile-photo {
|
|
width: 50px;
|
|
height: 50px;
|
|
border-radius: 50%;
|
|
margin-right: 10px;
|
|
}
|
|
|
|
.username {
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
|
|
.messages-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
max-height: 400px;
|
|
overflow-y: auto;
|
|
padding: 10px;
|
|
background-color: #ffffff;
|
|
border-radius: 5px;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
|
|
.message-item {
|
|
margin: 5px 0;
|
|
padding: 10px;
|
|
border-radius: 15px;
|
|
max-width: 75%;
|
|
word-wrap: break-word;
|
|
font-size: 14px;
|
|
}
|
|
|
|
|
|
.message-item.sent {
|
|
background-color: #0078ff;
|
|
color: #ffffff;
|
|
align-self: flex-end;
|
|
text-align: right;
|
|
}
|
|
|
|
|
|
.message-item.received {
|
|
background-color: #f1f0f0;
|
|
color: #000000;
|
|
align-self: flex-start;
|
|
text-align: left;
|
|
}
|
|
|
|
|
|
.message-timestamp {
|
|
font-size: 10px;
|
|
color: #666;
|
|
margin-top: 5px;
|
|
display: block;
|
|
}
|
|
|
|
|
|
.message-input {
|
|
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
textarea#message-input {
|
|
width: 100%;
|
|
height: 50px;
|
|
padding: 10px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 5px;
|
|
resize: none;
|
|
font-size: 14px;
|
|
}
|
|
|
|
button#send-message-btn {
|
|
background-color: #0078ff;
|
|
color: #ffffff;
|
|
border: none;
|
|
padding: 10px 15px;
|
|
border-radius: 5px;
|
|
font-size: 14px;
|
|
cursor: pointer;
|
|
transition: background-color 0.3s;
|
|
}
|
|
|
|
button#send-message-btn:hover {
|
|
background-color: #005bb5;
|
|
}
|
|
|
|
.received .seen-status{
|
|
display: none;
|
|
}
|
|
img{
|
|
max-width: 100%;
|
|
}
|
|
|
|
video{
|
|
max-width: 100%;
|
|
}
|
|
|
|
.input-group{
|
|
display: flex;
|
|
flex-direction: row-reverse;
|
|
align-items: center;
|
|
position: fixed;
|
|
bottom: 10px;
|
|
width: calc(100vw - 16.8px);
|
|
}
|
|
|
|
|
|
.file-upload-btn{
|
|
font-size: 25px;
|
|
margin-top: 5px;
|
|
}
|
|
|
|
|
|
.loading-circle {
|
|
display: inline-block;
|
|
width: 40px;
|
|
height: 40px;
|
|
border: 4px solid #0078ff;
|
|
border-top: 4px solid transparent;
|
|
border-radius: 50%;
|
|
animation: spin 0.8s linear infinite;
|
|
margin: 10px auto;
|
|
box-shadow: 0px 0px 10px rgba(0, 120, 255, 0.5);
|
|
}
|
|
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
|
|
|
|
|
|
.modal {
|
|
display: none;
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0, 0, 0, 0.8);
|
|
justify-content: center;
|
|
align-items: center;
|
|
z-index: 1000;
|
|
}
|
|
|
|
.modal-content {
|
|
background: white;
|
|
padding: 20px;
|
|
border-radius: 10px;
|
|
text-align: center;
|
|
position: relative;
|
|
max-width: 90%;
|
|
max-height: 90%;
|
|
margin-left: auto;
|
|
top: 50%;
|
|
margin-right: auto;
|
|
transform: translate(0, -50%);
|
|
}
|
|
|
|
#closeModal {
|
|
position: absolute;
|
|
top: 10px;
|
|
right: 10px;
|
|
background: red;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 50%;
|
|
width: 30px;
|
|
height: 30px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.send-button {
|
|
background: #007bff;
|
|
color: white;
|
|
border: none;
|
|
padding: 10px 20px;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.send-button:hover {
|
|
background: #0056b3;
|
|
}
|
|
|
|
#file-preview img,
|
|
#file-preview video {
|
|
max-width: 100px;
|
|
max-height: 100px;
|
|
border-radius: 5px;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
|
|
|
|
.loading-spinner {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
padding: 10px;
|
|
font-size: 14px;
|
|
color: #007bff;
|
|
background-color: #f1f1f1;
|
|
border-radius: 5px;
|
|
margin: 10px auto;
|
|
width: fit-content;
|
|
}
|
|
|
|
.loading-spinner::before {
|
|
content: "";
|
|
border: 3px solid #f3f3f3;
|
|
border-top: 3px solid #007bff;
|
|
border-radius: 50%;
|
|
width: 20px;
|
|
height: 20px;
|
|
animation: spin 1s linear infinite;
|
|
margin-right: 10px;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
|
|
.loading-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 10px;
|
|
background-color: #f1f1f1;
|
|
border-radius: 5px;
|
|
margin: 10px auto;
|
|
width: fit-content;
|
|
}
|
|
|
|
.loading-spinner {
|
|
font-size: 14px;
|
|
color: #007bff;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
|
|
.progress-bar {
|
|
width: 200px;
|
|
height: 10px;
|
|
background-color: #e0e0e0;
|
|
border-radius: 5px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.progress {
|
|
height: 100%;
|
|
background-color: #007bff;
|
|
width: 0%;
|
|
transition: width 0.3s ease;
|
|
}
|
|
</style>
|
|
<div class="messages-container">
|
|
|
|
<div class="user-info">
|
|
<img src="{{ url_for('static', filename=user.profile_photo) }}" alt="صورة المستخدم" class="profile-photo">
|
|
<span class="username">{{ user.username }}</span>
|
|
</div>
|
|
|
|
|
|
<div class="messages-list" id="messages-list">
|
|
<div id="loading-container" style="display: none;">
|
|
<div class="loading-circle"></div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="message-input">
|
|
<form id="messageForm" enctype="multipart/form-data">
|
|
<div class="input-group">
|
|
<label for="file-input" class="file-upload-btn">
|
|
📎
|
|
<input type="file" id="file-input" name="file" accept="image/*, video/*" hidden>
|
|
</label>
|
|
<textarea id="message-input" placeholder="اكتب رسالتك هنا..."></textarea>
|
|
|
|
<button type="submit" id="send-message-btn">ارسال</button>
|
|
</div>
|
|
<div id="file-preview"></div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div id="previewModal" class="modal">
|
|
<div class="modal-content">
|
|
<button id="closeModal">×</button>
|
|
<div id="modalPreview"></div>
|
|
<button id="sendFromModal" class="send-button">إرسال</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
$(document).ready(async function () {
|
|
const messageList = $('#messages-list');
|
|
const receiverId = {{ user.id }};
|
|
let selectedFile = null;
|
|
|
|
|
|
await fetch('/join_chat', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
|
|
|
|
await fetch('/mark_all_as_seen', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ receiver_id: receiverId }),
|
|
});
|
|
|
|
|
|
$('#file-input').change(function(e) {
|
|
const file = e.target.files[0];
|
|
if (file) {
|
|
selectedFile = file;
|
|
showFilePreview(file);
|
|
}
|
|
});
|
|
|
|
function showFilePreview(file) {
|
|
const preview = $('#file-preview');
|
|
preview.empty();
|
|
|
|
if (file.type.startsWith('image/')) {
|
|
preview.append(`<img src="${URL.createObjectURL(file)}" class="preview-image">`);
|
|
} else if (file.type.startsWith('video/')) {
|
|
preview.append(`
|
|
<video controls class="preview-video">
|
|
<source src="${URL.createObjectURL(file)}" type="${file.type}">
|
|
</video>
|
|
`);
|
|
}
|
|
|
|
preview.append(`
|
|
<div class="file-info">
|
|
<span>${file.name}</span>
|
|
<button type="button" class="remove-file-btn">×</button>
|
|
</div>
|
|
`);
|
|
|
|
$('.remove-file-btn').click(() => {
|
|
selectedFile = null;
|
|
preview.empty();
|
|
$('#file-input').val('');
|
|
});
|
|
}
|
|
async function loadMessages() {
|
|
try {
|
|
const response = await fetch(`/get_messages/${receiverId}`);
|
|
const messages = await response.json();
|
|
|
|
if (!Array.isArray(messages)) {
|
|
console.error('الرد ليس مصفوفة:', messages);
|
|
return;
|
|
}
|
|
|
|
messageList.empty();
|
|
messages.forEach(message => {
|
|
const messageItem = $('<div>')
|
|
.addClass('message-item')
|
|
.attr('data-message-id', message.id);
|
|
|
|
|
|
if (message.sender_id === {{ current_user.id }}) {
|
|
messageItem.addClass('sent');
|
|
|
|
|
|
if (message.watched_by_receiver) {
|
|
messageItem.append('<span class="seen-status">✓✓</span>');
|
|
} else {
|
|
messageItem.append('<span class="seen-status">✓</span>');
|
|
}
|
|
} else {
|
|
messageItem.addClass('received');
|
|
|
|
}
|
|
|
|
const contentContainer = $('<div>').addClass('message-content');
|
|
|
|
|
|
if (message.file_url) {
|
|
if (message.file_type.startsWith('image/')) {
|
|
|
|
contentContainer.append(`<img src="/${message.file_url}" class="message-media">`);
|
|
} else if (message.file_type.startsWith('video/')) {
|
|
|
|
contentContainer.append(`
|
|
<video controls class="message-media">
|
|
<source src="/${message.file_url}" type="${message.file_type}">
|
|
</video>
|
|
`);
|
|
}
|
|
}
|
|
|
|
|
|
if (message.content) {
|
|
contentContainer.append(`<span>${message.content}</span>`);
|
|
}
|
|
|
|
|
|
const timestamp = $('<span>')
|
|
.addClass('message-timestamp')
|
|
.text(message.created_at);
|
|
|
|
|
|
messageItem.append(contentContainer).append(timestamp);
|
|
messageList.append(messageItem);
|
|
});
|
|
|
|
|
|
messageList.scrollTop(messageList.prop('scrollHeight'));
|
|
} catch (error) {
|
|
console.error('حدث خطأ أثناء تحميل الرسائل:', error);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
const updateEventSource = new EventSource(`/stream_message_updates/${receiverId}`);
|
|
updateEventSource.onmessage = function (event) {
|
|
const data = JSON.parse(event.data);
|
|
const messageId = data.message_id;
|
|
const isSender = data.sender_id === {{ current_user.id }};
|
|
|
|
|
|
if (isSender) {
|
|
const seenStatus = $(`.message-item[data-message-id="${messageId}"] .seen-status`);
|
|
if (data.watched_by_receiver) {
|
|
seenStatus.text('✓✓');
|
|
} else {
|
|
seenStatus.text('✓');
|
|
}
|
|
}
|
|
};
|
|
|
|
updateEventSource.onerror = function () {
|
|
console.log('SSE error. Reconnecting...');
|
|
setTimeout(() => {
|
|
new EventSource(`/stream_message_updates/${receiverId}`);
|
|
}, 3000);
|
|
};
|
|
|
|
|
|
const eventSource = new EventSource(`/stream_messages/${receiverId}`);
|
|
eventSource.onmessage = function (event) {
|
|
loadMessages();
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$('#file-input').change(function(e) {
|
|
const file = e.target.files[0];
|
|
if (file) {
|
|
selectedFile = file;
|
|
showFilePreview(file);
|
|
}
|
|
});
|
|
|
|
|
|
function showFilePreview(file) {
|
|
const previewModal = $('#previewModal');
|
|
const modalPreview = $('#modalPreview');
|
|
|
|
|
|
modalPreview.empty();
|
|
|
|
if (file.type.startsWith('image')) {
|
|
|
|
const img = $('<img>').attr('src', URL.createObjectURL(file)).css({ maxWidth: '100%', maxHeight: '80vh' });
|
|
modalPreview.append(img);
|
|
} else if (file.type.startsWith('video')) {
|
|
|
|
const video = $('<video>').attr('src', URL.createObjectURL(file)).attr('controls', true).css({ maxWidth: '100%', maxHeight: '80vh' });
|
|
modalPreview.append(video);
|
|
}
|
|
|
|
|
|
previewModal.show();
|
|
}
|
|
|
|
|
|
$('#closeModal').click(function() {
|
|
$('#previewModal').hide();
|
|
});
|
|
|
|
|
|
$('#sendFromModal').click(function() {
|
|
$('#previewModal').hide();
|
|
$('#messageForm').submit();
|
|
});
|
|
|
|
|
|
$('#messageForm').submit(async function(e) {
|
|
e.preventDefault();
|
|
const messageContent = $('#message-input').val().trim();
|
|
const formData = new FormData();
|
|
formData.append('content', messageContent);
|
|
formData.append('receiver_id', receiverId);
|
|
|
|
if (selectedFile) {
|
|
formData.append('file', selectedFile);
|
|
|
|
|
|
$('#messages-list').append(`
|
|
<div class="loading-container">
|
|
<div class="loading-spinner">جاري التحميل...</div>
|
|
<div class="progress-bar">
|
|
<div class="progress"></div>
|
|
</div>
|
|
</div>
|
|
`);
|
|
}
|
|
|
|
if (!messageContent && !selectedFile) return;
|
|
|
|
try {
|
|
const isReceiverActive = await fetch(`/is_user_active/${receiverId}`).then(res => res.json());
|
|
|
|
|
|
const xhr = new XMLHttpRequest();
|
|
xhr.open('POST', '/send_message', true);
|
|
|
|
|
|
xhr.upload.onprogress = function(event) {
|
|
if (event.lengthComputable) {
|
|
const percentComplete = (event.loaded / event.total) * 100;
|
|
$('.progress').css('width', percentComplete + '%');
|
|
}
|
|
};
|
|
|
|
xhr.onload = function() {
|
|
if (xhr.status === 200) {
|
|
$('#message-input').val('');
|
|
$('#file-preview').empty();
|
|
selectedFile = null;
|
|
$('#file-input').val('');
|
|
|
|
if (isReceiverActive.active) {
|
|
const messageId = JSON.parse(xhr.responseText).message_id;
|
|
fetch(`/mark_message_as_seen/${messageId}`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
}
|
|
|
|
|
|
$('.loading-container').remove();
|
|
loadMessages();
|
|
} else {
|
|
console.error('حدث خطأ أثناء إرسال الرسالة:', xhr.statusText);
|
|
$('.loading-container').remove();
|
|
}
|
|
};
|
|
|
|
xhr.onerror = function() {
|
|
console.error('حدث خطأ أثناء إرسال الرسالة:', xhr.statusText);
|
|
$('.loading-container').remove();
|
|
};
|
|
|
|
xhr.send(formData);
|
|
} catch (error) {
|
|
console.error('حدث خطأ أثناء إرسال الرسالة:', error);
|
|
|
|
$('.loading-container').remove();
|
|
}
|
|
});
|
|
|
|
|
|
await loadMessages();
|
|
});
|
|
|
|
window.addEventListener('beforeunload', async function () {
|
|
await fetch('/leave_chat', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
|
|
if (updateEventSource) updateEventSource.close();
|
|
if (eventSource) eventSource.close();
|
|
});
|
|
</script>
|
|
|
|
|
|
{% endblock %} |