krz/rune

an ios client for njalla

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

main: Rune/Views/Domains/RecordAddView.swift · raw

  1import SwiftUI
  2
  3struct RecordAddView: View {
  4    let domainName: String
  5    @ObservedObject var viewModel: DomainViewModel
  6    let client: NjallaClient
  7
  8    @Environment(\.dismiss) private var dismiss
  9
 10    @State private var draft = DNSRecordDraft()
 11    var body: some View {
 12        Form {
 13            DNSRecordFormSections(draft: $draft)
 14
 15            Section {
 16                Button("Save") {
 17                    Task {
 18                        await save()
 19                    }
 20                }
 21                .disabled(viewModel.isSaving || !draft.canSubmit)
 22            }
 23        }
 24        .navigationTitle("Add Record")
 25        .navigationBarTitleDisplayMode(.inline)
 26        .overlay {
 27            if viewModel.isSaving {
 28                ProgressView()
 29                    .controlSize(.large)
 30            }
 31        }
 32        .toolbar {
 33            ToolbarItem(placement: .cancellationAction) {
 34                Button("Cancel", role: .cancel) {
 35                    dismiss()
 36                }
 37                .disabled(viewModel.isSaving)
 38            }
 39        }
 40        .interactiveDismissDisabled(viewModel.isSaving)
 41        .onChange(of: draft.type) { oldValue, newValue in
 42            guard oldValue != newValue else { return }
 43            draft.resetTypeSpecificFields()
 44        }
 45        .alert("Request Failed", isPresented: mutationErrorBinding) {
 46            Button("OK", role: .cancel) {}
 47        } message: {
 48            Text(viewModel.mutationErrorMessage ?? "")
 49        }
 50    }
 51
 52    private func save() async {
 53        guard !viewModel.isSaving else {
 54            return
 55        }
 56
 57        do {
 58            try await viewModel.addRecord(for: domainName, draft: draft, client: client)
 59            draft = DNSRecordDraft()
 60            dismiss()
 61        } catch is CancellationError {
 62            return
 63        } catch {
 64            return
 65        }
 66    }
 67
 68    private var mutationErrorBinding: Binding<Bool> {
 69        Binding(
 70            get: { viewModel.mutationErrorMessage != nil },
 71            set: { newValue in
 72                if !newValue {
 73                    viewModel.dismissMutationError()
 74                }
 75            }
 76        )
 77    }
 78}
 79
 80struct DNSRecordFormSections: View {
 81    @Binding var draft: DNSRecordDraft
 82
 83    var body: some View {
 84        Section("Record") {
 85            Picker("Type", selection: $draft.type) {
 86                ForEach(DNSRecordType.allCases) { type in
 87                    Text(type.rawValue).tag(type)
 88                }
 89            }
 90
 91            TextField("Name", text: $draft.name)
 92                .textInputAutocapitalization(.never)
 93                .autocorrectionDisabled()
 94        }
 95
 96        if draft.type.usesContent {
 97            Section("Content") {
 98                TextField("Content", text: $draft.content, axis: .vertical)
 99                    .textInputAutocapitalization(.never)
100                    .autocorrectionDisabled()
101            }
102        }
103
104        if draft.type.usesTTL {
105            Section("TTL") {
106                Picker("TTL", selection: $draft.ttlSeconds) {
107                    ForEach(draft.ttlOptions) { option in
108                        Text(option.isCustom ? "Custom (\(option.label))" : option.label)
109                            .tag(option.seconds)
110                    }
111                }
112            }
113        }
114
115        if draft.type.usesPriority {
116            Section("Priority") {
117                TextField("Priority", text: $draft.prio)
118                    .keyboardType(.numberPad)
119            }
120        }
121
122        if draft.type.usesWeight {
123            Section("Weight") {
124                TextField("Weight", text: $draft.weight)
125                    .keyboardType(.numberPad)
126            }
127        }
128
129        if draft.type.usesPort {
130            Section("Port") {
131                TextField("Port", text: $draft.port)
132                    .keyboardType(.numberPad)
133            }
134        }
135
136        if draft.type.usesTarget {
137            Section("Target") {
138                TextField("Target", text: $draft.target)
139                    .textInputAutocapitalization(.never)
140                    .autocorrectionDisabled()
141            }
142        }
143
144        if draft.type.usesSSHFields {
145            Section {
146                TextField("SSH Algorithm", text: $draft.sshAlgorithm)
147                    .keyboardType(.numberPad)
148                TextField("SSH Type", text: $draft.sshType)
149                    .keyboardType(.numberPad)
150            } header: {
151                Text("SSHFP")
152            } footer: {
153                Text("Algorithm values: 1-5. Type values: 1-2.")
154            }
155        }
156    }
157}