Skip to content

Commit

Permalink
feat: composite messages (#1916)
Browse files Browse the repository at this point in the history
* feat: receive and store composite messages [part-1] (#1888)

* receive composite messages

* map composite to proto

* detekt

* detekt

* db migration

* db migration

* sqm

* map to composite entity

* detekt

* remove is pending state from the DB

* Update logic/src/commonMain/kotlin/com/wire/kalium/logic/data/message/MessageContent.kt

Co-authored-by: Vitor Hugo Schwaab <[email protected]>

* Update logic/src/commonMain/kotlin/com/wire/kalium/logic/data/message/MessageContent.kt

Co-authored-by: Vitor Hugo Schwaab <[email protected]>

* address PR comments

* detekt

* remove composite message preview mapping

* fix migration

* merge issues

* address PR comments

* address PR comments

* detekt

---------

Co-authored-by: Vitor Hugo Schwaab <[email protected]>

* feat: use case to send Message Button Action [part-2] (#1896)

* receive composite messages

* map composite to proto

* detekt

* detekt

* db migration

* db migration

* sqm

* map to composite entity

* detekt

* remove is pending state from the DB

* Update logic/src/commonMain/kotlin/com/wire/kalium/logic/data/message/MessageContent.kt

Co-authored-by: Vitor Hugo Schwaab <[email protected]>

* Update logic/src/commonMain/kotlin/com/wire/kalium/logic/data/message/MessageContent.kt

Co-authored-by: Vitor Hugo Schwaab <[email protected]>

* address PR comments

* detekt

* remove composite message preview mapping

* fix migration

* feat: use case to execute composite message action

* expose use case to message scope

* missing param

* rename use case

* docs

* map to proto

* merge issues

* address PR comments

* address PR comments

* detekt

* sent the message only to the message original sender

* detekt

* Update logic/src/commonTest/kotlin/com/wire/kalium/logic/data/message/MessageMetaDataRepositoryTest.kt

Co-authored-by: Alexandre Ferris <[email protected]>

* PR comments

---------

Co-authored-by: Vitor Hugo Schwaab <[email protected]>
Co-authored-by: Alexandre Ferris <[email protected]>

* feat: handle composite action confermation message [part-3] (#1901)

* receive composite messages

* map composite to proto

* detekt

* detekt

* db migration

* db migration

* sqm

* map to composite entity

* detekt

* remove is pending state from the DB

* Update logic/src/commonMain/kotlin/com/wire/kalium/logic/data/message/MessageContent.kt

Co-authored-by: Vitor Hugo Schwaab <[email protected]>

* Update logic/src/commonMain/kotlin/com/wire/kalium/logic/data/message/MessageContent.kt

Co-authored-by: Vitor Hugo Schwaab <[email protected]>

* address PR comments

* detekt

* remove composite message preview mapping

* fix migration

* feat: use case to execute composite message action

* expose use case to message scope

* missing param

* rename use case

* docs

* map to proto

* merge issues

* address PR comments

* address PR comments

* detekt

* handle ButtonActionConfirmation event

* add unit test

* detekt

* detekt

* sent the message only to the message original sender

* detekt

---------

Co-authored-by: Vitor Hugo Schwaab <[email protected]>

* feat: composite message preview [part-4] (#1908)

* receive composite messages

* map composite to proto

* detekt

* detekt

* db migration

* db migration

* sqm

* map to composite entity

* detekt

* remove is pending state from the DB

* Update logic/src/commonMain/kotlin/com/wire/kalium/logic/data/message/MessageContent.kt

Co-authored-by: Vitor Hugo Schwaab <[email protected]>

* Update logic/src/commonMain/kotlin/com/wire/kalium/logic/data/message/MessageContent.kt

Co-authored-by: Vitor Hugo Schwaab <[email protected]>

* address PR comments

* detekt

* remove composite message preview mapping

* fix migration

* feat: use case to execute composite message action

* expose use case to message scope

* missing param

* rename use case

* docs

* map to proto

* merge issues

* address PR comments

* address PR comments

* detekt

* handle ButtonActionConfirmation event

* add unit test

* detekt

* detekt

* sent the message only to the message original sender

* detekt

* feat: map composite messages preview

* Update persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/message/MessageMapper.kt

Co-authored-by: Alexandre Ferris <[email protected]>

---------

Co-authored-by: Vitor Hugo Schwaab <[email protected]>
Co-authored-by: Alexandre Ferris <[email protected]>

* fix: composite messages are not included in the getLastMessages query [part-5] (#1909)

* include composite messages to message preview

* detekt

* feat: display composite message preview

* add test

* feat: composit messages backup [part-6] (#1914)

* fea: include message buttons into the backup

* import buttons

* merge issues

* unit test

* merge issues

* ignore Confirmation messages that are not fom the original message sender

* test

* cleanup

* detekt

* typo

* typo

* PR comments

* trigger CI

* empty

---------

Co-authored-by: Vitor Hugo Schwaab <[email protected]>
Co-authored-by: Alexandre Ferris <[email protected]>
Co-authored-by: Tommaso Piazza <[email protected]>
  • Loading branch information
4 people authored Jul 27, 2023
1 parent e858f56 commit e215508
Show file tree
Hide file tree
Showing 71 changed files with 2,344 additions and 303 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ sealed interface CoreFailure {
*/
object OnlySystemMessageAllowed : FeatureFailure()

/**
* The sender ID of the event is invalid.
* usually happens with events that alter a message state [ButtonActionConfirmation]
* when the sender ID is not the same are the original message sender id
*/
object InvalidEventSenderID : FeatureFailure()
/**
* This operation is not supported by proteus conversations
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ import com.wire.kalium.logic.sync.receiver.EventReceiver
import com.wire.kalium.logic.sync.receiver.conversation.MemberJoinEventHandler
import com.wire.kalium.logic.sync.receiver.conversation.MemberLeaveEventHandler

interface CommitBundleEventReceiver : EventReceiver<Event.Conversation>
internal interface CommitBundleEventReceiver : EventReceiver<Event.Conversation>

class CommitBundleEventReceiverImpl(
internal class CommitBundleEventReceiverImpl(
private val memberJoinEventHandler: MemberJoinEventHandler,
private val memberLeaveEventHandler: MemberLeaveEventHandler
) : CommitBundleEventReceiver {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ private fun CoreFailure.getStrategy(retryOnClientMismatch: Boolean = true): Comm
}

@Suppress("TooManyFunctions", "LongParameterList")
class MLSConversationDataSource(
internal class MLSConversationDataSource(
private val keyPackageRepository: KeyPackageRepository,
private val mlsClientProvider: MLSClientProvider,
private val mlsMessageApi: MLSMessageApi,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,6 @@ data class QualifiedClientID(
val clientId: ClientId,
val userId: UserId
)

typealias MessageId = String
typealias MessageButtonId = String
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,14 @@ data class BroadcastMessage(

is MessageContent.Availability -> mutableMapOf(
typeKey to "availability",
"content" to "$content",
)

is MessageContent.Cleared -> mutableMapOf(
typeKey to "cleared",
"content" to "$content",
)

is MessageContent.Reaction -> mutableMapOf(
typeKey to "reaction",
"content" to "$content",
)

is MessageContent.Receipt -> mutableMapOf(
Expand All @@ -84,8 +81,14 @@ data class BroadcastMessage(
)

MessageContent.Ignored -> mutableMapOf(
typeKey to "ignored",
"content" to "$content",
typeKey to "ignored"
)

is MessageContent.ButtonAction -> mutableMapOf(
typeKey to "buttonAction",
)
is MessageContent.ButtonActionConfirmation -> mutableMapOf(
typeKey to "buttonActionConfirmation",
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* 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.data.message

import com.wire.kalium.logic.StorageFailure
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.id.MessageId
import com.wire.kalium.logic.data.id.toDao
import com.wire.kalium.logic.functional.Either
import com.wire.kalium.logic.wrapStorageRequest
import com.wire.kalium.persistence.dao.message.CompositeMessageDAO

interface CompositeMessageRepository {
suspend fun markSelected(
messageId: MessageId,
conversationId: ConversationId,
buttonId: String
): Either<StorageFailure, Unit>

suspend fun resetSelection(
messageId: MessageId,
conversationId: ConversationId
): Either<StorageFailure, Unit>
}

internal class CompositeMessageDataSource internal constructor(
private val compositeMessageDAO: CompositeMessageDAO
) : CompositeMessageRepository {
override suspend fun markSelected(
messageId: MessageId,
conversationId: ConversationId,
buttonId: String
): Either<StorageFailure, Unit> = wrapStorageRequest {
compositeMessageDAO.markAsSelected(
messageId = messageId,
conversationId = conversationId.toDao(),
buttonId = buttonId
)
}

override suspend fun resetSelection(
messageId: MessageId,
conversationId: ConversationId
): Either<StorageFailure, Unit> = wrapStorageRequest {
compositeMessageDAO.resetSelection(
messageId = messageId,
conversationId = conversationId.toDao()
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ sealed interface Message {
is MessageContent.Unknown -> mutableMapOf(
typeKey to "unknown"
)

is MessageContent.Composite -> mutableMapOf(
typeKey to "composite"
)
}

val standardProperties = mapOf(
Expand Down Expand Up @@ -163,6 +167,7 @@ sealed interface Message {
override val isSelfMessage: Boolean,
override val expirationData: ExpirationData?
) : Sendable {
@Suppress("LongMethod")
override fun toLogString(): String {
return "${toLogMap().toJsonElement()}"
}
Expand Down Expand Up @@ -200,17 +205,14 @@ sealed interface Message {

is MessageContent.Availability -> mutableMapOf(
typeKey to "availability",
"content" to "$content",
)

is MessageContent.Cleared -> mutableMapOf(
typeKey to "cleared",
"content" to "$content",
)

is MessageContent.Reaction -> mutableMapOf(
typeKey to "reaction",
"content" to "$content",
)

is MessageContent.Receipt -> mutableMapOf(
Expand All @@ -220,7 +222,15 @@ sealed interface Message {

MessageContent.Ignored -> mutableMapOf(
typeKey to "ignored",
"content" to "$content",
"content" to content.getType(),
)

is MessageContent.ButtonAction -> mutableMapOf(
typeKey to "buttonAction"
)

is MessageContent.ButtonActionConfirmation -> mutableMapOf(
typeKey to "buttonActionConfirmation"
)
}

Expand Down Expand Up @@ -294,6 +304,7 @@ sealed interface Message {
MessageContent.HistoryLost -> mutableMapOf(
typeKey to "conversationMightLostHistory"
)

is MessageContent.ConversationMessageTimerChanged -> mutableMapOf(
typeKey to "conversationMessageTimerChanged"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@ package com.wire.kalium.logic.data.message
import com.wire.kalium.logger.obfuscateId
import com.wire.kalium.logic.data.conversation.ClientId
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.id.MessageButtonId
import com.wire.kalium.logic.data.id.MessageId
import com.wire.kalium.logic.data.message.mention.MessageMention
import com.wire.kalium.logic.data.message.receipt.ReceiptType
import com.wire.kalium.logic.data.user.UserAvailabilityStatus
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.protobuf.messages.Button
import kotlinx.datetime.Instant

sealed class MessageContent {
Expand Down Expand Up @@ -163,6 +166,54 @@ sealed class MessageContent {

data class Knock(val hotKnock: Boolean) : Regular()

data class Composite(
val textContent: Text?,
val buttonList: List<Button>
) : Regular() {
data class Button(
val text: String,
val id: String,
val isSelected: Boolean
)
}

/**
* Notifies the author of a [Composite] message that a user has
* selected one of its buttons.
* @see Composite
* @see ButtonActionConfirmation
*/
data class ButtonAction(
/**
* The ID of the original composite message.
*/
val referencedMessageId: MessageId,

/**
* ID of the button that was selected.
*/
val buttonId: MessageButtonId
) : Signaling()

/**
* Message sent by the author of a [Composite] to
* notify which button should be marked as selected.
* For example, after we send [ButtonAction], the author might reply
* with [ButtonActionConfirmation] to confirm that the button event was processed.
* @see ButtonAction
* @see Composite
*/
data class ButtonActionConfirmation(
/**
* ID fo the original composite message
*/
val referencedMessageId: MessageId,
/**
* ID of the selected button. Null if no button should be marked as selected.
*/
val buttonId: MessageButtonId?,
) : Signaling()

data class Unknown( // messages that aren't yet handled properly but stored in db in case
val typeName: String? = null,
val encodedData: ByteArray? = null,
Expand Down Expand Up @@ -255,7 +306,6 @@ fun MessageContent?.getType() = when (this) {
is MessageContent.Knock -> "Knock"
is MessageContent.RestrictedAsset -> "RestrictedAsset"
is MessageContent.Text -> "Text"
is MessageContent.Unknown -> "Unknown"
is MessageContent.Availability -> "Availability"
is MessageContent.Calling -> "Calling"
is MessageContent.Cleared -> "Cleared"
Expand Down Expand Up @@ -283,63 +333,70 @@ fun MessageContent?.getType() = when (this) {
is MessageContent.MLSWrongEpochWarning -> "MLSWrongEpochWarning"
is MessageContent.ConversationDegradedMLS -> "ConversationVerification.Degraded.MLS"
is MessageContent.ConversationDegradedProteus -> "ConversationVerification.Degraded.Proteus"
null -> "Unknown"
is MessageContent.Composite -> "Composite"
is MessageContent.ButtonAction -> "ButtonAction"
is MessageContent.ButtonActionConfirmation -> "ButtonActionConfirmation"
is MessageContent.Unknown -> "Unknown"
null -> "null"
}

sealed class MessagePreviewContent {
sealed interface MessagePreviewContent {

sealed interface WithUser : MessagePreviewContent {
val username: String?

sealed class WithUser(open val username: String?) : MessagePreviewContent() {
data class Text(override val username: String?, val messageBody: String) : WithUser

data class Text(override val username: String?, val messageBody: String) : WithUser(username)
data class Composite(override val username: String?, val messageBody: String?) : WithUser

data class Asset(override val username: String?, val type: AssetType) : WithUser(username)
data class Asset(override val username: String?, val type: AssetType) : WithUser

data class MentionedSelf(override val username: String?) : WithUser(username)
data class MentionedSelf(override val username: String?) : WithUser

data class QuotedSelf(override val username: String?) : WithUser(username)
data class QuotedSelf(override val username: String?) : WithUser

data class Knock(override val username: String?) : WithUser(username)
data class Knock(override val username: String?) : WithUser

data class MemberLeft(override val username: String?) : WithUser(username)
data class MemberLeft(override val username: String?) : WithUser

data class MemberJoined(override val username: String?) : WithUser(username)
data class MemberJoined(override val username: String?) : WithUser

data class MembersAdded(
val senderName: String?,
override val username: String?,
val isSelfUserAdded: Boolean,
val otherUserIdList: List<UserId> // TODO add usernames
) : WithUser(senderName)
) : WithUser

data class MembersRemoved(
val senderName: String?,
override val username: String?,
val isSelfUserRemoved: Boolean,
val otherUserIdList: List<UserId> // TODO add usernames
) : WithUser(senderName)
) : WithUser

data class MembersFailedToAdd(
val senderName: String?,
override val username: String?,
val isSelfUserRemoved: Boolean,
val otherUserIdList: List<UserId> // TODO add usernames
) : WithUser(senderName)
) : WithUser

data class MembersCreationAdded(
val senderName: String?,
override val username: String?,
val isSelfUserRemoved: Boolean,
val otherUserIdList: List<UserId> // TODO add usernames
) : WithUser(senderName)
) : WithUser

data class ConversationNameChange(val adminName: String?) : WithUser(adminName)
data class ConversationNameChange(override val username: String?) : WithUser

data class TeamMemberRemoved(val userName: String?) : WithUser(userName)
data class TeamMemberRemoved(override val username: String?) : WithUser

data class MissedCall(override val username: String?) : WithUser(username)
data class MissedCall(override val username: String?) : WithUser

}

data class Ephemeral(val isGroupConversation: Boolean) : MessagePreviewContent()
data class Ephemeral(val isGroupConversation: Boolean) : MessagePreviewContent

object CryptoSessionReset : MessagePreviewContent()
object CryptoSessionReset : MessagePreviewContent

object Unknown : MessagePreviewContent()
object Unknown : MessagePreviewContent

}
Loading

0 comments on commit e215508

Please sign in to comment.