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