Add total unread notification count to home screen (#7891)

* Add total unread notification count to home screen

This will be calculated on each sync heartbeat (for now, using the state updates for the room list service).

* Simplify `counter` parameter logic

* Read the total unread count when the home screen is first displayed

---------

Co-authored-by: Benoit Marty <benoitm@element.io>
This commit is contained in:
Jorge Martin Espinosa
2026-10-13 12:58:07 +02:00
committed by GitHub
parent 50aa723f63
commit 9cc5cf9a01
13 changed files with 121 additions and 14 deletions
@@ -18,7 +18,7 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatch
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
import io.element.android.libraries.matrix.api.roomlist.RoomListService
import io.element.android.libraries.matrix.api.sync.SyncService
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
import io.element.android.libraries.push.api.notifications.NotificationCleaner
import io.element.android.libraries.ui.strings.CommonStrings
@@ -34,7 +34,7 @@ class LoggedInEventProcessor(
@Assisted private val snackbarDispatcher: SnackbarDispatcher,
private val roomMembershipObserver: RoomMembershipObserver,
private val sessionId: SessionId,
private val roomListService: RoomListService,
private val syncService: SyncService,
private val notificationCleaner: NotificationCleaner,
private val dispatchers: CoroutineDispatchers
) {
@@ -73,9 +73,8 @@ class LoggedInEventProcessor(
}
.launchIn(childScope)
// Use the room list service state as a 'heartbeat' update to check existing notifications
roomListService.state
.filter { it == RoomListService.State.Running }
// Use the sync service heartbeat as a 'heartbeat' update to check existing notifications
syncService.heartBeat
.onEach {
notificationCleaner.clearReadRoomsNotifications(sessionId)
}
@@ -12,10 +12,9 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatch
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
import io.element.android.libraries.matrix.api.roomlist.RoomListService
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
import io.element.android.libraries.matrix.test.sync.FakeSyncService
import io.element.android.libraries.push.test.notifications.FakeNotificationCleaner
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value
@@ -155,24 +154,24 @@ class LoggedInEventProcessorTest {
}
@Test
fun `test notifications are cleared when room list service state is running`() = runTest {
fun `test notifications are cleared when sync heartbeat is emitted`() = runTest {
val clearReadNotificationsLambda = lambdaRecorder { _: SessionId -> }
val snackbarDispatcher = SnackbarDispatcher()
val notificationCleaner = FakeNotificationCleaner(clearReadRoomsNotificationsLambda = clearReadNotificationsLambda)
val roomListService = FakeRoomListService()
val syncService = FakeSyncService()
val loggedInEventProcessor = createLoggedInEventProcessor(
snackbarDispatcher = snackbarDispatcher,
notificationCleaner = notificationCleaner,
roomListService = roomListService,
syncService = syncService,
)
loggedInEventProcessor.observeEvents(backgroundScope)
testScheduler.runCurrent()
// Simulate the room list service state changing to Running
roomListService.postState(RoomListService.State.Running)
// Simulate a sync heartbeat
syncService.emitHeartBeat()
testScheduler.runCurrent()
@@ -184,13 +183,13 @@ class LoggedInEventProcessorTest {
snackbarDispatcher: SnackbarDispatcher,
roomMembershipObserver: RoomMembershipObserver = RoomMembershipObserver(),
sessionId: SessionId = A_SESSION_ID,
roomListService: FakeRoomListService = FakeRoomListService(),
syncService: FakeSyncService = FakeSyncService(),
notificationCleaner: FakeNotificationCleaner = FakeNotificationCleaner(),
) = LoggedInEventProcessor(
snackbarDispatcher = snackbarDispatcher,
roomMembershipObserver = roomMembershipObserver,
sessionId = sessionId,
roomListService = roomListService,
syncService = syncService,
notificationCleaner = notificationCleaner,
dispatchers = testCoroutineDispatchers(),
)