Added a wrapper to the message body results after decryption to be able to distinguish if the body was decrypted or not. Because even if decryption fails we still need to show a body.
MAILAND-2283
This commit is contained in:
parent
267ed05ad6
commit
5760b43c65
|
@ -53,6 +53,7 @@ import ch.protonmail.android.details.data.toConversationUiModel
|
|||
import ch.protonmail.android.details.presentation.MessageDetailsActivity
|
||||
import ch.protonmail.android.details.presentation.model.ConversationUiModel
|
||||
import ch.protonmail.android.domain.entity.LabelId
|
||||
import ch.protonmail.android.details.presentation.model.MessageBodyState
|
||||
import ch.protonmail.android.domain.entity.Name
|
||||
import ch.protonmail.android.events.DownloadEmbeddedImagesEvent
|
||||
import ch.protonmail.android.events.Status
|
||||
|
@ -300,19 +301,24 @@ internal class MessageDetailsViewModel @Inject constructor(
|
|||
Timber.v("loadMessageBody ${message.messageId} isNotDecrypted: ${message.decryptedHTML.isNullOrEmpty()}")
|
||||
|
||||
if (!message.decryptedHTML.isNullOrEmpty()) {
|
||||
emit(message)
|
||||
emit(MessageBodyState.Success(message))
|
||||
} else {
|
||||
val userId = userManager.requireCurrentUserId()
|
||||
val messageId = requireNotNull(message.messageId)
|
||||
val fetchedMessage = messageRepository.getMessage(userId, messageId, true)
|
||||
val isDecrypted = fetchedMessage?.tryDecrypt(publicKeys)
|
||||
if (isDecrypted == true) {
|
||||
Timber.v("message $messageId isDecrypted, isRead: ${fetchedMessage.isRead}")
|
||||
if (!fetchedMessage.isRead) {
|
||||
messageRepository.markRead(listOf(messageId))
|
||||
}
|
||||
emit(fetchedMessage)
|
||||
val fetchedMessage = messageRepository.getMessage(userId, messageId, true) ?: return@flow
|
||||
|
||||
val isDecrypted = fetchedMessage.tryDecrypt(publicKeys)
|
||||
Timber.v("message $messageId isDecrypted, isRead: ${fetchedMessage.isRead}")
|
||||
if (!fetchedMessage.isRead) {
|
||||
messageRepository.markRead(listOf(messageId))
|
||||
}
|
||||
|
||||
if (isDecrypted == true) {
|
||||
emit(MessageBodyState.Success(fetchedMessage))
|
||||
} else {
|
||||
emit(MessageBodyState.Error.DecryptionError(fetchedMessage))
|
||||
}
|
||||
|
||||
}
|
||||
}.flowOn(dispatchers.Io)
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ import ch.protonmail.android.data.local.model.Attachment
|
|||
import ch.protonmail.android.data.local.model.Message
|
||||
import ch.protonmail.android.details.domain.MessageBodyParser
|
||||
import ch.protonmail.android.details.presentation.model.ConversationUiModel
|
||||
import ch.protonmail.android.details.presentation.model.MessageBodyState
|
||||
import ch.protonmail.android.events.DownloadEmbeddedImagesEvent
|
||||
import ch.protonmail.android.events.DownloadedAttachmentEvent
|
||||
import ch.protonmail.android.events.PostPhishingReportEvent
|
||||
|
@ -178,6 +179,7 @@ internal class MessageDetailsActivity : BaseStoragePermissionActivity() {
|
|||
message.decryptedHTML,
|
||||
messageId,
|
||||
false,
|
||||
false,
|
||||
message.attachments
|
||||
)
|
||||
}
|
||||
|
@ -214,8 +216,10 @@ internal class MessageDetailsActivity : BaseStoragePermissionActivity() {
|
|||
|
||||
private fun onLoadMessageBody(message: Message) {
|
||||
if (message.messageId != null) {
|
||||
viewModel.loadMessageBody(message).mapLatest { loadedMessage ->
|
||||
viewModel.loadMessageBody(message).mapLatest { messageBodyState ->
|
||||
|
||||
val showDecryptionError = messageBodyState is MessageBodyState.Error.DecryptionError
|
||||
val loadedMessage = messageBodyState.message
|
||||
val parsedBody = viewModel.formatMessageHtmlBody(
|
||||
loadedMessage,
|
||||
UiUtil.getRenderWidth(this.windowManager),
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Proton Technologies AG
|
||||
*
|
||||
* This file is part of ProtonMail.
|
||||
*
|
||||
* ProtonMail is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonMail is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonMail. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
package ch.protonmail.android.details.presentation.model
|
||||
|
||||
import ch.protonmail.android.data.local.model.Message
|
||||
|
||||
sealed class MessageBodyState(val message: Message) {
|
||||
data class Success(val _message: Message) : MessageBodyState(_message)
|
||||
sealed class Error(message: Message) : MessageBodyState(message) {
|
||||
data class DecryptionError(val _message: Message) : Error(_message)
|
||||
}
|
||||
}
|
|
@ -29,7 +29,7 @@ import ch.protonmail.android.databinding.LayoutMessageDetailsDecryptionErrorInfo
|
|||
/**
|
||||
* A view for info banner in message details
|
||||
*/
|
||||
class MessageDetailsDecryptionErrorBannerView @JvmOverloads constructor(
|
||||
class DecryptionErrorBanner @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
|
|
|
@ -133,7 +133,7 @@
|
|||
android:visibility="visible"
|
||||
tools:text="@string/spam_score_101" />
|
||||
|
||||
<ch.protonmail.android.details.presentation.view.MessageDetailsDecryptionErrorBannerView
|
||||
<ch.protonmail.android.details.presentation.view.DecryptionErrorBanner
|
||||
android:id="@+id/decryptionErrorView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
@ -42,6 +42,7 @@ import ch.protonmail.android.details.presentation.MessageDetailsActivity.Compani
|
|||
import ch.protonmail.android.details.presentation.MessageDetailsActivity.Companion.EXTRA_MESSAGE_OR_CONVERSATION_ID
|
||||
import ch.protonmail.android.details.presentation.model.ConversationUiModel
|
||||
import ch.protonmail.android.domain.entity.LabelId
|
||||
import ch.protonmail.android.details.presentation.model.MessageBodyState
|
||||
import ch.protonmail.android.domain.entity.Name
|
||||
import ch.protonmail.android.labels.domain.usecase.MoveMessagesToFolder
|
||||
import ch.protonmail.android.mailbox.domain.ChangeConversationsReadStatus
|
||||
|
@ -69,7 +70,6 @@ import io.mockk.runs
|
|||
import io.mockk.verify
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
|
@ -538,14 +538,15 @@ class MessageDetailsViewModelTest : ArchTest, CoroutinesTest {
|
|||
|
||||
// When
|
||||
val actual = viewModel.loadMessageBody(message).first()
|
||||
val expected = MessageBodyState.Success(message)
|
||||
|
||||
// Then
|
||||
verify { messageRepository.markRead(listOf("messageId1")) }
|
||||
assertEquals(message, actual)
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadMessageBodyDoesNotMarkMessageAsReadWhenTheMessageDecryptionFails() = runBlockingTest {
|
||||
fun loadMessageBodyDoesMarksMessageAsReadWhenTheMessageDecryptionFails() = runBlockingTest {
|
||||
// Given
|
||||
val message = mockk<Message>(relaxed = true)
|
||||
every { message.messageId } returns "messageId2"
|
||||
|
@ -555,12 +556,15 @@ class MessageDetailsViewModelTest : ArchTest, CoroutinesTest {
|
|||
every { message.senderEmail } returns "senderEmail"
|
||||
every { message.decrypt(any(), any(), any()) } throws Exception("Test - Decryption failed")
|
||||
coEvery { messageRepository.getMessage(any(), any(), any()) } returns message
|
||||
coEvery { messageRepository.markRead(any()) } just Runs
|
||||
|
||||
// When
|
||||
val actual: Flow<Message> = viewModel.loadMessageBody(message)
|
||||
val actual = viewModel.loadMessageBody(message).first()
|
||||
val expected = MessageBodyState.Error.DecryptionError(message)
|
||||
|
||||
// Then
|
||||
verify(exactly = 0) { messageRepository.markRead(any()) }
|
||||
verify { messageRepository.markRead(listOf("messageId2")) }
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -582,7 +586,7 @@ class MessageDetailsViewModelTest : ArchTest, CoroutinesTest {
|
|||
|
||||
// Then
|
||||
verify(exactly = 0) { messageRepository.markRead(any()) }
|
||||
assertEquals(message, expectItem())
|
||||
assertEquals(MessageBodyState.Success(message), expectItem())
|
||||
expectComplete()
|
||||
}
|
||||
}
|
||||
|
@ -604,7 +608,7 @@ class MessageDetailsViewModelTest : ArchTest, CoroutinesTest {
|
|||
|
||||
// Then
|
||||
verify(exactly = 1) { messageRepository.markRead(any()) }
|
||||
assertEquals(message, expectItem())
|
||||
assertEquals(MessageBodyState.Success(message), expectItem())
|
||||
expectComplete()
|
||||
}
|
||||
}
|
||||
|
@ -687,7 +691,7 @@ class MessageDetailsViewModelTest : ArchTest, CoroutinesTest {
|
|||
|
||||
val decryptedMessage = viewModel.loadMessageBody(message).first()
|
||||
|
||||
assertEquals(decryptedMessageHtml, decryptedMessage.decryptedHTML)
|
||||
assertEquals(decryptedMessageHtml, (decryptedMessage as MessageBodyState.Success).message.decryptedHTML)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -712,9 +716,10 @@ class MessageDetailsViewModelTest : ArchTest, CoroutinesTest {
|
|||
|
||||
// When
|
||||
val decryptedMessage = viewModel.loadMessageBody(message).first()
|
||||
val expectedMessage = MessageBodyState.Success(fetchedMessage)
|
||||
|
||||
// Then
|
||||
assertEquals(fetchedMessage, decryptedMessage)
|
||||
assertEquals(expectedMessage, decryptedMessage)
|
||||
verify(exactly = 0) { messageRepository.markRead(any()) }
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue