Always display donation receipts page.

fork-5.53.8
Alex Hart 2022-05-10 15:25:28 -03:00
rodzic eaf36be9f6
commit 68ba3433a3
9 zmienionych plików z 102 dodań i 60 usunięć

Wyświetl plik

@ -98,31 +98,31 @@ class ManageDonationsFragment : DSLSettingsFragment(), ExpiredGiftSheet.Callback
if (activeSubscription != null) { if (activeSubscription != null) {
val subscription: Subscription? = state.availableSubscriptions.firstOrNull { activeSubscription.level == it.level } val subscription: Subscription? = state.availableSubscriptions.firstOrNull { activeSubscription.level == it.level }
if (subscription != null) { if (subscription != null) {
presentSubscriptionSettings(state.hasReceipts, activeSubscription, subscription, state.getRedemptionState()) presentSubscriptionSettings(activeSubscription, subscription, state.getRedemptionState())
} else { } else {
customPref(IndeterminateLoadingCircle) customPref(IndeterminateLoadingCircle)
} }
} else { } else {
presentNoSubscriptionSettings(state.hasReceipts) presentNoSubscriptionSettings()
} }
} else if (state.transactionState == ManageDonationsState.TransactionState.NetworkFailure) { } else if (state.transactionState == ManageDonationsState.TransactionState.NetworkFailure) {
presentNetworkFailureSettings(state.hasReceipts, state.getRedemptionState()) presentNetworkFailureSettings(state.getRedemptionState())
} else { } else {
customPref(IndeterminateLoadingCircle) customPref(IndeterminateLoadingCircle)
} }
} }
} }
private fun DSLConfiguration.presentNetworkFailureSettings(hasReceipts: Boolean, redemptionState: ManageDonationsState.SubscriptionRedemptionState) { private fun DSLConfiguration.presentNetworkFailureSettings(redemptionState: ManageDonationsState.SubscriptionRedemptionState) {
if (SignalStore.donationsValues().isLikelyASustainer()) { if (SignalStore.donationsValues().isLikelyASustainer()) {
presentSubscriptionSettingsWithNetworkError(hasReceipts, redemptionState) presentSubscriptionSettingsWithNetworkError(redemptionState)
} else { } else {
presentNoSubscriptionSettings(hasReceipts) presentNoSubscriptionSettings()
} }
} }
private fun DSLConfiguration.presentSubscriptionSettingsWithNetworkError(hasReceipts: Boolean, redemptionState: ManageDonationsState.SubscriptionRedemptionState) { private fun DSLConfiguration.presentSubscriptionSettingsWithNetworkError(redemptionState: ManageDonationsState.SubscriptionRedemptionState) {
presentSubscriptionSettingsWithState(hasReceipts, redemptionState) { presentSubscriptionSettingsWithState(redemptionState) {
customPref( customPref(
NetworkFailure.Model( NetworkFailure.Model(
onRetryClick = { onRetryClick = {
@ -134,12 +134,11 @@ class ManageDonationsFragment : DSLSettingsFragment(), ExpiredGiftSheet.Callback
} }
private fun DSLConfiguration.presentSubscriptionSettings( private fun DSLConfiguration.presentSubscriptionSettings(
hasReceipts: Boolean,
activeSubscription: ActiveSubscription.Subscription, activeSubscription: ActiveSubscription.Subscription,
subscription: Subscription, subscription: Subscription,
redemptionState: ManageDonationsState.SubscriptionRedemptionState redemptionState: ManageDonationsState.SubscriptionRedemptionState
) { ) {
presentSubscriptionSettingsWithState(hasReceipts, redemptionState) { presentSubscriptionSettingsWithState(redemptionState) {
val activeCurrency = Currency.getInstance(activeSubscription.currency) val activeCurrency = Currency.getInstance(activeSubscription.currency)
val activeAmount = activeSubscription.amount.movePointLeft(activeCurrency.defaultFractionDigits) val activeAmount = activeSubscription.amount.movePointLeft(activeCurrency.defaultFractionDigits)
@ -160,7 +159,6 @@ class ManageDonationsFragment : DSLSettingsFragment(), ExpiredGiftSheet.Callback
} }
private fun DSLConfiguration.presentSubscriptionSettingsWithState( private fun DSLConfiguration.presentSubscriptionSettingsWithState(
hasReceipts: Boolean,
redemptionState: ManageDonationsState.SubscriptionRedemptionState, redemptionState: ManageDonationsState.SubscriptionRedemptionState,
subscriptionBlock: DSLConfiguration.() -> Unit subscriptionBlock: DSLConfiguration.() -> Unit
) { ) {
@ -198,9 +196,7 @@ class ManageDonationsFragment : DSLSettingsFragment(), ExpiredGiftSheet.Callback
sectionHeaderPref(R.string.ManageDonationsFragment__more) sectionHeaderPref(R.string.ManageDonationsFragment__more)
if (hasReceipts) { presentDonationReceipts()
presentDonationReceipts()
}
externalLinkPref( externalLinkPref(
title = DSLSettingsText.from(R.string.ManageDonationsFragment__subscription_faq), title = DSLSettingsText.from(R.string.ManageDonationsFragment__subscription_faq),
@ -209,7 +205,7 @@ class ManageDonationsFragment : DSLSettingsFragment(), ExpiredGiftSheet.Callback
) )
} }
private fun DSLConfiguration.presentNoSubscriptionSettings(hasReceipts: Boolean) { private fun DSLConfiguration.presentNoSubscriptionSettings() {
space(DimensionUnit.DP.toPixels(16f).toInt()) space(DimensionUnit.DP.toPixels(16f).toInt())
noPadTextPref( noPadTextPref(
@ -227,11 +223,9 @@ class ManageDonationsFragment : DSLSettingsFragment(), ExpiredGiftSheet.Callback
presentOtherWaysToGive() presentOtherWaysToGive()
if (hasReceipts) { sectionHeaderPref(R.string.ManageDonationsFragment__receipts)
sectionHeaderPref(R.string.ManageDonationsFragment__receipts)
presentDonationReceipts() presentDonationReceipts()
}
} }
private fun DSLConfiguration.presentOtherWaysToGive() { private fun DSLConfiguration.presentOtherWaysToGive() {

Wyświetl plik

@ -8,7 +8,6 @@ data class ManageDonationsState(
val featuredBadge: Badge? = null, val featuredBadge: Badge? = null,
val transactionState: TransactionState = TransactionState.Init, val transactionState: TransactionState = TransactionState.Init,
val availableSubscriptions: List<Subscription> = emptyList(), val availableSubscriptions: List<Subscription> = emptyList(),
val hasReceipts: Boolean = false,
private val subscriptionRedemptionState: SubscriptionRedemptionState = SubscriptionRedemptionState.NONE private val subscriptionRedemptionState: SubscriptionRedemptionState = SubscriptionRedemptionState.NONE
) { ) {

Wyświetl plik

@ -9,10 +9,8 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.disposables.Disposable import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
import io.reactivex.rxjava3.kotlin.subscribeBy import io.reactivex.rxjava3.kotlin.subscribeBy
import io.reactivex.rxjava3.schedulers.Schedulers
import org.signal.core.util.logging.Log import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.components.settings.app.subscription.SubscriptionsRepository import org.thoughtcrime.securesms.components.settings.app.subscription.SubscriptionsRepository
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.jobmanager.JobTracker import org.thoughtcrime.securesms.jobmanager.JobTracker
import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.subscription.LevelUpdate import org.thoughtcrime.securesms.subscription.LevelUpdate
@ -107,14 +105,6 @@ class ManageDonationsViewModel(
Log.w(TAG, "Error retrieving subscriptions data", it) Log.w(TAG, "Error retrieving subscriptions data", it)
} }
) )
disposables += Single.fromCallable { SignalDatabase.donationReceipts.hasReceipts() }
.subscribeOn(Schedulers.io())
.subscribe { hasReceipts ->
store.update {
it.copy(hasReceipts = hasReceipts)
}
}
} }
class Factory( class Factory(

Wyświetl plik

@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.components.settings.app.subscription.receipts
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.constraintlayout.widget.Group
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
@ -14,6 +15,7 @@ import org.thoughtcrime.securesms.database.model.DonationReceiptRecord
import org.thoughtcrime.securesms.util.StickyHeaderDecoration import org.thoughtcrime.securesms.util.StickyHeaderDecoration
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil import org.thoughtcrime.securesms.util.livedata.LiveDataUtil
import org.thoughtcrime.securesms.util.navigation.safeNavigate import org.thoughtcrime.securesms.util.navigation.safeNavigate
import org.thoughtcrime.securesms.util.visible
class DonationReceiptListPageFragment : Fragment(R.layout.donation_receipt_list_page_fragment) { class DonationReceiptListPageFragment : Fragment(R.layout.donation_receipt_list_page_fragment) {
@ -31,6 +33,8 @@ class DonationReceiptListPageFragment : Fragment(R.layout.donation_receipt_list_
private val type: DonationReceiptRecord.Type? private val type: DonationReceiptRecord.Type?
get() = requireArguments().getString(ARG_TYPE)?.let { DonationReceiptRecord.Type.fromCode(it) } get() = requireArguments().getString(ARG_TYPE)?.let { DonationReceiptRecord.Type.fromCode(it) }
private lateinit var emptyStateGroup: Group
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val adapter = DonationReceiptListAdapter { model -> val adapter = DonationReceiptListAdapter { model ->
findNavController().safeNavigate(DonationReceiptListFragmentDirections.actionDonationReceiptListFragmentToDonationReceiptDetailFragment(model.record.id)) findNavController().safeNavigate(DonationReceiptListFragmentDirections.actionDonationReceiptListFragmentToDonationReceiptDetailFragment(model.record.id))
@ -41,22 +45,29 @@ class DonationReceiptListPageFragment : Fragment(R.layout.donation_receipt_list_
addItemDecoration(StickyHeaderDecoration(adapter, false, true, 0)) addItemDecoration(StickyHeaderDecoration(adapter, false, true, 0))
} }
emptyStateGroup = view.findViewById(R.id.empty_state)
LiveDataUtil.combineLatest( LiveDataUtil.combineLatest(
viewModel.state, viewModel.state,
sharedViewModel.state sharedViewModel.state
) { records, badges -> ) { state, badges ->
records.map { DonationReceiptListItem.Model(it, getBadgeForRecord(it, badges)) } state.isLoaded to state.records.map { DonationReceiptListItem.Model(it, getBadgeForRecord(it, badges)) }
}.observe(viewLifecycleOwner) { records -> }.observe(viewLifecycleOwner) { (isLoaded, records) ->
adapter.submitList( if (records.isNotEmpty()) {
records + emptyStateGroup.visible = false
TextPreference( adapter.submitList(
title = null, records +
summary = DSLSettingsText.from( TextPreference(
R.string.DonationReceiptListFragment__if_you_have, title = null,
DSLSettingsText.TextAppearanceModifier(R.style.TextAppearance_Signal_Subtitle) summary = DSLSettingsText.from(
R.string.DonationReceiptListFragment__if_you_have,
DSLSettingsText.TextAppearanceModifier(R.style.TextAppearance_Signal_Subtitle)
)
) )
) )
) } else {
emptyStateGroup.visible = isLoaded
}
} }
} }

Wyświetl plik

@ -0,0 +1,8 @@
package org.thoughtcrime.securesms.components.settings.app.subscription.receipts.list
import org.thoughtcrime.securesms.database.model.DonationReceiptRecord
data class DonationReceiptListPageState(
val records: List<DonationReceiptRecord> = emptyList(),
val isLoaded: Boolean = false
)

Wyświetl plik

@ -1,24 +1,29 @@
package org.thoughtcrime.securesms.components.settings.app.subscription.receipts.list package org.thoughtcrime.securesms.components.settings.app.subscription.receipts.list
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
import org.thoughtcrime.securesms.database.model.DonationReceiptRecord import org.thoughtcrime.securesms.database.model.DonationReceiptRecord
import org.thoughtcrime.securesms.util.livedata.Store
class DonationReceiptListPageViewModel(type: DonationReceiptRecord.Type?, repository: DonationReceiptListPageRepository) : ViewModel() { class DonationReceiptListPageViewModel(type: DonationReceiptRecord.Type?, repository: DonationReceiptListPageRepository) : ViewModel() {
private val disposables = CompositeDisposable() private val disposables = CompositeDisposable()
private val internalState = MutableLiveData<List<DonationReceiptRecord>>() private val store = Store(DonationReceiptListPageState())
val state: LiveData<List<DonationReceiptRecord>> = internalState val state: LiveData<DonationReceiptListPageState> = store.stateLiveData
init { init {
disposables += repository.getRecords(type) disposables += repository.getRecords(type)
.subscribe { records -> .subscribe { records ->
internalState.postValue(records) store.update {
it.copy(
records = records,
isLoaded = true
)
}
} }
} }

Wyświetl plik

@ -6,7 +6,6 @@ import androidx.core.content.contentValuesOf
import org.signal.core.util.CursorUtil import org.signal.core.util.CursorUtil
import org.signal.core.util.SqlUtil import org.signal.core.util.SqlUtil
import org.signal.core.util.money.FiatMoney import org.signal.core.util.money.FiatMoney
import org.signal.core.util.select
import org.thoughtcrime.securesms.database.model.DonationReceiptRecord import org.thoughtcrime.securesms.database.model.DonationReceiptRecord
import java.math.BigDecimal import java.math.BigDecimal
import java.util.Currency import java.util.Currency
@ -40,15 +39,6 @@ class DonationReceiptDatabase(context: Context, databaseHelper: SignalDatabase)
) )
} }
fun hasReceipts(): Boolean {
return readableDatabase.select("1")
.from(TABLE_NAME)
.where("")
.limit(1)
.run()
.use { it.moveToFirst() }
}
fun addReceipt(record: DonationReceiptRecord) { fun addReceipt(record: DonationReceiptRecord) {
require(record.id == -1L) require(record.id == -1L)

Wyświetl plik

@ -1,7 +1,50 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/recycler"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
<TextView
android:id="@+id/empty_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:gravity="center"
android:paddingHorizontal="32dp"
android:text="@string/DonationReceiptListFragment__no_receipts"
android:textAppearance="@style/Signal.Text.Body"
android:textColor="@color/signal_text_secondary"
app:layout_constraintBottom_toTopOf="@id/empty_description"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="@+id/empty_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingHorizontal="32dp"
android:text="@string/DonationReceiptListFragment__if_you_have"
android:textAppearance="@style/TextAppearance.Signal.Subtitle"
android:textColor="@color/signal_text_secondary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/empty_title" />
<androidx.constraintlayout.widget.Group
android:id="@+id/empty_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:constraint_referenced_ids="empty_title,empty_description" />
</androidx.constraintlayout.widget.ConstraintLayout>

Wyświetl plik

@ -4473,6 +4473,8 @@
<string name="DonationReceiptDetailsFragment__thank_you_for_supporting">Thank you for supporting Signal. Your contribution helps fuel the mission of developing open source privacy technology that protects free expression and enables secure global communication for millions around the world. If youre a resident of the United States, please retain this receipt for your tax records. Signal Technology Foundation is a tax–exempt nonprofit organization in the United States under section 501c3 of the Internal Revenue Code. Our Federal Tax ID is 82–4506840.</string> <string name="DonationReceiptDetailsFragment__thank_you_for_supporting">Thank you for supporting Signal. Your contribution helps fuel the mission of developing open source privacy technology that protects free expression and enables secure global communication for millions around the world. If youre a resident of the United States, please retain this receipt for your tax records. Signal Technology Foundation is a tax–exempt nonprofit organization in the United States under section 501c3 of the Internal Revenue Code. Our Federal Tax ID is 82–4506840.</string>
<!-- Donation receipt type --> <!-- Donation receipt type -->
<string name="DonationReceiptDetailsFragment__s_dash_s">%1$s - %2$s</string> <string name="DonationReceiptDetailsFragment__s_dash_s">%1$s - %2$s</string>
<!-- Donation reciepts screen empty state title -->
<string name="DonationReceiptListFragment__no_receipts">No receipts</string>
<!-- region "Stories Tab" --> <!-- region "Stories Tab" -->