mirror of
https://github.com/kkebo/DNSecure.git
synced 2026-03-11 08:54:36 +00:00
feat: add "Restore from Presets" button
This feature allows you to select and add any server from the presets. Without it, when a new server like Freifunk München DNS was added to the preset, existing users would have to reinstall the app in order to add it to their server list. This change resolves such a problem.
This commit is contained in:
parent
9e4a505697
commit
b18c5b60dc
3 changed files with 88 additions and 0 deletions
|
|
@ -24,6 +24,7 @@
|
|||
8940025924ACBD2800EBE74B /* DNSecureUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8940025824ACBD2800EBE74B /* DNSecureUITests.swift */; };
|
||||
8940026924ACBE4900EBE74B /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8940026824ACBE4900EBE74B /* NetworkExtension.framework */; };
|
||||
894958AD2548405E009691D5 /* RuleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 894958AC2548405E009691D5 /* RuleView.swift */; };
|
||||
894F33652C46D2F00060F385 /* RestorationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 894F33642C46D2A20060F385 /* RestorationView.swift */; };
|
||||
8963FDFB251DF1BC00E3DFE7 /* Bundle+displayName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8963FDFA251DF1BC00E3DFE7 /* Bundle+displayName.swift */; };
|
||||
8986CDCF251D9B3400D947CD /* Resolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8986CDCE251D9B3400D947CD /* Resolver.swift */; };
|
||||
8998041628DCDED800C8B421 /* DoTSections.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8998041528DCDED800C8B421 /* DoTSections.swift */; };
|
||||
|
|
@ -75,6 +76,7 @@
|
|||
8940026624ACBE4900EBE74B /* DNSecure.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DNSecure.entitlements; sourceTree = "<group>"; };
|
||||
8940026824ACBE4900EBE74B /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; };
|
||||
894958AC2548405E009691D5 /* RuleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleView.swift; sourceTree = "<group>"; };
|
||||
894F33642C46D2A20060F385 /* RestorationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestorationView.swift; sourceTree = "<group>"; };
|
||||
8963FDFA251DF1BC00E3DFE7 /* Bundle+displayName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+displayName.swift"; sourceTree = "<group>"; };
|
||||
8986CDCE251D9B3400D947CD /* Resolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Resolver.swift; sourceTree = "<group>"; };
|
||||
8998041528DCDED800C8B421 /* DoTSections.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoTSections.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -112,6 +114,7 @@
|
|||
893AA816258F790D0060B022 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
894F33642C46D2A20060F385 /* RestorationView.swift */,
|
||||
8940023D24ACBD2700EBE74B /* ContentView.swift */,
|
||||
89CB922025209DD100B6983C /* HowToActivateView.swift */,
|
||||
890B80D4251DC3A20046BAA0 /* DetailView.swift */,
|
||||
|
|
@ -354,6 +357,7 @@
|
|||
893AA853258F99630060B022 /* NEOnDemandRuleInterfaceType+CaseIterable.swift in Sources */,
|
||||
893AA871258F99AD0060B022 /* NEOnDemandRuleAction+Codable.swift in Sources */,
|
||||
893AA85D258F997A0060B022 /* NEOnDemandRuleInterfaceType+Codable.swift in Sources */,
|
||||
894F33652C46D2F00060F385 /* RestorationView.swift in Sources */,
|
||||
8940023E24ACBD2700EBE74B /* ContentView.swift in Sources */,
|
||||
894958AD2548405E009691D5 /* RuleView.swift in Sources */,
|
||||
8998041828DCDEEF00C8B421 /* DoHSections.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ struct ContentView {
|
|||
@State private var alertTitle = ""
|
||||
@State private var alertMessage = ""
|
||||
@State private var guideIsPresented = false
|
||||
@State private var isRestoring = false
|
||||
|
||||
private func addNewDoTServer() {
|
||||
self.servers.append(
|
||||
|
|
@ -40,6 +41,10 @@ struct ContentView {
|
|||
self.selection = self.servers.count - 1
|
||||
}
|
||||
|
||||
private func restoreFromPresets(resolvers: Set<Resolver>) {
|
||||
self.servers.append(contentsOf: resolvers)
|
||||
}
|
||||
|
||||
private func removeServers(at indexSet: IndexSet) {
|
||||
if let current = self.selection, indexSet.contains(where: { $0 <= current }) {
|
||||
// FIXME: This is a workaround not to crash on deletion.
|
||||
|
|
@ -271,9 +276,15 @@ extension ContentView: View {
|
|||
Menu {
|
||||
Button("DNS-over-TLS", action: self.addNewDoTServer)
|
||||
Button("DNS-over-HTTPS", action: self.addNewDoHServer)
|
||||
Button("Restore from Presets") {
|
||||
self.isRestoring = true
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "plus")
|
||||
}
|
||||
.sheet(isPresented: self.$isRestoring) {
|
||||
RestorationView(onAdd: self.restoreFromPresets)
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
EditButton()
|
||||
|
|
|
|||
73
DNSecure/Views/RestorationView.swift
Normal file
73
DNSecure/Views/RestorationView.swift
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// RestorationView.swift
|
||||
// DNSecure
|
||||
//
|
||||
// Created by Kenta Kubo on 7/17/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct RestorationView {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var selection = Set<Resolver>()
|
||||
@State private var keyword = ""
|
||||
let onAdd: (Set<Resolver>) -> ()
|
||||
|
||||
private var servers: Resolvers {
|
||||
guard !self.keyword.isEmpty else { return Presets.servers }
|
||||
return Presets.servers.filter { $0.name.localizedCaseInsensitiveContains(self.keyword) }
|
||||
}
|
||||
}
|
||||
|
||||
extension RestorationView: View {
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
List(self.servers, id: \.self) { resolver in
|
||||
Button {
|
||||
if self.selection.contains(resolver) {
|
||||
self.selection.remove(resolver)
|
||||
} else {
|
||||
self.selection.insert(resolver)
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
Text(resolver.name)
|
||||
Text(resolver.configuration.description)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
Spacer()
|
||||
if self.selection.contains(resolver) {
|
||||
Image(systemName: "checkmark")
|
||||
}
|
||||
}
|
||||
.tint(.primary)
|
||||
}
|
||||
}
|
||||
.navigationTitle("Presets")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button("Add") {
|
||||
self.onAdd(self.selection)
|
||||
self.dismiss()
|
||||
}
|
||||
.disabled(self.selection.isEmpty)
|
||||
}
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Cancel", role: .cancel) {
|
||||
self.dismiss()
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .bottomBar) {
|
||||
Text("\(self.selection.count) Selected")
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationViewStyle(.stack)
|
||||
.searchable(text: self.$keyword, placement: .navigationBarDrawer(displayMode: .always))
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
RestorationView { _ in }
|
||||
}
|
||||
Loading…
Reference in a new issue