fix: restore push delivery across clients
Some checks failed
Android CI / android (push) Has started running
Android Release / release (push) Has been cancelled
CI / test (push) Has started running

fix: stop suppressing server push notifications based on coarse online presence

fix: rely on FCM delivery for message notifications and remove duplicate Android realtime alerts
This commit is contained in:
2026-04-05 15:25:01 +03:00
parent efd21e6c0f
commit fc98720c4f
2 changed files with 17 additions and 55 deletions

View File

@@ -8,15 +8,12 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import ru.daemonlord.messenger.data.chat.local.dao.ChatDao import ru.daemonlord.messenger.data.chat.local.dao.ChatDao
import ru.daemonlord.messenger.core.notifications.ActiveChatTracker import ru.daemonlord.messenger.core.notifications.ActiveChatTracker
import ru.daemonlord.messenger.core.notifications.ChatNotificationPayload
import ru.daemonlord.messenger.core.notifications.NotificationDispatcher import ru.daemonlord.messenger.core.notifications.NotificationDispatcher
import ru.daemonlord.messenger.core.token.TokenRepository import ru.daemonlord.messenger.core.token.TokenRepository
import ru.daemonlord.messenger.data.message.local.dao.MessageDao import ru.daemonlord.messenger.data.message.local.dao.MessageDao
import ru.daemonlord.messenger.data.message.local.entity.MessageEntity import ru.daemonlord.messenger.data.message.local.entity.MessageEntity
import ru.daemonlord.messenger.domain.chat.repository.ChatRepository import ru.daemonlord.messenger.domain.chat.repository.ChatRepository
import ru.daemonlord.messenger.domain.message.repository.MessageRepository import ru.daemonlord.messenger.domain.message.repository.MessageRepository
import ru.daemonlord.messenger.domain.notifications.repository.NotificationSettingsRepository
import ru.daemonlord.messenger.domain.notifications.usecase.ShouldShowMessageNotificationUseCase
import ru.daemonlord.messenger.domain.realtime.RealtimeManager import ru.daemonlord.messenger.domain.realtime.RealtimeManager
import ru.daemonlord.messenger.domain.realtime.model.RealtimeEvent import ru.daemonlord.messenger.domain.realtime.model.RealtimeEvent
import javax.inject.Inject import javax.inject.Inject
@@ -32,8 +29,6 @@ class HandleRealtimeEventsUseCase @Inject constructor(
private val notificationDispatcher: NotificationDispatcher, private val notificationDispatcher: NotificationDispatcher,
private val activeChatTracker: ActiveChatTracker, private val activeChatTracker: ActiveChatTracker,
private val tokenRepository: TokenRepository, private val tokenRepository: TokenRepository,
private val notificationSettingsRepository: NotificationSettingsRepository,
private val shouldShowMessageNotificationUseCase: ShouldShowMessageNotificationUseCase,
) { ) {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
@@ -113,37 +108,6 @@ class HandleRealtimeEventsUseCase @Inject constructor(
) )
} }
chatRepository.refreshChat(chatId = event.chatId) chatRepository.refreshChat(chatId = event.chatId)
val muted = chatDao.isChatMuted(event.chatId) == true
val shouldNotify = shouldShowMessageNotificationUseCase(
chatId = event.chatId,
isMention = isMention,
serverMuted = muted,
)
if (!isOwnMessage && activeChatId != event.chatId && shouldNotify) {
val title = chatDao.getChatDisplayTitle(event.chatId) ?: "New message"
val previewEnabled = notificationSettingsRepository.getSettings().previewEnabled
val body = (if (previewEnabled) {
event.text?.takeIf { it.isNotBlank() }
} else {
null
}) ?: when (event.type?.lowercase()) {
"image" -> "Photo"
"video" -> "Video"
"audio" -> "Audio"
"voice" -> "Voice message"
"file" -> "File"
else -> "Open chat"
}
notificationDispatcher.showChatMessage(
ChatNotificationPayload(
chatId = event.chatId,
messageId = event.messageId,
title = title,
body = body,
isMention = isMention,
)
)
}
} }
is RealtimeEvent.MessageUpdated -> { is RealtimeEvent.MessageUpdated -> {

View File

@@ -18,7 +18,6 @@ from app.notifications.schemas import (
PushTokenUpsertRequest, PushTokenUpsertRequest,
) )
from app.notifications.tasks import send_mention_notification_task, send_push_notification_task from app.notifications.tasks import send_mention_notification_task, send_push_notification_task
from app.realtime.presence import is_user_online
from app.users.repository import list_users_by_ids from app.users.repository import list_users_by_ids
_MENTION_RE = re.compile(r"@([A-Za-z0-9_]{3,50})") _MENTION_RE = re.compile(r"@([A-Za-z0-9_]{3,50})")
@@ -82,24 +81,23 @@ async def dispatch_message_notifications(db: AsyncSession, message: Message) ->
if await is_chat_muted_for_user(db, chat_id=message.chat_id, user_id=recipient.id): if await is_chat_muted_for_user(db, chat_id=message.chat_id, user_id=recipient.id):
continue continue
if not await is_user_online(recipient.id): payload = {
payload = { **base_payload,
**base_payload, "type": "message",
"type": "offline_message", "text_preview": (message.text or "")[:120],
"text_preview": (message.text or "")[:120], }
} await create_notification_log(
await create_notification_log( db,
db, user_id=recipient.id,
user_id=recipient.id, event_type="message",
event_type="offline_message", payload=json.dumps(payload, ensure_ascii=True),
payload=json.dumps(payload, ensure_ascii=True), )
) send_push_notification_task.delay(
send_push_notification_task.delay( recipient.id,
recipient.id, f"New message from {sender_name}",
f"New message from {sender_name}", (message.text or "")[:120],
(message.text or "")[:120], payload,
payload, )
)
await db.commit() await db.commit()