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-361] Select new reward polish #899

Merged
merged 49 commits into from
Oct 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
3a5e5bd
Updated validation
justinswart Oct 9, 2019
fffa982
Fix snapshot tests, add submitButtonHidden
justinswart Oct 9, 2019
f933d99
Merge branch 'master' into change-payment-method-ui
justinswart Oct 9, 2019
97f4cd8
Add PledgeAmountSummaryViewController
justinswart Oct 10, 2019
02f290e
Merge branch 'master' into change-payment-method-ui
justinswart Oct 10, 2019
02bebe8
Add updateButtonTapped
justinswart Oct 10, 2019
82b6642
Add snapshot test and vm test
justinswart Oct 10, 2019
a4b8ffd
lol
justinswart Oct 10, 2019
e70e2e6
Merge branch 'master' into change-payment-method-ui
justinswart Oct 10, 2019
5cbf40b
Remove unused var
justinswart Oct 10, 2019
afa9c9a
Rename signal, fix payment source ID comparison
justinswart Oct 10, 2019
e2b3afd
Fix tests
justinswart Oct 10, 2019
1d69e99
Merge branch 'master' into change-payment-method-ui
justinswart Oct 11, 2019
c189ebe
Fix tests, simplify hiding of shipping location stackview
justinswart Oct 11, 2019
031ebc2
Wire up change payment method to mutation for payment source and Appl…
justinswart Oct 11, 2019
b1de7b0
Add updateReward context to PledgeViewModel
justinswart Oct 12, 2019
7c9f91c
Add tests
justinswart Oct 12, 2019
5a03263
Merge branch 'master' into change-payment-method-ui
justinswart Oct 12, 2019
30e77cc
Merge branch 'change-payment-method-ui' into change-payment-method-mu…
justinswart Oct 12, 2019
891f50b
Merge branch 'change-payment-method-mutation' into select-this-reward…
justinswart Oct 12, 2019
5bf6de3
Fix line lengths
justinswart Oct 12, 2019
3a6ae51
Merge branch 'master' into change-payment-method-ui
justinswart Oct 15, 2019
189adbe
Merge branch 'change-payment-method-ui' into change-payment-method-mu…
justinswart Oct 15, 2019
5ae6f86
Remove payment method when backing with Apple Pay and vice versa
justinswart Oct 15, 2019
d68df53
Update tests for apple pay sheet race condition fixes
justinswart Oct 15, 2019
a8092af
Replace zip with simpler signals
justinswart Oct 16, 2019
d65823d
Add Stripe token error test
justinswart Oct 16, 2019
4c49d3a
Fix snapshots
justinswart Oct 16, 2019
0ed69e0
Commit the actual snapshots
justinswart Oct 16, 2019
6d7bf10
Merge branch 'change-payment-method-ui' into change-payment-method-mu…
justinswart Oct 16, 2019
43c7655
Merge branch 'change-payment-method-mutation' into select-this-reward…
justinswart Oct 16, 2019
319688f
Merge branch 'master' into select-this-reward-instead
justinswart Oct 16, 2019
4445e9b
Hide payment methods when selecting a new reward
justinswart Oct 17, 2019
f6a3f26
Added output to change Carousel title according to context
Scollaco Oct 17, 2019
c079a94
Fixed and added test for title
Scollaco Oct 17, 2019
d406f97
Added logic to centralize backed reward
Scollaco Oct 17, 2019
7d6991c
SwiftFormat
Scollaco Oct 17, 2019
fbb661d
Merge branch 'master' into select-new-reward-polish
justinswart Oct 22, 2019
311f48e
Remove code that crept back in during merge
justinswart Oct 22, 2019
c84f8ad
Fixed logic to centralize the backed reward
Scollaco Oct 23, 2019
88ba19b
Fixed test
Scollaco Oct 23, 2019
38ac910
Merge branch 'master' into select-new-reward-polish
Scollaco Oct 24, 2019
044fcef
Merge branch 'master' into select-new-reward-polish
Scollaco Oct 24, 2019
b294416
Merge branch 'master' into select-new-reward-polish
Scollaco Oct 25, 2019
cd582dd
Renamed output
Scollaco Oct 25, 2019
c0cfd26
Merge remote-tracking branch 'oss/master' into select-new-reward-polish
Scollaco Oct 25, 2019
bd02acd
Updated private func to use sharedFunction
Scollaco Oct 25, 2019
880355e
Swiftformat
Scollaco Oct 25, 2019
d6261bb
Merge branch 'select-new-reward-polish' of https://github.com/kicksta…
Scollaco Oct 25, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,11 @@ final class ManagePledgeViewController: UIViewController, MessageBannerViewContr
// MARK: - Functions

private func goToRewards(_ project: Project) {
let rewardsVC = RewardsCollectionViewController.instantiate(with: project, refTag: nil)
let rewardsVC = RewardsCollectionViewController.instantiate(
with: project,
refTag: nil,
context: .managePledge
)

self.navigationController?.pushViewController(rewardsVC, animated: true)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ private func rewardsCollectionViewController(
refTag: RefTag?
) -> UINavigationController {
let rewardsCollectionViewController = RewardsCollectionViewController
.instantiate(with: project, refTag: refTag)
.instantiate(with: project, refTag: refTag, context: .createPledge)

let navigationController = RewardPledgeNavigationController(
rootViewController: rewardsCollectionViewController
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,13 @@ final class RewardsCollectionViewController: UICollectionViewController {

private let viewModel: RewardsCollectionViewModelType = RewardsCollectionViewModel()

static func instantiate(with project: Project, refTag: RefTag?) -> RewardsCollectionViewController {
static func instantiate(
with project: Project,
refTag: RefTag?,
context: RewardsCollectionViewContext
) -> RewardsCollectionViewController {
let rewardsCollectionVC = RewardsCollectionViewController()
rewardsCollectionVC.viewModel.inputs.configure(with: project, refTag: refTag)
rewardsCollectionVC.viewModel.inputs.configure(with: project, refTag: refTag, context: context)

return rewardsCollectionVC
}
Expand Down Expand Up @@ -99,6 +103,8 @@ final class RewardsCollectionViewController: UICollectionViewController {

if itemSize != layout.itemSize {
layout.invalidateLayout()
} else {
self.viewModel.inputs.viewDidLayoutSubviews()
}
}

Expand Down Expand Up @@ -128,13 +134,26 @@ final class RewardsCollectionViewController: UICollectionViewController {
override func bindViewModel() {
super.bindViewModel()

self.viewModel.outputs.title
.observeForUI()
.observeValues { [weak self] title in
_ = self
?|> \.title .~ title
}

self.viewModel.outputs.reloadDataWithValues
.observeForUI()
.observeValues { [weak self] values in
self?.dataSource.load(values)
self?.collectionView.reloadData()
}

self.viewModel.outputs.scrollToBackedRewardIndexPath
.observeForUI()
.observeValues { [weak self] indexPath in
self?.collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: false)
}

self.viewModel.outputs.goToPledge
.observeForControllerAction()
.observeValues { [weak self] data, context in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ final class RewardsCollectionViewControllerTests: TestCase {
combos(Language.allLanguages, [Device.phone4_7inch, Device.phone5_8inch, Device.pad]).forEach {
language, device in
withEnvironment(language: language, locale: .init(identifier: language.rawValue)) {
let vc = RewardsCollectionViewController.instantiate(with: project, refTag: nil)
let vc = RewardsCollectionViewController.instantiate(
with: project,
refTag: nil,
context: .createPledge
)
_ = traitControllers(device: device, orientation: .portrait, child: vc)

FBSnapshotVerifyView(vc.view, identifier: "lang_\(language)_device_\(device)")
Expand All @@ -41,7 +45,11 @@ final class RewardsCollectionViewControllerTests: TestCase {
combos(Language.allLanguages, [Device.phone4_7inch, Device.phone5_8inch, Device.pad]).forEach {
language, device in
withEnvironment(language: language, locale: .init(identifier: language.rawValue)) {
let vc = RewardsCollectionViewController.instantiate(with: project, refTag: nil)
let vc = RewardsCollectionViewController.instantiate(
with: project,
refTag: nil,
context: .createPledge
)
_ = traitControllers(device: device, orientation: .landscape, child: vc)

FBSnapshotVerifyView(vc.view, identifier: "lang_\(language)_device_\(device)")
Expand Down
51 changes: 47 additions & 4 deletions Library/ViewModels/RewardsCollectionViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@ import ReactiveSwift

public typealias PledgeData = (project: Project, reward: Reward, refTag: RefTag?)

public enum RewardsCollectionViewContext {
case createPledge
case managePledge
}

public protocol RewardsCollectionViewModelInputs {
func configure(with project: Project, refTag: RefTag?)
func configure(with project: Project, refTag: RefTag?, context: RewardsCollectionViewContext)
func rewardCellShouldShowDividerLine(_ show: Bool)
func rewardSelected(with rewardId: Int)
func traitCollectionDidChange(_ traitCollection: UITraitCollection)
func viewDidAppear()
func viewDidLayoutSubviews()
func viewDidLoad()
func viewWillAppear()
}
Expand All @@ -23,6 +29,9 @@ public protocol RewardsCollectionViewModelOutputs {
var navigationBarShadowImageHidden: Signal<Bool, Never> { get }
var reloadDataWithValues: Signal<[(Project, Either<Reward, Backing>)], Never> { get }
var rewardsCollectionViewFooterIsHidden: Signal<Bool, Never> { get }
var scrollToBackedRewardIndexPath: Signal<IndexPath, Never> { get }
var title: Signal<String, Never> { get }

func selectedReward() -> Reward?
}

Expand All @@ -46,6 +55,18 @@ public final class RewardsCollectionViewModel: RewardsCollectionViewModelType,
let rewards = project
.map { $0.rewards }

self.title = configData
.map(third)
.combineLatest(with: self.viewDidLoadProperty.signal.ignoreValues())
.map(first)
.map(titleForContext)

self.scrollToBackedRewardIndexPath = Signal.combineLatest(project, rewards)
.takeWhen(self.viewDidLayoutSubviewsProperty.signal.ignoreValues())
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using viewWillAppearProperty here didn't do anything, so viewDidLayoutSubviews was the way to go.

Copy link
Contributor

Choose a reason for hiding this comment

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

Interesting 🤔 I believe it needs a run loop to render the cells. We could probably go ahead with this but for interest sake, if you used viewWillAppear and added .ksr_delay(AppEnvironment.current.apiDelayInterval, on: AppEnvironment.current.scheduler) which is a delay of 0 and just puts the emission on the next run loop, that would also work.

.map(backedReward(_:rewards:))
.skipNil()
.take(first: 1)

self.reloadDataWithValues = Signal.combineLatest(project, rewards)
.map { project, rewards in
rewards.map { (project, Either<Reward, Backing>.left($0)) }
Expand Down Expand Up @@ -108,9 +129,9 @@ public final class RewardsCollectionViewModel: RewardsCollectionViewModelType,
)
}

private let configDataProperty = MutableProperty<(Project, RefTag?)?>(nil)
public func configure(with project: Project, refTag: RefTag?) {
self.configDataProperty.value = (project, refTag)
private let configDataProperty = MutableProperty<(Project, RefTag?, RewardsCollectionViewContext)?>(nil)
public func configure(with project: Project, refTag: RefTag?, context: RewardsCollectionViewContext) {
self.configDataProperty.value = (project, refTag, context)
}

private let rewardCellShouldShowDividerLineProperty = MutableProperty<Bool>(false)
Expand All @@ -133,6 +154,11 @@ public final class RewardsCollectionViewModel: RewardsCollectionViewModelType,
self.viewDidAppearProperty.value = ()
}

private let viewDidLayoutSubviewsProperty = MutableProperty(())
public func viewDidLayoutSubviews() {
self.viewDidLayoutSubviewsProperty.value = ()
}

private let viewDidLoadProperty = MutableProperty(())
public func viewDidLoad() {
self.viewDidLoadProperty.value = ()
Expand All @@ -150,6 +176,8 @@ public final class RewardsCollectionViewModel: RewardsCollectionViewModelType,
public let navigationBarShadowImageHidden: Signal<Bool, Never>
public let reloadDataWithValues: Signal<[(Project, Either<Reward, Backing>)], Never>
public let rewardsCollectionViewFooterIsHidden: Signal<Bool, Never>
public let scrollToBackedRewardIndexPath: Signal<IndexPath, Never>
public let title: Signal<String, Never>

private let selectedRewardProperty = MutableProperty<Reward?>(nil)
public func selectedReward() -> Reward? {
Expand All @@ -159,3 +187,18 @@ public final class RewardsCollectionViewModel: RewardsCollectionViewModelType,
public var inputs: RewardsCollectionViewModelInputs { return self }
public var outputs: RewardsCollectionViewModelOutputs { return self }
}

private func titleForContext(_ context: RewardsCollectionViewContext) -> String {
return context == .createPledge ? Strings.Back_this_project() : Strings.Choose_another_reward()
}

private func backedReward(_ project: Project, rewards: [Reward]) -> IndexPath? {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please update this to use the function @ifbarrera is using in #905 when that merges.

https://github.com/kickstarter/ios-oss/pull/905/files#diff-234cfa58d101616352832cb332756878R33

guard let backing = project.personalization.backing else {
return nil
}

let backedReward = reward(from: backing, inProject: project)
return rewards
.firstIndex(where: { $0.id == backedReward.id })
.flatMap { IndexPath(row: $0, section: 0) }
}
78 changes: 67 additions & 11 deletions Library/ViewModels/RewardsCollectionViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ final class RewardsCollectionViewModelTests: TestCase {
private let reloadDataWithValuesProject = TestObserver<[Project], Never>()
private let reloadDataWithValuesRewardOrBacking = TestObserver<[Either<Reward, Backing>], Never>()
private let rewardsCollectionViewFooterIsHidden = TestObserver<Bool, Never>()
private let scrollToBackedRewardIndexPath = TestObserver<IndexPath, Never>()
private let title = TestObserver<String, Never>()

override func setUp() {
super.setUp()
Expand All @@ -45,13 +47,15 @@ final class RewardsCollectionViewModelTests: TestCase {
.observe(self.reloadDataWithValuesRewardOrBacking.observer)
self.vm.outputs.rewardsCollectionViewFooterIsHidden
.observe(self.rewardsCollectionViewFooterIsHidden.observer)
self.vm.outputs.scrollToBackedRewardIndexPath.observe(self.scrollToBackedRewardIndexPath.observer)
self.vm.outputs.title.observe(self.title.observer)
}

func testConfigureWithProject() {
let project = Project.cosmicSurgery
let rewardsCount = project.rewards.count

self.vm.inputs.configure(with: project, refTag: RefTag.category)
self.vm.inputs.configure(with: project, refTag: RefTag.category, context: .createPledge)

self.reloadDataWithValues.assertDidNotEmitValue()

Expand All @@ -72,7 +76,7 @@ final class RewardsCollectionViewModelTests: TestCase {
let project = Project.cosmicSurgery
let firstRewardId = project.rewards.first!.id

self.vm.inputs.configure(with: project, refTag: .activity)
self.vm.inputs.configure(with: project, refTag: .activity, context: .createPledge)
self.vm.inputs.viewDidLoad()

self.goToDeprecatedPledgeProject.assertDidNotEmitValue()
Expand Down Expand Up @@ -119,7 +123,7 @@ final class RewardsCollectionViewModelTests: TestCase {
let project = Project.cosmicSurgery
let firstRewardId = project.rewards.first!.id

self.vm.inputs.configure(with: project, refTag: .activity)
self.vm.inputs.configure(with: project, refTag: .activity, context: .managePledge)
self.vm.inputs.viewDidLoad()

self.goToDeprecatedPledgeProject.assertDidNotEmitValue()
Expand Down Expand Up @@ -162,7 +166,7 @@ final class RewardsCollectionViewModelTests: TestCase {
let project = Project.cosmicSurgery
let firstRewardId = project.rewards.first!.id

self.vm.inputs.configure(with: project, refTag: .activity)
self.vm.inputs.configure(with: project, refTag: .activity, context: .createPledge)
self.vm.inputs.viewDidLoad()

self.goToDeprecatedPledgeProject.assertDidNotEmitValue()
Expand Down Expand Up @@ -201,7 +205,7 @@ final class RewardsCollectionViewModelTests: TestCase {
}

func testRewardsCollectionViewFooterViewIsHidden() {
self.vm.inputs.configure(with: Project.cosmicSurgery, refTag: .activity)
self.vm.inputs.configure(with: Project.cosmicSurgery, refTag: .activity, context: .createPledge)
self.vm.inputs.viewDidLoad()

self.rewardsCollectionViewFooterIsHidden.assertDidNotEmitValue()
Expand All @@ -223,14 +227,14 @@ final class RewardsCollectionViewModelTests: TestCase {
}

func testConfigureRewardsCollectionViewFooterWithCount() {
self.vm.inputs.configure(with: Project.cosmicSurgery, refTag: .activity)
self.vm.inputs.configure(with: Project.cosmicSurgery, refTag: .activity, context: .createPledge)
self.vm.inputs.viewDidLoad()

self.configureRewardsCollectionViewFooterWithCount.assertValues([Project.cosmicSurgery.rewards.count])
}

func testFlashScrollIndicators() {
self.vm.inputs.configure(with: Project.cosmicSurgery, refTag: .activity)
self.vm.inputs.configure(with: Project.cosmicSurgery, refTag: .activity, context: .createPledge)
self.vm.inputs.viewDidLoad()

self.flashScrollIndicators.assertDidNotEmitValue()
Expand All @@ -241,7 +245,7 @@ final class RewardsCollectionViewModelTests: TestCase {
}

func testNavigationBarShadowImageHidden() {
self.vm.inputs.configure(with: Project.cosmicSurgery, refTag: .activity)
self.vm.inputs.configure(with: Project.cosmicSurgery, refTag: .activity, context: .createPledge)
self.vm.inputs.viewDidLoad()

self.navigationBarShadowImageHidden.assertDidNotEmitValue()
Expand Down Expand Up @@ -275,7 +279,7 @@ final class RewardsCollectionViewModelTests: TestCase {
self.goToPledgeProject.assertDidNotEmitValue()
self.goToDeprecatedPledgeProject.assertDidNotEmitValue()

self.vm.inputs.configure(with: project, refTag: nil)
self.vm.inputs.configure(with: project, refTag: nil, context: .createPledge)
self.vm.inputs.viewDidLoad()
self.vm.inputs.viewWillAppear()
self.vm.inputs.viewDidAppear()
Expand Down Expand Up @@ -304,7 +308,7 @@ final class RewardsCollectionViewModelTests: TestCase {
self.goToPledgeProject.assertDidNotEmitValue()
self.goToDeprecatedPledgeProject.assertDidNotEmitValue()

self.vm.inputs.configure(with: project, refTag: nil)
self.vm.inputs.configure(with: project, refTag: nil, context: .createPledge)
self.vm.inputs.viewDidLoad()
self.vm.inputs.viewWillAppear()
self.vm.inputs.viewDidAppear()
Expand Down Expand Up @@ -338,7 +342,7 @@ final class RewardsCollectionViewModelTests: TestCase {
self.goToPledgeProject.assertDidNotEmitValue()
self.goToDeprecatedPledgeProject.assertDidNotEmitValue()

self.vm.inputs.configure(with: project, refTag: nil)
self.vm.inputs.configure(with: project, refTag: nil, context: .createPledge)
self.vm.inputs.viewDidLoad()
self.vm.inputs.viewWillAppear()
self.vm.inputs.viewDidAppear()
Expand All @@ -354,4 +358,56 @@ final class RewardsCollectionViewModelTests: TestCase {
self.goToDeprecatedPledgeProject.assertDidNotEmitValue()
}
}

func testTitle_CreatePledgeContext() {
self.vm.inputs.configure(with: Project.cosmicSurgery, refTag: .activity, context: .createPledge)
self.vm.inputs.viewDidLoad()

self.title.assertValue("Back this project")
}

func testTitle_ManagePledgeContext() {
self.vm.inputs.configure(with: Project.cosmicSurgery, refTag: .activity, context: .managePledge)
self.vm.inputs.viewDidLoad()

self.title.assertValue("Choose another reward")
}

func testBackedRewardIndexPath() {
let backedReward = Reward.template
|> Reward.lens.id .~ 5

let rewards = [
.template
|> Reward.lens.id .~ 1,
.template
|> Reward.lens.id .~ 2,
.template
|> Reward.lens.id .~ 3,
.template
|> Reward.lens.id .~ 4,
backedReward
]

let backing = Backing.template
|> Backing.lens.reward .~ backedReward
|> Backing.lens.rewardId .~ 5

let project = Project.template
|> Project.lens.rewards .~ rewards
|> Project.lens.state .~ .live
|> Project.lens.personalization.backing .~ backing
|> Project.lens.personalization.isBacking .~ true

self.vm.inputs.configure(with: project, refTag: .activity, context: .managePledge)
self.vm.inputs.viewDidLoad()

self.scrollToBackedRewardIndexPath.assertDidNotEmitValue()

self.vm.inputs.viewDidLayoutSubviews()

let indexPath = IndexPath(row: 4, section: 0)

self.scrollToBackedRewardIndexPath.assertValue(indexPath)
}
}