Updated on 2026-08-14

This commit is contained in:
Tangem 2024-07-03 18:36:02 +05:00
parent 540f27d1c4
commit 353efc2c21
22 changed files with 329 additions and 109 deletions

View file

@ -64,6 +64,7 @@ sealed class AnalyticsParam {
data object Send : ScreensSources("Send") data object Send : ScreensSources("Send")
data object Intro : ScreensSources("Introduction") data object Intro : ScreensSources("Introduction")
data object MyWallets : ScreensSources("My Wallets") data object MyWallets : ScreensSources("My Wallets")
data object Token : ScreensSources("Token")
} }
sealed class TxSentFrom(val value: String) { sealed class TxSentFrom(val value: String) {

View file

@ -53,14 +53,6 @@ object PreferencesKeys {
val LAST_SWAPPED_CRYPTOCURRENCY_ID_KEY by lazy { stringPreferencesKey(name = "lastSwappedCryptoCurrency") } val LAST_SWAPPED_CRYPTOCURRENCY_ID_KEY by lazy { stringPreferencesKey(name = "lastSwappedCryptoCurrency") }
val IS_WALLET_SWAP_PROMO_CHANGELLY_SHOW_KEY by lazy {
booleanPreferencesKey(name = "isWalletSwapPromoChangellyShown")
}
val IS_TOKEN_SWAP_PROMO_CHANGELLY_SHOW_KEY by lazy {
booleanPreferencesKey(name = "isTokenSwapPromoChangellyShown")
}
val IS_WALLET_TRAVALA_PROMO_SHOWN_KEY by lazy { val IS_WALLET_TRAVALA_PROMO_SHOWN_KEY by lazy {
booleanPreferencesKey(name = "isWalletTravalaPromoShown") booleanPreferencesKey(name = "isWalletTravalaPromoShown")
} }
@ -93,6 +85,14 @@ object PreferencesKeys {
val IS_WALLET_NAMES_MIGRATION_DONE_KEY by lazy { booleanPreferencesKey(name = "isWalletNamesMigrationDone") } val IS_WALLET_NAMES_MIGRATION_DONE_KEY by lazy { booleanPreferencesKey(name = "isWalletNamesMigrationDone") }
val IS_WALLET_SWAP_PROMO_OKX_SHOW_KEY by lazy {
booleanPreferencesKey(name = "isWalletSwapPromoOkxShown")
}
val IS_TOKEN_SWAP_PROMO_OKX_SHOW_KEY by lazy {
booleanPreferencesKey(name = "isTokenSwapPromoOkxShown")
}
fun getStart2CoinTOSAcceptedKey(region: String?) = booleanPreferencesKey(name = "start2Coin_tos_accepted_$region") fun getStart2CoinTOSAcceptedKey(region: String?) = booleanPreferencesKey(name = "start2Coin_tos_accepted_$region")
} }

View file

@ -550,6 +550,8 @@
<string name="story_meet_title">Встречайте Tangem</string> <string name="story_meet_title">Встречайте Tangem</string>
<string name="story_web3_description">Обменивайте, покупайте NFT, получайте займы и делайте вклады в более чем 100 различных децентрализованных сервисах</string> <string name="story_web3_description">Обменивайте, покупайте NFT, получайте займы и делайте вклады в более чем 100 различных децентрализованных сервисах</string>
<string name="story_web3_title">Поддержка Web 3.0</string> <string name="story_web3_title">Поддержка Web 3.0</string>
<string name="swap_promo_text">Обменивайте больше токенов по лучшим курсам прямо в вашем кошельке.</string>
<string name="swap_promo_title">Новый провайдер обмена!</string>
<string name="swapping_alert_cex_description">В сумму включено: \n• комиссия провайдера сервиса\n• комиссия сети за отправку %s от биржи обратно на адрес пользователя</string> <string name="swapping_alert_cex_description">В сумму включено: \n• комиссия провайдера сервиса\n• комиссия сети за отправку %s от биржи обратно на адрес пользователя</string>
<string name="swapping_alert_dex_description">В сумму включена комиссия провайдера сервиса.</string> <string name="swapping_alert_dex_description">В сумму включена комиссия провайдера сервиса.</string>
<string name="swapping_alert_title">Комиссии</string> <string name="swapping_alert_title">Комиссии</string>

View file

@ -543,6 +543,8 @@
<string name="story_meet_title">Meet Tangem</string> <string name="story_meet_title">Meet Tangem</string>
<string name="story_web3_description">Exchange, buy NFT\'s, make loans and deposits in more than 100 different decentralized services</string> <string name="story_web3_description">Exchange, buy NFT\'s, make loans and deposits in more than 100 different decentralized services</string>
<string name="story_web3_title">Web 3.0 Compatible</string> <string name="story_web3_title">Web 3.0 Compatible</string>
<string name="swap_promo_text">Exchange more tokens at better rates directly in your wallet.</string>
<string name="swap_promo_title">New Swap Provider Available!</string>
<string name="swapping_alert_cex_description">The amount includes:\n• service provider\'s fee\n• network fee for sending %s from the exchange back to the user\'s address.</string> <string name="swapping_alert_cex_description">The amount includes:\n• service provider\'s fee\n• network fee for sending %s from the exchange back to the user\'s address.</string>
<string name="swapping_alert_dex_description">The amount includes the service provider\'s fee.</string> <string name="swapping_alert_dex_description">The amount includes the service provider\'s fee.</string>
<string name="swapping_alert_title">Fees</string> <string name="swapping_alert_title">Fees</string>

View file

@ -0,0 +1,163 @@
package com.tangem.core.ui.components.notifications
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import com.tangem.core.ui.R
import com.tangem.core.ui.components.buttons.common.TangemButton
import com.tangem.core.ui.components.buttons.common.TangemButtonColors
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.res.LocalIsInDarkTheme
import com.tangem.core.ui.res.TangemColorPalette.Dark6
import com.tangem.core.ui.res.TangemColorPalette.Light4
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
private val OxkPromoColor = Color(0xFFBCFF2F)
@Composable
fun OkxPromoNotification(config: NotificationConfig, modifier: Modifier = Modifier) {
Column(
modifier
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(Dark6),
) {
Content(config = config)
Button(config = config)
}
}
@Composable
private fun Content(config: NotificationConfig) {
Row {
Icon(
painter = rememberVectorPainter(image = ImageVector.vectorResource(id = R.drawable.img_okx_dex_logo)),
contentDescription = null,
tint = TangemTheme.colors.icon.constant,
modifier = Modifier
.padding(start = TangemTheme.dimens.spacing12)
.align(Alignment.CenterVertically),
)
Column(
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
modifier = Modifier
.weight(1f)
.padding(TangemTheme.dimens.spacing12),
) {
Text(
text = config.title.resolveReference(),
style = TangemTheme.typography.button,
color = OxkPromoColor,
)
Text(
text = config.subtitle.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.constantWhite,
)
}
config.onCloseClick?.let {
Icon(
painter = rememberVectorPainter(
image = ImageVector.vectorResource(R.drawable.ic_close_24),
),
contentDescription = null,
tint = TangemTheme.colors.icon.constant,
modifier =
Modifier
.padding(
top = TangemTheme.dimens.spacing8,
end = TangemTheme.dimens.spacing8,
)
.size(TangemTheme.dimens.size20)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(radius = TangemTheme.dimens.radius10),
onClick = it,
),
)
}
}
}
@Composable
private fun Button(config: NotificationConfig) {
val button = config.buttonsState as? NotificationConfig.ButtonsState.SecondaryButtonConfig
button?.let {
val isDarkMode = LocalIsInDarkTheme.current
TangemButton(
text = button.text.resolveReference(),
icon = TangemButtonIconPosition.Start(button.iconResId ?: R.drawable.ic_exchange_vertical_24),
onClick = button.onClick,
colors = TangemButtonColors(
backgroundColor = if (isDarkMode) Light4 else TangemTheme.colors.button.secondary,
contentColor = Dark6,
disabledBackgroundColor = TangemTheme.colors.button.disabled,
disabledContentColor = TangemTheme.colors.text.disabled,
),
enabled = true,
showProgress = false,
modifier = Modifier
.fillMaxWidth()
.padding(
start = TangemTheme.dimens.spacing12,
end = TangemTheme.dimens.spacing12,
bottom = TangemTheme.dimens.spacing12,
),
)
}
}
// region Preview
@Preview(widthDp = 360)
@Preview(widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun OkxPromoNotification_Preview(
@PreviewParameter(OkxPromoNotificationPreviewProvider::class) data: NotificationConfig,
) {
TangemThemePreview {
OkxPromoNotification(data)
}
}
private class OkxPromoNotificationPreviewProvider : PreviewParameterProvider<NotificationConfig> {
override val values: Sequence<NotificationConfig>
get() = sequenceOf(
NotificationConfig(
title = resourceReference(R.string.swap_promo_title),
subtitle = resourceReference(R.string.swap_promo_text),
iconResId = R.drawable.img_okx_dex_logo,
onCloseClick = {},
),
NotificationConfig(
title = resourceReference(R.string.swap_promo_title),
subtitle = resourceReference(R.string.swap_promo_text),
iconResId = R.drawable.img_okx_dex_logo,
onCloseClick = {},
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
text = resourceReference(R.string.token_swap_promotion_button),
onClick = {},
),
),
)
}
// endregion

View file

@ -101,6 +101,7 @@ class TangemColors internal constructor(
private set private set
var attention by mutableStateOf(attention) var attention by mutableStateOf(attention)
private set private set
var constant by mutableStateOf(constant)
fun update(other: Icon) { fun update(other: Icon) {
primary1 = other.primary1 primary1 = other.primary1
@ -111,6 +112,7 @@ class TangemColors internal constructor(
accent = other.accent accent = other.accent
warning = other.warning warning = other.warning
attention = other.attention attention = other.attention
constant = other.constant
} }
} }

View file

@ -0,0 +1,29 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:viewportHeight="1232.1" android:viewportWidth="2516" android:width="49.00901dp">
<path android:fillColor="#FF000000" android:pathData="m740.2,0L23.5,0c-4.4,0 -8.7,1.6 -11.8,4.6 -3.1,2.9 -4.9,6.9 -4.9,11v670.6c0,4.1 1.8,8.1 4.9,11 3.1,2.9 7.4,4.6 11.8,4.6h716.7c4.4,0 8.7,-1.6 11.8,-4.6 3.1,-2.9 4.9,-6.9 4.9,-11L756.9,15.6c0,-4.1 -1.8,-8.1 -4.9,-11 -3.1,-2.9 -7.4,-4.6 -11.8,-4.6ZM506.9,452.3c0,4.1 -1.8,8.1 -4.9,11 -3.1,2.9 -7.4,4.6 -11.8,4.6h-216.7c-4.4,0 -8.7,-1.6 -11.8,-4.6 -3.1,-2.9 -4.9,-6.9 -4.9,-11v-202.8c0,-4.1 1.8,-8.1 4.9,-11 3.1,-2.9 7.4,-4.6 11.8,-4.6h216.7c4.4,0 8.7,1.6 11.8,4.6 3.1,2.9 4.9,6.9 4.9,11v202.8Z"/>
<path android:fillColor="#FF000000" android:pathData="m2240.6,234h-216.7c-9.2,0 -16.7,7 -16.7,15.6v202.8c0,8.6 7.5,15.6 16.7,15.6h216.7c9.2,0 16.7,-7 16.7,-15.6v-202.8c0,-8.6 -7.5,-15.6 -16.7,-15.6h0Z"/>
<path android:fillColor="#FF000000" android:pathData="m1990.7,0h-216.7c-9.2,0 -16.7,7 -16.7,15.6v202.8c0,8.6 7.5,15.6 16.7,15.6h216.7c9.2,0 16.7,-7 16.7,-15.6V15.6c0,-8.6 -7.5,-15.6 -16.7,-15.6Z"/>
<path android:fillColor="#FF000000" android:pathData="m2490.8,0h-216.7c-9.2,0 -16.7,7 -16.7,15.6v202.8c0,8.6 7.5,15.6 16.7,15.6h216.7c9.2,0 16.7,-7 16.7,-15.6V15.6c0,-8.6 -7.5,-15.6 -16.7,-15.6Z"/>
<path android:fillColor="#FF000000" android:pathData="m1990.7,467.9h-216.7c-9.2,0 -16.7,7 -16.7,15.6v202.8c0,8.6 7.5,15.6 16.7,15.6h216.7c9.2,0 16.7,-7 16.7,-15.6v-202.8c0,-8.6 -7.5,-15.6 -16.7,-15.6Z"/>
<path android:fillColor="#FF000000" android:pathData="m2490.8,467.9h-216.7c-9.2,0 -16.7,7 -16.7,15.6v202.8c0,8.6 7.5,15.6 16.7,15.6h216.7c9.2,0 16.7,-7 16.7,-15.6v-202.8c0,-8.6 -7.5,-15.6 -16.7,-15.6Z"/>
<path android:fillColor="#FF000000" android:pathData="m1615.3,0h-216.7c-9.2,0 -16.7,7 -16.7,15.6v202.8c0,8.6 7.5,15.6 16.7,15.6h216.7c9.2,0 16.7,-7 16.7,-15.6V15.6c0,-8.6 -7.5,-15.6 -16.7,-15.6Z"/>
<path android:fillColor="#FF000000" android:pathData="m1615.3,467.9h-216.7c-9.2,0 -16.7,7 -16.7,15.6v202.8c0,8.6 7.5,15.6 16.7,15.6h216.7c9.2,0 16.7,-7 16.7,-15.6v-202.8c0,-8.6 -7.5,-15.6 -16.7,-15.6Z"/>
<path android:fillColor="#FF000000" android:pathData="m1381.9,249.4c0,-4.1 -1.8,-8.1 -4.9,-11 -3.1,-2.9 -7.4,-4.6 -11.8,-4.6h-233.4V15.6c0,-4.1 -1.8,-8.1 -4.9,-11 -3.1,-2.9 -7.4,-4.6 -11.8,-4.6h-216.7c-4.4,0 -8.7,1.6 -11.8,4.6 -3.1,2.9 -4.9,6.9 -4.9,11v670.3c0,4.1 1.8,8.1 4.9,11 3.1,2.9 7.4,4.6 11.8,4.6h216.7c4.4,0 8.7,-1.6 11.8,-4.6 3.1,-2.9 4.9,-6.9 4.9,-11v-218.2h233.4c4.4,0 8.7,-1.6 11.8,-4.6 3.1,-2.9 4.9,-6.9 4.9,-11v-202.8Z"/>
<path android:fillColor="#FF000000" android:pathData="m1675.2,885c33.8,0 64.5,6.8 92,20.4 27.5,13.6 49.2,33.4 65.2,59.5 16,26.1 24,57.2 24,93.2s-8,67.2 -24,93.4c-16,26.3 -37.6,46.2 -65,60 -27.4,13.8 -58.1,20.6 -92.2,20.6h-125.2v-347.1h125.2ZM1666.2,1163.8c34.5,0 60.5,-9.2 78.3,-27.5 17.7,-18.3 26.6,-44.4 26.6,-78.3s-8.9,-59.4 -26.8,-77.5c-17.9,-18.2 -43.9,-27.3 -78,-27.3h-35.1v210.6h35.1Z"/>
<path android:fillColor="#FF000000" android:pathData="m2125.7,1092.2h-135.6v69.7h157v70.2h-238.1v-347.1h232.4v69.7h-151.3v68.3h135.6v69.2Z"/>
<path android:fillColor="#FF000000" android:pathData="m2509.8,885l-121.4,168.4 127.6,178.8h-100.1l-77.3,-112.9 -78.7,112.9h-92.9l127.6,-176.9 -121.4,-170.3h99.1l71.6,104.3 72.6,-104.3h93.4Z"/>
<path android:fillColor="#FF000000" android:pathData="M0,1033.9h1381.6v49h-1381.6z"/>
</vector>

View file

@ -32,8 +32,18 @@ internal class DefaultPromoRepository(
}.getOrNull() }.getOrNull()
} }
override suspend fun getOkxPromoBanner(): PromoBanner? {
return runCatching(dispatchers.io) {
promoResponseConverter.convert(
tangemApi.getPromotionInfo(OKX)
.getOrThrow(),
)
}.getOrNull()
}
private companion object { private companion object {
private const val CHANGELLY_NAME = "changelly" private const val CHANGELLY_NAME = "changelly"
private const val TRAVALA = "travala" private const val TRAVALA = "travala"
private const val OKX = "okx"
} }
} }

View file

@ -1,8 +1,8 @@
package com.tangem.data.settings package com.tangem.data.settings
import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys.IS_TOKEN_SWAP_PROMO_CHANGELLY_SHOW_KEY import com.tangem.datasource.local.preferences.PreferencesKeys.IS_TOKEN_SWAP_PROMO_OKX_SHOW_KEY
import com.tangem.datasource.local.preferences.PreferencesKeys.IS_WALLET_SWAP_PROMO_CHANGELLY_SHOW_KEY import com.tangem.datasource.local.preferences.PreferencesKeys.IS_WALLET_SWAP_PROMO_OKX_SHOW_KEY
import com.tangem.datasource.local.preferences.PreferencesKeys.IS_WALLET_TRAVALA_PROMO_SHOWN_KEY import com.tangem.datasource.local.preferences.PreferencesKeys.IS_WALLET_TRAVALA_PROMO_SHOWN_KEY
import com.tangem.datasource.local.preferences.utils.get import com.tangem.datasource.local.preferences.utils.get
import com.tangem.datasource.local.preferences.utils.store import com.tangem.datasource.local.preferences.utils.store
@ -16,23 +16,23 @@ class DefaultPromoSettingsRepository(
private val appPreferencesStore: AppPreferencesStore, private val appPreferencesStore: AppPreferencesStore,
) : PromoSettingsRepository { ) : PromoSettingsRepository {
override fun isReadyToShowWalletSwapPromo(): Flow<Boolean> { override fun isReadyToShowWalletSwapPromo(): Flow<Boolean> {
return appPreferencesStore.get(IS_WALLET_SWAP_PROMO_CHANGELLY_SHOW_KEY, true) return appPreferencesStore.get(IS_WALLET_SWAP_PROMO_OKX_SHOW_KEY, true)
} }
override fun isReadyToShowTokenSwapPromo(): Flow<Boolean> { override fun isReadyToShowTokenSwapPromo(): Flow<Boolean> {
return appPreferencesStore.get(IS_TOKEN_SWAP_PROMO_CHANGELLY_SHOW_KEY, true) return appPreferencesStore.get(IS_TOKEN_SWAP_PROMO_OKX_SHOW_KEY, true)
} }
override suspend fun setNeverToShowWalletSwapPromo() { override suspend fun setNeverToShowWalletSwapPromo() {
appPreferencesStore.store( appPreferencesStore.store(
key = IS_WALLET_SWAP_PROMO_CHANGELLY_SHOW_KEY, key = IS_WALLET_SWAP_PROMO_OKX_SHOW_KEY,
value = false, value = false,
) )
} }
override suspend fun setNeverToShowTokenSwapPromo() { override suspend fun setNeverToShowTokenSwapPromo() {
appPreferencesStore.store( appPreferencesStore.store(
key = IS_TOKEN_SWAP_PROMO_CHANGELLY_SHOW_KEY, key = IS_TOKEN_SWAP_PROMO_OKX_SHOW_KEY,
value = false, value = false,
) )
} }

View file

@ -1,18 +1,43 @@
package com.tangem.domain.tokens.models.analytics package com.tangem.domain.tokens.models.analytics
import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
sealed class TokenSwapPromoAnalyticsEvent( sealed class TokenSwapPromoAnalyticsEvent(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = mapOf(),
) : AnalyticsEvent("Swap Promo", event, params, null) { ) : AnalyticsEvent(category = "Promotion", event = event, params = params) {
class NoticePromotionBanner(
object Close : TokenSwapPromoAnalyticsEvent(event = "Button - Close") source: AnalyticsParam.ScreensSources,
programName: ProgramName,
class Exchange(
token: String,
) : TokenSwapPromoAnalyticsEvent( ) : TokenSwapPromoAnalyticsEvent(
event = "Button - Exchange Now", event = "Notice - Promotion Banner",
params = mapOf("Token" to token), params = mapOf(
AnalyticsParam.SOURCE to source.value,
"Program Name" to programName.name,
),
) )
class PromotionBannerClicked(
source: AnalyticsParam.ScreensSources,
programName: ProgramName,
action: BannerAction,
) : TokenSwapPromoAnalyticsEvent(
event = "Promo Banner Clicked",
params = mapOf(
AnalyticsParam.SOURCE to source.value,
"Program Name" to programName.name,
"Action" to action.action,
),
) {
sealed class BannerAction(val action: String) {
data object Clicked : BannerAction(action = "Clicked")
data object Closed : BannerAction(action = "Closed")
}
}
enum class ProgramName {
Travala,
OKX,
}
} }

View file

@ -84,7 +84,7 @@ class GetCurrencyWarningsUseCase(
): Flow<CryptoCurrencyWarning?> { ): Flow<CryptoCurrencyWarning?> {
val currency = currencyStatus.currency val currency = currencyStatus.currency
val cryptoStatuses = operations.getCurrenciesStatusesSync() val cryptoStatuses = operations.getCurrenciesStatusesSync()
val promoBanner = promoRepository.getChangellyPromoBanner() val promoBanner = promoRepository.getOkxPromoBanner()
return combine( return combine(
showSwapPromoTokenUseCase().conflate(), showSwapPromoTokenUseCase().conflate(),
flowOf(marketCryptoCurrencyRepository.isExchangeable(userWalletId, currency)).conflate(), flowOf(marketCryptoCurrencyRepository.isExchangeable(userWalletId, currency)).conflate(),

View file

@ -7,4 +7,6 @@ interface PromoRepository {
suspend fun getChangellyPromoBanner(): PromoBanner? suspend fun getChangellyPromoBanner(): PromoBanner?
suspend fun getTravalaPromoBanner(): PromoBanner? suspend fun getTravalaPromoBanner(): PromoBanner?
suspend fun getOkxPromoBanner(): PromoBanner?
} }

View file

@ -2,7 +2,9 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.analytics
import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.models.analytics.TokenSwapPromoAnalyticsEvent
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification
@ -35,12 +37,15 @@ internal class TokenDetailsNotificationsAnalyticsSender(
-> TokenDetailsAnalyticsEvent.Notice.NotEnoughFee( -> TokenDetailsAnalyticsEvent.Notice.NotEnoughFee(
currency = cryptoCurrency, currency = cryptoCurrency,
) )
is TokenDetailsNotification.SwapPromo -> TokenSwapPromoAnalyticsEvent.NoticePromotionBanner(
programName = TokenSwapPromoAnalyticsEvent.ProgramName.OKX,
source = AnalyticsParam.ScreensSources.Token,
)
is TokenDetailsNotification.NetworksUnreachable, is TokenDetailsNotification.NetworksUnreachable,
is TokenDetailsNotification.ExistentialDeposit, is TokenDetailsNotification.ExistentialDeposit,
is TokenDetailsNotification.NetworksNoAccount, is TokenDetailsNotification.NetworksNoAccount,
is TokenDetailsNotification.TopUpWithoutReserve, is TokenDetailsNotification.TopUpWithoutReserve,
is TokenDetailsNotification.RentInfo, is TokenDetailsNotification.RentInfo,
is TokenDetailsNotification.SwapPromo,
is TokenDetailsNotification.NetworkShutdown, is TokenDetailsNotification.NetworkShutdown,
is TokenDetailsNotification.HederaAssociateWarning, is TokenDetailsNotification.HederaAssociateWarning,
is TokenDetailsNotification.KoinosMana, is TokenDetailsNotification.KoinosMana,

View file

@ -2,7 +2,10 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.componen
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.tangem.core.ui.components.notifications.NotificationConfig import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.extensions.* import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.networkIconResId
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
import com.tangem.features.tokendetails.impl.R import com.tangem.features.tokendetails.impl.R
@ -45,16 +48,9 @@ internal sealed class TokenDetailsNotification(val config: NotificationConfig) {
val onCloseClick: () -> Unit, val onCloseClick: () -> Unit,
) : TokenDetailsNotification( ) : TokenDetailsNotification(
config = NotificationConfig( config = NotificationConfig(
title = resourceReference( title = resourceReference(id = R.string.swap_promo_title),
id = R.string.token_swap_changelly_promotion_title, subtitle = resourceReference(id = R.string.swap_promo_text),
formatArgs = wrappedList("0%"), iconResId = R.drawable.img_okx_dex_logo,
),
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_green_banner_background,
onCloseClick = onCloseClick, onCloseClick = onCloseClick,
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig( buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
text = resourceReference(id = com.tangem.core.ui.R.string.token_swap_promotion_button), text = resourceReference(id = com.tangem.core.ui.R.string.token_swap_promotion_button),

View file

@ -32,7 +32,7 @@ import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBotto
import com.tangem.core.ui.components.marketprice.MarketPriceBlock import com.tangem.core.ui.components.marketprice.MarketPriceBlock
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.components.notifications.Notification import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.components.notifications.NotificationWithBackground import com.tangem.core.ui.components.notifications.OkxPromoNotification
import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.components.transactions.txHistoryItems import com.tangem.core.ui.components.transactions.txHistoryItems
import com.tangem.core.ui.event.EventEffect import com.tangem.core.ui.event.EventEffect
@ -111,7 +111,7 @@ internal fun TokenDetailsScreen(state: TokenDetailsState) {
contentType = { it.config::class.java }, contentType = { it.config::class.java },
itemContent = { itemContent = {
if (it is TokenDetailsNotification.SwapPromo) { if (it is TokenDetailsNotification.SwapPromo) {
NotificationWithBackground( OkxPromoNotification(
config = it.config, config = it.config,
modifier = itemModifier.animateItemPlacement(), modifier = itemModifier.animateItemPlacement(),
) )

View file

@ -5,6 +5,7 @@ import androidx.paging.cachedIn
import arrow.core.getOrElse import arrow.core.getOrElse
import com.tangem.blockchain.common.address.AddressType import com.tangem.blockchain.common.address.AddressType
import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.deeplink.DeepLinksRegistry import com.tangem.core.deeplink.DeepLinksRegistry
import com.tangem.core.deeplink.global.BuyCurrencyDeepLink import com.tangem.core.deeplink.global.BuyCurrencyDeepLink
import com.tangem.core.ui.clipboard.ClipboardManager import com.tangem.core.ui.clipboard.ClipboardManager
@ -709,14 +710,26 @@ internal class TokenDetailsViewModel @Inject constructor(
override fun onSwapPromoDismiss() { override fun onSwapPromoDismiss() {
viewModelScope.launch(dispatchers.main) { viewModelScope.launch(dispatchers.main) {
shouldShowSwapPromoTokenUseCase.neverToShow() shouldShowSwapPromoTokenUseCase.neverToShow()
analyticsEventsHandler.send(TokenSwapPromoAnalyticsEvent.Close) analyticsEventsHandler.send(
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
source = AnalyticsParam.ScreensSources.Token,
programName = TokenSwapPromoAnalyticsEvent.ProgramName.OKX,
action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Closed,
),
)
} }
} }
override fun onSwapPromoClick() { override fun onSwapPromoClick() {
viewModelScope.launch(dispatchers.main) { viewModelScope.launch(dispatchers.main) {
shouldShowSwapPromoTokenUseCase.neverToShow() shouldShowSwapPromoTokenUseCase.neverToShow()
analyticsEventsHandler.send(TokenSwapPromoAnalyticsEvent.Exchange(cryptoCurrency.symbol)) analyticsEventsHandler.send(
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
source = AnalyticsParam.ScreensSources.Token,
programName = TokenSwapPromoAnalyticsEvent.ProgramName.OKX,
action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Clicked,
),
)
} }
onSwapClick(ScenarioUnavailabilityReason.None) onSwapClick(ScenarioUnavailabilityReason.None)
} }

View file

@ -121,38 +121,4 @@ sealed class WalletScreenAnalyticsEvent {
data object DeleteWalletTapped : MainScreen(event = "Button - Delete Wallet Tapped") data object DeleteWalletTapped : MainScreen(event = "Button - Delete Wallet Tapped")
} }
sealed class Promotion(
event: String,
params: Map<String, String> = mapOf(),
) : AnalyticsEvent(category = "Promotion", event = event, params = params) {
class NoticePromotionBanner(
source: AnalyticsParam.ScreensSources,
programName: String,
) : Promotion(
event = "Notice - Promotion Banner",
params = mapOf(
AnalyticsParam.SOURCE to source.value,
"Program Name" to programName,
),
)
class PromotionBannerClicked(
source: AnalyticsParam.ScreensSources,
programName: String,
action: BannerAction,
) : Promotion(
event = "Promo Banner Clicked",
params = mapOf(
AnalyticsParam.SOURCE to source.value,
"Program Name" to programName,
"Action" to action.action,
),
) {
sealed class BannerAction(val action: String) {
data object Clicked : BannerAction(action = "Clicked")
data object Closed : BannerAction(action = "Closed")
}
}
}
} }

View file

@ -3,7 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.analytics.utils
import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent import com.tangem.domain.tokens.models.analytics.TokenSwapPromoAnalyticsEvent
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
@ -46,16 +46,19 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
is WalletNotification.Informational.MissingAddresses -> MainScreen.MissingAddresses is WalletNotification.Informational.MissingAddresses -> MainScreen.MissingAddresses
is WalletNotification.RateApp -> MainScreen.HowDoYouLikeTangem is WalletNotification.RateApp -> MainScreen.HowDoYouLikeTangem
is WalletNotification.Critical.BackupError -> MainScreen.BackupError is WalletNotification.Critical.BackupError -> MainScreen.BackupError
is WalletNotification.TravalaPromo -> WalletScreenAnalyticsEvent.Promotion.NoticePromotionBanner( is WalletNotification.TravalaPromo -> TokenSwapPromoAnalyticsEvent.NoticePromotionBanner(
source = AnalyticsParam.ScreensSources.Main, source = AnalyticsParam.ScreensSources.Main,
programName = "Travala", programName = TokenSwapPromoAnalyticsEvent.ProgramName.Travala,
)
is WalletNotification.SwapPromo -> TokenSwapPromoAnalyticsEvent.NoticePromotionBanner(
source = AnalyticsParam.ScreensSources.Main,
programName = TokenSwapPromoAnalyticsEvent.ProgramName.OKX,
) )
is WalletNotification.UnlockWallets -> null // See [SelectedWalletAnalyticsSender] is WalletNotification.UnlockWallets -> null // See [SelectedWalletAnalyticsSender]
is WalletNotification.Informational.NoAccount, is WalletNotification.Informational.NoAccount,
is WalletNotification.Warning.LowSignatures, is WalletNotification.Warning.LowSignatures,
is WalletNotification.Warning.SomeNetworksUnreachable, is WalletNotification.Warning.SomeNetworksUnreachable,
is WalletNotification.Warning.NetworksUnreachable, is WalletNotification.Warning.NetworksUnreachable,
is WalletNotification.SwapPromo,
-> null -> null
} }
} }

View file

@ -6,7 +6,7 @@ import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.promo.PromoBanner import com.tangem.domain.promo.PromoBanner
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
import com.tangem.domain.settings.ShouldShowTravalaPromoWalletUseCase import com.tangem.domain.settings.ShouldShowSwapPromoWalletUseCase
import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.tokens.GetTokenListUseCase
import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrency
@ -34,7 +34,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private val getTokenListUseCase: GetTokenListUseCase, private val getTokenListUseCase: GetTokenListUseCase,
private val isDemoCardUseCase: IsDemoCardUseCase, private val isDemoCardUseCase: IsDemoCardUseCase,
private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase, private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
private val shouldShowTravalaPromoWalletUseCase: ShouldShowTravalaPromoWalletUseCase, private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase,
private val promoRepository: PromoRepository, private val promoRepository: PromoRepository,
private val isNeedToBackupUseCase: IsNeedToBackupUseCase, private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
private val backupValidator: BackupValidator, private val backupValidator: BackupValidator,
@ -45,18 +45,18 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): Flow<ImmutableList<WalletNotification>> { fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): Flow<ImmutableList<WalletNotification>> {
val cardTypesResolver = userWallet.scanResponse.cardTypesResolver val cardTypesResolver = userWallet.scanResponse.cardTypesResolver
val travalaPromoFlow = flow { emit(promoRepository.getTravalaPromoBanner()) } val promoFlow = flow { emit(promoRepository.getOkxPromoBanner()) }
return combine( return combine(
flow = getTokenListUseCase.launch(userWallet.walletId).conflate(), flow = getTokenListUseCase.launch(userWallet.walletId).conflate(),
flow2 = isReadyToShowRateAppUseCase().conflate(), flow2 = isReadyToShowRateAppUseCase().conflate(),
flow3 = isNeedToBackupUseCase(userWallet.walletId).conflate(), flow3 = isNeedToBackupUseCase(userWallet.walletId).conflate(),
flow4 = shouldShowTravalaPromoWalletUseCase().conflate(), flow4 = shouldShowSwapPromoWalletUseCase().conflate(),
flow5 = travalaPromoFlow.conflate(), flow5 = promoFlow.conflate(),
) { maybeTokenList, isReadyToShowRating, isNeedToBackup, shouldShowTravalaPromo, promoBanner -> ) { maybeTokenList, isReadyToShowRating, isNeedToBackup, shouldShowPromo, promoBanner ->
readyForRateAppNotification = true readyForRateAppNotification = true
buildList { buildList {
addTravalaPromoNotification(shouldShowTravalaPromo, promoBanner, clickIntents) addSwapPromoNotification(shouldShowPromo, promoBanner, clickIntents)
addCriticalNotifications(userWallet) addCriticalNotifications(userWallet)
@ -69,18 +69,16 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
} }
} }
private fun MutableList<WalletNotification>.addTravalaPromoNotification( private fun MutableList<WalletNotification>.addSwapPromoNotification(
shouldShowPromo: Boolean, shouldShowPromo: Boolean,
promoBanner: PromoBanner?, promoBanner: PromoBanner?,
clickIntents: WalletClickIntents, clickIntents: WalletClickIntents,
) { ) {
promoBanner ?: return promoBanner ?: return
val promoNotification = WalletNotification.TravalaPromo( val promoNotification = WalletNotification.SwapPromo(
startDateTime = promoBanner.bannerState.timeline.start, startDateTime = promoBanner.bannerState.timeline.start,
endDateTime = promoBanner.bannerState.timeline.end, endDateTime = promoBanner.bannerState.timeline.end,
bannerLink = promoBanner.bannerState.link, onCloseClick = clickIntents::onCloseSwapPromoClick,
onBookNowButtonClick = clickIntents::onTravalaPromoClick,
onCloseClick = clickIntents::onCloseTravalaPromoClick,
) )
addIf( addIf(
element = promoNotification, element = promoNotification,

View file

@ -2,7 +2,10 @@ package com.tangem.feature.wallet.presentation.wallet.state.model
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.tangem.core.ui.components.notifications.NotificationConfig import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.extensions.* import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.pluralReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.utils.DateTimeFormatters import com.tangem.core.ui.utils.DateTimeFormatters
import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.impl.R
import org.joda.time.DateTime import org.joda.time.DateTime
@ -205,16 +208,9 @@ sealed class WalletNotification(val config: NotificationConfig) {
val onCloseClick: () -> Unit, val onCloseClick: () -> Unit,
) : WalletNotification( ) : WalletNotification(
config = NotificationConfig( config = NotificationConfig(
title = resourceReference( title = resourceReference(id = R.string.swap_promo_title),
id = R.string.main_swap_changelly_promotion_title, subtitle = resourceReference(id = R.string.swap_promo_text),
formatArgs = wrappedList("0%"), iconResId = R.drawable.img_okx_dex_logo,
),
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_green_banner_background,
onCloseClick = onCloseClick, onCloseClick = onCloseClick,
), ),
) )

View file

@ -5,7 +5,7 @@ import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.tangem.core.ui.components.notifications.Notification import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.components.notifications.NotificationWithBackground import com.tangem.core.ui.components.notifications.OkxPromoNotification
import com.tangem.core.ui.components.notifications.TravalaNotificationWithBackground import com.tangem.core.ui.components.notifications.TravalaNotificationWithBackground
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
@ -29,7 +29,7 @@ internal fun LazyListScope.notifications(configs: ImmutableList<WalletNotificati
// TODO develop promo banner general component // TODO develop promo banner general component
when (it) { when (it) {
is WalletNotification.SwapPromo -> { is WalletNotification.SwapPromo -> {
NotificationWithBackground( OkxPromoNotification(
config = it.config, config = it.config,
modifier = modifier.animateItemPlacement(), modifier = modifier.animateItemPlacement(),
) )

View file

@ -14,13 +14,13 @@ import com.tangem.domain.settings.ShouldShowSwapPromoWalletUseCase
import com.tangem.domain.settings.ShouldShowTravalaPromoWalletUseCase import com.tangem.domain.settings.ShouldShowTravalaPromoWalletUseCase
import com.tangem.domain.tokens.FetchTokenListUseCase import com.tangem.domain.tokens.FetchTokenListUseCase
import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.models.analytics.TokenSwapPromoAnalyticsEvent
import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockType import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockType
import com.tangem.domain.wallets.models.UnlockWalletsError import com.tangem.domain.wallets.models.UnlockWalletsError
import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.UnlockWalletsUseCase import com.tangem.domain.wallets.usecase.UnlockWalletsUseCase
import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.Basic import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.Basic
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen
import com.tangem.feature.wallet.presentation.wallet.domain.ScanCardToUnlockWalletClickHandler import com.tangem.feature.wallet.presentation.wallet.domain.ScanCardToUnlockWalletClickHandler
@ -214,6 +214,13 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
} }
override fun onCloseSwapPromoClick() { override fun onCloseSwapPromoClick() {
analyticsEventHandler.send(
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
source = AnalyticsParam.ScreensSources.Main,
programName = TokenSwapPromoAnalyticsEvent.ProgramName.OKX,
action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Closed,
),
)
viewModelScope.launch(dispatchers.main) { viewModelScope.launch(dispatchers.main) {
shouldShowSwapPromoWalletUseCase.neverToShow() shouldShowSwapPromoWalletUseCase.neverToShow()
} }
@ -221,10 +228,10 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
override fun onTravalaPromoClick(link: String?) { override fun onTravalaPromoClick(link: String?) {
analyticsEventHandler.send( analyticsEventHandler.send(
WalletScreenAnalyticsEvent.Promotion.PromotionBannerClicked( TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
source = AnalyticsParam.ScreensSources.Main, source = AnalyticsParam.ScreensSources.Main,
programName = "Travala", programName = TokenSwapPromoAnalyticsEvent.ProgramName.Travala,
action = WalletScreenAnalyticsEvent.Promotion.PromotionBannerClicked.BannerAction.Clicked, action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Clicked,
), ),
) )
link?.let { link?.let {
@ -236,10 +243,10 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
override fun onCloseTravalaPromoClick() { override fun onCloseTravalaPromoClick() {
analyticsEventHandler.send( analyticsEventHandler.send(
WalletScreenAnalyticsEvent.Promotion.PromotionBannerClicked( TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
source = AnalyticsParam.ScreensSources.Main, source = AnalyticsParam.ScreensSources.Main,
programName = "Travala", programName = TokenSwapPromoAnalyticsEvent.ProgramName.Travala,
action = WalletScreenAnalyticsEvent.Promotion.PromotionBannerClicked.BannerAction.Closed, action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Closed,
), ),
) )
viewModelScope.launch(dispatchers.main) { viewModelScope.launch(dispatchers.main) {