Updated on 2026-08-14

This commit is contained in:
Tangem 2023-09-25 09:55:08 +08:00
parent 9a02d6a58a
commit 6c850776d5
37 changed files with 611 additions and 104 deletions

View file

@ -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()
}

View file

@ -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,
),

View file

@ -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)
}
}

View file

@ -19,9 +19,11 @@ internal interface WalletClickIntents {
fun onMultiWalletSignedHashesNotificationClick()
fun onLikeTangemAppClick()
fun onLikeAppClick()
fun onRateTheAppClick()
fun onDislikeAppClick()
fun onCloseRateAppNotificationClick()
fun onWalletChange(index: Int)

View file

@ -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,
)
}

View file

@ -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)
}