Updated on 2026-08-14
This commit is contained in:
parent
b5b086a985
commit
2e07841398
18 changed files with 153 additions and 53 deletions
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.tap.common.di.domain.wallets
|
||||
|
||||
import com.tangem.domain.wallets.legacy.WalletsStateHolder
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
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)
|
||||
object WalletsDomainModule {
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun providesGetWalletsUseCase(walletsStateHolder: WalletsStateHolder): GetWalletsUseCase {
|
||||
return GetWalletsUseCase(walletsStateHolder = walletsStateHolder)
|
||||
}
|
||||
}
|
||||
18
app/src/main/java/com/tangem/tap/di/AppStateHolderModule.kt
Normal file
18
app/src/main/java/com/tangem/tap/di/AppStateHolderModule.kt
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.tap.di
|
||||
|
||||
import com.tangem.domain.wallets.legacy.WalletsStateHolder
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
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 AppStateHolderModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindsWalletsStateHolder(appStateHolder: AppStateHolder): WalletsStateHolder
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import com.tangem.TangemSdk
|
|||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.legacy.WalletsStateHolder
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
|
|
@ -14,10 +15,12 @@ import org.rekotlin.Store
|
|||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Holds objects from old modules, that missing in DI graph
|
||||
* Object sets manually to use in new modules and [AppStateHolder] proxies its to DI
|
||||
* Holds objects from old modules, that missing in DI graph.
|
||||
* Object sets manually to use in new modules and [AppStateHolder] proxies its to DI.
|
||||
*/
|
||||
class AppStateHolder @Inject constructor() {
|
||||
class AppStateHolder @Inject constructor() : WalletsStateHolder {
|
||||
|
||||
override var userWalletsListManager: UserWalletsListManager? = null
|
||||
|
||||
@Deprecated("Use scan response from selected user wallet")
|
||||
var scanResponse: ScanResponse? = null
|
||||
|
|
@ -27,7 +30,6 @@ class AppStateHolder @Inject constructor() {
|
|||
var tangemSdkManager: TangemSdkManager? = null
|
||||
var tangemSdk: TangemSdk? = null
|
||||
var walletStoresManager: WalletStoresManager? = null
|
||||
var userWalletsListManager: UserWalletsListManager? = null
|
||||
var appFiatCurrency: FiatCurrency = FiatCurrency.Default
|
||||
|
||||
fun getActualCard(): CardDTO? {
|
||||
|
|
|
|||
10
common/src/main/java/com/tangem/common/Provider.kt
Normal file
10
common/src/main/java/com/tangem/common/Provider.kt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.common
|
||||
|
||||
/**
|
||||
* Provider for lazy initialization
|
||||
*
|
||||
* @param action initialization action
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class Provider<T>(action: () -> T) : () -> T by action
|
||||
|
|
@ -12,6 +12,7 @@ dependencies {
|
|||
|
||||
// region Domain modules
|
||||
implementation(project(":domain:legacy"))
|
||||
implementation(project(":domain:models"))
|
||||
implementation(project(":domain:wallets:models"))
|
||||
// endregion
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.domain.wallets.legacy
|
||||
|
||||
interface WalletsStateHolder {
|
||||
|
||||
val userWalletsListManager: UserWalletsListManager?
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.domain.wallets.usecase
|
||||
|
||||
import com.tangem.domain.wallets.legacy.WalletsStateHolder
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
* Use case for getting list of user wallets
|
||||
*
|
||||
* @property walletsStateHolder state holder for getting static initialized 'userWalletsListManager'
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class GetWalletsUseCase(private val walletsStateHolder: WalletsStateHolder) {
|
||||
|
||||
@Throws(IllegalArgumentException::class)
|
||||
operator fun invoke(): Flow<List<UserWallet>> {
|
||||
return requireNotNull(walletsStateHolder.userWalletsListManager).userWallets
|
||||
}
|
||||
}
|
||||
|
|
@ -40,5 +40,9 @@ dependencies {
|
|||
implementation(project(":features:wallet:api"))
|
||||
|
||||
/** Domain modules */
|
||||
implementation(project(":common"))
|
||||
implementation(project(":domain:legacy"))
|
||||
implementation(project(":domain:models"))
|
||||
implementation(project(":domain:wallets"))
|
||||
implementation(project(":domain:wallets:models"))
|
||||
}
|
||||
|
|
@ -47,7 +47,7 @@ internal class WalletFragment : Fragment() {
|
|||
}
|
||||
|
||||
isTransitionGroup = true
|
||||
_walletRouter.Initialize()
|
||||
_walletRouter.Initialize(fragmentManager = requireActivity().supportFragmentManager)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ internal object WalletPreviewData {
|
|||
name = "Polygon",
|
||||
)
|
||||
|
||||
val loadingTokenItemState = TokenItemState.Loading(id = UUID.randomUUID().toString())
|
||||
val loadingTokenItemState = TokenItemState.Loading
|
||||
|
||||
private const val networksSize = 10
|
||||
private const val tokensSize = 3
|
||||
|
|
|
|||
|
|
@ -7,20 +7,12 @@ import androidx.compose.runtime.Immutable
|
|||
@Immutable
|
||||
internal sealed interface TokenItemState {
|
||||
|
||||
/** Token id */
|
||||
val id: String
|
||||
|
||||
/**
|
||||
* Loading token state
|
||||
*
|
||||
* @property id token id
|
||||
*/
|
||||
data class Loading(override val id: String) : TokenItemState
|
||||
/** Loading token state */
|
||||
object Loading : TokenItemState
|
||||
|
||||
/**
|
||||
* Content token state
|
||||
*
|
||||
* @property id token id
|
||||
* @property tokenIconUrl token icon url
|
||||
* @property tokenIconResId token icon resource id
|
||||
* @property networkIconResId network icon resource id, may be null if it is a coin
|
||||
|
|
@ -30,7 +22,7 @@ internal sealed interface TokenItemState {
|
|||
* @property tokenOptions state for token options
|
||||
*/
|
||||
data class Content(
|
||||
override val id: String,
|
||||
val id: String,
|
||||
val tokenIconUrl: String?,
|
||||
@DrawableRes val tokenIconResId: Int,
|
||||
@DrawableRes val networkIconResId: Int?,
|
||||
|
|
@ -43,14 +35,13 @@ internal sealed interface TokenItemState {
|
|||
/**
|
||||
* Draggable token state
|
||||
*
|
||||
* @property id token id
|
||||
* @property tokenIconUrl token icon url
|
||||
* @property tokenIconResId token icon resource id
|
||||
* @property networkIconResId network icon resource id, may be null if it is a coin
|
||||
* @property name token name
|
||||
*/
|
||||
data class Draggable(
|
||||
override val id: String,
|
||||
val id: String,
|
||||
val tokenIconUrl: String?,
|
||||
@DrawableRes val tokenIconResId: Int,
|
||||
@DrawableRes val networkIconResId: Int?,
|
||||
|
|
@ -68,7 +59,7 @@ internal sealed interface TokenItemState {
|
|||
* @property name token name
|
||||
*/
|
||||
data class Unreachable(
|
||||
override val id: String,
|
||||
val id: String,
|
||||
val tokenIconUrl: String?,
|
||||
@DrawableRes val tokenIconResId: Int,
|
||||
@DrawableRes val networkIconResId: Int?,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.systemBarsPadding
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
|
|
@ -22,11 +23,14 @@ import kotlin.properties.Delegates
|
|||
internal class DefaultWalletRouter : InnerWalletRouter {
|
||||
|
||||
private var navController: NavHostController by Delegates.notNull()
|
||||
private var fragmentManager: FragmentManager by Delegates.notNull()
|
||||
|
||||
override fun getEntryFragment(): Fragment = WalletFragment.create()
|
||||
|
||||
@Composable
|
||||
override fun Initialize() {
|
||||
override fun Initialize(fragmentManager: FragmentManager) {
|
||||
this.fragmentManager = fragmentManager
|
||||
|
||||
TangemTheme {
|
||||
NavHost(
|
||||
navController = rememberNavController().apply { navController = this },
|
||||
|
|
@ -53,10 +57,24 @@ internal class DefaultWalletRouter : InnerWalletRouter {
|
|||
}
|
||||
|
||||
override fun popBackStack() {
|
||||
navController.popBackStack()
|
||||
/*
|
||||
* It's hack that avoid issue with closing the wallet screen.
|
||||
* We are using NavGraph only inside feature so first backstack's element is entry of NavGraph and
|
||||
* next element is wallet screen entry.
|
||||
* If backstack contains only NavGraph entry and wallet screen entry then we close the wallet fragment.
|
||||
*/
|
||||
if (navController.backQueue.size == BACKSTACK_ENTRY_COUNT_TO_CLOSE_WALLET_SCREEN) {
|
||||
fragmentManager.popBackStack()
|
||||
} else {
|
||||
navController.popBackStack()
|
||||
}
|
||||
}
|
||||
|
||||
override fun openOrganizeTokensScreen() {
|
||||
navController.navigate(WalletScreens.ORGANIZE_TOKENS.name)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val BACKSTACK_ENTRY_COUNT_TO_CLOSE_WALLET_SCREEN = 2
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.router
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import com.tangem.features.wallet.navigation.WalletRouter
|
||||
|
||||
/**
|
||||
|
|
@ -15,10 +16,14 @@ import com.tangem.features.wallet.navigation.WalletRouter
|
|||
@Stable
|
||||
internal interface InnerWalletRouter : WalletRouter {
|
||||
|
||||
/** Initialize router */
|
||||
/**
|
||||
* Initialize router
|
||||
*
|
||||
* @param fragmentManager fragment manager
|
||||
*/
|
||||
@Suppress("TopLevelComposableFunctions")
|
||||
@Composable
|
||||
fun Initialize()
|
||||
fun Initialize(fragmentManager: FragmentManager)
|
||||
|
||||
/** Pop back stack */
|
||||
fun popBackStack()
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ internal sealed interface WalletCardState {
|
|||
|
||||
/** Wallet image resource id */
|
||||
@get:DrawableRes
|
||||
val imageResId: Int
|
||||
val imageResId: Int?
|
||||
|
||||
/** Lambda be invoked when card is clicked */
|
||||
val onClick: (() -> Unit)?
|
||||
|
|
@ -37,7 +37,7 @@ internal sealed interface WalletCardState {
|
|||
override val id: String,
|
||||
override val title: String,
|
||||
override val additionalInfo: String,
|
||||
override val imageResId: Int,
|
||||
override val imageResId: Int?,
|
||||
override val onClick: (() -> Unit)? = null,
|
||||
val balance: String,
|
||||
) : WalletCardState
|
||||
|
|
@ -55,8 +55,8 @@ internal sealed interface WalletCardState {
|
|||
override val id: String,
|
||||
override val title: String,
|
||||
override val additionalInfo: String,
|
||||
override val imageResId: Int,
|
||||
override val onClick: (() -> Unit)?,
|
||||
override val imageResId: Int?,
|
||||
override val onClick: (() -> Unit)? = null,
|
||||
) : WalletCardState
|
||||
|
||||
/**
|
||||
|
|
@ -72,7 +72,7 @@ internal sealed interface WalletCardState {
|
|||
override val id: String,
|
||||
override val title: String,
|
||||
override val additionalInfo: String,
|
||||
override val imageResId: Int,
|
||||
override val imageResId: Int?,
|
||||
override val onClick: (() -> Unit)?,
|
||||
) : WalletCardState
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ internal sealed interface WalletCardState {
|
|||
override val id: String,
|
||||
override val title: String,
|
||||
override val additionalInfo: String,
|
||||
override val imageResId: Int,
|
||||
override val imageResId: Int?,
|
||||
override val onClick: (() -> Unit)?,
|
||||
) : WalletCardState
|
||||
}
|
||||
|
|
@ -52,4 +52,6 @@ internal sealed interface WalletContentItemState {
|
|||
*/
|
||||
data class Transaction(val state: TransactionState) : SingleCurrencyItem
|
||||
}
|
||||
|
||||
object Loading : WalletContentItemState
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ import com.tangem.feature.wallet.impl.R
|
|||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.common.component.NetworkGroupItem
|
||||
import com.tangem.feature.wallet.presentation.common.component.TokenItem
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletContentItemState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletTopBar
|
||||
|
|
@ -67,7 +68,6 @@ internal fun WalletScreen(state: WalletStateHolder) {
|
|||
WalletsList(
|
||||
config = state.walletsListConfig,
|
||||
lazyListState = walletsListState,
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing14),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ internal fun WalletScreen(state: WalletStateHolder) {
|
|||
item {
|
||||
WalletManageButtons(
|
||||
buttons = state.buttons,
|
||||
modifier = changeableItemModifier.padding(bottom = TangemTheme.dimens.spacing14),
|
||||
modifier = changeableItemModifier.padding(top = TangemTheme.dimens.spacing14),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -86,8 +86,8 @@ internal fun WalletScreen(state: WalletStateHolder) {
|
|||
Notification(
|
||||
state = item.state,
|
||||
modifier = changeableItemModifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.padding(bottom = TangemTheme.dimens.spacing14),
|
||||
.padding(top = TangemTheme.dimens.spacing14)
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
|
@ -96,7 +96,9 @@ internal fun WalletScreen(state: WalletStateHolder) {
|
|||
item {
|
||||
WalletMarketplaceBlock(
|
||||
state = state.marketplaceBlockState,
|
||||
modifier = changeableItemModifier.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
modifier = changeableItemModifier
|
||||
.padding(top = TangemTheme.dimens.spacing14)
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -106,10 +108,11 @@ internal fun WalletScreen(state: WalletStateHolder) {
|
|||
key = { index, item ->
|
||||
when (item) {
|
||||
is WalletContentItemState.MultiCurrencyItem.NetworkGroupTitle -> item.networkName
|
||||
is WalletContentItemState.MultiCurrencyItem.Token -> item.state.id
|
||||
is WalletContentItemState.MultiCurrencyItem.Token -> index
|
||||
is WalletContentItemState.SingleCurrencyItem.Title -> index
|
||||
is WalletContentItemState.SingleCurrencyItem.GroupTitle -> item.title
|
||||
is WalletContentItemState.SingleCurrencyItem.Transaction -> index
|
||||
is WalletContentItemState.Loading -> index
|
||||
}
|
||||
},
|
||||
itemContent = { index, item ->
|
||||
|
|
@ -155,6 +158,9 @@ private fun ContentItem(item: WalletContentItemState, modifier: Modifier = Modif
|
|||
is WalletContentItemState.SingleCurrencyItem.Transaction -> {
|
||||
Transaction(state = item.state, modifier = modifier)
|
||||
}
|
||||
WalletContentItemState.Loading -> {
|
||||
TokenItem(state = TokenItemState.Loading, modifier = modifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,16 +62,18 @@ internal fun WalletCard(state: WalletCardState, modifier: Modifier = Modifier) {
|
|||
}
|
||||
|
||||
val imageWidth = TangemTheme.dimens.size120
|
||||
WalletImage(
|
||||
id = state.imageResId,
|
||||
modifier = Modifier.constrainAs(imageItem) {
|
||||
centerVerticallyTo(parent)
|
||||
top.linkTo(parent.top)
|
||||
end.linkTo(parent.end)
|
||||
height = Dimension.fillToConstraints
|
||||
width = Dimension.value(imageWidth)
|
||||
},
|
||||
)
|
||||
state.imageResId?.let {
|
||||
WalletImage(
|
||||
id = it,
|
||||
modifier = Modifier.constrainAs(imageItem) {
|
||||
centerVerticallyTo(parent)
|
||||
top.linkTo(parent.top)
|
||||
end.linkTo(parent.end)
|
||||
height = Dimension.fillToConstraints
|
||||
width = Dimension.value(imageWidth)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,13 +20,8 @@ internal class ScrollOffsetCollector(
|
|||
private val LazyListItemInfo.halfItemSize get() = size.div(other = 2)
|
||||
|
||||
override suspend fun emit(value: List<LazyListItemInfo>) {
|
||||
val firstItem = requireNotNull(value.firstOrNull()) {
|
||||
"At least 1 item should be visible in list"
|
||||
}
|
||||
|
||||
val lastItem = requireNotNull(value.lastOrNull()) {
|
||||
"At least 1 item should be visible in list"
|
||||
}
|
||||
val firstItem = value.firstOrNull() ?: return
|
||||
val lastItem = value.lastOrNull() ?: return
|
||||
|
||||
if (abs(firstItem.offset) > firstItem.halfItemSize) {
|
||||
callback(firstItem.index + 1)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue