feat: role colours in chat

Signed-off-by: Infi <wingit@geist.ga>
This commit is contained in:
Infi 2023-08-21 04:34:14 +05:00
parent dace8da8a6
commit 813b29fecd
4 changed files with 88 additions and 28 deletions

View File

@ -0,0 +1,36 @@
package chat.revolt.api.internals
import chat.revolt.api.RevoltAPI
import chat.revolt.api.schemas.Role
object Roles {
// lowest rank = highest role
private fun resolveHighestRole(roles: List<Role?>): Role? {
return roles.minByOrNull { role ->
role?.rank ?: 0.0
}
}
private fun highestRoleWithColour(roles: List<Role?>): Role? {
return roles.filter { role ->
role?.colour != null
}.minByOrNull { role ->
role?.rank ?: 0.0
}
}
fun resolveHighestRole(serverId: String, userId: String, withColour: Boolean = false): Role? {
val server = RevoltAPI.serverCache[serverId] ?: return null
val member = RevoltAPI.members.getMember(serverId, userId) ?: return null
val roles = member.roles?.map { roleId ->
server.roles?.get(roleId)
} ?: return null
return if (withColour) {
highestRoleWithColour(roles)
} else {
resolveHighestRole(roles)
}
}
}

View File

@ -16,6 +16,19 @@ fun Brush.Companion.solidColor(colour: Color) = linearGradient(
)
)
// 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
// for the sake of all of us, please just use hex codes
// reference: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
private val ADDITIONAL_WEB_COLOURS = mapOf(
"orange" to Color(0xFFFFA500),
"rebeccapurple" to Color(0xFF663399),
"transparent" to Color.Transparent,
"inherit" to Color.Unspecified,
"initial" to Color.Unspecified,
"unset" to Color.Unspecified,
)
object WebCompat {
@Composable
private fun parseLinearGradient(gradient: String): Brush {
@ -59,15 +72,7 @@ object WebCompat {
)
}
else -> parseFunctionColour(colourPart) ?: try {
Color(android.graphics.Color.parseColor(colourPart))
} catch (e: IllegalArgumentException) {
Log.d(
"WebCompat",
"Failed to parse colour $colourPart in $gradient, falling back to LocalContentColor.current"
)
LocalContentColor.current
}
else -> parseFunctionColour(colourPart) ?: parseColourName(colourPart)
}
val stop = if (splitPart.size == 2) {
@ -149,6 +154,28 @@ object WebCompat {
return Brush.solidColor(parseVarToColour(varName))
}
@Composable
private fun parseColourName(colour: String): Color {
return try {
val additionalWebColour = ADDITIONAL_WEB_COLOURS[colour]
if (additionalWebColour != null) {
Log.d(
"WebCompat",
"Parsed additional web colour $colour to $additionalWebColour"
)
return additionalWebColour
}
Color(android.graphics.Color.parseColor(colour))
} catch (e: IllegalArgumentException) {
Log.d(
"WebCompat",
"Failed to parse colour $colour, falling back to LocalContentColor.current"
)
LocalContentColor.current
}
}
@Composable
fun parseColour(colour: String): Brush {
when {
@ -172,15 +199,7 @@ object WebCompat {
}
else -> {
return try {
Brush.solidColor(Color(android.graphics.Color.parseColor(colour)))
} catch (e: IllegalArgumentException) {
Log.d(
"WebCompat",
"Failed to parse colour $colour, falling back to LocalContentColor.current"
)
Brush.solidColor(LocalContentColor.current)
}
return Brush.solidColor(parseColourName(colour))
}
}
}

View File

@ -44,7 +44,8 @@ fun InReplyTo(
?: stringResource(id = R.string.unknown)
val contentColor = LocalContentColor.current
val usernameColor = message?.let { authorColour(it) } ?: Brush.solidColor(contentColor)
val usernameColor =
message?.let { authorColour(it) } ?: Brush.solidColor(contentColor)
Box(
modifier = modifier

View File

@ -35,7 +35,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
@ -50,6 +49,7 @@ import chat.revolt.activities.media.ImageViewActivity
import chat.revolt.activities.media.VideoViewActivity
import chat.revolt.api.REVOLT_FILES
import chat.revolt.api.RevoltAPI
import chat.revolt.api.internals.Roles
import chat.revolt.api.internals.SpecialUsers
import chat.revolt.api.internals.ULID
import chat.revolt.api.internals.WebCompat
@ -66,7 +66,16 @@ fun authorColour(message: MessageSchema): Brush {
return if (message.masquerade?.colour != null) {
WebCompat.parseColour(message.masquerade.colour)
} else {
Brush.solidColor(LocalContentColor.current)
val defaultColour = Brush.solidColor(LocalContentColor.current)
val serverId = RevoltAPI.channelCache[message.channel]?.server ?: return defaultColour
val highestRole = message.author?.let {
Roles.resolveHighestRole(serverId, it, withColour = true)
} ?: return defaultColour
highestRole.colour?.let { WebCompat.parseColour(it) }
?: defaultColour
}
}
@ -171,7 +180,7 @@ fun Message(
replyMessage.author
)
}
?: false,
?: false
) {
// TODO Add jump to message
if (replyMessage == null) {
@ -218,12 +227,7 @@ fun Message(
text = authorName(message),
style = LocalTextStyle.current.copy(
fontWeight = FontWeight.Bold,
brush = if (message.author == RevoltAPI.selfId) Brush.horizontalGradient(
listOf(
Color.Magenta,
Color.Cyan,
),
) else authorColour(message),
brush = authorColour(message),
),
maxLines = 1,
overflow = TextOverflow.Ellipsis