diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 0a272ecd..b030b8b4 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -330,6 +330,9 @@ }, "Acknowledged by another node" : { + }, + "Actions" : { + }, "Active" : { @@ -400,6 +403,9 @@ }, "Add Channels" : { + }, + "Add to favorites" : { + }, "Additional help" : { @@ -17150,6 +17156,9 @@ }, "Remove" : { + }, + "Remove from favorites" : { + }, "Replace Channels" : { diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index fa0de4c2..dac78a1a 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 251926852C3BA97800249DF5 /* FavoriteNodeButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 251926842C3BA97800249DF5 /* FavoriteNodeButton.swift */; }; 259792252C2F114500AD1659 /* ChannelEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD58C5F12919AD3C00D5BEFB /* ChannelEntityExtension.swift */; }; 259792262C2F114500AD1659 /* PositionEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */; }; 259792272C2F114500AD1659 /* TraceRouteEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE5B4052B227E3200FCDD05 /* TraceRouteEntityExtension.swift */; }; @@ -222,6 +223,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 251926842C3BA97800249DF5 /* FavoriteNodeButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteNodeButton.swift; sourceTree = ""; }; 25AECD4E2C2F723200862C8E /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; 6D825E612C34786C008DBEE4 /* CommonRegex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonRegex.swift; sourceTree = ""; }; 6DA39D8D2A92DC52007E311C /* MeshtasticAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticAppDelegate.swift; sourceTree = ""; }; @@ -874,6 +876,7 @@ DDDB26452AACC0B7003AFCB7 /* NodeInfoItem.swift */, DDDB26412AABF655003AFCB7 /* NodeListItem.swift */, DDDCD56F2BB26F5C00BE6B60 /* NodeListFilter.swift */, + 251926842C3BA97800249DF5 /* FavoriteNodeButton.swift */, ); path = Helpers; sourceTree = ""; @@ -1202,6 +1205,7 @@ DDC3B274283F411B00AC321C /* LastHeardText.swift in Sources */, DDDE5A1029AFE69700490C6C /* MeshActivityAttributes.swift in Sources */, DD1925B928CDA93900720036 /* SerialConfigEnums.swift in Sources */, + 251926852C3BA97800249DF5 /* FavoriteNodeButton.swift in Sources */, D9C983A02B79D0E800BDBE6A /* AlertButton.swift in Sources */, DD86D4112881D16900BAEB7A /* WriteCsvFile.swift in Sources */, DDDB445029F8AC9C00EE2349 /* UIImage.swift in Sources */, diff --git a/Meshtastic.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Meshtastic.xcworkspace/xcshareddata/swiftpm/Package.resolved index 10db40a7..ae36a98b 100644 --- a/Meshtastic.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Meshtastic.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "74b3ad6215f078d89f4436b6ce0e318f145842efa3453bbe055ab76057de7d6b", + "originHash" : "c5be9820b6e5add3da0e3bd134c3826b3eece5f926d667cb3800a26572f9e63c", "pins" : [ { "identity" : "cocoamqtt", diff --git a/Meshtastic/Views/Nodes/Helpers/FavoriteNodeButton.swift b/Meshtastic/Views/Nodes/Helpers/FavoriteNodeButton.swift new file mode 100644 index 00000000..096674c7 --- /dev/null +++ b/Meshtastic/Views/Nodes/Helpers/FavoriteNodeButton.swift @@ -0,0 +1,45 @@ +import CoreData +import OSLog +import SwiftUI + +struct FavoriteNodeButton: View { + var bleManager: BLEManager + var context: NSManagedObjectContext + + @ObservedObject + var node: NodeInfoEntity + + var body: some View { + Button { + guard let connectedNodeNum = bleManager.connectedPeripheral?.num else { return } + let success = if node.favorite { + bleManager.removeFavoriteNode( + node: node, + connectedNodeNum: Int64(connectedNodeNum) + ) + } else { + bleManager.setFavoriteNode( + node: node, + connectedNodeNum: Int64(connectedNodeNum) + ) + } + if success { + node.favorite = !node.favorite + do { + try context.save() + } catch { + context.rollback() + Logger.data.error("Save Node Favorite Error") + } + Logger.data.debug("Favorited a node") + } + } label: { + Label { + Text(node.favorite ? "Remove from favorites" : "Add to favorites") + } icon: { + Image(systemName: node.favorite ? "star.fill" : "star") + .symbolRenderingMode(.multicolor) + } + } + } +} diff --git a/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift b/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift index 03cd132f..757caaa5 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift @@ -19,6 +19,40 @@ struct NodeDetail: View { @ObservedObject var node: NodeInfoEntity var columnVisibility = NavigationSplitViewVisibility.all + var favoriteNodeAction: some View { + let connectedNodeNum = bleManager.connectedPeripheral?.num ?? 0 + return Button { + let success = if node.favorite { + bleManager.removeFavoriteNode( + node: node, + connectedNodeNum: Int64(connectedNodeNum) + ) + } else { + bleManager.setFavoriteNode( + node: node, + connectedNodeNum: Int64(connectedNodeNum) + ) + } + if success { + node.favorite = !node.favorite + do { + try context.save() + } catch { + context.rollback() + Logger.data.error("Save Node Favorite Error") + } + Logger.data.debug("Favorited a node") + } + } label: { + Label { + Text(node.favorite ? "Remove from favorites" : "Add to favorites") + } icon: { + Image(systemName: node.favorite ? "star.fill" : "star") + .symbolRenderingMode(.multicolor) + } + } + } + var body: some View { NavigationStack { List { @@ -182,6 +216,14 @@ struct NodeDetail: View { } } + Section("Actions") { + FavoriteNodeButton( + bleManager: bleManager, + context: context, + node: node + ) + } + if let metadata = node.metadata, let connectedNode, self.bleManager.connectedPeripheral != nil { @@ -207,43 +249,47 @@ struct NodeDetail: View { } if metadata.canShutdown { - Label("Power Off", systemImage: "power") - .onTapGesture { - showingShutdownConfirm = true - } - .confirmationDialog( - "are.you.sure", - isPresented: $showingShutdownConfirm - ) { - Button("Shutdown Node?", role: .destructive) { - if !bleManager.sendShutdown( - fromUser: connectedNode.user!, - toUser: node.user!, - adminIndex: connectedNode.myInfo!.adminIndex - ) { - Logger.mesh.warning("Shutdown Failed") - } - } - } - } - Label("reboot", systemImage: "arrow.triangle.2.circlepath") - .onTapGesture { - showingRebootConfirm = true - } - .confirmationDialog( + Button { + showingShutdownConfirm = true + } label: { + Label("Power Off", systemImage: "power") + }.confirmationDialog( "are.you.sure", - isPresented: $showingRebootConfirm + isPresented: $showingShutdownConfirm ) { - Button("reboot.node", role: .destructive) { - if !bleManager.sendReboot( + Button("Shutdown Node?", role: .destructive) { + if !bleManager.sendShutdown( fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo!.adminIndex ) { - Logger.mesh.warning("Reboot Failed") + Logger.mesh.warning("Shutdown Failed") } } } + } + + Button { + showingRebootConfirm = true + } label: { + Label( + "reboot", + systemImage: "arrow.triangle.2.circlepath" + ) + }.confirmationDialog( + "are.you.sure", + isPresented: $showingRebootConfirm + ) { + Button("reboot.node", role: .destructive) { + if !bleManager.sendReboot( + fromUser: connectedNode.user!, + toUser: node.user!, + adminIndex: connectedNode.myInfo!.adminIndex + ) { + Logger.mesh.warning("Reboot Failed") + } + } + } } } } diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index f4c25ac6..54af2db4 100644 --- a/Meshtastic/Views/Nodes/NodeList.swift +++ b/Meshtastic/Views/Nodes/NodeList.swift @@ -55,7 +55,7 @@ struct NodeList: View { // } // } - let connectedNodeNum = Int(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral?.num ?? 0 : 0) + let connectedNodeNum = Int(bleManager.connectedPeripheral?.num ?? 0) let connectedNode = nodes.first(where: { $0.num == connectedNodeNum }) List(nodes, id: \.self, selection: $selectedNode) { node in @@ -63,38 +63,12 @@ struct NodeList: View { connected: bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral?.num ?? -1 == node.num, connectedNode: (bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral?.num ?? -1 : -1)) .contextMenu { + FavoriteNodeButton( + bleManager: bleManager, + context: context, + node: node + ) - Button { - if !node.favorite { - - let success = bleManager.setFavoriteNode(node: node, connectedNodeNum: Int64(connectedNodeNum)) - if success { - node.favorite = !node.favorite - do { - try context.save() - } catch { - context.rollback() - Logger.data.error("Save Node Favorite Error") - } - Logger.data.debug("Favorited a node") - } - } else { - let success = bleManager.removeFavoriteNode(node: node, connectedNodeNum: Int64(connectedNodeNum)) - if success { - node.favorite = !node.favorite - do { - try context.save() - } catch { - context.rollback() - Logger.data.error("Save Node Favorite Error") - } - Logger.data.debug("Favorited a node") - } - } - - } label: { - Label(node.favorite ? "Un-Favorite" : "Favorite", systemImage: node.favorite ? "star.slash.fill" : "star.fill") - } if node.user != nil { Button { node.user!.mute = !node.user!.mute