Updated on 2026-08-14

This commit is contained in:
Tangem 2025-06-27 12:47:02 +04:00
parent 70ee620507
commit 51efb0e781
8 changed files with 101 additions and 59 deletions

View file

@ -12,7 +12,9 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.vectorResource
@ -178,32 +180,42 @@ private fun CardImage(imageState: UserWalletItemUM.ImageState, modifier: Modifie
)
}
is UserWalletItemUM.ImageState.Image -> {
SubcomposeAsyncImage(
modifier = imageModifier,
model = ImageRequest.Builder(LocalContext.current)
.size(
width = with(LocalDensity.current) { TangemTheme.dimens.size36.roundToPx() },
height = with(LocalDensity.current) { TangemTheme.dimens.size24.roundToPx() },
)
.data(imageState.artwork.verifiedArtwork?.toByteArray() ?: imageState.artwork.defaultUrl)
.crossfade(enable = true)
.allowHardware(enable = false)
.build(),
loading = {
RectangleShimmer(
modifier = imageModifier,
radius = TangemTheme.dimens.size2,
)
},
error = {
Image(
modifier = imageModifier.then(Modifier.rotate(90F)),
imageVector = ImageVector.vectorResource(R.drawable.img_card_wallet_2_gray_22_36),
contentDescription = null,
)
},
contentDescription = null,
)
val verifiedArtwork = imageState.artwork.verifiedArtwork
if (verifiedArtwork != null) {
Image(
modifier = imageModifier,
painter = BitmapPainter(verifiedArtwork),
contentScale = ContentScale.Fit,
contentDescription = null,
)
} else {
SubcomposeAsyncImage(
modifier = imageModifier,
model = ImageRequest.Builder(LocalContext.current)
.size(
width = with(LocalDensity.current) { TangemTheme.dimens.size36.roundToPx() },
height = with(LocalDensity.current) { TangemTheme.dimens.size24.roundToPx() },
)
.data(imageState.artwork.defaultUrl)
.crossfade(enable = true)
.allowHardware(enable = false)
.build(),
loading = {
RectangleShimmer(
modifier = imageModifier,
radius = TangemTheme.dimens.size2,
)
},
error = {
Image(
modifier = imageModifier.then(Modifier.rotate(90F)),
imageVector = ImageVector.vectorResource(R.drawable.img_card_wallet_2_gray_22_36),
contentDescription = null,
)
},
contentDescription = null,
)
}
}
}
}

View file

@ -0,0 +1,23 @@
package com.tangem.common.ui.userwallet.converter
import android.graphics.BitmapFactory
import androidx.compose.ui.graphics.asImageBitmap
import com.tangem.core.ui.components.artwork.ArtworkUM
import com.tangem.domain.models.ArtworkModel
import com.tangem.utils.converter.Converter
import javax.inject.Inject
class ArtworkUMConverter @Inject constructor() : Converter<ArtworkModel, ArtworkUM> {
override fun convert(value: ArtworkModel): ArtworkUM {
val bimap = try {
value.verifiedArtwork?.let { BitmapFactory.decodeByteArray(it, 0, it.size) }
} catch (ignore: Exception) {
null
}
return ArtworkUM(
verifiedArtwork = bimap?.asImageBitmap(),
defaultUrl = value.defaultUrl,
)
}
}

View file

@ -2,7 +2,6 @@ package com.tangem.common.ui.userwallet.converter
import com.tangem.common.ui.R
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.ui.components.artwork.ArtworkUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
@ -38,6 +37,8 @@ class UserWalletItemUMConverter(
private val artwork: ArtworkModel? = null,
) : Converter<UserWallet, UserWalletItemUM> {
private val artworkUMConverter = ArtworkUMConverter()
override fun convert(value: UserWallet): UserWalletItemUM {
return with(value) {
UserWalletItemUM(
@ -49,7 +50,7 @@ class UserWalletItemUMConverter(
endIcon = endIcon,
onClick = { onClick(value.walletId) },
imageState = artwork?.let {
UserWalletItemUM.ImageState.Image(ArtworkUM(it.verifiedArtwork, it.defaultUrl))
UserWalletItemUM.ImageState.Image(artworkUMConverter.convert(it))
} ?: UserWalletItemUM.ImageState.Loading,
)
}

View file

@ -1,17 +1,10 @@
package com.tangem.core.ui.components.artwork
import androidx.compose.runtime.Immutable
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import androidx.compose.ui.graphics.ImageBitmap
@Immutable
data class ArtworkUM(
val verifiedArtwork: ImmutableList<Byte>? = null,
val verifiedArtwork: ImageBitmap? = null,
val defaultUrl: String,
) {
constructor(bytes: ByteArray?, defaultUrl: String) : this(
verifiedArtwork = bytes?.toList()?.toImmutableList(),
defaultUrl = defaultUrl,
)
}
)

View file

@ -1,7 +1,9 @@
package com.tangem.features.onboarding.v2.common.ui
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
@ -12,16 +14,26 @@ import com.tangem.features.onboarding.v2.impl.R
@Composable
internal fun WalletCard(artwork: ArtworkUM?, modifier: Modifier = Modifier) {
AsyncImage(
modifier = modifier,
model = ImageRequest.Builder(LocalContext.current)
.data(artwork?.verifiedArtwork?.toByteArray() ?: artwork?.defaultUrl)
.crossfade(true)
.build(),
placeholder = painterResource(R.drawable.card_placeholder_black),
error = painterResource(R.drawable.card_placeholder_black),
fallback = painterResource(R.drawable.card_placeholder_black),
contentScale = ContentScale.Fit,
contentDescription = null,
)
val verifiedArtwork = artwork?.verifiedArtwork
if (verifiedArtwork != null) {
Image(
modifier = modifier,
painter = BitmapPainter(verifiedArtwork),
contentScale = ContentScale.Fit,
contentDescription = null,
)
} else {
AsyncImage(
modifier = modifier,
model = ImageRequest.Builder(LocalContext.current)
.data(artwork?.defaultUrl)
.crossfade(true)
.build(),
placeholder = painterResource(R.drawable.card_placeholder_black),
error = painterResource(R.drawable.card_placeholder_black),
fallback = painterResource(R.drawable.card_placeholder_black),
contentScale = ContentScale.Fit,
contentDescription = null,
)
}
}

View file

@ -1,19 +1,19 @@
package com.tangem.features.onboarding.v2.multiwallet.impl.model
import com.tangem.common.ui.userwallet.converter.ArtworkUMConverter
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.components.artwork.ArtworkUM
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ProductType
import com.tangem.domain.onboarding.repository.OnboardingRepository
import com.tangem.domain.wallets.usecase.GetCardImageUseCase
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
import com.tangem.features.onboarding.v2.common.analytics.OnboardingEvent
import com.tangem.features.onboarding.v2.common.ui.interruptBackupDialog
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletState.FinalizeStage
import com.tangem.features.onboarding.v2.multiwallet.impl.ui.state.OnboardingMultiWalletUM
@ -39,6 +39,7 @@ internal class OnboardingMultiWalletModel @Inject constructor(
private val onboardingRepository: OnboardingRepository,
private val getCardImageUseCase: GetCardImageUseCase,
private val uiMessageSender: UiMessageSender,
private val artworkUMConverter: ArtworkUMConverter,
) : Model() {
private val params = paramsContainer.require<OnboardingMultiWalletComponent.Params>()
private val _uiState = MutableStateFlow(OnboardingMultiWalletUM())
@ -90,7 +91,7 @@ internal class OnboardingMultiWalletModel @Inject constructor(
firmwareVersion = backup.card2.firmwareVersion,
)
_uiState.update {
it.copy(artwork2 = ArtworkUM(artwork.verifiedArtwork, artwork.defaultUrl))
it.copy(artwork2 = artworkUMConverter.convert(artwork))
}
}
_uiState.value.artwork3 == null && backup.card3 != null -> {
@ -103,7 +104,7 @@ internal class OnboardingMultiWalletModel @Inject constructor(
firmwareVersion = backup.card3.firmwareVersion,
)
_uiState.update {
it.copy(artwork3 = ArtworkUM(artwork.verifiedArtwork, artwork.defaultUrl))
it.copy(artwork3 = artworkUMConverter.convert(artwork))
}
}
}
@ -179,7 +180,7 @@ internal class OnboardingMultiWalletModel @Inject constructor(
)
_uiState.update {
it.copy(artwork1 = ArtworkUM(artwork.verifiedArtwork, artwork.defaultUrl))
it.copy(artwork1 = artworkUMConverter.convert(artwork))
}
}
}

View file

@ -22,7 +22,6 @@ import com.tangem.features.onboarding.v2.common.ui.RefreshButton
import com.tangem.features.onboarding.v2.common.ui.WalletCard
import com.tangem.features.onboarding.v2.impl.R
import com.valentinilk.shimmer.shimmer
import kotlinx.collections.immutable.ImmutableList
@Composable
fun OnboardingNoteTopUpHeader(
@ -95,7 +94,7 @@ private fun OnboardinNoteTopUpHeaderPreview() {
TangemThemePreview {
OnboardingNoteTopUpHeader(
balance = "0.00000001 BTC",
cardArtwork = ArtworkUM(null as ImmutableList<Byte>?, ""),
cardArtwork = ArtworkUM(null, ""),
onRefreshBalanceClick = {},
isRefreshing = false,
)

View file

@ -1,12 +1,12 @@
package com.tangem.features.onboarding.v2.note.impl.model
import com.arkivanov.decompose.router.stack.StackNavigation
import com.tangem.common.ui.userwallet.converter.ArtworkUMConverter
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.components.artwork.ArtworkUM
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.GetCardImageUseCase
import com.tangem.features.onboarding.v2.common.ui.exitOnboardingDialog
@ -29,6 +29,7 @@ internal class OnboardingNoteModel @Inject constructor(
private val router: Router,
private val messageSender: UiMessageSender,
private val getCardImageUseCase: GetCardImageUseCase,
private val artworkUMConverter: ArtworkUMConverter,
) : Model() {
@Suppress("UnusedPrivateMember")
@ -98,7 +99,7 @@ internal class OnboardingNoteModel @Inject constructor(
firmwareVersion = cardInfo.firmwareVersion.toSdkFirmwareVersion(),
)
_uiState.update {
it.copy(cardArtwork = ArtworkUM(artwork.verifiedArtwork, artwork.defaultUrl))
it.copy(cardArtwork = artworkUMConverter.convert(artwork))
}
}
}