Skip to content

Commit

Permalink
MBL-1287: Updating thanks page (#2000)
Browse files Browse the repository at this point in the history
* update copy

* hookup thanks page to late pledge flow

* fixed data being empty

* added ability to go back from thanks page when project is successfully late backed

* debugging

* debugging

* cleanup, tests

* linter

* cleanup

* linter

* remove and reorder imports for lint

* fix tests

* more lint

---------

Co-authored-by: Leigh Douglas <[email protected]>
Co-authored-by: Matthew <[email protected]>
  • Loading branch information
3 people authored Apr 2, 2024
1 parent 7c34d92 commit 7336ab1
Show file tree
Hide file tree
Showing 11 changed files with 239 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import com.kickstarter.ui.data.ActivityResult.Companion.create
import com.kickstarter.ui.data.CheckoutData
import com.kickstarter.ui.data.LoginReason
import com.kickstarter.ui.data.PledgeData
import com.kickstarter.ui.data.PledgeFlowContext
import com.kickstarter.ui.data.PledgeReason
import com.kickstarter.ui.data.ProjectData
import com.kickstarter.ui.extensions.finishWithAnimation
Expand Down Expand Up @@ -103,6 +104,7 @@ import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.launch
import timber.log.Timber
import type.CreditCardPaymentType

class ProjectPageActivity :
AppCompatActivity(),
Expand Down Expand Up @@ -503,6 +505,7 @@ class ProjectPageActivity :
val indexOfBackedReward = rewardSelectionUIState.initialRewardIndex
val rewardsList = rewardSelectionUIState.rewardList
val showRewardCarouselAlertDialog = rewardSelectionUIState.showAlertDialog
val selectedReward = rewardSelectionUIState.selectedReward
rewardsSelectionViewModel.sendEvent(expanded, currentPage, projectData)

LaunchedEffect(Unit) {
Expand Down Expand Up @@ -622,7 +625,6 @@ class ProjectPageActivity :
}
}

var selectedReward: Reward? = null
ProjectPledgeButtonAndFragmentContainer(
expanded = expanded,
onContinueClicked = { checkoutFlowViewModel.onBackThisProjectClicked() },
Expand Down Expand Up @@ -650,7 +652,6 @@ class ProjectPageActivity :
addOns = addOns,
project = projectData.project(),
onRewardSelected = { reward ->
selectedReward = reward
checkoutFlowViewModel.userRewardSelection(reward)
addOnsViewModel.userRewardSelection(reward)
rewardsSelectionViewModel.onUserRewardSelection(reward)
Expand Down Expand Up @@ -697,6 +698,28 @@ class ProjectPageActivity :
latePledgeCheckoutViewModel.onAddNewCardClicked(project = projectData.project(), totalAmount = totalAmount)
}
)

val successfulPledge = latePledgeCheckoutViewModel.onPledgeSuccess.collectAsStateWithLifecycle(initialValue = false).value

LaunchedEffect(successfulPledge) {
if (successfulPledge) {
latePledgeCheckoutViewModel.onPledgeSuccess.collect {
val checkoutData = CheckoutData.builder()
.amount(totalAmount)
.id(checkoutPayment.id)
.paymentType(CreditCardPaymentType.CREDIT_CARD)
.bonusAmount(totalBonusSupportAmount)
.shippingAmount(shippingAmount)
.build()
val pledgeData = PledgeData.with(PledgeFlowContext.forPledgeReason(PledgeReason.PLEDGE), projectData, selectedReward)
showCreatePledgeSuccess(Pair(checkoutData, pledgeData), userEmail)
checkoutFlowViewModel.onProjectSuccess()
refreshProject()
binding.pledgeContainerCompose.isGone = true
binding.pledgeContainerLayout.pledgeContainerRoot.isGone = false
}
}
}
}
}
}
Expand Down Expand Up @@ -1042,13 +1065,17 @@ class ProjectPageActivity :
showSnackbar(binding.snackbarAnchor, getString(R.string.Youve_canceled_your_pledge))
}

private fun showCreatePledgeSuccess(checkoutDatandProjectData: Pair<CheckoutData, PledgeData>) {
val checkoutData = checkoutDatandProjectData.first
val pledgeData = checkoutDatandProjectData.second
private fun showCreatePledgeSuccess(checkoutDataAndProjectData: Pair<CheckoutData, PledgeData>, email: String = "") {
val checkoutData = checkoutDataAndProjectData.first
val pledgeData = checkoutDataAndProjectData.second
val projectData = pledgeData.projectData()
if (clearFragmentBackStack()) {

val fFLatePledge = getEnvironment()?.featureFlagClient()?.getBoolean(FlagKey.ANDROID_POST_CAMPAIGN_PLEDGES) ?: false

if (clearFragmentBackStack() || (projectData.project().showLatePledgeFlow() && fFLatePledge)) {
startActivity(
Intent(this, ThanksActivity::class.java)
.putExtra(IntentKey.EMAIL, email)
.putExtra(IntentKey.PROJECT, projectData.project())
.putExtra(IntentKey.CHECKOUT_DATA, checkoutData)
.putExtra(IntentKey.PLEDGE_DATA, pledgeData)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.kickstarter.ui.adapters;

import android.util.Pair;
import android.view.LayoutInflater;
import android.view.ViewGroup;

Expand All @@ -20,6 +21,7 @@

import java.util.Collections;


public final class ThanksAdapter extends KSAdapter {
private static final int SECTION_SHARE_VIEW = 0;
private static final int SECTION_RECOMMENDED_PROJECTS_VIEW = 1;
Expand Down Expand Up @@ -60,7 +62,7 @@ public ThanksAdapter(final @NonNull Delegate delegate) {
}

public void takeData(final @NonNull ThanksData data) {
setSection(SECTION_SHARE_VIEW, Collections.singletonList(data.getBackedProject()));
setSection(SECTION_SHARE_VIEW, Collections.singletonList(Pair.create(Pair.create(data.getBackedProject(), data.getCheckoutData()), data.getUserEmail())));
setSection(SECTION_RECOMMENDED_PROJECTS_VIEW, data.getRecommendedProjects());
setSection(SECTION_CATEGORY_VIEW, Collections.singletonList(data.getCategory()));
notifyDataSetChanged();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import android.util.Pair
import com.kickstarter.models.Category
import com.kickstarter.models.Project
import com.kickstarter.services.DiscoveryParams
import com.kickstarter.ui.data.CheckoutData

class ThanksData(
val backedProject: Project,
val checkoutData: CheckoutData,
val userEmail: String,
val category: Category,
val recommendedProjects: List<Pair<Project, DiscoveryParams>>
)
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ import com.kickstarter.databinding.ThanksShareViewBinding
import com.kickstarter.libs.TweetComposer
import com.kickstarter.libs.utils.extensions.addToDisposable
import com.kickstarter.models.Project
import com.kickstarter.ui.data.CheckoutData
import com.kickstarter.viewmodels.ThanksShareHolderViewModel
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import java.math.RoundingMode

class ThanksShareViewHolder(private val binding: ThanksShareViewBinding) : KSViewHolder(binding.root) {
private val viewModel = ThanksShareHolderViewModel.ThanksShareViewHolderViewModel()
private val viewModel = ThanksShareHolderViewModel.ThanksShareViewHolderViewModel(environment())
private val ksString = requireNotNull(environment().ksString())
private var ksCurrency = requireNotNull(environment().ksCurrency())
private val shareDialog: ShareDialog = ShareDialog(context() as Activity)
private var disposables = CompositeDisposable()

Expand All @@ -28,6 +31,11 @@ class ThanksShareViewHolder(private val binding: ThanksShareViewBinding) : KSVie
.subscribe { showBackedProject(it) }
.addToDisposable(disposables)

viewModel.outputs.postCampaignPledgeText()
.observeOn(AndroidSchedulers.mainThread())
.subscribe { showPostCampaignPledgeText(it) }
.addToDisposable(disposables)

viewModel.outputs.startShare()
.observeOn(AndroidSchedulers.mainThread())
.subscribe { startShare(it) }
Expand Down Expand Up @@ -68,8 +76,8 @@ class ThanksShareViewHolder(private val binding: ThanksShareViewBinding) : KSVie

@Throws(Exception::class)
override fun bindData(data: Any?) {
val project = requireNotNull(data as Project?)
viewModel.inputs.configureWith(project)
val projectAndCheckoutData = requireNotNull(data as Pair<Pair<Project, CheckoutData>, String>?)
viewModel.inputs.configureWith(projectAndCheckoutData)
}

private fun shareString(projectName: String): String {
Expand All @@ -80,6 +88,10 @@ class ThanksShareViewHolder(private val binding: ThanksShareViewBinding) : KSVie
binding.backedProject.text = Html.fromHtml(ksString.format(context().getString(R.string.You_have_successfully_backed_project_html), "project_name", projectName))
}

private fun showPostCampaignPledgeText(pcptext: Triple<Project, Double, String>) {
binding.backedProject.text = Html.fromHtml(ksString.format(context().getString(R.string.You_have_successfully_pledged_to_project_post_campaign_html), "project_name", pcptext.first.name(), "pledge_total", ksCurrency.format(initialValue = pcptext.second, project = pcptext.first, roundingMode = RoundingMode.HALF_UP), "user_email", pcptext.third))
}

private fun startShare(projectNameAndShareUrl: Pair<String, String>) {
val projectName = projectNameAndShareUrl.first
val shareUrl = projectNameAndShareUrl.second
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.kickstarter.viewmodels

import android.util.Pair
import com.kickstarter.libs.Environment
import com.kickstarter.libs.RefTag.Companion.thanksFacebookShare
import com.kickstarter.libs.RefTag.Companion.thanksShare
import com.kickstarter.libs.RefTag.Companion.thanksTwitterShare
import com.kickstarter.libs.rx.transformers.Transformers
import com.kickstarter.libs.utils.UrlUtils.appendRefTag
import com.kickstarter.libs.utils.extensions.addToDisposable
import com.kickstarter.libs.utils.extensions.isFalse
import com.kickstarter.libs.utils.extensions.isTrue
import com.kickstarter.models.Project
import com.kickstarter.ui.data.CheckoutData
import io.reactivex.Observable
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.subjects.BehaviorSubject
Expand All @@ -16,7 +20,7 @@ import io.reactivex.subjects.PublishSubject
interface ThanksShareHolderViewModel {
interface Inputs {
/** Call to configure the view model with a project. */
fun configureWith(project: Project)
fun configureWith(thanksShareData: Pair<Pair<Project, CheckoutData>, String>)

/** Call when the share button is clicked. */
fun shareClick()
Expand All @@ -42,10 +46,13 @@ interface ThanksShareHolderViewModel {

/** Emits the project name and url to share using Twitter. */
fun startShareOnTwitter(): Observable<Pair<String, String>>

fun postCampaignPledgeText(): Observable<Triple<Project, Double, String>>
}

class ThanksShareViewHolderViewModel : Inputs, Outputs {
class ThanksShareViewHolderViewModel(environment: Environment) : Inputs, Outputs {

private val thanksShareData = PublishSubject.create<Pair<Pair<Project, CheckoutData>, String>>()
private val project = PublishSubject.create<Project>()
private val shareClick = PublishSubject.create<Unit>()
private val shareOnFacebookClick = PublishSubject.create<Unit>()
Expand All @@ -54,18 +61,33 @@ interface ThanksShareHolderViewModel {
private val startShare = PublishSubject.create<Pair<String, String>>()
private val startShareOnFacebook = PublishSubject.create<Pair<Project, String>>()
private val startShareOnTwitter = PublishSubject.create<Pair<String, String>>()
private val postCampaignText = PublishSubject.create<Triple<Project, Double, String>>()

val inputs: Inputs = this
val outputs: Outputs = this

private var disposables = CompositeDisposable()

init {
project
.map { it.name() }
thanksShareData
.map { it.first }
.subscribe { project.onNext(it.first) }
.addToDisposable(disposables)

thanksShareData
.filter { it.first.first.isInPostCampaignPledgingPhase().isFalse() }
.filter { it.first.first.postCampaignPledgingEnabled().isFalse() }
.map { it.first.first.name() }
.subscribe { projectName.onNext(it) }
.addToDisposable(disposables)

thanksShareData
.filter { it.first.first.isInPostCampaignPledgingPhase().isTrue() }
.filter { it.first.first.postCampaignPledgingEnabled().isTrue() }
.subscribe {
postCampaignText.onNext(Triple(it.first.first, it.first.second.amount(), it.second))
}
.addToDisposable(disposables)
project
.map {
Pair.create(
Expand Down Expand Up @@ -100,8 +122,8 @@ interface ThanksShareHolderViewModel {
.addToDisposable(disposables)
}

override fun configureWith(project: Project) {
this.project.onNext(project)
override fun configureWith(thanksShareData: Pair<Pair<Project, CheckoutData>, String>) {
this.thanksShareData.onNext(thanksShareData)
}

override fun shareClick() {
Expand All @@ -121,6 +143,7 @@ interface ThanksShareHolderViewModel {
override fun startShareOnTwitter(): Observable<Pair<String, String>> = startShareOnTwitter
override fun projectName(): Observable<String> = projectName

override fun postCampaignPledgeText(): Observable<Triple<Project, Double, String>> = postCampaignText
override fun onCleared() {
disposables.clear()
}
Expand Down
52 changes: 30 additions & 22 deletions app/src/main/java/com/kickstarter/viewmodels/ThanksViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ interface ThanksViewModel {
.ofType(Project::class.java)
.take(1)

val userEmail = intent()
.filter { it.getStringExtra(IntentKey.EMAIL).isNotNull() }
.map<String?> { it.getStringExtra(IntentKey.EMAIL) }
.ofType(String::class.java)
.take(1)

val rootCategory = project
.switchMap {
rootCategory(it, apolloClient)
Expand Down Expand Up @@ -193,28 +199,6 @@ interface ThanksViewModel {
)
}

Observable.combineLatest(
project,
rootCategory,
recommendedProjects.startWith(listOf())
) { backedProject, category, projects ->
ThanksData(backedProject, category, projects)
}.subscribe { adapterData.onNext(it) }
.addToDisposable(disposables)

adapterData
.compose(Transformers.takePairWhenV2(projectOnUserChangeSave))
.map {
Pair(it.first, it.second.updateStartedProjectAndDiscoveryParamsList(it.first.recommendedProjects))
}
.map {
ThanksData(it.first.backedProject, it.first.category, it.second)
}.distinctUntilChanged()
.subscribe {
adapterData.onNext(it)
}
.addToDisposable(disposables)

projectOnUserChangeSave
.subscribe { this.analyticEvents?.trackWatchProjectCTA(it, THANKS) }
.addToDisposable(disposables)
Expand Down Expand Up @@ -289,6 +273,30 @@ interface ThanksViewModel {
}
.addToDisposable(disposables)

Observable.combineLatest(
project,
rootCategory,
recommendedProjects.startWith(listOf()),
checkoutData,
userEmail
) { backedProject, category, projects, checkoutData, email ->
ThanksData(backedProject, checkoutData, email, category, projects)
}.subscribe { adapterData.onNext(it) }
.addToDisposable(disposables)

adapterData
.compose(Transformers.takePairWhenV2(projectOnUserChangeSave))
.withLatestFrom(userEmail) { adapterAndProjectData, email ->
Triple(adapterAndProjectData.first, adapterAndProjectData.second.updateStartedProjectAndDiscoveryParamsList(adapterAndProjectData.first.recommendedProjects), email)
}
.map {
ThanksData(it.first.backedProject, it.first.checkoutData, it.third, it.first.category, it.second)
}.distinctUntilChanged()
.subscribe {
adapterData.onNext(it)
}
.addToDisposable(disposables)

SendThirdPartyEventUseCaseV2(sharedPreferences, ffClient)
.sendThirdPartyEvent(
project = project,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ class CheckoutFlowViewModel(val environment: Environment) : ViewModel() {
}
}

fun onProjectSuccess() {
viewModelScope.launch {
// Open Flow
mutableFlowUIState.emit(FlowUIState(currentPage = 0, expanded = false))
}
}

class Factory(private val environment: Environment) :
ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ class LatePledgeCheckoutViewModel(val environment: Environment) : ViewModel() {

private var selectedReward: Reward? = null
private var mutableLatePledgeCheckoutUIState = MutableStateFlow(LatePledgeCheckoutUIState())

private var mutableOnPledgeSuccessAction = MutableSharedFlow<Boolean>()
val onPledgeSuccess: SharedFlow<Boolean>
get() = mutableOnPledgeSuccessAction
.asSharedFlow()
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(),
initialValue = false
)

val latePledgeCheckoutUIState: StateFlow<LatePledgeCheckoutUIState>
get() = mutableLatePledgeCheckoutUIState
.asStateFlow()
Expand Down Expand Up @@ -263,7 +274,7 @@ class LatePledgeCheckoutViewModel(val environment: Environment) : ViewModel() {
if (iDRequiresActionPair.second) {
mutablePaymentRequiresAction.emit(clientSecret)
} else {
// Go to Thanks Page, full complete flow
mutableOnPledgeSuccessAction.emit(true)
}
}.onCompletion {
emitCurrentState()
Expand Down
Loading

0 comments on commit 7336ab1

Please sign in to comment.