Merge branch 'develop' of https://github.com/vector-im/element-x-android into dla/feature/custom_room_notification_settings_list

This commit is contained in:
David Langley 2023-10-18 22:07:14 +01:00
commit 87b8bfe99d
1981 changed files with 15504 additions and 5063 deletions

View file

@ -18,12 +18,18 @@ package io.element.android.libraries.matrix.api.auth
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.sessionstorage.api.LoggedInState
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
interface MatrixAuthenticationService {
fun isLoggedIn(): Flow<Boolean>
fun loggedInStateFlow(): Flow<LoggedInState>
suspend fun getLatestSessionId(): SessionId?
/**
* Restore a session from a [sessionId].
* Do not restore anything it the access token is not valid anymore.
*/
suspend fun restoreSession(sessionId: SessionId): Result<MatrixClient>
fun getHomeserverDetails(): StateFlow<MatrixHomeServerDetails?>
suspend fun setHomeserver(homeserver: String): Result<Unit>

View file

@ -61,6 +61,10 @@ sealed interface NotificationContent {
) : MessageLike
data object RoomRedaction : MessageLike
data object Sticker : MessageLike
data class Poll(
val senderId: UserId,
val question: String,
) : MessageLike
}
sealed interface StateEvent : NotificationContent {

View file

@ -77,9 +77,9 @@ interface MatrixRoom : Closeable {
fun destroy()
fun subscribeToSync()
suspend fun subscribeToSync()
fun unsubscribeFromSync()
suspend fun unsubscribeFromSync()
suspend fun userDisplayName(userId: UserId): Result<String?>
@ -89,6 +89,8 @@ interface MatrixRoom : Closeable {
suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?): Result<Unit>
suspend fun enterSpecialMode(eventId: EventId?): Result<Unit>
suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?): Result<Unit>
suspend fun redactEvent(eventId: EventId, reason: String? = null): Result<Unit>
@ -183,8 +185,12 @@ interface MatrixRoom : Closeable {
*/
suspend fun endPoll(pollStartId: EventId, text: String): Result<Unit>
suspend fun sendVoiceMessage(
file: File,
audioInfo: AudioInfo,
waveform: List<Int>,
progressCallback: ProgressCallback?
): Result<MediaUploadHandler>
override fun close() = destroy()
}

View file

@ -17,7 +17,7 @@
package io.element.android.libraries.matrix.api.room
sealed interface MatrixRoomNotificationSettingsState {
object Unknown : MatrixRoomNotificationSettingsState
data object Unknown : MatrixRoomNotificationSettingsState
data class Pending(val prevRoomNotificationSettings: RoomNotificationSettings? = null) : MatrixRoomNotificationSettingsState
data class Error(val failure: Throwable, val prevRoomNotificationSettings: RoomNotificationSettings? = null) : MatrixRoomNotificationSettingsState
data class Ready(val roomNotificationSettings: RoomNotificationSettings) : MatrixRoomNotificationSettingsState

View file

@ -16,13 +16,8 @@
package io.element.android.libraries.matrix.api.timeline.item.event
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.media.AudioInfo
import io.element.android.libraries.matrix.api.media.FileInfo
import io.element.android.libraries.matrix.api.media.ImageInfo
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.api.media.VideoInfo
import io.element.android.libraries.matrix.api.poll.PollAnswer
import io.element.android.libraries.matrix.api.poll.PollKind
@ -33,37 +28,10 @@ data class MessageContent(
val inReplyTo: InReplyTo?,
val isEdited: Boolean,
val isThreaded: Boolean,
val type: MessageType?
val type: MessageType
) : EventContent
sealed interface InReplyTo {
/** The event details are not loaded yet. We can fetch them. */
data class NotLoaded(val eventId: EventId) : InReplyTo
/** The event details are pending to be fetched. We should **not** fetch them again. */
data object Pending : InReplyTo
/** The event details are available. */
data class Ready(
val eventId: EventId,
val content: EventContent,
val senderId: UserId,
val senderDisplayName: String?,
val senderAvatarUrl: String?,
) : InReplyTo
/**
* Fetching the event details failed.
*
* We can try to fetch them again **with a proper retry strategy**, but not blindly:
*
* If the reason for the failure is consistent on the server, we'd enter a loop
* where we keep trying to fetch the same event.
* */
data object Error : InReplyTo
}
object RedactedContent : EventContent
data object RedactedContent : EventContent
data class StickerContent(
val body: String,
@ -124,106 +92,4 @@ data class FailedToParseStateContent(
val error: String
) : EventContent
object UnknownContent : EventContent
sealed interface MessageType
object UnknownMessageType : MessageType
enum class MessageFormat {
HTML, UNKNOWN
}
data class FormattedBody(
val format: MessageFormat,
val body: String
)
data class EmoteMessageType(
val body: String,
val formatted: FormattedBody?
) : MessageType
data class ImageMessageType(
val body: String,
val source: MediaSource,
val info: ImageInfo?
) : MessageType
data class LocationMessageType(
val body: String,
val geoUri: String,
val description: String?,
) : MessageType
data class AudioMessageType(
val body: String,
val source: MediaSource,
val info: AudioInfo?
) : MessageType
data class VideoMessageType(
val body: String,
val source: MediaSource,
val info: VideoInfo?
) : MessageType
data class FileMessageType(
val body: String,
val source: MediaSource,
val info: FileInfo?
) : MessageType
data class NoticeMessageType(
val body: String,
val formatted: FormattedBody?
) : MessageType
data class TextMessageType(
val body: String,
val formatted: FormattedBody?
) : MessageType
enum class MembershipChange {
NONE,
ERROR,
JOINED,
LEFT,
BANNED,
UNBANNED,
KICKED,
INVITED,
KICKED_AND_BANNED,
INVITATION_ACCEPTED,
INVITATION_REJECTED,
INVITATION_REVOKED,
KNOCKED,
KNOCK_ACCEPTED,
KNOCK_RETRACTED,
KNOCK_DENIED,
NOT_IMPLEMENTED;
}
sealed interface OtherState {
data object PolicyRuleRoom : OtherState
data object PolicyRuleServer : OtherState
data object PolicyRuleUser : OtherState
data object RoomAliases : OtherState
data class RoomAvatar(val url: String?) : OtherState
data object RoomCanonicalAlias : OtherState
data object RoomCreate : OtherState
data object RoomEncryption : OtherState
data object RoomGuestAccess : OtherState
data object RoomHistoryVisibility : OtherState
data object RoomJoinRules : OtherState
data class RoomName(val name: String?) : OtherState
data object RoomPinnedEvents : OtherState
data object RoomPowerLevels : OtherState
data object RoomServerAcl : OtherState
data class RoomThirdPartyInvite(val displayName: String?) : OtherState
data object RoomTombstone : OtherState
data class RoomTopic(val topic: String?) : OtherState
data object SpaceChild : OtherState
data object SpaceParent : OtherState
data class Custom(val eventType: String) : OtherState
}
data object UnknownContent : EventContent

View file

@ -0,0 +1,22 @@
/*
* 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.timeline.item.event
data class FormattedBody(
val format: MessageFormat,
val body: String
)

View file

@ -0,0 +1,47 @@
/*
* 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.timeline.item.event
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId
sealed interface InReplyTo {
/** The event details are not loaded yet. We can fetch them. */
data class NotLoaded(val eventId: EventId) : InReplyTo
/** The event details are pending to be fetched. We should **not** fetch them again. */
data object Pending : InReplyTo
/** The event details are available. */
data class Ready(
val eventId: EventId,
val content: EventContent,
val senderId: UserId,
val senderDisplayName: String?,
val senderAvatarUrl: String?,
) : InReplyTo
/**
* Fetching the event details failed.
*
* We can try to fetch them again **with a proper retry strategy**, but not blindly:
*
* If the reason for the failure is consistent on the server, we'd enter a loop
* where we keep trying to fetch the same event.
* */
data object Error : InReplyTo
}

View file

@ -0,0 +1,37 @@
/*
* 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.timeline.item.event
enum class MembershipChange {
NONE,
ERROR,
JOINED,
LEFT,
BANNED,
UNBANNED,
KICKED,
INVITED,
KICKED_AND_BANNED,
INVITATION_ACCEPTED,
INVITATION_REJECTED,
INVITATION_REVOKED,
KNOCKED,
KNOCK_ACCEPTED,
KNOCK_RETRACTED,
KNOCK_DENIED,
NOT_IMPLEMENTED;
}

View file

@ -0,0 +1,21 @@
/*
* 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.timeline.item.event
enum class MessageFormat {
HTML, UNKNOWN
}

View file

@ -0,0 +1,72 @@
/*
* 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.timeline.item.event
import io.element.android.libraries.matrix.api.media.AudioInfo
import io.element.android.libraries.matrix.api.media.FileInfo
import io.element.android.libraries.matrix.api.media.ImageInfo
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.api.media.VideoInfo
sealed interface MessageType
data object UnknownMessageType : MessageType
data class EmoteMessageType(
val body: String,
val formatted: FormattedBody?
) : MessageType
data class ImageMessageType(
val body: String,
val source: MediaSource,
val info: ImageInfo?
) : MessageType
data class LocationMessageType(
val body: String,
val geoUri: String,
val description: String?,
) : MessageType
data class AudioMessageType(
val body: String,
val source: MediaSource,
val info: AudioInfo?
) : MessageType
data class VideoMessageType(
val body: String,
val source: MediaSource,
val info: VideoInfo?
) : MessageType
data class FileMessageType(
val body: String,
val source: MediaSource,
val info: FileInfo?
) : MessageType
data class NoticeMessageType(
val body: String,
val formatted: FormattedBody?
) : MessageType
data class TextMessageType(
val body: String,
val formatted: FormattedBody?
) : MessageType

View file

@ -0,0 +1,41 @@
/*
* 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.timeline.item.event
sealed interface OtherState {
data object PolicyRuleRoom : OtherState
data object PolicyRuleServer : OtherState
data object PolicyRuleUser : OtherState
data object RoomAliases : OtherState
data class RoomAvatar(val url: String?) : OtherState
data object RoomCanonicalAlias : OtherState
data object RoomCreate : OtherState
data object RoomEncryption : OtherState
data object RoomGuestAccess : OtherState
data object RoomHistoryVisibility : OtherState
data object RoomJoinRules : OtherState
data class RoomName(val name: String?) : OtherState
data object RoomPinnedEvents : OtherState
data object RoomPowerLevels : OtherState
data object RoomServerAcl : OtherState
data class RoomThirdPartyInvite(val displayName: String?) : OtherState
data object RoomTombstone : OtherState
data class RoomTopic(val topic: String?) : OtherState
data object SpaceChild : OtherState
data object SpaceParent : OtherState
data class Custom(val eventType: String) : OtherState
}

View file

@ -19,7 +19,7 @@ package io.element.android.libraries.matrix.api.auth
import com.google.common.truth.Truth.assertThat
import org.junit.Test
class AuthErrorCodeTests {
class AuthErrorCodeTest {
@Test
fun `errorCode finds UNKNOWN code`() {

View file

@ -0,0 +1,58 @@
/*
* 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 android.net.Uri
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
@RunWith(RobolectricTestRunner::class)
class MatrixToConverterTest {
@Test
fun `converting a matrix-to url does nothing`() {
val url = Uri.parse("https://matrix.to/#/#element-android:matrix.org")
assertThat(MatrixToConverter.convert(url)).isEqualTo(url)
}
@Test
fun `converting a url with a supported room path returns a matrix-to url`() {
val url = Uri.parse("https://riot.im/develop/#/room/#element-android:matrix.org")
assertThat(MatrixToConverter.convert(url)).isEqualTo(Uri.parse("https://matrix.to/#/#element-android:matrix.org"))
}
@Test
fun `converting a url with a supported user path returns a matrix-to url`() {
val url = Uri.parse("https://riot.im/develop/#/user/@test:matrix.org")
assertThat(MatrixToConverter.convert(url)).isEqualTo(Uri.parse("https://matrix.to/#/@test:matrix.org"))
}
@Test
fun `converting a url with a supported group path returns a matrix-to url`() {
val url = Uri.parse("https://riot.im/develop/#/group/+group:matrix.org")
assertThat(MatrixToConverter.convert(url)).isEqualTo(Uri.parse("https://matrix.to/#/+group:matrix.org"))
}
@Test
fun `converting an unsupported url returns null`() {
val url = Uri.parse("https://element.io/")
assertThat(MatrixToConverter.convert(url)).isNull()
}
}

View file

@ -0,0 +1,81 @@
/*
* 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 io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.tests.testutils.assertThrowsInDebug
import io.element.android.tests.testutils.isInDebug
import org.junit.Test
class PermalinkBuilderTest {
fun `building a permalink for an invalid user id throws when verifying the id`() {
assertThrowsInDebug {
val userId = UserId("some invalid user id")
PermalinkBuilder.permalinkForUser(userId)
}
}
fun `building a permalink for an invalid room id throws when verifying the id`() {
assertThrowsInDebug {
val roomId = RoomId("some invalid room id")
PermalinkBuilder.permalinkForRoomId(roomId)
}
}
@Test
fun `building a permalink for an invalid user id returns failure when not verifying the id`() {
if (!isInDebug()) {
val userId = UserId("some invalid user id")
assertThat(PermalinkBuilder.permalinkForUser(userId).isFailure).isTrue()
}
}
@Test
fun `building a permalink for an invalid room id returns failure when not verifying the id`() {
if (!isInDebug()) {
val roomId = RoomId("some invalid room id")
assertThat(PermalinkBuilder.permalinkForRoomId(roomId).isFailure).isTrue()
}
}
@Test
fun `building a permalink for an invalid room alias returns failure`() {
val roomAlias = "an invalid room alias"
assertThat(PermalinkBuilder.permalinkForRoomAlias(roomAlias).isFailure).isTrue()
}
@Test
fun `building a permalink for a valid user id returns a matrix-to url`() {
val userId = UserId("@user:matrix.org")
assertThat(PermalinkBuilder.permalinkForUser(userId).getOrNull()).isEqualTo("https://matrix.to/#/@user:matrix.org")
}
@Test
fun `building a permalink for a valid room id returns a matrix-to url`() {
val roomId = RoomId("!aBCdEFG1234:matrix.org")
assertThat(PermalinkBuilder.permalinkForRoomId(roomId).getOrNull()).isEqualTo("https://matrix.to/#/!aBCdEFG1234:matrix.org")
}
@Test
fun `building a permalink for a valid room alias returns a matrix-to url`() {
val roomAlias = "#room:matrix.org"
assertThat(PermalinkBuilder.permalinkForRoomAlias(roomAlias).getOrNull()).isEqualTo("https://matrix.to/#/#room:matrix.org")
}
}

View file

@ -0,0 +1,167 @@
/*
* 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 org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
@RunWith(RobolectricTestRunner::class)
class PermalinkParserTest {
@Test
fun `parsing an invalid url returns a fallback link`() {
val url = "https://element.io"
assertThat(PermalinkParser.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 url = "https://app.element.io/#/user"
assertThat(PermalinkParser.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 url = "https://app.element.io/#/user/"
assertThat(PermalinkParser.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 url = "https://app.element.io/#/user/some%20user!"
assertThat(PermalinkParser.parse(url)).isInstanceOf(PermalinkData.FallbackLink::class.java)
}
@Test
fun `parsing a valid user url returns a user link`() {
val url = "https://app.element.io/#/user/@test:matrix.org"
assertThat(PermalinkParser.parse(url)).isEqualTo(
PermalinkData.UserLink(
userId = "@test:matrix.org"
)
)
}
@Test
fun `parsing a valid room id url returns a room link`() {
val url = "https://app.element.io/#/room/!aBCD1234:matrix.org"
assertThat(PermalinkParser.parse(url)).isEqualTo(
PermalinkData.RoomLink(
roomIdOrAlias = "!aBCD1234:matrix.org",
isRoomAlias = false,
eventId = null,
viaParameters = emptyList(),
)
)
}
@Test
fun `parsing a valid room id with event id url returns a room link`() {
val url = "https://app.element.io/#/room/!aBCD1234:matrix.org/$1234567890abcdef:matrix.org"
assertThat(PermalinkParser.parse(url)).isEqualTo(
PermalinkData.RoomLink(
roomIdOrAlias = "!aBCD1234:matrix.org",
isRoomAlias = false,
eventId = "\$1234567890abcdef:matrix.org",
viaParameters = emptyList(),
)
)
}
@Test
fun `parsing a valid room id with and invalid event id url returns a room link with no event id`() {
val url = "https://app.element.io/#/room/!aBCD1234:matrix.org/1234567890abcdef:matrix.org"
assertThat(PermalinkParser.parse(url)).isEqualTo(
PermalinkData.RoomLink(
roomIdOrAlias = "!aBCD1234:matrix.org",
isRoomAlias = false,
eventId = null,
viaParameters = emptyList(),
)
)
}
@Test
fun `parsing a valid room id with event id and via parameters url returns a room link`() {
val url = "https://app.element.io/#/room/!aBCD1234:matrix.org/$1234567890abcdef:matrix.org?via=matrix.org&via=matrix.com"
assertThat(PermalinkParser.parse(url)).isEqualTo(
PermalinkData.RoomLink(
roomIdOrAlias = "!aBCD1234:matrix.org",
isRoomAlias = false,
eventId = "\$1234567890abcdef:matrix.org",
viaParameters = listOf("matrix.org", "matrix.com"),
)
)
}
@Test
fun `parsing a valid room alias url returns a room link`() {
val url = "https://app.element.io/#/room/#element-android:matrix.org"
assertThat(PermalinkParser.parse(url)).isEqualTo(
PermalinkData.RoomLink(
roomIdOrAlias = "#element-android:matrix.org",
isRoomAlias = true,
eventId = null,
viaParameters = emptyList(),
)
)
}
@Test
fun `parsing a url with an invalid signurl returns a fallback link`() {
// 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(PermalinkParser.parse(url)).isInstanceOf(PermalinkData.FallbackLink::class.java)
}
@Test
fun `parsing a url with signurl returns a room email invite link`() {
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(PermalinkParser.parse(url)).isEqualTo(
PermalinkData.RoomEmailInviteLink(
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 = "",
)
)
}
}