Try only synchronizing messages after last GUID
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -97,6 +97,7 @@ class CachedChatDatabase (private val realmConfig: RealmConfiguration) {
|
||||
date = conversation.date
|
||||
unreadCount = conversation.unreadCount
|
||||
lastMessagePreview = conversation.lastMessagePreview
|
||||
lastMessageGUID = conversation.lastMessageGUID
|
||||
}
|
||||
} catch (e: NoSuchElementException) {
|
||||
// Conversation does not exist. Copy it to the realm.
|
||||
@@ -121,6 +122,10 @@ class CachedChatDatabase (private val realmConfig: RealmConfiguration) {
|
||||
}
|
||||
|
||||
fun writeMessages(messages: List<ModelMessage>, conversation: ModelConversation, outgoing: Boolean = false) {
|
||||
if (messages.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val dbConversation = getManagedConversationByGuid(conversation.guid)
|
||||
realm.writeBlocking {
|
||||
messages
|
||||
@@ -128,8 +133,17 @@ class CachedChatDatabase (private val realmConfig: RealmConfiguration) {
|
||||
.map { copyToRealm(it, updatePolicy = UpdatePolicy.ALL) }
|
||||
|
||||
findLatest(dbConversation)?.let {
|
||||
it.lastMessagePreview = messages.last().displayText
|
||||
it.date = messages.last().date.toInstant().toRealmInstant()
|
||||
val lastMessage = messages.maxByOrNull { it.date }!!
|
||||
|
||||
val lastMessageDate = lastMessage.date.toInstant().toRealmInstant()
|
||||
if (lastMessageDate > it.date) {
|
||||
it.lastMessageGUID = lastMessage.guid
|
||||
it.lastMessagePreview = lastMessage.displayText
|
||||
|
||||
// This will cause sort order to change. I think this ends
|
||||
// up getting updated whenever we get conversation changes anyway.
|
||||
// it.date = lastMessageDate
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
package net.buzzert.kordophone.backend.db.model
|
||||
|
||||
import io.realm.kotlin.Realm
|
||||
import io.realm.kotlin.ext.realmListOf
|
||||
import io.realm.kotlin.ext.realmSetOf
|
||||
import io.realm.kotlin.ext.toRealmList
|
||||
import io.realm.kotlin.types.RealmInstant
|
||||
import io.realm.kotlin.types.RealmList
|
||||
import io.realm.kotlin.types.RealmObject
|
||||
import io.realm.kotlin.types.RealmSet
|
||||
import io.realm.kotlin.types.annotations.PrimaryKey
|
||||
import net.buzzert.kordophone.backend.model.GUID
|
||||
import org.mongodb.kbson.ObjectId
|
||||
import java.time.Instant
|
||||
import net.buzzert.kordophone.backend.model.Conversation as ModelConversation
|
||||
import java.util.Date
|
||||
|
||||
@@ -24,6 +20,7 @@ open class Conversation(
|
||||
var date: RealmInstant,
|
||||
var unreadCount: Int,
|
||||
|
||||
var lastMessageGUID: String?,
|
||||
var lastMessagePreview: String?,
|
||||
): RealmObject
|
||||
{
|
||||
@@ -35,10 +32,11 @@ open class Conversation(
|
||||
date = RealmInstant.now(),
|
||||
unreadCount = 0,
|
||||
lastMessagePreview = null,
|
||||
lastMessageGUID = null,
|
||||
)
|
||||
|
||||
fun toConversation(): ModelConversation {
|
||||
val conversation = ModelConversation(
|
||||
return ModelConversation(
|
||||
displayName = displayName,
|
||||
participants = participants.toList(),
|
||||
date = Date.from(date.toInstant()),
|
||||
@@ -46,9 +44,8 @@ open class Conversation(
|
||||
guid = guid,
|
||||
lastMessagePreview = lastMessagePreview,
|
||||
lastMessage = null,
|
||||
lastFetchedMessageGUID = lastMessageGUID
|
||||
)
|
||||
|
||||
return conversation
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
@@ -57,6 +54,10 @@ open class Conversation(
|
||||
val o = other as Conversation
|
||||
return guid == o.guid
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return guid.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
fun ModelConversation.toDatabaseConversation(): Conversation {
|
||||
@@ -67,6 +68,7 @@ fun ModelConversation.toDatabaseConversation(): Conversation {
|
||||
date = from.date.toInstant().toRealmInstant()
|
||||
unreadCount = from.unreadCount
|
||||
lastMessagePreview = from.lastMessagePreview
|
||||
lastMessageGUID = from.lastFetchedMessageGUID
|
||||
guid = from.guid
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ data class Conversation(
|
||||
|
||||
@SerializedName("lastMessage")
|
||||
var lastMessage: Message?,
|
||||
|
||||
var lastFetchedMessageGUID: String?,
|
||||
) {
|
||||
companion object {
|
||||
fun generate(): Conversation {
|
||||
@@ -38,6 +40,7 @@ data class Conversation(
|
||||
unreadCount = 0,
|
||||
lastMessagePreview = null,
|
||||
lastMessage = null,
|
||||
lastFetchedMessageGUID = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -59,7 +62,7 @@ data class Conversation(
|
||||
participants == o.participants &&
|
||||
displayName == o.displayName &&
|
||||
unreadCount == o.unreadCount &&
|
||||
lastMessagePreview == o.lastMessagePreview
|
||||
lastFetchedMessageGUID == o.lastFetchedMessageGUID
|
||||
)
|
||||
}
|
||||
|
||||
@@ -69,8 +72,8 @@ data class Conversation(
|
||||
result = 31 * result + participants.hashCode()
|
||||
result = 31 * result + (displayName?.hashCode() ?: 0)
|
||||
result = 31 * result + unreadCount
|
||||
result = 31 * result + (lastMessagePreview?.hashCode() ?: 0)
|
||||
result = 31 * result + (lastMessage?.hashCode() ?: 0)
|
||||
result = 31 * result + (lastFetchedMessageGUID?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,9 +182,7 @@ class ChatRepository(
|
||||
}
|
||||
|
||||
suspend fun synchronizeConversation(conversation: Conversation, limit: Int = 15) = withErrorChannelHandling {
|
||||
// TODO: Should only fetch messages after the last GUID we know about.
|
||||
// But keep in mind that outgoing message GUIDs are fake...
|
||||
val messages = fetchMessages(conversation, limit = limit)
|
||||
val messages = fetchMessages(conversation, limit = limit, afterGUID = conversation.lastFetchedMessageGUID)
|
||||
database.writeMessages(messages, conversation)
|
||||
}
|
||||
|
||||
@@ -233,10 +231,10 @@ class ChatRepository(
|
||||
private suspend fun fetchMessages(
|
||||
conversation: Conversation,
|
||||
limit: Int? = null,
|
||||
before: Message? = null,
|
||||
after: Message? = null,
|
||||
beforeGUID: String? = null,
|
||||
afterGUID: String? = null,
|
||||
): List<Message> {
|
||||
return apiInterface.getMessages(conversation.guid, limit, before?.guid, after?.guid)
|
||||
return apiInterface.getMessages(conversation.guid, limit, beforeGUID, afterGUID)
|
||||
.bodyOnSuccessOrThrow()
|
||||
.onEach { it.conversation = conversation }
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import net.buzzert.kordophone.backend.db.CachedChatDatabase
|
||||
import net.buzzert.kordophone.backend.model.Message
|
||||
import net.buzzert.kordophone.backend.model.OutgoingMessage
|
||||
import net.buzzert.kordophone.backend.server.APIClient
|
||||
import net.buzzert.kordophone.backend.server.APIInterface
|
||||
import net.buzzert.kordophone.backend.server.Authentication
|
||||
@@ -85,13 +86,18 @@ class BackendTests {
|
||||
val (repository, mockServer) = mockRepository()
|
||||
|
||||
val conversation = mockServer.addTestConversations(1).first()
|
||||
val outgoingMessage = MockServer.generateMessage(conversation)
|
||||
val generatedMessage = MockServer.generateMessage(conversation)
|
||||
val outgoingMessage = OutgoingMessage(
|
||||
body = generatedMessage.text,
|
||||
conversation = conversation,
|
||||
attachmentUris = setOf(),
|
||||
attachmentDataSource = { null },
|
||||
)
|
||||
|
||||
val guid = repository.enqueueOutgoingMessage(outgoingMessage, conversation)
|
||||
repository.enqueueOutgoingMessage(outgoingMessage)
|
||||
|
||||
val event = repository.messageDeliveredChannel.first()
|
||||
assertEquals(event.requestGuid, guid)
|
||||
assertEquals(event.message.text, outgoingMessage.text)
|
||||
assertEquals(event.message.text, outgoingMessage.body)
|
||||
|
||||
repository.close()
|
||||
}
|
||||
|
||||
@@ -18,18 +18,19 @@ import net.buzzert.kordophone.backend.server.AuthenticationRequest
|
||||
import net.buzzert.kordophone.backend.server.AuthenticationResponse
|
||||
import net.buzzert.kordophone.backend.server.SendMessageRequest
|
||||
import net.buzzert.kordophone.backend.server.SendMessageResponse
|
||||
import net.buzzert.kordophone.backend.server.UploadAttachmentResponse
|
||||
import net.buzzert.kordophone.backend.server.authenticatedWebSocketURL
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.ResponseBody
|
||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||
import okhttp3.WebSocket
|
||||
import okhttp3.WebSocketListener
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import retrofit2.Call
|
||||
import retrofit2.Response
|
||||
import java.util.Date
|
||||
import java.util.UUID
|
||||
@@ -66,7 +67,8 @@ class MockServer {
|
||||
unreadCount = 0,
|
||||
lastMessagePreview = null,
|
||||
lastMessage = null,
|
||||
guid = UUID.randomUUID().toString()
|
||||
guid = UUID.randomUUID().toString(),
|
||||
lastFetchedMessageGUID = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -166,6 +168,8 @@ class MockServerClient(private val server: MockServer): APIClient, WebSocketList
|
||||
private var updateWatchJob: Job? = null
|
||||
private val gson: Gson = Gson()
|
||||
|
||||
override val isConfigured: Boolean = true
|
||||
|
||||
override fun getAPIInterface(): APIInterface {
|
||||
return MockServerInterface(server)
|
||||
}
|
||||
@@ -268,6 +272,13 @@ class MockServerInterface(private val server: MockServer): APIInterface {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun uploadAttachment(
|
||||
filename: String,
|
||||
body: RequestBody
|
||||
): Response<UploadAttachmentResponse> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun authenticate(request: AuthenticationRequest): Response<AuthenticationResponse> {
|
||||
// Anything goes!
|
||||
val response = AuthenticationResponse(
|
||||
|
||||
Reference in New Issue
Block a user