feat: add setting for special embeds

Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
Infi 2024-10-04 19:01:39 +02:00
parent 3b2a1172e9
commit ca14507720
21 changed files with 81 additions and 55 deletions

View File

@ -56,7 +56,7 @@ import chat.revolt.api.routes.invites.joinInviteByCode
import chat.revolt.api.schemas.Invite
import chat.revolt.api.schemas.InviteJoined
import chat.revolt.api.schemas.RsResult
import chat.revolt.api.settings.GlobalState
import chat.revolt.api.settings.LoadedSettings
import chat.revolt.api.settings.SyncedSettings
import chat.revolt.components.generic.IconPlaceholder
import chat.revolt.components.generic.RemoteImage
@ -147,7 +147,7 @@ fun InviteScreen(
val invite = viewModel.inviteResult?.value
RevoltTheme(
requestedTheme = GlobalState.theme,
requestedTheme = LoadedSettings.theme,
colourOverrides = SyncedSettings.android.colourOverrides
) {
Surface(

View File

@ -45,7 +45,7 @@ import chat.revolt.api.RevoltHttp
import chat.revolt.api.api
import chat.revolt.api.routes.onboard.needsOnboarding
import chat.revolt.api.settings.Experiments
import chat.revolt.api.settings.GlobalState
import chat.revolt.api.settings.LoadedSettings
import chat.revolt.api.settings.SyncedSettings
import chat.revolt.ndk.NativeLibraries
import chat.revolt.persistence.KVStorage
@ -305,7 +305,7 @@ fun AppEntrypoint(
val navController = rememberNavController()
RevoltTheme(
requestedTheme = GlobalState.theme,
requestedTheme = LoadedSettings.theme,
colourOverrides = SyncedSettings.android.colourOverrides
) {
Surface(

View File

@ -59,7 +59,7 @@ import chat.revolt.api.routes.microservices.autumn.FileArgs
import chat.revolt.api.routes.microservices.autumn.MAX_ATTACHMENTS_PER_MESSAGE
import chat.revolt.api.routes.microservices.autumn.uploadToAutumn
import chat.revolt.api.schemas.ChannelType
import chat.revolt.api.settings.GlobalState
import chat.revolt.api.settings.LoadedSettings
import chat.revolt.api.settings.SyncedSettings
import chat.revolt.components.chat.NativeMessageField
import chat.revolt.components.emoji.EmojiPicker
@ -279,7 +279,7 @@ fun ShareTargetScreen(
var selectedChannel by rememberSaveable { mutableStateOf<String?>(null) }
RevoltTheme(
requestedTheme = GlobalState.theme,
requestedTheme = LoadedSettings.theme,
colourOverrides = SyncedSettings.android.colourOverrides
) {
Scaffold(

View File

@ -46,7 +46,7 @@ import chat.revolt.R
import chat.revolt.api.REVOLT_FILES
import chat.revolt.api.RevoltHttp
import chat.revolt.api.schemas.AutumnResource
import chat.revolt.api.settings.GlobalState
import chat.revolt.api.settings.LoadedSettings
import chat.revolt.api.settings.SyncedSettings
import chat.revolt.providers.getAttachmentContentUri
import chat.revolt.ui.theme.RevoltTheme
@ -185,7 +185,7 @@ fun ImageViewScreen(resource: AutumnResource, onClose: () -> Unit = {}) {
}
RevoltTheme(
requestedTheme = GlobalState.theme,
requestedTheme = LoadedSettings.theme,
colourOverrides = SyncedSettings.android.colourOverrides
) {
Scaffold(

View File

@ -36,7 +36,7 @@ import chat.revolt.api.realtime.frames.sendable.PingFrame
import chat.revolt.api.routes.server.fetchMember
import chat.revolt.api.schemas.Channel
import chat.revolt.api.schemas.ChannelType
import chat.revolt.api.settings.GlobalState
import chat.revolt.api.settings.LoadedSettings
import chat.revolt.api.settings.SyncedSettings
import chat.revolt.c2dm.ChannelRegistrator
import chat.revolt.persistence.Database
@ -731,7 +731,7 @@ object RealtimeSocket {
"Authenticated" -> {
SyncedSettings.fetch()
GlobalState.hydrateWithSettings(SyncedSettings)
LoadedSettings.hydrateWithSettings(SyncedSettings)
}
else -> {

View File

@ -8,6 +8,20 @@ data class OrderingSettings(
val servers: List<String> = emptyList()
)
@Serializable
data class AndroidSpecificSettingsSpecialEmbedSettings(
/**
* Whether to embed YouTube videos interactively.
* Boolean.
*/
val embedYouTube: Boolean = true,
/**
* Whether to embed Apple Music albums and tracks interactively.
* Boolean.
*/
val embedAppleMusic: Boolean = true
)
@Serializable
data class AndroidSpecificSettings(
/**
@ -29,5 +43,10 @@ data class AndroidSpecificSettings(
* Avatar radius.
* Must be integer in range 0..50 inclusive.
*/
var avatarRadius: Int? = null
var avatarRadius: Int? = null,
/**
* Controls preferences for special embeds.
* Object; See [AndroidSpecificSettingsSpecialEmbedSettings] for format.
*/
var specialEmbedSettings: AndroidSpecificSettingsSpecialEmbedSettings? = null
)

View File

@ -10,7 +10,7 @@ import chat.revolt.persistence.KVStorage
class ExperimentInstance(default: Boolean) {
private var _isEnabled by mutableStateOf(default)
val isEnabled: Boolean
get() = GlobalState.experimentsEnabled && _isEnabled
get() = LoadedSettings.experimentsEnabled && _isEnabled
fun setEnabled(enabled: Boolean) {
_isEnabled = enabled
@ -32,9 +32,9 @@ object Experiments {
val kvStorage = KVStorage(RevoltApplication.instance)
if (BuildConfig.DEBUG) {
GlobalState.experimentsEnabled = true
LoadedSettings.experimentsEnabled = true
} else {
GlobalState.experimentsEnabled = kvStorage.getBoolean("experimentsEnabled") ?: false
LoadedSettings.experimentsEnabled = kvStorage.getBoolean("experimentsEnabled") ?: false
}
useKotlinBasedMarkdownRenderer.setEnabled(

View File

@ -4,6 +4,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import chat.revolt.api.schemas.AndroidSpecificSettingsSpecialEmbedSettings
import chat.revolt.ui.theme.Theme
import chat.revolt.ui.theme.getDefaultTheme
@ -13,11 +14,14 @@ enum class MessageReplyStyle {
DoubleTap
}
object GlobalState {
typealias SpecialEmbedSettings = AndroidSpecificSettingsSpecialEmbedSettings
object LoadedSettings {
var theme by mutableStateOf(getDefaultTheme())
var messageReplyStyle by mutableStateOf(MessageReplyStyle.SwipeFromEnd)
var avatarRadius by mutableIntStateOf(50)
var experimentsEnabled by mutableStateOf(false)
var specialEmbedSettings by mutableStateOf(SpecialEmbedSettings())
fun hydrateWithSettings(settings: SyncedSettings) {
this.theme = settings.android.theme?.let { Theme.valueOf(it) } ?: getDefaultTheme()
@ -25,11 +29,13 @@ object GlobalState {
settings.android.messageReplyStyle?.let { MessageReplyStyle.valueOf(it) }
?: MessageReplyStyle.SwipeFromEnd
this.avatarRadius = settings.android.avatarRadius ?: 50
this.specialEmbedSettings = settings.android.specialEmbedSettings ?: SpecialEmbedSettings()
}
fun reset() {
theme = getDefaultTheme()
messageReplyStyle = MessageReplyStyle.SwipeFromEnd
avatarRadius = 50
specialEmbedSettings = SpecialEmbedSettings()
}
}

View File

@ -30,7 +30,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import chat.revolt.R
import chat.revolt.api.realtime.DisconnectionState
import chat.revolt.api.settings.GlobalState
import chat.revolt.api.settings.LoadedSettings
import chat.revolt.ui.theme.Theme
private val NON_MATERIAL_COLOURS = mapOf(
@ -108,7 +108,7 @@ fun DisconnectedNotice(state: DisconnectionState, onReconnect: () -> Unit) {
DisconnectionState.Connected to (MaterialTheme.colorScheme.primary to MaterialTheme.colorScheme.onPrimary)
)
val (background, foreground) = when (GlobalState.theme) {
val (background, foreground) = when (LoadedSettings.theme) {
Theme.M3Dynamic -> materialColours[state] ?: (Color.Unspecified to Color.Unspecified)
else -> NON_MATERIAL_COLOURS[state] ?: (Color.Unspecified to Color.Unspecified)
}

View File

@ -70,7 +70,7 @@ import chat.revolt.api.routes.microservices.january.asJanuaryProxyUrl
import chat.revolt.api.schemas.AutumnResource
import chat.revolt.api.schemas.User
import chat.revolt.api.settings.Experiments
import chat.revolt.api.settings.GlobalState
import chat.revolt.api.settings.LoadedSettings
import chat.revolt.api.settings.MessageReplyStyle
import chat.revolt.callbacks.Action
import chat.revolt.callbacks.ActionChannel
@ -275,7 +275,7 @@ fun Message(
.combinedClickable(
onClick = {},
onDoubleClick = {
if (canReply && GlobalState.messageReplyStyle == MessageReplyStyle.DoubleTap) {
if (canReply && LoadedSettings.messageReplyStyle == MessageReplyStyle.DoubleTap) {
onReply()
}
},

View File

@ -12,13 +12,13 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import chat.revolt.api.schemas.Special
import chat.revolt.api.settings.GlobalState
import chat.revolt.api.settings.LoadedSettings
import chat.revolt.ui.theme.isThemeDark
@SuppressLint("SetJavaScriptEnabled")
@Composable
fun AppleMusicEmbed(special: Special, modifier: Modifier = Modifier) {
val useDarkTheme = isThemeDark(GlobalState.theme)
val useDarkTheme = isThemeDark(LoadedSettings.theme)
AndroidView(
factory = { ctx ->

View File

@ -30,7 +30,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import chat.revolt.R
import chat.revolt.api.settings.GlobalState
import chat.revolt.api.settings.LoadedSettings
import com.bumptech.glide.integration.compose.CrossFade
import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi
import com.bumptech.glide.integration.compose.GlideImage
@ -139,7 +139,7 @@ fun InlineMediaPickerMediaPicker(
Modifier
.then(
if (useAvatarCircularity) {
Modifier.clip(RoundedCornerShape(GlobalState.avatarRadius))
Modifier.clip(RoundedCornerShape(LoadedSettings.avatarRadius))
} else {
Modifier.clip(CircleShape)
}
@ -162,7 +162,7 @@ fun InlineMediaPickerMediaPicker(
Modifier
.then(
if (useAvatarCircularity) {
Modifier.clip(RoundedCornerShape(GlobalState.avatarRadius))
Modifier.clip(RoundedCornerShape(LoadedSettings.avatarRadius))
} else {
Modifier.clip(CircleShape)
}

View File

@ -11,14 +11,14 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.viewinterop.AndroidView
import chat.revolt.api.settings.GlobalState
import chat.revolt.api.settings.LoadedSettings
import chat.revolt.ui.theme.Theme
import com.google.android.material.tabs.TabLayout
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PrimaryTabs(tabs: List<String>, currentIndex: Int, onTabSelected: (Int) -> Unit) {
when (GlobalState.theme) {
when (LoadedSettings.theme) {
Theme.M3Dynamic -> AndroidView(
factory = {
TabLayout(it).apply {

View File

@ -25,7 +25,7 @@ import chat.revolt.R
import chat.revolt.api.REVOLT_BASE
import chat.revolt.api.REVOLT_FILES
import chat.revolt.api.schemas.AutumnResource
import chat.revolt.api.settings.GlobalState
import chat.revolt.api.settings.LoadedSettings
enum class Presence {
Online,
@ -103,7 +103,7 @@ fun UserAvatar(
contentScale = ContentScale.Crop,
description = stringResource(id = R.string.avatar_alt, username),
modifier = Modifier
.clip(RoundedCornerShape(GlobalState.avatarRadius))
.clip(RoundedCornerShape(LoadedSettings.avatarRadius))
.size(size)
.then(
if (onLongClick != null || onClick != null) {
@ -122,7 +122,7 @@ fun UserAvatar(
url = "$REVOLT_BASE/users/${userId.ifBlank { "0".repeat(26) }}/default_avatar",
description = stringResource(id = R.string.avatar_alt, username),
modifier = Modifier
.clip(RoundedCornerShape(GlobalState.avatarRadius))
.clip(RoundedCornerShape(LoadedSettings.avatarRadius))
.size(size)
.then(
if (onLongClick != null || onClick != null) {

View File

@ -51,7 +51,7 @@ import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.revolt.R
import chat.revolt.api.settings.GlobalState
import chat.revolt.api.settings.LoadedSettings
import chat.revolt.components.markdown.Annotations
import chat.revolt.components.utils.detectTapGesturesConditionalConsume
import chat.revolt.ui.theme.FragmentMono
@ -427,7 +427,8 @@ private fun JBMCodeBlockContent(node: ASTNode, modifier: Modifier) {
val uiMode = LocalConfiguration.current.uiMode
val systemIsDark =
(uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
val themeIsDark = remember(GlobalState.theme) { isThemeDark(GlobalState.theme, systemIsDark) }
val themeIsDark =
remember(LoadedSettings.theme) { isThemeDark(LoadedSettings.theme, systemIsDark) }
val codeFenceLanguage = remember(node) {
node.children.firstOrNull { it.type == MarkdownTokenTypes.FENCE_LANG }

View File

@ -16,7 +16,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import chat.revolt.api.settings.GlobalState
import chat.revolt.api.settings.LoadedSettings
enum class MessageSkeletonVariant {
One,
@ -35,7 +35,7 @@ fun MessageSkeleton(variant: MessageSkeletonVariant, modifier: Modifier = Modifi
Spacer(modifier = Modifier.height(4.dp))
Box(
Modifier
.clip(RoundedCornerShape(GlobalState.avatarRadius))
.clip(RoundedCornerShape(LoadedSettings.avatarRadius))
.size(40.dp)
.background(skeletonColourOnBackground())
)

View File

@ -63,7 +63,7 @@ import androidx.navigation.NavController
import chat.revolt.R
import chat.revolt.api.RevoltCbor
import chat.revolt.api.RevoltJson
import chat.revolt.api.settings.GlobalState
import chat.revolt.api.settings.LoadedSettings
import chat.revolt.api.settings.SyncedSettings
import chat.revolt.components.generic.ListHeader
import chat.revolt.components.screens.settings.appearance.ColourChip
@ -94,14 +94,14 @@ class AppearanceSettingsScreenViewModel @Inject constructor(
var overridePickerSheetVisible by mutableStateOf(false)
fun saveNewTheme(theme: Theme) {
GlobalState.theme = theme
LoadedSettings.theme = theme
viewModelScope.launch {
SyncedSettings.updateAndroid(SyncedSettings.android.copy(theme = theme.name))
}
}
fun saveNewAvatarRadius(radius: Int) {
GlobalState.avatarRadius = radius
LoadedSettings.avatarRadius = radius
viewModelScope.launch {
SyncedSettings.updateAndroid(SyncedSettings.android.copy(avatarRadius = radius))
}
@ -322,7 +322,7 @@ fun AppearanceSettingsScreen(
ColourChip(
color = Color(0xff191919),
text = stringResource(id = R.string.settings_appearance_theme_revolt),
selected = GlobalState.theme == Theme.Revolt,
selected = LoadedSettings.theme == Theme.Revolt,
modifier = Modifier
.weight(1f)
.testTag("set_theme_revolt")
@ -333,7 +333,7 @@ fun AppearanceSettingsScreen(
ColourChip(
color = Color(0xfff7f7f7),
text = stringResource(id = R.string.settings_appearance_theme_light),
selected = GlobalState.theme == Theme.Light,
selected = LoadedSettings.theme == Theme.Light,
modifier = Modifier
.weight(1f)
.testTag("set_theme_light")
@ -344,7 +344,7 @@ fun AppearanceSettingsScreen(
ColourChip(
color = Color(0xff000000),
text = stringResource(id = R.string.settings_appearance_theme_amoled),
selected = GlobalState.theme == Theme.Amoled,
selected = LoadedSettings.theme == Theme.Amoled,
modifier = Modifier
.weight(1f)
.testTag("set_theme_amoled")
@ -355,7 +355,7 @@ fun AppearanceSettingsScreen(
ColourChip(
color = if (isSystemInDarkTheme()) Color(0xff191919) else Color(0xfff7f7f7),
text = stringResource(id = R.string.settings_appearance_theme_none),
selected = GlobalState.theme == Theme.None,
selected = LoadedSettings.theme == Theme.None,
modifier = Modifier
.weight(1f)
.testTag("set_theme_none")
@ -367,7 +367,7 @@ fun AppearanceSettingsScreen(
ColourChip(
color = dynamicDarkColorScheme(LocalContext.current).primary,
text = stringResource(id = R.string.settings_appearance_theme_m3dynamic),
selected = GlobalState.theme == Theme.M3Dynamic,
selected = LoadedSettings.theme == Theme.M3Dynamic,
modifier = Modifier
.weight(1f)
.testTag("set_theme_m3dynamic")
@ -408,7 +408,7 @@ fun AppearanceSettingsScreen(
Box(Modifier.padding(horizontal = 16.dp)) {
CornerRadiusPicker(
percentage = GlobalState.avatarRadius,
percentage = LoadedSettings.avatarRadius,
onUpdate = {
viewModel.saveNewAvatarRadius(it)
}

View File

@ -26,7 +26,7 @@ import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import chat.revolt.R
import chat.revolt.api.settings.GlobalState
import chat.revolt.api.settings.LoadedSettings
import chat.revolt.api.settings.MessageReplyStyle
import chat.revolt.api.settings.SyncedSettings
import chat.revolt.components.generic.ListHeader
@ -37,7 +37,7 @@ class ChatSettingsScreenViewModel : ViewModel() {
fun updateMessageReplyStyle(next: MessageReplyStyle) {
viewModelScope.launch {
SyncedSettings.updateAndroid(SyncedSettings.android.copy(messageReplyStyle = next.name))
GlobalState.messageReplyStyle = next
LoadedSettings.messageReplyStyle = next
}
}
}
@ -91,17 +91,17 @@ fun ChatSettingsScreen(
Column(Modifier.selectableGroup()) {
RadioItem(
selected = GlobalState.messageReplyStyle == MessageReplyStyle.None,
selected = LoadedSettings.messageReplyStyle == MessageReplyStyle.None,
onClick = { viewModel.updateMessageReplyStyle(MessageReplyStyle.None) },
label = { Text(text = stringResource(R.string.settings_chat_quick_reply_none)) }
)
RadioItem(
selected = GlobalState.messageReplyStyle == MessageReplyStyle.SwipeFromEnd,
selected = LoadedSettings.messageReplyStyle == MessageReplyStyle.SwipeFromEnd,
onClick = { viewModel.updateMessageReplyStyle(MessageReplyStyle.SwipeFromEnd) },
label = { Text(text = stringResource(R.string.settings_chat_quick_reply_swipe_from_end)) }
)
RadioItem(
selected = GlobalState.messageReplyStyle == MessageReplyStyle.DoubleTap,
selected = LoadedSettings.messageReplyStyle == MessageReplyStyle.DoubleTap,
onClick = { viewModel.updateMessageReplyStyle(MessageReplyStyle.DoubleTap) },
label = { Text(text = stringResource(R.string.settings_chat_quick_reply_double_tap)) }
)

View File

@ -17,7 +17,7 @@ import androidx.navigation.NavController
import chat.revolt.BuildConfig
import chat.revolt.RevoltApplication
import chat.revolt.api.settings.Experiments
import chat.revolt.api.settings.GlobalState
import chat.revolt.api.settings.LoadedSettings
import chat.revolt.persistence.KVStorage
import chat.revolt.settings.dsl.SettingsPage
import chat.revolt.settings.dsl.SubcategoryContentInsets
@ -29,7 +29,7 @@ class ExperimentsSettingsScreenViewModel : ViewModel() {
fun disableExperiments(then: () -> Unit = {}) {
viewModelScope.launch {
kv.remove("experimentsEnabled")
GlobalState.experimentsEnabled = false
LoadedSettings.experimentsEnabled = false
then()
}
}

View File

@ -44,7 +44,7 @@ import chat.revolt.R
import chat.revolt.activities.InviteActivity
import chat.revolt.api.RevoltAPI
import chat.revolt.api.settings.FeatureFlags
import chat.revolt.api.settings.GlobalState
import chat.revolt.api.settings.LoadedSettings
import chat.revolt.components.generic.ListHeader
import chat.revolt.components.screens.settings.SelfUserOverview
import chat.revolt.persistence.KVStorage
@ -59,7 +59,7 @@ class SettingsScreenViewModel @Inject constructor(
fun logout() {
runBlocking {
kvStorage.remove("sessionToken")
GlobalState.reset()
LoadedSettings.reset()
RevoltAPI.logout()
}
}
@ -261,7 +261,7 @@ fun SettingsScreen(
)
}
if (GlobalState.experimentsEnabled) {
if (LoadedSettings.experimentsEnabled) {
ListItem(
headlineContent = {
Text(

View File

@ -50,7 +50,7 @@ import chat.revolt.api.routes.custom.fetchEmoji
import chat.revolt.api.routes.user.fetchUser
import chat.revolt.api.schemas.Emoji
import chat.revolt.api.schemas.User
import chat.revolt.api.settings.GlobalState
import chat.revolt.api.settings.LoadedSettings
import chat.revolt.components.chat.MemberListItem
import chat.revolt.components.generic.RemoteImage
import chat.revolt.components.generic.SheetEnd
@ -151,7 +151,7 @@ fun ReactionInfoSheet(messageId: String, emoji: String, onDismiss: () -> Unit) {
tapCount++
if (tapCount > 9) {
tapCount = 0
if (GlobalState.experimentsEnabled) {
if (LoadedSettings.experimentsEnabled) {
showEnabledAlreadyAlert = true
} else {
showEnabledConfirmAlert = true
@ -182,7 +182,7 @@ fun ReactionInfoSheet(messageId: String, emoji: String, onDismiss: () -> Unit) {
confirmButton = {
TextButton(onClick = {
showEnabledConfirmAlert = false
GlobalState.experimentsEnabled = true
LoadedSettings.experimentsEnabled = true
scope.launch {
KVStorage(context).set("experimentsEnabled", true)
}