summaryrefslogtreecommitdiff
path: root/Rune/Views/Domains/GlueListView.swift
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-04-13 22:37:22 -0500
committerChristian Cleberg <[email protected]>2026-04-13 22:37:22 -0500
commit5dcfe0f147c7871eaf5f3a88bd9e9fded38d4633 (patch)
tree6bcbda4f02a131d3e3b870a3904ccd42f4af2f26 /Rune/Views/Domains/GlueListView.swift
parentfcf864a15b70e4ecb5bd789b1db3116221c34394 (diff)
downloadrune-1.2.0.tar.gz
rune-1.2.0.tar.bz2
rune-1.2.0.zip
v1.2.0 domain management and wallet expansionv1.2.0
Complete v1.1-aligned domain flows with forwards and glue CRUD, improve API payload resilience, and add wallet transaction/payment views. Harden error handling and UX states across domain screens, remove out-of-scope DNSSEC/renewal record flows for now, and keep auth/session behavior aligned with token-based usage.
Diffstat (limited to 'Rune/Views/Domains/GlueListView.swift')
-rw-r--r--Rune/Views/Domains/GlueListView.swift139
1 files changed, 139 insertions, 0 deletions
diff --git a/Rune/Views/Domains/GlueListView.swift b/Rune/Views/Domains/GlueListView.swift
new file mode 100644
index 0000000..b390a84
--- /dev/null
+++ b/Rune/Views/Domains/GlueListView.swift
@@ -0,0 +1,139 @@
+import SwiftUI
+
+struct GlueListView: View {
+ let domainName: String
+ @ObservedObject var viewModel: DomainViewModel
+ let client: NjallaClient
+
+ @State private var showingAddGlue = false
+ @State private var recordPendingDeletion: GlueRecord?
+
+ var body: some View {
+ List {
+ if let errorMessage = viewModel.glueErrorMessage {
+ Section {
+ InlineErrorView(message: errorMessage, retryTitle: "Retry Glue Records") {
+ Task {
+ await viewModel.loadGlue(for: domainName, client: client)
+ }
+ }
+ .listRowInsets(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
+ }
+ }
+
+ if viewModel.isLoadingGlue && viewModel.glueRecords.isEmpty {
+ Section {
+ HStack {
+ Spacer()
+ ProgressView("Loading Glue")
+ Spacer()
+ }
+ }
+ } else if viewModel.glueRecords.isEmpty {
+ Section {
+ ContentUnavailableView(
+ "No Glue Records",
+ systemImage: "list.bullet.rectangle",
+ description: Text("No glue records are configured for this domain.")
+ )
+ }
+ } else {
+ ForEach(viewModel.glueRecords) { record in
+ NavigationLink {
+ GlueEditView(domainName: domainName, existingRecord: record, viewModel: viewModel, client: client)
+ } label: {
+ VStack(alignment: .leading, spacing: 4) {
+ Text(record.name)
+ .font(.headline)
+ Text("IPv4: \(record.address4 ?? "n/a")")
+ .font(.subheadline)
+ .foregroundStyle(.secondary)
+ Text("IPv6: \(record.address6 ?? "n/a")")
+ .font(.subheadline)
+ .foregroundStyle(.secondary)
+ }
+ .padding(.vertical, 4)
+ }
+ .swipeActions {
+ Button("Delete", role: .destructive) {
+ recordPendingDeletion = record
+ }
+ }
+ .disabled(viewModel.isSaving)
+ }
+ }
+ }
+ .listStyle(.insetGrouped)
+ .navigationTitle("Glue Records")
+ .navigationBarTitleDisplayMode(.inline)
+ .toolbar {
+ Button {
+ showingAddGlue = true
+ } label: {
+ Label("Add Glue", systemImage: "plus")
+ }
+ .disabled(viewModel.isSaving)
+ }
+ .sheet(isPresented: $showingAddGlue) {
+ NavigationStack {
+ GlueEditView(domainName: domainName, existingRecord: nil, viewModel: viewModel, client: client)
+ }
+ }
+ .task {
+ await viewModel.loadGlue(for: domainName, client: client)
+ }
+ .refreshable {
+ await viewModel.loadGlue(for: domainName, client: client)
+ }
+ .alert(deleteAlertTitle, isPresented: deleteBinding) {
+ Button("Delete Glue", role: .destructive) {
+ guard let recordPendingDeletion else { return }
+ Task {
+ do {
+ try await viewModel.removeGlue(recordPendingDeletion, client: client)
+ self.recordPendingDeletion = nil
+ } catch {
+ return
+ }
+ }
+ }
+ Button("Cancel", role: .cancel) {
+ recordPendingDeletion = nil
+ }
+ } message: {
+ Text("Delete glue record \(recordPendingDeletion?.name ?? "")?")
+ }
+ .alert("Request Failed", isPresented: mutationErrorBinding) {
+ Button("OK", role: .cancel) {}
+ } message: {
+ Text(viewModel.mutationErrorMessage ?? "")
+ }
+ }
+
+ private var deleteAlertTitle: String {
+ guard let recordPendingDeletion else { return "" }
+ return "Delete glue record \(recordPendingDeletion.name)?"
+ }
+
+ private var deleteBinding: Binding<Bool> {
+ Binding(
+ get: { recordPendingDeletion != nil },
+ set: { newValue in
+ if !newValue {
+ recordPendingDeletion = nil
+ }
+ }
+ )
+ }
+
+ private var mutationErrorBinding: Binding<Bool> {
+ Binding(
+ get: { viewModel.mutationErrorMessage != nil },
+ set: { newValue in
+ if !newValue {
+ viewModel.dismissMutationError()
+ }
+ }
+ )
+ }
+}