Updated on 2026-08-14
This commit is contained in:
parent
c7e4ff0598
commit
b74acabcc4
15 changed files with 136 additions and 68 deletions
|
|
@ -191,12 +191,12 @@ interface TangemTechApi {
|
||||||
suspend fun getPromoBannerDisplays(
|
suspend fun getPromoBannerDisplays(
|
||||||
@Query("walletId") walletId: String,
|
@Query("walletId") walletId: String,
|
||||||
@Query("placeholder") placeholder: String,
|
@Query("placeholder") placeholder: String,
|
||||||
@Query("locale") locale: String,
|
@Query("lang") languageISOCode: String,
|
||||||
): ApiResponse<PromoBannerDisplaysResponse>
|
): ApiResponse<PromoBannerDisplaysResponse>
|
||||||
|
|
||||||
@PATCH("v1/displays/{displayId}")
|
@PATCH("v1/banner/displays/{displayId}")
|
||||||
suspend fun dismissPromoBannerDisplay(
|
suspend fun dismissPromoBannerDisplay(
|
||||||
@Path("displayId") displayId: String,
|
@Path("displayId") displayId: Int,
|
||||||
@Body body: DismissPromoBannerRequest,
|
@Body body: DismissPromoBannerRequest,
|
||||||
): ApiResponse<DismissPromoBannerResponse>
|
): ApiResponse<DismissPromoBannerResponse>
|
||||||
// endregion
|
// endregion
|
||||||
|
|
|
||||||
|
|
@ -5,20 +5,31 @@ import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class PromoBannerDisplaysResponse(
|
data class PromoBannerDisplaysResponse(
|
||||||
@Json(name = "items") val items: List<PromoBannerDisplayDTO>,
|
@Json(name = "items")
|
||||||
|
val items: List<PromoBannerDisplayDTO>,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Suppress("BooleanPropertyNaming")
|
@Suppress("BooleanPropertyNaming")
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class PromoBannerDisplayDTO(
|
data class PromoBannerDisplayDTO(
|
||||||
@Json(name = "id") val id: String,
|
@Json(name = "id")
|
||||||
@Json(name = "placeholder") val placeholder: String,
|
val id: Int,
|
||||||
@Json(name = "priority") val priority: String,
|
@Json(name = "placeholder")
|
||||||
@Json(name = "title") val title: String,
|
val placeholder: String,
|
||||||
@Json(name = "subtitle") val subtitle: String,
|
@Json(name = "priority")
|
||||||
@Json(name = "iconUrl") val iconUrl: String?,
|
val priority: String,
|
||||||
@Json(name = "deeplink") val deeplink: String?,
|
@Json(name = "title")
|
||||||
@Json(name = "buttonEnabled") val buttonEnabled: Boolean,
|
val title: String,
|
||||||
@Json(name = "buttonText") val buttonText: String?,
|
@Json(name = "subtitle")
|
||||||
@Json(name = "dismissable") val dismissable: Boolean,
|
val subtitle: String,
|
||||||
|
@Json(name = "iconUrl")
|
||||||
|
val iconUrl: String?,
|
||||||
|
@Json(name = "deeplink")
|
||||||
|
val deeplink: String?,
|
||||||
|
@Json(name = "buttonEnabled")
|
||||||
|
val buttonEnabled: Boolean,
|
||||||
|
@Json(name = "buttonText")
|
||||||
|
val buttonText: String?,
|
||||||
|
@Json(name = "dismissable")
|
||||||
|
val dismissable: Boolean,
|
||||||
)
|
)
|
||||||
|
|
@ -8,34 +8,51 @@ internal sealed class PromoBannerAnalyticsEvent(
|
||||||
) : AnalyticsEvent(category = "Promo Banner", event = event, params = params) {
|
) : AnalyticsEvent(category = "Promo Banner", event = event, params = params) {
|
||||||
|
|
||||||
data class Shown(
|
data class Shown(
|
||||||
private val displayId: String,
|
private val displayId: Int,
|
||||||
private val placeholder: String,
|
private val placeholder: String,
|
||||||
) : PromoBannerAnalyticsEvent(
|
) : PromoBannerAnalyticsEvent(
|
||||||
event = "Banner Shown",
|
event = "Banner Shown",
|
||||||
params = mapOf("Display Id" to displayId, "Placeholder" to placeholder),
|
params = mapOf(
|
||||||
|
PARAM_DISPLAY_ID to displayId.toString(),
|
||||||
|
PARAM_PLACEHOLDER to placeholder,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
data class CarouselScrolled(
|
data class CarouselScrolled(
|
||||||
private val displayId: String,
|
private val displayId: Int,
|
||||||
private val placeholder: String,
|
private val placeholder: String,
|
||||||
) : PromoBannerAnalyticsEvent(
|
) : PromoBannerAnalyticsEvent(
|
||||||
event = "Banner Carousel Scrolled",
|
event = "Banner Carousel Scrolled",
|
||||||
params = mapOf("Display Id" to displayId, "Placeholder" to placeholder),
|
params = mapOf(
|
||||||
|
PARAM_DISPLAY_ID to displayId.toString(),
|
||||||
|
PARAM_PLACEHOLDER to placeholder,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Clicked(
|
data class Clicked(
|
||||||
private val displayId: String,
|
private val displayId: Int,
|
||||||
private val placeholder: String,
|
private val placeholder: String,
|
||||||
) : PromoBannerAnalyticsEvent(
|
) : PromoBannerAnalyticsEvent(
|
||||||
event = "Banner Button Clicked",
|
event = "Banner Button Clicked",
|
||||||
params = mapOf("Display Id" to displayId, "Placeholder" to placeholder),
|
params = mapOf(
|
||||||
|
PARAM_DISPLAY_ID to displayId.toString(),
|
||||||
|
PARAM_PLACEHOLDER to placeholder,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Dismissed(
|
data class Dismissed(
|
||||||
private val displayId: String,
|
private val displayId: Int,
|
||||||
private val placeholder: String,
|
private val placeholder: String,
|
||||||
) : PromoBannerAnalyticsEvent(
|
) : PromoBannerAnalyticsEvent(
|
||||||
event = "Banner Dismissed",
|
event = "Banner Dismissed",
|
||||||
params = mapOf("Display Id" to displayId, "Placeholder" to placeholder),
|
params = mapOf(
|
||||||
|
PARAM_DISPLAY_ID to displayId.toString(),
|
||||||
|
PARAM_PLACEHOLDER to placeholder,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val PARAM_DISPLAY_ID = "Display Id"
|
||||||
|
const val PARAM_PLACEHOLDER = "Placeholder"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -25,7 +25,7 @@ internal class PromoBannerDisplayDTOConverter : Converter<PromoBannerDisplayDTO,
|
||||||
|
|
||||||
private fun convertPriority(value: String): PromoBannerPriority {
|
private fun convertPriority(value: String): PromoBannerPriority {
|
||||||
return when (value.uppercase(Locale.ROOT)) {
|
return when (value.uppercase(Locale.ROOT)) {
|
||||||
"IMPORTANT" -> PromoBannerPriority.IMPORTANT
|
"TOP" -> PromoBannerPriority.TOP
|
||||||
"HIGH" -> PromoBannerPriority.HIGH
|
"HIGH" -> PromoBannerPriority.HIGH
|
||||||
"MEDIUM" -> PromoBannerPriority.MEDIUM
|
"MEDIUM" -> PromoBannerPriority.MEDIUM
|
||||||
"LOW" -> PromoBannerPriority.LOW
|
"LOW" -> PromoBannerPriority.LOW
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ internal class PromoBannerDisplayToNotificationConverter {
|
||||||
fun convert(
|
fun convert(
|
||||||
banner: PromoBannerDisplay,
|
banner: PromoBannerDisplay,
|
||||||
onDeeplinkClick: (String?) -> Unit,
|
onDeeplinkClick: (String?) -> Unit,
|
||||||
onDismiss: (String) -> Unit,
|
onDismiss: (Int) -> Unit,
|
||||||
): PromoBannerNotificationUM {
|
): PromoBannerNotificationUM {
|
||||||
return PromoBannerNotificationUM(
|
return PromoBannerNotificationUM(
|
||||||
displayId = banner.id,
|
displayId = banner.id,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.tangem.features.promobanners.impl.model
|
package com.tangem.features.promobanners.impl.model
|
||||||
|
|
||||||
internal data class PromoBannerDisplay(
|
internal data class PromoBannerDisplay(
|
||||||
val id: String,
|
val id: Int,
|
||||||
val placeholder: String,
|
val placeholder: String,
|
||||||
val priority: PromoBannerPriority,
|
val priority: PromoBannerPriority,
|
||||||
val title: String,
|
val title: String,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,6 @@ package com.tangem.features.promobanners.impl.model
|
||||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||||
|
|
||||||
internal data class PromoBannerNotificationUM(
|
internal data class PromoBannerNotificationUM(
|
||||||
val displayId: String,
|
val displayId: Int,
|
||||||
val config: NotificationConfig,
|
val config: NotificationConfig,
|
||||||
)
|
)
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.tangem.features.promobanners.impl.model
|
package com.tangem.features.promobanners.impl.model
|
||||||
|
|
||||||
internal enum class PromoBannerPriority(val order: Int) {
|
internal enum class PromoBannerPriority(val order: Int) {
|
||||||
IMPORTANT(order = 0),
|
TOP(order = 0),
|
||||||
HIGH(order = 1),
|
HIGH(order = 1),
|
||||||
MEDIUM(order = 2),
|
MEDIUM(order = 2),
|
||||||
LOW(order = 3),
|
LOW(order = 3),
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import com.tangem.features.promobanners.impl.converters.PromoBannerDisplayToNoti
|
||||||
import com.tangem.features.promobanners.impl.repository.PromoBannersRepository
|
import com.tangem.features.promobanners.impl.repository.PromoBannersRepository
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import com.tangem.utils.coroutines.runSuspendCatching
|
import com.tangem.utils.coroutines.runSuspendCatching
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import com.tangem.utils.logging.TangemLogger
|
import com.tangem.utils.logging.TangemLogger
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
|
|
@ -34,11 +35,12 @@ internal class PromoBannersBlockModel @Inject constructor(
|
||||||
private val converter = PromoBannerDisplayToNotificationConverter()
|
private val converter = PromoBannerDisplayToNotificationConverter()
|
||||||
|
|
||||||
private val placeholder: String = params.placeholder.name.lowercase()
|
private val placeholder: String = params.placeholder.name.lowercase()
|
||||||
private val shownBannerIds: MutableSet<String> = ConcurrentHashMap.newKeySet()
|
private val shownBannerIds: MutableSet<Int> = ConcurrentHashMap.newKeySet()
|
||||||
private var wasCarouselScrolled = false
|
private var wasCarouselScrolled = false
|
||||||
|
private val savedDisplayIdByWalletId: MutableMap<String, Int> = mutableMapOf()
|
||||||
|
|
||||||
val uiState: StateFlow<PromoBannersBlockUM>
|
val uiState: StateFlow<PromoBannersBlockUM>
|
||||||
field = MutableStateFlow(PromoBannersBlockUM())
|
field = MutableStateFlow(getInitialState())
|
||||||
|
|
||||||
init {
|
init {
|
||||||
subscribeOnSelectedWallet()
|
subscribeOnSelectedWallet()
|
||||||
|
|
@ -59,46 +61,67 @@ internal class PromoBannersBlockModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun loadBanners(walletId: String) {
|
private suspend fun loadBanners(walletId: String) {
|
||||||
val locale = Locale.getDefault().language
|
val languageISOCode = Locale.getDefault().language
|
||||||
|
|
||||||
runSuspendCatching {
|
runSuspendCatching {
|
||||||
repository.getBanners(walletId, params.placeholder, locale)
|
repository.getBanners(walletId, params.placeholder, languageISOCode)
|
||||||
}.onSuccess { banners ->
|
}.onSuccess { banners ->
|
||||||
|
val bannerUMs = banners.map { banner ->
|
||||||
|
converter.convert(
|
||||||
|
banner = banner,
|
||||||
|
onDeeplinkClick = { deeplink -> onButtonClick(banner.id, deeplink) },
|
||||||
|
onDismiss = { displayId -> onBannerDismiss(walletId, displayId) },
|
||||||
|
)
|
||||||
|
}.toImmutableList()
|
||||||
|
|
||||||
|
val savedDisplayId = savedDisplayIdByWalletId[walletId]
|
||||||
|
val initialPage = if (savedDisplayId != null) {
|
||||||
|
bannerUMs.indexOfFirst { it.displayId == savedDisplayId }.coerceAtLeast(0)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
uiState.value = PromoBannersBlockUM(
|
uiState.value = PromoBannersBlockUM(
|
||||||
banners = banners.map { banner ->
|
userWalletId = walletId,
|
||||||
converter.convert(
|
initialPage = initialPage,
|
||||||
banner = banner,
|
banners = bannerUMs,
|
||||||
onDeeplinkClick = { deeplink -> onButtonClick(banner.id, deeplink) },
|
|
||||||
onDismiss = { displayId -> onBannerDismiss(walletId, displayId) },
|
|
||||||
)
|
|
||||||
}.toImmutableList(),
|
|
||||||
onBannerShown = ::onBannerShown,
|
onBannerShown = ::onBannerShown,
|
||||||
onCarouselScrolled = ::onCarouselScrolled,
|
onCarouselScrolled = ::onCarouselScrolled,
|
||||||
|
onPageChanged = { displayId -> savedDisplayIdByWalletId[walletId] = displayId },
|
||||||
)
|
)
|
||||||
}.onFailure { error ->
|
}.onFailure { error ->
|
||||||
TangemLogger.w("Failed to load promo banners", error)
|
TangemLogger.w("Failed to load promo banners", error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onBannerShown(displayId: String) {
|
private fun onBannerShown(displayId: Int) {
|
||||||
if (shownBannerIds.add(displayId)) {
|
if (shownBannerIds.add(displayId)) {
|
||||||
analyticsEventHandler.send(PromoBannerAnalyticsEvent.Shown(displayId, placeholder))
|
analyticsEventHandler.send(PromoBannerAnalyticsEvent.Shown(displayId, placeholder))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onCarouselScrolled(displayId: String) {
|
private fun onCarouselScrolled(displayId: Int) {
|
||||||
if (!wasCarouselScrolled) {
|
if (!wasCarouselScrolled) {
|
||||||
wasCarouselScrolled = true
|
wasCarouselScrolled = true
|
||||||
analyticsEventHandler.send(PromoBannerAnalyticsEvent.CarouselScrolled(displayId, placeholder))
|
analyticsEventHandler.send(PromoBannerAnalyticsEvent.CarouselScrolled(displayId, placeholder))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onButtonClick(displayId: String, deeplink: String?) {
|
private fun onButtonClick(displayId: Int, deeplink: String?) {
|
||||||
analyticsEventHandler.send(PromoBannerAnalyticsEvent.Clicked(displayId, placeholder))
|
analyticsEventHandler.send(PromoBannerAnalyticsEvent.Clicked(displayId, placeholder))
|
||||||
deeplink?.let { deeplinkLauncher.launch(it) }
|
deeplink?.let { deeplinkLauncher.launch(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onBannerDismiss(walletId: String, displayId: String) {
|
private fun getInitialState() = PromoBannersBlockUM(
|
||||||
|
userWalletId = "",
|
||||||
|
initialPage = 0,
|
||||||
|
banners = persistentListOf(),
|
||||||
|
onBannerShown = {},
|
||||||
|
onCarouselScrolled = {},
|
||||||
|
onPageChanged = {},
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun onBannerDismiss(walletId: String, displayId: Int) {
|
||||||
analyticsEventHandler.send(PromoBannerAnalyticsEvent.Dismissed(displayId, placeholder))
|
analyticsEventHandler.send(PromoBannerAnalyticsEvent.Dismissed(displayId, placeholder))
|
||||||
uiState.update { state ->
|
uiState.update { state ->
|
||||||
state.copy(
|
state.copy(
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
package com.tangem.features.promobanners.impl.model
|
package com.tangem.features.promobanners.impl.model
|
||||||
|
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
|
||||||
|
|
||||||
internal data class PromoBannersBlockUM(
|
internal data class PromoBannersBlockUM(
|
||||||
val banners: ImmutableList<PromoBannerNotificationUM> = persistentListOf(),
|
val userWalletId: String,
|
||||||
val onBannerShown: (displayId: String) -> Unit = {},
|
val initialPage: Int,
|
||||||
val onCarouselScrolled: (displayId: String) -> Unit = {},
|
val banners: ImmutableList<PromoBannerNotificationUM>,
|
||||||
|
val onBannerShown: (displayId: Int) -> Unit,
|
||||||
|
val onCarouselScrolled: (displayId: Int) -> Unit,
|
||||||
|
val onPageChanged: (displayId: Int) -> Unit,
|
||||||
)
|
)
|
||||||
|
|
@ -28,7 +28,7 @@ internal class DefaultPromoBannersRepository(
|
||||||
override suspend fun getBanners(
|
override suspend fun getBanners(
|
||||||
walletId: String,
|
walletId: String,
|
||||||
placeholder: Placeholder,
|
placeholder: Placeholder,
|
||||||
locale: String,
|
languageISOCode: String,
|
||||||
): List<PromoBannerDisplay> {
|
): List<PromoBannerDisplay> {
|
||||||
val key = BannersCacheKey(walletId, placeholder)
|
val key = BannersCacheKey(walletId, placeholder)
|
||||||
|
|
||||||
|
|
@ -38,7 +38,7 @@ internal class DefaultPromoBannersRepository(
|
||||||
tangemTechApi.getPromoBannerDisplays(
|
tangemTechApi.getPromoBannerDisplays(
|
||||||
walletId = walletId,
|
walletId = walletId,
|
||||||
placeholder = placeholder.toApiValue(),
|
placeholder = placeholder.toApiValue(),
|
||||||
locale = locale,
|
languageISOCode = languageISOCode,
|
||||||
).getOrThrow()
|
).getOrThrow()
|
||||||
.items
|
.items
|
||||||
.map(converter::convert)
|
.map(converter::convert)
|
||||||
|
|
@ -52,7 +52,7 @@ internal class DefaultPromoBannersRepository(
|
||||||
return banners
|
return banners
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun dismissBanner(walletId: String, displayId: String) {
|
override suspend fun dismissBanner(walletId: String, displayId: Int) {
|
||||||
cache.update(default = emptyMap()) { current ->
|
cache.update(default = emptyMap()) { current ->
|
||||||
current.mapValues { (key, banners) ->
|
current.mapValues { (key, banners) ->
|
||||||
if (key.walletId == walletId) {
|
if (key.walletId == walletId) {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,11 @@ import com.tangem.features.promobanners.impl.model.PromoBannerDisplay
|
||||||
|
|
||||||
internal interface PromoBannersRepository {
|
internal interface PromoBannersRepository {
|
||||||
|
|
||||||
suspend fun getBanners(walletId: String, placeholder: Placeholder, locale: String): List<PromoBannerDisplay>
|
suspend fun getBanners(
|
||||||
|
walletId: String,
|
||||||
|
placeholder: Placeholder,
|
||||||
|
languageISOCode: String,
|
||||||
|
): List<PromoBannerDisplay>
|
||||||
|
|
||||||
suspend fun dismissBanner(walletId: String, displayId: String)
|
suspend fun dismissBanner(walletId: String, displayId: Int)
|
||||||
}
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ import androidx.compose.foundation.pager.PagerState
|
||||||
import androidx.compose.foundation.pager.rememberPagerState
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.key
|
||||||
import androidx.compose.runtime.snapshotFlow
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -35,12 +36,16 @@ internal fun PromoBannersBlock(state: PromoBannersBlockUM, modifier: Modifier =
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
BannersCarousel(
|
key(state.userWalletId) {
|
||||||
banners = state.banners,
|
BannersCarousel(
|
||||||
onBannerShown = state.onBannerShown,
|
banners = state.banners,
|
||||||
onCarouselScroll = state.onCarouselScrolled,
|
initialPage = state.initialPage,
|
||||||
modifier = modifier,
|
onBannerShown = state.onBannerShown,
|
||||||
)
|
onCarouselScroll = state.onCarouselScrolled,
|
||||||
|
onPageChange = state.onPageChanged,
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,16 +61,22 @@ private fun SingleBanner(banner: PromoBannerNotificationUM, modifier: Modifier =
|
||||||
@Composable
|
@Composable
|
||||||
private fun BannersCarousel(
|
private fun BannersCarousel(
|
||||||
banners: List<PromoBannerNotificationUM>,
|
banners: List<PromoBannerNotificationUM>,
|
||||||
onBannerShown: (String) -> Unit,
|
initialPage: Int,
|
||||||
onCarouselScroll: (String) -> Unit,
|
onBannerShown: (Int) -> Unit,
|
||||||
|
onCarouselScroll: (Int) -> Unit,
|
||||||
|
onPageChange: (Int) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val pagerState = rememberPagerState(pageCount = { banners.size })
|
val pagerState = rememberPagerState(
|
||||||
|
initialPage = initialPage,
|
||||||
|
pageCount = { banners.size },
|
||||||
|
)
|
||||||
|
|
||||||
LaunchedEffect(pagerState, banners) {
|
LaunchedEffect(pagerState, banners) {
|
||||||
snapshotFlow { pagerState.currentPage }
|
snapshotFlow { pagerState.currentPage }
|
||||||
.collect { page ->
|
.collect { page ->
|
||||||
banners.getOrNull(page)?.let { banner ->
|
banners.getOrNull(page)?.let { banner ->
|
||||||
|
onPageChange(banner.displayId)
|
||||||
onBannerShown(banner.displayId)
|
onBannerShown(banner.displayId)
|
||||||
if (page == 1) {
|
if (page == 1) {
|
||||||
// one-time event when user scrolls from first to second page,
|
// one-time event when user scrolls from first to second page,
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ class PromoBannerDisplayDTOConverterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `should convert DTO to domain model`() {
|
fun `should convert DTO to domain model`() {
|
||||||
val dto = PromoBannerDisplayDTO(
|
val dto = PromoBannerDisplayDTO(
|
||||||
id = "123",
|
id = 123,
|
||||||
placeholder = "MAIN",
|
placeholder = "MAIN",
|
||||||
priority = "HIGH",
|
priority = "HIGH",
|
||||||
title = "Test Banner",
|
title = "Test Banner",
|
||||||
|
|
@ -26,7 +26,7 @@ class PromoBannerDisplayDTOConverterTest {
|
||||||
|
|
||||||
val result = converter.convert(dto)
|
val result = converter.convert(dto)
|
||||||
|
|
||||||
assertThat(result.id).isEqualTo("123")
|
assertThat(result.id).isEqualTo(123)
|
||||||
assertThat(result.placeholder).isEqualTo("MAIN")
|
assertThat(result.placeholder).isEqualTo("MAIN")
|
||||||
assertThat(result.priority).isEqualTo(PromoBannerPriority.HIGH)
|
assertThat(result.priority).isEqualTo(PromoBannerPriority.HIGH)
|
||||||
assertThat(result.title).isEqualTo("Test Banner")
|
assertThat(result.title).isEqualTo("Test Banner")
|
||||||
|
|
@ -63,7 +63,7 @@ class PromoBannerDisplayDTOConverterTest {
|
||||||
placeholder: String = "MAIN",
|
placeholder: String = "MAIN",
|
||||||
priority: String = "MEDIUM",
|
priority: String = "MEDIUM",
|
||||||
) = PromoBannerDisplayDTO(
|
) = PromoBannerDisplayDTO(
|
||||||
id = "1",
|
id = 1,
|
||||||
placeholder = placeholder,
|
placeholder = placeholder,
|
||||||
priority = priority,
|
priority = priority,
|
||||||
title = "Title",
|
title = "Title",
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,14 @@ class PromoBannerDisplayToNotificationConverterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `should convert banner with all fields`() {
|
fun `should convert banner with all fields`() {
|
||||||
val banner = createBanner(
|
val banner = createBanner(
|
||||||
id = "b1",
|
id = 1,
|
||||||
deeplink = "tangem://wallet",
|
deeplink = "tangem://wallet",
|
||||||
isButtonEnabled = true,
|
isButtonEnabled = true,
|
||||||
buttonText = "Open",
|
buttonText = "Open",
|
||||||
isDismissable = true,
|
isDismissable = true,
|
||||||
)
|
)
|
||||||
var clickedDeeplink: String? = "not_called"
|
var clickedDeeplink: String? = "not_called"
|
||||||
var dismissedId: String? = null
|
var dismissedId: Int? = null
|
||||||
|
|
||||||
val result = converter.convert(
|
val result = converter.convert(
|
||||||
banner = banner,
|
banner = banner,
|
||||||
|
|
@ -28,7 +28,7 @@ class PromoBannerDisplayToNotificationConverterTest {
|
||||||
onDismiss = { dismissedId = it },
|
onDismiss = { dismissedId = it },
|
||||||
)
|
)
|
||||||
|
|
||||||
assertThat(result.displayId).isEqualTo("b1")
|
assertThat(result.displayId).isEqualTo(1)
|
||||||
assertThat(result.config.title).isEqualTo(TextReference.Str("Title"))
|
assertThat(result.config.title).isEqualTo(TextReference.Str("Title"))
|
||||||
assertThat(result.config.subtitle).isEqualTo(TextReference.Str("Subtitle"))
|
assertThat(result.config.subtitle).isEqualTo(TextReference.Str("Subtitle"))
|
||||||
assertThat(result.config.iconUrl).isEqualTo("https://icon.png")
|
assertThat(result.config.iconUrl).isEqualTo("https://icon.png")
|
||||||
|
|
@ -42,7 +42,7 @@ class PromoBannerDisplayToNotificationConverterTest {
|
||||||
assertThat(clickedDeeplink).isEqualTo("tangem://wallet")
|
assertThat(clickedDeeplink).isEqualTo("tangem://wallet")
|
||||||
|
|
||||||
result.config.onCloseClick!!.invoke()
|
result.config.onCloseClick!!.invoke()
|
||||||
assertThat(dismissedId).isEqualTo("b1")
|
assertThat(dismissedId).isEqualTo(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -98,7 +98,7 @@ class PromoBannerDisplayToNotificationConverterTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createBanner(
|
private fun createBanner(
|
||||||
id: String = "id",
|
id: Int = 0,
|
||||||
deeplink: String? = "https://example.com",
|
deeplink: String? = "https://example.com",
|
||||||
isButtonEnabled: Boolean = false,
|
isButtonEnabled: Boolean = false,
|
||||||
buttonText: String? = null,
|
buttonText: String? = null,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue