Backups subscription flow odds and ends.

This commit is contained in:
Alex Hart 2024-07-16 09:13:45 -03:00 committed by Greyson Parrelli
parent 97974291d2
commit 89bfba3ee9
5 changed files with 35 additions and 14 deletions

View file

@ -5,6 +5,7 @@
package org.thoughtcrime.securesms.backup.v2.ui.subscription package org.thoughtcrime.securesms.backup.v2.ui.subscription
import androidx.activity.OnBackPressedCallback
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -38,6 +39,7 @@ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelega
@Composable @Composable
override fun FragmentContent() { override fun FragmentContent() {
val state by viewModel.stateFlow.collectAsState() val state by viewModel.stateFlow.collectAsState()
val pin by viewModel.pinState
val navController = rememberNavController() val navController = rememberNavController()
val checkoutDelegate = remember { val checkoutDelegate = remember {
@ -57,8 +59,12 @@ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelega
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
navController.setLifecycleOwner(this@MessageBackupsFlowFragment) navController.setLifecycleOwner(this@MessageBackupsFlowFragment)
navController.setOnBackPressedDispatcher(requireActivity().onBackPressedDispatcher)
navController.enableOnBackPressed(true) requireActivity().onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
viewModel.goToPreviousScreen()
}
})
} }
Nav.Host( Nav.Host(
@ -84,7 +90,7 @@ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelega
composable(route = MessageBackupsScreen.PIN_CONFIRMATION.name) { composable(route = MessageBackupsScreen.PIN_CONFIRMATION.name) {
MessageBackupsPinConfirmationScreen( MessageBackupsPinConfirmationScreen(
pin = state.pin, pin = pin,
onPinChanged = viewModel::onPinEntryUpdated, onPinChanged = viewModel::onPinEntryUpdated,
pinKeyboardType = state.pinKeyboardType, pinKeyboardType = state.pinKeyboardType,
onPinKeyboardTypeSelected = viewModel::onPinKeyboardTypeUpdated, onPinKeyboardTypeSelected = viewModel::onPinKeyboardTypeUpdated,

View file

@ -17,7 +17,6 @@ data class MessageBackupsFlowState(
val availableBackupTypes: List<MessageBackupsType> = emptyList(), val availableBackupTypes: List<MessageBackupsType> = emptyList(),
val selectedPaymentMethod: InAppPaymentData.PaymentMethodType? = null, val selectedPaymentMethod: InAppPaymentData.PaymentMethodType? = null,
val availablePaymentMethods: List<InAppPaymentData.PaymentMethodType> = emptyList(), val availablePaymentMethods: List<InAppPaymentData.PaymentMethodType> = emptyList(),
val pin: String = "",
val pinKeyboardType: PinKeyboardType = SignalStore.pin.keyboardType, val pinKeyboardType: PinKeyboardType = SignalStore.pin.keyboardType,
val inAppPayment: InAppPaymentTable.InAppPayment? = null, val inAppPayment: InAppPaymentTable.InAppPayment? = null,
val startScreen: MessageBackupsScreen, val startScreen: MessageBackupsScreen,

View file

@ -7,6 +7,7 @@ package org.thoughtcrime.securesms.backup.v2.ui.subscription
import android.text.TextUtils import android.text.TextUtils
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -43,7 +44,10 @@ class MessageBackupsFlowViewModel : ViewModel() {
) )
) )
private val internalPinState = mutableStateOf("")
val stateFlow: StateFlow<MessageBackupsFlowState> = internalStateFlow val stateFlow: StateFlow<MessageBackupsFlowState> = internalStateFlow
val pinState: State<String> = internalPinState
init { init {
viewModelScope.launch { viewModelScope.launch {
@ -58,11 +62,14 @@ class MessageBackupsFlowViewModel : ViewModel() {
} }
fun goToNextScreen() { fun goToNextScreen() {
val pinSnapshot = pinState.value
internalPinState.value = ""
internalStateFlow.update { internalStateFlow.update {
val nextScreen = when (it.screen) { val nextScreen = when (it.screen) {
MessageBackupsScreen.EDUCATION -> MessageBackupsScreen.PIN_EDUCATION MessageBackupsScreen.EDUCATION -> MessageBackupsScreen.PIN_EDUCATION
MessageBackupsScreen.PIN_EDUCATION -> MessageBackupsScreen.PIN_CONFIRMATION MessageBackupsScreen.PIN_EDUCATION -> MessageBackupsScreen.PIN_CONFIRMATION
MessageBackupsScreen.PIN_CONFIRMATION -> validatePinAndUpdateState(it.pin) MessageBackupsScreen.PIN_CONFIRMATION -> validatePinAndUpdateState(pinSnapshot)
MessageBackupsScreen.TYPE_SELECTION -> validateTypeAndUpdateState(it.selectedMessageBackupTier!!) MessageBackupsScreen.TYPE_SELECTION -> validateTypeAndUpdateState(it.selectedMessageBackupTier!!)
MessageBackupsScreen.CHECKOUT_SHEET -> validateGatewayAndUpdateState(it) MessageBackupsScreen.CHECKOUT_SHEET -> validateGatewayAndUpdateState(it)
MessageBackupsScreen.CREATING_IN_APP_PAYMENT -> error("This is driven by an async coroutine.") MessageBackupsScreen.CREATING_IN_APP_PAYMENT -> error("This is driven by an async coroutine.")
@ -107,10 +114,7 @@ class MessageBackupsFlowViewModel : ViewModel() {
} }
fun onPinEntryUpdated(pin: String) { fun onPinEntryUpdated(pin: String) {
// TODO [alex] -- shouldn't store this in a flow internalPinState.value = pin
internalStateFlow.update {
it.copy(pin = pin)
}
} }
fun onPinKeyboardTypeUpdated(pinKeyboardType: PinKeyboardType) { fun onPinKeyboardTypeUpdated(pinKeyboardType: PinKeyboardType) {
@ -137,13 +141,15 @@ class MessageBackupsFlowViewModel : ViewModel() {
} }
private fun validateTypeAndUpdateState(tier: MessageBackupTier): MessageBackupsScreen { private fun validateTypeAndUpdateState(tier: MessageBackupTier): MessageBackupsScreen {
SignalStore.backup.areBackupsEnabled = true
SignalStore.backup.backupTier = tier
// TODO [message-backups] - Does anything need to be kicked off? // TODO [message-backups] - Does anything need to be kicked off?
return when (tier) { return when (tier) {
MessageBackupTier.FREE -> MessageBackupsScreen.COMPLETED MessageBackupTier.FREE -> {
SignalStore.backup.areBackupsEnabled = true
SignalStore.backup.backupTier = MessageBackupTier.FREE
MessageBackupsScreen.COMPLETED
}
MessageBackupTier.PAID -> MessageBackupsScreen.CHECKOUT_SHEET MessageBackupTier.PAID -> MessageBackupsScreen.CHECKOUT_SHEET
} }
} }

View file

@ -32,6 +32,7 @@ import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
@ -99,7 +100,8 @@ fun MessageBackupsPinConfirmationScreen(
onDone = { onNextClick() } onDone = { onNextClick() }
), ),
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(
keyboardType = keyboardType keyboardType = keyboardType,
imeAction = ImeAction.Done
), ),
modifier = Modifier modifier = Modifier
.padding(top = 72.dp) .padding(top = 72.dp)

View file

@ -10,6 +10,7 @@ import org.signal.donations.PaymentSourceType
import org.signal.donations.StripeApi import org.signal.donations.StripeApi
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialRequestContext import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialRequestContext
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
import org.thoughtcrime.securesms.badges.Badges import org.thoughtcrime.securesms.badges.Badges
import org.thoughtcrime.securesms.badges.models.Badge import org.thoughtcrime.securesms.badges.models.Badge
import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository
@ -464,6 +465,10 @@ class InAppPaymentValues internal constructor(store: KeyValueStore) : SignalStor
markDonationManuallyCancelled() markDonationManuallyCancelled()
} else { } else {
markBackupSubscriptionpManuallyCancelled() markBackupSubscriptionpManuallyCancelled()
// TODO [message-backups] -- Handle downgrades?
SignalStore.backup.areBackupsEnabled = false
SignalStore.backup.backupTier = null
} }
val subscriber = InAppPaymentsRepository.getSubscriber(subscriberType) val subscriber = InAppPaymentsRepository.getSubscriber(subscriberType)
@ -504,6 +509,9 @@ class InAppPaymentValues internal constructor(store: KeyValueStore) : SignalStor
} }
} else { } else {
clearBackupSubscriptionManuallyCancelled() clearBackupSubscriptionManuallyCancelled()
SignalStore.backup.areBackupsEnabled = true
SignalStore.backup.backupTier = MessageBackupTier.PAID
} }
val subscriber = InAppPaymentsRepository.requireSubscriber(subscriberType) val subscriber = InAppPaymentsRepository.requireSubscriber(subscriberType)