Фикс чата

This commit is contained in:
Халимов Рустам
2026-03-20 01:40:02 +03:00
parent 473e9bcef6
commit f3e0941647
3 changed files with 54 additions and 19 deletions

View File

@@ -105,21 +105,35 @@ public sealed class MessageRepository : IMessageRepository
public async Task AddReadReceiptsAsync(Guid userId, List<Guid> messageIds, CancellationToken cancellationToken)
{
var filter = Builders<Message>.Filter.In(m => m.Id, messageIds);
var messages = await _messages.Find(filter).ToListAsync(cancellationToken);
if (!messages.Any()) return;
var chatId = messages.First().ChatId;
var maxDate = messages.Max(m => m.CreatedAt);
var notReadFilter = Builders<Message>.Filter.Not(
Builders<Message>.Filter.ElemMatch<ReadReceipt>(
"ReadBy",
Builders<ReadReceipt>.Filter.Eq(r => r.UserId, userId)
)
);
var finalFilter = Builders<Message>.Filter.And(
Builders<Message>.Filter.Eq(m => m.ChatId, chatId),
Builders<Message>.Filter.Ne(m => m.SenderId, userId),
Builders<Message>.Filter.Lte(m => m.CreatedAt, maxDate),
notReadFilter
);
var unreadMessagesToMark = await _messages.Find(finalFilter).ToListAsync(cancellationToken);
var writes = new List<WriteModel<Message>>();
foreach (var msg in messages)
foreach (var msg in unreadMessagesToMark)
{
if (!msg.ReadBy.Any(r => r.UserId == userId))
{
var receipt = new ReadReceipt(msg.Id, userId);
var pushUpdate = Builders<Message>.Update.Push("ReadBy", receipt);
var updateModel = new UpdateOneModel<Message>(Builders<Message>.Filter.Eq(m => m.Id, msg.Id), pushUpdate);
writes.Add(updateModel);
}
var receipt = new ReadReceipt(msg.Id, userId);
var pushUpdate = Builders<Message>.Update.Push("ReadBy", receipt);
var updateModel = new UpdateOneModel<Message>(Builders<Message>.Filter.Eq(m => m.Id, msg.Id), pushUpdate);
writes.Add(updateModel);
}
if (writes.Any())
@@ -180,8 +194,10 @@ public sealed class MessageRepository : IMessageRepository
public async Task<int> GetUnreadCountAsync(Guid chatId, Guid userId, CancellationToken cancellationToken)
{
var notReadFilter = Builders<Message>.Filter.Not(
Builders<Message>.Filter.ElemMatch("ReadBy",
Builders<BsonDocument>.Filter.Eq("UserId", userId))
Builders<Message>.Filter.ElemMatch<ReadReceipt>(
"ReadBy",
Builders<ReadReceipt>.Filter.Eq(r => r.UserId, userId)
)
);
var finalFilter = Builders<Message>.Filter.And(

View File

@@ -200,9 +200,21 @@ export default function ChatView({ onStartCall, onStartGroupCall }: { onStartCal
container.scrollTop = targetEl.offsetTop - 60;
} else {
container.scrollTop = container.scrollHeight;
requestAnimationFrame(() => {
if (container) container.scrollTop = container.scrollHeight;
});
setTimeout(() => {
if (container) container.scrollTop = container.scrollHeight;
}, 100);
}
} else {
container.scrollTop = container.scrollHeight;
requestAnimationFrame(() => {
if (container) container.scrollTop = container.scrollHeight;
});
setTimeout(() => {
if (container) container.scrollTop = container.scrollHeight;
}, 100);
}
setScrollReady(true);
}

View File

@@ -407,13 +407,18 @@ function MessageBubble({
</button>
)}
{(() => {
const hasReactions = Object.keys(reactionGroups).length > 0;
const needsFrame = !!message.content || !!message.forwardedFrom || !!message.replyTo || hasReactions;
return (
<div
id={`msg-${message.id}`}
onContextMenu={handleContextMenu}
onDoubleClick={handleReply}
title={t('reply') ? `${t('reply')} (Double Click)` : 'Double click to reply'}
className={`cursor-pointer max-w-full min-w-0 rounded-[1.25rem] overflow-hidden transition-all duration-300 ${
hasImage && !message.content && !message.forwardedFrom && !message.replyTo
hasImage && !needsFrame
? 'p-0 shadow-none border-none'
: isMine
? 'bubble-sent text-white shadow-sm px-3.5 py-2 hover:shadow-md hover:brightness-105 rounded-br-sm'
@@ -538,11 +543,11 @@ function MessageBubble({
return (
<div className={`
${(message.content || message.forwardedFrom) ? '-mx-4' : ''}
${(message.content || message.forwardedFrom) ? (message.forwardedFrom ? 'mt-2' : '-mt-2.5') : ''}
${(message.content || message.forwardedFrom) ? (message.content ? 'mb-2' : '-mb-2.5') : ''}
${isSingleGif && !(message.content || message.forwardedFrom) ? 'max-w-[260px] rounded-[1.25rem]' : ''}
${isSingleGif && (message.content || message.forwardedFrom) ? 'max-h-[260px] mx-auto' : ''}
${needsFrame ? '-mx-4' : ''}
${needsFrame ? (message.forwardedFrom ? 'mt-2' : '-mt-2.5') : ''}
${needsFrame ? (message.content ? 'mb-2' : '-mb-2.5') : ''}
${isSingleGif && !needsFrame ? 'max-w-[260px] rounded-[1.25rem]' : ''}
${isSingleGif && needsFrame ? 'max-h-[260px] mx-auto' : ''}
bg-black/20 overflow-hidden relative
`}>
<div className={`grid gap-[2px] ${galleryMedia.length >= 3
@@ -827,6 +832,8 @@ function MessageBubble({
</div>
)}
</div>
);
})()}
</div>
{isMine && (