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: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<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 {
final uri = Uri(scheme: 'tel', path: phoneNumber);
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
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 +147,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 +178,33 @@ class ContactModal extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.only(top: 10, right: 10),
child: PopupMenuButton<String>(
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
if (choice == 'delete') {
_deleteContact();
}
},
itemBuilder: (BuildContext context) {
return <PopupMenuEntry<String>>[
PopupMenuItem<String>(
return [
const PopupMenuItem<String>(
value: 'show_associated_contacts',
child: Text('Show associated contacts'),
),
PopupMenuItem<String>(
const PopupMenuItem<String>(
value: 'delete',
child: Text('Delete'),
),
PopupMenuItem<String>(
const PopupMenuItem<String>(
value: 'share',
child: Text('Share (via QR code)'),
),
PopupMenuItem<String>(
const PopupMenuItem<String>(
value: 'create_shortcut',
child:
Text('Create shortcut (to home screen)'),
),
PopupMenuItem<String>(
const PopupMenuItem<String>(
value: 'set_ringtone',
child: Text('Set ringtone'),
),
@ -126,7 +215,6 @@ class ContactModal extends StatelessWidget {
),
],
),
// Contact Profile
Padding(
padding: const EdgeInsets.all(16.0),
@ -134,88 +222,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),
],
),
),

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);
}
}