diff --git a/Meshtastic/Enums/LoraConfigEnums.swift b/Meshtastic/Enums/LoraConfigEnums.swift index acc72397f..e1383f958 100644 --- a/Meshtastic/Enums/LoraConfigEnums.swift +++ b/Meshtastic/Enums/LoraConfigEnums.swift @@ -525,12 +525,24 @@ enum ModemPresets: Int, CaseIterable, Identifiable { } } + /// Presets deprecated upstream that must no longer be offered for new selection, + /// mirroring how Android filters them out. They remain as cases so a radio already + /// configured on one round-trips through protobuf and renders the correct label. + var isDeprecated: Bool { + switch self { + case .longSlow: + return true + default: + return false + } + } + /// Presets selectable for a connected device, given whether its firmware /// implements the 2.8 rework. On older firmware the 2.8-only presets are /// dropped. Callers should additionally constrain this to the selected /// region's legal set via `RegionPresetInfo` when the firmware provides one. static func selectable(supports2_8: Bool) -> [ModemPresets] { - allCases.filter { supports2_8 || !$0.requiresFirmware2_8 } + allCases.filter { !$0.isDeprecated && (supports2_8 || !$0.requiresFirmware2_8) } } /// The conservative (pre-2.8) selectable set. Retained for callers that have diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index 56e16ba45..9d532dada 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -76,11 +76,17 @@ struct LoRaConfig: View { /// one. Never empty (spec §6 — never show an empty picker). private var availablePresets: [ModemPresets] { let base = ModemPresets.selectable(supports2_8: supports2_8) + var presets = base if let info = regionPresetInfo, !info.presets.isEmpty { let constrained = base.filter { info.presets.contains($0.protoEnumValue()) } - if !constrained.isEmpty { return constrained } + if !constrained.isEmpty { presets = constrained } } - return base + // Keep a currently-configured but deprecated preset (e.g. Long Slow on an existing + // radio) visible so the picker doesn't render a blank selection. + if let current = ModemPresets(rawValue: modemPreset), current.isDeprecated, !presets.contains(current) { + presets.append(current) + } + return presets } var body: some View { diff --git a/MeshtasticTests/LoraDeviceEnumTests.swift b/MeshtasticTests/LoraDeviceEnumTests.swift index 1eadb13b9..bb88b5bd9 100644 --- a/MeshtasticTests/LoraDeviceEnumTests.swift +++ b/MeshtasticTests/LoraDeviceEnumTests.swift @@ -636,6 +636,20 @@ struct LoRaFirmwareGatingTests { #expect(selectable.contains(.tinyFast)) #expect(selectable.contains(.liteSlow)) } + + @Test func deprecatedPreset_excludedFromSelectable() { + #expect(ModemPresets.longSlow.isDeprecated) + #expect(!ModemPresets.selectable(supports2_8: false).contains(.longSlow)) + #expect(!ModemPresets.selectable(supports2_8: true).contains(.longSlow)) + #expect(!ModemPresets.userSelectable.contains(.longSlow)) + } + + @Test func deprecatedPreset_stillExistsForDisplay() { + // longSlow remains a case so an existing radio's value round-trips and renders its label. + let preset = ModemPresets(rawValue: 1) + #expect(preset == .longSlow) + #expect(preset?.name == "LongSlow") + } } // MARK: - LoRaRegionPresetMap decoding (2.8 region→preset compatibility)