From 9e3543be37cfb6ba73a9d8a511e842f139b50a73 Mon Sep 17 00:00:00 2001 From: Infi Date: Fri, 1 Dec 2023 15:07:55 +0100 Subject: [PATCH] feat: redesign user info sheet Signed-off-by: Infi --- .../chat/revolt/api/internals/WebCompat.kt | 9 +- .../main/java/chat/revolt/api/schemas/User.kt | 19 + .../revolt/components/chat/RoleListEntry.kt | 39 + .../revolt/components/chat/UserBadgeList.kt | 184 ++ .../screens/settings/UserButtons.kt | 4 +- .../revolt/components/sheets/SheetTile.kt | 85 + .../java/chat/revolt/sheets/UserInfoSheet.kt | 250 +- .../res/drawable/user_badge_developer.xml | 10 + .../res/drawable/user_badge_disclosure.xml | 18 + .../res/drawable/user_badge_early_adopter.xml | 12 + .../main/res/drawable/user_badge_founder.xml | 11 + .../res/drawable/user_badge_moderation.xml | 65 + app/src/main/res/drawable/user_badge_paw.xml | 25 + .../main/res/drawable/user_badge_raccoon.xml | 36 + .../user_badge_reserved_relevant_one.xml | 33 + .../user_badge_reserved_relevant_two.xml | 2601 +++++++++++++++++ .../res/drawable/user_badge_supporter.xml | 52 + .../res/drawable/user_badge_translator.xml | 28 + app/src/main/res/values/strings.xml | 15 + 19 files changed, 3415 insertions(+), 81 deletions(-) create mode 100644 app/src/main/java/chat/revolt/components/chat/RoleListEntry.kt create mode 100644 app/src/main/java/chat/revolt/components/chat/UserBadgeList.kt create mode 100644 app/src/main/java/chat/revolt/components/sheets/SheetTile.kt create mode 100644 app/src/main/res/drawable/user_badge_developer.xml create mode 100644 app/src/main/res/drawable/user_badge_disclosure.xml create mode 100644 app/src/main/res/drawable/user_badge_early_adopter.xml create mode 100644 app/src/main/res/drawable/user_badge_founder.xml create mode 100644 app/src/main/res/drawable/user_badge_moderation.xml create mode 100644 app/src/main/res/drawable/user_badge_paw.xml create mode 100644 app/src/main/res/drawable/user_badge_raccoon.xml create mode 100644 app/src/main/res/drawable/user_badge_reserved_relevant_one.xml create mode 100644 app/src/main/res/drawable/user_badge_reserved_relevant_two.xml create mode 100644 app/src/main/res/drawable/user_badge_supporter.xml create mode 100644 app/src/main/res/drawable/user_badge_translator.xml diff --git a/app/src/main/java/chat/revolt/api/internals/WebCompat.kt b/app/src/main/java/chat/revolt/api/internals/WebCompat.kt index d4a22e4a..1071c8f2 100644 --- a/app/src/main/java/chat/revolt/api/internals/WebCompat.kt +++ b/app/src/main/java/chat/revolt/api/internals/WebCompat.kt @@ -7,14 +7,9 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush.Companion.linearGradient import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor -// color is spelled american because Color from compose is spelled american -fun Brush.Companion.solidColor(colour: Color) = linearGradient( - colorStops = arrayOf( - 0f to colour, - 1f to colour - ) -) +fun Brush.Companion.solidColor(colour: Color) = SolidColor(colour) // Some colours that are not present in Android's built-in list. // not exhaustive, but covers most of the ones I've seen in the wild diff --git a/app/src/main/java/chat/revolt/api/schemas/User.kt b/app/src/main/java/chat/revolt/api/schemas/User.kt index 9d460d2a..5ef061d3 100644 --- a/app/src/main/java/chat/revolt/api/schemas/User.kt +++ b/app/src/main/java/chat/revolt/api/schemas/User.kt @@ -65,6 +65,25 @@ data class User( } } +enum class UserBadges(val value: Long) { + Developer(1L shl 0), + Translator(1L shl 1), + Supporter(1L shl 2), + ResponsibleDisclosure(1L shl 3), + Founder(1L shl 4), + PlatformModeration(1L shl 5), + ActiveSupporter(1L shl 6), + Paw(1L shl 7), + EarlyAdopter(1L shl 8), + ReservedRelevantJokeBadge1(1L shl 9), + ReservedRelevantJokeBadge2(1L shl 10), +} + +infix fun Long?.has(flag: UserBadges): Boolean { + if (this == null) return false + return this and flag.value == flag.value +} + @Serializable data class Bot( val owner: String? = null diff --git a/app/src/main/java/chat/revolt/components/chat/RoleListEntry.kt b/app/src/main/java/chat/revolt/components/chat/RoleListEntry.kt new file mode 100644 index 00000000..0016c49d --- /dev/null +++ b/app/src/main/java/chat/revolt/components/chat/RoleListEntry.kt @@ -0,0 +1,39 @@ +package chat.revolt.components.chat + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp + +@Composable +fun RoleListEntry(label: String, brush: Brush, modifier: Modifier = Modifier) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = modifier + ) { + Text( + text = label, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + + Spacer(Modifier.weight(1f)) + + Box( + modifier = Modifier + .clip(CircleShape) + .size(14.dp) + .background(brush = brush) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/components/chat/UserBadgeList.kt b/app/src/main/java/chat/revolt/components/chat/UserBadgeList.kt new file mode 100644 index 00000000..3010fcf4 --- /dev/null +++ b/app/src/main/java/chat/revolt/components/chat/UserBadgeList.kt @@ -0,0 +1,184 @@ +package chat.revolt.components.chat + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import chat.revolt.R +import chat.revolt.api.schemas.UserBadges +import chat.revolt.api.schemas.has + +@Composable +fun BadgeListEntryTemplate( + label: String, + icon: Painter +) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Image( + painter = icon, + contentDescription = null, + modifier = Modifier + .size(24.dp) + ) + + Spacer(Modifier.width(8.dp)) + + Text( + text = label + ) + } +} + +@Composable +fun BadgeListEntry(badge: UserBadges) { + when (badge.value) { + UserBadges.Developer.value -> { + BadgeListEntryTemplate( + label = stringResource(R.string.user_badge_developer), + icon = painterResource(R.drawable.user_badge_developer) + ) + } + + UserBadges.Translator.value -> { + BadgeListEntryTemplate( + label = stringResource(R.string.user_badge_translator), + icon = painterResource(R.drawable.user_badge_translator) + ) + } + + UserBadges.Supporter.value -> { + BadgeListEntryTemplate( + label = stringResource(R.string.user_badge_supporter), + icon = painterResource(R.drawable.user_badge_supporter) + ) + } + + UserBadges.ResponsibleDisclosure.value -> { + BadgeListEntryTemplate( + label = stringResource(R.string.user_badge_responsible_disclosure), + icon = painterResource(R.drawable.user_badge_disclosure) + ) + } + + UserBadges.Founder.value -> { + BadgeListEntryTemplate( + label = stringResource(R.string.user_badge_founder), + icon = painterResource(R.drawable.user_badge_founder) + ) + } + + UserBadges.PlatformModeration.value -> { + BadgeListEntryTemplate( + label = stringResource(R.string.user_badge_platform_moderation), + icon = painterResource(R.drawable.user_badge_moderation) + ) + } + + UserBadges.ActiveSupporter.value -> { + BadgeListEntryTemplate( + label = stringResource(R.string.user_badge_active_supporter), + icon = painterResource(R.drawable.ic_human_greeting_variant_24dp) + ) + } + + UserBadges.Paw.value -> { + BadgeListEntryTemplate( + label = stringResource(R.string.user_badge_paw), + icon = painterResource(R.drawable.user_badge_paw) + ) + } + + UserBadges.EarlyAdopter.value -> { + BadgeListEntryTemplate( + label = stringResource(R.string.user_badge_early_adopter), + icon = painterResource(R.drawable.user_badge_early_adopter) + ) + } + + UserBadges.ReservedRelevantJokeBadge1.value -> { + BadgeListEntryTemplate( + label = stringResource(R.string.user_badge_reserved_relevant_joke_badge_1), + icon = painterResource(R.drawable.user_badge_reserved_relevant_one) + ) + } + + UserBadges.ReservedRelevantJokeBadge2.value -> { + BadgeListEntryTemplate( + label = stringResource(R.string.user_badge_reserved_relevant_joke_badge_2), + icon = painterResource(R.drawable.user_badge_reserved_relevant_two) + ) + } + } +} + +@Composable +fun UserBadgeList(badges: Long) { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + UserBadges.entries + .filter { badges has it } + .forEach { badge -> + BadgeListEntry(badge) + } + } +} + +@OptIn(ExperimentalLayoutApi::class) +@Composable +fun UserBadgeRow(badges: Long) { + FlowRow( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + UserBadges.entries + .filter { badges has it } + .forEach { badge -> + Image( + painter = when (badge) { + UserBadges.Developer -> painterResource(R.drawable.user_badge_developer) + UserBadges.Translator -> painterResource(R.drawable.user_badge_translator) + UserBadges.Supporter -> painterResource(R.drawable.user_badge_supporter) + UserBadges.ResponsibleDisclosure -> painterResource(R.drawable.user_badge_disclosure) + UserBadges.Founder -> painterResource(R.drawable.user_badge_founder) + UserBadges.PlatformModeration -> painterResource(R.drawable.user_badge_moderation) + UserBadges.ActiveSupporter -> painterResource(R.drawable.ic_human_greeting_variant_24dp) + UserBadges.Paw -> painterResource(R.drawable.user_badge_paw) + UserBadges.EarlyAdopter -> painterResource(R.drawable.user_badge_early_adopter) + UserBadges.ReservedRelevantJokeBadge1 -> painterResource(R.drawable.user_badge_reserved_relevant_one) + UserBadges.ReservedRelevantJokeBadge2 -> painterResource(R.drawable.user_badge_reserved_relevant_two) + }, + contentDescription = when (badge) { + UserBadges.Developer -> stringResource(R.string.user_badge_developer) + UserBadges.Translator -> stringResource(R.string.user_badge_translator) + UserBadges.Supporter -> stringResource(R.string.user_badge_supporter) + UserBadges.ResponsibleDisclosure -> stringResource(R.string.user_badge_responsible_disclosure) + UserBadges.Founder -> stringResource(R.string.user_badge_founder) + UserBadges.PlatformModeration -> stringResource(R.string.user_badge_platform_moderation) + UserBadges.ActiveSupporter -> stringResource(R.string.user_badge_active_supporter) + UserBadges.Paw -> stringResource(R.string.user_badge_paw) + UserBadges.EarlyAdopter -> stringResource(R.string.user_badge_early_adopter) + UserBadges.ReservedRelevantJokeBadge1 -> stringResource(R.string.user_badge_reserved_relevant_joke_badge_1) + UserBadges.ReservedRelevantJokeBadge2 -> stringResource(R.string.user_badge_reserved_relevant_joke_badge_2) + }, + modifier = Modifier + .size(32.dp) + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/components/screens/settings/UserButtons.kt b/app/src/main/java/chat/revolt/components/screens/settings/UserButtons.kt index 3bb28df2..30ae6620 100644 --- a/app/src/main/java/chat/revolt/components/screens/settings/UserButtons.kt +++ b/app/src/main/java/chat/revolt/components/screens/settings/UserButtons.kt @@ -11,7 +11,7 @@ import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.ElevatedButton +import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -100,7 +100,7 @@ fun UserButtons( } "Friend" -> { - ElevatedButton( + FilledTonalButton( onClick = { scope.launch { val dm = openDM(user.id) diff --git a/app/src/main/java/chat/revolt/components/sheets/SheetTile.kt b/app/src/main/java/chat/revolt/components/sheets/SheetTile.kt new file mode 100644 index 00000000..7f47910e --- /dev/null +++ b/app/src/main/java/chat/revolt/components/sheets/SheetTile.kt @@ -0,0 +1,85 @@ +package chat.revolt.components.sheets + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.surfaceColorAtElevation +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import chat.revolt.api.internals.solidColor + +@Composable +fun SheetTile( + modifier: Modifier = Modifier, + header: @Composable () -> Unit, + contentPreview: @Composable () -> Unit, + clickable: Boolean = true, + backgroundBrush: Brush = Brush.solidColor(MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp)), + content: @Composable () -> Unit, +) { + var isExpanded by remember { mutableStateOf(false) } + + if (isExpanded) { + Dialog(onDismissRequest = { + isExpanded = false + }) { + BoxWithConstraints { + Column( + modifier = Modifier + .clip(MaterialTheme.shapes.large) + .background(MaterialTheme.colorScheme.surface) + .padding(24.dp) + .width(maxWidth * 0.85f) + .heightIn(max = maxHeight * 0.85f) + ) { + CompositionLocalProvider(LocalTextStyle provides MaterialTheme.typography.headlineMedium) { + header() + } + + Spacer(Modifier.height(16.dp)) + + CompositionLocalProvider(LocalTextStyle provides MaterialTheme.typography.bodyMedium) { + content() + } + } + } + } + } + + Column( + modifier = Modifier + .clip(MaterialTheme.shapes.large) + .then(if (clickable) Modifier.clickable { isExpanded = true } else Modifier) + .background(backgroundBrush) + .padding(16.dp) + .height(128.dp) + .then(modifier) + ) { + CompositionLocalProvider(LocalTextStyle provides MaterialTheme.typography.headlineSmall) { + header() + } + + Spacer(Modifier.height(16.dp)) + + CompositionLocalProvider(LocalTextStyle provides MaterialTheme.typography.bodyMedium) { + contentPreview() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/sheets/UserInfoSheet.kt b/app/src/main/java/chat/revolt/sheets/UserInfoSheet.kt index a6e568e8..e845c921 100644 --- a/app/src/main/java/chat/revolt/sheets/UserInfoSheet.kt +++ b/app/src/main/java/chat/revolt/sheets/UserInfoSheet.kt @@ -1,16 +1,15 @@ package chat.revolt.sheets +import android.text.format.DateUtils import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ExperimentalLayoutApi -import androidx.compose.foundation.layout.FlowRow -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid +import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells +import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan import androidx.compose.material3.Icon import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme @@ -21,25 +20,30 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Brush import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import chat.revolt.R import chat.revolt.api.RevoltAPI +import chat.revolt.api.internals.ULID import chat.revolt.api.internals.WebCompat import chat.revolt.api.internals.solidColor import chat.revolt.api.routes.user.fetchUserProfile import chat.revolt.api.schemas.Profile -import chat.revolt.components.chat.RoleChip +import chat.revolt.components.chat.RoleListEntry +import chat.revolt.components.chat.UserBadgeList +import chat.revolt.components.chat.UserBadgeRow import chat.revolt.components.generic.NonIdealState import chat.revolt.components.generic.WebMarkdown import chat.revolt.components.screens.settings.RawUserOverview import chat.revolt.components.screens.settings.UserButtons +import chat.revolt.components.sheets.SheetTile +import kotlinx.datetime.Instant -@OptIn(ExperimentalLayoutApi::class) @Composable fun UserInfoSheet( userId: String, @@ -90,72 +94,174 @@ fun UserInfoSheet( return } - Column( - modifier = Modifier - .verticalScroll(rememberScrollState()) + LazyVerticalStaggeredGrid( + columns = StaggeredGridCells.Fixed(2), + horizontalArrangement = Arrangement.spacedBy(16.dp), + verticalItemSpacing = 16.dp, + modifier = Modifier.padding(16.dp) ) { - RawUserOverview(user, profile) + item(key = "overview", span = StaggeredGridItemSpan.FullLine) { + RawUserOverview(user, profile, internalPadding = false) + } - Column( - modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp, top = 8.dp) - ) { - UserButtons( - user, - dismissSheet - ) - - member?.roles?.let { - Text( - text = stringResource(id = R.string.user_info_sheet_category_roles), - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(vertical = 10.dp) - ) - - FlowRow( - horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { - it - .map { roleId -> server?.roles?.get(roleId) } - .sortedBy { it?.rank ?: 0.0 } - .forEach { role -> - role?.let { - RoleChip( - label = role.name ?: "null", - brush = role.colour?.let { WebCompat.parseColour(it) } - ?: Brush.solidColor(LocalContentColor.current) - ) - } + member?.roles?.let { + item(key = "roles") { + SheetTile( + header = { + Text(stringResource(R.string.user_info_sheet_category_roles)) + }, + contentPreview = { + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + it + .map { roleId -> server?.roles?.get(roleId) } + .sortedBy { it?.rank ?: 0.0 } + .take(3) + .forEach { role -> + role?.let { + RoleListEntry( + label = role.name ?: "null", + brush = role.colour?.let { WebCompat.parseColour(it) } + ?: Brush.solidColor(LocalContentColor.current) + ) + } + } } - } - } - - Text( - text = stringResource(id = R.string.user_info_sheet_category_bio), - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(vertical = 10.dp) - ) - - if (profile?.content.isNullOrBlank().not()) { - WebMarkdown( - text = profile!!.content!!, - maskLoading = true - ) - } else if (profile != null) { - Text( - text = stringResource(id = R.string.user_info_sheet_bio_empty), - color = LocalContentColor.current.copy(alpha = 0.6f) - ) - } else if (profileNotFound) { - Text( - text = stringResource(id = R.string.user_info_sheet_bio_not_found), - color = LocalContentColor.current.copy(alpha = 0.6f) - ) - } else { - Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxWidth()) { - CircularProgressIndicator() + } + ) { + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + it + .map { roleId -> server?.roles?.get(roleId) } + .sortedBy { it?.rank ?: 0.0 } + .forEach { role -> + role?.let { + RoleListEntry( + label = role.name ?: "null", + brush = role.colour?.let { WebCompat.parseColour(it) } + ?: Brush.solidColor(LocalContentColor.current) + ) + } + } + } } } } + val accountAt = user.id?.let { + DateUtils.getRelativeTimeSpanString( + ULID.asTimestamp(user.id), + System.currentTimeMillis(), + DateUtils.MINUTE_IN_MILLIS + ).toString() + } + val joinedAt = member?.joinedAt?.let { + DateUtils.getRelativeTimeSpanString( + Instant.parse(member.joinedAt).toEpochMilliseconds(), + System.currentTimeMillis(), + DateUtils.MINUTE_IN_MILLIS + ).toString() + } + + item(key = "joined") { + SheetTile( + header = { + Text(stringResource(R.string.user_info_sheet_category_joined)) + }, + contentPreview = { + if (joinedAt != null && server?.name != null) { + Text( + text = joinedAt, + fontSize = 14.sp + ) + + Text( + text = server.name, + style = MaterialTheme.typography.labelMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + + Spacer(Modifier.height(8.dp)) + } + + accountAt?.let { _ -> + Text( + text = accountAt, + fontSize = 14.sp + ) + + Text( + text = stringResource(id = R.string.user_info_sheet_category_joined_revolt), + style = MaterialTheme.typography.labelMedium + ) + } + } + ) { + if (joinedAt != null && server?.name != null) { + Text( + text = joinedAt, + style = MaterialTheme.typography.displaySmall + ) + + Text( + text = server.name, + style = MaterialTheme.typography.labelMedium + ) + + Spacer(Modifier.height(8.dp)) + } + + accountAt?.let { _ -> + Text( + text = accountAt, + style = MaterialTheme.typography.displaySmall + ) + + Text( + text = stringResource(id = R.string.user_info_sheet_category_joined_revolt), + style = MaterialTheme.typography.labelMedium + ) + } + } + } + + if ((user.badges ?: 0) > 0) { + item(key = "info") { + SheetTile( + header = { + Text(stringResource(R.string.user_info_sheet_category_badges)) + }, + contentPreview = { + user.badges?.let { UserBadgeRow(badges = it) } + } + ) { + user.badges?.let { UserBadgeList(badges = it) } + } + } + } + + if (profile?.content.isNullOrBlank().not()) { + item(key = "bio", span = StaggeredGridItemSpan.FullLine) { + SheetTile( + header = { + Text(stringResource(R.string.user_info_sheet_category_bio)) + }, + contentPreview = { + Text( + text = profile?.content!!, + maxLines = 4, + overflow = TextOverflow.Ellipsis + ) + } + ) { + WebMarkdown( + text = profile?.content!!, + maskLoading = true + ) + } + } + } + + item(key = "actions", span = StaggeredGridItemSpan.FullLine) { + UserButtons(user, dismissSheet) + } } -} +} \ No newline at end of file diff --git a/app/src/main/res/drawable/user_badge_developer.xml b/app/src/main/res/drawable/user_badge_developer.xml new file mode 100644 index 00000000..e78a8601 --- /dev/null +++ b/app/src/main/res/drawable/user_badge_developer.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/user_badge_disclosure.xml b/app/src/main/res/drawable/user_badge_disclosure.xml new file mode 100644 index 00000000..720b5934 --- /dev/null +++ b/app/src/main/res/drawable/user_badge_disclosure.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/app/src/main/res/drawable/user_badge_early_adopter.xml b/app/src/main/res/drawable/user_badge_early_adopter.xml new file mode 100644 index 00000000..cca7f8c9 --- /dev/null +++ b/app/src/main/res/drawable/user_badge_early_adopter.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/user_badge_founder.xml b/app/src/main/res/drawable/user_badge_founder.xml new file mode 100644 index 00000000..e5965c51 --- /dev/null +++ b/app/src/main/res/drawable/user_badge_founder.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/user_badge_moderation.xml b/app/src/main/res/drawable/user_badge_moderation.xml new file mode 100644 index 00000000..e3215f66 --- /dev/null +++ b/app/src/main/res/drawable/user_badge_moderation.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/user_badge_paw.xml b/app/src/main/res/drawable/user_badge_paw.xml new file mode 100644 index 00000000..625a5538 --- /dev/null +++ b/app/src/main/res/drawable/user_badge_paw.xml @@ -0,0 +1,25 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/user_badge_raccoon.xml b/app/src/main/res/drawable/user_badge_raccoon.xml new file mode 100644 index 00000000..148c4849 --- /dev/null +++ b/app/src/main/res/drawable/user_badge_raccoon.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/user_badge_reserved_relevant_one.xml b/app/src/main/res/drawable/user_badge_reserved_relevant_one.xml new file mode 100644 index 00000000..408a14a8 --- /dev/null +++ b/app/src/main/res/drawable/user_badge_reserved_relevant_one.xml @@ -0,0 +1,33 @@ + + + + + + + diff --git a/app/src/main/res/drawable/user_badge_reserved_relevant_two.xml b/app/src/main/res/drawable/user_badge_reserved_relevant_two.xml new file mode 100644 index 00000000..1f8620be --- /dev/null +++ b/app/src/main/res/drawable/user_badge_reserved_relevant_two.xml @@ -0,0 +1,2601 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/user_badge_supporter.xml b/app/src/main/res/drawable/user_badge_supporter.xml new file mode 100644 index 00000000..4c6c2af6 --- /dev/null +++ b/app/src/main/res/drawable/user_badge_supporter.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/user_badge_translator.xml b/app/src/main/res/drawable/user_badge_translator.xml new file mode 100644 index 00000000..2f0956cb --- /dev/null +++ b/app/src/main/res/drawable/user_badge_translator.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 09e9386e..c861b9db 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -277,6 +277,10 @@ This user hasn\'t set a bio yet. This user\'s bio could not be fetched. Please verify you share a server or are friends. Roles + Joined + Revolt + Badges + Bot Add Friend Send Message Remove Friend @@ -290,6 +294,17 @@ Copy ID Could not open DM with this user. + Developer + Translator + Supporter + Responsible Disclosure + Founder + Platform Moderation + Active Supporter + 🦊 + Early Adopter + sus + It\'s Morbin Time Add a server Join by invite code or link