Notification: ensure that a notification is displayed when a Push cannot be resolved.

Previously the error was logged and added to push history but no notification was shown, so the user fully miss the new message.
This commit is contained in:
Benoit Marty 2025-06-17 16:00:31 +02:00
parent cbec697f07
commit 5852558681
4 changed files with 49 additions and 9 deletions

View file

@ -68,6 +68,7 @@ sealed interface NotificationContent {
) : MessageLike ) : MessageLike
data object RoomEncrypted : MessageLike data object RoomEncrypted : MessageLike
data object UnableToResolve : MessageLike
data class RoomMessage( data class RoomMessage(
val senderId: UserId, val senderId: UserId,
val messageType: MessageType val messageType: MessageType

View file

@ -12,6 +12,7 @@ import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.notification.NotificationContent
import io.element.android.libraries.matrix.api.notification.NotificationData import io.element.android.libraries.matrix.api.notification.NotificationData
import io.element.android.libraries.matrix.api.notification.NotificationService import io.element.android.libraries.matrix.api.notification.NotificationService
import io.element.android.services.toolbox.api.systemclock.SystemClock import io.element.android.services.toolbox.api.systemclock.SystemClock
@ -24,7 +25,7 @@ class RustNotificationService(
private val sessionId: SessionId, private val sessionId: SessionId,
private val notificationClient: NotificationClient, private val notificationClient: NotificationClient,
private val dispatchers: CoroutineDispatchers, private val dispatchers: CoroutineDispatchers,
clock: SystemClock, private val clock: SystemClock,
) : NotificationService { ) : NotificationService {
private val notificationMapper: NotificationMapper = NotificationMapper(clock) private val notificationMapper: NotificationMapper = NotificationMapper(clock)
@ -43,11 +44,32 @@ class RustNotificationService(
val eventIds = requests.flatMap { it.eventIds } val eventIds = requests.flatMap { it.eventIds }
for (eventId in eventIds) { for (eventId in eventIds) {
val item = items[eventId] val item = items[eventId]
val roomId = RoomId(requests.find { it.eventIds.contains(eventId) }?.roomId!!)
if (item != null) { if (item != null) {
val roomId = RoomId(requests.find { it.eventIds.contains(eventId) }?.roomId!!)
put(EventId(eventId), notificationMapper.map(sessionId, EventId(eventId), roomId, item)) put(EventId(eventId), notificationMapper.map(sessionId, EventId(eventId), roomId, item))
} else { } else {
Timber.e("Could not retrieve event for notification with $eventId") Timber.e("Could not retrieve event for notification with $eventId")
put(
EventId(eventId),
NotificationData(
sessionId = sessionId,
eventId = EventId(eventId),
threadId = null,
roomId = roomId,
senderAvatarUrl = null,
senderDisplayName = null,
senderIsNameAmbiguous = false,
roomAvatarUrl = null,
roomDisplayName = null,
isDirect = false,
isDm = false,
isEncrypted = false,
isNoisy = false,
timestamp = clock.epochMillis(),
content = NotificationContent.MessageLike.UnableToResolve,
hasMention = false
)
)
} }
} }
} }

View file

@ -225,6 +225,11 @@ class DefaultNotifiableEventResolver @Inject constructor(
val fallbackNotifiableEvent = fallbackNotifiableEvent(userId, roomId, eventId) val fallbackNotifiableEvent = fallbackNotifiableEvent(userId, roomId, eventId)
ResolvedPushEvent.Event(fallbackNotifiableEvent) ResolvedPushEvent.Event(fallbackNotifiableEvent)
} }
NotificationContent.MessageLike.UnableToResolve -> {
Timber.tag(loggerTag.value).w("Unable to resolve notification -> fallback")
val fallbackNotifiableEvent = fallbackNotifiableEvent(userId, roomId, eventId)
ResolvedPushEvent.Event(fallbackNotifiableEvent)
}
is NotificationContent.MessageLike.RoomRedaction -> { is NotificationContent.MessageLike.RoomRedaction -> {
// Note: this case will be handled below // Note: this case will be handled below
val redactedEventId = content.redactedEventId val redactedEventId = content.redactedEventId

View file

@ -25,6 +25,7 @@ import io.element.android.libraries.push.impl.history.onUnableToRetrieveSession
import io.element.android.libraries.push.impl.notifications.NotificationEventRequest import io.element.android.libraries.push.impl.notifications.NotificationEventRequest
import io.element.android.libraries.push.impl.notifications.NotificationResolverQueue import io.element.android.libraries.push.impl.notifications.NotificationResolverQueue
import io.element.android.libraries.push.impl.notifications.channels.NotificationChannels import io.element.android.libraries.push.impl.notifications.channels.NotificationChannels
import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
import io.element.android.libraries.push.impl.notifications.model.NotifiableRingingCallEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableRingingCallEvent
import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent
@ -87,13 +88,24 @@ class DefaultPushHandler @Inject constructor(
} else { } else {
result.fold( result.fold(
onSuccess = { onSuccess = {
pushHistoryService.onSuccess( if (it is ResolvedPushEvent.Event && it.notifiableEvent is FallbackNotifiableEvent) {
providerInfo = request.providerInfo, pushHistoryService.onUnableToResolveEvent(
eventId = request.eventId, providerInfo = request.providerInfo,
roomId = request.roomId, eventId = request.eventId,
sessionId = request.sessionId, roomId = request.roomId,
comment = "Push handled successfully", sessionId = request.sessionId,
) reason = "Unable to resolve event, showing fallback notification",
)
mutableBatteryOptimizationStore.showBatteryOptimizationBanner()
} else {
pushHistoryService.onSuccess(
providerInfo = request.providerInfo,
eventId = request.eventId,
roomId = request.roomId,
sessionId = request.sessionId,
comment = "Push handled successfully",
)
}
}, },
onFailure = { exception -> onFailure = { exception ->
pushHistoryService.onUnableToResolveEvent( pushHistoryService.onUnableToResolveEvent(