diff --git a/Meshtastic/Export/WriteCsvFile.swift b/Meshtastic/Export/WriteCsvFile.swift index c246fa40..bdd516a9 100644 --- a/Meshtastic/Export/WriteCsvFile.swift +++ b/Meshtastic/Export/WriteCsvFile.swift @@ -96,3 +96,27 @@ func positionToCsvFile(positions: [PositionEntity]) -> String { } return csvString } + + +func routeToCsvFile(locations: [LocationEntity]) -> String { + var csvString: String = "" + let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmma", options: 0, locale: Locale.current) + let dateFormatString = (localeDateFormat ?? "MM/dd/YY j:mma").replacingOccurrences(of: ",", with: "") + // Create Position Header + csvString = "Id, Latitude, Longitude, Altitude, Speed, Heading" + for loc in locations { + csvString += "\n" + csvString += String(loc.id) + csvString += ", " + csvString += String((loc.latitude ?? 0)) + csvString += ", " + csvString += String(loc.longitude ?? 0) + csvString += ", " + csvString += String(loc.altitude) + csvString += ", " + csvString += String(loc.speed) + csvString += ", " + csvString += String(loc.heading) + } + return csvString +} diff --git a/Meshtastic/Views/Settings/Routes.swift b/Meshtastic/Views/Settings/Routes.swift index 44a65690..6c1de5a0 100644 --- a/Meshtastic/Views/Settings/Routes.swift +++ b/Meshtastic/Views/Settings/Routes.swift @@ -18,6 +18,8 @@ struct Routes: View { @State private var selectedRoute: RouteEntity? @State private var importing = false @State private var isShowingBadFileAlert = false + @State var isExporting = false + @State var exportString = "" @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "enabled", ascending: false), NSSortDescriptor(key: "name", ascending: true), NSSortDescriptor(key: "date", ascending: false)], animation: .default) @@ -33,7 +35,7 @@ struct Routes: View { .padding() .alert(isPresented: $isShowingBadFileAlert) { - Alert(title: Text("Not a valid route file"), message: Text("Your route file must have both Latitude and Longitude."), dismissButton: .default(Text("OK"))) + Alert(title: Text("Not a valid route file"), message: Text("Your route file must have both Latitude and Longitude columns and headers."), dismissButton: .default(Text("OK"))) } .fileImporter( isPresented: $importing, @@ -187,8 +189,36 @@ struct Routes: View { .stroke(Color(UIColor(hex: UInt32(selectedRoute?.color ?? 0))), style: dashed) } .frame(maxWidth: .infinity, maxHeight: .infinity) + .safeAreaInset(edge: .bottom, alignment: UIDevice.current.userInterfaceIdiom == .phone ? .leading : .trailing) { + Button { + exportString = routeToCsvFile(locations: selectedRoute!.locations!.array as? [LocationEntity] ?? []) + isExporting = true + } label: { + Label("save", systemImage: "square.and.arrow.down") + } + .buttonStyle(.bordered) + .buttonBorderShape(.capsule) + .controlSize(.large) + .padding(.bottom) + .padding(.leading) + } + } }.navigationTitle(" \(selectedRoute?.name ?? "Unknown Route") \(selectedRoute?.locations?.count ?? 0) points") } + .fileExporter( + isPresented: $isExporting, + document: CsvDocument(emptyCsv: exportString), + contentType: .commaSeparatedText, + defaultFilename: String("\(selectedRoute?.name ?? "Route") Log"), + onCompletion: { result in + if case .success = result { + print("Route log download succeeded.") + self.isExporting = false + } else { + print("Route log download failed: \(result).") + } + } + ) } } diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index 886acf8c..87d23bd3 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -313,7 +313,11 @@ struct Settings: View { } .onAppear { self.preferredNodeNum = UserDefaults.preferredPeripheralNum - if selectedNode == 0 { + if nodes.count > 1 { + if selectedNode == 0 { + self.selectedNode = Int(bleManager.connectedPeripheral != nil ? UserDefaults.preferredPeripheralNum : 0) + } + } else { self.selectedNode = Int(bleManager.connectedPeripheral != nil ? UserDefaults.preferredPeripheralNum : 0) } }