Dismiss notifications for rooms fully read in other devices (#7846)

* Dismiss notifications for rooms fully read in other devices
This commit is contained in:
Jorge Martin Espinosa
2026-10-05 17:56:16 +02:00
committed by GitHub
parent 774b4087f5
commit b76548f9a2
7 changed files with 404 additions and 15 deletions
@@ -8,28 +8,51 @@
package io.element.android.appnav package io.element.android.appnav
import dev.zacsweers.metro.Inject import androidx.annotation.VisibleForTesting
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedFactory
import dev.zacsweers.metro.AssistedInject
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.coroutine.childScope
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage 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.room.RoomMembershipObserver
import io.element.android.libraries.matrix.api.roomlist.RoomListService
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange 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 import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@Inject @AssistedInject
class LoggedInEventProcessor( class LoggedInEventProcessor(
private val snackbarDispatcher: SnackbarDispatcher, @Assisted private val snackbarDispatcher: SnackbarDispatcher,
private val roomMembershipObserver: RoomMembershipObserver, private val roomMembershipObserver: RoomMembershipObserver,
private val sessionId: SessionId,
private val roomListService: RoomListService,
private val notificationCleaner: NotificationCleaner,
private val dispatchers: CoroutineDispatchers
) { ) {
private var observingJob: Job? = null @AssistedFactory
interface Factory {
fun create(snackbarDispatcher: SnackbarDispatcher): LoggedInEventProcessor
}
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal var currentChildScope: CoroutineScope? = null
fun observeEvents(coroutineScope: CoroutineScope) { fun observeEvents(coroutineScope: CoroutineScope) {
observingJob = roomMembershipObserver.updates if (currentChildScope != null) return
val childScope = coroutineScope.childScope(dispatchers.computation, "LoggedInEventProcessor")
.also { currentChildScope = it }
roomMembershipObserver.updates
.filter { !it.isUserInRoom } .filter { !it.isUserInRoom }
.distinctUntilChanged() .distinctUntilChanged()
.onEach { roomMemberShipUpdate -> .onEach { roomMemberShipUpdate ->
@@ -48,12 +71,20 @@ class LoggedInEventProcessor(
else -> Unit else -> Unit
} }
} }
.launchIn(coroutineScope) .launchIn(childScope)
// Use the room list service state as a 'heartbeat' update to check existing notifications
roomListService.state
.filter { it == RoomListService.State.Running }
.onEach {
notificationCleaner.clearReadRoomsNotifications(sessionId)
}
.launchIn(childScope)
} }
fun stopObserving() { fun stopObserving() {
observingJob?.cancel() currentChildScope?.cancel()
observingJob = null currentChildScope = null
} }
private fun displayMessage(message: Int) { private fun displayMessage(message: Int) {
@@ -157,6 +157,7 @@ class LoggedInFlowNode(
private val createRoomEntryPoint: CreateRoomEntryPoint, private val createRoomEntryPoint: CreateRoomEntryPoint,
private val activeLiveLocationShareManager: ActiveLiveLocationShareManager, private val activeLiveLocationShareManager: ActiveLiveLocationShareManager,
private val customMapTilerConfigProvider: CustomMapTilerConfigProvider, private val customMapTilerConfigProvider: CustomMapTilerConfigProvider,
private val loggedInEventProcessorFactory: LoggedInEventProcessor.Factory,
) : BaseFlowNode<LoggedInFlowNode.NavTarget>( ) : BaseFlowNode<LoggedInFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.Placeholder, initialElement = NavTarget.Placeholder,
@@ -175,10 +176,7 @@ class LoggedInFlowNode(
} }
private val callback: Callback = callback() private val callback: Callback = callback()
private val loggedInFlowProcessor = LoggedInEventProcessor( private val loggedInFlowProcessor = loggedInEventProcessorFactory.create(snackbarDispatcher)
snackbarDispatcher = snackbarDispatcher,
roomMembershipObserver = matrixClient.roomMembershipObserver,
)
/** /**
* Display the incoming verification request, the app is expected to be in foreground. * Display the incoming verification request, the app is expected to be in foreground.
@@ -0,0 +1,197 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.appnav
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
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.push.test.notifications.FakeNotificationCleaner
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.isActive
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
class LoggedInEventProcessorTest {
@Test
fun `test observeEvents with left membership change`() = runTest {
val snackbarDispatcher = SnackbarDispatcher()
val roomMembershipObserver = RoomMembershipObserver()
val loggedInEventProcessor = createLoggedInEventProcessor(
snackbarDispatcher = snackbarDispatcher,
roomMembershipObserver = roomMembershipObserver,
)
loggedInEventProcessor.observeEvents(backgroundScope)
testScheduler.runCurrent()
roomMembershipObserver.notifyUserLeftRoom(
roomId = A_ROOM_ID,
isSpace = false,
membershipBeforeLeft = CurrentUserMembership.JOINED,
)
testScheduler.runCurrent()
// Verify that the snackbar message was displayed
assertThat(snackbarDispatcher.snackbarMessage.first()).isNotNull()
}
@Test
fun `test observeEvents with left membership change for space`() = runTest {
val snackbarDispatcher = SnackbarDispatcher()
val roomMembershipObserver = RoomMembershipObserver()
val loggedInEventProcessor = createLoggedInEventProcessor(
snackbarDispatcher = snackbarDispatcher,
roomMembershipObserver = roomMembershipObserver,
)
loggedInEventProcessor.observeEvents(backgroundScope)
testScheduler.runCurrent()
roomMembershipObserver.notifyUserLeftRoom(
roomId = A_ROOM_ID,
isSpace = true,
membershipBeforeLeft = CurrentUserMembership.JOINED,
)
testScheduler.runCurrent()
// Verify that the snackbar message was displayed
assertThat(snackbarDispatcher.snackbarMessage.first()).isNotNull()
}
@Test
fun `test observeEvents with invitation rejected membership change`() = runTest {
val snackbarDispatcher = SnackbarDispatcher()
val roomMembershipObserver = RoomMembershipObserver()
val loggedInEventProcessor = createLoggedInEventProcessor(
snackbarDispatcher = snackbarDispatcher,
roomMembershipObserver = roomMembershipObserver,
)
loggedInEventProcessor.observeEvents(backgroundScope)
testScheduler.runCurrent()
roomMembershipObserver.notifyUserLeftRoom(
roomId = A_ROOM_ID,
isSpace = false,
membershipBeforeLeft = CurrentUserMembership.INVITED,
)
testScheduler.runCurrent()
// Verify that the snackbar message was displayed
assertThat(snackbarDispatcher.snackbarMessage.first()).isNotNull()
}
@Test
fun `test observeEvents with knock retracted membership change`() = runTest {
val snackbarDispatcher = SnackbarDispatcher()
val roomMembershipObserver = RoomMembershipObserver()
val loggedInEventProcessor = createLoggedInEventProcessor(
snackbarDispatcher = snackbarDispatcher,
roomMembershipObserver = roomMembershipObserver,
)
loggedInEventProcessor.observeEvents(backgroundScope)
testScheduler.runCurrent()
roomMembershipObserver.notifyUserLeftRoom(
roomId = A_ROOM_ID,
isSpace = false,
membershipBeforeLeft = CurrentUserMembership.KNOCKED,
)
testScheduler.runCurrent()
// Verify that the snackbar message was displayed
assertThat(snackbarDispatcher.snackbarMessage.first()).isNotNull()
}
@Test
fun `test stopObserving cancels the child scope`() = runTest {
val snackbarDispatcher = SnackbarDispatcher()
val roomMembershipObserver = RoomMembershipObserver()
val loggedInEventProcessor = createLoggedInEventProcessor(
snackbarDispatcher = snackbarDispatcher,
roomMembershipObserver = roomMembershipObserver,
)
loggedInEventProcessor.observeEvents(backgroundScope)
assertThat(loggedInEventProcessor.currentChildScope?.isActive).isTrue()
testScheduler.runCurrent()
// Stop observing events
loggedInEventProcessor.stopObserving()
// Verify that the child scope is cancelled (the scope is null)
assertThat(loggedInEventProcessor.currentChildScope).isNull()
}
@Test
fun `test notifications are cleared when room list service state is running`() = runTest {
val clearReadNotificationsLambda = lambdaRecorder { _: SessionId -> }
val snackbarDispatcher = SnackbarDispatcher()
val notificationCleaner = FakeNotificationCleaner(clearReadRoomsNotificationsLambda = clearReadNotificationsLambda)
val roomListService = FakeRoomListService()
val loggedInEventProcessor = createLoggedInEventProcessor(
snackbarDispatcher = snackbarDispatcher,
notificationCleaner = notificationCleaner,
roomListService = roomListService,
)
loggedInEventProcessor.observeEvents(backgroundScope)
testScheduler.runCurrent()
// Simulate the room list service state changing to Running
roomListService.postState(RoomListService.State.Running)
testScheduler.runCurrent()
// Verify that the notifications were cleared
clearReadNotificationsLambda.assertions().isCalledOnce().with(value(A_SESSION_ID))
}
private fun TestScope.createLoggedInEventProcessor(
snackbarDispatcher: SnackbarDispatcher,
roomMembershipObserver: RoomMembershipObserver = RoomMembershipObserver(),
sessionId: SessionId = A_SESSION_ID,
roomListService: FakeRoomListService = FakeRoomListService(),
notificationCleaner: FakeNotificationCleaner = FakeNotificationCleaner(),
) = LoggedInEventProcessor(
snackbarDispatcher = snackbarDispatcher,
roomMembershipObserver = roomMembershipObserver,
sessionId = sessionId,
roomListService = roomListService,
notificationCleaner = notificationCleaner,
dispatchers = testCoroutineDispatchers(),
)
}
@@ -65,4 +65,11 @@ interface NotificationCleaner {
* @param roomId the room whose notifications are dismissed. * @param roomId the room whose notifications are dismissed.
*/ */
fun clearMembershipNotificationForRoom(sessionId: SessionId, roomId: RoomId) fun clearMembershipNotificationForRoom(sessionId: SessionId, roomId: RoomId)
/**
* Dismisses the notifications of every read room.
*
* @param sessionId the session whose notifications should be dismissed.
*/
fun clearReadRoomsNotifications(sessionId: SessionId)
} }
@@ -12,6 +12,7 @@ import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.ContributesBinding
import dev.zacsweers.metro.SingleIn import dev.zacsweers.metro.SingleIn
import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.features.lockscreen.api.LockScreenService
import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.di.annotations.AppCoroutineScope import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
@@ -53,7 +54,7 @@ class DefaultNotificationDrawerManager(
private val notificationRenderer: NotificationRenderer, private val notificationRenderer: NotificationRenderer,
private val appNavigationStateService: AppNavigationStateService, private val appNavigationStateService: AppNavigationStateService,
@AppCoroutineScope @AppCoroutineScope
coroutineScope: CoroutineScope, private val coroutineScope: CoroutineScope,
private val matrixClientProvider: MatrixClientProvider, private val matrixClientProvider: MatrixClientProvider,
private val imageLoaderHolder: ImageLoaderHolder, private val imageLoaderHolder: ImageLoaderHolder,
private val activeNotificationsProvider: ActiveNotificationsProvider, private val activeNotificationsProvider: ActiveNotificationsProvider,
@@ -187,6 +188,31 @@ class DefaultNotificationDrawerManager(
clearSummaryNotificationIfNeeded(sessionId) clearSummaryNotificationIfNeeded(sessionId)
} }
override fun clearReadRoomsNotifications(sessionId: SessionId) {
val notifications = activeNotificationsProvider.getNotificationsForSession(sessionId)
val notificationsGroupedByRoomId = notifications.mapNotNull {
tryOrNull { RoomId(it.tag) }?.let { roomId -> roomId to it }
}
if (notificationsGroupedByRoomId.isEmpty()) return
coroutineScope.launch {
val client = matrixClientProvider.getOrRestore(sessionId).getOrElse { return@launch }
notificationsGroupedByRoomId.forEach { (roomId, notification) ->
val roomInfo = client.getRoomInfo(roomId).getOrNull() ?: return@forEach
if (roomInfo.numUnreadNotifications > 0L || roomInfo.numUnreadMessages > 0 || roomInfo.numUnreadMentions > 0) {
return@forEach
}
notificationDisplayer.cancelNotification(notification.tag, notification.id)
}
clearSummaryNotificationIfNeeded(sessionId)
}
}
private fun clearSummaryNotificationIfNeeded(sessionId: SessionId) { private fun clearSummaryNotificationIfNeeded(sessionId: SessionId) {
val summaryNotification = activeNotificationsProvider.getSummaryNotification(sessionId) val summaryNotification = activeNotificationsProvider.getSummaryNotification(sessionId)
if (summaryNotification != null && activeNotificationsProvider.count(sessionId) == 1) { if (summaryNotification != null && activeNotificationsProvider.count(sessionId) == 1) {
@@ -9,6 +9,7 @@
package io.element.android.libraries.push.impl.notifications package io.element.android.libraries.push.impl.notifications
import android.app.Notification import android.app.Notification
import android.service.notification.StatusBarNotification
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.EnterpriseService
@@ -16,6 +17,8 @@ import io.element.android.features.enterprise.test.FakeEnterpriseService
import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.features.lockscreen.api.LockScreenService
import io.element.android.features.lockscreen.test.FakeLockScreenService import io.element.android.features.lockscreen.test.FakeLockScreenService
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.room.RoomInfo
import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.AN_EVENT_ID_2 import io.element.android.libraries.matrix.test.AN_EVENT_ID_2
import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID
@@ -27,6 +30,7 @@ import io.element.android.libraries.matrix.test.A_THREAD_ID_2
import io.element.android.libraries.matrix.test.A_TIMESTAMP import io.element.android.libraries.matrix.test.A_TIMESTAMP
import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
import io.element.android.libraries.matrix.test.room.aRoomInfo
import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUser
import io.element.android.libraries.matrix.ui.media.test.FakeImageLoaderHolder import io.element.android.libraries.matrix.ui.media.test.FakeImageLoaderHolder
import io.element.android.libraries.push.api.notifications.NotificationIdProvider import io.element.android.libraries.push.api.notifications.NotificationIdProvider
@@ -57,6 +61,7 @@ import io.element.android.services.appnavstate.test.aNavigationState
import io.element.android.services.appnavstate.test.anAppNavigationState import io.element.android.services.appnavstate.test.anAppNavigationState
import io.element.android.services.toolbox.test.systemclock.A_FAKE_TIMESTAMP import io.element.android.services.toolbox.test.systemclock.A_FAKE_TIMESTAMP
import io.element.android.tests.testutils.lambda.LambdaThreeParamsRecorder import io.element.android.tests.testutils.lambda.LambdaThreeParamsRecorder
import io.element.android.tests.testutils.lambda.LambdaTwoParamsRecorder
import io.element.android.tests.testutils.lambda.any import io.element.android.tests.testutils.lambda.any
import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.lambda.value
@@ -569,6 +574,126 @@ class DefaultNotificationDrawerManagerTest {
createFallbackNotificationResult.assertions().isNeverCalled() createFallbackNotificationResult.assertions().isNeverCalled()
} }
@Test
fun `clearReadRoomsNotifications cancels notifications of rooms without unread counts`() = runTest {
val cancelNotificationResult = lambdaRecorder<String?, Int, Unit> { _, _ -> }
val sut = createClearReadRoomsManager(
cancelNotificationResult = cancelNotificationResult,
notifications = listOf(aRoomStatusBarNotification(A_ROOM_ID, 1)),
roomInfo = aRoomInfo(id = A_ROOM_ID),
)
sut.clearReadRoomsNotifications(A_SESSION_ID)
runCurrent()
cancelNotificationResult.assertions().isCalledOnce().with(value(A_ROOM_ID.value), value(1))
}
@Test
fun `clearReadRoomsNotifications keeps notifications of rooms with unread messages`() = testKeepsNotification(
aRoomInfo(id = A_ROOM_ID, numUnreadMessages = 1)
)
@Test
fun `clearReadRoomsNotifications keeps notifications of rooms with unread notifications`() =
testKeepsNotification(aRoomInfo(id = A_ROOM_ID, numUnreadNotifications = 1))
@Test
fun `clearReadRoomsNotifications keeps notifications of rooms with unread mentions`() = testKeepsNotification(
aRoomInfo(id = A_ROOM_ID, numUnreadMentions = 1)
)
@Test
fun `clearReadRoomsNotifications keeps notifications when the room info is not found`() = testKeepsNotification(null)
@Test
fun `clearReadRoomsNotifications does nothing when the client cannot be restored`() = runTest {
val cancelNotificationResult = lambdaRecorder<String?, Int, Unit> { _, _ -> }
val sut = createClearReadRoomsManager(
cancelNotificationResult = cancelNotificationResult,
notifications = listOf(aRoomStatusBarNotification(A_ROOM_ID, 1)),
roomInfo = aRoomInfo(id = A_ROOM_ID),
clientResult = Result.failure(IllegalStateException("No client")),
)
sut.clearReadRoomsNotifications(A_SESSION_ID)
runCurrent()
cancelNotificationResult.assertions().isNeverCalled()
}
@Test
fun `clearReadRoomsNotifications ignores notifications not linked to a room`() = runTest {
val cancelNotificationResult = lambdaRecorder<String?, Int, Unit> { _, _ -> }
val sut = createClearReadRoomsManager(
cancelNotificationResult = cancelNotificationResult,
notifications = listOf(
mockk<StatusBarNotification> {
every { id } returns 1
every { tag } returns null
},
),
roomInfo = aRoomInfo(id = A_ROOM_ID),
)
sut.clearReadRoomsNotifications(A_SESSION_ID)
runCurrent()
cancelNotificationResult.assertions().isNeverCalled()
}
@Test
fun `clearReadRoomsNotifications clears the summary when it is the only notification left`() = runTest {
val cancelNotificationResult = lambdaRecorder<String?, Int, Unit> { _, _ -> }
val summaryId = NotificationIdProvider.getSummaryNotificationId(A_SESSION_ID)
val sut = createClearReadRoomsManager(
cancelNotificationResult = cancelNotificationResult,
notifications = listOf(aRoomStatusBarNotification(A_ROOM_ID, 1)),
roomInfo = aRoomInfo(id = A_ROOM_ID),
summaryNotification = mockk { every { id } returns summaryId },
count = 1,
)
sut.clearReadRoomsNotifications(A_SESSION_ID)
runCurrent()
cancelNotificationResult.assertions().isCalledExactly(2).withSequence(
listOf(value(A_ROOM_ID.value), value(1)),
listOf(value(null), value(summaryId)),
)
}
private fun testKeepsNotification(roomInfo: RoomInfo?) = runTest {
val cancelNotificationResult = lambdaRecorder<String?, Int, Unit> { _, _ -> }
val sut = createClearReadRoomsManager(
cancelNotificationResult = cancelNotificationResult,
notifications = listOf(aRoomStatusBarNotification(A_ROOM_ID, 1)),
roomInfo = roomInfo,
)
sut.clearReadRoomsNotifications(A_SESSION_ID)
runCurrent()
cancelNotificationResult.assertions().isNeverCalled()
}
private fun aRoomStatusBarNotification(roomId: RoomId, notificationId: Int): StatusBarNotification = mockk {
every { id } returns notificationId
every { tag } returns roomId.value
}
private fun TestScope.createClearReadRoomsManager(
cancelNotificationResult: LambdaTwoParamsRecorder<String?, Int, Unit>,
notifications: List<StatusBarNotification>,
roomInfo: RoomInfo?,
clientResult: Result<FakeMatrixClient>? = null,
summaryNotification: StatusBarNotification? = null,
count: Int = notifications.size,
): DefaultNotificationDrawerManager {
val matrixClient = FakeMatrixClient().apply {
getRoomInfoLambda = { Result.success(roomInfo) }
}
return createDefaultNotificationDrawerManager(
notificationDisplayer = FakeNotificationDisplayer(cancelNotificationResult = cancelNotificationResult),
activeNotificationsProvider = FakeActiveNotificationsProvider(
getNotificationsForSessionResult = { notifications },
getSummaryNotificationResult = { summaryNotification },
countResult = { count },
),
matrixClientProvider = FakeMatrixClientProvider(getClient = { clientResult ?: Result.success(matrixClient) }),
)
}
/** /**
* The content of the original event must be stripped, only the noisy flag, the ids and the timestamp are kept. * The content of the original event must be stripped, only the noisy flag, the ids and the timestamp are kept.
*/ */
@@ -21,7 +21,8 @@ class FakeNotificationCleaner(
private val clearMessagesForThreadLambda: (SessionId, RoomId, ThreadId) -> Unit = { _, _, _ -> lambdaError() }, private val clearMessagesForThreadLambda: (SessionId, RoomId, ThreadId) -> Unit = { _, _, _ -> lambdaError() },
private val clearEventLambda: (SessionId, EventId) -> Unit = { _, _ -> lambdaError() }, private val clearEventLambda: (SessionId, EventId) -> Unit = { _, _ -> lambdaError() },
private val clearMembershipNotificationForSessionLambda: (SessionId) -> Unit = { lambdaError() }, private val clearMembershipNotificationForSessionLambda: (SessionId) -> Unit = { lambdaError() },
private val clearMembershipNotificationForRoomLambda: (SessionId, RoomId) -> Unit = { _, _ -> lambdaError() } private val clearMembershipNotificationForRoomLambda: (SessionId, RoomId) -> Unit = { _, _ -> lambdaError() },
private val clearReadRoomsNotificationsLambda: (SessionId) -> Unit = { lambdaError() },
) : NotificationCleaner { ) : NotificationCleaner {
override fun clearAllMessagesEvents(sessionId: SessionId) { override fun clearAllMessagesEvents(sessionId: SessionId) {
clearAllMessagesEventsLambda(sessionId) clearAllMessagesEventsLambda(sessionId)
@@ -46,4 +47,8 @@ class FakeNotificationCleaner(
override fun clearMembershipNotificationForRoom(sessionId: SessionId, roomId: RoomId) { override fun clearMembershipNotificationForRoom(sessionId: SessionId, roomId: RoomId) {
clearMembershipNotificationForRoomLambda(sessionId, roomId) clearMembershipNotificationForRoomLambda(sessionId, roomId)
} }
override fun clearReadRoomsNotifications(sessionId: SessionId) {
clearReadRoomsNotificationsLambda(sessionId)
}
} }