krz/rune
an ios client for njalla
clone: git clone https://gitbay.org/krz/rune.git
v1.2.0: 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}