diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index ff361d442b..0dae1204d4 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -12,6 +12,7 @@ import com.google.android.material.snackbar.Snackbar import com.tangem.TangemSdk import com.tangem.domain.card.ScanCardUseCase import com.tangem.features.tester.api.TesterRouter +import com.tangem.features.wallet.navigation.WalletRouter import com.tangem.operations.backup.BackupService import com.tangem.sdk.extensions.init import com.tangem.tap.common.ActivityResultCallbackHolder @@ -86,6 +87,9 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac @Inject lateinit var scanCardUseCase: ScanCardUseCase + @Inject + lateinit var walletRouter: WalletRouter + private var snackbar: Snackbar? = null private val dialogManager = DialogManager() private val binding: ActivityMainBinding by viewBinding(ActivityMainBinding::bind) @@ -113,7 +117,13 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac ), ) - store.dispatch(DaggerGraphAction.SetActivityDependencies(testerRouter, scanCardUseCase)) + store.dispatch( + DaggerGraphAction.SetActivityDependencies( + testerRouter = testerRouter, + scanCardUseCase = scanCardUseCase, + walletRouter = walletRouter, + ), + ) } private fun initUserWalletsListManager() { diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index 1d0af74a36..f45d14c864 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -33,7 +33,6 @@ import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.store import com.tangem.wallet.R import timber.log.Timber -import com.tangem.feature.wallet.presentation.WalletFragment as RedesignedWalletFragment import com.tangem.tap.features.customtoken.impl.presentation.AddCustomTokenFragment as RedesignedAddCustomTokenFragment fun FragmentActivity.openFragment( @@ -140,7 +139,9 @@ private fun fragmentFactory(screen: AppScreen): Fragment { getDependency = DaggerGraphState::walletFeatureToggles, ) if (featureToggles.isRedesignedScreenEnabled) { - RedesignedWalletFragment() + store.state.daggerGraphState + .get(getDependency = DaggerGraphState::walletRouter) + .getEntryFragment() } else { WalletFragment() } diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt index 3c6621663b..99734202b0 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt @@ -2,6 +2,7 @@ package com.tangem.tap.proxy.redux import com.tangem.domain.card.ScanCardUseCase import com.tangem.features.tester.api.TesterRouter +import com.tangem.features.wallet.navigation.WalletRouter import org.rekotlin.Action sealed interface DaggerGraphAction : Action { @@ -9,5 +10,6 @@ sealed interface DaggerGraphAction : Action { data class SetActivityDependencies( val testerRouter: TesterRouter, val scanCardUseCase: ScanCardUseCase, + val walletRouter: WalletRouter, ) : DaggerGraphAction } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt index 267665cce3..a2c7994ce1 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt @@ -15,6 +15,7 @@ object DaggerGraphReducer { is DaggerGraphAction.SetActivityDependencies -> state.daggerGraphState.copy( testerRouter = action.testerRouter, scanCardUseCase = action.scanCardUseCase, + walletRouter = action.walletRouter, ) } } diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index 5e823c02da..b59edc1928 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -5,6 +5,7 @@ import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.domain.card.ScanCardUseCase import com.tangem.features.tester.api.TesterRouter import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles +import com.tangem.features.wallet.navigation.WalletRouter import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles import org.rekotlin.StateType @@ -15,6 +16,7 @@ data class DaggerGraphState( val customTokenFeatureToggles: CustomTokenFeatureToggles? = null, val scanCardUseCase: ScanCardUseCase? = null, val walletFeatureToggles: WalletFeatureToggles? = null, + val walletRouter: WalletRouter? = null, ) : StateType { inline fun get(getDependency: DaggerGraphState.() -> T?): T { diff --git a/features/wallet/api/build.gradle.kts b/features/wallet/api/build.gradle.kts index 1fbece85d5..3b5deb5bae 100644 --- a/features/wallet/api/build.gradle.kts +++ b/features/wallet/api/build.gradle.kts @@ -2,4 +2,9 @@ plugins { alias(deps.plugins.android.library) alias(deps.plugins.kotlin.android) id("configuration") +} + +dependencies { + /** AndroidX */ + implementation(deps.androidx.fragment.ktx) } \ No newline at end of file diff --git a/features/wallet/api/src/main/kotlin/com/tangem/features/wallet/navigation/WalletRouter.kt b/features/wallet/api/src/main/kotlin/com/tangem/features/wallet/navigation/WalletRouter.kt new file mode 100644 index 0000000000..168bb9eca5 --- /dev/null +++ b/features/wallet/api/src/main/kotlin/com/tangem/features/wallet/navigation/WalletRouter.kt @@ -0,0 +1,14 @@ +package com.tangem.features.wallet.navigation + +import androidx.fragment.app.Fragment + +/** + * Wallet feature router + * +[REDACTED_AUTHOR] + */ +interface WalletRouter { + + /** Get feature entry point [Fragment] */ + fun getEntryFragment(): Fragment +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/di/WalletRouterModule.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/di/WalletRouterModule.kt new file mode 100644 index 0000000000..d981abb47b --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/di/WalletRouterModule.kt @@ -0,0 +1,18 @@ +package com.tangem.feature.wallet.di + +import com.tangem.feature.wallet.presentation.router.DefaultWalletRouter +import com.tangem.features.wallet.navigation.WalletRouter +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent +import dagger.hilt.android.scopes.ActivityScoped + +@Module +@InstallIn(ActivityComponent::class) +internal object WalletRouterModule { + + @Provides + @ActivityScoped + fun provideWalletRouter(): WalletRouter = DefaultWalletRouter() +} \ 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 3c6f1114ad..1bc2cd04fd 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 @@ -8,9 +8,11 @@ import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView import androidx.core.view.WindowCompat import androidx.fragment.app.Fragment -import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.impl.R +import com.tangem.feature.wallet.presentation.router.InnerWalletRouter +import com.tangem.features.wallet.navigation.WalletRouter import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject /** * Wallet fragment @@ -18,7 +20,16 @@ import dagger.hilt.android.AndroidEntryPoint [REDACTED_AUTHOR] */ @AndroidEntryPoint -class WalletFragment : Fragment() { +internal class WalletFragment : Fragment() { + + /** Feature router */ + @Inject + internal lateinit var walletRouter: WalletRouter + + private val _walletRouter: InnerWalletRouter + get() = requireNotNull(walletRouter as? InnerWalletRouter) { + "_walletRouter should be instance of InnerWalletRouter" + } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { activity?.window?.let { WindowCompat.setDecorFitsSystemWindows(it, true) } @@ -31,11 +42,14 @@ class WalletFragment : Fragment() { return ComposeView(inflater.context).apply { setContent { isTransitionGroup = true - - TangemTheme { - // TODO: [REDACTED_TASK_KEY] Call WalletScreen - } + _walletRouter.Initialize() } } } + + companion object { + + /** Create wallet fragment instance */ + fun create(): WalletFragment = WalletFragment() + } } \ No newline at end of file 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 new file mode 100644 index 0000000000..90a84bef1c --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -0,0 +1,51 @@ +package com.tangem.feature.wallet.presentation.router + +import androidx.activity.compose.BackHandler +import androidx.compose.runtime.Composable +import androidx.fragment.app.Fragment +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.wallet.presentation.WalletFragment +import com.tangem.feature.wallet.presentation.ui.WalletScreen +import com.tangem.feature.wallet.presentation.viewmodels.WalletViewModel +import kotlin.properties.Delegates + +/** Default implementation of wallet feature router */ +internal class DefaultWalletRouter : InnerWalletRouter { + + private var navController: NavHostController by Delegates.notNull() + + override fun getEntryFragment(): Fragment = WalletFragment.create() + + @Composable + override fun Initialize() { + TangemTheme { + NavHost( + navController = rememberNavController().apply { navController = this }, + startDestination = WalletScreens.WALLET.name, + ) { + composable(WalletScreens.WALLET.name) { + val viewModel = hiltViewModel().apply { router = this@DefaultWalletRouter } + WalletScreen(state = viewModel.uiState) + } + + composable(WalletScreens.ORGANIZE_TOKENS.name) { + BackHandler(onBack = ::popBackStack) + // TODO: Add OrganizeTokensScreen + } + } + } + } + + override fun popBackStack() { + navController.popBackStack() + } + + override fun openOrganizeTokensScreen() { + navController.navigate(WalletScreens.ORGANIZE_TOKENS.name) + } +} \ 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 new file mode 100644 index 0000000000..00782aa81d --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt @@ -0,0 +1,23 @@ +package com.tangem.feature.wallet.presentation.router + +import androidx.compose.runtime.Composable +import com.tangem.features.wallet.navigation.WalletRouter + +/** + * Interface of inner wallet feature router + * +[REDACTED_AUTHOR] + */ +internal interface InnerWalletRouter : WalletRouter { + + /** Initialize router */ + @Suppress("TopLevelComposableFunctions") + @Composable + fun Initialize() + + /** Pop back stack */ + fun popBackStack() + + /** Open organize tokens screen */ + fun openOrganizeTokensScreen() +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/WalletScreens.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/WalletScreens.kt new file mode 100644 index 0000000000..0a74b766aa --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/WalletScreens.kt @@ -0,0 +1,10 @@ +package com.tangem.feature.wallet.presentation.router + +/** + * Wallet feature screens + * +[REDACTED_AUTHOR] + */ +enum class WalletScreens { + WALLET, ORGANIZE_TOKENS +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletPreviewData.kt index 535b09f1b4..b716022f9b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletPreviewData.kt @@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.ui import com.tangem.core.ui.R import com.tangem.feature.wallet.presentation.state.WalletCardState +import com.tangem.feature.wallet.presentation.state.WalletStateHolder import java.util.UUID internal object WalletPreviewData { @@ -38,4 +39,13 @@ internal object WalletPreviewData { imageResId = R.drawable.ill_businessman_3d, onClick = {}, ) + + val walletScreenState = WalletStateHolder( + onBackClick = {}, + headerConfig = WalletStateHolder.HeaderConfig( + wallets = listOf(walletCardContent, walletCardLoading, walletCardHiddenContent, walletCardError), + onScanCardClick = {}, + onMoreClick = {}, + ), + ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/components/WalletHeader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/components/WalletHeader.kt index 0c2169bb16..acfa7b9a49 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/components/WalletHeader.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/components/WalletHeader.kt @@ -1,11 +1,17 @@ package com.tangem.feature.wallet.presentation.ui.components +import androidx.compose.animation.core.* +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.LocalOverscrollConfiguration import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.* import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.painterResource @@ -26,6 +32,7 @@ import com.tangem.feature.wallet.presentation.ui.WalletPreviewData * [REDACTED_AUTHOR] */ +@OptIn(ExperimentalFoundationApi::class) @Composable internal fun WalletHeader(config: WalletStateHolder.HeaderConfig) { Column( @@ -34,15 +41,23 @@ internal fun WalletHeader(config: WalletStateHolder.HeaderConfig) { .padding(bottom = TangemTheme.dimens.spacing14), ) { TopBar(onScanCardClick = config.onScanCardClick, onMoreClick = config.onMoreClick) - LazyRow( - contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16), - horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), - ) { - items(items = config.wallets, key = WalletCardState::id) { state -> - WalletCard( - state = state, - modifier = Modifier.width(LocalConfiguration.current.screenWidthDp.dp - TangemTheme.dimens.size32), - ) + + CompositionLocalProvider(LocalOverscrollConfiguration provides null) { + val lazyListState = rememberLazyListState() + LazyRow( + state = lazyListState, + contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16), + horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), + flingBehavior = rememberSnapFlingBehavior(lazyListState = lazyListState), + ) { + items(items = config.wallets, key = WalletCardState::id) { state -> + WalletCard( + state = state, + modifier = Modifier.width( + LocalConfiguration.current.screenWidthDp.dp - TangemTheme.dimens.size32, + ), + ) + } } } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/viewmodels/WalletViewModel.kt new file mode 100644 index 0000000000..c69e4f439b --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/viewmodels/WalletViewModel.kt @@ -0,0 +1,36 @@ +package com.tangem.feature.wallet.presentation.viewmodels + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import com.tangem.feature.wallet.presentation.router.InnerWalletRouter +import com.tangem.feature.wallet.presentation.state.WalletStateHolder +import com.tangem.feature.wallet.presentation.ui.WalletPreviewData +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject +import kotlin.properties.Delegates + +/** + * Wallet screen view model + * +[REDACTED_AUTHOR] + */ +@HiltViewModel +internal class WalletViewModel @Inject constructor() : ViewModel() { + + /** Feature router */ + var router: InnerWalletRouter by Delegates.notNull() + + /** Screen state */ + var uiState by mutableStateOf(getInitialState()) + private set + + // TODO: [REDACTED_TASK_KEY] Use production data instead of WalletPreviewData + private fun getInitialState(): WalletStateHolder = WalletPreviewData.walletScreenState.copy( + onBackClick = { router.popBackStack() }, + headerConfig = WalletPreviewData.walletScreenState.headerConfig.copy( + onScanCardClick = { router.openOrganizeTokensScreen() }, + ), + ) +} \ No newline at end of file