mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
Add message filters and counts
This commit is contained in:
@@ -193,6 +193,59 @@ class MessagesProvider with ChangeNotifier {
|
||||
.where((m) => !m.isRead && !m.isSentMessage && !m.isSystemMessage)
|
||||
.length;
|
||||
|
||||
int getMessageCountForDestination(Contact contact) => _messages
|
||||
.where((message) => _isMessageForDestination(message, contact))
|
||||
.length;
|
||||
|
||||
int getUnreadCountForDestination(Contact contact) => _messages
|
||||
.where(
|
||||
(message) =>
|
||||
_isMessageForDestination(message, contact) &&
|
||||
!message.isRead &&
|
||||
!message.isSentMessage &&
|
||||
!message.isSystemMessage,
|
||||
)
|
||||
.length;
|
||||
|
||||
bool _isMessageForDestination(Message message, Contact contact) {
|
||||
if (message.isSystemMessage) return false;
|
||||
|
||||
if (contact.isChannel) {
|
||||
final channelIdx = contact.publicKey.length > 1
|
||||
? contact.publicKey[1]
|
||||
: 0;
|
||||
return message.isChannelMessage &&
|
||||
(message.channelIdx ?? 0) == channelIdx;
|
||||
}
|
||||
|
||||
if (!message.isContactMessage || contact.publicKey.length < 6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final selectedPrefix = contact.publicKey.sublist(0, 6);
|
||||
|
||||
if (message.recipientPublicKey != null &&
|
||||
message.recipientPublicKey!.length >= 6 &&
|
||||
_matchesPrefix(message.recipientPublicKey!, selectedPrefix)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (message.senderPublicKeyPrefix != null &&
|
||||
message.senderPublicKeyPrefix!.length >= 6 &&
|
||||
_matchesPrefix(message.senderPublicKeyPrefix!, selectedPrefix)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _matchesPrefix(Uint8List key, Uint8List prefix) {
|
||||
for (var i = 0; i < 6; i++) {
|
||||
if (key[i] != prefix[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Initialize and load persisted messages
|
||||
Future<void> initialize() async {
|
||||
if (_isInitialized) return;
|
||||
|
||||
@@ -7,6 +7,7 @@ import '../models/contact.dart';
|
||||
import '../providers/contacts_provider.dart';
|
||||
import '../providers/app_provider.dart';
|
||||
import '../providers/connection_provider.dart';
|
||||
import '../providers/messages_provider.dart';
|
||||
import '../widgets/contacts/contact_tile.dart';
|
||||
import '../widgets/contacts/add_channel_dialog.dart';
|
||||
|
||||
@@ -201,8 +202,8 @@ class _ContactsTabState extends State<ContactsTab> {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
|
||||
return Scaffold(
|
||||
body: Consumer<ContactsProvider>(
|
||||
builder: (context, contactsProvider, child) {
|
||||
body: Consumer2<ContactsProvider, MessagesProvider>(
|
||||
builder: (context, contactsProvider, messagesProvider, child) {
|
||||
final chatContacts = _sortContactsByDistance(
|
||||
contactsProvider.chatContacts,
|
||||
);
|
||||
@@ -284,6 +285,10 @@ class _ContactsTabState extends State<ContactsTab> {
|
||||
calculateDistance: _calculateDistanceInMeters,
|
||||
formatDistance: _formatDistance,
|
||||
onNavigateToMap: widget.onNavigateToMap,
|
||||
messageCount: messagesProvider
|
||||
.getMessageCountForDestination(contact),
|
||||
unreadMessageCount: messagesProvider
|
||||
.getUnreadCountForDestination(contact),
|
||||
),
|
||||
),
|
||||
const Divider(height: 32),
|
||||
@@ -303,6 +308,10 @@ class _ContactsTabState extends State<ContactsTab> {
|
||||
calculateDistance: _calculateDistanceInMeters,
|
||||
formatDistance: _formatDistance,
|
||||
onNavigateToMap: widget.onNavigateToMap,
|
||||
messageCount: messagesProvider
|
||||
.getMessageCountForDestination(contact),
|
||||
unreadMessageCount: messagesProvider
|
||||
.getUnreadCountForDestination(contact),
|
||||
),
|
||||
),
|
||||
const Divider(height: 32),
|
||||
@@ -322,6 +331,10 @@ class _ContactsTabState extends State<ContactsTab> {
|
||||
calculateDistance: _calculateDistanceInMeters,
|
||||
formatDistance: _formatDistance,
|
||||
onNavigateToMap: widget.onNavigateToMap,
|
||||
messageCount: messagesProvider
|
||||
.getMessageCountForDestination(contact),
|
||||
unreadMessageCount: messagesProvider
|
||||
.getUnreadCountForDestination(contact),
|
||||
),
|
||||
),
|
||||
const Divider(height: 32),
|
||||
@@ -341,6 +354,10 @@ class _ContactsTabState extends State<ContactsTab> {
|
||||
calculateDistance: _calculateDistanceInMeters,
|
||||
formatDistance: _formatDistance,
|
||||
onNavigateToMap: widget.onNavigateToMap,
|
||||
messageCount: messagesProvider
|
||||
.getMessageCountForDestination(contact),
|
||||
unreadMessageCount: messagesProvider
|
||||
.getUnreadCountForDestination(contact),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -21,6 +21,8 @@ class ContactTile extends StatelessWidget {
|
||||
final double Function(double, double, double, double)? calculateDistance;
|
||||
final String Function(double)? formatDistance;
|
||||
final VoidCallback? onNavigateToMap;
|
||||
final int messageCount;
|
||||
final int unreadMessageCount;
|
||||
|
||||
const ContactTile({
|
||||
super.key,
|
||||
@@ -29,6 +31,8 @@ class ContactTile extends StatelessWidget {
|
||||
this.calculateDistance,
|
||||
this.formatDistance,
|
||||
this.onNavigateToMap,
|
||||
this.messageCount = 0,
|
||||
this.unreadMessageCount = 0,
|
||||
});
|
||||
|
||||
/// Get localized time since last seen
|
||||
@@ -263,6 +267,13 @@ class ContactTile extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
if (messageCount > 0) ...[
|
||||
const SizedBox(width: 8),
|
||||
_MessageCountBadge(
|
||||
totalCount: messageCount,
|
||||
unreadCount: unreadMessageCount,
|
||||
),
|
||||
],
|
||||
const SizedBox(width: 8),
|
||||
Text(timeAgoText, style: timeAgoStyle),
|
||||
if (isPingInProgress) ...[
|
||||
@@ -1301,3 +1312,59 @@ class ContactTile extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MessageCountBadge extends StatelessWidget {
|
||||
final int totalCount;
|
||||
final int unreadCount;
|
||||
|
||||
const _MessageCountBadge({
|
||||
required this.totalCount,
|
||||
required this.unreadCount,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final hasUnread = unreadCount > 0;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: hasUnread
|
||||
? colorScheme.primaryContainer
|
||||
: colorScheme.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
border: Border.all(
|
||||
color: hasUnread
|
||||
? colorScheme.primary.withValues(alpha: 0.35)
|
||||
: colorScheme.outlineVariant.withValues(alpha: 0.45),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (hasUnread) ...[
|
||||
Container(
|
||||
width: 6,
|
||||
height: 6,
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.primary,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
],
|
||||
Text(
|
||||
hasUnread ? '$unreadCount/$totalCount' : '$totalCount',
|
||||
style: Theme.of(context).textTheme.labelSmall?.copyWith(
|
||||
color: hasUnread
|
||||
? colorScheme.onPrimaryContainer
|
||||
: colorScheme.onSurfaceVariant,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user