From 69917ebc2e84a073010c9e3f47a724f421694142 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Sat, 2 Jul 2022 00:11:41 +0200 Subject: [PATCH] Start DM - Handle third party invites --- .../sdk/api/session/events/model/EventType.kt | 3 ++ .../LocalRoomThirdPartyInviteContent.kt | 39 +++++++++++++++++++ .../room/create/CreateLocalRoomTask.kt | 33 ++++++++++++++++ .../create/CreateRoomFromLocalRoomTask.kt | 21 ++++++++-- .../helper/TimelineEventVisibilityHelper.kt | 5 ++- 5 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/localecho/LocalRoomThirdPartyInviteContent.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt index 8fdbba21c5..84c25776e7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt @@ -70,6 +70,9 @@ object EventType { const val STATE_ROOM_ENCRYPTION = "m.room.encryption" const val STATE_ROOM_SERVER_ACL = "m.room.server_acl" + // This type is for local purposes, it should never be processed by the server + const val LOCAL_STATE_ROOM_THIRD_PARTY_INVITE = "local.room.third_party_invite" + // Call Events const val CALL_INVITE = "m.call.invite" const val CALL_CANDIDATES = "m.call.candidates" diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/localecho/LocalRoomThirdPartyInviteContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/localecho/LocalRoomThirdPartyInviteContent.kt new file mode 100644 index 0000000000..72e998b377 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/localecho/LocalRoomThirdPartyInviteContent.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.api.session.room.model.localecho + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.api.session.room.model.Membership + +/** + * Class representing the EventType.LOCAL_STATE_ROOM_THIRD_PARTY_INVITE state event content + * This class is only used to store the third party invite data of a local room. + */ +@JsonClass(generateAdapter = true) +data class LocalRoomThirdPartyInviteContent( + @Json(name = "membership") val membership: Membership, + @Json(name = "displayname") val displayName: String? = null, + @Json(name = "is_direct") val isDirect: Boolean = false, + @Json(name = "third_party_invite") val thirdPartyInvite: LocalThreePid? = null, +) + +@JsonClass(generateAdapter = true) +data class LocalThreePid( + val email: String? = null, + val msisdn: String? = null, +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateLocalRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateLocalRoomTask.kt index e1429d8e5c..4f8dc11d2c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateLocalRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateLocalRoomTask.kt @@ -34,8 +34,11 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomMemberContent +import org.matrix.android.sdk.api.session.room.model.RoomThirdPartyInviteContent import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent +import org.matrix.android.sdk.api.session.room.model.localecho.LocalRoomThirdPartyInviteContent +import org.matrix.android.sdk.api.session.room.model.localecho.LocalThreePid import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.sync.model.RoomSyncSummary @@ -204,6 +207,7 @@ internal class DefaultCreateLocalRoomTask @Inject constructor( val myUser = userService.getUser(userId) ?: User(userId) val invitedUsers = createRoomBody.invitedUserIds.orEmpty() .mapNotNull { tryOrNull { userService.resolveUser(it) } } + val invited3Pids = createRoomBody.invite3pids.orEmpty() val createRoomEvent = createLocalEvent( type = EventType.STATE_ROOM_CREATE, @@ -233,11 +237,40 @@ internal class DefaultCreateLocalRoomTask @Inject constructor( ) } + val localRoomThreePidEvents = invited3Pids.map { body -> + createLocalEvent( + type = EventType.LOCAL_STATE_ROOM_THIRD_PARTY_INVITE, + content = LocalRoomThirdPartyInviteContent( + isDirect = createRoomBody.isDirect.orFalse(), + membership = Membership.INVITE, + displayName = body.address, + thirdPartyInvite = LocalThreePid( + msisdn = body.address.takeIf { body.medium == "msisdn" }, + email = body.address.takeIf { body.medium == "email" } + ) + ).toContent() + ) + } + + val roomThreePidEvents = invited3Pids.map { body -> + createLocalEvent( + type = EventType.STATE_ROOM_THIRD_PARTY_INVITE, + content = RoomThirdPartyInviteContent( + displayName = body.address, + keyValidityUrl = null, + publicKey = null, + publicKeys = null + ).toContent() + ) + } + return buildList { add(createRoomEvent) add(myRoomMemberEvent) addAll(createRoomBody.initialStates.orEmpty().map { createLocalEvent(it.type, it.content, it.stateKey) }) addAll(roomMemberEvents) + addAll(roomThreePidEvents) + addAll(localRoomThreePidEvents) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomFromLocalRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomFromLocalRoomTask.kt index 0720f2b5ec..1d569370db 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomFromLocalRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomFromLocalRoomTask.kt @@ -44,6 +44,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomNameContent import org.matrix.android.sdk.api.session.room.model.RoomTopicContent import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset +import org.matrix.android.sdk.api.session.room.model.localecho.LocalRoomThirdPartyInviteContent import org.matrix.android.sdk.api.session.room.model.tombstone.RoomTombstoneContent import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.internal.database.awaitNotEmptyResult @@ -104,13 +105,11 @@ internal class DefaultCreateRoomFromLocalRoomTask @Inject constructor( val roomId = createRoomTask.execute(createRoomParams) try { + // Wait for all the room events before triggering the replacement room awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm -> realm.where(RoomSummaryEntity::class.java) .equalTo(RoomSummaryEntityFields.ROOM_ID, roomId) - .equalTo( - RoomSummaryEntityFields.INVITED_MEMBERS_COUNT, - createRoomParams.invitedUserIds.size.minus(1) + createRoomParams.invite3pids.size - ) + .equalTo(RoomSummaryEntityFields.INVITED_MEMBERS_COUNT, createRoomParams.invitedUserIds.size.minus(1)) } awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm -> EventEntity.whereRoomId(realm, roomId) @@ -140,6 +139,7 @@ internal class DefaultCreateRoomFromLocalRoomTask @Inject constructor( stateEvents.forEach { event -> createRoomParams = when (event.type) { EventType.STATE_ROOM_MEMBER -> handleRoomMemberEvent(realm, event, createRoomParams) + EventType.LOCAL_STATE_ROOM_THIRD_PARTY_INVITE -> handleLocalRoomThirdPartyInviteEvent(realm, event, createRoomParams) EventType.STATE_ROOM_HISTORY_VISIBILITY -> handleRoomHistoryVisibilityEvent(realm, event, createRoomParams) EventType.STATE_ROOM_ALIASES -> handleRoomAliasesEvent(realm, event, createRoomParams) EventType.STATE_ROOM_AVATAR -> handleRoomAvatarEvent(realm, event, createRoomParams) @@ -196,6 +196,19 @@ internal class DefaultCreateRoomFromLocalRoomTask @Inject constructor( } } + private fun handleLocalRoomThirdPartyInviteEvent(realm: Realm, event: CurrentStateEventEntity, params: CreateRoomParams): CreateRoomParams = params.apply { + val content = getEventContent(realm, event.eventId) ?: return@apply + val threePid = when { + content.thirdPartyInvite?.email != null -> ThreePid.Email(content.thirdPartyInvite.email) + content.thirdPartyInvite?.msisdn != null -> ThreePid.Msisdn(content.thirdPartyInvite.msisdn) + else -> return@apply + } + invite3pids.add(threePid) + if (content.isDirect) { + setDirectMessage() + } + } + private fun handleRoomHistoryVisibilityEvent(realm: Realm, event: CurrentStateEventEntity, params: CreateRoomParams): CreateRoomParams = params.apply { val content = getEventContent(realm, event.eventId) ?: return@apply historyVisibility = content.historyVisibility diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt index e6765bf35a..d22b649b36 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt @@ -229,8 +229,9 @@ class TimelineEventVisibilityHelper @Inject constructor( // Hide fake events for local rooms if (RoomLocalEcho.isLocalEchoId(roomId) && - root.getClearType() == EventType.STATE_ROOM_MEMBER || - root.getClearType() == EventType.STATE_ROOM_HISTORY_VISIBILITY) { + (root.getClearType() == EventType.STATE_ROOM_MEMBER || + root.getClearType() == EventType.STATE_ROOM_HISTORY_VISIBILITY || + root.getClearType() == EventType.STATE_ROOM_THIRD_PARTY_INVITE)) { return true }