From 63bc7a5805b28f01ff54ff27f639aae642db656e Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 18 Jun 2025 17:51:09 -0700 Subject: [PATCH] Channels help --- Localizable.xcstrings | 12 +++ Meshtastic.xcodeproj/project.pbxproj | 4 + .../Views/Helpers/Help/ChannelsHelp.swift | 76 +++++++++++++++++++ Meshtastic/Views/Messages/ChannelList.swift | 26 ++++++- Meshtastic/Views/Settings/Channels.swift | 7 ++ 5 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 Meshtastic/Views/Helpers/Help/ChannelsHelp.swift diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 2676148d..0a5a5162 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -1683,6 +1683,12 @@ } } } + }, + "A channel index of 0 indicates the primary channel where all broadcast packets are sent from." : { + + }, + "A green lock means the channel is securely encrypted with either a 128 or 256 bit AES key." : { + }, "A Meshtastic QR code contains the LoRa config and channel values needed for radios to communicate. You can share a complete channel configuration using the Replace Channels option, if you choose Add Channels your shared channels will be added to the channels on the receiving radio." : { "localizations" : { @@ -1741,6 +1747,9 @@ } } } + }, + "A red lock with a slash means the channel is not securely encrypted, it uses either no key at all or a 1 byte known key. Traffic on this channel is easily intercepted." : { + }, "A Trace Route was sent, no response has been received." : { "localizations" : { @@ -6162,6 +6171,9 @@ } } } + }, + "Channels Help" : { + }, "Chart" : { "localizations" : { diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 745c3602..d583d3df 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -99,6 +99,7 @@ DD1BD0F32C63C65E008C0C70 /* SecurityConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1BD0F22C63C65E008C0C70 /* SecurityConfig.swift */; }; DD1BEF4A2E0292320090CE24 /* KeychainHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1BEF492E0292220090CE24 /* KeychainHelper.swift */; }; DD1BEF4C2E030D310090CE24 /* KeyBackupStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1BEF4B2E030D240090CE24 /* KeyBackupStatus.swift */; }; + DD1BEF4E2E03916A0090CE24 /* ChannelsHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1BEF4D2E0391620090CE24 /* ChannelsHelp.swift */; }; DD1BF2F92776FE2E008C8D2F /* UserMessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */; }; DD2160AF28C5552500C17253 /* MQTTConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2160AE28C5552500C17253 /* MQTTConfig.swift */; }; DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */; }; @@ -376,6 +377,7 @@ DD1BEF462DFF284C0090CE24 /* MeshtasticDataModelV 53.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 53.xcdatamodel"; sourceTree = ""; }; DD1BEF492E0292220090CE24 /* KeychainHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainHelper.swift; sourceTree = ""; }; DD1BEF4B2E030D240090CE24 /* KeyBackupStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupStatus.swift; sourceTree = ""; }; + DD1BEF4D2E0391620090CE24 /* ChannelsHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelsHelp.swift; sourceTree = ""; }; DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserMessageList.swift; sourceTree = ""; }; DD2160AE28C5552500C17253 /* MQTTConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MQTTConfig.swift; sourceTree = ""; }; DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralModel.swift; sourceTree = ""; }; @@ -850,6 +852,7 @@ DD6F65772C6EAB860053C113 /* Help */ = { isa = PBXGroup; children = ( + DD1BEF4D2E0391620090CE24 /* ChannelsHelp.swift */, DD6F65752C6EA5490053C113 /* AckErrors.swift */, DD6F65782C6EADE60053C113 /* DirectMessagesHelp.swift */, DD6F657A2C6EC2900053C113 /* LockLegend.swift */, @@ -1428,6 +1431,7 @@ D9C9839D2B79CFD700BDBE6A /* TextMessageSize.swift in Sources */, DDC94FCE29CF55310082EA6E /* RtttlConfig.swift in Sources */, DD964FBD296E6B01007C176F /* EmojiOnlyTextField.swift in Sources */, + DD1BEF4E2E03916A0090CE24 /* ChannelsHelp.swift in Sources */, DD8169FF272476C700F4AB02 /* LogDocument.swift in Sources */, BCD93CBA2D9E11A2006C9214 /* DisconnectNodeIntent.swift in Sources */, DDC94FC129CE063B0082EA6E /* BatteryLevel.swift in Sources */, diff --git a/Meshtastic/Views/Helpers/Help/ChannelsHelp.swift b/Meshtastic/Views/Helpers/Help/ChannelsHelp.swift new file mode 100644 index 00000000..830fe3cd --- /dev/null +++ b/Meshtastic/Views/Helpers/Help/ChannelsHelp.swift @@ -0,0 +1,76 @@ +// +// ChannelHelp.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 6/18/25. +// + +import SwiftUI + +struct ChannelsHelp: View { + private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom } + @Environment(\.dismiss) private var dismiss + + var body: some View { + ScrollView { + Label("Channels Help", systemImage: "questionmark.circle") + .font(.title) + .padding(.vertical) + VStack(alignment: .leading) { + HStack { + CircleText(text: String(0), color: .accentColor) + .brightness(0.2) + .offset(y: -10) + Text("A channel index of 0 indicates the primary channel where all broadcast packets are sent from.") + .fixedSize(horizontal: false, vertical: true) + .padding(.bottom) + } + HStack { + Image(systemName: "lock.fill") + .padding(.bottom) + .foregroundColor(Color.green) + .font(.largeTitle) + Text("A green lock means the channel is securely encrypted with either a 128 or 256 bit AES key.") + .fixedSize(horizontal: false, vertical: true) + .padding(.bottom) + } + HStack { + Image(systemName: "lock.slash.fill") + .padding(.bottom) + .foregroundColor(Color.red) + .font(.largeTitle) + Text("A red lock with a slash means the channel is not securely encrypted, it uses either no key at all or a 1 byte known key. Traffic on this channel is easily intercepted.") + .fixedSize(horizontal: false, vertical: true) + .padding(.bottom) + } + } + +#if targetEnvironment(macCatalyst) + Spacer() + Button { + dismiss() + } label: { + Label("Close", systemImage: "xmark") + } + .buttonStyle(.bordered) + .buttonBorderShape(.capsule) + .controlSize(.large) + .padding(.bottom) +#endif + } + .frame(minHeight: 0, maxHeight: .infinity, alignment: .leading) + .padding() + .presentationDetents([.large]) + .presentationContentInteraction(.scrolls) + .presentationDragIndicator(.visible) + .presentationBackgroundInteraction(.enabled(upThrough: .large)) + } +} + +struct ChannelHelpPreviews: PreviewProvider { + static var previews: some View { + VStack { + ChannelsHelp() + } + } +} diff --git a/Meshtastic/Views/Messages/ChannelList.swift b/Meshtastic/Views/Messages/ChannelList.swift index fec3ab8b..e97dbb47 100644 --- a/Meshtastic/Views/Messages/ChannelList.swift +++ b/Meshtastic/Views/Messages/ChannelList.swift @@ -21,8 +21,8 @@ struct ChannelList: View { var channelSelection: ChannelEntity? @State private var isPresentingDeleteChannelMessagesConfirm: Bool = false - @State private var isPresentingTraceRouteSentAlert = false + @State private var showingHelp = false var restrictedChannels = ["gpio", "mqtt", "serial", "admin"] @@ -168,8 +168,30 @@ struct ChannelList: View { } .padding([.top, .bottom]) .listStyle(.plain) + .navigationTitle("Channels") } } - .navigationTitle("Channels") + .sheet(isPresented: $showingHelp) { + ChannelsHelp() + .presentationDetents([.medium]) + } + .safeAreaInset(edge: .bottom, alignment: .leading) { + HStack { + Button(action: { + withAnimation { + showingHelp = !showingHelp + } + }) { + Image(systemName: !showingHelp ? "questionmark.circle" : "questionmark.circle.fill") + .padding(.vertical, 5) + } + .tint(Color(UIColor.secondarySystemBackground)) + .foregroundColor(.accentColor) + .buttonStyle(.borderedProminent) + } + .controlSize(.regular) + .padding(5) + } + .padding(.bottom, 5) } } diff --git a/Meshtastic/Views/Settings/Channels.swift b/Meshtastic/Views/Settings/Channels.swift index 0a24c8c5..d479403f 100644 --- a/Meshtastic/Views/Settings/Channels.swift +++ b/Meshtastic/Views/Settings/Channels.swift @@ -124,6 +124,13 @@ struct Channels: View { .brightness(0.1) VStack { HStack { + if channel.psk?.hexDescription.count ?? 0 < 3 { + Image(systemName: "lock.slash.fill") + .foregroundColor(.red) + } else { + Image(systemName: "lock.fill") + .foregroundColor(.green) + } if channel.name?.isEmpty ?? false { if channel.role == 1 { Text(String("PrimaryChannel").camelCaseToWords()).font(.headline)