feat(contacts): channel favourites — filter in Settings + context menu

Add per-channel favourite marking and a Settings filter (all/fav) for
the channel list, symmetric with the existing DM and Room filters.

NodePrefs:
- ch_fav_bitmask (uint64_t): bit i = channel i is marked favourite
- ch_fav_only (uint8_t): 0=show all channels, 1=favourites only
- SCHEMA_SENTINEL bumped to 0xC0DE0005

DataStore: save/load both new fields before the sentinel.

SettingsScreen: new CH_FILTER item ('Channels: all/fav') in the
Contacts section, positioned between DM_FILTER and ROOM_FILTER.

QuickMsgScreen:
- buildChannelList() skips non-favourite channels when ch_fav_only=1
- Channel context menu gains a 4th option 'Fav'/'Unfav' that toggles
  ch_fav_bitmask for the selected channel and rebuilds the list

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-05-27 17:33:52 +02:00
co-authored by Claude Sonnet 4.6
parent dd9c75d4f4
commit 5b40e91df3
3 changed files with 35 additions and 7 deletions
@@ -57,7 +57,7 @@ class SettingsScreen : public UIScreen {
TIMEZONE,
LOW_BAT,
// Contacts section
SECTION_CONTACTS, DM_FILTER, ROOM_FILTER,
SECTION_CONTACTS, DM_FILTER, CH_FILTER, ROOM_FILTER,
// Messages section
SECTION_MESSAGES,
MSG_SLOT_0, MSG_SLOT_1, MSG_SLOT_2, MSG_SLOT_3, MSG_SLOT_4,
@@ -533,6 +533,10 @@ class SettingsScreen : public UIScreen {
display.print("DM");
display.setCursor(display.valCol(), y);
display.print((p && p->dm_show_all) ? "all" : "fav");
} else if (item == CH_FILTER) {
display.print("Channels");
display.setCursor(display.valCol(), y);
display.print((p && p->ch_fav_only) ? "fav" : "all");
} else if (item == ROOM_FILTER) {
display.print("Rooms");
display.setCursor(display.valCol(), y);
@@ -783,6 +787,11 @@ public:
_dirty = true;
return true;
}
if (_selected == CH_FILTER && p && (left || right || enter)) {
p->ch_fav_only = p->ch_fav_only ? 0 : 1;
_dirty = true;
return true;
}
if (_selected == ROOM_FILTER && p && (left || right || enter)) {
p->room_fav_only = p->room_fav_only ? 0 : 1;
_dirty = true;