mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #58 from meshtastic/feature/message_ack
Feature/message ack
This commit is contained in:
commit
285e314af9
11 changed files with 101 additions and 85 deletions
|
|
@ -738,7 +738,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = MeshtasticClient/MeshtasticClient.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 10;
|
||||
CURRENT_PROJECT_VERSION = 12;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"MeshtasticClient/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = GCH7VS5Y9R;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
|
|
@ -769,7 +769,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = MeshtasticClient/MeshtasticClient.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 10;
|
||||
CURRENT_PROJECT_VERSION = 12;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"MeshtasticClient/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = GCH7VS5Y9R;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
|
|
|
|||
|
|
@ -742,7 +742,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
print("💾 Saved a new message for \(decodedInfo.packet.id)")
|
||||
if meshLoggingEnabled { MeshLogger.log("💾 Saved a new message for \(newMessage.messageId)") }
|
||||
|
||||
if newMessage.toUser!.num == self.broadcastNodeNum || self.connectedPeripheral != nil && self.connectedPeripheral.num == newMessage.toUser!.num {
|
||||
if newMessage.toUser != nil && newMessage.toUser!.num == self.broadcastNodeNum || self.connectedPeripheral != nil && self.connectedPeripheral.num == newMessage.toUser!.num {
|
||||
|
||||
// Create an iOS Notification for the received message and schedule it immediately
|
||||
let manager = LocalNotificationManager()
|
||||
|
|
@ -891,17 +891,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
// MARK: Incoming ROUTING_APP Packet
|
||||
} else if decodedInfo.packet.decoded.portnum == PortNum.routingApp {
|
||||
|
||||
let currentNodeNum = self.connectedPeripheral.num
|
||||
|
||||
if let routingMessage = try? Routing(serializedData: decodedInfo.packet.decoded.payload) {
|
||||
print(decodedInfo.packet.decoded.requestID)
|
||||
print(routingMessage)
|
||||
//let mes = routingMessage.
|
||||
let error = routingMessage.errorReason
|
||||
|
||||
//routingMessage.routeRequest
|
||||
}
|
||||
|
||||
if decodedInfo.packet.priority == MeshPacket.Priority.ack {
|
||||
|
||||
let fetchMessageRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MessageEntity")
|
||||
|
|
@ -909,15 +898,26 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
do {
|
||||
|
||||
let fetchedMessage = try context?.fetch(fetchMessageRequest) as! [MessageEntity]
|
||||
let fetchedMessage = try context?.fetch(fetchMessageRequest)[0] as? MessageEntity
|
||||
|
||||
if fetchedMessage.count > 0 {
|
||||
|
||||
if fetchedMessage != nil {
|
||||
fetchedMessage!.receivedACK = true
|
||||
fetchedMessage!.ackTimestamp = Int32(Date().timeIntervalSince1970)
|
||||
}
|
||||
|
||||
try context!.save()
|
||||
|
||||
if meshLoggingEnabled {
|
||||
MeshLogger.log("💾 ACK Received and saved for MessageID \(decodedInfo.packet.id)")
|
||||
}
|
||||
print("💾 ACK Received and saved for MessageID \(decodedInfo.packet.id)")
|
||||
|
||||
} catch {
|
||||
|
||||
|
||||
context!.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Saving ACK for message MessageID \(decodedInfo.packet.id) Error: \(nsError)")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1100,7 +1100,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
let fromNodeNum = connectedPeripheral.num
|
||||
|
||||
if fromNodeNum <= 0 {
|
||||
if fromNodeNum <= 0 || (LocationHelper.currentLocation.latitude == LocationHelper.DefaultLocation.latitude && LocationHelper.currentLocation.longitude == LocationHelper.DefaultLocation.longitude) {
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
@ -1188,6 +1188,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
// Send a position out to the mesh if "share location with the mesh" is enabled in settings
|
||||
if userSettings!.provideLocation {
|
||||
|
||||
let success = sendPosition(destNum: connectedPeripheral.num, wantResponse: false)
|
||||
if !success {
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ class LocationHelper: NSObject, ObservableObject {
|
|||
|
||||
static let shared = LocationHelper()
|
||||
|
||||
// Mount Rainier
|
||||
static let DefaultLocation = CLLocationCoordinate2D(latitude: 46.879967, longitude: -121.726906)
|
||||
// Apple Park
|
||||
static let DefaultLocation = CLLocationCoordinate2D(latitude: 37.3346, longitude: -122.0090)
|
||||
|
||||
static let DefaultAltitude = CLLocationDistance(integerLiteral: 0)
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<false/>
|
||||
<true/>
|
||||
<key>NSBluetoothAlwaysUsageDescription</key>
|
||||
<string>We use bluetooth to connect to nearby Meshtastic Devices</string>
|
||||
<key>NSBluetoothPeripheralUsageDescription</key>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21D62" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="MessageEntity" representedClassName="MessageEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="ackTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="direction" attributeType="String"/>
|
||||
<attribute name="isTapback" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="messageId" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
|
|
@ -76,7 +77,7 @@
|
|||
</fetchedProperty>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="MessageEntity" positionX="-36" positionY="63" width="128" height="185"/>
|
||||
<element name="MessageEntity" positionX="-36" positionY="63" width="128" height="200"/>
|
||||
<element name="MyInfoEntity" positionX="-18" positionY="81" width="128" height="179"/>
|
||||
<element name="NodeInfoEntity" positionX="-63" positionY="-18" width="128" height="149"/>
|
||||
<element name="PositionEntity" positionX="-54" positionY="54" width="128" height="134"/>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ struct CircleText: View {
|
|||
Circle()
|
||||
.fill(color)
|
||||
.frame(width: circleSize, height: circleSize)
|
||||
Text(text).textCase(.uppercase).font(font).foregroundColor(.white)
|
||||
Text(text).textCase(.uppercase).font(font).foregroundColor(.white).fixedSize()
|
||||
.frame(width: circleSize, height: circleSize, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/).offset(x: 0, y: 0)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,11 @@ struct Contacts: View {
|
|||
List(users) { (user: UserEntity) in
|
||||
|
||||
let allMessages = user.value(forKey: "allMessages") as! [MessageEntity]
|
||||
let connectedNodeNum = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.num : 0
|
||||
|
||||
NavigationLink(destination: UserMessageList(user: user)) {
|
||||
if user.num != connectedNodeNum {
|
||||
|
||||
NavigationLink(destination: UserMessageList(user: user)) {
|
||||
|
||||
if allMessages.count > 0 {
|
||||
|
||||
|
|
@ -120,6 +123,7 @@ struct Contacts: View {
|
|||
}.padding()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Contacts")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
|
|
|
|||
|
|
@ -218,15 +218,9 @@ struct UserMessageList: View {
|
|||
|
||||
VStack {
|
||||
|
||||
let time = Int32(message.messageTimestamp)
|
||||
let messageDate = Date(timeIntervalSince1970: TimeInterval(time))
|
||||
|
||||
if time != 0 {
|
||||
Text("Sent \(messageDate, style: .date) \(messageDate, style: .time)").font(.caption2).foregroundColor(.gray)
|
||||
} else {
|
||||
Text("Unknown").font(.caption2).foregroundColor(.gray)
|
||||
}
|
||||
let messageDate = Date(timeIntervalSince1970: TimeInterval(message.messageTimestamp))
|
||||
|
||||
Text("Sent \(messageDate, style: .date) \(messageDate, style: .time)").font(.caption2).foregroundColor(.gray)
|
||||
}
|
||||
|
||||
VStack {
|
||||
|
|
@ -234,6 +228,13 @@ struct UserMessageList: View {
|
|||
Text("Received ACK: \(message.receivedACK ? "✔️" : "")")
|
||||
|
||||
}
|
||||
if message.receivedACK {
|
||||
VStack {
|
||||
|
||||
let ackDate = Date(timeIntervalSince1970: TimeInterval(message.ackTimestamp))
|
||||
Text("ACK \(ackDate, style: .date) \(ackDate, style: .time)").font(.caption2).foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
Button(role: .destructive, action: {
|
||||
|
|
@ -285,13 +286,6 @@ struct UserMessageList: View {
|
|||
let time = Int32(message.messageTimestamp)
|
||||
let messageDate = Date(timeIntervalSince1970: TimeInterval(time))
|
||||
let showUntil = Date().addingTimeInterval(3600)
|
||||
|
||||
if time != 0 {
|
||||
// Text(messageDate, style: .date).font(.caption2).foregroundColor(.gray)
|
||||
// Text(messageDate, style: .time).font(.caption2).foregroundColor(.gray)
|
||||
} else {
|
||||
// Text("Unknown").font(.caption2).foregroundColor(.gray)
|
||||
}
|
||||
|
||||
if messageDate <= showUntil && message.receivedACK {
|
||||
|
||||
|
|
@ -352,7 +346,7 @@ struct UserMessageList: View {
|
|||
|
||||
let index = count - 1
|
||||
|
||||
if index > 2 {
|
||||
if index > 3 {
|
||||
|
||||
scrollView.scrollTo(index, anchor: .bottom)
|
||||
}
|
||||
|
|
@ -394,11 +388,25 @@ struct UserMessageList: View {
|
|||
sendPositionWithMessage = true
|
||||
if user.num == bleManager.broadcastNodeNum {
|
||||
|
||||
typingMessage = "📍 " + userLongName + " Has shared their position with the mesh."
|
||||
if userSettings.meshtasticUsername.count > 0 {
|
||||
|
||||
typingMessage = "📍 " + userSettings.meshtasticUsername + " has shared their position with the mesh from node " + userLongName
|
||||
} else {
|
||||
|
||||
typingMessage = "📍 " + userLongName + " has shared their position with the mesh."
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
typingMessage = "📍 " + userLongName + " Has shared their position with you."
|
||||
if userSettings.meshtasticUsername.count > 0 {
|
||||
|
||||
typingMessage = "📍 " + userSettings.meshtasticUsername + " has shared their position with you from node " + userLongName
|
||||
|
||||
} else {
|
||||
|
||||
typingMessage = "📍 " + userLongName + " has shared their position with you."
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "mappin.and.ellipse")
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ struct NodeDetail: View {
|
|||
MapAnnotation(
|
||||
coordinate: location.coordinate,
|
||||
content: {
|
||||
CircleText(text: node.user!.shortName ?? "???", color: .accentColor, circleSize: 33, fontSize: 16)
|
||||
CircleText(text: node.user!.shortName ?? "???", color: .accentColor, circleSize: 32, fontSize: 14)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -271,7 +271,7 @@ struct NodeDetail: View {
|
|||
.padding(1)
|
||||
}
|
||||
}
|
||||
.navigationTitle(node != nil ? String(node.user!.longName ?? "Unknown") : "Unknown")
|
||||
.navigationTitle((node != nil && node.user != nil) ? String(node.user!.longName ?? "Unknown") : "Unknown")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarItems(trailing:
|
||||
|
||||
|
|
|
|||
|
|
@ -87,28 +87,28 @@ struct NodeList: View {
|
|||
}
|
||||
.padding([.leading, .top, .bottom])
|
||||
}
|
||||
.swipeActions {
|
||||
|
||||
Button {
|
||||
|
||||
context.delete(node)
|
||||
|
||||
do {
|
||||
|
||||
try context.save()
|
||||
print("Successfully Deleted NodeInfoEntiy: \(node.num)")
|
||||
|
||||
} catch {
|
||||
|
||||
print("Failed to save context after deleting NodeInfoEntity Num: \(node.num)")
|
||||
}
|
||||
|
||||
} label: {
|
||||
|
||||
Label("Delete from app", systemImage: "trash")
|
||||
}
|
||||
.tint(.red)
|
||||
}
|
||||
// .swipeActions {
|
||||
//
|
||||
// Button {
|
||||
//
|
||||
// context.delete(node)
|
||||
//
|
||||
// do {
|
||||
//
|
||||
// try context.save()
|
||||
// print("Successfully Deleted NodeInfoEntiy: \(node.num)")
|
||||
//
|
||||
// } catch {
|
||||
//
|
||||
// print("Failed to save context after deleting NodeInfoEntity Num: \(node.num)")
|
||||
// }
|
||||
//
|
||||
// } label: {
|
||||
//
|
||||
// Label("Delete from app", systemImage: "trash")
|
||||
// }
|
||||
// .tint(.red)
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,11 +54,11 @@ enum MeshMapType: String, CaseIterable, Identifiable {
|
|||
}
|
||||
|
||||
class UserSettings: ObservableObject {
|
||||
// @Published var meshtasticUsername: String {
|
||||
// didSet {
|
||||
// UserDefaults.standard.set(meshtasticUsername, forKey: "meshtasticusername")
|
||||
// }
|
||||
// }
|
||||
@Published var meshtasticUsername: String {
|
||||
didSet {
|
||||
UserDefaults.standard.set(meshtasticUsername, forKey: "meshtasticusername")
|
||||
}
|
||||
}
|
||||
@Published var preferredPeripheralName: String {
|
||||
didSet {
|
||||
UserDefaults.standard.set(preferredPeripheralName, forKey: "preferredPeripheralName")
|
||||
|
|
@ -99,7 +99,7 @@ class UserSettings: ObservableObject {
|
|||
|
||||
init() {
|
||||
|
||||
// self.meshtasticUsername = UserDefaults.standard.object(forKey: "meshtasticusername") as? String ?? ""
|
||||
self.meshtasticUsername = UserDefaults.standard.object(forKey: "meshtasticusername") as? String ?? ""
|
||||
self.preferredPeripheralName = UserDefaults.standard.object(forKey: "preferredPeripheralName") as? String ?? ""
|
||||
self.preferredPeripheralId = UserDefaults.standard.object(forKey: "preferredPeripheralId") as? String ?? ""
|
||||
self.provideLocation = UserDefaults.standard.object(forKey: "provideLocation") as? Bool ?? false
|
||||
|
|
@ -131,12 +131,14 @@ struct AppSettings: View {
|
|||
List {
|
||||
Section(header: Text("USER DETAILS")) {
|
||||
|
||||
// HStack {
|
||||
// Label("Name", systemImage: "person.crop.rectangle.fill")
|
||||
// TextField("Username", text: $userSettings.meshtasticUsername)
|
||||
// .foregroundColor(.gray)
|
||||
// }
|
||||
// .listRowSeparator(.visible)
|
||||
HStack {
|
||||
Label("Name", systemImage: "person.crop.rectangle.fill")
|
||||
TextField("Username", text: $userSettings.meshtasticUsername)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
.keyboardType(.asciiCapable)
|
||||
.disableAutocorrection(true)
|
||||
.listRowSeparator(.visible)
|
||||
Toggle(isOn: $userSettings.provideLocation) {
|
||||
|
||||
Label("Provide location to mesh", systemImage: "location.circle.fill")
|
||||
|
|
@ -173,13 +175,13 @@ struct AppSettings: View {
|
|||
.pickerStyle(DefaultPickerStyle())
|
||||
// TextField("Custom Tile Server", text: $userSettings.meshMapCustomTileServer)
|
||||
}
|
||||
Section(header: Text("DEBUG")) {
|
||||
// Toggle(isOn: $userSettings.meshActivityLog) {
|
||||
Section(header: Text("DEBUG OPTIONS")) {
|
||||
Toggle(isOn: $userSettings.meshActivityLog) {
|
||||
|
||||
// Label("Log all Mesh activity", systemImage: "network")
|
||||
// }
|
||||
// .toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
if true {// userSettings.meshActivityLog {
|
||||
Label("Log all Mesh activity", systemImage: "network")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
if userSettings.meshActivityLog {
|
||||
NavigationLink(destination: MeshLog()) {
|
||||
Text("View Mesh Log")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue