feat: masquerade support
This commit is contained in:
parent
fcddb8a968
commit
9017fc52a5
|
|
@ -23,8 +23,13 @@ const val REVOLT_BASE = "https://api.revolt.chat"
|
||||||
const val REVOLT_SUPPORT = "https://support.revolt.chat"
|
const val REVOLT_SUPPORT = "https://support.revolt.chat"
|
||||||
const val REVOLT_MARKETING = "https://revolt.chat"
|
const val REVOLT_MARKETING = "https://revolt.chat"
|
||||||
const val REVOLT_FILES = "https://autumn.revolt.chat"
|
const val REVOLT_FILES = "https://autumn.revolt.chat"
|
||||||
|
const val REVOLT_JANUARY = "https://jan.revolt.chat"
|
||||||
const val REVOLT_WEBSOCKET = "wss://ws.revolt.chat"
|
const val REVOLT_WEBSOCKET = "wss://ws.revolt.chat"
|
||||||
|
|
||||||
|
fun asJanuaryProxyUrl(url: String): String {
|
||||||
|
return "$REVOLT_JANUARY/proxy?url=${url}"
|
||||||
|
}
|
||||||
|
|
||||||
private const val BACKEND_IS_STABLE = false
|
private const val BACKEND_IS_STABLE = false
|
||||||
|
|
||||||
val RevoltJson = Json { ignoreUnknownKeys = true }
|
val RevoltJson = Json { ignoreUnknownKeys = true }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package chat.revolt.api.internals
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.compose.material3.LocalContentColor
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
|
object WebCompat {
|
||||||
|
@Composable
|
||||||
|
fun parseColour(colour: String): Color {
|
||||||
|
if (colour.startsWith("var(")) {
|
||||||
|
return when (colour.substringAfter("var(").substringBefore(")")) {
|
||||||
|
"accent" -> MaterialTheme.colorScheme.primary
|
||||||
|
"foreground" -> MaterialTheme.colorScheme.onBackground
|
||||||
|
"background" -> MaterialTheme.colorScheme.background
|
||||||
|
"error" -> MaterialTheme.colorScheme.error
|
||||||
|
else -> LocalContentColor.current
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return Color(android.graphics.Color.parseColor(colour))
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
Log.d(
|
||||||
|
"WebCompat",
|
||||||
|
"Failed to parse colour $colour, falling back to LocalContentColor.current"
|
||||||
|
)
|
||||||
|
return LocalContentColor.current
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,7 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import chat.revolt.R
|
import chat.revolt.R
|
||||||
import chat.revolt.api.RevoltAPI
|
import chat.revolt.api.RevoltAPI
|
||||||
|
import chat.revolt.api.asJanuaryProxyUrl
|
||||||
import chat.revolt.components.generic.UserAvatar
|
import chat.revolt.components.generic.UserAvatar
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -27,6 +28,8 @@ fun InReplyTo(
|
||||||
val message = RevoltAPI.messageCache[messageId]
|
val message = RevoltAPI.messageCache[messageId]
|
||||||
val author = RevoltAPI.userCache[message?.author ?: ""]
|
val author = RevoltAPI.userCache[message?.author ?: ""]
|
||||||
|
|
||||||
|
val username = message?.masquerade?.name ?: author?.username ?: ""
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|
@ -38,22 +41,23 @@ fun InReplyTo(
|
||||||
|
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
UserAvatar(
|
UserAvatar(
|
||||||
username = author?.username ?: "",
|
username = username,
|
||||||
userId = author?.id ?: "",
|
userId = author?.id ?: "",
|
||||||
avatar = author?.avatar,
|
avatar = author?.avatar,
|
||||||
|
rawUrl = message.masquerade?.avatar?.let { asJanuaryProxyUrl(it) },
|
||||||
size = 16.dp
|
size = 16.dp
|
||||||
)
|
)
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = if (author != null) {
|
text = if (author != null) {
|
||||||
if (withMention) {
|
if (withMention) {
|
||||||
"@${author.username}"
|
"@$username"
|
||||||
} else {
|
} else {
|
||||||
author.username
|
username
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
stringResource(id = R.string.unknown)
|
stringResource(id = R.string.unknown)
|
||||||
} ?: stringResource(id = R.string.unknown),
|
},
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.9f),
|
color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.9f),
|
||||||
|
|
@ -62,6 +66,15 @@ fun InReplyTo(
|
||||||
modifier = Modifier.padding(horizontal = 4.dp)
|
modifier = Modifier.padding(horizontal = 4.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (message.masquerade != null && author?.bot != null) {
|
||||||
|
InlineBadge(
|
||||||
|
badge = InlineBadge.Masquerade,
|
||||||
|
colour = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.5f),
|
||||||
|
modifier = Modifier.size(8.dp)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
|
}
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = message.content ?: "",
|
text = message.content ?: "",
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
package chat.revolt.components.chat
|
||||||
|
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import chat.revolt.R
|
||||||
|
|
||||||
|
enum class InlineBadge {
|
||||||
|
Bot,
|
||||||
|
Masquerade,
|
||||||
|
PlatformModeration,
|
||||||
|
Developer
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun InlineBadge(
|
||||||
|
badge: InlineBadge,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
colour: Color = Color.Unspecified,
|
||||||
|
) {
|
||||||
|
when (badge) {
|
||||||
|
InlineBadge.Bot -> Icon(
|
||||||
|
painter = painterResource(id = R.drawable.ic_robot_24dp),
|
||||||
|
contentDescription = stringResource(id = R.string.badge_bot_alt),
|
||||||
|
tint = colour,
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
InlineBadge.Masquerade -> Icon(
|
||||||
|
painter = painterResource(id = R.drawable.ic_link_variant_24dp),
|
||||||
|
contentDescription = stringResource(id = R.string.badge_masquerade_alt),
|
||||||
|
tint = colour,
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
InlineBadge.PlatformModeration -> TODO()
|
||||||
|
InlineBadge.Developer -> TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,14 +3,9 @@ package chat.revolt.components.chat
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.browser.customtabs.CustomTabsIntent
|
import androidx.browser.customtabs.CustomTabsIntent
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.*
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.combinedClickable
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -26,7 +21,9 @@ import androidx.compose.ui.unit.sp
|
||||||
import chat.revolt.R
|
import chat.revolt.R
|
||||||
import chat.revolt.api.REVOLT_FILES
|
import chat.revolt.api.REVOLT_FILES
|
||||||
import chat.revolt.api.RevoltAPI
|
import chat.revolt.api.RevoltAPI
|
||||||
|
import chat.revolt.api.asJanuaryProxyUrl
|
||||||
import chat.revolt.api.internals.ULID
|
import chat.revolt.api.internals.ULID
|
||||||
|
import chat.revolt.api.internals.WebCompat
|
||||||
import chat.revolt.api.schemas.AutumnResource
|
import chat.revolt.api.schemas.AutumnResource
|
||||||
import chat.revolt.components.generic.RemoteImage
|
import chat.revolt.components.generic.RemoteImage
|
||||||
import chat.revolt.components.generic.UserAvatar
|
import chat.revolt.components.generic.UserAvatar
|
||||||
|
|
@ -50,12 +47,6 @@ fun formatLongAsTime(time: Long): String {
|
||||||
val format =
|
val format =
|
||||||
java.text.SimpleDateFormat("dd.MM.yyyy HH:mm:ss", java.util.Locale.getDefault())
|
java.text.SimpleDateFormat("dd.MM.yyyy HH:mm:ss", java.util.Locale.getDefault())
|
||||||
|
|
||||||
// EQUIVALENT CODE WITH kotlinx.datetime:
|
|
||||||
|
|
||||||
// val date = Instant.fromEpochMilliseconds(time)
|
|
||||||
// val format = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss")
|
|
||||||
|
|
||||||
|
|
||||||
return format.format(date)
|
return format.format(date)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,7 +100,8 @@ fun Message(
|
||||||
UserAvatar(
|
UserAvatar(
|
||||||
username = author.username ?: "",
|
username = author.username ?: "",
|
||||||
userId = author.id!!,
|
userId = author.id!!,
|
||||||
avatar = author.avatar
|
avatar = author.avatar,
|
||||||
|
rawUrl = message.masquerade?.avatar?.let { asJanuaryProxyUrl(it) }
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
UserAvatarWidthPlaceholder()
|
UserAvatarWidthPlaceholder()
|
||||||
|
|
@ -119,12 +111,25 @@ fun Message(
|
||||||
if (message.tail == false) {
|
if (message.tail == false) {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Text(
|
Text(
|
||||||
text = author.username ?: "",
|
text = message.masquerade?.name ?: author.username ?: "",
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = if (message.masquerade?.colour != null) {
|
||||||
|
WebCompat.parseColour(message.masquerade.colour)
|
||||||
|
} else LocalContentColor.current,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (message.masquerade != null && author.bot != null) {
|
||||||
|
Spacer(modifier = Modifier.width(5.dp))
|
||||||
|
|
||||||
|
InlineBadge(
|
||||||
|
badge = InlineBadge.Masquerade,
|
||||||
|
colour = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.5f),
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(5.dp))
|
Spacer(modifier = Modifier.width(5.dp))
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ fun UserAvatar(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
presence: Presence? = null,
|
presence: Presence? = null,
|
||||||
avatar: AutumnResource? = null,
|
avatar: AutumnResource? = null,
|
||||||
|
rawUrl: String? = null,
|
||||||
size: Dp = 40.dp,
|
size: Dp = 40.dp,
|
||||||
presenceSize: Dp = 16.dp,
|
presenceSize: Dp = 16.dp,
|
||||||
) {
|
) {
|
||||||
|
|
@ -67,7 +68,7 @@ fun UserAvatar(
|
||||||
) {
|
) {
|
||||||
if (avatar != null) {
|
if (avatar != null) {
|
||||||
RemoteImage(
|
RemoteImage(
|
||||||
url = "$REVOLT_FILES/avatars/${avatar.id!!}/user.png",
|
url = rawUrl ?: "$REVOLT_FILES/avatars/${avatar.id!!}/user.png",
|
||||||
description = stringResource(id = R.string.avatar_alt, username),
|
description = stringResource(id = R.string.avatar_alt, username),
|
||||||
contentScale = ContentScale.Crop,
|
contentScale = ContentScale.Crop,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:width="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M10.59,13.41C11,13.8 11,14.44 10.59,14.83C10.2,15.22 9.56,15.22 9.17,14.83C7.22,12.88 7.22,9.71 9.17,7.76V7.76L12.71,4.22C14.66,2.27 17.83,2.27 19.78,4.22C21.73,6.17 21.73,9.34 19.78,11.29L18.29,12.78C18.3,11.96 18.17,11.14 17.89,10.36L18.36,9.88C19.54,8.71 19.54,6.81 18.36,5.64C17.19,4.46 15.29,4.46 14.12,5.64L10.59,9.17C9.41,10.34 9.41,12.24 10.59,13.41M13.41,9.17C13.8,8.78 14.44,8.78 14.83,9.17C16.78,11.12 16.78,14.29 14.83,16.24V16.24L11.29,19.78C9.34,21.73 6.17,21.73 4.22,19.78C2.27,17.83 2.27,14.66 4.22,12.71L5.71,11.22C5.7,12.04 5.83,12.86 6.11,13.65L5.64,14.12C4.46,15.29 4.46,17.19 5.64,18.36C6.81,19.54 8.71,19.54 9.88,18.36L13.41,14.83C14.59,13.66 14.59,11.76 13.41,10.59C13,10.2 13,9.56 13.41,9.17Z"
|
||||||
|
android:fillColor="#ffffff" />
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:width="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M12,2A2,2 0 0,1 14,4C14,4.74 13.6,5.39 13,5.73V7H14A7,7 0 0,1 21,14H22A1,1 0 0,1 23,15V18A1,1 0 0,1 22,19H21V20A2,2 0 0,1 19,22H5A2,2 0 0,1 3,20V19H2A1,1 0 0,1 1,18V15A1,1 0 0,1 2,14H3A7,7 0 0,1 10,7H11V5.73C10.4,5.39 10,4.74 10,4A2,2 0 0,1 12,2M7.5,13A2.5,2.5 0 0,0 5,15.5A2.5,2.5 0 0,0 7.5,18A2.5,2.5 0 0,0 10,15.5A2.5,2.5 0 0,0 7.5,13M16.5,13A2.5,2.5 0 0,0 14,15.5A2.5,2.5 0 0,0 16.5,18A2.5,2.5 0 0,0 19,15.5A2.5,2.5 0 0,0 16.5,13Z"
|
||||||
|
android:fillColor="#ffffff" />
|
||||||
|
</vector>
|
||||||
|
|
@ -70,7 +70,9 @@
|
||||||
<string name="send_alt">Send</string>
|
<string name="send_alt">Send</string>
|
||||||
<string name="add_attachment_alt">Add attachment</string>
|
<string name="add_attachment_alt">Add attachment</string>
|
||||||
<string name="remove_attachment_alt">Remove attachment</string>
|
<string name="remove_attachment_alt">Remove attachment</string>
|
||||||
<string name="show_more_alt">Show more</string>
|
|
||||||
|
<string name="badge_bot_alt">Bot</string>
|
||||||
|
<string name="badge_masquerade_alt">From linked channel</string>
|
||||||
|
|
||||||
<string name="tutorial">Welcome to Revolt\'s in-progress Android experience!</string>
|
<string name="tutorial">Welcome to Revolt\'s in-progress Android experience!</string>
|
||||||
<string name="select_channel">Select a server and channel by swiping from the left.</string>
|
<string name="select_channel">Select a server and channel by swiping from the left.</string>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue