From 1db42dbbcdaa89580888e76cb1d423c9d7d9109d Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 2 Mar 2024 10:18:57 -0800 Subject: [PATCH] Add exchange position node list menu item, add improved search to the user list. --- Meshtastic/Helpers/BLEManager.swift | 3 - Meshtastic/Views/Messages/UserList.swift | 167 ++++++++++++----------- Meshtastic/Views/Nodes/NodeList.swift | 33 ++++- 3 files changed, 115 insertions(+), 88 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 163e19a3..cf663f10 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1039,9 +1039,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate } catch { return false } - return false - - var meshPacket = MeshPacket() meshPacket.to = UInt32(destNum) diff --git a/Meshtastic/Views/Messages/UserList.swift b/Meshtastic/Views/Messages/UserList.swift index e4fa4b55..542e7650 100644 --- a/Meshtastic/Views/Messages/UserList.swift +++ b/Meshtastic/Views/Messages/UserList.swift @@ -17,12 +17,19 @@ struct UserList: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager @State private var searchText = "" + var usersQuery: Binding { Binding { searchText } set: { newValue in searchText = newValue - users.nsPredicate = newValue.isEmpty ? nil : NSPredicate(format: "longName CONTAINS[c] %@ OR shortName CONTAINS[c] %@", newValue, newValue) + /// Case Insensitive Search Text Predicates + let searchPredicates = ["userId", "hwModel", "longName", "shortName"].map { property in + return NSPredicate(format: "%K CONTAINS[c] %@", property, searchText) + } + /// Create a compound predicate using each text search predicate as an OR + let textSearchPredicate = NSCompoundPredicate(type: .or, subpredicates: searchPredicates) + users.nsPredicate = newValue.isEmpty ? nil : textSearchPredicate } } @FetchRequest( @@ -48,94 +55,94 @@ struct UserList: View { let lastMessageDay = Calendar.current.dateComponents([.day], from: lastMessageTime).day ?? 0 let currentDay = Calendar.current.dateComponents([.day], from: Date()).day ?? 0 if user.num != bleManager.connectedPeripheral?.num ?? 0 { - NavigationLink(destination: UserMessageList(user: user)) { - ZStack { - Image(systemName: "circle.fill") - .opacity(user.unreadMessages > 0 ? 1 : 0) - .font(.system(size: 10)) - .foregroundColor(.accentColor) - .brightness(0.2) - } - - CircleText(text: user.shortName ?? "?", color: Color(UIColor(hex: UInt32(user.num)))) - - VStack(alignment: .leading){ - HStack{ - Text(user.longName ?? "unknown".localized) - .font(.headline) - Spacer() - if user.vip { - Image(systemName: "star.fill") - .foregroundColor(.yellow) + NavigationLink(destination: UserMessageList(user: user)) { + ZStack { + Image(systemName: "circle.fill") + .opacity(user.unreadMessages > 0 ? 1 : 0) + .font(.system(size: 10)) + .foregroundColor(.accentColor) + .brightness(0.2) + } + + CircleText(text: user.shortName ?? "?", color: Color(UIColor(hex: UInt32(user.num)))) + + VStack(alignment: .leading){ + HStack{ + Text(user.longName ?? "unknown".localized) + .font(.headline) + Spacer() + if user.vip { + Image(systemName: "star.fill") + .foregroundColor(.yellow) + } + if user.messageList.count > 0 { + if lastMessageDay == currentDay { + Text(lastMessageTime, style: .time ) + .font(.footnote) + .foregroundColor(.secondary) + } else if lastMessageDay == (currentDay - 1) { + Text("Yesterday") + .font(.footnote) + .foregroundColor(.secondary) + } else if lastMessageDay < (currentDay - 1) && lastMessageDay > (currentDay - 5) { + Text(lastMessageTime.formattedDate(format: dateFormatString)) + .font(.footnote) + .foregroundColor(.secondary) + } else if lastMessageDay < (currentDay - 1800) { + Text(lastMessageTime.formattedDate(format: dateFormatString)) + .font(.footnote) + .foregroundColor(.secondary) + } + } } + if user.messageList.count > 0 { - if lastMessageDay == currentDay { - Text(lastMessageTime, style: .time ) - .font(.footnote) - .foregroundColor(.secondary) - } else if lastMessageDay == (currentDay - 1) { - Text("Yesterday") - .font(.footnote) - .foregroundColor(.secondary) - } else if lastMessageDay < (currentDay - 1) && lastMessageDay > (currentDay - 5) { - Text(lastMessageTime.formattedDate(format: dateFormatString)) - .font(.footnote) - .foregroundColor(.secondary) - } else if lastMessageDay < (currentDay - 1800) { - Text(lastMessageTime.formattedDate(format: dateFormatString)) + HStack(alignment: .top) { + Text("\(mostRecent != nil ? mostRecent!.messagePayload! : " ")") .font(.footnote) .foregroundColor(.secondary) } } } - - if user.messageList.count > 0 { - HStack(alignment: .top) { - Text("\(mostRecent != nil ? mostRecent!.messagePayload! : " ")") - .font(.footnote) - .foregroundColor(.secondary) + } + .frame(height: 62) + .contextMenu { + Button { + user.vip = !user.vip + do { + try context.save() + } catch { + context.rollback() + print("💥 Save User VIP Error") + } + } label: { + Label(user.vip ? "Un-Favorite" : "Favorite", systemImage: user.vip ? "star.slash.fill" : "star.fill") + } + Button { + user.mute = !user.mute + do { + try context.save() + } catch { + context.rollback() + print("💥 Save User Mute Error") + } + } label: { + Label(user.mute ? "Show Alerts" : "Hide Alerts", systemImage: user.mute ? "bell" : "bell.slash") + } + if user.messageList.count > 0 { + Button(role: .destructive) { + isPresentingDeleteUserMessagesConfirm = true + userSelection = user + } label: { + Label("Delete Messages", systemImage: "trash") } } } - } - .frame(height: 62) - .contextMenu { - Button { - user.vip = !user.vip - do { - try context.save() - } catch { - context.rollback() - print("💥 Save User VIP Error") - } - } label: { - Label(user.vip ? "Un-Favorite" : "Favorite", systemImage: user.vip ? "star.slash.fill" : "star.fill") - } - Button { - user.mute = !user.mute - do { - try context.save() - } catch { - context.rollback() - print("💥 Save User Mute Error") - } - } label: { - Label(user.mute ? "Show Alerts" : "Hide Alerts", systemImage: user.mute ? "bell" : "bell.slash") - } - if user.messageList.count > 0 { - Button(role: .destructive) { - isPresentingDeleteUserMessagesConfirm = true - userSelection = user - } label: { - Label("Delete Messages", systemImage: "trash") - } - } - } - .confirmationDialog( - "This conversation will be deleted.", - isPresented: $isPresentingDeleteUserMessagesConfirm, - titleVisibility: .visible - ) { + .confirmationDialog( + "This conversation will be deleted.", + isPresented: $isPresentingDeleteUserMessagesConfirm, + titleVisibility: .visible + ) { Button(role: .destructive) { deleteUserMessages(user: userSelection!, context: context) context.refresh(node!.user!, mergeChanges: true) @@ -149,7 +156,7 @@ struct UserList: View { } .listStyle(.plain) .navigationTitle(String.localizedStringWithFormat("contacts %@".localized, String(users.count == 0 ? 0 : users.count - 1))) - .searchable(text: usersQuery, prompt: "Find a contact") + .searchable(text: usersQuery, placement: users.count > 10 ? .navigationBarDrawer(displayMode: .always) : .automatic, prompt: "Find a contact") } } } diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index 569b6651..122f8f10 100644 --- a/Meshtastic/Views/Nodes/NodeList.swift +++ b/Meshtastic/Views/Nodes/NodeList.swift @@ -36,6 +36,7 @@ struct NodeList: View { @State private var isPresentingTraceRouteSentAlert = false @State private var isPresentingClientHistorySentAlert = false @State private var isPresentingDeleteNodeAlert = false + @State private var isPresentingPositionSentAlert = false @State private var deleteNodeId: Int64 = 0 @State private var searchState = NodeSearchState() @@ -89,7 +90,21 @@ struct NodeList: View { } label: { Label(node.user!.mute ? "Show Alerts" : "Hide Alerts", systemImage: node.user!.mute ? "bell" : "bell.slash") } - if connectedNodeNum != node.num { + if bleManager.connectedPeripheral != nil { + Button { + let positionSent = bleManager.sendPosition( + channel: node.channel, + destNum: node.num, + wantResponse: true + ) + if positionSent { + isPresentingPositionSentAlert = true + } + } label: { + Label("Exchange Positions", systemImage: "arrow.triangle.2.circlepath") + } + } + if bleManager.connectedPeripheral != nil && connectedNodeNum != node.num { Button { let success = bleManager.sendTraceRouteRequest(destNum: node.user?.num ?? 0, wantResponse: true) if success { @@ -120,6 +135,14 @@ struct NodeList: View { } } } + .alert( + "Position Sent", + isPresented: $isPresentingPositionSentAlert + ) { + Button("OK", role: .cancel) { } + } message: { + Text("Your position has been sent with a request for a response with their position.") + } .alert( "Trace Route Sent", isPresented: $isPresentingTraceRouteSentAlert @@ -214,10 +237,10 @@ struct NodeList: View { } .navigationSplitViewStyle(.balanced) .onChange(of: searchState.searchText) { _ in - runSearch() + searchNodeList() } .onChange(of: searchState.searchScope) { _ in - runSearch() + searchNodeList() } .onAppear { if self.bleManager.context == nil { @@ -226,9 +249,9 @@ struct NodeList: View { } } - private func runSearch() { + private func searchNodeList() { /// Case Insensitive Search Text Predicates - var searchPredicates = ["user.userId", "user.hwModel", "user.longName", "user.shortName"].map { property in + let searchPredicates = ["user.userId", "user.hwModel", "user.longName", "user.shortName"].map { property in return NSPredicate(format: "%K CONTAINS[c] %@", property, searchState.searchText) } /// Create a compound predicate using each text search preicate as an OR