feat: Configuration module host selection improvements.

This commit is contained in:
Richard Gasztany 2024-04-11 11:35:18 +02:00 committed by Neil Marietta
parent a60078e7af
commit e4627b6ff3
5 changed files with 492 additions and 20 deletions

View File

@ -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,
)

View File

@ -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")
},

View File

@ -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
}

View File

@ -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) }
)
}

View File

@ -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>