Add payment history screens for backups.
This commit is contained in:
parent
690236c4e5
commit
45239c2264
18 changed files with 518 additions and 37 deletions
|
@ -5,15 +5,12 @@
|
|||
|
||||
package org.thoughtcrime.securesms.backup.v2.ui.subscription
|
||||
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.fragment.findNavController
|
||||
|
@ -22,6 +19,7 @@ import org.signal.donations.InAppPaymentType
|
|||
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.DonationCheckoutDelegate
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.DonationProcessorAction
|
||||
import org.thoughtcrime.securesms.compose.ComposeFragment
|
||||
import org.thoughtcrime.securesms.compose.Nav
|
||||
import org.thoughtcrime.securesms.database.InAppPaymentTable
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
import org.thoughtcrime.securesms.util.viewModel
|
||||
|
@ -62,13 +60,9 @@ class MessageBackupsFlowFragment : ComposeFragment(), DonationCheckoutDelegate.C
|
|||
navController.enableOnBackPressed(true)
|
||||
}
|
||||
|
||||
NavHost(
|
||||
Nav.Host(
|
||||
navController = navController,
|
||||
startDestination = state.startScreen.name,
|
||||
enterTransition = { slideInHorizontally(initialOffsetX = { it }) },
|
||||
exitTransition = { slideOutHorizontally(targetOffsetX = { -it }) },
|
||||
popEnterTransition = { slideInHorizontally(initialOffsetX = { -it }) },
|
||||
popExitTransition = { slideOutHorizontally(targetOffsetX = { it }) }
|
||||
startDestination = state.startScreen.name
|
||||
) {
|
||||
composable(route = MessageBackupsScreen.EDUCATION.name) {
|
||||
MessageBackupsEducationScreen(
|
||||
|
|
|
@ -119,7 +119,7 @@ class RemoteBackupsSettingsFragment : ComposeFragment() {
|
|||
}
|
||||
|
||||
override fun onViewPaymentHistory() {
|
||||
// TODO [message-backups] Navigate to payment history
|
||||
findNavController().safeNavigate(R.id.action_remoteBackupsSettingsFragment_to_remoteBackupsPaymentHistoryFragment)
|
||||
}
|
||||
|
||||
override fun onBackupNowClick() {
|
||||
|
|
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.components.settings.app.chats.backups.history
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.navArgument
|
||||
import kotlinx.collections.immutable.persistentMapOf
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import org.signal.core.ui.Buttons
|
||||
import org.signal.core.ui.Dividers
|
||||
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.ui.Texts
|
||||
import org.signal.core.util.money.FiatMoney
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.compose.ComposeFragment
|
||||
import org.thoughtcrime.securesms.compose.Nav
|
||||
import org.thoughtcrime.securesms.database.model.DonationReceiptRecord
|
||||
import org.thoughtcrime.securesms.payments.FiatMoneyUtil
|
||||
import org.thoughtcrime.securesms.util.DateUtils
|
||||
import java.math.BigDecimal
|
||||
import java.util.Calendar
|
||||
import java.util.Currency
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Displays a list or detail view of in-app-payment receipts related to
|
||||
* backups.
|
||||
*/
|
||||
class RemoteBackupsPaymentHistoryFragment : ComposeFragment() {
|
||||
|
||||
private val viewModel: RemoteBackupsPaymentHistoryViewModel by viewModels()
|
||||
|
||||
@Composable
|
||||
override fun FragmentContent() {
|
||||
val state by viewModel.state.collectAsState()
|
||||
val navController = rememberNavController()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
navController.setOnBackPressedDispatcher(requireActivity().onBackPressedDispatcher)
|
||||
navController.enableOnBackPressed(true)
|
||||
}
|
||||
|
||||
val onNavigationClick = remember {
|
||||
{
|
||||
if (!navController.popBackStack()) {
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Nav.Host(navController = navController, startDestination = "list") {
|
||||
composable("list") {
|
||||
PaymentHistoryContent(
|
||||
state = state,
|
||||
onNavigationClick = onNavigationClick,
|
||||
onRecordClick = { navController.navigate("detail/${it.id}") }
|
||||
)
|
||||
}
|
||||
|
||||
composable("detail/{recordId}", listOf(navArgument("recordId") { type = NavType.LongType })) { backStackEntry ->
|
||||
val recordId = backStackEntry.arguments?.getLong("recordId")!!
|
||||
val record = state.records[recordId]!!
|
||||
|
||||
PaymentHistoryDetails(
|
||||
record = record,
|
||||
onNavigationClick = onNavigationClick,
|
||||
onShareClick = {} // TODO [message-backups] Generate shareable png
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PaymentHistoryContent(
|
||||
state: RemoteBackupsPaymentHistoryState,
|
||||
onNavigationClick: () -> Unit,
|
||||
onRecordClick: (DonationReceiptRecord) -> Unit
|
||||
) {
|
||||
Scaffolds.Settings(
|
||||
title = stringResource(id = R.string.RemoteBackupsPaymentHistoryFragment__payment_history),
|
||||
navigationIconPainter = painterResource(id = R.drawable.symbol_arrow_left_24),
|
||||
onNavigationClick = onNavigationClick
|
||||
) {
|
||||
val itemList = remember(state.records) { state.records.values.toPersistentList() }
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(it)
|
||||
) {
|
||||
itemsIndexed(
|
||||
items = itemList,
|
||||
key = { _, item -> item.id }
|
||||
) { idx, item ->
|
||||
val previous = itemList.getOrNull(idx - 1)
|
||||
val previousYear = rememberYear(timestamp = previous?.timestamp ?: 0)
|
||||
val ourYear = rememberYear(timestamp = item.timestamp)
|
||||
|
||||
if (previousYear != ourYear) {
|
||||
Texts.SectionHeader(text = "$ourYear")
|
||||
}
|
||||
|
||||
PaymentHistoryRow(item, onRecordClick)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberYear(timestamp: Long): Int {
|
||||
if (timestamp == 0L) {
|
||||
return -1
|
||||
}
|
||||
|
||||
val calendar = remember {
|
||||
Calendar.getInstance()
|
||||
}
|
||||
|
||||
return remember(timestamp) {
|
||||
calendar.timeInMillis = timestamp
|
||||
calendar.get(Calendar.YEAR)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PaymentHistoryRow(
|
||||
record: DonationReceiptRecord,
|
||||
onRecordClick: (DonationReceiptRecord) -> Unit
|
||||
) {
|
||||
val date = remember(record.timestamp) {
|
||||
DateUtils.formatDateWithYear(Locale.getDefault(), record.timestamp)
|
||||
}
|
||||
|
||||
val onClick = remember(record) {
|
||||
{ onRecordClick(record) }
|
||||
}
|
||||
|
||||
Rows.TextRow(text = {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text(
|
||||
text = date,
|
||||
style = MaterialTheme.typography.bodyLarge
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.RemoteBackupsPaymentHistoryFragment__text_and_all_media_backup),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
|
||||
val resources = LocalContext.current.resources
|
||||
val fiat = remember(record.amount) {
|
||||
FiatMoneyUtil.format(resources, record.amount, FiatMoneyUtil.formatOptions().trimZerosAfterDecimal())
|
||||
}
|
||||
|
||||
Text(text = fiat)
|
||||
}, onClick = onClick)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PaymentHistoryDetails(
|
||||
record: DonationReceiptRecord,
|
||||
onNavigationClick: () -> Unit,
|
||||
onShareClick: () -> Unit
|
||||
) {
|
||||
Scaffolds.Settings(
|
||||
title = stringResource(id = R.string.RemoteBackupsPaymentHistoryFragment__payment_details),
|
||||
onNavigationClick = onNavigationClick,
|
||||
navigationIconPainter = painterResource(id = R.drawable.symbol_arrow_left_24)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(it)
|
||||
) {
|
||||
val resources = LocalContext.current.resources
|
||||
val formattedAmount = remember(record.amount) {
|
||||
FiatMoneyUtil.format(resources, record.amount, FiatMoneyUtil.formatOptions().trimZerosAfterDecimal())
|
||||
}
|
||||
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_signal_logo_type),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.align(alignment = Alignment.CenterHorizontally)
|
||||
.padding(top = 24.dp, bottom = 16.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = formattedAmount,
|
||||
style = MaterialTheme.typography.displayMedium,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
Dividers.Default()
|
||||
|
||||
Rows.TextRow(
|
||||
text = stringResource(id = R.string.RemoteBackupsPaymentHistoryFragment__backup_type),
|
||||
label = stringResource(id = R.string.RemoteBackupsPaymentHistoryFragment__text_and_all_media_backup)
|
||||
)
|
||||
|
||||
val formattedDate = remember(record.timestamp) {
|
||||
DateUtils.formatDateWithYear(Locale.getDefault(), record.timestamp)
|
||||
}
|
||||
|
||||
Rows.TextRow(
|
||||
text = stringResource(id = R.string.RemoteBackupsPaymentHistoryFragment__date_paid),
|
||||
label = formattedDate
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
Buttons.LargePrimary(
|
||||
onClick = onShareClick,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = dimensionResource(id = R.dimen.core_ui__gutter))
|
||||
.padding(bottom = 24.dp)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.RemoteBackupsPaymentHistoryFragment__share))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
private fun PaymentHistoryContentPreview() {
|
||||
Previews.Preview {
|
||||
PaymentHistoryContent(
|
||||
state = RemoteBackupsPaymentHistoryState(
|
||||
records = persistentMapOf(
|
||||
1L to testRecord()
|
||||
)
|
||||
),
|
||||
onNavigationClick = {},
|
||||
onRecordClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
private fun PaymentHistoryRowPreview() {
|
||||
Previews.Preview {
|
||||
PaymentHistoryRow(
|
||||
record = testRecord(),
|
||||
onRecordClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
private fun PaymentDetailsContentPreview() {
|
||||
Previews.Preview {
|
||||
PaymentHistoryDetails(
|
||||
record = testRecord(),
|
||||
onNavigationClick = {},
|
||||
onShareClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun testRecord(): DonationReceiptRecord {
|
||||
return DonationReceiptRecord(
|
||||
id = 1,
|
||||
amount = FiatMoney(BigDecimal.ONE, Currency.getInstance("USD")),
|
||||
timestamp = 1718739691000,
|
||||
type = DonationReceiptRecord.Type.RECURRING_BACKUP,
|
||||
subscriptionLevel = 201
|
||||
)
|
||||
}
|
|
@ -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.history
|
||||
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.DonationReceiptRecord
|
||||
|
||||
object RemoteBackupsPaymentHistoryRepository {
|
||||
|
||||
fun getReceipts(): List<DonationReceiptRecord> {
|
||||
return SignalDatabase.donationReceipts.getReceipts(DonationReceiptRecord.Type.RECURRING_BACKUP)
|
||||
}
|
||||
}
|
|
@ -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.history
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import kotlinx.collections.immutable.PersistentMap
|
||||
import kotlinx.collections.immutable.persistentMapOf
|
||||
import org.thoughtcrime.securesms.database.model.DonationReceiptRecord
|
||||
|
||||
@Stable
|
||||
data class RemoteBackupsPaymentHistoryState(
|
||||
val records: PersistentMap<Long, DonationReceiptRecord> = persistentMapOf()
|
||||
)
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.components.settings.app.chats.backups.history
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.collections.immutable.toPersistentMap
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class RemoteBackupsPaymentHistoryViewModel : ViewModel() {
|
||||
|
||||
private val internalStateFlow = MutableStateFlow(RemoteBackupsPaymentHistoryState())
|
||||
val state: StateFlow<RemoteBackupsPaymentHistoryState> = internalStateFlow
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
val receipts = withContext(Dispatchers.IO) {
|
||||
RemoteBackupsPaymentHistoryRepository.getReceipts()
|
||||
}
|
||||
|
||||
internalStateFlow.update { state -> state.copy(records = receipts.associateBy { it.id }.toPersistentMap()) }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.donate.Ch
|
|||
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
|
||||
|
@ -67,7 +68,7 @@ class BackupsTypeSettingsFragment : ComposeFragment() {
|
|||
}
|
||||
|
||||
override fun onPaymentHistoryClick() {
|
||||
// TODO [message-backups] Navigate to payment history
|
||||
findNavController().safeNavigate(R.id.action_backupsTypeSettingsFragment_to_remoteBackupsPaymentHistoryFragment)
|
||||
}
|
||||
|
||||
override fun onChangeOrCancelSubscriptionClick() {
|
||||
|
|
|
@ -68,9 +68,10 @@ class DonationReceiptDetailFragment : DSLSettingsFragment(layoutId = R.layout.do
|
|||
val today: String = DateUtils.formatDateWithDayOfWeek(Locale.getDefault(), System.currentTimeMillis())
|
||||
val amount: String = FiatMoneyUtil.format(resources, record.amount)
|
||||
val type: String = when (record.type) {
|
||||
DonationReceiptRecord.Type.RECURRING -> getString(R.string.DonationReceiptDetailsFragment__s_dash_s, subscriptionName, getString(R.string.DonationReceiptListFragment__recurring))
|
||||
DonationReceiptRecord.Type.BOOST -> getString(R.string.DonationReceiptListFragment__one_time)
|
||||
DonationReceiptRecord.Type.GIFT -> getString(R.string.DonationReceiptListFragment__donation_for_a_friend)
|
||||
DonationReceiptRecord.Type.RECURRING_DONATION -> getString(R.string.DonationReceiptDetailsFragment__s_dash_s, subscriptionName, getString(R.string.DonationReceiptListFragment__recurring))
|
||||
DonationReceiptRecord.Type.ONE_TIME_DONATION -> getString(R.string.DonationReceiptListFragment__one_time)
|
||||
DonationReceiptRecord.Type.ONE_TIME_GIFT -> getString(R.string.DonationReceiptListFragment__donation_for_a_friend)
|
||||
DonationReceiptRecord.Type.RECURRING_BACKUP -> error("Not supported in this fragment")
|
||||
}
|
||||
val datePaid: String = DateUtils.formatDate(Locale.getDefault(), record.timestamp)
|
||||
|
||||
|
@ -140,9 +141,10 @@ class DonationReceiptDetailFragment : DSLSettingsFragment(layoutId = R.layout.do
|
|||
title = DSLSettingsText.from(R.string.DonationReceiptDetailsFragment__donation_type),
|
||||
summary = DSLSettingsText.from(
|
||||
when (record.type) {
|
||||
DonationReceiptRecord.Type.RECURRING -> getString(R.string.DonationReceiptDetailsFragment__s_dash_s, subscriptionName, getString(R.string.DonationReceiptListFragment__recurring))
|
||||
DonationReceiptRecord.Type.BOOST -> getString(R.string.DonationReceiptListFragment__one_time)
|
||||
DonationReceiptRecord.Type.GIFT -> getString(R.string.DonationReceiptListFragment__donation_for_a_friend)
|
||||
DonationReceiptRecord.Type.RECURRING_DONATION -> getString(R.string.DonationReceiptDetailsFragment__s_dash_s, subscriptionName, getString(R.string.DonationReceiptListFragment__recurring))
|
||||
DonationReceiptRecord.Type.ONE_TIME_DONATION -> getString(R.string.DonationReceiptListFragment__one_time)
|
||||
DonationReceiptRecord.Type.ONE_TIME_GIFT -> getString(R.string.DonationReceiptListFragment__donation_for_a_friend)
|
||||
DonationReceiptRecord.Type.RECURRING_BACKUP -> error("Not supported in this fragment.")
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -42,9 +42,10 @@ object DonationReceiptListItem {
|
|||
dateView.text = DateUtils.formatDate(Locale.getDefault(), model.record.timestamp)
|
||||
typeView.setText(
|
||||
when (model.record.type) {
|
||||
DonationReceiptRecord.Type.RECURRING -> R.string.DonationReceiptListFragment__recurring
|
||||
DonationReceiptRecord.Type.BOOST -> R.string.DonationReceiptListFragment__one_time
|
||||
DonationReceiptRecord.Type.GIFT -> R.string.DonationReceiptListFragment__donation_for_a_friend
|
||||
DonationReceiptRecord.Type.RECURRING_DONATION -> R.string.DonationReceiptListFragment__recurring
|
||||
DonationReceiptRecord.Type.ONE_TIME_DONATION -> R.string.DonationReceiptListFragment__one_time
|
||||
DonationReceiptRecord.Type.ONE_TIME_GIFT -> R.string.DonationReceiptListFragment__donation_for_a_friend
|
||||
DonationReceiptRecord.Type.RECURRING_BACKUP -> error("Not supported in this fragment")
|
||||
}
|
||||
)
|
||||
moneyView.text = FiatMoneyUtil.format(context.resources, model.record.amount)
|
||||
|
|
|
@ -10,9 +10,9 @@ class DonationReceiptListPageAdapter(fragment: Fragment) : FragmentStateAdapter(
|
|||
override fun createFragment(position: Int): Fragment {
|
||||
return when (position) {
|
||||
0 -> DonationReceiptListPageFragment.create(null)
|
||||
1 -> DonationReceiptListPageFragment.create(DonationReceiptRecord.Type.RECURRING)
|
||||
2 -> DonationReceiptListPageFragment.create(DonationReceiptRecord.Type.BOOST)
|
||||
3 -> DonationReceiptListPageFragment.create(DonationReceiptRecord.Type.GIFT)
|
||||
1 -> DonationReceiptListPageFragment.create(DonationReceiptRecord.Type.RECURRING_DONATION)
|
||||
2 -> DonationReceiptListPageFragment.create(DonationReceiptRecord.Type.ONE_TIME_DONATION)
|
||||
3 -> DonationReceiptListPageFragment.create(DonationReceiptRecord.Type.ONE_TIME_GIFT)
|
||||
else -> error("Unsupported position $position")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,8 +73,8 @@ class DonationReceiptListPageFragment : Fragment(R.layout.donation_receipt_list_
|
|||
|
||||
private fun getBadgeForRecord(record: DonationReceiptRecord, badges: List<DonationReceiptBadge>): Badge? {
|
||||
return when (record.type) {
|
||||
DonationReceiptRecord.Type.BOOST -> badges.firstOrNull { it.type == DonationReceiptRecord.Type.BOOST }?.badge
|
||||
DonationReceiptRecord.Type.GIFT -> badges.firstOrNull { it.type == DonationReceiptRecord.Type.GIFT }?.badge
|
||||
DonationReceiptRecord.Type.ONE_TIME_DONATION -> badges.firstOrNull { it.type == DonationReceiptRecord.Type.ONE_TIME_DONATION }?.badge
|
||||
DonationReceiptRecord.Type.ONE_TIME_GIFT -> badges.firstOrNull { it.type == DonationReceiptRecord.Type.ONE_TIME_GIFT }?.badge
|
||||
else -> badges.firstOrNull { it.level == record.subscriptionLevel }?.badge
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,13 +17,13 @@ class DonationReceiptListRepository {
|
|||
}.map { response ->
|
||||
if (response.result.isPresent) {
|
||||
val config = response.result.get()
|
||||
val boostBadge = DonationReceiptBadge(DonationReceiptRecord.Type.BOOST, -1, config.getBoostBadges().first())
|
||||
val giftBadge = DonationReceiptBadge(DonationReceiptRecord.Type.GIFT, -1, config.getGiftBadges().first())
|
||||
val boostBadge = DonationReceiptBadge(DonationReceiptRecord.Type.ONE_TIME_DONATION, -1, config.getBoostBadges().first())
|
||||
val giftBadge = DonationReceiptBadge(DonationReceiptRecord.Type.ONE_TIME_GIFT, -1, config.getGiftBadges().first())
|
||||
val subBadges = config.getSubscriptionLevels().map {
|
||||
DonationReceiptBadge(
|
||||
level = it.key,
|
||||
badge = Badges.fromServiceBadge(it.value.badge),
|
||||
type = DonationReceiptRecord.Type.RECURRING
|
||||
type = DonationReceiptRecord.Type.RECURRING_DONATION
|
||||
)
|
||||
}
|
||||
subBadges + boostBadge + giftBadge
|
||||
|
|
49
app/src/main/java/org/thoughtcrime/securesms/compose/Nav.kt
Normal file
49
app/src/main/java/org/thoughtcrime/securesms/compose/Nav.kt
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.compose
|
||||
|
||||
import androidx.compose.animation.AnimatedContentTransitionScope
|
||||
import androidx.compose.animation.EnterTransition
|
||||
import androidx.compose.animation.ExitTransition
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
|
||||
/**
|
||||
* Default Navigation utilities for compose.
|
||||
*/
|
||||
object Nav {
|
||||
|
||||
@Composable
|
||||
fun Host(
|
||||
navController: NavHostController,
|
||||
startDestination: String,
|
||||
modifier: Modifier = Modifier,
|
||||
route: String? = null,
|
||||
enterTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition) = { slideInHorizontally(initialOffsetX = { it }) },
|
||||
exitTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition) = { slideOutHorizontally(targetOffsetX = { -it }) },
|
||||
popEnterTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition) = { slideInHorizontally(initialOffsetX = { -it }) },
|
||||
popExitTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition) = { slideOutHorizontally(targetOffsetX = { it }) },
|
||||
builder: NavGraphBuilder.() -> Unit
|
||||
) {
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = startDestination,
|
||||
modifier = modifier,
|
||||
route = route,
|
||||
enterTransition = enterTransition,
|
||||
exitTransition = exitTransition,
|
||||
popEnterTransition = popEnterTransition,
|
||||
popExitTransition = popExitTransition,
|
||||
builder = builder
|
||||
)
|
||||
}
|
||||
}
|
|
@ -74,7 +74,7 @@ class DonationReceiptTable(context: Context, databaseHelper: SignalDatabase) : D
|
|||
val (where, whereArgs) = if (type != null) {
|
||||
"$TYPE = ?" to SqlUtil.buildArgs(type.code)
|
||||
} else {
|
||||
null to null
|
||||
"$TYPE != ?" to SqlUtil.buildArgs(DonationReceiptRecord.Type.RECURRING_DONATION)
|
||||
}
|
||||
|
||||
readableDatabase.query(TABLE_NAME, null, where, whereArgs, null, null, "$DATE DESC").use { cursor ->
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.database.model
|
|||
|
||||
import org.signal.core.util.money.FiatMoney
|
||||
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
|
||||
import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration
|
||||
import java.util.Currency
|
||||
|
||||
data class DonationReceiptRecord(
|
||||
|
@ -12,9 +13,10 @@ data class DonationReceiptRecord(
|
|||
val subscriptionLevel: Int
|
||||
) {
|
||||
enum class Type(val code: String) {
|
||||
RECURRING("recurring"),
|
||||
BOOST("boost"),
|
||||
GIFT("gift");
|
||||
RECURRING_BACKUP("recurring_backup"),
|
||||
RECURRING_DONATION("recurring"),
|
||||
ONE_TIME_DONATION("boost"),
|
||||
ONE_TIME_GIFT("gift");
|
||||
|
||||
companion object {
|
||||
fun fromCode(code: String): Type {
|
||||
|
@ -34,7 +36,7 @@ data class DonationReceiptRecord(
|
|||
amount = FiatMoney(activeAmount, activeCurrency),
|
||||
timestamp = System.currentTimeMillis(),
|
||||
subscriptionLevel = subscription.level,
|
||||
type = Type.RECURRING
|
||||
type = if (subscription.level == SubscriptionsConfiguration.BACKUPS_LEVEL) Type.RECURRING_BACKUP else Type.RECURRING_DONATION
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -44,7 +46,7 @@ data class DonationReceiptRecord(
|
|||
amount = amount,
|
||||
timestamp = System.currentTimeMillis(),
|
||||
subscriptionLevel = -1,
|
||||
type = Type.BOOST
|
||||
type = Type.ONE_TIME_DONATION
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -54,7 +56,7 @@ data class DonationReceiptRecord(
|
|||
amount = amount,
|
||||
timestamp = System.currentTimeMillis(),
|
||||
subscriptionLevel = -1,
|
||||
type = Type.GIFT
|
||||
type = Type.ONE_TIME_GIFT
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -969,11 +969,32 @@
|
|||
app:exitAnim="@anim/fragment_open_exit"
|
||||
app:popEnterAnim="@anim/fragment_close_enter"
|
||||
app:popExitAnim="@anim/fragment_close_exit" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_remoteBackupsSettingsFragment_to_remoteBackupsPaymentHistoryFragment"
|
||||
app:destination="@id/remoteBackupsPaymentHistoryFragment"
|
||||
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" />
|
||||
android:name="org.thoughtcrime.securesms.components.settings.app.chats.backups.type.BackupsTypeSettingsFragment">
|
||||
|
||||
<action
|
||||
android:id="@+id/action_backupsTypeSettingsFragment_to_remoteBackupsPaymentHistoryFragment"
|
||||
app:destination="@id/remoteBackupsPaymentHistoryFragment"
|
||||
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/remoteBackupsPaymentHistoryFragment"
|
||||
android:name="org.thoughtcrime.securesms.components.settings.app.chats.backups.history.RemoteBackupsPaymentHistoryFragment"/>
|
||||
|
||||
<include app:graph="@navigation/username_link_settings" />
|
||||
<include app:graph="@navigation/story_privacy_settings" />
|
||||
|
|
|
@ -969,11 +969,33 @@
|
|||
app:exitAnim="@anim/fragment_open_exit"
|
||||
app:popEnterAnim="@anim/fragment_close_enter"
|
||||
app:popExitAnim="@anim/fragment_close_exit" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_remoteBackupsSettingsFragment_to_remoteBackupsPaymentHistoryFragment"
|
||||
app:destination="@id/remoteBackupsPaymentHistoryFragment"
|
||||
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/remoteBackupsPaymentHistoryFragment"
|
||||
android:name="org.thoughtcrime.securesms.components.settings.app.chats.backups.history.RemoteBackupsPaymentHistoryFragment">
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/backupsTypeSettingsFragment"
|
||||
android:name="org.thoughtcrime.securesms.components.settings.app.chats.backups.type.BackupsTypeSettingsFragment" />
|
||||
android:name="org.thoughtcrime.securesms.components.settings.app.chats.backups.type.BackupsTypeSettingsFragment">
|
||||
|
||||
<action
|
||||
android:id="@+id/action_backupsTypeSettingsFragment_to_remoteBackupsPaymentHistoryFragment"
|
||||
app:destination="@id/remoteBackupsPaymentHistoryFragment"
|
||||
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>
|
||||
|
||||
<include app:graph="@navigation/username_link_settings" />
|
||||
<include app:graph="@navigation/story_privacy_settings" />
|
||||
|
|
|
@ -7047,6 +7047,20 @@
|
|||
<!-- Educational bottom sheet confirm/dismiss button text shown to notify about delete syncs causing deletes to happen across all devices -->
|
||||
<string name="DeleteSyncEducation_acknowledge_button">OK</string>
|
||||
|
||||
<!-- RemoteBackupsPaymentHistoryFragment -->
|
||||
<!-- Title of the screen for payment history -->
|
||||
<string name="RemoteBackupsPaymentHistoryFragment__payment_history">Payment history</string>
|
||||
<!-- Description for backup rows -->
|
||||
<string name="RemoteBackupsPaymentHistoryFragment__text_and_all_media_backup">Text and all media backup</string>
|
||||
<!-- Title of the screen for payment details -->
|
||||
<string name="RemoteBackupsPaymentHistoryFragment__payment_details">Payment details</string>
|
||||
<!-- Title of row specifying the type of backup -->
|
||||
<string name="RemoteBackupsPaymentHistoryFragment__backup_type">Backup type</string>
|
||||
<!-- Title of row specifying the date the backup was paid on -->
|
||||
<string name="RemoteBackupsPaymentHistoryFragment__date_paid">Date paid</string>
|
||||
<!-- Button label to share the receipt -->
|
||||
<string name="RemoteBackupsPaymentHistoryFragment__share">Share</string>
|
||||
|
||||
<!-- RemoteBackupsSettingsFragment -->
|
||||
<!-- Displayed on the title bar -->
|
||||
<string name="RemoteBackupsSettingsFragment__signal_backups">Signal Backups</string>
|
||||
|
|
Loading…
Add table
Reference in a new issue