From ca0ee45580c385bf3c727eb8d6cf4ecf5e138463 Mon Sep 17 00:00:00 2001 From: Matt Bumgardner Date: Tue, 11 Sep 2018 12:34:09 -0500 Subject: [PATCH 01/22] WIP: Dashboard top performers UI --- .../Dashboard/Dashboard.storyboard | 116 ++++++++++++++++- .../Dashboard/DashboardViewController.swift | 31 +++-- .../TopPerformerDataViewController.swift | 87 +++++++++++++ .../TopPerformerDataViewController.xib | 60 +++++++++ .../MyStore/TopPerformersViewController.swift | 123 ++++++++++++++++++ .../WooCommerce.xcodeproj/project.pbxproj | 12 ++ 6 files changed, 413 insertions(+), 16 deletions(-) create mode 100644 WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift create mode 100644 WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.xib create mode 100644 WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersViewController.swift diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard b/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard index d83042c0123..7f8115ab7bc 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard @@ -90,14 +90,14 @@ - + - + - + @@ -130,6 +130,21 @@ + + + + + + + + + + + + + + + @@ -154,6 +169,7 @@ + @@ -215,7 +231,7 @@ - + @@ -325,7 +341,97 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift index 8d4425e3060..cdc196f1cfb 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift @@ -15,6 +15,7 @@ class DashboardViewController: UIViewController { private var storeStatsViewController: StoreStatsViewController! private var newOrdersViewController: NewOrdersViewController! + private var topPerformersViewController: TopPerformersViewController! private lazy var refreshControl: UIRefreshControl = { let refreshControl = UIRefreshControl() @@ -37,13 +38,16 @@ class DashboardViewController: UIViewController { } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - if let vc = segue.destination as? StoreStatsViewController, segue.identifier == Constants.storeStatsSegue { + if let vc = segue.destination as? StoreStatsViewController, segue.identifier == Segues.storeStatsSegue { storeStatsViewController = vc } - if let vc = segue.destination as? NewOrdersViewController, segue.identifier == Constants.newOrdersSegue { + if let vc = segue.destination as? NewOrdersViewController, segue.identifier == Segues.newOrdersSegue { newOrdersViewController = vc newOrdersViewController.delegate = self } + if let vc = segue.destination as? TopPerformersViewController, segue.identifier == Segues.topPerformersSegue { + topPerformersViewController = vc + } } } @@ -86,7 +90,7 @@ private extension DashboardViewController { private extension DashboardViewController { @objc func settingsTapped() { - performSegue(withIdentifier: Constants.settingsSegue, sender: nil) + performSegue(withIdentifier: Segues.settingsSegue, sender: nil) } @objc func pullToRefresh() { @@ -116,6 +120,7 @@ private extension DashboardViewController { DDLogInfo("♻️ Requesting dashboard data be reloaded...") storeStatsViewController.syncAllStats() newOrdersViewController.syncNewOrders() + topPerformersViewController.syncTopPerformers() refreshControl.endRefreshing() } @@ -149,14 +154,18 @@ private extension DashboardViewController { // MARK: - Constants // private extension DashboardViewController { + + struct Segues { + static let settingsSegue = "ShowSettingsViewController" + static let storeStatsSegue = "StoreStatsEmbedSegue" + static let newOrdersSegue = "NewOrdersEmbedSegue" + static let topPerformersSegue = "TopPerformersEmbedSegue" + } + struct Constants { - static let settingsSegue = "ShowSettingsViewController" - static let storeStatsSegue = "StoreStatsEmbedSegue" - static let newOrdersSegue = "NewOrdersEmbedSegue" - - static let hideAnimationDuration: TimeInterval = 0.25 - static let showAnimationDuration: TimeInterval = 0.50 - static let showSpringDamping: CGFloat = 0.7 - static let showSpringVelocity: CGFloat = 0.0 + static let hideAnimationDuration: TimeInterval = 0.25 + static let showAnimationDuration: TimeInterval = 0.50 + static let showSpringDamping: CGFloat = 0.7 + static let showSpringVelocity: CGFloat = 0.0 } } diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift new file mode 100644 index 00000000000..9b79ef5ea37 --- /dev/null +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift @@ -0,0 +1,87 @@ +import UIKit +import Yosemite +import Charts +import XLPagerTabStrip +import CocoaLumberjack + + +class TopPerformerDataViewController: UIViewController, IndicatorInfoProvider { + + // MARK: - Properties + + public let granularity: StatGranularity + + @IBOutlet private weak var tableView: UITableView! + @IBOutlet private weak var descriptionLabel: PaddedLabel! + @IBOutlet private weak var borderView: UIView! + + // MARK: - Initialization + + /// Designated Initializer + /// + init(granularity: StatGranularity) { + self.granularity = granularity + super.init(nibName: type(of: self).nibName, bundle: nil) + } + + /// NSCoder Conformance + /// + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) is not supported") + } + + // MARK: - View Lifecycle + + override func viewDidLoad() { + super.viewDidLoad() + configureView() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + reloadAllFields() + } +} + + +// MARK: - User Interface Configuration +// +private extension TopPerformerDataViewController { + + func configureView() { + view.backgroundColor = StyleManager.wooWhite + borderView.backgroundColor = StyleManager.wooGreyBorder + descriptionLabel.applyBodyStyle() + descriptionLabel.textInsets = Constants.descriptionLabelInsets + descriptionLabel.text = NSLocalizedString("Gain insights into how products are performing on your store", comment: "Description for Top Performers section of My Store tab.") + } +} + + +// MARK: - IndicatorInfoProvider Conformance (Tab Bar) +// +extension TopPerformerDataViewController { + func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo { + return IndicatorInfo(title: granularity.pluralizedString) + } +} + + +// MARK: - Private Helpers +// +private extension TopPerformerDataViewController { + + func reloadAllFields() { + // TODO: fill this in! + } +} + + +// MARK: - Constants! +// +private extension TopPerformerDataViewController { + enum Constants { + static let descriptionLabelInsets = UIEdgeInsets(top: 14, left: 14, bottom: 14, right: 14) + } +} + diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.xib b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.xib new file mode 100644 index 00000000000..961286281c9 --- /dev/null +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.xib @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersViewController.swift new file mode 100644 index 00000000000..c035f59f0cc --- /dev/null +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersViewController.swift @@ -0,0 +1,123 @@ +import UIKit +import Yosemite +import CocoaLumberjack +import XLPagerTabStrip + + +class TopPerformersViewController: ButtonBarPagerTabStripViewController { + + // MARK: - Properties + + @IBOutlet private weak var topBorder: UIView! + @IBOutlet private weak var middleBorder: UIView! + @IBOutlet private weak var bottomBorder: UIView! + + private var dataVCs = [TopPerformerDataViewController]() + + // MARK: - View Lifecycle + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + override func viewDidLoad() { + configureDataViewControllers() + configureTabStrip() + // 👆 must be called before super.viewDidLoad() + + super.viewDidLoad() + configureView() + } + + // MARK: - PagerTabStripDataSource + + override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] { + return dataVCs + } +} + + +// MARK: - Public Interface +// +extension TopPerformersViewController { + + func syncTopPerformers() { + dataVCs.forEach { (vc) in + syncTopPerformers(for: vc.granularity) + } + } +} + + +// MARK: - User Interface Configuration +// +private extension TopPerformersViewController { + + func configureView() { + view.backgroundColor = StyleManager.tableViewBackgroundColor + topBorder.backgroundColor = StyleManager.wooGreyBorder + middleBorder.backgroundColor = StyleManager.wooGreyBorder + bottomBorder.backgroundColor = StyleManager.wooGreyBorder + } + + func configureDataViewControllers() { + let dayVC = TopPerformerDataViewController(granularity: .day) + let weekVC = TopPerformerDataViewController(granularity: .week) + let monthVC = TopPerformerDataViewController(granularity: .month) + let yearVC = TopPerformerDataViewController(granularity: .year) + + dataVCs.append(dayVC) + dataVCs.append(weekVC) + dataVCs.append(monthVC) + dataVCs.append(yearVC) + } + + func configureTabStrip() { + settings.style.buttonBarBackgroundColor = StyleManager.wooWhite + settings.style.buttonBarItemBackgroundColor = StyleManager.wooWhite + settings.style.selectedBarBackgroundColor = StyleManager.wooCommerceBrandColor + settings.style.buttonBarItemFont = StyleManager.subheadlineFont + settings.style.selectedBarHeight = TabStrip.selectedBarHeight + settings.style.buttonBarItemTitleColor = StyleManager.defaultTextColor + settings.style.buttonBarItemsShouldFillAvailableWidth = false + settings.style.buttonBarItemLeftRightMargin = TabStrip.buttonLeftRightMargin + + changeCurrentIndexProgressive = { (oldCell: ButtonBarViewCell?, newCell: ButtonBarViewCell?, progressPercentage: CGFloat, changeCurrentIndex: Bool, animated: Bool) -> Void in + guard changeCurrentIndex == true else { return } + oldCell?.label.textColor = StyleManager.defaultTextColor + newCell?.label.textColor = StyleManager.wooCommerceBrandColor + } + } +} + + +// MARK: - Sync'ing Helpers +// +private extension TopPerformersViewController { + + func syncTopPerformers(for granularity: StatGranularity, onCompletion: ((Error?) -> ())? = nil) { + guard let siteID = StoresManager.shared.sessionManager.defaultStoreID else { + onCompletion?(nil) + return + } + + let action = StatsAction.retrieveTopEarnerStats(siteID: siteID, granularity: granularity, latestDateToInclude: Date()) { (error) in + if let error = error { + DDLogError("⛔️ Dashboard (Top Performers) — Error synchronizing top earner stats: \(error)") + } + } + + StoresManager.shared.dispatch(action) + } +} + + +// MARK: - Constants! +// +private extension TopPerformersViewController { + enum TabStrip { + static let buttonLeftRightMargin: CGFloat = 14.0 + static let selectedBarHeight: CGFloat = 3.0 + } +} + diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index c53067fdcef..8cab18807f1 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -31,6 +31,9 @@ 748C7780211E18A600814F2C /* OrderStats+Woo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 748C777F211E18A600814F2C /* OrderStats+Woo.swift */; }; 748C7782211E294000814F2C /* Double+Woo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 748C7781211E294000814F2C /* Double+Woo.swift */; }; 748C7784211E2D8400814F2C /* DoubleWooTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 748C7783211E2D8400814F2C /* DoubleWooTests.swift */; }; + 748D34DE214828DD00E21A2F /* TopPerformerDataViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 748D34DC214828DC00E21A2F /* TopPerformerDataViewController.xib */; }; + 748D34DF214828DD00E21A2F /* TopPerformersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 748D34DD214828DC00E21A2F /* TopPerformersViewController.swift */; }; + 748D34E12148291E00E21A2F /* TopPerformerDataViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 748D34E02148291E00E21A2F /* TopPerformerDataViewController.swift */; }; 74A93A40212DC60B00C13E04 /* NewOrdersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74A93A3F212DC60B00C13E04 /* NewOrdersViewController.swift */; }; 74AAF6A5212A04A900C612B0 /* ChartMarker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74AAF6A4212A04A900C612B0 /* ChartMarker.swift */; }; 74AFF2EA211B9B1B0038153A /* StoreStatsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74AFF2E9211B9B1B0038153A /* StoreStatsViewController.swift */; }; @@ -213,6 +216,9 @@ 748C777F211E18A600814F2C /* OrderStats+Woo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderStats+Woo.swift"; sourceTree = ""; }; 748C7781211E294000814F2C /* Double+Woo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Double+Woo.swift"; sourceTree = ""; }; 748C7783211E2D8400814F2C /* DoubleWooTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoubleWooTests.swift; sourceTree = ""; }; + 748D34DC214828DC00E21A2F /* TopPerformerDataViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TopPerformerDataViewController.xib; sourceTree = ""; }; + 748D34DD214828DC00E21A2F /* TopPerformersViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TopPerformersViewController.swift; sourceTree = ""; }; + 748D34E02148291E00E21A2F /* TopPerformerDataViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TopPerformerDataViewController.swift; sourceTree = ""; }; 74A93A3F212DC60B00C13E04 /* NewOrdersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewOrdersViewController.swift; sourceTree = ""; }; 74AAF6A4212A04A900C612B0 /* ChartMarker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartMarker.swift; sourceTree = ""; }; 74AFF2E9211B9B1B0038153A /* StoreStatsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreStatsViewController.swift; sourceTree = ""; }; @@ -389,6 +395,9 @@ 74036CBF211B882100E462C2 /* PeriodDataViewController.swift */, 74E0F440211C9AE600A79CCE /* PeriodDataViewController.xib */, 74AFF2E9211B9B1B0038153A /* StoreStatsViewController.swift */, + 748D34DD214828DC00E21A2F /* TopPerformersViewController.swift */, + 748D34E02148291E00E21A2F /* TopPerformerDataViewController.swift */, + 748D34DC214828DC00E21A2F /* TopPerformerDataViewController.xib */, ); path = MyStore; sourceTree = ""; @@ -939,6 +948,7 @@ CE1EC8F020B8A408009762BF /* OrderNoteTableViewCell.xib in Resources */, B55D4BFD20B5CDE700D7A50F /* replace_secrets.rb in Resources */, B5BE75DD213F1D3D00909A14 /* OverlayMessageView.xib in Resources */, + 748D34DE214828DD00E21A2F /* TopPerformerDataViewController.xib in Resources */, CEE006062077D1280079161F /* SummaryTableViewCell.xib in Resources */, CE24BCD0212DE8A6001CD12E /* HeadlineLabelTableViewCell.xib in Resources */, B559EBB020A0BF8F00836CD4 /* LICENSE in Resources */, @@ -1171,6 +1181,7 @@ CE24BCD8212F25D4001CD12E /* StorageOrder+Woo.swift in Sources */, B557DA1520979904005962F4 /* CustomerNoteTableViewCell.swift in Sources */, CE855366209BA6A700938BDC /* CustomerInfoTableViewCell.swift in Sources */, + 748D34E12148291E00E21A2F /* TopPerformerDataViewController.swift in Sources */, B5A8532220BDBFAF00FAAB4D /* CircularImageView.swift in Sources */, CE1F51252064179A00C6C810 /* UILabel+Helpers.swift in Sources */, B54FBE552111F70700390F57 /* ResultsController+UIKit.swift in Sources */, @@ -1204,6 +1215,7 @@ B582F95920FFCEAA0060934A /* UITableViewHeaderFooterView+Helpers.swift in Sources */, B5D1AFBA20BC515600DB0E8C /* UIColor+Woo.swift in Sources */, CE1CCB4B20570B1F000EE3AC /* OrderListCell.swift in Sources */, + 748D34DF214828DD00E21A2F /* TopPerformersViewController.swift in Sources */, B5AA7B3D20ED5D15004DA14F /* SessionManager.swift in Sources */, CE24BCCF212DE8A6001CD12E /* HeadlineLabelTableViewCell.swift in Sources */, B53B898D20D462A000EDB467 /* StoresManager.swift in Sources */, From 817e671d9db76e1fb671fd1859f5f98ee33c384b Mon Sep 17 00:00:00 2001 From: Matt Bumgardner Date: Tue, 11 Sep 2018 16:33:04 -0500 Subject: [PATCH 02/22] Top Perf: Added section label --- WooCommerce/Classes/AppDelegate.swift | 1 + .../Dashboard/Dashboard.storyboard | 41 +++++++++++++------ .../MyStore/TopPerformersViewController.swift | 9 ++++ 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/WooCommerce/Classes/AppDelegate.swift b/WooCommerce/Classes/AppDelegate.swift index 2cdea92b080..167b178f279 100644 --- a/WooCommerce/Classes/AppDelegate.swift +++ b/WooCommerce/Classes/AppDelegate.swift @@ -135,6 +135,7 @@ private extension AppDelegate { UINavigationBar.appearance().isTranslucent = false UINavigationBar.appearance().tintColor = .white UIApplication.shared.statusBarStyle = .lightContent + UILabel.appearance(whenContainedInInstancesOf: [UITableViewHeaderFooterView.self]).textColor = StyleManager.sectionTitleColor // Take advantage of a bug in UIAlertController // to style all UIAlertControllers with WC color diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard b/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard index 7f8115ab7bc..c13739db6ec 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard @@ -97,7 +97,7 @@ - + @@ -131,16 +131,16 @@ - + - + - + @@ -348,21 +348,38 @@ - + - + + + + + + + + + + + + + - + - + @@ -376,20 +393,20 @@ - + - + - + @@ -398,7 +415,6 @@ - @@ -411,6 +427,7 @@ + diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersViewController.swift index c035f59f0cc..d211b15c3ea 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersViewController.swift @@ -11,6 +11,7 @@ class TopPerformersViewController: ButtonBarPagerTabStripViewController { @IBOutlet private weak var topBorder: UIView! @IBOutlet private weak var middleBorder: UIView! @IBOutlet private weak var bottomBorder: UIView! + @IBOutlet private weak var headingLabel: PaddedLabel! private var dataVCs = [TopPerformerDataViewController]() @@ -58,6 +59,10 @@ private extension TopPerformersViewController { topBorder.backgroundColor = StyleManager.wooGreyBorder middleBorder.backgroundColor = StyleManager.wooGreyBorder bottomBorder.backgroundColor = StyleManager.wooGreyBorder + headingLabel.applyFootnoteStyle() + headingLabel.textColor = StyleManager.sectionTitleColor + headingLabel.textInsets = Constants.headerLabelInsets + headingLabel.text = NSLocalizedString("Top Performers", comment: "Header label for Top Performers section of My Store tab.").uppercased() } func configureDataViewControllers() { @@ -115,6 +120,10 @@ private extension TopPerformersViewController { // MARK: - Constants! // private extension TopPerformersViewController { + enum Constants { + static let headerLabelInsets = UIEdgeInsets(top: 0, left: 14, bottom: 6, right: 14) + } + enum TabStrip { static let buttonLeftRightMargin: CGFloat = 14.0 static let selectedBarHeight: CGFloat = 3.0 From 6dc3cc515f274b0f5f3e221bd37f2ade1b911291 Mon Sep 17 00:00:00 2001 From: Matt Bumgardner Date: Tue, 11 Sep 2018 16:45:15 -0500 Subject: [PATCH 03/22] Top Perf: Fixed a spacing issue --- .../Dashboard/Dashboard.storyboard | 30 +++++++++---------- .../MyStore/NewOrdersViewController.swift | 1 - 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard b/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard index c13739db6ec..b1c968a1e0f 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard @@ -115,21 +115,15 @@ - + - - + + - - - - - - @@ -238,11 +232,11 @@ - + - + @@ -284,6 +278,7 @@ + @@ -305,6 +300,12 @@ + + + + + + - @@ -325,8 +325,8 @@ + - @@ -341,7 +341,7 @@ - + @@ -434,7 +434,7 @@ - + diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/NewOrdersViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/NewOrdersViewController.swift index 38c04c4b791..9672ee31f10 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/NewOrdersViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/NewOrdersViewController.swift @@ -69,7 +69,6 @@ extension NewOrdersViewController { private extension NewOrdersViewController { func configureView() { - view.backgroundColor = StyleManager.wooWhite topBorder.backgroundColor = StyleManager.wooGreyBorder bottomBorder.backgroundColor = StyleManager.wooGreyBorder titleLabel.applyHeadlineStyle() From e7ebf1f2631b2437326a1a4bb910aa9a98de1479 Mon Sep 17 00:00:00 2001 From: Matt Bumgardner Date: Tue, 11 Sep 2018 17:00:43 -0500 Subject: [PATCH 04/22] Top Perf: Layout adjustments --- .../Dashboard/Dashboard.storyboard | 28 +++++++++---------- .../TopPerformerDataViewController.xib | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard b/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard index b1c968a1e0f..5e2d3e1f8e4 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard @@ -97,7 +97,7 @@ - + @@ -125,16 +125,16 @@ - + - + - + @@ -348,17 +348,17 @@ - + - + - + - + - + - + @@ -393,20 +393,20 @@ - + - + - + diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.xib b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.xib index 961286281c9..1d6cbd5b5c3 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.xib +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.xib @@ -25,7 +25,7 @@ - @@ -320,13 +321,13 @@ - - - + + + From 87bbb52af5eeb3bf037964fa4bf8a1d6e03e5375 Mon Sep 17 00:00:00 2001 From: Matt Bumgardner Date: Tue, 11 Sep 2018 17:29:46 -0500 Subject: [PATCH 06/22] Top Perf: Added tab titles --- .../TopPerformerDataViewController.swift | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift index 9b79ef5ea37..f11759b09d4 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift @@ -15,6 +15,21 @@ class TopPerformerDataViewController: UIViewController, IndicatorInfoProvider { @IBOutlet private weak var descriptionLabel: PaddedLabel! @IBOutlet private weak var borderView: UIView! + // MARK: - Computed Properties + + private var tabDescription: String { + switch granularity { + case .day: + return NSLocalizedString("Today", comment: "Top Performers section title - today") + case .week: + return NSLocalizedString("This Week", comment: "Top Performers section title - this week") + case .month: + return NSLocalizedString("This Month", comment: "Top Performers section title - this month") + case .year: + return NSLocalizedString("This Year", comment: "Top Performers section title - this year") + } + } + // MARK: - Initialization /// Designated Initializer @@ -62,7 +77,7 @@ private extension TopPerformerDataViewController { // extension TopPerformerDataViewController { func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo { - return IndicatorInfo(title: granularity.pluralizedString) + return IndicatorInfo(title: tabDescription) } } @@ -82,6 +97,7 @@ private extension TopPerformerDataViewController { private extension TopPerformerDataViewController { enum Constants { static let descriptionLabelInsets = UIEdgeInsets(top: 14, left: 14, bottom: 14, right: 14) + static let noActivityText = NSLocalizedString("No activity this period", comment: "Default text for Top Performers section when no data exists for a given period.") } } From df1a40c250d18e4622c5d8c21dd2fbb3141a9cf6 Mon Sep 17 00:00:00 2001 From: Matt Bumgardner Date: Wed, 12 Sep 2018 12:13:16 -0500 Subject: [PATCH 07/22] Removed superfluous VC from dash storyboard --- .../ViewRelated/Dashboard/Dashboard.storyboard | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard b/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard index 939ec2b9529..106743d6852 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard @@ -437,19 +437,5 @@ - - - - - - - - - - - - - - From 02da5b208995f7e5e59119225d1114733d66d99b Mon Sep 17 00:00:00 2001 From: Matt Bumgardner Date: Wed, 12 Sep 2018 15:42:46 -0500 Subject: [PATCH 08/22] Top Perf section headers --- .../TopPerformerDataViewController.swift | 100 ++++++++++++++---- .../TopPerformerDataViewController.xib | 41 +++---- .../MyStore/TopPerformersHeaderView.swift | 80 ++++++++++++++ .../MyStore/TopPerformersHeaderView.xib | 66 ++++++++++++ .../WooCommerce.xcodeproj/project.pbxproj | 8 ++ 5 files changed, 247 insertions(+), 48 deletions(-) create mode 100644 WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersHeaderView.swift create mode 100644 WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersHeaderView.xib diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift index f11759b09d4..b62113d2d14 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift @@ -12,8 +12,6 @@ class TopPerformerDataViewController: UIViewController, IndicatorInfoProvider { public let granularity: StatGranularity @IBOutlet private weak var tableView: UITableView! - @IBOutlet private weak var descriptionLabel: PaddedLabel! - @IBOutlet private weak var borderView: UIView! // MARK: - Computed Properties @@ -50,11 +48,9 @@ class TopPerformerDataViewController: UIViewController, IndicatorInfoProvider { override func viewDidLoad() { super.viewDidLoad() configureView() - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - reloadAllFields() + configureTableView() + registerTableViewCells() + registerTableViewHeaderFooters() } } @@ -64,11 +60,31 @@ class TopPerformerDataViewController: UIViewController, IndicatorInfoProvider { private extension TopPerformerDataViewController { func configureView() { - view.backgroundColor = StyleManager.wooWhite - borderView.backgroundColor = StyleManager.wooGreyBorder - descriptionLabel.applyBodyStyle() - descriptionLabel.textInsets = Constants.descriptionLabelInsets - descriptionLabel.text = NSLocalizedString("Gain insights into how products are performing on your store", comment: "Description for Top Performers section of My Store tab.") + view.backgroundColor = StyleManager.tableViewBackgroundColor + } + + func configureTableView() { + tableView.backgroundColor = StyleManager.wooWhite + tableView.allowsSelection = false + tableView.estimatedRowHeight = Settings.estimatedRowHeight + tableView.estimatedSectionHeaderHeight = Settings.estimatedSectionHeight + tableView.rowHeight = UITableViewAutomaticDimension + } + + func registerTableViewCells() { + let cells = [LeftImageTableViewCell.self] + + for cell in cells { + tableView.register(cell.loadNib(), forCellReuseIdentifier: cell.reuseIdentifier) + } + } + + func registerTableViewHeaderFooters() { + let headersAndFooters = [TopPerformersHeaderView.self] + + for kind in headersAndFooters { + tableView.register(kind.loadNib(), forHeaderFooterViewReuseIdentifier: kind.reuseIdentifier) + } } } @@ -82,12 +98,51 @@ extension TopPerformerDataViewController { } -// MARK: - Private Helpers +// MARK: - UITableViewDataSource Conformance // -private extension TopPerformerDataViewController { +extension TopPerformerDataViewController: UITableViewDataSource { + + func numberOfSections(in tableView: UITableView) -> Int { + return 1 + // FIXME: Make this work! + //return resultsController.sections.count + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 0 + // FIXME: Make this work! + //return resultsController.sections[section].numberOfObjects + } + + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + guard let cell = tableView.dequeueReusableHeaderFooterView(withIdentifier: TopPerformersHeaderView.reuseIdentifier) as? TopPerformersHeaderView else { + fatalError() + } + + cell.configure(descriptionText: Text.sectionDescription, + leftText: Text.sectionLeftColumn.uppercased(), + rightText: Text.sectionRightColumn.uppercased()) + return cell + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: LeftImageTableViewCell.reuseIdentifier, for: indexPath) as? LeftImageTableViewCell else { + fatalError() + } + + return cell - func reloadAllFields() { - // TODO: fill this in! + // FIXME: Make this work! + } +} + + +// MARK: - UITableViewDelegate Conformance +// +extension TopPerformerDataViewController: UITableViewDelegate { + + func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + return UITableViewAutomaticDimension } } @@ -95,9 +150,16 @@ private extension TopPerformerDataViewController { // MARK: - Constants! // private extension TopPerformerDataViewController { - enum Constants { - static let descriptionLabelInsets = UIEdgeInsets(top: 14, left: 14, bottom: 14, right: 14) - static let noActivityText = NSLocalizedString("No activity this period", comment: "Default text for Top Performers section when no data exists for a given period.") + enum Text { + static let noActivity = NSLocalizedString("No activity this period", comment: "Default text for Top Performers section when no data exists for a given period.") + static let sectionDescription = NSLocalizedString("Gain insights into how products are performing on your store", comment: "Description for Top Performers section of My Store tab.") + static let sectionLeftColumn = NSLocalizedString("Product", comment: "Description for Top Performers left column header") + static let sectionRightColumn = NSLocalizedString("Total Spend", comment: "Description for Top Performers right column header") + } + + enum Settings { + static let estimatedRowHeight = CGFloat(64) + static let estimatedSectionHeight = CGFloat(125) } } diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.xib b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.xib index 1d6cbd5b5c3..1894e689f8f 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.xib +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.xib @@ -11,8 +11,6 @@ - - @@ -22,39 +20,24 @@ - + - - - - - - - - - - - - - - - + + + + + + - - - - + + + + - + diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersHeaderView.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersHeaderView.swift new file mode 100644 index 00000000000..998c78e28c6 --- /dev/null +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersHeaderView.swift @@ -0,0 +1,80 @@ +import UIKit + + +/// Renders a section header for top performers with a main description label and two column labels [Left / Right]. +/// +class TopPerformersHeaderView: UITableViewHeaderFooterView { + + /// Main Description Label + /// + @IBOutlet private weak var descriptionLabel: UILabel! + + /// Left Label + /// + @IBOutlet private weak var leftColumn: UILabel! + + /// Right Label + /// + @IBOutlet private weak var rightColumn: UILabel! + + /// Bottom Border View + /// + @IBOutlet private weak var borderView: UIView! + + /// Description Label's Text + /// + var descriptionText: String? { + get { + return descriptionLabel.text + } + set { + descriptionLabel.text = newValue + } + } + + /// Left Label's Text + /// + var leftText: String? { + get { + return leftColumn.text + } + set { + leftColumn.text = newValue + } + } + + /// Right Label's Text + /// + var rightText: String? { + get { + return rightColumn.text + } + set { + rightColumn.text = newValue + } + } + + // MARK: - Initialization + + override func awakeFromNib() { + super.awakeFromNib() + backgroundColor = StyleManager.wooWhite + descriptionLabel.applyBodyStyle() + leftColumn.applyFootnoteStyle() + rightColumn.applyFootnoteStyle() + leftColumn.textColor = StyleManager.sectionTitleColor + rightColumn.textColor = StyleManager.sectionTitleColor + borderView.backgroundColor = StyleManager.wooGreyBorder + } +} + + +// MARK: - Public Methods +// +extension TopPerformersHeaderView { + func configure(descriptionText: String?, leftText: String?, rightText: String?) { + descriptionLabel.text = descriptionText + leftColumn.text = leftText + rightColumn.text = rightText + } +} diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersHeaderView.xib b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersHeaderView.xib new file mode 100644 index 00000000000..63dc29370c0 --- /dev/null +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersHeaderView.xib @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index 55c04b8354e..35dfed495a5 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -34,6 +34,8 @@ 748D34DE214828DD00E21A2F /* TopPerformerDataViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 748D34DC214828DC00E21A2F /* TopPerformerDataViewController.xib */; }; 748D34DF214828DD00E21A2F /* TopPerformersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 748D34DD214828DC00E21A2F /* TopPerformersViewController.swift */; }; 748D34E12148291E00E21A2F /* TopPerformerDataViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 748D34E02148291E00E21A2F /* TopPerformerDataViewController.swift */; }; + 7493BB8E2149852A003071A9 /* TopPerformersHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7493BB8C2149852A003071A9 /* TopPerformersHeaderView.swift */; }; + 7493BB8F2149852A003071A9 /* TopPerformersHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7493BB8D2149852A003071A9 /* TopPerformersHeaderView.xib */; }; 74A93A40212DC60B00C13E04 /* NewOrdersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74A93A3F212DC60B00C13E04 /* NewOrdersViewController.swift */; }; 74AAF6A5212A04A900C612B0 /* ChartMarker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74AAF6A4212A04A900C612B0 /* ChartMarker.swift */; }; 74AFF2EA211B9B1B0038153A /* StoreStatsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74AFF2E9211B9B1B0038153A /* StoreStatsViewController.swift */; }; @@ -222,6 +224,8 @@ 748D34DC214828DC00E21A2F /* TopPerformerDataViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TopPerformerDataViewController.xib; sourceTree = ""; }; 748D34DD214828DC00E21A2F /* TopPerformersViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TopPerformersViewController.swift; sourceTree = ""; }; 748D34E02148291E00E21A2F /* TopPerformerDataViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TopPerformerDataViewController.swift; sourceTree = ""; }; + 7493BB8C2149852A003071A9 /* TopPerformersHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TopPerformersHeaderView.swift; sourceTree = ""; }; + 7493BB8D2149852A003071A9 /* TopPerformersHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TopPerformersHeaderView.xib; sourceTree = ""; }; 74A93A3F212DC60B00C13E04 /* NewOrdersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewOrdersViewController.swift; sourceTree = ""; }; 74AAF6A4212A04A900C612B0 /* ChartMarker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartMarker.swift; sourceTree = ""; }; 74AFF2E9211B9B1B0038153A /* StoreStatsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreStatsViewController.swift; sourceTree = ""; }; @@ -404,6 +408,8 @@ 748D34DD214828DC00E21A2F /* TopPerformersViewController.swift */, 748D34E02148291E00E21A2F /* TopPerformerDataViewController.swift */, 748D34DC214828DC00E21A2F /* TopPerformerDataViewController.xib */, + 7493BB8C2149852A003071A9 /* TopPerformersHeaderView.swift */, + 7493BB8D2149852A003071A9 /* TopPerformersHeaderView.xib */, ); path = MyStore; sourceTree = ""; @@ -967,6 +973,7 @@ CE1CCB4720570A6C000EE3AC /* en.lproj in Resources */, CE32B11620BF8779006FBCF4 /* ProductListTableViewCell.xib in Resources */, B557652D20F6827900185843 /* StoreTableViewCell.xib in Resources */, + 7493BB8F2149852A003071A9 /* TopPerformersHeaderView.xib in Resources */, CE1EC8C620B46819009762BF /* PaymentTableViewCell.xib in Resources */, CE21B3E120FFC59700A259D5 /* ProductDetailsTableViewCell.xib in Resources */, CE85FD5320F677770080B73E /* Dashboard.storyboard in Resources */, @@ -1207,6 +1214,7 @@ CEE006052077D1280079161F /* SummaryTableViewCell.swift in Sources */, CE1F51272064345B00C6C810 /* UIColor+Helpers.swift in Sources */, B57B678C2107638C00AF8905 /* Order+Woo.swift in Sources */, + 7493BB8E2149852A003071A9 /* TopPerformersHeaderView.swift in Sources */, CE1EC8CA20B479F1009762BF /* TwoColumnLabelView.swift in Sources */, CE21B3DD20FF9BC200A259D5 /* ProductListViewController.swift in Sources */, 74AAF6A5212A04A900C612B0 /* ChartMarker.swift in Sources */, From 7f77f0363d9fc926db8dee911ce3e6c6a1872d42 Mon Sep 17 00:00:00 2001 From: Matt Bumgardner Date: Wed, 12 Sep 2018 16:02:54 -0500 Subject: [PATCH 09/22] Top perf tweaks --- .../Dashboard/MyStore/TopPerformerDataViewController.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift index b62113d2d14..fb5091442da 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift @@ -65,10 +65,12 @@ private extension TopPerformerDataViewController { func configureTableView() { tableView.backgroundColor = StyleManager.wooWhite + tableView.separatorColor = StyleManager.wooGreyBorder tableView.allowsSelection = false tableView.estimatedRowHeight = Settings.estimatedRowHeight tableView.estimatedSectionHeaderHeight = Settings.estimatedSectionHeight tableView.rowHeight = UITableViewAutomaticDimension + tableView.tableFooterView = UIView(frame: .zero) } func registerTableViewCells() { From 03d48836cf8aaf3899b8567ca225e31eaab9b227 Mon Sep 17 00:00:00 2001 From: Matt Bumgardner Date: Thu, 13 Sep 2018 10:16:45 -0500 Subject: [PATCH 10/22] Added ProductTableViewCell --- .../MyStore/ProductTableViewCell.swift | 45 +++++++++++ .../MyStore/ProductTableViewCell.xib | 74 +++++++++++++++++++ .../TopPerformerDataViewController.swift | 8 +- .../WooCommerce.xcodeproj/project.pbxproj | 8 ++ 4 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.swift create mode 100644 WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.xib diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.swift new file mode 100644 index 00000000000..fb42865348f --- /dev/null +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.swift @@ -0,0 +1,45 @@ +import UIKit + +class ProductTableViewCell: UITableViewCell { + + // MARK: - Properties + + @IBOutlet private weak var productImage: UIImageView! + @IBOutlet private var nameLabel: UILabel! + @IBOutlet private var detailLabel: UILabel! + @IBOutlet private var priceLabel: UILabel! + + var nameText: String? { + get { + return nameLabel.text + } + set { + nameLabel.text = newValue + } + } + + var detailText: String? { + get { + return detailLabel.text + } + set { + detailLabel.text = newValue + } + } + + var priceText: String? { + get { + return priceLabel.text + } + set { + priceLabel.text = newValue + } + } + + override func awakeFromNib() { + super.awakeFromNib() + nameLabel.applyBodyStyle() + priceLabel.applyBodyStyle() + detailLabel.applyFootnoteStyle() + } +} diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.xib b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.xib new file mode 100644 index 00000000000..3ceafd71c19 --- /dev/null +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.xib @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift index fb5091442da..5e768c71da0 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift @@ -74,7 +74,7 @@ private extension TopPerformerDataViewController { } func registerTableViewCells() { - let cells = [LeftImageTableViewCell.self] + let cells = [ProductTableViewCell.self] for cell in cells { tableView.register(cell.loadNib(), forCellReuseIdentifier: cell.reuseIdentifier) @@ -128,13 +128,13 @@ extension TopPerformerDataViewController: UITableViewDataSource { } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: LeftImageTableViewCell.reuseIdentifier, for: indexPath) as? LeftImageTableViewCell else { + guard let cell = tableView.dequeueReusableCell(withIdentifier: ProductTableViewCell.reuseIdentifier, + for: indexPath) as? ProductTableViewCell else { fatalError() } + // FIXME: Configure the cell return cell - - // FIXME: Make this work! } } diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index 35dfed495a5..6695a2337b5 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -24,6 +24,8 @@ 74036CC0211B882100E462C2 /* PeriodDataViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74036CBF211B882100E462C2 /* PeriodDataViewController.swift */; }; 7403F7E220EC04070097198F /* OrderStatusViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7403F7E120EC04070097198F /* OrderStatusViewModel.swift */; }; 7421344A210A323C00C13890 /* WooAnalyticsStat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74213449210A323C00C13890 /* WooAnalyticsStat.swift */; }; + 74334F36214AB130006D6AC5 /* ProductTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74334F34214AB12F006D6AC5 /* ProductTableViewCell.swift */; }; + 74334F37214AB130006D6AC5 /* ProductTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 74334F35214AB12F006D6AC5 /* ProductTableViewCell.xib */; }; 746791632108D7C0007CF1DC /* WooAnalyticsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 746791622108D7C0007CF1DC /* WooAnalyticsTests.swift */; }; 746791662108D87B007CF1DC /* MockupAnalyticsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 746791652108D87B007CF1DC /* MockupAnalyticsProvider.swift */; }; 747AA0892107CEC60047A89B /* AnalyticsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 747AA0882107CEC60047A89B /* AnalyticsProvider.swift */; }; @@ -214,6 +216,8 @@ 74036CBF211B882100E462C2 /* PeriodDataViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeriodDataViewController.swift; sourceTree = ""; }; 7403F7E120EC04070097198F /* OrderStatusViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderStatusViewModel.swift; sourceTree = ""; }; 74213449210A323C00C13890 /* WooAnalyticsStat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WooAnalyticsStat.swift; sourceTree = ""; }; + 74334F34214AB12F006D6AC5 /* ProductTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProductTableViewCell.swift; sourceTree = ""; }; + 74334F35214AB12F006D6AC5 /* ProductTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ProductTableViewCell.xib; sourceTree = ""; }; 746791622108D7C0007CF1DC /* WooAnalyticsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WooAnalyticsTests.swift; sourceTree = ""; }; 746791652108D87B007CF1DC /* MockupAnalyticsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockupAnalyticsProvider.swift; sourceTree = ""; }; 747AA0882107CEC60047A89B /* AnalyticsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsProvider.swift; sourceTree = ""; }; @@ -404,6 +408,8 @@ 74A93A3F212DC60B00C13E04 /* NewOrdersViewController.swift */, 74036CBF211B882100E462C2 /* PeriodDataViewController.swift */, 74E0F440211C9AE600A79CCE /* PeriodDataViewController.xib */, + 74334F34214AB12F006D6AC5 /* ProductTableViewCell.swift */, + 74334F35214AB12F006D6AC5 /* ProductTableViewCell.xib */, 74AFF2E9211B9B1B0038153A /* StoreStatsViewController.swift */, 748D34DD214828DC00E21A2F /* TopPerformersViewController.swift */, 748D34E02148291E00E21A2F /* TopPerformerDataViewController.swift */, @@ -979,6 +985,7 @@ CE85FD5320F677770080B73E /* Dashboard.storyboard in Resources */, B56DB3CF2049BFAA00D4AA8E /* Main.storyboard in Resources */, CE21B3D820FE669A00A259D5 /* BasicDisclosureTableViewCell.xib in Resources */, + 74334F37214AB130006D6AC5 /* ProductTableViewCell.xib in Resources */, CE1EC8C820B478B6009762BF /* TwoColumnLabelView.xib in Resources */, CE855365209BA6A700938BDC /* CustomerInfoTableViewCell.xib in Resources */, ); @@ -1187,6 +1194,7 @@ B55D4C0620B6027200D7A50F /* AuthenticationManager.swift in Sources */, CE85FD5A20F7A7640080B73E /* SettingsFooterView.swift in Sources */, B5BE368E20BED80B00BE0A8C /* SafariViewController.swift in Sources */, + 74334F36214AB130006D6AC5 /* ProductTableViewCell.swift in Sources */, CE855364209BA6A700938BDC /* ShowHideSectionFooter.swift in Sources */, B57C744A20F5649300EEFC87 /* EmptyStoresTableViewCell.swift in Sources */, B56DB3CA2049BFAA00D4AA8E /* AppDelegate.swift in Sources */, From c5ef920622c380d17e2bc1f66ac0d574c6b3b785 Mon Sep 17 00:00:00 2001 From: Matt Bumgardner Date: Thu, 13 Sep 2018 12:20:44 -0500 Subject: [PATCH 11/22] WIP: Loading data in top performers --- .../Classes/Extensions/Array+Helpers.swift | 9 +++ .../MyStore/ProductTableViewCell.swift | 11 +++ .../TopPerformerDataViewController.swift | 71 +++++++++++++++++-- .../MyStore/TopPerformersViewController.swift | 23 +----- Yosemite/Yosemite/Stores/StatsStore.swift | 55 +++++++++----- 5 files changed, 122 insertions(+), 47 deletions(-) diff --git a/WooCommerce/Classes/Extensions/Array+Helpers.swift b/WooCommerce/Classes/Extensions/Array+Helpers.swift index a83e3fc58b5..d84f51bf3aa 100644 --- a/WooCommerce/Classes/Extensions/Array+Helpers.swift +++ b/WooCommerce/Classes/Extensions/Array+Helpers.swift @@ -15,3 +15,12 @@ extension Array { return removeFirst() } } + +extension Collection { + + /// Returns the element at the specified index iff it is within bounds, otherwise nil. + /// + subscript (safe index: Index) -> Element? { + return indices.contains(index) ? self[index] : nil + } +} diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.swift index fb42865348f..456eff171c9 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.swift @@ -1,4 +1,5 @@ import UIKit +import Yosemite class ProductTableViewCell: UITableViewCell { @@ -43,3 +44,13 @@ class ProductTableViewCell: UITableViewCell { detailLabel.applyFootnoteStyle() } } + +// MARK: - Public Methods +// +extension ProductTableViewCell { + func configure(_ statsItem: TopEarnerStatsItem?) { + nameText = statsItem?.productName + detailText = String(statsItem?.quantity ?? 0) + priceText = statsItem?.total.friendlyString() + } +} diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift index 5e768c71da0..119b208968a 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift @@ -13,6 +13,17 @@ class TopPerformerDataViewController: UIViewController, IndicatorInfoProvider { @IBOutlet private weak var tableView: UITableView! + /// ResultsController: Surrounds us. Binds the galaxy together. And also, keeps the UITableView <> (Stored) TopEarnerStats in sync. + /// + private lazy var resultsController: ResultsController = { + let storageManager = AppDelegate.shared.storageManager + let formattedDateString = StatsStore.buildDateString(from: Date(), with: granularity) + let predicate = NSPredicate(format: "granularity = %@ AND date = %@", granularity.rawValue, formattedDateString) + let descriptor = NSSortDescriptor(key: "date", ascending: true) + + return ResultsController(storageManager: storageManager, matching: predicate, sortedBy: [descriptor]) + }() + // MARK: - Computed Properties private var tabDescription: String { @@ -51,6 +62,27 @@ class TopPerformerDataViewController: UIViewController, IndicatorInfoProvider { configureTableView() registerTableViewCells() registerTableViewHeaderFooters() + configureResultsController() + } +} + + +// MARK: - Public Interface +// +extension TopPerformerDataViewController { + + func syncTopPerformers(onCompletion: ((Error?) -> ())? = nil) { + guard let siteID = StoresManager.shared.sessionManager.defaultStoreID else { + onCompletion?(nil) + return + } + + let action = StatsAction.retrieveTopEarnerStats(siteID: siteID, granularity: granularity, latestDateToInclude: Date()) { (error) in + if let error = error { + DDLogError("⛔️ Dashboard (Top Performers) — Error synchronizing top earner stats: \(error)") + } + } + StoresManager.shared.dispatch(action) } } @@ -73,6 +105,11 @@ private extension TopPerformerDataViewController { tableView.tableFooterView = UIView(frame: .zero) } + func configureResultsController() { + resultsController.startForwardingEvents(to: tableView) + try? resultsController.performFetch() + } + func registerTableViewCells() { let cells = [ProductTableViewCell.self] @@ -105,15 +142,11 @@ extension TopPerformerDataViewController { extension TopPerformerDataViewController: UITableViewDataSource { func numberOfSections(in tableView: UITableView) -> Int { - return 1 - // FIXME: Make this work! - //return resultsController.sections.count + return resultsController.sections.count } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 0 - // FIXME: Make this work! - //return resultsController.sections[section].numberOfObjects + return numberOfRows() } func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { @@ -133,7 +166,7 @@ extension TopPerformerDataViewController: UITableViewDataSource { fatalError() } - // FIXME: Configure the cell + cell.configure(statsItem(at: indexPath)) return cell } } @@ -149,6 +182,30 @@ extension TopPerformerDataViewController: UITableViewDelegate { } +// MARK: - Convenience Methods +// +private extension TopPerformerDataViewController { + + func statsItem(at indexPath: IndexPath) -> TopEarnerStatsItem? { + guard let topEarnerStats = resultsController.fetchedObjects.first, + let topEarnerStatsItem = topEarnerStats.items?[safe: indexPath.row] else { + return nil + } + + return topEarnerStatsItem + } + + func numberOfRows() -> Int { + guard !resultsController.isEmpty else { + return 0 + } + + let topEarnerStats = resultsController.fetchedObjects.first + return topEarnerStats?.items?.count ?? 0 + } +} + + // MARK: - Constants! // private extension TopPerformerDataViewController { diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersViewController.swift index d211b15c3ea..cede1a27cc5 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersViewController.swift @@ -44,7 +44,7 @@ extension TopPerformersViewController { func syncTopPerformers() { dataVCs.forEach { (vc) in - syncTopPerformers(for: vc.granularity) + vc.syncTopPerformers() } } } @@ -96,27 +96,6 @@ private extension TopPerformersViewController { } -// MARK: - Sync'ing Helpers -// -private extension TopPerformersViewController { - - func syncTopPerformers(for granularity: StatGranularity, onCompletion: ((Error?) -> ())? = nil) { - guard let siteID = StoresManager.shared.sessionManager.defaultStoreID else { - onCompletion?(nil) - return - } - - let action = StatsAction.retrieveTopEarnerStats(siteID: siteID, granularity: granularity, latestDateToInclude: Date()) { (error) in - if let error = error { - DDLogError("⛔️ Dashboard (Top Performers) — Error synchronizing top earner stats: \(error)") - } - } - - StoresManager.shared.dispatch(action) - } -} - - // MARK: - Constants! // private extension TopPerformersViewController { diff --git a/Yosemite/Yosemite/Stores/StatsStore.swift b/Yosemite/Yosemite/Stores/StatsStore.swift index e190214a0e9..4c3e326f3e0 100644 --- a/Yosemite/Yosemite/Stores/StatsStore.swift +++ b/Yosemite/Yosemite/Stores/StatsStore.swift @@ -33,6 +33,27 @@ public class StatsStore: Store { } +// MARK: - Public Helpers +// +public extension StatsStore { + + /// Converts a Date into the appropriatly formatted string based on the `OrderStatGranularity` + /// + static func buildDateString(from date: Date, with granularity: StatGranularity) -> String { + switch granularity { + case .day: + return DateFormatter.Stats.statsDayFormatter.string(from: date) + case .week: + return DateFormatter.Stats.statsWeekFormatter.string(from: date) + case .month: + return DateFormatter.Stats.statsMonthFormatter.string(from: date) + case .year: + return DateFormatter.Stats.statsYearFormatter.string(from: date) + } + } +} + + // MARK: - Services! // private extension StatsStore { @@ -42,7 +63,7 @@ private extension StatsStore { func retrieveOrderStats(siteID: Int, granularity: StatGranularity, latestDateToInclude: Date, quantity: Int, onCompletion: @escaping (OrderStats?, Error?) -> Void) { let remote = OrderStatsRemote(network: network) - let formattedDateString = buildDateString(from: latestDateToInclude, with: granularity) + let formattedDateString = StatsStore.buildDateString(from: latestDateToInclude, with: granularity) remote.loadOrderStats(for: siteID, unit: granularity, latestDateToInclude: formattedDateString, quantity: quantity) { (orderStats, error) in guard let orderStats = orderStats else { @@ -75,9 +96,9 @@ private extension StatsStore { func retrieveTopEarnerStats(siteID: Int, granularity: StatGranularity, latestDateToInclude: Date, onCompletion: @escaping (Error?) -> Void) { let remote = TopEarnersStatsRemote(network: network) - let formattedDateString = buildDateString(from: latestDateToInclude, with: granularity) + let formattedDateString = StatsStore.buildDateString(from: latestDateToInclude, with: granularity) - remote.loadTopEarnersStats(for: siteID, unit: granularity, latestDateToInclude: formattedDateString, limit: 5) { [weak self] (topEarnerStats, error) in + remote.loadTopEarnersStats(for: siteID, unit: granularity, latestDateToInclude: formattedDateString, limit: Constants.defaultTopEarnerStatsLimit) { [weak self] (topEarnerStats, error) in guard let topEarnerStats = topEarnerStats else { onCompletion(error) return @@ -87,21 +108,6 @@ private extension StatsStore { onCompletion(nil) } } - - /// Converts a Date into the appropriatly formatted string based on the `OrderStatGranularity` - /// - func buildDateString(from date: Date, with granularity: StatGranularity) -> String { - switch granularity { - case .day: - return DateFormatter.Stats.statsDayFormatter.string(from: date) - case .week: - return DateFormatter.Stats.statsWeekFormatter.string(from: date) - case .month: - return DateFormatter.Stats.statsMonthFormatter.string(from: date) - case .year: - return DateFormatter.Stats.statsYearFormatter.string(from: date) - } - } } @@ -140,3 +146,16 @@ extension StatsStore { }) } } + + +// MARK: - Constants! +// +extension StatsStore { + + enum Constants { + + /// Default limit value for TopEarnerStats + /// + static let defaultTopEarnerStatsLimit: Int = 5 + } +} From 49717af1a997203c889401f6e870259006f18158 Mon Sep 17 00:00:00 2001 From: Matt Bumgardner Date: Thu, 13 Sep 2018 14:13:58 -0500 Subject: [PATCH 12/22] Top Perf: UI updates --- .../Classes/Extensions/UIImage+Woo.swift | 7 ++++ .../Dashboard/Dashboard.storyboard | 35 ++++++------------- .../MyStore/ProductTableViewCell.swift | 4 ++- .../MyStore/ProductTableViewCell.xib | 35 ++++++++++--------- .../TopPerformerDataViewController.swift | 4 +-- .../MyStore/TopPerformersHeaderView.swift | 1 - .../MyStore/TopPerformersHeaderView.xib | 10 +++++- .../MyStore/TopPerformersViewController.swift | 2 -- Yosemite/Yosemite/Stores/StatsStore.swift | 2 +- 9 files changed, 52 insertions(+), 48 deletions(-) diff --git a/WooCommerce/Classes/Extensions/UIImage+Woo.swift b/WooCommerce/Classes/Extensions/UIImage+Woo.swift index a14fd3f2989..1439ced5673 100644 --- a/WooCommerce/Classes/Extensions/UIImage+Woo.swift +++ b/WooCommerce/Classes/Extensions/UIImage+Woo.swift @@ -21,6 +21,13 @@ extension UIImage { return Gridicon.iconOfType(.chevronRight).imageWithTintColor(tintColor)! } + /// Product Placeholder Image + /// + static var productPlaceholderImage: UIImage { + let tintColor = StyleManager.wooGreyLight + return Gridicon.iconOfType(.product).imageWithTintColor(tintColor)! + } + /// Gravatar Placeholder Image /// static var gravatarPlaceholderImage: UIImage { diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard b/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard index 106743d6852..f466de9c3f1 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard @@ -90,14 +90,14 @@ - + - + - + @@ -125,20 +125,15 @@ - + - + + - - - - - - @@ -163,7 +158,7 @@ - + @@ -349,11 +344,11 @@ - + - + @@ -401,18 +396,11 @@ - + - + - - - - - - - @@ -425,7 +413,6 @@ - diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.swift index 456eff171c9..6e6d49bfa09 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.swift @@ -1,5 +1,6 @@ import UIKit import Yosemite +import Gridicons class ProductTableViewCell: UITableViewCell { @@ -42,6 +43,7 @@ class ProductTableViewCell: UITableViewCell { nameLabel.applyBodyStyle() priceLabel.applyBodyStyle() detailLabel.applyFootnoteStyle() + productImage.image = UIImage.productPlaceholderImage } } @@ -50,7 +52,7 @@ class ProductTableViewCell: UITableViewCell { extension ProductTableViewCell { func configure(_ statsItem: TopEarnerStatsItem?) { nameText = statsItem?.productName - detailText = String(statsItem?.quantity ?? 0) + detailText = String.localizedStringWithFormat( NSLocalizedString("Total Product Order: %ld", comment: "Top performers — label for the total number of products ordered"), statsItem?.quantity ?? 0) priceText = statsItem?.total.friendlyString() } } diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.xib b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.xib index 3ceafd71c19..cf3725e54c9 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.xib +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.xib @@ -12,63 +12,66 @@ - - + + - + - + + - + - - + - + + - - - + + + - + diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift index 119b208968a..7a91207609c 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift @@ -96,7 +96,7 @@ private extension TopPerformerDataViewController { } func configureTableView() { - tableView.backgroundColor = StyleManager.wooWhite + tableView.backgroundColor = StyleManager.tableViewBackgroundColor tableView.separatorColor = StyleManager.wooGreyBorder tableView.allowsSelection = false tableView.estimatedRowHeight = Settings.estimatedRowHeight @@ -217,7 +217,7 @@ private extension TopPerformerDataViewController { } enum Settings { - static let estimatedRowHeight = CGFloat(64) + static let estimatedRowHeight = CGFloat(80) static let estimatedSectionHeight = CGFloat(125) } } diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersHeaderView.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersHeaderView.swift index 998c78e28c6..c03a67330ba 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersHeaderView.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersHeaderView.swift @@ -58,7 +58,6 @@ class TopPerformersHeaderView: UITableViewHeaderFooterView { override func awakeFromNib() { super.awakeFromNib() - backgroundColor = StyleManager.wooWhite descriptionLabel.applyBodyStyle() leftColumn.applyFootnoteStyle() rightColumn.applyFootnoteStyle() diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersHeaderView.xib b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersHeaderView.xib index 63dc29370c0..6d1f180f14e 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersHeaderView.xib +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersHeaderView.xib @@ -14,6 +14,10 @@ + + + + + + @@ -49,7 +55,9 @@ + + @@ -60,7 +68,7 @@ - + diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersViewController.swift index cede1a27cc5..cb63d5ebbae 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformersViewController.swift @@ -10,7 +10,6 @@ class TopPerformersViewController: ButtonBarPagerTabStripViewController { @IBOutlet private weak var topBorder: UIView! @IBOutlet private weak var middleBorder: UIView! - @IBOutlet private weak var bottomBorder: UIView! @IBOutlet private weak var headingLabel: PaddedLabel! private var dataVCs = [TopPerformerDataViewController]() @@ -58,7 +57,6 @@ private extension TopPerformersViewController { view.backgroundColor = StyleManager.tableViewBackgroundColor topBorder.backgroundColor = StyleManager.wooGreyBorder middleBorder.backgroundColor = StyleManager.wooGreyBorder - bottomBorder.backgroundColor = StyleManager.wooGreyBorder headingLabel.applyFootnoteStyle() headingLabel.textColor = StyleManager.sectionTitleColor headingLabel.textInsets = Constants.headerLabelInsets diff --git a/Yosemite/Yosemite/Stores/StatsStore.swift b/Yosemite/Yosemite/Stores/StatsStore.swift index 4c3e326f3e0..0146aa4263d 100644 --- a/Yosemite/Yosemite/Stores/StatsStore.swift +++ b/Yosemite/Yosemite/Stores/StatsStore.swift @@ -156,6 +156,6 @@ extension StatsStore { /// Default limit value for TopEarnerStats /// - static let defaultTopEarnerStatsLimit: Int = 5 + static let defaultTopEarnerStatsLimit: Int = 3 } } From 9bcb8a91bb0df7d6fd499c971103eb5023d237fd Mon Sep 17 00:00:00 2001 From: Matt Bumgardner Date: Thu, 13 Sep 2018 15:05:35 -0500 Subject: [PATCH 13/22] ProductTableViewCell updates --- .../ViewRelated/Dashboard/Dashboard.storyboard | 14 +++++++------- .../Dashboard/MyStore/ProductTableViewCell.swift | 9 ++++++++- .../Dashboard/MyStore/ProductTableViewCell.xib | 4 ++-- .../MyStore/TopPerformerDataViewController.swift | 2 +- .../MyStore/TopPerformerDataViewController.xib | 2 +- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard b/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard index f466de9c3f1..175633bbf73 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard @@ -97,7 +97,7 @@ - + @@ -125,10 +125,10 @@ - + - - + + @@ -344,11 +344,11 @@ - + - + @@ -396,7 +396,7 @@ - + diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.swift index 6e6d49bfa09..14be7cbf839 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.swift @@ -1,5 +1,6 @@ import UIKit import Yosemite +import WordPressUI import Gridicons class ProductTableViewCell: UITableViewCell { @@ -43,7 +44,7 @@ class ProductTableViewCell: UITableViewCell { nameLabel.applyBodyStyle() priceLabel.applyBodyStyle() detailLabel.applyFootnoteStyle() - productImage.image = UIImage.productPlaceholderImage + productImage.contentMode = .scaleAspectFit } } @@ -54,5 +55,11 @@ extension ProductTableViewCell { nameText = statsItem?.productName detailText = String.localizedStringWithFormat( NSLocalizedString("Total Product Order: %ld", comment: "Top performers — label for the total number of products ordered"), statsItem?.quantity ?? 0) priceText = statsItem?.total.friendlyString() + + if let productURLString = statsItem?.imageUrl { + productImage.downloadImage(from: URL(string: productURLString), placeholderImage: UIImage.productPlaceholderImage) + } else { + productImage.image = UIImage.productPlaceholderImage + } } } diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.xib b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.xib index cf3725e54c9..9124f117f0f 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.xib +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ProductTableViewCell.xib @@ -35,7 +35,7 @@ - - + - - + + @@ -344,11 +344,11 @@ - + - + @@ -396,7 +396,7 @@ - + diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift index 578c38f54a2..bebe870a114 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift @@ -163,10 +163,6 @@ extension TopPerformerDataViewController: UITableViewDataSource { } func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { - guard hasTopEarnerStatsItems else { - return Constants.emptyView - } - guard let cell = tableView.dequeueReusableHeaderFooterView(withIdentifier: TopPerformersHeaderView.reuseIdentifier) as? TopPerformersHeaderView else { fatalError() } @@ -198,9 +194,6 @@ extension TopPerformerDataViewController: UITableViewDataSource { extension TopPerformerDataViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { - guard hasTopEarnerStatsItems else { - return CGFloat.leastNonzeroMagnitude - } return UITableViewAutomaticDimension } From 62bc388cebbb40c8ddb9a6d122066f9be2d9bce1 Mon Sep 17 00:00:00 2001 From: Matt Bumgardner Date: Mon, 17 Sep 2018 10:33:32 -0500 Subject: [PATCH 21/22] Top Perf: doc comment updates --- WooCommerce/Classes/Extensions/Array+Helpers.swift | 2 +- WooCommerce/Classes/Model/TopEarnerStatsItem+Woo.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/WooCommerce/Classes/Extensions/Array+Helpers.swift b/WooCommerce/Classes/Extensions/Array+Helpers.swift index d84f51bf3aa..fa9edcb9168 100644 --- a/WooCommerce/Classes/Extensions/Array+Helpers.swift +++ b/WooCommerce/Classes/Extensions/Array+Helpers.swift @@ -18,7 +18,7 @@ extension Array { extension Collection { - /// Returns the element at the specified index iff it is within bounds, otherwise nil. + /// Returns the element at the specified index if it is within bounds, otherwise nil. /// subscript (safe index: Index) -> Element? { return indices.contains(index) ? self[index] : nil diff --git a/WooCommerce/Classes/Model/TopEarnerStatsItem+Woo.swift b/WooCommerce/Classes/Model/TopEarnerStatsItem+Woo.swift index 1ed6ee7e06c..6051749abfb 100644 --- a/WooCommerce/Classes/Model/TopEarnerStatsItem+Woo.swift +++ b/WooCommerce/Classes/Model/TopEarnerStatsItem+Woo.swift @@ -19,7 +19,7 @@ extension TopEarnerStatsItem { return Locale(identifier: identifier).currencySymbol ?? currency } - /// Returns the a friendly-formatted total string including the currency symbol + /// Returns a friendly-formatted total string including the currency symbol /// var formattedTotalString: String { return currencySymbol + total.friendlyString() From d7f9513f2ce170552e545dd2c532a401ebab118e Mon Sep 17 00:00:00 2001 From: Matt Bumgardner Date: Mon, 17 Sep 2018 15:21:01 -0500 Subject: [PATCH 22/22] Added IntrinsicTableView subclass --- .../Dashboard/Dashboard.storyboard | 3 --- .../TopPerformerDataViewController.swift | 2 +- .../TopPerformerDataViewController.xib | 2 +- .../ReusableViews/IntrinsicTableView.swift | 26 +++++++++++++++++++ .../WooCommerce.xcodeproj/project.pbxproj | 4 +++ 5 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 WooCommerce/Classes/ViewRelated/ReusableViews/IntrinsicTableView.swift diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard b/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard index f6762120ddd..ff0d34ab011 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Dashboard.storyboard @@ -397,9 +397,6 @@ - - - diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift index bebe870a114..62bb54928a6 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.swift @@ -11,7 +11,7 @@ class TopPerformerDataViewController: UIViewController, IndicatorInfoProvider { public let granularity: StatGranularity - @IBOutlet private weak var tableView: UITableView! + @IBOutlet private weak var tableView: IntrinsicTableView! /// ResultsController: Loads TopEarnerStats for the current granularity from the Storage Layer /// diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.xib b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.xib index acfc1dd259d..957b22fcb0b 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.xib +++ b/WooCommerce/Classes/ViewRelated/Dashboard/MyStore/TopPerformerDataViewController.xib @@ -20,7 +20,7 @@ - + diff --git a/WooCommerce/Classes/ViewRelated/ReusableViews/IntrinsicTableView.swift b/WooCommerce/Classes/ViewRelated/ReusableViews/IntrinsicTableView.swift new file mode 100644 index 00000000000..9f225231a71 --- /dev/null +++ b/WooCommerce/Classes/ViewRelated/ReusableViews/IntrinsicTableView.swift @@ -0,0 +1,26 @@ +import Foundation +import UIKit + +class IntrinsicTableView: UITableView { + + override var contentSize:CGSize { + didSet { + invalidateIntrinsicContentSize() + } + } + + override var intrinsicContentSize: CGSize { + self.layoutIfNeeded() + return CGSize(width: UIViewNoIntrinsicMetric, height: contentSize.height) + } + + override func reloadData() { + super.reloadData() + invalidateIntrinsicContentSize() + } + + override func endUpdates() { + super.endUpdates() + invalidateIntrinsicContentSize() + } +} diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index 99f625c1d3b..ef3c68f1a39 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ 74334F36214AB130006D6AC5 /* ProductTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74334F34214AB12F006D6AC5 /* ProductTableViewCell.swift */; }; 74334F37214AB130006D6AC5 /* ProductTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 74334F35214AB12F006D6AC5 /* ProductTableViewCell.xib */; }; 743EDD9F214B05350039071B /* TopEarnerStatsItem+Woo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 743EDD9E214B05350039071B /* TopEarnerStatsItem+Woo.swift */; }; + 7441E1D221503F77004E6ECE /* IntrinsicTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7441E1D121503F77004E6ECE /* IntrinsicTableView.swift */; }; 746791632108D7C0007CF1DC /* WooAnalyticsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 746791622108D7C0007CF1DC /* WooAnalyticsTests.swift */; }; 746791662108D87B007CF1DC /* MockupAnalyticsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 746791652108D87B007CF1DC /* MockupAnalyticsProvider.swift */; }; 747AA0892107CEC60047A89B /* AnalyticsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 747AA0882107CEC60047A89B /* AnalyticsProvider.swift */; }; @@ -222,6 +223,7 @@ 74334F34214AB12F006D6AC5 /* ProductTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProductTableViewCell.swift; sourceTree = ""; }; 74334F35214AB12F006D6AC5 /* ProductTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ProductTableViewCell.xib; sourceTree = ""; }; 743EDD9E214B05350039071B /* TopEarnerStatsItem+Woo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TopEarnerStatsItem+Woo.swift"; sourceTree = ""; }; + 7441E1D121503F77004E6ECE /* IntrinsicTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntrinsicTableView.swift; sourceTree = ""; }; 746791622108D7C0007CF1DC /* WooAnalyticsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WooAnalyticsTests.swift; sourceTree = ""; }; 746791652108D87B007CF1DC /* MockupAnalyticsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockupAnalyticsProvider.swift; sourceTree = ""; }; 747AA0882107CEC60047A89B /* AnalyticsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsProvider.swift; sourceTree = ""; }; @@ -818,6 +820,7 @@ B5BE75DA213F1D1E00909A14 /* OverlayMessageView.swift */, B5BE75DC213F1D3D00909A14 /* OverlayMessageView.xib */, B50BB4152141828F00AF0F3C /* FooterSpinnerView.swift */, + 7441E1D121503F77004E6ECE /* IntrinsicTableView.swift */, ); path = ReusableViews; sourceTree = ""; @@ -1214,6 +1217,7 @@ B5BE368E20BED80B00BE0A8C /* SafariViewController.swift in Sources */, 74334F36214AB130006D6AC5 /* ProductTableViewCell.swift in Sources */, CE855364209BA6A700938BDC /* ShowHideSectionFooter.swift in Sources */, + 7441E1D221503F77004E6ECE /* IntrinsicTableView.swift in Sources */, B57C744A20F5649300EEFC87 /* EmptyStoresTableViewCell.swift in Sources */, B56DB3CA2049BFAA00D4AA8E /* AppDelegate.swift in Sources */, B5A82EE7210263460053ADC8 /* UIViewController+Helpers.swift in Sources */,