diff --git a/app/src/main/java/chat/revolt/api/settings/Experiments.kt b/app/src/main/java/chat/revolt/api/settings/Experiments.kt index 4bd755dc..818d997e 100644 --- a/app/src/main/java/chat/revolt/api/settings/Experiments.kt +++ b/app/src/main/java/chat/revolt/api/settings/Experiments.kt @@ -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 + ) } } \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/composables/chat/Message.kt b/app/src/main/java/chat/revolt/composables/chat/Message.kt index 83665575..7869f12c 100644 --- a/app/src/main/java/chat/revolt/composables/chat/Message.kt +++ b/app/src/main/java/chat/revolt/composables/chat/Message.kt @@ -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(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) diff --git a/app/src/main/java/chat/revolt/screens/settings/ExperimentsSettingsScreen.kt b/app/src/main/java/chat/revolt/screens/settings/ExperimentsSettingsScreen.kt index 7fd576af..5b931589 100644 --- a/app/src/main/java/chat/revolt/screens/settings/ExperimentsSettingsScreen.kt +++ b/app/src/main/java/chat/revolt/screens/settings/ExperimentsSettingsScreen.kt @@ -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") diff --git a/app/src/main/java/chat/revolt/sheets/ServerIdentityOptionsSheet.kt b/app/src/main/java/chat/revolt/sheets/ServerIdentityOptionsSheet.kt new file mode 100644 index 00000000..65004869 --- /dev/null +++ b/app/src/main/java/chat/revolt/sheets/ServerIdentityOptionsSheet.kt @@ -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, + ) + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/sheets/UserInfoSheet.kt b/app/src/main/java/chat/revolt/sheets/UserInfoSheet.kt index ddc86376..267841aa 100644 --- a/app/src/main/java/chat/revolt/sheets/UserInfoSheet.kt +++ b/app/src/main/java/chat/revolt/sheets/UserInfoSheet.kt @@ -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 + ) + } } } } diff --git a/app/src/main/res/drawable/icn_psychology_alt_24dp.xml b/app/src/main/res/drawable/icn_psychology_alt_24dp.xml new file mode 100644 index 00000000..fcd2b1a1 --- /dev/null +++ b/app/src/main/res/drawable/icn_psychology_alt_24dp.xml @@ -0,0 +1,10 @@ + + +