mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge remote-tracking branch 'refs/remotes/origin/2.3.11_Working_Changes'
This commit is contained in:
commit
81839ea313
7 changed files with 205 additions and 172 deletions
|
|
@ -1418,6 +1418,7 @@
|
|||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-expression-type-checking=200";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
|
|
@ -1475,6 +1476,7 @@
|
|||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-expression-type-checking=200";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
|
|
@ -1572,6 +1574,7 @@
|
|||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.3.10;
|
||||
OTHER_SWIFT_FLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
@ -1605,6 +1608,7 @@
|
|||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.3.10;
|
||||
OTHER_SWIFT_FLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
|
|||
|
|
@ -11,12 +11,10 @@ struct LogDocument: FileDocument {
|
|||
}
|
||||
|
||||
init(configuration: ReadConfiguration) throws {
|
||||
guard let data = configuration.file.regularFileContents,
|
||||
let string = String(data: data, encoding: .utf8)
|
||||
else {
|
||||
guard let data = configuration.file.regularFileContents else {
|
||||
throw CocoaError(.fileReadCorruptFile)
|
||||
}
|
||||
logFile = string
|
||||
logFile = String(decoding: data, as: UTF8.self)
|
||||
}
|
||||
|
||||
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class MeshLogger {
|
|||
fileHandle.closeFile()
|
||||
} else {
|
||||
try data.write(to: logFile, options: .atomicWrite)
|
||||
let log = String(data: data, encoding: .utf8) ?? "unknown".localized
|
||||
let log = String(decoding: data, as: UTF8.self)
|
||||
Logger.mesh.notice("\(log)")
|
||||
}
|
||||
} catch {
|
||||
|
|
|
|||
|
|
@ -73,7 +73,6 @@ struct Messages: View {
|
|||
|
||||
if let urlComponent = URLComponents(string: newPath ?? "") {
|
||||
let queryItems = urlComponent.queryItems
|
||||
let messageId = queryItems?.first(where: { $0.name == "messageId" })?.value
|
||||
let channel = queryItems?.first(where: { $0.name == "channel" })?.value
|
||||
|
||||
if let channel {
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ struct MeshMapContent: MapContent {
|
|||
}
|
||||
}
|
||||
}
|
||||
.onTapGesture { location in
|
||||
.onTapGesture { _ in
|
||||
selectedPosition = (selectedPosition == position ? nil : position)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,172 +35,203 @@ struct NodeMapSwiftUI: View {
|
|||
|
||||
@State private var mapRegion = MKCoordinateRegion.init()
|
||||
|
||||
@FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)],
|
||||
predicate: NSPredicate(
|
||||
format: "expire == nil || expire >= %@", Date() as NSDate
|
||||
), animation: .none)
|
||||
@FetchRequest(
|
||||
sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)],
|
||||
predicate: NSPredicate(
|
||||
format: "expire == nil || expire >= %@", Date() as NSDate
|
||||
),
|
||||
animation: .none
|
||||
)
|
||||
private var waypoints: FetchedResults<WaypointEntity>
|
||||
|
||||
var body: some View {
|
||||
var mostRecent = node.positions?.lastObject as? PositionEntity
|
||||
private var title: String {
|
||||
String((node.user?.shortName ?? "unknown".localized) + (" \(node.positions?.count ?? 0) points"))
|
||||
}
|
||||
|
||||
private var mostRecent: PositionEntity? {
|
||||
node.positions?.lastObject as? PositionEntity
|
||||
}
|
||||
|
||||
if node.hasPositions {
|
||||
ZStack {
|
||||
MapReader { _ in
|
||||
Map(position: $position, bounds: MapCameraBounds(minimumDistance: 1, maximumDistance: .infinity), scope: mapScope) {
|
||||
NodeMapContent(node: node)
|
||||
}
|
||||
.mapScope(mapScope)
|
||||
.mapStyle(mapStyle)
|
||||
.mapControls {
|
||||
MapScaleView(scope: mapScope)
|
||||
.mapControlVisibility(.visible)
|
||||
if showUserLocation {
|
||||
MapUserLocationButton(scope: mapScope)
|
||||
.mapControlVisibility(.visible)
|
||||
}
|
||||
MapPitchToggle(scope: mapScope)
|
||||
.mapControlVisibility(.visible)
|
||||
MapCompass(scope: mapScope)
|
||||
.mapControlVisibility(.visible)
|
||||
}
|
||||
.controlSize(.regular)
|
||||
.overlay(alignment: .bottom) {
|
||||
if scene != nil && isLookingAround {
|
||||
LookAroundPreview(initialScene: scene)
|
||||
.frame(height: UIDevice.current.userInterfaceIdiom == .phone ? 250 : 400)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||
.padding(.horizontal, 20)
|
||||
}
|
||||
}
|
||||
.overlay(alignment: .bottom) {
|
||||
if !isLookingAround && isShowingAltitude {
|
||||
PositionAltitudeChart(node: node)
|
||||
.frame(height: UIDevice.current.userInterfaceIdiom == .phone ? 250 : 400)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||
.padding(.horizontal, 20)
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $isEditingSettings) {
|
||||
MapSettingsForm(traffic: $showTraffic, pointsOfInterest: $showPointsOfInterest, mapLayer: $selectedMapLayer, meshMap: $isMeshMap)
|
||||
.onChange(of: (selectedMapLayer)) { newMapLayer in
|
||||
switch selectedMapLayer {
|
||||
case .standard:
|
||||
UserDefaults.mapLayer = newMapLayer
|
||||
mapStyle = MapStyle.standard(elevation: .flat, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic)
|
||||
case .hybrid:
|
||||
UserDefaults.mapLayer = newMapLayer
|
||||
mapStyle = MapStyle.hybrid(elevation: .flat, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic)
|
||||
case .satellite:
|
||||
UserDefaults.mapLayer = newMapLayer
|
||||
mapStyle = MapStyle.imagery(elevation: .flat)
|
||||
case .offline:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: node) {
|
||||
isLookingAround = false
|
||||
|
||||
|
||||
private var mapControls: some View {
|
||||
HStack {
|
||||
MapScaleView(scope: mapScope)
|
||||
.mapControlVisibility(.visible)
|
||||
if showUserLocation {
|
||||
MapUserLocationButton(scope: mapScope)
|
||||
.mapControlVisibility(.visible)
|
||||
}
|
||||
MapPitchToggle(scope: mapScope)
|
||||
.mapControlVisibility(.visible)
|
||||
MapCompass(scope: mapScope)
|
||||
.mapControlVisibility(.visible)
|
||||
}
|
||||
}
|
||||
|
||||
private var mapAccessoryControls: some View {
|
||||
HStack {
|
||||
Button(action: {
|
||||
withAnimation {
|
||||
isEditingSettings = !isEditingSettings
|
||||
}
|
||||
}) {
|
||||
Image(systemName: isEditingSettings ? "info.circle.fill" : "info.circle")
|
||||
.padding(.vertical, 5)
|
||||
}
|
||||
.tint(Color(UIColor.secondarySystemBackground))
|
||||
.foregroundColor(.accentColor)
|
||||
.buttonStyle(.borderedProminent)
|
||||
/// Look Around Button
|
||||
if self.scene != nil {
|
||||
Button(action: {
|
||||
if isShowingAltitude {
|
||||
isShowingAltitude = false
|
||||
mostRecent = node.positions?.lastObject as? PositionEntity
|
||||
if node.positions?.count ?? 0 > 1 {
|
||||
position = .automatic
|
||||
} else {
|
||||
position = .camera(MapCamera(centerCoordinate: mostRecent!.coordinate, distance: 8000, heading: 0, pitch: 60))
|
||||
}
|
||||
if let mostRecent {
|
||||
Task {
|
||||
scene = try? await fetchScene(for: mostRecent.coordinate)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
UIApplication.shared.isIdleTimerDisabled = true
|
||||
switch selectedMapLayer {
|
||||
case .standard:
|
||||
mapStyle = MapStyle.standard(elevation: .flat, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic)
|
||||
case .hybrid:
|
||||
mapStyle = MapStyle.hybrid(elevation: .flat, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic)
|
||||
case .satellite:
|
||||
mapStyle = MapStyle.imagery(elevation: .flat)
|
||||
case .offline:
|
||||
mapStyle = MapStyle.hybrid(elevation: .flat, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic)
|
||||
}
|
||||
mostRecent = node.positions?.lastObject as? PositionEntity
|
||||
if node.positions?.count ?? 0 > 1 {
|
||||
position = .automatic
|
||||
} else {
|
||||
position = .camera(MapCamera(centerCoordinate: mostRecent!.coordinate, distance: 8000, heading: 0, pitch: 60))
|
||||
}
|
||||
if self.scene == nil {
|
||||
Task {
|
||||
scene = try? await fetchScene(for: mostRecent!.coordinate)
|
||||
}
|
||||
}
|
||||
isLookingAround = !isLookingAround
|
||||
}) {
|
||||
Image(systemName: isLookingAround ? "binoculars.fill" : "binoculars")
|
||||
.padding(.vertical, 5)
|
||||
}
|
||||
.tint(Color(UIColor.secondarySystemBackground))
|
||||
.foregroundColor(.accentColor)
|
||||
.buttonStyle(.borderedProminent)
|
||||
}
|
||||
/// Altitude Button
|
||||
if node.positions?.count ?? 0 > 1 {
|
||||
Button(action: {
|
||||
if isLookingAround {
|
||||
isLookingAround = false
|
||||
}
|
||||
.safeAreaInset(edge: .bottom, alignment: .trailing) {
|
||||
HStack {
|
||||
Button(action: {
|
||||
withAnimation {
|
||||
isEditingSettings = !isEditingSettings
|
||||
}
|
||||
}) {
|
||||
Image(systemName: isEditingSettings ? "info.circle.fill" : "info.circle")
|
||||
.padding(.vertical, 5)
|
||||
}
|
||||
.tint(Color(UIColor.secondarySystemBackground))
|
||||
.foregroundColor(.accentColor)
|
||||
.buttonStyle(.borderedProminent)
|
||||
/// Look Around Button
|
||||
if self.scene != nil {
|
||||
Button(action: {
|
||||
if isShowingAltitude {
|
||||
isShowingAltitude = false
|
||||
}
|
||||
isLookingAround = !isLookingAround
|
||||
}) {
|
||||
Image(systemName: isLookingAround ? "binoculars.fill" : "binoculars")
|
||||
.padding(.vertical, 5)
|
||||
}
|
||||
.tint(Color(UIColor.secondarySystemBackground))
|
||||
.foregroundColor(.accentColor)
|
||||
.buttonStyle(.borderedProminent)
|
||||
}
|
||||
/// Altitude Button
|
||||
if node.positions?.count ?? 0 > 1 {
|
||||
Button(action: {
|
||||
if isLookingAround {
|
||||
isLookingAround = false
|
||||
}
|
||||
isShowingAltitude = !isShowingAltitude
|
||||
}) {
|
||||
Image(systemName: isShowingAltitude ? "mountain.2.fill" : "mountain.2")
|
||||
.padding(.vertical, 5)
|
||||
}
|
||||
.tint(Color(UIColor.secondarySystemBackground))
|
||||
.foregroundColor(.accentColor)
|
||||
.buttonStyle(.borderedProminent)
|
||||
}
|
||||
}
|
||||
.controlSize(.regular)
|
||||
.padding(5)
|
||||
isShowingAltitude = !isShowingAltitude
|
||||
}) {
|
||||
Image(systemName: isShowingAltitude ? "mountain.2.fill" : "mountain.2")
|
||||
.padding(.vertical, 5)
|
||||
}
|
||||
.tint(Color(UIColor.secondarySystemBackground))
|
||||
.foregroundColor(.accentColor)
|
||||
.buttonStyle(.borderedProminent)
|
||||
}
|
||||
}
|
||||
.controlSize(.regular)
|
||||
.padding(5)
|
||||
}
|
||||
|
||||
private var connectedDeviceIndicator: some View {
|
||||
ConnectedDevice(
|
||||
bluetoothOn: bleManager.isSwitchedOn,
|
||||
deviceConnected: bleManager.connectedPeripheral != nil,
|
||||
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?"
|
||||
)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if node.hasPositions {
|
||||
Map(
|
||||
position: $position,
|
||||
bounds: MapCameraBounds(
|
||||
minimumDistance: 1,
|
||||
maximumDistance: .infinity
|
||||
),
|
||||
scope: mapScope
|
||||
) {
|
||||
NodeMapContent(node: node)
|
||||
}
|
||||
.mapScope(mapScope)
|
||||
.mapStyle(mapStyle)
|
||||
.mapControls {
|
||||
mapControls
|
||||
}
|
||||
.controlSize(.regular)
|
||||
.overlay(alignment: .bottom) {
|
||||
if scene != nil, isLookingAround {
|
||||
LookAroundPreview(initialScene: scene)
|
||||
.frame(height: UIDevice.current.userInterfaceIdiom == .phone ? 250 : 400)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||
.padding(.horizontal, 20)
|
||||
} else if isShowingAltitude {
|
||||
PositionAltitudeChart(node: node)
|
||||
.frame(height: UIDevice.current.userInterfaceIdiom == .phone ? 250 : 400)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||
.padding(.horizontal, 20)
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $isEditingSettings) {
|
||||
MapSettingsForm(
|
||||
traffic: $showTraffic,
|
||||
pointsOfInterest: $showPointsOfInterest,
|
||||
mapLayer: $selectedMapLayer,
|
||||
meshMap: $isMeshMap
|
||||
).onChange(of: (selectedMapLayer)) { newMapLayer in
|
||||
switch selectedMapLayer {
|
||||
case .standard:
|
||||
UserDefaults.mapLayer = newMapLayer
|
||||
mapStyle = MapStyle.standard(elevation: .flat, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic)
|
||||
case .hybrid:
|
||||
UserDefaults.mapLayer = newMapLayer
|
||||
mapStyle = MapStyle.hybrid(elevation: .flat, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic)
|
||||
case .satellite:
|
||||
UserDefaults.mapLayer = newMapLayer
|
||||
mapStyle = MapStyle.imagery(elevation: .flat)
|
||||
case .offline:
|
||||
return
|
||||
}
|
||||
.onDisappear {
|
||||
UIApplication.shared.isIdleTimerDisabled = false
|
||||
}
|
||||
}
|
||||
.onChange(of: node) {
|
||||
isLookingAround = false
|
||||
isShowingAltitude = false
|
||||
if node.positions?.count ?? 0 > 1 {
|
||||
position = .automatic
|
||||
} else if let coordinate = mostRecent?.coordinate {
|
||||
position = .camera(MapCamera(centerCoordinate: coordinate, distance: 8000, heading: 0, pitch: 60))
|
||||
}
|
||||
if let coordinate = mostRecent?.coordinate {
|
||||
Task {
|
||||
scene = try await fetchScene(for: coordinate)
|
||||
}
|
||||
}}
|
||||
.navigationBarTitle(String((node.user?.shortName ?? "unknown".localized) + (" \(node.positions?.count ?? 0) points")), displayMode: .inline)
|
||||
.navigationBarItems(trailing:
|
||||
ZStack {
|
||||
ConnectedDevice(
|
||||
bluetoothOn: bleManager.isSwitchedOn,
|
||||
deviceConnected: bleManager.connectedPeripheral != nil,
|
||||
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?")
|
||||
})
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
setupMapView()
|
||||
}
|
||||
.safeAreaInset(edge: .bottom, alignment: .trailing) {
|
||||
mapAccessoryControls
|
||||
}
|
||||
.onDisappear {
|
||||
UIApplication.shared.isIdleTimerDisabled = false
|
||||
}
|
||||
.navigationBarTitle(title, displayMode: .inline)
|
||||
.navigationBarItems(trailing: connectedDeviceIndicator)
|
||||
} else {
|
||||
ContentUnavailableView("No Positions", systemImage: "mappin.slash")
|
||||
}
|
||||
}
|
||||
|
||||
private func setupMapView() {
|
||||
UIApplication.shared.isIdleTimerDisabled = true
|
||||
switch selectedMapLayer {
|
||||
case .standard:
|
||||
mapStyle = MapStyle.standard(elevation: .flat, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic)
|
||||
case .hybrid:
|
||||
mapStyle = MapStyle.hybrid(elevation: .flat, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic)
|
||||
case .satellite:
|
||||
mapStyle = MapStyle.imagery(elevation: .flat)
|
||||
case .offline:
|
||||
mapStyle = MapStyle.hybrid(elevation: .flat, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic)
|
||||
}
|
||||
if node.positions?.count ?? 0 > 1 {
|
||||
position = .automatic
|
||||
} else if let coordinate = mostRecent?.coordinate {
|
||||
position = .camera(MapCamera(centerCoordinate: coordinate, distance: 8000, heading: 0, pitch: 60))
|
||||
}
|
||||
if scene == nil, let coordinate = mostRecent?.coordinate {
|
||||
Task {
|
||||
scene = try await fetchScene(for: coordinate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the look around scene
|
||||
private func fetchScene(for coordinate: CLLocationCoordinate2D) async throws -> MKLookAroundScene? {
|
||||
let lookAroundScene = MKLookAroundSceneRequest(coordinate: coordinate)
|
||||
|
|
|
|||
|
|
@ -58,21 +58,22 @@ struct Routes: View {
|
|||
}
|
||||
|
||||
do {
|
||||
guard let fileContent = String(data: try Data(contentsOf: selectedFile), encoding: .utf8) else { return }
|
||||
let data = try Data(contentsOf: selectedFile)
|
||||
let fileContent = String(decoding: data, as: UTF8.self)
|
||||
let routeName = selectedFile.lastPathComponent.dropLast(4)
|
||||
let lines = fileContent.components(separatedBy: "\n")
|
||||
let headers = lines.first?.components(separatedBy: ",")
|
||||
var latIndex = -1
|
||||
var longIndex = -1
|
||||
for index in headers!.indices {
|
||||
Logger.services.debug("\(index): \( headers![index])")
|
||||
if headers![index].trimmingCharacters(in: .whitespaces) == "Latitude" {
|
||||
guard let headers = lines.first?.components(separatedBy: ",") else { return }
|
||||
var latIndex: Int?
|
||||
var longIndex: Int?
|
||||
for index in headers.indices {
|
||||
Logger.services.debug("\(index): \(headers[index])")
|
||||
if headers[index].trimmingCharacters(in: .whitespaces) == "Latitude" {
|
||||
latIndex = index
|
||||
} else if headers![index].trimmingCharacters(in: .whitespaces) == "Longitude" {
|
||||
} else if headers[index].trimmingCharacters(in: .whitespaces) == "Longitude" {
|
||||
longIndex = index
|
||||
}
|
||||
}
|
||||
if latIndex >= 0 && longIndex >= 0 {
|
||||
if let latIndex, let longIndex {
|
||||
let newRoute = RouteEntity(context: context)
|
||||
newRoute.name = String(routeName)
|
||||
newRoute.id = Int32.random(in: Int32(Int8.max) ... Int32.max)
|
||||
|
|
@ -83,8 +84,8 @@ struct Routes: View {
|
|||
lines.dropFirst().forEach { line in
|
||||
let data = line.components(separatedBy: ",")
|
||||
if data.count > 1 {
|
||||
let latitude = latIndex >= 0 ? data[latIndex].trimmingCharacters(in: .whitespaces) : "0"
|
||||
let longitude = longIndex >= 0 ? data[longIndex].trimmingCharacters(in: .whitespaces) : "0"
|
||||
let latitude = data[latIndex].trimmingCharacters(in: .whitespaces)
|
||||
let longitude = data[longIndex].trimmingCharacters(in: .whitespaces)
|
||||
let loc = LocationEntity(context: context)
|
||||
loc.latitudeI = Int32((Double(latitude) ?? 0) * 1e7)
|
||||
loc.longitudeI = Int32((Double(longitude) ?? 0) * 1e7)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue