From ca0b9e66d390f36a3a031691aaf782d5ec2e5dc2 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 17 Dec 2022 23:53:06 -0800 Subject: [PATCH] Enter for submit on mac --- Meshtastic/Helpers/BLEManager.swift | 1 - Meshtastic/Helpers/MeshPackets.swift | 1 + Meshtastic/Persistence/UpdateCoreData.swift | 5 +- .../Views/Messages/ChannelMessageList.swift | 17 ++- Meshtastic/Views/Messages/Contacts.swift | 128 +++++++++--------- .../Views/Messages/UserMessageList.swift | 15 ++ Meshtastic/Views/Settings/Channels.swift | 92 +++++-------- 7 files changed, 133 insertions(+), 126 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 80cce787..0c1790fa 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -957,7 +957,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { fetchedMyInfo[0].channels = mutableChannels do { try context!.save() - } catch { print("Failed to clear existing channels from local app database") } diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index dcd2e64b..03b1d0de 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -756,6 +756,7 @@ func channelPacket (channel: Channel, fromNum: Int64, context: NSManagedObjectCo if fetchedMyInfo.count == 1 { let newChannel = ChannelEntity(context: context) + newChannel.id = Int32(channel.index) newChannel.index = Int32(channel.index) newChannel.uplinkEnabled = channel.settings.uplinkEnabled newChannel.downlinkEnabled = channel.settings.downlinkEnabled diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index a189e58e..faa46e9b 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -60,9 +60,9 @@ public func clearTelemetry(destNum: Int64, metricsType: Int32, context: NSManage } } -public func deleteChannelMessages(channelIndex: Int32, context: NSManagedObjectContext) { +public func deleteChannelMessages(channel: ChannelEntity, context: NSManagedObjectContext) { let fetchChannelMessagesRequest = NSFetchRequest(entityName: "MessageEntity") - fetchChannelMessagesRequest.predicate = NSPredicate(format: "channel == %i AND toUser == nil AND admin == false", Int32(channelIndex)) + fetchChannelMessagesRequest.predicate = NSPredicate(format: "channel == %i AND toUser == nil AND admin == false", Int32(channel.id)) fetchChannelMessagesRequest.includesPropertyValues = false do { let objects = try context.fetch(fetchChannelMessagesRequest) as! [NSManagedObject] @@ -70,7 +70,6 @@ public func deleteChannelMessages(channelIndex: Int32, context: NSManagedObjectC context.delete(object) } try context.save() - context.refreshAllObjects() } catch let error as NSError { print("Error: \(error.localizedDescription)") } diff --git a/Meshtastic/Views/Messages/ChannelMessageList.swift b/Meshtastic/Views/Messages/ChannelMessageList.swift index afe52cb9..b59c3195 100644 --- a/Meshtastic/Views/Messages/ChannelMessageList.swift +++ b/Meshtastic/Views/Messages/ChannelMessageList.swift @@ -311,9 +311,22 @@ struct ChannelMessageList: View { .focused($focusedField, equals: .messageText) .multilineTextAlignment(.leading) .frame(minHeight: 50) - + .keyboardShortcut(.defaultAction) + .onSubmit { + #if targetEnvironment(macCatalyst) + if bleManager.sendMessage(message: typingMessage, toUserNum: 0, channel: channel.index, isEmoji: false, replyID: replyMessageId) { + typingMessage = "" + focusedField = nil + replyMessageId = 0 + if sendPositionWithMessage { + if bleManager.sendPosition(destNum: Int64(channel.index), wantAck: true) { + print("Location Sent") + } + } + } + #endif + } Text(typingMessage).opacity(0).padding(.all, 0) - } .overlay(RoundedRectangle(cornerRadius: 20).stroke(.tertiary, lineWidth: 1)) .padding(.bottom, 15) diff --git a/Meshtastic/Views/Messages/Contacts.swift b/Meshtastic/Views/Messages/Contacts.swift index 7d873cee..76ce62ed 100644 --- a/Meshtastic/Views/Messages/Contacts.swift +++ b/Meshtastic/Views/Messages/Contacts.swift @@ -33,82 +33,79 @@ struct Contacts: View { if node != nil && node!.myInfo != nil && node!.myInfo!.channels != nil { ForEach(node!.myInfo!.channels!.array as! [ChannelEntity], id: \.self) { (channel: ChannelEntity) in if channel.name?.lowercased() ?? "" != "admin" && channel.name?.lowercased() ?? "" != "gpio" && channel.name?.lowercased() ?? "" != "serial" { - VStack { - NavigationLink(destination: ChannelMessageList(channel: channel)) { - - let mostRecent = channel.allPrivateMessages.last(where: { $0.channel == channel.index }) - 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 - VStack(alignment: .leading) { - HStack { - CircleText(text: String(channel.index), color: .accentColor, circleSize: 52, fontSize: 40, brightness: 0.1) - .padding(.trailing, 5) - VStack { - HStack { - if channel.name?.isEmpty ?? false { - if channel.role == 1 { - Text(String("PrimaryChannel").camelCaseToWords()).font(.headline) - } else { - Text(String("Channel \(channel.index)").camelCaseToWords()).font(.headline) - } + NavigationLink(destination: ChannelMessageList(channel: channel)) { + + let mostRecent = channel.allPrivateMessages.last(where: { $0.channel == channel.index }) + 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 + VStack(alignment: .leading) { + HStack { + CircleText(text: String(channel.index), color: .accentColor, circleSize: 52, fontSize: 40, brightness: 0.1) + .padding(.trailing, 5) + VStack { + HStack { + if channel.name?.isEmpty ?? false { + if channel.role == 1 { + Text(String("PrimaryChannel").camelCaseToWords()).font(.headline) } else { - Text(String(channel.name ?? "Channel \(channel.index)").camelCaseToWords()).font(.headline) - } - Spacer() - if channel.allPrivateMessages.count > 0 { - VStack (alignment: .trailing) { - if lastMessageDay == currentDay { - Text(lastMessageTime, style: .time ) - .font(.subheadline) - } else if lastMessageDay == (currentDay - 1) { - Text("Yesterday") - .font(.subheadline) - } else if lastMessageDay < (currentDay - 1) && lastMessageDay > (currentDay - 5) { - Text(lastMessageTime.formattedDate(format: "MM/dd/yy")) - .font(.subheadline) - } else if lastMessageDay < (currentDay - 1800) { - Text(lastMessageTime.formattedDate(format: "MM/dd/yy")) - .font(.subheadline) - } - } - .brightness(-0.20) + Text(String("Channel \(channel.index)").camelCaseToWords()).font(.headline) } + } else { + Text(String(channel.name ?? "Channel \(channel.index)").camelCaseToWords()).font(.headline) } + Spacer() if channel.allPrivateMessages.count > 0 { - HStack(alignment: .top) { - Text("\(mostRecent != nil ? mostRecent!.messagePayload! : " ")") - .truncationMode(.tail) - .frame(maxWidth: .infinity, alignment: .leading) - .brightness(-0.20) - .font(.body) + VStack (alignment: .trailing) { + if lastMessageDay == currentDay { + Text(lastMessageTime, style: .time ) + .font(.subheadline) + } else if lastMessageDay == (currentDay - 1) { + Text("Yesterday") + .font(.subheadline) + } else if lastMessageDay < (currentDay - 1) && lastMessageDay > (currentDay - 5) { + Text(lastMessageTime.formattedDate(format: "MM/dd/yy")) + .font(.subheadline) + } else if lastMessageDay < (currentDay - 1800) { + Text(lastMessageTime.formattedDate(format: "MM/dd/yy")) + .font(.subheadline) + } } + .brightness(-0.20) + } + } + if channel.allPrivateMessages.count > 0 { + HStack(alignment: .top) { + Text("\(mostRecent != nil ? mostRecent!.messagePayload! : " ")") + .truncationMode(.tail) + .frame(maxWidth: .infinity, alignment: .leading) + .brightness(-0.20) + .font(.body) } } - .frame(maxWidth: .infinity, alignment: .leading) } + .frame(maxWidth: .infinity, alignment: .leading) } } } .frame(maxWidth: .infinity, maxHeight: 80, alignment: .leading) .contextMenu { -// Hide mute channel menu item until I can check it in notifications -// Button { -// channel.mute = !channel.mute -// -// do { -// try context.save() -// // Would rather not do this but the merge changes on -// // A single object is only working on mac GVH -// context.refreshAllObjects() -// //context.refresh(channel, mergeChanges: true) -// } catch { -// context.rollback() -// print("💥 Save Channel Mute Error") -// } -// } label: { -// Label(channel.mute ? "Show Alerts" : "Hide Alerts", systemImage: channel.mute ? "bell" : "bell.slash") -// } + Button { + channel.mute = !channel.mute + + do { + try context.save() + // Would rather not do this but the merge changes on + // A single object is only working on mac GVH + context.refreshAllObjects() + //context.refresh(channel, mergeChanges: true) + } catch { + context.rollback() + print("💥 Save Channel Mute Error") + } + } label: { + Label(channel.mute ? "Show Alerts" : "Hide Alerts", systemImage: channel.mute ? "bell" : "bell.slash") + } if channel.allPrivateMessages.count > 0 { Button(role: .destructive) { @@ -121,12 +118,13 @@ struct Contacts: View { .confirmationDialog( "This conversation will be deleted.", isPresented: $isPresentingDeleteChannelMessagesConfirm, + titleVisibility: .visible ) { Button(role: .destructive) { - deleteChannelMessages(channelIndex: channel.index, context: context) - context.refreshAllObjects() + deleteChannelMessages(channel: channel, context: context) + } label: { Text("delete") } diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index 2cb9b642..914f2f91 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -308,6 +308,21 @@ struct UserMessageList: View { .focused($focusedField, equals: .messageText) .multilineTextAlignment(.leading) .frame(minHeight: 50) + .keyboardShortcut(.defaultAction) + .onSubmit { + #if targetEnvironment(macCatalyst) + if bleManager.sendMessage(message: typingMessage, toUserNum: user.num, channel: 0, isEmoji: false, replyID: replyMessageId) { + typingMessage = "" + focusedField = nil + replyMessageId = 0 + if sendPositionWithMessage { + if bleManager.sendPosition(destNum: user.num, wantAck: true) { + print("Location Sent") + } + } + } + #endif + } Text(typingMessage).opacity(0).padding(.all, 0) } .overlay(RoundedRectangle(cornerRadius: 20).stroke(.tertiary, lineWidth: 1)) diff --git a/Meshtastic/Views/Settings/Channels.swift b/Meshtastic/Views/Settings/Channels.swift index cda0b2f1..48f16093 100644 --- a/Meshtastic/Views/Settings/Channels.swift +++ b/Meshtastic/Views/Settings/Channels.swift @@ -9,7 +9,7 @@ import CoreData func generateChannelKey(size: Int) -> String { var keyData = Data(count: size) - let result = keyData.withUnsafeMutableBytes { + _ = keyData.withUnsafeMutableBytes { SecRandomCopyBytes(kSecRandomDefault, size, $0.baseAddress!) } return keyData.base64EncodedString() @@ -25,73 +25,55 @@ struct Channels: View { var node: NodeInfoEntity? @State private var isPresentingEditView = false + @State private var selectedIndex: Int32 = -1 var body: some View { - ScrollView { - if node != nil && node?.myInfo != nil { - Grid() { - GridRow { - Text("Index") - .font(.caption2) - Text("name") - .font(.caption2) - if sizeCategory <= ContentSizeCategory.extraExtraLarge { - Text("Up/down link") - .font(.caption2) - } - Text("Edit") - .font(.caption2) - Text("Delete") - .font(.caption2) - } + NavigationStack { + List { + if node != nil && node?.myInfo != nil { ForEach(node!.myInfo!.channels?.array as! [ChannelEntity], id: \.self) { (channel: ChannelEntity) in - GridRow { - CircleText(text: String(channel.index), color: .accentColor, circleSize: 32) - Text(((channel.name!.isEmpty ? "Primary" : channel.name) ?? "Primary").camelCaseToWords()) - if sizeCategory <= ContentSizeCategory.extraExtraLarge { + Button(action: { + selectedIndex = channel.index + isPresentingEditView = true + print("Tapity tap") + }) { + VStack(alignment: .leading) { HStack { - if channel.uplinkEnabled { - Image(systemName: "checkmark.square") - } else { - Image(systemName: "square") - } - if channel.downlinkEnabled { - Image(systemName: "checkmark.square") - } else { - Image(systemName: "square") + CircleText(text: String(channel.index), color: .accentColor, circleSize: 45, fontSize: 36, brightness: 0.1) + .padding(.trailing, 5) + VStack { + HStack { + if channel.name?.isEmpty ?? false { + if channel.role == 1 { + Text(String("PrimaryChannel").camelCaseToWords()).font(.headline) + } else { + Text(String("Channel \(channel.index)").camelCaseToWords()).font(.headline) + } + } else { + Text(String(channel.name ?? "Channel \(channel.index)").camelCaseToWords()).font(.headline) + } + } } } } - Button { - print("Edit Channel") - - } label: { - Label("", systemImage: "square.and.pencil") - } - Button(role: .destructive) { - print("Delete Channel") - - } label: { - Label("", systemImage: "trash") - } - .disabled(channel.role == 1) } } } - if node!.myInfo!.channels?.array.count ?? 0 < 8 { + } + if node?.myInfo?.channels?.array.count ?? 0 < 8 { + + Button { + let key = generateChannelKey(size: 32) + print("Add Channel Key \(key) ") - Button { - print("Add Channel") - - } label: { - Label("Add Channel", systemImage: "plus.square") - } - .buttonStyle(.bordered) - .buttonBorderShape(.capsule) - .controlSize(.large) - .padding() + } label: { + Label("Add Channel", systemImage: "plus.square") } + .buttonStyle(.bordered) + .buttonBorderShape(.capsule) + .controlSize(.large) + .padding() } } .navigationTitle("Channels")