Skip to content

Commit 1f5b86a

Browse files
committed
Preview enabled in connected devices
1 parent 4784b44 commit 1f5b86a

File tree

2 files changed

+113
-93
lines changed

2 files changed

+113
-93
lines changed

Meshtastic/Views/Helpers/ConnectedDevice.swift

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ A view draws the indicator used in the upper right corner for views using BLE
66
import SwiftUI
77

88
struct ConnectedDevice: View {
9-
@EnvironmentObject var accessoryManager: AccessoryManager
109
var deviceConnected: Bool
1110
var name: String
1211
var mqttProxyConnected: Bool = false
@@ -15,7 +14,7 @@ struct ConnectedDevice: View {
1514
var mqttTopic: String = ""
1615
var phoneOnly: Bool = false
1716
var showActivityLights: Bool
18-
17+
1918
init(deviceConnected: Bool, name: String, mqttProxyConnected: Bool = false, mqttUplinkEnabled: Bool = false, mqttDownlinkEnabled: Bool = false, mqttTopic: String = "", phoneOnly: Bool = false, showActivityLights: Bool = true) {
2019
self.deviceConnected = deviceConnected
2120
self.name = name
@@ -70,18 +69,31 @@ struct ConnectedDevice: View {
7069
}
7170
}
7271

73-
struct ConnectedDevice_Previews: PreviewProvider {
74-
static var previews: some View {
75-
VStack(alignment: .trailing) {
76-
ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true)
77-
ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: false, mqttUplinkEnabled: true, mqttDownlinkEnabled: true)
78-
ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true, mqttUplinkEnabled: true, mqttDownlinkEnabled: true, mqttTopic: "msh/US/2/e/#")
79-
ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: false, mqttUplinkEnabled: true, mqttDownlinkEnabled: false)
80-
ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true, mqttUplinkEnabled: true, mqttDownlinkEnabled: false)
81-
ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: false, mqttUplinkEnabled: false, mqttDownlinkEnabled: true)
82-
ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true, mqttUplinkEnabled: false, mqttDownlinkEnabled: true)
83-
ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true)
84-
ConnectedDevice(deviceConnected: false, name: "MEMO", mqttProxyConnected: false)
85-
}.previewLayout(.fixed(width: 150, height: 275))
86-
}
72+
#Preview("Multiple variants") {
73+
VStack(alignment: .trailing) {
74+
ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true)
75+
ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: false, mqttUplinkEnabled: true, mqttDownlinkEnabled: true)
76+
ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true, mqttUplinkEnabled: true, mqttDownlinkEnabled: true, mqttTopic: "msh/US/2/e/#")
77+
ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: false, mqttUplinkEnabled: true, mqttDownlinkEnabled: false)
78+
ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true, mqttUplinkEnabled: true, mqttDownlinkEnabled: false)
79+
ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: false, mqttUplinkEnabled: false, mqttDownlinkEnabled: true)
80+
ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true, mqttUplinkEnabled: false, mqttDownlinkEnabled: true)
81+
ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: true)
82+
ConnectedDevice(deviceConnected: false, name: "MEMO", mqttProxyConnected: false)
83+
}
84+
.environmentObject(AccessoryManager.shared)
85+
}
86+
87+
#Preview("Navigation header item") {
88+
NavigationView {
89+
Text("Connect screen")
90+
.navigationTitle("Connect")
91+
.navigationBarItems(
92+
leading: MeshtasticLogo(),
93+
trailing: ZStack {
94+
ConnectedDevice(deviceConnected: true, name: "MEMO", mqttProxyConnected: false)
95+
.environmentObject(AccessoryManager.shared)
96+
}
97+
)
98+
}
8799
}

Meshtastic/Views/Helpers/RXTXIndicatorView.swift

Lines changed: 85 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -12,99 +12,107 @@ import OSLog
1212
struct RXTXIndicatorWidget: View {
1313
@EnvironmentObject var accessoryManager: AccessoryManager
1414
@State private var isPopoverOpen = false
15-
15+
1616
let fontSize: CGFloat = 7.0
1717
var body: some View {
18-
Button( action: {
19-
if !isPopoverOpen && accessoryManager.isConnected {
20-
Task {
21-
// TODO: replace with a heartbeat when the heartbeat works
22-
try await Task.sleep(for: .seconds(0.5)) // little delay for user affordance
23-
if accessoryManager.checkIsVersionSupported(forVersion: "2.7.4") {
24-
Logger.transport.debug("[RXTXIndicator] sending heartbeat (2.7.4+)")
25-
try await accessoryManager.sendHeartbeat()
26-
} else {
27-
Logger.transport.debug("[RXTXIndicator] sending metadata request (pre 2.7.4 does not support heartbeat nonce)")
28-
_ = try await accessoryManager.requestDeviceMetadata()
29-
}
18+
Button(
19+
action: togglePopover,
20+
label: {
21+
VStack(spacing: 3.0) {
22+
HStack(spacing: 2.0) {
23+
Image(systemName: "arrow.up")
24+
.font(.system(size: fontSize))
25+
LEDIndicator(flash: $accessoryManager.packetsSent, color: .green)
26+
}.frame(maxHeight: fontSize)
27+
HStack(spacing: 2.0) {
28+
Image(systemName: "arrow.down")
29+
.font(.system(size: fontSize))
30+
LEDIndicator(flash: $accessoryManager.packetsReceived, color: .red)
31+
}.frame(maxHeight: fontSize)
3032
}
31-
}
32-
#if targetEnvironment(macCatalyst)
33-
if #available(macOS 26.0, *) {
34-
// Don't show popover that crashes on mac 26
35-
} else {
36-
self.isPopoverOpen.toggle()
33+
.contentShape(Rectangle()) // Make sure the whole thing is tappable
34+
.popover(isPresented: self.$isPopoverOpen,
35+
attachmentAnchor: .rect(.bounds),
36+
arrowEdge: .top) {
37+
popoverEnabledView()
3738
}
38-
#else
39-
self.isPopoverOpen.toggle()
40-
#endif
41-
42-
}) {
43-
VStack(spacing: 3.0) {
44-
HStack(spacing: 2.0) {
45-
Image(systemName: "arrow.up")
46-
.font(.system(size: fontSize))
47-
LEDIndicator(flash: $accessoryManager.packetsSent, color: .green)
48-
}.frame(maxHeight: fontSize)
49-
HStack(spacing: 2.0) {
50-
Image(systemName: "arrow.down")
51-
.font(.system(size: fontSize))
52-
LEDIndicator(flash: $accessoryManager.packetsReceived, color: .red)
53-
}.frame(maxHeight: fontSize)
5439
}
55-
.contentShape(Rectangle()) // Make sure the whole thing is tappable
56-
.popover(isPresented: self.$isPopoverOpen,
57-
attachmentAnchor: .rect(.bounds),
58-
arrowEdge: .top) {
59-
Button(action: {
60-
self.isPopoverOpen = false
61-
}) {
62-
VStack(spacing: 0.5) {
63-
Text("Packet Count")
64-
.font(.caption)
65-
.bold()
66-
.padding(2.0)
67-
Divider()
68-
VStack(alignment: .leading) {
69-
HStack(spacing: 3.0) {
70-
HStack(spacing: 2.0) {
71-
LEDIndicator(flash: $accessoryManager.packetsSent, color: .green)
72-
.frame(maxHeight: fontSize)
73-
Image(systemName: "arrow.up")
74-
.font(.system(size: fontSize))
75-
}
76-
Text("To Radio (TX): \(accessoryManager.packetsSent)")
77-
.font(.caption2)
78-
Spacer()
40+
).buttonStyle(.borderless)
41+
}
42+
43+
@ViewBuilder
44+
func popoverEnabledView() -> some View {
45+
Button(
46+
action: { self.isPopoverOpen = false },
47+
label: {
48+
VStack(spacing: 0.5) {
49+
Text("Packet Count")
50+
.font(.caption)
51+
.bold()
52+
.padding(2.0)
53+
Divider()
54+
VStack(alignment: .leading) {
55+
HStack(spacing: 3.0) {
56+
HStack(spacing: 2.0) {
57+
LEDIndicator(flash: $accessoryManager.packetsSent, color: .green)
58+
.frame(maxHeight: fontSize)
59+
Image(systemName: "arrow.up")
60+
.font(.system(size: fontSize))
7961
}
80-
HStack(spacing: 3.0) {
81-
HStack(spacing: 2.0) {
82-
LEDIndicator(flash: $accessoryManager.packetsReceived, color: .red)
83-
.frame(maxHeight: fontSize)
84-
Image(systemName: "arrow.down")
85-
.font(.system(size: fontSize))
86-
}
87-
Text("From Radio (RX): \(accessoryManager.packetsReceived)")
88-
.font(.caption2)
89-
Spacer()
62+
Text("To Radio (TX): \(accessoryManager.packetsSent)")
63+
.font(.caption2)
64+
Spacer()
65+
}
66+
HStack(spacing: 3.0) {
67+
HStack(spacing: 2.0) {
68+
LEDIndicator(flash: $accessoryManager.packetsReceived, color: .red)
69+
.frame(maxHeight: fontSize)
70+
Image(systemName: "arrow.down")
71+
.font(.system(size: fontSize))
9072
}
91-
}.padding(2.0)
92-
}.padding(10)
93-
.contentShape(Rectangle()) // Make sure the whole thing is tappable
94-
}.buttonStyle(.plain)
95-
.presentationCompactAdaptation(.popover)
73+
Text("From Radio (RX): \(accessoryManager.packetsReceived)")
74+
.font(.caption2)
75+
Spacer()
76+
}
77+
}.padding(2.0)
78+
}.padding(10)
79+
.contentShape(Rectangle()) // Make sure the whole thing is tappable
9680
}
97-
}.buttonStyle(.borderless)
81+
).buttonStyle(.plain)
82+
.presentationCompactAdaptation(.popover)
83+
}
84+
85+
func togglePopover() {
86+
if !isPopoverOpen && accessoryManager.isConnected {
87+
Task {
88+
// TODO: replace with a heartbeat when the heartbeat works
89+
try await Task.sleep(for: .seconds(0.5)) // little delay for user affordance
90+
if accessoryManager.checkIsVersionSupported(forVersion: "2.7.4") {
91+
Logger.transport.debug("[RXTXIndicator] sending heartbeat (2.7.4+)")
92+
try await accessoryManager.sendHeartbeat()
93+
} else {
94+
Logger.transport.debug("[RXTXIndicator] sending metadata request (pre 2.7.4 does not support heartbeat nonce)")
95+
_ = try await accessoryManager.requestDeviceMetadata()
96+
}
97+
}
98+
}
99+
#if targetEnvironment(macCatalyst)
100+
if #unavailable(macOS 26.0) {
101+
self.isPopoverOpen.toggle()
102+
}
103+
#else
104+
self.isPopoverOpen.toggle()
105+
#endif
98106
}
99107
}
100108

101109
struct LEDIndicator: View {
102110
@Environment(\.colorScheme) var colorScheme
103111
@Binding var flash: Int
104112
let color: Color
105-
113+
106114
@State private var brightness: Double = 0.0
107-
115+
108116
var body: some View {
109117
Circle()
110118
.foregroundColor(color.opacity(brightness))

0 commit comments

Comments
 (0)