From 1964b3cfad1749cd649b7ff360b7c3438c55783d Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 27 Apr 2026 21:10:26 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../com/tangem/common/TangemBlogUrlBuilder.kt | 24 +++-- .../com/tangem/utils/SupportedLanguages.kt | 11 ++- .../com/tangem/utils/TangemBlogUrlBuilder.kt | 28 ------ .../tangem/utils/SupportedLanguagesTest.kt | 94 +++++++++++++++++++ .../common/locale/DefaultLocaleProvider.kt | 28 ------ .../data/common/locale/LocaleProvider.kt | 13 --- .../common/locale/di/LocaleProviderModule.kt | 20 ---- .../approval/impl/model/GiveApprovalModel.kt | 19 +--- .../features/details/model/DetailsModel.kt | 13 +-- .../impl/presentation/model/StakingModel.kt | 11 ++- .../model/StakingModelNavigationTest.kt | 17 +++- .../tangem/feature/swap/model/SwapModel.kt | 17 ++-- .../tangem/feature/swap/ui/StateBuilder.kt | 4 +- .../model/ExpressTransactionsModel.kt | 7 ++ .../model/TokenDetailsClickIntents.kt | 2 + .../tokendetails/model/TokenDetailsModel.kt | 7 ++ ...enDetailsSwapTransactionsStateConverter.kt | 11 +-- .../model/WcSendTransactionModel.kt | 12 +-- .../active/model/YieldSupplyActiveModel.kt | 8 +- .../impl/promo/model/YieldSupplyPromoModel.kt | 19 ++-- .../approve/model/YieldSupplyApproveModel.kt | 6 +- .../model/YieldSupplyStopEarningModel.kt | 6 +- 22 files changed, 204 insertions(+), 173 deletions(-) delete mode 100644 core/utils/src/main/java/com/tangem/utils/TangemBlogUrlBuilder.kt create mode 100644 core/utils/src/test/kotlin/com/tangem/utils/SupportedLanguagesTest.kt delete mode 100644 data/common/src/main/kotlin/com/tangem/data/common/locale/DefaultLocaleProvider.kt delete mode 100644 data/common/src/main/kotlin/com/tangem/data/common/locale/LocaleProvider.kt delete mode 100644 data/common/src/main/kotlin/com/tangem/data/common/locale/di/LocaleProviderModule.kt diff --git a/common/src/main/kotlin/com/tangem/common/TangemBlogUrlBuilder.kt b/common/src/main/kotlin/com/tangem/common/TangemBlogUrlBuilder.kt index b62a7c78ce..28c76f1551 100644 --- a/common/src/main/kotlin/com/tangem/common/TangemBlogUrlBuilder.kt +++ b/common/src/main/kotlin/com/tangem/common/TangemBlogUrlBuilder.kt @@ -16,14 +16,6 @@ object TangemBlogUrlBuilder { val path: String - data object SeedNotify : Post { - override val path: String = "seed-notify" - } - - data object SeedNotifySecond : Post { - override val path: String = "tangem-resolves-log-issue" - } - data object SeedPhraseRiskySolution : Post { override val path: String = "seed-phrase-faq" } @@ -39,5 +31,21 @@ object TangemBlogUrlBuilder { data object HowToScan : Post { override val path: String = "scan-tangem-card" } + + data object HowToStake : Post { + override val path: String = "how-to-stake-cryptocurrency" + } + + data object GiveRevokePermission : Post { + override val path: String = "give-revoke-permission" + } + + data object HowYieldModeWorks : Post { + override val path: String = "yield-mode" + } + + data object AboutCrossChainBridges : Post { + override val path: String = "an-overview-of-cross-chain-bridges" + } } } \ No newline at end of file diff --git a/core/utils/src/main/java/com/tangem/utils/SupportedLanguages.kt b/core/utils/src/main/java/com/tangem/utils/SupportedLanguages.kt index 420547d13c..07fc6985b2 100644 --- a/core/utils/src/main/java/com/tangem/utils/SupportedLanguages.kt +++ b/core/utils/src/main/java/com/tangem/utils/SupportedLanguages.kt @@ -13,7 +13,7 @@ object SupportedLanguages { const val CHINESE = "zh" const val SPANISH = "es" - val supportedLangugeCodes = listOf( + val supportedLanguageCodes = listOf( ENGLISH, RUSSIAN, GERMAN, @@ -25,10 +25,17 @@ object SupportedLanguages { SPANISH, ) + /** + * Returns the ISO 639-1 code of the device's current language when it belongs to + * [supportedLanguageCodes], otherwise falls back to [ENGLISH]. + * + * Intended for callers that need a plain two-letter language code (e.g. URL path segments + * like `tangem.com/{en|ru}/...`). + */ fun getCurrentSupportedLanguageCode(): String { val locale = Locale.getDefault() - return if (supportedLangugeCodes.contains(locale.language)) { + return if (supportedLanguageCodes.contains(locale.language)) { locale.language } else { ENGLISH diff --git a/core/utils/src/main/java/com/tangem/utils/TangemBlogUrlBuilder.kt b/core/utils/src/main/java/com/tangem/utils/TangemBlogUrlBuilder.kt deleted file mode 100644 index d24267a527..0000000000 --- a/core/utils/src/main/java/com/tangem/utils/TangemBlogUrlBuilder.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.tangem.utils - -import java.util.Locale - -@Deprecated("Use TangemBlogUrlBuilder from common module") -object TangemBlogUrlBuilder { - - private const val RU_LOCALE = "ru" - private const val EN_LOCALE = "en" - - private const val TANGEM_MAIN = "https://tangem.com/" - - val FEE_BLOG_LINK: String - get(): String { - val locale = if (Locale.getDefault().language == RU_LOCALE) RU_LOCALE else EN_LOCALE - return buildString { - append(TANGEM_MAIN) - append(locale) - append("/blog/post/what-is-a-transaction-fee-and-why-do-we-need-it/") - } - } - - const val RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP = "https://tangem.com/en/blog/post/give-revoke-permission/" - - const val YIELD_SUPPLY_HOW_IT_WORKS_URL = "https://tangem.com/en/blog/post/yield-mode" - const val YIELD_SUPPLY_TOS_URL = "https://aave.com/terms-of-service" - const val YIELD_SUPPLY_PRIVACY_URL = "https://aave.com/privacy-policy" -} \ No newline at end of file diff --git a/core/utils/src/test/kotlin/com/tangem/utils/SupportedLanguagesTest.kt b/core/utils/src/test/kotlin/com/tangem/utils/SupportedLanguagesTest.kt new file mode 100644 index 0000000000..3e3729e3f8 --- /dev/null +++ b/core/utils/src/test/kotlin/com/tangem/utils/SupportedLanguagesTest.kt @@ -0,0 +1,94 @@ +package com.tangem.utils + +import com.google.common.truth.Truth +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import java.util.Locale + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class SupportedLanguagesTest { + + private lateinit var originalLocale: Locale + + @BeforeEach + fun setUp() { + originalLocale = Locale.getDefault() + } + + @AfterEach + fun tearDown() { + Locale.setDefault(originalLocale) + } + + @Test + fun `getCurrentSupportedLanguageCode returns primary language when locale is supported`() { + // Arrange + Locale.setDefault(Locale("en", "US")) + + // Act + val actual = SupportedLanguages.getCurrentSupportedLanguageCode() + + // Assert + Truth.assertThat(actual).isEqualTo("en") + } + + @Test + fun `getCurrentSupportedLanguageCode drops region for supported language`() { + // Arrange + Locale.setDefault(Locale("zh", "CN")) + + // Act + val actual = SupportedLanguages.getCurrentSupportedLanguageCode() + + // Assert + Truth.assertThat(actual).isEqualTo("zh") + } + + @Test + fun `getCurrentSupportedLanguageCode returns ENGLISH when locale is not supported`() { + // Arrange — pt (Portuguese) is not in supportedLanguageCodes + Locale.setDefault(Locale("pt", "BR")) + + // Act + val actual = SupportedLanguages.getCurrentSupportedLanguageCode() + + // Assert + Truth.assertThat(actual).isEqualTo(SupportedLanguages.ENGLISH) + } + + @Test + fun `getCurrentSupportedLanguageCode returns ENGLISH for empty language`() { + // Arrange + Locale.setDefault(Locale("", "")) + + // Act + val actual = SupportedLanguages.getCurrentSupportedLanguageCode() + + // Assert + Truth.assertThat(actual).isEqualTo(SupportedLanguages.ENGLISH) + } + + @Test + fun `getCurrentSupportedLanguageCode supports every code in supportedLanguageCodes`() { + SupportedLanguages.supportedLanguageCodes.forEach { code -> + // Arrange + Locale.setDefault(Locale(code)) + + // Act + val actual = SupportedLanguages.getCurrentSupportedLanguageCode() + + // Assert + Truth.assertThat(actual).isEqualTo(code) + } + } + + @Test + fun `supportedLanguageCodes contains the expected nine ISO 639-1 codes`() { + // Assert + Truth.assertThat(SupportedLanguages.supportedLanguageCodes) + .containsExactly("en", "ru", "de", "fr", "it", "ja", "uk", "zh", "es") + .inOrder() + } +} \ No newline at end of file diff --git a/data/common/src/main/kotlin/com/tangem/data/common/locale/DefaultLocaleProvider.kt b/data/common/src/main/kotlin/com/tangem/data/common/locale/DefaultLocaleProvider.kt deleted file mode 100644 index 55cdf424ec..0000000000 --- a/data/common/src/main/kotlin/com/tangem/data/common/locale/DefaultLocaleProvider.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.tangem.data.common.locale - -import java.util.Locale - -/** -[REDACTED_AUTHOR] - */ -internal class DefaultLocaleProvider : LocaleProvider { - - override fun getLocale(): Locale { - return Locale.getDefault() - } - - override fun getWebUriLocaleLanguage(): String { - val language = getLocale().language - return if (LOCALE_LANG_RU.equals(language, true) || LOCALE_LANG_BY.equals(language, true)) { - LOCALE_LANG_RU - } else { - LOCALE_LANG_EN - } - } - - companion object { - const val LOCALE_LANG_RU = "ru" - const val LOCALE_LANG_BY = "by" - const val LOCALE_LANG_EN = "en" - } -} \ No newline at end of file diff --git a/data/common/src/main/kotlin/com/tangem/data/common/locale/LocaleProvider.kt b/data/common/src/main/kotlin/com/tangem/data/common/locale/LocaleProvider.kt deleted file mode 100644 index 5f52c75915..0000000000 --- a/data/common/src/main/kotlin/com/tangem/data/common/locale/LocaleProvider.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.tangem.data.common.locale - -import java.util.Locale - -/** -[REDACTED_AUTHOR] - */ -interface LocaleProvider { - - fun getLocale(): Locale - - fun getWebUriLocaleLanguage(): String -} \ No newline at end of file diff --git a/data/common/src/main/kotlin/com/tangem/data/common/locale/di/LocaleProviderModule.kt b/data/common/src/main/kotlin/com/tangem/data/common/locale/di/LocaleProviderModule.kt deleted file mode 100644 index 656c6ed529..0000000000 --- a/data/common/src/main/kotlin/com/tangem/data/common/locale/di/LocaleProviderModule.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.tangem.data.common.locale.di - -import com.tangem.data.common.locale.DefaultLocaleProvider -import com.tangem.data.common.locale.LocaleProvider -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 LocaleProviderModule { - - @Provides - @Singleton - fun provideCacheRegistry(): LocaleProvider { - return DefaultLocaleProvider() - } -} \ No newline at end of file diff --git a/features/approval/impl/src/main/java/com/tangem/features/approval/impl/model/GiveApprovalModel.kt b/features/approval/impl/src/main/java/com/tangem/features/approval/impl/model/GiveApprovalModel.kt index 727c03292f..1cecfc51ea 100644 --- a/features/approval/impl/src/main/java/com/tangem/features/approval/impl/model/GiveApprovalModel.kt +++ b/features/approval/impl/src/main/java/com/tangem/features/approval/impl/model/GiveApprovalModel.kt @@ -8,6 +8,7 @@ import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.TransactionSender.MultipleTransactionSendMode import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee +import com.tangem.common.TangemBlogUrlBuilder import com.tangem.common.ui.bottomsheet.permission.state.ApproveType import com.tangem.common.ui.userwallet.ext.walletInterationIcon import com.tangem.core.analytics.api.AnalyticsEventHandler @@ -16,10 +17,7 @@ import com.tangem.core.analytics.models.Basic import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer -import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.navigation.url.UrlOpener -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.message.DialogMessage import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.transaction.error.GetFeeError @@ -37,7 +35,6 @@ import com.tangem.features.approval.api.GiveApprovalComponent import com.tangem.features.send.v2.api.callbacks.FeeSelectorModelCallback import com.tangem.features.send.v2.api.entity.FeeItem import com.tangem.features.send.v2.api.entity.FeeSelectorUM -import com.tangem.utils.TangemBlogUrlBuilder.RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.logging.TangemLogger import kotlinx.coroutines.flow.MutableStateFlow @@ -61,7 +58,6 @@ internal class GiveApprovalModel @Inject constructor( private val getFeeForGaslessUseCase: GetFeeForGaslessUseCase, private val getFeeForTokenUseCase: GetFeeForTokenUseCase, private val createAndSendGaslessTransactionUseCase: CreateAndSendGaslessTransactionUseCase, - private val uiMessageSender: UiMessageSender, private val urlOpener: UrlOpener, private val getUserWalletUseCase: GetUserWalletUseCase, private val analyticsEventHandler: AnalyticsEventHandler, @@ -119,16 +115,9 @@ internal class GiveApprovalModel @Inject constructor( } fun onOpenLearnMoreAboutApproveClick() { - urlOpener.openUrl(RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP) - } - - fun showPermissionInfoDialog() { - uiMessageSender.send( - DialogMessage( - message = resourceReference(com.tangem.common.ui.R.string.give_permission_staking_footer), - title = resourceReference(com.tangem.common.ui.R.string.common_approve), - ), - ) + modelScope.launch { + urlOpener.openUrl(TangemBlogUrlBuilder.build(TangemBlogUrlBuilder.Post.GiveRevokePermission)) + } } suspend fun loadFee(): Either { diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/model/DetailsModel.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/model/DetailsModel.kt index 394aa04245..840d7776a1 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/model/DetailsModel.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/model/DetailsModel.kt @@ -1,8 +1,6 @@ package com.tangem.features.details.model -import android.content.res.Resources import arrow.core.getOrElse -import com.tangem.utils.logging.TangemLogger import com.tangem.common.routing.AppRoute import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsParam @@ -37,6 +35,7 @@ import com.tangem.features.details.utils.ItemsBuilder import com.tangem.features.details.utils.SocialsBuilder import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.info.AppInfoProvider +import com.tangem.utils.logging.TangemLogger import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.launchIn @@ -44,7 +43,6 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import java.util.Locale import javax.inject.Inject @ModelScoped @@ -270,13 +268,4 @@ internal class DetailsModel @Inject constructor( } private fun getAppVersion(): String = "${appInfoProvider.appVersion} (${appInfoProvider.appVersionCode})" - - private companion object { - val SYSTEM_LANGUAGE = runCatching { Resources.getSystem().configuration.locales[0].language }.getOrElse { "" } - val APP_LANGUAGE = Locale.getDefault().language - val UTM_MARKS = "utm_source=tangem-app" + - "&utm_medium=app" + - "&utm_campaign=users-$SYSTEM_LANGUAGE" + - "&utm_content=devicelang-$APP_LANGUAGE" - } } \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt index f3fbf4671e..0ed6ee204a 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt @@ -8,6 +8,7 @@ import com.arkivanov.decompose.router.slot.dismiss import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee +import com.tangem.common.TangemBlogUrlBuilder import com.tangem.common.getValidatorsCount import com.tangem.common.routing.AppRouter import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer.ReduceByData @@ -95,7 +96,6 @@ import com.tangem.features.staking.impl.presentation.state.utils.isSingleAction import com.tangem.features.staking.impl.presentation.state.utils.withStubUnstakeAction import com.tangem.lib.crypto.BlockchainUtils.isTon import com.tangem.utils.Provider -import com.tangem.utils.TangemBlogUrlBuilder.RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP import com.tangem.utils.coroutines.* import com.tangem.utils.extensions.isSingleItem import com.tangem.utils.extensions.orZero @@ -603,7 +603,9 @@ internal class StakingModel @Inject constructor( override fun onInitialInfoBannerClick() { analyticsEventHandler.send(StakingAnalyticsEvent.WhatIsStaking()) - innerRouter.openUrl(WHAT_IS_STAKING_ARTICLE_URL) + modelScope.launch { + innerRouter.openUrl(TangemBlogUrlBuilder.build(TangemBlogUrlBuilder.Post.HowToStake)) + } } override fun onInfoClick(infoType: InfoType) { @@ -1100,7 +1102,9 @@ internal class StakingModel @Inject constructor( } override fun onOpenLearnMoreAboutApproveClick() { - urlOpener.openUrl(RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP) + modelScope.launch { + urlOpener.openUrl(TangemBlogUrlBuilder.build(TangemBlogUrlBuilder.Post.GiveRevokePermission)) + } } override fun onActivateTonAccountNotificationClick() { @@ -1495,7 +1499,6 @@ internal class StakingModel @Inject constructor( } private companion object { - const val WHAT_IS_STAKING_ARTICLE_URL = "https://tangem.com/en/blog/post/how-to-stake-cryptocurrency/" const val ALLOWANCE_UPDATE_DELAY = 10_000L } } \ No newline at end of file diff --git a/features/staking/impl/src/test/kotlin/com/tangem/features/staking/impl/presentation/model/StakingModelNavigationTest.kt b/features/staking/impl/src/test/kotlin/com/tangem/features/staking/impl/presentation/model/StakingModelNavigationTest.kt index c933a896b6..00d51a6c28 100644 --- a/features/staking/impl/src/test/kotlin/com/tangem/features/staking/impl/presentation/model/StakingModelNavigationTest.kt +++ b/features/staking/impl/src/test/kotlin/com/tangem/features/staking/impl/presentation/model/StakingModelNavigationTest.kt @@ -1,6 +1,7 @@ package com.tangem.features.staking.impl.presentation.model import arrow.core.Either +import com.tangem.common.TangemBlogUrlBuilder import com.tangem.core.analytics.models.Basic import com.tangem.core.ui.haptic.TangemHapticEffect import com.tangem.core.ui.message.DialogMessage @@ -300,21 +301,24 @@ internal class StakingModelNavigationTest : StakingModelTestBase() { @Test fun `WHEN onInitialInfoBannerClick THEN analytics sent and url opened`() = runTest { + val expectedUrl = "https://tangem.com/blog/post/how-to-stake-cryptocurrency/?utm_source=tangem-app" + mockkObject(TangemBlogUrlBuilder) + coEvery { TangemBlogUrlBuilder.build(TangemBlogUrlBuilder.Post.HowToStake) } returns expectedUrl every { innerRouter.openUrl(any()) } just Runs val model = createModel(testScope = this) advanceUntilIdle() model.onInitialInfoBannerClick() + advanceUntilIdle() verify { analyticsEventHandler.send(match { it is StakingAnalyticsEvent.WhatIsStaking }) } - verify { - innerRouter.openUrl("https://tangem.com/en/blog/post/how-to-stake-cryptocurrency/") - } + verify { innerRouter.openUrl(expectedUrl) } model.onDestroy() + unmockkObject(TangemBlogUrlBuilder) } @Test @@ -478,15 +482,20 @@ internal class StakingModelNavigationTest : StakingModelTestBase() { @Test fun `WHEN onOpenLearnMoreAboutApproveClick THEN urlOpener opens approve url`() = runTest { + val expectedUrl = "https://tangem.com/blog/post/give-revoke-permission/?utm_source=tangem-app" + mockkObject(TangemBlogUrlBuilder) + coEvery { TangemBlogUrlBuilder.build(TangemBlogUrlBuilder.Post.GiveRevokePermission) } returns expectedUrl every { urlOpener.openUrl(any()) } just Runs val model = createModel(testScope = this) advanceUntilIdle() model.onOpenLearnMoreAboutApproveClick() + advanceUntilIdle() - verify { urlOpener.openUrl("https://tangem.com/en/blog/post/give-revoke-permission/") } + verify { urlOpener.openUrl(expectedUrl) } + unmockkObject(TangemBlogUrlBuilder) model.onDestroy() } } \ No newline at end of file diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt index 1cc6c4cd61..c1e58f73df 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt @@ -96,6 +96,7 @@ import com.tangem.feature.swap.utils.getContractAddress import com.tangem.features.approval.api.GiveApprovalComponent import com.tangem.features.send.v2.api.entity.FeeSelectorUM import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadTrigger +import com.tangem.common.TangemBlogUrlBuilder import com.tangem.features.swap.SwapComponent import com.tangem.utils.Provider import com.tangem.utils.coroutines.* @@ -1357,12 +1358,16 @@ internal class SwapModel @Inject constructor( val selectedFee = (getSelectedFee() as? TxFee.Legacy)?.feeType ?: FeeType.NORMAL val txFeeState = dataState.getCurrentLoadedSwapState()?.txFee as? TxFeeState.MultipleFeeState ?: return@UiActions - uiState = stateBuilder.showSelectFeeBottomSheet( - uiState = uiState, - selectedFee = selectedFee, - txFeeState = txFeeState, - ) { - uiState = stateBuilder.dismissBottomSheet(uiState) + modelScope.launch { + val readMoreUrl = TangemBlogUrlBuilder.build(TangemBlogUrlBuilder.Post.WhatIsTransactionFee) + uiState = stateBuilder.showSelectFeeBottomSheet( + uiState = uiState, + selectedFee = selectedFee, + txFeeState = txFeeState, + readMoreUrl = readMoreUrl, + ) { + uiState = stateBuilder.dismissBottomSheet(uiState) + } } }, onSelectFeeType = { txFee -> diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index 2c2551b3d5..61d9d800cd 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -37,7 +37,6 @@ import com.tangem.utils.Provider import com.tangem.utils.StringsSigns import com.tangem.utils.StringsSigns.DASH_SIGN import com.tangem.utils.StringsSigns.TILDE_SIGN -import com.tangem.utils.TangemBlogUrlBuilder.FEE_BLOG_LINK import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @@ -984,6 +983,7 @@ internal class StateBuilder( uiState: SwapStateHolder, selectedFee: FeeType, txFeeState: TxFeeState.MultipleFeeState, + readMoreUrl: String, onDismiss: () -> Unit, ): SwapStateHolder { val config = ChooseFeeBottomSheetConfig( @@ -995,7 +995,7 @@ internal class StateBuilder( } actions.onSelectFeeType.invoke(selectedItem) }, - readMoreUrl = FEE_BLOG_LINK, + readMoreUrl = readMoreUrl, feeItems = txFeeState.toFeeItemState(), readMore = resourceReference(R.string.common_read_more), onReadMoreClick = actions.onLinkClick, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/ExpressTransactionsModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/ExpressTransactionsModel.kt index bdeb591c42..e69efad8e5 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/ExpressTransactionsModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/ExpressTransactionsModel.kt @@ -2,6 +2,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.model import androidx.compose.runtime.Stable import arrow.core.getOrElse +import com.tangem.common.TangemBlogUrlBuilder import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM import com.tangem.common.ui.expressStatus.state.ExpressTransactionsBlockState @@ -114,6 +115,12 @@ internal class ExpressTransactionsModel @Inject constructor( router.openUrl(url) } + override fun onReadAboutCrossChainBridgesClick() { + modelScope.launch { + router.openUrl(TangemBlogUrlBuilder.build(TangemBlogUrlBuilder.Post.AboutCrossChainBridges)) + } + } + override fun onConfirmDisposeExpressStatus() { uiMessageSender.send( DialogMessage( diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsClickIntents.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsClickIntents.kt index 5518cfdedb..dcee418428 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsClickIntents.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsClickIntents.kt @@ -100,6 +100,8 @@ interface ExpressTransactionsClickIntents { fun onOpenUrlClick(url: String) + fun onReadAboutCrossChainBridgesClick() + fun onConfirmDisposeExpressStatus() fun onDisposeExpressStatus() diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt index b8b37a945e..8423c533ab 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt @@ -13,6 +13,7 @@ import com.tangem.domain.dynamicaddresses.DynamicAddressesDerivationChecker import com.tangem.domain.dynamicaddresses.DynamicAddressesFeatureToggles import com.tangem.domain.dynamicaddresses.DynamicAddressesSupportedBlockchains import com.tangem.domain.dynamicaddresses.IsXpubSupportedUseCase +import com.tangem.common.TangemBlogUrlBuilder import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter import com.tangem.common.ui.bottomsheet.receive.AddressModel @@ -960,6 +961,12 @@ internal class TokenDetailsModel @Inject constructor( router.openUrl(url) } + override fun onReadAboutCrossChainBridgesClick() { + modelScope.launch { + router.openUrl(TangemBlogUrlBuilder.build(TangemBlogUrlBuilder.Post.AboutCrossChainBridges)) + } + } + override fun onSwapPromoDismiss(promoId: PromoId) { modelScope.launch(dispatchers.main) { shouldShowPromoTokenUseCase.neverToShow(promoId) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt index c55c898406..e5d7a1341b 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt @@ -39,7 +39,6 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList import com.tangem.utils.logging.TangemLogger import java.math.BigDecimal -import java.util.Locale // Fixme [REDACTED_JIRA] @Suppress("LargeClass") @@ -232,7 +231,7 @@ internal class TokenDetailsSwapTransactionsStateConverter( } else { ExchangeStatusNotification.TokenRefunded( cryptoCurrency = refundToken, - onReadMoreClick = { clickIntents.onOpenUrlClick(url = getAboutCrossChainBridgesLink()) }, + onReadMoreClick = clickIntents::onReadAboutCrossChainBridgesClick, onGoToTokenClick = { clickIntents.onGoToRefundedTokenClick(refundToken) }, ) } @@ -424,12 +423,4 @@ internal class TokenDetailsSwapTransactionsStateConverter( isDone = isSendingDone, ) } - - private fun getAboutCrossChainBridgesLink(): String { - return if (Locale.getDefault().country == "RU") { - "https://tangem.com/ru/blog/post/an-overview-of-cross-chain-bridges/" - } else { - "https://tangem.com/en/blog/post/an-overview-of-cross-chain-bridges/" - } - } } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt index b6c43240ab..85ce399ac9 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSendTransactionModel.kt @@ -12,6 +12,7 @@ import com.domain.blockaid.models.transaction.SimulationResult import com.domain.blockaid.models.transaction.ValidationResult import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.transaction.TransactionFee +import com.tangem.common.TangemBlogUrlBuilder import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model @@ -59,7 +60,6 @@ import com.tangem.features.walletconnect.transaction.entity.send.WcSendTransacti import com.tangem.features.walletconnect.transaction.routes.WcTransactionRoutes import com.tangem.features.walletconnect.transaction.ui.blockaid.WcSendAndReceiveBlockAidUiConverter import com.tangem.features.walletconnect.utils.WcNotificationsFactory -import com.tangem.utils.SupportedLanguages import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch @@ -301,14 +301,10 @@ internal class WcSendTransactionModel @Inject constructor( stackNavigation.pop() } - @Deprecated("Use TangemBlockUrlBuilder instead") private fun onApproveLearnMoreClick() { - val code = SupportedLanguages.getCurrentSupportedLanguageCode() - .takeIf { it == SupportedLanguages.RUSSIAN } - ?: SupportedLanguages.ENGLISH - - val url = "https://tangem.com/$code/blog/post/give-revoke-permission/" - urlOpener.openUrl(url) + modelScope.launch { + urlOpener.openUrl(TangemBlogUrlBuilder.build(TangemBlogUrlBuilder.Post.GiveRevokePermission)) + } } private fun isMultipleSignRequired(useCase: WcSignUseCase<*>): Boolean { diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/YieldSupplyActiveModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/YieldSupplyActiveModel.kt index 92b9ebf190..4da4c1922b 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/YieldSupplyActiveModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/YieldSupplyActiveModel.kt @@ -4,6 +4,7 @@ import arrow.core.getOrElse import com.arkivanov.decompose.router.slot.SlotNavigation import com.arkivanov.decompose.router.slot.activate import com.arkivanov.decompose.router.slot.dismiss +import com.tangem.common.TangemBlogUrlBuilder import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.di.ModelScoped @@ -34,13 +35,12 @@ import com.tangem.features.yield.supply.impl.active.model.transformers.YieldSupp import com.tangem.features.yield.supply.impl.subcomponents.approve.YieldSupplyApproveComponent import com.tangem.features.yield.supply.impl.subcomponents.stopearning.YieldSupplyStopEarningComponent import com.tangem.utils.StringsSigns.DASH_SIGN -import com.tangem.utils.TangemBlogUrlBuilder import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import com.tangem.utils.logging.TangemLogger import com.tangem.utils.transformer.update import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch -import com.tangem.utils.logging.TangemLogger import javax.inject.Inject @Suppress("LongParameterList", "LargeClass") @@ -160,7 +160,9 @@ internal class YieldSupplyActiveModel @Inject constructor( } fun onReadMoreClick() { - urlOpener.openUrl(TangemBlogUrlBuilder.YIELD_SUPPLY_HOW_IT_WORKS_URL) + modelScope.launch { + urlOpener.openUrl(TangemBlogUrlBuilder.build(TangemBlogUrlBuilder.Post.HowYieldModeWorks)) + } } private fun subscribeOnCurrencyStatusUpdates() { diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/model/YieldSupplyPromoModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/model/YieldSupplyPromoModel.kt index a1384942e1..438a5ceb15 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/model/YieldSupplyPromoModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/promo/model/YieldSupplyPromoModel.kt @@ -2,6 +2,7 @@ package com.tangem.features.yield.supply.impl.promo.model import com.arkivanov.decompose.router.slot.SlotNavigation import com.arkivanov.decompose.router.slot.activate +import com.tangem.common.TangemBlogUrlBuilder import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.di.ModelScoped @@ -11,13 +12,12 @@ import com.tangem.core.navigation.url.UrlOpener import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.wrappedList import com.tangem.features.yield.supply.api.YieldSupplyPromoComponent -import com.tangem.features.yield.supply.impl.R import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics +import com.tangem.features.yield.supply.impl.R import com.tangem.features.yield.supply.impl.promo.YieldSupplyPromoConfig import com.tangem.features.yield.supply.impl.promo.entity.YieldSupplyPromoUM -import com.tangem.utils.TangemBlogUrlBuilder -import com.tangem.utils.TangemBlogUrlBuilder.YIELD_SUPPLY_HOW_IT_WORKS_URL import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.launch import javax.inject.Inject @ModelScoped @@ -32,8 +32,8 @@ internal class YieldSupplyPromoModel @Inject constructor( val params: YieldSupplyPromoComponent.Params = paramsContainer.require() val uiState: YieldSupplyPromoUM = YieldSupplyPromoUM( - tosLink = TangemBlogUrlBuilder.YIELD_SUPPLY_TOS_URL, - policyLink = TangemBlogUrlBuilder.YIELD_SUPPLY_PRIVACY_URL, + tosLink = AAVE_TOS_URL, + policyLink = AAVE_PRIVACY_URL, tokenSymbol = params.currency.symbol, title = resourceReference( R.string.yield_module_promo_screen_title_v2, @@ -69,10 +69,17 @@ internal class YieldSupplyPromoModel @Inject constructor( } override fun onHowItWorksClick() { - urlOpener.openUrl(YIELD_SUPPLY_HOW_IT_WORKS_URL) + modelScope.launch { + urlOpener.openUrl(TangemBlogUrlBuilder.build(TangemBlogUrlBuilder.Post.HowYieldModeWorks)) + } } override fun onStartEarningClick() { bottomSheetNavigation.activate(YieldSupplyPromoConfig.Action) } + + private companion object { + const val AAVE_TOS_URL = "https://aave.com/terms-of-service" + const val AAVE_PRIVACY_URL = "https://aave.com/privacy-policy" + } } \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt index cdd36725d5..1cbf1b303f 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt @@ -3,6 +3,7 @@ package com.tangem.features.yield.supply.impl.subcomponents.approve.model import arrow.core.getOrElse import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.transaction.TransactionFee +import com.tangem.common.TangemBlogUrlBuilder import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsParam @@ -39,7 +40,6 @@ import com.tangem.features.yield.supply.impl.subcomponents.approve.YieldSupplyAp import com.tangem.features.yield.supply.impl.subcomponents.notifications.YieldSupplyNotificationsComponent import com.tangem.features.yield.supply.impl.subcomponents.notifications.YieldSupplyNotificationsUpdateTrigger import com.tangem.features.yield.supply.impl.subcomponents.notifications.entity.YieldSupplyNotificationData -import com.tangem.utils.TangemBlogUrlBuilder import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.logging.TangemLogger import com.tangem.utils.transformer.update @@ -113,7 +113,9 @@ internal class YieldSupplyApproveModel @Inject constructor( } fun onReadMoreClick() { - urlOpener.openUrl(TangemBlogUrlBuilder.FEE_BLOG_LINK) + modelScope.launch { + urlOpener.openUrl(TangemBlogUrlBuilder.build(TangemBlogUrlBuilder.Post.WhatIsTransactionFee)) + } } override fun onFeeReload() { diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt index 3ab8efa5ae..0a339baa9b 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt @@ -1,6 +1,7 @@ package com.tangem.features.yield.supply.impl.subcomponents.stopearning.model import arrow.core.getOrElse +import com.tangem.common.TangemBlogUrlBuilder import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsParam @@ -39,7 +40,6 @@ import com.tangem.features.yield.supply.impl.subcomponents.notifications.YieldSu import com.tangem.features.yield.supply.impl.subcomponents.notifications.entity.YieldSupplyNotificationData import com.tangem.features.yield.supply.impl.subcomponents.stopearning.YieldSupplyStopEarningComponent import com.tangem.features.yield.supply.impl.subcomponents.stopearning.model.transformer.YieldSupplyStopEarningFeeContentTransformer -import com.tangem.utils.TangemBlogUrlBuilder import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.extensions.orZero import com.tangem.utils.logging.TangemLogger @@ -128,7 +128,9 @@ internal class YieldSupplyStopEarningModel @Inject constructor( } fun onReadMoreClick() { - urlOpener.openUrl(TangemBlogUrlBuilder.FEE_BLOG_LINK) + modelScope.launch { + urlOpener.openUrl(TangemBlogUrlBuilder.build(TangemBlogUrlBuilder.Post.WhatIsTransactionFee)) + } } fun onClick() {