Updated on 2026-08-14
This commit is contained in:
parent
febd32b964
commit
973109c974
13 changed files with 299 additions and 0 deletions
|
|
@ -35,6 +35,8 @@ import com.tangem.feature.tester.presentation.menu.ui.TesterMenuScreen
|
|||
import com.tangem.feature.tester.presentation.navigation.InnerTesterRouter
|
||||
import com.tangem.feature.tester.presentation.navigation.TesterScreen
|
||||
import com.tangem.feature.tester.presentation.providers.ui.BlockchainProvidersScreen
|
||||
import com.tangem.feature.tester.presentation.storybook.ui.StoryBookScreen
|
||||
import com.tangem.feature.tester.presentation.storybook.viewmodel.StoryBookViewModel
|
||||
import com.tangem.feature.tester.presentation.providers.viewmodel.BlockchainProvidersViewModel
|
||||
import com.tangem.feature.tester.presentation.testpush.ui.TestPushScreen
|
||||
import com.tangem.feature.tester.presentation.testpush.viewmodel.TestPushViewModel
|
||||
|
|
@ -86,6 +88,7 @@ internal class TesterActivity : ComposeActivity() {
|
|||
ButtonUM.TEST_PUSHES,
|
||||
ButtonUM.ACCOUNTS,
|
||||
ButtonUM.ADDRESSES_INFO,
|
||||
ButtonUM.STORY_BOOK,
|
||||
),
|
||||
onButtonClick = { buttonUM ->
|
||||
val route = when (buttonUM) {
|
||||
|
|
@ -97,6 +100,7 @@ internal class TesterActivity : ComposeActivity() {
|
|||
ButtonUM.TEST_PUSHES -> TesterScreen.TEST_PUSHES
|
||||
ButtonUM.ACCOUNTS -> TesterScreen.ACCOUNTS
|
||||
ButtonUM.ADDRESSES_INFO -> TesterScreen.ADDRESSES_INFO
|
||||
ButtonUM.STORY_BOOK -> TesterScreen.STORY_BOOK
|
||||
}
|
||||
|
||||
innerTesterRouter.open(route)
|
||||
|
|
@ -179,6 +183,15 @@ internal class TesterActivity : ComposeActivity() {
|
|||
|
||||
AddressesInfoScreen(state)
|
||||
}
|
||||
|
||||
composable(route = TesterScreen.STORY_BOOK.name) {
|
||||
val viewModel = hiltViewModel<StoryBookViewModel>().apply {
|
||||
setupNavigation(innerTesterRouter)
|
||||
}
|
||||
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
StoryBookScreen(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -26,5 +26,6 @@ data class TesterMenuUM(
|
|||
TEST_PUSHES(R.string.test_push),
|
||||
ACCOUNTS(R.string.accounts),
|
||||
ADDRESSES_INFO(R.string.addresses_info),
|
||||
STORY_BOOK(R.string.story_book),
|
||||
}
|
||||
}
|
||||
|
|
@ -15,4 +15,5 @@ internal enum class TesterScreen {
|
|||
TEST_PUSHES,
|
||||
ACCOUNTS,
|
||||
ADDRESSES_INFO,
|
||||
STORY_BOOK,
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.feature.tester.presentation.storybook.entity
|
||||
|
||||
internal sealed interface StoryBookPage
|
||||
|
||||
internal data object StoryList : StoryBookPage
|
||||
|
||||
internal data class NorthernLightsStory(
|
||||
val variant: Variant,
|
||||
val onVariantChange: (Variant) -> Unit,
|
||||
) : StoryBookPage {
|
||||
enum class Variant {
|
||||
Shader,
|
||||
Simple,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.tangem.feature.tester.presentation.storybook.entity
|
||||
|
||||
internal data class StoryBookUM(
|
||||
val currentPage: StoryBookPage = StoryList,
|
||||
val onBackClick: () -> Unit,
|
||||
val onStoryClick: (StoryPageFactory) -> Unit,
|
||||
)
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.feature.tester.presentation.storybook.entity
|
||||
|
||||
internal fun interface StoryPageFactory {
|
||||
fun create(updatePage: ((StoryBookPage) -> StoryBookPage) -> Unit): StoryBookPage
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.feature.tester.presentation.storybook.page.background
|
||||
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.NorthernLightsStory
|
||||
import com.tangem.feature.tester.presentation.storybook.viewmodel.StateUpdater
|
||||
import com.tangem.feature.tester.presentation.storybook.viewmodel.storyPageFactory
|
||||
|
||||
internal fun StateUpdater<NorthernLightsStory>.build(): NorthernLightsStory {
|
||||
return NorthernLightsStory(
|
||||
variant = NorthernLightsStory.Variant.Shader,
|
||||
onVariantChange = { newVariant ->
|
||||
updateStory { currentState ->
|
||||
currentState.copy(variant = newVariant)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
internal val northernLightsStoryFactory
|
||||
get() = storyPageFactory(StateUpdater<NorthernLightsStory>::build)
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
@file:Suppress("MagicNumber")
|
||||
package com.tangem.feature.tester.presentation.storybook.page.background
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.core.ui.components.background.northernlights.NorthernLightsBackground
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.NorthernLightsStory
|
||||
|
||||
@Composable
|
||||
internal fun NorthernLightsStory(state: NorthernLightsStory, modifier: Modifier = Modifier) {
|
||||
Box(modifier = modifier.fillMaxSize()) {
|
||||
NorthernLightsBackground(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
forceSimpleVersion = state.variant == NorthernLightsStory.Variant.Simple,
|
||||
)
|
||||
|
||||
NorthernLightsVariantToggle(
|
||||
selected = state.variant,
|
||||
onSelect = state.onVariantChange,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.navigationBarsPadding()
|
||||
.padding(bottom = 24.dp)
|
||||
.padding(horizontal = 24.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NorthernLightsVariantToggle(
|
||||
selected: NorthernLightsStory.Variant,
|
||||
onSelect: (NorthernLightsStory.Variant) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val shape = RoundedCornerShape(50)
|
||||
Row(
|
||||
modifier = modifier
|
||||
.clip(shape)
|
||||
.background(Color.Black.copy(alpha = 0.35f))
|
||||
.border(width = 1.dp, color = Color.White.copy(alpha = 0.15f), shape = shape)
|
||||
.padding(4.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
NorthernLightsStory.Variant.entries.forEach { variant ->
|
||||
VariantChip(
|
||||
label = variant.label,
|
||||
selected = variant == selected,
|
||||
onClick = { onSelect(variant) },
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun VariantChip(label: String, selected: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
val chipShape = RoundedCornerShape(50)
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = modifier
|
||||
.clip(chipShape)
|
||||
.background(if (selected) Color.White.copy(alpha = 0.2f) else Color.Transparent)
|
||||
.clickable(onClick = onClick)
|
||||
.padding(vertical = 10.dp, horizontal = 16.dp),
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
color = Color.White,
|
||||
fontSize = 14.sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val NorthernLightsStory.Variant.label: String
|
||||
get() = when (this) {
|
||||
NorthernLightsStory.Variant.Shader -> "Shader"
|
||||
NorthernLightsStory.Variant.Simple -> "Simple"
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package com.tangem.feature.tester.presentation.storybook.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.StoryBookUM
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.StoryPageFactory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.background.northernLightsStoryFactory
|
||||
|
||||
private data class StoryItem(val title: String, val factory: StoryPageFactory)
|
||||
|
||||
private fun buildStories() = listOf(
|
||||
StoryItem(title = "Northern Lights Background", factory = northernLightsStoryFactory),
|
||||
)
|
||||
|
||||
@Composable
|
||||
internal fun StoryBookListScreen(state: StoryBookUM, modifier: Modifier = Modifier) {
|
||||
val stories = remember { buildStories() }
|
||||
|
||||
LazyColumn(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.background(TangemTheme.colors.background.primary),
|
||||
) {
|
||||
stickyHeader {
|
||||
AppBarWithBackButton(
|
||||
onBackClick = state.onBackClick,
|
||||
text = "Storybook",
|
||||
containerColor = TangemTheme.colors.background.primary,
|
||||
)
|
||||
}
|
||||
|
||||
items(items = stories, key = { it.title }) { item ->
|
||||
PrimaryButton(
|
||||
text = item.title,
|
||||
onClick = { state.onStoryClick(item.factory) },
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.feature.tester.presentation.storybook.ui
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.NorthernLightsStory
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.StoryBookUM
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.StoryList
|
||||
import com.tangem.feature.tester.presentation.storybook.page.background.NorthernLightsStory
|
||||
|
||||
@Composable
|
||||
internal fun StoryBookScreen(state: StoryBookUM, modifier: Modifier = Modifier) {
|
||||
BackHandler(onBack = state.onBackClick)
|
||||
|
||||
AnimatedContent(
|
||||
targetState = state.currentPage,
|
||||
modifier = modifier,
|
||||
) { storyState ->
|
||||
when (storyState) {
|
||||
StoryList -> StoryBookListScreen(state = state)
|
||||
is NorthernLightsStory -> NorthernLightsStory(state = storyState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.feature.tester.presentation.storybook.viewmodel
|
||||
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.StoryBookPage
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.StoryPageFactory
|
||||
|
||||
internal interface StateUpdater<T> {
|
||||
fun updateStory(update: (T) -> T)
|
||||
}
|
||||
|
||||
internal inline fun <reified T : StoryBookPage> storyPageFactory(
|
||||
crossinline build: StateUpdater<T>.() -> T,
|
||||
): StoryPageFactory = StoryPageFactory { updatePage ->
|
||||
val updater = object : StateUpdater<T> {
|
||||
override fun updateStory(update: (T) -> T) {
|
||||
updatePage { current ->
|
||||
if (current is T) update(current) else current
|
||||
}
|
||||
}
|
||||
}
|
||||
updater.build()
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package com.tangem.feature.tester.presentation.storybook.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.tangem.feature.tester.presentation.navigation.InnerTesterRouter
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.StoryBookUM
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.StoryList
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.StoryPageFactory
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
internal class StoryBookViewModel @Inject constructor() : ViewModel() {
|
||||
|
||||
private var router: InnerTesterRouter? = null
|
||||
|
||||
private val _uiState = MutableStateFlow(
|
||||
StoryBookUM(
|
||||
onBackClick = ::onBackClick,
|
||||
onStoryClick = ::onStoryClick,
|
||||
),
|
||||
)
|
||||
val uiState: StateFlow<StoryBookUM> = _uiState.asStateFlow()
|
||||
|
||||
fun setupNavigation(router: InnerTesterRouter) {
|
||||
this.router = router
|
||||
}
|
||||
|
||||
private fun onBackClick() {
|
||||
if (_uiState.value.currentPage !is StoryList) {
|
||||
_uiState.update { it.copy(currentPage = StoryList) }
|
||||
} else {
|
||||
router?.back()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onStoryClick(factory: StoryPageFactory) {
|
||||
_uiState.update { state ->
|
||||
state.copy(
|
||||
currentPage = factory.create { update ->
|
||||
_uiState.update { s -> s.copy(currentPage = update(s.currentPage)) }
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,4 +22,5 @@
|
|||
<string name="news_details" translatable="false">News details</string>
|
||||
<string name="news_details_bottom_sheet" translatable="false">News details (Bottom Sheet)</string>
|
||||
<string name="addresses_info" translatable="false">Addresses info</string>
|
||||
<string name="story_book" translatable="false">Story book</string>
|
||||
</resources>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue