Add room badges (#2822)
* Add room badges * Remove no longer used `onShareRoomMember` callback * Update screenshots * Add changelog --------- Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
parent
4c1bd2d45b
commit
48bb0f4122
188 changed files with 355 additions and 80 deletions
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
@ -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() }
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
@ -160,7 +137,6 @@ class RoomDetailsNode @AssistedInject constructor(
|
|||
goBack = this::navigateUp,
|
||||
onActionClicked = ::onActionClicked,
|
||||
onShareRoom = ::onShareRoom,
|
||||
onShareMember = ::onShareMember,
|
||||
openRoomMemberList = ::openRoomMemberList,
|
||||
openRoomNotificationSettings = ::openRoomNotificationSettings,
|
||||
invitePeople = ::invitePeople,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -149,6 +150,7 @@ class RoomDetailsPresenter @Inject constructor(
|
|||
roomNotificationSettings = roomNotificationSettingsState.roomNotificationSettings(),
|
||||
isFavorite = isFavorite,
|
||||
displayRolesAndPermissionsSettings = !room.isDm && isUserAdmin,
|
||||
isPublic = isPublic,
|
||||
eventSink = ::handleEvents,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ data class RoomDetailsState(
|
|||
val roomNotificationSettings: RoomNotificationSettings?,
|
||||
val isFavorite: Boolean,
|
||||
val displayRolesAndPermissionsSettings: Boolean,
|
||||
val isPublic: Boolean,
|
||||
val eventSink: (RoomDetailsEvent) -> Unit
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
@ -97,6 +98,7 @@ fun aRoomDetailsState(
|
|||
roomNotificationSettings: RoomNotificationSettings = aRoomNotificationSettings(),
|
||||
isFavorite: Boolean = false,
|
||||
displayAdminSettings: Boolean = false,
|
||||
isPublic: Boolean = true,
|
||||
eventSink: (RoomDetailsEvent) -> Unit = {},
|
||||
) = RoomDetailsState(
|
||||
roomId = roomId,
|
||||
|
|
@ -116,6 +118,7 @@ fun aRoomDetailsState(
|
|||
roomNotificationSettings = roomNotificationSettings,
|
||||
isFavorite = isFavorite,
|
||||
displayRolesAndPermissionsSettings = displayAdminSettings,
|
||||
isPublic = isPublic,
|
||||
eventSink = eventSink
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -91,7 +91,6 @@ fun RoomDetailsView(
|
|||
goBack: () -> Unit,
|
||||
onActionClicked: (RoomDetailsAction) -> Unit,
|
||||
onShareRoom: () -> Unit,
|
||||
onShareMember: (RoomMember) -> Unit,
|
||||
openRoomMemberList: () -> Unit,
|
||||
openRoomNotificationSettings: () -> Unit,
|
||||
invitePeople: () -> Unit,
|
||||
|
|
@ -101,10 +100,6 @@ fun RoomDetailsView(
|
|||
onJoinCallClicked: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
fun onShareMember() {
|
||||
onShareMember((state.roomType as RoomDetailsType.Dm).roomMember)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
topBar = {
|
||||
|
|
@ -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)
|
||||
},
|
||||
|
|
@ -160,7 +157,7 @@ fun RoomDetailsView(
|
|||
)
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(18.dp))
|
||||
Spacer(Modifier.height(12.dp))
|
||||
|
||||
if (state.roomTopic !is RoomTopicState.Hidden) {
|
||||
TopicSection(
|
||||
|
|
@ -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
|
||||
|
|
@ -323,6 +322,8 @@ private fun RoomHeaderSection(
|
|||
roomId: RoomId,
|
||||
roomName: String,
|
||||
roomAlias: RoomAlias?,
|
||||
isEncrypted: Boolean,
|
||||
isPublic: Boolean,
|
||||
openAvatarPreview: (url: String) -> Unit,
|
||||
) {
|
||||
Column(
|
||||
|
|
@ -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,
|
||||
|
|
@ -489,7 +526,6 @@ private fun ContentToPreview(state: RoomDetailsState) {
|
|||
goBack = {},
|
||||
onActionClicked = {},
|
||||
onShareRoom = {},
|
||||
onShareMember = {},
|
||||
openRoomMemberList = {},
|
||||
openRoomNotificationSettings = {},
|
||||
invitePeople = {},
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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(),
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue