Three column working point

This commit is contained in:
Garth Vander Houwen 2023-09-08 18:52:59 -07:00
parent 40ab2b1954
commit 9fccb74f43
10 changed files with 245 additions and 30 deletions

View file

@ -116,7 +116,6 @@
DDB8F4102A9EE5B400230ECE /* Messages.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB8F40F2A9EE5B400230ECE /* Messages.swift */; };
DDB8F4122A9EE5DD00230ECE /* UserList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB8F4112A9EE5DD00230ECE /* UserList.swift */; };
DDB8F4142A9EE5F000230ECE /* ChannelList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB8F4132A9EE5F000230ECE /* ChannelList.swift */; };
DDC18AD12AAAE5920083FE1E /* NodeListDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC18AD02AAAE5920083FE1E /* NodeListDetail.swift */; };
DDC2E15826CE248E0042C5E4 /* MeshtasticApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */; };
DDC2E15C26CE248F0042C5E4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */; };
DDC2E15F26CE248F0042C5E4 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15E26CE248F0042C5E4 /* Preview Assets.xcassets */; };
@ -136,6 +135,9 @@
DDD6EEAF29BC024700383354 /* Firmware.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD6EEAE29BC024700383354 /* Firmware.swift */; };
DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */; };
DDD9E4E4284B208E003777C5 /* UserEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */; };
DDDB263F2AABEE20003AFCB7 /* NodeListSplit.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDB263E2AABEE20003AFCB7 /* NodeListSplit.swift */; };
DDDB26422AABF655003AFCB7 /* NodeListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDB26412AABF655003AFCB7 /* NodeListItem.swift */; };
DDDB26442AAC0206003AFCB7 /* NodeDetailItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDB26432AAC0206003AFCB7 /* NodeDetailItem.swift */; };
DDDB443629F6287000EE2349 /* MapButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDB443529F6287000EE2349 /* MapButtons.swift */; };
DDDB443D29F6592F00EE2349 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDB443C29F6592F00EE2349 /* NetworkManager.swift */; };
DDDB444029F79AB000EE2349 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDB443F29F79AB000EE2349 /* UserDefaults.swift */; };
@ -324,7 +326,6 @@
DDB8F4112A9EE5DD00230ECE /* UserList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserList.swift; sourceTree = "<group>"; };
DDB8F4132A9EE5F000230ECE /* ChannelList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelList.swift; sourceTree = "<group>"; };
DDBA45EC299ED78100DEEDDC /* MeshtasticDataModelV8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV8.xcdatamodel; sourceTree = "<group>"; };
DDC18AD02AAAE5920083FE1E /* NodeListDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeListDetail.swift; sourceTree = "<group>"; };
DDC2E15426CE248E0042C5E4 /* Meshtastic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Meshtastic.app; sourceTree = BUILT_PRODUCTS_DIR; };
DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticApp.swift; sourceTree = "<group>"; };
DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = ../Assets.xcassets; sourceTree = "<group>"; };
@ -353,6 +354,9 @@
DDD6EEAE29BC024700383354 /* Firmware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Firmware.swift; sourceTree = "<group>"; };
DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimeText.swift; sourceTree = "<group>"; };
DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserEntityExtension.swift; sourceTree = "<group>"; };
DDDB263E2AABEE20003AFCB7 /* NodeListSplit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeListSplit.swift; sourceTree = "<group>"; };
DDDB26412AABF655003AFCB7 /* NodeListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeListItem.swift; sourceTree = "<group>"; };
DDDB26432AAC0206003AFCB7 /* NodeDetailItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeDetailItem.swift; sourceTree = "<group>"; };
DDDB443529F6287000EE2349 /* MapButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapButtons.swift; sourceTree = "<group>"; };
DDDB443C29F6592F00EE2349 /* NetworkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = "<group>"; };
DDDB443F29F79AB000EE2349 /* UserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaults.swift; sourceTree = "<group>"; };
@ -460,6 +464,7 @@
DD47E3CA26F0E50300029299 /* Nodes */ = {
isa = PBXGroup;
children = (
DDDB26402AABEF7B003AFCB7 /* Helpers */,
DD769E0228D18BF0001A3F05 /* DeviceMetricsLog.swift */,
DD4F23CC28779A3C001D37CB /* EnvironmentMetricsLog.swift */,
DD2E65252767A01F00E45FC5 /* NodeDetail.swift */,
@ -468,6 +473,7 @@
DD73FD1028750779000852D6 /* PositionLog.swift */,
DD14E72D2A82A614006E39BC /* RemoteHardware.swift */,
6DEDA5592A957B8E00321D2E /* DetectionSensorLog.swift */,
DDDB263E2AABEE20003AFCB7 /* NodeListSplit.swift */,
);
path = Nodes;
sourceTree = "<group>";
@ -656,6 +662,7 @@
DDDB443E29F79A9400EE2349 /* Extensions */,
DDC2E1A526CEB32B0042C5E4 /* Helpers */,
DDC2E18826CE24EE0042C5E4 /* Model */,
DDDB263D2AABD34F003AFCB7 /* Navigation */,
DDC4D5662754996200A4208E /* Persistence */,
DDAF8C5626ED07740058C060 /* Protobufs */,
DDC2E18926CE24F70042C5E4 /* Resources */,
@ -792,6 +799,22 @@
path = Mqtt;
sourceTree = "<group>";
};
DDDB263D2AABD34F003AFCB7 /* Navigation */ = {
isa = PBXGroup;
children = (
);
path = Navigation;
sourceTree = "<group>";
};
DDDB26402AABEF7B003AFCB7 /* Helpers */ = {
isa = PBXGroup;
children = (
DDDB26412AABF655003AFCB7 /* NodeListItem.swift */,
DDDB26432AAC0206003AFCB7 /* NodeDetailItem.swift */,
);
path = Helpers;
sourceTree = "<group>";
};
DDDB443E29F79A9400EE2349 /* Extensions */ = {
isa = PBXGroup;
children = (
@ -833,7 +856,6 @@
isa = PBXGroup;
children = (
DDDEE5E029DA3E1100A8E078 /* NodeInfoView.swift */,
DDC18AD02AAAE5920083FE1E /* NodeListDetail.swift */,
);
path = Node;
sourceTree = "<group>";
@ -1075,6 +1097,7 @@
DD5E5213298EE33B00D21B61 /* deviceonly.pb.swift in Sources */,
DD5E5208298EE33B00D21B61 /* rtttl.pb.swift in Sources */,
DD6193792863875F00E59241 /* SerialConfig.swift in Sources */,
DDDB263F2AABEE20003AFCB7 /* NodeListSplit.swift in Sources */,
DDA0B6B2294CDC55001356EC /* Channels.swift in Sources */,
DDB8F4102A9EE5B400230ECE /* Messages.swift in Sources */,
DD4A911E2708C65400501B7E /* AppSettings.swift in Sources */,
@ -1085,7 +1108,6 @@
DD5E520F298EE33B00D21B61 /* cannedmessages.pb.swift in Sources */,
DDB75A232A13CDA9006ED576 /* BatteryLevelCompact.swift in Sources */,
DDB75A162A0594AD006ED576 /* TileOverlay.swift in Sources */,
DDC18AD12AAAE5920083FE1E /* NodeListDetail.swift in Sources */,
DDF924CA26FBB953009FE055 /* ConnectedDevice.swift in Sources */,
DD3CC6BE28E4CD9800FA9159 /* BatteryGauge.swift in Sources */,
DD6193772862F90F00E59241 /* CannedMessagesConfig.swift in Sources */,
@ -1101,6 +1123,7 @@
DDB8F4142A9EE5F000230ECE /* ChannelList.swift in Sources */,
DDD43FE32A78C8900083A3E9 /* MqttClientProxyManager.swift in Sources */,
DD007BB02AA5981000F5FA12 /* NodeInfoEntityExtension.swift in Sources */,
DDDB26422AABF655003AFCB7 /* NodeListItem.swift in Sources */,
DDDB444629F8A96500EE2349 /* Character.swift in Sources */,
DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */,
DDB6ABDB28B0AC6000384BA1 /* DistanceText.swift in Sources */,
@ -1163,6 +1186,7 @@
DD0F791B28713C8A00A6FDAD /* AdminMessageList.swift in Sources */,
DD3CC6BC28E366DF00FA9159 /* Meshtastic.xcdatamodeld in Sources */,
DDC4C9FF2A8D982900CE201C /* DetectionSensorConfig.swift in Sources */,
DDDB26442AAC0206003AFCB7 /* NodeDetailItem.swift in Sources */,
DD5E5210298EE33B00D21B61 /* telemetry.pb.swift in Sources */,
DD77093F2AA1B146007A8BF0 /* UIColor.swift in Sources */,
DD5E5205298EE33B00D21B61 /* mesh.pb.swift in Sources */,

View file

@ -41,7 +41,6 @@ extension UserDefaults {
UserDefaults.standard.set(newValue, forKey: "meshtasticUsername")
}
}
static var preferredPeripheralId: String {
get {
UserDefaults.standard.string(forKey: "preferredPeripheralId") ?? ""

View file

@ -24,6 +24,11 @@ struct ContentView: View {
Label("nodes", systemImage: "flipphone")
}
.tag(Tab.nodes)
NodeListSplit()
.tabItem {
Label("nodes", systemImage: "flipphone")
}
.tag(Tab.nodes2)
NodeMap()
.tabItem {
Label("map", systemImage: "map")
@ -51,5 +56,6 @@ enum Tab {
case map
case ble
case nodes
case nodes2
case settings
}

View file

@ -1,19 +0,0 @@
//
// NodeListDetail.swift
// Meshtastic
//
// Copyright(c) Garth Vander Houwen 9/7/23.
//
//import SwiftUI
//import CoreLocation
//import MapKit
//
//struct NodeListDetail: View {
//
// var node: NodeInfoEntity
//
// var body: some View {
//
// }
//}

View file

@ -24,8 +24,8 @@ struct ChannelMessageList: View {
var maxbytes = 228
@FocusState var focusedField: Field?
@ObservedObject var myInfo: MyInfoEntity
@ObservedObject var channel: ChannelEntity
@StateObject var myInfo: MyInfoEntity
@StateObject var channel: ChannelEntity
@State var showDeleteMessageAlert = false
@State private var deleteMessageId: Int64 = 0
@State private var replyMessageId: Int64 = 0
@ -233,7 +233,7 @@ struct ChannelMessageList: View {
message.read = true
do {
try context.save()
print("Read message \(message.messageId) ")
print("📖 Read message \(message.messageId) ")
appState.unreadChannelMessages = myInfo.unreadMessages
UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages
context.refresh(myInfo, mergeChanges: true)

View file

@ -222,7 +222,7 @@ struct UserMessageList: View {
message.read = true
do {
try context.save()
print("Read message \(message.messageId) ")
print("📖 Read message \(message.messageId) ")
appState.unreadDirectMessages = user.unreadMessages
UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages

View file

@ -0,0 +1,30 @@
//
// NodeDetailItem.swift
// Meshtastic
//
// Created by Garth Vander Houwen on 9/8/23.
//
import SwiftUI
import WeatherKit
import MapKit
import CoreLocation
struct NodeDetailItem: View {
var node: NodeInfoEntity
@FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)],
predicate: NSPredicate(
format: "expire == nil || expire >= %@", Date() as NSDate
), animation: .none)
private var waypoints: FetchedResults<WaypointEntity>
var body: some View {
NavigationStack {
}
}
}

View file

@ -0,0 +1,89 @@
//
// NodeListItem.swift
// Meshtastic
//
// Created by Garth Vander Houwen on 9/8/23.
//
import SwiftUI
struct NodeListItem: View {
@StateObject var node: NodeInfoEntity
var body: some View {
NavigationLink(value: node) {
let connected: Bool = false //(bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral?.num ?? -1 == node.num)
LazyVStack(alignment: .leading) {
HStack {
VStack(alignment: .leading) {
CircleText(text: node.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65)
.padding(.trailing, 5)
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: .caption2, iconFont: .callout, color: .accentColor)
}
}
VStack(alignment: .leading) {
Text(node.user?.longName ?? "unknown".localized)
.fontWeight(.medium)
.font(.callout)
if connected {
HStack(alignment: .bottom) {
Image(systemName: "antenna.radiowaves.left.and.right.circle.fill")
.font(.footnote)
.symbolRenderingMode(.hierarchical)
.foregroundColor(.green)
Text("connected").font(.caption)
}
}
HStack(alignment: .bottom) {
Image(systemName: node.isOnline ? "checkmark.circle.fill" : "moon.circle.fill")
.font(.footnote)
.symbolRenderingMode(.hierarchical)
.foregroundColor(node.isOnline ? .green : .orange)
LastHeardText(lastHeard: node.lastHeard)
.font(.caption)
}
// if node.positions?.count ?? 0 > 0 && (bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral?.num ?? -1 != node.num) {
// HStack(alignment: .bottom) {
// let lastPostion = node.positions!.reversed()[0] as! PositionEntity
// let myCoord = CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude)
// if lastPostion.nodeCoordinate != nil && myCoord.coordinate.longitude != LocationHelper.DefaultLocation.longitude && myCoord.coordinate.latitude != LocationHelper.DefaultLocation.latitude {
// let nodeCoord = CLLocation(latitude: lastPostion.nodeCoordinate!.latitude, longitude: lastPostion.nodeCoordinate!.longitude)
// let metersAway = nodeCoord.distance(from: myCoord)
// Image(systemName: "lines.measurement.horizontal")
// .font(.footnote)
// .symbolRenderingMode(.hierarchical)
// DistanceText(meters: metersAway).font(.caption)
// }
// }
// }
if node.channel > 0 {
HStack(alignment: .bottom) {
Image(systemName: "fibrechannel")
.font(.footnote)
.symbolRenderingMode(.hierarchical)
Text("Channel: \(node.channel)")
.font(.footnote)
}
}
// if !connected {
// HStack(alignment: .bottom) { let preset = ModemPresets(rawValue: Int(connectedNode?.loRaConfig?.modemPreset ?? 0))
// LoRaSignalStrengthMeter(snr: node.snr, rssi: node.rssi, preset: preset ?? ModemPresets.longFast, compact: true)
// }
// }
}
.frame(maxWidth: .infinity, alignment: .leading)
}
}
}
.padding([.top, .bottom])
}
}

View file

@ -12,6 +12,10 @@ import SwiftUI
import CoreLocation
struct NodeList: View {
init () {
//self.bleManager.context = context
}
@State private var searchText = ""
var nodesQuery: Binding<String> {
Binding {
@ -86,7 +90,7 @@ struct NodeList: View {
Image(systemName: "lines.measurement.horizontal")
.font(.footnote)
.symbolRenderingMode(.hierarchical)
DistanceText(meters: metersAway).font(.footnote)
DistanceText(meters: metersAway).font(.caption)
}
}
}
@ -119,7 +123,7 @@ struct NodeList: View {
MeshtasticLogo()
)
.onAppear {
self.bleManager.context = context
// self.bleManager.context = context
}
} detail: {
if let node = selection {

View file

@ -0,0 +1,82 @@
//
// NodeListSplit.swift
// Meshtastic
//
// Created by Garth Vander Houwen on 9/8/23.
//
import SwiftUI
import CoreLocation
struct NodeListSplit: View {
@State private var columnVisibility = NavigationSplitViewVisibility.all
@State private var searchText = ""
var nodesQuery: Binding<String> {
Binding {
searchText
} set: { newValue in
searchText = newValue
nodes.nsPredicate = newValue.isEmpty ? nil : NSPredicate(format: "user.longName CONTAINS[c] %@ OR user.shortName CONTAINS[c] %@", newValue, newValue)
}
}
@Environment(\.managedObjectContext) var context
@EnvironmentObject var bleManager: BLEManager
@FetchRequest(
sortDescriptors: [NSSortDescriptor(key: "lastHeard", ascending: false)],
animation: .default)
private var nodes: FetchedResults<NodeInfoEntity>
@State private var selection: NodeInfoEntity? // Nothing selected by default.
var body: some View {
NavigationSplitView(columnVisibility: $columnVisibility) {
let connectedNodeNum = Int(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral?.num ?? 0 : 0)
let connectedNode = nodes.first(where: { $0.num == connectedNodeNum })
List(nodes, id: \.self, selection: $selection) { node in
NodeListItem(node: node)
}
.searchable(text: nodesQuery, prompt: "Find a node")
.navigationTitle(String.localizedStringWithFormat("nodes %@".localized, String(nodes.count)))
.listStyle(.plain)
.navigationSplitViewColumnWidth(300)
.navigationBarItems(leading:
MeshtasticLogo()
)
} content: {
if let node = selection {
//NodeDetailItem(node: node)
NodeDetail(node: node)
.navigationSplitViewColumnWidth(300)
} else {
Text("select.node")
}
} detail: {
Text("Content")
}
.navigationSplitViewStyle(.balanced)
// } detail: {
// VStack {
// Button("Detail Only") {
// columnVisibility = .detailOnly
// }
//
// Button("Content and Detail") {
// columnVisibility = .doubleColumn
// }
//
// Button("Show All") {
// columnVisibility = .all
// }
// }
// }
}
}