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] Pledge Continue Button - UI Only #670

Merged
merged 9 commits into from
May 8, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
14 changes: 13 additions & 1 deletion Kickstarter-iOS/DataSources/PledgeDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ final class PledgeDataSource: ValueCellDataSource {
case summary
}

func load(amount: Double, currency: String, delivery: String) {
func load(amount: Double, currency: String, delivery: String, isLoggedIn: Bool) {
self.appendRow(
value: delivery,
cellClass: PledgeDescriptionCell.self,
Expand All @@ -28,11 +28,21 @@ final class PledgeDataSource: ValueCellDataSource {
toSection: Section.inputs.rawValue
)

self.loadSummarySection(isLoggedIn: isLoggedIn)
}

private func loadSummarySection(isLoggedIn: Bool) {
self.appendRow(
value: "Total",
cellClass: PledgeRowCell.self,
toSection: Section.summary.rawValue
)

if !isLoggedIn {
self.appendRow(value: (),
cellClass: PledgeContinueCell.self,
toSection: Section.summary.rawValue)
}
}

override func configureCell(tableCell cell: UITableViewCell, withValue value: Any) {
Expand All @@ -45,6 +55,8 @@ final class PledgeDataSource: ValueCellDataSource {
cell.configureWith(value: value)
case let (cell as PledgeShippingLocationCell, value as (String, String, Double)):
cell.configureWith(value: value)
case let (cell as PledgeContinueCell, value as ()):
cell.configureWith(value: value)
default:
assertionFailure("Unrecognized (cell, viewModel) combo.")
}
Expand Down
19 changes: 16 additions & 3 deletions Kickstarter-iOS/DataSources/PledgeDataSourceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ final class PledgeDataSourceTests: XCTestCase {
let tableView = UITableView(frame: .zero, style: .plain)

// swiftlint:disable line_length
func testLoad() {

self.dataSource.load(amount: 100, currency: "USD", delivery: "May 2020")
func testLoad_loggedIn() {
self.dataSource.load(amount: 100, currency: "USD", delivery: "May 2020", isLoggedIn: true)

XCTAssertEqual(3, self.dataSource.numberOfSections(in: self.tableView))
XCTAssertEqual(1, self.dataSource.tableView(self.tableView, numberOfRowsInSection: 0))
Expand All @@ -21,5 +20,19 @@ final class PledgeDataSourceTests: XCTestCase {
XCTAssertEqual(PledgeShippingLocationCell.defaultReusableId, self.dataSource.reusableId(item: 1, section: 1))
XCTAssertEqual(PledgeRowCell.defaultReusableId, self.dataSource.reusableId(item: 0, section: 2))
}

func testLoad_loggedOut() {
self.dataSource.load(amount: 100, currency: "USD", delivery: "May 2020", isLoggedIn: false)

XCTAssertEqual(3, self.dataSource.numberOfSections(in: self.tableView))
XCTAssertEqual(1, self.dataSource.tableView(self.tableView, numberOfRowsInSection: 0))
XCTAssertEqual(2, self.dataSource.tableView(self.tableView, numberOfRowsInSection: 1))
XCTAssertEqual(2, self.dataSource.tableView(self.tableView, numberOfRowsInSection: 2))
XCTAssertEqual(PledgeDescriptionCell.defaultReusableId, self.dataSource.reusableId(item: 0, section: 0))
XCTAssertEqual(PledgeAmountCell.defaultReusableId, self.dataSource.reusableId(item: 0, section: 1))
XCTAssertEqual(PledgeShippingLocationCell.defaultReusableId, self.dataSource.reusableId(item: 1, section: 1))
XCTAssertEqual(PledgeRowCell.defaultReusableId, self.dataSource.reusableId(item: 0, section: 2))
XCTAssertEqual(PledgeContinueCell.defaultReusableId, self.dataSource.reusableId(item: 1, section: 2))
}
// swiftlint:enable line_length
}
46 changes: 46 additions & 0 deletions Kickstarter-iOS/Views/Cells/PledgeContinueCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import Foundation
import Library
import Prelude

final class PledgeContinueCell: UITableViewCell, ValueCell {
private let continueButton = MultiLineButton(type: .custom)

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

self.setupSubviews()
}

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

override func bindStyles() {
super.bindStyles()

_ = self
|> \.backgroundColor .~ .ksr_grey_300

_ = self.contentView
|> \.layoutMargins .~ .init(all: Styles.grid(3))

_ = self.continueButton
|> checkoutGreenButtonStyle
|> UIButton.lens.title(for: .normal) %~ { _ in
return Strings.Continue()
}

_ = self.continueButton.titleLabel
?|> checkoutGreenButtonTitleLabelStyle
}

func configureWith(value: ()) {}

private func setupSubviews() {
_ = (self.continueButton, self.contentView)
|> ksr_addSubviewToParent()
|> ksr_constrainViewToMarginsInParent()

self.continueButton.heightAnchor.constraint(greaterThanOrEqualToConstant: Styles.grid(8)).isActive = true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class PledgeTableViewController: UITableViewController {
|> \.dataSource .~ self.dataSource

self.tableView.registerCellClass(PledgeAmountCell.self)
self.tableView.registerCellClass(PledgeContinueCell.self)
self.tableView.registerCellClass(PledgeDescriptionCell.self)
self.tableView.registerCellClass(PledgeRowCell.self)
self.tableView.registerCellClass(PledgeShippingLocationCell.self)
Expand All @@ -48,10 +49,11 @@ class PledgeTableViewController: UITableViewController {
override func bindViewModel() {
super.bindViewModel()

self.viewModel.outputs.amountCurrencyAndDelivery
self.viewModel.outputs.reloadWithData
.observeForUI()
.observeValues { [weak self] (amount, currency, delivery) in
self?.dataSource.load(amount: amount, currency: currency, delivery: delivery)
.observeValues { [weak self] (amount, currency, delivery, isLoggedIn) in
self?.dataSource.load(amount: amount, currency: currency, delivery: delivery, isLoggedIn: isLoggedIn)

self?.tableView.reloadData()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,13 @@ public final class ProjectPamphletViewController: UIViewController {
|> \.layoutMargins .~ .init(all: backThisProjectContainerViewMargins)

_ = self.backThisProjectButton
|> backThisProjectButtonStyle
|> checkoutGreenButtonStyle
|> UIButton.lens.title(for: .normal) %~ { _ in
return Strings.project_back_button()
}

_ = self.backThisProjectButton.titleLabel
?|> backThisProjectButtonTitleLabelStyle
?|> checkoutGreenButtonTitleLabelStyle
}

public override func bindViewModel() {
Expand Down Expand Up @@ -273,29 +276,3 @@ extension ProjectPamphletViewController: ProjectNavBarViewControllerDelegate {
self.contentController.tableView.scrollToTop()
}
}

// MARK: - Styles

private var backThisProjectButtonStyle = { (button: UIButton) -> UIButton in
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved these styles to CheckoutStyles.swift so we can reuse them for all green buttons

button
|> greenButtonStyle
|> roundedStyle(cornerRadius: 12)
|> UIButton.lens.layer.borderWidth .~ 0
|> UIButton.lens.titleEdgeInsets .~ .init(topBottom: Styles.grid(1), leftRight: Styles.grid(2))
|> UIButton.lens.title(for: .normal) %~ { _ in
return Strings.project_back_button()
}
}

private var backThisProjectButtonTitleLabelStyle = { (titleLabel: UILabel?) -> UILabel? in
_ = titleLabel
?|> \.font .~ UIFont.ksr_headline()
?|> \.numberOfLines .~ 0

// Breaking this up to help the compiler
_ = titleLabel
?|> \.textAlignment .~ NSTextAlignment.center
?|> \.lineBreakMode .~ NSLineBreakMode.byWordWrapping

return titleLabel
}
9 changes: 7 additions & 2 deletions Kickstarter.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,6 @@
774A76EE20D863EF0012A71F /* BetaTools.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 774A76AF20D83F6B0012A71F /* BetaTools.storyboard */; };
774A76F520D98EEF0012A71F /* BetaToolsViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 774A76F420D98EEF0012A71F /* BetaToolsViewControllerTests.swift */; };
774ACC19225F8DC30097FCE6 /* ProjectPamphletViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 774ACC18225F8DC30097FCE6 /* ProjectPamphletViewControllerTests.swift */; };
77502FC22147D92000620BBC /* Stripe.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 775229A72146805400595846 /* Stripe.framework */; };
77539E6C2147E4EE00A564CD /* Stripe.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 775229A72146805400595846 /* Stripe.framework */; };
77539E6D2147E50E00A564CD /* Stripe.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 775229A72146805400595846 /* Stripe.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
7754A0AE215A8361003AA36D /* ChangePasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7754A0AD215A8361003AA36D /* ChangePasswordViewController.swift */; };
Expand Down Expand Up @@ -220,6 +219,7 @@
77E6440120F64F0B005F6B38 /* HelpDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77E6440020F64F0B005F6B38 /* HelpDataSource.swift */; };
77E6440320F65074005F6B38 /* HelpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77E6440220F65074005F6B38 /* HelpViewController.swift */; };
77E84E0C2166A8C600DA8891 /* MessageBannerViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77E84E0B2166A8C600DA8891 /* MessageBannerViewControllerTests.swift */; };
77ED5200227A004700DAAD13 /* Stripe.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 775229A72146805400595846 /* Stripe.framework */; };
77EFBAA62268D32000DA5C3C /* RewardsCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77EFBAA52268D32000DA5C3C /* RewardsCollectionViewController.swift */; };
77EFBAE02268D34A00DA5C3C /* RewardsCollectionViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77EFBADE2268D33E00DA5C3C /* RewardsCollectionViewControllerTests.swift */; };
77EFBAE32268D48A00DA5C3C /* RewardsCollectionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77EFBAE12268D44D00DA5C3C /* RewardsCollectionViewModel.swift */; };
Expand All @@ -237,6 +237,7 @@
77FA6CAB20F3E3C200809E31 /* SettingsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 77FA6CAA20F3E3C200809E31 /* SettingsTableViewCell.xib */; };
77FA6CD220F53E5E00809E31 /* SettingsDataSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77FA6CD120F53E5E00809E31 /* SettingsDataSourceTests.swift */; };
77FB8BF720F6586500F91EEB /* SettingsHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 77FB8BF620F6586500F91EEB /* SettingsHeaderView.xib */; };
77FD60A122735AED0084B84C /* PledgeContinueCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77FD60A022735AED0084B84C /* PledgeContinueCell.swift */; };
77FD8B44216D6167000A95AC /* LoadingBarButtonItemView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 77FD8B43216D6167000A95AC /* LoadingBarButtonItemView.xib */; };
77FD8B46216D6245000A95AC /* LoadingBarButtonItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77FD8B45216D6245000A95AC /* LoadingBarButtonItemView.swift */; };
7DD8EE241F02DBED0070B63D /* Bolts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7DD8EE231F02DBED0070B63D /* Bolts.framework */; };
Expand Down Expand Up @@ -2058,6 +2059,7 @@
77FA6CAA20F3E3C200809E31 /* SettingsTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SettingsTableViewCell.xib; sourceTree = "<group>"; };
77FA6CD120F53E5E00809E31 /* SettingsDataSourceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDataSourceTests.swift; sourceTree = "<group>"; };
77FB8BF620F6586500F91EEB /* SettingsHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SettingsHeaderView.xib; sourceTree = "<group>"; };
77FD60A022735AED0084B84C /* PledgeContinueCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PledgeContinueCell.swift; sourceTree = "<group>"; };
77FD8B43216D6167000A95AC /* LoadingBarButtonItemView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = LoadingBarButtonItemView.xib; sourceTree = "<group>"; };
77FD8B45216D6245000A95AC /* LoadingBarButtonItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingBarButtonItemView.swift; sourceTree = "<group>"; };
7DD8EE231F02DBED0070B63D /* Bolts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Bolts.framework; path = Frameworks/FBSDK/iOS/Bolts.framework; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3023,8 +3025,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
77502FC22147D92000620BBC /* Stripe.framework in Frameworks */,
A701B5EF1EB384E1001D4E5F /* PassKit.framework in Frameworks */,
77ED5200227A004700DAAD13 /* Stripe.framework in Frameworks */,
D0D2ABA21E964852008D298A /* libsqlite3.tbd in Frameworks */,
D0D2AB5B1E964849008D298A /* CoreMedia.framework in Frameworks */,
A709698C1D1468A200DB39D3 /* Alamofire.framework in Frameworks */,
Expand Down Expand Up @@ -3457,6 +3459,8 @@
A773531E1D5E8AEF0017E239 /* MostPopularSearchProjectCell.swift */,
A7B1EBB21D90496A00BEE8B3 /* NoRewardCell.swift */,
A74382041D3458C900040A95 /* PaddingCell.swift */,
77FD60A022735AED0084B84C /* PledgeContinueCell.swift */,
37DEC22D2257CDD50051EF9B /* PledgeRowCell.swift */,
370ACAFF225D337900C8745F /* PledgeAmountCell.swift */,
D79A054A225E9B6B004BC6A8 /* PledgeDescriptionCell.swift */,
37DEC22D2257CDD50051EF9B /* PledgeRowCell.swift */,
Expand Down Expand Up @@ -6386,6 +6390,7 @@
D08A817C1DFAAD08000128DB /* LiveStreamCountdownViewController.swift in Sources */,
A773531F1D5E8AEF0017E239 /* MostPopularSearchProjectCell.swift in Sources */,
37E9E2A0225EABB000D29DD7 /* AmountInputView.swift in Sources */,
77FD60A122735AED0084B84C /* PledgeContinueCell.swift in Sources */,
A77352ED1D5E70FC0017E239 /* MostPopularCell.swift in Sources */,
D79F0F3721028C2600D3B32C /* SettingsPrivacyRecommendationCell.swift in Sources */,
D6B6766920FF8D850082717D /* SettingsNewslettersDataSource.swift in Sources */,
Expand Down
20 changes: 20 additions & 0 deletions Library/Styles/CheckoutStyles.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,26 @@ public func checkoutAdaptableStackViewStyle(_ isAccessibilityCategory: Bool) ->
}
}

public let checkoutGreenButtonStyle: ButtonStyle = { button -> UIButton in
button
|> greenButtonStyle
|> roundedStyle(cornerRadius: 12)
|> UIButton.lens.layer.borderWidth .~ 0
|> UIButton.lens.titleEdgeInsets .~ .init(topBottom: Styles.grid(1), leftRight: Styles.grid(2))
}

public let checkoutGreenButtonTitleLabelStyle = { (titleLabel: UILabel?) -> UILabel? in
_ = titleLabel
?|> \.font .~ UIFont.ksr_headline()
?|> \.numberOfLines .~ 0

_ = titleLabel
?|> \.textAlignment .~ NSTextAlignment.center
?|> \.lineBreakMode .~ NSLineBreakMode.byWordWrapping

return titleLabel
}

public let checkoutBackgroundStyle: ViewStyle = { (view: UIView) in
view
|> \.backgroundColor .~ UIColor.ksr_grey_300
Expand Down
19 changes: 16 additions & 3 deletions Library/ViewModels/PledgeViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import Prelude
import ReactiveSwift
import Result

public typealias PledgeTableViewData = (amount: Double, currency: String, delivery: String, isLoggedIn: Bool)

public protocol PledgeViewModelInputs {
func configureWith(project: Project, reward: Reward)
func viewDidLoad()
}

public protocol PledgeViewModelOutputs {
var amountCurrencyAndDelivery: Signal<(Double, String, String), NoError> { get }
var reloadWithData: Signal<PledgeTableViewData, NoError> { get }
}

public protocol PledgeViewModelType {
Expand All @@ -26,12 +28,23 @@ public class PledgeViewModel: PledgeViewModelType, PledgeViewModelInputs, Pledge
.map(first)
.skipNil()

self.amountCurrencyAndDelivery = projectAndReward.signal
let isLoggedIn = projectAndReward
.map { _ in AppEnvironment.current.currentUser }
.map(isNotNil)

let amountCurrencyDelivery = projectAndReward.signal
.map { (project, reward) in
(reward.minimum,
currencySymbol(forCountry: project.country).trimmed(),
reward.estimatedDeliveryOn
.map { Format.date(secondsInUTC: $0, template: "MMMMyyyy", timeZone: UTCTimeZone) } ?? "") }

self.reloadWithData = Signal.combineLatest(amountCurrencyDelivery, isLoggedIn)
ifbarrera marked this conversation as resolved.
Show resolved Hide resolved
.map { amountCurrencyDelivery, isLoggedIn in
let (amount, currency, delivery) = amountCurrencyDelivery

return (amount, currency, delivery, isLoggedIn)
}
}

private let configureProjectAndRewardProperty = MutableProperty<(Project, Reward)?>(nil)
Expand All @@ -44,7 +57,7 @@ public class PledgeViewModel: PledgeViewModelType, PledgeViewModelInputs, Pledge
self.viewDidLoadProperty.value = ()
}

public let amountCurrencyAndDelivery: Signal<(Double, String, String), NoError>
public let reloadWithData: Signal<PledgeTableViewData, NoError>

public var inputs: PledgeViewModelInputs { return self }
public var outputs: PledgeViewModelOutputs { return self }
Expand Down
43 changes: 31 additions & 12 deletions Library/ViewModels/PledgeViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,49 @@ final class PledgeViewModelTests: TestCase {

private let amount = TestObserver<Double, NoError>()
private let currency = TestObserver<String, NoError>()
private let isLoggedIn = TestObserver<Bool, NoError>()
private let estimatedDelivery = TestObserver<String, NoError>()

override func setUp() {
super.setUp()

self.vm.outputs.amountCurrencyAndDelivery.map { $0.0 }.observe(self.amount.observer)
self.vm.outputs.amountCurrencyAndDelivery.map { $0.1 }.observe(self.currency.observer)
self.vm.outputs.amountCurrencyAndDelivery.map { $0.2 }.observe(self.estimatedDelivery.observer)
self.vm.outputs.reloadWithData.map { $0.amount }.observe(self.amount.observer)
self.vm.outputs.reloadWithData.map { $0.currency }.observe(self.currency.observer)
self.vm.outputs.reloadWithData.map { $0.isLoggedIn }.observe(self.isLoggedIn.observer)
self.vm.outputs.reloadWithData.map { $0.delivery }.observe(self.estimatedDelivery.observer)
}

func testAmountCurrencyAndEstimatedDeliveryDate() {
let estimatedDelivery = 1468527587.32843
func testReloadWithData_loggedOut() {

let project = Project.template
let reward = Reward.template

withEnvironment(currentUser: nil) {
self.vm.inputs.configureWith(project: project, reward: reward)
self.vm.inputs.viewDidLoad()

self.isLoggedIn.assertValues([false])
}
}

func testReloadWithData_loggedIn() {
let estimatedDelivery = 1468527587.32843
let project = Project.template
let reward = Reward.template
|> Reward.lens.estimatedDeliveryOn .~ estimatedDelivery

self.vm.inputs.configureWith(project: project, reward: reward)
self.vm.inputs.viewDidLoad()
let user = User.template

withEnvironment(currentUser: user) {
self.vm.inputs.configureWith(project: project, reward: reward)
self.vm.inputs.viewDidLoad()

self.amount.assertValues([10])
self.currency.assertValues(["$"])
self.estimatedDelivery.assertValues(
[Format.date(secondsInUTC: estimatedDelivery, template: "MMMMyyyy", timeZone: UTCTimeZone)]
)
self.amount.assertValues([10])
self.currency.assertValues(["$"])
self.isLoggedIn.assertValues([true])
self.estimatedDelivery.assertValues(
[Format.date(secondsInUTC: estimatedDelivery, template: "MMMMyyyy", timeZone: UTCTimeZone)]
)
}
}
}