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] Description Cell User Interaction #668

Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
48419bd
wip description cell
cdolm92 Apr 11, 2019
ebbdb75
wip description cell, new strings
cdolm92 Apr 11, 2019
604d4e9
wip description cell, refactor
cdolm92 Apr 12, 2019
6d4c1dd
Merge branch 'feature-native-checkout' of https://github.com/kickstar…
cdolm92 Apr 17, 2019
559e8dc
wip- estimated deloivery date from reward
cdolm92 Apr 18, 2019
ca5e784
wip- datasource test
cdolm92 Apr 18, 2019
aff2427
new snapshots
cdolm92 Apr 18, 2019
45bc6a7
stackview refactor
cdolm92 Apr 22, 2019
060cbc5
stackview func name change
cdolm92 Apr 22, 2019
8021d27
func name change
cdolm92 Apr 22, 2019
73bd520
corrected datasource test
cdolm92 Apr 22, 2019
4db0ad7
refactor
cdolm92 Apr 22, 2019
c286076
setup delegate
cdolm92 Apr 22, 2019
33f37a0
pr feedback
cdolm92 Apr 22, 2019
a3a6c59
pr feedback
cdolm92 Apr 23, 2019
4b165e4
swiftlint fix
cdolm92 Apr 23, 2019
3e0bb0b
datasource test fix
cdolm92 Apr 23, 2019
fbfdbb8
datasource test fix
cdolm92 Apr 23, 2019
9fc2578
pledgevm
cdolm92 Apr 23, 2019
a66d7ca
Merge branch 'feature-native-checkout' into feature-native-checkout-d…
cdolm92 Apr 23, 2019
866fb98
description string
cdolm92 Apr 23, 2019
a8c6f62
fixing lib/framework tests
cdolm92 Apr 24, 2019
62ea450
new snapshots for deprecated rewards
cdolm92 Apr 24, 2019
6d61507
using new string
cdolm92 Apr 24, 2019
7b32601
wip- presenting trust and safety now
cdolm92 Apr 25, 2019
8270f29
Merge branch 'feature-native-checkout' of https://github.com/kickstar…
cdolm92 Apr 25, 2019
6cd6e57
Merge branch 'feature-native-checkout-description-cell' of https://gi…
cdolm92 Apr 25, 2019
ff29832
wip- vm tests
cdolm92 Apr 25, 2019
d9cc0ee
wip- refactor
cdolm92 Apr 26, 2019
2b837e6
wip
cdolm92 Apr 30, 2019
40e9329
button
cdolm92 Apr 30, 2019
162ea87
learn more higlight color change
cdolm92 May 1, 2019
80fe6ca
Merge branch 'feature-native-checkout' of https://github.com/kickstar…
cdolm92 May 6, 2019
6a7bfbd
snapshot tests
cdolm92 May 6, 2019
417f3e6
pr edit
cdolm92 May 6, 2019
4012757
pr edits
cdolm92 May 6, 2019
0a16081
snapshot tests and strings
cdolm92 May 6, 2019
3507018
Replace label and button with UITextView for tappable links
justinswart May 7, 2019
d114367
pr feedback
cdolm92 May 7, 2019
9444b89
font size removed
cdolm92 May 8, 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
77 changes: 55 additions & 22 deletions Kickstarter-iOS/Views/Cells/PledgeDescriptionCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ private enum Layout {
}
}

internal protocol PledgeDescriptionCellDelegate: class {
func pledgeDescriptionCellDidPresentTrustAndSafety(_ cell: PledgeDescriptionCell)
}

final class PledgeDescriptionCell: UITableViewCell, ValueCell {
fileprivate let viewModel = PledgeDescriptionCellViewModel()
internal weak var delegate: PledgeDescriptionCellDelegate?

// MARK: - Properties

private lazy var rootStackView: UIStackView = { UIStackView(frame: .zero) }()
Expand All @@ -25,9 +32,11 @@ final class PledgeDescriptionCell: UITableViewCell, ValueCell {
private lazy var estimatedDeliveryLabel: UILabel = { UILabel(frame: .zero) }()
private lazy var dateLabel: UILabel = { UILabel(frame: .zero) }()
private lazy var descriptionLabel: UILabel = { UILabel(frame: .zero) }()
private lazy var learnMoreLabel: UILabel = { UILabel(frame: .zero) }()
private lazy var spacerView: UIView = {
return UIView(frame: .zero) |> \.translatesAutoresizingMaskIntoConstraints .~ false }()
private lazy var spacerView: UIView = { UIView(frame: .zero) }()
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need the closure here? Or can't we just do private lazy var spacerView = UIView(frame: .zero)?

Copy link
Contributor

Choose a reason for hiding this comment

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

Good point! We actually don't need the closure. So there's going to be a number of places that we can change this. I think the function stuff is a hangover from having set local vars in that scope before to set properties on it, but seeing as we pipe forward to set these things it can all be done in-line.

private lazy var learnMoreButton: UIButton = {
return MultiLineButton(type: .custom)
|> \.translatesAutoresizingMaskIntoConstraints .~ false
}()

// MARK: - Lifecycle

Expand All @@ -46,6 +55,8 @@ final class PledgeDescriptionCell: UITableViewCell, ValueCell {
|> ksr_constrainViewToEdgesInParent()

self.configureStackView()
self.learnMoreButton.addTarget(self, action: #selector(learnMoreButtonTapped), for: .touchUpInside)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we move all this out of the init and into a private configureSubviews function?

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

    self.configureSubviews()
    self.bindViewModel()
  }

self.bindViewModel()
Copy link
Contributor

Choose a reason for hiding this comment

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

Note-to-self that we're going to find a way to override UITableViewCell initializers to not have to do this.


NSLayoutConstraint.activate([
self.containerImageView.widthAnchor.constraint(equalToConstant: Layout.ImageView.width),
Expand Down Expand Up @@ -90,17 +101,10 @@ final class PledgeDescriptionCell: UITableViewCell, ValueCell {
_ = self.descriptionLabel
|> descriptionLabelStyle

_ = self.learnMoreLabel
_ = self.learnMoreButton
|> checkoutBackgroundStyle
_ = self.learnMoreLabel
|> learnMoreLabelStyle
}

// MARK: - Configuration

func configureWith(value: String) {
_ = self.dateLabel
|> \.text .~ value
_ = self.learnMoreButton
Copy link
Contributor

Choose a reason for hiding this comment

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

Are these styles split up for compiler reasons? Or can we do:

_ = self.learnMoreButton
  |> checkoutBackgroundStyle
  |> learnMoreButtonStyle

Copy link
Contributor Author

@cdolm92 cdolm92 May 7, 2019

Choose a reason for hiding this comment

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

These are split because the compiler gives us an error. This is because learnMoreButtonStyle is expecting a UITextView but checkoutBackgroundStyle is returning a UIView.
Written like this it would work:

_ = self.learnMoreButton
  |> learnMoreButtonStyle
  |> checkoutBackgroundStyle

Let me know if I should make that change.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We also split it because the first part sets the background and the next the style. That way we don't have to worry about the order of passing the UITextView.

|> learnMoreButtonStyle
}

private func configureStackView() {
Expand All @@ -112,7 +116,7 @@ final class PledgeDescriptionCell: UITableViewCell, ValueCell {
self.estimatedDeliveryLabel,
self.dateLabel,
self.descriptionLabel,
self.learnMoreLabel], self.descriptionStackView)
self.learnMoreButton], self.descriptionStackView)
|> ksr_addArrangedSubviewsToStackView()

if #available(iOS 11.0, *) {
Expand All @@ -124,10 +128,36 @@ final class PledgeDescriptionCell: UITableViewCell, ValueCell {
view.heightAnchor.constraint(equalToConstant: Layout.SpacerView.height).isActive = true
self.descriptionStackView.insertArrangedSubview(view, at: 3)
}

_ = ([self.descriptionStackView], self.rootStackView)
|> ksr_addArrangedSubviewsToStackView()
}

// MARK: - Binding

internal override func bindViewModel() {
super.bindViewModel()

self.dateLabel.rac.text = self.viewModel.outputs.estimatedDeliveryText

self.viewModel.outputs.presentTrustAndSafety
.observeForUI()
.observeValues { [weak self] in
guard let _self = self else { return }
self?.delegate?.pledgeDescriptionCellDidPresentTrustAndSafety(_self)
Copy link
Contributor

Choose a reason for hiding this comment

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

Man, this seems really confusing (using two self references)...wouldn't this suffice?

guard let self = self else { return }
self.delegate.pledgeDescriptionCellDidPresentTrustAndSafety(self)

}
}

// MARK: - Actions

@objc private func learnMoreButtonTapped() {
self.viewModel.inputs.tapped()
}

// MARK: - Configuration

internal func configureWith(value: String) {
self.viewModel.inputs.configureWith(estimatedDeliveryDate: value)
}
}

private let rootStackViewStyle: StackViewStyle = { (stackView: UIStackView) in
Expand Down Expand Up @@ -172,11 +202,14 @@ private let descriptionLabelStyle: LabelStyle = { (label: UILabel) in
|> \.numberOfLines .~ 0
}

private let learnMoreLabelStyle: LabelStyle = { (label: UILabel) in
label
|> \.text %~ { _ in "\(Strings.Learn_more_about_accountability())." }
|> \.textColor .~ UIColor.ksr_green_500
|> \.font .~ UIFont.ksr_caption1()
|> \.adjustsFontForContentSizeCategory .~ true
|> \.numberOfLines .~ 0
private let learnMoreButtonStyle = { (button: UIButton) -> UIButton in
button
|> UIButton.lens.titleColor(for: .normal) .~ UIColor.ksr_green_500
|> UIButton.lens.titleLabel.font %~~ { _, label in
label.traitCollection.isRegularRegular ? .ksr_body(size: 17.0) : .ksr_body(size: 14.0)
}
|> UIButton.lens.contentHorizontalAlignment .~ .left
|> UIButton.lens.titleColor(for: .highlighted) .~ .ksr_text_dark_grey_500
|> UIButton.lens.title(for: .normal) %~ { _ in
return Strings.Learn_more_about_accountability() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@ internal final class DeprecatedRewardPledgeViewController: UIViewController {

_ = self.disclaimerButton
|> UIButton.lens.accessibilityLabel %~ { _ in
Strings.Kickstarter_is_not_a_store_Its_a_way_to_bring_creative_projects_to_life()
Strings.Kickstarter_is_not_a_store()
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for reverting this back..missed it in my prior PR review

+ " " + Strings.Its_a_way_to_bring_creative_projects_to_life()
+ " " + Strings.Learn_more_about_accountability()
}

Expand Down
16 changes: 16 additions & 0 deletions Kickstarter-iOS/Views/Controllers/PledgeTableViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,22 @@ class PledgeTableViewController: UITableViewController {
let footerView = tableView.dequeueReusableHeaderFooterView(withClass: PledgeFooterView.self)
return footerView
}

internal override func tableView(_ tableView: UITableView,
willDisplay cell: UITableViewCell,
forRowAt indexPath: IndexPath) {
if let descriptionCell = cell as? PledgeDescriptionCell {
descriptionCell.delegate = self
Copy link
Contributor

Choose a reason for hiding this comment

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

Could have used prelude for this _ = descriptionCell |> \.delegate .~ self

}
}
}

extension PledgeTableViewController: PledgeDescriptionCellDelegate {
internal func pledgeDescriptionCellDidPresentTrustAndSafety(_ cell: PledgeDescriptionCell) {
let vc = HelpWebViewController.configuredWith(helpType: .trust)
let nav = UINavigationController(rootViewController: vc)
self.present(nav, animated: true, completion: nil)
}
}

// MARK: - Styles
Expand Down
8 changes: 8 additions & 0 deletions Kickstarter.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,8 @@
D770DDE8217D729300B5319A /* UserCurrencyTemplates.swift in Sources */ = {isa = PBXBuildFile; fileRef = D770DDE7217D729300B5319A /* UserCurrencyTemplates.swift */; };
D770DE75217E598C00B5319A /* AddNewCardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D770DE3D217E339500B5319A /* AddNewCardViewController.swift */; };
D770DE78217E876000B5319A /* AddNewCardViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D770DE76217E874F00B5319A /* AddNewCardViewControllerTests.swift */; };
D77594DC226F9C9C005EE69B /* PledgeDescriptionCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77594DB226F9C9B005EE69B /* PledgeDescriptionCellViewModel.swift */; };
D775953C22725289005EE69B /* PledgeDescriptionCellViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D775953B22725289005EE69B /* PledgeDescriptionCellViewModelTests.swift */; };
D775EBEA207C09FD00885634 /* CrossDissolveTransitionAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79076F8207BC161008014EC /* CrossDissolveTransitionAnimator.swift */; };
D775EC3020812C6500885634 /* ProjectStatsEnvelope.ReferralAggregateStatsTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D775EC2F20812C6500885634 /* ProjectStatsEnvelope.ReferralAggregateStatsTemplate.swift */; };
D775EC682084FDE800885634 /* ProjectStatsEnvelope.ReferralAggregateStatsLenses.swift in Sources */ = {isa = PBXBuildFile; fileRef = D775EC672084FDE800885634 /* ProjectStatsEnvelope.ReferralAggregateStatsLenses.swift */; };
Expand Down Expand Up @@ -2916,6 +2918,8 @@
D770DDE7217D729300B5319A /* UserCurrencyTemplates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserCurrencyTemplates.swift; sourceTree = "<group>"; };
D770DE3D217E339500B5319A /* AddNewCardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddNewCardViewController.swift; sourceTree = "<group>"; };
D770DE76217E874F00B5319A /* AddNewCardViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddNewCardViewControllerTests.swift; sourceTree = "<group>"; };
D77594DB226F9C9B005EE69B /* PledgeDescriptionCellViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PledgeDescriptionCellViewModel.swift; sourceTree = "<group>"; };
D775953B22725289005EE69B /* PledgeDescriptionCellViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PledgeDescriptionCellViewModelTests.swift; sourceTree = "<group>"; };
D775EC2F20812C6500885634 /* ProjectStatsEnvelope.ReferralAggregateStatsTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectStatsEnvelope.ReferralAggregateStatsTemplate.swift; sourceTree = "<group>"; };
D775EC672084FDE800885634 /* ProjectStatsEnvelope.ReferralAggregateStatsLenses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectStatsEnvelope.ReferralAggregateStatsLenses.swift; sourceTree = "<group>"; };
D77743E2217A2D67008D679F /* UpdateUserProfileMutation .swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UpdateUserProfileMutation .swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4242,6 +4246,8 @@
D7A86A3A1F324EB300C7DA53 /* MostPopularSearchProjectCellViewModelTests.swift */,
D63BBD34217FAB85007E01F0 /* PaymentMethodsViewModel.swift */,
D66FB347218212B700A27BCC /* PaymentMethodsViewModelTests.swift */,
D77594DB226F9C9B005EE69B /* PledgeDescriptionCellViewModel.swift */,
D775953B22725289005EE69B /* PledgeDescriptionCellViewModelTests.swift */,
37DEC1E62257C9F30051EF9B /* PledgeViewModel.swift */,
37DEC21F2257CA0A0051EF9B /* PledgeViewModelTests.swift */,
A7F441A31D005A9400FE6FC5 /* ProfileViewModel.swift */,
Expand Down Expand Up @@ -5866,6 +5872,7 @@
A75CFB081CCE7FCF004CD5FA /* StaticTableViewCell.swift in Sources */,
A7F441D71D005A9400FE6FC5 /* ProfileViewModel.swift in Sources */,
597073521D05FE6B00B00444 /* ProjectNotificationsViewModel.swift in Sources */,
D77594DC226F9C9C005EE69B /* PledgeDescriptionCellViewModel.swift in Sources */,
77F6E73721222E97005A5C55 /* SettingsCellType.swift in Sources */,
9D1A2A4F1D5D200E009E1B3F /* DeprecatedCheckoutViewModel.swift in Sources */,
D07227021E671B4500C2E537 /* LiveStreamContainerPageViewModel.swift in Sources */,
Expand Down Expand Up @@ -6117,6 +6124,7 @@
A7ED1F3A1E830FDC00BFFA01 /* UILabel+SimpleHTMLTests.swift in Sources */,
D6ED1B37216D0C64007F7547 /* ChangeEmailViewModelTests.swift in Sources */,
A7ED1F271E830FDC00BFFA01 /* AppEnvironmentTests.swift in Sources */,
D775953C22725289005EE69B /* PledgeDescriptionCellViewModelTests.swift in Sources */,
D6B4F00021079F750079159D /* SettingsNewslettersCellViewModelTests.swift in Sources */,
A7ED1FE71E831C5C00BFFA01 /* FacebookConfirmationViewModelTests.swift in Sources */,
A7ED1FFE1E831C5C00BFFA01 /* RewardCellViewModelTests.swift in Sources */,
Expand Down
4 changes: 2 additions & 2 deletions Library/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3746,8 +3746,8 @@ with friends."
- **de**: "Kickstarter ist kein GeschΓ€ft."
- **es**: "Kickstarter no es una tienda."
- **fr**: "Kickstarter n'est pas un magasin."
- **ja**: "Kickstarter は商品を購ε…₯γ™γ‚‹ε ΄γ§γ―γ‚γ‚ŠγΎγ›γ‚“γ€‚"
*/
- **ja**: "Kickstarterはγ‚ͺγƒ³γƒ©γ‚€γƒ³γ‚Ήγƒˆγ‚’γ§γ―γ‚γ‚ŠγΎγ›γ‚“γ€‚"
*/
public static func Kickstarter_is_not_a_store() -> String {
return localizedString(
key: "Kickstarter_is_not_a_store",
Expand Down
46 changes: 46 additions & 0 deletions Library/ViewModels/PledgeDescriptionCellViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import Foundation
import KsApi
import Prelude
import ReactiveSwift
import ReactiveExtensions
import Result

public protocol PledgeDescriptionCellViewModelInputs {
func configureWith(estimatedDeliveryDate: String)
func tapped()
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use a more specific name for this input? Maybe learnMoreButtonTapped?

}

public protocol PledgeDescriptionCellViewModelOutputs {
var estimatedDeliveryText: Signal<String, NoError> { get }
var presentTrustAndSafety: Signal<Void, NoError> { get }
}

public protocol PledgeDescriptionCellViewModelType {
var inputs: PledgeDescriptionCellViewModelInputs { get }
var outputs: PledgeDescriptionCellViewModelOutputs { get }
}

public final class PledgeDescriptionCellViewModel: PledgeDescriptionCellViewModelType,
PledgeDescriptionCellViewModelInputs, PledgeDescriptionCellViewModelOutputs {

public init() {
self.estimatedDeliveryText = self.estimatedDeliveryDateProperty.signal

self.presentTrustAndSafety = self.tappedProperty.signal
}

private let estimatedDeliveryDateProperty = MutableProperty<String>("")
public func configureWith(estimatedDeliveryDate: String) {
self.estimatedDeliveryDateProperty.value = estimatedDeliveryDate
}
private let tappedProperty = MutableProperty(())
public func tapped() {
self.tappedProperty.value = ()
}

public let estimatedDeliveryText: Signal<String, NoError>
public let presentTrustAndSafety: Signal<Void, NoError>

public var inputs: PledgeDescriptionCellViewModelInputs { return self }
public var outputs: PledgeDescriptionCellViewModelOutputs { return self }
}
34 changes: 34 additions & 0 deletions Library/ViewModels/PledgeDescriptionCellViewModelTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import XCTest
@testable import KsApi
@testable import Library
@testable import ReactiveExtensions_TestHelpers
import Prelude
import Result

internal final class PledgeDescriptionCellViewModelTests: TestCase {
private let vm: PledgeDescriptionCellViewModelType = PledgeDescriptionCellViewModel()

private let estimatedDeliveryText = TestObserver<String, NoError>()
private let presentTrustAndSafety = TestObserver<Void, NoError>()

override func setUp() {
super.setUp()

self.vm.outputs.estimatedDeliveryText.observe(estimatedDeliveryText.observer)
self.vm.outputs.presentTrustAndSafety.observe(presentTrustAndSafety.observer)
}

func testEstimatedDeliveryDate() {
let estimatedDelivery = 1468527587.32843

let date = Format.date(secondsInUTC: estimatedDelivery, template: "MMMMyyyy", timeZone: UTCTimeZone)

self.vm.inputs.configureWith(estimatedDeliveryDate: date)
self.estimatedDeliveryText.assertValues(["July 2016"], "Emits the estimated delivery date")
}

func testPresentTrustAndSafety() {
self.vm.inputs.tapped()
self.presentTrustAndSafety.assertDidEmitValue()
}
}