Add missing accessibility identifiers to all interactive UI elements
Audit found ~50+ interactive elements (buttons, toggles, pickers, alerts, links) missing accessibility identifiers across 13 view files. Added centralized ID definitions and applied them to every entry detail button, guided reflection control, settings toggle, paywall unlock button, subscription/IAP button, lock screen control, and photo action dialog.
This commit is contained in:
@@ -49,6 +49,17 @@ enum AccessibilityID {
|
|||||||
static let noteButton = "entry_detail_note_button"
|
static let noteButton = "entry_detail_note_button"
|
||||||
static let noteArea = "entry_detail_note_area"
|
static let noteArea = "entry_detail_note_area"
|
||||||
static let moodGrid = "entry_detail_mood_grid"
|
static let moodGrid = "entry_detail_mood_grid"
|
||||||
|
static let reflectionBeginButton = "entry_detail_reflection_begin"
|
||||||
|
static let reflectionCard = "entry_detail_reflection_card"
|
||||||
|
static let photoButton = "entry_detail_photo_button"
|
||||||
|
static let photoPlaceholder = "entry_detail_photo_placeholder"
|
||||||
|
static let photoImage = "entry_detail_photo_image"
|
||||||
|
static let photoTakeButton = "entry_detail_photo_take"
|
||||||
|
static let photoChooseButton = "entry_detail_photo_choose"
|
||||||
|
static let photoRemoveButton = "entry_detail_photo_remove"
|
||||||
|
static let photoCancelButton = "entry_detail_photo_cancel"
|
||||||
|
static let deleteConfirmButton = "entry_detail_delete_confirm"
|
||||||
|
static let deleteCancelButton = "entry_detail_delete_cancel"
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Note Editor
|
// MARK: - Note Editor
|
||||||
@@ -56,6 +67,7 @@ enum AccessibilityID {
|
|||||||
static let textEditor = "note_editor_text"
|
static let textEditor = "note_editor_text"
|
||||||
static let saveButton = "note_editor_save"
|
static let saveButton = "note_editor_save"
|
||||||
static let cancelButton = "note_editor_cancel"
|
static let cancelButton = "note_editor_cancel"
|
||||||
|
static let keyboardDoneButton = "note_editor_keyboard_done"
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Guided Reflection
|
// MARK: - Guided Reflection
|
||||||
@@ -67,6 +79,9 @@ enum AccessibilityID {
|
|||||||
static let backButton = "guided_reflection_back"
|
static let backButton = "guided_reflection_back"
|
||||||
static let saveButton = "guided_reflection_save"
|
static let saveButton = "guided_reflection_save"
|
||||||
static let cancelButton = "guided_reflection_cancel"
|
static let cancelButton = "guided_reflection_cancel"
|
||||||
|
static let infoButton = "guided_reflection_info"
|
||||||
|
static let discardButton = "guided_reflection_discard"
|
||||||
|
static let keepEditingButton = "guided_reflection_keep_editing"
|
||||||
static func questionLabel(step: Int) -> String {
|
static func questionLabel(step: Int) -> String {
|
||||||
"guided_reflection_question_\(step)"
|
"guided_reflection_question_\(step)"
|
||||||
}
|
}
|
||||||
@@ -75,6 +90,11 @@ enum AccessibilityID {
|
|||||||
static func chip(label: String) -> String {
|
static func chip(label: String) -> String {
|
||||||
"guided_reflection_chip_\(label)"
|
"guided_reflection_chip_\(label)"
|
||||||
}
|
}
|
||||||
|
// Info view
|
||||||
|
static let infoDoneButton = "guided_reflection_info_done"
|
||||||
|
static let cbtLearnMoreLink = "guided_reflection_cbt_learn_more"
|
||||||
|
static let actLearnMoreLink = "guided_reflection_act_learn_more"
|
||||||
|
static let baLearnMoreLink = "guided_reflection_ba_learn_more"
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Settings
|
// MARK: - Settings
|
||||||
@@ -92,6 +112,14 @@ enum AccessibilityID {
|
|||||||
static let bypassSubscriptionToggle = "settings_bypass_subscription"
|
static let bypassSubscriptionToggle = "settings_bypass_subscription"
|
||||||
static let eulaButton = "settings_eula"
|
static let eulaButton = "settings_eula"
|
||||||
static let privacyPolicyButton = "settings_privacy_policy"
|
static let privacyPolicyButton = "settings_privacy_policy"
|
||||||
|
static let hapticFeedbackToggle = "settings_haptic_feedback_toggle"
|
||||||
|
static let deleteToggle = "settings_delete_toggle"
|
||||||
|
static let privacyLockToggle = "settings_privacy_lock_toggle"
|
||||||
|
static let healthSyncToggle = "settings_health_sync_toggle"
|
||||||
|
static let weatherToggle = "settings_weather_toggle"
|
||||||
|
static let reminderTimePicker = "settings_reminder_time_picker"
|
||||||
|
static let reminderSaveButton = "settings_reminder_save"
|
||||||
|
static let reminderCancelButton = "settings_reminder_cancel"
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Customize
|
// MARK: - Customize
|
||||||
@@ -126,6 +154,10 @@ enum AccessibilityID {
|
|||||||
static let monthOverlay = "paywall_month_overlay"
|
static let monthOverlay = "paywall_month_overlay"
|
||||||
static let yearOverlay = "paywall_year_overlay"
|
static let yearOverlay = "paywall_year_overlay"
|
||||||
static let insightsOverlay = "paywall_insights_overlay"
|
static let insightsOverlay = "paywall_insights_overlay"
|
||||||
|
static let monthUnlockButton = "paywall_month_unlock"
|
||||||
|
static let yearUnlockButton = "paywall_year_unlock"
|
||||||
|
static let insightsUnlockButton = "paywall_insights_unlock"
|
||||||
|
static let reportsUnlockButton = "paywall_reports_unlock"
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Day View Section Headers
|
// MARK: - Day View Section Headers
|
||||||
@@ -149,6 +181,15 @@ enum AccessibilityID {
|
|||||||
static let shareButton = "month_share_button"
|
static let shareButton = "month_share_button"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Month Detail
|
||||||
|
enum MonthDetail {
|
||||||
|
static let shareButton = "month_detail_share"
|
||||||
|
static let deleteButton = "month_detail_delete"
|
||||||
|
static func moodButton(_ mood: String) -> String {
|
||||||
|
"month_detail_mood_\(mood.lowercased())"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Year View
|
// MARK: - Year View
|
||||||
enum YearView {
|
enum YearView {
|
||||||
static let heatmap = "year_heatmap"
|
static let heatmap = "year_heatmap"
|
||||||
@@ -187,6 +228,36 @@ enum AccessibilityID {
|
|||||||
static let privacyConfirmation = "reports_privacy_confirmation"
|
static let privacyConfirmation = "reports_privacy_confirmation"
|
||||||
static let minimumEntriesWarning = "reports_minimum_entries_warning"
|
static let minimumEntriesWarning = "reports_minimum_entries_warning"
|
||||||
static let exportDataButton = "reports_export_data_button"
|
static let exportDataButton = "reports_export_data_button"
|
||||||
|
static let retryButton = "reports_retry_button"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Purchase / Subscription
|
||||||
|
enum Purchase {
|
||||||
|
static let manageSubscriptionButton = "purchase_manage_subscription"
|
||||||
|
static let changePlanButton = "purchase_change_plan"
|
||||||
|
static let restorePurchasesButton = "purchase_restore"
|
||||||
|
static let subscribeButton = "purchase_subscribe"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - IAP Warning
|
||||||
|
enum IAPWarning {
|
||||||
|
static let subscribeButton = "iap_warning_subscribe"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Lock Screen
|
||||||
|
enum LockScreen {
|
||||||
|
static let unlockButton = "lock_screen_unlock"
|
||||||
|
static let tryAgainButton = "lock_screen_try_again"
|
||||||
|
static let cancelButton = "lock_screen_cancel"
|
||||||
|
static func passcodeButton(_ digit: Int) -> String {
|
||||||
|
"lock_screen_passcode_\(digit)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Full Screen Photo
|
||||||
|
enum FullScreenPhoto {
|
||||||
|
static let closeButton = "full_screen_photo_close"
|
||||||
|
static let dismissArea = "full_screen_photo_dismiss"
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Common
|
// MARK: - Common
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ struct GuidedReflectionInfoView: View {
|
|||||||
title: String(localized: "guided_reflection_about_cbt_title"),
|
title: String(localized: "guided_reflection_about_cbt_title"),
|
||||||
body: String(localized: "guided_reflection_about_cbt_body"),
|
body: String(localized: "guided_reflection_about_cbt_body"),
|
||||||
citation: "Beck, J. S. (2020). Cognitive Behavior Therapy: Basics and Beyond, 3rd ed.",
|
citation: "Beck, J. S. (2020). Cognitive Behavior Therapy: Basics and Beyond, 3rd ed.",
|
||||||
url: cbtURL
|
url: cbtURL,
|
||||||
|
linkID: AccessibilityID.GuidedReflection.cbtLearnMoreLink
|
||||||
)
|
)
|
||||||
|
|
||||||
// ACT
|
// ACT
|
||||||
@@ -41,7 +42,8 @@ struct GuidedReflectionInfoView: View {
|
|||||||
title: String(localized: "guided_reflection_about_act_title"),
|
title: String(localized: "guided_reflection_about_act_title"),
|
||||||
body: String(localized: "guided_reflection_about_act_body"),
|
body: String(localized: "guided_reflection_about_act_body"),
|
||||||
citation: "Harris, R. (2009). ACT Made Simple. New Harbinger Publications.",
|
citation: "Harris, R. (2009). ACT Made Simple. New Harbinger Publications.",
|
||||||
url: actURL
|
url: actURL,
|
||||||
|
linkID: AccessibilityID.GuidedReflection.actLearnMoreLink
|
||||||
)
|
)
|
||||||
|
|
||||||
// BA
|
// BA
|
||||||
@@ -51,7 +53,8 @@ struct GuidedReflectionInfoView: View {
|
|||||||
title: String(localized: "guided_reflection_about_ba_title"),
|
title: String(localized: "guided_reflection_about_ba_title"),
|
||||||
body: String(localized: "guided_reflection_about_ba_body"),
|
body: String(localized: "guided_reflection_about_ba_body"),
|
||||||
citation: "Martell, C. R., Dimidjian, S., & Herman-Dunn, R. (2010). Behavioral Activation for Depression.",
|
citation: "Martell, C. R., Dimidjian, S., & Herman-Dunn, R. (2010). Behavioral Activation for Depression.",
|
||||||
url: baURL
|
url: baURL,
|
||||||
|
linkID: AccessibilityID.GuidedReflection.baLearnMoreLink
|
||||||
)
|
)
|
||||||
|
|
||||||
// Disclaimer
|
// Disclaimer
|
||||||
@@ -69,6 +72,7 @@ struct GuidedReflectionInfoView: View {
|
|||||||
Button(String(localized: "Done")) {
|
Button(String(localized: "Done")) {
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.GuidedReflection.infoDoneButton)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,7 +87,8 @@ struct GuidedReflectionInfoView: View {
|
|||||||
title: String,
|
title: String,
|
||||||
body: String,
|
body: String,
|
||||||
citation: String,
|
citation: String,
|
||||||
url: URL
|
url: URL,
|
||||||
|
linkID: String
|
||||||
) -> some View {
|
) -> some View {
|
||||||
VStack(alignment: .leading, spacing: 12) {
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
HStack(spacing: 10) {
|
HStack(spacing: 10) {
|
||||||
@@ -113,6 +118,7 @@ struct GuidedReflectionInfoView: View {
|
|||||||
}
|
}
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(linkID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
|
|||||||
@@ -101,7 +101,9 @@ struct GuidedReflectionView: View {
|
|||||||
Button(String(localized: "guided_reflection_discard"), role: .destructive) {
|
Button(String(localized: "guided_reflection_discard"), role: .destructive) {
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.GuidedReflection.discardButton)
|
||||||
Button(String(localized: "Cancel"), role: .cancel) { }
|
Button(String(localized: "Cancel"), role: .cancel) { }
|
||||||
|
.accessibilityIdentifier(AccessibilityID.GuidedReflection.keepEditingButton)
|
||||||
} message: {
|
} message: {
|
||||||
Text(String(localized: "guided_reflection_unsaved_message"))
|
Text(String(localized: "guided_reflection_unsaved_message"))
|
||||||
}
|
}
|
||||||
@@ -180,6 +182,7 @@ struct GuidedReflectionView: View {
|
|||||||
Image(systemName: "info.circle")
|
Image(systemName: "info.circle")
|
||||||
}
|
}
|
||||||
.accessibilityLabel(String(localized: "guided_reflection_about_title"))
|
.accessibilityLabel(String(localized: "guided_reflection_about_title"))
|
||||||
|
.accessibilityIdentifier(AccessibilityID.GuidedReflection.infoButton)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ struct IAPWarningView: View {
|
|||||||
.padding(.vertical, 12)
|
.padding(.vertical, 12)
|
||||||
.background(RoundedRectangle(cornerRadius: 10).fill(Color.pink))
|
.background(RoundedRectangle(cornerRadius: 10).fill(Color.pink))
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.IAPWarning.subscribeButton)
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
.background(theme.currentTheme.secondaryBGColor)
|
.background(theme.currentTheme.secondaryBGColor)
|
||||||
|
|||||||
@@ -218,6 +218,7 @@ struct InsightsView: View {
|
|||||||
.clipShape(RoundedRectangle(cornerRadius: 14))
|
.clipShape(RoundedRectangle(cornerRadius: 14))
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 24)
|
.padding(.horizontal, 24)
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Paywall.insightsUnlockButton)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -332,6 +332,7 @@ struct ReportsView: View {
|
|||||||
viewModel.generateReport()
|
viewModel.generateReport()
|
||||||
}
|
}
|
||||||
.font(.subheadline.weight(.medium))
|
.font(.subheadline.weight(.medium))
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Reports.retryButton)
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
.background(
|
.background(
|
||||||
@@ -403,6 +404,7 @@ struct ReportsView: View {
|
|||||||
.clipShape(RoundedRectangle(cornerRadius: 14))
|
.clipShape(RoundedRectangle(cornerRadius: 14))
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 24)
|
.padding(.horizontal, 24)
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Paywall.reportsUnlockButton)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1670,6 +1670,7 @@ struct LockScreenView: View {
|
|||||||
.opacity(showContent ? 1 : 0)
|
.opacity(showContent ? 1 : 0)
|
||||||
.offset(y: showContent ? 0 : 30)
|
.offset(y: showContent ? 0 : 30)
|
||||||
.padding(.horizontal, 32)
|
.padding(.horizontal, 32)
|
||||||
|
.accessibilityIdentifier(AccessibilityID.LockScreen.unlockButton)
|
||||||
.accessibilityLabel("Unlock")
|
.accessibilityLabel("Unlock")
|
||||||
.accessibilityHint("Double tap to authenticate with \(authManager.biometricName)")
|
.accessibilityHint("Double tap to authenticate with \(authManager.biometricName)")
|
||||||
|
|
||||||
@@ -1705,7 +1706,9 @@ struct LockScreenView: View {
|
|||||||
await authManager.authenticate()
|
await authManager.authenticate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.LockScreen.tryAgainButton)
|
||||||
Button("Cancel", role: .cancel) { }
|
Button("Cancel", role: .cancel) { }
|
||||||
|
.accessibilityIdentifier(AccessibilityID.LockScreen.cancelButton)
|
||||||
} message: {
|
} message: {
|
||||||
Text("Unable to verify your identity. Please try again.")
|
Text("Unable to verify your identity. Please try again.")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ struct MonthDetailView: View {
|
|||||||
Image(systemName: "square.and.arrow.up")
|
Image(systemName: "square.and.arrow.up")
|
||||||
.foregroundColor(textColor)
|
.foregroundColor(textColor)
|
||||||
.padding(.trailing)
|
.padding(.trailing)
|
||||||
|
.accessibilityIdentifier(AccessibilityID.MonthDetail.shareButton)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
let impactMed = UIImpactFeedbackGenerator(style: .heavy)
|
let impactMed = UIImpactFeedbackGenerator(style: .heavy)
|
||||||
impactMed.impactOccurred()
|
impactMed.impactOccurred()
|
||||||
@@ -97,6 +98,7 @@ struct MonthDetailView: View {
|
|||||||
showUpdateEntryAlert = false
|
showUpdateEntryAlert = false
|
||||||
selectedEntry = nil
|
selectedEntry = nil
|
||||||
})
|
})
|
||||||
|
.accessibilityIdentifier(AccessibilityID.MonthDetail.moodButton(mood.strValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let selectedEntry = selectedEntry,
|
if let selectedEntry = selectedEntry,
|
||||||
@@ -107,6 +109,7 @@ struct MonthDetailView: View {
|
|||||||
updateEntries()
|
updateEntries()
|
||||||
showUpdateEntryAlert = false
|
showUpdateEntryAlert = false
|
||||||
})
|
})
|
||||||
|
.accessibilityIdentifier(AccessibilityID.MonthDetail.deleteButton)
|
||||||
}
|
}
|
||||||
|
|
||||||
Button(String(localized: "content_view_fill_in_missing_entry_cancel"), role: .cancel, action: {
|
Button(String(localized: "content_view_fill_in_missing_entry_cancel"), role: .cancel, action: {
|
||||||
|
|||||||
@@ -324,6 +324,7 @@ struct MonthView: View {
|
|||||||
.clipShape(RoundedRectangle(cornerRadius: 14))
|
.clipShape(RoundedRectangle(cornerRadius: 14))
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 24)
|
.padding(.horizontal, 24)
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Paywall.monthUnlockButton)
|
||||||
}
|
}
|
||||||
.padding(.vertical, 24)
|
.padding(.vertical, 24)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ struct NoteEditorView: View {
|
|||||||
Button("Done") {
|
Button("Done") {
|
||||||
isTextFieldFocused = false
|
isTextFieldFocused = false
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.NoteEditor.keyboardDoneButton)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
@@ -226,7 +227,9 @@ struct EntryDetailView: View {
|
|||||||
onDelete()
|
onDelete()
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.EntryDetail.deleteConfirmButton)
|
||||||
Button("Cancel", role: .cancel) { }
|
Button("Cancel", role: .cancel) { }
|
||||||
|
.accessibilityIdentifier(AccessibilityID.EntryDetail.deleteCancelButton)
|
||||||
} message: {
|
} message: {
|
||||||
Text("Are you sure you want to delete this mood entry? This cannot be undone.")
|
Text("Are you sure you want to delete this mood entry? This cannot be undone.")
|
||||||
}
|
}
|
||||||
@@ -461,6 +464,7 @@ struct EntryDetailView: View {
|
|||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.fontWeight(.medium)
|
.fontWeight(.medium)
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.EntryDetail.reflectionBeginButton)
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
@@ -507,6 +511,7 @@ struct EntryDetailView: View {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
|
.accessibilityIdentifier(AccessibilityID.EntryDetail.reflectionCard)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,6 +531,7 @@ struct EntryDetailView: View {
|
|||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.fontWeight(.medium)
|
.fontWeight(.medium)
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.EntryDetail.photoButton)
|
||||||
}
|
}
|
||||||
.zIndex(1)
|
.zIndex(1)
|
||||||
|
|
||||||
@@ -542,6 +548,7 @@ struct EntryDetailView: View {
|
|||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
showFullScreenPhoto = true
|
showFullScreenPhoto = true
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.EntryDetail.photoImage)
|
||||||
} else {
|
} else {
|
||||||
Button {
|
Button {
|
||||||
showPhotoOptions = true
|
showPhotoOptions = true
|
||||||
@@ -568,22 +575,27 @@ struct EntryDetailView: View {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
|
.accessibilityIdentifier(AccessibilityID.EntryDetail.photoPlaceholder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.confirmationDialog("Photo", isPresented: $showPhotoOptions, titleVisibility: .visible) {
|
.confirmationDialog("Photo", isPresented: $showPhotoOptions, titleVisibility: .visible) {
|
||||||
Button("Take Photo") {
|
Button("Take Photo") {
|
||||||
showCamera = true
|
showCamera = true
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.EntryDetail.photoTakeButton)
|
||||||
Button("Choose from Library") {
|
Button("Choose from Library") {
|
||||||
showPhotoPicker = true
|
showPhotoPicker = true
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.EntryDetail.photoChooseButton)
|
||||||
if let photoID = entry.photoID {
|
if let photoID = entry.photoID {
|
||||||
Button("Remove Photo", role: .destructive) {
|
Button("Remove Photo", role: .destructive) {
|
||||||
_ = PhotoManager.shared.deletePhoto(id: photoID)
|
_ = PhotoManager.shared.deletePhoto(id: photoID)
|
||||||
_ = DataController.shared.updatePhoto(forDate: entry.forDate, photoID: nil)
|
_ = DataController.shared.updatePhoto(forDate: entry.forDate, photoID: nil)
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.EntryDetail.photoRemoveButton)
|
||||||
}
|
}
|
||||||
Button("Cancel", role: .cancel) { }
|
Button("Cancel", role: .cancel) { }
|
||||||
|
.accessibilityIdentifier(AccessibilityID.EntryDetail.photoCancelButton)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -634,9 +646,11 @@ struct FullScreenPhotoView: View {
|
|||||||
.foregroundStyle(.white.opacity(0.8))
|
.foregroundStyle(.white.opacity(0.8))
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.FullScreenPhoto.closeButton)
|
||||||
}
|
}
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.FullScreenPhoto.dismissArea)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ struct PurchaseButtonView: View {
|
|||||||
.foregroundColor(.blue)
|
.foregroundColor(.blue)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Purchase.manageSubscriptionButton)
|
||||||
|
|
||||||
// Show other subscription options
|
// Show other subscription options
|
||||||
if iapManager.sortedProducts.count > 1 {
|
if iapManager.sortedProducts.count > 1 {
|
||||||
@@ -98,6 +99,7 @@ struct PurchaseButtonView: View {
|
|||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Purchase.changePlanButton)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,6 +186,7 @@ struct PurchaseButtonView: View {
|
|||||||
.font(.body)
|
.font(.body)
|
||||||
.foregroundColor(.blue)
|
.foregroundColor(.blue)
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Purchase.restorePurchasesButton)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -814,6 +814,7 @@ struct SettingsContentView: View {
|
|||||||
}
|
}
|
||||||
))
|
))
|
||||||
.labelsHidden()
|
.labelsHidden()
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Settings.privacyLockToggle)
|
||||||
.accessibilityLabel(String(localized: "Privacy Lock"))
|
.accessibilityLabel(String(localized: "Privacy Lock"))
|
||||||
.accessibilityHint(String(localized: "Require biometric authentication to open app"))
|
.accessibilityHint(String(localized: "Require biometric authentication to open app"))
|
||||||
}
|
}
|
||||||
@@ -913,6 +914,7 @@ struct SettingsContentView: View {
|
|||||||
))
|
))
|
||||||
.labelsHidden()
|
.labelsHidden()
|
||||||
.disabled(iapManager.shouldShowPaywall)
|
.disabled(iapManager.shouldShowPaywall)
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Settings.healthSyncToggle)
|
||||||
.accessibilityLabel(String(localized: "Apple Health"))
|
.accessibilityLabel(String(localized: "Apple Health"))
|
||||||
.accessibilityHint(String(localized: "Sync mood data with Apple Health"))
|
.accessibilityHint(String(localized: "Sync mood data with Apple Health"))
|
||||||
} else {
|
} else {
|
||||||
@@ -1012,6 +1014,7 @@ struct SettingsContentView: View {
|
|||||||
))
|
))
|
||||||
.labelsHidden()
|
.labelsHidden()
|
||||||
.disabled(iapManager.shouldShowPaywall)
|
.disabled(iapManager.shouldShowPaywall)
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Settings.weatherToggle)
|
||||||
.accessibilityLabel(String(localized: "Weather"))
|
.accessibilityLabel(String(localized: "Weather"))
|
||||||
.accessibilityHint(String(localized: "Show weather details for each day"))
|
.accessibilityHint(String(localized: "Show weather details for each day"))
|
||||||
}
|
}
|
||||||
@@ -1128,6 +1131,7 @@ struct SettingsContentView: View {
|
|||||||
.onChange(of: hapticFeedbackEnabled) { _, newValue in
|
.onChange(of: hapticFeedbackEnabled) { _, newValue in
|
||||||
AnalyticsManager.shared.track(.hapticFeedbackToggled(enabled: newValue))
|
AnalyticsManager.shared.track(.hapticFeedbackToggled(enabled: newValue))
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Settings.hapticFeedbackToggle)
|
||||||
.accessibilityLabel(String(localized: "Haptic Feedback"))
|
.accessibilityLabel(String(localized: "Haptic Feedback"))
|
||||||
.accessibilityHint(String(localized: "Toggle vibration feedback when voting"))
|
.accessibilityHint(String(localized: "Toggle vibration feedback when voting"))
|
||||||
}
|
}
|
||||||
@@ -1145,6 +1149,7 @@ struct SettingsContentView: View {
|
|||||||
AnalyticsManager.shared.track(.deleteToggleChanged(enabled: newValue))
|
AnalyticsManager.shared.track(.deleteToggleChanged(enabled: newValue))
|
||||||
}
|
}
|
||||||
.foregroundColor(textColor)
|
.foregroundColor(textColor)
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Settings.deleteToggle)
|
||||||
.accessibilityHint(String(localized: "Allow deleting mood entries by swiping"))
|
.accessibilityHint(String(localized: "Allow deleting mood entries by swiping"))
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
@@ -1274,6 +1279,7 @@ struct ReminderTimePickerView: View {
|
|||||||
)
|
)
|
||||||
.datePickerStyle(.wheel)
|
.datePickerStyle(.wheel)
|
||||||
.labelsHidden()
|
.labelsHidden()
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Settings.reminderTimePicker)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
@@ -1285,12 +1291,14 @@ struct ReminderTimePickerView: View {
|
|||||||
Button("Cancel") {
|
Button("Cancel") {
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Settings.reminderCancelButton)
|
||||||
}
|
}
|
||||||
ToolbarItem(placement: .confirmationAction) {
|
ToolbarItem(placement: .confirmationAction) {
|
||||||
Button("Save") {
|
Button("Save") {
|
||||||
saveReminderTime()
|
saveReminderTime()
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Settings.reminderSaveButton)
|
||||||
.fontWeight(.semibold)
|
.fontWeight(.semibold)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1545,6 +1553,7 @@ struct SettingsView: View {
|
|||||||
}
|
}
|
||||||
))
|
))
|
||||||
.labelsHidden()
|
.labelsHidden()
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Settings.privacyLockToggle)
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
.background(theme.currentTheme.secondaryBGColor)
|
.background(theme.currentTheme.secondaryBGColor)
|
||||||
@@ -1614,6 +1623,7 @@ struct SettingsView: View {
|
|||||||
))
|
))
|
||||||
.labelsHidden()
|
.labelsHidden()
|
||||||
.disabled(iapManager.shouldShowPaywall)
|
.disabled(iapManager.shouldShowPaywall)
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Settings.healthSyncToggle)
|
||||||
} else {
|
} else {
|
||||||
Text("Not Available")
|
Text("Not Available")
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
@@ -1705,6 +1715,7 @@ struct SettingsView: View {
|
|||||||
))
|
))
|
||||||
.labelsHidden()
|
.labelsHidden()
|
||||||
.disabled(iapManager.shouldShowPaywall)
|
.disabled(iapManager.shouldShowPaywall)
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Settings.weatherToggle)
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
|
|
||||||
@@ -1963,13 +1974,14 @@ struct SettingsView: View {
|
|||||||
Text(String(localized: "settings_view_show_onboarding"))
|
Text(String(localized: "settings_view_show_onboarding"))
|
||||||
.foregroundColor(textColor)
|
.foregroundColor(textColor)
|
||||||
})
|
})
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Settings.showOnboardingButton)
|
||||||
.padding()
|
.padding()
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.background(theme.currentTheme.secondaryBGColor)
|
.background(theme.currentTheme.secondaryBGColor)
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
|
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
|
||||||
}
|
}
|
||||||
|
|
||||||
private var eulaButton: some View {
|
private var eulaButton: some View {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
AnalyticsManager.shared.track(.eulaViewed)
|
AnalyticsManager.shared.track(.eulaViewed)
|
||||||
@@ -1978,13 +1990,14 @@ struct SettingsView: View {
|
|||||||
Text(String(localized: "settings_view_show_eula"))
|
Text(String(localized: "settings_view_show_eula"))
|
||||||
.foregroundColor(textColor)
|
.foregroundColor(textColor)
|
||||||
})
|
})
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Settings.eulaButton)
|
||||||
.padding()
|
.padding()
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.background(theme.currentTheme.secondaryBGColor)
|
.background(theme.currentTheme.secondaryBGColor)
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
|
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
|
||||||
}
|
}
|
||||||
|
|
||||||
private var privacyButton: some View {
|
private var privacyButton: some View {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
AnalyticsManager.shared.track(.privacyPolicyViewed)
|
AnalyticsManager.shared.track(.privacyPolicyViewed)
|
||||||
@@ -1993,6 +2006,7 @@ struct SettingsView: View {
|
|||||||
Text(String(localized: "settings_view_show_privacy"))
|
Text(String(localized: "settings_view_show_privacy"))
|
||||||
.foregroundColor(textColor)
|
.foregroundColor(textColor)
|
||||||
})
|
})
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Settings.privacyPolicyButton)
|
||||||
.padding()
|
.padding()
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.background(theme.currentTheme.secondaryBGColor)
|
.background(theme.currentTheme.secondaryBGColor)
|
||||||
@@ -2062,6 +2076,7 @@ struct SettingsView: View {
|
|||||||
.onChange(of: hapticFeedbackEnabled) { _, newValue in
|
.onChange(of: hapticFeedbackEnabled) { _, newValue in
|
||||||
AnalyticsManager.shared.track(.hapticFeedbackToggled(enabled: newValue))
|
AnalyticsManager.shared.track(.hapticFeedbackToggled(enabled: newValue))
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Settings.hapticFeedbackToggle)
|
||||||
.accessibilityLabel(String(localized: "Haptic Feedback"))
|
.accessibilityLabel(String(localized: "Haptic Feedback"))
|
||||||
.accessibilityHint(String(localized: "Toggle vibration feedback when voting"))
|
.accessibilityHint(String(localized: "Toggle vibration feedback when voting"))
|
||||||
}
|
}
|
||||||
@@ -2079,6 +2094,7 @@ struct SettingsView: View {
|
|||||||
AnalyticsManager.shared.track(.deleteToggleChanged(enabled: newValue))
|
AnalyticsManager.shared.track(.deleteToggleChanged(enabled: newValue))
|
||||||
}
|
}
|
||||||
.foregroundColor(textColor)
|
.foregroundColor(textColor)
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Settings.deleteToggle)
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
.background(theme.currentTheme.secondaryBGColor)
|
.background(theme.currentTheme.secondaryBGColor)
|
||||||
|
|||||||
@@ -260,6 +260,7 @@ struct YearView: View {
|
|||||||
.clipShape(RoundedRectangle(cornerRadius: 14))
|
.clipShape(RoundedRectangle(cornerRadius: 14))
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 24)
|
.padding(.horizontal, 24)
|
||||||
|
.accessibilityIdentifier(AccessibilityID.Paywall.yearUnlockButton)
|
||||||
}
|
}
|
||||||
.padding(.vertical, 24)
|
.padding(.vertical, 24)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|||||||
Reference in New Issue
Block a user