Update send message to account for guid returned by servers
This commit is contained in:
1
.idea/misc.xml
generated
1
.idea/misc.xml
generated
@@ -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">
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()) }
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user