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:
@@ -10,6 +10,10 @@ import PhotosUI
|
||||
|
||||
struct NoteEditorView: View {
|
||||
|
||||
private enum AnimationConstants {
|
||||
static let keyboardAppearDelay: TimeInterval = 0.5
|
||||
}
|
||||
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system
|
||||
|
||||
@@ -57,18 +61,18 @@ struct NoteEditorView: View {
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.navigationTitle("Journal Note")
|
||||
.navigationTitle(String(localized: "Journal Note"))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Cancel") {
|
||||
Button(String(localized: "Cancel")) {
|
||||
dismiss()
|
||||
}
|
||||
.accessibilityIdentifier(AccessibilityID.NoteEditor.cancelButton)
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .confirmationAction) {
|
||||
Button("Save") {
|
||||
Button(String(localized: "Save")) {
|
||||
saveNote()
|
||||
}
|
||||
.disabled(isSaving || noteText.count > maxCharacters)
|
||||
@@ -78,14 +82,14 @@ struct NoteEditorView: View {
|
||||
|
||||
ToolbarItemGroup(placement: .keyboard) {
|
||||
Spacer()
|
||||
Button("Done") {
|
||||
Button(String(localized: "Done")) {
|
||||
isTextFieldFocused = false
|
||||
}
|
||||
.accessibilityIdentifier(AccessibilityID.NoteEditor.keyboardDoneButton)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + AnimationConstants.keyboardAppearDelay) {
|
||||
isTextFieldFocused = true
|
||||
}
|
||||
}
|
||||
@@ -205,12 +209,12 @@ struct EntryDetailView: View {
|
||||
.padding()
|
||||
}
|
||||
.background(Color(.systemGroupedBackground))
|
||||
.navigationTitle("Entry Details")
|
||||
.navigationTitle(String(localized: "Entry Details"))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.accessibilityIdentifier(AccessibilityID.EntryDetail.sheet)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .confirmationAction) {
|
||||
Button("Done") {
|
||||
Button(String(localized: "Done")) {
|
||||
dismiss()
|
||||
}
|
||||
.accessibilityIdentifier(AccessibilityID.EntryDetail.doneButton)
|
||||
@@ -222,16 +226,16 @@ struct EntryDetailView: View {
|
||||
.sheet(isPresented: $showReflectionFlow) {
|
||||
GuidedReflectionView(entry: entry)
|
||||
}
|
||||
.alert("Delete Entry", isPresented: $showDeleteConfirmation) {
|
||||
Button("Delete", role: .destructive) {
|
||||
.alert(String(localized: "Delete Entry"), isPresented: $showDeleteConfirmation) {
|
||||
Button(String(localized: "Delete"), role: .destructive) {
|
||||
onDelete()
|
||||
dismiss()
|
||||
}
|
||||
.accessibilityIdentifier(AccessibilityID.EntryDetail.deleteConfirmButton)
|
||||
Button("Cancel", role: .cancel) { }
|
||||
Button(String(localized: "Cancel"), role: .cancel) { }
|
||||
.accessibilityIdentifier(AccessibilityID.EntryDetail.deleteCancelButton)
|
||||
} message: {
|
||||
Text("Are you sure you want to delete this mood entry? This cannot be undone.")
|
||||
Text(String(localized: "Are you sure you want to delete this mood entry? This cannot be undone."))
|
||||
}
|
||||
.photosPicker(isPresented: $showPhotoPicker, selection: $selectedPhotoItem, matching: .images)
|
||||
.onChange(of: selectedPhotoItem) { _, newItem in
|
||||
|
||||
Reference in New Issue
Block a user