feat: support discriminators

Signed-off-by: Infi <wingit@geist.ga>
This commit is contained in:
Infi 2023-06-12 00:17:17 +02:00
parent 9508416d88
commit d3b6efa9fd
9 changed files with 95 additions and 32 deletions

View File

@ -2,11 +2,16 @@ package chat.revolt.api.internals
import chat.revolt.api.RevoltAPI import chat.revolt.api.RevoltAPI
import chat.revolt.api.schemas.Channel import chat.revolt.api.schemas.Channel
import chat.revolt.api.schemas.User
object ChannelUtils { object ChannelUtils {
fun resolveDMName(channel: Channel): String? { fun resolveDMName(channel: Channel): String? {
return channel.name return channel.name
?: RevoltAPI.userCache[channel.recipients?.first { u -> u != RevoltAPI.selfId }]?.username ?: RevoltAPI.userCache[channel.recipients?.first { u -> u != RevoltAPI.selfId }]?.let {
User.resolveDefaultName(
it
)
}
} }
fun resolveDMPartner(channel: Channel): String? { fun resolveDMPartner(channel: Channel): String? {

View File

@ -1,15 +1,16 @@
package chat.revolt.api.schemas package chat.revolt.api.schemas
import kotlinx.serialization.* import kotlinx.serialization.SerialName
import kotlinx.serialization.descriptors.* import kotlinx.serialization.Serializable
import kotlinx.serialization.encoding.*
import kotlinx.serialization.json.*
@Serializable @Serializable
data class User( data class User(
@SerialName("_id") @SerialName("_id")
val id: String? = null, val id: String? = null,
val username: String? = null, val username: String? = null,
val discriminator: String? = null,
@SerialName("display_name")
val displayName: String? = null,
val avatar: AutumnResource? = null, val avatar: AutumnResource? = null,
val relations: List<Relation>? = null, val relations: List<Relation>? = null,
val badges: Long? = null, val badges: Long? = null,
@ -25,6 +26,8 @@ data class User(
return User( return User(
id = partial.id ?: id, id = partial.id ?: id,
username = partial.username ?: username, username = partial.username ?: username,
discriminator = partial.discriminator ?: discriminator,
displayName = partial.displayName ?: displayName,
avatar = partial.avatar ?: avatar, avatar = partial.avatar ?: avatar,
relations = partial.relations ?: relations, relations = partial.relations ?: relations,
badges = partial.badges ?: badges, badges = partial.badges ?: badges,
@ -42,6 +45,8 @@ data class User(
fun getPlaceholder(forId: String) = User( fun getPlaceholder(forId: String) = User(
id = forId, id = forId,
username = "Unknown User", username = "Unknown User",
discriminator = "0000",
displayName = null,
avatar = null, avatar = null,
badges = 0, badges = 0,
status = null, status = null,
@ -52,6 +57,11 @@ data class User(
relationship = null, relationship = null,
online = false online = false
) )
fun resolveDefaultName(user: User, withDiscriminator: Boolean = false): String {
val maybeDiscriminator = if (withDiscriminator) "#${user.discriminator}" else ""
return user.displayName ?: "${user.username}$maybeDiscriminator"
}
} }
} }

View File

@ -22,8 +22,8 @@ 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.internals.WebCompat
import chat.revolt.api.routes.microservices.january.asJanuaryProxyUrl import chat.revolt.api.routes.microservices.january.asJanuaryProxyUrl
import chat.revolt.api.schemas.User
import chat.revolt.components.generic.UserAvatar import chat.revolt.components.generic.UserAvatar
@Composable @Composable
@ -36,12 +36,12 @@ 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 ?: "" val username = message?.let { authorName(it) }
?: author?.let { User.resolveDefaultName(it) }
?: stringResource(id = R.string.unknown)
val contentColor = LocalContentColor.current val contentColor = LocalContentColor.current
val usernameColor = message?.masquerade?.colour?.let { val usernameColor = message?.let { authorColour(it) } ?: contentColor
WebCompat.parseColour(it)
} ?: contentColor
Box( Box(
modifier = modifier modifier = modifier

View File

@ -52,6 +52,7 @@ import chat.revolt.api.internals.ULID
import chat.revolt.api.internals.WebCompat import chat.revolt.api.internals.WebCompat
import chat.revolt.api.routes.microservices.january.asJanuaryProxyUrl import chat.revolt.api.routes.microservices.january.asJanuaryProxyUrl
import chat.revolt.api.schemas.AutumnResource import chat.revolt.api.schemas.AutumnResource
import chat.revolt.api.schemas.User
import chat.revolt.components.generic.UserAvatar import chat.revolt.components.generic.UserAvatar
import chat.revolt.components.generic.UserAvatarWidthPlaceholder import chat.revolt.components.generic.UserAvatarWidthPlaceholder
import chat.revolt.api.schemas.Message as MessageSchema import chat.revolt.api.schemas.Message as MessageSchema
@ -67,11 +68,9 @@ fun authorColour(message: MessageSchema): Color {
@Composable @Composable
fun authorName(message: MessageSchema): String { fun authorName(message: MessageSchema): String {
return if (message.masquerade?.name != null) { return message.masquerade?.name
message.masquerade.name ?: RevoltAPI.userCache[message.author]?.let { User.resolveDefaultName(it) }
} else { ?: stringResource(R.string.unknown)
RevoltAPI.userCache[message.author]?.username ?: stringResource(id = R.string.unknown)
}
} }
fun viewUrlInBrowser(ctx: android.content.Context, url: String) { fun viewUrlInBrowser(ctx: android.content.Context, url: String) {
@ -194,7 +193,7 @@ fun Message(
Column { Column {
Spacer(modifier = Modifier.height(4.dp)) Spacer(modifier = Modifier.height(4.dp))
UserAvatar( UserAvatar(
username = author.username ?: "", username = User.resolveDefaultName(author),
userId = author.id ?: message.id ?: ULID.makeSpecial(0), userId = author.id ?: message.id ?: ULID.makeSpecial(0),
avatar = author.avatar, avatar = author.avatar,
rawUrl = message.masquerade?.avatar?.let { asJanuaryProxyUrl(it) } rawUrl = message.masquerade?.avatar?.let { asJanuaryProxyUrl(it) }

View File

@ -14,7 +14,6 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.material3.surfaceColorAtElevation
@ -29,10 +28,12 @@ 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.internals.WebCompat import chat.revolt.api.internals.ULID
import chat.revolt.api.routes.channel.SendMessageReply import chat.revolt.api.routes.channel.SendMessageReply
import chat.revolt.api.routes.microservices.january.asJanuaryProxyUrl import chat.revolt.api.routes.microservices.january.asJanuaryProxyUrl
import chat.revolt.api.schemas.Message import chat.revolt.api.schemas.Message
import chat.revolt.components.chat.authorColour
import chat.revolt.components.chat.authorName
import chat.revolt.components.generic.UserAvatar import chat.revolt.components.generic.UserAvatar
@Composable @Composable
@ -76,8 +77,8 @@ fun ManageableReply(
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
UserAvatar( UserAvatar(
username = replyAuthor.username!!, username = authorName(message = replyMessage),
userId = replyAuthor.id!!, userId = replyAuthor.id ?: ULID.makeSpecial(0),
avatar = replyAuthor.avatar, avatar = replyAuthor.avatar,
rawUrl = replyMessage.masquerade?.avatar?.let { asJanuaryProxyUrl(it) }, rawUrl = replyMessage.masquerade?.avatar?.let { asJanuaryProxyUrl(it) },
size = 16.dp size = 16.dp
@ -86,16 +87,14 @@ fun ManageableReply(
Spacer(modifier = Modifier.width(4.dp)) Spacer(modifier = Modifier.width(4.dp))
Text( Text(
text = replyMessage.masquerade?.name ?: replyAuthor.username, text = authorName(message = replyMessage),
fontSize = 12.sp, fontSize = 12.sp,
modifier = Modifier modifier = Modifier
.clickable { .clickable {
onToggleMention() onToggleMention()
} }
.padding(4.dp), .padding(4.dp),
color = if (replyMessage.masquerade?.colour != null) { color = authorColour(message = replyMessage),
WebCompat.parseColour(replyMessage.masquerade.colour)
} else LocalContentColor.current,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
) )

View File

@ -1,8 +1,17 @@
package chat.revolt.components.screens.chat package chat.revolt.components.screens.chat
import androidx.compose.animation.* import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
@ -17,6 +26,7 @@ import chat.revolt.R
import chat.revolt.activities.RevoltTweenFloat import chat.revolt.activities.RevoltTweenFloat
import chat.revolt.activities.RevoltTweenInt import chat.revolt.activities.RevoltTweenInt
import chat.revolt.api.RevoltAPI import chat.revolt.api.RevoltAPI
import chat.revolt.api.schemas.User
import chat.revolt.components.generic.UserAvatar import chat.revolt.components.generic.UserAvatar
@Composable @Composable
@ -33,7 +43,7 @@ fun StackedUserAvatars(
UserAvatar( UserAvatar(
avatar = user?.avatar, avatar = user?.avatar,
userId = userId, userId = userId,
username = user?.username ?: stringResource(id = R.string.unknown), username = user?.let {User.resolveDefaultName(it)} ?: stringResource(id = R.string.unknown),
size = 16.dp, size = 16.dp,
modifier = Modifier modifier = Modifier
.offset( .offset(
@ -87,7 +97,7 @@ fun TypingIndicator(
id = typingMessageResource(), id = typingMessageResource(),
users.joinToString { users.joinToString {
RevoltAPI.userCache[it]?.let { u -> RevoltAPI.userCache[it]?.let { u ->
u.username ?: u.id User.resolveDefaultName(u)
} ?: it } ?: it
} }
), ),

View File

@ -31,6 +31,7 @@ import chat.revolt.R
import chat.revolt.api.RevoltAPI import chat.revolt.api.RevoltAPI
import chat.revolt.api.internals.ChannelUtils import chat.revolt.api.internals.ChannelUtils
import chat.revolt.api.schemas.ChannelType import chat.revolt.api.schemas.ChannelType
import chat.revolt.api.schemas.User
import chat.revolt.components.generic.presenceFromStatus import chat.revolt.components.generic.presenceFromStatus
import chat.revolt.components.screens.chat.drawer.server.DrawerChannel import chat.revolt.components.screens.chat.drawer.server.DrawerChannel
import chat.revolt.sheets.ChannelContextSheet import chat.revolt.sheets.ChannelContextSheet
@ -157,7 +158,7 @@ fun RowScope.ChannelList(
)] else null )] else null
DrawerChannel( DrawerChannel(
name = partner?.username ?: channel.name name = partner?.let { p -> User.resolveDefaultName(p) } ?: channel.name
?: stringResource(R.string.unknown), ?: stringResource(R.string.unknown),
channelType = channel.channelType ?: ChannelType.TextChannel, channelType = channel.channelType ?: ChannelType.TextChannel,
selected = currentDestination == "channel/{channelId}" && currentChannel == channel.id, selected = currentDestination == "channel/{channelId}" && currentChannel == channel.id,
@ -169,7 +170,7 @@ fun RowScope.ChannelList(
} ?: false, } ?: false,
dmPartnerIcon = partner?.avatar ?: channel.icon, dmPartnerIcon = partner?.avatar ?: channel.icon,
dmPartnerId = partner?.id, dmPartnerId = partner?.id,
dmPartnerName = partner?.username, dmPartnerName = partner?.let { p -> User.resolveDefaultName(p) },
dmPartnerStatus = presenceFromStatus( dmPartnerStatus = presenceFromStatus(
partner?.status?.presence ?: "Offline" partner?.status?.presence ?: "Offline"
), ),

View File

@ -54,6 +54,7 @@ import chat.revolt.R
import chat.revolt.api.RevoltAPI import chat.revolt.api.RevoltAPI
import chat.revolt.api.realtime.DisconnectionState import chat.revolt.api.realtime.DisconnectionState
import chat.revolt.api.realtime.RealtimeSocket import chat.revolt.api.realtime.RealtimeSocket
import chat.revolt.api.schemas.User
import chat.revolt.api.settings.SyncedSettings import chat.revolt.api.settings.SyncedSettings
import chat.revolt.components.chat.DisconnectedNotice import chat.revolt.components.chat.DisconnectedNotice
import chat.revolt.components.generic.UserAvatar import chat.revolt.components.generic.UserAvatar
@ -332,7 +333,11 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = hil
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
UserAvatar( UserAvatar(
username = RevoltAPI.userCache[RevoltAPI.selfId]?.username username = RevoltAPI.userCache[RevoltAPI.selfId]?.let {
User.resolveDefaultName(
it
)
}
?: "", ?: "",
presence = presenceFromStatus( presence = presenceFromStatus(
RevoltAPI.userCache[RevoltAPI.selfId]?.status?.presence RevoltAPI.userCache[RevoltAPI.selfId]?.status?.presence

View File

@ -1,9 +1,11 @@
package chat.revolt.sheets package chat.revolt.sheets
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.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@ -11,12 +13,19 @@ import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
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 chat.revolt.R import chat.revolt.R
import chat.revolt.api.RevoltAPI import chat.revolt.api.RevoltAPI
import chat.revolt.api.internals.ULID
import chat.revolt.components.generic.SheetClickable import chat.revolt.components.generic.SheetClickable
import chat.revolt.components.generic.UserAvatar
import chat.revolt.components.generic.presenceFromStatus
@Composable @Composable
fun StatusSheet( fun StatusSheet(
@ -30,7 +39,32 @@ fun StatusSheet(
.padding(horizontal = 16.dp, vertical = 8.dp) .padding(horizontal = 16.dp, vertical = 8.dp)
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
) { ) {
Text(text = "Logged in as @${selfUser.username} (${selfUser.id})") Row(
verticalAlignment = Alignment.CenterVertically
) {
UserAvatar(
username = selfUser.displayName ?: stringResource(id = R.string.unknown),
userId = selfUser.id ?: ULID.makeSpecial(0),
avatar = selfUser.avatar,
size = 48.dp,
presence = presenceFromStatus(selfUser.status?.presence ?: "offline"),
)
Spacer(modifier = Modifier.width(12.dp))
Text(text = AnnotatedString.Builder().apply {
if (selfUser.displayName != null) {
pushStyle(SpanStyle(fontWeight = FontWeight.Bold))
append(selfUser.displayName)
pop()
append("\n")
}
append("${selfUser.username}")
pushStyle(SpanStyle(fontWeight = FontWeight.ExtraLight))
append("#${selfUser.discriminator}")
pop()
}.toAnnotatedString())
}
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))