From f3c092d5b8c1150488c6879b4cca7792a85c4d70 Mon Sep 17 00:00:00 2001 From: Florian Griffon Date: Thu, 12 Dec 2024 17:47:49 +0100 Subject: [PATCH] feat: all getContacts use the contactService | can favorite a contact --- .../lib/features/composition/composition.dart | 4 +- .../lib/features/contacts/contact_state.dart | 2 +- .../widgets/alphabet_scroll_page.dart | 154 ++++----------- .../features/favorites/favorites_page.dart | 180 ++++++++++++++++++ dialer/lib/features/home/home_page.dart | 9 +- .../contacts => widgets}/contact_service.dart | 0 6 files changed, 222 insertions(+), 127 deletions(-) rename dialer/lib/{features/contacts => widgets}/contact_service.dart (100%) diff --git a/dialer/lib/features/composition/composition.dart b/dialer/lib/features/composition/composition.dart index bdaf048..783fcf9 100644 --- a/dialer/lib/features/composition/composition.dart +++ b/dialer/lib/features/composition/composition.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_contacts/flutter_contacts.dart'; +import '../../widgets/contact_service.dart'; class CompositionPage extends StatefulWidget { const CompositionPage({super.key}); @@ -12,6 +13,7 @@ class _CompositionPageState extends State { String dialedNumber = ""; List _allContacts = []; List _filteredContacts = []; + final ContactService _contactService = ContactService(); @override void initState() { @@ -21,7 +23,7 @@ class _CompositionPageState extends State { Future _fetchContacts() async { if (await FlutterContacts.requestPermission()) { - _allContacts = await FlutterContacts.getContacts(withProperties: true); + _allContacts = await _contactService.fetchContacts(); _filteredContacts = _allContacts; setState(() {}); } diff --git a/dialer/lib/features/contacts/contact_state.dart b/dialer/lib/features/contacts/contact_state.dart index 45ab56a..8ba1d77 100644 --- a/dialer/lib/features/contacts/contact_state.dart +++ b/dialer/lib/features/contacts/contact_state.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_contacts/flutter_contacts.dart'; -import 'contact_service.dart'; +import '../../widgets/contact_service.dart'; class ContactState extends StatefulWidget { final Widget child; diff --git a/dialer/lib/features/contacts/widgets/alphabet_scroll_page.dart b/dialer/lib/features/contacts/widgets/alphabet_scroll_page.dart index 2cc80ed..0ff1cb2 100644 --- a/dialer/lib/features/contacts/widgets/alphabet_scroll_page.dart +++ b/dialer/lib/features/contacts/widgets/alphabet_scroll_page.dart @@ -3,6 +3,7 @@ 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 'add_contact_button.dart'; import 'contact_modal.dart'; import 'share_own_qr.dart'; @@ -21,6 +22,7 @@ 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() { @@ -37,138 +39,48 @@ class _AlphabetScrollPageState extends State { } Future _refreshContacts() async { - if (await FlutterContacts.requestPermission()) { - final updatedContacts = await FlutterContacts.getContacts( - withProperties: true, withThumbnail: true); - setState(() { - _contacts = updatedContacts; - }); + try { + // Use the fetchContacts method from ContactService + final updatedContacts = await _contactService.fetchContacts(); + + if (mounted) { + setState(() { + _contacts = updatedContacts; + }); + } + } 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'))); } } - // void _toggleFavorite(Contact contact) async { - // print(contact.id); - - // if (await FlutterContacts.requestPermission()) { - // try { - // // Fetch all contacts (this can be slow if there are many contacts) - // List allContacts = await FlutterContacts.getContacts( - // withProperties: true, - // withThumbnail: true, - // withAccounts: true, - // withPhoto: true - // ); - - // // Find the specific contact by matching contact.id - // // Use `orElse` to return a nullable Contact? or throw an exception if not found - // Contact? contactToUpdate = allContacts.firstWhere( - // (c) => c.id == contact.id, - // orElse: () => throw Exception( - // "Contact not found"), // Throw an exception if not found - // ); - - // if (contactToUpdate != null) { - // print("Contact fetched: ${contactToUpdate.displayName}"); - // print("Current isStarred status: ${contactToUpdate.isStarred}"); - - // // Toggle the favorite status - // contactToUpdate.isStarred = !contactToUpdate.isStarred; - // print("Updated isStarred status: ${contactToUpdate.isStarred}"); - - // // Update the contact - // await FlutterContacts.updateContact(contactToUpdate); - // print("Contact updated successfully"); - - // // Refresh the UI by updating the state - // setState(() { - // contact.isStarred = contactToUpdate.isStarred; - // }); - // } - // } catch (e) { - // print("Error updating favorite status: $e"); - // } - // } - // } - - // void _toggleFavorite(Contact contact) async { - // print(contact.id); - - // try { - // // Fetch the contact with details - // final contactWithDetails = await FlutterContacts.getContact(contact.id, - // withProperties: true, withThumbnail: true, withAccounts: true); - - // if (contactWithDetails != null) { - // print("Contact fetched: ${contactWithDetails.displayName}"); - // print("Current isStarred status: ${contactWithDetails.isStarred}"); - - // // Toggle the favorite status - // contactWithDetails.isStarred = !contactWithDetails.isStarred; - // print("Updated isStarred status: ${contactWithDetails.isStarred}"); - - // // Update the contact - // await FlutterContacts.updateContact(contactWithDetails); - // print("Contact updated successfully"); - - // // Reset the contact state (optional) - // setState(() { - // contact.isStarred = contactWithDetails.isStarred; - // }); - - // // Re-fetch the contact to ensure UI reflects changes - // final updatedContact = await FlutterContacts.getContact(contact.id, - // withProperties: true, withThumbnail: true, withAccounts: true); - - // if (updatedContact != null) { - // print("Re-fetched updated contact: ${updatedContact.displayName}"); - // print("Re-fetched isStarred status: ${updatedContact.isStarred}"); - // } else { - // print("Re-fetching contact failed."); - // } - // } else { - // print("Contact details are not available"); - // } - // } catch (e) { - // print("Error updating favorite status: $e"); - // } - // } - void _toggleFavorite(Contact contact) async { - print(contact.id); - try { - if (contact != null) { - print("Contact fetched: ${contact.displayName}"); - print("Current isStarred status: ${contact.isStarred}"); + // 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, + withPhoto: true, + withThumbnail: true); - // Toggle the favorite status - contact.isStarred = !contact.isStarred; - print("Updated isStarred status: ${contact.isStarred}"); - - // Update the contact - await FlutterContacts.updateContact(contact); - print("Contact updated successfully"); - - // Reset the contact state (optional) - setState(() { - contact.isStarred = contact.isStarred; - }); - - // Re-fetch the contact to ensure UI reflects changes - final updatedContact = await FlutterContacts.getContact(contact.id, - withProperties: true, withThumbnail: true, withAccounts: true); - - if (updatedContact != null) { - print("Re-fetched updated contact: ${updatedContact.displayName}"); - print("Re-fetched isStarred status: ${updatedContact.isStarred}"); - } else { - print("Re-fetching contact failed."); + if (fullContact != null) { + // Update the contact + fullContact.isStarred = !fullContact.isStarred; + await FlutterContacts.updateContact(fullContact); } + await _refreshContacts(); } else { - print("Contact details are not available"); + print("Could not fetch contact details"); } } 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'))); } } diff --git a/dialer/lib/features/favorites/favorites_page.dart b/dialer/lib/features/favorites/favorites_page.dart index 48227c5..4cfda5f 100644 --- a/dialer/lib/features/favorites/favorites_page.dart +++ b/dialer/lib/features/favorites/favorites_page.dart @@ -1,3 +1,183 @@ +// import 'package:flutter/material.dart'; +// import 'package:flutter_contacts/flutter_contacts.dart'; +// import '../../../widgets/color_darkener.dart'; +// import '../contacts/contact_state.dart'; +// import '../contacts/widgets/add_contact_button.dart'; +// import '../contacts/widgets/contact_modal.dart'; +// import '../contacts/widgets/share_own_qr.dart'; +// import 'package:dialer/widgets/username_color_generator.dart'; + +// class FavoritePage extends StatefulWidget { +// final List contacts; +// final double scrollOffset; + +// const FavoritePage({ +// super.key, +// required this.contacts, +// required this.scrollOffset, +// }); + +// @override +// _FavoritePageState createState() => _FavoritePageState(); +// } + +// class _FavoritePageState extends State { +// late ScrollController _scrollController; +// List _favoriteContacts = []; // Local list of favorite contacts + +// @override +// void initState() { +// super.initState(); +// _favoriteContacts = widget.contacts.where((contact) => contact.isStarred).toList(); // Filter only favorites +// _scrollController = ScrollController(initialScrollOffset: widget.scrollOffset); +// _scrollController.addListener(_onScroll); +// } + +// void _onScroll() { +// final contactState = ContactState.of(context); +// contactState.setScrollOffset(_scrollController.offset); +// } + +// Future _refreshContacts() async { +// if (await FlutterContacts.requestPermission()) { +// final updatedContacts = await FlutterContacts.getContacts( +// withProperties: true, +// withThumbnail: true, +// ); +// setState(() { +// _favoriteContacts = updatedContacts.where((contact) => contact.isStarred).toList(); +// }); +// } +// } + +// void _toggleFavorite(Contact contact) async { +// if (await FlutterContacts.requestPermission()) { +// try { +// // Fetch all contacts (this can be slow if there are many contacts) +// List allContacts = await FlutterContacts.getContacts( +// withProperties: true, +// withThumbnail: true, +// withAccounts: true, +// ); + +// // Find the specific contact by matching contact.id +// Contact? contactToUpdate = allContacts.firstWhere( +// (c) => c.id == contact.id, +// orElse: () => throw Exception("Contact not found"), +// ); + +// if (contactToUpdate != null) { +// contactToUpdate.isStarred = !contactToUpdate.isStarred; + +// // Update the contact +// await FlutterContacts.updateContact(contactToUpdate); + +// // Refresh the favorite contacts list +// setState(() { +// _favoriteContacts = allContacts.where((c) => c.isStarred).toList(); +// }); +// } +// } catch (e) { +// print("Error updating favorite status: $e"); +// } +// } +// } + +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// backgroundColor: Colors.black, +// appBar: AppBar( +// title: const Text('Favorites'), +// backgroundColor: Colors.black, +// actions: [ +// IconButton( +// icon: const Icon(Icons.refresh), +// onPressed: _refreshContacts, +// ), +// ], +// ), +// body: _favoriteContacts.isEmpty +// ? Center( +// child: Text( +// 'No favorite contacts yet!', +// style: TextStyle(color: Colors.white), +// ), +// ) +// : ListView.builder( +// controller: _scrollController, +// itemCount: _favoriteContacts.length, +// itemBuilder: (context, index) { +// Contact contact = _favoriteContacts[index]; +// String phoneNumber = contact.phones.isNotEmpty +// ? contact.phones.first.number +// : 'No phone number'; +// Color avatarColor = generateColorFromName(contact.displayName); + +// return ListTile( +// leading: (contact.thumbnail != null && contact.thumbnail!.isNotEmpty) +// ? CircleAvatar( +// backgroundImage: MemoryImage(contact.thumbnail!), +// ) +// : CircleAvatar( +// backgroundColor: avatarColor, +// child: Text( +// contact.displayName.isNotEmpty +// ? contact.displayName[0].toUpperCase() +// : '?', +// style: TextStyle(color: darken(avatarColor, 0.4)), +// ), +// ), +// title: Text(contact.displayName, style: TextStyle(color: Colors.white)), +// subtitle: Text(phoneNumber, style: TextStyle(color: Colors.white70)), +// onTap: () { +// showModalBottomSheet( +// context: context, +// isScrollControlled: true, +// backgroundColor: Colors.transparent, +// builder: (context) { +// return ContactModal( +// contact: contact, +// onEdit: () async { +// // Trigger edit logic and refresh contacts +// if (await FlutterContacts.requestPermission()) { +// final updatedContact = +// await FlutterContacts.openExternalEdit(contact.id); +// if (updatedContact != null) { +// await _refreshContacts(); +// Navigator.of(context).pop(); +// ScaffoldMessenger.of(context).showSnackBar( +// SnackBar(content: Text('${contact.displayName} updated successfully!')), +// ); +// } else { +// ScaffoldMessenger.of(context).showSnackBar( +// SnackBar(content: Text('Edit canceled or failed.')), +// ); +// } +// } +// }, +// onToggleFavorite: () { +// _toggleFavorite(contact); +// }, +// isFavorite: contact.isStarred, +// ); +// }, +// ); +// }, +// ); +// }, +// ), +// ); +// } + +// @override +// void dispose() { +// _scrollController.dispose(); +// super.dispose(); +// } +// } + + import 'package:flutter/material.dart'; class FavoritePage extends StatefulWidget { diff --git a/dialer/lib/features/home/home_page.dart b/dialer/lib/features/home/home_page.dart index f16de1f..6cb729b 100644 --- a/dialer/lib/features/home/home_page.dart +++ b/dialer/lib/features/home/home_page.dart @@ -5,12 +5,15 @@ import 'package:dialer/features/history/history_page.dart'; import 'package:dialer/features/composition/composition.dart'; import 'package:flutter_contacts/flutter_contacts.dart'; import 'package:dialer/features/settings/settings.dart'; +import '../../widgets/contact_service.dart'; class _MyHomePageState extends State with SingleTickerProviderStateMixin { late TabController _tabController; List _allContacts = []; List _contactSuggestions = []; + final ContactService _contactService = ContactService(); + @override void initState() { @@ -21,10 +24,8 @@ class _MyHomePageState extends State } void _fetchContacts() async { - if (await FlutterContacts.requestPermission()) { - _allContacts = await FlutterContacts.getContacts(withProperties: true); - setState(() {}); - } + _allContacts = await _contactService.fetchContacts(); + setState(() {}); } void _onSearchChanged(String query) { diff --git a/dialer/lib/features/contacts/contact_service.dart b/dialer/lib/widgets/contact_service.dart similarity index 100% rename from dialer/lib/features/contacts/contact_service.dart rename to dialer/lib/widgets/contact_service.dart