Merge pull request #1597 from meshtastic/2.7.9
Some checks failed
Upload dSYM Files / build (push) Has been cancelled

2.7.9 Working Changes
This commit is contained in:
Ben Meadors 2026-03-31 11:46:05 -05:00 committed by GitHub
commit d159b300e1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 63 additions and 13 deletions

View file

@ -41604,7 +41604,6 @@
}
},
"TAK" : {
"extractionState" : "stale",
"localizations" : {
"de" : {
"stringUnit" : {
@ -49740,4 +49739,4 @@
}
},
"version" : "1.1"
}
}

View file

@ -2170,7 +2170,7 @@
"@executable_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 14.6;
MARKETING_VERSION = 2.7.8;
MARKETING_VERSION = 2.7.9;
OTHER_LDFLAGS = (
"-weak_framework",
SwiftUI,
@ -2209,7 +2209,7 @@
"@executable_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 14.6;
MARKETING_VERSION = 2.7.8;
MARKETING_VERSION = 2.7.9;
OTHER_LDFLAGS = (
"-weak_framework",
SwiftUI,
@ -2245,7 +2245,7 @@
"@executable_path/../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 14.6;
MARKETING_VERSION = 2.7.8;
MARKETING_VERSION = 2.7.9;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -2278,7 +2278,7 @@
"@executable_path/../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 14.6;
MARKETING_VERSION = 2.7.8;
MARKETING_VERSION = 2.7.9;
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";

View file

@ -147,6 +147,8 @@ final class TAKMeshtasticBridge {
return
}
let channel = UInt32(TAKServerManager.shared.channel)
// Determine send method based on CoT type
let sendMethod = GenericCoTHandler.shared.classifySendMethod(for: cotMessage)
@ -159,7 +161,7 @@ final class TAKMeshtasticBridge {
}
do {
try await accessoryManager.sendTAKPacket(takPacket)
try await accessoryManager.sendTAKPacket(takPacket, channel: channel)
Logger.tak.info("Sent TAKPacket to mesh: \(cotMessage.type)")
} catch {
Logger.tak.error("Failed to send TAKPacket to mesh: \(error.localizedDescription)")
@ -169,7 +171,7 @@ final class TAKMeshtasticBridge {
// Use EXI compression on ATAK_FORWARDER port (257)
GenericCoTHandler.shared.accessoryManager = accessoryManager
do {
try await GenericCoTHandler.shared.sendGenericCoT(cotMessage)
try await GenericCoTHandler.shared.sendGenericCoT(cotMessage, channel: channel)
Logger.tak.info("Sent generic CoT to mesh via ATAK_FORWARDER: \(cotMessage.type)")
} catch {
Logger.tak.error("Failed to send generic CoT to mesh: \(error.localizedDescription)")

View file

@ -26,6 +26,8 @@ final class TAKServerManager: ObservableObject {
// MARK: - Configuration (persisted via AppStorage)
@AppStorage("takServerChannel") var channel: Int = 0
@AppStorage("takServerEnabled") var enabled = false {
didSet {
Task {

View file

@ -156,10 +156,16 @@ extension MeshPackets {
nonisolated public func deleteChannelMessages(channel: ChannelEntity, context: NSManagedObjectContext) {
do {
let objects = channel.allPrivateMessages
// Copied logic from ChannelEntity.allPrivateMessages, which is always on the MainActor
// But this code may not be on the MainActor.
let fetchRequest = MessageEntity.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "channel == %ld AND toUser == nil AND isEmoji == false", channel.index)
let objects = (try? context.fetch(fetchRequest)) ?? [MessageEntity]()
for object in objects {
context.delete(object)
}
try context.save()
} catch let error as NSError {
Logger.data.error("\(error.localizedDescription, privacy: .public)")

View file

@ -162,8 +162,13 @@ struct ChannelList: View {
Button(role: .destructive) {
Task {
await MeshPackets.shared.deleteChannelMessages(channel: channelToDeleteMessages!)
context.refresh(myInfo, mergeChanges: true)
channelToDeleteMessages = nil
await MainActor.run {
context.refresh(channel, mergeChanges: true)
context.refresh(myInfo, mergeChanges: true)
// Reset state
channelToDeleteMessages = nil
}
}
} label: {
Text("Delete")

View file

@ -70,7 +70,7 @@ struct AppSettings: View {
}
#endif
}
Section(header: Text("environment")) {
Section(header: Text("Environment")) {
VStack(alignment: .leading) {
Toggle(isOn: $environmentEnableWeatherKit) {
Label("Weather Conditions", systemImage: "cloud.sun")

View file

@ -8,6 +8,7 @@
import SwiftUI
import UniformTypeIdentifiers
import OSLog
import CoreData
enum CertificateImportType {
case p12
@ -15,6 +16,15 @@ enum CertificateImportType {
}
struct TAKServerConfig: View {
@Environment(\.managedObjectContext) var context
@EnvironmentObject var accessoryManager: AccessoryManager
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \ChannelEntity.index, ascending: true)],
predicate: NSPredicate(format: "role > 0"),
animation: .default
) private var channels: FetchedResults<ChannelEntity>
@StateObject private var takServer = TAKServerManager.shared
@State private var showingFileImporter = false
@State private var importType: CertificateImportType = .p12
@ -140,6 +150,17 @@ struct TAKServerConfig: View {
.foregroundColor(.secondary)
}
if !channels.isEmpty {
Picker(selection: $takServer.channel) {
ForEach(channels, id: \.index) { channel in
channelLabel(channel)
.tag(Int(channel.index))
}
} label: {
Label("TAK Channel Index", systemImage: "bubble.left.and.bubble.right")
}
}
if takServer.isRunning {
Button {
Task {
@ -152,7 +173,7 @@ struct TAKServerConfig: View {
} header: {
Text("Configuration")
} footer: {
Text("Secure mTLS connection on port 8089. Both server and client certificates are required.")
Text("Secure mTLS connection on port 8089. Both server and client certificates are required. TAK Channel Index selects the channel index where TAK messages will be sent.")
}
}
@ -280,6 +301,21 @@ struct TAKServerConfig: View {
}
// MARK: - Channel Label
@ViewBuilder
private func channelLabel(_ channel: ChannelEntity) -> some View {
if channel.name?.isEmpty ?? false {
if channel.role == 1 {
Text(String("PrimaryChannel").camelCaseToWords())
} else {
Text(String("Channel \(channel.index)").camelCaseToWords())
}
} else {
Text(String(channel.name ?? "Channel \(channel.index)").camelCaseToWords())
}
}
// MARK: - Import Handlers
private func handleP12Import(_ result: Result<[URL], Error>) {