When user manually mark a room as read, also dismiss the notifications for this room.
This commit is contained in:
parent
b593b47515
commit
e3c7e37f23
5 changed files with 42 additions and 8 deletions
|
|
@ -55,6 +55,7 @@ dependencies {
|
||||||
implementation(projects.libraries.permissions.api)
|
implementation(projects.libraries.permissions.api)
|
||||||
implementation(projects.libraries.permissions.noop)
|
implementation(projects.libraries.permissions.noop)
|
||||||
implementation(projects.libraries.preferences.api)
|
implementation(projects.libraries.preferences.api)
|
||||||
|
implementation(projects.libraries.push.api)
|
||||||
implementation(projects.features.invite.api)
|
implementation(projects.features.invite.api)
|
||||||
implementation(projects.features.networkmonitor.api)
|
implementation(projects.features.networkmonitor.api)
|
||||||
implementation(projects.features.leaveroom.api)
|
implementation(projects.features.leaveroom.api)
|
||||||
|
|
@ -79,6 +80,7 @@ dependencies {
|
||||||
testImplementation(projects.libraries.permissions.noop)
|
testImplementation(projects.libraries.permissions.noop)
|
||||||
testImplementation(projects.libraries.permissions.test)
|
testImplementation(projects.libraries.permissions.test)
|
||||||
testImplementation(projects.libraries.preferences.test)
|
testImplementation(projects.libraries.preferences.test)
|
||||||
|
testImplementation(projects.libraries.push.test)
|
||||||
testImplementation(projects.services.analytics.test)
|
testImplementation(projects.services.analytics.test)
|
||||||
testImplementation(projects.services.toolbox.test)
|
testImplementation(projects.services.toolbox.test)
|
||||||
testImplementation(projects.features.networkmonitor.test)
|
testImplementation(projects.features.networkmonitor.test)
|
||||||
|
|
|
||||||
|
|
@ -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.sync.SyncState
|
||||||
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
||||||
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
|
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.analytics.api.AnalyticsService
|
||||||
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
|
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
|
|
@ -97,6 +98,7 @@ class RoomListPresenter @Inject constructor(
|
||||||
private val analyticsService: AnalyticsService,
|
private val analyticsService: AnalyticsService,
|
||||||
private val acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState>,
|
private val acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState>,
|
||||||
private val fullScreenIntentPermissionsPresenter: FullScreenIntentPermissionsPresenter,
|
private val fullScreenIntentPermissionsPresenter: FullScreenIntentPermissionsPresenter,
|
||||||
|
private val notificationCleaner: NotificationCleaner,
|
||||||
) : Presenter<RoomListState> {
|
) : Presenter<RoomListState> {
|
||||||
private val encryptionService: EncryptionService = client.encryptionService()
|
private val encryptionService: EncryptionService = client.encryptionService()
|
||||||
private val syncService: SyncService = client.syncService()
|
private val syncService: SyncService = client.syncService()
|
||||||
|
|
@ -268,6 +270,7 @@ class RoomListPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.markAsRead(roomId: RoomId) = launch {
|
private fun CoroutineScope.markAsRead(roomId: RoomId) = launch {
|
||||||
|
notificationCleaner.clearMessagesForRoom(client.sessionId, roomId)
|
||||||
client.getRoom(roomId)?.use { room ->
|
client.getRoom(roomId)?.use { room ->
|
||||||
room.setUnreadFlag(isUnread = false)
|
room.setUnreadFlag(isUnread = false)
|
||||||
val receiptType = if (sessionPreferencesStore.isSendPublicReadReceiptsEnabled().first()) {
|
val receiptType = if (sessionPreferencesStore.isSendPublicReadReceiptsEnabled().first()) {
|
||||||
|
|
|
||||||
|
|
@ -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.indicator.impl.DefaultIndicatorService
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
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.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.BackupState
|
||||||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
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_AVATAR_URL
|
||||||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
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
|
||||||
|
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_ID
|
||||||
import io.element.android.libraries.matrix.test.A_USER_NAME
|
import io.element.android.libraries.matrix.test.A_USER_NAME
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
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.matrix.test.verification.FakeSessionVerificationService
|
||||||
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
|
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
|
||||||
import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore
|
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.api.AnalyticsService
|
||||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||||
import io.element.android.tests.testutils.EventsRecorder
|
import io.element.android.tests.testutils.EventsRecorder
|
||||||
|
|
@ -503,35 +509,54 @@ class RoomListPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - check that the room is marked as read with correct RR and as unread`() = runTest {
|
fun `present - check that the room is marked as read with correct RR and as unread`() = runTest {
|
||||||
val room = FakeMatrixRoom()
|
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 sessionPreferencesStore = InMemorySessionPreferencesStore()
|
||||||
val matrixClient = FakeMatrixClient().apply {
|
val matrixClient = FakeMatrixClient().apply {
|
||||||
givenGetRoomResult(A_ROOM_ID, room)
|
givenGetRoomResult(A_ROOM_ID, room)
|
||||||
|
givenGetRoomResult(A_ROOM_ID_2, room2)
|
||||||
|
givenGetRoomResult(A_ROOM_ID_3, room3)
|
||||||
}
|
}
|
||||||
val analyticsService = FakeAnalyticsService()
|
val analyticsService = FakeAnalyticsService()
|
||||||
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||||
|
val clearMessagesForRoomLambda = lambdaRecorder<SessionId, RoomId, Unit> { _, _ -> }
|
||||||
|
val notificationCleaner = FakeNotificationCleaner(
|
||||||
|
clearMessagesForRoomLambda = clearMessagesForRoomLambda,
|
||||||
|
)
|
||||||
val presenter = createRoomListPresenter(
|
val presenter = createRoomListPresenter(
|
||||||
client = matrixClient,
|
client = matrixClient,
|
||||||
coroutineScope = scope,
|
coroutineScope = scope,
|
||||||
sessionPreferencesStore = sessionPreferencesStore,
|
sessionPreferencesStore = sessionPreferencesStore,
|
||||||
analyticsService = analyticsService,
|
analyticsService = analyticsService,
|
||||||
|
notificationCleaner = notificationCleaner,
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(room.markAsReadCalls).isEmpty()
|
allRooms.forEach {
|
||||||
assertThat(room.setUnreadFlagCalls).isEmpty()
|
assertThat(it.markAsReadCalls).isEmpty()
|
||||||
|
assertThat(it.setUnreadFlagCalls).isEmpty()
|
||||||
|
}
|
||||||
initialState.eventSink.invoke(RoomListEvents.MarkAsRead(A_ROOM_ID))
|
initialState.eventSink.invoke(RoomListEvents.MarkAsRead(A_ROOM_ID))
|
||||||
assertThat(room.markAsReadCalls).isEqualTo(listOf(ReceiptType.READ))
|
assertThat(room.markAsReadCalls).isEqualTo(listOf(ReceiptType.READ))
|
||||||
assertThat(room.setUnreadFlagCalls).isEqualTo(listOf(false))
|
assertThat(room.setUnreadFlagCalls).isEqualTo(listOf(false))
|
||||||
initialState.eventSink.invoke(RoomListEvents.MarkAsUnread(A_ROOM_ID))
|
clearMessagesForRoomLambda.assertions().isCalledOnce()
|
||||||
assertThat(room.markAsReadCalls).isEqualTo(listOf(ReceiptType.READ))
|
.with(value(A_SESSION_ID), value(A_ROOM_ID))
|
||||||
assertThat(room.setUnreadFlagCalls).isEqualTo(listOf(false, true))
|
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
|
// Test again with private read receipts
|
||||||
sessionPreferencesStore.setSendPublicReadReceipts(false)
|
sessionPreferencesStore.setSendPublicReadReceipts(false)
|
||||||
initialState.eventSink.invoke(RoomListEvents.MarkAsRead(A_ROOM_ID))
|
initialState.eventSink.invoke(RoomListEvents.MarkAsRead(A_ROOM_ID_3))
|
||||||
assertThat(room.markAsReadCalls).isEqualTo(listOf(ReceiptType.READ, ReceiptType.READ_PRIVATE))
|
assertThat(room3.markAsReadCalls).isEqualTo(listOf(ReceiptType.READ_PRIVATE))
|
||||||
assertThat(room.setUnreadFlagCalls).isEqualTo(listOf(false, true, false))
|
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(
|
assertThat(analyticsService.capturedEvents).containsExactly(
|
||||||
Interaction(name = Interaction.Name.MobileRoomListRoomContextMenuUnreadToggle),
|
Interaction(name = Interaction.Name.MobileRoomListRoomContextMenuUnreadToggle),
|
||||||
Interaction(name = Interaction.Name.MobileRoomListRoomContextMenuUnreadToggle),
|
Interaction(name = Interaction.Name.MobileRoomListRoomContextMenuUnreadToggle),
|
||||||
|
|
@ -633,6 +658,7 @@ class RoomListPresenterTest {
|
||||||
filtersPresenter: Presenter<RoomListFiltersState> = Presenter { aRoomListFiltersState() },
|
filtersPresenter: Presenter<RoomListFiltersState> = Presenter { aRoomListFiltersState() },
|
||||||
searchPresenter: Presenter<RoomListSearchState> = Presenter { aRoomListSearchState() },
|
searchPresenter: Presenter<RoomListSearchState> = Presenter { aRoomListSearchState() },
|
||||||
acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState> = Presenter { anAcceptDeclineInviteState() },
|
acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState> = Presenter { anAcceptDeclineInviteState() },
|
||||||
|
notificationCleaner: NotificationCleaner = FakeNotificationCleaner(),
|
||||||
) = RoomListPresenter(
|
) = RoomListPresenter(
|
||||||
client = client,
|
client = client,
|
||||||
networkMonitor = networkMonitor,
|
networkMonitor = networkMonitor,
|
||||||
|
|
@ -660,5 +686,6 @@ class RoomListPresenterTest {
|
||||||
analyticsService = analyticsService,
|
analyticsService = analyticsService,
|
||||||
acceptDeclineInvitePresenter = acceptDeclineInvitePresenter,
|
acceptDeclineInvitePresenter = acceptDeclineInvitePresenter,
|
||||||
fullScreenIntentPermissionsPresenter = FakeFullScreenIntentPermissionsPresenter(),
|
fullScreenIntentPermissionsPresenter = FakeFullScreenIntentPermissionsPresenter(),
|
||||||
|
notificationCleaner = notificationCleaner,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ val A_SPACE_ID = SpaceId("!aSpaceId:domain")
|
||||||
val A_SPACE_ID_2 = SpaceId("!aSpaceId2:domain")
|
val A_SPACE_ID_2 = SpaceId("!aSpaceId2:domain")
|
||||||
val A_ROOM_ID = RoomId("!aRoomId:domain")
|
val A_ROOM_ID = RoomId("!aRoomId:domain")
|
||||||
val A_ROOM_ID_2 = RoomId("!aRoomId2: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 = ThreadId("\$aThreadId")
|
||||||
val A_THREAD_ID_2 = ThreadId("\$aThreadId2")
|
val A_THREAD_ID_2 = ThreadId("\$aThreadId2")
|
||||||
val AN_EVENT_ID = EventId("\$anEventId")
|
val AN_EVENT_ID = EventId("\$anEventId")
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,7 @@ class RoomListScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
notificationCleaner = FakeNotificationCleaner(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue