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">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<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 val pendingMessages: MutableStateFlow<List<Message>> = MutableStateFlow(listOf())
private val pendingMessages: MutableStateFlow<List<OutgoingMessage>> = MutableStateFlow(listOf())
init {
viewModelScope.launch {
@@ -45,14 +50,14 @@ class MessageListViewModel @Inject constructor(
// By now, the repository should've committed this to the store.
repository.messageDeliveredChannel.collectLatest { event ->
pendingMessages.value =
pendingMessages.value.filter { it.guid != event.message.guid }
pendingMessages.value.filter { it.requestGuid != event.requestGuid }
}
}
}
val messages: Flow<List<Message>>
get() = repository.messagesChanged(conversation!!)
.combine(pendingMessages) { a, b -> a.union(b) }
.combine(pendingMessages) { a, b -> a.union(b.map { it.message }) }
.map { messages ->
messages
.sortedBy { it.date }
@@ -70,12 +75,12 @@ class MessageListViewModel @Inject constructor(
conversation = conversation!!,
)
pendingMessages.value = pendingMessages.value + listOf(outgoingMessage)
repository.enqueueOutgoingMessage(outgoingMessage, conversation!!)
val outgoingGUID = repository.enqueueOutgoingMessage(outgoingMessage, conversation!!)
pendingMessages.value = pendingMessages.value + listOf(OutgoingMessage(outgoingGUID, outgoingMessage))
}
fun isPendingMessage(message: Message): Boolean {
return pendingMessages.value.contains(message)
return pendingMessages.value.any { it.message.guid == message.guid }
}
fun synchronize() = viewModelScope.launch {

View File

@@ -1,5 +1,6 @@
package net.buzzert.kordophone.backend.db
import android.util.Log
import io.realm.kotlin.MutableRealm
import io.realm.kotlin.Realm
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.toRealmInstant
import net.buzzert.kordophone.backend.model.GUID
import net.buzzert.kordophone.backend.server.REPO_LOG
import java.lang.IllegalArgumentException
import net.buzzert.kordophone.backend.model.Conversation as ModelConversation
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
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> {
val dbConversation = getConversationByGuid(conversation.guid)
return dbConversation.messages.map { it.toMessage(dbConversation.toConversation()) }

View File

@@ -22,7 +22,6 @@ open class Message(
var date: RealmInstant,
var conversationGUID: GUID,
var isOutgoing: Boolean,
): RealmObject
{
constructor() : this(
@@ -31,7 +30,6 @@ open class Message(
sender = null,
date = RealmInstant.now(),
conversationGUID = ObjectId().toString(),
isOutgoing = false,
)
fun toMessage(parentConversation: ModelConversation): ModelMessage {
@@ -53,6 +51,5 @@ fun ModelMessage.toDatabaseMessage(outgoing: Boolean = false): Message {
sender = from.sender
date = from.date.toInstant().toRealmInstant()
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
data class MessageDeliveredEvent(
val guid: GUID,
val message: Message,
val conversation: Conversation,
val requestGuid: GUID,
)

View File

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

View File

@@ -65,7 +65,7 @@ class ChatRepository(
private data class OutgoingMessageInfo (
val message: Message,
val conversation: Conversation,
val guid: GUID,
val requestGuid: GUID,
)
private val apiInterface = apiClient.getAPIInterface()
@@ -133,12 +133,6 @@ class ChatRepository(
val serverConversations = fetchConversations()
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
Log.d(REPO_LOG, "Synchronizing messages")
val sortedConversations = conversations.sortedBy { it.date }.reversed()
@@ -198,23 +192,39 @@ class ChatRepository(
while (true) {
val outgoingMessageRequest = outgoingMessageQueue.poll()?.let {
runBlocking {
val guid = it.guid
val message = it.message
val outgoingMessage = it.message
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(
SendMessageRequest(
conversationGUID = conversation.guid,
body = message.text,
body = outgoingMessage.text,
transferGUIDs = null,
)
)
if (result.isSuccessful) {
Log.d(REPO_LOG, "Successfully sent message.")
_messageDeliveredChannel.emit(MessageDeliveredEvent(guid, message, conversation))
val messageGuid = result.body()?.sentMessageGUID ?: outgoingMessage.guid
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 {
Log.e(REPO_LOG, "Error sending message. Enqueuing for retry.")
outgoingMessageQueue.add(it)