Moved some labels and message related logic for action sheet from MessageDetailsRepository to MessageRepository.

MAILAND-1401
This commit is contained in:
Tomasz Giszczak 2021-05-10 10:42:47 +02:00
parent 514f83e1bd
commit 36beb44829
10 changed files with 77 additions and 53 deletions

View File

@ -45,11 +45,8 @@ import ch.protonmail.android.domain.entity.Id
import ch.protonmail.android.jobs.ApplyLabelJob
import ch.protonmail.android.jobs.FetchMessageDetailJob
import ch.protonmail.android.jobs.PostReadJob
import ch.protonmail.android.jobs.PostStarJob
import ch.protonmail.android.jobs.PostUnreadJob
import ch.protonmail.android.jobs.PostUnstarJob
import ch.protonmail.android.jobs.RemoveLabelJob
import ch.protonmail.android.jobs.ReportPhishingJob
import ch.protonmail.android.utils.MessageUtils
import ch.protonmail.android.utils.extensions.asyncMap
import com.birbit.android.jobqueue.Job
@ -124,12 +121,6 @@ class MessageDetailsRepository @Inject constructor(
fun findMessageById(messageId: String): Flow<Message?> =
messagesDao.findMessageById(messageId).map { readMessageBodyFromFileIfNeeded(it) }
suspend fun findMessageByIdOnce(messageId: String): Message =
messagesDao.findMessageByIdOnce(messageId).apply { readMessageBodyFromFileIfNeeded(this) }
fun findSearchMessageByIdBlocking(messageId: String): Message? =
searchDatabaseDao.findMessageByIdBlocking(messageId)?.apply { readMessageBodyFromFileIfNeeded(this) }
fun findSearchMessageById(messageId: String): Flow<Message?> =
searchDatabaseDao.findMessageById(messageId).map { readMessageBodyFromFileIfNeeded(it) }
@ -552,33 +543,22 @@ class MessageDetailsRepository @Inject constructor(
attachmentsWorker.enqueue(messageId, userId, "")
}
@Deprecated("Use a method from [MessageRepository]")
fun markRead(messageIds: List<String>) {
jobManager.addJobInBackground(PostReadJob(messageIds))
}
@Deprecated("Use a method from [MessageRepository]")
fun markUnRead(messageIds: List<String>) {
jobManager.addJobInBackground(PostUnreadJob(messageIds))
}
fun starMessages(messageIds: List<String>) {
jobManager.addJobInBackground(PostStarJob(messageIds))
}
fun unStarMessages(messageIds: List<String>) {
jobManager.addJobInBackground(PostUnstarJob(messageIds))
}
fun findAllPendingSendsAsync(): LiveData<List<PendingSend>> =
pendingActionDao.findAllPendingSendsAsync()
fun findAllPendingUploadsAsync(): LiveData<List<PendingUpload>> =
pendingActionDao.findAllPendingUploadsAsync()
suspend fun reportPhishing(messageId: String) {
val message = findMessageByIdOnce(messageId)
jobManager.addJobInBackground(ReportPhishingJob(message))
}
@AssistedInject.Factory
interface AssistedFactory {

View File

@ -126,10 +126,6 @@ abstract class MessageDao {
message.Attachments = message.attachments(this)
}
suspend fun findMessageByIdOnce(messageId: String): Message = findMessageInfoByIdOnce(messageId).also { message ->
message.Attachments = message.attachmentsBlocking(this)
}
@Deprecated("Use Flow variant", ReplaceWith("findMessageById(messageId).first()"))
fun findMessageByIdBlocking(messageId: String): Message? = findMessageInfoByIdBlocking(messageId)
?.also { message ->
@ -182,7 +178,7 @@ abstract class MessageDao {
protected abstract fun findMessageInfoById(messageId: String): Flow<Message?>
@Query("SELECT * FROM $TABLE_MESSAGES WHERE $COLUMN_MESSAGE_ID = :messageId")
protected abstract suspend fun findMessageInfoByIdOnce(messageId: String): Message
protected abstract suspend fun findMessageInfoByIdOnce(messageId: String): Message?
@Deprecated("Use Flow variant", ReplaceWith("findMessageInfoById(messageId).first()"))
@Query("SELECT * FROM $TABLE_MESSAGES WHERE $COLUMN_MESSAGE_ID = :messageId")

View File

@ -20,11 +20,13 @@
package ch.protonmail.android.labels.domain.usecase
import ch.protonmail.android.activities.messageDetails.repository.MessageDetailsRepository
import ch.protonmail.android.repository.MessageRepository
import timber.log.Timber
import javax.inject.Inject
class UpdateLabels @Inject constructor(
private val messageDetailsRepository: MessageDetailsRepository
private val messageDetailsRepository: MessageDetailsRepository, // TODO: Replace it with future LabelsRepository
private val messageRepository: MessageRepository
) {
suspend operator fun invoke(
@ -32,7 +34,7 @@ class UpdateLabels @Inject constructor(
checkedLabelIds: List<String>,
isTransient: Boolean = false
) {
val message = messageDetailsRepository.findMessageByIdOnce(messageId)
val message = requireNotNull(messageRepository.findMessageById(messageId))
val existingLabels = messageDetailsRepository.getAllLabels()
.filter { it.id in message.labelIDsNotIncludingLocations }
Timber.v("UpdateLabels checkedLabelIds: $checkedLabelIds")

View File

@ -28,17 +28,24 @@ import ch.protonmail.android.domain.entity.Id
import ch.protonmail.android.jobs.MoveToFolderJob
import ch.protonmail.android.jobs.PostArchiveJob
import ch.protonmail.android.jobs.PostInboxJob
import ch.protonmail.android.jobs.PostReadJob
import ch.protonmail.android.jobs.PostSpamJob
import ch.protonmail.android.jobs.PostStarJob
import ch.protonmail.android.jobs.PostTrashJobV2
import ch.protonmail.android.jobs.PostUnreadJob
import ch.protonmail.android.jobs.PostUnstarJob
import ch.protonmail.android.utils.MessageBodyFileManager
import com.birbit.android.jobqueue.JobManager
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext
import me.proton.core.util.kotlin.DispatcherProvider
import timber.log.Timber
import javax.inject.Inject
const val MAX_BODY_SIZE_IN_DB = 900 * 1024 // 900 KB
private const val FILE_PREFIX = "file://"
/**
* A repository for getting and saving messages.
*/
@ -57,12 +64,22 @@ class MessageRepository @Inject constructor(
val messageDao = databaseProvider.provideMessageDao(userId)
return@withContext messageDao.findMessageById(messageId).first()?.apply {
messageBody?.let {
if (it.startsWith("file://"))
if (it.startsWith(FILE_PREFIX))
messageBody = messageBodyFileManager.readMessageBodyFromFile(this)
}
}
}
suspend fun findMessageById(messageId: String): Message? {
val currentUser = userManager.currentUserId
return if (currentUser != null) {
findMessage(currentUser, messageId)
} else {
Timber.w("Cannot find message for null user id")
null
}
}
private suspend fun saveMessage(userId: Id, message: Message): Long =
withContext(dispatcherProvider.Io) {
val messageToBeSaved = message.apply {
@ -166,4 +183,21 @@ class MessageRepository @Inject constructor(
MoveToFolderJob(messageIds, newFolderLocationId)
)
}
fun starMessages(messageIds: List<String>) {
jobManager.addJobInBackground(PostStarJob(messageIds))
}
fun unStarMessages(messageIds: List<String>) {
jobManager.addJobInBackground(PostUnstarJob(messageIds))
}
fun markRead(messageIds: List<String>) {
jobManager.addJobInBackground(PostReadJob(messageIds))
}
fun markUnRead(messageIds: List<String>) {
jobManager.addJobInBackground(PostUnreadJob(messageIds))
}
}

View File

@ -69,7 +69,7 @@ class MessageActionSheet : BottomSheetDialogFragment() {
val binding = FragmentMessageDetailsActionSheetBinding.inflate(inflater)
setupHeaderBindings(binding.actionSheetHeaderDetailsActions, arguments)
setupMessageReplyActionsBindings(binding.includeLayoutActionSheetButtons, originatorId)
setupReplyActionsBindings(binding.includeLayoutActionSheetButtons, originatorId)
setupManageSectionBindings(binding, viewModel, originatorId, messageIds, messageLocation)
setupMoveSectionBindings(binding, viewModel, messageIds, messageLocation)
setupMoreSectionBindings(binding, originatorId, messageIds)
@ -142,7 +142,7 @@ class MessageActionSheet : BottomSheetDialogFragment() {
}
}
private fun setupMessageReplyActionsBindings(
private fun setupReplyActionsBindings(
binding: LayoutMessageDetailsActionsSheetButtonsBinding,
originatorId: Int
) = with(binding) {

View File

@ -21,23 +21,24 @@ package ch.protonmail.android.ui.dialog
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import ch.protonmail.android.activities.messageDetails.repository.MessageDetailsRepository
import ch.protonmail.android.core.Constants
import ch.protonmail.android.labels.domain.usecase.MoveMessagesToFolder
import ch.protonmail.android.labels.presentation.ui.ManageLabelsActionSheet
import ch.protonmail.android.repository.MessageRepository
import ch.protonmail.android.usecase.delete.DeleteMessage
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import me.proton.core.util.kotlin.EMPTY_STRING
import timber.log.Timber
import javax.inject.Inject
@HiltViewModel
class MessageActionSheetViewModel @Inject constructor(
private val deleteMessage: DeleteMessage,
private val moveMessagesToFolder: MoveMessagesToFolder,
private val messageDetailsRepository: MessageDetailsRepository
private val messageRepository: MessageRepository
) : ViewModel() {
private val actionsMutableFlow = MutableStateFlow<MessageActionSheetAction>(MessageActionSheetAction.Default)
@ -65,8 +66,11 @@ class MessageActionSheetViewModel @Inject constructor(
): List<String> {
val checkedLabels = mutableListOf<String>()
messageIds.forEach { messageId ->
val message = messageDetailsRepository.findMessageByIdOnce(messageId)
checkedLabels.addAll(message.labelIDsNotIncludingLocations)
val message = messageRepository.findMessageById(messageId)
Timber.v("Checking message labels: ${message?.labelIDsNotIncludingLocations}")
message?.labelIDsNotIncludingLocations?.let {
checkedLabels.addAll(it)
}
}
return checkedLabels
}
@ -112,19 +116,19 @@ class MessageActionSheetViewModel @Inject constructor(
)
fun starMessage(messageId: List<String>) =
messageDetailsRepository.starMessages(messageId)
messageRepository.starMessages(messageId)
fun unStarMessage(messageId: List<String>) =
messageDetailsRepository.unStarMessages(messageId)
messageRepository.unStarMessages(messageId)
fun markUnread(messageIds: List<String>) = messageDetailsRepository.markUnRead(messageIds)
fun markUnread(messageIds: List<String>) = messageRepository.markUnRead(messageIds)
fun markRead(messageIds: List<String>) = messageDetailsRepository.markRead(messageIds)
fun markRead(messageIds: List<String>) = messageRepository.markRead(messageIds)
fun showMessageHeaders(messageId: String) {
viewModelScope.launch {
val message = messageDetailsRepository.findMessageByIdOnce(messageId)
actionsMutableFlow.value = MessageActionSheetAction.ShowMessageHeaders(message.header ?: EMPTY_STRING)
val message = messageRepository.findMessageById(messageId)
actionsMutableFlow.value = MessageActionSheetAction.ShowMessageHeaders(message?.header ?: EMPTY_STRING)
}
}
}

View File

@ -24,6 +24,7 @@ import me.proton.core.util.kotlin.DispatcherProvider
import okio.buffer
import okio.sink
import okio.source
import timber.log.Timber
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
@ -48,7 +49,10 @@ class FileHelper @Inject constructor(
FileInputStream(file)
.bufferedReader()
.use { it.readText() }
}.getOrNull()
}
.onFailure { Timber.i(it, "Unable to read file") }
.onSuccess { Timber.v("File ${file.path} read success") }
.getOrNull()
}
suspend fun writeToFile(file: File, text: String): Boolean = withContext(dispatcherProvider.Io) {

View File

@ -22,6 +22,7 @@ package ch.protonmail.android.labels.domain.usecase
import ch.protonmail.android.activities.messageDetails.repository.MessageDetailsRepository
import ch.protonmail.android.data.local.model.Label
import ch.protonmail.android.data.local.model.Message
import ch.protonmail.android.repository.MessageRepository
import io.mockk.MockKAnnotations
import io.mockk.Runs
import io.mockk.coEvery
@ -39,12 +40,15 @@ class UpdateLabelsTest {
@MockK
private lateinit var repository: MessageDetailsRepository
@MockK
private lateinit var newRepository: MessageRepository
private lateinit var useCase: UpdateLabels
@BeforeTest
fun setUp() {
MockKAnnotations.init(this)
useCase = UpdateLabels(repository)
useCase = UpdateLabels(repository, newRepository)
}
@Test
@ -61,7 +65,7 @@ class UpdateLabelsTest {
val label = mockk<Label> {
every { id } returns testLabelId1
}
coEvery { repository.findMessageByIdOnce(testMessageId) } returns message
coEvery { newRepository.findMessageById(testMessageId) } returns message
val existingLabels = listOf(label)
coEvery { repository.getAllLabels() } returns existingLabels
val checkedLabelIds = listOf(testLabelId1)

View File

@ -19,11 +19,11 @@
package ch.protonmail.android.ui.dialog
import ch.protonmail.android.activities.messageDetails.repository.MessageDetailsRepository
import ch.protonmail.android.core.Constants
import ch.protonmail.android.data.local.model.Message
import ch.protonmail.android.labels.domain.usecase.MoveMessagesToFolder
import ch.protonmail.android.labels.presentation.ui.ManageLabelsActionSheet
import ch.protonmail.android.repository.MessageRepository
import ch.protonmail.android.usecase.delete.DeleteMessage
import io.mockk.MockKAnnotations
import io.mockk.coEvery
@ -46,7 +46,7 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
private lateinit var moveMessagesToFolder: MoveMessagesToFolder
@MockK
private lateinit var repository: MessageDetailsRepository
private lateinit var repository: MessageRepository
private lateinit var viewModel: MessageActionSheetViewModel
@BeforeTest
@ -85,8 +85,8 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
every { messageId } returns messageId2
every { labelIDsNotIncludingLocations } returns listOf(labelId2)
}
coEvery { repository.findMessageByIdOnce(messageId1) } returns message1
coEvery { repository.findMessageByIdOnce(messageId2) } returns message2
coEvery { repository.findMessageById(messageId1) } returns message1
coEvery { repository.findMessageById(messageId2) } returns message2
// when
viewModel.showLabelsManager(messageIds, currentLocation)
@ -121,8 +121,8 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
every { messageId } returns messageId2
every { labelIDsNotIncludingLocations } returns listOf(labelId2)
}
coEvery { repository.findMessageByIdOnce(messageId1) } returns message1
coEvery { repository.findMessageByIdOnce(messageId2) } returns message2
coEvery { repository.findMessageById(messageId1) } returns message1
coEvery { repository.findMessageById(messageId2) } returns message2
// when
viewModel.showLabelsManager(messageIds, currentLocation, ManageLabelsActionSheet.Type.FOLDER)
@ -141,7 +141,7 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
every { messageId } returns messageId1
every { header } returns messageHeader
}
coEvery { repository.findMessageByIdOnce(messageId1) } returns message1
coEvery { repository.findMessageById(messageId1) } returns message1
val expected = MessageActionSheetAction.ShowMessageHeaders(messageHeader)
// when

View File

@ -160,7 +160,7 @@ class DeleteMessageTest {
every { db.findPendingUploadByMessageId(any()) } returns null
every { db.findPendingSendByMessageId(any()) } returns pendingSend
every { repository.findMessageByIdBlocking(messId) } returns null
every { repository.findSearchMessageByIdBlocking(messId) } returns message
every { repository.findSearchMessageById(messId) } returns flowOf(message)
coEvery { repository.saveMessage(message) } returns 1L
coEvery { repository.saveSearchMessage(message) } returns 0L
coEvery { repository.saveMessagesInOneTransaction(any()) } returns Unit