diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index 4030e30272..4d4b644bc1 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -141,6 +141,10 @@ class RoomDetailsFlowNode @AssistedInject constructor( backstack.push(NavTarget.PinnedMessagesList) } + override fun openKnockRequestsList() { + // TODO open the knock requests list screen + } + override fun onJoinCall() { val inputs = CallType.RoomCall( sessionId = room.sessionId, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt index 19c0b4ffe4..e9ad095c76 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt @@ -47,6 +47,7 @@ class RoomDetailsNode @AssistedInject constructor( fun openPollHistory() fun openAdminSettings() fun openPinnedMessagesList() + fun openKnockRequestsList() fun onJoinCall() } @@ -111,6 +112,10 @@ class RoomDetailsNode @AssistedInject constructor( callbacks.forEach { it.openPinnedMessagesList() } } + private fun openKnockRequestsLists() { + callbacks.forEach { it.openKnockRequestsList() } + } + @Composable override fun View(modifier: Modifier) { val context = LocalContext.current @@ -140,7 +145,8 @@ class RoomDetailsNode @AssistedInject constructor( openPollHistory = ::openPollHistory, openAdminSettings = this::openAdminSettings, onJoinCallClick = ::onJoinCall, - onPinnedMessagesClick = ::openPinnedMessages + onPinnedMessagesClick = ::openPinnedMessages, + onKnockRequestsClick = ::openKnockRequestsLists ) } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt index 586790b618..dc6568a60f 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt @@ -38,6 +38,7 @@ import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.powerlevels.canInvite import io.element.android.libraries.matrix.api.room.powerlevels.canSendState import io.element.android.libraries.matrix.api.room.roomNotificationSettings +import io.element.android.libraries.matrix.ui.room.canHandleKnockRequestsAsState import io.element.android.libraries.matrix.ui.room.getCurrentRoomMember import io.element.android.libraries.matrix.ui.room.getDirectRoomMember import io.element.android.libraries.matrix.ui.room.isOwnUserAdmin @@ -69,7 +70,7 @@ class RoomDetailsPresenter @Inject constructor( val canShowNotificationSettings = remember { mutableStateOf(false) } val roomInfo by room.roomInfoFlow.collectAsState(initial = null) val isUserAdmin = room.isOwnUserAdmin() - + val syncUpdateFlow = room.syncUpdateFlow.collectAsState() val roomAvatar by remember { derivedStateOf { roomInfo?.avatarUrl ?: room.avatarUrl } } val roomName by remember { derivedStateOf { (roomInfo?.name ?: room.displayName).trim() } } @@ -90,6 +91,7 @@ class RoomDetailsPresenter @Inject constructor( val membersState by room.membersStateFlow.collectAsState() val canInvite by getCanInvite(membersState) + val canEditName by getCanSendState(membersState, StateEventType.ROOM_NAME) val canEditAvatar by getCanSendState(membersState, StateEventType.ROOM_AVATAR) val canEditTopic by getCanSendState(membersState, StateEventType.ROOM_TOPIC) @@ -99,6 +101,8 @@ class RoomDetailsPresenter @Inject constructor( val roomType by getRoomType(dmMember, currentMember) val roomCallState = roomCallStatePresenter.present() + val canHandleKnockRequests by room.canHandleKnockRequestsAsState(syncUpdateFlow.value) + val topicState = remember(canEditTopic, roomTopic, roomType) { val topic = roomTopic @@ -109,6 +113,12 @@ class RoomDetailsPresenter @Inject constructor( } } + val isKnockRequestsEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.Knock).collectAsState(false) + val knockRequestsCount by remember { mutableStateOf(null) } + val canShowKnockRequests by remember { + derivedStateOf { isKnockRequestsEnabled && canHandleKnockRequests } + } + val roomNotificationSettingsState by room.roomNotificationSettingsStateFlow.collectAsState() fun handleEvents(event: RoomDetailsEvent) { @@ -153,10 +163,16 @@ class RoomDetailsPresenter @Inject constructor( heroes = roomInfo?.heroes.orEmpty().toPersistentList(), canShowPinnedMessages = canShowPinnedMessages, pinnedMessagesCount = pinnedMessagesCount, + canShowKnockRequests = canShowKnockRequests, + knockRequestsCount = knockRequestsCount, eventSink = ::handleEvents, ) } + private fun getCanBan(membersState: MatrixRoomMembersState): Any { + TODO("Not yet implemented") + } + @Composable private fun roomMemberDetailsPresenter(dmMemberState: RoomMember?) = remember(dmMemberState) { dmMemberState?.let { roomMember -> diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt index d43b0a813a..7f15c846f9 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt @@ -41,6 +41,8 @@ data class RoomDetailsState( val heroes: ImmutableList, val canShowPinnedMessages: Boolean, val pinnedMessagesCount: Int?, + val canShowKnockRequests: Boolean, + val knockRequestsCount: Int?, val eventSink: (RoomDetailsEvent) -> Unit ) { val roomBadges = buildList { diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt index 49b9f73cb5..dcf5bc3054 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt @@ -102,6 +102,8 @@ fun aRoomDetailsState( heroes: List = emptyList(), canShowPinnedMessages: Boolean = true, pinnedMessagesCount: Int? = null, + canShowKnockRequests: Boolean = false, + knockRequestsCount: Int? = null, eventSink: (RoomDetailsEvent) -> Unit = {}, ) = RoomDetailsState( roomId = roomId, @@ -125,6 +127,8 @@ fun aRoomDetailsState( heroes = heroes.toPersistentList(), canShowPinnedMessages = canShowPinnedMessages, pinnedMessagesCount = pinnedMessagesCount, + canShowKnockRequests = canShowKnockRequests, + knockRequestsCount = knockRequestsCount, eventSink = eventSink ) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index 7e89c3ef07..80f7beda56 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -104,6 +104,7 @@ fun RoomDetailsView( openAdminSettings: () -> Unit, onJoinCallClick: () -> Unit, onPinnedMessagesClick: () -> Unit, + onKnockRequestsClick: () -> Unit, modifier: Modifier = Modifier, ) { Scaffold( @@ -206,6 +207,12 @@ fun RoomDetailsView( memberCount = state.memberCount, openRoomMemberList = openRoomMemberList, ) + if (state.canShowKnockRequests) { + KnockRequestsItem( + knockRequestsCount = state.knockRequestsCount, + onKnockRequestsClick = onKnockRequestsClick + ) + } } } @@ -231,6 +238,20 @@ fun RoomDetailsView( } } +@Composable +private fun KnockRequestsItem(knockRequestsCount: Int?, onKnockRequestsClick: () -> Unit) { + ListItem( + headlineContent = { Text(stringResource(CommonStrings.screen_room_details_requests_to_join_title)) }, + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Notifications())), + trailingContent = if (knockRequestsCount == null || knockRequestsCount == 0) { + null + } else { + ListItemContent.Text(knockRequestsCount.toString()) + }, + onClick = onKnockRequestsClick, + ) +} + @OptIn(ExperimentalMaterial3Api::class) @Composable private fun RoomDetailsTopBar( @@ -613,5 +634,6 @@ private fun ContentToPreview(state: RoomDetailsState) { openAdminSettings = {}, onJoinCallClick = {}, onPinnedMessagesClick = {}, + onKnockRequestsClick = {}, ) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt index 682596a59d..ba81a5780c 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt @@ -57,6 +57,13 @@ suspend fun MatrixRoom.canRedactOwn(): Result = canUserRedactOwn(sessio */ suspend fun MatrixRoom.canRedactOther(): Result = canUserRedactOther(sessionId) +/** + * Shortcut for checking if current user can handle knock requests. + */ +suspend fun MatrixRoom.canHandleKnockRequests(): Result = runCatching { + canInvite().getOrThrow() || canBan().getOrThrow() || canKick().getOrThrow() +} + /** * Shortcut for calling [MatrixRoom.canUserPinUnpin] with our own user. */ diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt index 81ae3e6b89..59bd1fa773 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt @@ -17,6 +17,7 @@ import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.powerlevels.canBan +import io.element.android.libraries.matrix.api.room.powerlevels.canHandleKnockRequests import io.element.android.libraries.matrix.api.room.powerlevels.canInvite import io.element.android.libraries.matrix.api.room.powerlevels.canKick import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOther @@ -86,6 +87,13 @@ fun MatrixRoom.canBanAsState(updateKey: Long): State { } } +@Composable +fun MatrixRoom.canHandleKnockRequestsAsState(updateKey: Long): State { + return produceState(initialValue = false, key1 = updateKey) { + value = canHandleKnockRequests().getOrElse { false } + } +} + @Composable fun MatrixRoom.userPowerLevelAsState(updateKey: Long): State { return produceState(initialValue = 0, key1 = updateKey) {