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