diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index b02a5950..a2333832 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -2043,7 +2043,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.7.0; + MARKETING_VERSION = 2.7.1; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -2076,7 +2076,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.7.0; + MARKETING_VERSION = 2.7.1; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -2107,7 +2107,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.7.0; + MARKETING_VERSION = 2.7.1; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2139,7 +2139,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.7.0; + MARKETING_VERSION = 2.7.1; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/Meshtastic/Accessory/Accessory Manager/AccessoryManager+FromRadio.swift b/Meshtastic/Accessory/Accessory Manager/AccessoryManager+FromRadio.swift index 82f93253..7cc969fe 100644 --- a/Meshtastic/Accessory/Accessory Manager/AccessoryManager+FromRadio.swift +++ b/Meshtastic/Accessory/Accessory Manager/AccessoryManager+FromRadio.swift @@ -164,6 +164,10 @@ extension AccessoryManager { if moduleConfigPacket.payloadVariant == ModuleConfig.OneOf_PayloadVariant.cannedMessage(moduleConfigPacket.cannedMessage) { try? getCannedMessageModuleMessages(destNum: deviceNum, wantResponse: true) } + // Get the Ringtone if the Module is External Notifications + if moduleConfigPacket.payloadVariant == ModuleConfig.OneOf_PayloadVariant.externalNotification(moduleConfigPacket.externalNotification) { + try? getRingtone(destNum: deviceNum, wantResponse: true) + } } func handleDeviceMetadata(_ metadata: DeviceMetadata) { diff --git a/Meshtastic/Accessory/Accessory Manager/AccessoryManager+ToRadio.swift b/Meshtastic/Accessory/Accessory Manager/AccessoryManager+ToRadio.swift index fab276cf..2b2735e0 100644 --- a/Meshtastic/Accessory/Accessory Manager/AccessoryManager+ToRadio.swift +++ b/Meshtastic/Accessory/Accessory Manager/AccessoryManager+ToRadio.swift @@ -47,6 +47,43 @@ extension AccessoryManager { try await send(toRadio, debugDescription: logString) } } + + public func getRingtone(destNum: Int64, wantResponse: Bool) throws { + guard let deviceNum = self.activeConnection?.device.num else { + Logger.services.error("Error while sending RtttlConfig request. No active device.") + throw AccessoryError.ioFailed("No active device") + } + + var adminPacket = AdminMessage() + adminPacket.getRingtoneRequest = true + + var meshPacket: MeshPacket = MeshPacket() + meshPacket.to = UInt32(destNum) + meshPacket.from = UInt32(deviceNum) + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. Int64 { var adminPacket = AdminMessage() diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index d1e3c40e..76c1da96 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -585,8 +585,9 @@ func adminAppPacket (packet: MeshPacket, context: NSManagedObjectContext) { upsertTelemetryModuleConfigPacket(config: moduleConfig.telemetry, nodeNum: Int64(packet.from), context: context) } } else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getRingtoneResponse(adminMessage.getRingtoneResponse) { - let ringtone = adminMessage.getRingtoneResponse - upsertRtttlConfigPacket(ringtone: ringtone, nodeNum: Int64(packet.from), context: context) + if let rt = try? RTTTLConfig(serializedBytes: packet.decoded.payload) { + upsertRtttlConfigPacket(ringtone: rt.ringtone, nodeNum: Int64(packet.from), context: context) + } } else { Logger.mesh.error("πŸ•ΈοΈ MESH PACKET received Admin App UNHANDLED \((try? packet.decoded.jsonString()) ?? "JSON Decode Failure", privacy: .public)") } diff --git a/Meshtastic/Views/Connect/Connect.swift b/Meshtastic/Views/Connect/Connect.swift index 596e794b..65880035 100644 --- a/Meshtastic/Views/Connect/Connect.swift +++ b/Meshtastic/Views/Connect/Connect.swift @@ -314,7 +314,7 @@ struct Connect: View { .textCase(nil) } } - + .scrollContentBackground(.hidden) HStack(alignment: .center) { Spacer() #if targetEnvironment(macCatalyst) @@ -338,8 +338,9 @@ struct Connect: View { Spacer() } .padding(.bottom, 10) - .background(Color(.tertiarySystemGroupedBackground)) + } + .background(Color(.systemGroupedBackground)) .navigationTitle("Connect") .navigationBarItems( leading: MeshtasticLogo(), diff --git a/Meshtastic/Views/Helpers/RXTXIndicatorView.swift b/Meshtastic/Views/Helpers/RXTXIndicatorView.swift index b04cad89..d7e57326 100644 --- a/Meshtastic/Views/Helpers/RXTXIndicatorView.swift +++ b/Meshtastic/Views/Helpers/RXTXIndicatorView.swift @@ -29,7 +29,16 @@ struct RXTXIndicatorWidget: View { } } } - self.isPopoverOpen.toggle() + #if targetEnvironment(macCatalyst) + if #available(macOS 26.0, *) { + // Dont show popover that crashes on mac 26 + } else { + self.isPopoverOpen.toggle() + } + #else + self.isPopoverOpen.toggle() + #endif + }) { VStack(spacing: 3.0) { HStack(spacing: 2.0) { diff --git a/Meshtastic/Views/Messages/ChannelList.swift b/Meshtastic/Views/Messages/ChannelList.swift index fc1e8f96..f21a9292 100644 --- a/Meshtastic/Views/Messages/ChannelList.swift +++ b/Meshtastic/Views/Messages/ChannelList.swift @@ -13,13 +13,9 @@ struct ChannelList: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var accessoryManager: AccessoryManager - - @Binding - var node: NodeInfoEntity? - - @Binding - var channelSelection: ChannelEntity? - + @Binding var node: NodeInfoEntity? + @Binding var channelSelection: ChannelEntity? + @State private var channelToDeleteMessages: ChannelEntity? @State private var isPresentingDeleteChannelMessagesConfirm: Bool = false @State private var isPresentingTraceRouteSentAlert = false @State private var showingHelp = false @@ -123,7 +119,7 @@ struct ChannelList: View { if channel.allPrivateMessages.count > 0 { Button(role: .destructive) { isPresentingDeleteChannelMessagesConfirm = true - channelSelection = channel + channelToDeleteMessages = channel } label: { Label("Delete Messages", systemImage: "trash") } @@ -158,9 +154,9 @@ struct ChannelList: View { titleVisibility: .visible ) { Button(role: .destructive) { - deleteChannelMessages(channel: channelSelection!, context: context) + deleteChannelMessages(channel: channelToDeleteMessages!, context: context) context.refresh(myInfo, mergeChanges: true) - channelSelection = nil + channelToDeleteMessages = nil } label: { Text("Delete") } diff --git a/Meshtastic/Views/Messages/ChannelMessageList.swift b/Meshtastic/Views/Messages/ChannelMessageList.swift index 99382d5b..9cc91658 100644 --- a/Meshtastic/Views/Messages/ChannelMessageList.swift +++ b/Meshtastic/Views/Messages/ChannelMessageList.swift @@ -130,7 +130,6 @@ struct ChannelMessageList: View { TapbackResponses(message: message) { appState.unreadChannelMessages = myInfo.unreadMessages - context.refresh(myInfo, mergeChanges: true) } HStack { @@ -159,7 +158,9 @@ struct ChannelMessageList: View { .frame(maxWidth: .infinity) .id(message.messageId) .onAppear { - markMessagesAsRead() + if !message.read { + markMessagesAsRead() + } } } Color.clear diff --git a/Meshtastic/Views/Messages/TextMessageField/TextMessageField.swift b/Meshtastic/Views/Messages/TextMessageField/TextMessageField.swift index 4a4767ea..67bd7618 100644 --- a/Meshtastic/Views/Messages/TextMessageField/TextMessageField.swift +++ b/Meshtastic/Views/Messages/TextMessageField/TextMessageField.swift @@ -68,8 +68,8 @@ struct TextMessageField: View { } } .padding(15) - Divider() if isFocused { + Divider() HStack { Spacer() AlertButton { typingMessage += "πŸ”” Alert Bell Character! \u{7}" } diff --git a/Meshtastic/Views/Messages/UserList.swift b/Meshtastic/Views/Messages/UserList.swift index b1b771ad..b7119f72 100644 --- a/Meshtastic/Views/Messages/UserList.swift +++ b/Meshtastic/Views/Messages/UserList.swift @@ -18,10 +18,9 @@ struct UserList: View { @State private var showingHelp = false @State private var showingTrustConfirm: Bool = false @StateObject private var filters: NodeFilterParameters = NodeFilterParameters() - @Binding var node: NodeInfoEntity? @Binding var userSelection: UserEntity? - + @State private var userToDeleteMessages: UserEntity? @State private var isPresentingDeleteUserMessagesConfirm: Bool = false private func fetchUsers(withFilters: NodeFilterParameters) -> [UserEntity] { @@ -152,7 +151,7 @@ struct UserList: View { if user.messageList.count > 0 { Button(role: .destructive) { isPresentingDeleteUserMessagesConfirm = true - userSelection = user + userToDeleteMessages = user } label: { Label("Delete Messages", systemImage: "trash") } @@ -164,7 +163,7 @@ struct UserList: View { titleVisibility: .visible ) { Button(role: .destructive) { - deleteUserMessages(user: userSelection!, context: context) + deleteUserMessages(user: userToDeleteMessages!, context: context) context.refresh(node!.user!, mergeChanges: true) } label: { Text("Delete") diff --git a/Meshtastic/Views/Nodes/MeshMap.swift b/Meshtastic/Views/Nodes/MeshMap.swift index 812c887b..9d8911ab 100644 --- a/Meshtastic/Views/Nodes/MeshMap.swift +++ b/Meshtastic/Views/Nodes/MeshMap.swift @@ -74,6 +74,7 @@ struct MeshMap: View { .mapControlVisibility(.automatic) } .controlSize(.regular) + .offset(y: 100) .onMapCameraChange(frequency: MapCameraUpdateFrequency.continuous, { context in distance = context.camera.distance }) @@ -126,7 +127,7 @@ struct MeshMap: View { } .sheet(isPresented: $editingSettings) { MapSettingsForm(traffic: $showTraffic, pointsOfInterest: $showPointsOfInterest, mapLayer: $selectedMapLayer, meshMap: $isMeshMap, enabledOverlayConfigs: $enabledOverlayConfigs) - .presentationDetents([.fraction(0.85), .large]) + .presentationDetents([.large]) } .onChange(of: router.navigationState) { diff --git a/Meshtastic/Views/Settings/RouteRecorder.swift b/Meshtastic/Views/Settings/RouteRecorder.swift index 3c7bd81a..6a0fc539 100644 --- a/Meshtastic/Views/Settings/RouteRecorder.swift +++ b/Meshtastic/Views/Settings/RouteRecorder.swift @@ -49,6 +49,16 @@ struct RouteRecorder: View { } .mapStyle(mapStyle) } + .mapControls { + MapScaleView(scope: routerecorderscope) + .mapControlVisibility(.automatic) + MapPitchToggle(scope: routerecorderscope) + .mapControlVisibility(.automatic) + MapCompass(scope: routerecorderscope) + .mapControlVisibility(.automatic) + } + .controlSize(.regular) + .offset(y: 100) .mapScope(routerecorderscope) .safeAreaInset(edge: .bottom) { ZStack { diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index 329157c9..c8ce9148 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -236,7 +236,7 @@ struct Settings: View { } } - if isModuleSupported(.audioConfig) { + if isModuleSupported(.extnotifConfig) { NavigationLink(value: SettingsNavigationState.ringtone) { Label { Text("Ringtone")