From 630bba1c40dbdd359f9db94983f5026a0d5d64a2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 3 May 2023 13:05:48 +0300 Subject: [PATCH 01/14] Updated on 2026-08-14 --- .../ui/components/AddCustomTokenWarning.kt | 3 +- .../com/tangem/core/ui/res/TangemDimens.kt | 3 + .../com/tangem/core/ui/res/TangemShapes.kt | 2 + .../src/main/res/drawable/ic_eye_off_24.xml | 9 + features/wallet/api/.gitignore | 1 + features/wallet/api/build.gradle.kts | 5 + .../wallet/api/src/main/AndroidManifest.xml | 2 + features/wallet/impl/.gitignore | 1 + features/wallet/impl/build.gradle.kts | 34 ++++ .../wallet/impl/src/main/AndroidManifest.xml | 4 + .../main/presentation/ui/BalanceUIState.kt | 5 + .../main/presentation/ui/WalletHeader.kt | 185 ++++++++++++++++++ .../presentation/ui/WalletHeaderConfig.kt | 12 ++ .../extension/BaseExtensionConfigurations.kt | 4 +- settings.gradle.kts | 3 + 15 files changed, 269 insertions(+), 4 deletions(-) create mode 100644 core/ui/src/main/res/drawable/ic_eye_off_24.xml create mode 100644 features/wallet/api/.gitignore create mode 100644 features/wallet/api/build.gradle.kts create mode 100644 features/wallet/api/src/main/AndroidManifest.xml create mode 100644 features/wallet/impl/.gitignore create mode 100644 features/wallet/impl/build.gradle.kts create mode 100644 features/wallet/impl/src/main/AndroidManifest.xml create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/BalanceUIState.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/WalletHeader.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/WalletHeaderConfig.kt diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/ui/components/AddCustomTokenWarning.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/ui/components/AddCustomTokenWarning.kt index 5eb76c6e66..3e1b99796e 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/ui/components/AddCustomTokenWarning.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/ui/components/AddCustomTokenWarning.kt @@ -3,7 +3,6 @@ package com.tangem.tap.features.customtoken.impl.presentation.ui.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Card import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -30,7 +29,7 @@ import com.tangem.wallet.R internal fun AddCustomTokenWarning(description: TextReference, modifier: Modifier = Modifier) { Card( modifier = modifier, - shape = RoundedCornerShape(TangemTheme.dimens.radius4), + shape = TangemTheme.shapes.roundedCornersSmall2, backgroundColor = TangemColorPalette.Tangerine, contentColor = TangemColorPalette.White, elevation = TangemTheme.dimens.elevation4, diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt index d5d14244ae..5f37be0e27 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt @@ -65,7 +65,9 @@ data class TangemDimens internal constructor( val size93: Dp = 93.dp, val size96: Dp = 96.dp, val size102: Dp = 102.dp, + val size108: Dp = 108.dp, val size116: Dp = 116.dp, + val size120: Dp = 120.dp, val size142: Dp = 142.dp, val size164: Dp = 164.dp, val size200: Dp = 200.dp, @@ -95,5 +97,6 @@ data class TangemDimens internal constructor( val spacing54: Dp = 54.dp, val spacing56: Dp = 56.dp, val spacing92: Dp = 92.dp, + val spacing154: Dp = 154.dp, // endregion Spacing ) \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemShapes.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemShapes.kt index 57bd37658f..b452e38004 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemShapes.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemShapes.kt @@ -9,6 +9,7 @@ data class TangemShapes internal constructor( val roundedCornersSmall: Shape, val roundedCornersSmall2: Shape, val roundedCornersMedium: Shape, + val roundedCornersXMedium: Shape, val roundedCornersLarge: Shape, val bottomSheet: Shape, ) { @@ -16,6 +17,7 @@ data class TangemShapes internal constructor( roundedCornersSmall = RoundedCornerShape(size = dimens.radius2), roundedCornersSmall2 = RoundedCornerShape(size = dimens.radius4), roundedCornersMedium = RoundedCornerShape(size = dimens.radius12), + roundedCornersXMedium = RoundedCornerShape(size = dimens.radius16), roundedCornersLarge = RoundedCornerShape(size = dimens.radius28), bottomSheet = RoundedCornerShape( topStart = dimens.radius16, diff --git a/core/ui/src/main/res/drawable/ic_eye_off_24.xml b/core/ui/src/main/res/drawable/ic_eye_off_24.xml new file mode 100644 index 0000000000..7454717d84 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_eye_off_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/features/wallet/api/.gitignore b/features/wallet/api/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/features/wallet/api/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/features/wallet/api/build.gradle.kts b/features/wallet/api/build.gradle.kts new file mode 100644 index 0000000000..1fbece85d5 --- /dev/null +++ b/features/wallet/api/build.gradle.kts @@ -0,0 +1,5 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + id("configuration") +} \ No newline at end of file diff --git a/features/wallet/api/src/main/AndroidManifest.xml b/features/wallet/api/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..85a7d6c7c5 --- /dev/null +++ b/features/wallet/api/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/features/wallet/impl/.gitignore b/features/wallet/impl/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/features/wallet/impl/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts new file mode 100644 index 0000000000..ea6af49097 --- /dev/null +++ b/features/wallet/impl/build.gradle.kts @@ -0,0 +1,34 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.kapt) + alias(deps.plugins.hilt.android) + id("configuration") +} + +dependencies { + /** AndroidX */ + implementation(deps.androidx.activity.compose) + + /** Compose */ + implementation(deps.compose.material) + implementation(deps.compose.foundation) + implementation(deps.compose.navigation) + implementation(deps.compose.navigation.hilt) + implementation(deps.compose.ui) + implementation(deps.compose.ui.tooling) + implementation(deps.compose.shimmer) + implementation(deps.compose.accompanist.systemUiController) + + /** DI */ + implementation(deps.hilt.android) + kapt(deps.hilt.kapt) + + /** Core modules */ + implementation(project(":core:featuretoggles")) + implementation(project(":core:ui")) + + /** Feature Apis */ + implementation(project(":features:wallet:api")) + +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/AndroidManifest.xml b/features/wallet/impl/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a1a96bfa00 --- /dev/null +++ b/features/wallet/impl/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/BalanceUIState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/BalanceUIState.kt new file mode 100644 index 0000000000..b6010c0f04 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/BalanceUIState.kt @@ -0,0 +1,5 @@ +package com.tangem.feature.main.presentation.ui + +enum class BalanceUIState { + VISIBLE, LOADING, HIDDEN, NONE +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/WalletHeader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/WalletHeader.kt new file mode 100644 index 0000000000..8c2ba58d7e --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/WalletHeader.kt @@ -0,0 +1,185 @@ +package com.tangem.feature.main.presentation.ui + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.Icon +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.sp +import com.tangem.core.ui.R +import com.tangem.core.ui.components.* +import com.tangem.core.ui.res.TangemTheme +import com.valentinilk.shimmer.shimmer + +@OptIn(ExperimentalMaterialApi::class) +@Composable +fun WalletHeaderCard(config: WalletHeaderConfig, modifier: Modifier = Modifier) { + Surface( + modifier = modifier.defaultMinSize(minHeight = TangemTheme.dimens.size108), + shape = TangemTheme.shapes.roundedCornersXMedium, + color = TangemTheme.colors.background.primary, + onClick = config.onClick ?: {}, + enabled = config.onClick != null, + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .height(intrinsicSize = IntrinsicSize.Max), + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Column( + modifier = Modifier.padding(all = TangemTheme.dimens.spacing12), + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), + ) { + HeaderWalletName( + walletName = config.walletName, + balanceState = config.balanceState, + ) + HeaderBalanceTitle( + balance = config.balance, + balanceState = config.balanceState, + ) + Text( + text = config.additionalInfo, + color = TangemTheme.colors.text.disabled, + style = TangemTheme.typography.caption, + ) + } + Image( + modifier = Modifier + .width(TangemTheme.dimens.size120) + .padding(end = TangemTheme.dimens.spacing18) + .fillMaxHeight(), + painter = config.cardImage, + contentDescription = null, + contentScale = ContentScale.FillWidth, + ) + } + } +} + +@Composable +private fun HeaderWalletName(walletName: String, balanceState: BalanceUIState) { + if (balanceState == BalanceUIState.HIDDEN) { + Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4)) { + Text( + text = walletName, + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.body2, + maxLines = 1, + ) + Icon( + modifier = Modifier.size(size = TangemTheme.dimens.size20), + painter = painterResource(id = R.drawable.ic_eye_off_24), + contentDescription = null, + tint = TangemTheme.colors.icon.informative, + ) + } + } else { + Text( + text = walletName, + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.body2, + maxLines = 1, + ) + } +} + +@Composable +private fun HeaderBalanceTitle(balance: String, balanceState: BalanceUIState) { + when (balanceState) { + BalanceUIState.VISIBLE -> { + ResizableText( + text = balance, + color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography.h2, + fontSizeRange = FontSizeRange(min = 16.sp, max = TangemTheme.typography.h2.fontSize), + modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size32), + ) + } + BalanceUIState.LOADING -> { + Box( + modifier = Modifier + .padding(vertical = TangemTheme.dimens.spacing4) + .size(width = TangemTheme.dimens.size102, height = TangemTheme.dimens.size24) + .shimmer() + .background( + color = TangemTheme.colors.button.secondary, + shape = RoundedCornerShape(TangemTheme.dimens.radius6), + ), + ) + } + BalanceUIState.HIDDEN -> { + Text( + text = "•••", + color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography.h2, + ) + } + BalanceUIState.NONE -> { + Text( + text = "—", + color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography.h2, + ) + } + } +} + +// region Preview + +@Composable +private fun WarningsPreview() { + val walletHeaderConfig = WalletHeaderConfig( + walletName = "Wallet 1", + balance = "8923,05 $", + additionalInfo = "3 cards • Seed enabled", + balanceState = BalanceUIState.VISIBLE, + cardImage = painterResource(id = R.drawable.ill_businessman_3d), + onClick = {}, + ) + Column(modifier = Modifier.fillMaxWidth()) { + WalletHeaderCard( + config = walletHeaderConfig, + ) + SpacerH32() + WalletHeaderCard( + config = walletHeaderConfig.copy(balanceState = BalanceUIState.HIDDEN), + ) + SpacerH32() + WalletHeaderCard( + config = walletHeaderConfig.copy(balanceState = BalanceUIState.LOADING), + ) + SpacerH32() + WalletHeaderCard( + config = walletHeaderConfig.copy(balanceState = BalanceUIState.NONE), + ) + SpacerH32() + } +} + +@Preview(showBackground = true) +@Composable +private fun Preview_Warning_InLightTheme() { + TangemTheme(isDark = false) { + WarningsPreview() + } +} + +@Preview(showBackground = true) +@Composable +private fun Preview_Warning_InDarkTheme() { + TangemTheme(isDark = true) { + WarningsPreview() + } +} + +// endregion Preview \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/WalletHeaderConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/WalletHeaderConfig.kt new file mode 100644 index 0000000000..b8c22ca329 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/WalletHeaderConfig.kt @@ -0,0 +1,12 @@ +package com.tangem.feature.main.presentation.ui + +import androidx.compose.ui.graphics.painter.Painter + +data class WalletHeaderConfig( + val walletName: String, + val balance: String, + val additionalInfo: String, + val balanceState: BalanceUIState, + val cardImage: Painter, + val onClick: (() -> Unit)?, +) \ No newline at end of file diff --git a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/extension/BaseExtensionConfigurations.kt b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/extension/BaseExtensionConfigurations.kt index c1af6d55fe..c05a661a44 100644 --- a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/extension/BaseExtensionConfigurations.kt +++ b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/extension/BaseExtensionConfigurations.kt @@ -20,10 +20,10 @@ internal fun BaseExtension.configureCompilerOptions() { internal fun BaseExtension.configureCompose(project: Project) { val useCompose = with(project.path) { contains(":ui") || - contains(":onboarding") || + contains(":onboarding") || // TODO: divide on api/impl after migrating all onboarding to module contains(":presentation") || contains(":app") || // TODO: [REDACTED_JIRA] - contains(":tester:impl") // TODO: Rename module + contains(":impl") } buildFeatures.compose = useCompose if (useCompose) { diff --git a/settings.gradle.kts b/settings.gradle.kts index 70f5aeb9e2..2c4e5ecbcc 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -57,6 +57,9 @@ include(":features:swap:presentation") include(":features:tester:api") include(":features:tester:impl") + +include(":features:wallet:api") +include(":features:wallet:impl") // endregion Feature modules // region Domain modules From 2c09165a32ffcad219166164c218a278790d33ac Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 3 May 2023 15:53:16 +0500 Subject: [PATCH 02/14] Updated on 2026-08-14 --- app/src/main/assets/testnet_tokens.json | 11 ++++++ .../tap/common/extensions/Blockchain.kt | 1 + .../main/res/drawable/ic_cosmos_no_color.xml | 39 +++++++++++++++++++ .../core/ui/extensions/BlockchainIcons.kt | 1 + .../src/main/res/drawable/img_cosmos_22.xml | 39 +++++++++++++++++++ .../domain/common/extensions/Blockchain.kt | 5 +++ gradle/dependencies.toml | 2 +- 7 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 app/src/main/res/drawable/ic_cosmos_no_color.xml create mode 100644 core/ui/src/main/res/drawable/img_cosmos_22.xml diff --git a/app/src/main/assets/testnet_tokens.json b/app/src/main/assets/testnet_tokens.json index d36455384c..b7f5b4f8e4 100644 --- a/app/src/main/assets/testnet_tokens.json +++ b/app/src/main/assets/testnet_tokens.json @@ -484,6 +484,17 @@ "networkId": "ravencoin/test" } ] + }, + { + "id": "cosmos", + "symbol": "ATOM", + "name": "Cosmos Hub", + "networks": + [ + { + "networkId": "cosmos/test" + } + ] } ] } diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt b/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt index 5aae0ce706..58a7fa8076 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt @@ -40,6 +40,7 @@ fun Blockchain.getGreyedOutIconRes(): Int { Blockchain.TON, Blockchain.TONTestnet -> R.drawable.ic_ton_no_color Blockchain.Kava, Blockchain.KavaTestnet -> R.drawable.ic_kava_no_color Blockchain.Ravencoin, Blockchain.RavencoinTestnet -> R.drawable.ic_ravencoin_no_color + Blockchain.Cosmos, Blockchain.CosmosTestnet -> R.drawable.ic_cosmos_no_color else -> R.drawable.ic_tangem_logo } } diff --git a/app/src/main/res/drawable/ic_cosmos_no_color.xml b/app/src/main/res/drawable/ic_cosmos_no_color.xml new file mode 100644 index 0000000000..2b45162689 --- /dev/null +++ b/app/src/main/res/drawable/ic_cosmos_no_color.xml @@ -0,0 +1,39 @@ + + + + + + + + diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt index d760b3a96b..430fcb853f 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt @@ -36,6 +36,7 @@ fun getActiveIconRes(blockchainId: String): Int { "The-Open-Network", "The-Open-Network/test" -> R.drawable.img_ton_22 "KAVA", "KAVA/test" -> R.drawable.img_kava_22 "ravencoin", "ravencoin/test" -> R.drawable.img_ravencoin_22 + "cosmos", "cosmos/test" -> R.drawable.img_cosmos_22 else -> R.drawable.ic_alert_24 } } diff --git a/core/ui/src/main/res/drawable/img_cosmos_22.xml b/core/ui/src/main/res/drawable/img_cosmos_22.xml new file mode 100644 index 0000000000..14ea7757da --- /dev/null +++ b/core/ui/src/main/res/drawable/img_cosmos_22.xml @@ -0,0 +1,39 @@ + + + + + + + + diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt index fe9b1dfc51..92d51a1c57 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt @@ -56,6 +56,8 @@ fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? { "kava/test" -> Blockchain.KavaTestnet "ravencoin" -> Blockchain.Ravencoin "ravencoin/test" -> Blockchain.RavencoinTestnet + "cosmos" -> Blockchain.Cosmos + "cosmos/test" -> Blockchain.CosmosTestnet else -> null } } @@ -116,6 +118,8 @@ fun Blockchain.toNetworkId(): String { Blockchain.KavaTestnet -> "kava/test" Blockchain.Ravencoin -> "ravencoin" Blockchain.RavencoinTestnet -> "ravencoin/test" + Blockchain.Cosmos -> "cosmos" + Blockchain.CosmosTestnet -> "cosmos/test" } } @@ -154,6 +158,7 @@ fun Blockchain.toCoinId(): String { Blockchain.Unknown -> "unknown" Blockchain.Kava, Blockchain.KavaTestnet -> "kava" Blockchain.Ravencoin, Blockchain.RavencoinTestnet -> "ravencoin" + Blockchain.Cosmos, Blockchain.CosmosTestnet -> "cosmos" } } diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 47877b952c..8181f50b3c 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -72,7 +72,7 @@ arrow = "1.2.0-RC" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-209" +tangemBlockchainSdk = "develop-210" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-229" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds From b0f23e3660956bf91bc459bc1d10073033951004 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 4 May 2023 15:13:42 +0800 Subject: [PATCH 03/14] Updated on 2026-08-14 --- .../java/com/tangem/tap/TapApplication.kt | 20 +++++++++---------- .../tap/features/wallet/ui/WalletViewModel.kt | 6 ++---- .../tap/proxy/redux/DaggerGraphAction.kt | 11 ---------- .../tap/proxy/redux/DaggerGraphReducer.kt | 6 ------ 4 files changed, 11 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index f8efc046b2..58a834e134 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -53,7 +53,7 @@ import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeature import com.tangem.tap.features.tokens.api.featuretoggles.TokensListFeatureToggles import com.tangem.tap.persistence.PreferencesStorage import com.tangem.tap.proxy.AppStateHolder -import com.tangem.tap.proxy.redux.DaggerGraphAction +import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.wallet.BuildConfig import dagger.hilt.android.HiltAndroidApp import kotlinx.coroutines.launch @@ -142,7 +142,14 @@ class TapApplication : Application(), ImageLoaderFactory { appReducer(action, state, appStateHolder) }, middleware = AppState.getMiddleware(), - state = AppState(), + state = AppState( + daggerGraphState = DaggerGraphState( + assetReader = assetReader, + networkConnectionManager = networkConnectionManager, + tokensListFeatureToggles = tokensListFeatureToggles, + customTokenFeatureToggles = customTokenFeatureToggles, + ), + ), ) if (BuildConfig.DEBUG) { @@ -178,15 +185,6 @@ class TapApplication : Application(), ImageLoaderFactory { appStateHolder.userTokensRepository = userTokensRepository appStateHolder.walletStoresManager = walletStoresManager - store.dispatch( - action = DaggerGraphAction.SetApplicationDependencies( - assetReader = assetReader, - networkConnectionManager = networkConnectionManager, - tokensListFeatureToggles = tokensListFeatureToggles, - customTokenFeatureToggles = customTokenFeatureToggles, - ), - ) - scope.launch { featureTogglesManager.init() } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletViewModel.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletViewModel.kt index ce2e4ea085..ded1be23f3 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletViewModel.kt @@ -105,14 +105,12 @@ internal class WalletViewModel @Inject constructor( private fun bootstrapSelectedWalletStoresChanges(manager: UserWalletsListManager) { observeWalletStoresUpdatesJob = manager.selectedUserWallet .map { it.walletId } - .flatMapLatest { selectedUserWalletId -> - walletStoresManager.get(selectedUserWalletId) - } + .flatMapLatest(walletStoresManager::get) .debounce { walletStores -> if (walletStores.isNotEmpty()) WALLET_STORES_DEBOUNCE_TIMEOUT else 0 } .onEach { walletStores -> - store.dispatch(WalletAction.WalletStoresChanged(walletStores)) + store.dispatchOnMain(WalletAction.WalletStoresChanged(walletStores)) } .launchIn(viewModelScope) } diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt index 65a4cdb3e9..2952ed0257 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt @@ -1,20 +1,9 @@ package com.tangem.tap.proxy.redux -import com.tangem.datasource.asset.AssetReader -import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.features.tester.api.TesterRouter -import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles -import com.tangem.tap.features.tokens.api.featuretoggles.TokensListFeatureToggles import org.rekotlin.Action sealed interface DaggerGraphAction : Action { - data class SetApplicationDependencies( - val assetReader: AssetReader, - val networkConnectionManager: NetworkConnectionManager, - val tokensListFeatureToggles: TokensListFeatureToggles, - val customTokenFeatureToggles: CustomTokenFeatureToggles, - ) : DaggerGraphAction - data class SetActivityDependencies(val testerRouter: TesterRouter) : DaggerGraphAction } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt index 3bb0e65284..43cb94af5c 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt @@ -12,12 +12,6 @@ object DaggerGraphReducer { private fun internalReduce(action: DaggerGraphAction, state: AppState): DaggerGraphState { return when (action) { - is DaggerGraphAction.SetApplicationDependencies -> state.daggerGraphState.copy( - assetReader = action.assetReader, - networkConnectionManager = action.networkConnectionManager, - tokensListFeatureToggles = action.tokensListFeatureToggles, - customTokenFeatureToggles = action.customTokenFeatureToggles, - ) is DaggerGraphAction.SetActivityDependencies -> state.daggerGraphState.copy( testerRouter = action.testerRouter, ) From 938d7baee1e0e1ca283bb6a6da8ed19ac4039019 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 4 May 2023 12:49:25 +0300 Subject: [PATCH 04/14] Updated on 2026-08-14 --- .../tokens/impl/presentation/ui/TokenItem.kt | 1 + .../com/tangem/core/ui/components/Shimmers.kt | 80 ++++ .../com/tangem/core/ui/res/TangemDimens.kt | 1 + core/ui/src/main/res/drawable/ic_drag_24.xml | 10 + .../main/res/drawable/img_arrow_down_8.xml | 9 + .../src/main/res/drawable/img_arrow_up_8.xml | 9 + .../src/main/res/drawable/img_loader_15.xml | 19 + features/wallet/impl/build.gradle.kts | 2 + .../feature/wallet/presentation/ui/Tokens.kt | 355 ++++++++++++++++++ .../presentation/ui/WalletHeader.kt | 52 +-- .../presentation/ui/config/TokenV2Config.kt | 41 ++ .../ui/config}/WalletHeaderConfig.kt | 3 +- .../presentation/ui/state}/BalanceUIState.kt | 2 +- .../presentation/ui/state/TokenUIState.kt | 9 + 14 files changed, 569 insertions(+), 24 deletions(-) create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/Shimmers.kt create mode 100644 core/ui/src/main/res/drawable/ic_drag_24.xml create mode 100644 core/ui/src/main/res/drawable/img_arrow_down_8.xml create mode 100644 core/ui/src/main/res/drawable/img_arrow_up_8.xml create mode 100644 core/ui/src/main/res/drawable/img_loader_15.xml create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/Tokens.kt rename features/wallet/impl/src/main/java/com/tangem/feature/{main => wallet}/presentation/ui/WalletHeader.kt (79%) create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/config/TokenV2Config.kt rename features/wallet/impl/src/main/java/com/tangem/feature/{main/presentation/ui => wallet/presentation/ui/config}/WalletHeaderConfig.kt (67%) rename features/wallet/impl/src/main/java/com/tangem/feature/{main/presentation/ui => wallet/presentation/ui/state}/BalanceUIState.kt (53%) create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/state/TokenUIState.kt diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokenItem.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokenItem.kt index 9f3c695bb2..b3ee9856f3 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokenItem.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokenItem.kt @@ -139,6 +139,7 @@ private fun Icon(name: String, iconUrl: String, modifier: Modifier = Modifier) { ) } +// TODO(use CurrencyPlaceholderIcon here?) @Composable private fun PlaceholderIcon(name: String, modifier: Modifier = Modifier) { Box( diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Shimmers.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Shimmers.kt new file mode 100644 index 0000000000..3b6227abbc --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/Shimmers.kt @@ -0,0 +1,80 @@ +package com.tangem.core.ui.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.tangem.core.ui.res.TangemTheme +import com.valentinilk.shimmer.shimmer + +/** + * Rectangle shimmer item with rounded shape from DS + */ +@Composable +fun ShimmerRectangle(modifier: Modifier = Modifier) { + Box( + modifier = modifier + .shimmer() + .background( + color = TangemTheme.colors.button.secondary, + shape = RoundedCornerShape(TangemTheme.dimens.radius6), + ), + ) +} + +/** + * Circle shimmer item + * Size should be set in modifier + */ +@Composable +fun CircleShimmer(modifier: Modifier = Modifier) { + Box(modifier = modifier.shimmer()) { + Box( + modifier = Modifier + .matchParentSize() + .background( + color = TangemTheme.colors.button.secondary, + shape = CircleShape, + ), + ) + } +} + +// region preview + +@Composable +private fun ShimmersPreview() { + Column( + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing18), + ) { + ShimmerRectangle( + modifier = Modifier.size( + width = TangemTheme.dimens.size72, + height = TangemTheme.dimens.size12, + ), + ) + CircleShimmer(modifier = Modifier.size(size = TangemTheme.dimens.size42)) + } +} + +@Preview(showBackground = true) +@Composable +private fun Shimmers_InLightTheme() { + TangemTheme(isDark = false) { + ShimmersPreview() + } +} + +@Preview(showBackground = true) +@Composable +private fun Shimmers_InDarkTheme() { + TangemTheme(isDark = true) { + ShimmersPreview() + } +} + +// endregion preview \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt index 5f37be0e27..a3b0fc8c7e 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt @@ -58,6 +58,7 @@ data class TangemDimens internal constructor( val size50: Dp = 50.dp, val size56: Dp = 56.dp, val size62: Dp = 62.dp, + val size68: Dp = 68.dp, val size72: Dp = 72.dp, val size80: Dp = 80.dp, val size84: Dp = 84.dp, diff --git a/core/ui/src/main/res/drawable/ic_drag_24.xml b/core/ui/src/main/res/drawable/ic_drag_24.xml new file mode 100644 index 0000000000..9246315ec7 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_drag_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/core/ui/src/main/res/drawable/img_arrow_down_8.xml b/core/ui/src/main/res/drawable/img_arrow_down_8.xml new file mode 100644 index 0000000000..355357c8c3 --- /dev/null +++ b/core/ui/src/main/res/drawable/img_arrow_down_8.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/src/main/res/drawable/img_arrow_up_8.xml b/core/ui/src/main/res/drawable/img_arrow_up_8.xml new file mode 100644 index 0000000000..0d99c9d789 --- /dev/null +++ b/core/ui/src/main/res/drawable/img_arrow_up_8.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/src/main/res/drawable/img_loader_15.xml b/core/ui/src/main/res/drawable/img_loader_15.xml new file mode 100644 index 0000000000..3798701a08 --- /dev/null +++ b/core/ui/src/main/res/drawable/img_loader_15.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index ea6af49097..873d96fbe0 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -11,6 +11,8 @@ dependencies { implementation(deps.androidx.activity.compose) /** Compose */ + implementation(deps.compose.coil) + implementation(deps.compose.constraintLayout) implementation(deps.compose.material) implementation(deps.compose.foundation) implementation(deps.compose.navigation) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/Tokens.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/Tokens.kt new file mode 100644 index 0000000000..6d178bdab6 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/Tokens.kt @@ -0,0 +1,355 @@ +package com.tangem.feature.wallet.presentation.ui + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.Icon +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.Dimension +import coil.compose.SubcomposeAsyncImage +import coil.request.ImageRequest +import com.tangem.core.ui.R +import com.tangem.core.ui.components.* +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemTypography +import com.tangem.feature.wallet.presentation.ui.config.PriceChange +import com.tangem.feature.wallet.presentation.ui.config.PriceChangeType +import com.tangem.feature.wallet.presentation.ui.config.TokenV2Config +import com.tangem.feature.wallet.presentation.ui.state.TokenOptionsUIState +import com.tangem.feature.wallet.presentation.ui.state.TokenUIState + +private const val DOTS = "•••" + +@Composable +fun TokenItemV2(config: TokenV2Config) { + when (config.tokenUIState) { + TokenUIState.LOADED -> LoadedTokenState(config) + TokenUIState.LOADING -> LoadingTokenState() + } +} + +@Composable +private fun LoadedTokenState(config: TokenV2Config) { + BaseSurface { + ConstraintLayout( + modifier = Modifier + .fillMaxWidth() + .padding( + horizontal = TangemTheme.dimens.spacing12, + vertical = TangemTheme.dimens.spacing4, + ), + ) { + val (iconItem, tokenNameItem, fiatItem) = createRefs() + TokenIcon( + modifier = Modifier.constrainAs(iconItem) { + centerVerticallyTo(parent) + start.linkTo(parent.start) + }, + tokenIconUrl = config.iconUrl, + icon = config.icon, + networkIconRes = config.networkIcon, + ) + TokenTitleAmountBlock( + modifier = Modifier + .padding(horizontal = TangemTheme.dimens.spacing12) + .constrainAs(tokenNameItem) { + centerVerticallyTo(parent) + start.linkTo(iconItem.end) + end.linkTo(fiatItem.start) + width = Dimension.fillToConstraints + }, + title = config.name, + amount = config.amount, + hasPending = config.hasPending, + ) + TokenOptionsBlock( + config = config, + modifier = Modifier.constrainAs(fiatItem) { + centerVerticallyTo(parent) + end.linkTo(parent.end) + }, + ) + } + } +} + +/** + * Block for end part of token item + * shows status is reachable, is drag, hidden or show balance + */ +@Composable +private fun TokenOptionsBlock(config: TokenV2Config, modifier: Modifier = Modifier) { + when (config.tokenOptionsUIState) { + TokenOptionsUIState.VISIBLE -> TokenFiatPercentageBlock( + modifier = modifier, + fiatAmount = config.fiatAmount, + priceChange = config.priceChange, + ) + TokenOptionsUIState.UNREACHABLE -> Text( + modifier = modifier, + text = "Unreachable", // TODO (conform this text) + style = TangemTypography.body2, + color = TangemTheme.colors.text.tertiary, + ) + TokenOptionsUIState.HIDDEN -> TokenFiatPercentageBlock( + modifier = modifier, + fiatAmount = DOTS, + priceChange = config.priceChange, + ) + TokenOptionsUIState.DRAG -> Icon( + modifier = modifier, + painter = painterResource(id = R.drawable.ic_drag_24), + tint = TangemTheme.colors.icon.informative, + contentDescription = null, + ) + } +} + +@Composable +private fun LoadingTokenState() { + BaseSurface { + Row( + modifier = Modifier + .fillMaxWidth() + .padding( + horizontal = TangemTheme.dimens.spacing12, + vertical = TangemTheme.dimens.spacing4, + ), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + ) { + CircleShimmer(modifier = Modifier.size(size = TangemTheme.dimens.size42)) + SpacerW12() + Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) { + ShimmerRectangle( + modifier = Modifier.size( + width = TangemTheme.dimens.size72, + height = TangemTheme.dimens.size12, + ), + ) + ShimmerRectangle( + modifier = Modifier.size( + width = TangemTheme.dimens.size50, + height = TangemTheme.dimens.size12, + ), + ) + } + } + Column( + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), + ) { + ShimmerRectangle( + modifier = Modifier.size( + width = TangemTheme.dimens.size40, + height = TangemTheme.dimens.size12, + ), + ) + ShimmerRectangle( + modifier = Modifier.size( + width = TangemTheme.dimens.size40, + height = TangemTheme.dimens.size12, + ), + ) + } + } +} + +@OptIn(ExperimentalMaterialApi::class) +@Composable +private fun BaseSurface(onClick: (() -> Unit)? = null, content: @Composable () -> Unit) { + Surface( + modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size68), + color = TangemTheme.colors.background.primary, + onClick = onClick ?: {}, + enabled = onClick != null, + ) { + content() + } +} + +@Composable +private fun TokenTitleAmountBlock(title: String, amount: String, hasPending: Boolean, modifier: Modifier = Modifier) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2), + ) { + Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) { + Text( + text = title, + style = TangemTypography.subtitle2, + color = TangemTheme.colors.text.primary1, + ) + if (hasPending) { + Image( + modifier = Modifier.align(Alignment.CenterVertically), + painter = painterResource(id = R.drawable.img_loader_15), + contentDescription = null, + ) + } + } + Text( + text = amount, + style = TangemTypography.body2, + color = TangemTheme.colors.text.tertiary, + ) + } +} + +@Composable +private fun TokenFiatPercentageBlock(fiatAmount: String, priceChange: PriceChange, modifier: Modifier = Modifier) { + Column(modifier = modifier.requiredWidth(IntrinsicSize.Max)) { + Text( + modifier = Modifier.align(Alignment.End), + text = fiatAmount, + style = TangemTypography.body2, + color = TangemTheme.colors.text.primary1, + ) + SpacerH2() + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End, + ) { + val iconChangeArrow: Int + val changeTextColor: Color + if (priceChange.type == PriceChangeType.UP) { + iconChangeArrow = R.drawable.img_arrow_up_8 + changeTextColor = TangemTheme.colors.text.accent + } else { + iconChangeArrow = R.drawable.img_arrow_down_8 + changeTextColor = TangemTheme.colors.text.warning + } + Image( + modifier = Modifier.align(Alignment.CenterVertically), + painter = painterResource(id = iconChangeArrow), + contentDescription = null, + ) + SpacerW4() + Text( + modifier = Modifier.align(Alignment.CenterVertically), + text = priceChange.valuePercent, + style = TangemTypography.body2, + color = changeTextColor, + ) + } + } +} + +@Composable +private fun TokenIcon( + tokenIconUrl: String?, + modifier: Modifier = Modifier, + @DrawableRes icon: Int? = null, + @DrawableRes networkIconRes: Int? = null, +) { + Box( + modifier = modifier + .padding(end = TangemTheme.dimens.spacing16) + .size(TangemTheme.dimens.size42), + ) { + val tokenImageModifier = Modifier + .align(Alignment.BottomStart) + .size(TangemTheme.dimens.size36) + + val data = if (tokenIconUrl.isNullOrEmpty()) { + icon + } else { + tokenIconUrl + } + SubcomposeAsyncImage( + modifier = tokenImageModifier, + model = ImageRequest.Builder(LocalContext.current) + .data(data) + .crossfade(true) + .build(), + loading = { CircleShimmer(modifier = tokenImageModifier) }, + contentDescription = null, + ) + + if (networkIconRes != null) { + Box( + modifier = Modifier + .align(Alignment.TopEnd) + .size(TangemTheme.dimens.size18) + .background(color = Color.White, shape = CircleShape), + contentAlignment = Alignment.Center, + ) { + Image( + modifier = Modifier.padding(all = 0.5.dp), + painter = painterResource(id = networkIconRes), + contentDescription = null, + ) + } + } + } +} + +// region preview + +@Composable +private fun TokensPreview() { + Column( + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing18), + ) { + val config = TokenV2Config( + name = "Polygon", + amount = "5,412 MATIC", + fiatAmount = "321 $", + priceChange = PriceChange( + valuePercent = "2%", + type = PriceChangeType.UP, + ), + iconUrl = null, + icon = R.drawable.img_polygon_22, + networkIcon = R.drawable.img_polygon_22, + tokenUIState = TokenUIState.LOADED, + tokenOptionsUIState = TokenOptionsUIState.VISIBLE, + hasPending = true, + ) + // Loaded item + TokenItemV2(config) + // Unreachable item + TokenItemV2(config.copy(tokenOptionsUIState = TokenOptionsUIState.UNREACHABLE)) + // Drag item + TokenItemV2(config.copy(tokenOptionsUIState = TokenOptionsUIState.DRAG)) + // Hidden item + TokenItemV2(config.copy(tokenOptionsUIState = TokenOptionsUIState.UNREACHABLE)) + // Loading item + TokenItemV2( + config.copy( + tokenUIState = TokenUIState.LOADING, + ), + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun Preview_Tokens_InLightTheme() { + TangemTheme(isDark = false) { + TokensPreview() + } +} + +@Preview(showBackground = true) +@Composable +private fun Preview_Tokens_InDarkTheme() { + TangemTheme(isDark = true) { + TokensPreview() + } +} + +// endregion preview \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/WalletHeader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletHeader.kt similarity index 79% rename from features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/WalletHeader.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletHeader.kt index 8c2ba58d7e..13eb2f12f3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/WalletHeader.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/WalletHeader.kt @@ -1,9 +1,7 @@ -package com.tangem.feature.main.presentation.ui +package com.tangem.feature.wallet.presentation.ui import androidx.compose.foundation.Image -import androidx.compose.foundation.background import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Icon import androidx.compose.material.Surface @@ -14,10 +12,15 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.sp +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.Dimension import com.tangem.core.ui.R import com.tangem.core.ui.components.* import com.tangem.core.ui.res.TangemTheme -import com.valentinilk.shimmer.shimmer +import com.tangem.feature.wallet.presentation.ui.config.WalletHeaderConfig +import com.tangem.feature.wallet.presentation.ui.state.BalanceUIState + +private const val DOTS = "•••" @OptIn(ExperimentalMaterialApi::class) @Composable @@ -29,14 +32,19 @@ fun WalletHeaderCard(config: WalletHeaderConfig, modifier: Modifier = Modifier) onClick = config.onClick ?: {}, enabled = config.onClick != null, ) { - Row( + ConstraintLayout( modifier = Modifier .fillMaxWidth() - .height(intrinsicSize = IntrinsicSize.Max), - horizontalArrangement = Arrangement.SpaceBetween, + .padding(horizontal = TangemTheme.dimens.spacing12), ) { + val (balanceBlock, imageItem) = createRefs() Column( - modifier = Modifier.padding(all = TangemTheme.dimens.spacing12), + modifier = Modifier.constrainAs(balanceBlock) { + centerVerticallyTo(parent) + start.linkTo(parent.start) + end.linkTo(imageItem.start) + width = Dimension.fillToConstraints + }, verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), ) { HeaderWalletName( @@ -53,11 +61,15 @@ fun WalletHeaderCard(config: WalletHeaderConfig, modifier: Modifier = Modifier) style = TangemTheme.typography.caption, ) } + val imageWidth = TangemTheme.dimens.size120 Image( - modifier = Modifier - .width(TangemTheme.dimens.size120) - .padding(end = TangemTheme.dimens.spacing18) - .fillMaxHeight(), + modifier = Modifier.constrainAs(imageItem) { + centerVerticallyTo(parent) + top.linkTo(parent.top) + end.linkTo(parent.end) + height = Dimension.fillToConstraints + width = Dimension.value(imageWidth) + }, painter = config.cardImage, contentDescription = null, contentScale = ContentScale.FillWidth, @@ -106,20 +118,16 @@ private fun HeaderBalanceTitle(balance: String, balanceState: BalanceUIState) { ) } BalanceUIState.LOADING -> { - Box( - modifier = Modifier - .padding(vertical = TangemTheme.dimens.spacing4) - .size(width = TangemTheme.dimens.size102, height = TangemTheme.dimens.size24) - .shimmer() - .background( - color = TangemTheme.colors.button.secondary, - shape = RoundedCornerShape(TangemTheme.dimens.radius6), - ), + ShimmerRectangle( + modifier = Modifier.size( + width = TangemTheme.dimens.size102, + height = TangemTheme.dimens.size24, + ), ) } BalanceUIState.HIDDEN -> { Text( - text = "•••", + text = DOTS, color = TangemTheme.colors.text.primary1, style = TangemTheme.typography.h2, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/config/TokenV2Config.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/config/TokenV2Config.kt new file mode 100644 index 0000000000..bfa34bae3f --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/config/TokenV2Config.kt @@ -0,0 +1,41 @@ +package com.tangem.feature.wallet.presentation.ui.config + +import androidx.annotation.DrawableRes +import com.tangem.feature.wallet.presentation.ui.state.TokenOptionsUIState +import com.tangem.feature.wallet.presentation.ui.state.TokenUIState + +/** + * Token config + * + * @property name of token/coin + * @property amount of token + * @property fiatAmount amount in fiat + * @property priceChange value of price changing + * @property iconUrl + * @property icon token/coin icon + * @property networkIcon + * @property tokenUIState token state [TokenUIState] loading etc. + * @property tokenOptionsUIState state for token options like 'unreachable', 'hidden' etc + * @property hasPending pending tx in blockchain + */ +data class TokenV2Config( + val name: String, + val amount: String, + val fiatAmount: String, + val priceChange: PriceChange, + val iconUrl: String?, + @DrawableRes val icon: Int?, + @DrawableRes val networkIcon: Int, + val tokenUIState: TokenUIState, + val tokenOptionsUIState: TokenOptionsUIState, + val hasPending: Boolean, +) + +data class PriceChange( + val valuePercent: String, + val type: PriceChangeType, +) + +enum class PriceChangeType { + UP, DOWN +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/WalletHeaderConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/config/WalletHeaderConfig.kt similarity index 67% rename from features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/WalletHeaderConfig.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/config/WalletHeaderConfig.kt index b8c22ca329..4a621be4e5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/WalletHeaderConfig.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/config/WalletHeaderConfig.kt @@ -1,6 +1,7 @@ -package com.tangem.feature.main.presentation.ui +package com.tangem.feature.wallet.presentation.ui.config import androidx.compose.ui.graphics.painter.Painter +import com.tangem.feature.wallet.presentation.ui.state.BalanceUIState data class WalletHeaderConfig( val walletName: String, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/BalanceUIState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/state/BalanceUIState.kt similarity index 53% rename from features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/BalanceUIState.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/state/BalanceUIState.kt index b6010c0f04..c4a22f046c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/main/presentation/ui/BalanceUIState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/state/BalanceUIState.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.main.presentation.ui +package com.tangem.feature.wallet.presentation.ui.state enum class BalanceUIState { VISIBLE, LOADING, HIDDEN, NONE diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/state/TokenUIState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/state/TokenUIState.kt new file mode 100644 index 0000000000..0ef9163e07 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/ui/state/TokenUIState.kt @@ -0,0 +1,9 @@ +package com.tangem.feature.wallet.presentation.ui.state + +enum class TokenUIState { + LOADED, LOADING +} + +enum class TokenOptionsUIState { + VISIBLE, HIDDEN, DRAG, UNREACHABLE +} \ No newline at end of file From 5d1cd85382907be3ad7f5b01f71e4003f4026d41 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 26 Apr 2023 13:20:44 +0300 Subject: [PATCH 05/14] Updated on 2026-08-14 --- app/build.gradle.kts | 1 + .../java/com/tangem/tap/TapApplication.kt | 11 ++- .../common/analytics/topup/TopUpController.kt | 34 ++++--- .../com/tangem/tap/common/chat/ChatManager.kt | 8 +- .../common/redux/global/GlobalMiddleware.kt | 7 +- .../domain/tasks/product/ScanProductTask.kt | 6 +- .../disclaimer/DisclaimerDataProvider.kt | 2 +- .../tap/features/disclaimer/DisclaimerType.kt | 6 +- .../features/onboarding/OnboardingManager.kt | 2 +- .../middlewares/AppCurrencyMiddleware.kt | 17 ++-- .../persistence/CardBalanceStateAdapter.kt | 20 ----- data/source/preferences/.gitignore | 1 + data/source/preferences/build.gradle.kts | 17 ++++ .../preferences/src/main/AndroidManifest.xml | 2 + .../preferences/AppRatingLaunchObserver.kt | 73 +++++++++++++++ .../preferences/PreferencesDataSource.kt | 89 +++---------------- .../preferences/adapters/BigDecimalAdapter.kt | 13 +++ .../adapters/CardBalanceStateAdapter.kt | 20 +++++ .../preferences/di/PreferencesStoreModule.kt | 19 ++++ .../preferences/model/DataSourceCurrency.kt | 10 +++ .../model/DataSourceFiatCurrency.kt | 7 ++ .../preferences/model/DataSourceTopupInfo.kt | 13 +++ .../model/DataSourceUsedCardInfo.kt | 8 ++ .../model/DataSourceUsedCardInfoOld.kt | 7 ++ .../storage}/DisclaimerPrefStorage.kt | 5 +- .../storage}/FiatCurrenciesPrefStorage.kt | 23 ++--- .../source/preferences/storage/Migration.kt | 5 ++ .../storage}/ToppedUpWalletStorage.kt | 30 +++---- .../storage}/UsedCardsPrefStorage.kt | 74 +++++++-------- settings.gradle.kts | 6 +- 30 files changed, 332 insertions(+), 204 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/persistence/CardBalanceStateAdapter.kt create mode 100644 data/source/preferences/.gitignore create mode 100644 data/source/preferences/build.gradle.kts create mode 100644 data/source/preferences/src/main/AndroidManifest.xml create mode 100644 data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/AppRatingLaunchObserver.kt rename app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt => data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/PreferencesDataSource.kt (60%) create mode 100644 data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/adapters/BigDecimalAdapter.kt create mode 100644 data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/adapters/CardBalanceStateAdapter.kt create mode 100644 data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/di/PreferencesStoreModule.kt create mode 100644 data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceCurrency.kt create mode 100644 data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceFiatCurrency.kt create mode 100644 data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceTopupInfo.kt create mode 100644 data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceUsedCardInfo.kt create mode 100644 data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceUsedCardInfoOld.kt rename {app/src/main/java/com/tangem/tap/persistence => data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage}/DisclaimerPrefStorage.kt (73%) rename {app/src/main/java/com/tangem/tap/persistence => data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage}/FiatCurrenciesPrefStorage.kt (65%) create mode 100644 data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/Migration.kt rename {app/src/main/java/com/tangem/tap/persistence => data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage}/ToppedUpWalletStorage.kt (56%) rename {app/src/main/java/com/tangem/tap/persistence => data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage}/UsedCardsPrefStorage.kt (67%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ea77304740..89ca7c3734 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,6 +22,7 @@ dependencies { implementation(project(":core:utils")) implementation(project(":libs:crypto")) implementation(project(":libs:auth")) + implementation(project(":data:source:preferences")) /** Features */ implementation(project(":features:onboarding")) diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 58a834e134..ad51eea81f 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -20,6 +20,7 @@ import com.tangem.datasource.config.models.Config import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.domain.DomainLayer import com.tangem.domain.common.LogConfig +import com.tangem.data.source.preferences.PreferencesDataSource import com.tangem.tap.common.IntentHandler import com.tangem.tap.common.analytics.AnalyticsFactory import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder @@ -51,7 +52,6 @@ import com.tangem.tap.domain.walletStores.repository.di.provideDefaultImplementa import com.tangem.tap.domain.walletconnect.WalletConnectRepository import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles import com.tangem.tap.features.tokens.api.featuretoggles.TokensListFeatureToggles -import com.tangem.tap.persistence.PreferencesStorage import com.tangem.tap.proxy.AppStateHolder import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.wallet.BuildConfig @@ -66,7 +66,7 @@ lateinit var store: Store lateinit var foregroundActivityObserver: ForegroundActivityObserver lateinit var activityResultCaller: ActivityResultCaller -lateinit var preferencesStorage: PreferencesStorage +lateinit var preferencesStorage: PreferencesDataSource lateinit var walletConnectRepository: WalletConnectRepository lateinit var shopService: TangemShopService lateinit var userTokensRepository: UserTokensRepository @@ -134,6 +134,9 @@ class TapApplication : Application(), ImageLoaderFactory { @Inject lateinit var customTokenFeatureToggles: CustomTokenFeatureToggles + @Inject + lateinit var preferencesDataSource: PreferencesDataSource + override fun onCreate() { super.onCreate() @@ -161,7 +164,7 @@ class TapApplication : Application(), ImageLoaderFactory { registerActivityLifecycleCallbacks(foregroundActivityObserver.callbacks) DomainLayer.init() - preferencesStorage = PreferencesStorage(this) + preferencesStorage = preferencesDataSource walletConnectRepository = WalletConnectRepository(this) val configLoader = FeaturesLocalLoader(assetReader, MoshiConverter.sdkMoshi, BuildConfig.ENVIRONMENT) @@ -246,7 +249,7 @@ class TapApplication : Application(), ImageLoaderFactory { private fun initFeedbackManager( context: Context, - preferencesStorage: PreferencesStorage, + preferencesStorage: PreferencesDataSource, foregroundActivityObserver: ForegroundActivityObserver, store: Store, ) { diff --git a/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt b/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt index 30caf9f355..97edaae4c1 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt @@ -3,9 +3,11 @@ package com.tangem.tap.common.analytics.topup import com.tangem.common.extensions.guard import com.tangem.common.extensions.isZero import com.tangem.core.analytics.Analytics +import com.tangem.data.source.preferences.model.DataSourceTopupInfo +import com.tangem.data.source.preferences.storage.ToppedUpWalletStorage import com.tangem.domain.common.CardTypesResolver -import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.UserWalletId +import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.models.scan.ScanResponse import com.tangem.tap.common.analytics.converters.TopUpEventConverter import com.tangem.tap.common.analytics.events.AnalyticsParam @@ -19,7 +21,6 @@ import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.domain.walletCurrencies.WalletCurrenciesManager import com.tangem.tap.domain.walletStores.WalletStoresManager import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.persistence.ToppedUpWalletStorage import com.tangem.tap.scope import kotlinx.coroutines.launch import java.math.BigDecimal @@ -90,9 +91,9 @@ class TopUpController( val isToppedUpInPast = findToppedUpCurrenciesInPast(walletDataModels).isNotEmpty() if (isToppedUpInPast) { - val newWalletInfo = ToppedUpWalletStorage.TopupInfo( + val newWalletInfo = DataSourceTopupInfo( walletId = userWalletId.stringValue, - cardBalanceState = AnalyticsParam.CardBalanceState.Full, + cardBalanceState = DataSourceTopupInfo.CardBalanceState.Full, ) topupWalletStorage.save(newWalletInfo) return@launch @@ -109,9 +110,9 @@ class TopUpController( fun registerEmptyWallet(scanResponse: ScanResponse) { UserWalletIdBuilder.scanResponse(scanResponse).build()?.let { topupWalletStorage.save( - ToppedUpWalletStorage.TopupInfo( + DataSourceTopupInfo( walletId = it.stringValue, - cardBalanceState = AnalyticsParam.CardBalanceState.Empty, + cardBalanceState = DataSourceTopupInfo.CardBalanceState.Empty, ), ) } @@ -129,17 +130,28 @@ class TopUpController( cardTypesResolver: CardTypesResolver, ) { val topupInfo = topupWalletStorage.restore(userWalletId.stringValue).guard { - val topupInfo = ToppedUpWalletStorage.TopupInfo( + val topupInfo = DataSourceTopupInfo( walletId = userWalletId.stringValue, - cardBalanceState = cardBalanceState, + cardBalanceState = when (cardBalanceState) { + AnalyticsParam.CardBalanceState.BlockchainError -> + DataSourceTopupInfo.CardBalanceState.BlockchainError + AnalyticsParam.CardBalanceState.CustomToken -> + DataSourceTopupInfo.CardBalanceState.CustomToken + AnalyticsParam.CardBalanceState.Empty -> + DataSourceTopupInfo.CardBalanceState.Empty + AnalyticsParam.CardBalanceState.Full -> + DataSourceTopupInfo.CardBalanceState.Full + }, ) topupWalletStorage.save(topupInfo) return } - if (topupInfo.isToppedUp) return - if (!topupInfo.isToppedUp && cardBalanceState.isToppedUp()) { - topupWalletStorage.save(topupInfo.copy(cardBalanceState = AnalyticsParam.CardBalanceState.Full)) + val isToppedUp = topupInfo.cardBalanceState == DataSourceTopupInfo.CardBalanceState.Full + if (isToppedUp) return + + if (cardBalanceState.isToppedUp()) { + topupWalletStorage.save(topupInfo.copy(cardBalanceState = DataSourceTopupInfo.CardBalanceState.Full)) TopUpEventConverter().convert(cardTypesResolver)?.let { Analytics.send(it) } diff --git a/app/src/main/java/com/tangem/tap/common/chat/ChatManager.kt b/app/src/main/java/com/tangem/tap/common/chat/ChatManager.kt index bc69217fa1..182aa7350b 100644 --- a/app/src/main/java/com/tangem/tap/common/chat/ChatManager.kt +++ b/app/src/main/java/com/tangem/tap/common/chat/ChatManager.kt @@ -2,19 +2,19 @@ package com.tangem.tap.common.chat import android.content.Context import android.os.Build -import com.tangem.tap.ForegroundActivityObserver import com.tangem.datasource.config.models.ChatConfig -import com.tangem.tap.common.chat.opener.ChatOpener import com.tangem.datasource.config.models.SprinklrConfig import com.tangem.datasource.config.models.ZendeskConfig +import com.tangem.data.source.preferences.PreferencesDataSource +import com.tangem.tap.ForegroundActivityObserver +import com.tangem.tap.common.chat.opener.ChatOpener import com.tangem.tap.common.chat.opener.implementation.SprinklrChatOpener import com.tangem.tap.common.chat.opener.implementation.ZendeskChatOpener import com.tangem.tap.common.redux.AppState -import com.tangem.tap.persistence.PreferencesStorage import org.rekotlin.Store class ChatManager( - private val preferencesStorage: PreferencesStorage, + private val preferencesStorage: PreferencesDataSource, private val foregroundActivityObserver: ForegroundActivityObserver, private val store: Store, ) { diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt index 39d29a451f..01d00c9a82 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt @@ -5,11 +5,12 @@ import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.guard import com.tangem.datasource.config.models.Config import com.tangem.domain.common.LogConfig -import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.extensions.withMainContext +import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ProductType import com.tangem.domain.models.scan.ScanResponse +import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.extensions.dispatchDebugErrorNotification import com.tangem.tap.common.extensions.dispatchDialogShow import com.tangem.tap.common.extensions.dispatchOnMain @@ -75,7 +76,9 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di is GlobalAction.RestoreAppCurrency -> { store.dispatch( GlobalAction.RestoreAppCurrency.Success( - preferencesStorage.fiatCurrenciesPrefStorage.getAppCurrency(), + preferencesStorage.fiatCurrenciesPrefStorage.getAppCurrency() + ?.run { FiatCurrency(code, name, symbol) } + ?: FiatCurrency.Default, ), ) } diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt index 8f7fe94bac..86c7ecc477 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt @@ -19,9 +19,6 @@ import com.tangem.common.tlv.Tlv import com.tangem.common.tlv.TlvDecoder import com.tangem.crypto.CryptoUtils import com.tangem.crypto.hdWallet.DerivationPath -import com.tangem.domain.models.scan.CardDTO -import com.tangem.domain.models.scan.ProductType -import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.common.TapWorkarounds.isExcluded import com.tangem.domain.common.TapWorkarounds.isNotSupportedInThatRelease import com.tangem.domain.common.TapWorkarounds.isSaltPay @@ -29,6 +26,9 @@ import com.tangem.domain.common.TapWorkarounds.isStart2Coin import com.tangem.domain.common.TapWorkarounds.isTangemTwins import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation import com.tangem.domain.common.TwinsHelper +import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.models.scan.ProductType +import com.tangem.domain.models.scan.ScanResponse import com.tangem.operations.PreflightReadMode import com.tangem.operations.PreflightReadTask import com.tangem.operations.ScanTask diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/DisclaimerDataProvider.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/DisclaimerDataProvider.kt index 0097f34142..9d3b4aeda6 100644 --- a/app/src/main/java/com/tangem/tap/features/disclaimer/DisclaimerDataProvider.kt +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/DisclaimerDataProvider.kt @@ -1,6 +1,6 @@ package com.tangem.tap.features.disclaimer -import com.tangem.tap.persistence.DisclaimerPrefStorage +import com.tangem.data.source.preferences.storage.DisclaimerPrefStorage /** [REDACTED_AUTHOR] diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/DisclaimerType.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/DisclaimerType.kt index 30b3f43d1c..56519de4ff 100644 --- a/app/src/main/java/com/tangem/tap/features/disclaimer/DisclaimerType.kt +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/DisclaimerType.kt @@ -1,11 +1,11 @@ package com.tangem.tap.features.disclaimer -import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.common.TapWorkarounds.isSaltPay import com.tangem.domain.common.TapWorkarounds.isStart2Coin -import com.tangem.tap.persistence.DisclaimerPrefStorage +import com.tangem.domain.models.scan.CardDTO +import com.tangem.data.source.preferences.storage.DisclaimerPrefStorage import com.tangem.tap.preferencesStorage -import java.util.* +import java.util.Locale /** [REDACTED_AUTHOR] diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt index e35aa85d7a..bf80a5b458 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt @@ -15,7 +15,7 @@ import com.tangem.tap.domain.extensions.getOrLoadCardArtworkUrl import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.models.hasPendingTransactions import com.tangem.tap.features.wallet.redux.ProgressState -import com.tangem.tap.persistence.UsedCardsPrefStorage +import com.tangem.data.source.preferences.storage.UsedCardsPrefStorage import timber.log.Timber import java.math.BigDecimal diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/AppCurrencyMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/AppCurrencyMiddleware.kt index cabb464853..bc5e960649 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/AppCurrencyMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/AppCurrencyMiddleware.kt @@ -2,7 +2,9 @@ package com.tangem.tap.features.wallet.redux.middlewares import com.tangem.common.extensions.guard import com.tangem.core.analytics.Analytics -import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse +import com.tangem.data.source.preferences.model.DataSourceCurrency +import com.tangem.data.source.preferences.model.DataSourceFiatCurrency +import com.tangem.data.source.preferences.storage.FiatCurrenciesPrefStorage import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.MainScreen import com.tangem.tap.common.entities.FiatCurrency @@ -14,7 +16,6 @@ import com.tangem.tap.features.wallet.domain.WalletRepository import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.models.WalletDialog import com.tangem.tap.features.walletSelector.redux.WalletSelectorAction -import com.tangem.tap.persistence.FiatCurrenciesPrefStorage import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.tap.userWalletsListManager @@ -47,8 +48,10 @@ class AppCurrencyMiddleware( scope.launch { runCatching { walletRepository.getCurrencyList() } - .onSuccess { - val currenciesList = it.currencies + .onSuccess { response -> + val currenciesList = response.currencies + .map { with(it) { DataSourceCurrency(id, code, name, rateBTC, unit, type) } } + if (currenciesList.isNotEmpty() && currenciesList.toSet() != storedFiatCurrencies.toSet()) { fiatCurrenciesPrefStorage.save(currenciesList) store.dispatchDialogShow( @@ -64,7 +67,9 @@ class AppCurrencyMiddleware( private fun selectCurrency(action: WalletAction.AppCurrencyAction.SelectAppCurrency) { Analytics.send(MainScreen.MainCurrencyChanged(AnalyticsParam.CurrencyType.FiatCurrency(action.fiatCurrency))) - fiatCurrenciesPrefStorage.saveAppCurrency(action.fiatCurrency) + fiatCurrenciesPrefStorage.saveAppCurrency( + with(action.fiatCurrency) { DataSourceFiatCurrency(code, name, symbol) }, + ) store.dispatch(GlobalAction.ChangeAppCurrency(action.fiatCurrency)) store.dispatch(DetailsAction.ChangeAppCurrency(action.fiatCurrency)) store.dispatch(WalletSelectorAction.ChangeAppCurrency(action.fiatCurrency)) @@ -77,7 +82,7 @@ class AppCurrencyMiddleware( } } - private fun List.mapToUiModel(): List { + private fun List.mapToUiModel(): List { return this.map { FiatCurrency( code = it.code, diff --git a/app/src/main/java/com/tangem/tap/persistence/CardBalanceStateAdapter.kt b/app/src/main/java/com/tangem/tap/persistence/CardBalanceStateAdapter.kt deleted file mode 100644 index 0385071ff3..0000000000 --- a/app/src/main/java/com/tangem/tap/persistence/CardBalanceStateAdapter.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.tangem.tap.persistence - -import com.squareup.moshi.FromJson -import com.squareup.moshi.ToJson -import com.tangem.tap.common.analytics.events.AnalyticsParam - -class CardBalanceStateAdapter { - - @ToJson - fun toJson(src: AnalyticsParam.CardBalanceState): String = src.value - - @FromJson - fun fromJson(json: String): AnalyticsParam.CardBalanceState { - return when (json) { - AnalyticsParam.CardBalanceState.Empty.value -> AnalyticsParam.CardBalanceState.Empty - AnalyticsParam.CardBalanceState.Full.value -> AnalyticsParam.CardBalanceState.Full - else -> error("CardBalanceState not found") - } - } -} \ No newline at end of file diff --git a/data/source/preferences/.gitignore b/data/source/preferences/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/data/source/preferences/.gitignore @@ -0,0 +1 @@ +/build diff --git a/data/source/preferences/build.gradle.kts b/data/source/preferences/build.gradle.kts new file mode 100644 index 0000000000..7cd9e54515 --- /dev/null +++ b/data/source/preferences/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.kapt) + alias(deps.plugins.android.library) + id("configuration") +} + +dependencies { + implementation(deps.androidx.core.ktx) + implementation(deps.moshi) + implementation(deps.moshi.kotlin) + implementation(deps.hilt.android) + kapt(deps.hilt.kapt) + + // For MoshiJsonConverter + implementation(deps.tangem.card.core) +} \ No newline at end of file diff --git a/data/source/preferences/src/main/AndroidManifest.xml b/data/source/preferences/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..67568f1b9e --- /dev/null +++ b/data/source/preferences/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/AppRatingLaunchObserver.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/AppRatingLaunchObserver.kt new file mode 100644 index 0000000000..d65eb3813b --- /dev/null +++ b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/AppRatingLaunchObserver.kt @@ -0,0 +1,73 @@ +package com.tangem.data.source.preferences + +import android.content.SharedPreferences +import androidx.core.content.edit +import java.util.* + +@Deprecated("Create repository instead") +class AppRatingLaunchObserver internal constructor( + private val preferences: SharedPreferences, + private val launchCounts: Int, +) { + + private val deferShowing = 20 + private val firstShowing = 3 + private var fundsFoundDate: Calendar? = null + + init { + val msWhenFundsWasFound = preferences.getLong(K_FUNDS_FOUND_DATE, FUNDS_FOUND_DATE_UNDEFINED) + if (msWhenFundsWasFound != FUNDS_FOUND_DATE_UNDEFINED) { + fundsFoundDate = Calendar.getInstance().apply { timeInMillis = msWhenFundsWasFound } + } + } + + fun foundWalletWithFunds() { + if (fundsFoundDate != null) return + + fundsFoundDate = Calendar.getInstance() + preferences.edit(true) { + putLong(K_FUNDS_FOUND_DATE, fundsFoundDate!!.timeInMillis).apply() + putInt(K_SHOW_RATING_AT_LAUNCH_COUNT, launchCounts + firstShowing) + } + } + + fun isReadyToShow(): Boolean { + val fundsDate = fundsFoundDate ?: return false + + if (!userWasInteractWithRating()) { + val diff = Calendar.getInstance().timeInMillis - fundsDate.timeInMillis + val diffInDays = diff / DAY_MILLIS + return launchCounts >= getCounterOfNextShowing() && diffInDays >= firstShowing + } + + val nextShowing = getCounterOfNextShowing() + return launchCounts >= nextShowing + } + + fun applyDelayedShowing() { + updateNextShowing(launchCounts + deferShowing) + } + + fun setNeverToShow() { + updateNextShowing(Int.MAX_VALUE) + } + + private fun updateNextShowing(at: Int) { + val editor = preferences.edit() + editor.putInt(K_SHOW_RATING_AT_LAUNCH_COUNT, at) + editor.putBoolean(K_USER_WAS_INTERACT_WITH_RATING, true) + editor.apply() + } + + private fun userWasInteractWithRating(): Boolean = preferences.getBoolean(K_USER_WAS_INTERACT_WITH_RATING, false) + + private fun getCounterOfNextShowing(): Int = preferences.getInt(K_SHOW_RATING_AT_LAUNCH_COUNT, firstShowing) + + companion object { + private const val K_SHOW_RATING_AT_LAUNCH_COUNT = "showRatingDialogAtLaunchCount" + private const val K_FUNDS_FOUND_DATE = "fundsFoundDate" + private const val K_USER_WAS_INTERACT_WITH_RATING = "userWasInteractWithRating" + private const val FUNDS_FOUND_DATE_UNDEFINED = -1L + private const val DAY_MILLIS = 1000 * 60 * 60 * 24 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/PreferencesDataSource.kt similarity index 60% rename from app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt rename to data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/PreferencesDataSource.kt index 5a2fddd325..ec8b43876b 100644 --- a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt +++ b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/PreferencesDataSource.kt @@ -1,14 +1,21 @@ -package com.tangem.tap.persistence +package com.tangem.data.source.preferences -import android.app.Application import android.content.Context import android.content.SharedPreferences import androidx.core.content.edit import com.tangem.common.json.MoshiJsonConverter -import com.tangem.datasource.api.common.BigDecimalAdapter -import java.util.* +import com.tangem.data.source.preferences.adapters.BigDecimalAdapter +import com.tangem.data.source.preferences.adapters.CardBalanceStateAdapter +import com.tangem.data.source.preferences.storage.DisclaimerPrefStorage +import com.tangem.data.source.preferences.storage.FiatCurrenciesPrefStorage +import com.tangem.data.source.preferences.storage.ToppedUpWalletStorage +import com.tangem.data.source.preferences.storage.UsedCardsPrefStorage +import javax.inject.Inject -class PreferencesStorage(applicationContext: Application) { +// 🔥FIXME: Only logic to work with preferences must be here, must be separated to repositories +// TODO: Replace shared preferences with DataStore +@Deprecated("Create repository instead") +class PreferencesDataSource @Inject internal constructor(applicationContext: Context) { val appRatingLaunchObserver: AppRatingLaunchObserver val usedCardsPrefStorage: UsedCardsPrefStorage @@ -73,8 +80,6 @@ class PreferencesStorage(applicationContext: Application) { putBoolean(OPEN_WELCOME_ON_RESUME_KEY, value) } - fun getCountOfLaunches(): Int = preferences.getInt(APP_LAUNCH_COUNT_KEY, 1) - fun saveTwinsOnboardingShown() { preferences.edit { putBoolean(TWINS_ONBOARDING_SHOWN_KEY, true) } } @@ -83,6 +88,8 @@ class PreferencesStorage(applicationContext: Application) { return preferences.getBoolean(TWINS_ONBOARDING_SHOWN_KEY, false) } + private fun getCountOfLaunches(): Int = preferences.getInt(APP_LAUNCH_COUNT_KEY, 1) + private fun incrementLaunchCounter() { var count = preferences.getInt(APP_LAUNCH_COUNT_KEY, 0) preferences.edit { putInt(APP_LAUNCH_COUNT_KEY, ++count) } @@ -100,72 +107,4 @@ class PreferencesStorage(applicationContext: Application) { private const val APPLICATION_STOPPED_KEY = "applicationStopped" private const val OPEN_WELCOME_ON_RESUME_KEY = "openWelcomeOnResume" } -} - -class AppRatingLaunchObserver( - private val preferences: SharedPreferences, - private val launchCounts: Int, -) { - - private val deferShowing = 20 - private val firstShowing = 3 - private var fundsFoundDate: Calendar? = null - - init { - val msWhenFundsWasFound = preferences.getLong(K_FUNDS_FOUND_DATE, FUNDS_FOUND_DATE_UNDEFINED) - if (msWhenFundsWasFound != FUNDS_FOUND_DATE_UNDEFINED) { - fundsFoundDate = Calendar.getInstance().apply { timeInMillis = msWhenFundsWasFound } - } - } - - fun foundWalletWithFunds() { - if (fundsFoundDate != null) return - - fundsFoundDate = Calendar.getInstance() - preferences.edit(true) { - putLong(K_FUNDS_FOUND_DATE, fundsFoundDate!!.timeInMillis).apply() - putInt(K_SHOW_RATING_AT_LAUNCH_COUNT, launchCounts + firstShowing) - } - } - - @Suppress("MagicNumber") - fun isReadyToShow(): Boolean { - val fundsDate = fundsFoundDate ?: return false - - if (!userWasInteractWithRating()) { - val diff = Calendar.getInstance().timeInMillis - fundsDate.timeInMillis - val diffInDays = diff / (1000 * 60 * 60 * 24) - return launchCounts >= getCounterOfNextShowing() && diffInDays >= firstShowing - } - - val nextShowing = getCounterOfNextShowing() - return launchCounts >= nextShowing - } - - fun applyDelayedShowing() { - updateNextShowing(launchCounts + deferShowing) - } - - @Suppress("MagicNumber") - fun setNeverToShow() { - updateNextShowing(999999999) - } - - private fun updateNextShowing(at: Int) { - val editor = preferences.edit() - editor.putInt(K_SHOW_RATING_AT_LAUNCH_COUNT, at) - editor.putBoolean(K_USER_WAS_INTERACT_WITH_RATING, true) - editor.apply() - } - - private fun userWasInteractWithRating(): Boolean = preferences.getBoolean(K_USER_WAS_INTERACT_WITH_RATING, false) - - private fun getCounterOfNextShowing(): Int = preferences.getInt(K_SHOW_RATING_AT_LAUNCH_COUNT, firstShowing) - - companion object { - private const val K_SHOW_RATING_AT_LAUNCH_COUNT = "showRatingDialogAtLaunchCount" - private const val K_FUNDS_FOUND_DATE = "fundsFoundDate" - private const val K_USER_WAS_INTERACT_WITH_RATING = "userWasInteractWithRating" - private const val FUNDS_FOUND_DATE_UNDEFINED = -1L - } } \ No newline at end of file diff --git a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/adapters/BigDecimalAdapter.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/adapters/BigDecimalAdapter.kt new file mode 100644 index 0000000000..d2adc38e1d --- /dev/null +++ b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/adapters/BigDecimalAdapter.kt @@ -0,0 +1,13 @@ +package com.tangem.data.source.preferences.adapters + +import com.squareup.moshi.FromJson +import com.squareup.moshi.ToJson +import java.math.BigDecimal + +class BigDecimalAdapter { + @FromJson + fun fromJson(value: String) = BigDecimal(value) + + @ToJson + fun toJson(value: BigDecimal) = value.toString() +} \ No newline at end of file diff --git a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/adapters/CardBalanceStateAdapter.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/adapters/CardBalanceStateAdapter.kt new file mode 100644 index 0000000000..6f9ca6f810 --- /dev/null +++ b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/adapters/CardBalanceStateAdapter.kt @@ -0,0 +1,20 @@ +package com.tangem.data.source.preferences.adapters + +import com.squareup.moshi.FromJson +import com.squareup.moshi.ToJson +import com.tangem.data.source.preferences.model.DataSourceTopupInfo + +class CardBalanceStateAdapter { + + @ToJson + fun toJson(src: DataSourceTopupInfo.CardBalanceState): String = src.serializedName + + @FromJson + fun fromJson(json: String): DataSourceTopupInfo.CardBalanceState { + return when (json) { + DataSourceTopupInfo.CardBalanceState.Empty.serializedName -> DataSourceTopupInfo.CardBalanceState.Empty + DataSourceTopupInfo.CardBalanceState.Full.serializedName -> DataSourceTopupInfo.CardBalanceState.Full + else -> error("CardBalanceState not found") + } + } +} \ No newline at end of file diff --git a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/di/PreferencesStoreModule.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/di/PreferencesStoreModule.kt new file mode 100644 index 0000000000..5961efde84 --- /dev/null +++ b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/di/PreferencesStoreModule.kt @@ -0,0 +1,19 @@ +package com.tangem.data.source.preferences.di + +import android.content.Context +import com.tangem.data.source.preferences.PreferencesDataSource +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 PreferencesStoreModule { + + @Provides + @Singleton + fun providePreferencesStore(@ApplicationContext context: Context) = PreferencesDataSource(context) +} \ No newline at end of file diff --git a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceCurrency.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceCurrency.kt new file mode 100644 index 0000000000..3a2efbcd50 --- /dev/null +++ b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceCurrency.kt @@ -0,0 +1,10 @@ +package com.tangem.data.source.preferences.model + +data class DataSourceCurrency( + val id: String, + val code: String, + val name: String, + val rateBTC: String, + val unit: String, + val type: String, +) \ No newline at end of file diff --git a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceFiatCurrency.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceFiatCurrency.kt new file mode 100644 index 0000000000..9497c85988 --- /dev/null +++ b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceFiatCurrency.kt @@ -0,0 +1,7 @@ +package com.tangem.data.source.preferences.model + +data class DataSourceFiatCurrency( + val code: String, + val name: String, + val symbol: String, +) \ No newline at end of file diff --git a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceTopupInfo.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceTopupInfo.kt new file mode 100644 index 0000000000..0088d25b3e --- /dev/null +++ b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceTopupInfo.kt @@ -0,0 +1,13 @@ +package com.tangem.data.source.preferences.model + +data class DataSourceTopupInfo( + val walletId: String, + val cardBalanceState: CardBalanceState, +) { + enum class CardBalanceState(val serializedName: String) { + Empty(serializedName = "Empty"), + Full(serializedName = "Full"), + CustomToken(serializedName = "Custom token"), + BlockchainError(serializedName = "Blockchain error"), + } +} \ No newline at end of file diff --git a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceUsedCardInfo.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceUsedCardInfo.kt new file mode 100644 index 0000000000..d05eadc7ca --- /dev/null +++ b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceUsedCardInfo.kt @@ -0,0 +1,8 @@ +package com.tangem.data.source.preferences.model + +internal data class DataSourceUsedCardInfo( + val cardId: String, + val isScanned: Boolean = false, + val isActivationStarted: Boolean = false, + val isActivationFinished: Boolean = false, +) \ No newline at end of file diff --git a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceUsedCardInfoOld.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceUsedCardInfoOld.kt new file mode 100644 index 0000000000..ee01e6752a --- /dev/null +++ b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceUsedCardInfoOld.kt @@ -0,0 +1,7 @@ +package com.tangem.data.source.preferences.model + +internal data class DataSourceUsedCardInfoOld( + val cardId: String, + val isScanned: Boolean = false, + val isActivationStarted: Boolean = false, +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/persistence/DisclaimerPrefStorage.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/DisclaimerPrefStorage.kt similarity index 73% rename from app/src/main/java/com/tangem/tap/persistence/DisclaimerPrefStorage.kt rename to data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/DisclaimerPrefStorage.kt index 34dbf530a3..e91fb9e27c 100644 --- a/app/src/main/java/com/tangem/tap/persistence/DisclaimerPrefStorage.kt +++ b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/DisclaimerPrefStorage.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.persistence +package com.tangem.data.source.preferences.storage import android.content.SharedPreferences import androidx.core.content.edit @@ -6,7 +6,8 @@ import androidx.core.content.edit /** [REDACTED_AUTHOR] */ -class DisclaimerPrefStorage( +@Deprecated("Create repository instead") +class DisclaimerPrefStorage internal constructor( private val preferences: SharedPreferences, ) { diff --git a/app/src/main/java/com/tangem/tap/persistence/FiatCurrenciesPrefStorage.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/FiatCurrenciesPrefStorage.kt similarity index 65% rename from app/src/main/java/com/tangem/tap/persistence/FiatCurrenciesPrefStorage.kt rename to data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/FiatCurrenciesPrefStorage.kt index a52f38f854..e30fdbbe48 100644 --- a/app/src/main/java/com/tangem/tap/persistence/FiatCurrenciesPrefStorage.kt +++ b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/FiatCurrenciesPrefStorage.kt @@ -1,15 +1,16 @@ -package com.tangem.tap.persistence +package com.tangem.data.source.preferences.storage import android.content.SharedPreferences import androidx.core.content.edit import com.tangem.common.json.MoshiJsonConverter -import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse -import com.tangem.tap.common.entities.FiatCurrency +import com.tangem.data.source.preferences.model.DataSourceCurrency +import com.tangem.data.source.preferences.model.DataSourceFiatCurrency /** [REDACTED_AUTHOR] */ -class FiatCurrenciesPrefStorage( +@Deprecated("Create repository instead") +class FiatCurrenciesPrefStorage internal constructor( private val preferences: SharedPreferences, private val converter: MoshiJsonConverter, ) { @@ -20,26 +21,26 @@ class FiatCurrenciesPrefStorage( } } - fun getAppCurrency(): FiatCurrency { + fun getAppCurrency(): DataSourceFiatCurrency? { val json = preferences.getString(APP_CURRENCY_KEY, "") - if (json.isNullOrBlank()) return FiatCurrency.Default + if (json.isNullOrBlank()) return null - return converter.fromJson(json) ?: FiatCurrency.Default + return converter.fromJson(json) } - fun saveAppCurrency(fiatCurrency: FiatCurrency) { + fun saveAppCurrency(fiatCurrency: DataSourceFiatCurrency) { val json = converter.toJson(fiatCurrency) preferences.edit { putString(APP_CURRENCY_KEY, json) } } - fun save(currencies: List) { + fun save(currencies: List) { val json: String = converter.toJson(currencies) return preferences.edit().putString(FIAT_CURRENCIES_KEY, json).apply() } - fun restore(): List { + fun restore(): List { val json = preferences.getString(FIAT_CURRENCIES_KEY, "") - val type = converter.typedList(CurrenciesResponse.Currency::class.java) + val type = converter.typedList(DataSourceCurrency::class.java) if (json.isNullOrBlank()) return emptyList() return converter.fromJson(json, type) ?: emptyList() diff --git a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/Migration.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/Migration.kt new file mode 100644 index 0000000000..e1cc25e4ba --- /dev/null +++ b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/Migration.kt @@ -0,0 +1,5 @@ +package com.tangem.data.source.preferences.storage + +internal interface Migration { + fun migrate() +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/persistence/ToppedUpWalletStorage.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/ToppedUpWalletStorage.kt similarity index 56% rename from app/src/main/java/com/tangem/tap/persistence/ToppedUpWalletStorage.kt rename to data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/ToppedUpWalletStorage.kt index 892b1a2370..14b1e77b7f 100644 --- a/app/src/main/java/com/tangem/tap/persistence/ToppedUpWalletStorage.kt +++ b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/ToppedUpWalletStorage.kt @@ -1,51 +1,50 @@ -package com.tangem.tap.persistence +package com.tangem.data.source.preferences.storage import android.content.SharedPreferences import androidx.core.content.edit import com.tangem.common.json.MoshiJsonConverter -import com.tangem.tap.common.analytics.events.AnalyticsParam -import timber.log.Timber +import com.tangem.data.source.preferences.model.DataSourceTopupInfo /** [REDACTED_AUTHOR] */ -class ToppedUpWalletStorage( +@Deprecated("Create repository instead") +class ToppedUpWalletStorage internal constructor( private val preferences: SharedPreferences, private val jsonConverter: MoshiJsonConverter, ) { - private val walletList: MutableSet = mutableSetOf() + private val walletList: MutableSet = mutableSetOf() init { walletList.addAll(restore()) } - fun save(userWalletInfo: TopupInfo): Boolean { + fun save(userWalletInfo: DataSourceTopupInfo): Boolean { walletList.removeAll { it.walletId == userWalletInfo.walletId } walletList.add(userWalletInfo) return save(walletList) } - fun restore(walletId: String): TopupInfo? { + fun restore(walletId: String): DataSourceTopupInfo? { return walletList.firstOrNull { it.walletId == walletId } } - private fun save(userWallets: MutableSet): Boolean { + private fun save(userWallets: MutableSet): Boolean { return try { val json = jsonConverter.toJson(userWallets) preferences.edit(true) { putString(KEY, json) } true } catch (ex: Exception) { - Timber.e(ex) false } } - private fun restore(): MutableSet { + private fun restore(): MutableSet { val json = preferences.getString(KEY, null) ?: return mutableSetOf() return try { - val typedList = jsonConverter.typedList(TopupInfo::class.java) - val listData = jsonConverter.fromJson>(json, typedList)!! + val typedList = jsonConverter.typedList(DataSourceTopupInfo::class.java) + val listData = jsonConverter.fromJson>(json, typedList)!! listData.toMutableSet() } catch (ex: Exception) { preferences.edit(true) { remove(KEY) } @@ -53,13 +52,6 @@ class ToppedUpWalletStorage( } } - data class TopupInfo( - val walletId: String, - val cardBalanceState: AnalyticsParam.CardBalanceState, - ) { - val isToppedUp: Boolean = cardBalanceState == AnalyticsParam.CardBalanceState.Full - } - companion object { private const val KEY = "userWalletsInfo" } diff --git a/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/UsedCardsPrefStorage.kt similarity index 67% rename from app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt rename to data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/UsedCardsPrefStorage.kt index f814eb7e16..b1fbf61361 100644 --- a/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt +++ b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/UsedCardsPrefStorage.kt @@ -1,15 +1,16 @@ -package com.tangem.tap.persistence +package com.tangem.data.source.preferences.storage import android.content.SharedPreferences import androidx.core.content.edit import com.tangem.common.json.MoshiJsonConverter -import com.tangem.tap.common.extensions.replaceByOrAdd -import timber.log.Timber +import com.tangem.data.source.preferences.model.DataSourceUsedCardInfo +import com.tangem.data.source.preferences.model.DataSourceUsedCardInfoOld /** [REDACTED_AUTHOR] */ -class UsedCardsPrefStorage( +@Deprecated("Create repository instead") +class UsedCardsPrefStorage internal constructor( private val preferences: SharedPreferences, private val jsonConverter: MoshiJsonConverter, ) { @@ -26,7 +27,7 @@ class UsedCardsPrefStorage( fun scanned(cardId: String) { val restoredList = restore() val foundItem = findCardInfo(cardId, restoredList)?.copy(isScanned = true) - ?: UsedCardInfo(cardId, true) + ?: DataSourceUsedCardInfo(cardId, true) save(foundItem, restoredList) } @@ -38,14 +39,14 @@ class UsedCardsPrefStorage( fun activationStarted(cardId: String) { val restoredList = restore() val foundItem = findCardInfo(cardId, restoredList)?.copy(isActivationStarted = true) - ?: UsedCardInfo(cardId, isActivationStarted = true) + ?: DataSourceUsedCardInfo(cardId, isActivationStarted = true) save(foundItem, restoredList) } fun activationFinished(cardId: String) { val restoredList = restore() - var foundItem = findCardInfo(cardId, restoredList) ?: UsedCardInfo(cardId) + var foundItem = findCardInfo(cardId, restoredList) ?: DataSourceUsedCardInfo(cardId) foundItem = foundItem.copy( isActivationStarted = true, isActivationFinished = true, @@ -67,33 +68,38 @@ class UsedCardsPrefStorage( return cardInfo.isActivationStarted && !cardInfo.isActivationFinished } - private fun findCardInfo(cardId: String, list: MutableList? = null): UsedCardInfo? { + private fun findCardInfo( + cardId: String, + list: MutableList? = null, + ): DataSourceUsedCardInfo? { val findInList = list ?: restore() return findInList.firstOrNull { it.cardId == cardId } } - private fun save(usedCardInfo: UsedCardInfo?, usedCardsInfo: MutableList) { + private fun save(usedCardInfo: DataSourceUsedCardInfo?, usedCardsInfo: MutableList) { val info = usedCardInfo ?: return - usedCardsInfo.replaceByOrAdd(info) { it.cardId == info.cardId } + with(usedCardsInfo) { + val index = indexOfFirst { it.cardId == info.cardId } + if (index == -1) { + add(info) + } else { + set(index, info) + } + } + save(usedCardsInfo) } - private fun save(list: MutableList): Boolean { - return try { - val json = jsonConverter.toJson(list) - preferences.edit { putString(USED_CARDS_INFO_V2, json) } - true - } catch (ex: Exception) { - Timber.e(ex) - false - } + private fun save(list: MutableList) { + val json = jsonConverter.toJson(list) + preferences.edit { putString(USED_CARDS_INFO_V2, json) } } - private fun restore(): MutableList { + private fun restore(): MutableList { val json = preferences.getString(USED_CARDS_INFO_V2, null) ?: return mutableListOf() return try { - jsonConverter.fromJson(json, jsonConverter.typedList(UsedCardInfo::class.java))!! + jsonConverter.fromJson(json, jsonConverter.typedList(DataSourceUsedCardInfo::class.java))!! } catch (ex: Exception) { preferences.edit(true) { remove(USED_CARDS_INFO_V2) } mutableListOf() @@ -113,7 +119,7 @@ class UsedCardsPrefStorage( if (restoredCardsInfo.isEmpty()) return val newCardsInfo = restoredCardsInfo.map { cardInfo -> - UsedCardInfo( + DataSourceUsedCardInfo( cardId = cardInfo.cardId, isScanned = cardInfo.isScanned, isActivationStarted = true, @@ -123,10 +129,13 @@ class UsedCardsPrefStorage( storage.save(newCardsInfo) } - private fun restore(): MutableList { + private fun restore(): MutableList { val json = storage.preferences.getString(USED_CARDS_INFO, null) ?: return mutableListOf() return try { - storage.jsonConverter.fromJson(json, storage.jsonConverter.typedList(UsedCardInfoOld::class.java))!! + storage.jsonConverter.fromJson( + json, + storage.jsonConverter.typedList(DataSourceUsedCardInfoOld::class.java), + )!! } catch (ex: Exception) { mutableListOf() } finally { @@ -134,21 +143,4 @@ class UsedCardsPrefStorage( } } } - - private data class UsedCardInfo( - val cardId: String, - val isScanned: Boolean = false, - val isActivationStarted: Boolean = false, - val isActivationFinished: Boolean = false, - ) - - private data class UsedCardInfoOld( - val cardId: String, - val isScanned: Boolean = false, - val isActivationStarted: Boolean = false, - ) -} - -private interface Migration { - fun migrate() } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 2c4e5ecbcc..3928077a46 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -69,4 +69,8 @@ include(":domain:legacy") include(":domain:core") include(":domain:card") -// endregion Domain modules \ No newline at end of file +// endregion Domain modules + +// region Data modules +include(":data:source:preferences") +// endregion Data modules \ No newline at end of file From 40209bc1c305ba9e7ab55be584e473ad9a97f0bb Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 4 May 2023 15:24:24 +0300 Subject: [PATCH 06/14] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt index 72645564d0..c673ac8577 100644 --- a/app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt +++ b/app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt @@ -107,8 +107,8 @@ class DerivationManagerImpl( val selectedUserWallet = appStateHolder.userWalletsListManager?.selectedUserWalletSync val result = appStateHolder.tangemSdkManager?.derivePublicKeys( - scanResponse.card.cardId, - derivations, + cardId = null, // always ignore cardId in derive task + derivations = derivations, ) when (result) { is CompletionResult.Success -> { From 2e797e8027f3d82483ab79d66ca81e7ff38f197b Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 4 May 2023 21:00:14 +0800 Subject: [PATCH 07/14] Updated on 2026-08-14 --- .idea/inspectionProfiles/Project_Default.xml | 1 + .../data/converters/FoundTokenConverter.kt | 1 + .../impl/domain/models/FoundToken.kt | 17 +- .../presentation/AddCustomTokenFragment.kt | 3 + .../models/AddCustomTokenViewsModels.kt | 156 +-- .../presentation/models/CustomTokenType.kt | 4 + .../presentation/routers/CustomTokenRouter.kt | 3 + .../routers/DefaultCustomTokenRouter.kt | 5 + .../states/AddCustomTokenStateHolder.kt | 10 +- .../presentation/ui/AddCustomTokenContent.kt | 92 +- .../ui/AddCustomTokenPreviewData.kt | 124 +++ .../presentation/ui/AddCustomTokenScreen.kt | 24 +- .../ui/AddCustomTokenTestContent.kt | 98 +- .../AddCustomTokenFloatingButton.kt | 28 +- .../ui/components/AddCustomTokenForm.kt | 109 ++- ...enWarning.kt => AddCustomTokenWarnings.kt} | 42 +- .../AddCustomTokenAnalyticsSender.kt | 35 + .../viewmodels/AddCustomTokenViewModel.kt | 902 ++++++++++-------- .../legacy/compose/AddCustomTokenScreen.kt | 10 +- .../tokens/impl/di/TokensListRouterModule.kt | 5 +- .../router/DefaultTokensListRouter.kt | 13 +- .../tokens/legacy/redux/TokensMiddleware.kt | 31 +- .../java/com/tangem/domain/DomainWrapped.kt | 8 +- .../addCustomToken/redux/AddCustomTokenHub.kt | 55 +- 24 files changed, 1041 insertions(+), 735 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/models/CustomTokenType.kt create mode 100644 app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/ui/AddCustomTokenPreviewData.kt rename app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/ui/components/{AddCustomTokenWarning.kt => AddCustomTokenWarnings.kt} (53%) create mode 100644 app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenAnalyticsSender.kt diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 5294908fc5..6edc1d9d21 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -17,6 +17,7 @@