diff --git a/Meshtastic/Views/Connect/Connect.swift b/Meshtastic/Views/Connect/Connect.swift index 6dead473..596e794b 100644 --- a/Meshtastic/Views/Connect/Connect.swift +++ b/Meshtastic/Views/Connect/Connect.swift @@ -30,7 +30,7 @@ struct Connect: View { var body: some View { NavigationStack { - VStack { + VStack(spacing: 0) { List { Section { if let connectedDevice = accessoryManager.activeConnection?.device, @@ -338,6 +338,7 @@ struct Connect: View { Spacer() } .padding(.bottom, 10) + .background(Color(.tertiarySystemGroupedBackground)) } .navigationTitle("Connect") .navigationBarItems( diff --git a/Meshtastic/Views/Messages/TextMessageField/TextMessageField.swift b/Meshtastic/Views/Messages/TextMessageField/TextMessageField.swift index c5d44f96..4a4767ea 100644 --- a/Meshtastic/Views/Messages/TextMessageField/TextMessageField.swift +++ b/Meshtastic/Views/Messages/TextMessageField/TextMessageField.swift @@ -40,7 +40,7 @@ struct TextMessageField: View { .background( Capsule() .strokeBorder(.tertiary, lineWidth: 1) - .background(Capsule().fill(Color.white)) + .background(Capsule().fill(Color(.systemBackground))) ) .clipShape(Capsule()) .onChange(of: typingMessage) { _, value in @@ -58,6 +58,7 @@ struct TextMessageField: View { sendMessage() #endif } + .foregroundColor(.primary) if !typingMessage.isEmpty { Button(action: sendMessage) { Image(systemName: "arrow.up.circle.fill") diff --git a/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift b/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift index a5116dcd..0fad8f98 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift @@ -448,6 +448,15 @@ struct NodeDetail: View { node: node ) if connectedNode.num != node.num { + if !(node.user?.unmessagable ?? true) { + Button(action: { + if let url = URL(string: "meshtastic:///messages?userNum=\(node.num)") { + UIApplication.shared.open(url) + } + }) { + Label("Message", systemImage: "message") + } + } ExchangePositionsButton( node: node ) diff --git a/Meshtastic/Views/Nodes/MeshMap.swift b/Meshtastic/Views/Nodes/MeshMap.swift index ba7503d9..812c887b 100644 --- a/Meshtastic/Views/Nodes/MeshMap.swift +++ b/Meshtastic/Views/Nodes/MeshMap.swift @@ -1,8 +1,8 @@ // -// MeshMap.swift -// Meshtastic +//  MeshMap.swift +//  Meshtastic // -// Copyright(c) Garth Vander Houwen 11/7/23. +//  Copyright(c) Garth Vander Houwen 11/7/23. // import SwiftUI @@ -45,7 +45,6 @@ struct MeshMap: View { @StateObject var filters = NodeFilterParameters() var body: some View { - NavigationStack { ZStack { MapReader { reader in @@ -79,110 +78,111 @@ struct MeshMap: View { distance = context.camera.distance }) .onTapGesture(count: 1, perform: { position in - newWaypointCoord = reader.convert(position, from: .local) ?? CLLocationCoordinate2D.init() + newWaypointCoord = reader.convert(position, from: .local) ?? CLLocationCoordinate2D.init() }) .gesture( LongPressGesture(minimumDuration: 0.5) .sequenced(before: SpatialTapGesture(coordinateSpace: .local)) .onEnded { value in - switch value { - case let .second(_, tapValue): - guard let point = tapValue?.location else { - Logger.services.error("Unable to retreive tap location from gesture data.") - return - } + switch value { + case let .second(_, tapValue): + guard let point = tapValue?.location else { + Logger.services.error("Unable to retreive tap location from gesture data.") + return + } - guard let coordinate = reader.convert(point, from: .local) else { - Logger.services.error("Unable to convert local point to coordinate on map.") - return - } - centerMapAt(coordinate: coordinate) + guard let coordinate = reader.convert(point, from: .local) else { + Logger.services.error("Unable to convert local point to coordinate on map.") + return + } + centerMapAt(coordinate: coordinate) - newWaypointCoord = coordinate - editingWaypoint = WaypointEntity(context: context) - editingWaypoint!.name = "Waypoint Pin" - editingWaypoint!.expire = Date.now.addingTimeInterval(60 * 480) - editingWaypoint!.latitudeI = Int32((newWaypointCoord?.latitude ?? 0) * 1e7) - editingWaypoint!.longitudeI = Int32((newWaypointCoord?.longitude ?? 0) * 1e7) - editingWaypoint!.expire = Date.now.addingTimeInterval(60 * 480) - editingWaypoint!.id = 0 - Logger.services.debug("Long press occured at Lat: \(coordinate.latitude, privacy: .public) Long: \(coordinate.longitude, privacy: .public)") - default: return + newWaypointCoord = coordinate + editingWaypoint = WaypointEntity(context: context) + editingWaypoint!.name = "Waypoint Pin" + editingWaypoint!.expire = Date.now.addingTimeInterval(60 * 480) + editingWaypoint!.latitudeI = Int32((newWaypointCoord?.latitude ?? 0) * 1e7) + editingWaypoint!.longitudeI = Int32((newWaypointCoord?.longitude ?? 0) * 1e7) + editingWaypoint!.expire = Date.now.addingTimeInterval(60 * 480) + editingWaypoint!.id = 0 + Logger.services.debug("Long press occured at Lat: \(coordinate.latitude, privacy: .public) Long: \(coordinate.longitude, privacy: .public)") + default: return + } } - }) + ) + .ignoresSafeArea() } - } - .sheet(item: $selectedPosition) { selection in - PositionPopover(position: selection, popover: false) - .padding() - } - .sheet(item: $selectedWaypoint) { selection in - WaypointForm(waypoint: selection) - .padding() - } - .sheet(item: $editingWaypoint) { selection in - WaypointForm(waypoint: selection, editMode: true) - .padding() - } - .sheet(isPresented: $editingSettings) { - MapSettingsForm(traffic: $showTraffic, pointsOfInterest: $showPointsOfInterest, mapLayer: $selectedMapLayer, meshMap: $isMeshMap, enabledOverlayConfigs: $enabledOverlayConfigs) - } - .onChange(of: router.navigationState) { - guard case .map = router.navigationState.selectedTab else { return } - // TODO: handle deep link for waypoints - } - .onChange(of: selectedMapLayer) { _, newMapLayer in - switch selectedMapLayer { - case .standard: - UserDefaults.mapLayer = newMapLayer - mapStyle = MapStyle.standard(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic) - case .hybrid: - UserDefaults.mapLayer = newMapLayer - mapStyle = MapStyle.hybrid(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic) - case .satellite: - UserDefaults.mapLayer = newMapLayer - mapStyle = MapStyle.imagery(elevation: .realistic) - case .offline: - return + .sheet(item: $selectedPosition) { selection in + PositionPopover(position: selection, popover: false) + .padding() } - } - .sheet(isPresented: $editingFilters) { - NodeListFilter( - filters: filters - ) - } - .safeAreaInset(edge: .bottom, alignment: .trailing) { - HStack { - Spacer() - Button(action: { - withAnimation { - editingSettings = !editingSettings - } - }) { - Image(systemName: editingSettings ? "info.circle.fill" : "info.circle") - .padding(.vertical, 5) - } - .tint(Color(UIColor.secondarySystemBackground)) - .foregroundColor(.accentColor) - .buttonStyle(.borderedProminent) + .sheet(item: $selectedWaypoint) { selection in + WaypointForm(waypoint: selection) + .padding() } - .controlSize(.regular) - .padding(5) - } - } - .navigationBarItems(leading: MeshtasticLogo(), trailing: ZStack { - ConnectedDevice(deviceConnected: accessoryManager.isConnected, name: accessoryManager.activeConnection?.device.shortName ?? "?") - }) - .onFirstAppear { - UIApplication.shared.isIdleTimerDisabled = true + .sheet(item: $editingWaypoint) { selection in + WaypointForm(waypoint: selection, editMode: true) + .padding() + } + .sheet(isPresented: $editingSettings) { + MapSettingsForm(traffic: $showTraffic, pointsOfInterest: $showPointsOfInterest, mapLayer: $selectedMapLayer, meshMap: $isMeshMap, enabledOverlayConfigs: $enabledOverlayConfigs) + .presentationDetents([.fraction(0.85), .large]) + } + .onChange(of: router.navigationState) { + guard case .map = router.navigationState.selectedTab else { return } + // TODO: handle deep link for waypoints + } + .onChange(of: selectedMapLayer) { _, newMapLayer in + switch selectedMapLayer { + case .standard: + UserDefaults.mapLayer = newMapLayer + mapStyle = MapStyle.standard(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic) + case .hybrid: + UserDefaults.mapLayer = newMapLayer + mapStyle = MapStyle.hybrid(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic) + case .satellite: + UserDefaults.mapLayer = newMapLayer + mapStyle = MapStyle.imagery(elevation: .realistic) + case .offline: + return + } + } + .sheet(isPresented: $editingFilters) { + NodeListFilter( + filters: filters + ) + } + .safeAreaInset(edge: .bottom, alignment: .trailing) { + HStack { + Spacer() + Button(action: { + withAnimation { + editingSettings = !editingSettings + } + }) { + Image(systemName: editingSettings ? "info.circle.fill" : "info.circle") + .padding(.vertical, 5) + } + .tint(Color(UIColor.secondarySystemBackground)) + .foregroundColor(.accentColor) + .buttonStyle(.borderedProminent) + } + .controlSize(.regular) + .padding(5) + } + } + .navigationBarItems(leading: MeshtasticLogo(), trailing: ZStack { + ConnectedDevice(deviceConnected: accessoryManager.isConnected, name: accessoryManager.activeConnection?.device.shortName ?? "?") + }) + .toolbarBackground(.hidden, for: .navigationBar) + } + .onAppear { + UIApplication.shared.isIdleTimerDisabled = true // Initialize enabled overlay configs with all active files let activeFiles = GeoJSONOverlayManager.shared.getUploadedFilesWithState().filter { $0.isActive } enabledOverlayConfigs = Set(activeFiles.map { $0.id }) - // let wayPointEntity = getWaypoint(id: Int64(deepLinkManager.waypointId) ?? -1, context: context) - // if wayPointEntity.id > 0 { - // position = .camera(MapCamera(centerCoordinate: wayPointEntity.coordinate, distance: 1000, heading: 0, pitch: 60)) switch selectedMapLayer { case .standard: mapStyle = MapStyle.standard(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic) @@ -210,9 +210,9 @@ struct MeshMap: View { position = .camera( MapCamera( centerCoordinate: coordinate, // Set new center - distance: distance, // Preserve current zoom distance - heading: 0, // align north - pitch: 0 // set view to top down + distance: distance, // Preserve current zoom distance + heading: 0, // align north + pitch: 0 // set view to top down ) ) }) diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index 20a879b3..5d15b4fb 100644 --- a/Meshtastic/Views/Nodes/NodeList.swift +++ b/Meshtastic/Views/Nodes/NodeList.swift @@ -58,7 +58,7 @@ struct NodeList: View { /// Allow users to mute notifications for a node even if they are not connected if let user = node.user { NodeAlertsButton(context: context, node: node, user: user) - if !user.unmessagable { + if !user.unmessagable && user.num == UserDefaults.preferredPeripheralNum { Button(action: { shareContactNode = node }) { @@ -80,9 +80,6 @@ struct NodeList: View { Label("Message", systemImage: "message") } } - TraceRouteButton( - node: node - ) Button { Task { do { @@ -104,6 +101,9 @@ struct NodeList: View { } label: { Label("Exchange Positions", systemImage: "arrow.triangle.2.circlepath") } + TraceRouteButton( + node: node + ) IgnoreNodeButton( node: node )