From 3df328b1ab2d446447ec1507d8e3386a8662e51b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 15 Apr 2024 17:46:06 +0200 Subject: [PATCH 1/3] Parse permalink using `parseMatrixEntityFrom`. Create new PermalinkData type for link to Events. Keep matrixToConverter for now to first convert to matrix.to link. At some point it may be done by the SDK. Remove parse(Uri) --- .../features/messages/impl/MessagesNode.kt | 11 +- .../MessageComposerStateProvider.kt | 2 - .../matrix/api/permalink/PermalinkData.kt | 44 ++++-- .../matrix/api/permalink/PermalinkParser.kt | 9 +- .../libraries/matrix/api/room/Mention.kt | 3 +- .../matrix/api/permalink/PermalinkDataTest.kt | 47 ------ .../impl/permalink/DefaultPermalinkParser.kt | 138 +++++------------- .../permalink/DefaultPermalinkParserTest.kt | 54 ++++--- .../test/permalink/FakePermalinkParser.kt | 5 - .../matrixui/messages/ToHtmlDocumentTest.kt | 11 +- .../libraries/textcomposer/TextComposer.kt | 1 - .../mentions/MentionSpanProvider.kt | 16 +- .../impl/mentions/MentionSpanProviderTest.kt | 17 +-- .../minimal/OnlyFallbackPermalinkParser.kt | 4 - 14 files changed, 123 insertions(+), 239 deletions(-) delete mode 100644 libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkDataTest.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index aca840236ed..ff8c727f9c3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -17,7 +17,6 @@ package io.element.android.features.messages.impl import android.content.Context -import android.net.Uri import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier @@ -108,13 +107,19 @@ class MessagesNode @AssistedInject constructor( context: Context, url: String, ) { - when (val permalink = permalinkParser.parse(Uri.parse(url))) { + when (val permalink = permalinkParser.parse(url)) { is PermalinkData.UserLink -> { - callback?.onUserDataClicked(UserId(permalink.userId)) + callback?.onUserDataClicked(permalink.userId) } is PermalinkData.RoomLink -> { // TODO Implement room link handling } + is PermalinkData.EventIdAliasLink -> { + // TODO Implement room and Event link handling + } + is PermalinkData.EventIdLink -> { + // TODO Implement room and Event link handling + } is PermalinkData.FallbackLink, is PermalinkData.RoomEmailInviteLink -> { context.openUrlInExternalApp(url) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt index 82a4692a534..340f7328f33 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt @@ -16,7 +16,6 @@ package io.element.android.features.messages.impl.messagecomposer -import android.net.Uri import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.messages.impl.mentions.MentionSuggestion import io.element.android.libraries.matrix.api.core.UserId @@ -49,7 +48,6 @@ fun aMessageComposerState( richTextEditorState = richTextEditorState, permalinkParser = object : PermalinkParser { override fun parse(uriString: String): PermalinkData = TODO() - override fun parse(uri: Uri): PermalinkData = TODO() }, isFullScreen = isFullScreen, mode = mode, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt index e39fee5a223..d09394e8ddc 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt @@ -18,7 +18,9 @@ package io.element.android.libraries.matrix.api.permalink import android.net.Uri import androidx.compose.runtime.Immutable +import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.UserId import kotlinx.collections.immutable.ImmutableList /** @@ -27,28 +29,44 @@ import kotlinx.collections.immutable.ImmutableList */ @Immutable sealed interface PermalinkData { - data class RoomLink( - val roomIdOrAlias: String, - val isRoomAlias: Boolean, - val eventId: String?, + sealed interface RoomLink : PermalinkData { val viaParameters: ImmutableList - ) : PermalinkData { - fun getRoomId(): RoomId? { - return roomIdOrAlias.takeIf { !isRoomAlias }?.let(::RoomId) - } + } + + data class RoomIdLink( + val roomId: RoomId, + override val viaParameters: ImmutableList + ) : RoomLink + + data class RoomAliasLink( + val roomAlias: String, + override val viaParameters: ImmutableList + ) : RoomLink - fun getRoomAlias(): String? { - return roomIdOrAlias.takeIf { isRoomAlias } - } + sealed interface EventLink : PermalinkData { + val eventId: EventId + val viaParameters: ImmutableList } + data class EventIdLink( + val roomId: RoomId, + override val eventId: EventId, + override val viaParameters: ImmutableList + ) : EventLink + + data class EventIdAliasLink( + val roomAlias: String, + override val eventId: EventId, + override val viaParameters: ImmutableList + ) : EventLink + /* * &room_name=Team2 * &room_avatar_url=mxc: * &inviter_name=bob */ data class RoomEmailInviteLink( - val roomId: String, + val roomId: RoomId, val email: String, val signUrl: String, val roomName: String?, @@ -60,7 +78,7 @@ sealed interface PermalinkData { val roomType: String? ) : PermalinkData - data class UserLink(val userId: String) : PermalinkData + data class UserLink(val userId: UserId) : PermalinkData data class FallbackLink(val uri: Uri, val isLegacyGroupLink: Boolean = false) : PermalinkData } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt index 463f8fb32dc..ebe7596aa4e 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt @@ -16,8 +16,6 @@ package io.element.android.libraries.matrix.api.permalink -import android.net.Uri - /** * This class turns a uri to a [PermalinkData]. * element-based domains (e.g. https://app.element.io/#/user/@chagai95:matrix.org) permalinks @@ -27,12 +25,7 @@ import android.net.Uri interface PermalinkParser { /** * Turns a uri string to a [PermalinkData]. - */ - fun parse(uriString: String): PermalinkData - - /** - * Turns a uri to a [PermalinkData]. * https://github.com/matrix-org/matrix-doc/blob/master/proposals/1704-matrix.to-permalinks.md */ - fun parse(uri: Uri): PermalinkData + fun parse(uriString: String): PermalinkData } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/Mention.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/Mention.kt index 47fd900a8f5..52856387135 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/Mention.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/Mention.kt @@ -22,5 +22,6 @@ import io.element.android.libraries.matrix.api.core.UserId sealed interface Mention { data class User(val userId: UserId) : Mention data object AtRoom : Mention - data class Room(val roomId: RoomId?, val roomAlias: String?) : Mention + data class Room(val roomId: RoomId) : Mention + data class RoomAlias(val roomAlias: String?) : Mention } diff --git a/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkDataTest.kt b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkDataTest.kt deleted file mode 100644 index 03a56651e87..00000000000 --- a/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkDataTest.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2023 New Vector Ltd - * - * 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 io.element.android.libraries.matrix.api.permalink - -import com.google.common.truth.Truth.assertThat -import kotlinx.collections.immutable.persistentListOf -import org.junit.Test - -class PermalinkDataTest { - @Test - fun `getRoomId() returns value when isRoomAlias is false`() { - val permalinkData = PermalinkData.RoomLink( - roomIdOrAlias = "!abcdef123456:matrix.org", - isRoomAlias = false, - eventId = null, - viaParameters = persistentListOf(), - ) - assertThat(permalinkData.getRoomId()).isNotNull() - assertThat(permalinkData.getRoomAlias()).isNull() - } - - @Test - fun `getRoomAlias() returns value when isRoomAlias is true`() { - val permalinkData = PermalinkData.RoomLink( - roomIdOrAlias = "#room:matrix.org", - isRoomAlias = true, - eventId = null, - viaParameters = persistentListOf(), - ) - assertThat(permalinkData.getRoomId()).isNull() - assertThat(permalinkData.getRoomAlias()).isNotNull() - } -} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParser.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParser.kt index ab91a89af91..7bf095a9f8c 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParser.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParser.kt @@ -17,16 +17,17 @@ package io.element.android.libraries.matrix.impl.permalink import android.net.Uri -import android.net.UrlQuerySanitizer import com.squareup.anvil.annotations.ContributesBinding import io.element.android.libraries.di.AppScope -import io.element.android.libraries.matrix.api.core.MatrixPatterns +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.permalink.MatrixToConverter import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.permalink.PermalinkParser import kotlinx.collections.immutable.toImmutableList -import timber.log.Timber -import java.net.URLDecoder +import org.matrix.rustcomponents.sdk.MatrixId +import org.matrix.rustcomponents.sdk.parseMatrixEntityFrom import javax.inject.Inject /** @@ -41,118 +42,45 @@ class DefaultPermalinkParser @Inject constructor( ) : PermalinkParser { /** * Turns a uri string to a [PermalinkData]. + * https://github.com/matrix-org/matrix-doc/blob/master/proposals/1704-matrix.to-permalinks.md */ override fun parse(uriString: String): PermalinkData { val uri = Uri.parse(uriString) - return parse(uri) - } - - /** - * Turns a uri to a [PermalinkData]. - * https://github.com/matrix-org/matrix-doc/blob/master/proposals/1704-matrix.to-permalinks.md - */ - override fun parse(uri: Uri): PermalinkData { // the client or element-based domain permalinks (e.g. https://app.element.io/#/user/@chagai95:matrix.org) don't have the // mxid in the first param (like matrix.to does - https://matrix.to/#/@chagai95:matrix.org) but rather in the second after /user/ so /user/mxid // so convert URI to matrix.to to simplify parsing process val matrixToUri = matrixToConverter.convert(uri) ?: return PermalinkData.FallbackLink(uri) - // 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 = matrixToUri.toString().substringAfter("#") // uri.fragment - if (fragment.isEmpty()) { - return PermalinkData.FallbackLink(uri) - } - val safeFragment = fragment.substringBefore('?') - val viaQueryParameters = fragment.getViaParameters() - - // we are limiting to 2 params - val params = safeFragment - .split(MatrixPatterns.SEP_REGEX) - .filter { it.isNotEmpty() } - .take(2) - - val decodedParams = params - .map { URLDecoder.decode(it, "UTF-8") } - - val identifier = params.getOrNull(0) - val decodedIdentifier = decodedParams.getOrNull(0) - val extraParameter = decodedParams.getOrNull(1) - return when { - identifier.isNullOrEmpty() || decodedIdentifier.isNullOrEmpty() -> PermalinkData.FallbackLink(uri) - MatrixPatterns.isUserId(decodedIdentifier) -> PermalinkData.UserLink(userId = decodedIdentifier) - MatrixPatterns.isRoomId(decodedIdentifier) -> { - handleRoomIdCase(fragment, decodedIdentifier, matrixToUri, extraParameter, viaQueryParameters) - } - MatrixPatterns.isRoomAlias(decodedIdentifier) -> { - PermalinkData.RoomLink( - roomIdOrAlias = decodedIdentifier, - isRoomAlias = true, - eventId = extraParameter.takeIf { !it.isNullOrEmpty() && MatrixPatterns.isEventId(it) }, - viaParameters = viaQueryParameters.toImmutableList() + val result = runCatching { + parseMatrixEntityFrom(matrixToUri.toString()) + }.getOrNull() + return if (result == null) { + PermalinkData.FallbackLink(uri) + } else { + val viaParameters = result.via.toImmutableList() + when (val id = result.id) { + is MatrixId.Room -> PermalinkData.RoomIdLink( + roomId = RoomId(id.id), + viaParameters = viaParameters, ) - } - else -> PermalinkData.FallbackLink(uri, MatrixPatterns.isGroupId(identifier)) - } - } - - 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("missing `authority`") - val token = signValidUri.getQueryParameter("token") ?: throw IllegalArgumentException("missing `token`") - val privateKey = signValidUri.getQueryParameter("private_key") ?: throw IllegalArgumentException("missing `private_key`") - 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 + is MatrixId.User -> PermalinkData.UserLink( + userId = UserId(id.id), + ) + is MatrixId.RoomAlias -> PermalinkData.RoomAliasLink( + roomAlias = id.alias, + viaParameters = viaParameters, + ) + is MatrixId.EventOnRoomId -> PermalinkData.EventIdLink( + roomId = RoomId(id.roomId), + eventId = EventId(id.eventId), + viaParameters = viaParameters, + ) + is MatrixId.EventOnRoomAlias -> PermalinkData.EventIdAliasLink( + roomAlias = id.alias, + eventId = EventId(id.eventId), + viaParameters = viaParameters, ) - } 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.toImmutableList() - ) - } - } - - 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 runCatching { - UrlQuerySanitizer(this) - .parameterList - .filter { - it.mParameter == "via" - } - .map { - URLDecoder.decode(it.mValue, "UTF-8") - } - }.getOrDefault(emptyList()) } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParserTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParserTest.kt index 1e9e3bc2dc9..af9043be721 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParserTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParserTest.kt @@ -17,6 +17,9 @@ package io.element.android.libraries.matrix.impl.permalink import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.permalink.PermalinkData import kotlinx.collections.immutable.persistentListOf import org.junit.Test @@ -69,7 +72,7 @@ class DefaultPermalinkParserTest { val url = "https://app.element.io/#/user/@test:matrix.org" assertThat(sut.parse(url)).isEqualTo( PermalinkData.UserLink( - userId = "@test:matrix.org" + userId = UserId("@test:matrix.org") ) ) } @@ -81,10 +84,8 @@ class DefaultPermalinkParserTest { ) val url = "https://app.element.io/#/room/!aBCD1234:matrix.org" assertThat(sut.parse(url)).isEqualTo( - PermalinkData.RoomLink( - roomIdOrAlias = "!aBCD1234:matrix.org", - isRoomAlias = false, - eventId = null, + PermalinkData.RoomIdLink( + roomId = RoomId("!aBCD1234:matrix.org"), viaParameters = persistentListOf(), ) ) @@ -97,10 +98,9 @@ class DefaultPermalinkParserTest { ) val url = "https://app.element.io/#/room/!aBCD1234:matrix.org/$1234567890abcdef:matrix.org" assertThat(sut.parse(url)).isEqualTo( - PermalinkData.RoomLink( - roomIdOrAlias = "!aBCD1234:matrix.org", - isRoomAlias = false, - eventId = "\$1234567890abcdef:matrix.org", + PermalinkData.EventIdLink( + roomId = RoomId("!aBCD1234:matrix.org"), + eventId = EventId("$1234567890abcdef:matrix.org"), viaParameters = persistentListOf(), ) ) @@ -113,10 +113,8 @@ class DefaultPermalinkParserTest { ) val url = "https://app.element.io/#/room/!aBCD1234:matrix.org/1234567890abcdef:matrix.org" assertThat(sut.parse(url)).isEqualTo( - PermalinkData.RoomLink( - roomIdOrAlias = "!aBCD1234:matrix.org", - isRoomAlias = false, - eventId = null, + PermalinkData.RoomIdLink( + roomId = RoomId("!aBCD1234:matrix.org"), viaParameters = persistentListOf(), ) ) @@ -129,10 +127,9 @@ class DefaultPermalinkParserTest { ) val url = "https://app.element.io/#/room/!aBCD1234:matrix.org/$1234567890abcdef:matrix.org?via=matrix.org&via=matrix.com" assertThat(sut.parse(url)).isEqualTo( - PermalinkData.RoomLink( - roomIdOrAlias = "!aBCD1234:matrix.org", - isRoomAlias = false, - eventId = "\$1234567890abcdef:matrix.org", + PermalinkData.EventIdLink( + roomId = RoomId("!aBCD1234:matrix.org"), + eventId = EventId("$1234567890abcdef:matrix.org"), viaParameters = persistentListOf("matrix.org", "matrix.com"), ) ) @@ -145,10 +142,23 @@ class DefaultPermalinkParserTest { ) val url = "https://app.element.io/#/room/#element-android:matrix.org" assertThat(sut.parse(url)).isEqualTo( - PermalinkData.RoomLink( - roomIdOrAlias = "#element-android:matrix.org", - isRoomAlias = true, - eventId = null, + PermalinkData.RoomAliasLink( + roomAlias = "#element-android:matrix.org", + viaParameters = persistentListOf(), + ) + ) + } + + @Test + fun `parsing a valid room alias with eventId url returns a room link`() { + val sut = DefaultPermalinkParser( + matrixToConverter = DefaultMatrixToConverter(), + ) + val url = "https://app.element.io/#/room/#element-android:matrix.org/$1234567890abcdef:matrix.org" + assertThat(sut.parse(url)).isEqualTo( + PermalinkData.EventIdAliasLink( + roomAlias = "#element-android:matrix.org", + eventId = EventId("$1234567890abcdef:matrix.org"), viaParameters = persistentListOf(), ) ) @@ -188,7 +198,7 @@ class DefaultPermalinkParserTest { "&room_type=" assertThat(sut.parse(url)).isEqualTo( PermalinkData.RoomEmailInviteLink( - roomId = "!aBCDEF12345:matrix.org", + roomId = RoomId("!aBCDEF12345:matrix.org"), email = "testuser@element.io", signUrl = "https://vector.im/_matrix/identity/api/v1/sign-ed25519?token=a_token&private_key=a_private_key", roomName = "TestRoom", diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/permalink/FakePermalinkParser.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/permalink/FakePermalinkParser.kt index f046eadf93f..d1ffb70f99f 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/permalink/FakePermalinkParser.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/permalink/FakePermalinkParser.kt @@ -16,7 +16,6 @@ package io.element.android.libraries.matrix.test.permalink -import android.net.Uri import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.permalink.PermalinkParser @@ -30,8 +29,4 @@ class FakePermalinkParser( override fun parse(uriString: String): PermalinkData { return result() } - - override fun parse(uri: Uri): PermalinkData { - TODO("Not yet implemented") - } } diff --git a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrixui/messages/ToHtmlDocumentTest.kt b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrixui/messages/ToHtmlDocumentTest.kt index ab83f77e661..9dee2063395 100644 --- a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrixui/messages/ToHtmlDocumentTest.kt +++ b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrixui/messages/ToHtmlDocumentTest.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.matrixui.messages import android.net.Uri import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.permalink.PermalinkParser import io.element.android.libraries.matrix.api.timeline.item.event.FormattedBody @@ -78,10 +79,8 @@ class ToHtmlDocumentTest { val document = body.toHtmlDocument(permalinkParser = object : PermalinkParser { override fun parse(uriString: String): PermalinkData { - return PermalinkData.UserLink("@alice:matrix.org") + return PermalinkData.UserLink(UserId("@alice:matrix.org")) } - - override fun parse(uri: Uri): PermalinkData = TODO("Not yet implemented") }) assertThat(document?.text()).isEqualTo("Hey @Alice!") } @@ -95,10 +94,8 @@ class ToHtmlDocumentTest { val document = body.toHtmlDocument(permalinkParser = object : PermalinkParser { override fun parse(uriString: String): PermalinkData { - return PermalinkData.UserLink("@alice:matrix.org") + return PermalinkData.UserLink(UserId("@alice:matrix.org")) } - - override fun parse(uri: Uri): PermalinkData = TODO("Not yet implemented") }) assertThat(document?.text()).isEqualTo("Hey @Alice!") } @@ -114,8 +111,6 @@ class ToHtmlDocumentTest { override fun parse(uriString: String): PermalinkData { return PermalinkData.FallbackLink(uri = Uri.parse("https://matrix.org")) } - - override fun parse(uri: Uri): PermalinkData = TODO("Not yet implemented") }) assertThat(document?.text()).isEqualTo("Hey Alice!") } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index 2ac322827c6..0fea92a0141 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -914,7 +914,6 @@ private fun ATextComposer( voiceMessageState = voiceMessageState, permalinkParser = object : PermalinkParser { override fun parse(uriString: String): PermalinkData = TODO("Not yet implemented") - override fun parse(uri: Uri): PermalinkData = TODO("Not yet implemented") }, composerMode = composerMode, enableTextFormatting = enableTextFormatting, diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt index e50b0fc16a4..4d705983ff7 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt @@ -18,7 +18,6 @@ package io.element.android.libraries.textcomposer.mentions import android.graphics.Color import android.graphics.Typeface -import android.net.Uri import android.view.ViewGroup import android.widget.TextView import androidx.compose.foundation.layout.PaddingValues @@ -41,6 +40,7 @@ import io.element.android.libraries.designsystem.theme.currentUserMentionPillTex import io.element.android.libraries.designsystem.theme.mentionPillBackground import io.element.android.libraries.designsystem.theme.mentionPillText import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.permalink.PermalinkParser import kotlinx.collections.immutable.persistentListOf @@ -80,7 +80,7 @@ class MentionSpanProvider( val (startPaddingPx, endPaddingPx) = paddingValuesPx.value return when { permalinkData is PermalinkData.UserLink -> { - val isCurrentUser = permalinkData.userId == currentSessionId.value + val isCurrentUser = permalinkData.userId == currentSessionId MentionSpan( type = MentionSpan.Type.USER, backgroundColor = if (isCurrentUser) currentUserBackgroundColor else otherBackgroundColor, @@ -137,19 +137,15 @@ internal fun MentionSpanPreview() { permalinkParser = object : PermalinkParser { override fun parse(uriString: String): PermalinkData { return when (uriString) { - "https://matrix.to/#/@me:matrix.org" -> PermalinkData.UserLink("@me:matrix.org") - "https://matrix.to/#/@other:matrix.org" -> PermalinkData.UserLink("@other:matrix.org") - "https://matrix.to/#/#room:matrix.org" -> PermalinkData.RoomLink( - roomIdOrAlias = "#room:matrix.org", - isRoomAlias = true, - eventId = null, + "https://matrix.to/#/@me:matrix.org" -> PermalinkData.UserLink(UserId("@me:matrix.org")) + "https://matrix.to/#/@other:matrix.org" -> PermalinkData.UserLink(UserId("@other:matrix.org")) + "https://matrix.to/#/#room:matrix.org" -> PermalinkData.RoomAliasLink( + roomAlias = "#room:matrix.org", viaParameters = persistentListOf(), ) else -> TODO() } } - - override fun parse(uri: Uri): PermalinkData = TODO() }, ) ElementPreview { diff --git a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt index 475b8798953..a5f31718cd0 100644 --- a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt +++ b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.textcomposer.impl.mentions import android.graphics.Color import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser @@ -50,7 +51,7 @@ class MentionSpanProviderTest { @Test fun `getting mention span for current user should return a MentionSpan with custom colors`() { - permalinkParser.givenResult(PermalinkData.UserLink(currentUserId.value)) + permalinkParser.givenResult(PermalinkData.UserLink(currentUserId)) val mentionSpan = mentionSpanProvider.getMentionSpanFor("@me:matrix.org", "https://matrix.to/#/${currentUserId.value}") assertThat(mentionSpan.backgroundColor).isEqualTo(myUserColor) assertThat(mentionSpan.textColor).isEqualTo(myUserColor) @@ -58,7 +59,7 @@ class MentionSpanProviderTest { @Test fun `getting mention span for other user should return a MentionSpan with normal colors`() { - permalinkParser.givenResult(PermalinkData.UserLink("@other:matrix.org")) + permalinkParser.givenResult(PermalinkData.UserLink(UserId("@other:matrix.org"))) val mentionSpan = mentionSpanProvider.getMentionSpanFor("@other:matrix.org", "https://matrix.to/#/@other:matrix.org") assertThat(mentionSpan.backgroundColor).isEqualTo(otherColor) assertThat(mentionSpan.textColor).isEqualTo(otherColor) @@ -67,10 +68,8 @@ class MentionSpanProviderTest { @Test fun `getting mention span for a room should return a MentionSpan with normal colors`() { permalinkParser.givenResult( - PermalinkData.RoomLink( - roomIdOrAlias = "#room:matrix.org", - isRoomAlias = true, - eventId = null, + PermalinkData.RoomAliasLink( + roomAlias = "#room:matrix.org", viaParameters = persistentListOf(), ) ) @@ -82,10 +81,8 @@ class MentionSpanProviderTest { @Test fun `getting mention span for @room should return a MentionSpan with normal colors`() { permalinkParser.givenResult( - PermalinkData.RoomLink( - roomIdOrAlias = "#", - isRoomAlias = true, - eventId = null, + PermalinkData.RoomAliasLink( + roomAlias = "#", viaParameters = persistentListOf(), ) ) diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/OnlyFallbackPermalinkParser.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/OnlyFallbackPermalinkParser.kt index 50903a910d1..9825f29268b 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/OnlyFallbackPermalinkParser.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/OnlyFallbackPermalinkParser.kt @@ -24,8 +24,4 @@ class OnlyFallbackPermalinkParser : PermalinkParser { override fun parse(uriString: String): PermalinkData { return PermalinkData.FallbackLink(Uri.parse(uriString)) } - - override fun parse(uri: Uri): PermalinkData { - return PermalinkData.FallbackLink(uri) - } } From 83a49a88a552ff34e0ff29f543ac91e8b86f9e62 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 15 Apr 2024 17:51:51 +0200 Subject: [PATCH 2/3] changelog --- changelog.d/2709.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2709.misc diff --git a/changelog.d/2709.misc b/changelog.d/2709.misc new file mode 100644 index 00000000000..96328571cc4 --- /dev/null +++ b/changelog.d/2709.misc @@ -0,0 +1 @@ + Parse permalink using parseMatrixEntityFrom from the SDK From 9ee36e9ca4dc15c19b2daa3bec996b2c96054c7f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 15 Apr 2024 17:57:15 +0200 Subject: [PATCH 3/3] Remove unnecessary tests. --- .../permalink/DefaultPermalinkParserTest.kt | 214 ------------------ 1 file changed, 214 deletions(-) delete mode 100644 libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParserTest.kt diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParserTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParserTest.kt deleted file mode 100644 index af9043be721..00000000000 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/permalink/DefaultPermalinkParserTest.kt +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (c) 2024 New Vector Ltd - * - * 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 io.element.android.libraries.matrix.impl.permalink - -import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.matrix.api.core.EventId -import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.matrix.api.permalink.PermalinkData -import kotlinx.collections.immutable.persistentListOf -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner - -@RunWith(RobolectricTestRunner::class) -class DefaultPermalinkParserTest { - @Test - fun `parsing an invalid url returns a fallback link`() { - val sut = DefaultPermalinkParser( - matrixToConverter = DefaultMatrixToConverter(), - ) - val url = "https://element.io" - assertThat(sut.parse(url)).isInstanceOf(PermalinkData.FallbackLink::class.java) - } - - @Test - fun `parsing an invalid url with the right path but no content returns a fallback link`() { - val sut = DefaultPermalinkParser( - matrixToConverter = DefaultMatrixToConverter(), - ) - val url = "https://app.element.io/#/user" - assertThat(sut.parse(url)).isInstanceOf(PermalinkData.FallbackLink::class.java) - } - - @Test - fun `parsing an invalid url with the right path but empty content returns a fallback link`() { - val sut = DefaultPermalinkParser( - matrixToConverter = DefaultMatrixToConverter(), - ) - val url = "https://app.element.io/#/user/" - assertThat(sut.parse(url)).isInstanceOf(PermalinkData.FallbackLink::class.java) - } - - @Test - fun `parsing an invalid url with the right path but invalid content returns a fallback link`() { - val sut = DefaultPermalinkParser( - matrixToConverter = DefaultMatrixToConverter(), - ) - val url = "https://app.element.io/#/user/some%20user!" - assertThat(sut.parse(url)).isInstanceOf(PermalinkData.FallbackLink::class.java) - } - - @Test - fun `parsing a valid user url returns a user link`() { - val sut = DefaultPermalinkParser( - matrixToConverter = DefaultMatrixToConverter(), - ) - val url = "https://app.element.io/#/user/@test:matrix.org" - assertThat(sut.parse(url)).isEqualTo( - PermalinkData.UserLink( - userId = UserId("@test:matrix.org") - ) - ) - } - - @Test - fun `parsing a valid room id url returns a room link`() { - val sut = DefaultPermalinkParser( - matrixToConverter = DefaultMatrixToConverter(), - ) - val url = "https://app.element.io/#/room/!aBCD1234:matrix.org" - assertThat(sut.parse(url)).isEqualTo( - PermalinkData.RoomIdLink( - roomId = RoomId("!aBCD1234:matrix.org"), - viaParameters = persistentListOf(), - ) - ) - } - - @Test - fun `parsing a valid room id with event id url returns a room link`() { - val sut = DefaultPermalinkParser( - matrixToConverter = DefaultMatrixToConverter(), - ) - val url = "https://app.element.io/#/room/!aBCD1234:matrix.org/$1234567890abcdef:matrix.org" - assertThat(sut.parse(url)).isEqualTo( - PermalinkData.EventIdLink( - roomId = RoomId("!aBCD1234:matrix.org"), - eventId = EventId("$1234567890abcdef:matrix.org"), - viaParameters = persistentListOf(), - ) - ) - } - - @Test - fun `parsing a valid room id with and invalid event id url returns a room link with no event id`() { - val sut = DefaultPermalinkParser( - matrixToConverter = DefaultMatrixToConverter(), - ) - val url = "https://app.element.io/#/room/!aBCD1234:matrix.org/1234567890abcdef:matrix.org" - assertThat(sut.parse(url)).isEqualTo( - PermalinkData.RoomIdLink( - roomId = RoomId("!aBCD1234:matrix.org"), - viaParameters = persistentListOf(), - ) - ) - } - - @Test - fun `parsing a valid room id with event id and via parameters url returns a room link`() { - val sut = DefaultPermalinkParser( - matrixToConverter = DefaultMatrixToConverter(), - ) - val url = "https://app.element.io/#/room/!aBCD1234:matrix.org/$1234567890abcdef:matrix.org?via=matrix.org&via=matrix.com" - assertThat(sut.parse(url)).isEqualTo( - PermalinkData.EventIdLink( - roomId = RoomId("!aBCD1234:matrix.org"), - eventId = EventId("$1234567890abcdef:matrix.org"), - viaParameters = persistentListOf("matrix.org", "matrix.com"), - ) - ) - } - - @Test - fun `parsing a valid room alias url returns a room link`() { - val sut = DefaultPermalinkParser( - matrixToConverter = DefaultMatrixToConverter(), - ) - val url = "https://app.element.io/#/room/#element-android:matrix.org" - assertThat(sut.parse(url)).isEqualTo( - PermalinkData.RoomAliasLink( - roomAlias = "#element-android:matrix.org", - viaParameters = persistentListOf(), - ) - ) - } - - @Test - fun `parsing a valid room alias with eventId url returns a room link`() { - val sut = DefaultPermalinkParser( - matrixToConverter = DefaultMatrixToConverter(), - ) - val url = "https://app.element.io/#/room/#element-android:matrix.org/$1234567890abcdef:matrix.org" - assertThat(sut.parse(url)).isEqualTo( - PermalinkData.EventIdAliasLink( - roomAlias = "#element-android:matrix.org", - eventId = EventId("$1234567890abcdef:matrix.org"), - viaParameters = persistentListOf(), - ) - ) - } - - @Test - fun `parsing a url with an invalid signurl returns a fallback link`() { - val sut = DefaultPermalinkParser( - matrixToConverter = DefaultMatrixToConverter(), - ) - // This url has no private key - val url = "https://app.element.io/#/room/%21aBCDEF12345%3Amatrix.org" + - "?email=testuser%40element.io" + - "&signurl=https%3A%2F%2Fvector.im%2F_matrix%2Fidentity%2Fapi%2Fv1%2Fsign-ed25519%3Ftoken%3Da_token" + - "&room_name=TestRoom" + - "&room_avatar_url=" + - "&inviter_name=User" + - "&guest_access_token=" + - "&guest_user_id=" + - "&room_type=" - assertThat(sut.parse(url)).isInstanceOf(PermalinkData.FallbackLink::class.java) - } - - @Test - fun `parsing a url with signurl returns a room email invite link`() { - val sut = DefaultPermalinkParser( - matrixToConverter = DefaultMatrixToConverter(), - ) - val url = "https://app.element.io/#/room/%21aBCDEF12345%3Amatrix.org" + - "?email=testuser%40element.io" + - "&signurl=https%3A%2F%2Fvector.im%2F_matrix%2Fidentity%2Fapi%2Fv1%2Fsign-ed25519%3Ftoken%3Da_token%26private_key%3Da_private_key" + - "&room_name=TestRoom" + - "&room_avatar_url=" + - "&inviter_name=User" + - "&guest_access_token=" + - "&guest_user_id=" + - "&room_type=" - assertThat(sut.parse(url)).isEqualTo( - PermalinkData.RoomEmailInviteLink( - roomId = RoomId("!aBCDEF12345:matrix.org"), - email = "testuser@element.io", - signUrl = "https://vector.im/_matrix/identity/api/v1/sign-ed25519?token=a_token&private_key=a_private_key", - roomName = "TestRoom", - roomAvatarUrl = "", - inviterName = "User", - identityServer = "vector.im", - token = "a_token", - privateKey = "a_private_key", - roomType = "", - ) - ) - } -}