Updated on 2026-08-14
This commit is contained in:
parent
c6cdb300e6
commit
f011cba752
23 changed files with 505 additions and 28 deletions
|
|
@ -41,4 +41,10 @@ internal object OnrampDomainModule {
|
|||
fun provideOnrampSaveDefaultCountryUseCase(onrampRepository: OnrampRepository): OnrampSaveDefaultCountryUseCase {
|
||||
return OnrampSaveDefaultCountryUseCase(onrampRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCheckOnrampAvailabilityUseCase(onrampRepository: OnrampRepository): CheckOnrampAvailabilityUseCase {
|
||||
return CheckOnrampAvailabilityUseCase(onrampRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -55,11 +55,15 @@ object TradeCryptoMiddleware {
|
|||
|
||||
private fun proceedBuyAction(state: () -> AppState?, action: TradeCryptoAction.Buy) {
|
||||
val isOnrampEnabled = store.inject(DaggerGraphState::onrampFeatureToggles).isFeatureEnabled
|
||||
if (isOnrampEnabled) proceedWithOnramp() else proceedWithLegacyBuyAction(state, action)
|
||||
if (isOnrampEnabled) {
|
||||
proceedWithOnramp(action.cryptoCurrencyStatus.currency)
|
||||
} else {
|
||||
proceedWithLegacyBuyAction(state, action)
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedWithOnramp() {
|
||||
store.dispatchNavigationAction { push(AppRoute.Onramp) }
|
||||
private fun proceedWithOnramp(cryptoCurrency: CryptoCurrency) {
|
||||
store.dispatchNavigationAction { push(AppRoute.Onramp(cryptoCurrency)) }
|
||||
}
|
||||
|
||||
private fun proceedWithLegacyBuyAction(state: () -> AppState?, action: TradeCryptoAction.Buy) {
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ internal class ChildFactory @Inject constructor(
|
|||
is AppRoute.Onramp -> {
|
||||
route.asComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = OnrampComponent.Params(),
|
||||
params = OnrampComponent.Params(route.currency.name),
|
||||
componentFactory = onrampComponentFactory,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -295,7 +295,9 @@ sealed class AppRoute(val path: String) : Route {
|
|||
}
|
||||
|
||||
@Serializable
|
||||
data object Onramp : AppRoute(path = "/onramp")
|
||||
data class Onramp(val currency: CryptoCurrency) : AppRoute(path = "/onramp/${currency.symbol}"), RouteBundleParams {
|
||||
override fun getBundle(): Bundle = bundle(serializer())
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class BuyCrypto(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.domain.onramp.model
|
||||
|
||||
sealed interface OnrampAvailability {
|
||||
|
||||
data object Available : OnrampAvailability
|
||||
data class NotSupported(val country: OnrampCountry) : OnrampAvailability
|
||||
data class ConfirmResidency(val country: OnrampCountry) : OnrampAvailability
|
||||
}
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
package com.tangem.domain.onramp.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class OnrampCountry(
|
||||
val id: String,
|
||||
val name: String,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
package com.tangem.domain.onramp.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class OnrampCurrency(
|
||||
val name: String,
|
||||
val code: String,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.domain.onramp
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.onramp.model.OnrampAvailability
|
||||
import com.tangem.domain.onramp.repositories.OnrampRepository
|
||||
|
||||
class CheckOnrampAvailabilityUseCase(private val repository: OnrampRepository) {
|
||||
|
||||
suspend operator fun invoke(): Either<Throwable, OnrampAvailability> {
|
||||
return Either.catch {
|
||||
return@catch OnrampAvailability.Available
|
||||
repository.getDefaultCountrySync()?.let { savedCountry ->
|
||||
return@catch if (savedCountry.onrampAvailable) {
|
||||
OnrampAvailability.Available
|
||||
} else {
|
||||
OnrampAvailability.NotSupported(savedCountry)
|
||||
}
|
||||
}
|
||||
|
||||
val detectedCountry = repository.getCountryByIp()
|
||||
OnrampAvailability.ConfirmResidency(detectedCountry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import com.tangem.core.ui.decompose.ComposableContentComponent
|
|||
|
||||
interface OnrampComponent : ComposableContentComponent {
|
||||
|
||||
class Params
|
||||
data class Params(val currency: String)
|
||||
|
||||
interface Factory : ComponentFactory<Params, OnrampComponent>
|
||||
}
|
||||
|
|
@ -2,15 +2,11 @@ package com.tangem.features.onramp.confirmresidency
|
|||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.domain.onramp.model.OnrampCountry
|
||||
|
||||
internal interface ConfirmResidencyComponent : ComposableBottomSheetComponent {
|
||||
|
||||
data class Params(
|
||||
val countryName: String,
|
||||
val isOnrampSupported: Boolean,
|
||||
val countryFlagUrl: String,
|
||||
val onDismiss: () -> Unit,
|
||||
)
|
||||
data class Params(val country: OnrampCountry, val onDismiss: (OnrampCountry) -> Unit)
|
||||
|
||||
interface Factory : ComponentFactory<Params, ConfirmResidencyComponent>
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ internal class DefaultConfirmResidencyComponent @AssistedInject constructor(
|
|||
)
|
||||
|
||||
override fun dismiss() {
|
||||
params.onDismiss()
|
||||
router.pop()
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -5,18 +5,24 @@ import com.arkivanov.decompose.router.slot.activate
|
|||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.onramp.GetOnrampCountryUseCase
|
||||
import com.tangem.features.onramp.confirmresidency.ConfirmResidencyComponent
|
||||
import com.tangem.features.onramp.confirmresidency.entity.ConfirmResidencyUM
|
||||
import com.tangem.features.onramp.impl.R
|
||||
import com.tangem.features.onramp.entity.ConfirmResidencyBottomSheetConfig
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import javax.inject.Inject
|
||||
|
||||
@ComponentScoped
|
||||
internal class ConfirmResidencyModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val router: Router,
|
||||
getOnrampCountryUseCase: GetOnrampCountryUseCase,
|
||||
paramsContainer: ParamsContainer,
|
||||
) : Model() {
|
||||
|
||||
|
|
@ -24,9 +30,9 @@ internal class ConfirmResidencyModel @Inject constructor(
|
|||
val bottomSheetNavigation: SlotNavigation<ConfirmResidencyBottomSheetConfig> = SlotNavigation()
|
||||
val state: MutableStateFlow<ConfirmResidencyUM> = MutableStateFlow(
|
||||
ConfirmResidencyUM(
|
||||
country = params.countryName,
|
||||
countryFlagUrl = params.countryFlagUrl,
|
||||
isCountrySupported = params.isOnrampSupported,
|
||||
country = params.country.name,
|
||||
countryFlagUrl = params.country.image,
|
||||
isCountrySupported = params.country.onrampAvailable,
|
||||
primaryButtonConfig = getPrimaryButtonConfig(),
|
||||
secondaryButtonConfig = ConfirmResidencyUM.ActionButtonConfig(
|
||||
onClick = ::onChangeClick,
|
||||
|
|
@ -35,22 +41,29 @@ internal class ConfirmResidencyModel @Inject constructor(
|
|||
),
|
||||
)
|
||||
|
||||
private fun getPrimaryButtonConfig() = if (params.isOnrampSupported) {
|
||||
init {
|
||||
getOnrampCountryUseCase.invoke()
|
||||
.onEach { maybeCountry ->
|
||||
val country = maybeCountry.getOrNull()
|
||||
if (country != null) {
|
||||
params.onDismiss(country)
|
||||
}
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun getPrimaryButtonConfig() = if (params.country.onrampAvailable) {
|
||||
ConfirmResidencyUM.ActionButtonConfig(
|
||||
onClick = params.onDismiss,
|
||||
onClick = { params.onDismiss(params.country) },
|
||||
text = resourceReference(R.string.common_confirm),
|
||||
)
|
||||
} else {
|
||||
ConfirmResidencyUM.ActionButtonConfig(
|
||||
onClick = ::onCloseClick,
|
||||
onClick = router::pop,
|
||||
text = resourceReference(R.string.common_close),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("EmptyFunctionBlock")
|
||||
private fun onCloseClick() {
|
||||
}
|
||||
|
||||
private fun onChangeClick() {
|
||||
bottomSheetNavigation.activate(ConfirmResidencyBottomSheetConfig.SelectCountry)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
package com.tangem.features.onramp.main
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.subscribeAsState
|
||||
import com.arkivanov.decompose.router.slot.childSlot
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.features.onramp.confirmresidency.ConfirmResidencyComponent
|
||||
import com.tangem.features.onramp.main.entity.OnrampMainBottomSheetConfig
|
||||
import com.tangem.features.onramp.main.model.OnrampMainComponentModel
|
||||
import com.tangem.features.onramp.main.ui.OnrampMainComponentContent
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
internal class DefaultOnrampMainComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
@Assisted params: OnrampMainComponent.Params,
|
||||
private val confirmResidencyComponentFactory: ConfirmResidencyComponent.Factory,
|
||||
) : OnrampMainComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: OnrampMainComponentModel = getOrCreateModel(params)
|
||||
private val bottomSheetSlot = childSlot(
|
||||
source = model.bottomSheetNavigation,
|
||||
serializer = null,
|
||||
handleBackButton = false,
|
||||
childFactory = ::bottomSheetChild,
|
||||
)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.state.collectAsState()
|
||||
val bottomSheet by bottomSheetSlot.subscribeAsState()
|
||||
|
||||
OnrampMainComponentContent(modifier = modifier, state = state)
|
||||
bottomSheet.child?.instance?.BottomSheet()
|
||||
}
|
||||
|
||||
private fun bottomSheetChild(
|
||||
config: OnrampMainBottomSheetConfig,
|
||||
componentContext: ComponentContext,
|
||||
): ComposableBottomSheetComponent = when (config) {
|
||||
is OnrampMainBottomSheetConfig.ConfirmResidency -> confirmResidencyComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = ConfirmResidencyComponent.Params(
|
||||
country = config.country,
|
||||
onDismiss = { model.bottomSheetNavigation.dismiss() },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : OnrampMainComponent.Factory {
|
||||
override fun create(
|
||||
context: AppComponentContext,
|
||||
params: OnrampMainComponent.Params,
|
||||
): DefaultOnrampMainComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.features.onramp.main
|
||||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
|
||||
internal interface OnrampMainComponent : ComposableContentComponent {
|
||||
|
||||
class Params(val currency: String, val openSettings: () -> Unit)
|
||||
|
||||
interface Factory : ComponentFactory<Params, OnrampMainComponent>
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.features.onramp.main.di
|
||||
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.onramp.main.model.OnrampMainComponentModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
internal interface OnrampMainComponentModelModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(OnrampMainComponentModel::class)
|
||||
fun bindOnrampSelectCountryModel(model: OnrampMainComponentModel): Model
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.features.onramp.main.di
|
||||
|
||||
import com.tangem.features.onramp.main.DefaultOnrampMainComponent
|
||||
import com.tangem.features.onramp.main.OnrampMainComponent
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface OnrampMainComponentModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindOnrampMainComponentFactory(factory: DefaultOnrampMainComponent.Factory): OnrampMainComponent.Factory
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.features.onramp.main.entity
|
||||
|
||||
import com.tangem.domain.onramp.model.OnrampCountry
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
internal sealed interface OnrampMainBottomSheetConfig {
|
||||
@Serializable
|
||||
data class ConfirmResidency(val country: OnrampCountry) : OnrampMainBottomSheetConfig
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.tangem.features.onramp.main.entity
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.combinedReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.features.onramp.impl.R
|
||||
|
||||
@Immutable
|
||||
internal sealed interface OnrampMainComponentUM {
|
||||
|
||||
val topBarConfig: OnrampMainTopBarUM
|
||||
val buyButtonConfig: BuyButtonConfig
|
||||
|
||||
data class InitialLoading(
|
||||
val currency: String,
|
||||
val onClose: () -> Unit,
|
||||
val openSettings: () -> Unit,
|
||||
val onBuyClick: () -> Unit,
|
||||
) : OnrampMainComponentUM {
|
||||
override val topBarConfig: OnrampMainTopBarUM = OnrampMainTopBarUM(
|
||||
title = combinedReference(resourceReference(R.string.common_buy), stringReference(" $currency")),
|
||||
startButtonUM = TopAppBarButtonUM(
|
||||
iconRes = R.drawable.ic_close_24,
|
||||
onIconClicked = onClose,
|
||||
enabled = true,
|
||||
),
|
||||
endButtonUM = TopAppBarButtonUM(
|
||||
iconRes = R.drawable.ic_more_vertical_24,
|
||||
onIconClicked = openSettings,
|
||||
enabled = false,
|
||||
),
|
||||
)
|
||||
|
||||
override val buyButtonConfig: BuyButtonConfig = BuyButtonConfig(
|
||||
text = resourceReference(R.string.common_buy),
|
||||
onClick = onBuyClick,
|
||||
enabled = false,
|
||||
)
|
||||
}
|
||||
|
||||
data class Content(
|
||||
override val topBarConfig: OnrampMainTopBarUM,
|
||||
override val buyButtonConfig: BuyButtonConfig,
|
||||
) : OnrampMainComponentUM
|
||||
}
|
||||
|
||||
internal data class BuyButtonConfig(
|
||||
val text: TextReference,
|
||||
val onClick: () -> Unit,
|
||||
val enabled: Boolean,
|
||||
)
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.features.onramp.main.entity
|
||||
|
||||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
data class OnrampMainTopBarUM(
|
||||
val title: TextReference,
|
||||
val startButtonUM: TopAppBarButtonUM,
|
||||
val endButtonUM: TopAppBarButtonUM,
|
||||
)
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package com.tangem.features.onramp.main.entity.transformer
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.onramp.model.OnrampAvailability
|
||||
import com.tangem.domain.onramp.model.OnrampCountry
|
||||
import com.tangem.features.onramp.main.entity.OnrampMainBottomSheetConfig
|
||||
import com.tangem.features.onramp.main.entity.OnrampMainComponentUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import timber.log.Timber
|
||||
|
||||
internal class OnrampAvailabilityTransformer(
|
||||
private val maybeAvailabilityStatus: Either<Throwable, OnrampAvailability>,
|
||||
private val openBottomSheet: (OnrampMainBottomSheetConfig) -> Unit,
|
||||
) : Transformer<OnrampMainComponentUM> {
|
||||
|
||||
override fun transform(prevState: OnrampMainComponentUM): OnrampMainComponentUM {
|
||||
return maybeAvailabilityStatus.fold(
|
||||
ifLeft = {
|
||||
Timber.e(it)
|
||||
prevState
|
||||
},
|
||||
ifRight = { availability ->
|
||||
when (availability) {
|
||||
OnrampAvailability.Available -> handleAvailableStatus(prevState)
|
||||
is OnrampAvailability.ConfirmResidency -> handleShowingConfirmResidency(
|
||||
prevState = prevState,
|
||||
country = availability.country,
|
||||
)
|
||||
is OnrampAvailability.NotSupported -> handleShowingConfirmResidency(
|
||||
prevState = prevState,
|
||||
country = availability.country,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleAvailableStatus(prevState: OnrampMainComponentUM): OnrampMainComponentUM {
|
||||
val endButton = prevState.topBarConfig.endButtonUM.copy(enabled = true)
|
||||
return OnrampMainComponentUM.Content(
|
||||
topBarConfig = prevState.topBarConfig.copy(endButtonUM = endButton),
|
||||
buyButtonConfig = prevState.buyButtonConfig,
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleShowingConfirmResidency(
|
||||
prevState: OnrampMainComponentUM,
|
||||
country: OnrampCountry,
|
||||
): OnrampMainComponentUM {
|
||||
openBottomSheet.invoke(OnrampMainBottomSheetConfig.ConfirmResidency(country))
|
||||
return prevState
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package com.tangem.features.onramp.main.model
|
||||
|
||||
import com.arkivanov.decompose.router.slot.SlotNavigation
|
||||
import com.arkivanov.decompose.router.slot.activate
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.domain.onramp.CheckOnrampAvailabilityUseCase
|
||||
import com.tangem.features.onramp.main.OnrampMainComponent
|
||||
import com.tangem.features.onramp.main.entity.OnrampMainBottomSheetConfig
|
||||
import com.tangem.features.onramp.main.entity.OnrampMainComponentUM
|
||||
import com.tangem.features.onramp.main.entity.transformer.OnrampAvailabilityTransformer
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class OnrampMainComponentModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val router: Router,
|
||||
private val checkOnrampAvailabilityUseCase: CheckOnrampAvailabilityUseCase,
|
||||
paramsContainer: ParamsContainer,
|
||||
) : Model() {
|
||||
|
||||
private val params: OnrampMainComponent.Params = paramsContainer.require()
|
||||
private val _state: MutableStateFlow<OnrampMainComponentUM> = MutableStateFlow(
|
||||
value = OnrampMainComponentUM.InitialLoading(
|
||||
currency = params.currency,
|
||||
onClose = router::pop,
|
||||
openSettings = params.openSettings,
|
||||
onBuyClick = ::onBuyClick,
|
||||
),
|
||||
)
|
||||
val state: StateFlow<OnrampMainComponentUM> get() = _state.asStateFlow()
|
||||
val bottomSheetNavigation: SlotNavigation<OnrampMainBottomSheetConfig> = SlotNavigation()
|
||||
|
||||
init {
|
||||
checkResidenceCountry()
|
||||
}
|
||||
|
||||
private fun checkResidenceCountry() {
|
||||
modelScope.launch {
|
||||
OnrampAvailabilityTransformer(
|
||||
maybeAvailabilityStatus = checkOnrampAvailabilityUseCase.invoke(),
|
||||
openBottomSheet = ::openBottomSheet,
|
||||
).let(::updateState)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("EmptyFunctionBlock")
|
||||
private fun onBuyClick() {
|
||||
}
|
||||
|
||||
private fun openBottomSheet(config: OnrampMainBottomSheetConfig) {
|
||||
bottomSheetNavigation.activate(config)
|
||||
}
|
||||
|
||||
private fun updateState(transformer: Transformer<OnrampMainComponentUM>) {
|
||||
_state.update(transformer::transform)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
package com.tangem.features.onramp.main.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.FabPosition
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.tangem.core.ui.components.CircleShimmer
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.appbar.TangemTopAppBar
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.utils.WindowInsetsZero
|
||||
import com.tangem.features.onramp.impl.R
|
||||
import com.tangem.features.onramp.main.entity.OnrampMainComponentUM
|
||||
|
||||
@Composable
|
||||
internal fun OnrampMainComponentContent(state: OnrampMainComponentUM, modifier: Modifier = Modifier) {
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
contentWindowInsets = WindowInsetsZero,
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
topBar = {
|
||||
TangemTopAppBar(
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
startButton = state.topBarConfig.startButtonUM,
|
||||
endButton = state.topBarConfig.endButtonUM,
|
||||
title = state.topBarConfig.title.resolveReference(),
|
||||
)
|
||||
},
|
||||
content = { innerPadding ->
|
||||
val contentModifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
when (state) {
|
||||
is OnrampMainComponentUM.InitialLoading -> InitialLoading(modifier = contentModifier)
|
||||
is OnrampMainComponentUM.Content -> Content(modifier = contentModifier)
|
||||
}
|
||||
},
|
||||
floatingActionButton = {
|
||||
BuyButton(
|
||||
modifier = Modifier
|
||||
.navigationBarsPadding()
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
onClick = state.buyButtonConfig.onClick,
|
||||
enabled = state.buyButtonConfig.enabled,
|
||||
)
|
||||
},
|
||||
floatingActionButtonPosition = FabPosition.Center,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InitialLoading(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.clip(shape = RoundedCornerShape(size = TangemTheme.dimens.radius16))
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.padding(vertical = TangemTheme.dimens.spacing28),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
CircleShimmer(modifier = Modifier.size(TangemTheme.dimens.size40))
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(top = TangemTheme.dimens.spacing16)
|
||||
.size(width = TangemTheme.dimens.size96, height = TangemTheme.dimens.size24),
|
||||
radius = TangemTheme.dimens.radius3,
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(top = TangemTheme.dimens.spacing16)
|
||||
.size(width = TangemTheme.dimens.size72, height = TangemTheme.dimens.size12),
|
||||
radius = TangemTheme.dimens.radius3,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Content(modifier: Modifier = Modifier) {
|
||||
Box(modifier = modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BuyButton(onClick: () -> Unit, enabled: Boolean, modifier: Modifier = Modifier) {
|
||||
PrimaryButton(
|
||||
modifier = modifier,
|
||||
text = stringResource(id = R.string.common_buy),
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.features.onramp.root
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -9,10 +10,13 @@ import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.stackA
|
|||
import com.arkivanov.decompose.extensions.compose.jetpack.subscribeAsState
|
||||
import com.arkivanov.decompose.router.stack.StackNavigation
|
||||
import com.arkivanov.decompose.router.stack.childStack
|
||||
import com.arkivanov.decompose.router.stack.pop
|
||||
import com.arkivanov.decompose.router.stack.push
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.features.onramp.component.OnrampComponent
|
||||
import com.tangem.features.onramp.main.OnrampMainComponent
|
||||
import com.tangem.features.onramp.root.entity.OnrampChild
|
||||
import com.tangem.features.onramp.settings.OnrampSettingsComponent
|
||||
import dagger.assisted.Assisted
|
||||
|
|
@ -22,8 +26,9 @@ import dagger.assisted.AssistedInject
|
|||
@Suppress("UnusedPrivateMember")
|
||||
internal class DefaultOnrampComponent @AssistedInject constructor(
|
||||
@Assisted context: AppComponentContext,
|
||||
@Assisted params: OnrampComponent.Params,
|
||||
@Assisted private val params: OnrampComponent.Params,
|
||||
private val settingsComponentFactory: OnrampSettingsComponent.Factory,
|
||||
private val onrampMainComponentFactory: OnrampMainComponent.Factory,
|
||||
) : OnrampComponent, AppComponentContext by context {
|
||||
|
||||
private val navigation = StackNavigation<OnrampChild>()
|
||||
|
|
@ -31,7 +36,7 @@ internal class DefaultOnrampComponent @AssistedInject constructor(
|
|||
key = "onramp_content_stack",
|
||||
source = navigation,
|
||||
serializer = OnrampChild.serializer(),
|
||||
initialConfiguration = OnrampChild.Settings,
|
||||
initialConfiguration = OnrampChild.Main,
|
||||
handleBackButton = false,
|
||||
childFactory = ::screenChild,
|
||||
)
|
||||
|
|
@ -40,6 +45,7 @@ internal class DefaultOnrampComponent @AssistedInject constructor(
|
|||
override fun Content(modifier: Modifier) {
|
||||
val childStack by contentStack.subscribeAsState()
|
||||
|
||||
BackHandler(onBack = router::pop)
|
||||
Children(
|
||||
stack = childStack,
|
||||
animation = stackAnimation(),
|
||||
|
|
@ -52,10 +58,15 @@ internal class DefaultOnrampComponent @AssistedInject constructor(
|
|||
when (config) {
|
||||
OnrampChild.Settings -> settingsComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
// params = OnrampSettingsComponent.Params(navigation::pop) // todo uncomment after [REDACTED_JIRA]
|
||||
params = OnrampSettingsComponent.Params(router::pop),
|
||||
params = OnrampSettingsComponent.Params(navigation::pop),
|
||||
)
|
||||
OnrampChild.Main -> onrampMainComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = OnrampMainComponent.Params(
|
||||
currency = params.currency,
|
||||
openSettings = { navigation.push(OnrampChild.Settings) },
|
||||
),
|
||||
)
|
||||
OnrampChild.Main -> TODO("[REDACTED_JIRA]")
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue