Skip to content

Add room badges #2822

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/2822.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add room badges to room details screen.
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ import im.vector.app.features.analytics.plan.MobileScreen
import io.element.android.anvilannotations.ContributesNode
import io.element.android.libraries.androidutils.system.startSharePlainTextIntent
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.services.analytics.api.AnalyticsService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
Expand All @@ -48,7 +46,6 @@ class RoomDetailsNode @AssistedInject constructor(
private val presenter: RoomDetailsPresenter,
private val room: MatrixRoom,
private val analyticsService: AnalyticsService,
private val permalinkBuilder: PermalinkBuilder,
) : Node(buildContext, plugins = plugins) {
interface Callback : Plugin {
fun openRoomMemberList()
Expand Down Expand Up @@ -106,22 +103,6 @@ class RoomDetailsNode @AssistedInject constructor(
}
}

private fun onShareMember(context: Context, member: RoomMember) {
val permalinkResult = permalinkBuilder.permalinkForUser(member.userId)
permalinkResult
.onSuccess { permalink ->
context.startSharePlainTextIntent(
activityResultLauncher = null,
chooserTitle = context.getString(R.string.screen_room_details_share_room_title),
text = permalink,
noActivityFoundMessage = context.getString(AndroidUtilsR.string.error_no_compatible_app_found)
)
}
.onFailure {
Timber.e(it)
}
}

private fun onEditRoomDetails() {
callbacks.forEach { it.editRoomDetails() }
}
Expand All @@ -143,10 +124,6 @@ class RoomDetailsNode @AssistedInject constructor(
lifecycleScope.onShareRoom(context)
}

fun onShareMember(roomMember: RoomMember) {
this.onShareMember(context, roomMember)
}

fun onActionClicked(action: RoomDetailsAction) {
when (action) {
RoomDetailsAction.Edit -> onEditRoomDetails()
Expand All @@ -160,7 +137,6 @@ class RoomDetailsNode @AssistedInject constructor(
goBack = this::navigateUp,
onActionClicked = ::onActionClicked,
onShareRoom = ::onShareRoom,
onShareMember = ::onShareMember,
openRoomMemberList = ::openRoomMemberList,
openRoomNotificationSettings = ::openRoomNotificationSettings,
invitePeople = ::invitePeople,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class RoomDetailsPresenter @Inject constructor(
val roomName by remember { derivedStateOf { (roomInfo?.name ?: room.name ?: room.displayName).trim() } }
val roomTopic by remember { derivedStateOf { roomInfo?.topic ?: room.topic } }
val isFavorite by remember { derivedStateOf { roomInfo?.isFavorite.orFalse() } }
val isPublic by remember { derivedStateOf { roomInfo?.isPublic.orFalse() } }

LaunchedEffect(Unit) {
canShowNotificationSettings.value = featureFlagService.isFeatureEnabled(FeatureFlags.NotificationSettings)
Expand Down Expand Up @@ -149,6 +150,7 @@ class RoomDetailsPresenter @Inject constructor(
roomNotificationSettings = roomNotificationSettingsState.roomNotificationSettings(),
isFavorite = isFavorite,
displayRolesAndPermissionsSettings = !room.isDm && isUserAdmin,
isPublic = isPublic,
eventSink = ::handleEvents,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ data class RoomDetailsState(
val roomNotificationSettings: RoomNotificationSettings?,
val isFavorite: Boolean,
val displayRolesAndPermissionsSettings: Boolean,
val isPublic: Boolean,
val eventSink: (RoomDetailsEvent) -> Unit
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ open class RoomDetailsStateProvider : PreviewParameterProvider<RoomDetailsState>
roomNotificationSettings = aRoomNotificationSettings(mode = RoomNotificationMode.ALL_MESSAGES, isDefault = true)
),
aRoomDetailsState(canCall = false, canInvite = false),
aRoomDetailsState(isPublic = false),
// Add other state here
)
}
Expand Down Expand Up @@ -97,6 +98,7 @@ fun aRoomDetailsState(
roomNotificationSettings: RoomNotificationSettings = aRoomNotificationSettings(),
isFavorite: Boolean = false,
displayAdminSettings: Boolean = false,
isPublic: Boolean = true,
eventSink: (RoomDetailsEvent) -> Unit = {},
) = RoomDetailsState(
roomId = roomId,
Expand All @@ -116,6 +118,7 @@ fun aRoomDetailsState(
roomNotificationSettings = roomNotificationSettings,
isFavorite = isFavorite,
displayRolesAndPermissionsSettings = displayAdminSettings,
isPublic = isPublic,
eventSink = eventSink
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.leaveroom.api.LeaveRoomView
import io.element.android.features.roomdetails.impl.components.RoomBadge
import io.element.android.features.userprofile.shared.UserProfileHeaderSection
import io.element.android.features.userprofile.shared.blockuser.BlockUserDialogs
import io.element.android.features.userprofile.shared.blockuser.BlockUserSection
Expand Down Expand Up @@ -78,7 +79,6 @@ import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.CommonDrawables
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.room.RoomMember
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.matrix.api.room.getBestName
import io.element.android.libraries.testtags.TestTags
Expand All @@ -91,7 +91,6 @@ fun RoomDetailsView(
goBack: () -> Unit,
onActionClicked: (RoomDetailsAction) -> Unit,
onShareRoom: () -> Unit,
onShareMember: (RoomMember) -> Unit,
openRoomMemberList: () -> Unit,
openRoomNotificationSettings: () -> Unit,
invitePeople: () -> Unit,
Expand All @@ -101,10 +100,6 @@ fun RoomDetailsView(
onJoinCallClicked: () -> Unit,
modifier: Modifier = Modifier,
) {
fun onShareMember() {
onShareMember((state.roomType as RoomDetailsType.Dm).roomMember)
}

Scaffold(
modifier = modifier,
topBar = {
Expand All @@ -130,6 +125,8 @@ fun RoomDetailsView(
roomId = state.roomId,
roomName = state.roomName,
roomAlias = state.roomAlias,
isEncrypted = state.isEncrypted,
isPublic = state.isPublic,
openAvatarPreview = { avatarUrl ->
openAvatarPreview(state.roomName, avatarUrl)
},
Expand Down Expand Up @@ -160,7 +157,7 @@ fun RoomDetailsView(
)
}
}
Spacer(Modifier.height(18.dp))
Spacer(Modifier.height(12.dp))

if (state.roomTopic !is RoomTopicState.Hidden) {
TopicSection(
Expand Down Expand Up @@ -269,7 +266,9 @@ private fun MainActionsSection(
onCall: () -> Unit,
) {
Row(
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.SpaceEvenly,
) {
val roomNotificationSettings = state.roomNotificationSettings
Expand Down Expand Up @@ -323,6 +322,8 @@ private fun RoomHeaderSection(
roomId: RoomId,
roomName: String,
roomAlias: RoomAlias?,
isEncrypted: Boolean,
isPublic: Boolean,
openAvatarPreview: (url: String) -> Unit,
) {
Column(
Expand Down Expand Up @@ -353,10 +354,46 @@ private fun RoomHeaderSection(
textAlign = TextAlign.Center,
)
}
BadgeList(isEncrypted = isEncrypted, isPublic = isPublic)
Spacer(Modifier.height(32.dp))
}
}

@Composable
private fun BadgeList(
isEncrypted: Boolean,
isPublic: Boolean,
) {
if (isEncrypted || isPublic) {
Spacer(modifier = Modifier.height(8.dp))
Row(
modifier = Modifier.padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
if (isEncrypted) {
RoomBadge.View(
text = stringResource(R.string.screen_room_details_badge_encrypted),
icon = CompoundIcons.LockSolid(),
type = RoomBadge.Type.Positive,
)
} else {
RoomBadge.View(
text = stringResource(R.string.screen_room_details_badge_not_encrypted),
icon = CompoundIcons.LockOff(),
type = RoomBadge.Type.Neutral,
)
}
if (isPublic) {
RoomBadge.View(
text = stringResource(R.string.screen_room_details_badge_public),
icon = CompoundIcons.Public(),
type = RoomBadge.Type.Neutral,
)
}
}
}
}

@Composable
private fun TopicSection(
roomTopic: RoomTopicState,
Expand Down Expand Up @@ -489,7 +526,6 @@ private fun ContentToPreview(state: RoomDetailsState) {
goBack = {},
onActionClicked = {},
onShareRoom = {},
onShareMember = {},
openRoomMemberList = {},
openRoomNotificationSettings = {},
invitePeople = {},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.element.android.features.roomdetails.impl.components

import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.ImageVector
import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.libraries.designsystem.components.Badge
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.badgeNegativeBackgroundColor
import io.element.android.libraries.designsystem.theme.badgeNegativeContentColor
import io.element.android.libraries.designsystem.theme.badgeNeutralBackgroundColor
import io.element.android.libraries.designsystem.theme.badgeNeutralContentColor
import io.element.android.libraries.designsystem.theme.badgePositiveBackgroundColor
import io.element.android.libraries.designsystem.theme.badgePositiveContentColor

object RoomBadge {
enum class Type {
Positive,
Neutral,
Negative
}

@Composable fun View(
text: String,
icon: ImageVector,
type: Type,
) {
val backgroundColor = when (type) {
Type.Positive -> ElementTheme.colors.badgePositiveBackgroundColor
Type.Neutral -> ElementTheme.colors.badgeNeutralBackgroundColor
Type.Negative -> ElementTheme.colors.badgeNegativeBackgroundColor
}
val textColor = when (type) {
Type.Positive -> ElementTheme.colors.badgePositiveContentColor
Type.Neutral -> ElementTheme.colors.badgeNeutralContentColor
Type.Negative -> ElementTheme.colors.badgeNegativeContentColor
}
val iconColor = when (type) {
Type.Positive -> ElementTheme.colors.iconSuccessPrimary
Type.Neutral -> ElementTheme.colors.iconSecondary
Type.Negative -> ElementTheme.colors.iconCriticalPrimary
}
Badge(
text = text,
icon = icon,
backgroundColor = backgroundColor,
iconColor = iconColor,
textColor = textColor,
)
}
}

@PreviewsDayNight
@Composable
internal fun RoomBadgePositivePreview() {
ElementPreview {
RoomBadge.View(
text = "Trusted",
icon = CompoundIcons.Verified(),
type = RoomBadge.Type.Positive,
)
}
}

@PreviewsDayNight
@Composable
internal fun RoomBadgeNeutralPreview() {
ElementPreview {
RoomBadge.View(
text = "Public room",
icon = CompoundIcons.Public(),
type = RoomBadge.Type.Neutral,
)
}
}

@PreviewsDayNight
@Composable
internal fun RoomBadgeNegativePreview() {
ElementPreview {
RoomBadge.View(
text = "Not trusted",
icon = CompoundIcons.Error(),
type = RoomBadge.Type.Negative,
)
}
}
3 changes: 3 additions & 0 deletions features/roomdetails/impl/src/main/res/values/localazy.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
<string name="screen_room_details_add_topic_title">"Add topic"</string>
<string name="screen_room_details_already_a_member">"Already a member"</string>
<string name="screen_room_details_already_invited">"Already invited"</string>
<string name="screen_room_details_badge_encrypted">"Encrypted"</string>
<string name="screen_room_details_badge_not_encrypted">"Not encrypted"</string>
<string name="screen_room_details_badge_public">"Public room"</string>
<string name="screen_room_details_edit_room_title">"Edit Room"</string>
<string name="screen_room_details_edition_error">"There was an unknown error and the information couldn\'t be changed."</string>
<string name="screen_room_details_edition_error_title">"Unable to update room"</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.ui.strings.CommonStrings
Expand Down Expand Up @@ -251,7 +250,6 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRoomD
goBack: () -> Unit = EnsureNeverCalled(),
onActionClicked: (RoomDetailsAction) -> Unit = EnsureNeverCalledWithParam(),
onShareRoom: () -> Unit = EnsureNeverCalled(),
onShareMember: (RoomMember) -> Unit = EnsureNeverCalledWithParam(),
openRoomMemberList: () -> Unit = EnsureNeverCalled(),
openRoomNotificationSettings: () -> Unit = EnsureNeverCalled(),
invitePeople: () -> Unit = EnsureNeverCalled(),
Expand All @@ -266,7 +264,6 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRoomD
goBack = goBack,
onActionClicked = onActionClicked,
onShareRoom = onShareRoom,
onShareMember = onShareMember,
openRoomMemberList = openRoomMemberList,
openRoomNotificationSettings = openRoomNotificationSettings,
invitePeople = invitePeople,
Expand Down
Loading
Loading