Updated on 2026-08-14
This commit is contained in:
commit
d994b7840a
150 changed files with 971 additions and 4621 deletions
|
|
@ -31,18 +31,10 @@
|
|||
"name": "SWAP_REDESIGN_ENABLED",
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "HOT_WALLET_ENABLED",
|
||||
"version": "5.32.0"
|
||||
},
|
||||
{
|
||||
"name": "HOT_WALLET_CREATION_RESTRICTION_ENABLED",
|
||||
"version": "5.32.0"
|
||||
},
|
||||
{
|
||||
"name": "HOT_WALLET_VISIBLE",
|
||||
"version": "5.32.1"
|
||||
},
|
||||
{
|
||||
"name": "TANGEM_PAY_ENABLED",
|
||||
"version": "5.31.0"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.core.ui.components.buttons.actions
|
||||
|
||||
import androidx.compose.ui.semantics.SemanticsPropertyKey
|
||||
import androidx.compose.ui.semantics.SemanticsPropertyReceiver
|
||||
|
||||
val IsDimmedKey = SemanticsPropertyKey<Boolean>("IsDimmed")
|
||||
val HasBadgeKey = SemanticsPropertyKey<Boolean>("HasBadge")
|
||||
|
||||
var SemanticsPropertyReceiver.isDimmed by IsDimmedKey
|
||||
var SemanticsPropertyReceiver.hasBadge by HasBadgeKey
|
||||
|
|
@ -22,7 +22,6 @@ 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.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -136,11 +135,8 @@ fun ActionBaseButton(
|
|||
}
|
||||
.clip(shape)
|
||||
.semantics {
|
||||
contentDescription = if (config.shouldDimContent) {
|
||||
"Action button is dimmed"
|
||||
} else {
|
||||
"Action button is not dimmed"
|
||||
}
|
||||
isDimmed = config.shouldDimContent
|
||||
hasBadge = config.shouldShowBadge
|
||||
}
|
||||
.combinedClickable(
|
||||
enabled = config.isEnabled,
|
||||
|
|
|
|||
|
|
@ -74,13 +74,14 @@ private const val DEFAULT_SCALE = 1f
|
|||
|
||||
private const val SHAKE_AMPLITUDE_DP = 5f
|
||||
private const val SHAKE_DURATION_MS = 120
|
||||
private const val FADE_DURATION_MS = 150
|
||||
private const val FADE_DURATION_MS = 100
|
||||
|
||||
private const val BOUNCE_VELOCITY_MULTIPLIER = 1.25f
|
||||
|
||||
private const val HAPTIC_INITIAL_INTERVAL_MS = 200L
|
||||
private const val HAPTIC_MIN_INTERVAL_MS = 30L
|
||||
private const val HAPTIC_INITIAL_INTERVAL_MS = 50L
|
||||
private const val HAPTIC_MIN_INTERVAL_MS = 15L
|
||||
private const val HAPTIC_HEARTBEAT_INTERVAL_MS = 100L
|
||||
private const val HAPTIC_SUCCESS_DELAY_MS = 300L
|
||||
|
||||
// Easing: smooth acceleration, gradually picking up speed
|
||||
private val AccelerateEasing = CubicBezierEasing(
|
||||
|
|
@ -288,6 +289,7 @@ private fun Modifier.holdToConfirmGestures(
|
|||
state.isProgressVisible = true
|
||||
|
||||
// Soft heartbeat on success
|
||||
delay(HAPTIC_SUCCESS_DELAY_MS)
|
||||
performSuccessHapticFeedback(config.hapticManager)
|
||||
|
||||
state.scaleProgress.animateTo(
|
||||
|
|
@ -324,7 +326,7 @@ private suspend fun performAcceleratingHapticFeedback(hapticManager: HapticManag
|
|||
// Exponential decrease: interval decreases faster as progress increases
|
||||
val interval = (maxInterval * (1f - progress * progress) + minInterval).toLong()
|
||||
|
||||
hapticManager.perform(TangemHapticEffect.View.SegmentTick)
|
||||
hapticManager.perform(TangemHapticEffect.View.ClockTick)
|
||||
delay(interval)
|
||||
}
|
||||
}
|
||||
|
|
@ -333,18 +335,18 @@ private suspend fun performAcceleratingHapticFeedback(hapticManager: HapticManag
|
|||
* Performs strong heartbeat haptic feedback on release (two heavy clicks).
|
||||
*/
|
||||
private suspend fun performReleaseHapticFeedback(hapticManager: HapticManager) {
|
||||
hapticManager.perform(TangemHapticEffect.OneTime.HeavyClick)
|
||||
hapticManager.perform(TangemHapticEffect.View.Reject)
|
||||
delay(HAPTIC_HEARTBEAT_INTERVAL_MS)
|
||||
hapticManager.perform(TangemHapticEffect.OneTime.HeavyClick)
|
||||
hapticManager.perform(TangemHapticEffect.View.Reject)
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs soft heartbeat haptic feedback on success (two light clicks).
|
||||
*/
|
||||
private suspend fun performSuccessHapticFeedback(hapticManager: HapticManager) {
|
||||
hapticManager.perform(TangemHapticEffect.OneTime.Click)
|
||||
hapticManager.perform(TangemHapticEffect.View.Confirm)
|
||||
delay(HAPTIC_HEARTBEAT_INTERVAL_MS)
|
||||
hapticManager.perform(TangemHapticEffect.OneTime.Click)
|
||||
hapticManager.perform(TangemHapticEffect.View.Confirm)
|
||||
}
|
||||
|
||||
private suspend fun CoroutineScope.handleReleaseAnimation(
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ 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.BaseSearchBarTestTags
|
||||
import com.tangem.core.ui.test.SearchBarTestTags
|
||||
|
||||
@Composable
|
||||
fun SearchBar(
|
||||
|
|
@ -126,7 +127,9 @@ private fun DecorationBox(
|
|||
contentPadding = PaddingValues(all = TangemTheme.dimens.spacing12),
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size20),
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size20)
|
||||
.testTag(SearchBarTestTags.ICON),
|
||||
painter = painterResource(id = R.drawable.ic_search_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
|
|
@ -137,6 +140,7 @@ private fun DecorationBox(
|
|||
state = state,
|
||||
focusManager = focusManager,
|
||||
keyboardController = keyboardController,
|
||||
modifier = Modifier.testTag(SearchBarTestTags.CLEAR_BUTTON),
|
||||
)
|
||||
},
|
||||
placeholder = {
|
||||
|
|
@ -146,6 +150,7 @@ private fun DecorationBox(
|
|||
style = TangemTheme.typography.body2,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.testTag(SearchBarTestTags.PLACEHOLDER_TEXT),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object MarketTokenDetailsBottomSheetTestTags {
|
||||
const val PORTFOLIO_QUICK_ACTION_BUTTON = "MARKET_TOKEN_DETAILS_BOTTOM_SHEET_PORTFOLIO_QUICK_ACTION_BUTTON"
|
||||
const val PORTFOLIO_QUICK_ACTION_BUTTON_TITLE = "MARKET_TOKEN_DETAILS_BOTTOM_SHEET_PORTFOLIO_QUICK_ACTION_BUTTON_TITLE"
|
||||
const val PORTFOLIO_QUICK_ACTION_BUTTON_ICON = "MARKET_TOKEN_DETAILS_BOTTOM_SHEET_PORTFOLIO_QUICK_ACTION_BUTTON_ICON"
|
||||
const val PORTFOLIO_TOKEN_ITEM = "MARKET_TOKEN_DETAILS_BOTTOM_SHEET_PORTFOLIO_TOKEN_ITEM"
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object SearchBarTestTags {
|
||||
const val ICON = "SEARCH_BAR_ICON"
|
||||
const val CLEAR_BUTTON = "SEARCH_BAR_CLEAR_BUTTON"
|
||||
const val PLACEHOLDER_TEXT = "SEARCH_BAR_PLACEHOLDER_TEXT"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object SwapSelectTokenScreenTestTags {
|
||||
const val YOU_SWAP_BLOCK = "SWAP_SELECT_TOKEN_SCREEN_YOU_SWAP_BLOCK"
|
||||
const val CHOOSE_TOKEN_TEXT = "SWAP_SELECT_TOKEN_SCREEN_CHOOSE_TOKEN_TEXT"
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object SwapTokenScreenTestTags {
|
||||
const val CONTAINER = "SWAP_TOKEN_SCREEN_CONTAINER"
|
||||
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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue