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}