Files
honeyDueKMP/iosApp/iosApp/Helpers/WidgetActionProcessor.swift
Trey t 4a04aff1e6 Replace status_id with in_progress boolean across mobile apps
- Remove TaskStatus model and status_id foreign key references
- Add in_progress boolean field to task models and forms
- Update TaskApi to use dedicated POST endpoints for task actions:
  - POST /tasks/:id/cancel/ instead of PATCH with is_cancelled
  - POST /tasks/:id/uncancel/
  - POST /tasks/:id/archive/
  - POST /tasks/:id/unarchive/
- Fix iOS TaskViewModel to use error-first pattern for Kotlin-Swift
  generic type bridging issues
- Update iOS callback signatures to pass full TaskResponse instead
  of just taskId to avoid stale closure lookups
- Add in_progress localization strings
- Update widget preview data to use inProgress boolean

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 20:47:59 -06:00

106 lines
4.0 KiB
Swift

import Foundation
import ComposeApp
import WidgetKit
/// Processes pending actions queued by the widget extension
/// Call `processPendingActions()` when the app becomes active
@MainActor
final class WidgetActionProcessor {
static let shared = WidgetActionProcessor()
private init() {}
/// Check if there are pending widget actions to process
var hasPendingActions: Bool {
WidgetDataManager.shared.hasPendingActions
}
/// Process all pending widget actions
/// Should be called when app becomes active
func processPendingActions() {
guard DataManager.shared.isAuthenticated() else {
print("WidgetActionProcessor: Not authenticated, skipping action processing")
return
}
let actions = WidgetDataManager.shared.loadPendingActions()
guard !actions.isEmpty else {
print("WidgetActionProcessor: No pending actions")
return
}
print("WidgetActionProcessor: Processing \(actions.count) pending action(s)")
for action in actions {
Task {
await processAction(action)
}
}
}
/// Process a single widget action
private func processAction(_ action: WidgetDataManager.WidgetAction) async {
switch action {
case .completeTask(let taskId, let taskTitle):
await completeTask(taskId: taskId, taskTitle: taskTitle, action: action)
}
}
/// Complete a task via the API
private func completeTask(taskId: Int, taskTitle: String, action: WidgetDataManager.WidgetAction) async {
print("WidgetActionProcessor: Completing task \(taskId) - \(taskTitle)")
do {
// Create a task completion with default values (quick complete from widget)
let request = TaskCompletionCreateRequest(
taskId: Int32(taskId),
completedAt: nil, // Defaults to now on server
notes: "Completed from widget",
actualCost: nil,
rating: nil,
imageUrls: nil
)
let result = try await APILayer.shared.createTaskCompletion(request: request)
if result is ApiResultSuccess<TaskCompletionResponse> {
print("WidgetActionProcessor: Task \(taskId) completed successfully")
// Remove the processed action
WidgetDataManager.shared.removeAction(action)
// Clear pending state for this task
WidgetDataManager.shared.clearPendingState(forTaskId: taskId)
// Refresh tasks to update UI
await refreshTasks()
} else if let error = result as? ApiResultError {
print("WidgetActionProcessor: Failed to complete task \(taskId): \(error.message)")
// Remove action to avoid infinite retries
WidgetDataManager.shared.removeAction(action)
WidgetDataManager.shared.clearPendingState(forTaskId: taskId)
}
} catch {
print("WidgetActionProcessor: Error completing task \(taskId): \(error)")
// Remove action to avoid retries on error
WidgetDataManager.shared.removeAction(action)
WidgetDataManager.shared.clearPendingState(forTaskId: taskId)
}
}
/// Refresh tasks from the server to update UI and widget
private func refreshTasks() async {
do {
let result = try await APILayer.shared.getTasks(forceRefresh: true)
if let success = result as? ApiResultSuccess<TaskColumnsResponse>,
let data = success.data {
// Update widget with fresh data
WidgetDataManager.shared.saveTasks(from: data)
// Update summary from response (no extra API call needed)
if let summary = data.summary {
DataManager.shared.setTotalSummary(summary: summary)
}
}
} catch {
print("WidgetActionProcessor: Error refreshing tasks: \(error)")
}
}
}