feat: material 3-ise member sheet

Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
Infi 2024-01-13 16:17:08 +01:00
parent b75f540578
commit 1f81399da2
4 changed files with 110 additions and 99 deletions

View File

@ -0,0 +1,72 @@
package chat.revolt.components.chat
import androidx.compose.material3.ListItem
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.text.style.TextOverflow
import chat.revolt.api.REVOLT_FILES
import chat.revolt.api.internals.Roles
import chat.revolt.api.internals.WebCompat
import chat.revolt.api.internals.solidColor
import chat.revolt.api.schemas.Member
import chat.revolt.api.schemas.User
import chat.revolt.components.generic.UserAvatar
import chat.revolt.components.generic.presenceFromStatus
@Composable
fun MemberListItem(
member: Member?,
user: User?,
serverId: String?,
userId: String,
modifier: Modifier = Modifier,
) {
val highestColourRole = serverId?.let {
user?.id?.let { userId ->
Roles.resolveHighestRole(
it,
userId,
true
)
}
}
val colour = highestColourRole?.colour?.let { WebCompat.parseColour(it) }
?: Brush.solidColor(LocalContentColor.current)
ListItem(
modifier = modifier,
headlineContent = {
Text(
text = member?.nickname
?: user?.displayName
?: user?.username
?: user?.id
?: userId,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = LocalTextStyle.current.copy(brush = colour),
)
},
leadingContent = {
UserAvatar(
username = member?.nickname
?: user?.displayName
?: user?.username
?: user?.id
?: userId,
avatar = user?.avatar,
rawUrl = member?.avatar?.let { "$REVOLT_FILES/avatars/${it.id}?max_side=256" },
userId = userId,
presence = presenceFromStatus(
user?.status?.presence,
user?.online ?: false
)
)
},
)
}

View File

@ -9,17 +9,20 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@Composable @Composable
fun ListHeader( fun ListHeader(
modifier: Modifier = Modifier,
backgroundColor: Color = MaterialTheme.colorScheme.background,
content: @Composable () -> Unit, content: @Composable () -> Unit,
) { ) {
CompositionLocalProvider(LocalTextStyle provides MaterialTheme.typography.labelLarge) { CompositionLocalProvider(LocalTextStyle provides MaterialTheme.typography.labelLarge) {
Box( Box(
modifier = Modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.background(MaterialTheme.colorScheme.background) .background(backgroundColor)
.padding(top = 24.dp, bottom = 8.dp, start = 16.dp, end = 16.dp) .padding(top = 24.dp, bottom = 8.dp, start = 16.dp, end = 16.dp)
) { ) {
content() content()

View File

@ -4,16 +4,11 @@ import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
@ -34,31 +29,27 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import chat.revolt.R import chat.revolt.R
import chat.revolt.api.REVOLT_FILES
import chat.revolt.api.RevoltAPI import chat.revolt.api.RevoltAPI
import chat.revolt.api.internals.PermissionBit import chat.revolt.api.internals.PermissionBit
import chat.revolt.api.internals.Roles import chat.revolt.api.internals.Roles
import chat.revolt.api.internals.WebCompat
import chat.revolt.api.internals.hasPermission import chat.revolt.api.internals.hasPermission
import chat.revolt.api.internals.solidColor
import chat.revolt.api.routes.channel.fetchGroupParticipants import chat.revolt.api.routes.channel.fetchGroupParticipants
import chat.revolt.api.routes.server.fetchMembers import chat.revolt.api.routes.server.fetchMembers
import chat.revolt.api.schemas.Member import chat.revolt.api.schemas.Member
import chat.revolt.api.schemas.User import chat.revolt.api.schemas.User
import chat.revolt.components.chat.MemberListItem
import chat.revolt.components.generic.ListHeader
import chat.revolt.components.generic.PageHeader import chat.revolt.components.generic.PageHeader
import chat.revolt.components.generic.Presence import chat.revolt.components.generic.Presence
import chat.revolt.components.generic.UserAvatar
import chat.revolt.components.generic.presenceFromStatus import chat.revolt.components.generic.presenceFromStatus
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
@ -281,24 +272,26 @@ fun MemberListSheet(
} }
is MemberListItem.MemberItem -> item(key = item.member.id!!.user) { is MemberListItem.MemberItem -> item(key = item.member.id!!.user) {
MemberListMemberUser( MemberListItem(
user = RevoltAPI.userCache[item.member.id.user]!!, user = RevoltAPI.userCache[item.member.id.user],
member = item.member, member = item.member,
serverId = serverId, serverId = serverId,
onSelectUser = { userId = item.member.id.user,
userContextSheetTarget = it modifier = Modifier.clickable {
userContextSheetTarget = item.member.id.user
showUserContextSheet = true showUserContextSheet = true
} }
) )
} }
is MemberListItem.UserItem -> item(key = item.user.id!!) { is MemberListItem.UserItem -> item(key = item.user.id!!) {
MemberListMemberUser( MemberListItem(
user = item.user, user = item.user,
member = null, member = null,
serverId = serverId, serverId = serverId,
onSelectUser = { userId = item.user.id,
userContextSheetTarget = it modifier = Modifier.clickable {
userContextSheetTarget = item.user.id
showUserContextSheet = true showUserContextSheet = true
} }
) )
@ -309,86 +302,23 @@ fun MemberListSheet(
} }
} }
@Composable
fun MemberListMemberUser(
user: User,
member: Member?,
serverId: String?,
onSelectUser: (String) -> Unit
) {
val highestColourRole = serverId?.let {
user.id?.let { userId ->
Roles.resolveHighestRole(
it,
userId,
true
)
}
}
val colour = highestColourRole?.colour?.let { WebCompat.parseColour(it) }
?: Brush.solidColor(LocalContentColor.current)
Row(
modifier = Modifier
.clickable {
onSelectUser(user.id!!)
}
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
UserAvatar(
username = member?.nickname
?: user.displayName
?: user.username
?: user.id!!,
avatar = user.avatar,
rawUrl = member?.avatar?.let { "$REVOLT_FILES/avatars/${it.id}?max_side=256" },
userId = user.id!!,
presence = presenceFromStatus(
user.status?.presence,
user.online ?: false
)
)
Spacer(modifier = Modifier.width(12.dp))
Text(
text = member?.nickname
?: user.displayName
?: user.username
?: user.id,
style = LocalTextStyle.current.copy(
fontWeight = FontWeight.Bold,
brush = colour
),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
@Composable @Composable
fun MemberListCategory(text: String, count: Int) { fun MemberListCategory(text: String, count: Int) {
Text( ListHeader(backgroundColor = MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp)) {
text = AnnotatedString.Builder().apply { Text(
pushStyle(SpanStyle(fontWeight = FontWeight.Bold)) text = AnnotatedString.Builder().apply {
append(text) append(text)
pop()
pushStyle( pushStyle(
SpanStyle( SpanStyle(
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
fontSize = LocalTextStyle.current.fontSize * 0.8, fontSize = LocalTextStyle.current.fontSize * 0.8,
color = LocalContentColor.current.copy(alpha = 0.6f) color = LocalContentColor.current.copy(alpha = 0.6f)
)
) )
) append("$count")
append("$count") pop()
pop() }.toAnnotatedString()
}.toAnnotatedString(), )
modifier = Modifier }
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp))
.padding(horizontal = 12.dp, vertical = 8.dp)
)
} }

View File

@ -41,6 +41,7 @@ import chat.revolt.api.internals.isUlid
import chat.revolt.api.routes.custom.fetchEmoji import chat.revolt.api.routes.custom.fetchEmoji
import chat.revolt.api.schemas.Emoji import chat.revolt.api.schemas.Emoji
import chat.revolt.api.schemas.User import chat.revolt.api.schemas.User
import chat.revolt.components.chat.MemberListItem
import chat.revolt.components.generic.RemoteImage import chat.revolt.components.generic.RemoteImage
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@ -210,7 +211,12 @@ fun ReactionInfoSheet(messageId: String, emoji: String, onDismiss: () -> Unit) {
null null
} }
MemberListMemberUser(user = user, member = member, serverId = channel.server) {} MemberListItem(
member = member,
user = user,
serverId = channel.server,
userId = reaction,
)
} }
} }