mirror of
https://github.com/yggdrasil-network/yggdrasil-ios.git
synced 2025-04-28 22:25:10 +03:00
App updates
This commit is contained in:
parent
2cb5eebcd2
commit
5fbb735f56
9 changed files with 204 additions and 91 deletions
|
@ -13,6 +13,8 @@ import AppKit
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Yggdrasil
|
import Yggdrasil
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
|
import Foundation
|
||||||
|
import CoreData
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
class PlatformItemSource: NSObject, UIActivityItemSource {
|
class PlatformItemSource: NSObject, UIActivityItemSource {
|
||||||
|
@ -31,9 +33,10 @@ class PlatformItemSource: NSObject {}
|
||||||
class ConfigurationProxy: PlatformItemSource {
|
class ConfigurationProxy: PlatformItemSource {
|
||||||
private var manager: NETunnelProviderManager?
|
private var manager: NETunnelProviderManager?
|
||||||
private var json: Data? = nil
|
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
|
self.manager = manager
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
@ -64,14 +67,13 @@ class ConfigurationProxy: PlatformItemSource {
|
||||||
self.set("Listen", to: [] as [String])
|
self.set("Listen", to: [] as [String])
|
||||||
self.set("AdminListen", to: "none")
|
self.set("AdminListen", to: "none")
|
||||||
self.set("IfName", to: "dummy")
|
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 {
|
if self.get("AutoStart") == nil {
|
||||||
self.set("AutoStart", to: ["Any": false, "WiFi": false, "Mobile": false, "Ethernet": false] as [String: Bool])
|
self.set("AutoStart", to: ["Any": false, "WiFi": false, "Mobile": false, "Ethernet": false] as [String: Bool])
|
||||||
}
|
}
|
||||||
|
|
||||||
let multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? []
|
let multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? []
|
||||||
if multicastInterfaces.count == 0 {
|
if multicastInterfaces.count != 1 {
|
||||||
self.set("MulticastInterfaces", to: [
|
self.set("MulticastInterfaces", to: [
|
||||||
[
|
[
|
||||||
"Regex": "en.*",
|
"Regex": "en.*",
|
||||||
|
@ -94,7 +96,7 @@ class ConfigurationProxy: PlatformItemSource {
|
||||||
var multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? []
|
var multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? []
|
||||||
multicastInterfaces[0]["Beacon"] = newValue
|
multicastInterfaces[0]["Beacon"] = newValue
|
||||||
self.set("MulticastInterfaces", to: multicastInterfaces)
|
self.set("MulticastInterfaces", to: multicastInterfaces)
|
||||||
self.trySave()
|
self.saveSoon()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +112,23 @@ class ConfigurationProxy: PlatformItemSource {
|
||||||
var multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? []
|
var multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? []
|
||||||
multicastInterfaces[0]["Listen"] = newValue
|
multicastInterfaces[0]["Listen"] = newValue
|
||||||
self.set("MulticastInterfaces", to: multicastInterfaces)
|
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 {
|
set {
|
||||||
self.set("Any", inSection: "AutoStart", to: newValue)
|
self.set("Any", inSection: "AutoStart", to: newValue)
|
||||||
self.trySave()
|
self.saveSoon()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +148,7 @@ class ConfigurationProxy: PlatformItemSource {
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
self.set("WiFi", inSection: "AutoStart", to: newValue)
|
self.set("WiFi", inSection: "AutoStart", to: newValue)
|
||||||
self.trySave()
|
self.saveSoon()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +158,7 @@ class ConfigurationProxy: PlatformItemSource {
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
self.set("Ethernet", inSection: "AutoStart", to: newValue)
|
self.set("Ethernet", inSection: "AutoStart", to: newValue)
|
||||||
self.trySave()
|
self.saveSoon()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,7 +168,17 @@ class ConfigurationProxy: PlatformItemSource {
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
self.set("Mobile", inSection: "AutoStart", to: newValue)
|
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 {
|
set {
|
||||||
self.set("Peers", to: newValue)
|
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 {
|
if var manager = self.manager {
|
||||||
try? self.save(to: &manager)
|
try? self.save(to: &manager)
|
||||||
}
|
}
|
||||||
|
self.timer = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func save(to manager: inout NETunnelProviderManager) throws {
|
func save(to manager: inout NETunnelProviderManager) throws {
|
||||||
|
@ -254,6 +294,8 @@ class ConfigurationProxy: PlatformItemSource {
|
||||||
providerProtocol.serverAddress = "yggdrasil"
|
providerProtocol.serverAddress = "yggdrasil"
|
||||||
providerProtocol.username = self.get("PublicKey") as? String ?? self.get("SigningPublicKey") as? String ?? "(unknown public key)"
|
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()
|
let disconnectrule = NEOnDemandRuleDisconnect()
|
||||||
var rules: [NEOnDemandRule] = [disconnectrule]
|
var rules: [NEOnDemandRule] = [disconnectrule]
|
||||||
if self.get("Any", inSection: "AutoStart") as? Bool ?? false {
|
if self.get("Any", inSection: "AutoStart") as? Bool ?? false {
|
||||||
|
@ -283,10 +325,10 @@ class ConfigurationProxy: PlatformItemSource {
|
||||||
manager.onDemandRules = rules
|
manager.onDemandRules = rules
|
||||||
manager.isOnDemandEnabled = rules.count > 1
|
manager.isOnDemandEnabled = rules.count > 1
|
||||||
providerProtocol.disconnectOnSleep = rules.count > 1
|
providerProtocol.disconnectOnSleep = rules.count > 1
|
||||||
|
|
||||||
manager.protocolConfiguration = providerProtocol
|
manager.protocolConfiguration = providerProtocol
|
||||||
|
|
||||||
manager.saveToPreferences(completionHandler: { (error:Error?) in
|
manager.saveToPreferences(completionHandler: { error in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
print(error)
|
print(error)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -22,11 +22,12 @@ typealias ApplicationDelegateAdaptor = NSApplicationDelegateAdaptor
|
||||||
|
|
||||||
class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
|
class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
|
||||||
var vpnManager: NETunnelProviderManager = NETunnelProviderManager()
|
var vpnManager: NETunnelProviderManager = NETunnelProviderManager()
|
||||||
var yggdrasilConfig: ConfigurationProxy = ConfigurationProxy()
|
var yggdrasilConfig: ConfigurationProxy
|
||||||
let yggdrasilComponent = "eu.neilalexander.yggdrasil.extension"
|
let yggdrasilComponent = "eu.neilalexander.yggdrasil.extension"
|
||||||
private var adminTimer: DispatchSourceTimer?
|
private var adminTimer: DispatchSourceTimer?
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
|
self.yggdrasilConfig = ConfigurationProxy(manager: self.vpnManager)
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(forName: .NEVPNStatusDidChange, object: nil, queue: nil, using: { notification in
|
NotificationCenter.default.addObserver(forName: .NEVPNStatusDidChange, object: nil, queue: nil, using: { notification in
|
||||||
|
@ -34,7 +35,6 @@ class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
|
||||||
switch conn.status {
|
switch conn.status {
|
||||||
case .connected:
|
case .connected:
|
||||||
self.requestSummaryIPC()
|
self.requestSummaryIPC()
|
||||||
self.requestStatusIPC()
|
|
||||||
case .disconnecting, .disconnected:
|
case .disconnecting, .disconnected:
|
||||||
self.clearStatus()
|
self.clearStatus()
|
||||||
default:
|
default:
|
||||||
|
@ -73,9 +73,7 @@ class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
|
||||||
@Published var yggdrasilSubnet: String = "N/A"
|
@Published var yggdrasilSubnet: String = "N/A"
|
||||||
@Published var yggdrasilCoords: String = "[]"
|
@Published var yggdrasilCoords: String = "[]"
|
||||||
|
|
||||||
@Published var yggdrasilPeers: [[String: Any]] = [[:]]
|
@Published var yggdrasilPeers: [YggdrasilPeer] = []
|
||||||
@Published var yggdrasilDHT: [[String: Any]] = [[:]]
|
|
||||||
@Published var yggdrasilNodeInfo: [String: Any] = [:]
|
|
||||||
|
|
||||||
func yggdrasilVersion() -> String {
|
func yggdrasilVersion() -> String {
|
||||||
return Yggdrasil.MobileGetVersion()
|
return Yggdrasil.MobileGetVersion()
|
||||||
|
@ -111,7 +109,7 @@ class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
|
||||||
|
|
||||||
func updateStatus(conn: NEVPNConnection) {
|
func updateStatus(conn: NEVPNConnection) {
|
||||||
if conn.status == .connected {
|
if conn.status == .connected {
|
||||||
self.requestStatusIPC()
|
self.requestSummaryIPC()
|
||||||
} else if conn.status == .disconnecting || conn.status == .disconnected {
|
} else if conn.status == .disconnecting || conn.status == .disconnected {
|
||||||
self.clearStatus()
|
self.clearStatus()
|
||||||
}
|
}
|
||||||
|
@ -183,30 +181,14 @@ class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
|
||||||
if let session = self.vpnManager.connection as? NETunnelProviderSession {
|
if let session = self.vpnManager.connection as? NETunnelProviderSession {
|
||||||
try? session.sendProviderMessage("summary".data(using: .utf8)!) { js in
|
try? session.sendProviderMessage("summary".data(using: .utf8)!) { js in
|
||||||
if let js = js, let summary = try? JSONDecoder().decode(YggdrasilSummary.self, from: js) {
|
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.yggdrasilIP = summary.address
|
||||||
self.yggdrasilSubnet = summary.subnet
|
self.yggdrasilSubnet = summary.subnet
|
||||||
self.yggdrasilPublicKey = summary.publicKey
|
self.yggdrasilPublicKey = summary.publicKey
|
||||||
}
|
self.yggdrasilPeers = summary.peers
|
||||||
}
|
self.yggdrasilConnected = summary.peers.count > 0
|
||||||
}
|
|
||||||
}
|
print(self.yggdrasilPeers)
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -218,6 +200,5 @@ class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
|
||||||
self.yggdrasilSubnet = "N/A"
|
self.yggdrasilSubnet = "N/A"
|
||||||
self.yggdrasilCoords = "[]"
|
self.yggdrasilCoords = "[]"
|
||||||
self.yggdrasilPeers = []
|
self.yggdrasilPeers = []
|
||||||
self.yggdrasilDHT = []
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,29 @@ struct YggdrasilSummary: Codable {
|
||||||
var address: String
|
var address: String
|
||||||
var subnet: String
|
var subnet: String
|
||||||
var publicKey: String
|
var publicKey: String
|
||||||
|
var enabled: Bool
|
||||||
|
var peers: [YggdrasilPeer]
|
||||||
|
|
||||||
|
func list() -> [String] {
|
||||||
|
return peers.map { $0.remote }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct YggdrasilStatus: Codable {
|
struct YggdrasilPeer: Codable, Identifiable {
|
||||||
var enabled: Bool
|
var id: String { remote } // For Identifiable protocol
|
||||||
var coords: String
|
let remote: String
|
||||||
var peers: Data
|
let up: Bool
|
||||||
var dht: Data
|
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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,25 +98,28 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
let request = String(data: messageData, encoding: .utf8)
|
let request = String(data: messageData, encoding: .utf8)
|
||||||
switch request {
|
switch request {
|
||||||
case "summary":
|
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(
|
let summary = YggdrasilSummary(
|
||||||
address: self.yggdrasil.getAddressString(),
|
address: self.yggdrasil.getAddressString(),
|
||||||
subnet: self.yggdrasil.getSubnetString(),
|
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) {
|
if let json = try? JSONEncoder().encode(summary) {
|
||||||
completionHandler?(json)
|
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:
|
default:
|
||||||
completionHandler?(nil)
|
completionHandler?(nil)
|
||||||
|
|
|
@ -3,17 +3,18 @@
|
||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 55;
|
objectVersion = 54;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
391B72A82A2FD90100896278 /* PacketTunnelProvider+FileDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 391B72A62A2FD90100896278 /* PacketTunnelProvider+FileDescriptor.swift */; };
|
391B72A82A2FD90100896278 /* PacketTunnelProvider+FileDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 391B72A62A2FD90100896278 /* PacketTunnelProvider+FileDescriptor.swift */; };
|
||||||
3952ADB829945AF700B3835D /* ConfigurationProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3952ADB629945AF700B3835D /* ConfigurationProxy.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 */; };
|
39B51A4A2997062E0059D29D /* PeersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39B51A492997062E0059D29D /* PeersView.swift */; };
|
||||||
39B51A4B29994F240059D29D /* ConfigurationProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3952ADB629945AF700B3835D /* ConfigurationProxy.swift */; };
|
39B51A4B29994F240059D29D /* ConfigurationProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3952ADB629945AF700B3835D /* ConfigurationProxy.swift */; };
|
||||||
39B51A4C29994F350059D29D /* CrossPlatformAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3952ADB929945AFA00B3835D /* CrossPlatformAppDelegate.swift */; };
|
39B51A4C29994F350059D29D /* CrossPlatformAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3952ADB929945AFA00B3835D /* CrossPlatformAppDelegate.swift */; };
|
||||||
39B51A4D299951D60059D29D /* IPCResponses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CC924B221DEDCE004960DC /* IPCResponses.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 */; };
|
39BF9FC72A2F6FFD000E7269 /* Yggdrasil.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3996AF37270328080070947D /* Yggdrasil.xcframework */; };
|
||||||
39CC924D221DEDD3004960DC /* IPCResponses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CC924B221DEDCE004960DC /* IPCResponses.swift */; };
|
39CC924D221DEDD3004960DC /* IPCResponses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CC924B221DEDCE004960DC /* IPCResponses.swift */; };
|
||||||
39F0205E2996CD760093F603 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39F0205D2996CD760093F603 /* Application.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 */; };
|
39F020662996CD770093F603 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 39F020652996CD770093F603 /* Preview Assets.xcassets */; };
|
||||||
39F0206B2996CF260093F603 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39F0206A2996CF260093F603 /* SettingsView.swift */; };
|
39F0206B2996CF260093F603 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39F0206A2996CF260093F603 /* SettingsView.swift */; };
|
||||||
39F99B342A48F6E50045BD10 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 39AE88382319C93F0010FFF6 /* NetworkExtension.framework */; };
|
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 */; };
|
E593CE9C1DF905AF00D7265D /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E593CE9B1DF905AF00D7265D /* PacketTunnelProvider.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
@ -37,6 +38,17 @@
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase 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 */ = {
|
39F99B372A48F7380045BD10 /* CopyFiles */ = {
|
||||||
isa = PBXCopyFilesBuildPhase;
|
isa = PBXCopyFilesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -87,7 +99,7 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
3996AF39270328080070947D /* Yggdrasil.xcframework in Frameworks */,
|
39B9173C2BBD637200899F81 /* Yggdrasil.xcframework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -199,6 +211,7 @@
|
||||||
E593CE931DF905AF00D7265D /* Sources */,
|
E593CE931DF905AF00D7265D /* Sources */,
|
||||||
E593CE941DF905AF00D7265D /* Frameworks */,
|
E593CE941DF905AF00D7265D /* Frameworks */,
|
||||||
E593CE951DF905AF00D7265D /* Resources */,
|
E593CE951DF905AF00D7265D /* Resources */,
|
||||||
|
39B9173E2BBD637200899F81 /* Embed Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
|
@ -309,10 +322,6 @@
|
||||||
/* Begin PBXTargetDependency section */
|
/* Begin PBXTargetDependency section */
|
||||||
39F99B362A48F70F0045BD10 /* PBXTargetDependency */ = {
|
39F99B362A48F70F0045BD10 /* PBXTargetDependency */ = {
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
platformFilters = (
|
|
||||||
ios,
|
|
||||||
macos,
|
|
||||||
);
|
|
||||||
target = E593CE961DF905AF00D7265D /* YggdrasilNetworkExtension */;
|
target = E593CE961DF905AF00D7265D /* YggdrasilNetworkExtension */;
|
||||||
targetProxy = 39F99B352A48F70F0045BD10 /* PBXContainerItemProxy */;
|
targetProxy = 39F99B352A48F70F0045BD10 /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
|
@ -363,6 +372,7 @@
|
||||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
|
||||||
SUPPORTS_MACCATALYST = NO;
|
SUPPORTS_MACCATALYST = NO;
|
||||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
|
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
@ -413,6 +423,7 @@
|
||||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
|
||||||
SUPPORTS_MACCATALYST = NO;
|
SUPPORTS_MACCATALYST = NO;
|
||||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
|
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
@ -548,9 +559,13 @@
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = "Yggdrasil Network Extension/YggdrasilNetworkExtension.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "Yggdrasil Network Extension/YggdrasilNetworkExtension.entitlements";
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
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;
|
CURRENT_PROJECT_VERSION = 32;
|
||||||
DEVELOPMENT_TEAM = R9AV23TXF2;
|
DEVELOPMENT_TEAM = "";
|
||||||
|
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = R9AV23TXF2;
|
||||||
|
"DEVELOPMENT_TEAM[sdk=macosx*]" = R9AV23TXF2;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
@ -566,8 +581,12 @@
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = eu.neilalexander.yggdrasil.extension;
|
PRODUCT_BUNDLE_IDENTIFIER = eu.neilalexander.yggdrasil.extension;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
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_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_OBJC_BRIDGING_HEADER = "Yggdrasil Network Extension/YggdrasilNetworkExtension-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
@ -580,9 +599,13 @@
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = "Yggdrasil Network Extension/YggdrasilNetworkExtension.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "Yggdrasil Network Extension/YggdrasilNetworkExtension.entitlements";
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
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;
|
CURRENT_PROJECT_VERSION = 32;
|
||||||
DEVELOPMENT_TEAM = R9AV23TXF2;
|
DEVELOPMENT_TEAM = "";
|
||||||
|
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = R9AV23TXF2;
|
||||||
|
"DEVELOPMENT_TEAM[sdk=macosx*]" = R9AV23TXF2;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
@ -599,8 +622,12 @@
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE = "8ce353d5-fd5f-4d5e-b664-8ad294091125";
|
PROVISIONING_PROFILE = "8ce353d5-fd5f-4d5e-b664-8ad294091125";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
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_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_OBJC_BRIDGING_HEADER = "Yggdrasil Network Extension/YggdrasilNetworkExtension-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
|
|
@ -23,7 +23,7 @@ struct Application: App {
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
NavigationSplitView {
|
NavigationSplitView {
|
||||||
ZStack {
|
VStack {
|
||||||
List(selection: $selection) {
|
List(selection: $selection) {
|
||||||
NavigationLink(destination: StatusView()) {
|
NavigationLink(destination: StatusView()) {
|
||||||
HStack {
|
HStack {
|
||||||
|
|
|
@ -8,22 +8,25 @@
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct PeersView: View {
|
struct PeersView: View {
|
||||||
// @Binding public var yggdrasilConfiguration: ConfigurationProxy
|
|
||||||
@ObservedObject private var appDelegate = Application.appDelegate
|
@ObservedObject private var appDelegate = Application.appDelegate
|
||||||
|
#if os(iOS)
|
||||||
|
@Environment(\.editMode) var editMode
|
||||||
|
#endif
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
Section(content: {
|
Section(content: {
|
||||||
ForEach(Array(appDelegate.yggdrasilConfig.peers.enumerated()), id: \.offset) { index, peer in
|
ForEach(Array(appDelegate.yggdrasilConfig.peers.enumerated()), id: \.offset) { index, peer in
|
||||||
HStack() {
|
HStack() {
|
||||||
Text(peer)
|
TextField("Peer", text: $appDelegate.yggdrasilConfig.peers[index])
|
||||||
//TextField("", text: $yggdrasilConfiguration.peers[index])
|
.labelsHidden()
|
||||||
// .multilineTextAlignment(.leading)
|
#if os(iOS)
|
||||||
|
.disabled(!editMode!.wrappedValue.isEditing)
|
||||||
|
#endif
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
Spacer()
|
Spacer()
|
||||||
Button(role: .destructive) {
|
Button(role: .destructive) {
|
||||||
// appDelegate.yggdrasilConfig.peers.remove { $0 == peerURI }
|
appDelegate.yggdrasilConfig.peers.remove(at: index)
|
||||||
// self.delete(at: appDelegate.yggdrasilConfig.peers.firstIndex { $0 == peerURI })
|
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "xmark.circle.fill")
|
Image(systemName: "xmark.circle.fill")
|
||||||
}
|
}
|
||||||
|
@ -38,13 +41,15 @@ struct PeersView: View {
|
||||||
appDelegate.yggdrasilConfig.peers.remove(atOffsets: indexSet)
|
appDelegate.yggdrasilConfig.peers.remove(atOffsets: indexSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
Button {
|
Button {
|
||||||
appDelegate.yggdrasilConfig.peers.append("foo")
|
appDelegate.yggdrasilConfig.peers.append("")
|
||||||
} label: {
|
} label: {
|
||||||
Label("Add peer", systemImage: "plus")
|
Label("Add peer", systemImage: "plus")
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
.foregroundColor(.accentColor)
|
.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.")
|
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))
|
.font(.system(size: 11))
|
||||||
|
@ -70,20 +75,27 @@ struct PeersView: View {
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TextField("Multicast password", text: $appDelegate.yggdrasilConfig.multicastPassword)
|
||||||
}, header: {
|
}, header: {
|
||||||
Text("Local connectivity")
|
Text("Local connectivity")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
#if os(iOS)
|
|
||||||
.toolbar {
|
|
||||||
// EditButton()
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
.formStyle(.grouped)
|
.formStyle(.grouped)
|
||||||
.navigationTitle("Peers")
|
.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)
|
.navigationBarTitleDisplayMode(.large)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ struct SettingsView: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
Section(content: {
|
Section(content: {
|
||||||
TextField("Device Name", text: $deviceName)
|
TextField("Device Name", text: $appDelegate.yggdrasilConfig.deviceName)
|
||||||
}, header: {
|
}, header: {
|
||||||
Text("Public Identity")
|
Text("Public Identity")
|
||||||
})
|
})
|
||||||
|
|
|
@ -93,6 +93,8 @@ struct StatusView: View {
|
||||||
Text(appDelegate.yggdrasilVersion())
|
Text(appDelegate.yggdrasilVersion())
|
||||||
.foregroundColor(Color.gray)
|
.foregroundColor(Color.gray)
|
||||||
}
|
}
|
||||||
|
}, header: {
|
||||||
|
Text("Status")
|
||||||
})
|
})
|
||||||
|
|
||||||
Section(content: {
|
Section(content: {
|
||||||
|
@ -114,7 +116,7 @@ struct StatusView: View {
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.textSelection(.enabled)
|
.textSelection(.enabled)
|
||||||
}
|
}
|
||||||
HStack {
|
/*HStack {
|
||||||
Text("Coordinates")
|
Text("Coordinates")
|
||||||
Spacer()
|
Spacer()
|
||||||
Text(appDelegate.yggdrasilCoords)
|
Text(appDelegate.yggdrasilCoords)
|
||||||
|
@ -122,7 +124,7 @@ struct StatusView: View {
|
||||||
.truncationMode(.tail)
|
.truncationMode(.tail)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.textSelection(.enabled)
|
.textSelection(.enabled)
|
||||||
}
|
}*/
|
||||||
HStack {
|
HStack {
|
||||||
Text("Public Key")
|
Text("Public Key")
|
||||||
Spacer()
|
Spacer()
|
||||||
|
@ -133,7 +135,35 @@ struct StatusView: View {
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.textSelection(.enabled)
|
.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)
|
.formStyle(.grouped)
|
||||||
.navigationTitle("Yggdrasil")
|
.navigationTitle("Yggdrasil")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue