Updated on 2026-08-14
This commit is contained in:
commit
ae15bcfc55
211 changed files with 4715 additions and 3641 deletions
|
|
@ -5,6 +5,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.arkivanov.decompose.ExperimentalDecomposeApi
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.slot.childSlot
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
|
|
@ -14,6 +15,7 @@ import com.tangem.core.decompose.context.AppComponentContext
|
|||
import com.tangem.core.decompose.context.child
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
|
|
@ -23,6 +25,7 @@ import com.tangem.feature.wallet.child.wallet.model.WalletModel
|
|||
import com.tangem.feature.wallet.navigation.WalletRoute
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen2
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.KycRejectedComponent
|
||||
import com.tangem.feature.walletsettings.component.RenameWalletComponent
|
||||
import com.tangem.features.biometry.AskBiometryComponent
|
||||
|
|
@ -38,6 +41,7 @@ import dagger.assisted.AssistedFactory
|
|||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalDecomposeApi::class)
|
||||
@Suppress("LongParameterList")
|
||||
internal class WalletComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
|
|
@ -50,6 +54,7 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
private val tokenReceiveComponentFactory: TokenReceiveComponent.Factory,
|
||||
private val yieldSupplyDepositedWarningComponent: YieldSupplyDepositedWarningComponent.Factory,
|
||||
private val feedFeatureToggle: FeedFeatureToggle,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: WalletModel = getOrCreateModel()
|
||||
|
|
@ -148,18 +153,33 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
var headerSize by remember { mutableStateOf(0.dp) }
|
||||
val dialog by dialog.subscribeAsState()
|
||||
|
||||
WalletScreen(
|
||||
state = model.uiState.collectAsStateWithLifecycle().value,
|
||||
bottomSheetContent = {
|
||||
BottomSheetContent(
|
||||
bottomSheetState = bottomSheetState,
|
||||
onHeaderSizeChange = { headerSize = it },
|
||||
modifier = modifier,
|
||||
)
|
||||
},
|
||||
bottomSheetHeaderHeightProvider = { headerSize },
|
||||
onBottomSheetStateChange = { bottomSheetState.value = it },
|
||||
)
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
WalletScreen2(
|
||||
state = model.uiState.collectAsStateWithLifecycle().value,
|
||||
bottomSheetContent = {
|
||||
BottomSheetContent(
|
||||
bottomSheetState = bottomSheetState,
|
||||
onHeaderSizeChange = { headerSize = it },
|
||||
modifier = modifier,
|
||||
)
|
||||
},
|
||||
bottomSheetHeaderHeightProvider = { headerSize },
|
||||
onBottomSheetStateChange = { bottomSheetState.value = it },
|
||||
)
|
||||
} else {
|
||||
WalletScreen(
|
||||
state = model.uiState.collectAsStateWithLifecycle().value,
|
||||
bottomSheetContent = {
|
||||
BottomSheetContent(
|
||||
bottomSheetState = bottomSheetState,
|
||||
onHeaderSizeChange = { headerSize = it },
|
||||
modifier = modifier,
|
||||
)
|
||||
},
|
||||
bottomSheetHeaderHeightProvider = { headerSize },
|
||||
onBottomSheetStateChange = { bottomSheetState.value = it },
|
||||
)
|
||||
}
|
||||
|
||||
when (val dialog = dialog.child?.instance) {
|
||||
is ComposableDialogComponent -> dialog.Dialog()
|
||||
|
|
|
|||
|
|
@ -45,9 +45,7 @@ import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvid
|
|||
import com.tangem.features.biometry.AskBiometryComponent
|
||||
import com.tangem.features.feed.entry.featuretoggle.FeedFeatureToggle
|
||||
import com.tangem.features.pushnotifications.api.PushNotificationsModelCallbacks
|
||||
import com.tangem.features.tangempay.TangemPayFeatureToggles
|
||||
import com.tangem.features.wallet.deeplink.WalletDeepLinkActionListener
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyFeatureToggles
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.*
|
||||
import kotlinx.coroutines.*
|
||||
|
|
@ -90,10 +88,8 @@ internal class WalletModel @Inject constructor(
|
|||
private val setNotificationsEnabledUseCase: SetNotificationsEnabledUseCase,
|
||||
private val getIsHuaweiDeviceWithoutGoogleServicesUseCase: GetIsHuaweiDeviceWithoutGoogleServicesUseCase,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
|
||||
private val yieldSupplyApyUpdateUseCase: YieldSupplyApyUpdateUseCase,
|
||||
private val tangemPayOnboardingRepository: OnboardingRepository,
|
||||
private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase,
|
||||
private val getAppThemeModeUseCase: GetAppThemeModeUseCase,
|
||||
|
|
@ -166,10 +162,8 @@ internal class WalletModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun updateYieldSupplyApy() {
|
||||
if (yieldSupplyFeatureToggles.isYieldSupplyFeatureEnabled) {
|
||||
modelScope.launch(dispatchers.default) {
|
||||
yieldSupplyApyUpdateUseCase()
|
||||
}
|
||||
modelScope.launch(dispatchers.default) {
|
||||
yieldSupplyApyUpdateUseCase()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -394,7 +388,6 @@ internal class WalletModel @Inject constructor(
|
|||
* Update state each time a user opens/returns to wallet screen
|
||||
* and every minute while user stays on the main screen
|
||||
*/
|
||||
if (!tangemPayFeatureToggles.isTangemPayEnabled) return
|
||||
|
||||
combine(
|
||||
flow = screenLifecycleProvider.isBackgroundState,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletType
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -110,7 +111,7 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
when (walletState) {
|
||||
is WalletState.MultiCurrency -> {
|
||||
val wallet = wallets.firstOrNull { it.walletId == walletState.walletCardState.id }
|
||||
walletState.type == WalletState.MultiCurrency.WalletType.Hot && wallet is UserWallet.Cold
|
||||
walletState.type == WalletType.Hot && wallet is UserWallet.Cold
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
|
@ -212,7 +213,7 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
val previousState = state.wallets.firstOrNull { it.walletCardState.id == wallet.walletId }
|
||||
?: return@filter false
|
||||
wallet is UserWallet.Cold && previousState is WalletState.MultiCurrency &&
|
||||
previousState.type == WalletState.MultiCurrency.WalletType.Hot
|
||||
previousState.type == WalletType.Hot
|
||||
}
|
||||
return Action.ReinitializeWallets(selectedWallet, walletsToUpdate)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogCon
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TangemPayHideOnboardingStateTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TangemPayRefreshNeededStateTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TangemPayRefreshShowProgressTransformer
|
||||
import com.tangem.features.tangempay.TangemPayFeatureToggles
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -64,7 +63,6 @@ internal interface TangemPayIntents {
|
|||
@ModelScoped
|
||||
internal class TangemPayClickIntentsImplementor @Inject constructor(
|
||||
private val stateHolder: WalletStateController,
|
||||
private val featureToggles: TangemPayFeatureToggles,
|
||||
private val onboardingRepository: OnboardingRepository,
|
||||
private val produceInitialDataTangemPay: ProduceTangemPayInitialDataUseCase,
|
||||
private val getWalletMetainfoUseCase: GetWalletMetaInfoUseCase,
|
||||
|
|
@ -77,9 +75,7 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
|
|||
|
||||
override suspend fun onPullToRefresh() {
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
if (!featureToggles.isTangemPayEnabled ||
|
||||
!onboardingRepository.isTangemPayInitialDataProduced(userWalletId)
|
||||
) {
|
||||
if (!onboardingRepository.isTangemPayInitialDataProduced(userWalletId)) {
|
||||
return
|
||||
}
|
||||
tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId)
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@ 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.analytics.models.event.MainScreenAnalyticsEvent
|
||||
import com.tangem.core.analytics.models.event.OfframpAnalyticsEvent
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
|
@ -36,13 +38,12 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
|||
import com.tangem.domain.models.network.NetworkAddress
|
||||
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.redux.ReduxStateHolder
|
||||
import com.tangem.domain.staking.model.StakingOption
|
||||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.tokens.model.analytics.TokenReceiveCopyActionSource
|
||||
import com.tangem.domain.tokens.model.analytics.TokenReceiveNewAnalyticsEvent
|
||||
|
|
@ -63,7 +64,6 @@ 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.transformers.CloseBottomSheetTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyFeatureToggles
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -136,14 +136,14 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
private val getStoryContentUseCase: GetStoryContentUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val getOfframpUrlUseCase: GetOfframpUrlUseCase,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val vibratorHapticManager: VibratorHapticManager,
|
||||
private val clipboardManager: ClipboardManager,
|
||||
private val appRouter: AppRouter,
|
||||
private val rampStateManager: RampStateManager,
|
||||
private val saveViewedTokenReceiveWarningUseCase: SaveViewedTokenReceiveWarningUseCase,
|
||||
private val receiveAddressesFactory: ReceiveAddressesFactory,
|
||||
private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles,
|
||||
private val needShowYieldSupplyDepositedWarningUseCase: NeedShowYieldSupplyDepositedWarningUseCase,
|
||||
private val saveViewedYieldSupplyWarningUseCase: SaveViewedYieldSupplyWarningUseCase,
|
||||
private val isCryptoCurrencyCoinCouldHide: IsCryptoCurrencyCoinCouldHideUseCase,
|
||||
|
|
@ -335,12 +335,13 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
|
||||
showErrorIfDemoModeOrElse {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.Sell(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
appCurrencyCode = getSelectedAppCurrencyUseCase.unwrap().code,
|
||||
),
|
||||
)
|
||||
getOfframpUrlUseCase(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
appCurrencyCode = getSelectedAppCurrencyUseCase.unwrap().code,
|
||||
).onRight { url ->
|
||||
urlOpener.openUrl(url)
|
||||
analyticsEventHandler.send(OfframpAnalyticsEvent.ScreenOpened)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -472,9 +473,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
|
||||
override fun onMultiWalletSwapClick(userWalletId: UserWalletId) {
|
||||
val selectedWallet = stateHolder.getSelectedWallet() as? WalletState.MultiCurrency.Content ?: return
|
||||
val tokenListState = selectedWallet.tokensListState
|
||||
|
||||
when (tokenListState) {
|
||||
when (val tokenListState = selectedWallet.tokensListState) {
|
||||
is WalletTokensListState.ContentState.Content -> checkSwapCryptoAvailability(
|
||||
tokenCount = tokenListState.items.count { it is TokensListItemUM.Token },
|
||||
)
|
||||
|
|
@ -663,8 +662,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun needShowYieldSupplyWarning(cryptoCurrencyStatus: CryptoCurrencyStatus): Boolean {
|
||||
return yieldSupplyFeatureToggles.isYieldSupplyFeatureEnabled &&
|
||||
needShowYieldSupplyDepositedWarningUseCase(cryptoCurrencyStatus)
|
||||
return needShowYieldSupplyDepositedWarningUseCase(cryptoCurrencyStatus)
|
||||
}
|
||||
|
||||
private fun navigateToSend(cryptoCurrencyStatus: CryptoCurrencyStatus, userWalletId: UserWalletId) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.feature.wallet.presentation.common
|
||||
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
|
|
@ -186,17 +185,6 @@ internal object WalletPreviewData {
|
|||
)
|
||||
}
|
||||
|
||||
val bottomSheet by lazy {
|
||||
TangemBottomSheetConfig(
|
||||
isShown = false,
|
||||
onDismissRequest = {},
|
||||
content = WalletBottomSheetConfig.UnlockWallets(
|
||||
onUnlockClick = {},
|
||||
onScanClick = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
val actionsBottomSheet = ActionsBottomSheetConfig(
|
||||
actions = listOf(
|
||||
TokenActionButtonConfig(
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ internal object WalletScreenPreviewData {
|
|||
onItemClick = { },
|
||||
),
|
||||
tangemPayState = TangemPayState.Empty,
|
||||
type = WalletState.MultiCurrency.WalletType.Cold,
|
||||
type = WalletType.Cold,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -218,6 +218,7 @@ internal object WalletScreenPreviewData {
|
|||
singleWalletLockedState,
|
||||
multiWalletState,
|
||||
),
|
||||
wallets2 = persistentListOf(),
|
||||
onWalletChange = { _, _ -> },
|
||||
event = consumedEvent(),
|
||||
isHidingMode = false,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
package com.tangem.feature.wallet.presentation.preview
|
||||
|
||||
import com.tangem.core.ui.extensions.combinedReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.styledStringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletBalanceUM
|
||||
|
||||
internal object WalletBalancePreview {
|
||||
|
||||
val content: WalletBalanceUM.Content = WalletBalanceUM.Content(
|
||||
id = UserWalletId("0"),
|
||||
name = "My Wallet",
|
||||
balance = combinedReference(
|
||||
stringReference("1,234"),
|
||||
styledStringReference(
|
||||
".56",
|
||||
{
|
||||
TangemTheme.typography2.headingRegular28.toSpanStyle()
|
||||
},
|
||||
),
|
||||
stringReference(" $"),
|
||||
),
|
||||
isBalanceFlickering = false,
|
||||
isZeroBalance = false,
|
||||
)
|
||||
|
||||
val loading: WalletBalanceUM.Loading = WalletBalanceUM.Loading(
|
||||
id = UserWalletId("1"),
|
||||
name = "My Wallet",
|
||||
)
|
||||
|
||||
val error: WalletBalanceUM.Error = WalletBalanceUM.Error(
|
||||
id = UserWalletId("2"),
|
||||
name = "My Wallet",
|
||||
)
|
||||
}
|
||||
|
|
@ -8,9 +8,8 @@ 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.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.PushBannerPromo.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.PushBannerPromo.PushBanner
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -34,10 +33,29 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun send(displayedWalletUM: WalletUM?, newNotifications: List<WalletNotificationUM>) {
|
||||
if (screenLifecycleProvider.isBackgroundState.value) return
|
||||
if (newNotifications.isEmpty()) return
|
||||
if (displayedWalletUM == null || displayedWalletUM.pullToRefreshConfig.isRefreshing) return
|
||||
|
||||
val totalNotifications = displayedWalletUM.notifications + displayedWalletUM.notificationsCarousel
|
||||
val notificationsDiff = newNotifications.filter { it !in totalNotifications }
|
||||
|
||||
val eventsToSend = getEvents2(notificationsDiff)
|
||||
|
||||
eventsToSend.forEach { event ->
|
||||
analyticsEventHandler.send(event)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getEvents(warnings: List<WalletNotification>): Set<AnalyticsEvent> {
|
||||
return warnings.mapNotNullTo(mutableSetOf(), ::getEvent)
|
||||
}
|
||||
|
||||
private fun getEvents2(notifications: List<WalletNotificationUM>): Set<AnalyticsEvent> {
|
||||
return notifications.mapNotNullTo(mutableSetOf(), ::getEvent2)
|
||||
}
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
private fun getEvent(warning: WalletNotification): AnalyticsEvent? {
|
||||
return when (warning) {
|
||||
|
|
@ -106,4 +124,53 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
|
|||
is WalletNotification.UpgradeHotWalletPromo -> null
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
private fun getEvent2(notificationUM: WalletNotificationUM): AnalyticsEvent? {
|
||||
return when (notificationUM) {
|
||||
WalletNotificationUM.DevCard -> DevelopmentCard()
|
||||
WalletNotificationUM.FailedCardValidation -> ProductSampleCard()
|
||||
is WalletNotificationUM.MissingBackup -> BackupYourWallet()
|
||||
is WalletNotificationUM.NumberOfSignedHashesIncorrect -> CardSignedTransactions()
|
||||
WalletNotificationUM.TestnetCard -> TestnetCard()
|
||||
WalletNotificationUM.DemoCard -> DemoCard()
|
||||
is WalletNotificationUM.MissingAddresses -> MissingAddresses()
|
||||
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
|
||||
} else {
|
||||
NoticeFinishActivation.ActivationState.NotStarted
|
||||
}
|
||||
val balanceState = when (notificationUM.type) {
|
||||
WalletNotificationType.Warning -> AnalyticsParam.EmptyFull.Full
|
||||
else -> AnalyticsParam.EmptyFull.Empty
|
||||
}
|
||||
NoticeFinishActivation(
|
||||
activationState = activationState,
|
||||
balanceState = balanceState,
|
||||
)
|
||||
}
|
||||
is WalletNotificationUM.SeedPhraseNotification -> NoticeSeedPhraseSupport()
|
||||
is WalletNotificationUM.SeedPhraseSecondNotification -> NoticeSeedPhraseSupportSecond()
|
||||
is WalletNotificationUM.PushNotifications -> PushBanner()
|
||||
is WalletNotificationUM.UnlockWallets,
|
||||
is WalletNotificationUM.NoAccount,
|
||||
is WalletNotificationUM.LowSignatures,
|
||||
WalletNotificationUM.SomeNetworksUnreachable,
|
||||
is WalletNotificationUM.UsedOutdatedData,
|
||||
is WalletNotificationUM.CloreMigration,
|
||||
-> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,12 +4,8 @@ import com.tangem.common.routing.AppRoute.WalletBackup
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUMV2
|
||||
import com.tangem.core.ui.components.bottomsheets.message.icon
|
||||
import com.tangem.core.ui.components.bottomsheets.message.infoBlock
|
||||
import com.tangem.core.ui.components.bottomsheets.message.onClick
|
||||
import com.tangem.core.ui.components.bottomsheets.message.primaryButton
|
||||
import com.tangem.core.ui.components.bottomsheets.message.secondaryButton
|
||||
import com.tangem.core.ui.components.bottomsheets.message.*
|
||||
import com.tangem.core.ui.ds.message.TangemMessageEffect
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.bottomSheetMessage
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
|
|
@ -19,7 +15,9 @@ import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase
|
|||
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotificationUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.inject.Inject
|
||||
|
|
@ -71,6 +69,44 @@ internal class WalletWarningsSingleEventSender @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun send(
|
||||
userWalletId: UserWalletId,
|
||||
displayedWalletUM: WalletUM?,
|
||||
newNotifications: List<WalletNotificationUM>,
|
||||
) {
|
||||
if (screenLifecycleProvider.isBackgroundState.value) return
|
||||
if (newNotifications.isEmpty()) return
|
||||
if (displayedWalletUM == null || displayedWalletUM.pullToRefreshConfig.isRefreshing) return
|
||||
|
||||
val totalNotifications = displayedWalletUM.notifications + displayedWalletUM.notificationsCarousel
|
||||
val events = newNotifications.filter { it !in totalNotifications }
|
||||
|
||||
// We must show activation bs only for the first seen wallet when open the app (if need, see conditions below),
|
||||
// so we keep this wallet id and use for future checks, ignore other wallets during the app session.
|
||||
if (isActivationBottomSheetShown.isEmpty()) {
|
||||
isActivationBottomSheetShown[userWalletId] = false
|
||||
}
|
||||
|
||||
events.forEach { event ->
|
||||
when (event) {
|
||||
is WalletNotificationUM.SeedPhraseNotification -> {
|
||||
seedPhraseNotificationUseCase.notified(userWalletId = userWalletId)
|
||||
}
|
||||
is WalletNotificationUM.FinishWalletActivation -> {
|
||||
// We check that map contains the first seen wallet (will return null instead false/true otherwise)
|
||||
// and for this wallet we haven't shown the activation bs yet (check that returns false, not true)
|
||||
if (isActivationBottomSheetShown[userWalletId] == false) {
|
||||
if (event.messageEffect == TangemMessageEffect.Warning && event.isBackupExists.not()) {
|
||||
showFinishActivationBottomSheet(userWalletId)
|
||||
}
|
||||
isActivationBottomSheetShown[userWalletId] = true
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showFinishActivationBottomSheet(userWalletId: UserWalletId) {
|
||||
val userWallet = getUserWalletUseCase(userWalletId).getOrNull() ?: return
|
||||
if (userWallet !is UserWallet.Hot) return
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.domain
|
||||
|
||||
import com.tangem.common.TangemSiteUrlBuilder
|
||||
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.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
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotificationUM
|
||||
import com.tangem.utils.extensions.addIf
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Factory for creating a list of notifications that can be shown on the wallet screen.
|
||||
* These notifications are not critical and can be stacked with each other.
|
||||
*/
|
||||
@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(
|
||||
NotificationId.EnablePushesReminderNotification.key,
|
||||
).distinctUntilChanged(),
|
||||
flow3 = shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.OnePlusOne)
|
||||
.distinctUntilChanged(),
|
||||
flow4 = isReadyToShowRateAppUseCase().distinctUntilChanged(),
|
||||
flow5 = getWalletsUseCase().conflate(),
|
||||
) { showYieldPromo, showPushesNotification, showOnePlusOnePromo, showRateAppPromo, wallets ->
|
||||
|
||||
buildList {
|
||||
addNoteMigrationNotification(userWallet, wallets, clickIntents)
|
||||
addRateAppNotification(showRateAppPromo, clickIntents)
|
||||
addOnePlusOnePromoNotification(clickIntents, showOnePlusOnePromo)
|
||||
addYieldPromoNotification(clickIntents, showYieldPromo)
|
||||
|
||||
addPushNotification(
|
||||
shouldShow = showPushesNotification,
|
||||
isPushesAllowed = notificationsRepository.isUserAllowToSubscribeOnPushNotifications(),
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
}.sortedBy { it.type.ordinal }.toImmutableList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotificationUM>.addRateAppNotification(
|
||||
isReadyToShowRating: Boolean,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
addIf(isReadyToShowRating) {
|
||||
WalletNotificationUM.RateApp(
|
||||
onLikeClick = clickIntents::onLikeAppClick,
|
||||
onDislikeClick = clickIntents::onDislikeAppClick,
|
||||
onCloseClick = clickIntents::onCloseRateAppWarningClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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>,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
val cardTypesResolver = (userWallet as? UserWallet.Cold)?.scanResponse?.cardTypesResolver
|
||||
|
||||
val isUserHasWalletOrWallet2 = userWallets.filterIsInstance<UserWallet.Cold>().any { wallet ->
|
||||
val typesResolver = wallet.scanResponse.cardTypesResolver
|
||||
typesResolver.isTangemWallet() || typesResolver.isWallet2()
|
||||
}
|
||||
|
||||
addIf(cardTypesResolver != null && cardTypesResolver.isTangemNote() && !isUserHasWalletOrWallet2) {
|
||||
WalletNotificationUM.NoteMigration(
|
||||
onClick = { clickIntents.onNoteMigrationButtonClick(TangemSiteUrlBuilder.NOTE_MIGRATION_URL) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotificationUM>.addPushNotification(
|
||||
shouldShow: Boolean,
|
||||
isPushesAllowed: Boolean,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
addIf(shouldShow && !isPushesAllowed) {
|
||||
WalletNotificationUM.PushNotifications(
|
||||
onCloseClick = clickIntents::onDenyPermissions,
|
||||
onEnabledClick = clickIntents::onAllowPermissions,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,326 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.domain
|
||||
|
||||
import com.tangem.common.ui.userwallet.ext.walletInterationIcon
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.ui.ds.message.TangemMessageEffect
|
||||
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
|
||||
import com.tangem.domain.card.CardTypesResolver
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.hotwallet.GetAccessCodeSkippedUseCase
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.TotalFiatBalance
|
||||
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.wallets.models.SeedPhraseNotificationsStatus
|
||||
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
|
||||
import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotificationUM
|
||||
import com.tangem.hot.sdk.model.HotWalletId
|
||||
import com.tangem.lib.crypto.BlockchainUtils
|
||||
import com.tangem.utils.extensions.addIf
|
||||
import com.tangem.utils.extensions.isPositive
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Factory for creating a list of notifications that can be shown on the wallet screen.
|
||||
* These notifications are critical and should be shown separately from each other.
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
@ModelScoped
|
||||
internal class GetWalletWarningsFactory @Inject constructor(
|
||||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
|
||||
private val backupValidator: BackupValidator,
|
||||
private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase,
|
||||
private val accountDependencies: AccountDependencies,
|
||||
private val getAccessCodeSkippedUseCase: GetAccessCodeSkippedUseCase,
|
||||
private val hasSingleWalletSignedHashesUseCase: HasSingleWalletSignedHashesUseCase,
|
||||
) {
|
||||
fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): Flow<ImmutableList<WalletNotificationUM>> {
|
||||
val cardTypesResolver = (userWallet as? UserWallet.Cold)?.scanResponse?.cardTypesResolver
|
||||
|
||||
val params = SingleAccountStatusListProducer.Params(userWallet.walletId)
|
||||
val accountStatusListFlow = accountDependencies.singleAccountStatusListSupplier(params)
|
||||
|
||||
return combine(
|
||||
flow = accountStatusListFlow,
|
||||
flow2 = isNeedToBackupUseCase(userWallet.walletId).distinctUntilChanged(),
|
||||
flow3 = seedPhraseNotificationUseCase(userWalletId = userWallet.walletId).distinctUntilChanged(),
|
||||
flow4 = getAccessCodeSkippedUseCase(userWallet.walletId).distinctUntilChanged(),
|
||||
) { accountList, isNeedToBackup, seedPhraseIssueStatus, shouldAccessCodeSkipped ->
|
||||
val totalFiatBalance = accountList.totalFiatBalance
|
||||
val flattenCurrencies = accountList.flattenCurrencies()
|
||||
|
||||
buildList {
|
||||
addUsedOutdatedDataNotification(totalFiatBalance)
|
||||
|
||||
addCriticalNotifications(userWallet, seedPhraseIssueStatus, clickIntents)
|
||||
|
||||
addFinishWalletActivationNotification(
|
||||
userWallet = userWallet,
|
||||
totalFiatBalance = totalFiatBalance,
|
||||
clickIntents = clickIntents,
|
||||
shouldAccessCodeSkipped = shouldAccessCodeSkipped,
|
||||
)
|
||||
|
||||
addInformationalNotifications(
|
||||
userWallet = userWallet,
|
||||
cardTypesResolver = cardTypesResolver,
|
||||
flattenCurrencies = flattenCurrencies,
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
|
||||
addWarningNotifications(
|
||||
userWallet = userWallet,
|
||||
cardTypesResolver = cardTypesResolver,
|
||||
flattenCurrencies = flattenCurrencies,
|
||||
isNeedToBackup = isNeedToBackup,
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
}.sortedBy { it.type.ordinal }.toImmutableList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotificationUM>.addUsedOutdatedDataNotification(totalFiatBalance: TotalFiatBalance) {
|
||||
addIf(
|
||||
element = WalletNotificationUM.UsedOutdatedData,
|
||||
condition = (totalFiatBalance as? TotalFiatBalance.Loaded)?.source == StatusSource.ONLY_CACHE,
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotificationUM>.addCriticalNotifications(
|
||||
userWallet: UserWallet,
|
||||
seedPhraseIssueStatus: SeedPhraseNotificationsStatus,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
if (userWallet !is UserWallet.Cold) {
|
||||
return
|
||||
}
|
||||
|
||||
addSeedNotificationIfNeeded(userWallet, seedPhraseIssueStatus, clickIntents)
|
||||
|
||||
val cardTypesResolver = userWallet.scanResponse.cardTypesResolver
|
||||
addIf(
|
||||
element = WalletNotificationUM.BackupError { clickIntents.onSupportClick() },
|
||||
condition = !backupValidator.isValidBackupStatus(userWallet.scanResponse.card) || userWallet.hasBackupError,
|
||||
)
|
||||
|
||||
addIf(
|
||||
element = WalletNotificationUM.DevCard,
|
||||
condition = !cardTypesResolver.isReleaseFirmwareType(),
|
||||
)
|
||||
|
||||
addIf(
|
||||
element = WalletNotificationUM.FailedCardValidation,
|
||||
condition = cardTypesResolver.isReleaseFirmwareType() && cardTypesResolver.isAttestationFailed(),
|
||||
)
|
||||
|
||||
cardTypesResolver.getRemainingSignatures()?.let { remainingSignatures ->
|
||||
addIf(
|
||||
element = WalletNotificationUM.LowSignatures(count = remainingSignatures),
|
||||
condition = remainingSignatures <= MAX_REMAINING_SIGNATURES_COUNT,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotificationUM>.addInformationalNotifications(
|
||||
userWallet: UserWallet,
|
||||
cardTypesResolver: CardTypesResolver?,
|
||||
flattenCurrencies: List<CryptoCurrencyStatus>,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
addIf(
|
||||
element = WalletNotificationUM.DemoCard,
|
||||
condition = cardTypesResolver != null && isDemoCardUseCase(cardId = cardTypesResolver.getCardId()),
|
||||
)
|
||||
|
||||
addMissingAddressesNotification(userWallet, flattenCurrencies, clickIntents)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotificationUM>.addMissingAddressesNotification(
|
||||
userWallet: UserWallet,
|
||||
flattenCurrencies: List<CryptoCurrencyStatus>,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
val currencies = flattenCurrencies.getMissingAddressCurrencies().ifEmpty { return }
|
||||
|
||||
addIf(
|
||||
element = WalletNotificationUM.MissingAddresses(
|
||||
tangemIcon = walletInterationIcon(userWallet),
|
||||
missingAddressesCount = currencies.count(),
|
||||
onGenerateClick = {
|
||||
clickIntents.onGenerateMissedAddressesClick(missedAddressCurrencies = currencies)
|
||||
},
|
||||
),
|
||||
condition = currencies.isNotEmpty(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<CryptoCurrencyStatus>.getMissingAddressCurrencies(): List<CryptoCurrency> {
|
||||
return this
|
||||
.filter { it.value is CryptoCurrencyStatus.MissedDerivation }
|
||||
.map(CryptoCurrencyStatus::currency)
|
||||
}
|
||||
|
||||
private suspend fun MutableList<WalletNotificationUM>.addWarningNotifications(
|
||||
userWallet: UserWallet,
|
||||
cardTypesResolver: CardTypesResolver?,
|
||||
flattenCurrencies: List<CryptoCurrencyStatus>,
|
||||
isNeedToBackup: Boolean,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
addIf(
|
||||
element = WalletNotificationUM.MissingBackup(
|
||||
onClick = clickIntents::onAddBackupCardClick,
|
||||
),
|
||||
condition = isNeedToBackup,
|
||||
)
|
||||
|
||||
addIf(
|
||||
element = WalletNotificationUM.TestnetCard,
|
||||
condition = cardTypesResolver?.isTestCard() == true,
|
||||
)
|
||||
|
||||
addIf(
|
||||
element = WalletNotificationUM.SomeNetworksUnreachable,
|
||||
condition = flattenCurrencies.hasUnreachableNetworks(),
|
||||
)
|
||||
|
||||
addCloreMigrationNotification(flattenCurrencies, clickIntents)
|
||||
|
||||
addNoAccountWarning(cryptoCurrencyStatus = flattenCurrencies.firstOrNull())
|
||||
|
||||
addIf(
|
||||
element = WalletNotificationUM.NumberOfSignedHashesIncorrect(
|
||||
onCloseClick = clickIntents::onCloseAlreadySignedHashesWarningClick,
|
||||
),
|
||||
condition = hasSignedHashes(userWallet, flattenCurrencies.firstOrNull()),
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotificationUM>.addNoAccountWarning(cryptoCurrencyStatus: CryptoCurrencyStatus?) {
|
||||
val noAccountStatus = cryptoCurrencyStatus?.value as? CryptoCurrencyStatus.NoAccount
|
||||
if (noAccountStatus != null) {
|
||||
add(
|
||||
element = WalletNotificationUM.NoAccount(
|
||||
network = cryptoCurrencyStatus.currency.name,
|
||||
amount = noAccountStatus.amountToCreateAccount.toString(),
|
||||
symbol = cryptoCurrencyStatus.currency.symbol,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotificationUM>.addCloreMigrationNotification(
|
||||
flattenCurrencies: List<CryptoCurrencyStatus>,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
val cloreCurrency = flattenCurrencies.findCloreCurrency() ?: return
|
||||
|
||||
add(
|
||||
WalletNotificationUM.CloreMigration(
|
||||
onStartMigrationClick = { clickIntents.onCloreMigrationClick(cloreCurrency) },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<CryptoCurrencyStatus>.findCloreCurrency(): CryptoCurrencyStatus? {
|
||||
return find { currencyStatus ->
|
||||
BlockchainUtils.isClore(currencyStatus.currency.network.rawId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<CryptoCurrencyStatus>.hasUnreachableNetworks(): Boolean {
|
||||
return any { it.value is CryptoCurrencyStatus.Unreachable }
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotificationUM>.addFinishWalletActivationNotification(
|
||||
userWallet: UserWallet,
|
||||
totalFiatBalance: TotalFiatBalance,
|
||||
clickIntents: WalletClickIntents,
|
||||
shouldAccessCodeSkipped: Boolean,
|
||||
) {
|
||||
if (userWallet !is UserWallet.Hot) return
|
||||
|
||||
val isBackupExists = userWallet.backedUp
|
||||
val isAccessCodeRequired = userWallet.hotWalletId.authType == HotWalletId.AuthType.NoPassword &&
|
||||
!shouldAccessCodeSkipped
|
||||
val shouldShowFinishActivation = !isBackupExists || isAccessCodeRequired
|
||||
|
||||
val messageEffect = when (totalFiatBalance) {
|
||||
TotalFiatBalance.Failed,
|
||||
TotalFiatBalance.Loading,
|
||||
-> TangemMessageEffect.None
|
||||
is TotalFiatBalance.Loaded -> if (totalFiatBalance.amount.orZero().isPositive()) {
|
||||
TangemMessageEffect.Warning
|
||||
} else {
|
||||
TangemMessageEffect.None
|
||||
}
|
||||
}
|
||||
|
||||
addIf(
|
||||
element = WalletNotificationUM.FinishWalletActivation(
|
||||
messageEffect = messageEffect,
|
||||
onClick = { clickIntents.onFinishWalletActivationClick(isBackupExists) },
|
||||
isBackupExists = isBackupExists,
|
||||
),
|
||||
condition = shouldShowFinishActivation,
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotificationUM>.addSeedNotificationIfNeeded(
|
||||
userWallet: UserWallet.Cold,
|
||||
seedPhraseIssueStatus: SeedPhraseNotificationsStatus,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
val isNotificationAvailable = with(userWallet) {
|
||||
val isDemo = isDemoCardUseCase(cardId = userWallet.cardId)
|
||||
val isWalletWithSeedPhrase = scanResponse.cardTypesResolver.isWallet2() && userWallet.isImported
|
||||
|
||||
!isDemo && isWalletWithSeedPhrase
|
||||
}
|
||||
|
||||
when (seedPhraseIssueStatus) {
|
||||
SeedPhraseNotificationsStatus.SHOW_FIRST -> addIf(
|
||||
element = WalletNotificationUM.SeedPhraseNotification(
|
||||
onDeclineClick = clickIntents::onSeedPhraseNotificationDecline,
|
||||
onConfirmClick = clickIntents::onSeedPhraseNotificationConfirm,
|
||||
),
|
||||
condition = isNotificationAvailable,
|
||||
)
|
||||
SeedPhraseNotificationsStatus.SHOW_SECOND -> addIf(
|
||||
element = WalletNotificationUM.SeedPhraseSecondNotification(
|
||||
onDeclineClick = clickIntents::onSeedPhraseSecondNotificationReject,
|
||||
onConfirmClick = clickIntents::onSeedPhraseSecondNotificationAccept,
|
||||
),
|
||||
condition = isNotificationAvailable,
|
||||
)
|
||||
SeedPhraseNotificationsStatus.NOT_NEEDED -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun hasSignedHashes(
|
||||
selectedWallet: UserWallet,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus?,
|
||||
): Boolean {
|
||||
if (selectedWallet !is UserWallet.Cold || !selectedWallet.isMultiCurrency) return false
|
||||
val network = cryptoCurrencyStatus?.currency?.network ?: return false
|
||||
|
||||
return hasSingleWalletSignedHashesUseCase(userWallet = selectedWallet, network = network)
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.firstOrNull() == true
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val MAX_REMAINING_SIGNATURES_COUNT = 10
|
||||
}
|
||||
}
|
||||
|
|
@ -20,7 +20,6 @@ import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenList
|
|||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
|
||||
import com.tangem.features.tangempay.TangemPayFeatureToggles
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Deprecated("Use MultiWalletContentLoaderV2 instead")
|
||||
|
|
@ -44,7 +43,6 @@ internal class MultiWalletContentLoader(
|
|||
private val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
|
||||
private val stakingAvailabilityListUseCase: StakingAvailabilityListUseCase,
|
||||
private val yieldSupplyGetShouldShowMainPromoUseCase: YieldSupplyGetShouldShowMainPromoUseCase,
|
||||
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
|
||||
private val tangemPayMainSubscriberFactory: TangemPayMainSubscriber.Factory,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
||||
|
|
@ -88,9 +86,7 @@ internal class MultiWalletContentLoader(
|
|||
getStoryContentUseCase = getStoryContentUseCase,
|
||||
).let(::add)
|
||||
|
||||
if (tangemPayFeatureToggles.isTangemPayEnabled) {
|
||||
add(tangemPayMainSubscriberFactory.create(userWallet))
|
||||
}
|
||||
add(tangemPayMainSubscriberFactory.create(userWallet))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -20,7 +20,6 @@ import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenList
|
|||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.TangemPayMainSubscriber
|
||||
import com.tangem.features.tangempay.TangemPayFeatureToggles
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -43,7 +42,6 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
|
|||
private val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
|
||||
private val stakingAvailabilityListUseCase: StakingAvailabilityListUseCase,
|
||||
private val yieldSupplyGetShouldShowMainPromoUseCase: YieldSupplyGetShouldShowMainPromoUseCase,
|
||||
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
|
||||
private val tangemPayMainSubscriberFactory: TangemPayMainSubscriber.Factory,
|
||||
) {
|
||||
|
||||
|
|
@ -66,7 +64,6 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
|
|||
currenciesRepository = currenciesRepository,
|
||||
yieldSupplyApyFlowUseCase = yieldSupplyApyFlowUseCase,
|
||||
stakingAvailabilityListUseCase = stakingAvailabilityListUseCase,
|
||||
tangemPayFeatureToggles = tangemPayFeatureToggles,
|
||||
tangemPayMainSubscriberFactory = tangemPayMainSubscriberFactory,
|
||||
yieldSupplyGetShouldShowMainPromoUseCase = yieldSupplyGetShouldShowMainPromoUseCase,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarni
|
|||
import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
|
||||
import com.tangem.features.tangempay.TangemPayFeatureToggles
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
|
@ -25,11 +24,10 @@ internal class MultiWalletContentLoaderV2 @AssistedInject constructor(
|
|||
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
|
||||
private val getStoryContentUseCase: GetStoryContentUseCase,
|
||||
private val checkWalletWithFundsSubscriberFactory: CheckWalletWithFundsSubscriber.Factory,
|
||||
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
|
||||
private val tangemPayMainSubscriberFactory: TangemPayMainSubscriber.Factory,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
||||
override fun create(): List<WalletSubscriber> = listOfNotNull(
|
||||
override fun create(): List<WalletSubscriber> = listOf(
|
||||
accountListSubscriberFactory.create(userWallet = userWallet),
|
||||
walletNFTListSubscriberV2Factory.create(userWallet = userWallet),
|
||||
checkWalletWithFundsSubscriberFactory.create(userWallet = userWallet),
|
||||
|
|
@ -46,12 +44,7 @@ internal class MultiWalletContentLoaderV2 @AssistedInject constructor(
|
|||
stateHolder = stateController,
|
||||
getStoryContentUseCase = getStoryContentUseCase,
|
||||
),
|
||||
|
||||
if (tangemPayFeatureToggles.isTangemPayEnabled) {
|
||||
tangemPayMainSubscriberFactory.create(userWallet)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
tangemPayMainSubscriberFactory.create(userWallet),
|
||||
)
|
||||
|
||||
@AssistedFactory
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state
|
||||
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.NOT_INITIALIZED_WALLET_INDEX
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTopBarConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.OpenBottomSheetTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.WalletScreenStateTransformer
|
||||
|
|
@ -25,7 +23,9 @@ import javax.inject.Singleton
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Singleton
|
||||
internal class WalletStateController @Inject constructor() {
|
||||
internal class WalletStateController @Inject constructor(
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
) {
|
||||
|
||||
val uiState: StateFlow<WalletScreenState> get() = mutableUiState
|
||||
|
||||
|
|
@ -53,6 +53,10 @@ internal class WalletStateController @Inject constructor() {
|
|||
return value.wallets.firstOrNull { it.walletCardState.id == userWalletId }
|
||||
}
|
||||
|
||||
fun getWalletUM(userWalletId: UserWalletId): WalletUM? {
|
||||
return value.wallets2.firstOrNull { it.walletsBalanceUM.id == userWalletId }
|
||||
}
|
||||
|
||||
fun getWalletStateIfSelected(walletId: UserWalletId): WalletState? {
|
||||
val selectedWalletId = getSelectedWalletId()
|
||||
|
||||
|
|
@ -61,16 +65,40 @@ internal class WalletStateController @Inject constructor() {
|
|||
}
|
||||
}
|
||||
|
||||
fun getWalletUMIfSelected(walletId: UserWalletId): WalletUM? {
|
||||
val selectedWalletId = getSelectedWalletId()
|
||||
|
||||
return value.wallets2.firstOrNull {
|
||||
it.walletsBalanceUM.id == walletId && it.walletsBalanceUM.id == selectedWalletId
|
||||
}
|
||||
}
|
||||
|
||||
fun getSelectedWallet(): WalletState {
|
||||
return with(value) { wallets[selectedWalletIndex] }
|
||||
}
|
||||
|
||||
fun getSelectedWalletUM(): WalletUM {
|
||||
return with(value) { wallets2[selectedWalletIndex] }
|
||||
}
|
||||
|
||||
fun getSelectedWalletId(): UserWalletId {
|
||||
return with(value) { wallets[selectedWalletIndex].walletCardState.id }
|
||||
return with(value) {
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
wallets2[selectedWalletIndex].walletsBalanceUM.id
|
||||
} else {
|
||||
wallets[selectedWalletIndex].walletCardState.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getWalletIndexByWalletId(userWalletId: UserWalletId): Int? {
|
||||
return with(value) { wallets.indexOfFirstOrNull { it.walletCardState.id == userWalletId } }
|
||||
return with(value) {
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
wallets2.indexOfFirstOrNull { it.walletsBalanceUM.id == userWalletId }
|
||||
} else {
|
||||
wallets.indexOfFirstOrNull { it.walletCardState.id == userWalletId }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun showBottomSheet(
|
||||
|
|
@ -105,6 +133,7 @@ internal class WalletStateController @Inject constructor() {
|
|||
topBarConfig = WalletTopBarConfig(onDetailsClick = {}),
|
||||
selectedWalletIndex = NOT_INITIALIZED_WALLET_INDEX,
|
||||
wallets = persistentListOf(),
|
||||
wallets2 = persistentListOf(),
|
||||
onWalletChange = { _, _ -> },
|
||||
event = consumedEvent(),
|
||||
isHidingMode = false,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
/**
|
||||
* Represents the state of the wallet balance in the UI.
|
||||
*
|
||||
* The sealed interface has three implementations:
|
||||
* - [Content]: Represents the state when the wallet balance is successfully loaded.
|
||||
* - [Error]: Represents the state when there was an error loading the wallet balance.
|
||||
* - [Loading]: Represents the state when the wallet balance is currently being loaded.
|
||||
*
|
||||
* @property id The unique identifier of the wallet.
|
||||
* @property name The name of the wallet.
|
||||
*/
|
||||
@Immutable
|
||||
internal sealed interface WalletBalanceUM {
|
||||
|
||||
/** Wallet Id */
|
||||
val id: UserWalletId
|
||||
|
||||
/** Wallet Name */
|
||||
val name: String
|
||||
|
||||
/**
|
||||
* Wallet card content state
|
||||
*
|
||||
* @property id wallet id
|
||||
* @property name wallet name
|
||||
* @property balance wallet balance
|
||||
*/
|
||||
data class Content(
|
||||
override val id: UserWalletId,
|
||||
override val name: String,
|
||||
val balance: TextReference,
|
||||
val isBalanceFlickering: Boolean,
|
||||
val isZeroBalance: Boolean?,
|
||||
) : WalletBalanceUM
|
||||
|
||||
/**
|
||||
* Wallet card error state
|
||||
*
|
||||
* @property id wallet id
|
||||
* @property name wallet name
|
||||
*/
|
||||
data class Error(
|
||||
override val id: UserWalletId,
|
||||
override val name: String,
|
||||
) : WalletBalanceUM
|
||||
|
||||
/**
|
||||
* Wallet card loading state
|
||||
*
|
||||
* @property id wallet id
|
||||
* @property name wallet name
|
||||
*/
|
||||
data class Loading(
|
||||
override val id: UserWalletId,
|
||||
override val name: String,
|
||||
) : WalletBalanceUM
|
||||
|
||||
fun copySealed(name: String): WalletBalanceUM {
|
||||
return when (this) {
|
||||
is Content -> copy(name = name)
|
||||
is Error -> copy(name = name)
|
||||
is Loading -> copy(name = name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.model
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
|
||||
/**
|
||||
* Wallet bottom sheet config
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
sealed class WalletBottomSheetConfig(
|
||||
open val title: TextReference,
|
||||
open val subtitle: TextReference,
|
||||
@DrawableRes open val iconResId: Int,
|
||||
val primaryButtonConfig: ButtonConfig,
|
||||
val secondaryButtonConfig: ButtonConfig,
|
||||
) : TangemBottomSheetConfigContent {
|
||||
|
||||
data class ButtonConfig(
|
||||
val text: TextReference,
|
||||
val onClick: () -> Unit,
|
||||
@DrawableRes val iconResId: Int? = null,
|
||||
)
|
||||
|
||||
data class UnlockWallets(val onUnlockClick: () -> Unit, val onScanClick: () -> Unit) : WalletBottomSheetConfig(
|
||||
title = resourceReference(id = R.string.common_access_denied),
|
||||
subtitle = resourceReference(
|
||||
id = R.string.unlock_wallet_description_full,
|
||||
formatArgs = wrappedList(
|
||||
resourceReference(R.string.common_biometrics),
|
||||
),
|
||||
),
|
||||
iconResId = R.drawable.ic_locked_24,
|
||||
primaryButtonConfig = ButtonConfig(
|
||||
text = resourceReference(
|
||||
id = R.string.user_wallet_list_unlock_all_with,
|
||||
formatArgs = wrappedList(resourceReference(R.string.common_biometrics)),
|
||||
),
|
||||
onClick = onUnlockClick,
|
||||
),
|
||||
secondaryButtonConfig = ButtonConfig(
|
||||
text = resourceReference(id = R.string.welcome_unlock_card),
|
||||
onClick = onScanClick,
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,496 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.model
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.tangem.core.ui.ds.button.TangemButtonType
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.ds.message.TangemMessageButtonUM
|
||||
import com.tangem.core.ui.ds.message.TangemMessageEffect
|
||||
import com.tangem.core.ui.ds.message.TangemMessageUM
|
||||
import com.tangem.core.ui.extensions.pluralReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
/**
|
||||
* Wallet notification types
|
||||
*/
|
||||
internal enum class WalletNotificationType {
|
||||
Status,
|
||||
Critical,
|
||||
Warning,
|
||||
Promo,
|
||||
Survey,
|
||||
Informational,
|
||||
}
|
||||
|
||||
/**
|
||||
* Wallet notification UI model
|
||||
*
|
||||
* @property messageUM - message to show in notification
|
||||
* @property type - type of notification, affects design and priority
|
||||
*/
|
||||
internal sealed class WalletNotificationUM(val messageUM: TangemMessageUM, val type: WalletNotificationType) {
|
||||
|
||||
// region Status
|
||||
data object SomeNetworksUnreachable : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "SomeNetworksUnreachableNotification",
|
||||
title = resourceReference(id = R.string.warning_some_networks_unreachable_title),
|
||||
subtitle = resourceReference(id = R.string.warning_some_networks_unreachable_message),
|
||||
iconUM = TangemIconUM.Icon(
|
||||
iconRes = R.drawable.ic_attention_default_24,
|
||||
tintReference = { TangemTheme.colors2.graphic.status.attention },
|
||||
),
|
||||
messageEffect = TangemMessageEffect.None,
|
||||
),
|
||||
type = WalletNotificationType.Status,
|
||||
)
|
||||
|
||||
data object UsedOutdatedData : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "UsedOutdatedDataNotification",
|
||||
title = stringReference("Missing some token balances"), // todo redesign main lokalise
|
||||
subtitle = stringReference("Will be updated as soon as possible"), // todo redesign main lokalise
|
||||
iconUM = TangemIconUM.Icon(
|
||||
iconRes = R.drawable.ic_error_sync_default_24,
|
||||
tintReference = { TangemTheme.colors2.graphic.status.attention },
|
||||
),
|
||||
messageEffect = TangemMessageEffect.None,
|
||||
),
|
||||
type = WalletNotificationType.Status,
|
||||
)
|
||||
|
||||
data object FailedCardValidation : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "FailedCardValidationNotification",
|
||||
title = resourceReference(id = R.string.warning_failed_to_verify_card_title),
|
||||
subtitle = resourceReference(id = R.string.warning_failed_to_verify_card_message),
|
||||
iconUM = TangemIconUM.Icon(
|
||||
iconRes = R.drawable.ic_attention_default_24,
|
||||
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
|
||||
),
|
||||
messageEffect = TangemMessageEffect.Warning,
|
||||
),
|
||||
type = WalletNotificationType.Status,
|
||||
)
|
||||
|
||||
data object DevCard : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "DevCardNotification",
|
||||
title = resourceReference(id = R.string.warning_developer_card_title),
|
||||
subtitle = resourceReference(id = R.string.warning_developer_card_message),
|
||||
messageEffect = TangemMessageEffect.None,
|
||||
iconUM = TangemIconUM.Icon(
|
||||
iconRes = R.drawable.ic_attention_default_24,
|
||||
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
|
||||
),
|
||||
),
|
||||
type = WalletNotificationType.Status,
|
||||
)
|
||||
|
||||
data object TestnetCard : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "TestnetCardNotification",
|
||||
title = resourceReference(id = R.string.warning_testnet_card_title),
|
||||
subtitle = resourceReference(id = R.string.warning_testnet_card_message),
|
||||
messageEffect = TangemMessageEffect.None,
|
||||
iconUM = TangemIconUM.Icon(
|
||||
iconRes = R.drawable.ic_attention_default_24,
|
||||
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
|
||||
),
|
||||
),
|
||||
type = WalletNotificationType.Status,
|
||||
)
|
||||
|
||||
data object DemoCard : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "DemoCardNotification",
|
||||
title = resourceReference(id = R.string.warning_demo_mode_title),
|
||||
subtitle = resourceReference(id = R.string.warning_demo_mode_message),
|
||||
messageEffect = TangemMessageEffect.None,
|
||||
iconUM = TangemIconUM.Icon(
|
||||
iconRes = R.drawable.ic_attention_default_24,
|
||||
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
|
||||
),
|
||||
),
|
||||
type = WalletNotificationType.Status,
|
||||
)
|
||||
// endregion
|
||||
|
||||
// region Critical
|
||||
data class BackupError(val onClick: () -> Unit) : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "BackupErrorNotification",
|
||||
title = resourceReference(id = R.string.warning_backup_errors_title),
|
||||
subtitle = resourceReference(id = R.string.warning_backup_errors_message),
|
||||
messageEffect = TangemMessageEffect.Warning,
|
||||
iconUM = TangemIconUM.Icon(
|
||||
iconRes = R.drawable.ic_attention_default_24,
|
||||
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
|
||||
),
|
||||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(id = R.string.common_contact_support),
|
||||
type = TangemButtonType.PrimaryInverse,
|
||||
onClick = onClick,
|
||||
),
|
||||
),
|
||||
),
|
||||
type = WalletNotificationType.Critical,
|
||||
)
|
||||
|
||||
data class SeedPhraseNotification(
|
||||
val onDeclineClick: () -> Unit,
|
||||
val onConfirmClick: () -> Unit,
|
||||
) : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "SeedPhraseIssueNotification",
|
||||
title = resourceReference(id = R.string.warning_seedphrase_issue_title),
|
||||
subtitle = resourceReference(id = R.string.warning_seedphrase_issue_message),
|
||||
messageEffect = TangemMessageEffect.Warning,
|
||||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(id = R.string.common_no),
|
||||
type = TangemButtonType.PrimaryInverse,
|
||||
onClick = onDeclineClick,
|
||||
),
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(id = R.string.common_yes),
|
||||
type = TangemButtonType.PrimaryInverse,
|
||||
onClick = onConfirmClick,
|
||||
),
|
||||
),
|
||||
iconUM = TangemIconUM.Icon(
|
||||
iconRes = R.drawable.ic_attention_default_24,
|
||||
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
|
||||
),
|
||||
),
|
||||
type = WalletNotificationType.Critical,
|
||||
)
|
||||
|
||||
data class SeedPhraseSecondNotification(
|
||||
val onDeclineClick: () -> Unit,
|
||||
val onConfirmClick: () -> Unit,
|
||||
) : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "SeedPhraseSecondIssueNotification",
|
||||
title = resourceReference(id = R.string.warning_seedphrase_action_required_title),
|
||||
subtitle = resourceReference(id = R.string.warning_seedphrase_contacted_support),
|
||||
messageEffect = TangemMessageEffect.Warning,
|
||||
iconUM = TangemIconUM.Icon(
|
||||
iconRes = R.drawable.ic_attention_default_24,
|
||||
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
|
||||
),
|
||||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(id = R.string.seed_warning_no),
|
||||
type = TangemButtonType.PrimaryInverse,
|
||||
onClick = onDeclineClick,
|
||||
),
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(id = R.string.seed_warning_yes),
|
||||
type = TangemButtonType.PrimaryInverse,
|
||||
onClick = onConfirmClick,
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
type = WalletNotificationType.Critical,
|
||||
)
|
||||
|
||||
data class MissingBackup(val onClick: () -> Unit) : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "MissingBackupNotification",
|
||||
title = resourceReference(id = R.string.warning_no_backup_title),
|
||||
subtitle = resourceReference(id = R.string.warning_no_backup_message),
|
||||
messageEffect = TangemMessageEffect.Warning,
|
||||
iconUM = TangemIconUM.Icon(
|
||||
iconRes = R.drawable.ic_attention_default_24,
|
||||
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
|
||||
),
|
||||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(id = R.string.button_start_backup_process),
|
||||
type = TangemButtonType.PrimaryInverse,
|
||||
onClick = onClick,
|
||||
),
|
||||
),
|
||||
),
|
||||
type = WalletNotificationType.Critical,
|
||||
)
|
||||
|
||||
data class LowSignatures(val count: Int) : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "LowSignaturesNotification",
|
||||
title = resourceReference(id = R.string.warning_low_signatures_title),
|
||||
subtitle = resourceReference(
|
||||
id = R.string.warning_low_signatures_message,
|
||||
formatArgs = wrappedList(count.toString()),
|
||||
),
|
||||
iconUM = TangemIconUM.Icon(
|
||||
iconRes = R.drawable.ic_attention_default_24,
|
||||
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
|
||||
),
|
||||
messageEffect = TangemMessageEffect.None,
|
||||
),
|
||||
type = WalletNotificationType.Critical,
|
||||
)
|
||||
|
||||
data class FinishWalletActivation(
|
||||
val messageEffect: TangemMessageEffect,
|
||||
val isBackupExists: Boolean,
|
||||
val onClick: () -> Unit,
|
||||
) : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "FinishWalletActivationNotification",
|
||||
title = resourceReference(R.string.hw_activation_need_title),
|
||||
subtitle = if (isBackupExists) {
|
||||
resourceReference(R.string.hw_activation_need_warning_description)
|
||||
} else {
|
||||
resourceReference(R.string.hw_activation_need_description)
|
||||
},
|
||||
iconUM = TangemIconUM.Icon(
|
||||
iconRes = R.drawable.img_knight_shield_32,
|
||||
tintReference = {
|
||||
when (messageEffect) {
|
||||
TangemMessageEffect.Warning -> TangemTheme.colors2.graphic.neutral.primary
|
||||
else -> TangemTheme.colors2.graphic.status.attention
|
||||
}
|
||||
},
|
||||
),
|
||||
messageEffect = messageEffect,
|
||||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(R.string.hw_activation_need_finish),
|
||||
type = TangemButtonType.PrimaryInverse,
|
||||
onClick = onClick,
|
||||
),
|
||||
),
|
||||
),
|
||||
type = when (messageEffect) {
|
||||
TangemMessageEffect.Warning -> WalletNotificationType.Critical
|
||||
else -> WalletNotificationType.Warning
|
||||
},
|
||||
)
|
||||
|
||||
data class NumberOfSignedHashesIncorrect(val onCloseClick: () -> Unit) : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "NumberOfSignedHashesIncorrectNotification",
|
||||
title = resourceReference(id = R.string.warning_number_of_signed_hashes_incorrect_title),
|
||||
subtitle = resourceReference(id = R.string.warning_number_of_signed_hashes_incorrect_message),
|
||||
messageEffect = TangemMessageEffect.Warning,
|
||||
iconUM = TangemIconUM.Icon(
|
||||
iconRes = R.drawable.img_knight_shield_32,
|
||||
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
|
||||
),
|
||||
onCloseClick = onCloseClick,
|
||||
),
|
||||
type = WalletNotificationType.Critical,
|
||||
)
|
||||
// endregion
|
||||
|
||||
// region Warning
|
||||
data class MissingAddresses(
|
||||
@DrawableRes val tangemIcon: Int?,
|
||||
val missingAddressesCount: Int,
|
||||
val onGenerateClick: () -> Unit,
|
||||
) : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "MissingAddressesNotification",
|
||||
title = resourceReference(id = R.string.warning_missing_derivation_title),
|
||||
subtitle = pluralReference(
|
||||
id = R.plurals.warning_missing_derivation_message,
|
||||
count = missingAddressesCount,
|
||||
formatArgs = wrappedList(missingAddressesCount),
|
||||
),
|
||||
isCentered = true,
|
||||
messageEffect = TangemMessageEffect.Card,
|
||||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(id = R.string.common_generate_addresses),
|
||||
type = TangemButtonType.Primary,
|
||||
iconRes = tangemIcon,
|
||||
onClick = onGenerateClick,
|
||||
),
|
||||
),
|
||||
),
|
||||
type = WalletNotificationType.Warning,
|
||||
)
|
||||
|
||||
data class NoAccount(val network: String, val symbol: String, val amount: String) : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "NoAccountNotification",
|
||||
title = resourceReference(id = R.string.warning_no_account_title),
|
||||
subtitle = resourceReference(
|
||||
id = R.string.no_account_generic,
|
||||
wrappedList(network, amount, symbol),
|
||||
),
|
||||
messageEffect = TangemMessageEffect.None,
|
||||
),
|
||||
type = WalletNotificationType.Warning,
|
||||
)
|
||||
|
||||
data class UnlockWallets(val onClick: () -> Unit) : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "UnlockWalletsNotification",
|
||||
title = resourceReference(id = R.string.common_access_denied),
|
||||
subtitle = resourceReference(
|
||||
id = R.string.warning_access_denied_message,
|
||||
formatArgs = wrappedList(
|
||||
resourceReference(R.string.common_biometrics),
|
||||
),
|
||||
),
|
||||
onClick = onClick,
|
||||
messageEffect = TangemMessageEffect.Card,
|
||||
isCentered = true,
|
||||
),
|
||||
type = WalletNotificationType.Warning,
|
||||
)
|
||||
// endregion
|
||||
|
||||
// region Promo
|
||||
data class NoteMigration(val onClick: () -> Unit) : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "NoteMigrationNotification",
|
||||
title = resourceReference(R.string.wallet_promo_banner_title),
|
||||
subtitle = resourceReference(R.string.wallet_promo_banner_description),
|
||||
messageEffect = TangemMessageEffect.Magic,
|
||||
isCentered = true,
|
||||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(R.string.wallet_promo_banner_button_title),
|
||||
onClick = onClick,
|
||||
type = TangemButtonType.Primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
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,
|
||||
onCloseClick = onCloseClick,
|
||||
buttonsUM = persistentListOf(
|
||||
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
|
||||
data class RateApp(
|
||||
val onLikeClick: () -> Unit,
|
||||
val onDislikeClick: () -> Unit,
|
||||
val onCloseClick: () -> Unit,
|
||||
) : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "RateAppNotification",
|
||||
title = resourceReference(id = R.string.warning_rate_app_title),
|
||||
subtitle = resourceReference(id = R.string.warning_rate_app_message),
|
||||
isCentered = true,
|
||||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(id = R.string.warning_button_could_be_better),
|
||||
type = TangemButtonType.PrimaryInverse,
|
||||
onClick = onDislikeClick,
|
||||
),
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(id = R.string.warning_button_like_it),
|
||||
type = TangemButtonType.Primary,
|
||||
onClick = onLikeClick,
|
||||
),
|
||||
),
|
||||
messageEffect = TangemMessageEffect.None,
|
||||
onCloseClick = onCloseClick,
|
||||
),
|
||||
type = WalletNotificationType.Survey,
|
||||
)
|
||||
// endregion
|
||||
|
||||
// region Informational
|
||||
data class PushNotifications(
|
||||
val onCloseClick: () -> Unit,
|
||||
val onEnabledClick: () -> Unit,
|
||||
) : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "PushNotificationsNotification",
|
||||
title = resourceReference(R.string.user_push_notification_banner_title),
|
||||
subtitle = resourceReference(R.string.user_push_notification_banner_subtitle),
|
||||
onCloseClick = onCloseClick,
|
||||
messageEffect = TangemMessageEffect.Magic,
|
||||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(R.string.common_later),
|
||||
type = TangemButtonType.PrimaryInverse,
|
||||
onClick = onCloseClick,
|
||||
),
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(R.string.common_enable),
|
||||
type = TangemButtonType.Primary,
|
||||
onClick = onEnabledClick,
|
||||
),
|
||||
),
|
||||
),
|
||||
type = WalletNotificationType.Informational,
|
||||
)
|
||||
|
||||
data class CloreMigration(
|
||||
val onStartMigrationClick: () -> Unit,
|
||||
) : WalletNotificationUM(
|
||||
messageUM = TangemMessageUM(
|
||||
id = "CloreMigrationNotification",
|
||||
title = resourceReference(com.tangem.core.res.R.string.warning_clore_migration_title),
|
||||
subtitle = resourceReference(com.tangem.core.res.R.string.warning_clore_migration_description),
|
||||
iconUM = TangemIconUM.Icon(
|
||||
iconRes = R.drawable.ic_attention_default_24,
|
||||
tintReference = { TangemTheme.colors2.graphic.status.attention },
|
||||
),
|
||||
messageEffect = TangemMessageEffect.None,
|
||||
buttonsUM = persistentListOf(
|
||||
TangemMessageButtonUM(
|
||||
text = resourceReference(com.tangem.core.res.R.string.warning_clore_migration_button),
|
||||
onClick = onStartMigrationClick,
|
||||
type = TangemButtonType.PrimaryInverse,
|
||||
),
|
||||
),
|
||||
),
|
||||
type = WalletNotificationType.Informational,
|
||||
)
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ internal data class WalletScreenState(
|
|||
val topBarConfig: WalletTopBarConfig,
|
||||
val selectedWalletIndex: Int,
|
||||
val wallets: ImmutableList<WalletState>,
|
||||
val wallets2: ImmutableList<WalletUM>,
|
||||
val onWalletChange: (index: Int, onlyState: Boolean) -> Unit,
|
||||
val event: StateEvent<WalletEvent>,
|
||||
val isHidingMode: Boolean,
|
||||
|
|
|
|||
|
|
@ -55,11 +55,6 @@ internal sealed interface WalletState : WalletStateHolder {
|
|||
override val nftState: WalletNFTItemUM = WalletNFTItemUM.Hidden
|
||||
override val tangemPayState: TangemPayState = TangemPayState.Empty
|
||||
}
|
||||
|
||||
enum class WalletType {
|
||||
Hot,
|
||||
Cold,
|
||||
}
|
||||
}
|
||||
|
||||
sealed class SingleCurrency : WalletState, TxHistoryStateHolder {
|
||||
|
|
@ -96,4 +91,9 @@ internal sealed interface WalletState : WalletStateHolder {
|
|||
override val marketPriceBlockState: MarketPriceBlockState? = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class WalletType {
|
||||
Hot,
|
||||
Cold,
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.ds.button.TangemButtonUM
|
||||
import com.tangem.core.ui.ds.row.TangemRowUM
|
||||
import com.tangem.core.ui.ds.row.header.TangemHeaderRowUM
|
||||
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
/**
|
||||
* State of the tokens list in the wallet screen
|
||||
*
|
||||
* @property tokenList list of tokens to display
|
||||
* @property organizeButtonUM configuration for the "Organize Tokens" button, if it should
|
||||
*/
|
||||
@Immutable
|
||||
internal sealed class WalletTokensListUM {
|
||||
|
||||
abstract val tokenList: ImmutableList<TokensListItemUM2>
|
||||
abstract val organizeButtonUM: TangemButtonUM?
|
||||
|
||||
data object Empty : WalletTokensListUM() {
|
||||
override val tokenList: ImmutableList<TokensListItemUM2.Portfolio> = persistentListOf()
|
||||
override val organizeButtonUM: TangemButtonUM? = null
|
||||
}
|
||||
|
||||
data object Loading : WalletTokensListUM() {
|
||||
override val tokenList: ImmutableList<TokensListItemUM2> = persistentListOf(
|
||||
TokensListItemUM2.Portfolio(
|
||||
tokenRowUM = TangemTokenRowUM.Loading(id = "0"),
|
||||
tokenList = persistentListOf(),
|
||||
isExpanded = false,
|
||||
isCollapsable = true,
|
||||
),
|
||||
TokensListItemUM2.Portfolio(
|
||||
tokenRowUM = TangemTokenRowUM.Loading(id = "1"),
|
||||
tokenList = persistentListOf(),
|
||||
isExpanded = false,
|
||||
isCollapsable = true,
|
||||
),
|
||||
TokensListItemUM2.Portfolio(
|
||||
tokenRowUM = TangemTokenRowUM.Loading(id = "2"),
|
||||
tokenList = persistentListOf(),
|
||||
isExpanded = false,
|
||||
isCollapsable = true,
|
||||
),
|
||||
)
|
||||
override val organizeButtonUM: TangemButtonUM? = null
|
||||
}
|
||||
|
||||
data class Content(
|
||||
override val tokenList: ImmutableList<TokensListItemUM2>,
|
||||
override val organizeButtonUM: TangemButtonUM?,
|
||||
) : WalletTokensListUM()
|
||||
}
|
||||
|
||||
/**
|
||||
* State of token list item in the wallet screen
|
||||
*/
|
||||
@Immutable
|
||||
internal sealed interface TokensListItemUM2 {
|
||||
val tokenRowUM: TangemRowUM
|
||||
|
||||
data class GroupTitle(
|
||||
override val tokenRowUM: TangemHeaderRowUM,
|
||||
) : TokensListItemUM2
|
||||
|
||||
data class Token(
|
||||
override val tokenRowUM: TangemTokenRowUM,
|
||||
) : TokensListItemUM2
|
||||
|
||||
data class Portfolio(
|
||||
override val tokenRowUM: TangemTokenRowUM,
|
||||
val tokenList: ImmutableList<TokensListItemUM2>,
|
||||
val isExpanded: Boolean,
|
||||
val isCollapsable: Boolean,
|
||||
) : TokensListItemUM2
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
|
||||
import com.tangem.core.ui.ds.button.TangemButtonUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Immutable
|
||||
internal sealed interface WalletUM {
|
||||
|
||||
val pullToRefreshConfig: PullToRefreshConfig
|
||||
val walletsBalanceUM: WalletBalanceUM
|
||||
|
||||
val buttons: PersistentList<TangemButtonUM>
|
||||
val notifications: ImmutableList<WalletNotificationUM>
|
||||
val notificationsCarousel: ImmutableList<WalletNotificationUM>
|
||||
|
||||
val tokensListUM: WalletTokensListUM
|
||||
|
||||
val nftState: WalletNFTItemUM
|
||||
|
||||
val type: WalletType
|
||||
|
||||
val tangemPayState: TangemPayState
|
||||
|
||||
data class Content(
|
||||
override val pullToRefreshConfig: PullToRefreshConfig,
|
||||
override val walletsBalanceUM: WalletBalanceUM,
|
||||
override val buttons: PersistentList<TangemButtonUM>,
|
||||
override val notifications: ImmutableList<WalletNotificationUM>,
|
||||
override val notificationsCarousel: ImmutableList<WalletNotificationUM>,
|
||||
override val tokensListUM: WalletTokensListUM,
|
||||
override val nftState: WalletNFTItemUM,
|
||||
override val type: WalletType,
|
||||
override val tangemPayState: TangemPayState,
|
||||
) : WalletUM
|
||||
|
||||
data class Locked(
|
||||
override val walletsBalanceUM: WalletBalanceUM,
|
||||
override val buttons: PersistentList<TangemButtonUM>,
|
||||
override val type: WalletType,
|
||||
override val notifications: ImmutableList<WalletNotificationUM> = persistentListOf(),
|
||||
) : WalletUM {
|
||||
override val notificationsCarousel: ImmutableList<WalletNotificationUM> = persistentListOf()
|
||||
override val pullToRefreshConfig = PullToRefreshConfig(false, {})
|
||||
override val tokensListUM: WalletTokensListUM = WalletTokensListUM.Empty // todo redesign main locked state
|
||||
override val nftState: WalletNFTItemUM = WalletNFTItemUM.Hidden
|
||||
override val tangemPayState: TangemPayState = TangemPayState.Empty
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
|||
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
|
||||
internal class CloseBottomSheetTransformer(userWalletId: UserWalletId) : WalletStateTransformer(userWalletId) {
|
||||
|
||||
|
|
@ -22,6 +23,10 @@ internal class CloseBottomSheetTransformer(userWalletId: UserWalletId) : WalletS
|
|||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
|
||||
private fun updateConfig(prevState: WalletState) = prevState.bottomSheetConfig?.copy(
|
||||
isShown = false,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
|||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState.MultiCurrency.WalletType
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.createStateByWalletType
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
|||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
|
||||
internal class OpenBottomSheetTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
|
|
@ -28,6 +29,10 @@ internal class OpenBottomSheetTransformer(
|
|||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
|
||||
private fun updateConfig() = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = onDismissBottomSheet,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.domain.models.wallet.UserWallet
|
|||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory
|
||||
|
||||
/**
|
||||
|
|
@ -26,6 +27,10 @@ internal class ReinitializeWalletTransformer(
|
|||
)
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
|
||||
override fun transform(prevState: WalletState): WalletState {
|
||||
return walletLoadingStateFactory.create(
|
||||
userWallet = userWallet,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
|||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNFTItemUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
|
||||
internal class RemoveNFTCollectionsTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
|
|
@ -17,4 +18,8 @@ internal class RemoveNFTCollectionsTransformer(
|
|||
is WalletState.SingleCurrency.Locked,
|
||||
-> prevState
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import com.tangem.domain.tokens.model.TokenActionsState
|
|||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletManageButton
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import timber.log.Timber
|
||||
|
|
@ -35,6 +36,10 @@ internal class SetCryptoCurrencyActionsTransformer(
|
|||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
|
||||
private fun TokenActionsState.toManageButtons(): PersistentList<WalletManageButton> {
|
||||
return states
|
||||
.filterIfS2C()
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.domain.models.wallet.UserWalletId
|
|||
import com.tangem.domain.onramp.model.cache.OnrampTransaction
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.SingleWalletOnrampTransactionConverter
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import timber.log.Timber
|
||||
|
|
@ -56,6 +57,10 @@ internal class SetExpressStatusesTransformer(
|
|||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
|
||||
private fun TangemBottomSheetConfig.updateStateWithExpressStatusBottomSheet(
|
||||
expressState: ExpressTransactionStateUM?,
|
||||
): TangemBottomSheetConfig {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.domain.nft.models.NFTCollections
|
|||
import com.tangem.domain.nft.models.allLoadedCollectionsEmpty
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNFTItemUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
internal class SetNFTCollectionsTransformer(
|
||||
|
|
@ -28,6 +29,10 @@ internal class SetNFTCollectionsTransformer(
|
|||
-> prevState
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
|
||||
private fun createContentNFTItemUM(onItemClick: () -> Unit): WalletNFTItemUM.Content {
|
||||
val collectionsContent = nftCollections
|
||||
.map { it.content }
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
|||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.SingleWalletCardStateConverter
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.SingleWalletMarketPriceConverter
|
||||
import timber.log.Timber
|
||||
|
|
@ -35,6 +36,10 @@ internal class SetPrimaryCurrencyTransformer(
|
|||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
|
||||
private fun WalletCardState.toLoadedSingleCurrencyState(): WalletCardState {
|
||||
return SingleWalletCardStateConverter(status.value, userWallet, appCurrency).convert(value = this)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.domain.models.wallet.UserWalletId
|
|||
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.WalletTokensListState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.mutate
|
||||
|
||||
|
|
@ -33,6 +34,10 @@ internal class SetRefreshStateTransformer(
|
|||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
|
||||
private fun PullToRefreshConfig.toUpdatedState(isRefreshing: Boolean): PullToRefreshConfig {
|
||||
return copy(isRefreshing = isRefreshing)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfo
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.disableButtons
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -51,6 +52,10 @@ internal class SetTokenListErrorTransformer(
|
|||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
|
||||
private fun WalletCardState.toLoadedState(): WalletCardState {
|
||||
return WalletCardState.Content(
|
||||
id = id,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletCardStateConverter
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TokenListStateConverter
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.enableButtons
|
||||
|
|
@ -45,6 +46,10 @@ internal class SetTokenListTransformer(
|
|||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
|
||||
private fun WalletCardState.toLoadedState(): WalletCardState {
|
||||
val fiatBalance = when (params) {
|
||||
is TokenConverterParams.Account -> params.accountList.totalFiatBalance
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.tangem.domain.models.wallet.UserWallet
|
|||
import com.tangem.domain.txhistory.models.TxHistoryStateError
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TxHistoryItemStateConverter
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import timber.log.Timber
|
||||
|
|
@ -35,6 +36,10 @@ internal class SetTxHistoryCountErrorTransformer(
|
|||
)
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
|
||||
override fun transform(prevState: WalletState): WalletState {
|
||||
return when (prevState) {
|
||||
is WalletState.SingleCurrency.Content -> prevState.copy(txHistoryState = createErrorState())
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
|||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import timber.log.Timber
|
||||
|
|
@ -33,6 +34,10 @@ internal class SetTxHistoryCountTransformer(
|
|||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
|
||||
private fun TxHistoryState.toLoadingState(): TxHistoryState {
|
||||
return if (this is TxHistoryState.Content) {
|
||||
Timber.d("Load transactions history: $transactionsCount")
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.domain.models.wallet.UserWalletId
|
|||
import com.tangem.domain.txhistory.models.TxHistoryListError
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import timber.log.Timber
|
||||
|
||||
internal class SetTxHistoryItemsErrorTransformer(
|
||||
|
|
@ -27,6 +28,10 @@ internal class SetTxHistoryItemsErrorTransformer(
|
|||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
|
||||
private fun createErrorState(): TxHistoryState.Error = when (error) {
|
||||
is TxHistoryListError.DataError -> {
|
||||
TxHistoryState.Error(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
|||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TxHistoryItemFlowConverter
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import timber.log.Timber
|
||||
|
|
@ -32,6 +33,10 @@ internal class SetTxHistoryItemsTransformer(
|
|||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
|
||||
private fun TxHistoryState.toContentState(): TxHistoryState {
|
||||
val converter = TxHistoryItemFlowConverter(
|
||||
currentState = this,
|
||||
|
|
|
|||
|
|
@ -2,13 +2,18 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
|||
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotificationUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import timber.log.Timber
|
||||
|
||||
internal class SetWarningsTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
private val warnings: ImmutableList<WalletNotification>,
|
||||
private val notifications: ImmutableList<WalletNotificationUM> = persistentListOf(),
|
||||
private val notificationsCarousel: ImmutableList<WalletNotificationUM> = persistentListOf(),
|
||||
) : WalletStateTransformer(userWalletId) {
|
||||
|
||||
override fun transform(prevState: WalletState): WalletState {
|
||||
|
|
@ -23,4 +28,17 @@ internal class SetWarningsTransformer(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return when (walletUM) {
|
||||
is WalletUM.Content -> walletUM.copy(
|
||||
notifications = notifications,
|
||||
notificationsCarousel = notificationsCarousel,
|
||||
)
|
||||
is WalletUM.Locked -> {
|
||||
Timber.w("Impossible to update notifications for locked wallet")
|
||||
walletUM
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
|||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
|
||||
internal class TangemPayExposedDeviceTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
|
|
@ -14,4 +15,8 @@ internal class TangemPayExposedDeviceTransformer(
|
|||
prevState
|
||||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
|||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
|
||||
internal class TangemPayHiddenStateTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
|
|
@ -15,4 +16,8 @@ internal class TangemPayHiddenStateTransformer(
|
|||
prevState
|
||||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
|||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
|
||||
internal class TangemPayHideOnboardingStateTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
|
|
@ -15,4 +16,8 @@ internal class TangemPayHideOnboardingStateTransformer(
|
|||
prevState
|
||||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
|||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
|
||||
internal class TangemPayLoadingStateTransformer(userWalletId: UserWalletId) : WalletStateTransformer(userWalletId) {
|
||||
override fun transform(prevState: WalletState): WalletState {
|
||||
|
|
@ -12,4 +13,8 @@ internal class TangemPayLoadingStateTransformer(userWalletId: UserWalletId) : Wa
|
|||
prevState
|
||||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
|||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
|
||||
internal class TangemPayOnboardingBannerStateTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
|
|
@ -22,4 +23,8 @@ internal class TangemPayOnboardingBannerStateTransformer(
|
|||
prevState
|
||||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import com.tangem.feature.wallet.impl.R
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification.Warning.TangemPayRefreshNeeded
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
|
||||
internal class TangemPayRefreshNeededStateTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
|
|
@ -32,4 +33,8 @@ internal class TangemPayRefreshNeededStateTransformer(
|
|||
prevState
|
||||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import com.tangem.domain.models.wallet.UserWalletId
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
|
||||
internal class TangemPayRefreshShowProgressTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
|
|
@ -21,4 +22,8 @@ internal class TangemPayRefreshShowProgressTransformer(
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import com.tangem.domain.models.wallet.UserWalletId
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
|
||||
internal class TangemPayUnavailableStateTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
|
|
@ -20,4 +21,8 @@ internal class TangemPayUnavailableStateTransformer(
|
|||
prevState
|
||||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState.
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.domain.pay.model.CustomerInfo.KycStatus.APPROVED
|
||||
import com.tangem.domain.pay.model.CustomerInfo
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import java.util.Currency
|
||||
|
||||
/**
|
||||
|
|
@ -42,6 +43,10 @@ internal class TangemPayUpdateInfoStateTransformer(
|
|||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
|
||||
private fun createInitialState(): TangemPayState {
|
||||
val cardInfo = value.info.cardInfo
|
||||
val productInstance = value.info.productInstance
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
internal abstract class TypedWalletStateTransformer<S : WalletState>(
|
||||
userWalletId: UserWalletId,
|
||||
protected val targetStateClass: KClass<S>,
|
||||
) : WalletStateTransformer(userWalletId) {
|
||||
|
||||
abstract fun transformTyped(prevState: S): WalletState
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
final override fun transform(prevState: WalletState): WalletState {
|
||||
return if (prevState::class == targetStateClass) {
|
||||
transformTyped(prevState as S)
|
||||
} else {
|
||||
prevState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
|||
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.showSwapBadge
|
||||
|
||||
internal class UpdateMultiWalletActionButtonBadgeTransformer(
|
||||
|
|
@ -16,4 +17,8 @@ internal class UpdateMultiWalletActionButtonBadgeTransformer(
|
|||
else -> prevState
|
||||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfo
|
|||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import timber.log.Timber
|
||||
|
||||
internal class UpdateWalletCardsCountTransformer(
|
||||
|
|
@ -30,6 +31,10 @@ internal class UpdateWalletCardsCountTransformer(
|
|||
}
|
||||
}
|
||||
|
||||
override fun transform(walletUM: WalletUM): WalletUM {
|
||||
return walletUM // todo redesign main
|
||||
}
|
||||
|
||||
private fun WalletCardState.toUpdatedState(): WalletCardState {
|
||||
return when (this) {
|
||||
is WalletCardState.Content -> copy(
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
|||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
internal abstract class WalletStateTransformer(
|
||||
|
|
@ -11,6 +12,8 @@ internal abstract class WalletStateTransformer(
|
|||
|
||||
abstract fun transform(prevState: WalletState): WalletState
|
||||
|
||||
abstract fun transform(walletUM: WalletUM): WalletUM
|
||||
|
||||
final override fun transform(prevState: WalletScreenState): WalletScreenState {
|
||||
return prevState.copy(
|
||||
wallets = prevState.wallets
|
||||
|
|
@ -18,6 +21,11 @@ internal abstract class WalletStateTransformer(
|
|||
if (state.walletCardState.id == userWalletId) transform(state) else state
|
||||
}
|
||||
.toImmutableList(),
|
||||
wallets2 = prevState.wallets2
|
||||
.map { walletUM ->
|
||||
if (walletUM.walletsBalanceUM.id == userWalletId) transform(walletUM) else walletUM
|
||||
}
|
||||
.toImmutableList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ internal class WalletLoadingStateFactory(
|
|||
bottomSheetConfig = null,
|
||||
tokensListState = WalletTokensListState.ContentState.Loading,
|
||||
nftState = WalletNFTItemUM.Hidden,
|
||||
type = WalletState.MultiCurrency.WalletType.Hot,
|
||||
type = WalletType.Hot,
|
||||
tangemPayState = TangemPayState.Empty,
|
||||
)
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ internal class WalletLoadingStateFactory(
|
|||
bottomSheetConfig = null,
|
||||
tokensListState = WalletTokensListState.ContentState.Loading,
|
||||
nftState = WalletNFTItemUM.Hidden,
|
||||
type = WalletState.MultiCurrency.WalletType.Cold,
|
||||
type = WalletType.Cold,
|
||||
tangemPayState = TangemPayState.Empty,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,12 +9,9 @@ import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetWarningsTransformer
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
internal class MultiWalletWarningsSubscriber(
|
||||
private val userWallet: UserWallet,
|
||||
|
|
@ -37,7 +34,13 @@ internal class MultiWalletWarningsSubscriber(
|
|||
it.wallets.any { walletState -> walletState.walletCardState.id == userWallet.walletId }
|
||||
}
|
||||
|
||||
stateHolder.update(SetWarningsTransformer(userWallet.walletId, warnings))
|
||||
stateHolder.update(
|
||||
SetWarningsTransformer(
|
||||
userWalletId = userWallet.walletId,
|
||||
warnings = warnings,
|
||||
notifications = persistentListOf(),
|
||||
),
|
||||
)
|
||||
walletWarningsAnalyticsSender.send(displayedState, warnings)
|
||||
walletWarningsSingleEventSender.send(
|
||||
userWalletId = userWallet.walletId,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.GetWalletNotificationsCarouselFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.GetWalletWarningsFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotificationUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetWarningsTransformer
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class MultiWalletWarningsSubscriberV2(
|
||||
private val userWallet: UserWallet,
|
||||
private val stateHolder: WalletStateController,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val getWalletWarningsFactory: GetWalletWarningsFactory,
|
||||
private val getWalletNotificationsCarouselFactory: GetWalletNotificationsCarouselFactory,
|
||||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender,
|
||||
) : WalletSubscriber() {
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<ImmutableList<WalletNotificationUM>> {
|
||||
return combine(
|
||||
flow = getWalletWarningsFactory.create(userWallet, clickIntents).conflate().distinctUntilChanged(),
|
||||
flow2 = getWalletNotificationsCarouselFactory.create(userWallet, clickIntents).conflate()
|
||||
.distinctUntilChanged(),
|
||||
) { notifications, notificationsCarousel ->
|
||||
val displayedWalletUM = stateHolder.getWalletUM(userWallet.walletId)
|
||||
|
||||
// Wait until the wallet appears in the list
|
||||
stateHolder.uiState.first {
|
||||
it.wallets2.any { walletUM -> walletUM.walletsBalanceUM.id == userWallet.walletId }
|
||||
}
|
||||
|
||||
// If there are notifications, we need to filter out the RateApp notification from stackable notifications,
|
||||
// because it should not be shown together with other notifications.
|
||||
val alteredNotificationsCarousel = if (notifications.isNotEmpty()) {
|
||||
notificationsCarousel.filterNot { it is WalletNotificationUM.RateApp }
|
||||
} else {
|
||||
notificationsCarousel
|
||||
}.toPersistentList()
|
||||
|
||||
stateHolder.update(
|
||||
SetWarningsTransformer(
|
||||
userWalletId = userWallet.walletId,
|
||||
warnings = persistentListOf(),
|
||||
notifications = notifications,
|
||||
notificationsCarousel = alteredNotificationsCarousel,
|
||||
),
|
||||
)
|
||||
|
||||
val totalNotifications = (notifications + alteredNotificationsCarousel).toPersistentList()
|
||||
|
||||
walletWarningsAnalyticsSender.send(displayedWalletUM, totalNotifications)
|
||||
walletWarningsSingleEventSender.send(
|
||||
userWalletId = userWallet.walletId,
|
||||
displayedWalletUM = displayedWalletUM,
|
||||
newNotifications = totalNotifications,
|
||||
)
|
||||
|
||||
totalNotifications
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetWarningsTransformer
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
|
|
@ -32,7 +33,7 @@ internal class SingleWalletNotificationsSubscriber(
|
|||
.onEach { warnings ->
|
||||
val displayedState = stateHolder.getWalletState(userWallet.walletId)
|
||||
|
||||
stateHolder.update(SetWarningsTransformer(userWallet.walletId, warnings))
|
||||
stateHolder.update(SetWarningsTransformer(userWallet.walletId, warnings, persistentListOf()))
|
||||
walletWarningsAnalyticsSender.send(displayedState, warnings)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -720,7 +720,6 @@ internal fun LazyListScope.nftCollections(state: WalletState, itemModifier: Modi
|
|||
private fun ShowBottomSheet(bottomSheetConfig: TangemBottomSheetConfig?) {
|
||||
if (bottomSheetConfig != null) {
|
||||
when (bottomSheetConfig.content) {
|
||||
is WalletBottomSheetConfig -> WalletBottomSheet(config = bottomSheetConfig)
|
||||
is ActionsBottomSheetConfig -> TokenActionsBottomSheet(config = bottomSheetConfig)
|
||||
is ChooseAddressBottomSheetConfig -> ChooseAddressBottomSheet(config = bottomSheetConfig)
|
||||
is ExpressStatusBottomSheetConfig -> ExpressStatusBottomSheet(config = bottomSheetConfig)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,440 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.semantics.clearAndSetSemantics
|
||||
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 androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.arkivanov.decompose.ExperimentalDecomposeApi
|
||||
import com.tangem.core.ui.components.atoms.Hand
|
||||
import com.tangem.core.ui.components.atoms.handComposableComponentHeight
|
||||
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
|
||||
import com.tangem.core.ui.components.haze.hazeEffectTangem
|
||||
import com.tangem.core.ui.components.haze.hazeSourceTangem
|
||||
import com.tangem.core.ui.components.rememberIsKeyboardVisible
|
||||
import com.tangem.core.ui.components.sheetscaffold.*
|
||||
import com.tangem.core.ui.components.snackbar.CopiedTextSnackbar
|
||||
import com.tangem.core.ui.components.snackbar.TangemSnackbar
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.*
|
||||
import com.tangem.core.ui.test.MainScreenTestTags
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.accountScreenState
|
||||
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.accountScreenWithEmptyTokensState
|
||||
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.walletScreenState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.NOT_INITIALIZED_WALLET_INDEX
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
import dev.chrisbanes.haze.HazeProgressive
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalDecomposeApi::class)
|
||||
@Composable
|
||||
internal fun WalletScreen2(
|
||||
state: WalletScreenState,
|
||||
bottomSheetContent: @Composable (() -> Unit),
|
||||
bottomSheetHeaderHeightProvider: () -> Dp,
|
||||
onBottomSheetStateChange: (BottomSheetState) -> Unit,
|
||||
) {
|
||||
// It means that screen is still initializing
|
||||
if (state.selectedWalletIndex == NOT_INITIALIZED_WALLET_INDEX) return
|
||||
|
||||
val walletsListState = rememberLazyListState(initialFirstVisibleItemIndex = state.selectedWalletIndex)
|
||||
val snackbarHostState = remember(::SnackbarHostState)
|
||||
val isAutoScroll = remember { mutableStateOf(value = false) }
|
||||
|
||||
WalletContent2(
|
||||
state = state,
|
||||
walletsListState = walletsListState,
|
||||
snackbarHostState = snackbarHostState,
|
||||
isAutoScroll = isAutoScroll,
|
||||
onAutoScrollReset = { isAutoScroll.value = false },
|
||||
bottomSheetContent = bottomSheetContent,
|
||||
bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider,
|
||||
onBottomSheetStateChange = onBottomSheetStateChange,
|
||||
)
|
||||
|
||||
WalletEventEffect(
|
||||
walletsListState = walletsListState,
|
||||
snackbarHostState = snackbarHostState,
|
||||
event = state.event,
|
||||
onAutoScrollSet = { isAutoScroll.value = true },
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalDecomposeApi::class)
|
||||
@Suppress("LongMethod", "LongParameterList", "UnusedPrivateMember")
|
||||
@Composable
|
||||
private fun WalletContent2(
|
||||
state: WalletScreenState,
|
||||
walletsListState: LazyListState,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
isAutoScroll: State<Boolean>,
|
||||
onAutoScrollReset: () -> Unit,
|
||||
bottomSheetHeaderHeightProvider: () -> Dp,
|
||||
onBottomSheetStateChange: (BottomSheetState) -> Unit,
|
||||
bottomSheetContent: @Composable (() -> Unit),
|
||||
) {
|
||||
/*
|
||||
* Don't pass key to remember, because it will brake scroll animation.
|
||||
* selectedWalletIndex will be changed in WalletsListEffects.
|
||||
*/
|
||||
// val selectedWalletIndex by remember(state.selectedWalletIndex) { mutableIntStateOf(state.selectedWalletIndex) }
|
||||
// val selectedWallet = state.wallets2.getOrElse(selectedWalletIndex) { state.wallets2[state.selectedWalletIndex] }
|
||||
|
||||
val statusBarHeight = with(LocalDensity.current) { WindowInsets.statusBars.getBottom(this).toDp() }
|
||||
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
val partialCollapsedHeight = 64.dp + statusBarHeight
|
||||
|
||||
val scaffoldContent: @Composable (PaddingValues?) -> Unit = { _ ->
|
||||
|
||||
val pagerState = rememberPagerState(
|
||||
initialPage = state.selectedWalletIndex,
|
||||
pageCount = { state.wallets2.size },
|
||||
)
|
||||
|
||||
LaunchedEffect(pagerState.currentPage) {
|
||||
if (pagerState.currentPage != state.selectedWalletIndex) {
|
||||
state.onWalletChange(pagerState.currentPage, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BaseScaffoldWithMarkets(
|
||||
state = state,
|
||||
listState = listState,
|
||||
snackbarHostState = snackbarHostState,
|
||||
bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider,
|
||||
onBottomSheetStateChange = onBottomSheetStateChange,
|
||||
bottomSheetContent = bottomSheetContent,
|
||||
content = scaffoldContent,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList", "LongMethod", "CyclomaticComplexMethod", "UnusedPrivateMember")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private inline fun BaseScaffoldWithMarkets(
|
||||
state: WalletScreenState,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
listState: LazyListState,
|
||||
bottomSheetHeaderHeightProvider: () -> Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
noinline onBottomSheetStateChange: (BottomSheetState) -> Unit,
|
||||
crossinline bottomSheetContent: @Composable () -> Unit,
|
||||
crossinline content: @Composable (PaddingValues) -> Unit,
|
||||
) {
|
||||
val bottomSheetState = rememberTangemStandardBottomSheetState()
|
||||
val isPowerSaving by LocalPowerSavingState.current.isPowerSavingModeEnabled.collectAsStateWithLifecycle()
|
||||
|
||||
val isKeyboardVisible by rememberIsKeyboardVisible()
|
||||
|
||||
val scaffoldState = rememberTangemBottomSheetScaffoldState(
|
||||
bottomSheetState = bottomSheetState,
|
||||
snackbarHostState = snackbarHostState,
|
||||
)
|
||||
|
||||
val density = LocalDensity.current
|
||||
val bottomBarHeight = with(density) { WindowInsets.systemBars.getBottom(density = this).toDp() }
|
||||
val statusBarHeight = with(density) { WindowInsets.statusBars.getTop(density = this).toDp() }
|
||||
val peekHeight = bottomSheetHeaderHeightProvider() + handComposableComponentHeight + bottomBarHeight
|
||||
val maxHeight = LocalWindowSize.current.height
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val background = if (state.isNewMarketEnabled) {
|
||||
TangemTheme.colors.background.tertiary
|
||||
} else {
|
||||
TangemTheme.colors.background.primary
|
||||
}
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalMainBottomSheetColor provides remember(background) { mutableStateOf(background) },
|
||||
) {
|
||||
val backgroundColor = LocalMainBottomSheetColor.current
|
||||
var isSearchFieldFocused by remember { mutableStateOf(false) }
|
||||
val isNavBarVisible = remember { mutableStateOf(true) }
|
||||
|
||||
BottomSheetStateEffects(
|
||||
bottomSheetState = bottomSheetState,
|
||||
onBottomSheetStateChange = onBottomSheetStateChange,
|
||||
navigationBarVisible = isNavBarVisible,
|
||||
isSearchFieldFocused = isSearchFieldFocused,
|
||||
)
|
||||
|
||||
Box(modifier = modifier) {
|
||||
TangemBottomSheetScaffold(
|
||||
modifier = Modifier.background(
|
||||
brush = Brush.verticalGradient(
|
||||
listOf(
|
||||
TangemTheme.colors2.surface.level1,
|
||||
TangemTheme.colors2.surface.level2,
|
||||
),
|
||||
),
|
||||
),
|
||||
snackbarHost = { snackbarHostState ->
|
||||
WalletSnackbarHost(
|
||||
snackbarHostState = snackbarHostState,
|
||||
event = state.event,
|
||||
modifier = Modifier
|
||||
.padding(bottom = TangemTheme.dimens.spacing4)
|
||||
.navigationBarsPadding(),
|
||||
)
|
||||
},
|
||||
containerColor = Color.Unspecified,
|
||||
sheetContainerColor = backgroundColor.value,
|
||||
scaffoldState = scaffoldState,
|
||||
sheetPeekHeight = peekHeight,
|
||||
sheetShape = TangemTheme.shapes.bottomSheetLarge,
|
||||
sheetContent = {
|
||||
// hide bottom sheet when back pressed
|
||||
BackHandler(
|
||||
isKeyboardVisible.not() &&
|
||||
bottomSheetState.currentValue == TangemSheetValue.Expanded,
|
||||
) {
|
||||
coroutineScope.launch { bottomSheetState.partialExpand() }
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
// expand bottom sheet when clicked on the header
|
||||
.clickable(
|
||||
enabled = bottomSheetState.currentValue == TangemSheetValue.PartiallyExpanded,
|
||||
indication = null,
|
||||
interactionSource = null,
|
||||
) {
|
||||
coroutineScope.launch { bottomSheetState.expand() }
|
||||
}
|
||||
.sizeIn(maxHeight = maxHeight - statusBarHeight),
|
||||
) {
|
||||
Hand(Modifier.drawBehind { drawRect(backgroundColor.value) })
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.onFocusChanged {
|
||||
isSearchFieldFocused = it.isFocused
|
||||
},
|
||||
) {
|
||||
bottomSheetContent()
|
||||
}
|
||||
}
|
||||
},
|
||||
content = { paddingValues ->
|
||||
Box {
|
||||
Column(
|
||||
modifier = Modifier.hazeSourceTangem(-1f),
|
||||
) {
|
||||
content(paddingValues)
|
||||
}
|
||||
|
||||
Surface(
|
||||
color = Color.Unspecified,
|
||||
contentColor = Color.Unspecified,
|
||||
modifier = Modifier
|
||||
.hazeEffectTangem {
|
||||
progressive =
|
||||
HazeProgressive.verticalGradient(startIntensity = 1f, endIntensity = 0f)
|
||||
},
|
||||
) {
|
||||
TangemTopBar(
|
||||
title = stringReference(""), // todo balance
|
||||
startIconRes = R.drawable.ic_tangem_24,
|
||||
endIconRes = R.drawable.ic_more_default_24,
|
||||
onEndContentClick = state.topBarConfig.onDetailsClick,
|
||||
isGhostButtons = !isPowerSaving,
|
||||
modifier = Modifier
|
||||
.testTag(MainScreenTestTags.TOP_BAR),
|
||||
)
|
||||
}
|
||||
|
||||
BottomSheetScrim(
|
||||
color = if (state.showMarketsOnboarding) {
|
||||
Color.Black.copy(alpha = .65f)
|
||||
} else {
|
||||
Color.Black.copy(alpha = .40f)
|
||||
},
|
||||
visible = bottomSheetState.targetValue == TangemSheetValue.Expanded ||
|
||||
state.showMarketsOnboarding,
|
||||
onDismissRequest = {
|
||||
coroutineScope.launch { bottomSheetState.partialExpand() }
|
||||
state.onDismissMarketsTooltip()
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
AnimatedVisibility(
|
||||
modifier = Modifier.align(Alignment.BottomCenter),
|
||||
visible = isNavBarVisible.value,
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.background(backgroundColor.value)
|
||||
.height(bottomBarHeight)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(state.showMarketsOnboarding, bottomSheetState.targetValue) {
|
||||
if (state.showMarketsOnboarding && bottomSheetState.targetValue == TangemSheetValue.Expanded) {
|
||||
state.onDismissMarketsTooltip()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomSheetScrim(color: Color, visible: Boolean, onDismissRequest: () -> Unit) {
|
||||
val alpha by animateFloatAsState(
|
||||
targetValue = if (visible) 1f else 0f,
|
||||
animationSpec = tween(),
|
||||
label = "scrim",
|
||||
)
|
||||
val dismissSheet = if (visible) {
|
||||
Modifier
|
||||
.pointerInput(onDismissRequest) {
|
||||
detectTapGestures {
|
||||
onDismissRequest()
|
||||
}
|
||||
}
|
||||
.clearAndSetSemantics {}
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
Canvas(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.then(dismissSheet),
|
||||
) {
|
||||
drawRect(color = color, alpha = alpha)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("CyclomaticComplexMethod", "MagicNumber", "LongMethod")
|
||||
@Composable
|
||||
private fun BottomSheetStateEffects(
|
||||
bottomSheetState: TangemSheetState,
|
||||
navigationBarVisible: MutableState<Boolean>,
|
||||
onBottomSheetStateChange: (BottomSheetState) -> Unit,
|
||||
isSearchFieldFocused: Boolean,
|
||||
) {
|
||||
LaunchedEffect(bottomSheetState.targetValue) {
|
||||
when (bottomSheetState.targetValue) {
|
||||
TangemSheetValue.Hidden,
|
||||
TangemSheetValue.Expanded,
|
||||
-> navigationBarVisible.value = false
|
||||
TangemSheetValue.PartiallyExpanded,
|
||||
-> navigationBarVisible.value = true
|
||||
}
|
||||
}
|
||||
|
||||
// expand bottom sheet when keyboard appears
|
||||
val isKeyboardVisible by rememberIsKeyboardVisible()
|
||||
|
||||
LaunchedEffect(isKeyboardVisible) {
|
||||
if (isKeyboardVisible && isSearchFieldFocused) {
|
||||
bottomSheetState.expand()
|
||||
}
|
||||
}
|
||||
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
// hide keyboard when bottom sheet is about to be hidden
|
||||
LaunchedEffect(Unit) {
|
||||
snapshotFlow {
|
||||
bottomSheetState.currentValue == TangemSheetValue.Expanded &&
|
||||
bottomSheetState.targetValue == TangemSheetValue.PartiallyExpanded
|
||||
}.collect { sheetHasBeenHidden ->
|
||||
if (sheetHasBeenHidden) {
|
||||
keyboardController?.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val isSheetHidden = bottomSheetState.targetValue == TangemSheetValue.PartiallyExpanded
|
||||
LaunchedEffect(isSheetHidden) {
|
||||
onBottomSheetStateChange(
|
||||
if (isSheetHidden) {
|
||||
BottomSheetState.COLLAPSED
|
||||
} else {
|
||||
BottomSheetState.EXPANDED
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WalletSnackbarHost(
|
||||
snackbarHostState: SnackbarHostState,
|
||||
event: StateEvent<WalletEvent>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
SnackbarHost(hostState = snackbarHostState, modifier = modifier) { data ->
|
||||
if (event is StateEvent.Triggered && event.data is WalletEvent.CopyAddress) {
|
||||
CopiedTextSnackbar(data)
|
||||
} else {
|
||||
TangemSnackbar(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@OptIn(ExperimentalDecomposeApi::class)
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun WalletScreen2_Preview(@PreviewParameter(WalletScreen2PreviewProvider::class) data: WalletScreenState) {
|
||||
TangemThemePreviewRedesign {
|
||||
WalletScreen2(
|
||||
state = data,
|
||||
bottomSheetContent = {
|
||||
Text("Markets Content")
|
||||
},
|
||||
bottomSheetHeaderHeightProvider = { 10.dp },
|
||||
onBottomSheetStateChange = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class WalletScreen2PreviewProvider : PreviewParameterProvider<WalletScreenState> {
|
||||
override val values: Sequence<WalletScreenState>
|
||||
get() = sequenceOf(
|
||||
walletScreenState,
|
||||
walletScreenState.copy(selectedWalletIndex = 1),
|
||||
accountScreenState.copy(selectedWalletIndex = 1),
|
||||
accountScreenWithEmptyTokensState.copy(selectedWalletIndex = 1),
|
||||
)
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.common
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.*
|
||||
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.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.PrimaryButtonIconStart
|
||||
import com.tangem.core.ui.components.SecondaryButton
|
||||
import com.tangem.core.ui.components.SecondaryButtonIconStart
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletBottomSheetConfig
|
||||
|
||||
/**
|
||||
* Wallet bottom sheet with detail notification information
|
||||
*
|
||||
* @param config component config
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Composable
|
||||
internal fun WalletBottomSheet(config: TangemBottomSheetConfig) {
|
||||
TangemBottomSheet(config) { content: WalletBottomSheetConfig ->
|
||||
BottomSheetContent(config = content)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomSheetContent(config: WalletBottomSheetConfig) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.padding(top = TangemTheme.dimens.spacing40, bottom = TangemTheme.dimens.spacing16),
|
||||
verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing40),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = config.iconResId),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(size = TangemTheme.dimens.size48),
|
||||
tint = when (config) {
|
||||
is WalletBottomSheetConfig.UnlockWallets -> TangemTheme.colors.icon.primary1
|
||||
},
|
||||
)
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing16),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = config.title.resolveReference(),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
style = TangemTheme.typography.h2,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = config.subtitle.resolveReference(),
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
|
||||
Column(verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing10)) {
|
||||
val buttonModifier = Modifier.fillMaxWidth()
|
||||
|
||||
PrimaryButton(config = config.primaryButtonConfig, modifier = buttonModifier)
|
||||
|
||||
SecondaryButton(config = config.secondaryButtonConfig, modifier = buttonModifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PrimaryButton(config: WalletBottomSheetConfig.ButtonConfig, modifier: Modifier = Modifier) {
|
||||
if (config.iconResId == null) {
|
||||
PrimaryButton(
|
||||
text = config.text.resolveReference(),
|
||||
onClick = config.onClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
PrimaryButtonIconStart(
|
||||
text = config.text.resolveReference(),
|
||||
iconResId = config.iconResId,
|
||||
onClick = config.onClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SecondaryButton(config: WalletBottomSheetConfig.ButtonConfig, modifier: Modifier = Modifier) {
|
||||
if (config.iconResId == null) {
|
||||
SecondaryButton(
|
||||
text = config.text.resolveReference(),
|
||||
onClick = config.onClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
SecondaryButtonIconStart(
|
||||
text = config.text.resolveReference(),
|
||||
iconResId = config.iconResId,
|
||||
onClick = config.onClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun WalletBottomSheetContent_Preview(
|
||||
@PreviewParameter(WalletBottomSheetConfigProvider::class)
|
||||
config: WalletBottomSheetConfig,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
// Use preview of content because ModalBottomSheet isn't supported in Preview mode
|
||||
BottomSheetContent(config = config)
|
||||
}
|
||||
}
|
||||
|
||||
private class WalletBottomSheetConfigProvider : CollectionPreviewParameterProvider<WalletBottomSheetConfig>(
|
||||
collection = listOf(WalletPreviewData.bottomSheet.content as WalletBottomSheetConfig),
|
||||
)
|
||||
// endregion
|
||||
Loading…
Add table
Add a link
Reference in a new issue