Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-05 12:40:18 +02:00
parent 3ae32901d4
commit 8d02828096
3 changed files with 140 additions and 84 deletions

View file

@ -21,10 +21,15 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.geometry.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Outline
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
@ -64,8 +69,6 @@ import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.MainScreenTestTags
import com.tangem.core.ui.test.MarketTooltipTestTags
import com.tangem.core.ui.utils.TangemSharedTransitionLayout
import com.tangem.core.ui.utils.lineTo
import com.tangem.core.ui.utils.moveTo
import com.tangem.core.ui.utils.toPx
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewDataLegacy.accountScreenState
@ -396,17 +399,10 @@ private inline fun BaseScaffoldWithMarkets(
}
BottomSheetScrim(
color = if (state.showMarketsOnboarding) {
Color.Black.copy(alpha = .65f)
} else {
Color.Black.copy(alpha = .40f)
},
visible = bottomSheetState.targetValue == TangemSheetValue.Expanded ||
state.showMarketsOnboarding,
color = Color.Black.copy(alpha = .40f),
visible = bottomSheetState.targetValue == TangemSheetValue.Expanded,
onDismissRequest = {
if (!state.showMarketsOnboarding) {
coroutineScope.launch { bottomSheetState.partialExpand() }
}
coroutineScope.launch { bottomSheetState.partialExpand() }
},
)
@ -414,7 +410,7 @@ private inline fun BaseScaffoldWithMarkets(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 8.dp)
.padding(horizontal = 16.dp)
.padding(horizontal = 12.dp)
.fillMaxWidth(),
isVisible = state.showMarketsOnboarding,
availableHeight = maxHeight,
@ -527,33 +523,22 @@ private fun MarketsHint(isVisible: Boolean, modifier: Modifier = Modifier) {
@Composable
private fun MarketsTooltipContent(onCloseClick: () -> Unit, modifier: Modifier = Modifier) {
val backgroundColor = TangemTheme.colors.background.primary
val cornerRadius = CornerRadius(x = 16.dp.toPx())
val backgroundColor = TangemTheme.colors.background.action
val tipDpSize = DpSize(width = 20.dp, height = 8.dp)
val tooltipShape = remember(tipDpSize) { TooltipShape(cornerRadius = 16.dp, tipSize = tipDpSize) }
Row(
modifier = modifier
.padding(bottom = tipDpSize.height)
.drawBehind {
val rect = size.toRect()
val tipSize = tipDpSize.toSize()
val tipRect = Rect(
offset = Offset(
x = rect.center.x - tipSize.center.x,
y = rect.bottom,
),
size = tipSize,
)
drawRoundRect(color = backgroundColor, cornerRadius = cornerRadius)
val tipPath = Path().apply {
moveTo(tipRect.topLeft)
lineTo(tipRect.bottomCenter)
lineTo(tipRect.topRight)
}
drawPath(color = backgroundColor, path = tipPath)
}
.padding(all = 12.dp),
.shadow(
elevation = TangemTheme.dimens.elevation12,
shape = tooltipShape,
clip = false,
ambientColor = Color.Black.copy(alpha = 0.7f),
)
.background(backgroundColor, tooltipShape)
.clickable(interactionSource = null, indication = null, onClick = {})
.padding(all = 12.dp)
.padding(bottom = tipDpSize.height),
horizontalArrangement = Arrangement.spacedBy(space = 12.dp),
verticalAlignment = Alignment.Top,
) {
@ -594,6 +579,32 @@ private fun MarketsTooltipContent(onCloseClick: () -> Unit, modifier: Modifier =
}
}
private class TooltipShape(
private val cornerRadius: Dp,
private val tipSize: DpSize,
) : Shape {
override fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density): Outline {
val cornerRadiusPx = with(density) { cornerRadius.toPx() }
val tipWidth = with(density) { tipSize.width.toPx() }
val tipHeight = with(density) { tipSize.height.toPx() }
val bodyHeight = size.height - tipHeight
val path = Path().apply {
addRoundRect(
RoundRect(
rect = Rect(left = 0f, top = 0f, right = size.width, bottom = bodyHeight),
cornerRadius = CornerRadius(cornerRadiusPx),
),
)
moveTo(size.width / 2 - tipWidth / 2, bodyHeight)
lineTo(size.width / 2, size.height)
lineTo(size.width / 2 + tipWidth / 2, bodyHeight)
close()
}
return Outline.Generic(path)
}
}
@Composable
private fun BottomSheetScrim(color: Color, visible: Boolean, onDismissRequest: () -> Unit) {
val alpha by animateFloatAsState(

View file

@ -262,10 +262,12 @@ private fun WalletContent2(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 24.dp)
.fillMaxWidth(fraction = 0.7f),
.padding(horizontal = 12.dp)
.fillMaxWidth(),
isVisible = state.showMarketsOnboarding,
availableHeight = LocalWindowSize.current.height,
bottomSheetState = bottomSheetState,
onCloseClick = state.onDismissMarketsTooltip,
)
}
}
@ -370,16 +372,10 @@ private inline fun BaseScaffoldWithMarkets(
appBarContent()
BottomSheetScrim(
color = if (state.showMarketsOnboarding) {
Color.Black.copy(alpha = .65f)
} else {
Color.Black.copy(alpha = .40f)
},
visible = bottomSheetState.targetValue == TangemSheetValue.Expanded ||
state.showMarketsOnboarding,
color = Color.Black.copy(alpha = .40f),
visible = bottomSheetState.targetValue == TangemSheetValue.Expanded,
onDismissRequest = {
coroutineScope.launch { bottomSheetState.partialExpand() }
state.onDismissMarketsTooltip()
},
)
},

View file

@ -8,31 +8,40 @@ import androidx.compose.animation.core.spring
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideIn
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.geometry.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Outline
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.sheetscaffold.TangemSheetState
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.core.ui.test.MarketTooltipTestTags
import com.tangem.core.ui.utils.lineTo
import com.tangem.core.ui.utils.moveTo
import com.tangem.core.ui.utils.toPx
import com.tangem.feature.wallet.impl.R
import kotlinx.coroutines.delay
@ -43,6 +52,7 @@ internal fun MarketsTooltip(
availableHeight: Dp,
bottomSheetState: TangemSheetState,
isVisible: Boolean,
onCloseClick: () -> Unit,
modifier: Modifier = Modifier,
) {
val density = LocalDensity.current
@ -83,62 +93,101 @@ internal fun MarketsTooltip(
) + fadeIn(),
exit = fadeOut(),
) {
MarketsTooltipContent()
MarketsTooltipContent(onCloseClick = onCloseClick)
}
}
@Composable
internal fun MarketsTooltipContent(modifier: Modifier = Modifier) {
internal fun MarketsTooltipContent(onCloseClick: () -> Unit, modifier: Modifier = Modifier) {
val backgroundColor = TangemTheme.colors.background.action
val cornerRadius = CornerRadius(x = 14.dp.toPx())
val tipDpSize = DpSize(width = 20.dp, height = 8.dp)
val tooltipShape = remember(tipDpSize) { TooltipShape(cornerRadius = 16.dp, tipSize = tipDpSize) }
Column(
Row(
modifier = modifier
.padding(bottom = tipDpSize.height)
.drawBehind {
val rect = size.toRect()
val tipSize = tipDpSize.toSize()
val tipRect = Rect(
offset = Offset(
x = rect.center.x - tipSize.center.x,
y = rect.bottom,
),
size = tipSize,
)
drawRoundRect(color = backgroundColor, cornerRadius = cornerRadius)
val tipPath = Path().apply {
moveTo(tipRect.topLeft)
lineTo(tipRect.bottomCenter)
lineTo(tipRect.topRight)
}
drawPath(color = backgroundColor, path = tipPath)
}
.padding(all = 12.dp),
verticalArrangement = Arrangement.spacedBy(space = 4.dp),
horizontalAlignment = Alignment.Start,
.shadow(
elevation = TangemTheme.dimens.elevation12,
shape = tooltipShape,
clip = false,
ambientColor = Color.Black.copy(alpha = 0.7f),
)
.background(backgroundColor, tooltipShape)
.clickable(interactionSource = null, indication = null, onClick = {})
.padding(all = 12.dp)
.padding(bottom = tipDpSize.height),
horizontalArrangement = Arrangement.spacedBy(space = 12.dp),
verticalAlignment = Alignment.Top,
) {
Text(
text = stringResourceSafe(id = R.string.markets_tooltip_title),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.primary1,
Icon(
modifier = Modifier.size(size = 18.dp),
painter = painterResource(id = R.drawable.ic_plus_18),
tint = Color.Unspecified,
contentDescription = null,
)
Text(
text = stringResourceSafe(id = R.string.markets_tooltip_message),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.secondary,
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(space = 2.dp),
) {
Text(
text = stringResourceSafe(id = R.string.markets_tooltip_v2_title),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.primary1,
)
Text(
text = stringResourceSafe(id = R.string.markets_tooltip_message),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.secondary,
)
}
Icon(
modifier = Modifier
.size(size = 16.dp)
.clickable(
interactionSource = null,
indication = null,
onClick = onCloseClick,
)
.testTag(MarketTooltipTestTags.CLOSE_BUTTON),
painter = painterResource(id = R.drawable.ic_close_24),
tint = TangemTheme.colors.icon.informative,
contentDescription = null,
)
}
}
private class TooltipShape(
private val cornerRadius: Dp,
private val tipSize: DpSize,
) : Shape {
override fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density): Outline {
val cornerRadiusPx = with(density) { cornerRadius.toPx() }
val tipWidth = with(density) { tipSize.width.toPx() }
val tipHeight = with(density) { tipSize.height.toPx() }
val bodyHeight = size.height - tipHeight
val path = Path().apply {
addRoundRect(
RoundRect(
rect = Rect(left = 0f, top = 0f, right = size.width, bottom = bodyHeight),
cornerRadius = CornerRadius(cornerRadiusPx),
),
)
moveTo(size.width / 2 - tipWidth / 2, bodyHeight)
lineTo(size.width / 2, size.height)
lineTo(size.width / 2 + tipWidth / 2, bodyHeight)
close()
}
return Outline.Generic(path)
}
}
// region Preview
@Composable
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun MarketsTooltip_Preview() {
TangemThemePreviewRedesign {
MarketsTooltipContent()
MarketsTooltipContent(onCloseClick = {})
}
}
// endregion