diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 7e0e3951..014a5128 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -679,7 +679,7 @@ "Airtime" : { }, - "AirTm" : { + "AirTm %@%%" : { }, "Alert" : { @@ -1428,7 +1428,7 @@ "Barometric Pressure" : { }, - "Batt" : { + "Batt %@%%" : { }, "Battery Level %" : { @@ -3091,7 +3091,7 @@ "CHG" : { }, - "ChUtil" : { + "ChUtil %@%% " : { }, "Clear" : { @@ -22292,7 +22292,7 @@ "Via Mqtt" : { }, - "Volt" : { + "Volt %@ " : { }, "voltage" : { diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index e6a7f0cb..5d6cceb8 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1061,7 +1061,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate } } } catch { - + Logger.data.error("💥 Send message failure \(self.connectedPeripheral.num.toHex(), privacy: .public) to \(toUserNum.toHex(), privacy: .public)") } } return success diff --git a/Meshtastic/Views/Nodes/DeviceMetricsLog.swift b/Meshtastic/Views/Nodes/DeviceMetricsLog.swift index bbabf31e..b0c917dd 100644 --- a/Meshtastic/Views/Nodes/DeviceMetricsLog.swift +++ b/Meshtastic/Views/Nodes/DeviceMetricsLog.swift @@ -12,6 +12,7 @@ struct DeviceMetricsLog: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager + private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom } @State private var isPresentingClearLogConfirm: Bool = false @State var isExporting = false @@ -21,6 +22,8 @@ struct DeviceMetricsLog: View { @State private var airtimeChartColor: Color = .yellow @State private var channelUtilizationChartColor: Color = .green @ObservedObject var node: NodeInfoEntity + @State private var sortOrder = [KeyPathComparator(\TelemetryEntity.time, order: .reverse)] + @State private var selection: TelemetryEntity.ID? var body: some View { VStack { @@ -60,11 +63,9 @@ struct DeviceMetricsLog: View { RuleMark(y: .value("10% Airtime", 10)) .lineStyle(StrokeStyle(lineWidth: 1, dash: [5, 10])) .foregroundStyle(.yellow) - RuleMark(y: .value("Network Status Orange", 25)) .lineStyle(StrokeStyle(lineWidth: 1, dash: [5, 10])) .foregroundStyle(.orange) - RuleMark(y: .value("Network Status Red", 50)) .lineStyle(StrokeStyle(lineWidth: 1, dash: [5, 10])) .foregroundStyle(.red) @@ -97,10 +98,84 @@ struct DeviceMetricsLog: View { } let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMdjmma", options: 0, locale: Locale.current) let dateFormatString = (localeDateFormat ?? "M/d/YY j:mma").replacingOccurrences(of: ",", with: "") - if UIScreen.main.bounds.size.width > 768 && (UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac) { - // Add a table for mac and ipad - // Table(Array(deviceMetrics),id: \.self) { - Table(deviceMetrics) { + /// New SwiftUI Table + if #available(iOS 17.4, macOS 14.4, *) { + Table(deviceMetrics, selection: $selection, sortOrder: $sortOrder) { + if idiom == .phone { + TableColumn("battery.level") { dm in + HStack { + Text(dm.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized) + Spacer() + } + .font(.caption) + HStack { + if dm.batteryLevel > 100 { + Text("PWD") + } else { + Text("Batt \(String(dm.batteryLevel))%") + } + Text("Volt \(String(format: "%.2f", dm.voltage)) ") + Text("ChUtil \(String(format: "%.2f", dm.channelUtilization))% ") + Text("AirTm \(String(format: "%.2f", dm.airUtilTx))%") + Spacer() + } + .font(.caption2) + } + .width(ideal: 200, max: .infinity) + } else { + TableColumn("battery.level") { dm in + if dm.batteryLevel > 100 { + Text("Powered") + } else { + Text("\(String(dm.batteryLevel))%") + } + } + TableColumn("voltage") { dm in + Text("\(String(format: "%.2f", dm.voltage))") + } + TableColumn("channel.utilization") { dm in + Text("\(String(format: "%.2f", dm.channelUtilization))%") + } + TableColumn("airtime") { dm in + Text("\(String(format: "%.2f", dm.airUtilTx))%") + } + TableColumn("uptime") { dm in + let now = Date.now + let later = now + TimeInterval(dm.uptimeSeconds) + let components = (now.. 100 { + Text("PWD") + } else { + Text("Batt \(String(dm.batteryLevel))%") + } + Text("Volt \(String(format: "%.2f", dm.voltage)) ") + Text("ChUtil \(String(format: "%.2f", dm.channelUtilization))% ") + Text("AirTm \(String(format: "%.2f", dm.airUtilTx))%") + Spacer() + } + .font(.caption2) + } + .width(ideal: 200, max: .infinity) + } + } else { + Table(deviceMetrics, selection: $selection, sortOrder: $sortOrder) { TableColumn("battery.level") { dm in if dm.batteryLevel > 100 { Text("Powered") @@ -128,56 +203,6 @@ struct DeviceMetricsLog: View { } .width(min: 180) } - } else { - ScrollView { - let columns = [ - GridItem(.flexible(minimum: 20, maximum: 60), spacing: 0.1), - GridItem(.flexible(minimum: 20, maximum: 60), spacing: 0.1), - GridItem(.flexible(minimum: 20, maximum: 60), spacing: 0.1), - GridItem(.flexible(minimum: 20, maximum: 60), spacing: 0.1), - GridItem(.flexible(minimum: 100, maximum: .infinity), spacing: 0.1) - ] - LazyVGrid(columns: columns, alignment: .leading, spacing: 1) { - GridRow { - Text("Batt") - .font(.caption) - .fontWeight(.bold) - Text("Volt") - .font(.caption) - .fontWeight(.bold) - Text("ChUtil") - .font(.caption) - .fontWeight(.bold) - Text("AirTm") - .font(.caption) - .fontWeight(.bold) - Text("timestamp") - .font(.caption) - .fontWeight(.bold) - } - ForEach(deviceMetrics) { dm in - GridRow { - if dm.batteryLevel > 100 { - Text("PWD") - .font(.caption) - } else { - Text("\(String(dm.batteryLevel))%") - .font(.caption) - } - Text(String(dm.voltage)) - .font(.caption) - Text("\(String(format: "%.2f", dm.channelUtilization))%") - .font(.caption) - Text("\(String(format: "%.2f", dm.airUtilTx))%") - .font(.caption) - Text(dm.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized) - .font(.caption) - } - } - } - .padding(.leading, 15) - .padding(.trailing, 5) - } } HStack { Button(role: .destructive) { @@ -187,7 +212,7 @@ struct DeviceMetricsLog: View { } .buttonStyle(.bordered) .buttonBorderShape(.capsule) - .controlSize(.large) + .controlSize(idiom == .phone ? .regular : .large) .padding(.bottom) .padding(.leading) .confirmationDialog( @@ -212,7 +237,7 @@ struct DeviceMetricsLog: View { } .buttonStyle(.bordered) .buttonBorderShape(.capsule) - .controlSize(.large) + .controlSize(idiom == .phone ? .regular : .large) .padding(.bottom) .padding(.trailing) }