mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
More descriptive manual connection rows
This commit is contained in:
parent
486b4c1821
commit
5fb351b2f1
8 changed files with 142 additions and 23 deletions
|
|
@ -18921,6 +18921,13 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Last seen device:" : {
|
||||
"comment" : "A label displayed next to the last seen device text in the `DeviceConnectRow`.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Last seen device: %@" : {
|
||||
|
||||
},
|
||||
"Latitude" : {
|
||||
"localizations" : {
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
2344A2B12D68DFF800170A77 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25C49D8F2C471AEA0024FBD1 /* Constants.swift */; };
|
||||
2346A7192E2FB9A300CB9239 /* SerialConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2346A7182E2FB9A300CB9239 /* SerialConnection.swift */; };
|
||||
2346A71D2E2FB9C500CB9239 /* SerialTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2346A71C2E2FB9C500CB9239 /* SerialTransport.swift */; };
|
||||
2349A04A2EAE4DA30060A581 /* ManualConnectionList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2349A0492EAE4DA30060A581 /* ManualConnectionList.swift */; };
|
||||
2373AE132D0A216C0086C749 /* MetricsChartSeries.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2373AE122D0A216C0086C749 /* MetricsChartSeries.swift */; };
|
||||
2373AE152D0A24930086C749 /* MetricsSeriesList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2373AE142D0A24930086C749 /* MetricsSeriesList.swift */; };
|
||||
2373AE172D0A26620086C749 /* EnvironmentDefaultSeries.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2373AE162D0A26620086C749 /* EnvironmentDefaultSeries.swift */; };
|
||||
|
|
@ -357,6 +358,7 @@
|
|||
2344A2AE2D6697A700170A77 /* TelemetryEntity+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TelemetryEntity+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
2346A7182E2FB9A300CB9239 /* SerialConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConnection.swift; sourceTree = "<group>"; };
|
||||
2346A71C2E2FB9C500CB9239 /* SerialTransport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialTransport.swift; sourceTree = "<group>"; };
|
||||
2349A0492EAE4DA30060A581 /* ManualConnectionList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualConnectionList.swift; sourceTree = "<group>"; };
|
||||
2373AE122D0A216C0086C749 /* MetricsChartSeries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricsChartSeries.swift; sourceTree = "<group>"; };
|
||||
2373AE142D0A24930086C749 /* MetricsSeriesList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricsSeriesList.swift; sourceTree = "<group>"; };
|
||||
2373AE162D0A26620086C749 /* EnvironmentDefaultSeries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentDefaultSeries.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -832,6 +834,7 @@
|
|||
23D316922E5618D2002FA4FB /* AsyncGate.swift */,
|
||||
23E23F912E392C2B00919073 /* LogRecord+StringRepresentation.swift */,
|
||||
23D9D9382E50DA97005D1C18 /* ResettableTimer.swift */,
|
||||
2349A0492EAE4DA30060A581 /* ManualConnectionList.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1841,6 +1844,7 @@
|
|||
BCDDFA9A2DBB180D0065189C /* ScrollToBottomButton.swift in Sources */,
|
||||
DDC4C9FF2A8D982900CE201C /* DetectionSensorConfig.swift in Sources */,
|
||||
2344A2AF2D6697A700170A77 /* TelemetryEntity+CoreDataClass.swift in Sources */,
|
||||
2349A04A2EAE4DA30060A581 /* ManualConnectionList.swift in Sources */,
|
||||
2344A2B02D6697A700170A77 /* TelemetryEntity+CoreDataProperties.swift in Sources */,
|
||||
D9C983A22B79D1A600BDBE6A /* RequestPositionButton.swift in Sources */,
|
||||
237AEB8F2E1FE457003B7CE3 /* Transport.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -175,8 +175,12 @@ extension AccessoryManager {
|
|||
self.updateState(.subscribed)
|
||||
|
||||
// If we successfully connected to a manual connection, then save it to the list
|
||||
if device.isManualConnection {
|
||||
self.saveManualConnection(device: device)
|
||||
// Remember, Device is a value type (struct) so don't use use `device` here, thats
|
||||
// The value at the instantiation of the connect process. We want the currently
|
||||
// updated device object in `activeConnection` with its additonal metadata from
|
||||
// NodeInfo packets.
|
||||
if let activeDevice = self.activeConnection?.device, activeDevice.isManualConnection {
|
||||
ManualConnectionList.shared.insert(device: activeDevice)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -224,15 +228,6 @@ extension AccessoryManager {
|
|||
// All done, one way or another, clean up
|
||||
self.connectionStepper = nil
|
||||
}
|
||||
|
||||
fileprivate func saveManualConnection(device: Device) {
|
||||
var manualConnections = UserDefaults.manualConnections
|
||||
|
||||
if manualConnections.first(where: {$0.id == device.id}) == nil {
|
||||
manualConnections.append(device)
|
||||
UserDefaults.manualConnections = manualConnections
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sequentially stepped tasks
|
||||
|
|
|
|||
|
|
@ -113,6 +113,15 @@ extension AccessoryManager {
|
|||
updateDevice(deviceId: activeDevice.id, key: \.shortName, value: user.shortName ?? "?")
|
||||
updateDevice(deviceId: activeDevice.id, key: \.longName, value: user.longName ?? "Unknown".localized)
|
||||
updateDevice(deviceId: activeDevice.id, key: \.hardwareModel, value: user.hwModel)
|
||||
|
||||
if activeDevice.isManualConnection {
|
||||
// We just received a NodeInfo for the currently connected node and this is a
|
||||
// manual connection. Update the metadata for the device entry in UserDefaults
|
||||
// with this information for better display later
|
||||
ManualConnectionList.shared.updateDevice(deviceId: activeDevice.id, key: \.shortName, value: user.shortName)
|
||||
ManualConnectionList.shared.updateDevice(deviceId: activeDevice.id, key: \.longName, value: user.longName)
|
||||
ManualConnectionList.shared.updateDevice(deviceId: activeDevice.id, key: \.hardwareModel, value: user.hwModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -302,9 +302,9 @@ class AccessoryManager: ObservableObject, MqttClientProxyManagerDelegate {
|
|||
Logger.transport.error("updateDevice<T> with nil deviceId")
|
||||
return
|
||||
}
|
||||
|
||||
// Update the active device
|
||||
if let activeConnection {
|
||||
|
||||
// Update the active device if the UUID's match
|
||||
if let activeConnection, activeConnection.device.id == deviceId {
|
||||
var device = activeConnection.device
|
||||
if device[keyPath: key] != value {
|
||||
// Update the @Published stuff for the UI
|
||||
|
|
|
|||
61
Meshtastic/Accessory/Helpers/ManualConnectionList.swift
Normal file
61
Meshtastic/Accessory/Helpers/ManualConnectionList.swift
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// ManualConnectionList.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Created by jake on 10/26/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// Maintains an observable list of devices that's backed by UserDefaults
|
||||
public class ManualConnectionList: ObservableObject {
|
||||
static let shared = ManualConnectionList()
|
||||
|
||||
@Published private var _list: [Device]
|
||||
|
||||
private init() {
|
||||
_list = UserDefaults.manualConnections
|
||||
}
|
||||
|
||||
var connectionsList: [Device] {
|
||||
get {
|
||||
return _list
|
||||
}
|
||||
}
|
||||
|
||||
func insert(device: Device) {
|
||||
// Don't insert if already there
|
||||
guard !_list.contains(where: {$0.id == device.id}) else {
|
||||
return
|
||||
}
|
||||
|
||||
// Add the new entry
|
||||
var list = _list
|
||||
list.append(device)
|
||||
_list = list
|
||||
UserDefaults.manualConnections = list
|
||||
}
|
||||
|
||||
func updateDevice<T>(deviceId: UUID, key: WritableKeyPath<Device, T>, value: T) where T: Equatable {
|
||||
var list = _list
|
||||
if let deviceIndex = list.firstIndex(where: {$0.id == deviceId}) {
|
||||
list[deviceIndex][keyPath: key] = value
|
||||
_list = list
|
||||
UserDefaults.manualConnections = list
|
||||
}
|
||||
}
|
||||
|
||||
func remove(device: Device) {
|
||||
var list = _list
|
||||
list.removeAll(where: {$0.id == device.id})
|
||||
_list = list
|
||||
UserDefaults.manualConnections = list
|
||||
}
|
||||
|
||||
func remove(atOffsets: IndexSet) {
|
||||
var list = _list
|
||||
list.remove(atOffsets: atOffsets)
|
||||
_list = list
|
||||
UserDefaults.manualConnections = list
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,8 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
struct Device: Identifiable, Hashable, Codable {
|
||||
struct Device: Identifiable, Hashable, Codable, CustomStringConvertible {
|
||||
|
||||
let id: UUID
|
||||
var name: String
|
||||
var transportType: TransportType
|
||||
|
|
@ -56,4 +57,16 @@ struct Device: Identifiable, Hashable, Codable {
|
|||
}
|
||||
}
|
||||
|
||||
var description: String {
|
||||
switch (shortName, longName) {
|
||||
case (let shortName?, let longName?): // Both shortName and longName are non-nil
|
||||
return "\(longName) (\(shortName))"
|
||||
case (let shortName?, nil): // shortName is non-nil, longName is nil
|
||||
return "\(shortName)"
|
||||
case (nil, let longName?): // shortName is nil, longName is non-nil
|
||||
return "\(longName)"
|
||||
default: // Both are nil
|
||||
return "Device(id: \(id))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ struct Connect: View {
|
|||
@State var isUnsetRegion = false
|
||||
@State var invalidFirmwareVersion = false
|
||||
@State var liveActivityStarted = false
|
||||
@ObservedObject var manualConnections = ManualConnectionList.shared
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
|
|
@ -272,15 +273,23 @@ struct Connect: View {
|
|||
DeviceConnectRow(device: device)
|
||||
}
|
||||
}
|
||||
if UserDefaults.manualConnections.count > 0 {
|
||||
if manualConnections.connectionsList.count > 0 {
|
||||
Section(header: Text("Manual Connections").font(.title)) {
|
||||
ForEach(UserDefaults.manualConnections) { device in
|
||||
ForEach(manualConnections.connectionsList) { device in
|
||||
DeviceConnectRow(device: device)
|
||||
#if targetEnvironment(macCatalyst)
|
||||
.contextMenu {
|
||||
Button {
|
||||
manualConnections.remove(device: device)
|
||||
} label: {
|
||||
Label("Delete", systemImage: "trash")
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}.onDelete { offsets in
|
||||
var list = UserDefaults.manualConnections
|
||||
list.remove(atOffsets: offsets)
|
||||
UserDefaults.manualConnections = list
|
||||
manualConnections.remove(atOffsets: offsets)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -526,7 +535,7 @@ struct DeviceConnectRow: View {
|
|||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var accessoryManager: AccessoryManager
|
||||
@State var presentingSwitchPreferredPeripheral = false
|
||||
var device: Device
|
||||
let device: Device
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
|
|
@ -555,11 +564,31 @@ struct DeviceConnectRow: View {
|
|||
Text(device.name).font(.callout)
|
||||
}
|
||||
// Show transport type
|
||||
TransportIcon(transportType: device.transportType)
|
||||
#if !targetEnvironment(macCatalyst)
|
||||
HStack(alignment: .center){
|
||||
TransportIcon(transportType: device.transportType)
|
||||
if device.isManualConnection && (device.longName != nil || device.shortName != nil) {
|
||||
VStack (alignment: .leading) {
|
||||
Text("Last seen device:")
|
||||
Text("\(String(describing: device))")
|
||||
}
|
||||
}
|
||||
}.padding(.top, 3.0)
|
||||
#else
|
||||
//Different alignment for Mac
|
||||
HStack(alignment: .firstTextBaseline){
|
||||
TransportIcon(transportType: device.transportType)
|
||||
if device.isManualConnection && (device.longName != nil || device.shortName != nil) {
|
||||
Text("Last seen device: \(String(describing: device))")
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Spacer()
|
||||
VStack {
|
||||
device.getSignalStrength().map { SignalStrengthIndicator(signalStrength: $0) }
|
||||
device.getSignalStrength().map {
|
||||
SignalStrengthIndicator(signalStrength: $0)
|
||||
}
|
||||
}
|
||||
}.padding([.bottom, .top])
|
||||
.confirmationDialog("Connecting to a new radio will clear all app data on the phone.", isPresented: $presentingSwitchPreferredPeripheral, titleVisibility: .visible) {
|
||||
|
|
@ -578,3 +607,4 @@ struct DeviceConnectRow: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue