diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/RadioButton.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/RadioButton.kt index 42929335..218a1435 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/RadioButton.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/RadioButton.kt @@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -46,7 +47,7 @@ fun RadioButton( modifier: Modifier = Modifier, checkedContent: @Composable () -> Unit = { RadioButtonCheckedContent(state) }, uncheckedContent: @Composable () -> Unit = { RadioButtonUncheckedContent(state) }, - trailingContent: @Composable (() -> Unit)? = null, + trailingContent: @Composable (RowScope.() -> Unit)? = null, testTag: String? = null, ) { Row( diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/di/UseCaseModule.kt b/ui-lib/src/main/java/co/electriccoin/zcash/di/UseCaseModule.kt index 6911d616..6586530b 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/di/UseCaseModule.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/di/UseCaseModule.kt @@ -1,6 +1,7 @@ package co.electriccoin.zcash.di import co.electriccoin.zcash.ui.common.usecase.GetPersistableWalletUseCase +import co.electriccoin.zcash.ui.common.usecase.GetSelectedEndpointUseCase import co.electriccoin.zcash.ui.common.usecase.GetSynchronizerUseCase import co.electriccoin.zcash.ui.common.usecase.ObserveFastestServersUseCase import co.electriccoin.zcash.ui.common.usecase.ObserveSelectedEndpointUseCase @@ -21,4 +22,5 @@ val useCaseModule = singleOf(::PersistEndpointUseCase) singleOf(::ValidateEndpointUseCase) singleOf(::GetPersistableWalletUseCase) + singleOf(::GetSelectedEndpointUseCase) } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/repository/WalletRepository.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/repository/WalletRepository.kt index da2fd3a4..9c0f7d33 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/repository/WalletRepository.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/repository/WalletRepository.kt @@ -131,7 +131,7 @@ class WalletRepositoryImpl( .withIndex() .flatMapLatest { (_, synchronizer) -> synchronizer - ?.getFastestServers(application, getAllServers()) + ?.getFastestServers(application, getDefaultServers()) ?.map { when (it) { FastestServersResult.Measuring -> diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/usecase/GetSelectedEndpointUseCase.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/usecase/GetSelectedEndpointUseCase.kt new file mode 100644 index 00000000..7fd29a6a --- /dev/null +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/usecase/GetSelectedEndpointUseCase.kt @@ -0,0 +1,9 @@ +package co.electriccoin.zcash.ui.common.usecase + +import co.electriccoin.zcash.ui.common.repository.WalletRepository + +class GetSelectedEndpointUseCase( + private val walletRepository: WalletRepository +) { + suspend operator fun invoke() = walletRepository.getSelectedServer() +} diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/chooseserver/ChooseServerState.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/chooseserver/ChooseServerState.kt index 7eec8758..955dfa2b 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/chooseserver/ChooseServerState.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/chooseserver/ChooseServerState.kt @@ -43,6 +43,8 @@ sealed interface ServerState : Itemizable { data class Custom( val radioButtonState: RadioButtonState, val newServerTextFieldState: TextFieldState, + val badge: StringResource?, + val isExpanded: Boolean, ) : ServerState { override val contentType: Any = "Custom" override val key: Any = contentType diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/chooseserver/ChooseServerView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/chooseserver/ChooseServerView.kt index 536caaf5..e2598f2f 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/chooseserver/ChooseServerView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/chooseserver/ChooseServerView.kt @@ -35,6 +35,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate +import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource @@ -324,7 +325,8 @@ private fun FastestServersHeader(state: ServerListState.Fastest) { } else { Image( painter = painterResource(id = R.drawable.ic_retry), - contentDescription = state.retryButton.text.getValue() + contentDescription = state.retryButton.text.getValue(), + colorFilter = ColorFilter.tint(ZcashTheme.zashiColors.textPrimary) ) } } @@ -353,6 +355,7 @@ private fun ServerHeader(text: StringResource) { ) } +@Suppress("LongMethod") @Composable private fun CustomServerRadioButton( state: ServerState.Custom, @@ -362,21 +365,50 @@ private fun CustomServerRadioButton( RadioButton( state = state.radioButtonState, modifier = Modifier.fillMaxWidth(), + checkedContent = { + if (state.badge == null) { + RadioButtonCheckedContent(state.radioButtonState) + } else { + Image( + painter = + painterResource( + id = + if (isSystemInDarkTheme()) { + drawable.ic_radio_button_checked_variant_dark + } else { + drawable.ic_radio_button_checked_variant + } + ), + contentDescription = state.radioButtonState.text.getValue(), + ) + } + }, trailingContent = { + if (state.badge != null) { + Badge( + text = state.badge, + ) + Spacer( + modifier = Modifier.width(8.dp), + ) + } val iconAngle = animateFloatAsState( - targetValue = if (state.radioButtonState.isChecked) 180f else 0f, + targetValue = if (state.isExpanded) 180f else 0f, label = "iconAngle" ) Image( - modifier = Modifier.rotate(iconAngle.value), + modifier = + Modifier + .align(Alignment.CenterVertically) + .rotate(iconAngle.value), painter = painterResource(id = R.drawable.ic_expand), contentDescription = state.radioButtonState.text.getValue() ) } ) - AnimatedVisibility(visible = state.radioButtonState.isChecked) { + AnimatedVisibility(visible = state.isExpanded) { val focusManager = LocalFocusManager.current Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall)) ZashiTextField( @@ -459,6 +491,8 @@ private fun ChooseServerPreview( isEnabled = true, onValueChange = { }, ), + badge = null, + isExpanded = true ) } else { ServerState.Default( diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/chooseserver/ChooseServerViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/chooseserver/ChooseServerViewModel.kt index 0f2648fe..9f0f3e21 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/chooseserver/ChooseServerViewModel.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/chooseserver/ChooseServerViewModel.kt @@ -42,6 +42,8 @@ class ChooseServerViewModel( private val dialogState = MutableStateFlow(null) + private val isCustomEndpointExpanded = MutableStateFlow(false) + private val fastest = combine( observeSelectedEndpoint(), @@ -71,7 +73,8 @@ class ChooseServerViewModel( observeFastestServers(), userCustomEndpointText, userEndpointSelection, - ) { selectedEndpoint, fastest, userCustomEndpointText, userEndpointSelection -> + isCustomEndpointExpanded + ) { selectedEndpoint, fastest, userCustomEndpointText, userEndpointSelection, isCustomEndpointExpanded -> if (selectedEndpoint == null) return@combine null val isSelectedEndpointCustom = !getAvailableServers().contains(selectedEndpoint) @@ -81,7 +84,8 @@ class ChooseServerViewModel( userEndpointSelection = userEndpointSelection, isSelectedEndpointCustom = isSelectedEndpointCustom, userCustomEndpointText = userCustomEndpointText, - selectedEndpoint = selectedEndpoint + selectedEndpoint = selectedEndpoint, + isCustomEndpointExpanded = isCustomEndpointExpanded ) ServerListState.Other( @@ -109,7 +113,15 @@ class ChooseServerViewModel( userEndpointSelection, isSaveInProgress ) { selectedEndpoint, userEndpointSelection, isSaveInProgress -> - val userSelectedEndpoint = (userEndpointSelection as? Selection.Endpoint)?.endpoint + val userSelectedEndpoint = + when (userEndpointSelection) { + Selection.Custom -> { + val isSelectedEndpointCustom = !getAvailableServers().contains(selectedEndpoint) + if (isSelectedEndpointCustom) selectedEndpoint else null + } + is Selection.Endpoint -> userEndpointSelection.endpoint + null -> null + } ButtonState( text = stringRes(R.string.choose_server_save), isEnabled = userEndpointSelection != null && selectedEndpoint != userSelectedEndpoint, @@ -139,11 +151,17 @@ class ChooseServerViewModel( userEndpointSelection: Selection?, isSelectedEndpointCustom: Boolean, userCustomEndpointText: String?, - selectedEndpoint: LightWalletEndpoint + selectedEndpoint: LightWalletEndpoint, + isCustomEndpointExpanded: Boolean, ) = ServerState.Custom( radioButtonState = RadioButtonState( - text = stringRes(R.string.choose_server_custom), + text = + if (isSelectedEndpointCustom) { + stringRes(R.string.choose_server_full_server_name, selectedEndpoint.host, selectedEndpoint.port) + } else { + stringRes(R.string.choose_server_custom) + }, isChecked = userEndpointSelection is Selection.Custom || (userEndpointSelection == null && isSelectedEndpointCustom), @@ -163,6 +181,8 @@ class ChooseServerViewModel( }, onValueChange = ::onCustomEndpointTextChanged, ), + badge = if (isSelectedEndpointCustom) stringRes(R.string.choose_server_active) else null, + isExpanded = isCustomEndpointExpanded ) private fun createDefaultServerState( @@ -195,10 +215,12 @@ class ChooseServerViewModel( } private fun onEndpointClicked(endpoint: LightWalletEndpoint) { + isCustomEndpointExpanded.update { false } userEndpointSelection.update { Selection.Endpoint(endpoint) } } private fun onCustomEndpointClicked() { + isCustomEndpointExpanded.update { true } userEndpointSelection.update { Selection.Custom } } @@ -210,6 +232,7 @@ class ChooseServerViewModel( val selection = getUserEndpointSelectionOrShowError() ?: return@launch try { persistEndpoint(selection) + isCustomEndpointExpanded.update { false } } catch (e: PersistEndpointException) { showValidationErrorDialog(e.message) } @@ -228,7 +251,7 @@ class ChooseServerViewModel( */ private fun getUserEndpointSelectionOrShowError(): LightWalletEndpoint? { return when (val selection = userEndpointSelection.value) { - Selection.Custom -> { + is Selection.Custom -> { val endpoint = userCustomEndpointText.value val validated = validateEndpoint(endpoint.orEmpty()) if (validated == null) {