Updated on 2026-08-14
This commit is contained in:
parent
5f6767abaf
commit
74e4b644f7
42 changed files with 83 additions and 3490 deletions
|
|
@ -8,13 +8,11 @@ import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation
|
|||
import com.arkivanov.decompose.router.stack.ChildStack
|
||||
import com.arkivanov.decompose.router.stack.StackNavigation
|
||||
import com.arkivanov.decompose.router.stack.childStack
|
||||
import com.arkivanov.decompose.router.stack.pop
|
||||
import com.arkivanov.decompose.router.stack.pushNew
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.feature.wallet.child.organizetokens.OrganizeTokensComponentLegacy
|
||||
import com.tangem.feature.wallet.child.wallet.WalletComponent
|
||||
import com.tangem.feature.wallet.navigation.WalletRoute
|
||||
import com.tangem.features.wallet.WalletEntryComponent
|
||||
|
|
@ -24,7 +22,7 @@ import dagger.assisted.AssistedInject
|
|||
|
||||
internal class DefaultWalletEntryComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
@Assisted params: Unit,
|
||||
@Assisted val params: Unit,
|
||||
walletComponentFactory: WalletComponent.Factory,
|
||||
) : WalletEntryComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
|
|
@ -40,11 +38,6 @@ internal class DefaultWalletEntryComponent @AssistedInject constructor(
|
|||
appComponentContext = childByContext(context),
|
||||
navigate = { navigation.pushNew(it) },
|
||||
)
|
||||
is WalletRoute.OrganizeTokens -> OrganizeTokensComponentLegacy(
|
||||
appComponentContext = childByContext(context),
|
||||
params = OrganizeTokensComponentLegacy.Params(route.userWalletId),
|
||||
onBack = { navigation.pop() },
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -10,12 +10,10 @@ 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.core.ui.res.LocalRedesignEnabled
|
||||
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.feature.wallet.child.managetokens.ui.AddAndManageBottomSheetContentLegacy
|
||||
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorComponent
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
|
||||
|
|
@ -53,21 +51,12 @@ internal class AddAndManageBottomSheetComponent(
|
|||
val portfolioSelectorSlot by portfolioSelectorSlot.subscribeAsState()
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
|
||||
if (LocalRedesignEnabled.current) {
|
||||
AddAndManageBottomSheetContent(
|
||||
onAddTokensClick = model::onAddTokensClick,
|
||||
shouldShowOrganizeButton = state.shouldShowOrganize,
|
||||
onOrganizeTokensClick = model::onOrganizeTokensClick,
|
||||
onDismiss = ::dismiss,
|
||||
)
|
||||
} else {
|
||||
AddAndManageBottomSheetContentLegacy(
|
||||
onAddTokensClick = model::onAddTokensClick,
|
||||
shouldShowOrganizeButton = state.shouldShowOrganize,
|
||||
onOrganizeTokensClick = model::onOrganizeTokensClick,
|
||||
onDismiss = ::dismiss,
|
||||
)
|
||||
}
|
||||
|
||||
portfolioSelectorSlot.child?.instance?.BottomSheet()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,168 +0,0 @@
|
|||
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.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
|
||||
import com.tangem.core.res.R as ResR
|
||||
|
||||
@Composable
|
||||
internal fun AddAndManageBottomSheetContentLegacy(
|
||||
onAddTokensClick: () -> Unit,
|
||||
shouldShowOrganizeButton: Boolean,
|
||||
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,
|
||||
shouldShowOrganizeButton = shouldShowOrganizeButton,
|
||||
onOrganizeTokensClick = onOrganizeTokensClick,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AddAndManageContent(
|
||||
onAddTokensClick: () -> Unit,
|
||||
shouldShowOrganizeButton: Boolean,
|
||||
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 = if (shouldShowOrganizeButton) 1 else 0,
|
||||
addDefaultPadding = false,
|
||||
backgroundColor = TangemTheme.colors.background.action,
|
||||
),
|
||||
)
|
||||
if (shouldShowOrganizeButton) {
|
||||
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 = {},
|
||||
shouldShowOrganizeButton = true,
|
||||
onOrganizeTokensClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
package com.tangem.feature.wallet.child.organizetokens
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.child.organizetokens.model.OrganizeTokensModelLegacy
|
||||
import com.tangem.feature.wallet.child.organizetokens.ui.OrganizeTokensScreen
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal class OrganizeTokensComponentLegacy(
|
||||
appComponentContext: AppComponentContext,
|
||||
params: Params,
|
||||
onBack: () -> Unit,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: OrganizeTokensModelLegacy = getOrCreateModel(params)
|
||||
|
||||
init {
|
||||
componentScope.launch {
|
||||
model.onBack.collect { onBack() }
|
||||
}
|
||||
}
|
||||
|
||||
data class Params(val userWalletId: UserWalletId)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val uiState by model.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
OrganizeTokensScreen(state = uiState)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@ package com.tangem.feature.wallet.child.organizetokens.di
|
|||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.feature.wallet.child.organizetokens.model.OrganizeTokensModel
|
||||
import com.tangem.feature.wallet.child.organizetokens.model.OrganizeTokensModelLegacy
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -14,11 +13,6 @@ import dagger.multibindings.IntoMap
|
|||
@InstallIn(ModelComponent::class)
|
||||
internal interface OrganizeTokensModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(OrganizeTokensModelLegacy::class)
|
||||
fun bindOrganizeTokensModelLegacy(model: OrganizeTokensModelLegacy): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(OrganizeTokensModel::class)
|
||||
|
|
|
|||
|
|
@ -1,224 +0,0 @@
|
|||
package com.tangem.feature.wallet.child.organizetokens.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
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.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.account.status.usecase.ApplyTokenListSortingUseCase
|
||||
import com.tangem.domain.account.status.usecase.ToggleTokenListGroupingUseCase
|
||||
import com.tangem.domain.account.status.usecase.ToggleTokenListSortingUseCase
|
||||
import com.tangem.domain.account.status.usecase.IsAccountsModeEnabledUseCase
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.models.TokensSortType
|
||||
import com.tangem.feature.wallet.child.organizetokens.OrganizeTokensComponentLegacy
|
||||
import com.tangem.feature.wallet.child.organizetokens.analytics.PortfolioOrganizeTokensAnalyticsEvent
|
||||
import com.tangem.feature.wallet.child.organizetokens.entity.OrganizeTokensState
|
||||
import com.tangem.feature.wallet.child.organizetokens.model.dnd.DragAndDropAdapterLegacy
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Stable
|
||||
@ModelScoped
|
||||
internal class OrganizeTokensModelLegacy @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
private val toggleTokenListGroupingUseCase: ToggleTokenListGroupingUseCase,
|
||||
private val toggleTokenListSortingUseCase: ToggleTokenListSortingUseCase,
|
||||
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
|
||||
) : Model(), OrganizeTokensIntents {
|
||||
|
||||
private val selectedAppCurrencyFlow = createSelectedAppCurrencyFlow()
|
||||
|
||||
private var isBalanceHidden = true
|
||||
|
||||
@Suppress("PropertyUsedBeforeDeclaration")
|
||||
private val dragAndDropAdapterLegacy = DragAndDropAdapterLegacy(
|
||||
tokenListUMProvider = Provider { uiState.value.tokenListUM },
|
||||
)
|
||||
|
||||
private val stateHolder = OrganizeTokensStateHolder(
|
||||
intents = this,
|
||||
dragAndDropAdapterLegacy = dragAndDropAdapterLegacy,
|
||||
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
|
||||
)
|
||||
|
||||
private val userWalletId = paramsContainer.require<OrganizeTokensComponentLegacy.Params>().userWalletId
|
||||
|
||||
private var cachedAccountStatusList: AccountStatusList? = null
|
||||
|
||||
private var isAccountsModeEnabled: Boolean = false
|
||||
|
||||
val uiState: StateFlow<OrganizeTokensState> = stateHolder.stateFlow
|
||||
|
||||
val onBack = MutableSharedFlow<Unit>()
|
||||
|
||||
init {
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.ScreenOpened())
|
||||
|
||||
getBalanceHidingSettingsUseCase()
|
||||
.onEach { balanceHidingSettings ->
|
||||
isBalanceHidden = balanceHidingSettings.isBalanceHidden
|
||||
stateHolder.updateHiddenState(isBalanceHidden)
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
|
||||
bootstrapTokenList()
|
||||
bootstrapDragAndDropUpdates()
|
||||
}
|
||||
|
||||
override fun onBackClick() {
|
||||
modelScope.launch { onBack.emit(Unit) }
|
||||
}
|
||||
|
||||
override fun onSortClick() {
|
||||
val list = cachedAccountStatusList ?: return
|
||||
if (list.sortType == TokensSortType.BALANCE) return
|
||||
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.ByBalance())
|
||||
|
||||
modelScope.launch {
|
||||
toggleTokenListSortingUseCase(list).fold(
|
||||
ifLeft = stateHolder::updateStateWithError,
|
||||
ifRight = { accountStatusList ->
|
||||
stateHolder.updateStateAfterTokenListSorting(accountStatusList, isAccountsModeEnabled)
|
||||
cachedAccountStatusList = accountStatusList
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGroupClick() {
|
||||
val list = cachedAccountStatusList ?: return
|
||||
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.Group())
|
||||
|
||||
modelScope.launch {
|
||||
toggleTokenListGroupingUseCase(list).fold(
|
||||
ifLeft = stateHolder::updateStateWithError,
|
||||
ifRight = { accountStatusList ->
|
||||
stateHolder.updateStateAfterTokenListSorting(accountStatusList, isAccountsModeEnabled)
|
||||
cachedAccountStatusList = accountStatusList
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onApplyClick() {
|
||||
modelScope.launch {
|
||||
stateHolder.updateStateToDisplayProgress()
|
||||
val resolver = CryptoCurrenciesIdsResolver()
|
||||
val isSortedByBalance = uiState.value.header.isSortedByBalance
|
||||
val tokensListUM = uiState.value.tokenListUM
|
||||
|
||||
val isGroupedByNetwork = tokensListUM.isGrouped
|
||||
|
||||
sendAnalyticsEvent(
|
||||
isGroupedByNetwork = isGroupedByNetwork,
|
||||
isSortedByBalance = isSortedByBalance,
|
||||
)
|
||||
|
||||
val result = applyTokenListSortingUseCase(
|
||||
sortedTokensIdsByAccount = resolver.resolveLegacy(tokensListUM, cachedAccountStatusList),
|
||||
isGroupedByNetwork = isGroupedByNetwork,
|
||||
isSortedByBalance = isSortedByBalance,
|
||||
)
|
||||
|
||||
result.fold(
|
||||
ifLeft = stateHolder::updateStateWithError,
|
||||
ifRight = {
|
||||
modelScope.launch { onBack.emit(Unit) }
|
||||
stateHolder.updateStateToHideProgress()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCancelClick() {
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.Cancel())
|
||||
|
||||
modelScope.launch { onBack.emit(Unit) }
|
||||
}
|
||||
|
||||
private fun bootstrapTokenList() {
|
||||
modelScope.launch {
|
||||
val accountList = singleAccountStatusListSupplier.getSyncOrNull(
|
||||
SingleAccountStatusListProducer.Params(userWalletId),
|
||||
) ?: return@launch
|
||||
|
||||
isAccountsModeEnabled = isAccountsModeEnabledUseCase.invokeSync()
|
||||
|
||||
stateHolder.updateStateWithAccountList(
|
||||
accountStatusList = accountList,
|
||||
isAccountsModeEnabled = isAccountsModeEnabled,
|
||||
)
|
||||
|
||||
cachedAccountStatusList = accountList
|
||||
}
|
||||
}
|
||||
|
||||
private fun bootstrapDragAndDropUpdates() {
|
||||
dragAndDropAdapterLegacy.dragAndDropUpdates
|
||||
.distinctUntilChanged()
|
||||
.onEach { (type, updatedListState) ->
|
||||
disableSortingByBalanceIfListChanged(type)
|
||||
|
||||
stateHolder.updateStateWithManualSorting(updatedListState)
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun disableSortingByBalanceIfListChanged(dragOperationType: DragAndDropAdapterLegacy.DragOperation.Type) {
|
||||
if (dragOperationType !is DragAndDropAdapterLegacy.DragOperation.Type.End) return
|
||||
|
||||
if (uiState.value.header.isSortedByBalance && dragOperationType.isItemsOrderChanged) {
|
||||
cachedAccountStatusList = cachedAccountStatusList?.copy(sortType = TokensSortType.NONE)
|
||||
stateHolder.disableSortingByBalance()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSelectedAppCurrencyFlow(): StateFlow<AppCurrency> {
|
||||
return getSelectedAppCurrencyUseCase()
|
||||
.map { maybeAppCurrency ->
|
||||
maybeAppCurrency.getOrElse { AppCurrency.Default }
|
||||
}
|
||||
.stateIn(
|
||||
scope = modelScope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = AppCurrency.Default,
|
||||
)
|
||||
}
|
||||
|
||||
private fun sendAnalyticsEvent(isGroupedByNetwork: Boolean, isSortedByBalance: Boolean) {
|
||||
analyticsEventsHandler.send(
|
||||
PortfolioOrganizeTokensAnalyticsEvent.Apply(
|
||||
grouping = if (isGroupedByNetwork) {
|
||||
AnalyticsParam.OnOffState.On
|
||||
} else {
|
||||
AnalyticsParam.OnOffState.Off
|
||||
},
|
||||
organizeSortType = if (isSortedByBalance) {
|
||||
AnalyticsParam.OrganizeSortType.ByBalance
|
||||
} else {
|
||||
AnalyticsParam.OrganizeSortType.Manually
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.feature.wallet.child.tokenActions
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -8,18 +7,8 @@ import androidx.compose.ui.platform.LocalDensity
|
|||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.ui.components.SimpleSettingsRow
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.getDefaultRowColors
|
||||
import com.tangem.core.ui.components.getWarningRowColors
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.feature.wallet.child.tokenActions.TokenActionsComponent.Params
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.fastForEach
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
|
@ -50,35 +39,7 @@ internal class DefaultTokenActionsComponent @AssistedInject constructor(
|
|||
|
||||
@Composable
|
||||
override fun BottomSheet() {
|
||||
if (!LocalRedesignEnabled.current) {
|
||||
TangemBottomSheet<TangemBottomSheetConfigContent.Empty>(
|
||||
containerColor = TangemTheme.colors.background.primary,
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = ::dismiss,
|
||||
content = TangemBottomSheetConfigContent.Empty,
|
||||
),
|
||||
) {
|
||||
Column {
|
||||
params.actions.fastForEach { action ->
|
||||
if (action.isEnabled) {
|
||||
val rowColors = if (action.isWarning) {
|
||||
getWarningRowColors()
|
||||
} else {
|
||||
getDefaultRowColors()
|
||||
}
|
||||
SimpleSettingsRow(
|
||||
title = action.text.resolveReference(),
|
||||
icon = action.iconResId,
|
||||
enabled = action.isEnabled,
|
||||
rowColors = rowColors,
|
||||
onItemsClick = action.onClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// No-op: token actions are rendered via Content() in the redesigned UI
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -87,7 +48,6 @@ internal class DefaultTokenActionsComponent @AssistedInject constructor(
|
|||
dismiss()
|
||||
} else {
|
||||
val isBalanceHidden by isBalanceHiddenFlow.collectAsStateWithLifecycle()
|
||||
if (LocalRedesignEnabled.current) {
|
||||
val offset = with(LocalDensity.current) {
|
||||
DpOffset(params.offsetX.toDp(), params.offsetY.toDp())
|
||||
}
|
||||
|
|
@ -101,7 +61,6 @@ internal class DefaultTokenActionsComponent @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : TokenActionsComponent.Factory {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import com.tangem.core.decompose.context.AppComponentContext
|
|||
import com.tangem.core.decompose.context.child
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
|
||||
import com.tangem.core.ui.components.haze.hazeEffectTangem
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
|
|
@ -30,7 +29,6 @@ import com.tangem.feature.wallet.child.tokenActions.TokenActionsComponent
|
|||
import com.tangem.feature.wallet.child.wallet.model.WalletModel
|
||||
import com.tangem.feature.wallet.navigation.WalletRoute
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen2
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.KycRejectedComponent
|
||||
import com.tangem.feature.walletsettings.component.RenameWalletComponent
|
||||
|
|
@ -71,7 +69,6 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
private val tokenActionsComponentFactory: TokenActionsComponent.Factory,
|
||||
private val portfolioSelectorComponentFactory: PortfolioSelectorComponent.Factory,
|
||||
private val manageFundsComponentFactory: ManageFundsComponent.Factory,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: WalletModel = getOrCreateModel()
|
||||
|
|
@ -295,50 +292,28 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
var headerSize by remember { mutableStateOf(0.dp) }
|
||||
val dialog by dialog.subscribeAsState()
|
||||
val uiState by model.uiState.collectAsStateWithLifecycle()
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
WalletScreen2(
|
||||
state = uiState,
|
||||
promoBannersBlockComponent = promoBannersBlockComponent,
|
||||
tangemPayComponent = tangemPayMainBlockComponent,
|
||||
virtualAccountComponent = virtualAccountMainBlockComponent,
|
||||
modifier = modifier,
|
||||
bottomSheetContent = { onExpandSheet ->
|
||||
BottomSheetContent(
|
||||
bottomSheetState = bottomSheetState,
|
||||
onHeaderSizeChange = { headerSize = it },
|
||||
onExpandSheet = onExpandSheet,
|
||||
modifier = modifier,
|
||||
modifier = Modifier,
|
||||
)
|
||||
},
|
||||
bottomSheetHeaderHeightProvider = { headerSize },
|
||||
onBottomSheetStateChange = { bottomSheetState.value = it },
|
||||
)
|
||||
} else {
|
||||
WalletScreen(
|
||||
state = uiState,
|
||||
promoBannersBlockComponent = promoBannersBlockComponent,
|
||||
tangemPayComponent = tangemPayMainBlockComponent,
|
||||
virtualAccountComponent = virtualAccountMainBlockComponent,
|
||||
bottomSheetContent = { onExpandSheet ->
|
||||
BottomSheetContent(
|
||||
bottomSheetState = bottomSheetState,
|
||||
onHeaderSizeChange = { headerSize = it },
|
||||
onExpandSheet = onExpandSheet,
|
||||
modifier = modifier,
|
||||
)
|
||||
},
|
||||
bottomSheetHeaderHeightProvider = { headerSize },
|
||||
onBottomSheetStateChange = { bottomSheetState.value = it },
|
||||
)
|
||||
}
|
||||
|
||||
when (val dialog = dialog.child?.instance) {
|
||||
is ComposableDialogComponent -> dialog.Dialog()
|
||||
is DefaultTokenActionsComponent -> {
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
dialog.Content(Modifier.hazeEffectTangem())
|
||||
} else {
|
||||
dialog.BottomSheet()
|
||||
}
|
||||
}
|
||||
is ComposableBottomSheetComponent -> dialog.BottomSheet()
|
||||
else -> {}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.tangem.feature.wallet.child.wallet.model.intents
|
|||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isLocked
|
||||
|
|
@ -19,7 +18,6 @@ import com.tangem.feature.wallet.presentation.wallet.domain.WalletContentFetcher
|
|||
import com.tangem.feature.wallet.presentation.wallet.domain.unwrap
|
||||
import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetRefreshStateTransformer
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
@ -47,7 +45,6 @@ internal class WalletClickIntents @Inject constructor(
|
|||
private val onrampStatusFactory: OnrampStatusFactory,
|
||||
private val tangemPayIntents: TangemPayClickIntentsImplementor,
|
||||
private val yieldSupplyApyUpdateUseCase: YieldSupplyApyUpdateUseCase,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : BaseWalletClickIntents(),
|
||||
WalletCardClickIntents by walletCardClickIntentsImplementor,
|
||||
|
|
@ -94,25 +91,10 @@ internal class WalletClickIntents @Inject constructor(
|
|||
}
|
||||
|
||||
fun onRefreshSwipe(showRefreshState: Boolean) {
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
when (stateController.getSelectedWalletUM()) {
|
||||
is WalletUM.Content -> refreshMultiCurrencyContent(showRefreshState)
|
||||
is WalletUM.Locked -> Unit
|
||||
}
|
||||
} else {
|
||||
when (stateController.getSelectedWallet()) {
|
||||
is WalletState.MultiCurrency.Content -> {
|
||||
refreshMultiCurrencyContent(showRefreshState)
|
||||
}
|
||||
is WalletState.SingleCurrency.Content,
|
||||
-> {
|
||||
refreshSingleCurrencyContent(showRefreshState)
|
||||
}
|
||||
is WalletState.MultiCurrency.Locked,
|
||||
is WalletState.SingleCurrency.Locked,
|
||||
-> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onReloadClick() {
|
||||
|
|
|
|||
|
|
@ -333,11 +333,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onNFTClick(userWallet: UserWallet) {
|
||||
val nftState = if (stateHolder.value.isRedesignEnabled) {
|
||||
stateHolder.getSelectedWalletUM().nftState
|
||||
} else {
|
||||
(stateHolder.getSelectedWallet() as? WalletState.MultiCurrency.Content)?.nftState
|
||||
}
|
||||
val nftState = stateHolder.getSelectedWalletUM().nftState
|
||||
when (nftState) {
|
||||
is WalletNFTItemUM.Content -> {
|
||||
analyticsEventHandler.send(
|
||||
|
|
|
|||
|
|
@ -598,21 +598,8 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
|
||||
private fun isMultiWalletTokensLoaded(): Boolean {
|
||||
return if (stateHolder.value.isRedesignEnabled) {
|
||||
val selectedWalletUM = stateHolder.getSelectedWalletUM() as? WalletUM.Content ?: return false
|
||||
selectedWalletUM.tokensListUM is WalletTokensListUM.Content
|
||||
} else {
|
||||
val selectedWallet = stateHolder.getSelectedWallet() as? WalletState.MultiCurrency.Content ?: return false
|
||||
when (selectedWallet.tokensListState) {
|
||||
is WalletTokensListState.ContentState.Content,
|
||||
is WalletTokensListState.ContentState.PortfolioContent,
|
||||
-> true
|
||||
WalletTokensListState.ContentState.Loading,
|
||||
WalletTokensListState.ContentState.Locked,
|
||||
WalletTokensListState.Empty,
|
||||
-> false
|
||||
}
|
||||
}
|
||||
return selectedWalletUM.tokensListUM is WalletTokensListUM.Content
|
||||
}
|
||||
|
||||
private fun onMultiWalletActionClick(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.feature.wallet.navigation
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
|
|
@ -8,7 +7,4 @@ internal sealed class WalletRoute {
|
|||
|
||||
@Serializable
|
||||
data object Wallet : WalletRoute()
|
||||
|
||||
@Serializable
|
||||
data class OrganizeTokens(val userWalletId: UserWalletId) : WalletRoute()
|
||||
}
|
||||
|
|
@ -224,7 +224,6 @@ internal object WalletScreenPreviewData {
|
|||
isHidingMode = false,
|
||||
showMarketsOnboarding = false,
|
||||
onDismissMarketsTooltip = {},
|
||||
isRedesignEnabled = true,
|
||||
)
|
||||
|
||||
val defaultAccountState = defaultState.copy(
|
||||
|
|
|
|||
|
|
@ -1,268 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.common.preview
|
||||
|
||||
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState
|
||||
import com.tangem.core.ui.components.token.AccountItemPreviewData
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.components.tokenlist.state.PortfolioItemContentUM
|
||||
import com.tangem.core.ui.components.tokenlist.state.PortfolioTokensListItemUM
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewDataLegacy.topBarConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.*
|
||||
import com.tangem.features.tangempay.entity.TangemPayMainUM
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
internal object WalletScreenPreviewDataLegacy {
|
||||
|
||||
private val buyButton = WalletManageButton.Buy(enabled = false, dimContent = true, onClick = {})
|
||||
private val sendButton = WalletManageButton.Send(enabled = false, dimContent = true, onClick = {})
|
||||
private val receiveButton = WalletManageButton.Receive(
|
||||
enabled = false,
|
||||
dimContent = true,
|
||||
onClick = {},
|
||||
onLongClick = null,
|
||||
)
|
||||
|
||||
private val tokenItemState = TokenItemState.Content(
|
||||
id = "1",
|
||||
iconState = CurrencyIconState.Locked,
|
||||
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "Bitcoin")),
|
||||
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "12 368,14 \$"),
|
||||
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "0,35853044 BTC"),
|
||||
subtitleState = TokenItemState.SubtitleState.CryptoPriceContent(
|
||||
price = "34 496,75 \$",
|
||||
priceChangePercent = "0,43 %",
|
||||
type = PriceChangeType.DOWN,
|
||||
),
|
||||
onItemClick = {},
|
||||
onItemLongClick = {},
|
||||
)
|
||||
|
||||
private val textContentTokensState = WalletTokensListState.ContentState.Content(
|
||||
items = persistentListOf(
|
||||
TokensListItemUM.GroupTitle(id = 111, text = stringReference("Network Bitcoin")),
|
||||
TokensListItemUM.Token(state = tokenItemState),
|
||||
TokensListItemUM.GroupTitle(id = 222, text = stringReference("Network Ethereum")),
|
||||
TokensListItemUM.Token(
|
||||
state = tokenItemState.copy(
|
||||
id = "2",
|
||||
titleState = TokenItemState.TitleState.Content(text = stringReference("Ethereum")),
|
||||
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "3 340,79 \$"),
|
||||
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "1,856660295 ETH"),
|
||||
subtitleState = TokenItemState.SubtitleState.CryptoPriceContent(
|
||||
price = "1 799,41 \$",
|
||||
priceChangePercent = "5,16 %",
|
||||
type = PriceChangeType.UP,
|
||||
),
|
||||
),
|
||||
),
|
||||
TokensListItemUM.Token(
|
||||
state = TokenItemState.Unreachable(
|
||||
id = "3",
|
||||
iconState = CurrencyIconState.Locked,
|
||||
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "Polygon")),
|
||||
onItemClick = {},
|
||||
onItemLongClick = {},
|
||||
),
|
||||
),
|
||||
TokensListItemUM.Token(
|
||||
state = tokenItemState.copy(
|
||||
id = "4",
|
||||
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "Shiba Inu")),
|
||||
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "48,64 \$"),
|
||||
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "6 200 220,00 SHIB"),
|
||||
subtitleState = TokenItemState.SubtitleState.CryptoPriceContent(
|
||||
price = "0.01 \$",
|
||||
priceChangePercent = "1,34 %",
|
||||
type = PriceChangeType.DOWN,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
organizeTokensButtonConfig = WalletTokensListState.OrganizeTokensButtonConfig(
|
||||
textRes = R.string.organize_tokens_title,
|
||||
iconRes = R.drawable.ic_filter_24,
|
||||
isEnabled = true,
|
||||
onClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
private val portfolioContentState = WalletTokensListState.ContentState.PortfolioContent(
|
||||
items = persistentListOf(
|
||||
TokensListItemUM.Portfolio(
|
||||
content = PortfolioItemContentUM.Tokens(
|
||||
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>()
|
||||
.toPersistentList(),
|
||||
),
|
||||
isExpanded = false,
|
||||
isCollapsable = true,
|
||||
tokenItemUM = AccountItemPreviewData.accountItem
|
||||
.copy(iconState = AccountItemPreviewData.accountLetterIcon),
|
||||
),
|
||||
TokensListItemUM.Portfolio(
|
||||
content = PortfolioItemContentUM.Tokens(
|
||||
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>()
|
||||
.toPersistentList(),
|
||||
),
|
||||
isExpanded = true,
|
||||
isCollapsable = true,
|
||||
tokenItemUM = AccountItemPreviewData.accountItem,
|
||||
),
|
||||
),
|
||||
organizeTokensButtonConfig = WalletTokensListState.OrganizeTokensButtonConfig(
|
||||
textRes = R.string.organize_tokens_title,
|
||||
iconRes = R.drawable.ic_filter_24,
|
||||
isEnabled = true,
|
||||
onClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
private val emptyPortfolioContentState = WalletTokensListState.ContentState.PortfolioContent(
|
||||
items = persistentListOf(
|
||||
TokensListItemUM.Portfolio(
|
||||
content = PortfolioItemContentUM.Tokens(
|
||||
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>()
|
||||
.toPersistentList(),
|
||||
),
|
||||
isExpanded = false,
|
||||
isCollapsable = true,
|
||||
tokenItemUM = AccountItemPreviewData.accountItem
|
||||
.copy(iconState = AccountItemPreviewData.accountLetterIcon),
|
||||
),
|
||||
TokensListItemUM.Portfolio(
|
||||
content = PortfolioItemContentUM.Empty(
|
||||
action = PortfolioItemContentUM.Empty.Action(
|
||||
text = resourceReference(id = R.string.onboarding_add_tokens),
|
||||
onClick = {},
|
||||
),
|
||||
),
|
||||
isExpanded = true,
|
||||
isCollapsable = true,
|
||||
tokenItemUM = AccountItemPreviewData.accountItem,
|
||||
),
|
||||
),
|
||||
organizeTokensButtonConfig = WalletTokensListState.OrganizeTokensButtonConfig(
|
||||
textRes = R.string.organize_tokens_title,
|
||||
iconRes = R.drawable.ic_filter_24,
|
||||
isEnabled = true,
|
||||
onClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
private val noteLockedCard by lazy {
|
||||
WalletCardState.LockedContent(
|
||||
id = UserWalletId(stringValue = "1"),
|
||||
title = "Note",
|
||||
additionalInfo = WalletAdditionalInfo(
|
||||
hideable = false,
|
||||
content = WalletAdditionalInfo.Content.Text(TextReference.Str("Locked")),
|
||||
),
|
||||
imageResId = R.drawable.ill_note_btc_120_106,
|
||||
dropDownItems = persistentListOf(),
|
||||
)
|
||||
}
|
||||
private val miltiUnreachableCard by lazy {
|
||||
WalletCardState.Content(
|
||||
id = UserWalletId(stringValue = "2"),
|
||||
title = "Wallet 1",
|
||||
additionalInfo = WalletAdditionalInfo(
|
||||
hideable = false,
|
||||
content = WalletAdditionalInfo.Content.Text(TextReference.Str("Seed phrase")),
|
||||
),
|
||||
imageResId = R.drawable.ill_wallet2_cards3_120_106,
|
||||
cardCount = 3,
|
||||
balance = DASH_SIGN,
|
||||
dropDownItems = persistentListOf(),
|
||||
isZeroBalance = false,
|
||||
isBalanceFlickering = false,
|
||||
)
|
||||
}
|
||||
private val multiWalletState by lazy {
|
||||
WalletState.MultiCurrency.Content(
|
||||
pullToRefreshConfig = PullToRefreshConfig(
|
||||
isRefreshing = false,
|
||||
onRefresh = {},
|
||||
),
|
||||
walletCardState = miltiUnreachableCard,
|
||||
buttons = persistentListOf(buyButton),
|
||||
warnings = persistentListOf(
|
||||
WalletNotification.Warning.SomeNetworksUnreachable,
|
||||
WalletNotification.FinishWalletActivation(
|
||||
type = WalletActivationBannerType.Attention,
|
||||
buttonsState = ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.hw_activation_need_finish),
|
||||
onClick = { },
|
||||
),
|
||||
isBackupExists = false,
|
||||
),
|
||||
),
|
||||
bottomSheetConfig = null,
|
||||
tokensListState = textContentTokensState,
|
||||
nftState = WalletNFTItemUM.Content(
|
||||
previews = persistentListOf(WalletNFTItemUM.Content.CollectionPreview.Image("img1")),
|
||||
collectionsCount = 1,
|
||||
allAssetsCount = 3,
|
||||
noCollectionAssetsCount = 0,
|
||||
isFlickering = false,
|
||||
onItemClick = { },
|
||||
),
|
||||
type = WalletType.Cold,
|
||||
tangemPayMainUM = TangemPayMainUM.Empty,
|
||||
)
|
||||
}
|
||||
|
||||
private val singleWalletLockedState = WalletState.SingleCurrency.Locked(
|
||||
walletCardState = noteLockedCard,
|
||||
buttons = persistentListOf(
|
||||
buyButton,
|
||||
sendButton,
|
||||
receiveButton,
|
||||
),
|
||||
bottomSheetConfig = null,
|
||||
onUnlockNotificationClick = {},
|
||||
onExploreClick = {},
|
||||
)
|
||||
|
||||
internal val walletScreenState = WalletScreenState(
|
||||
topBarConfig = topBarConfig,
|
||||
selectedWalletIndex = 0,
|
||||
wallets = persistentListOf(
|
||||
singleWalletLockedState,
|
||||
multiWalletState,
|
||||
),
|
||||
wallets2 = persistentListOf(),
|
||||
onWalletChange = { _, _ -> },
|
||||
event = consumedEvent(),
|
||||
isHidingMode = false,
|
||||
showMarketsOnboarding = false,
|
||||
onDismissMarketsTooltip = {},
|
||||
isRedesignEnabled = false,
|
||||
)
|
||||
|
||||
internal val accountScreenState =
|
||||
walletScreenState.copy(
|
||||
wallets = persistentListOf(
|
||||
singleWalletLockedState,
|
||||
multiWalletState.copy(tokensListState = portfolioContentState),
|
||||
),
|
||||
)
|
||||
|
||||
internal val accountScreenWithEmptyTokensState =
|
||||
walletScreenState.copy(
|
||||
wallets = persistentListOf(
|
||||
singleWalletLockedState,
|
||||
multiWalletState.copy(tokensListState = emptyPortfolioContentState),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -8,7 +8,6 @@ import com.tangem.common.routing.AppRoute
|
|||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
|
|
@ -37,7 +36,6 @@ import javax.inject.Inject
|
|||
internal class DefaultWalletRouter @Inject constructor(
|
||||
private val router: AppRouter,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
) : InnerWalletRouter {
|
||||
|
||||
override val dialogNavigation: SlotNavigation<WalletDialogConfig> = SlotNavigation()
|
||||
|
|
@ -51,13 +49,9 @@ internal class DefaultWalletRouter @Inject constructor(
|
|||
get() = OrganizeCallbacks()
|
||||
|
||||
override fun openOrganizeTokensScreen(userWalletId: UserWalletId) {
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
dialogNavigation.activate(
|
||||
configuration = WalletDialogConfig.OrganizeTokens(userWalletId),
|
||||
)
|
||||
} else {
|
||||
navigateToFlow.tryEmit(WalletRoute.OrganizeTokens(userWalletId))
|
||||
}
|
||||
}
|
||||
|
||||
override fun openDetailsScreen(selectedWalletId: UserWalletId) {
|
||||
|
|
|
|||
|
|
@ -1,510 +0,0 @@
|
|||
@file:Suppress("MaximumLineLength")
|
||||
|
||||
package com.tangem.feature.wallet.presentation.wallet.domain
|
||||
|
||||
import com.tangem.common.ui.notifications.NotificationId
|
||||
import com.tangem.common.ui.userwallet.ext.walletInterationIcon
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
|
||||
import com.tangem.domain.assetsdiscovery.model.AssetsDiscoveryProgress
|
||||
import com.tangem.domain.assetsdiscovery.usecase.ObserveAssetsDiscoveryUseCase
|
||||
import com.tangem.domain.card.CardTypesResolver
|
||||
import com.tangem.domain.card.IsWalletBackupProblematicUseCase
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.hotwallet.CheckHotWalletUpgradeBannerUseCase
|
||||
import com.tangem.domain.hotwallet.GetAccessCodeSkippedUseCase
|
||||
import com.tangem.domain.hotwallet.GetUpgradeBannerClosureTimestampUseCase
|
||||
import com.tangem.domain.hotwallet.ShouldShowUpgradeHotWalletBannerUseCase
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.TotalFiatBalance
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.account.PaymentAccountStatusValue
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.notifications.repository.NotificationsRepository
|
||||
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
|
||||
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
|
||||
import com.tangem.domain.yield.supply.promo.usecase.ShouldShowYieldBoostMainBannerUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetShouldShowMainPromoUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||
import com.tangem.hot.sdk.model.HotWalletId
|
||||
import com.tangem.lib.crypto.BlockchainUtils
|
||||
import com.tangem.utils.annotations.RemoveWithToggle
|
||||
import com.tangem.utils.extensions.addIf
|
||||
import com.tangem.utils.extensions.isPositive
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]")
|
||||
@RemoveWithToggle("APP_REDESIGN_ENABLED")
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
@ModelScoped
|
||||
internal class GetMultiWalletWarningsFactory @Inject constructor(
|
||||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
|
||||
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
|
||||
private val isWalletBackupProblematicUseCase: IsWalletBackupProblematicUseCase,
|
||||
private val notificationsRepository: NotificationsRepository,
|
||||
private val accountDependencies: AccountDependencies,
|
||||
private val getAccessCodeSkippedUseCase: GetAccessCodeSkippedUseCase,
|
||||
private val shouldShowUpgradeHotWalletBannerUseCase: ShouldShowUpgradeHotWalletBannerUseCase,
|
||||
private val getUpgradeBannerClosureTimestampUseCase: GetUpgradeBannerClosureTimestampUseCase,
|
||||
private val checkHotWalletUpgradeBannerUseCase: CheckHotWalletUpgradeBannerUseCase,
|
||||
private val observeAssetsDiscoveryUseCase: ObserveAssetsDiscoveryUseCase,
|
||||
private val shouldShowYieldBoostMainBannerUseCase: ShouldShowYieldBoostMainBannerUseCase,
|
||||
private val yieldSupplyGetShouldShowMainPromoUseCase: YieldSupplyGetShouldShowMainPromoUseCase,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
) {
|
||||
|
||||
@Suppress("UNCHECKED_CAST", "MagicNumber", "LongMethod", "CastNullableToNonNullableType")
|
||||
fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): Flow<ImmutableList<WalletNotification>> {
|
||||
val cardTypesResolver = (userWallet as? UserWallet.Cold)?.scanResponse?.cardTypesResolver
|
||||
val params = SingleAccountStatusListProducer.Params(userWallet.walletId)
|
||||
val accountStatusListFlow = accountDependencies.singleAccountStatusListSupplier(params)
|
||||
|
||||
val assetsDiscoveryProgressFlow =
|
||||
if (userWallet is UserWallet.Hot) {
|
||||
observeAssetsDiscoveryUseCase(userWallet.walletId).distinctUntilChanged()
|
||||
} else {
|
||||
flowOf(AssetsDiscoveryProgress.Idle)
|
||||
}
|
||||
|
||||
return combine(
|
||||
accountStatusListFlow,
|
||||
isReadyToShowRateAppUseCase().distinctUntilChanged(),
|
||||
isNeedToBackupUseCase(userWallet.walletId).distinctUntilChanged(),
|
||||
notificationsRepository.getShouldShowNotification(NotificationId.EnablePushesReminderNotification.key)
|
||||
.distinctUntilChanged(),
|
||||
getAccessCodeSkippedUseCase(userWallet.walletId).distinctUntilChanged(),
|
||||
shouldShowUpgradeHotWalletBannerUseCase.invoke(userWallet.walletId)
|
||||
.distinctUntilChanged(),
|
||||
getUpgradeBannerClosureTimestampUseCase(userWallet.walletId)
|
||||
.distinctUntilChanged(),
|
||||
assetsDiscoveryProgressFlow,
|
||||
yieldSupplyGetShouldShowMainPromoUseCase().distinctUntilChanged(),
|
||||
) { array -> array }
|
||||
.map { array ->
|
||||
val accountStatusList = array[0] as AccountStatusList
|
||||
val isReadyToShowRating = array[1] as Boolean
|
||||
val isNeedToBackup = array[2] as Boolean
|
||||
val shouldShowEnablePushesReminderNotification = array[3] as Boolean
|
||||
val shouldAccessCodeSkipped = array[4] as Boolean
|
||||
val shouldShowUpgradeBanner = array[5] as Boolean
|
||||
val closureTimestamp = array[6] as? Long
|
||||
val assetsDiscoveryProgress = array[7] as AssetsDiscoveryProgress
|
||||
val shouldShowYieldBoostPromoLocal = array[8] as Boolean
|
||||
|
||||
val flattenCurrencies = accountStatusList.flattenCurrencies()
|
||||
val paymentAccountStatus = accountStatusList.accountStatuses
|
||||
.filterIsInstance<AccountStatus.Payment>()
|
||||
.firstOrNull()
|
||||
|
||||
val isAddFundsBannerShown = isAddFundsBannerVisible(accountStatusList.totalFiatBalance)
|
||||
|
||||
buildList {
|
||||
addBackupErrorNotification(userWallet, clickIntents)
|
||||
|
||||
addUsedOutdatedDataNotification(accountStatusList.totalFiatBalance)
|
||||
|
||||
addAddFundsBanner(
|
||||
isVisible = isAddFundsBannerShown,
|
||||
userWallet = userWallet,
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
|
||||
addCriticalNotifications(userWallet)
|
||||
|
||||
addUpgradeHotWalletPromoNotification(
|
||||
userWallet = userWallet,
|
||||
flattenCurrencies = flattenCurrencies,
|
||||
clickIntents = clickIntents,
|
||||
shouldShowUpgradeBanner = shouldShowUpgradeBanner,
|
||||
closureTimestamp = closureTimestamp,
|
||||
)
|
||||
|
||||
if (!isAddFundsBannerShown) {
|
||||
addFinishWalletActivationNotification(
|
||||
userWallet = userWallet,
|
||||
flattenCurrencies = flattenCurrencies,
|
||||
clickIntents = clickIntents,
|
||||
shouldAccessCodeSkipped = shouldAccessCodeSkipped,
|
||||
)
|
||||
}
|
||||
|
||||
addInformationalNotifications(
|
||||
userWallet = userWallet,
|
||||
cardTypesResolver = cardTypesResolver,
|
||||
flattenCurrencies = flattenCurrencies,
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
|
||||
addWarningNotifications(
|
||||
cardTypesResolver = cardTypesResolver,
|
||||
flattenCurrencies = flattenCurrencies,
|
||||
isNeedToBackup = isNeedToBackup,
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
|
||||
addAssetsDiscoveryCompletedNotification(
|
||||
userWallet = userWallet,
|
||||
assetsDiscoveryProgress = assetsDiscoveryProgress,
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
|
||||
addPushReminderNotification(
|
||||
clickIntents = clickIntents,
|
||||
shouldShowPushReminderBanner = shouldShowEnablePushesReminderNotification &&
|
||||
!notificationsRepository.isUserAllowToSubscribeOnPushNotifications(),
|
||||
)
|
||||
|
||||
val hasCriticalOrWarning = any { notification ->
|
||||
notification is WalletNotification.Critical || notification is WalletNotification.Warning
|
||||
}
|
||||
|
||||
if (!hasCriticalOrWarning) {
|
||||
addRateTheAppNotification(isReadyToShowRating, clickIntents)
|
||||
}
|
||||
|
||||
// add as last warning
|
||||
paymentAccountStatus?.let { paymentAccountStatus ->
|
||||
addTangemPayWarnings(
|
||||
status = paymentAccountStatus,
|
||||
userWallet = userWallet,
|
||||
walletClickIntents = clickIntents,
|
||||
)
|
||||
}
|
||||
|
||||
addYieldBoostBannerNotification(
|
||||
userWallet = userWallet,
|
||||
shouldShowLocal = shouldShowYieldBoostPromoLocal,
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
}.toImmutableList()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun MutableList<WalletNotification>.addYieldBoostBannerNotification(
|
||||
userWallet: UserWallet,
|
||||
shouldShowLocal: Boolean,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
if (!shouldShowLocal) return
|
||||
if (designFeatureToggles.isRedesignEnabled) return
|
||||
val shouldShow = shouldShowYieldBoostMainBannerUseCase(userWallet.walletId).getOrNull() == true
|
||||
if (!shouldShow) return
|
||||
add(
|
||||
WalletNotification.YieldBoostPromo(
|
||||
onClick = { clickIntents.onYieldBoostBannerClick(userWallet.walletId) },
|
||||
onCloseClick = { clickIntents.onDismissYieldBoostBanner(userWallet.walletId) },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addTangemPayWarnings(
|
||||
status: AccountStatus.Payment,
|
||||
userWallet: UserWallet,
|
||||
walletClickIntents: WalletClickIntents,
|
||||
) {
|
||||
val notification = when (status.value) {
|
||||
is PaymentAccountStatusValue.Error.NotSynced -> WalletNotification.Warning.TangemPayRefreshNeeded(
|
||||
buttonText = resourceReference(id = R.string.tangempay_sync_needed_button),
|
||||
onRefreshClick = { walletClickIntents.onRefreshPayToken(userWallet) },
|
||||
shouldShowProgress = false,
|
||||
)
|
||||
is PaymentAccountStatusValue.NotCreated -> WalletNotification.CreateTangemPayAccount(
|
||||
onClick = { walletClickIntents.onOnboardingBannerClick(userWallet.walletId) },
|
||||
onCloseClick = { walletClickIntents.onOnboardingBannerCloseClick(userWallet.walletId) },
|
||||
)
|
||||
is PaymentAccountStatusValue.Error.Unavailable -> WalletNotification.Warning.TangemPayUnreachable
|
||||
is PaymentAccountStatusValue.Error.CardIssueFailed,
|
||||
is PaymentAccountStatusValue.Error.ExposedDevice,
|
||||
is PaymentAccountStatusValue.IssuingCard,
|
||||
is PaymentAccountStatusValue.AwaitingPlanSelection,
|
||||
is PaymentAccountStatusValue.Inactive,
|
||||
is PaymentAccountStatusValue.Loaded,
|
||||
is PaymentAccountStatusValue.Loading,
|
||||
is PaymentAccountStatusValue.UnderReview,
|
||||
is PaymentAccountStatusValue.Empty,
|
||||
is PaymentAccountStatusValue.Deactivated,
|
||||
-> null
|
||||
}
|
||||
notification?.let(::add)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addUsedOutdatedDataNotification(totalFiatBalance: TotalFiatBalance) {
|
||||
addIf(
|
||||
element = WalletNotification.UsedOutdatedData,
|
||||
condition = (totalFiatBalance as? TotalFiatBalance.Loaded)?.source == StatusSource.ONLY_CACHE,
|
||||
)
|
||||
}
|
||||
|
||||
private fun isAddFundsBannerVisible(totalFiatBalance: TotalFiatBalance): Boolean {
|
||||
val loaded = totalFiatBalance as? TotalFiatBalance.Loaded ?: return false
|
||||
return loaded.amount.orZero().signum() == 0
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addAddFundsBanner(
|
||||
isVisible: Boolean,
|
||||
userWallet: UserWallet,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
addIf(
|
||||
element = WalletNotification.AddFunds(
|
||||
onClick = { clickIntents.onAddFundsPromoClick(userWallet.walletId) },
|
||||
),
|
||||
condition = isVisible,
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addBackupErrorNotification(
|
||||
userWallet: UserWallet,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
addIf(
|
||||
element = WalletNotification.Critical.BackupError { clickIntents.onBackupErrorClick() },
|
||||
condition = isWalletBackupProblematicUseCase(userWallet),
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addCriticalNotifications(userWallet: UserWallet) {
|
||||
if (userWallet !is UserWallet.Cold) {
|
||||
return
|
||||
}
|
||||
|
||||
val cardTypesResolver = userWallet.scanResponse.cardTypesResolver
|
||||
addIf(
|
||||
element = WalletNotification.Critical.DevCard,
|
||||
condition = !cardTypesResolver.isReleaseFirmwareType(),
|
||||
)
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.Critical.FailedCardValidation,
|
||||
condition = cardTypesResolver.isReleaseFirmwareType() && cardTypesResolver.isAttestationFailed(),
|
||||
)
|
||||
|
||||
cardTypesResolver.getRemainingSignatures()?.let { remainingSignatures ->
|
||||
addIf(
|
||||
element = WalletNotification.Warning.LowSignatures(count = remainingSignatures),
|
||||
condition = remainingSignatures <= MAX_REMAINING_SIGNATURES_COUNT,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addInformationalNotifications(
|
||||
userWallet: UserWallet,
|
||||
cardTypesResolver: CardTypesResolver?,
|
||||
flattenCurrencies: List<CryptoCurrencyStatus>,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
addIf(
|
||||
element = WalletNotification.Informational.DemoCard,
|
||||
condition = cardTypesResolver != null && isDemoCardUseCase(cardId = cardTypesResolver.getCardId()),
|
||||
)
|
||||
|
||||
addMissingAddressesNotification(userWallet, flattenCurrencies, clickIntents)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addMissingAddressesNotification(
|
||||
userWallet: UserWallet,
|
||||
flattenCurrencies: List<CryptoCurrencyStatus>,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
val currencies = flattenCurrencies.getMissingAddressCurrencies()
|
||||
.ifEmpty { return }
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.Informational.MissingAddresses(
|
||||
tangemIcon = walletInterationIcon(userWallet),
|
||||
missingAddressesCount = currencies.distinctBy { it.network.id }.count(),
|
||||
onGenerateClick = {
|
||||
clickIntents.onGenerateMissedAddressesClick(
|
||||
userWalletId = userWallet.walletId,
|
||||
missedAddressCurrencies = currencies,
|
||||
)
|
||||
},
|
||||
),
|
||||
condition = currencies.isNotEmpty(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<CryptoCurrencyStatus>.getMissingAddressCurrencies(): List<CryptoCurrency> {
|
||||
return this
|
||||
.filter { it.value is CryptoCurrencyStatus.MissedDerivation }
|
||||
.map(CryptoCurrencyStatus::currency)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addWarningNotifications(
|
||||
cardTypesResolver: CardTypesResolver?,
|
||||
flattenCurrencies: List<CryptoCurrencyStatus>,
|
||||
isNeedToBackup: Boolean,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
addIf(
|
||||
element = WalletNotification.Warning.MissingBackup(
|
||||
onStartBackupClick = clickIntents::onAddBackupCardClick,
|
||||
),
|
||||
condition = isNeedToBackup,
|
||||
)
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.Warning.TestNetCard,
|
||||
condition = cardTypesResolver?.isTestCard() == true,
|
||||
)
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.Warning.SomeNetworksUnreachable,
|
||||
condition = flattenCurrencies.hasUnreachableNetworks(),
|
||||
)
|
||||
|
||||
addCloreMigrationNotification(flattenCurrencies, clickIntents)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addCloreMigrationNotification(
|
||||
flattenCurrencies: List<CryptoCurrencyStatus>,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
val cloreCurrency = flattenCurrencies.findCloreCurrency() ?: return
|
||||
|
||||
add(
|
||||
WalletNotification.CloreMigration(
|
||||
onStartMigrationClick = { clickIntents.onCloreMigrationClick(cloreCurrency) },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<CryptoCurrencyStatus>.findCloreCurrency(): CryptoCurrencyStatus? {
|
||||
return this.find { currencyStatus ->
|
||||
BlockchainUtils.isClore(currencyStatus.currency.network.rawId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addPushReminderNotification(
|
||||
clickIntents: WalletClickIntents,
|
||||
shouldShowPushReminderBanner: Boolean,
|
||||
) {
|
||||
addIf(
|
||||
element = WalletNotification.PushNotifications(
|
||||
onCloseClick = clickIntents::onDenyPermissions,
|
||||
onEnabledClick = clickIntents::onAllowPermissions,
|
||||
),
|
||||
condition = shouldShowPushReminderBanner,
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<CryptoCurrencyStatus>.hasUnreachableNetworks(): Boolean {
|
||||
return this.any { it.value is CryptoCurrencyStatus.Unreachable }
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addAssetsDiscoveryCompletedNotification(
|
||||
userWallet: UserWallet,
|
||||
assetsDiscoveryProgress: AssetsDiscoveryProgress,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
addIf(
|
||||
element = WalletNotification.AssetsDiscoveryCompleted(
|
||||
onCloseClick = { clickIntents.onDismissAssetsDiscoveryNotification(userWallet.walletId) },
|
||||
onManageTokensClick = { clickIntents.onAssetsDiscoveryManageClick(userWallet.walletId) },
|
||||
),
|
||||
condition = assetsDiscoveryProgress is AssetsDiscoveryProgress.Completed,
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addRateTheAppNotification(
|
||||
isReadyToShowRating: Boolean,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
addIf(
|
||||
element = WalletNotification.RateApp(
|
||||
onLikeClick = clickIntents::onLikeAppClick,
|
||||
onDislikeClick = clickIntents::onDislikeAppClick,
|
||||
onCloseClick = clickIntents::onCloseRateAppWarningClick,
|
||||
),
|
||||
condition = isReadyToShowRating,
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<CryptoCurrencyStatus>?.getFinishWalletActivationType(): WalletActivationBannerType {
|
||||
return if (this?.any { it.value.amount.orZero().isPositive() } == true) {
|
||||
WalletActivationBannerType.Warning
|
||||
} else {
|
||||
WalletActivationBannerType.Attention
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addFinishWalletActivationNotification(
|
||||
userWallet: UserWallet,
|
||||
flattenCurrencies: List<CryptoCurrencyStatus>,
|
||||
clickIntents: WalletClickIntents,
|
||||
shouldAccessCodeSkipped: Boolean,
|
||||
) {
|
||||
if (userWallet !is UserWallet.Hot) return
|
||||
|
||||
val isBackupExists = userWallet.backedUp
|
||||
val isAccessCodeRequired = userWallet.hotWalletId.authType == HotWalletId.AuthType.NoPassword &&
|
||||
!shouldAccessCodeSkipped
|
||||
val shouldShowFinishActivation = !isBackupExists || isAccessCodeRequired
|
||||
val type = flattenCurrencies.getFinishWalletActivationType()
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.FinishWalletActivation(
|
||||
type = type,
|
||||
buttonsState = when (type) {
|
||||
WalletActivationBannerType.Warning -> ButtonsState.PrimaryButtonConfig(
|
||||
text = resourceReference(R.string.hw_activation_need_finish),
|
||||
onClick = { clickIntents.onFinishWalletActivationClick(isBackupExists) },
|
||||
)
|
||||
else -> ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.hw_activation_need_finish),
|
||||
onClick = { clickIntents.onFinishWalletActivationClick(isBackupExists) },
|
||||
)
|
||||
},
|
||||
isBackupExists = isBackupExists,
|
||||
),
|
||||
condition = shouldShowFinishActivation,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun MutableList<WalletNotification>.addUpgradeHotWalletPromoNotification(
|
||||
userWallet: UserWallet,
|
||||
flattenCurrencies: List<CryptoCurrencyStatus>,
|
||||
clickIntents: WalletClickIntents,
|
||||
shouldShowUpgradeBanner: Boolean,
|
||||
closureTimestamp: Long?,
|
||||
) {
|
||||
if (userWallet !is UserWallet.Hot) return
|
||||
|
||||
val hasBalance = flattenCurrencies.any { it.value.amount.orZero().isPositive() }
|
||||
|
||||
val shouldShow = checkHotWalletUpgradeBannerUseCase(
|
||||
walletId = userWallet.walletId,
|
||||
hasBalance = hasBalance,
|
||||
shouldShowUpgradeBanner = shouldShowUpgradeBanner,
|
||||
closureTimestamp = closureTimestamp,
|
||||
).getOrNull() ?: return
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.UpgradeHotWalletPromo(
|
||||
onLaterClick = { clickIntents.onCloseUpgradeBannerClick(userWallet.walletId) },
|
||||
onUpgradeClick = { clickIntents.onUpgradeHotWalletClick(userWallet.walletId) },
|
||||
),
|
||||
condition = shouldShow,
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val MAX_REMAINING_SIGNATURES_COUNT = 10
|
||||
}
|
||||
}
|
||||
|
|
@ -1,252 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.domain
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.right
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.account.status.producer.SingleAccountStatusProducer
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusSupplier
|
||||
import com.tangem.domain.card.CardTypesResolver
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||
import com.tangem.utils.annotations.RemoveWithToggle
|
||||
import com.tangem.utils.extensions.addIf
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]")
|
||||
@RemoveWithToggle("APP_REDESIGN_ENABLED")
|
||||
@ModelScoped
|
||||
@Suppress("LongParameterList")
|
||||
internal class GetSingleWalletWarningsFactory @Inject constructor(
|
||||
private val singleAccountStatusSupplier: SingleAccountStatusSupplier,
|
||||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
|
||||
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
|
||||
private val hasSingleWalletSignedHashesUseCase: HasSingleWalletSignedHashesUseCase,
|
||||
private val getWalletsUseCase: GetWalletsUseCase,
|
||||
) {
|
||||
|
||||
private var isReadyForRateAppNotification = false
|
||||
|
||||
fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): Flow<ImmutableList<WalletNotification>> {
|
||||
if (userWallet !is UserWallet.Cold) {
|
||||
return flowOf(emptyList<WalletNotification>().toImmutableList())
|
||||
}
|
||||
val cardTypesResolver = userWallet.scanResponse.cardTypesResolver
|
||||
|
||||
return combine(
|
||||
flow = getPrimaryCurrencyStatusFlow(userWallet),
|
||||
flow2 = isReadyToShowRateAppUseCase().conflate(),
|
||||
flow3 = isNeedToBackupUseCase(userWallet.walletId).conflate(),
|
||||
flow4 = getWalletsUseCase().conflate(),
|
||||
) { maybePrimaryCurrencyStatus, isReadyToShowRating, isNeedToBackup, userWallets ->
|
||||
isReadyForRateAppNotification = true
|
||||
buildList {
|
||||
addUsedOutdatedDataNotification(maybePrimaryCurrencyStatus)
|
||||
|
||||
addCriticalNotifications(
|
||||
cardTypesResolver = cardTypesResolver,
|
||||
)
|
||||
|
||||
addInformationalNotifications(
|
||||
userWallets = userWallets,
|
||||
cardTypesResolver = cardTypesResolver,
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
|
||||
addWarningNotifications(
|
||||
userWallet = userWallet,
|
||||
cardTypesResolver = cardTypesResolver,
|
||||
maybePrimaryCurrencyStatus = maybePrimaryCurrencyStatus,
|
||||
isNeedToBackup = isNeedToBackup,
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
|
||||
addRateTheAppNotification(
|
||||
isReadyToShowRating = isReadyToShowRating,
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
}.toImmutableList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addUsedOutdatedDataNotification(
|
||||
maybePrimaryCurrencyStatus: Either<CurrencyStatusError, CryptoCurrencyStatus>,
|
||||
) {
|
||||
addIf(
|
||||
element = WalletNotification.UsedOutdatedData,
|
||||
condition = maybePrimaryCurrencyStatus.fold(
|
||||
ifLeft = { false },
|
||||
ifRight = { it.value.sources.total == StatusSource.ONLY_CACHE },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addCriticalNotifications(cardTypesResolver: CardTypesResolver) {
|
||||
addIf(
|
||||
element = WalletNotification.Critical.DevCard,
|
||||
condition = !cardTypesResolver.isReleaseFirmwareType(),
|
||||
)
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.Critical.FailedCardValidation,
|
||||
condition = cardTypesResolver.isReleaseFirmwareType() && cardTypesResolver.isAttestationFailed(),
|
||||
)
|
||||
|
||||
cardTypesResolver.getRemainingSignatures()?.let { remainingSignatures ->
|
||||
addIf(
|
||||
element = WalletNotification.Warning.LowSignatures(count = remainingSignatures),
|
||||
condition = remainingSignatures <= MAX_REMAINING_SIGNATURES_COUNT,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addInformationalNotifications(
|
||||
userWallets: List<UserWallet>,
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
val hasWalletOrWallet2 = userWallets.filterIsInstance<UserWallet.Cold>().any { coldWallet ->
|
||||
val typesResolver = coldWallet.scanResponse.cardTypesResolver
|
||||
typesResolver.isTangemWallet() || typesResolver.isWallet2()
|
||||
}
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.NoteMigration(
|
||||
onClick = { clickIntents.onNoteMigrationButtonClick(NOTE_MIGRATION_URL) },
|
||||
),
|
||||
condition = cardTypesResolver.isSingleCurrency() && !hasWalletOrWallet2,
|
||||
)
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.Informational.DemoCard,
|
||||
condition = isDemoCardUseCase(cardId = cardTypesResolver.getCardId()),
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun MutableList<WalletNotification>.addWarningNotifications(
|
||||
userWallet: UserWallet.Cold,
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
maybePrimaryCurrencyStatus: Either<CurrencyStatusError, CryptoCurrencyStatus>,
|
||||
isNeedToBackup: Boolean,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
val cryptoCurrencyStatus = maybePrimaryCurrencyStatus.fold(ifLeft = { null }, ifRight = { it })
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.Warning.MissingBackup(
|
||||
onStartBackupClick = clickIntents::onAddBackupCardClick,
|
||||
),
|
||||
condition = isNeedToBackup,
|
||||
)
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.Warning.TestNetCard,
|
||||
condition = cardTypesResolver.isTestCard(),
|
||||
)
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.Warning.NetworksUnreachable,
|
||||
condition = cryptoCurrencyStatus?.value is CryptoCurrencyStatus.Unreachable,
|
||||
)
|
||||
|
||||
addNoAccountWarning(cryptoCurrencyStatus)
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.Warning.NumberOfSignedHashesIncorrect(
|
||||
onCloseClick = clickIntents::onCloseAlreadySignedHashesWarningClick,
|
||||
),
|
||||
condition = hasSignedHashes(userWallet, cryptoCurrencyStatus),
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addNoAccountWarning(cryptoCurrencyStatus: CryptoCurrencyStatus?) {
|
||||
val noAccountStatus = cryptoCurrencyStatus?.value as? CryptoCurrencyStatus.NoAccount
|
||||
if (noAccountStatus != null) {
|
||||
add(
|
||||
element = WalletNotification.Informational.NoAccount(
|
||||
network = cryptoCurrencyStatus.currency.name,
|
||||
amount = noAccountStatus.amountToCreateAccount.toString(),
|
||||
symbol = cryptoCurrencyStatus.currency.symbol,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun hasSignedHashes(
|
||||
selectedWallet: UserWallet.Cold,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus?,
|
||||
): Boolean {
|
||||
return cryptoCurrencyStatus?.currency?.network?.let { network ->
|
||||
hasSingleWalletSignedHashesUseCase(userWallet = selectedWallet, network = network)
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.firstOrNull()
|
||||
} == true
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addRateTheAppNotification(
|
||||
isReadyToShowRating: Boolean,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
addIf(
|
||||
element = WalletNotification.RateApp(
|
||||
onLikeClick = clickIntents::onLikeAppClick,
|
||||
onDislikeClick = clickIntents::onDislikeAppClick,
|
||||
onCloseClick = clickIntents::onCloseRateAppWarningClick,
|
||||
),
|
||||
condition = isReadyToShowRating && isReadyForRateAppNotification,
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addIf(element: WalletNotification, condition: Boolean) {
|
||||
addIf(condition) {
|
||||
if (element is WalletNotification.Critical ||
|
||||
element is WalletNotification.Warning ||
|
||||
element is WalletNotification.NoteMigration
|
||||
) {
|
||||
isReadyForRateAppNotification = false
|
||||
}
|
||||
|
||||
element
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPrimaryCurrencyStatusFlow(
|
||||
userWallet: UserWallet,
|
||||
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
|
||||
return getAccountStatusFlow(userWallet).mapNotNull { accountStatus ->
|
||||
accountStatus.flattenCurrencies().firstOrNull()
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.conflate()
|
||||
.map { it.right() }
|
||||
}
|
||||
|
||||
private fun getAccountStatusFlow(userWallet: UserWallet): Flow<AccountStatus.CryptoPortfolio> {
|
||||
val accountId = AccountId.forMainCryptoPortfolio(userWalletId = userWallet.walletId)
|
||||
|
||||
return singleAccountStatusSupplier(SingleAccountStatusProducer.Params(accountId))
|
||||
.filterIsInstance<AccountStatus.CryptoPortfolio>()
|
||||
.distinctUntilChanged()
|
||||
.conflate()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val NOTE_MIGRATION_URL = "https://tangem.com/en/?promocode=Note10"
|
||||
const val MAX_REMAINING_SIGNATURES_COUNT = 10
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +1,24 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.loaders
|
||||
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
import com.tangem.feature.wallet.presentation.wallet.loaders.implementors.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@ModelScoped
|
||||
internal class WalletContentLoaderFactory @Inject constructor(
|
||||
private val multiWalletContentLoaderFactory: MultiWalletContentLoader.Factory,
|
||||
private val singleWalletWithTokenContentLoaderFactory: SingleWalletWithTokenContentLoader.Factory,
|
||||
private val singleWalletContentLoaderLegacyFactory: SingleWalletContentLoaderLegacy.Factory,
|
||||
private val singleWalletContentLoader: SingleWalletContentLoader.Factory,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
) {
|
||||
|
||||
fun create(userWallet: UserWallet, isRefresh: Boolean = false): WalletContentLoader? {
|
||||
fun create(userWallet: UserWallet): WalletContentLoader? {
|
||||
return when {
|
||||
userWallet.isMultiCurrency -> {
|
||||
multiWalletContentLoaderFactory.create(userWallet)
|
||||
}
|
||||
userWallet is UserWallet.Cold && userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken() -> {
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
userWallet is UserWallet.Cold -> {
|
||||
singleWalletContentLoader.create(userWallet)
|
||||
} else {
|
||||
singleWalletWithTokenContentLoaderFactory.create(userWallet)
|
||||
}
|
||||
}
|
||||
userWallet is UserWallet.Cold && !userWallet.isMultiCurrency -> {
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
singleWalletContentLoader.create(userWallet)
|
||||
} else {
|
||||
singleWalletContentLoaderLegacyFactory.create(userWallet, isRefresh)
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,10 +63,7 @@ internal class WalletScreenContentLoader @Inject constructor(
|
|||
}
|
||||
|
||||
private fun loadInternal(userWallet: UserWallet, coroutineScope: CoroutineScope, isRefresh: Boolean) {
|
||||
val loader = factory.create(
|
||||
userWallet = userWallet,
|
||||
isRefresh = isRefresh,
|
||||
)
|
||||
val loader = factory.create(userWallet = userWallet)
|
||||
|
||||
if (loader == null) {
|
||||
TangemLogger.e("Impossible to create loader for $userWallet")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
||||
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
|
||||
import dagger.assisted.Assisted
|
||||
|
|
@ -13,24 +12,18 @@ internal class MultiWalletContentLoader @AssistedInject constructor(
|
|||
private val accountListSubscriberFactory: AccountListSubscriber.Factory,
|
||||
private val walletNFTListSubscriberFactory: WalletNFTListSubscriber.Factory,
|
||||
private val checkWalletWithFundsSubscriberFactory: CheckWalletWithFundsSubscriber.Factory,
|
||||
private val multiWalletWarningsSubscriberFactory: MultiWalletWarningsSubscriber.Factory,
|
||||
private val walletNotificationsSubscriberFactory: WalletNotificationsSubscriber.Factory,
|
||||
private val multiWalletActionButtonsSubscriberFactory: MultiWalletActionButtonsSubscriber.Factory,
|
||||
private val tangemPayMainSubscriberFactory: TangemPayMainSubscriber.Factory,
|
||||
private val assetsDiscoverySubscriberFactory: AssetsDiscoverySubscriber.Factory,
|
||||
private val tokenListAnalyticsSubscriberFactory: TokenListAnalyticsSubscriber.Factory,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
||||
override fun create(): List<WalletSubscriber> = listOfNotNull(
|
||||
accountListSubscriberFactory.create(userWallet),
|
||||
walletNFTListSubscriberFactory.create(userWallet),
|
||||
checkWalletWithFundsSubscriberFactory.create(userWallet),
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
walletNotificationsSubscriberFactory.create(userWallet)
|
||||
} else {
|
||||
multiWalletWarningsSubscriberFactory.create(userWallet)
|
||||
},
|
||||
walletNotificationsSubscriberFactory.create(userWallet),
|
||||
multiWalletActionButtonsSubscriberFactory.create(userWallet),
|
||||
tangemPayMainSubscriberFactory.create(userWallet),
|
||||
tokenListAnalyticsSubscriberFactory.create(userWallet),
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
|
||||
import com.tangem.utils.annotations.RemoveWithToggle
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]")
|
||||
@RemoveWithToggle("APP_REDESIGN_ENABLED")
|
||||
@Suppress("LongParameterList")
|
||||
internal class SingleWalletContentLoaderLegacy @AssistedInject constructor(
|
||||
@Assisted private val userWallet: UserWallet.Cold,
|
||||
@Assisted private val isRefresh: Boolean,
|
||||
private val primaryCurrencySubscriberFactory: PrimaryCurrencySubscriber.Factory,
|
||||
private val singleWalletButtonsSubscriberFactory: SingleWalletButtonsSubscriber.Factory,
|
||||
private val singleWalletNotificationsSubscriberFactory: SingleWalletNotificationsSubscriber.Factory,
|
||||
private val singleWalletExpressStatusesSubscriberFactory: SingleWalletExpressStatusesSubscriber.Factory,
|
||||
private val txHistorySubscriberLegacyFactory: TxHistorySubscriberLegacy.Factory,
|
||||
private val checkWalletWithFundsSubscriberFactory: CheckWalletWithFundsSubscriber.Factory,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
||||
override fun create(): List<WalletSubscriber> = listOf(
|
||||
primaryCurrencySubscriberFactory.create(userWallet),
|
||||
singleWalletButtonsSubscriberFactory.create(userWallet),
|
||||
singleWalletNotificationsSubscriberFactory.create(userWallet),
|
||||
singleWalletExpressStatusesSubscriberFactory.create(userWallet),
|
||||
txHistorySubscriberLegacyFactory.create(userWallet, isRefresh),
|
||||
checkWalletWithFundsSubscriberFactory.create(userWallet),
|
||||
)
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(userWallet: UserWallet.Cold, isRefresh: Boolean): SingleWalletContentLoaderLegacy
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.CheckWalletWithFundsSubscriber
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletWarningsSubscriber
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.SingleWalletWithTokenSubscriberLegacy
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber
|
||||
import com.tangem.utils.annotations.RemoveWithToggle
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]")
|
||||
@RemoveWithToggle("APP_REDESIGN_ENABLED")
|
||||
internal class SingleWalletWithTokenContentLoader @AssistedInject constructor(
|
||||
@Assisted private val userWallet: UserWallet.Cold,
|
||||
private val singleWalletWithTokenSubscriberLegacyFactory: SingleWalletWithTokenSubscriberLegacy.Factory,
|
||||
private val multiWalletWarningsSubscriberFactory: MultiWalletWarningsSubscriber.Factory,
|
||||
private val checkWalletWithFundsSubscriberFactory: CheckWalletWithFundsSubscriber.Factory,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
||||
override fun create(): List<WalletSubscriber> = listOf(
|
||||
singleWalletWithTokenSubscriberLegacyFactory.create(userWallet),
|
||||
multiWalletWarningsSubscriberFactory.create(userWallet),
|
||||
checkWalletWithFundsSubscriberFactory.create(userWallet),
|
||||
)
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(userWallet: UserWallet.Cold): SingleWalletWithTokenContentLoader
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state
|
||||
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
|
@ -24,9 +23,7 @@ import javax.inject.Singleton
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Singleton
|
||||
internal class WalletStateController @Inject constructor(
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
) {
|
||||
internal class WalletStateController @Inject constructor() {
|
||||
|
||||
val uiState: StateFlow<WalletScreenState> get() = mutableUiState
|
||||
|
||||
|
|
@ -91,21 +88,13 @@ internal class WalletStateController @Inject constructor(
|
|||
|
||||
fun getSelectedWalletId(): UserWalletId {
|
||||
return with(value) {
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
wallets2[selectedWalletIndex].walletsBalanceUM.id
|
||||
} else {
|
||||
wallets[selectedWalletIndex].walletCardState.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getWalletIndexByWalletId(userWalletId: UserWalletId): Int? {
|
||||
return with(value) {
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
wallets2.indexOfFirstOrNull { it.walletsBalanceUM.id == userWalletId }
|
||||
} else {
|
||||
wallets.indexOfFirstOrNull { it.walletCardState.id == userWalletId }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +136,6 @@ internal class WalletStateController @Inject constructor(
|
|||
isHidingMode = false,
|
||||
showMarketsOnboarding = false,
|
||||
onDismissMarketsTooltip = {},
|
||||
isRedesignEnabled = designFeatureToggles.isRedesignEnabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -15,5 +15,4 @@ internal data class WalletScreenState(
|
|||
val isHidingMode: Boolean,
|
||||
val showMarketsOnboarding: Boolean,
|
||||
val onDismissMarketsTooltip: () -> Unit,
|
||||
val isRedesignEnabled: Boolean,
|
||||
)
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.SingleWalletCardStateConverter
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.SingleWalletMarketPriceConverter
|
||||
import com.tangem.utils.annotations.RemoveWithToggle
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
|
||||
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]")
|
||||
@RemoveWithToggle("APP_REDESIGN_ENABLED")
|
||||
internal class SetPrimaryCurrencyTransformer(
|
||||
private val userWallet: UserWallet,
|
||||
private val status: CryptoCurrencyStatus,
|
||||
private val appCurrency: AppCurrency,
|
||||
) : WalletStateTransformer(userWallet.walletId) {
|
||||
|
||||
override fun transform(prevState: WalletState): WalletState {
|
||||
return when (prevState) {
|
||||
is WalletState.SingleCurrency.Content -> {
|
||||
prevState.copy(
|
||||
walletCardState = prevState.walletCardState.toLoadedSingleCurrencyState(),
|
||||
marketPriceBlockState = prevState.marketPriceBlockState.toLoadedState(),
|
||||
)
|
||||
}
|
||||
is WalletState.SingleCurrency.Locked -> {
|
||||
TangemLogger.w("Impossible to load primary currency status for locked wallet")
|
||||
prevState
|
||||
}
|
||||
is WalletState.MultiCurrency -> {
|
||||
TangemLogger.w("Impossible to load primary currency status for multi-currency wallet")
|
||||
prevState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // It will not be used
|
||||
}
|
||||
|
||||
private fun WalletCardState.toLoadedSingleCurrencyState(): WalletCardState {
|
||||
return SingleWalletCardStateConverter(status.value, userWallet, appCurrency).convert(value = this)
|
||||
}
|
||||
|
||||
private fun MarketPriceBlockState.toLoadedState(): MarketPriceBlockState {
|
||||
return SingleWalletMarketPriceConverter(status.value, appCurrency).convert(value = this)
|
||||
}
|
||||
}
|
||||
|
|
@ -26,7 +26,6 @@ internal class SetTokenListTransformer(
|
|||
private val stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability> = emptyMap(),
|
||||
private val shouldShowMainPromo: Boolean,
|
||||
private val isAccountsModeEnabled: Boolean,
|
||||
private val isRedesignEnabled: Boolean,
|
||||
private val isMultipleCardsEnabled: Boolean,
|
||||
private val isPolymarketEnabled: Boolean,
|
||||
) : WalletStateTransformer(userWallet.walletId) {
|
||||
|
|
@ -34,15 +33,12 @@ internal class SetTokenListTransformer(
|
|||
private val tangemPayConverter by lazy {
|
||||
TangemPayMainBlockConverter(
|
||||
tangemPayClickIntents = clickIntents,
|
||||
isRedesignEnabled = isRedesignEnabled,
|
||||
isMultipleCardsEnabled = isMultipleCardsEnabled,
|
||||
)
|
||||
}
|
||||
|
||||
private val virtualAccountConverter by lazy {
|
||||
VirtualAccountMainBlockConverter(
|
||||
isRedesignEnabled = isRedesignEnabled,
|
||||
)
|
||||
VirtualAccountMainBlockConverter()
|
||||
}
|
||||
|
||||
override fun transform(prevState: WalletState): WalletState {
|
||||
|
|
|
|||
|
|
@ -15,22 +15,12 @@ internal abstract class WalletStateTransformer(
|
|||
abstract fun transform(walletUM: WalletUM): WalletUM
|
||||
|
||||
final override fun transform(prevState: WalletScreenState): WalletScreenState {
|
||||
return if (prevState.isRedesignEnabled) {
|
||||
prevState.copy(
|
||||
return prevState.copy(
|
||||
wallets2 = prevState.wallets2
|
||||
.map { walletUM ->
|
||||
if (walletUM.walletsBalanceUM.id == userWalletId) transform(walletUM) else walletUM
|
||||
}
|
||||
.toImmutableList(),
|
||||
)
|
||||
} else {
|
||||
prevState.copy(
|
||||
wallets = prevState.wallets
|
||||
.map { state ->
|
||||
if (state.walletCardState.id == userWalletId) transform(state) else state
|
||||
}
|
||||
.toImmutableList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@ import androidx.compose.ui.text.SpanStyle
|
|||
import com.tangem.common.ui.R
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.format.bigdecimal.formatStyled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.models.StatusSource
|
||||
|
|
@ -20,7 +19,6 @@ import java.util.Currency
|
|||
|
||||
internal class TangemPayMainBlockConverter(
|
||||
private val tangemPayClickIntents: TangemPayIntents,
|
||||
private val isRedesignEnabled: Boolean,
|
||||
private val isMultipleCardsEnabled: Boolean,
|
||||
) : Converter<AccountStatus.Payment, TangemPayMainUM> {
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
|
|
@ -99,21 +97,12 @@ internal class TangemPayMainBlockConverter(
|
|||
|
||||
private fun getBalanceText(currencyCode: String, balance: BigDecimal): TextReference {
|
||||
val currency = Currency.getInstance(currencyCode)
|
||||
val formattedBalance = if (isRedesignEnabled) {
|
||||
balance.formatStyled {
|
||||
return balance.formatStyled {
|
||||
fiat(
|
||||
fiatCurrencyCode = currency.currencyCode,
|
||||
fiatCurrencySymbol = currency.symbol,
|
||||
spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.neutral.secondary) },
|
||||
)
|
||||
}
|
||||
} else {
|
||||
stringReference(
|
||||
balance.format {
|
||||
fiat(fiatCurrencyCode = currency.currencyCode, fiatCurrencySymbol = currency.symbol)
|
||||
},
|
||||
)
|
||||
}
|
||||
return formattedBalance
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ import com.tangem.common.ui.R
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.format.bigdecimal.formatStyled
|
||||
import com.tangem.core.ui.format.bigdecimal.getJavaCurrencyByCode
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -17,9 +16,7 @@ import com.tangem.features.virtualaccount.main.entity.VirtualAccountMainUM
|
|||
import com.tangem.utils.converter.Converter
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class VirtualAccountMainBlockConverter(
|
||||
private val isRedesignEnabled: Boolean,
|
||||
) : Converter<AccountStatus.Virtual, VirtualAccountMainUM> {
|
||||
internal class VirtualAccountMainBlockConverter : Converter<AccountStatus.Virtual, VirtualAccountMainUM> {
|
||||
|
||||
override fun convert(value: AccountStatus.Virtual): VirtualAccountMainUM {
|
||||
return when (val statusValue = value.value) {
|
||||
|
|
@ -66,21 +63,12 @@ internal class VirtualAccountMainBlockConverter(
|
|||
|
||||
private fun getBalanceText(currencyCode: String, balance: BigDecimal): TextReference {
|
||||
val currency = getJavaCurrencyByCode(currencyCode)
|
||||
val formattedBalance = if (isRedesignEnabled) {
|
||||
balance.formatStyled {
|
||||
return balance.formatStyled {
|
||||
fiat(
|
||||
fiatCurrencyCode = currency.currencyCode,
|
||||
fiatCurrencySymbol = currency.symbol,
|
||||
spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.neutral.secondary) },
|
||||
)
|
||||
}
|
||||
} else {
|
||||
stringReference(
|
||||
balance.format {
|
||||
fiat(fiatCurrencyCode = currency.currencyCode, fiatCurrencySymbol = currency.symbol)
|
||||
},
|
||||
)
|
||||
}
|
||||
return formattedBalance
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
|
|
@ -37,7 +36,6 @@ internal class AccountListSubscriber @AssistedInject constructor(
|
|||
private val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
|
||||
private val stakingAvailabilityListUseCase: StakingAvailabilityListUseCase,
|
||||
private val yieldSupplyGetShouldShowMainPromoUseCase: YieldSupplyGetShouldShowMainPromoUseCase,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
private val polymarketFeatureToggles: PolymarketFeatureToggles,
|
||||
) : BasicAccountListSubscriber() {
|
||||
|
||||
|
|
@ -83,7 +81,6 @@ internal class AccountListSubscriber @AssistedInject constructor(
|
|||
"promo=$shouldShowMainPromo, " +
|
||||
"stakingMap=${stakingAvailabilityMap.size}",
|
||||
)
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
updateState2(
|
||||
accountList = accountList,
|
||||
appCurrency = appCurrency,
|
||||
|
|
@ -95,17 +92,6 @@ internal class AccountListSubscriber @AssistedInject constructor(
|
|||
isMultipleCardsEnabled = true,
|
||||
isPolymarketEnabled = polymarketFeatureToggles.isPolymarketEnabled,
|
||||
)
|
||||
} else {
|
||||
updateState(
|
||||
accountList = accountList,
|
||||
appCurrency = appCurrency,
|
||||
expandedAccounts = expandedAccounts,
|
||||
isAccountMode = isAccountMode,
|
||||
yieldSupplyApyMap = yieldSupplyApyMap,
|
||||
stakingAvailabilityMap = stakingAvailabilityMap,
|
||||
shouldShowMainPromo = shouldShowMainPromo,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,20 +5,13 @@ import com.tangem.domain.account.models.AccountStatusList
|
|||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.core.utils.getOrElse
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
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.feature.wallet.presentation.wallet.state.transformers.SetTokenListErrorTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenListTransformer
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -43,48 +36,6 @@ internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() {
|
|||
.distinctUntilChanged()
|
||||
}
|
||||
|
||||
protected fun updateState(
|
||||
accountList: AccountStatusList,
|
||||
appCurrency: AppCurrency,
|
||||
expandedAccounts: Set<AccountId>,
|
||||
isAccountMode: Boolean,
|
||||
yieldSupplyApyMap: Map<String, BigDecimal> = emptyMap(),
|
||||
stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability> = emptyMap(),
|
||||
shouldShowMainPromo: Boolean = false,
|
||||
) {
|
||||
val mainAccount = accountList.mainAccount
|
||||
|
||||
when {
|
||||
!isAccountMode -> {
|
||||
val isMainAccountEmpty = mainAccount.tokenList.flattenCurrencies().isEmpty()
|
||||
val maybeTokenList = if (isMainAccountEmpty) {
|
||||
Lce.Error(TokenListError.EmptyTokens)
|
||||
} else {
|
||||
Lce.Content(mainAccount.tokenList)
|
||||
}
|
||||
|
||||
singleAccountTransform(
|
||||
maybeTokenList = maybeTokenList,
|
||||
appCurrency = appCurrency,
|
||||
mainAccount = mainAccount,
|
||||
yieldSupplyApyMap = yieldSupplyApyMap,
|
||||
stakingAvailabilityMap = stakingAvailabilityMap,
|
||||
shouldShowMainPromo = shouldShowMainPromo,
|
||||
)
|
||||
}
|
||||
isAccountMode -> {
|
||||
val convertParams = TokenConverterParams.Account(accountList, expandedAccounts)
|
||||
updateContent(
|
||||
params = convertParams,
|
||||
appCurrency = appCurrency,
|
||||
yieldSupplyApyMap = yieldSupplyApyMap,
|
||||
stakingAvailabilityMap = stakingAvailabilityMap,
|
||||
shouldShowMainPromo = shouldShowMainPromo,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun updateState2(
|
||||
accountList: AccountStatusList,
|
||||
appCurrency: AppCurrency,
|
||||
|
|
@ -106,75 +57,9 @@ internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() {
|
|||
stakingAvailabilityMap = stakingAvailabilityMap,
|
||||
shouldShowMainPromo = shouldShowMainPromo,
|
||||
isAccountsModeEnabled = isAccountMode,
|
||||
isRedesignEnabled = true,
|
||||
isMultipleCardsEnabled = isMultipleCardsEnabled,
|
||||
isPolymarketEnabled = isPolymarketEnabled,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun singleAccountTransform(
|
||||
maybeTokenList: Lce<TokenListError, TokenList>,
|
||||
appCurrency: AppCurrency,
|
||||
mainAccount: AccountStatus.CryptoPortfolio,
|
||||
yieldSupplyApyMap: Map<String, BigDecimal> = emptyMap(),
|
||||
stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability> = emptyMap(),
|
||||
shouldShowMainPromo: Boolean,
|
||||
) {
|
||||
val tokenList = maybeTokenList.getOrElse(
|
||||
ifLoading = { maybeContent ->
|
||||
val isRefreshing = stateController.getWalletState(userWallet.walletId)
|
||||
?.pullToRefreshConfig
|
||||
?.isRefreshing == true
|
||||
|
||||
maybeContent
|
||||
?.takeIf { !isRefreshing }
|
||||
?: return
|
||||
},
|
||||
ifError = { e ->
|
||||
TangemLogger.e("Failed to load token list: $e")
|
||||
stateController.update(
|
||||
SetTokenListErrorTransformer(
|
||||
selectedWallet = userWallet,
|
||||
error = e,
|
||||
appCurrency = appCurrency,
|
||||
clickIntents = clickIntents,
|
||||
),
|
||||
)
|
||||
return
|
||||
},
|
||||
)
|
||||
|
||||
updateContent(
|
||||
params = TokenConverterParams.Wallet(mainAccount, tokenList),
|
||||
appCurrency = appCurrency,
|
||||
yieldSupplyApyMap = yieldSupplyApyMap,
|
||||
stakingAvailabilityMap = stakingAvailabilityMap,
|
||||
shouldShowMainPromo = shouldShowMainPromo,
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateContent(
|
||||
params: TokenConverterParams,
|
||||
appCurrency: AppCurrency,
|
||||
yieldSupplyApyMap: Map<String, BigDecimal> = emptyMap(),
|
||||
stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability> = emptyMap(),
|
||||
shouldShowMainPromo: Boolean,
|
||||
) {
|
||||
stateController.update(
|
||||
SetTokenListTransformer(
|
||||
params = params,
|
||||
userWallet = userWallet,
|
||||
appCurrency = appCurrency,
|
||||
clickIntents = clickIntents,
|
||||
yieldSupplyApyMap = yieldSupplyApyMap,
|
||||
stakingAvailabilityMap = stakingAvailabilityMap,
|
||||
shouldShowMainPromo = shouldShowMainPromo,
|
||||
isAccountsModeEnabled = false,
|
||||
isRedesignEnabled = false,
|
||||
isMultipleCardsEnabled = false,
|
||||
isPolymarketEnabled = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.utils.annotations.RemoveWithToggle
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
|
||||
/**
|
||||
* Basic implementation of [WalletSubscriber] for single wallet.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]")
|
||||
@RemoveWithToggle("APP_REDESIGN_ENABLED")
|
||||
internal abstract class BasicSingleWalletSubscriber : BasicWalletSubscriber() {
|
||||
|
||||
/** Account ID for the main crypto portfolio of the user wallet */
|
||||
val accountId: AccountId
|
||||
get() = AccountId.forMainCryptoPortfolio(userWalletId = userWallet.walletId)
|
||||
|
||||
/**
|
||||
* Provides a flow of the primary [CryptoCurrencyStatus] for the associated user wallet.
|
||||
*
|
||||
* @return A [Flow] emitting the primary [CryptoCurrencyStatus] of the wallet, distinct and conflated.
|
||||
*/
|
||||
protected fun getPrimaryCurrencyStatusFlow(): Flow<CryptoCurrencyStatus> {
|
||||
return getMainAccountStatusFlow()
|
||||
.mapNotNull { accountStatus ->
|
||||
accountStatus.flattenCurrencies().firstOrNull()
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.conflate()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.stories.GetStoryContentUseCase
|
||||
import com.tangem.domain.stories.models.StoryContentIds
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetWarningsTransformer
|
||||
import com.tangem.utils.annotations.RemoveWithToggle
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]")
|
||||
@RemoveWithToggle("APP_REDESIGN_ENABLED")
|
||||
internal class MultiWalletWarningsSubscriber @AssistedInject constructor(
|
||||
@Assisted private val userWallet: UserWallet,
|
||||
private val stateController: WalletStateController,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
|
||||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender,
|
||||
private val getStoryContentUseCase: GetStoryContentUseCase,
|
||||
) : WalletSubscriber() {
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<ImmutableList<WalletNotification>> {
|
||||
return getMultiWalletWarningsFactory.create(userWallet, clickIntents)
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.onEach { warnings ->
|
||||
if (warnings.any { it is WalletNotification.YieldBoostPromo }) {
|
||||
coroutineScope.launch {
|
||||
getStoryContentUseCase.invokeSync(
|
||||
id = StoryContentIds.STORY_FIRST_TIME_YIELD_PROMO.id,
|
||||
refresh = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val displayedState = stateController.getWalletState(userWallet.walletId)
|
||||
|
||||
// Wait until the wallet appears in the list
|
||||
stateController.uiState.first {
|
||||
it.wallets.any { walletState -> walletState.walletCardState.id == userWallet.walletId }
|
||||
}
|
||||
|
||||
stateController.update(
|
||||
SetWarningsTransformer(
|
||||
userWalletId = userWallet.walletId,
|
||||
warnings = warnings,
|
||||
notifications = persistentListOf(),
|
||||
),
|
||||
)
|
||||
walletWarningsAnalyticsSender.send(displayedState, warnings)
|
||||
walletWarningsSingleEventSender.send(
|
||||
userWalletId = userWallet.walletId,
|
||||
displayedUiState = displayedState,
|
||||
newWarnings = warnings,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(userWallet: UserWallet): MultiWalletWarningsSubscriber
|
||||
}
|
||||
}
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetPrimaryCurrencyTransformer
|
||||
import com.tangem.utils.annotations.RemoveWithToggle
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]")
|
||||
@RemoveWithToggle("APP_REDESIGN_ENABLED")
|
||||
internal class PrimaryCurrencySubscriber @AssistedInject constructor(
|
||||
@Assisted override val userWallet: UserWallet,
|
||||
override val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val stateController: WalletStateController,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : BasicSingleWalletSubscriber() {
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<*> {
|
||||
return combine(
|
||||
flow = getPrimaryCurrencyStatusFlow(),
|
||||
flow2 = getSelectedAppCurrencyUseCase.invokeOrDefault(),
|
||||
transform = ::Pair,
|
||||
)
|
||||
.onEach { (status, appCurrency) ->
|
||||
updateContent(status, appCurrency)
|
||||
sendAnalyticsEvent(status)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateContent(status: CryptoCurrencyStatus, appCurrency: AppCurrency) {
|
||||
stateController.update(
|
||||
SetPrimaryCurrencyTransformer(
|
||||
status = status,
|
||||
userWallet = userWallet,
|
||||
appCurrency = appCurrency,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun sendAnalyticsEvent(status: CryptoCurrencyStatus) {
|
||||
val fiatAmount = status.value.fiatAmount
|
||||
val cardBalanceState = when (status.value) {
|
||||
is CryptoCurrencyStatus.Loaded,
|
||||
is CryptoCurrencyStatus.NoAccount,
|
||||
is CryptoCurrencyStatus.NoAmount,
|
||||
-> createCardBalanceState(fiatAmount)
|
||||
is CryptoCurrencyStatus.NoQuote -> AnalyticsParam.CardBalanceState.NoRate
|
||||
is CryptoCurrencyStatus.Unreachable,
|
||||
-> AnalyticsParam.CardBalanceState.BlockchainError
|
||||
is CryptoCurrencyStatus.MissedDerivation,
|
||||
is CryptoCurrencyStatus.Loading,
|
||||
is CryptoCurrencyStatus.Custom,
|
||||
-> null
|
||||
}
|
||||
|
||||
cardBalanceState?.let { balanceState ->
|
||||
// do not send tokens count for single currency wallet
|
||||
analyticsEventHandler.send(
|
||||
event = Basic.BalanceLoaded(
|
||||
balance = balanceState,
|
||||
tokensCount = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCardBalanceState(fiatAmount: BigDecimal?): AnalyticsParam.CardBalanceState? {
|
||||
return when {
|
||||
fiatAmount == null -> null
|
||||
fiatAmount.isZero() -> AnalyticsParam.CardBalanceState.Empty
|
||||
else -> AnalyticsParam.CardBalanceState.Full
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(userWallet: UserWallet): PrimaryCurrencySubscriber
|
||||
}
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.account.status.usecase.GetCryptoCurrencyActionsUseCaseV2
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.tokens.model.TokenActionsState
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetCryptoCurrencyActionsTransformer
|
||||
import com.tangem.utils.annotations.RemoveWithToggle
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]")
|
||||
@RemoveWithToggle("APP_REDESIGN_ENABLED")
|
||||
internal class SingleWalletButtonsSubscriber @AssistedInject constructor(
|
||||
@Assisted override val userWallet: UserWallet,
|
||||
override val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
private val stateController: WalletStateController,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val getCryptoCurrencyActionsUseCaseV2: GetCryptoCurrencyActionsUseCaseV2,
|
||||
) : BasicSingleWalletSubscriber() {
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<TokenActionsState> {
|
||||
return getPrimaryCurrencyStatusFlow()
|
||||
.flatMapLatest {
|
||||
getCryptoCurrencyActionsUseCaseV2(accountId = accountId, currency = it.currency)
|
||||
}
|
||||
.onEach {
|
||||
updateContent(tokenActionsState = it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateContent(tokenActionsState: TokenActionsState) {
|
||||
stateController.update(
|
||||
SetCryptoCurrencyActionsTransformer(
|
||||
tokenActionsState = tokenActionsState,
|
||||
userWallet = userWallet,
|
||||
clickIntents = clickIntents,
|
||||
accountId = accountId,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(userWallet: UserWallet): SingleWalletButtonsSubscriber
|
||||
}
|
||||
}
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.onramp.GetOnrampTransactionsUseCase
|
||||
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
|
||||
import com.tangem.domain.onramp.model.cache.OnrampTransaction
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetExpressStatusesTransformer
|
||||
import com.tangem.utils.annotations.RemoveWithToggle
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]")
|
||||
@RemoveWithToggle("APP_REDESIGN_ENABLED")
|
||||
@Suppress("LongParameterList")
|
||||
internal class SingleWalletExpressStatusesSubscriber @AssistedInject constructor(
|
||||
@Assisted override val userWallet: UserWallet,
|
||||
override val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,
|
||||
private val stateController: WalletStateController,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : BasicSingleWalletSubscriber() {
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<*> {
|
||||
val getOnrampTransactionsFlow = getPrimaryCurrencyStatusFlow().flatMapLatest { currencyStatus ->
|
||||
getOnrampTransactionsUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
cryptoCurrencyId = currencyStatus.currency.id,
|
||||
)
|
||||
.map { currencyStatus to it }
|
||||
}
|
||||
|
||||
return combine(
|
||||
flow = getOnrampTransactionsFlow,
|
||||
flow2 = getSelectedAppCurrencyUseCase.invokeOrDefault(),
|
||||
transform = ::toTriple,
|
||||
)
|
||||
.onEach { (status, maybeTransaction, appCurrency) ->
|
||||
maybeTransaction.fold(
|
||||
ifRight = { onrampTxs ->
|
||||
onrampTxs.clearHiddenTerminal()
|
||||
stateController.update(
|
||||
SetExpressStatusesTransformer(
|
||||
userWalletId = userWallet.walletId,
|
||||
onrampTxs = onrampTxs,
|
||||
clickIntents = clickIntents,
|
||||
cryptoCurrencyStatus = status,
|
||||
appCurrency = appCurrency,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
),
|
||||
)
|
||||
},
|
||||
ifLeft = {
|
||||
stateController.update(
|
||||
SetExpressStatusesTransformer(
|
||||
userWalletId = userWallet.walletId,
|
||||
onrampTxs = emptyList(),
|
||||
clickIntents = clickIntents,
|
||||
cryptoCurrencyStatus = status,
|
||||
appCurrency = appCurrency,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <A, B, C> toTriple(firstPair: Pair<A, B>, second: C): Triple<A, B, C> {
|
||||
return Triple(firstPair.first, firstPair.second, second)
|
||||
}
|
||||
|
||||
private suspend fun List<OnrampTransaction>.clearHiddenTerminal() {
|
||||
this
|
||||
.filter { it.status.isHidden && it.status.isTerminal }
|
||||
.forEach { onrampRemoveTransactionUseCase(txId = it.txId) }
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(userWallet: UserWallet): SingleWalletExpressStatusesSubscriber
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarningsFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetWarningsTransformer
|
||||
import com.tangem.utils.annotations.RemoveWithToggle
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]")
|
||||
@RemoveWithToggle("APP_REDESIGN_ENABLED")
|
||||
internal class SingleWalletNotificationsSubscriber @AssistedInject constructor(
|
||||
@Assisted private val userWallet: UserWallet,
|
||||
private val stateController: WalletStateController,
|
||||
private val getSingleWalletWarningsFactory: GetSingleWalletWarningsFactory,
|
||||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
) : WalletSubscriber() {
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<ImmutableList<WalletNotification>> {
|
||||
return getSingleWalletWarningsFactory.create(userWallet, clickIntents)
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.onEach { warnings ->
|
||||
val displayedState = stateController.getWalletState(userWallet.walletId)
|
||||
|
||||
stateController.update(SetWarningsTransformer(userWallet.walletId, warnings, persistentListOf()))
|
||||
walletWarningsAnalyticsSender.send(displayedState, warnings)
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(userWallet: UserWallet): SingleWalletNotificationsSubscriber
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
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.utils.annotations.RemoveWithToggle
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
|
||||
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]")
|
||||
@RemoveWithToggle("APP_REDESIGN_ENABLED")
|
||||
internal class SingleWalletWithTokenSubscriberLegacy @AssistedInject constructor(
|
||||
@Assisted override val userWallet: UserWallet.Cold,
|
||||
override val accountDependencies: AccountDependencies,
|
||||
override val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
override val stateController: WalletStateController,
|
||||
override val clickIntents: WalletClickIntents,
|
||||
) : BasicAccountListSubscriber() {
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<Unit> = combine(
|
||||
flow = getAccountStatusListFlow(),
|
||||
flow2 = getAppCurrencyFlow(),
|
||||
flow3 = accountDependencies.expandedAccountsHolder.expandedAccounts(userWallet),
|
||||
flow4 = accountDependencies.isAccountsModeEnabledUseCase(),
|
||||
transform = ::updateState,
|
||||
)
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(userWallet: UserWallet.Cold): SingleWalletWithTokenSubscriberLegacy
|
||||
}
|
||||
}
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.cachedIn
|
||||
import androidx.paging.map
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.network.TxInfo
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.txhistory.models.TxHistoryListError
|
||||
import com.tangem.domain.txhistory.models.TxHistoryStateError
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryCountErrorTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryCountTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryItemsErrorTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryItemsTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TxHistoryItemStateConverter
|
||||
import com.tangem.utils.annotations.RemoveWithToggle
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]")
|
||||
@RemoveWithToggle("APP_REDESIGN_ENABLED")
|
||||
@Suppress("LongParameterList")
|
||||
internal class TxHistorySubscriberLegacy @AssistedInject constructor(
|
||||
@Assisted override val userWallet: UserWallet.Cold,
|
||||
@Assisted private val isRefresh: Boolean,
|
||||
override val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
|
||||
private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase,
|
||||
private val stateController: WalletStateController,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
) : BasicSingleWalletSubscriber() {
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<PagingData<TxInfo>> {
|
||||
return flow {
|
||||
getPrimaryCurrencyStatusFlow().collectLatest { status ->
|
||||
val maybeTxHistoryItemCount = txHistoryItemsCountUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
currency = status.currency,
|
||||
)
|
||||
|
||||
setLoadingTxHistoryState(maybeTxHistoryItemCount, status)
|
||||
|
||||
maybeTxHistoryItemCount.onRight { _ ->
|
||||
val maybeTxHistoryItems = txHistoryItemsUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
currency = status.currency,
|
||||
refresh = isRefresh,
|
||||
).map { it.cachedIn(coroutineScope) }
|
||||
|
||||
setLoadedTxHistoryState(maybeTxHistoryItems, currency = status.currency)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setLoadingTxHistoryState(
|
||||
maybeTxHistoryItemCount: Either<TxHistoryStateError, Int>,
|
||||
status: CryptoCurrencyStatus,
|
||||
) {
|
||||
stateController.update(
|
||||
maybeTxHistoryItemCount.fold(
|
||||
ifLeft = { error ->
|
||||
SetTxHistoryCountErrorTransformer(
|
||||
userWallet = userWallet,
|
||||
error = error,
|
||||
pendingTransactions = status.value.pendingTransactions,
|
||||
clickIntents = clickIntents,
|
||||
currency = status.currency,
|
||||
)
|
||||
},
|
||||
ifRight = { txCount ->
|
||||
SetTxHistoryCountTransformer(
|
||||
userWalletId = userWallet.walletId,
|
||||
transactionsCount = txCount,
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun setLoadedTxHistoryState(
|
||||
maybeTxHistoryItems: Either<TxHistoryListError, Flow<PagingData<TxInfo>>>,
|
||||
currency: CryptoCurrency,
|
||||
) {
|
||||
stateController.update(
|
||||
maybeTxHistoryItems.fold(
|
||||
ifLeft = { error ->
|
||||
SetTxHistoryItemsErrorTransformer(
|
||||
userWalletId = userWallet.walletId,
|
||||
error = error,
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
},
|
||||
ifRight = { itemsFlow ->
|
||||
val blockchain = userWallet.scanResponse.cardTypesResolver.getBlockchain()
|
||||
val itemConverter = TxHistoryItemStateConverter(
|
||||
currency = currency,
|
||||
symbol = blockchain.currency,
|
||||
decimals = blockchain.decimals(),
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
|
||||
SetTxHistoryItemsTransformer(
|
||||
userWallet = userWallet,
|
||||
flow = itemsFlow.map { items ->
|
||||
items.map(itemConverter::convert)
|
||||
},
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(userWallet: UserWallet.Cold, isRefresh: Boolean): TxHistorySubscriberLegacy
|
||||
}
|
||||
}
|
||||
|
|
@ -1,847 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideIn
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.geometry.CornerRadius
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.geometry.RoundRect
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Outline
|
||||
import androidx.compose.ui.graphics.Path
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.semantics.clearAndSetSemantics
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.*
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import com.tangem.common.ui.bottomsheet.chooseaddress.ChooseAddressBottomSheet
|
||||
import com.tangem.common.ui.bottomsheet.chooseaddress.ChooseAddressBottomSheetConfig
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheet
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.common.ui.expressStatus.expressTransactionsItemsLegacy
|
||||
import com.tangem.core.ui.components.atoms.handComposableComponentHeight
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheetDraggableHeaderLegacy
|
||||
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
|
||||
import com.tangem.core.ui.components.containers.pullToRefresh.TangemPullToRefreshContainer
|
||||
import com.tangem.core.ui.components.rememberIsKeyboardVisible
|
||||
import com.tangem.core.ui.components.sheetscaffold.*
|
||||
import com.tangem.core.ui.components.snackbar.CopiedTextSnackbar
|
||||
import com.tangem.core.ui.components.snackbar.TangemSnackbar
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.core.ui.extensions.softLayerShadow
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalMainBottomSheetColor
|
||||
import com.tangem.core.ui.res.LocalWindowSize
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.MainScreenTestTags
|
||||
import com.tangem.core.ui.test.MarketTooltipTestTags
|
||||
import com.tangem.core.ui.utils.TangemSharedTransitionLayout
|
||||
import com.tangem.core.ui.utils.toPx
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewDataLegacy.accountScreenState
|
||||
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewDataLegacy.accountScreenWithEmptyTokensState
|
||||
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewDataLegacy.walletScreenState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.holder.TxHistoryStateHolder
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.common.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.nftCollections
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.organizeTokensButton
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.marketPriceBlock
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimator
|
||||
import com.tangem.features.promobanners.api.PromoBannersBlockComponent
|
||||
import com.tangem.features.tangempay.component.TangemPayMainBlockComponent
|
||||
import com.tangem.features.tangempay.entity.TangemPayMainUM
|
||||
import com.tangem.features.virtualaccount.main.component.VirtualAccountMainBlockComponent
|
||||
import com.tangem.features.virtualaccount.main.entity.VirtualAccountMainUM
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
internal fun WalletScreen(
|
||||
state: WalletScreenState,
|
||||
tangemPayComponent: TangemPayMainBlockComponent,
|
||||
virtualAccountComponent: VirtualAccountMainBlockComponent,
|
||||
promoBannersBlockComponent: PromoBannersBlockComponent? = null,
|
||||
bottomSheetContent: @Composable (onExpandSheet: () -> Unit) -> Unit,
|
||||
bottomSheetHeaderHeightProvider: () -> Dp,
|
||||
onBottomSheetStateChange: (BottomSheetState) -> Unit,
|
||||
) {
|
||||
// It means that screen is still initializing
|
||||
if (state.selectedWalletIndex == NOT_INITIALIZED_WALLET_INDEX) return
|
||||
|
||||
val walletsListState = rememberLazyListState(initialFirstVisibleItemIndex = state.selectedWalletIndex)
|
||||
val snackbarHostState = remember(::SnackbarHostState)
|
||||
val isAutoScroll = remember { mutableStateOf(value = false) }
|
||||
|
||||
WalletContent(
|
||||
state = state,
|
||||
tangemPayComponent = tangemPayComponent,
|
||||
virtualAccountComponent = virtualAccountComponent,
|
||||
walletsListState = walletsListState,
|
||||
snackbarHostState = snackbarHostState,
|
||||
isAutoScroll = isAutoScroll,
|
||||
onAutoScrollReset = { isAutoScroll.value = false },
|
||||
promoBannersBlockComponent = promoBannersBlockComponent,
|
||||
bottomSheetContent = bottomSheetContent,
|
||||
bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider,
|
||||
onBottomSheetStateChange = onBottomSheetStateChange,
|
||||
)
|
||||
|
||||
WalletEventEffectLegacy(
|
||||
walletsListState = walletsListState,
|
||||
snackbarHostState = snackbarHostState,
|
||||
event = state.event,
|
||||
onAutoScrollSet = { isAutoScroll.value = true },
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "LongParameterList", "CyclomaticComplexMethod")
|
||||
@Composable
|
||||
private fun WalletContent(
|
||||
state: WalletScreenState,
|
||||
tangemPayComponent: TangemPayMainBlockComponent,
|
||||
virtualAccountComponent: VirtualAccountMainBlockComponent,
|
||||
walletsListState: LazyListState,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
isAutoScroll: State<Boolean>,
|
||||
onAutoScrollReset: () -> Unit,
|
||||
promoBannersBlockComponent: PromoBannersBlockComponent? = null,
|
||||
bottomSheetHeaderHeightProvider: () -> Dp,
|
||||
onBottomSheetStateChange: (BottomSheetState) -> Unit,
|
||||
bottomSheetContent: @Composable (onExpandSheet: () -> Unit) -> Unit,
|
||||
) {
|
||||
/*
|
||||
* Don't pass key to remember, because it will brake scroll animation.
|
||||
* selectedWalletIndex will be changed in WalletsListEffects.
|
||||
*/
|
||||
val selectedWalletIndex by remember(state.selectedWalletIndex) { mutableIntStateOf(state.selectedWalletIndex) }
|
||||
val selectedWallet = state.wallets.getOrElse(selectedWalletIndex) { state.wallets[state.selectedWalletIndex] }
|
||||
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
val scaffoldContent: @Composable (PaddingValues?) -> Unit = { paddingValues ->
|
||||
val movableItemModifier = Modifier.changeWalletAnimator(walletsListState)
|
||||
|
||||
val lazyTxHistoryItems = (selectedWallet as? TxHistoryStateHolder)?.let { walletState ->
|
||||
(walletState.txHistoryState as? TxHistoryState.Content)?.contentItems?.collectAsLazyPagingItems()
|
||||
}
|
||||
|
||||
val txHistoryItems by remember(selectedWallet.walletCardState.id, lazyTxHistoryItems?.itemCount) {
|
||||
mutableStateOf(value = lazyTxHistoryItems)
|
||||
}
|
||||
|
||||
val betweenItemsPadding = TangemTheme.dimens.spacing12
|
||||
val horizontalPadding = TangemTheme.dimens.spacing16
|
||||
val itemModifier = movableItemModifier
|
||||
.padding(top = betweenItemsPadding)
|
||||
.padding(horizontal = horizontalPadding)
|
||||
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
||||
val marketHintAproxHeight = with(LocalDensity.current) {
|
||||
TangemTheme.typography.caption2.lineHeight.toDp() * 2
|
||||
} + 40.dp
|
||||
|
||||
val contentPadding = paddingValues?.let {
|
||||
PaddingValues(
|
||||
bottom = it.calculateBottomPadding() + marketHintAproxHeight + 52.dp,
|
||||
)
|
||||
} ?: PaddingValues(bottom = TangemTheme.dimens.spacing92 + bottomBarHeight)
|
||||
|
||||
TangemSharedTransitionLayout {
|
||||
LazyColumn(
|
||||
modifier = Modifier.testTag(MainScreenTestTags.SCREEN_CONTAINER),
|
||||
state = listState,
|
||||
contentPadding = contentPadding,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
item(
|
||||
// !!! Type of the key should be saveable via Bundle on Android !!!
|
||||
key = state.wallets.map { it.walletCardState.id.stringValue },
|
||||
contentType = state.wallets.map { it.walletCardState.id },
|
||||
) {
|
||||
WalletsList(
|
||||
modifier = Modifier.animateItem(fadeInSpec = null, fadeOutSpec = null),
|
||||
lazyListState = walletsListState,
|
||||
wallets = state.wallets.map(WalletState::walletCardState).toImmutableList(),
|
||||
isBalanceHidden = state.isHidingMode,
|
||||
)
|
||||
}
|
||||
|
||||
when (selectedWallet) {
|
||||
is WalletState.MultiCurrency -> {
|
||||
actions(
|
||||
actions = selectedWallet.buttons,
|
||||
selectedWalletIndex = selectedWalletIndex,
|
||||
modifier = movableItemModifier.padding(top = betweenItemsPadding),
|
||||
)
|
||||
}
|
||||
is WalletState.SingleCurrency -> {
|
||||
lazyActions(
|
||||
actions = selectedWallet.buttons,
|
||||
selectedWalletIndex = selectedWalletIndex,
|
||||
modifier = movableItemModifier.padding(top = betweenItemsPadding),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
notifications(configs = selectedWallet.warnings, modifier = itemModifier)
|
||||
|
||||
promoBannersBlockComponent?.let { component ->
|
||||
item(key = "PromoBannersBlock") {
|
||||
component.ContentWithPadding(
|
||||
horizontalItemPadding = 12.dp,
|
||||
modifier = itemModifier,
|
||||
walletId = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
tangemPayItem(
|
||||
modifier = itemModifier,
|
||||
state = selectedWallet,
|
||||
isHidingMode = state.isHidingMode,
|
||||
tangemPayComponent = tangemPayComponent,
|
||||
)
|
||||
|
||||
virtualAccountItem(
|
||||
modifier = itemModifier,
|
||||
state = selectedWallet,
|
||||
isHidingMode = state.isHidingMode,
|
||||
virtualAccountComponent = virtualAccountComponent,
|
||||
)
|
||||
|
||||
(selectedWallet as? WalletState.SingleCurrency)?.let { walletState ->
|
||||
walletState.marketPriceBlockState?.let { marketPriceBlockState ->
|
||||
marketPriceBlock(state = marketPriceBlockState, modifier = itemModifier)
|
||||
}
|
||||
if (walletState is WalletState.SingleCurrency.Content) {
|
||||
expressTransactionsItemsLegacy(
|
||||
expressTxs = walletState.expressTxsToDisplay,
|
||||
modifier = itemModifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
contentItems(
|
||||
state = selectedWallet,
|
||||
txHistoryItems = txHistoryItems,
|
||||
isBalanceHidden = state.isHidingMode,
|
||||
modifier = movableItemModifier,
|
||||
)
|
||||
|
||||
nftCollections(state = selectedWallet, itemModifier = itemModifier)
|
||||
|
||||
organizeTokens(state = selectedWallet, itemModifier = itemModifier)
|
||||
}
|
||||
}
|
||||
|
||||
ShowBottomSheet(bottomSheetConfig = selectedWallet.bottomSheetConfig)
|
||||
|
||||
WalletsListEffects(
|
||||
lazyListState = walletsListState,
|
||||
selectedWalletIndex = selectedWalletIndex,
|
||||
onUserScroll = onAutoScrollReset,
|
||||
onIndexChange = { index ->
|
||||
// Auto scroll must not change wallet
|
||||
if (isAutoScroll.value) {
|
||||
state.onWalletChange(index, true)
|
||||
} else {
|
||||
state.onWalletChange(index, false)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
BaseScaffoldWithMarkets(
|
||||
state = state,
|
||||
listState = listState,
|
||||
selectedWallet = selectedWallet,
|
||||
snackbarHostState = snackbarHostState,
|
||||
bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider,
|
||||
onBottomSheetStateChange = onBottomSheetStateChange,
|
||||
bottomSheetContent = bottomSheetContent,
|
||||
content = scaffoldContent,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList", "LongMethod", "CyclomaticComplexMethod")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private inline fun BaseScaffoldWithMarkets(
|
||||
state: WalletScreenState,
|
||||
listState: LazyListState,
|
||||
selectedWallet: WalletState,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
bottomSheetHeaderHeightProvider: () -> Dp,
|
||||
noinline onBottomSheetStateChange: (BottomSheetState) -> Unit,
|
||||
crossinline bottomSheetContent: @Composable (onExpandSheet: () -> Unit) -> Unit,
|
||||
crossinline content: @Composable (PaddingValues) -> Unit,
|
||||
) {
|
||||
val isKeyboardVisible by rememberIsKeyboardVisible()
|
||||
|
||||
val density = LocalDensity.current
|
||||
val bottomBarHeight = with(density) { WindowInsets.systemBars.getBottom(density = this).toDp() }
|
||||
val statusBarHeight = with(density) { WindowInsets.statusBars.getTop(density = this).toDp() }
|
||||
val peekHeight = bottomSheetHeaderHeightProvider() + handComposableComponentHeight + bottomBarHeight
|
||||
val maxHeight = LocalWindowSize.current.height
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val background = TangemTheme.colors.background.tertiary
|
||||
|
||||
val bottomSheetState = rememberTangemStandardBottomSheetState()
|
||||
val scaffoldState = rememberTangemBottomSheetScaffoldState(
|
||||
bottomSheetState = bottomSheetState,
|
||||
snackbarHostState = snackbarHostState,
|
||||
)
|
||||
|
||||
val showMarketsHint by remember {
|
||||
derivedStateOf {
|
||||
// Show hint only when there are items in the list
|
||||
// and when there a no items to scroll
|
||||
listState.layoutInfo.totalItemsCount > 0 &&
|
||||
!listState.canScrollBackward && !listState.canScrollForward ||
|
||||
listState.canScrollBackward && !listState.canScrollForward
|
||||
}
|
||||
}
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalMainBottomSheetColor provides remember(background) { mutableStateOf(background) },
|
||||
) {
|
||||
val backgroundColor by LocalMainBottomSheetColor.current
|
||||
var isSearchFieldFocused by remember { mutableStateOf(false) }
|
||||
val isNavBarVisible = remember { mutableStateOf(true) }
|
||||
|
||||
BottomSheetStateEffects(
|
||||
bottomSheetState = bottomSheetState,
|
||||
onBottomSheetStateChange = onBottomSheetStateChange,
|
||||
navigationBarVisible = isNavBarVisible,
|
||||
isSearchFieldFocused = isSearchFieldFocused,
|
||||
)
|
||||
|
||||
Box {
|
||||
TangemBottomSheetScaffold(
|
||||
snackbarHost = {
|
||||
WalletSnackbarHost(
|
||||
snackbarHostState = it,
|
||||
event = state.event,
|
||||
modifier = Modifier
|
||||
.padding(bottom = TangemTheme.dimens.spacing4)
|
||||
.navigationBarsPadding(),
|
||||
)
|
||||
},
|
||||
sheetPeekHeight = peekHeight,
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
scaffoldState = scaffoldState,
|
||||
bottomSheet = {
|
||||
CustomBottomSheet(
|
||||
state = scaffoldState.bottomSheetState,
|
||||
peekHeight = peekHeight,
|
||||
modifier = Modifier
|
||||
.softLayerShadow(
|
||||
radius = 8.dp,
|
||||
color = Color.Black.copy(
|
||||
alpha = if (isSystemInDarkTheme()) .16f else .08f,
|
||||
),
|
||||
shape = TangemTheme.shapes.bottomSheetLarge,
|
||||
offset = DpOffset(x = 0.dp, y = (-4).dp),
|
||||
isAlphaContentClip = true,
|
||||
)
|
||||
.clip(TangemTheme.shapes.bottomSheetLarge)
|
||||
.background(backgroundColor),
|
||||
content = {
|
||||
// hide bottom sheet when back pressed
|
||||
BackHandler(
|
||||
isKeyboardVisible.not() &&
|
||||
bottomSheetState.currentValue == TangemSheetValue.Expanded,
|
||||
) {
|
||||
coroutineScope.launch { bottomSheetState.partialExpand() }
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
// expand bottom sheet when clicked on the drag handle
|
||||
.clickable(
|
||||
enabled = bottomSheetState.currentValue == TangemSheetValue.PartiallyExpanded,
|
||||
indication = null,
|
||||
interactionSource = null,
|
||||
) {
|
||||
coroutineScope.launch { bottomSheetState.expand() }
|
||||
}
|
||||
.sizeIn(maxHeight = maxHeight - statusBarHeight),
|
||||
) {
|
||||
TangemBottomSheetDraggableHeaderLegacy(backgroundColor)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.onFocusChanged {
|
||||
isSearchFieldFocused = it.isFocused
|
||||
},
|
||||
) {
|
||||
bottomSheetContent {
|
||||
coroutineScope.launch { bottomSheetState.expand() }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
content = { paddingValues ->
|
||||
Box {
|
||||
MarketsHint(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(bottom = peekHeight + 12.dp)
|
||||
.fillMaxWidth(fraction = .4f),
|
||||
isVisible = showMarketsHint,
|
||||
)
|
||||
|
||||
Column {
|
||||
WalletTopBar(config = state.topBarConfig)
|
||||
TangemPullToRefreshContainer(config = selectedWallet.pullToRefreshConfig) {
|
||||
content(paddingValues)
|
||||
}
|
||||
}
|
||||
|
||||
BottomSheetScrim(
|
||||
color = Color.Black.copy(alpha = .40f),
|
||||
visible = bottomSheetState.targetValue == TangemSheetValue.Expanded,
|
||||
onDismissRequest = {
|
||||
coroutineScope.launch { bottomSheetState.partialExpand() }
|
||||
},
|
||||
)
|
||||
|
||||
MarketsTooltip(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(bottom = 8.dp)
|
||||
.padding(horizontal = 12.dp)
|
||||
.fillMaxWidth(),
|
||||
isVisible = state.showMarketsOnboarding,
|
||||
availableHeight = maxHeight,
|
||||
bottomSheetState = bottomSheetState,
|
||||
onCloseClick = state.onDismissMarketsTooltip,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
AnimatedVisibility(
|
||||
modifier = Modifier.align(Alignment.BottomCenter),
|
||||
visible = isNavBarVisible.value,
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.background(backgroundColor)
|
||||
.height(bottomBarHeight)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(state.showMarketsOnboarding, bottomSheetState.targetValue) {
|
||||
if (state.showMarketsOnboarding && bottomSheetState.targetValue == TangemSheetValue.Expanded) {
|
||||
state.onDismissMarketsTooltip()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MarketsTooltip(
|
||||
availableHeight: Dp,
|
||||
bottomSheetState: TangemSheetState,
|
||||
isVisible: Boolean,
|
||||
onCloseClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val density = LocalDensity.current
|
||||
val tooltipOffset by remember {
|
||||
derivedStateOf {
|
||||
val bottomSheetOffset = try {
|
||||
// Can throw exception during the first composition
|
||||
with(density) { bottomSheetState.requireOffset().toDp() }
|
||||
} catch (e: Exception) {
|
||||
0.dp
|
||||
}
|
||||
|
||||
bottomSheetOffset - availableHeight
|
||||
}
|
||||
}
|
||||
|
||||
var visible by remember { mutableStateOf(value = false) }
|
||||
LaunchedEffect(isVisible) {
|
||||
if (isVisible) {
|
||||
delay(timeMillis = 300)
|
||||
}
|
||||
|
||||
visible = isVisible
|
||||
}
|
||||
|
||||
val slideOffset = 40.dp.toPx()
|
||||
AnimatedVisibility(
|
||||
modifier = modifier
|
||||
.offset { IntOffset(x = 0, y = tooltipOffset.roundToPx()) }
|
||||
.testTag(MarketTooltipTestTags.CONTAINER),
|
||||
visible = visible,
|
||||
enter = slideIn(
|
||||
animationSpec = spring(
|
||||
stiffness = Spring.StiffnessLow,
|
||||
visibilityThreshold = IntOffset.VisibilityThreshold,
|
||||
),
|
||||
initialOffset = { _ -> IntOffset(y = -slideOffset.roundToInt(), x = 0) },
|
||||
) + fadeIn(),
|
||||
exit = fadeOut(),
|
||||
) {
|
||||
MarketsTooltipContent(onCloseClick = onCloseClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MarketsHint(isVisible: Boolean, modifier: Modifier = Modifier) {
|
||||
AnimatedVisibility(
|
||||
modifier = modifier,
|
||||
visible = isVisible,
|
||||
enter = fadeIn(animationSpec = tween(durationMillis = 500)),
|
||||
exit = fadeOut(),
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(space = 4.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.markets_hint),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
Icon(
|
||||
modifier = Modifier.size(size = 24.dp),
|
||||
painter = painterResource(id = R.drawable.ic_chevron_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MarketsTooltipContent(onCloseClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
val backgroundColor = TangemTheme.colors.background.action
|
||||
val tipDpSize = DpSize(width = 20.dp, height = 8.dp)
|
||||
val tooltipShape = remember(tipDpSize) { TooltipShape(cornerRadius = 16.dp, tipSize = tipDpSize) }
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.shadow(
|
||||
elevation = TangemTheme.dimens.elevation12,
|
||||
shape = tooltipShape,
|
||||
clip = false,
|
||||
ambientColor = Color.Black.copy(alpha = 0.7f),
|
||||
)
|
||||
.background(backgroundColor, tooltipShape)
|
||||
.clickable(interactionSource = null, indication = null, onClick = {})
|
||||
.padding(all = 12.dp)
|
||||
.padding(bottom = tipDpSize.height),
|
||||
horizontalArrangement = Arrangement.spacedBy(space = 12.dp),
|
||||
verticalAlignment = Alignment.Top,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(size = 18.dp),
|
||||
painter = painterResource(id = R.drawable.ic_plus_18),
|
||||
tint = Color.Unspecified,
|
||||
contentDescription = null,
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalArrangement = Arrangement.spacedBy(space = 2.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(id = R.string.markets_tooltip_v2_title),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Text(
|
||||
text = stringResourceSafe(id = R.string.markets_tooltip_message),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(size = 16.dp)
|
||||
.clickable(
|
||||
interactionSource = null,
|
||||
indication = null,
|
||||
onClick = onCloseClick,
|
||||
)
|
||||
.testTag(MarketTooltipTestTags.CLOSE_BUTTON),
|
||||
painter = painterResource(id = R.drawable.ic_close_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class TooltipShape(
|
||||
private val cornerRadius: Dp,
|
||||
private val tipSize: DpSize,
|
||||
) : Shape {
|
||||
override fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density): Outline {
|
||||
val cornerRadiusPx = with(density) { cornerRadius.toPx() }
|
||||
val tipWidth = with(density) { tipSize.width.toPx() }
|
||||
val tipHeight = with(density) { tipSize.height.toPx() }
|
||||
val bodyHeight = size.height - tipHeight
|
||||
|
||||
val path = Path().apply {
|
||||
addRoundRect(
|
||||
RoundRect(
|
||||
rect = Rect(left = 0f, top = 0f, right = size.width, bottom = bodyHeight),
|
||||
cornerRadius = CornerRadius(cornerRadiusPx),
|
||||
),
|
||||
)
|
||||
moveTo(size.width / 2 - tipWidth / 2, bodyHeight)
|
||||
lineTo(size.width / 2, size.height)
|
||||
lineTo(size.width / 2 + tipWidth / 2, bodyHeight)
|
||||
close()
|
||||
}
|
||||
return Outline.Generic(path)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomSheetScrim(color: Color, visible: Boolean, onDismissRequest: () -> Unit) {
|
||||
val alpha by animateFloatAsState(
|
||||
targetValue = if (visible) 1f else 0f,
|
||||
animationSpec = tween(),
|
||||
label = "scrim",
|
||||
)
|
||||
val dismissSheet = if (visible) {
|
||||
Modifier
|
||||
.pointerInput(onDismissRequest) {
|
||||
detectTapGestures {
|
||||
onDismissRequest()
|
||||
}
|
||||
}
|
||||
.clearAndSetSemantics {}
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
Canvas(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.then(dismissSheet),
|
||||
) {
|
||||
drawRect(color = color, alpha = alpha)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("CyclomaticComplexMethod", "MagicNumber", "LongMethod")
|
||||
@Composable
|
||||
private fun BottomSheetStateEffects(
|
||||
bottomSheetState: TangemSheetState,
|
||||
navigationBarVisible: MutableState<Boolean>,
|
||||
onBottomSheetStateChange: (BottomSheetState) -> Unit,
|
||||
isSearchFieldFocused: Boolean,
|
||||
) {
|
||||
LaunchedEffect(bottomSheetState.targetValue) {
|
||||
when (bottomSheetState.targetValue) {
|
||||
TangemSheetValue.Hidden,
|
||||
TangemSheetValue.Expanded,
|
||||
-> navigationBarVisible.value = false
|
||||
TangemSheetValue.PartiallyExpanded,
|
||||
-> navigationBarVisible.value = true
|
||||
}
|
||||
}
|
||||
|
||||
// expand bottom sheet when keyboard appears
|
||||
val isKeyboardVisible by rememberIsKeyboardVisible()
|
||||
|
||||
LaunchedEffect(isKeyboardVisible) {
|
||||
if (isKeyboardVisible && isSearchFieldFocused) {
|
||||
bottomSheetState.expand()
|
||||
}
|
||||
}
|
||||
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
// hide keyboard when bottom sheet is about to be hidden
|
||||
LaunchedEffect(Unit) {
|
||||
snapshotFlow {
|
||||
bottomSheetState.currentValue == TangemSheetValue.Expanded &&
|
||||
bottomSheetState.targetValue == TangemSheetValue.PartiallyExpanded
|
||||
}.collect { sheetHasBeenHidden ->
|
||||
if (sheetHasBeenHidden) {
|
||||
keyboardController?.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val isSheetHidden = bottomSheetState.targetValue == TangemSheetValue.PartiallyExpanded
|
||||
LaunchedEffect(isSheetHidden) {
|
||||
onBottomSheetStateChange(
|
||||
if (isSheetHidden) {
|
||||
BottomSheetState.COLLAPSED
|
||||
} else {
|
||||
BottomSheetState.EXPANDED
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WalletSnackbarHost(
|
||||
snackbarHostState: SnackbarHostState,
|
||||
event: StateEvent<WalletEvent>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
SnackbarHost(hostState = snackbarHostState, modifier = modifier) { data ->
|
||||
if (event is StateEvent.Triggered && event.data is WalletEvent.CopyAddress) {
|
||||
CopiedTextSnackbar(data)
|
||||
} else {
|
||||
TangemSnackbar(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun LazyListScope.organizeTokens(state: WalletState, itemModifier: Modifier) {
|
||||
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) {
|
||||
(state as? WalletState.MultiCurrency)?.let {
|
||||
nftCollections(
|
||||
modifier = itemModifier,
|
||||
state = it.nftState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun LazyListScope.tangemPayItem(
|
||||
state: WalletState,
|
||||
isHidingMode: Boolean,
|
||||
tangemPayComponent: TangemPayMainBlockComponent,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (state !is WalletState.MultiCurrency) return
|
||||
|
||||
with(tangemPayComponent) {
|
||||
tangemPayMainContent(modifier = modifier, state = state.tangemPayMainUM, isBalanceHidden = isHidingMode)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun LazyListScope.virtualAccountItem(
|
||||
state: WalletState,
|
||||
isHidingMode: Boolean,
|
||||
virtualAccountComponent: VirtualAccountMainBlockComponent,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (state !is WalletState.MultiCurrency) return
|
||||
|
||||
with(virtualAccountComponent) {
|
||||
virtualAccountMainContent(
|
||||
modifier = modifier,
|
||||
state = state.virtualAccountMainUM,
|
||||
isBalanceHidden = isHidingMode,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ShowBottomSheet(bottomSheetConfig: TangemBottomSheetConfig?) {
|
||||
if (bottomSheetConfig != null) {
|
||||
when (bottomSheetConfig.content) {
|
||||
is ChooseAddressBottomSheetConfig -> ChooseAddressBottomSheet(config = bottomSheetConfig)
|
||||
is ExpressStatusBottomSheetConfig -> ExpressStatusBottomSheet(config = bottomSheetConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun WalletScreen_Preview(@PreviewParameter(WalletScreenPreviewProvider::class) data: WalletScreenState) {
|
||||
TangemThemePreview {
|
||||
WalletScreen(
|
||||
state = data,
|
||||
tangemPayComponent = object : TangemPayMainBlockComponent {
|
||||
override fun LazyListScope.tangemPayMainContent(
|
||||
state: TangemPayMainUM,
|
||||
isBalanceHidden: Boolean,
|
||||
modifier: Modifier,
|
||||
) {
|
||||
}
|
||||
},
|
||||
virtualAccountComponent = object : VirtualAccountMainBlockComponent {
|
||||
override fun LazyListScope.virtualAccountMainContent(
|
||||
state: VirtualAccountMainUM,
|
||||
isBalanceHidden: Boolean,
|
||||
modifier: Modifier,
|
||||
) {
|
||||
}
|
||||
},
|
||||
bottomSheetContent = {
|
||||
Text("Markets Content")
|
||||
},
|
||||
bottomSheetHeaderHeightProvider = { 10.dp },
|
||||
onBottomSheetStateChange = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class WalletScreenPreviewProvider : PreviewParameterProvider<WalletScreenState> {
|
||||
override val values: Sequence<WalletScreenState>
|
||||
get() = sequenceOf(
|
||||
walletScreenState,
|
||||
walletScreenState.copy(selectedWalletIndex = 1),
|
||||
accountScreenState.copy(selectedWalletIndex = 1),
|
||||
accountScreenWithEmptyTokensState.copy(selectedWalletIndex = 1),
|
||||
)
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -13,11 +13,9 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.lerp
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.haze.hazeEffectTangem
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
|
|
@ -32,9 +30,7 @@ import com.tangem.core.ui.haptic.TangemHapticEffect
|
|||
import com.tangem.core.ui.res.*
|
||||
import com.tangem.core.ui.test.MainScreenTestTags
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewDataLegacy
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTopBarConfig
|
||||
import com.tangem.utils.annotations.RemoveWithToggle
|
||||
import dev.chrisbanes.haze.HazeProgressive
|
||||
import dev.chrisbanes.haze.HazeTint
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
@ -114,46 +110,6 @@ internal fun WalletTopBar(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wallet screen top bar
|
||||
*
|
||||
* @param config component config
|
||||
*/
|
||||
@Suppress("MagicNumber")
|
||||
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]")
|
||||
@RemoveWithToggle("APP_REDESIGN_ENABLED")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
internal fun WalletTopBar(config: WalletTopBarConfig) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Icon(painter = painterResource(id = R.drawable.img_tangem_logo_90_24), contentDescription = null)
|
||||
},
|
||||
actions = {
|
||||
config.endActions.forEach { action ->
|
||||
action.onClick?.let { onClick ->
|
||||
IconButton(onClick = onClick) {
|
||||
Icon(
|
||||
painter = painterResource(id = action.iconRes),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.rotate(if (action.iconRes == R.drawable.ic_more_default_24) 90f else 0f)
|
||||
.testTag(MainScreenTestTags.MORE_BUTTON),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
titleContentColor = TangemTheme.colors.icon.primary1,
|
||||
actionIconContentColor = TangemTheme.colors.icon.primary1,
|
||||
),
|
||||
scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(),
|
||||
modifier = Modifier.testTag(MainScreenTestTags.TOP_BAR),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Modifier.hazeEffectTangemTopBar(behavior: TangemCollapsingAppBarBehavior): Modifier {
|
||||
val rootBackground by LocalRootBackgroundColor.current
|
||||
|
|
@ -169,15 +125,6 @@ private fun Modifier.hazeEffectTangemTopBar(behavior: TangemCollapsingAppBarBeha
|
|||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview_WalletTopBar() {
|
||||
TangemThemePreview {
|
||||
WalletTopBar(config = WalletPreviewDataLegacy.topBarConfig)
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
|
|
|
|||
|
|
@ -73,7 +73,6 @@ class SetTokenListTransformerTest {
|
|||
clickIntents = mockk<WalletClickIntents>(relaxed = true),
|
||||
shouldShowMainPromo = false,
|
||||
isAccountsModeEnabled = false,
|
||||
isRedesignEnabled = true,
|
||||
isMultipleCardsEnabled = false,
|
||||
isPolymarketEnabled = false,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue