From 40bfd858ee9df6d942fb0863ac1bbc3215e04099 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Mon, 5 Feb 2024 01:40:10 +0100 Subject: [PATCH] Add permission checks to invite and kicks --- .../twom/viewroom/components/InviteFAB.kt | 6 +- .../viewroom/components/MemberListItem.kt | 5 +- .../twom/viewroom/effects/canIInvite.kt | 14 +++++ .../steffo/twom/viewroom/effects/canIKick.kt | 14 +++++ .../viewroom/effects/observePowerLevels.kt | 60 +++++++++++++++++++ 5 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/eu/steffo/twom/viewroom/effects/canIInvite.kt create mode 100644 app/src/main/java/eu/steffo/twom/viewroom/effects/canIKick.kt create mode 100644 app/src/main/java/eu/steffo/twom/viewroom/effects/observePowerLevels.kt diff --git a/app/src/main/java/eu/steffo/twom/viewroom/components/InviteFAB.kt b/app/src/main/java/eu/steffo/twom/viewroom/components/InviteFAB.kt index 191d6b9..1c94a4e 100644 --- a/app/src/main/java/eu/steffo/twom/viewroom/components/InviteFAB.kt +++ b/app/src/main/java/eu/steffo/twom/viewroom/components/InviteFAB.kt @@ -10,6 +10,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import eu.steffo.twom.R +import eu.steffo.twom.viewroom.effects.canIInvite @Composable @Preview @@ -17,7 +18,10 @@ fun InviteFAB( modifier: Modifier = Modifier, onClick: () -> Unit = {}, ) { - // TODO: Hide if unprivileged + if (!canIInvite()) { + return + } + ExtendedFloatingActionButton( modifier = modifier, onClick = { onClick() }, diff --git a/app/src/main/java/eu/steffo/twom/viewroom/components/MemberListItem.kt b/app/src/main/java/eu/steffo/twom/viewroom/components/MemberListItem.kt index 7ca12fe..0e207e8 100644 --- a/app/src/main/java/eu/steffo/twom/viewroom/components/MemberListItem.kt +++ b/app/src/main/java/eu/steffo/twom/viewroom/components/MemberListItem.kt @@ -28,6 +28,7 @@ import eu.steffo.twom.avatar.components.AvatarUser import eu.steffo.twom.errorhandling.components.ErrorText import eu.steffo.twom.matrix.complocals.LocalSession import eu.steffo.twom.viewroom.complocals.LocalRoom +import eu.steffo.twom.viewroom.effects.canIKick import eu.steffo.twom.viewroom.effects.observeRSVP import eu.steffo.twom.viewroom.effects.resolveUser import eu.steffo.twom.viewroom.utils.RSVPAnswer @@ -67,6 +68,7 @@ fun MemberListItem( // This might not be necessary; I'm not sure when the internal Matrix client resolves users val user = resolveUser(member.userId) + val canKick = canIKick() val scope = rememberCoroutineScope() @@ -132,8 +134,7 @@ fun MemberListItem( expanded = expanded, onDismissRequest = { expanded = false } ) { - // TODO: Also hide if unprivileged - if (member.userId != session.myUserId) { + if (member.userId != session.myUserId && canKick) { DropdownMenuItem( text = { Text(stringResource(R.string.room_uninvite_label)) diff --git a/app/src/main/java/eu/steffo/twom/viewroom/effects/canIInvite.kt b/app/src/main/java/eu/steffo/twom/viewroom/effects/canIInvite.kt new file mode 100644 index 0000000..4058e96 --- /dev/null +++ b/app/src/main/java/eu/steffo/twom/viewroom/effects/canIInvite.kt @@ -0,0 +1,14 @@ +package eu.steffo.twom.viewroom.effects + +import androidx.compose.runtime.Composable +import eu.steffo.twom.matrix.complocals.LocalSession +import observePowerLevels +import kotlin.jvm.optionals.getOrNull + +@Composable +fun canIInvite(): Boolean { + val session = LocalSession.current ?: return false + val powerLevelsRequest = observePowerLevels() ?: return false + val powerLevels = powerLevelsRequest.getOrNull() ?: return false + return powerLevels.second.isUserAbleToInvite(session.myUserId) +} \ No newline at end of file diff --git a/app/src/main/java/eu/steffo/twom/viewroom/effects/canIKick.kt b/app/src/main/java/eu/steffo/twom/viewroom/effects/canIKick.kt new file mode 100644 index 0000000..c4fa0c5 --- /dev/null +++ b/app/src/main/java/eu/steffo/twom/viewroom/effects/canIKick.kt @@ -0,0 +1,14 @@ +package eu.steffo.twom.viewroom.effects + +import androidx.compose.runtime.Composable +import eu.steffo.twom.matrix.complocals.LocalSession +import observePowerLevels +import kotlin.jvm.optionals.getOrNull + +@Composable +fun canIKick(): Boolean { + val session = LocalSession.current ?: return false + val powerLevelsRequest = observePowerLevels() ?: return false + val powerLevels = powerLevelsRequest.getOrNull() ?: return false + return powerLevels.second.isUserAbleToKick(session.myUserId) +} \ No newline at end of file diff --git a/app/src/main/java/eu/steffo/twom/viewroom/effects/observePowerLevels.kt b/app/src/main/java/eu/steffo/twom/viewroom/effects/observePowerLevels.kt new file mode 100644 index 0000000..95fe4b7 --- /dev/null +++ b/app/src/main/java/eu/steffo/twom/viewroom/effects/observePowerLevels.kt @@ -0,0 +1,60 @@ +import android.util.Log +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState +import eu.steffo.twom.viewroom.complocals.LocalRoom +import org.matrix.android.sdk.api.query.QueryStringValue +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent +import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + + +private const val TAG = "observePowerLevels" + + +@Composable +fun observePowerLevels(): Optional>? { + val roomRequest = LocalRoom.current + + if (roomRequest == null) { + Log.v(TAG, "Requesting room information, not doing anything.") + return null + } + + val room = roomRequest.getOrNull() + + if (room == null) { + Log.e(TAG, "Room was not found, not doing anything.") + return null + } + + val powerLevelsRequest by room.stateService().getStateEventLive( + eventType = "m.room.power_levels", + stateKey = QueryStringValue.IsEmpty, + ).observeAsState() + + if (powerLevelsRequest == null) { + Log.v(TAG, "Power level event is being requested.") + return null + } + + val powerLevels = powerLevelsRequest?.getOrNull() + + if (powerLevels == null) { + Log.v(TAG, "No power level event has been found.") + return Optional.empty() + } + + val powerLevelsContent = powerLevels.content.toModel() + + if (powerLevelsContent == null) { + Log.e(TAG, "Could not deserialize power levels event.") + return Optional.empty() + } + + val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent) + + return Optional.of(Pair(powerLevelsContent, powerLevelsHelper)) +} \ No newline at end of file