Skip to content
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: 14 additions & 0 deletions WordPress/Classes/Models/Plan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,17 @@ extension Plan {
return UIImage(named: activeImageName)!
}
}

// Blog extension
extension Blog {
/// The blog's active plan, if any.
/// - note: If the stored planID doesn't match a known plan, it returns `nil`
var plan: Plan? {
// FIXME: Remove cast if/when we merge https://github.com/wordpress-mobile/WordPress-iOS/pull/4762
// @koke 2016-02-03
guard let planID = planID as Int? else {
return nil
}
return Plan(rawValue: planID)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ - (void)showPeople
- (void)showPlans
{
// TODO(@koke, 2016-01-28): add analytics
PlanListViewController *controller = [PlanListViewController new];
PlanListViewController *controller = [[PlanListViewController alloc] initWithBlog:self.blog];
[self.navigationController pushViewController:controller animated:YES];
}

Expand Down
2 changes: 2 additions & 0 deletions WordPress/Classes/ViewRelated/Cells/WPImmuTableCells.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ class WPReusableTableViewCell: WPTableViewCell {

textLabel?.text = nil
textLabel?.textAlignment = .Left
textLabel?.adjustsFontSizeToFitWidth = false
detailTextLabel?.text = nil
detailTextLabel?.textColor = UIColor.blackColor()
imageView?.image = nil
accessoryType = .None
selectionStyle = .Default
Expand Down
202 changes: 177 additions & 25 deletions WordPress/Classes/ViewRelated/Plans/PlanListViewController.swift
Original file line number Diff line number Diff line change
@@ -1,48 +1,200 @@
import UIKit
import WordPressShared

class PlanListViewController: UITableViewController {
let cellIdentifier = "PlanListItem"
let availablePlans = [
Plan.Free,
Plan.Premium,
Plan.Business
]
let activePlan = Plan.Free

init() {
struct PlanListRow: ImmuTableRow {
static let cell = ImmuTableCell.Class(WPTableViewCellSubtitle)
static let customHeight: Float? = 92

let title: String
let active: Bool
let price: String
let description: String
let icon: UIImage

let action: ImmuTableAction? = nil

func configureCell(cell: UITableViewCell) {
WPStyleGuide.configureTableViewSmallSubtitleCell(cell)
cell.imageView?.image = icon
cell.textLabel?.attributedText = attributedTitle
cell.textLabel?.adjustsFontSizeToFitWidth = true
cell.detailTextLabel?.text = description
cell.detailTextLabel?.textColor = WPStyleGuide.grey()
cell.selectionStyle = .None
cell.separatorInset = UIEdgeInsetsZero
}

private var attributedTitle: NSAttributedString {
return Formatter.attributedTitle(title, price: price, active: active)
}

struct Formatter {
static let titleAttributes = [
NSFontAttributeName: WPStyleGuide.tableviewTextFont(),
NSForegroundColorAttributeName: WPStyleGuide.tableViewActionColor()
]
static let priceAttributes = [
NSFontAttributeName: WPFontManager.openSansRegularFontOfSize(14.0),
NSForegroundColorAttributeName: WPStyleGuide.darkGrey()
]
static let pricePeriodAttributes = [
NSFontAttributeName: WPFontManager.openSansItalicFontOfSize(13.0),
NSForegroundColorAttributeName: WPStyleGuide.greyLighten20()
]

static func attributedTitle(title: String, price: String, active: Bool) -> NSAttributedString {
let planTitle = NSAttributedString(string: title, attributes: titleAttributes)

let attributedTitle = NSMutableAttributedString(attributedString: planTitle)

if active {
let currentPlanAttributes = [
NSFontAttributeName: WPFontManager.openSansSemiBoldFontOfSize(11.0),
NSForegroundColorAttributeName: WPStyleGuide.validGreen()
]
let currentPlan = NSLocalizedString("Current Plan", comment: "").uppercaseStringWithLocale(NSLocale.currentLocale())
let attributedCurrentPlan = NSAttributedString(string: currentPlan, attributes: currentPlanAttributes)
attributedTitle.appendString(" ")
attributedTitle.appendAttributedString(attributedCurrentPlan)
} else if !price.isEmpty {
attributedTitle.appendString(" ")
let attributedPrice = NSAttributedString(string: price, attributes: priceAttributes)
attributedTitle.appendAttributedString(attributedPrice)

attributedTitle.appendString(" ")
let pricePeriod = NSAttributedString(string: NSLocalizedString("per year", comment: ""), attributes: pricePeriodAttributes)
attributedTitle.appendAttributedString(pricePeriod)
}
return attributedTitle
}
}
}

struct PlanListViewModel {
let activePlan: Plan?

var tableViewModel: ImmuTable {
return ImmuTable(sections: [
ImmuTableSection(
headerText: NSLocalizedString("WordPress.com Plans", comment: "Title for the Plans list header"),
rows: [
rowForPlan(.Free),
rowForPlan(.Premium),
rowForPlan(.Business)
])
])
}

private func rowForPlan(plan: Plan) -> PlanListRow {
let active = (activePlan == plan)
let icon = active ? plan.activeImage : plan.image

return PlanListRow(
title: plan.title,
active: active,
price: priceForPlan(plan),
description: plan.description,
icon: icon
)
}

// TODO: Prices should always come from StoreKit
// @koke 2016-02-02
private func priceForPlan(plan: Plan) -> String {
switch plan {
case .Free:
return ""
case .Premium:
return "$99.99"
case .Business:
return "$299.99"
}
}
}

final class PlanListViewController: UITableViewController {
private lazy var handler: ImmuTableViewHandler = {
return ImmuTableViewHandler(takeOver: self)
}()
private let viewModel: PlanListViewModel

static let restorationIdentifier = "PlanList"

convenience init(blog: Blog) {
self.init(activePlan: blog.plan)
}

convenience init(activePlan: Plan?) {
let viewModel = PlanListViewModel(activePlan: activePlan)
self.init(viewModel: viewModel)
}

private init(viewModel: PlanListViewModel) {
self.viewModel = viewModel
super.init(style: .Grouped)
title = NSLocalizedString("Plans", comment: "Title for the plan selector")
restorationIdentifier = PlanListViewController.restorationIdentifier
restorationClass = PlanListViewController.self
}

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

override func viewDidLoad() {
super.viewDidLoad()
tableView.registerClass(WPTableViewCellSubtitle.self, forCellReuseIdentifier: cellIdentifier)
WPStyleGuide.resetReadableMarginsForTableView(tableView)
WPStyleGuide.configureColorsForView(view, andTableView: tableView)
ImmuTable.registerRows([PlanListRow.self], tableView: tableView)
handler.viewModel = viewModel.tableViewModel
}
}

extension PlanListViewModel: Equatable {}
func ==(lhs: PlanListViewModel, rhs: PlanListViewModel) -> Bool {
return lhs.activePlan == rhs.activePlan
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return availablePlans.count
/*
Since PlanListViewModel is a struct, it can't conform to NSCoding.
We're just using the same naming for convenience.
*/
extension PlanListViewModel/*: NSCoding */ {
struct EncodingKey {
static let activePlan = "activePlan"

}
func encodeWithCoder(aCoder: NSCoder) {
if let plan = activePlan {
aCoder.encodeInteger(plan.rawValue, forKey: EncodingKey.activePlan)
}
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath)
let plan = availablePlans[indexPath.row]
init(coder aDecoder: NSCoder) {
let planID: Int? = {
guard aDecoder.containsValueForKey(EncodingKey.activePlan) else {
return nil
}
return aDecoder.decodeIntegerForKey(EncodingKey.activePlan)
}()

if plan == activePlan {
cell.imageView?.image = plan.activeImage
} else {
cell.imageView?.image = plan.image
let plan = planID.flatMap({ Plan(rawValue: $0) })
self.init(activePlan: plan)
}
}

extension PlanListViewController: UIViewControllerRestoration {
static func viewControllerWithRestorationIdentifierPath(identifierComponents: [AnyObject], coder: NSCoder) -> UIViewController? {
guard let identifier = identifierComponents.last as? String where identifier == PlanListViewController.restorationIdentifier else {
return nil
}
cell.textLabel?.text = plan.title
cell.detailTextLabel?.text = plan.description

cell.selectionStyle = .None
let viewModel = PlanListViewModel(coder: coder)
return PlanListViewController(viewModel: viewModel)
}

return cell
override func encodeRestorableStateWithCoder(coder: NSCoder) {
super.encodeRestorableStateWithCoder(coder)
viewModel.encodeWithCoder(coder)
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
4 changes: 4 additions & 0 deletions WordPress/WordPress.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@
E1AC282D18282423004D394C /* SFHFKeychainUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 292CECFF1027259000BD407D /* SFHFKeychainUtils.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
E1B23B081BFB3B370006559B /* MyProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1B23B071BFB3B370006559B /* MyProfileViewController.swift */; };
E1B289DB19F7AF7000DB0707 /* RemoteBlog.m in Sources */ = {isa = PBXBuildFile; fileRef = E1B289DA19F7AF7000DB0707 /* RemoteBlog.m */; };
E1B2FF591C637AE200F6BDEA /* PlanListViewControllerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1B2FF581C637AE200F6BDEA /* PlanListViewControllerTest.swift */; };
E1B62A7B13AA61A100A6FCA4 /* WPWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E1B62A7A13AA61A100A6FCA4 /* WPWebViewController.m */; };
E1B912811BB00EFD003C25B9 /* People.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E1B912801BB00EFD003C25B9 /* People.storyboard */; };
E1B912831BB01047003C25B9 /* PeopleRoleBadgeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1B912821BB01047003C25B9 /* PeopleRoleBadgeView.swift */; };
Expand Down Expand Up @@ -1624,6 +1625,7 @@
E1B23B071BFB3B370006559B /* MyProfileViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MyProfileViewController.swift; path = Me/MyProfileViewController.swift; sourceTree = "<group>"; };
E1B289D919F7AF7000DB0707 /* RemoteBlog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RemoteBlog.h; path = "Remote Objects/RemoteBlog.h"; sourceTree = "<group>"; };
E1B289DA19F7AF7000DB0707 /* RemoteBlog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RemoteBlog.m; path = "Remote Objects/RemoteBlog.m"; sourceTree = "<group>"; };
E1B2FF581C637AE200F6BDEA /* PlanListViewControllerTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlanListViewControllerTest.swift; sourceTree = "<group>"; };
E1B62A7913AA61A100A6FCA4 /* WPWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WPWebViewController.h; sourceTree = "<group>"; };
E1B62A7A13AA61A100A6FCA4 /* WPWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WPWebViewController.m; sourceTree = "<group>"; };
E1B912801BB00EFD003C25B9 /* People.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = People.storyboard; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3232,6 +3234,7 @@
isa = PBXGroup;
children = (
E10675C7183F82E900E5CE5C /* SettingsViewControllerTest.m */,
E1B2FF581C637AE200F6BDEA /* PlanListViewControllerTest.swift */,
);
name = ViewControllers;
sourceTree = "<group>";
Expand Down Expand Up @@ -5006,6 +5009,7 @@
5D2BEB4919758102005425F7 /* PhotonImageURLHelperTest.m in Sources */,
5948AD111AB73D19006E8882 /* WPAppAnalyticsTests.m in Sources */,
85D239C01AE5A7020074768D /* LoginFacadeTests.m in Sources */,
E1B2FF591C637AE200F6BDEA /* PlanListViewControllerTest.swift in Sources */,
5DA988061AEEA594002AFB12 /* DisplayableImageHelperTest.m in Sources */,
B566EC751B83867800278395 /* NSMutableAttributedStringTest.swift in Sources */,
E6B9B8AF1B94FA1C0001B92F /* ReaderStreamViewControllerTests.swift in Sources */,
Expand Down
Loading