Skip to content

Commit

Permalink
Merge pull request #55 from kkk669/issues/54
Browse files Browse the repository at this point in the history
Fix navigation issues on iOS 16, iPadOS 16, and Mac Catalyst (#54)
  • Loading branch information
kkebo authored Dec 11, 2022
2 parents 8b241c5 + bc678c3 commit 5c7e9f9
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 59 deletions.
186 changes: 127 additions & 59 deletions DNSecure/Views/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,64 @@ struct ContentView {

extension ContentView: View {
var body: some View {
if #available(iOS 16, *) {
self.modernBody
} else {
self.legacyBody
}
}

@available(iOS 16, *)
private var modernBody: some View {
NavigationSplitView {
List(selection: self.$selection) {
NavigationLink("Instruction", value: -1)
Section("Servers") {
ForEach(0..<self.servers.count, id: \.self) { i in
NavigationLink(value: i) {
self.sidebarRow(at: i)
}
}
.onDelete(perform: self.removeServers)
.onMove(perform: self.moveServers)
}
}
.navigationTitle(Bundle.main.displayName!)
.toolbar { self.toolbarContent }
.alert(self.alertTitle, isPresented: self.$alertIsPresented) {
} message: {
Text(self.alertMessage)
}
} detail: {
if self.selection == -1 {
HowToActivateView(isSheet: false)
} else if let i = self.selection {
self.detailView(at: i)
} else if !self.isEnabled {
HowToActivateView(isSheet: false)
} else {
Text("Select a server on the sidebar")
.navigationBarHidden(true)
}
}
.onAppear(perform: self.updateStatus)
.onChange(of: self.scenePhase) { phase in
if phase == .active {
self.updateStatus()
} else if phase == .background {
// FIXME: This is a workaround for self.$severs[i].
// That cannot save settings as soon as it is modified.
guard let id = self.usedID,
let uuid = UUID(uuidString: id),
let server = self.servers.find(by: uuid) else {
return
}
self.saveSettings(of: server)
}
}
}

private var legacyBody: some View {
NavigationView {
List {
NavigationLink(
Expand All @@ -144,73 +202,17 @@ extension ContentView: View {
tag: i,
selection: self.$selection
) {
DetailView(
server: self.$servers[i],
isOn: .init(
get: {
self.usedID == self.servers[i].id.uuidString
},
set: {
if $0 {
self.saveSettings(of: self.servers[i])
} else {
self.removeSettings()
}
}
)
)
self.detailView(at: i)
} label: {
VStack(alignment: .leading) {
Text(self.servers[i].name)
Text(self.servers[i].configuration.description)
.foregroundColor(.secondary)
}
if self.usedID == self.servers[i].id.uuidString {
Spacer()
Image(systemName: "checkmark")
}
self.sidebarRow(at: i)
}
}
.onDelete(perform: self.removeServers)
.onMove(perform: self.moveServers)
}
}
.navigationTitle(Bundle.main.displayName!)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Menu {
Button("DNS-over-TLS", action: self.addNewDoTServer)
Button("DNS-over-HTTPS", action: self.addNewDoHServer)
} label: {
Image(systemName: "plus")
}
}
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
ToolbarItem(placement: .status) {
VStack {
HStack {
Circle()
.frame(width: 10, height: 10)
.foregroundColor(self.isEnabled ? .green : .secondary)
Text(self.isEnabled ? "Active" : "Inactive")
#if targetEnvironment(macCatalyst)
Text("-")
Button("Refresh", action: self.updateStatus)
#endif
}
if !self.isEnabled {
Button("How to Activate") {
self.guideIsPresented = true
}
.sheet(isPresented: self.$guideIsPresented) {
HowToActivateView(isSheet: true)
}
}
}
}
}
.toolbar { self.toolbarContent }
.alert(self.alertTitle, isPresented: self.$alertIsPresented) {
} message: {
Text(self.alertMessage)
Expand Down Expand Up @@ -239,6 +241,72 @@ extension ContentView: View {
}
}
}

@ToolbarContentBuilder private var toolbarContent: some ToolbarContent {
ToolbarItem(placement: .navigationBarLeading) {
Menu {
Button("DNS-over-TLS", action: self.addNewDoTServer)
Button("DNS-over-HTTPS", action: self.addNewDoHServer)
} label: {
Image(systemName: "plus")
}
}
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
ToolbarItem(placement: .status) {
VStack {
HStack {
Circle()
.frame(width: 10, height: 10)
.foregroundColor(self.isEnabled ? .green : .secondary)
Text(self.isEnabled ? "Active" : "Inactive")
#if targetEnvironment(macCatalyst)
Text("-")
Button("Refresh", action: self.updateStatus)
#endif
}
if !self.isEnabled {
Button("How to Activate") {
self.guideIsPresented = true
}
.sheet(isPresented: self.$guideIsPresented) {
HowToActivateView(isSheet: true)
}
}
}
}
}

@ViewBuilder private func sidebarRow(at i: Int) -> some View {
VStack(alignment: .leading) {
Text(self.servers[i].name)
Text(self.servers[i].configuration.description)
.foregroundColor(.secondary)
}
if self.usedID == self.servers[i].id.uuidString {
Spacer()
Image(systemName: "checkmark")
}
}

private func detailView(at i: Int) -> some View {
DetailView(
server: self.$servers[i],
isOn: .init(
get: {
self.usedID == self.servers[i].id.uuidString
},
set: {
if $0 {
self.saveSettings(of: self.servers[i])
} else {
self.removeSettings()
}
}
)
)
}
}

struct ContentView_Previews: PreviewProvider {
Expand Down
51 changes: 51 additions & 0 deletions DNSecure/Views/DetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,57 @@ struct DetailView {

extension DetailView: View {
var body: some View {
if #available(iOS 16, *) {
self.modernBody
} else {
self.legacyBody
}
}

@available(iOS 16, *)
private var modernBody: some View {
NavigationStack {
Form {
Section {
Toggle("Use This Server", isOn: self.$isOn)
}
Section {
HStack {
Text("Name")
TextField("Name", text: self.$server.name)
.multilineTextAlignment(.trailing)
}
}
self.serverConfigurationSections
Section {
ForEach(self.server.onDemandRules) { rule in
NavigationLink(rule.name, value: rule)
}
.onDelete { self.server.onDemandRules.remove(atOffsets: $0) }
.onMove { self.server.onDemandRules.move(fromOffsets: $0, toOffset: $1) }
Button("Add New Rule") {
self.server.onDemandRules
.append(OnDemandRule(name: "New Rule"))
}
} header: {
EditButton()
.frame(maxWidth: .infinity, alignment: .trailing)
.overlay(alignment: .leading) {
Text("On Demand Rules")
}
}
}
.navigationDestination(for: OnDemandRule.self) { rule in
// When RuleView is opened and tap another server on the sidebar, the previous server's rule comes here.
if self.server.onDemandRules.contains(rule) {
RuleView(rule: self.binding(for: rule))
}
}
}
.navigationTitle(self.server.name)
}

private var legacyBody: some View {
Form {
Section {
Toggle("Use This Server", isOn: self.$isOn)
Expand Down

0 comments on commit 5c7e9f9

Please sign in to comment.