krz/rune
an ios client for njalla
clone: git clone https://gitbay.org/krz/rune.git
v1.2.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("Email") {
29 NavigationLink("Forwards") {
30 ForwardListView(domainName: domain.name, viewModel: viewModel, client: client)
31 }
32 }
33
34 Section("DNS") {
35 NavigationLink("Records") {
36 RecordListView(domainName: domain.name, viewModel: viewModel, client: client)
37 }
38 NavigationLink("Glue Records") {
39 GlueListView(domainName: domain.name, viewModel: viewModel, client: client)
40 }
41 }
42
43 }
44 .listStyle(.insetGrouped)
45 .toolbar {
46 NavigationLink("Edit") {
47 DomainEditView(domain: domain, viewModel: viewModel, client: client)
48 }
49 }
50 } else {
51 ContentUnavailableView("Domain Unavailable", systemImage: "globe", description: Text("The domain details could not be loaded."))
52 }
53 }
54 .navigationTitle(domainName)
55 .navigationBarTitleDisplayMode(.inline)
56 .task {
57 await viewModel.loadDomainDetail(named: domainName, client: client)
58 }
59 .alert("API Error", isPresented: errorBinding) {
60 Button("OK", role: .cancel) {}
61 } message: {
62 Text(viewModel.detailErrorMessage ?? "")
63 }
64 .alert("Request Failed", isPresented: mutationErrorBinding) {
65 Button("OK", role: .cancel) {}
66 } message: {
67 Text(viewModel.mutationErrorMessage ?? "")
68 }
69 }
70
71 private var currentDomain: Domain? {
72 if viewModel.selectedDomain?.name == domainName {
73 return viewModel.selectedDomain
74 }
75
76 return viewModel.domains.first(where: { $0.name == domainName })
77 }
78
79 private func boolText(_ value: Bool?) -> String {
80 guard let value else { return "Not available" }
81 return value ? "On" : "Off"
82 }
83
84 private func nameserverText(_ nameservers: [String]?) -> String {
85 guard let nameservers else {
86 return "Njalla"
87 }
88
89 guard !nameservers.isEmpty else {
90 return "Njalla"
91 }
92
93 return nameservers.joined(separator: ", ")
94 }
95
96 private func textValue(_ value: String?) -> String {
97 guard let value, !value.isEmpty else {
98 return "Not available"
99 }
100
101 return value
102 }
103
104 private var errorBinding: Binding<Bool> {
105 Binding(
106 get: { viewModel.detailErrorMessage != nil },
107 set: { newValue in
108 if !newValue {
109 viewModel.detailErrorMessage = nil
110 }
111 }
112 )
113 }
114
115 private var mutationErrorBinding: Binding<Bool> {
116 Binding(
117 get: { viewModel.mutationErrorMessage != nil },
118 set: { newValue in
119 if !newValue {
120 viewModel.dismissMutationError()
121 }
122 }
123 )
124 }
125}
126
127private struct DetailRow: View {
128 let label: String
129 let value: String
130
131 var body: some View {
132 HStack {
133 Text(label)
134 Spacer()
135 Text(value)
136 .foregroundStyle(.secondary)
137 .multilineTextAlignment(.trailing)
138 }
139 }
140}