Updated on 2026-08-14

This commit is contained in:
Tangem 2025-02-10 11:58:43 +05:00
commit b03396d7b1
44 changed files with 569 additions and 92 deletions

View file

@ -341,4 +341,12 @@ sealed class AppRoute(val path: String) : Route {
AddBackup, // continue backup process for existing wallet 1
}
}
@Serializable
data class Stories(
val storyId: String,
val nextScreen: AppRoute,
) : AppRoute(path = "/stories$storyId"), RouteBundleParams {
override fun getBundle(): Bundle = bundle(serializer())
}
}

View file

@ -36,6 +36,7 @@ dependencies {
implementation(projects.domain.transaction.models)
implementation(projects.domain.wallets.models)
implementation(projects.domain.onramp.models)
implementation(projects.domain.promo.models)
implementation(deps.tangem.card.core)
implementation(deps.tangem.blockchain) {

View file

@ -0,0 +1,48 @@
package com.tangem.common.ui.swapStoriesScreen
import com.tangem.common.ui.R
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.promo.models.StoryContent
import kotlinx.collections.immutable.persistentListOf
object SwapStoriesFactory {
// WARNING! Be careful with indices. Temporary solution.
// Use all data from v1/stories api (image url, title, subtitle)
@Suppress("MagicNumber")
fun createStoriesState(swapStory: StoryContent, onStoriesClose: () -> Unit): SwapStoriesUM {
val storyOrderedImageUrls = swapStory.getImageUrls()
if (storyOrderedImageUrls.size != 5) return SwapStoriesUM.Empty
return SwapStoriesUM.Content(
stories = persistentListOf(
SwapStoriesUM.Content.Config(
imageUrl = storyOrderedImageUrls[0],
title = resourceReference(R.string.swap_story_first_title),
subtitle = resourceReference(R.string.swap_story_first_subtitle),
),
SwapStoriesUM.Content.Config(
imageUrl = storyOrderedImageUrls[1],
title = resourceReference(R.string.swap_story_second_title),
subtitle = resourceReference(R.string.swap_story_second_subtitle),
),
SwapStoriesUM.Content.Config(
imageUrl = storyOrderedImageUrls[2],
title = resourceReference(R.string.swap_story_third_title),
subtitle = resourceReference(R.string.swap_story_third_subtitle),
),
SwapStoriesUM.Content.Config(
imageUrl = storyOrderedImageUrls[3],
title = resourceReference(R.string.swap_story_forth_title),
subtitle = resourceReference(R.string.swap_story_forth_subtitle),
),
SwapStoriesUM.Content.Config(
imageUrl = storyOrderedImageUrls[4],
title = resourceReference(R.string.swap_story_fifth_title),
subtitle = resourceReference(R.string.swap_story_fifth_subtitle),
),
),
onClose = onStoriesClose,
)
}
}

View file

@ -0,0 +1,124 @@
package com.tangem.common.ui.swapStoriesScreen
import android.content.res.Configuration
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.TextUnitType
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil.compose.SubcomposeAsyncImage
import coil.request.ImageRequest
import com.tangem.core.ui.components.SpacerH16
import com.tangem.core.ui.components.SystemBarsIconsDisposable
import com.tangem.core.ui.components.stories.StoriesContainer
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.LocalWindowSize
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import kotlinx.collections.immutable.persistentListOf
private val SubtitleColor = Color(0xFF868692)
private const val STORIES_RELATIVE_PADDING = 0.7
@Composable
fun SwapStoriesScreen(config: SwapStoriesUM) {
if (config !is SwapStoriesUM.Content) return
BackHandler(onBack = config.onClose)
SystemBarsIconsDisposable(darkIcons = false)
StoriesContainer(
config = config,
) { current, _ ->
Box(
modifier = Modifier
.fillMaxSize()
.background(TangemColorPalette.Black),
) {
SubcomposeAsyncImage(
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop,
model = ImageRequest.Builder(context = LocalContext.current)
.data(current.imageUrl)
.crossfade(enable = false)
.allowHardware(true)
.build(),
loading = { },
error = { },
contentDescription = null,
)
val height = LocalWindowSize.current.height.value
val textAlign = (height.dp.value * STORIES_RELATIVE_PADDING).dp
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.padding(
top = textAlign,
start = 56.dp,
end = 56.dp,
),
) {
Text(
text = current.title.resolveReference(),
style = TangemTheme.typography.head,
color = TangemColorPalette.SoftWhite,
textAlign = TextAlign.Center,
)
SpacerH16()
Text(
text = current.subtitle.resolveReference(),
style = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
letterSpacing = TextUnit(value = 0.1f, type = TextUnitType.Sp),
lineHeight = TextUnit(value = 20f, type = TextUnitType.Sp),
),
color = SubtitleColor,
textAlign = TextAlign.Center,
)
}
}
}
}
// region Preview
@Preview(showBackground = true, widthDp = 360, heightDp = 720)
@Preview(showBackground = true, widthDp = 360, heightDp = 720, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun SwapStoriesScreen_Preview() {
TangemThemePreview {
SwapStoriesScreen(
SwapStoriesUM.Content(
stories = persistentListOf(
SwapStoriesUM.Content.Config(
imageUrl = "https://devweb.tangem.com/images/stories/swap/image1.png",
title = stringReference("Exchange With Us"),
subtitle = stringReference(
"Trusted exchange providers let you swap assets effortlessly",
),
),
),
onClose = {},
),
)
}
}
// endregion

View file

@ -0,0 +1,26 @@
package com.tangem.common.ui.swapStoriesScreen
import com.tangem.core.ui.components.stories.model.StoriesContentConfig
import com.tangem.core.ui.components.stories.model.StoryConfig
import com.tangem.core.ui.extensions.TextReference
import kotlinx.collections.immutable.ImmutableList
sealed class SwapStoriesUM {
data object Empty : SwapStoriesUM()
data class Content(
override val stories: ImmutableList<Config>,
override val onClose: () -> Unit,
) : SwapStoriesUM(), StoriesContentConfig<Content.Config> {
override val isRestartable: Boolean = false
data class Config(
val imageUrl: String,
val title: TextReference,
val subtitle: TextReference,
) : StoryConfig {
override val duration: Int = 2000
}
}
}