extend map into the notch, get rid of little annoying white space on … (#1386)

* extend map into the notch, get rid of little annoying white space on connect

* Add message action to node details, restrict contact sharing to our own node

* Fix text area background colors

* Use primary for text

* Us primary background
This commit is contained in:
Garth Vander Houwen 2025-09-11 18:35:52 -07:00 committed by GitHub
parent 8b4ebf4645
commit 7c4d55219c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 112 additions and 101 deletions

View file

@ -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(

View file

@ -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")

View file

@ -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
)

View file

@ -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
)
)
})

View file

@ -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
)