Updated on 2026-08-14
This commit is contained in:
parent
9a02d6a58a
commit
6c850776d5
37 changed files with 611 additions and 104 deletions
|
|
@ -28,6 +28,7 @@ import com.tangem.domain.apptheme.repository.AppThemeModeRepository
|
|||
import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository
|
||||
import com.tangem.domain.card.ScanCardProcessor
|
||||
import com.tangem.domain.common.LogConfig
|
||||
import com.tangem.domain.settings.repositories.AppRatingRepository
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.legacy.WalletManagersRepository
|
||||
|
|
@ -196,15 +197,16 @@ internal class TapApplication : Application(), ImageLoaderFactory {
|
|||
|
||||
@Inject
|
||||
lateinit var userTokensStore: UserTokensStore
|
||||
|
||||
@Inject
|
||||
lateinit var appRatingRepository: AppRatingRepository
|
||||
// endregion Injected
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
store = Store(
|
||||
reducer = { action, state ->
|
||||
appReducer(action, state, appStateHolder)
|
||||
},
|
||||
reducer = { action, state -> appReducer(action, state, appStateHolder) },
|
||||
middleware = AppState.getMiddleware(),
|
||||
state = AppState(
|
||||
daggerGraphState = DaggerGraphState(
|
||||
|
|
@ -277,6 +279,7 @@ internal class TapApplication : Application(), ImageLoaderFactory {
|
|||
// [REDACTED_JIRA]
|
||||
runBlocking {
|
||||
featureTogglesManager.init()
|
||||
appRatingRepository.initialize()
|
||||
// learn2earnInteractor.init()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.domain.redux.domainStore
|
|||
import com.tangem.domain.redux.global.NetworkServices
|
||||
import com.tangem.tap.common.redux.global.GlobalMiddleware
|
||||
import com.tangem.tap.common.redux.global.GlobalState
|
||||
import com.tangem.tap.common.redux.legacy.LegacyMiddleware
|
||||
import com.tangem.tap.common.redux.navigation.navigationMiddleware
|
||||
import com.tangem.tap.features.details.redux.DetailsMiddleware
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
|
|
@ -71,7 +72,7 @@ data class AppState(
|
|||
val daggerGraphState: DaggerGraphState = DaggerGraphState(),
|
||||
) : StateType {
|
||||
|
||||
val domainState: DomainState
|
||||
private val domainState: DomainState
|
||||
get() = domainStore.state
|
||||
|
||||
val domainNetworks: NetworkServices
|
||||
|
|
@ -111,6 +112,7 @@ data class AppState(
|
|||
SprinklrMiddleware().middleware,
|
||||
SignInMiddleware.middleware,
|
||||
DaggerGraphMiddleware.daggerGraphMiddleware,
|
||||
LegacyMiddleware.legacyMiddleware,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.tap.common.redux.legacy
|
||||
|
||||
import com.tangem.domain.redux.LegacyAction
|
||||
import com.tangem.tap.common.feedback.RateCanBeBetterEmail
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.store
|
||||
import org.rekotlin.Middleware
|
||||
|
||||
internal object LegacyMiddleware {
|
||||
val legacyMiddleware: Middleware<AppState> = { _, _ ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
when (action) {
|
||||
is LegacyAction.SendEmailRateCanBeBetter -> {
|
||||
store.state.globalState.feedbackManager?.sendEmail(RateCanBeBetterEmail())
|
||||
}
|
||||
}
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import com.tangem.domain.balancehiding.IsBalanceHiddenUseCase
|
|||
import com.tangem.domain.balancehiding.ListenToFlipsUseCase
|
||||
import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository
|
||||
import com.tangem.domain.settings.*
|
||||
import com.tangem.domain.settings.repositories.AppRatingRepository
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.domain.settings.DefaultLegacySettingsRepository
|
||||
|
|
@ -20,8 +21,28 @@ internal object SettingsDomainModule {
|
|||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun providesGetWalletsUseCase(settingsRepository: SettingsRepository): IsUserAlreadyRateAppUseCase {
|
||||
return IsUserAlreadyRateAppUseCase(settingsRepository = settingsRepository)
|
||||
fun providesIsReadyToShowRatingUseCase(appRatingRepository: AppRatingRepository): IsReadyToShowRateAppUseCase {
|
||||
return IsReadyToShowRateAppUseCase(appRatingRepository = appRatingRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun providesRemindToRateAppLaterUseCase(appRatingRepository: AppRatingRepository): RemindToRateAppLaterUseCase {
|
||||
return RemindToRateAppLaterUseCase(appRatingRepository = appRatingRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun providesNeverToSuggestRateAppUseCase(appRatingRepository: AppRatingRepository): NeverToSuggestRateAppUseCase {
|
||||
return NeverToSuggestRateAppUseCase(appRatingRepository = appRatingRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun providesSetWalletWithFundsFoundUseCase(
|
||||
appRatingRepository: AppRatingRepository,
|
||||
): SetWalletWithFundsFoundUseCase {
|
||||
return SetWalletWithFundsFoundUseCase(appRatingRepository = appRatingRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import com.tangem.datasource.local.appcurrency.AvailableAppCurrenciesStore
|
|||
import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore
|
||||
import com.tangem.datasource.local.appcurrency.implementation.DefaultAvailableAppCurrenciesStore
|
||||
import com.tangem.datasource.local.appcurrency.implementation.DefaultSelectedAppCurrencyStore
|
||||
import com.tangem.datasource.local.datastore.JsonSharedPreferencesDataStore
|
||||
import com.tangem.datasource.local.datastore.RuntimeDataStore
|
||||
import com.tangem.datasource.local.datastore.SharedPreferencesDataStore
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -35,7 +35,7 @@ internal object AppCurrencyDataModule {
|
|||
@NetworkMoshi moshi: Moshi,
|
||||
): SelectedAppCurrencyStore {
|
||||
return DefaultSelectedAppCurrencyStore(
|
||||
dataStore = SharedPreferencesDataStore(
|
||||
dataStore = JsonSharedPreferencesDataStore(
|
||||
preferencesName = "selected_app_currency",
|
||||
context = context,
|
||||
adapter = moshi.adapter(CurrenciesResponse.Currency::class.java),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import android.content.Context
|
|||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.datasource.local.apptheme.AppThemeModeStore
|
||||
import com.tangem.datasource.local.apptheme.DefaultAppThemeModeStore
|
||||
import com.tangem.datasource.local.datastore.SharedPreferencesDataStore
|
||||
import com.tangem.datasource.local.datastore.JsonSharedPreferencesDataStore
|
||||
import com.tangem.domain.apptheme.model.AppThemeMode
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -19,7 +19,7 @@ internal object AppThemeModeDataModule {
|
|||
@Provides
|
||||
fun provideAppThemeModeStore(@ApplicationContext context: Context, @NetworkMoshi moshi: Moshi): AppThemeModeStore {
|
||||
return DefaultAppThemeModeStore(
|
||||
dataStore = SharedPreferencesDataStore(
|
||||
dataStore = JsonSharedPreferencesDataStore(
|
||||
preferencesName = "app_theme",
|
||||
context = context,
|
||||
adapter = moshi.adapter(AppThemeMode::class.java),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import com.squareup.moshi.Types
|
|||
import com.tangem.datasource.local.card.DefaultUsedCardsStore
|
||||
import com.tangem.datasource.local.card.UsedCardInfo
|
||||
import com.tangem.datasource.local.card.UsedCardsStore
|
||||
import com.tangem.datasource.local.datastore.SharedPreferencesDataStore
|
||||
import com.tangem.datasource.local.datastore.JsonSharedPreferencesDataStore
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -22,7 +22,7 @@ internal object CardDataModule {
|
|||
@Singleton
|
||||
fun provideUsedCardsStore(@ApplicationContext context: Context, @NetworkMoshi moshi: Moshi): UsedCardsStore {
|
||||
return DefaultUsedCardsStore(
|
||||
store = SharedPreferencesDataStore(
|
||||
store = JsonSharedPreferencesDataStore(
|
||||
preferencesName = "tapPrefs",
|
||||
context = context,
|
||||
adapter = moshi.adapter(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import android.content.Context
|
|||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.datasource.local.appcurrency.BalanceHidingSettingsStore
|
||||
import com.tangem.datasource.local.appcurrency.implementation.BalanceStateHidingSettingsStore
|
||||
import com.tangem.datasource.local.datastore.SharedPreferencesDataStore
|
||||
import com.tangem.datasource.local.datastore.JsonSharedPreferencesDataStore
|
||||
import com.tangem.domain.balancehiding.BalanceHidingSettings
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -22,7 +22,7 @@ internal object HiddenBalanceDataModule {
|
|||
@NetworkMoshi moshi: Moshi,
|
||||
): BalanceHidingSettingsStore {
|
||||
return BalanceStateHidingSettingsStore(
|
||||
dataStore = SharedPreferencesDataStore(
|
||||
dataStore = JsonSharedPreferencesDataStore(
|
||||
preferencesName = "balance_hiding_settings",
|
||||
context = context,
|
||||
adapter = moshi.adapter(BalanceHidingSettings::class.java),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.datasource.di
|
|||
|
||||
import android.content.Context
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.datasource.local.datastore.SharedPreferencesDataStore
|
||||
import com.tangem.datasource.local.datastore.JsonSharedPreferencesDataStore
|
||||
import com.tangem.datasource.local.quote.DefaultQuotesStore
|
||||
import com.tangem.datasource.local.quote.QuotesStore
|
||||
import com.tangem.datasource.local.quote.model.StoredQuote
|
||||
|
|
@ -21,7 +21,7 @@ internal object QuotesStoreModule {
|
|||
@Singleton
|
||||
fun provideQuotesStore(@ApplicationContext context: Context, @NetworkMoshi moshi: Moshi): QuotesStore {
|
||||
return DefaultQuotesStore(
|
||||
dataStore = SharedPreferencesDataStore(
|
||||
dataStore = JsonSharedPreferencesDataStore(
|
||||
preferencesName = "quotes",
|
||||
context = context,
|
||||
adapter = moshi.adapter(StoredQuote::class.java),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
package com.tangem.datasource.di
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.datasource.local.datastore.BooleanSharedPreferencesDataStore
|
||||
import com.tangem.datasource.local.datastore.IntSharedPreferencesDataStore
|
||||
import com.tangem.datasource.local.datastore.LongSharedPreferencesDataStore
|
||||
import com.tangem.datasource.local.settings.*
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object SettingsDataModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAppLaunchCountStore(@ApplicationContext context: Context): AppLaunchCountStore {
|
||||
return DefaultAppLaunchCountStore(
|
||||
store = IntSharedPreferencesDataStore(preferencesName = "tapPrefs", context = context),
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAppRatingShowingCountStore(@ApplicationContext context: Context): AppRatingShowingCountStore {
|
||||
return DefaultAppRatingShowingCountStore(
|
||||
store = IntSharedPreferencesDataStore(preferencesName = "tapPrefs", context = context),
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideFundsFoundDateInMillisStore(@ApplicationContext context: Context): FundsFoundDateInMillisStore {
|
||||
return DefaultFundsFoundDateInMillisStore(
|
||||
store = LongSharedPreferencesDataStore(preferencesName = "tapPrefs", context = context),
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideUserInteractingStatusStore(@ApplicationContext context: Context): UserInteractingStatusStore {
|
||||
return DefaultUserInteractingStatusStore(
|
||||
store = BooleanSharedPreferencesDataStore(preferencesName = "tapPrefs", context = context),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.datasource.local.datastore
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.content.edit
|
||||
|
||||
internal class BooleanSharedPreferencesDataStore(
|
||||
preferencesName: String,
|
||||
context: Context,
|
||||
) : SharedPreferencesDataStore<Boolean>(preferencesName, context) {
|
||||
|
||||
override fun getByKey(key: String): Boolean = sharedPreferences.getBoolean(key, false)
|
||||
|
||||
override fun storeByKey(key: String, value: Boolean) {
|
||||
sharedPreferences.edit { putBoolean(key, value) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.datasource.local.datastore
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.content.edit
|
||||
|
||||
internal class IntSharedPreferencesDataStore(
|
||||
preferencesName: String,
|
||||
context: Context,
|
||||
) : SharedPreferencesDataStore<Int>(preferencesName, context) {
|
||||
|
||||
override fun getByKey(key: String): Int = sharedPreferences.getInt(key, 0)
|
||||
|
||||
override fun storeByKey(key: String, value: Int) {
|
||||
sharedPreferences.edit { putInt(key, value) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.datasource.local.datastore
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.content.edit
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
|
||||
internal class JsonSharedPreferencesDataStore<Value : Any>(
|
||||
preferencesName: String,
|
||||
context: Context,
|
||||
private val adapter: JsonAdapter<Value>,
|
||||
) : SharedPreferencesDataStore<Value>(preferencesName, context) {
|
||||
|
||||
override fun getByKey(key: String): Value? {
|
||||
val json = sharedPreferences.getString(key, null) ?: return null
|
||||
|
||||
return adapter.fromJson(json)
|
||||
}
|
||||
|
||||
override fun storeByKey(key: String, value: Value) {
|
||||
val json = adapter.toJson(value)
|
||||
|
||||
sharedPreferences.edit { putString(key, json) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.datasource.local.datastore
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.content.edit
|
||||
|
||||
internal class LongSharedPreferencesDataStore(
|
||||
preferencesName: String,
|
||||
context: Context,
|
||||
) : SharedPreferencesDataStore<Long>(preferencesName, context) {
|
||||
|
||||
override fun getByKey(key: String): Long = sharedPreferences.getLong(key, 0)
|
||||
|
||||
override fun storeByKey(key: String, value: Long) {
|
||||
sharedPreferences.edit { putLong(key, value) }
|
||||
}
|
||||
}
|
||||
|
|
@ -4,53 +4,47 @@ import android.content.Context
|
|||
import android.content.Context.MODE_PRIVATE
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
||||
import com.tangem.datasource.local.datastore.utils.Trigger
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import timber.log.Timber
|
||||
|
||||
internal class SharedPreferencesDataStore<Value : Any>(
|
||||
internal abstract class SharedPreferencesDataStore<Value : Any>(
|
||||
preferencesName: String,
|
||||
private val context: Context,
|
||||
private val adapter: JsonAdapter<Value>,
|
||||
context: Context,
|
||||
) : StringKeyDataStore<Value> {
|
||||
|
||||
private val sharedPreferences: SharedPreferences by lazy {
|
||||
protected val sharedPreferences: SharedPreferences by lazy {
|
||||
context.getSharedPreferences(preferencesName, MODE_PRIVATE)
|
||||
}
|
||||
|
||||
private val writeTrigger = Trigger()
|
||||
|
||||
abstract fun getByKey(key: String): Value?
|
||||
|
||||
abstract fun storeByKey(key: String, value: Value)
|
||||
|
||||
override fun get(key: String): Flow<Value> {
|
||||
return writeTrigger
|
||||
.map { getInternal(key) }
|
||||
.filterNotNull()
|
||||
.mapNotNull { getInternal(key) }
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
|
||||
override fun getAll(): Flow<List<Value>> {
|
||||
return writeTrigger
|
||||
.map { getAllInternal() }
|
||||
.distinctUntilChanged()
|
||||
throw UnsupportedOperationException("Unknown key")
|
||||
}
|
||||
|
||||
override suspend fun getSyncOrNull(key: String): Value? {
|
||||
return getInternal(key)
|
||||
}
|
||||
override suspend fun getSyncOrNull(key: String): Value? = getInternal(key)
|
||||
|
||||
override suspend fun getAllSyncOrNull(): List<Value> {
|
||||
return getAllInternal()
|
||||
throw UnsupportedOperationException("Unknown key")
|
||||
}
|
||||
|
||||
override suspend fun store(key: String, value: Value) {
|
||||
try {
|
||||
val json = adapter.toJson(value)
|
||||
|
||||
sharedPreferences.edit { putString(key, json) }
|
||||
storeByKey(key, value)
|
||||
writeTrigger.trigger()
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e, "Unable to edit preferences: $key")
|
||||
|
|
@ -74,26 +68,13 @@ internal class SharedPreferencesDataStore<Value : Any>(
|
|||
}
|
||||
|
||||
private fun getInternal(key: String): Value? {
|
||||
return try {
|
||||
val json = sharedPreferences.getString(key, null) ?: return null
|
||||
if (!sharedPreferences.contains(key)) return null
|
||||
|
||||
adapter.fromJson(json)
|
||||
return try {
|
||||
getByKey(key)
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e, "Unable to get value from preferences: $key")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAllInternal(): List<Value> {
|
||||
return sharedPreferences.all.mapNotNull { (key, value) ->
|
||||
try {
|
||||
val json = value as? String ?: return@mapNotNull null
|
||||
|
||||
adapter.fromJson(json)
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e, "Unable to convert value from JSON: $key")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.datasource.local.settings
|
||||
|
||||
import com.tangem.datasource.local.datastore.IntSharedPreferencesDataStore
|
||||
import com.tangem.datasource.local.datastore.core.KeylessDataStoreDecorator
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface AppLaunchCountStore {
|
||||
|
||||
fun get(): Flow<Int>
|
||||
|
||||
suspend fun getSyncOrNull(): Int?
|
||||
|
||||
suspend fun store(item: Int)
|
||||
}
|
||||
|
||||
internal class DefaultAppLaunchCountStore(
|
||||
store: IntSharedPreferencesDataStore,
|
||||
) : AppLaunchCountStore, KeylessDataStoreDecorator<Int>(
|
||||
wrappedDataStore = store,
|
||||
key = "launchCount",
|
||||
)
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.datasource.local.settings
|
||||
|
||||
import com.tangem.datasource.local.datastore.IntSharedPreferencesDataStore
|
||||
import com.tangem.datasource.local.datastore.core.KeylessDataStoreDecorator
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface AppRatingShowingCountStore {
|
||||
|
||||
fun get(): Flow<Int>
|
||||
|
||||
suspend fun getSyncOrNull(): Int?
|
||||
|
||||
suspend fun store(item: Int)
|
||||
}
|
||||
|
||||
internal class DefaultAppRatingShowingCountStore(
|
||||
store: IntSharedPreferencesDataStore,
|
||||
) : AppRatingShowingCountStore, KeylessDataStoreDecorator<Int>(
|
||||
wrappedDataStore = store,
|
||||
key = "showRatingDialogAtLaunchCount",
|
||||
)
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.datasource.local.settings
|
||||
|
||||
import com.tangem.datasource.local.datastore.LongSharedPreferencesDataStore
|
||||
import com.tangem.datasource.local.datastore.core.KeylessDataStoreDecorator
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface FundsFoundDateInMillisStore {
|
||||
|
||||
fun get(): Flow<Long>
|
||||
|
||||
suspend fun getSyncOrNull(): Long?
|
||||
|
||||
suspend fun store(item: Long)
|
||||
}
|
||||
|
||||
internal class DefaultFundsFoundDateInMillisStore(
|
||||
store: LongSharedPreferencesDataStore,
|
||||
) : FundsFoundDateInMillisStore, KeylessDataStoreDecorator<Long>(
|
||||
wrappedDataStore = store,
|
||||
key = "fundsFoundDate",
|
||||
)
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.datasource.local.settings
|
||||
|
||||
import com.tangem.datasource.local.datastore.BooleanSharedPreferencesDataStore
|
||||
import com.tangem.datasource.local.datastore.core.KeylessDataStoreDecorator
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface UserInteractingStatusStore {
|
||||
|
||||
fun get(): Flow<Boolean>
|
||||
|
||||
suspend fun getSyncOrNull(): Boolean?
|
||||
|
||||
suspend fun store(item: Boolean)
|
||||
}
|
||||
|
||||
internal class DefaultUserInteractingStatusStore(
|
||||
store: BooleanSharedPreferencesDataStore,
|
||||
) : UserInteractingStatusStore, KeylessDataStoreDecorator<Boolean>(
|
||||
wrappedDataStore = store,
|
||||
key = "userWasInteractWithRating",
|
||||
)
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
package com.tangem.data.settings
|
||||
|
||||
import com.tangem.datasource.local.settings.AppLaunchCountStore
|
||||
import com.tangem.datasource.local.settings.AppRatingShowingCountStore
|
||||
import com.tangem.datasource.local.settings.FundsFoundDateInMillisStore
|
||||
import com.tangem.datasource.local.settings.UserInteractingStatusStore
|
||||
import com.tangem.domain.settings.repositories.AppRatingRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.Calendar
|
||||
|
||||
internal class DefaultAppRatingRepository(
|
||||
private val fundsFoundDateInMillisStore: FundsFoundDateInMillisStore,
|
||||
private val appLaunchCountStore: AppLaunchCountStore,
|
||||
private val appRatingShowingCountStore: AppRatingShowingCountStore,
|
||||
private val userInteractingStatusStore: UserInteractingStatusStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : AppRatingRepository {
|
||||
|
||||
override suspend fun initialize() {
|
||||
fundsFoundDateInMillisStore.getSyncOrNull()
|
||||
?: fundsFoundDateInMillisStore.store(item = FUNDS_FOUND_DATE_UNDEFINED)
|
||||
|
||||
appLaunchCountStore.getSyncOrNull()
|
||||
?: appLaunchCountStore.store(item = DEFAULT_APP_LAUNCH_COUNT)
|
||||
|
||||
appRatingShowingCountStore.getSyncOrNull()
|
||||
?: appRatingShowingCountStore.store(item = FIRST_SHOWING_COUNT)
|
||||
|
||||
userInteractingStatusStore.getSyncOrNull()
|
||||
?: userInteractingStatusStore.store(item = false)
|
||||
}
|
||||
|
||||
override suspend fun setWalletWithFundsFound() {
|
||||
withContext(dispatchers.io) {
|
||||
val foundDate = fundsFoundDateInMillisStore.getSyncOrNull()
|
||||
if (foundDate != null && foundDate != FUNDS_FOUND_DATE_UNDEFINED) return@withContext
|
||||
|
||||
fundsFoundDateInMillisStore.store(item = Calendar.getInstance().timeInMillis)
|
||||
|
||||
val appLaunchCount = appLaunchCountStore.getSyncOrNull().let { count ->
|
||||
if (count == null) {
|
||||
appLaunchCountStore.store(item = DEFAULT_APP_LAUNCH_COUNT)
|
||||
DEFAULT_APP_LAUNCH_COUNT
|
||||
} else {
|
||||
count
|
||||
}
|
||||
}
|
||||
|
||||
appRatingShowingCountStore.store(item = appLaunchCount + FIRST_SHOWING_COUNT)
|
||||
|
||||
userInteractingStatusStore.getSyncOrNull()
|
||||
?: userInteractingStatusStore.store(item = false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isReadyToShow(): Flow<Boolean> {
|
||||
// TODO: [REDACTED_JIRA]
|
||||
return combine(
|
||||
userInteractingStatusStore.get(),
|
||||
appRatingShowingCountStore.get(),
|
||||
appLaunchCountStore.get(),
|
||||
fundsFoundDateInMillisStore.get(),
|
||||
) { isInteracting, ratingShowingCount, appLaunchCount, fundsFoundDate ->
|
||||
if (!isInteracting) {
|
||||
val diff = Calendar.getInstance().timeInMillis - fundsFoundDate
|
||||
val diffInDays = diff / DAY_IN_MILLIS
|
||||
|
||||
appLaunchCount >= ratingShowingCount && diffInDays >= FIRST_SHOWING_COUNT
|
||||
} else {
|
||||
appLaunchCount >= ratingShowingCount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun remindLater() {
|
||||
appLaunchCountStore.getSyncOrNull()?.let {
|
||||
updateNextShowing(at = it + DEFER_SHOWING_COUNT)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun setNeverToShow() {
|
||||
updateNextShowing(at = Int.MAX_VALUE)
|
||||
}
|
||||
|
||||
private suspend fun updateNextShowing(at: Int) {
|
||||
withContext(dispatchers.io) {
|
||||
appRatingShowingCountStore.store(item = at)
|
||||
userInteractingStatusStore.store(item = true)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val FUNDS_FOUND_DATE_UNDEFINED = -1L
|
||||
const val DEFAULT_APP_LAUNCH_COUNT = 1
|
||||
const val FIRST_SHOWING_COUNT = 3
|
||||
const val DEFER_SHOWING_COUNT = 20
|
||||
const val DAY_IN_MILLIS = 1000 * 60 * 60 * 24
|
||||
}
|
||||
}
|
||||
|
|
@ -10,12 +10,6 @@ internal class DefaultSettingsRepository(
|
|||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : SettingsRepository {
|
||||
|
||||
override suspend fun isUserAlreadyRateApp(): Boolean {
|
||||
return withContext(dispatchers.io) {
|
||||
preferencesDataSource.appRatingLaunchObserver.isReadyToShow()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun shouldShowSaveUserWalletScreen(): Boolean {
|
||||
return withContext(dispatchers.io) { preferencesDataSource.shouldShowSaveUserWalletScreen }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
package com.tangem.data.settings.di
|
||||
|
||||
import com.tangem.data.settings.DefaultAppRatingRepository
|
||||
import com.tangem.data.settings.DefaultSettingsRepository
|
||||
import com.tangem.data.source.preferences.PreferencesDataSource
|
||||
import com.tangem.datasource.local.settings.AppLaunchCountStore
|
||||
import com.tangem.datasource.local.settings.AppRatingShowingCountStore
|
||||
import com.tangem.datasource.local.settings.FundsFoundDateInMillisStore
|
||||
import com.tangem.datasource.local.settings.UserInteractingStatusStore
|
||||
import com.tangem.domain.settings.repositories.AppRatingRepository
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
|
|
@ -25,4 +31,22 @@ internal object SettingsDataModule {
|
|||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAppRatingRepository(
|
||||
fundsFoundDateInMillisStore: FundsFoundDateInMillisStore,
|
||||
appLaunchCountStore: AppLaunchCountStore,
|
||||
appRatingShowingCountStore: AppRatingShowingCountStore,
|
||||
userInteractingStatusStore: UserInteractingStatusStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): AppRatingRepository {
|
||||
return DefaultAppRatingRepository(
|
||||
fundsFoundDateInMillisStore = fundsFoundDateInMillisStore,
|
||||
appLaunchCountStore = appLaunchCountStore,
|
||||
appRatingShowingCountStore = appRatingShowingCountStore,
|
||||
userInteractingStatusStore = userInteractingStatusStore,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.domain.redux
|
||||
|
||||
import org.rekotlin.Action
|
||||
|
||||
sealed interface LegacyAction : Action {
|
||||
|
||||
object SendEmailRateCanBeBetter : LegacyAction
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.domain.settings
|
||||
|
||||
import com.tangem.domain.settings.repositories.AppRatingRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class IsReadyToShowRateAppUseCase(private val appRatingRepository: AppRatingRepository) {
|
||||
|
||||
operator fun invoke(): Flow<Boolean> = appRatingRepository.isReadyToShow()
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package com.tangem.domain.settings
|
||||
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
|
||||
class IsUserAlreadyRateAppUseCase(private val settingsRepository: SettingsRepository) {
|
||||
|
||||
suspend operator fun invoke(): Boolean = settingsRepository.isUserAlreadyRateApp()
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.domain.settings
|
||||
|
||||
import com.tangem.domain.settings.repositories.AppRatingRepository
|
||||
|
||||
class NeverToSuggestRateAppUseCase(private val appRatingRepository: AppRatingRepository) {
|
||||
|
||||
suspend operator fun invoke() {
|
||||
appRatingRepository.setNeverToShow()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.domain.settings
|
||||
|
||||
import com.tangem.domain.settings.repositories.AppRatingRepository
|
||||
|
||||
class RemindToRateAppLaterUseCase(private val appRatingRepository: AppRatingRepository) {
|
||||
|
||||
suspend operator fun invoke() {
|
||||
appRatingRepository.remindLater()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.domain.settings
|
||||
|
||||
import com.tangem.domain.settings.repositories.AppRatingRepository
|
||||
|
||||
class SetWalletWithFundsFoundUseCase(private val appRatingRepository: AppRatingRepository) {
|
||||
|
||||
suspend operator fun invoke() = appRatingRepository.setWalletWithFundsFound()
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.domain.settings.repositories
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface AppRatingRepository {
|
||||
|
||||
// FIXME: We must to initialize all stores before calling [isReadyToShow], otherwise flow will not emit data
|
||||
suspend fun initialize()
|
||||
|
||||
suspend fun setWalletWithFundsFound()
|
||||
|
||||
fun isReadyToShow(): Flow<Boolean>
|
||||
|
||||
suspend fun remindLater()
|
||||
|
||||
suspend fun setNeverToShow()
|
||||
}
|
||||
|
|
@ -2,7 +2,5 @@ package com.tangem.domain.settings.repositories
|
|||
|
||||
interface SettingsRepository {
|
||||
|
||||
suspend fun isUserAlreadyRateApp(): Boolean
|
||||
|
||||
suspend fun shouldShowSaveUserWalletScreen(): Boolean
|
||||
}
|
||||
|
|
@ -29,11 +29,13 @@ dependencies {
|
|||
|
||||
/** Other libraries */
|
||||
implementation(deps.arrow.core)
|
||||
implementation(deps.googlePlay.core)
|
||||
implementation(deps.jodatime)
|
||||
implementation(deps.kotlin.immutable.collections)
|
||||
implementation(deps.reKotlin)
|
||||
implementation(deps.tangem.card.core)
|
||||
implementation(deps.tangem.blockchain)
|
||||
implementation(deps.timber)
|
||||
|
||||
/** DI */
|
||||
implementation(deps.hilt.android)
|
||||
|
|
|
|||
|
|
@ -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