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:
parent
882605d7a9
commit
d4be4a5dda
|
@ -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() {
|
||||||
|
|
|
@ -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 = {},
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue