Skip to content

Commit

Permalink
Add screen for managing backup type.
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-signal authored and cody-signal committed Apr 23, 2024
1 parent 04fb459 commit b771a21
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
}
}
Expand All @@ -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
Expand Down Expand Up @@ -184,7 +185,7 @@ private fun RemoteBackupsSettingsContent(
BackupTypeRow(
messageBackupsType = messageBackupsType,
onEnableBackupsClick = contentCallbacks::onEnableBackupsClick,
onChangeBackupsTypeClick = contentCallbacks::onChangeBackupsTypeClick
onChangeBackupsTypeClick = contentCallbacks::onBackupsTypeClick
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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 {}
)
}
}
Original file line number Diff line number Diff line change
@@ -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
)
Original file line number Diff line number Diff line change
@@ -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<BackupsTypeSettingsState> = internalState
}
15 changes: 14 additions & 1 deletion app/src/main/res/navigation/app_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -930,7 +930,20 @@

<fragment
android:id="@+id/remoteBackupsSettingsFragment"
android:name="org.thoughtcrime.securesms.components.settings.app.chats.backups.RemoteBackupsSettingsFragment" />
android:name="org.thoughtcrime.securesms.components.settings.app.chats.backups.RemoteBackupsSettingsFragment">

<action
android:id="@+id/action_remoteBackupsSettingsFragment_to_backupsTypeSettingsFragment"
app:destination="@id/backupsTypeSettingsFragment"
app:enterAnim="@anim/fragment_open_enter"
app:exitAnim="@anim/fragment_open_exit"
app:popEnterAnim="@anim/fragment_close_enter"
app:popExitAnim="@anim/fragment_close_exit" />
</fragment>

<fragment
android:id="@+id/backupsTypeSettingsFragment"
android:name="org.thoughtcrime.securesms.components.settings.app.chats.backups.type.BackupsTypeSettingsFragment" />

<include app:graph="@navigation/username_link_settings" />
<include app:graph="@navigation/story_privacy_settings" />
Expand Down
15 changes: 15 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6751,5 +6751,20 @@
<!-- Notification title shown while downloading backup restore data -->
<string name="BackupProgressService_title_downloading">Downloading backup data…</string>

<!-- BackupsTypeSettingsFragment -->
<!-- Displayed as the user's payment method as a label in a preference row -->
<string name="BackupsTypeSettingsFragment__credit_or_debit_card">Credit or debit card</string>
<!-- Displayed as the user's payment method as a label in a preference row -->
<string name="BackupsTypeSettingsFragment__iDEAL">iDEAL</string>
<!-- Displayed as the user's payment method as a label in a preference row -->
<string name="BackupsTypeSettingsFragment__google_pay">Google Pay</string>
<!-- Displayed as the user's payment method as a label in a preference row -->
<string name="BackupsTypeSettingsFragment__bank_transfer">Bank transfer</string>
<!-- Displayed as the user's payment method as a label in a preference row -->
<string name="BackupsTypeSettingsFragment__paypal">PayPal</string>
<!-- Displayed as the user's payment method as a label in a preference row -->
<string name="BackupsTypeSettingsFragment__unknown">Unknown</string>


<!-- EOF -->
</resources>

0 comments on commit b771a21

Please sign in to comment.