Updated on 2026-08-14
This commit is contained in:
parent
c1c244753e
commit
7653ea926d
27 changed files with 473 additions and 69 deletions
|
|
@ -7,8 +7,8 @@ import com.tangem.domain.balancehiding.UpdateBalanceHidingSettingsUseCase
|
|||
import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository
|
||||
import com.tangem.domain.settings.*
|
||||
import com.tangem.domain.settings.repositories.AppRatingRepository
|
||||
import com.tangem.domain.settings.repositories.PromoSettingsRepository
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.domain.settings.repositories.SwapPromoRepository
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.domain.settings.DefaultLegacySettingsRepository
|
||||
import dagger.Module
|
||||
|
|
@ -108,17 +108,25 @@ internal object SettingsDomainModule {
|
|||
@Provides
|
||||
@ViewModelScoped
|
||||
fun provideShouldShowSwapPromoWalletUseCase(
|
||||
swapPromoRepository: SwapPromoRepository,
|
||||
promoSettingsRepository: PromoSettingsRepository,
|
||||
): ShouldShowSwapPromoWalletUseCase {
|
||||
return ShouldShowSwapPromoWalletUseCase(swapPromoRepository)
|
||||
return ShouldShowSwapPromoWalletUseCase(promoSettingsRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun provideShouldShowTravalaPromoWalletUseCase(
|
||||
promoSettingsRepository: PromoSettingsRepository,
|
||||
): ShouldShowTravalaPromoWalletUseCase {
|
||||
return ShouldShowTravalaPromoWalletUseCase(promoSettingsRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun provideShouldShowSwapPromoTokenUseCase(
|
||||
swapPromoRepository: SwapPromoRepository,
|
||||
promoSettingsRepository: PromoSettingsRepository,
|
||||
): ShouldShowSwapPromoTokenUseCase {
|
||||
return ShouldShowSwapPromoTokenUseCase(swapPromoRepository)
|
||||
return ShouldShowSwapPromoTokenUseCase(promoSettingsRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ data class PromotionInfoResponse(
|
|||
data class BannerState(
|
||||
@Json(name = "timeline") val timeline: Timeline,
|
||||
@Json(name = "status") val status: String,
|
||||
@Json(name = "link") val link: String?,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
|
|
|
|||
|
|
@ -55,6 +55,10 @@ object PreferencesKeys {
|
|||
booleanPreferencesKey(name = "isTokenSwapPromoChangellyShown")
|
||||
}
|
||||
|
||||
val IS_WALLET_TRAVALA_PROMO_SHOWN_KEY by lazy {
|
||||
booleanPreferencesKey(name = "isWalletTravalaPromoShown")
|
||||
}
|
||||
|
||||
val FEATURE_TOGGLES_KEY by lazy { stringPreferencesKey(name = "featureToggles") }
|
||||
|
||||
val WAS_TWINS_ONBOARDING_SHOWN by lazy { booleanPreferencesKey(name = "twinsOnboardingShown") }
|
||||
|
|
|
|||
|
|
@ -267,6 +267,9 @@
|
|||
<string name="main_swap_changelly_promotion_message">Обменивайте свои токены с %1$s комиссии провайдера через Changelly с %2$s по %3$s февраля.</string>
|
||||
<string name="main_swap_changelly_promotion_title">Обмен с Changelly, %s комиссии</string>
|
||||
<string name="main_tokens">Токены</string>
|
||||
<string name="main_travala_promotion_button">Забронировать</string>
|
||||
<string name="main_travala_promotion_description" formatted="false">Оплатите его криптой и сэкономьте **50 долларов** через нашего партнера Travala: **%1s - %2s**</string>
|
||||
<string name="main_travala_promotion_title">Забронируйте отпуск с Tangem</string>
|
||||
<string name="manage_tokens_add">Добавить</string>
|
||||
<string name="manage_tokens_edit">Изменить</string>
|
||||
<string name="manage_tokens_list_header_title">Рыночная капитализация</string>
|
||||
|
|
|
|||
|
|
@ -267,6 +267,9 @@
|
|||
<string name="main_swap_changelly_promotion_message">Enjoy %1$s service fees on swaps via Changelly from February %2$s-%3$s</string>
|
||||
<string name="main_swap_changelly_promotion_title">Swap with Changelly, %s fees</string>
|
||||
<string name="main_tokens">Tokens</string>
|
||||
<string name="main_travala_promotion_button">Book now</string>
|
||||
<string name="main_travala_promotion_description" formatted="false">Save **50 USDT** while booking via our partner Travala: **%1s - %2s**</string>
|
||||
<string name="main_travala_promotion_title">Book your holidays with Tangem and pay in crypto</string>
|
||||
<string name="manage_tokens_add">Add</string>
|
||||
<string name="manage_tokens_edit">Edit</string>
|
||||
<string name="manage_tokens_list_header_title">Coin market cap</string>
|
||||
|
|
|
|||
|
|
@ -47,7 +47,11 @@ fun NotificationWithBackground(config: NotificationConfig, modifier: Modifier =
|
|||
modifier = modifier
|
||||
.defaultMinSize(minHeight = TangemTheme.dimens.size62)
|
||||
.fillMaxWidth()
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium),
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.clickable(
|
||||
enabled = config.onClick != null,
|
||||
onClick = config.onClick ?: {},
|
||||
),
|
||||
) {
|
||||
val (iconRef, titleRef, subtitleRef, closeIconRef, buttonRef, backgroundRef) = createRefs()
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,215 @@
|
|||
package com.tangem.core.ui.components.notifications
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
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.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.ScaleFactor
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.style.LineBreak
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerH8
|
||||
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.extensions.wrappedList
|
||||
import com.tangem.core.ui.res.TangemColorPalette.White
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
/**
|
||||
* Travala notification with image background
|
||||
* @see <a href="https://www.figma.com/file/Vs6SkVsFnUPsSCNwlnVf5U/Android-%E2%80%93-UI?type=design&node-id=11690-12057&mode=design&t=eFnsA9sNytcQIoQ4-4">Travala Promo</a>
|
||||
*/
|
||||
@Suppress("LongMethod", "DestructuringDeclarationWithTooManyEntries")
|
||||
@Composable
|
||||
fun TravalaNotificationWithBackground(config: NotificationConfig, modifier: Modifier = Modifier) {
|
||||
val button = config.buttonsState as? NotificationConfig.ButtonsState.SecondaryButtonConfig
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = TangemTheme.dimens.size62)
|
||||
.background(Color.Black)
|
||||
.fillMaxWidth()
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.clickable(
|
||||
enabled = config.onClick != null,
|
||||
onClick = config.onClick ?: {},
|
||||
),
|
||||
propagateMinConstraints = true,
|
||||
contentAlignment = Alignment.TopStart,
|
||||
) {
|
||||
val density = LocalDensity.current
|
||||
Image(
|
||||
painter = painterResource(R.drawable.img_travala_banner_promo_background),
|
||||
contentDescription = null,
|
||||
contentScale = TravalaBackgroundScale(density),
|
||||
alignment = Alignment.TopStart,
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.wrapContentSize(unbounded = true, align = Alignment.TopStart)
|
||||
.align(Alignment.TopStart),
|
||||
)
|
||||
Image(
|
||||
painter = painterResource(R.drawable.img_travala_banner_promo_background_2),
|
||||
contentDescription = null,
|
||||
contentScale = TravalaBackgroundScale(density),
|
||||
alignment = Alignment.TopStart,
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.wrapContentSize(unbounded = true, align = Alignment.TopEnd)
|
||||
.align(Alignment.TopEnd),
|
||||
)
|
||||
Column {
|
||||
Row {
|
||||
Box(modifier = Modifier.size(87.dp))
|
||||
Column(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.padding(top = TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
Text(
|
||||
text = config.title.resolveReference(),
|
||||
style = TangemTheme.typography.button.copy(
|
||||
lineBreak = LineBreak.Heading,
|
||||
),
|
||||
color = TangemTheme.colors.text.constantWhite,
|
||||
)
|
||||
SpacerH8()
|
||||
Text(
|
||||
text = formatSubtitle(config.subtitle.resolveReference()),
|
||||
style = TangemTheme.typography.caption2.copy(
|
||||
lineBreak = LineBreak.Heading,
|
||||
),
|
||||
color = TangemTheme.colors.text.constantWhite,
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_close_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.text.constantWhite,
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
top = TangemTheme.dimens.spacing12,
|
||||
end = TangemTheme.dimens.spacing12,
|
||||
start = TangemTheme.dimens.spacing2,
|
||||
)
|
||||
.size(TangemTheme.dimens.size16)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = rememberRipple(bounded = false),
|
||||
) {
|
||||
config.onCloseClick?.invoke()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
TangemButton(
|
||||
text = button?.text?.resolveReference().orEmpty(),
|
||||
icon = TangemButtonIconPosition.None,
|
||||
onClick = button?.onClick ?: {},
|
||||
colors = TangemButtonColors(
|
||||
backgroundColor = White.copy(alpha = 0.3f),
|
||||
contentColor = White,
|
||||
disabledBackgroundColor = TangemTheme.colors.button.disabled,
|
||||
disabledContentColor = TangemTheme.colors.text.disabled,
|
||||
),
|
||||
enabled = true,
|
||||
showProgress = false,
|
||||
modifier = Modifier
|
||||
.padding(TangemTheme.dimens.spacing12)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val TRAVALA_BACKGROUND_SRC_IMG_SCALE = 4
|
||||
|
||||
private class TravalaBackgroundScale(
|
||||
val density: Density,
|
||||
) : ContentScale {
|
||||
override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor {
|
||||
with(density) {
|
||||
val originalWidth = (srcSize.width / TRAVALA_BACKGROUND_SRC_IMG_SCALE).dp.toPx()
|
||||
val widthScale = originalWidth / srcSize.width
|
||||
val originalHeight = (srcSize.height / TRAVALA_BACKGROUND_SRC_IMG_SCALE).dp.toPx()
|
||||
val heightScale = originalHeight / srcSize.height
|
||||
return ScaleFactor(widthScale, heightScale)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun formatSubtitle(subtitle: String): AnnotatedString {
|
||||
val pattern = Regex("\\*\\*(.*?)\\*\\*")
|
||||
var startIndex = 0
|
||||
val annotatedString = buildAnnotatedString {
|
||||
pattern.findAll(subtitle).forEach { matchResult ->
|
||||
val index = matchResult.range.first
|
||||
val matchedValue = matchResult.groups[1]?.value ?: ""
|
||||
|
||||
// appends unformatted part
|
||||
append(subtitle.substring(startIndex, index))
|
||||
|
||||
// applies style on ^^-wrapped parts
|
||||
withStyle(SpanStyle(fontWeight = TangemTheme.typography.caption1.fontWeight)) {
|
||||
append(matchedValue)
|
||||
}
|
||||
|
||||
// goes to next part
|
||||
startIndex = matchResult.range.last + 1
|
||||
}
|
||||
|
||||
// appends remaining ending if exists
|
||||
append(subtitle.substring(startIndex))
|
||||
}
|
||||
|
||||
return annotatedString
|
||||
}
|
||||
|
||||
//region preview
|
||||
@Preview
|
||||
@Composable
|
||||
private fun TravalaNotificationWithBackgroundPreview() {
|
||||
TangemTheme {
|
||||
TravalaNotificationWithBackground(
|
||||
config = NotificationConfig(
|
||||
title = resourceReference(
|
||||
id = R.string.main_travala_promotion_title,
|
||||
),
|
||||
subtitle = resourceReference(
|
||||
id = R.string.main_travala_promotion_description,
|
||||
formatArgs = wrappedList("May 13", "June 12"),
|
||||
),
|
||||
iconResId = R.drawable.img_swap_promo,
|
||||
backgroundResId = R.drawable.img_travala_banner_promo_background,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(id = R.string.token_swap_promotion_button),
|
||||
onClick = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.core.ui.utils
|
||||
|
||||
import android.text.format.DateFormat
|
||||
import org.joda.time.DateTime
|
||||
import org.joda.time.format.DateTimeFormat
|
||||
import org.joda.time.format.DateTimeFormatter
|
||||
|
|
@ -55,6 +56,16 @@ object DateTimeFormatters {
|
|||
.withLocale(Locale.getDefault())
|
||||
}
|
||||
|
||||
/**
|
||||
* In API version < 24, there may be some problems with getting the best date and time format pattern.
|
||||
*/
|
||||
val dateMMMMd: DateTimeFormatter by lazy {
|
||||
DateTimeFormatterBuilder()
|
||||
.appendPattern(DateFormat.getBestDateTimePattern(Locale.getDefault(), "MMMM d"))
|
||||
.toFormatter()
|
||||
.withLocale(Locale.getDefault())
|
||||
}
|
||||
|
||||
val dateTimeFormatter: DateTimeFormatter by lazy {
|
||||
DateTimeFormat.forPattern("dd.MM.yyyy HH:mm")
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 441 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 409 KiB |
|
|
@ -23,7 +23,17 @@ internal class DefaultPromoRepository(
|
|||
}.getOrNull()
|
||||
}
|
||||
|
||||
override suspend fun getTravalaPromoBanner(): PromoBanner? {
|
||||
return runCatching(dispatchers.io) {
|
||||
promoResponseConverter.convert(
|
||||
tangemApi.getPromotionInfo(TRAVALA)
|
||||
.getOrThrow(),
|
||||
)
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val CHANGELLY_NAME = "changelly"
|
||||
private const val TRAVALA = "travala"
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ class PromoResponseConverter : Converter<PromotionInfoResponse, PromoBanner?> {
|
|||
name = value.name,
|
||||
bannerState = PromoBanner.BannerState(
|
||||
status = bannerState.status,
|
||||
link = bannerState.link,
|
||||
timeline = PromoBanner.Timeline(
|
||||
start = DateTime.parse(bannerState.timeline.start),
|
||||
end = DateTime.parse(bannerState.timeline.end),
|
||||
|
|
|
|||
|
|
@ -3,36 +3,48 @@ package com.tangem.data.settings
|
|||
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_WALLET_SWAP_PROMO_CHANGELLY_SHOW_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.store
|
||||
import com.tangem.domain.settings.repositories.SwapPromoRepository
|
||||
import com.tangem.domain.settings.repositories.PromoSettingsRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
* Repository for showing swap promo notification.
|
||||
*/
|
||||
class DefaultSwapPromoRepository(
|
||||
class DefaultPromoSettingsRepository(
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
) : SwapPromoRepository {
|
||||
override fun isReadyToShowWalletPromo(): Flow<Boolean> {
|
||||
) : PromoSettingsRepository {
|
||||
override fun isReadyToShowWalletSwapPromo(): Flow<Boolean> {
|
||||
return appPreferencesStore.get(IS_WALLET_SWAP_PROMO_CHANGELLY_SHOW_KEY, true)
|
||||
}
|
||||
|
||||
override fun isReadyToShowTokenPromo(): Flow<Boolean> {
|
||||
override fun isReadyToShowTokenSwapPromo(): Flow<Boolean> {
|
||||
return appPreferencesStore.get(IS_TOKEN_SWAP_PROMO_CHANGELLY_SHOW_KEY, true)
|
||||
}
|
||||
|
||||
override suspend fun setNeverToShowWalletPromo() {
|
||||
override suspend fun setNeverToShowWalletSwapPromo() {
|
||||
appPreferencesStore.store(
|
||||
key = IS_WALLET_SWAP_PROMO_CHANGELLY_SHOW_KEY,
|
||||
value = false,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun setNeverToShowTokenPromo() {
|
||||
override suspend fun setNeverToShowTokenSwapPromo() {
|
||||
appPreferencesStore.store(
|
||||
key = IS_TOKEN_SWAP_PROMO_CHANGELLY_SHOW_KEY,
|
||||
value = false,
|
||||
)
|
||||
}
|
||||
|
||||
override fun isReadyToShowWalletTravalaPromo(): Flow<Boolean> {
|
||||
return appPreferencesStore.get(IS_WALLET_TRAVALA_PROMO_SHOWN_KEY, true)
|
||||
}
|
||||
|
||||
override suspend fun setNeverToShowWalletTravalaPromo() {
|
||||
appPreferencesStore.store(
|
||||
key = IS_WALLET_TRAVALA_PROMO_SHOWN_KEY,
|
||||
value = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,12 +2,12 @@ package com.tangem.data.settings.di
|
|||
|
||||
import com.tangem.data.settings.DefaultAppRatingRepository
|
||||
import com.tangem.data.settings.DefaultSettingsRepository
|
||||
import com.tangem.data.settings.DefaultSwapPromoRepository
|
||||
import com.tangem.data.settings.DefaultPromoSettingsRepository
|
||||
import com.tangem.data.source.preferences.PreferencesDataSource
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.domain.settings.repositories.AppRatingRepository
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.domain.settings.repositories.SwapPromoRepository
|
||||
import com.tangem.domain.settings.repositories.PromoSettingsRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -41,7 +41,7 @@ internal object SettingsDataModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSwapPromoRepository(appPreferencesStore: AppPreferencesStore): SwapPromoRepository {
|
||||
return DefaultSwapPromoRepository(appPreferencesStore = appPreferencesStore)
|
||||
fun providePromoSettingsSettingsRepository(appPreferencesStore: AppPreferencesStore): PromoSettingsRepository {
|
||||
return DefaultPromoSettingsRepository(appPreferencesStore = appPreferencesStore)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
package com.tangem.domain.settings
|
||||
|
||||
import com.tangem.domain.settings.repositories.SwapPromoRepository
|
||||
import com.tangem.domain.settings.repositories.PromoSettingsRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class ShouldShowSwapPromoTokenUseCase(private val swapPromoRepository: SwapPromoRepository) {
|
||||
class ShouldShowSwapPromoTokenUseCase(private val promoSettingsRepository: PromoSettingsRepository) {
|
||||
|
||||
operator fun invoke(): Flow<Boolean> = swapPromoRepository.isReadyToShowTokenPromo()
|
||||
operator fun invoke(): Flow<Boolean> = promoSettingsRepository.isReadyToShowTokenSwapPromo()
|
||||
|
||||
suspend fun neverToShow() = swapPromoRepository.setNeverToShowTokenPromo()
|
||||
suspend fun neverToShow() = promoSettingsRepository.setNeverToShowTokenSwapPromo()
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
package com.tangem.domain.settings
|
||||
|
||||
import com.tangem.domain.settings.repositories.SwapPromoRepository
|
||||
import com.tangem.domain.settings.repositories.PromoSettingsRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class ShouldShowSwapPromoWalletUseCase(private val swapPromoRepository: SwapPromoRepository) {
|
||||
class ShouldShowSwapPromoWalletUseCase(private val promoSettingsRepository: PromoSettingsRepository) {
|
||||
|
||||
operator fun invoke(): Flow<Boolean> = swapPromoRepository.isReadyToShowWalletPromo()
|
||||
operator fun invoke(): Flow<Boolean> = promoSettingsRepository.isReadyToShowWalletSwapPromo()
|
||||
|
||||
suspend fun neverToShow() = swapPromoRepository.setNeverToShowWalletPromo()
|
||||
suspend fun neverToShow() = promoSettingsRepository.setNeverToShowWalletSwapPromo()
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.domain.settings
|
||||
|
||||
import com.tangem.domain.settings.repositories.PromoSettingsRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class ShouldShowTravalaPromoWalletUseCase(private val promoSettingsRepository: PromoSettingsRepository) {
|
||||
|
||||
operator fun invoke(): Flow<Boolean> = promoSettingsRepository.isReadyToShowWalletTravalaPromo()
|
||||
|
||||
suspend fun neverToShow() = promoSettingsRepository.setNeverToShowWalletTravalaPromo()
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.domain.settings.repositories
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface PromoSettingsRepository {
|
||||
fun isReadyToShowWalletSwapPromo(): Flow<Boolean>
|
||||
|
||||
fun isReadyToShowTokenSwapPromo(): Flow<Boolean>
|
||||
|
||||
suspend fun setNeverToShowWalletSwapPromo()
|
||||
|
||||
suspend fun setNeverToShowTokenSwapPromo()
|
||||
|
||||
fun isReadyToShowWalletTravalaPromo(): Flow<Boolean>
|
||||
|
||||
suspend fun setNeverToShowWalletTravalaPromo()
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package com.tangem.domain.settings.repositories
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface SwapPromoRepository {
|
||||
fun isReadyToShowWalletPromo(): Flow<Boolean>
|
||||
|
||||
fun isReadyToShowTokenPromo(): Flow<Boolean>
|
||||
|
||||
suspend fun setNeverToShowWalletPromo()
|
||||
|
||||
suspend fun setNeverToShowTokenPromo()
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ data class PromoBanner(
|
|||
data class BannerState(
|
||||
val timeline: Timeline,
|
||||
val status: String,
|
||||
val link: String?,
|
||||
)
|
||||
|
||||
data class Timeline(
|
||||
|
|
|
|||
|
|
@ -5,4 +5,6 @@ import com.tangem.domain.promo.PromoBanner
|
|||
interface PromoRepository {
|
||||
|
||||
suspend fun getChangellyPromoBanner(): PromoBanner?
|
||||
|
||||
suspend fun getTravalaPromoBanner(): PromoBanner?
|
||||
}
|
||||
|
|
@ -121,4 +121,38 @@ sealed class WalletScreenAnalyticsEvent {
|
|||
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@ package com.tangem.feature.wallet.presentation.wallet.analytics.utils
|
|||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
|
||||
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.WalletState
|
||||
|
|
@ -44,6 +46,10 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
|
|||
is WalletNotification.Informational.MissingAddresses -> MainScreen.MissingAddresses
|
||||
is WalletNotification.RateApp -> MainScreen.HowDoYouLikeTangem
|
||||
is WalletNotification.Critical.BackupError -> MainScreen.BackupError
|
||||
is WalletNotification.TravalaPromo -> WalletScreenAnalyticsEvent.Promotion.NoticePromotionBanner(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
programName = "Travala",
|
||||
)
|
||||
is WalletNotification.UnlockWallets -> null // See [SelectedWalletAnalyticsSender]
|
||||
is WalletNotification.Informational.NoAccount,
|
||||
is WalletNotification.Warning.LowSignatures,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import com.tangem.domain.common.util.cardTypesResolver
|
|||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.promo.PromoBanner
|
||||
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
|
||||
import com.tangem.domain.settings.ShouldShowSwapPromoWalletUseCase
|
||||
import com.tangem.domain.settings.ShouldShowTravalaPromoWalletUseCase
|
||||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
|
@ -34,7 +34,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
private val getTokenListUseCase: GetTokenListUseCase,
|
||||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
|
||||
private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase,
|
||||
private val shouldShowTravalaPromoWalletUseCase: ShouldShowTravalaPromoWalletUseCase,
|
||||
private val promoRepository: PromoRepository,
|
||||
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
|
||||
private val backupValidator: BackupValidator,
|
||||
|
|
@ -45,18 +45,18 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): Flow<ImmutableList<WalletNotification>> {
|
||||
val cardTypesResolver = userWallet.scanResponse.cardTypesResolver
|
||||
|
||||
val promoFlow = flow { emit(promoRepository.getChangellyPromoBanner()) }
|
||||
val travalaPromoFlow = flow { emit(promoRepository.getTravalaPromoBanner()) }
|
||||
return combine(
|
||||
flow = getTokenListUseCase(userWallet.walletId).conflate(),
|
||||
flow2 = isReadyToShowRateAppUseCase().conflate(),
|
||||
flow3 = isNeedToBackupUseCase(userWallet.walletId).conflate(),
|
||||
flow4 = shouldShowSwapPromoWalletUseCase().conflate(),
|
||||
flow5 = promoFlow,
|
||||
) { maybeTokenList, isReadyToShowRating, isNeedToBackup, shouldShowPromo, promoBanner ->
|
||||
flow4 = shouldShowTravalaPromoWalletUseCase().conflate(),
|
||||
flow5 = travalaPromoFlow.conflate(),
|
||||
) { maybeTokenList, isReadyToShowRating, isNeedToBackup, shouldShowTravalaPromo, promoBanner ->
|
||||
|
||||
readyForRateAppNotification = true
|
||||
buildList {
|
||||
addSwapPromoNotification(shouldShowPromo, promoBanner, clickIntents)
|
||||
addTravalaPromoNotification(shouldShowTravalaPromo, promoBanner, clickIntents)
|
||||
|
||||
addCriticalNotifications(userWallet)
|
||||
|
||||
|
|
@ -69,16 +69,18 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addSwapPromoNotification(
|
||||
private fun MutableList<WalletNotification>.addTravalaPromoNotification(
|
||||
shouldShowPromo: Boolean,
|
||||
promoBanner: PromoBanner?,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
promoBanner ?: return
|
||||
val promoNotification = WalletNotification.SwapPromo(
|
||||
val promoNotification = WalletNotification.TravalaPromo(
|
||||
startDateTime = promoBanner.bannerState.timeline.start,
|
||||
endDateTime = promoBanner.bannerState.timeline.end,
|
||||
onCloseClick = clickIntents::onCloseSwapPromoClick,
|
||||
bannerLink = promoBanner.bannerState.link,
|
||||
onBookNowButtonClick = clickIntents::onTravalaPromoClick,
|
||||
onCloseClick = clickIntents::onCloseTravalaPromoClick,
|
||||
)
|
||||
addIf(
|
||||
element = promoNotification,
|
||||
|
|
|
|||
|
|
@ -2,10 +2,8 @@ package com.tangem.feature.wallet.presentation.wallet.state.model
|
|||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
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.extensions.*
|
||||
import com.tangem.core.ui.utils.DateTimeFormatters
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import org.joda.time.DateTime
|
||||
|
||||
|
|
@ -173,6 +171,34 @@ sealed class WalletNotification(val config: NotificationConfig) {
|
|||
),
|
||||
)
|
||||
|
||||
data class TravalaPromo(
|
||||
val startDateTime: DateTime,
|
||||
val endDateTime: DateTime,
|
||||
val bannerLink: String?,
|
||||
val onBookNowButtonClick: (String?) -> Unit,
|
||||
val onCloseClick: () -> Unit,
|
||||
) : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = resourceReference(id = R.string.main_travala_promotion_title),
|
||||
subtitle = resourceReference(
|
||||
id = R.string.main_travala_promotion_description,
|
||||
wrappedList(
|
||||
DateTimeFormatters.formatDate(startDateTime, DateTimeFormatters.dateMMMMd),
|
||||
DateTimeFormatters.formatDate(endDateTime, DateTimeFormatters.dateMMMMd),
|
||||
),
|
||||
),
|
||||
// Stub. Travala has its own Composable implementation with correct img
|
||||
iconResId = R.drawable.ic_star_24,
|
||||
// Stub. Travala has its own Composable implementation with correct img
|
||||
backgroundResId = R.drawable.ic_star_24,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
onClick = { onBookNowButtonClick(bannerLink) },
|
||||
text = resourceReference(R.string.main_travala_promotion_button),
|
||||
),
|
||||
onCloseClick = onCloseClick,
|
||||
),
|
||||
)
|
||||
|
||||
data class SwapPromo(
|
||||
val startDateTime: DateTime,
|
||||
val endDateTime: DateTime,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import androidx.compose.foundation.lazy.items
|
|||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.components.notifications.Notification
|
||||
import com.tangem.core.ui.components.notifications.NotificationWithBackground
|
||||
import com.tangem.core.ui.components.notifications.TravalaNotificationWithBackground
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -25,23 +26,33 @@ internal fun LazyListScope.notifications(configs: ImmutableList<WalletNotificati
|
|||
key = { it::class.java },
|
||||
contentType = { it::class.java },
|
||||
itemContent = {
|
||||
if (it is WalletNotification.SwapPromo) {
|
||||
NotificationWithBackground(
|
||||
config = it.config,
|
||||
modifier = modifier.animateItemPlacement(),
|
||||
)
|
||||
} else {
|
||||
Notification(
|
||||
config = it.config,
|
||||
modifier = modifier.animateItemPlacement(),
|
||||
iconTint = when (it) {
|
||||
is WalletNotification.Critical -> TangemTheme.colors.icon.warning
|
||||
is WalletNotification.Informational -> TangemTheme.colors.icon.accent
|
||||
is WalletNotification.RateApp -> TangemTheme.colors.icon.attention
|
||||
is WalletNotification.UnlockWallets -> TangemTheme.colors.icon.primary1
|
||||
else -> null
|
||||
},
|
||||
)
|
||||
// TODO develop promo banner general component
|
||||
when (it) {
|
||||
is WalletNotification.SwapPromo -> {
|
||||
NotificationWithBackground(
|
||||
config = it.config,
|
||||
modifier = modifier.animateItemPlacement(),
|
||||
)
|
||||
}
|
||||
is WalletNotification.TravalaPromo -> {
|
||||
TravalaNotificationWithBackground(
|
||||
config = it.config,
|
||||
modifier = modifier.animateItemPlacement(),
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
Notification(
|
||||
config = it.config,
|
||||
modifier = modifier.animateItemPlacement(),
|
||||
iconTint = when (it) {
|
||||
is WalletNotification.Critical -> TangemTheme.colors.icon.warning
|
||||
is WalletNotification.Informational -> TangemTheme.colors.icon.accent
|
||||
is WalletNotification.RateApp -> TangemTheme.colors.icon.attention
|
||||
is WalletNotification.UnlockWallets -> TangemTheme.colors.icon.primary1
|
||||
else -> null
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import com.tangem.domain.redux.ReduxStateHolder
|
|||
import com.tangem.domain.settings.NeverToSuggestRateAppUseCase
|
||||
import com.tangem.domain.settings.RemindToRateAppLaterUseCase
|
||||
import com.tangem.domain.settings.ShouldShowSwapPromoWalletUseCase
|
||||
import com.tangem.domain.settings.ShouldShowTravalaPromoWalletUseCase
|
||||
import com.tangem.domain.tokens.FetchTokenListUseCase
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockType
|
||||
|
|
@ -19,6 +20,7 @@ import com.tangem.domain.wallets.models.UserWallet
|
|||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.UnlockWalletsUseCase
|
||||
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.MainScreen
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.ScanCardToUnlockWalletClickHandler
|
||||
|
|
@ -56,6 +58,10 @@ internal interface WalletWarningsClickIntents {
|
|||
fun onCloseRateAppWarningClick()
|
||||
|
||||
fun onCloseSwapPromoClick()
|
||||
|
||||
fun onTravalaPromoClick(link: String?)
|
||||
|
||||
fun onCloseTravalaPromoClick()
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -75,6 +81,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase,
|
||||
private val shouldShowTravalaPromoWalletUseCase: ShouldShowTravalaPromoWalletUseCase,
|
||||
) : BaseWalletClickIntents(), WalletWarningsClickIntents {
|
||||
|
||||
override fun onAddBackupCardClick() {
|
||||
|
|
@ -212,6 +219,34 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onTravalaPromoClick(link: String?) {
|
||||
analyticsEventHandler.send(
|
||||
WalletScreenAnalyticsEvent.Promotion.PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
programName = "Travala",
|
||||
action = WalletScreenAnalyticsEvent.Promotion.PromotionBannerClicked.BannerAction.Clicked,
|
||||
),
|
||||
)
|
||||
link?.let {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
router.openUrl(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCloseTravalaPromoClick() {
|
||||
analyticsEventHandler.send(
|
||||
WalletScreenAnalyticsEvent.Promotion.PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
programName = "Travala",
|
||||
action = WalletScreenAnalyticsEvent.Promotion.PromotionBannerClicked.BannerAction.Closed,
|
||||
),
|
||||
)
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
shouldShowTravalaPromoWalletUseCase.neverToShow()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getSelectedUserWallet(): UserWallet? {
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
return getUserWalletUseCase(userWalletId).getOrElse {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue