App updates

This commit is contained in:
Neil Alexander 2024-04-16 21:35:51 +01:00
parent 2cb5eebcd2
commit 5fbb735f56
No known key found for this signature in database
GPG key ID: A02A2019A2BB0944
9 changed files with 204 additions and 91 deletions

View file

@ -13,6 +13,8 @@ import AppKit
import SwiftUI
import Yggdrasil
import NetworkExtension
import Foundation
import CoreData
#if os(iOS)
class PlatformItemSource: NSObject, UIActivityItemSource {
@ -31,9 +33,10 @@ class PlatformItemSource: NSObject {}
class ConfigurationProxy: PlatformItemSource {
private var manager: NETunnelProviderManager?
private var json: Data? = nil
private var dict: [String: Any]? = nil
private var dict: [String: Any]? = [:]
private var timer: Timer?
init(manager: NETunnelProviderManager? = nil) {
init(manager: NETunnelProviderManager) {
self.manager = manager
super.init()
@ -64,14 +67,13 @@ class ConfigurationProxy: PlatformItemSource {
self.set("Listen", to: [] as [String])
self.set("AdminListen", to: "none")
self.set("IfName", to: "dummy")
self.set("Peers", to: ["tcp://172.22.97.1.5190", "tls://172.22.97.1:5191"])
if self.get("AutoStart") == nil {
self.set("AutoStart", to: ["Any": false, "WiFi": false, "Mobile": false, "Ethernet": false] as [String: Bool])
}
let multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? []
if multicastInterfaces.count == 0 {
if multicastInterfaces.count != 1 {
self.set("MulticastInterfaces", to: [
[
"Regex": "en.*",
@ -94,7 +96,7 @@ class ConfigurationProxy: PlatformItemSource {
var multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? []
multicastInterfaces[0]["Beacon"] = newValue
self.set("MulticastInterfaces", to: multicastInterfaces)
self.trySave()
self.saveSoon()
}
}
@ -110,7 +112,23 @@ class ConfigurationProxy: PlatformItemSource {
var multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? []
multicastInterfaces[0]["Listen"] = newValue
self.set("MulticastInterfaces", to: multicastInterfaces)
self.trySave()
self.saveSoon()
}
}
public var multicastPassword: String {
get {
let multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? []
if multicastInterfaces.count == 0 {
return ""
}
return multicastInterfaces[0]["Password"] as? String ?? ""
}
set {
var multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? []
multicastInterfaces[0]["Password"] = newValue
self.set("MulticastInterfaces", to: multicastInterfaces)
self.saveSoon()
}
}
@ -120,7 +138,7 @@ class ConfigurationProxy: PlatformItemSource {
}
set {
self.set("Any", inSection: "AutoStart", to: newValue)
self.trySave()
self.saveSoon()
}
}
@ -130,7 +148,7 @@ class ConfigurationProxy: PlatformItemSource {
}
set {
self.set("WiFi", inSection: "AutoStart", to: newValue)
self.trySave()
self.saveSoon()
}
}
@ -140,7 +158,7 @@ class ConfigurationProxy: PlatformItemSource {
}
set {
self.set("Ethernet", inSection: "AutoStart", to: newValue)
self.trySave()
self.saveSoon()
}
}
@ -150,7 +168,17 @@ class ConfigurationProxy: PlatformItemSource {
}
set {
self.set("Mobile", inSection: "AutoStart", to: newValue)
self.trySave()
self.saveSoon()
}
}
public var deviceName: String {
get {
return self.get("name", inSection: "NodeInfo") as? String ?? ""
}
set {
self.set("name", inSection: "NodeInfo", to: newValue)
self.saveSoon()
}
}
@ -160,7 +188,7 @@ class ConfigurationProxy: PlatformItemSource {
}
set {
self.set("Peers", to: newValue)
self.trySave()
self.saveSoon()
}
}
@ -239,10 +267,22 @@ class ConfigurationProxy: PlatformItemSource {
}
}
private func trySave() {
private func saveSoon() {
self.timer?.invalidate()
self.timer = Timer.scheduledTimer(
timeInterval: 1.0,
target: self,
selector: #selector(saveFromTimer),
userInfo: nil,
repeats: false
)
}
@objc private func saveFromTimer() {
if var manager = self.manager {
try? self.save(to: &manager)
}
self.timer = nil
}
func save(to manager: inout NETunnelProviderManager) throws {
@ -254,6 +294,8 @@ class ConfigurationProxy: PlatformItemSource {
providerProtocol.serverAddress = "yggdrasil"
providerProtocol.username = self.get("PublicKey") as? String ?? self.get("SigningPublicKey") as? String ?? "(unknown public key)"
NSLog(String(data: data, encoding: .utf8) ?? "(unknown)")
let disconnectrule = NEOnDemandRuleDisconnect()
var rules: [NEOnDemandRule] = [disconnectrule]
if self.get("Any", inSection: "AutoStart") as? Bool ?? false {
@ -286,7 +328,7 @@ class ConfigurationProxy: PlatformItemSource {
manager.protocolConfiguration = providerProtocol
manager.saveToPreferences(completionHandler: { (error:Error?) in
manager.saveToPreferences(completionHandler: { error in
if let error = error {
print(error)
} else {

View file

@ -22,11 +22,12 @@ typealias ApplicationDelegateAdaptor = NSApplicationDelegateAdaptor
class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
var vpnManager: NETunnelProviderManager = NETunnelProviderManager()
var yggdrasilConfig: ConfigurationProxy = ConfigurationProxy()
var yggdrasilConfig: ConfigurationProxy
let yggdrasilComponent = "eu.neilalexander.yggdrasil.extension"
private var adminTimer: DispatchSourceTimer?
override init() {
self.yggdrasilConfig = ConfigurationProxy(manager: self.vpnManager)
super.init()
NotificationCenter.default.addObserver(forName: .NEVPNStatusDidChange, object: nil, queue: nil, using: { notification in
@ -34,7 +35,6 @@ class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
switch conn.status {
case .connected:
self.requestSummaryIPC()
self.requestStatusIPC()
case .disconnecting, .disconnected:
self.clearStatus()
default:
@ -73,9 +73,7 @@ class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
@Published var yggdrasilSubnet: String = "N/A"
@Published var yggdrasilCoords: String = "[]"
@Published var yggdrasilPeers: [[String: Any]] = [[:]]
@Published var yggdrasilDHT: [[String: Any]] = [[:]]
@Published var yggdrasilNodeInfo: [String: Any] = [:]
@Published var yggdrasilPeers: [YggdrasilPeer] = []
func yggdrasilVersion() -> String {
return Yggdrasil.MobileGetVersion()
@ -111,7 +109,7 @@ class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
func updateStatus(conn: NEVPNConnection) {
if conn.status == .connected {
self.requestStatusIPC()
self.requestSummaryIPC()
} else if conn.status == .disconnecting || conn.status == .disconnected {
self.clearStatus()
}
@ -183,30 +181,14 @@ class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
if let session = self.vpnManager.connection as? NETunnelProviderSession {
try? session.sendProviderMessage("summary".data(using: .utf8)!) { js in
if let js = js, let summary = try? JSONDecoder().decode(YggdrasilSummary.self, from: js) {
self.yggdrasilEnabled = true
self.yggdrasilEnabled = summary.enabled
self.yggdrasilIP = summary.address
self.yggdrasilSubnet = summary.subnet
self.yggdrasilPublicKey = summary.publicKey
}
}
}
}
self.yggdrasilPeers = summary.peers
self.yggdrasilConnected = summary.peers.count > 0
func requestStatusIPC() {
if self.vpnManager.connection.status != .connected {
return
}
if let session = self.vpnManager.connection as? NETunnelProviderSession {
try? session.sendProviderMessage("status".data(using: .utf8)!) { js in
if let js = js, let status = try? JSONDecoder().decode(YggdrasilStatus.self, from: js) {
self.yggdrasilCoords = status.coords
if let jsonResponse = try? JSONSerialization.jsonObject(with: status.peers, options: []) as? [[String: Any]] {
self.yggdrasilPeers = jsonResponse
}
if let jsonResponse = try? JSONSerialization.jsonObject(with: status.dht, options: []) as? [[String: Any]] {
self.yggdrasilDHT = jsonResponse
}
self.yggdrasilConnected = self.yggdrasilEnabled && self.yggdrasilPeers.count > 0 && self.yggdrasilDHT.count > 0
print(self.yggdrasilPeers)
}
}
}
@ -218,6 +200,5 @@ class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
self.yggdrasilSubnet = "N/A"
self.yggdrasilCoords = "[]"
self.yggdrasilPeers = []
self.yggdrasilDHT = []
}
}

View file

@ -11,11 +11,29 @@ struct YggdrasilSummary: Codable {
var address: String
var subnet: String
var publicKey: String
var enabled: Bool
var peers: [YggdrasilPeer]
func list() -> [String] {
return peers.map { $0.remote }
}
}
struct YggdrasilStatus: Codable {
var enabled: Bool
var coords: String
var peers: Data
var dht: Data
struct YggdrasilPeer: Codable, Identifiable {
var id: String { remote } // For Identifiable protocol
let remote: String
let up: Bool
let address: String
let key: String
let priority: UInt8
let cost: UInt16?
enum CodingKeys: String, CodingKey {
case remote = "URI"
case up = "Up"
case address = "IP"
case key = "Key"
case priority = "Priority"
case cost = "Cost"
}
}

View file

@ -98,26 +98,29 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
let request = String(data: messageData, encoding: .utf8)
switch request {
case "summary":
let pj = self.yggdrasil.getPeersJSON()
var peers: [YggdrasilPeer] = []
do {
peers = try JSONDecoder().decode(
[YggdrasilPeer].self,
from: pj.data(using: .utf8)!
)
} catch {
NSLog("JSON Error: \(error)")
}
let summary = YggdrasilSummary(
address: self.yggdrasil.getAddressString(),
subnet: self.yggdrasil.getSubnetString(),
publicKey: self.yggdrasil.getPublicKeyString()
publicKey: self.yggdrasil.getPublicKeyString(),
enabled: true,
peers: peers.sorted(by: { a, b in
a.remote < b.remote
})
)
if let json = try? JSONEncoder().encode(summary) {
completionHandler?(json)
}
case "status":
let status = YggdrasilStatus(
enabled: true,
coords: self.yggdrasil.getCoordsString(),
peers: self.yggdrasil.getPeersJSON().data(using: .utf8) ?? Data(),
dht: self.yggdrasil.getDHTJSON().data(using: .utf8) ?? Data()
)
if let json = try? JSONEncoder().encode(status) {
completionHandler?(json)
}
default:
completionHandler?(nil)
}

View file

@ -3,17 +3,18 @@
archiveVersion = 1;
classes = {
};
objectVersion = 55;
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
391B72A82A2FD90100896278 /* PacketTunnelProvider+FileDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 391B72A62A2FD90100896278 /* PacketTunnelProvider+FileDescriptor.swift */; };
3952ADB829945AF700B3835D /* ConfigurationProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3952ADB629945AF700B3835D /* ConfigurationProxy.swift */; };
3996AF39270328080070947D /* Yggdrasil.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3996AF37270328080070947D /* Yggdrasil.xcframework */; };
39B51A4A2997062E0059D29D /* PeersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39B51A492997062E0059D29D /* PeersView.swift */; };
39B51A4B29994F240059D29D /* ConfigurationProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3952ADB629945AF700B3835D /* ConfigurationProxy.swift */; };
39B51A4C29994F350059D29D /* CrossPlatformAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3952ADB929945AFA00B3835D /* CrossPlatformAppDelegate.swift */; };
39B51A4D299951D60059D29D /* IPCResponses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CC924B221DEDCE004960DC /* IPCResponses.swift */; };
39B9173C2BBD637200899F81 /* Yggdrasil.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3996AF37270328080070947D /* Yggdrasil.xcframework */; };
39B9173D2BBD637200899F81 /* Yggdrasil.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3996AF37270328080070947D /* Yggdrasil.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
39BF9FC72A2F6FFD000E7269 /* Yggdrasil.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3996AF37270328080070947D /* Yggdrasil.xcframework */; };
39CC924D221DEDD3004960DC /* IPCResponses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CC924B221DEDCE004960DC /* IPCResponses.swift */; };
39F0205E2996CD760093F603 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39F0205D2996CD760093F603 /* Application.swift */; };
@ -22,7 +23,7 @@
39F020662996CD770093F603 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 39F020652996CD770093F603 /* Preview Assets.xcassets */; };
39F0206B2996CF260093F603 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39F0206A2996CF260093F603 /* SettingsView.swift */; };
39F99B342A48F6E50045BD10 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 39AE88382319C93F0010FFF6 /* NetworkExtension.framework */; };
39F99B382A48F74F0045BD10 /* YggdrasilNetworkExtension.appex in CopyFiles */ = {isa = PBXBuildFile; fileRef = E593CE971DF905AF00D7265D /* YggdrasilNetworkExtension.appex */; platformFilters = (ios, macos, ); settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
39F99B382A48F74F0045BD10 /* YggdrasilNetworkExtension.appex in CopyFiles */ = {isa = PBXBuildFile; fileRef = E593CE971DF905AF00D7265D /* YggdrasilNetworkExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
E593CE9C1DF905AF00D7265D /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E593CE9B1DF905AF00D7265D /* PacketTunnelProvider.swift */; };
/* End PBXBuildFile section */
@ -37,6 +38,17 @@
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
39B9173E2BBD637200899F81 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
39B9173D2BBD637200899F81 /* Yggdrasil.xcframework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
39F99B372A48F7380045BD10 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
@ -87,7 +99,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
3996AF39270328080070947D /* Yggdrasil.xcframework in Frameworks */,
39B9173C2BBD637200899F81 /* Yggdrasil.xcframework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -199,6 +211,7 @@
E593CE931DF905AF00D7265D /* Sources */,
E593CE941DF905AF00D7265D /* Frameworks */,
E593CE951DF905AF00D7265D /* Resources */,
39B9173E2BBD637200899F81 /* Embed Frameworks */,
);
buildRules = (
);
@ -309,10 +322,6 @@
/* Begin PBXTargetDependency section */
39F99B362A48F70F0045BD10 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
platformFilters = (
ios,
macos,
);
target = E593CE961DF905AF00D7265D /* YggdrasilNetworkExtension */;
targetProxy = 39F99B352A48F70F0045BD10 /* PBXContainerItemProxy */;
};
@ -363,6 +372,7 @@
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -413,6 +423,7 @@
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -548,9 +559,13 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "Yggdrasil Network Extension/YggdrasilNetworkExtension.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 32;
DEVELOPMENT_TEAM = R9AV23TXF2;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = R9AV23TXF2;
"DEVELOPMENT_TEAM[sdk=macosx*]" = R9AV23TXF2;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -566,8 +581,12 @@
PRODUCT_BUNDLE_IDENTIFIER = eu.neilalexander.yggdrasil.extension;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SUPPORTS_MACCATALYST = YES;
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Provisioning eu.neilalexander.yggdrasil.extension";
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "Provisioning2 eu.neilalexander.yggdrasil.extension";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Yggdrasil Network Extension/YggdrasilNetworkExtension-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -580,9 +599,13 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "Yggdrasil Network Extension/YggdrasilNetworkExtension.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 32;
DEVELOPMENT_TEAM = R9AV23TXF2;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = R9AV23TXF2;
"DEVELOPMENT_TEAM[sdk=macosx*]" = R9AV23TXF2;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -599,8 +622,12 @@
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "8ce353d5-fd5f-4d5e-b664-8ad294091125";
PROVISIONING_PROFILE_SPECIFIER = "";
SUPPORTS_MACCATALYST = YES;
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Provisioning eu.neilalexander.yggdrasil.extension";
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "Provisioning2 eu.neilalexander.yggdrasil.extension";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Yggdrasil Network Extension/YggdrasilNetworkExtension-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";

View file

@ -23,7 +23,7 @@ struct Application: App {
var body: some Scene {
WindowGroup {
NavigationSplitView {
ZStack {
VStack {
List(selection: $selection) {
NavigationLink(destination: StatusView()) {
HStack {

View file

@ -8,22 +8,25 @@
import SwiftUI
struct PeersView: View {
// @Binding public var yggdrasilConfiguration: ConfigurationProxy
@ObservedObject private var appDelegate = Application.appDelegate
#if os(iOS)
@Environment(\.editMode) var editMode
#endif
var body: some View {
Form {
Section(content: {
ForEach(Array(appDelegate.yggdrasilConfig.peers.enumerated()), id: \.offset) { index, peer in
HStack() {
Text(peer)
//TextField("", text: $yggdrasilConfiguration.peers[index])
// .multilineTextAlignment(.leading)
TextField("Peer", text: $appDelegate.yggdrasilConfig.peers[index])
.labelsHidden()
#if os(iOS)
.disabled(!editMode!.wrappedValue.isEditing)
#endif
#if os(macOS)
Spacer()
Button(role: .destructive) {
// appDelegate.yggdrasilConfig.peers.remove { $0 == peerURI }
// self.delete(at: appDelegate.yggdrasilConfig.peers.firstIndex { $0 == peerURI })
appDelegate.yggdrasilConfig.peers.remove(at: index)
} label: {
Image(systemName: "xmark.circle.fill")
}
@ -38,13 +41,15 @@ struct PeersView: View {
appDelegate.yggdrasilConfig.peers.remove(atOffsets: indexSet)
}
#if os(macOS)
Button {
appDelegate.yggdrasilConfig.peers.append("foo")
appDelegate.yggdrasilConfig.peers.append("")
} label: {
Label("Add peer", systemImage: "plus")
}
.buttonStyle(.plain)
.foregroundColor(.accentColor)
#endif
Text("Yggdrasil will automatically attempt to connect to configured peers when started. If you configure more than one peer, your device may carry traffic on behalf of other network nodes. Avoid this by configuring only a single peer. Data charges may apply when using mobile data.")
.font(.system(size: 11))
@ -70,20 +75,27 @@ struct PeersView: View {
.foregroundColor(.gray)
}
}
TextField("Multicast password", text: $appDelegate.yggdrasilConfig.multicastPassword)
}, header: {
Text("Local connectivity")
})
}
#if os(iOS)
.toolbar {
// EditButton()
}
#endif
.formStyle(.grouped)
.navigationTitle("Peers")
#if os(iOS)
#if os(iOS)
.toolbar {
Button("Add", systemImage: "plus") {
appDelegate.yggdrasilConfig.peers.append("")
}
EditButton()
.onChange(of: editMode!.wrappedValue) { edit in
if !edit.isEditing {
try? appDelegate.yggdrasilConfig.save(to: &appDelegate.vpnManager)
}
}
}
.navigationBarTitleDisplayMode(.large)
#endif
#endif
}
}

View file

@ -16,7 +16,7 @@ struct SettingsView: View {
var body: some View {
Form {
Section(content: {
TextField("Device Name", text: $deviceName)
TextField("Device Name", text: $appDelegate.yggdrasilConfig.deviceName)
}, header: {
Text("Public Identity")
})

View file

@ -93,6 +93,8 @@ struct StatusView: View {
Text(appDelegate.yggdrasilVersion())
.foregroundColor(Color.gray)
}
}, header: {
Text("Status")
})
Section(content: {
@ -114,7 +116,7 @@ struct StatusView: View {
.lineLimit(1)
.textSelection(.enabled)
}
HStack {
/*HStack {
Text("Coordinates")
Spacer()
Text(appDelegate.yggdrasilCoords)
@ -122,7 +124,7 @@ struct StatusView: View {
.truncationMode(.tail)
.lineLimit(1)
.textSelection(.enabled)
}
}*/
HStack {
Text("Public Key")
Spacer()
@ -133,7 +135,35 @@ struct StatusView: View {
.lineLimit(1)
.textSelection(.enabled)
}
}, header: {
Text("Details")
})
if self.appDelegate.yggdrasilEnabled {
Section(content: {
List(self.appDelegate.yggdrasilPeers.sorted(by: { a, b in
a.key < a.key
}), id: \.remote) { peer in
VStack {
Text(peer.remote)
.frame(maxWidth: .infinity, alignment: .leading)
.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(.all, 2)
}
}, header: {
Text("Peers")
})
}
}
.formStyle(.grouped)
.navigationTitle("Yggdrasil")