porting the messenger module to chat-engine

This commit is contained in:
Adam Brown 2022-10-09 19:22:03 +01:00
parent baf7cfc17a
commit e61dea7ba7
8 changed files with 102 additions and 59 deletions

View File

@ -178,11 +178,8 @@ internal class FeatureModules internal constructor(
val messengerModule by unsafeLazy {
MessengerModule(
matrixModules.engine,
matrixModules.message,
clock,
context,
base64,
imageContentReader,
storeModule.value.messageStore(),
)
}

View File

@ -20,4 +20,5 @@ interface ChatEngine {
suspend fun InputStream.importRoomKeys(password: String): Flow<ImportResult>
suspend fun send(message: SendMessage, room: RoomOverview)
}

View File

@ -197,4 +197,25 @@ sealed class MessageMeta {
}
}
}
}
sealed interface SendMessage {
data class TextMessage(
val content: String,
val reply: Reply? = null,
) : SendMessage {
data class Reply(
val author: RoomMember,
val originalMessage: String,
val eventId: EventId,
val timestampUtc: Long,
)
}
data class ImageMessage(
val uri: String,
) : SendMessage
}

View File

@ -3,10 +3,6 @@ apply plugin: 'kotlin-parcelize'
dependencies {
implementation project(":chat-engine")
implementation project(":matrix:services:sync")
implementation project(":matrix:services:message")
implementation project(":matrix:services:crypto")
implementation project(":matrix:services:room")
implementation project(":domains:android:compose-core")
implementation project(":domains:android:viewmodel")
implementation project(":domains:store")

View File

@ -5,33 +5,19 @@ import app.dapk.st.core.Base64
import app.dapk.st.core.ProvidableModule
import app.dapk.st.domain.application.message.MessageOptionsStore
import app.dapk.st.engine.ChatEngine
import app.dapk.st.matrix.common.CredentialsStore
import app.dapk.st.matrix.common.RoomId
import app.dapk.st.matrix.message.MessageService
import app.dapk.st.matrix.message.internal.ImageContentReader
import app.dapk.st.matrix.room.RoomService
import app.dapk.st.matrix.sync.RoomStore
import app.dapk.st.matrix.sync.SyncService
import java.time.Clock
class MessengerModule(
private val chatEngine: ChatEngine,
private val messageService: MessageService,
private val clock: Clock,
private val context: Context,
private val base64: Base64,
private val imageMetaReader: ImageContentReader,
private val messageOptionsStore: MessageOptionsStore,
) : ProvidableModule {
internal fun messengerViewModel(): MessengerViewModel {
return MessengerViewModel(
chatEngine,
messageService,
LocalIdFactory(),
imageMetaReader,
messageOptionsStore,
clock
)
}

View File

@ -6,9 +6,8 @@ import app.dapk.st.core.extensions.takeIfContent
import app.dapk.st.domain.application.message.MessageOptionsStore
import app.dapk.st.engine.ChatEngine
import app.dapk.st.engine.RoomEvent
import app.dapk.st.engine.SendMessage
import app.dapk.st.matrix.common.RoomId
import app.dapk.st.matrix.message.MessageService
import app.dapk.st.matrix.message.internal.ImageContentReader
import app.dapk.st.navigator.MessageAttachment
import app.dapk.st.viewmodel.DapkViewModel
import app.dapk.st.viewmodel.MutableStateFactory
@ -17,15 +16,10 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import java.time.Clock
internal class MessengerViewModel(
private val chatEngine: ChatEngine,
private val messageService: MessageService,
private val localIdFactory: LocalIdFactory,
private val imageContentReader: ImageContentReader,
private val messageOptionsStore: MessageOptionsStore,
private val clock: Clock,
factory: MutableStateFactory<MessengerScreenState> = defaultStateFactory(),
) : DapkViewModel<MessengerScreenState, MessengerEvent>(
initialState = MessengerScreenState(
@ -83,6 +77,7 @@ internal class MessengerViewModel(
}
}
private fun sendMessage() {
when (val composerState = state.composerState) {
is ComposerState.Text -> {
@ -92,27 +87,23 @@ internal class MessengerViewModel(
state.roomState.takeIfContent()?.let { content ->
val roomState = content.roomState
viewModelScope.launch {
messageService.scheduleMessage(
MessageService.Message.TextMessage(
MessageService.Message.Content.TextContent(body = copy.value),
roomId = roomState.roomOverview.roomId,
sendEncrypted = roomState.roomOverview.isEncrypted,
localId = localIdFactory.create(),
timestampUtc = clock.millis(),
chatEngine.send(
message = SendMessage.TextMessage(
content = copy.value,
reply = copy.reply?.let {
MessageService.Message.TextMessage.Reply(
SendMessage.TextMessage.Reply(
author = it.author,
originalMessage = when (it) {
is RoomEvent.Image -> TODO()
is RoomEvent.Reply -> TODO()
is RoomEvent.Message -> it.content
},
replyContent = copy.value,
eventId = it.eventId,
timestampUtc = it.utcTimestamp,
)
}
)
),
room = roomState.roomOverview,
)
}
}
@ -125,26 +116,7 @@ internal class MessengerViewModel(
state.roomState.takeIfContent()?.let { content ->
val roomState = content.roomState
viewModelScope.launch {
val imageUri = copy.values.first().uri.value
val meta = imageContentReader.meta(imageUri)
messageService.scheduleMessage(
MessageService.Message.ImageMessage(
MessageService.Message.Content.ImageContent(
uri = imageUri, MessageService.Message.Content.ImageContent.Meta(
height = meta.height,
width = meta.width,
size = meta.size,
fileName = meta.fileName,
mimeType = meta.mimeType,
)
),
roomId = roomState.roomOverview.roomId,
sendEncrypted = roomState.roomOverview.isEncrypted,
localId = localIdFactory.create(),
timestampUtc = clock.millis(),
)
)
chatEngine.send(SendMessage.ImageMessage(uri = copy.values.first().uri.value), roomState.roomOverview)
}
}

View File

@ -37,6 +37,7 @@ class MatrixEngine internal constructor(
private val directoryUseCase: Lazy<DirectoryUseCase>,
private val matrix: Lazy<MatrixClient>,
private val timelineUseCase: Lazy<ReadMarkingTimeline>,
private val sendMessageUseCase: Lazy<SendMessageUseCase>,
) : ChatEngine {
override fun directory() = directoryUseCase.value.state()
@ -67,6 +68,10 @@ class MatrixEngine internal constructor(
}
}
override suspend fun send(message: SendMessage, room: RoomOverview) {
sendMessageUseCase.value.send(message, room)
}
class Factory {
fun create(
@ -128,7 +133,12 @@ class MatrixEngine internal constructor(
ReadMarkingTimeline(roomStore, credentialsStore, timeline, matrix.roomService())
}
return MatrixEngine(directoryUseCase, lazyMatrix, timelineUseCase)
val sendMessageUseCase = unsafeLazy {
val matrix = lazyMatrix.value
SendMessageUseCase(matrix.messageService(), LocalIdFactory(), imageContentReader, Clock.systemUTC())
}
return MatrixEngine(directoryUseCase, lazyMatrix, timelineUseCase, sendMessageUseCase)
}

View File

@ -0,0 +1,60 @@
package app.dapk.st.engine
import app.dapk.st.matrix.message.MessageService
import app.dapk.st.matrix.message.internal.ImageContentReader
import java.time.Clock
internal class SendMessageUseCase(
private val messageService: MessageService,
private val localIdFactory: LocalIdFactory,
private val imageContentReader: ImageContentReader,
private val clock: Clock,
) {
suspend fun send(message: SendMessage, room: RoomOverview) {
when (message) {
is SendMessage.ImageMessage -> createImageMessage(message, room)
is SendMessage.TextMessage -> messageService.scheduleMessage(createTextMessage(message, room))
}
}
private suspend fun createImageMessage(message: SendMessage.ImageMessage, room: RoomOverview) {
val meta = imageContentReader.meta(message.uri)
messageService.scheduleMessage(
MessageService.Message.ImageMessage(
MessageService.Message.Content.ImageContent(
uri = message.uri,
MessageService.Message.Content.ImageContent.Meta(
height = meta.height,
width = meta.width,
size = meta.size,
fileName = meta.fileName,
mimeType = meta.mimeType,
)
),
roomId = room.roomId,
sendEncrypted = room.isEncrypted,
localId = localIdFactory.create(),
timestampUtc = clock.millis(),
)
)
}
private fun createTextMessage(message: SendMessage.TextMessage, room: RoomOverview) = MessageService.Message.TextMessage(
content = MessageService.Message.Content.TextContent(message.content),
roomId = room.roomId,
sendEncrypted = room.isEncrypted,
localId = localIdFactory.create(),
timestampUtc = clock.millis(),
reply = message.reply?.let {
MessageService.Message.TextMessage.Reply(
author = it.author,
originalMessage = it.originalMessage,
replyContent = message.content,
eventId = it.eventId,
timestampUtc = it.timestampUtc,
)
}
)
}