Fix retry flow and contact filters

This commit is contained in:
Janez T
2026-03-12 14:41:12 +01:00
parent abb73a4c65
commit 38cb3dc031

View File

@@ -33,6 +33,12 @@ class _ContactsTabState extends State<ContactsTab> {
Position? _currentPosition; Position? _currentPosition;
final Set<String> _resolvingAdvertKeys = <String>{}; final Set<String> _resolvingAdvertKeys = <String>{};
bool _isResolvingPendingBatch = false; bool _isResolvingPendingBatch = false;
final Map<ContactSection, String> _sectionFilters = {
ContactSection.teamMembers: '',
ContactSection.repeaters: '',
ContactSection.rooms: '',
ContactSection.channels: '',
};
final Map<ContactSection, ContactSortMode> _sortModes = { final Map<ContactSection, ContactSortMode> _sortModes = {
ContactSection.teamMembers: ContactSortMode.lastSeen, ContactSection.teamMembers: ContactSortMode.lastSeen,
ContactSection.repeaters: ContactSortMode.lastSeen, ContactSection.repeaters: ContactSortMode.lastSeen,
@@ -168,6 +174,22 @@ class _ContactsTabState extends State<ContactsTab> {
return l10n.daysAgo(diff.inDays); return l10n.daysAgo(diff.inDays);
} }
List<Contact> _filterContactsForSection(
List<Contact> contacts,
ContactSection section,
) {
final query = (_sectionFilters[section] ?? '').trim().toLowerCase();
if (query.isEmpty) {
return contacts;
}
return contacts.where((contact) {
final name = contact.displayName.toLowerCase();
final advertisedName = contact.advName.toLowerCase();
return name.contains(query) || advertisedName.contains(query);
}).toList();
}
List<Contact> _sortContacts(List<Contact> contacts, ContactSection section) { List<Contact> _sortContacts(List<Contact> contacts, ContactSection section) {
final sorted = List<Contact>.from(contacts); final sorted = List<Contact>.from(contacts);
if (section == ContactSection.channels) { if (section == ContactSection.channels) {
@@ -264,32 +286,48 @@ class _ContactsTabState extends State<ContactsTab> {
builder: (context, contactsProvider, child) { builder: (context, contactsProvider, child) {
final messagesProvider = context.watch<MessagesProvider>(); final messagesProvider = context.watch<MessagesProvider>();
final connectionProvider = context.watch<ConnectionProvider>(); final connectionProvider = context.watch<ConnectionProvider>();
final chatContacts = _sortContacts( final allChatContacts = _sortContacts(
contactsProvider.chatContacts, contactsProvider.chatContacts,
ContactSection.teamMembers, ContactSection.teamMembers,
); );
final repeaters = _sortContacts( final allRepeaters = _sortContacts(
contactsProvider.repeaters, contactsProvider.repeaters,
ContactSection.repeaters, ContactSection.repeaters,
); );
final rooms = _sortContacts( final allRooms = _sortContacts(
contactsProvider.rooms, contactsProvider.rooms,
ContactSection.rooms, ContactSection.rooms,
); );
final channels = _sortContacts( final allChannels = _sortContacts(
contactsProvider.channels, contactsProvider.channels,
ContactSection.channels, ContactSection.channels,
); );
final chatContacts = _filterContactsForSection(
allChatContacts,
ContactSection.teamMembers,
);
final repeaters = _filterContactsForSection(
allRepeaters,
ContactSection.repeaters,
);
final rooms = _filterContactsForSection(
allRooms,
ContactSection.rooms,
);
final filteredChannels = _filterContactsForSection(
allChannels,
ContactSection.channels,
);
final pendingAdverts = contactsProvider.pendingAdverts; final pendingAdverts = contactsProvider.pendingAdverts;
_schedulePendingAdvertResolution(pendingAdverts, connectionProvider); _schedulePendingAdvertResolution(pendingAdverts, connectionProvider);
// Check if there are any displayable contacts // Check if there are any displayable contacts
final hasDisplayableContacts = final hasDisplayableContacts =
chatContacts.isNotEmpty || allChatContacts.isNotEmpty ||
repeaters.isNotEmpty || allRepeaters.isNotEmpty ||
rooms.isNotEmpty || allRooms.isNotEmpty ||
channels.isNotEmpty || allChannels.isNotEmpty ||
pendingAdverts.isNotEmpty; pendingAdverts.isNotEmpty;
if (!hasDisplayableContacts) { if (!hasDisplayableContacts) {
@@ -324,7 +362,7 @@ class _ContactsTabState extends State<ContactsTab> {
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
children: [ children: [
// Team Members (Chat contacts) // Team Members (Chat contacts)
if (chatContacts.isNotEmpty) ...[ if (allChatContacts.isNotEmpty) ...[
_SectionHeader( _SectionHeader(
title: l10n.teamMembers, title: l10n.teamMembers,
count: chatContacts.length, count: chatContacts.length,
@@ -334,30 +372,42 @@ class _ContactsTabState extends State<ContactsTab> {
ContactSection.teamMembers, ContactSection.teamMembers,
), ),
), ),
_buildSectionFilterField(context, ContactSection.teamMembers),
if (chatContacts.isEmpty)
_buildEmptyFilterState(context)
else
..._buildContactSectionItems(chatContacts), ..._buildContactSectionItems(chatContacts),
const Divider(height: 32), const Divider(height: 32),
], ],
// Repeaters // Repeaters
if (repeaters.isNotEmpty) ...[ if (allRepeaters.isNotEmpty) ...[
_SectionHeader( _SectionHeader(
title: l10n.repeaters, title: l10n.repeaters,
count: repeaters.length, count: repeaters.length,
icon: Icons.router, icon: Icons.router,
trailing: _buildSortMenu(context, ContactSection.repeaters), trailing: _buildSortMenu(context, ContactSection.repeaters),
), ),
_buildSectionFilterField(context, ContactSection.repeaters),
if (repeaters.isEmpty)
_buildEmptyFilterState(context)
else
..._buildContactSectionItems(repeaters), ..._buildContactSectionItems(repeaters),
const Divider(height: 32), const Divider(height: 32),
], ],
// Rooms // Rooms
if (rooms.isNotEmpty) ...[ if (allRooms.isNotEmpty) ...[
_SectionHeader( _SectionHeader(
title: l10n.rooms, title: l10n.rooms,
count: rooms.length, count: rooms.length,
icon: Icons.tag, icon: Icons.tag,
trailing: _buildSortMenu(context, ContactSection.rooms), trailing: _buildSortMenu(context, ContactSection.rooms),
), ),
_buildSectionFilterField(context, ContactSection.rooms),
if (rooms.isEmpty)
_buildEmptyFilterState(context)
else
..._buildContactSectionItems(rooms), ..._buildContactSectionItems(rooms),
const Divider(height: 32), const Divider(height: 32),
], ],
@@ -386,11 +436,14 @@ class _ContactsTabState extends State<ContactsTab> {
// Channels (visible in both simple and advanced mode) // Channels (visible in both simple and advanced mode)
_SectionHeader( _SectionHeader(
title: l10n.channels, title: l10n.channels,
count: channels.length, count: filteredChannels.length,
icon: Icons.broadcast_on_personal, icon: Icons.broadcast_on_personal,
), ),
if (channels.isNotEmpty) ...[ _buildSectionFilterField(context, ContactSection.channels),
...channels.map( if (allChannels.isNotEmpty && filteredChannels.isEmpty)
_buildEmptyFilterState(context),
if (filteredChannels.isNotEmpty) ...[
...filteredChannels.map(
(channel) => _ChannelActivityCard( (channel) => _ChannelActivityCard(
channel: channel, channel: channel,
messagesProvider: messagesProvider, messagesProvider: messagesProvider,
@@ -454,6 +507,54 @@ class _ContactsTabState extends State<ContactsTab> {
}).toList(); }).toList();
} }
Widget _buildSectionFilterField(
BuildContext context,
ContactSection section,
) {
final hasFilter = (_sectionFilters[section] ?? '').isNotEmpty;
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: TextFormField(
key: ValueKey('${section.name}:${_sectionFilters[section] ?? ''}'),
initialValue: _sectionFilters[section] ?? '',
onChanged: (value) {
setState(() {
_sectionFilters[section] = value;
});
},
decoration: InputDecoration(
isDense: true,
prefixIcon: const Icon(Icons.search, size: 18),
hintText: 'Filter by name',
border: const OutlineInputBorder(),
suffixIcon: hasFilter
? IconButton(
icon: const Icon(Icons.close, size: 18),
onPressed: () {
setState(() {
_sectionFilters[section] = '';
});
},
)
: null,
),
),
);
}
Widget _buildEmptyFilterState(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Text(
'No matches for this filter.',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
);
}
Widget _buildSortMenu(BuildContext context, ContactSection section) { Widget _buildSortMenu(BuildContext context, ContactSection section) {
final l10n = AppLocalizations.of(context)!; final l10n = AppLocalizations.of(context)!;
final selectedMode = _sortModes[section] ?? ContactSortMode.lastSeen; final selectedMode = _sortModes[section] ?? ContactSortMode.lastSeen;