mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-06 07:47:54 +00:00
android: close the badge purchase screens in one step; ios, android: drop the order screen spinner
This commit is contained in:
@@ -16,7 +16,6 @@ struct BadgesCheckOrderView: View {
|
||||
let level: BadgeLevel
|
||||
let period: BadgePeriod
|
||||
@State private var purchasing = false
|
||||
@State private var progressByTimeout = false
|
||||
// presented from this view, not AlertManager: its host is behind the sheet these views open in
|
||||
@State private var alert: SomeAlert?
|
||||
|
||||
@@ -60,20 +59,6 @@ struct BadgesCheckOrderView: View {
|
||||
.frame(minHeight: g.size.height)
|
||||
}
|
||||
}
|
||||
.overlay {
|
||||
if progressByTimeout {
|
||||
ProgressView().scaleEffect(2)
|
||||
}
|
||||
}
|
||||
.onChange(of: purchasing) { _ in
|
||||
if purchasing {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
progressByTimeout = purchasing
|
||||
}
|
||||
} else {
|
||||
progressByTimeout = false
|
||||
}
|
||||
}
|
||||
.frame(maxHeight: .infinity)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.task { await store.load() }
|
||||
|
||||
+2
-29
@@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
@@ -19,24 +18,13 @@ import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.onboarding.OnboardingActionButton
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
@Composable
|
||||
fun BadgesCheckOrderView(level: BadgeLevel, period: BadgePeriod, modalManager: ModalManager) {
|
||||
fun BadgesCheckOrderView(level: BadgeLevel, period: BadgePeriod, modalManager: ModalManager, unwindToDepth: Int) {
|
||||
val purchasing = remember { mutableStateOf(false) }
|
||||
var progressByTimeout by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(purchasing.value) {
|
||||
progressByTimeout = if (purchasing.value) {
|
||||
delay(1000)
|
||||
purchasing.value
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) { BadgeStore.load() }
|
||||
CloseWhenSupportGivesWay(modalManager)
|
||||
CloseWhenSupportGivesWay(modalManager, unwindToDepth)
|
||||
|
||||
ColumnWithScrollBar(
|
||||
Modifier.padding(horizontal = 25.dp).padding(top = 8.dp, bottom = 20.dp),
|
||||
@@ -74,21 +62,6 @@ fun BadgesCheckOrderView(level: BadgeLevel, period: BadgePeriod, modalManager: M
|
||||
BadgeBillingFooter(period)
|
||||
}
|
||||
}
|
||||
|
||||
if (progressByTimeout) {
|
||||
Box(
|
||||
Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
Modifier
|
||||
.padding(horizontal = 2.dp)
|
||||
.size(30.dp),
|
||||
color = MaterialTheme.colors.secondary,
|
||||
strokeWidth = 3.dp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
+6
-6
@@ -60,11 +60,11 @@ enum class BadgeLevel {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BadgesChooseBadgeView(modalManager: ModalManager) {
|
||||
fun BadgesChooseBadgeView(modalManager: ModalManager, unwindToDepth: Int) {
|
||||
var selectedLevel by remember { mutableStateOf(BadgeLevel.Supporter) }
|
||||
|
||||
LaunchedEffect(Unit) { BadgeStore.load() }
|
||||
CloseWhenSupportGivesWay(modalManager)
|
||||
CloseWhenSupportGivesWay(modalManager, unwindToDepth)
|
||||
|
||||
ColumnWithScrollBar(
|
||||
Modifier.background(MaterialTheme.colors.background).padding(horizontal = 25.dp).padding(top = 8.dp, bottom = 20.dp),
|
||||
@@ -108,10 +108,10 @@ fun BadgesChooseBadgeView(modalManager: ModalManager) {
|
||||
// Nested Column with no spacing so the TextButtonBelowOnboardingButton sits directly under
|
||||
// the action button (matches onboarding pattern where its own 7.5dp top padding is the gap).
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
ContinueButton(selectedLevel, modalManager)
|
||||
ContinueButton(selectedLevel, modalManager, unwindToDepth)
|
||||
// redeeming a code is here only when Support SimpleX offers the browser instead
|
||||
if (badgeBrowserAllowed()) {
|
||||
RedeemCodeButton(modalManager)
|
||||
RedeemCodeButton(modalManager, unwindToDepth)
|
||||
} else {
|
||||
TextButtonBelowOnboardingButton("", null)
|
||||
}
|
||||
@@ -153,13 +153,13 @@ private fun LevelCard(level: BadgeLevel, selectedLevel: BadgeLevel, modifier: Mo
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ContinueButton(selectedLevel: BadgeLevel, modalManager: ModalManager) {
|
||||
private fun ContinueButton(selectedLevel: BadgeLevel, modalManager: ModalManager, unwindToDepth: Int) {
|
||||
OnboardingActionButton(
|
||||
modifier = if (appPlatform.isAndroid) Modifier.padding(horizontal = DEFAULT_ONBOARDING_HORIZONTAL_PADDING).fillMaxWidth() else Modifier.widthIn(min = 300.dp),
|
||||
labelId = MR.strings.badges_continue,
|
||||
onboarding = null,
|
||||
onclick = {
|
||||
modalManager.showModal { BadgesHowLongView(selectedLevel, modalManager) }
|
||||
modalManager.showModal { BadgesHowLongView(selectedLevel, modalManager, unwindToDepth) }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
+5
-5
@@ -74,11 +74,11 @@ enum class BadgePeriod {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BadgesHowLongView(level: BadgeLevel, modalManager: ModalManager) {
|
||||
fun BadgesHowLongView(level: BadgeLevel, modalManager: ModalManager, unwindToDepth: Int) {
|
||||
var selectedPeriod by remember { mutableStateOf(if (BadgePeriod.Monthly in badgePeriodsForSale) BadgePeriod.Monthly else BadgePeriod.OneMonth) }
|
||||
|
||||
LaunchedEffect(Unit) { BadgeStore.load() }
|
||||
CloseWhenSupportGivesWay(modalManager)
|
||||
CloseWhenSupportGivesWay(modalManager, unwindToDepth)
|
||||
|
||||
ColumnWithScrollBar(
|
||||
Modifier.background(MaterialTheme.colors.background).padding(horizontal = 25.dp).padding(top = 8.dp, bottom = 20.dp),
|
||||
@@ -121,7 +121,7 @@ fun BadgesHowLongView(level: BadgeLevel, modalManager: ModalManager) {
|
||||
Spacer(Modifier.weight(1f).heightIn(min = 8.dp))
|
||||
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
ContinueButton(level, selectedPeriod, modalManager)
|
||||
ContinueButton(level, selectedPeriod, modalManager, unwindToDepth)
|
||||
BadgeBillingFooter(selectedPeriod)
|
||||
}
|
||||
}
|
||||
@@ -186,13 +186,13 @@ private fun savingsPercent(level: BadgeLevel, period: BadgePeriod): Int? =
|
||||
if (period == BadgePeriod.Annual) BadgeStore.annualSavings(level) else null
|
||||
|
||||
@Composable
|
||||
private fun ContinueButton(level: BadgeLevel, selectedPeriod: BadgePeriod, modalManager: ModalManager) {
|
||||
private fun ContinueButton(level: BadgeLevel, selectedPeriod: BadgePeriod, modalManager: ModalManager, unwindToDepth: Int) {
|
||||
OnboardingActionButton(
|
||||
modifier = if (appPlatform.isAndroid) Modifier.padding(horizontal = DEFAULT_ONBOARDING_HORIZONTAL_PADDING).fillMaxWidth() else Modifier.widthIn(min = 300.dp),
|
||||
labelId = MR.strings.badges_continue,
|
||||
onboarding = null,
|
||||
onclick = {
|
||||
modalManager.showModal(cardScreen = true) { BadgesCheckOrderView(level, selectedPeriod, modalManager) }
|
||||
modalManager.showModal(cardScreen = true) { BadgesCheckOrderView(level, selectedPeriod, modalManager, unwindToDepth) }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
+2
-2
@@ -94,12 +94,12 @@ fun showCannotRedeemAlert(message: String) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BadgesRedeemCodeView(modalManager: ModalManager) {
|
||||
fun BadgesRedeemCodeView(modalManager: ModalManager, unwindToDepth: Int) {
|
||||
val rhId = remember { chatModel.remoteHostId() }
|
||||
val code = remember { mutableStateOf(TextFieldValue("")) }
|
||||
val canonicalCode = remember { mutableStateOf<String?>(null) }
|
||||
val submitting = remember { mutableStateOf(false) }
|
||||
CloseWhenSupportGivesWay(modalManager)
|
||||
CloseWhenSupportGivesWay(modalManager, unwindToDepth)
|
||||
|
||||
// when the text is unchanged, the field's own value is kept: it carries the cursor position and the
|
||||
// keyboard's composition state, which BasicTextField loses unless they are passed back to it
|
||||
|
||||
+11
-11
@@ -32,7 +32,7 @@ import chat.simplex.common.views.onboarding.TextButtonBelowOnboardingButton
|
||||
import chat.simplex.res.MR
|
||||
|
||||
@Composable
|
||||
fun BadgesSupportSimplexView(modalManager: ModalManager) {
|
||||
fun BadgesSupportSimplexView(modalManager: ModalManager, unwindToDepth: Int) {
|
||||
ColumnWithScrollBar(
|
||||
Modifier.background(MaterialTheme.colors.background).padding(horizontal = 25.dp).padding(top = 8.dp, bottom = 20.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
@@ -65,29 +65,29 @@ fun BadgesSupportSimplexView(modalManager: ModalManager) {
|
||||
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
if (badgeStoreAvailable) {
|
||||
ChooseLevelButton(modalManager)
|
||||
ChooseLevelButton(modalManager, unwindToDepth)
|
||||
if (badgeBrowserAllowed()) {
|
||||
BuyInBrowserTextButton()
|
||||
} else {
|
||||
RedeemCodeButton(modalManager)
|
||||
RedeemCodeButton(modalManager, unwindToDepth)
|
||||
}
|
||||
} else {
|
||||
BuyInBrowserButton(modalManager)
|
||||
RedeemCodeButton(modalManager)
|
||||
BuyInBrowserButton(modalManager, unwindToDepth)
|
||||
RedeemCodeButton(modalManager, unwindToDepth)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ChooseLevelButton(modalManager: ModalManager) {
|
||||
private fun ChooseLevelButton(modalManager: ModalManager, unwindToDepth: Int) {
|
||||
OnboardingActionButton(
|
||||
modifier = if (appPlatform.isAndroid) Modifier.padding(horizontal = DEFAULT_ONBOARDING_HORIZONTAL_PADDING).fillMaxWidth() else Modifier.widthIn(min = 300.dp),
|
||||
labelId = MR.strings.badges_choose_your_badge_title,
|
||||
onboarding = null,
|
||||
enabled = BadgeStore.canBuy(chatModel.currentUser.value?.userId),
|
||||
onclick = {
|
||||
modalManager.showModal { BadgesChooseBadgeView(modalManager) }
|
||||
modalManager.showModal { BadgesChooseBadgeView(modalManager, unwindToDepth) }
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -118,10 +118,10 @@ private fun HowItWorksButton(modalManager: ModalManager) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RedeemCodeButton(modalManager: ModalManager) {
|
||||
fun RedeemCodeButton(modalManager: ModalManager, unwindToDepth: Int) {
|
||||
TextButtonBelowOnboardingButton(
|
||||
text = stringResource(MR.strings.badges_redeem_code_button),
|
||||
onClick = { modalManager.showModal { BadgesRedeemCodeView(modalManager) } }
|
||||
onClick = { modalManager.showModal { BadgesRedeemCodeView(modalManager, unwindToDepth) } }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ private fun BuyInBrowserTextButton() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BuyInBrowserButton(modalManager: ModalManager) {
|
||||
private fun BuyInBrowserButton(modalManager: ModalManager, unwindToDepth: Int) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
OnboardingActionButton(
|
||||
modifier = if (appPlatform.isAndroid) Modifier.padding(horizontal = DEFAULT_ONBOARDING_HORIZONTAL_PADDING).fillMaxWidth() else Modifier.widthIn(min = 300.dp),
|
||||
@@ -145,7 +145,7 @@ private fun BuyInBrowserButton(modalManager: ModalManager) {
|
||||
uriHandler.openUriCatching(badgePageUrl)
|
||||
// desktop has no scheme to bring the code back, so the code is pasted into this screen, opened beside the browser
|
||||
if (appPlatform.isDesktop && !modalManager.hasModalOpen(ModalViewId.BADGE_REDEEM_CODE)) {
|
||||
modalManager.showModal(id = ModalViewId.BADGE_REDEEM_CODE) { BadgesRedeemCodeView(modalManager) }
|
||||
modalManager.showModal(id = ModalViewId.BADGE_REDEEM_CODE) { BadgesRedeemCodeView(modalManager, unwindToDepth) }
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
+6
-5
@@ -3,6 +3,7 @@ package chat.simplex.common.views.badges
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import chat.simplex.common.model.BadgeModel
|
||||
import chat.simplex.common.model.BadgeState
|
||||
import chat.simplex.common.platform.chatModel
|
||||
@@ -13,6 +14,7 @@ import chat.simplex.common.views.helpers.ModalView
|
||||
@Composable
|
||||
fun BadgesView(modalManager: ModalManager, close: () -> Unit) {
|
||||
val shownBadge = currentShownBadge()
|
||||
val unwindToDepth = remember { modalManager.openModalCount() }
|
||||
|
||||
// the card look is a modal setting, so the modal is composed here to follow the screen shown
|
||||
ModalView(close, cardScreen = shownBadge != null) {
|
||||
@@ -27,7 +29,7 @@ fun BadgesView(modalManager: ModalManager, close: () -> Unit) {
|
||||
// holds the purchase screens' slot, so a consumable cannot be bought twice
|
||||
BadgesPurchaseStateView(purchaseState, onDismiss = close)
|
||||
} else {
|
||||
BadgesSupportSimplexView(modalManager)
|
||||
BadgesSupportSimplexView(modalManager, unwindToDepth)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,13 +41,12 @@ fun currentShownBadge(): BadgeState? {
|
||||
return if (badgeState != null && badgeState.shown) badgeState else null
|
||||
}
|
||||
|
||||
// each purchase screen closes itself when it recomposes with a purchase in flight or a badge shown, as iOS
|
||||
// pops them when Support SimpleX gives way to either
|
||||
// runs in the screen on top, the only one composed, and closes every screen above the badges modal in one go
|
||||
@Composable
|
||||
fun CloseWhenSupportGivesWay(modalManager: ModalManager) {
|
||||
fun CloseWhenSupportGivesWay(modalManager: ModalManager, unwindToDepth: Int) {
|
||||
val gaveWay = BadgeStore.purchaseState(chatModel.currentUser.value?.userId) != null || currentShownBadge() != null
|
||||
LaunchedEffect(gaveWay) {
|
||||
if (gaveWay) modalManager.closeModal()
|
||||
if (gaveWay) while (modalManager.openModalCount() > unwindToDepth) modalManager.closeModal()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user