When user manually mark a room as read, also dismiss the notifications for this room.

This commit is contained in:
Benoit Marty
2024-07-16 13:07:30 +02:00
parent b593b47515
commit e3c7e37f23
5 changed files with 42 additions and 8 deletions

View File

@@ -55,6 +55,7 @@ dependencies {
implementation(projects.libraries.permissions.api)
implementation(projects.libraries.permissions.noop)
implementation(projects.libraries.preferences.api)
implementation(projects.libraries.push.api)
implementation(projects.features.invite.api)
implementation(projects.features.networkmonitor.api)
implementation(projects.features.leaveroom.api)
@@ -79,6 +80,7 @@ dependencies {
testImplementation(projects.libraries.permissions.noop)
testImplementation(projects.libraries.permissions.test)
testImplementation(projects.libraries.preferences.test)
testImplementation(projects.libraries.push.test)
testImplementation(projects.services.analytics.test)
testImplementation(projects.services.toolbox.test)
testImplementation(projects.features.networkmonitor.test)

View File

@@ -63,6 +63,7 @@ import io.element.android.libraries.matrix.api.sync.SyncService
import io.element.android.libraries.matrix.api.sync.SyncState
import io.element.android.libraries.matrix.api.timeline.ReceiptType
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
import io.element.android.libraries.push.api.notifications.NotificationCleaner
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
import kotlinx.collections.immutable.toPersistentList
@@ -97,6 +98,7 @@ class RoomListPresenter @Inject constructor(
private val analyticsService: AnalyticsService,
private val acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState>,
private val fullScreenIntentPermissionsPresenter: FullScreenIntentPermissionsPresenter,
private val notificationCleaner: NotificationCleaner,
) : Presenter<RoomListState> {
private val encryptionService: EncryptionService = client.encryptionService()
private val syncService: SyncService = client.syncService()
@@ -268,6 +270,7 @@ class RoomListPresenter @Inject constructor(
}
private fun CoroutineScope.markAsRead(roomId: RoomId) = launch {
notificationCleaner.clearMessagesForRoom(client.sessionId, roomId)
client.getRoom(roomId)?.use { room ->
room.setUnreadFlag(isUnread = false)
val receiptType = if (sessionPreferencesStore.isSendPublicReadReceiptsEnabled().first()) {

View File

@@ -51,6 +51,7 @@ import io.element.android.libraries.fullscreenintent.test.FakeFullScreenIntentPe
import io.element.android.libraries.indicator.impl.DefaultIndicatorService
import io.element.android.libraries.matrix.api.MatrixClient
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.encryption.BackupState
import io.element.android.libraries.matrix.api.encryption.RecoveryState
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
@@ -62,6 +63,9 @@ import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
import io.element.android.libraries.matrix.test.A_ROOM_ID_3
import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.A_USER_NAME
import io.element.android.libraries.matrix.test.FakeMatrixClient
@@ -75,6 +79,8 @@ import io.element.android.libraries.matrix.test.sync.FakeSyncService
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore
import io.element.android.libraries.push.api.notifications.NotificationCleaner
import io.element.android.libraries.push.test.notifications.FakeNotificationCleaner
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.EventsRecorder
@@ -503,35 +509,54 @@ class RoomListPresenterTest {
@Test
fun `present - check that the room is marked as read with correct RR and as unread`() = runTest {
val room = FakeMatrixRoom()
val room2 = FakeMatrixRoom(roomId = A_ROOM_ID_2)
val room3 = FakeMatrixRoom(roomId = A_ROOM_ID_3)
val allRooms = setOf(room, room2, room3)
val sessionPreferencesStore = InMemorySessionPreferencesStore()
val matrixClient = FakeMatrixClient().apply {
givenGetRoomResult(A_ROOM_ID, room)
givenGetRoomResult(A_ROOM_ID_2, room2)
givenGetRoomResult(A_ROOM_ID_3, room3)
}
val analyticsService = FakeAnalyticsService()
val scope = CoroutineScope(coroutineContext + SupervisorJob())
val clearMessagesForRoomLambda = lambdaRecorder<SessionId, RoomId, Unit> { _, _ -> }
val notificationCleaner = FakeNotificationCleaner(
clearMessagesForRoomLambda = clearMessagesForRoomLambda,
)
val presenter = createRoomListPresenter(
client = matrixClient,
coroutineScope = scope,
sessionPreferencesStore = sessionPreferencesStore,
analyticsService = analyticsService,
notificationCleaner = notificationCleaner,
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
assertThat(room.markAsReadCalls).isEmpty()
assertThat(room.setUnreadFlagCalls).isEmpty()
allRooms.forEach {
assertThat(it.markAsReadCalls).isEmpty()
assertThat(it.setUnreadFlagCalls).isEmpty()
}
initialState.eventSink.invoke(RoomListEvents.MarkAsRead(A_ROOM_ID))
assertThat(room.markAsReadCalls).isEqualTo(listOf(ReceiptType.READ))
assertThat(room.setUnreadFlagCalls).isEqualTo(listOf(false))
initialState.eventSink.invoke(RoomListEvents.MarkAsUnread(A_ROOM_ID))
assertThat(room.markAsReadCalls).isEqualTo(listOf(ReceiptType.READ))
assertThat(room.setUnreadFlagCalls).isEqualTo(listOf(false, true))
clearMessagesForRoomLambda.assertions().isCalledOnce()
.with(value(A_SESSION_ID), value(A_ROOM_ID))
initialState.eventSink.invoke(RoomListEvents.MarkAsUnread(A_ROOM_ID_2))
assertThat(room2.markAsReadCalls).isEqualTo(emptyList<ReceiptType>())
assertThat(room2.setUnreadFlagCalls).isEqualTo(listOf(true))
// Test again with private read receipts
sessionPreferencesStore.setSendPublicReadReceipts(false)
initialState.eventSink.invoke(RoomListEvents.MarkAsRead(A_ROOM_ID))
assertThat(room.markAsReadCalls).isEqualTo(listOf(ReceiptType.READ, ReceiptType.READ_PRIVATE))
assertThat(room.setUnreadFlagCalls).isEqualTo(listOf(false, true, false))
initialState.eventSink.invoke(RoomListEvents.MarkAsRead(A_ROOM_ID_3))
assertThat(room3.markAsReadCalls).isEqualTo(listOf(ReceiptType.READ_PRIVATE))
assertThat(room3.setUnreadFlagCalls).isEqualTo(listOf(false))
clearMessagesForRoomLambda.assertions().isCalledExactly(2)
.withSequence(
listOf(value(A_SESSION_ID), value(A_ROOM_ID)),
listOf(value(A_SESSION_ID), value(A_ROOM_ID_3)),
)
assertThat(analyticsService.capturedEvents).containsExactly(
Interaction(name = Interaction.Name.MobileRoomListRoomContextMenuUnreadToggle),
Interaction(name = Interaction.Name.MobileRoomListRoomContextMenuUnreadToggle),
@@ -633,6 +658,7 @@ class RoomListPresenterTest {
filtersPresenter: Presenter<RoomListFiltersState> = Presenter { aRoomListFiltersState() },
searchPresenter: Presenter<RoomListSearchState> = Presenter { aRoomListSearchState() },
acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState> = Presenter { anAcceptDeclineInviteState() },
notificationCleaner: NotificationCleaner = FakeNotificationCleaner(),
) = RoomListPresenter(
client = client,
networkMonitor = networkMonitor,
@@ -660,5 +686,6 @@ class RoomListPresenterTest {
analyticsService = analyticsService,
acceptDeclineInvitePresenter = acceptDeclineInvitePresenter,
fullScreenIntentPermissionsPresenter = FakeFullScreenIntentPermissionsPresenter(),
notificationCleaner = notificationCleaner,
)
}

View File

@@ -48,6 +48,7 @@ val A_SPACE_ID = SpaceId("!aSpaceId:domain")
val A_SPACE_ID_2 = SpaceId("!aSpaceId2:domain")
val A_ROOM_ID = RoomId("!aRoomId:domain")
val A_ROOM_ID_2 = RoomId("!aRoomId2:domain")
val A_ROOM_ID_3 = RoomId("!aRoomId3:domain")
val A_THREAD_ID = ThreadId("\$aThreadId")
val A_THREAD_ID_2 = ThreadId("\$aThreadId2")
val AN_EVENT_ID = EventId("\$anEventId")

View File

@@ -158,6 +158,7 @@ class RoomListScreen(
)
}
},
notificationCleaner = FakeNotificationCleaner(),
)
@Composable