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>
182 lines
6.2 KiB
Swift
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
|
|
}
|
|
}
|