Skip to content

Commit

Permalink
Checkout event improvements (#813)
Browse files Browse the repository at this point in the history
* pledge cta delegate

* event tracking is happening

* refactor

* tests

* fix vm tests

* swift format

* corrected koala tracking

* testing delegate

* more tests
  • Loading branch information
cdolm92 committed Aug 22, 2019
1 parent 7c65ae4 commit f95ac7d
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ public final class ProjectPamphletViewController: UIViewController {
.compactMap { $0 as? ProjectPamphletContentViewController }.first
self.contentController.delegate = self

self.pledgeCTAContainerView.delegate = self

self.viewModel.inputs.initial(topConstraint: self.initialTopConstraint)

self.viewModel.inputs.viewDidLoad()
Expand Down Expand Up @@ -90,10 +92,6 @@ public final class ProjectPamphletViewController: UIViewController {
_ = (self.pledgeCTAContainerView, self.view)
|> ksr_addSubviewToParent()

self.pledgeCTAContainerView.pledgeCTAButton.addTarget(
self, action: #selector(ProjectPamphletViewController.backThisProjectTapped), for: .touchUpInside
)

self.pledgeCTAContainerView.pledgeRetryButton.addTarget(
self, action: #selector(ProjectPamphletViewController.pledgeRetryButtonTapped), for: .touchUpInside
)
Expand Down Expand Up @@ -199,15 +197,17 @@ public final class ProjectPamphletViewController: UIViewController {

// MARK: - Selectors

@objc func backThisProjectTapped() {
self.viewModel.inputs.backThisProjectTapped()
}

@objc func pledgeRetryButtonTapped() {
self.viewModel.inputs.pledgeRetryButtonTapped()
}
}

extension ProjectPamphletViewController: PledgeCTAContainerViewDelegate {
func pledgeCTAButtonTapped() {
self.viewModel.inputs.backThisProjectTapped()
}
}

extension ProjectPamphletViewController: ProjectPamphletContentViewControllerDelegate {
public func projectPamphletContent(
_: ProjectPamphletContentViewController,
Expand Down
20 changes: 20 additions & 0 deletions Kickstarter-iOS/Views/PledgeCTAContainerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import Library
import Prelude
import UIKit

protocol PledgeCTAContainerViewDelegate: AnyObject {
func pledgeCTAButtonTapped()
}

private enum Layout {
enum Button {
static let minHeight: CGFloat = 48.0
Expand Down Expand Up @@ -58,6 +62,8 @@ final class PledgeCTAContainerView: UIView {

private lazy var titleLabel: UILabel = { UILabel(frame: .zero) }()

weak var delegate: PledgeCTAContainerViewDelegate?

private let viewModel: PledgeCTAContainerViewViewModelType = PledgeCTAContainerViewViewModel()

// MARK: - Lifecycle
Expand Down Expand Up @@ -105,6 +111,12 @@ final class PledgeCTAContainerView: UIView {
override func bindViewModel() {
super.bindViewModel()

self.viewModel.outputs.notifyDelegateCTATapped
.observeForUI()
.observeValues { [weak self] in
self?.delegate?.pledgeCTAButtonTapped()
}

self.viewModel.outputs.buttonStyleType
.observeForUI()
.observeValues { [weak self] buttonStyleType in
Expand Down Expand Up @@ -158,6 +170,10 @@ final class PledgeCTAContainerView: UIView {
self.rootStackView
)
|> ksr_addArrangedSubviewsToStackView()

self.pledgeCTAButton.addTarget(
self, action: #selector(self.pledgeCTAButtonTapped), for: .touchUpInside
)
}

private func setupConstraints() {
Expand All @@ -179,6 +195,10 @@ final class PledgeCTAContainerView: UIView {
?|> \.alpha .~ alpha
})
}

@objc func pledgeCTAButtonTapped() {
self.viewModel.inputs.pledgeCTAButtonTapped()
}
}

// MARK: - Styles
Expand Down
22 changes: 18 additions & 4 deletions Library/Koala/Koala.swift
Original file line number Diff line number Diff line change
Expand Up @@ -578,11 +578,25 @@ public final class Koala {

// MARK: - Checkout Events

public func trackBackThisButtonClicked(project: Project, screen: CheckoutContext) {
public func trackPledgeCTAButtonClicked(
stateType: PledgeStateCTAType,
project: Project, screen: CheckoutContext
) {
let props = properties(project: project)
.withAllValuesFrom(["screen": screen.trackingString])

self.track(event: "Back this Project Button Clicked", properties: props)
switch stateType {
case .fix:
self.track(event: "Fix Pledge Button Clicked", properties: props)
case .pledge:
self.track(event: "Back this Project Button Clicked", properties: props)
case .manage:
self.track(event: "Manage Pledge Button Clicked", properties: props)
case .viewBacking:
self.track(event: "View Your Pledge Button Clicked", properties: props)
case .viewRewards:
self.track(event: "View Rewards Button Clicked", properties: props)
}
}

public func trackSelectRewardButtonClicked(
Expand Down Expand Up @@ -1287,7 +1301,7 @@ public final class Koala {

// Deprecated event
self.track(
event: "Project Page",
event: "Project Page Viewed",
properties: props.withAllValuesFrom(deprecatedProps)
)

Expand All @@ -1296,7 +1310,7 @@ public final class Koala {
properties: props.withAllValuesFrom(deprecatedProps)
)

self.track(event: "Project Page Viewed", properties: props)
self.track(event: "Project Page", properties: props)
}

public func trackSwipedProject(_ project: Project, refTag: RefTag?, type: SwipeType) {
Expand Down
66 changes: 63 additions & 3 deletions Library/Koala/KoalaTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ final class KoalaTests: TestCase {

let properties = client.properties.last

XCTAssertEqual("Project Page Viewed", client.events.last)
XCTAssertEqual("Project Page", client.events.last)
XCTAssertEqual(project.stats.backersCount, properties?["project_backers_count"] as? Int)
XCTAssertEqual(project.country.countryCode, properties?["project_country"] as? String)
XCTAssertEqual(project.country.currencyCode, properties?["project_currency"] as? String)
Expand Down Expand Up @@ -468,21 +468,81 @@ final class KoalaTests: TestCase {
XCTAssertEqual(["Triggered Refresh"], client.events)
}

func testTrackBackThisButtonClicked() {
func testTrackPledgeCTAButtonClicked_FixState() {
let client = MockTrackingClient()
let project = Project.template
let loggedInUser = User.template |> \.id .~ 42

let koala = Koala(client: client, loggedInUser: loggedInUser)

koala.trackBackThisButtonClicked(project: project, screen: .projectPage)
koala.trackPledgeCTAButtonClicked(stateType: .fix, project: project, screen: .projectPage)

let properties = client.properties.last

XCTAssertEqual(["Fix Pledge Button Clicked"], client.events)
XCTAssertEqual("Project page", properties?["screen"] as? String)
}

func testTrackPledgeCTAButtonClicked_PledgeState() {
let client = MockTrackingClient()
let project = Project.template
let loggedInUser = User.template |> \.id .~ 42

let koala = Koala(client: client, loggedInUser: loggedInUser)

koala.trackPledgeCTAButtonClicked(stateType: .pledge, project: project, screen: .projectPage)

let properties = client.properties.last

XCTAssertEqual(["Back this Project Button Clicked"], client.events)
XCTAssertEqual("Project page", properties?["screen"] as? String)
}

func testTrackPledgeCTAButtonClicked_ManageState() {
let client = MockTrackingClient()
let project = Project.template
let loggedInUser = User.template |> \.id .~ 42

let koala = Koala(client: client, loggedInUser: loggedInUser)

koala.trackPledgeCTAButtonClicked(stateType: .manage, project: project, screen: .projectPage)

let properties = client.properties.last

XCTAssertEqual(["Manage Pledge Button Clicked"], client.events)
XCTAssertEqual("Project page", properties?["screen"] as? String)
}

func testTrackPledgeCTAButtonClicked_ViewBackingState() {
let client = MockTrackingClient()
let project = Project.template
let loggedInUser = User.template |> \.id .~ 42

let koala = Koala(client: client, loggedInUser: loggedInUser)

koala.trackPledgeCTAButtonClicked(stateType: .viewBacking, project: project, screen: .projectPage)

let properties = client.properties.last

XCTAssertEqual(["View Your Pledge Button Clicked"], client.events)
XCTAssertEqual("Project page", properties?["screen"] as? String)
}

func testTrackPledgeCTAButtonClicked_ViewRewardState() {
let client = MockTrackingClient()
let project = Project.template
let loggedInUser = User.template |> \.id .~ 42

let koala = Koala(client: client, loggedInUser: loggedInUser)

koala.trackPledgeCTAButtonClicked(stateType: .viewRewards, project: project, screen: .projectPage)

let properties = client.properties.last

XCTAssertEqual(["View Rewards Button Clicked"], client.events)
XCTAssertEqual("Project page", properties?["screen"] as? String)
}

func testTrackSelectRewardButtonClicked() {
let client = MockTrackingClient()
let reward = Reward.template
Expand Down
23 changes: 23 additions & 0 deletions Library/ViewModels/PledgeCTAContainerViewViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import ReactiveSwift

public protocol PledgeCTAContainerViewViewModelInputs {
func configureWith(value: (projectOrError: Either<Project, ErrorEnvelope>, isLoading: Bool))
func pledgeCTAButtonTapped()
}

public protocol PledgeCTAContainerViewViewModelOutputs {
var activityIndicatorIsHidden: Signal<Bool, Never> { get }
var buttonStyleType: Signal<ButtonStyleType, Never> { get }
var buttonTitleText: Signal<String, Never> { get }
var notifyDelegateCTATapped: Signal<(), Never> { get }
var pledgeCTAButtonIsHidden: Signal<Bool, Never> { get }
var pledgeRetryButtonIsHidden: Signal<Bool, Never> { get }
var spacerIsHidden: Signal<Bool, Never> { get }
Expand Down Expand Up @@ -61,6 +63,8 @@ public final class PledgeCTAContainerViewViewModel: PledgeCTAContainerViewViewMo
isLoading.filter(isFalse).ignoreValues()
)

self.notifyDelegateCTATapped = self.pledgeCTAButtonTappedProperty.signal

self.pledgeRetryButtonIsHidden = inError
.map(isFalse)
.takeWhen(updateButtonStates)
Expand All @@ -81,6 +85,19 @@ public final class PledgeCTAContainerViewViewModel: PledgeCTAContainerViewViewMo
self.titleText = pledgeState.map { $0.titleLabel }.skipNil()
self.subtitleText = Signal.combineLatest(project, pledgeState)
.map(subtitle(project:pledgeState:))

let pledgeTypeAndProject = Signal.combineLatest(pledgeState, project)

// Tracking
pledgeTypeAndProject
.takeWhen(self.pledgeCTAButtonTappedProperty.signal)
.observeValues {
AppEnvironment.current.koala.trackPledgeCTAButtonClicked(
stateType: $0,
project: $1,
screen: .projectPage
)
}
}

fileprivate let projectOrErrorProperty =
Expand All @@ -89,12 +106,18 @@ public final class PledgeCTAContainerViewViewModel: PledgeCTAContainerViewViewMo
self.projectOrErrorProperty.value = value
}

fileprivate let pledgeCTAButtonTappedProperty = MutableProperty(())
public func pledgeCTAButtonTapped() {
self.pledgeCTAButtonTappedProperty.value = ()
}

public var inputs: PledgeCTAContainerViewViewModelInputs { return self }
public var outputs: PledgeCTAContainerViewViewModelOutputs { return self }

public let activityIndicatorIsHidden: Signal<Bool, Never>
public let buttonStyleType: Signal<ButtonStyleType, Never>
public let buttonTitleText: Signal<String, Never>
public let notifyDelegateCTATapped: Signal<Void, Never>
public let pledgeCTAButtonIsHidden: Signal<Bool, Never>
public let pledgeRetryButtonIsHidden: Signal<Bool, Never>
public let spacerIsHidden: Signal<Bool, Never>
Expand Down
33 changes: 33 additions & 0 deletions Library/ViewModels/PledgeCTAContainerViewViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ internal final class PledgeCTAContainerViewViewModelTests: TestCase {
let activityIndicatorIsHidden = TestObserver<Bool, Never>()
let buttonStyleType = TestObserver<ButtonStyleType, Never>()
let buttonTitleText = TestObserver<String, Never>()
let notifyDelegateCTATapped = TestObserver<Void, Never>()
let pledgeCTAButtonIsHidden = TestObserver<Bool, Never>()
let pledgeRetryButtonIsHidden = TestObserver<Bool, Never>()
let spacerIsHidden = TestObserver<Bool, Never>()
Expand All @@ -25,6 +26,7 @@ internal final class PledgeCTAContainerViewViewModelTests: TestCase {
self.vm.outputs.activityIndicatorIsHidden.observe(self.activityIndicatorIsHidden.observer)
self.vm.outputs.buttonStyleType.observe(self.buttonStyleType.observer)
self.vm.outputs.buttonTitleText.observe(self.buttonTitleText.observer)
self.vm.outputs.notifyDelegateCTATapped.observe(self.notifyDelegateCTATapped.observer)
self.vm.outputs.pledgeCTAButtonIsHidden.observe(self.pledgeCTAButtonIsHidden.observer)
self.vm.outputs.pledgeRetryButtonIsHidden.observe(self.pledgeRetryButtonIsHidden.observer)
self.vm.outputs.spacerIsHidden.observe(self.spacerIsHidden.observer)
Expand Down Expand Up @@ -167,4 +169,35 @@ internal final class PledgeCTAContainerViewViewModelTests: TestCase {
self.vm.inputs.configureWith(value: (.right(.couldNotParseJSON), false))
self.pledgeRetryButtonIsHidden.assertValues([true, false])
}

func testNotifyDelegateCTATapped() {
let project = Project.template
|> Project.lens.personalization.backing .~ nil
|> Project.lens.personalization.isBacking .~ false

self.notifyDelegateCTATapped.assertDidNotEmitValue()

self.vm.inputs.configureWith(value: (.left(project), false))
self.buttonStyleType.assertValues([ButtonStyleType.green])
self.buttonTitleText.assertValues([Strings.Back_this_project()])

self.vm.inputs.pledgeCTAButtonTapped()
self.notifyDelegateCTATapped.assertValueCount(1)
}

func testTrackingEvents() {
let project = Project.template
|> Project.lens.state .~ .successful
|> Project.lens.personalization.isBacking .~ false

self.notifyDelegateCTATapped.assertDidNotEmitValue()

self.vm.inputs.configureWith(value: (.left(project), false))
self.buttonStyleType.assertValues([ButtonStyleType.black])
self.buttonTitleText.assertValues([Strings.View_rewards()])

self.vm.inputs.pledgeCTAButtonTapped()
self.notifyDelegateCTATapped.assertValueCount(1)
XCTAssertEqual(["View Rewards Button Clicked"], self.trackingClient.events)
}
}
7 changes: 0 additions & 7 deletions Library/ViewModels/ProjectPamphletViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,6 @@ public final class ProjectPamphletViewModel: ProjectPamphletViewModelType, Proje
.map(cookieFrom(refTag:project:))
.skipNil()
.observeValues { AppEnvironment.current.cookieStorage.setCookie($0) }

// Tracking
project
.takeWhen(self.backThisProjectTappedProperty.signal)
.observeValues {
AppEnvironment.current.koala.trackBackThisButtonClicked(project: $0, screen: .projectPage)
}
}

private let backThisProjectTappedProperty = MutableProperty(())
Expand Down
Loading

0 comments on commit f95ac7d

Please sign in to comment.