Skip to content

Commit

Permalink
feat: Inform user about degraded conversation verification status (WP…
Browse files Browse the repository at this point in the history
…D-573) (#1893)

* feat: Infrom user about Degraded Conversation VerificationStatus

* feat: Inform user about degraded conversation verification status: fixed DB-migration

* feat: Inform user about degraded conversation verification status: style fixes

* feat: Inform user about degraded conversation verification status: review fixes

* feat: Inform user about degraded conversation verification status: tests fix

* feat: Inform user about degraded conversation verification status: improved DB part to insert new messageContent

* feat: Inform user about degraded conversation verification status: type fix

* feat: Inform user about degraded conversation verification status: get read of new DB-table

* feat: Inform user about degraded conversation verification status: style fix

* feat: Inform user about degraded conversation verification status: review fixes

* feat: Inform user about degraded conversation verification status: fixed DB migration

* feat: Inform user about degraded conversation verification status: review fixes

---------

Co-authored-by: Mojtaba Chenani <[email protected]>
  • Loading branch information
borichellow and mchenani authored Jul 24, 2023
1 parent 8572b17 commit 143a07a
Show file tree
Hide file tree
Showing 24 changed files with 616 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ data class Conversation(
abstract fun name(): String
}

enum class Protocol { PROTEUS, MLS }

data class Member(val id: UserId, val role: Role) {
sealed class Role {
object Member : Role()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ import kotlin.time.toDuration
interface ConversationMapper {
fun fromApiModelToDaoModel(apiModel: ConversationResponse, mlsGroupState: GroupState?, selfUserTeamId: TeamId?): ConversationEntity
fun fromApiModelToDaoModel(apiModel: ConvProtocol): Protocol
fun fromDaoModel(daoProtocol: Protocol?): Conversation.Protocol?
fun toDaoModel(protocol: Conversation.Protocol?): Protocol?
fun fromDaoModel(daoModel: ConversationViewEntity): Conversation
fun fromDaoModel(daoModel: ConversationEntity): Conversation
fun fromDaoModelToDetails(
Expand Down Expand Up @@ -123,6 +125,18 @@ internal class ConversationMapperImpl(
ConvProtocol.MLS -> Protocol.MLS
}

override fun fromDaoModel(daoProtocol: Protocol?): Conversation.Protocol? = when (daoProtocol) {
Protocol.PROTEUS -> Conversation.Protocol.PROTEUS
Protocol.MLS -> Conversation.Protocol.MLS
null -> null
}

override fun toDaoModel(protocol: Conversation.Protocol?): Protocol? = when (protocol) {
Conversation.Protocol.PROTEUS -> Protocol.PROTEUS
Conversation.Protocol.MLS -> Protocol.MLS
null -> null
}

override fun fromDaoModel(daoModel: ConversationViewEntity): Conversation = with(daoModel) {
val lastReadDateEntity = if (type == ConversationEntity.Type.CONNECTION_PENDING) UNIX_FIRST_DATE
else lastReadDate.toIsoDateTimeString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import com.wire.kalium.persistence.dao.QualifiedIDEntity
import com.wire.kalium.persistence.dao.client.ClientDAO
import com.wire.kalium.persistence.dao.conversation.ConversationDAO
import com.wire.kalium.persistence.dao.conversation.ConversationEntity
import com.wire.kalium.persistence.dao.conversation.ConversationMetaDataDAO
import com.wire.kalium.persistence.dao.member.MemberDAO
import com.wire.kalium.persistence.dao.message.MessageDAO
import com.wire.kalium.persistence.dao.unread.UnreadEventTypeEntity
Expand Down Expand Up @@ -199,6 +200,11 @@ interface ConversationRepository {
suspend fun getConversationUnreadEventsCount(conversationId: ConversationId): Either<StorageFailure, Long>
suspend fun updateUserSelfDeletionTimer(conversationId: ConversationId, selfDeletionTimer: SelfDeletionTimer): Either<CoreFailure, Unit>
suspend fun syncConversationsWithoutMetadata(): Either<CoreFailure, Unit>
suspend fun isInformedAboutDegradedMLSVerification(conversationId: ConversationId): Either<StorageFailure, Boolean>
suspend fun setInformedAboutDegradedMLSVerificationFlag(
conversationId: ConversationId,
isInformed: Boolean
): Either<StorageFailure, Unit>
}

@Suppress("LongParameterList", "TooManyFunctions")
Expand All @@ -212,6 +218,7 @@ internal class ConversationDataSource internal constructor(
private val messageDAO: MessageDAO,
private val clientDAO: ClientDAO,
private val clientApi: ClientApi,
private val conversationMetaDataDAO: ConversationMetaDataDAO,
private val idMapper: IdMapper = MapperProvider.idMapper(),
private val conversationMapper: ConversationMapper = MapperProvider.conversationMapper(),
private val memberMapper: MemberMapper = MapperProvider.memberMapper(),
Expand Down Expand Up @@ -743,6 +750,19 @@ internal class ConversationDataSource internal constructor(
}
}

override suspend fun isInformedAboutDegradedMLSVerification(conversationId: ConversationId): Either<StorageFailure, Boolean> =
wrapStorageRequest {
conversationMetaDataDAO.isInformedAboutDegradedMLSVerification(conversationId.toDao())
}

override suspend fun setInformedAboutDegradedMLSVerificationFlag(
conversationId: ConversationId,
isInformed: Boolean
): Either<StorageFailure, Unit> =
wrapStorageRequest {
conversationMetaDataDAO.setInformedAboutDegradedMLSVerificationFlag(conversationId.toDao(), isInformed)
}

private suspend fun persistIncompleteConversations(
conversationsFailed: List<NetworkQualifiedId>
): Either<CoreFailure, Unit> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.wire.kalium.persistence.dao.conversation.ConversationEntity
interface ProtocolInfoMapper {
fun fromEntity(protocolInfo: ConversationEntity.ProtocolInfo): Conversation.ProtocolInfo
fun toEntity(protocolInfo: Conversation.ProtocolInfo): ConversationEntity.ProtocolInfo
fun fromInfoToProtocol(protocolInfo: Conversation.ProtocolInfo): Conversation.Protocol
}

class ProtocolInfoMapperImpl(
Expand Down Expand Up @@ -53,4 +54,10 @@ class ProtocolInfoMapperImpl(
ConversationEntity.CipherSuite.fromTag(protocolInfo.cipherSuite.tag)
)
}

override fun fromInfoToProtocol(protocolInfo: Conversation.ProtocolInfo): Conversation.Protocol =
when (protocolInfo) {
is Conversation.ProtocolInfo.Proteus -> Conversation.Protocol.PROTEUS
is Conversation.ProtocolInfo.MLS -> Conversation.Protocol.MLS
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,14 @@ sealed interface Message {
is MessageContent.MLSWrongEpochWarning -> mutableMapOf(
typeKey to "mlsWrongEpochWarning"
)

is MessageContent.ConversationDegradedMLS -> mutableMapOf(
typeKey to "conversationDegradedMLS"
)

is MessageContent.ConversationDegradedProteus -> mutableMapOf(
typeKey to "conversationDegradedProteus"
)
}

val standardProperties = mapOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ sealed class MessageContent {

object HistoryLost : System()
object ConversationCreated : System()
object ConversationDegradedMLS : System()
object ConversationDegradedProteus : System()
}

/**
Expand Down Expand Up @@ -279,6 +281,8 @@ fun MessageContent?.getType() = when (this) {
is MessageContent.MemberChange.CreationAdded -> "MemberChange.CreationAdded"
is MessageContent.MemberChange.FailedToAdd -> "MemberChange.FailedToAdd"
is MessageContent.MLSWrongEpochWarning -> "MLSWrongEpochWarning"
is MessageContent.ConversationDegradedMLS -> "ConversationVerification.Degraded.MLS"
is MessageContent.ConversationDegradedProteus -> "ConversationVerification.Degraded.Proteus"
null -> "Unknown"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ class MessageMapperImpl(
MessageEntity.ContentType.CONVERSATION_MESSAGE_TIMER_CHANGED -> null
MessageEntity.ContentType.CONVERSATION_CREATED -> null
MessageEntity.ContentType.MLS_WRONG_EPOCH_WARNING -> null
MessageEntity.ContentType.CONVERSATION_DEGRADED_MLS -> null
MessageEntity.ContentType.CONVERSATION_DEGRADED_PREOTEUS -> null
}
}

Expand Down Expand Up @@ -357,6 +359,8 @@ class MessageMapperImpl(
is MessageContent.ConversationMessageTimerChanged -> MessageEntityContent.ConversationMessageTimerChanged(messageTimer)
is MessageContent.ConversationCreated -> MessageEntityContent.ConversationCreated
is MessageContent.MLSWrongEpochWarning -> MessageEntityContent.MLSWrongEpochWarning
is MessageContent.ConversationDegradedMLS -> MessageEntityContent.ConversationDegradedMLS
is MessageContent.ConversationDegradedProteus -> MessageEntityContent.ConversationDegradedProteus
}

private fun MessageEntityContent.Regular.toMessageContent(hidden: Boolean): MessageContent.Regular = when (this) {
Expand Down Expand Up @@ -444,6 +448,8 @@ class MessageMapperImpl(
is MessageEntityContent.ConversationMessageTimerChanged -> MessageContent.ConversationMessageTimerChanged(messageTimer)
is MessageEntityContent.ConversationCreated -> MessageContent.ConversationCreated
is MessageEntityContent.MLSWrongEpochWarning -> MessageContent.MLSWrongEpochWarning
is MessageEntityContent.ConversationDegradedMLS -> MessageContent.ConversationDegradedMLS
is MessageEntityContent.ConversationDegradedProteus -> MessageContent.ConversationDegradedProteus
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,7 @@ internal class PersistMessageUseCaseImpl(
is MessageContent.MemberChange.FailedToAdd -> false
is MessageContent.ConversationCreated -> false
is MessageContent.MLSWrongEpochWarning -> false
MessageContent.ConversationDegradedMLS -> false
MessageContent.ConversationDegradedProteus -> false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ import com.wire.kalium.logic.feature.connection.ConnectionScope
import com.wire.kalium.logic.feature.connection.SyncConnectionsUseCase
import com.wire.kalium.logic.feature.connection.SyncConnectionsUseCaseImpl
import com.wire.kalium.logic.feature.conversation.ConversationScope
import com.wire.kalium.logic.feature.conversation.ConversationVerificationStatusHandler
import com.wire.kalium.logic.feature.conversation.ConversationVerificationStatusHandlerImpl
import com.wire.kalium.logic.feature.conversation.ConversationsRecoveryManager
import com.wire.kalium.logic.feature.conversation.ConversationsRecoveryManagerImpl
import com.wire.kalium.logic.feature.conversation.GetConversationVerificationStatusUseCase
Expand Down Expand Up @@ -524,7 +526,8 @@ class UserSessionScope internal constructor(
authenticatedNetworkContainer.conversationApi,
userStorage.database.messageDAO,
userStorage.database.clientDAO,
authenticatedNetworkContainer.clientApi
authenticatedNetworkContainer.clientApi,
userStorage.database.conversationMetaDataDAO
)

private val conversationGroupRepository: ConversationGroupRepository
Expand Down Expand Up @@ -1371,10 +1374,18 @@ class UserSessionScope internal constructor(
}
}

private val conversationVerificationStatusHandler: ConversationVerificationStatusHandler
get() = ConversationVerificationStatusHandlerImpl(
conversationRepository,
persistMessage,
userId
)

val getConversationVerificationStatus: GetConversationVerificationStatusUseCase
get() = GetConversationVerificationStatusUseCaseImpl(
conversationRepository,
mlsConversationRepository
mlsConversationRepository,
conversationVerificationStatusHandler
)

internal val getProxyCredentials: GetProxyCredentialsUseCase
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program 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.
*
* This program 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 this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.kalium.logic.feature.conversation

import com.benasher44.uuid.uuid4
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.conversation.ConversationRepository
import com.wire.kalium.logic.data.conversation.ConversationVerificationStatus
import com.wire.kalium.logic.data.message.Message
import com.wire.kalium.logic.data.message.MessageContent
import com.wire.kalium.logic.data.message.PersistMessageUseCase
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.functional.flatMap
import com.wire.kalium.logic.functional.getOrElse
import com.wire.kalium.util.DateTimeUtil
import com.wire.kalium.util.KaliumDispatcher
import com.wire.kalium.util.KaliumDispatcherImpl
import kotlinx.coroutines.withContext

/**
* Notify user (by adding System message in conversation) if needed about changes of Conversation Verification status.
*/
internal interface ConversationVerificationStatusHandler {
suspend operator fun invoke(conversation: Conversation, status: ConversationVerificationStatus)
}

internal class ConversationVerificationStatusHandlerImpl(
private val conversationRepository: ConversationRepository,
private val persistMessage: PersistMessageUseCase,
private val selfUserId: UserId,
kaliumDispatcher: KaliumDispatcher = KaliumDispatcherImpl
) : ConversationVerificationStatusHandler {
private val dispatcher = kaliumDispatcher.io

override suspend fun invoke(conversation: Conversation, status: ConversationVerificationStatus): Unit = withContext(dispatcher) {
if (shouldNotifyUser(conversation, status)) {
val content = when (conversation.protocol) {
is Conversation.ProtocolInfo.MLS -> MessageContent.ConversationDegradedMLS
Conversation.ProtocolInfo.Proteus -> MessageContent.ConversationDegradedProteus
}
val conversationDegradedMessage = Message.System(
id = uuid4().toString(),
content = content,
conversationId = conversation.id,
date = DateTimeUtil.currentIsoDateTimeString(),
senderUserId = selfUserId,
status = Message.Status.SENT,
visibility = Message.Visibility.VISIBLE,
expirationData = null
)

persistMessage(conversationDegradedMessage)
.flatMap { conversationRepository.setInformedAboutDegradedMLSVerificationFlag(conversation.id, true) }
} else if (status != ConversationVerificationStatus.DEGRADED) {
conversationRepository.setInformedAboutDegradedMLSVerificationFlag(conversation.id, false)
}
}

private suspend fun shouldNotifyUser(conversation: Conversation, status: ConversationVerificationStatus): Boolean =
if (status == ConversationVerificationStatus.DEGRADED) {
if (conversation.protocol is Conversation.ProtocolInfo.MLS) {
!conversationRepository.isInformedAboutDegradedMLSVerification(conversation.id).getOrElse(true)
} else {
// TODO check flag for Proteus after implementing it.
false
}
} else {
false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ import com.wire.kalium.logic.functional.Either
import com.wire.kalium.logic.functional.flatMap
import com.wire.kalium.logic.functional.getOrElse
import com.wire.kalium.logic.functional.map
import com.wire.kalium.logic.functional.onSuccess
import com.wire.kalium.util.KaliumDispatcher
import com.wire.kalium.util.KaliumDispatcherImpl
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

/**
Expand All @@ -43,9 +45,10 @@ interface GetConversationVerificationStatusUseCase {
suspend operator fun invoke(conversationId: ConversationId): ConversationVerificationStatusResult
}

class GetConversationVerificationStatusUseCaseImpl(
class GetConversationVerificationStatusUseCaseImpl internal constructor(
private val conversationRepository: ConversationRepository,
private val mlsConversationRepository: MLSConversationRepository,
private val verificationStatusHandler: ConversationVerificationStatusHandler,
kaliumDispatcher: KaliumDispatcher = KaliumDispatcherImpl
) : GetConversationVerificationStatusUseCase {
private val dispatcher = kaliumDispatcher.io
Expand All @@ -58,7 +61,12 @@ class GetConversationVerificationStatusUseCaseImpl(
} else {
getConversationProteusVerificationStatus(conversation.id)
}
}.getOrElse { ConversationVerificationStatusResult.Failure(it) }
.onSuccess {
if (it is ConversationVerificationStatusResult.Success)
launch { verificationStatusHandler(conversation, it.status) }
}
}
.getOrElse { ConversationVerificationStatusResult.Failure(it) }
}

private suspend fun getConversationMLSVerificationStatus(protocol: Conversation.ProtocolInfo.MLS) =
Expand All @@ -68,7 +76,7 @@ class GetConversationVerificationStatusUseCaseImpl(
private suspend fun getConversationProteusVerificationStatus(
conversationId: ConversationId
): Either<CoreFailure, ConversationVerificationStatusResult> {
// TODO needs to be handled by for Proteus conversation that is not implemented yet
// TODO needs to be handled for Proteus conversation that is not implemented yet
return Either.Right(
ConversationVerificationStatusResult.Success(
ConversationProtocol.PROTEUS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ import com.wire.kalium.persistence.dao.client.DeviceTypeEntity
import com.wire.kalium.persistence.dao.client.Client as ClientEntity
import com.wire.kalium.persistence.dao.conversation.ConversationDAO
import com.wire.kalium.persistence.dao.conversation.ConversationEntity
import com.wire.kalium.persistence.dao.conversation.ConversationMetaDataDAO
import com.wire.kalium.persistence.dao.conversation.ConversationViewEntity
import com.wire.kalium.persistence.dao.message.MessageDAO
import com.wire.kalium.persistence.dao.message.MessagePreviewEntity
Expand Down Expand Up @@ -1006,6 +1007,9 @@ class ConversationRepositoryTest {
@Mock
private val messageDAO = configure(mock(MessageDAO::class)) { stubsUnitByDefault = true }

@Mock
val conversationMetaDataDAO: ConversationMetaDataDAO = mock(ConversationMetaDataDAO::class)

@Mock
val renamedConversationEventHandler = configure(mock(RenamedConversationEventHandler::class)) { stubsUnitByDefault = true }

Expand All @@ -1019,7 +1023,8 @@ class ConversationRepositoryTest {
conversationApi,
messageDAO,
clientDao,
clientApi
clientApi,
conversationMetaDataDAO
)

init {
Expand Down
Loading

0 comments on commit 143a07a

Please sign in to comment.