From f1930549ed993fad35d8383f714eb8e042a66211 Mon Sep 17 00:00:00 2001 From: Strycher Date: Sun, 19 Jul 2026 00:54:18 -0400 Subject: [PATCH] feat(#308): add Sensors to the contact type filter Sensors were the one advert type with no filter. advTypeSensor (4) already existed in the protocol and Contact.typeLabelRaw already returned 'Sensor', but ContactTypeFilter stopped at rooms, so sensor contacts could not be isolated in either the contacts list or discovery. - ContactTypeFilter gains `sensors`. Both exhaustive switches over it were found by the analyzer rather than by hand: the contacts list predicate and the search-hint text. - discovery_screen's predicate has a `default: return false`, so it compiled without a sensors case but would have silently shown an empty list. Added explicitly. - New l10n strings contacts_searchSensors and listFilter_sensors, regenerated across all 18 locales. The 17 non-English locales fall back to the English text and are recorded in untranslated.json, matching how existing strings are handled. - Persisted round-trip verified: UiViewStateService stores the filter by name with orElse -> ContactTypeFilter.all, so older persisted values cannot throw and `sensors` persists like the rest. Groundwork for the Contacts filter rail (#307), but useful on its own: the existing filter menu now offers Sensors. flutter analyze clean, dart format clean, 445 tests pass. --- lib/l10n/app_en.arb | 12 ++++++++++++ lib/l10n/app_localizations.dart | 12 ++++++++++++ lib/l10n/app_localizations_bg.dart | 8 ++++++++ lib/l10n/app_localizations_de.dart | 8 ++++++++ lib/l10n/app_localizations_en.dart | 8 ++++++++ lib/l10n/app_localizations_es.dart | 8 ++++++++ lib/l10n/app_localizations_fr.dart | 8 ++++++++ lib/l10n/app_localizations_hu.dart | 8 ++++++++ lib/l10n/app_localizations_it.dart | 8 ++++++++ lib/l10n/app_localizations_ja.dart | 8 ++++++++ lib/l10n/app_localizations_ko.dart | 8 ++++++++ lib/l10n/app_localizations_nl.dart | 8 ++++++++ lib/l10n/app_localizations_pl.dart | 8 ++++++++ lib/l10n/app_localizations_pt.dart | 8 ++++++++ lib/l10n/app_localizations_ru.dart | 8 ++++++++ lib/l10n/app_localizations_sk.dart | 8 ++++++++ lib/l10n/app_localizations_sl.dart | 8 ++++++++ lib/l10n/app_localizations_sv.dart | 8 ++++++++ lib/l10n/app_localizations_uk.dart | 8 ++++++++ lib/l10n/app_localizations_zh.dart | 8 ++++++++ lib/screens/contacts_screen.dart | 10 ++++++++++ lib/screens/discovery_screen.dart | 8 ++++++++ lib/utils/contact_filter_types.dart | 2 +- lib/widgets/list_filter_widget.dart | 5 +++++ 24 files changed, 192 insertions(+), 1 deletion(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 54044fb..88673f8 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -452,6 +452,17 @@ } } }, + "contacts_searchSensors": "Search {number}{str} Sensors...", + "@contacts_searchSensors": { + "placeholders": { + "number": { + "type": "int" + }, + "str": { + "type": "String" + } + } + }, "contacts_noUnreadContacts": "No unread contacts", "contacts_noContactsFound": "No contacts or groups found", "contacts_deleteContact": "Delete Contact", @@ -2132,6 +2143,7 @@ "listFilter_users": "Users", "listFilter_repeaters": "Repeaters", "listFilter_roomServers": "Room servers", + "listFilter_sensors": "Sensors", "listFilter_unreadOnly": "Unread only", "listFilter_newGroup": "New group", "pathTrace_you": "You", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 3af670b..5ddefeb 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -1924,6 +1924,12 @@ abstract class AppLocalizations { /// **'Search {number}{str} Room servers...'** String contacts_searchRoomServers(int number, String str); + /// No description provided for @contacts_searchSensors. + /// + /// In en, this message translates to: + /// **'Search {number}{str} Sensors...'** + String contacts_searchSensors(int number, String str); + /// No description provided for @contacts_noUnreadContacts. /// /// In en, this message translates to: @@ -6710,6 +6716,12 @@ abstract class AppLocalizations { /// **'Room servers'** String get listFilter_roomServers; + /// No description provided for @listFilter_sensors. + /// + /// In en, this message translates to: + /// **'Sensors'** + String get listFilter_sensors; + /// No description provided for @listFilter_unreadOnly. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_bg.dart b/lib/l10n/app_localizations_bg.dart index f6ec54a..f1f49ef 100644 --- a/lib/l10n/app_localizations_bg.dart +++ b/lib/l10n/app_localizations_bg.dart @@ -1022,6 +1022,11 @@ class AppLocalizationsBg extends AppLocalizations { return 'Търсене на $number$str сървъри в стаята...'; } + @override + String contacts_searchSensors(int number, String str) { + return 'Search $number$str Sensors...'; + } + @override String get contacts_noUnreadContacts => 'Няма непрочетени контакти'; @@ -3885,6 +3890,9 @@ class AppLocalizationsBg extends AppLocalizations { @override String get listFilter_roomServers => 'Сървъри на стая'; + @override + String get listFilter_sensors => 'Sensors'; + @override String get listFilter_unreadOnly => 'Само непрочетените'; diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 8ed1ad4..eac244b 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -1017,6 +1017,11 @@ class AppLocalizationsDe extends AppLocalizations { return 'Suche $number$str Raumserver...'; } + @override + String contacts_searchSensors(int number, String str) { + return 'Search $number$str Sensors...'; + } + @override String get contacts_noUnreadContacts => 'Keine ungesehene Kontakte'; @@ -3895,6 +3900,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get listFilter_roomServers => 'Raumserver'; + @override + String get listFilter_sensors => 'Sensors'; + @override String get listFilter_unreadOnly => 'Nicht gelesen'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 0c2f5a4..3f25139 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -1001,6 +1001,11 @@ class AppLocalizationsEn extends AppLocalizations { return 'Search $number$str Room servers...'; } + @override + String contacts_searchSensors(int number, String str) { + return 'Search $number$str Sensors...'; + } + @override String get contacts_noUnreadContacts => 'No unread contacts'; @@ -3820,6 +3825,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get listFilter_roomServers => 'Room servers'; + @override + String get listFilter_sensors => 'Sensors'; + @override String get listFilter_unreadOnly => 'Unread only'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index c738200..aee1983 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -1016,6 +1016,11 @@ class AppLocalizationsEs extends AppLocalizations { return 'Buscar $number$str servidores de sala...'; } + @override + String contacts_searchSensors(int number, String str) { + return 'Search $number$str Sensors...'; + } + @override String get contacts_noUnreadContacts => 'No contactos sin leer'; @@ -3882,6 +3887,9 @@ class AppLocalizationsEs extends AppLocalizations { @override String get listFilter_roomServers => 'Servidores de la sala'; + @override + String get listFilter_sensors => 'Sensors'; + @override String get listFilter_unreadOnly => 'Solo sin leer'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index b4e705a..04cd4e9 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -1023,6 +1023,11 @@ class AppLocalizationsFr extends AppLocalizations { return 'Rechercher $number$str room server...'; } + @override + String contacts_searchSensors(int number, String str) { + return 'Search $number$str Sensors...'; + } + @override String get contacts_noUnreadContacts => 'Aucun contact non lu'; @@ -3906,6 +3911,9 @@ class AppLocalizationsFr extends AppLocalizations { @override String get listFilter_roomServers => 'Serveurs de salle'; + @override + String get listFilter_sensors => 'Sensors'; + @override String get listFilter_unreadOnly => 'Messages non lus seulement'; diff --git a/lib/l10n/app_localizations_hu.dart b/lib/l10n/app_localizations_hu.dart index 7696db2..48f7f97 100644 --- a/lib/l10n/app_localizations_hu.dart +++ b/lib/l10n/app_localizations_hu.dart @@ -1022,6 +1022,11 @@ class AppLocalizationsHu extends AppLocalizations { return 'Keresés $number-ban $str...'; } + @override + String contacts_searchSensors(int number, String str) { + return 'Search $number$str Sensors...'; + } + @override String get contacts_noUnreadContacts => 'Nincs olvasatlan üzenetek'; @@ -3897,6 +3902,9 @@ class AppLocalizationsHu extends AppLocalizations { @override String get listFilter_roomServers => 'Szoba-szolgálatok'; + @override + String get listFilter_sensors => 'Sensors'; + @override String get listFilter_unreadOnly => 'Csak olvasatlan'; diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index 61871f8..875eedc 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -1019,6 +1019,11 @@ class AppLocalizationsIt extends AppLocalizations { return 'Cerca $number$str server Room...'; } + @override + String contacts_searchSensors(int number, String str) { + return 'Search $number$str Sensors...'; + } + @override String get contacts_noUnreadContacts => 'Nessun contatto non letto'; @@ -3887,6 +3892,9 @@ class AppLocalizationsIt extends AppLocalizations { @override String get listFilter_roomServers => 'Server della stanza'; + @override + String get listFilter_sensors => 'Sensors'; + @override String get listFilter_unreadOnly => 'Solo non letto'; diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index 11d249a..4546578 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -970,6 +970,11 @@ class AppLocalizationsJa extends AppLocalizations { return '$number $str 部屋のサーバーを検索する...'; } + @override + String contacts_searchSensors(int number, String str) { + return 'Search $number$str Sensors...'; + } + @override String get contacts_noUnreadContacts => '未読の連絡先はありません'; @@ -3694,6 +3699,9 @@ class AppLocalizationsJa extends AppLocalizations { @override String get listFilter_roomServers => 'ルーム用サーバー'; + @override + String get listFilter_sensors => 'Sensors'; + @override String get listFilter_unreadOnly => '未読のみ'; diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart index ae6f9dc..d45ac1b 100644 --- a/lib/l10n/app_localizations_ko.dart +++ b/lib/l10n/app_localizations_ko.dart @@ -965,6 +965,11 @@ class AppLocalizationsKo extends AppLocalizations { return '$number $str 방 서버 검색'; } + @override + String contacts_searchSensors(int number, String str) { + return 'Search $number$str Sensors...'; + } + @override String get contacts_noUnreadContacts => '읽지 않은 연락처가 없습니다.'; @@ -3696,6 +3701,9 @@ class AppLocalizationsKo extends AppLocalizations { @override String get listFilter_roomServers => '방 내 서버'; + @override + String get listFilter_sensors => 'Sensors'; + @override String get listFilter_unreadOnly => '읽지 않은 항목만'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index 6b4de2e..5d857ce 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -1011,6 +1011,11 @@ class AppLocalizationsNl extends AppLocalizations { return 'Zoek $number$str Room servers...'; } + @override + String contacts_searchSensors(int number, String str) { + return 'Search $number$str Sensors...'; + } + @override String get contacts_noUnreadContacts => 'Geen ongelezen contacten'; @@ -3869,6 +3874,9 @@ class AppLocalizationsNl extends AppLocalizations { @override String get listFilter_roomServers => 'Roomservers'; + @override + String get listFilter_sensors => 'Sensors'; + @override String get listFilter_unreadOnly => 'Alleen ongelezen'; diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 5d99cfe..72d7782 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -1028,6 +1028,11 @@ class AppLocalizationsPl extends AppLocalizations { return 'Wyszukaj $number$str serwerów Room...'; } + @override + String contacts_searchSensors(int number, String str) { + return 'Search $number$str Sensors...'; + } + @override String get contacts_noUnreadContacts => 'Brak nieprzeczytanych kontaktów'; @@ -3900,6 +3905,9 @@ class AppLocalizationsPl extends AppLocalizations { @override String get listFilter_roomServers => 'Serwery pokoju'; + @override + String get listFilter_sensors => 'Sensors'; + @override String get listFilter_unreadOnly => 'Tylko nieprzeczytane'; diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index adaaeff..f7f33a4 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -1018,6 +1018,11 @@ class AppLocalizationsPt extends AppLocalizations { return 'Pesquisar $number$str servidores de sala...'; } + @override + String contacts_searchSensors(int number, String str) { + return 'Search $number$str Sensors...'; + } + @override String get contacts_noUnreadContacts => 'Sem contatos não lidos.'; @@ -3882,6 +3887,9 @@ class AppLocalizationsPt extends AppLocalizations { @override String get listFilter_roomServers => 'Servidores de sala'; + @override + String get listFilter_sensors => 'Sensors'; + @override String get listFilter_unreadOnly => 'Apenas não lido'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index 1cf1e62..c218e2c 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -1020,6 +1020,11 @@ class AppLocalizationsRu extends AppLocalizations { return 'Поиск $number$str серверов комнат...'; } + @override + String contacts_searchSensors(int number, String str) { + return 'Search $number$str Sensors...'; + } + @override String get contacts_noUnreadContacts => 'Нет непрочитанных контактов'; @@ -3890,6 +3895,9 @@ class AppLocalizationsRu extends AppLocalizations { @override String get listFilter_roomServers => 'Серверы комнат'; + @override + String get listFilter_sensors => 'Sensors'; + @override String get listFilter_unreadOnly => 'Только непрочитанные'; diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index af77b6f..14e8a14 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -1007,6 +1007,11 @@ class AppLocalizationsSk extends AppLocalizations { return 'Hľadaj $number$str serverov miestností...'; } + @override + String contacts_searchSensors(int number, String str) { + return 'Search $number$str Sensors...'; + } + @override String get contacts_noUnreadContacts => 'Žiadne neprečítané kontakty'; @@ -3866,6 +3871,9 @@ class AppLocalizationsSk extends AppLocalizations { @override String get listFilter_roomServers => 'Servéry miestnosti'; + @override + String get listFilter_sensors => 'Sensors'; + @override String get listFilter_unreadOnly => 'Nezaregistrované len'; diff --git a/lib/l10n/app_localizations_sl.dart b/lib/l10n/app_localizations_sl.dart index 4e53853..518d551 100644 --- a/lib/l10n/app_localizations_sl.dart +++ b/lib/l10n/app_localizations_sl.dart @@ -1007,6 +1007,11 @@ class AppLocalizationsSl extends AppLocalizations { return 'Išči $number$str strežnikov sob...'; } + @override + String contacts_searchSensors(int number, String str) { + return 'Search $number$str Sensors...'; + } + @override String get contacts_noUnreadContacts => 'Ne prebrani stiki.'; @@ -3861,6 +3866,9 @@ class AppLocalizationsSl extends AppLocalizations { @override String get listFilter_roomServers => 'Smeti za prostore'; + @override + String get listFilter_sensors => 'Sensors'; + @override String get listFilter_unreadOnly => 'Nezbrani samo'; diff --git a/lib/l10n/app_localizations_sv.dart b/lib/l10n/app_localizations_sv.dart index 11801a8..c6f9fed 100644 --- a/lib/l10n/app_localizations_sv.dart +++ b/lib/l10n/app_localizations_sv.dart @@ -1001,6 +1001,11 @@ class AppLocalizationsSv extends AppLocalizations { return 'Sök $number$str Room-servrar...'; } + @override + String contacts_searchSensors(int number, String str) { + return 'Search $number$str Sensors...'; + } + @override String get contacts_noUnreadContacts => 'Inga oinlästa kontakter'; @@ -3841,6 +3846,9 @@ class AppLocalizationsSv extends AppLocalizations { @override String get listFilter_roomServers => 'Rumservrar'; + @override + String get listFilter_sensors => 'Sensors'; + @override String get listFilter_unreadOnly => 'Endast oinlästa'; diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart index 79e1ec7..f0dab52 100644 --- a/lib/l10n/app_localizations_uk.dart +++ b/lib/l10n/app_localizations_uk.dart @@ -1015,6 +1015,11 @@ class AppLocalizationsUk extends AppLocalizations { return 'Пошук $number$str серверів кімнат...'; } + @override + String contacts_searchSensors(int number, String str) { + return 'Search $number$str Sensors...'; + } + @override String get contacts_noUnreadContacts => 'Немає непрочитаних контактів'; @@ -3892,6 +3897,9 @@ class AppLocalizationsUk extends AppLocalizations { @override String get listFilter_roomServers => 'Сервери кімнат'; + @override + String get listFilter_sensors => 'Sensors'; + @override String get listFilter_unreadOnly => 'Тільки непрочитані повідомлення'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index c74308e..d39404c 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -951,6 +951,11 @@ class AppLocalizationsZh extends AppLocalizations { return '搜索 $number$str 房间服务器...'; } + @override + String contacts_searchSensors(int number, String str) { + return 'Search $number$str Sensors...'; + } + @override String get contacts_noUnreadContacts => '没有未读内容'; @@ -3598,6 +3603,9 @@ class AppLocalizationsZh extends AppLocalizations { @override String get listFilter_roomServers => '房间服务器'; + @override + String get listFilter_sensors => 'Sensors'; + @override String get listFilter_unreadOnly => '仅显示未读'; diff --git a/lib/screens/contacts_screen.dart b/lib/screens/contacts_screen.dart index 2d15349..37bdcc9 100644 --- a/lib/screens/contacts_screen.dart +++ b/lib/screens/contacts_screen.dart @@ -673,6 +673,14 @@ class _ContactsScreenState extends State : "", ); break; + case ContactTypeFilter.sensors: + hintText = context.l10n.contacts_searchSensors( + filteredAndSorted.length, + viewState.contactsShowUnreadOnly + ? " ${context.l10n.contacts_unread}" + : "", + ); + break; } final groupsByName = {}; @@ -920,6 +928,8 @@ class _ContactsScreenState extends State return contact.type == advTypeRepeater; case ContactTypeFilter.rooms: return contact.type == advTypeRoom; + case ContactTypeFilter.sensors: + return contact.type == advTypeSensor; } } diff --git a/lib/screens/discovery_screen.dart b/lib/screens/discovery_screen.dart index d97f16f..f9565a0 100644 --- a/lib/screens/discovery_screen.dart +++ b/lib/screens/discovery_screen.dart @@ -353,6 +353,12 @@ class _DiscoveryScreenState extends State { showUnreadOnly ? " ${context.l10n.contacts_unread}" : "", ); break; + case ContactTypeFilter.sensors: + hintText = context.l10n.contacts_searchSensors( + filteredAndSorted.length, + showUnreadOnly ? " ${context.l10n.contacts_unread}" : "", + ); + break; } return Column( @@ -471,6 +477,8 @@ class _DiscoveryScreenState extends State { return contact.type == advTypeRepeater; case ContactTypeFilter.rooms: return contact.type == advTypeRoom; + case ContactTypeFilter.sensors: + return contact.type == advTypeSensor; default: return false; } diff --git a/lib/utils/contact_filter_types.dart b/lib/utils/contact_filter_types.dart index 08e07c2..82f7e80 100644 --- a/lib/utils/contact_filter_types.dart +++ b/lib/utils/contact_filter_types.dart @@ -1,3 +1,3 @@ enum ContactSortOption { lastSeen, recentMessages, name } -enum ContactTypeFilter { all, favorites, users, repeaters, rooms } +enum ContactTypeFilter { all, favorites, users, repeaters, rooms, sensors } diff --git a/lib/widgets/list_filter_widget.dart b/lib/widgets/list_filter_widget.dart index c4fd5aa..e2838f1 100644 --- a/lib/widgets/list_filter_widget.dart +++ b/lib/widgets/list_filter_widget.dart @@ -177,6 +177,11 @@ class ContactsFilterMenu extends StatelessWidget { label: l10n.listFilter_roomServers, checked: typeFilter == ContactTypeFilter.rooms, ), + SortFilterMenuOption( + value: _TypeFilterAction(ContactTypeFilter.sensors), + label: l10n.listFilter_sensors, + checked: typeFilter == ContactTypeFilter.sensors, + ), SortFilterMenuOption( value: const _ToggleUnreadAction(), label: l10n.listFilter_unreadOnly,