feat: material 3-ise friends screen

Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
Infi 2024-01-20 00:14:33 +01:00
parent 74b33380ab
commit ad4d06db7d
3 changed files with 110 additions and 215 deletions

View File

@ -4,12 +4,17 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
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.graphics.Color
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@Composable @Composable
@ -28,4 +33,29 @@ fun ListHeader(
content() content()
} }
} }
} }
@Composable
fun CountableListHeader(
text: String,
count: Int,
backgroundColor: Color = MaterialTheme.colorScheme.background
) {
ListHeader(backgroundColor = backgroundColor) {
Text(
text = AnnotatedString.Builder().apply {
append(text)
pushStyle(
SpanStyle(
fontWeight = FontWeight.Medium,
fontSize = LocalTextStyle.current.fontSize * 0.8,
color = LocalContentColor.current.copy(alpha = 0.6f)
)
)
append("$count")
pop()
}.toAnnotatedString()
)
}
}

View File

@ -1,15 +1,9 @@
package chat.revolt.screens.chat.views package chat.revolt.screens.chat.views
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.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
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.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu import androidx.compose.material.icons.filled.Menu
@ -17,9 +11,6 @@ import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.material3.rememberModalBottomSheetState
@ -29,25 +20,18 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import chat.revolt.R import chat.revolt.R
import chat.revolt.api.internals.FriendRequests import chat.revolt.api.internals.FriendRequests
import chat.revolt.api.routes.user.unfriendUser import chat.revolt.api.routes.user.unfriendUser
import chat.revolt.api.schemas.User
import chat.revolt.callbacks.Action import chat.revolt.callbacks.Action
import chat.revolt.callbacks.ActionChannel import chat.revolt.callbacks.ActionChannel
import chat.revolt.components.chat.MemberListItem
import chat.revolt.components.generic.CountableListHeader
import chat.revolt.components.generic.PageHeader import chat.revolt.components.generic.PageHeader
import chat.revolt.components.generic.SheetClickable import chat.revolt.components.generic.SheetClickable
import chat.revolt.components.generic.UserAvatar
import chat.revolt.components.generic.presenceFromStatus
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -132,221 +116,125 @@ fun FriendsScreen(useDrawer: Boolean, onDrawerClicked: () -> Unit) {
LazyColumn { LazyColumn {
stickyHeader(key = "incoming") { stickyHeader(key = "incoming") {
Text( CountableListHeader(
text = AnnotatedString.Builder().apply { text = stringResource(id = R.string.friends_incoming_requests),
pushStyle(SpanStyle(fontWeight = FontWeight.Bold)) count = FriendRequests.getIncoming().size
append(stringResource(id = R.string.friends_incoming_requests))
pop()
pushStyle(
SpanStyle(
fontWeight = FontWeight.Medium,
fontSize = LocalTextStyle.current.fontSize * 0.8,
color = LocalContentColor.current.copy(alpha = 0.6f)
)
)
append("${FriendRequests.getIncoming().size}")
pop()
}.toAnnotatedString(),
style = MaterialTheme.typography.labelLarge,
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.background)
.padding(10.dp)
) )
} }
items(FriendRequests.getIncoming().size) { items(FriendRequests.getIncoming().size) {
val item = FriendRequests.getIncoming()[it] val item = FriendRequests.getIncoming()[it]
UserItem(item, onClick = { MemberListItem(
scope.launch { member = null,
item.id?.let { userId -> user = item,
ActionChannel.send(Action.OpenUserSheet(userId, null)) serverId = null,
userId = item.id ?: "",
modifier = Modifier.clickable {
scope.launch {
item.id?.let { userId ->
ActionChannel.send(Action.OpenUserSheet(userId, null))
}
} }
} }
}) )
} }
stickyHeader(key = "outgoing") { stickyHeader(key = "outgoing") {
Text( CountableListHeader(
text = AnnotatedString.Builder().apply { text = stringResource(id = R.string.friends_outgoing_requests),
pushStyle(SpanStyle(fontWeight = FontWeight.Bold)) count = FriendRequests.getOutgoing().size
append(stringResource(id = R.string.friends_outgoing_requests))
pop()
pushStyle(
SpanStyle(
fontWeight = FontWeight.Medium,
fontSize = LocalTextStyle.current.fontSize * 0.8,
color = LocalContentColor.current.copy(alpha = 0.6f)
)
)
append("${FriendRequests.getOutgoing().size}")
pop()
}.toAnnotatedString(),
style = MaterialTheme.typography.labelLarge,
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.background)
.padding(10.dp)
) )
} }
items(FriendRequests.getOutgoing().size) { items(FriendRequests.getOutgoing().size) {
val item = FriendRequests.getOutgoing()[it] val item = FriendRequests.getOutgoing()[it]
UserItem(item, onClick = { MemberListItem(
scope.launch { member = null,
item.id?.let { userId -> user = item,
ActionChannel.send(Action.OpenUserSheet(userId, null)) serverId = null,
userId = item.id ?: "",
modifier = Modifier.clickable {
scope.launch {
item.id?.let { userId ->
ActionChannel.send(Action.OpenUserSheet(userId, null))
}
} }
} }
}) )
} }
stickyHeader(key = "online") { stickyHeader(key = "online") {
Text( CountableListHeader(
text = AnnotatedString.Builder().apply { text = stringResource(id = R.string.status_online),
pushStyle(SpanStyle(fontWeight = FontWeight.Bold)) count = FriendRequests.getOnlineFriends().size
append(stringResource(id = R.string.status_online))
pop()
pushStyle(
SpanStyle(
fontWeight = FontWeight.Medium,
fontSize = LocalTextStyle.current.fontSize * 0.8,
color = LocalContentColor.current.copy(alpha = 0.6f)
)
)
append("${FriendRequests.getOnlineFriends().size}")
pop()
}.toAnnotatedString(),
style = MaterialTheme.typography.labelLarge,
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.background)
.padding(10.dp)
) )
} }
items(FriendRequests.getOnlineFriends().size) { items(FriendRequests.getOnlineFriends().size) {
val item = FriendRequests.getOnlineFriends()[it] val item = FriendRequests.getOnlineFriends()[it]
UserItem(item, onClick = { MemberListItem(
scope.launch { member = null,
item.id?.let { userId -> user = item,
ActionChannel.send(Action.OpenUserSheet(userId, null)) serverId = null,
userId = item.id ?: "",
modifier = Modifier.clickable {
scope.launch {
item.id?.let { userId ->
ActionChannel.send(Action.OpenUserSheet(userId, null))
}
} }
} }
}) )
} }
stickyHeader(key = "not_online") { stickyHeader(key = "not_online") {
Text( CountableListHeader(
text = AnnotatedString.Builder().apply { text = stringResource(id = R.string.friends_all),
pushStyle(SpanStyle(fontWeight = FontWeight.Bold)) count = FriendRequests.getFriends(true).size
append(stringResource(id = R.string.friends_all))
pop()
pushStyle(
SpanStyle(
fontWeight = FontWeight.Medium,
fontSize = LocalTextStyle.current.fontSize * 0.8,
color = LocalContentColor.current.copy(alpha = 0.6f)
)
)
append("${FriendRequests.getFriends(true).size}")
pop()
}.toAnnotatedString(),
style = MaterialTheme.typography.labelLarge,
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.background)
.padding(10.dp)
) )
} }
items(FriendRequests.getFriends(true).size) { items(FriendRequests.getFriends(true).size) {
val item = FriendRequests.getFriends(true)[it] val item = FriendRequests.getFriends(true)[it]
UserItem(item, onClick = { MemberListItem(
scope.launch { member = null,
item.id?.let { userId -> user = item,
ActionChannel.send(Action.OpenUserSheet(userId, null)) serverId = null,
userId = item.id ?: "",
modifier = Modifier.clickable {
scope.launch {
item.id?.let { userId ->
ActionChannel.send(Action.OpenUserSheet(userId, null))
}
} }
} }
}) )
} }
stickyHeader(key = "blocked") { stickyHeader(key = "blocked") {
Text( CountableListHeader(
text = AnnotatedString.Builder().apply { text = stringResource(id = R.string.friends_blocked),
pushStyle(SpanStyle(fontWeight = FontWeight.Bold)) count = FriendRequests.getBlocked().size
append(stringResource(id = R.string.friends_blocked))
pop()
pushStyle(
SpanStyle(
fontWeight = FontWeight.Medium,
fontSize = LocalTextStyle.current.fontSize * 0.8,
color = LocalContentColor.current.copy(alpha = 0.6f)
)
)
append("${FriendRequests.getBlocked().size}")
pop()
}.toAnnotatedString(),
style = MaterialTheme.typography.labelLarge,
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.background)
.padding(10.dp)
) )
} }
items(FriendRequests.getBlocked().size) { items(FriendRequests.getBlocked().size) {
val item = FriendRequests.getBlocked()[it] val item = FriendRequests.getBlocked()[it]
UserItem(item, onClick = { MemberListItem(
scope.launch { member = null,
item.id?.let { userId -> user = item,
ActionChannel.send(Action.OpenUserSheet(userId, null)) serverId = null,
userId = item.id ?: "",
modifier = Modifier.clickable {
scope.launch {
item.id?.let { userId ->
ActionChannel.send(Action.OpenUserSheet(userId, null))
}
} }
} }
}) )
} }
} }
} }
}
@Composable
fun UserItem(user: User, onClick: () -> Unit = {}) {
Row(
modifier = Modifier
.clickable {
onClick()
}
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
UserAvatar(
username = user.displayName
?: user.username
?: user.id!!,
avatar = user.avatar,
userId = user.id!!,
presence = presenceFromStatus(
user.status?.presence,
user.online ?: false
)
)
Spacer(modifier = Modifier.width(12.dp))
Text(
text = user.displayName
?: user.username
?: user.id,
fontWeight = FontWeight.Bold,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
} }

View File

@ -12,11 +12,8 @@ import androidx.compose.foundation.layout.height
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
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -30,9 +27,6 @@ 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.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.font.FontWeight
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
@ -47,7 +41,7 @@ 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.chat.MemberListItem
import chat.revolt.components.generic.ListHeader import chat.revolt.components.generic.CountableListHeader
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.presenceFromStatus import chat.revolt.components.generic.presenceFromStatus
@ -268,7 +262,11 @@ fun MemberListSheet(
is MemberListSheetItem.CategoryItem -> stickyHeader( is MemberListSheetItem.CategoryItem -> stickyHeader(
key = "${item.category}-$index" key = "${item.category}-$index"
) { ) {
MemberListCategory(text = item.category, count = item.count) CountableListHeader(
text = item.category,
count = item.count,
backgroundColor = MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp)
)
} }
is MemberListSheetItem.MemberItem -> item(key = item.member.id!!.user) { is MemberListSheetItem.MemberItem -> item(key = item.member.id!!.user) {
@ -300,25 +298,4 @@ fun MemberListSheet(
} }
} }
} }
} }
@Composable
fun MemberListCategory(text: String, count: Int) {
ListHeader(backgroundColor = MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp)) {
Text(
text = AnnotatedString.Builder().apply {
append(text)
pushStyle(
SpanStyle(
fontWeight = FontWeight.Medium,
fontSize = LocalTextStyle.current.fontSize * 0.8,
color = LocalContentColor.current.copy(alpha = 0.6f)
)
)
append("$count")
pop()
}.toAnnotatedString()
)
}
}