v1.1 polish: accessibility, error logging, localization, and code quality sweep
- Wrap 30+ production print() statements in #if DEBUG guards across 18 files - Add VoiceOver labels, hints, and traits to Watch app, Live Activities, widgets - Add .accessibilityAddTraits(.isButton) to 15+ onTapGesture views - Add text alternatives for color-only indicators (progress dots, mood circles) - Localize raw string literals in NoteEditorView, EntryDetailView, widgets - Replace 25+ silent try? with do/catch + AppLogger error logging - Replace hardcoded font sizes with semantic Dynamic Type fonts - Fix FIXME in IconPickerView (log icon change errors) - Extract magic animation delays to named constants across 8 files - Add widget empty state "Log your first mood!" messaging - Hide decorative images from VoiceOver, add labels to ColorPickers - Remove stale TODO in Color+Codable (alpha change deferred for migration) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -69,8 +69,10 @@ struct IconPickerView: View {
|
||||
|
||||
ForEach(iconSets, id: \.self.0){ iconSet in
|
||||
Button(action: {
|
||||
UIApplication.shared.setAlternateIconName(iconSet.1) { (error) in
|
||||
// FIXME: Handle error
|
||||
UIApplication.shared.setAlternateIconName(iconSet.1) { error in
|
||||
if let error {
|
||||
AppLogger.settings.error("Failed to set app icon '\(iconSet.1)': \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
AnalyticsManager.shared.track(.appIconChanged(iconTitle: iconSet.1))
|
||||
}, label: {
|
||||
|
||||
@@ -48,6 +48,8 @@ struct ImagePackPickerView: View {
|
||||
imagePack = images
|
||||
AnalyticsManager.shared.track(.iconPackChanged(packId: images.rawValue))
|
||||
}
|
||||
.accessibilityAddTraits(.isButton)
|
||||
.accessibilityLabel(String(localized: "Select \(String(describing: images)) icon pack"))
|
||||
if images.rawValue != (MoodImages.allCases.sorted(by: { $0.rawValue > $1.rawValue }).first?.rawValue) ?? 0 {
|
||||
Divider()
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ struct PersonalityPackPickerView: View {
|
||||
LocalNotification.rescheduleNotifiations()
|
||||
// }
|
||||
}
|
||||
.accessibilityAddTraits(.isButton)
|
||||
.accessibilityLabel(String(localized: "Select \(aPack.title()) personality pack"))
|
||||
// .blur(radius: aPack.rawValue == PersonalityPack.Rude.rawValue && !showNSFW ? 5 : 0)
|
||||
.alert(isPresented: $showOver18Alert) {
|
||||
let primaryButton = Alert.Button.default(Text(String(localized: "customize_view_over18alert_ok"))) {
|
||||
|
||||
@@ -35,6 +35,8 @@ struct ShapePickerView: View {
|
||||
.onTapGesture {
|
||||
shapeRefreshToggleThing.toggle()
|
||||
}
|
||||
.accessibilityAddTraits(.isButton)
|
||||
.accessibilityLabel(String(localized: "Refresh shapes"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +53,8 @@ struct ShapePickerView: View {
|
||||
shape = ashape
|
||||
AnalyticsManager.shared.track(.moodShapeChanged(shapeId: shape.rawValue))
|
||||
}
|
||||
.accessibilityAddTraits(.isButton)
|
||||
.accessibilityLabel(String(localized: "Select \(String(describing: ashape)) shape"))
|
||||
.contentShape(Rectangle())
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 10, style: .continuous)
|
||||
|
||||
Reference in New Issue
Block a user