Updated on 2026-08-14
This commit is contained in:
parent
1afa4a5d0e
commit
28ed702a51
27 changed files with 468 additions and 25 deletions
|
|
@ -138,6 +138,7 @@ internal class ChildFactory @Inject constructor(
|
|||
AppRoute.ManageTokens.Source.SETTINGS -> ManageTokensSource.SETTINGS
|
||||
AppRoute.ManageTokens.Source.STORIES -> ManageTokensSource.STORIES
|
||||
AppRoute.ManageTokens.Source.ACCOUNT -> ManageTokensSource.ACCOUNT
|
||||
AppRoute.ManageTokens.Source.WALLET -> ManageTokensSource.WALLET
|
||||
}
|
||||
|
||||
val mode = route.accountId?.let { ManageTokensMode.Account(it) } ?: ManageTokensMode.None
|
||||
|
|
|
|||
|
|
@ -146,6 +146,7 @@ sealed class AppRoute(val path: String) : Route {
|
|||
STORIES,
|
||||
SETTINGS,
|
||||
ACCOUNT,
|
||||
WALLET,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,5 +79,9 @@
|
|||
{
|
||||
"name": "SOLANA_TX_HISTORY_ENABLED",
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "ADD_AND_MANAGE_TOKENS_ENABLED",
|
||||
"version": "undefined"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ enum class ManageTokensSource(val analyticsName: String) {
|
|||
ONBOARDING(analyticsName = "Onboarding"),
|
||||
SETTINGS(analyticsName = "Wallet Settings"),
|
||||
ACCOUNT(analyticsName = "Account"),
|
||||
WALLET(analyticsName = "Wallet"),
|
||||
SEND_VIA_SWAP(analyticsName = "SendViaSwap"),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,4 +10,6 @@ interface WalletFeatureToggles {
|
|||
val isWalletReorderFeatureEnabled: Boolean
|
||||
|
||||
val isMainScreenQrScanningEnabled: Boolean
|
||||
|
||||
val isAddAndManageTokensEnabled: Boolean
|
||||
}
|
||||
|
|
@ -128,6 +128,7 @@ dependencies {
|
|||
implementation(projects.domain.assetsdiscovery)
|
||||
|
||||
/** Feature Apis */
|
||||
implementation(projects.features.account.api)
|
||||
implementation(projects.features.details.api)
|
||||
implementation(projects.features.hotWallet.api)
|
||||
implementation(projects.features.manageTokens.api)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
package com.tangem.feature.wallet.child.managetokens
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.slot.childSlot
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.child.managetokens.model.AddAndManageModel
|
||||
import com.tangem.feature.wallet.child.managetokens.ui.AddAndManageBottomSheetContent
|
||||
import com.tangem.features.account.PortfolioSelectorComponent
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
|
||||
internal class AddAndManageBottomSheetComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
private val params: Params,
|
||||
private val portfolioSelectorComponentFactory: PortfolioSelectorComponent.Factory,
|
||||
) : ComposableBottomSheetComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: AddAndManageModel = getOrCreateModel(params)
|
||||
|
||||
private val portfolioSelectorSlot = childSlot(
|
||||
source = model.portfolioSelectorNavigation,
|
||||
serializer = Unit.serializer(),
|
||||
handleBackButton = false,
|
||||
childFactory = { _, context -> portfolioSelectorChild(context) },
|
||||
)
|
||||
|
||||
private fun portfolioSelectorChild(componentContext: ComponentContext): ComposableBottomSheetComponent =
|
||||
portfolioSelectorComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = PortfolioSelectorComponent.Params(
|
||||
portfolioFetcher = model.portfolioFetcher,
|
||||
controller = model.portfolioSelectorController,
|
||||
bsCallback = model.portfolioSelectorCallback,
|
||||
),
|
||||
)
|
||||
|
||||
override fun dismiss() {
|
||||
params.onDismiss()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun BottomSheet() {
|
||||
val portfolioSelectorSlot by portfolioSelectorSlot.subscribeAsState()
|
||||
|
||||
AddAndManageBottomSheetContent(
|
||||
onAddTokensClick = model::onAddTokensClick,
|
||||
onOrganizeTokensClick = model::onOrganizeTokensClick,
|
||||
onDismiss = ::dismiss,
|
||||
)
|
||||
|
||||
portfolioSelectorSlot.child?.instance?.BottomSheet()
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val userWalletId: UserWalletId,
|
||||
val onDismiss: () -> Unit,
|
||||
val onOrganizeTokensClick: () -> Unit,
|
||||
val onManageTokensClick: (AccountId) -> Unit,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.feature.wallet.child.managetokens.di
|
||||
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.feature.wallet.child.managetokens.model.AddAndManageModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface AddAndManageModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(AddAndManageModel::class)
|
||||
fun bindAddAndManageModel(model: AddAndManageModel): Model
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
package com.tangem.feature.wallet.child.managetokens.model
|
||||
|
||||
import com.arkivanov.decompose.router.slot.SlotNavigation
|
||||
import com.arkivanov.decompose.router.slot.activate
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.feature.wallet.child.managetokens.AddAndManageBottomSheetComponent
|
||||
import com.tangem.features.account.PortfolioFetcher
|
||||
import com.tangem.features.account.PortfolioSelectorComponent
|
||||
import com.tangem.features.account.PortfolioSelectorController
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
internal class AddAndManageModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val portfolioFetcherFactory: PortfolioFetcher.Factory,
|
||||
val portfolioSelectorController: PortfolioSelectorController,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<AddAndManageBottomSheetComponent.Params>()
|
||||
|
||||
val portfolioSelectorNavigation: SlotNavigation<Unit> = SlotNavigation()
|
||||
|
||||
val portfolioFetcher: PortfolioFetcher by lazy {
|
||||
portfolioFetcherFactory.create(
|
||||
mode = PortfolioFetcher.Mode.Wallet(params.userWalletId),
|
||||
scope = modelScope,
|
||||
)
|
||||
}
|
||||
|
||||
val portfolioSelectorCallback = object : PortfolioSelectorComponent.BottomSheetCallback {
|
||||
override val onDismiss: () -> Unit = { portfolioSelectorNavigation.dismiss() }
|
||||
override val onBack: () -> Unit = { portfolioSelectorNavigation.dismiss() }
|
||||
}
|
||||
|
||||
init {
|
||||
observeAccountSelection()
|
||||
}
|
||||
|
||||
fun onAddTokensClick() {
|
||||
modelScope.launch {
|
||||
val data = portfolioFetcher.data.first()
|
||||
val isSingleAccount = data.isSingleChoice(params.userWalletId)
|
||||
|
||||
if (isSingleAccount) {
|
||||
val mainAccountId = data.balances[params.userWalletId]
|
||||
?.accountsBalance
|
||||
?.mainAccount
|
||||
?.accountId
|
||||
?: AccountId.forMainCryptoPortfolio(params.userWalletId)
|
||||
|
||||
params.onDismiss()
|
||||
params.onManageTokensClick(mainAccountId)
|
||||
} else {
|
||||
portfolioSelectorNavigation.activate(Unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onOrganizeTokensClick() {
|
||||
params.onDismiss()
|
||||
params.onOrganizeTokensClick()
|
||||
}
|
||||
|
||||
private fun observeAccountSelection() {
|
||||
modelScope.launch {
|
||||
portfolioSelectorController.selectedAccount.collect { accountId ->
|
||||
if (accountId != null) {
|
||||
portfolioSelectorNavigation.dismiss()
|
||||
params.onDismiss()
|
||||
params.onManageTokensClick(accountId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
package com.tangem.feature.wallet.child.managetokens.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.res.R as ResR
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
@Composable
|
||||
internal fun AddAndManageBottomSheetContent(
|
||||
onAddTokensClick: () -> Unit,
|
||||
onOrganizeTokensClick: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = onDismiss,
|
||||
content = AddAndManageBottomSheetConfigContent,
|
||||
)
|
||||
|
||||
TangemModalBottomSheet<AddAndManageBottomSheetConfigContent>(
|
||||
config = config,
|
||||
containerColor = TangemTheme.colors.background.primary,
|
||||
title = {
|
||||
TangemModalBottomSheetTitle(
|
||||
title = resourceReference(ResR.string.main_add_and_manage_tokens),
|
||||
endIconRes = R.drawable.ic_close_24,
|
||||
onEndClick = onDismiss,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
AddAndManageContent(
|
||||
onAddTokensClick = onAddTokensClick,
|
||||
onOrganizeTokensClick = onOrganizeTokensClick,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AddAndManageContent(onAddTokensClick: () -> Unit, onOrganizeTokensClick: () -> Unit) {
|
||||
Column(
|
||||
modifier = Modifier.padding(
|
||||
start = 16.dp,
|
||||
end = 16.dp,
|
||||
bottom = 16.dp,
|
||||
),
|
||||
) {
|
||||
AddAndManageRow(
|
||||
iconRes = R.drawable.ic_plus_24,
|
||||
title = ResR.string.add_and_manage_sheet_manage_title,
|
||||
subtitle = ResR.string.add_and_manage_sheet_manage_subtitle,
|
||||
onClick = onAddTokensClick,
|
||||
modifier = Modifier.roundedShapeItemDecoration(
|
||||
currentIndex = 0,
|
||||
lastIndex = 1,
|
||||
addDefaultPadding = false,
|
||||
backgroundColor = TangemTheme.colors.background.action,
|
||||
),
|
||||
)
|
||||
AddAndManageRow(
|
||||
iconRes = R.drawable.ic_filter_default_24,
|
||||
title = ResR.string.add_and_manage_sheet_organize_title,
|
||||
subtitle = ResR.string.add_and_manage_sheet_organize_subtitle,
|
||||
onClick = onOrganizeTokensClick,
|
||||
modifier = Modifier.roundedShapeItemDecoration(
|
||||
currentIndex = 1,
|
||||
lastIndex = 1,
|
||||
addDefaultPadding = false,
|
||||
backgroundColor = TangemTheme.colors.background.action,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AddAndManageRow(
|
||||
iconRes: Int,
|
||||
title: Int,
|
||||
subtitle: Int,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(horizontal = 12.dp, vertical = 15.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.clip(CircleShape)
|
||||
.background(TangemTheme.colors.icon.accent.copy(alpha = 0.1f)),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(18.dp),
|
||||
painter = rememberVectorPainter(ImageVector.vectorResource(id = iconRes)),
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(id = title),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Text(
|
||||
text = stringResourceSafe(id = subtitle),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object AddAndManageBottomSheetConfigContent : TangemBottomSheetConfigContent
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
private fun AddAndManageBottomSheetContent_Preview() {
|
||||
TangemThemePreview {
|
||||
AddAndManageContent(
|
||||
onAddTokensClick = {},
|
||||
onOrganizeTokensClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -23,7 +23,9 @@ import com.tangem.core.ui.decompose.ComposableContentComponent
|
|||
import com.tangem.core.ui.decompose.ComposableDialogComponent
|
||||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.domain.tokens.model.details.TokenAction
|
||||
import com.tangem.feature.wallet.child.managetokens.AddAndManageBottomSheetComponent
|
||||
import com.tangem.feature.wallet.child.organizetokens.OrganizeTokensComponent
|
||||
import com.tangem.features.account.PortfolioSelectorComponent
|
||||
import com.tangem.feature.wallet.child.tokenActions.DefaultTokenActionsComponent
|
||||
import com.tangem.feature.wallet.child.tokenActions.TokenActionsComponent
|
||||
import com.tangem.feature.wallet.child.wallet.model.WalletModel
|
||||
|
|
@ -64,6 +66,7 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
private val newPromoBannersFeatureToggles: NewPromoBannersFeatureToggles,
|
||||
private val networkSelectionComponentFactory: NetworkSelectionComponent.Factory,
|
||||
private val tokenActionsComponentFactory: TokenActionsComponent.Factory,
|
||||
private val portfolioSelectorComponentFactory: PortfolioSelectorComponent.Factory,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
|
|
@ -178,6 +181,25 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
),
|
||||
)
|
||||
}
|
||||
is WalletDialogConfig.AddAndManage -> {
|
||||
AddAndManageBottomSheetComponent(
|
||||
appComponentContext = childByContext(componentContext),
|
||||
params = AddAndManageBottomSheetComponent.Params(
|
||||
userWalletId = dialogConfig.userWalletId,
|
||||
onDismiss = model.innerWalletRouter.dialogNavigation::dismiss,
|
||||
onOrganizeTokensClick = {
|
||||
model.innerWalletRouter.openOrganizeTokensScreen(dialogConfig.userWalletId)
|
||||
},
|
||||
onManageTokensClick = { accountId ->
|
||||
model.innerWalletRouter.openManageTokensScreen(
|
||||
accountId = accountId,
|
||||
source = AppRoute.ManageTokens.Source.WALLET,
|
||||
)
|
||||
},
|
||||
),
|
||||
portfolioSelectorComponentFactory = portfolioSelectorComponentFactory,
|
||||
)
|
||||
}
|
||||
is WalletDialogConfig.OrganizeTokens -> {
|
||||
OrganizeTokensComponent(
|
||||
appComponentContext = childByContext(componentContext),
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBot
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.OpenBottomSheetTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletCurrencyActionsConverter
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
|
||||
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
|
@ -112,6 +113,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
private val yieldSupplySetShouldShowMainPromoUseCase: YieldSupplySetShouldShowMainPromoUseCase,
|
||||
private val tokenListAnalyticsSender: TokenListAnalyticsSender,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
private val walletFeatureToggles: WalletFeatureToggles,
|
||||
) : BaseWalletClickIntents(), WalletContentClickIntents {
|
||||
|
||||
override fun onDetailsClick() {
|
||||
|
|
@ -119,7 +121,12 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onOrganizeTokensClick() {
|
||||
router.openOrganizeTokensScreen(userWalletId = stateHolder.getSelectedWalletId())
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
if (walletFeatureToggles.isAddAndManageTokensEnabled) {
|
||||
router.openAddAndManageBottomSheet(userWalletId = userWalletId)
|
||||
} else {
|
||||
router.openOrganizeTokensScreen(userWalletId = userWalletId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDismissMarketsTooltip() {
|
||||
|
|
|
|||
|
|
@ -14,4 +14,7 @@ internal class DefaultWalletFeatureToggles @Inject constructor(
|
|||
|
||||
override val isMainScreenQrScanningEnabled: Boolean
|
||||
get() = featureToggles.isFeatureEnabled(FeatureToggles.MAIN_SCREEN_QR_SCANNING_ENABLED)
|
||||
|
||||
override val isAddAndManageTokensEnabled: Boolean
|
||||
get() = featureToggles.isFeatureEnabled(FeatureToggles.ADD_AND_MANAGE_TOKENS_ENABLED)
|
||||
}
|
||||
|
|
@ -91,6 +91,8 @@ internal object WalletScreenPreviewDataLegacy {
|
|||
),
|
||||
),
|
||||
organizeTokensButtonConfig = WalletTokensListState.OrganizeTokensButtonConfig(
|
||||
textRes = R.string.organize_tokens_title,
|
||||
iconRes = R.drawable.ic_filter_24,
|
||||
isEnabled = true,
|
||||
onClick = {},
|
||||
),
|
||||
|
|
@ -119,6 +121,8 @@ internal object WalletScreenPreviewDataLegacy {
|
|||
),
|
||||
),
|
||||
organizeTokensButtonConfig = WalletTokensListState.OrganizeTokensButtonConfig(
|
||||
textRes = R.string.organize_tokens_title,
|
||||
iconRes = R.drawable.ic_filter_24,
|
||||
isEnabled = true,
|
||||
onClick = {},
|
||||
),
|
||||
|
|
@ -149,6 +153,8 @@ internal object WalletScreenPreviewDataLegacy {
|
|||
),
|
||||
),
|
||||
organizeTokensButtonConfig = WalletTokensListState.OrganizeTokensButtonConfig(
|
||||
textRes = R.string.organize_tokens_title,
|
||||
iconRes = R.drawable.ic_filter_24,
|
||||
isEnabled = true,
|
||||
onClick = {},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import javax.inject.Inject
|
|||
|
||||
/** Default implementation of wallet feature router */
|
||||
@ModelScoped
|
||||
@Suppress("TooManyFunctions")
|
||||
internal class DefaultWalletRouter @Inject constructor(
|
||||
private val router: AppRouter,
|
||||
private val urlOpener: UrlOpener,
|
||||
|
|
@ -66,14 +67,20 @@ internal class DefaultWalletRouter @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
override fun openManageTokensScreen(accountId: AccountId) {
|
||||
override fun openManageTokensScreen(accountId: AccountId, source: AppRoute.ManageTokens.Source) {
|
||||
val route = AppRoute.ManageTokens(
|
||||
source = AppRoute.ManageTokens.Source.ACCOUNT,
|
||||
source = source,
|
||||
accountId = accountId,
|
||||
)
|
||||
router.push(route)
|
||||
}
|
||||
|
||||
override fun openAddAndManageBottomSheet(userWalletId: UserWalletId) {
|
||||
dialogNavigation.activate(
|
||||
configuration = WalletDialogConfig.AddAndManage(userWalletId = userWalletId),
|
||||
)
|
||||
}
|
||||
|
||||
override fun openOnboardingScreen(scanResponse: ScanResponse, continueBackup: Boolean) {
|
||||
router.push(
|
||||
AppRoute.Onboarding(
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import kotlinx.coroutines.flow.SharedFlow
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Stable
|
||||
@Suppress("TooManyFunctions")
|
||||
internal interface InnerWalletRouter {
|
||||
|
||||
val dialogNavigation: SlotNavigation<WalletDialogConfig>
|
||||
|
|
@ -47,7 +48,13 @@ internal interface InnerWalletRouter {
|
|||
fun openDetailsScreen(selectedWalletId: UserWalletId)
|
||||
|
||||
/** Open manage tokens screen */
|
||||
fun openManageTokensScreen(accountId: AccountId)
|
||||
fun openManageTokensScreen(
|
||||
accountId: AccountId,
|
||||
source: AppRoute.ManageTokens.Source = AppRoute.ManageTokens.Source.ACCOUNT,
|
||||
)
|
||||
|
||||
/** Open add and manage tokens bottom sheet */
|
||||
fun openAddAndManageBottomSheet(userWalletId: UserWalletId)
|
||||
|
||||
/** Open onboarding screen */
|
||||
fun openOnboardingScreen(scanResponse: ScanResponse, continueBackup: Boolean = false)
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@ internal sealed interface WalletDialogConfig {
|
|||
@Serializable
|
||||
data class KycRejected(val walletId: UserWalletId, val customerId: String) : WalletDialogConfig
|
||||
|
||||
@Serializable
|
||||
data class AddAndManage(val userWalletId: UserWalletId) : WalletDialogConfig
|
||||
|
||||
@Serializable
|
||||
data class OrganizeTokens(val userWalletId: UserWalletId) : WalletDialogConfig
|
||||
|
||||
|
|
|
|||
|
|
@ -49,5 +49,10 @@ internal sealed class WalletTokensListState {
|
|||
}
|
||||
}
|
||||
|
||||
data class OrganizeTokensButtonConfig(val isEnabled: Boolean, val onClick: () -> Unit)
|
||||
data class OrganizeTokensButtonConfig(
|
||||
val textRes: Int,
|
||||
val iconRes: Int,
|
||||
val isEnabled: Boolean,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ internal class SetTokenListTransformer(
|
|||
private val shouldShowMainPromo: Boolean,
|
||||
private val isAccountsModeEnabled: Boolean,
|
||||
private val isRedesignEnabled: Boolean,
|
||||
private val isAddAndManageTokensEnabled: Boolean,
|
||||
) : WalletStateTransformer(userWallet.walletId) {
|
||||
|
||||
private val tangemPayConverter by lazy {
|
||||
|
|
@ -105,6 +106,7 @@ internal class SetTokenListTransformer(
|
|||
yieldModuleApyMap = yieldSupplyApyMap,
|
||||
stakingAvailabilityMap = stakingAvailabilityMap,
|
||||
shouldShowMainPromo = shouldShowMainPromo,
|
||||
isAddAndManageTokensEnabled = isAddAndManageTokensEnabled,
|
||||
).convert(value = this)
|
||||
}
|
||||
|
||||
|
|
@ -137,6 +139,7 @@ internal class SetTokenListTransformer(
|
|||
shouldShowMainPromo = shouldShowMainPromo,
|
||||
isAccountsModeEnabled = isAccountsModeEnabled,
|
||||
expandedAccounts = params.expandedAccounts,
|
||||
isAddAndManageTokensEnabled = isAddAndManageTokensEnabled,
|
||||
).convert(value = params.accountList)
|
||||
}
|
||||
}
|
||||
|
|
@ -41,6 +41,7 @@ internal class TokenListStateConverter(
|
|||
private val yieldModuleApyMap: Map<String, BigDecimal>,
|
||||
private val stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability>,
|
||||
private val shouldShowMainPromo: Boolean,
|
||||
private val isAddAndManageTokensEnabled: Boolean,
|
||||
) : Converter<WalletTokensListState, WalletTokensListState> {
|
||||
|
||||
private val yieldSupplyPromoBannerConverter = YieldSupplyPromoBannerConverter(
|
||||
|
|
@ -157,6 +158,8 @@ internal class TokenListStateConverter(
|
|||
}
|
||||
return if (currenciesSize > 1 && !isSingleCurrencyWalletWithToken()) {
|
||||
WalletOrganizeTokensButtonConfig(
|
||||
textRes = organizeButtonTextRes(),
|
||||
iconRes = organizeButtonIconRes(),
|
||||
isEnabled = tokenList.totalFiatBalance !is TotalFiatBalance.Loading,
|
||||
onClick = clickIntents::onOrganizeTokensClick,
|
||||
)
|
||||
|
|
@ -168,6 +171,8 @@ internal class TokenListStateConverter(
|
|||
private fun getOrganizeTokensButtonStateV2(accountList: AccountStatusList): WalletOrganizeTokensButtonConfig? {
|
||||
return if (accountList.flattenCurrencies().size > 1 && !isSingleCurrencyWalletWithToken()) {
|
||||
WalletOrganizeTokensButtonConfig(
|
||||
textRes = organizeButtonTextRes(),
|
||||
iconRes = organizeButtonIconRes(),
|
||||
isEnabled = accountList.totalFiatBalance !is TotalFiatBalance.Loading,
|
||||
onClick = clickIntents::onOrganizeTokensClick,
|
||||
)
|
||||
|
|
@ -176,6 +181,18 @@ internal class TokenListStateConverter(
|
|||
}
|
||||
}
|
||||
|
||||
private fun organizeButtonTextRes(): Int = if (isAddAndManageTokensEnabled) {
|
||||
R.string.main_add_and_manage_tokens
|
||||
} else {
|
||||
R.string.organize_tokens_title
|
||||
}
|
||||
|
||||
private fun organizeButtonIconRes(): Int = if (isAddAndManageTokensEnabled) {
|
||||
R.drawable.ic_filter_default_24
|
||||
} else {
|
||||
R.drawable.ic_filter_24
|
||||
}
|
||||
|
||||
private fun isSingleCurrencyWalletWithToken(): Boolean {
|
||||
return selectedWallet is UserWallet.Cold &&
|
||||
selectedWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ internal class WalletTokensListUMConverter(
|
|||
private val isAccountsModeEnabled: Boolean,
|
||||
private val expandedAccounts: Set<AccountId>,
|
||||
private val stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability>,
|
||||
private val isAddAndManageTokensEnabled: Boolean,
|
||||
shouldShowMainPromo: Boolean,
|
||||
) : Converter<AccountStatusList, WalletTokensListUM> {
|
||||
|
||||
|
|
@ -164,15 +165,25 @@ internal class WalletTokensListUMConverter(
|
|||
}
|
||||
|
||||
private fun getOrganizeButtonUM(accountList: AccountStatusList): TangemButtonUM? {
|
||||
val textRes = if (isAddAndManageTokensEnabled) {
|
||||
R.string.main_add_and_manage_tokens
|
||||
} else {
|
||||
R.string.organize_tokens_title
|
||||
}
|
||||
val iconRes = if (isAddAndManageTokensEnabled) {
|
||||
R.drawable.ic_filter_default_24
|
||||
} else {
|
||||
R.drawable.ic_filter_24
|
||||
}
|
||||
return if (accountList.flattenCurrencies().size > 1 && !selectedWallet.isSingleWalletWithToken()) {
|
||||
TangemButtonUM(
|
||||
text = resourceReference(R.string.organize_tokens_title),
|
||||
text = resourceReference(textRes),
|
||||
isEnabled = accountList.totalFiatBalance !is TotalFiatBalance.Loading,
|
||||
size = TangemButtonSize.X9,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
type = TangemButtonType.PrimaryInverse,
|
||||
tangemIconUM = TangemIconUM.Icon(
|
||||
iconRes = R.drawable.ic_filter_default_24,
|
||||
iconRes = iconRes,
|
||||
tintReference = {
|
||||
if (accountList.totalFiatBalance !is TotalFiatBalance.Loading) {
|
||||
TangemTheme.colors2.graphic.neutral.primary
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.tangem.domain.yield.supply.usecase.YieldSupplyGetShouldShowMainPromoU
|
|||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
|
||||
import com.tangem.utils.coroutines.combine7
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
|
|
@ -39,8 +40,12 @@ internal class AccountListSubscriber @AssistedInject constructor(
|
|||
private val stakingAvailabilityListUseCase: StakingAvailabilityListUseCase,
|
||||
private val yieldSupplyGetShouldShowMainPromoUseCase: YieldSupplyGetShouldShowMainPromoUseCase,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
private val walletFeatureToggles: WalletFeatureToggles,
|
||||
) : BasicAccountListSubscriber() {
|
||||
|
||||
override val isAddAndManageTokensEnabled: Boolean
|
||||
get() = walletFeatureToggles.isAddAndManageTokensEnabled
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<*> = combine7(
|
||||
flow1 = getAccountStatusListFlow(),
|
||||
flow2 = getAppCurrencyFlow(),
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() {
|
|||
abstract val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase
|
||||
abstract val stateController: WalletStateController
|
||||
abstract val clickIntents: WalletClickIntents
|
||||
abstract val isAddAndManageTokensEnabled: Boolean
|
||||
|
||||
override val singleAccountStatusListSupplier: SingleAccountStatusListSupplier
|
||||
get() = accountDependencies.singleAccountStatusListSupplier
|
||||
|
|
@ -105,6 +106,7 @@ internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() {
|
|||
shouldShowMainPromo = shouldShowMainPromo,
|
||||
isAccountsModeEnabled = isAccountMode,
|
||||
isRedesignEnabled = true,
|
||||
isAddAndManageTokensEnabled = isAddAndManageTokensEnabled,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -168,6 +170,7 @@ internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() {
|
|||
shouldShowMainPromo = shouldShowMainPromo,
|
||||
isAccountsModeEnabled = false,
|
||||
isRedesignEnabled = false,
|
||||
isAddAndManageTokensEnabled = isAddAndManageTokensEnabled,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.domain.models.wallet.UserWallet
|
|||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
|
@ -18,8 +19,12 @@ internal class SingleWalletSubscriber @AssistedInject constructor(
|
|||
override val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
override val stateController: WalletStateController,
|
||||
override val clickIntents: WalletClickIntents,
|
||||
private val walletFeatureToggles: WalletFeatureToggles,
|
||||
) : BasicAccountListSubscriber() {
|
||||
|
||||
override val isAddAndManageTokensEnabled: Boolean
|
||||
get() = walletFeatureToggles.isAddAndManageTokensEnabled
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<Unit> = combine(
|
||||
flow = getAccountStatusListFlow(),
|
||||
flow2 = getAppCurrencyFlow(),
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.domain.models.wallet.UserWallet
|
|||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
|
@ -19,8 +20,12 @@ internal class SingleWalletWithTokenSubscriberLegacy @AssistedInject constructor
|
|||
override val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
override val stateController: WalletStateController,
|
||||
override val clickIntents: WalletClickIntents,
|
||||
private val walletFeatureToggles: WalletFeatureToggles,
|
||||
) : BasicAccountListSubscriber() {
|
||||
|
||||
override val isAddAndManageTokensEnabled: Boolean
|
||||
get() = walletFeatureToggles.isAddAndManageTokensEnabled
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<Unit> = combine(
|
||||
flow = getAccountStatusListFlow(),
|
||||
flow2 = getAppCurrencyFlow(),
|
||||
|
|
|
|||
|
|
@ -726,17 +726,13 @@ private fun WalletSnackbarHost(
|
|||
}
|
||||
|
||||
internal fun LazyListScope.organizeTokens(state: WalletState, itemModifier: Modifier) {
|
||||
(state as? WalletState.MultiCurrency)?.let {
|
||||
(state.tokensListState as? WalletTokensListState.ContentState)?.let {
|
||||
it.organizeTokensButtonConfig?.let { config ->
|
||||
organizeTokensButton(
|
||||
modifier = itemModifier,
|
||||
isEnabled = config.isEnabled,
|
||||
onClick = config.onClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
val multiCurrencyState = state as? WalletState.MultiCurrency ?: return
|
||||
val contentState = multiCurrencyState.tokensListState as? WalletTokensListState.ContentState ?: return
|
||||
val config = contentState.organizeTokensButtonConfig ?: return
|
||||
organizeTokensButton(
|
||||
modifier = itemModifier,
|
||||
config = config,
|
||||
)
|
||||
}
|
||||
|
||||
internal fun LazyListScope.nftCollections(state: WalletState, itemModifier: Modifier) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
|||
import com.tangem.core.ui.components.buttons.actions.RoundedActionButton
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.test.MainScreenTestTags
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState
|
||||
|
||||
private const val ORGANIZE_BUTTON_CONTENT_TYPE = "OrganizeTokensButton"
|
||||
|
||||
|
|
@ -20,18 +20,17 @@ private const val ORGANIZE_BUTTON_CONTENT_TYPE = "OrganizeTokensButton"
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal fun LazyListScope.organizeTokensButton(
|
||||
isEnabled: Boolean,
|
||||
onClick: () -> Unit,
|
||||
config: WalletTokensListState.OrganizeTokensButtonConfig,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
item(key = ORGANIZE_BUTTON_CONTENT_TYPE, contentType = ORGANIZE_BUTTON_CONTENT_TYPE) {
|
||||
RoundedActionButton(
|
||||
modifier = modifier.testTag(MainScreenTestTags.ORGANIZE_TOKENS_BUTTON),
|
||||
config = ActionButtonConfig(
|
||||
text = resourceReference(id = R.string.organize_tokens_title),
|
||||
iconResId = R.drawable.ic_filter_24,
|
||||
onClick = onClick,
|
||||
isEnabled = isEnabled,
|
||||
text = resourceReference(id = config.textRes),
|
||||
iconResId = config.iconRes,
|
||||
onClick = config.onClick,
|
||||
isEnabled = config.isEnabled,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue