mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Add exchange position node list menu item, add improved search to the user list.
This commit is contained in:
parent
78b902dae0
commit
1db42dbbcd
3 changed files with 115 additions and 88 deletions
|
|
@ -1039,9 +1039,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
} catch {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
|
||||
|
||||
|
||||
var meshPacket = MeshPacket()
|
||||
meshPacket.to = UInt32(destNum)
|
||||
|
|
|
|||
|
|
@ -17,12 +17,19 @@ struct UserList: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@State private var searchText = ""
|
||||
|
||||
var usersQuery: Binding<String> {
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue