Skip to content

Commit

Permalink
[馃挸] Unavailable cards in pledge screen (#579)
Browse files Browse the repository at this point in the history
* Added ProjectUtils.acceptedCardType to determine whether a card is accepted for a project's currency.
Added StoredCard.usdCardTypes and nonUsdCardTypes but will be replaced by API.
RewardCardAdapter now takes in a project so the cards can be disabled if they're not accepted.
Disabling non accepted cards in PledgeFragment.
Added button_select for a disablable soft black button.
Updated card expiration copy.
Manually added Not available string because I am impatient.
Tests.

* Merging in master and updating tests. Some naming feedback.
  • Loading branch information
eoji committed Aug 7, 2019
1 parent 12ba36b commit e793454
Show file tree
Hide file tree
Showing 21 changed files with 344 additions and 73 deletions.
11 changes: 11 additions & 0 deletions app/src/main/java/com/kickstarter/libs/utils/ProjectUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

import com.kickstarter.R;
import com.kickstarter.libs.KSString;
import com.kickstarter.libs.models.Country;
import com.kickstarter.models.Project;
import com.kickstarter.models.StoredCard;
import com.kickstarter.models.User;
import com.kickstarter.services.DiscoveryParams;

Expand All @@ -17,10 +19,19 @@

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import type.CreditCardTypes;

public final class ProjectUtils {
private ProjectUtils() {}

public static boolean acceptedCardType(final @NonNull CreditCardTypes type, final @NonNull Project project) {
if (project.currency().equals(Country.US.getCurrencyCode())) {
return StoredCard.Companion.getUsdCardTypes().contains(type);
} else {
return StoredCard.Companion.getNonUsdCardTypes().contains(type);
}
}

public static List<Pair<Project, DiscoveryParams>> combineProjectsAndParams(final @NonNull List<Project> projects, final @NonNull DiscoveryParams params) {
final ArrayList<Pair<Project, DiscoveryParams>> projectAndParams = new ArrayList<>(projects.size());
for (int i = 0; i < projects.size(); i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ private LocationFactory() {}
.build();
}

public static @NonNull Location mexico() {
return Location.builder()
.id(638242)
.displayableName("Mexico City, Mexico")
.name("Mexico City")
.state("Mexico")
.country("MX")
.build();
}

public static @NonNull Location sydney() {
return Location.builder()
.id(1105779)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ private ProjectFactory() {}
.currentCurrency("MXN")
.currencySymbol("$")
.currency("MXN")
.location(LocationFactory.mexico())
.staticUsdRate(0.75f)
.fxRate(0.75f)
.build();
Expand Down
14 changes: 13 additions & 1 deletion app/src/main/java/com/kickstarter/models/StoredCard.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.kickstarter.models
import android.os.Parcelable
import auto.parcel.AutoParcel
import com.kickstarter.R
import com.kickstarter.libs.qualifiers.AutoGson
import type.CreditCardTypes
import java.util.*

Expand Down Expand Up @@ -31,6 +30,19 @@ abstract class StoredCard : Parcelable {
return AutoParcel_StoredCard.Builder()
}

private val allowedCardTypes = listOf(CreditCardTypes.AMEX,
CreditCardTypes.DINERS,
CreditCardTypes.DISCOVER,
CreditCardTypes.JCB,
CreditCardTypes.MASTERCARD,
CreditCardTypes.UNION_PAY,
CreditCardTypes.VISA)

val usdCardTypes = allowedCardTypes;
val nonUsdCardTypes = listOf(CreditCardTypes.AMEX,
CreditCardTypes.MASTERCARD,
CreditCardTypes.VISA)

internal fun getCardTypeDrawable(cardType: CreditCardTypes): Int {
return when (cardType) {
CreditCardTypes.AMEX -> R.drawable.amex_md
Expand Down
15 changes: 10 additions & 5 deletions app/src/main/java/com/kickstarter/ui/adapters/RewardCardAdapter.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.kickstarter.ui.adapters

import android.util.Pair
import android.view.View
import androidx.annotation.NonNull
import androidx.recyclerview.widget.RecyclerView
import com.kickstarter.R
import com.kickstarter.models.Project
import com.kickstarter.models.StoredCard
import com.kickstarter.ui.data.CardState
import com.kickstarter.ui.viewholders.*
import rx.Observable

class RewardCardAdapter(private val delegate: Delegate) : KSAdapter() {
interface Delegate : RewardCardViewHolder.Delegate, RewardPledgeCardViewHolder.Delegate, RewardAddCardViewHolder.Delegate
Expand Down Expand Up @@ -47,9 +49,12 @@ class RewardCardAdapter(private val delegate: Delegate) : KSAdapter() {
}
}

fun takeCards(@NonNull cards: List<StoredCard>) {
fun takeCards(cards: List<StoredCard>, project: Project) {
sections().clear()
addSection(cards)
addSection(Observable.from(cards)
.map { Pair(it, project) }
.toList().toBlocking().single()
)
addSection(listOf(null))
notifyDataSetChanged()
}
Expand All @@ -69,10 +74,10 @@ class RewardCardAdapter(private val delegate: Delegate) : KSAdapter() {
notifyItemChanged(position)
}

fun insertCard(storedCard: StoredCard) : Int {
fun insertCard(storedCardAndProject: Pair<StoredCard, Project>) : Int {
val storedCards = sections()[0]
val position = 0
storedCards.add(position, storedCard)
storedCards.add(position, storedCardAndProject)
notifyItemInserted(position)

return position
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,10 @@ class PledgeFragment : BaseFragment<PledgeFragmentViewModel.ViewModel>(), Reward
.compose(observeForUI())
.subscribe { setTextColor(it, total_amount, total_symbol_start, total_symbol_end) }

this.viewModel.outputs.cards()
this.viewModel.outputs.cardsAndProject()
.compose(bindToLifecycle())
.compose(observeForUI())
.subscribe { (cards_recycler.adapter as RewardCardAdapter).takeCards(it) }
.subscribe { (cards_recycler.adapter as RewardCardAdapter).takeCards(it.first, it.second) }

this.viewModel.outputs.addedCard()
.compose(bindToLifecycle())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.kickstarter.ui.viewholders

import android.util.Pair
import android.view.View
import com.kickstarter.R
import com.kickstarter.libs.KSString
import com.kickstarter.libs.rx.transformers.Transformers.observeForUI
import com.kickstarter.libs.utils.ViewUtils
import com.kickstarter.models.Project
import com.kickstarter.models.StoredCard
import com.kickstarter.viewmodels.RewardCardViewHolderViewModel
import kotlinx.android.synthetic.main.item_reward_credit_card.view.*
Expand All @@ -17,15 +20,16 @@ class RewardCardViewHolder(val view : View, val delegate : Delegate) : KSViewHol
private val viewModel: RewardCardViewHolderViewModel.ViewModel = RewardCardViewHolderViewModel.ViewModel(environment())
private val ksString: KSString = environment().ksString()

private val cardNotAllowedString = this.context().getString(R.string.You_cant_use_this_credit_card_to_back_a_project_from_project_country)
private val creditCardExpirationString = this.context().getString(R.string.Credit_card_expiration)
private val cardEndingInString = this.context().getString(R.string.Card_ending_in_last_four)
private val endingInString = this.context().getString(R.string.Ending_in_last_four)

init {

this.viewModel.outputs.expirationDate()
.compose(bindToLifecycle())
.compose(observeForUI())
.subscribe { setExpirationDateTextView(it) }
.subscribe { setExpirationDateText(it) }

this.viewModel.outputs.issuerImage()
.compose(bindToLifecycle())
Expand All @@ -35,25 +39,51 @@ class RewardCardViewHolder(val view : View, val delegate : Delegate) : KSViewHol
this.viewModel.outputs.lastFour()
.compose(bindToLifecycle())
.compose(observeForUI())
.subscribe { setLastFourTextView(it) }
.subscribe { setLastFourText(it) }

this.viewModel.outputs.buttonCTA()
.compose(bindToLifecycle())
.compose(observeForUI())
.subscribe { this.view.select_button.setText(it) }

this.viewModel.outputs.buttonEnabled()
.compose(bindToLifecycle())
.compose(observeForUI())
.subscribe { this.view.select_button.isEnabled = it }

this.viewModel.outputs.notAvailableCopyIsVisible()
.compose(bindToLifecycle())
.compose(observeForUI())
.subscribe { ViewUtils.setGone(this.view.card_not_allowed_warning, !it) }

this.viewModel.outputs.projectCountry()
.compose(bindToLifecycle())
.compose(observeForUI())
.subscribe { setCardNotAllowedWarningText(it) }

this.view.select_button.setOnClickListener {
this.delegate.selectCardButtonClicked(adapterPosition)
}
}

override fun bindData(data: Any?) {
val card = requireNotNull(data as StoredCard)
this.viewModel.inputs.configureWith(card)
@Suppress("UNCHECKED_CAST")
val cardAndProject = requireNotNull(data) as Pair<StoredCard, Project>
this.viewModel.inputs.configureWith(cardAndProject)
}

private fun setCardNotAllowedWarningText(country: String) {
this.view.card_not_allowed_warning.text = this.ksString.format(this.cardNotAllowedString,
"project_country", country)
}

private fun setExpirationDateTextView(date: String) {
private fun setExpirationDateText(date: String) {
this.view.reward_card_expiration_date.text = this.ksString.format(this.creditCardExpirationString,
"expiration_date", date)
}

private fun setLastFourTextView(lastFour: String) {
this.view.reward_card_last_four.text = this.ksString.format(this.cardEndingInString, "last_four", lastFour)
private fun setLastFourText(lastFour: String) {
this.view.reward_card_last_four.text = this.ksString.format(this.endingInString, "last_four", lastFour)
}

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.kickstarter.ui.viewholders

import android.util.Pair
import android.view.View
import com.kickstarter.R
import com.kickstarter.libs.KSString
import com.kickstarter.libs.rx.transformers.Transformers.observeForUI
import com.kickstarter.libs.utils.AnimationUtils
import com.kickstarter.models.Project
import com.kickstarter.models.StoredCard
import com.kickstarter.viewmodels.RewardLoadingCardViewHolderViewModel
import kotlinx.android.synthetic.main.item_reward_loading_card.view.*
Expand All @@ -15,7 +17,7 @@ class RewardLoadingCardViewHolder(val view: View) : KSViewHolder(view) {
private val ksString: KSString = environment().ksString()

private val creditCardExpirationString = this.context().getString(R.string.Credit_card_expiration)
private val cardEndingInString = this.context().getString(R.string.Card_ending_in_last_four)
private val endingInString = this.context().getString(R.string.Ending_in_last_four)

init {

Expand Down Expand Up @@ -45,8 +47,9 @@ class RewardLoadingCardViewHolder(val view: View) : KSViewHolder(view) {
}

override fun bindData(data: Any?) {
val card = requireNotNull(data as StoredCard)
this.viewModel.inputs.configureWith(card)
@Suppress("UNCHECKED_CAST")
val cardAndProject = requireNotNull(data) as Pair<StoredCard, Project>
this.viewModel.inputs.configureWith(cardAndProject)
}

private fun setExpirationDateTextView(date: String) {
Expand All @@ -55,7 +58,7 @@ class RewardLoadingCardViewHolder(val view: View) : KSViewHolder(view) {
}

private fun setLastFourTextView(lastFour: String) {
this.view.reward_loading_card_last_four.text = this.ksString.format(this.cardEndingInString, "last_four", lastFour)
this.view.reward_loading_card_last_four.text = this.ksString.format(this.endingInString, "last_four", lastFour)
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.kickstarter.ui.viewholders

import android.util.Pair
import android.view.View
import com.kickstarter.R
import com.kickstarter.libs.KSString
import com.kickstarter.libs.rx.transformers.Transformers.observeForUI
import com.kickstarter.models.Project
import com.kickstarter.models.StoredCard
import com.kickstarter.viewmodels.RewardPledgeCardViewHolderViewModel
import kotlinx.android.synthetic.main.item_reward_pledge_card.view.*
Expand All @@ -16,7 +18,7 @@ class RewardPledgeCardViewHolder(val view : View, val delegate : Delegate) : KSV
}

private val creditCardExpirationString = this.context().getString(R.string.Credit_card_expiration)
private val cardEndingInString = this.context().getString(R.string.Card_ending_in_last_four)
private val endingInString = this.context().getString(R.string.Ending_in_last_four)

private val viewModel: RewardPledgeCardViewHolderViewModel.ViewModel = RewardPledgeCardViewHolderViewModel.ViewModel(environment())
private val ksString: KSString = environment().ksString()
Expand Down Expand Up @@ -53,8 +55,9 @@ class RewardPledgeCardViewHolder(val view : View, val delegate : Delegate) : KSV
}

override fun bindData(data: Any?) {
val card = requireNotNull(data as StoredCard)
this.viewModel.inputs.configureWith(card)
@Suppress("UNCHECKED_CAST")
val cardAndProject = requireNotNull(data) as Pair<StoredCard, Project>
this.viewModel.inputs.configureWith(cardAndProject)
}

private fun setExpirationDateTextView(date: String) {
Expand All @@ -63,7 +66,7 @@ class RewardPledgeCardViewHolder(val view : View, val delegate : Delegate) : KSV
}

private fun setLastFourTextView(lastFour: String) {
this.view.reward_pledge_card_last_four.text = this.ksString.format(this.cardEndingInString, "last_four", lastFour)
this.view.reward_pledge_card_last_four.text = this.ksString.format(this.endingInString, "last_four", lastFour)
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.kickstarter.viewmodels

import android.util.Pair
import com.kickstarter.libs.ActivityViewModel
import com.kickstarter.libs.Environment
import com.kickstarter.models.Project
import com.kickstarter.models.StoredCard
import com.kickstarter.ui.viewholders.KSViewHolder
import rx.Observable
Expand All @@ -12,8 +14,8 @@ import java.util.*

interface BaseRewardCardViewHolderViewModel {
interface Inputs {
/** Call to configure view model with a stored card. */
fun configureWith(storedCard: StoredCard)
/** Call to configure view model with a stored card and project. */
fun configureWith(cardAndProject: Pair<StoredCard, Project>)
}

interface Outputs {
Expand All @@ -31,7 +33,7 @@ interface BaseRewardCardViewHolderViewModel {
}

abstract class ViewModel(val environment: Environment) : ActivityViewModel<KSViewHolder>(environment), Inputs, Outputs {
private val card = PublishSubject.create<StoredCard>()
protected val cardAndProject: PublishSubject<Pair<StoredCard, Project>> = PublishSubject.create()

private val expirationDate = BehaviorSubject.create<String>()
private val id = BehaviorSubject.create<String>()
Expand All @@ -41,22 +43,26 @@ interface BaseRewardCardViewHolderViewModel {
private val sdf = SimpleDateFormat(StoredCard.DATE_FORMAT, Locale.getDefault())

init {
this.card.map { it.id() }

val card = cardAndProject
.map { it.first }

card.map { it.id() }
.subscribe(this.id)

this.card.map { it.expiration() }
card.map { it.expiration() }
.map { sdf.format(it).toString() }
.subscribe { this.expirationDate.onNext(it) }

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

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

}
override fun configureWith(storedCard: StoredCard) = this.card.onNext(storedCard)
override fun configureWith(cardAndProject: Pair<StoredCard, Project>) = this.cardAndProject.onNext(cardAndProject)

override fun id(): Observable<String> = this.id

Expand Down

0 comments on commit e793454

Please sign in to comment.