Files
multica/apps/ios/Multica/ViewModels/IssueListViewModel.swift
Jiayuan 31c2476a7b feat(ios): add MVP iOS app with issue management and agent log viewing
SwiftUI app (iOS 17+) with zero third-party dependencies:
- Passwordless email authentication with Keychain token storage
- Workspace selection and multi-workspace support
- Issue list grouped by status with search and filtering
- Issue detail with inline status/priority/assignee editing
- Comments with threaded display and compose
- Agent task runs history and real-time execution log streaming
- WebSocket integration for live updates
2026-04-02 23:50:28 +08:00

64 lines
1.8 KiB
Swift

import Foundation
@MainActor
@Observable
final class IssueListViewModel {
var issues: [Issue] = []
var isLoading = false
var error: String?
var statusFilter: IssueStatus?
var searchText = ""
var filteredIssues: [Issue] {
var result = issues
if let statusFilter {
result = result.filter { $0.status == statusFilter }
}
if !searchText.isEmpty {
result = result.filter {
$0.title.localizedCaseInsensitiveContains(searchText) ||
$0.identifier.localizedCaseInsensitiveContains(searchText)
}
}
return result
}
// Group issues by status for sectioned display
var issuesByStatus: [(IssueStatus, [Issue])] {
let grouped = Dictionary(grouping: filteredIssues, by: \.status)
let order: [IssueStatus] = [.inProgress, .todo, .inReview, .blocked, .backlog, .done, .cancelled]
return order.compactMap { status in
guard let issues = grouped[status], !issues.isEmpty else { return nil }
return (status, issues)
}
}
func loadIssues() async {
isLoading = true
error = nil
do {
issues = try await APIClient.shared.listIssues()
} catch {
self.error = error.localizedDescription
}
isLoading = false
}
func refresh() async {
do {
issues = try await APIClient.shared.listIssues()
} catch {
self.error = error.localizedDescription
}
}
func deleteIssue(_ issue: Issue) async {
do {
try await APIClient.shared.deleteIssue(issue.id)
issues.removeAll { $0.id == issue.id }
} catch {
self.error = error.localizedDescription
}
}
}