Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tapping user mxid or room alias copies it to the clipboard #3502

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
data object LeaveRoom : RoomDetailsEvent
data object MuteNotification : RoomDetailsEvent
data object UnmuteNotification : RoomDetailsEvent
data class CopyID(val text: String) : RoomDetailsEvent

Check warning on line 14 in features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsEvent.kt

View check run for this annotation

Codecov / codecov/patch

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsEvent.kt#L14

Added line #L14 was not covered by tests
data class SetFavorite(val isFavorite: Boolean) : RoomDetailsEvent
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,13 @@
import io.element.android.features.leaveroom.api.LeaveRoomPresenter
import io.element.android.features.messages.api.pinned.IsPinnedMessagesFeatureEnabled
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter
import io.element.android.libraries.androidutils.clipboard.ClipboardHelper
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.MatrixClient
Expand All @@ -41,6 +45,7 @@
import io.element.android.libraries.matrix.ui.room.getCurrentRoomMember
import io.element.android.libraries.matrix.ui.room.getDirectRoomMember
import io.element.android.libraries.matrix.ui.room.isOwnUserAdmin
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
import kotlinx.collections.immutable.toPersistentList
Expand All @@ -60,6 +65,8 @@
private val dispatchers: CoroutineDispatchers,
private val analyticsService: AnalyticsService,
private val isPinnedMessagesFeatureEnabled: IsPinnedMessagesFeatureEnabled,
private val clipboardHelper: ClipboardHelper,
private val snackbarDispatcher: SnackbarDispatcher,
) : Presenter<RoomDetailsState> {
@Composable
override fun present(): RoomDetailsState {
Expand Down Expand Up @@ -110,6 +117,7 @@
}
}

val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
val roomNotificationSettingsState by room.roomNotificationSettingsStateFlow.collectAsState()

fun handleEvents(event: RoomDetailsEvent) {
Expand All @@ -126,6 +134,12 @@
client.notificationSettingsService().unmuteRoom(room.roomId, room.isEncrypted, room.isOneToOne)
}
}
is RoomDetailsEvent.CopyID -> {
scope.launch(dispatchers.io) {

Check warning on line 138 in features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt

View check run for this annotation

Codecov / codecov/patch

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt#L138

Added line #L138 was not covered by tests
clipboardHelper.copyPlainText(event.text)
snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_copied_to_clipboard))
}
}
is RoomDetailsEvent.SetFavorite -> scope.setFavorite(event.isFavorite)
}
}
Expand Down Expand Up @@ -154,6 +168,7 @@
heroes = roomInfo?.heroes.orEmpty().toPersistentList(),
canShowPinnedMessages = canShowPinnedMessages,
pinnedMessagesCount = pinnedMessagesCount,
snackbarMessage = snackbarMessage,
eventSink = ::handleEvents,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package io.element.android.features.roomdetails.impl
import androidx.compose.runtime.Immutable
import io.element.android.features.leaveroom.api.LeaveRoomState
import io.element.android.features.userprofile.shared.UserProfileState
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.matrix.api.core.RoomAlias
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.RoomMember
Expand Down Expand Up @@ -39,6 +40,7 @@ data class RoomDetailsState(
val heroes: ImmutableList<MatrixUser>,
val canShowPinnedMessages: Boolean,
val pinnedMessagesCount: Int?,
val snackbarMessage: SnackbarMessage?,
val eventSink: (RoomDetailsEvent) -> Unit
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import io.element.android.features.leaveroom.api.aLeaveRoomState
import io.element.android.features.roomdetails.impl.members.aRoomMember
import io.element.android.features.userprofile.shared.UserProfileState
import io.element.android.features.userprofile.shared.aUserProfileState
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.matrix.api.core.RoomAlias
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
Expand Down Expand Up @@ -99,6 +100,7 @@ fun aRoomDetailsState(
heroes: List<MatrixUser> = emptyList(),
canShowPinnedMessages: Boolean = true,
pinnedMessagesCount: Int? = null,
snackbarMessage: SnackbarMessage? = null,
eventSink: (RoomDetailsEvent) -> Unit = {},
) = RoomDetailsState(
roomId = roomId,
Expand All @@ -122,7 +124,8 @@ fun aRoomDetailsState(
heroes = heroes.toPersistentList(),
canShowPinnedMessages = canShowPinnedMessages,
pinnedMessagesCount = pinnedMessagesCount,
eventSink = eventSink
snackbarMessage = snackbarMessage,
eventSink = eventSink,
)

fun aRoomNotificationSettings(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
Expand All @@ -33,6 +34,7 @@
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewParameter
Expand Down Expand Up @@ -71,6 +73,8 @@
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.core.RoomAlias
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.RoomMember
Expand Down Expand Up @@ -102,6 +106,8 @@
onPinnedMessagesClick: () -> Unit,
modifier: Modifier = Modifier,
) {
val snackbarHostState = rememberSnackbarHostState(snackbarMessage = state.snackbarMessage)

Scaffold(
modifier = modifier,
topBar = {
Expand All @@ -111,6 +117,7 @@
onActionClick = onActionClick
)
},
snackbarHost = { SnackbarHost(snackbarHostState) },
) { padding ->
Column(
modifier = Modifier
Expand All @@ -131,6 +138,9 @@
openAvatarPreview = { avatarUrl ->
openAvatarPreview(state.roomName, avatarUrl)
},
onSubtitleClick = { subtitle ->
state.eventSink(RoomDetailsEvent.CopyID(subtitle))

Check warning on line 142 in features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt

View check run for this annotation

Codecov / codecov/patch

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt#L142

Added line #L142 was not covered by tests
},
)
}
is RoomDetailsType.Dm -> {
Expand All @@ -141,6 +151,9 @@
openAvatarPreview = { name, avatarUrl ->
openAvatarPreview(name, avatarUrl)
},
onSubtitleClick = { subtitle ->
state.eventSink(RoomDetailsEvent.CopyID(subtitle))

Check warning on line 155 in features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt

View check run for this annotation

Codecov / codecov/patch

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt#L155

Added line #L155 was not covered by tests
},
)
}
}
Expand Down Expand Up @@ -330,6 +343,7 @@
roomAlias: RoomAlias?,
heroes: ImmutableList<MatrixUser>,
openAvatarPreview: (url: String) -> Unit,
onSubtitleClick: (String) -> Unit,
) {
Column(
modifier = Modifier
Expand All @@ -346,7 +360,11 @@
.clickable(enabled = avatarUrl != null) { openAvatarPreview(avatarUrl!!) }
.testTag(TestTags.roomDetailAvatar)
)
TitleAndSubtitle(title = roomName, subtitle = roomAlias?.value)
TitleAndSubtitle(
title = roomName,
subtitle = roomAlias?.value,
onSubtitleClick = onSubtitleClick,
)
}
}

Expand All @@ -356,6 +374,7 @@
otherMember: RoomMember,
roomName: String,
openAvatarPreview: (name: String, url: String) -> Unit,
onSubtitleClick: (String) -> Unit,
modifier: Modifier = Modifier
) {
Column(
Expand All @@ -373,6 +392,7 @@
TitleAndSubtitle(
title = roomName,
subtitle = otherMember.userId.value,
onSubtitleClick = onSubtitleClick,
)
}
}
Expand All @@ -381,6 +401,7 @@
private fun ColumnScope.TitleAndSubtitle(
title: String,
subtitle: String?,
onSubtitleClick: (String) -> Unit,
) {
Spacer(modifier = Modifier.height(24.dp))
Text(
Expand All @@ -391,6 +412,10 @@
if (subtitle != null) {
Spacer(modifier = Modifier.height(6.dp))
Text(
modifier = Modifier
.clip(RoundedCornerShape(4.dp))
.clickable { onSubtitleClick(subtitle) }
.padding(horizontal = 4.dp),
text = subtitle,
style = ElementTheme.typography.fontBodyLgRegular,
color = MaterialTheme.colorScheme.secondary,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
import dagger.Provides
import io.element.android.features.createroom.api.StartDMAction
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter
import io.element.android.libraries.androidutils.clipboard.ClipboardHelper
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.UserId
Expand All @@ -25,10 +28,13 @@
matrixClient: MatrixClient,
room: MatrixRoom,
startDMAction: StartDMAction,
dispatchers: CoroutineDispatchers,
clipboardHelper: ClipboardHelper,
snackbarDispatcher: SnackbarDispatcher,
): RoomMemberDetailsPresenter.Factory {
return object : RoomMemberDetailsPresenter.Factory {
override fun create(roomMemberId: UserId): RoomMemberDetailsPresenter {
return RoomMemberDetailsPresenter(roomMemberId, matrixClient, room, startDMAction)
return RoomMemberDetailsPresenter(roomMemberId, matrixClient, room, startDMAction, dispatchers, clipboardHelper, snackbarDispatcher)

Check warning on line 37 in features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt

View check run for this annotation

Codecov / codecov/patch

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt#L37

Added line #L37 was not covered by tests
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,22 @@
import io.element.android.features.userprofile.shared.UserProfilePresenterHelper
import io.element.android.features.userprofile.shared.UserProfileState
import io.element.android.features.userprofile.shared.UserProfileState.ConfirmationDialog
import io.element.android.libraries.androidutils.clipboard.ClipboardHelper
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
import io.element.android.libraries.matrix.api.MatrixClient
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.room.MatrixRoom
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.room.getRoomMemberAsState
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
Expand All @@ -44,6 +50,9 @@
private val client: MatrixClient,
private val room: MatrixRoom,
private val startDMAction: StartDMAction,
private val dispatchers: CoroutineDispatchers,
private val clipboardHelper: ClipboardHelper,
private val snackbarDispatcher: SnackbarDispatcher,
) : Presenter<UserProfileState> {
interface Factory {
fun create(roomMemberId: UserId): RoomMemberDetailsPresenter
Expand All @@ -65,6 +74,7 @@
val isCurrentUser = remember { client.isMe(roomMemberId) }
val dmRoomId by userProfilePresenterHelper.getDmRoomId()
val canCall by userProfilePresenterHelper.getCanCall(dmRoomId)
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
LaunchedEffect(Unit) {
client.ignoredUsersFlow
.map { ignoredUsers -> roomMemberId in ignoredUsers }
Expand Down Expand Up @@ -112,6 +122,12 @@
UserProfileEvents.ClearStartDMState -> {
startDmActionState.value = AsyncAction.Uninitialized
}
is UserProfileEvents.CopyID -> {
coroutineScope.launch(dispatchers.io) {

Check warning on line 126 in features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt

View check run for this annotation

Codecov / codecov/patch

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt#L126

Added line #L126 was not covered by tests
clipboardHelper.copyPlainText(event.text)
snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_copied_to_clipboard))
}
}
}
}

Expand Down Expand Up @@ -155,7 +171,8 @@
isCurrentUser = isCurrentUser,
dmRoomId = dmRoomId,
canCall = canCall,
eventSink = ::handleEvents
snackbarMessage = snackbarMessage,
eventSink = ::handleEvents,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import io.element.android.features.roomdetails.impl.RoomDetailsType
import io.element.android.features.roomdetails.impl.RoomTopicState
import io.element.android.features.roomdetails.impl.members.aRoomMember
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter
import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.core.UserId
Expand Down Expand Up @@ -77,11 +79,13 @@ class RoomDetailsPresenterTest {
notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(),
analyticsService: AnalyticsService = FakeAnalyticsService(),
isPinnedMessagesFeatureEnabled: Boolean = true,
clipboardHelper: FakeClipboardHelper = FakeClipboardHelper(),
snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(),
): RoomDetailsPresenter {
val matrixClient = FakeMatrixClient(notificationSettingsService = notificationSettingsService)
val roomMemberDetailsPresenterFactory = object : RoomMemberDetailsPresenter.Factory {
override fun create(roomMemberId: UserId): RoomMemberDetailsPresenter {
return RoomMemberDetailsPresenter(roomMemberId, matrixClient, room, FakeStartDMAction())
return RoomMemberDetailsPresenter(roomMemberId, matrixClient, room, FakeStartDMAction(), dispatchers, clipboardHelper, snackbarDispatcher)
}
}
val featureFlagService = FakeFeatureFlagService(
Expand All @@ -97,6 +101,8 @@ class RoomDetailsPresenterTest {
dispatchers = dispatchers,
isPinnedMessagesFeatureEnabled = { isPinnedMessagesFeatureEnabled },
analyticsService = analyticsService,
clipboardHelper = clipboardHelper,
snackbarDispatcher = snackbarDispatcher,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ import io.element.android.features.roomdetails.impl.members.aRoomMember
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter
import io.element.android.features.userprofile.shared.UserProfileEvents
import io.element.android.features.userprofile.shared.UserProfileState
import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.MatrixRoom
Expand All @@ -31,8 +33,10 @@ import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.ui.components.aMatrixUser
import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
Expand Down Expand Up @@ -332,17 +336,22 @@ class RoomMemberDetailsPresenterTest {
return awaitItem()
}

private fun createRoomMemberDetailsPresenter(
private fun TestScope.createRoomMemberDetailsPresenter(
room: MatrixRoom,
client: MatrixClient = FakeMatrixClient(),
roomMemberId: UserId = UserId("@alice:server.org"),
startDMAction: StartDMAction = FakeStartDMAction()
startDMAction: StartDMAction = FakeStartDMAction(),
clipboardHelper: FakeClipboardHelper = FakeClipboardHelper(),
snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(),
): RoomMemberDetailsPresenter {
return RoomMemberDetailsPresenter(
roomMemberId = roomMemberId,
client = client,
room = room,
startDMAction = startDMAction
startDMAction = startDMAction,
dispatchers = testCoroutineDispatchers(),
clipboardHelper = clipboardHelper,
snackbarDispatcher = snackbarDispatcher,
)
}
}
Loading
Loading