diff options
Diffstat (limited to 'Hutch/Views/Lists/MailingListListView.swift')
| -rw-r--r-- | Hutch/Views/Lists/MailingListListView.swift | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/Hutch/Views/Lists/MailingListListView.swift b/Hutch/Views/Lists/MailingListListView.swift index 2fcc640..09fa2a0 100644 --- a/Hutch/Views/Lists/MailingListListView.swift +++ b/Hutch/Views/Lists/MailingListListView.swift @@ -5,6 +5,7 @@ import SwiftUI final class MailingListListViewModel { private(set) var mailingLists: [InboxMailingListReference] = [] private(set) var isLoading = false + private(set) var isPerformingAction = false var error: String? var searchText = "" @@ -28,10 +29,51 @@ final class MailingListListViewModel { } """ + private static let unsubscribeMutation = """ + mutation mailingListUnsubscribe($listID: Int!) { + subscription: mailingListUnsubscribe(listID: $listID) { id } + } + """ + init(client: SRHTClient) { self.client = client } + /// Unsubscribes from a list and drops it from the list on success. This view + /// is built from the subscriptions query, so a successful unsubscribe means + /// the row no longer belongs here. + func unsubscribe(from mailingList: InboxMailingListReference) async { + guard !isPerformingAction else { return } + isPerformingAction = true + error = nil + defer { isPerformingAction = false } + + let previousLists = mailingLists + mailingLists.removeAll { $0.rid == mailingList.rid } + + do { + struct Response: Decodable, Sendable { + // mailingListUnsubscribe is nullable: sr.ht returns null when there + // was no subscription to remove, which is still a success. + let subscription: SubscriptionPayload? + } + + struct SubscriptionPayload: Decodable, Sendable { + let id: Int + } + + _ = try await client.execute( + service: .lists, + query: Self.unsubscribeMutation, + variables: ["listID": mailingList.id], + responseType: Response.self + ) + } catch { + mailingLists = previousLists + self.error = "Couldn't unsubscribe from \(mailingList.name). \(error.userFacingMessage)" + } + } + var filteredMailingLists: [InboxMailingListReference] { let q = searchText.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() guard !q.isEmpty else { return mailingLists } @@ -106,6 +148,7 @@ final class MailingListListViewModel { struct MailingListListView: View { @Environment(AppState.self) private var appState @State private var viewModel: MailingListListViewModel? + @State private var pendingUnsubscribe: InboxMailingListReference? var body: some View { Group { @@ -141,6 +184,14 @@ struct MailingListListView: View { } .padding(.vertical, 2) } + .swipeActions(edge: .trailing) { + Button { + pendingUnsubscribe = mailingList + } label: { + SwiftUI.Label("Unsubscribe", systemImage: "bell.slash") + } + .tint(.orange) + } } .themedRow() } @@ -151,6 +202,22 @@ struct MailingListListView: View { placement: .navigationBarDrawer(displayMode: .always), prompt: "Search lists" ) + .confirmationDialog( + pendingUnsubscribe.map { "Unsubscribe from \($0.name)?" } ?? "", + isPresented: .init( + get: { pendingUnsubscribe != nil }, + set: { if !$0 { pendingUnsubscribe = nil } } + ), + titleVisibility: .visible, + presenting: pendingUnsubscribe + ) { mailingList in + Button("Unsubscribe", role: .destructive) { + Task { await viewModel.unsubscribe(from: mailingList) } + } + Button("Cancel", role: .cancel) { pendingUnsubscribe = nil } + } message: { _ in + Text("You will stop receiving email from this list. Hutch cannot resubscribe you — you would need to do that from the list's page on the web.") + } .overlay { if viewModel.isLoading, viewModel.mailingLists.isEmpty { SRHTLoadingStateView(message: "Loading mailing lists…") |
