From 8668564464d577ba14958f45fc1031ddc4793c9b Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Mon, 9 Feb 2026 12:56:38 +0200 Subject: [PATCH 01/20] Correct unread badges for tabs; people first contacts sort option --- lib/connector/meshcore_connector.dart | 19 ++++++++-- lib/l10n/app_en.arb | 1 + lib/l10n/app_localizations.dart | 6 +++ lib/l10n/app_localizations_bg.dart | 3 ++ lib/l10n/app_localizations_de.dart | 3 ++ lib/l10n/app_localizations_en.dart | 3 ++ lib/l10n/app_localizations_es.dart | 3 ++ lib/l10n/app_localizations_fr.dart | 3 ++ lib/l10n/app_localizations_it.dart | 3 ++ lib/l10n/app_localizations_nl.dart | 3 ++ lib/l10n/app_localizations_pl.dart | 3 ++ lib/l10n/app_localizations_pt.dart | 3 ++ lib/l10n/app_localizations_ru.dart | 3 ++ lib/l10n/app_localizations_sk.dart | 3 ++ lib/l10n/app_localizations_sl.dart | 3 ++ lib/l10n/app_localizations_sv.dart | 3 ++ lib/l10n/app_localizations_uk.dart | 11 ++++-- lib/l10n/app_localizations_zh.dart | 3 ++ lib/l10n/app_uk.arb | 9 +++-- lib/screens/channels_screen.dart | 2 + lib/screens/contacts_screen.dart | 37 +++++++++++++++--- lib/screens/device_screen.dart | 6 ++- lib/screens/map_screen.dart | 2 + lib/widgets/list_filter_widget.dart | 13 +++++++ lib/widgets/quick_switch_bar.dart | 35 ++++++++++++++++- untranslated.json | 54 ++++++++++++++++++++++++++- 26 files changed, 215 insertions(+), 22 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 2c56c373..b96aef98 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -336,15 +336,26 @@ class MeshCoreConnector extends ChangeNotifier { } int getTotalUnreadCount() { + if (!_unreadStateLoaded) return 0; + return getTotalContactsUnreadCount() + getTotalChannelsUnreadCount(); + } + + int getTotalContactsUnreadCount() { if (!_unreadStateLoaded) return 0; var total = 0; - // Count unread contact messages for (final contact in _contacts) { total += getUnreadCountForContact(contact); } - // Count unread channel messages - for (final channelIndex in _channelMessages.keys) { - total += getUnreadCountForChannelIndex(channelIndex); + return total; + } + + int getTotalChannelsUnreadCount() { + if (!_unreadStateLoaded) return 0; + var total = 0; + // Check all channels (from _channels or _cachedChannels) + final allChannels = _channels.isNotEmpty ? _channels : _cachedChannels; + for (final channel in allChannels) { + total += channel.unreadCount; } return total; } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index ee5cf7d6..c91345d7 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1304,6 +1304,7 @@ "listFilter_latestMessages": "Latest messages", "listFilter_heardRecently": "Heard recently", "listFilter_az": "A-Z", + "listFilter_usersFirst": "Users first", "listFilter_filters": "Filters", "listFilter_all": "All", "listFilter_users": "Users", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 055667fa..33aa4032 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -4658,6 +4658,12 @@ abstract class AppLocalizations { /// **'A-Z'** String get listFilter_az; + /// No description provided for @listFilter_usersFirst. + /// + /// In en, this message translates to: + /// **'Users first'** + String get listFilter_usersFirst; + /// No description provided for @listFilter_filters. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_bg.dart b/lib/l10n/app_localizations_bg.dart index 701429e6..e373a156 100644 --- a/lib/l10n/app_localizations_bg.dart +++ b/lib/l10n/app_localizations_bg.dart @@ -2662,6 +2662,9 @@ class AppLocalizationsBg extends AppLocalizations { @override String get listFilter_az => 'A-Z'; + @override + String get listFilter_usersFirst => 'Users first'; + @override String get listFilter_filters => 'Филтри'; diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 514a7a19..779a5344 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -2666,6 +2666,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get listFilter_az => 'A-Z'; + @override + String get listFilter_usersFirst => 'Users first'; + @override String get listFilter_filters => 'Filtere'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 040d8090..29f83919 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -2622,6 +2622,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get listFilter_az => 'A-Z'; + @override + String get listFilter_usersFirst => 'Users first'; + @override String get listFilter_filters => 'Filters'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index e65cbcd3..7f61ee25 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -2661,6 +2661,9 @@ class AppLocalizationsEs extends AppLocalizations { @override String get listFilter_az => 'A-Z'; + @override + String get listFilter_usersFirst => 'Users first'; + @override String get listFilter_filters => 'Filtros'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 4496fc84..d858934e 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -2678,6 +2678,9 @@ class AppLocalizationsFr extends AppLocalizations { @override String get listFilter_az => 'A à Z'; + @override + String get listFilter_usersFirst => 'Users first'; + @override String get listFilter_filters => 'Filtres'; diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index 02345c4e..9786262d 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -2661,6 +2661,9 @@ class AppLocalizationsIt extends AppLocalizations { @override String get listFilter_az => 'A-Z'; + @override + String get listFilter_usersFirst => 'Users first'; + @override String get listFilter_filters => 'Filtri'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index 292181fc..74f2abae 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -2652,6 +2652,9 @@ class AppLocalizationsNl extends AppLocalizations { @override String get listFilter_az => 'A-Z'; + @override + String get listFilter_usersFirst => 'Users first'; + @override String get listFilter_filters => 'Filters'; diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 08323295..13bb1bf4 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -2660,6 +2660,9 @@ class AppLocalizationsPl extends AppLocalizations { @override String get listFilter_az => 'A-Z'; + @override + String get listFilter_usersFirst => 'Users first'; + @override String get listFilter_filters => 'Filtry'; diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index eadea3bc..c555be3b 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -2663,6 +2663,9 @@ class AppLocalizationsPt extends AppLocalizations { @override String get listFilter_az => 'A-Z'; + @override + String get listFilter_usersFirst => 'Users first'; + @override String get listFilter_filters => 'Filtros'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index ec0f1ba3..6e3a9d7c 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -2665,6 +2665,9 @@ class AppLocalizationsRu extends AppLocalizations { @override String get listFilter_az => 'По алфавиту'; + @override + String get listFilter_usersFirst => 'Users first'; + @override String get listFilter_filters => 'Фильтры'; diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index 346047b0..65e41fcd 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -2648,6 +2648,9 @@ class AppLocalizationsSk extends AppLocalizations { @override String get listFilter_az => 'A-Z'; + @override + String get listFilter_usersFirst => 'Users first'; + @override String get listFilter_filters => 'Filtre'; diff --git a/lib/l10n/app_localizations_sl.dart b/lib/l10n/app_localizations_sl.dart index ed711224..c3c46aed 100644 --- a/lib/l10n/app_localizations_sl.dart +++ b/lib/l10n/app_localizations_sl.dart @@ -2651,6 +2651,9 @@ class AppLocalizationsSl extends AppLocalizations { @override String get listFilter_az => 'A-Z'; + @override + String get listFilter_usersFirst => 'Users first'; + @override String get listFilter_filters => 'Filtri'; diff --git a/lib/l10n/app_localizations_sv.dart b/lib/l10n/app_localizations_sv.dart index 97b849fa..f8652ae2 100644 --- a/lib/l10n/app_localizations_sv.dart +++ b/lib/l10n/app_localizations_sv.dart @@ -2636,6 +2636,9 @@ class AppLocalizationsSv extends AppLocalizations { @override String get listFilter_az => 'A-Z'; + @override + String get listFilter_usersFirst => 'Users first'; + @override String get listFilter_filters => 'Filteralternativ'; diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart index 899d540d..cfb88d58 100644 --- a/lib/l10n/app_localizations_uk.dart +++ b/lib/l10n/app_localizations_uk.dart @@ -2672,6 +2672,9 @@ class AppLocalizationsUk extends AppLocalizations { @override String get listFilter_az => 'А-Я'; + @override + String get listFilter_usersFirst => 'Спочатку користувачі'; + @override String get listFilter_filters => 'Фільтри'; @@ -2703,7 +2706,7 @@ class AppLocalizationsUk extends AppLocalizations { String get pathTrace_notAvailable => 'Трасування шляху недоступне.'; @override - String get pathTrace_refreshTooltip => 'Оновити Path Trace'; + String get pathTrace_refreshTooltip => 'Оновити трасування шляху'; @override String get contacts_pathTrace => 'Трасування шляхів'; @@ -2744,10 +2747,10 @@ class AppLocalizationsUk extends AppLocalizations { String get contacts_contactImportFailed => 'Контакт не вдалося імпортувати'; @override - String get contacts_zeroHopAdvert => 'Реклама без перехоплення'; + String get contacts_zeroHopAdvert => 'Оголошення без ретрансляції'; @override - String get contacts_floodAdvert => 'Залив реклами'; + String get contacts_floodAdvert => 'Оголошення з ретрансляцією'; @override String get contacts_copyAdvertToClipboard => @@ -2774,7 +2777,7 @@ class AppLocalizationsUk extends AppLocalizations { @override String get contacts_contactAdvertCopied => - 'Рекламу скопійовано до буфера обміну.'; + 'Оголошення скопійовано до буфера обміну.'; @override String get contacts_contactAdvertCopyFailed => diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index 77467924..d6dc2ff4 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -2521,6 +2521,9 @@ class AppLocalizationsZh extends AppLocalizations { @override String get listFilter_az => 'A 到 Z'; + @override + String get listFilter_usersFirst => 'Users first'; + @override String get listFilter_filters => '过滤器'; diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb index 3362d409..35edeee3 100644 --- a/lib/l10n/app_uk.arb +++ b/lib/l10n/app_uk.arb @@ -1343,6 +1343,7 @@ "listFilter_latestMessages": "Останні повідомлення", "listFilter_heardRecently": "Нещодавно чули", "listFilter_az": "А-Я", + "listFilter_usersFirst": "Спочатку користувачі", "listFilter_filters": "Фільтри", "listFilter_all": "Все", "listFilter_users": "Користувачі", @@ -1545,7 +1546,7 @@ "pathTrace_you": "Ви", "pathTrace_failed": "Відстеження шляху не вдалося.", "pathTrace_notAvailable": "Трасування шляху недоступне.", - "pathTrace_refreshTooltip": "Оновити Path Trace", + "pathTrace_refreshTooltip": "Оновити трасування шляху", "contacts_pathTrace": "Трасування шляхів", "contacts_ping": "Пінгувати", "contacts_repeaterPathTrace": "Трасування шляху до повторювача", @@ -1557,14 +1558,14 @@ "contacts_invalidAdvertFormat": "Недійсні контактні дані", "contacts_contactImported": "Контакт було імпортовано.", "contacts_contactImportFailed": "Контакт не вдалося імпортувати", - "contacts_zeroHopAdvert": "Реклама без перехоплення", - "contacts_floodAdvert": "Залив реклами", + "contacts_zeroHopAdvert": "Оголошення без ретрансляції", + "contacts_floodAdvert": "Оголошення з ретрансляцією", "contacts_copyAdvertToClipboard": "Копіювати оголошення в буфер обміну", "contacts_clipboardEmpty": "Буфер обміну порожній", "appSettings_languageRu": "Російська", "contacts_ShareContact": "Копіювати контакт у буфер обміну", "contacts_zeroHopContactAdvertFailed": "Не вдалося надіслати контакт.", - "contacts_contactAdvertCopied": "Рекламу скопійовано до буфера обміну.", + "contacts_contactAdvertCopied": "Оголошення скопійовано до буфера обміну.", "contacts_contactAdvertCopyFailed": "Копіювання оголошення в буфер обміну завершилося невдало", "contacts_zeroHopContactAdvertSent": "Відправлено контакт за оголошенням", "contacts_addContactFromClipboard": "Додати контакт з буфера обміну", diff --git a/lib/screens/channels_screen.dart b/lib/screens/channels_screen.dart index 6b8b92d4..6d8ef038 100644 --- a/lib/screens/channels_screen.dart +++ b/lib/screens/channels_screen.dart @@ -343,6 +343,8 @@ class _ChannelsScreenState extends State selectedIndex: 1, onDestinationSelected: (index) => _handleQuickSwitch(index, context), + contactsUnreadCount: connector.getTotalContactsUnreadCount(), + channelsUnreadCount: connector.getTotalChannelsUnreadCount(), ), ), ), diff --git a/lib/screens/contacts_screen.dart b/lib/screens/contacts_screen.dart index f04bc502..ac157356 100644 --- a/lib/screens/contacts_screen.dart +++ b/lib/screens/contacts_screen.dart @@ -48,6 +48,7 @@ class _ContactsScreenState extends State String _searchQuery = ''; ContactSortOption _sortOption = ContactSortOption.lastSeen; bool _showUnreadOnly = false; + bool _prioritizePeople = true; ContactTypeFilter _typeFilter = ContactTypeFilter.all; final ContactGroupStore _groupStore = ContactGroupStore(); List _groups = []; @@ -332,6 +333,8 @@ class _ContactsScreenState extends State selectedIndex: 0, onDestinationSelected: (index) => _handleQuickSwitch(index, context), + contactsUnreadCount: connector.getTotalContactsUnreadCount(), + channelsUnreadCount: connector.getTotalChannelsUnreadCount(), ), ), ), @@ -350,6 +353,7 @@ class _ContactsScreenState extends State sortOption: _sortOption, typeFilter: _typeFilter, showUnreadOnly: _showUnreadOnly, + prioritizePeople: _prioritizePeople, onSortChanged: (value) { setState(() { _sortOption = value; @@ -365,6 +369,11 @@ class _ContactsScreenState extends State _showUnreadOnly = value; }); }, + onPrioritizePeopleChanged: (value) { + setState(() { + _prioritizePeople = value; + }); + }, onNewGroup: () => _showGroupEditor(context, connector.contacts), ); } @@ -545,14 +554,34 @@ class _ContactsScreenState extends State }).toList(); } + // Apply sorting within groups if prioritizing people + if (_prioritizePeople) { + // Separate people (advTypeChat) from others + final people = filtered.where((c) => c.type == advTypeChat).toList(); + final others = filtered.where((c) => c.type != advTypeChat).toList(); + + // Sort each group separately + _applySorting(people, connector); + _applySorting(others, connector); + + // Combine: people first, then others + filtered = [...people, ...others]; + } else { + _applySorting(filtered, connector); + } + + return filtered; + } + + void _applySorting(List contacts, MeshCoreConnector connector) { switch (_sortOption) { case ContactSortOption.lastSeen: - filtered.sort( + contacts.sort( (a, b) => _resolveLastSeen(b).compareTo(_resolveLastSeen(a)), ); break; case ContactSortOption.recentMessages: - filtered.sort((a, b) { + contacts.sort((a, b) { final aMessages = connector.getMessages(a); final bMessages = connector.getMessages(b); final aLastMsg = aMessages.isEmpty @@ -565,13 +594,11 @@ class _ContactsScreenState extends State }); break; case ContactSortOption.name: - filtered.sort( + contacts.sort( (a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()), ); break; } - - return filtered; } bool _matchesTypeFilter(Contact contact) { diff --git a/lib/screens/device_screen.dart b/lib/screens/device_screen.dart index c5967cf4..c4a5310b 100644 --- a/lib/screens/device_screen.dart +++ b/lib/screens/device_screen.dart @@ -71,7 +71,7 @@ class _DeviceScreenState extends State const SizedBox(height: 16), _buildSectionLabel(theme, context.l10n.device_quickSwitch), const SizedBox(height: 12), - _buildQuickSwitchBar(context), + _buildQuickSwitchBar(context, connector), ], ), ), @@ -196,12 +196,14 @@ class _DeviceScreenState extends State ); } - Widget _buildQuickSwitchBar(BuildContext context) { + Widget _buildQuickSwitchBar(BuildContext context, MeshCoreConnector connector) { return QuickSwitchBar( selectedIndex: _quickIndex, onDestinationSelected: (index) { _openQuickDestination(index, context); }, + contactsUnreadCount: connector.getTotalContactsUnreadCount(), + channelsUnreadCount: connector.getTotalChannelsUnreadCount(), ); } diff --git a/lib/screens/map_screen.dart b/lib/screens/map_screen.dart index 0da99601..4253391f 100644 --- a/lib/screens/map_screen.dart +++ b/lib/screens/map_screen.dart @@ -362,6 +362,8 @@ class _MapScreenState extends State { selectedIndex: 2, onDestinationSelected: (index) => _handleQuickSwitch(index, context), + contactsUnreadCount: connector.getTotalContactsUnreadCount(), + channelsUnreadCount: connector.getTotalChannelsUnreadCount(), ), ), floatingActionButton: FloatingActionButton( diff --git a/lib/widgets/list_filter_widget.dart b/lib/widgets/list_filter_widget.dart index e9c0d9e8..6075fecb 100644 --- a/lib/widgets/list_filter_widget.dart +++ b/lib/widgets/list_filter_widget.dart @@ -99,14 +99,17 @@ const int _actionFilterRepeaters = 6; const int _actionFilterRooms = 7; const int _actionToggleUnreadOnly = 8; const int _actionNewGroup = 9; +const int _actionTogglePrioritizePeople = 10; class ContactsFilterMenu extends StatelessWidget { final ContactSortOption sortOption; final ContactTypeFilter typeFilter; final bool showUnreadOnly; + final bool prioritizePeople; final ValueChanged onSortChanged; final ValueChanged onTypeFilterChanged; final ValueChanged onUnreadOnlyChanged; + final ValueChanged onPrioritizePeopleChanged; final VoidCallback onNewGroup; const ContactsFilterMenu({ @@ -114,9 +117,11 @@ class ContactsFilterMenu extends StatelessWidget { required this.sortOption, required this.typeFilter, required this.showUnreadOnly, + required this.prioritizePeople, required this.onSortChanged, required this.onTypeFilterChanged, required this.onUnreadOnlyChanged, + required this.onPrioritizePeopleChanged, required this.onNewGroup, }); @@ -144,6 +149,11 @@ class ContactsFilterMenu extends StatelessWidget { label: l10n.listFilter_az, checked: sortOption == ContactSortOption.name, ), + SortFilterMenuOption( + value: _actionTogglePrioritizePeople, + label: l10n.listFilter_usersFirst, + checked: prioritizePeople, + ), ], ), SortFilterMenuSection( @@ -192,6 +202,9 @@ class ContactsFilterMenu extends StatelessWidget { case _actionSortLastSeen: onSortChanged(ContactSortOption.lastSeen); break; + case _actionTogglePrioritizePeople: + onPrioritizePeopleChanged(!prioritizePeople); + break; case _actionFilterAll: onTypeFilterChanged(ContactTypeFilter.all); break; diff --git a/lib/widgets/quick_switch_bar.dart b/lib/widgets/quick_switch_bar.dart index 134091ff..78874fdf 100644 --- a/lib/widgets/quick_switch_bar.dart +++ b/lib/widgets/quick_switch_bar.dart @@ -6,11 +6,15 @@ import '../l10n/l10n.dart'; class QuickSwitchBar extends StatelessWidget { final int selectedIndex; final ValueChanged onDestinationSelected; + final int contactsUnreadCount; + final int channelsUnreadCount; const QuickSwitchBar({ super.key, required this.selectedIndex, required this.onDestinationSelected, + this.contactsUnreadCount = 0, + this.channelsUnreadCount = 0, }); @override @@ -62,15 +66,30 @@ class QuickSwitchBar extends StatelessWidget { onDestinationSelected: onDestinationSelected, destinations: [ NavigationDestination( - icon: const Icon(Icons.people_outline), + icon: _buildIconWithBadge( + const Icon(Icons.people_outline), + contactsUnreadCount, + ), + selectedIcon: _buildIconWithBadge( + const Icon(Icons.people), + contactsUnreadCount, + ), label: context.l10n.nav_contacts, ), NavigationDestination( - icon: const Icon(Icons.tag), + icon: _buildIconWithBadge( + const Icon(Icons.tag), + channelsUnreadCount, + ), + selectedIcon: _buildIconWithBadge( + const Icon(Icons.tag), + channelsUnreadCount, + ), label: context.l10n.nav_channels, ), NavigationDestination( icon: const Icon(Icons.map_outlined), + selectedIcon: const Icon(Icons.map), label: context.l10n.nav_map, ), ], @@ -81,4 +100,16 @@ class QuickSwitchBar extends StatelessWidget { ), ); } + + Widget _buildIconWithBadge(Icon icon, int count) { + if (count <= 0) return icon; + + return Badge( + label: Text( + count > 99 ? '99+' : count.toString(), + style: const TextStyle(fontSize: 10), + ), + child: icon, + ); + } } diff --git a/untranslated.json b/untranslated.json index 9e26dfee..29112d61 100644 --- a/untranslated.json +++ b/untranslated.json @@ -1 +1,53 @@ -{} \ No newline at end of file +{ + "bg": [ + "listFilter_usersFirst" + ], + + "de": [ + "listFilter_usersFirst" + ], + + "es": [ + "listFilter_usersFirst" + ], + + "fr": [ + "listFilter_usersFirst" + ], + + "it": [ + "listFilter_usersFirst" + ], + + "nl": [ + "listFilter_usersFirst" + ], + + "pl": [ + "listFilter_usersFirst" + ], + + "pt": [ + "listFilter_usersFirst" + ], + + "ru": [ + "listFilter_usersFirst" + ], + + "sk": [ + "listFilter_usersFirst" + ], + + "sl": [ + "listFilter_usersFirst" + ], + + "sv": [ + "listFilter_usersFirst" + ], + + "zh": [ + "listFilter_usersFirst" + ] +} From fd305fd55bcaf0b562c21b2b8b9bc773bf07e319 Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Mon, 9 Feb 2026 13:19:31 +0200 Subject: [PATCH 02/20] Update generated plugin registrants after merge --- macos/Flutter/GeneratedPluginRegistrant.swift | 2 - pubspec.lock | 50 +------------------ .../flutter/generated_plugin_registrant.cc | 3 -- windows/flutter/generated_plugins.cmake | 1 - 4 files changed, 1 insertion(+), 55 deletions(-) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index d2ea57e9..b4a41dd1 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -9,7 +9,6 @@ import flutter_blue_plus_darwin import flutter_local_notifications import mobile_scanner import package_info_plus -import share_plus import shared_preferences_foundation import sqflite_darwin import url_launcher_macos @@ -20,7 +19,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) - SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) diff --git a/pubspec.lock b/pubspec.lock index fc116566..1e275d4f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -121,14 +121,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.2" - cross_file: - dependency: transitive - description: - name: cross_file - sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937" - url: "https://pub.dev" - source: hosted - version: "0.3.5+2" crypto: dependency: "direct main" description: @@ -349,14 +341,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" - gpx: - dependency: "direct main" - description: - name: gpx - sha256: f5b12b86402c639079243600ee2b3afd85cd08d26117fc8885cf48efce471d8e - url: "https://pub.dev" - source: hosted - version: "2.3.0" hooks: dependency: transitive description: @@ -517,14 +501,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" - mime: - dependency: transitive - description: - name: mime - sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" - url: "https://pub.dev" - source: hosted - version: "2.0.0" mobile_scanner: dependency: "direct main" description: @@ -590,7 +566,7 @@ packages: source: hosted version: "1.9.1" path_provider: - dependency: "direct main" + dependency: transitive description: name: path_provider sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" @@ -725,14 +701,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.0" - quiver: - dependency: transitive - description: - name: quiver - sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 - url: "https://pub.dev" - source: hosted - version: "3.2.2" rxdart: dependency: transitive description: @@ -741,22 +709,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.28.0" - share_plus: - dependency: "direct main" - description: - name: share_plus - sha256: "14c8860d4de93d3a7e53af51bff479598c4e999605290756bbbe45cf65b37840" - url: "https://pub.dev" - source: hosted - version: "12.0.1" - share_plus_platform_interface: - dependency: transitive - description: - name: share_plus_platform_interface - sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a" - url: "https://pub.dev" - source: hosted - version: "6.1.0" shared_preferences: dependency: "direct main" description: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index cd4fc19b..eeb548fa 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,14 +7,11 @@ #include "generated_plugin_registrant.h" #include -#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { FlutterBluePlusPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterBluePlusPlugin")); - SharePlusWindowsPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 571addb8..68825d8b 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,7 +4,6 @@ list(APPEND FLUTTER_PLUGIN_LIST flutter_blue_plus_winrt - share_plus url_launcher_windows ) From 2bce14224d9d756df8571054d48d657b543bc913 Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Mon, 9 Feb 2026 16:27:53 +0200 Subject: [PATCH 03/20] Update generated plugin registrants after merge --- macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 50 ++++++++++++++++++- .../flutter/generated_plugin_registrant.cc | 3 ++ windows/flutter/generated_plugins.cmake | 1 + 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index b4a41dd1..d2ea57e9 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -9,6 +9,7 @@ import flutter_blue_plus_darwin import flutter_local_notifications import mobile_scanner import package_info_plus +import share_plus import shared_preferences_foundation import sqflite_darwin import url_launcher_macos @@ -19,6 +20,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) + SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 1e275d4f..fc116566 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -121,6 +121,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.2" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937" + url: "https://pub.dev" + source: hosted + version: "0.3.5+2" crypto: dependency: "direct main" description: @@ -341,6 +349,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" + gpx: + dependency: "direct main" + description: + name: gpx + sha256: f5b12b86402c639079243600ee2b3afd85cd08d26117fc8885cf48efce471d8e + url: "https://pub.dev" + source: hosted + version: "2.3.0" hooks: dependency: transitive description: @@ -501,6 +517,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" mobile_scanner: dependency: "direct main" description: @@ -566,7 +590,7 @@ packages: source: hosted version: "1.9.1" path_provider: - dependency: transitive + dependency: "direct main" description: name: path_provider sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" @@ -701,6 +725,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.0" + quiver: + dependency: transitive + description: + name: quiver + sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 + url: "https://pub.dev" + source: hosted + version: "3.2.2" rxdart: dependency: transitive description: @@ -709,6 +741,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.28.0" + share_plus: + dependency: "direct main" + description: + name: share_plus + sha256: "14c8860d4de93d3a7e53af51bff479598c4e999605290756bbbe45cf65b37840" + url: "https://pub.dev" + source: hosted + version: "12.0.1" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a" + url: "https://pub.dev" + source: hosted + version: "6.1.0" shared_preferences: dependency: "direct main" description: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index eeb548fa..cd4fc19b 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,11 +7,14 @@ #include "generated_plugin_registrant.h" #include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { FlutterBluePlusPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterBluePlusPlugin")); + SharePlusWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 68825d8b..571addb8 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST flutter_blue_plus_winrt + share_plus url_launcher_windows ) From c4f5c7b1717265b430530dbee8f2782a9790cdb5 Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Mon, 9 Feb 2026 17:18:21 +0200 Subject: [PATCH 04/20] Cache for unread total --- lib/connector/meshcore_connector.dart | 37 +++++++++++++++++++-------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index b96aef98..9483a202 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -150,6 +150,8 @@ class MeshCoreConnector extends ChangeNotifier { final Set _knownContactKeys = {}; final Map _contactUnreadCount = {}; bool _unreadStateLoaded = false; + int _cachedContactsUnreadTotal = 0; + int _cachedChannelsUnreadTotal = 0; final Map _pendingRepeaterAcks = {}; String? _activeContactKey; int? _activeChannelIndex; @@ -342,22 +344,28 @@ class MeshCoreConnector extends ChangeNotifier { int getTotalContactsUnreadCount() { if (!_unreadStateLoaded) return 0; - var total = 0; - for (final contact in _contacts) { - total += getUnreadCountForContact(contact); - } - return total; + return _cachedContactsUnreadTotal; } int getTotalChannelsUnreadCount() { if (!_unreadStateLoaded) return 0; - var total = 0; - // Check all channels (from _channels or _cachedChannels) + return _cachedChannelsUnreadTotal; + } + + /// Recalculates both cached unread totals from scratch. + /// Called when unread state is first loaded. + void _recalculateCachedUnreadTotals() { + _recalculateCachedContactsUnreadTotal(); + _recalculateCachedChannelsUnreadTotal(); + } + + void _recalculateCachedContactsUnreadTotal() { + _cachedContactsUnreadTotal = _contactUnreadCount.values.fold(0, (a, b) => a + b); + } + + void _recalculateCachedChannelsUnreadTotal() { final allChannels = _channels.isNotEmpty ? _channels : _cachedChannels; - for (final channel in allChannels) { - total += channel.unreadCount; - } - return total; + _cachedChannelsUnreadTotal = allChannels.fold(0, (total, ch) => total + ch.unreadCount); } bool isChannelSmazEnabled(int channelIndex) { @@ -377,11 +385,13 @@ class MeshCoreConnector extends ChangeNotifier { ..clear() ..addAll(await _unreadStore.loadContactUnreadCount()); _unreadStateLoaded = true; + _recalculateCachedUnreadTotals(); notifyListeners(); } Future loadCachedChannels() async { _cachedChannels = await _channelStore.loadChannels(); + _recalculateCachedChannelsUnreadTotal(); } void setActiveContact(String? contactKeyHex) { @@ -408,6 +418,7 @@ class MeshCoreConnector extends ChangeNotifier { final previousCount = _contactUnreadCount[contactKeyHex] ?? 0; if (previousCount > 0) { _contactUnreadCount[contactKeyHex] = 0; + _cachedContactsUnreadTotal -= previousCount; _appDebugLogService?.info( 'Contact $contactKeyHex marked as read (was $previousCount unread)', tag: 'Unread', @@ -424,6 +435,7 @@ class MeshCoreConnector extends ChangeNotifier { if (channel != null && channel.unreadCount > 0) { final previousCount = channel.unreadCount; channel.unreadCount = 0; + _cachedChannelsUnreadTotal -= previousCount; _appDebugLogService?.info( 'Channel ${channel.name.isNotEmpty ? channel.name : channelIndex} marked as read (was $previousCount unread)', tag: 'Unread', @@ -1656,6 +1668,7 @@ class MeshCoreConnector extends ChangeNotifier { if (completed) { _hasLoadedChannels = true; _previousChannelsCache.clear(); + _recalculateCachedChannelsUnreadTotal(); } // Keep cache on failure/disconnection for future attempts } @@ -2703,6 +2716,7 @@ class MeshCoreConnector extends ChangeNotifier { final channel = _findChannelByIndex(channelIndex); if (channel != null) { channel.unreadCount++; + _cachedChannelsUnreadTotal++; _appDebugLogService?.info( 'Channel ${channel.name.isNotEmpty ? channel.name : channelIndex} unread count incremented to ${channel.unreadCount}', tag: 'Unread', @@ -2747,6 +2761,7 @@ class MeshCoreConnector extends ChangeNotifier { final currentCount = _contactUnreadCount[contactKey] ?? 0; _contactUnreadCount[contactKey] = currentCount + 1; + _cachedContactsUnreadTotal++; _appDebugLogService?.info( 'Contact $contactKey unread count incremented to ${currentCount + 1}', tag: 'Unread', From 68bb031bb68c0bd2cf25d7ce85263e243e464bcd Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Mon, 9 Feb 2026 17:34:18 +0200 Subject: [PATCH 05/20] "Users first" instead of "People first" everywhere --- lib/screens/contacts_screen.dart | 23 ++++++++++++----------- lib/widgets/list_filter_widget.dart | 18 +++++++++--------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/lib/screens/contacts_screen.dart b/lib/screens/contacts_screen.dart index 394970f9..97e0d513 100644 --- a/lib/screens/contacts_screen.dart +++ b/lib/screens/contacts_screen.dart @@ -48,7 +48,7 @@ class _ContactsScreenState extends State String _searchQuery = ''; ContactSortOption _sortOption = ContactSortOption.lastSeen; bool _showUnreadOnly = false; - bool _prioritizePeople = true; + bool _prioritizeUsers = true; ContactTypeFilter _typeFilter = ContactTypeFilter.all; final ContactGroupStore _groupStore = ContactGroupStore(); List _groups = []; @@ -353,7 +353,7 @@ class _ContactsScreenState extends State sortOption: _sortOption, typeFilter: _typeFilter, showUnreadOnly: _showUnreadOnly, - prioritizePeople: _prioritizePeople, + prioritizeUsers: _prioritizeUsers, onSortChanged: (value) { setState(() { _sortOption = value; @@ -369,9 +369,9 @@ class _ContactsScreenState extends State _showUnreadOnly = value; }); }, - onPrioritizePeopleChanged: (value) { + onPrioritizeUsersChanged: (value) { setState(() { - _prioritizePeople = value; + _prioritizeUsers = value; }); }, onNewGroup: () => _showGroupEditor(context, connector.contacts), @@ -554,18 +554,19 @@ class _ContactsScreenState extends State }).toList(); } - // Apply sorting within groups if prioritizing people - if (_prioritizePeople) { - // Separate people (advTypeChat) from others - final people = filtered.where((c) => c.type == advTypeChat).toList(); + // Apply "users first" partitioning: separate users from other node types, + // sort each partition, then combine with users on top + if (_prioritizeUsers) { + // Separate users (advTypeChat) from others + final users = filtered.where((c) => c.type == advTypeChat).toList(); final others = filtered.where((c) => c.type != advTypeChat).toList(); // Sort each group separately - _applySorting(people, connector); + _applySorting(users, connector); _applySorting(others, connector); - // Combine: people first, then others - filtered = [...people, ...others]; + // Combine: users first, then others + filtered = [...users, ...others]; } else { _applySorting(filtered, connector); } diff --git a/lib/widgets/list_filter_widget.dart b/lib/widgets/list_filter_widget.dart index 6075fecb..4b233708 100644 --- a/lib/widgets/list_filter_widget.dart +++ b/lib/widgets/list_filter_widget.dart @@ -99,17 +99,17 @@ const int _actionFilterRepeaters = 6; const int _actionFilterRooms = 7; const int _actionToggleUnreadOnly = 8; const int _actionNewGroup = 9; -const int _actionTogglePrioritizePeople = 10; +const int _actionTogglePrioritizeUsers = 10; class ContactsFilterMenu extends StatelessWidget { final ContactSortOption sortOption; final ContactTypeFilter typeFilter; final bool showUnreadOnly; - final bool prioritizePeople; + final bool prioritizeUsers; final ValueChanged onSortChanged; final ValueChanged onTypeFilterChanged; final ValueChanged onUnreadOnlyChanged; - final ValueChanged onPrioritizePeopleChanged; + final ValueChanged onPrioritizeUsersChanged; final VoidCallback onNewGroup; const ContactsFilterMenu({ @@ -117,11 +117,11 @@ class ContactsFilterMenu extends StatelessWidget { required this.sortOption, required this.typeFilter, required this.showUnreadOnly, - required this.prioritizePeople, + required this.prioritizeUsers, required this.onSortChanged, required this.onTypeFilterChanged, required this.onUnreadOnlyChanged, - required this.onPrioritizePeopleChanged, + required this.onPrioritizeUsersChanged, required this.onNewGroup, }); @@ -150,9 +150,9 @@ class ContactsFilterMenu extends StatelessWidget { checked: sortOption == ContactSortOption.name, ), SortFilterMenuOption( - value: _actionTogglePrioritizePeople, + value: _actionTogglePrioritizeUsers, label: l10n.listFilter_usersFirst, - checked: prioritizePeople, + checked: prioritizeUsers, ), ], ), @@ -202,8 +202,8 @@ class ContactsFilterMenu extends StatelessWidget { case _actionSortLastSeen: onSortChanged(ContactSortOption.lastSeen); break; - case _actionTogglePrioritizePeople: - onPrioritizePeopleChanged(!prioritizePeople); + case _actionTogglePrioritizeUsers: + onPrioritizeUsersChanged(!prioritizeUsers); break; case _actionFilterAll: onTypeFilterChanged(ContactTypeFilter.all); From 87bcb6a6a37c1fc6f7b7a653b06432103386c358 Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Mon, 9 Feb 2026 17:40:56 +0200 Subject: [PATCH 06/20] Proper formatting --- lib/widgets/quick_switch_bar.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/widgets/quick_switch_bar.dart b/lib/widgets/quick_switch_bar.dart index 78874fdf..bbac8d3b 100644 --- a/lib/widgets/quick_switch_bar.dart +++ b/lib/widgets/quick_switch_bar.dart @@ -103,7 +103,7 @@ class QuickSwitchBar extends StatelessWidget { Widget _buildIconWithBadge(Icon icon, int count) { if (count <= 0) return icon; - + return Badge( label: Text( count > 99 ? '99+' : count.toString(), From afcc4db4054a5d4886b003dfafb7909ba4757e05 Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Wed, 18 Feb 2026 20:15:29 +0200 Subject: [PATCH 07/20] fix: clamp cached unread totals to prevent negative badge counts Clamp both _cachedContactsUnreadTotal and _cachedChannelsUnreadTotal to >= 0 after decrementing in markContactRead() and markChannelRead(). This prevents the totals from going negative if the cache drifts out-of-sync, which could cause UI badges to display incorrect values. --- lib/connector/meshcore_connector.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 9483a202..a426df34 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -418,7 +418,7 @@ class MeshCoreConnector extends ChangeNotifier { final previousCount = _contactUnreadCount[contactKeyHex] ?? 0; if (previousCount > 0) { _contactUnreadCount[contactKeyHex] = 0; - _cachedContactsUnreadTotal -= previousCount; + _cachedContactsUnreadTotal = (_cachedContactsUnreadTotal - previousCount).clamp(0, _cachedContactsUnreadTotal); _appDebugLogService?.info( 'Contact $contactKeyHex marked as read (was $previousCount unread)', tag: 'Unread', @@ -435,7 +435,7 @@ class MeshCoreConnector extends ChangeNotifier { if (channel != null && channel.unreadCount > 0) { final previousCount = channel.unreadCount; channel.unreadCount = 0; - _cachedChannelsUnreadTotal -= previousCount; + _cachedChannelsUnreadTotal = (_cachedChannelsUnreadTotal - previousCount).clamp(0, _cachedChannelsUnreadTotal); _appDebugLogService?.info( 'Channel ${channel.name.isNotEmpty ? channel.name : channelIndex} marked as read (was $previousCount unread)', tag: 'Unread', From a30fc439f3b5e7553c3cb41dda0e2bf96de84200 Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Fri, 27 Feb 2026 12:22:26 +0200 Subject: [PATCH 08/20] refactor: use UnreadBadge widget in QuickSwitchBar for consistent badge styling --- lib/widgets/quick_switch_bar.dart | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/widgets/quick_switch_bar.dart b/lib/widgets/quick_switch_bar.dart index bbac8d3b..5a157a02 100644 --- a/lib/widgets/quick_switch_bar.dart +++ b/lib/widgets/quick_switch_bar.dart @@ -2,6 +2,7 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import '../l10n/l10n.dart'; +import 'unread_badge.dart'; class QuickSwitchBar extends StatelessWidget { final int selectedIndex; @@ -104,12 +105,16 @@ class QuickSwitchBar extends StatelessWidget { Widget _buildIconWithBadge(Icon icon, int count) { if (count <= 0) return icon; - return Badge( - label: Text( - count > 99 ? '99+' : count.toString(), - style: const TextStyle(fontSize: 10), - ), - child: icon, + return Stack( + clipBehavior: Clip.none, + children: [ + icon, + Positioned( + right: -6, + top: -4, + child: UnreadBadge(count: count), + ), + ], ); } } From c47a4cb622b715fe1a506848f01291a3e442237f Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Fri, 27 Feb 2026 12:28:57 +0200 Subject: [PATCH 09/20] fix: filter by _shouldTrackUnreadForContactKey when recalculating cached contacts unread total --- lib/connector/meshcore_connector.dart | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index a426df34..34ab744d 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -360,7 +360,13 @@ class MeshCoreConnector extends ChangeNotifier { } void _recalculateCachedContactsUnreadTotal() { - _cachedContactsUnreadTotal = _contactUnreadCount.values.fold(0, (a, b) => a + b); + int total = 0; + _contactUnreadCount.forEach((contactKeyHex, count) { + if (_shouldTrackUnreadForContactKey(contactKeyHex)) { + total += count; + } + }); + _cachedContactsUnreadTotal = total; } void _recalculateCachedChannelsUnreadTotal() { From 87b25655d02b65a520135a5aac52f3ebe41a7f2a Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Fri, 27 Feb 2026 12:43:21 +0200 Subject: [PATCH 10/20] Package updates from main --- pubspec.lock | 210 +++++++++++++++--------- windows/flutter/generated_plugins.cmake | 1 + 2 files changed, 130 insertions(+), 81 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index fc116566..2e3329c4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: archive - sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff url: "https://pub.dev" source: hosted - version: "4.0.7" + version: "4.0.9" args: dependency: transitive description: @@ -153,14 +153,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + dart_polylabel2: + dependency: transitive + description: + name: dart_polylabel2 + sha256: "7eeab15ce72894e4bdba6a8765712231fc81be0bd95247de4ad9966abc57adc6" + url: "https://pub.dev" + source: hosted + version: "1.0.0" dbus: dependency: transitive description: name: dbus - sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c" + sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270 url: "https://pub.dev" source: hosted - version: "0.7.11" + version: "0.7.12" fake_async: dependency: transitive description: @@ -173,10 +181,10 @@ packages: dependency: transitive description: name: ffi - sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c + sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.2.0" file: dependency: transitive description: @@ -202,58 +210,58 @@ packages: dependency: "direct main" description: name: flutter_blue_plus - sha256: "399b3dbc15562ef59749f04e43a99ccbb91540022380d5f269aff3c2787534e4" + sha256: "4fba86c513feab2c5cdb9497da0910ed5b50c0fa8d6cec4a26ffb1a558a24eb8" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.1" flutter_blue_plus_android: dependency: transitive description: name: flutter_blue_plus_android - sha256: "5010b0960cce533a8fa71401573f044362c3e2e111dc6eb4898c92e85f85f50c" + sha256: "2a73e264685574d1d29dcdd565bad9ecfdf237630237c508ae8b47f5cc791f1d" url: "https://pub.dev" source: hosted - version: "8.1.0" + version: "8.2.1" flutter_blue_plus_darwin: dependency: transitive description: name: flutter_blue_plus_darwin - sha256: d160a8128e3a016fa58dd65ab6dac05cbc73e0fa799a1f24211d041641ed63ba + sha256: cfef171db550670cf8110f6eb25baf15d9bc8bad2af29550f9bbc0d8fceaf285 url: "https://pub.dev" source: hosted - version: "8.1.0" + version: "8.2.1" flutter_blue_plus_linux: dependency: transitive description: name: flutter_blue_plus_linux - sha256: f5b02244d89465ba82c8c512686c66362fbb01f52fa03d645ed353ebf3883242 + sha256: "5add6c14d2f90672c5e3ded1455b9ca8e6fe44adf9b53cdc60eb3417d38f34fe" url: "https://pub.dev" source: hosted - version: "8.1.0" + version: "8.2.1" flutter_blue_plus_platform_interface: dependency: transitive description: name: flutter_blue_plus_platform_interface - sha256: "6e0fc04b77491dbfdbcd46c1a021b12f2f5fc5d6e01777f93a38a8431989b7f0" + sha256: "226fb6753a74a407e3b9975c0fc00de02c490ae655b31c6508cb5790ad30965d" url: "https://pub.dev" source: hosted - version: "8.1.0" + version: "8.2.1" flutter_blue_plus_web: dependency: transitive description: name: flutter_blue_plus_web - sha256: "376aad9595ee389c7cd56e0c373e78abcaa790c821ece9cb81f0969ec94c5bca" + sha256: "10a7465ccfc50138280abf32c8ab314f5029aa19039628ad9b4d0ed786e0021f" url: "https://pub.dev" source: hosted - version: "8.1.0" + version: "8.2.1" flutter_blue_plus_winrt: dependency: transitive description: name: flutter_blue_plus_winrt - sha256: "34be2d8e23d5881b46accebb0e71025f7d52869d72ea98b5082c20764e06aa80" + sha256: ed894f0ab341f4cece8fa33edc381d46424a7c5bfd0e841d933d0f8c34c86521 url: "https://pub.dev" source: hosted - version: "0.0.16" + version: "0.0.18" flutter_cache_manager: dependency: "direct main" description: @@ -266,18 +274,18 @@ packages: dependency: "direct main" description: name: flutter_foreground_task - sha256: "6cf10a27f5e344cd2ecad0752d3a5f4ec32846d82fda8753b3fe2480ebb832a3" + sha256: "48ea45056155a99fb30b15f14f4039a044d925bc85f381ed0b2d3b00a60b99de" url: "https://pub.dev" source: hosted - version: "6.5.0" + version: "9.2.0" flutter_launcher_icons: dependency: "direct dev" description: name: flutter_launcher_icons - sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" + sha256: "10f13781741a2e3972126fae08393d3c4e01fa4cd7473326b94b72cf594195e7" url: "https://pub.dev" source: hosted - version: "0.13.1" + version: "0.14.4" flutter_linkify: dependency: "direct main" description: @@ -290,34 +298,42 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "6.0.0" flutter_local_notifications: dependency: "direct main" description: name: flutter_local_notifications - sha256: ef41ae901e7529e52934feba19ed82827b11baa67336829564aeab3129460610 + sha256: "2b50e938a275e1ad77352d6a25e25770f4130baa61eaf02de7a9a884680954ad" url: "https://pub.dev" source: hosted - version: "18.0.1" + version: "20.1.0" flutter_local_notifications_linux: dependency: transitive description: name: flutter_local_notifications_linux - sha256: "8f685642876742c941b29c32030f6f4f6dacd0e4eaecb3efbb187d6a3812ca01" + sha256: dce0116868cedd2cdf768af0365fc37ff1cbef7c02c4f51d0587482e625868d0 url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "7.0.0" flutter_local_notifications_platform_interface: dependency: transitive description: name: flutter_local_notifications_platform_interface - sha256: "6c5b83c86bf819cdb177a9247a3722067dd8cc6313827ce7c77a4b238a26fd52" + sha256: "23de31678a48c084169d7ae95866df9de5c9d2a44be3e5915a2ff067aeeba899" url: "https://pub.dev" source: hosted - version: "8.0.0" + version: "10.0.0" + flutter_local_notifications_windows: + dependency: transitive + description: + name: flutter_local_notifications_windows + sha256: e97a1a3016512437d9c0b12fae7d1491c3c7b9aa7f03a69b974308840656b02a + url: "https://pub.dev" + source: hosted + version: "2.0.1" flutter_localizations: dependency: "direct main" description: flutter @@ -327,10 +343,18 @@ packages: dependency: "direct main" description: name: flutter_map - sha256: "2ecb34619a4be19df6f40c2f8dce1591675b4eff7a6857bd8f533706977385da" + sha256: "391e7dc95cc3f5190748210a69d4cfeb5d8f84dcdfa9c3235d0a9d7742ccb3f8" url: "https://pub.dev" source: hosted - version: "7.0.2" + version: "8.2.2" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + sha256: "87fbd7c534435b6c5d9d98b01e1fd527812b82e68ddd8bd35fc45ed0fa8f0a95" + url: "https://pub.dev" + source: hosted + version: "2.2.3" flutter_test: dependency: "direct dev" description: flutter @@ -361,10 +385,10 @@ packages: dependency: transitive description: name: hooks - sha256: "5d309c86e7ce34cd8e37aa71cb30cb652d3829b900ab145e4d9da564b31d59f7" + sha256: "7a08a0d684cb3b8fb604b78455d5d352f502b68079f7b80b831c62220ab0a4f6" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.1" http: dependency: "direct main" description: @@ -385,10 +409,10 @@ packages: dependency: transitive description: name: image - sha256: "492bd52f6c4fbb6ee41f781ff27765ce5f627910e1e0cbecfa3d9add5562604c" + sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce url: "https://pub.dev" source: hosted - version: "4.7.2" + version: "4.8.0" intl: dependency: "direct main" description: @@ -397,22 +421,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.20.2" - js: - dependency: transitive - description: - name: js - sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" - url: "https://pub.dev" - source: hosted - version: "0.7.2" json_annotation: dependency: transitive description: name: json_annotation - sha256: "805fa86df56383000f640384b282ce0cb8431f1a7a2396de92fb66186d8c57df" + sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8 url: "https://pub.dev" source: hosted - version: "4.10.0" + version: "4.11.0" latlong2: dependency: "direct main" description: @@ -457,10 +473,10 @@ packages: dependency: transitive description: name: lints - sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "6.1.0" lists: dependency: transitive description: @@ -501,6 +517,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.11.1" + material_symbols_icons: + dependency: "direct main" + description: + name: material_symbols_icons + sha256: c62b15f2b3de98d72cbff0148812f5ef5159f05e61fc9f9a089ec2bb234df082 + url: "https://pub.dev" + source: hosted + version: "4.2906.0" meta: dependency: transitive description: @@ -529,10 +553,10 @@ packages: dependency: "direct main" description: name: mobile_scanner - sha256: "0b466a0a8a211b366c2e87f3345715faef9b6011c7147556ad22f37de6ba3173" + sha256: c92c26bf2231695b6d3477c8dcf435f51e28f87b1745966b1fe4c47a286171ce url: "https://pub.dev" source: hosted - version: "6.0.11" + version: "7.2.0" native_toolchain_c: dependency: transitive description: @@ -553,10 +577,10 @@ packages: dependency: transitive description: name: objective_c - sha256: "983c7fa1501f6dcc0cb7af4e42072e9993cb28d73604d25ebf4dab08165d997e" + sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52" url: "https://pub.dev" source: hosted - version: "9.2.5" + version: "9.3.0" octo_image: dependency: transitive description: @@ -569,10 +593,10 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: "16eee997588c60225bda0488b6dcfac69280a6b7a3cf02c741895dd370a02968" + sha256: f69da0d3189a4b4ceaeb1a3defb0f329b3b352517f52bed4290f83d4f06bc08d url: "https://pub.dev" source: hosted - version: "8.3.1" + version: "9.0.0" package_info_plus_platform_interface: dependency: transitive description: @@ -589,6 +613,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" + url: "https://pub.dev" + source: hosted + version: "1.1.0" path_provider: dependency: "direct main" description: @@ -641,10 +673,10 @@ packages: dependency: transitive description: name: petitparser - sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1" + sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675" url: "https://pub.dev" source: hosted - version: "7.0.1" + version: "7.0.2" platform: dependency: transitive description: @@ -665,26 +697,18 @@ packages: dependency: "direct main" description: name: pointycastle - sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" + sha256: "92aa3841d083cc4b0f4709b5c74fd6409a3e6ba833ffc7dc6a8fee096366acf5" url: "https://pub.dev" source: hosted - version: "3.9.1" - polylabel: - dependency: transitive - description: - name: polylabel - sha256: "41b9099afb2aa6c1730bdd8a0fab1400d287694ec7615dd8516935fa3144214b" - url: "https://pub.dev" - source: hosted - version: "1.0.1" + version: "4.0.0" posix: dependency: transitive description: name: posix - sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" + sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07" url: "https://pub.dev" source: hosted - version: "6.0.3" + version: "6.5.0" proj4dart: dependency: transitive description: @@ -769,10 +793,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: cbc40be9be1c5af4dab4d6e0de4d5d3729e6f3d65b89d21e1815d57705644a6f + sha256: "8374d6200ab33ac99031a852eba4c8eb2170c4bf20778b3e2c9eccb45384fb41" url: "https://pub.dev" source: hosted - version: "2.4.20" + version: "2.4.21" shared_preferences_foundation: dependency: transitive description: @@ -822,10 +846,10 @@ packages: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.2" sqflite: dependency: transitive description: @@ -958,10 +982,10 @@ packages: dependency: transitive description: name: url_launcher_ios - sha256: cfde38aa257dae62ffe79c87fab20165dfdf6988c1d31b58ebf59b9106062aad + sha256: "580fe5dfb51671ae38191d316e027f6b76272b026370708c2d898799750a02b0" url: "https://pub.dev" source: hosted - version: "6.3.6" + version: "6.4.1" url_launcher_linux: dependency: transitive description: @@ -1006,10 +1030,34 @@ packages: dependency: "direct main" description: name: uuid - sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8 + sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489" url: "https://pub.dev" source: hosted - version: "4.5.2" + version: "4.5.3" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6 + url: "https://pub.dev" + source: hosted + version: "1.1.19" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" + url: "https://pub.dev" + source: hosted + version: "1.1.13" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "5a88dd14c0954a5398af544651c7fb51b457a2a556949bfb25369b210ef73a74" + url: "https://pub.dev" + source: hosted + version: "1.2.0" vector_math: dependency: transitive description: @@ -1030,10 +1078,10 @@ packages: dependency: "direct main" description: name: wakelock_plus - sha256: "61713aa82b7f85c21c9f4cd0a148abd75f38a74ec645fcb1e446f882c82fd09b" + sha256: "9296d40c9adbedaba95d1e704f4e0b434be446e2792948d0e4aa977048104228" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.4.0" wakelock_plus_platform_interface: dependency: transitive description: @@ -1043,7 +1091,7 @@ packages: source: hosted version: "1.3.0" web: - dependency: transitive + dependency: "direct main" description: name: web sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 571addb8..4c358e7f 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -9,6 +9,7 @@ list(APPEND FLUTTER_PLUGIN_LIST ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + flutter_local_notifications_windows ) set(PLUGIN_BUNDLED_LIBRARIES) From 3ae14781f0c07650a0d6bfa84830cc1cec941ccf Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Fri, 27 Feb 2026 12:58:32 +0200 Subject: [PATCH 11/20] AI translations for "Users first" --- lib/l10n/app_bg.arb | 1 + lib/l10n/app_de.arb | 1 + lib/l10n/app_es.arb | 1 + lib/l10n/app_fr.arb | 1 + lib/l10n/app_it.arb | 1 + lib/l10n/app_localizations_bg.dart | 2 +- lib/l10n/app_localizations_de.dart | 2 +- lib/l10n/app_localizations_es.dart | 2 +- lib/l10n/app_localizations_fr.dart | 2 +- lib/l10n/app_localizations_it.dart | 2 +- lib/l10n/app_localizations_nl.dart | 2 +- lib/l10n/app_localizations_pl.dart | 2 +- lib/l10n/app_localizations_pt.dart | 2 +- lib/l10n/app_localizations_ru.dart | 2 +- lib/l10n/app_localizations_sk.dart | 2 +- lib/l10n/app_localizations_sl.dart | 2 +- lib/l10n/app_localizations_sv.dart | 2 +- lib/l10n/app_nl.arb | 1 + lib/l10n/app_pl.arb | 1 + lib/l10n/app_pt.arb | 1 + lib/l10n/app_ru.arb | 1 + lib/l10n/app_sk.arb | 1 + lib/l10n/app_sl.arb | 1 + lib/l10n/app_sv.arb | 1 + lib/l10n/app_zh.arb | 1 + 25 files changed, 25 insertions(+), 12 deletions(-) diff --git a/lib/l10n/app_bg.arb b/lib/l10n/app_bg.arb index 975e0671..daa2b11b 100644 --- a/lib/l10n/app_bg.arb +++ b/lib/l10n/app_bg.arb @@ -1352,6 +1352,7 @@ "listFilter_users": "Потребители", "listFilter_repeaters": "Повторители", "listFilter_roomServers": "Сървъри на стая", + "listFilter_usersFirst": "Първо потребители", "listFilter_unreadOnly": "Само непрочетените", "listFilter_newGroup": "Нова група", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 74b6c05a..f39c3420 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -1355,6 +1355,7 @@ "listFilter_users": "Benutzer", "listFilter_repeaters": "Repeater", "listFilter_roomServers": "Raumserver", + "listFilter_usersFirst": "Benutzer zuerst", "listFilter_unreadOnly": "Nicht gelesen", "listFilter_newGroup": "Neue Gruppe", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 74339ff7..8824ddb1 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -1352,6 +1352,7 @@ "listFilter_users": "Usuarios", "listFilter_repeaters": "Repetidores", "listFilter_roomServers": "Servidores de la sala", + "listFilter_usersFirst": "Usuarios primero", "listFilter_unreadOnly": "Solo sin leer", "listFilter_newGroup": "Nuevo grupo", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 0697aee7..9b1c79fb 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -1352,6 +1352,7 @@ "listFilter_users": "Utilisateurs", "listFilter_repeaters": "Répéteurs", "listFilter_roomServers": "Room servers", + "listFilter_usersFirst": "Utilisateurs en premier", "listFilter_unreadOnly": "Messages non lus seulement", "listFilter_newGroup": "Nouveau groupe", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index 4798d263..16a1e770 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -1352,6 +1352,7 @@ "listFilter_users": "Utenti", "listFilter_repeaters": "Ripetitori", "listFilter_roomServers": "Server della stanza", + "listFilter_usersFirst": "Utenti per primi", "listFilter_unreadOnly": "Solo non letto", "listFilter_newGroup": "Nuovo gruppo", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_localizations_bg.dart b/lib/l10n/app_localizations_bg.dart index 123d01c2..0221945c 100644 --- a/lib/l10n/app_localizations_bg.dart +++ b/lib/l10n/app_localizations_bg.dart @@ -2751,7 +2751,7 @@ class AppLocalizationsBg extends AppLocalizations { String get listFilter_az => 'A-Z'; @override - String get listFilter_usersFirst => 'Users first'; + String get listFilter_usersFirst => 'Първо потребители'; @override String get listFilter_filters => 'Филтри'; diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 18a9aec3..afcb7c76 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -2756,7 +2756,7 @@ class AppLocalizationsDe extends AppLocalizations { String get listFilter_az => 'A-Z'; @override - String get listFilter_usersFirst => 'Users first'; + String get listFilter_usersFirst => 'Benutzer zuerst'; @override String get listFilter_filters => 'Filtere'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index 66fe8237..1f00a634 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -2749,7 +2749,7 @@ class AppLocalizationsEs extends AppLocalizations { String get listFilter_az => 'A-Z'; @override - String get listFilter_usersFirst => 'Users first'; + String get listFilter_usersFirst => 'Usuarios primero'; @override String get listFilter_filters => 'Filtros'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 903ef299..1515a99d 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -2765,7 +2765,7 @@ class AppLocalizationsFr extends AppLocalizations { String get listFilter_az => 'A à Z'; @override - String get listFilter_usersFirst => 'Users first'; + String get listFilter_usersFirst => 'Utilisateurs en premier'; @override String get listFilter_filters => 'Filtres'; diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index 67c6c925..ce25bdf7 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -2749,7 +2749,7 @@ class AppLocalizationsIt extends AppLocalizations { String get listFilter_az => 'A-Z'; @override - String get listFilter_usersFirst => 'Users first'; + String get listFilter_usersFirst => 'Utenti per primi'; @override String get listFilter_filters => 'Filtri'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index 313d37f7..634c7717 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -2740,7 +2740,7 @@ class AppLocalizationsNl extends AppLocalizations { String get listFilter_az => 'A-Z'; @override - String get listFilter_usersFirst => 'Users first'; + String get listFilter_usersFirst => 'Gebruikers eerst'; @override String get listFilter_filters => 'Filters'; diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 77137f2e..91e26df7 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -2747,7 +2747,7 @@ class AppLocalizationsPl extends AppLocalizations { String get listFilter_az => 'A-Z'; @override - String get listFilter_usersFirst => 'Users first'; + String get listFilter_usersFirst => 'Najpierw użytkownicy'; @override String get listFilter_filters => 'Filtry'; diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index 0606f1c6..e781514c 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -2750,7 +2750,7 @@ class AppLocalizationsPt extends AppLocalizations { String get listFilter_az => 'A-Z'; @override - String get listFilter_usersFirst => 'Users first'; + String get listFilter_usersFirst => 'Utilizadores primeiro'; @override String get listFilter_filters => 'Filtros'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index 1c05f651..f2784f7c 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -2753,7 +2753,7 @@ class AppLocalizationsRu extends AppLocalizations { String get listFilter_az => 'По алфавиту'; @override - String get listFilter_usersFirst => 'Users first'; + String get listFilter_usersFirst => 'Сначала пользователи'; @override String get listFilter_filters => 'Фильтры'; diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index f71695b7..640fbc4c 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -2735,7 +2735,7 @@ class AppLocalizationsSk extends AppLocalizations { String get listFilter_az => 'A-Z'; @override - String get listFilter_usersFirst => 'Users first'; + String get listFilter_usersFirst => 'Najskôr používatelia'; @override String get listFilter_filters => 'Filtre'; diff --git a/lib/l10n/app_localizations_sl.dart b/lib/l10n/app_localizations_sl.dart index 62de2713..2cbf9b40 100644 --- a/lib/l10n/app_localizations_sl.dart +++ b/lib/l10n/app_localizations_sl.dart @@ -2738,7 +2738,7 @@ class AppLocalizationsSl extends AppLocalizations { String get listFilter_az => 'A-Z'; @override - String get listFilter_usersFirst => 'Users first'; + String get listFilter_usersFirst => 'Najprej uporabniki'; @override String get listFilter_filters => 'Filtri'; diff --git a/lib/l10n/app_localizations_sv.dart b/lib/l10n/app_localizations_sv.dart index 49ee78e0..63e27e51 100644 --- a/lib/l10n/app_localizations_sv.dart +++ b/lib/l10n/app_localizations_sv.dart @@ -2723,7 +2723,7 @@ class AppLocalizationsSv extends AppLocalizations { String get listFilter_az => 'A-Z'; @override - String get listFilter_usersFirst => 'Users first'; + String get listFilter_usersFirst => 'Användare först'; @override String get listFilter_filters => 'Filteralternativ'; diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index 69ebecce..0cf24fa0 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -1352,6 +1352,7 @@ "listFilter_users": "Gebruikers", "listFilter_repeaters": "Repeaters", "listFilter_roomServers": "Roomservers", + "listFilter_usersFirst": "Gebruikers eerst", "listFilter_unreadOnly": "Alleen ongelezen", "listFilter_newGroup": "Nieuwe groep", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 75e1d348..34543eb1 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -1352,6 +1352,7 @@ "listFilter_users": "Użytkownicy", "listFilter_repeaters": "Powtarzacze", "listFilter_roomServers": "Serwery pokoju", + "listFilter_usersFirst": "Najpierw użytkownicy", "listFilter_unreadOnly": "Tylko nieprzeczytane", "listFilter_newGroup": "Nowa grupa", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index f6ada195..38c7ed69 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -1352,6 +1352,7 @@ "listFilter_users": "Usuários", "listFilter_repeaters": "Repetidores", "listFilter_roomServers": "Servidores de sala", + "listFilter_usersFirst": "Utilizadores primeiro", "listFilter_unreadOnly": "Apenas não lido", "listFilter_newGroup": "Novo grupo", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index 9aef298e..a87fe09a 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -758,6 +758,7 @@ "listFilter_users": "Пользователи", "listFilter_repeaters": "Репитеры", "listFilter_roomServers": "Серверы комнат", + "listFilter_usersFirst": "Сначала пользователи", "listFilter_unreadOnly": "Только непрочитанные", "listFilter_newGroup": "Новая группа", "@chat_couldNotOpenLink": { diff --git a/lib/l10n/app_sk.arb b/lib/l10n/app_sk.arb index 672b7d74..34b4c86a 100644 --- a/lib/l10n/app_sk.arb +++ b/lib/l10n/app_sk.arb @@ -1352,6 +1352,7 @@ "listFilter_users": "Používatelia", "listFilter_repeaters": "Opakovadlá", "listFilter_roomServers": "Servéry miestnosti", + "listFilter_usersFirst": "Najskôr používatelia", "listFilter_unreadOnly": "Nezaregistrované len", "listFilter_newGroup": "Nová skupina", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_sl.arb b/lib/l10n/app_sl.arb index 09359a13..ff16b953 100644 --- a/lib/l10n/app_sl.arb +++ b/lib/l10n/app_sl.arb @@ -1352,6 +1352,7 @@ "listFilter_users": "Uporabniki", "listFilter_repeaters": "Ponovitve", "listFilter_roomServers": "Smeti za prostore", + "listFilter_usersFirst": "Najprej uporabniki", "listFilter_unreadOnly": "Nezbrani samo", "listFilter_newGroup": "Nova skupina", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_sv.arb b/lib/l10n/app_sv.arb index a923cc9c..45541a15 100644 --- a/lib/l10n/app_sv.arb +++ b/lib/l10n/app_sv.arb @@ -1352,6 +1352,7 @@ "listFilter_users": "Användare", "listFilter_repeaters": "Upprepare", "listFilter_roomServers": "Rumservrar", + "listFilter_usersFirst": "Användare först", "listFilter_unreadOnly": "Endast oinlästa", "listFilter_newGroup": "Ny grupp", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 63c02a57..68bb5252 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -1546,6 +1546,7 @@ "listFilter_users": "用户", "listFilter_repeaters": "转发节点", "listFilter_roomServers": "房间服务器", + "listFilter_usersFirst": "用户优先", "listFilter_unreadOnly": "仅显示未读", "listFilter_newGroup": "新建群聊", "pathTrace_you": "我自己", From 1b94442ab657ee70b02329c0858d0a28e27cde90 Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Fri, 27 Feb 2026 21:19:13 +0200 Subject: [PATCH 12/20] Fix action constant collision: change _actionTogglePrioritizeUsers from 10 to 11 --- lib/widgets/list_filter_widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/widgets/list_filter_widget.dart b/lib/widgets/list_filter_widget.dart index 63ab5807..fa7e67e2 100644 --- a/lib/widgets/list_filter_widget.dart +++ b/lib/widgets/list_filter_widget.dart @@ -100,7 +100,7 @@ const int _actionFilterRepeaters = 7; const int _actionFilterRooms = 8; const int _actionToggleUnreadOnly = 9; const int _actionNewGroup = 10; -const int _actionTogglePrioritizeUsers = 10; +const int _actionTogglePrioritizeUsers = 11; class ContactsFilterMenu extends StatelessWidget { final ContactSortOption sortOption; From 297516fc805516a7e026f744228f6ecb85db7c14 Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Tue, 21 Apr 2026 16:31:09 +0300 Subject: [PATCH 13/20] Update cached unread total when removing contact unread entries When contacts are removed in removeContact, _handleContact, or _handleContactAdvert, subtract their unread count from _cachedContactsUnreadTotal immediately so badge counts reflect the true total without waiting for a full reload. --- lib/connector/meshcore_connector.dart | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index e1740461..63a8639e 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -431,7 +431,10 @@ class MeshCoreConnector extends ChangeNotifier { void _recalculateCachedChannelsUnreadTotal() { final allChannels = _channels.isNotEmpty ? _channels : _cachedChannels; - _cachedChannelsUnreadTotal = allChannels.fold(0, (total, ch) => total + ch.unreadCount); + _cachedChannelsUnreadTotal = allChannels.fold( + 0, + (total, ch) => total + ch.unreadCount, + ); } bool isChannelSmazEnabled(int channelIndex) { @@ -484,7 +487,8 @@ class MeshCoreConnector extends ChangeNotifier { final previousCount = _contactUnreadCount[contactKeyHex] ?? 0; if (previousCount > 0) { _contactUnreadCount[contactKeyHex] = 0; - _cachedContactsUnreadTotal = (_cachedContactsUnreadTotal - previousCount).clamp(0, _cachedContactsUnreadTotal); + _cachedContactsUnreadTotal = (_cachedContactsUnreadTotal - previousCount) + .clamp(0, _cachedContactsUnreadTotal); _appDebugLogService?.info( 'Contact $contactKeyHex marked as read (was $previousCount unread)', tag: 'Unread', @@ -501,7 +505,8 @@ class MeshCoreConnector extends ChangeNotifier { if (channel != null && channel.unreadCount > 0) { final previousCount = channel.unreadCount; channel.unreadCount = 0; - _cachedChannelsUnreadTotal = (_cachedChannelsUnreadTotal - previousCount).clamp(0, _cachedChannelsUnreadTotal); + _cachedChannelsUnreadTotal = (_cachedChannelsUnreadTotal - previousCount) + .clamp(0, _cachedChannelsUnreadTotal); _appDebugLogService?.info( 'Channel ${channel.name.isNotEmpty ? channel.name : channelIndex} marked as read (was $previousCount unread)', tag: 'Unread', @@ -1524,6 +1529,9 @@ class MeshCoreConnector extends ChangeNotifier { unawaited(_persistContacts()); _conversations.remove(contact.publicKeyHex); _loadedConversationKeys.remove(contact.publicKeyHex); + final removedCount = _contactUnreadCount[contact.publicKeyHex] ?? 0; + _cachedContactsUnreadTotal = (_cachedContactsUnreadTotal - removedCount) + .clamp(0, _cachedContactsUnreadTotal); _contactUnreadCount.remove(contact.publicKeyHex); _unreadStore.saveContactUnreadCount( Map.from(_contactUnreadCount), @@ -2158,6 +2166,9 @@ class MeshCoreConnector extends ChangeNotifier { final contact = Contact.fromFrame(frame); if (contact != null) { if (contact.type == advTypeRepeater) { + final removedCount = _contactUnreadCount[contact.publicKeyHex] ?? 0; + _cachedContactsUnreadTotal = (_cachedContactsUnreadTotal - removedCount) + .clamp(0, _cachedContactsUnreadTotal); _contactUnreadCount.remove(contact.publicKeyHex); _unreadStore.saveContactUnreadCount( Map.from(_contactUnreadCount), @@ -2233,6 +2244,9 @@ class MeshCoreConnector extends ChangeNotifier { } if (contact.type == advTypeRepeater) { + final removedCount = _contactUnreadCount[contact.publicKeyHex] ?? 0; + _cachedContactsUnreadTotal = (_cachedContactsUnreadTotal - removedCount) + .clamp(0, _cachedContactsUnreadTotal); _contactUnreadCount.remove(contact.publicKeyHex); _unreadStore.saveContactUnreadCount( Map.from(_contactUnreadCount), From 8611adab1fcd2a438e6f47e4d33dab02f876c826 Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Tue, 21 Apr 2026 16:51:10 +0300 Subject: [PATCH 14/20] Run dart format and verify analyze --- lib/widgets/quick_switch_bar.dart | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/widgets/quick_switch_bar.dart b/lib/widgets/quick_switch_bar.dart index 5a157a02..4f014bd3 100644 --- a/lib/widgets/quick_switch_bar.dart +++ b/lib/widgets/quick_switch_bar.dart @@ -109,11 +109,7 @@ class QuickSwitchBar extends StatelessWidget { clipBehavior: Clip.none, children: [ icon, - Positioned( - right: -6, - top: -4, - child: UnreadBadge(count: count), - ), + Positioned(right: -6, top: -4, child: UnreadBadge(count: count)), ], ); } From 9fe4a3710de2f720b4c2395d89bb2a4ac8e122fd Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Tue, 21 Apr 2026 17:01:51 +0300 Subject: [PATCH 15/20] Add missing users-first translations for hu/ja/ko and regen outputs --- lib/l10n/app_hu.arb | 1 + lib/l10n/app_ja.arb | 1 + lib/l10n/app_ko.arb | 1 + lib/l10n/app_localizations_hu.dart | 3 +++ lib/l10n/app_localizations_ja.dart | 3 +++ lib/l10n/app_localizations_ko.dart | 3 +++ linux/flutter/generated_plugins.cmake | 1 - windows/flutter/generated_plugins.cmake | 1 - 8 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/l10n/app_hu.arb b/lib/l10n/app_hu.arb index 6f43463c..1985899c 100644 --- a/lib/l10n/app_hu.arb +++ b/lib/l10n/app_hu.arb @@ -1698,6 +1698,7 @@ "listFilter_latestMessages": "Legfrissebb üzenetek", "listFilter_heardRecently": "Úgy hallottam, hogy...", "listFilter_az": "A-Z", + "listFilter_usersFirst": "Felhasználók elöl", "listFilter_filters": "Szűrők", "listFilter_all": "Mind", "listFilter_favorites": "Kedvencek", diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb index b63f146d..e6ac0151 100644 --- a/lib/l10n/app_ja.arb +++ b/lib/l10n/app_ja.arb @@ -1698,6 +1698,7 @@ "listFilter_latestMessages": "最新のメッセージ", "listFilter_heardRecently": "最近、聞いた", "listFilter_az": "AからZ", + "listFilter_usersFirst": "ユーザー優先", "listFilter_filters": "フィルター", "listFilter_all": "すべて", "listFilter_favorites": "お気に入り", diff --git a/lib/l10n/app_ko.arb b/lib/l10n/app_ko.arb index 40721fe1..d3930a1b 100644 --- a/lib/l10n/app_ko.arb +++ b/lib/l10n/app_ko.arb @@ -1698,6 +1698,7 @@ "listFilter_latestMessages": "최신 메시지", "listFilter_heardRecently": "최근에 들었습니다", "listFilter_az": "A부터 Z까지", + "listFilter_usersFirst": "사용자 우선", "listFilter_filters": "필터", "listFilter_all": "모든", "listFilter_favorites": "관심 목록", diff --git a/lib/l10n/app_localizations_hu.dart b/lib/l10n/app_localizations_hu.dart index 1ad8558c..9557c3cc 100644 --- a/lib/l10n/app_localizations_hu.dart +++ b/lib/l10n/app_localizations_hu.dart @@ -3081,6 +3081,9 @@ class AppLocalizationsHu extends AppLocalizations { @override String get listFilter_az => 'A-Z'; + @override + String get listFilter_usersFirst => 'Felhasználók elöl'; + @override String get listFilter_filters => 'Szűrők'; diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index afb8c29b..b838f9a1 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -2929,6 +2929,9 @@ class AppLocalizationsJa extends AppLocalizations { @override String get listFilter_az => 'AからZ'; + @override + String get listFilter_usersFirst => 'ユーザー優先'; + @override String get listFilter_filters => 'フィルター'; diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart index ff4bd261..3af9dd04 100644 --- a/lib/l10n/app_localizations_ko.dart +++ b/lib/l10n/app_localizations_ko.dart @@ -2929,6 +2929,9 @@ class AppLocalizationsKo extends AppLocalizations { @override String get listFilter_az => 'A부터 Z까지'; + @override + String get listFilter_usersFirst => '사용자 우선'; + @override String get listFilter_filters => '필터'; diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 93e46829..379e36fa 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -8,7 +8,6 @@ list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST flserial - jni ) set(PLUGIN_BUNDLED_LIBRARIES) diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 533a1712..f02857f4 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -11,7 +11,6 @@ list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST flserial flutter_local_notifications_windows - jni ) set(PLUGIN_BUNDLED_LIBRARIES) From 16ce1359d7e0cc23d51427c8a0d1078da8d96800 Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Tue, 21 Apr 2026 23:10:39 +0300 Subject: [PATCH 16/20] Remove unused 'Users first' translation key --- lib/l10n/app_bg.arb | 1 - lib/l10n/app_de.arb | 1 - lib/l10n/app_en.arb | 1 - lib/l10n/app_es.arb | 1 - lib/l10n/app_fr.arb | 1 - lib/l10n/app_hu.arb | 1 - lib/l10n/app_it.arb | 1 - lib/l10n/app_ja.arb | 1 - lib/l10n/app_ko.arb | 1 - lib/l10n/app_localizations.dart | 6 ------ lib/l10n/app_localizations_bg.dart | 3 --- lib/l10n/app_localizations_de.dart | 3 --- lib/l10n/app_localizations_en.dart | 3 --- lib/l10n/app_localizations_es.dart | 3 --- lib/l10n/app_localizations_fr.dart | 3 --- lib/l10n/app_localizations_hu.dart | 3 --- lib/l10n/app_localizations_it.dart | 3 --- lib/l10n/app_localizations_ja.dart | 3 --- lib/l10n/app_localizations_ko.dart | 3 --- lib/l10n/app_localizations_nl.dart | 3 --- lib/l10n/app_localizations_pl.dart | 3 --- lib/l10n/app_localizations_pt.dart | 3 --- lib/l10n/app_localizations_ru.dart | 3 --- lib/l10n/app_localizations_sk.dart | 3 --- lib/l10n/app_localizations_sl.dart | 3 --- lib/l10n/app_localizations_sv.dart | 3 --- lib/l10n/app_localizations_uk.dart | 3 --- lib/l10n/app_localizations_zh.dart | 3 --- lib/l10n/app_nl.arb | 1 - lib/l10n/app_pl.arb | 1 - lib/l10n/app_pt.arb | 1 - lib/l10n/app_ru.arb | 1 - lib/l10n/app_sk.arb | 1 - lib/l10n/app_sl.arb | 1 - lib/l10n/app_sv.arb | 1 - lib/l10n/app_uk.arb | 1 - lib/l10n/app_zh.arb | 1 - 37 files changed, 78 deletions(-) diff --git a/lib/l10n/app_bg.arb b/lib/l10n/app_bg.arb index bd87d03a..7ac54177 100644 --- a/lib/l10n/app_bg.arb +++ b/lib/l10n/app_bg.arb @@ -1353,7 +1353,6 @@ "listFilter_users": "Потребители", "listFilter_repeaters": "Повторители", "listFilter_roomServers": "Сървъри на стая", - "listFilter_usersFirst": "Първо потребители", "listFilter_unreadOnly": "Само непрочетените", "listFilter_newGroup": "Нова група", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 0e63be20..46955052 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -1356,7 +1356,6 @@ "listFilter_users": "Benutzer", "listFilter_repeaters": "Repeater", "listFilter_roomServers": "Raumserver", - "listFilter_usersFirst": "Benutzer zuerst", "listFilter_unreadOnly": "Nicht gelesen", "listFilter_newGroup": "Neue Gruppe", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 6b845b23..8ad6bf37 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1754,7 +1754,6 @@ "listFilter_latestMessages": "Latest messages", "listFilter_heardRecently": "Heard recently", "listFilter_az": "A-Z", - "listFilter_usersFirst": "Users first", "listFilter_filters": "Filters", "listFilter_all": "All", "listFilter_favorites": "Favorites", diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 6e7500ce..ac9527bd 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -1353,7 +1353,6 @@ "listFilter_users": "Usuarios", "listFilter_repeaters": "Repetidores", "listFilter_roomServers": "Servidores de la sala", - "listFilter_usersFirst": "Usuarios primero", "listFilter_unreadOnly": "Solo sin leer", "listFilter_newGroup": "Nuevo grupo", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 7890c3de..a942aa2b 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -1353,7 +1353,6 @@ "listFilter_users": "Utilisateurs", "listFilter_repeaters": "Répéteurs", "listFilter_roomServers": "Room servers", - "listFilter_usersFirst": "Utilisateurs en premier", "listFilter_unreadOnly": "Messages non lus seulement", "listFilter_newGroup": "Nouveau groupe", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_hu.arb b/lib/l10n/app_hu.arb index 1985899c..6f43463c 100644 --- a/lib/l10n/app_hu.arb +++ b/lib/l10n/app_hu.arb @@ -1698,7 +1698,6 @@ "listFilter_latestMessages": "Legfrissebb üzenetek", "listFilter_heardRecently": "Úgy hallottam, hogy...", "listFilter_az": "A-Z", - "listFilter_usersFirst": "Felhasználók elöl", "listFilter_filters": "Szűrők", "listFilter_all": "Mind", "listFilter_favorites": "Kedvencek", diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index a14c5ca5..387c8cff 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -1353,7 +1353,6 @@ "listFilter_users": "Utenti", "listFilter_repeaters": "Ripetitori", "listFilter_roomServers": "Server della stanza", - "listFilter_usersFirst": "Utenti per primi", "listFilter_unreadOnly": "Solo non letto", "listFilter_newGroup": "Nuovo gruppo", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb index e6ac0151..b63f146d 100644 --- a/lib/l10n/app_ja.arb +++ b/lib/l10n/app_ja.arb @@ -1698,7 +1698,6 @@ "listFilter_latestMessages": "最新のメッセージ", "listFilter_heardRecently": "最近、聞いた", "listFilter_az": "AからZ", - "listFilter_usersFirst": "ユーザー優先", "listFilter_filters": "フィルター", "listFilter_all": "すべて", "listFilter_favorites": "お気に入り", diff --git a/lib/l10n/app_ko.arb b/lib/l10n/app_ko.arb index d3930a1b..40721fe1 100644 --- a/lib/l10n/app_ko.arb +++ b/lib/l10n/app_ko.arb @@ -1698,7 +1698,6 @@ "listFilter_latestMessages": "최신 메시지", "listFilter_heardRecently": "최근에 들었습니다", "listFilter_az": "A부터 Z까지", - "listFilter_usersFirst": "사용자 우선", "listFilter_filters": "필터", "listFilter_all": "모든", "listFilter_favorites": "관심 목록", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 4c54dd39..2c1342d0 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -5354,12 +5354,6 @@ abstract class AppLocalizations { /// **'A-Z'** String get listFilter_az; - /// No description provided for @listFilter_usersFirst. - /// - /// In en, this message translates to: - /// **'Users first'** - String get listFilter_usersFirst; - /// No description provided for @listFilter_filters. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_bg.dart b/lib/l10n/app_localizations_bg.dart index c51bb6b9..b3e12799 100644 --- a/lib/l10n/app_localizations_bg.dart +++ b/lib/l10n/app_localizations_bg.dart @@ -3070,9 +3070,6 @@ class AppLocalizationsBg extends AppLocalizations { @override String get listFilter_az => 'A-Z'; - @override - String get listFilter_usersFirst => 'Първо потребители'; - @override String get listFilter_filters => 'Филтри'; diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 7d69ebd3..d7c16914 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -3074,9 +3074,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get listFilter_az => 'A-Z'; - @override - String get listFilter_usersFirst => 'Benutzer zuerst'; - @override String get listFilter_filters => 'Filtere'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index c36d11c0..a2a88b0d 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -3015,9 +3015,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get listFilter_az => 'A-Z'; - @override - String get listFilter_usersFirst => 'Users first'; - @override String get listFilter_filters => 'Filters'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index 326144cf..a1270124 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -3067,9 +3067,6 @@ class AppLocalizationsEs extends AppLocalizations { @override String get listFilter_az => 'A-Z'; - @override - String get listFilter_usersFirst => 'Usuarios primero'; - @override String get listFilter_filters => 'Filtros'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 3bfc05d3..a0063914 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -3086,9 +3086,6 @@ class AppLocalizationsFr extends AppLocalizations { @override String get listFilter_az => 'A à Z'; - @override - String get listFilter_usersFirst => 'Utilisateurs en premier'; - @override String get listFilter_filters => 'Filtres'; diff --git a/lib/l10n/app_localizations_hu.dart b/lib/l10n/app_localizations_hu.dart index 9557c3cc..1ad8558c 100644 --- a/lib/l10n/app_localizations_hu.dart +++ b/lib/l10n/app_localizations_hu.dart @@ -3081,9 +3081,6 @@ class AppLocalizationsHu extends AppLocalizations { @override String get listFilter_az => 'A-Z'; - @override - String get listFilter_usersFirst => 'Felhasználók elöl'; - @override String get listFilter_filters => 'Szűrők'; diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index d077cc87..3a55559a 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -3070,9 +3070,6 @@ class AppLocalizationsIt extends AppLocalizations { @override String get listFilter_az => 'A-Z'; - @override - String get listFilter_usersFirst => 'Utenti per primi'; - @override String get listFilter_filters => 'Filtri'; diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index b838f9a1..afb8c29b 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -2929,9 +2929,6 @@ class AppLocalizationsJa extends AppLocalizations { @override String get listFilter_az => 'AからZ'; - @override - String get listFilter_usersFirst => 'ユーザー優先'; - @override String get listFilter_filters => 'フィルター'; diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart index 3af9dd04..ff4bd261 100644 --- a/lib/l10n/app_localizations_ko.dart +++ b/lib/l10n/app_localizations_ko.dart @@ -2929,9 +2929,6 @@ class AppLocalizationsKo extends AppLocalizations { @override String get listFilter_az => 'A부터 Z까지'; - @override - String get listFilter_usersFirst => '사용자 우선'; - @override String get listFilter_filters => '필터'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index 28fe6dee..dd770e15 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -3053,9 +3053,6 @@ class AppLocalizationsNl extends AppLocalizations { @override String get listFilter_az => 'A-Z'; - @override - String get listFilter_usersFirst => 'Gebruikers eerst'; - @override String get listFilter_filters => 'Filters'; diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 4fab5c11..357dd7e3 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -3079,9 +3079,6 @@ class AppLocalizationsPl extends AppLocalizations { @override String get listFilter_az => 'A-Z'; - @override - String get listFilter_usersFirst => 'Najpierw użytkownicy'; - @override String get listFilter_filters => 'Filtry'; diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index eeb20f3d..2dfcd8bd 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -3068,9 +3068,6 @@ class AppLocalizationsPt extends AppLocalizations { @override String get listFilter_az => 'A-Z'; - @override - String get listFilter_usersFirst => 'Utilizadores primeiro'; - @override String get listFilter_filters => 'Filtros'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index 97f0d26e..4fac42ce 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -3073,9 +3073,6 @@ class AppLocalizationsRu extends AppLocalizations { @override String get listFilter_az => 'По алфавиту'; - @override - String get listFilter_usersFirst => 'Сначала пользователи'; - @override String get listFilter_filters => 'Фильтры'; diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index d2787b69..c42e0249 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -3048,9 +3048,6 @@ class AppLocalizationsSk extends AppLocalizations { @override String get listFilter_az => 'A-Z'; - @override - String get listFilter_usersFirst => 'Najskôr používatelia'; - @override String get listFilter_filters => 'Filtre'; diff --git a/lib/l10n/app_localizations_sl.dart b/lib/l10n/app_localizations_sl.dart index 0dd91eb8..2d89aa41 100644 --- a/lib/l10n/app_localizations_sl.dart +++ b/lib/l10n/app_localizations_sl.dart @@ -3049,9 +3049,6 @@ class AppLocalizationsSl extends AppLocalizations { @override String get listFilter_az => 'A-Z'; - @override - String get listFilter_usersFirst => 'Najprej uporabniki'; - @override String get listFilter_filters => 'Filtri'; diff --git a/lib/l10n/app_localizations_sv.dart b/lib/l10n/app_localizations_sv.dart index a789c3c6..38e08939 100644 --- a/lib/l10n/app_localizations_sv.dart +++ b/lib/l10n/app_localizations_sv.dart @@ -3033,9 +3033,6 @@ class AppLocalizationsSv extends AppLocalizations { @override String get listFilter_az => 'A-Z'; - @override - String get listFilter_usersFirst => 'Användare först'; - @override String get listFilter_filters => 'Filteralternativ'; diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart index 4bba7f7f..4cab52cd 100644 --- a/lib/l10n/app_localizations_uk.dart +++ b/lib/l10n/app_localizations_uk.dart @@ -3075,9 +3075,6 @@ class AppLocalizationsUk extends AppLocalizations { @override String get listFilter_az => 'А-Я'; - @override - String get listFilter_usersFirst => 'Спочатку користувачі'; - @override String get listFilter_filters => 'Фільтри'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index ec3182d3..4f38c64a 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -2854,9 +2854,6 @@ class AppLocalizationsZh extends AppLocalizations { @override String get listFilter_az => 'A-Z'; - @override - String get listFilter_usersFirst => '用户优先'; - @override String get listFilter_filters => '筛选'; diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index a7509184..96bdb845 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -1353,7 +1353,6 @@ "listFilter_users": "Gebruikers", "listFilter_repeaters": "Repeaters", "listFilter_roomServers": "Roomservers", - "listFilter_usersFirst": "Gebruikers eerst", "listFilter_unreadOnly": "Alleen ongelezen", "listFilter_newGroup": "Nieuwe groep", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 2215f632..b62a78af 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -1363,7 +1363,6 @@ "listFilter_users": "Użytkownicy", "listFilter_repeaters": "Przekaźniki", "listFilter_roomServers": "Serwery pokoju", - "listFilter_usersFirst": "Najpierw użytkownicy", "listFilter_unreadOnly": "Tylko nieprzeczytane", "listFilter_newGroup": "Nowa grupa", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index e6fe447c..bf3e8936 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -1353,7 +1353,6 @@ "listFilter_users": "Usuários", "listFilter_repeaters": "Repetidores", "listFilter_roomServers": "Servidores de sala", - "listFilter_usersFirst": "Utilizadores primeiro", "listFilter_unreadOnly": "Apenas não lido", "listFilter_newGroup": "Novo grupo", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index a089c496..a83d1394 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -759,7 +759,6 @@ "listFilter_users": "Пользователи", "listFilter_repeaters": "Репитеры", "listFilter_roomServers": "Серверы комнат", - "listFilter_usersFirst": "Сначала пользователи", "listFilter_unreadOnly": "Только непрочитанные", "listFilter_newGroup": "Новая группа", "@chat_couldNotOpenLink": { diff --git a/lib/l10n/app_sk.arb b/lib/l10n/app_sk.arb index bed40af7..e4466c30 100644 --- a/lib/l10n/app_sk.arb +++ b/lib/l10n/app_sk.arb @@ -1353,7 +1353,6 @@ "listFilter_users": "Používatelia", "listFilter_repeaters": "Opakovadlá", "listFilter_roomServers": "Servéry miestnosti", - "listFilter_usersFirst": "Najskôr používatelia", "listFilter_unreadOnly": "Nezaregistrované len", "listFilter_newGroup": "Nová skupina", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_sl.arb b/lib/l10n/app_sl.arb index 98bf60d1..f6a317ef 100644 --- a/lib/l10n/app_sl.arb +++ b/lib/l10n/app_sl.arb @@ -1353,7 +1353,6 @@ "listFilter_users": "Uporabniki", "listFilter_repeaters": "Ponovitve", "listFilter_roomServers": "Smeti za prostore", - "listFilter_usersFirst": "Najprej uporabniki", "listFilter_unreadOnly": "Nezbrani samo", "listFilter_newGroup": "Nova skupina", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_sv.arb b/lib/l10n/app_sv.arb index 3fbf1ece..eab348c7 100644 --- a/lib/l10n/app_sv.arb +++ b/lib/l10n/app_sv.arb @@ -1353,7 +1353,6 @@ "listFilter_users": "Användare", "listFilter_repeaters": "Upprepare", "listFilter_roomServers": "Rumservrar", - "listFilter_usersFirst": "Användare först", "listFilter_unreadOnly": "Endast oinlästa", "listFilter_newGroup": "Ny grupp", "@neighbors_errorLoading": { diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb index f272d0ff..90f2f267 100644 --- a/lib/l10n/app_uk.arb +++ b/lib/l10n/app_uk.arb @@ -1349,7 +1349,6 @@ "listFilter_latestMessages": "Останні повідомлення", "listFilter_heardRecently": "Нещодавно чули", "listFilter_az": "А-Я", - "listFilter_usersFirst": "Спочатку користувачі", "listFilter_filters": "Фільтри", "listFilter_all": "Все", "listFilter_users": "Користувачі", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index f6937949..9dc2325c 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -1547,7 +1547,6 @@ "listFilter_users": "用户", "listFilter_repeaters": "转发节点", "listFilter_roomServers": "房间服务器", - "listFilter_usersFirst": "用户优先", "listFilter_unreadOnly": "仅显示未读", "listFilter_newGroup": "新建群聊", "pathTrace_you": "我自己", From d0d6a34fb547877f795126ddf8fb1127894185ea Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Tue, 21 Apr 2026 23:44:14 +0300 Subject: [PATCH 17/20] Restore jni to whatever is in main --- linux/flutter/generated_plugins.cmake | 1 + windows/flutter/generated_plugins.cmake | 1 + 2 files changed, 2 insertions(+) diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 379e36fa..93e46829 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -8,6 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST flserial + jni ) set(PLUGIN_BUNDLED_LIBRARIES) diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index f02857f4..533a1712 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -11,6 +11,7 @@ list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST flserial flutter_local_notifications_windows + jni ) set(PLUGIN_BUNDLED_LIBRARIES) From 0c1e163b889a292bc47be76ef5f359754540f4fa Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Tue, 21 Apr 2026 23:50:08 +0300 Subject: [PATCH 18/20] Reverted Ukrainian translations, will be in a separate PR --- lib/l10n/app_localizations_uk.dart | 8 ++++---- lib/l10n/app_uk.arb | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart index 4cab52cd..8ed4b9ff 100644 --- a/lib/l10n/app_localizations_uk.dart +++ b/lib/l10n/app_localizations_uk.dart @@ -3115,7 +3115,7 @@ class AppLocalizationsUk extends AppLocalizations { String get pathTrace_notAvailable => 'Трасування шляху недоступне.'; @override - String get pathTrace_refreshTooltip => 'Оновити трасування шляху'; + String get pathTrace_refreshTooltip => 'Оновити Path Trace'; @override String get pathTrace_someHopsNoLocation => @@ -3302,10 +3302,10 @@ class AppLocalizationsUk extends AppLocalizations { String get contacts_contactImportFailed => 'Контакт не вдалося імпортувати'; @override - String get contacts_zeroHopAdvert => 'Оголошення без ретрансляції'; + String get contacts_zeroHopAdvert => 'Реклама без перехоплення'; @override - String get contacts_floodAdvert => 'Оголошення з ретрансляцією'; + String get contacts_floodAdvert => 'Залив реклами'; @override String get contacts_copyAdvertToClipboard => @@ -3332,7 +3332,7 @@ class AppLocalizationsUk extends AppLocalizations { @override String get contacts_contactAdvertCopied => - 'Оголошення скопійовано до буфера обміну.'; + 'Рекламу скопійовано до буфера обміну.'; @override String get contacts_contactAdvertCopyFailed => diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb index 90f2f267..a005b369 100644 --- a/lib/l10n/app_uk.arb +++ b/lib/l10n/app_uk.arb @@ -1551,7 +1551,7 @@ "pathTrace_you": "Ви", "pathTrace_failed": "Відстеження шляху не вдалося.", "pathTrace_notAvailable": "Трасування шляху недоступне.", - "pathTrace_refreshTooltip": "Оновити трасування шляху", + "pathTrace_refreshTooltip": "Оновити Path Trace", "contacts_pathTrace": "Трасування шляхів", "contacts_ping": "Пінгувати", "contacts_repeaterPathTrace": "Трасування шляху до повторювача", @@ -1563,8 +1563,8 @@ "contacts_invalidAdvertFormat": "Недійсні контактні дані", "contacts_contactImported": "Контакт було імпортовано.", "contacts_contactImportFailed": "Контакт не вдалося імпортувати", - "contacts_zeroHopAdvert": "Оголошення без ретрансляції", - "contacts_floodAdvert": "Оголошення з ретрансляцією", + "contacts_zeroHopAdvert": "Реклама без перехоплення", + "contacts_floodAdvert": "Залив реклами", "contacts_copyAdvertToClipboard": "Копіювати оголошення в буфер обміну", "contacts_clipboardEmpty": "Буфер обміну порожній", "appSettings_languageRu": "Російська", @@ -1572,7 +1572,7 @@ "appSettings_enableMessageTracingSubtitle": "Показувати детальні метадані про маршрутизацію та час для повідомлень", "contacts_ShareContact": "Копіювати контакт у буфер обміну", "contacts_zeroHopContactAdvertFailed": "Не вдалося надіслати контакт.", - "contacts_contactAdvertCopied": "Оголошення скопійовано до буфера обміну.", + "contacts_contactAdvertCopied": "Рекламу скопійовано до буфера обміну.", "contacts_contactAdvertCopyFailed": "Копіювання оголошення в буфер обміну завершилося невдало", "contacts_zeroHopContactAdvertSent": "Відправлено контакт за оголошенням", "contacts_addContactFromClipboard": "Додати контакт з буфера обміну", From d3c7d8e43aa3660d95c1d862d3e07e76b48bed6d Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Wed, 22 Apr 2026 01:57:12 +0300 Subject: [PATCH 19/20] Red dot unread indicator in bottom tabs, keep numeric unreads only for the lists; fixed unread indicator wasn't on all screens --- lib/screens/contacts_screen.dart | 2 ++ lib/screens/line_of_sight_map_screen.dart | 6 ++++++ lib/widgets/quick_switch_bar.dart | 14 ++++++++++++-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/screens/contacts_screen.dart b/lib/screens/contacts_screen.dart index 54d32990..a034d04a 100644 --- a/lib/screens/contacts_screen.dart +++ b/lib/screens/contacts_screen.dart @@ -429,6 +429,8 @@ class _ContactsScreenState extends State selectedIndex: 0, onDestinationSelected: (index) => _handleQuickSwitch(index, context), + contactsUnreadCount: connector.getTotalContactsUnreadCount(), + channelsUnreadCount: connector.getTotalChannelsUnreadCount(), ), ), ), diff --git a/lib/screens/line_of_sight_map_screen.dart b/lib/screens/line_of_sight_map_screen.dart index ec8a391f..1148edf0 100644 --- a/lib/screens/line_of_sight_map_screen.dart +++ b/lib/screens/line_of_sight_map_screen.dart @@ -431,6 +431,12 @@ class _LineOfSightMapScreenState extends State { child: QuickSwitchBar( selectedIndex: 2, onDestinationSelected: (index) => _handleQuickSwitch(index, context), + contactsUnreadCount: context + .watch() + .getTotalContactsUnreadCount(), + channelsUnreadCount: context + .watch() + .getTotalChannelsUnreadCount(), ), ), ); diff --git a/lib/widgets/quick_switch_bar.dart b/lib/widgets/quick_switch_bar.dart index 4f014bd3..40dcb59a 100644 --- a/lib/widgets/quick_switch_bar.dart +++ b/lib/widgets/quick_switch_bar.dart @@ -2,7 +2,6 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import '../l10n/l10n.dart'; -import 'unread_badge.dart'; class QuickSwitchBar extends StatelessWidget { final int selectedIndex; @@ -109,7 +108,18 @@ class QuickSwitchBar extends StatelessWidget { clipBehavior: Clip.none, children: [ icon, - Positioned(right: -6, top: -4, child: UnreadBadge(count: count)), + Positioned( + right: -2, + top: -2, + child: Container( + width: 8, + height: 8, + decoration: const BoxDecoration( + color: Colors.redAccent, + shape: BoxShape.circle, + ), + ), + ), ], ); } From 77018dc35887cdbebb739a6c232efffbb5522c81 Mon Sep 17 00:00:00 2001 From: Serge Tarkovski Date: Tue, 12 May 2026 00:47:26 +0300 Subject: [PATCH 20/20] Recompute channels unread total after cachedChannels is updated --- lib/connector/meshcore_connector.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 78e02a7b..ead7fb36 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -3442,6 +3442,7 @@ class MeshCoreConnector extends ChangeNotifier { // Cache channels for offline use _cachedChannels = List.from(_channels); unawaited(_channelStore.saveChannels(_channels)); + _recalculateCachedChannelsUnreadTotal(); // Apply ordering and notify UI _applyChannelOrder(); @@ -3460,7 +3461,6 @@ class MeshCoreConnector extends ChangeNotifier { if (completed) { _hasLoadedChannels = true; _previousChannelsCache.clear(); - _recalculateCachedChannelsUnreadTotal(); } // Fallback: if contact sync was deferred waiting for channel 0 but