diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationRepository.kt index 64f244ec077..ea3fb460933 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationRepository.kt @@ -282,6 +282,11 @@ interface ConversationRepository { */ suspend fun updateProtocolLocally(conversationId: ConversationId, protocol: Conversation.Protocol): Either + suspend fun observeDegradedConversationNotified(conversationId: QualifiedID): Flow + suspend fun setDegradedConversationNotifiedFlag( + conversationId: QualifiedID, + value: Boolean + ): Either } @Suppress("LongParameterList", "TooManyFunctions", "LargeClass") @@ -1056,6 +1061,19 @@ internal class ConversationDataSource internal constructor( } } + override suspend fun setDegradedConversationNotifiedFlag( + conversationId: QualifiedID, + value: Boolean + ): Either = + wrapStorageRequest { + conversationDAO.updateDegradedConversationNotifiedFlag(conversationId.toDao(), value) + } + + override suspend fun observeDegradedConversationNotified(conversationId: QualifiedID): Flow = + conversationDAO.observeDegradedConversationNotified(conversationId.toDao()) + .wrapStorageRequest() + .mapToRightOr(true) + companion object { const val DEFAULT_MEMBER_ROLE = "wire_member" } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/conversation/ConversationScope.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/conversation/ConversationScope.kt index 105fd62efff..08d1cac07e0 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/conversation/ConversationScope.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/conversation/ConversationScope.kt @@ -307,4 +307,9 @@ class ConversationScope internal constructor( val clearUsersTypingEvents: ClearUsersTypingEventsUseCase get() = ClearUsersTypingEventsUseCaseImpl(typingIndicatorIncomingRepository) + val setUserInformedAboutVerificationBeforeMessagingUseCase: SetUserInformedAboutVerificationUseCase + get() = SetUserInformedAboutVerificationUseCaseImpl(conversationRepository) + val observeInformAboutVerificationBeforeMessagingFlagUseCase: ObserveDegradedConversationNotifiedUseCase + get() = ObserveDegradedConversationNotifiedUseCaseImpl(conversationRepository) + } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/conversation/ObserveDegradedConversationNotifiedUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/conversation/ObserveDegradedConversationNotifiedUseCase.kt new file mode 100644 index 00000000000..ffe6197f556 --- /dev/null +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/conversation/ObserveDegradedConversationNotifiedUseCase.kt @@ -0,0 +1,41 @@ +/* + * 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.wire.kalium.logic.data.conversation.ConversationRepository +import com.wire.kalium.logic.data.id.ConversationId +import kotlinx.coroutines.flow.Flow + +/** + * UseCase for observing if User was notified about verification degrading of conversation + */ +interface ObserveDegradedConversationNotifiedUseCase { + /** + * @return [Flow] of [Boolean], false means conversation's verification degraded and user needs to be notified + * true in other cases. + */ + suspend operator fun invoke(conversationId: ConversationId): Flow +} + +class ObserveDegradedConversationNotifiedUseCaseImpl internal constructor( + private val conversationRepository: ConversationRepository +) : ObserveDegradedConversationNotifiedUseCase { + + override suspend fun invoke(conversationId: ConversationId): Flow = + conversationRepository.observeDegradedConversationNotified(conversationId) +} diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/conversation/SetUserInformedAboutVerificationUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/conversation/SetUserInformedAboutVerificationUseCase.kt new file mode 100644 index 00000000000..a6988a4f7fe --- /dev/null +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/conversation/SetUserInformedAboutVerificationUseCase.kt @@ -0,0 +1,39 @@ +/* + * 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.wire.kalium.logic.data.conversation.ConversationRepository +import com.wire.kalium.logic.data.id.ConversationId + +/** + * UseCase for setting DegradedConversationNotified flag to true, + * means user was notified about verification changes and no need to do it again. + */ +interface SetUserInformedAboutVerificationUseCase { + suspend operator fun invoke(conversationId: ConversationId) +} + +class SetUserInformedAboutVerificationUseCaseImpl internal constructor( + private val conversationRepository: ConversationRepository +) : SetUserInformedAboutVerificationUseCase { + + override suspend fun invoke(conversationId: ConversationId) { + conversationRepository.setDegradedConversationNotifiedFlag(conversationId, true) + } + +} diff --git a/persistence/src/commonMain/db_user/com/wire/kalium/persistence/Conversations.sq b/persistence/src/commonMain/db_user/com/wire/kalium/persistence/Conversations.sq index bc6eb9d1913..bf8b0aaa685 100644 --- a/persistence/src/commonMain/db_user/com/wire/kalium/persistence/Conversations.sq +++ b/persistence/src/commonMain/db_user/com/wire/kalium/persistence/Conversations.sq @@ -42,7 +42,8 @@ CREATE TABLE Conversation ( archived_date_time INTEGER AS Instant, -- mls_verification_status verification_status TEXT AS ConversationEntity.VerificationStatus NOT NULL DEFAULT "NOT_VERIFIED", - proteus_verification_status TEXT AS ConversationEntity.VerificationStatus NOT NULL DEFAULT "NOT_VERIFIED" + proteus_verification_status TEXT AS ConversationEntity.VerificationStatus NOT NULL DEFAULT "NOT_VERIFIED", + degraded_conversation_notified INTEGER AS Boolean NOT NULL DEFAULT 1 ); -- Optimise comparisons and sorting by dates: @@ -141,6 +142,11 @@ UPDATE Conversation SET mls_degraded_notified = ? WHERE qualified_id = :qualified_id; +updateDegradedConversationNotifiedFlag: +UPDATE Conversation +SET degraded_conversation_notified = ? +WHERE qualified_id = :qualified_id; + CREATE VIEW IF NOT EXISTS ConversationDetails AS SELECT Conversation.qualified_id AS qualifiedId, @@ -401,6 +407,10 @@ UPDATE Conversation SET verification_status = :status WHERE qualified_id = :conversationId; +selectDegradedConversationNotified: +SELECT degraded_conversation_notified FROM Conversation +WHERE qualified_id = :conversationId; + clearContent { DELETE FROM Asset WHERE key IN (SELECT asset_id FROM MessageAssetContent WHERE conversation_id = :conversationId); DELETE FROM Message WHERE conversation_id = :conversationId; @@ -430,4 +440,8 @@ BEGIN "SENT", "VISIBLE" ); + + UPDATE Conversation + SET degraded_conversation_notified = (new.proteus_verification_status != "DEGRADED") + WHERE qualified_id = new.qualified_id; END; diff --git a/persistence/src/commonMain/db_user/migrations/65.sqm b/persistence/src/commonMain/db_user/migrations/65.sqm new file mode 100644 index 00000000000..8f81d670470 --- /dev/null +++ b/persistence/src/commonMain/db_user/migrations/65.sqm @@ -0,0 +1,33 @@ +import kotlin.Boolean; + +ALTER TABLE Conversation +ADD COLUMN degraded_conversation_notified INTEGER AS Boolean NOT NULL DEFAULT 1; + +DROP TRIGGER IF EXISTS addMessageAfterProteusVerificationStatusChange; + +CREATE TRIGGER addMessageAfterProteusVerificationStatusChange +AFTER UPDATE ON Conversation +WHEN new.proteus_verification_status != old.proteus_verification_status +AND (new.proteus_verification_status = "VERIFIED" OR old.proteus_verification_status = "VERIFIED") +BEGIN + INSERT OR IGNORE INTO Message(id, content_type, conversation_id, creation_date, sender_user_id, sender_client_id, status, visibility) + VALUES( + (SELECT lower(hex(randomblob(4)) || '-' || lower(hex(randomblob(2))) || '-4' || + substr(lower(hex(randomblob(2))),2) || '-a' || substr(lower(hex(randomblob(2))),2) + || '-' || lower(hex(randomblob(6))))), + (CASE (new.proteus_verification_status) + WHEN "VERIFIED" THEN "CONVERSATION_VERIFIED_PROTEUS" + ELSE "CONVERSATION_DEGRADED_PROTEUS" END + ), + new.qualified_id, + (SELECT CAST((julianday('now') - 2440587.5) * 86400 * 1000 AS INTEGER)), + (SELECT SelfUser.id FROM SelfUser LIMIT 1), + (SELECT Client.id FROM Client WHERE Client.user_id = (SELECT SelfUser.id FROM SelfUser LIMIT 1) LIMIT 1), + "SENT", + "VISIBLE" + ); + + UPDATE Conversation + SET degraded_conversation_notified = (new.proteus_verification_status != "DEGRADED") + WHERE qualified_id = new.qualified_id; +END; diff --git a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAO.kt b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAO.kt index 043859b1ca0..00dd421fb69 100644 --- a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAO.kt +++ b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAO.kt @@ -107,4 +107,6 @@ interface ConversationDAO { suspend fun updateMlsVerificationStatus(verificationStatus: ConversationEntity.VerificationStatus, conversationId: QualifiedIDEntity) suspend fun getConversationByGroupID(groupID: String): ConversationViewEntity suspend fun observeUnreadArchivedConversationsCount(): Flow + suspend fun observeDegradedConversationNotified(conversationId: QualifiedIDEntity): Flow + suspend fun updateDegradedConversationNotifiedFlag(conversationId: QualifiedIDEntity, updateFlag: Boolean) } diff --git a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAOImpl.kt b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAOImpl.kt index f6291c7428a..fef6c661761 100644 --- a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAOImpl.kt +++ b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAOImpl.kt @@ -26,6 +26,7 @@ import com.wire.kalium.persistence.dao.QualifiedIDEntity import com.wire.kalium.persistence.dao.UserIDEntity import com.wire.kalium.persistence.util.mapToList import com.wire.kalium.persistence.util.mapToOne +import com.wire.kalium.persistence.util.mapToOneOrDefault import com.wire.kalium.persistence.util.mapToOneOrNull import com.wire.kalium.util.DateTimeUtil import com.wire.kalium.util.DateTimeUtil.toIsoDateTimeString @@ -373,6 +374,17 @@ internal class ConversationDAOImpl internal constructor( conversationQueries.selectConversationIdsWithoutMetadata().executeAsList() } + override suspend fun updateDegradedConversationNotifiedFlag(conversationId: QualifiedIDEntity, updateFlag: Boolean) = + withContext(coroutineContext) { + conversationQueries.updateDegradedConversationNotifiedFlag(updateFlag, conversationId) + } + + override suspend fun observeDegradedConversationNotified(conversationId: QualifiedIDEntity): Flow = + conversationQueries.selectDegradedConversationNotified(conversationId) + .asFlow() + .mapToOneOrDefault(true) + .flowOn(coroutineContext) + override suspend fun clearContent(conversationId: QualifiedIDEntity) = withContext(coroutineContext) { conversationQueries.clearContent(conversationId) }