-
-
Notifications
You must be signed in to change notification settings - Fork 177
2.7.6 Working Changes #1479
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
2.7.6 Working Changes #1479
Conversation
* Remove extra want config call when adding a contact * App badge and unnecessary notification fixes (#1455) * - Fix for app badge not going to zero if a message arrives while you have that chat open - Fix for push notifications popping up when a message is received while that chat is open * Fix for cancelling notifications, works now. And scroll to bottom of conversation upon new message * Fix: Channels help grammer fix (#1452) * remove outdated TCP not available on Apple devices (#1450) * Update initial onboarding view * remove toggle gating for mac * Update crash reporting opt out in real time * Update onboarding text * Use mDNS text records for node name * TCP IP and port on the connection screen * Hide app icon chooser on mac * Infinite loop hang bugfixes and performance improvements for both `UserMessageList` and `ChannelMessageList` (#1465) * 2.7.5 Working Changes (#1460) * Remove extra want config call when adding a contact * App badge and unnecessary notification fixes (#1455) * - Fix for app badge not going to zero if a message arrives while you have that chat open - Fix for push notifications popping up when a message is received while that chat is open * Fix for cancelling notifications, works now. And scroll to bottom of conversation upon new message * Fix: Channels help grammer fix (#1452) * remove outdated TCP not available on Apple devices (#1450) * Update initial onboarding view * remove toggle gating for mac * Update crash reporting opt out in real time * Update onboarding text --------- Co-authored-by: Gnome Adrift <[email protected]> Co-authored-by: Zain Kergaye <[email protected]> Co-authored-by: NillRudd <[email protected]> * UserEntity: add mostRecentMessage and unreadMessages with early exit when lastMessage is nil, and fetch 1 row (not N) otherwise * UserList: replace 5 slow calls to user.messageList with new fast calls * NodeList: always put the connected node at the top of list (if it matches the node filters) * ChannelEntity: add faster mostRecentPrivateMessage and unreadMessages which fetch 1 row (not N) * ChannelList: replace 5 calls to channel.allPrivateMessage with new fast calls * Fix incorrect appState.unreadDirectMessages calculations * MyInfoEntity: also fix unreadMessages count here to be fast, and use it for appState.unreadChannelMessages * UserMessageList: use @fetchrequest to prevent the N^2 behavior that was happening in calls to allPrivateMessages * Refactor ChannelEntityExtension and MyInfoEntityExtension to be more similar to UserEntityExtension * Remove SwiftUI-infinite-loop-causing `.id(redrawTapbacksTrigger)` in ChannelMessageList and UserMessageList (duplicate row ids) * MyInfoEntityExtension: exclude emoji tapbacks (which never get marked as read anyway) from unread message count * Add SaveChannelLinkData so MessageText and MeshtasticApp can use .sheet(item: ...) and avoid infinite loop hang due to Binding rebuild * ChannelMessageList and UserMessageList: switch to stable messageId for ForEach SwiftUI row identity * ChannelMessageList and UserMessageList: debouncedScrollToBottom; keyboardWillShowNotification/keyboardDidShowNotification * ChannelMessageList and UserMessageList: scroll to bottom onFirstAppear * ChannelMessageList and UserMessageList: block spurious markMessagesAsRead when this View is not active --------- Co-authored-by: Garth Vander Houwen <[email protected]> Co-authored-by: Gnome Adrift <[email protected]> Co-authored-by: Zain Kergaye <[email protected]> Co-authored-by: NillRudd <[email protected]> * message-list-performance: revert scrolling changes (#1472) * Revert e0f0b4a (ChannelMessageList and UserMessageList: scroll to bottom onFirstAppear) * Revert "ChannelMessageList and UserMessageList: debouncedScrollToBottom; keyboardWillShowNotification/keyboardDidShowNotification" This reverts commit ee1a7c4. --------- Co-authored-by: Gnome Adrift <[email protected]> Co-authored-by: Zain Kergaye <[email protected]> Co-authored-by: NillRudd <[email protected]> Co-authored-by: Jake-B <[email protected]> Co-authored-by: Mike Robbins <[email protected]>
…d from #1424 (#1477) * Fix: "Retrieving nodes" significantly slower after reconnect (#1424) The node database retrieval was calling context.save() for every single NodeInfo packet received (250 saves for 250 nodes). This caused severe performance degradation on reconnect when CoreData had accumulated state. Root Cause: - nodeInfoPacket() called context.save() immediately for each node - With 250 nodes, this meant 250 individual CoreData save operations - On first connection, CoreData is fresh and fast - On reconnect, CoreData has accumulated change tracking, undo management, and memory pressure, making each save progressively slower - This resulted in 10+ second retrieval times vs 1-2 seconds initially Solution: - Added deferSave parameter to nodeInfoPacket() function - During database retrieval (.retrievingDatabase state), defer all saves - Perform a single batch save when database retrieval completes (when NONCE_ONLY_DB configCompleteID is received) - This reduces 250 saves to 1 save Performance Impact: - Eliminates N individual saves during node database sync - Reduces database retrieval time back to 1-2 seconds on reconnect - Matches first-connection performance consistently Fixes #1424 * Revert *MessageListUnified files --------- Co-authored-by: Martin Bogomolni <[email protected]> Co-authored-by: Jake-B <[email protected]>
…ckable at the highest zoom levels (#1478) Co-authored-by: Garth Vander Houwen <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR updates the app to version 2.7.6 with improvements focused on performance optimization, UI refinements, and message handling enhancements.
Key changes:
- Optimized Core Data operations by introducing deferred saves during database retrieval and using count-based queries for unread messages
- Refactored channel/message link handling to use a new
SaveChannelLinkDatastruct for cleaner state management - Enhanced map visualization with fuzzing for overlapping nodes and precision circle indicators
- Improved TCP device display by showing connection details (IP:Port) in the UI
Reviewed Changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| SaveChannelQRCode.swift | Adds SaveChannelLinkData struct to support refactored channel link handling |
| AppSettings.swift | Hides app icon picker on macCatalyst platform |
| DeviceOnboarding.swift | Simplifies privacy notice text |
| NodeList.swift | Sorts nodes to display connected device first |
| ShareContactQRDialog.swift | Adds manuallyVerified field to shared contacts |
| MapSettingsForm.swift | Removes redundant tap gesture handler for node history toggle |
| MeshMapContent.swift | Implements precision-based coordinate fuzzing and adds precision circles for reduced-precision positions |
| UserMessageList.swift | Optimizes message fetching with @FetchRequest and precomputed previous message lookup |
| UserList.swift | Uses computed properties to avoid redundant message list access |
| MessageText.swift | Refactors to use SaveChannelLinkData for channel link state management |
| ChannelMessageList.swift | Implements same optimizations as UserMessageList |
| ChannelList.swift | Uses computed properties for message checks |
| Connect.swift | Displays IP:Port for TCP connections |
| MeshtasticApp.swift | Disables Datadog analytics and uses SaveChannelLinkData for URL handling |
| MeshPackets.swift | Adds deferSave parameter for batch processing and uses context-aware unread count methods |
| UserEntityExtension.swift | Introduces efficient fetch request-based methods for messages and unread counts |
| PositionEntityExtension.swift | Adds coordinate fuzzing logic for overlapping nodes |
| MyInfoEntityExtension.swift | Implements efficient count-based unread message tracking |
| ChannelEntityExtension.swift | Adds fetch request methods and optimized unread counting |
| TCPTransport.swift | Parses service TXT records to extract node names and stores connection details |
| Device.swift | Adds connectionDetails property for additional connection information |
| AccessoryManager.swift | Implements batch save after database retrieval for performance |
| AccessoryManager+MQTT.swift | Updates to use context-aware unread message methods |
| AccessoryManager+FromRadio.swift | Defers saves during database retrieval |
| project.pbxproj | Updates marketing version to 2.7.6 |
| Localizable.xcstrings | Updates localization strings for privacy notice |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift
Outdated
Show resolved
Hide resolved
Co-authored-by: Copilot <[email protected]>
…wift Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
* Keep list of previous manual connections * More descriptive manual connection rows * Merge fixes and new way to show IP on Connect view --------- Co-authored-by: Jake-B <[email protected]>
* Add identification for node that relayed text messages and add count for ammount of relayers of your message * Ack Relays
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 40 out of 41 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import OSLog | ||
|
|
||
| struct ShareContactQRDialog: View { | ||
| let manuallyVerified = false |
Copilot
AI
Oct 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The manuallyVerified constant is hardcoded to false. Consider making this a parameter or property if manual verification functionality will be added in the future, or add a comment explaining why it's always false.
| let manuallyVerified = false | |
| let manuallyVerified: Bool |
| let isCurrentUser: Bool | ||
| @Binding var isShowingDeleteConfirmation: Bool | ||
| let onReply: () -> Void | ||
| @State var relayDisplay: String? = nil |
Copilot
AI
Oct 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The relayDisplay state variable should be marked as private to encapsulate internal view state and prevent unintended external access.
| @State var relayDisplay: String? = nil | |
| @State private var relayDisplay: String? = nil |
| // Store the Data in UserDefaults | ||
| UserDefaults.standard.set(data, forKey: Keys.manualConnections.rawValue) | ||
| } catch { | ||
| print("Failed to encode manualConnections: \(error)") |
Copilot
AI
Oct 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use Logger instead of print() for error logging to maintain consistency with the rest of the codebase and enable proper log level filtering.
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
* MeshMap: change onMapCameraChange frequency to .onEnd so that zooming doesn't cause continuous SwiftUI reevaluation on every frame * MeshMapContent: factor out reducedPrecisionMapCircles into a separate function * MeshMapContent: when multiple reducedPrecisionCircles have the same (lat,lon,radius), just draw one (big perf boost in dense areas)
* NodeMapContent: move Route Lines out of ForEach * NodeMapContent: move Convex Hull out of ForEach * NodeMapContent: Replace `position.nodePosition?` with `node` * NodeMapContent: drop unnecessary LazyVStack in showNodeHistory * NodeMapContent: hoist out nodeColorSwift * Move lineCoords, loraCoords calculations within showRouteLines, showConvexHull respectively * Hoist out repeated node.metadata?.positionFlags lookups / PositionFlags creation * NodeMapContent: remove unused @State * NodeMapSwiftUI: add NodeMapContentEquatableWrapper and NodeMapContentSignature to prevent frequent NodeMapContent recomputation and infinite render loops * NodeMapSwiftUI: disable animation during SwiftUI transactions * NodeMapContent: hoist nodeBorderColor and set allowsHitTesting(false) on history point views * NodeMapContent: prerenderHistoryPointCircle and prerenderHistoryPointArrow to avoid thousands of vector draw operations * NodeMapContent: Shared coordinate list for Route Lines and Convex Hull * NodeMapContent.prerenderHistoryPointArrow: add .frame(width: 16, height: 16)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 48 out of 49 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return false // First message will have no timestamp | ||
| } | ||
|
|
||
| func relayDisplay() -> String? { |
Copilot
AI
Oct 30, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra whitespace before opening brace. Remove the extra space for consistency with Swift style guidelines.
| func relayDisplay() -> String? { | |
| func relayDisplay() -> String? { |
| guard self.relayNode != 0 else { return nil } | ||
| let context = PersistenceController.shared.container.viewContext | ||
|
|
||
| let relaySuffix = Int64(self.relayNode & 0xFF) | ||
| let request: NSFetchRequest<UserEntity> = UserEntity.fetchRequest() | ||
| request.predicate = NSPredicate(format: "(num & 0xFF) == %lld", relaySuffix) | ||
|
|
||
| do { | ||
| let users = try context.fetch(request) | ||
|
|
||
| // If exactly one match is found, return its name | ||
| if users.count == 1, let name = users.first?.longName, !name.isEmpty { | ||
| return "\(name)" | ||
| } | ||
|
|
||
| // If no exact match, find the node with the smallest hopsAway | ||
| if let closestNode = users.min(by: { lhs, rhs in | ||
| guard let lhsHops = lhs.userNode?.hopsAway, let rhsHops = rhs.userNode?.hopsAway else { | ||
| return false | ||
| } | ||
| return lhsHops < rhsHops | ||
| }), let name = closestNode.longName, !name.isEmpty { | ||
| return "\(name)" | ||
| } | ||
|
|
||
| // Fallback to hex node number if no matches | ||
| return String(format: "Node 0x%02X", UInt32(self.relayNode & 0xFF)) | ||
|
|
||
| } catch { | ||
| return String(format: "Node 0x%02X", UInt32(self.relayNode & 0xFF)) | ||
| } | ||
| } |
Copilot
AI
Oct 30, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent indentation using spaces instead of tabs. Lines 44, 47-75 use spaces while the rest of the file uses tabs. Normalize to use tabs throughout for consistency.
| guard self.relayNode != 0 else { return nil } | |
| let context = PersistenceController.shared.container.viewContext | |
| let relaySuffix = Int64(self.relayNode & 0xFF) | |
| let request: NSFetchRequest<UserEntity> = UserEntity.fetchRequest() | |
| request.predicate = NSPredicate(format: "(num & 0xFF) == %lld", relaySuffix) | |
| do { | |
| let users = try context.fetch(request) | |
| // If exactly one match is found, return its name | |
| if users.count == 1, let name = users.first?.longName, !name.isEmpty { | |
| return "\(name)" | |
| } | |
| // If no exact match, find the node with the smallest hopsAway | |
| if let closestNode = users.min(by: { lhs, rhs in | |
| guard let lhsHops = lhs.userNode?.hopsAway, let rhsHops = rhs.userNode?.hopsAway else { | |
| return false | |
| } | |
| return lhsHops < rhsHops | |
| }), let name = closestNode.longName, !name.isEmpty { | |
| return "\(name)" | |
| } | |
| // Fallback to hex node number if no matches | |
| return String(format: "Node 0x%02X", UInt32(self.relayNode & 0xFF)) | |
| } catch { | |
| return String(format: "Node 0x%02X", UInt32(self.relayNode & 0xFF)) | |
| } | |
| } | |
| guard self.relayNode != 0 else { return nil } | |
| let context = PersistenceController.shared.container.viewContext | |
| let relaySuffix = Int64(self.relayNode & 0xFF) | |
| let request: NSFetchRequest<UserEntity> = UserEntity.fetchRequest() | |
| request.predicate = NSPredicate(format: "(num & 0xFF) == %lld", relaySuffix) | |
| do { | |
| let users = try context.fetch(request) | |
| // If exactly one match is found, return its name | |
| if users.count == 1, let name = users.first?.longName, !name.isEmpty { | |
| return "\(name)" | |
| } | |
| // If no exact match, find the node with the smallest hopsAway | |
| if let closestNode = users.min(by: { lhs, rhs in | |
| guard let lhsHops = lhs.userNode?.hopsAway, let rhsHops = rhs.userNode?.hopsAway else { | |
| return false | |
| } | |
| return lhsHops < rhsHops | |
| }), let name = closestNode.longName, !name.isEmpty { | |
| return "\(name)" | |
| } | |
| // Fallback to hex node number if no matches | |
| return String(format: "Node 0x%02X", UInt32(self.relayNode & 0xFF)) | |
| } catch { | |
| return String(format: "Node 0x%02X", UInt32(self.relayNode & 0xFF)) | |
| } | |
| } |
| // Store the Data in UserDefaults | ||
| UserDefaults.standard.set(data, forKey: Keys.manualConnections.rawValue) | ||
| } catch { | ||
| print("Failed to encode manualConnections: \(error)") |
Copilot
AI
Oct 30, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use Logger instead of print for consistent logging throughout the app. Replace with Logger.data.error(\"Failed to encode manualConnections: \\(error.localizedDescription, privacy: .public)\").
| import Foundation | ||
|
|
||
| // Maintains an observable list of devices that's backed by UserDefaults | ||
| public class ManualConnectionList: ObservableObject { |
Copilot
AI
Oct 30, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The class is marked as public but is in an internal module. Change to final class ManualConnectionList: ObservableObject for better performance (final prevents subclassing and enables optimizations) and appropriate access control.
| public class ManualConnectionList: ObservableObject { | |
| final class ManualConnectionList: ObservableObject { |
| if let nodeId = txtRecords["id"], nodeId.count > 4 { | ||
| if nodeNameString.count > 0 { | ||
| nodeNameString += "_" | ||
| } | ||
| nodeNameString += String(decoding: nodeId.suffix(4), as: UTF8.self) | ||
| } |
Copilot
AI
Oct 30, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Magic number 4 is used without explanation. Add a comment or named constant to clarify why the last 4 characters of the node ID are used, e.g., let nodeIdSuffixLength = 4 // Last 4 hex digits of node ID.
| VStack (alignment: .leading) { | ||
| Text("Last seen device:") | ||
| Text("\(String(describing: device))") | ||
| } |
Copilot
AI
Oct 30, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using String(describing:) on a type that already conforms to CustomStringConvertible is redundant. Simplify to Text(device.description) or use string interpolation directly: Text(\"\\(device)\").
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
…ven when configured hops was higher (#1495) * fix hops setting in dms * Fix hops for exchange position * Final fix
Uh oh!
There was an error while loading. Please reload this page.