Updated on 2026-08-14
This commit is contained in:
commit
bc84d8f5f8
579 changed files with 13604 additions and 3422 deletions
|
|
@ -52,7 +52,7 @@ dependencies {
|
|||
implementation(projects.core.utils)
|
||||
implementation(projects.core.analytics)
|
||||
implementation(projects.core.analytics.models)
|
||||
implementation(projects.common)
|
||||
implementation(projects.common.routing)
|
||||
implementation(projects.core.deepLinks)
|
||||
implementation(projects.core.deepLinks.global)
|
||||
|
||||
|
|
@ -77,6 +77,7 @@ dependencies {
|
|||
implementation(projects.domain.balanceHiding.models)
|
||||
implementation(projects.domain.analytics)
|
||||
implementation(projects.domain.visa)
|
||||
implementation(projects.domain.staking)
|
||||
|
||||
//TODO: Create api/impl modules for onboarding [REDACTED_JIRA]
|
||||
implementation(projects.features.onboarding)
|
||||
|
|
@ -88,4 +89,5 @@ dependencies {
|
|||
implementation(projects.features.tester.api)
|
||||
implementation(projects.features.manageTokens.api)
|
||||
implementation(projects.features.details.api)
|
||||
implementation(projects.features.pushNotifications.api)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,8 +3,6 @@ package com.tangem.feature.wallet.presentation
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
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
|
||||
|
|
@ -37,11 +35,6 @@ internal class WalletFragment : ComposeFragment() {
|
|||
|
||||
@Composable
|
||||
override fun ScreenContent(modifier: Modifier) {
|
||||
val systemBarsColor = TangemTheme.colors.background.secondary
|
||||
SystemBarsEffect {
|
||||
setSystemBarsColor(systemBarsColor)
|
||||
}
|
||||
|
||||
_walletRouter.Initialize(
|
||||
onFinish = requireActivity()::finish,
|
||||
manageTokensUi = manageTokensUi,
|
||||
|
|
|
|||
|
|
@ -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.Strings
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState.CryptoAmountState as TokenCryptoAmountState
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -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.Strings
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState.FiatAmountState as TokenFiatAmountState
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -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.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,27 @@ 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 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()
|
||||
|
|
@ -87,7 +84,6 @@ internal class DefaultWalletRouter(
|
|||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
OrganizeTokensScreen(
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
state = uiState,
|
||||
)
|
||||
}
|
||||
|
|
@ -95,7 +91,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 +99,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 +110,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,7 +2,7 @@ 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
|
||||
|
|
@ -29,7 +29,7 @@ internal interface InnerWalletRouter : WalletRouter {
|
|||
fun Initialize(onFinish: () -> Unit, manageTokensUi: ManageTokensUi)
|
||||
|
||||
/** Pop back stack */
|
||||
fun popBackStack(screen: AppScreen? = null)
|
||||
fun popBackStack()
|
||||
|
||||
/** Open organize tokens screen */
|
||||
fun openOrganizeTokensScreen(userWalletId: UserWalletId)
|
||||
|
|
@ -59,5 +59,5 @@ internal interface InnerWalletRouter : WalletRouter {
|
|||
fun openManageTokensScreen()
|
||||
|
||||
/** Open scan failed dialog */
|
||||
fun openScanFailedDialog()
|
||||
fun openScanFailedDialog(onTryAgain: () -> Unit)
|
||||
}
|
||||
|
|
@ -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,9 @@ 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.Strings
|
||||
|
||||
/** Wallet card state */
|
||||
@Immutable
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.Strings
|
||||
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 ${Strings.DOT} ${value.status.capitalize()}"
|
||||
|
||||
return TransactionState.Content(
|
||||
txHash = value.id,
|
||||
|
|
|
|||
|
|
@ -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,16 +34,14 @@ 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.Keyboard
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.SystemBarsEffect
|
||||
import com.tangem.core.ui.components.*
|
||||
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
|
||||
import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig
|
||||
import com.tangem.core.ui.components.keyboardAsState
|
||||
import com.tangem.core.ui.components.snackbar.CopiedTextSnackbar
|
||||
import com.tangem.core.ui.components.snackbar.TangemSnackbar
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
|
|
@ -50,10 +49,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.*
|
||||
|
|
@ -112,7 +113,7 @@ internal fun WalletScreen(
|
|||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "LongParameterList")
|
||||
@Suppress("LongMethod", "LongParameterList", "CyclomaticComplexMethod")
|
||||
@Composable
|
||||
private fun WalletContent(
|
||||
state: WalletScreenState,
|
||||
|
|
@ -144,17 +145,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 +206,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,
|
||||
|
|
@ -329,7 +323,7 @@ private fun BaseScaffoldManageTokenRedesign(
|
|||
coroutineScope.launch { bottomSheetState.partialExpand() }
|
||||
}
|
||||
},
|
||||
content = { paddingValues ->
|
||||
content = { _ ->
|
||||
val pullRefreshState = rememberPullRefreshState(
|
||||
refreshing = selectedWallet.pullToRefreshConfig.isRefreshing,
|
||||
onRefresh = {
|
||||
|
|
@ -337,9 +331,7 @@ private fun BaseScaffoldManageTokenRedesign(
|
|||
},
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
) {
|
||||
Column {
|
||||
WalletTopBar(config = state.topBarConfig)
|
||||
Box(
|
||||
modifier = Modifier.pullRefresh(pullRefreshState),
|
||||
|
|
@ -391,7 +383,7 @@ private fun BottomSheetScrim(color: Color, visible: Boolean, onDismissRequest: (
|
|||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
@Suppress("CyclomaticComplexMethod", "MagicNumber")
|
||||
@Composable
|
||||
private fun BottomSheetStateEffects(
|
||||
bottomSheetState: SheetState,
|
||||
|
|
@ -424,19 +416,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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -505,6 +507,7 @@ private fun BaseScaffold(
|
|||
) {
|
||||
Scaffold(
|
||||
topBar = { WalletTopBar(config = state.topBarConfig) },
|
||||
contentWindowInsets = WindowInsetsZero,
|
||||
snackbarHost = {
|
||||
WalletSnackbarHost(
|
||||
snackbarHostState = snackbarHostState,
|
||||
|
|
@ -519,7 +522,12 @@ private fun BaseScaffold(
|
|||
)
|
||||
}
|
||||
|
||||
manageTokensButtonConfig?.let { ManageTokensButton(onClick = it.onClick) }
|
||||
manageTokensButtonConfig?.let {
|
||||
ManageTokensButton(
|
||||
modifier = Modifier.navigationBarsPadding(),
|
||||
onClick = it.onClick,
|
||||
)
|
||||
}
|
||||
},
|
||||
floatingActionButtonPosition = FabPosition.Center,
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
|
|
@ -543,6 +551,8 @@ private fun BaseScaffold(
|
|||
state = pullRefreshState,
|
||||
modifier = Modifier.align(Alignment.TopCenter),
|
||||
)
|
||||
|
||||
BottomFade(Modifier.align(Alignment.BottomCenter))
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -564,11 +574,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 +598,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)
|
||||
|
|
|
|||
|
|
@ -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.Strings
|
||||
|
||||
private const val HALF_OF_ITEM_WIDTH = 0.5
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ 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.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
|
|
@ -19,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.WalletPullToRefreshConfig
|
||||
|
|
@ -28,6 +29,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSend
|
|||
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.utils.PUSH_PERMISSION
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
|
|
@ -55,12 +57,16 @@ 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 shouldInitiallyAskPermissionUseCase: ShouldInitiallyAskPermissionUseCase,
|
||||
private val isFirstTimeAskingPermissionUseCase: IsFirstTimeAskingPermissionUseCase,
|
||||
private val shouldAskPermissionUseCase: ShouldAskPermissionUseCase,
|
||||
private val settingsManager: SettingsManager,
|
||||
analyticsEventsHandler: AnalyticsEventHandler,
|
||||
) : ViewModel() {
|
||||
|
||||
val uiState: StateFlow<WalletScreenState> = stateHolder.uiState
|
||||
|
|
@ -80,6 +86,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
subscribeOnBalanceHiding()
|
||||
subscribeOnSelectedWalletFlow()
|
||||
subscribeToScreenBackgroundState()
|
||||
subscribeOnPushNotificationsPermission()
|
||||
}
|
||||
|
||||
private fun maybeMigrateNames() {
|
||||
|
|
@ -145,6 +152,31 @@ internal class WalletViewModel @Inject constructor(
|
|||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
private fun subscribeOnPushNotificationsPermission() {
|
||||
viewModelScope.launch {
|
||||
if (!shouldAskPermissionUseCase(PUSH_PERMISSION)) 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,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,
|
||||
|
|
@ -45,7 +46,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)
|
||||
|
|
@ -55,6 +57,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue