Updated on 2026-08-14
This commit is contained in:
parent
3a4ce4d24b
commit
76c64ef543
27 changed files with 270 additions and 238 deletions
|
|
@ -37,7 +37,7 @@ import com.tangem.domain.card.ScanCardUseCase
|
|||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.feature.qrscanning.QrScanningRouter
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensRouter
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensUi
|
||||
import com.tangem.features.send.api.navigation.SendRouter
|
||||
import com.tangem.features.tester.api.TesterRouter
|
||||
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
||||
|
|
@ -126,7 +126,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
lateinit var tokenDetailsRouter: TokenDetailsRouter
|
||||
|
||||
@Inject
|
||||
lateinit var manageTokensRouter: ManageTokensRouter
|
||||
lateinit var manageTokensUi: ManageTokensUi
|
||||
|
||||
@Inject
|
||||
lateinit var walletConnectInteractor: WalletConnectInteractor
|
||||
|
|
@ -213,7 +213,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
walletRouter = walletRouter,
|
||||
walletConnectInteractor = walletConnectInteractor,
|
||||
tokenDetailsRouter = tokenDetailsRouter,
|
||||
manageTokensRouter = manageTokensRouter,
|
||||
manageTokensUi = manageTokensUi,
|
||||
cardSdkConfigRepository = cardSdkConfigRepository,
|
||||
sendRouter = sendRouter,
|
||||
qrScanningRouter = qrScanningRouter,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.tap.proxy.redux
|
|||
import com.tangem.feature.qrscanning.QrScanningRouter
|
||||
import com.tangem.domain.card.ScanCardUseCase
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensRouter
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensUi
|
||||
import com.tangem.features.send.api.navigation.SendRouter
|
||||
import com.tangem.features.tester.api.TesterRouter
|
||||
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
||||
|
|
@ -19,7 +19,7 @@ sealed interface DaggerGraphAction : Action {
|
|||
val walletRouter: WalletRouter,
|
||||
val walletConnectInteractor: WalletConnectInteractor,
|
||||
val tokenDetailsRouter: TokenDetailsRouter,
|
||||
val manageTokensRouter: ManageTokensRouter,
|
||||
val manageTokensUi: ManageTokensUi,
|
||||
val cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
val sendRouter: SendRouter,
|
||||
val qrScanningRouter: QrScanningRouter,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ object DaggerGraphReducer {
|
|||
walletRouter = action.walletRouter,
|
||||
walletConnectInteractor = action.walletConnectInteractor,
|
||||
tokenDetailsRouter = action.tokenDetailsRouter,
|
||||
manageTokensRouter = action.manageTokensRouter,
|
||||
manageTokensUi = action.manageTokensUi,
|
||||
cardSdkConfigRepository = action.cardSdkConfigRepository,
|
||||
sendRouter = action.sendRouter,
|
||||
qrScanningRouter = action.qrScanningRouter,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import com.tangem.domain.walletmanager.WalletManagersFacade
|
|||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.feature.qrscanning.QrScanningRouter
|
||||
import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensRouter
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensUi
|
||||
import com.tangem.features.send.api.featuretoggles.SendFeatureToggles
|
||||
import com.tangem.features.send.api.navigation.SendRouter
|
||||
import com.tangem.features.tester.api.TesterRouter
|
||||
|
|
@ -39,7 +39,7 @@ data class DaggerGraphState(
|
|||
val walletConnectInteractor: WalletConnectInteractor? = null,
|
||||
val tokenDetailsRouter: TokenDetailsRouter? = null,
|
||||
val manageTokensFeatureToggles: ManageTokensFeatureToggles? = null,
|
||||
val manageTokensRouter: ManageTokensRouter? = null,
|
||||
val manageTokensUi: ManageTokensUi? = null,
|
||||
val scanCardProcessor: ScanCardProcessor? = null,
|
||||
val cardSdkConfigRepository: CardSdkConfigRepository? = null,
|
||||
val appCurrencyRepository: AppCurrencyRepository? = null,
|
||||
|
|
|
|||
|
|
@ -6,4 +6,6 @@ import androidx.compose.runtime.Immutable
|
|||
* General interface for bottom sheet config model
|
||||
*/
|
||||
@Immutable
|
||||
interface TangemBottomSheetConfigContent
|
||||
interface TangemBottomSheetConfigContent {
|
||||
object Empty : TangemBottomSheetConfigContent
|
||||
}
|
||||
|
|
@ -9,9 +9,5 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
/** AndroidX */
|
||||
implementation(deps.androidx.fragment.ktx)
|
||||
|
||||
implementation(deps.compose.foundation)
|
||||
implementation(deps.compose.navigation)
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package com.tangem.features.managetokens.navigation
|
||||
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavHostController
|
||||
|
||||
interface ManageTokensRouter {
|
||||
|
||||
val startDestination: String
|
||||
|
||||
fun NavGraphBuilder.initialize(navController: NavHostController, onHeaderSizeChange: (Dp) -> Unit)
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.features.managetokens.navigation
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.unit.Dp
|
||||
|
||||
interface ManageTokensUi {
|
||||
@Suppress("TopLevelComposableFunctions")
|
||||
@Composable
|
||||
fun Content(onHeaderSizeChange: (Dp) -> Unit)
|
||||
}
|
||||
|
|
@ -1,21 +1,18 @@
|
|||
package com.tangem.managetokens.di
|
||||
|
||||
import com.tangem.core.navigation.ReduxNavController
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensRouter
|
||||
import com.tangem.managetokens.presentation.router.DefaultManageTokensRouter
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensUi
|
||||
import com.tangem.managetokens.presentation.router.ManageTokensUiImpl
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ActivityComponent
|
||||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(ActivityComponent::class)
|
||||
internal object ManageTokensRouterModule {
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface ManageTokensRouterModule {
|
||||
|
||||
@Provides
|
||||
@ActivityScoped
|
||||
fun provideManageTokensRouter(reduxNavController: ReduxNavController): ManageTokensRouter {
|
||||
return DefaultManageTokensRouter(reduxNavController)
|
||||
}
|
||||
@Binds
|
||||
@Singleton
|
||||
fun provideManageTokensRouter(manageTokensUiImpl: ManageTokensUiImpl): ManageTokensUi
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.managetokens.presentation.addcustomtoken.router
|
||||
|
||||
/**
|
||||
* Add Custom Tokens screens
|
||||
* @property route route string representation
|
||||
*/
|
||||
internal sealed class AddCustomTokenRoute(val route: String) {
|
||||
object Main : AddCustomTokenRoute("$BASE_ROUTE/main")
|
||||
object ChooseNetwork : AddCustomTokenRoute("$BASE_ROUTE/choose_network")
|
||||
object ChooseWallet : AddCustomTokenRoute("$BASE_ROUTE/choose_wallet")
|
||||
object ChooseDerivation : AddCustomTokenRoute("$BASE_ROUTE/choose_derivation")
|
||||
}
|
||||
|
||||
private const val BASE_ROUTE = "manage_tokens/add_custom_token"
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.tangem.managetokens.presentation.addcustomtoken.router
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.navigation.NavController
|
||||
|
||||
@Stable
|
||||
internal class AddCustomTokenRouter(
|
||||
private val navController: NavController,
|
||||
) {
|
||||
/** Pop back stack */
|
||||
fun popBackStack() {
|
||||
navController.popBackStack()
|
||||
}
|
||||
|
||||
/** Open custom token choose network screen */
|
||||
fun openCustomTokenChooseNetwork() {
|
||||
navController.navigate(AddCustomTokenRoute.ChooseNetwork.route)
|
||||
}
|
||||
|
||||
/** Open custom token choose derivation screen */
|
||||
fun openCustomTokenChooseDerivation() {
|
||||
navController.navigate(AddCustomTokenRoute.ChooseDerivation.route)
|
||||
}
|
||||
|
||||
/** Open custom token choose wallet screen */
|
||||
fun openCustomTokenChooseWallet() {
|
||||
navController.navigate(AddCustomTokenRoute.ChooseWallet.route)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
package com.tangem.managetokens.presentation.addcustomtoken.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetDraggableHeader
|
||||
import com.tangem.core.ui.components.bottomsheets.collapse
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.managetokens.presentation.addcustomtoken.router.AddCustomTokenRoute
|
||||
import com.tangem.managetokens.presentation.addcustomtoken.router.AddCustomTokenRouter
|
||||
import com.tangem.managetokens.presentation.addcustomtoken.viewmodels.AddCustomTokenViewModel
|
||||
import com.tangem.managetokens.presentation.common.state.ChooseWalletState
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AddCustomTokenBottomSheet(config: TangemBottomSheetConfig) {
|
||||
val viewModel = hiltViewModel<AddCustomTokenViewModel>()
|
||||
|
||||
var isVisible by remember { mutableStateOf(value = config.isShow) }
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
|
||||
if (isVisible) {
|
||||
// ViewModel cannot be scoped to ModalBottomSheet's lifecycle,
|
||||
// so we have to manually initialize and dispose its state when bottom sheet enters and leaves a composition
|
||||
DisposableEffect(viewModel) {
|
||||
viewModel.onInitialize()
|
||||
onDispose { viewModel.onDispose() }
|
||||
}
|
||||
|
||||
// FIXME: handle back presses after updating material3 to 1.2.0
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = config.onDismissRequest,
|
||||
sheetState = sheetState,
|
||||
containerColor = TangemTheme.colors.background.tertiary,
|
||||
shape = TangemTheme.shapes.bottomSheetLarge,
|
||||
windowInsets = WindowInsets.systemBars.only(WindowInsetsSides.Top),
|
||||
dragHandle = { TangemBottomSheetDraggableHeader(color = TangemTheme.colors.background.tertiary) },
|
||||
) {
|
||||
Content(onDismissRequest = config.onDismissRequest, viewModel = viewModel)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = config.isShow) {
|
||||
if (config.isShow) {
|
||||
isVisible = true
|
||||
} else {
|
||||
sheetState.collapse { isVisible = false }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Composable
|
||||
private fun Content(viewModel: AddCustomTokenViewModel, onDismissRequest: () -> Unit) {
|
||||
val navController = rememberNavController()
|
||||
|
||||
LaunchedEffect(navController) {
|
||||
navController.currentBackStack
|
||||
.collect {
|
||||
if (it.isEmpty()) {
|
||||
onDismissRequest()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val router = remember(navController) { AddCustomTokenRouter(navController) }
|
||||
|
||||
viewModel.router = router
|
||||
|
||||
NavHost(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
navController = navController,
|
||||
startDestination = AddCustomTokenRoute.Main.route,
|
||||
) {
|
||||
composable(
|
||||
route = AddCustomTokenRoute.Main.route,
|
||||
) {
|
||||
AddCustomTokenScreen(state = viewModel.uiState)
|
||||
}
|
||||
composable(
|
||||
route = AddCustomTokenRoute.ChooseNetwork.route,
|
||||
) {
|
||||
ChooseNetworkCustomScreen(state = viewModel.uiState.chooseNetworkState)
|
||||
}
|
||||
composable(
|
||||
route = AddCustomTokenRoute.ChooseDerivation.route,
|
||||
) {
|
||||
ChooseDerivationScreen(state = requireNotNull(viewModel.uiState.chooseDerivationState))
|
||||
}
|
||||
composable(
|
||||
route = AddCustomTokenRoute.ChooseWallet.route,
|
||||
) {
|
||||
CustomTokensChooseWalletScreen(state = viewModel.uiState.chooseWalletState as ChooseWalletState.Choose)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.managetokens.presentation.addcustomtoken.viewmodels
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
|
|
@ -20,6 +21,7 @@ import com.tangem.domain.wallets.models.UserWalletId
|
|||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.domain.wallets.usecase.SelectWalletUseCase
|
||||
import com.tangem.managetokens.presentation.addcustomtoken.router.AddCustomTokenRouter
|
||||
import com.tangem.managetokens.presentation.common.analytics.ManageTokens
|
||||
import com.tangem.managetokens.presentation.common.state.AlertState
|
||||
import com.tangem.managetokens.presentation.common.state.Event
|
||||
|
|
@ -29,22 +31,20 @@ import com.tangem.managetokens.presentation.addcustomtoken.state.factory.AddCust
|
|||
import com.tangem.managetokens.presentation.addcustomtoken.state.factory.ContractAddressToCustomTokenDataConverter
|
||||
import com.tangem.managetokens.presentation.addcustomtoken.state.factory.AddCustomTokenStateFactory
|
||||
import com.tangem.managetokens.presentation.addcustomtoken.state.factory.FoundTokenToCustomTokenDataConverter
|
||||
import com.tangem.managetokens.presentation.router.InnerManageTokensRouter
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.Debouncer
|
||||
import com.tangem.utils.coroutines.Debouncer.Companion.DEFAULT_WAIT_TIME_MS
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.collections.immutable.toPersistentSet
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.plus
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
@Suppress("LongParameterList", "TooManyFunctions", "LargeClass")
|
||||
@Stable
|
||||
@HiltViewModel
|
||||
internal class AddCustomTokenViewModel @Inject constructor(
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
@ -67,12 +67,15 @@ internal class AddCustomTokenViewModel @Inject constructor(
|
|||
clickIntents = this,
|
||||
)
|
||||
|
||||
var router: InnerManageTokensRouter by Delegates.notNull()
|
||||
var router: AddCustomTokenRouter by Delegates.notNull()
|
||||
|
||||
var uiState: AddCustomTokenState by mutableStateOf(stateFactory.getInitialState())
|
||||
private set
|
||||
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
/**
|
||||
* Called when the bottom sheet was opened
|
||||
*/
|
||||
fun onInitialize() {
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
getWalletsUseCase()
|
||||
.distinctUntilChanged()
|
||||
|
|
@ -92,6 +95,16 @@ internal class AddCustomTokenViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the bottom sheet is closed
|
||||
*/
|
||||
fun onDispose() {
|
||||
// We have to manually cancel viewModelScope's child jobs when bottom sheet is closed
|
||||
viewModelScope.coroutineContext.cancelChildren()
|
||||
// and reset state
|
||||
uiState = stateFactory.getInitialState()
|
||||
}
|
||||
|
||||
override fun onDestroy(owner: LifecycleOwner) {
|
||||
uiState = stateFactory.getInitialState()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.managetokens.presentation.managetokens.state
|
||||
|
||||
import androidx.paging.PagingData
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.managetokens.presentation.common.state.ChooseWalletState
|
||||
import com.tangem.managetokens.presentation.common.state.Event
|
||||
|
|
@ -15,6 +16,7 @@ internal data class ManageTokensState(
|
|||
val derivationNotification: DerivationNotificationState? = null,
|
||||
val selectedToken: TokenItemState.Loaded? = null,
|
||||
val showChooseWalletScreen: Boolean = false,
|
||||
val customTokenBottomSheetConfig: TangemBottomSheetConfig,
|
||||
val event: StateEvent<Event>,
|
||||
val onEmptySearchResult: (String) -> Unit,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.managetokens.presentation.managetokens.state.factory
|
||||
|
||||
import androidx.paging.PagingData
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.event.triggeredEvent
|
||||
import com.tangem.domain.tokens.CurrencyCompatibilityError
|
||||
|
|
@ -39,6 +41,11 @@ internal class ManageTokensStateFactory(
|
|||
event = consumedEvent(),
|
||||
chooseWalletState = ChooseWalletState.NoSelection,
|
||||
onEmptySearchResult = uiIntents::onEmptySearchResult,
|
||||
customTokenBottomSheetConfig = TangemBottomSheetConfig(
|
||||
isShow = false,
|
||||
onDismissRequest = uiIntents::onAddCustomTokenSheetDismissed,
|
||||
content = TangemBottomSheetConfigContent.Empty,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.managetokens.presentation.managetokens.state.previewdata
|
||||
|
||||
import androidx.paging.PagingData
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.managetokens.presentation.common.state.previewdata.ChooseWalletStatePreviewData
|
||||
import com.tangem.managetokens.presentation.managetokens.state.AddCustomTokenButton
|
||||
|
|
@ -20,6 +22,7 @@ internal object ManageTokensStatePreviewData {
|
|||
event = consumedEvent(),
|
||||
chooseWalletState = ChooseWalletStatePreviewData.state,
|
||||
onEmptySearchResult = {},
|
||||
customTokenBottomSheetConfig = TangemBottomSheetConfig(false, {}, TangemBottomSheetConfigContent.Empty),
|
||||
)
|
||||
|
||||
val loadingState: ManageTokensState
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import com.tangem.core.ui.components.SpacerH18
|
|||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.keyboardAsState
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.managetokens.presentation.addcustomtoken.ui.AddCustomTokenBottomSheet
|
||||
import com.tangem.managetokens.presentation.common.state.AlertState
|
||||
import com.tangem.managetokens.presentation.common.state.ChooseWalletState
|
||||
import com.tangem.managetokens.presentation.common.ui.ChooseWalletBottomSheet
|
||||
|
|
@ -49,6 +50,8 @@ internal fun ManageTokensScreen(state: ManageTokensState, onHeaderSizeChange: (D
|
|||
}
|
||||
|
||||
Content(state = state, onHeaderSizeChange = onHeaderSizeChange)
|
||||
|
||||
AddCustomTokenBottomSheet(state.customTokenBottomSheetConfig)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -122,6 +125,9 @@ private fun Content(state: ManageTokensState, onHeaderSizeChange: (Dp) -> Unit)
|
|||
with(density) { tokenListAlertBottomPadding = it.size.height.toDp() }
|
||||
},
|
||||
)
|
||||
DisposableEffect(Unit) {
|
||||
onDispose { tokenListAlertBottomPadding = 0.dp }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,4 +3,6 @@ package com.tangem.managetokens.presentation.managetokens.viewmodels
|
|||
internal interface ManageTokensUiEvents {
|
||||
|
||||
fun onEmptySearchResult(query: String)
|
||||
|
||||
fun onAddCustomTokenSheetDismissed()
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@ package com.tangem.managetokens.presentation.managetokens.viewmodels
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.PagingData
|
||||
|
|
@ -30,7 +29,6 @@ import com.tangem.managetokens.presentation.managetokens.state.ManageTokensState
|
|||
import com.tangem.managetokens.presentation.managetokens.state.TokenButtonType
|
||||
import com.tangem.managetokens.presentation.managetokens.state.TokenItemState
|
||||
import com.tangem.managetokens.presentation.managetokens.state.factory.*
|
||||
import com.tangem.managetokens.presentation.router.InnerManageTokensRouter
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.Debouncer
|
||||
|
|
@ -62,7 +60,7 @@ internal class ManageTokensViewModel @Inject constructor(
|
|||
private val checkCurrencyCompatibilityUseCase: CheckCurrencyCompatibilityUseCase,
|
||||
private val isCryptoCurrencyCoinCouldHide: IsCryptoCurrencyCoinCouldHideUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : ViewModel(), ManageTokensClickIntents, ManageTokensUiEvents, DefaultLifecycleObserver {
|
||||
) : ViewModel(), ManageTokensClickIntents, ManageTokensUiEvents {
|
||||
|
||||
private val debouncer = Debouncer()
|
||||
|
||||
|
|
@ -72,8 +70,6 @@ internal class ManageTokensViewModel @Inject constructor(
|
|||
uiIntents = this,
|
||||
)
|
||||
|
||||
var router: InnerManageTokensRouter by Delegates.notNull()
|
||||
|
||||
var uiState: ManageTokensState by mutableStateOf(stateFactory.getInitialState(flowOf(PagingData.from(emptyList()))))
|
||||
private set
|
||||
|
||||
|
|
@ -190,7 +186,7 @@ internal class ManageTokensViewModel @Inject constructor(
|
|||
|
||||
override fun onAddCustomTokensButtonClick() {
|
||||
analyticsEventHandler.send(ManageTokens.ButtonCustomToken)
|
||||
router.openAddCustomTokenScreen()
|
||||
uiState = uiState.copy(customTokenBottomSheetConfig = uiState.customTokenBottomSheetConfig.copy(isShow = true))
|
||||
}
|
||||
|
||||
override fun onSearchQueryChange(query: String) {
|
||||
|
|
@ -407,4 +403,8 @@ internal class ManageTokensViewModel @Inject constructor(
|
|||
override fun onEmptySearchResult(query: String) {
|
||||
analyticsEventHandler.send(ManageTokens.TokenIsNotFound(query))
|
||||
}
|
||||
|
||||
override fun onAddCustomTokenSheetDismissed() {
|
||||
uiState = uiState.copy(customTokenBottomSheetConfig = uiState.customTokenBottomSheetConfig.copy(isShow = false))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,124 +0,0 @@
|
|||
package com.tangem.managetokens.presentation.router
|
||||
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.navigation
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.core.navigation.ReduxNavController
|
||||
import com.tangem.core.ui.extensions.parentHiltViewModel
|
||||
import com.tangem.managetokens.presentation.addcustomtoken.ui.AddCustomTokenScreen
|
||||
import com.tangem.managetokens.presentation.addcustomtoken.ui.ChooseDerivationScreen
|
||||
import com.tangem.managetokens.presentation.addcustomtoken.ui.ChooseNetworkCustomScreen
|
||||
import com.tangem.managetokens.presentation.addcustomtoken.ui.CustomTokensChooseWalletScreen
|
||||
import com.tangem.managetokens.presentation.addcustomtoken.viewmodels.AddCustomTokenViewModel
|
||||
import com.tangem.managetokens.presentation.common.state.ChooseWalletState
|
||||
import com.tangem.managetokens.presentation.managetokens.ui.ManageTokensScreen
|
||||
import com.tangem.managetokens.presentation.managetokens.viewmodels.ManageTokensViewModel
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
internal class DefaultManageTokensRouter(
|
||||
private val reduxNavController: ReduxNavController,
|
||||
) : InnerManageTokensRouter {
|
||||
|
||||
private var navController: NavHostController by Delegates.notNull()
|
||||
|
||||
override val startDestination: String = ManageTokensRoute.ManageTokens.route
|
||||
|
||||
override fun NavGraphBuilder.initialize(navController: NavHostController, onHeaderSizeChange: (Dp) -> Unit) {
|
||||
this@DefaultManageTokensRouter.navController = navController
|
||||
|
||||
composable(ManageTokensRoute.ManageTokens.route) {
|
||||
val viewModel = hiltViewModel<ManageTokensViewModel>().apply {
|
||||
router = this@DefaultManageTokensRouter
|
||||
}
|
||||
LocalLifecycleOwner.current.lifecycle.addObserver(viewModel)
|
||||
|
||||
ManageTokensScreen(
|
||||
state = viewModel.uiState,
|
||||
onHeaderSizeChange = onHeaderSizeChange,
|
||||
)
|
||||
}
|
||||
|
||||
navigation(
|
||||
route = ManageTokensRoute.AddCustomToken.route,
|
||||
startDestination = ManageTokensRoute.AddCustomToken.Main.route,
|
||||
) {
|
||||
composable(
|
||||
ManageTokensRoute.AddCustomToken.Main.route,
|
||||
) { backStackEntry ->
|
||||
val viewModel =
|
||||
backStackEntry.parentHiltViewModel<AddCustomTokenViewModel>(navController = navController).apply {
|
||||
router = this@DefaultManageTokensRouter
|
||||
}
|
||||
LocalLifecycleOwner.current.lifecycle.addObserver(viewModel)
|
||||
AddCustomTokenScreen(state = viewModel.uiState)
|
||||
}
|
||||
composable(
|
||||
ManageTokensRoute.AddCustomToken.ChooseNetwork.route,
|
||||
) { backStackEntry ->
|
||||
val viewModel =
|
||||
backStackEntry.parentHiltViewModel<AddCustomTokenViewModel>(navController = navController).apply {
|
||||
router = this@DefaultManageTokensRouter
|
||||
}
|
||||
ChooseNetworkCustomScreen(
|
||||
state = viewModel.uiState.chooseNetworkState,
|
||||
)
|
||||
}
|
||||
composable(
|
||||
ManageTokensRoute.AddCustomToken.ChooseDerivation.route,
|
||||
) { backStackEntry ->
|
||||
val viewModel =
|
||||
backStackEntry.parentHiltViewModel<AddCustomTokenViewModel>(navController = navController).apply {
|
||||
router = this@DefaultManageTokensRouter
|
||||
}
|
||||
ChooseDerivationScreen(
|
||||
state = requireNotNull(viewModel.uiState.chooseDerivationState),
|
||||
)
|
||||
}
|
||||
composable(
|
||||
ManageTokensRoute.AddCustomToken.ChooseWallet.route,
|
||||
) { backStackEntry ->
|
||||
val viewModel =
|
||||
backStackEntry.parentHiltViewModel<AddCustomTokenViewModel>(navController = navController).apply {
|
||||
router = this@DefaultManageTokensRouter
|
||||
}
|
||||
CustomTokensChooseWalletScreen(
|
||||
state = viewModel.uiState.chooseWalletState as ChooseWalletState.Choose,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun popBackStack(screen: AppScreen?) {
|
||||
if (screen != null) {
|
||||
reduxNavController.navigate(action = NavigationAction.PopBackTo(screen))
|
||||
} else {
|
||||
navController.popBackStack()
|
||||
}
|
||||
}
|
||||
|
||||
override fun openManageTokensScreen() {
|
||||
navController.navigate(ManageTokensRoute.ManageTokens.route)
|
||||
}
|
||||
|
||||
override fun openAddCustomTokenScreen() {
|
||||
navController.navigate(ManageTokensRoute.AddCustomToken.route)
|
||||
}
|
||||
|
||||
override fun openCustomTokenChooseNetwork() {
|
||||
navController.navigate(ManageTokensRoute.AddCustomToken.ChooseNetwork.route)
|
||||
}
|
||||
|
||||
override fun openCustomTokenChooseDerivation() {
|
||||
navController.navigate(ManageTokensRoute.AddCustomToken.ChooseDerivation.route)
|
||||
}
|
||||
|
||||
override fun openCustomTokenChooseWallet() {
|
||||
navController.navigate(ManageTokensRoute.AddCustomToken.ChooseWallet.route)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
package com.tangem.managetokens.presentation.router
|
||||
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensRouter
|
||||
|
||||
internal interface InnerManageTokensRouter : ManageTokensRouter {
|
||||
|
||||
/** Pop back stack */
|
||||
fun popBackStack(screen: AppScreen? = null)
|
||||
|
||||
/** Open manage tokens screen */
|
||||
fun openManageTokensScreen()
|
||||
|
||||
/** Open add custom token screen */
|
||||
fun openAddCustomTokenScreen()
|
||||
|
||||
/** Open custom token choose network screen */
|
||||
fun openCustomTokenChooseNetwork()
|
||||
|
||||
/** Open custom token choose derivation screen */
|
||||
fun openCustomTokenChooseDerivation()
|
||||
|
||||
/** Open custom token choose wallet screen */
|
||||
fun openCustomTokenChooseWallet()
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package com.tangem.managetokens.presentation.router
|
||||
|
||||
/**
|
||||
* Manage Tokens screens
|
||||
*
|
||||
* @property route route string representation
|
||||
*
|
||||
*/
|
||||
internal sealed class ManageTokensRoute(val route: String) {
|
||||
|
||||
object ManageTokens : ManageTokensRoute(route = "manage_tokens")
|
||||
|
||||
object AddCustomToken : ManageTokensRoute(route = "manage_tokens/add_custom_token") {
|
||||
object Main : ManageTokensRoute(AddCustomToken.route + "/main")
|
||||
object ChooseNetwork : ManageTokensRoute(AddCustomToken.route + "/choose_network")
|
||||
object ChooseWallet : ManageTokensRoute(AddCustomToken.route + "/choose_wallet")
|
||||
object ChooseDerivation : ManageTokensRoute(AddCustomToken.route + "/choose_derivation")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.managetokens.presentation.router
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensUi
|
||||
import com.tangem.managetokens.presentation.managetokens.ui.ManageTokensScreen
|
||||
import com.tangem.managetokens.presentation.managetokens.viewmodels.ManageTokensViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class ManageTokensUiImpl @Inject constructor() : ManageTokensUi {
|
||||
|
||||
@Composable
|
||||
override fun Content(onHeaderSizeChange: (Dp) -> Unit) {
|
||||
val viewModel = hiltViewModel<ManageTokensViewModel>()
|
||||
|
||||
ManageTokensScreen(
|
||||
state = viewModel.uiState,
|
||||
onHeaderSizeChange = onHeaderSizeChange,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
import com.tangem.core.ui.screen.ComposeFragment
|
||||
import com.tangem.core.ui.theme.AppThemeModeHolder
|
||||
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensRouter
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensUi
|
||||
import com.tangem.features.wallet.navigation.WalletRouter
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
|
|
@ -24,7 +24,7 @@ internal class WalletFragment : ComposeFragment() {
|
|||
override lateinit var appThemeModeHolder: AppThemeModeHolder
|
||||
|
||||
@Inject
|
||||
internal lateinit var manageTokensRouter: ManageTokensRouter
|
||||
internal lateinit var manageTokensUi: ManageTokensUi
|
||||
|
||||
/** Feature router */
|
||||
@Inject
|
||||
|
|
@ -44,7 +44,7 @@ internal class WalletFragment : ComposeFragment() {
|
|||
|
||||
_walletRouter.Initialize(
|
||||
onFinish = requireActivity()::finish,
|
||||
manageTokensRouter = manageTokensRouter,
|
||||
manageTokensUi = manageTokensUi,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.router
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -27,7 +28,7 @@ import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensScree
|
|||
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensViewModel
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletViewModel
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensRouter
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensUi
|
||||
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
|
|
@ -42,7 +43,7 @@ internal class DefaultWalletRouter(
|
|||
override fun getEntryFragment(): Fragment = WalletFragment.create()
|
||||
|
||||
@Composable
|
||||
override fun Initialize(onFinish: () -> Unit, manageTokensRouter: ManageTokensRouter) {
|
||||
override fun Initialize(onFinish: () -> Unit, manageTokensUi: ManageTokensUi) {
|
||||
this.onFinish = onFinish
|
||||
|
||||
NavHost(
|
||||
|
|
@ -56,24 +57,15 @@ internal class DefaultWalletRouter(
|
|||
}
|
||||
|
||||
var bottomSheetHeaderHeight by remember { mutableStateOf(0.dp) }
|
||||
val innerController = rememberNavController()
|
||||
|
||||
WalletScreen(
|
||||
state = viewModel.uiState.collectAsStateWithLifecycle().value,
|
||||
bottomSheetHeaderHeightProvider = { bottomSheetHeaderHeight },
|
||||
bottomSheetContent = {
|
||||
NavHost(
|
||||
navController = innerController,
|
||||
startDestination = manageTokensRouter.startDestination,
|
||||
) {
|
||||
// Manage Tokens
|
||||
with(manageTokensRouter) {
|
||||
initialize(
|
||||
navController = innerController,
|
||||
onHeaderSizeChange = { bottomSheetHeaderHeight = it },
|
||||
)
|
||||
}
|
||||
}
|
||||
// Manage Tokens
|
||||
manageTokensUi.Content(
|
||||
onHeaderSizeChange = { bottomSheetHeaderHeight = it },
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -97,6 +89,7 @@ internal class DefaultWalletRouter(
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
override fun popBackStack(screen: AppScreen?) {
|
||||
/*
|
||||
* It's hack that avoid issue with closing the wallet screen.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import androidx.compose.runtime.Stable
|
|||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensRouter
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensUi
|
||||
import com.tangem.features.wallet.navigation.WalletRouter
|
||||
|
||||
/**
|
||||
|
|
@ -26,7 +26,7 @@ internal interface InnerWalletRouter : WalletRouter {
|
|||
*/
|
||||
@Suppress("TopLevelComposableFunctions")
|
||||
@Composable
|
||||
fun Initialize(onFinish: () -> Unit, manageTokensRouter: ManageTokensRouter)
|
||||
fun Initialize(onFinish: () -> Unit, manageTokensUi: ManageTokensUi)
|
||||
|
||||
/** Pop back stack */
|
||||
fun popBackStack(screen: AppScreen? = null)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ internal fun BaseExtension.configureCompose(project: Project) {
|
|||
contains(Regex(pattern = ":onboarding\$")) || // TODO: divide on api/impl after migrating all onboarding to module
|
||||
contains(Regex(pattern = ":presentation\$")) ||
|
||||
contains(Regex(pattern = ":app\$")) || // TODO: [REDACTED_JIRA]
|
||||
contains(Regex(pattern = ":features:manage-tokens:api\$")) || // provides Composable function
|
||||
contains(Regex(pattern = ":impl\$"))
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue