Badges support in the app
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_12" data-name="Layer 12" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #652d90;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: #ffde17;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g>
|
||||
<path class="cls-2" d="M500,224.206c0-74.5345-194.9003-17.3066-150.6048,44.2215-44.2955-61.5281-177.3163,91.9861-102.7888,118.6246-74.5275-26.6385-103.4357,174.4223-22.3381,155.3652-81.0976,19.0571,3.285,203.8292,65.2047,142.7784-61.9197,61.0508,108.9628,170.8703,132.0456,84.8605-23.0828,86.0097,180.0456,86.0097,156.9629,0,23.0828,86.0097,193.9653-23.8098,132.0456-84.8605,61.9197,61.0508,146.3023-123.7213,65.2047-142.7784,81.0976,19.0571,52.1894-182.0037-22.3381-155.3652,74.5275-26.6385-58.4933-180.1527-102.7888-118.6246,44.2955-61.5281-150.6048-118.756-150.6048-44.2215Z"/>
|
||||
<polygon class="cls-1" points="680.2813 428.2256 624.5839 374.5166 447.929 557.7138 379.2446 491.4722 325.5312 547.1663 449.8956 667.1077 453.7317 663.1302 453.7493 663.1472 680.2813 428.2256"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="cls-2" d="M500,224.206c0-74.5345-194.9003-17.3066-150.6048,44.2215-44.2955-61.5281-177.3163,91.9861-102.7888,118.6246-74.5275-26.6385-103.4357,174.4223-22.3381,155.3652-81.0976,19.0571,3.285,203.8292,65.2047,142.7784-61.9197,61.0508,108.9628,170.8703,132.0456,84.8605-23.0828,86.0097,180.0456,86.0097,156.9629,0,23.0828,86.0097,193.9653-23.8098,132.0456-84.8605,61.9197,61.0508,146.3023-123.7213,65.2047-142.7784,81.0976,19.0571,52.1894-182.0037-22.3381-155.3652,74.5275-26.6385-58.4933-180.1527-102.7888-118.6246,44.2955-61.5281-150.6048-118.756-150.6048-44.2215Z"/>
|
||||
<polygon class="cls-1" points="680.2813 428.2256 624.5839 374.5166 447.929 557.7138 379.2446 491.4722 325.5312 547.1663 449.8956 667.1077 453.7317 663.1302 453.7493 663.1472 680.2813 428.2256"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="cls-2" d="M500,224.206c0-74.5345-194.9003-17.3066-150.6048,44.2215-44.2955-61.5281-177.3163,91.9861-102.7888,118.6246-74.5275-26.6385-103.4357,174.4223-22.3381,155.3652-81.0976,19.0571,3.285,203.8292,65.2047,142.7784-61.9197,61.0508,108.9628,170.8703,132.0456,84.8605-23.0828,86.0097,180.0456,86.0097,156.9629,0,23.0828,86.0097,193.9653-23.8098,132.0456-84.8605,61.9197,61.0508,146.3023-123.7213,65.2047-142.7784,81.0976,19.0571,52.1894-182.0037-22.3381-155.3652,74.5275-26.6385-58.4933-180.1527-102.7888-118.6246,44.2955-61.5281-150.6048-118.756-150.6048-44.2215Z"/>
|
||||
<polygon class="cls-1" points="680.2813 428.2256 624.5839 374.5166 447.929 557.7138 379.2446 491.4722 325.5312 547.1663 449.8956 667.1077 453.7317 663.1302 453.7493 663.1472 680.2813 428.2256"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
|
|
@ -5,9 +5,15 @@ import androidx.compose.material3.LocalContentColor
|
|||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.zekochat.api.PeptideAPI
|
||||
import chat.zekochat.api.internals.BrushCompat
|
||||
import chat.zekochat.api.internals.Roles
|
||||
|
|
@ -44,16 +50,30 @@ fun MemberListItem(
|
|||
colors = TransparentListItemColours,
|
||||
modifier = modifier,
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = member?.nickname
|
||||
?: user?.displayName
|
||||
?: user?.username
|
||||
?: user?.id
|
||||
?: userId,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = LocalTextStyle.current.copy(brush = colour),
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
text = member?.nickname
|
||||
?: user?.displayName
|
||||
?: user?.username
|
||||
?: user?.id
|
||||
?: userId,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = LocalTextStyle.current.copy(brush = colour),
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
if (hasVerifiedVendor(user?.badges)) {
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
UserVendorBadge(
|
||||
badges = user?.badges,
|
||||
size = 16.dp
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
supportingContent = {
|
||||
user?.status?.text?.let {
|
||||
|
|
|
|||
|
|
@ -417,6 +417,14 @@ fun Message(
|
|||
)
|
||||
)
|
||||
|
||||
if (hasVerifiedVendor(author.badges)) {
|
||||
Spacer(modifier = Modifier.width(5.dp))
|
||||
UserVendorBadge(
|
||||
badges = author.badges,
|
||||
size = 16.dp
|
||||
)
|
||||
}
|
||||
|
||||
InlineBadges(
|
||||
bot = author.bot != null && message.masquerade == null,
|
||||
bridge = message.masquerade != null && author.bot != null,
|
||||
|
|
|
|||
|
|
@ -50,77 +50,70 @@ fun BadgeListEntry(badge: UserBadges) {
|
|||
UserBadges.Developer.value -> {
|
||||
BadgeListEntryTemplate(
|
||||
label = stringResource(R.string.user_badge_developer),
|
||||
icon = painterResource(R.drawable.user_badge_developer)
|
||||
icon = painterResource(R.drawable.badge_developer)
|
||||
)
|
||||
}
|
||||
|
||||
UserBadges.Translator.value -> {
|
||||
BadgeListEntryTemplate(
|
||||
label = stringResource(R.string.user_badge_translator),
|
||||
icon = painterResource(R.drawable.user_badge_translator)
|
||||
label = stringResource(R.string.user_badge_first_100_members),
|
||||
icon = painterResource(R.drawable.badge_first_100_members)
|
||||
)
|
||||
}
|
||||
|
||||
UserBadges.Supporter.value -> {
|
||||
BadgeListEntryTemplate(
|
||||
label = stringResource(R.string.user_badge_supporter),
|
||||
icon = painterResource(R.drawable.user_badge_supporter)
|
||||
icon = painterResource(R.drawable.badge_supporter)
|
||||
)
|
||||
}
|
||||
|
||||
UserBadges.ResponsibleDisclosure.value -> {
|
||||
BadgeListEntryTemplate(
|
||||
label = stringResource(R.string.user_badge_responsible_disclosure),
|
||||
icon = painterResource(R.drawable.user_badge_disclosure)
|
||||
label = stringResource(R.string.user_badge_trusted_seller),
|
||||
icon = painterResource(R.drawable.badge_trusted_seller)
|
||||
)
|
||||
}
|
||||
|
||||
UserBadges.Founder.value -> {
|
||||
BadgeListEntryTemplate(
|
||||
label = stringResource(R.string.user_badge_founder),
|
||||
icon = painterResource(R.drawable.user_badge_founder)
|
||||
icon = painterResource(R.drawable.badge_founder)
|
||||
)
|
||||
}
|
||||
|
||||
UserBadges.PlatformModeration.value -> {
|
||||
BadgeListEntryTemplate(
|
||||
label = stringResource(R.string.user_badge_platform_moderation),
|
||||
icon = painterResource(R.drawable.user_badge_moderation)
|
||||
)
|
||||
}
|
||||
|
||||
UserBadges.ActiveSupporter.value -> {
|
||||
BadgeListEntryTemplate(
|
||||
label = stringResource(R.string.user_badge_active_supporter),
|
||||
icon = painterResource(R.drawable.icn_emoji_people_24dp)
|
||||
label = stringResource(R.string.user_badge_administrator),
|
||||
icon = painterResource(R.drawable.badge_administrator)
|
||||
)
|
||||
}
|
||||
|
||||
UserBadges.Paw.value -> {
|
||||
BadgeListEntryTemplate(
|
||||
label = stringResource(R.string.user_badge_paw),
|
||||
icon = painterResource(R.drawable.user_badge_paw)
|
||||
label = stringResource(R.string.user_badge_clown),
|
||||
icon = painterResource(R.drawable.badge_clown)
|
||||
)
|
||||
}
|
||||
|
||||
UserBadges.EarlyAdopter.value -> {
|
||||
BadgeListEntryTemplate(
|
||||
label = stringResource(R.string.user_badge_early_adopter),
|
||||
icon = painterResource(R.drawable.user_badge_early_adopter)
|
||||
label = stringResource(R.string.user_badge_top_contributor),
|
||||
icon = painterResource(R.drawable.badge_top_contributor)
|
||||
)
|
||||
}
|
||||
|
||||
UserBadges.ReservedRelevantJokeBadge1.value -> {
|
||||
BadgeListEntryTemplate(
|
||||
label = stringResource(R.string.user_badge_reserved_relevant_joke_badge_1),
|
||||
icon = painterResource(R.drawable.user_badge_reserved_relevant_one)
|
||||
label = stringResource(R.string.user_badge_karen),
|
||||
icon = painterResource(R.drawable.badge_karen)
|
||||
)
|
||||
}
|
||||
|
||||
UserBadges.ReservedRelevantJokeBadge2.value -> {
|
||||
BadgeListEntryTemplate(
|
||||
label = stringResource(R.string.user_badge_reserved_relevant_joke_badge_2),
|
||||
icon = painterResource(R.drawable.user_badge_reserved_relevant_two)
|
||||
label = stringResource(R.string.user_badge_gump),
|
||||
icon = painterResource(R.drawable.badge_gump)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -132,7 +125,7 @@ fun UserBadgeList(badges: Long) {
|
|||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
UserBadges.entries
|
||||
.filter { badges has it }
|
||||
.filter { it != UserBadges.ActiveSupporter && (badges has it) }
|
||||
.forEach { badge ->
|
||||
BadgeListEntry(badge)
|
||||
}
|
||||
|
|
@ -147,34 +140,34 @@ fun UserBadgeRow(badges: Long) {
|
|||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
UserBadges.entries
|
||||
.filter { badges has it }
|
||||
.filter { it != UserBadges.ActiveSupporter && (badges has it) }
|
||||
.forEach { badge ->
|
||||
Image(
|
||||
painter = when (badge) {
|
||||
UserBadges.Developer -> painterResource(R.drawable.user_badge_developer)
|
||||
UserBadges.Translator -> painterResource(R.drawable.user_badge_translator)
|
||||
UserBadges.Supporter -> painterResource(R.drawable.user_badge_supporter)
|
||||
UserBadges.ResponsibleDisclosure -> painterResource(R.drawable.user_badge_disclosure)
|
||||
UserBadges.Founder -> painterResource(R.drawable.user_badge_founder)
|
||||
UserBadges.PlatformModeration -> painterResource(R.drawable.user_badge_moderation)
|
||||
UserBadges.Developer -> painterResource(R.drawable.badge_developer)
|
||||
UserBadges.Translator -> painterResource(R.drawable.badge_first_100_members)
|
||||
UserBadges.Supporter -> painterResource(R.drawable.badge_supporter)
|
||||
UserBadges.ResponsibleDisclosure -> painterResource(R.drawable.badge_trusted_seller)
|
||||
UserBadges.Founder -> painterResource(R.drawable.badge_founder)
|
||||
UserBadges.PlatformModeration -> painterResource(R.drawable.badge_administrator)
|
||||
UserBadges.ActiveSupporter -> painterResource(R.drawable.icn_emoji_people_24dp)
|
||||
UserBadges.Paw -> painterResource(R.drawable.user_badge_paw)
|
||||
UserBadges.EarlyAdopter -> painterResource(R.drawable.user_badge_early_adopter)
|
||||
UserBadges.ReservedRelevantJokeBadge1 -> painterResource(R.drawable.user_badge_reserved_relevant_one)
|
||||
UserBadges.ReservedRelevantJokeBadge2 -> painterResource(R.drawable.user_badge_reserved_relevant_two)
|
||||
UserBadges.Paw -> painterResource(R.drawable.badge_clown)
|
||||
UserBadges.EarlyAdopter -> painterResource(R.drawable.badge_top_contributor)
|
||||
UserBadges.ReservedRelevantJokeBadge1 -> painterResource(R.drawable.badge_karen)
|
||||
UserBadges.ReservedRelevantJokeBadge2 -> painterResource(R.drawable.badge_gump)
|
||||
},
|
||||
contentDescription = when (badge) {
|
||||
UserBadges.Developer -> stringResource(R.string.user_badge_developer)
|
||||
UserBadges.Translator -> stringResource(R.string.user_badge_translator)
|
||||
UserBadges.Translator -> stringResource(R.string.user_badge_first_100_members)
|
||||
UserBadges.Supporter -> stringResource(R.string.user_badge_supporter)
|
||||
UserBadges.ResponsibleDisclosure -> stringResource(R.string.user_badge_responsible_disclosure)
|
||||
UserBadges.ResponsibleDisclosure -> stringResource(R.string.user_badge_trusted_seller)
|
||||
UserBadges.Founder -> stringResource(R.string.user_badge_founder)
|
||||
UserBadges.PlatformModeration -> stringResource(R.string.user_badge_platform_moderation)
|
||||
UserBadges.PlatformModeration -> stringResource(R.string.user_badge_administrator)
|
||||
UserBadges.ActiveSupporter -> stringResource(R.string.user_badge_active_supporter)
|
||||
UserBadges.Paw -> stringResource(R.string.user_badge_paw)
|
||||
UserBadges.EarlyAdopter -> stringResource(R.string.user_badge_early_adopter)
|
||||
UserBadges.ReservedRelevantJokeBadge1 -> stringResource(R.string.user_badge_reserved_relevant_joke_badge_1)
|
||||
UserBadges.ReservedRelevantJokeBadge2 -> stringResource(R.string.user_badge_reserved_relevant_joke_badge_2)
|
||||
UserBadges.Paw -> stringResource(R.string.user_badge_clown)
|
||||
UserBadges.EarlyAdopter -> stringResource(R.string.user_badge_top_contributor)
|
||||
UserBadges.ReservedRelevantJokeBadge1 -> stringResource(R.string.user_badge_karen)
|
||||
UserBadges.ReservedRelevantJokeBadge2 -> stringResource(R.string.user_badge_gump)
|
||||
},
|
||||
modifier = Modifier
|
||||
.size(32.dp)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
package chat.zekochat.composables.chat
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.zekochat.R
|
||||
import chat.zekochat.api.schemas.UserBadges
|
||||
|
||||
/**
|
||||
* Compact "Verified Vendor" indicator shown next to usernames.
|
||||
*
|
||||
* Web mapping notes:
|
||||
* - ResponsibleDisclosure (8) and ReservedRelevantJokeBadge1 (512) both trigger the vendor badge.
|
||||
*/
|
||||
fun hasVerifiedVendor(badges: Long?): Boolean {
|
||||
val b = badges ?: 0L
|
||||
return (b and UserBadges.ResponsibleDisclosure.value) != 0L ||
|
||||
(b and UserBadges.ReservedRelevantJokeBadge1.value) != 0L
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserVendorBadge(
|
||||
badges: Long?,
|
||||
modifier: Modifier = Modifier,
|
||||
size: Dp = 16.dp,
|
||||
) {
|
||||
if (!hasVerifiedVendor(badges)) return
|
||||
|
||||
Image(
|
||||
painter = painterResource(R.drawable.badge_verified_vendor),
|
||||
contentDescription = stringResource(R.string.user_badge_verified_vendor),
|
||||
modifier = modifier.then(Modifier.size(size)),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -63,6 +63,8 @@ import chat.zekochat.api.settings.NotificationType
|
|||
import chat.zekochat.api.settings.SyncedSettings
|
||||
import chat.zekochat.callbacks.Action
|
||||
import chat.zekochat.callbacks.ActionChannel
|
||||
import chat.zekochat.composables.chat.UserBadgeRow
|
||||
import chat.zekochat.composables.chat.UserVendorBadge
|
||||
import chat.zekochat.composables.generic.NonIdealState
|
||||
import chat.zekochat.composables.generic.SheetButton
|
||||
import chat.zekochat.composables.generic.UserAvatar
|
||||
|
|
@ -193,13 +195,29 @@ fun UserInfoSheet(
|
|||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
|
||||
resolvedUser!!.username?.let { uname ->
|
||||
Text(
|
||||
text = "@$uname",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "@$uname",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
UserVendorBadge(
|
||||
badges = resolvedUser!!.badges,
|
||||
size = 16.dp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val badgesValue = resolvedUser!!.badges ?: 0L
|
||||
if (badgesValue > 0L) {
|
||||
Spacer(Modifier.height(8.dp))
|
||||
UserBadgeRow(badges = badgesValue)
|
||||
}
|
||||
}
|
||||
if (Experiments.enableServerIdentityOptions.isEnabled) {
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
|
@ -0,0 +1,17 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="1000"
|
||||
android:viewportHeight="1000">
|
||||
|
||||
<!-- Yellow star/pill shape (from verifiedvendor.svg). -->
|
||||
<path
|
||||
android:fillColor="#FFDE17"
|
||||
android:pathData="M500,224.206c0-74.5345-194.9003-17.3066-150.6048,44.2215-44.2955-61.5281-177.3163,91.9861-102.7888,118.6246-74.5275-26.6385-103.4357,174.4223-22.3381,155.3652-81.0976,19.0571,3.285,203.8292,65.2047,142.7784-61.9197,61.0508,108.9628,170.8703,132.0456,84.8605-23.0828,86.0097,180.0456,86.0097,156.9629,0,23.0828,86.0097,193.9653-23.8098,132.0456-84.8605,61.9197,61.0508,146.3023-123.7213,65.2047-142.7784,81.0976,19.0571,52.1894-182.0037-22.3381-155.3652,74.5275-26.6385-58.4933-180.1527-102.7888-118.6246,44.2955-61.5281-150.6048-118.756-150.6048-44.2215Z" />
|
||||
|
||||
<!-- Purple highlight polygon (from verifiedvendor.svg). -->
|
||||
<path
|
||||
android:fillColor="#652D90"
|
||||
android:pathData="M680.2813,428.2256L624.5839,374.5166L447.929,557.7138L379.2446,491.4722L325.5312,547.1663L449.8956,667.1077L453.7317,663.1302L453.7493,663.1472Z" />
|
||||
</vector>
|
||||
|
||||
|
|
@ -465,6 +465,16 @@
|
|||
<string name="user_badge_reserved_relevant_joke_badge_1" translatable="false">sus</string>
|
||||
<string name="user_badge_reserved_relevant_joke_badge_2" translatable="false">It\'s Morbin Time</string>
|
||||
|
||||
<!-- Web badge mapping labels (used as accessibility/tooltips on Android). -->
|
||||
<string name="user_badge_first_100_members" translatable="false">First 100 Members</string>
|
||||
<string name="user_badge_top_contributor" translatable="false">Top Contributor</string>
|
||||
<string name="user_badge_administrator" translatable="false">Administrator</string>
|
||||
<string name="user_badge_trusted_seller" translatable="false">Trusted Seller</string>
|
||||
<string name="user_badge_karen" translatable="false">Karen</string>
|
||||
<string name="user_badge_gump" translatable="false">Gump</string>
|
||||
<string name="user_badge_clown" translatable="false">Clown</string>
|
||||
<string name="user_badge_verified_vendor" translatable="false">Verified Vendor</string>
|
||||
|
||||
<string name="add_server_sheet_step_0_title">Add Server</string>
|
||||
<string name="add_server_sheet_step_0_description">A server is where the magic happens. Chat with your friends, share memes, build communities, and more.</string>
|
||||
<string name="add_server_sheet_step_0_join">Join by invite code or link</string>
|
||||
|
|
|
|||