diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 5049c74e..a0f67809 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -532,7 +532,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate connectedPeripheral.name = myInfo?.bleName ?? "unknown".localized connectedPeripheral.longName = myInfo?.bleName ?? "unknown".localized } - tryClearExistingChannels() + //tryClearExistingChannels() } // NodeInfo if decodedInfo.nodeInfo.num > 0 {// && !invalidVersion { diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index dc1e16b6..f8cee778 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -154,7 +154,10 @@ func channelPacket (channel: Channel, fromNum: Int64, context: NSManagedObjectCo return } if mutableChannels.contains(newChannel) { - mutableChannels.replaceObject(at: Int(newChannel.index), with: newChannel) + let oldChannel = mutableChannels.first(where: {($0 as AnyObject).index == newChannel.index }) as! ChannelEntity + newChannel.mute = oldChannel.mute + let index = mutableChannels.index(of: oldChannel as Any) + mutableChannels.replaceObject(at: index, with: newChannel) } else { mutableChannels.add(newChannel) } diff --git a/Meshtastic/Views/Settings/Channels.swift b/Meshtastic/Views/Settings/Channels.swift index 1ff1d65f..40657517 100644 --- a/Meshtastic/Views/Settings/Channels.swift +++ b/Meshtastic/Views/Settings/Channels.swift @@ -67,8 +67,8 @@ struct Channels: View { channelName = channel.name ?? "" uplink = channel.uplinkEnabled downlink = channel.downlinkEnabled - isPresentingEditView = true hasChanges = false + isPresentingEditView = true }) { VStack(alignment: .leading) { HStack { @@ -94,6 +94,238 @@ struct Channels: View { } } } + .sheet(isPresented: $isPresentingEditView) { + #if targetEnvironment(macCatalyst) + Text("channel") + .font(.largeTitle) + .padding() + #endif + Form { + HStack { + Text("name") + Spacer() + TextField( + "Channel Name", + text: $channelName + ) + .disableAutocorrection(true) + .keyboardType(.alphabet) + .foregroundColor(Color.gray) + .onChange(of: channelName, perform: { _ in + channelName = channelName.replacing(" ", with: "") + let totalBytes = channelName.utf8.count + // Only mess with the value if it is too big + if totalBytes > 11 { + let firstNBytes = Data(channelName.utf8.prefix(11)) + if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) { + // Set the channelName back to the last place where it was the right size + channelName = maxBytesString + } + } + hasChanges = true + }) + } + HStack { + Picker("Key Size", selection: $channelKeySize) { + Text("Empty").tag(0) + Text("Default").tag(-1) + Text("1 byte").tag(1) + Text("128 bit").tag(16) + Text("192 bit").tag(24) + Text("256 bit").tag(32) + } + .pickerStyle(DefaultPickerStyle()) + Spacer() + Button { + if channelKeySize == -1 { + channelKey = "AQ==" + } else { + let key = generateChannelKey(size: channelKeySize) + channelKey = key + } + } label: { + Image(systemName: "lock.rotation") + .font(.title) + } + .buttonStyle(.bordered) + .buttonBorderShape(.capsule) + .controlSize(.small) + } + HStack(alignment: .center) { + Text("Key") + Spacer() + TextField( + "Key", + text: $channelKey, + axis: .vertical + ) + .padding(6) + .disableAutocorrection(true) + .keyboardType(.alphabet) + .foregroundColor(Color.gray) + .textSelection(.enabled) + .background( + RoundedRectangle(cornerRadius: 10.0) + .stroke( + hasValidKey ? + Color.clear : + Color.red + , lineWidth: 2.0) + + ) + .onChange(of: channelKey, perform: { _ in + let tempKey = Data(base64Encoded: channelKey) ?? Data() + if tempKey.count == channelKeySize || channelKeySize == -1{ + hasValidKey = true + } + else { + hasValidKey = false + } + hasChanges = true + }) + .disabled(channelKeySize <= 0) + } + HStack { + if channelRole == 1 { + Picker("Channel Role", selection: $channelRole) { + Text("Primary").tag(1) + } + .pickerStyle(.automatic) + .disabled(true) + } else { + Text("Channel Role") + Spacer() + Picker("Channel Role", selection: $channelRole) { + Text("Disabled").tag(0) + Text("Secondary").tag(2) + } + .pickerStyle(.segmented) + } + } + Toggle("Uplink Enabled", isOn: $uplink) + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Toggle("Downlink Enabled", isOn: $downlink) + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + } + .onAppear { + let tempKey = Data(base64Encoded: channelKey) ?? Data() + if tempKey.count == channelKeySize || channelKeySize == -1{ + hasValidKey = true + } + else { + hasValidKey = false + } + } + .onChange(of: channelName) { _ in + hasChanges = true + } + .onChange(of: channelKeySize) { _ in + if channelKeySize == -1 { + channelKey = "AQ==" + } else { + let key = generateChannelKey(size: channelKeySize) + channelKey = key + } + hasChanges = true + } + .onChange(of: channelKey) { _ in + hasChanges = true + } + .onChange(of: channelRole) { _ in + hasChanges = true + } + .onChange(of: uplink) { _ in + hasChanges = true + } + .onChange(of: downlink) { _ in + hasChanges = true + } + HStack { + Button { + var channel = Channel() + channel.index = channelIndex + channel.role = ChannelRoles(rawValue: channelRole)?.protoEnumValue() ?? .secondary + if channel.role != Channel.Role.disabled { + channel.index = channelIndex + channel.settings.name = channelName + channel.settings.psk = Data(base64Encoded: channelKey) ?? Data() + channel.settings.uplinkEnabled = uplink + channel.settings.downlinkEnabled = downlink + + 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 + newChannel.name = channel.settings.name + newChannel.role = Int32(channel.role.rawValue) + newChannel.psk = channel.settings.psk + guard let mutableChannels = node?.myInfo?.channels?.mutableCopy() as? NSMutableOrderedSet else { + return + } + if mutableChannels.contains(newChannel) { + mutableChannels.replaceObject(at: Int(newChannel.index), with: newChannel) + } else { + mutableChannels.add(newChannel) + } + node!.myInfo!.channels = mutableChannels.copy() as? NSOrderedSet + context.refresh(newChannel, mergeChanges: true) + do { + try context.save() + print("💾 Saved Channel: \(channel.settings.name)") + } catch { + context.rollback() + let nsError = error as NSError + print("💥 Unresolved Core Data error in the channel editor. Error: \(nsError)") + } + } else { + if channelIndex <= node!.myInfo!.channels?.count ?? 0 { + guard let channelEntity = node!.myInfo!.channels?[Int(channelIndex)] as? ChannelEntity else { + return + } + context.delete(channelEntity) + do { + try context.save() + print("💾 Deleted Channel: \(channel.settings.name)") + } catch { + context.rollback() + let nsError = error as NSError + print("💥 Unresolved Core Data error in the channel editor. Error: \(nsError)") + } + } + } + + let adminMessageId = bleManager.saveChannel(channel: channel, fromUser: node!.user!, toUser: node!.user!) + + if adminMessageId > 0 { + self.isPresentingEditView = false + channelName = "" + channelRole = 2 + hasChanges = false + _ = bleManager.getChannel(channel: channel, fromUser: node!.user!, toUser: node!.user!) + } + } label: { + Label("save", systemImage: "square.and.arrow.down") + } + .disabled(bleManager.connectedPeripheral == nil || !hasChanges || !hasValidKey) + .buttonStyle(.bordered) + .buttonBorderShape(.capsule) + .controlSize(.large) + .padding(.bottom) + #if targetEnvironment(macCatalyst) + Button { + isPresentingEditView = false + } label: { + Label("close", systemImage: "xmark") + } + .buttonStyle(.bordered) + .buttonBorderShape(.capsule) + .controlSize(.large) + .padding(.bottom) + #endif + } + .presentationDetents([.medium, .large]) + } if node?.myInfo?.channels?.array.count ?? 0 < 8 && node != nil { Button { @@ -121,239 +353,6 @@ struct Channels: View { .buttonBorderShape(.capsule) .controlSize(.large) .padding() - .sheet(isPresented: $isPresentingEditView) { - - #if targetEnvironment(macCatalyst) - Text("channel") - .font(.largeTitle) - .padding() - #endif - Form { - HStack { - Text("name") - Spacer() - TextField( - "Channel Name", - text: $channelName - ) - .disableAutocorrection(true) - .keyboardType(.alphabet) - .foregroundColor(Color.gray) - .onChange(of: channelName, perform: { _ in - channelName = channelName.replacing(" ", with: "") - let totalBytes = channelName.utf8.count - // Only mess with the value if it is too big - if totalBytes > 11 { - let firstNBytes = Data(channelName.utf8.prefix(11)) - if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) { - // Set the channelName back to the last place where it was the right size - channelName = maxBytesString - } - } - hasChanges = true - }) - } - HStack { - Picker("Key Size", selection: $channelKeySize) { - Text("Empty").tag(0) - Text("Default").tag(-1) - Text("1 byte").tag(1) - Text("128 bit").tag(16) - Text("192 bit").tag(24) - Text("256 bit").tag(32) - } - .pickerStyle(DefaultPickerStyle()) - Spacer() - Button { - if channelKeySize == -1 { - channelKey = "AQ==" - } else { - let key = generateChannelKey(size: channelKeySize) - channelKey = key - } - } label: { - Image(systemName: "lock.rotation") - .font(.title) - } - .buttonStyle(.bordered) - .buttonBorderShape(.capsule) - .controlSize(.small) - } - HStack(alignment: .center) { - Text("Key") - Spacer() - TextField( - "Key", - text: $channelKey, - axis: .vertical - ) - .padding(6) - .disableAutocorrection(true) - .keyboardType(.alphabet) - .foregroundColor(Color.gray) - .textSelection(.enabled) - .background( - RoundedRectangle(cornerRadius: 10.0) - .stroke( - hasValidKey ? - Color.clear : - Color.red - , lineWidth: 2.0) - - ) - .onChange(of: channelKey, perform: { _ in - let tempKey = Data(base64Encoded: channelKey) ?? Data() - if tempKey.count == channelKeySize || channelKeySize == -1{ - hasValidKey = true - } - else { - hasValidKey = false - } - hasChanges = true - }) - .disabled(channelKeySize <= 0) - } - HStack { - if channelRole == 1 { - Picker("Channel Role", selection: $channelRole) { - Text("Primary").tag(1) - } - .pickerStyle(.automatic) - .disabled(true) - } else { - Text("Channel Role") - Spacer() - Picker("Channel Role", selection: $channelRole) { - Text("Disabled").tag(0) - Text("Secondary").tag(2) - } - .pickerStyle(.segmented) - } - } - Toggle("Uplink Enabled", isOn: $uplink) - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Toggle("Downlink Enabled", isOn: $downlink) - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - } - .onAppear { - let tempKey = Data(base64Encoded: channelKey) ?? Data() - if tempKey.count == channelKeySize || channelKeySize == -1{ - hasValidKey = true - } - else { - hasValidKey = false - } - } - .onChange(of: channelName) { _ in - hasChanges = true - } - .onChange(of: channelKeySize) { _ in - if channelKeySize == -1 { - channelKey = "AQ==" - } else { - let key = generateChannelKey(size: channelKeySize) - channelKey = key - } - hasChanges = true - } - .onChange(of: channelKey) { _ in - hasChanges = true - } - .onChange(of: channelRole) { _ in - hasChanges = true - } - .onChange(of: uplink) { _ in - hasChanges = true - } - .onChange(of: downlink) { _ in - hasChanges = true - } - HStack { - Button { - var channel = Channel() - channel.index = channelIndex - channel.role = ChannelRoles(rawValue: channelRole)?.protoEnumValue() ?? .secondary - if channel.role != Channel.Role.disabled { - channel.index = channelIndex - channel.settings.name = channelName - channel.settings.psk = Data(base64Encoded: channelKey) ?? Data() - channel.settings.uplinkEnabled = uplink - channel.settings.downlinkEnabled = downlink - - 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 - newChannel.name = channel.settings.name - newChannel.role = Int32(channel.role.rawValue) - newChannel.psk = channel.settings.psk - guard let mutableChannels = node?.myInfo?.channels?.mutableCopy() as? NSMutableOrderedSet else { - return - } - if mutableChannels.contains(newChannel) { - mutableChannels.replaceObject(at: Int(newChannel.index), with: newChannel) - } else { - mutableChannels.add(newChannel) - } - node!.myInfo!.channels = mutableChannels.copy() as? NSOrderedSet - context.refresh(newChannel, mergeChanges: true) - do { - try context.save() - print("💾 Saved Channel: \(channel.settings.name)") - } catch { - context.rollback() - let nsError = error as NSError - print("💥 Unresolved Core Data error in the channel editor. Error: \(nsError)") - } - } else { - if channelIndex <= node!.myInfo!.channels?.count ?? 0 { - guard let channelEntity = node!.myInfo!.channels?[Int(channelIndex)] as? ChannelEntity else { - return - } - context.delete(channelEntity) - do { - try context.save() - print("💾 Deleted Channel: \(channel.settings.name)") - } catch { - context.rollback() - let nsError = error as NSError - print("💥 Unresolved Core Data error in the channel editor. Error: \(nsError)") - } - } - } - - let adminMessageId = bleManager.saveChannel(channel: channel, fromUser: node!.user!, toUser: node!.user!) - - if adminMessageId > 0 { - self.isPresentingEditView = false - channelName = "" - channelRole = 2 - hasChanges = false - _ = bleManager.getChannel(channel: channel, fromUser: node!.user!, toUser: node!.user!) - } - } label: { - Label("save", systemImage: "square.and.arrow.down") - } - .disabled(bleManager.connectedPeripheral == nil || !hasChanges || !hasValidKey) - .buttonStyle(.bordered) - .buttonBorderShape(.capsule) - .controlSize(.large) - .padding(.bottom) - #if targetEnvironment(macCatalyst) - Button { - isPresentingEditView = false - } label: { - Label("close", systemImage: "xmark") - } - .buttonStyle(.bordered) - .buttonBorderShape(.capsule) - .controlSize(.large) - .padding(.bottom) - #endif - } - .presentationDetents([.medium, .large]) - } } } .navigationTitle("channels")