Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-17 13:10:09 +02:00
parent 8beb80f5a7
commit 43d6e92a89
26 changed files with 639 additions and 8 deletions

View file

@ -59,5 +59,9 @@
{
"name": "MAIN_SCREEN_QR_SCANNING_ENABLED",
"version": "undefined"
},
{
"name": "NEW_PROMO_BANNERS_ENABLED",
"version": "undefined"
}
]

View file

@ -5,6 +5,9 @@ import com.tangem.datasource.api.promotion.models.PromoBannerResponse
import com.tangem.datasource.api.promotion.models.PromoBannerV2Response
import com.tangem.datasource.api.promotion.models.StoryContentResponse
import com.tangem.datasource.api.tangemTech.models.*
import com.tangem.datasource.api.tangemTech.models.promobanners.PromoBannerDisplaysResponse
import com.tangem.datasource.api.tangemTech.models.promobanners.DismissPromoBannerRequest
import com.tangem.datasource.api.tangemTech.models.promobanners.DismissPromoBannerResponse
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
import com.tangem.datasource.api.tangemTech.models.account.GetWalletArchivedAccountsResponse
import com.tangem.datasource.api.tangemTech.models.account.SaveWalletAccountsResponse
@ -197,6 +200,21 @@ interface TangemTechApi {
): ApiResponse<PromoBannerV2Response>
// endregion
// region promo banners
@GET("v1/banner/displays")
suspend fun getPromoBannerDisplays(
@Query("walletId") walletId: String,
@Query("placeholder") placeholder: String,
@Query("locale") locale: String,
): ApiResponse<PromoBannerDisplaysResponse>
@PATCH("v1/displays/{displayId}")
suspend fun dismissPromoBannerDisplay(
@Path("displayId") displayId: String,
@Body body: DismissPromoBannerRequest,
): ApiResponse<DismissPromoBannerResponse>
// endregion
/**
* Stores transaction hash in cache to prevent duplicate push
* notifications for yield operations (deposit, withdraw, send).

View file

@ -0,0 +1,10 @@
package com.tangem.datasource.api.tangemTech.models.promobanners
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class DismissPromoBannerRequest(
@Json(name = "walletId") val walletId: String,
@Json(name = "isDismissed") val isDismissed: Boolean,
)

View file

@ -0,0 +1,12 @@
package com.tangem.datasource.api.tangemTech.models.promobanners
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class DismissPromoBannerResponse(
@Json(name = "displayId") val displayId: String,
@Json(name = "walletId") val walletId: String,
@Json(name = "isDismissed") val isDismissed: Boolean,
@Json(name = "dismissedAt") val dismissedAt: String?,
)

View file

@ -0,0 +1,24 @@
package com.tangem.datasource.api.tangemTech.models.promobanners
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class PromoBannerDisplaysResponse(
@Json(name = "items") val items: List<PromoBannerDisplayDTO>,
)
@Suppress("BooleanPropertyNaming")
@JsonClass(generateAdapter = true)
data class PromoBannerDisplayDTO(
@Json(name = "id") val id: String,
@Json(name = "placeholder") val placeholder: String,
@Json(name = "priority") val priority: String,
@Json(name = "title") val title: String,
@Json(name = "subtitle") 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,
)

View file

@ -2,6 +2,7 @@ package com.tangem.core.ui.components.notifications
import android.content.res.Configuration
import androidx.annotation.DrawableRes
import coil.compose.SubcomposeAsyncImage
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.LocalIndication
@ -86,6 +87,7 @@ fun Notification(
subtitleColor = subtitleColor,
showArrowIcon = isEnabled && config.shouldShowArrowIcon,
hasCloseButton = config.onCloseClick != null,
iconUrl = config.iconUrl,
)
}
}
@ -150,16 +152,29 @@ private fun MainContent(
subtitleColor: Color,
showArrowIcon: Boolean,
hasCloseButton: Boolean,
iconUrl: String? = null,
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
iconResId = iconResId,
tint = iconTint,
modifier = Modifier
.size(size = iconSize)
.align(alignment = Alignment.Top)
.testTag(NotificationTestTags.ICON),
)
if (iconUrl != null) {
UrlIcon(
iconUrl = iconUrl,
fallbackIconResId = iconResId,
fallbackIconTint = iconTint,
modifier = Modifier
.size(size = iconSize)
.align(alignment = Alignment.Top)
.testTag(NotificationTestTags.ICON),
)
} else {
Icon(
iconResId = iconResId,
tint = iconTint,
modifier = Modifier
.size(size = iconSize)
.align(alignment = Alignment.Top)
.testTag(NotificationTestTags.ICON),
)
}
SpacerW(width = TangemTheme.dimens.spacing10)
@ -212,6 +227,30 @@ private fun Icon(@DrawableRes iconResId: Int, tint: Color?, modifier: Modifier =
}
}
@Composable
private fun UrlIcon(
iconUrl: String,
@DrawableRes fallbackIconResId: Int,
fallbackIconTint: Color?,
modifier: Modifier = Modifier,
) {
SubcomposeAsyncImage(
model = iconUrl,
contentDescription = null,
modifier = modifier.clip(CircleShape),
loading = {
CircleShimmer(modifier = Modifier.matchParentSize())
},
error = {
Icon(
iconResId = fallbackIconResId,
tint = fallbackIconTint,
modifier = Modifier.matchParentSize(),
)
},
)
}
@Composable
internal fun TextsBlock(
title: TextReference?,

View file

@ -10,6 +10,7 @@ import com.tangem.core.ui.extensions.TextReference
*
* @property subtitle subtitle
* @property iconResId icon resource id
* @property iconUrl icon URL for remote images
* @property title title
* @property backgroundResId background resource id
* @property buttonsState buttons state
@ -21,6 +22,7 @@ import com.tangem.core.ui.extensions.TextReference
data class NotificationConfig(
val subtitle: TextReference,
@DrawableRes val iconResId: Int,
val iconUrl: String? = null,
val title: TextReference? = null,
@DrawableRes val backgroundResId: Int? = null,
val buttonsState: ButtonsState? = null,