contact-modal-delete-contact (#19)
All checks were successful
/ mirror (push) Successful in 4s

Reviewed-on: #19
Co-authored-by: Florian Griffon <florian.griffon@epitech.eu>
Co-committed-by: Florian Griffon <florian.griffon@epitech.eu>
This commit is contained in:
Florian Griffon 2024-12-20 09:20:17 +00:00 committed by stcb
parent e8aba933d0
commit e05880c9d8
2 changed files with 224 additions and 58 deletions

View File

@ -2,8 +2,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_contacts/flutter_contacts.dart'; import 'package:flutter_contacts/flutter_contacts.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'package:dialer/widgets/username_color_generator.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 Contact contact;
final Function onEdit; final Function onEdit;
final Function onToggleFavorite; final Function onToggleFavorite;
@ -17,6 +18,55 @@ class ContactModal extends StatelessWidget {
required this.isFavorite, required this.isFavorite,
}); });
@override
_ContactModalState createState() => _ContactModalState();
}
class _ContactModalState extends State<ContactModal> {
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<void> _checkIfBlocked() async {
if (phoneNumber != 'No phone number') {
bool blocked = await BlockService().isNumberBlocked(phoneNumber);
setState(() {
isBlocked = blocked;
});
}
}
Future<void> _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 { void _launchPhoneDialer(String phoneNumber) async {
final uri = Uri(scheme: 'tel', path: phoneNumber); final uri = Uri(scheme: 'tel', path: phoneNumber);
if (await canLaunchUrl(uri)) { if (await canLaunchUrl(uri)) {
@ -44,13 +94,51 @@ class ContactModal extends StatelessWidget {
} }
} }
void _deleteContact() async {
final bool shouldDelete = await showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Delete Contact'),
content: Text('Are you sure you want to delete ${widget.contact.displayName}?'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text('Cancel'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text('Delete'),
),
],
),
);
if (shouldDelete) {
try {
// Delete the contact
await FlutterContacts.deleteContact(widget.contact);
// Show success message
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('${widget.contact.displayName} deleted')),
);
// Close the modal
Navigator.of(context).pop();
} catch (e) {
// Handle errors and show a failure message
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to delete ${widget.contact.displayName}: $e')),
);
}
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
String phoneNumber = contact.phones.isNotEmpty String email = widget.contact.emails.isNotEmpty
? contact.phones.first.number ? widget.contact.emails.first.address
: 'No phone number'; : 'No email';
String email =
contact.emails.isNotEmpty ? contact.emails.first.address : 'No email';
return GestureDetector( return GestureDetector(
onTap: () => Navigator.of(context).pop(), onTap: () => Navigator.of(context).pop(),
@ -59,17 +147,16 @@ class ContactModal extends StatelessWidget {
child: GestureDetector( child: GestureDetector(
onTap: () {}, onTap: () {},
child: FractionallySizedBox( child: FractionallySizedBox(
heightFactor: 0.7,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.grey[900], color: Colors.grey[900],
borderRadius: BorderRadius.vertical(top: Radius.circular(20)), borderRadius:
const BorderRadius.vertical(top: Radius.circular(20)),
), ),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
// Modal Handle // Modal Handle and Three-Dot Menu
// Top Bar with Handle and Three-Dot Menu
Stack( Stack(
children: [ children: [
Align( Align(
@ -91,31 +178,33 @@ class ContactModal extends StatelessWidget {
child: Padding( child: Padding(
padding: const EdgeInsets.only(top: 10, right: 10), padding: const EdgeInsets.only(top: 10, right: 10),
child: PopupMenuButton<String>( child: PopupMenuButton<String>(
icon: Icon(Icons.more_vert, color: Colors.white), icon: const Icon(Icons.more_vert,
color: Colors.white),
onSelected: (String choice) { onSelected: (String choice) {
print( if (choice == 'delete') {
'Selected: $choice'); // Placeholder for menu actions _deleteContact();
}
}, },
itemBuilder: (BuildContext context) { itemBuilder: (BuildContext context) {
return <PopupMenuEntry<String>>[ return [
PopupMenuItem<String>( const PopupMenuItem<String>(
value: 'show_associated_contacts', value: 'show_associated_contacts',
child: Text('Show associated contacts'), child: Text('Show associated contacts'),
), ),
PopupMenuItem<String>( const PopupMenuItem<String>(
value: 'delete', value: 'delete',
child: Text('Delete'), child: Text('Delete'),
), ),
PopupMenuItem<String>( const PopupMenuItem<String>(
value: 'share', value: 'share',
child: Text('Share (via QR code)'), child: Text('Share (via QR code)'),
), ),
PopupMenuItem<String>( const PopupMenuItem<String>(
value: 'create_shortcut', value: 'create_shortcut',
child: child:
Text('Create shortcut (to home screen)'), Text('Create shortcut (to home screen)'),
), ),
PopupMenuItem<String>( const PopupMenuItem<String>(
value: 'set_ringtone', value: 'set_ringtone',
child: Text('Set ringtone'), child: Text('Set ringtone'),
), ),
@ -126,7 +215,6 @@ class ContactModal extends StatelessWidget {
), ),
], ],
), ),
// Contact Profile // Contact Profile
Padding( Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
@ -134,88 +222,114 @@ class ContactModal extends StatelessWidget {
children: [ children: [
CircleAvatar( CircleAvatar(
radius: 50, radius: 50,
backgroundImage: (contact.thumbnail != null && backgroundImage: (widget.contact.thumbnail != null &&
contact.thumbnail!.isNotEmpty) widget.contact.thumbnail!.isNotEmpty)
? MemoryImage(contact.thumbnail!) ? MemoryImage(widget.contact.thumbnail!)
: null, : null,
backgroundColor: backgroundColor:
generateColorFromName(contact.displayName), generateColorFromName(widget.contact.displayName),
child: (contact.thumbnail == null || child: (widget.contact.thumbnail == null ||
contact.thumbnail!.isEmpty) widget.contact.thumbnail!.isEmpty)
? Text( ? Text(
contact.displayName.isNotEmpty widget.contact.displayName.isNotEmpty
? contact.displayName[0].toUpperCase() ? widget.contact.displayName[0]
.toUpperCase()
: '?', : '?',
style: TextStyle( style: const TextStyle(
fontSize: 40, color: Colors.white), fontSize: 40, color: Colors.white),
) )
: null, : null,
), ),
SizedBox(height: 10), const SizedBox(height: 10),
Text( Text(
contact.displayName, widget.contact.displayName,
style: TextStyle( style: const TextStyle(
fontSize: 24, fontWeight: FontWeight.bold), fontSize: 24, fontWeight: FontWeight.bold),
), ),
], ],
), ),
), ),
const Divider(),
// Contact Actions // Contact Actions
Divider(),
ListTile( ListTile(
leading: Icon(Icons.phone, color: Colors.green), leading: const Icon(Icons.phone, color: Colors.green),
title: Text(phoneNumber), title: Text(phoneNumber),
onTap: () { onTap: () {
if (contact.phones.isNotEmpty) { if (widget.contact.phones.isNotEmpty) {
_launchPhoneDialer(phoneNumber); _launchPhoneDialer(phoneNumber);
} }
}, },
), ),
ListTile( ListTile(
leading: Icon(Icons.message, color: Colors.blue), leading: const Icon(Icons.message, color: Colors.blue),
title: Text(phoneNumber), title: Text(phoneNumber),
onTap: () { onTap: () {
if (contact.phones.isNotEmpty) { if (widget.contact.phones.isNotEmpty) {
_launchSms(phoneNumber); _launchSms(phoneNumber);
} }
}, },
), ),
ListTile( ListTile(
leading: Icon(Icons.email, color: Colors.orange), leading: const Icon(Icons.email, color: Colors.orange),
title: Text(email), title: Text(email),
onTap: () { onTap: () {
if (contact.emails.isNotEmpty) { if (widget.contact.emails.isNotEmpty) {
_launchEmail(email); _launchEmail(email);
} }
}, },
), ),
Divider(), const Divider(),
// Favorite and Edit Buttons // Favorite, Edit, and Block/Unblock Buttons
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0), padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
ElevatedButton.icon( // Favorite button
onPressed: () { SizedBox(
Navigator.of(context).pop(); width: double
onToggleFavorite(); .infinity, // This makes the button take full width
}, child: ElevatedButton.icon(
icon: Icon(contact.isStarred onPressed: () {
? Icons.star Navigator.of(context).pop();
: Icons.star_border), widget.onToggleFavorite();
label: Text( },
contact.isStarred ? 'Unfavorite' : 'Favorite'), icon: Icon(widget.isFavorite
? Icons.star
: Icons.star_border),
label: Text(
widget.isFavorite ? 'Unfavorite' : 'Favorite'),
),
), ),
ElevatedButton.icon( const SizedBox(height: 10), // Space between buttons
onPressed: () => onEdit(),
icon: Icon(Icons.edit), // Edit button
label: Text('Edit Contact'), 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),
], ],
), ),
), ),

View File

@ -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<void> blockNumber(String number) async {
if (number.isEmpty) return;
final prefs = await SharedPreferences.getInstance();
List<String> 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<void> unblockNumber(String number) async {
if (number.isEmpty) return;
final prefs = await SharedPreferences.getInstance();
List<String> 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<bool> isNumberBlocked(String number) async {
if (number.isEmpty) return false;
final prefs = await SharedPreferences.getInstance();
List<String> blockedNumbers = prefs.getStringList('blockedNumbers') ?? [];
return blockedNumbers.contains(number);
}
}