Updated on 2026-08-14
This commit is contained in:
parent
e3c0b0b311
commit
fcdfe622f9
27 changed files with 384 additions and 54 deletions
|
|
@ -28,7 +28,9 @@ dependencies {
|
||||||
implementation(projects.domain.card)
|
implementation(projects.domain.card)
|
||||||
implementation(project(":domain:wallets"))
|
implementation(project(":domain:wallets"))
|
||||||
implementation(project(":domain:wallets:models"))
|
implementation(project(":domain:wallets:models"))
|
||||||
|
implementation(projects.domain.settings)
|
||||||
implementation(projects.domain.tokens)
|
implementation(projects.domain.tokens)
|
||||||
|
|
||||||
implementation(project(":common"))
|
implementation(project(":common"))
|
||||||
implementation(project(":core:analytics"))
|
implementation(project(":core:analytics"))
|
||||||
implementation(projects.core.analytics.models)
|
implementation(projects.core.analytics.models)
|
||||||
|
|
@ -42,6 +44,7 @@ dependencies {
|
||||||
implementation(project(":libs:auth"))
|
implementation(project(":libs:auth"))
|
||||||
implementation(project(":data:source:preferences"))
|
implementation(project(":data:source:preferences"))
|
||||||
implementation(projects.data.card)
|
implementation(projects.data.card)
|
||||||
|
implementation(projects.data.settings)
|
||||||
implementation(projects.data.tokens)
|
implementation(projects.data.tokens)
|
||||||
implementation(projects.data.common)
|
implementation(projects.data.common)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
package com.tangem.tap.di.domain
|
package com.tangem.tap.di.domain
|
||||||
|
|
||||||
import com.tangem.domain.card.GetAccessCodeSavingStatusUseCase
|
import com.tangem.domain.card.*
|
||||||
import com.tangem.domain.card.GetBiometricsStatusUseCase
|
import com.tangem.domain.card.repository.CardRepository
|
||||||
import com.tangem.domain.card.ScanCardProcessor
|
|
||||||
import com.tangem.domain.card.SetAccessCodeRequestPolicyUseCase
|
|
||||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||||
import com.tangem.tap.domain.scanCard.DefaultScanCardProcessor
|
import com.tangem.tap.domain.scanCard.DefaultScanCardProcessor
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
|
@ -43,4 +41,10 @@ internal object CardDomainModule {
|
||||||
): GetAccessCodeSavingStatusUseCase {
|
): GetAccessCodeSavingStatusUseCase {
|
||||||
return GetAccessCodeSavingStatusUseCase(cardSdkConfigRepository = cardSdkConfigRepository)
|
return GetAccessCodeSavingStatusUseCase(cardSdkConfigRepository = cardSdkConfigRepository)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideGetCardWasScannedUseCase(cardRepository: CardRepository): GetCardWasScannedUseCase {
|
||||||
|
return GetCardWasScannedUseCase(cardRepository = cardRepository)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.tangem.tap.di.domain
|
||||||
|
|
||||||
|
import com.tangem.domain.settings.IsUserAlreadyRateAppUseCase
|
||||||
|
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.android.components.ViewModelComponent
|
||||||
|
import dagger.hilt.android.scopes.ViewModelScoped
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(ViewModelComponent::class)
|
||||||
|
internal object SettingsDomainModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@ViewModelScoped
|
||||||
|
fun providesGetWalletsUseCase(settingsRepository: SettingsRepository): IsUserAlreadyRateAppUseCase {
|
||||||
|
return IsUserAlreadyRateAppUseCase(settingsRepository = settingsRepository)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.tangem.tap.common.di.domain.wallets
|
package com.tangem.tap.di.domain
|
||||||
|
|
||||||
import com.tangem.domain.wallets.legacy.WalletsStateHolder
|
import com.tangem.domain.wallets.legacy.WalletsStateHolder
|
||||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||||
|
|
@ -11,7 +11,7 @@ import dagger.hilt.android.scopes.ViewModelScoped
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(ViewModelComponent::class)
|
@InstallIn(ViewModelComponent::class)
|
||||||
object WalletsDomainModule {
|
internal object WalletsDomainModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@ViewModelScoped
|
@ViewModelScoped
|
||||||
|
|
@ -13,10 +13,14 @@ android {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(deps.hilt.android)
|
implementation(deps.hilt.android)
|
||||||
kapt(deps.hilt.kapt)
|
kapt(deps.hilt.kapt)
|
||||||
|
|
||||||
implementation(deps.tangem.card.android)
|
implementation(deps.tangem.card.android)
|
||||||
implementation(deps.tangem.card.core)
|
implementation(deps.tangem.card.core)
|
||||||
|
|
||||||
|
implementation(projects.core.utils)
|
||||||
|
|
||||||
implementation(projects.data.source.preferences)
|
implementation(projects.data.source.preferences)
|
||||||
|
|
||||||
implementation(projects.domain.card)
|
implementation(projects.domain.card)
|
||||||
implementation(projects.domain.models)
|
implementation(projects.domain.models)
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.tangem.data.card
|
||||||
|
|
||||||
|
import com.tangem.data.source.preferences.PreferencesDataSource
|
||||||
|
import com.tangem.domain.card.repository.CardRepository
|
||||||
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
internal class DefaultCardRepository(
|
||||||
|
private val preferencesDataSource: PreferencesDataSource,
|
||||||
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
|
) : CardRepository {
|
||||||
|
|
||||||
|
override suspend fun wasCardScanned(cardId: String): Boolean {
|
||||||
|
return withContext(dispatchers.io) { preferencesDataSource.usedCardsPrefStorage.wasScanned(cardId) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
package com.tangem.data.card.di
|
package com.tangem.data.card.di
|
||||||
|
|
||||||
|
import com.tangem.data.card.DefaultCardRepository
|
||||||
import com.tangem.data.card.DefaultCardSdkConfigRepository
|
import com.tangem.data.card.DefaultCardSdkConfigRepository
|
||||||
import com.tangem.data.card.sdk.CardSdkProvider
|
import com.tangem.data.card.sdk.CardSdkProvider
|
||||||
import com.tangem.data.source.preferences.PreferencesDataSource
|
import com.tangem.data.source.preferences.PreferencesDataSource
|
||||||
|
import com.tangem.domain.card.repository.CardRepository
|
||||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||||
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
|
|
@ -25,4 +28,13 @@ internal object CardDataModule {
|
||||||
preferencesDataSource = preferencesDataSource,
|
preferencesDataSource = preferencesDataSource,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideCardRepository(
|
||||||
|
preferencesDataSource: PreferencesDataSource,
|
||||||
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
|
): CardRepository {
|
||||||
|
return DefaultCardRepository(preferencesDataSource = preferencesDataSource, dispatchers = dispatchers)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
1
data/settings/.gitignore
vendored
Normal file
1
data/settings/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/build
|
||||||
26
data/settings/build.gradle.kts
Normal file
26
data/settings/build.gradle.kts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
plugins {
|
||||||
|
alias(deps.plugins.android.library)
|
||||||
|
alias(deps.plugins.kotlin.android)
|
||||||
|
alias(deps.plugins.kotlin.kapt)
|
||||||
|
alias(deps.plugins.hilt.android)
|
||||||
|
id("configuration")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.tangem.data.settings"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
/** DI */
|
||||||
|
implementation(deps.hilt.android)
|
||||||
|
kapt(deps.hilt.kapt)
|
||||||
|
|
||||||
|
implementation(deps.kotlin.coroutines)
|
||||||
|
|
||||||
|
implementation(projects.core.utils)
|
||||||
|
|
||||||
|
implementation(projects.domain.settings)
|
||||||
|
|
||||||
|
implementation(projects.data.source.preferences)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.tangem.data.settings
|
||||||
|
|
||||||
|
import com.tangem.data.source.preferences.PreferencesDataSource
|
||||||
|
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||||
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
internal class DefaultSettingsRepository(
|
||||||
|
private val preferencesDataSource: PreferencesDataSource,
|
||||||
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
|
) : SettingsRepository {
|
||||||
|
|
||||||
|
override suspend fun isUserAlreadyRateApp(): Boolean {
|
||||||
|
return withContext(dispatchers.io) {
|
||||||
|
preferencesDataSource.appRatingLaunchObserver.isReadyToShow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.tangem.data.settings.di
|
||||||
|
|
||||||
|
import com.tangem.data.settings.DefaultSettingsRepository
|
||||||
|
import com.tangem.data.source.preferences.PreferencesDataSource
|
||||||
|
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||||
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
internal object SettingsDataModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideSettingsRepository(
|
||||||
|
preferencesDataSource: PreferencesDataSource,
|
||||||
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
|
): SettingsRepository {
|
||||||
|
return DefaultSettingsRepository(preferencesDataSource = preferencesDataSource, dispatchers = dispatchers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.tangem.domain.card
|
||||||
|
|
||||||
|
import com.tangem.domain.card.repository.CardRepository
|
||||||
|
|
||||||
|
class GetCardWasScannedUseCase(private val cardRepository: CardRepository) {
|
||||||
|
|
||||||
|
suspend operator fun invoke(cardId: String): Boolean = cardRepository.wasCardScanned(cardId)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.tangem.domain.card.repository
|
||||||
|
|
||||||
|
interface CardRepository {
|
||||||
|
|
||||||
|
suspend fun wasCardScanned(cardId: String): Boolean
|
||||||
|
}
|
||||||
|
|
@ -26,4 +26,20 @@ interface CardTypesResolver {
|
||||||
fun getPrimaryToken(): Token?
|
fun getPrimaryToken(): Token?
|
||||||
|
|
||||||
fun getBackupCardsCount(): Int
|
fun getBackupCardsCount(): Int
|
||||||
|
|
||||||
|
fun isReleaseFirmwareType(): Boolean
|
||||||
|
|
||||||
|
fun getRemainingSignatures(): Int?
|
||||||
|
|
||||||
|
fun getCardId(): String
|
||||||
|
|
||||||
|
fun isTestCard(): Boolean
|
||||||
|
|
||||||
|
fun isAttestationFailed(): Boolean
|
||||||
|
|
||||||
|
fun hasWalletSignedHashes(): Boolean
|
||||||
|
|
||||||
|
fun hasBackup(): Boolean
|
||||||
|
|
||||||
|
fun isBackupForbidden(): Boolean
|
||||||
}
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ import com.tangem.domain.common.TapWorkarounds.isStart2Coin
|
||||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||||
import com.tangem.domain.models.scan.CardDTO
|
import com.tangem.domain.models.scan.CardDTO
|
||||||
import com.tangem.domain.models.scan.ProductType
|
import com.tangem.domain.models.scan.ProductType
|
||||||
|
import com.tangem.operations.attestation.Attestation
|
||||||
|
|
||||||
internal class TangemCardTypesResolver(
|
internal class TangemCardTypesResolver(
|
||||||
private val card: CardDTO,
|
private val card: CardDTO,
|
||||||
|
|
@ -73,6 +74,27 @@ internal class TangemCardTypesResolver(
|
||||||
|
|
||||||
override fun getBackupCardsCount(): Int = card.wallets.size
|
override fun getBackupCardsCount(): Int = card.wallets.size
|
||||||
|
|
||||||
|
override fun isReleaseFirmwareType(): Boolean = card.firmwareVersion.type == FirmwareVersion.FirmwareType.Release
|
||||||
|
|
||||||
|
override fun getRemainingSignatures(): Int? = card.wallets.firstOrNull()?.remainingSignatures
|
||||||
|
|
||||||
|
override fun getCardId(): String = card.cardId
|
||||||
|
|
||||||
|
override fun isTestCard(): Boolean = card.isTestCard
|
||||||
|
|
||||||
|
override fun isAttestationFailed(): Boolean = card.attestation.status == Attestation.Status.Failed
|
||||||
|
|
||||||
|
override fun hasWalletSignedHashes(): Boolean {
|
||||||
|
return card.wallets.any {
|
||||||
|
val totalSignedHashes = it.totalSignedHashes ?: 0
|
||||||
|
totalSignedHashes > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasBackup(): Boolean = card.backupStatus != CardDTO.BackupStatus.NoBackup
|
||||||
|
|
||||||
|
override fun isBackupForbidden(): Boolean = !(card.settings.isBackupAllowed || card.settings.isHDWalletAllowed)
|
||||||
|
|
||||||
private fun Blockchain.Companion.fromBlockchainName(blockchainName: String): Blockchain {
|
private fun Blockchain.Companion.fromBlockchainName(blockchainName: String): Blockchain {
|
||||||
// workaround for BSC (BNB) notes cards
|
// workaround for BSC (BNB) notes cards
|
||||||
return when (blockchainName) {
|
return when (blockchainName) {
|
||||||
|
|
|
||||||
1
domain/settings/.gitignore
vendored
Normal file
1
domain/settings/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/build
|
||||||
4
domain/settings/build.gradle.kts
Normal file
4
domain/settings/build.gradle.kts
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
plugins {
|
||||||
|
alias(deps.plugins.kotlin.jvm)
|
||||||
|
id("configuration")
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
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,6 @@
|
||||||
|
package com.tangem.domain.settings.repositories
|
||||||
|
|
||||||
|
interface SettingsRepository {
|
||||||
|
|
||||||
|
suspend fun isUserAlreadyRateApp(): Boolean
|
||||||
|
}
|
||||||
|
|
@ -37,20 +37,21 @@ dependencies {
|
||||||
kapt(deps.hilt.kapt)
|
kapt(deps.hilt.kapt)
|
||||||
|
|
||||||
/** Core modules */
|
/** Core modules */
|
||||||
implementation(project(":core:featuretoggles"))
|
implementation(projects.core.featuretoggles)
|
||||||
implementation(project(":core:navigation"))
|
implementation(projects.core.navigation)
|
||||||
implementation(project(":core:ui"))
|
implementation(projects.core.ui)
|
||||||
implementation(projects.core.utils)
|
implementation(projects.core.utils)
|
||||||
|
|
||||||
/** Feature Apis */
|
|
||||||
implementation(project(":features:wallet:api"))
|
|
||||||
|
|
||||||
/** Domain modules */
|
/** Domain modules */
|
||||||
implementation(project(":common"))
|
implementation(projects.common)
|
||||||
implementation(projects.domain.card)
|
implementation(projects.domain.card)
|
||||||
implementation(project(":domain:legacy"))
|
implementation(projects.domain.legacy)
|
||||||
implementation(project(":domain:models"))
|
implementation(projects.domain.models)
|
||||||
implementation(project(":domain:wallets"))
|
implementation(projects.domain.settings)
|
||||||
implementation(project(":domain:wallets:models"))
|
|
||||||
implementation(projects.domain.tokens)
|
implementation(projects.domain.tokens)
|
||||||
|
implementation(projects.domain.wallets)
|
||||||
|
implementation(projects.domain.wallets.models)
|
||||||
|
|
||||||
|
/** Feature Apis */
|
||||||
|
implementation(projects.features.wallet.api)
|
||||||
}
|
}
|
||||||
|
|
@ -15,7 +15,6 @@ import androidx.navigation.compose.rememberNavController
|
||||||
import com.tangem.core.navigation.AppScreen
|
import com.tangem.core.navigation.AppScreen
|
||||||
import com.tangem.core.navigation.NavigationAction
|
import com.tangem.core.navigation.NavigationAction
|
||||||
import com.tangem.core.navigation.NavigationStateHolder
|
import com.tangem.core.navigation.NavigationStateHolder
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
|
||||||
import com.tangem.feature.wallet.presentation.WalletFragment
|
import com.tangem.feature.wallet.presentation.WalletFragment
|
||||||
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensScreen
|
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensScreen
|
||||||
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensViewModel
|
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensViewModel
|
||||||
|
|
@ -35,29 +34,27 @@ internal class DefaultWalletRouter(private val navigationStateHolder: Navigation
|
||||||
override fun Initialize(fragmentManager: FragmentManager) {
|
override fun Initialize(fragmentManager: FragmentManager) {
|
||||||
this.fragmentManager = fragmentManager
|
this.fragmentManager = fragmentManager
|
||||||
|
|
||||||
TangemTheme {
|
NavHost(
|
||||||
NavHost(
|
navController = rememberNavController().apply { navController = this },
|
||||||
navController = rememberNavController().apply { navController = this },
|
startDestination = WalletScreens.WALLET.name,
|
||||||
startDestination = WalletScreens.WALLET.name,
|
) {
|
||||||
) {
|
composable(WalletScreens.WALLET.name) {
|
||||||
composable(WalletScreens.WALLET.name) {
|
val viewModel = hiltViewModel<WalletViewModel>().apply { router = this@DefaultWalletRouter }
|
||||||
val viewModel = hiltViewModel<WalletViewModel>().apply { router = this@DefaultWalletRouter }
|
LocalLifecycleOwner.current.lifecycle.addObserver(observer = viewModel)
|
||||||
LocalLifecycleOwner.current.lifecycle.addObserver(observer = viewModel)
|
|
||||||
|
|
||||||
WalletScreen(state = viewModel.uiState)
|
WalletScreen(state = viewModel.uiState)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable(WalletScreens.ORGANIZE_TOKENS.name) {
|
composable(WalletScreens.ORGANIZE_TOKENS.name) {
|
||||||
BackHandler(onBack = ::popBackStack)
|
BackHandler(onBack = ::popBackStack)
|
||||||
|
|
||||||
val viewModel: OrganizeTokensViewModel = hiltViewModel<OrganizeTokensViewModel>()
|
val viewModel: OrganizeTokensViewModel = hiltViewModel<OrganizeTokensViewModel>()
|
||||||
.apply { router = this@DefaultWalletRouter }
|
.apply { router = this@DefaultWalletRouter }
|
||||||
|
|
||||||
OrganizeTokensScreen(
|
OrganizeTokensScreen(
|
||||||
modifier = Modifier.systemBarsPadding(),
|
modifier = Modifier.systemBarsPadding(),
|
||||||
state = viewModel.uiState,
|
state = viewModel.uiState,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,12 +63,29 @@ sealed class WalletNotification(open val state: NotificationState) {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Remaining signatures left" notification
|
||||||
|
*
|
||||||
|
* @param count number of remaining signatures
|
||||||
|
*/
|
||||||
|
class RemainingSignaturesLeft(count: Int) : WalletNotification(
|
||||||
|
state = NotificationState.Simple(
|
||||||
|
title = TextReference.Res(id = R.string.common_warning),
|
||||||
|
subtitle = TextReference.Res(
|
||||||
|
id = R.string.warning_low_signatures_format,
|
||||||
|
formatArgs = WrappedList(data = listOf(count)),
|
||||||
|
),
|
||||||
|
iconResId = R.drawable.ic_alert_circle_24,
|
||||||
|
tint = TangemColorPalette.Amaranth,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "Wallet already signed hashes" notification
|
* "Wallet already signed hashes" notification
|
||||||
*
|
*
|
||||||
* @property onClick lambda be invoked when notification is clicked
|
* @property onClick lambda be invoked when notification is clicked
|
||||||
*/
|
*/
|
||||||
data class WalletAlreadySignedHashes(override val onClick: () -> Unit) : Clickable, WalletNotification(
|
data class AlreadyToppedUpAndSignedHashes(override val onClick: () -> Unit) : Clickable, WalletNotification(
|
||||||
state = NotificationState.Clickable(
|
state = NotificationState.Clickable(
|
||||||
title = TextReference.Res(id = R.string.common_warning),
|
title = TextReference.Res(id = R.string.common_warning),
|
||||||
subtitle = TextReference.Res(id = R.string.alert_card_signed_transactions),
|
subtitle = TextReference.Res(id = R.string.alert_card_signed_transactions),
|
||||||
|
|
@ -83,7 +100,7 @@ sealed class WalletNotification(open val state: NotificationState) {
|
||||||
*
|
*
|
||||||
* @property onClick lambda be invoked when notification is clicked
|
* @property onClick lambda be invoked when notification is clicked
|
||||||
*/
|
*/
|
||||||
data class MultiWalletAlreadySignedHashes(override val onClick: () -> Unit) : Clickable, WalletNotification(
|
data class SignedTransactionsInThePast(override val onClick: () -> Unit) : Clickable, WalletNotification(
|
||||||
state = NotificationState.Clickable(
|
state = NotificationState.Clickable(
|
||||||
title = TextReference.Res(
|
title = TextReference.Res(
|
||||||
id = R.string.warning_important_security_info,
|
id = R.string.warning_important_security_info,
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ internal class WalletStateFactory(
|
||||||
walletsListConfig = createWalletsListConfig(wallets),
|
walletsListConfig = createWalletsListConfig(wallets),
|
||||||
pullToRefreshConfig = createPullToRefreshConfig(),
|
pullToRefreshConfig = createPullToRefreshConfig(),
|
||||||
contentItems = persistentListOf(),
|
contentItems = persistentListOf(),
|
||||||
notifications = persistentListOf(), // TODO: create notifications
|
notifications = persistentListOf(),
|
||||||
bottomSheet = WalletBottomSheetConfig(
|
bottomSheet = WalletBottomSheetConfig(
|
||||||
// TODO: check notifications
|
// TODO: check notifications
|
||||||
isShow = false,
|
isShow = false,
|
||||||
|
|
@ -58,7 +58,7 @@ internal class WalletStateFactory(
|
||||||
walletsListConfig = createWalletsListConfig(wallets),
|
walletsListConfig = createWalletsListConfig(wallets),
|
||||||
pullToRefreshConfig = createPullToRefreshConfig(),
|
pullToRefreshConfig = createPullToRefreshConfig(),
|
||||||
contentItems = persistentListOf(),
|
contentItems = persistentListOf(),
|
||||||
notifications = persistentListOf(), // TODO: create notifications
|
notifications = persistentListOf(),
|
||||||
bottomSheet = WalletBottomSheetConfig(
|
bottomSheet = WalletBottomSheetConfig(
|
||||||
// TODO: check notifications
|
// TODO: check notifications
|
||||||
isShow = false,
|
isShow = false,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.material.ExperimentalMaterialApi
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||||
|
import androidx.compose.material.pullrefresh.PullRefreshState
|
||||||
import androidx.compose.material.pullrefresh.pullRefresh
|
import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
|
|
@ -40,6 +41,7 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList
|
||||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.TransactionsBlockGroupTitle
|
import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.TransactionsBlockGroupTitle
|
||||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.TransactionsBlockTitle
|
import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.TransactionsBlockTitle
|
||||||
import com.tangem.feature.wallet.presentation.wallet.ui.decorations.walletContentItemDecoration
|
import com.tangem.feature.wallet.presentation.wallet.ui.decorations.walletContentItemDecoration
|
||||||
|
import com.tangem.feature.wallet.presentation.wallet.ui.utils.ScrollOffsetCollector
|
||||||
import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimator
|
import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimator
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -54,13 +56,13 @@ import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimat
|
||||||
@Composable
|
@Composable
|
||||||
internal fun WalletScreen(state: WalletStateHolder) {
|
internal fun WalletScreen(state: WalletStateHolder) {
|
||||||
BackHandler(onBack = state.onBackClick)
|
BackHandler(onBack = state.onBackClick)
|
||||||
|
val walletsListState = rememberLazyListState()
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = { WalletTopBar(config = state.topBarConfig) },
|
topBar = { WalletTopBar(config = state.topBarConfig) },
|
||||||
containerColor = TangemTheme.colors.background.secondary,
|
containerColor = TangemTheme.colors.background.secondary,
|
||||||
) { scaffoldPaddings ->
|
) { scaffoldPaddings ->
|
||||||
|
|
||||||
val walletsListState = rememberLazyListState()
|
|
||||||
val changeableItemModifier = Modifier.changeWalletAnimator(walletsListState)
|
val changeableItemModifier = Modifier.changeWalletAnimator(walletsListState)
|
||||||
val pullRefreshState = rememberPullRefreshState(
|
val pullRefreshState = rememberPullRefreshState(
|
||||||
refreshing = state.pullToRefreshConfig.isRefreshing,
|
refreshing = state.pullToRefreshConfig.isRefreshing,
|
||||||
|
|
@ -153,8 +155,8 @@ internal fun WalletScreen(state: WalletStateHolder) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PullRefreshIndicator(
|
PullToRefreshIndicator(
|
||||||
refreshing = state.pullToRefreshConfig.isRefreshing,
|
isRefreshing = state.pullToRefreshConfig.isRefreshing,
|
||||||
state = pullRefreshState,
|
state = pullRefreshState,
|
||||||
modifier = Modifier.align(Alignment.TopCenter),
|
modifier = Modifier.align(Alignment.TopCenter),
|
||||||
)
|
)
|
||||||
|
|
@ -164,6 +166,21 @@ internal fun WalletScreen(state: WalletStateHolder) {
|
||||||
state.bottomSheet?.let { bottomSheetConfig ->
|
state.bottomSheet?.let { bottomSheetConfig ->
|
||||||
if (bottomSheetConfig.isShow) WalletBottomSheet(config = bottomSheetConfig)
|
if (bottomSheetConfig.isShow) WalletBottomSheet(config = bottomSheetConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(key1 = walletsListState, key2 = state.walletsListConfig.onWalletChange) {
|
||||||
|
snapshotFlow { walletsListState.layoutInfo.visibleItemsInfo }
|
||||||
|
.collect(collector = ScrollOffsetCollector(callback = state.walletsListConfig.onWalletChange))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
|
@Composable
|
||||||
|
private fun PullToRefreshIndicator(isRefreshing: Boolean, state: PullRefreshState, modifier: Modifier = Modifier) {
|
||||||
|
PullRefreshIndicator(
|
||||||
|
refreshing = isRefreshing,
|
||||||
|
state = state,
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,6 @@ import androidx.compose.foundation.lazy.LazyRow
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.snapshotFlow
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
|
@ -20,7 +18,6 @@ import androidx.compose.ui.unit.dp
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletsListConfig
|
import com.tangem.feature.wallet.presentation.wallet.state.WalletsListConfig
|
||||||
import com.tangem.feature.wallet.presentation.wallet.ui.utils.ScrollOffsetCollector
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wallets list component
|
* Wallets list component
|
||||||
|
|
@ -47,11 +44,6 @@ internal fun WalletsList(config: WalletsListConfig, lazyListState: LazyListState
|
||||||
WalletCard(state = state, modifier = Modifier.width(itemWidth))
|
WalletCard(state = state, modifier = Modifier.width(itemWidth))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(lazyListState) {
|
|
||||||
snapshotFlow { lazyListState.layoutInfo.visibleItemsInfo }
|
|
||||||
.collect(collector = ScrollOffsetCollector(callback = config.onWalletChange))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
package com.tangem.feature.wallet.presentation.wallet.viewmodels
|
||||||
|
|
||||||
|
import com.tangem.domain.common.CardTypesResolver
|
||||||
|
import com.tangem.feature.wallet.presentation.wallet.state.WalletNotification
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
|
||||||
|
/**
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
@Suppress("LongParameterList")
|
||||||
|
internal class WalletNotificationsListFactory(
|
||||||
|
private val wasCardScannedCallback: suspend (String) -> Boolean,
|
||||||
|
private val isUserAlreadyRateAppCallback: suspend () -> Boolean,
|
||||||
|
private val onWalletAlreadySignedHashesClick: () -> Unit,
|
||||||
|
private val onMultiWalletAlreadySignedHashesClick: () -> Unit,
|
||||||
|
private val onBackupCardClick: () -> Unit,
|
||||||
|
private val hasUnreachableNetworksCallback: () -> Boolean,
|
||||||
|
private val onLikeTangemAppClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun create(cardTypesResolver: CardTypesResolver): Flow<ImmutableList<WalletNotification>> {
|
||||||
|
// TODO: [REDACTED_JIRA] order
|
||||||
|
return flow {
|
||||||
|
emit(
|
||||||
|
buildList {
|
||||||
|
if (cardTypesResolver.isTestCard()) {
|
||||||
|
add(element = WalletNotification.TestCard)
|
||||||
|
return@buildList
|
||||||
|
}
|
||||||
|
|
||||||
|
addRemainingSignaturesLeftNotifications(cardTypesResolver)
|
||||||
|
|
||||||
|
if (!cardTypesResolver.isReleaseFirmwareType()) {
|
||||||
|
add(element = WalletNotification.DevCard)
|
||||||
|
} else {
|
||||||
|
addReleaseSpecialNotifications(cardTypesResolver)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: [REDACTED_JIRA] - DemoModeDatasource
|
||||||
|
|
||||||
|
if (hasUnreachableNetworksCallback()) {
|
||||||
|
add(element = WalletNotification.UnreachableNetworks)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cardTypesResolver.isBackupForbidden() && !cardTypesResolver.hasBackup()) {
|
||||||
|
add(element = WalletNotification.BackupCard(onClick = onBackupCardClick))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: [REDACTED_JIRA]
|
||||||
|
* List<WalletStoreModel>
|
||||||
|
* .filter { store ->
|
||||||
|
* store.walletsData.any { it.status is WalletDataModel.MissedDerivation }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (isUserAlreadyRateAppCallback()) {
|
||||||
|
add(element = WalletNotification.LikeTangemApp(onClick = onLikeTangemAppClick))
|
||||||
|
}
|
||||||
|
}.toImmutableList(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableList<WalletNotification>.addRemainingSignaturesLeftNotifications(
|
||||||
|
cardTypesResolver: CardTypesResolver,
|
||||||
|
) {
|
||||||
|
val remainingSignatures = cardTypesResolver.getRemainingSignatures()
|
||||||
|
if (remainingSignatures != null && remainingSignatures <= MAX_REMAINING_SIGNATURES_COUNT) {
|
||||||
|
add(element = WalletNotification.RemainingSignaturesLeft(remainingSignatures))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun MutableList<WalletNotification>.addReleaseSpecialNotifications(
|
||||||
|
cardTypesResolver: CardTypesResolver,
|
||||||
|
) {
|
||||||
|
// TODO: [REDACTED_JIRA] !isDemo
|
||||||
|
if (!wasCardScannedCallback(cardTypesResolver.getCardId()) && cardTypesResolver.isMultiwalletAllowed()) {
|
||||||
|
if (cardTypesResolver.isBackupForbidden() && cardTypesResolver.hasWalletSignedHashes()) {
|
||||||
|
add(
|
||||||
|
element = WalletNotification.SignedTransactionsInThePast(
|
||||||
|
onClick = onMultiWalletAlreadySignedHashesClick,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else if (cardTypesResolver.hasWalletSignedHashes()) {
|
||||||
|
add(
|
||||||
|
element = WalletNotification.AlreadyToppedUpAndSignedHashes(
|
||||||
|
onClick = onWalletAlreadySignedHashesClick,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cardTypesResolver.isAttestationFailed()) {
|
||||||
|
add(element = WalletNotification.CardVerificationFailed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val MAX_REMAINING_SIGNATURES_COUNT = 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -79,9 +79,10 @@ include(":domain:legacy")
|
||||||
|
|
||||||
include(":domain:core")
|
include(":domain:core")
|
||||||
include(":domain:card")
|
include(":domain:card")
|
||||||
|
include(":domain:settings")
|
||||||
|
include(":domain:tokens")
|
||||||
include(":domain:wallets")
|
include(":domain:wallets")
|
||||||
include(":domain:wallets:models")
|
include(":domain:wallets:models")
|
||||||
include(":domain:tokens")
|
|
||||||
// endregion Domain modules
|
// endregion Domain modules
|
||||||
|
|
||||||
// region Data modules
|
// region Data modules
|
||||||
|
|
@ -90,3 +91,4 @@ include(":data:card")
|
||||||
include(":data:tokens")
|
include(":data:tokens")
|
||||||
include(":data:source:preferences")
|
include(":data:source:preferences")
|
||||||
// endregion Data modules
|
// endregion Data modules
|
||||||
|
include(":data:settings")
|
||||||
Loading…
Add table
Add a link
Reference in a new issue