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}