krz/rune
an ios client for njalla
clone: git clone https://gitbay.org/krz/rune.git
main: Rune/Views/Domains/RecordListView.swift · raw
1import SwiftUI
2
3struct RecordListView: View {
4 let domainName: String
5 @ObservedObject var viewModel: DomainViewModel
6 let client: NjallaClient
7
8 @State private var showingAddRecord = false
9
10 var body: some View {
11 List {
12 if let errorMessage = viewModel.recordsErrorMessage {
13 Section {
14 InlineErrorView(message: errorMessage, retryTitle: "Retry Records") {
15 Task {
16 await viewModel.loadRecords(for: domainName, client: client)
17 }
18 }
19 .listRowInsets(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
20 }
21 }
22
23 if viewModel.isLoadingRecords && viewModel.records.isEmpty {
24 Section {
25 HStack {
26 Spacer()
27 ProgressView("Loading Records")
28 Spacer()
29 }
30 }
31 } else if viewModel.records.isEmpty {
32 Section {
33 ContentUnavailableView(
34 "No Records",
35 systemImage: "list.bullet",
36 description: Text("No DNS records for this domain yet. Add a record to get started.")
37 )
38 }
39 } else {
40 ForEach(viewModel.records) { record in
41 NavigationLink {
42 RecordEditView(domainName: domainName, record: record, viewModel: viewModel, client: client)
43 } label: {
44 RecordRow(record: record)
45 }
46 }
47 }
48 }
49 .listStyle(.insetGrouped)
50 .navigationTitle("DNS Records")
51 .navigationBarTitleDisplayMode(.inline)
52 .toolbar {
53 Button {
54 showingAddRecord = true
55 } label: {
56 Label("Add Record", systemImage: "plus")
57 }
58 }
59 .sheet(isPresented: $showingAddRecord) {
60 NavigationStack {
61 RecordAddView(domainName: domainName, viewModel: viewModel, client: client)
62 }
63 }
64 .task {
65 await viewModel.loadRecords(for: domainName, client: client)
66 }
67 .onAppear {
68 viewModel.startAutoRefreshRecords(for: domainName, client: client)
69 }
70 .onDisappear {
71 viewModel.stopAutoRefreshRecords()
72 }
73 .refreshable {
74 await viewModel.loadRecords(for: domainName, client: client)
75 }
76 .alert("Request Failed", isPresented: mutationErrorBinding) {
77 Button("OK", role: .cancel) {}
78 } message: {
79 Text(viewModel.mutationErrorMessage ?? "")
80 }
81 }
82
83 private var mutationErrorBinding: Binding<Bool> {
84 Binding(
85 get: { viewModel.mutationErrorMessage != nil },
86 set: { newValue in
87 if !newValue {
88 viewModel.dismissMutationError()
89 }
90 }
91 )
92 }
93}
94
95private struct RecordRow: View {
96 let record: DNSRecord
97
98 var body: some View {
99 VStack(alignment: .leading, spacing: 4) {
100 Text("\(record.type) \(record.name)")
101 .font(.headline)
102 if let detail = [record.content, record.target].compactMap({ $0 }).first, !detail.isEmpty {
103 Text(detail)
104 .font(.subheadline)
105 .foregroundStyle(.secondary)
106 }
107 }
108 .padding(.vertical, 4)
109 }
110}