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

[NT-359] Integrate Updating Backing mutation #893

Merged
merged 9 commits into from
Oct 16, 2019
2 changes: 1 addition & 1 deletion KsApi/mutations/inputs/ApplePayParams.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

public struct ApplePayParams: Encodable {
public struct ApplePayParams: Encodable, Equatable {
let paymentInstrumentName: String
let paymentNetwork: String
let transactionIdentifier: String
Expand Down
7 changes: 4 additions & 3 deletions Library/UpdateBackingInput+Constructor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import KsApi

extension UpdateBackingInput {
internal static func input(
from updateBackingData: UpdateBackingData
from updateBackingData: UpdateBackingData,
isApplePay: Bool
) -> UpdateBackingInput {
let pledgeAmount: String? = updateBackingData.pledgeAmount.flatMap { pledgeAmount in
pledgeAmountString(withAmount: pledgeAmount, shippingRule: updateBackingData.shippingRule)
Expand All @@ -14,10 +15,10 @@ extension UpdateBackingInput {

return UpdateBackingInput(
amount: pledgeAmount,
applePay: nil,
applePay: isApplePay ? updateBackingData.applePayParams : nil,
id: backingId,
locationId: locationId,
paymentSourceId: nil,
paymentSourceId: isApplePay ? nil : updateBackingData.paymentSourceId,
rewardId: rewardId
)
}
Expand Down
43 changes: 40 additions & 3 deletions Library/UpdateBackingInput+ConstructorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,56 @@
import XCTest

final class UpdateBackingInput_ConstructorTests: TestCase {
func testUpdateBackingInput_UpdateBackingData() {
func testUpdateBackingInput_UpdateBackingData_NotApplePay() {
let applePayParams = ApplePayParams(
paymentInstrumentName: "paymentInstrumentName",
paymentNetwork: "paymentNetwork",
transactionIdentifier: "transactionIdentifier",
token: "token"
)

let data: UpdateBackingData = (
backing: Backing.template,
reward: Reward.template,
pledgeAmount: 100,
shippingRule: ShippingRule.template
shippingRule: ShippingRule.template,
paymentSourceId: GraphUserCreditCard.amex.id,
applePayParams: applePayParams
)
let input = UpdateBackingInput.input(from: data)

let input = UpdateBackingInput.input(from: data, isApplePay: false)

XCTAssertEqual(input.amount, "105.00")
XCTAssertNil(input.applePay)
XCTAssertEqual(input.id, "QmFja2luZy0x")
XCTAssertEqual(input.locationId, "42")
XCTAssertEqual(input.paymentSourceId, "6")
XCTAssertEqual(input.rewardId, "UmV3YXJkLTE=")
}

func testUpdateBackingInput_UpdateBackingData_IsApplePay() {
let applePayParams = ApplePayParams(
paymentInstrumentName: "paymentInstrumentName",
paymentNetwork: "paymentNetwork",
transactionIdentifier: "transactionIdentifier",
token: "token"
)

let data: UpdateBackingData = (
backing: Backing.template,
reward: Reward.template,
pledgeAmount: 100,
shippingRule: ShippingRule.template,
paymentSourceId: GraphUserCreditCard.amex.id,
applePayParams: applePayParams
)

let input = UpdateBackingInput.input(from: data, isApplePay: true)

XCTAssertEqual(input.amount, "105.00")
XCTAssertEqual(input.applePay, applePayParams)
XCTAssertEqual(input.id, "QmFja2luZy0x")
XCTAssertEqual(input.locationId, "42")
XCTAssertNil(input.paymentSourceId)
XCTAssertEqual(input.rewardId, "UmV3YXJkLTE=")
}
Expand Down
181 changes: 138 additions & 43 deletions Library/ViewModels/PledgeViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ public typealias UpdateBackingData = (
backing: Backing,
reward: Reward,
pledgeAmount: Double?,
shippingRule: ShippingRule?
shippingRule: ShippingRule?,
paymentSourceId: String?,
applePayParams: ApplePayParams?
)
public typealias PaymentAuthorizationData = (
project: Project, reward: Reward, pledgeAmount: Double,
Expand Down Expand Up @@ -168,6 +170,11 @@ public class PledgeViewModel: PledgeViewModelType, PledgeViewModelInputs, Pledge
self.shippingRuleSelectedSignal.wrapInOptional()
)

let selectedPaymentSourceId = Signal.merge(
initialData.mapConst(nil),
self.creditCardSelectedSignal.wrapInOptional()
)

let createBackingData = Signal.combineLatest(
project,
reward,
Expand All @@ -177,7 +184,7 @@ public class PledgeViewModel: PledgeViewModelType, PledgeViewModelInputs, Pledge
)
.map { $0 as CreateBackingData }

// MARK: Create Backing
// MARK: - Create Backing

let createButtonTapped = Signal.combineLatest(
self.submitButtonTappedSignal,
Expand Down Expand Up @@ -209,7 +216,7 @@ public class PledgeViewModel: PledgeViewModelType, PledgeViewModelInputs, Pledge
let createBackingEventError = createBackingEvent.errors()
.map { $0.localizedDescription }

// MARK: Apple Pay
// MARK: - Apple Pay

let paymentAuthorizationData = createBackingData.map {
(
Expand Down Expand Up @@ -276,14 +283,20 @@ public class PledgeViewModel: PledgeViewModelType, PledgeViewModelInputs, Pledge
applePayStatusFailure
)

// MARK: - Apple Pay
// MARK: - Create Apple Pay Backing

let willCreateApplePayBacking = Signal.combineLatest(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This and willUpdateApplePayBacking are to ensure that upon completion of these asynchronous events we show the appropriate messaging/view to the user.

applePayStatusSuccess,
context
)
.map { $1.isCreating }

let createApplePayBackingData = Signal.combineLatest(
createBackingData,
pkPaymentData.skipNil(),
self.stripeTokenSignal.skipNil()
)
.takeWhen(applePayStatusSuccess)
.takeWhen(willCreateApplePayBacking.filter(isTrue))
Copy link
Contributor

Choose a reason for hiding this comment

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

It might make sense to just put this filter(isTrue) as part of willCreateApplePayBacking.

.map { backingData, paymentData, stripeToken
-> (Project, Reward, Double, ShippingRule?, PKPaymentData, String, RefTag?) in
(
Expand All @@ -308,69 +321,67 @@ public class PledgeViewModel: PledgeViewModelType, PledgeViewModelInputs, Pledge
.materialize()
}

let createApplePayBackingEventSuccess = createApplePayBackingEvent.values()

let createApplePayBackingEventErrors = createApplePayBackingEvent.errors()
.map { $0.localizedDescription }
let createApplePayBackingError = createApplePayBackingEventErrors
.takeWhen(self.paymentAuthorizationDidFinishSignal)
// MARK: - Update Apple Pay Backing

self.createBackingError = Signal.merge(
createApplePayBackingError,
createBackingEventError
let applePayParams = Signal.combineLatest(
pkPaymentData.skipNil(),
self.stripeTokenSignal.skipNil()
)
.map { paymentData, token in
(
paymentData.displayName,
paymentData.network,
paymentData.transactionIdentifier,
token
)
}
.map(ApplePayParams.init)

let applePayTransactionCompleted = Signal.combineLatest(project, createApplePayBackingEventSuccess)
.takeWhen(self.paymentAuthorizationDidFinishSignal)
.map(first)

self.confirmationLabelAttributedText = Signal.merge(
project,
project.takeWhen(self.traitCollectionDidChangeSignal)
let applePayParamsData = Signal.merge(
initialData.mapConst(nil),
applePayParams.wrapInOptional()
)
.map(attributedConfirmationString(with:))
.skipNil()

let createBackingTransactionSuccess = project.takeWhen(createBackingEventSuccess)

self.goToThanks = Signal.merge(applePayTransactionCompleted, createBackingTransactionSuccess)

self.submitButtonTitle = context.map { $0.submitButtonTitle }
self.title = context.map { $0.title }

// MARK: Update Backing

let updateBackingData = Signal.combineLatest(
backing,
reward,
pledgeAmount.wrapInOptional(),
selectedShippingRule
selectedShippingRule,
selectedPaymentSourceId,
applePayParamsData
)
.map { $0 as UpdateBackingData }

// MARK: - Update Backing

let willUpdateApplePayBacking = Signal.combineLatest(
applePayStatusSuccess,
context
)
.map { $1.isUpdating }

let updateButtonTapped = Signal.combineLatest(
self.submitButtonTappedSignal,
context
)
.filter { _, context in context.isUpdating }
.ignoreValues()

let updateBackingEvent = updateBackingData
.takeWhen(updateButtonTapped)
.map(UpdateBackingInput.input(from:))
let updateBackingDataAndIsApplePay = updateBackingData.takePairWhen(
Signal.merge(
updateButtonTapped.mapConst(false),
willUpdateApplePayBacking.filter(isTrue)
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here would it make sense to put this filter(isTrue) as part of willUpdateApplePayBacking?

)
)

let updateBackingEvent = updateBackingDataAndIsApplePay
.map(UpdateBackingInput.input(from:isApplePay:))
.switchMap { input in
AppEnvironment.current.apiService.updateBacking(input: input)
.ksr_delay(AppEnvironment.current.apiDelayInterval, on: AppEnvironment.current.scheduler)
.materialize()
}

self.notifyDelegateUpdatePledgeDidSucceedWithMessage = updateBackingEvent.values()
.mapConst(Strings.Got_it_your_changes_have_been_saved())
self.updatePledgeFailedWithError = updateBackingEvent.errors()
.map { $0.localizedDescription }

self.popViewController = self.notifyDelegateUpdatePledgeDidSucceedWithMessage.ignoreValues()

// MARK: - Form Validation

let amountChangedAndValid = Signal.combineLatest(
Expand Down Expand Up @@ -428,6 +439,90 @@ public class PledgeViewModel: PledgeViewModelType, PledgeViewModelInputs, Pledge
createBackingEvent.filter { $0.isTerminating }.mapConst(true),
updateBackingEvent.filter { $0.isTerminating }.mapConst(true)
)

// MARK: - Success/Failure Create

let createPaymentAuthorizationDidFinishSignal = willCreateApplePayBacking
.takeWhen(self.paymentAuthorizationDidFinishSignal)
.filter(isTrue)

let createApplePayBackingCompleted = Signal.combineLatest(
createApplePayBackingEvent
.filter { $0.isTerminating }
.ignoreValues(),
createPaymentAuthorizationDidFinishSignal
)

let createApplePayBackingEventSuccess = createApplePayBackingEvent.values()

let createApplePayBackingEventErrors = createApplePayBackingEvent.errors()
.map { $0.localizedDescription }

let createApplePayBackingError = createApplePayBackingEventErrors
.takeWhen(createApplePayBackingCompleted)

self.createBackingError = Signal.merge(
createApplePayBackingError,
createBackingEventError
)

let applePayTransactionCompleted = Signal.combineLatest(project, createApplePayBackingEventSuccess)
.takeWhen(createApplePayBackingCompleted)
.map(first)

self.confirmationLabelAttributedText = Signal.merge(
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we move this somewhere higher up in the pledge view model? It's kind of separate from the success/failure stuff.

project,
project.takeWhen(self.traitCollectionDidChangeSignal)
)
.map(attributedConfirmationString(with:))
.skipNil()

let createBackingTransactionSuccess = project.takeWhen(createBackingEventSuccess)

self.goToThanks = Signal.merge(applePayTransactionCompleted, createBackingTransactionSuccess)

// MARK: - Success/Failure Update

let updatePaymentAuthorizationDidFinishSignal = willUpdateApplePayBacking
.takeWhen(self.paymentAuthorizationDidFinishSignal)
.filter(isTrue)

let updateApplePayBackingCompleted = Signal.combineLatest(
updateBackingEvent
.filter { $0.isTerminating }
.ignoreValues(),
updatePaymentAuthorizationDidFinishSignal
)

let updateApplePayBackingSuccess = updateBackingEvent.values()
.takeWhen(updateApplePayBackingCompleted)

let updateBackingDidCompleteApplePay = updateApplePayBackingSuccess
.ignoreValues()

let submitButtonTapped = Signal.merge(
self.submitButtonTappedSignal.mapConst(true),
willUpdateApplePayBacking.mapConst(false)
)

let updateBackingDidComplete = submitButtonTapped
.takeWhen(updateBackingEvent.values())
.filter(isTrue)
.ignoreValues()

self.notifyDelegateUpdatePledgeDidSucceedWithMessage = Signal.merge(
updateBackingDidCompleteApplePay,
updateBackingDidComplete
)
.mapConst(Strings.Got_it_your_changes_have_been_saved())

self.updatePledgeFailedWithError = updateBackingEvent.errors()
.map { $0.localizedDescription }

self.popViewController = self.notifyDelegateUpdatePledgeDidSucceedWithMessage.ignoreValues()

self.submitButtonTitle = context.map { $0.submitButtonTitle }
self.title = context.map { $0.title }
}

// MARK: - Inputs
Expand Down
Loading