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

💲[Native Checkout] Login & Sign-up on the pledge screen (Part 1) #727

Merged
merged 16 commits into from
Jul 12, 2019
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
28 changes: 28 additions & 0 deletions Kickstarter-iOS/Views/Cells/PledgeContinueCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,41 @@ import Foundation
import Library
import Prelude

protocol PledgeContinueCellDelegate: AnyObject {
func pledgeContinueCellDidTapContinue(_ cell: PledgeContinueCell)
}

final class PledgeContinueCell: UITableViewCell, ValueCell {
// MARK: - Properties

private let continueButton = MultiLineButton(type: .custom)
internal weak var delegate: PledgeContinueCellDelegate?
private let viewModel = PledgeContinueCellViewModel()

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)

self.setupSubviews()

self.bindViewModel()
}

required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func bindViewModel() {
super.bindViewModel()

self.viewModel.outputs.goToLoginSignup
.observeForControllerAction()
.observeValues { [weak self] in
guard let self = self else { return }

self.delegate?.pledgeContinueCellDidTapContinue(self)
}
}

override func bindStyles() {
super.bindStyles()

Expand Down Expand Up @@ -42,5 +64,11 @@ final class PledgeContinueCell: UITableViewCell, ValueCell {
|> ksr_constrainViewToMarginsInParent()

self.continueButton.heightAnchor.constraint(greaterThanOrEqualToConstant: Styles.grid(8)).isActive = true

self.continueButton.addTarget(self, action: #selector(self.continueButtonTapped), for: .touchUpInside)
ifbarrera marked this conversation as resolved.
Show resolved Hide resolved
}

@objc private func continueButtonTapped() {
self.viewModel.inputs.continueButtonTapped()
}
}
35 changes: 35 additions & 0 deletions Kickstarter-iOS/Views/Controllers/PledgeTableViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class PledgeTableViewController: UITableViewController {
private let dataSource: PledgeDataSource = PledgeDataSource()
private weak var pledgeSummaryCell: PledgeSummaryCell?
private weak var shippingLocationCell: PledgeShippingLocationCell?
private var sessionStartedObserver: Any?
private let viewModel: PledgeViewModelType = PledgeViewModel()

// MARK: - Lifecycle
Expand Down Expand Up @@ -42,6 +43,10 @@ class PledgeTableViewController: UITableViewController {
self.viewModel.inputs.viewDidLoad()
}

deinit {
self.sessionStartedObserver.doIfSome(NotificationCenter.default.removeObserver)
}

// MARK: - Styles

override func bindStyles() {
Expand All @@ -56,6 +61,12 @@ class PledgeTableViewController: UITableViewController {
override func bindViewModel() {
super.bindViewModel()

self.viewModel.outputs.goToLoginSignup
.observeForControllerAction()
.observeValues { [weak self] intent in
self?.goToLoginSignup(with: intent)
}

self.viewModel.outputs.pledgeViewDataAndReload
.observeForUI()
.observeValues { [weak self] data, reload in
Expand All @@ -70,6 +81,21 @@ class PledgeTableViewController: UITableViewController {
.observeValues { [weak self] project, pledgeTotal in
self?.pledgeSummaryCell?.configureWith(value: (project, pledgeTotal))
}

self.sessionStartedObserver = NotificationCenter.default
.addObserver(forName: .ksr_sessionStarted, object: nil, queue: nil) { [weak self] _ in
self?.viewModel.inputs.userSessionStarted()
}
}

// MARK: - Private Helpers

private func goToLoginSignup(with intent: LoginIntent) {
let loginSignupViewController = LoginToutViewController.configuredWith(loginIntent: intent)
let navigationController = UINavigationController(rootViewController: loginSignupViewController)
let sheetOverlayViewController = SheetOverlayViewController(child: navigationController)

self.present(sheetOverlayViewController, animated: true)
}

// MARK: - Actions
Expand Down Expand Up @@ -101,6 +127,9 @@ class PledgeTableViewController: UITableViewController {
let shippingLocationCell = (cell as? PledgeShippingLocationCell)
shippingLocationCell?.delegate = self
self.shippingLocationCell = shippingLocationCell
case is PledgeContinueCell:
let pledgeContinueCell = cell as? PledgeContinueCell
pledgeContinueCell?.delegate = self
default:
break
}
Expand Down Expand Up @@ -133,6 +162,12 @@ extension PledgeTableViewController: PledgeShippingLocationCellDelegate {
}
}

extension PledgeTableViewController: PledgeContinueCellDelegate {
func pledgeContinueCellDidTapContinue(_: PledgeContinueCell) {
self.viewModel.inputs.continueButtonTapped()
}
}

extension PledgeTableViewController: PledgeAmountCellDelegate {
func pledgeAmountCell(_: PledgeAmountCell, didUpdateAmount amount: Double) {
self.viewModel.inputs.pledgeAmountDidUpdate(to: amount)
Expand Down
8 changes: 8 additions & 0 deletions Kickstarter.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@
77C5E21A214182A2002E1670 /* SettingsPrivacySwitchCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 77C5E219214182A2002E1670 /* SettingsPrivacySwitchCell.xib */; };
77C5E252214182CA002E1670 /* SettingsPrivacySwitchCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C5E251214182CA002E1670 /* SettingsPrivacySwitchCell.swift */; };
77C7B654226E0E54001101AC /* RewardsCollectionViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C7B653226E0E54001101AC /* RewardsCollectionViewModelTests.swift */; };
77C93C1A22C13DED005D3195 /* PledgeContinueCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C93C1922C13DED005D3195 /* PledgeContinueCellViewModel.swift */; };
77CB227022C3B3CB00AEAAF1 /* PledgeContinueCellViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77CB226F22C3B3CB00AEAAF1 /* PledgeContinueCellViewModelTests.swift */; };
77C93C2522C27443005D3195 /* DebugData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 775D922822BD7CFD00442301 /* DebugData.swift */; };
77CD894921791B05003066DA /* ProjectStatsTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77CD894821791B05003066DA /* ProjectStatsTemplate.swift */; };
77CD8981217FA01B003066DA /* ProjectPamphletContentViewControllerConversionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77CD8980217FA01B003066DA /* ProjectPamphletContentViewControllerConversionTests.swift */; };
Expand Down Expand Up @@ -1492,6 +1494,8 @@
77C5E219214182A2002E1670 /* SettingsPrivacySwitchCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SettingsPrivacySwitchCell.xib; sourceTree = "<group>"; };
77C5E251214182CA002E1670 /* SettingsPrivacySwitchCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPrivacySwitchCell.swift; sourceTree = "<group>"; };
77C7B653226E0E54001101AC /* RewardsCollectionViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RewardsCollectionViewModelTests.swift; sourceTree = "<group>"; };
77C93C1922C13DED005D3195 /* PledgeContinueCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PledgeContinueCellViewModel.swift; sourceTree = "<group>"; };
77CB226F22C3B3CB00AEAAF1 /* PledgeContinueCellViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PledgeContinueCellViewModelTests.swift; sourceTree = "<group>"; };
77CD894821791B05003066DA /* ProjectStatsTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectStatsTemplate.swift; sourceTree = "<group>"; };
77CD8980217FA01B003066DA /* ProjectPamphletContentViewControllerConversionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectPamphletContentViewControllerConversionTests.swift; sourceTree = "<group>"; };
77D7A738214187A9003F258C /* SettingsSwitchCellType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSwitchCellType.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3318,6 +3322,8 @@
D66FB347218212B700A27BCC /* PaymentMethodsViewModelTests.swift */,
3706408122A8A66E00889CBD /* PledgeAmountCellViewModel.swift */,
3706408322A8A68600889CBD /* PledgeAmountCellViewModelTests.swift */,
77C93C1922C13DED005D3195 /* PledgeContinueCellViewModel.swift */,
77CB226F22C3B3CB00AEAAF1 /* PledgeContinueCellViewModelTests.swift */,
D77594DB226F9C9B005EE69B /* PledgeDescriptionCellViewModel.swift */,
D775953B22725289005EE69B /* PledgeDescriptionCellViewModelTests.swift */,
3706408522A8A6D700889CBD /* PledgeShippingLocationCellViewModel.swift */,
Expand Down Expand Up @@ -4470,6 +4476,7 @@
A707BADA1CFFAB9400653B2F /* Notifications.swift in Sources */,
A721DF651C8CF5A3000CB97C /* KoalaTrackingClient.swift in Sources */,
77556F5420A099B3008CEA57 /* Config+Helpers.swift in Sources */,
77C93C1A22C13DED005D3195 /* PledgeContinueCellViewModel.swift in Sources */,
A76126BB1C90C94000EDCCB9 /* UITableView-Extensions.swift in Sources */,
9D9F58191D13243900CE81DE /* ProjectActivitiesViewModel.swift in Sources */,
A72C3A8E1D00F6A80075227E /* SelectableRow.swift in Sources */,
Expand Down Expand Up @@ -4655,6 +4662,7 @@
A7ED20001E831C5C00BFFA01 /* SearchViewModelTests.swift in Sources */,
D08CD201219216BA009F89F0 /* WatchProjectViewModelTests.swift in Sources */,
A7ED1F291E830FDC00BFFA01 /* EnvironmentTests.swift in Sources */,
77CB227022C3B3CB00AEAAF1 /* PledgeContinueCellViewModelTests.swift in Sources */,
A7ED1F2E1E830FDC00BFFA01 /* LaunchedCountriesTests.swift in Sources */,
A7ED1FAD1E831C5C00BFFA01 /* DeprecatedCheckoutViewModelTests.swift in Sources */,
3705CF48222EE77F0025D37E /* EnvironmentVariablesTests.swift in Sources */,
Expand Down
37 changes: 37 additions & 0 deletions Library/ViewModels/PledgeContinueCellViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import Foundation
import ReactiveSwift

public protocol PledgeContinueCellViewModelOutputs {
var goToLoginSignup: Signal<Void, Never> { get }
ifbarrera marked this conversation as resolved.
Show resolved Hide resolved
}

public protocol PledgeContinueCellViewModelInputs {
func continueButtonTapped()
ifbarrera marked this conversation as resolved.
Show resolved Hide resolved
}

public protocol PledgeContinueCellViewModelType {
var inputs: PledgeContinueCellViewModelInputs { get }
var outputs: PledgeContinueCellViewModelOutputs { get }
}

public final class PledgeContinueCellViewModel: PledgeContinueCellViewModelType,
PledgeContinueCellViewModelInputs, PledgeContinueCellViewModelOutputs {
public init() {
self.goToLoginSignup = self.continueButtonTappedProperty.signal
}

private let continueButtonTappedProperty = MutableProperty(())
public func continueButtonTapped() {
self.continueButtonTappedProperty.value = ()
}

public let goToLoginSignup: Signal<Void, Never>

public var inputs: PledgeContinueCellViewModelInputs {
ifbarrera marked this conversation as resolved.
Show resolved Hide resolved
return self
}

public var outputs: PledgeContinueCellViewModelOutputs {
return self
}
}
29 changes: 29 additions & 0 deletions Library/ViewModels/PledgeContinueCellViewModelTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@testable import KsApi
@testable import Library
import Prelude
import ReactiveExtensions_TestHelpers
import XCTest

final class PledgeContinueCellViewModelTests: TestCase {
private let vm = PledgeContinueCellViewModel()

private let goToLoginSignup = TestObserver<Void, Never>()

override func setUp() {
super.setUp()

self.vm.outputs.goToLoginSignup.observe(self.goToLoginSignup.observer)
}

func testGoToLoginSignup() {
self.goToLoginSignup.assertDidNotEmitValue()

self.vm.inputs.continueButtonTapped()

self.goToLoginSignup.assertValueCount(1)

self.vm.inputs.continueButtonTapped()

self.goToLoginSignup.assertValueCount(2)
}
}
37 changes: 35 additions & 2 deletions Library/ViewModels/PledgeViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ public typealias PledgeViewData = (

public protocol PledgeViewModelInputs {
func configureWith(project: Project, reward: Reward)
func continueButtonTapped()
func userSessionStarted()
func pledgeAmountDidUpdate(to amount: Double)
func shippingRuleDidUpdate(to rule: ShippingRule)
func viewDidLoad()
}

public protocol PledgeViewModelOutputs {
var goToLoginSignup: Signal<LoginIntent, Never> { get }
var configureSummaryCellWithProjectAndPledgeTotal: Signal<(Project, Double), Never> { get }
var pledgeViewDataAndReload: Signal<(PledgeViewData, Bool), Never> { get }
}
Expand Down Expand Up @@ -59,22 +62,51 @@ public class PledgeViewModel: PledgeViewModelType, PledgeViewModelInputs, Pledge
(project, reward, isLoggedIn, reward.shipping.enabled, total)
}

let userUpdatedReload = data.takeWhen(self.userSessionStartedSignal)
ifbarrera marked this conversation as resolved.
Show resolved Hide resolved
.map { data -> (PledgeViewData, Bool) in
let loggedIn = AppEnvironment.current.currentUser != nil
return ((
project: data.project,
reward: data.reward,
isLoggedIn: loggedIn,
isShippingEnabled: data.isShippingEnabled,
pledgeTotal: data.pledgeTotal
), true)
}

let initialLoad = data.take(first: 1).map { data in (data, true) }
let silentReload = data.skip(first: 1).map { data in (data, false) }

self.pledgeViewDataAndReload = Signal.merge(
data.take(first: 1).map { data in (data, true) },
data.skip(first: 1).map { data in (data, false) }
initialLoad,
silentReload,
userUpdatedReload
)

self.goToLoginSignup = self.continueButtonTappedSignal
.map { _ in LoginIntent.backProject }

self.configureSummaryCellWithProjectAndPledgeTotal = self.pledgeViewDataAndReload
.filter(second >>> isFalse)
.map(first)
.map { ($0.project, $0.pledgeTotal) }
}

private let (continueButtonTappedSignal, continueButtonTappedObserver) = Signal<Void, Never>.pipe()
public func continueButtonTapped() {
self.continueButtonTappedObserver.send(value: ())
}

private let configureProjectAndRewardProperty = MutableProperty<(Project, Reward)?>(nil)
public func configureWith(project: Project, reward: Reward) {
self.configureProjectAndRewardProperty.value = (project, reward)
}

private let (userSessionStartedSignal, userSessionStartedObserver) = Signal<Void, Never>.pipe()
public func userSessionStarted() {
self.userSessionStartedObserver.send(value: ())
}

private let (pledgeAmountSignal, pledgeAmountObserver) = Signal<Double, Never>.pipe()
public func pledgeAmountDidUpdate(to amount: Double) {
self.pledgeAmountObserver.send(value: amount)
Expand All @@ -90,6 +122,7 @@ public class PledgeViewModel: PledgeViewModelType, PledgeViewModelInputs, Pledge
self.viewDidLoadProperty.value = ()
}

public let goToLoginSignup: Signal<LoginIntent, Never>
public let configureSummaryCellWithProjectAndPledgeTotal: Signal<(Project, Double), Never>
public let pledgeViewDataAndReload: Signal<(PledgeViewData, Bool), Never>

Expand Down
33 changes: 33 additions & 0 deletions Library/ViewModels/PledgeViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ReactiveExtensions_TestHelpers
final class PledgeViewModelTests: TestCase {
private let vm: PledgeViewModelType = PledgeViewModel()

private let goToLoginSignup = TestObserver<LoginIntent, Never>()
private let configureSummaryCellWithProject = TestObserver<Project, Never>()
private let configureSummaryCellWithPledgeTotal = TestObserver<Double, Never>()
private let project = TestObserver<Project, Never>()
Expand All @@ -24,6 +25,7 @@ final class PledgeViewModelTests: TestCase {
override func setUp() {
super.setUp()

self.vm.outputs.goToLoginSignup.observe(self.goToLoginSignup.observer)
self.vm.outputs.configureSummaryCellWithProjectAndPledgeTotal.map(first)
.observe(self.configureSummaryCellWithProject.observer)
self.vm.outputs.configureSummaryCellWithProjectAndPledgeTotal.map(second)
Expand Down Expand Up @@ -73,6 +75,37 @@ final class PledgeViewModelTests: TestCase {
}
}

func testLoginSignup() {
let project = Project.template
let reward = Reward.template
let user = User.template

self.vm.inputs.configureWith(project: project, reward: reward)
self.vm.inputs.viewDidLoad()

self.project.assertValues([project])
self.reward.assertValues([reward])
self.isLoggedIn.assertValues([false])
self.isShippingEnabled.assertValues([false])
self.total.assertValues([reward.minimum])
self.reload.assertValues([true])

self.vm.inputs.continueButtonTapped()

self.goToLoginSignup.assertValue(LoginIntent.backProject)

withEnvironment(currentUser: user) {
self.vm.inputs.userSessionStarted()

self.project.assertValues([project, project])
self.reward.assertValues([reward, reward])
self.isLoggedIn.assertValues([false, true])
self.isShippingEnabled.assertValues([false, false])
self.total.assertValues([reward.minimum, reward.minimum])
self.reload.assertValues([true, true])
}
}

func testReloadWithData_ShippingEnabled() {
let reward = Reward.template
|> Reward.lens.shipping .~ (.template |> Reward.Shipping.lens.enabled .~ true)
Expand Down