Merge branch 'develop'
This commit is contained in:
commit
26d69fb718
@ -106,6 +106,38 @@ def _build_message_search_snippet(message, query, max_length=140):
|
||||
return 'Сообщение без текста'
|
||||
|
||||
|
||||
def _filter_messages_before_anchor(queryset, chat, before_id):
|
||||
try:
|
||||
anchor_id = int(before_id)
|
||||
except (TypeError, ValueError):
|
||||
return queryset
|
||||
|
||||
anchor = chat.messages.only('id', 'created_at').filter(pk=anchor_id).first()
|
||||
if not anchor:
|
||||
return queryset.filter(id__lt=anchor_id)
|
||||
|
||||
return queryset.filter(
|
||||
Q(created_at__lt=anchor.created_at)
|
||||
| Q(created_at=anchor.created_at, id__lt=anchor.id)
|
||||
)
|
||||
|
||||
|
||||
def _filter_messages_after_anchor(queryset, chat, after_id):
|
||||
try:
|
||||
anchor_id = int(after_id)
|
||||
except (TypeError, ValueError):
|
||||
return queryset
|
||||
|
||||
anchor = chat.messages.only('id', 'created_at').filter(pk=anchor_id).first()
|
||||
if not anchor:
|
||||
return queryset.filter(id__gt=anchor_id)
|
||||
|
||||
return queryset.filter(
|
||||
Q(created_at__gt=anchor.created_at)
|
||||
| Q(created_at=anchor.created_at, id__gt=anchor.id)
|
||||
)
|
||||
|
||||
|
||||
class ChatViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
ViewSet для управления чатами
|
||||
@ -675,28 +707,22 @@ class ChatViewSet(viewsets.ModelViewSet):
|
||||
if after_id or after_ts:
|
||||
# Для загрузки новых сообщений - сортируем по возрастанию (от старых к
|
||||
# новым)
|
||||
queryset = queryset.order_by('created_at')
|
||||
queryset = queryset.order_by('created_at', 'id')
|
||||
else:
|
||||
# Для загрузки старых или начальной загрузки - по убыванию (от новых к
|
||||
# старым)
|
||||
queryset = queryset.order_by('-created_at')
|
||||
queryset = queryset.order_by('-created_at', '-id')
|
||||
|
||||
# Фильтрация по ID (приоритет) или timestamp
|
||||
if before_id:
|
||||
try:
|
||||
queryset = queryset.filter(id__lt=int(before_id))
|
||||
except ValueError:
|
||||
pass
|
||||
queryset = _filter_messages_before_anchor(queryset, chat, before_id)
|
||||
elif before_ts:
|
||||
before_dt = _coerce_ts(before_ts)
|
||||
if before_dt:
|
||||
queryset = queryset.filter(created_at__lt=before_dt)
|
||||
|
||||
if after_id:
|
||||
try:
|
||||
queryset = queryset.filter(id__gt=int(after_id))
|
||||
except ValueError:
|
||||
pass
|
||||
queryset = _filter_messages_after_anchor(queryset, chat, after_id)
|
||||
elif after_ts:
|
||||
after_dt = _coerce_ts(after_ts)
|
||||
if after_dt:
|
||||
|
||||
@ -6,6 +6,7 @@ import pytest
|
||||
from communications.models import Chat, ChatMembership, ChatReadState, ChatUserSettings, Message
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from employees.models import Department
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
@ -328,6 +329,62 @@ class TestChatViewSet:
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.data["messages"]) <= 10
|
||||
|
||||
def test_messages_pagination_before_id_respects_created_at_anchor(
|
||||
self, auth_client, private_chat, user1
|
||||
):
|
||||
"""before_id должен пагинировать по временной ленте, а не по голому id."""
|
||||
msg_old = Message.objects.create(
|
||||
chat=private_chat, author=user1, content="Old by time"
|
||||
)
|
||||
msg_middle = Message.objects.create(
|
||||
chat=private_chat, author=user1, content="Middle by time"
|
||||
)
|
||||
msg_new = Message.objects.create(
|
||||
chat=private_chat, author=user1, content="New by time"
|
||||
)
|
||||
|
||||
base = timezone.now()
|
||||
Message.objects.filter(pk=msg_old.pk).update(created_at=base)
|
||||
Message.objects.filter(pk=msg_middle.pk).update(created_at=base + timezone.timedelta(minutes=20))
|
||||
Message.objects.filter(pk=msg_new.pk).update(created_at=base + timezone.timedelta(minutes=10))
|
||||
|
||||
url = f"/api/v1/communications/chats/{private_chat.pk}/messages/"
|
||||
response = auth_client.get(url, {"before_id": msg_middle.id, "limit": 10})
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
returned_ids = [item["id"] for item in response.data["messages"]]
|
||||
|
||||
assert msg_old.id in returned_ids
|
||||
assert msg_new.id in returned_ids
|
||||
assert msg_middle.id not in returned_ids
|
||||
|
||||
def test_messages_pagination_after_id_respects_created_at_anchor(
|
||||
self, auth_client, private_chat, user1
|
||||
):
|
||||
"""after_id должен догружать по временной ленте даже при несогласованных id и created_at."""
|
||||
msg_old = Message.objects.create(
|
||||
chat=private_chat, author=user1, content="Old by time"
|
||||
)
|
||||
msg_middle = Message.objects.create(
|
||||
chat=private_chat, author=user1, content="Middle by time"
|
||||
)
|
||||
msg_new = Message.objects.create(
|
||||
chat=private_chat, author=user1, content="New by time"
|
||||
)
|
||||
|
||||
base = timezone.now()
|
||||
Message.objects.filter(pk=msg_old.pk).update(created_at=base)
|
||||
Message.objects.filter(pk=msg_middle.pk).update(created_at=base + timezone.timedelta(minutes=20))
|
||||
Message.objects.filter(pk=msg_new.pk).update(created_at=base + timezone.timedelta(minutes=10))
|
||||
|
||||
url = f"/api/v1/communications/chats/{private_chat.pk}/messages/"
|
||||
response = auth_client.get(url, {"after_id": msg_new.id, "limit": 10})
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
returned_ids = [item["id"] for item in response.data["messages"]]
|
||||
|
||||
assert returned_ids == [msg_middle.id]
|
||||
|
||||
def test_chat_access_denied(self, user3, private_chat):
|
||||
"""Попытка доступа к чату без прав"""
|
||||
client = APIClient()
|
||||
|
||||
@ -419,7 +419,7 @@ function MessageDialogPageContent() {
|
||||
const viewport = c.containerRef.current;
|
||||
|
||||
const onScroll = () => {
|
||||
if (!cm.loadingOlder && !cm.messagesLoading && cm.hasMoreOlder && viewport.scrollTop <= 120) {
|
||||
if (!cm.loadingOlder && !cm.messagesLoading && (cm.hasMoreOlder || cm.allowOneOlderProbe) && viewport.scrollTop <= 120) {
|
||||
cm.loadOlderMessages();
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user