Skip to content

Commit 51f6774

Browse files
Adapt 'change roles' screens to the new creator/owner role (#5076)
* Replace `RoomMember.Role.CREATOR` with `RoomMember.Role.Owner` - Make `RoomMember.Role` a sealed interface instead * Adapt room member role mapping to include the power level to distinguish between admins and owners * Use new `RoomMember.Role` sealed interface through the app * Change how `MembersByRole` groups members to add owners to the admins section * Adapt the `ChangeRoles` screen to the new roles: - Owners can't modify other owner's roles. - They can modify the roles of any other user, without confirmation. * Adapt 'roles and permissions' screen: - Owners can't demote themselves. - The admin count also counts owners. * Add more tests and screenshots * Add owners to its own section in the 'change roles' screen * Update screenshots --------- Co-authored-by: ElementBot <[email protected]>
1 parent 4534229 commit 51f6774

File tree

77 files changed

+662
-300
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+662
-300
lines changed

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsPickerView.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ internal fun SuggestionsPickerViewPreview() {
157157
powerLevel = 0L,
158158
normalizedPowerLevel = 0L,
159159
isIgnored = false,
160-
role = RoomMember.Role.USER,
160+
role = RoomMember.Role.User,
161161
membershipChangeReason = null,
162162
)
163163
val anAlias = remember { RoomAlias("#room:domain.org") }

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ fun aDmRoomMember(
6969
powerLevel: Long = 0,
7070
normalizedPowerLevel: Long = powerLevel,
7171
isIgnored: Boolean = false,
72-
role: RoomMember.Role = RoomMember.Role.USER,
72+
role: RoomMember.Role = RoomMember.Role.User,
7373
membershipChangeReason: String? = null,
7474
) = RoomMember(
7575
userId = userId,

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/analytics/AnalyticUtils.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsV
1313
import io.element.android.services.analytics.api.AnalyticsService
1414

1515
internal fun RoomMember.Role.toAnalyticsMemberRole(): RoomModeration.Role = when (this) {
16-
RoomMember.Role.CREATOR -> RoomModeration.Role.Administrator // TODO - distinguish creator from admin
17-
RoomMember.Role.ADMIN -> RoomModeration.Role.Administrator
18-
RoomMember.Role.MODERATOR -> RoomModeration.Role.Moderator
19-
RoomMember.Role.USER -> RoomModeration.Role.User
16+
is RoomMember.Role.Owner -> RoomModeration.Role.Administrator // TODO - distinguish creator from admin
17+
RoomMember.Role.Admin -> RoomModeration.Role.Administrator
18+
RoomMember.Role.Moderator -> RoomModeration.Role.Moderator
19+
RoomMember.Role.User -> RoomModeration.Role.User
2020
}
2121

2222
internal fun analyticsMemberRoleForPowerLevel(powerLevel: Long): RoomModeration.Role {

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ fun aRoomMember(
150150
powerLevel: Long = 0L,
151151
normalizedPowerLevel: Long = 0L,
152152
isIgnored: Boolean = false,
153-
role: RoomMember.Role = RoomMember.Role.USER,
153+
role: RoomMember.Role = RoomMember.Role.User,
154154
membershipChangeReason: String? = null,
155155
) = RoomMember(
156156
userId = userId,
@@ -178,8 +178,8 @@ fun aRoomMemberList() = persistentListOf(
178178
aWalter(),
179179
)
180180

181-
fun anAlice() = aRoomMember(UserId("@alice:server.org"), "Alice", role = RoomMember.Role.ADMIN)
182-
fun aBob() = aRoomMember(UserId("@bob:server.org"), "Bob", role = RoomMember.Role.MODERATOR)
181+
fun anAlice() = aRoomMember(UserId("@alice:server.org"), "Alice", role = RoomMember.Role.Admin)
182+
fun aBob() = aRoomMember(UserId("@bob:server.org"), "Bob", role = RoomMember.Role.Moderator)
183183

184184
fun aVictor() = aRoomMember(UserId("@victor:server.org"), "Victor", membership = RoomMembershipState.INVITE)
185185

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ import io.element.android.libraries.designsystem.theme.components.TopAppBar
6161
import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
6262
import io.element.android.libraries.matrix.api.room.RoomMember
6363
import io.element.android.libraries.matrix.api.room.getBestName
64-
import io.element.android.libraries.matrix.api.room.isOwner
6564
import io.element.android.libraries.matrix.api.room.toMatrixUser
6665
import io.element.android.libraries.matrix.ui.components.MatrixUserRow
6766
import io.element.android.libraries.ui.strings.CommonStrings
@@ -295,14 +294,11 @@ private fun RoomMemberListItem(
295294
modifier: Modifier = Modifier,
296295
) {
297296
val member = roomMemberWithIdentity.roomMember
298-
val roleText = if (member.isOwner()) {
299-
stringResource(R.string.screen_room_member_list_role_owner)
300-
} else {
301-
when (member.role) {
302-
RoomMember.Role.ADMIN -> stringResource(R.string.screen_room_member_list_role_administrator)
303-
RoomMember.Role.MODERATOR -> stringResource(R.string.screen_room_member_list_role_moderator)
304-
else -> null
305-
}
297+
val roleText = when (member.role) {
298+
RoomMember.Role.Admin -> stringResource(R.string.screen_room_member_list_role_administrator)
299+
RoomMember.Role.Moderator -> stringResource(R.string.screen_room_member_list_role_moderator)
300+
is RoomMember.Role.Owner -> stringResource(R.string.screen_room_member_list_role_owner)
301+
else -> null
306302
}
307303

308304
MatrixUserRow(

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class RolesAndPermissionsNode @AssistedInject constructor(
6161
room.roomInfoFlow
6262
.filter { info ->
6363
val role = info.roleOf(room.sessionId)
64-
role != RoomMember.Role.ADMIN && role != RoomMember.Role.CREATOR
64+
role != RoomMember.Role.Admin && role !is RoomMember.Role.Owner
6565
}
6666
.take(1)
6767
.onEach { navigateUp() }

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsPresenter.kt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import io.element.android.libraries.matrix.api.room.RoomInfo
2626
import io.element.android.libraries.matrix.api.room.RoomMember
2727
import io.element.android.libraries.matrix.api.room.activeRoomMembers
2828
import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange
29+
import io.element.android.libraries.matrix.ui.model.roleOf
2930
import io.element.android.services.analytics.api.AnalyticsService
3031
import kotlinx.coroutines.CoroutineScope
3132
import kotlinx.coroutines.launch
@@ -50,14 +51,23 @@ class RolesAndPermissionsPresenter @Inject constructor(
5051
}
5152
val moderatorCount by remember {
5253
derivedStateOf {
53-
roomInfo.userCountWithRole(activeRoomMemberIds, RoomMember.Role.MODERATOR)
54+
roomInfo.userCountWithRole(activeRoomMemberIds, RoomMember.Role.Moderator)
5455
}
5556
}
5657
val adminCount by remember {
5758
derivedStateOf {
58-
roomInfo.userCountWithRole(activeRoomMemberIds, RoomMember.Role.ADMIN)
59+
val admins = roomInfo.userCountWithRole(activeRoomMemberIds, RoomMember.Role.Admin)
60+
val ownersCount = if (roomInfo.privilegedCreatorRole) {
61+
val superAdmins = roomInfo.userCountWithRole(activeRoomMemberIds, RoomMember.Role.Owner(isCreator = false))
62+
val creators = roomInfo.userCountWithRole(activeRoomMemberIds, RoomMember.Role.Owner(isCreator = true))
63+
superAdmins + creators
64+
} else {
65+
0
66+
}
67+
admins + ownersCount
5968
}
6069
}
70+
val canDemoteSelf = remember { derivedStateOf { roomInfo.roleOf(room.sessionId) !is RoomMember.Role.Owner } }
6171
val changeOwnRoleAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
6272
val resetPermissionsAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
6373

@@ -83,8 +93,10 @@ class RolesAndPermissionsPresenter @Inject constructor(
8393
}
8494

8595
return RolesAndPermissionsState(
96+
roomSupportsOwnerRole = roomInfo.privilegedCreatorRole,
8697
adminCount = adminCount,
8798
moderatorCount = moderatorCount,
99+
canDemoteSelf = canDemoteSelf.value,
88100
changeOwnRoleAction = changeOwnRoleAction.value,
89101
resetPermissionsAction = resetPermissionsAction.value,
90102
eventSink = { handleEvent(it) },

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsState.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ package io.element.android.features.roomdetails.impl.rolesandpermissions
1010
import io.element.android.libraries.architecture.AsyncAction
1111

1212
data class RolesAndPermissionsState(
13+
val roomSupportsOwnerRole: Boolean,
1314
val adminCount: Int,
1415
val moderatorCount: Int,
16+
val canDemoteSelf: Boolean,
1517
val changeOwnRoleAction: AsyncAction<Unit>,
1618
val resetPermissionsAction: AsyncAction<Unit>,
1719
val eventSink: (RolesAndPermissionsEvents) -> Unit,

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsStateProvider.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import io.element.android.libraries.architecture.AsyncAction
1313
class RolesAndPermissionsStateProvider : PreviewParameterProvider<RolesAndPermissionsState> {
1414
override val values: Sequence<RolesAndPermissionsState>
1515
get() = sequenceOf(
16-
aRolesAndPermissionsState(),
16+
aRolesAndPermissionsState(roomSupportsOwners = false),
1717
aRolesAndPermissionsState(adminCount = 1, moderatorCount = 2),
1818
aRolesAndPermissionsState(
1919
adminCount = 1,
@@ -45,17 +45,22 @@ class RolesAndPermissionsStateProvider : PreviewParameterProvider<RolesAndPermis
4545
moderatorCount = 2,
4646
resetPermissionsAction = AsyncAction.Failure(IllegalStateException("Failed to reset permissions")),
4747
),
48+
aRolesAndPermissionsState(canDemoteSelf = false),
4849
)
4950
}
5051

5152
internal fun aRolesAndPermissionsState(
53+
roomSupportsOwners: Boolean = true,
5254
adminCount: Int = 0,
5355
moderatorCount: Int = 0,
56+
canDemoteSelf: Boolean = true,
5457
changeOwnRoleAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
5558
resetPermissionsAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
5659
eventSink: (RolesAndPermissionsEvents) -> Unit = {},
5760
) = RolesAndPermissionsState(
61+
roomSupportsOwnerRole = roomSupportsOwners,
5862
adminCount = adminCount,
63+
canDemoteSelf = canDemoteSelf,
5964
moderatorCount = moderatorCount,
6065
changeOwnRoleAction = changeOwnRoleAction,
6166
resetPermissionsAction = resetPermissionsAction,

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsView.kt

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,14 @@ fun RolesAndPermissionsView(
5555
onBackClick = rolesAndPermissionsNavigator::onBackClick,
5656
) {
5757
ListSectionHeader(title = stringResource(R.string.screen_room_roles_and_permissions_roles_header), hasDivider = false)
58+
59+
val adminsTitle = if (state.roomSupportsOwnerRole) {
60+
stringResource(R.string.screen_room_roles_and_permissions_admins_and_owners)
61+
} else {
62+
stringResource(R.string.screen_room_roles_and_permissions_admins)
63+
}
5864
ListItem(
59-
headlineContent = { Text(stringResource(R.string.screen_room_roles_and_permissions_admins)) },
65+
headlineContent = { Text(adminsTitle) },
6066
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Admin())),
6167
trailingContent = ListItemContent.Text("${state.adminCount}"),
6268
onClick = { rolesAndPermissionsNavigator.openAdminList() },
@@ -67,11 +73,13 @@ fun RolesAndPermissionsView(
6773
trailingContent = ListItemContent.Text("${state.moderatorCount}"),
6874
onClick = { rolesAndPermissionsNavigator.openModeratorList() },
6975
)
70-
ListItem(
71-
headlineContent = { Text(stringResource(R.string.screen_room_roles_and_permissions_change_my_role)) },
72-
onClick = { state.eventSink(RolesAndPermissionsEvents.ChangeOwnRole) },
73-
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Edit()))
74-
)
76+
if (state.canDemoteSelf) {
77+
ListItem(
78+
headlineContent = { Text(stringResource(R.string.screen_room_roles_and_permissions_change_my_role)) },
79+
onClick = { state.eventSink(RolesAndPermissionsEvents.ChangeOwnRole) },
80+
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Edit()))
81+
)
82+
}
7583
ListSectionHeader(title = stringResource(R.string.screen_room_roles_and_permissions_permissions_header), hasDivider = true)
7684
ListItem(
7785
headlineContent = { Text(stringResource(R.string.screen_room_roles_and_permissions_room_details)) },
@@ -170,7 +178,7 @@ private fun ChangeOwnRoleBottomSheet(
170178
headlineContent = { Text(stringResource(R.string.screen_room_roles_and_permissions_change_role_demote_to_moderator)) },
171179
onClick = {
172180
sheetState.hide(coroutineScope) {
173-
eventSink(RolesAndPermissionsEvents.DemoteSelfTo(RoomMember.Role.MODERATOR))
181+
eventSink(RolesAndPermissionsEvents.DemoteSelfTo(RoomMember.Role.Moderator))
174182
}
175183
},
176184
style = ListItemStyle.Destructive,
@@ -179,7 +187,7 @@ private fun ChangeOwnRoleBottomSheet(
179187
headlineContent = { Text(stringResource(R.string.screen_room_roles_and_permissions_change_role_demote_to_member)) },
180188
onClick = {
181189
sheetState.hide(coroutineScope) {
182-
eventSink(RolesAndPermissionsEvents.DemoteSelfTo(RoomMember.Role.USER))
190+
eventSink(RolesAndPermissionsEvents.DemoteSelfTo(RoomMember.Role.User))
183191
}
184192
},
185193
style = ListItemStyle.Destructive,

0 commit comments

Comments
 (0)