forked from github/wulkanowy-mirror
Update readBy and unreadBy fields during message list fetch (#1452)
This commit is contained in:
parent
2f43b6e552
commit
57d11e825b
@ -4,10 +4,12 @@ import android.content.Context
|
||||
import com.squareup.moshi.Moshi
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.Resource
|
||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
||||
import io.github.wulkanowy.data.db.dao.MessagesDao
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
||||
import io.github.wulkanowy.data.db.entities.Recipient
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
@ -48,22 +50,54 @@ class MessageRepository @Inject constructor(
|
||||
private val cacheKey = "message"
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun getMessages(student: Student, semester: Semester, folder: MessageFolder, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
|
||||
fun getMessages(
|
||||
student: Student, semester: Semester,
|
||||
folder: MessageFolder, forceRefresh: Boolean, notify: Boolean = false
|
||||
): Flow<Resource<List<Message>>> = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, student, folder)) },
|
||||
shouldFetch = {
|
||||
it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(
|
||||
getRefreshKey(cacheKey, student, folder)
|
||||
)
|
||||
},
|
||||
query = { messagesDb.loadAll(student.id.toInt(), folder.id) },
|
||||
fetch = { sdk.init(student).getMessages(Folder.valueOf(folder.name), now().minusMonths(3), now()).mapToEntities(student) },
|
||||
fetch = {
|
||||
sdk.init(student).getMessages(Folder.valueOf(folder.name), now().minusMonths(3), now())
|
||||
.mapToEntities(student)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
messagesDb.deleteAll(old uniqueSubtract new)
|
||||
messagesDb.insertAll((new uniqueSubtract old).onEach {
|
||||
it.isNotified = !notify
|
||||
})
|
||||
messagesDb.updateAll(getMessagesWithReadByChange(old, new, !notify))
|
||||
|
||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student, folder))
|
||||
}
|
||||
)
|
||||
|
||||
fun getMessage(student: Student, message: Message, markAsRead: Boolean = false) = networkBoundResource(
|
||||
private fun getMessagesWithReadByChange(
|
||||
old: List<Message>, new: List<Message>,
|
||||
setNotified: Boolean
|
||||
): List<Message> {
|
||||
val oldMeta = old.map { Triple(it, it.readBy, it.unreadBy) }
|
||||
val newMeta = new.map { Triple(it, it.readBy, it.unreadBy) }
|
||||
|
||||
val updatedItems = newMeta uniqueSubtract oldMeta
|
||||
|
||||
return updatedItems.map {
|
||||
val oldItem = old.find { item -> item.messageId == it.first.messageId }
|
||||
it.first.apply {
|
||||
id = oldItem?.id ?: 0
|
||||
isNotified = oldItem?.isNotified ?: setNotified
|
||||
content = oldItem?.content.orEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getMessage(
|
||||
student: Student, message: Message, markAsRead: Boolean = false
|
||||
): Flow<Resource<MessageWithAttachment?>> = networkBoundResource(
|
||||
shouldFetch = {
|
||||
checkNotNull(it, { "This message no longer exist!" })
|
||||
Timber.d("Message content in db empty: ${it.message.content.isEmpty()}")
|
||||
@ -71,7 +105,12 @@ class MessageRepository @Inject constructor(
|
||||
},
|
||||
query = { messagesDb.loadMessageWithAttachment(student.id.toInt(), message.messageId) },
|
||||
fetch = {
|
||||
sdk.init(student).getMessageDetails(it!!.message.messageId, message.folderId, markAsRead, message.realId).let { details ->
|
||||
sdk.init(student).getMessageDetails(
|
||||
messageId = it!!.message.messageId,
|
||||
folderId = message.folderId,
|
||||
read = markAsRead,
|
||||
id = message.realId
|
||||
).let { details ->
|
||||
details.content to details.attachments.mapToEntities()
|
||||
}
|
||||
},
|
||||
@ -95,26 +134,34 @@ class MessageRepository @Inject constructor(
|
||||
return messagesDb.updateAll(messages)
|
||||
}
|
||||
|
||||
suspend fun sendMessage(student: Student, subject: String, content: String, recipients: List<Recipient>): SentMessage {
|
||||
return sdk.init(student).sendMessage(
|
||||
subject = subject,
|
||||
content = content,
|
||||
recipients = recipients.mapFromEntities()
|
||||
)
|
||||
}
|
||||
suspend fun sendMessage(
|
||||
student: Student, subject: String, content: String,
|
||||
recipients: List<Recipient>
|
||||
): SentMessage = sdk.init(student).sendMessage(
|
||||
subject = subject,
|
||||
content = content,
|
||||
recipients = recipients.mapFromEntities()
|
||||
)
|
||||
|
||||
suspend fun deleteMessage(student: Student, message: Message) {
|
||||
val isDeleted = sdk.init(student).deleteMessages(listOf(message.messageId), message.folderId)
|
||||
val isDeleted = sdk.init(student).deleteMessages(
|
||||
messages = listOf(message.messageId), message.folderId
|
||||
)
|
||||
|
||||
if (message.folderId != MessageFolder.TRASHED.id) {
|
||||
if (isDeleted) messagesDb.updateAll(listOf(message.copy(folderId = MessageFolder.TRASHED.id).apply {
|
||||
if (message.folderId != MessageFolder.TRASHED.id && isDeleted) {
|
||||
val deletedMessage = message.copy(folderId = MessageFolder.TRASHED.id).apply {
|
||||
id = message.id
|
||||
content = message.content
|
||||
}))
|
||||
}
|
||||
messagesDb.updateAll(listOf(deletedMessage))
|
||||
} else messagesDb.deleteAll(listOf(message))
|
||||
}
|
||||
|
||||
var draftMessage: MessageDraft?
|
||||
get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_send_draft))?.let { MessageDraftJsonAdapter(moshi).fromJson(it) }
|
||||
set(value) = sharedPrefProvider.putString(context.getString(R.string.pref_key_message_send_draft), value?.let { MessageDraftJsonAdapter(moshi).toJson(it) })
|
||||
get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_send_draft))
|
||||
?.let { MessageDraftJsonAdapter(moshi).fromJson(it) }
|
||||
set(value) = sharedPrefProvider.putString(
|
||||
context.getString(R.string.pref_key_message_send_draft),
|
||||
value?.let { MessageDraftJsonAdapter(moshi).toJson(it) }
|
||||
)
|
||||
}
|
||||
|
@ -8,19 +8,25 @@ import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
||||
import io.github.wulkanowy.data.db.dao.MessagesDao
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
||||
import io.github.wulkanowy.data.enums.MessageFolder
|
||||
import io.github.wulkanowy.getSemesterEntity
|
||||
import io.github.wulkanowy.getStudentEntity
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.sdk.pojo.Folder
|
||||
import io.github.wulkanowy.sdk.pojo.MessageDetails
|
||||
import io.github.wulkanowy.sdk.pojo.Sender
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.toFirstResult
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.Runs
|
||||
import io.mockk.checkEquals
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.impl.annotations.SpyK
|
||||
import io.mockk.just
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@ -29,6 +35,7 @@ import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.net.UnknownHostException
|
||||
import java.time.LocalDateTime
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class MessageRepositoryTest {
|
||||
|
||||
@ -52,7 +59,9 @@ class MessageRepositoryTest {
|
||||
|
||||
private val student = getStudentEntity()
|
||||
|
||||
private lateinit var messageRepository: MessageRepository
|
||||
private val semester = getSemesterEntity()
|
||||
|
||||
private lateinit var repository: MessageRepository
|
||||
|
||||
@MockK
|
||||
private lateinit var moshi: Moshi
|
||||
@ -62,15 +71,92 @@ class MessageRepositoryTest {
|
||||
MockKAnnotations.init(this)
|
||||
every { refreshHelper.isShouldBeRefreshed(any()) } returns false
|
||||
|
||||
messageRepository = MessageRepository(messageDb, messageAttachmentDao, sdk, context, refreshHelper, sharedPrefProvider, moshi)
|
||||
repository = MessageRepository(
|
||||
messagesDb = messageDb,
|
||||
messageAttachmentDao = messageAttachmentDao,
|
||||
sdk = sdk,
|
||||
context = context,
|
||||
refreshHelper = refreshHelper,
|
||||
sharedPrefProvider = sharedPrefProvider,
|
||||
moshi = moshi,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `get messages when read by values was changed on already read message`() = runBlocking {
|
||||
every { messageDb.loadAll(any(), any()) } returns flow {
|
||||
val dbMessage = getMessageEntity(3, "", false).apply {
|
||||
unreadBy = 10
|
||||
readBy = 5
|
||||
isNotified = true
|
||||
}
|
||||
emit(listOf(dbMessage))
|
||||
}
|
||||
coEvery { sdk.getMessages(Folder.RECEIVED, any(), any()) } returns listOf(
|
||||
getMessageDto(messageId = 3, content = "", unread = false).copy(
|
||||
unreadBy = 5,
|
||||
readBy = 10,
|
||||
)
|
||||
)
|
||||
coEvery { messageDb.deleteAll(any()) } just Runs
|
||||
coEvery { messageDb.insertAll(any()) } returns listOf()
|
||||
|
||||
repository.getMessages(
|
||||
student = student,
|
||||
semester = semester,
|
||||
folder = MessageFolder.RECEIVED,
|
||||
forceRefresh = true,
|
||||
notify = true, // all new messages will be marked as not notified
|
||||
).toFirstResult().data.orEmpty()
|
||||
|
||||
coVerify(exactly = 1) { messageDb.deleteAll(emptyList()) }
|
||||
coVerify(exactly = 1) { messageDb.insertAll(emptyList()) }
|
||||
coVerify(exactly = 1) {
|
||||
messageDb.updateAll(withArg {
|
||||
assertEquals(1, it.size)
|
||||
assertEquals(5, it.single().unreadBy)
|
||||
assertEquals(10, it.single().readBy)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `get messages when fetched completely new message without notify`() = runBlocking {
|
||||
every { messageDb.loadAll(any(), any()) } returns flowOf(emptyList())
|
||||
coEvery { sdk.getMessages(Folder.RECEIVED, any(), any()) } returns listOf(
|
||||
getMessageDto(messageId = 4, content = "Test", unread = true).copy(
|
||||
unreadBy = 5,
|
||||
readBy = 10,
|
||||
)
|
||||
)
|
||||
coEvery { messageDb.deleteAll(any()) } just Runs
|
||||
coEvery { messageDb.insertAll(any()) } returns listOf()
|
||||
|
||||
repository.getMessages(
|
||||
student = student,
|
||||
semester = semester,
|
||||
folder = MessageFolder.RECEIVED,
|
||||
forceRefresh = true,
|
||||
notify = false,
|
||||
).toFirstResult().data.orEmpty()
|
||||
|
||||
coVerify(exactly = 1) { messageDb.deleteAll(withArg { checkEquals(emptyList<Message>()) }) }
|
||||
coVerify {
|
||||
messageDb.insertAll(withArg {
|
||||
assertEquals(4, it.single().messageId)
|
||||
assertTrue(it.single().isNotified)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = NoSuchElementException::class)
|
||||
fun `throw error when message is not in the db`() {
|
||||
val testMessage = getMessageEntity(1, "", false)
|
||||
coEvery { messageDb.loadMessageWithAttachment(1, 1) } throws NoSuchElementException("No message in database")
|
||||
coEvery {
|
||||
messageDb.loadMessageWithAttachment(1, 1)
|
||||
} throws NoSuchElementException("No message in database")
|
||||
|
||||
runBlocking { messageRepository.getMessage(student, testMessage).toFirstResult() }
|
||||
runBlocking { repository.getMessage(student, testMessage).toFirstResult() }
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -78,9 +164,11 @@ class MessageRepositoryTest {
|
||||
val testMessage = getMessageEntity(123, "Test", false)
|
||||
val messageWithAttachment = MessageWithAttachment(testMessage, emptyList())
|
||||
|
||||
coEvery { messageDb.loadMessageWithAttachment(1, testMessage.messageId) } returns flowOf(messageWithAttachment)
|
||||
coEvery { messageDb.loadMessageWithAttachment(1, testMessage.messageId) } returns flowOf(
|
||||
messageWithAttachment
|
||||
)
|
||||
|
||||
val res = runBlocking { messageRepository.getMessage(student, testMessage).toFirstResult() }
|
||||
val res = runBlocking { repository.getMessage(student, testMessage).toFirstResult() }
|
||||
|
||||
assertEquals(null, res.error)
|
||||
assertEquals(Status.SUCCESS, res.status)
|
||||
@ -95,12 +183,24 @@ class MessageRepositoryTest {
|
||||
val mWa = MessageWithAttachment(testMessage, emptyList())
|
||||
val mWaWithContent = MessageWithAttachment(testMessageWithContent, emptyList())
|
||||
|
||||
coEvery { messageDb.loadMessageWithAttachment(1, testMessage.messageId) } returnsMany listOf(flowOf(mWa), flowOf(mWaWithContent))
|
||||
coEvery { sdk.getMessageDetails(testMessage.messageId, 1, false, testMessage.realId) } returns MessageDetails("Test", emptyList())
|
||||
coEvery {
|
||||
messageDb.loadMessageWithAttachment(
|
||||
1,
|
||||
testMessage.messageId
|
||||
)
|
||||
} returnsMany listOf(flowOf(mWa), flowOf(mWaWithContent))
|
||||
coEvery {
|
||||
sdk.getMessageDetails(
|
||||
messageId = testMessage.messageId,
|
||||
folderId = 1,
|
||||
read = false,
|
||||
id = testMessage.realId
|
||||
)
|
||||
} returns MessageDetails("Test", emptyList())
|
||||
coEvery { messageDb.updateAll(any()) } just Runs
|
||||
coEvery { messageAttachmentDao.insertAttachments(any()) } returns listOf(1)
|
||||
|
||||
val res = runBlocking { messageRepository.getMessage(student, testMessage).toFirstResult() }
|
||||
val res = runBlocking { repository.getMessage(student, testMessage).toFirstResult() }
|
||||
|
||||
assertEquals(null, res.error)
|
||||
assertEquals(Status.SUCCESS, res.status)
|
||||
@ -112,18 +212,22 @@ class MessageRepositoryTest {
|
||||
fun `get message when content in db is empty and there is no internet connection`() {
|
||||
val testMessage = getMessageEntity(123, "", false)
|
||||
|
||||
coEvery { messageDb.loadMessageWithAttachment(1, testMessage.messageId) } throws UnknownHostException()
|
||||
coEvery {
|
||||
messageDb.loadMessageWithAttachment(1, testMessage.messageId)
|
||||
} throws UnknownHostException()
|
||||
|
||||
runBlocking { messageRepository.getMessage(student, testMessage).toFirstResult() }
|
||||
runBlocking { repository.getMessage(student, testMessage).toFirstResult() }
|
||||
}
|
||||
|
||||
@Test(expected = UnknownHostException::class)
|
||||
fun `get message when content in db is empty, unread and there is no internet connection`() {
|
||||
val testMessage = getMessageEntity(123, "", true)
|
||||
|
||||
coEvery { messageDb.loadMessageWithAttachment(1, testMessage.messageId) } throws UnknownHostException()
|
||||
coEvery {
|
||||
messageDb.loadMessageWithAttachment(1, testMessage.messageId)
|
||||
} throws UnknownHostException()
|
||||
|
||||
runBlocking { messageRepository.getMessage(student, testMessage).toList()[1] }
|
||||
runBlocking { repository.getMessage(student, testMessage).toList()[1] }
|
||||
}
|
||||
|
||||
private fun getMessageEntity(
|
||||
@ -135,10 +239,10 @@ class MessageRepositoryTest {
|
||||
realId = 1,
|
||||
messageId = messageId,
|
||||
sender = "",
|
||||
senderId = 1,
|
||||
recipient = "",
|
||||
senderId = 0,
|
||||
recipient = "Wielu adresatów",
|
||||
subject = "",
|
||||
date = LocalDateTime.now(),
|
||||
date = LocalDateTime.MAX,
|
||||
folderId = 1,
|
||||
unread = unread,
|
||||
removed = false,
|
||||
@ -148,4 +252,24 @@ class MessageRepositoryTest {
|
||||
unreadBy = 1
|
||||
readBy = 1
|
||||
}
|
||||
|
||||
private fun getMessageDto(
|
||||
messageId: Int,
|
||||
content: String,
|
||||
unread: Boolean,
|
||||
) = io.github.wulkanowy.sdk.pojo.Message(
|
||||
id = 1,
|
||||
messageId = messageId,
|
||||
sender = Sender("", "", 0, 0, 0, ""),
|
||||
recipients = listOf(),
|
||||
subject = "",
|
||||
content = content,
|
||||
date = LocalDateTime.MAX,
|
||||
folderId = 1,
|
||||
unread = unread,
|
||||
unreadBy = 0,
|
||||
readBy = 0,
|
||||
removed = false,
|
||||
hasAttachments = false,
|
||||
)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user