diff --git a/Meshtastic/Views/Helpers/BatteryLevelCompact.swift b/Meshtastic/Views/Helpers/BatteryLevelCompact.swift index f00dc6e5..f45c1c33 100644 --- a/Meshtastic/Views/Helpers/BatteryLevelCompact.swift +++ b/Meshtastic/Views/Helpers/BatteryLevelCompact.swift @@ -7,76 +7,70 @@ import SwiftUI struct BatteryLevelCompact: View { - var batteryLevel: Int32? + + @ObservedObject var node: NodeInfoEntity + var font: Font var iconFont: Font var color: Color var body: some View { - HStack(alignment: .center, spacing: 0) { - if batteryLevel == 100 { - Image(systemName: "battery.100.bolt") - .font(iconFont) - .foregroundColor(color) - .symbolRenderingMode(.hierarchical) - } else if batteryLevel! < 100 && batteryLevel! > 74 { - - Image(systemName: "battery.75") - .font(iconFont) - .foregroundColor(color) - .symbolRenderingMode(.hierarchical) - } else if batteryLevel! < 75 && batteryLevel! > 49 { - - Image(systemName: "battery.50") - .font(iconFont) - .foregroundColor(color) - .symbolRenderingMode(.hierarchical) - } else if batteryLevel! < 50 && batteryLevel! > 14 { - - Image(systemName: "battery.25") - .font(iconFont) - .foregroundColor(color) - .symbolRenderingMode(.hierarchical) - } else if batteryLevel! < 15 && batteryLevel! > 0 { - - Image(systemName: "battery.0") - .font(iconFont) - .foregroundColor(color) - .symbolRenderingMode(.hierarchical) - } else if batteryLevel! == 0 { - Image(systemName: "battery.0") - .font(iconFont) - .foregroundColor(.red) - .symbolRenderingMode(.hierarchical) - } else if batteryLevel! > 100 { - Image(systemName: "powerplug") - .font(iconFont) - .foregroundColor(color) - .symbolRenderingMode(.hierarchical) - } - if batteryLevel ?? 0 > 100 { - Text("PWD") - .font(font) - } else if batteryLevel == 100 { - Text("CHG") - .font(font) - } else { - Text("\(batteryLevel ?? 0)%") - .font(font) + let deviceMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0")) + let mostRecent = deviceMetrics?.lastObject as? TelemetryEntity + let batteryLevel = mostRecent?.batteryLevel ?? 0 + if deviceMetrics?.count ?? 0 > 0 { + HStack(alignment: .center, spacing: 0) { + if batteryLevel == 100 { + Image(systemName: "battery.100.bolt") + .font(iconFont) + .foregroundColor(color) + .symbolRenderingMode(.hierarchical) + } else if batteryLevel < 100 && batteryLevel > 74 { + + Image(systemName: "battery.75") + .font(iconFont) + .foregroundColor(color) + .symbolRenderingMode(.hierarchical) + } else if batteryLevel < 75 && batteryLevel > 49 { + + Image(systemName: "battery.50") + .font(iconFont) + .foregroundColor(color) + .symbolRenderingMode(.hierarchical) + } else if batteryLevel < 50 && batteryLevel > 14 { + + Image(systemName: "battery.25") + .font(iconFont) + .foregroundColor(color) + .symbolRenderingMode(.hierarchical) + } else if batteryLevel < 15 && batteryLevel > 0 { + + Image(systemName: "battery.0") + .font(iconFont) + .foregroundColor(color) + .symbolRenderingMode(.hierarchical) + } else if batteryLevel == 0 { + Image(systemName: "battery.0") + .font(iconFont) + .foregroundColor(.red) + .symbolRenderingMode(.hierarchical) + } else if batteryLevel > 100 { + Image(systemName: "powerplug") + .font(iconFont) + .foregroundColor(color) + .symbolRenderingMode(.hierarchical) + } + if batteryLevel > 100 { + Text("PWD") + .font(font) + } else if batteryLevel == 100 { + Text("CHG") + .font(font) + } else { + Text("\(batteryLevel)%") + .font(font) + } } } } } - -struct BatteryLevelCompact_Previews: PreviewProvider { - static var previews: some View { - VStack { - BatteryLevelCompact(batteryLevel: 111, font: .footnote, iconFont: .callout, color: Color.accentColor) - BatteryLevelCompact(batteryLevel: 100, font: .footnote, iconFont: .callout, color: Color.accentColor) - BatteryLevelCompact(batteryLevel: 99, font: .footnote, iconFont: .callout, color: Color.accentColor) - BatteryLevelCompact(batteryLevel: 74, font: .footnote, iconFont: .callout, color: Color.accentColor) - BatteryLevelCompact(batteryLevel: 49, font: .footnote, iconFont: .callout, color: Color.accentColor) - BatteryLevelCompact(batteryLevel: 14, font: .footnote, iconFont: .callout, color: Color.accentColor) - } - } -} diff --git a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift index a4b9f1e9..bf46fae7 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift @@ -26,7 +26,7 @@ struct NodeListItem: View { let deviceMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0")) if deviceMetrics?.count ?? 0 >= 1 { let mostRecent = deviceMetrics?.lastObject as? TelemetryEntity - BatteryLevelCompact(batteryLevel: mostRecent?.batteryLevel, font: .caption, iconFont: .callout, color: .accentColor) + BatteryLevelCompact(node: node, font: .caption, iconFont: .callout, color: .accentColor) } } VStack(alignment: .leading) { diff --git a/Meshtastic/Views/Nodes/Helpers/PositionPopover.swift b/Meshtastic/Views/Nodes/Helpers/PositionPopover.swift index 22d7269a..ffd3333e 100644 --- a/Meshtastic/Views/Nodes/Helpers/PositionPopover.swift +++ b/Meshtastic/Views/Nodes/Helpers/PositionPopover.swift @@ -14,115 +14,125 @@ struct PositionPopover: View { var body: some View { VStack (alignment: .leading) { HStack { - CircleText(text: position.nodePosition?.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(position.nodePosition?.user?.num ?? 0))), circleSize: 60) + CircleText(text: position.nodePosition?.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(position.nodePosition?.user?.num ?? 0))), circleSize: 75) + Spacer() Text(position.nodePosition?.user?.longName ?? "Unknown") .font(.largeTitle) - let degrees = Angle.degrees(Double(position.heading)) + Spacer() } Divider() - VStack (alignment: .leading) { - /// Time - Label { - LastHeardText(lastHeard: position.time) - .foregroundColor(.primary) - } icon: { - Image(systemName: position.nodePosition?.isOnline ?? false ? "checkmark.circle.fill" : "moon.circle.fill") - .symbolRenderingMode(.hierarchical) - .foregroundColor(position.nodePosition?.isOnline ?? false ? .green : .orange) - .frame(width: 35) - } - .padding(.bottom, 5) - /// Coordinate - Label { - Text("\(String(format: "%.6f", position.coordinate.latitude)), \(String(format: "%.6f", position.coordinate.longitude))") - .font(.footnote) - .textSelection(.enabled) - .foregroundColor(.primary) - } icon: { - Image(systemName: "mappin.and.ellipse") - .symbolRenderingMode(.hierarchical) - .frame(width: 35) - } - .padding(.bottom, 5) - /// Altitude - Label { - Text("Altitude: \(distanceFormatter.string(fromDistance: Double(position.altitude)))") - .foregroundColor(.primary) - } icon: { - Image(systemName: "mountain.2.fill") - .symbolRenderingMode(.hierarchical) - .frame(width: 35) - } - .padding(.bottom, 5) - let pf = PositionFlags(rawValue: Int(position.nodePosition?.metadata?.positionFlags ?? 3)) - /// Sats in view - if pf.contains(.Satsinview) { + HStack (alignment: .center) { + VStack (alignment: .leading) { + /// Time Label { - Text("Sats in view: \(String(position.satsInView))") + Text("heard".localized + ":") + LastHeardText(lastHeard: position.time) .foregroundColor(.primary) } icon: { - Image(systemName: "sparkles") + Image(systemName: position.nodePosition?.isOnline ?? false ? "checkmark.circle.fill" : "moon.circle.fill") + .symbolRenderingMode(.hierarchical) + .foregroundColor(position.nodePosition?.isOnline ?? false ? .green : .orange) + .frame(width: 35) + } + .padding(.bottom, 5) + /// Coordinate + Label { + Text("\(String(format: "%.6f", position.coordinate.latitude)), \(String(format: "%.6f", position.coordinate.longitude))") + .textSelection(.enabled) + .foregroundColor(.primary) + } icon: { + Image(systemName: "mappin.and.ellipse") .symbolRenderingMode(.hierarchical) .frame(width: 35) } .padding(.bottom, 5) - } - /// Sequence Number - if pf.contains(.SeqNo) { + /// Altitude Label { - Text("Sequence: \(String(position.seqNo))") + Text("Altitude: \(distanceFormatter.string(fromDistance: Double(position.altitude)))") .foregroundColor(.primary) } icon: { - Image(systemName: "number") + Image(systemName: "mountain.2.fill") .symbolRenderingMode(.hierarchical) .frame(width: 35) } .padding(.bottom, 5) - } - /// Heading - if pf.contains(.Heading) { - let degrees = Angle.degrees(Double(position.heading)) - Label { - let heading = Measurement(value: degrees.degrees, unit: UnitAngle.degrees) - Text("Heading: \(heading.formatted())") - .foregroundColor(.primary) - } icon: { - Image(systemName: "location.north") - .symbolRenderingMode(.hierarchical) - .frame(width: 35) - .rotationEffect(degrees) + let pf = PositionFlags(rawValue: Int(position.nodePosition?.metadata?.positionFlags ?? 3)) + /// Sats in view + if pf.contains(.Satsinview) { + Label { + Text("Sats in view: \(String(position.satsInView))") + .foregroundColor(.primary) + } icon: { + Image(systemName: "sparkles") + .symbolRenderingMode(.hierarchical) + .frame(width: 35) + } + .padding(.bottom, 5) } - .padding(.bottom, 5) - } - /// Speed - if pf.contains(.Speed) { - let formatter = MeasurementFormatter() - Label { - Text("Speed: \(formatter.string(from: Measurement(value: Double(position.speed), unit: UnitSpeed.kilometersPerHour)))") - // .font(.footnote) - .foregroundColor(.primary) - } icon: { - Image(systemName: "gauge.with.dots.needle.33percent") - .symbolRenderingMode(.hierarchical) - .frame(width: 35) + /// Sequence Number + if pf.contains(.SeqNo) { + Label { + Text("Sequence: \(String(position.seqNo))") + .foregroundColor(.primary) + } icon: { + Image(systemName: "number") + .symbolRenderingMode(.hierarchical) + .frame(width: 35) + } + .padding(.bottom, 5) + } + /// Heading + if pf.contains(.Heading) { + let degrees = Angle.degrees(Double(position.heading)) + Label { + let heading = Measurement(value: degrees.degrees, unit: UnitAngle.degrees) + Text("Heading: \(heading.formatted())") + .foregroundColor(.primary) + } icon: { + Image(systemName: "location.north") + .symbolRenderingMode(.hierarchical) + .frame(width: 35) + .rotationEffect(degrees) + } + .padding(.bottom, 5) + } + /// Speed + if pf.contains(.Speed) { + let formatter = MeasurementFormatter() + Label { + Text("Speed: \(formatter.string(from: Measurement(value: Double(position.speed), unit: UnitSpeed.kilometersPerHour)))") + // .font(.footnote) + .foregroundColor(.primary) + } icon: { + Image(systemName: "gauge.with.dots.needle.33percent") + .symbolRenderingMode(.hierarchical) + .frame(width: 35) + } + .padding(.bottom, 5) + } + /// Distance + if LocationHelper.currentLocation.distance(from: LocationHelper.DefaultLocation) > 0.0 { + let metersAway = position.coordinate.distance(from: LocationHelper.currentLocation) + Label { + Text("distance".localized + ": \(distanceFormatter.string(fromDistance: Double(metersAway)))") + // .font(.footnote) + .foregroundColor(.primary) + } icon: { + Image(systemName: "lines.measurement.horizontal") + .symbolRenderingMode(.hierarchical) + .frame(width: 35) + } } - .padding(.bottom, 5) } - /// Distance - if LocationHelper.currentLocation.distance(from: LocationHelper.DefaultLocation) > 0.0 { - let metersAway = position.coordinate.distance(from: LocationHelper.currentLocation) - Label { - Text("distance".localized + ": \(distanceFormatter.string(fromDistance: Double(metersAway)))") - // .font(.footnote) - .foregroundColor(.primary) - } icon: { - Image(systemName: "lines.measurement.horizontal") - .symbolRenderingMode(.hierarchical) - .frame(width: 35) + Spacer() + VStack (alignment: .trailing) { + if position.nodePosition != nil { + BatteryGauge(node: position.nodePosition!) } } } - } + .padding(.top) + } .presentationDetents([.fraction(0.4), .medium]) .presentationDragIndicator(.visible) }