vOOice/VoiceInk/Views/Metrics/MetricsContent.swift
2025-03-22 15:52:06 +05:45

154 lines
5.4 KiB
Swift

import SwiftUI
import Charts
struct MetricsContent: View {
let transcriptions: [Transcription]
var body: some View {
if transcriptions.isEmpty {
emptyStateView
} else {
ScrollView {
VStack(spacing: 20) {
TimeEfficiencyView(totalRecordedTime: totalRecordedTime, estimatedTypingTime: estimatedTypingTime)
metricsGrid
voiceInkTrendChart
}
.padding()
}
}
}
private var emptyStateView: some View {
VStack(spacing: 20) {
Image(systemName: "waveform")
.font(.system(size: 50))
.foregroundColor(.secondary)
Text("No Transcriptions Yet")
.font(.title2)
.fontWeight(.semibold)
Text("Start recording to see your metrics")
.foregroundColor(.secondary)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(.windowBackgroundColor))
}
private var metricsGrid: some View {
LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 20) {
MetricCard(
title: "Words Captured",
value: "\(totalWordsTranscribed)",
icon: "text.word.spacing",
color: .blue
)
MetricCard(
title: "Voice-to-Text Sessions",
value: "\(transcriptions.count)",
icon: "mic.circle.fill",
color: .green
)
MetricCard(
title: "Average Words/Minute",
value: String(format: "%.1f", averageWordsPerMinute),
icon: "speedometer",
color: .orange
)
MetricCard(
title: "Words/Session",
value: String(format: "%.1f", averageWordsPerSession),
icon: "chart.bar.fill",
color: .purple
)
}
}
private var voiceInkTrendChart: some View {
VStack(alignment: .leading, spacing: 10) {
Text("30-Day VoiceInk Trend")
.font(.headline)
Chart {
ForEach(dailyTranscriptionCounts, id: \.date) { item in
LineMark(
x: .value("Date", item.date),
y: .value("Sessions", item.count)
)
.interpolationMethod(.catmullRom)
AreaMark(
x: .value("Date", item.date),
y: .value("Sessions", item.count)
)
.foregroundStyle(LinearGradient(colors: [.blue.opacity(0.3), .blue.opacity(0.1)], startPoint: .top, endPoint: .bottom))
.interpolationMethod(.catmullRom)
}
}
.chartXAxis {
AxisMarks(values: .stride(by: .day, count: 7)) { _ in
AxisGridLine()
AxisTick()
AxisValueLabel(format: .dateTime.day().month(), centered: true)
}
}
.chartYAxis {
AxisMarks { value in
AxisGridLine()
AxisTick()
AxisValueLabel()
}
}
.frame(height: 250)
}
.padding()
.background(Color(.controlBackgroundColor))
.cornerRadius(10)
.shadow(radius: 2)
}
// Computed properties for metrics
private var totalWordsTranscribed: Int {
transcriptions.reduce(0) { $0 + $1.text.split(separator: " ").count }
}
private var totalRecordedTime: TimeInterval {
transcriptions.reduce(0) { $0 + $1.duration }
}
private var estimatedTypingTime: TimeInterval {
let averageTypingSpeed: Double = 35 // words per minute
let totalWords = Double(totalWordsTranscribed)
let estimatedTypingTimeInMinutes = totalWords / averageTypingSpeed
return estimatedTypingTimeInMinutes * 60
}
private var dailyTranscriptionCounts: [(date: Date, count: Int)] {
let calendar = Calendar.current
let now = Date()
let thirtyDaysAgo = calendar.date(byAdding: .day, value: -29, to: now)!
let dailyData = (0..<30).compactMap { dayOffset -> (date: Date, count: Int)? in
guard let date = calendar.date(byAdding: .day, value: -dayOffset, to: now) else { return nil }
let startOfDay = calendar.startOfDay(for: date)
let endOfDay = calendar.date(byAdding: .day, value: 1, to: startOfDay)!
let count = transcriptions.filter { $0.timestamp >= startOfDay && $0.timestamp < endOfDay }.count
return (date: startOfDay, count: count)
}
return dailyData.reversed()
}
// Add computed properties for new metrics
private var averageWordsPerMinute: Double {
guard totalRecordedTime > 0 else { return 0 }
return Double(totalWordsTranscribed) / (totalRecordedTime / 60.0)
}
private var averageWordsPerSession: Double {
guard !transcriptions.isEmpty else { return 0 }
return Double(totalWordsTranscribed) / Double(transcriptions.count)
}
}