Files
honeyDueKMP/iosApp/iosApp/Task/TaskViewModel.swift
Trey t 67e0057bfa Refactor iOS codebase with SOLID/DRY patterns
Core Infrastructure:
- Add StateFlowObserver for reusable Kotlin StateFlow observation
- Add ValidationRules for centralized form validation
- Add ActionState enum for tracking async operations
- Add KotlinTypeExtensions with .asKotlin helpers
- Add Dependencies factory for dependency injection
- Add ViewState, FormField, and FormState for view layer
- Add LoadingOverlay and AsyncContentView components
- Add form state containers (Task, Residence, Contractor, Document)

ViewModel Updates (9 files):
- Refactor all ViewModels to use StateFlowObserver pattern
- Add optional DI support via initializer parameters
- Reduce boilerplate by ~330 lines across ViewModels

View Updates (4 files):
- Update ResidencesListView to use ListAsyncContentView
- Update ContractorsListView to use ListAsyncContentView
- Update WarrantiesTabContent to use ListAsyncContentView
- Update DocumentsTabContent to use ListAsyncContentView

Net reduction: -332 lines (1007 removed, 675 added)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-24 21:15:11 -06:00

182 lines
6.2 KiB
Swift

import Foundation
import ComposeApp
import Combine
@MainActor
class TaskViewModel: ObservableObject {
// MARK: - Published Properties
@Published var actionState: ActionState<TaskActionType> = .idle
@Published var errorMessage: String?
// MARK: - Computed Properties (Backward Compatibility)
var isLoading: Bool { actionState.isLoading }
var taskCreated: Bool { actionState.isSuccess(.create) }
var taskUpdated: Bool { actionState.isSuccess(.update) }
var taskCancelled: Bool { actionState.isSuccess(.cancel) }
var taskUncancelled: Bool { actionState.isSuccess(.uncancel) }
var taskMarkedInProgress: Bool { actionState.isSuccess(.markInProgress) }
var taskArchived: Bool { actionState.isSuccess(.archive) }
var taskUnarchived: Bool { actionState.isSuccess(.unarchive) }
// MARK: - Private Properties
private let sharedViewModel: ComposeApp.TaskViewModel
// MARK: - Initialization
init(sharedViewModel: ComposeApp.TaskViewModel? = nil) {
self.sharedViewModel = sharedViewModel ?? ComposeApp.TaskViewModel()
}
// MARK: - Public Methods
func createTask(request: TaskCreateRequest, completion: @escaping (Bool) -> Void) {
actionState = .loading(.create)
errorMessage = nil
sharedViewModel.createNewTask(request: request)
StateFlowObserver.observeWithCompletion(
sharedViewModel.taskAddNewCustomTaskState,
loadingSetter: { [weak self] loading in
if loading { self?.actionState = .loading(.create) }
},
errorSetter: { [weak self] error in
if let error = error {
self?.actionState = .error(.create, error)
self?.errorMessage = error
}
},
onSuccess: { [weak self] (_: CustomTask) in
self?.actionState = .success(.create)
},
completion: completion,
resetState: { [weak self] in self?.sharedViewModel.resetAddTaskState() }
)
}
func cancelTask(id: Int32, completion: @escaping (Bool) -> Void) {
actionState = .loading(.cancel)
errorMessage = nil
sharedViewModel.cancelTask(taskId: id) { success in
Task { @MainActor in
if success.boolValue {
self.actionState = .success(.cancel)
completion(true)
} else {
let errorMsg = "Failed to cancel task"
self.actionState = .error(.cancel, errorMsg)
self.errorMessage = errorMsg
completion(false)
}
}
}
}
func uncancelTask(id: Int32, completion: @escaping (Bool) -> Void) {
actionState = .loading(.uncancel)
errorMessage = nil
sharedViewModel.uncancelTask(taskId: id) { success in
Task { @MainActor in
if success.boolValue {
self.actionState = .success(.uncancel)
completion(true)
} else {
let errorMsg = "Failed to uncancel task"
self.actionState = .error(.uncancel, errorMsg)
self.errorMessage = errorMsg
completion(false)
}
}
}
}
func markInProgress(id: Int32, completion: @escaping (Bool) -> Void) {
actionState = .loading(.markInProgress)
errorMessage = nil
sharedViewModel.markInProgress(taskId: id) { success in
Task { @MainActor in
if success.boolValue {
self.actionState = .success(.markInProgress)
completion(true)
} else {
let errorMsg = "Failed to mark task in progress"
self.actionState = .error(.markInProgress, errorMsg)
self.errorMessage = errorMsg
completion(false)
}
}
}
}
func archiveTask(id: Int32, completion: @escaping (Bool) -> Void) {
actionState = .loading(.archive)
errorMessage = nil
sharedViewModel.archiveTask(taskId: id) { success in
Task { @MainActor in
if success.boolValue {
self.actionState = .success(.archive)
completion(true)
} else {
let errorMsg = "Failed to archive task"
self.actionState = .error(.archive, errorMsg)
self.errorMessage = errorMsg
completion(false)
}
}
}
}
func unarchiveTask(id: Int32, completion: @escaping (Bool) -> Void) {
actionState = .loading(.unarchive)
errorMessage = nil
sharedViewModel.unarchiveTask(taskId: id) { success in
Task { @MainActor in
if success.boolValue {
self.actionState = .success(.unarchive)
completion(true)
} else {
let errorMsg = "Failed to unarchive task"
self.actionState = .error(.unarchive, errorMsg)
self.errorMessage = errorMsg
completion(false)
}
}
}
}
func updateTask(id: Int32, request: TaskCreateRequest, completion: @escaping (Bool) -> Void) {
actionState = .loading(.update)
errorMessage = nil
sharedViewModel.updateTask(taskId: id, request: request) { success in
Task { @MainActor in
if success.boolValue {
self.actionState = .success(.update)
completion(true)
} else {
let errorMsg = "Failed to update task"
self.actionState = .error(.update, errorMsg)
self.errorMessage = errorMsg
completion(false)
}
}
}
}
func clearError() {
errorMessage = nil
if case .error = actionState {
actionState = .idle
}
}
func resetState() {
actionState = .idle
errorMessage = nil
}
}