Improve deleted reply preview context

This commit is contained in:
lizerk 2026-04-03 21:01:10 +03:00
parent 71d0a6092c
commit 219a93215f
4 changed files with 28 additions and 13 deletions

View File

@ -91,9 +91,7 @@ def _serialize_reply_preview(reply_msg) -> dict:
return {
"id": reply_msg.id,
"content": ""
if is_deleted
else (reply_msg.content[:100] if reply_msg.content else ""),
"content": reply_msg.content[:100] if reply_msg.content else "",
"author_name": (
reply_msg.author.get_full_name()
if reply_msg.author

View File

@ -2451,10 +2451,10 @@ class TestDeleteVariousMessageTypes:
assert reply2.is_deleted is False
assert reply1.reply_to_id == original.id
def test_messages_around_keeps_deleted_reply_preview_non_clickable_contract(
def test_messages_around_keeps_deleted_reply_preview_context_contract(
self, auth_client, private_chat, user1
):
"""Ответ на удаленное сообщение сохраняет preview, но помечается как deleted."""
"""Ответ на удаленное сообщение сохраняет контекст preview и помечается как deleted."""
original = Message.objects.create(
chat=private_chat, author=user1, content="Original to delete"
)
@ -2477,7 +2477,7 @@ class TestDeleteVariousMessageTypes:
assert serialized_reply["reply_to"]["id"] == original.id
assert serialized_reply["reply_to"]["is_deleted"] is True
assert serialized_reply["reply_to"]["content"] == ""
assert serialized_reply["reply_to"]["content"] == "Original to delete"
assert "author_name" in serialized_reply["reply_to"]
def test_bulk_delete_various_types(self, auth_client, private_chat, user1):

View File

@ -17,6 +17,7 @@ import { resolveMediaUrl } from "@/lib/url";
import {
formatFileSize,
getReplyPreview,
getReplyPreviewStatusLabel,
getReplyPreviewText,
getReplyToId,
getMessageDate,
@ -133,6 +134,7 @@ export default function ChatMessageItem({
const replyPreviewMessage = getReplyPreview(message, repliedMessage);
const replyToId = getReplyToId(message);
const canJumpToReply = Boolean(replyToId && !isReplyPreviewDeleted(replyPreviewMessage));
const replyStatusLabel = getReplyPreviewStatusLabel(replyPreviewMessage);
const isMine = Boolean(
currentUserId &&
(message.author_id === currentUserId || message.author?.id === currentUserId || message.sender?.id === currentUserId)
@ -211,18 +213,25 @@ export default function ChatMessageItem({
) : (
<div
className={[
"mb-2 block w-full rounded-xl border-l-2 px-2.5 py-1.5 text-left text-xs opacity-85",
"mb-2 block w-full rounded-xl border-l-2 px-2.5 py-1.5 text-left text-xs opacity-95",
isMine
? "border-sky-200/80 bg-sky-400/20 text-sky-50/95"
: "border-gray-300 bg-gray-100 text-gray-500",
: "border-gray-300 bg-gray-100 text-gray-600",
].join(" ")}
title="Исходное сообщение удалено"
aria-label={`Исходное сообщение удалено${replyPreviewMessage?.author_name ? `, автор ${replyPreviewMessage.author_name}` : ""}`}
>
<p className="font-medium">
{replyPreviewMessage?.author_name || "Ответ на сообщение"}
</p>
<p className="truncate">
<div className="flex items-center justify-between gap-2">
<p className="min-w-0 truncate font-medium">
{replyPreviewMessage?.author_name || "Ответ на сообщение"}
</p>
{replyStatusLabel ? (
<span className={`shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide ${isMine ? "bg-white/18 text-white/90" : "bg-gray-200 text-gray-600"}`}>
{replyStatusLabel}
</span>
) : null}
</div>
<p className="truncate opacity-90">
{replyPreviewMessage ? getReplyPreviewText(replyPreviewMessage) : "Сообщение удалено"}
</p>
</div>

View File

@ -118,15 +118,23 @@ export function isReplyPreviewDeleted(preview: ReplyPreviewSource | null | undef
}
export function getReplyPreviewText(preview: ReplyPreviewSource): string {
if (preview.is_deleted) return "Сообщение удалено";
if (preview.content?.trim()) return preview.content.trim();
if ("attachments" in preview && Array.isArray(preview.attachments) && preview.attachments.length > 0) {
return "[Вложение]";
}
if (preview.has_attachments) return "[Вложение]";
if (preview.is_deleted) return "Сообщение удалено";
return "[Сообщение]";
}
export function getReplyPreviewStatusLabel(preview: ReplyPreviewSource | null | undefined): string | null {
if (!preview?.is_deleted) {
return null;
}
return "Удалено";
}
export function getReplyToId(message: Message): number | null {
if (typeof message.reply_to_id === "number") return message.reply_to_id;
if (typeof message.reply_to === "number") return message.reply_to;