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. 14
      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.http import JsonResponse
from django.shortcuts import render, get_object_or_404 from django.shortcuts import render, get_object_or_404
from django.views.decorators.http import require_http_methods 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 django.utils.translation import gettext_lazy as _
from apps.chat.models import RoomMessage, ChatMessage from apps.chat.models import RoomMessage, ChatMessage
@ -39,7 +39,13 @@ def live_chat_dashboard_view(request):
def api_get_rooms(request): def api_get_rooms(request):
"""گرفتن لیست روم‌ها بر اساس دسترسی و حریم خصوصی مطلق""" """گرفتن لیست روم‌ها بر اساس دسترسی و حریم خصوصی مطلق"""
user = request.user 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) 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: if r.room_type == 'group' and r.course:
title = r.course.title title = r.course.title
unread_count = getattr(r, 'admin_unread_count', 0)
data.append({ data.append({
"id": r.id, "id": r.id,
"title": title, "title": title,
"type": r.room_type, "type": r.room_type,
"unread": r.unread_messages_count,
"unread": unread_count,
"time": r.updated_at.strftime("%H:%M") if r.updated_at else "" "time": r.updated_at.strftime("%H:%M") if r.updated_at else ""
}) })
@ -82,6 +90,19 @@ def api_get_messages(request, room_id):
is_deleted=False is_deleted=False
).order_by('id') ).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 = [] data = []
for m in messages: for m in messages:
avatar = m.sender.avatar.url if getattr(m.sender, 'avatar', None) else None avatar = m.sender.avatar.url if getattr(m.sender, 'avatar', None) else None

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-style Header ── */
.telegram-header { .telegram-header {
background: linear-gradient(135deg, #0A522E, #052B18); background: linear-gradient(135deg, #0A522E, #052B18);

14
static/js/live_chat.js

@ -155,7 +155,7 @@ document.addEventListener('DOMContentLoaded', function () {
</div> </div>
<div class="flex justify-between items-center"> <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> <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> </div>
</div> </div>
@ -168,6 +168,15 @@ document.addEventListener('DOMContentLoaded', function () {
lastMessageId = 0; lastMessageId = 0;
isFetchingMessages = false; 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 برای کارکرد درست بلوک‌ها // 🟢 تغییر invisible به hidden برای کارکرد درست بلوک‌ها
UI.noChatSelected.classList.add('hidden'); UI.noChatSelected.classList.add('hidden');
UI.chatHeaderInfo.classList.remove('hidden'); // 🟢 تغییر یافته UI.chatHeaderInfo.classList.remove('hidden'); // 🟢 تغییر یافته
@ -177,12 +186,11 @@ document.addEventListener('DOMContentLoaded', function () {
UI.chatTitle.textContent = title; 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>'; 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(); stopPolling();
fetchMessages().then(() => { fetchMessages().then(() => {
startPolling(); startPolling();
scrollToBottom(); scrollToBottom();
fetchRooms(); // Sync with backend to ensure exact unread counts
}); });
UI.input.focus(); UI.input.focus();

Loading…
Cancel
Save