Meshtastic-Apple/Meshtastic/Views/Helpers/RXTXIndicatorView.swift
Garth Vander Houwen f5ad2454bb
2.7.1 Working Changes 2 (#1393)
* Bump version

* Offset for map controls on the mesh map

* Only mark messages as read if they are unread (#1388)

* Only mark messages as read if they are unread

* More cheap optimizations

* Fix map control positions on the route recorder

* Add seperate state variable for delete all channel messges button since the channelSelection is being used for navigation

* Use a seperate state variable to track what user messages are being deleted for as userSelection is being used for navigation

* Get the ringtone if external notifications is enabled

* Fix RTTTL typo

* Dont show modem lights popover if we are on macOS 26 cause it crashes

* Fix annoying connect bottom background bug

* Update mesh map detents

* Move divider inside of the hstack keyboard toolbar

* Update Meshtastic/Helpers/MeshPackets.swift

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Remove extra environment variable that is not getting used

* Fix ack time for 24hour locales, only hide TLS setting for the public MQTT broker

* Icon Composer Icon (#1374)

* Icon Composer Icon

* Tweaks to icon

---------

Co-authored-by: Jake-B <jake-b@users.noreply.github.com>

* Move if statement out of if statement

* Update Meshtastic/Views/Helpers/RXTXIndicatorView.swift

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: jake-b <1012393+jake-b@users.noreply.github.com>
Co-authored-by: Jake-B <jake-b@users.noreply.github.com>
2025-09-12 23:03:38 -07:00

121 lines
3.4 KiB
Swift

//
// RXTXIndicatorView.swift
// Meshtastic
//
// Created by jake on 8/5/25.
//
import Foundation
import SwiftUI
import OSLog
struct RXTXIndicatorWidget: View {
@EnvironmentObject var accessoryManager: AccessoryManager
@State private var isPopoverOpen = false
let fontSize: CGFloat = 7.0
var body: some View {
Button( action: {
if !isPopoverOpen && accessoryManager.isConnected {
Task {
// TODO: replace with a heartbeat when the heartbeat works
try await Task.sleep(for: .seconds(0.5)) // little delay for user affordance
if accessoryManager.checkIsVersionSupported(forVersion: "2.7.4") {
Logger.transport.debug("[RXTXIndicator] sending heartbeat (2.7.4+)")
try await accessoryManager.sendHeartbeat()
} else {
Logger.transport.debug("[RXTXIndicator] sending metadata request (pre 2.7.4 does not support heartbeat nonce)")
_ = try await accessoryManager.requestDeviceMetadata()
}
}
}
#if targetEnvironment(macCatalyst)
if #available(macOS 26.0, *) {
// Don't show popover that crashes on mac 26
} else {
self.isPopoverOpen.toggle()
}
#else
self.isPopoverOpen.toggle()
#endif
}) {
VStack(spacing: 3.0) {
HStack(spacing: 2.0) {
Image(systemName: "arrow.up")
.font(.system(size: fontSize))
LEDIndicator(flash: $accessoryManager.packetsSent, color: .green)
}.frame(maxHeight: fontSize)
HStack(spacing: 2.0) {
Image(systemName: "arrow.down")
.font(.system(size: fontSize))
LEDIndicator(flash: $accessoryManager.packetsReceived, color: .red)
}.frame(maxHeight: fontSize)
}
.contentShape(Rectangle()) // Make sure the whole thing is tappable
.popover(isPresented: self.$isPopoverOpen,
attachmentAnchor: .rect(.bounds),
arrowEdge: .top) {
Button(action: {
self.isPopoverOpen = false
}) {
VStack(spacing: 0.5) {
Text("Packet Count")
.font(.caption)
.bold()
.padding(2.0)
Divider()
VStack(alignment: .leading) {
HStack(spacing: 3.0) {
HStack(spacing: 2.0) {
LEDIndicator(flash: $accessoryManager.packetsSent, color: .green)
.frame(maxHeight: fontSize)
Image(systemName: "arrow.up")
.font(.system(size: fontSize))
}
Text("To Radio (TX): \(accessoryManager.packetsSent)")
.font(.caption2)
Spacer()
}
HStack(spacing: 3.0) {
HStack(spacing: 2.0) {
LEDIndicator(flash: $accessoryManager.packetsReceived, color: .red)
.frame(maxHeight: fontSize)
Image(systemName: "arrow.down")
.font(.system(size: fontSize))
}
Text("From Radio (RX): \(accessoryManager.packetsReceived)")
.font(.caption2)
Spacer()
}
}.padding(2.0)
}.padding(10)
.contentShape(Rectangle()) // Make sure the whole thing is tappable
}.buttonStyle(.plain)
.presentationCompactAdaptation(.popover)
}
}.buttonStyle(.borderless)
}
}
struct LEDIndicator: View {
@Environment(\.colorScheme) var colorScheme
@Binding var flash: Int
let color: Color
@State private var brightness: Double = 0.0
var body: some View {
Circle()
.foregroundColor(color.opacity(brightness))
.overlay(
Circle()
.stroke(colorScheme == .light ? Color.black : Color.white, lineWidth: 0.5)
).onChange(of: flash) { _, _ in
brightness = 1.0
withAnimation(.easeOut(duration: 0.3)) {
brightness = 0.0
}
}
}
}