improve: enhance dashboard styling and metrics - Add Words/Minute and Words/Session metrics, improve card designs, update TimeEfficiencyView, ensure consistent styling

This commit is contained in:
Beingpax 2025-02-27 17:15:13 +05:45
parent 49aacc8cd6
commit 09b73a350c
5 changed files with 85 additions and 54 deletions

View File

@ -3,15 +3,31 @@ import SwiftUI
struct MetricCard: View {
let title: String
let value: String
let icon: String
let color: Color
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Text(title)
.font(.headline)
.foregroundColor(.secondary)
Text(value)
.font(.title)
.fontWeight(.bold)
VStack(alignment: .leading, spacing: 16) {
HStack(spacing: 12) {
// Icon
Image(systemName: icon)
.font(.system(size: 24))
.foregroundColor(color)
.frame(width: 32, height: 32)
.background(
Circle()
.fill(color.opacity(0.1))
)
VStack(alignment: .leading, spacing: 8) {
Text(title)
.font(.subheadline)
.foregroundColor(.secondary)
Text(value)
.font(.system(size: 24, weight: .bold, design: .rounded))
.foregroundColor(.primary)
}
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding()

View File

@ -38,8 +38,30 @@ struct MetricsContent: View {
private var metricsGrid: some View {
LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 20) {
MetricCard(title: "Words Captured", value: "\(totalWordsTranscribed)")
MetricCard(title: "Voice-to-Text Sessions", value: "\(transcriptions.count)")
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
)
}
}
@ -117,4 +139,15 @@ struct MetricsContent: View {
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)
}
}

View File

@ -45,8 +45,9 @@ struct TimeEfficiencyView: View {
bottomSection
}
.padding(.vertical, 24)
.background(backgroundDesign)
.overlay(borderOverlay)
.background(Color(.controlBackgroundColor))
.cornerRadius(10)
.shadow(radius: 2)
}
// MARK: - Subviews
@ -154,34 +155,6 @@ struct TimeEfficiencyView: View {
)
}
}
// Extension to allow hex color initialization
// MARK: - Styling Views
private var backgroundDesign: some View {
RoundedRectangle(cornerRadius: 12)
.fill(Color(nsColor: .controlBackgroundColor))
}
private var borderOverlay: some View {
RoundedRectangle(cornerRadius: 12)
.stroke(
.linearGradient(
colors: [
Color(nsColor: .controlAccentColor).opacity(0.2),
Color.clear,
Color.clear,
Color(nsColor: .controlAccentColor).opacity(0.1)
],
startPoint: .topLeading,
endPoint: .bottomTrailing
),
lineWidth: 1
)
}
private var efficiencyGradient: LinearGradient {
LinearGradient(

View File

@ -43,7 +43,7 @@ struct MetricsView: View {
}
}
}
.background(Color(.windowBackgroundColor))
.background(Color(.controlBackgroundColor))
.task {
// Ensure the model context is ready
hasLoadedData = true

View File

@ -661,22 +661,31 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate {
await toggleRecord()
}
} else {
// Start a parallel task for both UI and recording
// Serialize audio operations to prevent deadlocks
Task {
// Play start sound first
SoundManager.shared.playStartSound()
// Start audio engine immediately - this can happen in parallel
audioEngine.startAudioEngine()
// Show UI (this is quick now that we removed animations)
await MainActor.run {
showRecorderPanel() // Modified version that doesn't start audio engine
isMiniRecorderVisible = true
do {
// First start the audio engine
await MainActor.run {
audioEngine.startAudioEngine()
}
// Small delay to ensure audio system is ready
try await Task.sleep(nanoseconds: 50_000_000) // 50ms
// Now play the sound
SoundManager.shared.playStartSound()
// Show UI
await MainActor.run {
showRecorderPanel()
isMiniRecorderVisible = true
}
// Finally start recording
await toggleRecord()
} catch {
logger.error("Error during recorder initialization: \(error)")
}
// Start recording (this will happen in parallel with UI showing)
await toggleRecord()
}
}
}