diff options
| author | Christian Cleberg <[email protected]> | 2026-03-22 20:42:53 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-03-22 20:42:53 -0500 |
| commit | 82e54ad018ae5fa5cdf90c0d763e09e99bc0e933 (patch) | |
| tree | cafdbc8f4116af5cc6e0ca706e05ced599e26570 /Hutch/Views/Pastes | |
| parent | 1f8b7a23529462c3c332753d33810b5c9f692bbc (diff) | |
| download | hutch-82e54ad018ae5fa5cdf90c0d763e09e99bc0e933.tar.gz hutch-82e54ad018ae5fa5cdf90c0d763e09e99bc0e933.tar.bz2 hutch-82e54ad018ae5fa5cdf90c0d763e09e99bc0e933.zip | |
feat: swipe actions on tickets, pastes, builds, and home dashboard
Diffstat (limited to 'Hutch/Views/Pastes')
| -rw-r--r-- | Hutch/Views/Pastes/PasteListView.swift | 77 | ||||
| -rw-r--r-- | Hutch/Views/Pastes/PasteListViewModel.swift | 41 |
2 files changed, 117 insertions, 1 deletions
diff --git a/Hutch/Views/Pastes/PasteListView.swift b/Hutch/Views/Pastes/PasteListView.swift index 84f2926..22f6ff9 100644 --- a/Hutch/Views/Pastes/PasteListView.swift +++ b/Hutch/Views/Pastes/PasteListView.swift @@ -1,10 +1,12 @@ import SwiftUI struct PasteListView: View { + @AppStorage(AppStorageKeys.swipeActionsEnabled) private var swipeActionsEnabled = true @Environment(AppState.self) private var appState @State private var viewModel: PasteListViewModel? @State private var showCreatePasteSheet = false @State private var createdPaste: Paste? + @State private var pasteToDelete: Paste? var body: some View { Group { @@ -68,10 +70,32 @@ struct PasteListView: View { @Bindable var vm = viewModel List { - ForEach(viewModel.pastes) { paste in + ForEach(viewModel.filteredPastes) { paste in NavigationLink(value: paste) { PasteRowView(paste: paste) } + .swipeActions(edge: .leading, allowsFullSwipe: true) { + if swipeActionsEnabled { + Button { + Task { await viewModel.cycleVisibility(for: paste) } + } label: { + Label( + nextVisibilityLabel(for: paste.visibility), + systemImage: nextVisibilityIcon(for: paste.visibility) + ) + } + .tint(nextVisibilityColor(for: paste.visibility)) + } + } + .swipeActions(edge: .trailing, allowsFullSwipe: false) { + if swipeActionsEnabled { + Button(role: .destructive) { + pasteToDelete = paste + } label: { + Label("Delete", systemImage: "trash") + } + } + } .task { await viewModel.loadMoreIfNeeded(currentItem: paste) } @@ -115,6 +139,24 @@ struct PasteListView: View { await viewModel.loadPastes() } .srhtErrorBanner(error: $vm.error) + .alert("Delete Paste?", isPresented: Binding( + get: { pasteToDelete != nil }, + set: { if !$0 { pasteToDelete = nil } } + )) { + Button("Cancel", role: .cancel) { + pasteToDelete = nil + } + Button("Delete", role: .destructive) { + if let paste = pasteToDelete { + pasteToDelete = nil + Task { + await viewModel.deletePaste(paste) + } + } + } + } message: { + Text("This paste will be permanently deleted from SourceHut.") + } .refreshable { await viewModel.loadPastes() } @@ -130,6 +172,39 @@ struct PasteListView: View { ) } } + + private func nextVisibilityLabel(for visibility: Visibility) -> String { + switch visibility { + case .public: + return "Make Unlisted" + case .unlisted: + return "Make Private" + case .private: + return "Make Public" + } + } + + private func nextVisibilityIcon(for visibility: Visibility) -> String { + switch visibility { + case .public: + return "eye.slash" + case .unlisted: + return "lock" + case .private: + return "globe" + } + } + + private func nextVisibilityColor(for visibility: Visibility) -> Color { + switch visibility { + case .public: + return .orange + case .unlisted: + return .red + case .private: + return .green + } + } } private struct PasteRowView: View { diff --git a/Hutch/Views/Pastes/PasteListViewModel.swift b/Hutch/Views/Pastes/PasteListViewModel.swift index d41cf96..b521e18 100644 --- a/Hutch/Views/Pastes/PasteListViewModel.swift +++ b/Hutch/Views/Pastes/PasteListViewModel.swift @@ -107,6 +107,47 @@ final class PasteListViewModel { } } + func deletePaste(_ paste: Paste) async { + do { + _ = try await service.deletePaste(id: paste.id) + removePaste(id: paste.id) + } catch { + self.error = error.userFacingMessage + } + } + + func cycleVisibility(for paste: Paste) async { + let next: Visibility + switch paste.visibility { + case .public: + next = .unlisted + case .unlisted: + next = .private + case .private: + next = .public + } + + let original = pastes + if let index = pastes.firstIndex(where: { $0.id == paste.id }) { + pastes[index] = Paste( + id: paste.id, + created: paste.created, + visibility: next, + files: paste.files, + user: paste.user + ) + } + + do { + if let updated = try await service.updateVisibility(id: paste.id, visibility: next) { + upsertPaste(updated) + } + } catch { + pastes = original + self.error = error.userFacingMessage + } + } + func upsertPaste(_ paste: Paste) { if let index = pastes.firstIndex(where: { $0.id == paste.id }) { pastes[index] = paste |
