feat(labs): experiment with a settings DSL

Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
Infi 2024-08-10 21:20:36 +02:00
parent afbdfc6bb4
commit aa186719d9
4 changed files with 174 additions and 0 deletions

View File

@ -157,6 +157,15 @@ fun LabsHomeScreen(navController: NavController) {
}
)
HorizontalDivider()
ListItem(
headlineContent = {
Text("Settings DSL")
},
modifier = Modifier.clickable {
navController.navigate("sandboxes/settingsdsl")
}
)
HorizontalDivider()
}
}
}

View File

@ -14,6 +14,7 @@ import androidx.navigation.compose.rememberNavController
import chat.revolt.api.settings.FeatureFlags
import chat.revolt.screens.labs.ui.mockups.CallScreenMockup
import chat.revolt.screens.labs.ui.sandbox.CryptographicAgeVerificationSandbox
import chat.revolt.screens.labs.ui.sandbox.SettingsDslSandbox
annotation class LabsFeature
@ -67,6 +68,9 @@ fun LabsRootScreen(topNav: NavController) {
composable("sandboxes/cryptoageverif") {
CryptographicAgeVerificationSandbox(labsNav)
}
composable("sandboxes/settingsdsl") {
SettingsDslSandbox(labsNav)
}
}
}
}

View File

@ -0,0 +1,44 @@
package chat.revolt.screens.labs.ui.sandbox
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.text.style.TextOverflow
import androidx.navigation.NavController
import chat.revolt.settings.dsl.SettingsPage
import chat.revolt.settings.dsl.SubcategoryContentInsets
enum class SettingsDslSandboxTab {
General,
Appearance,
}
@Composable
fun SettingsDslSandbox(navController: NavController) {
SettingsPage(
navController = navController,
title = {
Text(
text = "Settings DSL Demo",
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
) {
Subcategory(
title = { Text("General") },
contentInsets = SubcategoryContentInsets
) {
Text(text = "General settings")
}
Subcategory(
title = { Text("Appearance") }
) {
RadioOptions(
options = SettingsDslSandboxTab.entries,
selectedOption = SettingsDslSandboxTab.General,
onOptionSelected = { }
)
}
}
}

View File

@ -0,0 +1,117 @@
package chat.revolt.settings.dsl
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.selection.selectableGroup
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LargeTopAppBar
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import chat.revolt.R
import chat.revolt.components.generic.ListHeader
import chat.revolt.components.generic.RadioItem
import kotlin.enums.EnumEntries
val SubcategoryContentInsets = PaddingValues(horizontal = 16.dp)
interface SettingsPageScope {
@Composable
fun Subcategory(
title: @Composable () -> Unit,
contentInsets: PaddingValues,
content: @Composable SettingsPageScope.() -> Unit
) {
ListHeader {
title()
}
Column(
Modifier
.padding(contentInsets)
) {
content()
}
}
@Composable
fun Subcategory(
title: @Composable () -> Unit,
content: @Composable SettingsPageScope.() -> Unit
) = Subcategory(
title = title,
contentInsets = PaddingValues(0.dp),
content = content
)
@Composable
fun <X : Enum<X>> RadioOptions(
options: EnumEntries<X>,
selectedOption: X,
onOptionSelected: (X) -> Unit
) {
Column(Modifier.selectableGroup()) {
for (option in options) {
RadioItem(
selected = option == selectedOption,
onClick = { onOptionSelected(option) },
label = { Text(option.toString()) }
)
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SettingsPage(
navController: NavController?,
title: @Composable () -> Unit,
content: @Composable SettingsPageScope.() -> Unit
) {
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
LargeTopAppBar(
scrollBehavior = scrollBehavior,
title = title,
navigationIcon = {
navController?.let {
IconButton(onClick = {
navController.popBackStack()
}) {
Icon(
imageVector = Icons.AutoMirrored.Default.ArrowBack,
contentDescription = stringResource(id = R.string.back)
)
}
}
},
)
},
) { pv ->
Column(
Modifier
.padding(pv)
.fillMaxSize()
) {
content(
object : SettingsPageScope {}
)
}
}
}