Updated on 2026-08-14
This commit is contained in:
commit
226a413558
994 changed files with 27178 additions and 11929 deletions
|
|
@ -1,20 +0,0 @@
|
|||
package com.tangem.feature.wallet.di
|
||||
|
||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.feature.wallet.featuretoggle.WalletFeatureToggles
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object FeatureTogglesModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideWalletFeatureToggles(featureTogglesManager: FeatureTogglesManager): WalletFeatureToggles {
|
||||
return WalletFeatureToggles(featureTogglesManager)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.feature.wallet.di
|
||||
|
||||
import com.tangem.core.navigation.ReduxNavController
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.feature.wallet.presentation.router.DefaultWalletRouter
|
||||
import com.tangem.features.wallet.navigation.WalletRouter
|
||||
import dagger.Module
|
||||
|
|
@ -15,7 +17,11 @@ internal object WalletRouterModule {
|
|||
|
||||
@Provides
|
||||
@ActivityScoped
|
||||
fun provideWalletRouter(reduxNavController: ReduxNavController): WalletRouter {
|
||||
return DefaultWalletRouter(reduxNavController = reduxNavController)
|
||||
fun provideWalletRouter(
|
||||
appRouter: AppRouter,
|
||||
urlOpener: UrlOpener,
|
||||
reduxStateHolder: ReduxStateHolder,
|
||||
): WalletRouter {
|
||||
return DefaultWalletRouter(appRouter, urlOpener, reduxStateHolder)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
package com.tangem.feature.wallet.featuretoggle
|
||||
|
||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
|
||||
internal class WalletFeatureToggles(
|
||||
private val featureTogglesManager: FeatureTogglesManager,
|
||||
) {
|
||||
|
||||
val isTokenListLceFlowEnabled: Boolean
|
||||
get() = featureTogglesManager.isFeatureEnabled("TOKEN_LIST_LCE_ENABLED")
|
||||
}
|
||||
|
|
@ -1,14 +1,18 @@
|
|||
package com.tangem.feature.wallet.presentation
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.arkivanov.decompose.defaultComponentContext
|
||||
import com.tangem.core.decompose.context.DefaultAppComponentContext
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.ui.UiDependencies
|
||||
import com.tangem.core.ui.components.SystemBarsEffect
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.screen.ComposeFragment
|
||||
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensUi
|
||||
import com.tangem.features.markets.MarketsFeatureToggles
|
||||
import com.tangem.features.markets.component.MarketsListComponent
|
||||
import com.tangem.features.wallet.navigation.WalletRouter
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -23,28 +27,49 @@ internal class WalletFragment : ComposeFragment() {
|
|||
@Inject
|
||||
override lateinit var uiDependencies: UiDependencies
|
||||
|
||||
@Inject
|
||||
internal lateinit var manageTokensUi: ManageTokensUi
|
||||
|
||||
/** Feature router */
|
||||
@Inject
|
||||
internal lateinit var walletRouter: WalletRouter
|
||||
|
||||
@Inject
|
||||
internal lateinit var marketsListComponentFactory: MarketsListComponent.Factory
|
||||
|
||||
@Inject
|
||||
internal lateinit var coroutineDispatcherProvider: CoroutineDispatcherProvider
|
||||
|
||||
@Inject
|
||||
internal lateinit var componentBuilder: DecomposeComponent.Builder
|
||||
|
||||
@Inject
|
||||
internal lateinit var marketsFeatureToggles: MarketsFeatureToggles
|
||||
|
||||
private var marketsListComponent: MarketsListComponent? = null
|
||||
|
||||
private val _walletRouter: InnerWalletRouter
|
||||
get() = requireNotNull(walletRouter as? InnerWalletRouter) {
|
||||
"_walletRouter should be instance of InnerWalletRouter"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (marketsFeatureToggles.isFeatureEnabled) {
|
||||
val appContext = DefaultAppComponentContext(
|
||||
componentContext = defaultComponentContext(requireActivity().onBackPressedDispatcher),
|
||||
messageHandler = uiDependencies.eventMessageHandler,
|
||||
dispatchers = coroutineDispatcherProvider,
|
||||
hiltComponentBuilder = componentBuilder,
|
||||
)
|
||||
|
||||
marketsListComponent = marketsListComponentFactory.create(appContext)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun ScreenContent(modifier: Modifier) {
|
||||
val systemBarsColor = TangemTheme.colors.background.secondary
|
||||
SystemBarsEffect {
|
||||
setSystemBarsColor(systemBarsColor)
|
||||
}
|
||||
|
||||
_walletRouter.Initialize(
|
||||
onFinish = requireActivity()::finish,
|
||||
manageTokensUi = manageTokensUi,
|
||||
marketsListComponent = marketsListComponent,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.feature.wallet.presentation.common
|
||||
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.currency.tokenicon.TokenIconState
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
|
@ -67,7 +67,7 @@ internal object WalletPreviewData {
|
|||
}
|
||||
|
||||
val coinIconState
|
||||
get() = TokenIconState.CoinIcon(
|
||||
get() = CurrencyIconState.CoinIcon(
|
||||
url = null,
|
||||
fallbackResId = R.drawable.img_polygon_22,
|
||||
isGrayscale = false,
|
||||
|
|
@ -75,9 +75,9 @@ internal object WalletPreviewData {
|
|||
)
|
||||
|
||||
private val tokenIconState
|
||||
get() = TokenIconState.TokenIcon(
|
||||
get() = CurrencyIconState.TokenIcon(
|
||||
url = null,
|
||||
networkBadgeIconResId = R.drawable.img_polygon_22,
|
||||
topBadgeIconResId = R.drawable.img_polygon_22,
|
||||
fallbackTint = TangemColorPalette.Black,
|
||||
fallbackBackground = TangemColorPalette.Meadow,
|
||||
isGrayscale = false,
|
||||
|
|
@ -85,10 +85,10 @@ internal object WalletPreviewData {
|
|||
)
|
||||
|
||||
private val customTokenIconState
|
||||
get() = TokenIconState.CustomTokenIcon(
|
||||
get() = CurrencyIconState.CustomTokenIcon(
|
||||
tint = TangemColorPalette.Black,
|
||||
background = TangemColorPalette.Meadow,
|
||||
networkBadgeIconResId = R.drawable.img_polygon_22,
|
||||
topBadgeIconResId = R.drawable.img_polygon_22,
|
||||
isGrayscale = false,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.compose.ui.unit.Constraints
|
||||
import com.tangem.core.ui.components.currency.tokenicon.TokenIcon
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIcon
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.extensions.rememberHapticFeedback
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -49,7 +49,7 @@ internal fun TokenItem(
|
|||
.tokenClickable(state = state)
|
||||
.background(color = TangemTheme.colors.background.primary),
|
||||
) {
|
||||
TokenIcon(
|
||||
CurrencyIcon(
|
||||
state = state.iconState,
|
||||
modifier = Modifier
|
||||
.layoutId(layoutId = LayoutId.ICON)
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.tangem.common.Strings
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.utils.StringsSigns
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState.CryptoAmountState as TokenCryptoAmountState
|
||||
|
||||
@Composable
|
||||
|
|
@ -23,7 +23,7 @@ internal fun TokenCryptoAmount(
|
|||
when (state) {
|
||||
is TokenCryptoAmountState.Content -> {
|
||||
CryptoAmountText(
|
||||
amount = if (isBalanceHidden) Strings.STARS else state.text,
|
||||
amount = if (isBalanceHidden) StringsSigns.STARS else state.text,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.tangem.common.Strings
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.utils.StringsSigns
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState.FiatAmountState as TokenFiatAmountState
|
||||
|
||||
@Composable
|
||||
|
|
@ -17,7 +17,7 @@ internal fun TokenFiatAmount(state: TokenFiatAmountState?, isBalanceHidden: Bool
|
|||
when (state) {
|
||||
is TokenFiatAmountState.Content -> {
|
||||
FiatAmountText(
|
||||
text = if (isBalanceHidden) Strings.STARS else state.text,
|
||||
text = if (isBalanceHidden) StringsSigns.STARS else state.text,
|
||||
modifier,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import com.tangem.core.ui.components.SpacerW6
|
|||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState.CryptoPriceState as TokenPriceChangeState
|
||||
|
||||
@Composable
|
||||
|
|
@ -33,7 +33,7 @@ internal fun TokenPrice(state: TokenPriceChangeState?, modifier: Modifier = Modi
|
|||
)
|
||||
}
|
||||
is TokenPriceChangeState.Unknown -> {
|
||||
PriceText(text = TokenItemState.UNKNOWN_AMOUNT_SIGN, modifier = modifier)
|
||||
PriceText(text = DASH_SIGN, modifier = modifier)
|
||||
}
|
||||
is TokenPriceChangeState.Loading -> {
|
||||
RectangleShimmer(modifier = modifier.placeholderSize(), radius = TangemTheme.dimens.radius4)
|
||||
|
|
@ -104,7 +104,7 @@ private fun PriceChangeIcon(type: PriceChangeType) {
|
|||
private fun PriceChangeText(type: PriceChangeType?, text: String?, modifier: Modifier = Modifier) {
|
||||
AnimatedContent(targetState = text, modifier = modifier, label = "Update the price change's text") { animatedText ->
|
||||
Text(
|
||||
text = animatedText ?: TokenItemState.UNKNOWN_AMOUNT_SIGN,
|
||||
text = animatedText ?: DASH_SIGN,
|
||||
color = when (type) {
|
||||
PriceChangeType.UP -> TangemTheme.colors.text.accent
|
||||
PriceChangeType.DOWN -> TangemTheme.colors.text.warning
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.common.preview
|
||||
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import com.tangem.core.ui.components.currency.tokenicon.TokenIconState
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
|
@ -12,13 +11,12 @@ import com.tangem.feature.wallet.impl.R
|
|||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData.topBarConfig
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.*
|
||||
import com.tangem.features.managetokens.navigation.ExpandableState
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
internal object WalletScreenPreviewData {
|
||||
private val tokenItemState = TokenItemState.Content(
|
||||
id = "1",
|
||||
iconState = TokenIconState.Locked,
|
||||
iconState = CurrencyIconState.Locked,
|
||||
titleState = TokenItemState.TitleState.Content(text = "Bitcoin"),
|
||||
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "12 368,14 \$"),
|
||||
cryptoAmountState = TokenItemState.CryptoAmountState.Content(text = "0,35853044 BTC"),
|
||||
|
|
@ -58,7 +56,7 @@ internal object WalletScreenPreviewData {
|
|||
WalletTokensListState.TokensListItemState.Token(
|
||||
state = TokenItemState.Unreachable(
|
||||
id = "3",
|
||||
iconState = TokenIconState.Locked,
|
||||
iconState = CurrencyIconState.Locked,
|
||||
titleState = TokenItemState.TitleState.Content(text = "Polygon"),
|
||||
onItemClick = {},
|
||||
onItemLongClick = {},
|
||||
|
|
@ -151,7 +149,6 @@ internal object WalletScreenPreviewData {
|
|||
|
||||
internal val walletScreenState = WalletScreenState(
|
||||
onBackClick = {},
|
||||
manageTokensExpandableState = mutableStateOf(ExpandableState.COLLAPSED),
|
||||
topBarConfig = topBarConfig,
|
||||
selectedWalletIndex = 0,
|
||||
wallets = persistentListOf(
|
||||
|
|
@ -161,6 +158,5 @@ internal object WalletScreenPreviewData {
|
|||
onWalletChange = {},
|
||||
event = consumedEvent(),
|
||||
isHidingMode = false,
|
||||
manageTokenRedesignToggle = false,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.feature.wallet.presentation.common.state
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.currency.tokenicon.TokenIconState
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
|
||||
/** Token item state */
|
||||
|
|
@ -10,7 +10,7 @@ internal sealed class TokenItemState {
|
|||
|
||||
abstract val id: String
|
||||
|
||||
abstract val iconState: TokenIconState
|
||||
abstract val iconState: CurrencyIconState
|
||||
|
||||
abstract val titleState: TitleState
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ internal sealed class TokenItemState {
|
|||
/** Loading token state */
|
||||
data class Loading(
|
||||
override val id: String,
|
||||
override val iconState: TokenIconState,
|
||||
override val iconState: CurrencyIconState,
|
||||
override val titleState: TitleState.Content,
|
||||
) : TokenItemState() {
|
||||
override val fiatAmountState: FiatAmountState = FiatAmountState.Loading
|
||||
|
|
@ -33,7 +33,7 @@ internal sealed class TokenItemState {
|
|||
|
||||
/** Locked token state */
|
||||
data class Locked(override val id: String) : TokenItemState() {
|
||||
override val iconState: TokenIconState = TokenIconState.Locked
|
||||
override val iconState: CurrencyIconState = CurrencyIconState.Locked
|
||||
override val titleState: TitleState = TitleState.Locked
|
||||
override val fiatAmountState: FiatAmountState = FiatAmountState.Locked
|
||||
override val cryptoAmountState: CryptoAmountState = CryptoAmountState.Locked
|
||||
|
|
@ -51,7 +51,7 @@ internal sealed class TokenItemState {
|
|||
*/
|
||||
data class Content(
|
||||
override val id: String,
|
||||
override val iconState: TokenIconState,
|
||||
override val iconState: CurrencyIconState,
|
||||
override val titleState: TitleState,
|
||||
override val fiatAmountState: FiatAmountState,
|
||||
override val cryptoAmountState: CryptoAmountState.Content,
|
||||
|
|
@ -69,7 +69,7 @@ internal sealed class TokenItemState {
|
|||
*/
|
||||
data class Draggable(
|
||||
override val id: String,
|
||||
override val iconState: TokenIconState,
|
||||
override val iconState: CurrencyIconState,
|
||||
override val titleState: TitleState,
|
||||
override val cryptoAmountState: CryptoAmountState,
|
||||
) : TokenItemState() {
|
||||
|
|
@ -88,7 +88,7 @@ internal sealed class TokenItemState {
|
|||
*/
|
||||
data class Unreachable(
|
||||
override val id: String,
|
||||
override val iconState: TokenIconState,
|
||||
override val iconState: CurrencyIconState,
|
||||
override val titleState: TitleState,
|
||||
val onItemClick: () -> Unit,
|
||||
val onItemLongClick: () -> Unit,
|
||||
|
|
@ -108,7 +108,7 @@ internal sealed class TokenItemState {
|
|||
*/
|
||||
data class NoAddress(
|
||||
override val id: String,
|
||||
override val iconState: TokenIconState,
|
||||
override val iconState: CurrencyIconState,
|
||||
override val titleState: TitleState,
|
||||
val onItemLongClick: () -> Unit,
|
||||
) : TokenItemState() {
|
||||
|
|
@ -161,8 +161,4 @@ internal sealed class TokenItemState {
|
|||
|
||||
object Locked : CryptoPriceState()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val UNKNOWN_AMOUNT_SIGN = "—"
|
||||
}
|
||||
}
|
||||
|
|
@ -16,11 +16,10 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -28,6 +27,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
|||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.BottomFade
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.SecondaryButton
|
||||
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
||||
|
|
@ -36,6 +36,7 @@ import com.tangem.core.ui.event.EventEffect
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.utils.WindowInsetsZero
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.common.component.DraggableNetworkGroupItem
|
||||
|
|
@ -57,8 +58,12 @@ internal fun OrganizeTokensScreen(state: OrganizeTokensState, modifier: Modifier
|
|||
Scaffold(
|
||||
modifier = modifier,
|
||||
topBar = {
|
||||
TopBar(state.header, tokensListState)
|
||||
TopBar(
|
||||
config = state.header,
|
||||
tokensListState = tokensListState,
|
||||
)
|
||||
},
|
||||
contentWindowInsets = WindowInsetsZero,
|
||||
content = { paddingValues ->
|
||||
TokenList(
|
||||
modifier = Modifier
|
||||
|
|
@ -72,7 +77,9 @@ internal fun OrganizeTokensScreen(state: OrganizeTokensState, modifier: Modifier
|
|||
},
|
||||
floatingActionButtonPosition = FabPosition.Center,
|
||||
floatingActionButton = {
|
||||
Actions(state.actions)
|
||||
Box(modifier = Modifier.navigationBarsPadding()) {
|
||||
Actions(state.actions)
|
||||
}
|
||||
},
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
)
|
||||
|
|
@ -104,9 +111,11 @@ private fun TokenList(
|
|||
onDragEnd = onDragEnd,
|
||||
)
|
||||
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
||||
val listContentPadding = PaddingValues(
|
||||
top = TangemTheme.dimens.spacing4,
|
||||
bottom = TangemTheme.dimens.spacing92,
|
||||
bottom = TangemTheme.dimens.spacing92 + bottomBarHeight,
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
)
|
||||
|
|
@ -140,7 +149,7 @@ private fun TokenList(
|
|||
}
|
||||
}
|
||||
|
||||
BottomGradient(modifier = Modifier.align(Alignment.BottomCenter))
|
||||
BottomFade(modifier = Modifier.align(Alignment.BottomCenter))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -191,23 +200,6 @@ private fun LazyItemScope.DraggableItem(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomGradient(modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(TangemTheme.dimens.size116)
|
||||
.background(
|
||||
brush = Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
Color.Transparent,
|
||||
TangemTheme.colors.background.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TopBar(
|
||||
config: OrganizeTokensState.HeaderConfig,
|
||||
|
|
@ -228,6 +220,7 @@ private fun TopBar(
|
|||
modifier = modifier
|
||||
.shadow(elevation)
|
||||
.background(TangemTheme.colors.background.secondary)
|
||||
.statusBarsPadding()
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -13,9 +13,7 @@ import com.tangem.domain.tokens.GetTokenListUseCase
|
|||
import com.tangem.domain.tokens.ToggleTokenListGroupingUseCase
|
||||
import com.tangem.domain.tokens.ToggleTokenListSortingUseCase
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.domain.tokens.model.TotalFiatBalance
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.wallet.featuretoggle.WalletFeatureToggles
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.analytics.PortfolioOrganizeTokensAnalyticsEvent
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState
|
||||
|
|
@ -42,7 +40,6 @@ internal class OrganizeTokensViewModel @Inject constructor(
|
|||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||
private val walletFeatureToggles: WalletFeatureToggles,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel(), DefaultLifecycleObserver, OrganizeTokensIntents {
|
||||
|
|
@ -173,34 +170,21 @@ internal class OrganizeTokensViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun getTokenList(): TokenList? {
|
||||
return if (walletFeatureToggles.isTokenListLceFlowEnabled) {
|
||||
val tokenList = getTokenListUseCase.launchLce(userWalletId)
|
||||
.transform { maybeTokenList ->
|
||||
val tokenList = maybeTokenList.getOrElse(
|
||||
ifLoading = { return@transform },
|
||||
ifError = { error ->
|
||||
stateHolder.updateStateWithError(error)
|
||||
val tokenList = getTokenListUseCase.launch(userWalletId)
|
||||
.transform { maybeTokenList ->
|
||||
val tokenList = maybeTokenList.getOrElse(
|
||||
ifLoading = { return@transform },
|
||||
ifError = { error ->
|
||||
stateHolder.updateStateWithError(error)
|
||||
|
||||
return@transform
|
||||
},
|
||||
)
|
||||
return@transform
|
||||
},
|
||||
)
|
||||
|
||||
emit(tokenList)
|
||||
}
|
||||
|
||||
tokenList.firstOrNull()
|
||||
} else {
|
||||
val maybeTokenList = getTokenListUseCase.launch(userWalletId)
|
||||
.first { maybeTokenList ->
|
||||
maybeTokenList.getOrNull()?.totalFiatBalance !is TotalFiatBalance.Loading
|
||||
}
|
||||
|
||||
maybeTokenList.getOrElse { error ->
|
||||
stateHolder.updateStateWithError(error)
|
||||
|
||||
null
|
||||
emit(tokenList)
|
||||
}
|
||||
}
|
||||
|
||||
return tokenList.firstOrNull()
|
||||
}
|
||||
|
||||
private fun bootstrapDragAndDropUpdates() {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items
|
||||
|
||||
import com.tangem.core.ui.components.currency.tokenicon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
package com.tangem.feature.wallet.presentation.router
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
|
|
@ -16,27 +13,26 @@ import androidx.navigation.compose.NavHost
|
|||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.core.navigation.ReduxNavController
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.onboarding.navigation.OnboardingRouter
|
||||
import com.tangem.feature.wallet.presentation.WalletFragment
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensScreen
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensViewModel
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletViewModel
|
||||
import com.tangem.features.details.DetailsEntryPoint
|
||||
import com.tangem.features.managetokens.navigation.ExpandableState
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensUi
|
||||
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
||||
import com.tangem.features.markets.component.MarketsListComponent
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
/** Default implementation of wallet feature router */
|
||||
internal class DefaultWalletRouter(
|
||||
private val reduxNavController: ReduxNavController,
|
||||
private val router: AppRouter,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
) : InnerWalletRouter {
|
||||
|
||||
private var navController: NavHostController by Delegates.notNull()
|
||||
|
|
@ -45,7 +41,7 @@ internal class DefaultWalletRouter(
|
|||
override fun getEntryFragment(): Fragment = WalletFragment.create()
|
||||
|
||||
@Composable
|
||||
override fun Initialize(onFinish: () -> Unit, manageTokensUi: ManageTokensUi) {
|
||||
override fun Initialize(onFinish: () -> Unit, marketsListComponent: MarketsListComponent?) {
|
||||
this.onFinish = onFinish
|
||||
|
||||
NavHost(
|
||||
|
|
@ -58,20 +54,9 @@ internal class DefaultWalletRouter(
|
|||
subscribeToLifecycle(LocalLifecycleOwner.current)
|
||||
}
|
||||
|
||||
var bottomSheetHeaderHeight by remember { mutableStateOf(0.dp) }
|
||||
|
||||
WalletScreen(
|
||||
state = viewModel.uiState.collectAsStateWithLifecycle().value,
|
||||
bottomSheetHeaderHeightProvider = { bottomSheetHeaderHeight },
|
||||
bottomSheetContent = {
|
||||
val state = remember { mutableStateOf(ExpandableState.COLLAPSED) }
|
||||
// Manage Tokens
|
||||
manageTokensUi.Content(
|
||||
onHeaderSizeChange = { bottomSheetHeaderHeight = it },
|
||||
state = state,
|
||||
)
|
||||
viewModel.setExpandableState(state)
|
||||
},
|
||||
marketsListComponent = marketsListComponent,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -87,7 +72,6 @@ internal class DefaultWalletRouter(
|
|||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
OrganizeTokensScreen(
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
state = uiState,
|
||||
)
|
||||
}
|
||||
|
|
@ -95,7 +79,7 @@ internal class DefaultWalletRouter(
|
|||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
override fun popBackStack(screen: AppScreen?) {
|
||||
override fun popBackStack() {
|
||||
/*
|
||||
* It's hack that avoid issue with closing the wallet screen.
|
||||
* We are using NavGraph only inside feature so first backstack's element is entry of NavGraph and
|
||||
|
|
@ -103,11 +87,7 @@ internal class DefaultWalletRouter(
|
|||
* If backstack contains only NavGraph entry and wallet screen entry then we close the wallet fragment.
|
||||
*/
|
||||
if (navController.currentBackStack.value.size == BACKSTACK_ENTRY_COUNT_TO_CLOSE_WALLET_SCREEN) {
|
||||
if (screen != null) {
|
||||
reduxNavController.navigate(action = NavigationAction.PopBackTo(screen))
|
||||
} else {
|
||||
onFinish.invoke()
|
||||
}
|
||||
onFinish.invoke()
|
||||
} else {
|
||||
navController.popBackStack()
|
||||
}
|
||||
|
|
@ -118,64 +98,53 @@ internal class DefaultWalletRouter(
|
|||
}
|
||||
|
||||
override fun openDetailsScreen(selectedWalletId: UserWalletId) {
|
||||
reduxNavController.navigate(
|
||||
action = NavigationAction.NavigateTo(
|
||||
screen = AppScreen.Details,
|
||||
bundle = bundleOf(
|
||||
DetailsEntryPoint.USER_WALLET_ID_KEY to selectedWalletId.stringValue,
|
||||
),
|
||||
router.push(
|
||||
AppRoute.Details(
|
||||
userWalletId = selectedWalletId,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun openOnboardingScreen() {
|
||||
reduxNavController.navigate(
|
||||
action = NavigationAction.NavigateTo(
|
||||
screen = AppScreen.OnboardingWallet,
|
||||
bundle = bundleOf(OnboardingRouter.CAN_SKIP_BACKUP to false),
|
||||
),
|
||||
router.push(
|
||||
AppRoute.OnboardingWallet(canSkipBackup = false),
|
||||
)
|
||||
}
|
||||
|
||||
override fun openUrl(url: String) {
|
||||
reduxNavController.navigate(action = NavigationAction.OpenUrl(url))
|
||||
urlOpener.openUrl(url)
|
||||
}
|
||||
|
||||
override fun openTokenDetails(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus) {
|
||||
val networkAddress = currencyStatus.value.networkAddress
|
||||
if (networkAddress != null && networkAddress.defaultAddress.value.isNotEmpty()) {
|
||||
reduxNavController.navigate(
|
||||
action = NavigationAction.NavigateTo(
|
||||
screen = AppScreen.WalletDetails,
|
||||
bundle = bundleOf(
|
||||
TokenDetailsRouter.USER_WALLET_ID_KEY to userWalletId.stringValue,
|
||||
TokenDetailsRouter.CRYPTO_CURRENCY_KEY to currencyStatus.currency,
|
||||
),
|
||||
router.push(
|
||||
AppRoute.CurrencyDetails(
|
||||
userWalletId = userWalletId,
|
||||
currency = currencyStatus.currency,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun openStoriesScreen() {
|
||||
reduxNavController.navigate(action = NavigationAction.NavigateTo(screen = AppScreen.Home))
|
||||
router.push(AppRoute.Home)
|
||||
}
|
||||
|
||||
override fun openSaveUserWalletScreen() {
|
||||
reduxNavController.navigate(action = NavigationAction.NavigateTo(AppScreen.SaveWallet))
|
||||
router.push(AppRoute.SaveWallet)
|
||||
}
|
||||
|
||||
override fun isWalletLastScreen(): Boolean = reduxNavController.getBackStack().lastOrNull() == AppScreen.Wallet
|
||||
override fun isWalletLastScreen(): Boolean {
|
||||
return router.stack.lastOrNull() is AppRoute.Wallet
|
||||
}
|
||||
|
||||
override fun openManageTokensScreen() {
|
||||
reduxNavController.navigate(action = NavigationAction.NavigateTo(AppScreen.ManageTokens))
|
||||
router.push(AppRoute.ManageTokens)
|
||||
}
|
||||
|
||||
override fun openScanFailedDialog() {
|
||||
reduxNavController.navigate(
|
||||
action = NavigationAction.OpenDialog(
|
||||
StateDialog.ScanFailsDialog(StateDialog.ScanFailsSource.MAIN),
|
||||
),
|
||||
)
|
||||
override fun openScanFailedDialog(onTryAgain: () -> Unit) {
|
||||
reduxStateHolder.dispatchDialogShow(StateDialog.ScanFailsDialog(StateDialog.ScanFailsSource.MAIN, onTryAgain))
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
|
|
|||
|
|
@ -2,10 +2,9 @@ package com.tangem.feature.wallet.presentation.router
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensUi
|
||||
import com.tangem.features.markets.component.MarketsListComponent
|
||||
import com.tangem.features.wallet.navigation.WalletRouter
|
||||
|
||||
/**
|
||||
|
|
@ -24,12 +23,11 @@ internal interface InnerWalletRouter : WalletRouter {
|
|||
*
|
||||
* @param onFinish finish activity callback
|
||||
*/
|
||||
@Suppress("TopLevelComposableFunctions")
|
||||
@Composable
|
||||
fun Initialize(onFinish: () -> Unit, manageTokensUi: ManageTokensUi)
|
||||
fun Initialize(onFinish: () -> Unit, marketsListComponent: MarketsListComponent?)
|
||||
|
||||
/** Pop back stack */
|
||||
fun popBackStack(screen: AppScreen? = null)
|
||||
fun popBackStack()
|
||||
|
||||
/** Open organize tokens screen */
|
||||
fun openOrganizeTokensScreen(userWalletId: UserWalletId)
|
||||
|
|
@ -59,5 +57,5 @@ internal interface InnerWalletRouter : WalletRouter {
|
|||
fun openManageTokensScreen()
|
||||
|
||||
/** Open scan failed dialog */
|
||||
fun openScanFailedDialog()
|
||||
fun openScanFailedDialog(onTryAgain: () -> Unit)
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.domain
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.common.CardTypesResolver
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.promo.PromoBanner
|
||||
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
|
||||
|
|
@ -23,7 +23,6 @@ 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.flow
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.count
|
||||
|
|
@ -47,11 +46,11 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
|
||||
val promoFlow = flow { emit(promoRepository.getOkxPromoBanner()) }
|
||||
return combine(
|
||||
flow = getTokenListUseCase.launch(userWallet.walletId).conflate(),
|
||||
flow2 = isReadyToShowRateAppUseCase().conflate(),
|
||||
flow3 = isNeedToBackupUseCase(userWallet.walletId).conflate(),
|
||||
flow4 = shouldShowSwapPromoWalletUseCase().conflate(),
|
||||
flow5 = promoFlow.conflate(),
|
||||
flow = getTokenListUseCase.launch(userWallet.walletId),
|
||||
flow2 = isReadyToShowRateAppUseCase(),
|
||||
flow3 = isNeedToBackupUseCase(userWallet.walletId),
|
||||
flow4 = shouldShowSwapPromoWalletUseCase(),
|
||||
flow5 = promoFlow,
|
||||
) { maybeTokenList, isReadyToShowRating, isNeedToBackup, shouldShowPromo, promoBanner ->
|
||||
|
||||
readyForRateAppNotification = true
|
||||
|
|
@ -113,7 +112,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
|
||||
private fun MutableList<WalletNotification>.addInformationalNotifications(
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
maybeTokenList: Either<TokenListError, TokenList>,
|
||||
maybeTokenList: Lce<TokenListError, TokenList>,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
addIf(
|
||||
|
|
@ -125,7 +124,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addMissingAddressesNotification(
|
||||
maybeTokenList: Either<TokenListError, TokenList>,
|
||||
maybeTokenList: Lce<TokenListError, TokenList>,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
val currencies = maybeTokenList.getMissingAddressCurrencies()
|
||||
|
|
@ -141,26 +140,23 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun Either<TokenListError, TokenList>.getMissingAddressCurrencies(): List<CryptoCurrency> {
|
||||
return fold(
|
||||
ifLeft = { emptyList() },
|
||||
ifRight = { tokenList ->
|
||||
val currencies = when (tokenList) {
|
||||
is TokenList.GroupedByNetwork -> tokenList.groups.flatMap(NetworkGroup::currencies)
|
||||
is TokenList.Ungrouped -> tokenList.currencies
|
||||
is TokenList.Empty -> emptyList()
|
||||
}
|
||||
private fun Lce<TokenListError, TokenList>.getMissingAddressCurrencies(): List<CryptoCurrency> {
|
||||
val tokenList = getOrNull(isPartialContentAccepted = false) ?: return emptyList()
|
||||
|
||||
currencies
|
||||
.filter { it.value is CryptoCurrencyStatus.MissedDerivation }
|
||||
.map(CryptoCurrencyStatus::currency)
|
||||
},
|
||||
)
|
||||
val currencies = when (tokenList) {
|
||||
is TokenList.GroupedByNetwork -> tokenList.groups.flatMap(NetworkGroup::currencies)
|
||||
is TokenList.Ungrouped -> tokenList.currencies
|
||||
is TokenList.Empty -> emptyList()
|
||||
}
|
||||
|
||||
return currencies
|
||||
.filter { it.value is CryptoCurrencyStatus.MissedDerivation }
|
||||
.map(CryptoCurrencyStatus::currency)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addWarningNotifications(
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
tokenList: Either<TokenListError, TokenList>,
|
||||
tokenList: Lce<TokenListError, TokenList>,
|
||||
isNeedToBackup: Boolean,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
|
|
@ -182,19 +178,16 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun Either<TokenListError, TokenList>.hasUnreachableNetworks(): Boolean {
|
||||
return fold(
|
||||
ifLeft = { false },
|
||||
ifRight = { tokenList ->
|
||||
val currencies = when (tokenList) {
|
||||
is TokenList.GroupedByNetwork -> tokenList.groups.flatMap(NetworkGroup::currencies)
|
||||
is TokenList.Ungrouped -> tokenList.currencies
|
||||
is TokenList.Empty -> emptyList()
|
||||
}
|
||||
private fun Lce<TokenListError, TokenList>.hasUnreachableNetworks(): Boolean {
|
||||
val tokenList = getOrNull(isPartialContentAccepted = false) ?: return false
|
||||
|
||||
currencies.any { it.value is CryptoCurrencyStatus.Unreachable }
|
||||
},
|
||||
)
|
||||
val currencies = when (tokenList) {
|
||||
is TokenList.GroupedByNetwork -> tokenList.groups.flatMap(NetworkGroup::currencies)
|
||||
is TokenList.Ungrouped -> tokenList.currencies
|
||||
is TokenList.Empty -> emptyList()
|
||||
}
|
||||
|
||||
return currencies.any { it.value is CryptoCurrencyStatus.Unreachable }
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addRateTheAppNotification(
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
|
|||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.featuretoggle.WalletFeatureToggles
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory
|
||||
|
|
@ -28,7 +27,6 @@ internal class MultiWalletContentLoader(
|
|||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
|
||||
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
|
||||
private val walletFeatureToggles: WalletFeatureToggles,
|
||||
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
||||
|
|
@ -42,7 +40,6 @@ internal class MultiWalletContentLoader(
|
|||
walletWithFundsChecker = walletWithFundsChecker,
|
||||
getTokenListUseCase = getTokenListUseCase,
|
||||
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
|
||||
walletFeatureToggles = walletFeatureToggles,
|
||||
applyTokenListSortingUseCase = applyTokenListSortingUseCase,
|
||||
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
|
|||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.featuretoggle.WalletFeatureToggles
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory
|
||||
|
|
@ -26,7 +25,6 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
|
|||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
|
||||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
private val walletFeatureToggles: WalletFeatureToggles,
|
||||
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
|
||||
) {
|
||||
|
||||
|
|
@ -42,7 +40,6 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
|
|||
getMultiWalletWarningsFactory = getMultiWalletWarningsFactory,
|
||||
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
|
||||
applyTokenListSortingUseCase = applyTokenListSortingUseCase,
|
||||
walletFeatureToggles = walletFeatureToggles,
|
||||
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state
|
||||
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
|
@ -11,8 +10,6 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTopBarCon
|
|||
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
|
||||
import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles
|
||||
import com.tangem.features.managetokens.navigation.ExpandableState
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -27,9 +24,7 @@ import javax.inject.Singleton
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Singleton
|
||||
internal class WalletStateController @Inject constructor(
|
||||
private val manageTokensFeatureToggles: ManageTokensFeatureToggles,
|
||||
) {
|
||||
internal class WalletStateController @Inject constructor() {
|
||||
|
||||
val uiState: StateFlow<WalletScreenState> get() = mutableUiState
|
||||
|
||||
|
|
@ -89,8 +84,6 @@ internal class WalletStateController @Inject constructor(
|
|||
onWalletChange = {},
|
||||
event = consumedEvent(),
|
||||
isHidingMode = false,
|
||||
manageTokenRedesignToggle = manageTokensFeatureToggles.isRedesignedScreenEnabled,
|
||||
manageTokensExpandableState = mutableStateOf(ExpandableState.EXPANDED),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.model
|
||||
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
|
||||
data class PushNotificationsBottomSheetConfig(
|
||||
val isFirstTimeAsking: Boolean,
|
||||
val onRequest: () -> Unit,
|
||||
val onAllow: () -> Unit,
|
||||
val onDeny: () -> Unit,
|
||||
val openSettings: () -> Unit,
|
||||
) : TangemBottomSheetConfigContent
|
||||
|
|
@ -2,9 +2,10 @@ package com.tangem.feature.wallet.presentation.wallet.state.model
|
|||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.common.Strings
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.StringsSigns
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
|
||||
/** Wallet card state */
|
||||
@Immutable
|
||||
|
|
@ -123,7 +124,7 @@ internal sealed interface WalletCardState {
|
|||
}
|
||||
|
||||
companion object {
|
||||
val HIDDEN_BALANCE_TEXT by lazy { TextReference.Str(value = Strings.STARS) }
|
||||
val EMPTY_BALANCE_TEXT by lazy { TextReference.Str(value = "—") }
|
||||
val HIDDEN_BALANCE_TEXT by lazy { TextReference.Str(value = StringsSigns.STARS) }
|
||||
val EMPTY_BALANCE_TEXT by lazy { TextReference.Str(value = DASH_SIGN) }
|
||||
}
|
||||
}
|
||||
|
|
@ -89,6 +89,19 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
|
|||
),
|
||||
)
|
||||
|
||||
data class Stake(
|
||||
override val enabled: Boolean,
|
||||
override val dimContent: Boolean,
|
||||
override val onClick: () -> Unit,
|
||||
) : WalletManageButton(
|
||||
config = ActionButtonConfig(
|
||||
text = TextReference.Res(id = R.string.common_stake),
|
||||
iconResId = R.drawable.ic_arrow_down_24, // TODO staking
|
||||
onClick = onClick,
|
||||
dimContent = dimContent,
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* Sell
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,18 +1,14 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.model
|
||||
|
||||
import androidx.compose.runtime.MutableState
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.features.managetokens.navigation.ExpandableState
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal data class WalletScreenState(
|
||||
val onBackClick: () -> Unit,
|
||||
val manageTokensExpandableState: MutableState<ExpandableState>,
|
||||
val topBarConfig: WalletTopBarConfig,
|
||||
val selectedWalletIndex: Int,
|
||||
val wallets: ImmutableList<WalletState>,
|
||||
val onWalletChange: (Int) -> Unit,
|
||||
val event: StateEvent<WalletEvent>,
|
||||
val isHidingMode: Boolean,
|
||||
val manageTokenRedesignToggle: Boolean,
|
||||
)
|
||||
|
|
@ -19,24 +19,25 @@ internal class SetBalancesAndLimitsTransformer(
|
|||
private val userWallet: UserWallet,
|
||||
private val maybeVisaCurrency: Either<Throwable, VisaCurrency>,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
) : WalletStateTransformer(userWallet.walletId) {
|
||||
) : TypedWalletStateTransformer<WalletState.Visa.Content>(
|
||||
userWalletId = userWallet.walletId,
|
||||
targetStateClass = WalletState.Visa.Content::class,
|
||||
) {
|
||||
|
||||
override fun transform(prevState: WalletState): WalletState {
|
||||
return prevState.transformWhenInState<WalletState.Visa.Content> { state ->
|
||||
val visaCurrency = maybeVisaCurrency.getOrElse {
|
||||
return state.copy(
|
||||
walletCardState = getErrorWalletCardState(state.walletCardState),
|
||||
depositButtonState = state.depositButtonState.copy(isEnabled = false),
|
||||
balancesAndLimitBlockState = BalancesAndLimitsBlockState.Error,
|
||||
)
|
||||
}
|
||||
|
||||
state.copy(
|
||||
walletCardState = getContentWalletCardState(state.walletCardState, visaCurrency),
|
||||
depositButtonState = state.depositButtonState.copy(isEnabled = true),
|
||||
balancesAndLimitBlockState = getContentBlockState(visaCurrency),
|
||||
override fun transformTyped(prevState: WalletState.Visa.Content): WalletState {
|
||||
val visaCurrency = maybeVisaCurrency.getOrElse {
|
||||
return prevState.copy(
|
||||
walletCardState = getErrorWalletCardState(prevState.walletCardState),
|
||||
depositButtonState = prevState.depositButtonState.copy(isEnabled = false),
|
||||
balancesAndLimitBlockState = BalancesAndLimitsBlockState.Error,
|
||||
)
|
||||
}
|
||||
|
||||
return prevState.copy(
|
||||
walletCardState = getContentWalletCardState(prevState.walletCardState, visaCurrency),
|
||||
depositButtonState = prevState.depositButtonState.copy(isEnabled = true),
|
||||
balancesAndLimitBlockState = getContentBlockState(visaCurrency),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getContentBlockState(visaCurrency: VisaCurrency) = BalancesAndLimitsBlockState.Content(
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ internal class SetRefreshStateTransformer(
|
|||
is WalletManageButton.Send -> button.copy(enabled = isButtonsEnabled)
|
||||
is WalletManageButton.Sell -> button.copy(enabled = isButtonsEnabled)
|
||||
is WalletManageButton.Receive -> button
|
||||
is WalletManageButton.Stake -> null
|
||||
is WalletManageButton.Swap -> null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.domain.wallets.models.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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@ import com.tangem.domain.wallets.models.UserWalletId
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import timber.log.Timber
|
||||
|
||||
internal abstract class WalletStateTransformer(
|
||||
protected val userWalletId: UserWalletId,
|
||||
|
|
@ -12,7 +11,7 @@ internal abstract class WalletStateTransformer(
|
|||
|
||||
abstract fun transform(prevState: WalletState): WalletState
|
||||
|
||||
override fun transform(prevState: WalletScreenState): WalletScreenState {
|
||||
final override fun transform(prevState: WalletScreenState): WalletScreenState {
|
||||
return prevState.copy(
|
||||
wallets = prevState.wallets
|
||||
.map { state ->
|
||||
|
|
@ -21,13 +20,4 @@ internal abstract class WalletStateTransformer(
|
|||
.toImmutableList(),
|
||||
)
|
||||
}
|
||||
|
||||
protected inline fun <reified S : WalletState> WalletState.transformWhenInState(
|
||||
transform: (state: S) -> WalletState,
|
||||
): WalletState = if (this is S) {
|
||||
transform(this)
|
||||
} else {
|
||||
Timber.w("Impossible to transform ${this::class.simpleName} because current is ${S::class.simpleName}")
|
||||
this
|
||||
}
|
||||
}
|
||||
|
|
@ -59,6 +59,11 @@ internal class MultiWalletCurrencyActionsConverter(
|
|||
icon = R.drawable.ic_arrow_down_24
|
||||
action = { clickIntents.onReceiveClick(cryptoCurrencyStatus) }
|
||||
}
|
||||
is TokenActionsState.ActionState.Stake -> {
|
||||
title = resourceReference(R.string.common_stake)
|
||||
icon = R.drawable.ic_arrow_down_24 // TODO staking replace icon
|
||||
action = { clickIntents.onStakeClick(cryptoCurrencyStatus) }
|
||||
}
|
||||
is TokenActionsState.ActionState.Sell -> {
|
||||
title = resourceReference(R.string.common_sell)
|
||||
icon = R.drawable.ic_currency_24
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
|
||||
|
||||
import com.tangem.core.ui.components.currency.tokenicon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.components.marketprice.utils.PriceChangeConverter
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.staking.model.YieldBalance
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class TokenItemStateConverter(
|
||||
|
|
@ -61,13 +64,16 @@ internal class TokenItemStateConverter(
|
|||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.getFormattedAmount(): String {
|
||||
val amount = value.amount ?: return TokenItemState.UNKNOWN_AMOUNT_SIGN
|
||||
val yieldBalance = (value.yieldBalance as? YieldBalance.Data)?.getTotalStakingBalance().orZero()
|
||||
val amount = value.amount?.plus(yieldBalance) ?: return DASH_SIGN
|
||||
|
||||
return BigDecimalFormatter.formatCryptoAmount(amount, currency.symbol, currency.decimals)
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.getFormattedFiatAmount(): String {
|
||||
val fiatAmount = value.fiatAmount ?: return TokenItemState.UNKNOWN_AMOUNT_SIGN
|
||||
val yieldBalance = (value.yieldBalance as? YieldBalance.Data)?.getTotalStakingBalance().orZero()
|
||||
val fiatYieldBalance = value.fiatRate?.times(yieldBalance).orZero()
|
||||
val fiatAmount = value.fiatAmount?.plus(fiatYieldBalance) ?: return DASH_SIGN
|
||||
val appCurrency = appCurrencyProvider()
|
||||
|
||||
return BigDecimalFormatter.formatFiatAmount(fiatAmount, appCurrency.code, appCurrency.symbol)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import com.tangem.core.ui.utils.toTimeFormat
|
|||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import com.tangem.utils.StringsSigns.MINUS
|
||||
import com.tangem.utils.StringsSigns.PLUS
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.toBriefAddressFormat
|
||||
import com.tangem.utils.toFormattedCurrencyString
|
||||
|
|
@ -96,7 +98,7 @@ internal class TxHistoryItemStateConverter(
|
|||
private fun TxHistoryItem.getAmount(): String {
|
||||
val prefix = when (status) {
|
||||
TxHistoryItem.TransactionStatus.Failed -> ""
|
||||
else -> if (isOutgoing) "-" else "+"
|
||||
else -> if (isOutgoing) MINUS else PLUS
|
||||
}
|
||||
return prefix + amount.toFormattedCurrencyString(currency = symbol, decimals = decimals)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.tangem.domain.visa.model.VisaCurrency
|
|||
import com.tangem.domain.visa.model.VisaTxHistoryItem
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.VisaWalletIntents
|
||||
import com.tangem.utils.StringsSigns
|
||||
import com.tangem.utils.converter.Converter
|
||||
import org.joda.time.DateTimeZone
|
||||
|
||||
|
|
@ -20,7 +21,7 @@ internal class VisaTxHistoryItemStateConverter(
|
|||
override fun convert(value: VisaTxHistoryItem): TransactionState {
|
||||
val localDate = value.date.withZone(DateTimeZone.getDefault())
|
||||
val time = DateTimeFormatters.formatDate(localDate, DateTimeFormatters.timeFormatter)
|
||||
val subtitle = "$time • ${value.status.capitalize()}"
|
||||
val subtitle = "$time ${StringsSigns.DOT} ${value.status.capitalize()}"
|
||||
|
||||
return TransactionState.Content(
|
||||
txHash = value.id,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.tangem.feature.wallet.presentation.wallet.subscribers
|
|||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.core.utils.toLce
|
||||
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
|
||||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
|
|
@ -12,19 +11,16 @@ import com.tangem.domain.tokens.model.CryptoCurrency
|
|||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.domain.tokens.model.TotalFiatBalance
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.featuretoggle.WalletFeatureToggles
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
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.viewmodels.intents.WalletClickIntents
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class MultiWalletTokenListSubscriber(
|
||||
private val userWallet: UserWallet,
|
||||
private val getTokenListUseCase: GetTokenListUseCase,
|
||||
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
|
||||
private val walletFeatureToggles: WalletFeatureToggles,
|
||||
stateHolder: WalletStateController,
|
||||
clickIntents: WalletClickIntents,
|
||||
tokenListAnalyticsSender: TokenListAnalyticsSender,
|
||||
|
|
@ -42,11 +38,7 @@ internal class MultiWalletTokenListSubscriber(
|
|||
) {
|
||||
|
||||
override fun tokenListFlow(): LceFlow<TokenListError, TokenList> {
|
||||
return if (walletFeatureToggles.isTokenListLceFlowEnabled) {
|
||||
getTokenListUseCase.launchLce(userWallet.walletId)
|
||||
} else {
|
||||
getTokenListUseCase.launch(userWallet.walletId).map { it.toLce() }
|
||||
}
|
||||
return getTokenListUseCase.launch(userWallet.walletId)
|
||||
}
|
||||
|
||||
override suspend fun onTokenListReceived(maybeTokenList: Lce<TokenListError, TokenList>) {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import androidx.compose.runtime.*
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.luminance
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
|
|
@ -33,11 +34,12 @@ import androidx.compose.ui.unit.Dp
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import com.tangem.core.ui.components.BottomFade
|
||||
import com.tangem.core.ui.components.Keyboard
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.SystemBarsEffect
|
||||
import com.tangem.core.ui.components.atoms.Hand
|
||||
import com.tangem.core.ui.components.atoms.handComposableComponentHeight
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheet
|
||||
|
|
@ -50,10 +52,12 @@ import com.tangem.core.ui.event.StateEvent
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.TestTags
|
||||
import com.tangem.core.ui.utils.WindowInsetsZero
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.walletScreenState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.holder.TxHistoryStateHolder
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.PushNotificationsBottomSheet
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.TokenActionsBottomSheet
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.common.*
|
||||
|
|
@ -65,16 +69,13 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.VisaTxDe
|
|||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.balancesAndLimitsBlock
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.depositButton
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimator
|
||||
import com.tangem.features.managetokens.navigation.ExpandableState
|
||||
import com.tangem.features.markets.component.BottomSheetState
|
||||
import com.tangem.features.markets.component.MarketsListComponent
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
internal fun WalletScreen(
|
||||
state: WalletScreenState,
|
||||
bottomSheetHeaderHeightProvider: () -> Dp,
|
||||
bottomSheetContent: @Composable () -> Unit,
|
||||
) {
|
||||
internal fun WalletScreen(state: WalletScreenState, marketsListComponent: MarketsListComponent?) {
|
||||
BackHandler(onBack = state.onBackClick)
|
||||
|
||||
// It means that screen is still initializing
|
||||
|
|
@ -97,8 +98,7 @@ internal fun WalletScreen(
|
|||
snackbarHostState = snackbarHostState,
|
||||
isAutoScroll = isAutoScroll,
|
||||
onAutoScrollReset = { isAutoScroll.value = false },
|
||||
bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider,
|
||||
bottomSheetContent = bottomSheetContent,
|
||||
marketsListComponent = marketsListComponent,
|
||||
alertConfig = alertConfig,
|
||||
)
|
||||
|
||||
|
|
@ -112,17 +112,16 @@ internal fun WalletScreen(
|
|||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "LongParameterList")
|
||||
@Suppress("LongMethod", "LongParameterList", "CyclomaticComplexMethod")
|
||||
@Composable
|
||||
private fun WalletContent(
|
||||
state: WalletScreenState,
|
||||
walletsListState: LazyListState,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
isAutoScroll: State<Boolean>,
|
||||
bottomSheetHeaderHeightProvider: () -> Dp,
|
||||
onAutoScrollReset: () -> Unit,
|
||||
bottomSheetContent: @Composable () -> Unit,
|
||||
marketsListComponent: MarketsListComponent?,
|
||||
alertConfig: WalletAlertState?,
|
||||
onAutoScrollReset: () -> Unit,
|
||||
) {
|
||||
var selectedWalletIndex by remember(state.selectedWalletIndex) { mutableIntStateOf(state.selectedWalletIndex) }
|
||||
val selectedWallet = state.wallets.getOrElse(selectedWalletIndex) { state.wallets[state.selectedWalletIndex] }
|
||||
|
|
@ -144,17 +143,20 @@ private fun WalletContent(
|
|||
.padding(top = betweenItemsPadding)
|
||||
.padding(horizontal = horizontalPadding)
|
||||
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.testTag(TestTags.WALLET_SCREEN),
|
||||
contentPadding = PaddingValues(
|
||||
bottom = TangemTheme.dimens.spacing92,
|
||||
bottom = TangemTheme.dimens.spacing92 + bottomBarHeight,
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
item(
|
||||
key = state.wallets.map { it.walletCardState.id },
|
||||
// !!! Type of the key should be saveable via Bundle on Android !!!
|
||||
key = state.wallets.map { it.walletCardState.id.stringValue },
|
||||
contentType = state.wallets.map { it.walletCardState.id },
|
||||
) {
|
||||
WalletsList(
|
||||
|
|
@ -202,17 +204,7 @@ private fun WalletContent(
|
|||
organizeTokens(state = selectedWallet, itemModifier = itemModifier)
|
||||
}
|
||||
|
||||
val bottomSheetConfig = selectedWallet.bottomSheetConfig
|
||||
if (bottomSheetConfig != null) {
|
||||
when (bottomSheetConfig.content) {
|
||||
is WalletBottomSheetConfig -> WalletBottomSheet(config = bottomSheetConfig)
|
||||
is TokenReceiveBottomSheetConfig -> TokenReceiveBottomSheet(config = bottomSheetConfig)
|
||||
is ActionsBottomSheetConfig -> TokenActionsBottomSheet(config = bottomSheetConfig)
|
||||
is ChooseAddressBottomSheetConfig -> ChooseAddressBottomSheet(config = bottomSheetConfig)
|
||||
is BalancesAndLimitsBottomSheetConfig -> BalancesAndLimitsBottomSheet(config = bottomSheetConfig)
|
||||
is VisaTxDetailsBottomSheetConfig -> VisaTxDetailsBottomSheet(config = bottomSheetConfig)
|
||||
}
|
||||
}
|
||||
ShowBottomSheet(bottomSheetConfig = selectedWallet.bottomSheetConfig)
|
||||
|
||||
WalletsListEffects(
|
||||
lazyListState = walletsListState,
|
||||
|
|
@ -224,14 +216,28 @@ private fun WalletContent(
|
|||
)
|
||||
}
|
||||
|
||||
if (state.manageTokenRedesignToggle) {
|
||||
BaseScaffoldManageTokenRedesign(
|
||||
if (marketsListComponent != null) {
|
||||
val bottomSheetState = remember {
|
||||
mutableStateOf(BottomSheetState.COLLAPSED)
|
||||
}
|
||||
var headerSize by remember {
|
||||
mutableStateOf(0.dp)
|
||||
}
|
||||
|
||||
BaseScaffoldWithMarkets(
|
||||
state = state,
|
||||
selectedWallet = selectedWallet,
|
||||
snackbarHostState = snackbarHostState,
|
||||
bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider,
|
||||
bottomSheetContent = bottomSheetContent,
|
||||
bottomSheetHeaderHeightProvider = { headerSize },
|
||||
alertConfig = alertConfig,
|
||||
onBottomSheetStateChange = { bottomSheetState.value = it },
|
||||
bottomSheetContent = {
|
||||
marketsListComponent.BottomSheetContent(
|
||||
bottomSheetState = bottomSheetState,
|
||||
onHeaderSizeChange = { headerSize = it },
|
||||
modifier = Modifier,
|
||||
)
|
||||
},
|
||||
) {
|
||||
scaffoldContent()
|
||||
}
|
||||
|
|
@ -246,16 +252,78 @@ private fun WalletContent(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
private fun BaseScaffold(
|
||||
state: WalletScreenState,
|
||||
selectedWallet: WalletState,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = { WalletTopBar(config = state.topBarConfig) },
|
||||
contentWindowInsets = WindowInsetsZero,
|
||||
snackbarHost = {
|
||||
WalletSnackbarHost(
|
||||
snackbarHostState = snackbarHostState,
|
||||
event = state.event,
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing16),
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
val manageTokensButtonConfig by remember(state.selectedWalletIndex) {
|
||||
mutableStateOf(
|
||||
(state.wallets[state.selectedWalletIndex] as? WalletState.MultiCurrency)?.manageTokensButtonConfig,
|
||||
)
|
||||
}
|
||||
|
||||
manageTokensButtonConfig?.let {
|
||||
ManageTokensButton(
|
||||
modifier = Modifier.navigationBarsPadding(),
|
||||
onClick = it.onClick,
|
||||
)
|
||||
}
|
||||
},
|
||||
floatingActionButtonPosition = FabPosition.Center,
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
content = {
|
||||
val pullRefreshState = rememberPullRefreshState(
|
||||
refreshing = selectedWallet.pullToRefreshConfig.isRefreshing,
|
||||
onRefresh = {
|
||||
selectedWallet.pullToRefreshConfig.onRefresh(WalletPullToRefreshConfig.ShowRefreshState(true))
|
||||
},
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.pullRefresh(pullRefreshState)
|
||||
.padding(it),
|
||||
) {
|
||||
content()
|
||||
|
||||
WalletPullToRefreshIndicator(
|
||||
isRefreshing = selectedWallet.pullToRefreshConfig.isRefreshing,
|
||||
state = pullRefreshState,
|
||||
modifier = Modifier.align(Alignment.TopCenter),
|
||||
)
|
||||
|
||||
BottomFade(Modifier.align(Alignment.BottomCenter))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList", "LongMethod")
|
||||
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun BaseScaffoldManageTokenRedesign(
|
||||
private fun BaseScaffoldWithMarkets(
|
||||
state: WalletScreenState,
|
||||
selectedWallet: WalletState,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
bottomSheetHeaderHeightProvider: () -> Dp,
|
||||
bottomSheetContent: @Composable () -> Unit,
|
||||
alertConfig: WalletAlertState?,
|
||||
onBottomSheetStateChange: (BottomSheetState) -> Unit,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
// show the bottom sheet if there is at least one multicurrency wallet
|
||||
|
|
@ -278,10 +346,10 @@ private fun BaseScaffoldManageTokenRedesign(
|
|||
|
||||
BottomSheetStateEffects(
|
||||
bottomSheetState = bottomSheetState,
|
||||
state = state,
|
||||
showManageTokensBottomSheet = showManageTokensBottomSheet,
|
||||
alertConfig = alertConfig,
|
||||
keyboardShown = keyboardShown,
|
||||
onBottomSheetStateChange = onBottomSheetStateChange,
|
||||
)
|
||||
|
||||
val scaffoldState = rememberBottomSheetScaffoldState(
|
||||
|
|
@ -310,6 +378,8 @@ private fun BaseScaffoldManageTokenRedesign(
|
|||
sheetDragHandle = {
|
||||
Hand(modifier = Modifier.background(color = TangemTheme.colors.background.primary))
|
||||
},
|
||||
sheetTonalElevation = 8.dp,
|
||||
sheetShadowElevation = 8.dp,
|
||||
sheetContent = {
|
||||
BoxWithConstraints {
|
||||
Box(
|
||||
|
|
@ -329,7 +399,7 @@ private fun BaseScaffoldManageTokenRedesign(
|
|||
coroutineScope.launch { bottomSheetState.partialExpand() }
|
||||
}
|
||||
},
|
||||
content = { paddingValues ->
|
||||
content = { _ ->
|
||||
val pullRefreshState = rememberPullRefreshState(
|
||||
refreshing = selectedWallet.pullToRefreshConfig.isRefreshing,
|
||||
onRefresh = {
|
||||
|
|
@ -337,9 +407,7 @@ private fun BaseScaffoldManageTokenRedesign(
|
|||
},
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
) {
|
||||
Column {
|
||||
WalletTopBar(config = state.topBarConfig)
|
||||
Box(
|
||||
modifier = Modifier.pullRefresh(pullRefreshState),
|
||||
|
|
@ -391,14 +459,14 @@ private fun BottomSheetScrim(color: Color, visible: Boolean, onDismissRequest: (
|
|||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
@Suppress("CyclomaticComplexMethod", "MagicNumber", "LongMethod")
|
||||
@Composable
|
||||
private fun BottomSheetStateEffects(
|
||||
bottomSheetState: SheetState,
|
||||
state: WalletScreenState,
|
||||
showManageTokensBottomSheet: Boolean,
|
||||
alertConfig: WalletAlertState?,
|
||||
keyboardShown: State<Keyboard>,
|
||||
onBottomSheetStateChange: (BottomSheetState) -> Unit,
|
||||
) {
|
||||
// Bottom sheet during initialization internally expand partially after its content was remeasured,
|
||||
// therefore initialValue = SheetValue.Hidden in rememberStandardBottomSheetState doesn't work as expected
|
||||
|
|
@ -424,19 +492,29 @@ private fun BottomSheetStateEffects(
|
|||
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val navigationBarColor = TangemTheme.colors.background.primary
|
||||
val navigationBarColorWithout = TangemTheme.colors.background.secondary
|
||||
|
||||
SystemBarsEffect {
|
||||
if (showManageTokensBottomSheet) {
|
||||
setNavigationBarColor(navigationBarColor)
|
||||
LaunchedEffect(key1 = bottomSheetState.targetValue, navigationBarColor) {
|
||||
when (bottomSheetState.targetValue) {
|
||||
SheetValue.Hidden,
|
||||
SheetValue.Expanded,
|
||||
-> systemUiController.setNavigationBarColor(
|
||||
color = Color.Transparent,
|
||||
darkIcons = navigationBarColor.luminance() > 0.5f,
|
||||
navigationBarContrastEnforced = true,
|
||||
)
|
||||
SheetValue.PartiallyExpanded,
|
||||
-> systemUiController.setNavigationBarColor(navigationBarColor)
|
||||
}
|
||||
}
|
||||
DisposableEffect(
|
||||
showManageTokensBottomSheet,
|
||||
) {
|
||||
|
||||
DisposableEffect(showManageTokensBottomSheet) {
|
||||
onDispose {
|
||||
if (showManageTokensBottomSheet) {
|
||||
systemUiController.setNavigationBarColor(navigationBarColorWithout)
|
||||
systemUiController.setNavigationBarColor(
|
||||
color = Color.Transparent,
|
||||
darkIcons = navigationBarColor.luminance() > 0.5f,
|
||||
navigationBarContrastEnforced = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -463,11 +541,13 @@ private fun BottomSheetStateEffects(
|
|||
|
||||
val isSheetHidden = bottomSheetState.targetValue == SheetValue.PartiallyExpanded
|
||||
LaunchedEffect(isSheetHidden) {
|
||||
if (isSheetHidden) {
|
||||
state.manageTokensExpandableState.value = ExpandableState.COLLAPSED
|
||||
} else {
|
||||
state.manageTokensExpandableState.value = ExpandableState.EXPANDED
|
||||
}
|
||||
onBottomSheetStateChange(
|
||||
if (isSheetHidden) {
|
||||
BottomSheetState.COLLAPSED
|
||||
} else {
|
||||
BottomSheetState.EXPANDED
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -495,59 +575,6 @@ private fun rememberSheetStateEnhanced(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
private fun BaseScaffold(
|
||||
state: WalletScreenState,
|
||||
selectedWallet: WalletState,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = { WalletTopBar(config = state.topBarConfig) },
|
||||
snackbarHost = {
|
||||
WalletSnackbarHost(
|
||||
snackbarHostState = snackbarHostState,
|
||||
event = state.event,
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing16),
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
val manageTokensButtonConfig by remember(state.selectedWalletIndex) {
|
||||
mutableStateOf(
|
||||
(state.wallets[state.selectedWalletIndex] as? WalletState.MultiCurrency)?.manageTokensButtonConfig,
|
||||
)
|
||||
}
|
||||
|
||||
manageTokensButtonConfig?.let { ManageTokensButton(onClick = it.onClick) }
|
||||
},
|
||||
floatingActionButtonPosition = FabPosition.Center,
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
content = {
|
||||
val pullRefreshState = rememberPullRefreshState(
|
||||
refreshing = selectedWallet.pullToRefreshConfig.isRefreshing,
|
||||
onRefresh = {
|
||||
selectedWallet.pullToRefreshConfig.onRefresh(WalletPullToRefreshConfig.ShowRefreshState(true))
|
||||
},
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.pullRefresh(pullRefreshState)
|
||||
.padding(it),
|
||||
) {
|
||||
content()
|
||||
|
||||
WalletPullToRefreshIndicator(
|
||||
isRefreshing = selectedWallet.pullToRefreshConfig.isRefreshing,
|
||||
state = pullRefreshState,
|
||||
modifier = Modifier.align(Alignment.TopCenter),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WalletSnackbarHost(
|
||||
snackbarHostState: SnackbarHostState,
|
||||
|
|
@ -564,11 +591,11 @@ private fun WalletSnackbarHost(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun ManageTokensButton(onClick: () -> Unit) {
|
||||
private fun ManageTokensButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
PrimaryButton(
|
||||
text = stringResource(id = R.string.main_manage_tokens),
|
||||
onClick = onClick,
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
)
|
||||
|
|
@ -588,6 +615,21 @@ internal fun LazyListScope.organizeTokens(state: WalletState, itemModifier: Modi
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ShowBottomSheet(bottomSheetConfig: TangemBottomSheetConfig?) {
|
||||
if (bottomSheetConfig != null) {
|
||||
when (bottomSheetConfig.content) {
|
||||
is WalletBottomSheetConfig -> WalletBottomSheet(config = bottomSheetConfig)
|
||||
is TokenReceiveBottomSheetConfig -> TokenReceiveBottomSheet(config = bottomSheetConfig)
|
||||
is ActionsBottomSheetConfig -> TokenActionsBottomSheet(config = bottomSheetConfig)
|
||||
is ChooseAddressBottomSheetConfig -> ChooseAddressBottomSheet(config = bottomSheetConfig)
|
||||
is BalancesAndLimitsBottomSheetConfig -> BalancesAndLimitsBottomSheet(config = bottomSheetConfig)
|
||||
is VisaTxDetailsBottomSheetConfig -> VisaTxDetailsBottomSheet(config = bottomSheetConfig)
|
||||
is PushNotificationsBottomSheetConfig -> PushNotificationsBottomSheet(config = bottomSheetConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
|
|
@ -596,8 +638,7 @@ private fun WalletScreen_Preview(@PreviewParameter(WalletScreenPreviewProvider::
|
|||
TangemThemePreview {
|
||||
WalletScreen(
|
||||
state = data,
|
||||
bottomSheetHeaderHeightProvider = { 0.dp },
|
||||
bottomSheetContent = {},
|
||||
marketsListComponent = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,119 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.SecondaryButton
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.showcase.ShowcaseContent
|
||||
import com.tangem.core.ui.components.showcase.model.ShowcaseItemModel
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.utils.requestPushPermission
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.PushNotificationsBottomSheetConfig
|
||||
import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Composable
|
||||
internal fun PushNotificationsBottomSheet(config: TangemBottomSheetConfig) {
|
||||
TangemBottomSheet<PushNotificationsBottomSheetConfig>(config = config) {
|
||||
PushNotificationsSheetContent(content = it, onDismiss = config.onDismissRequest)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PushNotificationsSheetContent(content: PushNotificationsBottomSheetConfig, onDismiss: () -> Unit) {
|
||||
val isClicked = remember { mutableStateOf(false) }
|
||||
val requestPushPermission = requestPushPermission(
|
||||
pushPermission = getPushPermissionOrNull(),
|
||||
isFirstTimeAsking = content.isFirstTimeAsking,
|
||||
isClicked = isClicked,
|
||||
onAllow = {
|
||||
content.onAllow()
|
||||
onDismiss()
|
||||
},
|
||||
onDeny = {
|
||||
content.onDeny()
|
||||
onDismiss()
|
||||
},
|
||||
onOpenSettings = content.openSettings,
|
||||
)
|
||||
|
||||
Column(modifier = Modifier.background(TangemTheme.colors.background.primary)) {
|
||||
ShowcaseContent(
|
||||
headerIconRes = R.drawable.ic_notifications_unread_24,
|
||||
headerText = resourceReference(R.string.user_push_notification_agreement_header),
|
||||
showcaseItems = persistentListOf(
|
||||
ShowcaseItemModel(
|
||||
iconRes = R.drawable.ic_rocket_launch_24,
|
||||
text = resourceReference(R.string.user_push_notification_agreement_argument_one),
|
||||
),
|
||||
ShowcaseItemModel(
|
||||
iconRes = R.drawable.ic_storefront_24,
|
||||
text = resourceReference(R.string.user_push_notification_agreement_argument_two),
|
||||
),
|
||||
),
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing40),
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
modifier = Modifier.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
top = TangemTheme.dimens.spacing40,
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
) {
|
||||
SecondaryButton(
|
||||
text = stringResource(R.string.common_later),
|
||||
onClick = {
|
||||
content.onDeny()
|
||||
onDismiss()
|
||||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
PrimaryButton(
|
||||
text = stringResource(R.string.common_allow),
|
||||
onClick = {
|
||||
isClicked.value = true
|
||||
content.onRequest()
|
||||
requestPushPermission()
|
||||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PushNotificationsSheetContent_Preview() {
|
||||
TangemThemePreview {
|
||||
PushNotificationsSheetContent(
|
||||
PushNotificationsBottomSheetConfig(
|
||||
isFirstTimeAsking = false,
|
||||
onRequest = {},
|
||||
onAllow = {},
|
||||
onDeny = {},
|
||||
openSettings = {},
|
||||
),
|
||||
onDismiss = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -42,7 +42,6 @@ import androidx.compose.ui.unit.sp
|
|||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.constraintlayout.compose.ConstraintLayoutScope
|
||||
import androidx.constraintlayout.compose.Dimension
|
||||
import com.tangem.common.Strings
|
||||
import com.tangem.core.ui.components.FontSizeRange
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.ResizableText
|
||||
|
|
@ -54,6 +53,7 @@ import com.tangem.core.ui.res.TangemThemePreview
|
|||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
|
||||
import com.tangem.utils.StringsSigns
|
||||
|
||||
private const val HALF_OF_ITEM_WIDTH = 0.5
|
||||
|
||||
|
|
@ -291,7 +291,7 @@ private fun Balance(state: WalletCardState, isBalanceHidden: Boolean, modifier:
|
|||
when (walletCardState) {
|
||||
is WalletCardState.Content -> {
|
||||
ResizableText(
|
||||
text = if (isBalanceHidden) Strings.STARS else walletCardState.balance,
|
||||
text = if (isBalanceHidden) StringsSigns.STARS else walletCardState.balance,
|
||||
fontSizeRange = FontSizeRange(min = 16.sp, max = TangemTheme.typography.h2.fontSize),
|
||||
modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size32),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.viewmodels
|
||||
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.navigation.settings.SettingsManager
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.settings.CanUseBiometryUseCase
|
||||
import com.tangem.domain.settings.IsWalletsScrollPreviewEnabled
|
||||
import com.tangem.domain.settings.ShouldShowSaveWalletScreenUseCase
|
||||
import com.tangem.domain.settings.*
|
||||
import com.tangem.domain.tokens.RefreshMultiCurrencyWalletQuotesUseCase
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
|
||||
|
|
@ -21,6 +19,7 @@ import com.tangem.feature.wallet.presentation.wallet.analytics.utils.SelectedWal
|
|||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletNameMigrationUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.PushNotificationsBottomSheetConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent.DemonstrateWalletsScrollPreview.Direction
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
|
|
@ -28,7 +27,9 @@ import com.tangem.feature.wallet.presentation.wallet.state.transformers.*
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import com.tangem.features.managetokens.navigation.ExpandableState
|
||||
import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles
|
||||
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
|
||||
import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
|
|
@ -56,13 +57,18 @@ internal class WalletViewModel @Inject constructor(
|
|||
private val canUseBiometryUseCase: CanUseBiometryUseCase,
|
||||
private val isWalletsScrollPreviewEnabled: IsWalletsScrollPreviewEnabled,
|
||||
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
analyticsEventsHandler: AnalyticsEventHandler,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val screenLifecycleProvider: ScreenLifecycleProvider,
|
||||
private val selectedWalletAnalyticsSender: SelectedWalletAnalyticsSender,
|
||||
private val walletDeepLinksHandler: WalletDeepLinksHandler,
|
||||
private val walletNameMigrationUseCase: WalletNameMigrationUseCase,
|
||||
private val refreshMultiCurrencyWalletQuotesUseCase: RefreshMultiCurrencyWalletQuotesUseCase,
|
||||
private val shouldInitiallyAskPermissionUseCase: ShouldInitiallyAskPermissionUseCase,
|
||||
private val isFirstTimeAskingPermissionUseCase: IsFirstTimeAskingPermissionUseCase,
|
||||
private val shouldAskPermissionUseCase: ShouldAskPermissionUseCase,
|
||||
private val pushNotificationsFeatureToggles: PushNotificationsFeatureToggles,
|
||||
private val settingsManager: SettingsManager,
|
||||
analyticsEventsHandler: AnalyticsEventHandler,
|
||||
) : ViewModel() {
|
||||
|
||||
val uiState: StateFlow<WalletScreenState> = stateHolder.uiState
|
||||
|
|
@ -82,6 +88,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
subscribeOnBalanceHiding()
|
||||
subscribeOnSelectedWalletFlow()
|
||||
subscribeToScreenBackgroundState()
|
||||
subscribeOnPushNotificationsPermission()
|
||||
}
|
||||
|
||||
private fun maybeMigrateNames() {
|
||||
|
|
@ -95,12 +102,6 @@ internal class WalletViewModel @Inject constructor(
|
|||
clickIntents.initialize(router, viewModelScope)
|
||||
}
|
||||
|
||||
fun setExpandableState(state: MutableState<ExpandableState>) {
|
||||
stateHolder.update {
|
||||
it.copy(manageTokensExpandableState = state)
|
||||
}
|
||||
}
|
||||
|
||||
fun subscribeToLifecycle(lifecycleOwner: LifecycleOwner) {
|
||||
lifecycleOwner.lifecycle.addObserver(screenLifecycleProvider)
|
||||
}
|
||||
|
|
@ -147,6 +148,34 @@ internal class WalletViewModel @Inject constructor(
|
|||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
private fun subscribeOnPushNotificationsPermission() {
|
||||
viewModelScope.launch {
|
||||
val isPushToggled = pushNotificationsFeatureToggles.isPushNotificationsEnabled
|
||||
val shouldRequestPush = shouldAskPermissionUseCase(PUSH_PERMISSION)
|
||||
val isPushPermissionAvailable = getPushPermissionOrNull() != null
|
||||
if (!isPushToggled || !shouldRequestPush || !isPushPermissionAvailable) return@launch
|
||||
|
||||
delay(timeMillis = 1_800)
|
||||
|
||||
val isFirstTimeAsking = isFirstTimeAskingPermissionUseCase(PUSH_PERMISSION).getOrElse { true }
|
||||
val wasInitiallyAsk = shouldInitiallyAskPermissionUseCase(PUSH_PERMISSION).getOrElse { true }
|
||||
val onDenyClick: () -> Unit = if (wasInitiallyAsk) {
|
||||
clickIntents::onDelayAskPushPermission
|
||||
} else {
|
||||
clickIntents::onNeverAskPushPermission
|
||||
}
|
||||
stateHolder.showBottomSheet(
|
||||
PushNotificationsBottomSheetConfig(
|
||||
isFirstTimeAsking = isFirstTimeAsking,
|
||||
onRequest = clickIntents::onRequestPushPermission,
|
||||
onAllow = clickIntents::onNeverAskPushPermission,
|
||||
onDeny = onDenyClick,
|
||||
openSettings = settingsManager::openSettings,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeOnSelectedWalletFlow() {
|
||||
getSelectedWalletUseCase().onRight {
|
||||
it
|
||||
|
|
|
|||
|
|
@ -1,21 +1,16 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.domain.wallets.usecase.GetWalletNamesUseCase
|
||||
import com.tangem.domain.wallets.usecase.RenameWalletUseCase
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.domain.card.DeleteSavedAccessCodesUseCase
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.core.navigation.ReduxNavController
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.DeleteWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.*
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen
|
||||
import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
|
|
@ -52,7 +47,7 @@ internal class WalletCardClickIntentsImplementor @Inject constructor(
|
|||
private val deleteSavedAccessCodesUseCase: DeleteSavedAccessCodesUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val reduxNavController: ReduxNavController,
|
||||
private val appRouter: AppRouter,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : BaseWalletClickIntents(), WalletCardClickIntents {
|
||||
|
||||
|
|
@ -123,7 +118,7 @@ internal class WalletCardClickIntentsImplementor @Inject constructor(
|
|||
reduxStateHolder.onUserWalletSelected(selectedWallet)
|
||||
} else {
|
||||
stateHolder.clear()
|
||||
reduxNavController.navigate(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
appRouter.replaceAll(AppRoute.Home)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ internal class WalletClickIntents @Inject constructor(
|
|||
private val currencyActionsClickIntentsImplementor: WalletCurrencyActionsClickIntentsImplementor,
|
||||
private val contentClickIntentsImplementor: WalletContentClickIntentsImplementor,
|
||||
private val visaWalletIntentsImplementor: VisaWalletIntentsImplementor,
|
||||
private val pushPermissionClickIntentsImplementor: WalletPushPermissionClickIntentsImplementor,
|
||||
private val stateHolder: WalletStateController,
|
||||
private val walletScreenContentLoader: WalletScreenContentLoader,
|
||||
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
|
||||
|
|
@ -48,7 +49,8 @@ internal class WalletClickIntents @Inject constructor(
|
|||
WalletWarningsClickIntents by warningsClickIntentsImplementer,
|
||||
WalletCurrencyActionsClickIntents by currencyActionsClickIntentsImplementor,
|
||||
WalletContentClickIntents by contentClickIntentsImplementor,
|
||||
VisaWalletIntents by visaWalletIntentsImplementor {
|
||||
VisaWalletIntents by visaWalletIntentsImplementor,
|
||||
WalletPushPermissionClickIntents by pushPermissionClickIntentsImplementor {
|
||||
|
||||
override fun initialize(router: InnerWalletRouter, coroutineScope: CoroutineScope) {
|
||||
super.initialize(router, coroutineScope)
|
||||
|
|
@ -58,6 +60,7 @@ internal class WalletClickIntents @Inject constructor(
|
|||
currencyActionsClickIntentsImplementor.initialize(router, coroutineScope)
|
||||
contentClickIntentsImplementor.initialize(router, coroutineScope)
|
||||
visaWalletIntentsImplementor.initialize(router, coroutineScope)
|
||||
pushPermissionClickIntentsImplementor.initialize(router, coroutineScope)
|
||||
}
|
||||
|
||||
fun onWalletChange(index: Int) {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
interface WalletCurrencyActionsClickIntents {
|
||||
|
|
@ -58,6 +59,8 @@ interface WalletCurrencyActionsClickIntents {
|
|||
|
||||
fun onReceiveClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
fun onStakeClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
fun onCopyAddressLongClick(cryptoCurrencyStatus: CryptoCurrencyStatus): TextReference?
|
||||
|
||||
fun onCopyAddressClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
|
|
@ -364,6 +367,11 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
showErrorIfDemoModeOrElse(action = ::openExplorer)
|
||||
}
|
||||
|
||||
override fun onStakeClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
// TODO staking
|
||||
Timber.e("Not implemented yet")
|
||||
}
|
||||
|
||||
private fun openExplorer() {
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
|
||||
|
|
@ -466,6 +474,12 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
|
||||
private fun getUnavailabilityReasonText(unavailabilityReason: ScenarioUnavailabilityReason): TextReference {
|
||||
return when (unavailabilityReason) {
|
||||
is ScenarioUnavailabilityReason.StakingUnavailable -> {
|
||||
resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_staking_unavailable,
|
||||
formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName),
|
||||
)
|
||||
}
|
||||
is ScenarioUnavailabilityReason.PendingTransaction -> {
|
||||
when (unavailabilityReason.withdrawalScenario) {
|
||||
ScenarioUnavailabilityReason.WithdrawalScenario.SEND -> resourceReference(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
|
||||
|
||||
import com.tangem.domain.settings.DelayPermissionRequestUseCase
|
||||
import com.tangem.domain.settings.NeverRequestPermissionUseCase
|
||||
import com.tangem.domain.settings.SetFirstTimeAskingPermissionUseCase
|
||||
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface WalletPushPermissionClickIntents {
|
||||
|
||||
fun onRequestPushPermission()
|
||||
|
||||
fun onDelayAskPushPermission()
|
||||
|
||||
fun onNeverAskPushPermission()
|
||||
}
|
||||
|
||||
@ViewModelScoped
|
||||
internal class WalletPushPermissionClickIntentsImplementor @Inject constructor(
|
||||
private val setFirstTimeAskingPermissionUseCase: SetFirstTimeAskingPermissionUseCase,
|
||||
private val neverRequestPermissionUseCase: NeverRequestPermissionUseCase,
|
||||
private val delayPermissionRequestUseCase: DelayPermissionRequestUseCase,
|
||||
) : BaseWalletClickIntents(), WalletPushPermissionClickIntents {
|
||||
|
||||
override fun onRequestPushPermission() {
|
||||
viewModelScope.launch {
|
||||
setFirstTimeAskingPermissionUseCase(PUSH_PERMISSION)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDelayAskPushPermission() {
|
||||
viewModelScope.launch {
|
||||
delayPermissionRequestUseCase(PUSH_PERMISSION)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNeverAskPushPermission() {
|
||||
viewModelScope.launch {
|
||||
neverRequestPermissionUseCase(PUSH_PERMISSION)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -165,7 +165,10 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
|
||||
override fun onScanToUnlockWalletClick() {
|
||||
analyticsEventHandler.send(MainScreen.UnlockWithCardScan)
|
||||
openScanCardDialog()
|
||||
}
|
||||
|
||||
private fun openScanCardDialog() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
scanCardToUnlockWalletClickHandler(walletId = stateHolder.getSelectedWalletId())
|
||||
.onLeft { error ->
|
||||
|
|
@ -175,7 +178,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
event = WalletEvent.ShowAlert(WalletAlertState.WrongCardIsScanned),
|
||||
)
|
||||
}
|
||||
ScanCardToUnlockWalletError.ManyScanFails -> router.openScanFailedDialog()
|
||||
ScanCardToUnlockWalletError.ManyScanFails -> router.openScanFailedDialog(::openScanCardDialog)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -201,7 +204,12 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
viewModelScope.launch(dispatchers.main) {
|
||||
neverToSuggestRateAppUseCase()
|
||||
|
||||
reduxStateHolder.dispatch(LegacyAction.SendEmailRateCanBeBetter)
|
||||
reduxStateHolder.dispatch(
|
||||
LegacyAction.SendEmailRateCanBeBetter(
|
||||
scanResponse = getSelectedUserWallet()?.scanResponse
|
||||
?: error("ScanResponse must be not null"),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -254,7 +262,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun getSelectedUserWallet(): UserWallet? {
|
||||
private fun getSelectedUserWallet(): UserWallet? {
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
return getUserWalletUseCase(userWalletId).getOrElse {
|
||||
Timber.e(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue