krz/rune
an ios client for njalla
clone: git clone https://gitbay.org/krz/rune.git
v1.0.0: Rune/Views/Domains/DomainDetailView.swift · raw
1import SwiftUI
2
3struct DomainDetailView: View {
4 let domainName: String
5 @ObservedObject var viewModel: DomainViewModel
6 let client: NjallaClient
7
8 var body: some View {
9 Group {
10 if viewModel.isLoadingDetail && viewModel.selectedDomain?.name != domainName {
11 ProgressView()
12 } else if let domain = currentDomain {
13 List {
14 Section("Status") {
15 DetailRow(label: "Name", value: domain.name)
16 DetailRow(label: "Status", value: textValue(domain.status))
17 DetailRow(label: "Expiry", value: domain.expiry?.formattedExpiry() ?? "Not available")
18 DetailRow(label: "Autorenew", value: boolText(domain.autorenew))
19 }
20
21 Section("Settings") {
22 DetailRow(label: "Mail Forwarding", value: boolText(domain.mailforwarding))
23 DetailRow(label: "DNSSEC", value: boolText(domain.dnssec))
24 DetailRow(label: "Registrar Lock", value: boolText(domain.lock))
25 DetailRow(label: "Nameservers", value: nameserverText(domain.nameservers))
26 }
27
28 Section("DNS") {
29 NavigationLink("Records") {
30 RecordListView(domainName: domain.name, viewModel: viewModel, client: client)
31 }
32 }
33 }
34 .listStyle(.insetGrouped)
35 .toolbar {
36 NavigationLink("Edit") {
37 DomainEditView(domain: domain, viewModel: viewModel, client: client)
38 }
39 }
40 } else {
41 ContentUnavailableView("Domain Unavailable", systemImage: "globe", description: Text("The domain details could not be loaded."))
42 }
43 }
44 .navigationTitle(domainName)
45 .navigationBarTitleDisplayMode(.inline)
46 .task {
47 await viewModel.loadDomainDetail(named: domainName, client: client)
48 }
49 .alert("API Error", isPresented: errorBinding) {
50 Button("OK", role: .cancel) {}
51 } message: {
52 Text(viewModel.errorMessage ?? "")
53 }
54 }
55
56 private var currentDomain: Domain? {
57 if viewModel.selectedDomain?.name == domainName {
58 return viewModel.selectedDomain
59 }
60
61 return viewModel.domains.first(where: { $0.name == domainName })
62 }
63
64 private func boolText(_ value: Bool?) -> String {
65 guard let value else { return "Not available" }
66 return value ? "On" : "Off"
67 }
68
69 private func nameserverText(_ nameservers: [String]?) -> String {
70 guard let nameservers else {
71 return "Not available"
72 }
73
74 guard !nameservers.isEmpty else {
75 return "Default"
76 }
77
78 return nameservers.joined(separator: ", ")
79 }
80
81 private func textValue(_ value: String?) -> String {
82 guard let value, !value.isEmpty else {
83 return "Not available"
84 }
85
86 return value
87 }
88
89 private var errorBinding: Binding<Bool> {
90 Binding(
91 get: { viewModel.errorMessage != nil },
92 set: { newValue in
93 if !newValue {
94 viewModel.errorMessage = nil
95 }
96 }
97 )
98 }
99}
100
101private struct DetailRow: View {
102 let label: String
103 let value: String
104
105 var body: some View {
106 HStack {
107 Text(label)
108 Spacer()
109 Text(value)
110 .foregroundStyle(.secondary)
111 .multilineTextAlignment(.trailing)
112 }
113 }
114}