Skip to content

Commit

Permalink
PAY-1764: Update UI with for payment method using PaymentSheet (#1666)
Browse files Browse the repository at this point in the history
  • Loading branch information
Arkariang authored Jul 28, 2022
1 parent d57353a commit 373d957
Show file tree
Hide file tree
Showing 19 changed files with 209 additions and 119 deletions.
2 changes: 1 addition & 1 deletion app/external_version_code.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2013150891
2013150892
2 changes: 1 addition & 1 deletion app/external_version_name.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.3.0
3.4.0
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ fun Project.updateProjectWith(config: Config, user: User?): Project {
*
* @return boolean that represents if the card type is available
*/
fun Project.acceptedCardType(cardType: CreditCardTypes) = this.availableCardTypes()?.contains(cardType.rawValue()) ?: false
fun Project.acceptedCardType(cardType: CreditCardTypes?) = this.availableCardTypes()?.contains(cardType?.rawValue()) ?: false

/**
* Combines each project in the list with the discovery param
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ import type.CreditCardTypes
import java.util.Date

object StoredCardFactory {

fun fromPaymentSheetCard(): StoredCard {
return StoredCard.builder()
.lastFourDigits("1234")
.resourceId(1234)
.clientSetupId("ClientSetupId")
.build()
}

@JvmStatic
fun discoverCard(): StoredCard {
return StoredCard.builder()
Expand Down
54 changes: 26 additions & 28 deletions app/src/main/java/com/kickstarter/models/StoredCard.kt
Original file line number Diff line number Diff line change
@@ -1,40 +1,49 @@
package com.kickstarter.models

import android.os.Parcelable
import com.kickstarter.R
import com.stripe.android.model.CardBrand
import kotlinx.parcelize.Parcelize
import type.CreditCardTypes
import java.util.Date

@Parcelize
class StoredCard private constructor(
private val id: String,
private val expiration: Date,
private val lastFourDigits: String,
private val type: CreditCardTypes
private val id: String?,
private val expiration: Date?,
private val lastFourDigits: String?,
private val type: CreditCardTypes?,
private val resourceId: Int?,
private val clientSetupId: String?
) : Parcelable {
fun id() = this.id
fun expiration() = this.expiration
fun lastFourDigits() = this.lastFourDigits
fun type() = this.type
fun resourceId() = this.resourceId
fun clientSetupId() = this.clientSetupId

@Parcelize
data class Builder(
private var id: String = "0L",
private var lastFourDigits: String = "",
private var expiration: Date = Date(),
private var type: CreditCardTypes = CreditCardTypes.`$UNKNOWN`
private var id: String? = "0L",
private var lastFourDigits: String? = "",
private var expiration: Date? = null,
private var type: CreditCardTypes? = CreditCardTypes.`$UNKNOWN`,
private var resourceId: Int? = null,
private var clientSetupId: String? = null
) : Parcelable {
fun id(id: String) = apply { this.id = id }
fun lastFourDigits(lastFourDigits: String) = apply { this.lastFourDigits = lastFourDigits }
fun expiration(expiration: Date) = apply { this.expiration = expiration }
fun type(type: CreditCardTypes) = apply { this.type = type }
fun id(id: String?) = apply { this.id = id }
fun lastFourDigits(lastFourDigits: String?) = apply { this.lastFourDigits = lastFourDigits }
fun expiration(expiration: Date?) = apply { this.expiration = expiration }
fun type(type: CreditCardTypes?) = apply { this.type = type }
fun resourceId(resourceId: Int?) = apply { this.resourceId = resourceId }
fun clientSetupId(clientSetupId: String?) = apply { this.clientSetupId = clientSetupId }
fun build() = StoredCard(
id = id,
lastFourDigits = lastFourDigits,
expiration = expiration,
type = type
type = type,
resourceId = resourceId,
clientSetupId = clientSetupId
)
}

Expand All @@ -44,7 +53,9 @@ class StoredCard private constructor(
equals = id() == obj.id() &&
lastFourDigits() == obj.lastFourDigits() &&
expiration() == obj.expiration() &&
type() == obj.type()
type() == obj.type() &&
resourceId() == obj.resourceId() &&
clientSetupId() == obj.clientSetupId()
}
return equals
}
Expand All @@ -66,19 +77,6 @@ class StoredCard private constructor(
return Builder()
}

internal fun getCardTypeDrawable(cardType: CreditCardTypes): Int {
return when (cardType) {
CreditCardTypes.AMEX -> R.drawable.amex_md
CreditCardTypes.DINERS -> R.drawable.diners_md
CreditCardTypes.DISCOVER -> R.drawable.discover_md
CreditCardTypes.JCB -> R.drawable.jcb_md
CreditCardTypes.MASTERCARD -> R.drawable.mastercard_md
CreditCardTypes.UNION_PAY -> R.drawable.union_pay_md
CreditCardTypes.VISA -> R.drawable.visa_md
else -> R.drawable.generic_bank_md
}
}

internal fun issuer(cardType: CreditCardTypes): String {
return when (cardType) {
CreditCardTypes.AMEX -> CardBrand.AmericanExpress.code
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
@file:JvmName("StoredCard")
package com.kickstarter.models.extensions

import com.kickstarter.R
import com.kickstarter.models.PaymentSource
import com.kickstarter.models.StoredCard
import type.CreditCardTypes

fun StoredCard.getCardTypeDrawable(): Int {
val cardType = this.type() ?: CreditCardTypes.`$UNKNOWN`
val resourceId = this.expiration()?.let {
getCardTypeDrawable(cardType)
} ?: this.resourceId() ?: R.drawable.generic_bank_md

return resourceId
}

fun StoredCard.isFromPaymentSheet(): Boolean {
return this.type() == CreditCardTypes.`$UNKNOWN` &&
this.lastFourDigits()?.isNotEmpty() ?: false &&
this.clientSetupId()?.isNotEmpty() ?: false
}

fun PaymentSource.getCardTypeDrawable(): Int {
val type = CreditCardTypes.safeValueOf(this.type())
return getCardTypeDrawable(type)
}

fun getCardTypeDrawable(cardType: CreditCardTypes): Int {
return when (cardType) {
CreditCardTypes.AMEX -> R.drawable.amex_md
CreditCardTypes.DINERS -> R.drawable.diners_md
CreditCardTypes.DISCOVER -> R.drawable.discover_md
CreditCardTypes.JCB -> R.drawable.jcb_md
CreditCardTypes.MASTERCARD -> R.drawable.mastercard_md
CreditCardTypes.UNION_PAY -> R.drawable.union_pay_md
CreditCardTypes.VISA -> R.drawable.visa_md
else -> R.drawable.generic_bank_md
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ class ProjectPageActivity :
}

override fun cardSaved(storedCard: StoredCard) {
pledgeFragment()?.cardAdded(storedCard)
pledgeFragmentLegacy()?.cardAdded(storedCard)
supportFragmentManager.popBackStack()
}

Expand Down Expand Up @@ -599,8 +599,8 @@ class ProjectPageActivity :
finish()
}

private fun pledgeFragment() = supportFragmentManager
.findFragmentByTag(PledgeFragment::class.java.simpleName) as PledgeFragment?
private fun pledgeFragmentLegacy() = supportFragmentManager
.findFragmentByTag(PledgeFragmentLegacy::class.java.simpleName) as PledgeFragmentLegacy?

private fun renderProject(backingFragment: BackingFragment, rewardsFragment: RewardsFragment, projectData: ProjectData) {
rewardsFragment.configureWith(projectData)
Expand Down
11 changes: 9 additions & 2 deletions app/src/main/java/com/kickstarter/ui/fragments/PledgeFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class PledgeFragment :
private lateinit var adapter: ShippingRulesAdapter
private var headerAdapter = ExpandableHeaderAdapter()
private var isExpanded = false
private var setupClientId: String = ""
private lateinit var flowController: PaymentSheet.FlowController

private var binding: FragmentPledgeBinding? = null
Expand Down Expand Up @@ -566,6 +567,7 @@ class PledgeFragment :
.compose(observeForUI())
.compose(bindToLifecycle())
.subscribe {
setupClientId = it
flowControllerPresentPaymentOption(it)
}

Expand Down Expand Up @@ -664,8 +666,13 @@ class PledgeFragment :
// Update the UI with the returned PaymentOption
private fun onPaymentOption(paymentOption: PaymentOption?) {
paymentOption?.let {
// TODO: add input to viewModel with the paymentOption to populate the UI for the new payment method in PAY-1764
Timber.d(" ${this.javaClass.canonicalName} onPaymentOption with $paymentOption")
val storedCard = StoredCard.Builder(
lastFourDigits = paymentOption.label.takeLast(4),
resourceId = paymentOption.drawableResourceId,
clientSetupId = setupClientId
).build()
this.viewModel.inputs.cardSaved(storedCard)
Timber.d(" ${this.javaClass.canonicalName} onPaymentOption with ${storedCard.lastFourDigits()} and ${storedCard.clientSetupId()}")
flowController.confirm()
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.kickstarter.ui.viewholders

import android.util.Pair
import androidx.core.view.isGone
import com.kickstarter.R
import com.kickstarter.databinding.ItemRewardSelectedCardBinding
import com.kickstarter.libs.rx.transformers.Transformers.observeForUI
Expand All @@ -24,6 +25,13 @@ class RewardCardSelectedViewHolder(val binding: ItemRewardSelectedCardBinding) :
.compose(observeForUI())
.subscribe { setExpirationDateTextView(it) }

this.viewModel.outputs.expirationIsGone()
.compose(bindToLifecycle())
.compose(observeForUI())
.subscribe {
binding.rewardCardDetails.rewardCardExpirationDate.isGone = it
}

this.viewModel.outputs.issuerImage()
.compose(bindToLifecycle())
.compose(observeForUI())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.kickstarter.ui.viewholders

import android.util.Pair
import androidx.core.content.ContextCompat
import androidx.core.view.isGone
import com.kickstarter.R
import com.kickstarter.databinding.ItemRewardUnselectedCardBinding
import com.kickstarter.libs.rx.transformers.Transformers.observeForUI
Expand Down Expand Up @@ -29,6 +30,13 @@ class RewardCardUnselectedViewHolder(val binding: ItemRewardUnselectedCardBindin
.compose(observeForUI())
.subscribe { setExpirationDateText(it) }

this.viewModel.outputs.expirationIsGone()
.compose(bindToLifecycle())
.compose(observeForUI())
.subscribe {
this.binding.rewardCardDetailsLayout.rewardCardExpirationDate.isGone = it
}

this.viewModel.outputs.isClickable()
.compose(bindToLifecycle())
.compose(observeForUI())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.kickstarter.models.Project
import com.kickstarter.models.Reward
import com.kickstarter.models.StoredCard
import com.kickstarter.models.User
import com.kickstarter.models.extensions.getCardTypeDrawable
import com.kickstarter.ui.data.PledgeStatusData
import com.kickstarter.ui.data.ProjectData
import com.kickstarter.ui.fragments.BackingFragment
Expand Down Expand Up @@ -507,7 +508,7 @@ interface BackingFragmentViewModel {
return when (CreditCardPaymentType.safeValueOf(paymentSource.paymentType())) {
CreditCardPaymentType.ANDROID_PAY -> R.drawable.google_pay_mark
CreditCardPaymentType.APPLE_PAY -> R.drawable.apple_pay_mark
CreditCardPaymentType.CREDIT_CARD -> StoredCard.getCardTypeDrawable(CreditCardTypes.safeValueOf(paymentSource.type()))
CreditCardPaymentType.CREDIT_CARD -> paymentSource.getCardTypeDrawable()
else -> R.drawable.generic_bank_md
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import android.util.Pair
import com.kickstarter.libs.ActivityViewModel
import com.kickstarter.libs.Environment
import com.kickstarter.libs.rx.transformers.Transformers.combineLatestPair
import com.kickstarter.libs.utils.ObjectUtils
import com.kickstarter.libs.utils.extensions.isErrored
import com.kickstarter.models.Backing
import com.kickstarter.models.Project
import com.kickstarter.models.StoredCard
import com.kickstarter.models.extensions.getCardTypeDrawable
import com.kickstarter.ui.viewholders.KSViewHolder
import com.stripe.android.model.Card
import rx.Observable
Expand All @@ -26,6 +28,9 @@ interface BaseRewardCardViewHolderViewModel {
/** Emits the expiration date for a credit card. */
fun expirationDate(): Observable<String>

/** Emits when the expiration date label should be gone. */
fun expirationIsGone(): Observable<Boolean>

/** Emits the name of the card issuer from [Card.CardBrand]. */
fun issuer(): Observable<String>

Expand All @@ -47,6 +52,7 @@ interface BaseRewardCardViewHolderViewModel {
private val issuerImage = BehaviorSubject.create<Int>()
private val lastFour = BehaviorSubject.create<String>()
private val retryCopyIsVisible = PublishSubject.create<Boolean>()
private val expirationIsGone = PublishSubject.create<Boolean>()

private val sdf = SimpleDateFormat(StoredCard.DATE_FORMAT, Locale.getDefault())

Expand All @@ -57,20 +63,29 @@ interface BaseRewardCardViewHolderViewModel {

card
.map { it.expiration() }
.filter { ObjectUtils.isNotNull(it) }
.map { sdf.format(it).toString() }
.subscribe(this.expirationDate)

card
.map { it.expiration() }
.subscribe {
this.expirationIsGone.onNext(it == null)
}

card
.map { it.lastFourDigits() }
.subscribe(this.lastFour)

card
.map { it.type() }
.map { StoredCard.getCardTypeDrawable(it) }
.filter { ObjectUtils.isNotNull(it) }
.map { it.getCardTypeDrawable() }
.subscribe(this.issuerImage)

card
.map { it.type() }
.filter { ObjectUtils.isNotNull(it) }
.map { requireNotNull(it) }
.map { StoredCard.issuer(it) }
.subscribe(this.issuer)

Expand Down Expand Up @@ -100,5 +115,7 @@ interface BaseRewardCardViewHolderViewModel {
override fun lastFour(): Observable<String> = this.lastFour

override fun retryCopyIsVisible(): Observable<Boolean> = this.retryCopyIsVisible

override fun expirationIsGone(): Observable<Boolean> = this.expirationIsGone
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package com.kickstarter.viewmodels
import com.kickstarter.libs.ActivityViewModel
import com.kickstarter.libs.Environment
import com.kickstarter.libs.rx.transformers.Transformers.takeWhen
import com.kickstarter.libs.utils.ObjectUtils
import com.kickstarter.models.StoredCard
import com.kickstarter.models.extensions.getCardTypeDrawable
import com.kickstarter.ui.viewholders.PaymentMethodsViewHolder
import com.stripe.android.model.Card
import rx.Observable
Expand Down Expand Up @@ -58,6 +60,7 @@ interface PaymentMethodsViewHolderViewModel {
init {
this.card
.map { it.expiration() }
.filter { ObjectUtils.isNotNull(it) }
.map { sdf.format(it).toString() }
.subscribe(this.expirationDate)

Expand All @@ -71,12 +74,14 @@ interface PaymentMethodsViewHolderViewModel {
.subscribe(this.lastFour)

this.card
.map { it.type() }
.map { StoredCard.getCardTypeDrawable(it) }
.map { requireNotNull(it) }
.map { it.getCardTypeDrawable() }
.subscribe(this.issuerImage)

this.card
.map { it.type() }
.filter { ObjectUtils.isNotNull(it) }
.map { requireNotNull(it) }
.map { StoredCard.issuer(it) }
.subscribe(this.issuer)
}
Expand Down
Loading

0 comments on commit 373d957

Please sign in to comment.