feat: feature section search bar

This commit is contained in:
rhunk 2023-08-18 11:03:14 +02:00
parent f0df0045d6
commit b0dfcd5470
2 changed files with 127 additions and 10 deletions

View File

@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@ -20,9 +21,12 @@ import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.FolderOpen import androidx.compose.material.icons.filled.FolderOpen
import androidx.compose.material.icons.filled.OpenInNew import androidx.compose.material.icons.filled.OpenInNew
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.rounded.Save import androidx.compose.material.icons.rounded.Save
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
@ -35,13 +39,18 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.Switch import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.rememberBottomSheetScaffoldState import androidx.compose.material3.rememberBottomSheetScaffoldState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
@ -50,12 +59,17 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.navigation.NavGraphBuilder import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.navigation import androidx.navigation.navigation
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.rhunk.snapenhance.core.config.ConfigContainer import me.rhunk.snapenhance.core.config.ConfigContainer
import me.rhunk.snapenhance.core.config.DataProcessors import me.rhunk.snapenhance.core.config.DataProcessors
import me.rhunk.snapenhance.core.config.PropertyKey
import me.rhunk.snapenhance.core.config.PropertyPair import me.rhunk.snapenhance.core.config.PropertyPair
import me.rhunk.snapenhance.core.config.PropertyValue
import me.rhunk.snapenhance.ui.manager.Section import me.rhunk.snapenhance.ui.manager.Section
import me.rhunk.snapenhance.ui.util.ChooseFolderHelper import me.rhunk.snapenhance.ui.util.ChooseFolderHelper
@ -64,7 +78,8 @@ class FeaturesSection : Section() {
companion object { companion object {
const val MAIN_ROUTE = "feature_root" const val MAIN_ROUTE = "feature_root"
const val FEATURE_CONTAINER_ROOT = "feature_container/{name}" const val FEATURE_CONTAINER_ROUTE = "feature_container/{name}"
const val SEARCH_FEATURE_ROUTE = "search_feature/{keyword}"
} }
private lateinit var openFolderCallback: (uri: String) -> Unit private lateinit var openFolderCallback: (uri: String) -> Unit
@ -86,6 +101,17 @@ class FeaturesSection : Section() {
containers containers
} }
private val allProperties by lazy {
val properties = mutableMapOf<PropertyKey<*>, PropertyValue<*>>()
allContainers.values.forEach {
val container = it.value.get() as ConfigContainer
container.properties.forEach { property ->
properties[property.key] = property.value
}
}
properties
}
override fun init() { override fun init() {
openFolderLauncher = ChooseFolderHelper.createChooseFolder(context.activity!! as ComponentActivity) { openFolderLauncher = ChooseFolderHelper.createChooseFolder(context.activity!! as ComponentActivity) {
openFolderCallback(it) openFolderCallback(it)
@ -122,13 +148,25 @@ class FeaturesSection : Section() {
Container(context.config.root) Container(context.config.root)
} }
composable(FEATURE_CONTAINER_ROOT) { backStackEntry -> composable(FEATURE_CONTAINER_ROUTE) { backStackEntry ->
backStackEntry.arguments?.getString("name")?.let { containerName -> backStackEntry.arguments?.getString("name")?.let { containerName ->
allContainers[containerName]?.let { allContainers[containerName]?.let {
Container(it.value.get() as ConfigContainer) Container(it.value.get() as ConfigContainer)
} }
} }
} }
composable(SEARCH_FEATURE_ROUTE) { backStackEntry ->
backStackEntry.arguments?.getString("keyword")?.let { keyword ->
val properties = allProperties.filter {
it.key.name.contains(keyword, ignoreCase = true) ||
context.translation["${it.key.propertyTranslationPath()}.name"].contains(keyword, ignoreCase = true) ||
context.translation["${it.key.propertyTranslationPath()}.description"].contains(keyword, ignoreCase = true)
}.map { PropertyPair(it.key, it.value) }
PropertiesView(properties)
}
}
} }
} }
@ -228,7 +266,7 @@ class FeaturesSection : Section() {
val container = propertyValue.get() as ConfigContainer val container = propertyValue.get() as ConfigContainer
registerClickCallback { registerClickCallback {
navController.navigate(FEATURE_CONTAINER_ROOT.replace("{name}", property.name)) navController.navigate(FEATURE_CONTAINER_ROUTE.replace("{name}", property.name))
} }
if (container.globalState == null) return if (container.globalState == null) return
@ -341,16 +379,84 @@ class FeaturesSection : Section() {
} }
} }
@Composable
private fun FeatureSearchBar(rowScope: RowScope, focusRequester: FocusRequester) {
val searchValue = remember { mutableStateOf("") }
val scope = rememberCoroutineScope()
val currentSearchJob = remember { mutableStateOf<Job?>(null) }
rowScope.apply {
TextField(
value = searchValue.value,
onValueChange = { keyword ->
searchValue.value = keyword
if (keyword.isEmpty()) {
navController.navigate(MAIN_ROUTE)
return@TextField
}
currentSearchJob.value?.cancel()
scope.launch {
delay(300)
navController.navigate(SEARCH_FEATURE_ROUTE.replace("{keyword}", keyword), NavOptions.Builder()
.setLaunchSingleTop(true)
.setPopUpTo(MAIN_ROUTE, false)
.build()
)
}.also { currentSearchJob.value = it }
},
keyboardActions = KeyboardActions(onDone = {
focusRequester.freeFocus()
}),
modifier = Modifier
.focusRequester(focusRequester)
.weight(1f, fill = true)
.padding(end = 10.dp)
.height(70.dp),
singleLine = true,
colors = TextFieldDefaults.colors(
unfocusedContainerColor = MaterialTheme.colorScheme.surface,
focusedContainerColor = MaterialTheme.colorScheme.surface,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
cursorColor = MaterialTheme.colorScheme.primary
)
)
}
}
@Composable
override fun TopBarActions(rowScope: RowScope) {
val showSearchBar = remember { mutableStateOf(false) }
val focusRequester = remember { FocusRequester() }
if (showSearchBar.value) {
FeatureSearchBar(rowScope, focusRequester)
LaunchedEffect(true) {
focusRequester.requestFocus()
}
}
IconButton(onClick = {
showSearchBar.value = showSearchBar.value.not()
if (!showSearchBar.value && navController.currentBackStackEntry?.destination?.route == SEARCH_FEATURE_ROUTE) {
navController.navigate(MAIN_ROUTE)
}
}) {
Icon(
imageVector = if (showSearchBar.value) Icons.Filled.Close
else Icons.Filled.Search,
contentDescription = null
)
}
}
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun Container( private fun PropertiesView(
configContainer: ConfigContainer properties: List<PropertyPair<*>>
) { ) {
val properties = remember {
configContainer.properties.map { PropertyPair(it.key, it.value) }
}
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val scaffoldState = rememberBottomSheetScaffoldState() val scaffoldState = rememberBottomSheetScaffoldState()
Scaffold( Scaffold(
@ -392,4 +498,15 @@ class FeaturesSection : Section() {
) )
} }
@Composable
private fun Container(
configContainer: ConfigContainer
) {
val properties = remember {
configContainer.properties.map { PropertyPair(it.key, it.value) }
}
PropertiesView(properties)
}
} }

View File

@ -332,7 +332,7 @@
"progress": "Progress", "progress": "Progress",
"failure": "Failure" "failure": "Failure"
}, },
"auto_save_messages": { "auto_save_messages_in_conversations": {
"NOTE": "Audio Note", "NOTE": "Audio Note",
"CHAT": "Chat", "CHAT": "Chat",
"EXTERNAL_MEDIA": "External Media", "EXTERNAL_MEDIA": "External Media",