summaryrefslogtreecommitdiff
path: root/Hutch/Views/Tickets/TicketDetailViewModel.swift
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-03-19 15:18:38 -0500
committerChristian Cleberg <[email protected]>2026-03-19 15:18:38 -0500
commit659c76df9ff3926cb68886c16167808c23bd8f75 (patch)
tree572bef546fcca19ad37bc1aa7cfb153eb2bf8eb7 /Hutch/Views/Tickets/TicketDetailViewModel.swift
parented48e72520c51e4635cd3840dbc569bdd74c950f (diff)
parent6ba4e967d5dfb5d3c7bb97a0f2662f3180595563 (diff)
downloadhutch-2.tar.gz
hutch-2.tar.bz2
hutch-2.zip
v2.0: Merge branch 'dev'v2
Diffstat (limited to 'Hutch/Views/Tickets/TicketDetailViewModel.swift')
-rw-r--r--Hutch/Views/Tickets/TicketDetailViewModel.swift80
1 files changed, 80 insertions, 0 deletions
diff --git a/Hutch/Views/Tickets/TicketDetailViewModel.swift b/Hutch/Views/Tickets/TicketDetailViewModel.swift
index 90e33aa..660715b 100644
--- a/Hutch/Views/Tickets/TicketDetailViewModel.swift
+++ b/Hutch/Views/Tickets/TicketDetailViewModel.swift
@@ -433,6 +433,64 @@ final class TicketDetailViewModel {
isPerformingAction = false
}
+ func assignToCurrentUser(_ user: User) async {
+ guard !isPerformingAction, let currentTicket = ticket else { return }
+
+ let currentAssignees = currentTicket.assignees
+ let currentEntity = Entity(canonicalName: user.canonicalName)
+ guard !currentAssignees.contains(where: { Self.matchesAssignee($0, user: user) }) else {
+ return
+ }
+
+ isPerformingAction = true
+ error = nil
+
+ ticket = TicketDetail(
+ id: currentTicket.id,
+ created: currentTicket.created,
+ updated: currentTicket.updated,
+ title: currentTicket.title,
+ description: currentTicket.description,
+ status: currentTicket.status,
+ resolution: currentTicket.resolution,
+ authenticity: currentTicket.authenticity,
+ submitter: currentTicket.submitter,
+ assignees: currentAssignees + [currentEntity],
+ labels: currentTicket.labels
+ )
+
+ do {
+ _ = try await client.execute(
+ service: .todo,
+ query: Self.assignUserMutation,
+ variables: [
+ "trackerId": trackerId,
+ "ticketId": ticketId,
+ "userId": user.id
+ ],
+ responseType: AssignUserResponse.self
+ )
+ await loadTicket()
+ } catch {
+ ticket = TicketDetail(
+ id: currentTicket.id,
+ created: currentTicket.created,
+ updated: currentTicket.updated,
+ title: currentTicket.title,
+ description: currentTicket.description,
+ status: currentTicket.status,
+ resolution: currentTicket.resolution,
+ authenticity: currentTicket.authenticity,
+ submitter: currentTicket.submitter,
+ assignees: currentAssignees,
+ labels: currentTicket.labels
+ )
+ self.error = error.localizedDescription
+ }
+
+ isPerformingAction = false
+ }
+
func unassignUser(username: String) async {
guard !isPerformingAction else { return }
isPerformingAction = true
@@ -558,4 +616,26 @@ final class TicketDetailViewModel {
isPerformingAction = false
}
+ static func matchesAssignee(_ entity: Entity, user: User) -> Bool {
+ let assigneeCanonical = normalizedCanonicalName(entity.canonicalName)
+ let userCanonical = normalizedCanonicalName(user.canonicalName)
+ if assigneeCanonical == userCanonical {
+ return true
+ }
+ return normalizedUsername(entity.canonicalName) == normalizedUsername(user.username)
+ }
+
+ private static func normalizedCanonicalName(_ value: String) -> String {
+ let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
+ if trimmed.hasPrefix("~") {
+ return trimmed
+ }
+ return "~\(trimmed)"
+ }
+
+ private static func normalizedUsername(_ value: String) -> String {
+ let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
+ return trimmed.hasPrefix("~") ? String(trimmed.dropFirst()) : trimmed
+ }
+
}