feat: can block/unblock in history page #29

Merged
stcb merged 1 commits from historyPageBlockContact into dev 2025-01-30 14:25:05 +00:00

View File

@ -6,6 +6,7 @@ import 'package:intl/intl.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'package:dialer/features/contacts/contact_state.dart'; import 'package:dialer/features/contacts/contact_state.dart';
import 'package:dialer/widgets/username_color_generator.dart'; import 'package:dialer/widgets/username_color_generator.dart';
import '../../services/block_service.dart';
class History { class History {
final Contact contact; final Contact contact;
@ -15,12 +16,12 @@ class History {
final int attempts; final int attempts;
History( History(
this.contact, this.contact,
this.date, this.date,
this.callType, this.callType,
this.callStatus, this.callStatus,
this.attempts, this.attempts,
); );
} }
class HistoryPage extends StatefulWidget { class HistoryPage extends StatefulWidget {
@ -30,7 +31,8 @@ class HistoryPage extends StatefulWidget {
_HistoryPageState createState() => _HistoryPageState(); _HistoryPageState createState() => _HistoryPageState();
} }
class _HistoryPageState extends State<HistoryPage> with SingleTickerProviderStateMixin { class _HistoryPageState extends State<HistoryPage>
with SingleTickerProviderStateMixin {
List<History> histories = []; List<History> histories = [];
bool loading = true; bool loading = true;
int? _expandedIndex; int? _expandedIndex;
@ -66,7 +68,7 @@ class _HistoryPageState extends State<HistoryPage> with SingleTickerProviderStat
setState(() { setState(() {
histories = List.generate( histories = List.generate(
contacts.length >= 10 ? 10 : contacts.length, contacts.length >= 10 ? 10 : contacts.length,
(index) => History( (index) => History(
contacts[index], contacts[index],
DateTime.now().subtract(Duration(hours: (index + 1) * 2)), DateTime.now().subtract(Duration(hours: (index + 1) * 2)),
index % 2 == 0 ? 'outgoing' : 'incoming', index % 2 == 0 ? 'outgoing' : 'incoming',
@ -91,7 +93,8 @@ class _HistoryPageState extends State<HistoryPage> with SingleTickerProviderStat
List<History> olderHistories = []; List<History> olderHistories = [];
for (var history in historyList) { 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) { if (callDate == today) {
todayHistories.add(history); todayHistories.add(history);
} else if (callDate == yesterday) { } else if (callDate == yesterday) {
@ -145,7 +148,8 @@ class _HistoryPageState extends State<HistoryPage> with SingleTickerProviderStat
} }
// Filter missed calls // Filter missed calls
List<History> missedCalls = histories.where((h) => h.callStatus == 'missed').toList(); List<History> missedCalls =
histories.where((h) => h.callStatus == 'missed').toList();
final allItems = _buildGroupedList(histories); final allItems = _buildGroupedList(histories);
final missedItems = _buildGroupedList(missedCalls); final missedItems = _buildGroupedList(missedCalls);
@ -211,17 +215,10 @@ class _HistoryPageState extends State<HistoryPage> with SingleTickerProviderStat
ListTile( ListTile(
leading: ObfuscatedAvatar( leading: ObfuscatedAvatar(
imageBytes: contact.thumbnail, imageBytes: contact.thumbnail,
radius: 25, radius: 25,
backgroundColor: avatarColor, backgroundColor: avatarColor,
fallbackInitial: contact.displayName, fallbackInitial: contact.displayName,
), ),
// child: Text(
// contact.displayName.isNotEmpty
// ? contact.displayName[0].toUpperCase()
// : '?',
// style: TextStyle(color: darken(avatarColor, 0.4)),
// ),
// ),
title: Text( title: Text(
_obfuscateService.obfuscateData(contact.displayName), _obfuscateService.obfuscateData(contact.displayName),
style: const TextStyle(color: Colors.white), style: const TextStyle(color: Colors.white),
@ -241,18 +238,20 @@ class _HistoryPageState extends State<HistoryPage> with SingleTickerProviderStat
icon: const Icon(Icons.phone, color: Colors.green), icon: const Icon(Icons.phone, color: Colors.green),
onPressed: () async { onPressed: () async {
if (contact.phones.isNotEmpty) { if (contact.phones.isNotEmpty) {
final Uri callUri = final Uri callUri = Uri(
Uri(scheme: 'tel', path: contact.phones.first.number); scheme: 'tel', path: contact.phones.first.number);
if (await canLaunchUrl(callUri)) { if (await canLaunchUrl(callUri)) {
await launchUrl(callUri); await launchUrl(callUri);
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Could not launch call')), const SnackBar(
content: Text('Could not launch call')),
); );
} }
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( 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<HistoryPage> with SingleTickerProviderStat
if (isExpanded) if (isExpanded)
Container( Container(
color: Colors.grey[850], color: Colors.grey[850],
child: Row( child: FutureBuilder<bool>(
mainAxisAlignment: MainAxisAlignment.spaceAround, future: BlockService().isNumberBlocked(
children: [ contact.phones.isNotEmpty
TextButton.icon( ? contact.phones.first.number
onPressed: () async { : ''),
if (history.contact.phones.isNotEmpty) { builder: (context, snapshot) {
final Uri smsUri = final isBlocked = snapshot.data ?? false;
Uri(scheme: 'sms', path: history.contact.phones.first.number); return Row(
if (await canLaunchUrl(smsUri)) { mainAxisAlignment: MainAxisAlignment.spaceAround,
await launchUrl(smsUri); children: [
} else { TextButton.icon(
ScaffoldMessenger.of(context).showSnackBar( onPressed: () async {
const SnackBar(content: Text('Could not send message')), 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 { icon: const Icon(Icons.info, color: Colors.white),
ScaffoldMessenger.of(context).showSnackBar( label: const Text('Details',
const SnackBar(content: Text('Contact has no phone number')), style: TextStyle(color: Colors.white)),
); ),
} TextButton.icon(
}, onPressed: () async {
icon: const Icon(Icons.message, color: Colors.white), final phoneNumber = contact.phones.isNotEmpty
label: const Text('Message', style: TextStyle(color: Colors.white)), ? contact.phones.first.number
), : null;
TextButton.icon( if (phoneNumber == null) {
onPressed: () { ScaffoldMessenger.of(context).showSnackBar(
// Navigate to Call Details page const SnackBar(
Navigator.push( content:
context, Text('Contact has no phone number'),
MaterialPageRoute( ),
builder: (_) => CallDetailsPage(history: history), );
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, fallbackInitial: contact.displayName,
) )
: CircleAvatar( : CircleAvatar(
backgroundColor: generateColorFromName(contact.displayName), backgroundColor:
radius: 30, generateColorFromName(contact.displayName),
child: Text( radius: 30,
contact.displayName.isNotEmpty child: Text(
? contact.displayName[0].toUpperCase() contact.displayName.isNotEmpty
: '?', ? contact.displayName[0].toUpperCase()
style: TextStyle(color: contactLetter), : '?',
), style: TextStyle(color: contactLetter),
), ),
),
const SizedBox(width: 16), const SizedBox(width: 16),
Expanded( Expanded(
child: Text( child: Text(
@ -407,7 +450,8 @@ class CallDetailsPage extends StatelessWidget {
if (contact.phones.isNotEmpty) if (contact.phones.isNotEmpty)
DetailRow( DetailRow(
label: 'Number:', 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 label;
final String value; 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -430,7 +475,8 @@ class DetailRow extends StatelessWidget {
children: [ children: [
Text( Text(
label, label,
style: const TextStyle(color: Colors.white70, fontWeight: FontWeight.bold), style: const TextStyle(
color: Colors.white70, fontWeight: FontWeight.bold),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
Expanded( Expanded(