Files
honeyDueKMP/iosApp/iosApp/PushNotifications/NotificationCategories.swift
T
Trey T db65db6232
Android UI Tests / ui-tests (push) Has been cancelled
i18n: complete app-wide localization (10 languages) + audit tooling
Localize all user-facing strings across iOS (SwiftUI), shared Kotlin, and
Android Compose into en/es/fr/de/pt/it/ja/ko/nl/zh:
- iOS String Catalogs: main + widget Localizable.xcstrings, InfoPlist.xcstrings
  (permissions), plural variations, ~200 new keys translated
- Shared Kotlin ClientStrings table + Android composeResources/values-* (884 keys
  ×10), routed Api/ViewModel/util error & UI strings through localization
- Backend-localized lookups/suggestions consumed via display names
- Widget extension catalog; theme names, home-profile fallbacks, validation,
  network errors, accessibility labels all localized

Add re-runnable verification gates:
- scripts/i18n_audit.py  — enumerate every literal, partition to GAP=0
- scripts/i18n_coverage.py — all 10 locales translated, format-specifier parity

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 20:52:28 -05:00

143 lines
5.0 KiB
Swift

import UserNotifications
/// Notification Category Identifiers matching backend constants
enum NotificationCategoryID: String {
case taskActionable = "TASK_ACTIONABLE" // overdue, due_soon, upcoming
case taskInProgress = "TASK_IN_PROGRESS" // tasks in progress
case taskCancelled = "TASK_CANCELLED" // cancelled tasks
case taskCompleted = "TASK_COMPLETED" // completed tasks (read-only)
case taskGeneric = "TASK_NOTIFICATION_GENERIC" // non-premium users
}
/// Action Identifiers for notification buttons
enum NotificationActionID: String {
// Task actions
case viewTask = "VIEW_TASK"
case completeTask = "COMPLETE_TASK"
case markInProgress = "MARK_IN_PROGRESS"
case cancelTask = "CANCEL_TASK"
case uncancelTask = "UNCANCEL_TASK"
case editTask = "EDIT_TASK"
// Default action (tapped notification body)
case defaultAction = "com.apple.UNNotificationDefaultActionIdentifier"
}
/// Manages notification category registration
struct NotificationCategories {
/// Registers all notification categories with the system
/// Call this early in app launch (didFinishLaunching)
static func registerCategories() {
let categories = createAllCategories()
UNUserNotificationCenter.current().setNotificationCategories(categories)
#if DEBUG
print("Registered \(categories.count) notification categories")
#endif
}
/// Creates all notification categories for the app
private static func createAllCategories() -> Set<UNNotificationCategory> {
return [
createTaskActionableCategory(),
createTaskInProgressCategory(),
createTaskCancelledCategory(),
createTaskCompletedCategory(),
createTaskGenericCategory()
]
}
// MARK: - Category Definitions
/// TASK_ACTIONABLE: For overdue, due_soon, upcoming tasks
/// Actions: Complete, Mark In Progress, Cancel
private static func createTaskActionableCategory() -> UNNotificationCategory {
let completeAction = UNNotificationAction(
identifier: NotificationActionID.completeTask.rawValue,
title: String(localized: "Complete"),
options: []
)
let inProgressAction = UNNotificationAction(
identifier: NotificationActionID.markInProgress.rawValue,
title: String(localized: "Start"),
options: []
)
let cancelAction = UNNotificationAction(
identifier: NotificationActionID.cancelTask.rawValue,
title: String(localized: "Cancel"),
options: [.destructive]
)
return UNNotificationCategory(
identifier: NotificationCategoryID.taskActionable.rawValue,
actions: [completeAction, inProgressAction, cancelAction],
intentIdentifiers: [],
options: []
)
}
/// TASK_IN_PROGRESS: For tasks currently being worked on
/// Actions: Complete, Cancel
private static func createTaskInProgressCategory() -> UNNotificationCategory {
let completeAction = UNNotificationAction(
identifier: NotificationActionID.completeTask.rawValue,
title: String(localized: "Complete"),
options: []
)
let cancelAction = UNNotificationAction(
identifier: NotificationActionID.cancelTask.rawValue,
title: String(localized: "Cancel"),
options: [.destructive]
)
return UNNotificationCategory(
identifier: NotificationCategoryID.taskInProgress.rawValue,
actions: [completeAction, cancelAction],
intentIdentifiers: [],
options: []
)
}
/// TASK_CANCELLED: For cancelled tasks
/// Actions: Uncancel
private static func createTaskCancelledCategory() -> UNNotificationCategory {
let uncancelAction = UNNotificationAction(
identifier: NotificationActionID.uncancelTask.rawValue,
title: String(localized: "Restore"),
options: []
)
return UNNotificationCategory(
identifier: NotificationCategoryID.taskCancelled.rawValue,
actions: [uncancelAction],
intentIdentifiers: [],
options: []
)
}
/// TASK_COMPLETED: For completed tasks (read-only, tap to view)
/// No actions - just view on tap
private static func createTaskCompletedCategory() -> UNNotificationCategory {
return UNNotificationCategory(
identifier: NotificationCategoryID.taskCompleted.rawValue,
actions: [],
intentIdentifiers: [],
options: []
)
}
/// TASK_NOTIFICATION_GENERIC: For non-premium users
/// No actions - tap opens app home
private static func createTaskGenericCategory() -> UNNotificationCategory {
return UNNotificationCategory(
identifier: NotificationCategoryID.taskGeneric.rawValue,
actions: [],
intentIdentifiers: [],
options: []
)
}
}