diff --git a/build.gradle b/build.gradle index 9931c2c60..6c3cdf617 100644 --- a/build.gradle +++ b/build.gradle @@ -44,7 +44,7 @@ buildscript { composeCompiler : '1.5.3', composeConstraintLayout: '1.1.0-alpha05', uiCore : '0.2.7', - soraCard : '0.1.53', + soraCard : '0.1.54', lazySodium : '5.0.2', jna : '5.8.0', accompanist : '0.30.1', diff --git a/common_wallet/src/main/java/jp/co/soramitsu/common_wallet/presentation/compose/states/CardState.kt b/common_wallet/src/main/java/jp/co/soramitsu/common_wallet/presentation/compose/states/CardState.kt index 563667706..1a53997dc 100644 --- a/common_wallet/src/main/java/jp/co/soramitsu/common_wallet/presentation/compose/states/CardState.kt +++ b/common_wallet/src/main/java/jp/co/soramitsu/common_wallet/presentation/compose/states/CardState.kt @@ -51,7 +51,9 @@ data class CardsState( val cards: List = emptyList(), ) -sealed interface CardState +sealed class CardState( + open val loading: Boolean, +) sealed interface AssetCardState @@ -62,7 +64,8 @@ data class TitledAmountCardState( val collapsedState: Boolean = false, val onCollapseClick: () -> Unit, val onExpandClick: (() -> Unit)? = null, -) : CardState + override val loading: Boolean, +) : CardState(loading) class FavoriteAssetsCardState( val assets: List @@ -118,18 +121,22 @@ fun mapAssetsToCardState( } data class SoraCardState( - val balance: String?, + val success: Boolean, + val ibanBalance: String?, val kycStatus: String?, val visible: Boolean = false, -) : CardState + override val loading: Boolean, +) : CardState(loading) data class BuyXorState( val visible: Boolean = false, -) : CardState + override val loading: Boolean, +) : CardState(loading) data class ReferralState( val visible: Boolean = false, -) : CardState + override val loading: Boolean, +) : CardState(loading) class FavoritePoolsCardState( val state: PoolsListState, diff --git a/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/GetSoraCardFragment.kt b/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/GetSoraCardFragment.kt index a14365399..0b32ac8e2 100644 --- a/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/GetSoraCardFragment.kt +++ b/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/GetSoraCardFragment.kt @@ -37,6 +37,7 @@ import android.view.View import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.ScrollState import androidx.fragment.app.viewModels +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable @@ -84,9 +85,10 @@ class GetSoraCardFragment : SoraBaseFragment() { composable( route = theOnlyRoute, ) { + val state = viewModel.state.collectAsStateWithLifecycle().value GetSoraCardScreen( scrollState = scrollState, - state = viewModel.state.value, + state = state, viewModel::onSeeBlacklist, viewModel::onEnableCard, ) diff --git a/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/GetSoraCardScreen.kt b/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/GetSoraCardScreen.kt index 1d9fbd6d3..534d63c26 100644 --- a/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/GetSoraCardScreen.kt +++ b/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/GetSoraCardScreen.kt @@ -130,7 +130,6 @@ fun GetSoraCardScreen( ), color = MaterialTheme.customColors.statusError, ) - FilledButton( modifier = Modifier .fillMaxWidth() diff --git a/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/GetSoraCardViewModel.kt b/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/GetSoraCardViewModel.kt index f3b9febd6..87f1e5071 100644 --- a/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/GetSoraCardViewModel.kt +++ b/feature_sora_card_impl/src/main/java/jp/co/soramitsu/feature_sora_card_impl/presentation/GetSoraCardViewModel.kt @@ -32,7 +32,6 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package jp.co.soramitsu.feature_sora_card_impl.presentation -import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.LiveData import androidx.lifecycle.viewModelScope import dagger.assisted.Assisted @@ -55,10 +54,12 @@ import jp.co.soramitsu.oauth.base.sdk.contract.SoraCardContractData import jp.co.soramitsu.oauth.base.sdk.contract.SoraCardResult import jp.co.soramitsu.sora.substrate.runtime.SubstrateOptionsProvider import jp.co.soramitsu.sora.substrate.substrate.ConnectionManager +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch class GetSoraCardViewModel @AssistedInject constructor( private val assetsRouter: AssetsRouter, @@ -85,41 +86,40 @@ class GetSoraCardViewModel @AssistedInject constructor( private val _launchSoraCardRegistration = SingleLiveEvent() val launchSoraCardRegistration: LiveData = _launchSoraCardRegistration - var state = mutableStateOf(GetSoraCardState(applicationFee = "")) - private set + private val _state = MutableStateFlow(GetSoraCardState(applicationFee = ".")) + val state = _state.asStateFlow() + + private var applicationFeeCache: String? = null init { _toolbarState.value = initSmallTitle2( title = R.string.get_sora_card_title, ) - viewModelScope.launch { - tryCatch { - state.value = state.value.copy( - applicationFee = soraCardInteractor.fetchApplicationFee() - ) - } - } - soraCardInteractor.subscribeToSoraCardAvailabilityFlow() - .onEach { + .combine(connectionManager.connectionState) { f1, f2 -> + f1 to f2 + } + .catch { onError(it) } + .onEach { (info, connection) -> currentSoraCardContractData = createSoraCardContract( - userAvailableXorAmount = it.xorBalance.toDouble(), - isEnoughXorAvailable = it.enoughXor, + userAvailableXorAmount = info.xorBalance.toDouble(), + isEnoughXorAvailable = info.enoughXor, ) - state.value = state.value.copy( - xorRatioAvailable = it.xorRatioAvailable, + _state.value = _state.value.copy( + connection = connection, + applicationFee = fetchApplicationFee(), + xorRatioAvailable = info.xorRatioAvailable, ) - }.launchIn(viewModelScope) - - connectionManager.connectionState - .catch { onError(it) } - .onEach { - state.value = state.value.copy(connection = it) } .launchIn(viewModelScope) } + private suspend fun fetchApplicationFee(): String = + applicationFeeCache ?: soraCardInteractor.fetchApplicationFee().also { + applicationFeeCache = it + } + fun handleSoraCardResult(soraCardResult: SoraCardResult) { when (soraCardResult) { is SoraCardResult.NavigateTo -> { diff --git a/feature_wallet_impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/presentation/cardshub/CardsHubFragment.kt b/feature_wallet_impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/presentation/cardshub/CardsHubFragment.kt index a90aa4da4..ab9a2e68f 100644 --- a/feature_wallet_impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/presentation/cardshub/CardsHubFragment.kt +++ b/feature_wallet_impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/presentation/cardshub/CardsHubFragment.kt @@ -34,14 +34,17 @@ package jp.co.soramitsu.feature_wallet_impl.presentation.cardshub import android.os.Bundle import android.view.View -import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.ScrollState import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material.CircularProgressIndicator +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -49,6 +52,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.fragment.app.viewModels import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -60,8 +64,10 @@ import jp.co.soramitsu.common.R import jp.co.soramitsu.common.base.SoraBaseFragment import jp.co.soramitsu.common.base.theOnlyRoute import jp.co.soramitsu.common.domain.BottomBarController +import jp.co.soramitsu.common.util.StringPair import jp.co.soramitsu.common_wallet.presentation.compose.components.PoolsList import jp.co.soramitsu.common_wallet.presentation.compose.states.BuyXorState +import jp.co.soramitsu.common_wallet.presentation.compose.states.CardsState import jp.co.soramitsu.common_wallet.presentation.compose.states.FavoriteAssetsCardState import jp.co.soramitsu.common_wallet.presentation.compose.states.FavoritePoolsCardState import jp.co.soramitsu.common_wallet.presentation.compose.states.ReferralState @@ -72,6 +78,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.resources.Dimens +import jp.co.soramitsu.ui_core.theme.customColors @AndroidEntryPoint class CardsHubFragment : SoraBaseFragment() { @@ -90,7 +97,6 @@ class CardsHubFragment : SoraBaseFragment() { } } - @OptIn(ExperimentalAnimationApi::class) override fun NavGraphBuilder.content( scrollState: ScrollState, navController: NavHostController @@ -110,78 +116,133 @@ class CardsHubFragment : SoraBaseFragment() { .fillMaxSize() ) { val state = viewModel.state.collectAsStateWithLifecycle().value - TopBar( - account = state.curAccount, + CardsMainScreen( + scrollState = scrollState, + state = state, onAccountClick = viewModel::onAccountClick, onQrClick = onQrClick, + onAssetClick = viewModel::onAssetClick, + onPoolClick = viewModel::onPoolClick, + onSoraCardClick = viewModel::onCardStateClicked, + onSoraCardClose = viewModel::onRemoveSoraCard, + onBuyXorClick = viewModel::onBuyCrypto, + onBuyXorClose = viewModel::onRemoveBuyXorToken, + onReferralClick = viewModel::onStartReferral, + onReferralClose = viewModel::onRemoveReferralCard, + onEdit = viewModel::onEditViewClick, ) - Spacer(modifier = Modifier.size(size = 16.dp)) - Column( - modifier = Modifier - .fillMaxSize() - .verticalScroll(scrollState) - .padding(horizontal = Dimens.x2) - ) { - state.cards.forEach { cardState -> - when (cardState) { - is TitledAmountCardState -> { - CommonHubCard( - title = cardState.title, - amount = cardState.amount, - onExpandClick = cardState.onExpandClick, - collapseState = cardState.collapsedState, - onCollapseClick = cardState.onCollapseClick - ) { - when (cardState.state) { - is FavoriteAssetsCardState -> AssetsCard( - cardState.state as FavoriteAssetsCardState, - viewModel::onAssetClick, - ) - is FavoritePoolsCardState -> PoolsList( - (cardState.state as FavoritePoolsCardState).state, - viewModel::onPoolClick, - ) - } - } - } - - is SoraCardState -> { - SoraCard( - state = cardState, - onCardStateClicked = viewModel::onCardStateClicked, - onCloseClicked = viewModel::onRemoveSoraCard, - ) - } - - is BuyXorState -> { - BuyXorCard( - onBuyXorClicked = viewModel::onBuyCrypto, - onCloseCard = viewModel::onRemoveBuyXorToken, - ) - } - - is ReferralState -> { - ReferralCard( - onStartClicked = viewModel::onStartReferral, - onCloseCard = viewModel::onRemoveReferralCard, - ) - } + } + } + } +} + +@Composable +private fun CardsMainScreen( + scrollState: ScrollState, + state: CardsState, + onAccountClick: () -> Unit, + onQrClick: () -> Unit, + onAssetClick: (String) -> Unit, + onPoolClick: (StringPair) -> Unit, + onSoraCardClick: () -> Unit, + onSoraCardClose: () -> Unit, + onBuyXorClick: () -> Unit, + onBuyXorClose: () -> Unit, + onReferralClick: () -> Unit, + onReferralClose: () -> Unit, + onEdit: () -> Unit, +) { + TopBar( + account = state.curAccount, + onAccountClick = onAccountClick, + onQrClick = onQrClick, + ) + Spacer(modifier = Modifier.size(size = 16.dp)) + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(scrollState) + .padding(horizontal = Dimens.x2), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + if (state.loading) { + CircularProgressIndicator( + color = MaterialTheme.customColors.fgPrimary, + ) + } + state.cards.forEach { cardState -> + when (cardState) { + is TitledAmountCardState -> { + CommonHubCard( + title = cardState.title, + amount = cardState.amount, + onExpandClick = cardState.onExpandClick, + collapseState = cardState.collapsedState, + onCollapseClick = cardState.onCollapseClick + ) { + when (cardState.state) { + is FavoriteAssetsCardState -> AssetsCard( + cardState.state as FavoriteAssetsCardState, + onAssetClick, + ) + is FavoritePoolsCardState -> PoolsList( + (cardState.state as FavoritePoolsCardState).state, + onPoolClick, + ) } - Spacer(modifier = Modifier.size(size = 16.dp)) } + } - if (state.cards.isNotEmpty()) - BleachedButton( - modifier = Modifier - .padding(bottom = Dimens.x4) - .align(Alignment.CenterHorizontally), - size = Size.Small, - order = Order.SECONDARY, - text = stringResource(id = R.string.edit_view), - onClick = { viewModel.onEditViewClick() } - ) + is SoraCardState -> { + SoraCard( + state = cardState, + onCardStateClicked = onSoraCardClick, + onCloseClicked = onSoraCardClose, + ) + } + + is BuyXorState -> { + BuyXorCard( + onBuyXorClicked = onBuyXorClick, + onCloseCard = onBuyXorClose, + ) + } + + is ReferralState -> { + ReferralCard( + onStartClicked = onReferralClick, + onCloseCard = onReferralClose, + ) } } + Spacer(modifier = Modifier.size(size = 16.dp)) } + + if (state.cards.isNotEmpty()) + BleachedButton( + modifier = Modifier + .padding(bottom = Dimens.x4) + .align(Alignment.CenterHorizontally), + size = Size.Small, + order = Order.SECONDARY, + text = stringResource(id = R.string.edit_view), + onClick = onEdit, + ) + } +} + +@Composable +@Preview +private fun PreviewCardsMainScreen() { + Column() { + CardsMainScreen( + scrollState = rememberScrollState(), + state = CardsState( + curAccount = "cnVko", + loading = true, + cards = emptyList(), + ), + {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, + ) } } 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 68f1cd93f..f183a7354 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 @@ -44,12 +44,12 @@ import jp.co.soramitsu.common.domain.CoroutineManager import jp.co.soramitsu.common.domain.fiatSum import jp.co.soramitsu.common.domain.fiatSymbol import jp.co.soramitsu.common.domain.formatFiatAmount -import jp.co.soramitsu.common.interfaces.WithProgress import jp.co.soramitsu.common.presentation.SingleLiveEvent import jp.co.soramitsu.common.presentation.viewmodel.BaseViewModel import jp.co.soramitsu.common.resourses.ResourceManager import jp.co.soramitsu.common.util.NumbersFormatter import jp.co.soramitsu.common.util.StringPair +import jp.co.soramitsu.common.util.ext.safeCast import jp.co.soramitsu.common_wallet.domain.model.CommonUserPoolData import jp.co.soramitsu.common_wallet.domain.model.fiatSymbol import jp.co.soramitsu.common_wallet.presentation.compose.states.BuyXorState @@ -85,10 +85,12 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.withIndex import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -98,7 +100,6 @@ class CardsHubViewModel @Inject constructor( private val poolsInteractor: PoolsInteractor, private val cardsHubInteractorImpl: CardsHubInteractorImpl, private val numbersFormatter: NumbersFormatter, - private val progress: WithProgress, private val resourceManager: ResourceManager, private val router: WalletRouter, private val mainRouter: MainRouter, @@ -108,7 +109,7 @@ class CardsHubViewModel @Inject constructor( private val connectionManager: ConnectionManager, private val soraCardInteractor: SoraCardInteractor, private val coroutineManager: CoroutineManager, -) : BaseViewModel(), WithProgress by progress { +) : BaseViewModel() { private val _state = MutableStateFlow( CardsState( @@ -135,12 +136,23 @@ class CardsHubViewModel @Inject constructor( .subscribeVisibleCardsHubList() .catch { onError(it) } .distinctUntilChanged() - .flatMapLatest { data -> - _state.value = _state.value.copy(curAccount = data.first.accountTitle()) + .withIndex() + .flatMapLatest { indexed -> + val data = indexed.value + if (indexed.index == 0) { + _state.value = _state.value.copy( + curAccount = data.first.accountTitle(), + loading = false, + cards = emptyList(), + ) + } val flows = data.second.filter { it.visibility }.map { cardHub -> when (cardHub.cardType) { CardHubType.ASSETS -> { assetsInteractor.subscribeAssetsFavoriteOfAccount(data.first) + .onStart { + this.emit(emptyList()) + } .map { cardHub to it } @@ -148,38 +160,55 @@ class CardsHubViewModel @Inject constructor( CardHubType.POOLS -> { poolsInteractor.subscribePoolsCacheOfAccount(data.first) + .onStart { + this.emit(emptyList()) + } .map { list -> cardHub to list.filter { it.user.favorite } } } CardHubType.GET_SORA_CARD -> { - soraCardInteractor.subscribeSoraCardStatus().map { status -> - val mapped = mapKycStatus(status) - cardHub to listOf( - SoraCardState( + soraCardInteractor.subscribeSoraCardStatus() + .map { status -> + val mapped = mapKycStatus(status) + cardHub to SoraCardState( visible = cardHub.visibility, - kycStatus = mapped, - balance = if (status == SoraCardCommonVerification.Successful) soraCardInteractor.fetchIbanBalance().getOrDefault("") else null, + kycStatus = mapped.first, + loading = false, + success = mapped.second, + ibanBalance = if (mapped.second) soraCardInteractor.fetchIbanBalance().getOrNull() else null, ) - ) - } + } + .onStart { + this.emit( + cardHub to SoraCardState( + success = false, + kycStatus = null, + visible = cardHub.visibility, + loading = true, + ibanBalance = null, + ) + ) + } } CardHubType.REFERRAL_SYSTEM -> { - flow { - emit(listOf(ReferralState(visible = cardHub.visibility))) - }.map { - cardHub to it - } + flowOf( + cardHub to ReferralState( + visible = cardHub.visibility, + loading = false, + ) + ) } CardHubType.BUY_XOR_TOKEN -> { - flow { - emit(listOf(BuyXorState(visible = cardHub.visibility))) - }.map { - cardHub to it - } + flowOf( + cardHub to BuyXorState( + visible = cardHub.visibility, + loading = false, + ) + ) } } } @@ -187,8 +216,8 @@ class CardsHubViewModel @Inject constructor( } .distinctUntilChanged() .collectLatest { cards -> - val usableCards = cards.filter { - it.first.cardType == CardHubType.ASSETS || it.second.isNotEmpty() + val usableCards = cards.filterNot { + (it.first.cardType != CardHubType.ASSETS) && (it.second.safeCast>()?.size == 0) } _state.value = _state.value.copy( loading = false, @@ -205,26 +234,26 @@ class CardsHubViewModel @Inject constructor( }.launchIn(viewModelScope) } - private fun mapKycStatus(kycStatus: SoraCardCommonVerification): String? { + private fun mapKycStatus(kycStatus: SoraCardCommonVerification): Pair { return when (kycStatus) { SoraCardCommonVerification.Failed -> { - resourceManager.getString(R.string.sora_card_verification_failed) + resourceManager.getString(R.string.sora_card_verification_failed) to false } SoraCardCommonVerification.Rejected -> { - resourceManager.getString(R.string.sora_card_verification_rejected) + resourceManager.getString(R.string.sora_card_verification_rejected) to false } SoraCardCommonVerification.Pending -> { - resourceManager.getString(R.string.sora_card_verification_in_progress) + resourceManager.getString(R.string.sora_card_verification_in_progress) to false } SoraCardCommonVerification.Successful -> { - resourceManager.getString(R.string.sora_card_verification_successful) + resourceManager.getString(R.string.sora_card_verification_successful) to true } else -> { - null + null to false } } } @@ -284,9 +313,9 @@ class CardsHubViewModel @Inject constructor( it.second as List ) - CardHubType.GET_SORA_CARD -> (it.second as List).first() - CardHubType.BUY_XOR_TOKEN -> (it.second as List).first() - CardHubType.REFERRAL_SYSTEM -> (it.second as List).first() + CardHubType.GET_SORA_CARD -> (it.second as SoraCardState) + CardHubType.BUY_XOR_TOKEN -> (it.second as BuyXorState) + CardHubType.REFERRAL_SYSTEM -> (it.second as ReferralState) } } } @@ -298,7 +327,8 @@ class CardsHubViewModel @Inject constructor( state = FavoriteAssetsCardState(mapAssetsToCardState(assets, numbersFormatter)), collapsedState = collapsed, onCollapseClick = { collapseCardToggle(CardHubType.ASSETS.hubName, !collapsed) }, - onExpandClick = ::expandAssetsCard + loading = false, + onExpandClick = ::expandAssetsCard, ) } @@ -311,6 +341,7 @@ class CardsHubViewModel @Inject constructor( state = data.first ), onExpandClick = ::expandPoolsCard, + loading = false, onCollapseClick = { collapseCardToggle(CardHubType.POOLS.hubName, !collapsed) }, collapsedState = collapsed ) @@ -338,7 +369,7 @@ class CardsHubViewModel @Inject constructor( if (card.kycStatus == null) { if (!connectionManager.isConnected) return mainRouter.showGetSoraCard() - } else if (card.balance != null) { + } else if (card.success) { mainRouter.showSoraCardDetails() } else { currentSoraCardContractData?.let { contractData -> diff --git a/feature_wallet_impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/presentation/cardshub/SoraCard.kt b/feature_wallet_impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/presentation/cardshub/SoraCard.kt index 620855efd..0b1b3707b 100644 --- a/feature_wallet_impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/presentation/cardshub/SoraCard.kt +++ b/feature_wallet_impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/presentation/cardshub/SoraCard.kt @@ -33,13 +33,16 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package jp.co.soramitsu.feature_wallet_impl.presentation.cardshub import android.content.res.Configuration +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -48,8 +51,8 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import jp.co.soramitsu.common.R -import jp.co.soramitsu.common.domain.OptionsProvider.euroSign import jp.co.soramitsu.common.presentation.compose.components.SoraCardImage import jp.co.soramitsu.common.presentation.compose.theme.SoraAppTheme import jp.co.soramitsu.common.util.ext.testTagAsId @@ -61,6 +64,7 @@ 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.resources.Dimens import jp.co.soramitsu.ui_core.theme.borderRadius +import jp.co.soramitsu.ui_core.theme.customColors @Composable fun SoraCard( @@ -73,30 +77,43 @@ fun SoraCard( Box( modifier = modifier .clip(shape) - .clickable { onCardStateClicked() } + .clickable(onClick = onCardStateClicked, enabled = state.loading.not()) .fillMaxWidth() ) { SoraCardImage( modifier = Modifier.fillMaxWidth(), ) - CardStateButton( - modifier = Modifier - .wrapContentWidth() - .run { - if (state.balance == null) - padding(bottom = Dimens.x3) else padding(all = Dimens.x1) - } - .run { - if (state.balance == null) - align(Alignment.BottomCenter) else align(Alignment.BottomEnd) - }, - kycStatus = state.kycStatus, - balance = state.balance, - onCardStateClicked = onCardStateClicked, - ) + if (state.loading) { + CircularProgressIndicator( + modifier = Modifier + .padding(bottom = Dimens.x2, end = Dimens.x2) + .size(Dimens.x5) + .background(color = MaterialTheme.customColors.bgSurface, shape = CircleShape) + .align(Alignment.BottomEnd) + .padding(8.dp), + color = MaterialTheme.customColors.fgPrimary, + ) + } else { + CardStateButton( + modifier = Modifier + .wrapContentWidth() + .run { + if (state.success.not()) + padding(bottom = Dimens.x3) else padding(all = Dimens.x1) + } + .run { + if (state.success.not()) + align(Alignment.BottomCenter) else align(Alignment.BottomEnd) + }, + kycStatus = state.kycStatus, + balance = state.ibanBalance, + success = state.success, + onCardStateClicked = onCardStateClicked, + ) + } - if (state.balance == null) + if (state.success.not()) BleachedButton( modifier = Modifier .wrapContentWidth() @@ -116,6 +133,7 @@ private fun CardStateButton( modifier: Modifier = Modifier, kycStatus: String?, balance: String?, + success: Boolean, onCardStateClicked: () -> Unit ) { if (kycStatus == null) { @@ -128,14 +146,14 @@ private fun CardStateButton( text = stringResource(R.string.sora_card_see_details), ) } else { - if (balance != null) + if (success) BleachedButton( modifier = modifier .testTagAsId("CardInfo"), size = Size.ExtraSmall, order = Order.SECONDARY, onClick = onCardStateClicked, - text = balance, + text = balance ?: stringResource(id = R.string.cant_load_balance), ) else TonalButton( @@ -154,7 +172,14 @@ private fun CardStateButton( private fun PreviewSoraCard1() { SoraCard( modifier = Modifier.fillMaxWidth(), - state = SoraCardState(kycStatus = "", balance = "${euroSign}345.3", visible = true), + state = SoraCardState( + kycStatus = "", +// ibanBalance = "${euroSign}345.3", + ibanBalance = null, + visible = true, + loading = false, + success = true, + ), onCloseClicked = {}, onCardStateClicked = {} ) @@ -166,7 +191,13 @@ private fun PreviewSoraCard2() { SoraAppTheme { SoraCard( modifier = Modifier.fillMaxWidth(), - state = SoraCardState(kycStatus = "Pending", balance = null, visible = true), + state = SoraCardState( + kycStatus = "Pending", + ibanBalance = null, + visible = true, + loading = false, + success = false, + ), onCloseClicked = {}, onCardStateClicked = {} ) @@ -179,7 +210,32 @@ private fun PreviewSoraCard3() { SoraAppTheme { SoraCard( modifier = Modifier.fillMaxWidth(), - state = SoraCardState(kycStatus = null, balance = null, visible = true), + state = SoraCardState( + kycStatus = null, + ibanBalance = null, + visible = true, + loading = false, + success = false, + ), + onCloseClicked = {}, + onCardStateClicked = {} + ) + } +} + +@Composable +@Preview(locale = "en", uiMode = Configuration.UI_MODE_NIGHT_NO) +private fun PreviewSoraCard4() { + SoraAppTheme { + SoraCard( + modifier = Modifier.fillMaxWidth(), + state = SoraCardState( + kycStatus = null, + ibanBalance = null, + visible = true, + loading = true, + success = false, + ), onCloseClicked = {}, onCardStateClicked = {} ) diff --git a/feature_wallet_impl/src/test/java/jp/co/soramitsu/feature_wallet_impl/presentation/wallet/CardsHubViewModelTest.kt b/feature_wallet_impl/src/test/java/jp/co/soramitsu/feature_wallet_impl/presentation/wallet/CardsHubViewModelTest.kt index 37cf3813d..426867302 100644 --- a/feature_wallet_impl/src/test/java/jp/co/soramitsu/feature_wallet_impl/presentation/wallet/CardsHubViewModelTest.kt +++ b/feature_wallet_impl/src/test/java/jp/co/soramitsu/feature_wallet_impl/presentation/wallet/CardsHubViewModelTest.kt @@ -204,7 +204,6 @@ class CardsHubViewModelTest { poolsInteractor, cardsHubInteractorImpl, numbersFormatter, - progress, resourceManager, router, mainRouter,