diff --git a/InputMetrics/InputMetrics/Models/DailySummary.swift b/InputMetrics/InputMetrics/Models/DailySummary.swift index 94929ac..1252279 100644 --- a/InputMetrics/InputMetrics/Models/DailySummary.swift +++ b/InputMetrics/InputMetrics/Models/DailySummary.swift @@ -52,4 +52,24 @@ struct DailySummary: Codable, FetchableRecord, PersistableRecord { static let peakMouseSpeed = Column(CodingKeys.peakMouseSpeed) static let peakWPM = Column(CodingKeys.peakWPM) } + + static func zero(for date: String) -> DailySummary { + DailySummary(date: date, mouseDistancePx: 0, mouseClicksLeft: 0, mouseClicksRight: 0, mouseClicksMiddle: 0, keystrokes: 0, scrollDistanceVertical: 0, scrollDistanceHorizontal: 0) + } +} + +extension [DailySummary] { + func fillingMissingDays(from start: Date, to end: Date) -> [DailySummary] { + let calendar = Calendar.current + let formatter = DateHelper.self + let existing = Dictionary(uniqueKeysWithValues: self.map { ($0.date, $0) }) + var result: [DailySummary] = [] + var current = start + while current <= end { + let key = formatter.string(from: current) + result.append(existing[key] ?? .zero(for: key)) + current = calendar.date(byAdding: .day, value: 1, to: current) ?? current + } + return result + } } diff --git a/InputMetrics/InputMetrics/ViewModels/KeyboardStatsViewModel.swift b/InputMetrics/InputMetrics/ViewModels/KeyboardStatsViewModel.swift index a7466f4..e48df16 100644 --- a/InputMetrics/InputMetrics/ViewModels/KeyboardStatsViewModel.swift +++ b/InputMetrics/InputMetrics/ViewModels/KeyboardStatsViewModel.swift @@ -86,6 +86,10 @@ final class KeyboardStatsViewModel { } } + if selectedRange == .week || selectedRange == .month { + chartData = chartData.fillingMissingDays(from: startDate, to: selectedDate) + } + if selectedRange == .year { chartData = aggregateByWeek(chartData) } diff --git a/InputMetrics/InputMetrics/ViewModels/MouseStatsViewModel.swift b/InputMetrics/InputMetrics/ViewModels/MouseStatsViewModel.swift index 0dcd70e..ccc34ea 100644 --- a/InputMetrics/InputMetrics/ViewModels/MouseStatsViewModel.swift +++ b/InputMetrics/InputMetrics/ViewModels/MouseStatsViewModel.swift @@ -81,6 +81,10 @@ final class MouseStatsViewModel { } } + if selectedRange == .week || selectedRange == .month { + chartData = chartData.fillingMissingDays(from: startDate, to: selectedDate) + } + if selectedRange == .year { chartData = aggregateByWeek(chartData) } diff --git a/InputMetrics/InputMetrics/Views/ChartView.swift b/InputMetrics/InputMetrics/Views/ChartView.swift index 56b9769..f27a6bd 100644 --- a/InputMetrics/InputMetrics/Views/ChartView.swift +++ b/InputMetrics/InputMetrics/Views/ChartView.swift @@ -26,32 +26,31 @@ struct ChartView: View { .frame(maxWidth: .infinity, maxHeight: .infinity) } else { Chart(data, id: \.date) { item in - let label = formatLabel(from: item.date) let value = metricValue(for: item) - LineMark( - x: .value("Date", label), + AreaMark( + x: .value("Date", item.date), y: .value("Value", value) ) - .foregroundStyle(Color.blue) + .foregroundStyle(Color.blue.opacity(0.15)) .interpolationMethod(.catmullRom) - AreaMark( - x: .value("Date", label), + LineMark( + x: .value("Date", item.date), y: .value("Value", value) ) - .foregroundStyle(Color.blue.opacity(0.1)) + .foregroundStyle(Color.blue) .interpolationMethod(.catmullRom) PointMark( - x: .value("Date", label), + x: .value("Date", item.date), y: .value("Value", value) ) .foregroundStyle(Color.blue) .symbolSize(30) - if hoveredLabel == label { - RuleMark(x: .value("Date", label)) + if hoveredLabel == item.date { + RuleMark(x: .value("Date", item.date)) .foregroundStyle(Color.secondary.opacity(0.3)) .annotation(position: .top, spacing: 4) { Text(formattedTooltip(value: value)) @@ -63,6 +62,18 @@ struct ChartView: View { } } } + .chartLegend(.hidden) + .chartXAxis { + AxisMarks { value in + AxisValueLabel { + if let dateStr = value.as(String.self) { + Text(formatLabel(from: dateStr)) + } + } + AxisGridLine() + AxisTick() + } + } .accessibilityElement(children: .ignore) .accessibilityLabel("\(chartTitle) chart for \(range == .week ? "this week" : range == .month ? "this month" : "this year")") .chartYAxisLabel(yAxisLabel)