Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-19 14:16:53 +03:00
parent 228c0c8af4
commit 82fa3edf45
18 changed files with 97 additions and 61 deletions

View file

@ -1,20 +1,15 @@
package com.tangem.common
import com.tangem.utils.SupportedLanguages
/**
[REDACTED_AUTHOR]
*/
object TangemBlogUrlBuilder {
fun build(post: Post): String {
val code = SupportedLanguages.getCurrentSupportedLanguageCode()
.takeIf { code ->
code == SupportedLanguages.RUSSIAN || code == SupportedLanguages.ENGLISH
}
?: SupportedLanguages.ENGLISH
return "https://tangem.com/$code/blog/post/${post.path}/"
suspend fun build(post: Post): String {
return TangemSiteUrlBuilder.url(
path = "/blog/post/${post.path}/",
campaign = "articles",
)
}
sealed interface Post {
@ -28,5 +23,13 @@ object TangemBlogUrlBuilder {
data object SeedNotifySecond : Post {
override val path: String = "tangem-resolves-log-issue"
}
data object SeedPhraseRiskySolution : Post {
override val path: String = "seed-phrase-a-risky-solution"
}
data object WhatWalletToChoose : Post {
override val path: String = "mobile-wallet"
}
}
}

View file

@ -0,0 +1,42 @@
package com.tangem.common
import android.content.res.Resources
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.ktx.Firebase
import java.util.Locale
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
object TangemSiteUrlBuilder {
suspend fun getUtmTags(campaign: String?): String {
val langCode = Locale.getDefault().language
val utmCampaignPart = campaign?.let { "&utm_campaign=$it-$langCode" }.orEmpty()
val utmContent = deviceLang()?.let { "&utm_content=devicelang-$it" }.orEmpty()
val appInstanceIdPart = getAppInstanceId()?.let { "&app_instance_id=$it" }.orEmpty()
return "utm_source=tangem-app&utm_medium=app$utmCampaignPart$utmContent$appInstanceIdPart"
}
suspend fun url(path: String, campaign: String): String {
val normalizedPath = path.trim('/')
return "https://tangem.com/$normalizedPath?${getUtmTags(campaign)}"
}
private fun deviceLang(): String? {
return runCatching {
Resources.getSystem().configuration.locales.get(0).toLanguageTag()
}.getOrNull()
}
private suspend fun getAppInstanceId(): String? {
return suspendCoroutine { cont ->
Firebase.analytics.appInstanceId
.addOnSuccessListener { id ->
cont.resume(id)
}
.addOnFailureListener {
cont.resume(null)
}
}
}
}

View file

@ -10,7 +10,7 @@ object SupportedLanguages {
const val ITALIAN = "it"
const val JAPANESE = "ja"
const val UKRAINIAN = "uk"
const val CHINESE = "uk"
const val CHINESE = "zh"
const val SPANISH = "es"
val supportedLangugeCodes = listOf(

View file

@ -2,6 +2,7 @@ package com.tangem.utils
import java.util.Locale
@Deprecated("Use TangemBlogUrlBuilder from common module")
object TangemBlogUrlBuilder {
private const val RU_LOCALE = "ru"

View file

@ -35,6 +35,8 @@ dependencies {
implementation(projects.domain.hotWallet)
// endregion
implementation(projects.common)
// region Tangem libraries
implementation(tangemDeps.blockchain) // android-library
implementation(tangemDeps.card.core)

View file

@ -1,23 +1,24 @@
package com.tangem.domain.wallets.usecase
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.ktx.Firebase
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import com.tangem.common.TangemSiteUrlBuilder
import java.util.Locale
class GenerateBuyTangemCardLinkUseCase {
suspend operator fun invoke(): String = suspendCoroutine { cont ->
Firebase.analytics.appInstanceId
.addOnSuccessListener { id ->
cont.resume("$NEW_BUY_WALLET_URL&app_instance_id=$id")
}
.addOnFailureListener {
cont.resume(NEW_BUY_WALLET_URL)
}
suspend operator fun invoke(source: Source): String {
return invoke(source.utmCampaign)
}
companion object {
private const val NEW_BUY_WALLET_URL = "https://buy.tangem.com/?utm_source=tangem-app&utm_medium=app"
suspend operator fun invoke(utmCampaign: String?): String {
val langCode = Locale.getDefault().language
val utmTags = TangemSiteUrlBuilder.getUtmTags(utmCampaign)
return "https://buy.tangem.com/$langCode?$utmTags"
}
enum class Source(val utmCampaign: String) {
Creation("prospect"),
Settings("users"),
Backup("backup"),
Upgrade("upgrade"),
}
}

View file

@ -162,7 +162,8 @@ internal class CreateWalletStartModel @Inject constructor(
private fun onBuyClick() {
analyticsEventHandler.send(Basic.ButtonBuy(source = AnalyticsParam.ScreensSources.CreateWalletIntro))
modelScope.launch {
generateBuyTangemCardLinkUseCase.invoke().let { urlOpener.openUrl(it) }
generateBuyTangemCardLinkUseCase
.invoke(GenerateBuyTangemCardLinkUseCase.Source.Creation).let { urlOpener.openUrl(it) }
}
}

View file

@ -232,8 +232,10 @@ internal class DetailsModel @Inject constructor(
modelScope.launch {
if (hotWalletFeatureToggles.isHotWalletEnabled) {
analyticsEventHandler.send(Basic.ButtonBuy(source = AnalyticsParam.ScreensSources.Settings))
generateBuyTangemCardLinkUseCase.invoke().let { urlOpener.openUrl(it) }
generateBuyTangemCardLinkUseCase
.invoke(GenerateBuyTangemCardLinkUseCase.Source.Settings).let { urlOpener.openUrl(it) }
} else {
// This is incorrect implementation of buy link generation, but it is left here
urlOpener.openUrl(buildBuyLink())
}
}

View file

@ -135,7 +135,7 @@ internal class HomeModel @Inject constructor(
analyticsEventHandler.send(IntroductionProcess.ButtonBuyCards())
analyticsEventHandler.send(Shop.ScreenOpened())
modelScope.launch {
generateBuyTangemCardLinkUseCase.invoke().let { urlOpener.openUrl(it) }
generateBuyTangemCardLinkUseCase.invoke(null).let { urlOpener.openUrl(it) }
}
}

View file

@ -78,7 +78,8 @@ internal class CreateHardwareWalletModel @Inject constructor(
private fun onBuyTangemWalletClick() {
analyticsEventHandler.send(Basic.ButtonBuy(source = AnalyticsParam.ScreensSources.CreateWallet))
modelScope.launch {
generateBuyTangemCardLinkUseCase.invoke().let { urlOpener.openUrl(it) }
generateBuyTangemCardLinkUseCase
.invoke(GenerateBuyTangemCardLinkUseCase.Source.Upgrade).let { urlOpener.openUrl(it) }
}
}

View file

@ -94,7 +94,8 @@ internal class UpgradeWalletModel @Inject constructor(
private fun onBuyTangemWalletClick() {
analyticsEventHandler.send(Basic.ButtonBuy(source = AnalyticsParam.ScreensSources.Upgrade))
modelScope.launch {
generateBuyTangemCardLinkUseCase.invoke().let { urlOpener.openUrl(it) }
generateBuyTangemCardLinkUseCase
.invoke(GenerateBuyTangemCardLinkUseCase.Source.Upgrade).let { urlOpener.openUrl(it) }
}
}

View file

@ -132,7 +132,8 @@ internal class WalletBackupModel @Inject constructor(
private fun onBuyClick() {
analyticsEventHandler.send(Basic.ButtonBuy(source = AnalyticsParam.ScreensSources.Backup))
modelScope.launch {
generateBuyTangemCardLinkUseCase.invoke().let { urlOpener.openUrl(it) }
generateBuyTangemCardLinkUseCase
.invoke(GenerateBuyTangemCardLinkUseCase.Source.Backup).let { urlOpener.openUrl(it) }
}
}

View file

@ -171,7 +171,8 @@ internal class WalletHardwareBackupModel @Inject constructor(
private fun onBuyClick() {
analyticsEventHandler.send(Basic.ButtonBuy(source = AnalyticsParam.ScreensSources.HardwareWallet))
modelScope.launch {
generateBuyTangemCardLinkUseCase.invoke().let { urlOpener.openUrl(it) }
generateBuyTangemCardLinkUseCase
.invoke(GenerateBuyTangemCardLinkUseCase.Source.Backup).let { urlOpener.openUrl(it) }
}
}

View file

@ -34,6 +34,7 @@ dependencies {
/** Common */
implementation(projects.common.ui)
implementation(projects.common.routing)
implementation(projects.common)
/** Domain */
implementation(projects.domain.models)

View file

@ -3,6 +3,7 @@ package com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.mode
import androidx.compose.runtime.Stable
import arrow.core.getOrElse
import com.tangem.common.CompletionResult
import com.tangem.common.TangemBlogUrlBuilder
import com.tangem.common.core.TangemSdkError
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
@ -161,7 +162,11 @@ internal class MultiWalletSeedPhraseModel @Inject constructor(
openGeneratedSeedPhrase()
},
onLearnMoreClicked = {
urlOpener.openUrl(seedPhraseLearnMoreUrl())
modelScope.launch {
urlOpener.openUrl(
TangemBlogUrlBuilder.build(TangemBlogUrlBuilder.Post.SeedPhraseRiskySolution),
)
}
},
)
}

View file

@ -1,28 +0,0 @@
package com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.model
import android.net.Uri
import com.tangem.utils.SupportedLanguages
import java.util.Locale
// TODO: [REDACTED_JIRA]
internal fun seedPhraseLearnMoreUrl(): String {
val language = Locale.getDefault().language
val languageBy = "by"
if (SupportedLanguages.RUSSIAN.equals(language, true) ||
languageBy.equals(language, true)
) {
SupportedLanguages.RUSSIAN
} else {
SupportedLanguages.ENGLISH
}
val webUri = Uri.Builder()
.scheme("https")
.authority("tangem.com")
.appendPath(language)
.appendPath("blog/post/seed-phrase-a-risky-solution")
.build()
return webUri.toString()
}

View file

@ -1194,6 +1194,7 @@ internal class StateBuilder(
)
}
@Deprecated("Use TangemBlockUrlBuilder instead")
private fun buildReadMoreUrl(): String {
return buildString {
append(FEE_READ_MORE_URL_FIRST_PART)

View file

@ -297,6 +297,7 @@ internal class WcSendTransactionModel @Inject constructor(
stackNavigation.pop()
}
@Deprecated("Use TangemBlockUrlBuilder instead")
private fun onApproveLearnMoreClick() {
val code = SupportedLanguages.getCurrentSupportedLanguageCode()
.takeIf { it == SupportedLanguages.RUSSIAN }