krz/rune

an ios client for njalla

clone: git clone https://gitbay.org/krz/rune.git

v1.2.0: Rune/Views/Domains/GlueListView.swift · raw

  1import SwiftUI
  2
  3struct GlueListView: View {
  4    let domainName: String
  5    @ObservedObject var viewModel: DomainViewModel
  6    let client: NjallaClient
  7
  8    @State private var showingAddGlue = false
  9    @State private var recordPendingDeletion: GlueRecord?
 10
 11    var body: some View {
 12        List {
 13            if let errorMessage = viewModel.glueErrorMessage {
 14                Section {
 15                    InlineErrorView(message: errorMessage, retryTitle: "Retry Glue Records") {
 16                        Task {
 17                            await viewModel.loadGlue(for: domainName, client: client)
 18                        }
 19                    }
 20                    .listRowInsets(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
 21                }
 22            }
 23
 24            if viewModel.isLoadingGlue && viewModel.glueRecords.isEmpty {
 25                Section {
 26                    HStack {
 27                        Spacer()
 28                        ProgressView("Loading Glue")
 29                        Spacer()
 30                    }
 31                }
 32            } else if viewModel.glueRecords.isEmpty {
 33                Section {
 34                    ContentUnavailableView(
 35                        "No Glue Records",
 36                        systemImage: "list.bullet.rectangle",
 37                        description: Text("No glue records are configured for this domain.")
 38                    )
 39                }
 40            } else {
 41                ForEach(viewModel.glueRecords) { record in
 42                    NavigationLink {
 43                        GlueEditView(domainName: domainName, existingRecord: record, viewModel: viewModel, client: client)
 44                    } label: {
 45                        VStack(alignment: .leading, spacing: 4) {
 46                            Text(record.name)
 47                                .font(.headline)
 48                            Text("IPv4: \(record.address4 ?? "n/a")")
 49                                .font(.subheadline)
 50                                .foregroundStyle(.secondary)
 51                            Text("IPv6: \(record.address6 ?? "n/a")")
 52                                .font(.subheadline)
 53                                .foregroundStyle(.secondary)
 54                        }
 55                        .padding(.vertical, 4)
 56                    }
 57                    .swipeActions {
 58                        Button("Delete", role: .destructive) {
 59                            recordPendingDeletion = record
 60                        }
 61                    }
 62                    .disabled(viewModel.isSaving)
 63                }
 64            }
 65        }
 66        .listStyle(.insetGrouped)
 67        .navigationTitle("Glue Records")
 68        .navigationBarTitleDisplayMode(.inline)
 69        .toolbar {
 70            Button {
 71                showingAddGlue = true
 72            } label: {
 73                Label("Add Glue", systemImage: "plus")
 74            }
 75            .disabled(viewModel.isSaving)
 76        }
 77        .sheet(isPresented: $showingAddGlue) {
 78            NavigationStack {
 79                GlueEditView(domainName: domainName, existingRecord: nil, viewModel: viewModel, client: client)
 80            }
 81        }
 82        .task {
 83            await viewModel.loadGlue(for: domainName, client: client)
 84        }
 85        .refreshable {
 86            await viewModel.loadGlue(for: domainName, client: client)
 87        }
 88        .alert(deleteAlertTitle, isPresented: deleteBinding) {
 89            Button("Delete Glue", role: .destructive) {
 90                guard let recordPendingDeletion else { return }
 91                Task {
 92                    do {
 93                        try await viewModel.removeGlue(recordPendingDeletion, client: client)
 94                        self.recordPendingDeletion = nil
 95                    } catch {
 96                        return
 97                    }
 98                }
 99            }
100            Button("Cancel", role: .cancel) {
101                recordPendingDeletion = nil
102            }
103        } message: {
104            Text("Delete glue record \(recordPendingDeletion?.name ?? "")?")
105        }
106        .alert("Request Failed", isPresented: mutationErrorBinding) {
107            Button("OK", role: .cancel) {}
108        } message: {
109            Text(viewModel.mutationErrorMessage ?? "")
110        }
111    }
112
113    private var deleteAlertTitle: String {
114        guard let recordPendingDeletion else { return "" }
115        return "Delete glue record \(recordPendingDeletion.name)?"
116    }
117
118    private var deleteBinding: Binding<Bool> {
119        Binding(
120            get: { recordPendingDeletion != nil },
121            set: { newValue in
122                if !newValue {
123                    recordPendingDeletion = nil
124                }
125            }
126        )
127    }
128
129    private var mutationErrorBinding: Binding<Bool> {
130        Binding(
131            get: { viewModel.mutationErrorMessage != nil },
132            set: { newValue in
133                if !newValue {
134                    viewModel.dismissMutationError()
135                }
136            }
137        )
138    }
139}