From f99fdaa1609ac6c7cb912f0957676e4ed8f7de6d Mon Sep 17 00:00:00 2001 From: Florian Griffon Date: Thu, 30 Jan 2025 14:25:04 +0000 Subject: [PATCH] feat: can block/unblock in history page (#29) Reviewed-on: https://git.gmoker.com/icing/monorepo/pulls/29 Co-authored-by: Florian Griffon Co-committed-by: Florian Griffon --- dialer/lib/features/history/history_page.dart | 216 +++++++++++------- 1 file changed, 131 insertions(+), 85 deletions(-) diff --git a/dialer/lib/features/history/history_page.dart b/dialer/lib/features/history/history_page.dart index b8c0567..642b942 100644 --- a/dialer/lib/features/history/history_page.dart +++ b/dialer/lib/features/history/history_page.dart @@ -6,6 +6,7 @@ import 'package:intl/intl.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:dialer/features/contacts/contact_state.dart'; import 'package:dialer/widgets/username_color_generator.dart'; +import '../../services/block_service.dart'; class History { final Contact contact; @@ -15,12 +16,12 @@ class History { final int attempts; History( - this.contact, - this.date, - this.callType, - this.callStatus, - this.attempts, - ); + this.contact, + this.date, + this.callType, + this.callStatus, + this.attempts, + ); } class HistoryPage extends StatefulWidget { @@ -30,7 +31,8 @@ class HistoryPage extends StatefulWidget { _HistoryPageState createState() => _HistoryPageState(); } -class _HistoryPageState extends State with SingleTickerProviderStateMixin { +class _HistoryPageState extends State + with SingleTickerProviderStateMixin { List histories = []; bool loading = true; int? _expandedIndex; @@ -66,7 +68,7 @@ class _HistoryPageState extends State with SingleTickerProviderStat setState(() { histories = List.generate( contacts.length >= 10 ? 10 : contacts.length, - (index) => History( + (index) => History( contacts[index], DateTime.now().subtract(Duration(hours: (index + 1) * 2)), index % 2 == 0 ? 'outgoing' : 'incoming', @@ -91,7 +93,8 @@ class _HistoryPageState extends State with SingleTickerProviderStat List olderHistories = []; for (var history in historyList) { - final callDate = DateTime(history.date.year, history.date.month, history.date.day); + final callDate = + DateTime(history.date.year, history.date.month, history.date.day); if (callDate == today) { todayHistories.add(history); } else if (callDate == yesterday) { @@ -145,7 +148,8 @@ class _HistoryPageState extends State with SingleTickerProviderStat } // Filter missed calls - List missedCalls = histories.where((h) => h.callStatus == 'missed').toList(); + List missedCalls = + histories.where((h) => h.callStatus == 'missed').toList(); final allItems = _buildGroupedList(histories); final missedItems = _buildGroupedList(missedCalls); @@ -211,17 +215,10 @@ class _HistoryPageState extends State with SingleTickerProviderStat ListTile( leading: ObfuscatedAvatar( imageBytes: contact.thumbnail, - radius: 25, - backgroundColor: avatarColor, - fallbackInitial: contact.displayName, - ), - // child: Text( - // contact.displayName.isNotEmpty - // ? contact.displayName[0].toUpperCase() - // : '?', - // style: TextStyle(color: darken(avatarColor, 0.4)), - // ), - // ), + radius: 25, + backgroundColor: avatarColor, + fallbackInitial: contact.displayName, + ), title: Text( _obfuscateService.obfuscateData(contact.displayName), style: const TextStyle(color: Colors.white), @@ -241,18 +238,20 @@ class _HistoryPageState extends State with SingleTickerProviderStat icon: const Icon(Icons.phone, color: Colors.green), onPressed: () async { if (contact.phones.isNotEmpty) { - final Uri callUri = - Uri(scheme: 'tel', path: contact.phones.first.number); + final Uri callUri = Uri( + scheme: 'tel', path: contact.phones.first.number); if (await canLaunchUrl(callUri)) { await launchUrl(callUri); } else { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Could not launch call')), + const SnackBar( + content: Text('Could not launch call')), ); } } else { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Contact has no phone number')), + const SnackBar( + content: Text('Contact has no phone number')), ); } }, @@ -268,56 +267,99 @@ class _HistoryPageState extends State with SingleTickerProviderStat if (isExpanded) Container( color: Colors.grey[850], - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - TextButton.icon( - onPressed: () async { - if (history.contact.phones.isNotEmpty) { - final Uri smsUri = - Uri(scheme: 'sms', path: history.contact.phones.first.number); - if (await canLaunchUrl(smsUri)) { - await launchUrl(smsUri); - } else { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Could not send message')), + child: FutureBuilder( + future: BlockService().isNumberBlocked( + contact.phones.isNotEmpty + ? contact.phones.first.number + : ''), + builder: (context, snapshot) { + final isBlocked = snapshot.data ?? false; + return Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + TextButton.icon( + onPressed: () async { + if (history.contact.phones.isNotEmpty) { + final Uri smsUri = Uri( + scheme: 'sms', + path: history.contact.phones.first.number); + if (await canLaunchUrl(smsUri)) { + await launchUrl(smsUri); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: + Text('Could not send message')), + ); + } + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: + Text('Contact has no phone number')), + ); + } + }, + icon: + const Icon(Icons.message, color: Colors.white), + label: const Text('Message', + style: TextStyle(color: Colors.white)), + ), + TextButton.icon( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => + CallDetailsPage(history: history), + ), ); - } - } else { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Contact has no phone number')), - ); - } - }, - icon: const Icon(Icons.message, color: Colors.white), - label: const Text('Message', style: TextStyle(color: Colors.white)), - ), - TextButton.icon( - onPressed: () { - // Navigate to Call Details page - Navigator.push( - context, - MaterialPageRoute( - builder: (_) => CallDetailsPage(history: history), + }, + icon: const Icon(Icons.info, color: Colors.white), + label: const Text('Details', + style: TextStyle(color: Colors.white)), + ), + TextButton.icon( + onPressed: () async { + final phoneNumber = contact.phones.isNotEmpty + ? contact.phones.first.number + : null; + if (phoneNumber == null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: + Text('Contact has no phone number'), + ), + ); + return; + } + + if (isBlocked) { + await BlockService().unblockNumber(phoneNumber); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('$phoneNumber unblocked')), + ); + } else { + await BlockService().blockNumber(phoneNumber); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('$phoneNumber blocked')), + ); + } + setState(() {}); // Refresh the button state + }, + icon: Icon( + isBlocked ? Icons.lock_open : Icons.block, + color: Colors.white), + label: Text( + isBlocked ? 'Unblock' : 'Block', + style: const TextStyle(color: Colors.white), ), - ); - }, - icon: const Icon(Icons.info, color: Colors.white), - label: const Text('Details', style: TextStyle(color: Colors.white)), - ), - TextButton.icon( - onPressed: () { - // Implement block number functionality - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Number blocked (functionality not implemented)'), - ), - ); - }, - icon: const Icon(Icons.block, color: Colors.white), - label: const Text('Block', style: TextStyle(color: Colors.white)), - ), - ], + ), + ], + ); + }, ), ), ], @@ -363,15 +405,16 @@ class CallDetailsPage extends StatelessWidget { fallbackInitial: contact.displayName, ) : CircleAvatar( - backgroundColor: generateColorFromName(contact.displayName), - radius: 30, - child: Text( - contact.displayName.isNotEmpty - ? contact.displayName[0].toUpperCase() - : '?', - style: TextStyle(color: contactLetter), - ), - ), + backgroundColor: + generateColorFromName(contact.displayName), + radius: 30, + child: Text( + contact.displayName.isNotEmpty + ? contact.displayName[0].toUpperCase() + : '?', + style: TextStyle(color: contactLetter), + ), + ), const SizedBox(width: 16), Expanded( child: Text( @@ -407,7 +450,8 @@ class CallDetailsPage extends StatelessWidget { if (contact.phones.isNotEmpty) DetailRow( label: 'Number:', - value: _obfuscateService.obfuscateData(contact.phones.first.number), + value: _obfuscateService + .obfuscateData(contact.phones.first.number), ), ], ), @@ -420,7 +464,8 @@ class DetailRow extends StatelessWidget { final String label; final String value; - const DetailRow({Key? key, required this.label, required this.value}) : super(key: key); + const DetailRow({Key? key, required this.label, required this.value}) + : super(key: key); @override Widget build(BuildContext context) { @@ -430,7 +475,8 @@ class DetailRow extends StatelessWidget { children: [ Text( label, - style: const TextStyle(color: Colors.white70, fontWeight: FontWeight.bold), + style: const TextStyle( + color: Colors.white70, fontWeight: FontWeight.bold), ), const SizedBox(width: 8), Expanded(