krz/rune

an ios client for njalla

clone: git clone https://gitbay.org/krz/rune.git

v1.2.0: Rune/Views/Domains/GlueEditView.swift · raw

  1import SwiftUI
  2
  3struct GlueEditView: View {
  4    let domainName: String
  5    let existingRecord: GlueRecord?
  6    @ObservedObject var viewModel: DomainViewModel
  7    let client: NjallaClient
  8
  9    @Environment(\.dismiss) private var dismiss
 10    @State private var name: String
 11    @State private var address4: String
 12    @State private var address6: String
 13    @State private var localErrorMessage: String?
 14
 15    init(domainName: String, existingRecord: GlueRecord?, viewModel: DomainViewModel, client: NjallaClient) {
 16        self.domainName = domainName
 17        self.existingRecord = existingRecord
 18        self.viewModel = viewModel
 19        self.client = client
 20        _name = State(initialValue: existingRecord?.name ?? "")
 21        _address4 = State(initialValue: existingRecord?.address4 ?? "")
 22        _address6 = State(initialValue: existingRecord?.address6 ?? "")
 23    }
 24
 25    var body: some View {
 26        Form {
 27            Section("Record") {
 28                TextField("Name", text: $name)
 29                    .textInputAutocapitalization(.never)
 30                    .autocorrectionDisabled()
 31                    .disabled(existingRecord != nil)
 32
 33                TextField("IPv4", text: $address4)
 34                    .textInputAutocapitalization(.never)
 35                    .autocorrectionDisabled()
 36
 37                TextField("IPv6", text: $address6)
 38                    .textInputAutocapitalization(.never)
 39                    .autocorrectionDisabled()
 40            }
 41
 42            Section {
 43                Button("Save") {
 44                    Task {
 45                        await save()
 46                    }
 47                }
 48                .disabled(viewModel.isSaving)
 49            }
 50        }
 51        .navigationTitle(existingRecord == nil ? "Add Glue" : "Edit Glue")
 52        .navigationBarTitleDisplayMode(.inline)
 53        .overlay {
 54            if viewModel.isSaving {
 55                ProgressView()
 56                    .controlSize(.large)
 57            }
 58        }
 59        .toolbar {
 60            ToolbarItem(placement: .cancellationAction) {
 61                Button("Cancel", role: .cancel) {
 62                    dismiss()
 63                }
 64                .disabled(viewModel.isSaving)
 65            }
 66        }
 67        .alert("Invalid Glue Data", isPresented: localErrorBinding) {
 68            Button("OK", role: .cancel) {}
 69        } message: {
 70            Text(localErrorMessage ?? "")
 71        }
 72        .alert("Request Failed", isPresented: mutationErrorBinding) {
 73            Button("OK", role: .cancel) {}
 74        } message: {
 75            Text(viewModel.mutationErrorMessage ?? "")
 76        }
 77    }
 78
 79    private func save() async {
 80        let trimmedName = name.trimmingCharacters(in: .whitespacesAndNewlines)
 81        let trimmedAddress4 = address4.trimmingCharacters(in: .whitespacesAndNewlines)
 82        let trimmedAddress6 = address6.trimmingCharacters(in: .whitespacesAndNewlines)
 83
 84        guard !trimmedName.isEmpty else {
 85            localErrorMessage = "Glue record name is required."
 86            return
 87        }
 88
 89        guard !trimmedAddress4.isEmpty || !trimmedAddress6.isEmpty else {
 90            localErrorMessage = "Provide at least one address (IPv4 or IPv6)."
 91            return
 92        }
 93
 94        do {
 95            if existingRecord == nil {
 96                try await viewModel.addGlue(
 97                    for: domainName,
 98                    name: trimmedName,
 99                    address4: trimmedAddress4.isEmpty ? nil : trimmedAddress4,
100                    address6: trimmedAddress6.isEmpty ? nil : trimmedAddress6,
101                    client: client
102                )
103            } else {
104                try await viewModel.editGlue(
105                    for: domainName,
106                    name: trimmedName,
107                    address4: trimmedAddress4.isEmpty ? nil : trimmedAddress4,
108                    address6: trimmedAddress6.isEmpty ? nil : trimmedAddress6,
109                    client: client
110                )
111            }
112            dismiss()
113        } catch {
114            return
115        }
116    }
117
118    private var localErrorBinding: Binding<Bool> {
119        Binding(
120            get: { localErrorMessage != nil },
121            set: { newValue in
122                if !newValue {
123                    localErrorMessage = nil
124                }
125            }
126        )
127    }
128
129    private var mutationErrorBinding: Binding<Bool> {
130        Binding(
131            get: { viewModel.mutationErrorMessage != nil },
132            set: { newValue in
133                if !newValue {
134                    viewModel.dismissMutationError()
135                }
136            }
137        )
138    }
139}