diff --git a/dialer/lib/features/contacts/widgets/contact_modal.dart b/dialer/lib/features/contacts/widgets/contact_modal.dart index 2d7d395..265a769 100644 --- a/dialer/lib/features/contacts/widgets/contact_modal.dart +++ b/dialer/lib/features/contacts/widgets/contact_modal.dart @@ -2,8 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_contacts/flutter_contacts.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:dialer/widgets/username_color_generator.dart'; +import '../../../widgets/block_service.dart'; -class ContactModal extends StatelessWidget { +class ContactModal extends StatefulWidget { final Contact contact; final Function onEdit; final Function onToggleFavorite; @@ -17,6 +18,55 @@ class ContactModal extends StatelessWidget { required this.isFavorite, }); + @override + _ContactModalState createState() => _ContactModalState(); +} + +class _ContactModalState extends State { + late String phoneNumber; + bool isBlocked = false; + + @override + void initState() { + super.initState(); + phoneNumber = widget.contact.phones.isNotEmpty + ? widget.contact.phones.first.number + : 'No phone number'; + _checkIfBlocked(); + } + + Future _checkIfBlocked() async { + if (phoneNumber != 'No phone number') { + bool blocked = await BlockService().isNumberBlocked(phoneNumber); + setState(() { + isBlocked = blocked; + }); + } + } + + Future _toggleBlockState() async { + if (phoneNumber == 'No phone number') { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('No phone number to block or unblock')), + ); + } else if (isBlocked) { + await BlockService().unblockNumber(phoneNumber); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('$phoneNumber has been unblocked')), + ); + } else { + await BlockService().blockNumber(phoneNumber); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('$phoneNumber has been blocked')), + ); + } + + if (phoneNumber != 'No phone number') { + _checkIfBlocked(); + } + Navigator.of(context).pop(); + } + void _launchPhoneDialer(String phoneNumber) async { final uri = Uri(scheme: 'tel', path: phoneNumber); if (await canLaunchUrl(uri)) { @@ -46,11 +96,9 @@ class ContactModal extends StatelessWidget { @override Widget build(BuildContext context) { - String phoneNumber = contact.phones.isNotEmpty - ? contact.phones.first.number - : 'No phone number'; - String email = - contact.emails.isNotEmpty ? contact.emails.first.address : 'No email'; + String email = widget.contact.emails.isNotEmpty + ? widget.contact.emails.first.address + : 'No email'; return GestureDetector( onTap: () => Navigator.of(context).pop(), @@ -59,17 +107,16 @@ class ContactModal extends StatelessWidget { child: GestureDetector( onTap: () {}, child: FractionallySizedBox( - heightFactor: 0.7, child: Container( decoration: BoxDecoration( color: Colors.grey[900], - borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + borderRadius: + const BorderRadius.vertical(top: Radius.circular(20)), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ - // Modal Handle - // Top Bar with Handle and Three-Dot Menu + // Modal Handle and Three-Dot Menu Stack( children: [ Align( @@ -91,31 +138,32 @@ class ContactModal extends StatelessWidget { child: Padding( padding: const EdgeInsets.only(top: 10, right: 10), child: PopupMenuButton( - icon: Icon(Icons.more_vert, color: Colors.white), + icon: const Icon(Icons.more_vert, + color: Colors.white), onSelected: (String choice) { - print( - 'Selected: $choice'); // Placeholder for menu actions + // Implement actions here + debugPrint('Selected: $choice'); }, itemBuilder: (BuildContext context) { - return >[ - PopupMenuItem( + return [ + const PopupMenuItem( value: 'show_associated_contacts', child: Text('Show associated contacts'), ), - PopupMenuItem( + const PopupMenuItem( value: 'delete', child: Text('Delete'), ), - PopupMenuItem( + const PopupMenuItem( value: 'share', child: Text('Share (via QR code)'), ), - PopupMenuItem( + const PopupMenuItem( value: 'create_shortcut', child: Text('Create shortcut (to home screen)'), ), - PopupMenuItem( + const PopupMenuItem( value: 'set_ringtone', child: Text('Set ringtone'), ), @@ -126,7 +174,6 @@ class ContactModal extends StatelessWidget { ), ], ), - // Contact Profile Padding( padding: const EdgeInsets.all(16.0), @@ -134,88 +181,114 @@ class ContactModal extends StatelessWidget { children: [ CircleAvatar( radius: 50, - backgroundImage: (contact.thumbnail != null && - contact.thumbnail!.isNotEmpty) - ? MemoryImage(contact.thumbnail!) + backgroundImage: (widget.contact.thumbnail != null && + widget.contact.thumbnail!.isNotEmpty) + ? MemoryImage(widget.contact.thumbnail!) : null, backgroundColor: - generateColorFromName(contact.displayName), - child: (contact.thumbnail == null || - contact.thumbnail!.isEmpty) + generateColorFromName(widget.contact.displayName), + child: (widget.contact.thumbnail == null || + widget.contact.thumbnail!.isEmpty) ? Text( - contact.displayName.isNotEmpty - ? contact.displayName[0].toUpperCase() + widget.contact.displayName.isNotEmpty + ? widget.contact.displayName[0] + .toUpperCase() : '?', - style: TextStyle( + style: const TextStyle( fontSize: 40, color: Colors.white), ) : null, ), - SizedBox(height: 10), + const SizedBox(height: 10), Text( - contact.displayName, - style: TextStyle( + widget.contact.displayName, + style: const TextStyle( fontSize: 24, fontWeight: FontWeight.bold), ), ], ), ), + const Divider(), // Contact Actions - Divider(), ListTile( - leading: Icon(Icons.phone, color: Colors.green), + leading: const Icon(Icons.phone, color: Colors.green), title: Text(phoneNumber), onTap: () { - if (contact.phones.isNotEmpty) { + if (widget.contact.phones.isNotEmpty) { _launchPhoneDialer(phoneNumber); } }, ), ListTile( - leading: Icon(Icons.message, color: Colors.blue), + leading: const Icon(Icons.message, color: Colors.blue), title: Text(phoneNumber), onTap: () { - if (contact.phones.isNotEmpty) { + if (widget.contact.phones.isNotEmpty) { _launchSms(phoneNumber); } }, ), ListTile( - leading: Icon(Icons.email, color: Colors.orange), + leading: const Icon(Icons.email, color: Colors.orange), title: Text(email), onTap: () { - if (contact.emails.isNotEmpty) { + if (widget.contact.emails.isNotEmpty) { _launchEmail(email); } }, ), - Divider(), - // Favorite and Edit Buttons + const Divider(), + // Favorite, Edit, and Block/Unblock Buttons Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + child: Column( children: [ - ElevatedButton.icon( - onPressed: () { - Navigator.of(context).pop(); - onToggleFavorite(); - }, - icon: Icon(contact.isStarred - ? Icons.star - : Icons.star_border), - label: Text( - contact.isStarred ? 'Unfavorite' : 'Favorite'), + // Favorite button + SizedBox( + width: double + .infinity, // This makes the button take full width + child: ElevatedButton.icon( + onPressed: () { + Navigator.of(context).pop(); + widget.onToggleFavorite(); + }, + icon: Icon(widget.isFavorite + ? Icons.star + : Icons.star_border), + label: Text( + widget.isFavorite ? 'Unfavorite' : 'Favorite'), + ), ), - ElevatedButton.icon( - onPressed: () => onEdit(), - icon: Icon(Icons.edit), - label: Text('Edit Contact'), + const SizedBox(height: 10), // Space between buttons + + // Edit button + SizedBox( + width: double + .infinity, // This makes the button take full width + child: ElevatedButton.icon( + onPressed: () => widget.onEdit(), + icon: const Icon(Icons.edit), + label: const Text('Edit Contact'), + ), + ), + const SizedBox(height: 10), // Space between buttons + + // Block/Unblock button + SizedBox( + width: double + .infinity, // This makes the button take full width + child: ElevatedButton.icon( + onPressed: _toggleBlockState, + icon: Icon( + isBlocked ? Icons.block : Icons.block_flipped), + label: Text(isBlocked ? 'Unblock' : 'Block'), + ), ), ], ), ), - SizedBox(height: 16), + + const SizedBox(height: 16), ], ), ), diff --git a/dialer/lib/widgets/block_service.dart b/dialer/lib/widgets/block_service.dart new file mode 100644 index 0000000..d8aaaf0 --- /dev/null +++ b/dialer/lib/widgets/block_service.dart @@ -0,0 +1,52 @@ +import 'package:shared_preferences/shared_preferences.dart'; + +class BlockService { + static final BlockService _instance = BlockService._internal(); + + factory BlockService() { + return _instance; + } + + BlockService._internal(); + + // Function to add a number to the blocked list + Future blockNumber(String number) async { + if (number.isEmpty) return; + + final prefs = await SharedPreferences.getInstance(); + List blockedNumbers = prefs.getStringList('blockedNumbers') ?? []; + + if (!blockedNumbers.contains(number)) { + blockedNumbers.add(number); + await prefs.setStringList('blockedNumbers', blockedNumbers); + print('$number has been blocked'); + } else { + print('$number is already blocked'); + } + } + + // Function to remove a number from the blocked list + Future unblockNumber(String number) async { + if (number.isEmpty) return; + + final prefs = await SharedPreferences.getInstance(); + List blockedNumbers = prefs.getStringList('blockedNumbers') ?? []; + + if (blockedNumbers.contains(number)) { + blockedNumbers.remove(number); + await prefs.setStringList('blockedNumbers', blockedNumbers); + print('$number has been unblocked'); + } else { + print('$number is not blocked'); + } + } + + // Check if a number is blocked + Future isNumberBlocked(String number) async { + if (number.isEmpty) return false; + + final prefs = await SharedPreferences.getInstance(); + List blockedNumbers = prefs.getStringList('blockedNumbers') ?? []; + return blockedNumbers.contains(number); + } +}