krz/rune
an ios client for njalla
clone: git clone https://gitbay.org/krz/rune.git
v1.0.0: 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 Group {
12 if viewModel.isLoadingRecords && viewModel.records.isEmpty {
13 ProgressView()
14 } else if viewModel.records.isEmpty {
15 ContentUnavailableView("No Records", systemImage: "list.bullet", description: Text("No DNS records for this domain."))
16 } else {
17 List(viewModel.records) { record in
18 NavigationLink {
19 RecordEditView(domainName: domainName, record: record, viewModel: viewModel, client: client)
20 } label: {
21 RecordRow(record: record)
22 }
23 }
24 .listStyle(.insetGrouped)
25 }
26 }
27 .navigationTitle("DNS Records")
28 .navigationBarTitleDisplayMode(.inline)
29 .toolbar {
30 Button {
31 showingAddRecord = true
32 } label: {
33 Label("Add Record", systemImage: "plus")
34 }
35 }
36 .sheet(isPresented: $showingAddRecord) {
37 NavigationStack {
38 RecordAddView(domainName: domainName, viewModel: viewModel, client: client)
39 }
40 }
41 .task {
42 await viewModel.loadRecords(for: domainName, client: client)
43 }
44 .refreshable {
45 await viewModel.loadRecords(for: domainName, client: client)
46 }
47 .alert("API Error", isPresented: errorBinding) {
48 Button("OK", role: .cancel) {}
49 } message: {
50 Text(viewModel.errorMessage ?? "")
51 }
52 }
53
54 private var errorBinding: Binding<Bool> {
55 Binding(
56 get: { viewModel.errorMessage != nil },
57 set: { newValue in
58 if !newValue {
59 viewModel.errorMessage = nil
60 }
61 }
62 )
63 }
64}
65
66private struct RecordRow: View {
67 let record: DNSRecord
68
69 var body: some View {
70 VStack(alignment: .leading, spacing: 4) {
71 Text("\(record.type) \(record.name)")
72 .font(.headline)
73 if let detail = [record.content, record.target].compactMap({ $0 }).first, !detail.isEmpty {
74 Text(detail)
75 .font(.subheadline)
76 .foregroundStyle(.secondary)
77 }
78 }
79 .padding(.vertical, 4)
80 }
81}