feat: Configuration module host selection improvements.
This commit is contained in:
parent
a60078e7af
commit
e4627b6ff3
|
@ -39,6 +39,7 @@ open class ConfigurationUseCase(
|
|||
val isAdvanced: Boolean = true,
|
||||
val isPreserved: Boolean = false,
|
||||
val value: Any? = "",
|
||||
val isSearchable: Boolean = false,
|
||||
val fetcher: (suspend (String) -> Any)? = null,
|
||||
)
|
||||
|
||||
|
|
|
@ -35,7 +35,13 @@ class EnvironmentConfigurationUseCase @Inject constructor(
|
|||
contentResolverConfigManager = contentResolverConfigManager,
|
||||
configClass = EnvironmentConfiguration::class,
|
||||
supportedContractFieldSet = setOf(
|
||||
ConfigField(ConfigContract::host.name, isAdvanced = false, isPreserved = true, value = defaultConfig.host),
|
||||
ConfigField(
|
||||
ConfigContract::host.name,
|
||||
isAdvanced = false,
|
||||
isPreserved = true,
|
||||
value = defaultConfig.host,
|
||||
isSearchable = true
|
||||
),
|
||||
ConfigField(ConfigContract::proxyToken.name, isAdvanced = false, isPreserved = true) {
|
||||
quark.baseUrl(appConfig.proxyUrl).getProxyToken() ?: error("Could not obtain proxy token")
|
||||
},
|
||||
|
|
|
@ -22,6 +22,7 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
|
@ -67,8 +68,7 @@ fun ConfigurationScreen(
|
|||
},
|
||||
onConfigurationFieldFetch = {
|
||||
configViewModel.perform(ConfigurationScreenViewModel.Action.FetchConfigField(it))
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
configViewModel.errorFlow.collect {
|
||||
|
@ -127,24 +127,55 @@ private fun ConfigurationFields(
|
|||
) {
|
||||
configFields.forEach { configField ->
|
||||
val fetchAction = configField.fetcher?.let { { onConfigurationFieldFetch(configField.name) } }
|
||||
when (configField.value) {
|
||||
is String -> ConfigurationTextField(
|
||||
configField = configField,
|
||||
onValueChange = { newValue ->
|
||||
onFieldUpdate(configField.name, newValue)
|
||||
},
|
||||
fetchAction = fetchAction
|
||||
)
|
||||
if (configField.isSearchable) {
|
||||
var showingSearchView by remember { mutableStateOf(true) }
|
||||
var selectedResult by remember { mutableStateOf("") }
|
||||
val onResultSelected: (String) -> Unit = { result ->
|
||||
selectedResult = result
|
||||
onFieldUpdate(configField.name, handleDomain(result)) // Update immediately
|
||||
showingSearchView = false // Hide the SearchView after a result is selected
|
||||
}
|
||||
val onDismissRequest: () -> Unit = {
|
||||
showingSearchView = false // Action to take on dismissing the search
|
||||
}
|
||||
|
||||
is Boolean -> ConfigurationCheckbox(
|
||||
configField = configField,
|
||||
onCheckChanged = { newValue ->
|
||||
onFieldUpdate(configField.name, newValue)
|
||||
}
|
||||
)
|
||||
val domains = LocalContext.current.resources.getStringArray(R.array.domains)
|
||||
|
||||
null -> Unit
|
||||
else -> error("Unsupported configuration field type for key ${configField.name}")
|
||||
if (showingSearchView) {
|
||||
SearchableConfigurationTextField(
|
||||
searchData = domains.toMutableList(),
|
||||
onResultSelected = onResultSelected,
|
||||
onDismissRequest = onDismissRequest
|
||||
)
|
||||
} else {
|
||||
ConfigurationTextField(
|
||||
configField = configField,
|
||||
onValueChange = {
|
||||
showingSearchView = true
|
||||
},
|
||||
fetchAction = fetchAction
|
||||
)
|
||||
}
|
||||
} else {
|
||||
when (configField.value) {
|
||||
is String -> ConfigurationTextField(
|
||||
configField = configField,
|
||||
onValueChange = { newValue ->
|
||||
onFieldUpdate(configField.name, newValue)
|
||||
},
|
||||
fetchAction = fetchAction
|
||||
)
|
||||
|
||||
is Boolean -> ConfigurationCheckbox(
|
||||
configField = configField,
|
||||
onCheckChanged = { newValue ->
|
||||
onFieldUpdate(configField.name, newValue)
|
||||
}
|
||||
)
|
||||
|
||||
null -> Unit
|
||||
else -> error("Unsupported configuration field type for key ${configField.name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -279,6 +310,25 @@ private fun ConfigActionButton(
|
|||
|
||||
private fun Modifier.bottomPad(bottomPadding: Dp) = fillMaxWidth().padding(bottom = bottomPadding)
|
||||
|
||||
private val Boolean.drawable: Int @DrawableRes get() = if (this) R.drawable.ic_proton_arrow_up else R.drawable.ic_proton_arrow_down
|
||||
private
|
||||
val Boolean.drawable: Int
|
||||
@DrawableRes get() = if (this) R.drawable.ic_proton_arrow_up else R.drawable.ic_proton_arrow_down
|
||||
|
||||
fun String.toSpacedWords(): String = replace("(?<=\\p{Lower})(?=[A-Z])".toRegex(), " ").capitalize()
|
||||
|
||||
|
||||
sealed class Domain(val rawValue: String) {
|
||||
object Black : Domain("proton.black")
|
||||
class Custom(name: String) : Domain("$name.proton.black")
|
||||
object Production : Domain("proton.me")
|
||||
}
|
||||
|
||||
fun handleDomain(stringValue: String): String {
|
||||
val domain = when (stringValue) {
|
||||
"black" -> Domain.Black
|
||||
"production" -> Domain.Production
|
||||
else -> Domain.Custom(name = stringValue)
|
||||
}
|
||||
|
||||
return domain.rawValue
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Proton Technologies AG
|
||||
* This file is part of Proton AG and ProtonCore.
|
||||
*
|
||||
* ProtonCore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonCore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.core.configuration.configurator.presentation.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import me.proton.core.compose.theme.ProtonDimens
|
||||
|
||||
@Composable
|
||||
fun SearchableConfigurationTextField(
|
||||
searchData: List<String>,
|
||||
onResultSelected: (String) -> Unit,
|
||||
onDismissRequest: () -> Unit
|
||||
) {
|
||||
// State for the search text
|
||||
var searchText by remember { mutableStateOf("") }
|
||||
|
||||
// Filter the search data based on the search text
|
||||
val filteredItems = if (searchText.isEmpty()) searchData else searchData.filter {
|
||||
it.contains(searchText, ignoreCase = true)
|
||||
}
|
||||
|
||||
Surface {
|
||||
Column(modifier = Modifier.height(300.dp)) {
|
||||
// Top App Bar with a TextField for searching
|
||||
TopAppBar(
|
||||
title = {
|
||||
BasicTextField(
|
||||
value = searchText,
|
||||
onValueChange = { searchText = it },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
decorationBox = { innerTextField ->
|
||||
if (searchText.isEmpty()) {
|
||||
Text("Search", color = Color.LightGray)
|
||||
}
|
||||
innerTextField()
|
||||
}
|
||||
)
|
||||
},
|
||||
actions = {
|
||||
// Wrap 'Cancel' text in clickable modifier to handle taps
|
||||
Text(
|
||||
text = "Cancel",
|
||||
modifier = Modifier
|
||||
.padding(horizontal = ProtonDimens.SmallSpacing)
|
||||
.clickable { onDismissRequest() }, // This calls the provided `onDismissRequest` callback
|
||||
color = Color.White
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
// List of filtered items
|
||||
LazyColumn {
|
||||
items(filteredItems, key = { it }) { item ->
|
||||
ItemView(item = item, onItemClicked = onResultSelected)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ItemView(item: String, onItemClicked: (String) -> Unit) {
|
||||
Text(
|
||||
text = item,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(ProtonDimens.SmallSpacing)
|
||||
.clickable { onItemClicked(item) }
|
||||
)
|
||||
}
|
|
@ -23,4 +23,315 @@
|
|||
<string name="configuration_title_network_configuration" translatable="false">Network Configuration</string>
|
||||
<string name="configuration_text_advanced" translatable="false">Advanced</string>
|
||||
<string name="configuration_error_unknown" translatable="false">Could not save configuration. Unknown error</string>
|
||||
<string-array name="domains" translatable="false">
|
||||
<item>production</item>
|
||||
<item>payments</item>
|
||||
<item>agassiz</item>
|
||||
<item>al-khalili</item>
|
||||
<item>alcala</item>
|
||||
<item>ali</item>
|
||||
<item>alvarez</item>
|
||||
<item>anderson</item>
|
||||
<item>anning</item>
|
||||
<item>apgar</item>
|
||||
<item>arber</item>
|
||||
<item>arrhenius</item>
|
||||
<item>avery</item>
|
||||
<item>avogadro</item>
|
||||
<item>babbage</item>
|
||||
<item>bacon</item>
|
||||
<item>bain</item>
|
||||
<item>banks</item>
|
||||
<item>barba</item>
|
||||
<item>bardeen</item>
|
||||
<item>barkla</item>
|
||||
<item>battuta</item>
|
||||
<item>bayliss</item>
|
||||
<item>beadle</item>
|
||||
<item>becquerel</item>
|
||||
<item>berliner</item>
|
||||
<item>bernard</item>
|
||||
<item>bernoulli</item>
|
||||
<item>berzelius</item>
|
||||
<item>bessemer</item>
|
||||
<item>bethe</item>
|
||||
<item>binet</item>
|
||||
<item>birdseye</item>
|
||||
<item>birkeland</item>
|
||||
<item>black</item>
|
||||
<item>blackwell</item>
|
||||
<item>blalock</item>
|
||||
<item>boas</item>
|
||||
<item>bohm</item>
|
||||
<item>bohr</item>
|
||||
<item>boltzmann</item>
|
||||
<item>born</item>
|
||||
<item>bosch</item>
|
||||
<item>boyle</item>
|
||||
<item>bragg</item>
|
||||
<item>brahe</item>
|
||||
<item>brand</item>
|
||||
<item>brandt</item>
|
||||
<item>brongniart</item>
|
||||
<item>brown</item>
|
||||
<item>buchner</item>
|
||||
<item>buck</item>
|
||||
<item>buckland</item>
|
||||
<item>bunsen</item>
|
||||
<item>burbank</item>
|
||||
<item>burnet</item>
|
||||
<item>cabrera</item>
|
||||
<item>carson</item>
|
||||
<item>cavendish</item>
|
||||
<item>celsius</item>
|
||||
<item>chadwick</item>
|
||||
<item>chandrasekhar</item>
|
||||
<item>chargaff</item>
|
||||
<item>chomsky</item>
|
||||
<item>chu</item>
|
||||
<item>clark</item>
|
||||
<item>cockcroft</item>
|
||||
<item>compton</item>
|
||||
<item>copernicus</item>
|
||||
<item>cousteau</item>
|
||||
<item>cox</item>
|
||||
<item>crick</item>
|
||||
<item>croll</item>
|
||||
<item>culpeper</item>
|
||||
<item>curie</item>
|
||||
<item>cuvier</item>
|
||||
<item>czerny</item>
|
||||
<item>daimler</item>
|
||||
<item>dalton</item>
|
||||
<item>darwin</item>
|
||||
<item>davy</item>
|
||||
<item>debye</item>
|
||||
<item>delbruck</item>
|
||||
<item>descartes</item>
|
||||
<item>dirac</item>
|
||||
<item>divis</item>
|
||||
<item>dobzhansky</item>
|
||||
<item>drake</item>
|
||||
<item>eccles</item>
|
||||
<item>eddington</item>
|
||||
<item>edison</item>
|
||||
<item>ehrlich</item>
|
||||
<item>einstein</item>
|
||||
<item>elion</item>
|
||||
<item>euler</item>
|
||||
<item>faraday</item>
|
||||
<item>fermi</item>
|
||||
<item>feynman</item>
|
||||
<item>fischer</item>
|
||||
<item>fisher</item>
|
||||
<item>fleming</item>
|
||||
<item>florey</item>
|
||||
<item>ford</item>
|
||||
<item>fossey</item>
|
||||
<item>foucault</item>
|
||||
<item>franklin</item>
|
||||
<item>freud</item>
|
||||
<item>galilei</item>
|
||||
<item>galton</item>
|
||||
<item>galvani</item>
|
||||
<item>gamow</item>
|
||||
<item>gardner</item>
|
||||
<item>gell-mann</item>
|
||||
<item>germain</item>
|
||||
<item>gibbs</item>
|
||||
<item>gilbert</item>
|
||||
<item>goddard</item>
|
||||
<item>goeppert-mayer</item>
|
||||
<item>gold</item>
|
||||
<item>goodall</item>
|
||||
<item>haber</item>
|
||||
<item>haeckel</item>
|
||||
<item>hahn</item>
|
||||
<item>halley</item>
|
||||
<item>hardy</item>
|
||||
<item>harriot</item>
|
||||
<item>harvey</item>
|
||||
<item>hawking</item>
|
||||
<item>haxel</item>
|
||||
<item>heisenberg</item>
|
||||
<item>henry</item>
|
||||
<item>herschel</item>
|
||||
<item>hertz</item>
|
||||
<item>hewish</item>
|
||||
<item>hilbert</item>
|
||||
<item>hilleman</item>
|
||||
<item>hirase</item>
|
||||
<item>hodgkin</item>
|
||||
<item>hooke</item>
|
||||
<item>hopkins</item>
|
||||
<item>hornby</item>
|
||||
<item>horner</item>
|
||||
<item>houssay</item>
|
||||
<item>hoyle</item>
|
||||
<item>hubble</item>
|
||||
<item>hutton</item>
|
||||
<item>huygens</item>
|
||||
<item>illy</item>
|
||||
<item>ising</item>
|
||||
<item>ito</item>
|
||||
<item>jenner</item>
|
||||
<item>joliot-curie</item>
|
||||
<item>kaku</item>
|
||||
<item>kapitsa</item>
|
||||
<item>kelsey</item>
|
||||
<item>kendrick</item>
|
||||
<item>kepler</item>
|
||||
<item>khayyam</item>
|
||||
<item>kinsey</item>
|
||||
<item>kirchoff</item>
|
||||
<item>klaproth</item>
|
||||
<item>koch</item>
|
||||
<item>kraepelin</item>
|
||||
<item>kuhn</item>
|
||||
<item>kwolek</item>
|
||||
<item>lagrange</item>
|
||||
<item>lamarck</item>
|
||||
<item>lamarr</item>
|
||||
<item>landsteiner</item>
|
||||
<item>laplace</item>
|
||||
<item>lavoisier</item>
|
||||
<item>lawrence</item>
|
||||
<item>leavitt</item>
|
||||
<item>lehmann</item>
|
||||
<item>leibniz</item>
|
||||
<item>lemaître</item>
|
||||
<item>leoniceno</item>
|
||||
<item>leopold</item>
|
||||
<item>levi-montalcini</item>
|
||||
<item>levi-strauss</item>
|
||||
<item>linnaeus</item>
|
||||
<item>lister</item>
|
||||
<item>locke</item>
|
||||
<item>lorenz</item>
|
||||
<item>lovelace</item>
|
||||
<item>lowell</item>
|
||||
<item>lyell</item>
|
||||
<item>lysenko</item>
|
||||
<item>mach</item>
|
||||
<item>malpighi</item>
|
||||
<item>marcet</item>
|
||||
<item>marconi</item>
|
||||
<item>margulis</item>
|
||||
<item>matzinger</item>
|
||||
<item>maury</item>
|
||||
<item>mayr</item>
|
||||
<item>mcclintock</item>
|
||||
<item>meitner</item>
|
||||
<item>mendel</item>
|
||||
<item>mendeleev</item>
|
||||
<item>mesmer</item>
|
||||
<item>meucci</item>
|
||||
<item>michell</item>
|
||||
<item>milanković</item>
|
||||
<item>mitchell</item>
|
||||
<item>molina</item>
|
||||
<item>morse</item>
|
||||
<item>moseley</item>
|
||||
<item>nakaya</item>
|
||||
<item>napier</item>
|
||||
<item>natta</item>
|
||||
<item>needham</item>
|
||||
<item>newcomen</item>
|
||||
<item>newton</item>
|
||||
<item>nicolle</item>
|
||||
<item>nightingale</item>
|
||||
<item>noakes</item>
|
||||
<item>nobel</item>
|
||||
<item>noether</item>
|
||||
<item>nusslein-volhard</item>
|
||||
<item>nye</item>
|
||||
<item>ohm</item>
|
||||
<item>ostwald</item>
|
||||
<item>oughtred</item>
|
||||
<item>pascal</item>
|
||||
<item>pasteur</item>
|
||||
<item>pauling</item>
|
||||
<item>pausch</item>
|
||||
<item>pavlov</item>
|
||||
<item>payne-gaposchkin</item>
|
||||
<item>penfield</item>
|
||||
<item>perey</item>
|
||||
<item>perkin</item>
|
||||
<item>philoponus</item>
|
||||
<item>piaget</item>
|
||||
<item>pinel</item>
|
||||
<item>planck</item>
|
||||
<item>poincaré</item>
|
||||
<item>popper</item>
|
||||
<item>potter</item>
|
||||
<item>priestley</item>
|
||||
<item>ptolemy</item>
|
||||
<item>quetelet</item>
|
||||
<item>quimby</item>
|
||||
<item>ramanujan</item>
|
||||
<item>ramsay</item>
|
||||
<item>ray</item>
|
||||
<item>redi</item>
|
||||
<item>ride</item>
|
||||
<item>riemann</item>
|
||||
<item>röntgen</item>
|
||||
<item>rorschach</item>
|
||||
<item>ross</item>
|
||||
<item>rushd</item>
|
||||
<item>rutherford</item>
|
||||
<item>sagan</item>
|
||||
<item>salam</item>
|
||||
<item>salk</item>
|
||||
<item>sanger</item>
|
||||
<item>santos-dumont</item>
|
||||
<item>schottky</item>
|
||||
<item>schrödinger</item>
|
||||
<item>schwann</item>
|
||||
<item>seaborg</item>
|
||||
<item>selye</item>
|
||||
<item>sherrington</item>
|
||||
<item>shoemaker</item>
|
||||
<item>smith</item>
|
||||
<item>soddy</item>
|
||||
<item>somerville</item>
|
||||
<item>sommerfeld</item>
|
||||
<item>staudinger</item>
|
||||
<item>steno</item>
|
||||
<item>stevens</item>
|
||||
<item>szilard</item>
|
||||
<item>tartaglia</item>
|
||||
<item>teller</item>
|
||||
<item>tesla</item>
|
||||
<item>thompson</item>
|
||||
<item>thomson</item>
|
||||
<item>tombaugh</item>
|
||||
<item>tonegawa</item>
|
||||
<item>torricelli</item>
|
||||
<item>townes</item>
|
||||
<item>tu</item>
|
||||
<item>turing</item>
|
||||
<item>urey</item>
|
||||
<item>venter</item>
|
||||
<item>vernadsky</item>
|
||||
<item>vesalius</item>
|
||||
<item>virchow</item>
|
||||
<item>virtanen</item>
|
||||
<item>volta</item>
|
||||
<item>waksman</item>
|
||||
<item>wald</item>
|
||||
<item>wallis</item>
|
||||
<item>walton</item>
|
||||
<item>watson</item>
|
||||
<item>watt</item>
|
||||
<item>wegener</item>
|
||||
<item>wilkins</item>
|
||||
<item>willis</item>
|
||||
<item>wingqvist</item>
|
||||
<item>winogradsky</item>
|
||||
<item>woese</item>
|
||||
<item>wöhler</item>
|
||||
<item>wundt</item>
|
||||
<item>yang</item>
|
||||
<item>zewail</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue