diff --git a/Yggdrasil Network Cross-Platform/CrossPlatformAppDelegate.swift b/Yggdrasil Network Cross-Platform/CrossPlatformAppDelegate.swift index 68a1e0c..311d1b7 100644 --- a/Yggdrasil Network Cross-Platform/CrossPlatformAppDelegate.swift +++ b/Yggdrasil Network Cross-Platform/CrossPlatformAppDelegate.swift @@ -112,6 +112,7 @@ class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject { self.requestSummaryIPC() } else if conn.status == .disconnecting || conn.status == .disconnected { self.clearStatus() + self.yggdrasilEnabled = false } } @@ -127,7 +128,7 @@ class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject { guard let savedManagers else { print("Expected to find saved managers but didn't") - self.yggdrasilSupported = false + // self.yggdrasilSupported = false return } @@ -186,9 +187,7 @@ class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject { self.yggdrasilSubnet = summary.subnet self.yggdrasilPublicKey = summary.publicKey self.yggdrasilPeers = summary.peers - self.yggdrasilConnected = summary.peers.count > 0 - - print(self.yggdrasilPeers) + self.yggdrasilConnected = summary.peers.filter { $0.up }.count > 0 } } } diff --git a/Yggdrasil Network Cross-Platform/IPCResponses.swift b/Yggdrasil Network Cross-Platform/IPCResponses.swift index 9f938b7..774a107 100644 --- a/Yggdrasil Network Cross-Platform/IPCResponses.swift +++ b/Yggdrasil Network Cross-Platform/IPCResponses.swift @@ -6,6 +6,7 @@ // import Foundation +import SwiftUI struct YggdrasilSummary: Codable { var address: String @@ -17,14 +18,18 @@ struct YggdrasilSummary: Codable { func list() -> [String] { return peers.map { $0.remote } } + + func listUp() -> [String] { + return peers.filter { $0.up }.map { $0.remote } + } } struct YggdrasilPeer: Codable, Identifiable { var id: String { remote } // For Identifiable protocol let remote: String let up: Bool - let address: String - let key: String + let address: String? + let key: String? let priority: UInt8 let cost: UInt16? @@ -36,4 +41,11 @@ struct YggdrasilPeer: Codable, Identifiable { case priority = "Priority" case cost = "Cost" } + + public func getStatusBadgeColor() -> SwiftUI.Color { + if self.up { + return .green + } + return .gray + } } diff --git a/Yggdrasil Network Extension/PacketTunnelProvider.swift b/Yggdrasil Network Extension/PacketTunnelProvider.swift index e96ff04..ebeeb1e 100644 --- a/Yggdrasil Network Extension/PacketTunnelProvider.swift +++ b/Yggdrasil Network Extension/PacketTunnelProvider.swift @@ -113,9 +113,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { subnet: self.yggdrasil.getSubnetString(), publicKey: self.yggdrasil.getPublicKeyString(), enabled: true, - peers: peers.sorted(by: { a, b in - a.remote < b.remote - }) + peers: peers ) if let json = try? JSONEncoder().encode(summary) { completionHandler?(json) diff --git a/Yggdrasil Network.xcodeproj/project.pbxproj b/Yggdrasil Network.xcodeproj/project.pbxproj index d6a1166..bc0109d 100644 --- a/Yggdrasil Network.xcodeproj/project.pbxproj +++ b/Yggdrasil Network.xcodeproj/project.pbxproj @@ -73,6 +73,7 @@ 39B51A492997062E0059D29D /* PeersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeersView.swift; sourceTree = ""; }; 39BF9FC12A2E9E51000E7269 /* YggdrasilNetworkExtension-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "YggdrasilNetworkExtension-Bridging-Header.h"; sourceTree = ""; }; 39CC924B221DEDCE004960DC /* IPCResponses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPCResponses.swift; sourceTree = ""; }; + 39DB9BEC2BCF28FA009BF2A4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 39F0205B2996CD760093F603 /* YggdrasilSwiftUI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = YggdrasilSwiftUI.app; sourceTree = BUILT_PRODUCTS_DIR; }; 39F0205D2996CD760093F603 /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = ""; }; 39F0205F2996CD760093F603 /* StatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusView.swift; sourceTree = ""; }; @@ -129,6 +130,7 @@ 39F0205C2996CD760093F603 /* YggdrasilSwiftUI */ = { isa = PBXGroup; children = ( + 39DB9BEC2BCF28FA009BF2A4 /* Info.plist */, 39F0205D2996CD760093F603 /* Application.swift */, 39F0205F2996CD760093F603 /* StatusView.swift */, 39F0206A2996CF260093F603 /* SettingsView.swift */, @@ -348,7 +350,9 @@ ENABLE_PREVIEWS = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = YggdrasilSwiftUI/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Yggdrasil; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; @@ -400,7 +404,9 @@ ENABLE_PREVIEWS = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = YggdrasilSwiftUI/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Yggdrasil; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; diff --git a/Yggdrasil Network.xcodeproj/xcshareddata/xcschemes/YggdrasilSwiftUI.xcscheme b/Yggdrasil Network.xcodeproj/xcshareddata/xcschemes/YggdrasilSwiftUI.xcscheme new file mode 100644 index 0000000..e0f02e0 --- /dev/null +++ b/Yggdrasil Network.xcodeproj/xcshareddata/xcschemes/YggdrasilSwiftUI.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/YggdrasilSwiftUI/Info.plist b/YggdrasilSwiftUI/Info.plist new file mode 100644 index 0000000..0c67376 --- /dev/null +++ b/YggdrasilSwiftUI/Info.plist @@ -0,0 +1,5 @@ + + + + + diff --git a/YggdrasilSwiftUI/PeersView.swift b/YggdrasilSwiftUI/PeersView.swift index 518528b..3cbf742 100644 --- a/YggdrasilSwiftUI/PeersView.swift +++ b/YggdrasilSwiftUI/PeersView.swift @@ -76,6 +76,7 @@ struct PeersView: View { } } TextField("Multicast password", text: $appDelegate.yggdrasilConfig.multicastPassword) + .labelStyle(.titleAndIcon) }, header: { Text("Local connectivity") }) diff --git a/YggdrasilSwiftUI/SettingsView.swift b/YggdrasilSwiftUI/SettingsView.swift index 4b569f7..be25d7a 100644 --- a/YggdrasilSwiftUI/SettingsView.swift +++ b/YggdrasilSwiftUI/SettingsView.swift @@ -34,6 +34,7 @@ struct SettingsView: View { Text("Automatically start when connected to") }) + /* Section(content: { VStack(alignment: .leading) { Button("Import configuration") { @@ -76,6 +77,7 @@ struct SettingsView: View { }, header: { Text("Configuration") }) + */ } .formStyle(.grouped) .navigationTitle("Settings") diff --git a/YggdrasilSwiftUI/StatusView.swift b/YggdrasilSwiftUI/StatusView.swift index 5fadd1f..7a8b552 100644 --- a/YggdrasilSwiftUI/StatusView.swift +++ b/YggdrasilSwiftUI/StatusView.swift @@ -22,13 +22,12 @@ struct StatusView: View { private func getStatusBadgeColor() -> SwiftUI.Color { if !appDelegate.yggdrasilSupported { return .gray - } else if !appDelegate.yggdrasilEnabled { - return .gray - } else if !appDelegate.yggdrasilConnected { - return .yellow - } else { + } else if appDelegate.yggdrasilConnected { return .green + } else if appDelegate.yggdrasilEnabled { + return .yellow } + return .gray } private func getStatusBadgeText() -> String { @@ -39,7 +38,7 @@ struct StatusView: View { } else if !appDelegate.yggdrasilConnected { return "No peers connected" } else { - return "Connected to \(appDelegate.yggdrasilPeers.count) peer(s)" + return "Connected to \(appDelegate.yggdrasilPeers.filter { $0.up }.count) peer(s)" } } @@ -49,6 +48,7 @@ struct StatusView: View { VStack(alignment: .leading) { Toggle("Enable Yggdrasil", isOn: $appDelegate.yggdrasilEnabled) .disabled(!appDelegate.yggdrasilSupported) + .padding(.bottom, 2) HStack { Image(systemName: "circlebadge.fill") .foregroundColor(statusBadgeColor) @@ -116,15 +116,6 @@ struct StatusView: View { .lineLimit(1) .textSelection(.enabled) } - /*HStack { - Text("Coordinates") - Spacer() - Text(appDelegate.yggdrasilCoords) - .foregroundColor(Color.gray) - .truncationMode(.tail) - .lineLimit(1) - .textSelection(.enabled) - }*/ HStack { Text("Public Key") Spacer() @@ -139,10 +130,20 @@ struct StatusView: View { Text("Details") }) - if self.appDelegate.yggdrasilEnabled { - Section(content: { + Section(content: { + if self.appDelegate.yggdrasilPeers.count == 0 { + Text("No peers are connected") + .foregroundStyle(.tertiary) + .frame(maxWidth: .infinity, alignment: .center) + } else { List(self.appDelegate.yggdrasilPeers.sorted(by: { a, b in - a.key < a.key + if a.up && !b.up { + return true + } + if !a.up && b.up { + return false + } + return a.remote < b.remote }), id: \.remote) { peer in VStack { Text(peer.remote) @@ -150,20 +151,30 @@ struct StatusView: View { .truncationMode(.tail) .lineLimit(1) .textSelection(.enabled) - Text(peer.address) - .frame(maxWidth: .infinity, alignment: .leading) - .foregroundColor(Color.gray) - .font(.system(size: 11, design: .monospaced)) - .truncationMode(.tail) - .lineLimit(1) - .textSelection(.enabled) + .padding(.bottom, 2) + HStack { + Image(systemName: "circlebadge.fill") + .foregroundColor(peer.getStatusBadgeColor()) + .onChange(of: peer.up) { newValue in + statusBadgeColor = peer.getStatusBadgeColor() + } + Text(peer.up ? peer.address ?? "Unknown IP address" : "Not connected") + .frame(maxWidth: .infinity, alignment: .leading) + .foregroundColor(Color.gray) + .font(.system(size: 11)) + .truncationMode(.tail) + .lineLimit(1) + .textSelection(.enabled) + } } .padding(.all, 2) + .padding(.top, 4) + .padding(.bottom, 4) } - }, header: { - Text("Peers") - }) - } + } + }, header: { + Text("Peers") + }) } .formStyle(.grouped) .navigationTitle("Yggdrasil")