From 023ed0924bd201cf58405b058433769b533981d9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 6 Feb 2024 17:58:35 +0300 Subject: [PATCH 01/17] Updated on 2026-08-14 --- .../tap/network/exchangeServices/mercuryo/MercuryoService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt index a19ca0cd84..d0f3c8269e 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt @@ -36,7 +36,7 @@ internal class MercuryoService(private val environment: MercuryoEnvironment) : E val availableCurrency = availableMercuryoCurrencies.firstOrNull { it.currencySymbol == currency.currencySymbol && it.network == mercuryoNetwork && - it.contractAddress == contractAddress + it.contractAddress.equals(contractAddress, ignoreCase = true) } return availableCurrency != null } From d4a315b003ca11c381f6f800a2e0aceccac9fe3b Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 6 Feb 2024 18:54:01 +0300 Subject: [PATCH 02/17] Updated on 2026-08-14 --- .../tangem/tap/common/feedback/AdditionalFeedbackInfo.kt | 8 +++++--- gradle/dependencies.toml | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt b/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt index b588d0d641..975fb1fb2f 100644 --- a/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt +++ b/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt @@ -72,9 +72,11 @@ class AdditionalFeedbackInfo { cardIssuer = data.card.issuer.name signedHashesCount = formatSignedHashes(data.card.wallets) userWalletId = UserWalletIdBuilder.scanResponse(data).build()?.stringValue ?: "" - extendedPublicKey = data.card.wallets.firstOrNull { it.extendedPublicKey != null } - ?.extendedPublicKey - ?.serialize(networkType = NetworkType.Mainnet).orEmpty() + extendedPublicKey = runCatching { + data.card.wallets.firstOrNull { it.extendedPublicKey != null } + ?.extendedPublicKey + ?.serialize(networkType = NetworkType.Mainnet).orEmpty() + }.getOrDefault("couldn't retrieve extended public key") } @Deprecated("Don't use it directly") diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 826d5cf01f..6d347dd3d4 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -85,7 +85,7 @@ spr-client = "3.6.2" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.6-472" +tangemBlockchainSdk = "release-app_5.6-480" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.6-325" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 1e0876eea6a965a6113e852ca84e8b77b756a6cc Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 8 Feb 2024 11:17:26 +0300 Subject: [PATCH 03/17] Updated on 2026-08-14 --- app/build.gradle.kts | 1 + .../tap/di/domain/TokensDomainModule.kt | 2 + .../datasource/api/promotion/PromotionApi.kt | 29 ------------ .../models/AbstractPromotionResponse.kt | 18 ------- .../api/promotion/models/AwardRequestBody.kt | 12 ----- .../api/promotion/models/AwardResponse.kt | 11 ----- .../promotion/models/CodeAwardRequestBody.kt | 12 ----- .../api/promotion/models/CodeAwardResponse.kt | 11 ----- .../models/CodeValidateRequestBody.kt | 11 ----- .../promotion/models/CodeValidateResponse.kt | 11 ----- .../promotion/models/PromotionInfoResponse.kt | 43 ++++++----------- .../promotion/models/ValidateRequestBody.kt | 11 ----- .../api/promotion/models/ValidateResponse.kt | 11 ----- .../api/tangemTech/TangemTechApi.kt | 4 ++ .../{PromotionOneInch.kt => DevTangemApi.kt} | 2 +- .../com/tangem/datasource/di/NetworkModule.kt | 39 ++++++---------- .../local/preferences/PreferencesKeys.kt | 8 +++- core/res/src/main/res/values-ru/strings.xml | 8 ++-- core/res/src/main/res/values/strings.xml | 8 ++-- .../NotificationWithBackground.kt | 19 +++++--- ...mg_swap_promo_blue_banner_background.webp} | Bin ...mg_swap_promo_green_banner_background.webp | Bin 0 -> 12118 bytes data/promo/.gitignore | 1 + data/promo/build.gradle.kts | 25 ++++++++++ .../data/promo/DefaultPromoRepository.kt | 29 ++++++++++++ .../converters/PromoResponseConverter.kt | 23 +++++++++ .../tangem/data/promo/di/PromoDataModule.kt | 26 +++++++++++ .../settings/DefaultSwapPromoRepository.kt | 44 ++++-------------- .../ShouldShowSwapPromoTokenUseCase.kt | 4 +- .../ShouldShowSwapPromoWalletUseCase.kt | 4 +- .../repositories/SwapPromoRepository.kt | 8 ++-- domain/tokens/models/build.gradle.kts | 1 + .../com/tangem/domain/promo/PromoBanner.kt | 25 ++++++++++ .../model/warnings/CryptoCurrencyWarning.kt | 6 ++- .../tokens/GetCurrencyWarningsUseCase.kt | 15 +++--- .../tokens/repository/PromoRepository.kt | 8 ++++ .../components/TokenDetailsNotification.kt | 15 ++++-- .../TokenDetailsNotificationConverter.kt | 2 + .../domain/GetMultiWalletWarningsFactory.kt | 31 +++++++++++- .../state/components/WalletNotification.kt | 15 ++++-- .../wallet/viewmodels/WalletClickIntents.kt | 2 - .../WalletNotificationsListFactory.kt | 19 +------- .../wallet/viewmodels/WalletViewModel.kt | 8 ---- .../intents/WalletWarningsClickIntents.kt | 10 ++++ settings.gradle.kts | 1 + 45 files changed, 301 insertions(+), 292 deletions(-) delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/PromotionApi.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AbstractPromotionResponse.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardRequestBody.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardResponse.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardRequestBody.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardResponse.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateRequestBody.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateResponse.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateRequestBody.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateResponse.kt rename core/datasource/src/main/java/com/tangem/datasource/di/{PromotionOneInch.kt => DevTangemApi.kt} (80%) rename core/ui/src/main/res/drawable/{img_swap_promo_banner_background.webp => img_swap_promo_blue_banner_background.webp} (100%) create mode 100644 core/ui/src/main/res/drawable/img_swap_promo_green_banner_background.webp create mode 100644 data/promo/.gitignore create mode 100644 data/promo/build.gradle.kts create mode 100644 data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt create mode 100644 data/promo/src/main/java/com/tangem/data/promo/converters/PromoResponseConverter.kt create mode 100644 data/promo/src/main/java/com/tangem/data/promo/di/PromoDataModule.kt create mode 100644 domain/tokens/models/src/main/java/com/tangem/domain/promo/PromoBanner.kt create mode 100644 domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/PromoRepository.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8c5969e599..aa64c0d699 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -80,6 +80,7 @@ dependencies { implementation(projects.data.analytics) implementation(projects.data.transaction) implementation(projects.data.visa) + implementation(projects.data.promo) /** Features */ implementation(projects.features.onboarding) diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index b439d53019..7149fc13da 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -100,6 +100,7 @@ internal object TokensDomainModule { marketCryptoCurrencyRepository: MarketCryptoCurrencyRepository, swapRepository: SwapRepository, showSwapPromoTokenUseCase: ShouldShowSwapPromoTokenUseCase, + promoRepository: PromoRepository, dispatchers: CoroutineDispatcherProvider, ): GetCurrencyWarningsUseCase { return GetCurrencyWarningsUseCase( @@ -110,6 +111,7 @@ internal object TokensDomainModule { marketCryptoCurrencyRepository = marketCryptoCurrencyRepository, swapRepository = swapRepository, showSwapPromoTokenUseCase = showSwapPromoTokenUseCase, + promoRepository = promoRepository, dispatchers = dispatchers, ) } diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/PromotionApi.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/PromotionApi.kt deleted file mode 100644 index d71061c21a..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/PromotionApi.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.tangem.datasource.api.promotion - -import com.tangem.datasource.api.promotion.models.* -import retrofit2.http.* - -/** - * - * Promotion API - * @see Documentation -[REDACTED_AUTHOR] - */ -interface PromotionApi { - - @Headers("Cache-Control: max-age=3600") - @GET("promotion") - suspend fun getPromotionInfo(@Query("programName") name: String): PromotionInfoResponse - - @POST("promotion/code/validate") - suspend fun validateCode(@Body request: CodeValidateRequestBody): CodeValidateResponse - - @POST("promotion/code/award") - suspend fun requestAwardByCode(@Body request: CodeAwardRequestBody): CodeAwardResponse - - @POST("promotion/validate") - suspend fun validate(@Body request: ValidateRequestBody): ValidateResponse - - @POST("promotion/award") - suspend fun requestAward(@Body request: AwardRequestBody): AwardResponse -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AbstractPromotionResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AbstractPromotionResponse.kt deleted file mode 100644 index edfc8e990d..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AbstractPromotionResponse.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.tangem.datasource.api.promotion.models - -import com.squareup.moshi.Json - -/** -[REDACTED_AUTHOR] - */ -abstract class AbstractPromotionResponse { - - abstract val error: Error? - - fun isError(): Boolean = error != null - - data class Error( - @Json(name = "code") val code: Int, - @Json(name = "message") val message: String, - ) -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardRequestBody.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardRequestBody.kt deleted file mode 100644 index c8ac7faed1..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardRequestBody.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.tangem.datasource.api.promotion.models - -import com.squareup.moshi.Json - -/** -[REDACTED_AUTHOR] - */ -data class AwardRequestBody( - @Json(name = "walletId") val walletId: String, - @Json(name = "address") val address: String, - @Json(name = "programName") val programName: String, -) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardResponse.kt deleted file mode 100644 index c43c59fa45..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardResponse.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.tangem.datasource.api.promotion.models - -import com.squareup.moshi.Json - -/** -[REDACTED_AUTHOR] - */ -data class AwardResponse( - @Json(name = "status") val status: Boolean?, - @Json(name = "error") override val error: Error? = null, -) : AbstractPromotionResponse() \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardRequestBody.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardRequestBody.kt deleted file mode 100644 index 51e8626d4b..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardRequestBody.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.tangem.datasource.api.promotion.models - -import com.squareup.moshi.Json - -/** -[REDACTED_AUTHOR] - */ -data class CodeAwardRequestBody( - @Json(name = "walletId") val walletId: String, - @Json(name = "address") val address: String, - @Json(name = "code") val code: String?, -) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardResponse.kt deleted file mode 100644 index 33d9313c42..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardResponse.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.tangem.datasource.api.promotion.models - -import com.squareup.moshi.Json - -/** -[REDACTED_AUTHOR] - */ -data class CodeAwardResponse( - @Json(name = "status") val status: Boolean?, - @Json(name = "error") override val error: Error? = null, -) : AbstractPromotionResponse() \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateRequestBody.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateRequestBody.kt deleted file mode 100644 index 769ecdad26..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateRequestBody.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.tangem.datasource.api.promotion.models - -import com.squareup.moshi.Json - -/** -[REDACTED_AUTHOR] - */ -data class CodeValidateRequestBody( - @Json(name = "walletId") val walletId: String, - @Json(name = "code") val code: String?, -) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateResponse.kt deleted file mode 100644 index e6e87e0ff3..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateResponse.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.tangem.datasource.api.promotion.models - -import com.squareup.moshi.Json - -/** -[REDACTED_AUTHOR] - */ -data class CodeValidateResponse( - @Json(name = "valid") val valid: Boolean?, - @Json(name = "error") override val error: Error? = null, -) : AbstractPromotionResponse() \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/PromotionInfoResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/PromotionInfoResponse.kt index ca5406aba5..fa9140eea1 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/PromotionInfoResponse.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/PromotionInfoResponse.kt @@ -1,40 +1,23 @@ package com.tangem.datasource.api.promotion.models import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass -/** -[REDACTED_AUTHOR] - */ +@JsonClass(generateAdapter = true) data class PromotionInfoResponse( - @Json(name = "newCard") val newCard: Data?, - @Json(name = "oldCard") val oldCard: Data?, - @Json(name = "awardPaymentToken") val awardPaymentToken: TokenData?, - @Json(name = "error") override val error: Error? = null, -) : AbstractPromotionResponse() { + @Json(name = "name") val name: String, + @Json(name = "all") val bannerState: BannerState?, +) { - data class Data( - @Json(name = "status") val status: Status, - @Json(name = "award") val award: Double, + @JsonClass(generateAdapter = true) + data class BannerState( + @Json(name = "timeline") val timeline: Timeline, + @Json(name = "status") val status: String, ) - enum class Status(val value: String) { - @Json(name = "pending") - PENDING("pending"), - - @Json(name = "active") - ACTIVE("active"), - - @Json(name = "finished") - FINISHED("finished"), - } - - data class TokenData( - @Json(name = "id") val id: String, - @Json(name = "name") val name: String, - @Json(name = "symbol") val symbol: String, - @Json(name = "active") val active: Boolean, - @Json(name = "networkId") val networkId: String, - @Json(name = "contractAddress") val contractAddress: String, - @Json(name = "decimalCount") val decimalCount: Int, + @JsonClass(generateAdapter = true) + data class Timeline( + @Json(name = "start") val start: String, + @Json(name = "end") val end: String, ) } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateRequestBody.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateRequestBody.kt deleted file mode 100644 index 090f0e0d29..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateRequestBody.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.tangem.datasource.api.promotion.models - -import com.squareup.moshi.Json - -/** -[REDACTED_AUTHOR] - */ -data class ValidateRequestBody( - @Json(name = "walletId") val walletId: String, - @Json(name = "programName") val programName: String, -) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateResponse.kt deleted file mode 100644 index 97cd74645d..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateResponse.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.tangem.datasource.api.promotion.models - -import com.squareup.moshi.Json - -/** -[REDACTED_AUTHOR] - */ -data class ValidateResponse( - @Json(name = "valid") val valid: Boolean?, - @Json(name = "error") override val error: Error? = null, -) : AbstractPromotionResponse() \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt index 9992d45c5f..43ff3af6e5 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt @@ -1,6 +1,7 @@ package com.tangem.datasource.api.tangemTech import com.tangem.datasource.api.common.response.ApiResponse +import com.tangem.datasource.api.promotion.models.PromotionInfoResponse import com.tangem.datasource.api.tangemTech.models.* import retrofit2.http.* @@ -71,4 +72,7 @@ interface TangemTechApi { @Query("coinIds") coinIds: String, @Query("fields") fields: String = "price,priceChange24h,lastUpdatedAt", ): ApiResponse + + @GET("promotion") + suspend fun getPromotionInfo(@Query("programName") name: String): ApiResponse } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/PromotionOneInch.kt b/core/datasource/src/main/java/com/tangem/datasource/di/DevTangemApi.kt similarity index 80% rename from core/datasource/src/main/java/com/tangem/datasource/di/PromotionOneInch.kt rename to core/datasource/src/main/java/com/tangem/datasource/di/DevTangemApi.kt index 9e2d6b3981..4bfacf89db 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/PromotionOneInch.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/DevTangemApi.kt @@ -5,4 +5,4 @@ import javax.inject.Qualifier @Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) -annotation class PromotionOneInch \ No newline at end of file +annotation class DevTangemApi \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt index 754936c7a5..eb61db86b2 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt @@ -5,12 +5,10 @@ import com.squareup.moshi.Moshi import com.tangem.datasource.BuildConfig import com.tangem.datasource.api.common.response.ApiResponseCallAdapterFactory import com.tangem.datasource.api.express.TangemExpressApi -import com.tangem.datasource.api.promotion.PromotionApi import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.utils.RequestHeader.* import com.tangem.datasource.utils.addHeaders import com.tangem.datasource.utils.addLoggers -import com.tangem.lib.auth.AuthProvider import com.tangem.lib.auth.ExpressAuthProvider import dagger.Module import dagger.Provides @@ -20,7 +18,6 @@ import dagger.hilt.components.SingletonComponent import okhttp3.OkHttpClient import retrofit2.Retrofit import retrofit2.converter.moshi.MoshiConverterFactory -import java.util.concurrent.TimeUnit import javax.inject.Singleton @Module @@ -75,31 +72,25 @@ class NetworkModule { } @Provides + @DevTangemApi @Singleton - @PromotionOneInch - fun providePromotionOneInchApi( - authProvider: AuthProvider, - @NetworkMoshi moshi: Moshi, - @ApplicationContext context: Context, - ): PromotionApi { - val okClient = OkHttpClient.Builder() - .addHeaders(AuthenticationHeader(authProvider)) - .addLoggers(context) - .callTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS) - .connectTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS) - .readTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS) - .writeTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS) - .build() - return createBasePromotionRetrofit(okClient, moshi) - } - - private fun createBasePromotionRetrofit(okHttpClient: OkHttpClient, moshi: Moshi): PromotionApi { + fun provideTangemTechDevApi(@NetworkMoshi moshi: Moshi, @ApplicationContext context: Context): TangemTechApi { return Retrofit.Builder() .addConverterFactory(MoshiConverterFactory.create(moshi)) - .baseUrl(PROD_TANGEM_TECH_BASE_URL) - .client(okHttpClient) + .addCallAdapterFactory(ApiResponseCallAdapterFactory.create()) + .baseUrl(DEV_TANGEM_TECH_BASE_URL) + .client( + OkHttpClient.Builder() + .addHeaders( + CacheControlHeader, + // TODO("refactor header init") get auth data after biometric auth to avoid race condition + // AuthenticationHeader(authProvider), + ) + .addLoggers(context) + .build(), + ) .build() - .create(PromotionApi::class.java) + .create(TangemTechApi::class.java) } private companion object { diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt index 02432a3d03..652187147e 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt @@ -45,9 +45,13 @@ object PreferencesKeys { val LAST_SWAPPED_CRYPTOCURRENCY_ID_KEY by lazy { stringPreferencesKey(name = "lastSwappedCryptoCurrency") } - val IS_WALLET_SWAP_PROMO_SHOW_KEY by lazy { booleanPreferencesKey(name = "isWalletSwapPromoShown") } + val IS_WALLET_SWAP_PROMO_CHANGELLY_SHOW_KEY by lazy { + booleanPreferencesKey(name = "isWalletSwapPromoChangellyShown") + } - val IS_TOKEN_SWAP_PROMO_SHOW_KEY by lazy { booleanPreferencesKey(name = "isTokenSwapPromoShown") } + val IS_TOKEN_SWAP_PROMO_CHANGELLY_SHOW_KEY by lazy { + booleanPreferencesKey(name = "isTokenSwapPromoChangellyShown") + } } /** Preferences keys set that should be migrated from "PreferencesDataSource" to a new DataStore */ diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index e257132463..0276e3c034 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -273,8 +273,8 @@ 1INCH токены будут зачислены на адрес вашего кошелька в сети %s в течение 48 часов Чтобы получить доступ ко всем сетям, вам необходимо отсканировать карту Отсканируйте карту - Обменивайте свои цифровые активы между различными сетями - Кроссчейн-своп теперь доступен + Обменивайте свои токены с %1$s комиссии провайдера через Changelly с %2$s по %3$s февраля. + Обмен с Changelly, %s комиссии Токены Добавить Изменить @@ -575,9 +575,9 @@ Токен %1$s является основной валютой в сети %2$s и не может быть скрыт до тех пор, пока у вас в списке есть другие токены этой сети. Невозможно скрыть %s Нет цены + Обменивайте этот токен на другие с %1$s комиссии за обслуживание с %2$s по %3$s февраля. + Обмен с Changelly, %s комиссии Обменять - Обменяйте этот токен на другие активы в вашем портфеле - Представляем кроссчейн-своп контракт: %s У вас еще нет транзакций Не удалось загрузить историю транзакций.\nНажмите на кнопку перезагрузки, чтобы обновить информацию. diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index dd42088e6b..43914b654e 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -273,8 +273,8 @@ 1INCH tokens will be credited to your %s wallet address within 48 hours To access all the networks you need to scan the card Scan your card - Swap multiple currencies across several blockchains - Cross-chain swaps are now available + Enjoy %1$s service fees on swaps via Changelly from February %2$s-%3$s + Swap with Changelly, %s fees Tokens Add Edit @@ -587,8 +587,8 @@ The %1$s token is the main currency on the %2$s network and cannot be hidden as long as you have other tokens on this network in the list. Unable to hide %s No rate - Exchange this token for other assets in your portfolio - Introducing cross-chain swaps + Exchange this token for another at %1$s service fees from February %2$s-%3$s. + Swap with Changelly, %s fees Swap now contract: %s You don\'t have any transactions yet diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/NotificationWithBackground.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/NotificationWithBackground.kt index 691cded7ba..e4e8a06833 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/NotificationWithBackground.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/NotificationWithBackground.kt @@ -28,6 +28,7 @@ import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.res.LocalIsInDarkTheme import com.tangem.core.ui.res.TangemColorPalette.Dark6 import com.tangem.core.ui.res.TangemColorPalette.Light4 @@ -55,7 +56,7 @@ fun NotificationWithBackground(config: NotificationConfig, modifier: Modifier = val spacing14 = TangemTheme.dimens.spacing14 Image( - painter = painterResource(config.backgroundResId ?: R.drawable.img_swap_promo_banner_background), + painter = painterResource(config.backgroundResId ?: R.drawable.img_swap_promo_blue_banner_background), contentDescription = null, contentScale = ContentScale.Crop, modifier = Modifier.constrainAs(backgroundRef) { @@ -171,13 +172,19 @@ private class NotificationWithBackgroundPreviewProvider : PreviewParameterProvid override val values: Sequence get() = sequenceOf( NotificationConfig( - title = resourceReference(id = R.string.main_swap_promotion_title), - subtitle = resourceReference(id = R.string.main_swap_promotion_message), + title = resourceReference(id = R.string.main_swap_changelly_promotion_title), + subtitle = resourceReference( + id = R.string.main_swap_changelly_promotion_message, + formatArgs = wrappedList("1", "2"), + ), iconResId = R.drawable.img_swap_promo, - backgroundResId = R.drawable.img_swap_promo_banner_background, + backgroundResId = R.drawable.img_swap_promo_blue_banner_background, ), NotificationConfig( - title = resourceReference(id = R.string.token_swap_promotion_title), + title = resourceReference( + id = R.string.token_swap_changelly_promotion_title, + formatArgs = wrappedList("1", "2"), + ), subtitle = stringReference( "Swap multiple currencies between any chains you wish. Swap multiple " + "currencies between any chains you wish. Swap multiple " + @@ -185,7 +192,7 @@ private class NotificationWithBackgroundPreviewProvider : PreviewParameterProvid "till Dec 31.", ), iconResId = R.drawable.img_swap_promo, - backgroundResId = R.drawable.img_swap_promo_banner_background, + backgroundResId = R.drawable.img_swap_promo_blue_banner_background, buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig( text = resourceReference(id = R.string.token_swap_promotion_button), onClick = {}, diff --git a/core/ui/src/main/res/drawable/img_swap_promo_banner_background.webp b/core/ui/src/main/res/drawable/img_swap_promo_blue_banner_background.webp similarity index 100% rename from core/ui/src/main/res/drawable/img_swap_promo_banner_background.webp rename to core/ui/src/main/res/drawable/img_swap_promo_blue_banner_background.webp diff --git a/core/ui/src/main/res/drawable/img_swap_promo_green_banner_background.webp b/core/ui/src/main/res/drawable/img_swap_promo_green_banner_background.webp new file mode 100644 index 0000000000000000000000000000000000000000..ffaced2ad24053b210b26e771c88376f428826da GIT binary patch literal 12118 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 MDW{!NqL?560Qs9?9RL6T literal 0 HcmV?d00001 diff --git a/data/promo/.gitignore b/data/promo/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/data/promo/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/data/promo/build.gradle.kts b/data/promo/build.gradle.kts new file mode 100644 index 0000000000..b11dc26ed9 --- /dev/null +++ b/data/promo/build.gradle.kts @@ -0,0 +1,25 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.kapt) + alias(deps.plugins.hilt.android) + id("configuration") +} + +android { + namespace = "com.tangem.data.promo" +} + +dependencies { + implementation(deps.androidx.datastore) + + implementation(deps.jodatime) + implementation(deps.hilt.android) + kapt(deps.hilt.kapt) + + implementation(projects.domain.tokens) + implementation(projects.domain.tokens.models) + + implementation(projects.core.datasource) + implementation(projects.core.utils) +} \ No newline at end of file diff --git a/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt b/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt new file mode 100644 index 0000000000..3121a961d7 --- /dev/null +++ b/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt @@ -0,0 +1,29 @@ +package com.tangem.data.promo + +import com.tangem.data.promo.converters.PromoResponseConverter +import com.tangem.datasource.api.common.response.getOrThrow +import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.domain.promo.PromoBanner +import com.tangem.domain.tokens.repository.PromoRepository +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import com.tangem.utils.coroutines.runCatching + +internal class DefaultPromoRepository( + private val tangemApi: TangemTechApi, + private val dispatchers: CoroutineDispatcherProvider, +) : PromoRepository { + + private val promoResponseConverter = PromoResponseConverter() + override suspend fun getChangellyPromoBanner(): PromoBanner? { + return runCatching(dispatchers.io) { + promoResponseConverter.convert( + tangemApi.getPromotionInfo(CHANGELLY_NAME) + .getOrThrow(), + ) + }.getOrNull() + } + + private companion object { + private const val CHANGELLY_NAME = "changelly" + } +} \ No newline at end of file diff --git a/data/promo/src/main/java/com/tangem/data/promo/converters/PromoResponseConverter.kt b/data/promo/src/main/java/com/tangem/data/promo/converters/PromoResponseConverter.kt new file mode 100644 index 0000000000..c7ad8f331a --- /dev/null +++ b/data/promo/src/main/java/com/tangem/data/promo/converters/PromoResponseConverter.kt @@ -0,0 +1,23 @@ +package com.tangem.data.promo.converters + +import com.tangem.datasource.api.promotion.models.PromotionInfoResponse +import com.tangem.domain.promo.PromoBanner +import com.tangem.utils.converter.Converter +import org.joda.time.DateTime + +class PromoResponseConverter : Converter { + + override fun convert(value: PromotionInfoResponse): PromoBanner? { + val bannerState = value.bannerState ?: return null + return PromoBanner( + name = value.name, + bannerState = PromoBanner.BannerState( + status = bannerState.status, + timeline = PromoBanner.Timeline( + start = DateTime.parse(bannerState.timeline.start), + end = DateTime.parse(bannerState.timeline.end), + ), + ), + ) + } +} \ No newline at end of file diff --git a/data/promo/src/main/java/com/tangem/data/promo/di/PromoDataModule.kt b/data/promo/src/main/java/com/tangem/data/promo/di/PromoDataModule.kt new file mode 100644 index 0000000000..c094043a9c --- /dev/null +++ b/data/promo/src/main/java/com/tangem/data/promo/di/PromoDataModule.kt @@ -0,0 +1,26 @@ +package com.tangem.data.promo.di + +import com.tangem.data.promo.DefaultPromoRepository +import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.di.DevTangemApi +import com.tangem.domain.tokens.repository.PromoRepository +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object PromoDataModule { + + @Provides + @Singleton + fun providePromoRepository( + @DevTangemApi tangemTechApi: TangemTechApi, + dispatchers: CoroutineDispatcherProvider, + ): PromoRepository { + return DefaultPromoRepository(tangemTechApi, dispatchers) + } +} \ No newline at end of file diff --git a/data/settings/src/main/java/com/tangem/data/settings/DefaultSwapPromoRepository.kt b/data/settings/src/main/java/com/tangem/data/settings/DefaultSwapPromoRepository.kt index 8ba9fe211e..6d9ccf740a 100644 --- a/data/settings/src/main/java/com/tangem/data/settings/DefaultSwapPromoRepository.kt +++ b/data/settings/src/main/java/com/tangem/data/settings/DefaultSwapPromoRepository.kt @@ -1,14 +1,12 @@ package com.tangem.data.settings import com.tangem.datasource.local.preferences.AppPreferencesStore -import com.tangem.datasource.local.preferences.PreferencesKeys.IS_TOKEN_SWAP_PROMO_SHOW_KEY -import com.tangem.datasource.local.preferences.PreferencesKeys.IS_WALLET_SWAP_PROMO_SHOW_KEY +import com.tangem.datasource.local.preferences.PreferencesKeys.IS_TOKEN_SWAP_PROMO_CHANGELLY_SHOW_KEY +import com.tangem.datasource.local.preferences.PreferencesKeys.IS_WALLET_SWAP_PROMO_CHANGELLY_SHOW_KEY import com.tangem.datasource.local.preferences.utils.get import com.tangem.datasource.local.preferences.utils.store import com.tangem.domain.settings.repositories.SwapPromoRepository import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map -import java.util.Calendar /** * Repository for showing swap promo notification. @@ -16,47 +14,25 @@ import java.util.Calendar class DefaultSwapPromoRepository( private val appPreferencesStore: AppPreferencesStore, ) : SwapPromoRepository { - override fun isReadyToShowWallet(): Flow { - return appPreferencesStore.get(IS_WALLET_SWAP_PROMO_SHOW_KEY, true) - .map { it && checkPromoPeriod() } + override fun isReadyToShowWalletPromo(): Flow { + return appPreferencesStore.get(IS_WALLET_SWAP_PROMO_CHANGELLY_SHOW_KEY, true) } - override fun isReadyToShowToken(): Flow { - return appPreferencesStore.get(IS_TOKEN_SWAP_PROMO_SHOW_KEY, true) - .map { it && checkPromoPeriod() } + override fun isReadyToShowTokenPromo(): Flow { + return appPreferencesStore.get(IS_TOKEN_SWAP_PROMO_CHANGELLY_SHOW_KEY, true) } - override suspend fun setNeverToShowWallet() { + override suspend fun setNeverToShowWalletPromo() { appPreferencesStore.store( - key = IS_WALLET_SWAP_PROMO_SHOW_KEY, + key = IS_WALLET_SWAP_PROMO_CHANGELLY_SHOW_KEY, value = false, ) } - override suspend fun setNeverToShowToken() { + override suspend fun setNeverToShowTokenPromo() { appPreferencesStore.store( - key = IS_TOKEN_SWAP_PROMO_SHOW_KEY, + key = IS_TOKEN_SWAP_PROMO_CHANGELLY_SHOW_KEY, value = false, ) } - - private suspend fun checkPromoPeriod(): Boolean { - val calendar = Calendar.getInstance() - val currentTime = calendar.timeInMillis - calendar.set(END_YEAR_KEY, END_MONTH_KEY, END_DAY_KEY, 0, 0, 0) - val endTime = calendar.timeInMillis - - val shouldShow = endTime - currentTime > 0 - if (!shouldShow) { - setNeverToShowToken() - setNeverToShowWallet() - } - return shouldShow - } - - companion object { - private const val END_DAY_KEY = 1 - private const val END_MONTH_KEY = 1 // February - private const val END_YEAR_KEY = 2024 - } } \ No newline at end of file diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowSwapPromoTokenUseCase.kt b/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowSwapPromoTokenUseCase.kt index 0c75a4f52a..966e1bcf83 100644 --- a/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowSwapPromoTokenUseCase.kt +++ b/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowSwapPromoTokenUseCase.kt @@ -5,7 +5,7 @@ import kotlinx.coroutines.flow.Flow class ShouldShowSwapPromoTokenUseCase(private val swapPromoRepository: SwapPromoRepository) { - operator fun invoke(): Flow = swapPromoRepository.isReadyToShowToken() + operator fun invoke(): Flow = swapPromoRepository.isReadyToShowTokenPromo() - suspend fun neverToShow() = swapPromoRepository.setNeverToShowToken() + suspend fun neverToShow() = swapPromoRepository.setNeverToShowTokenPromo() } \ No newline at end of file diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowSwapPromoWalletUseCase.kt b/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowSwapPromoWalletUseCase.kt index 573a5ea94b..e2b30e1ba5 100644 --- a/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowSwapPromoWalletUseCase.kt +++ b/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowSwapPromoWalletUseCase.kt @@ -5,7 +5,7 @@ import kotlinx.coroutines.flow.Flow class ShouldShowSwapPromoWalletUseCase(private val swapPromoRepository: SwapPromoRepository) { - operator fun invoke(): Flow = swapPromoRepository.isReadyToShowWallet() + operator fun invoke(): Flow = swapPromoRepository.isReadyToShowWalletPromo() - suspend fun neverToShow() = swapPromoRepository.setNeverToShowWallet() + suspend fun neverToShow() = swapPromoRepository.setNeverToShowWalletPromo() } \ No newline at end of file diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SwapPromoRepository.kt b/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SwapPromoRepository.kt index 95d31f65d8..ca97755e66 100644 --- a/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SwapPromoRepository.kt +++ b/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SwapPromoRepository.kt @@ -3,11 +3,11 @@ package com.tangem.domain.settings.repositories import kotlinx.coroutines.flow.Flow interface SwapPromoRepository { - fun isReadyToShowWallet(): Flow + fun isReadyToShowWalletPromo(): Flow - fun isReadyToShowToken(): Flow + fun isReadyToShowTokenPromo(): Flow - suspend fun setNeverToShowWallet() + suspend fun setNeverToShowWalletPromo() - suspend fun setNeverToShowToken() + suspend fun setNeverToShowTokenPromo() } \ No newline at end of file diff --git a/domain/tokens/models/build.gradle.kts b/domain/tokens/models/build.gradle.kts index a354fa4700..f3bf4ce59c 100644 --- a/domain/tokens/models/build.gradle.kts +++ b/domain/tokens/models/build.gradle.kts @@ -15,4 +15,5 @@ dependencies { implementation(deps.tangem.blockchain) { exclude(module = "joda-time") } + implementation(deps.jodatime) } \ No newline at end of file diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/promo/PromoBanner.kt b/domain/tokens/models/src/main/java/com/tangem/domain/promo/PromoBanner.kt new file mode 100644 index 0000000000..564a49e3d4 --- /dev/null +++ b/domain/tokens/models/src/main/java/com/tangem/domain/promo/PromoBanner.kt @@ -0,0 +1,25 @@ +package com.tangem.domain.promo + +import org.joda.time.DateTime + +data class PromoBanner( + val name: String, + val bannerState: BannerState, +) { + + val isActive = bannerState.status == ACTIVE_STATUS && bannerState.timeline.end.isAfterNow + + data class BannerState( + val timeline: Timeline, + val status: String, + ) + + data class Timeline( + val start: DateTime, + val end: DateTime, + ) + + private companion object { + const val ACTIVE_STATUS = "active" + } +} \ No newline at end of file diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyWarning.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyWarning.kt index e393e4fbd3..b90d787043 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyWarning.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyWarning.kt @@ -1,6 +1,7 @@ package com.tangem.domain.tokens.model.warnings import com.tangem.domain.tokens.model.CryptoCurrency +import org.joda.time.DateTime import java.math.BigDecimal sealed class CryptoCurrencyWarning { @@ -42,5 +43,8 @@ sealed class CryptoCurrencyWarning { data class HasPendingTransactions(val blockchainSymbol: String) : CryptoCurrencyWarning() - object SwapPromo : CryptoCurrencyWarning() + data class SwapPromo( + val startDateTime: DateTime, + val endDateTime: DateTime, + ) : CryptoCurrencyWarning() } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt index b43052898e..3e5b6a3ed4 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt @@ -7,10 +7,7 @@ import com.tangem.domain.tokens.model.FeePaidCurrency import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations -import com.tangem.domain.tokens.repository.CurrenciesRepository -import com.tangem.domain.tokens.repository.MarketCryptoCurrencyRepository -import com.tangem.domain.tokens.repository.NetworksRepository -import com.tangem.domain.tokens.repository.QuotesRepository +import com.tangem.domain.tokens.repository.* import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.swap.domain.api.SwapRepository @@ -29,6 +26,7 @@ class GetCurrencyWarningsUseCase( private val networksRepository: NetworksRepository, private val swapRepository: SwapRepository, private val marketCryptoCurrencyRepository: MarketCryptoCurrencyRepository, + private val promoRepository: PromoRepository, private val showSwapPromoTokenUseCase: ShouldShowSwapPromoTokenUseCase, private val dispatchers: CoroutineDispatcherProvider, ) { @@ -90,7 +88,9 @@ class GetCurrencyWarningsUseCase( showSwapPromoTokenUseCase().conflate(), flowOf(marketCryptoCurrencyRepository.isExchangeable(userWalletId, currency)).conflate(), ) { shouldShowSwapPromo, isExchangeable -> - if (shouldShowSwapPromo && isExchangeable && currencyStatus.value !is CryptoCurrencyStatus.Unreachable) { + val promoBanner = promoRepository.getChangellyPromoBanner() ?: return@combine null + val showPromo = promoBanner.isActive && shouldShowSwapPromo + if (showPromo && isExchangeable && currencyStatus.value !is CryptoCurrencyStatus.Unreachable) { cryptoStatuses.fold( ifLeft = { null }, ifRight = { cryptoCurrencyStatuses -> @@ -124,7 +124,10 @@ class GetCurrencyWarningsUseCase( } } if (showPromo) { - CryptoCurrencyWarning.SwapPromo + CryptoCurrencyWarning.SwapPromo( + startDateTime = promoBanner.bannerState.timeline.start, + endDateTime = promoBanner.bannerState.timeline.end, + ) } else { null } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/PromoRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/PromoRepository.kt new file mode 100644 index 0000000000..b15e05db54 --- /dev/null +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/PromoRepository.kt @@ -0,0 +1,8 @@ +package com.tangem.domain.tokens.repository + +import com.tangem.domain.promo.PromoBanner + +interface PromoRepository { + + suspend fun getChangellyPromoBanner(): PromoBanner? +} \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsNotification.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsNotification.kt index 8a4732ea16..185abeff5d 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsNotification.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsNotification.kt @@ -9,6 +9,7 @@ import com.tangem.core.ui.extensions.wrappedList import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning import com.tangem.features.tokendetails.impl.R +import org.joda.time.DateTime @Immutable internal sealed class TokenDetailsNotification(val config: NotificationConfig) { @@ -41,14 +42,22 @@ internal sealed class TokenDetailsNotification(val config: NotificationConfig) { ) data class SwapPromo( + val startDateTime: DateTime, + val endDateTime: DateTime, val onSwapClick: () -> Unit, val onCloseClick: () -> Unit, ) : TokenDetailsNotification( config = NotificationConfig( - title = resourceReference(id = R.string.token_swap_promotion_title), - subtitle = resourceReference(id = R.string.token_swap_promotion_message), + title = resourceReference( + id = R.string.token_swap_changelly_promotion_title, + formatArgs = wrappedList("0%"), + ), + subtitle = resourceReference( + id = R.string.token_swap_changelly_promotion_message, + formatArgs = wrappedList("0%", startDateTime.dayOfMonth, endDateTime.dayOfMonth), + ), iconResId = R.drawable.img_swap_promo, - backgroundResId = R.drawable.img_swap_promo_banner_background, + backgroundResId = R.drawable.img_swap_promo_green_banner_background, onCloseClick = onCloseClick, buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig( text = resourceReference(id = com.tangem.core.ui.R.string.token_swap_promotion_button), diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsNotificationConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsNotificationConverter.kt index c2a5ea5591..bf37784e25 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsNotificationConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsNotificationConverter.kt @@ -68,6 +68,8 @@ internal class TokenDetailsNotificationConverter( coinSymbol = warning.blockchainSymbol, ) is CryptoCurrencyWarning.SwapPromo -> SwapPromo( + startDateTime = warning.startDateTime, + endDateTime = warning.endDateTime, onSwapClick = clickIntents::onSwapPromoClick, onCloseClick = clickIntents::onSwapPromoDismiss, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt index 574705c9f4..e4bec421bb 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt @@ -4,13 +4,16 @@ import arrow.core.Either import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.demo.IsDemoCardUseCase +import com.tangem.domain.promo.PromoBanner import com.tangem.domain.settings.IsReadyToShowRateAppUseCase +import com.tangem.domain.settings.ShouldShowSwapPromoWalletUseCase import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkGroup import com.tangem.domain.tokens.model.TokenList +import com.tangem.domain.tokens.repository.PromoRepository import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification @@ -26,12 +29,15 @@ import kotlinx.coroutines.flow.flowOf import timber.log.Timber import javax.inject.Inject +@Suppress("LongParameterList") @ViewModelScoped internal class GetMultiWalletWarningsFactory @Inject constructor( private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, private val getTokenListUseCase: GetTokenListUseCase, private val isDemoCardUseCase: IsDemoCardUseCase, private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase, + private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase, + private val promoRepository: PromoRepository, private val isNeedToBackupUseCase: IsNeedToBackupUseCase, ) { @@ -52,9 +58,15 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( flow = getTokenListUseCase(userWallet.walletId).conflate(), flow2 = isReadyToShowRateAppUseCase().conflate(), flow3 = isNeedToBackupUseCase(userWallet.walletId).conflate(), - ) { maybeTokenList, isReadyToShowRating, isNeedToBackup -> + flow4 = shouldShowSwapPromoWalletUseCase().conflate(), + ) { maybeTokenList, isReadyToShowRating, isNeedToBackup, shouldShowPromo -> + + val promoBanner = promoRepository.getChangellyPromoBanner() + readyForRateAppNotification = true buildList { + addSwapPromoNotification(shouldShowPromo, promoBanner, clickIntents) + addCriticalNotifications(cardTypesResolver) addInformationalNotifications(cardTypesResolver, maybeTokenList, clickIntents) @@ -66,6 +78,23 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( } } + private fun MutableList.addSwapPromoNotification( + shouldShowPromo: Boolean, + promoBanner: PromoBanner?, + clickIntents: WalletClickIntentsV2, + ) { + promoBanner ?: return + val promoNotification = WalletNotification.SwapPromo( + startDateTime = promoBanner.bannerState.timeline.start, + endDateTime = promoBanner.bannerState.timeline.end, + onCloseClick = clickIntents::onCloseSwapPromoClick, + ) + addIf( + element = promoNotification, + condition = shouldShowPromo && promoBanner.isActive, + ) + } + private fun MutableList.addCriticalNotifications(cardTypesResolver: CardTypesResolver) { addIf( element = WalletNotification.Critical.DevCard, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletNotification.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletNotification.kt index 7f6bfd6b46..6feefe3150 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletNotification.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletNotification.kt @@ -7,6 +7,7 @@ import com.tangem.core.ui.extensions.pluralReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.wrappedList import com.tangem.feature.wallet.impl.R +import org.joda.time.DateTime /** * Wallet notification component state @@ -168,13 +169,21 @@ sealed class WalletNotification(val config: NotificationConfig) { ) data class SwapPromo( + val startDateTime: DateTime, + val endDateTime: DateTime, val onCloseClick: () -> Unit, ) : WalletNotification( config = NotificationConfig( - title = resourceReference(id = R.string.main_swap_promotion_title), - subtitle = resourceReference(id = R.string.main_swap_promotion_message), + title = resourceReference( + id = R.string.main_swap_changelly_promotion_title, + formatArgs = wrappedList("0%"), + ), + subtitle = resourceReference( + id = R.string.main_swap_changelly_promotion_message, + formatArgs = wrappedList("0%", startDateTime.dayOfMonth, endDateTime.dayOfMonth), + ), iconResId = R.drawable.img_swap_promo, - backgroundResId = R.drawable.img_swap_promo_banner_background, + backgroundResId = R.drawable.img_swap_promo_green_banner_background, onCloseClick = onCloseClick, ), ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt index dad5efb741..5c390dcb0b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt @@ -72,6 +72,4 @@ internal interface WalletClickIntents { fun onExploreClick() fun onTransactionClick(txHash: String) - - fun onCloseSwapPromoNotificationClick() } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt index c0145c919b..da74cc71c1 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt @@ -5,7 +5,6 @@ import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.settings.IsReadyToShowRateAppUseCase -import com.tangem.domain.settings.ShouldShowSwapPromoWalletUseCase import com.tangem.domain.tokens.GetMissedAddressesCryptoCurrenciesUseCase import com.tangem.domain.tokens.error.GetCurrenciesError import com.tangem.domain.tokens.model.CryptoCurrency @@ -41,7 +40,6 @@ internal class WalletNotificationsListFactory( private val isNeedToBackupUseCase: IsNeedToBackupUseCase, private val getMissedAddressCryptoCurrenciesUseCase: GetMissedAddressesCryptoCurrenciesUseCase, private val hasSingleWalletSignedHashesUseCase: HasSingleWalletSignedHashesUseCase, - private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase, private val clickIntents: WalletClickIntents, ) { @@ -57,12 +55,9 @@ internal class WalletNotificationsListFactory( flow2 = isReadyToShowRateAppUseCase().conflate(), flow3 = isNeedToBackupUseCase(selectedWallet.walletId).conflate(), flow4 = getMissedAddressCryptoCurrenciesUseCase(selectedWallet.walletId).conflate(), - flow5 = shouldShowSwapPromoWalletUseCase().conflate(), - ) { hasSignedHashes, isReadyToShowRating, isNeedToBackup, maybeMissedAddressCurrencies, isShowSwapPromo -> + ) { hasSignedHashes, isReadyToShowRating, isNeedToBackup, maybeMissedAddressCurrencies -> readyForRateAppNotification = true buildList { - addSwapPromoNotification(isShowSwapPromo, cardTypesResolver) - addCriticalNotifications(cardTypesResolver) addInformationalNotifications(cardTypesResolver, maybeMissedAddressCurrencies) @@ -86,18 +81,6 @@ internal class WalletNotificationsListFactory( } } - private fun MutableList.addSwapPromoNotification( - showSwapPromo: Boolean, - cardTypesResolver: CardTypesResolver, - ) { - addIf( - element = WalletNotification.SwapPromo( - clickIntents::onCloseSwapPromoNotificationClick, - ), - condition = showSwapPromo && cardTypesResolver.isMultiwalletAllowed(), - ) - } - private fun MutableList.addCriticalNotifications(cardTypesResolver: CardTypesResolver) { addIf( element = WalletNotification.Critical.DevCard, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 8adfe05590..b1c839e9d0 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -129,7 +129,6 @@ internal class WalletViewModel @Inject constructor( private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase, private val isDemoCardUseCase: IsDemoCardUseCase, private val scanCardToUnlockWalletUseCase: ScanCardToUnlockWalletClickHandler, - private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase, isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase, isNeedToBackupUseCase: IsNeedToBackupUseCase, getMissedAddressesCryptoCurrenciesUseCase: GetMissedAddressesCryptoCurrenciesUseCase, @@ -148,7 +147,6 @@ internal class WalletViewModel @Inject constructor( isNeedToBackupUseCase = isNeedToBackupUseCase, getMissedAddressCryptoCurrenciesUseCase = getMissedAddressesCryptoCurrenciesUseCase, hasSingleWalletSignedHashesUseCase = hasSingleWalletSignedHashesUseCase, - shouldShowSwapPromoWalletUseCase = shouldShowSwapPromoWalletUseCase, clickIntents = this, ) @@ -795,12 +793,6 @@ internal class WalletViewModel @Inject constructor( refreshSingleCurrencyContent(selectedWalletIndex) } - override fun onCloseSwapPromoNotificationClick() { - viewModelScope.launch(dispatchers.main) { - shouldShowSwapPromoWalletUseCase.neverToShow() - } - } - // FIXME: refreshSingleCurrencyContent mustn't update the TxHistory and Buttons. It only must fetch primary // currency. Now it not works because GetPrimaryCurrency's subscriber uses .distinctUntilChanged() private fun refreshSingleCurrencyContent(walletIndex: Int) { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt index ae87db6d02..a9c4287571 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt @@ -18,6 +18,7 @@ import com.tangem.domain.redux.LegacyAction import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.settings.NeverToSuggestRateAppUseCase import com.tangem.domain.settings.RemindToRateAppLaterUseCase +import com.tangem.domain.settings.ShouldShowSwapPromoWalletUseCase import com.tangem.domain.tokens.FetchTokenListUseCase import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UnlockWalletsError @@ -62,6 +63,8 @@ internal interface WalletWarningsClickIntents { fun onDislikeAppClick() fun onCloseRateAppWarningClick() + + fun onCloseSwapPromoClick() } @Suppress("LongParameterList") @@ -82,6 +85,7 @@ internal class WalletWarningsClickIntentsImplementer @Inject constructor( private val reduxStateHolder: ReduxStateHolder, private val testerFeatureToggles: TesterFeatureToggles, private val dispatchers: CoroutineDispatcherProvider, + private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase, ) : BaseWalletClickIntents(), WalletWarningsClickIntents { override fun onAddBackupCardClick() { @@ -302,4 +306,10 @@ internal class WalletWarningsClickIntentsImplementer @Inject constructor( remindToRateAppLaterUseCase() } } + + override fun onCloseSwapPromoClick() { + viewModelScope.launch(dispatchers.main) { + shouldShowSwapPromoWalletUseCase.neverToShow() + } + } } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 7fc4e85cba..11c13bfbe1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -152,4 +152,5 @@ include(":data:wallets") include(":data:analytics") include(":data:transaction") include(":data:visa") +include(":data:promo") // endregion Data modules \ No newline at end of file From 5cd875c12181cb0d5af1902f1ba24ff67a677366 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 7 Feb 2024 12:46:03 +0300 Subject: [PATCH 04/17] Updated on 2026-08-14 --- .../tangem/tap/common/redux/global/GlobalMiddleware.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) 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 841c3ff5a4..666a16ab82 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 @@ -4,17 +4,14 @@ import com.tangem.common.CompletionResult import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.guard import com.tangem.core.analytics.Analytics +import com.tangem.core.analytics.models.Basic import com.tangem.core.navigation.StateDialog import com.tangem.datasource.config.models.Config import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.common.LogConfig import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse -import com.tangem.core.analytics.models.Basic -import com.tangem.tap.common.extensions.dispatchDebugErrorNotification -import com.tangem.tap.common.extensions.dispatchDialogShow -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.extensions.dispatchWithMain +import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppState import com.tangem.tap.features.send.redux.SendAction import com.tangem.tap.mainScope @@ -167,6 +164,7 @@ private fun handleAction(action: Action, appState: () -> AppState?) { action.manager.selectedUserWallet .distinctUntilChanged() .onEach { userWallet -> + Analytics.setContext(userWallet.scanResponse) Analytics.send(Basic.WalletOpened()) store.state.globalState.feedbackManager?.infoHolder?.let { infoHolder -> From 6e7b57a57de21a6703a2d5dd742fc7e34e05a88f Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 8 Feb 2024 19:55:59 +0300 Subject: [PATCH 05/17] Updated on 2026-08-14 --- .../tokens/models/analytics/TokenExchangeAnalyticsEvent.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/analytics/TokenExchangeAnalyticsEvent.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/analytics/TokenExchangeAnalyticsEvent.kt index a2161adc46..260c9ce93f 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/analytics/TokenExchangeAnalyticsEvent.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/analytics/TokenExchangeAnalyticsEvent.kt @@ -8,12 +8,12 @@ class TokenExchangeAnalyticsEvent( ) : AnalyticsEvent("Token", event, params, null) { class CexTxStatusOpened(token: String) : TokenScreenAnalyticsEvent( - event = "ChangeNow Status Opened", + event = "Swap Status Opened", params = mapOf("Token" to token), ) class CexTxStatusChanged(token: String, status: String) : TokenScreenAnalyticsEvent( - event = "ChangeNow Status", + event = "Swap Status", params = mapOf("Token" to token, "Status" to status), ) From 6f22bf53b21b656a0e4a78938548423e29b494bd Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 8 Feb 2024 19:56:50 +0300 Subject: [PATCH 06/17] Updated on 2026-08-14 --- .../redux/OnboardingOtherCardsMiddleware.kt | 24 +++++++++++++++++-- .../saveWallet/redux/SaveWalletMiddleware.kt | 9 ++++++- .../domain/common/configs/CardConfig.kt | 2 +- .../common/configs/GenericCardConfig.kt | 14 ++++++----- 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt index 3ee9db75ea..243319a1c0 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt @@ -1,12 +1,15 @@ package com.tangem.tap.features.onboarding.products.otherCards.redux import com.tangem.common.CompletionResult +import com.tangem.common.core.TangemSdkError import com.tangem.core.analytics.Analytics import com.tangem.domain.common.extensions.withMainContext import com.tangem.tap.common.analytics.events.Onboarding +import com.tangem.tap.common.extensions.dispatchDialogShow import com.tangem.tap.common.postUi import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.features.onboarding.OnboardingDialog import com.tangem.tap.features.onboarding.OnboardingHelper import com.tangem.tap.scope import com.tangem.tap.store @@ -72,7 +75,10 @@ private fun handleOtherCardsAction(action: Action) { } is OnboardingOtherCardsAction.CreateWallet -> { scope.launch { - val result = tangemSdkManager.createProductWallet(onboardingManager.scanResponse) + val result = tangemSdkManager.createProductWallet( + scanResponse = onboardingManager.scanResponse, + shouldReset = globalState.onboardingState.shouldResetOnCreate, + ) withMainContext { when (result) { is CompletionResult.Success -> { @@ -87,7 +93,11 @@ private fun handleOtherCardsAction(action: Action) { delay(DELAY_SDK_DIALOG_CLOSE) store.dispatch(OnboardingOtherCardsAction.SetStepOfScreen(OnboardingOtherCardsStep.Done)) } - is CompletionResult.Failure -> Unit + is CompletionResult.Failure -> { + if (result.error is TangemSdkError.WalletAlreadyCreated) { + handleActivationError() + } + } } } } @@ -101,4 +111,14 @@ private fun handleOtherCardsAction(action: Action) { } else -> Unit } +} + +private fun handleActivationError() { + store.dispatchDialogShow( + OnboardingDialog.WalletActivationError( + onConfirm = { + store.dispatch(GlobalAction.Onboarding.ShouldResetCardOnCreate(true)) + }, + ), + ) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt index 7ab41ec265..9408cbcc9e 100644 --- a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt @@ -129,7 +129,14 @@ internal class SaveWalletMiddleware { } store.dispatchOnMain(SaveWalletAction.Save.Success) - store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet)) + + store.dispatchOnMain( + if (store.state.navigationState.backStack.contains(AppScreen.Wallet)) { + NavigationAction.PopBackTo(AppScreen.Wallet) + } else { + NavigationAction.NavigateTo(AppScreen.Wallet) + }, + ) } } } diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt b/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt index 51ff7de9b3..7e3fc92045 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt @@ -22,7 +22,7 @@ sealed interface CardConfig { ) { return MultiWalletCardConfig } - return GenericCardConfig + return GenericCardConfig(cardDTO.settings.maxWalletsCount) } } } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/configs/GenericCardConfig.kt b/domain/legacy/src/main/java/com/tangem/domain/common/configs/GenericCardConfig.kt index 4955dbdb11..547bfe780d 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/configs/GenericCardConfig.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/configs/GenericCardConfig.kt @@ -4,12 +4,14 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.common.card.EllipticCurve import timber.log.Timber -object GenericCardConfig : CardConfig { - override val mandatoryCurves: List - get() = listOf( - EllipticCurve.Secp256k1, - EllipticCurve.Ed25519, - ) +internal class GenericCardConfig(maxWalletCount: Int) : CardConfig { + + override val mandatoryCurves: List = buildList { + add(EllipticCurve.Secp256k1) + if (maxWalletCount > 1) { + add(EllipticCurve.Ed25519) + } + } /** * Old logic to determine primary curve for blockchain From a7b2af9832a57de01b930cb5e189e4098490d035 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 8 Feb 2024 13:49:26 +0300 Subject: [PATCH 07/17] Updated on 2026-08-14 --- .../tap/features/details/redux/DetailsMiddleware.kt | 6 +++++- .../wallet/redux/OnboardingWalletMiddleware.kt | 12 ++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index f039fbb8b1..508f29e664 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -122,7 +122,6 @@ class DetailsMiddleware { doBeforeErase() tangemSdkManager.resetToFactorySettings(card.cardId, true) - .flatMap { userWalletsListManager.delete(listOfNotNull(userWalletId)) } .flatMap { tangemSdkManager.deleteSavedUserCodes(setOf(card.cardId)) } .doOnSuccess { Analytics.send(Settings.CardSettings.FactoryResetFinished()) @@ -130,16 +129,21 @@ class DetailsMiddleware { val selectedUserWallet = userWalletsListManager.selectedUserWalletSync if (selectedUserWallet != null) { if (userWalletsListManager.isLockedSync) { + userWalletsListManager.delete(listOfNotNull(userWalletId)) store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Welcome)) } else { store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet)) + delay(timeMillis = 500) + userWalletsListManager.delete(listOfNotNull(userWalletId)) store.onUserWalletSelected(selectedUserWallet) } } else { + userWalletsListManager.delete(listOfNotNull(userWalletId)) store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Home)) } } .doOnFailure { error -> + userWalletsListManager.delete(listOfNotNull(userWalletId)) if (error is TangemSdkError && error !is TangemSdkError.UserCancelled) { Analytics.send(Settings.CardSettings.FactoryResetFinished(error)) } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index 418ca44742..3e602349e8 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -1,6 +1,7 @@ package com.tangem.tap.features.onboarding.products.wallet.redux import android.net.Uri +import com.google.firebase.crashlytics.FirebaseCrashlytics import com.tangem.common.CompletionResult import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.guard @@ -70,8 +71,8 @@ private fun handleWalletAction(action: Action) { when (action) { OnboardingWalletAction.Init -> { - ifNotNull(onboardingManager, card) { manager, card -> - if (!manager.isActivationStarted(card.cardId)) { + ifNotNull(onboardingManager, card) { manager, notNullCard -> + if (!manager.isActivationStarted(notNullCard.cardId)) { Analytics.send(Onboarding.Started()) } } @@ -403,8 +404,9 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) is CompletionResult.Success -> { store.dispatchOnMain(BackupAction.AddBackupCard.Success) } - is CompletionResult.Failure -> { + val crashlytics = FirebaseCrashlytics.getInstance() + when (val error = result.error) { is TangemSdkError.BackupFailedNotEmptyWallets -> { if (card?.canSkipBackup == false) { @@ -413,6 +415,8 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) BackupDialog.ResetBackupCard(error.cardId), ), ) + } else { + crashlytics.recordException(error) } } is TangemSdkError.IssuerSignatureLoadingFailed -> { @@ -420,7 +424,7 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) GlobalAction.ShowDialog(BackupDialog.AttestationFailed), ) } - else -> Unit + else -> crashlytics.recordException(error) } } } From 5d5e5038dd46c43a0f2a56d1aea57d9cb425eb82 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 8 Feb 2024 14:06:22 +0300 Subject: [PATCH 08/17] Updated on 2026-08-14 --- .../wallets/RenameWalletDialogContent.kt | 59 ------------------- .../presentation/common/WalletPreviewData.kt | 6 +- .../wallet/state/WalletAlertState.kt | 44 ++++++++++---- .../state/components/WalletCardState.kt | 10 ++-- .../factory/WalletSkeletonStateConverter.kt | 4 +- .../InitializeWalletsTransformer.kt | 2 +- .../state2/utils/WalletLoadingStateFactory.kt | 2 +- .../presentation/wallet/ui/WalletAlert.kt | 36 ++++++++++- .../wallet/ui/components/common/WalletCard.kt | 38 +++++------- .../wallet/viewmodels/WalletClickIntents.kt | 4 +- .../wallet/viewmodels/WalletViewModel.kt | 18 +++++- .../intents/WalletCardClickIntents.kt | 23 ++++++-- 12 files changed, 129 insertions(+), 117 deletions(-) delete mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/wallets/RenameWalletDialogContent.kt diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/wallets/RenameWalletDialogContent.kt b/core/ui/src/main/java/com/tangem/core/ui/components/wallets/RenameWalletDialogContent.kt deleted file mode 100644 index eabd0fbeb8..0000000000 --- a/core/ui/src/main/java/com/tangem/core/ui/components/wallets/RenameWalletDialogContent.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.tangem.core.ui.components.wallets - -import androidx.compose.runtime.* -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.TextFieldValue -import androidx.compose.ui.tooling.preview.Preview -import com.tangem.core.ui.R -import com.tangem.core.ui.components.AdditionalTextInputDialogParams -import com.tangem.core.ui.components.DialogButton -import com.tangem.core.ui.components.TextInputDialog -import com.tangem.core.ui.res.TangemTheme - -/** - * Rename a wallet dialog - * - * @param name wallet name - * @param onConfirm lambda be invoked when Confirm button is clicked - * @param onDismiss lambda be invoked when dialog is dismissed - */ -@Composable -fun RenameWalletDialogContent(name: String, onConfirm: (newName: String) -> Unit, onDismiss: () -> Unit) { - var value by remember { mutableStateOf(TextFieldValue(text = name)) } - - TextInputDialog( - fieldValue = value, - confirmButton = DialogButton( - title = stringResource(id = R.string.common_ok), - enabled = value.text.isNotEmpty() && value.text != name, - onClick = { onConfirm(value.text) }, - ), - onDismissDialog = onDismiss, - onValueChange = { value = it }, - title = stringResource(R.string.user_wallet_list_rename_popup_title), - dismissButton = DialogButton(title = stringResource(id = R.string.common_cancel), onClick = onDismiss), - textFieldParams = AdditionalTextInputDialogParams( - label = stringResource(R.string.user_wallet_list_rename_popup_placeholder), - ), - ) -} - -// region Preview - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun RenameWalletDialogContentPreview_Light() { - TangemTheme(isDark = false) { - RenameWalletDialogContent(name = "", onConfirm = {}, onDismiss = {}) - } -} - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun RenameWalletDialogContentPreview_Dark() { - TangemTheme(isDark = true) { - RenameWalletDialogContent(name = "", onConfirm = {}, onDismiss = {}) - } -} - -// endregion Preview \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index 9ffd482c85..be817ab65a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -46,7 +46,7 @@ internal object WalletPreviewData { content = TextReference.Str("3 cards • Seed phrase3 cards • Seed phrasephrasephrasephrase"), ), imageResId = R.drawable.ill_wallet2_cards3_120_106, - onRenameClick = { _, _ -> }, + onRenameClick = { _ -> }, onDeleteClick = {}, cardCount = 1, ) @@ -57,7 +57,7 @@ internal object WalletPreviewData { id = UserWalletId("321"), title = "Wallet 1", imageResId = R.drawable.ill_wallet2_cards3_120_106, - onRenameClick = { _, _ -> }, + onRenameClick = { _ -> }, onDeleteClick = {}, ) } @@ -67,7 +67,7 @@ internal object WalletPreviewData { id = UserWalletId("24"), title = "Wallet 1", imageResId = R.drawable.ill_wallet2_cards3_120_106, - onRenameClick = { _, _ -> }, + onRenameClick = { _ -> }, onDeleteClick = {}, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletAlertState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletAlertState.kt index 300369728f..47a7834251 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletAlertState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletAlertState.kt @@ -7,40 +7,60 @@ import com.tangem.core.ui.extensions.stringReference import com.tangem.feature.wallet.impl.R @Immutable -internal sealed class WalletAlertState { +internal sealed interface WalletAlertState { - abstract val title: TextReference? - abstract val message: TextReference - open val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok) - open val isWarningConfirmButton: Boolean = false - abstract val onConfirmClick: (() -> Unit)? + @Immutable + sealed class Basic : WalletAlertState { + abstract val title: TextReference? + abstract val message: TextReference + open val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok) + open val isWarningConfirmButton: Boolean = false + abstract val onConfirmClick: (() -> Unit)? + } + + @Immutable + sealed class TextInput : WalletAlertState { + abstract val title: TextReference + abstract val label: TextReference + open val text: String = "" + open val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok) + abstract val onConfirmClick: (String) -> Unit + } data class DefaultAlert( override val title: TextReference, override val message: TextReference, override val onConfirmClick: (() -> Unit)?, - ) : WalletAlertState() + ) : Basic() - data class RemoveWalletAlert(override val onConfirmClick: (() -> Unit)?) : WalletAlertState() { + data class RenameWalletAlert( + override val text: String, + override val onConfirmClick: (String) -> Unit, + ) : TextInput() { + override val title: TextReference = resourceReference(id = R.string.user_wallet_list_rename_popup_title) + override val label: TextReference = resourceReference(id = R.string.user_wallet_list_rename_popup_placeholder) + } + + data class RemoveWalletAlert(override val onConfirmClick: (() -> Unit)?) : Basic() { override val title: TextReference? = null override val message: TextReference = resourceReference(id = R.string.user_wallet_list_delete_prompt) override val confirmButtonText: TextReference = resourceReference(id = R.string.common_delete) override val isWarningConfirmButton: Boolean = true } - object WrongCardIsScanned : WalletAlertState() { + object WrongCardIsScanned : Basic() { override val title: TextReference = resourceReference(R.string.common_warning) override val message: TextReference = resourceReference(R.string.error_wrong_wallet_tapped) override val onConfirmClick: (() -> Unit)? = null } - object RescanWallets : WalletAlertState() { + object RescanWallets : Basic() { override val title: TextReference = resourceReference(R.string.common_attention) override val message: TextReference = resourceReference(R.string.key_invalidated_warning_description) override val onConfirmClick: (() -> Unit)? = null } - object VisaBalancesInfo : WalletAlertState() { + object VisaBalancesInfo : Basic() { override val title: TextReference? = null override val message: TextReference = stringReference( value = "Available balance is actual funds available, considering pending transactions, " + @@ -49,7 +69,7 @@ internal sealed class WalletAlertState { override val onConfirmClick: (() -> Unit)? = null } - object VisaLimitsInfo : WalletAlertState() { + object VisaLimitsInfo : Basic() { override val title: TextReference? = null override val message: TextReference = stringReference( value = "Limits are needed to control costs, improve security, manage risk. " + diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt index 094d5338c1..9b06ec02ca 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt @@ -24,7 +24,7 @@ internal sealed interface WalletCardState { val imageResId: Int? /** Lambda be invoked when Rename button is clicked */ - val onRenameClick: (UserWalletId, String) -> Unit + val onRenameClick: (UserWalletId) -> Unit /** Lambda be invoked when Delete button is clicked */ val onDeleteClick: (UserWalletId) -> Unit @@ -46,7 +46,7 @@ internal sealed interface WalletCardState { override val title: String, override val additionalInfo: WalletAdditionalInfo, override val imageResId: Int?, - override val onRenameClick: (UserWalletId, String) -> Unit, + override val onRenameClick: (UserWalletId) -> Unit, override val onDeleteClick: (UserWalletId) -> Unit, val cardCount: Int?, val balance: String, @@ -67,7 +67,7 @@ internal sealed interface WalletCardState { override val title: String, override val additionalInfo: WalletAdditionalInfo, override val imageResId: Int?, - override val onRenameClick: (UserWalletId, String) -> Unit, + override val onRenameClick: (UserWalletId) -> Unit, override val onDeleteClick: (UserWalletId) -> Unit, ) : WalletCardState @@ -85,7 +85,7 @@ internal sealed interface WalletCardState { override val title: String, override val additionalInfo: WalletAdditionalInfo? = defaultAdditionalInfo, override val imageResId: Int?, - override val onRenameClick: (UserWalletId, String) -> Unit, + override val onRenameClick: (UserWalletId) -> Unit, override val onDeleteClick: (UserWalletId) -> Unit, ) : WalletCardState { @@ -109,7 +109,7 @@ internal sealed interface WalletCardState { override val title: String, override val additionalInfo: WalletAdditionalInfo? = null, override val imageResId: Int?, - override val onRenameClick: (UserWalletId, String) -> Unit, + override val onRenameClick: (UserWalletId) -> Unit, override val onDeleteClick: (UserWalletId) -> Unit, ) : WalletCardState diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt index 1c3c556b1f..8fb80d29d7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt @@ -119,7 +119,7 @@ internal class WalletSkeletonStateConverter( title = name, additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = this), imageResId = createImageResId(), - onRenameClick = clickIntents::onRenameClick, + onRenameClick = clickIntents::onRenameBeforeConfirmationClick, onDeleteClick = clickIntents::onDeleteBeforeConfirmationClick, ) } @@ -130,7 +130,7 @@ internal class WalletSkeletonStateConverter( title = name, additionalInfo = if (isMultiCurrency) WalletAdditionalInfoFactory.resolve(wallet = this) else null, imageResId = createImageResId(), - onRenameClick = clickIntents::onRenameClick, + onRenameClick = clickIntents::onRenameBeforeConfirmationClick, onDeleteClick = clickIntents::onDeleteBeforeConfirmationClick, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/InitializeWalletsTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/InitializeWalletsTransformer.kt index 46b15ef604..a191f69f72 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/InitializeWalletsTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/InitializeWalletsTransformer.kt @@ -90,7 +90,7 @@ internal class InitializeWalletsTransformer( title = name, additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = this), imageResId = WalletImageResolver.resolve(userWallet = this), - onRenameClick = clickIntents::onRenameClick, + onRenameClick = clickIntents::onRenameBeforeConfirmationClick, onDeleteClick = clickIntents::onDeleteBeforeConfirmationClick, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/utils/WalletLoadingStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/utils/WalletLoadingStateFactory.kt index fdf4fb6287..b9654a68e2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/utils/WalletLoadingStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/utils/WalletLoadingStateFactory.kt @@ -84,7 +84,7 @@ internal class WalletLoadingStateFactory(private val clickIntents: WalletClickIn title = name, additionalInfo = if (isMultiCurrency) WalletAdditionalInfoFactory.resolve(wallet = this) else null, imageResId = WalletImageResolver.resolve(userWallet = this), - onRenameClick = clickIntents::onRenameClick, + onRenameClick = clickIntents::onRenameBeforeConfirmationClick, onDeleteClick = clickIntents::onDeleteBeforeConfirmationClick, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletAlert.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletAlert.kt index 1edcb46df5..ab5bb8bff6 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletAlert.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletAlert.kt @@ -1,20 +1,26 @@ package com.tangem.feature.wallet.presentation.wallet.ui -import androidx.compose.runtime.Composable +import androidx.compose.runtime.* import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.TextFieldValue +import com.tangem.core.ui.components.AdditionalTextInputDialogParams import com.tangem.core.ui.components.BasicDialog import com.tangem.core.ui.components.DialogButton +import com.tangem.core.ui.components.TextInputDialog import com.tangem.core.ui.extensions.resolveReference import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.wallet.state.WalletAlertState @Composable internal fun WalletAlert(state: WalletAlertState, onDismiss: () -> Unit) { - DefaultAlert(state = state, onDismiss = onDismiss) + when (state) { + is WalletAlertState.Basic -> BasicAlert(state, onDismiss) + is WalletAlertState.TextInput -> TextInputAlert(state, onDismiss) + } } @Composable -private fun DefaultAlert(state: WalletAlertState, onDismiss: () -> Unit) { +private fun BasicAlert(state: WalletAlertState.Basic, onDismiss: () -> Unit) { val confirmButton: DialogButton val dismissButton: DialogButton? @@ -48,4 +54,28 @@ private fun DefaultAlert(state: WalletAlertState, onDismiss: () -> Unit) { title = state.title?.resolveReference(), dismissButton = dismissButton, ) +} + +@Composable +private fun TextInputAlert(state: WalletAlertState.TextInput, onDismiss: () -> Unit) { + var value by remember { mutableStateOf(TextFieldValue(text = state.text)) } + + TextInputDialog( + fieldValue = value, + confirmButton = DialogButton( + title = state.confirmButtonText.resolveReference(), + enabled = value.text.isNotEmpty() && value.text != state.text, + onClick = { + state.onConfirmClick(value.text) + onDismiss() + }, + ), + onDismissDialog = onDismiss, + onValueChange = { value = it }, + title = state.title.resolveReference(), + dismissButton = DialogButton(title = stringResource(id = R.string.common_cancel), onClick = onDismiss), + textFieldParams = AdditionalTextInputDialogParams( + label = state.label.resolveReference(), + ), + ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt index 345627ff8d..039920d7c4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt @@ -4,8 +4,11 @@ import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.compose.animation.* import androidx.compose.animation.core.tween -import androidx.compose.foundation.* +import androidx.compose.foundation.Image +import androidx.compose.foundation.LocalIndication +import androidx.compose.foundation.background import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.indication import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.PressInteraction import androidx.compose.foundation.layout.* @@ -31,13 +34,17 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider -import androidx.compose.ui.unit.* -import androidx.constraintlayout.compose.* +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.DpOffset +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.sp +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.ConstraintLayoutScope +import androidx.constraintlayout.compose.Dimension import com.tangem.common.Strings import com.tangem.core.ui.components.FontSizeRange import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.components.ResizableText -import com.tangem.core.ui.components.wallets.RenameWalletDialogContent import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemDimens @@ -61,9 +68,8 @@ private const val HALF_OF_ITEM_WIDTH = 0.5 internal fun WalletCard(state: WalletCardState, isBalanceHidden: Boolean, modifier: Modifier = Modifier) { @Suppress("DestructuringDeclarationWithTooManyEntries") CardContainer( - name = state.title, onDeleteClick = { state.onDeleteClick(state.id) }, - onRenameClick = { state.onRenameClick(state.id, it) }, + onRenameClick = { state.onRenameClick(state.id) }, isLockedState = state is WalletCardState.LockedContent, modifier = modifier, ) { itemSize -> @@ -141,9 +147,8 @@ internal fun WalletCard(state: WalletCardState, isBalanceHidden: Boolean, modifi @Composable private fun CardContainer( - name: String, onDeleteClick: () -> Unit, - onRenameClick: (String) -> Unit, + onRenameClick: () -> Unit, isLockedState: Boolean, modifier: Modifier = Modifier, content: @Composable (ConstraintLayoutScope.(IntSize) -> Unit), @@ -196,8 +201,6 @@ private fun CardContainer( } } - var isRenameWalletDialogVisible by rememberSaveable { mutableStateOf(value = false) } - val itemHeight by remember(itemSize.height) { mutableStateOf(value = with(density) { itemSize.height.toDp() }) } @@ -206,20 +209,9 @@ private fun CardContainer( pressOffset = pressOffset, itemHeight = itemHeight, onDismissRequest = { isMenuVisible = false }, - onShowRenameWalletDialogClick = { isRenameWalletDialogVisible = true }, + onShowRenameWalletDialogClick = onRenameClick, onDeleteClick = onDeleteClick, ) - - if (isRenameWalletDialogVisible) { - RenameWalletDialogContent( - name = name, - onConfirm = { - onRenameClick(it) - isRenameWalletDialogVisible = false - }, - onDismiss = { isRenameWalletDialogVisible = false }, - ) - } } @Suppress("LongParameterList") @@ -365,7 +357,7 @@ private fun AdditionalInfoText(text: TextReference) { } private fun Modifier.nonContentAdditionalInfoSize(dimens: TangemDimens): Modifier { - return size(width = dimens.size84, height = dimens.size16) + return this.size(width = dimens.size84, height = dimens.size16) } @Composable diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt index 5c390dcb0b..5b12a1cf6b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt @@ -41,7 +41,9 @@ internal interface WalletClickIntents { fun onTokenItemLongClick(cryptoCurrencyStatus: CryptoCurrencyStatus) - fun onRenameClick(userWalletId: UserWalletId, name: String) + fun onRenameBeforeConfirmationClick(userWalletId: UserWalletId) + + fun onRenameAfterConfirmationClick(userWalletId: UserWalletId, name: String) fun onDeleteBeforeConfirmationClick(userWalletId: UserWalletId) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index b1c839e9d0..c0a3cd5476 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -958,9 +958,23 @@ internal class WalletViewModel @Inject constructor( } } - override fun onRenameClick(userWalletId: UserWalletId, name: String) { + override fun onRenameBeforeConfirmationClick(userWalletId: UserWalletId) { + val state = uiState as? WalletState.ContentState ?: return + uiState = stateFactory.getStateAndTriggerEvent( + state = uiState, + event = WalletEvent.ShowAlert( + state = WalletAlertState.RenameWalletAlert( + text = state.walletsListConfig.wallets[state.walletsListConfig.selectedWalletIndex].title, + onConfirmClick = { onRenameAfterConfirmationClick(userWalletId, it) }, + ), + ), + setUiState = { uiState = it }, + ) + } + + override fun onRenameAfterConfirmationClick(userWalletId: UserWalletId, name: String) { viewModelScope.launch(dispatchers.io) { - updateWalletUseCase(userWalletId = userWalletId, update = { it.copy(name) }) + updateWalletUseCase(userWalletId = userWalletId, update = { it.copy(name = name) }) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCardClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCardClickIntents.kt index e1866a709c..e8c30c9aac 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCardClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCardClickIntents.kt @@ -20,7 +20,9 @@ import javax.inject.Inject internal interface WalletCardClickIntents { - fun onRenameClick(userWalletId: UserWalletId, name: String) + fun onRenameBeforeConfirmationClick(userWalletId: UserWalletId) + + fun onRenameAfterConfirmationClick(userWalletId: UserWalletId, name: String) fun onDeleteBeforeConfirmationClick(userWalletId: UserWalletId) @@ -39,15 +41,28 @@ internal class WalletCardClickIntentsImplementor @Inject constructor( private val dispatchers: CoroutineDispatcherProvider, ) : BaseWalletClickIntents(), WalletCardClickIntents { - override fun onRenameClick(userWalletId: UserWalletId, name: String) { + override fun onRenameBeforeConfirmationClick(userWalletId: UserWalletId) { analyticsEventHandler.send(MainScreen.EditWalletTapped) + walletEventSender.send( + event = WalletEvent.ShowAlert( + state = WalletAlertState.RenameWalletAlert( + text = stateHolder.getSelectedWallet().walletCardState.title, + onConfirmClick = { onRenameAfterConfirmationClick(userWalletId, it) }, + ), + ), + ) + } + + override fun onRenameAfterConfirmationClick(userWalletId: UserWalletId, name: String) { viewModelScope.launch(dispatchers.main) { - updateWalletUseCase(userWalletId = userWalletId, update = { it.copy(name) }) + updateWalletUseCase(userWalletId = userWalletId, update = { it.copy(name = name) }) } } override fun onDeleteBeforeConfirmationClick(userWalletId: UserWalletId) { + analyticsEventHandler.send(MainScreen.DeleteWalletTapped) + walletEventSender.send( event = WalletEvent.ShowAlert( state = WalletAlertState.RemoveWalletAlert( @@ -58,8 +73,6 @@ internal class WalletCardClickIntentsImplementor @Inject constructor( } override fun onDeleteAfterConfirmationClick(userWalletId: UserWalletId) { - analyticsEventHandler.send(MainScreen.DeleteWalletTapped) - viewModelScope.launch(dispatchers.main) { walletScreenContentLoader.cancel(userWalletId) deleteWalletUseCase(userWalletId) From b0cfaa4a86b6aa7226df4a56e9bfadc311dee52d Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 8 Feb 2024 16:02:12 +0300 Subject: [PATCH 09/17] Updated on 2026-08-14 --- .../wallet/viewmodels/intents/WalletWarningsClickIntents.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt index a9c4287571..0a32e8454a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt @@ -227,6 +227,8 @@ internal class WalletWarningsClickIntentsImplementer @Inject constructor( class DerivationData(val derivations: Pair>) override fun onOpenUnlockWalletsBottomSheetClick() { + analyticsEventHandler.send(MainScreen.WalletUnlockTapped) + val config = requireNotNull(stateHolder.getSelectedWallet().bottomSheetConfig) { "Impossible to open unlock wallet bottom sheet if it's null" } From 7e4dc3dbb42ef9a5705d626639e8d135091676aa Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 8 Feb 2024 21:37:02 +0300 Subject: [PATCH 10/17] Updated on 2026-08-14 --- .../wallet/redux/OnboardingWalletAction.kt | 1 + .../redux/OnboardingWalletMiddleware.kt | 3 +- .../wallet/redux/OnboardingWalletReducer.kt | 1 + .../wallet/redux/OnboardingWalletState.kt | 1 + .../wallet/ui/OnboardingWalletFragment.kt | 21 ++++++++++++-- .../layout_onboarding_buttons_add_cards.xml | 29 ++++++++++++++----- app/src/main/res/values/colors.xml | 1 - 7 files changed, 46 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt index ce2c8784a8..6b536fc754 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt @@ -65,6 +65,7 @@ sealed class BackupAction : Action { object StartAddingBackupCards : BackupAction() object AddBackupCard : BackupAction() { object Success : BackupAction() + data class ChangeButtonLoading(val isLoading: Boolean) : BackupAction() } object FinishAddingBackupCards : BackupAction() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index 3e602349e8..2a4ca101c4 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -395,11 +395,12 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) } } is BackupAction.AddBackupCard -> { + store.dispatchOnMain(BackupAction.AddBackupCard.ChangeButtonLoading(true)) backupService.addBackupCard { result -> backupService.skipCompatibilityChecks = false store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository).sdk .config.filter.cardIdFilter = null - + store.dispatchOnMain(BackupAction.AddBackupCard.ChangeButtonLoading(false)) when (result) { is CompletionResult.Success -> { store.dispatchOnMain(BackupAction.AddBackupCard.Success) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt index 1e6dc646b8..61bee3be0f 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt @@ -81,6 +81,7 @@ private object BackupReducer { BackupAction.StartAddingPrimaryCard -> state.copy(backupStep = BackupStep.ScanOriginCard) BackupAction.StartAddingBackupCards -> state.copy(backupStep = BackupStep.AddBackupCards) BackupAction.AddBackupCard.Success -> state.copy(backupCardsNumber = state.backupCardsNumber + 1) + is BackupAction.AddBackupCard.ChangeButtonLoading -> state.copy(showBtnLoading = action.isLoading) BackupAction.ShowAccessCodeInfoScreen -> state.copy(backupStep = BackupStep.SetAccessCode) BackupAction.ShowEnterAccessCodeScreen -> state.copy( backupStep = BackupStep.EnterAccessCode, diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt index 508e40db10..0920fb4a9b 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt @@ -69,6 +69,7 @@ data class BackupState( val maxBackupCards: Int = 2, val canSkipBackup: Boolean = true, val isInterruptedBackup: Boolean = false, + val showBtnLoading: Boolean = false, ) enum class AccessCodeError { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt index fa12a0f3d5..9a08513cdb 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt @@ -1,5 +1,6 @@ package com.tangem.tap.features.onboarding.products.wallet.ui +import android.content.res.ColorStateList import android.net.Uri import android.os.Bundle import android.util.TypedValue @@ -296,10 +297,26 @@ class OnboardingWalletFragment : layoutButtonsAddCards.root.show() layoutButtonsCommon.root.hide() - layoutButtonsAddCards.btnAddCard.text = getText(R.string.onboarding_button_add_backup_card) layoutButtonsAddCards.btnAddCard.setIconResource(R.drawable.ic_tangem_24) + if (state.showBtnLoading) { + layoutButtonsAddCards.btnAddCard.iconTint = ColorStateList.valueOf( + resources.getColor(R.color.button_secondary), + ) + layoutButtonsAddCards.btnAddCard.text = "" + layoutButtonsAddCards.btnAddCard.isEnabled = false + layoutButtonsAddCards.btnProgress.show() + } else { + layoutButtonsAddCards.btnAddCard.iconTint = ColorStateList.valueOf( + resources.getColor(R.color.icon_primary_1), + ) + layoutButtonsAddCards.btnProgress.hide() + layoutButtonsAddCards.btnAddCard.text = getText(R.string.onboarding_button_add_backup_card) + layoutButtonsAddCards.btnAddCard.isEnabled = true + } if (state.backupCardsNumber < state.maxBackupCards) { - layoutButtonsAddCards.btnAddCard.setOnClickListener { store.dispatch(BackupAction.AddBackupCard) } + layoutButtonsAddCards.btnAddCard.setOnClickListener { + store.dispatch(BackupAction.AddBackupCard) + } } else { layoutButtonsAddCards.btnAddCard.isEnabled = false } diff --git a/app/src/main/res/layout/layout_onboarding_buttons_add_cards.xml b/app/src/main/res/layout/layout_onboarding_buttons_add_cards.xml index 505d485b6f..a6fd01cc5b 100644 --- a/app/src/main/res/layout/layout_onboarding_buttons_add_cards.xml +++ b/app/src/main/res/layout/layout_onboarding_buttons_add_cards.xml @@ -5,22 +5,37 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - + app:layout_constraintStart_toStartOf="@id/btn_continue"> + + + + + #000000 #00000000 - d #FFFFFF #F0FFFF From 8f7f7c85ca530acdd7dfbf83b1c58dfd167435c3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 8 Feb 2024 22:00:38 +0300 Subject: [PATCH 11/17] Updated on 2026-08-14 --- .../domain/tokens/GetCurrencyWarningsUseCase.kt | 3 ++- .../wallet/domain/GetMultiWalletWarningsFactory.kt | 11 ++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt index 3e5b6a3ed4..f8f9049833 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt @@ -84,11 +84,12 @@ class GetCurrencyWarningsUseCase( ): Flow { val currency = currencyStatus.currency val cryptoStatuses = operations.getCurrenciesStatusesSync() + val promoBanner = promoRepository.getChangellyPromoBanner() return combine( showSwapPromoTokenUseCase().conflate(), flowOf(marketCryptoCurrencyRepository.isExchangeable(userWalletId, currency)).conflate(), ) { shouldShowSwapPromo, isExchangeable -> - val promoBanner = promoRepository.getChangellyPromoBanner() ?: return@combine null + promoBanner ?: return@combine null val showPromo = promoBanner.isActive && shouldShowSwapPromo if (showPromo && isExchangeable && currencyStatus.value !is CryptoCurrencyStatus.Unreachable) { cryptoStatuses.fold( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt index e4bec421bb..281f54a762 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt @@ -22,10 +22,7 @@ import dagger.hilt.android.scopes.ViewModelScoped import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.conflate -import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.* import timber.log.Timber import javax.inject.Inject @@ -54,14 +51,14 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( val cardTypesResolver = userWallet.scanResponse.cardTypesResolver + val promoFlow = flow { emit(promoRepository.getChangellyPromoBanner()) } return combine( flow = getTokenListUseCase(userWallet.walletId).conflate(), flow2 = isReadyToShowRateAppUseCase().conflate(), flow3 = isNeedToBackupUseCase(userWallet.walletId).conflate(), flow4 = shouldShowSwapPromoWalletUseCase().conflate(), - ) { maybeTokenList, isReadyToShowRating, isNeedToBackup, shouldShowPromo -> - - val promoBanner = promoRepository.getChangellyPromoBanner() + flow5 = promoFlow, + ) { maybeTokenList, isReadyToShowRating, isNeedToBackup, shouldShowPromo, promoBanner -> readyForRateAppNotification = true buildList { From 8d5c607bcfe355d2bbc47c0f9dddfac516124a00 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 8 Feb 2024 20:40:12 +0300 Subject: [PATCH 12/17] Updated on 2026-08-14 --- .../components/buttons/common/TangemButton.kt | 8 +++++- .../core/ui/utils/MultipleClickPreventer.kt | 27 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 core/ui/src/main/java/com/tangem/core/ui/utils/MultipleClickPreventer.kt diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt index ae762b2803..b603622d2f 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.composed @@ -17,6 +18,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.sp import com.tangem.core.ui.components.ResizableText import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.utils.MultipleClickPreventer @Suppress("LongParameterList") @Composable @@ -34,9 +36,13 @@ fun TangemButton( textStyle: TextStyle = TangemTheme.typography.button, shape: Shape = size.toShape(), ) { + val multipleClickPreventer = remember { MultipleClickPreventer.get() } + Button( modifier = modifier.heightIn(min = size.toHeightDp()), - onClick = { if (!showProgress) onClick() }, + onClick = { + multipleClickPreventer.processEvent { if (!showProgress) onClick() } + }, enabled = enabled, elevation = elevation, shape = shape, diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/MultipleClickPreventer.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/MultipleClickPreventer.kt new file mode 100644 index 0000000000..15ede204ae --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/utils/MultipleClickPreventer.kt @@ -0,0 +1,27 @@ +package com.tangem.core.ui.utils + +import android.os.SystemClock + +interface MultipleClickPreventer { + + fun processEvent(event: () -> Unit) + + companion object { + + fun get(): MultipleClickPreventer = DefaultMultipleClickPreventer() + } +} + +private class DefaultMultipleClickPreventer : MultipleClickPreventer { + private val now: Long get() = SystemClock.elapsedRealtime() + private var lastEventTimeMs: Long = 0 + + override fun processEvent(event: () -> Unit) { + if (now - lastEventTimeMs >= TIME_BETWEEN_CLICK_IN_MS) event() + lastEventTimeMs = now + } + + companion object { + private const val TIME_BETWEEN_CLICK_IN_MS = 500L + } +} \ No newline at end of file From 2a641624a20a9ace9e28f6ee1e5cb06e461d48c7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Feb 2024 11:00:48 +0300 Subject: [PATCH 13/17] Updated on 2026-08-14 --- .../tap/domain/card/DefaultDerivationsRepositoryTest.kt | 2 +- .../tangem/tap/domain/card/MissedDerivationsFinderTest.kt | 6 +++--- .../com/tangem/tap/domain/card/ScanResponseMockFactory.kt | 2 +- .../com/tangem/domain/common/configs/GenericCardConfig.kt | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/test/kotlin/com/tangem/tap/domain/card/DefaultDerivationsRepositoryTest.kt b/app/src/test/kotlin/com/tangem/tap/domain/card/DefaultDerivationsRepositoryTest.kt index e818b118ab..730ddb8d96 100644 --- a/app/src/test/kotlin/com/tangem/tap/domain/card/DefaultDerivationsRepositoryTest.kt +++ b/app/src/test/kotlin/com/tangem/tap/domain/card/DefaultDerivationsRepositoryTest.kt @@ -36,7 +36,7 @@ internal class DefaultDerivationsRepositoryTest { artworkUrl = "", cardsInWallet = setOf(), isMultiCurrency = false, - scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig, derivedKeys = emptyMap()), + scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap()), ) @Test diff --git a/app/src/test/kotlin/com/tangem/tap/domain/card/MissedDerivationsFinderTest.kt b/app/src/test/kotlin/com/tangem/tap/domain/card/MissedDerivationsFinderTest.kt index 2506e539a5..7091e26f4f 100644 --- a/app/src/test/kotlin/com/tangem/tap/domain/card/MissedDerivationsFinderTest.kt +++ b/app/src/test/kotlin/com/tangem/tap/domain/card/MissedDerivationsFinderTest.kt @@ -20,7 +20,7 @@ internal class MissedDerivationsFinderTest { @Test fun `empty derivations for empty currencies`() { - val scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig, derivedKeys = emptyMap()) + val scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap()) val finder = MissedDerivationsFinder(scanResponse) val actual = finder.find(emptyList()) @@ -31,7 +31,7 @@ internal class MissedDerivationsFinderTest { @Test fun `empty derivations for non supported blockchains`() { // Bls is not supported - val scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig, derivedKeys = emptyMap()) + val scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap()) val finder = MissedDerivationsFinder(scanResponse) val currencies = CryptoCurrenciesMocks(scanResponse).chia @@ -44,7 +44,7 @@ internal class MissedDerivationsFinderTest { fun `derivations ONLY for supported blockchains`() { // Bls is not supported val scanResponse = ScanResponseMockFactory.create( - cardConfig = GenericCardConfig, + cardConfig = GenericCardConfig(2), derivedKeys = emptyMap(), ).let { it.copy( diff --git a/app/src/test/kotlin/com/tangem/tap/domain/card/ScanResponseMockFactory.kt b/app/src/test/kotlin/com/tangem/tap/domain/card/ScanResponseMockFactory.kt index 359cde99df..71f1514668 100644 --- a/app/src/test/kotlin/com/tangem/tap/domain/card/ScanResponseMockFactory.kt +++ b/app/src/test/kotlin/com/tangem/tap/domain/card/ScanResponseMockFactory.kt @@ -47,7 +47,7 @@ object ScanResponseMockFactory { batchId = "NEVER-MIND", cardPublicKey = ByteArray(0), firmwareVersion = when (cardConfig) { - GenericCardConfig -> genericFirmwareVersion + is GenericCardConfig -> genericFirmwareVersion MultiWalletCardConfig -> walletFirmwareVersion Wallet2CardConfig -> wallet2FirmwareVersion }, diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/configs/GenericCardConfig.kt b/domain/legacy/src/main/java/com/tangem/domain/common/configs/GenericCardConfig.kt index 547bfe780d..e75ff6da09 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/configs/GenericCardConfig.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/configs/GenericCardConfig.kt @@ -4,7 +4,7 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.common.card.EllipticCurve import timber.log.Timber -internal class GenericCardConfig(maxWalletCount: Int) : CardConfig { +class GenericCardConfig(maxWalletCount: Int) : CardConfig { override val mandatoryCurves: List = buildList { add(EllipticCurve.Secp256k1) From 31e702e5e4e6e062840ce5078064aa3f1f646731 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Feb 2024 11:44:26 +0300 Subject: [PATCH 14/17] Updated on 2026-08-14 --- .../src/main/java/com/tangem/data/promo/di/PromoDataModule.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data/promo/src/main/java/com/tangem/data/promo/di/PromoDataModule.kt b/data/promo/src/main/java/com/tangem/data/promo/di/PromoDataModule.kt index c094043a9c..5ed2c6855f 100644 --- a/data/promo/src/main/java/com/tangem/data/promo/di/PromoDataModule.kt +++ b/data/promo/src/main/java/com/tangem/data/promo/di/PromoDataModule.kt @@ -2,7 +2,6 @@ package com.tangem.data.promo.di import com.tangem.data.promo.DefaultPromoRepository import com.tangem.datasource.api.tangemTech.TangemTechApi -import com.tangem.datasource.di.DevTangemApi import com.tangem.domain.tokens.repository.PromoRepository import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module @@ -18,7 +17,7 @@ internal object PromoDataModule { @Provides @Singleton fun providePromoRepository( - @DevTangemApi tangemTechApi: TangemTechApi, + tangemTechApi: TangemTechApi, dispatchers: CoroutineDispatcherProvider, ): PromoRepository { return DefaultPromoRepository(tangemTechApi, dispatchers) From b3a1dc5518d04a92a4523c6a35ef0fb85bd722f4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Feb 2024 15:50:51 +0300 Subject: [PATCH 15/17] Updated on 2026-08-14 --- .../domain/card/ScanResponseMockFactory.kt | 6 ++--- .../domain/common/configs/CardConfig.kt | 5 +++++ .../configs/EdSingleCurrencyCardConfig.kt | 22 +++++++++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 domain/legacy/src/main/java/com/tangem/domain/common/configs/EdSingleCurrencyCardConfig.kt diff --git a/app/src/test/kotlin/com/tangem/tap/domain/card/ScanResponseMockFactory.kt b/app/src/test/kotlin/com/tangem/tap/domain/card/ScanResponseMockFactory.kt index 71f1514668..da79e800f0 100644 --- a/app/src/test/kotlin/com/tangem/tap/domain/card/ScanResponseMockFactory.kt +++ b/app/src/test/kotlin/com/tangem/tap/domain/card/ScanResponseMockFactory.kt @@ -2,10 +2,7 @@ package com.tangem.tap.domain.card import com.tangem.common.card.CardWallet import com.tangem.common.card.FirmwareVersion -import com.tangem.domain.common.configs.CardConfig -import com.tangem.domain.common.configs.GenericCardConfig -import com.tangem.domain.common.configs.MultiWalletCardConfig -import com.tangem.domain.common.configs.Wallet2CardConfig +import com.tangem.domain.common.configs.* import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.KeyWalletPublicKey import com.tangem.domain.models.scan.ProductType @@ -50,6 +47,7 @@ object ScanResponseMockFactory { is GenericCardConfig -> genericFirmwareVersion MultiWalletCardConfig -> walletFirmwareVersion Wallet2CardConfig -> wallet2FirmwareVersion + EdSingleCurrencyCardConfig -> genericFirmwareVersion }, manufacturer = CardDTO.Manufacturer(name = "NEVER-MIND", manufactureDate = Date(), signature = null), issuer = CardDTO.Issuer(name = "NEVER-MIND", publicKey = ByteArray(0)), diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt b/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt index 7e3fc92045..8099ff7d09 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt @@ -22,6 +22,11 @@ sealed interface CardConfig { ) { return MultiWalletCardConfig } + if (cardDTO.supportedCurves.size == 1 && + cardDTO.supportedCurves.contains(EllipticCurve.Ed25519) + ) { + return EdSingleCurrencyCardConfig + } return GenericCardConfig(cardDTO.settings.maxWalletsCount) } } diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/configs/EdSingleCurrencyCardConfig.kt b/domain/legacy/src/main/java/com/tangem/domain/common/configs/EdSingleCurrencyCardConfig.kt new file mode 100644 index 0000000000..6792eeff3e --- /dev/null +++ b/domain/legacy/src/main/java/com/tangem/domain/common/configs/EdSingleCurrencyCardConfig.kt @@ -0,0 +1,22 @@ +package com.tangem.domain.common.configs + +import com.tangem.blockchain.common.Blockchain +import com.tangem.common.card.EllipticCurve +import timber.log.Timber + +object EdSingleCurrencyCardConfig : CardConfig { + + override val mandatoryCurves: List = listOf(EllipticCurve.Ed25519) + + override fun primaryCurve(blockchain: Blockchain): EllipticCurve? { + return when { + blockchain.getSupportedCurves().contains(EllipticCurve.Ed25519) -> { + EllipticCurve.Ed25519 + } + else -> { + Timber.e("Unsupported blockchain, curve not found") + null + } + } + } +} \ No newline at end of file From 2d975f74e549262637c1d01ef6f4b9a42c23ebe1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Feb 2024 16:31:24 +0300 Subject: [PATCH 16/17] Updated on 2026-08-14 --- .../presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt index 79fef13194..2717ca2dfa 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt @@ -61,7 +61,7 @@ sealed class WalletScreenAnalyticsEvent { object NoticeBackupYourWalletTapped : MainScreen(event = "Notice - Backup Your Wallet Tapped") object NoticeScanYourCardTapped : MainScreen(event = "Notice - Scan Your Card Tapped") object NoticeWalletLocked : MainScreen(event = "Notice - Wallet Locked") - object WalletUnlockTapped : MainScreen(event = "Button - Wallet Unlock Tapped") + object WalletUnlockTapped : MainScreen(event = "Notice - Wallet Unlock Tapped") object NetworksUnreachable : MainScreen(event = "Notice - Networks Unreachable") From 5749d33c0ec11ac86a36bd1833198f1cc9ad8f4b Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Feb 2024 17:23:06 +0300 Subject: [PATCH 17/17] Updated on 2026-08-14 --- .../state/TokenDetailsTopAppBarConfig.kt | 2 +- .../state/factory/TokenDetailsStateFactory.kt | 32 +++++++++++++++++++ .../ui/components/TokenDetailsTopAppBar.kt | 20 ++++++++---- .../viewmodels/TokenDetailsViewModel.kt | 9 ++++++ 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsTopAppBarConfig.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsTopAppBarConfig.kt index 88ecf53046..f72f09f20f 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsTopAppBarConfig.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsTopAppBarConfig.kt @@ -2,5 +2,5 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state internal data class TokenDetailsTopAppBarConfig( val onBackClick: () -> Unit, - val tokenDetailsAppBarMenuConfig: TokenDetailsAppBarMenuConfig, + val tokenDetailsAppBarMenuConfig: TokenDetailsAppBarMenuConfig?, ) \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt index b4b567a444..c0e14ea621 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt @@ -11,7 +11,9 @@ import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.event.consumedEvent import com.tangem.core.ui.event.triggeredEvent import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.res.TangemTheme import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.tokens.error.CurrencyStatusError import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus @@ -22,6 +24,7 @@ import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.domain.txhistory.models.TxHistoryStateError import com.tangem.feature.tokendetails.presentation.tokendetails.state.SwapTransactionsState +import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsAppBarMenuConfig import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsDialogConfig import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadedTxHistoryConverter @@ -29,7 +32,9 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.t import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadingTxHistoryConverter.TokenDetailsLoadingTxHistoryModel import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.exchange.ExchangeStatusBottomSheetConfig import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents +import com.tangem.features.tokendetails.impl.R import com.tangem.utils.Provider +import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -276,4 +281,31 @@ internal class TokenDetailsStateFactory( ), ) } + + fun getStateWithUpdatedMenu(cardTypesResolver: CardTypesResolver): TokenDetailsState { + return with(currentStateProvider()) { + copy( + topAppBarConfig = topAppBarConfig.copy( + tokenDetailsAppBarMenuConfig = topAppBarConfig.tokenDetailsAppBarMenuConfig + ?.updateMenu(cardTypesResolver), + ), + ) + } + } + + private fun TokenDetailsAppBarMenuConfig.updateMenu( + cardTypesResolver: CardTypesResolver, + ): TokenDetailsAppBarMenuConfig? { + if (cardTypesResolver.isSingleWalletWithToken()) return null + + return copy( + items = persistentListOf( + TokenDetailsAppBarMenuConfig.MenuItem( + title = TextReference.Res(id = R.string.token_details_hide_token), + textColorProvider = { TangemTheme.colors.text.warning }, + onClick = clickIntents::onHideClick, + ), + ), + ) + } } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsTopAppBar.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsTopAppBar.kt index b5e0839679..1ed6603ba3 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsTopAppBar.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsTopAppBar.kt @@ -1,5 +1,6 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.padding @@ -38,12 +39,17 @@ internal fun TokenDetailsTopAppBar(config: TokenDetailsTopAppBarConfig) { }, title = {}, actions = { - IconButton(onClick = { showDropdownMenu = true }) { - Icon( - painter = painterResource(id = R.drawable.ic_more_vertical_24), - tint = TangemTheme.colors.icon.primary1, - contentDescription = "More", - ) + AnimatedVisibility( + visible = config.tokenDetailsAppBarMenuConfig != null && + config.tokenDetailsAppBarMenuConfig.items.isNotEmpty(), + ) { + IconButton(onClick = { showDropdownMenu = true }) { + Icon( + painter = painterResource(id = R.drawable.ic_more_vertical_24), + tint = TangemTheme.colors.icon.primary1, + contentDescription = "More", + ) + } } TangemDropdownMenu( @@ -52,7 +58,7 @@ internal fun TokenDetailsTopAppBar(config: TokenDetailsTopAppBarConfig) { onDismissRequest = { showDropdownMenu = false }, offset = DpOffset(x = TangemTheme.dimens.spacing20, y = TangemTheme.dimens.spacing10.times(-1)), content = { - config.tokenDetailsAppBarMenuConfig.items.fastForEach { + config.tokenDetailsAppBarMenuConfig?.items?.fastForEach { AppBarDropdownItem( item = it, dismissParent = { showDropdownMenu = false }, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 1fba010ecc..382d9ca76e 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -185,6 +185,7 @@ internal class TokenDetailsViewModel @Inject constructor( analyticsEventsHandler.send( event = TokenScreenAnalyticsEvent.DetailsScreenOpened(token = cryptoCurrency.symbol), ) + updateTopBarMenu() updateContent() handleBalanceHiding(owner) } @@ -339,6 +340,14 @@ internal class TokenDetailsViewModel @Inject constructor( } } + private fun updateTopBarMenu() { + viewModelScope.launch(dispatchers.main) { + val wallet = getUserWalletUseCase(userWalletId).getOrElse { return@launch } + + uiState = stateFactory.getStateWithUpdatedMenu(wallet.scanResponse.cardTypesResolver) + } + } + private fun createSelectedAppCurrencyFlow(): StateFlow { return getSelectedAppCurrencyUseCase() .map { maybeAppCurrency ->