lifting directory local echo merging to its own class

This commit is contained in:
Adam Brown 2022-11-05 11:12:12 +00:00
parent 9048f9fb0c
commit a21f3a1663
7 changed files with 66 additions and 51 deletions

View File

@ -0,0 +1,53 @@
package app.dapk.st.engine
import app.dapk.st.matrix.common.RoomId
import app.dapk.st.matrix.common.RoomMember
import app.dapk.st.matrix.common.UserId
import app.dapk.st.matrix.common.asString
import app.dapk.st.matrix.message.MessageService
import app.dapk.st.matrix.room.RoomService
internal typealias DirectoryMergeWithLocalEchosUseCase = suspend (OverviewState, UserId, Map<RoomId, List<MessageService.LocalEcho>>) -> OverviewState
internal class DirectoryMergeWithLocalEchosUseCaseImpl(
private val roomService: RoomService,
) : DirectoryMergeWithLocalEchosUseCase {
override suspend fun invoke(overview: OverviewState, selfId: UserId, echos: Map<RoomId, List<MessageService.LocalEcho>>): OverviewState {
return when {
echos.isEmpty() -> overview
else -> overview.map {
when (val roomEchos = echos[it.roomId]) {
null -> it
else -> it.mergeWithLocalEchos(
member = roomService.findMember(it.roomId, selfId) ?: RoomMember(
selfId,
null,
avatarUrl = null,
),
echos = roomEchos,
)
}
}
}
}
private fun RoomOverview.mergeWithLocalEchos(member: RoomMember, echos: List<MessageService.LocalEcho>): RoomOverview {
val latestEcho = echos.maxByOrNull { it.timestampUtc }
return if (latestEcho != null && latestEcho.timestampUtc > (this.lastMessage?.utcTimestamp ?: 0)) {
this.copy(
lastMessage = RoomOverview.LastMessage(
content = when (val message = latestEcho.message) {
is MessageService.Message.TextMessage -> message.content.body.asString()
is MessageService.Message.ImageMessage -> "\uD83D\uDCF7"
},
utcTimestamp = latestEcho.timestampUtc,
author = member,
)
)
} else {
this
}
}
}

View File

@ -1,7 +1,7 @@
package app.dapk.st.engine package app.dapk.st.engine
import app.dapk.st.core.extensions.combine import app.dapk.st.core.extensions.combine
import app.dapk.st.matrix.common.* import app.dapk.st.matrix.common.CredentialsStore
import app.dapk.st.matrix.message.MessageService import app.dapk.st.matrix.message.MessageService
import app.dapk.st.matrix.room.RoomService import app.dapk.st.matrix.room.RoomService
import app.dapk.st.matrix.sync.RoomStore import app.dapk.st.matrix.sync.RoomStore
@ -15,9 +15,9 @@ import kotlinx.coroutines.flow.map
internal class DirectoryUseCase( internal class DirectoryUseCase(
private val syncService: SyncService, private val syncService: SyncService,
private val messageService: MessageService, private val messageService: MessageService,
private val roomService: RoomService,
private val credentialsStore: CredentialsStore, private val credentialsStore: CredentialsStore,
private val roomStore: RoomStore, private val roomStore: RoomStore,
private val mergeLocalEchosUseCase: DirectoryMergeWithLocalEchosUseCaseImpl
) { ) {
fun state(): Flow<DirectoryState> { fun state(): Flow<DirectoryState> {
@ -30,7 +30,7 @@ internal class DirectoryUseCase(
syncService.events(), syncService.events(),
roomStore.observeMuted(), roomStore.observeMuted(),
) { _, overviewState, localEchos, unread, events, muted -> ) { _, overviewState, localEchos, unread, events, muted ->
overviewState.mergeWithLocalEchos(localEchos, userId).map { roomOverview -> mergeLocalEchosUseCase.invoke(overviewState, userId, localEchos).map { roomOverview ->
DirectoryItem( DirectoryItem(
overview = roomOverview, overview = roomOverview,
unreadCount = UnreadCount(unread[roomOverview.roomId] ?: 0), unreadCount = UnreadCount(unread[roomOverview.roomId] ?: 0),
@ -41,42 +41,4 @@ internal class DirectoryUseCase(
} }
} }
} }
private suspend fun OverviewState.mergeWithLocalEchos(localEchos: Map<RoomId, List<MessageService.LocalEcho>>, userId: UserId): OverviewState {
return when {
localEchos.isEmpty() -> this
else -> this.map {
when (val roomEchos = localEchos[it.roomId]) {
null -> it
else -> it.mergeWithLocalEchos(
member = roomService.findMember(it.roomId, userId) ?: RoomMember(
userId,
null,
avatarUrl = null,
),
echos = roomEchos,
)
}
}
}
}
private fun RoomOverview.mergeWithLocalEchos(member: RoomMember, echos: List<MessageService.LocalEcho>): RoomOverview {
val latestEcho = echos.maxByOrNull { it.timestampUtc }
return if (latestEcho != null && latestEcho.timestampUtc > (this.lastMessage?.utcTimestamp ?: 0)) {
this.copy(
lastMessage = RoomOverview.LastMessage(
content = when (val message = latestEcho.message) {
is MessageService.Message.TextMessage -> message.content.body.asString()
is MessageService.Message.ImageMessage -> "\uD83D\uDCF7"
},
utcTimestamp = latestEcho.timestampUtc,
author = member,
)
)
} else {
this
}
}
} }

View File

@ -173,14 +173,14 @@ class MatrixEngine internal constructor(
DirectoryUseCase( DirectoryUseCase(
matrix.syncService(), matrix.syncService(),
matrix.messageService(), matrix.messageService(),
matrix.roomService(),
credentialsStore, credentialsStore,
roomStore roomStore,
DirectoryMergeWithLocalEchosUseCaseImpl(matrix.roomService()),
) )
} }
val timelineUseCase = unsafeLazy { val timelineUseCase = unsafeLazy {
val matrix = lazyMatrix.value val matrix = lazyMatrix.value
val mergeWithLocalEchosUseCase = MergeWithLocalEchosUseCaseImpl(LocalEchoMapper(MetaMapper())) val mergeWithLocalEchosUseCase = TimelineMergeWithLocalEchosUseCaseImpl(LocalEchoMapper(MetaMapper()))
val timeline = TimelineUseCaseImpl(matrix.syncService(), matrix.messageService(), matrix.roomService(), mergeWithLocalEchosUseCase) val timeline = TimelineUseCaseImpl(matrix.syncService(), matrix.messageService(), matrix.roomService(), mergeWithLocalEchosUseCase)
ReadMarkingTimeline(roomStore, credentialsStore, timeline, matrix.roomService()) ReadMarkingTimeline(roomStore, credentialsStore, timeline, matrix.roomService())
} }

View File

@ -4,11 +4,11 @@ import app.dapk.st.matrix.common.EventId
import app.dapk.st.matrix.common.RoomMember import app.dapk.st.matrix.common.RoomMember
import app.dapk.st.matrix.message.MessageService import app.dapk.st.matrix.message.MessageService
internal typealias MergeWithLocalEchosUseCase = (RoomState, RoomMember, List<MessageService.LocalEcho>) -> RoomState internal typealias TimelineMergeWithLocalEchosUseCase = (RoomState, RoomMember, List<MessageService.LocalEcho>) -> RoomState
internal class MergeWithLocalEchosUseCaseImpl( internal class TimelineMergeWithLocalEchosUseCaseImpl(
private val localEventMapper: LocalEchoMapper, private val localEventMapper: LocalEchoMapper,
) : MergeWithLocalEchosUseCase { ) : TimelineMergeWithLocalEchosUseCase {
override fun invoke(roomState: RoomState, member: RoomMember, echos: List<MessageService.LocalEcho>): RoomState { override fun invoke(roomState: RoomState, member: RoomMember, echos: List<MessageService.LocalEcho>): RoomState {
val echosByEventId = echos.associateBy { it.eventId } val echosByEventId = echos.associateBy { it.eventId }

View File

@ -16,7 +16,7 @@ internal class TimelineUseCaseImpl(
private val syncService: SyncService, private val syncService: SyncService,
private val messageService: MessageService, private val messageService: MessageService,
private val roomService: RoomService, private val roomService: RoomService,
private val mergeWithLocalEchosUseCase: MergeWithLocalEchosUseCase private val timelineMergeWithLocalEchosUseCase: TimelineMergeWithLocalEchosUseCase,
) : ObserveTimelineUseCase { ) : ObserveTimelineUseCase {
override fun invoke(roomId: RoomId, userId: UserId): Flow<MessengerPageState> { override fun invoke(roomId: RoomId, userId: UserId): Flow<MessengerPageState> {
@ -30,7 +30,7 @@ internal class TimelineUseCaseImpl(
roomState = when { roomState = when {
localEchos.isEmpty() -> roomState localEchos.isEmpty() -> roomState
else -> { else -> {
mergeWithLocalEchosUseCase.invoke( timelineMergeWithLocalEchosUseCase.invoke(
roomState, roomState,
roomService.findMember(roomId, userId) ?: userId.toFallbackMember(), roomService.findMember(roomId, userId) ?: userId.toFallbackMember(),
localEchos, localEchos,

View File

@ -17,7 +17,7 @@ private val ANOTHER_ROOM_MESSAGE_EVENT = A_ROOM_MESSAGE_EVENT.copy(eventId = anE
class MergeWithLocalEchosUseCaseTest { class MergeWithLocalEchosUseCaseTest {
private val fakeLocalEchoMapper = fake.FakeLocalEventMapper() private val fakeLocalEchoMapper = fake.FakeLocalEventMapper()
private val mergeWithLocalEchosUseCase = MergeWithLocalEchosUseCaseImpl(fakeLocalEchoMapper.instance) private val mergeWithLocalEchosUseCase = TimelineMergeWithLocalEchosUseCaseImpl(fakeLocalEchoMapper.instance)
@Test @Test
fun `given no local echos, when merging text message, then returns original state`() { fun `given no local echos, when merging text message, then returns original state`() {

View File

@ -113,7 +113,7 @@ suspend fun <T> Flow<T>.test(scope: CoroutineScope) = FlowTestObserver(scope, th
this.collect() this.collect()
} }
class FakeMergeWithLocalEchosUseCase : MergeWithLocalEchosUseCase by mockk() { class FakeMergeWithLocalEchosUseCase : TimelineMergeWithLocalEchosUseCase by mockk() {
fun givenMerging(roomState: RoomState, roomMember: RoomMember, echos: List<MessageService.LocalEcho>) = every { fun givenMerging(roomState: RoomState, roomMember: RoomMember, echos: List<MessageService.LocalEcho>) = every {
this@FakeMergeWithLocalEchosUseCase.invoke(roomState.engine(), roomMember, echos) this@FakeMergeWithLocalEchosUseCase.invoke(roomState.engine(), roomMember, echos)
}.delegateReturn() }.delegateReturn()