Add new joined donations screen.

This commit is contained in:
Alex Hart
2022-09-29 09:32:49 -03:00
parent c829fba332
commit daa3721145
79 changed files with 2516 additions and 1819 deletions

View File

@@ -15,6 +15,7 @@ import io.reactivex.rxjava3.subjects.PublishSubject
import org.signal.core.util.logging.Log
import org.signal.core.util.money.FiatMoney
import org.signal.donations.GooglePayApi
import org.signal.donations.GooglePayPaymentSource
import org.thoughtcrime.securesms.badges.gifts.Gifts
import org.thoughtcrime.securesms.badges.models.Badge
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationEvent
@@ -164,7 +165,7 @@ class GiftFlowViewModel(
store.update { it.copy(stage = GiftFlowState.Stage.PAYMENT_PIPELINE) }
donationPaymentRepository.continuePayment(gift.price, paymentData, recipient, store.state.additionalMessage?.toString(), gift.level).subscribeBy(
donationPaymentRepository.continuePayment(gift.price, GooglePayPaymentSource(paymentData), recipient, store.state.additionalMessage?.toString(), gift.level).subscribeBy(
onError = this@GiftFlowViewModel::onPaymentFlowError,
onComplete = {
store.update { it.copy(stage = GiftFlowState.Stage.READY) }

View File

@@ -1,16 +1,14 @@
package org.thoughtcrime.securesms.badges.gifts.flow
import android.view.View
import android.widget.TextView
import org.signal.core.util.money.FiatMoney
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.badges.BadgeImageView
import org.thoughtcrime.securesms.badges.models.Badge
import org.thoughtcrime.securesms.databinding.SubscriptionPreferenceBinding
import org.thoughtcrime.securesms.payments.FiatMoneyUtil
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
import org.thoughtcrime.securesms.util.adapter.mapping.BindingFactory
import org.thoughtcrime.securesms.util.adapter.mapping.BindingViewHolder
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel
import org.thoughtcrime.securesms.util.adapter.mapping.MappingViewHolder
import org.thoughtcrime.securesms.util.visible
import java.util.concurrent.TimeUnit
@@ -19,7 +17,7 @@ import java.util.concurrent.TimeUnit
*/
object GiftRowItem {
fun register(mappingAdapter: MappingAdapter) {
mappingAdapter.registerFactory(Model::class.java, LayoutFactory(::ViewHolder, R.layout.subscription_preference))
mappingAdapter.registerFactory(Model::class.java, BindingFactory(::ViewHolder, SubscriptionPreferenceBinding::inflate))
}
class Model(val giftBadge: Badge, val price: FiatMoney) : MappingModel<Model> {
@@ -28,23 +26,15 @@ object GiftRowItem {
override fun areContentsTheSame(newItem: Model): Boolean = giftBadge == newItem.giftBadge && price == newItem.price
}
class ViewHolder(itemView: View) : MappingViewHolder<Model>(itemView) {
private val badgeView = itemView.findViewById<BadgeImageView>(R.id.badge)
private val titleView = itemView.findViewById<TextView>(R.id.title)
private val checkView = itemView.findViewById<View>(R.id.check)
private val taglineView = itemView.findViewById<TextView>(R.id.tagline)
private val priceView = itemView.findViewById<TextView>(R.id.price)
class ViewHolder(binding: SubscriptionPreferenceBinding) : BindingViewHolder<Model, SubscriptionPreferenceBinding>(binding) {
init {
itemView.isSelected = true
binding.root.isSelected = true
}
override fun bind(model: Model) {
checkView.visible = false
badgeView.setBadge(model.giftBadge)
titleView.text = model.giftBadge.name
taglineView.setText(R.string.GiftRowItem__send_a_gift_badge)
binding.check.visible = false
binding.badge.setBadge(model.giftBadge)
binding.tagline.setText(R.string.GiftRowItem__send_a_gift_badge)
val price = FiatMoneyUtil.format(
context.resources,
@@ -56,7 +46,7 @@ object GiftRowItem {
val duration = TimeUnit.MILLISECONDS.toDays(model.giftBadge.duration)
priceView.text = context.resources.getQuantityString(R.plurals.GiftRowItem_s_dot_d_day_duration, duration.toInt(), price, duration)
binding.title.text = context.resources.getQuantityString(R.plurals.GiftRowItem_s_dot_d_day_duration, duration.toInt(), price, duration)
}
}
}

View File

@@ -10,6 +10,9 @@ import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel
import org.thoughtcrime.securesms.util.adapter.mapping.MappingViewHolder
/**
* "Hero Image" for displaying an Avatar and badge. Allows the user to see what their profile will look like with a particular badge applied.
*/
object BadgePreview {
fun register(mappingAdapter: MappingAdapter) {
@@ -19,6 +22,11 @@ object BadgePreview {
}
sealed class BadgeModel<T : BadgeModel<T>> : MappingModel<T> {
companion object {
const val PAYLOAD_BADGE = "badge"
}
abstract val badge: Badge?
abstract val recipient: Recipient
@@ -33,12 +41,20 @@ object BadgePreview {
data class GiftedBadgeModel(override val badge: Badge?, override val recipient: Recipient) : BadgeModel<GiftedBadgeModel>()
override fun areItemsTheSame(newItem: T): Boolean {
return badge?.id == newItem.badge?.id && recipient.id == newItem.recipient.id
return recipient.id == newItem.recipient.id
}
override fun areContentsTheSame(newItem: T): Boolean {
return badge == newItem.badge && recipient.hasSameContent(newItem.recipient)
}
override fun getChangePayload(newItem: T): Any? {
return if (recipient.hasSameContent(newItem.recipient) && badge != newItem.badge) {
PAYLOAD_BADGE
} else {
null
}
}
}
class ViewHolder<T : BadgeModel<T>>(itemView: View) : MappingViewHolder<T>(itemView) {

View File

@@ -10,15 +10,15 @@ import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.viewModels
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.button.MaterialButton
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.badges.BadgeRepository
import org.thoughtcrime.securesms.badges.models.Badge
import org.thoughtcrime.securesms.badges.models.LargeBadge
import org.thoughtcrime.securesms.components.FixedRoundedCornerBottomSheetDialogFragment
import org.thoughtcrime.securesms.components.ViewBinderDelegate
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity
import org.thoughtcrime.securesms.databinding.ViewBadgeBottomSheetDialogFragmentBinding
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.util.BottomSheetUtil
@@ -43,6 +43,8 @@ class ViewBadgeBottomSheetDialogFragment : FixedRoundedCornerBottomSheetDialogFr
textSize = ViewUtil.spToPx(16f).toFloat()
}
private val binding by ViewBinderDelegate(ViewBadgeBottomSheetDialogFragmentBinding::bind)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.view_badge_bottom_sheet_dialog_fragment, container, false)
}
@@ -50,41 +52,36 @@ class ViewBadgeBottomSheetDialogFragment : FixedRoundedCornerBottomSheetDialogFr
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
postponeEnterTransition()
val pager: ViewPager2 = view.findViewById(R.id.pager)
val tabs: TabLayout = view.findViewById(R.id.tab_layout)
val action: MaterialButton = view.findViewById(R.id.action)
val noSupport: View = view.findViewById(R.id.no_support)
if (getRecipientId() == Recipient.self().id) {
action.visible = false
binding.action.visible = false
}
@Suppress("CascadeIf")
if (PlayServicesUtil.getPlayServicesStatus(requireContext()) != PlayServicesUtil.PlayServicesStatus.SUCCESS) {
noSupport.visible = true
action.icon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_open_20)
action.setText(R.string.preferences__donate_to_signal)
action.setOnClickListener {
binding.noSupport.visible = true
binding.action.icon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_open_20)
binding.action.setText(R.string.preferences__donate_to_signal)
binding.action.setOnClickListener {
CommunicationActions.openBrowserLink(requireContext(), getString(R.string.donate_url))
}
} else if (Recipient.self().badges.none { it.category == Badge.Category.Donor && !it.isBoost() && !it.isExpired() }) {
action.setOnClickListener {
binding.action.setOnClickListener {
startActivity(AppSettingsActivity.subscriptions(requireContext()))
}
} else {
action.visible = false
binding.action.visible = false
}
val adapter = MappingAdapter()
LargeBadge.register(adapter)
pager.adapter = adapter
binding.pager.adapter = adapter
adapter.submitList(listOf(LargeBadge.EmptyModel()))
TabLayoutMediator(tabs, pager) { _, _ ->
TabLayoutMediator(binding.tabLayout, binding.pager) { _, _ ->
}.attach()
pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
binding.pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
if (adapter.getModel(position).map { it is LargeBadge.Model }.orElse(false)) {
viewModel.onPageSelected(position)
@@ -101,7 +98,8 @@ class ViewBadgeBottomSheetDialogFragment : FixedRoundedCornerBottomSheetDialogFr
dismissAllowingStateLoss()
}
tabs.visible = state.allBadgesVisibleOnProfile.size > 1
binding.tabLayout.visible = state.allBadgesVisibleOnProfile.size > 1
binding.singlePageSpace.visible = state.allBadgesVisibleOnProfile.size > 1
var maxLines = 3
state.allBadgesVisibleOnProfile.forEach { badge ->
@@ -117,8 +115,8 @@ class ViewBadgeBottomSheetDialogFragment : FixedRoundedCornerBottomSheetDialogFr
}
) {
val stateSelectedIndex = state.allBadgesVisibleOnProfile.indexOf(state.selectedBadge)
if (state.selectedBadge != null && pager.currentItem != stateSelectedIndex) {
pager.currentItem = stateSelectedIndex
if (state.selectedBadge != null && binding.pager.currentItem != stateSelectedIndex) {
binding.pager.currentItem = stateSelectedIndex
}
}
}