Updated on 2026-08-14
This commit is contained in:
commit
74f71080ea
23 changed files with 143 additions and 95 deletions
|
|
@ -146,7 +146,7 @@ dependencies {
|
|||
|
||||
/** Features */
|
||||
implementation(projects.features.onboarding)
|
||||
implementation(projects.features.referral.presentation)
|
||||
implementation(projects.features.referral.impl)
|
||||
implementation(projects.features.referral.domain)
|
||||
implementation(projects.features.referral.data)
|
||||
implementation(projects.features.swap.api)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.tap.routing.utils
|
|||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.feature.qrscanning.QrScanningComponent
|
||||
import com.tangem.feature.referral.ReferralFragment
|
||||
import com.tangem.feature.referral.api.ReferralComponent
|
||||
import com.tangem.feature.stories.api.StoriesComponent
|
||||
import com.tangem.feature.walletsettings.component.WalletSettingsComponent
|
||||
import com.tangem.features.details.component.DetailsComponent
|
||||
|
|
@ -71,6 +71,7 @@ internal class ChildFactory @Inject constructor(
|
|||
private val appSettingsComponentFactory: AppSettingsComponent.Factory,
|
||||
private val securityModeComponentFactory: SecurityModeComponent.Factory,
|
||||
private val resetCardComponentFactory: ResetCardComponent.Factory,
|
||||
private val referralComponentFactory: ReferralComponent.Factory,
|
||||
private val walletRouter: WalletRouter,
|
||||
private val testerRouter: TesterRouter,
|
||||
private val pushNotificationRouter: PushNotificationsRouter,
|
||||
|
|
@ -342,12 +343,18 @@ internal class ChildFactory @Inject constructor(
|
|||
componentFactory = resetCardComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.ReferralProgram -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = ReferralComponent.Params(route.userWalletId),
|
||||
componentFactory = referralComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.SaveWallet,
|
||||
is AppRoute.OnboardingNote,
|
||||
is AppRoute.OnboardingOther,
|
||||
is AppRoute.OnboardingTwins,
|
||||
is AppRoute.OnboardingWallet,
|
||||
is AppRoute.ReferralProgram,
|
||||
is AppRoute.Wallet,
|
||||
is AppRoute.PushNotification,
|
||||
-> error("Unsupported route: $route")
|
||||
|
|
@ -475,7 +482,11 @@ internal class ChildFactory @Inject constructor(
|
|||
)
|
||||
}
|
||||
is AppRoute.ReferralProgram -> {
|
||||
route.asFragmentChild(Provider { ReferralFragment() })
|
||||
route.asComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = ReferralComponent.Params(route.userWalletId),
|
||||
componentFactory = referralComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.ResetToFactory -> {
|
||||
route.asComponentChild(
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import dagger.hilt.components.SingletonComponent
|
|||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
interface CoroutineDispatcherProviderModule {
|
||||
internal interface CoroutineDispatcherProviderModule {
|
||||
|
||||
@Binds
|
||||
fun bindCoroutineDispatcherProvider(
|
||||
|
|
|
|||
1
features/referral/api/.gitignore
vendored
Normal file
1
features/referral/api/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
20
features/referral/api/build.gradle.kts
Normal file
20
features/referral/api/build.gradle.kts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
plugins {
|
||||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
alias(deps.plugins.kotlin.serialization)
|
||||
id("kotlin-parcelize")
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.features.referral.api"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
/* Project - Core */
|
||||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.ui)
|
||||
|
||||
/* Project - Domain */
|
||||
implementation(projects.domain.wallets.models)
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.feature.referral.api
|
||||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
interface ReferralComponent : ComposableContentComponent {
|
||||
|
||||
data class Params(
|
||||
val userWalletId: UserWalletId,
|
||||
)
|
||||
|
||||
interface Factory : ComponentFactory<Params, ReferralComponent>
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ dependencies {
|
|||
|
||||
/** Libs */
|
||||
implementation(projects.core.utils)
|
||||
implementation(projects.core.decompose)
|
||||
|
||||
/** Core modules */
|
||||
implementation(projects.libs.crypto)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package com.tangem.feature.referral.domain.di
|
||||
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.domain.card.DerivePublicKeysUseCase
|
||||
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
|
|
@ -10,15 +12,13 @@ import com.tangem.lib.crypto.UserWalletManager
|
|||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ViewModelComponent
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
|
||||
@Module
|
||||
@InstallIn(ViewModelComponent::class)
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
class ReferralDomainModule {
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@ComponentScoped
|
||||
fun provideReferralInteractor(
|
||||
referralRepository: ReferralRepository,
|
||||
userWalletManager: UserWalletManager,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
/** Api */
|
||||
api(projects.features.referral.api)
|
||||
|
||||
/** Core modules */
|
||||
implementation(projects.core.analytics)
|
||||
implementation(projects.core.analytics.models)
|
||||
|
|
@ -18,6 +21,7 @@ dependencies {
|
|||
implementation(projects.core.res)
|
||||
implementation(projects.core.utils)
|
||||
implementation(projects.core.ui)
|
||||
implementation(projects.core.decompose)
|
||||
implementation(projects.libs.crypto)
|
||||
implementation(projects.common.routing)
|
||||
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.tangem.feature.referral
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.feature.referral.api.ReferralComponent
|
||||
import com.tangem.feature.referral.model.ReferralModel
|
||||
import com.tangem.feature.referral.ui.ReferralScreen
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
class DefaultReferralComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
@Assisted params: ReferralComponent.Params,
|
||||
) : ReferralComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: ReferralModel = getOrCreateModel(params)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
ReferralScreen(stateHolder = model.uiState)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : ReferralComponent.Factory {
|
||||
override fun create(context: AppComponentContext, params: ReferralComponent.Params): DefaultReferralComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.feature.referral.di
|
||||
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.feature.referral.DefaultReferralComponent
|
||||
import com.tangem.feature.referral.api.ReferralComponent
|
||||
import com.tangem.feature.referral.model.ReferralModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface ComponentModule {
|
||||
|
||||
@Binds
|
||||
fun provideReferralComponentFactory(impl: DefaultReferralComponent.Factory): ReferralComponent.Factory
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(ReferralModel::class)
|
||||
fun bindModel(model: ReferralModel): Model
|
||||
}
|
||||
|
|
@ -1,21 +1,20 @@
|
|||
package com.tangem.feature.referral.viewmodels
|
||||
package com.tangem.feature.referral.model
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.bundle.unbundle
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
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.navigation.share.ShareManager
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.feature.referral.analytics.ReferralEvents
|
||||
import com.tangem.feature.referral.api.ReferralComponent
|
||||
import com.tangem.feature.referral.domain.ReferralInteractor
|
||||
import com.tangem.feature.referral.domain.errors.ReferralError
|
||||
import com.tangem.feature.referral.domain.models.DiscountType
|
||||
|
|
@ -25,29 +24,26 @@ import com.tangem.feature.referral.models.DemoModeException
|
|||
import com.tangem.feature.referral.models.ReferralStateHolder
|
||||
import com.tangem.feature.referral.models.ReferralStateHolder.ErrorSnackbar
|
||||
import com.tangem.feature.referral.models.ReferralStateHolder.ReferralInfoState
|
||||
import com.tangem.feature.referral.router.ReferralRouter
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@HiltViewModel
|
||||
internal class ReferralViewModel @Inject constructor(
|
||||
@Stable
|
||||
@ComponentScoped
|
||||
internal class ReferralModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val referralInteractor: ReferralInteractor,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val shareManager: ShareManager,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel() {
|
||||
private val appRouter: AppRouter,
|
||||
) : Model() {
|
||||
|
||||
private val userWalletId = savedStateHandle.get<Bundle>(AppRoute.ReferralProgram.USER_WALLET_ID_KEY)
|
||||
?.unbundle(UserWalletId.serializer())
|
||||
?: error("User wallet ID is required for Referral screen")
|
||||
|
||||
private var referralRouter: ReferralRouter by Delegates.notNull()
|
||||
private val params = paramsContainer.require<ReferralComponent.Params>()
|
||||
|
||||
private var lastReferralData: ReferralData? = null
|
||||
|
||||
|
|
@ -55,20 +51,14 @@ internal class ReferralViewModel @Inject constructor(
|
|||
private set
|
||||
|
||||
init {
|
||||
analyticsEventHandler.send(ReferralEvents.ReferralScreenOpened)
|
||||
loadReferralData()
|
||||
}
|
||||
|
||||
fun setRouter(router: ReferralRouter) {
|
||||
referralRouter = router
|
||||
uiState = uiState.copy(headerState = ReferralStateHolder.HeaderState(onBackClicked = router::back))
|
||||
}
|
||||
|
||||
fun onScreenOpened() {
|
||||
analyticsEventHandler.send(ReferralEvents.ReferralScreenOpened)
|
||||
}
|
||||
|
||||
private fun createInitiallyUiState() = ReferralStateHolder(
|
||||
headerState = ReferralStateHolder.HeaderState(onBackClicked = { }),
|
||||
headerState = ReferralStateHolder.HeaderState(
|
||||
onBackClicked = appRouter::pop,
|
||||
),
|
||||
referralInfoState = ReferralInfoState.Loading,
|
||||
errorSnackbar = null,
|
||||
analytics = ReferralStateHolder.Analytics(
|
||||
|
|
@ -80,9 +70,9 @@ internal class ReferralViewModel @Inject constructor(
|
|||
|
||||
private fun loadReferralData() {
|
||||
uiState = uiState.copy(referralInfoState = ReferralInfoState.Loading)
|
||||
viewModelScope.launch {
|
||||
modelScope.launch {
|
||||
runCatching {
|
||||
referralInteractor.getReferralStatus(userWalletId).apply {
|
||||
referralInteractor.getReferralStatus(params.userWalletId).apply {
|
||||
lastReferralData = this
|
||||
}
|
||||
}
|
||||
|
|
@ -92,15 +82,15 @@ internal class ReferralViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun participate() {
|
||||
val userWallet = getUserWalletUseCase(userWalletId).getOrNull() ?: error("User wallet not found")
|
||||
val userWallet = getUserWalletUseCase(params.userWalletId).getOrNull() ?: error("User wallet not found")
|
||||
|
||||
if (isDemoCardUseCase(cardId = userWallet.cardId)) {
|
||||
showErrorSnackbar(DemoModeException())
|
||||
} else {
|
||||
analyticsEventHandler.send(ReferralEvents.ClickParticipate)
|
||||
uiState = uiState.copy(referralInfoState = ReferralInfoState.Loading)
|
||||
viewModelScope.launch {
|
||||
runCatching { referralInteractor.startReferral(userWalletId) }
|
||||
modelScope.launch {
|
||||
runCatching { referralInteractor.startReferral(params.userWalletId) }
|
||||
.onSuccess(::showContent)
|
||||
.onFailure { throwable ->
|
||||
if (throwable is ReferralError.UserCancelledException) {
|
||||
|
|
@ -121,7 +111,7 @@ internal class ReferralViewModel @Inject constructor(
|
|||
|
||||
private fun showErrorSnackbar(throwable: Throwable) {
|
||||
uiState = uiState.copy(
|
||||
errorSnackbar = ErrorSnackbar(throwable = throwable, onOkClicked = referralRouter::back),
|
||||
errorSnackbar = ErrorSnackbar(throwable = throwable, onOkClicked = appRouter::pop),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
package com.tangem.feature.referral
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.ui.UiDependencies
|
||||
import com.tangem.core.ui.screen.ComposeFragment
|
||||
import com.tangem.feature.referral.router.ReferralRouter
|
||||
import com.tangem.feature.referral.ui.ReferralScreen
|
||||
import com.tangem.feature.referral.viewmodels.ReferralViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ReferralFragment : ComposeFragment() {
|
||||
|
||||
@Inject
|
||||
override lateinit var uiDependencies: UiDependencies
|
||||
|
||||
@Inject
|
||||
internal lateinit var appRouter: AppRouter
|
||||
|
||||
private val viewModel by viewModels<ReferralViewModel>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
viewModel.onScreenOpened()
|
||||
viewModel.setRouter(ReferralRouter(appRouter))
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun ScreenContent(modifier: Modifier) {
|
||||
ReferralScreen(stateHolder = viewModel.uiState)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package com.tangem.feature.referral.router
|
||||
|
||||
import com.tangem.common.routing.AppRouter
|
||||
|
||||
internal class ReferralRouter(
|
||||
private val appRouter: AppRouter,
|
||||
) {
|
||||
|
||||
fun back() {
|
||||
appRouter.pop()
|
||||
}
|
||||
}
|
||||
|
|
@ -171,9 +171,10 @@ include(":features:onboarding")
|
|||
include(":features:onboarding-v2:api")
|
||||
include(":features:onboarding-v2:impl")
|
||||
|
||||
include(":features:referral:api")
|
||||
include(":features:referral:data")
|
||||
include(":features:referral:domain")
|
||||
include(":features:referral:presentation")
|
||||
include(":features:referral:impl")
|
||||
|
||||
include(":features:swap:api")
|
||||
include(":features:swap:data")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue