diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8b1db8e62c..0a8341a590 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -28,7 +28,9 @@ dependencies { implementation(projects.domain.card) implementation(project(":domain:wallets")) implementation(project(":domain:wallets:models")) + implementation(projects.domain.settings) implementation(projects.domain.tokens) + implementation(project(":common")) implementation(project(":core:analytics")) implementation(projects.core.analytics.models) @@ -42,6 +44,7 @@ dependencies { implementation(project(":libs:auth")) implementation(project(":data:source:preferences")) implementation(projects.data.card) + implementation(projects.data.settings) implementation(projects.data.tokens) implementation(projects.data.common) diff --git a/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt index 4af9f75d50..d2f09c474f 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt @@ -1,9 +1,7 @@ package com.tangem.tap.di.domain -import com.tangem.domain.card.GetAccessCodeSavingStatusUseCase -import com.tangem.domain.card.GetBiometricsStatusUseCase -import com.tangem.domain.card.ScanCardProcessor -import com.tangem.domain.card.SetAccessCodeRequestPolicyUseCase +import com.tangem.domain.card.* +import com.tangem.domain.card.repository.CardRepository import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.tap.domain.scanCard.DefaultScanCardProcessor import dagger.Module @@ -43,4 +41,10 @@ internal object CardDomainModule { ): GetAccessCodeSavingStatusUseCase { return GetAccessCodeSavingStatusUseCase(cardSdkConfigRepository = cardSdkConfigRepository) } + + @Provides + @Singleton + fun provideGetCardWasScannedUseCase(cardRepository: CardRepository): GetCardWasScannedUseCase { + return GetCardWasScannedUseCase(cardRepository = cardRepository) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt new file mode 100644 index 0000000000..4897db1d24 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/di/domain/SettingsDomainModule.kt @@ -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) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/di/domain/wallets/WalletsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt similarity index 91% rename from app/src/main/java/com/tangem/tap/common/di/domain/wallets/WalletsDomainModule.kt rename to app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt index bcf08adf87..6fedc25c0e 100644 --- a/app/src/main/java/com/tangem/tap/common/di/domain/wallets/WalletsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt @@ -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.usecase.GetWalletsUseCase @@ -11,7 +11,7 @@ import dagger.hilt.android.scopes.ViewModelScoped @Module @InstallIn(ViewModelComponent::class) -object WalletsDomainModule { +internal object WalletsDomainModule { @Provides @ViewModelScoped diff --git a/data/card/build.gradle.kts b/data/card/build.gradle.kts index 26b3010f2e..0999cc6959 100644 --- a/data/card/build.gradle.kts +++ b/data/card/build.gradle.kts @@ -13,10 +13,14 @@ android { dependencies { implementation(deps.hilt.android) kapt(deps.hilt.kapt) + implementation(deps.tangem.card.android) implementation(deps.tangem.card.core) + implementation(projects.core.utils) + implementation(projects.data.source.preferences) + implementation(projects.domain.card) implementation(projects.domain.models) } \ No newline at end of file diff --git a/data/card/src/main/java/com/tangem/data/card/DefaultCardRepository.kt b/data/card/src/main/java/com/tangem/data/card/DefaultCardRepository.kt new file mode 100644 index 0000000000..b1bc033cb1 --- /dev/null +++ b/data/card/src/main/java/com/tangem/data/card/DefaultCardRepository.kt @@ -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) } + } +} \ No newline at end of file diff --git a/data/card/src/main/java/com/tangem/data/card/di/CardDataModule.kt b/data/card/src/main/java/com/tangem/data/card/di/CardDataModule.kt index 11f63f4a31..b5edf95c45 100644 --- a/data/card/src/main/java/com/tangem/data/card/di/CardDataModule.kt +++ b/data/card/src/main/java/com/tangem/data/card/di/CardDataModule.kt @@ -1,9 +1,12 @@ package com.tangem.data.card.di +import com.tangem.data.card.DefaultCardRepository import com.tangem.data.card.DefaultCardSdkConfigRepository import com.tangem.data.card.sdk.CardSdkProvider import com.tangem.data.source.preferences.PreferencesDataSource +import com.tangem.domain.card.repository.CardRepository import com.tangem.domain.card.repository.CardSdkConfigRepository +import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -25,4 +28,13 @@ internal object CardDataModule { preferencesDataSource = preferencesDataSource, ) } + + @Provides + @Singleton + fun provideCardRepository( + preferencesDataSource: PreferencesDataSource, + dispatchers: CoroutineDispatcherProvider, + ): CardRepository { + return DefaultCardRepository(preferencesDataSource = preferencesDataSource, dispatchers = dispatchers) + } } \ No newline at end of file diff --git a/data/settings/.gitignore b/data/settings/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/data/settings/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/data/settings/build.gradle.kts b/data/settings/build.gradle.kts new file mode 100644 index 0000000000..1413053d65 --- /dev/null +++ b/data/settings/build.gradle.kts @@ -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) +} diff --git a/data/settings/src/main/java/com/tangem/data/settings/DefaultSettingsRepository.kt b/data/settings/src/main/java/com/tangem/data/settings/DefaultSettingsRepository.kt new file mode 100644 index 0000000000..c1ee6fc57e --- /dev/null +++ b/data/settings/src/main/java/com/tangem/data/settings/DefaultSettingsRepository.kt @@ -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() + } + } +} \ No newline at end of file diff --git a/data/settings/src/main/java/com/tangem/data/settings/di/SettingsDataModule.kt b/data/settings/src/main/java/com/tangem/data/settings/di/SettingsDataModule.kt new file mode 100644 index 0000000000..6ef7e9a280 --- /dev/null +++ b/data/settings/src/main/java/com/tangem/data/settings/di/SettingsDataModule.kt @@ -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) + } +} \ No newline at end of file diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/GetCardWasScannedUseCase.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/GetCardWasScannedUseCase.kt new file mode 100644 index 0000000000..91c077997d --- /dev/null +++ b/domain/card/src/main/kotlin/com/tangem/domain/card/GetCardWasScannedUseCase.kt @@ -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) +} \ No newline at end of file diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/repository/CardRepository.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/repository/CardRepository.kt new file mode 100644 index 0000000000..40ac6ec968 --- /dev/null +++ b/domain/card/src/main/kotlin/com/tangem/domain/card/repository/CardRepository.kt @@ -0,0 +1,6 @@ +package com.tangem.domain.card.repository + +interface CardRepository { + + suspend fun wasCardScanned(cardId: String): Boolean +} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/CardTypesResolver.kt b/domain/legacy/src/main/java/com/tangem/domain/common/CardTypesResolver.kt index ed40b0b3b6..10d2f401bd 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/CardTypesResolver.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/CardTypesResolver.kt @@ -26,4 +26,20 @@ interface CardTypesResolver { fun getPrimaryToken(): Token? 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 } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt b/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt index 9f993a407b..0eb5a72085 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt @@ -10,6 +10,7 @@ import com.tangem.domain.common.TapWorkarounds.isStart2Coin import com.tangem.domain.common.TapWorkarounds.isTestCard import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ProductType +import com.tangem.operations.attestation.Attestation internal class TangemCardTypesResolver( private val card: CardDTO, @@ -73,6 +74,27 @@ internal class TangemCardTypesResolver( 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 { // workaround for BSC (BNB) notes cards return when (blockchainName) { diff --git a/domain/settings/.gitignore b/domain/settings/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/domain/settings/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/domain/settings/build.gradle.kts b/domain/settings/build.gradle.kts new file mode 100644 index 0000000000..7ff7fb7522 --- /dev/null +++ b/domain/settings/build.gradle.kts @@ -0,0 +1,4 @@ +plugins { + alias(deps.plugins.kotlin.jvm) + id("configuration") +} \ No newline at end of file diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/IsUserAlreadyRateAppUseCase.kt b/domain/settings/src/main/java/com/tangem/domain/settings/IsUserAlreadyRateAppUseCase.kt new file mode 100644 index 0000000000..fa71e80df8 --- /dev/null +++ b/domain/settings/src/main/java/com/tangem/domain/settings/IsUserAlreadyRateAppUseCase.kt @@ -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() +} \ No newline at end of file diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SettingsRepository.kt b/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SettingsRepository.kt new file mode 100644 index 0000000000..c33c9d4cf7 --- /dev/null +++ b/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SettingsRepository.kt @@ -0,0 +1,6 @@ +package com.tangem.domain.settings.repositories + +interface SettingsRepository { + + suspend fun isUserAlreadyRateApp(): Boolean +} \ No newline at end of file diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index 55e9edc0c3..13a57f56c7 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -37,20 +37,21 @@ dependencies { kapt(deps.hilt.kapt) /** Core modules */ - implementation(project(":core:featuretoggles")) - implementation(project(":core:navigation")) - implementation(project(":core:ui")) + implementation(projects.core.featuretoggles) + implementation(projects.core.navigation) + implementation(projects.core.ui) implementation(projects.core.utils) - /** Feature Apis */ - implementation(project(":features:wallet:api")) - /** Domain modules */ - implementation(project(":common")) + implementation(projects.common) implementation(projects.domain.card) - implementation(project(":domain:legacy")) - implementation(project(":domain:models")) - implementation(project(":domain:wallets")) - implementation(project(":domain:wallets:models")) + implementation(projects.domain.legacy) + implementation(projects.domain.models) + implementation(projects.domain.settings) implementation(projects.domain.tokens) + implementation(projects.domain.wallets) + implementation(projects.domain.wallets.models) + + /** Feature Apis */ + implementation(projects.features.wallet.api) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index 2f30c3d355..3964582227 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -15,7 +15,6 @@ import androidx.navigation.compose.rememberNavController import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction 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.organizetokens.OrganizeTokensScreen import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensViewModel @@ -35,29 +34,27 @@ internal class DefaultWalletRouter(private val navigationStateHolder: Navigation override fun Initialize(fragmentManager: FragmentManager) { this.fragmentManager = fragmentManager - TangemTheme { - NavHost( - navController = rememberNavController().apply { navController = this }, - startDestination = WalletScreens.WALLET.name, - ) { - composable(WalletScreens.WALLET.name) { - val viewModel = hiltViewModel().apply { router = this@DefaultWalletRouter } - LocalLifecycleOwner.current.lifecycle.addObserver(observer = viewModel) + NavHost( + navController = rememberNavController().apply { navController = this }, + startDestination = WalletScreens.WALLET.name, + ) { + composable(WalletScreens.WALLET.name) { + val viewModel = hiltViewModel().apply { router = this@DefaultWalletRouter } + LocalLifecycleOwner.current.lifecycle.addObserver(observer = viewModel) - WalletScreen(state = viewModel.uiState) - } + WalletScreen(state = viewModel.uiState) + } - composable(WalletScreens.ORGANIZE_TOKENS.name) { - BackHandler(onBack = ::popBackStack) + composable(WalletScreens.ORGANIZE_TOKENS.name) { + BackHandler(onBack = ::popBackStack) - val viewModel: OrganizeTokensViewModel = hiltViewModel() - .apply { router = this@DefaultWalletRouter } + val viewModel: OrganizeTokensViewModel = hiltViewModel() + .apply { router = this@DefaultWalletRouter } - OrganizeTokensScreen( - modifier = Modifier.systemBarsPadding(), - state = viewModel.uiState, - ) - } + OrganizeTokensScreen( + modifier = Modifier.systemBarsPadding(), + state = viewModel.uiState, + ) } } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletNotification.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletNotification.kt index 66079037c6..b1813c0544 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletNotification.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletNotification.kt @@ -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 * * @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( title = TextReference.Res(id = R.string.common_warning), 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 */ - data class MultiWalletAlreadySignedHashes(override val onClick: () -> Unit) : Clickable, WalletNotification( + data class SignedTransactionsInThePast(override val onClick: () -> Unit) : Clickable, WalletNotification( state = NotificationState.Clickable( title = TextReference.Res( id = R.string.warning_important_security_info, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/builder/WalletStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/builder/WalletStateFactory.kt index 21bf52bee0..081985c3f6 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/builder/WalletStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/builder/WalletStateFactory.kt @@ -37,7 +37,7 @@ internal class WalletStateFactory( walletsListConfig = createWalletsListConfig(wallets), pullToRefreshConfig = createPullToRefreshConfig(), contentItems = persistentListOf(), - notifications = persistentListOf(), // TODO: create notifications + notifications = persistentListOf(), bottomSheet = WalletBottomSheetConfig( // TODO: check notifications isShow = false, @@ -58,7 +58,7 @@ internal class WalletStateFactory( walletsListConfig = createWalletsListConfig(wallets), pullToRefreshConfig = createPullToRefreshConfig(), contentItems = persistentListOf(), - notifications = persistentListOf(), // TODO: create notifications + notifications = persistentListOf(), bottomSheet = WalletBottomSheetConfig( // TODO: check notifications isShow = false, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index 5f825b106f..c777e06a49 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.pullrefresh.PullRefreshIndicator +import androidx.compose.material.pullrefresh.PullRefreshState import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.rememberPullRefreshState 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.TransactionsBlockTitle 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 /** @@ -54,13 +56,13 @@ import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimat @Composable internal fun WalletScreen(state: WalletStateHolder) { BackHandler(onBack = state.onBackClick) + val walletsListState = rememberLazyListState() Scaffold( topBar = { WalletTopBar(config = state.topBarConfig) }, containerColor = TangemTheme.colors.background.secondary, ) { scaffoldPaddings -> - val walletsListState = rememberLazyListState() val changeableItemModifier = Modifier.changeWalletAnimator(walletsListState) val pullRefreshState = rememberPullRefreshState( refreshing = state.pullToRefreshConfig.isRefreshing, @@ -153,8 +155,8 @@ internal fun WalletScreen(state: WalletStateHolder) { } } - PullRefreshIndicator( - refreshing = state.pullToRefreshConfig.isRefreshing, + PullToRefreshIndicator( + isRefreshing = state.pullToRefreshConfig.isRefreshing, state = pullRefreshState, modifier = Modifier.align(Alignment.TopCenter), ) @@ -164,6 +166,21 @@ internal fun WalletScreen(state: WalletStateHolder) { state.bottomSheet?.let { 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 diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt index 3f162e41f0..34dc515850 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt @@ -11,8 +11,6 @@ import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration 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.feature.wallet.presentation.common.WalletPreviewData import com.tangem.feature.wallet.presentation.wallet.state.WalletsListConfig -import com.tangem.feature.wallet.presentation.wallet.ui.utils.ScrollOffsetCollector /** * Wallets list component @@ -47,11 +44,6 @@ internal fun WalletsList(config: WalletsListConfig, lazyListState: LazyListState WalletCard(state = state, modifier = Modifier.width(itemWidth)) } } - - LaunchedEffect(lazyListState) { - snapshotFlow { lazyListState.layoutInfo.visibleItemsInfo } - .collect(collector = ScrollOffsetCollector(callback = config.onWalletChange)) - } } @Preview diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt new file mode 100644 index 0000000000..f2de50be61 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt @@ -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> { + // 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 + * .filter { store -> + * store.walletsData.any { it.status is WalletDataModel.MissedDerivation } + * } + */ + + if (isUserAlreadyRateAppCallback()) { + add(element = WalletNotification.LikeTangemApp(onClick = onLikeTangemAppClick)) + } + }.toImmutableList(), + ) + } + } + + private fun MutableList.addRemainingSignaturesLeftNotifications( + cardTypesResolver: CardTypesResolver, + ) { + val remainingSignatures = cardTypesResolver.getRemainingSignatures() + if (remainingSignatures != null && remainingSignatures <= MAX_REMAINING_SIGNATURES_COUNT) { + add(element = WalletNotification.RemainingSignaturesLeft(remainingSignatures)) + } + } + + private suspend fun MutableList.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 + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 276169d26e..c77f58438b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -79,9 +79,10 @@ include(":domain:legacy") include(":domain:core") include(":domain:card") +include(":domain:settings") +include(":domain:tokens") include(":domain:wallets") include(":domain:wallets:models") -include(":domain:tokens") // endregion Domain modules // region Data modules @@ -89,4 +90,5 @@ include(":data:common") include(":data:card") include(":data:tokens") include(":data:source:preferences") -// endregion Data modules \ No newline at end of file +// endregion Data modules +include(":data:settings") \ No newline at end of file