Updated on 2026-08-14
This commit is contained in:
parent
9a02d6a58a
commit
6c850776d5
37 changed files with 611 additions and 104 deletions
|
|
@ -15,4 +15,6 @@ internal sealed class WalletEvent {
|
|||
data class CopyAddress(val address: String) : WalletEvent()
|
||||
|
||||
data class ShowWalletAlreadySignedHashesMessage(val onUnderstandClick: () -> Unit) : WalletEvent()
|
||||
|
||||
data class RateApp(val onDismissClick: () -> Unit) : WalletEvent()
|
||||
}
|
||||
|
|
@ -135,8 +135,8 @@ sealed class WalletNotification(val config: NotificationConfig) {
|
|||
)
|
||||
|
||||
data class RateApp(
|
||||
val onPositiveClick: () -> Unit,
|
||||
val onNegativeClick: () -> Unit,
|
||||
val onLikeClick: () -> Unit,
|
||||
val onDislikeClick: () -> Unit,
|
||||
val onCloseClick: () -> Unit,
|
||||
) : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
|
|
@ -145,9 +145,9 @@ sealed class WalletNotification(val config: NotificationConfig) {
|
|||
iconResId = R.drawable.ic_star_24,
|
||||
buttonsState = NotificationConfig.ButtonsState.PairButtonsConfig(
|
||||
primaryText = resourceReference(id = R.string.warning_button_love_it),
|
||||
onPrimaryClick = onPositiveClick,
|
||||
onPrimaryClick = onLikeClick,
|
||||
secondaryText = resourceReference(id = R.string.warning_button_can_be_better),
|
||||
onSecondaryClick = onNegativeClick,
|
||||
onSecondaryClick = onDislikeClick,
|
||||
),
|
||||
onCloseClick = onCloseClick,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
|
|
@ -7,11 +8,16 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import com.google.android.play.core.review.ReviewInfo
|
||||
import com.google.android.play.core.review.ReviewManager
|
||||
import com.google.android.play.core.review.ReviewManagerFactory
|
||||
import com.google.android.play.core.tasks.Task
|
||||
import com.tangem.core.ui.event.EventEffect
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletAlertState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent
|
||||
import timber.log.Timber
|
||||
|
||||
@Composable
|
||||
internal fun WalletEventEffect(
|
||||
|
|
@ -46,7 +52,40 @@ internal fun WalletEventEffect(
|
|||
WalletAlertState.WalletAlreadySignedHashes(onUnderstandClick = value.onUnderstandClick),
|
||||
)
|
||||
}
|
||||
is WalletEvent.RateApp -> {
|
||||
val reviewManager = ReviewManagerFactory.create(context)
|
||||
val requestTask = reviewManager.requestReviewFlow()
|
||||
|
||||
requestTask
|
||||
.addOnCompleteListener {
|
||||
handleOnCompleteRequestTask(
|
||||
reviewManager = reviewManager,
|
||||
activity = context as? Activity ?: return@addOnCompleteListener,
|
||||
task = it,
|
||||
onDismissClick = value.onDismissClick,
|
||||
)
|
||||
}
|
||||
.addOnFailureListener(Timber::e)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleOnCompleteRequestTask(
|
||||
reviewManager: ReviewManager,
|
||||
activity: Activity,
|
||||
task: Task<ReviewInfo>,
|
||||
onDismissClick: () -> Unit,
|
||||
) {
|
||||
if (task.isSuccessful) {
|
||||
val reviewFlow = reviewManager.launchReviewFlow(activity, task.result)
|
||||
reviewFlow
|
||||
.addOnCompleteListener { resultReviewTask ->
|
||||
if (!resultReviewTask.isSuccessful) onDismissClick()
|
||||
}
|
||||
.addOnFailureListener(Timber::e)
|
||||
} else {
|
||||
Timber.e(task.exception)
|
||||
}
|
||||
}
|
||||
|
|
@ -19,9 +19,11 @@ internal interface WalletClickIntents {
|
|||
|
||||
fun onMultiWalletSignedHashesNotificationClick()
|
||||
|
||||
fun onLikeTangemAppClick()
|
||||
fun onLikeAppClick()
|
||||
|
||||
fun onRateTheAppClick()
|
||||
fun onDislikeAppClick()
|
||||
|
||||
fun onCloseRateAppNotificationClick()
|
||||
|
||||
fun onWalletChange(index: Int)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,22 +3,21 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels
|
|||
import com.tangem.domain.card.WasCardScannedUseCase
|
||||
import com.tangem.domain.common.CardTypesResolver
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.settings.IsUserAlreadyRateAppUseCase
|
||||
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlin.collections.count
|
||||
|
||||
/**
|
||||
* Wallet notifications list factory
|
||||
*
|
||||
* @property isDemoCardUseCase use case that check if card is demo
|
||||
* @property isUserAlreadyRateAppUseCase use case that check if card is user already rate app
|
||||
* @property isReadyToShowRateAppUseCase use case that check if card is user already rate app
|
||||
* @property wasCardScannedUseCase use case that check if card was scanned
|
||||
* @property clickIntents screen click intents
|
||||
*
|
||||
|
|
@ -26,7 +25,7 @@ import kotlin.collections.count
|
|||
*/
|
||||
internal class WalletNotificationsListFactory(
|
||||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
private val isUserAlreadyRateAppUseCase: IsUserAlreadyRateAppUseCase,
|
||||
private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
|
||||
private val wasCardScannedUseCase: WasCardScannedUseCase,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
) {
|
||||
|
|
@ -35,19 +34,20 @@ internal class WalletNotificationsListFactory(
|
|||
cardTypesResolver: CardTypesResolver,
|
||||
cryptoCurrencyList: List<CryptoCurrencyStatus>,
|
||||
): Flow<ImmutableList<WalletNotification>> {
|
||||
return wasCardScannedUseCase.invoke(cardTypesResolver.getCardId())
|
||||
.distinctUntilChanged()
|
||||
.map {
|
||||
buildList {
|
||||
addCriticalNotifications(cardTypesResolver)
|
||||
return combine(
|
||||
flow = wasCardScannedUseCase(cardTypesResolver.getCardId()),
|
||||
flow2 = isReadyToShowRateAppUseCase(),
|
||||
) { wasCardScanned, isReadyToShowRating ->
|
||||
buildList {
|
||||
addCriticalNotifications(cardTypesResolver)
|
||||
|
||||
addMissingAddressesNotification(cryptoCurrencyList)
|
||||
addMissingAddressesNotification(cryptoCurrencyList)
|
||||
|
||||
addRateTheAppNotification()
|
||||
addRateTheAppNotification(isReadyToShowRating)
|
||||
|
||||
addWarningNotifications(cardTypesResolver, cryptoCurrencyList, it)
|
||||
}.toImmutableList()
|
||||
}
|
||||
addWarningNotifications(cardTypesResolver, cryptoCurrencyList, wasCardScanned)
|
||||
}.toImmutableList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addCriticalNotifications(cardTypesResolver: CardTypesResolver) {
|
||||
|
|
@ -101,15 +101,14 @@ internal class WalletNotificationsListFactory(
|
|||
.map(CryptoCurrencyStatus::currency)
|
||||
}
|
||||
|
||||
// TODO: [REDACTED_JIRA]
|
||||
private suspend fun MutableList<WalletNotification>.addRateTheAppNotification() {
|
||||
private fun MutableList<WalletNotification>.addRateTheAppNotification(isReadyToShowRating: Boolean) {
|
||||
addIf(
|
||||
element = WalletNotification.RateApp(
|
||||
onPositiveClick = {},
|
||||
onNegativeClick = {},
|
||||
onCloseClick = {},
|
||||
onLikeClick = clickIntents::onLikeAppClick,
|
||||
onDislikeClick = clickIntents::onDislikeAppClick,
|
||||
onCloseClick = clickIntents::onCloseRateAppNotificationClick,
|
||||
),
|
||||
condition = isUserAlreadyRateAppUseCase(),
|
||||
condition = isReadyToShowRating,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import com.tangem.common.card.EllipticCurve
|
|||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.common.extensions.toMapKey
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
|
|
@ -30,13 +31,15 @@ import com.tangem.domain.common.util.cardTypesResolver
|
|||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.redux.LegacyAction
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.settings.CanUseBiometryUseCase
|
||||
import com.tangem.domain.settings.IsUserAlreadyRateAppUseCase
|
||||
import com.tangem.domain.settings.ShouldShowSaveWalletScreenUseCase
|
||||
import com.tangem.domain.settings.*
|
||||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.model.*
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.NetworkGroup
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
|
||||
import com.tangem.domain.userwallets.UserWalletBuilder
|
||||
|
|
@ -107,8 +110,11 @@ internal class WalletViewModel @Inject constructor(
|
|||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||
private val setCardWasScannedUseCase: SetCardWasScannedUseCase,
|
||||
private val remindToRateAppLaterUseCase: RemindToRateAppLaterUseCase,
|
||||
private val neverToSuggestRateAppUseCase: NeverToSuggestRateAppUseCase,
|
||||
private val setWalletWithFundsFoundUseCase: SetWalletWithFundsFoundUseCase,
|
||||
wasCardScannedUseCase: WasCardScannedUseCase,
|
||||
isUserAlreadyRateAppUseCase: IsUserAlreadyRateAppUseCase,
|
||||
isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
|
||||
isDemoCardUseCase: IsDemoCardUseCase,
|
||||
// endregion Parameters
|
||||
) : ViewModel(), DefaultLifecycleObserver, WalletClickIntents {
|
||||
|
|
@ -121,7 +127,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
|
||||
private val notificationsListFactory = WalletNotificationsListFactory(
|
||||
wasCardScannedUseCase = wasCardScannedUseCase,
|
||||
isUserAlreadyRateAppUseCase = isUserAlreadyRateAppUseCase,
|
||||
isReadyToShowRateAppUseCase = isReadyToShowRateAppUseCase,
|
||||
isDemoCardUseCase = isDemoCardUseCase,
|
||||
clickIntents = this,
|
||||
)
|
||||
|
|
@ -431,12 +437,32 @@ internal class WalletViewModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
override fun onLikeTangemAppClick() {
|
||||
// TODO: [REDACTED_JIRA]
|
||||
override fun onLikeAppClick() {
|
||||
uiState = stateFactory.getStateAndTriggerEvent(
|
||||
state = uiState,
|
||||
event = WalletEvent.RateApp(
|
||||
onDismissClick = {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
neverToSuggestRateAppUseCase()
|
||||
}
|
||||
},
|
||||
),
|
||||
setUiState = { uiState = it },
|
||||
)
|
||||
}
|
||||
|
||||
override fun onRateTheAppClick() {
|
||||
// TODO: [REDACTED_JIRA]
|
||||
override fun onDislikeAppClick() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
neverToSuggestRateAppUseCase()
|
||||
|
||||
reduxStateHolder.dispatch(LegacyAction.SendEmailRateCanBeBetter)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCloseRateAppNotificationClick() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
remindToRateAppLaterUseCase()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onWalletChange(index: Int) {
|
||||
|
|
@ -780,6 +806,8 @@ internal class WalletViewModel @Inject constructor(
|
|||
.onEach { maybeTokenList ->
|
||||
uiState = stateFactory.getStateByTokensList(maybeTokenList)
|
||||
|
||||
maybeTokenList.onRight { checkMultiWalletWithFunds(it) }
|
||||
|
||||
updateNotifications(
|
||||
index = walletIndex,
|
||||
tokenList = maybeTokenList.fold(ifLeft = { null }, ifRight = { it }),
|
||||
|
|
@ -790,6 +818,29 @@ internal class WalletViewModel @Inject constructor(
|
|||
.saveIn(tokensJobHolder)
|
||||
}
|
||||
|
||||
private suspend fun checkMultiWalletWithFunds(tokenList: TokenList) {
|
||||
val hasNonZeroWallets = when (tokenList) {
|
||||
is TokenList.GroupedByNetwork -> {
|
||||
tokenList.groups
|
||||
.flatMap(NetworkGroup::currencies)
|
||||
.hasNonZeroWallets()
|
||||
}
|
||||
is TokenList.Ungrouped -> tokenList.currencies.hasNonZeroWallets()
|
||||
TokenList.NotInitialized -> false
|
||||
}
|
||||
|
||||
if (hasNonZeroWallets) {
|
||||
setWalletWithFundsFoundUseCase()
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<CryptoCurrencyStatus>.hasNonZeroWallets(): Boolean {
|
||||
return any {
|
||||
val amount = it.value.amount ?: return@any false
|
||||
!amount.isZero()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSingleCurrencyContent(index: Int) {
|
||||
val wallet = getWallet(index)
|
||||
updatePrimaryCurrencyStatus(userWalletId = wallet.walletId)
|
||||
|
|
@ -822,6 +873,11 @@ internal class WalletViewModel @Inject constructor(
|
|||
|
||||
maybeCryptoCurrencyStatus.onRight { status ->
|
||||
singleWalletCryptoCurrencyStatus = status
|
||||
|
||||
if (status.value.amount?.isZero() == false) {
|
||||
setWalletWithFundsFoundUseCase()
|
||||
}
|
||||
|
||||
updateButtons(userWalletId = userWalletId, currencyStatus = status)
|
||||
updateTxHistory(status.currency)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue