diffing historical event timestamps to avoid acting on them as new events
- fixes an issue where the database window would pull in legacy events after marking new events as read
This commit is contained in:
parent
63618276b7
commit
b0a438ee98
|
@ -34,10 +34,12 @@ private fun Flow<UnreadNotifications>.onlyRenderableChanges(): Flow<UnreadNotifi
|
|||
log(AppLogTag.NOTIFICATION, "Ignoring unread change due to no renderable changes")
|
||||
false
|
||||
}
|
||||
|
||||
inferredCurrentNotifications.isEmpty() && diff.removed.isNotEmpty() -> {
|
||||
log(AppLogTag.NOTIFICATION, "Ignoring unread change due to no currently showing messages and changes are all messages marked as read")
|
||||
false
|
||||
}
|
||||
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
@ -45,29 +47,48 @@ private fun Flow<UnreadNotifications>.onlyRenderableChanges(): Flow<UnreadNotifi
|
|||
}
|
||||
|
||||
private fun Flow<Map<RoomOverview, List<RoomEvent>>>.mapWithDiff(): Flow<Pair<Map<RoomOverview, List<RoomEvent>>, NotificationDiff>> {
|
||||
val previousUnreadEvents = mutableMapOf<RoomId, List<EventId>>()
|
||||
val previousUnreadEvents = mutableMapOf<RoomId, List<TimestampedEventId>>()
|
||||
return this.map { each ->
|
||||
val allUnreadIds = each.toIds()
|
||||
val allUnreadIds = each.toTimestampedIds()
|
||||
val notificationDiff = calculateDiff(allUnreadIds, previousUnreadEvents)
|
||||
previousUnreadEvents.clearAndPutAll(allUnreadIds)
|
||||
each to notificationDiff
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateDiff(allUnread: Map<RoomId, List<EventId>>, previousUnread: Map<RoomId, List<EventId>>?): NotificationDiff {
|
||||
private fun calculateDiff(allUnread: Map<RoomId, List<TimestampedEventId>>, previousUnread: Map<RoomId, List<TimestampedEventId>>?): NotificationDiff {
|
||||
val previousLatestEventTimestamps = previousUnread.toLatestTimestamps()
|
||||
val newRooms = allUnread.filter { !previousUnread.containsKey(it.key) }.keys
|
||||
val unchanged = previousUnread?.filter { allUnread.containsKey(it.key) && it.value == allUnread[it.key] } ?: emptyMap()
|
||||
val changedOrNew = allUnread.filterNot { unchanged.containsKey(it.key) }
|
||||
|
||||
val unchanged = previousUnread?.filter {
|
||||
allUnread.containsKey(it.key) && (it.value == allUnread[it.key])
|
||||
} ?: emptyMap()
|
||||
val changedOrNew = allUnread.filterNot { unchanged.containsKey(it.key) }.mapValues { (key, value) ->
|
||||
val isChangedRoom = !newRooms.contains(key)
|
||||
if (isChangedRoom) {
|
||||
val latest = previousLatestEventTimestamps[key] ?: 0L
|
||||
value.filter {
|
||||
val isExistingEvent = (previousUnread?.get(key)?.contains(it) ?: false)
|
||||
!isExistingEvent && it.second > latest
|
||||
}
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}.filter { it.value.isNotEmpty() }
|
||||
val removed = previousUnread?.filter { !allUnread.containsKey(it.key) } ?: emptyMap()
|
||||
return NotificationDiff(unchanged, changedOrNew, removed, newRooms)
|
||||
return NotificationDiff(unchanged.toEventIds(), changedOrNew.toEventIds(), removed.toEventIds(), newRooms)
|
||||
}
|
||||
|
||||
private fun List<RoomEvent>.toEventIds() = this.map { it.eventId }
|
||||
private fun Map<RoomId, List<TimestampedEventId>>?.toLatestTimestamps() = this?.mapValues { it.value.maxOf { it.second } } ?: emptyMap()
|
||||
|
||||
private fun Map<RoomOverview, List<RoomEvent>>.toIds() = this
|
||||
private fun Map<RoomId, List<TimestampedEventId>>.toEventIds() = this.mapValues { it.value.map { it.first } }
|
||||
|
||||
private fun Map<RoomOverview, List<RoomEvent>>.toTimestampedIds() = this
|
||||
.mapValues { it.value.toEventIds() }
|
||||
.mapKeys { it.key.roomId }
|
||||
|
||||
private fun List<RoomEvent>.toEventIds() = this.map { it.eventId to it.utcTimestamp }
|
||||
|
||||
private fun <T> Flow<T>.avoidShowingPreviousNotificationsOnLaunch() = drop(1)
|
||||
|
||||
data class NotificationDiff(
|
||||
|
@ -76,3 +97,5 @@ data class NotificationDiff(
|
|||
val removed: Map<RoomId, List<EventId>>,
|
||||
val newRooms: Set<RoomId>
|
||||
)
|
||||
|
||||
typealias TimestampedEventId = Pair<EventId, Long>
|
|
@ -3,8 +3,11 @@ package app.dapk.st.notifications
|
|||
import app.dapk.st.matrix.sync.RoomEvent
|
||||
import app.dapk.st.matrix.sync.RoomOverview
|
||||
import fake.FakeRoomStore
|
||||
import fixture.*
|
||||
import fixture.NotificationDiffFixtures.aNotificationDiff
|
||||
import fixture.aRoomId
|
||||
import fixture.aRoomMessageEvent
|
||||
import fixture.aRoomOverview
|
||||
import fixture.anEventId
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
@ -12,8 +15,8 @@ import org.amshove.kluent.shouldBeEqualTo
|
|||
import org.junit.Test
|
||||
|
||||
private val NO_UNREADS = emptyMap<RoomOverview, List<RoomEvent>>()
|
||||
private val A_MESSAGE = aRoomMessageEvent(eventId = anEventId("1"), content = "hello")
|
||||
private val A_MESSAGE_2 = aRoomMessageEvent(eventId = anEventId("2"), content = "world")
|
||||
private val A_MESSAGE = aRoomMessageEvent(eventId = anEventId("1"), content = "hello", utcTimestamp = 1000)
|
||||
private val A_MESSAGE_2 = aRoomMessageEvent(eventId = anEventId("2"), content = "world", utcTimestamp = 2000)
|
||||
private val A_ROOM_OVERVIEW = aRoomOverview(roomId = aRoomId("1"))
|
||||
private val A_ROOM_OVERVIEW_2 = aRoomOverview(roomId = aRoomId("2"))
|
||||
|
||||
|
@ -48,7 +51,7 @@ class ObserveUnreadRenderNotificationsUseCaseTest {
|
|||
changedOrNew = A_ROOM_OVERVIEW.toDiff(A_MESSAGE),
|
||||
newRooms = setOf(A_ROOM_OVERVIEW.roomId)
|
||||
),
|
||||
A_ROOM_OVERVIEW.withUnreads(A_MESSAGE, A_MESSAGE_2) to aNotificationDiff(changedOrNew = A_ROOM_OVERVIEW.toDiff(A_MESSAGE, A_MESSAGE_2))
|
||||
A_ROOM_OVERVIEW.withUnreads(A_MESSAGE, A_MESSAGE_2) to aNotificationDiff(changedOrNew = A_ROOM_OVERVIEW.toDiff(A_MESSAGE_2))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -61,7 +64,7 @@ class ObserveUnreadRenderNotificationsUseCaseTest {
|
|||
val result = useCase.invoke().toList()
|
||||
|
||||
result shouldBeEqualTo listOf(
|
||||
A_ROOM_OVERVIEW.withUnreads(A_MESSAGE, A_MESSAGE_2) to aNotificationDiff(changedOrNew = A_ROOM_OVERVIEW.toDiff(A_MESSAGE, A_MESSAGE_2))
|
||||
A_ROOM_OVERVIEW.withUnreads(A_MESSAGE, A_MESSAGE_2) to aNotificationDiff(changedOrNew = A_ROOM_OVERVIEW.toDiff(A_MESSAGE_2))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -76,6 +79,26 @@ class ObserveUnreadRenderNotificationsUseCaseTest {
|
|||
result shouldBeEqualTo emptyList()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given new and then historical message, when reading a message, then only emits the latest`() = runTest {
|
||||
fakeRoomStore.givenUnreadEvents(
|
||||
flowOf(
|
||||
NO_UNREADS,
|
||||
A_ROOM_OVERVIEW.withUnreads(A_MESSAGE),
|
||||
A_ROOM_OVERVIEW.withUnreads(A_MESSAGE, A_MESSAGE.copy(eventId = anEventId("old"), utcTimestamp = -1))
|
||||
)
|
||||
)
|
||||
|
||||
val result = useCase.invoke().toList()
|
||||
|
||||
result shouldBeEqualTo listOf(
|
||||
A_ROOM_OVERVIEW.withUnreads(A_MESSAGE) to aNotificationDiff(
|
||||
changedOrNew = A_ROOM_OVERVIEW.toDiff(A_MESSAGE),
|
||||
newRooms = setOf(A_ROOM_OVERVIEW.roomId)
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given initial unreads, when reading a duplicate unread, then emits nothing`() = runTest {
|
||||
fakeRoomStore.givenUnreadEvents(
|
||||
|
|
Loading…
Reference in New Issue