diff options
| author | Christian Cleberg <[email protected]> | 2026-04-13 09:57:23 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-04-13 09:57:23 -0500 |
| commit | 0e461a382257979037e8927e5f6b468635b0a7fe (patch) | |
| tree | 0aa3a19f6f8f966b0e90f4874ed54359a7898107 /Hutch/Views/Tickets/TicketDetailViewModel.swift | |
| parent | ee9f2904aa319231b7047fca3cb069f0c07019cd (diff) | |
| download | hutch-0e461a382257979037e8927e5f6b468635b0a7fe.tar.gz hutch-0e461a382257979037e8927e5f6b468635b0a7fe.tar.bz2 hutch-0e461a382257979037e8927e5f6b468635b0a7fe.zip | |
feat: add power user actions
Implements: https://todo.sr.ht/~ccleberg/hutch/52
Implements: https://todo.sr.ht/~ccleberg/hutch/53
Implements: https://todo.sr.ht/~ccleberg/hutch/54
Diffstat (limited to 'Hutch/Views/Tickets/TicketDetailViewModel.swift')
| -rw-r--r-- | Hutch/Views/Tickets/TicketDetailViewModel.swift | 66 |
1 files changed, 60 insertions, 6 deletions
diff --git a/Hutch/Views/Tickets/TicketDetailViewModel.swift b/Hutch/Views/Tickets/TicketDetailViewModel.swift index 999374b..483f749 100644 --- a/Hutch/Views/Tickets/TicketDetailViewModel.swift +++ b/Hutch/Views/Tickets/TicketDetailViewModel.swift @@ -97,6 +97,9 @@ private struct LabelsPage: Decodable, Sendable { @Observable @MainActor final class TicketDetailViewModel { + private static func cacheKey(ownerUsername: String, trackerRid: String, ticketId: Int) -> String { + "ticket.detail.\(ownerUsername).\(trackerRid).\(ticketId)" + } let ownerUsername: String let trackerName: String @@ -110,6 +113,7 @@ final class TicketDetailViewModel { private(set) var isSubmitting = false private(set) var isPerformingAction = false private(set) var trackerLabels: [TicketLabel] = [] + private(set) var rawTicketResponse: String? var commentText = "" var error: String? @@ -284,6 +288,7 @@ final class TicketDetailViewModel { guard !isLoading else { return } isLoading = true error = nil + rawTicketResponse = nil do { let result = try await client.execute( @@ -317,6 +322,47 @@ final class TicketDetailViewModel { isLoading = false } + func loadTicketWithDebugCapture() async { + guard !isLoading else { return } + isLoading = true + error = nil + + do { + let cacheKey = Self.cacheKey(ownerUsername: ownerUsername, trackerRid: trackerRid, ticketId: ticketId) + let result = try await client.executeAndCache( + service: .todo, + query: Self.detailQuery, + variables: [ + "rid": trackerRid, + "ticketId": ticketId + ], + responseType: TicketDetailResponse.self, + cacheKey: cacheKey + ) + rawTicketResponse = client.responseCache.get(forKey: cacheKey) + .flatMap { String(data: $0, encoding: .utf8) } + let payload = result.tracker.ticket + ticket = TicketDetail( + id: payload.id, + created: payload.created, + updated: payload.updated, + title: payload.title, + description: payload.description, + status: payload.status, + resolution: payload.resolution, + authenticity: payload.authenticity, + submitter: payload.submitter, + assignees: payload.assignees, + labels: payload.labels + ) + events = payload.events.results.sorted(by: Self.timelineOrder) + } catch { + self.error = error.userFacingMessage + } + + isLoading = false + } + func submitComment() async { let text = commentText.trimmingCharacters(in: .whitespacesAndNewlines) guard !text.isEmpty, !isSubmitting else { return } @@ -378,7 +424,7 @@ final class TicketDetailViewModel { responseType: UpdateStatusResponse.self ) // Re-fetch the ticket to get updated status/resolution - await loadTicket() + await reloadTicketPreservingDebugState() } catch { self.error = error.userFacingMessage } @@ -412,7 +458,7 @@ final class TicketDetailViewModel { responseType: AssignUserResponse.self ) // Reload to reflect the change - await loadTicket() + await reloadTicketPreservingDebugState() } catch { self.error = error.userFacingMessage } @@ -457,7 +503,7 @@ final class TicketDetailViewModel { ], responseType: AssignUserResponse.self ) - await loadTicket() + await reloadTicketPreservingDebugState() } catch { ticket = TicketDetail( id: currentTicket.id, @@ -505,7 +551,7 @@ final class TicketDetailViewModel { responseType: UnassignUserResponse.self ) // Reload to reflect the change - await loadTicket() + await reloadTicketPreservingDebugState() } catch { self.error = error.userFacingMessage } @@ -529,7 +575,7 @@ final class TicketDetailViewModel { ], responseType: LabelTicketResponse.self ) - await loadTicket() + await reloadTicketPreservingDebugState() } catch { self.error = error.userFacingMessage } @@ -553,7 +599,7 @@ final class TicketDetailViewModel { ], responseType: UnlabelTicketResponse.self ) - await loadTicket() + await reloadTicketPreservingDebugState() } catch { self.error = error.userFacingMessage } @@ -600,6 +646,14 @@ final class TicketDetailViewModel { isPerformingAction = false } + private func reloadTicketPreservingDebugState() async { + if rawTicketResponse != nil { + await loadTicketWithDebugCapture() + } else { + await loadTicket() + } + } + static func matchesAssignee(_ entity: Entity, user: User) -> Bool { let assigneeCanonical = normalizedCanonicalName(entity.canonicalName) let userCanonical = normalizedCanonicalName(user.canonicalName) |
