summaryrefslogtreecommitdiff
path: root/Hutch/Views/Pastes
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-03-22 20:42:53 -0500
committerChristian Cleberg <[email protected]>2026-03-22 20:42:53 -0500
commit82e54ad018ae5fa5cdf90c0d763e09e99bc0e933 (patch)
treecafdbc8f4116af5cc6e0ca706e05ced599e26570 /Hutch/Views/Pastes
parent1f8b7a23529462c3c332753d33810b5c9f692bbc (diff)
downloadhutch-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.swift77
-rw-r--r--Hutch/Views/Pastes/PasteListViewModel.swift41
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