Fix theme selection bug and update onboarding with AppTheme picker
- Change Theme enum from Int to String raw values to fix theme selection bug - Replace OnboardingStyle icon/color pickers with unified AppTheme grid - Remove visible text labels from voting layouts while keeping accessibility labels (WCAG 2.1 AA compliant) - Update widget voting views to use icons only with proper accessibility support - Consolidate app icons to single unified icon set 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -146,27 +146,21 @@ struct CardVotingView: View {
|
||||
LazyVGrid(columns: columns, spacing: 12) {
|
||||
ForEach(Mood.allValues) { mood in
|
||||
Button(action: { onMoodSelected(mood) }) {
|
||||
VStack(spacing: 8) {
|
||||
mood.icon
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 40, height: 40)
|
||||
.foregroundColor(moodTint.color(forMood: mood))
|
||||
|
||||
Text(mood.strValue)
|
||||
.font(.caption.weight(.medium))
|
||||
.foregroundColor(moodTint.color(forMood: mood))
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 16)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.fill(moodTint.color(forMood: mood).opacity(0.15))
|
||||
)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.stroke(moodTint.color(forMood: mood).opacity(0.3), lineWidth: 1)
|
||||
)
|
||||
mood.icon
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 40, height: 40)
|
||||
.foregroundColor(moodTint.color(forMood: mood))
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 20)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.fill(moodTint.color(forMood: mood).opacity(0.15))
|
||||
)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.stroke(moodTint.color(forMood: mood).opacity(0.3), lineWidth: 1)
|
||||
)
|
||||
}
|
||||
.buttonStyle(CardButtonStyle())
|
||||
.accessibilityLabel(mood.strValue)
|
||||
@@ -195,22 +189,16 @@ struct RadialVotingView: View {
|
||||
let position = positionForAngle(angle, radius: radius, center: center)
|
||||
|
||||
Button(action: { onMoodSelected(mood) }) {
|
||||
VStack(spacing: 4) {
|
||||
mood.icon
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 44, height: 44)
|
||||
.foregroundColor(moodTint.color(forMood: mood))
|
||||
|
||||
Text(mood.strValue)
|
||||
.font(.caption2.weight(.medium))
|
||||
.foregroundColor(moodTint.color(forMood: mood))
|
||||
}
|
||||
.padding(8)
|
||||
.background(
|
||||
Circle()
|
||||
.fill(moodTint.color(forMood: mood).opacity(0.1))
|
||||
)
|
||||
mood.icon
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 44, height: 44)
|
||||
.foregroundColor(moodTint.color(forMood: mood))
|
||||
.padding(12)
|
||||
.background(
|
||||
Circle()
|
||||
.fill(moodTint.color(forMood: mood).opacity(0.1))
|
||||
)
|
||||
}
|
||||
.buttonStyle(MoodButtonStyle())
|
||||
.position(position)
|
||||
@@ -312,59 +300,51 @@ struct AuraVotingView: View {
|
||||
let color = moodTint.color(forMood: mood)
|
||||
|
||||
return Button(action: { onMoodSelected(mood) }) {
|
||||
VStack(spacing: 10) {
|
||||
// Glowing orb
|
||||
ZStack {
|
||||
// Outer atmospheric glow
|
||||
Circle()
|
||||
.fill(
|
||||
RadialGradient(
|
||||
colors: [
|
||||
color.opacity(0.5),
|
||||
color.opacity(0.2),
|
||||
Color.clear
|
||||
],
|
||||
center: .center,
|
||||
startRadius: 0,
|
||||
endRadius: 45
|
||||
)
|
||||
// Glowing orb
|
||||
ZStack {
|
||||
// Outer atmospheric glow
|
||||
Circle()
|
||||
.fill(
|
||||
RadialGradient(
|
||||
colors: [
|
||||
color.opacity(0.5),
|
||||
color.opacity(0.2),
|
||||
Color.clear
|
||||
],
|
||||
center: .center,
|
||||
startRadius: 0,
|
||||
endRadius: 45
|
||||
)
|
||||
.frame(width: 90, height: 90)
|
||||
)
|
||||
.frame(width: 90, height: 90)
|
||||
|
||||
// Middle glow ring
|
||||
Circle()
|
||||
.fill(
|
||||
RadialGradient(
|
||||
colors: [
|
||||
color.opacity(0.8),
|
||||
color.opacity(0.4)
|
||||
],
|
||||
center: .center,
|
||||
startRadius: 10,
|
||||
endRadius: 30
|
||||
)
|
||||
// Middle glow ring
|
||||
Circle()
|
||||
.fill(
|
||||
RadialGradient(
|
||||
colors: [
|
||||
color.opacity(0.8),
|
||||
color.opacity(0.4)
|
||||
],
|
||||
center: .center,
|
||||
startRadius: 10,
|
||||
endRadius: 30
|
||||
)
|
||||
.frame(width: 60, height: 60)
|
||||
)
|
||||
.frame(width: 60, height: 60)
|
||||
|
||||
// Inner solid core
|
||||
Circle()
|
||||
.fill(color)
|
||||
.frame(width: 48, height: 48)
|
||||
.shadow(color: color.opacity(0.8), radius: 12, x: 0, y: 0)
|
||||
// Inner solid core
|
||||
Circle()
|
||||
.fill(color)
|
||||
.frame(width: 48, height: 48)
|
||||
.shadow(color: color.opacity(0.8), radius: 12, x: 0, y: 0)
|
||||
|
||||
// Icon
|
||||
mood.icon
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 26, height: 26)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
|
||||
// Label with elegant typography
|
||||
Text(mood.strValue)
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundColor(color)
|
||||
.tracking(0.5)
|
||||
// Icon
|
||||
mood.icon
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 26, height: 26)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
}
|
||||
.buttonStyle(AuraButtonStyle(color: color))
|
||||
@@ -687,66 +667,60 @@ struct NeonEqualizerBar: View {
|
||||
|
||||
var body: some View {
|
||||
Button(action: onTap) {
|
||||
VStack(spacing: 8) {
|
||||
// The equalizer bar
|
||||
ZStack(alignment: .bottom) {
|
||||
// Glow background
|
||||
RoundedRectangle(cornerRadius: 6)
|
||||
.fill(barColor.opacity(pulsePhase ? 0.15 : 0.08))
|
||||
.frame(height: barHeight + 20)
|
||||
.blur(radius: 15)
|
||||
// The equalizer bar
|
||||
ZStack(alignment: .bottom) {
|
||||
// Glow background
|
||||
RoundedRectangle(cornerRadius: 6)
|
||||
.fill(barColor.opacity(pulsePhase ? 0.15 : 0.08))
|
||||
.frame(height: barHeight + 20)
|
||||
.blur(radius: 15)
|
||||
|
||||
// Main bar
|
||||
RoundedRectangle(cornerRadius: 6)
|
||||
.fill(
|
||||
LinearGradient(
|
||||
colors: [
|
||||
barColor,
|
||||
barColor.opacity(0.7)
|
||||
],
|
||||
startPoint: .top,
|
||||
endPoint: .bottom
|
||||
)
|
||||
// Main bar
|
||||
RoundedRectangle(cornerRadius: 6)
|
||||
.fill(
|
||||
LinearGradient(
|
||||
colors: [
|
||||
barColor,
|
||||
barColor.opacity(0.7)
|
||||
],
|
||||
startPoint: .top,
|
||||
endPoint: .bottom
|
||||
)
|
||||
.frame(height: isPressed ? barHeight * 0.9 : barHeight)
|
||||
.shadow(color: barColor.opacity(0.8), radius: pulsePhase ? 12 : 8, x: 0, y: 0)
|
||||
.shadow(color: barColor.opacity(0.4), radius: pulsePhase ? 20 : 15, x: 0, y: 5)
|
||||
)
|
||||
.frame(height: isPressed ? barHeight * 0.9 : barHeight)
|
||||
.shadow(color: barColor.opacity(0.8), radius: pulsePhase ? 12 : 8, x: 0, y: 0)
|
||||
.shadow(color: barColor.opacity(0.4), radius: pulsePhase ? 20 : 15, x: 0, y: 5)
|
||||
|
||||
// Top highlight
|
||||
RoundedRectangle(cornerRadius: 6)
|
||||
.fill(
|
||||
LinearGradient(
|
||||
colors: [Color.white.opacity(0.5), Color.clear],
|
||||
startPoint: .top,
|
||||
endPoint: .center
|
||||
)
|
||||
// Top highlight
|
||||
RoundedRectangle(cornerRadius: 6)
|
||||
.fill(
|
||||
LinearGradient(
|
||||
colors: [Color.white.opacity(0.5), Color.clear],
|
||||
startPoint: .top,
|
||||
endPoint: .center
|
||||
)
|
||||
.frame(height: isPressed ? barHeight * 0.9 : barHeight)
|
||||
)
|
||||
.frame(height: isPressed ? barHeight * 0.9 : barHeight)
|
||||
|
||||
// Level indicators (horizontal lines)
|
||||
VStack(spacing: 8) {
|
||||
ForEach(0..<Int(barHeight / 15), id: \.self) { _ in
|
||||
Rectangle()
|
||||
.fill(Color.black.opacity(0.3))
|
||||
.frame(height: 2)
|
||||
}
|
||||
// Level indicators (horizontal lines)
|
||||
VStack(spacing: 8) {
|
||||
ForEach(0..<Int(barHeight / 15), id: \.self) { _ in
|
||||
Rectangle()
|
||||
.fill(Color.black.opacity(0.3))
|
||||
.frame(height: 2)
|
||||
}
|
||||
.frame(height: isPressed ? barHeight * 0.9 - 10 : barHeight - 10)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 4))
|
||||
.padding(.horizontal, 4)
|
||||
.padding(.bottom, 5)
|
||||
}
|
||||
.frame(maxHeight: 160, alignment: .bottom)
|
||||
|
||||
// Mood label
|
||||
Text(mood.shortLabel)
|
||||
.font(.system(size: 10, weight: .bold, design: .monospaced))
|
||||
.foregroundColor(barColor)
|
||||
.shadow(color: barColor.opacity(0.8), radius: 4, x: 0, y: 0)
|
||||
.frame(height: isPressed ? barHeight * 0.9 - 10 : barHeight - 10)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 4))
|
||||
.padding(.horizontal, 4)
|
||||
.padding(.bottom, 5)
|
||||
}
|
||||
.frame(maxHeight: 180, alignment: .bottom)
|
||||
}
|
||||
.buttonStyle(NeonBarButtonStyle(isPressed: $isPressed))
|
||||
.frame(maxWidth: .infinity)
|
||||
.accessibilityLabel(mood.strValue)
|
||||
.accessibilityHint(String(localized: "Select this mood"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -763,15 +737,3 @@ struct NeonBarButtonStyle: ButtonStyle {
|
||||
}
|
||||
}
|
||||
|
||||
private extension Mood {
|
||||
var shortLabel: String {
|
||||
switch self {
|
||||
case .great: return "GRT"
|
||||
case .good: return "GUD"
|
||||
case .average: return "AVG"
|
||||
case .bad: return "BAD"
|
||||
case .horrible: return "HRB"
|
||||
default: return "---"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user