Skip to content

Commit 20db0f7

Browse files
committed
Merge pull request #4772 from wordpress-mobile/feature/plans-list
Plans: plans list
2 parents 685b273 + 26b615c commit 20db0f7

File tree

12 files changed

+315
-26
lines changed

12 files changed

+315
-26
lines changed

WordPress/Classes/Models/Plan.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,17 @@ extension Plan {
106106
return UIImage(named: activeImageName)!
107107
}
108108
}
109+
110+
// Blog extension
111+
extension Blog {
112+
/// The blog's active plan, if any.
113+
/// - note: If the stored planID doesn't match a known plan, it returns `nil`
114+
var plan: Plan? {
115+
// FIXME: Remove cast if/when we merge https://github.com/wordpress-mobile/WordPress-iOS/pull/4762
116+
// @koke 2016-02-03
117+
guard let planID = planID as Int? else {
118+
return nil
119+
}
120+
return Plan(rawValue: planID)
121+
}
122+
}

WordPress/Classes/ViewRelated/Blog/BlogDetailsViewController.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,7 @@ - (void)showPeople
500500
- (void)showPlans
501501
{
502502
// TODO(@koke, 2016-01-28): add analytics
503-
PlanListViewController *controller = [PlanListViewController new];
503+
PlanListViewController *controller = [[PlanListViewController alloc] initWithBlog:self.blog];
504504
[self.navigationController pushViewController:controller animated:YES];
505505
}
506506

WordPress/Classes/ViewRelated/Cells/WPImmuTableCells.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ class WPReusableTableViewCell: WPTableViewCell {
88

99
textLabel?.text = nil
1010
textLabel?.textAlignment = .Left
11+
textLabel?.adjustsFontSizeToFitWidth = false
1112
detailTextLabel?.text = nil
13+
detailTextLabel?.textColor = UIColor.blackColor()
1214
imageView?.image = nil
1315
accessoryType = .None
1416
selectionStyle = .Default
Lines changed: 177 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,200 @@
11
import UIKit
22
import WordPressShared
33

4-
class PlanListViewController: UITableViewController {
5-
let cellIdentifier = "PlanListItem"
6-
let availablePlans = [
7-
Plan.Free,
8-
Plan.Premium,
9-
Plan.Business
10-
]
11-
let activePlan = Plan.Free
12-
13-
init() {
4+
struct PlanListRow: ImmuTableRow {
5+
static let cell = ImmuTableCell.Class(WPTableViewCellSubtitle)
6+
static let customHeight: Float? = 92
7+
8+
let title: String
9+
let active: Bool
10+
let price: String
11+
let description: String
12+
let icon: UIImage
13+
14+
let action: ImmuTableAction? = nil
15+
16+
func configureCell(cell: UITableViewCell) {
17+
WPStyleGuide.configureTableViewSmallSubtitleCell(cell)
18+
cell.imageView?.image = icon
19+
cell.textLabel?.attributedText = attributedTitle
20+
cell.textLabel?.adjustsFontSizeToFitWidth = true
21+
cell.detailTextLabel?.text = description
22+
cell.detailTextLabel?.textColor = WPStyleGuide.grey()
23+
cell.selectionStyle = .None
24+
cell.separatorInset = UIEdgeInsetsZero
25+
}
26+
27+
private var attributedTitle: NSAttributedString {
28+
return Formatter.attributedTitle(title, price: price, active: active)
29+
}
30+
31+
struct Formatter {
32+
static let titleAttributes = [
33+
NSFontAttributeName: WPStyleGuide.tableviewTextFont(),
34+
NSForegroundColorAttributeName: WPStyleGuide.tableViewActionColor()
35+
]
36+
static let priceAttributes = [
37+
NSFontAttributeName: WPFontManager.openSansRegularFontOfSize(14.0),
38+
NSForegroundColorAttributeName: WPStyleGuide.darkGrey()
39+
]
40+
static let pricePeriodAttributes = [
41+
NSFontAttributeName: WPFontManager.openSansItalicFontOfSize(13.0),
42+
NSForegroundColorAttributeName: WPStyleGuide.greyLighten20()
43+
]
44+
45+
static func attributedTitle(title: String, price: String, active: Bool) -> NSAttributedString {
46+
let planTitle = NSAttributedString(string: title, attributes: titleAttributes)
47+
48+
let attributedTitle = NSMutableAttributedString(attributedString: planTitle)
49+
50+
if active {
51+
let currentPlanAttributes = [
52+
NSFontAttributeName: WPFontManager.openSansSemiBoldFontOfSize(11.0),
53+
NSForegroundColorAttributeName: WPStyleGuide.validGreen()
54+
]
55+
let currentPlan = NSLocalizedString("Current Plan", comment: "").uppercaseStringWithLocale(NSLocale.currentLocale())
56+
let attributedCurrentPlan = NSAttributedString(string: currentPlan, attributes: currentPlanAttributes)
57+
attributedTitle.appendString(" ")
58+
attributedTitle.appendAttributedString(attributedCurrentPlan)
59+
} else if !price.isEmpty {
60+
attributedTitle.appendString(" ")
61+
let attributedPrice = NSAttributedString(string: price, attributes: priceAttributes)
62+
attributedTitle.appendAttributedString(attributedPrice)
63+
64+
attributedTitle.appendString(" ")
65+
let pricePeriod = NSAttributedString(string: NSLocalizedString("per year", comment: ""), attributes: pricePeriodAttributes)
66+
attributedTitle.appendAttributedString(pricePeriod)
67+
}
68+
return attributedTitle
69+
}
70+
}
71+
}
72+
73+
struct PlanListViewModel {
74+
let activePlan: Plan?
75+
76+
var tableViewModel: ImmuTable {
77+
return ImmuTable(sections: [
78+
ImmuTableSection(
79+
headerText: NSLocalizedString("WordPress.com Plans", comment: "Title for the Plans list header"),
80+
rows: [
81+
rowForPlan(.Free),
82+
rowForPlan(.Premium),
83+
rowForPlan(.Business)
84+
])
85+
])
86+
}
87+
88+
private func rowForPlan(plan: Plan) -> PlanListRow {
89+
let active = (activePlan == plan)
90+
let icon = active ? plan.activeImage : plan.image
91+
92+
return PlanListRow(
93+
title: plan.title,
94+
active: active,
95+
price: priceForPlan(plan),
96+
description: plan.description,
97+
icon: icon
98+
)
99+
}
100+
101+
// TODO: Prices should always come from StoreKit
102+
// @koke 2016-02-02
103+
private func priceForPlan(plan: Plan) -> String {
104+
switch plan {
105+
case .Free:
106+
return ""
107+
case .Premium:
108+
return "$99.99"
109+
case .Business:
110+
return "$299.99"
111+
}
112+
}
113+
}
114+
115+
final class PlanListViewController: UITableViewController {
116+
private lazy var handler: ImmuTableViewHandler = {
117+
return ImmuTableViewHandler(takeOver: self)
118+
}()
119+
private let viewModel: PlanListViewModel
120+
121+
static let restorationIdentifier = "PlanList"
122+
123+
convenience init(blog: Blog) {
124+
self.init(activePlan: blog.plan)
125+
}
126+
127+
convenience init(activePlan: Plan?) {
128+
let viewModel = PlanListViewModel(activePlan: activePlan)
129+
self.init(viewModel: viewModel)
130+
}
131+
132+
private init(viewModel: PlanListViewModel) {
133+
self.viewModel = viewModel
14134
super.init(style: .Grouped)
15135
title = NSLocalizedString("Plans", comment: "Title for the plan selector")
136+
restorationIdentifier = PlanListViewController.restorationIdentifier
137+
restorationClass = PlanListViewController.self
16138
}
17-
139+
18140
required init?(coder aDecoder: NSCoder) {
19141
fatalError("init(coder:) has not been implemented")
20142
}
21143

22144
override func viewDidLoad() {
23145
super.viewDidLoad()
24-
tableView.registerClass(WPTableViewCellSubtitle.self, forCellReuseIdentifier: cellIdentifier)
146+
WPStyleGuide.resetReadableMarginsForTableView(tableView)
25147
WPStyleGuide.configureColorsForView(view, andTableView: tableView)
148+
ImmuTable.registerRows([PlanListRow.self], tableView: tableView)
149+
handler.viewModel = viewModel.tableViewModel
26150
}
151+
}
152+
153+
extension PlanListViewModel: Equatable {}
154+
func ==(lhs: PlanListViewModel, rhs: PlanListViewModel) -> Bool {
155+
return lhs.activePlan == rhs.activePlan
156+
}
27157

28-
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
29-
return availablePlans.count
158+
/*
159+
Since PlanListViewModel is a struct, it can't conform to NSCoding.
160+
We're just using the same naming for convenience.
161+
*/
162+
extension PlanListViewModel/*: NSCoding */ {
163+
struct EncodingKey {
164+
static let activePlan = "activePlan"
165+
166+
}
167+
func encodeWithCoder(aCoder: NSCoder) {
168+
if let plan = activePlan {
169+
aCoder.encodeInteger(plan.rawValue, forKey: EncodingKey.activePlan)
170+
}
30171
}
31172

32-
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
33-
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath)
34-
let plan = availablePlans[indexPath.row]
173+
init(coder aDecoder: NSCoder) {
174+
let planID: Int? = {
175+
guard aDecoder.containsValueForKey(EncodingKey.activePlan) else {
176+
return nil
177+
}
178+
return aDecoder.decodeIntegerForKey(EncodingKey.activePlan)
179+
}()
35180

36-
if plan == activePlan {
37-
cell.imageView?.image = plan.activeImage
38-
} else {
39-
cell.imageView?.image = plan.image
181+
let plan = planID.flatMap({ Plan(rawValue: $0) })
182+
self.init(activePlan: plan)
183+
}
184+
}
185+
186+
extension PlanListViewController: UIViewControllerRestoration {
187+
static func viewControllerWithRestorationIdentifierPath(identifierComponents: [AnyObject], coder: NSCoder) -> UIViewController? {
188+
guard let identifier = identifierComponents.last as? String where identifier == PlanListViewController.restorationIdentifier else {
189+
return nil
40190
}
41-
cell.textLabel?.text = plan.title
42-
cell.detailTextLabel?.text = plan.description
43191

44-
cell.selectionStyle = .None
192+
let viewModel = PlanListViewModel(coder: coder)
193+
return PlanListViewController(viewModel: viewModel)
194+
}
45195

46-
return cell
196+
override func encodeRestorableStateWithCoder(coder: NSCoder) {
197+
super.encodeRestorableStateWithCoder(coder)
198+
viewModel.encodeWithCoder(coder)
47199
}
48200
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)