From 3e7845d3d899bc7ff1e87c2353c20917e5426b84 Mon Sep 17 00:00:00 2001 From: Florian Griffon Date: Fri, 13 Dec 2024 10:00:20 +0100 Subject: [PATCH] feat: fetch Favorite, fix fetch redundancy --- .../lib/features/contacts/contact_page.dart | 2 +- .../lib/features/contacts/contact_state.dart | 21 +++++--- .../widgets/alphabet_scroll_page.dart | 54 +++++++------------ dialer/lib/widgets/contact_service.dart | 20 ++++++- 4 files changed, 52 insertions(+), 45 deletions(-) diff --git a/dialer/lib/features/contacts/contact_page.dart b/dialer/lib/features/contacts/contact_page.dart index 7374f49..93acc45 100644 --- a/dialer/lib/features/contacts/contact_page.dart +++ b/dialer/lib/features/contacts/contact_page.dart @@ -18,7 +18,7 @@ class _ContactPageState extends State { body: contactState.loading ? const LoadingIndicatorWidget() // : ContactListWidget(contacts: contactState.contacts), - : AlphabetScrollPage(contacts: contactState.contacts, scrollOffset: contactState.scrollOffset), + : AlphabetScrollPage(scrollOffset: contactState.scrollOffset), ); } } diff --git a/dialer/lib/features/contacts/contact_state.dart b/dialer/lib/features/contacts/contact_state.dart index 8ba1d77..75e70d8 100644 --- a/dialer/lib/features/contacts/contact_state.dart +++ b/dialer/lib/features/contacts/contact_state.dart @@ -8,7 +8,9 @@ class ContactState extends StatefulWidget { const ContactState({super.key, required this.child}); static _ContactStateState of(BuildContext context) { - return context.dependOnInheritedWidgetOfExactType<_InheritedContactState>()!.data; + return context + .dependOnInheritedWidgetOfExactType<_InheritedContactState>()! + .data; } @override @@ -30,13 +32,13 @@ class _ContactStateState extends State { @override void initState() { super.initState(); - _fetchContacts(); + fetchContacts(); // Add listener for contact changes FlutterContacts.addListener(_onContactChange); } - void _onContactChange() => _fetchContacts(); + void _onContactChange() => fetchContacts(); @override void dispose() { @@ -45,16 +47,20 @@ class _ContactStateState extends State { super.dispose(); } - Future _fetchContacts() async { - List contacts = await _contactService.fetchContacts(); + Future fetchContacts({bool onlyStarred = false}) async { + List contacts = onlyStarred + ? await _contactService.fetchFavoriteContacts() + : await _contactService.fetchContacts(); - debugPrint("Fetched ${contacts.length} contacts"); + debugPrint( + "Fetched ${contacts.length} ${onlyStarred ? 'favorite' : ''} contacts"); // Find selfContact before filtering _selfContact = contacts.firstWhere( (contact) => contact.displayName.toLowerCase() == "user", orElse: () => Contact(), ); + if (_selfContact!.phones.isEmpty) { debugPrint("Self contact has no phone numbers"); _selfContact = null; @@ -62,6 +68,7 @@ class _ContactStateState extends State { contacts = contacts.where((contact) => contact.phones.isNotEmpty).toList(); contacts.sort((a, b) => a.displayName.compareTo(b.displayName)); + setState(() { _contacts = contacts; _loading = false; @@ -71,7 +78,7 @@ class _ContactStateState extends State { Future addNewContact(Contact contact) async { await _contactService.addNewContact(contact); - await _fetchContacts(); + await fetchContacts(); } void setScrollOffset(double offset) { diff --git a/dialer/lib/features/contacts/widgets/alphabet_scroll_page.dart b/dialer/lib/features/contacts/widgets/alphabet_scroll_page.dart index 0ff1cb2..7453181 100644 --- a/dialer/lib/features/contacts/widgets/alphabet_scroll_page.dart +++ b/dialer/lib/features/contacts/widgets/alphabet_scroll_page.dart @@ -1,19 +1,16 @@ import 'package:dialer/widgets/username_color_generator.dart'; import 'package:flutter/material.dart'; import 'package:flutter_contacts/flutter_contacts.dart'; -import '../../../widgets/color_darkener.dart'; import '../contact_state.dart'; -import '../../../widgets/contact_service.dart'; +import '../../../widgets/color_darkener.dart'; import 'add_contact_button.dart'; import 'contact_modal.dart'; import 'share_own_qr.dart'; class AlphabetScrollPage extends StatefulWidget { - final List contacts; final double scrollOffset; - const AlphabetScrollPage( - {super.key, required this.contacts, required this.scrollOffset}); + const AlphabetScrollPage({super.key, required this.scrollOffset}); @override _AlphabetScrollPageState createState() => _AlphabetScrollPageState(); @@ -21,13 +18,10 @@ class AlphabetScrollPage extends StatefulWidget { class _AlphabetScrollPageState extends State { late ScrollController _scrollController; - List _contacts = []; // Local copy of contacts for updating - final ContactService _contactService = ContactService(); @override void initState() { super.initState(); - _contacts = widget.contacts; // Initialize with the provided contacts _scrollController = ScrollController(initialScrollOffset: widget.scrollOffset); _scrollController.addListener(_onScroll); @@ -39,28 +33,20 @@ class _AlphabetScrollPageState extends State { } Future _refreshContacts() async { + final contactState = ContactState.of(context); try { - // Use the fetchContacts method from ContactService - final updatedContacts = await _contactService.fetchContacts(); - - if (mounted) { - setState(() { - _contacts = updatedContacts; - }); - } + await contactState.fetchContacts(); } catch (e) { print('Error refreshing contacts: $e'); - // Optionally show a user-friendly error message - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text('Failed to refresh contacts'))); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Failed to refresh contacts')), + ); } } void _toggleFavorite(Contact contact) async { try { - // Request permission first if (await FlutterContacts.requestPermission()) { - // Fetch the full contact details with all available properties Contact? fullContact = await FlutterContacts.getContact(contact.id, withProperties: true, withAccounts: true, @@ -68,7 +54,6 @@ class _AlphabetScrollPageState extends State { withThumbnail: true); if (fullContact != null) { - // Update the contact fullContact.isStarred = !fullContact.isStarred; await FlutterContacts.updateContact(fullContact); } @@ -78,16 +63,20 @@ class _AlphabetScrollPageState extends State { } } catch (e) { print("Error updating favorite status: $e"); - // Optional: Show a user-friendly error message ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Failed to update contact favorite status'))); + SnackBar(content: Text('Failed to update contact favorite status')), + ); } } @override Widget build(BuildContext context) { + final contactState = ContactState.of(context); + final contacts = contactState.contacts; + final selfContact = contactState.selfContact; + Map> alphabetizedContacts = {}; - for (var contact in _contacts) { + for (var contact in contacts) { String firstLetter = contact.displayName.isNotEmpty ? contact.displayName[0].toUpperCase() : '#'; @@ -106,15 +95,12 @@ class _AlphabetScrollPageState extends State { // Top buttons row Container( color: Colors.black, - padding: - const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ AddContactButton(), - QRCodeButton( - contacts: _contacts, - selfContact: ContactState.of(context).selfContact), + QRCodeButton(contacts: contacts, selfContact: selfContact), ], ), ), @@ -125,7 +111,7 @@ class _AlphabetScrollPageState extends State { itemCount: alphabetKeys.length, itemBuilder: (context, index) { String letter = alphabetKeys[index]; - List contacts = alphabetizedContacts[letter]!; + List contactsForLetter = alphabetizedContacts[letter]!; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -143,7 +129,7 @@ class _AlphabetScrollPageState extends State { ), ), // Contact Entries - ...contacts.map((contact) { + ...contactsForLetter.map((contact) { String phoneNumber = contact.phones.isNotEmpty ? contact.phones.first.number : 'No phone number'; @@ -179,9 +165,7 @@ class _AlphabetScrollPageState extends State { return ContactModal( contact: contact, onEdit: () async { - // Trigger edit logic and refresh contacts - if (await FlutterContacts - .requestPermission()) { + if (await FlutterContacts.requestPermission()) { final updatedContact = await FlutterContacts.openExternalEdit( contact.id); diff --git a/dialer/lib/widgets/contact_service.dart b/dialer/lib/widgets/contact_service.dart index 88c92da..61d365d 100644 --- a/dialer/lib/widgets/contact_service.dart +++ b/dialer/lib/widgets/contact_service.dart @@ -4,12 +4,28 @@ import 'package:flutter_contacts/flutter_contacts.dart'; class ContactService { Future> fetchContacts() async { if (await FlutterContacts.requestPermission()) { - return await FlutterContacts.getContacts(withProperties: true, withThumbnail: true, withAccounts: true, withGroups: true, withPhoto: true); + return await FlutterContacts.getContacts( + withProperties: true, + withThumbnail: true, + withAccounts: true, + withGroups: true, + withPhoto: true); } return []; } + Future> fetchFavoriteContacts() async { + // Fetch all contacts + List contacts = await fetchContacts(); + + // Filter contacts to only include those with isStarred: true + List favoriteContacts = + contacts.where((contact) => contact.isStarred).toList(); + + return favoriteContacts; + } + Future addNewContact(Contact contact) async { await FlutterContacts.insertContact(contact); } -} \ No newline at end of file +}