diff --git a/feature_assets_impl/src/main/java/jp/co/soramitsu/feature_assets_impl/presentation/components/compose/assetdetails/AssetDetailsBalanceCard.kt b/feature_assets_impl/src/main/java/jp/co/soramitsu/feature_assets_impl/presentation/components/compose/assetdetails/AssetDetailsBalanceCard.kt index db0ce31e7..44cdcf20f 100644 --- a/feature_assets_impl/src/main/java/jp/co/soramitsu/feature_assets_impl/presentation/components/compose/assetdetails/AssetDetailsBalanceCard.kt +++ b/feature_assets_impl/src/main/java/jp/co/soramitsu/feature_assets_impl/presentation/components/compose/assetdetails/AssetDetailsBalanceCard.kt @@ -63,6 +63,7 @@ import jp.co.soramitsu.ui_core.component.button.BleachedButton import jp.co.soramitsu.ui_core.component.button.properties.Order import jp.co.soramitsu.ui_core.component.button.properties.Size import jp.co.soramitsu.ui_core.component.card.ContentCard +import jp.co.soramitsu.ui_core.extensions.withOpacity import jp.co.soramitsu.ui_core.resources.Dimens import jp.co.soramitsu.ui_core.theme.customColors import jp.co.soramitsu.ui_core.theme.customTypography @@ -186,17 +187,20 @@ internal fun AssetDetailsBalanceCard( AmountCardIcon( res = R.drawable.ic_new_arrow_up_24, text = stringResource(id = R.string.common_send), + enabled = true, onClick = onSendClick, ) } AmountCardIcon( res = R.drawable.ic_new_arrow_down_24, text = stringResource(id = R.string.common_receive), + enabled = true, onClick = onReceiveClick, ) AmountCardIcon( res = R.drawable.ic_refresh_24, text = stringResource(id = R.string.polkaswap_swap_title), + enabled = true, onClick = onSwapClick, ) @@ -204,6 +208,7 @@ internal fun AssetDetailsBalanceCard( AmountCardIcon( res = R.drawable.ic_buy_crypto, text = stringResource(id = R.string.common_buy), + enabled = false, onClick = onBuyCryptoClick ) } @@ -216,6 +221,7 @@ internal fun AssetDetailsBalanceCard( private fun AmountCardIcon( @DrawableRes res: Int, text: String, + enabled: Boolean, onClick: () -> Unit, ) { Column( @@ -228,6 +234,7 @@ private fun AmountCardIcon( shape = CircleShape, size = Size.Large, order = Order.TERTIARY, + enabled = enabled, leftIcon = painterResource(res), onClick = onClick, ) @@ -238,7 +245,7 @@ private fun AmountCardIcon( .padding(top = Dimens.x1), text = text, style = MaterialTheme.customTypography.textXSBold, - color = MaterialTheme.customColors.fgSecondary, + color = MaterialTheme.customColors.fgSecondary.withOpacity(opacity = if (enabled) 1.0f else 0.3f), ) } } diff --git a/feature_main_impl/src/main/java/jp/co/soramitsu/feature_main_impl/presentation/profile/ProfileScreen.kt b/feature_main_impl/src/main/java/jp/co/soramitsu/feature_main_impl/presentation/profile/ProfileScreen.kt index 439e20f53..dd939ca94 100644 --- a/feature_main_impl/src/main/java/jp/co/soramitsu/feature_main_impl/presentation/profile/ProfileScreen.kt +++ b/feature_main_impl/src/main/java/jp/co/soramitsu/feature_main_impl/presentation/profile/ProfileScreen.kt @@ -85,15 +85,15 @@ internal fun ProfileItems( icon = R.drawable.ic_buy_crypto, onClick = onSoraCardClick, ) - CategoryItem( - modifier = Modifier - .testTagAsId("BuyXor") - .padding(top = Dimens.x2), - title = stringResource(id = R.string.buy_crypto_buy_xor_with_fiat_title), - subtitle = stringResource(id = R.string.buy_crypto_buy_xor_with_fiat_subtitle), - icon = R.drawable.ic_settings_buy_crypto, - onClick = onBuyCrypto, - ) +// CategoryItem( +// modifier = Modifier +// .testTagAsId("BuyXor") +// .padding(top = Dimens.x2), +// title = stringResource(id = R.string.buy_crypto_buy_xor_with_fiat_title), +// subtitle = stringResource(id = R.string.buy_crypto_buy_xor_with_fiat_subtitle), +// icon = R.drawable.ic_settings_buy_crypto, +// onClick = onBuyCrypto, +// ) } CategoryItem( modifier = Modifier diff --git a/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/domain/SoraCardInteractorImpl.kt b/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/domain/SoraCardInteractorImpl.kt index c590be16b..9c527aa5e 100644 --- a/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/domain/SoraCardInteractorImpl.kt +++ b/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/domain/SoraCardInteractorImpl.kt @@ -48,16 +48,19 @@ import jp.co.soramitsu.oauth.base.sdk.contract.IbanInfo import jp.co.soramitsu.oauth.base.sdk.contract.SoraCardCommonVerification import jp.co.soramitsu.sora.substrate.runtime.SubstrateOptionsProvider import kotlin.math.min +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch internal class SoraCardInteractorImpl @Inject constructor( private val assetsInteractor: AssetsInteractor, @@ -85,38 +88,42 @@ internal class SoraCardInteractorImpl @Inject constructor( @Suppress("UNCHECKED_CAST") override suspend fun initialize() { - combine( - flow { emit(soraCardClientProxy.init()) }, - needInstallUpdate(), - fetchApplicationFee(), - _ibanFlow.asStateFlow(), - subscribeToSoraCardAvailabilityFlow(), - checkSoraCardPending(), - _phoneFlow.asStateFlow(), - ) { flows -> - val init = flows[0] as Pair - val needUpdate = flows[1] as Boolean - val fee = flows[2] as String - val ibanInfo = flows[3] as IbanInfo? - val availability = flows[4] as SoraCardAvailabilityInfo - val verification = flows[5] as SoraCardCommonVerification - val phone = flows[6] as String - SoraCardBasicStatus( - initialized = init.first, - initError = init.second, - availabilityInfo = availability, - verification = verification, - needInstallUpdate = needUpdate, - applicationFee = fee, - ibanInfo = ibanInfo, - phone = phone, - ) - } - .debounce(1000) - .collect { - _soraCardBasicStatus.value = it + coroutineScope { + launch { + resetInfo() } - resetInfo() + combine( + flow { emit(soraCardClientProxy.init()) }, + needInstallUpdate(), + fetchApplicationFee(), + _ibanFlow.asStateFlow(), + subscribeToSoraCardAvailabilityFlow(), + checkSoraCardPending(), + _phoneFlow.asStateFlow(), + ) { flows -> + val init = flows[0] as Pair + val needUpdate = flows[1] as Boolean + val fee = flows[2] as String + val ibanInfo = flows[3] as IbanInfo? + val availability = flows[4] as SoraCardAvailabilityInfo + val verification = flows[5] as SoraCardCommonVerification + val phone = flows[6] as String + SoraCardBasicStatus( + initialized = init.first, + initError = init.second, + availabilityInfo = availability, + verification = verification, + needInstallUpdate = needUpdate, + applicationFee = fee, + ibanInfo = ibanInfo, + phone = phone, + ) + } + .debounce(1000) + .collectLatest { + _soraCardBasicStatus.value = it + } + } } override val basicStatus: StateFlow = _soraCardBasicStatus.asStateFlow() diff --git a/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/details/SoraCardDetailsViewModel.kt b/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/details/SoraCardDetailsViewModel.kt index e05e879c9..804e1365a 100644 --- a/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/details/SoraCardDetailsViewModel.kt +++ b/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/details/SoraCardDetailsViewModel.kt @@ -108,12 +108,10 @@ class SoraCardDetailsViewModel @Inject constructor( ibanCache = basicStatus.ibanInfo val phoneFormatted = basicStatus.phone?.let { "+$it" } _soraCardDetailsScreenState.value = local.copy( - soraCardIBANCardState = basicStatus.ibanInfo?.let { iban -> - SoraCardIBANCardState( - iban.iban, - iban.ibanStatus == IbanStatus.CLOSED, - ) - }, + soraCardIBANCardState = SoraCardIBANCardState( + iban = basicStatus.ibanInfo?.iban.orEmpty(), + closed = basicStatus.ibanInfo?.ibanStatus == IbanStatus.CLOSED, + ), soraCardMainSoraContentCardState = local.soraCardMainSoraContentCardState.copy( balance = basicStatus.ibanInfo?.balance, phone = phoneFormatted, diff --git a/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/details/SoraCardMainSoraContentCard.kt b/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/details/SoraCardMainSoraContentCard.kt index 852c70fcd..579d9ced8 100644 --- a/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/details/SoraCardMainSoraContentCard.kt +++ b/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/details/SoraCardMainSoraContentCard.kt @@ -180,7 +180,7 @@ fun SoraCardMainSoraContentCard( .padding(horizontal = Dimens.x1) .weight(1f), size = Size.Large, - enabled = true, + enabled = false, order = Order.PRIMARY, onClick = onExchangeXor, text = stringResource(id = jp.co.soramitsu.oauth.R.string.exchange_xor), diff --git a/feature_sora_card_impl/src/test/java/jp/co/soramitsu/feature_sora_card_impl/presentation/get/card/SoraCardInteractorTest.kt b/feature_sora_card_impl/src/test/java/jp/co/soramitsu/feature_sora_card_impl/presentation/get/card/SoraCardInteractorTest.kt new file mode 100644 index 000000000..dcaaa75af --- /dev/null +++ b/feature_sora_card_impl/src/test/java/jp/co/soramitsu/feature_sora_card_impl/presentation/get/card/SoraCardInteractorTest.kt @@ -0,0 +1,120 @@ +package jp.co.soramitsu.feature_sora_card_impl.presentation.get.card + +import java.math.BigDecimal +import jp.co.soramitsu.androidfoundation.coroutine.CoroutineManager +import jp.co.soramitsu.androidfoundation.testing.MainCoroutineRule +import jp.co.soramitsu.demeter.domain.DemeterFarmingInteractor +import jp.co.soramitsu.feature_assets_api.domain.AssetsInteractor +import jp.co.soramitsu.feature_polkaswap_api.domain.interfaces.PoolsInteractor +import jp.co.soramitsu.feature_sora_card_api.domain.SoraCardInteractor +import jp.co.soramitsu.feature_sora_card_impl.domain.SoraCardClientProxy +import jp.co.soramitsu.feature_sora_card_impl.domain.SoraCardInteractorImpl +import jp.co.soramitsu.oauth.base.sdk.contract.IbanInfo +import jp.co.soramitsu.oauth.base.sdk.contract.IbanStatus +import jp.co.soramitsu.oauth.base.sdk.contract.SoraCardCommonVerification +import jp.co.soramitsu.test_data.PolkaswapTestData.POOL_DATA +import jp.co.soramitsu.test_data.TestAssets +import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.advanceTimeBy +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.junit.MockitoJUnitRunner +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.wheneverBlocking + +@RunWith(MockitoJUnitRunner::class) +@OptIn(ExperimentalCoroutinesApi::class) +class SoraCardInteractorTest { + + @get:Rule + var mainCoroutineRule = MainCoroutineRule() + + @Mock + private lateinit var ai: AssetsInteractor + + @Mock + private lateinit var pi: PoolsInteractor + + @Mock + private lateinit var sccp: SoraCardClientProxy + + @Mock + private lateinit var dfi: DemeterFarmingInteractor + + @Mock + private lateinit var cm: CoroutineManager + + private lateinit var soraCardInteractor: SoraCardInteractor + + @OptIn(ExperimentalStdlibApi::class) + @Before + fun setUp() = runTest { + `when`(cm.io).thenReturn(this.coroutineContext[CoroutineDispatcher]!!) + wheneverBlocking { sccp.init() } doReturn (true to "") + wheneverBlocking { sccp.getVersion() } doReturn Result.success("2.3.4") + wheneverBlocking { sccp.getApplicationFee() } doReturn "$8.98" + wheneverBlocking { + sccp.getKycStatus() + } doReturn Result.success(SoraCardCommonVerification.Successful) + wheneverBlocking { ai.subscribeAssetOfCurAccount(any()) } doReturn flowOf(TestAssets.xorAsset()) + wheneverBlocking { pi.getPoolsCacheOfCurAccount() } doReturn listOf(POOL_DATA) + wheneverBlocking { dfi.getStakedFarmedBalanceOfAsset(any()) } doReturn BigDecimal.ONE + soraCardInteractor = SoraCardInteractorImpl( + assetsInteractor = ai, + poolsInteractor = pi, + soraCardClientProxy = sccp, + demeterFarmingInteractor = dfi, + coroutineManager = cm, + ) + } + + @Test + fun `test init with full data`() = runTest { + wheneverBlocking { sccp.getIBAN() } doReturn Result.success( + IbanInfo( + "iban", + IbanStatus.ACTIVE, + "$12.34", + "ok", + ) + ) + wheneverBlocking { sccp.getPhone() } doReturn "+123" + advanceUntilIdle() + backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) { + soraCardInteractor.initialize() + } + advanceTimeBy(1100.milliseconds) + val bs = soraCardInteractor.basicStatus.value + assertTrue(bs.initialized) + } + + @Test + fun `test call status set`() = runTest { + wheneverBlocking { sccp.getIBAN() } doReturn Result.success(null) + wheneverBlocking { sccp.getPhone() } doReturn "+123" + advanceUntilIdle() + backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) { + soraCardInteractor.initialize() + } + advanceTimeBy(1100.milliseconds) + var bs = soraCardInteractor.basicStatus.value + assertTrue(bs.initialized) + soraCardInteractor.setStatus(SoraCardCommonVerification.Pending) + advanceUntilIdle() + bs = soraCardInteractor.basicStatus.value + assertTrue(bs.initialized) + } +} diff --git a/feature_wallet_impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/repository/WalletRepositoryImpl.kt b/feature_wallet_impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/repository/WalletRepositoryImpl.kt index 60966ff07..39a1e9f57 100644 --- a/feature_wallet_impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/repository/WalletRepositoryImpl.kt +++ b/feature_wallet_impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/repository/WalletRepositoryImpl.kt @@ -129,7 +129,8 @@ class WalletRepositoryImpl @Inject constructor( val card = CardsHubMapper.map(cardLocal) when (card?.cardType) { CardHubType.GET_SORA_CARD -> if (soraCard) card else null - CardHubType.BUY_XOR_TOKEN -> if (soraCard) card else null +// CardHubType.BUY_XOR_TOKEN -> if (soraCard) card else null + CardHubType.BUY_XOR_TOKEN -> null else -> card } } diff --git a/feature_wallet_impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/presentation/cardshub/CardsHubViewModel.kt b/feature_wallet_impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/presentation/cardshub/CardsHubViewModel.kt index 969391ffe..ef9221a2a 100644 --- a/feature_wallet_impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/presentation/cardshub/CardsHubViewModel.kt +++ b/feature_wallet_impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/presentation/cardshub/CardsHubViewModel.kt @@ -144,7 +144,6 @@ class CardsHubViewModel @Inject constructor( _state.value = _state.value.copy( accountAddress = data.first.substrateAddress, curAccount = data.first.accountTitle(), - loading = false, ) val flows = data.second.filter { it.visibility }.map { cardHub -> when (cardHub.cardType) { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 52660d701..fc48ed339 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -16,7 +16,7 @@ svg = "1.4" jdenticon = "1.1" archFragmentTest = "1.7.1" mockk = "1.13.8" -truth = "1.1.5" +truth = "1.4.2" mockitoKotlin = "5.2.1" mockito = "5.2.0" junit = "4.13.2" @@ -39,7 +39,7 @@ firebaseAppDistribution = "3.2.0" tripletVersion = "3.8.4" timber = "5.0.1" ktor = "2.3.1" -soracard = "1.1.15" +soracard = "1.1.16" xnetworkingLib = "1.0.7" xsubstrate = "1.2.7" uicore = "0.2.32"