Files
honeyDueKMP/iosApp/iosApp/Subviews/Task/TaskActionButtons.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

220 lines
6.6 KiB
Swift

import SwiftUI
import ComposeApp
// Action buttons accept a shared TaskViewModel from the parent view to avoid redundant instances.
// MARK: - Edit Task Button
struct EditTaskButton: View {
let taskId: Int32
let onCompletion: () -> Void
let onError: (String) -> Void
var body: some View {
Button(action: {
// Edit navigates to edit screen - handled by parent
onCompletion()
}) {
Label("Edit", systemImage: "pencil")
.font(.subheadline)
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
.accessibilityLabel("Edit task")
.accessibilityHint("Double tap to edit this task")
}
}
// MARK: - Cancel Task Button
struct CancelTaskButton: View {
let taskId: Int32
let onCompletion: () -> Void
let onError: (String) -> Void
let viewModel: TaskViewModel
@State private var showConfirmation = false
var body: some View {
Button(action: {
showConfirmation = true
}) {
Label("Cancel", systemImage: "xmark.circle")
.font(.subheadline)
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
.tint(Color.appError)
.accessibilityLabel("Cancel task")
.accessibilityHint("Double tap to cancel this task")
.alert("Cancel Task", isPresented: $showConfirmation) {
Button("Cancel", role: .cancel) { }
Button("Cancel Task", role: .destructive) {
viewModel.cancelTask(id: taskId) { success in
if success {
onCompletion()
} else {
onError(String(localized: "Failed to cancel task"))
}
}
}
} message: {
Text("Are you sure you want to cancel this task? You can undo this later.")
}
}
}
// MARK: - Uncancel (Restore) Task Button
struct UncancelTaskButton: View {
let taskId: Int32
let onCompletion: () -> Void
let onError: (String) -> Void
let viewModel: TaskViewModel
var body: some View {
Button(action: {
viewModel.uncancelTask(id: taskId) { success in
if success {
onCompletion()
} else {
onError(String(localized: "Failed to restore task"))
}
}
}) {
Label("Restore", systemImage: "arrow.uturn.backward")
.font(.subheadline)
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.tint(Color.appPrimary)
.accessibilityLabel("Restore task")
.accessibilityHint("Double tap to restore this task")
}
}
// MARK: - Mark In Progress Button
struct MarkInProgressButton: View {
let taskId: Int32
let onCompletion: () -> Void
let onError: (String) -> Void
let viewModel: TaskViewModel
var body: some View {
Button(action: {
viewModel.markInProgress(id: taskId) { success in
if success {
onCompletion()
} else {
onError(String(localized: "Failed to mark task in progress"))
}
}
}) {
HStack {
Image(systemName: "play.circle.fill")
.resizable()
.frame(width: 18, height: 18)
Text("In Progress")
.font(.subheadline.weight(.semibold))
}
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
.tint(Color.appAccent)
.accessibilityLabel("Mark as in progress")
.accessibilityHint("Double tap to mark this task as in progress")
}
}
// MARK: - Complete Task Button
struct CompleteTaskButton: View {
let taskId: Int32
let onCompletion: () -> Void
let onError: (String) -> Void
var body: some View {
Button(action: {
// Complete shows dialog - handled by parent
onCompletion()
}) {
HStack {
Image(systemName: "checkmark.circle.fill")
.resizable()
.frame(width: 18, height: 18)
Text("Complete")
.font(.subheadline.weight(.semibold))
}
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.accessibilityLabel("Complete task")
.accessibilityHint("Double tap to complete this task")
}
}
// MARK: - Archive Task Button
struct ArchiveTaskButton: View {
let taskId: Int32
let onCompletion: () -> Void
let onError: (String) -> Void
let viewModel: TaskViewModel
@State private var showConfirmation = false
var body: some View {
Button(action: {
showConfirmation = true
}) {
Label("Archive", systemImage: "archivebox")
.font(.subheadline)
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
.tint(.gray)
.accessibilityLabel("Archive task")
.accessibilityHint("Double tap to archive this task")
.alert("Archive Task", isPresented: $showConfirmation) {
Button("Cancel", role: .cancel) { }
Button("Archive", role: .destructive) {
viewModel.archiveTask(id: taskId) { success in
if success {
onCompletion()
} else {
onError(String(localized: "Failed to archive task"))
}
}
}
} message: {
Text("Are you sure you want to archive this task? You can unarchive it later from archived tasks.")
}
}
}
// MARK: - Unarchive Task Button
struct UnarchiveTaskButton: View {
let taskId: Int32
let onCompletion: () -> Void
let onError: (String) -> Void
let viewModel: TaskViewModel
var body: some View {
Button(action: {
viewModel.unarchiveTask(id: taskId) { success in
if success {
onCompletion()
} else {
onError(String(localized: "Failed to unarchive task"))
}
}
}) {
Label("Unarchive", systemImage: "tray.and.arrow.up")
.font(.subheadline)
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
.tint(Color.appPrimary)
.accessibilityLabel("Unarchive task")
.accessibilityHint("Double tap to unarchive this task")
}
}