Skip to content

Commit

Permalink
[NT-361] Select new reward polish (#899)
Browse files Browse the repository at this point in the history
  • Loading branch information
Scollaco committed Oct 25, 2019
1 parent 22466c1 commit 47e2808
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 21 deletions.
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())
.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? {
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)
}
}

0 comments on commit 47e2808

Please sign in to comment.