Updated on 2026-08-14
This commit is contained in:
commit
92849d43c2
1407 changed files with 64092 additions and 11990 deletions
|
|
@ -1,11 +1,13 @@
|
|||
package com.tangem.feature.tester.presentation.actions
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import java.io.File
|
||||
|
||||
internal data class TesterActionsContentState(
|
||||
val hideAllCurrenciesUM: HideAllCurrenciesUM,
|
||||
val toggleHotWalletRestrictionUM: ToggleHotWalletRestrictionUM,
|
||||
val usedeskTokenTtlUM: UsedeskTokenTtlUM,
|
||||
val shareLogsUM: ShareLogsUM,
|
||||
val onBackClick: () -> Unit,
|
||||
) {
|
||||
|
|
@ -21,6 +23,24 @@ internal data class TesterActionsContentState(
|
|||
val onClick: () -> Unit,
|
||||
)
|
||||
|
||||
/**
|
||||
* State of the Usedesk chat token TTL setting.
|
||||
*
|
||||
* @param currentLabel human-readable current TTL (e.g. "7 days", "15 minutes")
|
||||
* @param presets selectable preset durations
|
||||
* @param onPresetSelected applies one of the [presets] (value in milliseconds)
|
||||
* @param onCustomMinutesSelected applies a custom TTL entered in minutes
|
||||
*/
|
||||
@Immutable
|
||||
data class UsedeskTokenTtlUM(
|
||||
val currentLabel: String,
|
||||
val presets: ImmutableList<Preset>,
|
||||
val onPresetSelected: (Long) -> Unit,
|
||||
val onCustomMinutesSelected: (Long) -> Unit,
|
||||
) {
|
||||
data class Preset(val label: String, val millis: Long)
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class ShareLogsUM(val file: File?)
|
||||
}
|
||||
|
|
@ -8,15 +8,23 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.app.ShareCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.FileProvider
|
||||
import com.tangem.core.ui.components.AdditionalTextInputDialogUM
|
||||
import com.tangem.core.ui.components.DialogButtonUM
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.SelectorDialog
|
||||
import com.tangem.core.ui.components.TextInputDialog
|
||||
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -25,12 +33,19 @@ import com.tangem.core.ui.utils.findActivity
|
|||
import com.tangem.feature.tester.impl.R
|
||||
import com.tangem.feature.tester.presentation.actions.TesterActionsContentState.HideAllCurrenciesUM
|
||||
import com.tangem.feature.tester.presentation.actions.TesterActionsContentState.ToggleHotWalletRestrictionUM
|
||||
import com.tangem.feature.tester.presentation.actions.TesterActionsContentState.UsedeskTokenTtlUM
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import java.io.File
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
internal fun TesterActionsScreen(state: TesterActionsContentState, modifier: Modifier = Modifier) {
|
||||
var shouldShowTtlSelector by remember { mutableStateOf(value = false) }
|
||||
var shouldShowTtlCustomInput by remember { mutableStateOf(value = false) }
|
||||
|
||||
LazyColumn(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
|
|
@ -65,6 +80,13 @@ internal fun TesterActionsScreen(state: TesterActionsContentState, modifier: Mod
|
|||
)
|
||||
}
|
||||
|
||||
item {
|
||||
TesterActionItem(
|
||||
name = stringResourceSafe(id = R.string.usedesk_token_ttl, state.usedeskTokenTtlUM.currentLabel),
|
||||
onClick = { shouldShowTtlSelector = true },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
val activity = LocalContext.current.findActivity()
|
||||
|
||||
|
|
@ -75,6 +97,24 @@ internal fun TesterActionsScreen(state: TesterActionsContentState, modifier: Mod
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldShowTtlSelector) {
|
||||
UsedeskTtlSelectorDialog(
|
||||
um = state.usedeskTokenTtlUM,
|
||||
onCustomRequest = {
|
||||
shouldShowTtlSelector = false
|
||||
shouldShowTtlCustomInput = true
|
||||
},
|
||||
onDismiss = { shouldShowTtlSelector = false },
|
||||
)
|
||||
}
|
||||
|
||||
if (shouldShowTtlCustomInput) {
|
||||
UsedeskTtlCustomInputDialog(
|
||||
um = state.usedeskTokenTtlUM,
|
||||
onDismiss = { shouldShowTtlCustomInput = false },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -90,6 +130,62 @@ private fun TesterActionItem(name: String, onClick: () -> Unit, progress: Boolea
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UsedeskTtlSelectorDialog(um: UsedeskTokenTtlUM, onCustomRequest: () -> Unit, onDismiss: () -> Unit) {
|
||||
val customLabel = stringResourceSafe(R.string.usedesk_token_ttl_custom)
|
||||
val items = remember(um.presets, customLabel) {
|
||||
(um.presets.map(UsedeskTokenTtlUM.Preset::label) + customLabel).toImmutableList()
|
||||
}
|
||||
val selectedIndex = remember(um.presets, um.currentLabel) {
|
||||
um.presets.indexOfFirst { it.label == um.currentLabel }
|
||||
.takeIf { it >= 0 }
|
||||
?: um.presets.size
|
||||
}
|
||||
|
||||
SelectorDialog(
|
||||
title = stringResourceSafe(R.string.usedesk_token_ttl_title),
|
||||
items = items,
|
||||
selectedItemIndex = selectedIndex,
|
||||
confirmButton = DialogButtonUM(title = stringResourceSafe(id = R.string.common_close), onClick = onDismiss),
|
||||
onSelect = { index ->
|
||||
val preset = um.presets.getOrNull(index)
|
||||
if (preset != null) {
|
||||
um.onPresetSelected(preset.millis)
|
||||
onDismiss()
|
||||
} else {
|
||||
onCustomRequest()
|
||||
}
|
||||
},
|
||||
onDismissDialog = onDismiss,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UsedeskTtlCustomInputDialog(um: UsedeskTokenTtlUM, onDismiss: () -> Unit) {
|
||||
var value by remember { mutableStateOf(TextFieldValue()) }
|
||||
val minutes = value.text.toLongOrNull()
|
||||
val isValid = minutes != null && minutes > 0
|
||||
|
||||
TextInputDialog(
|
||||
title = stringResourceSafe(R.string.usedesk_token_ttl_title),
|
||||
fieldValue = value,
|
||||
onValueChange = { newValue -> value = newValue.copy(text = newValue.text.filter(Char::isDigit)) },
|
||||
textFieldParams = AdditionalTextInputDialogUM(
|
||||
label = stringResourceSafe(R.string.usedesk_token_ttl_custom),
|
||||
placeholder = "15",
|
||||
),
|
||||
confirmButton = DialogButtonUM(
|
||||
isEnabled = isValid,
|
||||
onClick = {
|
||||
if (isValid) um.onCustomMinutesSelected(minutes)
|
||||
onDismiss()
|
||||
},
|
||||
),
|
||||
dismissButton = DialogButtonUM(title = stringResourceSafe(id = R.string.common_close), onClick = onDismiss),
|
||||
onDismissDialog = onDismiss,
|
||||
)
|
||||
}
|
||||
|
||||
private fun Activity.shareFile(file: File?) {
|
||||
val originalIntent = createEmailShareIntent(activity = this, file = file)
|
||||
|
||||
|
|
@ -126,6 +222,15 @@ private fun TesterActionsScreenSample(modifier: Modifier = Modifier) {
|
|||
state = TesterActionsContentState(
|
||||
hideAllCurrenciesUM = HideAllCurrenciesUM.Clickable {},
|
||||
toggleHotWalletRestrictionUM = ToggleHotWalletRestrictionUM(isEnabled = true) {},
|
||||
usedeskTokenTtlUM = UsedeskTokenTtlUM(
|
||||
currentLabel = "7 days",
|
||||
presets = listOf(
|
||||
UsedeskTokenTtlUM.Preset(label = "7 days", millis = 7.days.inWholeMilliseconds),
|
||||
UsedeskTokenTtlUM.Preset(label = "15 minutes", millis = 15.minutes.inWholeMilliseconds),
|
||||
).toImmutableList(),
|
||||
onPresetSelected = {},
|
||||
onCustomMinutesSelected = {},
|
||||
),
|
||||
shareLogsUM = TesterActionsContentState.ShareLogsUM(file = null),
|
||||
onBackClick = {},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -9,14 +9,21 @@ import com.tangem.data.common.account.WalletAccountsSaver
|
|||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.feedback.repository.FeedbackRepository
|
||||
import com.tangem.domain.settings.HotWalletRestrictionManager
|
||||
import com.tangem.domain.settings.UsedeskTokenTtlManager
|
||||
import com.tangem.feature.tester.presentation.actions.TesterActionsContentState.HideAllCurrenciesUM
|
||||
import com.tangem.feature.tester.presentation.actions.TesterActionsContentState.ToggleHotWalletRestrictionUM
|
||||
import com.tangem.feature.tester.presentation.actions.TesterActionsContentState.UsedeskTokenTtlUM
|
||||
import com.tangem.feature.tester.presentation.navigation.InnerTesterRouter
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
|
||||
@HiltViewModel
|
||||
internal class TesterActionsViewModel @Inject constructor(
|
||||
|
|
@ -24,6 +31,7 @@ internal class TesterActionsViewModel @Inject constructor(
|
|||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val walletAccountsSaver: WalletAccountsSaver,
|
||||
private val hotWalletRestrictionManager: HotWalletRestrictionManager,
|
||||
private val usedeskTokenTtlManager: UsedeskTokenTtlManager,
|
||||
) : ViewModel() {
|
||||
|
||||
var uiState: TesterActionsContentState by mutableStateOf(initialState)
|
||||
|
|
@ -36,12 +44,19 @@ internal class TesterActionsViewModel @Inject constructor(
|
|||
isEnabled = hotWalletRestrictionManager.isCreationEnabledSync(),
|
||||
onClick = this::toggleHotWalletRestriction,
|
||||
),
|
||||
usedeskTokenTtlUM = UsedeskTokenTtlUM(
|
||||
currentLabel = formatTtl(usedeskTokenTtlManager.getTokenTtlMillisSync()),
|
||||
presets = TTL_PRESETS,
|
||||
onPresetSelected = this::setUsedeskTokenTtl,
|
||||
onCustomMinutesSelected = { minutes -> setUsedeskTokenTtl(minutes.minutes.inWholeMilliseconds) },
|
||||
),
|
||||
shareLogsUM = TesterActionsContentState.ShareLogsUM(file = feedbackRepository.getLogFile()),
|
||||
onBackClick = { /* no-op */ },
|
||||
)
|
||||
|
||||
init {
|
||||
bootstrapHotWalletRestrictionUpdates()
|
||||
bootstrapUsedeskTokenTtlUpdates()
|
||||
}
|
||||
|
||||
fun setupNavigation(router: InnerTesterRouter) {
|
||||
|
|
@ -76,6 +91,10 @@ internal class TesterActionsViewModel @Inject constructor(
|
|||
hotWalletRestrictionManager.toggleCreationEnabled()
|
||||
}
|
||||
|
||||
private fun setUsedeskTokenTtl(millis: Long) = viewModelScope.launch {
|
||||
usedeskTokenTtlManager.setTokenTtlMillis(millis)
|
||||
}
|
||||
|
||||
private fun bootstrapHotWalletRestrictionUpdates() {
|
||||
hotWalletRestrictionManager.isCreationEnabled()
|
||||
.onEach { isEnabled ->
|
||||
|
|
@ -87,4 +106,52 @@ internal class TesterActionsViewModel @Inject constructor(
|
|||
}
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
private fun bootstrapUsedeskTokenTtlUpdates() {
|
||||
usedeskTokenTtlManager.getTokenTtlMillis()
|
||||
.onEach { millis ->
|
||||
uiState = uiState.copy(
|
||||
usedeskTokenTtlUM = uiState.usedeskTokenTtlUM.copy(
|
||||
currentLabel = formatTtl(millis),
|
||||
),
|
||||
)
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
val TTL_PRESETS = persistentListOf(
|
||||
UsedeskTokenTtlUM.Preset(
|
||||
label = formatTtl(7.days.inWholeMilliseconds),
|
||||
millis = 7.days.inWholeMilliseconds,
|
||||
),
|
||||
UsedeskTokenTtlUM.Preset(
|
||||
label = formatTtl(1.days.inWholeMilliseconds),
|
||||
millis = 1.days.inWholeMilliseconds,
|
||||
),
|
||||
UsedeskTokenTtlUM.Preset(
|
||||
label = formatTtl(1.hours.inWholeMilliseconds),
|
||||
millis = 1.hours.inWholeMilliseconds,
|
||||
),
|
||||
UsedeskTokenTtlUM.Preset(
|
||||
label = formatTtl(15.minutes.inWholeMilliseconds),
|
||||
millis = 15.minutes.inWholeMilliseconds,
|
||||
),
|
||||
)
|
||||
|
||||
/** Picks the largest whole unit (days → hours → minutes → seconds) for a readable label. */
|
||||
fun formatTtl(millis: Long): String {
|
||||
val duration = millis.milliseconds
|
||||
return when {
|
||||
duration.inWholeDays >= 1 && duration == duration.inWholeDays.days -> duration.inWholeDays.unit("day")
|
||||
duration.inWholeHours >= 1 && duration == duration.inWholeHours.hours ->
|
||||
duration.inWholeHours.unit("hour")
|
||||
duration.inWholeMinutes >= 1 -> duration.inWholeMinutes.unit("minute")
|
||||
else -> duration.inWholeSeconds.unit("second")
|
||||
}
|
||||
}
|
||||
|
||||
fun Long.unit(name: String): String = "$this $name${if (this == 1L) "" else "s"}"
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,9 @@ import com.tangem.core.ui.ds.topbar.TangemTopBarType
|
|||
import com.tangem.core.ui.ds2.badge.TangemBadge
|
||||
import com.tangem.core.ui.ds2.button.TangemButton
|
||||
import com.tangem.core.ui.ds2.fade.TangemFade
|
||||
import com.tangem.core.ui.ds2.glowring.TangemGlowRing
|
||||
import com.tangem.core.ui.ds2.loader.TangemLoaderSize
|
||||
import com.tangem.core.ui.ds2.messagebanner.TangemMessageBanner
|
||||
import com.tangem.core.ui.ds2.row.TangemRowContentLead
|
||||
import com.tangem.core.ui.ds2.row.TangemRowVerticalAlignment
|
||||
import com.tangem.core.ui.ds2.topnavigation.TangemTopNavigation
|
||||
|
|
@ -351,6 +353,59 @@ internal data class TangemCheckmarkStory(
|
|||
val onEnabledToggle: () -> Unit,
|
||||
) : DsStoryBookPage
|
||||
|
||||
internal data class TangemGlowRingStory(
|
||||
val variant: TangemGlowRing.Variant,
|
||||
val quality: TangemGlowRing.Quality,
|
||||
val background: Background,
|
||||
val isAnimated: Boolean,
|
||||
val onVariantChange: (TangemGlowRing.Variant) -> Unit,
|
||||
val onQualityChange: (TangemGlowRing.Quality) -> Unit,
|
||||
val onBackgroundChange: (Background) -> Unit,
|
||||
val onAnimatedToggle: () -> Unit,
|
||||
) : DsStoryBookPage {
|
||||
|
||||
/** Backdrop the glow-ring preview is rendered on top of. */
|
||||
enum class Background(val label: String) {
|
||||
BgPrimary("bg.primary"),
|
||||
BgSecondary("bg.secondary"),
|
||||
BgInverse("bg.inverse"),
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("BooleanPropertyNaming")
|
||||
internal data class TangemMessageBannerStory(
|
||||
val variant: TangemMessageBanner.Variant,
|
||||
val contentAlign: TangemMessageBanner.ContentAlign,
|
||||
val hasGlowRing: Boolean,
|
||||
val hasDescription: Boolean,
|
||||
val hasSecondaryButton: Boolean,
|
||||
val hasPrimaryButton: Boolean,
|
||||
val hasCloseButton: Boolean,
|
||||
val hasSlotStart: Boolean,
|
||||
val hasSlotEnd: Boolean,
|
||||
val hasExtraContent: Boolean,
|
||||
val background: Background,
|
||||
val onVariantChange: (TangemMessageBanner.Variant) -> Unit,
|
||||
val onContentAlignChange: (TangemMessageBanner.ContentAlign) -> Unit,
|
||||
val onGlowRingToggle: () -> Unit,
|
||||
val onDescriptionToggle: () -> Unit,
|
||||
val onSecondaryButtonToggle: () -> Unit,
|
||||
val onPrimaryButtonToggle: () -> Unit,
|
||||
val onCloseButtonToggle: () -> Unit,
|
||||
val onSlotStartToggle: () -> Unit,
|
||||
val onSlotEndToggle: () -> Unit,
|
||||
val onExtraContentToggle: () -> Unit,
|
||||
val onBackgroundChange: (Background) -> Unit,
|
||||
) : DsStoryBookPage {
|
||||
|
||||
/** Backdrop the banner preview is rendered on top of. */
|
||||
enum class Background(val label: String) {
|
||||
BgPrimary("bg.primary"),
|
||||
BgSecondary("bg.secondary"),
|
||||
BgInverse("bg.inverse"),
|
||||
}
|
||||
}
|
||||
|
||||
internal data class TangemBadgeV2Story(
|
||||
val variant: TangemBadge.Variant,
|
||||
val status: TangemBadge.Status,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@ import com.tangem.feature.tester.presentation.storybook.page.ds.button.tangemBut
|
|||
import com.tangem.feature.tester.presentation.storybook.page.ds.checkbox.tangemCheckboxV2StoryFactory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.checkmark.tangemCheckmarkStoryFactory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.fade.tangemFadeStoryFactory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.glowring.tangemGlowRingStoryFactory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.loader.tangemLoaderStoryFactory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.messagebanner.tangemMessageBannerStoryFactory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.row.tangemRowStoryFactory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.search.tangemSearchStoryFactory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.shimmer.tangemShimmerStoryFactory
|
||||
|
|
@ -39,6 +41,8 @@ private fun buildDsStories() = listOf(
|
|||
DsStoryItem(title = "✨ TangemShimmer", factory = tangemShimmerStoryFactory),
|
||||
DsStoryItem(title = "🌫️ TangemFade", factory = tangemFadeStoryFactory),
|
||||
DsStoryItem(title = "🧭 TangemTopNavigation", factory = tangemTopNavigationStoryFactory),
|
||||
DsStoryItem(title = "💫 TangemGlowRing", factory = tangemGlowRingStoryFactory),
|
||||
DsStoryItem(title = "📢 TangemMessageBanner", factory = tangemMessageBannerStoryFactory),
|
||||
)
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
package com.tangem.feature.tester.presentation.storybook.page.ds.glowring
|
||||
|
||||
import com.tangem.core.ui.ds2.glowring.TangemGlowRing
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.TangemGlowRingStory
|
||||
import com.tangem.feature.tester.presentation.storybook.viewmodel.StateUpdater
|
||||
import com.tangem.feature.tester.presentation.storybook.viewmodel.storyPageFactory
|
||||
|
||||
internal fun StateUpdater<TangemGlowRingStory>.build(): TangemGlowRingStory {
|
||||
return TangemGlowRingStory(
|
||||
variant = TangemGlowRing.Variant.Magic,
|
||||
quality = TangemGlowRing.Quality.Auto,
|
||||
background = TangemGlowRingStory.Background.BgPrimary,
|
||||
isAnimated = true,
|
||||
onVariantChange = { variant ->
|
||||
updateStory { it.copy(variant = variant) }
|
||||
},
|
||||
onQualityChange = { quality ->
|
||||
updateStory { it.copy(quality = quality) }
|
||||
},
|
||||
onBackgroundChange = { background ->
|
||||
updateStory { it.copy(background = background) }
|
||||
},
|
||||
onAnimatedToggle = {
|
||||
updateStory { it.copy(isAnimated = !it.isAnimated) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
internal val tangemGlowRingStoryFactory
|
||||
get() = storyPageFactory(StateUpdater<TangemGlowRingStory>::build)
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
@file:Suppress("MagicNumber")
|
||||
|
||||
package com.tangem.feature.tester.presentation.storybook.page.ds.glowring
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
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.unit.dp
|
||||
import com.tangem.core.ui.ds2.glowring.TangemGlowRing
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.TangemGlowRingStory
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.TangemGlowRingStory.Background
|
||||
|
||||
@Composable
|
||||
internal fun TangemGlowRingStory(state: TangemGlowRingStory, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.padding(vertical = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
// Preview stays pinned at the top.
|
||||
ComponentPreview(state = state)
|
||||
// Only the controls scroll.
|
||||
Column(
|
||||
modifier = Modifier.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
VariantSelector(selected = state.variant, onSelect = state.onVariantChange)
|
||||
QualitySelector(selected = state.quality, onSelect = state.onQualityChange)
|
||||
BackgroundSelector(selected = state.background, onSelect = state.onBackgroundChange)
|
||||
Toggles(state = state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PreviewBackground(background: Background, modifier: Modifier = Modifier) {
|
||||
when (background) {
|
||||
Background.BgPrimary -> Box(modifier.background(TangemTheme.colors3.bg.primary))
|
||||
Background.BgSecondary -> Box(modifier.background(TangemTheme.colors3.bg.secondary))
|
||||
Background.BgInverse -> Box(modifier.background(TangemTheme.colors3.bg.inverse))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ComponentPreview(state: TangemGlowRingStory) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.clip(RoundedCornerShape(16.dp)),
|
||||
) {
|
||||
PreviewBackground(
|
||||
background = state.background,
|
||||
modifier = Modifier.matchParentSize(),
|
||||
)
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 48.dp),
|
||||
) {
|
||||
TangemGlowRing(
|
||||
modifier = Modifier.size(width = 200.dp, height = 120.dp),
|
||||
variant = state.variant,
|
||||
animated = state.isAnimated,
|
||||
quality = state.quality,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun VariantSelector(selected: TangemGlowRing.Variant, onSelect: (TangemGlowRing.Variant) -> Unit) {
|
||||
Section(label = "Variant") {
|
||||
ChipGrid(
|
||||
items = TangemGlowRing.Variant.entries,
|
||||
label = { it.name },
|
||||
isSelected = { it == selected },
|
||||
onSelect = onSelect,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun QualitySelector(selected: TangemGlowRing.Quality, onSelect: (TangemGlowRing.Quality) -> Unit) {
|
||||
Section(label = "Quality (renderer)") {
|
||||
ChipGrid(
|
||||
items = TangemGlowRing.Quality.entries,
|
||||
label = { it.name },
|
||||
isSelected = { it == selected },
|
||||
onSelect = onSelect,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BackgroundSelector(selected: Background, onSelect: (Background) -> Unit) {
|
||||
Section(label = "Background") {
|
||||
ChipGrid(
|
||||
items = Background.entries,
|
||||
label = { it.label },
|
||||
isSelected = { it == selected },
|
||||
onSelect = onSelect,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Toggles(state: TangemGlowRingStory) {
|
||||
Section(label = "Flags") {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
ToggleRow(label = "animated", checked = state.isAnimated, onToggle = state.onAnimatedToggle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Section(label: String, content: @Composable () -> Unit) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
text = label,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun <T> ChipGrid(items: List<T>, label: (T) -> String, isSelected: (T) -> Boolean, onSelect: (T) -> Unit) {
|
||||
val shape = RoundedCornerShape(50)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.clip(shape)
|
||||
.background(TangemTheme.colors2.surface.level2)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = TangemTheme.colors2.border.neutral.secondary,
|
||||
shape = shape,
|
||||
)
|
||||
.padding(4.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
items.forEach { item ->
|
||||
Chip(
|
||||
label = label(item),
|
||||
selected = isSelected(item),
|
||||
onClick = { onSelect(item) },
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Chip(label: String, selected: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
val chipShape = RoundedCornerShape(50)
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = modifier
|
||||
.clip(chipShape)
|
||||
.background(
|
||||
if (selected) TangemTheme.colors2.surface.level3 else TangemTheme.colors2.surface.level2,
|
||||
)
|
||||
.clickable(onClick = onClick)
|
||||
.padding(vertical = 8.dp, horizontal = 4.dp),
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = if (selected) TangemTheme.colors.text.primary1 else TangemTheme.colors.text.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ToggleRow(label: String, checked: Boolean, onToggle: () -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(TangemTheme.colors2.surface.level2)
|
||||
.clickable(onClick = onToggle)
|
||||
.padding(horizontal = 12.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Text(
|
||||
text = if (checked) "ON" else "OFF",
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = if (checked) TangemTheme.colors.text.accent else TangemTheme.colors.text.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.tangem.feature.tester.presentation.storybook.page.ds.messagebanner
|
||||
|
||||
import com.tangem.core.ui.ds2.messagebanner.TangemMessageBanner
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.TangemMessageBannerStory
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.TangemMessageBannerStory.Background
|
||||
import com.tangem.feature.tester.presentation.storybook.viewmodel.StateUpdater
|
||||
import com.tangem.feature.tester.presentation.storybook.viewmodel.storyPageFactory
|
||||
|
||||
internal fun StateUpdater<TangemMessageBannerStory>.build(): TangemMessageBannerStory {
|
||||
return TangemMessageBannerStory(
|
||||
variant = TangemMessageBanner.Variant.Default,
|
||||
contentAlign = TangemMessageBanner.ContentAlign.Start,
|
||||
hasGlowRing = true,
|
||||
hasDescription = true,
|
||||
hasSecondaryButton = true,
|
||||
hasPrimaryButton = true,
|
||||
hasCloseButton = true,
|
||||
hasSlotStart = true,
|
||||
hasSlotEnd = true,
|
||||
hasExtraContent = true,
|
||||
background = Background.BgSecondary,
|
||||
onVariantChange = { variant -> updateStory { it.copy(variant = variant) } },
|
||||
onContentAlignChange = { align -> updateStory { it.copy(contentAlign = align) } },
|
||||
onGlowRingToggle = { updateStory { it.copy(hasGlowRing = !it.hasGlowRing) } },
|
||||
onDescriptionToggle = { updateStory { it.copy(hasDescription = !it.hasDescription) } },
|
||||
onSecondaryButtonToggle = { updateStory { it.copy(hasSecondaryButton = !it.hasSecondaryButton) } },
|
||||
onPrimaryButtonToggle = { updateStory { it.copy(hasPrimaryButton = !it.hasPrimaryButton) } },
|
||||
onCloseButtonToggle = { updateStory { it.copy(hasCloseButton = !it.hasCloseButton) } },
|
||||
onSlotStartToggle = { updateStory { it.copy(hasSlotStart = !it.hasSlotStart) } },
|
||||
onSlotEndToggle = { updateStory { it.copy(hasSlotEnd = !it.hasSlotEnd) } },
|
||||
onExtraContentToggle = { updateStory { it.copy(hasExtraContent = !it.hasExtraContent) } },
|
||||
onBackgroundChange = { background -> updateStory { it.copy(background = background) } },
|
||||
)
|
||||
}
|
||||
|
||||
internal val tangemMessageBannerStoryFactory
|
||||
get() = storyPageFactory(StateUpdater<TangemMessageBannerStory>::build)
|
||||
|
|
@ -0,0 +1,325 @@
|
|||
@file:Suppress("MagicNumber")
|
||||
|
||||
package com.tangem.feature.tester.presentation.storybook.page.ds.messagebanner
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Icon
|
||||
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.res.painterResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.ds2.messagebanner.CloseButton
|
||||
import com.tangem.core.ui.ds2.messagebanner.TangemMessageBanner
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.TangemMessageBannerStory
|
||||
import com.tangem.feature.tester.presentation.storybook.entity.TangemMessageBannerStory.Background
|
||||
|
||||
@Composable
|
||||
internal fun TangemMessageBannerStory(state: TangemMessageBannerStory, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.padding(vertical = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
// Preview stays pinned at the top.
|
||||
ComponentPreview(state = state)
|
||||
// Only the controls scroll.
|
||||
Column(
|
||||
modifier = Modifier.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
VariantSelector(selected = state.variant, onSelect = state.onVariantChange)
|
||||
ContentAlignSelector(selected = state.contentAlign, onSelect = state.onContentAlignChange)
|
||||
BackgroundSelector(selected = state.background, onSelect = state.onBackgroundChange)
|
||||
Toggles(state = state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PreviewBackground(background: Background, modifier: Modifier = Modifier) {
|
||||
when (background) {
|
||||
Background.BgPrimary -> Box(modifier.background(TangemTheme.colors3.bg.primary))
|
||||
Background.BgSecondary -> Box(modifier.background(TangemTheme.colors3.bg.secondary))
|
||||
Background.BgInverse -> Box(modifier.background(TangemTheme.colors3.bg.inverse))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ComponentPreview(state: TangemMessageBannerStory) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.clip(RoundedCornerShape(16.dp)),
|
||||
) {
|
||||
PreviewBackground(background = state.background, modifier = Modifier.matchParentSize())
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
) {
|
||||
PreviewBanner(state = state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PreviewBanner(state: TangemMessageBannerStory) {
|
||||
TangemMessageBanner(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
variant = state.variant,
|
||||
contentAlign = state.contentAlign,
|
||||
showGlowRing = state.hasGlowRing,
|
||||
title = stringReference("Would you predict?"),
|
||||
description = if (state.hasDescription) {
|
||||
stringReference("France will win FIFA 2026")
|
||||
} else {
|
||||
null
|
||||
},
|
||||
secondaryButton = if (state.hasSecondaryButton) {
|
||||
TangemMessageBanner.Button(text = stringReference("Yes"), onClick = {})
|
||||
} else {
|
||||
null
|
||||
},
|
||||
primaryButton = if (state.hasPrimaryButton) {
|
||||
TangemMessageBanner.Button(text = stringReference("Oh, yes"), onClick = {})
|
||||
} else {
|
||||
null
|
||||
},
|
||||
slotStart = if (state.hasSlotStart) {
|
||||
{ BannerLeadingIcon() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
slotEnd = when {
|
||||
state.hasCloseButton -> {
|
||||
{ TangemMessageBanner.CloseButton(onClick = {}, contentDescription = "Dismiss") }
|
||||
}
|
||||
state.hasSlotEnd -> {
|
||||
{ CirclePlaceholder(size = 24.dp) }
|
||||
}
|
||||
else -> null
|
||||
},
|
||||
extraBottomSlot = if (state.hasExtraContent) {
|
||||
{ ProtectedByRow() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BannerLeadingIcon() {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.img_solana_22),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.clip(RoundedCornerShape(percent = 50)),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ProtectedByRow() {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "Protected by Tangem Security",
|
||||
style = TangemTheme.typography3.body.medium,
|
||||
color = TangemTheme.colors3.text.primary,
|
||||
)
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_shield_check_16),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors3.icon.status.info,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CirclePlaceholder(size: Dp) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(size)
|
||||
.clip(RoundedCornerShape(percent = 50))
|
||||
.background(TangemTheme.colors3.bg.tertiary),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BackgroundSelector(selected: Background, onSelect: (Background) -> Unit) {
|
||||
Section(label = "Background") {
|
||||
ChipGrid(
|
||||
items = Background.entries,
|
||||
label = { it.label },
|
||||
isSelected = { it == selected },
|
||||
onSelect = onSelect,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun VariantSelector(selected: TangemMessageBanner.Variant, onSelect: (TangemMessageBanner.Variant) -> Unit) {
|
||||
Section(label = "Variant") {
|
||||
ChipGrid(
|
||||
items = TangemMessageBanner.Variant.entries,
|
||||
label = { it.name },
|
||||
isSelected = { it == selected },
|
||||
onSelect = onSelect,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ContentAlignSelector(
|
||||
selected: TangemMessageBanner.ContentAlign,
|
||||
onSelect: (TangemMessageBanner.ContentAlign) -> Unit,
|
||||
) {
|
||||
Section(label = "Content align") {
|
||||
ChipGrid(
|
||||
items = TangemMessageBanner.ContentAlign.entries,
|
||||
label = { it.name },
|
||||
isSelected = { it == selected },
|
||||
onSelect = onSelect,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Toggles(state: TangemMessageBannerStory) {
|
||||
Section(label = "Flags") {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
ToggleRow(label = "glowRing", checked = state.hasGlowRing, onToggle = state.onGlowRingToggle)
|
||||
ToggleRow(label = "description", checked = state.hasDescription, onToggle = state.onDescriptionToggle)
|
||||
ToggleRow(
|
||||
label = "secondaryButton",
|
||||
checked = state.hasSecondaryButton,
|
||||
onToggle = state.onSecondaryButtonToggle,
|
||||
)
|
||||
ToggleRow(label = "primaryButton", checked = state.hasPrimaryButton, onToggle = state.onPrimaryButtonToggle)
|
||||
ToggleRow(label = "closeButton", checked = state.hasCloseButton, onToggle = state.onCloseButtonToggle)
|
||||
ToggleRow(label = "slotStart", checked = state.hasSlotStart, onToggle = state.onSlotStartToggle)
|
||||
ToggleRow(label = "slotEnd", checked = state.hasSlotEnd, onToggle = state.onSlotEndToggle)
|
||||
ToggleRow(label = "extraContent", checked = state.hasExtraContent, onToggle = state.onExtraContentToggle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Section(label: String, content: @Composable () -> Unit) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
text = label,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun <T> ChipGrid(items: List<T>, label: (T) -> String, isSelected: (T) -> Boolean, onSelect: (T) -> Unit) {
|
||||
val shape = RoundedCornerShape(50)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.clip(shape)
|
||||
.background(TangemTheme.colors2.surface.level2)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = TangemTheme.colors2.border.neutral.secondary,
|
||||
shape = shape,
|
||||
)
|
||||
.padding(4.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
items.forEach { item ->
|
||||
Chip(
|
||||
label = label(item),
|
||||
selected = isSelected(item),
|
||||
onClick = { onSelect(item) },
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Chip(label: String, selected: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
val chipShape = RoundedCornerShape(50)
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = modifier
|
||||
.clip(chipShape)
|
||||
.background(
|
||||
if (selected) TangemTheme.colors2.surface.level3 else Color.Transparent,
|
||||
)
|
||||
.clickable(onClick = onClick)
|
||||
.padding(vertical = 8.dp, horizontal = 4.dp),
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = if (selected) TangemTheme.colors.text.primary1 else TangemTheme.colors.text.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ToggleRow(label: String, checked: Boolean, onToggle: () -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(TangemTheme.colors2.surface.level2)
|
||||
.clickable(onClick = onToggle)
|
||||
.padding(horizontal = 12.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Text(
|
||||
text = if (checked) "ON" else "OFF",
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = if (checked) TangemTheme.colors.text.accent else TangemTheme.colors.text.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -42,7 +42,9 @@ import com.tangem.feature.tester.presentation.storybook.page.ds.button.TangemBut
|
|||
import com.tangem.feature.tester.presentation.storybook.page.ds.checkbox.TangemCheckboxV2Story
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.checkmark.TangemCheckmarkStory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.fade.TangemFadeStory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.glowring.TangemGlowRingStory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.loader.TangemLoaderStory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.messagebanner.TangemMessageBannerStory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.row.TangemRowStory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.search.TangemSearchStory
|
||||
import com.tangem.feature.tester.presentation.storybook.page.ds.shimmer.TangemShimmerStory
|
||||
|
|
@ -96,11 +98,13 @@ internal fun StoryBookScreen(state: StoryBookUM, modifier: Modifier = Modifier)
|
|||
is TangemBadgeV2Story -> TangemBadgeV2Story(state = storyState)
|
||||
is TangemCheckboxV2Story -> TangemCheckboxV2Story(state = storyState)
|
||||
is TangemCheckmarkStory -> TangemCheckmarkStory(state = storyState)
|
||||
is TangemGlowRingStory -> TangemGlowRingStory(state = storyState)
|
||||
is TangemRowStory -> TangemRowStory(state = storyState)
|
||||
is TangemSearchStory -> TangemSearchStory(state = storyState)
|
||||
is TangemShimmerStory -> TangemShimmerStory(state = storyState)
|
||||
is TangemFadeStory -> TangemFadeStory(state = storyState)
|
||||
is TangemTopNavigationStory -> TangemTopNavigationStory(state = storyState)
|
||||
is TangemMessageBannerStory -> TangemMessageBannerStory(state = storyState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,9 @@
|
|||
<string name="excluded_blockchains_search_placeholder" translatable="false">Filter by name or symbol</string>
|
||||
<string name="blockchain_providers" translatable="false">Blockchain providers</string>
|
||||
<string name="toggle_hot_wallet_restriction" translatable="false">Hot wallet creation restriction - %s</string>
|
||||
<string name="usedesk_token_ttl" translatable="false">Usedesk chat token TTL - %s</string>
|
||||
<string name="usedesk_token_ttl_title" translatable="false">Usedesk chat token TTL</string>
|
||||
<string name="usedesk_token_ttl_custom" translatable="false">Custom (minutes)</string>
|
||||
<string name="share_logs" translatable="false">Share logs</string>
|
||||
<string name="test_push" translatable="false">Test push</string>
|
||||
<string name="accounts" translatable="false">Accounts</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue