From 82e54ad018ae5fa5cdf90c0d763e09e99bc0e933 Mon Sep 17 00:00:00 2001 From: Christian Cleberg Date: Sun, 22 Mar 2026 20:42:53 -0500 Subject: feat: swipe actions on tickets, pastes, builds, and home dashboard --- Hutch/Views/Home/HomeView.swift | 87 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) (limited to 'Hutch/Views/Home/HomeView.swift') diff --git a/Hutch/Views/Home/HomeView.swift b/Hutch/Views/Home/HomeView.swift index 55ec41e..d3d370a 100644 --- a/Hutch/Views/Home/HomeView.swift +++ b/Hutch/Views/Home/HomeView.swift @@ -1,6 +1,7 @@ import SwiftUI struct HomeView: View { + @AppStorage(AppStorageKeys.swipeActionsEnabled) private var swipeActionsEnabled = true @Environment(AppState.self) private var appState @State private var viewModel: HomeViewModel? private let previewLimit = 4 @@ -109,6 +110,23 @@ struct HomeView: View { } label: { HomeAssignedTicketRow(ticket: ticket) } + .swipeActions(edge: .leading, allowsFullSwipe: true) { + if swipeActionsEnabled { + ticketLeadingSwipeAction(ticket, viewModel: viewModel) + } + } + .swipeActions(edge: .trailing, allowsFullSwipe: false) { + if swipeActionsEnabled { + Button { + Task { + await viewModel.unassignFromMe(ticket) + } + } label: { + Label("Unassign Me", systemImage: "person.badge.minus") + } + .tint(.orange) + } + } } } } header: { @@ -142,6 +160,19 @@ struct HomeView: View { } label: { HomeBuildRow(build: build) } + .swipeActions(edge: .leading, allowsFullSwipe: true) { + if swipeActionsEnabled, build.job.status.isCancellable { + Button { + Task { + await viewModel.cancelBuild(build) + } + } + label: { + Label("Cancel", systemImage: "xmark.circle") + } + .tint(.red) + } + } } } } header: { @@ -151,6 +182,32 @@ struct HomeView: View { } } + @ViewBuilder + private func ticketLeadingSwipeAction( + _ ticket: HomeAssignedTicket, + viewModel: HomeViewModel + ) -> some View { + if ticket.ticket.status.isOpen { + Button { + Task { + await viewModel.resolveTicket(ticket) + } + } label: { + Label("Resolve", systemImage: "checkmark.circle") + } + .tint(.green) + } else { + Button { + Task { + await viewModel.reopenTicket(ticket) + } + } label: { + Label("Reopen", systemImage: "arrow.uturn.backward") + } + .tint(.blue) + } + } + } private struct HomeInboxToolbarIcon: View { @@ -367,6 +424,7 @@ private struct HomeSectionActionHeader: View { private struct HomeAssignedTicketsListView: View { let viewModel: HomeViewModel + @AppStorage(AppStorageKeys.swipeActionsEnabled) private var swipeActionsEnabled = true var body: some View { List { @@ -382,6 +440,35 @@ private struct HomeAssignedTicketsListView: View { } label: { HomeAssignedTicketRow(ticket: ticket) } + .swipeActions(edge: .leading, allowsFullSwipe: true) { + if swipeActionsEnabled { + if ticket.ticket.status.isOpen { + Button { + Task { await viewModel.resolveTicket(ticket) } + } label: { + Label("Resolve", systemImage: "checkmark.circle") + } + .tint(.green) + } else { + Button { + Task { await viewModel.reopenTicket(ticket) } + } label: { + Label("Reopen", systemImage: "arrow.uturn.backward") + } + .tint(.blue) + } + } + } + .swipeActions(edge: .trailing, allowsFullSwipe: false) { + if swipeActionsEnabled { + Button { + Task { await viewModel.unassignFromMe(ticket) } + } label: { + Label("Unassign Me", systemImage: "person.badge.minus") + } + .tint(.orange) + } + } } if !viewModel.isLoadingAssignedTickets && viewModel.assignedTickets.isEmpty { -- cgit v1.2.3