Handle tapping on user mentions (#2021)
This commit is contained in:
committed by
GitHub
parent
8d0db8d6d3
commit
3b59b70e65
1
changelog.d/1448.feature
Normal file
1
changelog.d/1448.feature
Normal file
@@ -0,0 +1 @@
|
||||
Tapping on a user mention pill opens their profile.
|
||||
@@ -44,6 +44,7 @@ import io.element.android.libraries.di.SingleIn
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.Mention
|
||||
@@ -335,7 +336,7 @@ class MessageComposerPresenter @Inject constructor(
|
||||
add(Mention.AtRoom)
|
||||
}
|
||||
for (userId in state.userIds) {
|
||||
add(Mention.User(userId))
|
||||
add(Mention.User(UserId(userId)))
|
||||
}
|
||||
}
|
||||
}.orEmpty()
|
||||
|
||||
@@ -62,8 +62,8 @@ import androidx.compose.ui.zIndex
|
||||
import androidx.constraintlayout.compose.ConstrainScope
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.features.messages.impl.timeline.TimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
||||
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
|
||||
import io.element.android.features.messages.impl.timeline.components.event.toExtraPadding
|
||||
@@ -98,10 +98,10 @@ 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.permalink.PermalinkData
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
||||
import io.element.android.libraries.matrix.api.room.Mention
|
||||
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@@ -138,6 +138,13 @@ fun TimelineItemEventRow(
|
||||
inReplyToClick(inReplyToEventId)
|
||||
}
|
||||
|
||||
fun onMentionClicked(mention: Mention) {
|
||||
when (mention) {
|
||||
is Mention.User -> onUserDataClick(mention.userId)
|
||||
else -> Unit // TODO implement actions for other mentions being clicked
|
||||
}
|
||||
}
|
||||
|
||||
Column(modifier = modifier.fillMaxWidth()) {
|
||||
if (event.groupPosition.isNew()) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
@@ -182,6 +189,7 @@ fun TimelineItemEventRow(
|
||||
onReactionClicked = { emoji -> onReactionClick(emoji, event) },
|
||||
onReactionLongClicked = { emoji -> onReactionLongClick(emoji, event) },
|
||||
onMoreReactionsClicked = { onMoreReactionsClick(event) },
|
||||
onMentionClicked = ::onMentionClicked,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
}
|
||||
@@ -200,6 +208,7 @@ fun TimelineItemEventRow(
|
||||
onReactionClicked = { emoji -> onReactionClick(emoji, event) },
|
||||
onReactionLongClicked = { emoji -> onReactionLongClick(emoji, event) },
|
||||
onMoreReactionsClicked = { onMoreReactionsClick(event) },
|
||||
onMentionClicked = ::onMentionClicked,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
}
|
||||
@@ -254,6 +263,7 @@ private fun TimelineItemEventRowContent(
|
||||
onReactionClicked: (emoji: String) -> Unit,
|
||||
onReactionLongClicked: (emoji: String) -> Unit,
|
||||
onMoreReactionsClicked: (event: TimelineItem.Event) -> Unit,
|
||||
onMentionClicked: (Mention) -> Unit,
|
||||
eventSink: (TimelineEvents) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
@@ -316,6 +326,7 @@ private fun TimelineItemEventRowContent(
|
||||
onTimestampClicked = {
|
||||
onTimestampClicked(event)
|
||||
},
|
||||
onMentionClicked = onMentionClicked,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
}
|
||||
@@ -387,6 +398,7 @@ private fun MessageEventBubbleContent(
|
||||
onMessageLongClick: () -> Unit,
|
||||
inReplyToClick: () -> Unit,
|
||||
onTimestampClicked: () -> Unit,
|
||||
onMentionClicked: (Mention) -> Unit,
|
||||
eventSink: (TimelineEvents) -> Unit,
|
||||
@SuppressLint("ModifierParameter")
|
||||
@Suppress("ModifierNaming")
|
||||
@@ -512,15 +524,17 @@ private fun MessageEventBubbleContent(
|
||||
isMine = event.isMine,
|
||||
isEditable = event.isEditable,
|
||||
onLinkClicked = { url ->
|
||||
Timber.d("Clicked on: $url")
|
||||
when (PermalinkParser.parse(Uri.parse(url))) {
|
||||
when (val permalink = PermalinkParser.parse(Uri.parse(url))) {
|
||||
is PermalinkData.UserLink -> {
|
||||
// TODO open member details
|
||||
onMentionClicked(Mention.User(UserId(permalink.userId)))
|
||||
}
|
||||
is PermalinkData.FallbackLink -> {
|
||||
is PermalinkData.RoomLink -> {
|
||||
onMentionClicked(Mention.Room(permalink.getRoomId(), permalink.getRoomAlias()))
|
||||
}
|
||||
is PermalinkData.FallbackLink,
|
||||
is PermalinkData.RoomEmailInviteLink -> {
|
||||
context.openUrlInExternalApp(url)
|
||||
}
|
||||
else -> Unit // TODO handle other types of links, as room ones
|
||||
}
|
||||
},
|
||||
extraPadding = event.toExtraPadding(),
|
||||
|
||||
@@ -862,7 +862,7 @@ class MessageComposerPresenterTest {
|
||||
|
||||
advanceUntilIdle()
|
||||
|
||||
assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID.value)))
|
||||
assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID)))
|
||||
|
||||
// Check intentional mentions on reply sent
|
||||
initialState.eventSink(MessageComposerEvents.SetMode(aReplyMode()))
|
||||
@@ -877,7 +877,7 @@ class MessageComposerPresenterTest {
|
||||
initialState.eventSink(MessageComposerEvents.SendMessage(A_MESSAGE.toMessage()))
|
||||
advanceUntilIdle()
|
||||
|
||||
assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID_2.value)))
|
||||
assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID_2)))
|
||||
|
||||
// Check intentional mentions on edit message
|
||||
skipItems(1)
|
||||
@@ -893,7 +893,7 @@ class MessageComposerPresenterTest {
|
||||
initialState.eventSink(MessageComposerEvents.SendMessage(A_MESSAGE.toMessage()))
|
||||
advanceUntilIdle()
|
||||
|
||||
assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID_3.value)))
|
||||
assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID_3)))
|
||||
|
||||
skipItems(1)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ 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.RoomId
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
/**
|
||||
@@ -32,7 +33,15 @@ sealed interface PermalinkData {
|
||||
val isRoomAlias: Boolean,
|
||||
val eventId: String?,
|
||||
val viaParameters: ImmutableList<String>
|
||||
) : PermalinkData
|
||||
) : PermalinkData {
|
||||
fun getRoomId(): RoomId? {
|
||||
return roomIdOrAlias.takeIf { !isRoomAlias }?.let(::RoomId)
|
||||
}
|
||||
|
||||
fun getRoomAlias(): String? {
|
||||
return roomIdOrAlias.takeIf { isRoomAlias }
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* &room_name=Team2
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
package io.element.android.libraries.matrix.api.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
|
||||
sealed interface Mention {
|
||||
data class User(val userId: String): Mention
|
||||
data class User(val userId: UserId): Mention
|
||||
data object AtRoom: Mention
|
||||
data class Room(val roomId: RoomId?, val roomAlias: String?): Mention
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,6 +21,6 @@ import org.matrix.rustcomponents.sdk.Mentions
|
||||
|
||||
fun List<Mention>.map(): Mentions {
|
||||
val hasAtRoom = any { it is Mention.AtRoom }
|
||||
val userIds = filterIsInstance<Mention.User>().map { it.userId }
|
||||
val userIds = filterIsInstance<Mention.User>().map { it.userId.value }
|
||||
return Mentions(userIds, hasAtRoom)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user