159 lines
5.6 KiB
Swift
159 lines
5.6 KiB
Swift
import Libbox
|
|
import Library
|
|
import SwiftUI
|
|
|
|
@MainActor
|
|
public struct ConnectionListView: View {
|
|
@Environment(\.scenePhase) private var scenePhase
|
|
@State private var isLoading = true
|
|
@StateObject private var commandClient = CommandClient(.connections)
|
|
@State private var connections: [Connection] = []
|
|
@State private var selection: ConnectionListPage = .active
|
|
@State private var searchText = ""
|
|
@State private var alert: Alert?
|
|
|
|
public init() {}
|
|
public var body: some View {
|
|
VStack {
|
|
if isLoading {
|
|
Text("Loading...")
|
|
} else {
|
|
if connections.isEmpty {
|
|
Text("Empty connections")
|
|
} else {
|
|
ScrollView {
|
|
LazyVGrid(columns: [GridItem(.flexible())], alignment: .leading) {
|
|
ForEach(connections.filter { it in
|
|
searchText == "" || it.performSearch(searchText)
|
|
}, id: \.hashValue) { it in
|
|
ConnectionView(it)
|
|
}
|
|
}
|
|
}
|
|
.padding()
|
|
}
|
|
}
|
|
}
|
|
.toolbar {
|
|
ToolbarItem {
|
|
Menu {
|
|
Picker("State", selection: $commandClient.connectionStateFilter) {
|
|
ForEach(ConnectionStateFilter.allCases) { state in
|
|
Text(state.name)
|
|
}
|
|
}
|
|
|
|
Picker("Sort By", selection: $commandClient.connectionSort) {
|
|
ForEach(ConnectionSort.allCases, id: \.self) { sortBy in
|
|
Text(sortBy.name)
|
|
}
|
|
}
|
|
|
|
Button("Close All Connections", role: .destructive) {
|
|
do {
|
|
try LibboxNewStandaloneCommandClient()!.closeConnections()
|
|
} catch {
|
|
alert = Alert(error)
|
|
}
|
|
}
|
|
} label: {
|
|
Label("Filter", systemImage: "line.3.horizontal.circle")
|
|
}
|
|
}
|
|
}
|
|
.searchable(text: $searchText)
|
|
.alertBinding($alert)
|
|
.onAppear {
|
|
connect()
|
|
}
|
|
.onDisappear {
|
|
commandClient.disconnect()
|
|
}
|
|
.onChangeCompat(of: scenePhase) { newValue in
|
|
if newValue == .active {
|
|
commandClient.connect()
|
|
} else {
|
|
commandClient.disconnect()
|
|
}
|
|
}
|
|
.onChangeCompat(of: commandClient.connectionStateFilter) { it in
|
|
commandClient.filterConnectionsNow()
|
|
Task {
|
|
await SharedPreferences.connectionStateFilter.set(it.rawValue)
|
|
}
|
|
}
|
|
.onChangeCompat(of: commandClient.connectionSort) { it in
|
|
commandClient.filterConnectionsNow()
|
|
Task {
|
|
await SharedPreferences.connectionSort.set(it.rawValue)
|
|
}
|
|
}
|
|
.onReceive(commandClient.$connections, perform: { connections in
|
|
if let connections {
|
|
self.connections = convertConnections(connections)
|
|
isLoading = false
|
|
}
|
|
})
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
|
|
#if os(iOS)
|
|
.background(Color(uiColor: .systemGroupedBackground))
|
|
#endif
|
|
}
|
|
|
|
private var backgroundColor: Color {
|
|
#if os(iOS)
|
|
return Color(uiColor: .secondarySystemGroupedBackground)
|
|
#elseif os(macOS)
|
|
return Color(nsColor: .textBackgroundColor)
|
|
#elseif os(tvOS)
|
|
return Color(uiColor: .black)
|
|
#endif
|
|
}
|
|
|
|
private func connect() {
|
|
if ApplicationLibrary.inPreview {
|
|
isLoading = false
|
|
} else {
|
|
commandClient.connect()
|
|
}
|
|
}
|
|
|
|
private func convertConnections(_ goConnections: [LibboxConnection]) -> [Connection] {
|
|
var connections = [Connection]()
|
|
for goConnection in goConnections {
|
|
if goConnection.outboundType == "dns" {
|
|
continue
|
|
}
|
|
var closedAt: Date?
|
|
if goConnection.closedAt > 0 {
|
|
closedAt = Date(timeIntervalSince1970: Double(goConnection.closedAt) / 1000)
|
|
}
|
|
connections.append(Connection(
|
|
id: goConnection.id_,
|
|
inbound: goConnection.inbound,
|
|
inboundType: goConnection.inboundType,
|
|
ipVersion: goConnection.ipVersion,
|
|
network: goConnection.network,
|
|
source: goConnection.source,
|
|
destination: goConnection.destination,
|
|
domain: goConnection.domain,
|
|
displayDestination: goConnection.displayDestination(),
|
|
protocolName: goConnection.protocol,
|
|
user: goConnection.user,
|
|
fromOutbound: goConnection.fromOutbound,
|
|
createdAt: Date(timeIntervalSince1970: Double(goConnection.createdAt) / 1000),
|
|
closedAt: closedAt,
|
|
upload: goConnection.uplink,
|
|
download: goConnection.downlink,
|
|
uploadTotal: goConnection.uplinkTotal,
|
|
downloadTotal: goConnection.downlinkTotal,
|
|
rule: goConnection.rule,
|
|
outbound: goConnection.outbound,
|
|
outboundType: goConnection.outboundType,
|
|
chain: goConnection.chain()!.toArray()
|
|
))
|
|
}
|
|
return connections
|
|
}
|
|
}
|