Skip to content

Commit

Permalink
MBL-2000 Fix Error State for PLOT (#2208)
Browse files Browse the repository at this point in the history
  • Loading branch information
jlplks authored Jan 23, 2025
1 parent 31d4d5f commit 0f22c14
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ fun Backing.isBacked(reward: Reward): Boolean {

fun Backing.isErrored(): Boolean = this.status() == Backing.STATUS_ERRORED

fun Backing.isErroredWithPLOT(): Boolean = (this.status() == Backing.STATUS_AUTHENTICATION_REQUIRED || this.status() == Backing.STATUS_ERRORED) && !this.paymentIncrements().isNullOrEmpty()

fun Backing.isShippable(): Boolean {
val reward = this.reward() ?: return false
return isShippable(reward)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ fun Uri.isDiscoverSortParam(): Boolean {
getQueryParameter("sort").isNotNull()
}

fun Uri.isBackingDetailsUri(webEndpoint: String): Boolean {
return isKickstarterUri(webEndpoint) && PROJECT_BACKING_DETAILS_URL.matcher(path()).matches()
}

private const val VERIFICATION = "/profile/verify_email"
private const val KSDOMAIN = "kickstarter.com"

Expand Down Expand Up @@ -313,3 +317,8 @@ private val USER_SURVEY = Pattern.compile(
private val MAIN_PAGE_OPEN_BUTTON_QUERYPARAMS = Pattern.compile(
"\\Aapp_banner=1&ref=nav\\z"
)

// /projects/:creator_param/:project_param/backing/details
private val PROJECT_BACKING_DETAILS_URL = Pattern.compile(
"\\A\\/projects(\\/[a-zA-Z0-9_-]+)?\\/[a-zA-Z0-9_-]+\\/backing\\/details\\z"
)
2 changes: 1 addition & 1 deletion app/src/main/java/com/kickstarter/ui/data/PlotData.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package com.kickstarter.ui.data

data class PlotData(val plotAmount: String?, val plotFirstScheduleCollection: String?)
data class PlotData(val plotAmount: String?, val plotFirstScheduleCollection: String?, val fixPledgeUrl: String?,)
61 changes: 52 additions & 9 deletions app/src/main/java/com/kickstarter/ui/fragments/BackingFragment.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package com.kickstarter.ui.fragments

import PaymentSchedule
import android.content.Intent
import android.graphics.Typeface
import android.net.Uri
import android.os.Bundle
import android.text.Spannable
import android.text.SpannableString
import android.text.Spanned
import android.text.method.LinkMovementMethod
import android.text.style.ClickableSpan
import android.text.style.ForegroundColorSpan
import android.text.style.StyleSpan
import android.util.Pair
Expand Down Expand Up @@ -34,6 +39,7 @@ import com.kickstarter.models.Reward
import com.kickstarter.ui.activities.BackingActivity
import com.kickstarter.ui.activities.DisclaimerItems
import com.kickstarter.ui.adapters.RewardAndAddOnsAdapter
import com.kickstarter.ui.compose.designsystem.KSBetaBadge
import com.kickstarter.ui.compose.designsystem.KSTheme
import com.kickstarter.ui.data.PledgeStatusData
import com.kickstarter.ui.data.ProjectData
Expand Down Expand Up @@ -129,6 +135,11 @@ class BackingFragment : Fragment() {
.subscribe { binding?.fixPaymentMethodMessage?.isGone = it }
.addToDisposable(disposables)

this.viewModel.outputs.betaBadgeIsGone().observeOn(AndroidSchedulers.mainThread())
.subscribe {
binding?.betaBadgeContainer?.isGone = it
}.addToDisposable(disposables)

this.viewModel.outputs.notifyDelegateToRefreshProject()
.observeOn(AndroidSchedulers.mainThread())
.subscribe { (activity as BackingDelegate?)?.refreshProject() }
Expand Down Expand Up @@ -273,6 +284,12 @@ class BackingFragment : Fragment() {
resources.getColor(R.color.kds_support_400, null)
)

binding?.betaBadge?.setContent {
KSTheme {
KSBetaBadge()
}
}

binding?.paymentScheduleComposeView?.setContent {
KSTheme {
val pledgeIsPlot by viewModel.outputs.pledgeIsPlot()
Expand Down Expand Up @@ -478,6 +495,7 @@ class BackingFragment : Fragment() {
)
}
}

R.string.fpo_if_the_project_reaches_its_funding_goal_you_will_be_charged_total_on_project_deadline -> {
this.viewModel.ksString?.let { ksString ->
ksString.format(
Expand All @@ -488,21 +506,46 @@ class BackingFragment : Fragment() {
}
}

R.string.fpo_we_cant_process_your_pledge_over_time_please_view_your_pledge_on_a_web_browser_and_log_in_to_fix_your_payment -> {
getString(it)
}

else -> getString(it)
}
}

pledgeStatusText?.let { statusText ->
val spannablePledgeStatus = SpannableString(statusText)
pledgeStatusData.pledgeTotal?.let { ViewUtils.addBoldSpan(spannablePledgeStatus, it) }
pledgeStatusData.projectDeadline?.let {
ViewUtils.addBoldSpan(
spannablePledgeStatus,
it
)
}
if (pledgeStatusData.statusStringRes == R.string.fpo_we_cant_process_your_pledge_over_time_please_view_your_pledge_on_a_web_browser_and_log_in_to_fix_your_payment) {
val spannablePledgeStatus = SpannableString(statusText)
val clickableText = "view your pledge"
val startIndex = statusText.indexOf(clickableText)
if (startIndex != -1) {
val endIndex = startIndex + clickableText.length
val clickableSpan = object : ClickableSpan() {
override fun onClick(widget: View) {
val url = pledgeStatusData.plotData?.fixPledgeUrl
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(intent)
}
}
spannablePledgeStatus.setSpan(
clickableSpan, startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)

binding?.backerPledgeStatus?.text = spannablePledgeStatus
binding?.backerPledgeStatus?.text = spannablePledgeStatus

binding?.backerPledgeStatus?.movementMethod = LinkMovementMethod.getInstance()
}
} else {
val spannablePledgeStatus = SpannableString(statusText)

pledgeStatusData.pledgeTotal?.let { ViewUtils.addBoldSpan(spannablePledgeStatus, it) }
pledgeStatusData.projectDeadline?.let {
ViewUtils.addBoldSpan(spannablePledgeStatus, it)
}

binding?.backerPledgeStatus?.text = spannablePledgeStatus
}
} ?: run {
binding?.backerPledgeStatus?.text = null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import com.kickstarter.libs.utils.RewardUtils
import com.kickstarter.libs.utils.extensions.addToDisposable
import com.kickstarter.libs.utils.extensions.backedReward
import com.kickstarter.libs.utils.extensions.isErrored
import com.kickstarter.libs.utils.extensions.isErroredWithPLOT
import com.kickstarter.libs.utils.extensions.isNotNull
import com.kickstarter.libs.utils.extensions.isNull
import com.kickstarter.libs.utils.extensions.negate
Expand Down Expand Up @@ -90,6 +91,9 @@ interface BackingFragmentViewModel {
/** Emits the card brand drawable to display. */
fun cardLogo(): Observable<Int>

/** Emits a boolean determining if the beta badge should be visible. */
fun betaBadgeIsGone(): Observable<Boolean>

/** Emits a boolean determining if the fix payment method button should be visible. */
fun fixPaymentMethodButtonIsGone(): Observable<Boolean>

Expand Down Expand Up @@ -184,6 +188,7 @@ interface BackingFragmentViewModel {
private val cardLogo = BehaviorSubject.create<Int>()
private val fixPaymentMethodButtonIsGone = BehaviorSubject.create<Boolean>()
private val fixPaymentMethodMessageIsGone = BehaviorSubject.create<Boolean>()
private val betaBadgeIsGone = BehaviorSubject.create<Boolean>()
private val notifyDelegateToRefreshProject = PublishSubject.create<Unit>()
private val notifyDelegateToShowFixPledge = PublishSubject.create<Unit>()
private val paymentMethodIsGone = BehaviorSubject.create<Boolean>()
Expand Down Expand Up @@ -401,8 +406,16 @@ interface BackingFragmentViewModel {
.subscribe { this.cardLogo.onNext(it) }
.addToDisposable(disposables)

val backingIsErroredWithPlot = backing
.map { it.isErroredWithPLOT() }
.distinctUntilChanged()
.map { it.negate() }

backingIsErroredWithPlot.subscribe { this.betaBadgeIsGone.onNext(it) }
.addToDisposable(disposables)

val backingIsNotErrored = backing
.map { it.isErrored() }
.map { it.isErrored() && !it.isErroredWithPLOT() }
.distinctUntilChanged()
.map { it.negate() }

Expand Down Expand Up @@ -582,7 +595,17 @@ interface BackingFragmentViewModel {
Backing.STATUS_CANCELED -> R.string.You_canceled_your_pledge_for_this_project
Backing.STATUS_COLLECTED -> R.string.We_collected_your_pledge_for_this_project
Backing.STATUS_DROPPED -> R.string.Your_pledge_was_dropped_because_of_payment_errors
Backing.STATUS_ERRORED -> R.string.We_cant_process_your_pledge_Please_update_your_payment_method
Backing.STATUS_AUTHENTICATION_REQUIRED,
Backing.STATUS_ERRORED -> {
if (!backing.paymentIncrements()
.isNullOrEmpty() && !project.isLive
) {
R.string.fpo_we_cant_process_your_pledge_over_time_please_view_your_pledge_on_a_web_browser_and_log_in_to_fix_your_payment
} else {
R.string.We_cant_process_your_pledge_Please_update_your_payment_method
}
}

Backing.STATUS_PLEDGED -> {
if (environment.featureFlagClient()
?.getBoolean(FlagKey.ANDROID_PLEDGE_OVER_TIME) == true && !backing.paymentIncrements()
Expand All @@ -598,8 +621,9 @@ interface BackingFragmentViewModel {
R.string.fpo_if_the_project_reaches_its_funding_goal_you_will_be_charged_total_on_project_deadline
}
}

Backing.STATUS_PREAUTH -> R.string.We_re_processing_your_pledge_pull_to_refresh
Backing.STATUS_AUTHENTICATION_REQUIRED -> R.string.fpo_authentication_required_backing_state

else -> null
}
}
Expand All @@ -623,12 +647,20 @@ interface BackingFragmentViewModel {
val pledgeTotal = backing.amount()
val pledgeTotalString = this.ksCurrency.format(pledgeTotal, project)
val plotData = if (isPlot) {
val plotAmountString = backing.paymentIncrements()?.first()?.paymentIncrementAmount?.formattedAmount
val plotAmountString =
backing.paymentIncrements()?.first()?.paymentIncrementAmount?.formattedAmount
// TODO: VERIFY IF WE WANT TO SHOW DECIMALS OR NOT
// val plotAmountString = this.ksCurrency.format(backing.paymentIncrements()?.first()?.amount?.amount.parseToDouble(), project, RoundingMode.UNNECESSARY)
val plotFirstScheduleCollection = backing.paymentIncrements()
?.first()?.scheduledCollection?.let { DateTimeUtils.longDate(it) }
PlotData(plotAmount = plotAmountString, plotFirstScheduleCollection = plotFirstScheduleCollection)
val fixPledgeUrl =
"${environment.webEndpoint()}/projects/${project.slug()}/backing/details"

PlotData(
plotAmount = plotAmountString,
plotFirstScheduleCollection = plotFirstScheduleCollection,
fixPledgeUrl = fixPledgeUrl
)
} else {
null
}
Expand Down Expand Up @@ -687,6 +719,9 @@ interface BackingFragmentViewModel {
override fun fixPaymentMethodMessageIsGone(): Observable<Boolean> =
this.fixPaymentMethodMessageIsGone

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

override fun notifyDelegateToRefreshProject(): Observable<Unit> =
this.notifyDelegateToRefreshProject

Expand Down
38 changes: 31 additions & 7 deletions app/src/main/res/layout/fragment_backing.xml
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,44 @@
</LinearLayout>
</LinearLayout>

<TextView
android:id="@+id/backer_pledge_status"
style="@style/BodyPrimary"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/grid_4"
android:orientation="vertical"
android:background="@color/kds_support_200"
android:gravity="center"
android:paddingStart="@dimen/grid_4"
android:paddingTop="@dimen/grid_2"
android:paddingEnd="@dimen/grid_4"
android:paddingBottom="@dimen/grid_2"
tools:text="@string/If_the_project_reaches_its_funding_goal_you_will_be_charged_total_on_project_deadline" />
android:paddingBottom="@dimen/grid_2">
<LinearLayout
android:id="@+id/beta_badge_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingBottom="@dimen/grid_2"
android:visibility="gone"
tools:visibility="visible"
>
<androidx.compose.ui.platform.ComposeView
android:id="@+id/beta_badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center">

<TextView
android:id="@+id/backer_pledge_status"
style="@style/BodyPrimary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="@string/If_the_project_reaches_its_funding_goal_you_will_be_charged_total_on_project_deadline" />
</LinearLayout>
</LinearLayout>
<include
android:id="@+id/fragment_pledge_section_summary_pledge"
layout="@layout/fragment_pledge_section_summary_pledge"
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
<!-- TODO: Get translations for this and put it in i18n, then remove -->
<string name="project_status_project_was_successfully_funded_on_deadline_but_you_can_still_pledge_for_available_rewards" formatted="false">This project was successfully funded on %{deadline}, but you can still pledge for available rewards.</string>
<string name="fpo_if_the_project_reaches_its_funding_goal_you_will_be_charged_total_on_project_deadline" formatted="false"> If the project reaches its funding goal, you will be charged %{total} on %{project_deadline}.</string>

<string name="fpo_we_cant_process_your_pledge_over_time_please_view_your_pledge_on_a_web_browser_and_log_in_to_fix_your_payment" formatted="false">We can’t process your Pledge Over Time payment. Please view your pledge on a web browser and log in to fix your payment.</string>
<!--TODO: Get design fot this string for Backing state authentication_required-->
<string name="fpo_authentication_required_backing_state">We can\'t process your pledge. Authentication is required.</string>

Expand Down
7 changes: 7 additions & 0 deletions app/src/test/java/com/kickstarter/services/UriExtTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.kickstarter.services

import android.net.Uri
import com.kickstarter.KSRobolectricTestCase
import com.kickstarter.libs.utils.extensions.isBackingDetailsUri
import com.kickstarter.libs.utils.extensions.isCheckoutUri
import com.kickstarter.libs.utils.extensions.isDiscoverCategoriesPath
import com.kickstarter.libs.utils.extensions.isDiscoverPlacesPath
Expand Down Expand Up @@ -52,6 +53,7 @@ class UriExtTest : KSRobolectricTestCase() {
private val discoverPlacesUri = Uri.parse("https://www.ksr.com/discover/places/newest")
private val newGuestCheckoutUri = Uri.parse("https://www.ksr.com/checkouts/1/guest/new")
private val projectUri = Uri.parse("https://www.ksr.com/projects/creator/project")
private val projectDetailUri = Uri.parse("https://www.ksr.com/projects/creator/project/backing/details")
private val signUpUri = Uri.parse("https://www.ksr.com/signup")
private val verificationEmail = Uri.parse("https://www.ksr.com/profile/verify_email")
private val projectPreviewUri =
Expand Down Expand Up @@ -212,6 +214,11 @@ class UriExtTest : KSRobolectricTestCase() {
assertTrue(signUpUri.isSignupUri(webEndpoint))
}

@Test
fun testUri_isProjectIsBackingDetailsUri() {
assertTrue(projectDetailUri.isBackingDetailsUri(webEndpoint))
}

@Test
fun testUri_isVerificationEmailUrl() {
assertFalse(projectUri.isVerificationEmailUrl())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,7 @@ class BackingFragmentViewModelTest : KSRobolectricTestCase() {
?.first()?.paymentIncrementAmount?.formattedAmount(),
plotFirstScheduleCollection = backing.paymentIncrements()
?.first()?.scheduledCollection?.let { DateTimeUtils.longDate(it) },
fixPledgeUrl = "https://www.kickstarter.com/projects/slug-1/backing/details"
),
)
)
Expand Down Expand Up @@ -905,6 +906,7 @@ class BackingFragmentViewModelTest : KSRobolectricTestCase() {
?.first()?.paymentIncrementAmount?.formattedAmount(),
plotFirstScheduleCollection = backing.paymentIncrements()
?.first()?.scheduledCollection?.let { DateTimeUtils.longDate(it) },
fixPledgeUrl = "https://www.kickstarter.com/projects/slug-1/backing/details"
),
)
)
Expand Down

0 comments on commit 0f22c14

Please sign in to comment.