diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsFragment.kt index ddb7bd3f84..d1c0cd378c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsFragment.kt @@ -52,6 +52,7 @@ import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType import org.thoughtcrime.securesms.compose.ComposeFragment import org.thoughtcrime.securesms.payments.FiatMoneyUtil import org.thoughtcrime.securesms.util.DateUtils +import org.thoughtcrime.securesms.util.navigation.safeNavigate import org.thoughtcrime.securesms.util.viewModel import java.math.BigDecimal import java.util.Currency @@ -130,8 +131,8 @@ class RemoteBackupsSettingsFragment : ComposeFragment() { viewModel.turnOffAndDeleteBackups() } - override fun onChangeBackupsTypeClick() { - // TODO - launch flow at appropriate point + override fun onBackupsTypeClick() { + findNavController().safeNavigate(R.id.action_remoteBackupsSettingsFragment_to_backupsTypeSettingsFragment) } } } @@ -142,7 +143,7 @@ class RemoteBackupsSettingsFragment : ComposeFragment() { private interface ContentCallbacks { fun onNavigationClick() = Unit fun onEnableBackupsClick() = Unit - fun onChangeBackupsTypeClick() = Unit + fun onBackupsTypeClick() = Unit fun onBackUpUsingCellularClick(canUseCellular: Boolean) = Unit fun onViewPaymentHistory() = Unit fun onBackupNowClick() = Unit @@ -184,7 +185,7 @@ private fun RemoteBackupsSettingsContent( BackupTypeRow( messageBackupsType = messageBackupsType, onEnableBackupsClick = contentCallbacks::onEnableBackupsClick, - onChangeBackupsTypeClick = contentCallbacks::onChangeBackupsTypeClick + onChangeBackupsTypeClick = contentCallbacks::onBackupsTypeClick ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsFragment.kt new file mode 100644 index 0000000000..66b1f33184 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsFragment.kt @@ -0,0 +1,197 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.chats.backups.type + +import android.content.Intent +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.navigation.fragment.findNavController +import kotlinx.collections.immutable.persistentListOf +import org.signal.core.ui.Previews +import org.signal.core.ui.Rows +import org.signal.core.ui.Scaffolds +import org.signal.core.ui.SignalPreview +import org.signal.core.util.money.FiatMoney +import org.signal.donations.PaymentSourceType +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsFlowActivity +import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType +import org.thoughtcrime.securesms.compose.ComposeFragment +import org.thoughtcrime.securesms.payments.FiatMoneyUtil +import org.thoughtcrime.securesms.util.DateUtils +import org.thoughtcrime.securesms.util.viewModel +import java.math.BigDecimal +import java.util.Currency +import java.util.Locale + +/** + * Allows the user to modify their backup plan + */ +class BackupsTypeSettingsFragment : ComposeFragment() { + + private val viewModel: BackupsTypeSettingsViewModel by viewModel { + BackupsTypeSettingsViewModel() + } + + @Composable + override fun FragmentContent() { + val contentCallbacks = remember { + Callbacks() + } + + val state by viewModel.state + + BackupsTypeSettingsContent( + state = state, + contentCallbacks = contentCallbacks + ) + } + + private inner class Callbacks : ContentCallbacks { + override fun onNavigationClick() { + findNavController().popBackStack() + } + + override fun onPaymentHistoryClick() { + // TODO [message-backups] Navigate to payment history + } + + override fun onChangeOrCancelSubscriptionClick() { + startActivity(Intent(requireContext(), MessageBackupsFlowActivity::class.java)) + } + } +} + +private interface ContentCallbacks { + fun onNavigationClick() = Unit + fun onPaymentHistoryClick() = Unit + fun onChangeOrCancelSubscriptionClick() = Unit +} + +@Composable +private fun BackupsTypeSettingsContent( + state: BackupsTypeSettingsState, + contentCallbacks: ContentCallbacks +) { + if (state.backupsType == null) { + return + } + + Scaffolds.Settings( + title = "Backup Type", + onNavigationClick = contentCallbacks::onNavigationClick, + navigationIconPainter = painterResource(id = R.drawable.symbol_arrow_left_24) + ) { + LazyColumn( + modifier = Modifier.padding(it) + ) { + item { + BackupsTypeRow( + backupsType = state.backupsType, + nextRenewalTimestamp = state.nextRenewalTimestamp + ) + } + + item { + PaymentSourceRow( + paymentSourceType = state.paymentSourceType + ) + } + + item { + Rows.TextRow( + text = "Change or cancel subscription", // TODO [message-backups] final copy + onClick = contentCallbacks::onChangeOrCancelSubscriptionClick + ) + } + + item { + Rows.TextRow( + text = "Payment history", // TODO [message-backups] final copy + onClick = contentCallbacks::onPaymentHistoryClick + ) + } + } + } +} + +@Composable +private fun BackupsTypeRow( + backupsType: MessageBackupsType, + nextRenewalTimestamp: Long +) { + val resources = LocalContext.current.resources + val formattedAmount = remember(backupsType.pricePerMonth) { + FiatMoneyUtil.format(resources, backupsType.pricePerMonth, FiatMoneyUtil.formatOptions().trimZerosAfterDecimal()) + } + + val renewal = remember(nextRenewalTimestamp) { + DateUtils.formatDateWithoutDayOfWeek(Locale.getDefault(), nextRenewalTimestamp) + } + + Rows.TextRow(text = { + Column { + Text(text = backupsType.title) + Text( + text = "$formattedAmount/month . Renews $renewal", // TODO [message-backups] final copy + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + }) +} + +@Composable +private fun PaymentSourceRow(paymentSourceType: PaymentSourceType) { + val paymentSourceTextResId = remember(paymentSourceType) { + when (paymentSourceType) { + is PaymentSourceType.Stripe.CreditCard -> R.string.BackupsTypeSettingsFragment__credit_or_debit_card + is PaymentSourceType.Stripe.IDEAL -> R.string.BackupsTypeSettingsFragment__iDEAL + is PaymentSourceType.Stripe.GooglePay -> R.string.BackupsTypeSettingsFragment__google_pay + is PaymentSourceType.Stripe.SEPADebit -> R.string.BackupsTypeSettingsFragment__bank_transfer + is PaymentSourceType.PayPal -> R.string.BackupsTypeSettingsFragment__paypal + is PaymentSourceType.Unknown -> R.string.BackupsTypeSettingsFragment__unknown + } + } + + Rows.TextRow(text = { + Column { + Text(text = "Payment method") // TOD [message-backups] Final copy + Text( + text = stringResource(id = paymentSourceTextResId), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + }) +} + +@SignalPreview +@Composable +private fun BackupsTypeSettingsContentPreview() { + Previews.Preview { + BackupsTypeSettingsContent( + state = BackupsTypeSettingsState( + backupsType = MessageBackupsType( + pricePerMonth = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("USD")), + title = "Text + all media", + features = persistentListOf() + ) + ), + contentCallbacks = object : ContentCallbacks {} + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsState.kt new file mode 100644 index 0000000000..5449a81a04 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsState.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.chats.backups.type + +import androidx.compose.runtime.Stable +import org.signal.donations.PaymentSourceType +import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType + +@Stable +data class BackupsTypeSettingsState( + val backupsType: MessageBackupsType? = null, + val paymentSourceType: PaymentSourceType = PaymentSourceType.Unknown, + val nextRenewalTimestamp: Long = 0 +) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsViewModel.kt new file mode 100644 index 0000000000..c8ddcf34ea --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsViewModel.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.chats.backups.type + +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel + +class BackupsTypeSettingsViewModel : ViewModel() { + private val internalState = mutableStateOf(BackupsTypeSettingsState()) + + val state: State = internalState +} diff --git a/app/src/main/res/navigation/app_settings.xml b/app/src/main/res/navigation/app_settings.xml index 38005862fb..20ea3a6c49 100644 --- a/app/src/main/res/navigation/app_settings.xml +++ b/app/src/main/res/navigation/app_settings.xml @@ -930,7 +930,20 @@ + android:name="org.thoughtcrime.securesms.components.settings.app.chats.backups.RemoteBackupsSettingsFragment"> + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bf629496d3..35987d413f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6751,5 +6751,20 @@ Downloading backup data… + + + Credit or debit card + + iDEAL + + Google Pay + + Bank transfer + + PayPal + + Unknown + +