diff --git a/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsBlock.kt b/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsBlock.kt index 3f12092079..8cb08d87c0 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsBlock.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsBlock.kt @@ -52,13 +52,13 @@ fun NavigationButtonsBlock( horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), ) { PreviousButton(state?.prevButton) - PrimaryButton(state?.primaryButton, modifier = Modifier.weight(1f)) + NavigationPrimaryButton(state?.primaryButton, modifier = Modifier.weight(1f)) } } } @Composable -private fun PrimaryButton(primaryButton: NavigationButton?, modifier: Modifier = Modifier) { +fun NavigationPrimaryButton(primaryButton: NavigationButton?, modifier: Modifier = Modifier) { val wrappedButton by rememberNavigationButton(primaryButton) AnimatedContent( targetState = wrappedButton, diff --git a/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsState.kt b/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsState.kt index e9a1fd8a83..cb7a3743ec 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsState.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsState.kt @@ -12,6 +12,7 @@ sealed class NavigationButtonsState { val prevButton: NavigationButton?, val extraButtons: ImmutableList, val txUrl: String? = null, + val onTextClick: (String) -> Unit, ) : NavigationButtonsState() } diff --git a/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/preview/NavigationButtonsPreview.kt b/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/preview/NavigationButtonsPreview.kt index b98e5de371..9d2e71fe85 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/preview/NavigationButtonsPreview.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/preview/NavigationButtonsPreview.kt @@ -54,5 +54,6 @@ internal object NavigationButtonsPreview { prevButton = prev, extraButtons = extraButtons, txUrl = "https://tangem.com", + onTextClick = {}, ) } \ No newline at end of file diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index b0e97efb9f..2f740dd9d7 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -132,6 +132,7 @@ Primär Karte Passphrase Einfügen + Datenschutzbestimmungen %1$s-%2$s %1$s — %2$s Weiterlesen @@ -159,6 +160,7 @@ Unterstützung Tauschen Allgemeine Geschäftsbedingungen + Nutzungsbedingungen Heute Transaktion fehlgeschlagen Transaktionen @@ -264,7 +266,6 @@ Durch die Nutzung der Swap-Funktion erklärst du dich mit den folgenden Bedingungen des Anbieters einverstanden %s Durch die Nutzung der Swap-Funktionalität erklärst du dich mit des Anbieters %1$s und %2$s einverstanden Weitere Anbieter werden bald folgen.\nBleib dabei! - Datenschutzbestimmungen Anbieter Bester Preis Verfügbar bis zu %s @@ -272,7 +273,6 @@ Für dieses Paar nicht verfügbar Erlaubnis erforderlich Empfohlen - Nutzungsbedingungen Keine Token gefunden. Bitte versuche eine andere Anfrage ID: %s Transaktions-ID kopiert diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index 6b76c494d8..87595dbdf6 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -132,6 +132,7 @@ Carte principale Passphrase Coller + Politique de confidentialité %1$s-%2$s %1$s — %2$s En savoir plus @@ -159,6 +160,7 @@ Support Échanger termes et conditions + Conditions d\'utilisation Aujourd\'hui La transaction a échoué Transactions @@ -264,7 +266,6 @@ En utilisant la fonctionnalité d\'échange, vous acceptez les %s En utilisant la fonctionnalité d\'échange, vous acceptez les conditions %1$s et %2$s du fournisseur D\'autres fournisseurs arriveront bientôt.\nRestez branchés ! - Politique de confidentialité Fournisseur Meilleur taux Disponible jusqu\'à %s @@ -272,7 +273,6 @@ Indisponible pour cette paire Permission requise Recommandé - Conditions d\'utilisation Aucun jeton trouvé. Veuillez essayer une autre demande ID : %s ID de transaction copié diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index 3d498f8938..33f3202506 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -129,6 +129,7 @@ プライマリーカード パスフレーズ ペースト + プライバシーポリシー %1$s-%2$s %1$s — %2$s 続きを読む @@ -156,6 +157,7 @@ サポート スワップ 利用規約 + 利用規約 今日 取引が失敗しました 取引 @@ -261,7 +263,6 @@ スワップ機能を使用すると、プロバイダーの%sに同意したことになります。 スワップ機能を使用すると、プロバイダーの%1$sおよび%2$sに同意したことになります。 さらに多くのプロバイダーを追加予定です。 \nお楽しみに。 - プライバシーポリシー プロバイダー ベストレート 最大 %s まで使用可能 @@ -269,7 +270,6 @@ このペアは利用できません 許可が必要です 推奨 - 利用規約 トークンが見つかりません。別のリクエストをお試しください。 ID: %s 取引IDをコピーしました diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 2f4348ef70..4d2fc19ca5 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -136,6 +136,7 @@ Основная карта Парольная фраза Вставить + Политикой конфиденциальности %1$s-%2$s %1$s — %2$s Подробнее @@ -163,6 +164,7 @@ Поддержка Обмен условия участия + Условиями использования Сегодня Ошибка транзакции Транзакции @@ -266,7 +268,6 @@ Пользуясь сервисом, вы соглашаетесь с %s Пользуясь сервисом, вы соглашаетесь с %1$s и %2$s Больше провайдеров на подходе.\nСледите за обновлениями! - Политикой конфиденциальности Провайдер Лучший курс Доступно до %s @@ -274,7 +275,6 @@ Недоступно для этой пары Требуется разрешение Рекомендовано - Условиями использования Токены не найдены. Пожалуйста, попробуйте другой запрос ID: %s ID транзакции скопирован diff --git a/core/res/src/main/res/values-uk-rUA/strings.xml b/core/res/src/main/res/values-uk-rUA/strings.xml index 83d2675b15..ca0bc61e34 100644 --- a/core/res/src/main/res/values-uk-rUA/strings.xml +++ b/core/res/src/main/res/values-uk-rUA/strings.xml @@ -129,6 +129,7 @@ Основна картка Парольна фраза Вставити + Політикою конфіденційності %1$s-%2$s %1$s — %2$s Детальніше @@ -156,6 +157,7 @@ Підтримка Обмін умови участі + Умовами використання Сьогодні Помилка транзакції Транзакції @@ -259,7 +261,6 @@ Використовуючи сервіс обміну, ви погоджуєтеся з його %s Використовуючи сервіс обміну, ви погоджуєтеся з його %1$s та %2$s Незабаром з\'являться нові провайдери.\nСлідкуйте за новинами! - Політикою конфіденційності Провайдер Найкращий курс Доступно до %s @@ -267,7 +268,6 @@ Недоступно для цієї пари Потрібен дозвіл Рекомендовано - Умовами використання Токенів не знайдено. Будь ласка, спробуйте інший запит ID: %s ID транзакції скопійовано diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 3c7f4884ca..b89491b528 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -133,6 +133,7 @@ Primary Card Passphrase Paste + Privacy Policy %1$s-%2$s %1$s — %2$s Read more @@ -160,6 +161,7 @@ Support Swap terms and conditions + Terms of Use Today Transaction failed Transactions @@ -265,7 +267,6 @@ By using swap functionality, you agree with provider’s %s By using swap functionality, you agree with provider’s %1$s and %2$s More providers are coming soon.\nStay tuned! - Privacy Policy Provider Best rate Available up to %s @@ -273,7 +274,6 @@ Unavailable for this pair Permission Required Recommended - Terms of Use No tokens found. Please try another request ID: %s Transaction ID copied @@ -664,6 +664,7 @@ The period you must wait after requesting to withdraw funds from staking before the tokens become available. Warmup period The allocated time for activating participation in staking. + By using staking functionality, you agree with provider’s %1$s and %2$s Locked Migrate Native staking diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt index 6f007f76cb..8a47bfe35d 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt @@ -2,6 +2,7 @@ package com.tangem.features.staking.impl.presentation.state import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.common.ui.navigationButtons.NavigationButtonsState +import com.tangem.core.navigation.url.UrlOpener import com.tangem.core.ui.event.consumedEvent import com.tangem.core.ui.event.triggeredEvent import com.tangem.core.ui.extensions.TextReference @@ -19,7 +20,9 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -internal class StakingStateController @Inject constructor() { +internal class StakingStateController @Inject constructor( + private val urlOpener: UrlOpener, +) { val value: StakingUiState get() = uiState.value @@ -27,7 +30,7 @@ internal class StakingStateController @Inject constructor() { val uiState: StateFlow get() = mutableUiState.asStateFlow() - private val buttonsTransformer = SetButtonsStateTransformer() + private val buttonsTransformer = SetButtonsStateTransformer(urlOpener) private val titleTransformer = SetTitleTransformer fun update(function: (StakingUiState) -> StakingUiState) { diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetButtonsStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetButtonsStateTransformer.kt index 5ad39ad9be..d9f2906670 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetButtonsStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetButtonsStateTransformer.kt @@ -2,6 +2,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers import com.tangem.common.ui.navigationButtons.NavigationButton import com.tangem.common.ui.navigationButtons.NavigationButtonsState +import com.tangem.core.navigation.url.UrlOpener import com.tangem.core.ui.R import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference @@ -12,7 +13,9 @@ import com.tangem.utils.transformer.Transformer import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -internal class SetButtonsStateTransformer : Transformer { +internal class SetButtonsStateTransformer( + private val urlOpener: UrlOpener, +) : Transformer { override fun transform(prevState: StakingUiState): StakingUiState { val confirmState = prevState.confirmationState as? StakingStates.ConfirmationState.Data @@ -23,6 +26,7 @@ internal class SetButtonsStateTransformer : Transformer { prevButton = getPrevButton(prevState), extraButtons = getExtraButtons(prevState), txUrl = (confirmState?.transactionDoneState as? TransactionDoneState.Content)?.txUrl, + onTextClick = urlOpener::openUrl, ) } else { NavigationButtonsState.Empty diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt index 6020b27b49..5e2f5c710b 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt @@ -28,6 +28,9 @@ import androidx.compose.ui.text.withStyle 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.Density +import com.tangem.common.ui.navigationButtons.NavigationButtonsState +import com.tangem.common.ui.navigationButtons.NavigationPrimaryButton import com.tangem.core.ui.components.SpacerH12 import com.tangem.core.ui.components.containers.FooterContainer import com.tangem.core.ui.components.inputrow.InputRowDefault @@ -56,14 +59,21 @@ import kotlinx.collections.immutable.ImmutableList private const val BANNER_BLOCK_KEY = "BannerBlock" private const val STAKING_REWARD_BLOCK_KEY = "StakingRewardBlock" private const val ACTIVE_STAKING_BLOCK_KEY = "ActiveStakingBlock" +private const val STAKE_PRIMARY_BUTTON_KEY = "StakePrimaryButton" @OptIn(ExperimentalFoundationApi::class) @Composable -internal fun StakingInitialInfoContent(state: StakingStates.InitialInfoState, clickIntents: StakingClickIntents) { +internal fun StakingInitialInfoContent( + state: StakingStates.InitialInfoState, + buttonState: NavigationButtonsState, + clickIntents: StakingClickIntents, +) { if (state !is StakingStates.InitialInfoState.Data) return LazyColumn( + verticalArrangement = alignLastToBottom(), modifier = Modifier + .fillMaxSize() .background(TangemTheme.colors.background.secondary) .padding(horizontal = TangemTheme.dimens.spacing16), ) { @@ -105,6 +115,10 @@ internal fun StakingInitialInfoContent(state: StakingStates.InitialInfoState, cl } } } + + item(STAKE_PRIMARY_BUTTON_KEY) { + StakeButtonBlock(buttonState) + } } } @@ -236,6 +250,21 @@ private fun ActiveStakingBlock(groups: ImmutableList, onCli } } +@Composable +private fun StakeButtonBlock(buttonState: NavigationButtonsState) { + val state = buttonState as? NavigationButtonsState.Data + val primaryButton = state?.primaryButton + + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), + ) { + state?.onTextClick?.let { StakingTosText(it) } + NavigationPrimaryButton(primaryButton = primaryButton) + } +} + @Composable private fun getCaption(balanceType: BalanceType, balance: BalanceState): TextReference { return when (balanceType) { @@ -289,6 +318,24 @@ private val textGradientColors = listOf( Color(0xff8fb4df), ) +@Composable +private fun alignLastToBottom() = remember { + object : Arrangement.Vertical { + override fun Density.arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) { + var currentOffset = 0 + + sizes.forEachIndexed { index, size -> + if (index == sizes.lastIndex) { + outPositions[index] = totalSize - size + } else { + outPositions[index] = currentOffset + currentOffset += size + } + } + } + } +} + // region preview @Preview(showBackground = true, widthDp = 360) @@ -300,6 +347,7 @@ private fun StakingInitialInfoContent_Preview( TangemThemePreview { StakingInitialInfoContent( state = feeState, + buttonState = NavigationButtonsState.Empty, clickIntents = StakingClickIntentsStub, ) } diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt index 54f2aedeb1..48b48cd7a0 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt @@ -16,6 +16,7 @@ import com.tangem.common.ui.amountScreen.AmountScreenContent import com.tangem.common.ui.bottomsheet.permission.GiveTxPermissionBottomSheet import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionBottomSheetConfig import com.tangem.common.ui.navigationButtons.NavigationButtonsBlock +import com.tangem.common.ui.navigationButtons.NavigationButtonsState import com.tangem.core.ui.components.appbar.AppBarWithBackButtonAndIcon import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.extensions.resolveReference @@ -55,7 +56,8 @@ internal fun StakingScreen(uiState: StakingUiState) { modifier = Modifier.weight(1f), ) NavigationButtonsBlock( - buttonState = uiState.buttonsState, + buttonState = uiState.buttonsState.takeUnless { uiState.currentStep == StakingStep.InitialInfo } + ?: NavigationButtonsState.Empty, footerText = confirmationState?.footerText.takeIf { uiState.currentStep == StakingStep.Confirmation }, modifier = Modifier.padding( start = TangemTheme.dimens.spacing16, @@ -153,6 +155,7 @@ private fun StakingScreenContent(uiState: StakingUiState, modifier: Modifier = M when (state) { StakingStep.InitialInfo -> StakingInitialInfoContent( state = uiState.initialInfoState, + buttonState = uiState.buttonsState, clickIntents = uiState.clickIntents, ) StakingStep.RewardsValidators -> { diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingTosText.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingTosText.kt new file mode 100644 index 0000000000..2f239888e8 --- /dev/null +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingTosText.kt @@ -0,0 +1,62 @@ +package com.tangem.features.staking.impl.presentation.ui + +import androidx.compose.foundation.text.ClickableText +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.style.TextAlign +import com.tangem.core.ui.extensions.appendColored +import com.tangem.core.ui.res.TangemTheme +import com.tangem.features.staking.impl.R + +private const val TERMS_OF_USE_KEY = "termsOfUse" +private const val PRIVACY_POLICY_KEY = "privacyPolicy" + +private const val TERMS_OF_USE_URL = "https://docs.stakek.it/docs/terms-of-use" +private const val PRIVACY_POLICY_URL = "https://docs.stakek.it/docs/privacy-policy" + +@Composable +internal fun StakingTosText(onTextClick: (String) -> Unit) { + val termsOfUse = stringResource(R.string.common_terms_of_use) + val privacyPolicy = stringResource(R.string.common_privacy_policy) + val tosText = stringResource(R.string.staking_legal, termsOfUse, privacyPolicy) + + val clickableAnnotation = buildAnnotatedString { + append(tosText.substringBefore(termsOfUse)) + + pushStringAnnotation(TERMS_OF_USE_KEY, "") + appendColored(termsOfUse, TangemTheme.colors.text.accent) + pop() + + append(tosText.substringAfter(termsOfUse).substringBefore(privacyPolicy)) + + pushStringAnnotation(PRIVACY_POLICY_KEY, "") + appendColored(privacyPolicy, TangemTheme.colors.text.accent) + pop() + } + + ClickableText( + text = clickableAnnotation, + style = TangemTheme.typography.caption2.copy( + color = TangemTheme.colors.text.tertiary, + textAlign = TextAlign.Center, + ), + onClick = { offset -> + clickableAnnotation.getStringAnnotations( + tag = TERMS_OF_USE_KEY, + start = offset, + end = offset, + ).firstOrNull()?.let { + onTextClick(TERMS_OF_USE_URL) + } + + clickableAnnotation.getStringAnnotations( + tag = PRIVACY_POLICY_KEY, + start = offset, + end = offset, + ).firstOrNull()?.let { + onTextClick(PRIVACY_POLICY_URL) + } + }, + ) +} \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index 157e1ee276..34bf5fb2ec 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -323,14 +323,14 @@ internal class StateBuilder( return TosState( tosLink = swapProvider.termsOfUse?.let { LegalState( - title = resourceReference(R.string.express_terms_of_use), + title = resourceReference(R.string.common_terms_of_use), link = it, onClick = actions.onTosClick, ) }, policyLink = swapProvider.privacyPolicy?.let { LegalState( - title = resourceReference(R.string.express_privacy_policy), + title = resourceReference(R.string.common_privacy_policy), link = it, onClick = actions.onPolicyClick, )