summaryrefslogtreecommitdiff
path: root/Rune/Views/Domains/GlueListView.swift
blob: b390a847cb86251d589c8babdd2ae363137bdb86 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import SwiftUI

struct GlueListView: View {
    let domainName: String
    @ObservedObject var viewModel: DomainViewModel
    let client: NjallaClient

    @State private var showingAddGlue = false
    @State private var recordPendingDeletion: GlueRecord?

    var body: some View {
        List {
            if let errorMessage = viewModel.glueErrorMessage {
                Section {
                    InlineErrorView(message: errorMessage, retryTitle: "Retry Glue Records") {
                        Task {
                            await viewModel.loadGlue(for: domainName, client: client)
                        }
                    }
                    .listRowInsets(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
                }
            }

            if viewModel.isLoadingGlue && viewModel.glueRecords.isEmpty {
                Section {
                    HStack {
                        Spacer()
                        ProgressView("Loading Glue")
                        Spacer()
                    }
                }
            } else if viewModel.glueRecords.isEmpty {
                Section {
                    ContentUnavailableView(
                        "No Glue Records",
                        systemImage: "list.bullet.rectangle",
                        description: Text("No glue records are configured for this domain.")
                    )
                }
            } else {
                ForEach(viewModel.glueRecords) { record in
                    NavigationLink {
                        GlueEditView(domainName: domainName, existingRecord: record, viewModel: viewModel, client: client)
                    } label: {
                        VStack(alignment: .leading, spacing: 4) {
                            Text(record.name)
                                .font(.headline)
                            Text("IPv4: \(record.address4 ?? "n/a")")
                                .font(.subheadline)
                                .foregroundStyle(.secondary)
                            Text("IPv6: \(record.address6 ?? "n/a")")
                                .font(.subheadline)
                                .foregroundStyle(.secondary)
                        }
                        .padding(.vertical, 4)
                    }
                    .swipeActions {
                        Button("Delete", role: .destructive) {
                            recordPendingDeletion = record
                        }
                    }
                    .disabled(viewModel.isSaving)
                }
            }
        }
        .listStyle(.insetGrouped)
        .navigationTitle("Glue Records")
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            Button {
                showingAddGlue = true
            } label: {
                Label("Add Glue", systemImage: "plus")
            }
            .disabled(viewModel.isSaving)
        }
        .sheet(isPresented: $showingAddGlue) {
            NavigationStack {
                GlueEditView(domainName: domainName, existingRecord: nil, viewModel: viewModel, client: client)
            }
        }
        .task {
            await viewModel.loadGlue(for: domainName, client: client)
        }
        .refreshable {
            await viewModel.loadGlue(for: domainName, client: client)
        }
        .alert(deleteAlertTitle, isPresented: deleteBinding) {
            Button("Delete Glue", role: .destructive) {
                guard let recordPendingDeletion else { return }
                Task {
                    do {
                        try await viewModel.removeGlue(recordPendingDeletion, client: client)
                        self.recordPendingDeletion = nil
                    } catch {
                        return
                    }
                }
            }
            Button("Cancel", role: .cancel) {
                recordPendingDeletion = nil
            }
        } message: {
            Text("Delete glue record \(recordPendingDeletion?.name ?? "")?")
        }
        .alert("Request Failed", isPresented: mutationErrorBinding) {
            Button("OK", role: .cancel) {}
        } message: {
            Text(viewModel.mutationErrorMessage ?? "")
        }
    }

    private var deleteAlertTitle: String {
        guard let recordPendingDeletion else { return "" }
        return "Delete glue record \(recordPendingDeletion.name)?"
    }

    private var deleteBinding: Binding<Bool> {
        Binding(
            get: { recordPendingDeletion != nil },
            set: { newValue in
                if !newValue {
                    recordPendingDeletion = nil
                }
            }
        )
    }

    private var mutationErrorBinding: Binding<Bool> {
        Binding(
            get: { viewModel.mutationErrorMessage != nil },
            set: { newValue in
                if !newValue {
                    viewModel.dismissMutationError()
                }
            }
        )
    }
}