From 73f98e1707604723f4ead033def7339323e0454b Mon Sep 17 00:00:00 2001 From: Rafael Date: Thu, 26 Dec 2024 12:48:46 +0600 Subject: [PATCH] Apply new design to buy subscription module --- .../bankwallet/core/Extensions.kt | 2 + .../bankwallet/core/NavController.kt | 2 +- .../market/favorites/MarketSignalsFragment.kt | 4 +- .../modules/premium/PremiumFragment.kt | 70 --- .../modules/premium/PremiumUiElements.kg.kt | 170 ------ .../premium/SelectPremiumPlanScreen.kt | 547 ------------------ .../settings/main/MainSettingsScreen.kt | 2 +- .../modules/settings/main/PremiumBanner.kt | 2 +- .../BuySubscriptionChoosePlanFragment.kt | 110 ---- .../BuySubscriptionChoosePlanViewModel.kt | 32 +- .../BuySubscriptionFragment.kt | 93 ++- .../usersubscription/BuySubscriptionModel.kt | 84 +++ .../BuySubscriptionViewModel.kt | 3 +- .../ui/PremiumSubscribedScreen.kt} | 78 ++- .../ui/SelectSubscriptionScreen.kt | 170 ++++++ .../modules/usersubscription/ui/UiElements.kt | 480 +++++++++++++++ app/src/main/res/drawable/prem_duress_24.xml | 14 + .../main/res/drawable/prem_portfolio_24.xml | 13 + app/src/main/res/drawable/prem_tor_24.xml | 27 + app/src/main/res/navigation/main_graph.xml | 6 - app/src/main/res/values/strings.xml | 45 +- .../subscriptions/core/IPaidAction.kt | 32 +- .../core/UserSubscriptionManager.kt | 38 +- 23 files changed, 1009 insertions(+), 1015 deletions(-) delete mode 100644 app/src/main/java/io/horizontalsystems/bankwallet/modules/premium/PremiumFragment.kt delete mode 100644 app/src/main/java/io/horizontalsystems/bankwallet/modules/premium/PremiumUiElements.kg.kt delete mode 100644 app/src/main/java/io/horizontalsystems/bankwallet/modules/premium/SelectPremiumPlanScreen.kt delete mode 100644 app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionChoosePlanFragment.kt create mode 100644 app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionModel.kt rename app/src/main/java/io/horizontalsystems/bankwallet/modules/{premium/PremiumSubscribed.kt => usersubscription/ui/PremiumSubscribedScreen.kt} (59%) create mode 100644 app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/ui/SelectSubscriptionScreen.kt create mode 100644 app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/ui/UiElements.kt create mode 100644 app/src/main/res/drawable/prem_duress_24.xml create mode 100644 app/src/main/res/drawable/prem_portfolio_24.xml create mode 100644 app/src/main/res/drawable/prem_tor_24.xml diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/core/Extensions.kt b/app/src/main/java/io/horizontalsystems/bankwallet/core/Extensions.kt index 0479db6252..db43b3aaa8 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/core/Extensions.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/core/Extensions.kt @@ -191,10 +191,12 @@ fun NavGraphBuilder.composablePage( fun NavGraphBuilder.composablePopup( route: String, + arguments: List = emptyList(), content: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit ) { composable( route, + arguments = arguments, enterTransition = { slideIntoContainer( AnimatedContentTransitionScope.SlideDirection.Up, diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/core/NavController.kt b/app/src/main/java/io/horizontalsystems/bankwallet/core/NavController.kt index 3874d19e37..2fc9ddbf66 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/core/NavController.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/core/NavController.kt @@ -61,7 +61,7 @@ fun NavController.paidAction(paidAction: IPaidAction, block: () -> Unit) { if (UserSubscriptionManager.isActionAllowed(paidAction)) { block.invoke() } else { - slideFromRightForResult( + slideFromBottomForResult( R.id.buySubscriptionFragment, BuySubscriptionFragment.Input(paidAction) ) { diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/favorites/MarketSignalsFragment.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/favorites/MarketSignalsFragment.kt index 60037cafce..4c00045c6b 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/favorites/MarketSignalsFragment.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/favorites/MarketSignalsFragment.kt @@ -37,7 +37,7 @@ import io.horizontalsystems.bankwallet.ui.compose.components.VSpacer import io.horizontalsystems.bankwallet.ui.compose.components.sectionItemBorder import io.horizontalsystems.bankwallet.ui.compose.components.subhead2_leah import io.horizontalsystems.marketkit.models.Analytics.TechnicalAdvice.Advice -import io.horizontalsystems.subscriptions.core.EnableWatchlistSignals +import io.horizontalsystems.subscriptions.core.TradeSignals import kotlinx.parcelize.Parcelize class MarketSignalsFragment : BaseComposeFragment() { @@ -130,7 +130,7 @@ fun MarketSignalsScreen(navController: NavController) { .padding(start = 16.dp, end = 16.dp), title = stringResource(R.string.Market_Signal_TurnOn), onClick = { - navController.paidAction(EnableWatchlistSignals) { + navController.paidAction(TradeSignals) { navController.setNavigationResultX(MarketSignalsFragment.Result(true)) navController.popBackStack() } diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/premium/PremiumFragment.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/premium/PremiumFragment.kt deleted file mode 100644 index 809342c40a..0000000000 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/premium/PremiumFragment.kt +++ /dev/null @@ -1,70 +0,0 @@ -package io.horizontalsystems.bankwallet.modules.premium - -import android.os.Parcelable -import androidx.compose.runtime.Composable -import androidx.navigation.NavController -import androidx.navigation.NavType -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.rememberNavController -import androidx.navigation.navArgument -import io.horizontalsystems.bankwallet.core.BaseComposeFragment -import io.horizontalsystems.bankwallet.core.composablePage -import io.horizontalsystems.bankwallet.ui.compose.ComposeAppTheme -import io.horizontalsystems.subscriptions.core.IPaidAction -import kotlinx.parcelize.Parcelize - -class PremiumFragment : BaseComposeFragment() { - - @Composable - override fun GetContent(navController: NavController) { - ComposeAppTheme { - PremiumNavHost( - navController = navController, - onClose = { navController.popBackStack() } - ) - } - } - - @Parcelize - data class Input(val action: IPaidAction) : Parcelable - - @Parcelize - class Result : Parcelable -} - -@Composable -fun PremiumNavHost( - navController: NavController, - onClose: () -> Unit -) { - val navHostController = rememberNavController() - NavHost( - navController = navHostController, - startDestination = "select_premium_plan", - ) { - composable("select_premium_plan") { - SelectPremiumPlanScreen( - navHostController, - onCloseClick = onClose - ) - } - composablePage( - "premium_subscribed_page?type={type}", - arguments = listOf( - navArgument("type") { - type = NavType.StringType - defaultValue = null - nullable = true - }, - ) - ) { navBackStackEntry -> - val type = - navBackStackEntry.arguments?.getString("type") ?: PremiumPlanType.ProPlan.name - PremiumSubscribedScreen( - type = PremiumPlanType.valueOf(type), - onCloseClick = onClose - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/premium/PremiumUiElements.kg.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/premium/PremiumUiElements.kg.kt deleted file mode 100644 index 388921774c..0000000000 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/premium/PremiumUiElements.kg.kt +++ /dev/null @@ -1,170 +0,0 @@ -package io.horizontalsystems.bankwallet.modules.premium - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.defaultMinSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.Icon -import androidx.compose.material.ProvideTextStyle -import androidx.compose.material.Surface -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Brush -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.text.withStyle -import androidx.compose.ui.unit.dp -import io.horizontalsystems.bankwallet.R -import io.horizontalsystems.bankwallet.ui.compose.ComposeAppTheme -import io.horizontalsystems.bankwallet.ui.compose.components.ButtonPrimaryDefaults -import io.horizontalsystems.bankwallet.ui.compose.components.ButtonSecondary -import io.horizontalsystems.bankwallet.ui.compose.components.HsIconButton -import io.horizontalsystems.bankwallet.ui.compose.components.SecondaryButtonDefaults.buttonColors -import io.horizontalsystems.bankwallet.ui.compose.components.headline1_leah - -@Composable -fun highlightText( - text: String, - highlightPart: String, - color: Color -): AnnotatedString { - - return buildAnnotatedString { - val highlightIndex = text - .lowercase() - .indexOf(highlightPart.lowercase()) - - if (highlightIndex != -1) { - append(text.substring(0, highlightIndex)) - - withStyle( - SpanStyle(color = color) - ) { - append( - text.substring( - highlightIndex, - highlightIndex + highlightPart.length - ) - ) - } - - append( - text.substring(highlightIndex + highlightPart.length) - ) - } else { - append(text) - } - } -} - -@OptIn(ExperimentalMaterialApi::class) -@Composable -fun ButtonPrimaryCustomColor( - modifier: Modifier = Modifier, - title: String, - brush: Brush, - onClick: () -> Unit, - enabled: Boolean = true, - contentPadding: PaddingValues = ButtonPrimaryDefaults.ContentPadding, -) { - Surface( - modifier = modifier - .clip(RoundedCornerShape(25.dp)) - .background(brush), - shape = RoundedCornerShape(25.dp), - color = Color.Transparent, - contentColor = ComposeAppTheme.colors.dark, - onClick = onClick, - enabled = enabled, - ) { - ProvideTextStyle( - value = ComposeAppTheme.typography.headline2 - ) { - Row( - Modifier - .defaultMinSize( - minWidth = ButtonPrimaryDefaults.MinWidth, - minHeight = ButtonPrimaryDefaults.MinHeight - ) - .padding(contentPadding), - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - title, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - } - } - } -} - -@Composable -fun TitleCenteredTopBar( - title: String, - modifier: Modifier = Modifier, - onCloseClick: () -> Unit -) { - Box( - modifier = modifier - .height(64.dp) - .fillMaxWidth(), - ) { - headline1_leah( - text = title, - modifier = Modifier.align(Alignment.Center) - ) - HsIconButton( - modifier = Modifier.align(Alignment.CenterEnd), - onClick = onCloseClick - ) { - Icon( - painter = painterResource(id = R.drawable.ic_close), - contentDescription = "close button", - tint = ComposeAppTheme.colors.jacob, - ) - } - } -} - -@Composable -fun ColoredTextSecondaryButton( - title: String, - color: Color, - modifier: Modifier = Modifier, - onClick: () -> Unit -) { - ButtonSecondary( - modifier = modifier, - onClick = onClick, - buttonColors = buttonColors( - backgroundColor = ComposeAppTheme.colors.transparent, - contentColor = color, - disabledBackgroundColor = ComposeAppTheme.colors.transparent, - disabledContentColor = ComposeAppTheme.colors.grey50, - ), - content = { - Text( - text = title, - maxLines = 1, - style = ComposeAppTheme.typography.body, - overflow = TextOverflow.Ellipsis, - ) - }, - ) -} diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/premium/SelectPremiumPlanScreen.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/premium/SelectPremiumPlanScreen.kt deleted file mode 100644 index 62510011fb..0000000000 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/premium/SelectPremiumPlanScreen.kt +++ /dev/null @@ -1,547 +0,0 @@ -package io.horizontalsystems.bankwallet.modules.premium - -import androidx.annotation.StringRes -import androidx.compose.animation.core.Animatable -import androidx.compose.animation.core.TweenSpec -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.pager.HorizontalPager -import androidx.compose.foundation.pager.rememberPagerState -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Divider -import androidx.compose.material.Icon -import androidx.compose.material.ModalBottomSheetLayout -import androidx.compose.material.ModalBottomSheetValue -import androidx.compose.material.Scaffold -import androidx.compose.material.Tab -import androidx.compose.material.TabRow -import androidx.compose.material.TabRowDefaults -import androidx.compose.material.TabRowDefaults.tabIndicatorOffset -import androidx.compose.material.Text -import androidx.compose.material.rememberModalBottomSheetState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Brush -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import io.horizontalsystems.bankwallet.R -import io.horizontalsystems.bankwallet.modules.evmfee.ButtonsGroupWithShade -import io.horizontalsystems.bankwallet.ui.compose.ComposeAppTheme -import io.horizontalsystems.bankwallet.ui.compose.Steel20 -import io.horizontalsystems.bankwallet.ui.compose.components.ButtonPrimaryTransparent -import io.horizontalsystems.bankwallet.ui.compose.components.ButtonPrimaryYellow -import io.horizontalsystems.bankwallet.ui.compose.components.HSpacer -import io.horizontalsystems.bankwallet.ui.compose.components.RadialBackground -import io.horizontalsystems.bankwallet.ui.compose.components.VSpacer -import io.horizontalsystems.bankwallet.ui.compose.components.body_grey -import io.horizontalsystems.bankwallet.ui.compose.components.caption_grey -import io.horizontalsystems.bankwallet.ui.compose.components.headline1_leah -import io.horizontalsystems.bankwallet.ui.compose.components.subhead1_leah -import io.horizontalsystems.bankwallet.ui.compose.components.subhead2_jacob -import io.horizontalsystems.bankwallet.ui.compose.components.subhead2_remus -import io.horizontalsystems.bankwallet.ui.extensions.BottomSheetHeader -import kotlinx.coroutines.launch - -enum class PremiumPlanType(@StringRes val titleResId: Int) { - ProPlan(R.string.Premium_PlanPro), - VipPlan(R.string.Premium_PlanVip); -} - -val yellowGradient = Brush.horizontalGradient( - colors = listOf( - Color(0xFFFFD000), - Color(0xFFFFA800), - ) -) - -private val steelBrush = Brush.horizontalGradient( - colors = listOf(Steel20, Steel20) -) - -@Composable -fun SelectPremiumPlanScreen( - navController: NavController, - onCloseClick: () -> Unit = {} -) { - val coroutineScope = rememberCoroutineScope() - val selectedTabIndex = remember { mutableStateOf(0) } - val animationSpec = remember { - Animatable(0f) - .run { - TweenSpec(durationMillis = 300) - } - } - val modalBottomSheetState = rememberModalBottomSheetState( - initialValue = ModalBottomSheetValue.Hidden, - skipHalfExpanded = true, - animationSpec = animationSpec - ) - ModalBottomSheetLayout( - sheetState = modalBottomSheetState, - sheetBackgroundColor = ComposeAppTheme.colors.transparent, - sheetContent = { - SelectSubscriptionBottomSheet( - type = PremiumPlanType.entries[selectedTabIndex.value], - onDismiss = { - coroutineScope.launch { - modalBottomSheetState.hide() - } - }, - onSubscribeClick = { type -> - coroutineScope.launch { - modalBottomSheetState.hide() - navController.navigate("premium_subscribed_page?type=${type.name}") - } - } - ) - }, - ) { - Scaffold( - backgroundColor = ComposeAppTheme.colors.tyler, - topBar = { - TitleCenteredTopBar( - title = stringResource(R.string.Premium_Title), - onCloseClick = onCloseClick - ) - } - ) { paddingValues -> - Box( - modifier = Modifier - .padding(paddingValues) - .fillMaxSize() - ) { - RadialBackground() - Column { - Column( - modifier = Modifier - .padding(paddingValues) - .weight(1f) - .verticalScroll(rememberScrollState()) - ) - { - VSpacer(12.dp) - body_grey( - text = stringResource(R.string.Premium_ChoosePlanForYou), - modifier = Modifier.fillMaxWidth(), - textAlign = TextAlign.Center - ) - VSpacer(24.dp) - PlanTabs( - selectedTabIndex = selectedTabIndex.value, - onTabChange = { index -> - selectedTabIndex.value = index - } - ) - VSpacer(32.dp) - } - - ButtonsGroupWithShade { - Column( - modifier= Modifier.padding(horizontal = 24.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - ButtonPrimaryCustomColor( - modifier = Modifier.fillMaxWidth(), - title = stringResource(R.string.Premium_TryForFree), - brush = yellowGradient, - onClick = { - coroutineScope.launch { - modalBottomSheetState.show() - } - }, - ) - VSpacer(12.dp) - ColoredTextSecondaryButton( - title = stringResource(R.string.Premium_Restore), - onClick = { - // - }, - color = ComposeAppTheme.colors.leah - ) - } - } - } - } - } - } -} - -@Composable -fun SelectSubscriptionBottomSheet( - type: PremiumPlanType, - onDismiss: () -> Unit, - onSubscribeClick: (PremiumPlanType) -> Unit -) { - val selectedItem = remember { mutableIntStateOf(0) } - BottomSheetHeader( - iconPainter = painterResource(R.drawable.prem_star_yellow_16), - iconTint = ColorFilter.tint(ComposeAppTheme.colors.jacob), - title = stringResource(R.string.Premium_PlanPro), - onCloseClick = onDismiss - ) { - Column( - modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp), - verticalArrangement = Arrangement.SpaceBetween, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Column( - verticalArrangement = Arrangement.spacedBy(10.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - SubscriptionOption( - title = stringResource(R.string.Premium_Annually), - price = "US$99 / year", - note = "($8.33 / month)", - isSelected = selectedItem.value == 0, - badgeText = "SAVE 35%", - onClick = { - selectedItem.value = 0 - } - ) - SubscriptionOption( - title = stringResource(R.string.Premium_Monthly), - price = "US$15 / month", - note = "", - isSelected = selectedItem.value == 1, - badgeText = null, - onClick = { - selectedItem.value = 1 - } - ) - } - - val bottomText = highlightText( - text = stringResource(R.string.Premium_EnjoyFirst7DaysFree_Description), - highlightPart = stringResource(R.string.Premium_EnjoyFirst7DaysFree), - color = ComposeAppTheme.colors.remus - ) - VSpacer(12.dp) - Text( - text = bottomText, - color = ComposeAppTheme.colors.grey, - style = ComposeAppTheme.typography.subhead2, - textAlign = TextAlign.Center, - modifier = Modifier.padding(horizontal = 32.dp, vertical = 12.dp) - ) - VSpacer(24.dp) - ButtonPrimaryYellow( - modifier = Modifier.fillMaxWidth(), - title = stringResource(R.string.Premium_Get7DaysFree), - onClick = { - onSubscribeClick(type) - } - ) - VSpacer(12.dp) - ButtonPrimaryTransparent( - modifier = Modifier.align(Alignment.CenterHorizontally), - title = stringResource(R.string.Premium_AddPromoCode), - onClick = { - //TODO - } - ) - VSpacer(36.dp) - } - } -} - -@Composable -fun SubscriptionOption( - title: String, - price: String, - note: String, - isSelected: Boolean, - badgeText: String?, - onClick: () -> Unit -) { - val borderColor = - if (isSelected) ComposeAppTheme.colors.jacob else ComposeAppTheme.colors.steel20 - - Box( - modifier = Modifier - .fillMaxWidth() - .border(1.dp, borderColor, shape = RoundedCornerShape(12.dp)) - .clip(RoundedCornerShape(12.dp)) - .clickable { onClick() } - .padding(horizontal = 16.dp, vertical = 12.dp) - ) { - Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { - // Title and badge - Row(verticalAlignment = Alignment.CenterVertically) { - headline1_leah(title) - if (badgeText != null) { - Spacer(modifier = Modifier.width(8.dp)) - Box( - modifier = Modifier - .background( - ComposeAppTheme.colors.remus, - shape = RoundedCornerShape(8.dp) - ) - .padding(horizontal = 6.dp, vertical = 2.dp) - ) { - Text( - text = badgeText, - color = ComposeAppTheme.colors.claude, - style = ComposeAppTheme.typography.microSB, - ) - } - } - } - - Row() { - subhead2_jacob(price) - if (note.isNotEmpty()) { - HSpacer(4.dp) - subhead2_remus(note) - } - } - } - } -} - -@Composable -private fun PlanTabs( - selectedTabIndex: Int, - onTabChange: (Int) -> Unit -) { - val tabs = PremiumPlanType.entries.toTypedArray() - - val pagerState = rememberPagerState(initialPage = selectedTabIndex) { tabs.size } - val scrollScope = rememberCoroutineScope() - - Column( - modifier = Modifier - .padding(horizontal = 16.dp) - .clip(RoundedCornerShape(12.dp)), - ) { - TabRow( - selectedTabIndex = selectedTabIndex, - backgroundColor = ComposeAppTheme.colors.transparent, // Dark background - contentColor = Color(0xFFEDD716), - indicator = { tabPositions -> - TabRowDefaults.Indicator( - Modifier - .tabIndicatorOffset(tabPositions[selectedTabIndex]) - .height(0.dp), // No indicator line - color = Color.Transparent - ) - } - ) { - tabs.forEachIndexed { index, tab -> - Tab( - selected = selectedTabIndex == index, - onClick = { - onTabChange(index) - scrollScope.launch { - pagerState.scrollToPage(index) - } - }, - modifier = Modifier.background( - brush = - if (selectedTabIndex == index) yellowGradient else steelBrush, - ), - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .height(44.dp) - .padding(vertical = 8.dp, horizontal = 16.dp) - ) { - Icon( - painter = painterResource(if (index == 0) R.drawable.prem_star_yellow_16 else R.drawable.prem_crown_yellow_16), - contentDescription = null, - tint = if (selectedTabIndex == index) ComposeAppTheme.colors.dark else ComposeAppTheme.colors.jacob, - modifier = Modifier.size(18.dp) - ) - Spacer(modifier = Modifier.width(8.dp)) - Text( - text = stringResource(tab.titleResId), - color = if (selectedTabIndex == index) ComposeAppTheme.colors.dark else ComposeAppTheme.colors.grey, - style = ComposeAppTheme.typography.captionSB - ) - } - } - } - } - - HorizontalPager( - state = pagerState, - userScrollEnabled = false - ) { page -> - when (tabs[page]) { - PremiumPlanType.ProPlan -> ProPlanItems() - PremiumPlanType.VipPlan -> VipPlanItems() - } - } - } -} - -@Composable -private fun ProPlanItems() { - Column { - FeatureItem( - icon = R.drawable.prem_search_discovery_24, - title = R.string.Premium_UpgradeFeature_3, - subtitle = R.string.Premium_UpgradeFeature_Description_3 - ) - Divider(color = ComposeAppTheme.colors.steel20) - FeatureItem( - icon = R.drawable.prem_ring_24, - title = R.string.Premium_UpgradeFeature_4, - subtitle = R.string.Premium_UpgradeFeature_Description_4 - ) - Divider(color = ComposeAppTheme.colors.steel20) - FeatureItem( - icon = R.drawable.prem_percent_24, - title = R.string.Premium_UpgradeFeature_5, - subtitle = R.string.Premium_UpgradeFeature_Description_5 - ) - Divider(color = ComposeAppTheme.colors.steel20) - FeatureItem( - icon = R.drawable.prem_outgoingraw_24, - title = R.string.Premium_UpgradeFeature_6, - subtitle = R.string.Premium_UpgradeFeature_Description_6 - ) - Divider(color = ComposeAppTheme.colors.steel20) - FeatureItem( - icon = R.drawable.prem_switch_wallet_24, - title = R.string.Premium_UpgradeFeature_7, - subtitle = R.string.Premium_UpgradeFeature_Description_7 - ) - Divider(color = ComposeAppTheme.colors.steel20) - FeatureItem( - icon = R.drawable.prem_shield_24, - title = R.string.Premium_UpgradeFeature_8, - subtitle = R.string.Premium_UpgradeFeature_Description_8 - ) - Divider(color = ComposeAppTheme.colors.steel20) - FeatureItem( - icon = R.drawable.prem_fraud_24, - title = R.string.Premium_UpgradeFeature_9, - subtitle = R.string.Premium_UpgradeFeature_Description_9 - ) - } -} - -@Composable -private fun VipPlanItems() { - Column { - FeatureItem( - icon = R.drawable.prem_vip_support_24, - title = R.string.Premium_UpgradeFeature_1, - subtitle = R.string.Premium_UpgradeFeature_Description_1, - tint = ComposeAppTheme.colors.jacob - ) - Divider(color = ComposeAppTheme.colors.steel20) - FeatureItem( - icon = R.drawable.prem_chat_support_24, - title = R.string.Premium_UpgradeFeature_2, - subtitle = R.string.Premium_UpgradeFeature_Description_2, - tint = ComposeAppTheme.colors.jacob - ) - Divider(color = ComposeAppTheme.colors.steel20) - FeatureItem( - icon = R.drawable.prem_search_discovery_24, - title = R.string.Premium_UpgradeFeature_3, - subtitle = R.string.Premium_UpgradeFeature_Description_3 - ) - Divider(color = ComposeAppTheme.colors.steel20) - FeatureItem( - icon = R.drawable.prem_ring_24, - title = R.string.Premium_UpgradeFeature_4, - subtitle = R.string.Premium_UpgradeFeature_Description_4 - ) - Divider(color = ComposeAppTheme.colors.steel20) - FeatureItem( - icon = R.drawable.prem_percent_24, - title = R.string.Premium_UpgradeFeature_5, - subtitle = R.string.Premium_UpgradeFeature_Description_5 - ) - Divider(color = ComposeAppTheme.colors.steel20) - FeatureItem( - icon = R.drawable.prem_outgoingraw_24, - title = R.string.Premium_UpgradeFeature_6, - subtitle = R.string.Premium_UpgradeFeature_Description_6 - ) - Divider(color = ComposeAppTheme.colors.steel20) - FeatureItem( - icon = R.drawable.prem_switch_wallet_24, - title = R.string.Premium_UpgradeFeature_7, - subtitle = R.string.Premium_UpgradeFeature_Description_7 - ) - Divider(color = ComposeAppTheme.colors.steel20) - FeatureItem( - icon = R.drawable.prem_shield_24, - title = R.string.Premium_UpgradeFeature_8, - subtitle = R.string.Premium_UpgradeFeature_Description_8 - ) - Divider(color = ComposeAppTheme.colors.steel20) - FeatureItem( - icon = R.drawable.prem_fraud_24, - title = R.string.Premium_UpgradeFeature_9, - subtitle = R.string.Premium_UpgradeFeature_Description_9 - ) - } -} - -@Composable -private fun FeatureItem( - icon: Int, - title: Int, - subtitle: Int, - tint: Color = ComposeAppTheme.colors.leah -) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .fillMaxWidth() - .background(ComposeAppTheme.colors.steel10) - .padding(vertical = 12.dp, horizontal = 16.dp) - ) { - Icon( - painter = painterResource(icon), - modifier = Modifier.size(24.dp), - tint = tint, - contentDescription = null - ) - HSpacer(24.dp) - Column { - subhead1_leah(stringResource(title)) - caption_grey(stringResource(subtitle)) - } - } -} - -@Preview -@Composable -private fun SelectPremiumPlanScreenPreview() { - ComposeAppTheme { - val ctx = androidx.compose.ui.platform.LocalContext.current - SelectPremiumPlanScreen(navController = NavController(ctx)) - } -} \ No newline at end of file diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/settings/main/MainSettingsScreen.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/settings/main/MainSettingsScreen.kt index cfa7f1d347..74561b196a 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/settings/main/MainSettingsScreen.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/settings/main/MainSettingsScreen.kt @@ -91,7 +91,7 @@ private fun SettingSections( PremiumBanner( onClick = { - navController.slideFromRight(R.id.premiumSubscriptionFragment) + navController.slideFromBottom(R.id.buySubscriptionFragment) } ) diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/settings/main/PremiumBanner.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/settings/main/PremiumBanner.kt index 51b3f8ef15..5273ae01c0 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/settings/main/PremiumBanner.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/settings/main/PremiumBanner.kt @@ -37,7 +37,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import io.horizontalsystems.bankwallet.R -import io.horizontalsystems.bankwallet.modules.premium.highlightText +import io.horizontalsystems.bankwallet.modules.usersubscription.ui.highlightText import io.horizontalsystems.bankwallet.ui.compose.ComposeAppTheme import io.horizontalsystems.bankwallet.ui.compose.components.VSpacer diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionChoosePlanFragment.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionChoosePlanFragment.kt deleted file mode 100644 index 8e19f73088..0000000000 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionChoosePlanFragment.kt +++ /dev/null @@ -1,110 +0,0 @@ -package io.horizontalsystems.bankwallet.modules.usersubscription - -import android.os.Parcelable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material.Scaffold -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalView -import androidx.compose.ui.unit.dp -import androidx.fragment.app.FragmentActivity -import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.navigation.NavController -import io.horizontalsystems.bankwallet.core.BaseComposeFragment -import io.horizontalsystems.bankwallet.core.setNavigationResultX -import io.horizontalsystems.bankwallet.ui.compose.ComposeAppTheme -import io.horizontalsystems.bankwallet.ui.compose.components.AppBar -import io.horizontalsystems.bankwallet.ui.compose.components.ButtonPrimaryDefault -import io.horizontalsystems.bankwallet.ui.compose.components.HsBackButton -import io.horizontalsystems.bankwallet.ui.compose.components.TextImportantWarning -import io.horizontalsystems.bankwallet.ui.compose.components.VSpacer -import io.horizontalsystems.core.SnackbarDuration -import io.horizontalsystems.core.helpers.HudHelper -import io.horizontalsystems.subscriptions.core.BasePlan -import kotlinx.coroutines.delay -import kotlinx.parcelize.Parcelize - -class BuySubscriptionChoosePlanFragment : BaseComposeFragment() { - - @Composable - override fun GetContent(navController: NavController) { - withInput(navController) { input -> - BuySubscriptionChoosePlanScreen( - navController, - requireActivity(), - input - ) - } - } - - @Parcelize - data class Input(val subscriptionId: String) : Parcelable - - @Parcelize - class Result : Parcelable -} - -@Composable -fun BuySubscriptionChoosePlanScreen( - navController: NavController, - activity: FragmentActivity, - input: BuySubscriptionChoosePlanFragment.Input -) { - val viewModel = viewModel( - factory = BuySubscriptionChoosePlanViewModel.Factory(input.subscriptionId) - ) - - val view = LocalView.current - - val uiState = viewModel.uiState - - LaunchedEffect(uiState.purchase) { - uiState.purchase?.let { purchase -> - HudHelper.showSuccessMessage(view, purchase.toString(), SnackbarDuration.LONG) - - delay(300) - - navController.setNavigationResultX(BuySubscriptionChoosePlanFragment.Result()) - navController.popBackStack() - } - } - - Scaffold( - backgroundColor = ComposeAppTheme.colors.tyler, - topBar = { - AppBar( - title = "Subscriptions", - navigationIcon = { - HsBackButton(onClick = { navController.popBackStack() }) - }, - ) - } - ) { - Column(modifier = Modifier.padding(it)) { - uiState.basePlans.forEach { basePlan -> - ButtonPrimaryDefault( - modifier = Modifier.fillMaxWidth(), - title = basePlan.stringRepresentation(), - onClick = { - viewModel.launchPurchaseFlow(basePlan.id, activity) - }, - enabled = uiState.choosePlanEnabled - ) - VSpacer(height = 12.dp) - } - - uiState.error?.let { - TextImportantWarning(text = it.message ?: it.javaClass.name) - } - } - } -} - -fun BasePlan.stringRepresentation(): String { - return pricingPhases.map { - "${it.formattedPrice}/${it.billingPeriod}" - }.joinToString(" then ") -} diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionChoosePlanViewModel.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionChoosePlanViewModel.kt index ce92646180..9dcb6f15ab 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionChoosePlanViewModel.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionChoosePlanViewModel.kt @@ -1,8 +1,6 @@ package io.horizontalsystems.bankwallet.modules.usersubscription import android.app.Activity -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import io.horizontalsystems.bankwallet.core.ViewModelUiState import io.horizontalsystems.subscriptions.core.BasePlan @@ -10,20 +8,12 @@ import io.horizontalsystems.subscriptions.core.HSPurchase import io.horizontalsystems.subscriptions.core.UserSubscriptionManager import kotlinx.coroutines.launch -class BuySubscriptionChoosePlanViewModel(private val subscriptionId: String) : ViewModelUiState() { +class BuySubscriptionChoosePlanViewModel : ViewModelUiState() { private var basePlans: List = listOf() private var purchaseInProgress = false private var purchase: HSPurchase? = null private var error: Throwable? = null - init { - viewModelScope.launch { - basePlans = UserSubscriptionManager.getBasePlans(subscriptionId) - - emitState() - } - } - override fun createState() = BuySubscriptionChoosePlanUiState( basePlans = basePlans, purchaseInProgress = purchaseInProgress, @@ -31,7 +21,19 @@ class BuySubscriptionChoosePlanViewModel(private val subscriptionId: String) : V purchase = purchase ) - fun launchPurchaseFlow(planId: String, activity: Activity) { + fun getBasePlans(subscriptionId: String) { + viewModelScope.launch { + try { + basePlans = UserSubscriptionManager.getBasePlans(subscriptionId) + emitState() + } catch (e: Throwable) { + error = e + emitState() + } + } + } + + fun launchPurchaseFlow(subscriptionId: String, planId: String, activity: Activity) { purchaseInProgress = true error = null emitState() @@ -51,12 +53,6 @@ class BuySubscriptionChoosePlanViewModel(private val subscriptionId: String) : V } } - class Factory(private val subscriptionId: String) : ViewModelProvider.Factory { - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return BuySubscriptionChoosePlanViewModel(subscriptionId) as T - } - } } data class BuySubscriptionChoosePlanUiState( diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionFragment.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionFragment.kt index 11c1e621f3..c0e882ca09 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionFragment.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionFragment.kt @@ -1,33 +1,25 @@ package io.horizontalsystems.bankwallet.modules.usersubscription import android.os.Parcelable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material.Scaffold import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController -import io.horizontalsystems.bankwallet.R +import androidx.navigation.NavType +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import androidx.navigation.navArgument import io.horizontalsystems.bankwallet.core.BaseComposeFragment -import io.horizontalsystems.bankwallet.core.setNavigationResultX -import io.horizontalsystems.bankwallet.core.slideFromRightForResult -import io.horizontalsystems.bankwallet.ui.compose.ComposeAppTheme -import io.horizontalsystems.bankwallet.ui.compose.components.AppBar -import io.horizontalsystems.bankwallet.ui.compose.components.ButtonPrimaryDefault -import io.horizontalsystems.bankwallet.ui.compose.components.HsBackButton -import io.horizontalsystems.bankwallet.ui.compose.components.VSpacer +import io.horizontalsystems.bankwallet.core.composablePopup +import io.horizontalsystems.bankwallet.modules.usersubscription.ui.PremiumPlanType +import io.horizontalsystems.bankwallet.modules.usersubscription.ui.PremiumSubscribedScreen +import io.horizontalsystems.bankwallet.modules.usersubscription.ui.SelectSubscriptionScreen import io.horizontalsystems.subscriptions.core.IPaidAction import kotlinx.parcelize.Parcelize class BuySubscriptionFragment : BaseComposeFragment() { @Composable override fun GetContent(navController: NavController) { - withInput(navController) { input -> - BuySubscriptionScreen(navController, input) - } + SubscriptionNavHost(onClose = { navController.popBackStack() }) } @Parcelize @@ -38,46 +30,37 @@ class BuySubscriptionFragment : BaseComposeFragment() { } @Composable -private fun BuySubscriptionScreen( - navController: NavController, - input: BuySubscriptionFragment.Input, +fun SubscriptionNavHost( + onClose: () -> Unit ) { - val viewModel = viewModel { - BuySubscriptionViewModel(input.action) - } - - val uiState = viewModel.uiState - - val subscriptions = uiState.subscriptions - - Scaffold( - backgroundColor = ComposeAppTheme.colors.tyler, - topBar = { - AppBar( - title = "Subscriptions", - navigationIcon = { - HsBackButton(onClick = { navController.popBackStack() }) - }, + val navHostController = rememberNavController() + NavHost( + navController = navHostController, + startDestination = "select_subscription", + ) { + composable("select_subscription") { + SelectSubscriptionScreen( + navHostController, + onCloseClick = onClose ) } - ) { - Column(modifier = Modifier.padding(it)) { - subscriptions.forEach { subscription -> - ButtonPrimaryDefault( - modifier = Modifier.fillMaxWidth(), - title = subscription.name, - onClick = { - navController.slideFromRightForResult( - R.id.buySubscriptionChoosePlanFragment, - BuySubscriptionChoosePlanFragment.Input(subscription.id) - ) { - navController.setNavigationResultX(BuySubscriptionFragment.Result()) - navController.popBackStack() - } - } - ) - VSpacer(height = 12.dp) - } + composablePopup( + "premium_subscribed_page?type={type}", + arguments = listOf( + navArgument("type") { + type = NavType.StringType + defaultValue = null + nullable = true + }, + ) + ) { navBackStackEntry -> + val type = + navBackStackEntry.arguments?.getString("type") + ?: PremiumPlanType.ProPlan.name + PremiumSubscribedScreen( + type = PremiumPlanType.valueOf(type), + onCloseClick = onClose + ) } } } diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionModel.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionModel.kt new file mode 100644 index 0000000000..8cd683dc1c --- /dev/null +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionModel.kt @@ -0,0 +1,84 @@ +package io.horizontalsystems.bankwallet.modules.usersubscription + +import io.horizontalsystems.bankwallet.R +import io.horizontalsystems.bankwallet.core.providers.Translator +import io.horizontalsystems.subscriptions.core.AddressVerification +import io.horizontalsystems.subscriptions.core.AdvancedSearch +import io.horizontalsystems.subscriptions.core.BasePlan +import io.horizontalsystems.subscriptions.core.DuressMode +import io.horizontalsystems.subscriptions.core.FavorableSwaps +import io.horizontalsystems.subscriptions.core.IPaidAction +import io.horizontalsystems.subscriptions.core.PricingPhase +import io.horizontalsystems.subscriptions.core.PrivacyMode +import io.horizontalsystems.subscriptions.core.TokenInsights +import io.horizontalsystems.subscriptions.core.Tor +import io.horizontalsystems.subscriptions.core.TradeSignals +import io.horizontalsystems.subscriptions.core.TransactionSpeedTools +import io.horizontalsystems.subscriptions.core.VIPClub +import io.horizontalsystems.subscriptions.core.VIPSupport + +object BuySubscriptionModel { + + val IPaidAction.titleStringRes: Int + get() = when (this) { + TokenInsights -> R.string.Premium_UpgradeFeature_TokenInsights + AdvancedSearch -> R.string.Premium_UpgradeFeature_AdvancedSearch + TradeSignals -> R.string.Premium_UpgradeFeature_TradeSignals + FavorableSwaps -> R.string.Premium_UpgradeFeature_FavorableSwaps + TransactionSpeedTools -> R.string.Premium_UpgradeFeature_TxSpeedTools + DuressMode -> R.string.Premium_UpgradeFeature_DuressMode + AddressVerification -> R.string.Premium_UpgradeFeature_AddressVerification + Tor -> R.string.Premium_UpgradeFeature_Tor + PrivacyMode -> R.string.Premium_UpgradeFeature_PrivacyMode + VIPSupport -> R.string.Premium_UpgradeFeature_VipSupport + VIPClub -> R.string.Premium_UpgradeFeature_VipClub + else -> throw IllegalArgumentException("Unknown IPaidAction") + } + + val IPaidAction.descriptionStringRes: Int + get() = when (this) { + TokenInsights -> R.string.Premium_UpgradeFeature_TokenInsights_Description + AdvancedSearch -> R.string.Premium_UpgradeFeature_AdvancedSearch_Description + TradeSignals -> R.string.Premium_UpgradeFeature_TradeSignals_Description + FavorableSwaps -> R.string.Premium_UpgradeFeature_FavorableSwaps_Description + TransactionSpeedTools -> R.string.Premium_UpgradeFeature_TxSpeedTools_Description + DuressMode -> R.string.Premium_UpgradeFeature_DuressMode_Description + AddressVerification -> R.string.Premium_UpgradeFeature_AddressVerification_Description + Tor -> R.string.Premium_UpgradeFeature_Tor_Description + PrivacyMode -> R.string.Premium_UpgradeFeature_PrivacyMode_Description + VIPSupport -> R.string.Premium_UpgradeFeature_VipSupport_Description + VIPClub -> R.string.Premium_UpgradeFeature_VipClub_Description + else -> throw IllegalArgumentException("Unknown IPaidAction") + } + + val IPaidAction.iconRes: Int + get() = when (this) { + TokenInsights -> R.drawable.prem_portfolio_24 + AdvancedSearch -> R.drawable.prem_search_discovery_24 + TradeSignals -> R.drawable.prem_ring_24 + FavorableSwaps -> R.drawable.prem_percent_24 + TransactionSpeedTools -> R.drawable.prem_outgoingraw_24 + DuressMode -> R.drawable.prem_duress_24 + AddressVerification -> R.drawable.prem_shield_24 + Tor -> R.drawable.prem_tor_24 + PrivacyMode -> R.drawable.prem_fraud_24 + VIPSupport -> R.drawable.prem_vip_support_24 + VIPClub -> R.drawable.prem_chat_support_24 + else -> throw IllegalArgumentException("Unknown IPaidAction") + } + + fun BasePlan.stringRepresentation(): String { + return pricingPhases.map { + "${it.formattedPrice} / ${it.period()}" + }.joinToString(" then ") + } + + //billing periods: P1M, P3M, P6M, P1Y + private fun PricingPhase.period(): String { + return when (billingPeriod) { + "P1M" -> Translator.getString(R.string.Premium_SubscriptionPeriod_Month) + "P1Y" -> Translator.getString(R.string.Premium_SubscriptionPeriod_Year) + else -> billingPeriod + } + } +} diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionViewModel.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionViewModel.kt index 47efd5c2c9..a9e2501773 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionViewModel.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/BuySubscriptionViewModel.kt @@ -2,12 +2,11 @@ package io.horizontalsystems.bankwallet.modules.usersubscription import androidx.lifecycle.viewModelScope import io.horizontalsystems.bankwallet.core.ViewModelUiState -import io.horizontalsystems.subscriptions.core.IPaidAction import io.horizontalsystems.subscriptions.core.Subscription import io.horizontalsystems.subscriptions.core.UserSubscriptionManager import kotlinx.coroutines.launch -class BuySubscriptionViewModel(action: IPaidAction) : ViewModelUiState() { +class BuySubscriptionViewModel : ViewModelUiState() { private var subscriptions = listOf() init { diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/premium/PremiumSubscribed.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/ui/PremiumSubscribedScreen.kt similarity index 59% rename from app/src/main/java/io/horizontalsystems/bankwallet/modules/premium/PremiumSubscribed.kt rename to app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/ui/PremiumSubscribedScreen.kt index 4b8cfe4e08..804858f662 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/premium/PremiumSubscribed.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/ui/PremiumSubscribedScreen.kt @@ -1,18 +1,27 @@ -package io.horizontalsystems.bankwallet.modules.premium +package io.horizontalsystems.bankwallet.modules.usersubscription.ui import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll +import androidx.compose.material.Divider +import androidx.compose.material.Icon import androidx.compose.material.Scaffold import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign @@ -20,8 +29,11 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.horizontalsystems.bankwallet.R import io.horizontalsystems.bankwallet.ui.compose.ComposeAppTheme +import io.horizontalsystems.bankwallet.ui.compose.components.HSpacer import io.horizontalsystems.bankwallet.ui.compose.components.RadialBackground import io.horizontalsystems.bankwallet.ui.compose.components.VSpacer +import io.horizontalsystems.bankwallet.ui.compose.components.body_leah +import io.horizontalsystems.bankwallet.ui.compose.components.headline1_leah @Composable fun PremiumSubscribedScreen( @@ -60,10 +72,8 @@ fun PremiumSubscribedScreen( .fillMaxWidth() ) VSpacer(24.dp) - Text( + headline1_leah( text = stringResource(R.string.Premium_ThankYouForSubscription), - style = ComposeAppTheme.typography.headline1, - color = ComposeAppTheme.colors.white, textAlign = TextAlign.Center, modifier = Modifier .fillMaxWidth() @@ -87,12 +97,35 @@ fun PremiumSubscribedScreen( .fillMaxWidth() .padding(horizontal = 32.dp) ) + if (type == PremiumPlanType.VipPlan) { + VSpacer(24.dp) + Column( + modifier = Modifier + .padding(horizontal = 16.dp) + .clip(RoundedCornerShape(12.dp)) + .background(ComposeAppTheme.colors.steel10), + ) { + VipItem( + icon = R.drawable.prem_vip_support_24, + title = R.string.Premium_UpgradeFeature_VipSupport, + ) { + //todo + } + Divider(color = ComposeAppTheme.colors.steel20) + VipItem( + icon = R.drawable.prem_chat_support_24, + title = R.string.Premium_UpgradeFeature_VipClub, + ) { + //todo + } + } + } VSpacer(24.dp) } Column( Modifier .padding(horizontal = 24.dp) - .padding(bottom = 56.dp) + .padding(bottom = 32.dp) ) { ButtonPrimaryCustomColor( modifier = Modifier.fillMaxWidth(), @@ -106,9 +139,42 @@ fun PremiumSubscribedScreen( } } +@Composable +fun VipItem( + icon: Int, + title: Int, + click: () -> Unit = {} +) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .clickable { click() } + .background(ComposeAppTheme.colors.steel10) + .padding(vertical = 12.dp, horizontal = 16.dp) + ) { + Icon( + painter = painterResource(icon), + modifier = Modifier.size(24.dp), + tint = ComposeAppTheme.colors.jacob, + contentDescription = null + ) + HSpacer(16.dp) + body_leah( + text = stringResource(title), + modifier = Modifier.weight(1f) + ) + Image( + modifier = Modifier.size(20.dp), + painter = painterResource(id = R.drawable.ic_arrow_right), + contentDescription = null, + ) + } +} + @Preview @Composable -fun PremiumSubscribedPreview() { +fun PremiumSubscribedScreenPreview() { ComposeAppTheme { PremiumSubscribedScreen( type = PremiumPlanType.ProPlan, diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/ui/SelectSubscriptionScreen.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/ui/SelectSubscriptionScreen.kt new file mode 100644 index 0000000000..b7811fb49f --- /dev/null +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/ui/SelectSubscriptionScreen.kt @@ -0,0 +1,170 @@ +package io.horizontalsystems.bankwallet.modules.usersubscription.ui + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.Scaffold +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavController +import io.horizontalsystems.bankwallet.R +import io.horizontalsystems.bankwallet.modules.evmfee.ButtonsGroupWithShade +import io.horizontalsystems.bankwallet.modules.usersubscription.BuySubscriptionViewModel +import io.horizontalsystems.bankwallet.ui.compose.ComposeAppTheme +import io.horizontalsystems.bankwallet.ui.compose.components.RadialBackground +import io.horizontalsystems.bankwallet.ui.compose.components.VSpacer +import io.horizontalsystems.bankwallet.ui.compose.components.body_grey +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SelectSubscriptionScreen( + navController: NavController, + onCloseClick: () -> Unit +) { + val viewModel = viewModel { + BuySubscriptionViewModel() + } + + val uiState = viewModel.uiState + + val subscriptions = uiState.subscriptions + + val coroutineScope = rememberCoroutineScope() + val selectedTabIndex = remember { mutableStateOf(0) } + val modalBottomSheetState = + androidx.compose.material3.rememberModalBottomSheetState(skipPartiallyExpanded = true) + var isBottomSheetVisible by remember { mutableStateOf(false) } + + Scaffold( + backgroundColor = ComposeAppTheme.colors.tyler, + topBar = { + TitleCenteredTopBar( + title = stringResource(R.string.Premium_Title), + onCloseClick = onCloseClick + ) + } + ) { paddingValues -> + Box( + modifier = Modifier + .padding(paddingValues) + .fillMaxSize() + ) { + RadialBackground() + Column { + Column( + modifier = Modifier + .padding(paddingValues) + .weight(1f) + .verticalScroll(rememberScrollState()) + ) + { + VSpacer(12.dp) + body_grey( + text = stringResource(R.string.Premium_ChoosePlanForYou), + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center + ) + VSpacer(24.dp) + + if (subscriptions.isNotEmpty()) { + PlanTabs( + items = subscriptions, + selectedTabIndex = selectedTabIndex.value, + onTabChange = { index -> + selectedTabIndex.value = index + } + ) + VSpacer(32.dp) + } + } + + ButtonsGroupWithShade { + Column( + modifier = Modifier.padding(horizontal = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + ButtonPrimaryCustomColor( + modifier = Modifier.fillMaxWidth(), + title = stringResource(R.string.Premium_TryForFree), + brush = yellowGradient, + onClick = { + coroutineScope.launch { + isBottomSheetVisible = true + modalBottomSheetState.show() + } + }, + ) + VSpacer(12.dp) + ColoredTextSecondaryButton( + title = stringResource(R.string.Premium_Restore), + onClick = { + // + }, + color = ComposeAppTheme.colors.leah + ) + } + } + } + } + if (isBottomSheetVisible) { + ModalBottomSheet( + onDismissRequest = { + coroutineScope.launch { + modalBottomSheetState.hide() + } + isBottomSheetVisible = false + }, + sheetState = modalBottomSheetState, + containerColor = ComposeAppTheme.colors.transparent + ) { + if (subscriptions.isNotEmpty()) { + SelectSubscriptionBottomSheet( + subscriptionId = subscriptions[selectedTabIndex.value].id, + type = PremiumPlanType.entries[selectedTabIndex.value], + onDismiss = { + coroutineScope.launch { + modalBottomSheetState.hide() + isBottomSheetVisible = false + } + }, + onSubscribeClick = { type -> + coroutineScope.launch { + modalBottomSheetState.hide() + isBottomSheetVisible = false + navController.navigate("premium_subscribed_page?type=${type.name}") + } + } + ) + } + } + } + } +} + +@Preview +@Composable +private fun SelectSubscriptionScreenPreview() { + ComposeAppTheme { + val ctx = LocalContext.current + SelectSubscriptionScreen(navController = NavController(ctx)) {} + } +} \ No newline at end of file diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/ui/UiElements.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/ui/UiElements.kt new file mode 100644 index 0000000000..fbe3b47c6c --- /dev/null +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/usersubscription/ui/UiElements.kt @@ -0,0 +1,480 @@ +package io.horizontalsystems.bankwallet.modules.usersubscription.ui + +import androidx.annotation.StringRes +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Divider +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.Icon +import androidx.compose.material.ProvideTextStyle +import androidx.compose.material.Surface +import androidx.compose.material.Tab +import androidx.compose.material.TabRow +import androidx.compose.material.TabRowDefaults +import androidx.compose.material.TabRowDefaults.tabIndicatorOffset +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import io.horizontalsystems.bankwallet.R +import io.horizontalsystems.bankwallet.modules.usersubscription.BuySubscriptionChoosePlanViewModel +import io.horizontalsystems.bankwallet.modules.usersubscription.BuySubscriptionModel.descriptionStringRes +import io.horizontalsystems.bankwallet.modules.usersubscription.BuySubscriptionModel.iconRes +import io.horizontalsystems.bankwallet.modules.usersubscription.BuySubscriptionModel.stringRepresentation +import io.horizontalsystems.bankwallet.modules.usersubscription.BuySubscriptionModel.titleStringRes +import io.horizontalsystems.bankwallet.ui.compose.ComposeAppTheme +import io.horizontalsystems.bankwallet.ui.compose.Steel20 +import io.horizontalsystems.bankwallet.ui.compose.components.ButtonPrimaryDefaults +import io.horizontalsystems.bankwallet.ui.compose.components.ButtonPrimaryTransparent +import io.horizontalsystems.bankwallet.ui.compose.components.ButtonPrimaryYellow +import io.horizontalsystems.bankwallet.ui.compose.components.ButtonSecondary +import io.horizontalsystems.bankwallet.ui.compose.components.HSpacer +import io.horizontalsystems.bankwallet.ui.compose.components.HsIconButton +import io.horizontalsystems.bankwallet.ui.compose.components.SecondaryButtonDefaults.buttonColors +import io.horizontalsystems.bankwallet.ui.compose.components.VSpacer +import io.horizontalsystems.bankwallet.ui.compose.components.caption_grey +import io.horizontalsystems.bankwallet.ui.compose.components.headline1_leah +import io.horizontalsystems.bankwallet.ui.compose.components.subhead1_leah +import io.horizontalsystems.bankwallet.ui.compose.components.subhead2_jacob +import io.horizontalsystems.bankwallet.ui.compose.components.subhead2_remus +import io.horizontalsystems.bankwallet.ui.extensions.BottomSheetHeader +import io.horizontalsystems.subscriptions.core.IPaidAction +import io.horizontalsystems.subscriptions.core.Subscription +import io.horizontalsystems.subscriptions.core.VIPClub +import io.horizontalsystems.subscriptions.core.VIPSupport +import kotlinx.coroutines.launch + +enum class PremiumPlanType(@StringRes val titleResId: Int) { + ProPlan(R.string.Premium_PlanPro), + VipPlan(R.string.Premium_PlanVip); +} + +val yellowGradient = Brush.horizontalGradient( + colors = listOf( + Color(0xFFFFD000), + Color(0xFFFFA800), + ) +) + +private val steelBrush = Brush.horizontalGradient( + colors = listOf(Steel20, Steel20) +) + +@Composable +fun SelectSubscriptionBottomSheet( + subscriptionId: String, + type: PremiumPlanType, + onDismiss: () -> Unit, + viewModel: BuySubscriptionChoosePlanViewModel = viewModel(), + onSubscribeClick: (PremiumPlanType) -> Unit +) { + + LaunchedEffect(Unit) { + viewModel.getBasePlans(subscriptionId) + } + + val uiState = viewModel.uiState + + val selectedItemIndex = remember { mutableIntStateOf(0) } + BottomSheetHeader( + iconPainter = painterResource(R.drawable.prem_star_yellow_16), + iconTint = ColorFilter.tint(ComposeAppTheme.colors.jacob), + title = stringResource(type.titleResId), + onCloseClick = onDismiss + ) { + Column( + modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp), + verticalArrangement = Arrangement.SpaceBetween, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Column( + verticalArrangement = Arrangement.spacedBy(10.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + uiState.basePlans.forEachIndexed() { index, basePlan -> + SubscriptionOption( + title = basePlan.id, + price = basePlan.stringRepresentation(), + note = "", + isSelected = selectedItemIndex.intValue == index, + badgeText = null, + onClick = { + selectedItemIndex.intValue = index + } + ) + } + } + + val bottomText = highlightText( + text = stringResource(R.string.Premium_EnjoyFirst7DaysFree_Description), + highlightPart = stringResource(R.string.Premium_EnjoyFirst7DaysFree), + color = ComposeAppTheme.colors.remus + ) + VSpacer(12.dp) + Text( + text = bottomText, + color = ComposeAppTheme.colors.grey, + style = ComposeAppTheme.typography.subhead2, + textAlign = TextAlign.Center, + modifier = Modifier.padding(horizontal = 32.dp, vertical = 12.dp) + ) + VSpacer(24.dp) + ButtonPrimaryYellow( + modifier = Modifier.fillMaxWidth(), + title = stringResource(R.string.Premium_Get7DaysFree), + onClick = { + onSubscribeClick(type) + } + ) + VSpacer(12.dp) + ButtonPrimaryTransparent( + modifier = Modifier.align(Alignment.CenterHorizontally), + title = stringResource(R.string.Premium_AddPromoCode), + onClick = { + //TODO + } + ) + VSpacer(36.dp) + } + } +} + +@Composable +fun SubscriptionOption( + title: String, + price: String, + note: String, + isSelected: Boolean, + badgeText: String?, + onClick: () -> Unit +) { + val borderColor = + if (isSelected) ComposeAppTheme.colors.jacob else ComposeAppTheme.colors.steel20 + + Box( + modifier = Modifier + .fillMaxWidth() + .border(1.dp, borderColor, shape = RoundedCornerShape(12.dp)) + .clip(RoundedCornerShape(12.dp)) + .clickable { onClick() } + .padding(horizontal = 16.dp, vertical = 12.dp) + ) { + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + Row(verticalAlignment = Alignment.CenterVertically) { + headline1_leah(title) + if (badgeText != null) { + Spacer(modifier = Modifier.width(8.dp)) + Box( + modifier = Modifier + .background( + ComposeAppTheme.colors.remus, + shape = RoundedCornerShape(8.dp) + ) + .padding(horizontal = 6.dp, vertical = 2.dp) + ) { + Text( + text = badgeText, + color = ComposeAppTheme.colors.claude, + style = ComposeAppTheme.typography.microSB, + ) + } + } + } + + Row() { + subhead2_jacob(price) + if (note.isNotEmpty()) { + HSpacer(4.dp) + subhead2_remus(note) + } + } + } + } +} + +@Composable +fun PlanTabs( + items: List, + selectedTabIndex: Int, + onTabChange: (Int) -> Unit +) { + + val pagerState = rememberPagerState(initialPage = selectedTabIndex) { 2 } + val scrollScope = rememberCoroutineScope() + + Column( + modifier = Modifier + .padding(horizontal = 16.dp) + .clip(RoundedCornerShape(12.dp)), + ) { + TabRow( + selectedTabIndex = selectedTabIndex, + backgroundColor = ComposeAppTheme.colors.transparent, // Dark background + contentColor = Color(0xFFEDD716), + indicator = { tabPositions -> + TabRowDefaults.Indicator( + Modifier + .tabIndicatorOffset(tabPositions[selectedTabIndex]) + .height(0.dp), // No indicator line + color = Color.Transparent + ) + } + ) { + items.forEachIndexed { index, tab -> + Tab( + selected = selectedTabIndex == index, + onClick = { + onTabChange(index) + scrollScope.launch { + pagerState.scrollToPage(index) + } + }, + modifier = Modifier.background( + brush = + if (selectedTabIndex == index) yellowGradient else steelBrush, + ), + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .height(44.dp) + .padding(vertical = 8.dp, horizontal = 16.dp) + ) { + Icon( + painter = painterResource(if (index == 0) R.drawable.prem_star_yellow_16 else R.drawable.prem_crown_yellow_16), + contentDescription = null, + tint = if (selectedTabIndex == index) ComposeAppTheme.colors.dark else ComposeAppTheme.colors.jacob, + modifier = Modifier.size(18.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = tab.name, + color = if (selectedTabIndex == index) ComposeAppTheme.colors.dark else ComposeAppTheme.colors.grey, + style = ComposeAppTheme.typography.captionSB + ) + } + } + } + } + + HorizontalPager( + state = pagerState, + userScrollEnabled = false + ) { page -> + PlanItems(items[page].actions) + } + } +} + +@Composable +private fun PlanItems(items: List) { + Column { + items.forEachIndexed { index, item -> + PremiumFeatureItem( + icon = item.iconRes, + title = item.titleStringRes, + subtitle = item.descriptionStringRes, + tint = if (item is VIPClub || item is VIPSupport) ComposeAppTheme.colors.jacob else ComposeAppTheme.colors.leah + ) + if (index < items.size - 1) { + Divider(color = ComposeAppTheme.colors.steel20) + } + } + } +} + +@Composable +fun PremiumFeatureItem( + icon: Int, + title: Int, + subtitle: Int, + tint: Color = ComposeAppTheme.colors.leah, + click: () -> Unit = {} +) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .background(ComposeAppTheme.colors.steel10) + .padding(vertical = 12.dp, horizontal = 16.dp) + ) { + Icon( + painter = painterResource(icon), + modifier = Modifier.size(24.dp), + tint = tint, + contentDescription = null + ) + HSpacer(16.dp) + Column { + subhead1_leah(stringResource(title)) + caption_grey(stringResource(subtitle)) + } + } +} + +@Composable +fun highlightText( + text: String, + highlightPart: String, + color: Color +): AnnotatedString { + + return buildAnnotatedString { + val highlightIndex = text + .lowercase() + .indexOf(highlightPart.lowercase()) + + if (highlightIndex != -1) { + append(text.substring(0, highlightIndex)) + + withStyle( + SpanStyle(color = color) + ) { + append( + text.substring( + highlightIndex, + highlightIndex + highlightPart.length + ) + ) + } + + append( + text.substring(highlightIndex + highlightPart.length) + ) + } else { + append(text) + } + } +} + +@OptIn(ExperimentalMaterialApi::class) +@Composable +fun ButtonPrimaryCustomColor( + modifier: Modifier = Modifier, + title: String, + brush: Brush, + onClick: () -> Unit, + enabled: Boolean = true, + contentPadding: PaddingValues = ButtonPrimaryDefaults.ContentPadding, +) { + Surface( + modifier = modifier + .clip(RoundedCornerShape(25.dp)) + .background(brush), + shape = RoundedCornerShape(25.dp), + color = Color.Transparent, + contentColor = ComposeAppTheme.colors.dark, + onClick = onClick, + enabled = enabled, + ) { + ProvideTextStyle( + value = ComposeAppTheme.typography.headline2 + ) { + Row( + Modifier + .defaultMinSize( + minWidth = ButtonPrimaryDefaults.MinWidth, + minHeight = ButtonPrimaryDefaults.MinHeight + ) + .padding(contentPadding), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + title, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } + } + } +} + +@Composable +fun TitleCenteredTopBar( + title: String, + modifier: Modifier = Modifier, + onCloseClick: () -> Unit +) { + Box( + modifier = modifier + .height(64.dp) + .fillMaxWidth(), + ) { + headline1_leah( + text = title, + modifier = Modifier.align(Alignment.Center) + ) + HsIconButton( + modifier = Modifier.align(Alignment.CenterEnd), + onClick = onCloseClick + ) { + Icon( + painter = painterResource(id = R.drawable.ic_close), + contentDescription = "close button", + tint = ComposeAppTheme.colors.jacob, + ) + } + } +} + +@Composable +fun ColoredTextSecondaryButton( + title: String, + color: Color, + modifier: Modifier = Modifier, + onClick: () -> Unit +) { + ButtonSecondary( + modifier = modifier, + onClick = onClick, + buttonColors = buttonColors( + backgroundColor = ComposeAppTheme.colors.transparent, + contentColor = color, + disabledBackgroundColor = ComposeAppTheme.colors.transparent, + disabledContentColor = ComposeAppTheme.colors.grey50, + ), + content = { + Text( + text = title, + maxLines = 1, + style = ComposeAppTheme.typography.body, + overflow = TextOverflow.Ellipsis, + ) + }, + ) +} diff --git a/app/src/main/res/drawable/prem_duress_24.xml b/app/src/main/res/drawable/prem_duress_24.xml new file mode 100644 index 0000000000..cf9922a65c --- /dev/null +++ b/app/src/main/res/drawable/prem_duress_24.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/prem_portfolio_24.xml b/app/src/main/res/drawable/prem_portfolio_24.xml new file mode 100644 index 0000000000..dcbe3564e0 --- /dev/null +++ b/app/src/main/res/drawable/prem_portfolio_24.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/drawable/prem_tor_24.xml b/app/src/main/res/drawable/prem_tor_24.xml new file mode 100644 index 0000000000..86d3697219 --- /dev/null +++ b/app/src/main/res/drawable/prem_tor_24.xml @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/app/src/main/res/navigation/main_graph.xml b/app/src/main/res/navigation/main_graph.xml index f1823fd65b..5f2cd355ad 100644 --- a/app/src/main/res/navigation/main_graph.xml +++ b/app/src/main/res/navigation/main_graph.xml @@ -403,10 +403,4 @@ - - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 789c9789ab..05e4100156 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -889,25 +889,32 @@ You have activated the %s plan. Go to App - Vip Support - Vip Chat Group - Token Insights - Trade Signals - Favorable Swaps - Transaction Speed Tools - Duress Mode - Address Verification - Privacy Mode - - Fast response support. - Private chat with exclusive information. - Full analytics and data about tokens. - Signals for confident trading. - Swaps with no extra fees. - Speed up and cancel transactions. - Hides main wallets in case of coercion. - Additional address check for fraud prevention. - Full disconnection from data collection. + VIP Support + VIP Club + Token Insights + Advanced Search + Trade Signals + Favorable Swaps + Transaction Speed Tools + Duress Mode + Address Verification + TOR + Privacy Mode + + Fast response support. + Private chat with exclusive information. + Full analytics and data about tokens. + Custom filters for token searches. + Signals for confident trading. + Swaps with no extra fees. + Speed up and cancel transactions. + Hides main wallets in case of coercion. + Additional address check for fraud prevention. + Secure connection. + Full disconnection from data collection. + + month + year MARKETS TAB BALANCE TAB diff --git a/subscriptions-core/src/main/java/io/horizontalsystems/subscriptions/core/IPaidAction.kt b/subscriptions-core/src/main/java/io/horizontalsystems/subscriptions/core/IPaidAction.kt index e5b3b3ad10..78fd8b09e9 100644 --- a/subscriptions-core/src/main/java/io/horizontalsystems/subscriptions/core/IPaidAction.kt +++ b/subscriptions-core/src/main/java/io/horizontalsystems/subscriptions/core/IPaidAction.kt @@ -6,4 +6,34 @@ import kotlinx.parcelize.Parcelize interface IPaidAction : Parcelable @Parcelize -object EnableWatchlistSignals : IPaidAction +object TokenInsights : IPaidAction + +@Parcelize +object AdvancedSearch : IPaidAction + +@Parcelize +object TradeSignals : IPaidAction + +@Parcelize +object FavorableSwaps : IPaidAction + +@Parcelize +object TransactionSpeedTools : IPaidAction + +@Parcelize +object DuressMode : IPaidAction + +@Parcelize +object AddressVerification : IPaidAction + +@Parcelize +object Tor : IPaidAction + +@Parcelize +object PrivacyMode : IPaidAction + +@Parcelize +object VIPSupport : IPaidAction + +@Parcelize +object VIPClub : IPaidAction diff --git a/subscriptions-core/src/main/java/io/horizontalsystems/subscriptions/core/UserSubscriptionManager.kt b/subscriptions-core/src/main/java/io/horizontalsystems/subscriptions/core/UserSubscriptionManager.kt index b6a40c1d1c..a999da62d2 100644 --- a/subscriptions-core/src/main/java/io/horizontalsystems/subscriptions/core/UserSubscriptionManager.kt +++ b/subscriptions-core/src/main/java/io/horizontalsystems/subscriptions/core/UserSubscriptionManager.kt @@ -6,15 +6,37 @@ object UserSubscriptionManager { private val predefinedSubscriptions = listOf( Subscription( id = "test.subscription_1", - name = "Test Subscription #1", + name = "PRO", description = "", - actions = listOf(EnableWatchlistSignals) + actions = listOf( + TokenInsights, + AdvancedSearch, + TradeSignals, + FavorableSwaps, + TransactionSpeedTools, + DuressMode, + AddressVerification, + Tor, + PrivacyMode, + ) ), Subscription( id = "test.subscription_2", - name = "Test Subscription #2", + name = "VIP", description = "", - actions = listOf() + actions = listOf( + VIPSupport, + VIPClub, + TokenInsights, + AdvancedSearch, + TradeSignals, + FavorableSwaps, + TransactionSpeedTools, + DuressMode, + AddressVerification, + Tor, + PrivacyMode, + ) ), ) private lateinit var service: SubscriptionService @@ -24,7 +46,7 @@ object UserSubscriptionManager { UserSubscriptionManager.service = service } - fun isActionAllowed(paidAction: IPaidAction) : Boolean { + fun isActionAllowed(paidAction: IPaidAction): Boolean { return service.isActionAllowed(paidAction) } @@ -32,7 +54,11 @@ object UserSubscriptionManager { return service.getSubscriptions() } - suspend fun launchPurchaseFlow(subscriptionId: String, planId: String, activity: Activity): HSPurchase? { + suspend fun launchPurchaseFlow( + subscriptionId: String, + planId: String, + activity: Activity + ): HSPurchase? { return service.launchPurchaseFlow(subscriptionId, planId, activity) }