Skip to content

Commit

Permalink
💲[Native Checkout] Pledge Summary Cell Layout (#715)
Browse files Browse the repository at this point in the history
* Pledge Summary Cell layout

* SwiftFormat

* Fix test

* 💲[Native Checkout] Pledge Summary Cell Handle Links (#717)

* Open links in web view

* Add tests

* DRY it up a little

* Formatting

* Remove PledgeRowCell

* Fix textview voiceover

* Fix amount label voice-over

* 💲[Native Checkout] Pledge Summary Cell Handle State Change (#718)

* Open links in web view

* Add tests

* DRY it up a little

* Formatting

* Handle amount state between cells

* swiftlint

* Add tests

* Move parentheses

* Pass ShippingRule to delegate

* total -> pledgeTotal

* Use the same output for reloading and updating the tableview

* Move a brace

* Add minimumScaleFactor to total label

* Abstract isShippingEnabled implementation detail to PledgeViewModel

* SwiftFormat lol

* Improve output naming and type, add tests
  • Loading branch information
justinswart committed Aug 5, 2019
1 parent dd8fd1c commit 0221e6e
Show file tree
Hide file tree
Showing 20 changed files with 742 additions and 107 deletions.
20 changes: 11 additions & 9 deletions Kickstarter-iOS/DataSources/PledgeDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,36 @@ final class PledgeDataSource: ValueCellDataSource {

// MARK: - Load

func load(project: Project, reward: Reward, isLoggedIn: Bool) {
func load(data: PledgeViewData) {
self.clearValues()

self.appendRow(
value: reward,
value: data.reward,
cellClass: PledgeDescriptionCell.self,
toSection: Section.project.rawValue
)

self.appendRow(
value: (project, reward),
value: (data.project, data.reward),
cellClass: PledgeAmountCell.self,
toSection: Section.inputs.rawValue
)

if reward.shipping.enabled {
if data.isShippingEnabled {
self.appendRow(
value: (project, reward),
value: (data.project, data.reward),
cellClass: PledgeShippingLocationCell.self,
toSection: Section.inputs.rawValue
)
}

self.appendRow(
value: Strings.Total(),
cellClass: PledgeRowCell.self,
value: (data.project, data.pledgeTotal),
cellClass: PledgeSummaryCell.self,
toSection: Section.summary.rawValue
)

if !isLoggedIn {
if !data.isLoggedIn {
self.appendRow(
value: (),
cellClass: PledgeContinueCell.self,
Expand All @@ -56,7 +58,7 @@ final class PledgeDataSource: ValueCellDataSource {
cell.configureWith(value: value)
case let (cell as PledgeDescriptionCell, value as Reward):
cell.configureWith(value: value)
case let (cell as PledgeRowCell, value as String):
case let (cell as PledgeSummaryCell, value as PledgeSummaryCellData):
cell.configureWith(value: value)
case let (cell as PledgeShippingLocationCell, value as (Project, Reward)):
cell.configureWith(value: value)
Expand Down
64 changes: 56 additions & 8 deletions Kickstarter-iOS/DataSources/PledgeDataSourceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,48 +11,96 @@ final class PledgeDataSourceTests: XCTestCase {
let tableView = UITableView(frame: .zero, style: .plain)

func testLoad_LoggedIn() {
self.dataSource.load(project: .template, reward: .template, isLoggedIn: true)
let data: PledgeViewData = (
project: .template, reward: .template, isLoggedIn: true, isShippingEnabled: false, pledgeTotal: 0.0
)
self.dataSource.load(data: data)

XCTAssertEqual(3, self.dataSource.numberOfSections(in: self.tableView))
XCTAssertEqual(1, self.dataSource.tableView(self.tableView, numberOfRowsInSection: 0))
XCTAssertEqual(1, self.dataSource.tableView(self.tableView, numberOfRowsInSection: 1))
XCTAssertEqual(1, 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(PledgeRowCell.defaultReusableId, self.dataSource.reusableId(item: 0, section: 2))
XCTAssertEqual(PledgeSummaryCell.defaultReusableId, self.dataSource.reusableId(item: 0, section: 2))
}

func testLoad_Idempotent() {
let data: PledgeViewData = (
project: .template, reward: .template, isLoggedIn: true, isShippingEnabled: false, pledgeTotal: 0.0
)
self.dataSource.load(data: data)

XCTAssertEqual(3, self.dataSource.numberOfSections(in: self.tableView))
XCTAssertEqual(1, self.dataSource.tableView(self.tableView, numberOfRowsInSection: 0))
XCTAssertEqual(1, self.dataSource.tableView(self.tableView, numberOfRowsInSection: 1))
XCTAssertEqual(1, 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(PledgeSummaryCell.defaultReusableId, self.dataSource.reusableId(item: 0, section: 2))

self.dataSource.load(data: data)

XCTAssertEqual(3, self.dataSource.numberOfSections(in: self.tableView))
XCTAssertEqual(1, self.dataSource.tableView(self.tableView, numberOfRowsInSection: 0))
XCTAssertEqual(1, self.dataSource.tableView(self.tableView, numberOfRowsInSection: 1))
XCTAssertEqual(1, 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(PledgeSummaryCell.defaultReusableId, self.dataSource.reusableId(item: 0, section: 2))
}

func testLoad_LoggedOut() {
self.dataSource.load(project: .template, reward: .template, isLoggedIn: false)
let data: PledgeViewData = (
project: .template, reward: .template, isLoggedIn: false, isShippingEnabled: false, pledgeTotal: 0.0
)
self.dataSource.load(data: data)

XCTAssertEqual(3, self.dataSource.numberOfSections(in: self.tableView))
XCTAssertEqual(1, self.dataSource.tableView(self.tableView, numberOfRowsInSection: 0))
XCTAssertEqual(1, 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(PledgeRowCell.defaultReusableId, self.dataSource.reusableId(item: 0, section: 2))
XCTAssertEqual(PledgeSummaryCell.defaultReusableId, self.dataSource.reusableId(item: 0, section: 2))
XCTAssertEqual(PledgeContinueCell.defaultReusableId, self.dataSource.reusableId(item: 1, section: 2))
}

func testLoad_Shipping_Disabled() {
self.dataSource.load(project: .template, reward: .template, isLoggedIn: false)
let reward = Reward.template

let data: PledgeViewData = (
project: .template,
reward: reward,
isLoggedIn: false,
isShippingEnabled: reward.shipping.enabled,
pledgeTotal: 0.0
)

self.dataSource.load(data: data)

XCTAssertEqual(3, self.dataSource.numberOfSections(in: self.tableView))
XCTAssertEqual(1, self.dataSource.tableView(self.tableView, numberOfRowsInSection: PledgeDataSource.Section.project.rawValue))
XCTAssertEqual(1, self.dataSource.tableView(self.tableView, numberOfRowsInSection: PledgeDataSource.Section.inputs.rawValue))
XCTAssertEqual(2, self.dataSource.tableView(self.tableView, numberOfRowsInSection: PledgeDataSource.Section.summary.rawValue))
XCTAssertEqual(PledgeDescriptionCell.defaultReusableId, self.dataSource.reusableId(item: 0, section: PledgeDataSource.Section.project.rawValue))
XCTAssertEqual(PledgeAmountCell.defaultReusableId, self.dataSource.reusableId(item: 0, section: PledgeDataSource.Section.inputs.rawValue))
XCTAssertEqual(PledgeRowCell.defaultReusableId, self.dataSource.reusableId(item: 0, section: PledgeDataSource.Section.summary.rawValue))
XCTAssertEqual(PledgeSummaryCell.defaultReusableId, self.dataSource.reusableId(item: 0, section: PledgeDataSource.Section.summary.rawValue))
XCTAssertEqual(PledgeContinueCell.defaultReusableId, self.dataSource.reusableId(item: 1, section: PledgeDataSource.Section.summary.rawValue))
}

func testLoad_Shipping_Enabled() {
let shipping = Reward.Shipping.template |> Reward.Shipping.lens.enabled .~ true
let reward = Reward.template |> Reward.lens.shipping .~ shipping
let data: PledgeViewData = (
project: .template,
reward: reward,
isLoggedIn: false,
isShippingEnabled: reward.shipping.enabled,
pledgeTotal: 0.0
)

self.dataSource.load(project: .template, reward: reward, isLoggedIn: false)
self.dataSource.load(data: data)

XCTAssertEqual(3, self.dataSource.numberOfSections(in: self.tableView))
XCTAssertEqual(1, self.dataSource.tableView(self.tableView, numberOfRowsInSection: PledgeDataSource.Section.project.rawValue))
Expand All @@ -61,7 +109,7 @@ final class PledgeDataSourceTests: XCTestCase {
XCTAssertEqual(PledgeDescriptionCell.defaultReusableId, self.dataSource.reusableId(item: 0, section: PledgeDataSource.Section.project.rawValue))
XCTAssertEqual(PledgeAmountCell.defaultReusableId, self.dataSource.reusableId(item: 0, section: PledgeDataSource.Section.inputs.rawValue))
XCTAssertEqual(PledgeShippingLocationCell.defaultReusableId, self.dataSource.reusableId(item: 1, section: PledgeDataSource.Section.inputs.rawValue))
XCTAssertEqual(PledgeRowCell.defaultReusableId, self.dataSource.reusableId(item: 0, section: PledgeDataSource.Section.summary.rawValue))
XCTAssertEqual(PledgeSummaryCell.defaultReusableId, self.dataSource.reusableId(item: 0, section: PledgeDataSource.Section.summary.rawValue))
XCTAssertEqual(PledgeContinueCell.defaultReusableId, self.dataSource.reusableId(item: 1, section: PledgeDataSource.Section.summary.rawValue))
}
}
21 changes: 1 addition & 20 deletions Kickstarter-iOS/ViewModels/HelpWebViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ internal final class HelpWebViewModel: HelpWebViewModelType, HelpWebViewModelInp
internal init() {
self.webViewLoadRequest = self.helpTypeProperty.signal.skipNil()
.takeWhen(self.viewDidLoadProperty.signal)
.map { urlForHelpType($0, baseUrl: AppEnvironment.current.apiService.serverConfig.webBaseUrl) }
.map { $0.url(withBaseUrl: AppEnvironment.current.apiService.serverConfig.webBaseUrl) }
.skipNil()
.map { AppEnvironment.current.apiService.preparedRequest(forURL: $0) }
}
Expand All @@ -44,22 +44,3 @@ internal final class HelpWebViewModel: HelpWebViewModelType, HelpWebViewModelInp
self.viewDidLoadProperty.value = ()
}
}

public func urlForHelpType(_ helpType: HelpType, baseUrl: URL) -> URL? {
switch helpType {
case .cookie:
return baseUrl.appendingPathComponent("cookies")
case .contact:
return nil
case .helpCenter:
return baseUrl.appendingPathComponent("help")
case .howItWorks:
return baseUrl.appendingPathComponent("about")
case .privacy:
return baseUrl.appendingPathComponent("privacy")
case .terms:
return baseUrl.appendingPathComponent("terms-of-use")
case .trust:
return baseUrl.appendingPathComponent("trust")
}
}
12 changes: 12 additions & 0 deletions Kickstarter-iOS/Views/Cells/PledgeAmountCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ import Prelude
import Prelude_UIKit
import UIKit

protocol PledgeAmountCellDelegate: AnyObject {
func pledgeAmountCell(_ cell: PledgeAmountCell, didUpdateAmount amount: Double)
}

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

public weak var delegate: PledgeAmountCellDelegate?
private let viewModel = PledgeAmountCellViewModel()

private lazy var adaptableStackView: UIStackView = { UIStackView(frame: .zero) }()
Expand Down Expand Up @@ -111,6 +116,13 @@ final class PledgeAmountCell: UITableViewCell, ValueCell {
self.viewModel.outputs.generateNotificationWarningFeedback
.observeForUI()
.observeValues { generateNotificationWarningFeedback() }

self.viewModel.outputs.amountPrimitive
.observeForUI()
.observeValues { [weak self] amount in
guard let self = self else { return }
self.delegate?.pledgeAmountCell(self, didUpdateAmount: amount)
}
}

// MARK: - Configuration
Expand Down
39 changes: 9 additions & 30 deletions Kickstarter-iOS/Views/Cells/PledgeDescriptionCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ private let rootStackViewStyle: StackViewStyle = { (stackView: UIStackView) in
|> \.axis .~ NSLayoutConstraint.Axis.horizontal
|> \.translatesAutoresizingMaskIntoConstraints .~ false
|> \.isLayoutMarginsRelativeArrangement .~ true
|> \.layoutMargins .~ UIEdgeInsets.init(topBottom: Styles.grid(5), leftRight: Styles.grid(4))
|> \.layoutMargins .~ UIEdgeInsets.init(topBottom: Styles.grid(5), leftRight: Styles.grid(2))
|> \.spacing .~ Styles.grid(3)
}

Expand Down Expand Up @@ -203,18 +203,9 @@ private let dateLabelStyle: LabelStyle = { (label: UILabel) in

private let learnMoreTextViewStyle: TextViewStyle = { (textView: UITextView) -> UITextView in
_ = textView
|> tappableLinksViewStyle
|> \.attributedText .~ attributedLearnMoreText()
|> \.isScrollEnabled .~ false
|> \.isEditable .~ false
|> \.isUserInteractionEnabled .~ true
|> \.adjustsFontForContentSizeCategory .~ true

_ = textView
|> \.textContainerInset .~ UIEdgeInsets.zero
|> \.textContainer.lineFragmentPadding .~ 0
|> \.linkTextAttributes .~ [
.foregroundColor: UIColor.ksr_green_500
]
|> \.accessibilityTraits .~ [.staticText]

return textView
}
Expand All @@ -223,25 +214,13 @@ private func attributedLearnMoreText() -> NSAttributedString? {
// swiftlint:disable line_length
let string = localizedString(
key: "Kickstarter_is_not_a_store_Its_a_way_to_bring_creative_projects_to_life_Learn_more_about_accountability",
defaultValue: "<p>Kickstarter is not a store. It's a way to bring creative projects to life.</br><a href=\"https://www.kickstarter.com/trust\">Learn more about accountability</a><p>"
defaultValue: "<p>Kickstarter is not a store. It's a way to bring creative projects to life.</br><a href=\"%{trust_link}\">Learn more about accountability</a><p>",
substitutions: [
"trust_link": HelpType.trust.url(withBaseUrl: AppEnvironment.current.apiService.serverConfig.webBaseUrl)?.absoluteString
]
.compactMapValues { $0.coalesceWith("") }
)
// swiftlint:enable line_length

guard let attributedString = try? NSMutableAttributedString(
data: Data(bytes: string.utf8),
options: [.documentType: NSAttributedString.DocumentType.html],
documentAttributes: nil
) else { return nil }

let attributes: String.Attributes = [
.font: UIFont.ksr_caption1(),
.foregroundColor: UIColor.ksr_text_dark_grey_500,
.underlineStyle: 0
]

let fullRange = (attributedString.string as NSString).range(of: attributedString.string)

attributedString.addAttributes(attributes, range: fullRange)

return attributedString
return checkoutAttributedLink(with: string)
}
8 changes: 0 additions & 8 deletions Kickstarter-iOS/Views/Cells/PledgeRowCell.swift

This file was deleted.

12 changes: 12 additions & 0 deletions Kickstarter-iOS/Views/Cells/PledgeShippingLocationCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ import Library
import Prelude
import UIKit

protocol PledgeShippingLocationCellDelegate: AnyObject {
func pledgeShippingCell(_ cell: PledgeShippingLocationCell, didSelectShippingRule rule: ShippingRule)
}

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

public weak var delegate: PledgeShippingLocationCellDelegate?
private let viewModel = PledgeShippingLocationCellViewModel()

private lazy var adaptableStackView: UIStackView = { UIStackView(frame: .zero) }()
Expand Down Expand Up @@ -96,6 +101,13 @@ final class PledgeShippingLocationCell: UITableViewCell, ValueCell {
.observeValues { [weak self] isLoading in
self?.animate(isLoading)
}

self.viewModel.outputs.selectedShippingRule
.observeForUI()
.observeValues { [weak self] rule in
guard let self = self else { return }
self.delegate?.pledgeShippingCell(self, didSelectShippingRule: rule)
}
}

// MARK: - Configuration
Expand Down
Loading

0 comments on commit 0221e6e

Please sign in to comment.