Seperate apps settings and data clearing, update logic for online nodes text

This commit is contained in:
Garth Vander Houwen 2024-08-25 23:02:43 -07:00
parent 25f8e424ed
commit d3a7de2f76
9 changed files with 159 additions and 60 deletions

View file

@ -140,6 +140,16 @@
},
"%@%%" : {
},
"%@%% %@%%" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "%1$@%% %2$@%%"
}
}
}
},
"%@°F" : {
@ -6712,6 +6722,9 @@
},
"Drag & Drop is the recommended way to update firmware for NRF devices. If your iPhone or iPad is USB-C it will work with your regular USB-C charging cable, for lightning devices you need the Apple Lightning to USB camera adaptor." : {
},
"Dupe / Bad Packets: %d" : {
},
"echo" : {
"localizations" : {
@ -16072,6 +16085,12 @@
},
"Override automatic OLED screen detection." : {
},
"Packets Received: %d" : {
},
"Packets Sent: %d" : {
},
"password" : {
"localizations" : {
@ -17387,6 +17406,9 @@
},
"Requires that there be an accelerometer on your device." : {
},
"Reset App Settings" : {
},
"Reset NodeDB" : {
@ -22679,7 +22701,7 @@
"Your Firmware is up to date" : {
},
"Your MQTT Server must support TLS." : {
"Your MQTT Server must support TLS. Not available via the public mqtt server." : {
},
"Your position has been sent with a request for a response with their position. You will receive a notification when a position is returned." : {

View file

@ -22,9 +22,9 @@ extension NodeInfoEntity {
return self.telemetries?.filtered(using: NSPredicate(format: "metricsType == 1")).lastObject as? TelemetryEntity
}
var latestLocalStats: TelemetryEntity? {
return self.telemetries?.filtered(using: NSPredicate(format: "metricsType == 6")).lastObject as? TelemetryEntity
}
// var latestLocalStats: TelemetryEntity? {
// return self.telemetries?.filtered(using: NSPredicate(format: "metricsType == 6")).lastObject as? TelemetryEntity
// }
var hasPositions: Bool {
return positions?.count ?? 0 > 0

View file

@ -52,38 +52,80 @@ struct Connect: View {
if #available(iOS 17.0, macOS 14.0, *) {
TipView(BluetoothConnectionTip(), arrowEdge: .bottom)
}
HStack {
VStack(alignment: .center) {
CircleText(text: node?.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(node?.num ?? 0))), circleSize: 90)
}
.padding(.trailing)
VStack(alignment: .leading) {
if node != nil {
Text(connectedPeripheral.longName).font(.title2)
VStack(alignment: .leading) {
HStack {
VStack(alignment: .center) {
CircleText(text: node?.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(node?.num ?? 0))), circleSize: 90)
.padding(.trailing, 5)
if node?.latestDeviceMetrics != nil {
BatteryCompact(batteryLevel: node?.latestDeviceMetrics?.batteryLevel ?? 0, font: .caption, iconFont: .callout, color: .accentColor)
.padding(.trailing, 5)
}
}
Text("ble.name").font(.callout)+Text(": \(bleManager.connectedPeripheral?.peripheral.name ?? "unknown".localized)")
.font(.callout).foregroundColor(Color.gray)
if node != nil {
Text("firmware.version").font(.callout)+Text(": \(node?.metadata?.firmwareVersion ?? "unknown".localized)")
.padding(.trailing)
VStack(alignment: .leading) {
if node != nil {
Text(connectedPeripheral.longName).font(.title2)
}
Text("ble.name").font(.callout)+Text(": \(bleManager.connectedPeripheral?.peripheral.name ?? "unknown".localized)")
.font(.callout).foregroundColor(Color.gray)
}
if bleManager.isSubscribed {
Text("subscribed").font(.callout)
.foregroundColor(.green)
} else {
HStack {
if #available(iOS 17.0, macOS 14.0, *) {
Image(systemName: "square.stack.3d.down.forward")
.symbolRenderingMode(.multicolor)
.symbolEffect(.variableColor.reversing.cumulative, options: .repeat(20).speed(3))
if node != nil {
Text("firmware.version").font(.callout)+Text(": \(node?.metadata?.firmwareVersion ?? "unknown".localized)")
.font(.callout).foregroundColor(Color.gray)
}
if bleManager.isSubscribed {
Text("subscribed").font(.callout)
.foregroundColor(.green)
} else {
HStack {
if #available(iOS 17.0, macOS 14.0, *) {
Image(systemName: "square.stack.3d.down.forward")
.symbolRenderingMode(.multicolor)
.symbolEffect(.variableColor.reversing.cumulative, options: .repeat(20).speed(3))
.foregroundColor(.orange)
}
Text("communicating").font(.callout)
.foregroundColor(.orange)
}
Text("communicating").font(.callout)
.foregroundColor(.orange)
}
}
}
VStack {
let localStats = node?.telemetries?.filtered(using: NSPredicate(format: "metricsType == 6")).lastObject as? TelemetryEntity
if localStats != nil {
Divider()
if localStats?.numTotalNodes ?? 0 >= 100 {
Text("\(String(format: "Connected: %d nodes online", localStats?.numOnlineNodes ?? 0))")
.font(.callout)
.fontWeight(.medium)
.foregroundStyle(.secondary)
.fixedSize()
} else {
Text("\(String(format: "Connected: %d of %d nodes online", localStats?.numOnlineNodes ?? 0, localStats?.numTotalNodes ?? 0))")
.font(.callout)
.fontWeight(.medium)
.foregroundStyle(.secondary)
.fixedSize()
}
Text("\(String(format: "Ch. Util: %.2f", localStats?.channelUtilization ?? 0))% \(String(format: "Airtime: %.2f", localStats?.airUtilTx ?? 0))%")
.font(.caption)
.fontWeight(.medium)
.foregroundStyle(.secondary)
Text("Packets Sent: \(localStats?.numPacketsTx ?? 0)")
.font(.caption)
.fontWeight(.medium)
.foregroundStyle(.secondary)
Text("Packets Received: \(localStats?.numPacketsRx ?? 0)")
.font(.caption)
.fontWeight(.medium)
.foregroundStyle(.secondary)
Text("Dupe / Bad Packets: \(localStats?.numPacketsRxBad ?? 0)")
.font(.caption)
.fontWeight(.medium)
.foregroundStyle(.secondary)
.fixedSize()
}
}
}
.font(.caption)
.foregroundColor(Color.gray)

View file

@ -98,9 +98,6 @@ struct ChannelMessageList: View {
Text("\(ackErrorVal?.display ?? "Empty Ack Error")").fixedSize(horizontal: false, vertical: true)
.foregroundStyle(ackErrorVal?.color ?? Color.red)
.font(.caption2)
} else {
let messageDate = message.timestamp
Text(" \(messageDate.formattedDate(format: MessageText.dateFormatString))").font(.caption2).foregroundColor(.gray)
}
}
}

View file

@ -76,9 +76,11 @@ struct AppSettings: View {
}
clearCoreDataDatabase(context: context, includeRoutes: true)
context.refreshAllObjects()
UserDefaults.standard.reset()
}
}
Button("Reset App Settings", systemImage: "clipboard") {
UserDefaults.standard.reset()
}
}
if totalDownloadedTileSize != "0MB" {
Section(header: Text("Map Tile Data")) {

View file

@ -98,7 +98,12 @@ struct Channels: View {
preciseLocation = false
positionsEnabled = false
} else {
if positionPrecision == 32 {
if channelKey == "AQ==" {
preciseLocation = false
if positionPrecision < 10 || positionPrecision > 16 {
positionPrecision = 13
}
} else if positionPrecision == 32 {
preciseLocation = true
positionsEnabled = true
} else {
@ -250,7 +255,7 @@ struct Channels: View {
channelKey = key
positionsEnabled = false
preciseLocation = false
positionPrecision = 0
positionPrecision = 13
uplink = false
downlink = false
hasChanges = true

View file

@ -157,11 +157,20 @@ struct ChannelForm: View {
if !preciseLocation {
VStack(alignment: .leading) {
Label("Approximate Location", systemImage: "location.slash.circle.fill")
Slider(value: $positionPrecision, in: 10...19, step: 1) {
} minimumValueLabel: {
Image(systemName: "minus")
} maximumValueLabel: {
Image(systemName: "plus")
if channelKeySize == -1 {
Slider(value: $positionPrecision, in: 10...16, step: 1) {
} minimumValueLabel: {
Image(systemName: "minus")
} maximumValueLabel: {
Image(systemName: "plus")
}
} else {
Slider(value: $positionPrecision, in: 10...19, step: 1) {
} minimumValueLabel: {
Image(systemName: "minus")
} maximumValueLabel: {
Image(systemName: "plus")
}
}
Text(PositionPrecision(rawValue: Int(positionPrecision))?.description ?? "")
.foregroundColor(.gray)
@ -199,6 +208,11 @@ struct ChannelForm: View {
.onChange(of: channelKey) { _ in
hasChanges = true
}
.onChange(of: channelKeySize) { _ in
if channelKeySize == -1 {
preciseLocation = false
}
}
.onChange(of: channelRole) { _ in
hasChanges = true
}

View file

@ -24,7 +24,7 @@ struct MQTTConfig: View {
@State var password = ""
@State var encryptionEnabled = true
@State var jsonEnabled = false
@State var tlsEnabled = true
@State var tlsEnabled = false
@State var root = "msh"
@State var selectedTopic = ""
@State var mqttConnected: Bool = false
@ -234,7 +234,7 @@ struct MQTTConfig: View {
.listRowSeparator(/*@START_MENU_TOKEN@*/.visible/*@END_MENU_TOKEN@*/)
Toggle(isOn: $tlsEnabled) {
Label("TLS Enabled", systemImage: "checkmark.shield.fill")
Text("Your MQTT Server must support TLS.")
Text("Your MQTT Server must support TLS. Not available via the public mqtt server.")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
}
@ -288,9 +288,6 @@ struct MQTTConfig: View {
jsonEnabled = false
}
if newProxyToClientEnabled != node?.mqttConfig?.proxyToClientEnabled { hasChanges = true }
if newProxyToClientEnabled {
jsonEnabled = false
}
}
.onChange(of: address) { newAddress in
if node != nil && node?.mqttConfig != nil {
@ -324,8 +321,12 @@ struct MQTTConfig: View {
}
if newJsonEnabled != node?.mqttConfig?.jsonEnabled { hasChanges = true }
}
.onChange(of: tlsEnabled) {
if $0 != node?.mqttConfig?.tlsEnabled { hasChanges = true }
.onChange(of: tlsEnabled) { newTlsEnabled in
if address.lowercased() == "mqtt.meshtastic.org" {
tlsEnabled = false
} else {
if newTlsEnabled != node?.mqttConfig?.tlsEnabled { hasChanges = true }
}
}
.onChange(of: mqttConnected) { newMqttConnected in
if newMqttConnected == false {

View file

@ -23,7 +23,7 @@ struct WidgetsLiveActivity: Widget {
nodesOnline: context.state.nodesOnline,
totalNodes: context.state.totalNodes,
timerRange: context.state.timerRange)
.widgetURL(URL(string: "meshtastic:///node/\(context.attributes.name)"))
.widgetURL(URL(string: "meshtastic:///bluetooth"))
} dynamicIsland: { context in
DynamicIsland {
@ -38,10 +38,17 @@ struct WidgetsLiveActivity: Widget {
.fixedSize()
Spacer()
}
Text("\(context.state.nodesOnline) nodes online")
.font(.caption)
.foregroundStyle(.secondary)
.fixedSize()
if context.state.nodesOnline >= 100 {
Text("100+ online")
.font(.caption)
.foregroundStyle(.secondary)
.fixedSize()
} else {
Text("\(context.state.nodesOnline) of \(context.state.totalNodes) online")
.font(.caption)
.foregroundStyle(.secondary)
.fixedSize()
}
Text("\(String(format: "Ch. Util: %.2f", context.state.channelUtilization))%")
.font(.caption)
.foregroundStyle(.secondary)
@ -74,7 +81,7 @@ struct WidgetsLiveActivity: Widget {
.font(.caption)
.foregroundStyle(.secondary)
.fixedSize()
Text("Dupe / Bad: \(context.state.badReceivedPackets)")
Text("Dupe / Bad \(context.state.badReceivedPackets)")
.font(.caption)
.foregroundStyle(.secondary)
.fixedSize()
@ -108,7 +115,7 @@ struct WidgetsLiveActivity: Widget {
.contentMargins(.trailing, 32, for: .expanded)
.contentMargins([.leading, .top, .bottom], 6, for: .compactLeading)
.contentMargins(.all, 6, for: .minimal)
.widgetURL(URL(string: "meshtastic:///node/\(context.attributes.name)"))
.widgetURL(URL(string: "meshtastic:///bluetooth"))
}
}
}
@ -214,12 +221,21 @@ struct NodeInfoView: View {
.foregroundStyle(.secondary)
.opacity(isLuminanceReduced ? 0.8 : 1.0)
.fixedSize()
Text("\(String(format: "Connected: %d nodes online", nodesOnline, totalNodes))")
.font(.caption)
.fontWeight(.medium)
.foregroundStyle(.secondary)
.opacity(isLuminanceReduced ? 0.8 : 1.0)
.fixedSize()
if totalNodes >= 100 {
Text("\(String(format: "Connected: %d nodes online", nodesOnline))")
.font(.caption)
.fontWeight(.medium)
.foregroundStyle(.secondary)
.opacity(isLuminanceReduced ? 0.8 : 1.0)
.fixedSize()
} else {
Text("\(String(format: "Connected: %d of %d nodes online", nodesOnline, totalNodes))")
.font(.caption)
.fontWeight(.medium)
.foregroundStyle(.secondary)
.opacity(isLuminanceReduced ? 0.8 : 1.0)
.fixedSize()
}
let now = Date()
Text("Last Heard: \(now.formatted())")
.font(.caption)