Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MBL-1744: Refactor Fix Pledge Flow #2142

Merged
merged 9 commits into from
Oct 7, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ enum class FlagKey(val key: String) {
ANDROID_ENCRYPT("android_encrypt_token"),
ANDROID_STRIPE_LINK("android_stripe_link"),
ANDROID_PLEDGED_PROJECTS_OVERVIEW("android_pledged_projects_overview"),
ANDROID_PLEDGE_REDEMPTION("android_pledge_redemption")
ANDROID_PLEDGE_REDEMPTION("android_pledge_redemption"),
ANDROID_FIX_PLEDGE_REFACTOR("android_fix_pledge_refactor")
}

fun FeatureFlagClient.getFetchInterval(): Long =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@ import com.kickstarter.ui.fragments.PledgeFragment

fun Fragment.selectPledgeFragment(
pledgeData: PledgeData,
pledgeReason: PledgeReason
pledgeReason: PledgeReason,
ffEnabled: Boolean = false
): Fragment {
val fragment = if (pledgeReason == PledgeReason.FIX_PLEDGE) {
PledgeFragment()
} else CrowdfundCheckoutFragment()
val fragment = when (pledgeReason) {
PledgeReason.FIX_PLEDGE ->
if (ffEnabled) CrowdfundCheckoutFragment()
else PledgeFragment()
else -> CrowdfundCheckoutFragment()
}

return fragment.withData(pledgeData, pledgeReason)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ fun getUpdateBackingData(
backing: Backing,
amount: String? = null,
locationId: String? = null,
rewardsList: List<Reward> = listOf(),
rewardsList: List<Reward>? = null,
pMethod: StoredCard? = null
): UpdateBackingData {
return pMethod?.let { card ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import com.kickstarter.libs.Environment
import com.kickstarter.libs.KSString
import com.kickstarter.libs.MessagePreviousScreenType
import com.kickstarter.libs.ProjectPagerTabs
import com.kickstarter.libs.featureflag.FeatureFlagClientType
import com.kickstarter.libs.featureflag.FlagKey
import com.kickstarter.libs.rx.transformers.Transformers
import com.kickstarter.libs.utils.ApplicationUtils
Expand Down Expand Up @@ -175,6 +176,9 @@ class ProjectPageActivity :
env
}

val ffClient = requireNotNull(environment?.featureFlagClient())
ffClient.activate(this)
Copy link
Contributor Author

@Arkariang Arkariang Oct 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calling activate when the activity is loaded will minimize the fist installation gap when the information requested to firebase remote config has not yet been applied.


flowController = PaymentSheet.FlowController.create(
activity = this,
paymentOptionCallback = ::onPaymentOption,
Expand Down Expand Up @@ -369,7 +373,7 @@ class ProjectPageActivity :

this.viewModel.outputs.showUpdatePledge()
.observeOn(AndroidSchedulers.mainThread())
.subscribe { showPledgeFragment(it) }
.subscribe { showPledgeFragment(it, ffClient) }
.addToDisposable(disposables)

this.viewModel.outputs.startRootCommentsActivity()
Expand Down Expand Up @@ -1075,8 +1079,12 @@ class ProjectPageActivity :
.show()
}

private fun showPledgeFragment(pledgeDataAndPledgeReason: Pair<PledgeData, PledgeReason>) {
val pledgeFragment = this.selectPledgeFragment(pledgeDataAndPledgeReason.first, pledgeDataAndPledgeReason.second)
private fun showPledgeFragment(
pledgeDataAndPledgeReason: Pair<PledgeData, PledgeReason>,
ffClient: FeatureFlagClientType
) {
val ffEnabled = ffClient.getBoolean(FlagKey.ANDROID_FIX_PLEDGE_REFACTOR)
val pledgeFragment = this.selectPledgeFragment(pledgeDataAndPledgeReason.first, pledgeDataAndPledgeReason.second, ffEnabled)
val tag = pledgeFragment::class.java.simpleName
supportFragmentManager
.beginTransaction()
Expand Down
10 changes: 7 additions & 3 deletions app/src/main/java/com/kickstarter/ui/extensions/ActivityExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,14 @@ fun Activity.hideKeyboard() {
fun Activity.selectPledgeFragment(
pledgeData: PledgeData,
pledgeReason: PledgeReason,
ffEnabled: Boolean = false
): Fragment {
val fragment = if (pledgeReason == PledgeReason.FIX_PLEDGE) {
PledgeFragment()
} else CrowdfundCheckoutFragment()
val fragment = when (pledgeReason) {
PledgeReason.FIX_PLEDGE ->
if (ffEnabled) CrowdfundCheckoutFragment()
else PledgeFragment()
else -> CrowdfundCheckoutFragment()
}
return fragment.withData(pledgeData, pledgeReason)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.kickstarter.R
import com.kickstarter.databinding.FragmentBackingAddonsBinding
import com.kickstarter.libs.featureflag.FeatureFlagClientType
import com.kickstarter.libs.featureflag.FlagKey
import com.kickstarter.libs.utils.extensions.getEnvironment
import com.kickstarter.libs.utils.extensions.selectPledgeFragment
import com.kickstarter.ui.ArgumentsKey
Expand Down Expand Up @@ -42,13 +44,15 @@ class BackingAddOnsFragment : Fragment() {
binding = FragmentBackingAddonsBinding.inflate(inflater, container, false)
val view = binding?.root
binding?.composeView?.apply {
val env = this?.context?.getEnvironment()?.let { env ->
val env = this.context?.getEnvironment()?.let { env ->
viewModelFactoryC = AddOnsViewModel.Factory(env, bundle = arguments)
// viewModelFactory = BackingAddOnsFragmentViewModel.Factory(env, bundle = arguments)
viewModelC.provideBundle(arguments)
env
}

val ffClient = requireNotNull(env?.featureFlagClient())
activity?.let { ffClient.activate(it) }

viewModelC.provideErrorAction { message ->
activity?.runOnUiThread {
showErrorToast(
Expand Down Expand Up @@ -95,7 +99,8 @@ class BackingAddOnsFragment : Fragment() {
viewModelC.getPledgeDataAndReason()?.let { pDataAndReason ->
showPledgeFragment(
pledgeData = pDataAndReason.first,
pledgeReason = pDataAndReason.second
pledgeReason = pDataAndReason.second,
ffClient = ffClient
)
}
} else {
Expand All @@ -116,8 +121,13 @@ class BackingAddOnsFragment : Fragment() {
return view
}

private fun showPledgeFragment(pledgeData: PledgeData, pledgeReason: PledgeReason) {
val fragment = this.selectPledgeFragment(pledgeData, pledgeReason)
private fun showPledgeFragment(
pledgeData: PledgeData,
pledgeReason: PledgeReason,
ffClient: FeatureFlagClientType
) {
val ffEnabled = ffClient.getBoolean(FlagKey.ANDROID_FIX_PLEDGE_REFACTOR)
val fragment = this.selectPledgeFragment(pledgeData, pledgeReason, ffEnabled)
parentFragmentManager
.beginTransaction()
.setCustomAnimations(R.anim.slide_up, 0, 0, R.anim.slide_down)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,12 @@ class CrowdfundCheckoutFragment : Fragment() {
(activity as PledgeDelegate?)?.pledgeSuccessfullyCreated(checkoutSuccess)
if (pledgeReason == PledgeReason.UPDATE_PAYMENT)
(activity as PledgeDelegate?)?.pledgePaymentSuccessfullyUpdated()
if (pledgeReason == PledgeReason.UPDATE_REWARD || pledgeReason == PledgeReason.UPDATE_PLEDGE)
if (pledgeReason == PledgeReason.UPDATE_REWARD || pledgeReason == PledgeReason.UPDATE_PLEDGE || pledgeReason == PledgeReason.FIX_PLEDGE)
(activity as PledgeDelegate?)?.pledgeSuccessfullyUpdated()
}
}

KSTheme {
// TODO: update to display local pickup
// TODO: hide bonus support if 0
CheckoutScreen(
rewardsList = getRewardListAndPrices(rwList, environment, project),
selectedRewardsAndAddOns = rwList,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,11 @@ class CrowdfundCheckoutViewModel(val environment: Environment, bundle: Bundle? =
when (flowContext) {
PledgeFlowContext.NEW_PLEDGE,
PledgeFlowContext.CHANGE_REWARD -> getPledgeInfoFrom(pData)
PledgeFlowContext.MANAGE_REWARD -> {
PledgeFlowContext.MANAGE_REWARD,
PledgeFlowContext.FIX_ERRORED_PLEDGE
-> {
backing?.let { getPledgeInfoFrom(it) }
}

else -> {
errorAction.invoke(null)
}
Expand Down Expand Up @@ -363,7 +364,8 @@ class CrowdfundCheckoutViewModel(val environment: Environment, bundle: Bundle? =
PledgeReason.PLEDGE -> createBacking()
PledgeReason.UPDATE_PLEDGE,
PledgeReason.UPDATE_REWARD,
PledgeReason.UPDATE_PAYMENT -> updateBacking()
PledgeReason.UPDATE_PAYMENT,
PledgeReason.FIX_PLEDGE -> updateBacking()
else -> {
errorAction.invoke(null)
}
Expand Down Expand Up @@ -407,6 +409,7 @@ class CrowdfundCheckoutViewModel(val environment: Environment, bundle: Bundle? =
project.backing()?.let { backing ->
val backingData = when (pledgeReason) {
PledgeReason.UPDATE_PAYMENT -> {
// - Update payment should NOT send amounts
val locationId = backing.locationId() ?: 0
val rwl = mutableListOf<Reward>()
backing.reward()?.let {
Expand All @@ -424,7 +427,9 @@ class CrowdfundCheckoutViewModel(val environment: Environment, bundle: Bundle? =
selectedPaymentMethod
)
}
PledgeReason.UPDATE_REWARD -> {
PledgeReason.UPDATE_REWARD,
PledgeReason.UPDATE_PLEDGE -> {
// - Update Reward/Pledge should send ALL newly selected rewards/Locations
val isShippable = pledgeData?.reward()?.let { RewardUtils.isShippable(it) } ?: false
val locationIdOrNull =
if (isShippable) pledgeData?.shippingRule()?.location()?.id().toString()
Expand All @@ -441,9 +446,14 @@ class CrowdfundCheckoutViewModel(val environment: Environment, bundle: Bundle? =
selectedPaymentMethod
)
}
PledgeReason.FIX_PLEDGE, // Managed on PledgeFragment/ViewModel
PledgeReason.FIX_PLEDGE -> {
// - Fix pledge should NOT send amounts/rewardId's/locationId ONLY selected paymentMethod
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

getUpdateBackingData(
backing = backing,
pMethod = selectedPaymentMethod
)
}
PledgeReason.PLEDGE, // Error
PledgeReason.UPDATE_PLEDGE, // Error
PledgeReason.LATE_PLEDGE, // Error
null -> { null }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,33 @@ import org.junit.Test

class FragmentExtTest : KSRobolectricTestCase() {

fun `test fragment is PledgeFragment when fix_pledge and ff disabled`() {
val project = ProjectFactory.project()
val projectData = ProjectDataFactory.project(project)

val pledgeData = PledgeData.builder()
.pledgeFlowContext(PledgeFlowContext.FIX_ERRORED_PLEDGE)
.projectData(projectData)
.build()

val fragment = Fragment().selectPledgeFragment(pledgeData, PledgeReason.FIX_PLEDGE, false)
assertTrue(fragment is PledgeFragment)
}

@Test
fun `test fragment is CrowdfundCheckoutFragment when fix_pledge and ff enabled`() {
val project = ProjectFactory.project()
val projectData = ProjectDataFactory.project(project)

val pledgeData = PledgeData.builder()
.pledgeFlowContext(PledgeFlowContext.FIX_ERRORED_PLEDGE)
.projectData(projectData)
.build()

val fragment = Fragment().selectPledgeFragment(pledgeData, PledgeReason.FIX_PLEDGE, true)
assertTrue(fragment is CrowdfundCheckoutFragment)
}

@Test
fun testFragment_whenData_Null() {
val fragment = Fragment().withData(null, null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,106 @@ class CrowdfundCheckoutViewModelTest : KSRobolectricTestCase() {
segmentTrack.assertValue(EventName.PAGE_VIEWED.eventName)
}

@Test
fun `test fix Pledge Flow`() = runTest {
val shippingRules = ShippingRulesEnvelopeFactory.shippingRules().shippingRules()
val rewardBacked = RewardFactory.rewardWithShipping().toBuilder()
.shippingRules(shippingRules = shippingRules)
.build()

val addOns1Backed = RewardFactory.rewardWithShipping()
.toBuilder()
.isAddOn(true)
.shippingRules(shippingRules)
.build()

val backing = BackingFactory.backing(rewardBacked)
.toBuilder()
.addOns(listOf(addOns1Backed))
.location(shippingRules.first().location())
.locationId(shippingRules.first().location()?.id())
.bonusAmount(5.0)
.amount(44.0)
.shippingAmount(33f)
.paymentSource(PaymentSourceFactory.visa())
.build()

val project = ProjectFactory.project().toBuilder()
.backing(backing)
.isBacking(true)
.rewards(listOf(rewardBacked))
.build()

val cards = listOf(StoredCardFactory.visa(), StoredCardFactory.discoverCard(), StoredCardFactory.fromPaymentSheetCard())

val user = UserFactory.user()
val currentUserV2 = MockCurrentUserV2(initialUser = user)

val projectData = ProjectDataFactory.project(project)

val bundle = Bundle()

val pledgeData = PledgeData.with(
PledgeFlowContext.forPledgeReason(PledgeReason.FIX_PLEDGE),
projectData,
rewardBacked,
bonusAmount = 7.0,
addOns = listOf(addOns1Backed.toBuilder().quantity(5).build())
)

bundle.putParcelable(
ArgumentsKey.PLEDGE_PLEDGE_DATA,
pledgeData
)
bundle.putSerializable(ArgumentsKey.PLEDGE_PLEDGE_REASON, PledgeReason.FIX_PLEDGE)

lateinit var data: UpdateBackingData
val environment = environment().toBuilder()
.apolloClientV2(object : MockApolloClientV2() {
override fun getStoredCards(): Observable<List<StoredCard>> {
return Observable.just(cards)
}

override fun userPrivacy(): Observable<UserPrivacy> {
return Observable.just(
UserPrivacy("", "[email protected]", true, true, true, true, "USD")
)
}

override fun updateBacking(updateBackingData: UpdateBackingData): Observable<Checkout> {
data = updateBackingData
val checkout = Checkout.builder().id(77L).backing(Checkout.Backing.builder().requiresAction(false).clientSecret("clientSecret").build()).build()
return Observable.just(checkout)
}
})
.currentUserV2(currentUserV2)
.build()

setUpEnvironment(environment)

val dispatcher = UnconfinedTestDispatcher(testScheduler)
val checkout = mutableListOf<Pair<CheckoutData, PledgeData>>()

backgroundScope.launch(dispatcher) {
viewModel.provideScopeAndDispatcher(this, dispatcher)
viewModel.provideBundle(bundle)
viewModel.userChangedPaymentMethodSelected(cards.first())
viewModel.pledgeOrUpdatePledge()
viewModel.checkoutResultState.toList(checkout)
}
advanceUntilIdle()

assertEquals(data.rewardsIds?.size, null)
assertEquals(data.rewardsIds?.first(), null)
assertEquals(data.amount, null)

// Fix pledge flow should only payment ID anything else
assertEquals(data.rewardsIds?.size, null)
assertEquals(data.rewardsIds?.first(), null)
assertEquals(data.amount, null)
assertEquals(data.paymentSourceId, cards.first().id())
}

@Test
fun `test adding new paymentMethod throw Stripe's paymentSheet`() = runTest {

Expand Down