Updated on 2026-08-14
|
|
@ -42,7 +42,6 @@ dependencies {
|
|||
/** Compose */
|
||||
implementation(deps.compose.constraintLayout)
|
||||
implementation(deps.compose.foundation)
|
||||
implementation(deps.compose.material)
|
||||
implementation(deps.compose.material3)
|
||||
implementation(deps.compose.paging)
|
||||
implementation(deps.compose.ui.tooling)
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ fun AppBarWithBackButton(
|
|||
TangemTopAppBar(
|
||||
modifier = modifier,
|
||||
title = text,
|
||||
startButton = TopAppBarButtonUM(
|
||||
startButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = iconRes ?: R.drawable.ic_back_24,
|
||||
onIconClicked = onBackClick,
|
||||
onClicked = onBackClick,
|
||||
),
|
||||
containerColor = containerColor,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -27,14 +27,14 @@ fun AppBarWithBackButtonAndIcon(
|
|||
title = text,
|
||||
subtitle = subtitle,
|
||||
containerColor = backgroundColor,
|
||||
startButton = TopAppBarButtonUM(
|
||||
startButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = backIconRes ?: R.drawable.ic_back_24,
|
||||
onIconClicked = onBackClick,
|
||||
onClicked = onBackClick,
|
||||
),
|
||||
endButton = if (iconRes != null && onIconClick != null) {
|
||||
TopAppBarButtonUM(
|
||||
TopAppBarButtonUM.Icon(
|
||||
iconRes = iconRes,
|
||||
onIconClicked = onIconClick,
|
||||
onClicked = onIconClick,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import androidx.compose.runtime.ReadOnlyComposable
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
|
|
@ -20,6 +21,7 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.TopAppBarTestTags
|
||||
|
||||
/**
|
||||
* [TangemTopAppBar] height options.
|
||||
|
|
@ -127,6 +129,7 @@ fun TangemTopAppBar(
|
|||
TopAppBarButton(
|
||||
button = endButton,
|
||||
tint = iconTint,
|
||||
modifier = Modifier.testTag(TopAppBarTestTags.MORE_BUTTON),
|
||||
)
|
||||
}
|
||||
},
|
||||
|
|
@ -172,6 +175,7 @@ fun TangemTopAppBar(
|
|||
TopAppBarButton(
|
||||
button = startButton,
|
||||
tint = iconTint,
|
||||
modifier = Modifier.testTag(TopAppBarTestTags.CLOSE_BUTTON),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -222,6 +226,7 @@ private fun TopAppBarTitle(
|
|||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.testTag(TopAppBarTestTags.TITLE),
|
||||
)
|
||||
|
||||
AnimatedVisibility(
|
||||
|
|
@ -296,25 +301,25 @@ private class BasicTopAppBarPMPreviewProvider : PreviewParameterProvider<BasicTo
|
|||
height = TangemTopAppBarHeight.BOTTOM_SHEET,
|
||||
),
|
||||
BasicTopAppBarPM(
|
||||
startButton = TopAppBarButtonUM(
|
||||
startButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_scan_24,
|
||||
onIconClicked = {},
|
||||
onClicked = {},
|
||||
),
|
||||
endButton = TopAppBarButtonUM(
|
||||
endButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_more_vertical_24,
|
||||
onIconClicked = {},
|
||||
onClicked = {},
|
||||
),
|
||||
),
|
||||
BasicTopAppBarPM(
|
||||
startButton = TopAppBarButtonUM(
|
||||
startButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_scan_24,
|
||||
onIconClicked = {},
|
||||
onClicked = {},
|
||||
),
|
||||
),
|
||||
BasicTopAppBarPM(
|
||||
endButton = TopAppBarButtonUM(
|
||||
endButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_more_vertical_24,
|
||||
onIconClicked = {},
|
||||
onClicked = {},
|
||||
),
|
||||
height = TangemTopAppBarHeight.BOTTOM_SHEET,
|
||||
),
|
||||
|
|
@ -322,13 +327,13 @@ private class BasicTopAppBarPMPreviewProvider : PreviewParameterProvider<BasicTo
|
|||
title = "1234567891011121314151617181920",
|
||||
subtitle = "12345678910111213141516171819202122232425",
|
||||
titleAlignment = Alignment.Start,
|
||||
startButton = TopAppBarButtonUM(
|
||||
startButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_scan_24,
|
||||
onIconClicked = {},
|
||||
onClicked = {},
|
||||
),
|
||||
endButton = TopAppBarButtonUM(
|
||||
endButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_more_vertical_24,
|
||||
onIconClicked = {},
|
||||
onClicked = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,27 +1,49 @@
|
|||
package com.tangem.core.ui.components.appbar
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.core.ui.extensions.conditional
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
fun TopAppBarButton(button: TopAppBarButtonUM, tint: Color, modifier: Modifier = Modifier) {
|
||||
IconButton(
|
||||
enabled = button.enabled,
|
||||
modifier = modifier.size(TangemTheme.dimens.size32),
|
||||
onClick = button.onIconClicked,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||
painter = painterResource(id = button.iconRes),
|
||||
tint = tint,
|
||||
contentDescription = null,
|
||||
)
|
||||
when (button) {
|
||||
is TopAppBarButtonUM.Icon -> {
|
||||
IconButton(
|
||||
enabled = button.enabled,
|
||||
modifier = modifier.size(TangemTheme.dimens.size32),
|
||||
onClick = button.onClicked,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||
painter = painterResource(id = button.iconRes),
|
||||
tint = tint,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
is TopAppBarButtonUM.Text -> {
|
||||
Text(
|
||||
modifier = modifier
|
||||
.conditional(button.enabled) {
|
||||
clickable { button.onClicked() }
|
||||
}
|
||||
.padding(4.dp),
|
||||
text = button.text.resolveReference(),
|
||||
color = tint,
|
||||
style = TangemTheme.typography.body1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,21 +2,39 @@ package com.tangem.core.ui.components.appbar.models
|
|||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
data class TopAppBarButtonUM(
|
||||
@DrawableRes val iconRes: Int,
|
||||
val onIconClicked: () -> Unit,
|
||||
val enabled: Boolean = true,
|
||||
sealed class TopAppBarButtonUM(
|
||||
open val onClicked: () -> Unit,
|
||||
open val enabled: Boolean = true,
|
||||
) {
|
||||
|
||||
data class Icon(
|
||||
@DrawableRes val iconRes: Int,
|
||||
override val onClicked: () -> Unit,
|
||||
override val enabled: Boolean = true,
|
||||
) : TopAppBarButtonUM(onClicked, enabled)
|
||||
|
||||
data class Text(
|
||||
val text: TextReference,
|
||||
override val onClicked: () -> Unit,
|
||||
override val enabled: Boolean = true,
|
||||
) : TopAppBarButtonUM(onClicked, enabled)
|
||||
|
||||
@Suppress("FunctionName")
|
||||
companion object {
|
||||
|
||||
fun Back(onBackClicked: () -> Unit) = Back(true, onBackClicked)
|
||||
|
||||
fun Back(enabled: Boolean = true, onBackClicked: () -> Unit) = TopAppBarButtonUM(
|
||||
fun Back(enabled: Boolean = true, onBackClicked: () -> Unit) = Icon(
|
||||
iconRes = R.drawable.ic_back_24,
|
||||
onIconClicked = onBackClicked,
|
||||
onClicked = onBackClicked,
|
||||
enabled = enabled,
|
||||
)
|
||||
|
||||
fun Text(text: TextReference, onTextClicked: () -> Unit, enabled: Boolean = true) = Text(
|
||||
text = text,
|
||||
onClicked = onTextClicked,
|
||||
enabled = enabled,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.tangem.core.ui.components.block.model.BlockUM
|
||||
import com.tangem.core.ui.components.label.Label
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
|
|
@ -38,6 +39,7 @@ fun BlockItem(model: BlockUM, modifier: Modifier = Modifier) {
|
|||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = model.text.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = when (model.accentType) {
|
||||
|
|
@ -48,6 +50,8 @@ fun BlockItem(model: BlockUM, modifier: Modifier = Modifier) {
|
|||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
model.label?.let { Label(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.core.ui.components.block.model
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.tangem.core.ui.components.label.entity.LabelUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
data class BlockUM(
|
||||
|
|
@ -8,6 +9,7 @@ data class BlockUM(
|
|||
@DrawableRes val iconRes: Int,
|
||||
val onClick: () -> Unit,
|
||||
val accentType: AccentType = AccentType.NONE,
|
||||
val label: LabelUM? = null,
|
||||
) {
|
||||
|
||||
enum class AccentType {
|
||||
|
|
|
|||
|
|
@ -179,6 +179,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicModalBottomSheet(
|
|||
onBack = onBack,
|
||||
dragHandle = null,
|
||||
content = bsContent,
|
||||
scrimColor = TangemTheme.colors.overlay.secondary,
|
||||
)
|
||||
} else {
|
||||
ModalBottomSheet(
|
||||
|
|
@ -190,6 +191,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicModalBottomSheet(
|
|||
contentWindowInsets = { WindowInsetsZero },
|
||||
dragHandle = null,
|
||||
content = bsContent,
|
||||
scrimColor = TangemTheme.colors.overlay.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -200,58 +202,62 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicModalBottomSheet(
|
|||
@Composable
|
||||
private fun TangemModalBottomSheet_Preview() {
|
||||
TangemThemePreview {
|
||||
TangemModalBottomSheet<TangemBottomSheetConfigContentPreviewConfig>(
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = {},
|
||||
content = TangemBottomSheetConfigContentPreviewConfig(),
|
||||
),
|
||||
title = {
|
||||
TangemModalBottomSheetTitle(
|
||||
endIconRes = R.drawable.ic_close_24,
|
||||
onEndClick = {},
|
||||
)
|
||||
},
|
||||
content = {
|
||||
Column(
|
||||
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(56.dp)
|
||||
.clip(RoundedCornerShape(100))
|
||||
.background(TangemTheme.colors.icon.informative.copy(alpha = 0.1f))
|
||||
.padding(12.dp),
|
||||
painter = rememberVectorPainter(
|
||||
ImageVector.vectorResource(R.drawable.ic_alert_24),
|
||||
),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
Box(
|
||||
Modifier.background(TangemTheme.colors.background.tertiary),
|
||||
) {
|
||||
TangemModalBottomSheet<TangemBottomSheetConfigContentPreviewConfig>(
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = {},
|
||||
content = TangemBottomSheetConfigContentPreviewConfig(),
|
||||
),
|
||||
title = {
|
||||
TangemModalBottomSheetTitle(
|
||||
endIconRes = R.drawable.ic_close_24,
|
||||
onEndClick = {},
|
||||
)
|
||||
SpacerH24()
|
||||
Text(
|
||||
text = "Unsuported networks",
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
SpacerH8()
|
||||
Text(
|
||||
text = "Tangem does not currently support a required network by React App.",
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
SpacerH(48.dp)
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Go it",
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
content = {
|
||||
Column(
|
||||
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(56.dp)
|
||||
.clip(RoundedCornerShape(100))
|
||||
.background(TangemTheme.colors.icon.informative.copy(alpha = 0.1f))
|
||||
.padding(12.dp),
|
||||
painter = rememberVectorPainter(
|
||||
ImageVector.vectorResource(R.drawable.ic_alert_24),
|
||||
),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
SpacerH24()
|
||||
Text(
|
||||
text = "Unsuported networks",
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
SpacerH8()
|
||||
Text(
|
||||
text = "Tangem does not currently support a required network by React App.",
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
SpacerH(48.dp)
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Go it",
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import com.tangem.core.ui.res.LocalBottomSheetAlwaysVisible
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.utils.WindowInsetsZero
|
||||
import com.tangem.core.ui.utils.toPx
|
||||
|
||||
/**
|
||||
* Modal bottom sheet with [content], [footer] and optional [title].
|
||||
|
|
@ -154,6 +155,21 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicModalBottomSheetWit
|
|||
val initial = 0
|
||||
val scrollState = rememberScrollState(initial = initial)
|
||||
|
||||
val isKeyboardOpen by rememberIsKeyboardVisible()
|
||||
val buttonHeight = TangemTheme.dimens.spacing80
|
||||
val contentBottomPadding = TangemTheme.dimens.spacing80
|
||||
// Offset calculation for keyboard scroll adjustment:
|
||||
// 1) Button height (footer)
|
||||
// 2) Column content bottom padding
|
||||
// 3) Additional spacing (40dp) for visual comfort when keyboard is open
|
||||
val scrollOffset = buttonHeight.toPx() + buttonHeight.toPx() + 40.dp.toPx()
|
||||
|
||||
LaunchedEffect(isKeyboardOpen) {
|
||||
if (isKeyboardOpen) {
|
||||
scrollState.animateScrollTo(scrollState.value + scrollOffset.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.systemBarsPadding()
|
||||
|
|
@ -186,7 +202,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicModalBottomSheetWit
|
|||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(state = scrollState)
|
||||
.padding(bottom = TangemTheme.dimens.spacing80),
|
||||
.padding(bottom = contentBottomPadding),
|
||||
) {
|
||||
content(model)
|
||||
}
|
||||
|
|
@ -199,7 +215,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicModalBottomSheetWit
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(80.dp)
|
||||
.height(buttonHeight)
|
||||
.align(Alignment.BottomCenter),
|
||||
) {
|
||||
footer(model)
|
||||
|
|
@ -219,6 +235,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicModalBottomSheetWit
|
|||
onBack = onBack,
|
||||
dragHandle = null,
|
||||
content = bsContent,
|
||||
scrimColor = TangemTheme.colors.overlay.secondary,
|
||||
)
|
||||
} else {
|
||||
ModalBottomSheet(
|
||||
|
|
@ -230,6 +247,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicModalBottomSheetWit
|
|||
contentWindowInsets = { WindowInsetsZero },
|
||||
dragHandle = null,
|
||||
content = bsContent,
|
||||
scrimColor = TangemTheme.colors.overlay.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,6 +192,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicBottomSheet(
|
|||
dragHandle = { TangemBottomSheetDraggableHeader(color = containerColor) },
|
||||
onBack = onBack,
|
||||
content = bsContent,
|
||||
scrimColor = TangemTheme.colors.overlay.secondary,
|
||||
)
|
||||
} else {
|
||||
ModalBottomSheet(
|
||||
|
|
@ -203,6 +204,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicBottomSheet(
|
|||
contentWindowInsets = { WindowInsetsZero },
|
||||
dragHandle = { TangemBottomSheetDraggableHeader(color = containerColor) },
|
||||
content = bsContent,
|
||||
scrimColor = TangemTheme.colors.overlay.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -57,9 +57,9 @@ private fun Preview_TangemBottomSheetTitle() {
|
|||
TangemThemePreview {
|
||||
TangemBottomSheetTitle(
|
||||
title = "Title",
|
||||
endButton = TopAppBarButtonUM(
|
||||
endButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_information_24,
|
||||
onIconClicked = {},
|
||||
onClicked = {},
|
||||
),
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ 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.platform.testTag
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
|
|
@ -21,6 +22,7 @@ import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.TokenDetailsScreenTestTags
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
|
@ -32,7 +34,9 @@ fun HorizontalActionChips(
|
|||
contentPadding: PaddingValues = PaddingValues(TangemTheme.dimens.spacing0),
|
||||
) {
|
||||
LazyRow(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.testTag(TokenDetailsScreenTestTags.HORIZONTAL_ACTION_CHIPS),
|
||||
horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing8),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = contentPadding,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import androidx.compose.ui.draw.clip
|
|||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -33,6 +34,7 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.TokenDetailsScreenTestTags
|
||||
|
||||
/**
|
||||
* Rounded action button
|
||||
|
|
@ -98,7 +100,7 @@ fun ActionButton(
|
|||
),
|
||||
)
|
||||
},
|
||||
modifier = modifier,
|
||||
modifier = modifier.testTag(TokenDetailsScreenTestTags.ACTION_BUTTON),
|
||||
color = color,
|
||||
containerColor = containerColor,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -25,10 +25,10 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.core.ui.components.ResizableText
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.test.DialogTestTags
|
||||
import com.tangem.core.ui.test.BaseButtonTestTags
|
||||
import com.tangem.core.ui.utils.MultipleClickPreventer
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Suppress("LongParameterList", "LongMethod")
|
||||
@Composable
|
||||
fun TangemButton(
|
||||
text: String,
|
||||
|
|
@ -51,7 +51,7 @@ fun TangemButton(
|
|||
Button(
|
||||
modifier = modifier
|
||||
.heightIn(min = size.toHeightDp())
|
||||
.testTag(DialogTestTags.BUTTON),
|
||||
.testTag(BaseButtonTestTags.BUTTON),
|
||||
onClick = {
|
||||
multipleClickPreventer.processEvent { if (!showProgress) onClick() }
|
||||
},
|
||||
|
|
@ -78,7 +78,8 @@ fun TangemButton(
|
|||
ResizableText(
|
||||
modifier = Modifier
|
||||
.weight(1f, fill = false)
|
||||
.heightIn(MinButtonContentSize, maxContentSize),
|
||||
.heightIn(MinButtonContentSize, maxContentSize)
|
||||
.testTag(BaseButtonTestTags.TEXT),
|
||||
text = text,
|
||||
style = textStyle,
|
||||
color = colors.contentColor(enabled = enabled).value,
|
||||
|
|
@ -92,7 +93,8 @@ fun TangemButton(
|
|||
Icon(
|
||||
modifier = Modifier
|
||||
.buttonContentSize(maxContentSize)
|
||||
.padding(vertical = 2.dp),
|
||||
.padding(vertical = 2.dp)
|
||||
.testTag(BaseButtonTestTags.ICON),
|
||||
painter = painterResource(id = iconResId),
|
||||
tint = colors.contentColor(enabled = enabled).value,
|
||||
contentDescription = null,
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@ package com.tangem.core.ui.components.buttons.small
|
|||
import android.content.res.Configuration
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
|
|
@ -41,20 +42,19 @@ fun TangemIconButton(
|
|||
background: Color = TangemTheme.colors.button.secondary,
|
||||
iconTint: Color = TangemTheme.colors.icon.secondary,
|
||||
) {
|
||||
IconButton(
|
||||
onClick = onClick,
|
||||
Icon(
|
||||
painter = rememberVectorPainter(ImageVector.vectorResource(iconRes)),
|
||||
contentDescription = "",
|
||||
tint = iconTint,
|
||||
modifier = modifier
|
||||
.size(24.dp)
|
||||
.clip(shape)
|
||||
.background(background)
|
||||
.size(24.dp),
|
||||
) {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(ImageVector.vectorResource(iconRes)),
|
||||
contentDescription = "",
|
||||
tint = iconTint,
|
||||
modifier = Modifier.size(16.dp),
|
||||
)
|
||||
}
|
||||
.padding(4.dp)
|
||||
.clickable(
|
||||
onClick = onClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// region Preview
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import com.tangem.core.ui.extensions.getTintForTokenIcon
|
|||
import com.tangem.core.ui.extensions.networkIconResId
|
||||
import com.tangem.core.ui.extensions.tryGetBackgroundForTokenIcon
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -12,12 +12,8 @@ import androidx.compose.ui.Alignment.Companion.TopCenter
|
|||
import androidx.compose.ui.Alignment.Companion.TopStart
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.ParagraphIntrinsics
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.createFontFamilyResolver
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
|
|
@ -28,6 +24,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
|||
import com.tangem.core.ui.components.fields.visualtransformations.AmountVisualTransformation
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.StakingSendScreenTestTags
|
||||
import com.tangem.core.ui.utils.*
|
||||
import java.math.BigDecimal
|
||||
import java.text.DecimalFormat
|
||||
|
|
@ -77,24 +74,10 @@ fun AmountTextField(
|
|||
) {
|
||||
val decimalFormat = rememberDecimalFormat()
|
||||
BoxWithConstraints(modifier = modifier) {
|
||||
var fontSize = textStyle.fontSize
|
||||
if (isAutoResize) {
|
||||
val calculateIntrinsics = @Composable {
|
||||
val transformedText = visualTransformation.filter(AnnotatedString(value)).text.text
|
||||
ParagraphIntrinsics(
|
||||
text = transformedText,
|
||||
style = textStyle.copy(fontSize = fontSize),
|
||||
density = LocalDensity.current,
|
||||
fontFamilyResolver = createFontFamilyResolver(LocalContext.current),
|
||||
)
|
||||
}
|
||||
var intrinsics = calculateIntrinsics()
|
||||
with(LocalDensity.current) {
|
||||
while (intrinsics.maxIntrinsicWidth > maxWidth.toPx()) {
|
||||
fontSize *= reduceFactor
|
||||
intrinsics = calculateIntrinsics()
|
||||
}
|
||||
}
|
||||
val fontSize = if (isAutoResize) {
|
||||
resizeFont(visualTransformation, value, textStyle, reduceFactor)
|
||||
} else {
|
||||
textStyle.fontSize
|
||||
}
|
||||
val textColor = if (value.isBlank()) TangemTheme.colors.text.disabled else color
|
||||
SimpleTextField(
|
||||
|
|
@ -121,7 +104,9 @@ fun AmountTextField(
|
|||
singleLine = true,
|
||||
readOnly = !isEnabled,
|
||||
visualTransformation = visualTransformation,
|
||||
modifier = Modifier.background(backgroundColor),
|
||||
modifier = Modifier
|
||||
.background(backgroundColor)
|
||||
.testTag(StakingSendScreenTestTags.INPUT_TEXT_FIELD),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,204 @@
|
|||
package com.tangem.core.ui.components.fields
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.BoxWithConstraintsScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.ParagraphIntrinsics
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.createFontFamilyResolver
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.text.style.TextDirection
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
/**
|
||||
* Simple text field for auto size input.
|
||||
* Can display aligned placeholder.
|
||||
*
|
||||
* @param value initial text
|
||||
* @param onValueChange callback
|
||||
* @param isAutoResize is text font auto resize
|
||||
* @param reduceFactor font resize factor
|
||||
* @param textStyle text and placeholder styles
|
||||
* @param textFieldModifier modifier for [SimpleTextField]
|
||||
* @param boxModifier modifier for [BoxWithConstraints]
|
||||
* @see [SimpleTextField] for other text field params
|
||||
*/
|
||||
@SuppressLint("UnusedBoxWithConstraintsScope")
|
||||
@Composable
|
||||
fun AutoSizeTextField(
|
||||
value: String,
|
||||
onValueChange: (String) -> Unit,
|
||||
|
||||
// region AutoSize
|
||||
isAutoResize: Boolean = true,
|
||||
@FloatRange(from = 0.0, to = 1.0, fromInclusive = false, toInclusive = false)
|
||||
reduceFactor: Double = 0.9,
|
||||
|
||||
// region TextField
|
||||
textFieldModifier: Modifier = Modifier,
|
||||
boxModifier: Modifier = Modifier,
|
||||
placeholder: TextReference? = null,
|
||||
singleLine: Boolean = isAutoResize,
|
||||
centered: Boolean = false,
|
||||
visualTransformation: VisualTransformation = VisualTransformation.None,
|
||||
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
|
||||
keyboardActions: KeyboardActions = KeyboardActions.Default,
|
||||
color: Color = TangemTheme.colors.text.primary1,
|
||||
textStyle: TextStyle = TangemTheme.typography.body2.copy(color = color),
|
||||
placeholderColor: Color = TangemTheme.colors.text.disabled,
|
||||
readOnly: Boolean = false,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
isValuePasted: Boolean = false,
|
||||
onValuePastedTriggerDismiss: () -> Unit = {},
|
||||
decorationBox: (@Composable (innerTextField: @Composable () -> Unit) -> Unit)? = null,
|
||||
) {
|
||||
BoxWithConstraints(modifier = boxModifier) {
|
||||
val fontSize = if (isAutoResize) {
|
||||
resizeFont(visualTransformation, value, textStyle, reduceFactor)
|
||||
} else {
|
||||
textStyle.fontSize
|
||||
}
|
||||
val textColor = if (value.isBlank()) TangemTheme.colors.text.disabled else color
|
||||
SimpleTextField(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
textStyle = textStyle.copy(
|
||||
fontSize = fontSize,
|
||||
textDirection = TextDirection.ContentOrLtr,
|
||||
),
|
||||
isValuePasted = isValuePasted,
|
||||
onValuePastedTriggerDismiss = onValuePastedTriggerDismiss,
|
||||
color = textColor,
|
||||
keyboardOptions = keyboardOptions,
|
||||
keyboardActions = keyboardActions,
|
||||
placeholder = placeholder,
|
||||
placeholderColor = placeholderColor,
|
||||
singleLine = singleLine,
|
||||
interactionSource = interactionSource,
|
||||
readOnly = readOnly,
|
||||
centered = centered,
|
||||
visualTransformation = visualTransformation,
|
||||
decorationBox = decorationBox,
|
||||
modifier = textFieldModifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun BoxWithConstraintsScope.resizeFont(
|
||||
visualTransformation: VisualTransformation,
|
||||
value: String,
|
||||
textStyle: TextStyle,
|
||||
reduceFactor: Double,
|
||||
): TextUnit {
|
||||
var result = textStyle.fontSize
|
||||
val calculateIntrinsics = @Composable {
|
||||
val transformedText = visualTransformation.filter(AnnotatedString(value)).text.text
|
||||
ParagraphIntrinsics(
|
||||
text = transformedText,
|
||||
style = textStyle.copy(fontSize = result),
|
||||
density = LocalDensity.current,
|
||||
fontFamilyResolver = createFontFamilyResolver(LocalContext.current),
|
||||
)
|
||||
}
|
||||
var intrinsics = calculateIntrinsics()
|
||||
with(LocalDensity.current) {
|
||||
while (intrinsics.maxIntrinsicWidth > maxWidth.toPx()) {
|
||||
result *= reduceFactor
|
||||
intrinsics = calculateIntrinsics()
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// region preview
|
||||
@Preview(widthDp = 360, showBackground = true)
|
||||
@Composable
|
||||
private fun AmountTextFieldPreview(
|
||||
@PreviewParameter(AutoSizeTextFieldPreviewProvider::class) data: AutoSizeTextFieldPreviewData,
|
||||
) {
|
||||
var text by remember { mutableStateOf(data.value) }
|
||||
TangemThemePreview {
|
||||
AutoSizeTextField(
|
||||
textFieldModifier = Modifier.fillMaxWidth(),
|
||||
value = text,
|
||||
onValueChange = { text = it },
|
||||
centered = data.centered,
|
||||
isAutoResize = data.isAutoResize,
|
||||
placeholder = data.placeholder,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class AutoSizeTextFieldPreviewProvider : PreviewParameterProvider<AutoSizeTextFieldPreviewData> {
|
||||
override val values = sequenceOf(
|
||||
AutoSizeTextFieldPreviewData(
|
||||
value = "AutoSizeTextField",
|
||||
placeholder = stringReference("placeholder"),
|
||||
isAutoResize = true,
|
||||
centered = false,
|
||||
),
|
||||
AutoSizeTextFieldPreviewData(
|
||||
value = "AutoSizeTextFieldAutoSizeTextFieldAutoSizeTextFieldAutoSizeTextField",
|
||||
placeholder = stringReference("placeholder"),
|
||||
isAutoResize = true,
|
||||
centered = false,
|
||||
),
|
||||
AutoSizeTextFieldPreviewData(
|
||||
value = "AutoSizeTextFieldAutoSizeTextFieldAutoSizeTextFieldAutoSizeTextField",
|
||||
placeholder = stringReference("Placeholder"),
|
||||
isAutoResize = true,
|
||||
centered = false,
|
||||
),
|
||||
AutoSizeTextFieldPreviewData(
|
||||
value = "",
|
||||
placeholder = stringReference("Placeholder"),
|
||||
isAutoResize = true,
|
||||
centered = false,
|
||||
),
|
||||
AutoSizeTextFieldPreviewData(
|
||||
value = "AutoSizeTextField",
|
||||
placeholder = stringReference("Placeholder"),
|
||||
isAutoResize = false,
|
||||
centered = true,
|
||||
),
|
||||
AutoSizeTextFieldPreviewData(
|
||||
value = "AutoSizeTextFieldAutoSizeTextFieldAutoSizeTextFieldAutoSizeTextField",
|
||||
placeholder = stringReference("Placeholder"),
|
||||
isAutoResize = false,
|
||||
centered = true,
|
||||
),
|
||||
AutoSizeTextFieldPreviewData(
|
||||
value = "",
|
||||
placeholder = stringReference("Placeholder"),
|
||||
isAutoResize = false,
|
||||
centered = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private data class AutoSizeTextFieldPreviewData(
|
||||
val value: String,
|
||||
val placeholder: TextReference,
|
||||
val isAutoResize: Boolean,
|
||||
val centered: Boolean,
|
||||
)
|
||||
// endregion
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.core.ui.components.fields
|
|||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
|
|
@ -36,9 +37,9 @@ fun PinTextField(
|
|||
value: String,
|
||||
length: Int,
|
||||
isPasswordVisual: Boolean,
|
||||
pinTextColor: PinTextColor,
|
||||
onValueChange: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
wrongCode: Boolean = false,
|
||||
) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val textFieldValue = remember(value) {
|
||||
|
|
@ -72,7 +73,7 @@ fun PinTextField(
|
|||
CellDecoration(
|
||||
length = length,
|
||||
isPasswordVisual = isPasswordVisual,
|
||||
wrongCode = wrongCode,
|
||||
pinTextColor = pinTextColor,
|
||||
value = value,
|
||||
)
|
||||
},
|
||||
|
|
@ -84,17 +85,25 @@ fun PinTextField(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
enum class PinTextColor {
|
||||
Primary,
|
||||
WrongCode,
|
||||
Success,
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber", "LongMethod")
|
||||
@Composable
|
||||
private fun CellDecoration(
|
||||
length: Int,
|
||||
wrongCode: Boolean,
|
||||
pinTextColor: PinTextColor,
|
||||
value: String,
|
||||
modifier: Modifier = Modifier,
|
||||
isPasswordVisual: Boolean = false,
|
||||
) {
|
||||
val textMeasurer = rememberTextMeasurer()
|
||||
val width = textMeasurer.measure("0")
|
||||
val minSize = textMeasurer.measure("0")
|
||||
val minWidth = maxOf(minSize.size.width.dp + 8.dp, 24.dp + 3.dp) // 24.dp is the minimum width of a pin cell
|
||||
val minHeight = maxOf(minSize.size.height.dp, 48.dp) // 48.dp is the minimum height of a pin cell
|
||||
|
||||
Row(
|
||||
modifier = modifier,
|
||||
|
|
@ -107,6 +116,18 @@ private fun CellDecoration(
|
|||
""
|
||||
}
|
||||
|
||||
val color = when (pinTextColor) {
|
||||
PinTextColor.Primary -> {
|
||||
if (isPasswordVisual) {
|
||||
TangemTheme.colors.icon.informative
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
}
|
||||
}
|
||||
PinTextColor.WrongCode -> TangemTheme.colors.icon.warning
|
||||
PinTextColor.Success -> TangemTheme.colors.icon.accent
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
|
|
@ -119,26 +140,34 @@ private fun CellDecoration(
|
|||
targetState = char,
|
||||
transitionSpec = {
|
||||
(
|
||||
fadeIn(animationSpec = tween(220, delayMillis = 90)) +
|
||||
slideInVertically(animationSpec = tween(330, delayMillis = 0))
|
||||
fadeIn(animationSpec = tween(90, delayMillis = 90)) +
|
||||
slideInVertically(animationSpec = tween(220, delayMillis = 0))
|
||||
)
|
||||
.togetherWith(
|
||||
fadeOut(animationSpec = tween(90)) + slideOutVertically(tween(220)),
|
||||
)
|
||||
},
|
||||
) { text ->
|
||||
Text(
|
||||
modifier = Modifier.sizeIn(minWidth = width.size.width.dp + 8.dp, minHeight = 48.dp),
|
||||
text = text,
|
||||
style = TangemTheme.typography.h3,
|
||||
color = if (wrongCode) {
|
||||
TangemTheme.colors.text.warning
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
},
|
||||
textAlign = TextAlign.Center,
|
||||
lineHeight = 48.sp,
|
||||
)
|
||||
if (isPasswordVisual && text.isNotEmpty()) {
|
||||
Canvas(
|
||||
Modifier.sizeIn(minWidth = minWidth, minHeight = minHeight),
|
||||
) {
|
||||
drawCircle(
|
||||
color = color,
|
||||
radius = 4.dp.toPx(),
|
||||
center = center,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Text(
|
||||
modifier = Modifier.sizeIn(minWidth = minWidth, minHeight = minHeight),
|
||||
text = text,
|
||||
style = TangemTheme.typography.h3,
|
||||
color = color,
|
||||
textAlign = TextAlign.Center,
|
||||
lineHeight = 48.sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -152,10 +181,18 @@ private fun Preview() {
|
|||
var text by remember { mutableStateOf("123") }
|
||||
|
||||
Column {
|
||||
PinTextField(
|
||||
value = text,
|
||||
onValueChange = { text = it },
|
||||
isPasswordVisual = true,
|
||||
pinTextColor = PinTextColor.Success,
|
||||
length = 6,
|
||||
)
|
||||
PinTextField(
|
||||
value = text,
|
||||
onValueChange = { text = it },
|
||||
isPasswordVisual = false,
|
||||
pinTextColor = PinTextColor.Primary,
|
||||
length = 6,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -11,16 +11,20 @@ import androidx.compose.foundation.text.KeyboardActions
|
|||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusManager
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.platform.SoftwareKeyboardController
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
|
|
@ -35,6 +39,7 @@ import com.tangem.core.ui.extensions.resolveReference
|
|||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.SelectCountryBottomSheetTestTags
|
||||
|
||||
@Composable
|
||||
fun SearchBar(
|
||||
|
|
@ -45,6 +50,7 @@ fun SearchBar(
|
|||
) {
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
|
||||
BasicTextField(
|
||||
|
|
@ -57,7 +63,9 @@ fun SearchBar(
|
|||
} else {
|
||||
state.onActiveChange(false)
|
||||
}
|
||||
},
|
||||
}
|
||||
.focusRequester(focusRequester)
|
||||
.testTag(SelectCountryBottomSheetTestTags.SEARCH_BAR),
|
||||
enabled = enabled,
|
||||
value = state.query,
|
||||
onValueChange = state.onQueryChange,
|
||||
|
|
@ -89,6 +97,10 @@ fun SearchBar(
|
|||
)
|
||||
},
|
||||
)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import androidx.compose.foundation.text.KeyboardActions
|
|||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
|
|
@ -17,6 +18,7 @@ import androidx.compose.ui.text.TextRange
|
|||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -39,6 +41,7 @@ fun SimpleTextField(
|
|||
textStyle: TextStyle = TangemTheme.typography.body2.copy(color = color),
|
||||
placeholderColor: Color = TangemTheme.colors.text.disabled,
|
||||
readOnly: Boolean = false,
|
||||
centered: Boolean = false,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
isValuePasted: Boolean = false,
|
||||
onValuePastedTriggerDismiss: () -> Unit = {},
|
||||
|
|
@ -80,6 +83,8 @@ fun SimpleTextField(
|
|||
onValuePastedTriggerDismiss()
|
||||
}
|
||||
}
|
||||
var textStyle = textStyle.copy(color = color)
|
||||
if (centered) textStyle = textStyle.copy(textAlign = TextAlign.Center)
|
||||
|
||||
BasicTextField(
|
||||
value = textFieldValue,
|
||||
|
|
@ -91,7 +96,7 @@ fun SimpleTextField(
|
|||
|
||||
if (stringChangedSinceLastInvocation) onValueChange(newTextFieldValueState.text)
|
||||
},
|
||||
textStyle = textStyle.copy(color = color),
|
||||
textStyle = textStyle,
|
||||
cursorBrush = SolidColor(TangemTheme.colors.text.primary1),
|
||||
singleLine = singleLine,
|
||||
readOnly = readOnly,
|
||||
|
|
@ -105,6 +110,7 @@ fun SimpleTextField(
|
|||
value = value,
|
||||
textStyle = textStyle,
|
||||
textValue = textValue,
|
||||
centered = centered,
|
||||
color = placeholderColor,
|
||||
)
|
||||
},
|
||||
|
|
@ -118,10 +124,11 @@ private fun SimpleTextPlaceholder(
|
|||
placeholder: TextReference?,
|
||||
value: String,
|
||||
textStyle: TextStyle,
|
||||
centered: Boolean,
|
||||
textValue: @Composable () -> Unit,
|
||||
color: Color = TangemTheme.colors.text.disabled,
|
||||
) {
|
||||
Box {
|
||||
Box(contentAlignment = if (centered) Alignment.Center else Alignment.TopStart) {
|
||||
if (value.isBlank() && placeholder != null) {
|
||||
AnimatedContent(
|
||||
targetState = placeholder,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
|
|
@ -26,6 +27,7 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.BaseBlockTestTags
|
||||
|
||||
/**
|
||||
* [InputRowDefault](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=2100-807&mode=design&t=86eKp9izWxUvmoCq-4)
|
||||
|
|
@ -64,20 +66,24 @@ fun InputRowDefault(
|
|||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f),
|
||||
.weight(1f)
|
||||
.testTag(BaseBlockTestTags.BLOCK),
|
||||
) {
|
||||
title?.let {
|
||||
Text(
|
||||
text = title.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = titleColor,
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing8),
|
||||
modifier = Modifier
|
||||
.padding(bottom = TangemTheme.dimens.spacing8)
|
||||
.testTag(BaseBlockTestTags.BLOCK_TITLE),
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = text.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = textColor,
|
||||
modifier = Modifier.testTag(BaseBlockTestTags.BLOCK_TEXT),
|
||||
)
|
||||
}
|
||||
iconRes?.let {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment.Companion.CenterEnd
|
||||
|
|
@ -19,6 +20,7 @@ import androidx.compose.ui.draw.clip
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.buttons.small.TangemIconButton
|
||||
import com.tangem.core.ui.components.fields.SimpleTextField
|
||||
|
|
@ -69,11 +71,12 @@ fun InputRowRecipient(
|
|||
showDivider: Boolean = false,
|
||||
isLoading: Boolean = false,
|
||||
isValuePasted: Boolean = false,
|
||||
resolvedAddress: String? = null,
|
||||
) {
|
||||
val (titleText, color) = if (isError && error != null) {
|
||||
error to TangemTheme.colors.text.warning
|
||||
} else {
|
||||
title to TangemTheme.colors.text.secondary
|
||||
title to TangemTheme.colors.text.tertiary
|
||||
}
|
||||
DividerContainer(
|
||||
modifier = modifier,
|
||||
|
|
@ -144,11 +147,15 @@ fun InputRowRecipient(
|
|||
} else {
|
||||
TangemTheme.colors.text.primary2
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens.spacing8),
|
||||
modifier = Modifier.padding(start = TangemTheme.dimens.spacing8),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ResolvedAddressRow(
|
||||
isLoading = isLoading,
|
||||
resolvedAddress = resolvedAddress,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -203,6 +210,38 @@ private fun RowScope.InputIcon(isLoading: Boolean, value: String) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ResolvedAddressRow(isLoading: Boolean, resolvedAddress: String?) {
|
||||
AnimatedContent(
|
||||
targetState = if (resolvedAddress.isNullOrBlank() || isLoading) {
|
||||
ResolvedState.Hide
|
||||
} else {
|
||||
ResolvedState.Show(resolvedAddress)
|
||||
},
|
||||
label = "Resolved Address",
|
||||
) { state ->
|
||||
if (state is ResolvedState.Show) {
|
||||
Column {
|
||||
HorizontalDivider(
|
||||
thickness = 0.5.dp,
|
||||
modifier = Modifier.padding(top = 12.dp, bottom = 12.dp),
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
)
|
||||
Text(
|
||||
text = state.address,
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed interface ResolvedState {
|
||||
data object Hide : ResolvedState
|
||||
data class Show(val address: String) : ResolvedState
|
||||
}
|
||||
|
||||
//region preview
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
|
|
@ -224,6 +263,7 @@ private fun InputRowRecipientPreview(
|
|||
onQrCodeClick = {},
|
||||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||
isRedesignEnabled = false,
|
||||
resolvedAddress = value.resolvedAddress,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -232,6 +272,7 @@ private data class InputRowRecipientPreviewData(
|
|||
val value: String,
|
||||
val isError: Boolean,
|
||||
val isLoading: Boolean = false,
|
||||
val resolvedAddress: String? = null,
|
||||
)
|
||||
|
||||
private class InputRowRecipientPreviewDataProvider : PreviewParameterProvider<InputRowRecipientPreviewData> {
|
||||
|
|
@ -250,6 +291,12 @@ private class InputRowRecipientPreviewDataProvider : PreviewParameterProvider<In
|
|||
isLoading = true,
|
||||
isError = true,
|
||||
),
|
||||
InputRowRecipientPreviewData(
|
||||
value = "vitalik.eth",
|
||||
isLoading = false,
|
||||
isError = true,
|
||||
resolvedAddress = "0x391316d97a07027a0702c8A002c8A0C25d8470",
|
||||
),
|
||||
)
|
||||
}
|
||||
//endregion
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
package com.tangem.core.ui.components.label
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.label.entity.LabelStyle
|
||||
import com.tangem.core.ui.components.label.entity.LabelUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
/**
|
||||
* Label component
|
||||
*
|
||||
* @param state component state
|
||||
* @param modifier composable modifier
|
||||
*
|
||||
* @see <a href="https://www.figma.com/design/14ISV23YB1yVW1uNVwqrKv/Android?node-id=4480-1459&t=2QTpi1G7FeTexTFS-4">Figma</a>
|
||||
*/
|
||||
@Composable
|
||||
fun Label(state: LabelUM, modifier: Modifier = Modifier) {
|
||||
val backgroundColor by animateColorAsState(
|
||||
targetValue = when (state.style) {
|
||||
LabelStyle.ACCENT -> TangemTheme.colors.text.accent.copy(alpha = 0.1f)
|
||||
LabelStyle.REGULAR -> TangemTheme.colors.control.unchecked
|
||||
LabelStyle.WARNING -> TangemTheme.colors.text.warning.copy(alpha = 0.1f)
|
||||
},
|
||||
)
|
||||
|
||||
val textColor by animateColorAsState(
|
||||
targetValue = when (state.style) {
|
||||
LabelStyle.ACCENT -> TangemTheme.colors.text.accent
|
||||
LabelStyle.REGULAR -> TangemTheme.colors.text.secondary
|
||||
LabelStyle.WARNING -> TangemTheme.colors.text.warning
|
||||
},
|
||||
)
|
||||
|
||||
AnimatedContent(targetState = state.text) { text ->
|
||||
Box(
|
||||
modifier = modifier
|
||||
.padding(horizontal = 4.dp)
|
||||
.background(
|
||||
color = backgroundColor,
|
||||
shape = TangemTheme.shapes.roundedCorners8,
|
||||
)
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp),
|
||||
) {
|
||||
Text(
|
||||
text = text.resolveReference(),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = textColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun LabelPreview() {
|
||||
TangemThemePreview {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
) {
|
||||
Label(
|
||||
state = LabelUM(
|
||||
text = TextReference.Str("Regular Label"),
|
||||
style = LabelStyle.REGULAR,
|
||||
),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Label(
|
||||
state = LabelUM(
|
||||
text = TextReference.Str("Accent Label"),
|
||||
style = LabelStyle.ACCENT,
|
||||
),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Label(
|
||||
state = LabelUM(
|
||||
text = TextReference.Str("Warning Label"),
|
||||
style = LabelStyle.WARNING,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.core.ui.components.label.entity
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
data class LabelUM(
|
||||
val text: TextReference,
|
||||
val style: LabelStyle,
|
||||
)
|
||||
|
||||
enum class LabelStyle {
|
||||
REGULAR, ACCENT, WARNING,
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ import com.tangem.core.ui.components.RectangleShimmer
|
|||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
|
||||
/**
|
||||
* Market price block
|
||||
|
|
@ -120,7 +120,7 @@ private fun PriceBlock(state: MarketPriceBlockState, priceWidthDp: Dp) {
|
|||
)
|
||||
}
|
||||
} else {
|
||||
Price(price = BigDecimalFormatter.EMPTY_BALANCE_SIGN, modifier = priceModifier)
|
||||
Price(price = DASH_SIGN, modifier = priceModifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ 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.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -35,6 +36,7 @@ import com.tangem.core.ui.extensions.resolveReference
|
|||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.NotificationTestTags
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState as NotificationButtonsState
|
||||
|
||||
/**
|
||||
|
|
@ -206,6 +208,7 @@ internal fun TextsBlock(
|
|||
text = titleText,
|
||||
color = titleColor,
|
||||
style = TangemTheme.typography.button,
|
||||
modifier = Modifier.testTag(NotificationTestTags.TITLE),
|
||||
)
|
||||
|
||||
SpacerH(height = TangemTheme.dimens.spacing2)
|
||||
|
|
@ -217,6 +220,7 @@ internal fun TextsBlock(
|
|||
text = subtitleText,
|
||||
color = subtitleColor,
|
||||
style = TangemTheme.typography.caption2,
|
||||
modifier = Modifier.testTag(NotificationTestTags.TEXT),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import androidx.constraintlayout.compose.Visibility
|
|||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.audits.AuditLabel
|
||||
import com.tangem.core.ui.components.audits.AuditLabelUM
|
||||
import com.tangem.core.ui.components.badge.Badge
|
||||
|
|
@ -50,8 +51,6 @@ private const val DISABLED_ICON_ALPHA = 0.4f
|
|||
fun ProviderChooseCrypto(providerChooseUM: ProviderChooseUM, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
ConstraintLayout(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.clip(RoundedCornerShape(14.dp))
|
||||
.selectedBorder(isSelected = providerChooseUM.isSelected)
|
||||
.clickable(
|
||||
enabled = !providerChooseUM.hasError(),
|
||||
|
|
@ -133,13 +132,23 @@ private fun IconContent(iconUrl: String, modifier: Modifier = Modifier) {
|
|||
SubcomposeAsyncImage(
|
||||
modifier = modifier
|
||||
.size(40.dp)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(TangemColorPalette.Light1),
|
||||
.clip(RoundedCornerShape(8.dp)),
|
||||
model = ImageRequest.Builder(context = LocalContext.current)
|
||||
.data(iconUrl)
|
||||
.crossfade(enable = true)
|
||||
.allowHardware(false)
|
||||
.build(),
|
||||
loading = {
|
||||
RectangleShimmer(radius = 8.dp)
|
||||
},
|
||||
error = {
|
||||
Box(
|
||||
modifier = Modifier.background(
|
||||
color = TangemColorPalette.Light1,
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
),
|
||||
)
|
||||
},
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -23,6 +24,7 @@ import com.tangem.core.ui.R
|
|||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.StakingDetailsScreenTestTags
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
|
|
@ -54,7 +56,8 @@ fun RoundableCornersRow(
|
|||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
vertical = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
)
|
||||
.testTag(StakingDetailsScreenTestTags.PARAMETER_BLOCK),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
|
|
@ -63,6 +66,7 @@ fun RoundableCornersRow(
|
|||
color = startTextColor,
|
||||
maxLines = 1,
|
||||
style = startTextStyle,
|
||||
modifier = Modifier.testTag(StakingDetailsScreenTestTags.PARAMETER_NAME),
|
||||
)
|
||||
if (iconResId != null && iconClick != null) {
|
||||
Icon(
|
||||
|
|
@ -85,6 +89,7 @@ fun RoundableCornersRow(
|
|||
color = endTextColor,
|
||||
maxLines = 1,
|
||||
style = endTextStyle,
|
||||
modifier = Modifier.testTag(StakingDetailsScreenTestTags.PARAMETER_VALUE),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.core.ui.components.rows
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
|
|
@ -13,6 +12,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
|
|
@ -22,14 +22,15 @@ import com.tangem.core.ui.components.atoms.text.EllipsisText
|
|||
import com.tangem.core.ui.components.atoms.text.TextEllipsis
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.SelectNetworkFeeBottomSheetTestTags
|
||||
import com.tangem.utils.StringsSigns
|
||||
|
||||
@Composable
|
||||
fun SelectorRowItem(
|
||||
@StringRes titleRes: Int,
|
||||
title: TextReference,
|
||||
@DrawableRes iconRes: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
paddingValues: PaddingValues = PaddingValues(TangemTheme.dimens.spacing12),
|
||||
|
|
@ -68,7 +69,8 @@ fun SelectorRowItem(
|
|||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(paddingValues),
|
||||
.padding(paddingValues)
|
||||
.testTag(SelectNetworkFeeBottomSheetTestTags.SELECTOR_ITEM),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
|
|
@ -77,7 +79,7 @@ fun SelectorRowItem(
|
|||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
text = stringResourceSafe(titleRes),
|
||||
text = title.resolveReference(),
|
||||
style = textStyle,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier.padding(start = TangemTheme.dimens.spacing8),
|
||||
|
|
@ -148,7 +150,7 @@ private fun RowScope.SelectorValueContent(
|
|||
private fun SelectorRowItemPreview() {
|
||||
TangemThemePreview {
|
||||
SelectorRowItem(
|
||||
titleRes = R.string.common_fee_selector_option_slow,
|
||||
title = resourceReference(R.string.common_fee_selector_option_slow),
|
||||
iconRes = R.drawable.ic_tortoise_24,
|
||||
preDot = TextReference.Str("1000 ETH"),
|
||||
postDot = TextReference.Str("1000 $"),
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -28,6 +29,7 @@ import com.tangem.core.ui.components.stories.model.StoryConfig
|
|||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.SwapStoriesScreenTestTags
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
|
@ -101,7 +103,8 @@ inline fun <reified T : StoryConfig> StoriesContainer(
|
|||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = LocalIndication.current,
|
||||
onClick = { config.onClose(watchedCounter) },
|
||||
),
|
||||
)
|
||||
.testTag(SwapStoriesScreenTestTags.CLOSE_BUTTON),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ fun getActiveIconRes(blockchainId: String): Int {
|
|||
"zklink", "zklink/test" -> R.drawable.img_zklink_22
|
||||
"vanar-chain", "vanar-chain/test" -> R.drawable.img_vanar_22
|
||||
"pepecoin", "pepecoin/test" -> R.drawable.img_pepecoin_22
|
||||
"hyperliquid", "hyperliquid/test" -> R.drawable.img_hyperliquid_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -186,6 +187,7 @@ fun getActiveIconResByCoinId(coinId: String): Int {
|
|||
"zklink", "zklink/test" -> R.drawable.img_zklink_22
|
||||
"vanar-chain", "vanar-chain/test" -> R.drawable.img_vanar_22
|
||||
"pepecoin-network", "pepecoin-network/test" -> R.drawable.img_pepecoin_22
|
||||
"hyperliquid", "hyperliquid/test" -> R.drawable.img_hyperliquid_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -281,6 +283,7 @@ fun getGreyedOutIconRes(blockchainId: String): Int {
|
|||
"zklink", "zklink/test" -> R.drawable.ic_zklink_22
|
||||
"vanar-chain", "vanar-chain/test" -> R.drawable.ic_vanar_22
|
||||
"pepecoin", "pepecoin/test" -> R.drawable.ic_pepecoin_22
|
||||
"hyperliquid", "hyperliquid/test" -> R.drawable.ic_hyperliquid_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
package com.tangem.core.ui.extensions
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavController
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* The ViewModel is scoped to the parent route Navigation graph
|
||||
* and is provided using the Hilt-generated ViewModel factory
|
||||
*
|
||||
* ```
|
||||
* val navController = rememberNavController()
|
||||
*
|
||||
* navigation(
|
||||
* route = "parent",
|
||||
* startDestination = "parent/1"
|
||||
* ) {
|
||||
* composable("route/1") { entry ->
|
||||
* val viewModel = entry.parentHiltViewModel(navController)
|
||||
* }
|
||||
* composable("route/2") { entry ->
|
||||
* val viewModel = entry.parentHiltViewModel(navController)
|
||||
* }
|
||||
* composable("route/3") { entry ->
|
||||
* val viewModel = entry.parentHiltViewModel(navController)
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param navController NavController within the common NavGraph
|
||||
* @throws Exception if there is no parent route
|
||||
*/
|
||||
@Composable
|
||||
inline fun <reified T : ViewModel> NavBackStackEntry.parentHiltViewModel(navController: NavController): T {
|
||||
val viewModelStoreOwner = remember(this) {
|
||||
try {
|
||||
navController.getBackStackEntry(this.destination.parent!!.id)
|
||||
} catch (e: Exception) {
|
||||
Timber.tag("scopedViewModel").e(e, "There is no parent route'")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
return hiltViewModel<T>(viewModelStoreOwner)
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
package com.tangem.core.ui.extensions
|
||||
|
||||
import android.R
|
||||
import android.content.Context
|
||||
import android.graphics.Color.*
|
||||
import android.view.WindowManager
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import kotlin.math.sqrt
|
||||
|
||||
@Deprecated("Use only in legacy fragments")
|
||||
fun Fragment.setStatusBarColor(@ColorRes colorResId: Int) {
|
||||
with(requireActivity().window) {
|
||||
clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||
addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||
statusBarColor = ContextCompat.getColor(requireContext(), colorResId)
|
||||
val view = view ?: return
|
||||
val windowInsetsController = WindowCompat.getInsetsController(this, view)
|
||||
windowInsetsController.isAppearanceLightStatusBars = luminance(requireContext(), colorResId)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO replace by android.graphics.luminance() after bump min API to 24
|
||||
@Suppress("MagicNumber")
|
||||
fun luminance(context: Context, @ColorRes colorRes: Int): Boolean {
|
||||
val color = context.resources.getColor(colorRes, null)
|
||||
if (R.color.transparent == color) return true
|
||||
var rtnValue = false
|
||||
val rgb = intArrayOf(red(color), green(color), blue(color))
|
||||
val brightness = sqrt(
|
||||
rgb[0] * rgb[0] * .241 +
|
||||
rgb[1] * rgb[1] * .691 +
|
||||
rgb[2] * rgb[2] * .068,
|
||||
).toInt()
|
||||
|
||||
// color is light
|
||||
if (brightness >= 200) {
|
||||
rtnValue = true
|
||||
}
|
||||
return rtnValue
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@ import androidx.compose.foundation.LocalIndication
|
|||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
@ -72,26 +71,24 @@ fun Modifier.conditionalCompose(
|
|||
fun Modifier.selectedBorder(
|
||||
isSelected: Boolean,
|
||||
width: Dp = 2.5.dp,
|
||||
color: Color = TangemTheme.colors.text.accent.copy(alpha = 0.1f),
|
||||
color: Color = TangemTheme.colors.text.accent,
|
||||
radius: Dp = 16.dp,
|
||||
) = conditionalCompose(
|
||||
condition = isSelected,
|
||||
modifier = {
|
||||
border(
|
||||
outsetBorder(
|
||||
width = width,
|
||||
color = color,
|
||||
shape = RoundedCornerShape(radius),
|
||||
color = color.copy(alpha = 0.15f),
|
||||
shape = RoundedCornerShape(radius + 2.dp),
|
||||
)
|
||||
.padding(width)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = TangemTheme.colors.text.accent,
|
||||
shape = RoundedCornerShape(radius - 2.dp),
|
||||
color = color,
|
||||
shape = RoundedCornerShape(radius),
|
||||
)
|
||||
.clip(RoundedCornerShape(radius - 2.dp))
|
||||
.clip(RoundedCornerShape(radius))
|
||||
},
|
||||
otherModifier = {
|
||||
padding(width)
|
||||
.clip(RoundedCornerShape(radius - 2.dp))
|
||||
clip(RoundedCornerShape(radius))
|
||||
},
|
||||
)
|
||||
|
|
@ -121,7 +121,10 @@ fun BigDecimalFiatFormat.price(): BigDecimalFormat = BigDecimalFormat { value ->
|
|||
|
||||
private fun BigDecimal.isLessThanThreshold() = this > BigDecimal.ZERO && this < FIAT_FORMAT_THRESHOLD
|
||||
|
||||
private fun getFiatPriceAmountWithScale(value: BigDecimal): Pair<BigDecimal, Int> {
|
||||
/**
|
||||
* Returns amount with correct scale
|
||||
*/
|
||||
fun getFiatPriceAmountWithScale(value: BigDecimal): Pair<BigDecimal, Int> {
|
||||
return if (value < BigDecimal.ONE) {
|
||||
val leadingZeroes = value.scale() - value.precision()
|
||||
val scale = leadingZeroes + FRACTIONAL_PART_LENGTH_AFTER_LEADING_ZEROES
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ class TangemColors internal constructor(
|
|||
control: Control,
|
||||
stroke: Stroke,
|
||||
field: Field,
|
||||
overlay: Overlay,
|
||||
) {
|
||||
var text by mutableStateOf(text)
|
||||
private set
|
||||
|
|
@ -31,6 +32,7 @@ class TangemColors internal constructor(
|
|||
private set
|
||||
var field by mutableStateOf(field)
|
||||
private set
|
||||
var overlay by mutableStateOf(overlay)
|
||||
|
||||
@Stable
|
||||
class Text internal constructor(
|
||||
|
|
@ -220,6 +222,22 @@ class TangemColors internal constructor(
|
|||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
class Overlay internal constructor(
|
||||
primary: Color,
|
||||
secondary: Color,
|
||||
) {
|
||||
var primary by mutableStateOf(primary)
|
||||
private set
|
||||
var secondary by mutableStateOf(secondary)
|
||||
private set
|
||||
|
||||
fun update(other: Overlay) {
|
||||
primary = other.primary
|
||||
secondary = other.secondary
|
||||
}
|
||||
}
|
||||
|
||||
fun update(other: TangemColors) {
|
||||
text.update(other.text)
|
||||
icon.update(other.icon)
|
||||
|
|
@ -228,5 +246,6 @@ class TangemColors internal constructor(
|
|||
control.update(other.control)
|
||||
stroke.update(other.stroke)
|
||||
field.update(other.field)
|
||||
overlay.update(other.overlay)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,9 +4,9 @@ import android.app.Activity
|
|||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
|
||||
import androidx.compose.foundation.text.selection.TextSelectionColors
|
||||
import androidx.compose.material.Colors
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.ProvideTextStyle
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ProvideTextStyle
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
|
@ -90,7 +90,7 @@ fun TangemTheme(
|
|||
val rootBackgroundColor = rememberedColors.background.secondary
|
||||
|
||||
MaterialTheme(
|
||||
colors = materialThemeColors(colors = themeColors, isDark = isDark),
|
||||
colorScheme = tangemColorScheme(colors = themeColors),
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalTangemColors provides rememberedColors,
|
||||
|
|
@ -143,21 +143,51 @@ object TangemTheme {
|
|||
|
||||
@Stable
|
||||
@Composable
|
||||
private fun materialThemeColors(colors: TangemColors, isDark: Boolean): Colors {
|
||||
return Colors(
|
||||
private fun tangemColorScheme(colors: TangemColors): ColorScheme {
|
||||
return ColorScheme(
|
||||
primary = colors.background.primary,
|
||||
primaryVariant = colors.background.secondary,
|
||||
secondary = colors.button.primary,
|
||||
secondaryVariant = colors.text.accent,
|
||||
background = colors.background.primary,
|
||||
surface = colors.background.secondary,
|
||||
error = colors.text.warning,
|
||||
onPrimary = colors.text.primary1,
|
||||
primaryContainer = colors.background.secondary,
|
||||
onPrimaryContainer = colors.background.action,
|
||||
inversePrimary = colors.background.action,
|
||||
|
||||
secondary = colors.button.primary,
|
||||
onSecondary = colors.text.primary1,
|
||||
secondaryContainer = colors.background.secondary,
|
||||
onSecondaryContainer = colors.text.primary1,
|
||||
|
||||
tertiary = colors.background.tertiary,
|
||||
onTertiary = colors.text.tertiary,
|
||||
tertiaryContainer = colors.background.tertiary,
|
||||
onTertiaryContainer = colors.text.tertiary,
|
||||
|
||||
background = colors.background.primary,
|
||||
onBackground = colors.text.primary1,
|
||||
|
||||
surface = colors.background.secondary,
|
||||
surfaceVariant = colors.background.tertiary,
|
||||
onSurface = colors.text.primary1,
|
||||
onSurfaceVariant = colors.text.secondary,
|
||||
surfaceTint = colors.background.tertiary,
|
||||
inverseSurface = colors.button.disabled,
|
||||
inverseOnSurface = colors.button.primary,
|
||||
surfaceBright = colors.background.secondary,
|
||||
surfaceDim = colors.background.tertiary,
|
||||
surfaceContainer = colors.background.tertiary,
|
||||
surfaceContainerHigh = colors.background.tertiary,
|
||||
surfaceContainerHighest = colors.background.tertiary,
|
||||
surfaceContainerLow = colors.background.tertiary,
|
||||
surfaceContainerLowest = colors.background.tertiary,
|
||||
|
||||
error = colors.text.warning,
|
||||
errorContainer = colors.background.tertiary,
|
||||
onErrorContainer = colors.text.primary2,
|
||||
onError = colors.text.primary2,
|
||||
isLight = !isDark,
|
||||
|
||||
outline = colors.stroke.primary,
|
||||
outlineVariant = colors.stroke.secondary,
|
||||
|
||||
scrim = colors.stroke.transparency,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -208,6 +238,10 @@ private fun lightThemeColors(): TangemColors {
|
|||
primary = TangemColorPalette.Light1,
|
||||
focused = TangemColorPalette.Light2,
|
||||
),
|
||||
overlay = TangemColors.Overlay(
|
||||
primary = TangemColorPalette.Black.copy(alpha = 0.4f),
|
||||
secondary = TangemColorPalette.Black.copy(alpha = 0.7f),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -258,6 +292,10 @@ private fun darkThemeColors(): TangemColors {
|
|||
primary = TangemColorPalette.Dark5,
|
||||
focused = TangemColorPalette.Dark4,
|
||||
),
|
||||
overlay = TangemColors.Overlay(
|
||||
primary = TangemColorPalette.Black.copy(alpha = 0.4f),
|
||||
secondary = TangemColorPalette.Black.copy(alpha = 0.7f),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
package com.tangem.core.ui.screen
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
/**
|
||||
* An abstract base class for bottom sheet dialogs that use Compose for UI rendering.
|
||||
* Extends [BottomSheetDialogFragment] and implements [ComposeScreen] interface.
|
||||
*/
|
||||
abstract class ComposeBottomSheetFragment : BottomSheetDialogFragment(), ComposeScreen {
|
||||
|
||||
/**
|
||||
* The initial state of the bottom sheet. Default is [BottomSheetBehavior.STATE_EXPANDED].
|
||||
*/
|
||||
open val initialBottomSheetState = BottomSheetBehavior.STATE_EXPANDED
|
||||
|
||||
/**
|
||||
* The fraction of the screen height that the bottom sheet should take when expanded.
|
||||
* Default is `null`, indicating that the height will be determined by the content.
|
||||
*/
|
||||
@FloatRange(from = 0.0, to = 1.0)
|
||||
open val expandedHeightFraction: Float? = null
|
||||
|
||||
override val screenModifier: Modifier
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = Modifier
|
||||
.fillMaxWidth()
|
||||
.let {
|
||||
if (expandedHeightFraction != null) it.fillMaxHeight(expandedHeightFraction!!) else it
|
||||
}
|
||||
.background(
|
||||
color = TangemTheme.colors.background.primary,
|
||||
shape = TangemTheme.shapes.bottomSheet,
|
||||
)
|
||||
|
||||
override fun getTheme(): Int = R.style.AppTheme_TransparentBottomSheetDialog
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
return createComposeView(
|
||||
context = inflater.context,
|
||||
activity = requireActivity(),
|
||||
overrideSystemBarColors = false,
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val dialog = super.onCreateDialog(savedInstanceState)
|
||||
|
||||
(dialog as BottomSheetDialog).behavior.apply {
|
||||
state = initialBottomSheetState
|
||||
skipCollapsed = true
|
||||
}
|
||||
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
package com.tangem.core.ui.screen
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.tangem.core.ui.R
|
||||
|
||||
/**
|
||||
* An abstract base class for fragments that use Compose for UI rendering.
|
||||
* Extends [Fragment] and implements [ComposeScreen] interface.
|
||||
*/
|
||||
abstract class ComposeFragment : Fragment(), ComposeScreen {
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
val isTransitionsInflated = TransitionInflater.from(requireContext()).inflateTransitions()
|
||||
|
||||
return createComposeView(inflater.context, requireActivity()).also {
|
||||
it.isTransitionGroup = isTransitionsInflated
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
|
||||
/*
|
||||
* We need to manually dispatch configuration changes to the Compose view.
|
||||
*
|
||||
|
||||
* `android:configChanges="uiMode"` is set in the manifest.
|
||||
* */
|
||||
view?.dispatchConfigurationChanged(newConfig)
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates transitions for the fragment. Override this method to customize
|
||||
* enter and exit transitions for the fragment.
|
||||
*
|
||||
* @return `true` if transitions were inflated; `false` otherwise.
|
||||
*/
|
||||
protected open fun TransitionInflater.inflateTransitions(): Boolean {
|
||||
enterTransition = inflateTransition(R.transition.fade)
|
||||
exitTransition = inflateTransition(R.transition.fade)
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object BaseBlockTestTags {
|
||||
const val BLOCK = "BASE_BLOCK"
|
||||
const val BLOCK_TITLE = "BASE_BLOCK_TITLE"
|
||||
const val BLOCK_TEXT = "BASE_BLOCK_REWARDS_TEXT"
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object BaseButtonTestTags {
|
||||
const val BUTTON = "BASE_BUTTON"
|
||||
const val ICON = "BASE_BUTTON_ICON"
|
||||
const val TEXT = "BASE_BUTTON_TEXT"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object BuyTokenDetailsScreenTestTags {
|
||||
const val EXPAND_FIAT_LIST_BUTTON = "BUY_TOKEN_DETAILS_SCREEN_EXPAND_FIAT_LIST_BUTTON"
|
||||
const val FIAT_CURRENCY_ICON = "BUY_TOKEN_DETAILS_SCREEN_FIAT_CURRENCY_ICON"
|
||||
const val FIAT_AMOUNT_TEXT_FIELD = "BUY_TOKEN_DETAILS_SCREEN_FIAT_AMOUNT_TEXT_FIELD"
|
||||
const val TOKEN_AMOUNT = "BUY_TOKEN_DETAILS_SCREEN_TOKEN_AMOUNT"
|
||||
|
||||
const val PROVIDER_LOADING_TITLE = "BUY_TOKEN_DETAILS_SCREEN_PROVIDER_LOADING_TITLE"
|
||||
const val PROVIDER_LOADING_TEXT = "BUY_TOKEN_DETAILS_SCREEN_PROVIDER_LOADING_TITLE"
|
||||
|
||||
const val PROVIDER_TITLE = "BUY_TOKEN_DETAILS_SCREEN_PROVIDER_TITLE"
|
||||
const val PROVIDER_TEXT = "BUY_TOKEN_DETAILS_SCREEN_PROVIDER_TEXT"
|
||||
|
||||
const val TOS_BLOCK = "BUY_TOKEN_DETAILS_SCREEN_TOS_BLOCK"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object BuyTokenFiatListTestTags {
|
||||
const val LAZY_LIST = "BUY_TOKEN_FIAT_LIST_LAZY_LIST"
|
||||
const val LAZY_LIST_ITEM = "BUY_TOKEN_FIAT_LIST_LAZY_LIST_ITEM"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object BuyTokenScreenTestTags {
|
||||
const val LAZY_LIST = "BUY_TOKEN_SCREEN_LAZY_LIST"
|
||||
const val LAZY_LIST_ITEM = "BUY_TOKEN_SCREEN_LAZY_LIST_ITEM"
|
||||
}
|
||||
|
|
@ -2,5 +2,4 @@ package com.tangem.core.ui.test
|
|||
|
||||
object DialogTestTags {
|
||||
const val DIALOG_CONTAINER = "DIALOG_CONTAINER"
|
||||
const val BUTTON = "DIALOG_BUTTON"
|
||||
}
|
||||
|
|
@ -3,4 +3,5 @@ package com.tangem.core.ui.test
|
|||
object DisclaimerScreenTestTags {
|
||||
const val SCREEN_CONTAINER = "DISCLAIMER_SCREEN_CONTAINER"
|
||||
const val ACCEPT_BUTTON = "DISCLAIMER_SCREEN_ACCEPT_BUTTON"
|
||||
const val WEB_VIEW = "DISCLAIMER_SCREEN_WEB_VIEW"
|
||||
}
|
||||
|
|
@ -8,4 +8,5 @@ object MainScreenTestTags {
|
|||
const val WALLET_BALANCE = "MAIN_SCREEN_WALLET_BALANCE"
|
||||
const val WALLET_LIST_ITEM = "MAIN_SCREEN_WALLET_LIST_ITEM"
|
||||
const val ORGANIZE_TOKENS_BUTTON = "MAIN_SCREEN_ORGANIZE_TOKENS_BUTTON"
|
||||
const val MULTI_CURRENCY_ACTION_BUTTON = "MAIN_SCREEN_MULTI_CURRENCY_ACTION_BUTTON"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object NotificationTestTags {
|
||||
const val TITLE = "NOTIFICATION_TITLE"
|
||||
const val TEXT = "NOTIFICATION_TEXT"
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object ReferralProgramScreenTestTags {
|
||||
const val IMAGE = "REFERRAL_PROGRAM_SCREEN_IMAGE"
|
||||
const val CONDITION_BLOCK = "REFERRAL_PROGRAM_SCREEN_CONDITION_BLOCK"
|
||||
const val INFO_FOR_YOU_TEXT = "REFERRAL_PROGRAM_SCREEN_INFO_FOR_YOU_TEXT"
|
||||
const val INFO_FOR_YOUR_FRIEND_TEXT = "REFERRAL_PROGRAM_INFO_FOR_YOUR_FRIEND_TEXT"
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object ResidenceSettingsScreenTestTags {
|
||||
const val COUNTRY_NAME = "RESIDENCE_SETTINGS_SCREEN_COUNTRY_NAME"
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object SelectCountryBottomSheetTestTags {
|
||||
|
||||
const val LAZY_LIST = "SELECT_COUNTRY_BOTTOM_SHEET_LAZY_LIST"
|
||||
const val COUNTRY_ITEM = "SELECT_COUNTRY_BOTTOM_SHEET_COUNTRY_ITEM"
|
||||
const val UNAVAILABLE_COUNTRY_ITEM = "SELECT_COUNTRY_BOTTOM_SHEET_UNAVAILABLE_COUNTRY_ITEM"
|
||||
const val SEARCH_BAR = "SELECT_COUNTRY_BOTTOM_SHEET_SEARCH_BAR"
|
||||
const val COUNTRY_ICON = "SELECT_COUNTRY_BOTTOM_SHEET_COUNTRY_ICON"
|
||||
const val UNAVAILABLE_COUNTRY_ICON = "SELECT_COUNTRY_BOTTOM_SHEET_UNAVAILABLE_COUNTRY_ICON"
|
||||
const val COUNTRY_NAME = "SELECT_COUNTRY_BOTTOM_SHEET_COUNTRY_NAME"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object SelectNetworkFeeBottomSheetTestTags {
|
||||
const val READ_MORE_TEXT = "SELECT_NETWORK_FEE_READ_MORE_TEXT"
|
||||
const val SELECTOR_ITEM = "SELECT_NETWORK_FEE_SELECTOR_ITEM"
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object SelectPaymentMethodBottomSheetTestTags {
|
||||
|
||||
const val LAZY_LIST = "SELECT_PAYMENT_METHOD_LAZY_LIST"
|
||||
const val PAYMENT_METHOD_ICON = "PAYMENT_METHOD_NAME"
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object SelectProviderBottomSheetTestTags {
|
||||
|
||||
const val PAYMENT_METHOD_ICON = "SELECT_PROVIDER_BOTTOM_SHEET_PAYMENT_METHOD_ICON"
|
||||
const val PAYMENT_METHOD_TITLE = "SELECT_PROVIDER_BOTTOM_SHEET_PAYMENT_METHOD_TITLE"
|
||||
const val PAYMENT_METHOD_NAME = "SELECT_PROVIDER_BOTTOM_SHEET_PAYMENT_METHOD_NAME"
|
||||
const val PAYMENT_METHOD_EXPAND_BUTTON = "SELECT_PROVIDER_BOTTOM_SHEET_PAYMENT_METHOD_EXPAND_BUTTON"
|
||||
const val TOKEN_AMOUNT = "SELECT_PROVIDER_BOTTOM_SHEET_TOKEN_AMOUNT"
|
||||
const val AVAILABLE_PROVIDER_NAME = "SELECT_PROVIDER_BOTTOM_SHEET_AVAILABLE_PROVIDER_NAME"
|
||||
const val AVAILABLE_PROVIDER_ITEM = "SELECT_PROVIDER_BOTTOM_SHEET_AVAILABLE_PROVIDER_ITEM"
|
||||
const val UNAVAILABLE_PROVIDER_ITEM = "SELECT_PROVIDER_BOTTOM_SHEET_UNAVAILABLE_PROVIDER_ITEM"
|
||||
const val UNAVAILABLE_PROVIDER_NAME = "SELECT_PROVIDER_BOTTOM_SHEET_UNAVAILABLE_PROVIDER_NAME"
|
||||
const val UNAVAILABLE_PROVIDER_SUBTITLE = "SELECT_PROVIDER_BOTTOM_SHEET_UNAVAILABLE_PROVIDER_SUBTITLE"
|
||||
const val MORE_PROVIDERS_ICON = "SELECT_PROVIDER_BOTTOM_SHEET_MORE_PROVIDERS_ICON"
|
||||
const val MORE_PROVIDERS_TEXT = "SELECT_PROVIDER_BOTTOM_SHEET_MORE_PROVIDERS_TEXT"
|
||||
const val BEST_RATE_LABEL = "SELECT_PROVIDER_BOTTOM_SHEET_BEST_RATE_LABEL"
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object StakingDetailsScreenTestTags {
|
||||
const val SCREEN_CONTAINER = "TOKEN_DETAILS_SCREEN_CONTAINER"
|
||||
|
||||
const val BANNER_IMAGE = "TOKEN_DETAILS_SCREEN_BANNER_IMAGE"
|
||||
const val BANNER_TEXT = "TOKEN_DETAILS_SCREEN_BANNER_TEXT"
|
||||
|
||||
const val PARAMETER_BLOCK = "STAKING_DETAILS_PARAMETER_BLOCK"
|
||||
const val PARAMETER_NAME = "STAKING_DETAILS_PARAMETER_NAME"
|
||||
const val PARAMETER_VALUE = "STAKING_DETAILS_PARAMETER_VALUE"
|
||||
const val TOS_TEXT = "STAKING_DETAILS_TOS_TEXT"
|
||||
|
||||
const val ACTIVE_STAKING_BLOCK = "STAKING_DETAILS_ACTIVE_STAKING_BLOCK"
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object StakingSendDetailsScreenTestTags {
|
||||
|
||||
const val PRIMARY_AMOUNT = "STAKING_SEND_DETAILS_SCREEN_PRIMARY_AMOUNT"
|
||||
const val SECONDARY_AMOUNT = "TAKING_SEND_DETAILS_SCREEN_SECONDARY_AMOUNT"
|
||||
|
||||
const val VALIDATOR_BLOCK = "TAKING_SEND_DETAILS_SCREEN_VALIDATOR_BLOCK"
|
||||
const val NETWORK_FEE_BLOCK = "TAKING_SEND_DETAILS_SCREEN_NETWORK_FEE_BLOCK"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object StakingSendScreenTestTags {
|
||||
const val SCREEN_CONTAINER = "STAKING_SEND_SCREEN_CONTAINER"
|
||||
|
||||
const val AMOUNT_CONTAINER_TITLE = "STAKING_SEND_SCREEN_AMOUNT_CONTAINER_TITLE"
|
||||
const val AMOUNT_CONTAINER_TEXT = "STAKING_SEND_SCREEN_AMOUNT_CONTAINER_TEXT"
|
||||
const val INPUT_TEXT_FIELD = "STAKING_SEND_SCREEN_INPUT_TEXT_FIELD"
|
||||
const val SECONDARY_AMOUNT = "STAKING_SEND_SCREEN_SECONDARY_AMOUNT"
|
||||
|
||||
const val CURRENCY_BUTTON = "STAKING_SEND_SCREEN_CURRENCY_BUTTON"
|
||||
const val FIAT_ICON = "STAKING_SEND_SCREEN_FIAT_ICON"
|
||||
const val CURRENCY_ICON = "STAKING_SEND_SCREEN_CURRENCY_ICON"
|
||||
const val MAX_BUTTON = "STAKING_SEND_SCREEN_MAX_BUTTON"
|
||||
const val PREVIOUS_BUTTON = "STAKING_SEND_SCREEN_PREVIOUS_BUTTON"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object SwapStoriesScreenTestTags {
|
||||
const val SCREEN_CONTAINER = "SWAP_STORIES_SCREEN_CONTAINER"
|
||||
const val CLOSE_BUTTON = "SWAP_STORIES_SCREEN_CLOSE_BUTTON"
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object SwapTokenScreenTestTags {
|
||||
const val SWAP_BLOCK_HEADER = "SWAP_TOKEN_SCREEN_SWAP_BLOCK"
|
||||
const val BALANCE = "SWAP_TOKEN_SCREEN_BALANCE"
|
||||
const val SWAP_TEXT_FIELD = "SWAP_TOKEN_SCREEN_SWAP_TEXT_FIELD"
|
||||
const val RECEIVE_TEXT_FIELD = "SWAP_TOKEN_SCREEN_RECEIVE_TEXT_FIELD"
|
||||
const val RECEIVE_AMOUNT_SHIMMER = "SWAP_TOKEN_SCREEN_RECEIVE_AMOUNT_SHIMMER"
|
||||
const val PROVIDERS_BLOCK = "SWAP_TOKEN_SCREEN_PROVIDERS_BLOCK"
|
||||
const val SWAP_BUTTON = "SWAP_TOKEN_SCREEN_SWAP_BUTTON"
|
||||
const val TOKEN = "SWAP_TOKEN_SCREEN_TOKEN"
|
||||
const val TOKEN_NAME = "SWAP_TOKEN_SCREEN_TOKEN_NAME"
|
||||
const val TOKEN_ICON = "SWAP_TOKEN_SCREEN_TOKEN_ICON"
|
||||
}
|
||||
|
|
@ -2,4 +2,19 @@ package com.tangem.core.ui.test
|
|||
|
||||
object TokenDetailsScreenTestTags {
|
||||
const val SCREEN_CONTAINER = "TOKEN_DETAILS_SCREEN_CONTAINER"
|
||||
|
||||
const val TOKEN_TITLE = "TOKEN_DETAILS_SCREEN_TOKEN_TITLE"
|
||||
const val ACTION_BUTTON = "TOKEN_DETAILS_SCREEN_ACTION_BUTTON"
|
||||
const val HORIZONTAL_ACTION_CHIPS = "TOKEN_DETAILS_SCREEN_HORIZONTAL_ACTION_CHIPS"
|
||||
|
||||
const val STAKING_BLOCK = "TOKEN_DETAILS_SCREEN_STAKING_BLOCK"
|
||||
const val STAKING_AVAILABLE_BLOCK = "TOKEN_DETAILS_SCREEN_STAKING_AVAILABLE_BLOCK"
|
||||
const val STAKING_CURRENCY_ICON = "TOKEN_DETAILS_SCREEN_STAKING_STAKING_CURRENCY_ICON"
|
||||
const val STAKING_SERVICE_TITLE = "TOKEN_DETAILS_SCREEN_STAKING_STAKING_SERVICE_TITLE"
|
||||
const val STAKING_SERVICE_TEXT = "TOKEN_DETAILS_SCREEN_STAKING_STAKING_SERVICE_TEXT"
|
||||
const val STAKING_FIAT_AMOUNT = "TOKEN_DETAILS_SCREEN_STAKING_FIAT_AMOUNT"
|
||||
const val STAKING_DOT = "TOKEN_DETAILS_SCREEN_STAKING_DOT"
|
||||
const val STAKING_TOKEN_AMOUNT = "TOKEN_DETAILS_SCREEN_STAKING_TOKEN_AMOUNT"
|
||||
const val STAKING_REWARD_VALUE = "TOKEN_DETAILS_SCREEN_STAKING_REWARD_VALUE"
|
||||
const val STAKING_CHEVRON_ICON = "TOKEN_DETAILS_SCREEN_STAKING_CHEVRON_ICON"
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object TopAppBarTestTags {
|
||||
const val TITLE = "TOP_APP_BAR_TITLE"
|
||||
const val MORE_BUTTON = "TOP_APP_BAR_MORE_BUTTON"
|
||||
const val CLOSE_BUTTON = "TOP_APP_BAR_CLOSE_BUTTON"
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.tangem.core.ui.utils
|
||||
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.animation.core.AnimationVector1D
|
||||
import androidx.compose.animation.core.Easing
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
typealias AnimatedValue = Pair<Float, Float>
|
||||
|
||||
@Composable
|
||||
fun AnimatedValue.toAnimatable(
|
||||
isPaused: Boolean,
|
||||
duration: Int,
|
||||
easing: Easing = LinearEasing,
|
||||
): Animatable<Float, AnimationVector1D> {
|
||||
return animatable(
|
||||
values = this,
|
||||
isPaused = isPaused,
|
||||
duration = duration,
|
||||
easing = easing,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun animatable(
|
||||
values: AnimatedValue,
|
||||
duration: Int,
|
||||
isPaused: Boolean = false,
|
||||
easing: Easing = LinearEasing,
|
||||
): Animatable<Float, AnimationVector1D> {
|
||||
val animatable = remember { Animatable(values.first) }
|
||||
|
||||
LaunchedEffect(isPaused) {
|
||||
if (isPaused) {
|
||||
animatable.stop()
|
||||
} else {
|
||||
animatable.animateTo(
|
||||
targetValue = values.second,
|
||||
animationSpec = tween(
|
||||
durationMillis = duration,
|
||||
easing = easing,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
return animatable
|
||||
}
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
package com.tangem.core.ui.utils
|
||||
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import com.tangem.utils.StringsSigns.LOWER_SIGN
|
||||
import com.tangem.utils.StringsSigns.TILDE_SIGN
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import java.text.NumberFormat
|
||||
import java.util.Currency
|
||||
import java.util.Locale
|
||||
|
||||
@Suppress("LargeClass")
|
||||
@Deprecated("Use BigDecimal.format")
|
||||
object BigDecimalFormatter {
|
||||
|
||||
const val EMPTY_BALANCE_SIGN = DASH_SIGN
|
||||
private const val CAN_BE_LOWER_SIGN = LOWER_SIGN
|
||||
|
||||
private val FIAT_FORMAT_THRESHOLD = BigDecimal("0.01")
|
||||
|
||||
private const val FIAT_MARKET_DEFAULT_DIGITS = 2
|
||||
private const val FIAT_MARKET_EXTENDED_DIGITS = 6
|
||||
private const val FRACTIONAL_PART_LENGTH_AFTER_LEADING_ZEROES = 4
|
||||
|
||||
private val usdCurrency = Currency.getInstance("USD")
|
||||
|
||||
@Deprecated("Use BigDecimal.format")
|
||||
fun formatFiatAmount(
|
||||
fiatAmount: BigDecimal?,
|
||||
fiatCurrencyCode: String,
|
||||
fiatCurrencySymbol: String,
|
||||
decimals: Int = FIAT_MARKET_DEFAULT_DIGITS,
|
||||
locale: Locale = Locale.getDefault(),
|
||||
withApproximateSign: Boolean = false,
|
||||
): String {
|
||||
if (fiatAmount == null) return EMPTY_BALANCE_SIGN
|
||||
|
||||
val formatterCurrency = getCurrency(fiatCurrencyCode)
|
||||
val formatter = NumberFormat.getCurrencyInstance(locale).apply {
|
||||
currency = formatterCurrency
|
||||
maximumFractionDigits = decimals
|
||||
minimumFractionDigits = decimals
|
||||
roundingMode = RoundingMode.HALF_UP
|
||||
}
|
||||
|
||||
return if (fiatAmount.checkFiatThreshold()) {
|
||||
buildString {
|
||||
append(CAN_BE_LOWER_SIGN)
|
||||
append(
|
||||
formatter.format(FIAT_FORMAT_THRESHOLD)
|
||||
.replace(formatterCurrency.getSymbol(locale), fiatCurrencySymbol),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val formattedAmount = formatter.format(fiatAmount)
|
||||
.replace(formatterCurrency.getSymbol(locale), fiatCurrencySymbol)
|
||||
|
||||
if (withApproximateSign) {
|
||||
buildString {
|
||||
append(TILDE_SIGN)
|
||||
append(formattedAmount)
|
||||
}
|
||||
} else {
|
||||
formattedAmount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Use BigDecimal.format")
|
||||
fun formatFiatAmountUncapped(
|
||||
fiatAmount: BigDecimal?,
|
||||
fiatCurrencyCode: String,
|
||||
fiatCurrencySymbol: String,
|
||||
locale: Locale = Locale.getDefault(),
|
||||
): String {
|
||||
if (fiatAmount == null) return EMPTY_BALANCE_SIGN
|
||||
val formatterCurrency = getCurrency(fiatCurrencyCode)
|
||||
|
||||
val digits = if (fiatAmount.checkFiatThreshold()) {
|
||||
FIAT_MARKET_EXTENDED_DIGITS
|
||||
} else {
|
||||
FIAT_MARKET_DEFAULT_DIGITS
|
||||
}
|
||||
val formatter = NumberFormat.getCurrencyInstance(locale).apply {
|
||||
currency = formatterCurrency
|
||||
maximumFractionDigits = digits
|
||||
minimumFractionDigits = FIAT_MARKET_DEFAULT_DIGITS
|
||||
roundingMode = RoundingMode.HALF_UP
|
||||
}
|
||||
|
||||
return formatter.format(fiatAmount)
|
||||
.replace(formatterCurrency.getSymbol(locale), fiatCurrencySymbol)
|
||||
}
|
||||
|
||||
@Deprecated("Use BigDecimal.format")
|
||||
fun formatFiatPriceUncapped(
|
||||
fiatAmount: BigDecimal?,
|
||||
fiatCurrencyCode: String,
|
||||
fiatCurrencySymbol: String,
|
||||
locale: Locale = Locale.getDefault(),
|
||||
): String {
|
||||
if (fiatAmount == null) return EMPTY_BALANCE_SIGN
|
||||
val formatterCurrency = getCurrency(fiatCurrencyCode)
|
||||
|
||||
val (formattedAmount, finalScale) = getFiatPriceUncappedWithScale(value = fiatAmount)
|
||||
|
||||
val formatter = NumberFormat.getCurrencyInstance(locale).apply {
|
||||
currency = formatterCurrency
|
||||
maximumFractionDigits = finalScale
|
||||
minimumFractionDigits = FIAT_MARKET_DEFAULT_DIGITS
|
||||
roundingMode = RoundingMode.HALF_UP
|
||||
}
|
||||
|
||||
return formatter.format(formattedAmount)
|
||||
.replace(formatterCurrency.getSymbol(locale), fiatCurrencySymbol)
|
||||
}
|
||||
|
||||
@Deprecated("Use BigDecimal.format")
|
||||
fun getFiatPriceUncappedWithScale(value: BigDecimal): Pair<BigDecimal, Int> {
|
||||
return if (value < BigDecimal.ONE) {
|
||||
val leadingZeroes = value.scale() - value.precision()
|
||||
val scale = leadingZeroes + FRACTIONAL_PART_LENGTH_AFTER_LEADING_ZEROES
|
||||
|
||||
val amount = value
|
||||
.setScale(scale, RoundingMode.HALF_UP)
|
||||
.stripTrailingZeros()
|
||||
|
||||
amount to amount.scale()
|
||||
} else {
|
||||
value to FIAT_MARKET_DEFAULT_DIGITS
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCurrency(code: String): Currency {
|
||||
return runCatching { Currency.getInstance(code) }
|
||||
.getOrElse { e ->
|
||||
// Currency code is not valid ISO 4217 code
|
||||
if (e is IllegalArgumentException) {
|
||||
usdCurrency
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BigDecimal.checkFiatThreshold() = this > BigDecimal.ZERO && this < FIAT_FORMAT_THRESHOLD
|
||||
}
|
||||
|
|
@ -1,9 +1,15 @@
|
|||
package com.tangem.core.ui.utils
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Stable
|
||||
@Composable
|
||||
|
|
@ -15,4 +21,16 @@ fun convertPxToDp(px: Float): Dp = convertPxToDp(px, density = LocalDensity.curr
|
|||
|
||||
fun Dp.toPx(density: Float): Float = this.value * density
|
||||
|
||||
fun convertPxToDp(px: Float, density: Float): Dp = Dp(value = px / density)
|
||||
fun convertPxToDp(px: Float, density: Float): Dp = Dp(value = px / density)
|
||||
|
||||
fun Context.dpToPx(dp: Float): Float = dp * resources.displayMetrics.density
|
||||
fun Context.pxToDp(px: Float): Float = (px / resources.displayMetrics.density).roundToInt().toFloat()
|
||||
|
||||
@Composable
|
||||
fun Painter.dpSize(): DpSize = DpSize(
|
||||
intrinsicSize.width.pxToDp().dp,
|
||||
intrinsicSize.height.pxToDp().dp,
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun Float.pxToDp(): Float = LocalContext.current.pxToDp(this)
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.core.ui.utils
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Composable
|
||||
fun asImageBitmap(@DrawableRes drawableId: Int): ImageBitmap {
|
||||
val drawable = requireNotNull(AppCompatResources.getDrawable(LocalContext.current, drawableId)) {
|
||||
"drawable is null"
|
||||
}
|
||||
return drawable.toBitmap().asImageBitmap()
|
||||
}
|
||||
BIN
core/ui/src/main/res/drawable-hdpi/img_meet_tangem.webp
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
core/ui/src/main/res/drawable-hdpi/img_revolutionary_wallet.webp
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
core/ui/src/main/res/drawable-hdpi/img_tangem_for_everyone.webp
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
core/ui/src/main/res/drawable-mdpi/img_meet_tangem.webp
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
core/ui/src/main/res/drawable-mdpi/img_revolutionary_wallet.webp
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
core/ui/src/main/res/drawable-mdpi/img_tangem_for_everyone.webp
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
core/ui/src/main/res/drawable-xhdpi/img_meet_tangem.webp
Normal file
|
After Width: | Height: | Size: 234 KiB |
|
After Width: | Height: | Size: 86 KiB |
BIN
core/ui/src/main/res/drawable-xhdpi/img_tangem_for_everyone.webp
Normal file
|
After Width: | Height: | Size: 222 KiB |
BIN
core/ui/src/main/res/drawable-xxhdpi/img_meet_tangem.webp
Normal file
|
After Width: | Height: | Size: 509 KiB |
|
After Width: | Height: | Size: 156 KiB |
|
After Width: | Height: | Size: 484 KiB |
BIN
core/ui/src/main/res/drawable-xxxhdpi/img_meet_tangem.webp
Normal file
|
After Width: | Height: | Size: 901 KiB |
|
After Width: | Height: | Size: 243 KiB |
|
After Width: | Height: | Size: 818 KiB |
BIN
core/ui/src/main/res/drawable/currency0.webp
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
core/ui/src/main/res/drawable/currency1.webp
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
core/ui/src/main/res/drawable/currency2.webp
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
core/ui/src/main/res/drawable/currency3.webp
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
core/ui/src/main/res/drawable/currency4.webp
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
core/ui/src/main/res/drawable/dapps1.webp
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
core/ui/src/main/res/drawable/dapps2.webp
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
core/ui/src/main/res/drawable/dapps3.webp
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
core/ui/src/main/res/drawable/dapps4.webp
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
core/ui/src/main/res/drawable/dapps5.webp
Normal file
|
After Width: | Height: | Size: 128 KiB |
13
core/ui/src/main/res/drawable/ic_archive_24.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M0,0h24v24h-24z"/>
|
||||
<path
|
||||
android:pathData="M12,18L16,14L14.6,12.6L13,14.2V10H11V14.2L9.4,12.6L8,14L12,18ZM5,8V19H19V8H5ZM5,21C4.45,21 3.979,20.804 3.588,20.413C3.196,20.021 3,19.55 3,19V6.525C3,6.292 3.037,6.067 3.112,5.85C3.188,5.633 3.3,5.433 3.45,5.25L4.7,3.725C4.883,3.492 5.113,3.313 5.387,3.188C5.662,3.063 5.95,3 6.25,3H17.75C18.05,3 18.337,3.063 18.612,3.188C18.888,3.313 19.117,3.492 19.3,3.725L20.55,5.25C20.7,5.433 20.813,5.633 20.888,5.85C20.962,6.067 21,6.292 21,6.525V19C21,19.55 20.804,20.021 20.413,20.413C20.021,20.804 19.55,21 19,21H5ZM5.4,6H18.6L17.75,5H6.25L5.4,6Z"
|
||||
android:fillColor="#000000"/>
|
||||
</group>
|
||||
</vector>
|
||||
9
core/ui/src/main/res/drawable/ic_hyperliquid_22.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="22dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="22">
|
||||
<path
|
||||
android:pathData="M20,10.912C20,17.156 16.358,19.16 14.439,17.376C12.86,15.921 12.39,12.847 10.014,12.531C6.999,12.133 6.738,16.346 4.754,16.346C2.444,16.346 2,12.819 2,11.008C2,9.155 2.496,6.63 4.467,6.63C6.764,6.63 6.895,10.24 9.767,10.047C12.625,9.842 12.677,6.081 14.531,4.476C16.149,3.09 20,4.585 20,10.912Z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
||||
19
core/ui/src/main/res/drawable/ic_mobile_wallet_icon_24.xml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<vector xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:pathData="M6.839,11.095C7.657,11.095 8.32,11.759 8.32,12.576V20.148C8.32,20.966 7.657,21.629 6.839,21.629H2.864C2.046,21.629 1.383,20.966 1.383,20.148V12.576C1.383,11.759 2.046,11.095 2.864,11.095H6.839ZM14.064,2.37C14.831,2.37 15.401,2.37 15.894,2.502C17.227,2.858 18.268,3.897 18.625,5.226C18.709,5.54 18.74,5.885 18.751,6.291C18.895,6.304 19.034,6.319 19.167,6.337C20.062,6.457 20.853,6.719 21.486,7.351C22.119,7.983 22.382,8.772 22.503,9.665C22.618,10.516 22.617,11.591 22.617,12.895V14.957C22.617,16.261 22.618,17.336 22.503,18.187C22.382,19.08 22.12,19.869 21.486,20.501C20.853,21.133 20.062,21.395 19.167,21.515C18.314,21.629 17.236,21.629 15.929,21.629H9.999C9.987,21.629 9.975,21.628 9.963,21.628C10.176,21.18 10.297,20.678 10.297,20.149V12.577C10.297,10.668 8.749,9.12 6.84,9.12H2.865C2.335,9.12 1.832,9.24 1.383,9.453V5.259C1.383,3.664 2.679,2.37 4.278,2.37H14.064ZM18.406,12.436C17.468,12.436 16.707,13.196 16.707,14.134C16.707,15.073 17.468,15.833 18.406,15.833C19.345,15.833 20.105,15.073 20.105,14.134C20.105,13.196 19.344,12.436 18.406,12.436ZM4.159,12.573C3.723,12.573 3.369,12.926 3.369,13.363C3.369,13.799 3.723,14.153 4.159,14.153H5.542C5.978,14.153 6.332,13.799 6.332,13.363C6.332,12.926 5.978,12.573 5.542,12.573H4.159ZM4.278,4.296C3.745,4.296 3.313,4.727 3.313,5.259C3.313,5.791 3.745,6.222 4.278,6.222H15.929C16.237,6.222 16.533,6.222 16.816,6.224C16.808,5.975 16.791,5.839 16.76,5.724C16.581,5.059 16.061,4.54 15.394,4.362C15.18,4.304 14.889,4.296 13.93,4.296H4.278Z">
|
||||
|
||||
<aapt:attr name="android:fillColor">
|
||||
|
||||
<gradient android:centerX="7.343" android:centerY="0.784" android:gradientRadius="33.786" android:type="radial">
|
||||
|
||||
<item android:color="#FF96999B" android:offset="0"/>
|
||||
|
||||
<item android:color="#FF4D4E53" android:offset="1"/>
|
||||
|
||||
</gradient>
|
||||
|
||||
</aapt:attr>
|
||||
|
||||
</path>
|
||||
|
||||
</vector>
|
||||
13
core/ui/src/main/res/drawable/ic_passcode_lock_32.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="33dp"
|
||||
android:viewportWidth="32"
|
||||
android:viewportHeight="33">
|
||||
<path
|
||||
android:pathData="M16,4.062C17.587,4.062 19.084,4.339 20.489,4.893C21.894,5.447 23.151,6.22 24.259,7.212C25.366,8.204 26.272,9.361 26.974,10.684C27.677,11.998 28.119,13.425 28.301,14.962H30.26C30.5,14.962 30.682,15.012 30.806,15.111C30.93,15.21 30.988,15.342 30.98,15.508C30.971,15.665 30.901,15.843 30.769,16.041L28.066,19.885C27.892,20.133 27.694,20.257 27.47,20.257C27.255,20.249 27.065,20.125 26.9,19.885L24.197,16.029C24.064,15.839 23.994,15.665 23.986,15.508C23.986,15.342 24.048,15.21 24.172,15.111C24.296,15.012 24.474,14.962 24.705,14.962H26.677C26.495,13.648 26.094,12.428 25.474,11.304C24.862,10.172 24.077,9.184 23.118,8.34C22.159,7.497 21.072,6.84 19.857,6.369C18.65,5.898 17.364,5.662 16,5.662C14.405,5.662 12.904,5.988 11.499,6.642C10.101,7.286 8.903,8.171 7.903,9.295C7.712,9.502 7.518,9.614 7.32,9.63C7.121,9.638 6.944,9.585 6.786,9.469C6.613,9.345 6.505,9.167 6.464,8.936C6.423,8.696 6.497,8.469 6.687,8.254C7.836,6.956 9.217,5.935 10.829,5.191C12.441,4.438 14.165,4.062 16,4.062ZM16,28.938C14.413,28.938 12.916,28.656 11.511,28.094C10.106,27.54 8.849,26.767 7.741,25.775C6.634,24.792 5.724,23.642 5.013,22.328C4.311,21.005 3.868,19.575 3.686,18.038H1.727C1.487,18.038 1.31,17.988 1.194,17.889C1.07,17.789 1.012,17.657 1.02,17.492C1.02,17.327 1.086,17.149 1.219,16.959L3.922,13.102C4.096,12.862 4.29,12.743 4.505,12.743C4.728,12.743 4.926,12.862 5.1,13.102L7.803,16.971C7.936,17.161 8.002,17.339 8.002,17.504C8.002,17.661 7.94,17.789 7.816,17.889C7.7,17.988 7.522,18.038 7.283,18.038H5.323C5.497,19.344 5.894,20.563 6.514,21.696C7.134,22.82 7.919,23.808 8.87,24.659C9.829,25.503 10.912,26.16 12.119,26.631C13.334,27.102 14.628,27.338 16,27.338C17.587,27.338 19.079,27.011 20.476,26.358C21.882,25.713 23.089,24.825 24.097,23.692C24.288,23.494 24.478,23.39 24.668,23.382C24.866,23.366 25.044,23.411 25.201,23.518C25.375,23.651 25.482,23.837 25.524,24.076C25.565,24.308 25.49,24.531 25.3,24.746C24.16,26.036 22.779,27.057 21.159,27.809C19.546,28.561 17.827,28.938 16,28.938Z"
|
||||
android:fillColor="#0099FF"/>
|
||||
<path
|
||||
android:pathData="M16.153,10.844C17.725,10.924 18.975,12.223 18.975,13.815V15.295C19.876,15.317 20.6,16.055 20.6,16.961V20.492C20.6,21.413 19.853,22.159 18.933,22.159H13.069C12.148,22.159 11.402,21.413 11.402,20.492V16.961C11.402,16.055 12.125,15.318 13.026,15.295V13.815C13.026,12.172 14.357,10.84 16,10.84L16.153,10.844ZM16,12.23C15.125,12.23 14.415,12.939 14.415,13.815V15.294H17.586V13.815C17.586,12.939 16.876,12.23 16,12.23Z"
|
||||
android:fillColor="#0099FF"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
20
core/ui/src/main/res/drawable/ic_passcode_lock_56.xml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="56dp"
|
||||
android:height="56dp"
|
||||
android:viewportWidth="56"
|
||||
android:viewportHeight="56">
|
||||
<path
|
||||
android:pathData="M49.875,26.906V20.475C49.875,17.902 49.875,16.616 49.377,15.633C48.939,14.769 48.24,14.066 47.381,13.626C46.403,13.125 45.124,13.125 42.566,13.125H13.434C10.876,13.125 9.597,13.125 8.619,13.626C7.76,14.066 7.061,14.769 6.623,15.633C6.125,16.616 6.125,17.902 6.125,20.475V28.744C6.125,31.316 6.125,32.603 6.623,33.585C7.061,34.45 7.76,35.153 8.619,35.593C9.597,36.094 10.876,36.094 13.434,36.094H25.716M28,24.609H28.011M38.546,24.609H38.557M17.454,24.609H17.466M28.571,24.609C28.571,24.927 28.315,25.184 28,25.184C27.685,25.184 27.429,24.927 27.429,24.609C27.429,24.292 27.685,24.035 28,24.035C28.315,24.035 28.571,24.292 28.571,24.609ZM39.117,24.609C39.117,24.927 38.861,25.184 38.546,25.184C38.23,25.184 37.975,24.927 37.975,24.609C37.975,24.292 38.23,24.035 38.546,24.035C38.861,24.035 39.117,24.292 39.117,24.609ZM18.025,24.609C18.025,24.927 17.77,25.184 17.454,25.184C17.139,25.184 16.883,24.927 16.883,24.609C16.883,24.292 17.139,24.035 17.454,24.035C17.77,24.035 18.025,24.292 18.025,24.609Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="3"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#008EFF"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M45.242,38.664V34.645C45.242,32.425 43.452,30.625 41.244,30.625C39.037,30.625 37.247,32.425 37.247,34.645V38.664M36.905,47.852H45.584C46.864,47.852 47.503,47.852 47.992,47.601C48.422,47.381 48.771,47.03 48.99,46.597C49.239,46.106 49.239,45.463 49.239,44.177V42.339C49.239,41.053 49.239,40.41 48.99,39.918C48.771,39.486 48.422,39.135 47.992,38.914C47.503,38.664 46.864,38.664 45.584,38.664H36.905C35.625,38.664 34.986,38.664 34.497,38.914C34.067,39.135 33.718,39.486 33.499,39.918C33.25,40.41 33.25,41.053 33.25,42.339V44.177C33.25,45.463 33.25,46.106 33.499,46.597C33.718,47.03 34.067,47.381 34.497,47.601C34.986,47.852 35.625,47.852 36.905,47.852Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="3"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#008EFF"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
16
core/ui/src/main/res/drawable/ic_stack_new_24.xml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M18.656,15.224C18.898,14.92 19.328,14.854 19.648,15.059L19.71,15.104L19.875,15.245C20.247,15.585 20.532,16.024 20.532,16.6C20.532,17.432 19.947,17.969 19.336,18.362C18.868,18.663 18.238,18.97 17.483,19.319L16.688,19.683L15.213,20.355C13.847,20.978 12.961,21.399 12,21.399C11.28,21.399 10.601,21.162 9.73,20.781L8.787,20.355L7.312,19.683C6.2,19.177 5.288,18.763 4.664,18.362C4.053,17.969 3.468,17.432 3.468,16.6C3.468,15.941 3.84,15.462 4.29,15.104L4.352,15.059C4.672,14.854 5.102,14.92 5.344,15.224C5.602,15.548 5.548,16.02 5.224,16.278L5.135,16.353C5.059,16.423 5.017,16.477 4.996,16.513C4.972,16.552 4.968,16.575 4.968,16.6C4.968,16.617 4.951,16.763 5.475,17.1C5.986,17.428 6.776,17.79 7.934,18.318L9.409,18.99L10.364,19.418C11.171,19.769 11.581,19.899 12,19.899C12.559,19.899 13.101,19.669 14.591,18.99L16.066,18.318L16.867,17.952C17.601,17.612 18.142,17.346 18.525,17.1C19.049,16.763 19.032,16.617 19.032,16.6C19.032,16.575 19.028,16.552 19.004,16.513C18.983,16.477 18.941,16.423 18.865,16.353L18.776,16.278L18.718,16.227C18.446,15.962 18.414,15.528 18.656,15.224Z"
|
||||
android:fillColor="#1E1E1E"/>
|
||||
<path
|
||||
android:pathData="M3.791,10.784C4.031,10.446 4.499,10.367 4.836,10.607C5.174,10.847 5.253,11.315 5.013,11.653C4.972,11.711 4.968,11.741 4.968,11.766C4.968,11.783 4.951,11.929 5.475,12.266C5.986,12.594 6.776,12.956 7.934,13.484L9.409,14.156L10.364,14.584C11.171,14.936 11.581,15.065 12,15.065C12.559,15.065 13.101,14.835 14.591,14.156L16.066,13.484L16.867,13.118C17.601,12.778 18.142,12.512 18.525,12.266C19.049,11.929 19.032,11.783 19.032,11.766C19.032,11.753 19.031,11.74 19.025,11.722L18.987,11.653L18.946,11.588C18.758,11.258 18.847,10.832 19.164,10.607C19.48,10.382 19.911,10.437 20.162,10.723L20.209,10.784L20.282,10.893C20.44,11.152 20.532,11.444 20.532,11.766C20.532,12.598 19.947,13.135 19.336,13.528C18.868,13.829 18.238,14.137 17.483,14.486L16.688,14.849L15.213,15.521C13.847,16.143 12.961,16.565 12,16.565C11.28,16.565 10.601,16.328 9.73,15.947L8.787,15.521L7.312,14.849C6.2,14.342 5.288,13.929 4.664,13.528C4.053,13.135 3.468,12.598 3.468,11.766C3.468,11.398 3.588,11.069 3.791,10.784Z"
|
||||
android:fillColor="#1E1E1E"/>
|
||||
<path
|
||||
android:pathData="M12,2.604C12.964,2.604 13.852,3.032 15.217,3.664L16.691,4.345L17.487,4.714C18.242,5.068 18.872,5.381 19.34,5.686C19.954,6.086 20.532,6.628 20.532,7.461C20.532,8.294 19.954,8.837 19.34,9.237C18.872,9.542 18.242,9.854 17.487,10.208L16.691,10.577L15.217,11.258C13.852,11.889 12.964,12.318 12,12.318C11.278,12.318 10.597,12.077 9.726,11.69L8.783,11.258L7.309,10.577C6.197,10.063 5.284,9.644 4.66,9.237C4.046,8.837 3.468,8.294 3.468,7.461C3.468,6.628 4.046,6.086 4.66,5.686C5.284,5.279 6.197,4.859 7.309,4.345L8.783,3.664L9.726,3.232C10.597,2.845 11.278,2.604 12,2.604ZM12,4.104C11.583,4.104 11.175,4.234 10.368,4.59L9.412,5.025L7.938,5.706C6.78,6.242 5.99,6.609 5.479,6.942C4.958,7.282 4.968,7.433 4.968,7.461C4.968,7.49 4.96,7.642 5.479,7.98C5.99,8.313 6.78,8.68 7.938,9.215L9.412,9.897L10.368,10.332C11.175,10.688 11.583,10.818 12,10.818C12.556,10.818 13.097,10.586 14.588,9.897L16.062,9.215L16.863,8.843C17.598,8.498 18.138,8.23 18.521,7.98C19.04,7.642 19.032,7.49 19.032,7.461C19.032,7.433 19.042,7.282 18.521,6.942C18.138,6.692 17.598,6.423 16.863,6.079L16.062,5.706L14.588,5.025C13.097,4.336 12.556,4.104 12,4.104Z"
|
||||
android:fillColor="#1E1E1E"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
10
core/ui/src/main/res/drawable/ic_tangem_logo.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="51dp"
|
||||
android:height="11dp"
|
||||
android:viewportWidth="74"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M2.924,0H9.413C10.436,0 10.948,0 11.339,0.199C11.683,0.374 11.962,0.654 12.137,0.998C12.337,1.389 12.337,1.9 12.337,2.924V2.924V4.009H0V2.924C0,1.9 0,1.389 0.199,0.998C0.374,0.654 0.654,0.375 0.998,0.199C1.389,0 1.9,0 2.924,0ZM12.337,7.402H8.224L8.225,7.971V15.421H9.413H9.413C10.436,15.421 10.948,15.421 11.339,15.221C11.683,15.047 11.962,14.767 12.137,14.423C12.337,14.032 12.337,13.52 12.337,12.497V7.402ZM4.111,7.402H0V12.497C0,13.52 0,14.032 0.199,14.423C0.375,14.767 0.654,15.047 0.998,15.221C1.389,15.421 1.9,15.421 2.924,15.421H2.924H4.111V7.402ZM22.85,12.479C21.201,12.479 20.346,11.591 20.346,10.022V5.488H19.236V4.046H20.346V2.223H22.264V4.046H24.087V5.488H22.264V9.848C22.264,10.577 22.612,10.91 23.215,10.91C23.595,10.91 23.881,10.862 24.15,10.751V12.273C23.849,12.384 23.437,12.479 22.85,12.479ZM28.298,11.116C29.471,11.116 30.343,10.498 30.343,9.436V8.611H29.36C27.901,8.611 26.966,8.944 26.966,9.959C26.966,10.656 27.347,11.116 28.298,11.116ZM27.838,12.495C26.316,12.495 25.064,11.766 25.064,10.038C25.064,8.104 26.934,7.406 29.312,7.406H30.343V6.978C30.343,5.869 29.962,5.314 28.837,5.314C27.838,5.314 27.347,5.789 27.251,6.614H25.397C25.555,4.68 27.093,3.871 28.948,3.871C30.802,3.871 32.261,4.632 32.261,6.899V12.337H30.374V11.322C29.835,12.035 29.106,12.495 27.838,12.495ZM33.841,4.046V12.337H35.759V7.454C35.759,6.138 36.631,5.472 37.709,5.472C38.85,5.472 39.358,6.043 39.358,7.295V12.337H41.276V7.121C41.276,4.838 40.103,3.871 38.438,3.871C37.075,3.871 36.155,4.553 35.759,5.361V4.046H33.841ZM46.72,10.529C48.051,10.529 49.018,9.578 49.018,7.993V7.882C49.018,6.313 48.162,5.377 46.783,5.377C45.356,5.377 44.532,6.408 44.532,7.914V8.041C44.532,9.578 45.483,10.529 46.72,10.529ZM46.656,15.38C44.056,15.38 42.883,14.207 42.661,12.622H44.595C44.754,13.446 45.388,13.922 46.64,13.922C48.13,13.922 48.955,13.177 48.955,11.687V10.466C48.495,11.259 47.465,12.004 46.244,12.004C44.167,12.004 42.566,10.45 42.566,8.056V7.945C42.566,5.615 44.151,3.871 46.292,3.871C47.655,3.871 48.479,4.49 48.955,5.314V4.046H50.873V11.718C50.857,14.16 49.224,15.38 46.656,15.38ZM52.249,8.278C52.249,10.894 54.009,12.495 56.45,12.495C58.574,12.495 59.985,11.544 60.255,9.8H58.4C58.257,10.609 57.655,11.068 56.498,11.068C55.071,11.068 54.278,10.181 54.215,8.611H60.286V8.056C60.286,5.092 58.431,3.871 56.371,3.871C54.009,3.871 52.249,5.583 52.249,8.151V8.278ZM58.384,7.327H54.246C54.437,6.043 55.213,5.266 56.371,5.266C57.56,5.266 58.289,5.9 58.384,7.327ZM61.651,12.337V4.046H63.569V5.314C63.965,4.537 64.869,3.871 66.089,3.871C67.167,3.871 68.023,4.331 68.436,5.361C69.07,4.331 70.211,3.871 71.241,3.871C72.763,3.871 74,4.807 74,7.089V12.337H72.081V7.216C72.081,5.996 71.558,5.472 70.607,5.472C69.656,5.472 68.784,6.107 68.784,7.375V12.337H66.866V7.216C66.866,5.996 66.327,5.472 65.392,5.472C64.441,5.472 63.569,6.107 63.569,7.375V12.337H61.651Z" />
|
||||
</vector>
|
||||
14
core/ui/src/main/res/drawable/ic_warning_16.xml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:pathData="M8.003,9.316C7.646,9.316 7.447,9.11 7.44,8.746L7.35,5.007C7.344,4.642 7.612,4.381 7.997,4.381C8.368,4.381 8.656,4.649 8.65,5.014L8.546,8.746C8.54,9.117 8.34,9.316 8.003,9.316ZM8.003,11.619C7.591,11.619 7.234,11.289 7.234,10.883C7.234,10.471 7.584,10.141 8.003,10.141C8.416,10.141 8.766,10.464 8.766,10.883C8.766,11.296 8.409,11.619 8.003,11.619Z"
|
||||
android:fillColor="#919191"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M8,1.164C4.225,1.164 1.164,4.225 1.164,8C1.164,11.775 4.225,14.836 8,14.836C11.775,14.836 14.836,11.775 14.836,8C14.836,4.225 11.775,1.164 8,1.164ZM2.164,8C2.164,4.777 4.777,2.164 8,2.164C11.223,2.164 13.836,4.777 13.836,8C13.836,11.223 11.223,13.836 8,13.836C4.777,13.836 2.164,11.223 2.164,8Z"
|
||||
android:fillColor="#919191"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||