Merge pull request #5950 from element-hq/feature/fga/iterate_permissions_screen

Changes : iterate again on permissions
This commit is contained in:
ganfra 2025-12-22 18:41:38 +01:00 committed by GitHub
commit 76bc487f28
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 243 additions and 151 deletions

View file

@ -85,7 +85,7 @@ sealed interface ListItemContent {
data class Text(val text: String) : ListItemContent
/** Displays any custom content. */
data class Custom(val content: @Composable () -> Unit) : ListItemContent
data class Custom(val content: @Composable (enabled: Boolean) -> Unit) : ListItemContent
/** Displays a badge. */
data object Badge : ListItemContent
@ -131,7 +131,7 @@ sealed interface ListItemContent {
is Counter -> {
CounterAtom(count = count)
}
is Custom -> content()
is Custom -> content(isItemEnabled)
}
}
}

View file

@ -43,7 +43,6 @@ fun PreferenceCheckbox(
leadingContent = preferenceIcon(
icon = icon,
iconResourceId = iconResourceId,
enabled = enabled,
showIconAreaIfNoIcon = showIconAreaIfNoIcon,
),
headlineContent = {

View file

@ -40,7 +40,7 @@ import io.element.android.libraries.designsystem.theme.components.DropdownMenuIt
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.toEnabledColor
import io.element.android.libraries.designsystem.toIconSecondaryEnabledColor
import io.element.android.libraries.designsystem.toSecondaryEnabledColor
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
@ -64,7 +64,6 @@ fun <T : DropdownOption> PreferenceDropdown(
leadingContent = preferenceIcon(
icon = icon,
iconResourceId = iconResourceId,
enabled = enabled,
showIconAreaIfNoIcon = showIconAreaIfNoIcon,
),
headlineContent = {
@ -72,7 +71,6 @@ fun <T : DropdownOption> PreferenceDropdown(
style = ElementTheme.typography.fontBodyLgRegular,
modifier = Modifier.fillMaxWidth(),
text = title,
color = enabled.toEnabledColor(),
)
},
supportingContent = supportingText?.let {
@ -80,22 +78,23 @@ fun <T : DropdownOption> PreferenceDropdown(
Text(
style = ElementTheme.typography.fontBodyMdRegular,
text = it,
color = enabled.toSecondaryEnabledColor(),
)
}
},
trailingContent = ListItemContent.Custom(
content = {
content = { enabled ->
DropdownTrailingContent(
selectedOption = selectedOption,
options = options,
onSelectOption = onSelectOption,
expanded = isDropdownExpanded,
onExpandedChange = { isDropdownExpanded = it },
enabled = enabled,
modifier = Modifier.fillMaxSize(0.3f)
)
}
),
enabled = enabled,
onClick = { isDropdownExpanded = true }.takeIf { !isDropdownExpanded },
)
}
@ -118,6 +117,7 @@ private fun <T : DropdownOption> DropdownTrailingContent(
expanded: Boolean,
onExpandedChange: (Boolean) -> Unit,
onSelectOption: (T) -> Unit,
enabled: Boolean,
modifier: Modifier = Modifier,
) {
Row(
@ -129,7 +129,7 @@ private fun <T : DropdownOption> DropdownTrailingContent(
text = selectedOption?.getText().orEmpty(),
maxLines = 1,
style = ElementTheme.typography.fontBodyMdRegular,
color = ElementTheme.colors.textSecondary,
color = enabled.toSecondaryEnabledColor(),
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.End,
modifier = Modifier.weight(1f),
@ -137,7 +137,7 @@ private fun <T : DropdownOption> DropdownTrailingContent(
Icon(
imageVector = CompoundIcons.ChevronDown(),
contentDescription = null,
tint = ElementTheme.colors.iconSecondary,
tint = enabled.toIconSecondaryEnabledColor(),
)
DropdownMenu(
expanded = expanded,
@ -146,6 +146,7 @@ private fun <T : DropdownOption> DropdownTrailingContent(
) {
options.forEach { option ->
DropdownMenuItem(
enabled = enabled,
text = {
Text(
text = option.getText(),
@ -206,5 +207,14 @@ internal fun PreferenceDropdownPreview() = ElementThemedPreview {
options = options,
onSelectOption = {},
)
PreferenceDropdown(
title = "Dropdown",
supportingText = "Options for dropdown",
icon = CompoundIcons.Threads(),
selectedOption = options.first(),
options = options,
onSelectOption = {},
enabled = false
)
}
}

View file

@ -44,7 +44,6 @@ fun PreferenceSlide(
leadingContent = preferenceIcon(
icon = icon,
iconResourceId = iconResourceId,
enabled = enabled,
showIconAreaIfNoIcon = showIconAreaIfNoIcon,
),
headlineContent = {

View file

@ -42,7 +42,6 @@ fun PreferenceSwitch(
leadingContent = preferenceIcon(
icon = icon,
iconResourceId = iconResourceId,
enabled = enabled,
showIconAreaIfNoIcon = showIconAreaIfNoIcon,
),
headlineContent = {

View file

@ -34,11 +34,10 @@ fun preferenceIcon(
@DrawableRes iconResourceId: Int? = null,
showIconBadge: Boolean = false,
tintColor: Color? = null,
enabled: Boolean = true,
showIconAreaIfNoIcon: Boolean = false,
): ListItemContent.Custom? {
return if (icon != null || iconResourceId != null || showIconAreaIfNoIcon) {
ListItemContent.Custom {
ListItemContent.Custom { enabled ->
PreferenceIcon(
icon = icon,
iconResourceId = iconResourceId,

View file

@ -21,6 +21,20 @@ fun RoomInfo.getAvatarData(size: AvatarSize) = AvatarData(
size = size,
)
/**
* Returns the power level of the user in the room.
* If the user is a creator and [RoomInfo.privilegedCreatorRole] is true, returns the power level of [RoomMember.Role.Owner].
* Otherwise, checks the room's power levels for the user's power level.
* If no specific power level is set for the user, defaults to 0.
*/
fun RoomInfo.powerLevelOf(userId: UserId): Long {
return if (privilegedCreatorRole && creators.contains(userId)) {
RoomMember.Role.Owner(isCreator = true).powerLevel
} else {
roomPowerLevels?.powerLevelOf(userId = userId) ?: 0L
}
}
/**
* Returns the role of the user in the room.
* If the user is a creator and [RoomInfo.privilegedCreatorRole] is true, returns [RoomMember.Role.Owner].
@ -28,9 +42,6 @@ fun RoomInfo.getAvatarData(size: AvatarSize) = AvatarData(
* If no specific power level is set for the user, defaults to [RoomMember.Role.User].
*/
fun RoomInfo.roleOf(userId: UserId): RoomMember.Role {
return if (privilegedCreatorRole && creators.contains(userId)) {
RoomMember.Role.Owner(isCreator = true)
} else {
roomPowerLevels?.roleOf(userId) ?: RoomMember.Role.User
}
val powerLevel = powerLevelOf(userId = userId)
return RoomMember.Role.forPowerLevel(powerLevel)
}

View file

@ -1,34 +0,0 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.ui.room
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import io.element.android.libraries.matrix.api.room.BaseRoom
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.ui.model.roleOf
@Composable
fun BaseRoom.userPowerLevelAsState(updateKey: Long): State<Long> {
return produceState(initialValue = 0, key1 = updateKey) {
value = userRole(sessionId)
.getOrDefault(RoomMember.Role.User)
.powerLevel
}
}
@Composable
fun BaseRoom.isOwnUserAdmin(): Boolean {
val roomInfo by roomInfoFlow.collectAsState()
val role = roomInfo.roleOf(sessionId)
return role == RoomMember.Role.Admin || role is RoomMember.Role.Owner
}