Skip to content

Commit 23976f5

Browse files
authored
Merge pull request #18509 from wordpress-mobile/feature/stats-revamp-v2-views-visitors
Stats Revamp: Views Visitors Card and Graph
2 parents 8c4b3ca + b49ab1e commit 23976f5

20 files changed

+2157
-15
lines changed

WordPress/Classes/Stores/StatsStore+Cache.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ extension StatsStoreCacheable {
1414
extension StatsInsightsStore: StatsStoreCacheable {
1515
func containsCachedData(for type: InsightType) -> Bool {
1616
switch type {
17+
case .viewsVisitors:
18+
return true
1719
case .latestPostSummary:
1820
return state.lastPostInsight != nil
1921
case .allTimeStats, .growAudience:

WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ import Foundation
109109
case statsReaderDiscoverNudgeTapped
110110
case statsReaderDiscoverNudgeDismissed
111111
case statsReaderDiscoverNudgeCompleted
112+
case statsLineChartTapped
112113

113114
// Stats - Insights
114115
case statsCustomizeInsightsShown
@@ -541,6 +542,8 @@ import Foundation
541542
return "stats_reader_discover_nudge_dismissed"
542543
case .statsReaderDiscoverNudgeCompleted:
543544
return "stats_reader_discover_nudge_completed"
545+
case .statsLineChartTapped:
546+
return "stats_line_chart_tapped"
544547

545548
// Stats - Insights
546549
case .statsCustomizeInsightsShown:

WordPress/Classes/ViewRelated/Stats/Charts/Charts+Support.swift

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,30 @@ protocol BarChartStyling {
5454
var yAxisValueFormatter: IAxisValueFormatter { get }
5555
}
5656

57+
protocol LineChartStyling {
58+
59+
/// This corresponds to the primary bar color.
60+
var primaryLineColor: UIColor { get }
61+
62+
/// This bar color is used if bars are overlayed.
63+
var secondaryLineColor: UIColor? { get }
64+
65+
/// This corresponds to the color of a single selected point
66+
var primaryHighlightColor: UIColor? { get }
67+
68+
/// This corresponds to the color of axis labels on the chart
69+
var labelColor: UIColor { get }
70+
71+
/// If specified, a legend will be presented with this value. It maps to the secondary bar color above.
72+
var legendTitle: String? { get }
73+
74+
/// This corresponds to the color of axis and grid lines on the chart
75+
var lineColor: UIColor { get }
76+
77+
/// Formatter for y-axis values
78+
var yAxisValueFormatter: IAxisValueFormatter { get }
79+
}
80+
5781
/// Transforms a given data set for consumption by BarChartView in the Charts framework.
5882
///
5983
protocol BarChartDataConvertible {
@@ -65,6 +89,17 @@ protocol BarChartDataConvertible {
6589
var barChartData: BarChartData { get }
6690
}
6791

92+
/// Transforms a given data set for consumption by LineChartView in the Charts framework.
93+
///
94+
protocol LineChartDataConvertible {
95+
96+
/// Describe the chart for VoiceOver usage
97+
var accessibilityDescription: String { get }
98+
99+
/// Adapts the original data format for consumption by the Charts framework.
100+
var lineChartData: LineChartData { get }
101+
}
102+
68103
// MARK: - Charts & analytics
69104

70105
/// Vends property values for analytics events that use granularity.
@@ -73,6 +108,10 @@ enum BarChartAnalyticsPropertyGranularityValue: String, CaseIterable {
73108
case days, weeks, months, years
74109
}
75110

111+
enum LineChartAnalyticsPropertyGranularityValue: String, CaseIterable {
112+
case days, weeks, months, years
113+
}
114+
76115
extension StatsPeriodUnit {
77116
var analyticsGranularity: BarChartAnalyticsPropertyGranularityValue {
78117
switch self {
@@ -86,4 +125,17 @@ extension StatsPeriodUnit {
86125
return .years
87126
}
88127
}
128+
129+
var analyticsGranularityLine: LineChartAnalyticsPropertyGranularityValue {
130+
switch self {
131+
case .day:
132+
return .days
133+
case .week:
134+
return .weeks
135+
case .month:
136+
return .months
137+
case .year:
138+
return .years
139+
}
140+
}
89141
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import Foundation
2+
import Charts
3+
import Kanvas
4+
5+
// MARK: - StatsInsightsFilterDimension
6+
7+
enum StatsInsightsFilterDimension: Int, CaseIterable {
8+
case views = 0, visitors
9+
}
10+
11+
extension StatsInsightsFilterDimension {
12+
var accessibleDescription: String {
13+
switch self {
14+
case .views:
15+
return NSLocalizedString("Line Chart depicting Views for insights.", comment: "This description is used to set the accessibility label for the Insights chart, with Views selected.")
16+
case .visitors:
17+
return NSLocalizedString("Line Chart depicting Visitors for insights.", comment: "This description is used to set the accessibility label for the Insights chart, with Visitors selected.")
18+
}
19+
}
20+
21+
var analyticsProperty: String {
22+
switch self {
23+
case .views:
24+
return "views"
25+
case .visitors:
26+
return "visitors"
27+
}
28+
}
29+
}
30+
31+
// MARK: - InsightsLineChart
32+
33+
class InsightsLineChart {
34+
35+
private let rawChartData: [StatsSummaryTimeIntervalDataAsAWeek]
36+
private var filterDimension: StatsInsightsFilterDimension
37+
38+
private(set) var lineChartData: [LineChartDataConvertible] = []
39+
private(set) var lineChartStyling: [LineChartStyling] = []
40+
41+
init(data: [StatsSummaryTimeIntervalDataAsAWeek], filterDimension: StatsInsightsFilterDimension = .views) {
42+
rawChartData = data
43+
self.filterDimension = filterDimension
44+
45+
let (data, styling) = transform()
46+
47+
lineChartData = data
48+
lineChartStyling = styling
49+
}
50+
51+
private static let dataSetValueFormatter = DefaultValueFormatter(decimals: 0)
52+
53+
/// Transforms the raw data into the line chart data and styling.
54+
/// similar to PeriodChart transform
55+
/// - Returns: A tuple containing the line chart data and styling.
56+
func transform() -> (lineChartData: [LineChartDataConvertible], lineChartStyling: [LineChartStyling]) {
57+
var thisWeekEntries = [ChartDataEntry]()
58+
var prevWeekEntries = [ChartDataEntry]()
59+
60+
switch filterDimension {
61+
case .views:
62+
(thisWeekEntries, prevWeekEntries) = filterData(path: \StatsSummaryData.viewsCount)
63+
case .visitors:
64+
(thisWeekEntries, prevWeekEntries) = filterData(path: \StatsSummaryData.visitorsCount)
65+
}
66+
67+
var chartData = createLineChartData(thisWeekEntries: thisWeekEntries, prevWeekEntries: prevWeekEntries)
68+
let lineChartDataConvertibles = createLineChartDataConvertibles(chartData: chartData)
69+
70+
let chartStyling: [LineChartStyling] = [
71+
ViewsInsightsLineChartStyling(primaryLineColor: Constants.primaryLineColorViews,
72+
secondaryLineColor: Constants.secondaryLineColor,
73+
primaryHighlightColor: Constants.primaryHighlightColor),
74+
VisitorsInsightsLineChartStyling(primaryLineColor: Constants.primaryLineColorVisitors,
75+
secondaryLineColor: Constants.secondaryLineColor,
76+
primaryHighlightColor: Constants.primaryHighlightColor),
77+
]
78+
79+
return (lineChartDataConvertibles, chartStyling)
80+
}
81+
82+
func createLineChartData(thisWeekEntries: [ChartDataEntry], prevWeekEntries: [ChartDataEntry]) -> [LineChartData] {
83+
var chartData = [LineChartData]()
84+
85+
let thisWeekDataSet = LineChartDataSet(entries: thisWeekEntries,
86+
label: NSLocalizedString("This Week", comment: "Accessibility label used for distinguishing Views and Visitors in the Stats → Insights Views Visitors Line chart."))
87+
let prevWeekDataSet = LineChartDataSet(entries: prevWeekEntries,
88+
label: NSLocalizedString("Previous Week", comment: "Accessibility label used for distinguishing Views and Visitors in the Stats → Insights Views Visitors Line chart."))
89+
let viewsDataSets = [ thisWeekDataSet, prevWeekDataSet ]
90+
let viewsChartData = LineChartData(dataSets: viewsDataSets)
91+
chartData.append(viewsChartData)
92+
93+
return chartData
94+
}
95+
96+
func createLineChartDataConvertibles(chartData: [LineChartData]) -> [LineChartDataConvertible] {
97+
var lineChartDataConvertibles = [LineChartDataConvertible]()
98+
99+
for filterDimension in StatsInsightsFilterDimension.allCases {
100+
let filterIndex = filterDimension.rawValue
101+
102+
let accessibleDescription = filterDimension.accessibleDescription
103+
let data = chartData[filterIndex]
104+
let insightsChartData = InsightsLineChartData(accessibilityDescription: accessibleDescription, lineChartData: data)
105+
106+
lineChartDataConvertibles.append(insightsChartData)
107+
break
108+
}
109+
110+
return lineChartDataConvertibles
111+
}
112+
113+
func filterData(path: KeyPath<StatsSummaryData, Int>) -> (thisWeekEntries: [ChartDataEntry], prevWeekEntries: [ChartDataEntry]) {
114+
var thisWeekEntries = [ChartDataEntry]()
115+
var prevWeekEntries = [ChartDataEntry]()
116+
117+
rawChartData.forEach { statsSummaryTimeIntervalDataAsAWeek in
118+
switch statsSummaryTimeIntervalDataAsAWeek {
119+
case .thisWeek(let data):
120+
for (index, statsSummaryData) in data.summaryData.enumerated() {
121+
thisWeekEntries.append(ChartDataEntry(x: Double(index), y: Double(statsSummaryData[keyPath: path])))
122+
}
123+
case .prevWeek(let data):
124+
for (index, statsSummaryData) in data.summaryData.enumerated() {
125+
prevWeekEntries.append(ChartDataEntry(x: Double(index), y: Double(statsSummaryData[keyPath: path])))
126+
}
127+
}
128+
}
129+
130+
return (thisWeekEntries: thisWeekEntries, prevWeekEntries: prevWeekEntries)
131+
}
132+
133+
func primaryLineColor(forFilterDimension filterDimension: StatsInsightsFilterDimension) -> UIColor {
134+
switch filterDimension {
135+
case .views:
136+
return UIColor(light: .muriel(name: .blue, .shade50), dark: .muriel(name: .blue, .shade50))
137+
case .visitors:
138+
return UIColor(light: .muriel(name: .purple, .shade50), dark: .muriel(name: .purple, .shade50))
139+
}
140+
}
141+
}
142+
143+
private extension InsightsLineChart {
144+
enum Constants {
145+
static let primaryHighlightColor: UIColor = UIColor(red: 209.0/255.0, green: 209.0/255.0, blue: 214.0/255.0, alpha: 1.0)
146+
static let secondaryLineColor: UIColor = UIColor(light: .textQuaternary, dark: .textTertiary)
147+
static let primaryLineColorViews: UIColor = UIColor(light: .muriel(name: .blue, .shade50), dark: .muriel(name: .blue, .shade50))
148+
static let primaryLineColorVisitors: UIColor = UIColor(light: .muriel(name: .purple, .shade50), dark: .muriel(name: .purple, .shade50))
149+
}
150+
}
151+
152+
// MARK: - InsightsLineChartData
153+
154+
private struct InsightsLineChartData: LineChartDataConvertible {
155+
let accessibilityDescription: String
156+
let lineChartData: LineChartData
157+
}
158+
159+
// MARK: - ViewsInsightsLineChartStyling
160+
161+
private struct ViewsInsightsLineChartStyling: LineChartStyling {
162+
let primaryLineColor: UIColor
163+
let secondaryLineColor: UIColor?
164+
let primaryHighlightColor: UIColor?
165+
let labelColor: UIColor = UIColor(light: .secondaryLabel, dark: .tertiaryLabel)
166+
let legendColor: UIColor? = .primary(.shade60)
167+
let legendTitle: String? = NSLocalizedString("Views", comment: "Title for Views count in the legend of the Stats Insights views and visitors line chart")
168+
let lineColor: UIColor = .neutral(.shade5)
169+
let yAxisValueFormatter: IAxisValueFormatter = VerticalAxisFormatter()
170+
}
171+
172+
// MARK: - VisitorsInsightsLineChartStyling
173+
174+
private struct VisitorsInsightsLineChartStyling: LineChartStyling {
175+
let primaryLineColor: UIColor
176+
let secondaryLineColor: UIColor?
177+
let primaryHighlightColor: UIColor?
178+
let labelColor: UIColor = UIColor(light: .secondaryLabel, dark: .tertiaryLabel)
179+
let legendColor: UIColor? = .primary(.shade60)
180+
let legendTitle: String? = NSLocalizedString("Visitors", comment: "Title for Visitors count in the legend of the Stats Insights views and visitors line chart")
181+
let lineColor: UIColor = .neutral(.shade5)
182+
let yAxisValueFormatter: IAxisValueFormatter = VerticalAxisFormatter()
183+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
struct StatsLineChartConfiguration {
3+
let data: LineChartDataConvertible
4+
let styling: LineChartStyling
5+
let analyticsGranularity: LineChartAnalyticsPropertyGranularityValue?
6+
let indexToHighlight: Int?
7+
let xAxisDates: [Date]
8+
}

0 commit comments

Comments
 (0)