diff --git a/app/src/main/java/com/tangem/tap/common/di/domain/wallets/WalletsDomainModule.kt b/app/src/main/java/com/tangem/tap/common/di/domain/wallets/WalletsDomainModule.kt new file mode 100644 index 0000000000..f945462b83 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/di/domain/wallets/WalletsDomainModule.kt @@ -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) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/AppStateHolderModule.kt b/app/src/main/java/com/tangem/tap/di/AppStateHolderModule.kt new file mode 100644 index 0000000000..6dba03c902 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/di/AppStateHolderModule.kt @@ -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 +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt b/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt index 0235d9d10e..f3dddfc812 100644 --- a/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt +++ b/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt @@ -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? { diff --git a/common/src/main/java/com/tangem/common/Provider.kt b/common/src/main/java/com/tangem/common/Provider.kt new file mode 100644 index 0000000000..7275695e98 --- /dev/null +++ b/common/src/main/java/com/tangem/common/Provider.kt @@ -0,0 +1,10 @@ +package com.tangem.common + +/** + * Provider for lazy initialization + * + * @param action initialization action + * +[REDACTED_AUTHOR] + */ +class Provider(action: () -> T) : () -> T by action \ No newline at end of file diff --git a/domain/wallets/build.gradle.kts b/domain/wallets/build.gradle.kts index bbcf863dc9..a9a1449956 100644 --- a/domain/wallets/build.gradle.kts +++ b/domain/wallets/build.gradle.kts @@ -12,6 +12,7 @@ dependencies { // region Domain modules implementation(project(":domain:legacy")) + implementation(project(":domain:models")) implementation(project(":domain:wallets:models")) // endregion diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/WalletsStateHolder.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/WalletsStateHolder.kt new file mode 100644 index 0000000000..3c7b38bbbe --- /dev/null +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/WalletsStateHolder.kt @@ -0,0 +1,6 @@ +package com.tangem.domain.wallets.legacy + +interface WalletsStateHolder { + + val userWalletsListManager: UserWalletsListManager? +} \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetWalletsUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetWalletsUseCase.kt new file mode 100644 index 0000000000..3c589f1bcd --- /dev/null +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetWalletsUseCase.kt @@ -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> { + return requireNotNull(walletsStateHolder.userWalletsListManager).userWallets + } +} \ No newline at end of file diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index 603f0e303f..cf9833ef5b 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -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")) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt index 954467d0fe..0df20799ee 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt @@ -47,7 +47,7 @@ internal class WalletFragment : Fragment() { } isTransitionGroup = true - _walletRouter.Initialize() + _walletRouter.Initialize(fragmentManager = requireActivity().supportFragmentManager) } } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index 00146fce48..6f6b726f82 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -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 diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt index 5e358b5728..5489b5e602 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt @@ -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?, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index 0e1c8436b3..0f01b919af 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -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 + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt index 7558ac61c7..4c5d9d90ba 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt @@ -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() diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt index 2f000581e2..a04b0e3f45 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt @@ -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 } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletContentItemState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletContentItemState.kt index 87278f27e9..19142be1cd 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletContentItemState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletContentItemState.kt @@ -52,4 +52,6 @@ internal sealed interface WalletContentItemState { */ data class Transaction(val state: TransactionState) : SingleCurrencyItem } + + object Loading : WalletContentItemState } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index 11ec076546..438aaf5d32 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -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) + } } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCard.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCard.kt index d92651ac6f..afc27705b9 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCard.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCard.kt @@ -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) + }, + ) + } } } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ScrollOffsetCollector.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ScrollOffsetCollector.kt index b0b2072867..41cbf50ec1 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ScrollOffsetCollector.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ScrollOffsetCollector.kt @@ -20,13 +20,8 @@ internal class ScrollOffsetCollector( private val LazyListItemInfo.halfItemSize get() = size.div(other = 2) override suspend fun emit(value: List) { - 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)