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
1 change: 1 addition & 0 deletions Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ target 'WooCommerce' do
pod 'KeychainAccess', '~> 3.1'
pod 'CocoaLumberjack/Swift', '~> 3.4'
pod 'XLPagerTabStrip', '~> 8.0'
pod 'Charts', '~> 3.1'

# Unit Tests
# ==========
Expand Down
8 changes: 7 additions & 1 deletion Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ PODS:
- CocoaLumberjack (~> 3.4.1)
- Reachability (~> 3.1)
- UIDeviceIdentifier (~> 0.4)
- Charts (3.1.1):
- Charts/Core (= 3.1.1)
- Charts/Core (3.1.1)
- CocoaLumberjack (3.4.2):
- CocoaLumberjack/Default (= 3.4.2)
- CocoaLumberjack/Extensions (= 3.4.2)
Expand Down Expand Up @@ -69,6 +72,7 @@ PODS:
DEPENDENCIES:
- Alamofire (~> 4.7)
- Automattic-Tracks-iOS (from `https://github.com/Automattic/Automattic-Tracks-iOS.git`, tag `0.2.3`)
- Charts (~> 3.1)
- CocoaLumberjack/Swift (~> 3.4)
- Crashlytics (~> 3.10)
- Gridicons (= 0.15)
Expand All @@ -81,6 +85,7 @@ SPEC REPOS:
https://github.com/cocoapods/specs.git:
- 1PasswordExtension
- Alamofire
- Charts
- CocoaLumberjack
- Crashlytics
- Fabric
Expand Down Expand Up @@ -116,6 +121,7 @@ SPEC CHECKSUMS:
1PasswordExtension: 0e95bdea64ec8ff2f4f693be5467a09fac42a83d
Alamofire: e4fa87002c137ba2d8d634d2c51fabcda0d5c223
Automattic-Tracks-iOS: d8c6c6c1351b1905a73e45f431b15598d71963b5
Charts: 90a4d61da0f6e06684c591e3bcab11940fe61736
CocoaLumberjack: db7cc9e464771f12054c22ff6947c5a58d43a0fd
Crashlytics: ccaac42660eb9351b9960c0d66106b0bcf99f4fa
Fabric: f233c9492b3bbc1f04e3882986740f7988a58edb
Expand All @@ -137,6 +143,6 @@ SPEC CHECKSUMS:
wpxmlrpc: bfc572f62ce7ee897f6f38b098d2ba08732ecef4
XLPagerTabStrip: c908b17cbf42fcd2598ee1adfc49bae25444d88a

PODFILE CHECKSUM: f61f936dde41c2e27f9fe80a0da46c2def730421
PODFILE CHECKSUM: 579bb6345aecc3f27e78d220e137a749e9ee5f08

COCOAPODS: 1.5.3
46 changes: 46 additions & 0 deletions WooCommerce/Classes/Extensions/Date+Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,50 @@ extension DateFormatter {
return formatter
}()
}

/// Chart Formatters
///
struct Charts {

/// Date formatter used for creating the date displayed on a chart axis for **day** granularity.
///
public static let chartsDayFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(identifier: "GMT")
formatter.dateFormat = "MMM d"
return formatter
}()

/// Date formatter used for creating the date displayed on a chart axis for **week** granularity.
///
public static let chartsWeekFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(identifier: "GMT")
formatter.dateFormat = "MMM d"
return formatter
}()

/// Date formatter used for creating the date displayed on a chart axis for **month** granularity.
///
public static let chartsMonthFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(identifier: "GMT")
formatter.dateFormat = "MMM"
return formatter
}()

/// Date formatter used for creating the date displayed on a chart axis for **year** granularity.
///
public static let chartsYearFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(identifier: "GMT")
formatter.dateFormat = "yyyy"
return formatter
}()
}

}
9 changes: 9 additions & 0 deletions WooCommerce/Classes/Model/OrderStats+Woo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,13 @@ extension OrderStats {

return Locale(identifier: identifier).currencySymbol ?? currency
}

/// Returns the sum of total sales this stats period. This value is typically used in the dashboard for revenue reporting.
///
/// *Note:* The value returned here is an aggregation of all the `OrderStatsItem.totalSales` values and
/// _not_ `OrderStats.totalGrossSales` or `OrderStats.totalNetSales`.
///
var totalSales: Double {
return items?.map({ $0.totalSales }).reduce(0.0, +) ?? 0.0
}
}
6 changes: 6 additions & 0 deletions WooCommerce/Classes/Styles/Style.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ protocol Style {
var buttonDisabledHighlightedColor: UIColor { get }
var buttonDisabledTitleColor: UIColor { get }
var cellSeparatorColor: UIColor { get }
var chartLabelFont: UIFont { get }
var defaultTextColor: UIColor { get }
var destructiveActionColor: UIColor { get }
var navBarImage: UIImage { get }
Expand Down Expand Up @@ -51,6 +52,7 @@ class DefaultStyle: Style {
let actionButtonTitleFont = UIFont.font(forStyle: .headline, weight: .semibold)
let alternativeLoginsTitleFont = UIFont.font(forStyle: .subheadline, weight: .semibold)
let subheadlineFont = UIFont.font(forStyle: .subheadline, weight: .regular)
let chartLabelFont = UIFont.font(forStyle: .caption2, weight: .ultraLight)

// Colors!
//
Expand Down Expand Up @@ -182,6 +184,10 @@ class StyleManager {
return active.cellSeparatorColor
}

static var chartLabelFont: UIFont {
return active.chartLabelFont
}

static var defaultTextColor: UIColor {
return active.defaultTextColor
}
Expand Down
193 changes: 193 additions & 0 deletions WooCommerce/Classes/ViewRelated/Dashboard/MyStore/ChartMarker.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import Foundation
import Charts


/// This class is a custom view which is displayed over a chart element (e.g. a Bar) when it is highlighted.
///
/// See: https://github.com/danielgindi/Charts/blob/master/ChartsDemo-iOS/Swift/Components/BalloonMarker.swift
///
class ChartMarker: MarkerImage {
@objc open var color: UIColor
@objc open var arrowSize = Constants.arrowSize
@objc open var font: UIFont
@objc open var textColor: UIColor
@objc open var insets: UIEdgeInsets
@objc open var minimumSize = CGSize()

private var label: String?
private var _labelSize: CGSize = CGSize()
private var _paragraphStyle: NSMutableParagraphStyle?
private var _drawAttributes = [NSAttributedStringKey: AnyObject]()

@objc public init(chartView: ChartViewBase?, color: UIColor, font: UIFont, textColor: UIColor, insets: UIEdgeInsets) {
self.color = color
self.font = font
self.textColor = textColor
self.insets = insets

_paragraphStyle = NSParagraphStyle.default.mutableCopy() as? NSMutableParagraphStyle
_paragraphStyle?.alignment = .center
super.init()
self.chartView = chartView
}

open override func offsetForDrawing(atPoint point: CGPoint) -> CGPoint {
var offset = self.offset
var size = self.size

if let image = image, size.width == 0.0 {
size.width = image.size.width
}

if let image = image, size.height == 0.0 {
size.height = image.size.height
}

let width = size.width
let height = size.height
let padding = Constants.offsetPadding

var origin = point
origin.x -= width / 2
origin.y -= height

if (origin.x + offset.x) < 0.0 {
offset.x = -origin.x + padding
} else if let chart = chartView, (origin.x + width + offset.x) > chart.bounds.size.width {
offset.x = chart.bounds.size.width - origin.x - width - padding
}

if (origin.y + offset.y) < 0 {
offset.y = height + padding
} else if let chart = chartView, (origin.y + height + offset.y) > chart.bounds.size.height {
offset.y = chart.bounds.size.height - origin.y - height - padding
}

return CGPoint(x: round(offset.x), y: round(offset.y))
}

open override func draw(context: CGContext, point: CGPoint) {
guard let label = label else {
return
}

let offset = self.offsetForDrawing(atPoint: point)
let size = self.size

var rect = CGRect(
origin: CGPoint(
x: point.x + offset.x,
y: point.y + offset.y),
size: size)
rect.origin.x -= size.width / 2.0
rect.origin.y -= size.height
rect = rect.integral

context.saveGState()
context.setFillColor(color.cgColor)

if offset.y > 0 {
context.beginPath()
context.move(to: CGPoint(
x: rect.origin.x,
y: rect.origin.y + arrowSize.height))
context.addLine(to: CGPoint(
x: rect.origin.x + (rect.size.width - arrowSize.width) / 2.0,
y: rect.origin.y + arrowSize.height))

// Arrow vertex
context.addLine(to: CGPoint(
x: point.x,
y: point.y))
context.addLine(to: CGPoint(
x: rect.origin.x + (rect.size.width + arrowSize.width) / 2.0,
y: rect.origin.y + arrowSize.height))
context.addLine(to: CGPoint(
x: rect.origin.x + rect.size.width,
y: rect.origin.y + arrowSize.height))
context.addLine(to: CGPoint(
x: rect.origin.x + rect.size.width,
y: rect.origin.y + rect.size.height))
context.addLine(to: CGPoint(
x: rect.origin.x,
y: rect.origin.y + rect.size.height))
context.addLine(to: CGPoint(
x: rect.origin.x,
y: rect.origin.y + arrowSize.height))
context.fillPath()
} else {
context.beginPath()
context.move(to: CGPoint(
x: rect.origin.x,
y: rect.origin.y))
context.addLine(to: CGPoint(
x: rect.origin.x + rect.size.width,
y: rect.origin.y))
context.addLine(to: CGPoint(
x: rect.origin.x + rect.size.width,
y: rect.origin.y + rect.size.height - arrowSize.height))
context.addLine(to: CGPoint(
x: rect.origin.x + (rect.size.width + arrowSize.width) / 2.0,
y: rect.origin.y + rect.size.height - arrowSize.height))

//Arrow vertex
context.addLine(to: CGPoint(
x: point.x,
y: point.y))
context.addLine(to: CGPoint(
x: rect.origin.x + (rect.size.width - arrowSize.width) / 2.0,
y: rect.origin.y + rect.size.height - arrowSize.height))
context.addLine(to: CGPoint(
x: rect.origin.x,
y: rect.origin.y + rect.size.height - arrowSize.height))
context.addLine(to: CGPoint(
x: rect.origin.x,
y: rect.origin.y))
context.fillPath()
}

if offset.y > 0 {
rect.origin.y += self.insets.top + arrowSize.height
} else {
rect.origin.y += self.insets.top
}
rect.size.height -= self.insets.top + self.insets.bottom
rect = rect.integral
UIGraphicsPushContext(context)
label.draw(in: rect, withAttributes: _drawAttributes)
UIGraphicsPopContext()
context.restoreGState()
}

open override func refreshContent(entry: ChartDataEntry, highlight: Highlight) {
let hintString = entry.accessibilityValue ?? String(entry.y)
setLabel(hintString)
}

@objc open func setLabel(_ newLabel: String) {
label = newLabel

_drawAttributes.removeAll()
_drawAttributes[.font] = self.font
_drawAttributes[.paragraphStyle] = _paragraphStyle
_drawAttributes[.foregroundColor] = self.textColor
_labelSize = label?.size(withAttributes: _drawAttributes) ?? CGSize.zero

var size = CGSize()
size.width = _labelSize.width + self.insets.left + self.insets.right
size.height = _labelSize.height + self.insets.top + self.insets.bottom
size.width = max(minimumSize.width, size.width)
size.height = max(minimumSize.height, size.height)
self.size = size
}
}


// MARK: - Constants!
Copy link
Collaborator

Choose a reason for hiding this comment

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

⚠️ MARK comment should be in valid format. e.g. ‘// MARK: …’ or ‘// MARK: - …’

//
private extension ChartMarker {
enum Constants {
static let arrowSize = CGSize(width: 20, height: 14)
static let offsetPadding: CGFloat = 4.0
}
}
Loading