Skip to content
Open
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 Modules/Sources/JetpackStats/Analytics/StatsEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ extension DateRangeComparisonPeriod {
switch self {
case .precedingPeriod: "previous_period"
case .samePeriodLastYear: "previous_year"
case .off: "off"
}
}
}
Expand Down
7 changes: 5 additions & 2 deletions Modules/Sources/JetpackStats/Cards/ChartCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,13 @@ struct ChartCard: View {
@ViewBuilder
private var contentView: some View {
VStack(spacing: Constants.step1) {
chartHeaderView
.padding(.trailing, -Constants.step0_5)
if dateRange.comparison != .off || metrics.count == 1 {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We still need to show it for a (pretty rate) scenario where you add a chart and remove all metrics form it except for one.

chartHeaderView
.padding(.trailing, -Constants.step0_5)
}
chartContentView
}
.environment(\.showComparison, dateRange.comparison != .off)
.animation(.spring, value: selectedMetric)
.animation(.spring, value: selectedChartType)
.animation(.easeInOut, value: viewModel.isFirstLoad)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ struct StandaloneChartCard: View {
dateRangeControls
.dynamicTypeSize(...DynamicTypeSize.xLarge)
}
.environment(\.showComparison, dateRange.comparison != .off)
.padding(.vertical, Constants.step2)
.padding(.horizontal, Constants.step3)
.dynamicTypeSize(...DynamicTypeSize.xxLarge)
Expand Down
4 changes: 3 additions & 1 deletion Modules/Sources/JetpackStats/Cards/TopListViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,9 @@ final class TopListViewModel: ObservableObject, TrafficCardViewModel {

// Fetch previous data only for items that support it
async let previousTask: TopListResponse? = {
guard selection.item != .archive else { return nil }
guard selection.item != .archive && dateRange.comparison != .off else {
return nil
}
return try await service.getTopListData(
fetchItem,
metric: selection.metric,
Expand Down
5 changes: 4 additions & 1 deletion Modules/Sources/JetpackStats/Charts/BarChartView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ struct BarChartView: View {

@Environment(\.context) var context
@Environment(\.colorScheme) var colorScheme
@Environment(\.showComparison) private var showComparison

private var valueFormatter: StatsValueFormatter {
StatsValueFormatter(metric: data.metric)
Expand All @@ -23,7 +24,9 @@ struct BarChartView: View {

var body: some View {
Chart {
previousPeriodBars
if showComparison {
previousPeriodBars
}
currentPeriodBars
averageLine
significantPointAnnotations
Expand Down
5 changes: 4 additions & 1 deletion Modules/Sources/JetpackStats/Charts/LineChartView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ struct LineChartView: View {

@Environment(\.colorScheme) var colorScheme
@Environment(\.context) var context
@Environment(\.showComparison) private var showComparison

private var valueFormatter: StatsValueFormatter {
StatsValueFormatter(metric: data.metric)
Expand All @@ -22,7 +23,9 @@ struct LineChartView: View {
var body: some View {
Chart {
currentPeriodMarks
previousPeriodMarks
if showComparison {
previousPeriodMarks
}
averageLine
significantPointAnnotations
selectionIndicatorMarks
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import Foundation

enum DateRangeComparisonPeriod: Equatable, Sendable, CaseIterable, Identifiable {
enum DateRangeComparisonPeriod: String, Equatable, Sendable, CaseIterable, Identifiable {
case precedingPeriod
case samePeriodLastYear
case off

var id: DateRangeComparisonPeriod { self }

var localizedTitle: String {
switch self {
case .precedingPeriod: Strings.DatePicker.precedingPeriod
case .samePeriodLastYear: Strings.DatePicker.samePeriodLastYear
case .off: Strings.DatePicker.comparisonOff
}
}
}
Expand All @@ -26,6 +28,8 @@ extension Calendar {
return dateInterval
}
return DateInterval(start: newStart, end: newEnd)
case .off:
return dateInterval
}
}

Expand Down
23 changes: 22 additions & 1 deletion Modules/Sources/JetpackStats/StatsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ final class StatsViewModel: ObservableObject, CardConfigurationDelegate {
updateViewModelsDateRange()
if !isNavigationStackLocked {
saveSelectedDateRangePreset()
saveSelectedComparisonPeriod()
}
if !dateRange.isAdjacent(to: oldValue) {
clearNavigationStack()
Expand All @@ -30,6 +31,7 @@ final class StatsViewModel: ObservableObject, CardConfigurationDelegate {
private let userDefaults: UserDefaults
private static let configurationKey = "JetpackStatsTrafficConfiguration"
private static let dateRangePresetKey = "JetpackStatsSelectedDateRangePreset"
private static let comparisonPeriodKey = "JetpackStatsComparisonPeriod"
private static let versionKey = "JetpackStatsVersionKey"

init(context: StatsContext, userDefaults: UserDefaults = .standard) {
Expand All @@ -39,7 +41,11 @@ final class StatsViewModel: ObservableObject, CardConfigurationDelegate {
Self.performMigrations(userDefaults: userDefaults, context: context)

let preset = Self.loadDateRangePreset(from: userDefaults)
self.dateRange = context.calendar.makeDateRange(for: preset ?? .last7Days)
let comparison = Self.loadComparisonPeriod(from: userDefaults)
self.dateRange = context.calendar.makeDateRange(
for: preset ?? .last7Days,
comparison: comparison ?? .precedingPeriod
)

let configuraiton = Self.getConfiguration(from: userDefaults)
self.trafficCardConfiguration = configuraiton ?? Self.makeDefaultConfiguration(context: context)
Expand Down Expand Up @@ -314,6 +320,18 @@ final class StatsViewModel: ObservableObject, CardConfigurationDelegate {
return preset
}

private func saveSelectedComparisonPeriod() {
userDefaults.set(dateRange.comparison.rawValue, forKey: Self.comparisonPeriodKey)
}

private static func loadComparisonPeriod(from userDefaults: UserDefaults) -> DateRangeComparisonPeriod? {
guard let rawValue = userDefaults.string(forKey: Self.comparisonPeriodKey),
let comparisonPeriod = DateRangeComparisonPeriod(rawValue: rawValue) else {
return nil
}
return comparisonPeriod
}

// MARK: - Reset Settings

/// Resets all persistently stored settings including card configuration and date range preset
Expand All @@ -324,6 +342,9 @@ final class StatsViewModel: ObservableObject, CardConfigurationDelegate {
// Reset date range preset
userDefaults.removeObject(forKey: Self.dateRangePresetKey)

// Reset comparison period
userDefaults.removeObject(forKey: Self.comparisonPeriodKey)

// Reset date range to default
dateRange = context.calendar.makeDateRange(for: .last7Days)
}
Expand Down
1 change: 1 addition & 0 deletions Modules/Sources/JetpackStats/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ enum Strings {
static let compareWith = AppLocalizedString("jetpackStats.datePicker.compareWith", value: "Compare With…", comment: "Title for comparison menu")
static let precedingPeriod = AppLocalizedString("jetpackStats.datePicker.precedingPeriod", value: "Preceding Period", comment: "Compare with preceding period option")
static let samePeriodLastYear = AppLocalizedString("jetpackStats.datePicker.lastYear", value: "Last Year", comment: "Compare with same period last year option")
static let comparisonOff = AppLocalizedString("jetpackStats.datePicker.comparisonOff", value: "No Comparison", comment: "Option to turn off period comparison")
}

enum DateRangeTips {
Expand Down
12 changes: 12 additions & 0 deletions Modules/Sources/JetpackStats/Utilities/ChartEnvironment.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import SwiftUI

private struct ShowComparisonKey: EnvironmentKey {
static let defaultValue = true
}

extension EnvironmentValues {
var showComparison: Bool {
get { self[ShowComparisonKey.self] }
set { self[ShowComparisonKey.self] = newValue }
}
}
8 changes: 6 additions & 2 deletions Modules/Sources/JetpackStats/Utilities/StatsDateRange.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ struct StatsDateRange: Equatable, Sendable {
/// date period. If nil, uses duration-based navigation.
var component: Calendar.Component

/// The comparison period type. Defaults to `.precedingPeriod` if nil.
/// The comparison period type.
var comparison: DateRangeComparisonPeriod

/// The calculated comparison date range.
Expand Down Expand Up @@ -56,7 +56,11 @@ struct StatsDateRange: Equatable, Sendable {
}

private mutating func refreshEffectiveComparisonPeriodInterval() {
effectiveComparisonInterval = calendar.comparisonRange(for: dateInterval, period: comparison, component: component)
effectiveComparisonInterval = calendar.comparisonRange(
for: dateInterval,
period: comparison == .off ? .precedingPeriod : comparison,
component: component
)
}

// MARK: - Navigation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,6 @@ struct ChartValuesSummaryView: View {
.font(.system(.headline, design: .rounded, weight: .semibold))
.foregroundColor(.primary)
.contentTransition(.numericText())

Text(trend.formattedPreviousValue)
.font(.system(.footnote, design: .rounded))
.foregroundColor(.secondary.opacity(0.75)).tracking(-0.2)
.contentTransition(.numericText())
}

Text(trend.formattedTrend)
Expand Down
18 changes: 13 additions & 5 deletions Modules/Sources/JetpackStats/Views/StatsDateRangePickerMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ struct StatsDateRangePickerMenu: View {
selection.update(preset: preset)
UIImpactFeedbackGenerator(style: .light).impactOccurred()

// Track preset selection
context.tracker?.send(.dateRangePresetSelected, properties: [
"selected_preset": preset.analyticsName
])
Expand All @@ -64,25 +63,34 @@ struct StatsDateRangePickerMenu: View {
ForEach(DateRangeComparisonPeriod.allCases) { period in
Button(action: {
let previousPeriod = selection.comparison
selection.update(comparisonPeriod: period)
withAnimation {
selection.update(comparisonPeriod: period)
}
UIImpactFeedbackGenerator(style: .light).impactOccurred()

// Track comparison period change
context.tracker?.send(.comparisonPeriodChanged, properties: [
"from_period": previousPeriod.analyticsName,
"to_period": period.analyticsName
])
}) {
Text(period.localizedTitle)
Text(formattedComparisonRange(for: period))
if period != .off {
Text(formattedComparisonRange(for: period))
}
if selection.comparison == period {
Image(systemName: "checkmark")
}
}
.lineLimit(1)
}
} label: {
Label(Strings.DatePicker.compareWith, systemImage: "arrow.left.arrow.right")
Button(action: {}) {
Image(systemName: "arrow.up.right")
Text(Strings.DatePicker.compareWith)
if selection.comparison != .off {
Text(selection.comparison.localizedTitle)
}
}
}
}

Expand Down
1 change: 1 addition & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* [*] Fix previewing posts on WordPress.com atomic sites [#25045]
* [**] Stats: Add Country/Region/City picker to the Locations card [#25125]
* [*] Stats: Rename "External Links" to "Clicks" to be consistent with the web [#25090]
* [*] Stats: Add an option to disable automatic comparisons and remember the selection across launches [#25143]
* [*] Stats: Add long-press support for bars in a "Bar Chart" view [#25133]
* [*] Stats: Fix an issue where bar chart would prevent vertical scroll [#25133]
* [*] Stats: Fix an issue with External Links (Clicks) sometimes displayed incorrectly [#25128]
Expand Down