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