Updated on 2026-08-14
This commit is contained in:
commit
a22e77ebe8
669 changed files with 27695 additions and 7905 deletions
|
|
@ -8,4 +8,6 @@ package com.tangem.features.wallet.featuretoggles
|
|||
interface WalletFeatureToggles {
|
||||
|
||||
val isAddAndManageTokensEnabled: Boolean
|
||||
|
||||
val isAddFundsStage1Enabled: Boolean
|
||||
}
|
||||
|
|
@ -105,8 +105,8 @@ dependencies {
|
|||
implementation(projects.domain.offramp)
|
||||
implementation(projects.domain.onramp)
|
||||
implementation(projects.domain.onramp.models)
|
||||
implementation(projects.domain.promo)
|
||||
implementation(projects.domain.promo.models)
|
||||
implementation(projects.domain.stories)
|
||||
implementation(projects.domain.stories.models)
|
||||
implementation(projects.domain.quotes)
|
||||
implementation(projects.domain.settings)
|
||||
implementation(projects.domain.staking)
|
||||
|
|
@ -119,6 +119,7 @@ dependencies {
|
|||
implementation(projects.domain.wallets)
|
||||
implementation(projects.domain.wallets.models)
|
||||
implementation(projects.domain.notifications)
|
||||
implementation(projects.domain.pushNotificationPreferences)
|
||||
implementation(projects.domain.transaction)
|
||||
implementation(projects.domain.yieldSupply)
|
||||
implementation(projects.domain.yieldSupply.models)
|
||||
|
|
@ -136,6 +137,7 @@ dependencies {
|
|||
implementation(projects.features.onboardingV2.api)
|
||||
implementation(projects.features.onramp.api)
|
||||
implementation(projects.features.pushNotifications.api)
|
||||
implementation(projects.features.pushNotificationSettings.api)
|
||||
implementation(projects.features.swap.api)
|
||||
implementation(projects.features.tester.api)
|
||||
implementation(projects.features.tokendetails.api)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.wallet.child.managetokens
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.slot.childSlot
|
||||
|
|
@ -48,9 +49,11 @@ internal class AddAndManageBottomSheetComponent(
|
|||
@Composable
|
||||
override fun BottomSheet() {
|
||||
val portfolioSelectorSlot by portfolioSelectorSlot.subscribeAsState()
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
|
||||
AddAndManageBottomSheetContent(
|
||||
onAddTokensClick = model::onAddTokensClick,
|
||||
shouldShowOrganizeButton = state.shouldShowOrganize,
|
||||
onOrganizeTokensClick = model::onOrganizeTokensClick,
|
||||
onDismiss = ::dismiss,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
|
|||
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.hasMultiCurrencyAccount
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.feature.wallet.child.managetokens.AddAndManageBottomSheetComponent
|
||||
import com.tangem.feature.wallet.child.managetokens.analytics.PortfolioAnalyticsEvent
|
||||
|
|
@ -14,7 +16,10 @@ import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioFetcher
|
|||
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorComponent
|
||||
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorController
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -25,18 +30,20 @@ internal class AddAndManageModel @Inject constructor(
|
|||
private val portfolioFetcherFactory: PortfolioFetcher.Factory,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
val portfolioSelectorController: PortfolioSelectorController,
|
||||
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<AddAndManageBottomSheetComponent.Params>()
|
||||
|
||||
val portfolioSelectorNavigation: SlotNavigation<Unit> = SlotNavigation()
|
||||
|
||||
val portfolioFetcher: PortfolioFetcher by lazy {
|
||||
portfolioFetcherFactory.create(
|
||||
mode = PortfolioFetcher.Mode.Wallet(params.userWalletId),
|
||||
scope = modelScope,
|
||||
)
|
||||
}
|
||||
val state: StateFlow<AddAndManageState>
|
||||
field = MutableStateFlow(AddAndManageState(shouldShowOrganize = true))
|
||||
|
||||
val portfolioSelectorCallback = object : PortfolioSelectorComponent.BottomSheetCallback {
|
||||
override val onDismiss: () -> Unit = { portfolioSelectorNavigation.dismiss() }
|
||||
|
|
@ -45,6 +52,7 @@ internal class AddAndManageModel @Inject constructor(
|
|||
|
||||
init {
|
||||
observeAccountSelection()
|
||||
updateShouldShowOrganizeButtonState()
|
||||
}
|
||||
|
||||
fun onAddTokensClick() {
|
||||
|
|
@ -85,4 +93,12 @@ internal class AddAndManageModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateShouldShowOrganizeButtonState() {
|
||||
modelScope.launch {
|
||||
val accountStatusesList = singleAccountStatusListSupplier.getSyncOrNull(params.userWalletId)
|
||||
val hasMultiCurrencyAccount = accountStatusesList?.hasMultiCurrencyAccount() == true
|
||||
state.update { it.copy(shouldShowOrganize = hasMultiCurrencyAccount) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.feature.wallet.child.managetokens.model
|
||||
|
||||
data class AddAndManageState(
|
||||
val shouldShowOrganize: Boolean,
|
||||
)
|
||||
|
|
@ -31,6 +31,7 @@ import com.tangem.core.ui.res.TangemThemePreview
|
|||
@Composable
|
||||
internal fun AddAndManageBottomSheetContent(
|
||||
onAddTokensClick: () -> Unit,
|
||||
shouldShowOrganizeButton: Boolean,
|
||||
onOrganizeTokensClick: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
|
|
@ -53,6 +54,7 @@ internal fun AddAndManageBottomSheetContent(
|
|||
content = {
|
||||
AddAndManageContent(
|
||||
onAddTokensClick = onAddTokensClick,
|
||||
shouldShowOrganizeButton = shouldShowOrganizeButton,
|
||||
onOrganizeTokensClick = onOrganizeTokensClick,
|
||||
)
|
||||
},
|
||||
|
|
@ -60,7 +62,11 @@ internal fun AddAndManageBottomSheetContent(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun AddAndManageContent(onAddTokensClick: () -> Unit, onOrganizeTokensClick: () -> Unit) {
|
||||
private fun AddAndManageContent(
|
||||
onAddTokensClick: () -> Unit,
|
||||
shouldShowOrganizeButton: Boolean,
|
||||
onOrganizeTokensClick: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(
|
||||
start = 16.dp,
|
||||
|
|
@ -75,23 +81,25 @@ private fun AddAndManageContent(onAddTokensClick: () -> Unit, onOrganizeTokensCl
|
|||
onClick = onAddTokensClick,
|
||||
modifier = Modifier.roundedShapeItemDecoration(
|
||||
currentIndex = 0,
|
||||
lastIndex = 1,
|
||||
addDefaultPadding = false,
|
||||
backgroundColor = TangemTheme.colors.background.action,
|
||||
),
|
||||
)
|
||||
AddAndManageRow(
|
||||
iconRes = R.drawable.ic_filter_default_24,
|
||||
title = ResR.string.add_and_manage_sheet_organize_title,
|
||||
subtitle = ResR.string.add_and_manage_sheet_organize_subtitle,
|
||||
onClick = onOrganizeTokensClick,
|
||||
modifier = Modifier.roundedShapeItemDecoration(
|
||||
currentIndex = 1,
|
||||
lastIndex = 1,
|
||||
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,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -152,6 +160,7 @@ private fun AddAndManageBottomSheetContent_Preview() {
|
|||
TangemThemePreview {
|
||||
AddAndManageContent(
|
||||
onAddTokensClick = {},
|
||||
shouldShowOrganizeButton = true,
|
||||
onOrganizeTokensClick = {},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ 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.core.ui.ds.button.TangemButtonShape
|
||||
import com.tangem.core.ui.ds.button.TangemButtonSize
|
||||
import com.tangem.core.ui.ds.button.TangemButtonType
|
||||
import com.tangem.core.ui.ds.button.TangemButtonUM
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
|
|
@ -268,11 +270,15 @@ internal class OrganizeTokensModel @Inject constructor(
|
|||
text = resourceReference(R.string.common_cancel),
|
||||
onClick = ::onCancelClick,
|
||||
type = TangemButtonType.Secondary,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
size = TangemButtonSize.X12,
|
||||
),
|
||||
applyButton = TangemButtonUM(
|
||||
text = resourceReference(R.string.common_apply),
|
||||
onClick = ::onApplyClick,
|
||||
type = TangemButtonType.Primary,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
size = TangemButtonSize.X12,
|
||||
),
|
||||
scrollListToTop = consumedEvent(),
|
||||
isBalanceHidden = true,
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ internal class OrganizeTokensListConverter(
|
|||
return value.accountStatuses
|
||||
.asSequence()
|
||||
.filterCryptoPortfolio()
|
||||
.filter { it.tokenList !is TokenList.Empty }
|
||||
.flatMap { accountStatus ->
|
||||
buildList {
|
||||
addIf(
|
||||
|
|
|
|||
|
|
@ -1,13 +1,22 @@
|
|||
package com.tangem.feature.wallet.child.organizetokens.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
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.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.ds.contextmenu.TangemContextMenu
|
||||
import com.tangem.core.ui.ds.contextmenu.TangemContextMenuCheckboxItem
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.clickableSingle
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.child.organizetokens.entity.OrganizeTokensUM
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
|
|
@ -25,19 +34,104 @@ internal fun OrganizeDropDownMenu(
|
|||
offset = DpOffset.Zero,
|
||||
modifier = modifier,
|
||||
) {
|
||||
TangemContextMenuCheckboxItem(
|
||||
title = TextReference.Res(R.string.organize_tokens_sort_by_balance),
|
||||
isChecked = organizeMenuUM.isSortedByBalance,
|
||||
onClick = organizeMenuUM.onSortClick,
|
||||
)
|
||||
HorizontalDivider(
|
||||
thickness = 0.5.dp,
|
||||
color = TangemTheme.colors2.border.neutral.quaternary,
|
||||
)
|
||||
TangemContextMenuCheckboxItem(
|
||||
title = TextReference.Res(R.string.organize_tokens_group),
|
||||
isChecked = organizeMenuUM.isGrouped,
|
||||
onClick = organizeMenuUM.onGroupClick,
|
||||
Menu(
|
||||
organizeMenuUM = organizeMenuUM,
|
||||
onDropdownDismiss = onDropdownDismiss,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Menu(
|
||||
onDropdownDismiss: () -> Unit,
|
||||
organizeMenuUM: OrganizeTokensUM.OrganizeMenuUM,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier) {
|
||||
SortByBalanceMenuSection(
|
||||
organizeMenuUM = organizeMenuUM,
|
||||
onDropdownDismiss = onDropdownDismiss,
|
||||
)
|
||||
GroupTokensMenuSection(
|
||||
organizeMenuUM = organizeMenuUM,
|
||||
onDropdownDismiss = onDropdownDismiss,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SortByBalanceMenuSection(organizeMenuUM: OrganizeTokensUM.OrganizeMenuUM, onDropdownDismiss: () -> Unit) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.organize_tokens_sort_by_balance),
|
||||
style = TangemTheme.typography2.headingSemibold17,
|
||||
color = if (organizeMenuUM.isSortedByBalance) {
|
||||
TangemTheme.colors2.text.status.disabled
|
||||
} else {
|
||||
TangemTheme.colors2.text.neutral.primary
|
||||
},
|
||||
maxLines = 1,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.widthIn(238.dp)
|
||||
.clickableSingle(
|
||||
onClick = {
|
||||
organizeMenuUM.onSortClick()
|
||||
onDropdownDismiss()
|
||||
},
|
||||
enabled = !organizeMenuUM.isSortedByBalance,
|
||||
)
|
||||
.padding(vertical = TangemTheme.dimens2.x5, horizontal = TangemTheme.dimens2.x4),
|
||||
)
|
||||
|
||||
HorizontalDivider(
|
||||
thickness = 0.5.dp,
|
||||
color = TangemTheme.colors2.border.neutral.quaternary,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun GroupTokensMenuSection(organizeMenuUM: OrganizeTokensUM.OrganizeMenuUM, onDropdownDismiss: () -> Unit) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.widthIn(238.dp)
|
||||
.clickableSingle(
|
||||
onClick = {
|
||||
organizeMenuUM.onGroupClick()
|
||||
onDropdownDismiss()
|
||||
},
|
||||
)
|
||||
.padding(vertical = TangemTheme.dimens2.x5, horizontal = TangemTheme.dimens2.x4),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.organize_tokens_group),
|
||||
style = TangemTheme.typography2.headingSemibold17,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
maxLines = 1,
|
||||
)
|
||||
if (organizeMenuUM.isGrouped) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(TangemTheme.dimens2.x0_5)
|
||||
.size(TangemTheme.dimens2.x5)
|
||||
.background(
|
||||
color = TangemTheme.colors2.graphic.neutral.primary,
|
||||
shape = CircleShape,
|
||||
),
|
||||
) {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(
|
||||
ImageVector.vectorResource(R.drawable.ic_check_default_24),
|
||||
),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.neutral.primaryInverted,
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.padding(TangemTheme.dimens2.x0_5)
|
||||
.size(TangemTheme.dimens2.x4),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ import androidx.compose.ui.draw.shadow
|
|||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.platform.testTag
|
||||
|
|
@ -26,6 +27,7 @@ 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.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
|
|
@ -33,8 +35,14 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
|||
import com.tangem.core.ui.components.haze.hazeEffectTangem
|
||||
import com.tangem.core.ui.components.haze.hazeSourceTangem
|
||||
import com.tangem.core.ui.ds.button.TangemButton
|
||||
import com.tangem.core.ui.ds.image.TangemIcon
|
||||
import com.tangem.core.ui.ds.row.TangemRowContainer
|
||||
import com.tangem.core.ui.ds.row.TangemRowLayoutId
|
||||
import com.tangem.core.ui.ds.row.header.TangemHeaderRow
|
||||
import com.tangem.core.ui.ds.row.token.TangemTokenRow
|
||||
import com.tangem.core.ui.ds.row.internal.TangemRowTail
|
||||
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
|
||||
import com.tangem.core.ui.ds.row.token.internal.TokenRowEndContent
|
||||
import com.tangem.core.ui.ds.row.token.internal.TokenRowTitle
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBarActionContent
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBarActionUM
|
||||
|
|
@ -44,6 +52,7 @@ import com.tangem.core.ui.reordarable.ReorderableItem
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.core.ui.test.OrganizeTokensScreenTestTags
|
||||
import com.tangem.core.ui.test.TokenElementsTestTags
|
||||
import com.tangem.core.ui.utils.lazyListItemPosition
|
||||
import com.tangem.feature.wallet.child.organizetokens.entity.DraggableItem
|
||||
import com.tangem.feature.wallet.child.organizetokens.entity.OrganizeRowItemUM
|
||||
|
|
@ -93,7 +102,9 @@ internal fun OrganizeTokensContent(
|
|||
organizeMenuUM = organizeTokensUM.organizeMenuUM,
|
||||
showDropdownMenu = isShowDropdownMenu,
|
||||
onDropdownDismiss = { isShowDropdownMenu = false },
|
||||
modifier = Modifier.hazeEffectTangem(hazeState),
|
||||
modifier = Modifier.hazeEffectTangem(hazeState) {
|
||||
blurRadius = 6.dp
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
|
|
@ -219,7 +230,7 @@ private fun LazyItemScope.DraggableItem(
|
|||
headerRowUM = item.headerRowUM,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
)
|
||||
is OrganizeRowItemUM.Token -> TangemTokenRow(
|
||||
is OrganizeRowItemUM.Token -> OrganizeTokenRow(
|
||||
modifier = modifierWithBackground,
|
||||
tokenRowUM = item.tokenRowUM,
|
||||
reorderableState = reorderableState,
|
||||
|
|
@ -291,6 +302,55 @@ private fun Modifier.applyShapeAndShadow(roundingMode: RoundingModeUM, showShado
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OrganizeTokenRow(
|
||||
tokenRowUM: TangemTokenRowUM,
|
||||
isBalanceHidden: Boolean,
|
||||
reorderableState: ReorderableLazyListState?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
TangemRowContainer(
|
||||
content = {
|
||||
TangemIcon(
|
||||
tangemIconUM = tokenRowUM.headIconUM,
|
||||
modifier = Modifier
|
||||
.layoutId(layoutId = TangemRowLayoutId.HEAD)
|
||||
.padding(end = TangemTheme.dimens2.x3)
|
||||
.size(TangemTheme.dimens2.x10)
|
||||
.testTag(tag = TokenElementsTestTags.TOKEN_ICON),
|
||||
)
|
||||
|
||||
TokenRowTitle(
|
||||
titleUM = tokenRowUM.titleUM,
|
||||
modifier = Modifier
|
||||
.layoutId(layoutId = TangemRowLayoutId.START_TOP)
|
||||
.padding(end = TangemTheme.dimens2.x2)
|
||||
.testTag(tag = TokenElementsTestTags.TOKEN_TITLE),
|
||||
)
|
||||
|
||||
TokenRowEndContent(
|
||||
endContentUM = tokenRowUM.topEndContentUM,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
textStyle = TangemTheme.typography2.captionSemibold12,
|
||||
textColor = TangemTheme.colors2.text.neutral.secondary,
|
||||
placeholderWidth = TangemTheme.dimens2.x11,
|
||||
modifier = Modifier
|
||||
.layoutId(layoutId = TangemRowLayoutId.START_BOTTOM)
|
||||
.testTag(tag = TokenElementsTestTags.TOKEN_FIAT_AMOUNT),
|
||||
)
|
||||
|
||||
TangemRowTail(
|
||||
tangemRowTailUM = tokenRowUM.tailUM,
|
||||
reorderableState = reorderableState,
|
||||
modifier = Modifier
|
||||
.layoutId(layoutId = TangemRowLayoutId.TAIL)
|
||||
.testTag(tag = TokenElementsTestTags.TOKEN_NON_FIAT_BLOCK),
|
||||
)
|
||||
},
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun getItemGap(roundingMode: RoundingModeUM): PaddingValues {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.KycRejec
|
|||
import com.tangem.feature.walletsettings.component.RenameWalletComponent
|
||||
import com.tangem.features.biometry.AskBiometryComponent
|
||||
import com.tangem.features.feed.entry.components.FeedEntryComponent
|
||||
import com.tangem.features.promobanners.api.NewPromoBannersFeatureToggles
|
||||
import com.tangem.features.promobanners.api.PromoBannersBlockComponent
|
||||
import com.tangem.features.pushnotifications.api.PushNotificationsBottomSheetComponent
|
||||
import com.tangem.features.pushnotifications.api.PushNotificationsParams
|
||||
|
|
@ -63,7 +62,6 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
private val tokenReceiveComponentFactory: TokenReceiveComponent.Factory,
|
||||
private val yieldSupplyDepositedWarningComponent: YieldSupplyDepositedWarningComponent.Factory,
|
||||
private val promoBannersBlockComponentFactory: PromoBannersBlockComponent.Factory,
|
||||
private val newPromoBannersFeatureToggles: NewPromoBannersFeatureToggles,
|
||||
private val networkSelectionComponentFactory: NetworkSelectionComponent.Factory,
|
||||
private val tokenActionsComponentFactory: TokenActionsComponent.Factory,
|
||||
private val portfolioSelectorComponentFactory: PortfolioSelectorComponent.Factory,
|
||||
|
|
@ -85,8 +83,7 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private val promoBannersBlockComponent: PromoBannersBlockComponent? by lazy {
|
||||
if (!newPromoBannersFeatureToggles.isNewPromoBannersEnabled) return@lazy null
|
||||
private val promoBannersBlockComponent: PromoBannersBlockComponent by lazy {
|
||||
promoBannersBlockComponentFactory.create(
|
||||
context = child("promoBannersBlockComponent"),
|
||||
params = PromoBannersBlockComponent.Params(
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ import com.tangem.feature.wallet.presentation.wallet.analytics.utils.SelectedWal
|
|||
import com.tangem.feature.wallet.presentation.wallet.domain.OnrampStatusFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletContentFetcher
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
|
||||
import com.tangem.domain.pushnotificationpreferences.PreloadWalletPushNotificationPreferencesUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletNameMigrationUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
|
|
@ -61,8 +62,10 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.KycRejec
|
|||
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
|
||||
import com.tangem.features.biometry.AskBiometryComponent
|
||||
import com.tangem.features.hotwallet.HotWalletFeatureToggles
|
||||
import com.tangem.features.pushnotificationsettings.PushNotificationSettingsFeatureToggles
|
||||
import com.tangem.features.pushnotifications.api.PushNotificationsModelCallbacks
|
||||
import com.tangem.features.wallet.deeplink.WalletDeepLinkActionListener
|
||||
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.*
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
|
|
@ -93,6 +96,7 @@ internal class WalletModel @Inject constructor(
|
|||
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
private val selectedWalletAnalyticsSender: SelectedWalletAnalyticsSender,
|
||||
private val walletNameMigrationUseCase: WalletNameMigrationUseCase,
|
||||
private val preloadWalletPushNotificationPreferencesUseCase: PreloadWalletPushNotificationPreferencesUseCase,
|
||||
private val refreshMultiCurrencyWalletQuotesUseCase: RefreshMultiCurrencyWalletQuotesUseCase,
|
||||
private val walletImageResolver: WalletImageResolver,
|
||||
private val onrampStatusFactory: OnrampStatusFactory,
|
||||
|
|
@ -120,6 +124,8 @@ internal class WalletModel @Inject constructor(
|
|||
private val paymentAccountStatusFetcher: PaymentAccountStatusFetcher,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
|
||||
private val walletFeatureToggles: WalletFeatureToggles,
|
||||
private val pushNotificationSettingsFeatureToggles: PushNotificationSettingsFeatureToggles,
|
||||
private val startAssetsDiscoveryUseCase: StartAssetsDiscoveryUseCase,
|
||||
val screenLifecycleProvider: ScreenLifecycleProvider,
|
||||
val innerWalletRouter: InnerWalletRouter,
|
||||
|
|
@ -144,6 +150,7 @@ internal class WalletModel @Inject constructor(
|
|||
|
||||
maybeMigrateNames()
|
||||
maybeSetWalletFirstTimeUsage()
|
||||
preloadPushNotificationPreferences()
|
||||
updateYieldSupplyApy()
|
||||
subscribeToUserWalletsUpdates()
|
||||
subscribeOnBalanceHiding()
|
||||
|
|
@ -190,6 +197,19 @@ internal class WalletModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun preloadPushNotificationPreferences() {
|
||||
if (!pushNotificationSettingsFeatureToggles.isPushNotificationSettingsEnabled) return
|
||||
getWalletsUseCase()
|
||||
.map { wallets -> wallets.map(UserWallet::walletId) }
|
||||
.distinctUntilChanged()
|
||||
.onEach { walletIds ->
|
||||
walletIds.forEach { walletId ->
|
||||
modelScope.launch { preloadWalletPushNotificationPreferencesUseCase(walletId) }
|
||||
}
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun maybeSetWalletFirstTimeUsage() {
|
||||
modelScope.launch {
|
||||
setWalletFirstTimeUsageUseCase()
|
||||
|
|
@ -243,10 +263,6 @@ internal class WalletModel @Inject constructor(
|
|||
} else {
|
||||
null
|
||||
}
|
||||
val isBackedUp = when (selectedWallet) {
|
||||
is UserWallet.Cold -> selectedWallet.scanResponse.card.backupStatus?.isActive == true
|
||||
is UserWallet.Hot -> selectedWallet.backedUp
|
||||
}
|
||||
val result = getAppThemeModeUseCase().firstOrNull()
|
||||
val theme = result?.getOrElse { AppThemeMode.FOLLOW_SYSTEM } ?: AppThemeMode.FOLLOW_SYSTEM
|
||||
val appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default }.code
|
||||
|
|
@ -254,7 +270,7 @@ internal class WalletModel @Inject constructor(
|
|||
WalletScreenAnalyticsEvent.MainScreen.ScreenOpened(
|
||||
hasMobileWallet = hasMobileWallet,
|
||||
accountsCount = accountsCount,
|
||||
isBackedUp = isBackedUp,
|
||||
isBackedUp = selectedWallet.isBackedUpForAnalytics(),
|
||||
theme = theme.value,
|
||||
isImported = selectedWallet.isImported(),
|
||||
referralId = appsFlyerStore.get()?.refcode,
|
||||
|
|
@ -548,6 +564,7 @@ internal class WalletModel @Inject constructor(
|
|||
clickIntents = clickIntents,
|
||||
walletImageResolver = walletImageResolver,
|
||||
getWalletIconUseCase = getWalletIconUseCase,
|
||||
isAddFundsStage1Enabled = walletFeatureToggles.isAddFundsStage1Enabled,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -594,6 +611,7 @@ internal class WalletModel @Inject constructor(
|
|||
clickIntents = clickIntents,
|
||||
walletImageResolver = walletImageResolver,
|
||||
getWalletIconUseCase = getWalletIconUseCase,
|
||||
isAddFundsStage1Enabled = walletFeatureToggles.isAddFundsStage1Enabled,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -615,6 +633,7 @@ internal class WalletModel @Inject constructor(
|
|||
clickIntents = clickIntents,
|
||||
walletImageResolver = walletImageResolver,
|
||||
getWalletIconUseCase = getWalletIconUseCase,
|
||||
isAddFundsStage1Enabled = walletFeatureToggles.isAddFundsStage1Enabled,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -629,6 +648,7 @@ internal class WalletModel @Inject constructor(
|
|||
clickIntents = clickIntents,
|
||||
walletImageResolver = walletImageResolver,
|
||||
getWalletIconUseCase = getWalletIconUseCase,
|
||||
isAddFundsStage1Enabled = walletFeatureToggles.isAddFundsStage1Enabled,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -690,6 +710,7 @@ internal class WalletModel @Inject constructor(
|
|||
clickIntents = clickIntents,
|
||||
walletImageResolver = walletImageResolver,
|
||||
getWalletIconUseCase = getWalletIconUseCase,
|
||||
isAddFundsStage1Enabled = walletFeatureToggles.isAddFundsStage1Enabled,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
|
|||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
import com.tangem.domain.feedback.models.WalletMetaInfo
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.TangemPayDetailsConfig
|
||||
import com.tangem.domain.pay.TangemPayEligibilityManager
|
||||
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
|
||||
import com.tangem.domain.pay.model.TangemPayEntryPoint
|
||||
|
|
@ -40,7 +40,7 @@ internal interface TangemPayIntents {
|
|||
|
||||
fun onRefreshPayToken(userWallet: UserWallet)
|
||||
|
||||
fun openDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig)
|
||||
fun openDetails(status: AccountStatus.Payment)
|
||||
|
||||
fun onKycProgressClicked(userWalletId: UserWalletId)
|
||||
|
||||
|
|
@ -110,11 +110,8 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun openDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig) {
|
||||
router.openTangemPayDetails(
|
||||
userWalletId = userWalletId,
|
||||
config = config,
|
||||
)
|
||||
override fun openDetails(status: AccountStatus.Payment) {
|
||||
router.openTangemPayDetails(status = status)
|
||||
}
|
||||
|
||||
override fun onKycProgressClicked(userWalletId: UserWalletId) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
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
|
||||
import com.tangem.domain.onramp.FetchHotCryptoUseCase
|
||||
import com.tangem.domain.settings.NeverToShowWalletsScrollPreview
|
||||
|
|
@ -44,6 +47,7 @@ internal class WalletClickIntents @Inject constructor(
|
|||
private val tangemPayIntents: TangemPayClickIntentsImplementor,
|
||||
private val yieldSupplyApyUpdateUseCase: YieldSupplyApyUpdateUseCase,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : BaseWalletClickIntents(),
|
||||
WalletCardClickIntents by walletCardClickIntentsImplementor,
|
||||
WalletWarningsClickIntents by warningsClickIntentsImplementer,
|
||||
|
|
@ -114,6 +118,11 @@ internal class WalletClickIntents @Inject constructor(
|
|||
refreshSingleCurrencyContent(showRefreshState = true)
|
||||
}
|
||||
|
||||
fun onAddFundsClick(userWalletId: UserWalletId) {
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.ButtonAddFunds())
|
||||
router.openAddFunds(userWalletId)
|
||||
}
|
||||
|
||||
private fun refreshMultiCurrencyContent(showRefreshState: Boolean) {
|
||||
val userWallet = getSelectedWalletSyncUseCase.unwrap() ?: return
|
||||
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ import com.tangem.domain.models.wallet.UserWallet
|
|||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.offramp.GetOfframpUrlUseCase
|
||||
import com.tangem.domain.onramp.model.OnrampSource
|
||||
import com.tangem.domain.promo.GetStoryContentUseCase
|
||||
import com.tangem.domain.promo.models.StoryContentIds
|
||||
import com.tangem.domain.stories.GetStoryContentUseCase
|
||||
import com.tangem.domain.stories.models.StoryContentIds
|
||||
import com.tangem.domain.staking.model.StakingOption
|
||||
import com.tangem.domain.tokens.NeedShowYieldSupplyDepositedWarningUseCase
|
||||
import com.tangem.domain.tokens.SaveViewedTokenReceiveWarningUseCase
|
||||
|
|
@ -65,6 +65,8 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertUM
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -453,16 +455,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onMultiWalletSwapClick(userWalletId: UserWalletId) {
|
||||
val selectedWallet = stateHolder.getSelectedWallet() as? WalletState.MultiCurrency.Content ?: return
|
||||
when (selectedWallet.tokensListState) {
|
||||
is WalletTokensListState.ContentState.Content,
|
||||
is WalletTokensListState.ContentState.PortfolioContent,
|
||||
-> Unit
|
||||
WalletTokensListState.ContentState.Loading,
|
||||
WalletTokensListState.ContentState.Locked,
|
||||
WalletTokensListState.Empty,
|
||||
-> return
|
||||
}
|
||||
if (!isMultiWalletTokensLoaded()) return
|
||||
|
||||
modelScope.launch {
|
||||
val swapRoute = getSwapRoute(
|
||||
|
|
@ -576,6 +569,24 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
return true
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onMultiWalletActionClick(
|
||||
statusFlow: Flow<Lce<Throwable, Any>>,
|
||||
route: AppRoute,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import com.tangem.core.analytics.models.event.AssetsDiscoveryAnalyticsEvent
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.navigation.review.ReviewManager
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.domain.assetsdiscovery.usecase.AcknowledgeAssetsDiscoveryCompletionUseCase
|
||||
import com.tangem.domain.card.SetCardWasScannedUseCase
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
|
|
@ -29,17 +28,11 @@ import com.tangem.domain.models.wallet.requireColdWallet
|
|||
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
|
||||
import com.tangem.domain.notifications.SetShouldShowNotificationUseCase
|
||||
import com.tangem.domain.notifications.repository.NotificationsRepository
|
||||
import com.tangem.domain.onramp.model.OnrampSource
|
||||
import com.tangem.domain.promo.ShouldShowPromoWalletUseCase
|
||||
import com.tangem.domain.promo.models.PromoId
|
||||
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
|
||||
import com.tangem.domain.settings.NeverToSuggestRateAppUseCase
|
||||
import com.tangem.domain.settings.RemindToRateAppLaterUseCase
|
||||
import com.tangem.domain.staking.StakingIdFactory
|
||||
import com.tangem.domain.staking.multi.MultiStakingBalanceFetcher
|
||||
import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent
|
||||
import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent.Program
|
||||
import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent.PromotionBannerClicked
|
||||
import com.tangem.domain.tokens.model.details.NavigationAction
|
||||
import com.tangem.domain.wallets.usecase.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
|
||||
|
|
@ -75,10 +68,6 @@ internal interface WalletWarningsClickIntents {
|
|||
|
||||
fun onCloseRateAppWarningClick()
|
||||
|
||||
fun onClosePromoClick(promoId: PromoId)
|
||||
|
||||
fun onPromoClick(promoId: PromoId, cryptoCurrency: CryptoCurrency? = null)
|
||||
|
||||
fun onSupportClick()
|
||||
|
||||
fun onBackupErrorClick()
|
||||
|
|
@ -91,8 +80,6 @@ internal interface WalletWarningsClickIntents {
|
|||
|
||||
fun onFinishWalletActivationClick(isBackupExists: Boolean)
|
||||
|
||||
fun onYieldPromoTermsAndConditionsClick()
|
||||
|
||||
fun onUpgradeHotWalletClick(userWalletId: UserWalletId)
|
||||
|
||||
fun onCloseUpgradeBannerClick(userWalletId: UserWalletId)
|
||||
|
|
@ -115,10 +102,8 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
private val nonBiometricUnlockWalletUseCase: NonBiometricUnlockWalletUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val shouldShowPromoWalletUseCase: ShouldShowPromoWalletUseCase,
|
||||
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
|
||||
private val multiStakingBalanceFetcher: MultiStakingBalanceFetcher,
|
||||
|
|
@ -243,91 +228,6 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onClosePromoClick(promoId: PromoId) {
|
||||
analyticsEventHandler.send(
|
||||
when (promoId) {
|
||||
PromoId.Referral -> MainScreen.ReferralPromoButtonDismiss()
|
||||
PromoId.Sepa -> PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.Sepa,
|
||||
action = PromotionBannerClicked.BannerAction.Closed(),
|
||||
)
|
||||
PromoId.VisaPresale -> PromoAnalyticsEvent.VisaWaitlistPromoDismiss()
|
||||
PromoId.BlackFriday -> PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.BlackFriday,
|
||||
action = PromotionBannerClicked.BannerAction.Closed(),
|
||||
)
|
||||
PromoId.OnePlusOne -> PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.OnePlusOne,
|
||||
action = PromotionBannerClicked.BannerAction.Closed(),
|
||||
)
|
||||
PromoId.YieldPromo -> PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.YieldPromo,
|
||||
action = PromotionBannerClicked.BannerAction.Closed(),
|
||||
)
|
||||
},
|
||||
)
|
||||
modelScope.launch(dispatchers.main) {
|
||||
shouldShowPromoWalletUseCase.neverToShow(promoId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPromoClick(promoId: PromoId, cryptoCurrency: CryptoCurrency?) {
|
||||
val userWallet = getSelectedUserWallet() ?: return
|
||||
when (promoId) {
|
||||
PromoId.Referral -> {
|
||||
analyticsEventHandler.send(MainScreen.ReferralPromoButtonParticipate())
|
||||
appRouter.push(ReferralProgram(userWalletId = userWallet.walletId))
|
||||
}
|
||||
PromoId.Sepa -> {
|
||||
analyticsEventHandler.send(
|
||||
PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.Sepa,
|
||||
action = PromotionBannerClicked.BannerAction.Clicked(),
|
||||
),
|
||||
)
|
||||
cryptoCurrency ?: return
|
||||
appRouter.push(
|
||||
Onramp(
|
||||
userWalletId = userWallet.walletId,
|
||||
currency = cryptoCurrency,
|
||||
source = OnrampSource.SEPA_BANNER,
|
||||
shouldLaunchSepa = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
PromoId.VisaPresale -> {
|
||||
analyticsEventHandler.send(PromoAnalyticsEvent.VisaWaitlistPromoJoin())
|
||||
urlOpener.openUrl(VISA_PROMO_LINK)
|
||||
}
|
||||
PromoId.BlackFriday -> {
|
||||
analyticsEventHandler.send(
|
||||
PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.BlackFriday,
|
||||
action = PromotionBannerClicked.BannerAction.Clicked(),
|
||||
),
|
||||
)
|
||||
urlOpener.openUrl(BLACK_FRIDAY_PROMO_LINK)
|
||||
}
|
||||
PromoId.OnePlusOne -> {
|
||||
analyticsEventHandler.send(
|
||||
PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.OnePlusOne,
|
||||
action = PromotionBannerClicked.BannerAction.Clicked(),
|
||||
),
|
||||
)
|
||||
urlOpener.openUrl(ONE_PLUS_ONE_PROMO_LINK)
|
||||
}
|
||||
PromoId.YieldPromo -> Unit // banner is not clickable, only terms and conditions button
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSupportClick() {
|
||||
val userWallet = getSelectedUserWallet() ?: return
|
||||
|
||||
|
|
@ -478,17 +378,6 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onYieldPromoTermsAndConditionsClick() {
|
||||
analyticsEventHandler.send(
|
||||
PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.YieldPromo,
|
||||
action = PromotionBannerClicked.BannerAction.Clicked(),
|
||||
),
|
||||
)
|
||||
urlOpener.openUrl(YIELD_PROMO_TERMS_LINK)
|
||||
}
|
||||
|
||||
override fun onUpgradeHotWalletClick(userWalletId: UserWalletId) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
val userWallet = getUserWalletUseCase(userWalletId).getOrNull()
|
||||
|
|
@ -519,21 +408,4 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
AccountId.forMainCryptoPortfolio(userWalletId),
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val VISA_PROMO_LINK = "https://tangem.com/en/cardwaitlist/?utm_source=tangem-app-banner" +
|
||||
"&utm_medium=banner" +
|
||||
"&utm_campaign=tangempaywaitlist"
|
||||
const val BLACK_FRIDAY_PROMO_LINK = "https://tangem.com/en/pricing/" +
|
||||
"?promocode=BF2025" +
|
||||
"&utm_source=tangem-app-banner" +
|
||||
"&utm_medium=banner" +
|
||||
"&utm_campaign=BlackFriday2025"
|
||||
const val ONE_PLUS_ONE_PROMO_LINK = "https://tangem.com/pricing/" +
|
||||
"?cat=family" +
|
||||
"&utm_source=tangem-app-banner" +
|
||||
"&utm_medium=banner" +
|
||||
"&utm_campaign=BOGO50"
|
||||
const val YIELD_PROMO_TERMS_LINK = "https://tangem.com/docs/yield-mode-toc.html"
|
||||
}
|
||||
}
|
||||
|
|
@ -11,4 +11,7 @@ internal class DefaultWalletFeatureToggles @Inject constructor(
|
|||
|
||||
override val isAddAndManageTokensEnabled: Boolean
|
||||
get() = featureToggles.isFeatureEnabled(FeatureToggles.ADD_AND_MANAGE_TOKENS_ENABLED)
|
||||
|
||||
override val isAddFundsStage1Enabled: Boolean
|
||||
get() = featureToggles.isFeatureEnabled(FeatureToggles.AND_15310_ADD_FUNDS_STAGE1)
|
||||
}
|
||||
|
|
@ -12,13 +12,13 @@ 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
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.qrscanning.models.QrSendTarget
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.TangemPayDetailsConfig
|
||||
import com.tangem.domain.qrscanning.models.QrSendTarget
|
||||
import com.tangem.domain.tokens.model.details.NavigationAction
|
||||
import com.tangem.domain.tokens.model.details.TokenAction
|
||||
import com.tangem.feature.wallet.child.organizetokens.OrganizeTokensComponent
|
||||
|
|
@ -119,6 +119,10 @@ internal class DefaultWalletRouter @Inject constructor(
|
|||
router.push(AppRoute.Home())
|
||||
}
|
||||
|
||||
override fun openAddFunds(userWalletId: UserWalletId) {
|
||||
router.push(AppRoute.AddFunds(userWalletId = userWalletId))
|
||||
}
|
||||
|
||||
override fun isWalletLastScreen(): Boolean {
|
||||
return router.stack.lastOrNull() is AppRoute.Wallet
|
||||
}
|
||||
|
|
@ -142,8 +146,8 @@ internal class DefaultWalletRouter @Inject constructor(
|
|||
router.push(route = AppRoute.TangemPayOnboarding(mode = mode))
|
||||
}
|
||||
|
||||
override fun openTangemPayDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig) {
|
||||
router.push(AppRoute.TangemPayDetails(userWalletId = userWalletId, config = config))
|
||||
override fun openTangemPayDetails(status: AccountStatus.Payment) {
|
||||
router.push(AppRoute.TangemPayDetails(status = status))
|
||||
}
|
||||
|
||||
override fun openYieldSupplyBottomSheet(
|
||||
|
|
|
|||
|
|
@ -7,13 +7,13 @@ import com.tangem.common.routing.AppRoute
|
|||
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
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.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.qrscanning.models.QrSendTarget
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.TangemPayDetailsConfig
|
||||
import com.tangem.domain.qrscanning.models.QrSendTarget
|
||||
import com.tangem.domain.tokens.model.details.NavigationAction
|
||||
import com.tangem.domain.tokens.model.details.TokenAction
|
||||
import com.tangem.feature.wallet.child.organizetokens.OrganizeTokensComponent
|
||||
|
|
@ -82,7 +82,7 @@ internal interface InnerWalletRouter {
|
|||
|
||||
fun openTangemPayOnboarding(mode: AppRoute.TangemPayOnboarding.Mode)
|
||||
|
||||
fun openTangemPayDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig)
|
||||
fun openTangemPayDetails(status: AccountStatus.Payment)
|
||||
|
||||
/** Open BS abput yield supply active and all money deposited in AAVE */
|
||||
fun openYieldSupplyBottomSheet(
|
||||
|
|
@ -117,4 +117,7 @@ internal interface InnerWalletRouter {
|
|||
|
||||
/** Open network selection bottom sheet for multiple QR matches */
|
||||
fun openNetworkSelectionBottomSheet(target: QrSendTarget.Multiple)
|
||||
|
||||
/** Open Add Funds screen */
|
||||
fun openAddFunds(userWalletId: UserWalletId)
|
||||
}
|
||||
|
|
@ -1,56 +1,14 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.analytics
|
||||
|
||||
import com.tangem.core.analytics.models.*
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
sealed class WalletScreenAnalyticsEvent {
|
||||
|
||||
sealed class Basic(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent(category = "Basic", event = event, params = params) {
|
||||
|
||||
class WalletToppedUp(userWalletId: UserWalletId, walletType: AnalyticsParam.WalletType) :
|
||||
Basic(
|
||||
event = "Topped up",
|
||||
params = mapOf(AnalyticsParam.CURRENCY to walletType.value),
|
||||
),
|
||||
OneTimeAnalyticsEvent, AppsFlyerIncludedEvent {
|
||||
|
||||
override val oneTimeEventId: String = id + userWalletId.stringValue
|
||||
}
|
||||
|
||||
class CardWasScanned(source: AnalyticsParam.ScreensSources) : Basic(
|
||||
event = "Card Was Scanned",
|
||||
params = mapOf(
|
||||
AnalyticsParam.SOURCE to source.value,
|
||||
),
|
||||
)
|
||||
|
||||
class BalanceLoaded(balance: AnalyticsParam.CardBalanceState, tokensCount: Int?) : Basic(
|
||||
event = "Balance Loaded",
|
||||
params = buildMap {
|
||||
put(AnalyticsParam.BALANCE, balance.value)
|
||||
tokensCount?.let { put(AnalyticsParam.TOKENS_COUNT, it.toString()) }
|
||||
},
|
||||
), AppsFlyerIncludedEvent
|
||||
|
||||
class TokenBalance(balance: AnalyticsParam.EmptyFull, token: String) : Basic(
|
||||
event = "Token Balance",
|
||||
params = mapOf(
|
||||
AnalyticsParam.STATE to balance.value,
|
||||
AnalyticsParam.TOKEN_PARAM to token,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
sealed class MainScreen(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent(category = "Main Screen", event = event, params = params) {
|
||||
|
||||
class ScreenOpenedLegacy : MainScreen(event = "Screen opened")
|
||||
|
||||
data class ScreenOpened(
|
||||
private val hasMobileWallet: Boolean,
|
||||
private val accountsCount: Int?,
|
||||
|
|
@ -167,12 +125,6 @@ sealed class WalletScreenAnalyticsEvent {
|
|||
if (blockchain != null) put("Blockchain", blockchain)
|
||||
},
|
||||
)
|
||||
|
||||
// region Referral Promo
|
||||
class ReferralPromo : MainScreen(event = "Referral Banner")
|
||||
class ReferralPromoButtonParticipate : MainScreen(event = "Button - Referral Participate")
|
||||
class ReferralPromoButtonDismiss : MainScreen(event = "Button - Referral Dismiss")
|
||||
//endregion
|
||||
}
|
||||
|
||||
sealed class PushBannerPromo(
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.tangem.blockchainsdk.utils.fromNetworkId
|
|||
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.core.analytics.models.event.MainScreenAnalyticsEvent
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.analytics.CheckIsWalletToppedUpUseCase
|
||||
|
|
@ -18,7 +19,6 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
|||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.Basic
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
|
||||
|
|
@ -206,7 +206,12 @@ internal class TokenListAnalyticsSender @Inject constructor(
|
|||
AnalyticsParam.WalletType.SingleCurrency(currency.currency.name)
|
||||
}
|
||||
|
||||
analyticsEventHandler.send(Basic.WalletToppedUp(userWallet.walletId, walletType))
|
||||
analyticsEventHandler.send(
|
||||
Basic.ToppedUp(
|
||||
userWalletId = userWallet.walletId.stringValue,
|
||||
walletType = walletType,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
|
|||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent.*
|
||||
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen.*
|
||||
|
|
@ -69,28 +68,6 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
|
|||
is WalletNotification.RateApp -> HowDoYouLikeTangem()
|
||||
is WalletNotification.Critical.BackupError -> BackupError()
|
||||
is WalletNotification.NoteMigration -> NotePromo()
|
||||
is WalletNotification.SwapPromo -> NoticePromotionBanner(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.Empty, // Use it on new promo action
|
||||
)
|
||||
is WalletNotification.Sepa -> NoticePromotionBanner(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.Sepa,
|
||||
)
|
||||
is WalletNotification.BlackFridayPromo -> NoticePromotionBanner(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.BlackFriday,
|
||||
)
|
||||
is WalletNotification.OnePlusOnePromo -> NoticePromotionBanner(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.OnePlusOne,
|
||||
)
|
||||
is WalletNotification.YieldPromo -> NoticePromotionBanner(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.YieldPromo,
|
||||
)
|
||||
is WalletNotification.ReferralPromo -> MainScreen.ReferralPromo()
|
||||
is WalletNotification.VisaPresalePromo -> VisaWaitlistPromo()
|
||||
is WalletNotification.UnlockWallets -> null // See [SelectedWalletAnalyticsSender]
|
||||
is WalletNotification.Informational.NoAccount,
|
||||
is WalletNotification.Warning.LowSignatures,
|
||||
|
|
@ -138,14 +115,6 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
|
|||
is WalletNotificationUM.RateApp -> HowDoYouLikeTangem()
|
||||
is WalletNotificationUM.BackupError -> BackupError()
|
||||
is WalletNotificationUM.NoteMigration -> NotePromo()
|
||||
is WalletNotificationUM.OnePlusOnePromo -> NoticePromotionBanner(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.OnePlusOne,
|
||||
)
|
||||
is WalletNotificationUM.YieldPromo -> NoticePromotionBanner(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.YieldPromo,
|
||||
)
|
||||
is WalletNotificationUM.FinishWalletActivation -> {
|
||||
val activationState = if (notificationUM.isBackupExists) {
|
||||
NoticeFinishActivation.ActivationState.Unfinished
|
||||
|
|
|
|||
|
|
@ -24,8 +24,6 @@ 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.promo.ShouldShowPromoWalletUseCase
|
||||
import com.tangem.domain.promo.models.PromoId
|
||||
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
|
||||
import com.tangem.domain.assetsdiscovery.model.AssetsDiscoveryProgress
|
||||
import com.tangem.domain.assetsdiscovery.usecase.ObserveAssetsDiscoveryUseCase
|
||||
|
|
@ -54,7 +52,6 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
|
||||
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
|
||||
private val backupValidator: BackupValidator,
|
||||
private val shouldShowPromoWalletUseCase: ShouldShowPromoWalletUseCase,
|
||||
private val notificationsRepository: NotificationsRepository,
|
||||
private val accountDependencies: AccountDependencies,
|
||||
private val getAccessCodeSkippedUseCase: GetAccessCodeSkippedUseCase,
|
||||
|
|
@ -82,13 +79,9 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
accountStatusListFlow,
|
||||
isReadyToShowRateAppUseCase().distinctUntilChanged(),
|
||||
isNeedToBackupUseCase(userWallet.walletId).distinctUntilChanged(),
|
||||
shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.OnePlusOne)
|
||||
.distinctUntilChanged(),
|
||||
notificationsRepository.getShouldShowNotification(NotificationId.EnablePushesReminderNotification.key)
|
||||
.distinctUntilChanged(),
|
||||
getAccessCodeSkippedUseCase(userWallet.walletId).distinctUntilChanged(),
|
||||
shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.YieldPromo)
|
||||
.distinctUntilChanged(),
|
||||
shouldShowUpgradeHotWalletBannerUseCase.invoke(userWallet.walletId)
|
||||
.distinctUntilChanged(),
|
||||
getUpgradeBannerClosureTimestampUseCase(userWallet.walletId)
|
||||
|
|
@ -99,13 +92,11 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
val accountStatusList = array[0] as AccountStatusList
|
||||
val isReadyToShowRating = array[1] as Boolean
|
||||
val isNeedToBackup = array[2] as Boolean
|
||||
val shouldShowOnePlusOnePromo = array[3] as Boolean
|
||||
val shouldShowEnablePushesReminderNotification = array[4] as Boolean
|
||||
val shouldAccessCodeSkipped = array[5] as Boolean
|
||||
val shouldShowYieldPromo = array[6] as Boolean
|
||||
val shouldShowUpgradeBanner = array[7] as Boolean
|
||||
val closureTimestamp = array[8] as? Long
|
||||
val assetsDiscoveryProgress = array[9] as AssetsDiscoveryProgress
|
||||
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 flattenCurrencies = accountStatusList.flattenCurrencies()
|
||||
val paymentAccountStatus = accountStatusList.accountStatuses
|
||||
|
|
@ -132,10 +123,6 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
shouldAccessCodeSkipped = shouldAccessCodeSkipped,
|
||||
)
|
||||
|
||||
addOnePlusOnePromoNotification(clickIntents, shouldShowOnePlusOnePromo)
|
||||
|
||||
addYieldPromoNotification(clickIntents, shouldShowYieldPromo)
|
||||
|
||||
addInformationalNotifications(
|
||||
userWallet = userWallet,
|
||||
cardTypesResolver = cardTypesResolver,
|
||||
|
|
@ -162,9 +149,6 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
!notificationsRepository.isUserAllowToSubscribeOnPushNotifications(),
|
||||
)
|
||||
|
||||
// Remove in first iteration of yield supply feature
|
||||
// addYieldSupplyNotifications(flattenCurrencies)
|
||||
|
||||
val hasCriticalOrWarning = any { notification ->
|
||||
notification is WalletNotification.Critical || notification is WalletNotification.Warning
|
||||
}
|
||||
|
|
@ -296,41 +280,6 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
.map(CryptoCurrencyStatus::currency)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addOnePlusOnePromoNotification(
|
||||
clickIntents: WalletClickIntents,
|
||||
shouldShowPromo: Boolean,
|
||||
) {
|
||||
addIf(
|
||||
element = WalletNotification.OnePlusOnePromo(
|
||||
onCloseClick = { clickIntents.onClosePromoClick(promoId = PromoId.OnePlusOne) },
|
||||
onClick = { clickIntents.onPromoClick(promoId = PromoId.OnePlusOne) },
|
||||
),
|
||||
condition = shouldShowPromo,
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addYieldPromoNotification(
|
||||
clickIntents: WalletClickIntents,
|
||||
shouldShowPromo: Boolean,
|
||||
) {
|
||||
addIf(
|
||||
element = WalletNotification.YieldPromo(
|
||||
onCloseClick = { clickIntents.onClosePromoClick(promoId = PromoId.YieldPromo) },
|
||||
onTermsAndConditionsClick = { clickIntents.onYieldPromoTermsAndConditionsClick() },
|
||||
),
|
||||
condition = shouldShowPromo,
|
||||
)
|
||||
}
|
||||
|
||||
// private fun MutableList<WalletNotification>.addYieldSupplyNotifications(
|
||||
// flattenCurrencies: Lce<TokenListError, List<CryptoCurrencyStatus>>,
|
||||
// ) {
|
||||
// addIf(
|
||||
// element = WalletNotification.Warning.YeildSupplyApprove,
|
||||
// condition = flattenCurrencies.hasTokensWithActivatedSupplyWithoutApprove(),
|
||||
// )
|
||||
// }
|
||||
|
||||
private fun MutableList<WalletNotification>.addWarningNotifications(
|
||||
cardTypesResolver: CardTypesResolver?,
|
||||
flattenCurrencies: List<CryptoCurrencyStatus>,
|
||||
|
|
@ -393,15 +342,6 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
return this.any { it.value is CryptoCurrencyStatus.Unreachable }
|
||||
}
|
||||
|
||||
// Remove in first iteration of yield supply feature
|
||||
// private fun Lce<TokenListError, List<CryptoCurrencyStatus>>.hasTokensWithActivatedSupplyWithoutApprove(): Boolean {
|
||||
// val flattenCurrencies = getOrNull(isPartialContentAccepted = false) ?: return false
|
||||
// val yieldSupplyEnabled = yieldSupplyFeatureToggles.isYieldSupplyFeatureEnabled
|
||||
// return yieldSupplyEnabled && flattenCurrencies.any {
|
||||
// it.value.yieldSupplyStatus?.isAllowedToSpend == false
|
||||
// }
|
||||
// }
|
||||
|
||||
private fun MutableList<WalletNotification>.addAssetsDiscoveryCompletedNotification(
|
||||
userWallet: UserWallet,
|
||||
assetsDiscoveryProgress: AssetsDiscoveryProgress,
|
||||
|
|
|
|||
|
|
@ -5,10 +5,7 @@ import com.tangem.common.ui.notifications.NotificationId
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
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.domain.notifications.repository.NotificationsRepository
|
||||
import com.tangem.domain.promo.ShouldShowPromoWalletUseCase
|
||||
import com.tangem.domain.promo.models.PromoId
|
||||
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
|
|
@ -29,32 +26,22 @@ import javax.inject.Inject
|
|||
@ModelScoped
|
||||
internal class GetWalletNotificationsCarouselFactory @Inject constructor(
|
||||
private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
|
||||
private val shouldShowPromoWalletUseCase: ShouldShowPromoWalletUseCase,
|
||||
private val getWalletsUseCase: GetWalletsUseCase,
|
||||
private val notificationsRepository: NotificationsRepository,
|
||||
) {
|
||||
fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): Flow<ImmutableList<WalletNotificationUM>> {
|
||||
return combine(
|
||||
flow = shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.YieldPromo)
|
||||
.distinctUntilChanged(),
|
||||
flow2 = notificationsRepository.getShouldShowNotification(
|
||||
flow = notificationsRepository.getShouldShowNotification(
|
||||
NotificationId.EnablePushesReminderNotification.key,
|
||||
).distinctUntilChanged(),
|
||||
flow3 = shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.OnePlusOne)
|
||||
.distinctUntilChanged(),
|
||||
flow4 = isReadyToShowRateAppUseCase().distinctUntilChanged(),
|
||||
flow5 = getWalletsUseCase().conflate(),
|
||||
) { showYieldPromo, showPushesNotification, showOnePlusOnePromo, showRateAppPromo, wallets ->
|
||||
flow2 = isReadyToShowRateAppUseCase().distinctUntilChanged(),
|
||||
flow3 = getWalletsUseCase().conflate(),
|
||||
) { showPushesNotification, showRateAppPromo, wallets ->
|
||||
|
||||
buildList {
|
||||
addNoteMigrationNotification(userWallet, wallets, clickIntents)
|
||||
addRateAppNotification(showRateAppPromo, clickIntents)
|
||||
|
||||
if (userWallet.isMultiCurrency) {
|
||||
addOnePlusOnePromoNotification(clickIntents, showOnePlusOnePromo)
|
||||
addYieldPromoNotification(clickIntents, showYieldPromo)
|
||||
}
|
||||
|
||||
addPushNotification(
|
||||
shouldShow = showPushesNotification,
|
||||
isPushesAllowed = notificationsRepository.isUserAllowToSubscribeOnPushNotifications(),
|
||||
|
|
@ -77,30 +64,6 @@ internal class GetWalletNotificationsCarouselFactory @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotificationUM>.addYieldPromoNotification(
|
||||
clickIntents: WalletClickIntents,
|
||||
shouldShowPromo: Boolean,
|
||||
) {
|
||||
addIf(shouldShowPromo) {
|
||||
WalletNotificationUM.YieldPromo(
|
||||
onCloseClick = { clickIntents.onClosePromoClick(promoId = PromoId.YieldPromo) },
|
||||
onTermsAndConditionsClick = { clickIntents.onYieldPromoTermsAndConditionsClick() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotificationUM>.addOnePlusOnePromoNotification(
|
||||
clickIntents: WalletClickIntents,
|
||||
shouldShowPromo: Boolean,
|
||||
) {
|
||||
addIf(shouldShowPromo) {
|
||||
WalletNotificationUM.OnePlusOnePromo(
|
||||
onCloseClick = { clickIntents.onClosePromoClick(promoId = PromoId.OnePlusOne) },
|
||||
onClick = { clickIntents.onPromoClick(promoId = PromoId.OnePlusOne) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotificationUM>.addNoteMigrationNotification(
|
||||
userWallet: UserWallet,
|
||||
userWallets: List<UserWallet>,
|
||||
|
|
|
|||
|
|
@ -51,6 +51,20 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
|
|||
),
|
||||
)
|
||||
|
||||
data class AddFunds(
|
||||
override val enabled: Boolean,
|
||||
override val dimContent: Boolean,
|
||||
override val onClick: () -> Unit,
|
||||
) : WalletManageButton(
|
||||
config = ActionButtonConfig(
|
||||
text = TextReference.Res(id = R.string.common_add_funds),
|
||||
iconResId = R.drawable.ic_plus_24,
|
||||
onClick = onClick,
|
||||
isEnabled = enabled,
|
||||
shouldDimContent = dimContent,
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* Send
|
||||
*
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import org.joda.time.DateTime
|
||||
|
||||
/**
|
||||
* Wallet notification component state
|
||||
|
|
@ -229,19 +228,6 @@ sealed class WalletNotification(val config: NotificationConfig) {
|
|||
),
|
||||
)
|
||||
|
||||
data class SwapPromo(
|
||||
val startDateTime: DateTime,
|
||||
val endDateTime: DateTime,
|
||||
val onCloseClick: () -> Unit,
|
||||
) : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = resourceReference(id = R.string.swap_promo_title),
|
||||
subtitle = resourceReference(id = R.string.swap_promo_text),
|
||||
iconResId = R.drawable.img_okx_dex_logo,
|
||||
onCloseClick = onCloseClick,
|
||||
),
|
||||
)
|
||||
|
||||
data class NoteMigration(val onClick: () -> Unit) : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = resourceReference(R.string.wallet_promo_banner_title),
|
||||
|
|
@ -282,108 +268,6 @@ sealed class WalletNotification(val config: NotificationConfig) {
|
|||
),
|
||||
)
|
||||
|
||||
data class ReferralPromo(
|
||||
val onCloseClick: () -> Unit,
|
||||
val onClick: () -> Unit,
|
||||
) : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = resourceReference(R.string.notification_referral_promo_title),
|
||||
subtitle = resourceReference(R.string.notification_referral_promo_text),
|
||||
iconResId = R.drawable.img_referral_promo,
|
||||
onCloseClick = onCloseClick,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.notification_referral_promo_button),
|
||||
onClick = onClick,
|
||||
),
|
||||
iconSize = 54.dp,
|
||||
),
|
||||
)
|
||||
|
||||
data class VisaPresalePromo(
|
||||
val onCloseClick: () -> Unit,
|
||||
val onClick: () -> Unit,
|
||||
) : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = resourceReference(R.string.notification_visa_waitlist_promo_title),
|
||||
subtitle = resourceReference(R.string.notification_visa_waitlist_promo_text),
|
||||
iconResId = R.drawable.img_visa_waitlist_promo,
|
||||
onCloseClick = onCloseClick,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.notification_referral_promo_button),
|
||||
onClick = onClick,
|
||||
),
|
||||
iconSize = 54.dp,
|
||||
),
|
||||
)
|
||||
|
||||
data class Sepa(
|
||||
val onCloseClick: () -> Unit,
|
||||
val onClick: () -> Unit,
|
||||
) : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = resourceReference(R.string.notification_sepa_title),
|
||||
subtitle = resourceReference(R.string.notification_sepa_text),
|
||||
iconResId = R.drawable.img_notification_sepa,
|
||||
onCloseClick = onCloseClick,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.notification_sepa_button),
|
||||
onClick = onClick,
|
||||
),
|
||||
iconSize = 54.dp,
|
||||
),
|
||||
)
|
||||
|
||||
data class BlackFridayPromo(
|
||||
val onCloseClick: () -> Unit,
|
||||
val onClick: () -> Unit,
|
||||
) : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = resourceReference(R.string.notification_black_friday_title),
|
||||
subtitle = resourceReference(R.string.notification_black_friday_text),
|
||||
iconResId = R.drawable.img_black_friday_promo,
|
||||
onCloseClick = onCloseClick,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.common_claim),
|
||||
onClick = onClick,
|
||||
),
|
||||
iconSize = 54.dp,
|
||||
),
|
||||
)
|
||||
|
||||
data class OnePlusOnePromo(
|
||||
val onCloseClick: () -> Unit,
|
||||
val onClick: () -> Unit,
|
||||
) : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = resourceReference(R.string.notification_one_plus_one_title),
|
||||
subtitle = resourceReference(R.string.notification_one_plus_one_text),
|
||||
iconResId = R.drawable.img_one_plus_one_promo,
|
||||
onCloseClick = onCloseClick,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.notification_one_plus_one_button),
|
||||
onClick = onClick,
|
||||
),
|
||||
iconSize = 54.dp,
|
||||
),
|
||||
)
|
||||
|
||||
data class YieldPromo(
|
||||
val onCloseClick: () -> Unit,
|
||||
val onTermsAndConditionsClick: () -> Unit,
|
||||
) : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = resourceReference(R.string.notification_yield_promo_title),
|
||||
subtitle = resourceReference(R.string.notification_yield_promo_text),
|
||||
iconResId = R.drawable.ic_yield_promo_36,
|
||||
onCloseClick = onCloseClick,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.notification_yield_promo_button),
|
||||
onClick = onTermsAndConditionsClick,
|
||||
),
|
||||
iconSize = 36.dp,
|
||||
),
|
||||
)
|
||||
|
||||
data class PushNotifications(
|
||||
val onCloseClick: () -> Unit,
|
||||
val onEnabledClick: () -> Unit,
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ internal sealed class WalletNotificationUM(val messageUM: TangemMessageUM, val t
|
|||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(id = R.string.common_contact_support),
|
||||
type = TangemButtonType.PrimaryInverse,
|
||||
type = TangemButtonType.Secondary,
|
||||
onClick = onClick,
|
||||
),
|
||||
),
|
||||
|
|
@ -153,7 +153,7 @@ internal sealed class WalletNotificationUM(val messageUM: TangemMessageUM, val t
|
|||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(id = R.string.button_start_backup_process),
|
||||
type = TangemButtonType.PrimaryInverse,
|
||||
type = TangemButtonType.Secondary,
|
||||
onClick = onClick,
|
||||
),
|
||||
),
|
||||
|
|
@ -204,7 +204,7 @@ internal sealed class WalletNotificationUM(val messageUM: TangemMessageUM, val t
|
|||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(R.string.hw_activation_need_finish),
|
||||
type = TangemButtonType.PrimaryInverse,
|
||||
type = TangemButtonType.Secondary,
|
||||
onClick = onClick,
|
||||
),
|
||||
),
|
||||
|
|
@ -360,53 +360,6 @@ internal sealed class WalletNotificationUM(val messageUM: TangemMessageUM, val t
|
|||
type = WalletNotificationType.Promo,
|
||||
)
|
||||
|
||||
data class OnePlusOnePromo(
|
||||
val onCloseClick: () -> Unit,
|
||||
val onClick: () -> Unit,
|
||||
) : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "OnePlusOnePromoNotification",
|
||||
title = resourceReference(R.string.notification_one_plus_one_title),
|
||||
subtitle = resourceReference(R.string.notification_one_plus_one_text),
|
||||
messageEffect = TangemMessageEffect.Magic,
|
||||
iconUM = TangemIconUM.Image(R.drawable.img_one_plus_one_promo),
|
||||
iconSize = 54.dp,
|
||||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(R.string.common_later),
|
||||
type = TangemButtonType.PrimaryInverse,
|
||||
onClick = onCloseClick,
|
||||
),
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(R.string.notification_one_plus_one_button),
|
||||
type = TangemButtonType.Primary,
|
||||
onClick = onClick,
|
||||
),
|
||||
),
|
||||
),
|
||||
type = WalletNotificationType.Promo,
|
||||
)
|
||||
|
||||
data class YieldPromo(
|
||||
val onCloseClick: () -> Unit,
|
||||
val onTermsAndConditionsClick: () -> Unit,
|
||||
) : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "YieldPromoNotification",
|
||||
title = resourceReference(R.string.notification_yield_promo_title),
|
||||
subtitle = resourceReference(R.string.notification_yield_promo_text),
|
||||
onCloseClick = onCloseClick,
|
||||
messageEffect = TangemMessageEffect.Magic,
|
||||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(R.string.notification_yield_promo_button),
|
||||
type = TangemButtonType.Primary,
|
||||
onClick = onTermsAndConditionsClick,
|
||||
),
|
||||
),
|
||||
),
|
||||
type = WalletNotificationType.Promo,
|
||||
)
|
||||
// endregion
|
||||
|
||||
// region Survey
|
||||
|
|
@ -423,7 +376,7 @@ internal sealed class WalletNotificationUM(val messageUM: TangemMessageUM, val t
|
|||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(id = R.string.warning_button_could_be_better),
|
||||
type = TangemButtonType.PrimaryInverse,
|
||||
type = TangemButtonType.Secondary,
|
||||
onClick = onDislikeClick,
|
||||
),
|
||||
TangemMessageButtonUM(
|
||||
|
|
@ -454,7 +407,7 @@ internal sealed class WalletNotificationUM(val messageUM: TangemMessageUM, val t
|
|||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(R.string.common_later),
|
||||
type = TangemButtonType.PrimaryInverse,
|
||||
type = TangemButtonType.Secondary,
|
||||
onClick = onCloseClick,
|
||||
),
|
||||
TangemMessageButtonUM(
|
||||
|
|
@ -483,7 +436,7 @@ internal sealed class WalletNotificationUM(val messageUM: TangemMessageUM, val t
|
|||
TangemMessageButtonUM(
|
||||
text = resourceReference(com.tangem.core.res.R.string.warning_clore_migration_button),
|
||||
onClick = onStartMigrationClick,
|
||||
type = TangemButtonType.PrimaryInverse,
|
||||
type = TangemButtonType.Secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ internal class AddWalletTransformer(
|
|||
private val clickIntents: WalletClickIntents,
|
||||
private val walletImageResolver: WalletImageResolver,
|
||||
private val getWalletIconUseCase: GetWalletIconUseCase,
|
||||
private val isAddFundsStage1Enabled: Boolean,
|
||||
) : WalletScreenStateTransformer {
|
||||
|
||||
private val walletLoadingStateFactory by lazy {
|
||||
|
|
@ -20,6 +21,7 @@ internal class AddWalletTransformer(
|
|||
clickIntents = clickIntents,
|
||||
walletImageResolver = walletImageResolver,
|
||||
getWalletIconUseCase = getWalletIconUseCase,
|
||||
isAddFundsStage1Enabled = isAddFundsStage1Enabled,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ internal class InitializeWalletsTransformer(
|
|||
private val clickIntents: WalletClickIntents,
|
||||
private val walletImageResolver: WalletImageResolver,
|
||||
private val getWalletIconUseCase: GetWalletIconUseCase,
|
||||
private val isAddFundsStage1Enabled: Boolean,
|
||||
) : WalletScreenStateTransformer {
|
||||
|
||||
private val walletLoadingStateFactory by lazy {
|
||||
|
|
@ -34,6 +35,7 @@ internal class InitializeWalletsTransformer(
|
|||
clickIntents = clickIntents,
|
||||
walletImageResolver = walletImageResolver,
|
||||
getWalletIconUseCase = getWalletIconUseCase,
|
||||
isAddFundsStage1Enabled = isAddFundsStage1Enabled,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -147,8 +149,18 @@ internal class InitializeWalletsTransformer(
|
|||
userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()
|
||||
if (isSingleWalletWithToken) return persistentListOf()
|
||||
|
||||
val firstButton = if (isAddFundsStage1Enabled) {
|
||||
WalletManageButton.AddFunds(
|
||||
enabled = false,
|
||||
dimContent = false,
|
||||
onClick = {},
|
||||
)
|
||||
} else {
|
||||
WalletManageButton.Buy(enabled = false, dimContent = false, onClick = {})
|
||||
}
|
||||
|
||||
return persistentListOf(
|
||||
WalletManageButton.Buy(enabled = false, dimContent = false, onClick = {}),
|
||||
firstButton,
|
||||
WalletManageButton.Swap(enabled = false, dimContent = false, onClick = {}),
|
||||
WalletManageButton.Sell(enabled = false, dimContent = false, onClick = {}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ internal class ReinitializeNewWalletTransformer(
|
|||
private val clickIntents: WalletClickIntents,
|
||||
private val walletImageResolver: WalletImageResolver,
|
||||
private val getWalletIconUseCase: GetWalletIconUseCase,
|
||||
private val isAddFundsStage1Enabled: Boolean,
|
||||
) : WalletScreenStateTransformer {
|
||||
|
||||
private val walletLoadingStateFactory by lazy {
|
||||
|
|
@ -31,6 +32,7 @@ internal class ReinitializeNewWalletTransformer(
|
|||
clickIntents = clickIntents,
|
||||
walletImageResolver = walletImageResolver,
|
||||
getWalletIconUseCase = getWalletIconUseCase,
|
||||
isAddFundsStage1Enabled = isAddFundsStage1Enabled,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ internal class ReinitializeWalletTransformer(
|
|||
private val clickIntents: WalletClickIntents,
|
||||
private val walletImageResolver: WalletImageResolver,
|
||||
private val getWalletIconUseCase: GetWalletIconUseCase,
|
||||
private val isAddFundsStage1Enabled: Boolean,
|
||||
) : WalletStateTransformer(userWalletId = userWallet.walletId) {
|
||||
|
||||
private val walletLoadingStateFactory by lazy {
|
||||
|
|
@ -27,6 +28,7 @@ internal class ReinitializeWalletTransformer(
|
|||
clickIntents = clickIntents,
|
||||
walletImageResolver = walletImageResolver,
|
||||
getWalletIconUseCase = getWalletIconUseCase,
|
||||
isAddFundsStage1Enabled = isAddFundsStage1Enabled,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -72,10 +72,11 @@ internal class SetRefreshStateTransformer(
|
|||
private fun PersistentList<WalletManageButton>.toUpdatedState(): PersistentList<WalletManageButton> {
|
||||
val isButtonsEnabled = !isRefreshing
|
||||
|
||||
return mutate {
|
||||
it.mapNotNull { button ->
|
||||
return mutate { items ->
|
||||
items.mapNotNull { button ->
|
||||
when (button) {
|
||||
is WalletManageButton.Buy -> button.copy(enabled = isButtonsEnabled)
|
||||
is WalletManageButton.AddFunds -> button.copy(enabled = isButtonsEnabled)
|
||||
is WalletManageButton.Send -> button.copy(enabled = isButtonsEnabled)
|
||||
is WalletManageButton.Sell -> button.copy(enabled = isButtonsEnabled)
|
||||
is WalletManageButton.Receive -> button
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ internal class UnlockWalletTransformer(
|
|||
private val clickIntents: WalletClickIntents,
|
||||
private val walletImageResolver: WalletImageResolver,
|
||||
private val getWalletIconUseCase: GetWalletIconUseCase,
|
||||
private val isAddFundsStage1Enabled: Boolean,
|
||||
) : WalletScreenStateTransformer {
|
||||
|
||||
private val walletLoadingStateFactory by lazy {
|
||||
|
|
@ -25,6 +26,7 @@ internal class UnlockWalletTransformer(
|
|||
clickIntents = clickIntents,
|
||||
walletImageResolver = walletImageResolver,
|
||||
getWalletIconUseCase = getWalletIconUseCase,
|
||||
isAddFundsStage1Enabled = isAddFundsStage1Enabled,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,16 +13,12 @@ import com.tangem.domain.models.StatusSource
|
|||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.account.PaymentAccountStatusValue
|
||||
import com.tangem.domain.models.kyc.KycStatus
|
||||
import com.tangem.domain.pay.TangemPayDetailsConfig
|
||||
import com.tangem.domain.visa.model.TangemPayCardFrozenState
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.TangemPayIntents
|
||||
import com.tangem.features.tangempay.entity.TangemPayMainUM
|
||||
import com.tangem.utils.converter.Converter
|
||||
import java.math.BigDecimal
|
||||
import java.util.Currency
|
||||
|
||||
private const val POLYGON_CHAIN_ID = 137
|
||||
|
||||
internal class TangemPayMainBlockConverter(
|
||||
private val tangemPayClickIntents: TangemPayIntents,
|
||||
private val isRedesignEnabled: Boolean,
|
||||
|
|
@ -64,25 +60,9 @@ internal class TangemPayMainBlockConverter(
|
|||
currencyCode = statusValue.fiatBalance.currency,
|
||||
balance = statusValue.fiatBalance.availableBalance,
|
||||
),
|
||||
balanceSubtitle = stringReference("USDC"), // TODO hardcode for now
|
||||
balanceSubtitle = stringReference(statusValue.cryptoCurrency.symbol),
|
||||
shouldShowOnlyCacheWarning = statusValue.source == StatusSource.ONLY_CACHE,
|
||||
onClick = {
|
||||
// Dummy config for deactivated account just to open details screen
|
||||
tangemPayClickIntents.openDetails(
|
||||
userWalletId = value.account.userWalletId,
|
||||
config = TangemPayDetailsConfig(
|
||||
customerId = "",
|
||||
cardId = "",
|
||||
isPinSet = false,
|
||||
cardFrozenState = TangemPayCardFrozenState.Unfrozen,
|
||||
cardNumberEnd = "",
|
||||
chainId = POLYGON_CHAIN_ID,
|
||||
displayName = null,
|
||||
isTangemPayDeactivated = true,
|
||||
isReissuing = false,
|
||||
),
|
||||
)
|
||||
},
|
||||
onClick = { tangemPayClickIntents.openDetails(value) },
|
||||
)
|
||||
is PaymentAccountStatusValue.Loaded -> {
|
||||
val card = statusValue.cards.firstOrNull() ?: return TangemPayMainUM.TemporaryUnavailable
|
||||
|
|
@ -97,28 +77,9 @@ internal class TangemPayMainBlockConverter(
|
|||
currencyCode = statusValue.currencyCode,
|
||||
balance = statusValue.fiatBalance.availableBalance,
|
||||
),
|
||||
balanceSubtitle = stringReference("USDC"), // TODO hardcode for now
|
||||
balanceSubtitle = stringReference(statusValue.cryptoCurrency.symbol),
|
||||
shouldShowOnlyCacheWarning = statusValue.source == StatusSource.ONLY_CACHE,
|
||||
onClick = {
|
||||
tangemPayClickIntents.openDetails(
|
||||
value.account.userWalletId,
|
||||
TangemPayDetailsConfig(
|
||||
customerId = statusValue.customerId,
|
||||
cardId = card.id,
|
||||
isPinSet = card.hasPinCode,
|
||||
cardFrozenState = if (card.isFrozen) {
|
||||
TangemPayCardFrozenState.Frozen
|
||||
} else {
|
||||
TangemPayCardFrozenState.Unfrozen
|
||||
},
|
||||
cardNumberEnd = card.lastDigits,
|
||||
chainId = POLYGON_CHAIN_ID,
|
||||
displayName = card.displayName,
|
||||
isTangemPayDeactivated = false,
|
||||
isReissuing = card.isReissuing,
|
||||
),
|
||||
)
|
||||
},
|
||||
onClick = { tangemPayClickIntents.openDetails(value) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.tangem.core.ui.components.tokenlist.state.PortfolioTokensListItemUM
|
|||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.models.hasMultiCurrencyAccount
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.TotalFiatBalance
|
||||
|
|
@ -40,7 +41,7 @@ internal class TokenListStateConverter(
|
|||
private val clickIntents: WalletClickIntents,
|
||||
private val yieldModuleApyMap: Map<String, BigDecimal>,
|
||||
private val stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability>,
|
||||
private val shouldShowMainPromo: Boolean,
|
||||
shouldShowMainPromo: Boolean,
|
||||
private val isAddAndManageTokensEnabled: Boolean,
|
||||
) : Converter<WalletTokensListState, WalletTokensListState> {
|
||||
|
||||
|
|
@ -169,7 +170,8 @@ internal class TokenListStateConverter(
|
|||
}
|
||||
|
||||
private fun getOrganizeTokensButtonStateV2(accountList: AccountStatusList): WalletOrganizeTokensButtonConfig? {
|
||||
return if (accountList.flattenCurrencies().size > 1 && !isSingleCurrencyWalletWithToken()) {
|
||||
val shouldShowOrganizeIfOldButton = accountList.hasMultiCurrencyAccount() || isAddAndManageTokensEnabled
|
||||
return if (shouldShowOrganizeIfOldButton && !isSingleCurrencyWalletWithToken()) {
|
||||
WalletOrganizeTokensButtonConfig(
|
||||
textRes = organizeButtonTextRes(),
|
||||
iconRes = organizeButtonIconRes(),
|
||||
|
|
|
|||
|
|
@ -84,15 +84,11 @@ internal class WalletTokensListUMConverter(
|
|||
.asSequence()
|
||||
.flatMap { accountStatus ->
|
||||
if (isAccountsModeEnabled) {
|
||||
val currencies = accountStatus.tokenList.flattenCurrencies()
|
||||
val isCollapsable = currencies.isNotEmpty()
|
||||
val isExpanded =
|
||||
currencies.isEmpty() || expandedAccounts.contains(accountStatus.account.accountId)
|
||||
sequenceOf(
|
||||
TokensListItemUM2.Portfolio(
|
||||
tokenRowUM = accountRowConverter.convert(accountStatus),
|
||||
isExpanded = isExpanded,
|
||||
isCollapsable = isCollapsable,
|
||||
isExpanded = expandedAccounts.contains(accountStatus.account.accountId),
|
||||
isCollapsable = true,
|
||||
onEmptyClick = { clickIntents.onManageTokensClick(accountStatus.account.accountId) },
|
||||
tokenList = getTokenListItems(
|
||||
accountStatus,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.utils
|
||||
|
||||
import com.tangem.core.ui.ds.button.TangemButtonUM
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletManageButton
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
|
|
@ -16,11 +18,24 @@ internal fun WalletState.MultiCurrency.Content.disableButtons(): PersistentList<
|
|||
}
|
||||
|
||||
internal fun WalletUM.Content.enableButtons(): PersistentList<TangemButtonUM> {
|
||||
return buttons.map { it.copy(isEnabled = true) }.toPersistentList()
|
||||
return buttons.map { it.withEnabled(isEnabled = true) }.toPersistentList()
|
||||
}
|
||||
|
||||
internal fun WalletUM.Content.disableButtons(): PersistentList<TangemButtonUM> {
|
||||
return buttons.map { it.copy(isEnabled = false) }.toPersistentList()
|
||||
return buttons.map { it.withEnabled(isEnabled = false) }.toPersistentList()
|
||||
}
|
||||
|
||||
private fun TangemButtonUM.withEnabled(isEnabled: Boolean): TangemButtonUM {
|
||||
val refreshedIcon = (tangemIconUM as? TangemIconUM.Icon)?.copy(
|
||||
tint = {
|
||||
if (isEnabled) {
|
||||
TangemTheme.colors2.graphic.neutral.primary
|
||||
} else {
|
||||
TangemTheme.colors2.graphic.neutral.quaternary
|
||||
}
|
||||
},
|
||||
) ?: tangemIconUM
|
||||
return copy(isEnabled = isEnabled, tangemIconUM = refreshedIcon)
|
||||
}
|
||||
|
||||
private fun WalletState.MultiCurrency.Content.changeAvailability(enabled: Boolean): PersistentList<WalletManageButton> {
|
||||
|
|
@ -28,6 +43,7 @@ private fun WalletState.MultiCurrency.Content.changeAvailability(enabled: Boolea
|
|||
.map { action ->
|
||||
when (action) {
|
||||
is WalletManageButton.Buy -> action.copy(enabled = enabled)
|
||||
is WalletManageButton.AddFunds -> action.copy(enabled = enabled)
|
||||
is WalletManageButton.Sell -> action.copy(enabled = enabled)
|
||||
is WalletManageButton.Swap -> action.copy(enabled = enabled)
|
||||
else -> action
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ internal class WalletLoadingStateFactory(
|
|||
private val clickIntents: WalletClickIntents,
|
||||
private val walletImageResolver: WalletImageResolver,
|
||||
private val getWalletIconUseCase: GetWalletIconUseCase,
|
||||
private val isAddFundsStage1Enabled: Boolean,
|
||||
) {
|
||||
|
||||
fun create(userWallet: UserWallet): WalletState {
|
||||
|
|
@ -149,7 +150,13 @@ internal class WalletLoadingStateFactory(
|
|||
userWallet is UserWallet.Cold && userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()
|
||||
if (isSingleWalletWithToken) return persistentListOf()
|
||||
|
||||
return persistentListOf(
|
||||
val firstButton = if (isAddFundsStage1Enabled) {
|
||||
WalletManageButton.AddFunds(
|
||||
enabled = true,
|
||||
dimContent = false,
|
||||
onClick = { clickIntents.onAddFundsClick(userWallet.walletId) },
|
||||
)
|
||||
} else {
|
||||
WalletManageButton.Buy(
|
||||
enabled = true,
|
||||
dimContent = false,
|
||||
|
|
@ -159,7 +166,11 @@ internal class WalletLoadingStateFactory(
|
|||
WALLET_TYPE,
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return persistentListOf(
|
||||
firstButton,
|
||||
WalletManageButton.Swap(
|
||||
enabled = true,
|
||||
dimContent = false,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.promo.GetStoryContentUseCase
|
||||
import com.tangem.domain.promo.models.StoryContentIds
|
||||
import com.tangem.domain.stories.GetStoryContentUseCase
|
||||
import com.tangem.domain.stories.models.StoryContentIds
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.UpdateMultiWalletActionButtonBadgeTransformer
|
||||
import dagger.assisted.Assisted
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ 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.analytics.WalletScreenAnalyticsEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetPrimaryCurrencyTransformer
|
||||
import dagger.assisted.Assisted
|
||||
|
|
@ -70,7 +70,7 @@ internal class PrimaryCurrencySubscriber @AssistedInject constructor(
|
|||
cardBalanceState?.let { balanceState ->
|
||||
// do not send tokens count for single currency wallet
|
||||
analyticsEventHandler.send(
|
||||
event = WalletScreenAnalyticsEvent.Basic.BalanceLoaded(
|
||||
event = Basic.BalanceLoaded(
|
||||
balance = balanceState,
|
||||
tokensCount = null,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ 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.expressTransactionsItems
|
||||
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
|
||||
|
|
@ -236,7 +236,7 @@ private fun WalletContent(
|
|||
marketPriceBlock(state = marketPriceBlockState, modifier = itemModifier)
|
||||
}
|
||||
if (walletState is WalletState.SingleCurrency.Content) {
|
||||
expressTransactionsItems(
|
||||
expressTransactionsItemsLegacy(
|
||||
expressTxs = walletState.expressTxsToDisplay,
|
||||
modifier = itemModifier,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ 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.animateColorAsState
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.spring
|
||||
|
|
@ -42,6 +43,7 @@ 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.Dp
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.arkivanov.decompose.ExperimentalDecomposeApi
|
||||
import com.tangem.core.ui.components.BottomFade
|
||||
|
|
@ -57,6 +59,7 @@ import com.tangem.core.ui.components.sheetscaffold.*
|
|||
import com.tangem.core.ui.ds.topbar.collapsing.TangemCollapsingAppBarBehavior
|
||||
import com.tangem.core.ui.ds.topbar.collapsing.TangemCollapsingTopBar
|
||||
import com.tangem.core.ui.ds.topbar.collapsing.rememberTangemExitUntilCollapsedScrollBehavior
|
||||
import com.tangem.core.ui.extensions.softLayerShadow
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.res.*
|
||||
import com.tangem.core.ui.utils.TangemSharedTransitionLayout
|
||||
|
|
@ -184,6 +187,7 @@ private fun WalletContent2(
|
|||
WalletTopBar(
|
||||
topBarConfig = state.topBarConfig,
|
||||
walletBalance = walletBalance,
|
||||
isBalanceHidden = state.isHidingMode,
|
||||
behavior = behavior,
|
||||
)
|
||||
},
|
||||
|
|
@ -209,16 +213,27 @@ private fun WalletContent2(
|
|||
.fillMaxSize()
|
||||
.hazeSourceTangem(zIndex = -2f),
|
||||
) {
|
||||
NorthernLightsBackground(
|
||||
containerColor = if (LocalIsInDarkTheme.current) {
|
||||
TangemTheme.colors2.surface.level1
|
||||
} else {
|
||||
TangemTheme.colors2.surface.level2
|
||||
},
|
||||
val backgroundColor = if (LocalIsInDarkTheme.current) {
|
||||
TangemTheme.colors2.surface.level1
|
||||
} else {
|
||||
TangemTheme.colors2.surface.level2
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.graphicsLayer { alpha = 1 - behavior.state.collapsedFraction * 2 }
|
||||
.matchParentSize(),
|
||||
.matchParentSize()
|
||||
.background(backgroundColor),
|
||||
)
|
||||
val isSheetExpanded by remember {
|
||||
derivedStateOf { bottomSheetState.targetValue == TangemSheetValue.Expanded }
|
||||
}
|
||||
if (!isSheetExpanded) {
|
||||
NorthernLightsBackground(
|
||||
containerColor = backgroundColor,
|
||||
modifier = Modifier
|
||||
.graphicsLayer { alpha = 1 - behavior.state.collapsedFraction * 2 }
|
||||
.matchParentSize(),
|
||||
)
|
||||
}
|
||||
|
||||
WalletPagerIndicator(
|
||||
pagerState = walletsPagerState,
|
||||
|
|
@ -351,13 +366,23 @@ private inline fun BaseScaffoldWithMarkets(
|
|||
val peekHeight = bottomSheetHeaderHeightProvider() + TangemTheme.dimens2.x3 + bottomBarHeight
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val background = TangemTheme.colors2.surface.level2
|
||||
|
||||
val bottomSheetState = rememberTangemStandardBottomSheetState()
|
||||
val scaffoldState = rememberTangemBottomSheetScaffoldState(bottomSheetState = bottomSheetState)
|
||||
|
||||
val expandedBackground = TangemTheme.colors2.surface.level2
|
||||
val collapsedBackground = TangemTheme.colors2.surface.level3
|
||||
val background by animateColorAsState(
|
||||
targetValue = if (bottomSheetState.targetValue == TangemSheetValue.Expanded) {
|
||||
expandedBackground
|
||||
} else {
|
||||
collapsedBackground
|
||||
},
|
||||
label = "bottomSheetBackground",
|
||||
)
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalMainBottomSheetColor provides remember(background) { mutableStateOf(background) },
|
||||
LocalMainBottomSheetColor provides remember { mutableStateOf(background) }.apply { value = background },
|
||||
) {
|
||||
val backgroundColor by LocalMainBottomSheetColor.current
|
||||
var isSearchFieldFocused by remember { mutableStateOf(false) }
|
||||
|
|
@ -487,6 +512,13 @@ private fun BottomSheet(
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.softLayerShadow(
|
||||
radius = 16.dp,
|
||||
color = Color.Black.copy(alpha = if (LocalIsInDarkTheme.current) .24f else .12f),
|
||||
shape = shape,
|
||||
offset = DpOffset(x = 0.dp, y = (-6).dp),
|
||||
isAlphaContentClip = true,
|
||||
)
|
||||
.clip(shape)
|
||||
.background(backgroundColor)
|
||||
.onFocusChanged(onFocusChange),
|
||||
|
|
|
|||
|
|
@ -27,11 +27,7 @@ internal fun LazyListScope.notifications(configs: ImmutableList<WalletNotificati
|
|||
key = { it::class.java },
|
||||
contentType = { it::class.java },
|
||||
itemContent = { item ->
|
||||
// TODO develop promo banner general component
|
||||
when (item) {
|
||||
is WalletNotification.SwapPromo -> {
|
||||
// Use it on new promo action
|
||||
}
|
||||
is WalletNotification.NoteMigration -> {
|
||||
NoteMigrationNotification(
|
||||
config = item.config,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.pager.PagerState
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.scale
|
||||
|
|
@ -58,13 +59,13 @@ internal fun WalletPagerIndicator(
|
|||
.fillMaxWidth()
|
||||
.height(height)
|
||||
.alpha(alpha),
|
||||
contentAlignment = Alignment.TopCenter,
|
||||
) {
|
||||
TangemPagerIndicator(
|
||||
pagerState = pagerState,
|
||||
modifier = Modifier
|
||||
.padding(top = padding)
|
||||
.scale(scaleY = 1f, scaleX = scale)
|
||||
.fillMaxWidth(),
|
||||
.scale(scaleY = 1f, scaleX = scale),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common
|
|||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
|
|
@ -27,6 +26,7 @@ import com.tangem.core.ui.ds.topbar.TangemTopBarActionUM
|
|||
import com.tangem.core.ui.ds.topbar.collapsing.TangemCollapsingAppBarBehavior
|
||||
import com.tangem.core.ui.ds.topbar.collapsing.rememberTangemExitUntilCollapsedScrollBehavior
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.orMaskWithStars
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.LocalRootBackgroundColor
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -47,12 +47,14 @@ private const val VISIBILITY_THRESHOLD = 0.5f
|
|||
*
|
||||
* @param topBarConfig top bar config
|
||||
* @param walletBalance wallet balance text reference
|
||||
* @param isBalanceHidden whether the balance must be masked with stars
|
||||
* @param behavior collapsing behavior
|
||||
*/
|
||||
@Composable
|
||||
internal fun WalletTopBar(
|
||||
topBarConfig: WalletTopBarConfig,
|
||||
walletBalance: TextReference?,
|
||||
isBalanceHidden: Boolean,
|
||||
behavior: TangemCollapsingAppBarBehavior,
|
||||
) {
|
||||
Surface(
|
||||
|
|
@ -64,8 +66,8 @@ internal fun WalletTopBar(
|
|||
derivedStateOf { behavior.state.collapsedFraction > VISIBILITY_THRESHOLD }
|
||||
}
|
||||
|
||||
val wrappedBalance = remember(walletBalance, isWrappedBalanceShown) {
|
||||
walletBalance.takeIf { isWrappedBalanceShown }
|
||||
val wrappedBalance = remember(walletBalance, isWrappedBalanceShown, isBalanceHidden) {
|
||||
walletBalance?.orMaskWithStars(isBalanceHidden).takeIf { isWrappedBalanceShown }
|
||||
}
|
||||
|
||||
TangemTopBar(
|
||||
|
|
@ -80,7 +82,6 @@ internal fun WalletTopBar(
|
|||
},
|
||||
endContent = {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x5),
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.background(
|
||||
|
|
@ -175,6 +176,7 @@ private fun WalletTopBar_Preview() {
|
|||
WalletTopBar(
|
||||
topBarConfig = WalletTopBarConfig(),
|
||||
walletBalance = stringReference("$ 8923,05"),
|
||||
isBalanceHidden = false,
|
||||
behavior = rememberTangemExitUntilCollapsedScrollBehavior(),
|
||||
)
|
||||
}
|
||||
|
|
@ -199,6 +201,7 @@ private fun WalletTopBar_WithQrButton_Preview() {
|
|||
),
|
||||
),
|
||||
walletBalance = stringReference("$ 8923,05"),
|
||||
isBalanceHidden = false,
|
||||
behavior = rememberTangemExitUntilCollapsedScrollBehavior(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,12 +34,13 @@ import com.tangem.common.ui.tokens.NonContentItemContent
|
|||
import com.tangem.common.ui.tokens.SlideInItemVisibility
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.account.AccountIconSize
|
||||
import com.tangem.core.ui.components.account.toBoxSize
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.tokenlist.NON_CONTENT_TOKENS_LIST_KEY
|
||||
import com.tangem.core.ui.components.tokenlist.TokenListItem
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.ds.button.PrimaryInverseTangemButton
|
||||
import com.tangem.core.ui.ds.button.SecondaryTangemButton
|
||||
import com.tangem.core.ui.ds.button.TangemButtonShape
|
||||
import com.tangem.core.ui.ds.button.TangemButtonSize
|
||||
import com.tangem.core.ui.ds.image.TangemIcon
|
||||
|
|
@ -55,6 +56,7 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
import com.tangem.core.ui.test.MainScreenTestTags
|
||||
import com.tangem.core.ui.utils.ProvideSharedTransitionScope
|
||||
import com.tangem.core.ui.utils.lazyListItemPosition
|
||||
import com.tangem.core.ui.utils.sharedBoundsSafely
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TokensListItemUM2
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState
|
||||
|
|
@ -205,8 +207,6 @@ private fun LazyListScope.portfolioItem(
|
|||
if (listItem.tokenList.isEmpty()) {
|
||||
nonContentAccountItem(
|
||||
listItem = listItem,
|
||||
index = index,
|
||||
lastIndex = lastIndex,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
|
|
@ -221,7 +221,7 @@ private fun LazyListScope.portfolioItem(
|
|||
modifier = modifier
|
||||
.animateItem(fadeInSpec = null, placementSpec = null, fadeOutSpec = null)
|
||||
.roundedShapeItemDecoration(
|
||||
radius = 18.dp,
|
||||
radius = if (listItem.isExpanded) TangemTheme.dimens2.x6 else TangemTheme.dimens2.x5,
|
||||
currentIndex = tokenIndex + 1,
|
||||
addDefaultPadding = false,
|
||||
lastIndex = lastIndex,
|
||||
|
|
@ -293,7 +293,7 @@ private fun LazyListScope.accountItem(
|
|||
.semantics { lazyListItemPosition = index }
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = 0,
|
||||
radius = 18.dp,
|
||||
radius = if (listItem.isExpanded) TangemTheme.dimens2.x6 else TangemTheme.dimens2.x5,
|
||||
addDefaultPadding = false,
|
||||
lastIndex = effectiveLastIndex,
|
||||
backgroundColor = TangemTheme.colors2.surface.level3,
|
||||
|
|
@ -382,13 +382,15 @@ internal fun PortfolioRowItem(
|
|||
headIcon
|
||||
}
|
||||
|
||||
val iconBoxSize = when (headIcon) {
|
||||
is TangemIconUM.Empty -> TangemTheme.dimens2.x9
|
||||
else -> size.toBoxSize()
|
||||
}
|
||||
TangemIcon(
|
||||
tangemIconUM = sizedHeadIcon,
|
||||
modifier = modifier
|
||||
.conditionalCompose(headIcon is TangemIconUM.Empty) {
|
||||
size(TangemTheme.dimens2.x9)
|
||||
}
|
||||
.sharedBounds(
|
||||
.size(iconBoxSize)
|
||||
.sharedBoundsSafely(
|
||||
sharedContentState = iconSharedContentState,
|
||||
animatedVisibilityScope = animatedContentScope,
|
||||
boundsTransform = boundsTransform,
|
||||
|
|
@ -422,7 +424,7 @@ internal fun PortfolioRowItem(
|
|||
|
||||
TokenRowTitle(
|
||||
titleUM = resizedTitle,
|
||||
modifier = modifier.sharedBounds(
|
||||
modifier = modifier.sharedBoundsSafely(
|
||||
sharedContentState = titleSharedContentState,
|
||||
animatedVisibilityScope = animatedContentScope,
|
||||
boundsTransform = boundsTransform,
|
||||
|
|
@ -489,23 +491,18 @@ private fun LazyListScope.nonContentItem2(onEmptyClick: () -> Unit, modifier: Mo
|
|||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.nonContentAccountItem(
|
||||
listItem: TokensListItemUM2.Portfolio,
|
||||
index: Int,
|
||||
lastIndex: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
private fun LazyListScope.nonContentAccountItem(listItem: TokensListItemUM2.Portfolio, modifier: Modifier = Modifier) {
|
||||
item(
|
||||
key = "$NON_CONTENT_TOKENS_LIST_KEY account-${listItem.tokenRowUM.id}",
|
||||
contentType = "$NON_CONTENT_TOKENS_LIST_KEY account-${listItem.tokenRowUM.id}",
|
||||
) {
|
||||
SlideInItemVisibility(
|
||||
currentIndex = index + 1,
|
||||
lastIndex = lastIndex,
|
||||
currentIndex = 1,
|
||||
lastIndex = 1,
|
||||
modifier = modifier
|
||||
.animateItem(fadeInSpec = null, placementSpec = null, fadeOutSpec = null)
|
||||
.roundedShapeItemDecoration(
|
||||
radius = 18.dp,
|
||||
radius = if (listItem.isExpanded) TangemTheme.dimens2.x6 else TangemTheme.dimens2.x5,
|
||||
addDefaultPadding = false,
|
||||
currentIndex = 1,
|
||||
lastIndex = 1,
|
||||
|
|
@ -542,8 +539,8 @@ internal fun NonContentItemContentV2(textColor: Color, modifier: Modifier = Modi
|
|||
textAlign = TextAlign.Center,
|
||||
style = TangemTheme.typography2.bodyRegular14,
|
||||
)
|
||||
SpacerH(TangemTheme.dimens2.x2)
|
||||
PrimaryInverseTangemButton(
|
||||
SpacerH(TangemTheme.dimens2.x4)
|
||||
SecondaryTangemButton(
|
||||
text = resourceReference(id = R.string.common_add_tokens),
|
||||
onClick = onClick,
|
||||
size = TangemButtonSize.X8,
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="36dp"
|
||||
android:height="36dp"
|
||||
android:viewportWidth="36"
|
||||
android:viewportHeight="36">
|
||||
<path
|
||||
android:pathData="M18,18m-18,0a18,18 0,1 1,36 0a18,18 0,1 1,-36 0"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillColor="#0099FF"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:pathData="M18.057,7.745C20.247,7.745 21.968,7.745 23.312,7.926C24.688,8.111 25.781,8.498 26.639,9.356C27.497,10.214 27.885,11.307 28.07,12.683C28.25,14.027 28.25,15.748 28.25,17.938V18.052C28.25,20.243 28.25,21.964 28.07,23.307C27.885,24.684 27.497,25.776 26.639,26.635C25.781,27.493 24.688,27.879 23.312,28.064C21.968,28.245 20.247,28.245 18.057,28.245H17.943C15.752,28.245 14.031,28.245 12.688,28.064C11.311,27.879 10.219,27.493 9.361,26.635C8.502,25.776 8.116,24.684 7.931,23.307C7.75,21.964 7.75,20.243 7.75,18.052V17.938C7.75,15.748 7.75,14.027 7.931,12.683C8.116,11.307 8.503,10.214 9.361,9.356C10.219,8.498 11.311,8.111 12.688,7.926C14.031,7.745 15.752,7.745 17.943,7.745H18.057ZM13,21.248C12.586,21.248 12.25,21.584 12.25,21.998V23.998C12.25,24.412 12.586,24.748 13,24.748C13.414,24.748 13.75,24.412 13.75,23.998V21.998C13.75,21.584 13.414,21.248 13,21.248ZM18,20.247C17.586,20.247 17.25,20.583 17.25,20.997V23.997C17.25,24.411 17.586,24.747 18,24.747C18.414,24.747 18.75,24.411 18.75,23.997V20.997C18.75,20.583 18.414,20.247 18,20.247ZM23,18.245C22.586,18.245 22.25,18.581 22.25,18.995V23.995C22.25,24.409 22.586,24.745 23,24.745C23.414,24.745 23.75,24.409 23.75,23.995V18.995C23.75,18.581 23.414,18.245 23,18.245ZM21.859,11.247C21.654,11.256 21.416,11.295 21.179,11.335C21.168,11.337 21.157,11.339 21.146,11.341L19.878,11.55C19.47,11.617 19.193,12.002 19.26,12.411C19.327,12.82 19.714,13.097 20.122,13.029L20.846,12.91C18.963,15.552 15.953,16.745 12,16.745C11.586,16.745 11.25,17.081 11.25,17.495C11.25,17.909 11.586,18.245 12,18.245C16.322,18.245 19.898,16.9 22.132,13.689L22.279,14.201C22.393,14.599 22.808,14.829 23.206,14.716C23.604,14.601 23.835,14.186 23.721,13.788L23.403,12.677C23.399,12.666 23.396,12.655 23.393,12.643C23.335,12.44 23.271,12.218 23.189,12.036C23.088,11.814 22.93,11.576 22.645,11.414C22.369,11.257 22.09,11.236 21.859,11.247Z"
|
||||
android:fillColor="#0099FF"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
|
|
@ -4,12 +4,18 @@ import com.google.common.truth.Truth.assertThat
|
|||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.decompose.model.MutableParamsContainer
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
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.tokenlist.TokenList
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.child.managetokens.AddAndManageBottomSheetComponent
|
||||
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioFetcher
|
||||
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorController
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.slot
|
||||
|
|
@ -38,6 +44,9 @@ internal class AddAndManageModelTest {
|
|||
private val portfolioSelectorController: PortfolioSelectorController = mockk(relaxed = true) {
|
||||
every { selectedAccount } returns flowOf(null)
|
||||
}
|
||||
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier = mockk(relaxed = true) {
|
||||
coEvery { getSyncOrNull(any<UserWalletId>()) } returns null
|
||||
}
|
||||
|
||||
private val onDismiss: () -> Unit = mockk(relaxed = true)
|
||||
private val onOrganizeTokensClick: () -> Unit = mockk(relaxed = true)
|
||||
|
|
@ -58,6 +67,7 @@ internal class AddAndManageModelTest {
|
|||
portfolioFetcherFactory = portfolioFetcherFactory,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
portfolioSelectorController = portfolioSelectorController,
|
||||
singleAccountStatusListSupplier = singleAccountStatusListSupplier,
|
||||
)
|
||||
|
||||
@Test
|
||||
|
|
@ -98,4 +108,38 @@ internal class AddAndManageModelTest {
|
|||
verify(exactly = 1) { onDismiss() }
|
||||
verify(exactly = 1) { onOrganizeTokensClick() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN wallet has multi currency account WHEN model is created THEN shouldShowOrganize is true`() = runTest {
|
||||
coEvery { singleAccountStatusListSupplier.getSyncOrNull(userWalletId) } returns
|
||||
accountStatusListWithCurrencyCounts(2)
|
||||
|
||||
val model = createModel()
|
||||
|
||||
assertThat(model.state.value.shouldShowOrganize).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN wallet has no multi currency account WHEN model is created THEN shouldShowOrganize is false`() = runTest {
|
||||
coEvery { singleAccountStatusListSupplier.getSyncOrNull(userWalletId) } returns
|
||||
accountStatusListWithCurrencyCounts(1)
|
||||
|
||||
val model = createModel()
|
||||
|
||||
assertThat(model.state.value.shouldShowOrganize).isFalse()
|
||||
}
|
||||
|
||||
private fun accountStatusListWithCurrencyCounts(vararg currencyCounts: Int): AccountStatusList {
|
||||
val statuses: List<AccountStatus> = currencyCounts.map { count ->
|
||||
val tokenList = mockk<TokenList> {
|
||||
every { flattenCurrencies() } returns List(count) { mockk<CryptoCurrencyStatus>() }
|
||||
}
|
||||
mockk<AccountStatus.CryptoPortfolio> {
|
||||
every { this@mockk.tokenList } returns tokenList
|
||||
}
|
||||
}
|
||||
return mockk {
|
||||
every { accountStatuses } returns statuses
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue