Files
multica/apps/ios/Multica/ViewModels/WorkspaceViewModel.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

71 lines
2.3 KiB
Swift

import Foundation
@MainActor
@Observable
final class WorkspaceViewModel {
var workspaces: [Workspace] = []
var selectedWorkspace: Workspace?
var members: [Member] = []
var agents: [Agent] = []
var isLoading = false
var error: String?
var hasSelectedWorkspace: Bool {
selectedWorkspace != nil
}
func loadWorkspaces() async {
isLoading = true
error = nil
do {
workspaces = try await APIClient.shared.listWorkspaces()
// Auto-select if there's a saved workspace or only one
if let savedId = APIClient.shared.workspaceId,
let saved = workspaces.first(where: { $0.id == savedId }) {
await selectWorkspace(saved)
} else if workspaces.count == 1 {
await selectWorkspace(workspaces[0])
}
} catch {
self.error = error.localizedDescription
}
isLoading = false
}
func selectWorkspace(_ workspace: Workspace) async {
selectedWorkspace = workspace
APIClient.shared.workspaceId = workspace.id
WebSocketClient.shared.disconnect()
WebSocketClient.shared.connect()
await loadWorkspaceData()
}
func loadWorkspaceData() async {
guard let workspace = selectedWorkspace else { return }
do {
async let membersResult = APIClient.shared.listMembers(workspaceId: workspace.id)
async let agentsResult = APIClient.shared.listAgents()
members = try await membersResult
agents = try await agentsResult
} catch {
self.error = error.localizedDescription
}
}
/// All possible assignees (members + agents)
var assignees: [Assignee] {
let memberAssignees = members.map { Assignee.member($0) }
let agentAssignees = agents.map { Assignee.agent($0) }
return memberAssignees + agentAssignees
}
func assigneeName(type: String?, id: String?) -> String {
guard let type, let id else { return "Unassigned" }
if type == "agent" {
return agents.first(where: { $0.id == id })?.name ?? "Agent"
} else {
return members.first(where: { $0.userId == id })?.displayName ?? "Member"
}
}
}