From 69c84a246021bdf51c59030fa6e17107d992fb30 Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 26 Jul 2021 17:19:06 +0200 Subject: [PATCH 1/6] support email invite --- .../matrix/android/sdk/PermalinkParserTest.kt | 60 ++++++++++++++ .../api/session/identity/IdentityService.kt | 5 ++ .../api/session/permalinks/PermalinkData.kt | 21 +++++ .../api/session/permalinks/PermalinkParser.kt | 57 +++++++++++-- .../sdk/api/session/room/RoomService.kt | 13 +++ .../sdk/internal/network/NetworkConstants.kt | 1 + .../identity/DefaultIdentityService.kt | 10 +++ .../internal/session/identity/IdentityAPI.kt | 14 ++++ .../identity/Sign3pidInvitationTask.kt | 49 ++++++++++++ .../identity/model/SignInvitationBody.kt | 29 +++++++ .../identity/model/SignInvitationResult.kt | 31 +++++++ .../session/room/DefaultRoomService.kt | 7 ++ .../sdk/internal/session/room/RoomAPI.kt | 2 +- .../sdk/internal/session/room/RoomModule.kt | 5 ++ .../room/membership/joining/JoinRoomTask.kt | 11 ++- .../matrixto/MatrixToBottomSheetViewModel.kt | 1 + .../features/navigation/DefaultNavigator.kt | 3 +- .../app/features/navigation/Navigator.kt | 3 +- .../features/permalink/PermalinkHandler.kt | 20 ++++- .../roompreview/RoomPreviewAction.kt | 1 + .../roompreview/RoomPreviewActivity.kt | 5 +- .../RoomPreviewNoPreviewFragment.kt | 62 +++++++++++--- .../roompreview/RoomPreviewViewModel.kt | 80 ++++++++++++++++++- .../roompreview/RoomPreviewViewState.kt | 16 +++- .../settings/VectorSettingsActivity.kt | 5 ++ vector/src/main/res/values/strings.xml | 6 ++ 26 files changed, 484 insertions(+), 33 deletions(-) create mode 100644 matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/PermalinkParserTest.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/Sign3pidInvitationTask.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/model/SignInvitationBody.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/model/SignInvitationResult.kt diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/PermalinkParserTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/PermalinkParserTest.kt new file mode 100644 index 0000000000..b11a538949 --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/PermalinkParserTest.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2021 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 + +import org.junit.Assert +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runners.MethodSorters +import org.matrix.android.sdk.api.session.permalinks.PermalinkData +import org.matrix.android.sdk.api.session.permalinks.PermalinkParser + +@FixMethodOrder(MethodSorters.JVM) +class PermalinkParserTest { + + @Test + fun testParseEmailInvite() { + val rawInvite = """ + https://app.element.io/#/room/%21MRBNLPtFnMAazZVPMO%3Amatrix.org?email=bob%2Bspace%40example.com&signurl=https%3A%2F%2Fvector.im%2F_matrix%2Fidentity%2Fapi%2Fv1%2Fsign-ed25519%3Ftoken%3DXmOwRZnSFabCRhTywFbJWKXWVNPysOpXIbroMGaUymqkJSvHeVKRsjHajwjCYdBsvGSvHauxbKfJmOxtXldtyLnyBMLKpBQCMzyYggrdapbVIceWZBtmslOQrXLABRoe%26private_key%3DT2gq0c3kJB_8OroXVxl1pBnzHsN7V6Xn4bEBSeW1ep4&room_name=Team2&room_avatar_url=&inviter_name=hiphop5&guest_access_token=&guest_user_id= + """.trimIndent() + .replace("https://app.element.io/#/room/", "https://matrix.to/#/") + + val parsedLink = PermalinkParser.parse(rawInvite) + Assert.assertTrue("Should be parsed as email invite but was ${parsedLink::class.java}", parsedLink is PermalinkData.RoomEmailInviteLink) + parsedLink as PermalinkData.RoomEmailInviteLink + Assert.assertEquals("!MRBNLPtFnMAazZVPMO:matrix.org", parsedLink.roomId) + Assert.assertEquals("XmOwRZnSFabCRhTywFbJWKXWVNPysOpXIbroMGaUymqkJSvHeVKRsjHajwjCYdBsvGSvHauxbKfJmOxtXldtyLnyBMLKpBQCMzyYggrdapbVIceWZBtmslOQrXLABRoe", parsedLink.token) + Assert.assertEquals("vector.im", parsedLink.identityServer) + Assert.assertEquals("Team2", parsedLink.roomName) + Assert.assertEquals("hiphop5", parsedLink.inviterName) + } + + @Test + fun testParseLinkWIthEvent() { + val rawInvite = "https://matrix.to/#/!OGEhHVWSdvArJzumhm:matrix.org/\$xuvJUVDJnwEeVjPx029rAOZ50difpmU_5gZk_T0jGfc?via=matrix.org&via=libera.chat&via=matrix.example.io" + + val parsedLink = PermalinkParser.parse(rawInvite) + Assert.assertTrue("Should be parsed as room link", parsedLink is PermalinkData.RoomLink) + parsedLink as PermalinkData.RoomLink + Assert.assertEquals("!OGEhHVWSdvArJzumhm:matrix.org", parsedLink.roomIdOrAlias) + Assert.assertEquals("\$xuvJUVDJnwEeVjPx029rAOZ50difpmU_5gZk_T0jGfc", parsedLink.eventId) + Assert.assertEquals(3, parsedLink.viaParameters.size) + Assert.assertTrue(parsedLink.viaParameters.contains("matrix.example.io")) + Assert.assertTrue(parsedLink.viaParameters.contains("matrix.org")) + Assert.assertTrue(parsedLink.viaParameters.contains("matrix.example.io")) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt index ae546b6cec..15ca748e4e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt @@ -16,6 +16,8 @@ package org.matrix.android.sdk.api.session.identity +import org.matrix.android.sdk.internal.session.identity.model.SignInvitationResult + /** * Provides access to the identity server configuration and services identity server can provide */ @@ -121,6 +123,9 @@ interface IdentityService { */ suspend fun getShareStatus(threePids: List): Map + suspend fun sign3pidInvitation(identiyServer: String, token: String, secret: String) : SignInvitationResult + fun addListener(listener: IdentityServiceListener) + fun removeListener(listener: IdentityServiceListener) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkData.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkData.kt index b3fbdcf1a2..85291cf0f6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkData.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkData.kt @@ -17,6 +17,8 @@ package org.matrix.android.sdk.api.session.permalinks import android.net.Uri +import android.os.Parcelable +import kotlinx.parcelize.Parcelize /** * This sealed class represents all the permalink cases. @@ -31,6 +33,25 @@ sealed class PermalinkData { val viaParameters: List ) : PermalinkData() + /** + * &room_name=Team2 + &room_avatar_url=mxc: + &inviter_name=bob + */ + @Parcelize + data class RoomEmailInviteLink( + val roomId: String, + val email: String, + val signUrl: String, + val roomName: String?, + val roomAvatarUrl: String?, + val inviterName: String?, + val identityServer: String, + val token: String, + val privateKey: String, + val roomType: String? + ) : PermalinkData(), Parcelable + data class UserLink(val userId: String) : PermalinkData() data class GroupLink(val groupId: String) : PermalinkData() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt index 347a3bb531..99cca715b8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt @@ -19,6 +19,8 @@ package org.matrix.android.sdk.api.session.permalinks import android.net.Uri import android.net.UrlQuerySanitizer import org.matrix.android.sdk.api.MatrixPatterns +import timber.log.Timber +import java.net.URLDecoder /** * This class turns an uri to a [PermalinkData] @@ -35,12 +37,15 @@ object PermalinkParser { /** * Turns an uri to a [PermalinkData] + * https://github.com/matrix-org/matrix-doc/blob/master/proposals/1704-matrix.to-permalinks.md */ fun parse(uri: Uri): PermalinkData { if (!uri.toString().startsWith(PermalinkService.MATRIX_TO_URL_BASE)) { return PermalinkData.FallbackLink(uri) } - val fragment = uri.fragment + // We can't use uri.fragment as it is decoding to early and it will break the parsing + // of parameters that represents url (like signurl) + val fragment = uri.toString().substringAfter("#") // uri.fragment if (fragment.isNullOrEmpty()) { return PermalinkData.FallbackLink(uri) } @@ -51,6 +56,7 @@ object PermalinkParser { val params = safeFragment .split(MatrixPatterns.SEP_REGEX) .filter { it.isNotEmpty() } + .map { URLDecoder.decode(it, "UTF-8") } .take(2) val identifier = params.getOrNull(0) @@ -60,12 +66,45 @@ object PermalinkParser { MatrixPatterns.isUserId(identifier) -> PermalinkData.UserLink(userId = identifier) MatrixPatterns.isGroupId(identifier) -> PermalinkData.GroupLink(groupId = identifier) MatrixPatterns.isRoomId(identifier) -> { - PermalinkData.RoomLink( - roomIdOrAlias = identifier, - isRoomAlias = false, - eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) }, - viaParameters = viaQueryParameters - ) + // Can't rely on built in parsing because it's messing around the signurl + val paramList = fragment.substringAfter("?").split('&').mapNotNull { + val splitNameValue = it.split("=") + if (splitNameValue.size == 2) { + Pair(splitNameValue[0], URLDecoder.decode(splitNameValue[1], "UTF-8")) + } else null + } + val signUrl = paramList.firstOrNull { it.first == "signurl" }?.second + val email = paramList.firstOrNull { it.first == "email" }?.second + if (signUrl.isNullOrEmpty().not() && email.isNullOrEmpty().not()) { + try { + val signValidUri = Uri.parse(signUrl) + val identityServerHost = signValidUri.authority ?: throw IllegalArgumentException() + val token = signValidUri.getQueryParameter("token") ?: throw IllegalArgumentException() + val privateKey = signValidUri.getQueryParameter("private_key") ?: throw IllegalArgumentException() + PermalinkData.RoomEmailInviteLink( + roomId = identifier, + email = email!!, + signUrl = signUrl!!, + roomName = paramList.firstOrNull { it.first == "room_name" }?.second, + inviterName = paramList.firstOrNull { it.first == "inviter_name" }?.second, + roomAvatarUrl = paramList.firstOrNull { it.first == "room_avatar_url" }?.second, + roomType = paramList.firstOrNull { it.first == "room_type" }?.second, + identityServer = identityServerHost, + token = token, + privateKey = privateKey + ) + } catch (failure: Throwable) { + Timber.i("## Permalink: Failed to parse permalink $signUrl") + PermalinkData.FallbackLink(uri) + } + } else { + PermalinkData.RoomLink( + roomIdOrAlias = identifier, + isRoomAlias = false, + eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) }, + viaParameters = viaQueryParameters + ) + } } MatrixPatterns.isRoomAlias(identifier) -> { PermalinkData.RoomLink( @@ -85,7 +124,9 @@ object PermalinkParser { .filter { it.mParameter == "via" }.map { - it.mValue + it.mValue.let { + URLDecoder.decode(it, "UTF-8") + } } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt index b7377df1b3..5d26b21349 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt @@ -27,6 +27,7 @@ import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.peeking.PeekResult import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount import org.matrix.android.sdk.api.util.Optional +import org.matrix.android.sdk.internal.session.identity.model.SignInvitationResult import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription /** @@ -63,6 +64,18 @@ interface RoomService { reason: String? = null, viaServers: List = emptyList()) + /** + * @param roomId the roomId of the room to join + * @param reason optional reason for joining the room + * @param thirdPartySigned A signature of an m.third_party_invite token to prove that this user owns a third party identity + * which has been invited to the room. + */ + suspend fun joinRoom( + roomId: String, + reason: String? = null, + thirdPartySigned: SignInvitationResult + ) + /** * Get a room from a roomId * @param roomId the roomId to look for. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConstants.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConstants.kt index 99c12255cd..361a306d4f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConstants.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConstants.kt @@ -30,6 +30,7 @@ internal object NetworkConstants { // Identity server const val URI_IDENTITY_PREFIX_PATH = "_matrix/identity/v2" const val URI_IDENTITY_PATH_V2 = "$URI_IDENTITY_PREFIX_PATH/" + const val URI_IDENTITY_PATH_V1 = "_matrix/identity/api/v1/" // Push Gateway const val URI_PUSH_GATEWAY_PREFIX_PATH = "_matrix/push/v1/" diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt index 48870b86b7..fdb6caf53f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt @@ -52,6 +52,7 @@ import kotlinx.coroutines.withContext import okhttp3.OkHttpClient import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.internal.session.identity.model.SignInvitationResult import timber.log.Timber import javax.inject.Inject import javax.net.ssl.HttpsURLConnection @@ -79,6 +80,7 @@ internal class DefaultIdentityService @Inject constructor( private val identityApiProvider: IdentityApiProvider, private val accountDataDataSource: UserAccountDataDataSource, private val homeServerCapabilitiesService: HomeServerCapabilitiesService, + private val sign3pidInvitationTask: DefaultSign3pidInvitationTask, private val sessionParams: SessionParams ) : IdentityService, SessionLifecycleObserver { @@ -290,6 +292,14 @@ internal class DefaultIdentityService @Inject constructor( return token.token } + override suspend fun sign3pidInvitation(identiyServer: String, token: String, secret: String): SignInvitationResult { + return sign3pidInvitationTask.execute(Sign3pidInvitationTask.Params( + url = identiyServer, + token = token, + privateKey = secret + )) + } + override fun addListener(listener: IdentityServiceListener) { listeners.add(listener) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityAPI.kt index e9e4d17e3a..99bd740463 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityAPI.kt @@ -26,10 +26,12 @@ import org.matrix.android.sdk.internal.session.identity.model.IdentityRequestOwn import org.matrix.android.sdk.internal.session.identity.model.IdentityRequestTokenForEmailBody import org.matrix.android.sdk.internal.session.identity.model.IdentityRequestTokenForMsisdnBody import org.matrix.android.sdk.internal.session.identity.model.IdentityRequestTokenResponse +import org.matrix.android.sdk.internal.session.identity.model.SignInvitationResult import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST import retrofit2.http.Path +import retrofit2.http.Query /** * Ref: https://matrix.org/docs/spec/identity_service/latest @@ -95,4 +97,16 @@ internal interface IdentityAPI { @POST(NetworkConstants.URI_IDENTITY_PATH_V2 + "validate/{medium}/submitToken") suspend fun submitToken(@Path("medium") medium: String, @Body body: IdentityRequestOwnershipParams): SuccessResult + + /** + * https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-sign-ed25519 + * + * Have to rely on V1 for now + */ + @POST(NetworkConstants.URI_IDENTITY_PATH_V1 + "sign-ed25519") + suspend fun signInvitationDetails( + @Query("token") token: String, + @Query("private_key") privateKey: String, + @Query("mxid") mxid: String + ): SignInvitationResult } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/Sign3pidInvitationTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/Sign3pidInvitationTask.kt new file mode 100644 index 0000000000..a8bb1d475b --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/Sign3pidInvitationTask.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2021 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.internal.session.identity + +import dagger.Lazy +import okhttp3.OkHttpClient +import org.matrix.android.sdk.internal.di.AuthenticatedIdentity +import org.matrix.android.sdk.internal.di.UserId +import org.matrix.android.sdk.internal.network.RetrofitFactory +import org.matrix.android.sdk.internal.session.identity.model.SignInvitationResult +import org.matrix.android.sdk.internal.task.Task +import javax.inject.Inject + +internal interface Sign3pidInvitationTask : Task { + data class Params( + val token: String, + val url: String, + val privateKey: String + ) +} + +internal class DefaultSign3pidInvitationTask @Inject constructor( + @AuthenticatedIdentity + private val okHttpClient: Lazy, + private val retrofitFactory: RetrofitFactory, + @UserId private val userId: String +) : Sign3pidInvitationTask { + + override suspend fun execute(params: Sign3pidInvitationTask.Params): SignInvitationResult { + val identityAPI = params.url + .let { retrofitFactory.create(okHttpClient, "https://$it") } + .create(IdentityAPI::class.java) + return identityAPI.signInvitationDetails(params.token, params.privateKey, userId) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/model/SignInvitationBody.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/model/SignInvitationBody.kt new file mode 100644 index 0000000000..6998257263 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/model/SignInvitationBody.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2021 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.internal.session.identity.model + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +internal data class SignInvitationBody( + /**The Matrix user ID of the user accepting the invitation.*/ + val mxid: String, + /**The token from the call to store- invite..*/ + val token: String, + /** The private key, encoded as Unpadded base64. */ + val private_key: String +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/model/SignInvitationResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/model/SignInvitationResult.kt new file mode 100644 index 0000000000..27a3f3209f --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/model/SignInvitationResult.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2021 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.internal.session.identity.model + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class SignInvitationResult( + /** The Matrix user ID of the user accepting the invitation.*/ + val mxid: String, + /** The Matrix user ID of the user who sent the invitation.*/ + val sender: String, + /**The token from the call to store- invite..*/ + val signatures: Map, + /** The token for the invitation */ + val token: String +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt index 632ea4c450..5ea38ff682 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt @@ -38,6 +38,7 @@ import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields import org.matrix.android.sdk.internal.di.SessionDatabase +import org.matrix.android.sdk.internal.session.identity.model.SignInvitationResult import org.matrix.android.sdk.internal.session.room.alias.DeleteRoomAliasTask import org.matrix.android.sdk.internal.session.room.alias.GetRoomIdByAliasTask import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription @@ -122,6 +123,12 @@ internal class DefaultRoomService @Inject constructor( joinRoomTask.execute(JoinRoomTask.Params(roomIdOrAlias, reason, viaServers)) } + override suspend fun joinRoom(roomIdOrAlias: String, + reason: String?, + thirdPartySigned: SignInvitationResult) { + joinRoomTask.execute(JoinRoomTask.Params(roomIdOrAlias, reason, thirdPartySigned = thirdPartySigned)) + } + override suspend fun markAllAsRead(roomIds: List) { markAllRoomsReadTask.execute(MarkAllRoomsReadTask.Params(roomIds)) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt index 82cafa9dfc..23bded7a79 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt @@ -254,7 +254,7 @@ internal interface RoomAPI { @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "join/{roomIdOrAlias}") suspend fun join(@Path("roomIdOrAlias") roomIdOrAlias: String, @Query("server_name") viaServers: List, - @Body params: Map): JoinRoomResponse + @Body params: @JvmSuppressWildcards Map): JoinRoomResponse /** * Leave the given room. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt index c04c899e18..794970705c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt @@ -28,6 +28,8 @@ import org.matrix.android.sdk.api.session.space.SpaceService import org.matrix.android.sdk.internal.session.DefaultFileService import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.directory.DirectoryAPI +import org.matrix.android.sdk.internal.session.identity.DefaultSign3pidInvitationTask +import org.matrix.android.sdk.internal.session.identity.Sign3pidInvitationTask import org.matrix.android.sdk.internal.session.room.accountdata.DefaultUpdateRoomAccountDataTask import org.matrix.android.sdk.internal.session.room.accountdata.UpdateRoomAccountDataTask import org.matrix.android.sdk.internal.session.room.alias.AddRoomAliasTask @@ -248,4 +250,7 @@ internal abstract class RoomModule { @Binds abstract fun bindRoomVersionUpgradeTask(task: DefaultRoomVersionUpgradeTask): RoomVersionUpgradeTask + + @Binds + abstract fun bindSign3pidInvitationTask(task: DefaultSign3pidInvitationTask): Sign3pidInvitationTask } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt index 562b25683b..209a904fad 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.room.membership.joining import io.realm.Realm import io.realm.RealmConfiguration import kotlinx.coroutines.TimeoutCancellationException +import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.room.failure.JoinRoomFailure import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.Membership @@ -29,6 +30,7 @@ import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.session.identity.model.SignInvitationResult import org.matrix.android.sdk.internal.session.room.RoomAPI import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembershipStateDataSource import org.matrix.android.sdk.internal.session.room.read.SetReadMarkersTask @@ -40,7 +42,8 @@ internal interface JoinRoomTask : Task { data class Params( val roomIdOrAlias: String, val reason: String?, - val viaServers: List = emptyList() + val viaServers: List = emptyList(), + val thirdPartySigned : SignInvitationResult? = null ) } @@ -59,12 +62,16 @@ internal class DefaultJoinRoomTask @Inject constructor( return } roomChangeMembershipStateDataSource.updateState(params.roomIdOrAlias, ChangeMembershipState.Joining) + val extraParams = mutableMapOf().apply { + params.reason?.let { this["reason"] = it } + params.thirdPartySigned?.let { this["third_party_signed"] = it.toContent() } + } val joinRoomResponse = try { executeRequest(globalErrorReceiver) { roomAPI.join( roomIdOrAlias = params.roomIdOrAlias, viaServers = params.viaServers.take(3), - params = mapOf("reason" to params.reason) + params = extraParams ) } } catch (failure: Throwable) { diff --git a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt index 1c78348b56..566cf769f2 100644 --- a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt @@ -183,6 +183,7 @@ class MatrixToBottomSheetViewModel @AssistedInject constructor( // not yet supported _viewEvents.post(MatrixToViewEvents.Dismiss) } + is PermalinkData.RoomEmailInviteLink, is PermalinkData.FallbackLink -> { _viewEvents.post(MatrixToViewEvents.Dismiss) } diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index 01290a57ea..fd163f7a34 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -86,6 +86,7 @@ import im.vector.app.features.widgets.WidgetActivity import im.vector.app.features.widgets.WidgetArgsBuilder import im.vector.app.space import org.matrix.android.sdk.api.session.crypto.verification.IncomingSasVerificationTransaction +import org.matrix.android.sdk.api.session.permalinks.PermalinkData import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom import org.matrix.android.sdk.api.session.terms.TermsService import org.matrix.android.sdk.api.session.widgets.model.Widget @@ -249,7 +250,7 @@ class DefaultNavigator @Inject constructor( context.startActivity(intent) } - override fun openRoomPreview(context: Context, roomPreviewData: RoomPreviewData) { + override fun openRoomPreview(context: Context, roomPreviewData: RoomPreviewData, fromEmailInviteLink: PermalinkData.RoomEmailInviteLink?) { val intent = RoomPreviewActivity.newIntent(context, roomPreviewData) context.startActivity(intent) } diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index cf0263a1e8..ffe9562761 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -30,6 +30,7 @@ import im.vector.app.features.roomdirectory.RoomDirectoryData import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.share.SharedData +import org.matrix.android.sdk.api.session.permalinks.PermalinkData import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom import org.matrix.android.sdk.api.session.terms.TermsService import org.matrix.android.sdk.api.session.widgets.model.Widget @@ -65,7 +66,7 @@ interface Navigator { fun openRoomPreview(context: Context, publicRoom: PublicRoom, roomDirectoryData: RoomDirectoryData) - fun openRoomPreview(context: Context, roomPreviewData: RoomPreviewData) + fun openRoomPreview(context: Context, roomPreviewData: RoomPreviewData, fromEmailInviteLink: PermalinkData.RoomEmailInviteLink? = null) fun openMatrixToBottomSheet(context: Context, link: String) diff --git a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt index aa977730db..ecaeea1899 100644 --- a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt +++ b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt @@ -22,6 +22,7 @@ import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.utils.toast import im.vector.app.features.navigation.Navigator +import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers @@ -77,7 +78,7 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti buildTask: Boolean ): Single { return when (permalinkData) { - is PermalinkData.RoomLink -> { + is PermalinkData.RoomLink -> { permalinkData.getRoomId() .observeOn(AndroidSchedulers.mainThread()) .map { @@ -94,19 +95,30 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti true } } - is PermalinkData.GroupLink -> { + is PermalinkData.GroupLink -> { navigator.openGroupDetail(permalinkData.groupId, context, buildTask) Single.just(true) } - is PermalinkData.UserLink -> { + is PermalinkData.UserLink -> { if (navigationInterceptor?.navToMemberProfile(permalinkData.userId, rawLink) != true) { navigator.openRoomMemberProfile(userId = permalinkData.userId, roomId = null, context = context, buildTask = buildTask) } Single.just(true) } - is PermalinkData.FallbackLink -> { + is PermalinkData.FallbackLink -> { Single.just(false) } + is PermalinkData.RoomEmailInviteLink -> { + val data = RoomPreviewData( + roomId = permalinkData.roomId, + roomName = permalinkData.roomName, + avatarUrl = permalinkData.roomAvatarUrl, + fromEmailInvite = permalinkData, + roomType = permalinkData.roomType + ) + navigator.openRoomPreview(context, data) + Single.just(true) + } } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewAction.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewAction.kt index 8e0d2db67e..a44affff12 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewAction.kt @@ -20,4 +20,5 @@ import im.vector.app.core.platform.VectorViewModelAction sealed class RoomPreviewAction : VectorViewModelAction { object Join : RoomPreviewAction() + object JoinThirdParty : RoomPreviewAction() } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt index d56870d623..0121d5d795 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt @@ -27,6 +27,7 @@ import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivitySimpleBinding import im.vector.app.features.roomdirectory.RoomDirectoryData import kotlinx.parcelize.Parcelize +import org.matrix.android.sdk.api.session.permalinks.PermalinkData import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom import org.matrix.android.sdk.api.util.MatrixItem import timber.log.Timber @@ -37,12 +38,14 @@ data class RoomPreviewData( val eventId: String? = null, val roomName: String? = null, val roomAlias: String? = null, + val roomType: String? = null, val topic: String? = null, val worldReadable: Boolean = false, val avatarUrl: String? = null, val homeServers: List = emptyList(), val peekFromServer: Boolean = false, - val buildTask: Boolean = false + val buildTask: Boolean = false, + val fromEmailInvite: PermalinkData.RoomEmailInviteLink? = null ) : Parcelable { val matrixItem: MatrixItem get() = MatrixItem.RoomItem(roomId, roomName ?: roomAlias, avatarUrl) diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt index e1536281ef..07c4c65a28 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt @@ -16,12 +16,15 @@ package im.vector.app.features.roomdirectory.roompreview +import android.graphics.Typeface import android.os.Bundle +import android.text.method.LinkMovementMethod +import android.text.style.ClickableSpan import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.text.toSpannable import androidx.core.view.isVisible -import androidx.transition.TransitionManager import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Success import com.airbnb.mvrx.args @@ -31,10 +34,16 @@ import im.vector.app.R import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.platform.ButtonStateView import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.core.utils.styleMatchingText +import im.vector.app.core.utils.tappableMatchingText import im.vector.app.databinding.FragmentRoomPreviewNoPreviewBinding import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.navigation.Navigator import im.vector.app.features.roomdirectory.JoinState - +import im.vector.app.features.settings.VectorSettingsActivity +import im.vector.app.features.themes.ThemeUtils +import me.gujun.android.span.span +import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.util.MatrixItem import javax.inject.Inject @@ -61,7 +70,6 @@ class RoomPreviewNoPreviewFragment @Inject constructor( } override fun invalidate() = withState(roomPreviewViewModel) { state -> - TransitionManager.beginDelayedTransition(views.coordinatorLayout) views.roomPreviewNoPreviewJoin.render( when (state.roomJoinState) { @@ -83,7 +91,11 @@ class RoomPreviewNoPreviewFragment @Inject constructor( // Quit this screen requireActivity().finish() // Open room - navigator.openRoom(requireActivity(), state.roomId, roomPreviewData.eventId, roomPreviewData.buildTask) + if (state.roomType == RoomType.SPACE) { + navigator.switchToSpace(requireActivity(), state.roomId, Navigator.PostSwitchSpaceAction.None) + } else { + navigator.openRoom(requireActivity(), state.roomId, roomPreviewData.eventId, roomPreviewData.buildTask) + } } val bestName = state.roomName ?: state.roomAlias ?: state.roomId @@ -98,19 +110,51 @@ class RoomPreviewNoPreviewFragment @Inject constructor( PeekingState.FOUND -> { // show join buttons views.roomPreviewNoPreviewJoin.isVisible = true - renderState(bestName, state.matrixItem(), state.roomTopic) + renderState(bestName, state.matrixItem(), state.roomTopic, state.roomType) + if (state.fromEmailInvite != null && !state.isEmailBoundToAccount) { + views.roomPreviewNoPreviewLabel.text = + span { + span { + textColor = ThemeUtils.getColor(requireContext(), R.attr.vctr_content_primary) + text = if (state.roomType == RoomType.SPACE) { + getString(R.string.this_invite_to_this_space_was_sent, state.fromEmailInvite.email) + } else { + getString(R.string.this_invite_to_this_room_was_sent, state.fromEmailInvite.email) + } + .toSpannable() + .styleMatchingText(state.fromEmailInvite.email, Typeface.BOLD) + } + +"\n" + span { + text = getString(R.string.link_this_email_with_your_account) + .toSpannable() + .tappableMatchingText(getString(R.string.link_this_email_settings_link), object : ClickableSpan() { + override fun onClick(widget: View) { + navigator.openSettings( + requireContext(), + VectorSettingsActivity.EXTRA_DIRECT_ACCESS_DISCOVERY_SETTINGS + ) + } + }) + } + } + views.roomPreviewNoPreviewLabel.movementMethod = LinkMovementMethod.getInstance() + views.roomPreviewNoPreviewJoin.commonClicked = { + roomPreviewViewModel.handle(RoomPreviewAction.JoinThirdParty) + } + } } PeekingState.NO_ACCESS -> { views.roomPreviewNoPreviewJoin.isVisible = true views.roomPreviewNoPreviewLabel.isVisible = true views.roomPreviewNoPreviewLabel.setText(R.string.room_preview_no_preview_join) - renderState(bestName, state.matrixItem().takeIf { state.roomAlias != null }, state.roomTopic) + renderState(bestName, state.matrixItem().takeIf { state.roomAlias != null }, state.roomTopic, state.roomType) } else -> { views.roomPreviewNoPreviewJoin.isVisible = false views.roomPreviewNoPreviewLabel.isVisible = true views.roomPreviewNoPreviewLabel.setText(R.string.room_preview_not_found) - renderState(bestName, null, state.roomTopic) + renderState(bestName, null, state.roomTopic, state.roomType) } } } @@ -118,13 +162,13 @@ class RoomPreviewNoPreviewFragment @Inject constructor( // Render with initial state, no peeking views.roomPreviewPeekingProgress.isVisible = false views.roomPreviewNoPreviewJoin.isVisible = true - renderState(bestName, state.matrixItem(), state.roomTopic) + renderState(bestName, state.matrixItem(), state.roomTopic, state.roomType) views.roomPreviewNoPreviewLabel.isVisible = false } } } - private fun renderState(roomName: String, matrixItem: MatrixItem?, topic: String?) { + private fun renderState(roomName: String, matrixItem: MatrixItem?, topic: String?, roomType: String?) { // Toolbar if (matrixItem != null) { views.roomPreviewNoPreviewToolbarAvatar.isVisible = true diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt index c4d479374b..94b7a315c3 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt @@ -23,8 +23,8 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -34,6 +34,8 @@ import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.identity.SharedState +import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.peeking.PeekResult @@ -64,7 +66,39 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini observeRoomSummary() observeMembershipChanges() - if (initialState.shouldPeekFromServer) { + if (initialState.fromEmailInvite != null) { + setState { + copy(peekingState = Loading()) + } + viewModelScope.launch { + // we might want to check if the mail is bound to this account? + // if it is the invite + val threePids = session + .getThreePids() + .filterIsInstance() + + val status = if (threePids.indexOfFirst { it.email == initialState.fromEmailInvite.email } != -1) { + try { + session.identityService().getShareStatus(threePids) + } catch (failure: Throwable) { + Timber.w(failure, "## Room Invite: Failed to get 3pids shared status") + // If terms not signed, or no identity server setup, or different + // id server from the one in the email invite, we consider the mails as not bound + emptyMap() + }.firstNotNullOfOrNull { if (it.key.value == initialState.fromEmailInvite.email) it.value else null } + ?: SharedState.NOT_SHARED + } else { + SharedState.NOT_SHARED + } + + setState { + copy( + isEmailBoundToAccount = status == SharedState.SHARED, + peekingState = Success(PeekingState.FOUND) + ) + } + } + } else if (initialState.shouldPeekFromServer) { peekRoomFromServer() } } @@ -128,6 +162,7 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini private fun observeRoomSummary() { val queryParams = roomSummaryQueryParams { roomId = QueryStringValue.Equals(initialState.roomId) + excludeType = null } session .rx() @@ -136,6 +171,11 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini val isRoomJoined = list.any { it.membership == Membership.JOIN } + list.firstOrNull { it.roomId == initialState.roomId }?.roomType?.let { + setState { + copy(roomType = it) + } + } if (isRoomJoined) { setState { copy(roomJoinState = JoinState.JOINED) } } @@ -163,10 +203,44 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini override fun handle(action: RoomPreviewAction) { when (action) { - is RoomPreviewAction.Join -> handleJoinRoom() + is RoomPreviewAction.Join -> handleJoinRoom() + RoomPreviewAction.JoinThirdParty -> handleJoinRoomThirdParty() }.exhaustive } + private fun handleJoinRoomThirdParty() = withState { state -> + if (state.roomJoinState == JoinState.JOINING) { + // Request already sent, should not happen + Timber.w("Try to join an already joining room. Should not happen") + return@withState + } + // local echo + setState { + copy(roomJoinState = JoinState.JOINING) + } + viewModelScope.launch { + try { + // XXX this could be done locally, but the spec is incomplete and it's not clear + // what needs to be signed with what? + val thirdPartySigned = session.identityService().sign3pidInvitation( + state.fromEmailInvite?.identityServer ?: "", + state.fromEmailInvite?.token ?: "", + state.fromEmailInvite?.privateKey ?: "" + ) + + session.joinRoom(state.roomId, reason = null, thirdPartySigned) + } catch (failure: Throwable) { + setState { + copy( + roomJoinState = JoinState.JOINING_ERROR, + lastError = failure + ) + } + } + } + + } + private fun handleJoinRoom() = withState { state -> if (state.roomJoinState == JoinState.JOINING) { // Request already sent, should not happen diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt index 0b2e0c048b..75286ea24f 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt @@ -20,6 +20,8 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.roomdirectory.JoinState +import org.matrix.android.sdk.api.session.permalinks.PermalinkData +import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.util.MatrixItem data class RoomPreviewViewState( @@ -27,6 +29,7 @@ data class RoomPreviewViewState( // The room id val roomId: String = "", val roomAlias: String? = null, + val roomType: String? = null, val roomName: String? = null, val roomTopic: String? = null, @@ -40,7 +43,11 @@ data class RoomPreviewViewState( // Current state of the room in preview val roomJoinState: JoinState = JoinState.NOT_JOINED, // Last error of join room request - val lastError: Throwable? = null + val lastError: Throwable? = null, + + val fromEmailInvite: PermalinkData.RoomEmailInviteLink? = null, + // used only if it's an email invite + val isEmailBoundToAccount: Boolean = false ) : MvRxState { constructor(args: RoomPreviewData) : this( @@ -50,10 +57,13 @@ data class RoomPreviewViewState( roomName = args.roomName, roomTopic = args.topic, avatarUrl = args.avatarUrl, - shouldPeekFromServer = args.peekFromServer + shouldPeekFromServer = args.peekFromServer, + fromEmailInvite = args.fromEmailInvite, + roomType = args.roomType ) fun matrixItem() : MatrixItem { - return MatrixItem.RoomItem(roomId, roomName ?: roomAlias, avatarUrl) + return if (roomType == RoomType.SPACE) MatrixItem.SpaceItem(roomId, roomName ?: roomAlias, avatarUrl) + else MatrixItem.RoomItem(roomId, roomName ?: roomAlias, avatarUrl) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt index 646709626c..4546313198 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt @@ -26,6 +26,7 @@ import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivityVectorSettingsBinding +import im.vector.app.features.discovery.DiscoverySettingsFragment import im.vector.app.features.settings.devices.VectorSettingsDevicesFragment import im.vector.app.features.settings.notifications.VectorSettingsNotificationPreferenceFragment @@ -79,6 +80,9 @@ class VectorSettingsActivity : VectorBaseActivity requestHighlightPreferenceKeyOnResume(VectorPreferences.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY) replaceFragment(R.id.vector_settings_page, VectorSettingsNotificationPreferenceFragment::class.java, null, FRAGMENT_TAG) } + EXTRA_DIRECT_ACCESS_DISCOVERY_SETTINGS -> { + replaceFragment(R.id.vector_settings_page, DiscoverySettingsFragment::class.java, null, FRAGMENT_TAG) + } else -> replaceFragment(R.id.vector_settings_page, VectorSettingsRootFragment::class.java, null, FRAGMENT_TAG) @@ -160,6 +164,7 @@ class VectorSettingsActivity : VectorBaseActivity const val EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY_MANAGE_SESSIONS = 3 const val EXTRA_DIRECT_ACCESS_GENERAL = 4 const val EXTRA_DIRECT_ACCESS_NOTIFICATIONS = 5 + const val EXTRA_DIRECT_ACCESS_DISCOVERY_SETTINGS = 6 private const val FRAGMENT_TAG = "VectorSettingsPreferencesFragment" } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 405e12fae3..691289f5b4 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -3543,4 +3543,10 @@ This makes it easy for rooms to stay private to a space, while letting people in the space find and join them. All new rooms in a space will have this option available. Help space members find private rooms To help space members find and join a private room, go to that room’s settings by tapping on the avatar. + + This invite to this room was sent to %s which is not associated with your account + This invite to this space was sent to %s which is not associated with your account + + Link this email with your account + Link this email with your account in Settings to receive invites directly in Element. From f2ab0987ccf2a581df282bb60c5d12398918b5e4 Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 27 Jul 2021 10:06:10 +0200 Subject: [PATCH 2/6] Add change log + cleaning --- changelog.d/3691.feature | 1 + changelog.d/3695.feature | 1 + .../android/sdk/internal/session/room/DefaultRoomService.kt | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 changelog.d/3691.feature create mode 100644 changelog.d/3695.feature diff --git a/changelog.d/3691.feature b/changelog.d/3691.feature new file mode 100644 index 0000000000..ae139681b0 --- /dev/null +++ b/changelog.d/3691.feature @@ -0,0 +1 @@ + Support accept 3pid invite when email is not bound to account \ No newline at end of file diff --git a/changelog.d/3695.feature b/changelog.d/3695.feature new file mode 100644 index 0000000000..bd5934529d --- /dev/null +++ b/changelog.d/3695.feature @@ -0,0 +1 @@ +Update Email invite to be aware of spaces \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt index 5ea38ff682..c3bcef60f2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt @@ -123,7 +123,7 @@ internal class DefaultRoomService @Inject constructor( joinRoomTask.execute(JoinRoomTask.Params(roomIdOrAlias, reason, viaServers)) } - override suspend fun joinRoom(roomIdOrAlias: String, + override suspend fun joinRoom(roomId: String, reason: String?, thirdPartySigned: SignInvitationResult) { joinRoomTask.execute(JoinRoomTask.Params(roomIdOrAlias, reason, thirdPartySigned = thirdPartySigned)) From 819e7c49b0937ba02c5609aec786c91a80be3f40 Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 30 Jul 2021 12:10:15 +0200 Subject: [PATCH 3/6] Code review --- .../sdk/api/session/identity/IdentityService.kt | 9 +++++++++ .../sdk/api/session/permalinks/PermalinkParser.kt | 14 ++++++++------ .../session/identity/Sign3pidInvitationTask.kt | 4 ++-- .../internal/session/room/DefaultRoomService.kt | 2 +- .../roompreview/RoomPreviewNoPreviewFragment.kt | 5 ++++- .../roompreview/RoomPreviewViewModel.kt | 1 - vector/src/main/res/values/strings.xml | 7 +++++-- 7 files changed, 29 insertions(+), 13 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt index 15ca748e4e..1485ec478b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt @@ -123,6 +123,15 @@ interface IdentityService { */ suspend fun getShareStatus(threePids: List): Map + /** + * When one performs a 3pid invite and the third party identifier is unknown, the home server + * will store the invitation in the Identity server and store some information in the room state membership event. + * The email invite will contains the token and secret that can be used to claim the stored invitation + * + * To aid clients who may not be able to perform crypto themselves, + * the identity server offers some crypto functionality to help in accepting invitations. + * This is less secure than the client doing it itself, but may be useful where this isn't possible. + */ suspend fun sign3pidInvitation(identiyServer: String, token: String, secret: String) : SignInvitationResult fun addListener(listener: IdentityServiceListener) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt index 99cca715b8..0108bbf7e4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt @@ -67,12 +67,7 @@ object PermalinkParser { MatrixPatterns.isGroupId(identifier) -> PermalinkData.GroupLink(groupId = identifier) MatrixPatterns.isRoomId(identifier) -> { // Can't rely on built in parsing because it's messing around the signurl - val paramList = fragment.substringAfter("?").split('&').mapNotNull { - val splitNameValue = it.split("=") - if (splitNameValue.size == 2) { - Pair(splitNameValue[0], URLDecoder.decode(splitNameValue[1], "UTF-8")) - } else null - } + val paramList = safeExtractParams(fragment) val signUrl = paramList.firstOrNull { it.first == "signurl" }?.second val email = paramList.firstOrNull { it.first == "email" }?.second if (signUrl.isNullOrEmpty().not() && email.isNullOrEmpty().not()) { @@ -118,6 +113,13 @@ object PermalinkParser { } } + private fun safeExtractParams(fragment: String) = fragment.substringAfter("?").split('&').mapNotNull { + val splitNameValue = it.split("=") + if (splitNameValue.size == 2) { + Pair(splitNameValue[0], URLDecoder.decode(splitNameValue[1], "UTF-8")) + } else null + } + private fun String.getViaParameters(): List { return UrlQuerySanitizer(this) .parameterList diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/Sign3pidInvitationTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/Sign3pidInvitationTask.kt index a8bb1d475b..11ec51e505 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/Sign3pidInvitationTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/Sign3pidInvitationTask.kt @@ -18,7 +18,7 @@ package org.matrix.android.sdk.internal.session.identity import dagger.Lazy import okhttp3.OkHttpClient -import org.matrix.android.sdk.internal.di.AuthenticatedIdentity +import org.matrix.android.sdk.internal.di.Unauthenticated import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.network.RetrofitFactory import org.matrix.android.sdk.internal.session.identity.model.SignInvitationResult @@ -34,7 +34,7 @@ internal interface Sign3pidInvitationTask : Task, private val retrofitFactory: RetrofitFactory, @UserId private val userId: String diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt index c3bcef60f2..f69949cbb6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt @@ -126,7 +126,7 @@ internal class DefaultRoomService @Inject constructor( override suspend fun joinRoom(roomId: String, reason: String?, thirdPartySigned: SignInvitationResult) { - joinRoomTask.execute(JoinRoomTask.Params(roomIdOrAlias, reason, thirdPartySigned = thirdPartySigned)) + joinRoomTask.execute(JoinRoomTask.Params(roomId, reason, thirdPartySigned = thirdPartySigned)) } override suspend fun markAllAsRead(roomIds: List) { diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt index 07c4c65a28..99667f620a 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt @@ -126,7 +126,10 @@ class RoomPreviewNoPreviewFragment @Inject constructor( } +"\n" span { - text = getString(R.string.link_this_email_with_your_account) + text = getString( + R.string.link_this_email_with_your_account, + getString(R.string.link_this_email_settings_link) + ) .toSpannable() .tappableMatchingText(getString(R.string.link_this_email_settings_link), object : ClickableSpan() { override fun onClick(widget: View) { diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt index 94b7a315c3..a559b0554d 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt @@ -238,7 +238,6 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini } } } - } private fun handleJoinRoom() = withState { state -> diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 691289f5b4..47a55442b4 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -3544,9 +3544,12 @@ Help space members find private rooms To help space members find and join a private room, go to that room’s settings by tapping on the avatar. + This invite to this room was sent to %s which is not associated with your account + This invite to this space was sent to %s which is not associated with your account - + Link this email with your account - Link this email with your account in Settings to receive invites directly in Element. + + %s in Settings to receive invites directly in Element. From 1ffacd1788a87604b8452e4268d72e3dea427bbf Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 27 Aug 2021 15:49:48 +0200 Subject: [PATCH 4/6] Code review --- .../api/session/permalinks/PermalinkParser.kt | 72 ++++++++++--------- .../identity/Sign3pidInvitationTask.kt | 4 +- .../sdk/internal/session/room/RoomAPI.kt | 2 +- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt index 0108bbf7e4..c8ab7db196 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt @@ -66,40 +66,7 @@ object PermalinkParser { MatrixPatterns.isUserId(identifier) -> PermalinkData.UserLink(userId = identifier) MatrixPatterns.isGroupId(identifier) -> PermalinkData.GroupLink(groupId = identifier) MatrixPatterns.isRoomId(identifier) -> { - // Can't rely on built in parsing because it's messing around the signurl - val paramList = safeExtractParams(fragment) - val signUrl = paramList.firstOrNull { it.first == "signurl" }?.second - val email = paramList.firstOrNull { it.first == "email" }?.second - if (signUrl.isNullOrEmpty().not() && email.isNullOrEmpty().not()) { - try { - val signValidUri = Uri.parse(signUrl) - val identityServerHost = signValidUri.authority ?: throw IllegalArgumentException() - val token = signValidUri.getQueryParameter("token") ?: throw IllegalArgumentException() - val privateKey = signValidUri.getQueryParameter("private_key") ?: throw IllegalArgumentException() - PermalinkData.RoomEmailInviteLink( - roomId = identifier, - email = email!!, - signUrl = signUrl!!, - roomName = paramList.firstOrNull { it.first == "room_name" }?.second, - inviterName = paramList.firstOrNull { it.first == "inviter_name" }?.second, - roomAvatarUrl = paramList.firstOrNull { it.first == "room_avatar_url" }?.second, - roomType = paramList.firstOrNull { it.first == "room_type" }?.second, - identityServer = identityServerHost, - token = token, - privateKey = privateKey - ) - } catch (failure: Throwable) { - Timber.i("## Permalink: Failed to parse permalink $signUrl") - PermalinkData.FallbackLink(uri) - } - } else { - PermalinkData.RoomLink( - roomIdOrAlias = identifier, - isRoomAlias = false, - eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) }, - viaParameters = viaQueryParameters - ) - } + handleRoomIdCase(fragment, identifier, uri, extraParameter, viaQueryParameters) } MatrixPatterns.isRoomAlias(identifier) -> { PermalinkData.RoomLink( @@ -113,6 +80,43 @@ object PermalinkParser { } } + private fun handleRoomIdCase(fragment: String, identifier: String, uri: Uri, extraParameter: String?, viaQueryParameters: List): PermalinkData { + // Can't rely on built in parsing because it's messing around the signurl + val paramList = safeExtractParams(fragment) + val signUrl = paramList.firstOrNull { it.first == "signurl" }?.second + val email = paramList.firstOrNull { it.first == "email" }?.second + return if (signUrl.isNullOrEmpty().not() && email.isNullOrEmpty().not()) { + try { + val signValidUri = Uri.parse(signUrl) + val identityServerHost = signValidUri.authority ?: throw IllegalArgumentException() + val token = signValidUri.getQueryParameter("token") ?: throw IllegalArgumentException() + val privateKey = signValidUri.getQueryParameter("private_key") ?: throw IllegalArgumentException() + PermalinkData.RoomEmailInviteLink( + roomId = identifier, + email = email!!, + signUrl = signUrl!!, + roomName = paramList.firstOrNull { it.first == "room_name" }?.second, + inviterName = paramList.firstOrNull { it.first == "inviter_name" }?.second, + roomAvatarUrl = paramList.firstOrNull { it.first == "room_avatar_url" }?.second, + roomType = paramList.firstOrNull { it.first == "room_type" }?.second, + identityServer = identityServerHost, + token = token, + privateKey = privateKey + ) + } catch (failure: Throwable) { + Timber.i("## Permalink: Failed to parse permalink $signUrl") + PermalinkData.FallbackLink(uri) + } + } else { + PermalinkData.RoomLink( + roomIdOrAlias = identifier, + isRoomAlias = false, + eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) }, + viaParameters = viaQueryParameters + ) + } + } + private fun safeExtractParams(fragment: String) = fragment.substringAfter("?").split('&').mapNotNull { val splitNameValue = it.split("=") if (splitNameValue.size == 2) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/Sign3pidInvitationTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/Sign3pidInvitationTask.kt index 11ec51e505..d491af33ca 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/Sign3pidInvitationTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/Sign3pidInvitationTask.kt @@ -41,8 +41,8 @@ internal class DefaultSign3pidInvitationTask @Inject constructor( ) : Sign3pidInvitationTask { override suspend fun execute(params: Sign3pidInvitationTask.Params): SignInvitationResult { - val identityAPI = params.url - .let { retrofitFactory.create(okHttpClient, "https://$it") } + val identityAPI = retrofitFactory + .create(okHttpClient, "https://${params.url}") .create(IdentityAPI::class.java) return identityAPI.signInvitationDetails(params.token, params.privateKey, userId) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt index 23bded7a79..535fa9df71 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt @@ -254,7 +254,7 @@ internal interface RoomAPI { @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "join/{roomIdOrAlias}") suspend fun join(@Path("roomIdOrAlias") roomIdOrAlias: String, @Query("server_name") viaServers: List, - @Body params: @JvmSuppressWildcards Map): JoinRoomResponse + @Body params: JsonDict): JoinRoomResponse /** * Leave the given room. From efa9ff0959ccd503b4e44b64862e9502ce97d603 Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 27 Aug 2021 16:34:18 +0200 Subject: [PATCH 5/6] unused param --- .../roompreview/RoomPreviewNoPreviewFragment.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt index 99667f620a..fb25b22e93 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt @@ -165,13 +165,16 @@ class RoomPreviewNoPreviewFragment @Inject constructor( // Render with initial state, no peeking views.roomPreviewPeekingProgress.isVisible = false views.roomPreviewNoPreviewJoin.isVisible = true - renderState(bestName, state.matrixItem(), state.roomTopic, state.roomType) + renderState(bestName, state.matrixItem(), state.roomTopic + /**, state.roomType*/) views.roomPreviewNoPreviewLabel.isVisible = false } } } - private fun renderState(roomName: String, matrixItem: MatrixItem?, topic: String?, roomType: String?) { + private fun renderState(roomName: String, matrixItem: MatrixItem?, topic: String? + /**, roomType: String?*/ + ) { // Toolbar if (matrixItem != null) { views.roomPreviewNoPreviewToolbarAvatar.isVisible = true From 67295c6bdd602d1f0773c8df6e906dcb9a9027b4 Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 27 Aug 2021 17:55:52 +0200 Subject: [PATCH 6/6] Fix compilation --- .../roompreview/RoomPreviewNoPreviewFragment.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt index fb25b22e93..ef70a31a00 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt @@ -110,7 +110,8 @@ class RoomPreviewNoPreviewFragment @Inject constructor( PeekingState.FOUND -> { // show join buttons views.roomPreviewNoPreviewJoin.isVisible = true - renderState(bestName, state.matrixItem(), state.roomTopic, state.roomType) + renderState(bestName, state.matrixItem(), state.roomTopic + /**, state.roomType*/) if (state.fromEmailInvite != null && !state.isEmailBoundToAccount) { views.roomPreviewNoPreviewLabel.text = span { @@ -151,13 +152,15 @@ class RoomPreviewNoPreviewFragment @Inject constructor( views.roomPreviewNoPreviewJoin.isVisible = true views.roomPreviewNoPreviewLabel.isVisible = true views.roomPreviewNoPreviewLabel.setText(R.string.room_preview_no_preview_join) - renderState(bestName, state.matrixItem().takeIf { state.roomAlias != null }, state.roomTopic, state.roomType) + renderState(bestName, state.matrixItem().takeIf { state.roomAlias != null }, state.roomTopic + /**, state.roomType*/) } else -> { views.roomPreviewNoPreviewJoin.isVisible = false views.roomPreviewNoPreviewLabel.isVisible = true views.roomPreviewNoPreviewLabel.setText(R.string.room_preview_not_found) - renderState(bestName, null, state.roomTopic, state.roomType) + renderState(bestName, null, state.roomTopic + /**, state.roomType*/) } } }