Browse Source

unread messages handles for the admin panel.

master
Mohsen Taba 2 months ago
parent
commit
9055e06e04
  1. 27
      apps/chat/admin_views.py
  2. 18
      static/css/live_chat.css
  3. 18
      static/js/live_chat.js

27
apps/chat/admin_views.py

@ -5,7 +5,7 @@ from django.conf import settings
from django.http import JsonResponse
from django.shortcuts import render, get_object_or_404
from django.views.decorators.http import require_http_methods
from django.db.models import Q
from django.db.models import Q, Count
from django.utils.translation import gettext_lazy as _
from apps.chat.models import RoomMessage, ChatMessage
@ -39,7 +39,13 @@ def live_chat_dashboard_view(request):
def api_get_rooms(request):
"""گرفتن لیست روم‌ها بر اساس دسترسی و حریم خصوصی مطلق"""
user = request.user
queryset = RoomMessage.objects.all().order_by('-updated_at')
queryset = RoomMessage.objects.annotate(
admin_unread_count=Count(
'messages',
filter=Q(messages__is_read=False) & ~Q(messages__sender=user)
)
).order_by('-updated_at')
personal_q = Q(initiator=user) | Q(recipient=user)
@ -61,11 +67,13 @@ def api_get_rooms(request):
if r.room_type == 'group' and r.course:
title = r.course.title
unread_count = getattr(r, 'admin_unread_count', 0)
data.append({
"id": r.id,
"title": title,
"type": r.room_type,
"unread": r.unread_messages_count,
"unread": unread_count,
"time": r.updated_at.strftime("%H:%M") if r.updated_at else ""
})
@ -81,6 +89,19 @@ def api_get_messages(request, room_id):
id__gt=last_id, # جادوی پولینگ: فقط آیدی‌های بزرگتر از آخرین پیامی که داریم رو بگیر
is_deleted=False
).order_by('id')
# 🟢 مارک کردن پیام‌ها به عنوان خوانده شده (سین کردن) برای تمام انواع چت
if messages.exists():
# پیام‌های طرف مقابل را سین کن
unread_msgs = messages.filter(is_read=False).exclude(sender=request.user)
if unread_msgs.exists():
unread_msgs.update(is_read=True)
if room.room_type == RoomMessage.RoomTypeChoices.GROUP:
# همگام‌سازی با فیلد استاتیک برای جلوگیری از مشکلات احتمالی در FastAPI
if room.unread_messages_count > 0:
room.unread_messages_count = 0
room.save(update_fields=['unread_messages_count'])
data = []
for m in messages:

18
static/css/live_chat.css

@ -147,6 +147,24 @@ html {
/* آواتار روم فعال با گرادیان سبز اصلی و تیره */
}
.unread-badge {
background: #0A522E;
color: #FAF6E9;
margin-left: 8px;
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 20px;
height: 20px;
padding: 1px 6px 0 6px; /* افزودن یک پیکسل به بالا برای تراز چشمی فونت */
line-height: 1;
}
.dark .unread-badge {
background: #1a854d;
}
/* ── Telegram-style Header ── */
.telegram-header {
background: linear-gradient(135deg, #0A522E, #052B18);

18
static/js/live_chat.js

@ -155,7 +155,7 @@ document.addEventListener('DOMContentLoaded', function () {
</div>
<div class="flex justify-between items-center">
<span class="text-[12px] font-medium text-gray-500 truncate">${room.type === 'group' ? '👥 Group Chat' : '👤 Private Chat'}</span>
${room.unread > 0 ? `<span class="bg-blue-500 shadow-sm text-white text-[10px] font-bold px-2 py-0.5 rounded-full">${room.unread}</span>` : ''}
${room.unread > 0 ? `<span class="shadow-sm text-[10px] font-bold rounded-full unread-badge">${room.unread}</span>` : ''}
</div>
</div>
</div>
@ -168,6 +168,15 @@ document.addEventListener('DOMContentLoaded', function () {
lastMessageId = 0;
isFetchingMessages = false;
// 🟢 Optimistic UI: Update active state and remove unread badge immediately
document.querySelectorAll('.room-item').forEach(el => el.classList.remove('room-item-active'));
const clickedRoom = document.querySelector(`.room-item[onclick*="selectRoom(${id}"]`);
if (clickedRoom) {
clickedRoom.classList.add('room-item-active');
const badge = clickedRoom.querySelector('.unread-badge');
if (badge) badge.remove(); // Hide badge instantly
}
// 🟢 تغییر invisible به hidden برای کارکرد درست بلوک‌ها
UI.noChatSelected.classList.add('hidden');
UI.chatHeaderInfo.classList.remove('hidden'); // 🟢 تغییر یافته
@ -177,12 +186,11 @@ document.addEventListener('DOMContentLoaded', function () {
UI.chatTitle.textContent = title;
UI.messages.innerHTML = '<div class="flex items-center justify-center h-full"><span class="material-symbols-outlined animate-spin text-gray-400 text-3xl">autorenew</span></div>';
fetchRooms();
stopPolling();
fetchMessages().then(() => {
startPolling();
scrollToBottom();
fetchRooms(); // Sync with backend to ensure exact unread counts
});
UI.input.focus();
@ -249,12 +257,12 @@ document.addEventListener('DOMContentLoaded', function () {
}
// ── Textarea Auto-resize & Key Handling ──
UI.input.addEventListener('input', function() {
UI.input.addEventListener('input', function () {
this.style.height = 'auto';
this.style.height = (this.scrollHeight) + 'px';
});
UI.input.addEventListener('keydown', function(e) {
UI.input.addEventListener('keydown', function (e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
// Trigger form submit

Loading…
Cancel
Save