Flexa hidden behind feature flag (#1676)

* Flexa hidden behind feature flag

* Remove unused flag IS_FIAT_CONVERSION_ENABLED

* Code cleanup

* Kotlin flow handling

---------

Co-authored-by: Honza <rychnovsky.honza@gmail.com>
This commit is contained in:
Milan 2024-11-13 13:20:29 +01:00 committed by GitHub
parent 882605d7a9
commit d4be4a5dda
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 89 additions and 141 deletions

View File

@ -297,19 +297,6 @@ fun Reference(
} }
} }
@Composable
fun BodyWithFiatCurrencySymbol(
amount: String,
modifier: Modifier = Modifier
) {
Text(
text = amount,
style = MaterialTheme.typography.bodyLarge,
color = ZcashTheme.colors.textPrimary,
modifier = modifier
)
}
@Preview @Preview
@Composable @Composable
private fun NavigationTabTextPreview() { private fun NavigationTabTextPreview() {

View File

@ -15,7 +15,6 @@ import java.util.concurrent.atomic.AtomicInteger
class BalancesTestSetup( class BalancesTestSetup(
private val composeTestRule: ComposeContentTestRule, private val composeTestRule: ComposeContentTestRule,
private val walletSnapshot: WalletSnapshot, private val walletSnapshot: WalletSnapshot,
private val isShowFiatConversion: Boolean
) { ) {
private val onSettingsCount = AtomicInteger(0) private val onSettingsCount = AtomicInteger(0)
@ -43,7 +42,6 @@ class BalancesTestSetup(
showStatusDialog = null, showStatusDialog = null,
onStatusClick = {}, onStatusClick = {},
snackbarHostState = SnackbarHostState(), snackbarHostState = SnackbarHostState(),
isFiatConversionEnabled = isShowFiatConversion,
isUpdateAvailable = false, isUpdateAvailable = false,
isShowingErrorDialog = false, isShowingErrorDialog = false,
setShowErrorDialog = {}, setShowErrorDialog = {},

View File

@ -27,7 +27,6 @@ class BalancesViewIntegrationTest : UiTestPrerequisites() {
BalancesTestSetup( BalancesTestSetup(
composeTestRule, composeTestRule,
walletSnapshot, walletSnapshot,
isShowFiatConversion = true
) )
// This is just basic sanity check that we still have UI set up as expected after the state restore // This is just basic sanity check that we still have UI set up as expected after the state restore

View File

@ -8,7 +8,6 @@ import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.common.model.WalletSnapshot import co.electriccoin.zcash.ui.common.model.WalletSnapshot
import co.electriccoin.zcash.ui.design.component.CommonTag import co.electriccoin.zcash.ui.design.component.CommonTag
import co.electriccoin.zcash.ui.fixture.WalletSnapshotFixture import co.electriccoin.zcash.ui.fixture.WalletSnapshotFixture
import co.electriccoin.zcash.ui.screen.balances.BalancesTag
import co.electriccoin.zcash.ui.screen.balances.BalancesTestSetup import co.electriccoin.zcash.ui.screen.balances.BalancesTestSetup
import co.electriccoin.zcash.ui.screen.send.clickSettingsTopAppBarMenu import co.electriccoin.zcash.ui.screen.send.clickSettingsTopAppBarMenu
import org.junit.Assert import org.junit.Assert
@ -23,6 +22,14 @@ class BalancesViewTest : UiTestPrerequisites() {
@get:Rule @get:Rule
val composeTestRule = createComposeRule() val composeTestRule = createComposeRule()
private fun newTestSetup(walletSnapshot: WalletSnapshot = WalletSnapshotFixture.new()) =
BalancesTestSetup(
composeTestRule,
walletSnapshot = walletSnapshot,
).apply {
setDefaultContent()
}
@Test @Test
@MediumTest @MediumTest
fun check_all_elementary_ui_elements_displayed() { fun check_all_elementary_ui_elements_displayed() {
@ -34,16 +41,6 @@ class BalancesViewTest : UiTestPrerequisites() {
} }
} }
@Test
@MediumTest
fun hide_fiat_conversion() {
newTestSetup(isShowFiatConversion = false)
composeTestRule.onNodeWithTag(BalancesTag.FIAT_CONVERSION).also {
it.assertDoesNotExist()
}
}
@Test @Test
@MediumTest @MediumTest
fun hamburger_settings_test() { fun hamburger_settings_test() {
@ -55,15 +52,4 @@ class BalancesViewTest : UiTestPrerequisites() {
Assert.assertEquals(1, testSetup.getOnSettingsCount()) Assert.assertEquals(1, testSetup.getOnSettingsCount())
} }
private fun newTestSetup(
isShowFiatConversion: Boolean = true,
walletSnapshot: WalletSnapshot = WalletSnapshotFixture.new()
) = BalancesTestSetup(
composeTestRule,
walletSnapshot = walletSnapshot,
isShowFiatConversion = isShowFiatConversion
).apply {
setDefaultContent()
}
} }

View File

@ -17,6 +17,7 @@ import co.electriccoin.zcash.ui.common.usecase.ObserveConfigurationUseCase
import co.electriccoin.zcash.ui.common.usecase.ObserveContactByAddressUseCase import co.electriccoin.zcash.ui.common.usecase.ObserveContactByAddressUseCase
import co.electriccoin.zcash.ui.common.usecase.ObserveContactPickedUseCase import co.electriccoin.zcash.ui.common.usecase.ObserveContactPickedUseCase
import co.electriccoin.zcash.ui.common.usecase.ObserveFastestServersUseCase import co.electriccoin.zcash.ui.common.usecase.ObserveFastestServersUseCase
import co.electriccoin.zcash.ui.common.usecase.ObserveIsFlexaAvailableUseCase
import co.electriccoin.zcash.ui.common.usecase.ObserveSelectedEndpointUseCase import co.electriccoin.zcash.ui.common.usecase.ObserveSelectedEndpointUseCase
import co.electriccoin.zcash.ui.common.usecase.ObserveSynchronizerUseCase import co.electriccoin.zcash.ui.common.usecase.ObserveSynchronizerUseCase
import co.electriccoin.zcash.ui.common.usecase.ObserveWalletStateUseCase import co.electriccoin.zcash.ui.common.usecase.ObserveWalletStateUseCase
@ -63,6 +64,7 @@ val useCaseModule =
singleOf(::GetAddressesUseCase) singleOf(::GetAddressesUseCase)
singleOf(::CopyToClipboardUseCase) singleOf(::CopyToClipboardUseCase)
singleOf(::IsFlexaAvailableUseCase) singleOf(::IsFlexaAvailableUseCase)
singleOf(::ObserveIsFlexaAvailableUseCase)
singleOf(::ShareImageUseCase) singleOf(::ShareImageUseCase)
singleOf(::Zip321BuildUriUseCase) singleOf(::Zip321BuildUriUseCase)
singleOf(::Zip321ProposalFromUriUseCase) singleOf(::Zip321ProposalFromUriUseCase)

View File

@ -3,25 +3,59 @@ package co.electriccoin.zcash.ui.common.repository
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
import co.electriccoin.zcash.configuration.api.ConfigurationProvider import co.electriccoin.zcash.configuration.api.ConfigurationProvider
import co.electriccoin.zcash.configuration.model.map.Configuration import co.electriccoin.zcash.configuration.model.map.Configuration
import co.electriccoin.zcash.ui.BuildConfig
import co.electriccoin.zcash.ui.common.provider.GetVersionInfoProvider
import co.electriccoin.zcash.ui.configuration.ConfigurationEntries
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.WhileSubscribed
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
interface ConfigurationRepository { interface ConfigurationRepository {
val configurationFlow: StateFlow<Configuration?> val configurationFlow: StateFlow<Configuration?>
/**
* Returns true if Flexa is available, false otherwise & null if loading.
*/
val isFlexaAvailable: StateFlow<Boolean?>
suspend fun isFlexaAvailable(): Boolean
} }
class ConfigurationRepositoryImpl(androidConfigurationProvider: ConfigurationProvider) : ConfigurationRepository { class ConfigurationRepositoryImpl(
androidConfigurationProvider: ConfigurationProvider,
private val getVersionInfo: GetVersionInfoProvider,
) : ConfigurationRepository {
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
override val configurationFlow: StateFlow<Configuration?> = override val configurationFlow: StateFlow<Configuration?> =
androidConfigurationProvider.getConfigurationFlow() androidConfigurationProvider.getConfigurationFlow()
.stateIn( .stateIn(
scope, scope = scope,
SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT.inWholeMilliseconds), started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
null initialValue = null
) )
override val isFlexaAvailable: StateFlow<Boolean?> =
configurationFlow
.filterNotNull()
.map {
val versionInfo = getVersionInfo()
!versionInfo.isTestnet &&
ConfigurationEntries.IS_FLEXA_AVAILABLE.getValue(it) &&
BuildConfig.ZCASH_FLEXA_KEY.isNotEmpty()
}
.stateIn(
scope = scope,
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
initialValue = null
)
override suspend fun isFlexaAvailable(): Boolean = isFlexaAvailable.filterNotNull().first()
} }

View File

@ -25,6 +25,7 @@ interface FlexaRepository {
class FlexaRepositoryImpl( class FlexaRepositoryImpl(
private val balanceRepository: BalanceRepository, private val balanceRepository: BalanceRepository,
private val application: Application, private val application: Application,
private val configurationRepository: ConfigurationRepository,
) : FlexaRepository { ) : FlexaRepository {
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
@ -32,9 +33,8 @@ class FlexaRepositoryImpl(
get() = BuildConfig.ZCASH_FLEXA_KEY.takeIf { it.isNotEmpty() } get() = BuildConfig.ZCASH_FLEXA_KEY.takeIf { it.isNotEmpty() }
override fun init() { override fun init() {
if (publishableKey == null) return
scope.launch { scope.launch {
if (!configurationRepository.isFlexaAvailable()) return@launch
val configuration = getFlexaClientConfiguration() val configuration = getFlexaClientConfiguration()
if (configuration != null) { if (configuration != null) {
Flexa.init(configuration) Flexa.init(configuration)

View File

@ -1,14 +1,9 @@
package co.electriccoin.zcash.ui.common.usecase package co.electriccoin.zcash.ui.common.usecase
import co.electriccoin.zcash.ui.BuildConfig import co.electriccoin.zcash.ui.common.repository.ConfigurationRepository
import co.electriccoin.zcash.ui.common.provider.GetVersionInfoProvider
class IsFlexaAvailableUseCase( class IsFlexaAvailableUseCase(
private val getVersionInfo: GetVersionInfoProvider, private val configurationRepository: ConfigurationRepository
) { ) {
operator fun invoke(): Boolean { suspend operator fun invoke() = configurationRepository.isFlexaAvailable()
val versionInfo = getVersionInfo()
val isDebug = versionInfo.let { it.isDebuggable && !it.isRunningUnderTestService }
return !versionInfo.isTestnet && (BuildConfig.ZCASH_FLEXA_KEY.isNotEmpty() || isDebug)
}
} }

View File

@ -0,0 +1,9 @@
package co.electriccoin.zcash.ui.common.usecase
import co.electriccoin.zcash.ui.common.repository.ConfigurationRepository
class ObserveIsFlexaAvailableUseCase(
private val configurationRepository: ConfigurationRepository
) {
operator fun invoke() = configurationRepository.isFlexaAvailable
}

View File

@ -17,7 +17,6 @@ import cash.z.ecc.sdk.type.fromResources
import co.electriccoin.zcash.preference.EncryptedPreferenceProvider import co.electriccoin.zcash.preference.EncryptedPreferenceProvider
import co.electriccoin.zcash.preference.StandardPreferenceProvider import co.electriccoin.zcash.preference.StandardPreferenceProvider
import co.electriccoin.zcash.spackle.Twig import co.electriccoin.zcash.spackle.Twig
import co.electriccoin.zcash.ui.BuildConfig
import co.electriccoin.zcash.ui.common.model.OnboardingState import co.electriccoin.zcash.ui.common.model.OnboardingState
import co.electriccoin.zcash.ui.common.model.WalletRestoringState import co.electriccoin.zcash.ui.common.model.WalletRestoringState
import co.electriccoin.zcash.ui.common.model.WalletSnapshot import co.electriccoin.zcash.ui.common.model.WalletSnapshot
@ -50,8 +49,6 @@ import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
// To make this more multiplatform compatible, we need to remove the dependency on Context // To make this more multiplatform compatible, we need to remove the dependency on Context
// for loading the preferences. // for loading the preferences.
@ -271,15 +268,11 @@ class WalletViewModel(
} }
} }
private suspend fun disconnectFlexa() = private suspend inline fun disconnectFlexa() {
suspendCoroutine { cont -> if (isFlexaAvailable()) {
if (isFlexaAvailable() && BuildConfig.ZCASH_FLEXA_KEY.isNotEmpty()) { Flexa.buildIdentity().build().disconnect()
Flexa.buildIdentity().build().disconnect()
cont.resume(Unit)
} else {
cont.resume(Unit)
}
} }
}
} }
/** /**

View File

@ -6,13 +6,10 @@ import co.electriccoin.zcash.configuration.model.entry.ConfigKey
object ConfigurationEntries { object ConfigurationEntries {
val IS_APP_UPDATE_CHECK_ENABLED = BooleanConfigurationEntry(ConfigKey("is_update_check_enabled"), true) val IS_APP_UPDATE_CHECK_ENABLED = BooleanConfigurationEntry(ConfigKey("is_update_check_enabled"), true)
/*
* This isn't fully implemented yet, so it is disabled from being shown.
*/
val IS_FIAT_CONVERSION_ENABLED = BooleanConfigurationEntry(ConfigKey("is_fiat_conversion_enabled"), false)
/* /*
* A troubleshooting step. If we fix our bugs, this should be unnecessary. * A troubleshooting step. If we fix our bugs, this should be unnecessary.
*/ */
val IS_RESCAN_ENABLED = BooleanConfigurationEntry(ConfigKey("is_rescan_enabled"), true) val IS_RESCAN_ENABLED = BooleanConfigurationEntry(ConfigKey("is_rescan_enabled"), true)
val IS_FLEXA_AVAILABLE = BooleanConfigurationEntry(ConfigKey("is_flexa_available"), false)
} }

View File

@ -29,8 +29,6 @@ import co.electriccoin.zcash.ui.common.model.WalletSnapshot
import co.electriccoin.zcash.ui.common.viewmodel.CheckUpdateViewModel import co.electriccoin.zcash.ui.common.viewmodel.CheckUpdateViewModel
import co.electriccoin.zcash.ui.common.viewmodel.HomeViewModel import co.electriccoin.zcash.ui.common.viewmodel.HomeViewModel
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.configuration.ConfigurationEntries
import co.electriccoin.zcash.ui.configuration.RemoteConfig
import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator
import co.electriccoin.zcash.ui.screen.balances.model.ShieldState import co.electriccoin.zcash.ui.screen.balances.model.ShieldState
import co.electriccoin.zcash.ui.screen.balances.model.StatusAction import co.electriccoin.zcash.ui.screen.balances.model.StatusAction
@ -133,8 +131,6 @@ internal fun WrapBalances(
it?.appUpdateInfo != null && it.state == UpdateState.Prepared it?.appUpdateInfo != null && it.state == UpdateState.Prepared
} }
val isFiatConversionEnabled = ConfigurationEntries.IS_FIAT_CONVERSION_ENABLED.getValue(RemoteConfig.current)
val (shieldState, setShieldState) = val (shieldState, setShieldState) =
rememberSaveable(stateSaver = ShieldState.Saver) { mutableStateOf(ShieldState.None) } rememberSaveable(stateSaver = ShieldState.Saver) { mutableStateOf(ShieldState.None) }
@ -171,7 +167,6 @@ internal fun WrapBalances(
} else { } else {
Balances( Balances(
balanceState = balanceState, balanceState = balanceState,
isFiatConversionEnabled = isFiatConversionEnabled,
isHideBalances = isHideBalances, isHideBalances = isHideBalances,
isUpdateAvailable = isUpdateAvailable, isUpdateAvailable = isUpdateAvailable,
onHideBalances = onHideBalances, onHideBalances = onHideBalances,

View File

@ -5,5 +5,4 @@ package co.electriccoin.zcash.ui.screen.balances
*/ */
object BalancesTag { object BalancesTag {
const val STATUS = "status" const val STATUS = "status"
const val FIAT_CONVERSION = "fiat_conversion"
} }

View File

@ -45,7 +45,6 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import cash.z.ecc.android.sdk.model.FiatCurrencyConversionRateState
import cash.z.ecc.sdk.extension.DEFAULT_FEE import cash.z.ecc.sdk.extension.DEFAULT_FEE
import cash.z.ecc.sdk.extension.toZecStringFull import cash.z.ecc.sdk.extension.toZecStringFull
import cash.z.ecc.sdk.type.ZcashCurrency import cash.z.ecc.sdk.type.ZcashCurrency
@ -67,9 +66,7 @@ import co.electriccoin.zcash.ui.common.test.CommonTag
import co.electriccoin.zcash.ui.design.component.AppAlertDialog import co.electriccoin.zcash.ui.design.component.AppAlertDialog
import co.electriccoin.zcash.ui.design.component.BlankBgScaffold import co.electriccoin.zcash.ui.design.component.BlankBgScaffold
import co.electriccoin.zcash.ui.design.component.BlankSurface import co.electriccoin.zcash.ui.design.component.BlankSurface
import co.electriccoin.zcash.ui.design.component.Body
import co.electriccoin.zcash.ui.design.component.BodySmall import co.electriccoin.zcash.ui.design.component.BodySmall
import co.electriccoin.zcash.ui.design.component.BodyWithFiatCurrencySymbol
import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator
import co.electriccoin.zcash.ui.design.component.CircularSmallProgressIndicator import co.electriccoin.zcash.ui.design.component.CircularSmallProgressIndicator
import co.electriccoin.zcash.ui.design.component.Reference import co.electriccoin.zcash.ui.design.component.Reference
@ -88,7 +85,6 @@ import co.electriccoin.zcash.ui.fixture.WalletSnapshotFixture
import co.electriccoin.zcash.ui.screen.balances.BalancesTag import co.electriccoin.zcash.ui.screen.balances.BalancesTag
import co.electriccoin.zcash.ui.screen.balances.model.ShieldState import co.electriccoin.zcash.ui.screen.balances.model.ShieldState
import co.electriccoin.zcash.ui.screen.balances.model.StatusAction import co.electriccoin.zcash.ui.screen.balances.model.StatusAction
import co.electriccoin.zcash.ui.screen.balances.model.WalletDisplayValues
@Preview @Preview
@Composable @Composable
@ -96,7 +92,6 @@ private fun ComposableBalancesPreview() {
ZcashTheme(forceDarkMode = false) { ZcashTheme(forceDarkMode = false) {
Balances( Balances(
balanceState = BalanceStateFixture.new(), balanceState = BalanceStateFixture.new(),
isFiatConversionEnabled = false,
isHideBalances = false, isHideBalances = false,
isUpdateAvailable = false, isUpdateAvailable = false,
isShowingErrorDialog = false, isShowingErrorDialog = false,
@ -123,7 +118,6 @@ private fun ComposableBalancesShieldDarkPreview() {
ZcashTheme(forceDarkMode = true) { ZcashTheme(forceDarkMode = true) {
Balances( Balances(
balanceState = BalanceStateFixture.new(), balanceState = BalanceStateFixture.new(),
isFiatConversionEnabled = false,
isHideBalances = false, isHideBalances = false,
isUpdateAvailable = false, isUpdateAvailable = false,
isShowingErrorDialog = true, isShowingErrorDialog = true,
@ -162,7 +156,6 @@ private fun ComposableBalancesShieldErrorDialogPreview() {
@Composable @Composable
fun Balances( fun Balances(
balanceState: BalanceState, balanceState: BalanceState,
isFiatConversionEnabled: Boolean,
isHideBalances: Boolean, isHideBalances: Boolean,
isUpdateAvailable: Boolean, isUpdateAvailable: Boolean,
isShowingErrorDialog: Boolean, isShowingErrorDialog: Boolean,
@ -198,7 +191,6 @@ fun Balances(
} else { } else {
BalancesMainContent( BalancesMainContent(
balanceState = balanceState, balanceState = balanceState,
isFiatConversionEnabled = isFiatConversionEnabled,
isHideBalances = isHideBalances, isHideBalances = isHideBalances,
isUpdateAvailable = isUpdateAvailable, isUpdateAvailable = isUpdateAvailable,
onShielding = onShielding, onShielding = onShielding,
@ -353,7 +345,6 @@ private fun BalancesTopAppBar(
@Composable @Composable
private fun BalancesMainContent( private fun BalancesMainContent(
balanceState: BalanceState, balanceState: BalanceState,
isFiatConversionEnabled: Boolean,
isHideBalances: Boolean, isHideBalances: Boolean,
isUpdateAvailable: Boolean, isUpdateAvailable: Boolean,
onShielding: () -> Unit, onShielding: () -> Unit,
@ -390,7 +381,6 @@ private fun BalancesMainContent(
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault)) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
BalancesOverview( BalancesOverview(
isFiatConversionEnabled = isFiatConversionEnabled,
isHideBalances = isHideBalances, isHideBalances = isHideBalances,
walletSnapshot = walletSnapshot, walletSnapshot = walletSnapshot,
) )
@ -571,7 +561,6 @@ fun TransparentBalanceHelpPanel(onHideHelpPanel: () -> Unit) {
@Composable @Composable
fun BalancesOverview( fun BalancesOverview(
walletSnapshot: WalletSnapshot, walletSnapshot: WalletSnapshot,
isFiatConversionEnabled: Boolean,
isHideBalances: Boolean, isHideBalances: Boolean,
) { ) {
Column { Column {
@ -585,38 +574,6 @@ fun BalancesOverview(
// aka value pending // aka value pending
PendingTransactionsRow(isHideBalances, walletSnapshot) PendingTransactionsRow(isHideBalances, walletSnapshot)
if (isFiatConversionEnabled) {
val walletDisplayValues =
WalletDisplayValues.getNextValues(
context = LocalContext.current,
walletSnapshot = walletSnapshot,
isUpdateAvailable = false,
)
Column(Modifier.testTag(BalancesTag.FIAT_CONVERSION)) {
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall))
when (walletDisplayValues.fiatCurrencyAmountState) {
is FiatCurrencyConversionRateState.Current -> {
BodyWithFiatCurrencySymbol(
amount = walletDisplayValues.fiatCurrencyAmountText
)
}
is FiatCurrencyConversionRateState.Stale -> {
// Note: we should show information about staleness too
BodyWithFiatCurrencySymbol(
amount = walletDisplayValues.fiatCurrencyAmountText
)
}
is FiatCurrencyConversionRateState.Unavailable -> {
Body(text = walletDisplayValues.fiatCurrencyAmountText)
}
}
}
}
} }
} }

View File

@ -1,7 +1,6 @@
package co.electriccoin.zcash.ui.screen.integrations package co.electriccoin.zcash.ui.screen.integrations
import android.net.Uri import android.net.Uri
import android.widget.Toast
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.browser.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsIntent
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -9,7 +8,6 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.di.koinActivityViewModel import co.electriccoin.zcash.di.koinActivityViewModel
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.LocalActivity import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.compose.LocalNavController import co.electriccoin.zcash.ui.common.compose.LocalNavController
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
@ -57,12 +55,6 @@ internal fun WrapIntegrations() {
} }
} }
LaunchedEffect(Unit) {
viewModel.showFlexaErrorToastCommand.collect {
Toast.makeText(activity, R.string.integrations_flexa_key_missing, Toast.LENGTH_LONG).show()
}
}
BackHandler { BackHandler {
viewModel.onBack() viewModel.onBack()
} }

View File

@ -27,6 +27,7 @@ import co.electriccoin.zcash.ui.common.usecase.GetSynchronizerUseCase
import co.electriccoin.zcash.ui.common.usecase.GetTransparentAddressUseCase import co.electriccoin.zcash.ui.common.usecase.GetTransparentAddressUseCase
import co.electriccoin.zcash.ui.common.usecase.IsCoinbaseAvailableUseCase import co.electriccoin.zcash.ui.common.usecase.IsCoinbaseAvailableUseCase
import co.electriccoin.zcash.ui.common.usecase.IsFlexaAvailableUseCase import co.electriccoin.zcash.ui.common.usecase.IsFlexaAvailableUseCase
import co.electriccoin.zcash.ui.common.usecase.ObserveIsFlexaAvailableUseCase
import co.electriccoin.zcash.ui.common.usecase.ObserveWalletStateUseCase import co.electriccoin.zcash.ui.common.usecase.ObserveWalletStateUseCase
import co.electriccoin.zcash.ui.design.component.ZashiSettingsListItemState import co.electriccoin.zcash.ui.design.component.ZashiSettingsListItemState
import co.electriccoin.zcash.ui.design.util.stringRes import co.electriccoin.zcash.ui.design.util.stringRes
@ -40,6 +41,7 @@ import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.WhileSubscribed import kotlinx.coroutines.flow.WhileSubscribed
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -48,6 +50,7 @@ class IntegrationsViewModel(
getVersionInfo: GetVersionInfoProvider, getVersionInfo: GetVersionInfoProvider,
getZcashCurrency: GetZcashCurrencyProvider, getZcashCurrency: GetZcashCurrencyProvider,
observeWalletState: ObserveWalletStateUseCase, observeWalletState: ObserveWalletStateUseCase,
observeIsFlexaAvailableUseCase: ObserveIsFlexaAvailableUseCase,
private val getSynchronizer: GetSynchronizerUseCase, private val getSynchronizer: GetSynchronizerUseCase,
private val getTransparentAddress: GetTransparentAddressUseCase, private val getTransparentAddress: GetTransparentAddressUseCase,
private val isFlexaAvailable: IsFlexaAvailableUseCase, private val isFlexaAvailable: IsFlexaAvailableUseCase,
@ -59,7 +62,6 @@ class IntegrationsViewModel(
val backNavigationCommand = MutableSharedFlow<Unit>() val backNavigationCommand = MutableSharedFlow<Unit>()
val flexaNavigationCommand = MutableSharedFlow<Unit>() val flexaNavigationCommand = MutableSharedFlow<Unit>()
val coinbaseNavigationCommand = MutableSharedFlow<String>() val coinbaseNavigationCommand = MutableSharedFlow<String>()
val showFlexaErrorToastCommand = MutableSharedFlow<Unit>()
private val versionInfo = getVersionInfo() private val versionInfo = getVersionInfo()
private val isDebug = versionInfo.let { it.isDebuggable && !it.isRunningUnderTestService } private val isDebug = versionInfo.let { it.isDebuggable && !it.isRunningUnderTestService }
@ -71,7 +73,7 @@ class IntegrationsViewModel(
} }
val state = val state =
isEnabled.map { isEnabled -> combine(observeIsFlexaAvailableUseCase(), isEnabled) { isFlexaAvailable, isEnabled ->
IntegrationsState( IntegrationsState(
version = stringRes(R.string.integrations_version, versionInfo.versionName), version = stringRes(R.string.integrations_version, versionInfo.versionName),
disabledInfo = stringRes(R.string.integrations_disabled_info).takeIf { isEnabled.not() }, disabledInfo = stringRes(R.string.integrations_disabled_info).takeIf { isEnabled.not() },
@ -103,7 +105,7 @@ class IntegrationsViewModel(
text = stringRes(R.string.integrations_flexa), text = stringRes(R.string.integrations_flexa),
subtitle = stringRes(R.string.integrations_flexa_subtitle), subtitle = stringRes(R.string.integrations_flexa_subtitle),
onClick = ::onFlexaClicked onClick = ::onFlexaClicked
).takeIf { isFlexaAvailable() } ).takeIf { isFlexaAvailable == true }
).toImmutableList() ).toImmutableList()
) )
}.stateIn( }.stateIn(
@ -144,9 +146,7 @@ class IntegrationsViewModel(
private fun onFlexaClicked() = private fun onFlexaClicked() =
viewModelScope.launch { viewModelScope.launch {
if (BuildConfig.ZCASH_FLEXA_KEY.isEmpty()) { if (isFlexaAvailable()) {
showFlexaErrorToastCommand.emit(Unit)
} else {
flexaNavigationCommand.emit(Unit) flexaNavigationCommand.emit(Unit)
} }
} }

View File

@ -11,8 +11,8 @@ import co.electriccoin.zcash.ui.NavigationTargets.INTEGRATIONS
import co.electriccoin.zcash.ui.NavigationTargets.SUPPORT import co.electriccoin.zcash.ui.NavigationTargets.SUPPORT
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.provider.GetVersionInfoProvider import co.electriccoin.zcash.ui.common.provider.GetVersionInfoProvider
import co.electriccoin.zcash.ui.common.usecase.IsFlexaAvailableUseCase
import co.electriccoin.zcash.ui.common.usecase.ObserveConfigurationUseCase import co.electriccoin.zcash.ui.common.usecase.ObserveConfigurationUseCase
import co.electriccoin.zcash.ui.common.usecase.ObserveIsFlexaAvailableUseCase
import co.electriccoin.zcash.ui.common.usecase.RescanBlockchainUseCase import co.electriccoin.zcash.ui.common.usecase.RescanBlockchainUseCase
import co.electriccoin.zcash.ui.common.usecase.SensitiveSettingsVisibleUseCase import co.electriccoin.zcash.ui.common.usecase.SensitiveSettingsVisibleUseCase
import co.electriccoin.zcash.ui.configuration.ConfigurationEntries import co.electriccoin.zcash.ui.configuration.ConfigurationEntries
@ -38,10 +38,10 @@ import kotlinx.coroutines.launch
class SettingsViewModel( class SettingsViewModel(
observeConfiguration: ObserveConfigurationUseCase, observeConfiguration: ObserveConfigurationUseCase,
isSensitiveSettingsVisible: SensitiveSettingsVisibleUseCase, isSensitiveSettingsVisible: SensitiveSettingsVisibleUseCase,
observeIsFlexaAvailable: ObserveIsFlexaAvailableUseCase,
private val standardPreferenceProvider: StandardPreferenceProvider, private val standardPreferenceProvider: StandardPreferenceProvider,
private val getVersionInfo: GetVersionInfoProvider, private val getVersionInfo: GetVersionInfoProvider,
private val rescanBlockchain: RescanBlockchainUseCase, private val rescanBlockchain: RescanBlockchainUseCase,
private val isFlexaAvailable: IsFlexaAvailableUseCase
) : ViewModel() { ) : ViewModel() {
private val versionInfo by lazy { getVersionInfo() } private val versionInfo by lazy { getVersionInfo() }
@ -92,22 +92,29 @@ class SettingsViewModel(
val state: StateFlow<SettingsState> = val state: StateFlow<SettingsState> =
combine( combine(
troubleshootingState, troubleshootingState,
isSensitiveSettingsVisible() isSensitiveSettingsVisible(),
) { troubleshootingState, isSensitiveSettingsVisible -> observeIsFlexaAvailable(),
createState(troubleshootingState, isSensitiveSettingsVisible) ) { troubleshootingState, isSensitiveSettingsVisible, isFlexaAvailable ->
createState(
troubleshootingState = troubleshootingState,
isSensitiveSettingsVisible = isSensitiveSettingsVisible,
isFlexaAvailable = isFlexaAvailable == true
)
}.stateIn( }.stateIn(
scope = viewModelScope, scope = viewModelScope,
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
initialValue = initialValue =
createState( createState(
troubleshootingState = null, troubleshootingState = null,
isSensitiveSettingsVisible = isSensitiveSettingsVisible().value isSensitiveSettingsVisible = isSensitiveSettingsVisible().value,
isFlexaAvailable = observeIsFlexaAvailable().value == true
) )
) )
private fun createState( private fun createState(
troubleshootingState: SettingsTroubleshootingState?, troubleshootingState: SettingsTroubleshootingState?,
isSensitiveSettingsVisible: Boolean isSensitiveSettingsVisible: Boolean,
isFlexaAvailable: Boolean
) = SettingsState( ) = SettingsState(
debugMenu = troubleshootingState, debugMenu = troubleshootingState,
onBack = ::onBack, onBack = ::onBack,
@ -125,7 +132,7 @@ class SettingsViewModel(
titleIcons = titleIcons =
listOfNotNull( listOfNotNull(
R.drawable.ic_integrations_coinbase, R.drawable.ic_integrations_coinbase,
R.drawable.ic_integrations_flexa.takeIf { isFlexaAvailable() } R.drawable.ic_integrations_flexa.takeIf { isFlexaAvailable }
).toImmutableList() ).toImmutableList()
).takeIf { isSensitiveSettingsVisible }, ).takeIf { isSensitiveSettingsVisible },
ZashiSettingsListItemState( ZashiSettingsListItemState(

View File

@ -7,5 +7,4 @@
<string name="integrations_flexa">Paga con Flexa</string> <string name="integrations_flexa">Paga con Flexa</string>
<string name="integrations_flexa_subtitle">Paga con clips de pago de Flexa y explora una nueva forma de gastar Zcash.</string> <string name="integrations_flexa_subtitle">Paga con clips de pago de Flexa y explora una nueva forma de gastar Zcash.</string>
<string name="integrations_flexa_biometric_message">Autentifícate para pagar con Flexa</string> <string name="integrations_flexa_biometric_message">Autentifícate para pagar con Flexa</string>
<string name="integrations_flexa_key_missing">Clave de Flexa faltante</string>
</resources> </resources>

View File

@ -7,5 +7,4 @@
<string name="integrations_flexa">Pay with Flexa</string> <string name="integrations_flexa">Pay with Flexa</string>
<string name="integrations_flexa_subtitle">Pay with Flexa payment clips and explore a new way of spending Zcash.</string> <string name="integrations_flexa_subtitle">Pay with Flexa payment clips and explore a new way of spending Zcash.</string>
<string name="integrations_flexa_biometric_message">Authenticate yourself to pay with Flexa</string> <string name="integrations_flexa_biometric_message">Authenticate yourself to pay with Flexa</string>
<string name="integrations_flexa_key_missing">Flexa key missing</string>
</resources> </resources>