mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
capitilize node list, update unmessagable logic, clean up contact list filtering
This commit is contained in:
parent
a0c8c6f4a0
commit
530872e50f
6 changed files with 168 additions and 150 deletions
|
|
@ -66,7 +66,7 @@
|
|||
BCB613852C68703800485544 /* NodePositionIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613842C68703800485544 /* NodePositionIntent.swift */; };
|
||||
BCB613872C69A0FB00485544 /* AppIntentErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613862C69A0FB00485544 /* AppIntentErrors.swift */; };
|
||||
BCD93CBA2D9E11A2006C9214 /* DisconnectNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD93CB92D9E11A2006C9214 /* DisconnectNodeIntent.swift */; };
|
||||
BCDDFA9A2DBB180D0065189C /* ScrollToBottomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCDDFA992DBB180D0065189C /* ScrollToBottomButton.swift */; };
|
||||
BCDDFA9A2DBB180D0065189C /* ScrollToBottomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCDDFA992DBB180D0065189C /* ScrollToBottomButton.swift */; };
|
||||
BCE2D3C32C7ADF42008E6199 /* ShutDownNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE2D3C22C7ADF42008E6199 /* ShutDownNodeIntent.swift */; };
|
||||
BCE2D3C52C7AE369008E6199 /* RestartNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE2D3C42C7AE369008E6199 /* RestartNodeIntent.swift */; };
|
||||
BCE2D3C72C7B0D0A008E6199 /* ShortcutsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE2D3C62C7B0D0A008E6199 /* ShortcutsProvider.swift */; };
|
||||
|
|
@ -333,7 +333,7 @@
|
|||
BCB613842C68703800485544 /* NodePositionIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodePositionIntent.swift; sourceTree = "<group>"; };
|
||||
BCB613862C69A0FB00485544 /* AppIntentErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIntentErrors.swift; sourceTree = "<group>"; };
|
||||
BCD93CB92D9E11A2006C9214 /* DisconnectNodeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisconnectNodeIntent.swift; sourceTree = "<group>"; };
|
||||
BCDDFA992DBB180D0065189C /* ScrollToBottomButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollToBottomButton.swift; sourceTree = "<group>"; };
|
||||
BCDDFA992DBB180D0065189C /* ScrollToBottomButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollToBottomButton.swift; sourceTree = "<group>"; };
|
||||
BCE2D3C22C7ADF42008E6199 /* ShutDownNodeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShutDownNodeIntent.swift; sourceTree = "<group>"; };
|
||||
BCE2D3C42C7AE369008E6199 /* RestartNodeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestartNodeIntent.swift; sourceTree = "<group>"; };
|
||||
BCE2D3C62C7B0D0A008E6199 /* ShortcutsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsProvider.swift; sourceTree = "<group>"; };
|
||||
|
|
|
|||
|
|
@ -317,12 +317,17 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
newUser.pkiEncrypted = true
|
||||
newUser.publicKey = nodeInfo.user.publicKey
|
||||
}
|
||||
let roles: [Int32] = [2, 4, 5, 6, 7, 10, 11]
|
||||
if roles.contains(Int32(newUser.role)) {
|
||||
newUser.unmessagable = true
|
||||
/// For nodes that have the optional isUnmessagable boolean use that, otherwise excluded roles that are unmessagable by default
|
||||
if nodeInfo.user.hasIsUnmessagable {
|
||||
newUser.unmessagable = nodeInfo.user.isUnmessagable
|
||||
} else {
|
||||
newUser.unmessagable = false
|
||||
}
|
||||
let roles = [2, 4, 5, 6, 7, 10, 11]
|
||||
let containsRole = roles.contains(Int(newUser.role))
|
||||
if containsRole {
|
||||
newUser.unmessagable = true
|
||||
} else {
|
||||
newUser.unmessagable = false
|
||||
}}
|
||||
newNode.user = newUser
|
||||
} else if nodeInfo.num > Constants.minimumNodeNum {
|
||||
let newUser = createUser(num: Int64(nodeInfo.num), context: context)
|
||||
|
|
@ -386,25 +391,31 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
fetchedNode[0].user?.pkiEncrypted = true
|
||||
fetchedNode[0].user?.publicKey = nodeInfo.user.publicKey
|
||||
}
|
||||
fetchedNode[0].user!.userId = nodeInfo.user.id
|
||||
fetchedNode[0].user!.num = Int64(nodeInfo.num)
|
||||
fetchedNode[0].user!.numString = String(nodeInfo.num)
|
||||
fetchedNode[0].user!.longName = nodeInfo.user.longName
|
||||
fetchedNode[0].user!.shortName = nodeInfo.user.shortName
|
||||
fetchedNode[0].user!.isLicensed = nodeInfo.user.isLicensed
|
||||
fetchedNode[0].user!.role = Int32(nodeInfo.user.role.rawValue)
|
||||
fetchedNode[0].user!.hwModel = String(describing: nodeInfo.user.hwModel).uppercased()
|
||||
fetchedNode[0].user!.hwModelId = Int32(nodeInfo.user.hwModel.rawValue)
|
||||
let roles: [Int32] = [-1, 2, 4, 5, 6, 7, 10, 11]
|
||||
if roles.contains(Int32(fetchedNode[0].user?.role ?? -1)) {
|
||||
fetchedNode[0].user!.unmessagable = true
|
||||
fetchedNode[0].user?.userId = nodeInfo.user.id
|
||||
fetchedNode[0].user?.num = Int64(nodeInfo.num)
|
||||
fetchedNode[0].user?.numString = String(nodeInfo.num)
|
||||
fetchedNode[0].user?.longName = nodeInfo.user.longName
|
||||
fetchedNode[0].user?.shortName = nodeInfo.user.shortName
|
||||
fetchedNode[0].user?.isLicensed = nodeInfo.user.isLicensed
|
||||
fetchedNode[0].user?.role = Int32(nodeInfo.user.role.rawValue)
|
||||
fetchedNode[0].user?.hwModel = String(describing: nodeInfo.user.hwModel).uppercased()
|
||||
fetchedNode[0].user?.hwModelId = Int32(nodeInfo.user.hwModel.rawValue)
|
||||
/// For nodes that have the optional isUnmessagable boolean use that, otherwise excluded roles that are unmessagable by default
|
||||
if nodeInfo.user.hasIsUnmessagable {
|
||||
fetchedNode[0].user?.unmessagable = nodeInfo.user.isUnmessagable
|
||||
} else {
|
||||
fetchedNode[0].user!.unmessagable = false
|
||||
let roles = [-1, 2, 4, 5, 6, 7, 10, 11]
|
||||
let containsRole = roles.contains(Int(fetchedNode[0].user?.role ?? -1))
|
||||
if containsRole {
|
||||
fetchedNode[0].user?.unmessagable = true
|
||||
} else {
|
||||
fetchedNode[0].user?.unmessagable = false
|
||||
}
|
||||
}
|
||||
Task {
|
||||
Api().loadDeviceHardwareData { (hw) in
|
||||
let dh = hw.first(where: { $0.hwModel == fetchedNode[0].user!.hwModelId })
|
||||
fetchedNode[0].user!.hwDisplayName = dh?.displayName
|
||||
fetchedNode[0].user?.hwDisplayName = dh?.displayName
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -479,7 +479,7 @@
|
|||
<attribute name="publicKey" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="role" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="shortName" attributeType="String"/>
|
||||
<attribute name="unmessagable" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="unmessagable" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="userId" attributeType="String"/>
|
||||
<relationship name="receivedMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="toUser" inverseEntity="MessageEntity"/>
|
||||
<relationship name="sentMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="fromUser" inverseEntity="MessageEntity"/>
|
||||
|
|
|
|||
|
|
@ -183,12 +183,13 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
newUser.role = Int32(newUserMessage.role.rawValue)
|
||||
newUser.hwModel = String(describing: newUserMessage.hwModel).uppercased()
|
||||
newUser.hwModelId = Int32(newUserMessage.hwModel.rawValue)
|
||||
/// For nodes that have the optional isUnmessagable boolean use that, otherwise excluded roles that are unmessagable by default
|
||||
if newUserMessage.hasIsUnmessagable {
|
||||
newUser.unmessagable = newUserMessage.isUnmessagable
|
||||
} else {
|
||||
// For older firmare make Repeater, Router, Router Late, Sensor, Tracker, TAK, and TAK Tracker unmessagable
|
||||
let roles: [Int32] = [2, 4, 5, 6, 7, 10, 11]
|
||||
if roles.contains(Int32(newUser.role)) {
|
||||
let roles = [2, 4, 5, 6, 7, 10, 11]
|
||||
let containsRole = roles.contains(Int(newUser.role))
|
||||
if containsRole {
|
||||
newUser.unmessagable = true
|
||||
} else {
|
||||
newUser.unmessagable = false
|
||||
|
|
@ -290,15 +291,16 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
fetchedNode[0].user!.role = Int32(nodeInfoMessage.user.role.rawValue)
|
||||
fetchedNode[0].user!.hwModel = String(describing: nodeInfoMessage.user.hwModel).uppercased()
|
||||
fetchedNode[0].user!.hwModelId = Int32(nodeInfoMessage.user.hwModel.rawValue)
|
||||
/// For nodes that have the optional isUnmessagable boolean use that, otherwise excluded roles that are unmessagable by default
|
||||
if nodeInfoMessage.user.hasIsUnmessagable {
|
||||
fetchedNode[0].user!.unmessagable = nodeInfoMessage.user.isUnmessagable
|
||||
} else {
|
||||
// For older firmare make Repeater, Router, Router Late, Sensor, Tracker, TAK, and TAK Tracker unmessagable
|
||||
let roles: [Int32] = [-1, 2, 4, 5, 6, 7, 10, 11]
|
||||
if roles.contains(Int32(fetchedNode[0].user?.role ?? -1)) {
|
||||
fetchedNode[0].user!.unmessagable = true
|
||||
let roles = [-1, 2, 4, 5, 6, 7, 10, 11]
|
||||
let containsRole = roles.contains(Int(fetchedNode[0].user?.role ?? -1))
|
||||
if containsRole {
|
||||
fetchedNode[0].user?.unmessagable = true
|
||||
} else {
|
||||
fetchedNode[0].user!.unmessagable = false
|
||||
fetchedNode[0].user?.unmessagable = false
|
||||
}
|
||||
}
|
||||
if !nodeInfoMessage.user.publicKey.isEmpty {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ struct UserList: View {
|
|||
@State private var isPkiEncrypted = false
|
||||
@State private var isFavorite = false
|
||||
@State private var isIgnored = false
|
||||
@State private var isUnmessagable = false
|
||||
@State private var isEnvironment = false
|
||||
@State private var distanceFilter = false
|
||||
@State private var maxDistance: Double = 800000
|
||||
|
|
@ -46,8 +47,9 @@ struct UserList: View {
|
|||
NSSortDescriptor(key: "userNode.lastHeard", ascending: false),
|
||||
NSSortDescriptor(key: "longName", ascending: true)],
|
||||
predicate: NSPredicate(
|
||||
format: "userNode.ignored == false && longName != '' AND unmessagable == false"
|
||||
), animation: .default)
|
||||
format: "userNode.ignored == NO AND unmessagable = NO"
|
||||
), animation: .spring
|
||||
)
|
||||
var users: FetchedResults<UserEntity>
|
||||
|
||||
@Binding var node: NodeInfoEntity?
|
||||
|
|
@ -59,141 +61,139 @@ struct UserList: View {
|
|||
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMdd", options: 0, locale: Locale.current)
|
||||
let dateFormatString = (localeDateFormat ?? "MM/dd/YY")
|
||||
VStack {
|
||||
List(selection: $userSelection) {
|
||||
ForEach(users) { (user: UserEntity) in
|
||||
let mostRecent = user.messageList.last
|
||||
let lastMessageTime = Date(timeIntervalSince1970: TimeInterval(Int64((mostRecent?.messageTimestamp ?? 0 ))))
|
||||
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(value: user) {
|
||||
ZStack {
|
||||
Image(systemName: "circle.fill")
|
||||
.opacity(user.unreadMessages > 0 ? 1 : 0)
|
||||
.font(.system(size: 10))
|
||||
.foregroundColor(.accentColor)
|
||||
.brightness(0.2)
|
||||
}
|
||||
List(users, selection: $userSelection) { (user: UserEntity) in
|
||||
let mostRecent = user.messageList.last
|
||||
let lastMessageTime = Date(timeIntervalSince1970: TimeInterval(Int64((mostRecent?.messageTimestamp ?? 0 ))))
|
||||
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(value: 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))))
|
||||
CircleText(text: user.shortName ?? "?", color: Color(UIColor(hex: UInt32(user.num))))
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
if user.pkiEncrypted {
|
||||
if !user.keyMatch {
|
||||
/// Public Key on the User and the Public Key on the Last Message don't match
|
||||
Image(systemName: "key.slash")
|
||||
.foregroundColor(.red)
|
||||
} else {
|
||||
Image(systemName: "lock.fill")
|
||||
.foregroundColor(.green)
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
if user.pkiEncrypted {
|
||||
if !user.keyMatch {
|
||||
/// Public Key on the User and the Public Key on the Last Message don't match
|
||||
Image(systemName: "key.slash")
|
||||
.foregroundColor(.red)
|
||||
} else {
|
||||
Image(systemName: "lock.open.fill")
|
||||
.foregroundColor(.yellow)
|
||||
}
|
||||
Text(user.longName ?? "Unknown".localized)
|
||||
.font(.headline)
|
||||
.allowsTightening(true)
|
||||
Spacer()
|
||||
if user.userNode?.favorite ?? false {
|
||||
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)
|
||||
}
|
||||
Image(systemName: "lock.fill")
|
||||
.foregroundColor(.green)
|
||||
}
|
||||
} else {
|
||||
Image(systemName: "lock.open.fill")
|
||||
.foregroundColor(.yellow)
|
||||
}
|
||||
Text(user.longName ?? "Unknown".localized)
|
||||
.font(.headline)
|
||||
.allowsTightening(true)
|
||||
Spacer()
|
||||
if user.userNode?.favorite ?? false {
|
||||
Image(systemName: "star.fill")
|
||||
.foregroundColor(.yellow)
|
||||
}
|
||||
|
||||
if user.messageList.count > 0 {
|
||||
HStack(alignment: .top) {
|
||||
Text("\(mostRecent != nil ? mostRecent!.messagePayload! : " ")")
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(height: 62)
|
||||
.contextMenu {
|
||||
Button {
|
||||
|
||||
if node != nil && !(user.userNode?.favorite ?? false) {
|
||||
let success = bleManager.setFavoriteNode(node: user.userNode!, connectedNodeNum: Int64(node!.num))
|
||||
if success {
|
||||
user.userNode?.favorite = !(user.userNode?.favorite ?? true)
|
||||
Logger.data.info("Favorited a node")
|
||||
}
|
||||
} else {
|
||||
let success = bleManager.removeFavoriteNode(node: user.userNode!, connectedNodeNum: Int64(node!.num))
|
||||
if success {
|
||||
user.userNode?.favorite = !(user.userNode?.favorite ?? true)
|
||||
Logger.data.info("Un Favorited a node")
|
||||
}
|
||||
}
|
||||
context.refresh(user, mergeChanges: true)
|
||||
do {
|
||||
try context.save()
|
||||
} catch {
|
||||
context.rollback()
|
||||
Logger.data.error("Save Node Favorite Error")
|
||||
}
|
||||
} label: {
|
||||
Label((user.userNode?.favorite ?? false) ? "Un-Favorite" : "Favorite", systemImage: (user.userNode?.favorite ?? false) ? "star.slash.fill" : "star.fill")
|
||||
}
|
||||
Button {
|
||||
user.mute = !user.mute
|
||||
do {
|
||||
try context.save()
|
||||
} catch {
|
||||
context.rollback()
|
||||
Logger.data.error("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")
|
||||
if user.messageList.count > 0 {
|
||||
HStack(alignment: .top) {
|
||||
Text("\(mostRecent != nil ? mostRecent!.messagePayload! : " ")")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
.confirmationDialog(
|
||||
"This conversation will be deleted.",
|
||||
isPresented: $isPresentingDeleteUserMessagesConfirm,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button(role: .destructive) {
|
||||
deleteUserMessages(user: userSelection!, context: context)
|
||||
context.refresh(node!.user!, mergeChanges: true)
|
||||
} label: {
|
||||
Text("Delete")
|
||||
}
|
||||
.frame(height: 62)
|
||||
.contextMenu {
|
||||
Button {
|
||||
|
||||
if node != nil && !(user.userNode?.favorite ?? false) {
|
||||
let success = bleManager.setFavoriteNode(node: user.userNode!, connectedNodeNum: Int64(node!.num))
|
||||
if success {
|
||||
user.userNode?.favorite = !(user.userNode?.favorite ?? true)
|
||||
Logger.data.info("Favorited a node")
|
||||
}
|
||||
} else {
|
||||
let success = bleManager.removeFavoriteNode(node: user.userNode!, connectedNodeNum: Int64(node!.num))
|
||||
if success {
|
||||
user.userNode?.favorite = !(user.userNode?.favorite ?? true)
|
||||
Logger.data.info("Un Favorited a node")
|
||||
}
|
||||
}
|
||||
context.refresh(user, mergeChanges: true)
|
||||
do {
|
||||
try context.save()
|
||||
} catch {
|
||||
context.rollback()
|
||||
Logger.data.error("Save Node Favorite Error")
|
||||
}
|
||||
} label: {
|
||||
Label((user.userNode?.favorite ?? false) ? "Un-Favorite" : "Favorite", systemImage: (user.userNode?.favorite ?? false) ? "star.slash.fill" : "star.fill")
|
||||
}
|
||||
Button {
|
||||
user.mute = !user.mute
|
||||
do {
|
||||
try context.save()
|
||||
} catch {
|
||||
context.rollback()
|
||||
Logger.data.error("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
|
||||
) {
|
||||
Button(role: .destructive) {
|
||||
deleteUserMessages(user: userSelection!, context: context)
|
||||
context.refresh(node!.user!, mergeChanges: true)
|
||||
} label: {
|
||||
Text("Delete")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.navigationTitle(String.localizedStringWithFormat("Contacts (%@)".localized, String(users.count == 0 ? 0 : users.count)))
|
||||
.navigationTitle(String.localizedStringWithFormat("Contacts (%@)".localized, String(users.count == 0 ? 0 : users.count - 1)))
|
||||
.sheet(isPresented: $editingFilters) {
|
||||
NodeListFilter(filterTitle: "Contact Filters", viaLora: $viaLora, viaMqtt: $viaMqtt, isOnline: $isOnline, isPkiEncrypted: $isPkiEncrypted, isFavorite: $isFavorite, isIgnored: $isIgnored, isEnvironment: $isEnvironment, distanceFilter: $distanceFilter, maximumDistance: $maxDistance, hopsAway: $hopsAway, roleFilter: $roleFilter, deviceRoles: $deviceRoles)
|
||||
}
|
||||
|
|
@ -246,7 +246,7 @@ struct UserList: View {
|
|||
await searchUserList()
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
.onFirstAppear {
|
||||
Task {
|
||||
await searchUserList()
|
||||
}
|
||||
|
|
@ -297,8 +297,6 @@ struct UserList: View {
|
|||
let textSearchPredicate = NSCompoundPredicate(type: .or, subpredicates: searchPredicates)
|
||||
/// Create an array of predicates to hold our AND predicates
|
||||
var predicates: [NSPredicate] = []
|
||||
let defaultPredicate = NSPredicate(format: "userNode.ignored == NO AND longName != '' AND unmessagable == NO")
|
||||
predicates.append(defaultPredicate)
|
||||
/// Mqtt and lora
|
||||
if !(viaLora && viaMqtt) {
|
||||
if viaLora {
|
||||
|
|
@ -345,6 +343,13 @@ struct UserList: View {
|
|||
let isFavoritePredicate = NSPredicate(format: "userNode.favorite == YES")
|
||||
predicates.append(isFavoritePredicate)
|
||||
}
|
||||
/// Ignored
|
||||
let isIgnoredPredicate = NSPredicate(format: "userNode.ignored == NO")
|
||||
predicates.append(isIgnoredPredicate)
|
||||
/// Unmessagable
|
||||
let isUnmessagablePredicate = NSPredicate(format: "unmessagable == NO")
|
||||
predicates.append(isUnmessagablePredicate)
|
||||
|
||||
/// Distance
|
||||
if distanceFilter {
|
||||
let pointOfInterest = LocationsHandler.currentLocation
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ struct PositionPopover: View {
|
|||
if lastLocation.distance(from: CLLocation(latitude: LocationsHandler.DefaultLocation.latitude, longitude: LocationsHandler.DefaultLocation.longitude)) > 0.0 {
|
||||
let metersAway = position.coordinate.distance(from: CLLocationCoordinate2D(latitude: lastLocation.coordinate.latitude, longitude: lastLocation.coordinate.longitude))
|
||||
Label {
|
||||
Text("distance".localized + ": \(distanceFormatter.string(fromDistance: Double(metersAway)))")
|
||||
Text("Distance".localized + ": \(distanceFormatter.string(fromDistance: Double(metersAway)))")
|
||||
.foregroundColor(.primary)
|
||||
.font(idiom == .phone ? .callout : .body)
|
||||
} icon: {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue