Private
Public Access
1
0

Update send message to account for guid returned by servers

This commit is contained in:
2023-12-10 18:30:56 -08:00
parent 6375900710
commit a8886279c6
7 changed files with 52 additions and 36 deletions

1
.idea/misc.xml generated
View File

@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="jbr-17" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">

View File

@@ -36,8 +36,13 @@ class MessageListViewModel @Inject constructor(
} }
} }
private data class OutgoingMessage(
val requestGuid: String,
val message: Message,
)
private var conversation: Conversation? = null private var conversation: Conversation? = null
private val pendingMessages: MutableStateFlow<List<Message>> = MutableStateFlow(listOf()) private val pendingMessages: MutableStateFlow<List<OutgoingMessage>> = MutableStateFlow(listOf())
init { init {
viewModelScope.launch { viewModelScope.launch {
@@ -45,14 +50,14 @@ class MessageListViewModel @Inject constructor(
// By now, the repository should've committed this to the store. // By now, the repository should've committed this to the store.
repository.messageDeliveredChannel.collectLatest { event -> repository.messageDeliveredChannel.collectLatest { event ->
pendingMessages.value = pendingMessages.value =
pendingMessages.value.filter { it.guid != event.message.guid } pendingMessages.value.filter { it.requestGuid != event.requestGuid }
} }
} }
} }
val messages: Flow<List<Message>> val messages: Flow<List<Message>>
get() = repository.messagesChanged(conversation!!) get() = repository.messagesChanged(conversation!!)
.combine(pendingMessages) { a, b -> a.union(b) } .combine(pendingMessages) { a, b -> a.union(b.map { it.message }) }
.map { messages -> .map { messages ->
messages messages
.sortedBy { it.date } .sortedBy { it.date }
@@ -70,12 +75,12 @@ class MessageListViewModel @Inject constructor(
conversation = conversation!!, conversation = conversation!!,
) )
pendingMessages.value = pendingMessages.value + listOf(outgoingMessage) val outgoingGUID = repository.enqueueOutgoingMessage(outgoingMessage, conversation!!)
repository.enqueueOutgoingMessage(outgoingMessage, conversation!!) pendingMessages.value = pendingMessages.value + listOf(OutgoingMessage(outgoingGUID, outgoingMessage))
} }
fun isPendingMessage(message: Message): Boolean { fun isPendingMessage(message: Message): Boolean {
return pendingMessages.value.contains(message) return pendingMessages.value.any { it.message.guid == message.guid }
} }
fun synchronize() = viewModelScope.launch { fun synchronize() = viewModelScope.launch {

View File

@@ -1,5 +1,6 @@
package net.buzzert.kordophone.backend.db package net.buzzert.kordophone.backend.db
import android.util.Log
import io.realm.kotlin.MutableRealm import io.realm.kotlin.MutableRealm
import io.realm.kotlin.Realm import io.realm.kotlin.Realm
import io.realm.kotlin.RealmConfiguration import io.realm.kotlin.RealmConfiguration
@@ -15,6 +16,7 @@ import net.buzzert.kordophone.backend.db.model.toDatabaseConversation
import net.buzzert.kordophone.backend.db.model.toDatabaseMessage import net.buzzert.kordophone.backend.db.model.toDatabaseMessage
import net.buzzert.kordophone.backend.db.model.toRealmInstant import net.buzzert.kordophone.backend.db.model.toRealmInstant
import net.buzzert.kordophone.backend.model.GUID import net.buzzert.kordophone.backend.model.GUID
import net.buzzert.kordophone.backend.server.REPO_LOG
import java.lang.IllegalArgumentException import java.lang.IllegalArgumentException
import net.buzzert.kordophone.backend.model.Conversation as ModelConversation import net.buzzert.kordophone.backend.model.Conversation as ModelConversation
import net.buzzert.kordophone.backend.model.Message as ModelMessage import net.buzzert.kordophone.backend.model.Message as ModelMessage
@@ -41,7 +43,15 @@ class CachedChatDatabase (private val realmConfig: RealmConfiguration) {
} }
} }
private val realm = Realm.open(realmConfig) private val realm = runCatching {
Realm.open(realmConfig)
}.recover {
// We're just a caching layer, so in the event of a migration error, just delete and start over.
Log.d(REPO_LOG, "Error opening (${it.message}). Recovering by deleting database.")
Realm.deleteRealm(realmConfig)
return@recover Realm.open(realmConfig)
}.getOrThrow()
// Flow for watching changes to the database // Flow for watching changes to the database
val conversationChanges: Flow<List<ModelConversation>> val conversationChanges: Flow<List<ModelConversation>>
@@ -122,16 +132,6 @@ class CachedChatDatabase (private val realmConfig: RealmConfiguration) {
} }
} }
fun clearOutgoingMessages() {
realm.query(Message::class, "isOutgoing == true").find().forEach { message ->
realm.writeBlocking {
findLatest(message)?.let {
delete(it)
}
}
}
}
fun fetchMessages(conversation: ModelConversation): List<ModelMessage> { fun fetchMessages(conversation: ModelConversation): List<ModelMessage> {
val dbConversation = getConversationByGuid(conversation.guid) val dbConversation = getConversationByGuid(conversation.guid)
return dbConversation.messages.map { it.toMessage(dbConversation.toConversation()) } return dbConversation.messages.map { it.toMessage(dbConversation.toConversation()) }

View File

@@ -22,7 +22,6 @@ open class Message(
var date: RealmInstant, var date: RealmInstant,
var conversationGUID: GUID, var conversationGUID: GUID,
var isOutgoing: Boolean,
): RealmObject ): RealmObject
{ {
constructor() : this( constructor() : this(
@@ -31,7 +30,6 @@ open class Message(
sender = null, sender = null,
date = RealmInstant.now(), date = RealmInstant.now(),
conversationGUID = ObjectId().toString(), conversationGUID = ObjectId().toString(),
isOutgoing = false,
) )
fun toMessage(parentConversation: ModelConversation): ModelMessage { fun toMessage(parentConversation: ModelConversation): ModelMessage {
@@ -53,6 +51,5 @@ fun ModelMessage.toDatabaseMessage(outgoing: Boolean = false): Message {
sender = from.sender sender = from.sender
date = from.date.toInstant().toRealmInstant() date = from.date.toInstant().toRealmInstant()
conversationGUID = from.conversation.guid conversationGUID = from.conversation.guid
isOutgoing = outgoing
} }
} }

View File

@@ -5,7 +5,7 @@ import net.buzzert.kordophone.backend.model.GUID
import net.buzzert.kordophone.backend.model.Message import net.buzzert.kordophone.backend.model.Message
data class MessageDeliveredEvent( data class MessageDeliveredEvent(
val guid: GUID,
val message: Message, val message: Message,
val conversation: Conversation, val conversation: Conversation,
val requestGuid: GUID,
) )

View File

@@ -25,6 +25,11 @@ data class SendMessageRequest(
val transferGUIDs: List<String>?, val transferGUIDs: List<String>?,
) )
data class SendMessageResponse(
@SerializedName("guid")
val sentMessageGUID: String,
)
interface APIInterface { interface APIInterface {
@GET("/version") @GET("/version")
suspend fun getVersion(): ResponseBody suspend fun getVersion(): ResponseBody
@@ -41,7 +46,7 @@ interface APIInterface {
): Response<List<Message>> ): Response<List<Message>>
@POST("/sendMessage") @POST("/sendMessage")
suspend fun sendMessage(@Body request: SendMessageRequest): Response<Void> suspend fun sendMessage(@Body request: SendMessageRequest): Response<SendMessageResponse>
} }
class ResponseDecodeError(val response: ResponseBody): Exception() class ResponseDecodeError(val response: ResponseBody): Exception()

View File

@@ -65,7 +65,7 @@ class ChatRepository(
private data class OutgoingMessageInfo ( private data class OutgoingMessageInfo (
val message: Message, val message: Message,
val conversation: Conversation, val conversation: Conversation,
val guid: GUID, val requestGuid: GUID,
) )
private val apiInterface = apiClient.getAPIInterface() private val apiInterface = apiClient.getAPIInterface()
@@ -133,12 +133,6 @@ class ChatRepository(
val serverConversations = fetchConversations() val serverConversations = fetchConversations()
database.updateConversations(serverConversations) database.updateConversations(serverConversations)
// Delete outgoing conversations
// This is an unfortunate limitation in that we don't know what outgoing GUIDs are going to
// be before we send them.
// TODO: Keep this in mind when syncing messages after a certain GUID. The outgoing GUIDs are fake.
database.clearOutgoingMessages()
// Sync top N number of conversations' message content // Sync top N number of conversations' message content
Log.d(REPO_LOG, "Synchronizing messages") Log.d(REPO_LOG, "Synchronizing messages")
val sortedConversations = conversations.sortedBy { it.date }.reversed() val sortedConversations = conversations.sortedBy { it.date }.reversed()
@@ -198,23 +192,39 @@ class ChatRepository(
while (true) { while (true) {
val outgoingMessageRequest = outgoingMessageQueue.poll()?.let { val outgoingMessageRequest = outgoingMessageQueue.poll()?.let {
runBlocking { runBlocking {
val guid = it.guid val outgoingMessage = it.message
val message = it.message
val conversation = it.conversation val conversation = it.conversation
val requestGuid = it.requestGuid
Log.d(REPO_LOG, "Sending message to $conversation: $message") Log.d(REPO_LOG, "Sending message to $conversation: $outgoingMessage")
val result = apiInterface.sendMessage( val result = apiInterface.sendMessage(
SendMessageRequest( SendMessageRequest(
conversationGUID = conversation.guid, conversationGUID = conversation.guid,
body = message.text, body = outgoingMessage.text,
transferGUIDs = null, transferGUIDs = null,
) )
) )
if (result.isSuccessful) { if (result.isSuccessful) {
Log.d(REPO_LOG, "Successfully sent message.") val messageGuid = result.body()?.sentMessageGUID ?: outgoingMessage.guid
_messageDeliveredChannel.emit(MessageDeliveredEvent(guid, message, conversation)) Log.d(REPO_LOG, "Successfully sent message: $messageGuid")
val newMessage = Message(
guid = messageGuid,
text = outgoingMessage.text,
sender = null,
conversation = it.conversation,
date = outgoingMessage.date
)
_messageDeliveredChannel.emit(
MessageDeliveredEvent(
newMessage,
conversation,
requestGuid
)
)
} else { } else {
Log.e(REPO_LOG, "Error sending message. Enqueuing for retry.") Log.e(REPO_LOG, "Error sending message. Enqueuing for retry.")
outgoingMessageQueue.add(it) outgoingMessageQueue.add(it)