feat: display role icons
This commit is contained in:
parent
55dbb2a8e5
commit
126011a3e3
|
|
@ -13,12 +13,12 @@ import androidx.compose.material3.LocalContentColor
|
||||||
import androidx.compose.material3.LocalTextStyle
|
import androidx.compose.material3.LocalTextStyle
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
|
|
@ -28,9 +28,12 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import chat.stoat.R
|
import chat.stoat.R
|
||||||
import chat.stoat.api.StoatAPI
|
import chat.stoat.api.StoatAPI
|
||||||
|
import chat.stoat.api.internals.SpecialUsers
|
||||||
import chat.stoat.api.internals.solidColor
|
import chat.stoat.api.internals.solidColor
|
||||||
import chat.stoat.api.routes.channel.fetchSingleMessage
|
import chat.stoat.api.routes.channel.fetchSingleMessage
|
||||||
|
import chat.stoat.composables.generic.RemoteImage
|
||||||
import chat.stoat.composables.generic.UserAvatar
|
import chat.stoat.composables.generic.UserAvatar
|
||||||
|
import chat.stoat.core.model.data.STOAT_FILES
|
||||||
import chat.stoat.core.model.schemas.User
|
import chat.stoat.core.model.schemas.User
|
||||||
import java.util.concurrent.CancellationException
|
import java.util.concurrent.CancellationException
|
||||||
|
|
||||||
|
|
@ -52,6 +55,7 @@ fun InReplyTo(
|
||||||
val contentColor = LocalContentColor.current
|
val contentColor = LocalContentColor.current
|
||||||
val usernameColor =
|
val usernameColor =
|
||||||
message?.let { authorColour(it) } ?: Brush.solidColor(contentColor)
|
message?.let { authorColour(it) } ?: Brush.solidColor(contentColor)
|
||||||
|
val roleIcon = message?.let { authorRoleIcon(it) }
|
||||||
|
|
||||||
val serverId = remember(channelId) { StoatAPI.channelCache[channelId]?.server }
|
val serverId = remember(channelId) { StoatAPI.channelCache[channelId]?.server }
|
||||||
|
|
||||||
|
|
@ -107,11 +111,26 @@ fun InReplyTo(
|
||||||
modifier = Modifier.padding(horizontal = 4.dp)
|
modifier = Modifier.padding(horizontal = 4.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (roleIcon != null) {
|
||||||
|
RemoteImage(
|
||||||
|
url = "$STOAT_FILES/icons/${roleIcon.id}",
|
||||||
|
contentScale = ContentScale.Fit,
|
||||||
|
description = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(12.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
|
}
|
||||||
|
|
||||||
InlineBadges(
|
InlineBadges(
|
||||||
bot = message.masquerade == null && author?.bot != null,
|
bot = message.masquerade == null && author?.bot != null,
|
||||||
bridge = message.masquerade != null && author?.bot != null,
|
bridge = message.masquerade != null && author?.bot != null,
|
||||||
|
platformModeration = author?.id == SpecialUsers.PLATFORM_MODERATION_USER,
|
||||||
|
teamMember = author?.id in SpecialUsers.TEAM_MEMBER_FLAIRS.keys,
|
||||||
|
webhook = message.webhook != null,
|
||||||
colour = contentColor.copy(alpha = 0.5f),
|
colour = contentColor.copy(alpha = 0.5f),
|
||||||
modifier = Modifier.size(8.dp),
|
modifier = Modifier.size(12.dp),
|
||||||
followingIfAny = {
|
followingIfAny = {
|
||||||
Spacer(modifier = Modifier.width(4.dp))
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import android.content.Intent
|
||||||
import android.icu.text.DateFormat
|
import android.icu.text.DateFormat
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.browser.customtabs.CustomTabsIntent
|
import androidx.browser.customtabs.CustomTabsIntent
|
||||||
|
|
@ -111,6 +110,17 @@ fun authorColour(message: MessageSchema): Brush {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun authorRoleIcon(message: MessageSchema): AutumnResource? {
|
||||||
|
val serverId = StoatAPI.channelCache[message.channel]?.server ?: return null
|
||||||
|
|
||||||
|
val highestRole = message.author?.let {
|
||||||
|
Roles.resolveHighestRole(serverId, it)
|
||||||
|
} ?: return null
|
||||||
|
|
||||||
|
return highestRole.icon
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun displayNameInChannel(userId: String, channelId: String): String {
|
fun displayNameInChannel(userId: String, channelId: String): String {
|
||||||
val serverId =
|
val serverId =
|
||||||
|
|
@ -321,9 +331,6 @@ fun Message(
|
||||||
} == true),
|
} == true),
|
||||||
) {
|
) {
|
||||||
// TODO Add jump to message
|
// TODO Add jump to message
|
||||||
if (replyMessage == null) {
|
|
||||||
Toast.makeText(context, "lmao prankd", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -399,6 +406,19 @@ fun Message(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val roleIcon = authorRoleIcon(message)
|
||||||
|
if (roleIcon != null) {
|
||||||
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
|
|
||||||
|
RemoteImage(
|
||||||
|
url = "$STOAT_FILES/icons/${roleIcon.id}",
|
||||||
|
contentScale = ContentScale.Fit,
|
||||||
|
description = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
InlineBadges(
|
InlineBadges(
|
||||||
bot = author.bot != null && message.masquerade == null,
|
bot = author.bot != null && message.masquerade == null,
|
||||||
bridge = message.masquerade != null && author.bot != null,
|
bridge = message.masquerade != null && author.bot != null,
|
||||||
|
|
@ -520,9 +540,12 @@ fun Message(
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
Embed(embed = embed, serverId = StoatAPI.channelCache[message.channel]?.server, onLinkClick = {
|
Embed(
|
||||||
viewUrlInBrowser(context, it)
|
embed = embed,
|
||||||
})
|
serverId = StoatAPI.channelCache[message.channel]?.server,
|
||||||
|
onLinkClick = {
|
||||||
|
viewUrlInBrowser(context, it)
|
||||||
|
})
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package chat.stoat.composables.generic
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.LocalContentColor
|
import androidx.compose.material3.LocalContentColor
|
||||||
|
|
@ -39,23 +40,29 @@ fun ListHeader(
|
||||||
fun CountableListHeader(
|
fun CountableListHeader(
|
||||||
text: String,
|
text: String,
|
||||||
count: Int,
|
count: Int,
|
||||||
|
icon: @Composable (() -> Unit)? = null,
|
||||||
backgroundColor: Color = MaterialTheme.colorScheme.surface
|
backgroundColor: Color = MaterialTheme.colorScheme.surface
|
||||||
) {
|
) {
|
||||||
ListHeader(backgroundColor = backgroundColor) {
|
ListHeader(backgroundColor = backgroundColor) {
|
||||||
Text(
|
Row {
|
||||||
text = AnnotatedString.Builder().apply {
|
if (icon != null) {
|
||||||
append(text)
|
icon()
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = AnnotatedString.Builder().apply {
|
||||||
|
append(text)
|
||||||
|
|
||||||
pushStyle(
|
pushStyle(
|
||||||
SpanStyle(
|
SpanStyle(
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
fontSize = LocalTextStyle.current.fontSize * 0.8,
|
fontSize = LocalTextStyle.current.fontSize * 0.8,
|
||||||
color = LocalContentColor.current.copy(alpha = 0.6f)
|
color = LocalContentColor.current.copy(alpha = 0.6f)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
append("—$count")
|
||||||
append("—$count")
|
pop()
|
||||||
pop()
|
}.toAnnotatedString()
|
||||||
}.toAnnotatedString()
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
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.size
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
|
|
@ -28,6 +29,7 @@ import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.runtime.snapshotFlow
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
|
@ -42,8 +44,11 @@ import chat.stoat.api.routes.server.fetchMembers
|
||||||
import chat.stoat.composables.chat.MemberListItem
|
import chat.stoat.composables.chat.MemberListItem
|
||||||
import chat.stoat.composables.generic.CountableListHeader
|
import chat.stoat.composables.generic.CountableListHeader
|
||||||
import chat.stoat.composables.generic.Presence
|
import chat.stoat.composables.generic.Presence
|
||||||
|
import chat.stoat.composables.generic.RemoteImage
|
||||||
import chat.stoat.composables.generic.SheetHeaderPadding
|
import chat.stoat.composables.generic.SheetHeaderPadding
|
||||||
import chat.stoat.composables.generic.presenceFromStatus
|
import chat.stoat.composables.generic.presenceFromStatus
|
||||||
|
import chat.stoat.core.model.data.STOAT_FILES
|
||||||
|
import chat.stoat.core.model.schemas.AutumnResource
|
||||||
import chat.stoat.core.model.schemas.Member
|
import chat.stoat.core.model.schemas.Member
|
||||||
import chat.stoat.core.model.schemas.User
|
import chat.stoat.core.model.schemas.User
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
|
@ -57,7 +62,11 @@ val DO_NOT_FETCH_OFFLINE_MEMBERS_SERVERS = listOf(
|
||||||
sealed class MemberListSheetItem {
|
sealed class MemberListSheetItem {
|
||||||
data class MemberItem(val member: Member) : MemberListSheetItem()
|
data class MemberItem(val member: Member) : MemberListSheetItem()
|
||||||
data class UserItem(val user: User) : MemberListSheetItem()
|
data class UserItem(val user: User) : MemberListSheetItem()
|
||||||
data class CategoryItem(val category: String, val count: Int) : MemberListSheetItem()
|
data class CategoryItem(
|
||||||
|
val category: String,
|
||||||
|
val count: Int,
|
||||||
|
val icon: AutumnResource? = null
|
||||||
|
) : MemberListSheetItem()
|
||||||
}
|
}
|
||||||
|
|
||||||
class MemberListSheetViewModel(
|
class MemberListSheetViewModel(
|
||||||
|
|
@ -116,7 +125,13 @@ class MemberListSheetViewModel(
|
||||||
// Hoisted roles
|
// Hoisted roles
|
||||||
Roles.inOrder(serverId) { it.hoist == true }.forEach { role ->
|
Roles.inOrder(serverId) { it.hoist == true }.forEach { role ->
|
||||||
val members = categories[role.name] ?: return@forEach
|
val members = categories[role.name] ?: return@forEach
|
||||||
fullItemList.add(MemberListSheetItem.CategoryItem(role.name ?: "", members.size))
|
fullItemList.add(
|
||||||
|
MemberListSheetItem.CategoryItem(
|
||||||
|
role.name ?: "",
|
||||||
|
members.size,
|
||||||
|
role.icon
|
||||||
|
)
|
||||||
|
)
|
||||||
members.forEach { member ->
|
members.forEach { member ->
|
||||||
fullItemList.add(MemberListSheetItem.MemberItem(member))
|
fullItemList.add(MemberListSheetItem.MemberItem(member))
|
||||||
}
|
}
|
||||||
|
|
@ -312,6 +327,17 @@ fun MemberListSheet(
|
||||||
CountableListHeader(
|
CountableListHeader(
|
||||||
text = item.category,
|
text = item.category,
|
||||||
count = item.count,
|
count = item.count,
|
||||||
|
icon = {
|
||||||
|
if (item.icon == null) return@CountableListHeader
|
||||||
|
RemoteImage(
|
||||||
|
url = "$STOAT_FILES/icons/${item.icon.id}",
|
||||||
|
contentScale = ContentScale.Fit,
|
||||||
|
description = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(end = 8.dp)
|
||||||
|
.size(16.dp)
|
||||||
|
)
|
||||||
|
},
|
||||||
backgroundColor = MaterialTheme.colorScheme.surfaceContainerLow
|
backgroundColor = MaterialTheme.colorScheme.surfaceContainerLow
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,8 @@ data class Role(
|
||||||
val permissions: PermissionDescription? = null,
|
val permissions: PermissionDescription? = null,
|
||||||
val colour: String? = null,
|
val colour: String? = null,
|
||||||
val hoist: Boolean? = null,
|
val hoist: Boolean? = null,
|
||||||
val rank: Double? = null
|
val rank: Double? = null,
|
||||||
|
val icon: AutumnResource? = null
|
||||||
) {
|
) {
|
||||||
fun mergeWithPartial(other: Role): Role {
|
fun mergeWithPartial(other: Role): Role {
|
||||||
return Role(
|
return Role(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue