Updated on 2026-08-14
27
core/ui/src/main/res/drawable/ic_replace_20.xml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<!--
|
||||
~ Copyright (C) 2026 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="20">
|
||||
<path
|
||||
android:pathData="M15.75,9.25C16.164,9.25 16.5,9.586 16.5,10C16.5,13.59 13.59,16.5 10,16.5C7.986,16.5 6.19,15.581 5,14.144V14.791C5,15.205 4.664,15.541 4.25,15.541C3.836,15.541 3.5,15.205 3.5,14.791V12.396C3.5,11.981 3.836,11.646 4.25,11.646H6.646C7.06,11.646 7.395,11.981 7.396,12.396C7.396,12.81 7.06,13.146 6.646,13.146H6.123C7.04,14.276 8.434,15 10,15C12.762,15 15,12.762 15,10C15,9.586 15.336,9.25 15.75,9.25Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M10,3.5C12.014,3.5 13.81,4.419 15,5.855V5.208C15,4.794 15.336,4.458 15.75,4.458C16.164,4.458 16.5,4.794 16.5,5.208V7.604C16.5,8.018 16.164,8.354 15.75,8.354H13.354C12.94,8.353 12.604,8.018 12.604,7.604C12.605,7.19 12.941,6.854 13.354,6.854H13.876C12.96,5.723 11.565,5 10,5C7.238,5 5,7.238 5,10C5,10.414 4.664,10.75 4.25,10.75C3.836,10.75 3.5,10.414 3.5,10C3.5,6.41 6.41,3.5 10,3.5Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
||||
29
core/ui/src/main/res/drawable/ic_visa_logo.xml
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<!--
|
||||
~ Copyright (C) 2026 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="8dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="8">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M0,0h22v8h-22z"/>
|
||||
<path
|
||||
android:pathData="M5.503,7.217H3.709L2.364,2.086C2.3,1.849 2.164,1.641 1.965,1.542C1.467,1.295 0.919,1.099 0.321,0.999V0.802H3.21C3.609,0.802 3.908,1.099 3.958,1.443L4.656,5.145L6.449,0.802H8.193L5.503,7.217ZM9.189,7.217H7.495L8.89,0.802H10.585L9.189,7.217ZM12.777,2.578C12.827,2.233 13.126,2.035 13.475,2.035C14.023,1.986 14.62,2.085 15.119,2.331L15.418,0.95C14.92,0.753 14.371,0.653 13.873,0.653C12.229,0.653 11.033,1.542 11.033,2.775C11.033,3.713 11.88,4.206 12.479,4.503C13.126,4.798 13.375,4.996 13.325,5.292C13.325,5.736 12.827,5.933 12.329,5.933C11.731,5.933 11.132,5.785 10.585,5.538L10.286,6.92C10.884,7.167 11.531,7.266 12.13,7.266C13.973,7.314 15.119,6.427 15.119,5.094C15.119,3.416 12.777,3.318 12.777,2.578ZM21.048,7.217L19.703,0.802H18.258C17.959,0.802 17.66,0.999 17.56,1.295L15.069,7.217H16.813L17.161,6.279H19.304L19.503,7.217H21.048ZM18.507,2.529L19.004,4.946H17.609L18.507,2.529Z"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillType="evenOdd"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
|
@ -16,11 +16,13 @@ import com.tangem.core.decompose.model.getOrCreateModel
|
|||
import com.tangem.core.ui.components.NavigationBar3ButtonsScrim
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.features.tangempay.components.txHistory.DefaultTangemPayTxHistoryComponent
|
||||
import com.tangem.features.tangempay.components.txHistory.TangemPayTxHistoryDetailsComponent
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsNavigation
|
||||
import com.tangem.features.tangempay.model.TangemPayDetailsModel
|
||||
import com.tangem.features.tangempay.ui.TangemPayDetailsScreen
|
||||
import com.tangem.features.tangempay.ui.TangemPayDetailsScreenV2
|
||||
import com.tangem.features.tangempay.utils.requireLoaded
|
||||
import com.tangem.features.tangempay.utils.userWalletId
|
||||
import com.tangem.features.tokendetails.ExpressTransactionsComponent
|
||||
|
|
@ -72,12 +74,21 @@ internal class TangemPayDetailsComponent(
|
|||
val bottomSheet by bottomSheetSlot.subscribeAsState()
|
||||
|
||||
NavigationBar3ButtonsScrim()
|
||||
TangemPayDetailsScreen(
|
||||
state = state,
|
||||
txHistoryComponent = txHistoryComponent,
|
||||
expressTransactionsComponent = expressTransactionsComponent,
|
||||
modifier = modifier,
|
||||
)
|
||||
if (LocalRedesignEnabled.current) {
|
||||
TangemPayDetailsScreenV2(
|
||||
state = state,
|
||||
txHistoryComponent = txHistoryComponent,
|
||||
expressTransactionsComponent = expressTransactionsComponent,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
TangemPayDetailsScreen(
|
||||
state = state,
|
||||
txHistoryComponent = txHistoryComponent,
|
||||
expressTransactionsComponent = expressTransactionsComponent,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
bottomSheet.child?.instance?.BottomSheet()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ internal data class TangemPayCardPageUM(
|
|||
val dailyLimitState: TangemPayDailyLimitBlockState,
|
||||
val addToWalletBlockState: AddToWalletBlockState? = null,
|
||||
val isReissueInProgress: Boolean = false,
|
||||
val menuItems: ImmutableList<TangemPayDropDownItemUM>,
|
||||
) {
|
||||
companion object {
|
||||
fun stub(
|
||||
|
|
@ -40,6 +41,7 @@ internal data class TangemPayCardPageUM(
|
|||
onBackClick = {},
|
||||
isReissueInProgress = isReissueInProgress,
|
||||
dailyLimitState = dailyLimitState,
|
||||
menuItems = persistentListOf(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,12 @@ import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
|||
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
|
||||
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.themedColor
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.generated.icons.Icons
|
||||
import com.tangem.core.ui.res.generated.icons.ic_document_20
|
||||
import com.tangem.domain.models.pay.TangemPayCardFrozenState
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.tangempay.utils.TangemPayDetailIntents
|
||||
|
|
@ -31,6 +34,7 @@ internal class TangemPayDetailsStateFactory(
|
|||
onBackClick = onBack,
|
||||
onOpenMenu = onOpenMenu,
|
||||
items = getTopBarMenuItems(isTangemPayDeactivated),
|
||||
itemsV2 = getTopBarMenuItemsV2(isTangemPayDeactivated),
|
||||
),
|
||||
pullToRefreshConfig = PullToRefreshConfig(
|
||||
isRefreshing = false,
|
||||
|
|
@ -89,4 +93,31 @@ internal class TangemPayDetailsStateFactory(
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getTopBarMenuItemsV2(isTangemPayDeactivated: Boolean): ImmutableList<TangemPayDropDownItemUM> {
|
||||
if (isTangemPayDeactivated) return persistentListOf()
|
||||
|
||||
return persistentListOf(
|
||||
TangemPayDropDownItemUM(
|
||||
title = resourceReference(R.string.tangem_pay_terms_limits),
|
||||
onClick = intents::onClickTermsAndLimits,
|
||||
icon = TangemIconUM.Icon(
|
||||
imageVector = Icons.ic_document_20,
|
||||
tintReference = {
|
||||
TangemTheme.colors3.icon.primary
|
||||
},
|
||||
),
|
||||
),
|
||||
TangemPayDropDownItemUM(
|
||||
title = resourceReference(R.string.tangempay_pay_support),
|
||||
onClick = intents::onContactSupportClicked,
|
||||
icon = TangemIconUM.Icon(
|
||||
imageVector = Icons.ic_document_20,
|
||||
tintReference = {
|
||||
TangemTheme.colors3.icon.primary
|
||||
},
|
||||
), // TODO change when the icon will be ready in design
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -7,4 +7,5 @@ internal data class TangemPayDetailsTopBarConfig(
|
|||
val onBackClick: () -> Unit,
|
||||
val onOpenMenu: () -> Unit,
|
||||
val items: ImmutableList<TangemDropdownMenuItem>,
|
||||
val itemsV2: ImmutableList<TangemPayDropDownItemUM>,
|
||||
)
|
||||
|
|
@ -77,7 +77,7 @@ internal sealed class TangemPayDetailsBalanceBlockState {
|
|||
data class Content(
|
||||
override val actionButtons: ImmutableList<ActionButtonConfig>,
|
||||
override val cardsBlockState: CardsBlockState?,
|
||||
val fiatBalance: String,
|
||||
val fiatBalance: TextReference,
|
||||
val isBalanceFlickering: Boolean,
|
||||
) : TangemPayDetailsBalanceBlockState()
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.features.tangempay.entity
|
||||
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
internal data class TangemPayDropDownItemUM(
|
||||
val onClick: () -> Unit,
|
||||
val title: TextReference,
|
||||
val icon: TangemIconUM,
|
||||
)
|
||||
|
|
@ -13,6 +13,7 @@ import com.tangem.core.decompose.model.ParamsContainer
|
|||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
|
|
@ -20,6 +21,7 @@ import com.tangem.core.ui.format.bigdecimal.format
|
|||
import com.tangem.core.ui.format.bigdecimal.getJavaCurrencyByCode
|
||||
import com.tangem.core.ui.format.bigdecimal.optionalDecimals
|
||||
import com.tangem.core.ui.message.SnackbarMessage
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.test.TangemPayTestTags
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
|
|
@ -53,7 +55,7 @@ import kotlinx.coroutines.launch
|
|||
import javax.inject.Inject
|
||||
import com.tangem.core.ui.R as CoreUiR
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
@Stable
|
||||
@ModelScoped
|
||||
internal class TangemPayCardPageModel @Inject constructor(
|
||||
|
|
@ -89,6 +91,7 @@ internal class TangemPayCardPageModel @Inject constructor(
|
|||
dailyLimitState = TangemPayDailyLimitBlockState.Loading,
|
||||
settings = persistentListOf(),
|
||||
settingsV2 = persistentListOf(),
|
||||
menuItems = buildMenuItems(),
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -210,6 +213,21 @@ internal class TangemPayCardPageModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun buildMenuItems(): ImmutableList<TangemPayDropDownItemUM> {
|
||||
return persistentListOf(
|
||||
TangemPayDropDownItemUM(
|
||||
title = TextReference.Res(R.string.tangempay_card_details_reissue_card),
|
||||
onClick = ::onClickReissueCard,
|
||||
icon = TangemIconUM.Icon(
|
||||
iconRes = CoreUiR.drawable.ic_replace_20,
|
||||
tintReference = {
|
||||
TangemTheme.colors3.icon.primary
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun onClickViewDetails() {
|
||||
modelScope.launch(dispatchers.default) {
|
||||
cardDetailsEventListener.send(CardDetailsEvent.Show)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@ package com.tangem.features.tangempay.model.transformers
|
|||
|
||||
import arrow.core.Either
|
||||
import com.tangem.core.error.UniversalError
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.format.bigdecimal.formatStyled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.pay.model.TangemPayCardBalance
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsBalanceBlockState
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsUM
|
||||
|
|
@ -35,10 +37,14 @@ internal class DetailsBalanceTransformer(
|
|||
return prevState.copy(balanceBlockState = balance)
|
||||
}
|
||||
|
||||
private fun getFiatBalanceText(balance: TangemPayCardBalance): String {
|
||||
private fun getFiatBalanceText(balance: TangemPayCardBalance): TextReference {
|
||||
val currency = Currency.getInstance(balance.currencyCode)
|
||||
return balance.fiatBalance.format {
|
||||
fiat(fiatCurrencyCode = currency.currencyCode, fiatCurrencySymbol = currency.symbol)
|
||||
return balance.fiatBalance.formatStyled {
|
||||
fiat(
|
||||
fiatCurrencyCode = currency.currencyCode,
|
||||
fiatCurrencySymbol = currency.symbol,
|
||||
spanStyleReference = { TangemTheme.typography3.heading.medium.toSpanStyle() },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,9 +15,8 @@ import androidx.compose.foundation.lazy.LazyListScope
|
|||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.ScaffoldDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
|
|
@ -25,6 +24,9 @@ import androidx.compose.ui.platform.testTag
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.ds2.button.TangemButton
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
|
|
@ -36,7 +38,9 @@ import com.tangem.features.tangempay.components.cardDetails.PreviewTangemPayCard
|
|||
import com.tangem.features.tangempay.components.cardDetails.TangemPayCardDetailsBlockComponent
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.tangempay.entity.*
|
||||
import com.tangem.features.tangempay.ui.components.PayContextMenuBlock
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import com.tangem.core.ui.R as CoreUiR
|
||||
|
||||
private const val CONTENT_FADE_DURATION_MS = 300
|
||||
|
||||
|
|
@ -51,8 +55,8 @@ internal fun TangemPayCardPageScreen(
|
|||
Scaffold(
|
||||
modifier = modifier,
|
||||
topBar = {
|
||||
AppBarWithBackButton(
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
CardPageTopBar(
|
||||
items = state.menuItems,
|
||||
onBackClick = state.onBackClick,
|
||||
)
|
||||
},
|
||||
|
|
@ -170,6 +174,48 @@ private fun TangemPayCardPageSettingRow(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CardPageTopBar(
|
||||
onBackClick: () -> Unit,
|
||||
items: ImmutableList<TangemPayDropDownItemUM>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
var isDropdownMenuShown by rememberSaveable { mutableStateOf(false) }
|
||||
TangemTopBar(
|
||||
modifier = modifier.statusBarsPadding(),
|
||||
startContent = {
|
||||
TangemButton(
|
||||
iconStart = TangemIconUM.Icon(iconRes = R.drawable.ic_arrow_back_28),
|
||||
onClick = onBackClick,
|
||||
size = TangemButton.Size.X11,
|
||||
variant = TangemButton.Variant.Material,
|
||||
)
|
||||
},
|
||||
endContent = {
|
||||
Box {
|
||||
TangemButton(
|
||||
iconStart = TangemIconUM.Icon(iconRes = CoreUiR.drawable.ic_more_default_24),
|
||||
onClick = { isDropdownMenuShown = true },
|
||||
size = TangemButton.Size.X11,
|
||||
variant = TangemButton.Variant.Material,
|
||||
)
|
||||
PayContextMenuBlock(
|
||||
items = items,
|
||||
onMenuDismiss = { isDropdownMenuShown = false },
|
||||
isDropdownMenuShown = isDropdownMenuShown,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
AppBarWithBackButton(
|
||||
modifier = modifier,
|
||||
onBackClick = onBackClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.cardPageItem(
|
||||
key: Any? = null,
|
||||
contentType: Any? = null,
|
||||
|
|
|
|||
|
|
@ -2,20 +2,20 @@ package com.tangem.features.tangempay.ui
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.ds2.button.TangemButton
|
||||
import com.tangem.core.ui.extensions.resolveAnnotatedReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.features.tangempay.entity.TangemPayCardPageSettingV2
|
||||
import com.tangem.features.tangempay.ui.components.TangemPayActionButton
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
|
|
@ -30,41 +30,18 @@ internal fun TangemPayCardPageSettingsButtonsBlock(
|
|||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
settings.fastForEach { setting ->
|
||||
TangemPaySettingButton(
|
||||
setting = setting,
|
||||
TangemPayActionButton(
|
||||
modifier = Modifier.then(if (setting.testTag != null) Modifier.testTag(setting.testTag) else Modifier),
|
||||
title = setting.title,
|
||||
iconRes = setting.iconRes,
|
||||
onClick = setting.onClick,
|
||||
isEnabled = setting.isEnabled,
|
||||
isLoading = setting.isLoading,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TangemPaySettingButton(setting: TangemPayCardPageSettingV2, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier.padding(horizontal = TangemTheme.dimens2.x6),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
TangemButton(
|
||||
variant = TangemButton.Variant.Material,
|
||||
size = TangemButton.Size.X14,
|
||||
onClick = setting.onClick,
|
||||
iconStart = TangemIconUM.Icon(
|
||||
iconRes = setting.iconRes,
|
||||
tintReference = { TangemTheme.colors3.icon.primary },
|
||||
),
|
||||
isLoading = setting.isLoading,
|
||||
isEnabled = setting.isEnabled,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = setting.title.resolveAnnotatedReference(),
|
||||
style = TangemTheme.typography3.subheading.medium,
|
||||
color = TangemTheme.colors3.text.primary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import androidx.compose.ui.layout.ContentScale
|
|||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.tooling.preview.Devices
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
|
|
@ -45,9 +46,7 @@ import com.tangem.core.ui.components.dropdownmenu.TangemDropdownItem
|
|||
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenu
|
||||
import com.tangem.core.ui.components.notifications.Notification
|
||||
import com.tangem.core.ui.components.text.applyBladeBrush
|
||||
import com.tangem.core.ui.extensions.orMaskWithStars
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.TangemPayTestTags
|
||||
|
|
@ -343,7 +342,7 @@ private fun FiatBalance(
|
|||
)
|
||||
is TangemPayDetailsBalanceBlockState.Content -> Text(
|
||||
modifier = modifier.testTag(TangemPayTestTags.PAYMENT_ACCOUNT_BALANCE),
|
||||
text = state.fiatBalance.orMaskWithStars(isBalanceHidden),
|
||||
text = state.fiatBalance.orMaskWithStars(isBalanceHidden).resolveReference(),
|
||||
style = TangemTheme.typography.h2.applyBladeBrush(
|
||||
isEnabled = state.isBalanceFlickering,
|
||||
textColor = TangemTheme.colors.text.primary1,
|
||||
|
|
@ -432,10 +431,15 @@ private fun TangemPayDetailsScreenPreview(
|
|||
}
|
||||
}
|
||||
|
||||
private class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider<TangemPayDetailsUM>(
|
||||
internal class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider<TangemPayDetailsUM>(
|
||||
collection = listOf(
|
||||
TangemPayDetailsUM(
|
||||
topBarConfig = TangemPayDetailsTopBarConfig(onBackClick = {}, onOpenMenu = {}, items = persistentListOf()),
|
||||
topBarConfig = TangemPayDetailsTopBarConfig(
|
||||
onBackClick = {},
|
||||
onOpenMenu = {},
|
||||
items = persistentListOf(),
|
||||
itemsV2 = persistentListOf(),
|
||||
),
|
||||
pullToRefreshConfig = PullToRefreshConfig(isRefreshing = false, onRefresh = {}),
|
||||
balanceBlockState = TangemPayDetailsBalanceBlockState.Content(
|
||||
actionButtons = persistentListOf(
|
||||
|
|
@ -445,7 +449,14 @@ private class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider<Ta
|
|||
onClick = {},
|
||||
),
|
||||
),
|
||||
fiatBalance = "$1234.56",
|
||||
fiatBalance = combinedReference(
|
||||
stringReference("1,234"),
|
||||
styledStringReference(
|
||||
".56",
|
||||
{ SpanStyle(color = TangemTheme.colors3.text.primary) },
|
||||
),
|
||||
stringReference(" $"),
|
||||
),
|
||||
isBalanceFlickering = false,
|
||||
cardsBlockState = TangemPayDetailsBalanceBlockState.CardsBlockState(
|
||||
cards = persistentListOf(
|
||||
|
|
@ -465,7 +476,12 @@ private class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider<Ta
|
|||
accountDeactivatedNotificationConfig = null,
|
||||
),
|
||||
TangemPayDetailsUM(
|
||||
topBarConfig = TangemPayDetailsTopBarConfig(onBackClick = {}, onOpenMenu = {}, items = persistentListOf()),
|
||||
topBarConfig = TangemPayDetailsTopBarConfig(
|
||||
onBackClick = {},
|
||||
onOpenMenu = {},
|
||||
items = persistentListOf(),
|
||||
itemsV2 = persistentListOf(),
|
||||
),
|
||||
pullToRefreshConfig = PullToRefreshConfig(isRefreshing = false, onRefresh = {}),
|
||||
balanceBlockState = TangemPayDetailsBalanceBlockState.Loading(
|
||||
actionButtons = persistentListOf(),
|
||||
|
|
@ -502,7 +518,7 @@ private fun TangemPayDetailsTxHistoryScreenPreview(
|
|||
}
|
||||
}
|
||||
|
||||
private class TangemPayDetailsTxHistoryProvider : CollectionPreviewParameterProvider<TangemPayTxHistoryUM>(
|
||||
internal class TangemPayDetailsTxHistoryProvider : CollectionPreviewParameterProvider<TangemPayTxHistoryUM>(
|
||||
collection = listOf(
|
||||
PreviewTangemPayTxHistoryComponent.loadingUM,
|
||||
PreviewTangemPayTxHistoryComponent.contentUM,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,404 @@
|
|||
package com.tangem.features.tangempay.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.*
|
||||
import androidx.compose.foundation.text.TextAutoSize
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
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.Devices
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastAny
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.ui.components.SpacerW
|
||||
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
||||
import com.tangem.core.ui.components.containers.pullToRefresh.TangemPullToRefreshSlidingContainer
|
||||
import com.tangem.core.ui.components.text.applyBladeBrush
|
||||
import com.tangem.core.ui.components.topFade
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.ds.message.TangemMessage
|
||||
import com.tangem.core.ui.ds.message.TangemMessageEffect
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.ds2.button.TangemButton
|
||||
import com.tangem.core.ui.ds2.shimmers.TextShimmer
|
||||
import com.tangem.core.ui.ds2.shimmers.TextShimmerStyle
|
||||
import com.tangem.core.ui.extensions.clickableSingle
|
||||
import com.tangem.core.ui.extensions.orMaskWithStars
|
||||
import com.tangem.core.ui.extensions.resolveAnnotatedReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.core.ui.test.TangemPayTestTags
|
||||
import com.tangem.features.tangempay.components.express.PreviewEmptyExpressTransactionsComponent
|
||||
import com.tangem.features.tangempay.components.txHistory.PreviewTangemPayTxHistoryComponent
|
||||
import com.tangem.features.tangempay.components.txHistory.TangemPayTxHistoryComponent
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsBalanceBlockState
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsTopBarConfig
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsUM
|
||||
import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM
|
||||
import com.tangem.features.tangempay.ui.components.PayContextMenuBlock
|
||||
import com.tangem.features.tangempay.ui.components.TangemPayActionButton
|
||||
import com.tangem.features.tangempay.ui.components.TangemPayAddCardView
|
||||
import com.tangem.features.tangempay.ui.components.TangemPayCardView
|
||||
import com.tangem.features.tokendetails.ExpressTransactionsComponent
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import com.tangem.core.ui.R as CoreUiR
|
||||
|
||||
private val InitialTopBarHeight: Dp = 64.dp
|
||||
private val BgImageFadeDistance: Dp = 96.dp
|
||||
private const val TOP_FADE_MID_STOP = 0.8f
|
||||
private const val TOP_FADE_MID_ALPHA = 0.8f
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
internal fun TangemPayDetailsScreenV2(
|
||||
state: TangemPayDetailsUM,
|
||||
txHistoryComponent: TangemPayTxHistoryComponent,
|
||||
expressTransactionsComponent: ExpressTransactionsComponent,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val listState = rememberLazyListState()
|
||||
val density = LocalDensity.current
|
||||
val statusBarHeight = with(density) { WindowInsets.systemBars.getTop(this).toDp() }
|
||||
val bottomBarHeight = with(density) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
var topBarTotalHeight by remember { mutableStateOf(InitialTopBarHeight + statusBarHeight) }
|
||||
val rootBackground = TangemTheme.colors3.bg.primary
|
||||
val fadeDistancePx = with(LocalDensity.current) { BgImageFadeDistance.toPx() }
|
||||
val bgImageAlpha by remember(fadeDistancePx) {
|
||||
derivedStateOf {
|
||||
val scrollOffsetPx = when (listState.firstVisibleItemIndex) {
|
||||
0 -> listState.firstVisibleItemScrollOffset.toFloat()
|
||||
else -> fadeDistancePx
|
||||
}.coerceAtLeast(0f)
|
||||
(1f - scrollOffsetPx / fadeDistancePx).coerceIn(0f, 1f)
|
||||
}
|
||||
}
|
||||
|
||||
val txHistoryState by txHistoryComponent.state.collectAsStateWithLifecycle()
|
||||
val expressState by expressTransactionsComponent.state.collectAsStateWithLifecycle()
|
||||
val expressTransactionsBottomSheetState = expressState.bottomSheetSlot
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.background(rootBackground),
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
painter = painterResource(R.drawable.img_bg_pay_details),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
alpha = bgImageAlpha,
|
||||
)
|
||||
|
||||
TangemPullToRefreshSlidingContainer(
|
||||
config = state.pullToRefreshConfig,
|
||||
indicatorOffset = topBarTotalHeight,
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.topFade(
|
||||
height = topBarTotalHeight,
|
||||
0f to rootBackground.copy(alpha = 1f - bgImageAlpha),
|
||||
TOP_FADE_MID_STOP to rootBackground.copy(alpha = TOP_FADE_MID_ALPHA * (1f - bgImageAlpha)),
|
||||
1f to Color.Transparent,
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
state = listState,
|
||||
contentPadding = PaddingValues(
|
||||
top = topBarTotalHeight,
|
||||
bottom = TangemTheme.dimens2.x4 + bottomBarHeight,
|
||||
),
|
||||
) {
|
||||
payDetailsBody(state)
|
||||
with(expressTransactionsComponent) {
|
||||
expressTransactionsContent(
|
||||
state = expressState.transactionsToDisplay,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(top = 12.dp)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
with(txHistoryComponent) { txHistoryContent(listState = listState, state = txHistoryState) }
|
||||
}
|
||||
}
|
||||
|
||||
PayDetailsTopBar(
|
||||
config = state.topBarConfig,
|
||||
onHeightChange = { measuredHeight ->
|
||||
if (topBarTotalHeight != measuredHeight) topBarTotalHeight = measuredHeight
|
||||
},
|
||||
)
|
||||
}
|
||||
expressTransactionsBottomSheetState?.content(null)
|
||||
}
|
||||
|
||||
private fun LazyListScope.payDetailsBody(state: TangemPayDetailsUM) {
|
||||
item("balanceBlock") {
|
||||
BalanceBlock(
|
||||
state = state.balanceBlockState,
|
||||
isBalanceHidden = state.isBalanceHidden,
|
||||
)
|
||||
}
|
||||
state.balanceBlockState.cardsBlockState?.let { cardsState ->
|
||||
item("cardsBlock") {
|
||||
CardsBlock(cardsBlockState = cardsState)
|
||||
}
|
||||
}
|
||||
if (state.balanceBlockState.actionButtons.isNotEmpty()) {
|
||||
item("actionButtonsBlock") {
|
||||
ActionBlock(actionButtons = state.balanceBlockState.actionButtons)
|
||||
}
|
||||
}
|
||||
when {
|
||||
state.balanceBlockState.cardsBlockState?.cards?.fastAny { it.isReissuing } == true -> {
|
||||
item("reissuingBannerBlock") {
|
||||
TangemMessage(
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens2.x4),
|
||||
title = resourceReference(R.string.tangempay_reissue_card_in_progress),
|
||||
subtitle = resourceReference(R.string.tangempay_reissue_card_in_progress_description),
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
if (state.addToWalletBlockState != null) {
|
||||
item("addToWalletBannerBlock") {
|
||||
TangemPayAddToWalletBlock(
|
||||
state = state.addToWalletBlockState,
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens2.x4),
|
||||
)
|
||||
}
|
||||
}
|
||||
if (state.accountDeactivatedNotificationConfig != null) {
|
||||
item("deactivationBannerBlock") {
|
||||
TangemMessage(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens2.x4)
|
||||
.clickableSingle(
|
||||
onClick = { state.accountDeactivatedNotificationConfig.onClick?.invoke() },
|
||||
),
|
||||
title = state.accountDeactivatedNotificationConfig.title,
|
||||
subtitle = state.accountDeactivatedNotificationConfig.subtitle,
|
||||
messageEffect = TangemMessageEffect.Warning,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PayDetailsTopBar(
|
||||
config: TangemPayDetailsTopBarConfig,
|
||||
onHeightChange: (Dp) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val density = LocalDensity.current
|
||||
TangemTopBar(
|
||||
modifier = modifier
|
||||
.onSizeChanged { size ->
|
||||
onHeightChange(with(density) { size.height.toDp() })
|
||||
}
|
||||
.statusBarsPadding(),
|
||||
title = resourceReference(R.string.tangempay_payment_account),
|
||||
subtitle = resourceReference(R.string.tangempay_usdc_on_polygon_network),
|
||||
startContent = {
|
||||
TangemButton(
|
||||
iconStart = TangemIconUM.Icon(iconRes = CoreUiR.drawable.ic_arrow_back_28),
|
||||
onClick = config.onBackClick,
|
||||
size = TangemButton.Size.X11,
|
||||
variant = TangemButton.Variant.Material,
|
||||
)
|
||||
},
|
||||
endContent = if (config.itemsV2.isNotEmpty()) {
|
||||
{
|
||||
var isDropdownMenuShown by rememberSaveable { mutableStateOf(false) }
|
||||
Box {
|
||||
TangemButton(
|
||||
iconStart = TangemIconUM.Icon(iconRes = CoreUiR.drawable.ic_more_default_24),
|
||||
onClick = {
|
||||
config.onOpenMenu()
|
||||
isDropdownMenuShown = true
|
||||
},
|
||||
size = TangemButton.Size.X11,
|
||||
variant = TangemButton.Variant.Material,
|
||||
)
|
||||
PayContextMenuBlock(
|
||||
items = config.itemsV2,
|
||||
onMenuDismiss = { isDropdownMenuShown = false },
|
||||
isDropdownMenuShown = isDropdownMenuShown,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BalanceBlock(
|
||||
state: TangemPayDetailsBalanceBlockState,
|
||||
isBalanceHidden: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens2.x4)
|
||||
.padding(top = TangemTheme.dimens2.x12),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
AnimatedContent(
|
||||
targetState = state,
|
||||
label = "Updating the balance",
|
||||
transitionSpec = {
|
||||
fadeIn(animationSpec = tween(durationMillis = 220, delayMillis = 90)) togetherWith
|
||||
fadeOut(animationSpec = tween(durationMillis = 90))
|
||||
},
|
||||
) { animatedState ->
|
||||
when (animatedState) {
|
||||
is TangemPayDetailsBalanceBlockState.Loading -> TextShimmer(
|
||||
modifier = Modifier.size(width = 160.dp, height = 56.dp),
|
||||
text = "1234.00",
|
||||
style = TextShimmerStyle.HEADING_MEDIUM,
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
is TangemPayDetailsBalanceBlockState.Content -> Text(
|
||||
modifier = Modifier.testTag(TangemPayTestTags.PAYMENT_ACCOUNT_BALANCE),
|
||||
text = animatedState.fiatBalance.orMaskWithStars(isBalanceHidden).resolveAnnotatedReference(),
|
||||
style = TangemTheme.typography3.display.medium.applyBladeBrush(
|
||||
isEnabled = animatedState.isBalanceFlickering,
|
||||
textColor = TangemTheme.colors3.text.primary,
|
||||
),
|
||||
color = TangemTheme.colors3.text.primary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
autoSize = TextAutoSize.StepBased(
|
||||
minFontSize = TangemTheme.typography3.heading.medium.fontSize,
|
||||
maxFontSize = TangemTheme.typography3.display.medium.fontSize,
|
||||
),
|
||||
)
|
||||
is TangemPayDetailsBalanceBlockState.Error -> Text(
|
||||
modifier = Modifier.testTag(TangemPayTestTags.PAYMENT_ACCOUNT_BALANCE),
|
||||
text = DASH_SIGN.orMaskWithStars(isBalanceHidden),
|
||||
style = TangemTheme.typography3.display.medium,
|
||||
color = TangemTheme.colors3.text.primary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CardsBlock(
|
||||
cardsBlockState: TangemPayDetailsBalanceBlockState.CardsBlockState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LazyRow(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
state = rememberLazyListState(),
|
||||
contentPadding = PaddingValues(
|
||||
horizontal = TangemTheme.dimens2.x4,
|
||||
vertical = TangemTheme.dimens2.x6,
|
||||
),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
items(items = cardsBlockState.cards) { item ->
|
||||
TangemPayCardView(
|
||||
isReissuing = item.isReissuing,
|
||||
lastDigits = item.lastDigits,
|
||||
onClick = item.onClick,
|
||||
)
|
||||
SpacerW(TangemTheme.dimens2.x2)
|
||||
}
|
||||
item {
|
||||
TangemPayAddCardView(onClick = cardsBlockState.onAddCardClick)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LazyItemScope.ActionBlock(
|
||||
actionButtons: ImmutableList<ActionButtonConfig>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = TangemTheme.dimens2.x6),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
actionButtons.fastForEach { actionConfig ->
|
||||
TangemPayActionButton(
|
||||
iconRes = actionConfig.iconResId,
|
||||
onClick = actionConfig.onClick,
|
||||
isEnabled = actionConfig.isEnabled,
|
||||
isLoading = actionConfig.isInProgress,
|
||||
title = actionConfig.text,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region preview
|
||||
|
||||
@Preview(device = Devices.PIXEL_7_PRO)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.PIXEL_7_PRO)
|
||||
@Composable
|
||||
private fun TangemPayDetailsScreenPreview(
|
||||
@PreviewParameter(TangemPayDetailsUMProvider::class) state: TangemPayDetailsUM,
|
||||
) {
|
||||
TangemThemePreviewRedesign {
|
||||
TangemPayDetailsScreenV2(
|
||||
state = state,
|
||||
txHistoryComponent = PreviewTangemPayTxHistoryComponent(
|
||||
txHistoryUM = PreviewTangemPayTxHistoryComponent.contentUM,
|
||||
),
|
||||
expressTransactionsComponent = PreviewEmptyExpressTransactionsComponent(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(device = Devices.PIXEL_7_PRO)
|
||||
@Composable
|
||||
private fun TangemPayDetailsTxHistoryScreenPreview(
|
||||
@PreviewParameter(TangemPayDetailsTxHistoryProvider::class) state: TangemPayTxHistoryUM,
|
||||
) {
|
||||
TangemThemePreviewRedesign {
|
||||
TangemPayDetailsScreenV2(
|
||||
state = TangemPayDetailsUMProvider().values.first(),
|
||||
txHistoryComponent = PreviewTangemPayTxHistoryComponent(txHistoryUM = state),
|
||||
expressTransactionsComponent = PreviewEmptyExpressTransactionsComponent(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// end region preview
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package com.tangem.features.tangempay.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import com.tangem.core.ui.ds.contextmenu.TangemContextMenu
|
||||
import com.tangem.core.ui.ds.image.TangemIcon
|
||||
import com.tangem.core.ui.extensions.clickableSingle
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.tangempay.entity.TangemPayDropDownItemUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
internal fun PayContextMenuBlock(
|
||||
items: ImmutableList<TangemPayDropDownItemUM>,
|
||||
isDropdownMenuShown: Boolean,
|
||||
onMenuDismiss: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
TangemContextMenu(
|
||||
expanded = isDropdownMenuShown,
|
||||
onDismissRequest = onMenuDismiss,
|
||||
offset = DpOffset.Zero,
|
||||
modifier = modifier,
|
||||
) {
|
||||
items.fastForEach { item ->
|
||||
Column {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
|
||||
modifier = Modifier
|
||||
.clickableSingle(
|
||||
onClick = {
|
||||
item.onClick()
|
||||
onMenuDismiss()
|
||||
},
|
||||
)
|
||||
.padding(vertical = TangemTheme.dimens2.x3, horizontal = TangemTheme.dimens2.x4),
|
||||
) {
|
||||
TangemIcon(
|
||||
modifier = Modifier.size(TangemTheme.dimens2.x5),
|
||||
tangemIconUM = item.icon,
|
||||
)
|
||||
Text(
|
||||
text = item.title.resolveReference(),
|
||||
style = TangemTheme.typography3.body.medium,
|
||||
color = TangemTheme.colors3.text.primary,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package com.tangem.features.tangempay.ui.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.ds2.button.TangemButton
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveAnnotatedReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
|
||||
@Composable
|
||||
internal fun TangemPayActionButton(
|
||||
title: TextReference,
|
||||
@DrawableRes iconRes: Int,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
isEnabled: Boolean = true,
|
||||
isLoading: Boolean = false,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier.padding(horizontal = TangemTheme.dimens2.x4),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
TangemButton(
|
||||
variant = TangemButton.Variant.Material,
|
||||
size = TangemButton.Size.X14,
|
||||
onClick = onClick,
|
||||
iconStart = TangemIconUM.Icon(
|
||||
iconRes = iconRes,
|
||||
tintReference = { TangemTheme.colors3.icon.primary },
|
||||
),
|
||||
isLoading = isLoading,
|
||||
isEnabled = isEnabled,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = title.resolveAnnotatedReference(),
|
||||
style = TangemTheme.typography3.subheading.medium,
|
||||
color = TangemTheme.colors3.text.primary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun TangemPayActionButtonPreview() {
|
||||
TangemThemePreviewRedesign {
|
||||
TangemPayActionButton(
|
||||
title = stringReference("Action"),
|
||||
iconRes = R.drawable.ic_arrow_down_24,
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
package com.tangem.features.tangempay.ui.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.TileMode
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.tooling.preview.Devices
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.extensions.clickableSingle
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.core.ui.res.generated.icons.Icons
|
||||
import com.tangem.core.ui.res.generated.icons.ic_clock_12
|
||||
import com.tangem.core.ui.res.generated.icons.ic_cloud_12
|
||||
import com.tangem.core.ui.test.TangemPayTestTags
|
||||
|
||||
private const val DEFAULT_CARD_BG = 0xFF1C1F29
|
||||
private const val REISSUING_CARD_BG = 0xFF1E1E1E
|
||||
|
||||
@Composable
|
||||
internal fun TangemPayCardView(
|
||||
isReissuing: Boolean,
|
||||
lastDigits: String,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
CardBackground(
|
||||
modifier = modifier
|
||||
.size(
|
||||
height = TangemTheme.dimens2.x10,
|
||||
width = TangemTheme.dimens2.x14,
|
||||
)
|
||||
.testTag(TangemPayTestTags.PAYMENT_ACCOUNT_CARD_BUTTON),
|
||||
isReissuing = isReissuing,
|
||||
onClick = onClick,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens2.x1)
|
||||
.padding(top = TangemTheme.dimens2.x1),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens2.x3),
|
||||
imageVector = if (isReissuing) {
|
||||
Icons.ic_clock_12
|
||||
} else {
|
||||
Icons.ic_cloud_12
|
||||
},
|
||||
tint = TangemTheme.colors3.icon.staticDark,
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
Icon(
|
||||
modifier = Modifier.size(height = TangemTheme.dimens2.x2, width = 22.dp),
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_visa_logo),
|
||||
tint = TangemTheme.colors3.icon.staticDark,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(end = TangemTheme.dimens2.x1, bottom = TangemTheme.dimens2.x1),
|
||||
text = lastDigits,
|
||||
style = TangemTheme.typography3.caption.medium.copy(fontSize = 10.sp),
|
||||
color = TangemTheme.colors3.text.staticDark.primary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun TangemPayAddCardView(onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(
|
||||
height = TangemTheme.dimens2.x10,
|
||||
width = TangemTheme.dimens2.x14,
|
||||
)
|
||||
.clip(RoundedCornerShape(TangemTheme.dimens3.borderRadius.b075))
|
||||
.background(TangemTheme.colors3.bg.opaque.primary)
|
||||
.clickableSingle(onClick = onClick),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(20.dp),
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_plus_default_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors3.icon.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
private fun CardBackground(
|
||||
isReissuing: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable BoxScope.() -> Unit,
|
||||
) {
|
||||
val bgColor = remember(isReissuing) {
|
||||
if (isReissuing) {
|
||||
Color(REISSUING_CARD_BG)
|
||||
} else {
|
||||
Color(DEFAULT_CARD_BG)
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.clip(RoundedCornerShape(TangemTheme.dimens3.borderRadius.b075))
|
||||
.drawBehind {
|
||||
drawRect(bgColor)
|
||||
|
||||
val w = size.width
|
||||
val h = size.height
|
||||
val radiusScaleRightCorner = h / 2f
|
||||
val radiusScaleLeftCorner = h / 1.27f
|
||||
|
||||
drawRect(
|
||||
brush = Brush.radialGradient(
|
||||
colors = listOf(
|
||||
Color(
|
||||
if (isReissuing) 0xFFB0B4BC else 0xFF38587F,
|
||||
).copy(
|
||||
if (isReissuing) .1f else .41f,
|
||||
),
|
||||
Color.Transparent,
|
||||
),
|
||||
center = Offset(w - 20f, h * .05f),
|
||||
radius = radiusScaleRightCorner,
|
||||
tileMode = TileMode.Clamp,
|
||||
),
|
||||
)
|
||||
|
||||
if (!isReissuing) {
|
||||
drawRect(
|
||||
brush = Brush.radialGradient(
|
||||
colors = listOf(
|
||||
Color(0xFF2881FF).copy(.25f),
|
||||
Color.Transparent,
|
||||
),
|
||||
center = Offset(0f, h + h * .1f),
|
||||
radius = radiusScaleLeftCorner,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = TangemTheme.colors3.border.primary,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens3.borderRadius.b075),
|
||||
)
|
||||
.clickableSingle(onClick = onClick),
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.PIXEL_7_PRO)
|
||||
@Composable
|
||||
private fun CardBackgroundPreview() {
|
||||
TangemThemePreviewRedesign {
|
||||
Column(modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.Center) {
|
||||
CardBackground(
|
||||
modifier = Modifier.size(
|
||||
height = TangemTheme.dimens2.x10,
|
||||
width = TangemTheme.dimens2.x14,
|
||||
),
|
||||
isReissuing = false,
|
||||
onClick = {},
|
||||
content = {},
|
||||
)
|
||||
SpacerH(TangemTheme.dimens2.x4)
|
||||
CardBackground(
|
||||
modifier = Modifier.size(
|
||||
height = TangemTheme.dimens2.x10,
|
||||
width = TangemTheme.dimens2.x14,
|
||||
),
|
||||
isReissuing = true,
|
||||
onClick = {},
|
||||
content = {},
|
||||
)
|
||||
SpacerH(TangemTheme.dimens2.x4)
|
||||
TangemPayCardView(isReissuing = false, onClick = {}, lastDigits = "1234")
|
||||
SpacerH(TangemTheme.dimens2.x4)
|
||||
TangemPayCardView(isReissuing = true, onClick = {}, lastDigits = "")
|
||||
SpacerH(TangemTheme.dimens2.x4)
|
||||
TangemPayAddCardView(onClick = {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 3 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 12 KiB |