Skip to content

fix: restore favorited nodes to the top of the admin/configuration node picker#1977

Merged
garthvh merged 1 commit into
meshtastic:mainfrom
bruschill:fix/admin-picker-favorites-on-top
Jun 23, 2026
Merged

fix: restore favorited nodes to the top of the admin/configuration node picker#1977
garthvh merged 1 commit into
meshtastic:mainfrom
bruschill:fix/admin-picker-favorites-on-top

Conversation

@bruschill

Copy link
Copy Markdown
Contributor

What changed?

Favorited nodes are once again listed at the top of the Remote Admin / Configuration node picker (Settings.swift), above non-favorites. Within each group, the existing most-recently-heard order is preserved.

The ordering is applied via a new helper, NodeInfoEntity.adminPickerOrder(_:), a stable favorites-first partition over the picker's @Query results. The picker's ForEach now iterates a sortedNodes computed property instead of the raw @Query.

Why did it change?

The CoreData→SwiftData conversion (#1668) replaced the picker's multi-key @FetchRequest sort with @Query(sort: \.lastHeard, .reverse), which silently dropped favorite as a sort key. As a result, favorited nodes were no longer surfaced at the top of the list — they landed wherever their last-heard time placed them.

Favorite-on-top can't be expressed as a SwiftData SortDescriptor because favorite is a Bool and Bool isn't Comparable (so it's not a valid SortDescriptor key on a non-NSObject @Model). It's restored in memory instead, as a stable O(n) partition over the @Query's already-lastHeard-descending results — rather than an O(n log n) re-sort — so it stays cheap to evaluate per render and deterministic across re-renders.

How is this tested?

Adds SettingsNodeSortTests (Swift Testing, 7 cases) exercising NodeInfoEntity.adminPickerOrder(_:):

  • favorites hoisted above non-favorites regardless of recency (the regression itself),
  • recency order preserved within both the favorites and non-favorites groups,
  • combined/interleaved ordering,
  • a never-heard (lastHeard == nil) favorite still sorts above a recently-heard non-favorite,
  • already-ordered and empty-input edge cases.

All 7 pass locally (iPhone 16, iOS 18 simulator).

Screenshots/Videos (when applicable)

N/A — ordering change in the existing admin node picker; no UI structure change.

Checklist

  • My code adheres to the project's coding and style guidelines.
  • I have conducted a self-review of my code.
  • I have commented my code, particularly in complex areas.
  • I have verified whether these changes require updates to the in-app documentation under docs/user/ or docs/developer/, and updated accordingly. No doc update needed (no documented behavior changes) — please apply the skip-docs-check label.
  • I have tested the change to ensure that it works as intended.

…de picker

The CoreData→SwiftData conversion (meshtastic#1668) replaced the admin node picker's
multi-key @fetchrequest sort with @query(sort: \.lastHeard, .reverse), which
dropped `favorite` as a sort key. Favorited nodes were no longer surfaced at
the top of the Remote Admin / Configuration list.

Favorite-on-top can't be expressed as a SwiftData SortDescriptor because
`favorite` is a `Bool` and `Bool` isn't `Comparable`, so it's restored in
memory via NodeInfoEntity.adminPickerOrder(_:) — a stable, O(n) favorites-first
partition over the @query's already-lastHeard-descending results. Using a
partition (rather than a full re-sort) keeps it cheap to call per render and
deterministic across re-renders, avoiding the kind of per-render work the node
lists were recently tuned to minimize.

Adds SettingsNodeSortTests covering favorites hoisting, recency preservation
within each group, never-heard favorites, and edge cases.
@garthvh garthvh merged commit c3b4791 into meshtastic:main Jun 23, 2026
2 of 3 checks passed
@bruschill bruschill deleted the fix/admin-picker-favorites-on-top branch June 30, 2026 01:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants