diff --git a/app/src/main/java/chat/revolt/api/internals/SpecialUsers.kt b/app/src/main/java/chat/revolt/api/internals/SpecialUsers.kt index 33bca118..1bf0d23c 100644 --- a/app/src/main/java/chat/revolt/api/internals/SpecialUsers.kt +++ b/app/src/main/java/chat/revolt/api/internals/SpecialUsers.kt @@ -1,10 +1,96 @@ package chat.revolt.api.internals +import android.content.Context +import android.graphics.RuntimeShader +import android.os.Build +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ShaderBrush +import org.intellij.lang.annotations.Language +import androidx.compose.ui.graphics.Brush as AndroidBrush + object SpecialUsers { val PLATFORM_MODERATION_USER = "01FC17E1WTM2BGE4F3ARN3FDAF" - + val TRUSTED_MODERATION_BOTS = listOf( "01GXBYCNQ52A9QYCQ99RBPXPAW", // AutoMod "01FCXRNNVW69AMSHBE61W1M5T3", // AutoMod Nightly ) + + sealed class TeamMemberFlair { + data class Brush(val brush: AndroidBrush) : TeamMemberFlair() + data class AGSLShader(val shader: String, val fallback: AndroidBrush) : + TeamMemberFlair() + } + + @Language("AGSL") + private val INSERT_SHADER = """ + uniform float value; + + half4 main(in float2 fragCoord) { + return half4(fragCoord[0] / 1000.0, fragCoord[1] / 1000.0, sin(value), 1.0); + } + """.trimIndent() + + val TEAM_MEMBER_FLAIRS = mapOf( + "01F1WKM5TK2V6KCZWR6DGBJDTZ" to TeamMemberFlair.Brush( + AndroidBrush.linearGradient( + listOf( + Color(0xFFD62900), + Color(0xFFFF9B55), + Color(0xFFFFFFFF), + Color(0xFFD461A6), + Color(0xFFA50062), + ), + start = Offset.Zero, + end = Offset.Infinite + ) + ), // jen + "01EX2NCWQ0CHS3QJF0FEQS1GR4" to TeamMemberFlair.AGSLShader( + INSERT_SHADER, + AndroidBrush.linearGradient( + listOf( + Color(0xFF68224F), + Color(0xFFC68235), + ), + start = Offset.Zero, + end = Offset.Infinite + ) + ), // insert + "01EXAF3KX65608AJ4NG27YG1HM" to TeamMemberFlair.Brush( + AndroidBrush.solidColor(Color(0xFFFFC1F1)) + ), // lea + "01FEEFJCKY5C4DMMJYZ20ACWWC" to TeamMemberFlair.Brush( + AndroidBrush.linearGradient( + listOf( + Color(0xFF0BA39F), + Color(0xFFCD1414) + ) + ) + ), // sophie + "01FD58YK5W7QRV5H3D64KTQYX3" to TeamMemberFlair.Brush( + AndroidBrush.verticalGradient( + listOf( + Color(0xFF980000), + Color(0xFF1000AF) + ) + ) + ), // zomatree + ) + + fun teamFlairAsBrush(context: Context, id: String): AndroidBrush? { + return when (val flair = TEAM_MEMBER_FLAIRS[id]) { + is TeamMemberFlair.Brush -> flair.brush + + is TeamMemberFlair.AGSLShader -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + val shader = RuntimeShader(flair.shader) + shader.setFloatUniform("value", (0..1000).random().toFloat()) + ShaderBrush(shader) + } else { + flair.fallback + } + + null -> null + } + } } \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/components/screens/settings/UserOverview.kt b/app/src/main/java/chat/revolt/components/screens/settings/UserOverview.kt index 8bd656aa..070f1496 100644 --- a/app/src/main/java/chat/revolt/components/screens/settings/UserOverview.kt +++ b/app/src/main/java/chat/revolt/components/screens/settings/UserOverview.kt @@ -1,6 +1,7 @@ package chat.revolt.components.screens.settings import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -9,6 +10,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -18,9 +20,11 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue 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.graphics.Color import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.SpanStyle @@ -29,13 +33,17 @@ import androidx.compose.ui.unit.dp import chat.revolt.R import chat.revolt.api.REVOLT_FILES import chat.revolt.api.RevoltAPI +import chat.revolt.api.internals.SpecialUsers import chat.revolt.api.internals.ULID +import chat.revolt.api.internals.solidColor import chat.revolt.api.routes.user.fetchUserProfile import chat.revolt.api.schemas.Profile import chat.revolt.api.schemas.User import chat.revolt.components.generic.RemoteImage import chat.revolt.components.generic.UserAvatar import chat.revolt.components.generic.presenceFromStatus +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking @Composable fun SelfUserOverview() { @@ -63,8 +71,36 @@ fun UserOverview(user: User) { @Composable fun RawUserOverview(user: User, profile: Profile? = null) { + val context = LocalContext.current + var teamMemberFlair by remember { mutableStateOf(null) } + + LaunchedEffect(user) { + runBlocking(Dispatchers.IO) { + user.id?.let { + teamMemberFlair = SpecialUsers.teamFlairAsBrush( + context, + it + ) + } + } + } + Box( contentAlignment = Alignment.BottomStart, + modifier = Modifier + .padding(horizontal = 16.dp) + .clip(MaterialTheme.shapes.large) + .then( + if (user.id in SpecialUsers.TEAM_MEMBER_FLAIRS.keys) { + Modifier + .border( + width = 4.dp, + brush = teamMemberFlair + ?: Brush.solidColor(Color.Transparent), + shape = MaterialTheme.shapes.large, + ) + } else Modifier + ) ) { profile?.background?.let { background -> RemoteImage(