Click on userId / room alias to copy value to clipboard. (#4549)
This commit is contained in:
@@ -11,5 +11,6 @@ sealed interface RoomDetailsEvent {
|
||||
data object LeaveRoom : RoomDetailsEvent
|
||||
data object MuteNotification : RoomDetailsEvent
|
||||
data object UnmuteNotification : RoomDetailsEvent
|
||||
data class CopyToClipboard(val text: String) : RoomDetailsEvent
|
||||
data class SetFavorite(val isFavorite: Boolean) : RoomDetailsEvent
|
||||
}
|
||||
|
||||
@@ -24,8 +24,12 @@ import io.element.android.features.messages.api.pinned.IsPinnedMessagesFeatureEn
|
||||
import io.element.android.features.roomcall.api.RoomCallState
|
||||
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter
|
||||
import io.element.android.features.roomdetails.impl.securityandprivacy.permissions.securityAndPrivacyPermissionsAsState
|
||||
import io.element.android.libraries.androidutils.clipboard.ClipboardHelper
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.LocalSnackbarDispatcher
|
||||
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
|
||||
@@ -45,6 +49,7 @@ import io.element.android.libraries.matrix.ui.room.getDirectRoomMember
|
||||
import io.element.android.libraries.matrix.ui.room.isDmAsState
|
||||
import io.element.android.libraries.matrix.ui.room.isOwnUserAdmin
|
||||
import io.element.android.libraries.matrix.ui.room.roomMemberIdentityStateChange
|
||||
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
|
||||
@@ -65,6 +70,7 @@ class RoomDetailsPresenter @Inject constructor(
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
private val analyticsService: AnalyticsService,
|
||||
private val isPinnedMessagesFeatureEnabled: IsPinnedMessagesFeatureEnabled,
|
||||
private val clipboardHelper: ClipboardHelper,
|
||||
) : Presenter<RoomDetailsState> {
|
||||
@Composable
|
||||
override fun present(): RoomDetailsState {
|
||||
@@ -134,6 +140,9 @@ class RoomDetailsPresenter @Inject constructor(
|
||||
|
||||
val roomNotificationSettingsState by room.roomNotificationSettingsStateFlow.collectAsState()
|
||||
|
||||
val snackbarDispatcher = LocalSnackbarDispatcher.current
|
||||
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
|
||||
|
||||
fun handleEvents(event: RoomDetailsEvent) {
|
||||
when (event) {
|
||||
RoomDetailsEvent.LeaveRoom ->
|
||||
@@ -149,6 +158,10 @@ class RoomDetailsPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
is RoomDetailsEvent.SetFavorite -> scope.setFavorite(event.isFavorite)
|
||||
is RoomDetailsEvent.CopyToClipboard -> {
|
||||
clipboardHelper.copyPlainText(event.text)
|
||||
snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_copied_to_clipboard))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,6 +203,7 @@ class RoomDetailsPresenter @Inject constructor(
|
||||
canShowPinnedMessages = canShowPinnedMessages,
|
||||
canShowMediaGallery = canShowMediaGallery,
|
||||
pinnedMessagesCount = pinnedMessagesCount,
|
||||
snackbarMessage = snackbarMessage,
|
||||
canShowKnockRequests = canShowKnockRequests,
|
||||
knockRequestsCount = knockRequestsCount,
|
||||
canShowSecurityAndPrivacy = canShowSecurityAndPrivacy,
|
||||
|
||||
@@ -11,6 +11,7 @@ import androidx.compose.runtime.Immutable
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomState
|
||||
import io.element.android.features.roomcall.api.RoomCallState
|
||||
import io.element.android.features.userprofile.api.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
|
||||
@@ -42,6 +43,7 @@ data class RoomDetailsState(
|
||||
val canShowPinnedMessages: Boolean,
|
||||
val canShowMediaGallery: Boolean,
|
||||
val pinnedMessagesCount: Int?,
|
||||
val snackbarMessage: SnackbarMessage?,
|
||||
val canShowKnockRequests: Boolean,
|
||||
val knockRequestsCount: Int?,
|
||||
val canShowSecurityAndPrivacy: Boolean,
|
||||
|
||||
@@ -17,6 +17,7 @@ import io.element.android.features.userprofile.api.UserProfileState
|
||||
import io.element.android.features.userprofile.api.UserProfileVerificationState
|
||||
import io.element.android.features.userprofile.shared.aUserProfileState
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
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
|
||||
@@ -111,6 +112,7 @@ fun aRoomDetailsState(
|
||||
canShowPinnedMessages: Boolean = true,
|
||||
canShowMediaGallery: Boolean = true,
|
||||
pinnedMessagesCount: Int? = null,
|
||||
snackbarMessage: SnackbarMessage? = null,
|
||||
canShowKnockRequests: Boolean = false,
|
||||
knockRequestsCount: Int? = null,
|
||||
canShowSecurityAndPrivacy: Boolean = true,
|
||||
@@ -139,11 +141,12 @@ fun aRoomDetailsState(
|
||||
canShowPinnedMessages = canShowPinnedMessages,
|
||||
canShowMediaGallery = canShowMediaGallery,
|
||||
pinnedMessagesCount = pinnedMessagesCount,
|
||||
snackbarMessage = snackbarMessage,
|
||||
canShowKnockRequests = canShowKnockRequests,
|
||||
knockRequestsCount = knockRequestsCount,
|
||||
canShowSecurityAndPrivacy = canShowSecurityAndPrivacy,
|
||||
hasMemberVerificationViolations = hasMemberVerificationViolations,
|
||||
eventSink = eventSink
|
||||
eventSink = eventSink,
|
||||
)
|
||||
|
||||
fun aRoomNotificationSettings(
|
||||
|
||||
@@ -55,6 +55,7 @@ import io.element.android.libraries.designsystem.components.button.MainActionBut
|
||||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
|
||||
import io.element.android.libraries.designsystem.modifiers.niceClickable
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||
import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight
|
||||
@@ -69,6 +70,8 @@ import io.element.android.libraries.designsystem.theme.components.ListItemStyle
|
||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||
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.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.core.UserId
|
||||
@@ -106,6 +109,7 @@ fun RoomDetailsView(
|
||||
onProfileClick: (UserId) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val snackbarHostState = rememberSnackbarHostState(snackbarMessage = state.snackbarMessage)
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
topBar = {
|
||||
@@ -115,6 +119,7 @@ fun RoomDetailsView(
|
||||
onActionClick = onActionClick
|
||||
)
|
||||
},
|
||||
snackbarHost = { SnackbarHost(snackbarHostState) },
|
||||
) { padding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@@ -135,6 +140,9 @@ fun RoomDetailsView(
|
||||
openAvatarPreview = { avatarUrl ->
|
||||
openAvatarPreview(state.roomName, avatarUrl)
|
||||
},
|
||||
onSubtitleClick = { subtitle ->
|
||||
state.eventSink(RoomDetailsEvent.CopyToClipboard(subtitle))
|
||||
}
|
||||
)
|
||||
}
|
||||
is RoomDetailsType.Dm -> {
|
||||
@@ -145,6 +153,9 @@ fun RoomDetailsView(
|
||||
openAvatarPreview = { name, avatarUrl ->
|
||||
openAvatarPreview(name, avatarUrl)
|
||||
},
|
||||
onSubtitleClick = { subtitle ->
|
||||
state.eventSink(RoomDetailsEvent.CopyToClipboard(subtitle))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -368,6 +379,7 @@ private fun RoomHeaderSection(
|
||||
roomAlias: RoomAlias?,
|
||||
heroes: ImmutableList<MatrixUser>,
|
||||
openAvatarPreview: (url: String) -> Unit,
|
||||
onSubtitleClick: (String) -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@@ -384,7 +396,11 @@ private fun RoomHeaderSection(
|
||||
.clickable(enabled = avatarUrl != null) { openAvatarPreview(avatarUrl!!) }
|
||||
.testTag(TestTags.roomDetailAvatar)
|
||||
)
|
||||
TitleAndSubtitle(title = roomName, subtitle = roomAlias?.value)
|
||||
TitleAndSubtitle(
|
||||
title = roomName,
|
||||
subtitle = roomAlias?.value,
|
||||
onSubtitleClick = onSubtitleClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,6 +410,7 @@ private fun DmHeaderSection(
|
||||
otherMember: RoomMember,
|
||||
roomName: String,
|
||||
openAvatarPreview: (name: String, url: String) -> Unit,
|
||||
onSubtitleClick: (String) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
@@ -411,6 +428,7 @@ private fun DmHeaderSection(
|
||||
TitleAndSubtitle(
|
||||
title = roomName,
|
||||
subtitle = otherMember.userId.value,
|
||||
onSubtitleClick = onSubtitleClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -419,6 +437,7 @@ private fun DmHeaderSection(
|
||||
private fun TitleAndSubtitle(
|
||||
title: String,
|
||||
subtitle: String?,
|
||||
onSubtitleClick: (String) -> Unit,
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
@@ -430,6 +449,7 @@ private fun TitleAndSubtitle(
|
||||
if (subtitle != null) {
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
Text(
|
||||
modifier = Modifier.niceClickable { onSubtitleClick(subtitle) },
|
||||
text = subtitle,
|
||||
style = ElementTheme.typography.fontBodyLgRegular,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
@@ -612,13 +632,13 @@ private fun PinnedMessagesItem(
|
||||
headlineContent = { Text(stringResource(R.string.screen_room_details_pinned_events_row_title)) },
|
||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Pin())),
|
||||
trailingContent =
|
||||
if (pinnedMessagesCount == null) {
|
||||
ListItemContent.Custom {
|
||||
CircularProgressIndicator(strokeWidth = 2.dp, modifier = Modifier.size(24.dp))
|
||||
}
|
||||
} else {
|
||||
ListItemContent.Text(pinnedMessagesCount.toString())
|
||||
},
|
||||
if (pinnedMessagesCount == null) {
|
||||
ListItemContent.Custom {
|
||||
CircularProgressIndicator(strokeWidth = 2.dp, modifier = Modifier.size(24.dp))
|
||||
}
|
||||
} else {
|
||||
ListItemContent.Text(pinnedMessagesCount.toString())
|
||||
},
|
||||
onClick = {
|
||||
analyticsService.captureInteraction(Interaction.Name.PinnedMessageRoomInfoButton)
|
||||
onPinnedMessagesClick()
|
||||
|
||||
@@ -12,6 +12,7 @@ import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter
|
||||
import io.element.android.features.userprofile.api.UserProfilePresenterFactory
|
||||
import io.element.android.libraries.androidutils.clipboard.ClipboardHelper
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
@@ -25,6 +26,7 @@ object RoomMemberModule {
|
||||
room: MatrixRoom,
|
||||
userProfilePresenterFactory: UserProfilePresenterFactory,
|
||||
encryptionService: EncryptionService,
|
||||
clipboardHelper: ClipboardHelper,
|
||||
): RoomMemberDetailsPresenter.Factory {
|
||||
return object : RoomMemberDetailsPresenter.Factory {
|
||||
override fun create(roomMemberId: UserId): RoomMemberDetailsPresenter {
|
||||
@@ -33,6 +35,7 @@ object RoomMemberModule {
|
||||
room = room,
|
||||
userProfilePresenterFactory = userProfilePresenterFactory,
|
||||
encryptionService = encryptionService,
|
||||
clipboardHelper = clipboardHelper,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,11 @@ import io.element.android.features.userprofile.api.UserProfileEvents
|
||||
import io.element.android.features.userprofile.api.UserProfilePresenterFactory
|
||||
import io.element.android.features.userprofile.api.UserProfileState
|
||||
import io.element.android.features.userprofile.api.UserProfileVerificationState
|
||||
import io.element.android.libraries.androidutils.clipboard.ClipboardHelper
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.LocalSnackbarDispatcher
|
||||
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.core.UserId
|
||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
|
||||
@@ -27,6 +31,7 @@ import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.ui.room.getRoomMemberAsState
|
||||
import io.element.android.libraries.matrix.ui.room.roomMemberIdentityStateChange
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
@@ -42,6 +47,7 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
|
||||
@Assisted private val roomMemberId: UserId,
|
||||
private val room: MatrixRoom,
|
||||
private val encryptionService: EncryptionService,
|
||||
private val clipboardHelper: ClipboardHelper,
|
||||
userProfilePresenterFactory: UserProfilePresenterFactory,
|
||||
) : Presenter<UserProfileState> {
|
||||
interface Factory {
|
||||
@@ -55,6 +61,8 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
|
||||
override fun present(): UserProfileState {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
val snackbarDispatcher = LocalSnackbarDispatcher.current
|
||||
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
|
||||
val roomMember by room.getRoomMemberAsState(roomMemberId)
|
||||
LaunchedEffect(Unit) {
|
||||
// Update room member info when opening this screen
|
||||
@@ -111,7 +119,11 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
|
||||
UserProfileEvents.WithdrawVerification -> coroutineScope.launch {
|
||||
encryptionService.withdrawVerification(roomMemberId)
|
||||
}
|
||||
else -> Unit
|
||||
is UserProfileEvents.CopyToClipboard -> {
|
||||
clipboardHelper.copyPlainText(event.text)
|
||||
snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_copied_to_clipboard))
|
||||
}
|
||||
else -> userProfileState.eventSink(event)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,13 +131,8 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
|
||||
userName = roomUserName ?: userProfileState.userName,
|
||||
avatarUrl = roomUserAvatar ?: userProfileState.avatarUrl,
|
||||
verificationState = verificationState,
|
||||
eventSink = { event ->
|
||||
if (event is UserProfileEvents.WithdrawVerification) {
|
||||
eventSink(UserProfileEvents.WithdrawVerification)
|
||||
} else {
|
||||
userProfileState.eventSink(event)
|
||||
}
|
||||
}
|
||||
snackbarMessage = snackbarMessage,
|
||||
eventSink = ::eventSink
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ import io.element.android.features.roomcall.api.aStandByCallState
|
||||
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.aUserProfileState
|
||||
import io.element.android.libraries.androidutils.clipboard.ClipboardHelper
|
||||
import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
@@ -81,6 +83,7 @@ class RoomDetailsPresenterTest {
|
||||
),
|
||||
isPinnedMessagesFeatureEnabled: Boolean = true,
|
||||
encryptionService: FakeEncryptionService = FakeEncryptionService(),
|
||||
clipboardHelper: ClipboardHelper = FakeClipboardHelper(),
|
||||
): RoomDetailsPresenter {
|
||||
val matrixClient = FakeMatrixClient(notificationSettingsService = notificationSettingsService)
|
||||
val roomMemberDetailsPresenterFactory = object : RoomMemberDetailsPresenter.Factory {
|
||||
@@ -92,6 +95,7 @@ class RoomDetailsPresenterTest {
|
||||
Presenter { aUserProfileState() }
|
||||
},
|
||||
encryptionService = encryptionService,
|
||||
clipboardHelper = clipboardHelper,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -106,6 +110,7 @@ class RoomDetailsPresenterTest {
|
||||
dispatchers = dispatchers,
|
||||
isPinnedMessagesFeatureEnabled = { isPinnedMessagesFeatureEnabled },
|
||||
analyticsService = analyticsService,
|
||||
clipboardHelper = clipboardHelper,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ import io.element.android.features.userprofile.api.UserProfileEvents
|
||||
import io.element.android.features.userprofile.api.UserProfilePresenterFactory
|
||||
import io.element.android.features.userprofile.api.UserProfileVerificationState
|
||||
import io.element.android.features.userprofile.shared.aUserProfileState
|
||||
import io.element.android.libraries.androidutils.clipboard.ClipboardHelper
|
||||
import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
|
||||
@@ -350,12 +352,14 @@ class RoomMemberDetailsPresenterTest {
|
||||
}
|
||||
},
|
||||
encryptionService: FakeEncryptionService = FakeEncryptionService(getUserIdentityResult = { Result.success(null) }),
|
||||
clipboardHelper: ClipboardHelper = FakeClipboardHelper(),
|
||||
): RoomMemberDetailsPresenter {
|
||||
return RoomMemberDetailsPresenter(
|
||||
roomMemberId = UserId("@alice:server.org"),
|
||||
room = room,
|
||||
userProfilePresenterFactory = userProfilePresenterFactory,
|
||||
encryptionService = encryptionService,
|
||||
clipboardHelper = clipboardHelper,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,5 +16,6 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.designsystem)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
}
|
||||
|
||||
@@ -15,4 +15,5 @@ sealed interface UserProfileEvents {
|
||||
data object ClearBlockUserError : UserProfileEvents
|
||||
data object ClearConfirmationDialog : UserProfileEvents
|
||||
data object WithdrawVerification : UserProfileEvents
|
||||
data class CopyToClipboard(val text: String) : UserProfileEvents
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ package io.element.android.features.userprofile.api
|
||||
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
|
||||
@@ -23,6 +24,7 @@ data class UserProfileState(
|
||||
val isCurrentUser: Boolean,
|
||||
val dmRoomId: RoomId?,
|
||||
val canCall: Boolean,
|
||||
val snackbarMessage: SnackbarMessage?,
|
||||
val eventSink: (UserProfileEvents) -> Unit
|
||||
) {
|
||||
enum class ConfirmationDialog {
|
||||
|
||||
@@ -120,8 +120,9 @@ class UserProfilePresenter @AssistedInject constructor(
|
||||
UserProfileEvents.ClearStartDMState -> {
|
||||
startDmActionState.value = AsyncAction.Uninitialized
|
||||
}
|
||||
// Do nothing for withdrawing verification as it's handled by the RoomMemberDetailsPresenter if needed
|
||||
UserProfileEvents.WithdrawVerification -> Unit
|
||||
// Do nothing for other event as they are handled by the RoomMemberDetailsPresenter if needed
|
||||
UserProfileEvents.WithdrawVerification,
|
||||
is UserProfileEvents.CopyToClipboard -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +137,7 @@ class UserProfilePresenter @AssistedInject constructor(
|
||||
isCurrentUser = isCurrentUser,
|
||||
dmRoomId = dmRoomId,
|
||||
canCall = canCall,
|
||||
snackbarMessage = null,
|
||||
eventSink = ::handleEvents
|
||||
)
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import io.element.android.libraries.designsystem.atomic.molecules.MatrixBadgeRow
|
||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.designsystem.modifiers.niceClickable
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.ButtonSize
|
||||
@@ -48,6 +49,7 @@ fun UserProfileHeaderSection(
|
||||
userName: String?,
|
||||
verificationState: UserProfileVerificationState,
|
||||
openAvatarPreview: (url: String) -> Unit,
|
||||
onUserIdClick: () -> Unit,
|
||||
withdrawVerificationClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
@@ -75,6 +77,7 @@ fun UserProfileHeaderSection(
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.niceClickable { onUserIdClick() },
|
||||
text = userId.value,
|
||||
style = ElementTheme.typography.fontBodyLgRegular,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
@@ -125,6 +128,7 @@ internal fun UserProfileHeaderSectionPreview() = ElementPreview {
|
||||
userName = "Alice",
|
||||
verificationState = UserProfileVerificationState.VERIFIED,
|
||||
openAvatarPreview = {},
|
||||
onUserIdClick = {},
|
||||
withdrawVerificationClick = {},
|
||||
)
|
||||
}
|
||||
@@ -138,6 +142,7 @@ internal fun UserProfileHeaderSectionWithVerificationViolationPreview() = Elemen
|
||||
userName = "Alice",
|
||||
verificationState = UserProfileVerificationState.VERIFICATION_VIOLATION,
|
||||
openAvatarPreview = {},
|
||||
onUserIdClick = {},
|
||||
withdrawVerificationClick = {},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import io.element.android.features.userprofile.api.UserProfileState
|
||||
import io.element.android.features.userprofile.api.UserProfileVerificationState
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
||||
@@ -45,6 +46,7 @@ fun aUserProfileState(
|
||||
isCurrentUser: Boolean = false,
|
||||
dmRoomId: RoomId? = null,
|
||||
canCall: Boolean = false,
|
||||
snackbarMessage: SnackbarMessage? = null,
|
||||
eventSink: (UserProfileEvents) -> Unit = {},
|
||||
) = UserProfileState(
|
||||
userId = userId,
|
||||
@@ -57,5 +59,6 @@ fun aUserProfileState(
|
||||
isCurrentUser = isCurrentUser,
|
||||
dmRoomId = dmRoomId,
|
||||
canCall = canCall,
|
||||
snackbarMessage = snackbarMessage,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
|
||||
@@ -38,6 +38,8 @@ import io.element.android.libraries.designsystem.theme.components.ListItem
|
||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||
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.snackbar.SnackbarHost
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.ui.components.CreateDmConfirmationBottomSheet
|
||||
@@ -55,17 +57,19 @@ fun UserProfileView(
|
||||
onVerifyClick: (UserId) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val snackbarHostState = rememberSnackbarHostState(snackbarMessage = state.snackbarMessage)
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
topBar = {
|
||||
TopAppBar(title = { }, navigationIcon = { BackButton(onClick = goBack) })
|
||||
},
|
||||
snackbarHost = { SnackbarHost(snackbarHostState) },
|
||||
) { padding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
UserProfileHeaderSection(
|
||||
avatarUrl = state.avatarUrl,
|
||||
@@ -75,6 +79,9 @@ fun UserProfileView(
|
||||
openAvatarPreview = { avatarUrl ->
|
||||
openAvatarPreview(state.userName ?: state.userId.value, avatarUrl)
|
||||
},
|
||||
onUserIdClick = {
|
||||
state.eventSink(UserProfileEvents.CopyToClipboard(state.userId.value))
|
||||
},
|
||||
withdrawVerificationClick = { state.eventSink(UserProfileEvents.WithdrawVerification) },
|
||||
)
|
||||
UserProfileMainActionsSection(
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
package io.element.android.libraries.designsystem.modifiers
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
fun Modifier.clickableIfNotNull(onClick: (() -> Unit)? = null): Modifier = then(
|
||||
if (onClick != null) {
|
||||
@@ -17,3 +21,11 @@ fun Modifier.clickableIfNotNull(onClick: (() -> Unit)? = null): Modifier = then(
|
||||
Modifier
|
||||
}
|
||||
)
|
||||
|
||||
fun Modifier.niceClickable(
|
||||
onClick: () -> Unit,
|
||||
): Modifier {
|
||||
return clip(RoundedCornerShape(4.dp))
|
||||
.clickable { onClick() }
|
||||
.padding(horizontal = 4.dp)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user