feat(experiment): server identity options
Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
parent
62a9a6c022
commit
2d8e9c3804
|
|
@ -28,6 +28,7 @@ class ExperimentInstance(default: Boolean) {
|
|||
object Experiments {
|
||||
val useKotlinBasedMarkdownRenderer = ExperimentInstance(false)
|
||||
val usePolar = ExperimentInstance(false)
|
||||
val enableServerIdentityOptions = ExperimentInstance(false)
|
||||
|
||||
suspend fun hydrateWithKv() {
|
||||
val kvStorage = KVStorage(RevoltApplication.instance)
|
||||
|
|
@ -35,7 +36,7 @@ object Experiments {
|
|||
if (BuildConfig.DEBUG) {
|
||||
LoadedSettings.experimentsEnabled = true
|
||||
} else {
|
||||
LoadedSettings.experimentsEnabled = kvStorage.getBoolean("experimentsEnabled") ?: false
|
||||
LoadedSettings.experimentsEnabled = kvStorage.getBoolean("experimentsEnabled") == true
|
||||
}
|
||||
|
||||
useKotlinBasedMarkdownRenderer.setEnabled(
|
||||
|
|
@ -44,5 +45,8 @@ object Experiments {
|
|||
usePolar.setEnabled(
|
||||
kvStorage.getBoolean("exp/usePolar") == true
|
||||
)
|
||||
enableServerIdentityOptions.setEnabled(
|
||||
kvStorage.getBoolean("exp/enableServerIdentityOptions") == true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -53,8 +53,11 @@ import androidx.compose.ui.layout.ContentScale
|
|||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
|
@ -90,6 +93,7 @@ import chat.revolt.internals.text.MessageProcessor
|
|||
import chat.revolt.markdown.jbm.JBM
|
||||
import chat.revolt.markdown.jbm.JBMRenderer
|
||||
import chat.revolt.markdown.jbm.LocalJBMarkdownTreeState
|
||||
import chat.revolt.persistence.KVStorage
|
||||
import kotlinx.coroutines.launch
|
||||
import chat.revolt.api.schemas.Message as MessageSchema
|
||||
|
||||
|
|
@ -200,6 +204,21 @@ fun Message(
|
|||
val context = LocalContext.current
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
var kv by remember { mutableStateOf<KVStorage?>(null) }
|
||||
var showUsernameDiscriminator by remember { mutableStateOf(false) }
|
||||
var ignoreServerAvatar by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(Unit) {
|
||||
if (Experiments.enableServerIdentityOptions.isEnabled) {
|
||||
val userId = author.id ?: return@LaunchedEffect
|
||||
kv = KVStorage(context)
|
||||
kv?.let {
|
||||
showUsernameDiscriminator =
|
||||
it.getBoolean("exp/serverIdentityOptions/$userId/showUsernameDiscriminator") == true
|
||||
ignoreServerAvatar =
|
||||
it.getBoolean("exp/serverIdentityOptions/$userId/ignoreServerAvatar") == true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val attachmentView = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.StartActivityForResult(),
|
||||
|
|
@ -321,7 +340,7 @@ fun Message(
|
|||
username = User.resolveDefaultName(author),
|
||||
userId = author.id ?: message.id ?: ULID.makeSpecial(0),
|
||||
avatar = author.avatar,
|
||||
rawUrl = authorAvatarUrl(message),
|
||||
rawUrl = if (ignoreServerAvatar) null else authorAvatarUrl(message),
|
||||
onClick = onAvatarClick
|
||||
)
|
||||
}
|
||||
|
|
@ -333,7 +352,24 @@ fun Message(
|
|||
if (message.tail == false) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = webhookName ?: authorName(message),
|
||||
text = buildAnnotatedString {
|
||||
if (showUsernameDiscriminator) {
|
||||
pushStyle(
|
||||
SpanStyle(
|
||||
color = LocalContentColor.current.copy(
|
||||
alpha = 0.5f
|
||||
),
|
||||
fontWeight = FontWeight.Bold,
|
||||
textDecoration = TextDecoration.LineThrough
|
||||
)
|
||||
)
|
||||
}
|
||||
append(webhookName ?: authorName(message))
|
||||
if (showUsernameDiscriminator) {
|
||||
pop()
|
||||
append(" ${author.username ?: "<?>"}#${author.discriminator ?: "0000"}")
|
||||
}
|
||||
},
|
||||
style = LocalTextStyle.current.copy(
|
||||
fontWeight = FontWeight.Bold,
|
||||
brush = authorColour(message)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ class ExperimentsSettingsScreenViewModel : ViewModel() {
|
|||
viewModelScope.launch {
|
||||
useKotlinMdRendererChecked.value = Experiments.useKotlinBasedMarkdownRenderer.isEnabled
|
||||
usePolarChecked.value = Experiments.usePolar.isEnabled
|
||||
enableServerIdentityOptionsChecked.value =
|
||||
Experiments.enableServerIdentityOptions.isEnabled
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -84,6 +86,16 @@ class ExperimentsSettingsScreenViewModel : ViewModel() {
|
|||
usePolarChecked.value = value
|
||||
}
|
||||
}
|
||||
|
||||
val enableServerIdentityOptionsChecked = mutableStateOf(false)
|
||||
|
||||
fun setEnableServerIdentityOptionsChecked(value: Boolean) {
|
||||
viewModelScope.launch {
|
||||
kv.set("exp/enableServerIdentityOptions", value)
|
||||
Experiments.enableServerIdentityOptions.setEnabled(value)
|
||||
enableServerIdentityOptionsChecked.value = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -167,6 +179,22 @@ fun ExperimentsSettingsScreen(
|
|||
modifier = Modifier.clickable { viewModel.setUsePolarChecked(!viewModel.usePolarChecked.value) }
|
||||
)
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text("Server Identity Options")
|
||||
},
|
||||
supportingContent = {
|
||||
Text("Enable options to control what parts of others' server identities you want to see.")
|
||||
},
|
||||
trailingContent = {
|
||||
Switch(
|
||||
checked = viewModel.enableServerIdentityOptionsChecked.value,
|
||||
onCheckedChange = null
|
||||
)
|
||||
},
|
||||
modifier = Modifier.clickable { viewModel.setEnableServerIdentityOptionsChecked(!viewModel.enableServerIdentityOptionsChecked.value) }
|
||||
)
|
||||
|
||||
Subcategory(
|
||||
title = {
|
||||
Text("Disable experiments")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
package chat.revolt.sheets
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.revolt.api.RevoltAPI
|
||||
import chat.revolt.persistence.KVStorage
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
// Internal: Untranslated
|
||||
@Composable
|
||||
fun ServerIdentityOptionsSheet(userId: String) {
|
||||
val user = RevoltAPI.userCache[userId]
|
||||
|
||||
if (user == null) {
|
||||
Text("No such user")
|
||||
return
|
||||
}
|
||||
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val kv = remember { KVStorage(context) }
|
||||
|
||||
var showUsernameDiscriminator by remember { mutableStateOf(false) }
|
||||
var ignoreServerAvatar by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
showUsernameDiscriminator = kv.getBoolean(
|
||||
"exp/serverIdentityOptions/$userId/showUsernameDiscriminator"
|
||||
) == true
|
||||
ignoreServerAvatar = kv.getBoolean(
|
||||
"exp/serverIdentityOptions/$userId/ignoreServerAvatar"
|
||||
) == true
|
||||
}
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
modifier = Modifier.padding(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = "Identity Options",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Text(
|
||||
text = "${user.username}#${user.discriminator}",
|
||||
style = MaterialTheme.typography.titleMedium.copy(
|
||||
fontWeight = FontWeight.Normal
|
||||
),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
|
||||
Column {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text("Show Username#Tag next to nickname or display name")
|
||||
},
|
||||
trailingContent = {
|
||||
Switch(
|
||||
checked = showUsernameDiscriminator,
|
||||
onCheckedChange = null
|
||||
)
|
||||
},
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
kv.set(
|
||||
"exp/serverIdentityOptions/$userId/showUsernameDiscriminator",
|
||||
!showUsernameDiscriminator
|
||||
)
|
||||
showUsernameDiscriminator = !showUsernameDiscriminator
|
||||
}
|
||||
},
|
||||
colors = ListItemDefaults.colors().copy(
|
||||
containerColor = Color.Transparent,
|
||||
)
|
||||
)
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text("Ignore server avatar")
|
||||
},
|
||||
trailingContent = {
|
||||
Switch(
|
||||
checked = ignoreServerAvatar,
|
||||
onCheckedChange = null
|
||||
)
|
||||
},
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
kv.set(
|
||||
"exp/serverIdentityOptions/$userId/ignoreServerAvatar",
|
||||
!ignoreServerAvatar
|
||||
)
|
||||
ignoreServerAvatar = !ignoreServerAvatar
|
||||
}
|
||||
},
|
||||
colors = ListItemDefaults.colors().copy(
|
||||
containerColor = Color.Transparent,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -45,6 +45,7 @@ import chat.revolt.api.internals.ULID
|
|||
import chat.revolt.api.internals.solidColor
|
||||
import chat.revolt.api.routes.user.fetchUserProfile
|
||||
import chat.revolt.api.schemas.Profile
|
||||
import chat.revolt.api.settings.Experiments
|
||||
import chat.revolt.api.settings.FeatureFlags
|
||||
import chat.revolt.composables.chat.RoleListEntry
|
||||
import chat.revolt.composables.chat.UserBadgeList
|
||||
|
|
@ -120,6 +121,19 @@ fun UserInfoSheet(
|
|||
}
|
||||
}
|
||||
|
||||
var showServerIdentityOptions by remember { mutableStateOf(false) }
|
||||
if (showServerIdentityOptions) {
|
||||
val sheetState = rememberModalBottomSheetState(true)
|
||||
ModalBottomSheet(
|
||||
sheetState = sheetState,
|
||||
onDismissRequest = { showServerIdentityOptions = false }
|
||||
) {
|
||||
ServerIdentityOptionsSheet(
|
||||
userId = user.id!!
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LazyVerticalStaggeredGrid(
|
||||
columns = StaggeredGridCells.Fixed(2),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
|
|
@ -129,17 +143,31 @@ fun UserInfoSheet(
|
|||
item(key = "overview", span = StaggeredGridItemSpan.FullLine) {
|
||||
Box {
|
||||
RawUserOverview(user, profile, internalPadding = false)
|
||||
if (FeatureFlags.userCardsGranted) {
|
||||
SmallFloatingActionButton(
|
||||
onClick = { showUserCard = true },
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(top = 8.dp, end = 8.dp)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_badge_account_horizontal_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(top = 8.dp, end = 8.dp)
|
||||
) {
|
||||
if (Experiments.enableServerIdentityOptions.isEnabled) {
|
||||
SmallFloatingActionButton(
|
||||
onClick = { showServerIdentityOptions = true },
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.icn_psychology_alt_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (FeatureFlags.userCardsGranted) {
|
||||
SmallFloatingActionButton(
|
||||
onClick = { showUserCard = true },
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_badge_account_horizontal_24dp),
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M240,880L240,708Q183,656 151.5,586.5Q120,517 120,440Q120,290 225,185Q330,80 480,80Q605,80 701.5,153.5Q798,227 827,345L879,550Q884,569 872,584.5Q860,600 840,600L760,600L760,720Q760,753 736.5,776.5Q713,800 680,800L600,800L600,880L520,880L520,720L680,720Q680,720 680,720Q680,720 680,720L680,520L788,520L750,365Q727,274 652,217Q577,160 480,160Q364,160 282,241Q200,322 200,438Q200,498 224.5,552Q249,606 294,648L320,672L320,880L240,880ZM494,520L494,520L494,520Q494,520 494,520Q494,520 494,520Q494,520 494,520Q494,520 494,520Q494,520 494,520Q494,520 494,520L494,520L494,520L494,520Q494,520 494,520Q494,520 494,520L494,520L494,520ZM480,640Q497,640 508.5,628.5Q520,617 520,600Q520,583 508.5,571.5Q497,560 480,560Q463,560 451.5,571.5Q440,583 440,600Q440,617 451.5,628.5Q463,640 480,640ZM450,512L511,512Q511,487 517.5,471.5Q524,456 544,434Q562,414 579,393.5Q596,373 596,340Q596,298 563.5,269Q531,240 483,240Q443,240 410.5,263Q378,286 365,323L420,346Q427,324 444.5,310.5Q462,297 483,297Q505,297 519.5,309Q534,321 534,340Q534,361 521.5,377.5Q509,394 492,411Q472,432 461,453Q450,474 450,512Z"/>
|
||||
</vector>
|
||||
Loading…
Reference in New Issue