parent
95ac835b9c
commit
9c99e5ac80
|
@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
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.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
@ -46,7 +47,7 @@ fun RadioButton(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
checkedContent: @Composable () -> Unit = { RadioButtonCheckedContent(state) },
|
checkedContent: @Composable () -> Unit = { RadioButtonCheckedContent(state) },
|
||||||
uncheckedContent: @Composable () -> Unit = { RadioButtonUncheckedContent(state) },
|
uncheckedContent: @Composable () -> Unit = { RadioButtonUncheckedContent(state) },
|
||||||
trailingContent: @Composable (() -> Unit)? = null,
|
trailingContent: @Composable (RowScope.() -> Unit)? = null,
|
||||||
testTag: String? = null,
|
testTag: String? = null,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package co.electriccoin.zcash.di
|
package co.electriccoin.zcash.di
|
||||||
|
|
||||||
import co.electriccoin.zcash.ui.common.usecase.GetPersistableWalletUseCase
|
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.GetSynchronizerUseCase
|
||||||
import co.electriccoin.zcash.ui.common.usecase.ObserveFastestServersUseCase
|
import co.electriccoin.zcash.ui.common.usecase.ObserveFastestServersUseCase
|
||||||
import co.electriccoin.zcash.ui.common.usecase.ObserveSelectedEndpointUseCase
|
import co.electriccoin.zcash.ui.common.usecase.ObserveSelectedEndpointUseCase
|
||||||
|
@ -21,4 +22,5 @@ val useCaseModule =
|
||||||
singleOf(::PersistEndpointUseCase)
|
singleOf(::PersistEndpointUseCase)
|
||||||
singleOf(::ValidateEndpointUseCase)
|
singleOf(::ValidateEndpointUseCase)
|
||||||
singleOf(::GetPersistableWalletUseCase)
|
singleOf(::GetPersistableWalletUseCase)
|
||||||
|
singleOf(::GetSelectedEndpointUseCase)
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,7 +131,7 @@ class WalletRepositoryImpl(
|
||||||
.withIndex()
|
.withIndex()
|
||||||
.flatMapLatest { (_, synchronizer) ->
|
.flatMapLatest { (_, synchronizer) ->
|
||||||
synchronizer
|
synchronizer
|
||||||
?.getFastestServers(application, getAllServers())
|
?.getFastestServers(application, getDefaultServers())
|
||||||
?.map {
|
?.map {
|
||||||
when (it) {
|
when (it) {
|
||||||
FastestServersResult.Measuring ->
|
FastestServersResult.Measuring ->
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
|
@ -43,6 +43,8 @@ sealed interface ServerState : Itemizable {
|
||||||
data class Custom(
|
data class Custom(
|
||||||
val radioButtonState: RadioButtonState,
|
val radioButtonState: RadioButtonState,
|
||||||
val newServerTextFieldState: TextFieldState,
|
val newServerTextFieldState: TextFieldState,
|
||||||
|
val badge: StringResource?,
|
||||||
|
val isExpanded: Boolean,
|
||||||
) : ServerState {
|
) : ServerState {
|
||||||
override val contentType: Any = "Custom"
|
override val contentType: Any = "Custom"
|
||||||
override val key: Any = contentType
|
override val key: Any = contentType
|
||||||
|
|
|
@ -35,6 +35,7 @@ import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.rotate
|
import androidx.compose.ui.draw.rotate
|
||||||
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.platform.testTag
|
import androidx.compose.ui.platform.testTag
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
|
@ -324,7 +325,8 @@ private fun FastestServersHeader(state: ServerListState.Fastest) {
|
||||||
} else {
|
} else {
|
||||||
Image(
|
Image(
|
||||||
painter = painterResource(id = R.drawable.ic_retry),
|
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
|
@Composable
|
||||||
private fun CustomServerRadioButton(
|
private fun CustomServerRadioButton(
|
||||||
state: ServerState.Custom,
|
state: ServerState.Custom,
|
||||||
|
@ -362,21 +365,50 @@ private fun CustomServerRadioButton(
|
||||||
RadioButton(
|
RadioButton(
|
||||||
state = state.radioButtonState,
|
state = state.radioButtonState,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
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 = {
|
trailingContent = {
|
||||||
|
if (state.badge != null) {
|
||||||
|
Badge(
|
||||||
|
text = state.badge,
|
||||||
|
)
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier.width(8.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
val iconAngle =
|
val iconAngle =
|
||||||
animateFloatAsState(
|
animateFloatAsState(
|
||||||
targetValue = if (state.radioButtonState.isChecked) 180f else 0f,
|
targetValue = if (state.isExpanded) 180f else 0f,
|
||||||
label = "iconAngle"
|
label = "iconAngle"
|
||||||
)
|
)
|
||||||
Image(
|
Image(
|
||||||
modifier = Modifier.rotate(iconAngle.value),
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.align(Alignment.CenterVertically)
|
||||||
|
.rotate(iconAngle.value),
|
||||||
painter = painterResource(id = R.drawable.ic_expand),
|
painter = painterResource(id = R.drawable.ic_expand),
|
||||||
contentDescription = state.radioButtonState.text.getValue()
|
contentDescription = state.radioButtonState.text.getValue()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
AnimatedVisibility(visible = state.radioButtonState.isChecked) {
|
AnimatedVisibility(visible = state.isExpanded) {
|
||||||
val focusManager = LocalFocusManager.current
|
val focusManager = LocalFocusManager.current
|
||||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall))
|
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall))
|
||||||
ZashiTextField(
|
ZashiTextField(
|
||||||
|
@ -459,6 +491,8 @@ private fun ChooseServerPreview(
|
||||||
isEnabled = true,
|
isEnabled = true,
|
||||||
onValueChange = { },
|
onValueChange = { },
|
||||||
),
|
),
|
||||||
|
badge = null,
|
||||||
|
isExpanded = true
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ServerState.Default(
|
ServerState.Default(
|
||||||
|
|
|
@ -42,6 +42,8 @@ class ChooseServerViewModel(
|
||||||
|
|
||||||
private val dialogState = MutableStateFlow<ServerDialogState?>(null)
|
private val dialogState = MutableStateFlow<ServerDialogState?>(null)
|
||||||
|
|
||||||
|
private val isCustomEndpointExpanded = MutableStateFlow(false)
|
||||||
|
|
||||||
private val fastest =
|
private val fastest =
|
||||||
combine(
|
combine(
|
||||||
observeSelectedEndpoint(),
|
observeSelectedEndpoint(),
|
||||||
|
@ -71,7 +73,8 @@ class ChooseServerViewModel(
|
||||||
observeFastestServers(),
|
observeFastestServers(),
|
||||||
userCustomEndpointText,
|
userCustomEndpointText,
|
||||||
userEndpointSelection,
|
userEndpointSelection,
|
||||||
) { selectedEndpoint, fastest, userCustomEndpointText, userEndpointSelection ->
|
isCustomEndpointExpanded
|
||||||
|
) { selectedEndpoint, fastest, userCustomEndpointText, userEndpointSelection, isCustomEndpointExpanded ->
|
||||||
if (selectedEndpoint == null) return@combine null
|
if (selectedEndpoint == null) return@combine null
|
||||||
|
|
||||||
val isSelectedEndpointCustom = !getAvailableServers().contains(selectedEndpoint)
|
val isSelectedEndpointCustom = !getAvailableServers().contains(selectedEndpoint)
|
||||||
|
@ -81,7 +84,8 @@ class ChooseServerViewModel(
|
||||||
userEndpointSelection = userEndpointSelection,
|
userEndpointSelection = userEndpointSelection,
|
||||||
isSelectedEndpointCustom = isSelectedEndpointCustom,
|
isSelectedEndpointCustom = isSelectedEndpointCustom,
|
||||||
userCustomEndpointText = userCustomEndpointText,
|
userCustomEndpointText = userCustomEndpointText,
|
||||||
selectedEndpoint = selectedEndpoint
|
selectedEndpoint = selectedEndpoint,
|
||||||
|
isCustomEndpointExpanded = isCustomEndpointExpanded
|
||||||
)
|
)
|
||||||
|
|
||||||
ServerListState.Other(
|
ServerListState.Other(
|
||||||
|
@ -109,7 +113,15 @@ class ChooseServerViewModel(
|
||||||
userEndpointSelection,
|
userEndpointSelection,
|
||||||
isSaveInProgress
|
isSaveInProgress
|
||||||
) { selectedEndpoint, 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(
|
ButtonState(
|
||||||
text = stringRes(R.string.choose_server_save),
|
text = stringRes(R.string.choose_server_save),
|
||||||
isEnabled = userEndpointSelection != null && selectedEndpoint != userSelectedEndpoint,
|
isEnabled = userEndpointSelection != null && selectedEndpoint != userSelectedEndpoint,
|
||||||
|
@ -139,11 +151,17 @@ class ChooseServerViewModel(
|
||||||
userEndpointSelection: Selection?,
|
userEndpointSelection: Selection?,
|
||||||
isSelectedEndpointCustom: Boolean,
|
isSelectedEndpointCustom: Boolean,
|
||||||
userCustomEndpointText: String?,
|
userCustomEndpointText: String?,
|
||||||
selectedEndpoint: LightWalletEndpoint
|
selectedEndpoint: LightWalletEndpoint,
|
||||||
|
isCustomEndpointExpanded: Boolean,
|
||||||
) = ServerState.Custom(
|
) = ServerState.Custom(
|
||||||
radioButtonState =
|
radioButtonState =
|
||||||
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 =
|
isChecked =
|
||||||
userEndpointSelection is Selection.Custom ||
|
userEndpointSelection is Selection.Custom ||
|
||||||
(userEndpointSelection == null && isSelectedEndpointCustom),
|
(userEndpointSelection == null && isSelectedEndpointCustom),
|
||||||
|
@ -163,6 +181,8 @@ class ChooseServerViewModel(
|
||||||
},
|
},
|
||||||
onValueChange = ::onCustomEndpointTextChanged,
|
onValueChange = ::onCustomEndpointTextChanged,
|
||||||
),
|
),
|
||||||
|
badge = if (isSelectedEndpointCustom) stringRes(R.string.choose_server_active) else null,
|
||||||
|
isExpanded = isCustomEndpointExpanded
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun createDefaultServerState(
|
private fun createDefaultServerState(
|
||||||
|
@ -195,10 +215,12 @@ class ChooseServerViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onEndpointClicked(endpoint: LightWalletEndpoint) {
|
private fun onEndpointClicked(endpoint: LightWalletEndpoint) {
|
||||||
|
isCustomEndpointExpanded.update { false }
|
||||||
userEndpointSelection.update { Selection.Endpoint(endpoint) }
|
userEndpointSelection.update { Selection.Endpoint(endpoint) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onCustomEndpointClicked() {
|
private fun onCustomEndpointClicked() {
|
||||||
|
isCustomEndpointExpanded.update { true }
|
||||||
userEndpointSelection.update { Selection.Custom }
|
userEndpointSelection.update { Selection.Custom }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,6 +232,7 @@ class ChooseServerViewModel(
|
||||||
val selection = getUserEndpointSelectionOrShowError() ?: return@launch
|
val selection = getUserEndpointSelectionOrShowError() ?: return@launch
|
||||||
try {
|
try {
|
||||||
persistEndpoint(selection)
|
persistEndpoint(selection)
|
||||||
|
isCustomEndpointExpanded.update { false }
|
||||||
} catch (e: PersistEndpointException) {
|
} catch (e: PersistEndpointException) {
|
||||||
showValidationErrorDialog(e.message)
|
showValidationErrorDialog(e.message)
|
||||||
}
|
}
|
||||||
|
@ -228,7 +251,7 @@ class ChooseServerViewModel(
|
||||||
*/
|
*/
|
||||||
private fun getUserEndpointSelectionOrShowError(): LightWalletEndpoint? {
|
private fun getUserEndpointSelectionOrShowError(): LightWalletEndpoint? {
|
||||||
return when (val selection = userEndpointSelection.value) {
|
return when (val selection = userEndpointSelection.value) {
|
||||||
Selection.Custom -> {
|
is Selection.Custom -> {
|
||||||
val endpoint = userCustomEndpointText.value
|
val endpoint = userCustomEndpointText.value
|
||||||
val validated = validateEndpoint(endpoint.orEmpty())
|
val validated = validateEndpoint(endpoint.orEmpty())
|
||||||
if (validated == null) {
|
if (validated == null) {
|
||||||
|
|
Loading…
Reference in New Issue