fix: migrate filter menus to type-safe generics and harden popup dismissal

- Move ContactSortOption/ContactTypeFilter enums to dedicated
  contact_filter_types.dart (re-exported from contact_search.dart)
- Migrate ContactsFilterMenu and DiscoveryContactsFilterMenu to use
  sealed class action types with SortFilterMenu<T> generics, replacing
  int action constants and switch statements
- Guard _closeDropdownAndRun with ModalRoute.isCurrent check to prevent
  accidental dismissal of parent routes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
zjs81
2026-03-14 17:59:48 -07:00
parent 86e9b7fe01
commit 566e3aadf8
4 changed files with 66 additions and 78 deletions
+57 -73
View File
@@ -87,15 +87,23 @@ class SortFilterMenu<T> extends StatelessWidget {
}
}
const int _actionSortRecentMessages = 1;
const int _actionSortName = 2;
const int _actionSortLastSeen = 3;
const int _actionFilterAll = 4;
const int _actionFilterFavorites = 5;
const int _actionFilterUsers = 6;
const int _actionFilterRepeaters = 7;
const int _actionFilterRooms = 8;
const int _actionToggleUnreadOnly = 9;
sealed class _ContactsFilterAction {
const _ContactsFilterAction();
}
class _SortAction extends _ContactsFilterAction {
final ContactSortOption option;
const _SortAction(this.option);
}
class _TypeFilterAction extends _ContactsFilterAction {
final ContactTypeFilter filter;
const _TypeFilterAction(this.filter);
}
class _ToggleUnreadAction extends _ContactsFilterAction {
const _ToggleUnreadAction();
}
class ContactsFilterMenu extends StatelessWidget {
final ContactSortOption sortOption;
@@ -118,24 +126,24 @@ class ContactsFilterMenu extends StatelessWidget {
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
return SortFilterMenu(
return SortFilterMenu<_ContactsFilterAction>(
tooltip: l10n.listFilter_tooltip,
sections: [
SortFilterMenuSection(
title: l10n.listFilter_sortBy,
options: [
SortFilterMenuOption(
value: _actionSortRecentMessages,
value: _SortAction(ContactSortOption.recentMessages),
label: l10n.listFilter_latestMessages,
checked: sortOption == ContactSortOption.recentMessages,
),
SortFilterMenuOption(
value: _actionSortLastSeen,
value: _SortAction(ContactSortOption.lastSeen),
label: l10n.listFilter_heardRecently,
checked: sortOption == ContactSortOption.lastSeen,
),
SortFilterMenuOption(
value: _actionSortName,
value: _SortAction(ContactSortOption.name),
label: l10n.listFilter_az,
checked: sortOption == ContactSortOption.name,
),
@@ -145,32 +153,32 @@ class ContactsFilterMenu extends StatelessWidget {
title: l10n.listFilter_filters,
options: [
SortFilterMenuOption(
value: _actionFilterAll,
value: _TypeFilterAction(ContactTypeFilter.all),
label: l10n.listFilter_all,
checked: typeFilter == ContactTypeFilter.all,
),
SortFilterMenuOption(
value: _actionFilterFavorites,
value: _TypeFilterAction(ContactTypeFilter.favorites),
label: l10n.listFilter_favorites,
checked: typeFilter == ContactTypeFilter.favorites,
),
SortFilterMenuOption(
value: _actionFilterUsers,
value: _TypeFilterAction(ContactTypeFilter.users),
label: l10n.listFilter_users,
checked: typeFilter == ContactTypeFilter.users,
),
SortFilterMenuOption(
value: _actionFilterRepeaters,
value: _TypeFilterAction(ContactTypeFilter.repeaters),
label: l10n.listFilter_repeaters,
checked: typeFilter == ContactTypeFilter.repeaters,
),
SortFilterMenuOption(
value: _actionFilterRooms,
value: _TypeFilterAction(ContactTypeFilter.rooms),
label: l10n.listFilter_roomServers,
checked: typeFilter == ContactTypeFilter.rooms,
),
SortFilterMenuOption(
value: _actionToggleUnreadOnly,
value: const _ToggleUnreadAction(),
label: l10n.listFilter_unreadOnly,
checked: showUnreadOnly,
),
@@ -179,39 +187,32 @@ class ContactsFilterMenu extends StatelessWidget {
],
onSelected: (action) {
switch (action) {
case _actionSortRecentMessages:
onSortChanged(ContactSortOption.recentMessages);
break;
case _actionSortName:
onSortChanged(ContactSortOption.name);
break;
case _actionSortLastSeen:
onSortChanged(ContactSortOption.lastSeen);
break;
case _actionFilterAll:
onTypeFilterChanged(ContactTypeFilter.all);
break;
case _actionFilterUsers:
onTypeFilterChanged(ContactTypeFilter.users);
break;
case _actionFilterFavorites:
onTypeFilterChanged(ContactTypeFilter.favorites);
break;
case _actionFilterRepeaters:
onTypeFilterChanged(ContactTypeFilter.repeaters);
break;
case _actionFilterRooms:
onTypeFilterChanged(ContactTypeFilter.rooms);
break;
case _actionToggleUnreadOnly:
case _SortAction(:final option):
onSortChanged(option);
case _TypeFilterAction(:final filter):
onTypeFilterChanged(filter);
case _ToggleUnreadAction():
onUnreadOnlyChanged(!showUnreadOnly);
break;
}
},
);
}
}
sealed class _DiscoveryFilterAction {
const _DiscoveryFilterAction();
}
class _DiscoverySortAction extends _DiscoveryFilterAction {
final ContactSortOption option;
const _DiscoverySortAction(this.option);
}
class _DiscoveryTypeFilterAction extends _DiscoveryFilterAction {
final ContactTypeFilter filter;
const _DiscoveryTypeFilterAction(this.filter);
}
class DiscoveryContactsFilterMenu extends StatelessWidget {
final ContactSortOption sortOption;
final ContactTypeFilter typeFilter;
@@ -229,19 +230,19 @@ class DiscoveryContactsFilterMenu extends StatelessWidget {
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
return SortFilterMenu(
return SortFilterMenu<_DiscoveryFilterAction>(
tooltip: l10n.listFilter_tooltip,
sections: [
SortFilterMenuSection(
title: l10n.listFilter_sortBy,
options: [
SortFilterMenuOption(
value: _actionSortLastSeen,
value: _DiscoverySortAction(ContactSortOption.lastSeen),
label: l10n.listFilter_heardRecently,
checked: sortOption == ContactSortOption.lastSeen,
),
SortFilterMenuOption(
value: _actionSortName,
value: _DiscoverySortAction(ContactSortOption.name),
label: l10n.listFilter_az,
checked: sortOption == ContactSortOption.name,
),
@@ -251,22 +252,22 @@ class DiscoveryContactsFilterMenu extends StatelessWidget {
title: l10n.listFilter_filters,
options: [
SortFilterMenuOption(
value: _actionFilterAll,
value: _DiscoveryTypeFilterAction(ContactTypeFilter.all),
label: l10n.listFilter_all,
checked: typeFilter == ContactTypeFilter.all,
),
SortFilterMenuOption(
value: _actionFilterUsers,
value: _DiscoveryTypeFilterAction(ContactTypeFilter.users),
label: l10n.listFilter_users,
checked: typeFilter == ContactTypeFilter.users,
),
SortFilterMenuOption(
value: _actionFilterRepeaters,
value: _DiscoveryTypeFilterAction(ContactTypeFilter.repeaters),
label: l10n.listFilter_repeaters,
checked: typeFilter == ContactTypeFilter.repeaters,
),
SortFilterMenuOption(
value: _actionFilterRooms,
value: _DiscoveryTypeFilterAction(ContactTypeFilter.rooms),
label: l10n.listFilter_roomServers,
checked: typeFilter == ContactTypeFilter.rooms,
),
@@ -275,27 +276,10 @@ class DiscoveryContactsFilterMenu extends StatelessWidget {
],
onSelected: (action) {
switch (action) {
case _actionSortName:
onSortChanged(ContactSortOption.name);
break;
case _actionSortLastSeen:
onSortChanged(ContactSortOption.lastSeen);
break;
case _actionFilterAll:
onTypeFilterChanged(ContactTypeFilter.all);
break;
case _actionFilterUsers:
onTypeFilterChanged(ContactTypeFilter.users);
break;
case _actionFilterFavorites:
onTypeFilterChanged(ContactTypeFilter.favorites);
break;
case _actionFilterRepeaters:
onTypeFilterChanged(ContactTypeFilter.repeaters);
break;
case _actionFilterRooms:
onTypeFilterChanged(ContactTypeFilter.rooms);
break;
case _DiscoverySortAction(:final option):
onSortChanged(option);
case _DiscoveryTypeFilterAction(:final filter):
onTypeFilterChanged(filter);
}
},
);