Add connections dashboard

This commit is contained in:
世界
2024-10-06 21:41:07 +08:00
parent c223f50ffb
commit 9dadf5f648
17 changed files with 670 additions and 25 deletions
@@ -0,0 +1,121 @@
import Libbox
import SwiftUI
@MainActor
public struct ConnectionView: View {
private let connection: Connection
public init(_ connection: Connection) {
self.connection = connection
}
private func format(_ date: Date) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm:ss"
return formatter.string(from: date)
}
public func formatInterval(_ createdAt: Date, _ closedAt: Date) -> String {
LibboxFormatDuration(Int64((closedAt.timeIntervalSince1970 - createdAt.timeIntervalSince1970) * 1000))
}
@State private var alert: Alert?
public var body: some View {
FormNavigationLink {
ConnectionDetailsView(connection)
} label: {
VStack {
HStack {
VStack(alignment: .leading) {
HStack(alignment: .center) {
Text("\(connection.network.uppercased()) \(connection.displayDestination)")
Spacer()
if connection.closedAt == nil {
Text("Active").foregroundStyle(.green)
} else {
Text("Closed").foregroundStyle(.red)
}
}
.font(.caption2.monospaced().bold())
.padding([.bottom], 4)
HStack {
if let closedAt = connection.closedAt {
VStack(alignment: .leading) {
Text("\(LibboxFormatBytes(connection.uploadTotal))")
Text("\(LibboxFormatBytes(connection.downloadTotal))")
}
.font(.caption2)
VStack(alignment: .leading) {
Text(format(connection.createdAt))
Text(formatInterval(connection.createdAt, closedAt))
}
Spacer()
VStack(alignment: .trailing) {
Text(connection.inboundType + "/" + connection.inbound)
Text(connection.chain.reversed().joined(separator: "/"))
}
} else {
VStack(alignment: .leading) {
Text("\(LibboxFormatBytes(connection.upload))/s")
Text("\(LibboxFormatBytes(connection.download))/s")
}
.font(.caption2)
VStack(alignment: .leading) {
Text(LibboxFormatBytes(connection.uploadTotal))
Text(LibboxFormatBytes(connection.downloadTotal))
}
Spacer()
VStack(alignment: .trailing) {
Text(connection.inboundType + "/" + connection.inbound)
Text(connection.chain.reversed().joined(separator: "/"))
}
}
}
.font(.caption2.monospaced())
}
}
.foregroundColor(.textColor)
#if !os(tvOS)
.padding(EdgeInsets(top: 10, leading: 13, bottom: 10, trailing: 13))
.background(backgroundColor)
.cornerRadius(10)
#endif
}
.background(.clear)
}
#if !os(tvOS)
.buttonStyle(.borderless)
#endif
.alertBinding($alert)
.contextMenu {
if connection.closedAt == nil {
Button("Close", role: .destructive) {
Task {
await closeConnection()
}
}
.frame(maxWidth: .infinity, alignment: .leading)
}
}
}
private var backgroundColor: Color {
#if os(iOS)
return Color(uiColor: .secondarySystemGroupedBackground)
#elseif os(macOS)
return Color(nsColor: .textBackgroundColor)
#elseif os(tvOS)
return Color.black
#endif
}
private nonisolated func closeConnection() async {
do {
try LibboxNewStandaloneCommandClient()!.closeConnection(connection.id)
} catch {
await MainActor.run {
alert = Alert(error)
}
}
}
}