mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
feat: Favourites support using firmware flags bit 0
- Contact.isFavourite reads bit 0 of firmware flags field - Toggle favourite via contact action sheet (writes to device) - Favourites section shown first in contacts tab - Path size options corrected to [1, 2, 3] bytes
This commit is contained in:
@@ -342,6 +342,8 @@ class ContactsProvider with ChangeNotifier {
|
||||
}
|
||||
|
||||
List<Contact> get contacts => _contacts.values.toList();
|
||||
List<Contact> get favouriteContacts =>
|
||||
_contacts.values.where((c) => c.isFavourite).toList();
|
||||
List<SavedContactGroup> get savedContactGroups =>
|
||||
List<SavedContactGroup>.from(_savedContactGroups)
|
||||
..sort((a, b) => b.createdAt.compareTo(a.createdAt));
|
||||
|
||||
@@ -688,6 +688,19 @@ class _ContactsTabState extends State<ContactsTab> {
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(8),
|
||||
children: [
|
||||
// Favourites (contacts with firmware favourite flag set)
|
||||
if (contactsProvider.favouriteContacts.isNotEmpty) ...[
|
||||
_SectionHeader(
|
||||
title: 'Favourites',
|
||||
count: contactsProvider.favouriteContacts.length,
|
||||
icon: Icons.star,
|
||||
),
|
||||
..._buildContactSectionItems(
|
||||
contactsProvider.favouriteContacts,
|
||||
),
|
||||
const Divider(height: 32),
|
||||
],
|
||||
|
||||
// Team Members (Chat contacts)
|
||||
if (showTeamMembersSection) ...[
|
||||
_SectionHeader(
|
||||
|
||||
@@ -210,6 +210,38 @@ class ContactStorageService {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Favorites ───────────────────────────────────────────────────────────────
|
||||
|
||||
static const String _favoritesKey = 'contact_favorites';
|
||||
|
||||
Future<void> saveFavorites(
|
||||
Set<String> publicKeyHexes, {
|
||||
String? namespace,
|
||||
}) async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setStringList(
|
||||
_key(_favoritesKey, namespace: namespace),
|
||||
publicKeyHexes.toList(),
|
||||
);
|
||||
} catch (e) {
|
||||
debugPrint('❌ [ContactStorage] Error saving favorites: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<Set<String>> loadFavorites({String? namespace}) async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final list = prefs.getStringList(
|
||||
_key(_favoritesKey, namespace: namespace),
|
||||
);
|
||||
return list?.toSet() ?? {};
|
||||
} catch (e) {
|
||||
debugPrint('❌ [ContactStorage] Error loading favorites: $e');
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/// Get storage statistics
|
||||
Future<Map<String, dynamic>> getStorageStats({String? namespace}) async {
|
||||
try {
|
||||
|
||||
@@ -25,8 +25,7 @@ class RouteHashPreferences {
|
||||
|
||||
/// Valid hash sizes per the MeshCore protocol.
|
||||
/// The path encoding uses 2 bits: 00=1B, 01=2B, 10=3B, 11=4B.
|
||||
/// The official app offers 1, 2, 4 (skipping 3).
|
||||
static const List<int> supportedSizes = [1, 2, 4];
|
||||
static const List<int> supportedSizes = [1, 2, 3];
|
||||
|
||||
static int _normalize(int value) {
|
||||
if (supportedSizes.contains(value)) return value;
|
||||
|
||||
@@ -348,6 +348,29 @@ class ContactTile extends StatelessWidget {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (!contact.isChannel)
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
contact.isFavourite ? Icons.star : Icons.star_outline,
|
||||
color: contact.isFavourite ? Colors.amber : null,
|
||||
),
|
||||
title: Text(
|
||||
contact.isFavourite
|
||||
? 'Remove from Favourites'
|
||||
: 'Add to Favourites',
|
||||
),
|
||||
onTap: () async {
|
||||
Navigator.pop(sheetContext);
|
||||
final toggled = contact.toggleFavourite();
|
||||
final connectionProvider =
|
||||
context.read<ConnectionProvider>();
|
||||
await connectionProvider.addOrUpdateContact(toggled);
|
||||
if (context.mounted) {
|
||||
// Refresh contact from device so the local cache is updated
|
||||
await connectionProvider.getContact(contact.publicKey);
|
||||
}
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.message_outlined),
|
||||
title: Text(l10n.messages),
|
||||
|
||||
Reference in New Issue
Block a user