mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
* Delete Messages fix * Bump version to 2.7.9 * Bump widgets version * TAK Server channel index picker Create a settings picker for the TAK Server's channel index. This allows users to specify TAK traffic to use the non-primary channel to help reduce channel congestion. * Changed capitalization from 'environment' to 'Environment' for section header. (#1591) * Add Danish (da) translations — resolves merge conflicts from PR #1609 (#1612) * Initial plan * Add Danish (da) translations from PR #1609 Resolves merge conflicts from PR #1609 by adding Danish translations to the Localizable.xcstrings file. The PR adds Danish translation strings throughout the app while preserving all existing translations for other languages. Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com> * Migrate test project to Swift Testing and add connect view and router tests (#1643) * Migrate to Swift Testing and add connect view tests - Convert RouterTests.swift from XCTest to Swift Testing (@Suite, @Test, #expect, #require) - Create ConnectViewTests.swift with tests for connect view child types: - Device struct (creation, signal strength, RSSI, description, codable) - TransportType enum (cases, raw values, codable) - ConnectionState enum (equality, codable) - BLESignalStrength enum (raw values, init) - TransportStatus enum (equality) - NavigationState (defaults, tabs, sub-states) - InvalidVersion view (creation with versions) - ConnectedDevice view (connected/disconnected/MQTT states) - CircleText view (default/custom sizes, emoji) - BatteryCompact view (levels, nil, charging, plugged in) - SignalStrengthIndicator view (dimensions, strength levels) - Update Xcode project to include new test file Agent-Logs-Url: https://github.com/meshtastic/Meshtastic-Apple/sessions/d7bb7a89-2105-4fcb-96bc-7ec794467c74 Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com> * Fix signal strength test boundary conditions The getSignalStrength() method uses NSNumber.compare(.orderedDescending), which is a strict greater-than check. Fix the boundary test cases: - RSSI -65 is .normal (not .strong), since -65 is not > -65 - RSSI -85 is .weak (not .normal), since -85 is not > -85 - Add -64 → .strong and -84 → .normal as adjacent boundary tests Agent-Logs-Url: https://github.com/meshtastic/Meshtastic-Apple/sessions/4fcbc01e-cbea-4d11-b2c0-e923c6730d69 Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com> * Improve and complete router tests with comprehensive coverage Added tests for: - Custom initial state - Invalid scheme / unknown path handling (state unchanged) - navigateToNodeDetail public method - Messages edge cases: channelId only, userNum only, messageId only, non-numeric params - Nodes with non-numeric nodenum - Map with both nodenum+waypointId (nodeId priority), non-numeric params - Parameterized settings test covering all 31 SettingsNavigationState cases - State transitions: consecutive routes, invalid scheme preserves existing state Agent-Logs-Url: https://github.com/meshtastic/Meshtastic-Apple/sessions/f69b7352-21aa-494c-8864-31fc0f4b21b8 Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com> * Localizable update * Merge translations file --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com> Co-authored-by: Garth Vander Houwen <garthvh@yahoo.com> * Fix merge conflicts in PR #1614 (Spanish translations) (#1644) * 20% of strings translated to spanish * add more translations * add rest of translations * small fixes --------- Co-authored-by: Joel Pérez Izquierdo <joelperez91@gmail.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com> * fix typo in hop limit option description (#1631) O hop -> 0 hop --------- Co-authored-by: Jake-B <jake-b@users.noreply.github.com> Co-authored-by: Garth Vander Houwen <garthvh@yahoo.com> Co-authored-by: niccellular <79813408+niccellular@users.noreply.github.com> Co-authored-by: Austin Hargis <25471876+austinhargis@users.noreply.github.com> Co-authored-by: Ben Meadors <benmmeadors@gmail.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com> Co-authored-by: Joel Pérez Izquierdo <joelperez91@gmail.com> Co-authored-by: axunes <axunes@axunes.net>
This commit is contained in:
parent
6ee6579cdb
commit
54ff386c03
11 changed files with 13949 additions and 185 deletions
|
|
@ -157,6 +157,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)
|
||||
|
||||
|
|
@ -169,7 +171,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)")
|
||||
|
|
@ -179,7 +181,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)")
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@ final class TAKServerManager: ObservableObject {
|
|||
|
||||
// MARK: - Configuration (persisted via AppStorage)
|
||||
|
||||
@AppStorage("takServerChannel") var channel: Int = 0
|
||||
|
||||
@AppStorage("takServerEnabled") var enabled = false {
|
||||
didSet {
|
||||
Task {
|
||||
|
|
|
|||
|
|
@ -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)")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ struct LoRaConfig: View {
|
|||
.tag($0)
|
||||
}
|
||||
}
|
||||
Text("Sets the maximum number of hops, default is 3. Increasing hops also increases congestion and should be used carefully. O hop broadcast messages will not get ACKs.")
|
||||
Text("Sets the maximum number of hops, default is 3. Increasing hops also increases congestion and should be used carefully. 0 hop broadcast messages will not get ACKs.")
|
||||
.foregroundColor(.gray)
|
||||
.font(.callout)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
@EnvironmentObject var accessoryManager: AccessoryManager
|
||||
@Environment(\.managedObjectContext) var context
|
||||
|
|
@ -267,6 +277,17 @@ struct TAKServerConfig: View {
|
|||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
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 {
|
||||
|
|
@ -279,7 +300,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.")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -407,6 +428,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>) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue