mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
Fix retry flow and contact filters
This commit is contained in:
@@ -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,31 +372,43 @@ class _ContactsTabState extends State<ContactsTab> {
|
|||||||
ContactSection.teamMembers,
|
ContactSection.teamMembers,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
..._buildContactSectionItems(chatContacts),
|
_buildSectionFilterField(context, ContactSection.teamMembers),
|
||||||
|
if (chatContacts.isEmpty)
|
||||||
|
_buildEmptyFilterState(context)
|
||||||
|
else
|
||||||
|
..._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),
|
||||||
),
|
),
|
||||||
..._buildContactSectionItems(repeaters),
|
_buildSectionFilterField(context, ContactSection.repeaters),
|
||||||
|
if (repeaters.isEmpty)
|
||||||
|
_buildEmptyFilterState(context)
|
||||||
|
else
|
||||||
|
..._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),
|
||||||
),
|
),
|
||||||
..._buildContactSectionItems(rooms),
|
_buildSectionFilterField(context, ContactSection.rooms),
|
||||||
|
if (rooms.isEmpty)
|
||||||
|
_buildEmptyFilterState(context)
|
||||||
|
else
|
||||||
|
..._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;
|
||||||
|
|||||||
Reference in New Issue
Block a user